跳转至

浏览器场景契约

capture_browser_scenario 通过 MCP 和 capture-browser-scenario INPUT_JSON CLI 命令接受与提供商无关的 browserScenarioSchema。该请求是声明式的:它允许固定的操作词汇表、显式的 HTTP(S) 目标、确定性的浏览器设置、显式的存储种子以及提供商拥有的存活期限。INPUT_JSON 可以是内联 JSON,也可以是 JSON 文件路径。

最小请求仅包含浏览器启动/连接选择、start_url 和至少一个 actions 条目。启动 headless 默认为 true,在主机支持可见浏览器的情况下可设置为 false。导航遵循场景指定的目标和页面的普通浏览器请求,而 请求步骤 仅发送到 start URL 的来源;不存在单独的来源允许列表。请求步骤使用临时分派闸门进行取消;普通页面请求保留其浏览器行为。默认提供环境设置(包括默认阻止的 service worker)、空存储和最终的经过清理的 URL 捕获。当应用程序需要 service worker 时,将 environment.service_workers 设置为 allow。已连接的 service-worker 策略持有者会在发现、附加和恢复过程中保留其确切的 CDP 传输通道。当套接字清理也失败时,失败的准入会保留其主诊断;后续策略清理会重试该保留的传输通道,并且只有在关闭成功后才会消除清理不确定性。对于已附加的浏览器,当所选页面或其 frame 受 service worker 控制或已有注册时,block 会拒绝捕获,包括在导航之后。策略检查会包围每次工件捕获,包括最后一步。如果外部 worker 在工件捕获期间取得控制权,REA 会拒绝该捕获,并将已完成的工件保留在 details.diagnostics.collected_artifacts 中;它们不构成符合策略的完成。现有调用方的注册永远不会被移除。导航可以在新发现的 worker 被拒绝之前运行目标代码;该拒绝不会回滚浏览器效果。REA 会移除其拥有的未来文档脚本,恢复活动 frame 方法,然后断开连接。仅在需要时添加机密声明、存储种子以及额外的工件或事件捕获。缺少环境变量支持的机密会在获取浏览器之前作为输入错误失败,并指出声明和所需变量。显式设置为空字符串被视为已解析的值。操作、机密和存储的数量不受限制。

持续时间、操作和导航超时与 CDP 命令期限保持分离。command_timeout_ms(默认 20000;省略它可保留该默认值并将其排除在 Evidence 之外)约束启动启动、基于 CDP 的连接握手、目标查找、环境命令以及存储 CDP 命令,包括来自已附加子目标的回复。它还约束初始 start_url 导航直至 load 事件;重定向或替换导航不会让启动无限期等待。在场景 JSON 中设置它。capture-browser-scenario 没有 --command-timeout-ms 选项。它不会改变操作的 timeout_ms、wait_for_timeout.duration_ms 或操作导航。Service Worker 恢复保持固定的 5000 ms 宽限期,并且不会继承提高后的 command_timeout_ms。CDP 发现保持其自身的固定期限。当 service worker 被阻止时,发现之前的注册检查有自己的固定 10000 ms 期限;command_timeout_ms 不适用,因为发现尚未开始。发现期限仅涵盖端点发现。随后的 WebSocket 握手和 Target.attachToTarget 各自使用 command_timeout_ms。capture.limits 控制保留事件和网络内容的预算;它不会改变这些存活期限。

最小化启动示例

将此保存为 browser-scenario.json,将可执行文件路径替换为已安装的 Chrome 系列浏览器,并将 URL 替换为你正在运行的 fixture:

{
  "browser": {
    "mode": "launch",
    "executable_path": "/absolute/path/to/chromium"
  },
  "start_url": { "url": "http://127.0.0.1:3000" },
  "actions": [
    { "step_id": "settle", "action": "wait_for_timeout", "duration_ms": 100 }
  ]
}
rea capture-browser-scenario ./browser-scenario.json --json > browser-capture.json

默认捕获包含最终 URL。仅在它们有助于回答问题时才选择 DOM、截图或事件。REA 会为此请求启动并清理浏览器配置文件;它不会启动你的 fixture 服务器或安装浏览器。通过模式验证的场景并不能证明可执行文件或目标在主机上可用。

交互示例:捕获可见结果

默认的最终 URL 回答的是“页面最终到了哪里?”,而不是“页面显示了什么?”。许多交互(例如在当前位置渲染结果的搜索按钮)会改变页面但不会改变 URL。只使用默认 url 工件的捕获随后会报告点击成功且 URL 未变,这无法告诉你结果是否出现。

请求能够回答你问题的工件,并等待你想要检查的状态。此场景点击一个 Search 按钮,等待当前搜索结果,并捕获最终的 DOM、无障碍树和 URL。它假设本地页面在加载时清除 #results 的 data-state,并在渲染当前响应后将其设置为 ready。根据你的页面调整选择器和就绪标记;已经可见的容器或先前的结果是不够的。

{
  "browser": {
    "mode": "launch",
    "executable_path": "/absolute/path/to/chromium"
  },
  "start_url": { "url": "http://127.0.0.1:3000" },
  "actions": [
    {
      "step_id": "search",
      "action": "click",
      "locator": { "kind": "css", "selector": "#search" },
      "timeout_ms": 10000
    },
    {
      "step_id": "results-ready",
      "action": "wait_for",
      "locator": { "kind": "css", "selector": "#results[data-state='ready']" },
      "state": "visible",
      "timeout_ms": 10000
    }
  ],
  "capture": { "at_end": ["dom", "accessibility", "url"] }
}

将其保存为 browser-search.json,并以与最小示例相同的方式运行它:

rea capture-browser-scenario ./browser-search.json --json > browser-search-capture.json

通过 MCP 调用 capture_browser_scenario 时,将同一对象作为其参数传递。最终步骤的 DOM 和可访问性工件会内联保留捕获时点的页面状态。REA 仍会启动并清理浏览器配置文件。

动作的 timeout_ms 和 wait_for_timeout.duration_ms 接受 1 到 2,147,483,647 毫秒之间的整数,与底层计时器表示一致。更大的值会在浏览器启动前收到输入错误。省略 timeout_ms 会使动作保持挂起状态,直到完成或请求取消。

点击动作的 timeout_ms 覆盖点击操作本身。非导航性质的 fetch 或延迟渲染可能在此之后才完成,因此在最终捕获前使用 wait_for 观察页面特定的就绪条件。同步更新的页面可以省略该等待。REA 不会根据捕获选择来推断应用的就绪状态。

根据所提出的问题选择捕获内容:

  • dom 和 accessibility:页面渲染的文本或结构,例如搜索结果、验证消息或更新后的列表。可访问性树通常是读取可见文本的更紧凑方式。
  • screenshot:视觉布局或样式,或未以文本形式暴露的内容,例如 canvas。
  • history 或 storage:导航历史,或交互写入的 cookie 和 Web 存储。仅当这些状态是所问的问题时才请求它们;它们绝不会被隐式捕获。
  • 使用 after_each_step 而不是 at_end:多步骤场景中各动作之间的中间状态。
  • events(console、page-errors、network 等):页面达到其结果的途径,例如搜索发出的请求或页面记录的错误。只选择你需要的 event 类别。

捕获状态按工件逐一报告。未请求的工件为 not_requested,表示 REA 没有查看,而不是页面没有可展示的内容。步骤的 completeness 为 complete 时,仅涵盖已请求的部分。仅含 URL 的捕获可以是 complete,但仍无法回答关于渲染内容的问题,因此应添加相关工件并重新运行场景,而不是根据其缺失来推断。捕获完整性也不断言页面的后台工作已经完成。

浏览器边界是约定的一部分。启动模式需要调用方选择的可执行文件,并且始终使用提供方拥有的临时配置文件,该配置文件会在清理期间被关闭并删除。连接模式仅接受显式端口的回环 CDP 端点。Playwright 驱动保持这一区分:它会关闭并删除提供方拥有的配置文件,但对外部 CDP 浏览器仅断开连接。真实浏览器验证会检查这两种结果,并确认所连接的外部浏览器仍然存活。

该清理需要 REA 保持运行。突然终止(包括 Windows 进程树终止或 POSIX SIGKILL)可能会留下启动配置文件和 Playwright 工件。配置文件可能保留 cookie、历史记录、缓存和源存储。后续的 REA 运行目前不会回收这些路径。Playwright 1.58.2 在启动准备失败且浏览器进程尚不存在时(例如可执行文件缺失)也可能留下工件目录。POSIX 包装器可以通过 SIGINT 或 SIGTERM 请求等待中的取消;Windows 进程终止不是等效的优雅取消通道。不要仅凭目录前缀或创建时间来识别拥有的工件。有关剩余的恢复与包装器取消工作,请参阅 #4659。

保持浏览器存活并不能保证其模拟状态能够存续。当页面 CDP 会话断开时,Chromium 可能清除调用方拥有的 navigator.platform 模拟,包括那些未发送任何模拟命令的会话。REA 无法快照其他客户端的完整 user-agent/client-hint 设置,也不会重放部分重建的结果。请使用启动模式将此影响限制在提供方拥有的浏览器内,或让外部拥有的客户端在捕获后重新应用其完整配置。连接的捕获会报告此限制;请参阅 issue #4089。

environment.locale 在两种模式下都设置 Intl 格式化。启动模式还会设置 navigator.language、navigator.languages 以及请求的 Accept-Language。连接模式保留调用方的页面和请求语言;每次连接的捕获都会指出此 locale 限制,包括保留的部分观察结果。当应用必须通过 navigator 值或请求头来选择所请求的语言时,请使用启动模式。工件完整性涵盖所选的观察结果,而不是启动模式与连接模式语言环境的等价性。

HTTP(S) URL 可以直接包含普通查询值和片段。当某个值需要引用已声明的、由环境支持的机密时,请使用有序的结构化查询条目。表单值、存储值和 cookie 值要么是字面字符串,要么是已声明的机密引用;请求步骤的请求头还可以从页面读取 cookie 或 local storage 值。原始 URL 中的 userinfo 凭据会被拒绝。捕获到的凭据请求头值不会被保留,已声明的机密值会被自动脱敏。可以仅为脱敏匹配到的观察内容而声明机密;动作、URL、存储值中的每个机密引用仍然需要声明。普通查询值和片段保持不变。持久化结果会将已解析的机密值替换为对应的机密引用。

确切的 start_url、导航目标以及存储种子(storage-seed)源定义了场景的输入;普通的页面请求由浏览器处理。存储 cookie 在每次捕获时都限定于当时的页面 URL。不支持的 action 标签、未知字段、重复的步骤 ID 以及未声明的机密引用都会导致验证失败。

Cookie 会在初始导航之前写入。本地存储条目按浏览器存储分区各预置一次;会话存储条目在每个受检测的顶级浏览上下文中按源各预置一次。预置发生在所选页面及其受检测框架的第一批文档脚本执行之前。由后续操作首次到达的源会在首次访问时收到其预置数据。应用的更新、删除和空值随后会在同一区域内的后续导航中持续存在;新场景会以自身的预置数据开始。分区框架即使共享同一个源,也可以接收独立的本地存储预置数据。脚本前机制使用 CDP 调试器插桩。观察者仅恢复其顶级框架属于其私有预置脚本的暂停;页面断点和 debugger 语句仍受所连接调试器客户端的控制。

如果 Chromium 将私有隔离世界的源留空,REA 会在选择预置数据之前查找该确切框架所报告的安全源。它不会从框架 URL 或序列化的存储键推断源。框架身份或安全源无法解析时,初始化将失败,而不会在未知初始状态下静默运行。

弹窗发现并不能确定其第一个脚本被所选页面的存储插桩所暂停。当声明了非 Cookie 预置数据并发现弹窗时,捕获会保留已完成的观察结果,并将 storage 覆盖标记为缺失,且不具备相等性资格。即使未选择弹窗生命周期事件,这一点也适用。目前不保证弹窗首个脚本的存储初始化。

结果是 Evidence,包含一个初始状态,随后是每个已声明操作的一条记录。每一步都会报告操作状态、经过时间、经清理的 URL、事件边界,以及针对截图、DOM、可访问性、URL、历史和存储的独立类型化捕获状态。在失败或被取消的操作之后的操作不会运行,并记录为 cancelled,附带未执行错误。控制台、页面错误、网络、WebSocket、框架、Worker、弹窗和已取消下载事件均归属于相应步骤。保留的事件和请求的产物会内联返回;被省略的事件按类型计数,并标识受影响的网络事务。

事件和所选网络内容的保留受 capture.limits 下由调用者选择的限制约束。省略的值使用下面的默认值;每个提供的值必须是正安全整数,调用者可以提高或降低默认值。字节限制是对保留事件值的保守分配成本估算,包括 UTF-16 存储和 JSON 扩展,而不是序列化字节数,也不是对确切进程内存的承诺。events.omissions.by_kind 会报告被省略事件类型的计数和原因。其 network_transactions 行标识受影响的事务 ID 和被省略的事件类型;network_transaction_range 标识超出事务限制的连续 ID。省略元数据受所选事务限制的约束。其他缺失部分保持显式,并使捕获不具备相等性声明资格。

默认限制为:10,000 个事件,单个事件预估分配 4 MiB,保留事件总计预估分配 16 MiB,1,024 个网络事务,每个事务阶段 64 KiB 的响应体字节,响应体编辑/base64 预估分配 1 MiB,保留的头部值 1 MiB,网络元数据 1 MiB。相同的限制也包含在捕获 Evidence 中。

Playwright 会在 REA 检查响应体字节长度之前将响应体物化;max_network_body_bytes 限制后续保留副本,并不限制浏览器 API 读取本身。超过所选限制的响应体会具有显式的 body-size-limit 或 body-allocation-limit 不可用状态,并附有观测值和允许值。

其他实现限制包括:16 个并发网络内容读取、每个提供程序 API 读取的 5 秒截止时间,以及 2,048 个活动事件监听器所有者。这些不可由调用者选择。并发限制会对已接纳的内容作业排队,本身不会省略观察结果;提供程序读取超时会产生类型化的不可用状态,而超出监听器容量则会产生事件类型省略计数。当当前可用的事件预算已拒绝该响应体时,预检会避免响应体编辑和 base64 副本。并发读取可能各自在其事件被保留之前通过该检查;它们的保留副本工作量受所选每响应体分配限制的 16 倍约束。Playwright 响应 Buffer 本身会在 REA 应用该限制之前被物化。请求的产物和保留的事件会在声明限制内内联返回。附加模式捕获还会声明不可避免的附加前事件间隙。

所选事件族会在根页面和已发现的子级弹窗(包括嵌套弹窗)上被观察。仅当选择了 popups 时,才会发出弹窗生命周期记录。启动模式的网络观察从导航前所属上下文开始,因此弹窗文档请求、重定向和响应会被包含一次。连接的上下文可能包含无关的标签页:只有其框架属于根页面或已发现弹窗的网络事件才会被保留。在共享上下文中,没有框架的初始弹窗请求无法归属,因此会被省略;附加覆盖仍然不完整。

Playwright 会在新弹窗页面的首次导航提交后报告这些页面。它会缓冲早期的控制台消息和页面错误,但固定的 API 无法恢复所有更早的框架、Worker、WebSocket 或下载事件。如果选择了这些事件族之一并发现弹窗,REA 会在 limitations 中报告该事件族的间隙,将 events 标记为缺失,并禁用相等性资格。后续事件仍然可用;不会根据弹窗当前 URL 合成初始导航。

每个请求都会指定确切的启动可执行文件或回环 CDP 端点、目标、操作和捕获行为。当场景需要时,会包含源过滤器和环境变量名称。启动模式持有临时配置文件;连接模式会与所选外部浏览器断开连接,但不会关闭它。请求本身定义操作;没有额外的 REA 授权步骤。浏览器和主机仍会执行各自的访问和进程规则。

请求步骤

request 步骤会通过页面自身的 fetch 从页面发送一个 HTTP 请求,因此浏览器会附带会话的 cookie,并且该请求会像应用程序发出的任何请求一样被记录在该步骤的网络事件中。当为每次调用驱动 UI 比直接调用 UI 所调用的端点更慢或更不精确时,可使用它来记录应用程序的 API(包括写操作)。它是唯一无需用户手势即可更改应用程序状态的操作,因此仅可对允许更改数据的实例运行包含写请求的场景。以下场景创建一项:

{
  "browser": {
    "mode": "launch",
    "executable_path": "/absolute/path/to/chromium"
  },
  "start_url": { "url": "http://127.0.0.1:3000" },
  "actions": [
    {
      "step_id": "create-item",
      "action": "request",
      "method": "POST",
      "path": "/api/items",
      "query": [
        { "name": "notify", "value": { "source": "literal", "value": "false" } }
      ],
      "headers": [
        {
          "name": "X-CSRFToken",
          "value": { "source": "cookie", "name": "csrftoken" }
        },
        {
          "name": "Authorization",
          "value": {
            "source": "local_storage",
            "key": "auth",
            "json_pointer": "/token"
          },
          "prefix": "Bearer "
        }
      ],
      "json": { "title": "Scratch item 1" },
      "expected_status": [201],
      "timeout_ms": 10000
    }
  ]
}
  • method 为 GET、HEAD、POST、PUT、PATCH 或 DELETE。json 是请求体,除非某个请求头已设置 Content-Type,否则将以 Content-Type: application/json 发送;GET 或 HEAD 步骤不能携带请求体。
  • path 以一个 / 开头,且必须解析到起始 URL 的源(origin),验证会在浏览器启动前检查这一点。步骤运行时,页面必须处于该源上,请求发送时会在页面内再次检查;如果之前的操作使页面离开了该源,请先添加一个 goto 步骤。path 中的查询串按原样发送(片段(fragment)不会发送),query 条目会按顺序追加在其后,并可引用已声明的机密。
  • 请求步骤不跟随重定向:重定向响应会使步骤失败,且不会向重定向所指向的位置发送任何内容,不过重定向所应答的写入可能已经生效。expected_status 不能指定 3xx 状态。请将路径设置为重定向所指向的位置;如果选择了请求头值,捕获会记录其 Location。
  • 请求头值可以是字面量、已声明的机密、浏览器为请求 URL 持有的 cookie({"source": "cookie", "name"},包括 HttpOnly cookie,页面脚本在请求运行期间可以看到其值),或起始源上的本地存储条目({"source": "local_storage", "key"},可选地带有 json_pointer,指向存储的 JSON 值中的字符串或数字)。prefix 会在该值之前发送。从页面读取的值会像已声明的机密一样被脱敏,在步骤读取之后生成的记录中显示为 [REDACTED:page:<step_id>.<header>];较早记录的响应(例如返回令牌的登录响应)会保留该值。再次读取的值会保留其第一个标记,短于 8 个字符的值不会被脱敏,因为脱敏它会重写整个捕获中的 ID、端口和主机。值的脱敏会按其存储形式以及 fetch 发送形式进行,不包含周围的空格和制表符。缺少 cookie、键或指针会使步骤失败。读取页面值会提高脱敏后续每个请求体的预估成本,因此 max_network_body_allocation_bytes(默认为 1 MiB)内保留的请求体会变小:在读取了 32 字符的 X-CSRFToken 值后,超过约 47 KiB 的请求体会以 unavailable 形式保留。当应用程序的响应较大时,请提高 capture.limits。
  • 验证会拒绝以下情况:步骤中重复给出的请求头名称;页面 fetch 无法设置的请求头名称,如 Cookie、Origin、Host 或 Sec-*;以及 Chrome 会丢弃的 User-Agent。验证还拒绝字面量和前缀中的换行符及其他 ASCII 控制字符;当机密或页面值包含这些字符时,步骤会在发送前失败。
  • 步骤在完整读取响应后完成。设置了 expected_status 时,任何其他状态都会使步骤失败,后续操作会被记录为 cancelled;未设置时,任何响应都会完成该步骤。网络错误会使步骤失败。timeout_ms 涵盖读取请求头值和发送请求的过程:超时或被取消的步骤,如果在其请求发送之前发生,则不会发送任何内容,但已发送的请求在步骤失败后仍可能到达应用程序。绝对截止时间还涵盖排在页面工作之后排队的浏览器评估(browser evaluation)。临时路由会在网络分发时再次检查取消状态,并在请求评估完成时移除。拒绝由 service worker 控制的页面,因为其 fetch 可能绕过该分发门控;对于请求步骤请使用默认的 blocked service-worker 策略。

请求在页面的主世界(main world)中运行,因此包装了 fetch 的应用程序可以看到它,并且它会与其他页面请求一样经过相同的网络观察:选择网络内容(请求体或请求头值)来记录它。包含请求步骤的捕获会在 limitations 中说明这些步骤是通过页面的 fetch 发送其声明的请求的。不含请求步骤的场景保持不变。

网络证据

capture.events: ["network"] 会保留请求、响应、完成、失败和未完成请求的元数据。可独立选择要记录的内容:

{
  "browser": {
    "mode": "launch",
    "executable_path": "/absolute/path/to/chromium"
  },
  "start_url": { "url": "http://127.0.0.1:3000" },
  "actions": [
    {
      "step_id": "run",
      "action": "click",
      "locator": { "kind": "css", "selector": "#start" },
      "timeout_ms": 10000
    },
    {
      "step_id": "ready",
      "action": "wait_for",
      "locator": { "kind": "css", "selector": "#done" },
      "state": "visible",
      "timeout_ms": 10000
    }
  ],
  "capture": {
    "network": {
      "request_body": true,
      "response_body": true,
      "header_values": true
    }
  }
}

所有三个选项默认为 false。任何选定的网络内容也会启用网络元数据观察;此时在 events 中列出 network 是可选的。每个被观察的请求对象都会收到一个捕获作用域内的 transaction_id。每个 request 事件都携带 observed_at,即 REA 观察到请求开始的挂钟时间。Playwright 仅在请求结束时才报告浏览器自身的启动时间,因此当浏览器报告这些信息时,每个 request-finished 或 request-failed 事件都会携带 started_at(浏览器的挂钟启动时间)和 elapsed_ms(从该启动到响应结束所经过的时间)。环境从这些字段中提取时间场景记录,其方式与 HAR 条目计时相同:在浏览器报告了启动时间时使用浏览器的启动时间,否则使用 observed_at——由于 REA 在读取其他响应,它比实际启动时间滞后最多数十毫秒——并在浏览器未报告时长时使用 0 作为持续时间。在这些字段存在之前制作的捕获不包含这些字段,并按记录顺序读取。早于这些字段的 REA 版本会拒绝携带这些字段的捕获。场景比较忽略全部三个字段(ignore_network_request_timing)。共享同一 URL 的请求仍被视为不同的请求。重定向仅在观察到前一事务时才与之链接;null 前驱并不证明不存在更早的跳转。请求事件要求两个事务字段均存在;缺少 ID 属于格式错误的捕获数据。当 Playwright 未观察到前驱时,redirected_from_transaction_state 记录 root;当存在前驱但该前驱位于捕获范围之外时,记录 unknown。被链接的前驱通过 redirected_from_transaction_id 携带,且不带状态字段。没有状态字段的旧捕获保留保守含义:null 前驱表示 unknown。

scenario.network_content 记录解析后的内容选择。比较具有不同选择项的捕获时,网络证据将报告为 unknown,并附有恢复说明。仅捕获更多数据本身并不能证明网站发生了变化。

network-content 事件通过 source_event_sequence 引用确切的请求或响应事件。它们包含独立类型化的标头状态和正文状态。捕获的正文保留 base64 字节、字节数、媒体类型以及所保留字节的 SHA-256。请求字节是 Playwright 暴露出的字节;not_exposed 请求正文不能证明正文不存在,也不能保证 multipart 文件被完整覆盖。响应字节是 Playwright 的 Response.body() API 的结果。当 CDP 没有响应正文时,Playwright 可能会通过对该请求 URL 的带凭据后续加载来满足该 API。事件和事务引用标识了关联的 Playwright 响应对象,但不验证所保留的字节是否来自原始网络事务。成功选定的响应正文会在捕获中包含这一限制。这些字节不是压缩后的线路传输表示。

标头保留生产者顺序和重复项。Authorization、Proxy-Authorization、Cookie 和 Set-Cookie 的值为 null,并带有 redacted: true;普通值保持不变。声明的 UTF-8 秘密值及其标准的 URI/表单或 JSON 转义表示会被替换,而不会解码无关的二进制字节。其他编码不会被解释。摘要描述脱敏后所保留的表示形式。选定的正文字节限制同样约束秘密值扩展之后、base64 序列化之前所保留的表示形式的上限。凭据标头值在保留标头分配核算之前即被丢弃;它们的名称和 null 投影仍然计入。传输/API 已经物化了传入的正文和标头,因此这些保留限制不会约束生产者端的输入分配。

只读取已完成的响应。生产者观察到的终止状态会被独立记录,无论其事件元数据是否符合保留预算。省略完成事件不能将已结束的事务变成未结束的事务。当最后一步结束时,观察停止;仍在飞行中的请求会发出 request-unfinished 事件,其观察到的响应具有明确不可用的正文。已获准的内容读取会在浏览器清理之前排空。最多同时运行 16 个读取;排队的工作受选定的网络事务限制及其请求或响应事件是否成功获准的约束。每次提供方 API 读取一旦开始,就有自己的五秒超时,因此队列等待会增加总排空和清理时间;这并不是五秒的总捕获截止时间。取消操作会中断正在进行的读取,排队的任务会产生 cancelled 状态,而不会调用其提供方 API。此排空不会等待长期存在的未完成流式连接,也不会重新获取未完成的响应正文。失败或未暴露的读取会保留各内容的状态,并使所选事件覆盖不完整。正文事件可能在后续步骤或最终排空期间到达;事件边界描述的是步骤期间的接收情况,而源事件引用则确定事务阶段。与事务中的其他所有事件一样,比较将内容归属于发送请求的那一步(当该请求未被观察时,归属于接收步骤),并将序列引用排除在归一化之外。Playwright 场景捕获不提供发起者堆栈;被动 CDP 观察保留其自身的发起者证据。

存储值指纹

新的存储快照包含 fingerprint_version: 2。非秘密、格式良好的 JavaScript 字符串保留其基于 UTF-8 的 SHA-256。具有未配对 UTF-16 代理项的字符串使用基于原始 0xff 字节后接其精确 UTF-16LE 代码单元的 SHA-256;此前缀不可能出现在 UTF-8 中。声明的秘密值保持为 redacted-secret,摘要为 null,且没有第二个或旧版摘要。

缺少版本号表示旧版 UTF-8 指纹,这些指纹替换了未配对的代理项,因此无法确定值的同一性。包含任何旧版哈希值的存储会被保守地比较为 unknown,包括旧版对旧版的比较;要可靠地进行比较,请重新捕获两侧。这也保守地不揭示一些原本可证明的旧版差异。空存储或秘密值完全脱敏的存储保持其现有的比较语义。版本在归一化之前被检查,并排除在内容比较之外:仅更改编码元数据本身并不能证明网站发生了变化。现有捕获及其完整性记录不会被重写。

场景比较

compare_web_captures 和 compare-web-captures 也接受两个完整的浏览器场景捕获。步骤仅通过精确、唯一的 step_id 对齐。重复的 ID、缺失的对应项、更改的操作类型以及不兼容的浏览器/来源捕获上下文都会作为对齐失败返回。它们阻止做出“未更改”的判定;观察到的差异仍可能证明结果已更改。

比较会记录其完整的规范化承诺和 SHA-256 摘要。内置规则排除步骤耗时和事件序列/索引字段,按内容摘要比较截图,并通过规范化文本比较 DOM/可访问性捕获。网络事件按事务分组,不包含其捕获本地的事务 ID,归属到发送请求的步骤,并在调用方规则应用后作为无序集合进行比较,因为并发响应以任意顺序到达,且可能落入后续步骤;重定向保留完整的前驱谱系,包括每一跳自身的请求、响应结果和选定的网络内容。每个事务中捕获本地的 ID、序列/索引字段、内容来源序列锚点以及请求时间(observed_at、started_at、elapsed_ms)都会被移除。调用方规则在哈希之前对保留的字符串进行规范化。preserve_redirect_transaction_ancestry 内置规则为每一跳生成 { events, redirected_from } 的规范 SHA-256 指纹:events 是其规范化事件 SHA-256 摘要的排序列表,redirected_from 是其前驱指纹,对于根节点则为 null。选定的事务指纹经过排序,用于该步骤的网络比较。临时数字图索引仅作为查找引用,不进入最终摘要。缺失或循环的前驱证据会产生“未知”可比性,而不是“未更改”的判定。其他事件保持接收顺序。可选的调用方规则是对命名工件类型的受限精确字面量替换。规则和工件类型列表在应用前会进行规范排序,使相同策略无论输入顺序如何都可复现。规则仅作用于已经脱敏的持久捕获字段,并会保留在结果中。

Cookie、本地存储和会话存储在调用方字面量规则应用后,作为无序清单进行比较。仅凭枚举顺序不能确定存储变更。规范排序使用完整条目,即使脱敏或字面量规则导致名称冲突,也会保留 Cookie 作用域/标志、值指纹和重复条目。捕获的存储数组保留其原始顺序。compare_storage_entries_as_unordered_collection 内置规则参与规范化承诺;因此新的存储比较摘要不同于早期对顺序敏感的比较。ignore_network_request_timing 内置规则同样加入该承诺,因此规范化摘要不同于该规则之前的比较,尽管带与不带请求时间的捕获比较结果相同。

每个对齐的步骤报告“已更改”、“未更改”或“未知”。已更改和未知的操作、截图、DOM、可访问性、URL、历史记录、存储和事件工件都会携带其规范化后的前后摘要和捕获状态。每个对齐步骤都会内联返回每个已更改或未知的工件记录。缺失、截断或不匹配的捕获覆盖范围保持“未知”,而不是被视为相等。

scenario.event_families 记录排序、去重后的 capture.events 选择。选择不同的捕获会将事件证据报告为“未知”,并附带恢复说明,即使没有选中的事件族发出事件。在此字段存在之前写入的捕获会省略该字段,因此其事件证据也是“未知”的。保留捕获的读取器接受这种省略,而不会从观察到的事件中推导选择;其他工件族仍可比较。当前的捕获生产者仍然要求并发布选定的事件族。清单、环境和脚本导出读取器共享保留捕获的准入规则,并保留相同的缺失事实。

网络规范化同样承诺移除捕获本地的事务 ID、归属到发出请求的步骤,以及无序的事务比较。这些内置规则标识参与规范化摘要。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 rea2/rea