# @codepress/interactions-browser 中文文档

规范地址：https://npmcn.com/package/%40codepress/interactions-browser/

版本：1\.1\.0
npm 元数据更新时间：2026-11-05T22:19:45.341Z
中文译文更新时间：2026-11-10T07:38:46.574Z
软件包许可：Apache-2\.0

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

原始 npm 页面：https://www.npmjs.com/package/%40codepress/interactions-browser
原文对照：https://npmcn.com/package/%40codepress/interactions-browser/?lang=original
项目主页：https://github.com/quantfive/codepress-interactions#readme
源码仓库：https://github.com/quantfive/codepress-interactions

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

- 包体积（解压后）：422 kB，18 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- 运行依赖：0 个
- 首次发布：2026-11-05T15:33:37.301Z
- 最新版本发布：2026-11-05T22:19:45.036Z
- 近 30 天下载量：3306（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：4（近 12 个月 4 个）

## 中文简介

隐私第一浏览器交互证据 SDK:屏幕快照,点击后续,UI检查和可插接的汇

## 安装

```sh
npm install @codepress/interactions-browser
```

也可以使用：`pnpm add @codepress/interactions-browser`、`yarn add @codepress/interactions-browser`、`bun add @codepress/interactions-browser`

## README

# 浏览器交互 SDK

在应用程序客户端入口处初始化一次 。 SSR期间导入软件包是安全的;被禁用/SSR初始化返回一个惰性手柄.

```ts
import { initInteractions } from "@codepress/interactions-browser";

const interactions = initInteractions({
  projectKey: "public-project-key",
  endpoint: "https://interactions.example.com",
  allowedApiOrigins: [window.location.origin],
});
```

终点是服务基础 URL; SDK 发送协议 v2 批次给 `/v2/batches` 并快照身体到 `/v2/snapshots/{sha256}` (单位:千美元)[计划](https://github.com/quantfive/codepress-interactions/blob/main/protocol)) (中文(简体) ). 使用 `sink` 改为 `endpoint` 将证据送交其他任何地方(见 [银色](https://npmcn.com/package/%40codepress/interactions-browser/#readme-sinks)) (中文(简体) ). 公钥解决服务项目/环境. `environment` 是一个本地诊断配置标签, 从来不授予权限 。 服务源准入和租赁配置必须已经允许安装。 API的出处默认为此页面的出处. 仅在您的申请和许可控制下明确添加单独的 API 来源 `X-Interaction-Id` cORS的配置.

初始设置抓取会点击语义控件(包括动态挂载控件),格式提交,导航信号,被绑定的结构DOM变化,以及被允许的源取/XHR观测. 它不需要每个控制的说明。 使用控制标识符 `data-interaction-id`编号 :`data-testid` 传递到 [测试ID过滤器](https://npmcn.com/package/%40codepress/interactions-browser/#readme-test-ids-and-privacy),否则,一个 - 最多五倍的祖先暗示。 这种倒计时不独有,从不扫描兄弟会收藏. 这些标识符是结构提示,在布局变化时可以改变. 标记私有子树 `data-interactions-exclude`。不要将用户数据或秘密输入明确的跟踪识别符.

不记录 DOM 文本, HTML, 输入值, 请求/响应机构, URL 路径, 查询, 饼干或用户ID 。 变异证据报告改变了记录计数,而不是重放或断言自旋者代表业务完成. HTTP 状态被限制 `data.phase` 标签, 如 `http_201`; 浏览器请求结果为 `completed`编号 :`error`,从不服务器商业结果。 没有使用可持久浏览器存储 .

## 测试身份证和隐私

测试ID常由数据构建: `row-${record.id}`, (中文(简体) ), `domain-row-${hostname}`。录制后,它会把数据从页面上移出,每个记录的ID会将一个发现分成许多。 所以每个 `data-interaction-id` 财务报告和审定财务报表 `data-testid` 在任何地方使用之前先通过过滤器:快照 `test_id` 词典,单击 `control_id`, (中文(简体) ), `control_id` 关于UI事件,一个发现 `stable_id` 还有指纹.

默认过滤器, `defaultTestIdFilter`,只保留一个ID 当它看起来静止。 它降低了一个值:

-   包含一个 `.`,或
-   有一段( 断开 `-`, (中文(简体) ), `_`, (中文(简体) ), `.` 或者说 `:`)含有数字或超过32个字符.

| 数值 | 默认 |
| --- | --- |
| `save-button`, (中文(简体) ), `nav:settings`, (中文(简体) ), `plan_pro_cta` | 保留 |
| `item-42`, (中文(简体) ), `step2` | 已删除:一个数字 |
| `row-3f2a9c1e-7b4d-4e8a-9c21-5d6e7f809a1b` | 已删除:一个数字 |
| `domain-row-example.com`, (中文(简体) ), `checkout.pay` | 弃: a `.` |

丢弃的身份证被视为没有。 点击时使用标记路径,发现时使用从最近的祖先发出的结构路径并保留了ID. `data-interaction-id` 遵循同样的规则: `data-testid`。当它被丢弃时,元素的 `data-testid` 将尝试下一个.

过滤器无法识别每个值 。 短句的一首诗(`project-acme-launch`),名称(`row-JaneDoe`)或无位数的随机ID外观为静态并被保留,而有位数的静态ID(`step2`)被丢弃。 如果你的ID可以持有流星或名字,或者需要ID的默认滴出,只允许您知道的ID:

```ts
const STATIC_TEST_IDS = new Set(["save-button", "nav:settings", "step2"]);

initInteractions({
  sink,
  testId: (value) => (STATIC_TEST_IDS.has(value) ? value : null),
});
```

这个 `testId` 选项替换默认值。 它运行在默认的任何地方,带有属性的值和元素. 返回值,或静态替换,以保留它 `null` 放弃它:

```ts
import { defaultTestIdFilter, initInteractions } from "@codepress/interactions-browser";

initInteractions({
  sink,
  // Collapse per-record rows into one ID, and use the default for the rest.
  testId: (value) => (value.startsWith("order-row-") ? "order-row" : defaultTestIdFilter(value)),
});
```

钩子代替了默认而非添加,所以 `testId: (value) => value` 保留 `item-42`调用 `defaultTestIdFilter(value)` 从钩子上保护它。 结果仍必须是明确的标识(`[a-zA-Z][a-zA-Z0-9_.:-]*`,最多128个字符)或被删除。 丢出或返回字符串以外的任何东西的钩子 `null`中,放下ID;继续抓取,没有任何记录。 钩子为每个元素运行,带有一个快照行走的ID,在点击和UI状态变化时,使其快速而无副作用. 传递一个稳定函数: 如 `routeTemplate`,在 `initInteractions` 被再次调用,而使用不同钩子的调用会得到一个不活跃的手柄.

## 关联

自动点击是单独的交互尝试. 自动请求和突变/导航观测的归属不明:SDK没有将它们附在最近发生的哪个点击上. 对于已知的因果工作,使用可选的明确行动:

```ts
await interactions.runAction("transfer.confirm", async (context) => {
  await context.fetch("/api/transfer", { method: "POST" });
  await someOtherWork();
  await context.fetch("/api/verify");
});
```

`runAction` 设置上下文,只在调用时同步引用。 同步部分的普通接取/ XHR 呼叫继承动作 。 之后 `await`,计时器或另一个调用器,使用已提供的 `context.fetch` 保存已知的链接。 在等待和独立投票后,普通无限制取票仍无关联。 这避免了全球同步环境在重叠行动之间泄露. 商业回召值、抛出的错误和被拒绝的承诺保持不变。 现有 `X-Trace-Id` 保存; 原始标题从未被记录。 摄取和未配置的第三方来源从来就没有仪器或寄出关联头.

## 寿命周期和界限

`flush()` 尝试一个不可改变的批次,返回时不会出现运输错误。 可重试的故障保留了准确的批次/活动ID和有效载荷. 默认:1000个队列事件,100个事件/批次,6万字节/批次,每5个冲出,请求超时10个,3个有1s指数基数的回放,每回调50个突变-观察工作单位. 使用覆盖 `maxQueueEvents`, (中文(简体) ), `maxBatchEvents`, (中文(简体) ), `maxBatchBytes`, (中文(简体) ), `flushIntervalMs`, (中文(简体) ), `requestTimeoutMs`, (中文(简体) ), `maxRetries`, (中文(简体) ), `retryBaseMs`, (中文(简体) ), `maxMutationRecords`。 。 。 . `maxMutationRecords` 保留其现有名称,但是一个共享的召回预算(最多1,000个):记录、祖先隐私检查,以及每个被检查的树下节点——包括被排除或非元素节点——控制单位。 它永远不会被重置为另一个记录或子树。 被排除在外的后人被跳过,疲惫状态停止检查;对于这个被截断的回调,取消了一次诊断递增,而没有要求精确地计算未见节点或事件。 这些是局部的。 过度流出和累累累累累累的累累累累累累累累累累累累累累累累累累累累累的累累累累累累累累累累累累累累累累累累累的累累累累累累累累累累累累累累累累累累累的累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累累 重试计时器由冲出间隔驱动 . `getDiagnostics()` 暴露本地接受/失败/投放的计数和出错;它不声称端到端安装健康.

页面出口试图 一小批保存。 交付是最好的努力,并且可能失败,特别是在上传已经激活或浏览器终止的情况下. 不在 URL 中设置秘密 。 HTTP 401/403停止捕捉并清除待取出的证据;重新连接需要在确定录取后明确重新启动.

重复相同的初始化通过参考计算租赁共享一个记录器。 每个老板都打电话 `shutdown()` 在它的手柄上; 最后一个所有者停止计时器/观测器, 中止 SDK 上传, 清除队列, 并且只恢复 SDK 仍然拥有的补丁 。 后来的第三方补丁被保留. 不同的并发项目配置返回一个惰性手柄:在切换账户/org之前关闭旧项目,所以待决事件永远不会被重新标记. 反应"严格模式"在完全清理后可能会启动新的记录器,这是安全的.

未覆盖:闭合阴影根,帧内容(帧为一不透明盒),一般承诺/计时器传播,持续下线回放,输入录制,截图,抽样完整性推论,或全局交叉批量回扣. 浏览器遥测是不信任的证据. Service v1需要不可改变的批次重试;事件ID从未被重新分配到另一批.

结构反馈包括出现或消失的进展/状况指标, `aria-busy` 转换和禁用控制。 省去数值和状态文本; 没有语义标记的 CSS 唯一的自旋器可能仍然是未知的结构变化. 已签名的URL被排除在请求的仪器之外. 已存在的交互头 保存; 有效的UUID用于关联性,否则属性未知.

## 路由模板

事件和快照带有路由模板,从不包含URL. 默认情况下, 每个路径段都被替换为 `:id`这样 `/u/jane-smith` 财务报告和审定财务报表 `/acme-corp/projects` 均作为 `/:id/:id`: 小写 sattle可以是用户名,组织或标题,所以除非应用程序如此表示,否则就永远不会保存. 列出您在 `routeSegments` (单位:千美元)`/acme-corp/projects` 与 `routeSegments: ["projects"]` 变成 `/:id/projects`),或通过 `routeTemplate` 以返回您的路由器自己的模式。 查询和散列从未使用 .

## 屏幕快照

每个已安放的屏幕都作为元素框记录在柱形体中([计划](https://github.com/quantfive/codepress-interactions/blob/main/protocol/v2-snapshot.schema.json), (中文(简体) ), [实例](https://github.com/quantfive/codepress-interactions/blob/main/protocol/v2-snapshot.json): 父母,标签,角色,安全测试ID, `x`编号 :`y`编号 :`width`编号 :`height` (查看端口 CSS 像素来自 `getBoundingClientRect`),z-index,CSS 位置,旗帜(可见,可交互,可剪接,可滚动,可覆盖,被忽略,指针事件无,不透明,可剪接内容,以阴影根,打开弹出,椭圆或行夹,断开图像, `aria-hidden` 和文本长度。 "入影根"指元素自有树为"入影根";分档光-DOM内容不标出. 取景口记录主指针是否粗糙(touch)和屏幕上的主题(`light`, (中文(简体) ), `dark` 或者说 `unknown`: `theme()` 选项,否则 `prefers-color-scheme`编辑 `ui.snapshot` 事件带有相同的 `theme`,因此屏幕可以按路由,取景口和主题进行分组,而无需阅读机构. 对于有直接文字的元素,它也记录了已变换文字的框本身(来自 `Range`,仅指几何),所以一个元素框可以重叠某个东西,而其文本则没有。 对于可见的互动元素,它探测 `elementFromPoint` 在中心点和四个陷入点 并存储多少土地 在别的东西上, 和哪个元素在上. `elementFromPoint` 从不要求其他要素。 没有记录文字,值,属性值(除了角色和安全测试ID),URL或HTML.

当路由结算(包括第一个屏幕),点击结算后,调整大小结算后,以及标签再次被显示时,即刻取出快照. `handle.snapshot()` 需要一分钱 每个正体被散列(SHA-256)准确的未压缩字节;本页会话中已经发送的散列不再发送. 与 `endpoint`,身体由上传 `HEAD` 那么,仅仅在404年,一个gzip `PUT` 改为 `/v2/snapshots/{sha256}`。服务必须允许 `HEAD`编号 :`PUT` 编辑 `Authorization` 财务报告和审定财务报表 `Content-Encoding` 来源于您的信头 .

走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走行走走行走行走走行走行走行走行走走走行走行走行走行走行走走走行走走行走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走走 `maxSliceMs` 已经过去了,所以一个切片 超支最多一个批8。 它停止在 `maxElements` 并标记尸体 `truncated`; 体型大于 `maxSnapshotBytes` 滴入后行为 `truncated`。如果DOM、滚动位置或取景口在切片之间发生变化,则标出天体 `torn`. 开口的阴影根被穿透(其槽下有分层内容);闭口根是一盒. 相机、相机、相机和相机 `opaque`。 。 。 . `display: none` 次树被跳过。 浏览器不渲染的内容( 已关闭的正文) `<details>`, (中文(简体) ), `content-visibility: hidden`)被记录时没有明显的旗帜. 子树匹配 `ignoreSelector`, (中文(简体) ), `[data-layout-ignore]` 或者说 `[data-interactions-exclude]` 记录为一个标记的框 `ignored` 所以支票可以跳过 跟踪剪切检测 `overflow` 财务报告和审定财务报表 `contain: paint`;它并不模拟每个案例(例如绝对定位后人逃离了非定位剪接祖先).

## 单击后续

每次点击一个控件都会启动后续窗口. 当页面保持安静时结束(没有DOM更改,没有在飞行中观察到请求) `clickSettleMs`,并报告为一个 `ui.click.settled` 事件: 第一次可见反馈的时间( DOM 更改、 路由更改、 对话框或焦点移动)、 结算时间、 突变数、 请求、 失败请求、 未发现错误和长的任务、 排版移位分数、 路由改变、 对话框打开或焦点移动、 屏幕散列前后。 自0.3.0起,它也表示点击是否落在了控制上(`target_interactive`),修改了字段的值(`value_changed`,例如复选框),将焦点放入文本字段(`field_focused`; 在已聚焦的字段中, 注意是效果, 或打开新标签或窗口时, 每次点击都再次为真( name`new_context`) (中文(简体) ). 自1.0起,它说明是点击自己的按键,还是点击打开时的页面,修改了文本选择,还是移动了护栏(`selection_changed`; 位置比较,选定的文本永远不会被读取。 焦点和选择只计为答案 `rage_click`;它们不是反馈 `first_feedback_ms`, (中文(简体) ), `dead_click` 或者说 `slow_feedback`。后一指针或密钥输入造成的焦点或反馈不贷记到更早的打开点击中。 内容 `interaction_id` 是点击的 `interaction.started` 身份证及其归属总是 `inferred`;窗口中观察到的请求会在那里被计算,从不作为清晰的附加在点击上. 第一次和解前的第二次点击 `overlapped` 两者都不计算在内 点击后仍然打开 `clickTimeoutMs`,或当页面被隐藏或卸下时,报告 `incomplete`自0.3.0起点击 `interaction.started` 还携带 `target_key`,单击元素的每个会话编号不透明(仅平等意味着任何东西),单击一个 `<label>` 记录一次,作为单击其字段控制(例如 `<button role="checkbox">` 带有 `<label for>`),而不是当浏览器转发到字段时再次. 一个字段的控制是它上方最近的控制,或者它通过 `form` 属性。 已禁用字段的标签和标签内的链接都是自己的点击.

## 用户界面检查

`@codepress/interactions-browser/detect` 将快照和事件转化为发现。 检查是纯函数而不是数据:否 `document`,没有 `window`,在浏览器、节点和测试跑取器中得出同样的结果([剧作家助手](https://github.com/quantfive/codepress-interactions/blob/main/packages/playwright/README.md)) (中文(简体) ). 默认情况下, SDK 在每次新快照之后的空闲时间运行它们, 在每次确定点击后, 在每次冲出时, 发送每个发现作为 `ui.finding` 每页会话每个指纹一次事件(`findings: false` 关掉这个). SDK通过谨慎记起多达2000个发送的指纹:一个再次发现的指纹算作是最近才发现的,没有被发现的最长指纹被遗忘了,因此只有在此后才再次发送. 在当前运行中发现的指纹永远不会忘记腾出空间,所以一个不断产生相同发现的屏幕永远不会重新发送;如果运行中发现有超过2000个新的指纹,剩下的就等待后一运行. 从第一个快照还没有取景口之前点击查找,所以它等待给它一个快照(并用它来表示正确的宽幅带和指纹);它只是当页面消失或者会话没有取景时才发送.

发现是指针,不是判断:它命名元素并携带 `evidence`,仅计量事实(数字、布尔斯或 `null` 页:1 是否是bug是给读它的人的;检查不会通过自信过滤.

| 检查( C)`check`(中文(简体) ) | 报告 |
| --- | --- |
| `layout.overlap` | 二个元素的可见相框相接 超过 `overlapMinPx` 在两个方向上,两者都不包含对方。 主体是下面的元素。 证据:重叠的大小,每个覆盖部分的分数,被屏蔽的下方控制点的命中测试点,是否覆盖了被检查者提供的文本(`null` 对于不透明盒,无论主体的任何内容(文本、控制、图像或不透明盒)都位于重叠(`subject_content_covered`),每个元素是否为控件(`subject_interactive`, (中文(简体) ), `other_interactive`),CSS位置,深度为共享母体,覆盖旗帜. |
| `layout.spill` | 一个元素穿过它的最近 `overflow: hidden/clip` 祖先超过 `tolerancePx`。只报告最外溢的元素. |
| `layout.page_h_scroll` | 文档比取景口要宽;主体是到达最右边的元素. |
| `layout.offscreen_control` | 页面在区域外的控件可以滚动到(或者,如果固定的话,在取景口外). |
| `layout.small_tap_target` | 一个小于 `minTapTargetPx` 仅用于触摸输入. |
| `layout.clipped_text` | 被剪接盒所剪去的渲染文本,除非以椭圆或行夹来结束. |
| `click.dead_click` | 对一个没有反馈、请求、路由更改、对话、焦点移动、值变化、字段焦点或新标签的控制进行定点点击. |
| `click.rage_click` | `rageClickCount` 点击一个控件的相同元素 `rageClickWindowMs`,而一些点击没有回答。 一种形态的不同字段不是一出突起,而标签点击(浏览器向它的字段转发)是一出突起. 一次突袭,每一次确定点击都得到了回答,这并非愤怒:明显的反馈(一个步子,一个切换),一个焦点文字字段的护身符(`field_focused`),或修改文本选择(`selection_changed`,例如三击选择行。 一个仍在定居,以后再决定。 证据: `click_count`, (中文(简体) ), `span_ms`, (中文(简体) ), `clicks_settled`, (中文(简体) ), `clicks_with_feedback` (已回答点击; `null` ),没有单击后续. |
| `click.slow_feedback` | 之后的第一次反馈 `slowFeedbackMs`,或者在请求持续了那么长的时间内没有. |
| `click.silent_failure` | 点击时请求失败, 屏幕没有更改 . |
| `click.error_after_click` | 点击时未发现错误 . |
| `stability.stuck_busy` | 繁忙或进度指示器仍然显示于 `stuckBusyMs`。 。 。 . |
| `stability.layout_shift` | 性能窗口的布局变化分数在或以上 `layoutShiftScore`。 。 。 . |
| `stability.slow_input` | 最慢的事件时间在或以上 `slowInputMs`。 。 。 . |
| `stability.broken_image` | 一个没有图像数据的图像装入完成 . |

未报告的重叠或溢出: 对话框、 菜单、 列表框、 工具提示和打开的弹出符与下方的弹出符( 它们是单独的层, 如固定的全屏背景); 内容跨越滚动器的边缘( 滚动); 内容被粘度或固定元素所滚动( 一旦页被滚动; 在页源中报告包含内容的固定或粘度元素); 子树 `data-layout-ignore`; 1×1 px 或更少的元素。 A 类 `torn` 快照没有基于位置的发现; 缺少行 `truncated` 一个不给。 点击检查只使用窗口 `settled`: 点击其页面已导航、超时或被另一点击所重叠的 `dead_click`。 。 。 .

排序将屏蔽控件放在首位( 任何 `*_blocked_points` > 0),然后被覆盖或剪接文本,再将其余的,再进行装饰或空洞的发现. 在其余部分中,首先要发现涉及控制:一个小自带目标、一个外屏控制、一个点击查找、一个要素是控制时的重叠或溢出(`*_interactive`),或一对外对,在主体内阻断控制(`subject_blocked_controls` > 0,例如: `main × widget`(三); `involvesControl(finding)` 说哪一个。 然后通过检查,顺序找到。 它永远不会放弃一个发现。 当一个要素是装饰层时,发现是装饰性的或空的(`*_decorative`:没有文本,没有控制,以及 `pointer-events: none` 或者说 `aria-hidden`),其任何要素都没有文本或控制(`*_has_content: false`,例如堆放或被裁剪的图像),或重叠内容没有覆盖主体的内容(`subject_content_covered: false`) (中文(简体) ). `findingTier(finding)` 给出等级,0到3。 早期的阶地取胜:一个用被覆盖的文本覆盖文本类型的装饰层. 重叠证据也有 `subject_text_covered_ratio`,该主题的文本区域中覆盖的部分.

### 衡量的行为值得了解

这些报告按衡量标准报告。 每个发现都包含着说出它是什么的证据,因此读者可以不猜测地决定.

-   **旁边的固定部件 `<main>` 报告于 `<main>`。 。 。 .** 一个聊天按钮,即 `position: fixed` 和一个兄弟 `<main>`被报告为 `main × chat`没有 `save × chat`被嵌入的元素所重复的碰撞会倒塌到最外侧的一对上,而一对上方的证据涵盖了其中的一切. 覆盖的按钮仍然出现 : `subject_blocked_points` 财务报告和审定财务报表 `subject_blocked_controls` 数一下它被封锁的命中点, 所以首先发现的种类, 主体的 `index` 点数 `<main>`"行",其中快照按住按钮.
-   **一个固定的页眉上加的 `<main>` 报告为空.** 在页面来源, a `position: fixed` 标题 `<main style="padding-top: 64px">` 交叉 `<main>`框,所以报告为 `main × header`它的证据说里面什么都没有: `subject_blocked_points: 0`, (中文(简体) ), `subject_text_covered: false` 财务报告和审定财务报表 `subject_content_covered: false`里面有装饰性的和空洞的发现 如果你的布局是怎样的,请接受 `ignoreFingerprints` 测试或放置 `data-layout-ignore` 在头部。 页面已滚动后,固定标题下的内容会滚动而未报告.
-   **点击浏览器不转发的标签, 而改变什么, 是一个死点击 .** 点击标签内容时,浏览器不会转发到字段(一个嵌入式按钮,一个链接,一个正文) `<details>`,一个 `<img usemap>` 带有空映射),或者在隐藏或已禁用字段的标签上,是自己的点击. 当什么都没有发生,它给 `dead_click` 标签上的控件(例如表格) `target_interactive: true` 因为标签是控件。 一个禁用字段的标签给出了相同的标签 .
-   **加入到另一个窗体的字段属于该窗体.** 与 `form` 属性,以及任何标签,都记录在它加入的表格上,无论它坐落在DOM中在哪里:一个标签点击就是一个 `interaction.started` 在那张表格上.

指纹是SHA-256的32个十六进制字符 在检查、路由模板、取景口宽带和元素上 `stable_id`,从不坐标、滚动偏移或文本。 在另一个卷轴偏移时, 在另一个会话中, 或者在另一个机器上, 同样的错误也是相同的 . `stable_id` 是测试的ID,或者,当没有或曾经 [过滤出](https://npmcn.com/package/%40codepress/interactions-browser/#readme-test-ids-and-privacy),从最近的祖先取出的结构标记路径并带有测试ID(`#cart/div/button`; 证据 `subject_structural_id` 编号 : `structural_id` 这么说的 结构路径在标记完成后会改变,拥有相同标记的兄弟姐妹共享一个.

```ts
import { checkEvents, checkSnapshot, formatFinding } from "@codepress/interactions-browser/detect";

const findings = [...checkSnapshot(body, { sha256 }), ...checkEvents(batch.events)];
findings.map(formatFinding);
```

| 检测选项 | 默认 |  |
| --- | --- | --- |
| `checks` | 全部 | 要运行的简称, 例如 . `["overlap", "spill"]`。 。 。 . |
| `overlapMinPx` | 0.5 (韩语) | 重叠必须比此更宽更高(子像素圆环不计数). |
| `tolerancePx` | 1个 | 溢出、屏外、被剪取的文字和页卷必须比这更交叉. |
| `minTapTargetPx` | 24个 | WCAG 2.2目标最小尺寸. |
| `touch` | 快照的粗指针 | 检查目标. |
| `viewportBands` | `[480, 768, 1024, 1440]` | 指纹带的宽断点. |
| `slowFeedbackMs` | 1 000个 |  |
| `rageClickCount` 编号 : `rageClickWindowMs` | 3个/1000个 |  |
| `stuckBusyMs` | 1万个 |  |
| `layoutShiftScore` | 0.1 数据 |  |
| `slowInputMs` | 200个 |  |
| `now` | 上次事件时间( SDK: 当前时间) | 何时 `stuckBusyMs` 被测量. |

## 性能信号

布局班次、事件时间安排(40 ms和较慢)、长任务、图像加载错误和未发现错误/未处理的拒绝被计算和发送 `perf.summary` 事件。 每个 API 被特性检测:浏览器不支持的信号是 `null` 列入 `unavailable`,从未报告为零。 不读取错误消息、堆栈、文件名和图像 URL.

## 银色

`sink: { sendBatch, sendSnapshot }` 替换 `endpoint`· ; `projectKey` 则不需要。 每种方法都可返回(或解析) `"accepted"` (默认), `"retry"`, (中文(简体) ), `"rejected"` 或者说 `"denied"` (停止捕捉并清除待取证据,如HTTP 401/403). 扔出、拒绝或永不解决的沉槽不会影响页面:电话被隔离,超时 `requestTimeoutMs` 并按常理重审. `createMemorySink()` 将一切保存在内, 用于测试 :

```ts
import { createMemorySink, initInteractions } from "@codepress/interactions-browser";

const sink = createMemorySink();
const handle = initInteractions({ sink });
await handle.snapshot();
await handle.flush();
sink.batches; // protocol v2 batches
sink.snapshots; // { sha256, serialized, body }
```

发送到网络上的水槽 `context` 作为其最后的论点: `keepalive` (页面正在卸下;保持小并使用) `fetch`是因为 `keepalive`, (中文(简体) ), `signal` (在超时或当捕获停止时堕胎)和 `fetch`,页面的 `fetch` 没有SDK的包装。 使用 `context.fetch` 所以水槽本身的上传不记录为应用程序的要求,这也会使每个冲浪引起另一个. 请求在第一个汇开始之前开始 `await` 选择 `fetch` 或者说 `XMLHttpRequest` 它们被使用; 后一种被记录, 除非它们被使用 `context.fetch`。 。 。 . [`examples/react-vite`](https://github.com/quantfive/codepress-interactions/blob/main/examples/react-vite) 拥有一个通过 Vite Dev 服务器写入文件的槽.

```ts
const sink: InteractionSink = {
  async sendBatch(_batch, serialized, { keepalive, signal, fetch }) {
    const response = await fetch("/my-collector/batches", { method: "POST", body: serialized, keepalive, signal });
    return response.ok ? "accepted" : response.status >= 500 ? "retry" : "rejected";
  },
  async sendSnapshot({ sha256, serialized }, { signal, fetch }) {
    await fetch(`/my-collector/snapshots/${sha256}`, { method: "PUT", body: serialized, signal });
  },
};
```

## 选项

| 选项 | 默认 | 页:1 |
| --- | --- | --- |
| `endpoint` + 键 `projectKey` |  | 主机服务基地URL和公钥. |
| `sink` |  | 备选案文 `endpoint`。 。 。 . |
| `release` | 无 | 每一批都贴有标签. |
| `routeSegments` | 无 | 默认路由模板所保存的静态小写路径词,例如. `["orders", "items"]`。 。 。 . |
| `routeTemplate(url)` | 每个路径段 `:id` 除非为 `routeSegments`; 没有查询或散列 | 返回有边框的路由标签, 如 `/orders/[id]`中。将删除带有查询或不安全字符的值. |
| `testId(value, element)` | `defaultTestIdFilter` | 哪些测试ID被记录;参见 [测试身份证和隐私](https://npmcn.com/package/%40codepress/interactions-browser/#readme-test-ids-and-privacy)。 。 。 . |
| `requestLabel(url, init)` | 无 | 观测到的请求的固定标签`[A-Za-z0-9_.:/-]`,64个字符). |
| `propagateHeader` | `X-Interaction-Id` | 允许来源的关联页眉名称; `false` 无。 可读 `X-Trace-Id` 反应头被记录. |
| `snapshots` | `true` | 关闭屏幕快照. |
| `maxElements` | 5000 (最多50,000) | 每一个快照之前的元素 `truncated`。 。 。 . |
| `maxSliceMs` | 6(最多50个) | 每走一片主线. |
| `maxSnapshotBytes` | 1 500 000美元 | 无压缩体盖. |
| `maxSnapshotsPerSession` | 100个 | 每页行走. |
| `routeSettleMs` | 1 000个 | 在路由改变后等待(和第一个负载),然后是快照. |
| `clickSettleMs` | 500个 | 安静时间结束点击的后继. |
| `clickTimeoutMs` | 1万个 | 时间最长之前 `incomplete`。 。 。 . |
| `resizeSettleMs` | 500个 | 最后一次调整后安静. |
| `visibleSettleMs` | 三百块 | 标签显示后再次显示延迟 . |
| `ignoreSelector` | 无 | 记录为一的额外子树 `ignored` 框中输入. |
| `findings` | `true` | 运行用户界面检查并发送 `ui.finding` 事件. |
| `detect` | 以上默认值 | UI检查阈值. |
| `theme` | `prefers-color-scheme` | `() => "light" | "dark" | "unknown"`:主题实际显示,用于应用自己的切换. 每张快照都读取;任何其他值或掷出,记录为 `unknown`。 。 。 . |

以上运输选项(`maxQueueEvents`, (中文(简体) ), `flushIntervalMs`,.)保持他们的v1默认值.

## 常见问题

### @codepress/interactions-browser 是什么？

隐私第一浏览器交互证据 SDK:屏幕快照,点击后续,UI检查和可插接的汇

### 如何安装 @codepress/interactions-browser？

在项目目录运行 npm install @codepress/interactions-browser，或使用 pnpm add / yarn add / bun add。

### @codepress/interactions-browser 支持 TypeScript 吗？

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

### @codepress/interactions-browser 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### @codepress/interactions-browser 可以商用吗？

@codepress/interactions-browser 使用 Apache-2\.0 许可。宽松许可：允许商业使用、修改和再分发，并附带明确的专利授权；分发时需保留许可与 NOTICE 文件，修改过的文件要注明改动。以上为概括，不构成法律意见，请以许可原文为准。

### @codepress/interactions-browser 的下载量有多少？

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

### @codepress/interactions-browser 还在维护吗？

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

