# @curvetechnologies/analytics-node 中文文档

规范地址：https://npmcn.com/package/%40curvetechnologies/analytics-node/

版本：0\.1\.1
npm 元数据更新时间：2026-08-18T18:51:18.987Z
中文译文更新时间：2026-11-14T14:15:12.531Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40curvetechnologies/analytics-node
原文对照：https://npmcn.com/package/%40curvetechnologies/analytics-node/?lang=original
项目主页：https://curvecompliance.com/
源码仓库：https://github.com/Curve-Compliance/curve-node-sdk

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：57.4 kB，6 个文件
- 模块格式：CommonJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=18
- 运行依赖：0 个
- 首次发布：2026-08-18T16:13:28.744Z
- 最新版本发布：2026-08-18T18:51:18.810Z
- 近 30 天下载量：2017（2026-10-11 至 2026-11-09，npm 官方下载统计）
- 已发布版本数：2（近 12 个月 2 个）

## 中文简介

Curve 服务器边事件客户端 。 将后端转换和已存在的浏览器跟踪器一起发送到 Curve .

## 安装

```sh
npm install @curvetechnologies/analytics-node
```

也可以使用：`pnpm add @curvetechnologies/analytics-node`、`yarn add @curvetechnologies/analytics-node`、`bun add @curvetechnologies/analytics-node`

## README

# @ 曲线技术/分析-节点

Curve 服务器边事件客户端 。 节点18+,零依赖.

运行 **并行** 您现有的浏览器跟踪器( Curve) `/t.js`或另一个供应商)。 没有 与它们一起声明: 这是从您的服务器中单独发送的 HTTPS 呼叫, 因此两者可以并行运行 无限.

兄弟姐妹包 : [`@curvetechnologies/analytics-react-native`](https://www.npmjs.com/package/@curvetechnologies/analytics-react-native) iOS/Android应用程序.

* * *

## 问题解决

浏览器事件知道 **用户来自何处** (单位:千美元) `gclid`编号 :`fbclid` 在登陆 URL 中),但无法看到 用户关闭标签后会发生什么: 由 Strepe webhook 确认的付款, 订阅 被晚间工作续约.

服务器事件是相反的:它们知道 **发生了什么事** 但没有点击ID.

曲线重接两个 **电子邮件**数字 :

1.  您启动的页面会捕捉浏览器中的电子邮件, 点击的ID仍然存在 。 曲线写入 两者均改为 `email_click_ids`键入 `(organization_id, email_canonical)`。 。 。 . `email_canonical` 实值 简单 `lower(email)`,所以加地址在两侧都很重要.
2.  几天后,你的服务器张贴了 购买,除了那封邮件.
3.  在摄入曲线上查看电子邮件,并重新附上原件 `gclid`,因此谷歌广告属性 购买到导致它的点击.

**后果: 电子邮件是 SDK 无法错误的一个字段 .** 它的规范化与 弯道摄入 `" <Customer@Example.COM> "` 从服务器 webhook 和 `customer@example.com` 从浏览器解析到同行。 需要电子邮件的事件 没有可用的是 **拒绝客户端** 而不是发送。 吃过但从未吃过的东西 属性比永不摄入的属性还要差,因为仪表板上看起来很好.

* * *

## 安装

```bash
npm install @curvetechnologies/analytics-node
```

客户在仪表板下到达SDK **配置 > 服务器 SDK**,它自己的导航项目.

## 设置

```js
// lib/curve.js. One module-level client, reused across requests.
const { CurveClient } = require('@curvetechnologies/analytics-node');

// Your allowlist: the descriptive event names you mapped in the Curve dashboard.
const CURVE_EVENTS = [
  { name: 'purchase_completed', stage: 'purchase', requiresEmail: true, expectsStripeCustomer: true },
  { name: 'lead_submitted',     stage: 'identity', requiresEmail: true },
];

module.exports.curve = new CurveClient({
  apiKey: process.env.CURVE_API_KEY,          // server-side only. Never NEXT_PUBLIC_*.
  events: CURVE_EVENTS,
  domain: 'example.com',                      // must be REGISTERED in the Curve dashboard, see below
  enabled: process.env.NODE_ENV === 'production',   // preview/CI validate but do not send
  onError: (err, ctx) => console.warn('[curve]', ctx.eventName, err.message),
});
```

`apiKey`, (中文(简体) ), `events` 财务报告和审定财务报表 `domain` 需要并投掷建筑。 这是有意的: 部署时的错误,在启动时应该失败,而不是默不作声地放弃每个转换一周.

`endpoint` 默认为 Curve 的公开摄入主机,因此大多数集成从未设置过. 如果 您的组织在自定义的摄入域.

### `domain` 不是可选的,必须登记

`POST /events/track` 答题 **400个缺少所需栏目** 对于一个没有 `domain`,以及 **403 域数 未登记** 当价值不在组织的范围之内时 `domains` 页:1 注册主机名 曲线仪表板 _在此之前_ 第一个部署,并通过赤色主机名(`example.com`没有 `https://example.com/checkout`,尽管一个 URL 已被接受并简化为您的主机名.

### 在没有浏览器的情况下, 何谓识别对象

该路线还要求 `anonymous_user_id`,因为没有设备。 这个 SDK 发送 `email_<normalized email>`,用于没有设备而到达的转换的合成键. Curve 将电子邮件 前缀视为不是设备,也不会发明来自它的访客. 过 `anonymousUserId` 明确,如果您有更好的稳定标识符.

## 发送事件

```js
const result = await curve.track('purchase_completed', {
  email: session.customer_email,        // the join key
  stripeCustomerId: session.customer,   // 'cus_...', exists only after purchase
  purchaseOptionType: 'membership_annual',
  paymentIntentId: session.payment_intent,
  value: session.amount_total / 100,    // MAJOR units. Stripe reports cents; /100 or report 100x revenue
  currency: session.currency,
});

if (!result.ok) {
  // Never throws. Log and carry on. Your checkout must not fail because Curve is unreachable.
  console.warn('[curve]', result.reason, result.message);
}

// result.warnings is worth logging too: dropped currency codes, a purchase missing its Stripe ID, and
// reserved `custom` keys are reported there rather than by failing the send.
```

### `result.clickIdAttribution`: 此转换是否真的加入 ?

`ok: true` 表示曲线接受事件. 没错 **没有** 表示事件会属性化. 一个买家 在浏览器的摄入量中 从来没有看到过电子邮件 和加入的一样幸福 完全一样,然后到达Google/Meta 没有任何信用。 这就是整个失败 整合的存在是为了预防,它从状态代码中看不见.

因此,路线报告一个判决 和SDK携带它通过:

| `result.clickIdAttribution` | 含义 |
| --- | --- |
| `'present'` | 你自己发了个身份证明 没什么好恢复的. |
| `'recovered'` | 曲线重接了浏览器会话的点击ID出局 `email_click_ids`。 。 。 . **这是幸福的道路.** |
| `'none'` | 没有什么可归结的 该事件被存储并转发,但无属性. |
| 缺席 | 后端没有报告:旧部署,或非转换事件. |

```js
if (result.clickIdAttribution === 'none') {
  metrics.increment('curve.unattributed_purchase');   // alert if this stops being rare
}
```

稳住的一滴 `'none'` 这是正常的(直接交通、有机的、从未见过广告的访客)。 A 类 _步骤 变动_ 指浏览器方面停止了收取电子邮件,或双方停止同意. 这个 后端在 WARN 中也会用一个蒙面的电子邮件记录每个, 因此在 Sentry 中可以计算, 没有任何 工具在你身边.

### 三把钥匙

| 密钥 | 当它存在时 | 作用 |
| --- | --- | --- |
| `email` | 从开始的页面开始 | **归属加盟**,从浏览器会话中恢复 ID |
| `purchaseOptionType` | 在选择计划时 | 按购入的部件转换 |
| `stripeCustomerId` | 仅在购买后 | 转变后的持久身份 |

仅在电子邮件上预购事件密钥 。 后购事所取所取所取三. SDK警告( 仍然) 当购买阶段的事件没有花旗标识时, 因为提前退账事件是正当的.

## 同意

可选的, 并省略它不会改变任何东西: 未经同意的事件对象决定 Curve 的 允许/ BAA- 默认 Egress 姿态, 即服务器集成之前的每个行为 选项已存在.

等你的后端知道访客同意状态后发去 弯道的后卫也一样 适用于浏览器事件的规则: `advertising_allowed: false`切换到 联系字段的有限处理模式已剥去, Google Ads、 Microsoft 和 LinkedIn 则不 完全传送。 没有它,同一人的浏览器购买可以受到限制,同时 服务器侧的购买记录是无限制的.

```js
await curve.track('purchase_completed', {
  email: session.customer_email,
  paymentIntentId: session.payment_intent,
  consent: {
    consent_given: true,
    advertising_allowed: false,   // this visitor opted out of advertising
    analytics_allowed: true,
    is_explicit: true,            // they chose, rather than inheriting a jurisdiction default
  },
})
```

只有布尔人。 非布尔( N)`'false'`, (中文(简体) ), `0`)作为 **缺席**从不被迫否认, 并警告人们不要沉默地重新解释. `consent` 内部 `custom` 是一个 保留了钥匙并被丢掉 所以有一条办法可以表示同意.

## 同上:在接线前读取

花旗传送网路 **至少一次**,所以一次付款可以到达两三次.

SDK得到一个决定论 **UUIDv5 设备** `event_id` 从它发现的第一个稳定的商业键 (单位:千美元)`orderId` → `paymentIntentId` → `checkoutSessionId` → `subscriptionId`) (中文(简体) ). 每次重送都一样 因此,付款持有同样的身份证.

```js
await curve.track('purchase_completed', {
  email, stripeCustomerId,
  paymentIntentId: event.data.object.payment_intent,   // <- makes the ID stable across redeliveries
});
```

**确切地说,那买什么, 因为这不是所有的东西:**

|  | 醉了吗? |
| --- | --- |
| Meta CAPI 转换, 使用Meta dedups `(event_name, event_id)` | **对**两次交货成为一次转换 |
| 曲线 `events` 表格和曲线仪表板计数 | **没有**摄入没有将军 `event_id` 脱下 |

所以,决定性身份 使得 **广告平台** 对 它没有使 Curve 的生事件计数正确. 如果仪表板的号码必须准确,请将自己的已处理的网页表按住Frede的键 `event.id` 跳过 `track()` 请重新发送。 (身份证必须是UUID-形状,这样任何一种文件都可以使用) 工作:摄入路线保持客户端提供 `event_id` 仅当它与 v1- v5 UUID 相匹配且默默 删除其他任何内容。 )

如果没有稳定的密钥存在, ID是随机的, 这是正确的 "现在发生"事件 错误的 任何网友都能重现的. **在由webhook驱动的事件上总是通过一个稳定的密钥.**

## 时间戳和回填

`occurredAt` 进入 **毫秒** 当你通过一个数字。 每条花纹时间戳`session.created`, (中文(简体) ), `invoice.created`, (中文(简体) ), `event.created`属于 _秒数_这样 `occurredAt: session.created` 日期为 改为1970年1月。 SDK 拒绝下方的任何时间值 `1e12`,警告,并使用电流 时间代替。 传出 a `Date`,或乘以1000.

`occurredAt` 套装 `event_time_ms` 在电线上,Meta把它作为转换时间 **只有当它是 在最后一小时之内**总是给事件自己盖章 `occurred_at` 作为 _现在_复出 因此,这周的网络呼号 与今天的事件 上周的 `event_time_ms` 记录 `event_data`它确实 **没有** 恢复原来的时间戳。 回填前与曲线交谈; Meta 也 硬弹道转换已超过七日.

## 失败行为

| 情况 | 发生什么事 |
| --- | --- |
| 网络下行/ 超时( 3s 默认) | 然后用紧张的后退来重试 `{ok:false, reason:'unreachable'}` |
| `5xx` 或者说 `429` | 重审 |
| `401` 编号 : `403` / 其他 `4xx` | **没有** 重审, 因为一个错误的密钥或未注册的域名无法再次尝试修复 |
| 缺少/无效的电子邮件 | 在提出任何请求之前被驳回`missing_email`(中文(简体) ) |
| 没有电子邮件也没有 `anonymousUserId` | 被拒绝(`missing_identity`),因为路线会回答400 |
| 待命 `domain` 这不是主机名 | 被拒绝(`invalid_domain`(中文(简体) ) |
| 未知事件名称 | 拒绝,因为登记处是许可名单(`unknown_event`(中文(简体) ) |
| 服务器发送的应用事件 | 被拒绝(`wrong_origin`),参见双数后卫 |
|  `onError` 处理器投掷 | 吞没 |

`track()` **从不拒绝.** 每一次失败都是一个解决的结果对象. 唯一的掷出在 建筑师.

默认值( E)`timeoutMs: 3000`, (中文(简体) ), `retries: 2`完全无法到达的曲线给呼叫者造成高达~9s的费用 加上紧张。 下调 `retries` 或用途 `trackAsync()` 如果它包含在用户面前的请求中.

### 利率限制, 以及它们为什么从服务器上更重要

允许无效 **每实施伙伴每分钟1 000个事件** 财务报告和审定财务报表 **每个组织每分钟10 000美元**。浏览器 流量遍及每个访客的IP,从未接近每个IP的限制;您的后端帖子 所有来自一两个egress地址的东西, 所以它是第一个可以到达它的集成.

网路交通稳定 离这里不远 批量回填或夜间工作 即: 超过限制的路由答案 `429`而SDK的两台回放 共用同样的爆裂而不是 把它冲干 Pace批量发送到每分钟几百个,并在大回填前与Curve交谈.

### 无服务器

在韦尔塞尔/兰布达, `await track()` 或使用平台的 `waitUntil()`。 。 。 . `trackAsync()` 立即返回, 但是,一个没有服务器的容器可以在返回回复的瞬间被冻结,从而杀死飞行中的发送.

## 事件登记

您通过的阵列 `events` 是许可列表. `track()` 拒绝任何没有声明的名字, 所以打字 在测试中失败, 而不是悄悄创建垃圾事件类型 。 每个条目:

| 外地 | 默认 | 含义 |
| --- | --- | --- |
| `name` | 所需经费 | 描述事件名称, 与仪表板上的地图完全相同 |
| `origin` | `'server'` | `'in_app'` 标记您的应用程序在浏览器中起火的事件; `track()` 拒绝 |
| `stage` | `'lifecycle'` | `'identity'` 编号 : `'purchase'` 编号 : `'lifecycle'`。文档,加上条纹标识警告 |
| `requiresEmail` | `true` | 无法使用电子邮件时拒绝调用 |
| `expectsStripeCustomer` | `false` | 当买到没有花旗客户身份证时发出警告( 仍在发送) |

### 双重计数警卫

如果您的登记人也宣布了事件,则您 **应用** 浏览器中起火,标记 `origin: 'in_app'` 财务报告和审定财务报表 `track()` 将拒绝他们 `reason: 'wrong_origin'`。 。 。 .

这不是私奔。 当浏览器事件和服务器事件都描述同一购买时,发送 后端的每一次采购都算两次 报告ROAS的真相是两倍 那种错误 这看起来像一个好消息 几个星期。 最安全生产登记处只宣布服务器事件.

### 描述性名称,而非别名

发送 `purchase_completed`曲线绘制到你们组织的HIPAA安全 `event_N` 别名服务器侧。 这样做 未发送 `event_N` 从你的应用, 因为那绕过 映射你的遵守状态取决于。 一个 未标记的描述性名称作为绘图建议出现在仪表板上并被固定在那里.

### `custom` 属性

任何内容 `custom` 合并为 `event_data`. SDK 本身的钥匙(`email`,条纹/顺序 识别器, `value`, (中文(简体) ), `currency`)并按住摄取路线储备(`event_id`, (中文(简体) ), `event_time_ms`, (中文(简体) ), `user_id`,移动摄入和同意字段) **在警告下抛出** 而不是允许 覆盖他们。 一个生的 `custom.email` 降落在一个正常的上面 正是如何 归属关系会平静下来 点击 ID 是 _没有_ 保留: 如果您的服务器持有一个真实的 `gclid`发送它.

PHI sanitization 仍然运行服务器侧,但不要依赖它. 保持临床数据 `custom`。 。 。 .

## 测试您的整合

```js
const client = new CurveClient({
  apiKey: 'x', events: CURVE_EVENTS, domain: 'example.com', fetch: fakeFetch,
});
const built = client.buildPayload('purchase_completed', { email: 'a@b.com', stripeCustomerId: 'cus_1' });
// inspect built.payload without sending anything
```

或者设定 `enabled: false` 没有网络的完全验证.

## 常见问题

### @curvetechnologies/analytics-node 是什么？

Curve 服务器边事件客户端 。 将后端转换和已存在的浏览器跟踪器一起发送到 Curve \.

### 如何安装 @curvetechnologies/analytics-node？

在项目目录运行 npm install @curvetechnologies/analytics-node，或使用 pnpm add / yarn add / bun add。

### @curvetechnologies/analytics-node 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @curvetechnologies/analytics-node 支持 ESM 还是 CommonJS？

以 CommonJS 形式发布，可以直接 require\(\)；在 ES Module 项目中也可以用 import 引入（Node\.js 会把 module\.exports 作为默认导出）。

### @curvetechnologies/analytics-node 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=18。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @curvetechnologies/analytics-node 可以商用吗？

@curvetechnologies/analytics-node 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @curvetechnologies/analytics-node 的下载量有多少？

根据 npm 官方下载统计，@curvetechnologies/analytics-node 在 2026/10/11 – 2026/11/09 这 30 天内被下载 2,017 次，日均约 67 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @curvetechnologies/analytics-node 还在维护吗？

最新版本 0\.1\.1 发布于 2026/08/19（2 个月前）；至今共发布 2 个版本，其中过去 12 个月发布了 2 个；首次发布于 2026/08/19。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

