# 补环境代码可读性与中文注释规范 每次生成、重构或交付 `case/result/` 中的补环境代码前读取本文件。目标是让最终代码 **简洁、可读、可维护、中文注释正常显示**,并避免生成压缩、堆叠、无注释或乱码代码。 ## 硬性原则 1. **先规划目录,再写代码** 写代码前先列出最终目录、每个文件职责和调用关系。不要边写边临时堆文件。 2. **按职责拆模块** 不要把所有补环境逻辑塞进一个 `env.js`。推荐结构: ```text case/result/ ├── final.js ├── 最终项目总结.md └── src/ ├── env/ │ ├── install-env.js │ ├── native-api.js │ ├── manifest.js │ ├── core/ │ │ ├── descriptors.js │ │ └── cookie-store.js │ ├── browser-objects/ │ │ ├── window.js │ │ ├── navigator.js │ │ ├── document.js │ │ ├── location.js │ │ ├── screen.js │ │ ├── xhr.js │ │ └── storage.js │ └── fingerprint/ │ ├── canvas.js │ ├── webgl.js │ └── dom-geometry.js ├── target/ │ └── entry.js ├── request/ │ └── client.js └── utils/ └── normalize.js ``` 如果需要 Node 诊断执行器、runtime probe 或 Akamai / Shape / F5 等高强度检测的本地执行器,应继续拆成下列职责,而不是把所有 WebAPI 放进 `src/signer/*probe*.js`: ```text case/result/src/ ├── signer/ │ ├── runtime_signer.py │ └── akamai_runtime_probe.js # 只做入口和编排,建议少于 150 行 └── node-runtime/ ├── bootstrap/ │ ├── read-stdin.js │ ├── execute-script.js │ └── result-recorder.js ├── utils/ │ ├── hash.js │ ├── sanitize-url.js │ └── body-summary.js └── env/ ├── install-env.js ├── native-api.js ├── node-leakage.js ├── browser-objects/ │ ├── window.js │ ├── navigator.js │ ├── document.js │ ├── location.js │ ├── history.js │ ├── screen.js │ ├── storage.js │ ├── performance.js │ └── events.js ├── fingerprint/ │ ├── canvas.js │ ├── webgl.js │ ├── audio.js │ └── dom-geometry.js └── network/ ├── xhr.js ├── fetch.js └── beacon.js ``` 3. **signer / probe 不得承载补环境主体** `src/signer/`、`src/request/`、`src/resources/`、`runtime_probe.js`、`probe.js`、`runner.js`、`diagnostic.js` 等文件只允许做编排、进程入口、请求封装、资源刷新或安全摘要输出;只要文件内实现了 `navigator`、`document`、`window`、`screen`、`Storage`、`XMLHttpRequest`、`fetch`、`Canvas`、`WebGL`、`Audio`、`performance`、DOM 构造链、事件系统等浏览器 WebAPI 主体,就必须拆入 `src/env/` 或 `src/node-runtime/env/` 下的真实模块。不得因为文件名叫 probe、runtime、signer 或 diagnostic 就豁免模块化要求。 4. **补环境代码必须可读** 禁止压缩代码、单行堆叠多个语句、过度匿名函数、无意义变量名、超长函数和超深嵌套。原始目标 bundle 如必须保留,应放到 `src/target/original/` 或等价目录,并与手写补环境代码分离。 5. **中文注释必须正常显示** 所有交付源码使用 UTF-8 无 BOM。不要使用未指定编码的 PowerShell / cmd 重定向写中文源码。中文注释中不得出现问号、连续问号或替换字符。 6. **WebAPI 实现必须可审计** 补环境源码不得把 WebAPI 写成无法审查的一行堆叠代码。禁止一行连续堆多个 `Object.defineProperty`、`Object.defineProperties`、`Object.assign`、函数定义或对象字面量方法。`install-env.js` 只负责装配,不应成为巨型文件;`navigator`、`screen`、`document`、`storage`、`indexedDB`、`canvas`、`webgl`、`events`、`worker`、`request` 等应按职责拆模块。 7. **普通 WebAPI 函数必须 addon-first** 不要在 WebAPI 主路径中直接写 `ctx.Blob = function(){}`、`ctx.indexedDB = { open() {} }`、`prototype = { getContext() {} }` 或 `ctx.URL.createObjectURL = function(){}`。普通函数也要提取为具名函数,并通过 addon-first helper 包装后再安装 descriptor。addon 不可用时才用 `NativeProtect` / JS fallback,并在注释与阶段报告中说明原因。 8. **浏览器对象私有状态不得泄露** 不要在浏览器可见对象上写 `this.__readyState`、`this._headers`、`__children`、`__parentNode`、`__responseHeaders`、`_store` 等内部字段。non-enumerable 也会被 `Object.getOwnPropertyNames`、`Reflect.ownKeys` 和 descriptor 检测到。内部状态优先使用 addon / xbs private API,其次使用模块级 `WeakMap`;XHR、fetch、Headers、Response、DOM 节点、EventTarget、Storage、Performance 等都适用。 9. **XHR/fetch 必须拆为网络环境模块并桥接 Session** `XMLHttpRequest` / `fetch` / `Request` / `Response` / `Headers` / `sendBeacon` 的浏览器语义放在 `src/env/network/` 或 `src/node-runtime/env/network/`。需要真实请求时,网络模块只能调用 live session bridge;不得在 env 里直接调用 Node 宿主 `fetch`、`http`、`https`、`axios`、`undici`。fixture/default response 只能保留为 `offline-fixture` 诊断模式。 10. **构造函数错误必须可追溯** 构造函数的报错类型和 message 必须来自目标浏览器采样。不要写泛化 `throw new TypeError('Illegal constructor')`;需要在注释、fixture 或阶段报告中说明该错误来自哪个浏览器、哪个调用方式。 11. **addon 实例不再二次标记对象类型** `markObjectType` 不是批准 API。addon 构造函数 / `createProtoChains` 实例工厂创建出的实例本身应已处理 `Object.prototype.toString`;只有 JS fallback 普通对象才允许 `markObjectToString` / `Symbol.toStringTag`,并必须写明 fallback 原因。 12. **注释说明“为什么”和“来源”** 注释不是逐行翻译代码,而是说明模块职责、浏览器样本来源、RuyiTrace 证据、fixture 匹配规则、addon-first 决策和 fallback 原因。 13. **isolated-vm 也必须文件化模块交付** isolated-vm 底层确实需要把源码字符串交给 V8,但最终项目不得把补环境源码写成大段 `String.raw`、`CORE_SCRIPT`、`BROWSER_OBJECTS_SCRIPT`、`*_SCRIPT` 聚合字符串。正确做法是把 `navigator.js`、`document.js`、`window.js`、`canvas.js`、`webgl.js` 等作为真实源码文件保存,由宿主侧 runtime 使用 `fs.readFileSync` 读取,再通过 `compileScriptSync(source, { filename })` 或等价方法注入同一个 isolated-vm Context。只有少量 bootstrap 片段允许字符串形式,且必须少于 40 行、不能承载主要 WebAPI 实现,并写明原因。 ## 中文注释要求 必须写中文注释的位置: - 文件顶部:说明模块职责、输入来源和边界。 - 每个环境对象模块:说明模拟哪个浏览器对象。 - 关键 getter / setter:说明真实值来自 fixture、RuyiTrace、请求样本还是用户配置。 - native-like 函数和构造函数:说明挂载位置、浏览器行为和 addon-first 处理。 - fallback 分支:说明 addon 不可用、ABI 不兼容、API 调用失败或用户明确豁免的原因。 - 指纹值回放函数:说明匹配 key、采样来源和缺失样本时的处理。 - 加密参数生成入口:说明输入、输出和 fixtures 验证方式。 禁止: ```js // 不合格:用问句解释实现 // 不合格:连续问号乱码示例 // 临时先这样 ``` 推荐: ```js // 安装 Navigator 相关环境对象,字段值来自浏览器 fixture function installNavigatorEnv(globalObject, fixture, nativeApi) { // Navigator 构造函数错误信息来自 constructor-errors.fixture.json 中的浏览器采样 const Navigator = nativeApi.createNativeConstructor('Navigator', 0, function Navigator(isNew) { if (isNew) { return nativeApi.throwIllegalConstructor('Navigator'); } return nativeApi.throwIllegalConstructor('Navigator'); }); // userAgent 使用真实浏览器采样值,不在 Node.js 中伪造默认值 nativeApi.defineNativeGetter(Navigator.prototype, 'userAgent', function getUserAgent() { return fixture.browser.userAgent; }); } ``` ## 命名与函数拆分 - 函数名使用业务含义,例如 `installNavigatorEnv`、`createStorageArea`、`replayCanvasToDataURL`。 - 避免 `a`、`b`、`fn1`、`tmp`、`xxx` 作为长期变量名。 - 单函数建议不超过 90 行。 - 单文件建议不超过 500 行。 - 单行建议不超过 180 字符。 - 属性描述符、构造函数 callback、WebAPI 方法安装、`Object.assign`、`try/catch` 较长逻辑不得压成一行。 - 嵌套层级尽量不超过 6 层;复杂逻辑用提前返回或拆函数。 ## isolated-vm 文件化加载示例 推荐由 `install-env.js` 只负责装配,由 runtime 顺序读取文件: ```js const ENV_FILES = [ 'src/env/core/descriptors.js', 'src/env/browser-objects/window.js', 'src/env/browser-objects/navigator.js', 'src/env/browser-objects/document.js', 'src/env/fingerprint/canvas.js', 'src/env/fingerprint/webgl.js', ]; function installEnv(runtime, config) { runtime.setCopy('__ENV_CONFIG__', config); runtime.runFiles(ENV_FILES); runtime.runFile('src/env/install-all.js'); } ``` 每个文件都应是正常 JavaScript 文件,文件顶部用中文说明模块职责。Context 内不需要也不应该直接 `require('./navigator.js')`;由宿主 runtime 读取文件内容后注入,既保持 isolated-vm 隔离,又保留源码文件结构、中文注释和 filename 栈定位。 ## 最终交付前检查 交付前必须运行: ```bash node scripts/check_code_quality.js --case-dir case --markdown node scripts/check_object_shape_audit.js --case-dir case --markdown node scripts/check_xhr_fetch_session_bridge.js --case-dir case --markdown node scripts/check_trace_runtime_conformance.js --case-dir case --markdown node scripts/check_xhr_fetch_semantics.js --case-dir case --require --require-no-send --out case/tmp/xhr-fetch-semantics-audit.json --markdown node scripts/check_environment_closure.js --case-dir case --before-real-request --markdown node scripts/check_webapi_addon_coverage.js --case-dir case --markdown node scripts/check_env_realism.js --case-dir case --markdown node scripts/check_final_artifact.js --case-dir case --markdown ``` 如果 `check_code_quality.js` 或 `check_webapi_addon_coverage.js` 失败,必须立即停止后续补环境验证、真实请求验证和交付;先重构代码、拆分 signer/probe 中的 WebAPI 主体、修复中文注释、编码问题、普通 WebAPI 函数、普通对象和宿主透传,再重新运行检查。不得以“当前请求能跑通”“只是诊断脚本”“只是 probe”为理由跳过失败项。 ## 常见修复方式 | 问题 | 修复方式 | |---|---| | 单个 `env.js` 太大 | 按 navigator、document、storage、fingerprint 拆模块 | | isolated-vm 生成大段 `String.raw` | 改为真实 `.js` 文件,由 runtime.runFile / runtime.runFiles 读取后注入 Context | | `script-browser-objects.js` / `script-core.js` 承载主要补环境 | 拆成 `browser-objects/navigator.js`、`document.js`、`window.js`、`fingerprint/canvas.js` 等模块 | | 没有中文注释 | 增加文件头、对象说明、来源说明和 fallback 原因 | | 中文注释有问号或乱码 | 用 UTF-8 重新写入,避免默认 shell 重定向 | | 函数过长 | 拆成解析 fixture、创建构造函数、安装属性、安装实例四步 | | 普通赋值太多 | 改为统一 helper,例如 `defineValue`、`defineNativeGetter` | | 普通 WebAPI 函数 | 提取具名 callback,用 `addon.createNativeFunction` 或 addon-first helper 包装 | | 普通 WebAPI 对象 | 用 `createProtoChains` 建立构造函数、prototype 和实例工厂,再安装 descriptor | | 泛化 `Illegal constructor` | 先采样浏览器错误类型和 message,再用 `throwBrowserTypeError` / `throwIllegalConstructor` 等 helper 复现 | | `markObjectType` | 删除该调用;addon 实例不需要二次标记,JS fallback 才用 `markObjectToString` 并记录原因 | | `prototype = {}` | 改为 `createProtoChains` 或 `Object.defineProperties`,保留 `constructor` 和 `Symbol.toStringTag` | | 直接复用宿主 Web API | 按浏览器样本和目标调用范围重建可控实现,不盲目透传 Node 宿主对象 | | `this.__readyState` / `_headers` / `_store` | 改用 addon / xbs private API 或模块级 WeakMap | | XHR/fetch 返回默认 200 / fixture | 标为 offline-fixture;真实请求时改为 live session bridge | | Python curl_cffi 最终请求但 Node XHR 自己发请求 | 改为 final.py 持有同一 Session,Node 通过 IPC 请求 Python 发送 | | target bundle 混入手写代码 | 原始 bundle 放 `src/target/original/`,入口包装放 `src/target/entry.js` |