diff --git a/.github/workflows/browser-env.yml b/.github/workflows/browser-env.yml new file mode 100644 index 000000000000..d82fd743d16b --- /dev/null +++ b/.github/workflows/browser-env.yml @@ -0,0 +1,26 @@ +name: Browser environment + +on: + push: + branches: + - codex/browser-env + workflow_dispatch: + +permissions: + contents: read + +jobs: + test-browser-environment: + runs-on: ubuntu-24.04 + timeout-minutes: 45 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - name: Configure Debug build + run: ./configure --debug + + - name: Build Node + run: make -j4 node + + - name: Run browser environment regression test + run: out/Debug/node test/parallel/test-browser-env.js diff --git a/.github/workflows/publish-native-release.yml b/.github/workflows/publish-native-release.yml new file mode 100644 index 000000000000..41681bd8efb6 --- /dev/null +++ b/.github/workflows/publish-native-release.yml @@ -0,0 +1,171 @@ +name: Publish native release + +on: + push: + tags: + - 'mode_20*_v*' + workflow_dispatch: + inputs: + release_tag: + description: 'Release tag, for example mode_20260910_v22.0.2' + required: true + type: string + +permissions: + contents: write + +jobs: + validate-release-tag: + runs-on: ubuntu-24.04 + outputs: + release_tag: ${{ steps.release.outputs.tag }} + release_suffix: ${{ steps.release.outputs.suffix }} + steps: + - id: release + env: + RELEASE_TAG: ${{ inputs.release_tag || github.ref_name }} + run: | + if [[ ! "$RELEASE_TAG" =~ ^mode_[0-9]{8}_v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Release tag must use mode_YYYYmmdd_vX.Y.Z: $RELEASE_TAG" >&2 + exit 1 + fi + echo "tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT" + echo "suffix=${RELEASE_TAG#mode_}" >> "$GITHUB_OUTPUT" + + mac-arm64: + needs: validate-release-tag + runs-on: macos-15 + timeout-minutes: 120 + env: + RELEASE_SUFFIX: ${{ needs.validate-release-tag.outputs.release_suffix }} + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - name: Verify Apple Silicon runner + run: test "$(uname -m)" = arm64 + - name: Configure and build + run: | + ./configure + make -j3 node + - name: Run Mode regressions + run: | + out/Release/node test/parallel/test-browser-env.js + out/Release/node --test rs_mode_server/test/server.test.js + - name: Package + run: | + package="mode_mac_arm_${RELEASE_SUFFIX}" + mkdir -p "dist/$package" + cp out/Release/node "dist/$package/mode" + tar -czf "dist/$package.tar.gz" -C dist "$package" + - name: Stage release asset + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: mode-mac-arm + path: dist/*.tar.gz + if-no-files-found: error + + windows-x64: + needs: validate-release-tag + runs-on: windows-2022 + timeout-minutes: 60 + env: + RELEASE_SUFFIX: ${{ needs.validate-release-tag.outputs.release_suffix }} + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - name: Build x64 binary + shell: cmd + run: vcbuild.bat x64 + - name: Run Mode regressions + shell: pwsh + run: | + .\out\Release\node.exe test\parallel\test-browser-env.js + .\out\Release\node.exe --test rs_mode_server/test/server.test.js + - name: Package + shell: pwsh + run: | + $package = "mode_win_x64_$env:RELEASE_SUFFIX" + New-Item -ItemType Directory -Path "dist\$package" -Force | Out-Null + Copy-Item "out\Release\node.exe" "dist\$package\mode.exe" + Compress-Archive -Path "dist\$package" -DestinationPath "dist\$package.zip" + - name: Stage release asset + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: mode-windows-x64 + path: dist/*.zip + if-no-files-found: error + + linux-x64: + needs: validate-release-tag + runs-on: ubuntu-24.04 + timeout-minutes: 120 + env: + RELEASE_SUFFIX: ${{ needs.validate-release-tag.outputs.release_suffix }} + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - name: Configure and build + run: | + ./configure + make -j4 node + - name: Run Mode regressions + run: | + out/Release/node test/parallel/test-browser-env.js + out/Release/node --test rs_mode_server/test/server.test.js + - name: Package + run: | + package="mode_linux_x64_${RELEASE_SUFFIX}" + mkdir -p "dist/$package" + cp out/Release/node "dist/$package/mode" + tar -czf "dist/$package.tar.gz" -C dist "$package" + - name: Stage release asset + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: mode-linux-x64 + path: dist/*.tar.gz + if-no-files-found: error + + publish-release: + needs: [validate-release-tag, mac-arm64, windows-x64, linux-x64] + runs-on: ubuntu-24.04 + env: + RELEASE_TAG: ${{ needs.validate-release-tag.outputs.release_tag }} + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + steps: + # --notes-from-tag reads the annotated tag through the local Git checkout. + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: 0 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + path: dist + merge-multiple: true + - name: 编写中文发布说明 + run: | + printf '%s\n' \ + '## 本次更新' \ + '' \ + '- 补齐 `HTMLDivElement`、`HTMLIFrameElement`、空锚点 URL、`NetworkInformation` 与 `BatteryManager`,修正挑战实际创建的 DOM/BOM 对象原型和标签。' \ + '- 浏览器环境方法现在为常见 `fn.toString()` 检测提供原生函数形态,并保留真实调用行为。' \ + '- 新增源码诊断器:可不重新编译而直接以当前工作区的浏览器环境源码执行缓存挑战,用于发布前定位环境差异。' \ + '- `rs_mode_server` 的挑战 Worker 现在隔离 `global`、`process`、`module`、`require`、`setImmediate` 等 Node 全局别名,并取消向挑战脚本注入 CommonJS `require`;保留 `Buffer` 兼容既有挑战脚本。' \ + '- 保持 `document.all`、原始 HTML、动态 UA、Cookie、Storage、location/history 与每请求独立 Worker 的兼容行为。' \ + '' \ + '## 构建与验证' \ + '' \ + '- 已在 macOS ARM64、Windows x64、Linux x64 统一构建,并执行浏览器环境和 rs_mode_server 本地回归测试。' \ + > release-notes.md + - name: Create or update the unified release + run: | + shopt -s nullglob + assets=(dist/*.tar.gz dist/*.zip) + if (( ${#assets[@]} != 3 )); then + echo "Expected exactly three release assets:" >&2 + find dist -maxdepth 2 -type f -print >&2 + exit 1 + fi + if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then + gh release edit "$RELEASE_TAG" --title "$RELEASE_TAG" --notes-file release-notes.md + gh release upload "$RELEASE_TAG" "${assets[@]}" --clobber + else + gh release create "$RELEASE_TAG" "${assets[@]}" \ + --verify-tag --title "$RELEASE_TAG" --notes-file release-notes.md + fi diff --git a/.gitignore b/.gitignore index 69c1dd205316..eeae5e710fbd 100644 --- a/.gitignore +++ b/.gitignore @@ -164,6 +164,7 @@ install_manifest.txt # === Rules for AI assistants === CLAUDE.md AGENTS.md +js_reverse_cache/ # === Global Rules === # Keep last to avoid being excluded @@ -181,3 +182,4 @@ compile_commands.json .devcontainer/** !.devcontainer/base/ !.devcontainer/base/devcontainer.json +/rs_mode_server.zip diff --git a/README.md b/README.md index e27d95f26f1f..e1ee037d6189 100644 --- a/README.md +++ b/README.md @@ -1,912 +1,108 @@ -# Node.js +# Mode -Node.js is an open-source, cross-platform JavaScript runtime environment. +Mode 是基于 Node.js 的定制运行时。默认仍可像 Node.js 一样执行 JavaScript;在显式 +启用后,它会在当前 Realm 注入轻量浏览器兼容环境,以运行依赖常见 DOM/BOM 的脚本, +而不需要嵌入 Chromium 或启动图形浏览器。 -For information on using Node.js, see the [Node.js website][]. +当前发布版:[`mode_20260914_v22.0.10`](https://github.com/wen2go/mode/releases/tag/mode_20260914_v22.0.10)。 -The Node.js project uses an [open governance model](./GOVERNANCE.md). The -[OpenJS Foundation][] provides support for the project. +## 与官方 Node.js 的区别 -Contributors are expected to act in a collaborative manner to move -the project forward. We encourage the constructive exchange of contrary -opinions and compromise. The [TSC](./GOVERNANCE.md#technical-steering-committee) -reserves the right to limit or block contributors who repeatedly act in ways -that discourage, exhaust, or otherwise negatively affect other participants. +| 范围 | 官方 Node.js | Mode | +| --- | --- | --- | +| 启用方式 | 没有浏览器环境。 | 新增 `node:browser-env` 内置模块(CommonJS/ESM)和 `--browser-env-profile=file`。未启用时保持普通 Node.js 行为。 | +| DOM | 没有 `window`、`document` 或 HTML 树。 | 提供轻量 DOM、HTML 解析、节点/属性增删改、文本、查询 API、事件、`DOMParser` 及常用 HTML 元素构造器。 | +| BOM | 没有浏览器的 `location`、`history`、`navigator`、`screen`。 | 提供虚拟 `location/history`,可配置 UA、平台、语言、屏幕和窗口尺寸,以及 `NetworkInformation`、`BatteryManager`、MIME 类型、`sendBeacon()`。 | +| Cookie 与存储 | 没有浏览器 Cookie/Storage 接口。 | 提供内存 `document.cookie`、`localStorage`、`sessionStorage`。 | +| 浏览器特征 | 没有 `document.all`。 | 使用 Node/V8 内部 binding 提供特殊 `document.all` 语义,并为常见 `fn.toString()` 检查提供原生函数形态。 | +| 兼容存根 | 没有这些浏览器全局。 | 提供 `XMLHttpRequest` 状态/参数记录、`MutationObserver`、`indexedDB`、`chrome` 元数据、`open()`、`prompt()`、`msCrypto` 别名和确定性的 Canvas 表面。 | +| RS 本地服务 | 不提供。 | `rs_mode_server/server.js` 以每请求独立 Worker 执行挑战脚本;可使用原始 HTML、动态 UA、Cookie、Storage 和 Mode DOM/BOM。 | -**This project has a [Code of Conduct][].** +## 安装与启动 -## Table of contents +从 [GitHub Releases](https://github.com/wen2go/mode/releases/tag/mode_20260914_v22.0.10) +下载与系统匹配的文件: -* [Support](#support) -* [Release types](#release-types) - * [Download](#download) - * [Current and LTS releases](#current-and-lts-releases) - * [Nightly releases](#nightly-releases) - * [API documentation](#api-documentation) - * [Verifying binaries](#verifying-binaries) -* [Building Node.js](#building-nodejs) -* [Security](#security) -* [Contributing to Node.js](#contributing-to-nodejs) -* [Current project team members](#current-project-team-members) - * [TSC (Technical Steering Committee)](#tsc-technical-steering-committee) - * [Collaborators](#collaborators) - * [Triagers](#triagers) - * [Release keys](#release-keys) -* [License](#license) +| 平台 | 文件 | +| --- | --- | +| macOS Apple Silicon | `mode_mac_arm_20260914_v22.0.10.tar.gz` | +| Linux x64 | `mode_linux_x64_20260914_v22.0.10.tar.gz` | +| Windows x64 | `mode_win_x64_20260914_v22.0.10.zip` | -## Support - -Looking for help? Check out the -[instructions for getting support](.github/SUPPORT.md). - -## Release types - -* **Current**: Under active development. Code for the Current release is in the - branch for its major version number (for example, - [v22.x](https://github.com/nodejs/node/tree/v22.x)). Node.js releases a new - major version every 6 months, allowing for breaking changes. This happens in - April and October every year. Releases appearing each October have a support - life of 8 months. Releases appearing each April convert to LTS (see below) - each October. -* **LTS**: Releases that receive Long Term Support, with a focus on stability - and security. Every even-numbered major version will become an LTS release. - LTS releases receive 12 months of _Active LTS_ support and a further 18 months - of _Maintenance_. LTS release lines have alphabetically-ordered code names, - beginning with v4 Argon. There are no breaking changes or feature additions, - except in some special circumstances. -* **Nightly**: Code from the Current branch built every 24-hours when there are - changes. Use with caution. - -Current and LTS releases follow [semantic versioning](https://semver.org). A -member of the Release Team [signs](#release-keys) each Current and LTS release. -For more information, see the -[Release README](https://github.com/nodejs/Release#readme). - -### Download - -Binaries, installers, and source tarballs are available at -. - -#### Current and LTS releases - - - -The [latest](https://nodejs.org/download/release/latest/) directory is an -alias for the latest Current release. The latest-_codename_ directory is an -alias for the latest release from an LTS line. For example, the -[latest-hydrogen](https://nodejs.org/download/release/latest-hydrogen/) -directory contains the latest Hydrogen (Node.js 18) release. - -#### Nightly releases - - - -Each directory and filename includes the version (e.g., `v22.0.0`), -followed by the UTC date (e.g., `20240424` for April 24, 2024), -and the short commit SHA of the HEAD of the release (e.g., `ddd0a9e494`). -For instance, a full directory name might look like `v22.0.0-nightly20240424ddd0a9e494`. - -#### API documentation - -Documentation for the latest Current release is at . -Version-specific documentation is available in each release directory in the -_docs_ subdirectory. Version-specific documentation is also at -. - -### Verifying binaries - -Download directories contain a `SHASUMS256.txt.asc` file with SHA checksums for the -files and the releaser PGP signature. - -You can get a trusted keyring from nodejs/release-keys, e.g. using `curl`: +macOS/Linux: ```bash -curl -fsLo "/path/to/nodejs-keyring.kbx" "https://github.com/nodejs/release-keys/raw/HEAD/gpg/pubring.kbx" +tar -xzf mode_linux_x64_20260914_v22.0.10.tar.gz +mkdir -p "$HOME/.local/bin" +install -m 755 mode_linux_x64_20260914_v22.0.10/mode "$HOME/.local/bin/mode" +export PATH="$HOME/.local/bin:$PATH" +mode -e "console.log(typeof require('node:browser-env').install)" ``` -Alternatively, you can import the releaser keys in your default keyring, see -[Release keys](#release-keys) for commands on how to do that. +macOS Apple Silicon 请把文件和目录名替换为 `mode_mac_arm_20260914_v22.0.10`。 +最后一条命令输出 `function` 即表示当前 shell 正在使用 Mode。 -Then, you can verify the files you've downloaded locally -(if you're using your default keyring, pass `--keyring="${GNUPGHOME:-~/.gnupg}/pubring.kbx"`): +Windows:解压 ZIP 后运行: -```bash -curl -fsO "https://nodejs.org/dist/${VERSION}/SHASUMS256.txt.asc" \ -&& gpgv --keyring="/path/to/nodejs-keyring.kbx" --output SHASUMS256.txt < SHASUMS256.txt.asc \ -&& shasum --check SHASUMS256.txt --ignore-missing +```powershell +.\mode_win_x64_20260914_v22.0.10\mode.exe -e "console.log(typeof require('node:browser-env').install)" ``` -## Building Node.js - -See [BUILDING.md](BUILDING.md) for instructions on how to build Node.js from -source and a list of supported platforms. - -## Security - -For information on reporting security vulnerabilities in Node.js, see -[SECURITY.md](./SECURITY.md). - -## Contributing to Node.js - -* [Contributing to the project][] -* [Working Groups][] -* [Strategic initiatives][] -* [Technical values and prioritization][] - -## Current project team members - -For information about the governance of the Node.js project, see -[GOVERNANCE.md](./GOVERNANCE.md). - - - -### TSC (Technical Steering Committee) - -#### TSC voting members - - - -* [aduh95](https://github.com/aduh95) - - **Antoine du Hamel** <> (he/him) -* [anonrig](https://github.com/anonrig) - - **Yagiz Nizipli** <> (he/him) -* [benjamingr](https://github.com/benjamingr) - - **Benjamin Gruenbaum** <> -* [BridgeAR](https://github.com/BridgeAR) - - **Ruben Bridgewater** <> (he/him) -* [gireeshpunathil](https://github.com/gireeshpunathil) - - **Gireesh Punathil** <> (he/him) -* [jasnell](https://github.com/jasnell) - - **James M Snell** <> (he/him) -* [joyeecheung](https://github.com/joyeecheung) - - **Joyee Cheung** <> (she/her) -* [legendecas](https://github.com/legendecas) - - **Chengzhong Wu** <> (he/him) -* [marco-ippolito](https://github.com/marco-ippolito) - - **Marco Ippolito** <> (he/him) -* [mcollina](https://github.com/mcollina) - - **Matteo Collina** <> (he/him) -* [panva](https://github.com/panva) - - **Filip Skokan** <> (he/him) -* [RafaelGSS](https://github.com/RafaelGSS) - - **Rafael Gonzaga** <> (he/him) -* [RaisinTen](https://github.com/RaisinTen) - - **Darshan Sen** <> (he/him) -* [richardlau](https://github.com/richardlau) - - **Richard Lau** <> -* [ronag](https://github.com/ronag) - - **Robert Nagy** <> -* [ruyadorno](https://github.com/ruyadorno) - - **Ruy Adorno** <> (he/him) -* [ShogunPanda](https://github.com/ShogunPanda) - - **Paolo Insogna** <> (he/him) -* [targos](https://github.com/targos) - - **Michaël Zasso** <> (he/him) -* [tniessen](https://github.com/tniessen) - - **Tobias Nießen** <> (he/him) - -#### TSC regular members - -* [BethGriggs](https://github.com/BethGriggs) - - **Beth Griggs** <> (she/her) -* [bnoordhuis](https://github.com/bnoordhuis) - - **Ben Noordhuis** <> -* [cjihrig](https://github.com/cjihrig) - - **Colin Ihrig** <> (he/him) -* [codebytere](https://github.com/codebytere) - - **Shelley Vohr** <> (she/her) -* [GeoffreyBooth](https://github.com/GeoffreyBooth) - - **Geoffrey Booth** <> (he/him) -* [MoLow](https://github.com/MoLow) - - **Moshe Atlow** <> (he/him) -* [Trott](https://github.com/Trott) - - **Rich Trott** <> (he/him) - -
+使用 JSON profile 在目标脚本运行前安装浏览器环境: -TSC emeriti members - -#### TSC emeriti members - -* [addaleax](https://github.com/addaleax) - - **Anna Henningsen** <> (she/her) -* [apapirovski](https://github.com/apapirovski) - - **Anatoli Papirovski** <> (he/him) -* [ChALkeR](https://github.com/ChALkeR) - - **Сковорода Никита Андреевич** <> (he/him) -* [chrisdickinson](https://github.com/chrisdickinson) - - **Chris Dickinson** <> -* [danbev](https://github.com/danbev) - - **Daniel Bevenius** <> (he/him) -* [danielleadams](https://github.com/danielleadams) - - **Danielle Adams** <> (she/her) -* [evanlucas](https://github.com/evanlucas) - - **Evan Lucas** <> (he/him) -* [fhinkel](https://github.com/fhinkel) - - **Franziska Hinkelmann** <> (she/her) -* [Fishrock123](https://github.com/Fishrock123) - - **Jeremiah Senkpiel** <> (he/they) -* [gabrielschulhof](https://github.com/gabrielschulhof) - - **Gabriel Schulhof** <> -* [gibfahn](https://github.com/gibfahn) - - **Gibson Fahnestock** <> (he/him) -* [indutny](https://github.com/indutny) - - **Fedor Indutny** <> -* [isaacs](https://github.com/isaacs) - - **Isaac Z. Schlueter** <> -* [joshgav](https://github.com/joshgav) - - **Josh Gavant** <> -* [mhdawson](https://github.com/mhdawson) - - **Michael Dawson** <> (he/him) -* [mmarchini](https://github.com/mmarchini) - - **Mary Marchini** <> (she/her) -* [mscdex](https://github.com/mscdex) - - **Brian White** <> -* [MylesBorins](https://github.com/MylesBorins) - - **Myles Borins** <> (he/him) -* [nebrius](https://github.com/nebrius) - - **Bryan Hughes** <> -* [ofrobots](https://github.com/ofrobots) - - **Ali Ijaz Sheikh** <> (he/him) -* [orangemocha](https://github.com/orangemocha) - - **Alexis Campailla** <> -* [piscisaureus](https://github.com/piscisaureus) - - **Bert Belder** <> -* [rvagg](https://github.com/rvagg) - - **Rod Vagg** <> -* [sam-github](https://github.com/sam-github) - - **Sam Roberts** <> -* [shigeki](https://github.com/shigeki) - - **Shigeki Ohtsu** <> (he/him) -* [thefourtheye](https://github.com/thefourtheye) - - **Sakthipriyan Vairamani** <> (he/him) -* [TimothyGu](https://github.com/TimothyGu) - - **Tiancheng "Timothy" Gu** <> (he/him) -* [trevnorris](https://github.com/trevnorris) - - **Trevor Norris** <> - -
- - - -### Collaborators - -* [abmusse](https://github.com/abmusse) - - **Abdirahim Musse** <> -* [addaleax](https://github.com/addaleax) - - **Anna Henningsen** <> (she/her) -* [Aditi-1400](https://github.com/Aditi-1400) - - **Aditi Singh** <> (she/her) -* [aduh95](https://github.com/aduh95) - - **Antoine du Hamel** <> (he/him) - [Support me](https://github.com/sponsors/aduh95) -* [anonrig](https://github.com/anonrig) - - **Yagiz Nizipli** <> (he/him) - [Support me](https://github.com/sponsors/anonrig) -* [atlowChemi](https://github.com/atlowChemi) - - **Chemi Atlow** <> (he/him) -* [avivkeller](https://github.com/avivkeller) - - **Aviv Keller** <> (he/him) - [Support me](https://github.com/sponsors/avivkeller) -* [Ayase-252](https://github.com/Ayase-252) - - **Qingyu Deng** <> -* [bengl](https://github.com/bengl) - - **Bryan English** <> (he/him) -* [benjamingr](https://github.com/benjamingr) - - **Benjamin Gruenbaum** <> -* [BethGriggs](https://github.com/BethGriggs) - - **Beth Griggs** <> (she/her) -* [bnb](https://github.com/bnb) - - **Tierney Cyren** <> (they/them) -* [bnoordhuis](https://github.com/bnoordhuis) - - **Ben Noordhuis** <> -* [BridgeAR](https://github.com/BridgeAR) - - **Ruben Bridgewater** <> (he/him) -* [cclauss](https://github.com/cclauss) - - **Christian Clauss** <> (he/him) -* [ChALkeR](https://github.com/ChALkeR) - - **Сковорода Никита Андреевич** <> (he/him) -* [cjihrig](https://github.com/cjihrig) - - **Colin Ihrig** <> (he/him) -* [codebytere](https://github.com/codebytere) - - **Shelley Vohr** <> (she/her) -* [cola119](https://github.com/cola119) - - **Kohei Ueno** <> (he/him) -* [daeyeon](https://github.com/daeyeon) - - **Daeyeon Jeong** <> (he/him) -* [dario-piotrowicz](https://github.com/dario-piotrowicz) - - **Dario Piotrowicz** <> (he/him) -* [deokjinkim](https://github.com/deokjinkim) - - **Deokjin Kim** <> (he/him) -* [ErickWendel](https://github.com/ErickWendel) - - **Erick Wendel** <> (he/him) -* [Ethan-Arrowood](https://github.com/Ethan-Arrowood) - - **Ethan Arrowood** <> (he/him) -* [Flarna](https://github.com/Flarna) - - **Gerhard Stöbich** <> (he/they) -* [gabrielschulhof](https://github.com/gabrielschulhof) - - **Gabriel Schulhof** <> -* [geeksilva97](https://github.com/geeksilva97) - - **Edy Silva** <> (he/him) -* [gengjiawen](https://github.com/gengjiawen) - - **Jiawen Geng** <> -* [GeoffreyBooth](https://github.com/GeoffreyBooth) - - **Geoffrey Booth** <> (he/him) -* [gireeshpunathil](https://github.com/gireeshpunathil) - - **Gireesh Punathil** <> (he/him) -* [gurgunday](https://github.com/gurgunday) - - **Gürgün Dayıoğlu** <> (he/him) -* [guybedford](https://github.com/guybedford) - - **Guy Bedford** <> (he/him) -* [H4ad](https://github.com/H4ad) - - **Vinícius Lourenço Claro Cardoso** <> (he/him) -* [HarshithaKP](https://github.com/HarshithaKP) - - **Harshitha K P** <> (she/her) -* [himself65](https://github.com/himself65) - - **Zeyu "Alex" Yang** <> (he/him) -* [hybrist](https://github.com/hybrist) - - **Jan Martin** <> (he/him) -* [IlyasShabi](https://github.com/IlyasShabi) - - **Ilyas Shabi** <> (he/him) -* [islandryu](https://github.com/islandryu) - - **Ryuhei Shima** <> (he/him) -* [jakecastelli](https://github.com/jakecastelli) - - **Jake Yuesong Li** <> (he/him) -* [JakobJingleheimer](https://github.com/JakobJingleheimer) - - **Jacob Smith** <> (he/him) -* [jasnell](https://github.com/jasnell) - - **James M Snell** <> (he/him) -* [jazelly](https://github.com/jazelly) - - **Jason Zhang** <> (he/him) -* [joyeecheung](https://github.com/joyeecheung) - - **Joyee Cheung** <> (she/her) -* [juanarbol](https://github.com/juanarbol) - - **Juan José Arboleda** <> (he/him) -* [JungMinu](https://github.com/JungMinu) - - **Minwoo Jung** <> (he/him) -* [KhafraDev](https://github.com/KhafraDev) - - **Matthew Aitken** <> (he/him) -* [legendecas](https://github.com/legendecas) - - **Chengzhong Wu** <> (he/him) -* [lemire](https://github.com/lemire) - - **Daniel Lemire** <> -* [LiviaMedeiros](https://github.com/LiviaMedeiros) - - **LiviaMedeiros** <> -* [ljharb](https://github.com/ljharb) - - **Jordan Harband** <> -* [lpinca](https://github.com/lpinca) - - **Luigi Pinca** <> (he/him) -* [Lxxyx](https://github.com/Lxxyx) - - **Zijian Liu** <> (he/him) -* [marco-ippolito](https://github.com/marco-ippolito) - - **Marco Ippolito** <> (he/him) - [Support me](https://github.com/sponsors/marco-ippolito) -* [marsonya](https://github.com/marsonya) - - **Akhil Marsonya** <> (he/him) -* [MattiasBuelens](https://github.com/MattiasBuelens) - - **Mattias Buelens** <> (he/him) -* [mcollina](https://github.com/mcollina) - - **Matteo Collina** <> (he/him) - [Support me](https://github.com/sponsors/mcollina) -* [meixg](https://github.com/meixg) - - **Xuguang Mei** <> (he/him) -* [MikeMcC399](https://github.com/MikeMcC399) - - **Mike McCready** <<66998419+MikeMcC399@users.noreply.github.com>> (he/him) -* [MoLow](https://github.com/MoLow) - - **Moshe Atlow** <> (he/him) -* [MrJithil](https://github.com/MrJithil) - - **Jithil P Ponnan** <> (he/him) -* [ovflowd](https://github.com/ovflowd) - - **Claudio Wunder** <> (he/they) -* [panva](https://github.com/panva) - - **Filip Skokan** <> (he/him) - [Support me](https://github.com/sponsors/panva) -* [pimterry](https://github.com/pimterry) - - **Tim Perry** <> (he/him) -* [pmarchini](https://github.com/pmarchini) - - **Pietro Marchini** <> (he/him) -* [Qard](https://github.com/Qard) - - **Stephen Belanger** <> (he/him) -* [RafaelGSS](https://github.com/RafaelGSS) - - **Rafael Gonzaga** <> (he/him) - [Support me](https://github.com/sponsors/RafaelGSS) -* [RaisinTen](https://github.com/RaisinTen) - - **Darshan Sen** <> (he/him) - [Support me](https://github.com/sponsors/RaisinTen) -* [Renegade334](https://github.com/Renegade334) - - **René** <> -* [richardlau](https://github.com/richardlau) - - **Richard Lau** <> -* [rluvaton](https://github.com/rluvaton) - - **Raz Luvaton** <> (he/him) -* [ronag](https://github.com/ronag) - - **Robert Nagy** <> -* [ruyadorno](https://github.com/ruyadorno) - - **Ruy Adorno** <> (he/him) -* [santigimeno](https://github.com/santigimeno) - - **Santiago Gimeno** <> -* [ShogunPanda](https://github.com/ShogunPanda) - - **Paolo Insogna** <> (he/him) -* [srl295](https://github.com/srl295) - - **Steven R Loomis** <> -* [StefanStojanovic](https://github.com/StefanStojanovic) - - **Stefan Stojanovic** <> (he/him) -* [sxa](https://github.com/sxa) - - **Stewart X Addison** <> (he/him) -* [targos](https://github.com/targos) - - **Michaël Zasso** <> (he/him) -* [theanarkh](https://github.com/theanarkh) - - **theanarkh** <> (he/him) -* [tniessen](https://github.com/tniessen) - - **Tobias Nießen** <> (he/him) -* [trivikr](https://github.com/trivikr) - - **Trivikram Kamat** <> -* [Trott](https://github.com/Trott) - - **Rich Trott** <> (he/him) -* [UlisesGascon](https://github.com/UlisesGascon) - - **Ulises Gascón** <> (he/him) -* [vmoroz](https://github.com/vmoroz) - - **Vladimir Morozov** <> (he/him) -* [watilde](https://github.com/watilde) - - **Daijiro Wachi** <> (he/him) -* [ZYSzys](https://github.com/ZYSzys) - - **Yongsheng Zhang** <> (he/him) - -
- -Emeriti - - - -### Collaborator emeriti - -* [ak239](https://github.com/ak239) - - **Aleksei Koziatinskii** <> -* [andrasq](https://github.com/andrasq) - - **Andras** <> -* [AndreasMadsen](https://github.com/AndreasMadsen) - - **Andreas Madsen** <> (he/him) -* [AnnaMag](https://github.com/AnnaMag) - - **Anna M. Kedzierska** <> -* [antsmartian](https://github.com/antsmartian) - - **Anto Aravinth** <> (he/him) -* [apapirovski](https://github.com/apapirovski) - - **Anatoli Papirovski** <> (he/him) -* [aqrln](https://github.com/aqrln) - - **Alexey Orlenko** <> (he/him) -* [AshCripps](https://github.com/AshCripps) - - **Ash Cripps** <> -* [bcoe](https://github.com/bcoe) - - **Ben Coe** <> (he/him) -* [bmeck](https://github.com/bmeck) - - **Bradley Farias** <> -* [bmeurer](https://github.com/bmeurer) - - **Benedikt Meurer** <> -* [boneskull](https://github.com/boneskull) - - **Christopher Hiller** <> (he/him) -* [brendanashworth](https://github.com/brendanashworth) - - **Brendan Ashworth** <> -* [bzoz](https://github.com/bzoz) - - **Bartosz Sosnowski** <> -* [calvinmetcalf](https://github.com/calvinmetcalf) - - **Calvin Metcalf** <> -* [chrisdickinson](https://github.com/chrisdickinson) - - **Chris Dickinson** <> -* [claudiorodriguez](https://github.com/claudiorodriguez) - - **Claudio Rodriguez** <> -* [danbev](https://github.com/danbev) - - **Daniel Bevenius** <> (he/him) -* [danielleadams](https://github.com/danielleadams) - - **Danielle Adams** <> (she/her) -* [DavidCai1111](https://github.com/DavidCai1111) - - **David Cai** <> (he/him) -* [davisjam](https://github.com/davisjam) - - **Jamie Davis** <> (he/him) -* [debadree25](https://github.com/debadree25) - - **Debadree Chatterjee** <> (he/him) -* [devnexen](https://github.com/devnexen) - - **David Carlier** <> -* [devsnek](https://github.com/devsnek) - - **Gus Caplan** <> (they/them) -* [digitalinfinity](https://github.com/digitalinfinity) - - **Hitesh Kanwathirtha** <> (he/him) -* [dmabupt](https://github.com/dmabupt) - - **Xu Meng** <> (he/him) -* [dnlup](https://github.com/dnlup) - - **dnlup** <> -* [edsadr](https://github.com/edsadr) - - **Adrian Estrada** <> (he/him) -* [eljefedelrodeodeljefe](https://github.com/eljefedelrodeodeljefe) - - **Robert Jefe Lindstaedt** <> -* [estliberitas](https://github.com/estliberitas) - - **Alexander Makarenko** <> -* [eugeneo](https://github.com/eugeneo) - - **Eugene Ostroukhov** <> -* [evanlucas](https://github.com/evanlucas) - - **Evan Lucas** <> (he/him) -* [F3n67u](https://github.com/F3n67u) - - **Feng Yu** <> (he/him) -* [fhinkel](https://github.com/fhinkel) - - **Franziska Hinkelmann** <> (she/her) -* [firedfox](https://github.com/firedfox) - - **Daniel Wang** <> -* [Fishrock123](https://github.com/Fishrock123) - - **Jeremiah Senkpiel** <> (he/they) -* [gdams](https://github.com/gdams) - - **George Adams** <> (he/him) -* [geek](https://github.com/geek) - - **Wyatt Preul** <> -* [gibfahn](https://github.com/gibfahn) - - **Gibson Fahnestock** <> (he/him) -* [glentiki](https://github.com/glentiki) - - **Glen Keane** <> (he/him) -* [hashseed](https://github.com/hashseed) - - **Yang Guo** <> (he/him) -* [hiroppy](https://github.com/hiroppy) - - **Yuta Hiroto** <> (he/him) -* [iansu](https://github.com/iansu) - - **Ian Sutherland** <> -* [iarna](https://github.com/iarna) - - **Rebecca Turner** <> -* [imran-iq](https://github.com/imran-iq) - - **Imran Iqbal** <> -* [imyller](https://github.com/imyller) - - **Ilkka Myller** <> -* [indutny](https://github.com/indutny) - - **Fedor Indutny** <> -* [isaacs](https://github.com/isaacs) - - **Isaac Z. Schlueter** <> -* [italoacasas](https://github.com/italoacasas) - - **Italo A. Casas** <> (he/him) -* [JacksonTian](https://github.com/JacksonTian) - - **Jackson Tian** <> -* [jasongin](https://github.com/jasongin) - - **Jason Ginchereau** <> -* [jbergstroem](https://github.com/jbergstroem) - - **Johan Bergström** <> -* [jdalton](https://github.com/jdalton) - - **John-David Dalton** <> -* [jhamhader](https://github.com/jhamhader) - - **Yuval Brik** <> -* [joaocgreis](https://github.com/joaocgreis) - - **João Reis** <> -* [joesepi](https://github.com/joesepi) - - **Joe Sepi** <> (he/him) -* [JonasBa](https://github.com/JonasBa) - - **Jonas Badalic** <> (he/him) -* [joshgav](https://github.com/joshgav) - - **Josh Gavant** <> -* [julianduque](https://github.com/julianduque) - - **Julian Duque** <> (he/him) -* [kfarnung](https://github.com/kfarnung) - - **Kyle Farnung** <> (he/him) -* [kunalspathak](https://github.com/kunalspathak) - - **Kunal Pathak** <> -* [kuriyosh](https://github.com/kuriyosh) - - **Yoshiki Kurihara** <> (he/him) -* [kvakil](https://github.com/kvakil) - - **Keyhan Vakil** <> -* [lance](https://github.com/lance) - - **Lance Ball** <> (he/him) -* [Leko](https://github.com/Leko) - - **Shingo Inoue** <> (he/him) -* [Linkgoron](https://github.com/Linkgoron) - - **Nitzan Uziely** <> -* [lucamaraschi](https://github.com/lucamaraschi) - - **Luca Maraschi** <> (he/him) -* [lukekarrys](https://github.com/lukekarrys) - - **Luke Karrys** <> (he/him) -* [lundibundi](https://github.com/lundibundi) - - **Denys Otrishko** <> (he/him) -* [lxe](https://github.com/lxe) - - **Aleksey Smolenchuk** <> -* [maclover7](https://github.com/maclover7) - - **Jon Moss** <> (he/him) -* [mafintosh](https://github.com/mafintosh) - - **Mathias Buus** <> (he/him) -* [matthewloring](https://github.com/matthewloring) - - **Matthew Loring** <> -* [Mesteery](https://github.com/Mesteery) - - **Mestery** <> (he/him) -* [mhdawson](https://github.com/mhdawson) - - **Michael Dawson** <> (he/him) -* [micnic](https://github.com/micnic) - - **Nicu Micleușanu** <> (he/him) -* [mikeal](https://github.com/mikeal) - - **Mikeal Rogers** <> -* [miladfarca](https://github.com/miladfarca) - - **Milad Fa** <> (he/him) -* [mildsunrise](https://github.com/mildsunrise) - - **Alba Mendez** <> (she/her) -* [misterdjules](https://github.com/misterdjules) - - **Julien Gilli** <> -* [mmarchini](https://github.com/mmarchini) - - **Mary Marchini** <> (she/her) -* [monsanto](https://github.com/monsanto) - - **Christopher Monsanto** <> -* [MoonBall](https://github.com/MoonBall) - - **Chen Gang** <> -* [mscdex](https://github.com/mscdex) - - **Brian White** <> -* [MylesBorins](https://github.com/MylesBorins) - - **Myles Borins** <> (he/him) -* [not-an-aardvark](https://github.com/not-an-aardvark) - - **Teddy Katz** <> (he/him) -* [ofrobots](https://github.com/ofrobots) - - **Ali Ijaz Sheikh** <> (he/him) -* [Olegas](https://github.com/Olegas) - - **Oleg Elifantiev** <> -* [orangemocha](https://github.com/orangemocha) - - **Alexis Campailla** <> -* [othiym23](https://github.com/othiym23) - - **Forrest L Norvell** <> (they/them/themself) -* [oyyd](https://github.com/oyyd) - - **Ouyang Yadong** <> (he/him) -* [petkaantonov](https://github.com/petkaantonov) - - **Petka Antonov** <> -* [phillipj](https://github.com/phillipj) - - **Phillip Johnsen** <> -* [piscisaureus](https://github.com/piscisaureus) - - **Bert Belder** <> -* [pmq20](https://github.com/pmq20) - - **Minqi Pan** <> -* [PoojaDurgad](https://github.com/PoojaDurgad) - - **Pooja D P** <> (she/her) -* [princejwesley](https://github.com/princejwesley) - - **Prince John Wesley** <> -* [psmarshall](https://github.com/psmarshall) - - **Peter Marshall** <> (he/him) -* [puskin](https://github.com/puskin) - - **Giovanni Bucci** <> (he/him) -* [puzpuzpuz](https://github.com/puzpuzpuz) - - **Andrey Pechkurov** <> (he/him) -* [refack](https://github.com/refack) - - **Refael Ackermann (רפאל פלחי)** <> (he/him/הוא/אתה) -* [rexagod](https://github.com/rexagod) - - **Pranshu Srivastava** <> (he/him) -* [rickyes](https://github.com/rickyes) - - **Ricky Zhou** <<0x19951125@gmail.com>> (he/him) -* [rlidwka](https://github.com/rlidwka) - - **Alex Kocharin** <> -* [rmg](https://github.com/rmg) - - **Ryan Graham** <> -* [robertkowalski](https://github.com/robertkowalski) - - **Robert Kowalski** <> -* [romankl](https://github.com/romankl) - - **Roman Klauke** <> -* [ronkorving](https://github.com/ronkorving) - - **Ron Korving** <> -* [RReverser](https://github.com/RReverser) - - **Ingvar Stepanyan** <> -* [rubys](https://github.com/rubys) - - **Sam Ruby** <> -* [rvagg](https://github.com/rvagg) - - **Rod Vagg** <> -* [ryzokuken](https://github.com/ryzokuken) - - **Ujjwal Sharma** <> (he/him) -* [saghul](https://github.com/saghul) - - **Saúl Ibarra Corretgé** <> -* [sam-github](https://github.com/sam-github) - - **Sam Roberts** <> -* [sebdeckers](https://github.com/sebdeckers) - - **Sebastiaan Deckers** <> -* [seishun](https://github.com/seishun) - - **Nikolai Vavilov** <> -* [shigeki](https://github.com/shigeki) - - **Shigeki Ohtsu** <> (he/him) -* [shisama](https://github.com/shisama) - - **Masashi Hirano** <> (he/him) -* [silverwind](https://github.com/silverwind) - - **Roman Reiss** <> -* [starkwang](https://github.com/starkwang) - - **Weijia Wang** <> -* [stefanmb](https://github.com/stefanmb) - - **Stefan Budeanu** <> -* [tellnes](https://github.com/tellnes) - - **Christian Tellnes** <> -* [thefourtheye](https://github.com/thefourtheye) - - **Sakthipriyan Vairamani** <> (he/him) -* [thlorenz](https://github.com/thlorenz) - - **Thorsten Lorenz** <> -* [TimothyGu](https://github.com/TimothyGu) - - **Tiancheng "Timothy" Gu** <> (he/him) -* [trevnorris](https://github.com/trevnorris) - - **Trevor Norris** <> -* [tunniclm](https://github.com/tunniclm) - - **Mike Tunnicliffe** <> -* [vdeturckheim](https://github.com/vdeturckheim) - - **Vladimir de Turckheim** <> (he/him) -* [vkurchatkin](https://github.com/vkurchatkin) - - **Vladimir Kurchatkin** <> -* [VoltrexKeyva](https://github.com/VoltrexKeyva) - - **Mohammed Keyvanzadeh** <> (he/him) -* [vsemozhetbyt](https://github.com/vsemozhetbyt) - - **Vse Mozhet Byt** <> (he/him) -* [watson](https://github.com/watson) - - **Thomas Watson** <> -* [whitlockjc](https://github.com/whitlockjc) - - **Jeremy Whitlock** <> -* [XadillaX](https://github.com/XadillaX) - - **Khaidi Chu** <> (he/him) -* [yashLadha](https://github.com/yashLadha) - - **Yash Ladha** <> (he/him) -* [yhwang](https://github.com/yhwang) - - **Yihong Wang** <> -* [yorkie](https://github.com/yorkie) - - **Yorkie Liu** <> -* [yosuke-furukawa](https://github.com/yosuke-furukawa) - - **Yosuke Furukawa** <> -* [zcbenz](https://github.com/zcbenz) - - **Cheng Zhao** <> (he/him) - -
- - - -Collaborators follow the [Collaborator Guide](./doc/contributing/collaborator-guide.md) in -maintaining the Node.js project. - -### Triagers - -* [1ilsang](https://github.com/1ilsang) - - **Sangchul Lee** <<1ilsang.dev@gmail.com>> (he/him) -* [bjohansebas](https://github.com/bjohansebas) - - **Sebastian Beltran** <> -* [bmuenzenmeyer](https://github.com/bmuenzenmeyer) - - **Brian Muenzenmeyer** <> (he/him) -* [efekrskl](https://github.com/efekrskl) - - **Efe Karasakal** <> (he/him) -* [gireeshpunathil](https://github.com/gireeshpunathil) - - **Gireesh Punathil** <> (he/him) -* [haramj](https://github.com/haramj) - - **Haram Jeong** <> -* [HBSPS](https://github.com/HBSPS) - - **Wiyeong Seo** <> -* [iam-frankqiu](https://github.com/iam-frankqiu) - - **Frank Qiu** <> (he/him) -* [milesguicent](https://github.com/milesguicent) - - **Miles Guicent** <> (he/him) -* [preveen-stack](https://github.com/preveen-stack) - - **Preveen Padmanabhan** <> (he/him) - -Triagers follow the [Triage Guide](./doc/contributing/issues.md#triaging-a-bug-report) when -responding to new issues. - -### Release keys - -Primary GPG keys for Node.js Releasers (some Releasers sign with subkeys): - -* **Antoine du Hamel** <> - `5BE8A3F6C8A5C01D106C0AD820B1A390B168D356` -* **Juan José Arboleda** <> - `DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7` -* **Marco Ippolito** <> - `CC68F5A3106FF448322E48ED27F5E38D5B0A215F` -* **Michaël Zasso** <> - `8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600` -* **Rafael Gonzaga** <> - `890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4` -* **Richard Lau** <> - `C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C` -* **Ruy Adorno** <> - `108F52B48DB57BB0CC439B2997B01419BD92F80A` -* **Stewart X Addison** <> - `655F3B5C1FB3FA8D1A0CA6BDE4A7D232B936D2FD` -* **Ulises Gascón** <> - `A363A499291CBBC940DD62E41F10027AF002F8B0` - -You can use the keyring the project maintains at -. -Alternatively, you can import them from a public key server. Have in mind that -the project cannot guarantee the availability of the server nor the keys on -that server. +```json +{ + "url": "https://example.test/", + "html": "
hello
", + "navigator": { "platform": "Win32", "languages": ["zh-CN", "zh"] } +} +``` ```bash -gpg --keyserver hkps://keys.openpgp.org --recv-keys 5BE8A3F6C8A5C01D106C0AD820B1A390B168D356 # Antoine du Hamel -gpg --keyserver hkps://keys.openpgp.org --recv-keys DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 # Juan José Arboleda -gpg --keyserver hkps://keys.openpgp.org --recv-keys CC68F5A3106FF448322E48ED27F5E38D5B0A215F # Marco Ippolito -gpg --keyserver hkps://keys.openpgp.org --recv-keys 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 # Michaël Zasso -gpg --keyserver hkps://keys.openpgp.org --recv-keys 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 # Rafael Gonzaga -gpg --keyserver hkps://keys.openpgp.org --recv-keys C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C # Richard Lau -gpg --keyserver hkps://keys.openpgp.org --recv-keys 108F52B48DB57BB0CC439B2997B01419BD92F80A # Ruy Adorno -gpg --keyserver hkps://keys.openpgp.org --recv-keys 655F3B5C1FB3FA8D1A0CA6BDE4A7D232B936D2FD # Stewart X Addison -gpg --keyserver hkps://keys.openpgp.org --recv-keys A363A499291CBBC940DD62E41F10027AF002F8B0 # Ulises Gascón +mode --browser-env-profile=./browser-profile.json target.js ``` -See [Verifying binaries](#verifying-binaries) for how to use these keys to -verify a downloaded file. - -
- -Other keys used to sign some previous releases +需要 getter、setter 或函数型自定义属性时,在包装脚本中先调用 `install()`,再加载 +目标脚本: -* **Antoine du Hamel** <> - `C0D6248439F1D5604AAFFB4021D900FFDB233756` -* **Beth Griggs** <> - `4ED778F539E3634C779C87C6D7062848A1AB005C` -* **Bryan English** <> - `141F07595B7B3FFE74309A937405533BE57C7D57` -* **Chris Dickinson** <> - `9554F04D7259F04124DE6B476D5A82AC7E37093B` -* **Colin Ihrig** <> - `94AE36675C464D64BAFA68DD7434390BDBE9B9C5` -* **Danielle Adams** <> - `1C050899334244A8AF75E53792EF661D867B9DFA` - `74F12602B6F1C4E913FAA37AD3A89613643B6201` -* **Evan Lucas** <> - `B9AE9905FFD7803F25714661B63B535A4C206CA9` -* **Gibson Fahnestock** <> - `77984A986EBC2AA786BC0F66B01FBB92821C587A` -* **Isaac Z. Schlueter** <> - `93C7E9E91B49E432C2F75674B0A78B0A6C481CF6` -* **Italo A. Casas** <> - `56730D5401028683275BD23C23EFEFE93C4CFFFE` -* **James M Snell** <> - `71DCFD284A79C3B38668286BC97EC7A07EDE3FC1` -* **Jeremiah Senkpiel** <> - `FD3A5288F042B6850C66B31F09FE44734EB7990E` -* **Juan José Arboleda** <> - `61FC681DFB92A079F1685E77973F295594EC4689` -* **Julien Gilli** <> - `114F43EE0176B71C7BC219DD50A3051F888C628D` -* **Myles Borins** <> - `C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8` -* **Rod Vagg** <> - `DD8F2338BAE7501E3DD5AC78C273792F7D83545D` -* **Ruben Bridgewater** <> - `A48C2BEE680E841632CD4E44F07496B3EB3C1762` -* **Shelley Vohr** <> - `B9E2F5981AA6E0CD28160D9FF13993A75599653C` -* **Timothy J Fontaine** <> - `7937DFD2AB06298B2293C3187D33FF9D0246406D` +```js +const { install } = require('node:browser-env'); -The project maintains a keyring able to verify all past releases of Node.js at -. +install({ + url: 'https://example.test/', + html: '
', + navigator: { userAgent: 'Mozilla/5.0', platform: 'Win32' }, +}); -
- -### Security release stewards - -When possible, the commitment to take slots in the -security release steward rotation is made by companies in order -to ensure individuals who act as security stewards have the -support and recognition from their employer to be able to -prioritize security releases. Security release stewards manage security -releases on a rotation basis as outlined in the -[security release process](./doc/contributing/security-release-process.md). - -* [Datadog](https://www.datadoghq.com/) - * [bengl](https://github.com/bengl) - - **Bryan English** <> (he/him) -* [HeroDevs](https://www.herodevs.com/) - * [juanarbol](https://github.com/juanarbol) - OpenJS Slack handle: `juanarbol` - **Juan José Arboleda** <> (he/him) - * [marco-ippolito](https://github.com/marco-ippolito) - OpenJS Slack handle: `Marco Ippolito` - **Marco Ippolito** <> (he/him) -* [NodeSource](https://nodesource.com/) - * [RafaelGSS](https://github.com/RafaelGSS) - OpenJS Slack handle: `RafaelGSS` - **Rafael Gonzaga** <> (he/him) -* [Platformatic](https://platformatic.dev/) - * [mcollina](https://github.com/mcollina) - OpenJS Slack handle: `mcollina` - **Matteo Collina** <> (he/him) -* [Red Hat](https://redhat.com) / [IBM](https://ibm.com) - * [BethGriggs](https://github.com/BethGriggs) - - **Beth Griggs** <> (she/her) - * [sxa](https://github.com/sxa) - - **Stewart X Addison** <> (he/him) - -## License - -Node.js is licensed under the [MIT License](https://opensource.org/licenses/MIT). +require('./target.js'); +``` -This project also depends on external libraries that may use different open-source -licenses. For a complete list of included licenses, please see the -[LICENSE](https://github.com/nodejs/node/blob/main/LICENSE) file. +使用 RS 兼容服务: -If you are contributing documentation or source changes, please ensure your -additions comply with the project’s license guidelines. +```bash +mode rs_mode_server/server.js +``` -[Code of Conduct]: https://github.com/nodejs/admin/blob/HEAD/CODE_OF_CONDUCT.md -[Contributing to the project]: CONTRIBUTING.md -[Node.js website]: https://nodejs.org/ -[OpenJS Foundation]: https://openjsf.org/ -[Strategic initiatives]: doc/contributing/strategic-initiatives.md -[Technical values and prioritization]: doc/contributing/technical-values.md -[Working Groups]: https://github.com/nodejs/TSC/blob/HEAD/WORKING_GROUPS.md +服务默认监听 `http://127.0.0.1:8080`,提供 `GET /health` 与原有的 +`POST /rs_env` 协议。 + +## 安全与能力边界 + +- Mode 是脚本兼容层,不是 Chromium:没有页面渲染、CSS/layout、真实 iframe 文档、 + Canvas 图像指纹、WebGL、WebRTC、AudioContext、浏览器 Worker API、真实导航,也不会 + 自动执行初始 HTML 内的 `
', + navigator: { userAgent: 'Mozilla/5.0 ModeTest' }, + }); + + assert.strictEqual(window.innerWidth, 1920); + assert.strictEqual(window.outerHeight, 1080); + assert.strictEqual(screen.orientation.type, 'landscape-primary'); + assert.strictEqual(location.ancestorOrigins.length, 0); + assert.strictEqual(clientInformation, navigator); + assert.strictEqual(navigator.appName, 'Netscape'); + assert.strictEqual(navigator.appVersion, '5.0 ModeTest'); + assert.strictEqual(navigator.connection.effectiveType, '4g'); + assert(navigator.connection instanceof NetworkInformation); + assert.strictEqual(Object.prototype.toString.call(navigator.connection), '[object NetworkInformation]'); + assert.deepStrictEqual(Object.keys(navigator.connection), []); + assert.strictEqual(navigator.mimeTypes.length, 2); + assert.strictEqual(navigator.mimeTypes.namedItem('application/pdf').suffixes, 'pdf'); + assert.strictEqual(navigator.sendBeacon('/beacon', 'body'), true); + assert.strictEqual(String(navigator.getBattery), 'function getBattery() { [native code] }'); + const battery = await navigator.getBattery(); + assert(battery instanceof BatteryManager); + assert.strictEqual(Object.prototype.toString.call(battery), '[object BatteryManager]'); + assert.strictEqual(battery.charging, true); + + assert(document instanceof Document); + assert(document.body instanceof HTMLElement); + assert(document.createTextNode('text') instanceof Text); + const inputs = document.getElementsByTagName('input'); + assert.strictEqual(Object.prototype.toString.call(inputs), '[object HTMLCollection]'); + assert(inputs instanceof HTMLCollection); + assert.strictEqual(inputs.constructor, HTMLCollection); + assert.deepStrictEqual(Object.keys(inputs), ['0']); + assert.strictEqual(Object.prototype.toString.call(document), '[object HTMLDocument]'); + assert.strictEqual(Object.prototype.toString.call(document.head), '[object HTMLHeadElement]'); + assert.strictEqual(Object.prototype.toString.call(document.body), '[object HTMLBodyElement]'); + assert.deepStrictEqual(Object.keys(document), []); + assert.deepStrictEqual(Object.keys(document.body), []); + assert.deepStrictEqual(Object.keys(navigator.mimeTypes), ['0', '1']); + assert.deepStrictEqual(document.createExpression(), Object.create(null)); + + const anchor = document.createElement('a'); + assert.strictEqual(anchor.href, ''); + anchor.href = '/next?q=1#hash'; + assert(anchor instanceof HTMLAnchorElement); + assert.strictEqual(anchor.href, 'https://example.test/next?q=1#hash'); + assert.strictEqual(anchor.host, 'example.test'); + + const div = document.createElement('div'); + assert(div instanceof HTMLDivElement); + assert.strictEqual(div.constructor, HTMLDivElement); + assert.strictEqual(Object.prototype.toString.call(div), '[object HTMLDivElement]'); + assert.strictEqual(String(div.getAttribute), 'function getAttribute() { [native code] }'); + + const iframe = document.createElement('iframe'); + assert(iframe instanceof HTMLIFrameElement); + assert.strictEqual(iframe.constructor, HTMLIFrameElement); + assert.strictEqual(Object.prototype.toString.call(iframe), '[object HTMLIFrameElement]'); + assert.strictEqual(iframe.contentDocument, null); + assert.strictEqual(iframe.contentWindow, null); + assert.strictEqual(String(location.assign), 'function assign() { [native code] }'); + + const meta = document.querySelector('#challenge'); + assert(meta instanceof HTMLMetaElement); + assert.strictEqual(Object.prototype.toString.call(meta), '[object HTMLMetaElement]'); + assert.strictEqual(meta.content, 'initial-content'); + meta.content = 'updated-content'; + assert.strictEqual(meta.getAttribute('content'), 'updated-content'); + const script = document.querySelector('script'); + assert(script instanceof HTMLScriptElement); + assert.strictEqual(Object.prototype.toString.call(script), '[object HTMLScriptElement]'); + assert.strictEqual(script.innerText, 'initial-script'); + script.innerText = 'updated-script'; + assert.strictEqual(script.textContent, 'updated-script'); + assert.strictEqual(script.src, 'https://example.test/challenge.js'); + assert.strictEqual(document.getElementsByTagName('script').length, 1); + + const form = document.querySelector('#form'); + assert(form instanceof HTMLFormElement); + assert.strictEqual(form.elements.namedItem('token').value, 'initial'); + + const canvas = document.createElement('canvas'); + assert(canvas instanceof HTMLCanvasElement); + assert(canvas.getContext('2d') instanceof CanvasRenderingContext2D); + assert.strictEqual(canvas.toDataURL(), 'data:,'); + + const parsed = new DOMParser().parseFromString('

ok

', 'text/html'); + assert(parsed instanceof Document); + assert.strictEqual(parsed.querySelector('#parsed').textContent, 'ok'); + + const xhr = new XMLHttpRequest(); + const states = []; + xhr.addEventListener('readystatechange', () => states.push(xhr.readyState)); + xhr.open('POST', '/challenge'); + xhr.setRequestHeader('x-test', 'yes'); + xhr.send('payload'); + assert.deepStrictEqual(states, [XMLHttpRequest.OPENED, XMLHttpRequest.DONE]); + assert.strictEqual(xhr.status, 0); + assert.strictEqual(xhr.responseURL, 'https://example.test/challenge'); + assert.strictEqual(window.encode_url, '/challenge'); + assert.strictEqual(window.encode_data, 'payload'); + + let observed = false; + const observer = new MutationObserver(() => { observed = true; }); + observer.observe(document.body, { childList: true }); + assert.strictEqual(observer.takeRecords().length, 0); + observer.disconnect(); + assert.strictEqual(observed, false); + assert.strictEqual(indexedDB.open('mode-test').result.name, 'mode-test'); + assert.strictEqual(chrome.app.isInstalled, false); + assert.strictEqual(typeof chrome.loadTimes, 'function'); + assert.strictEqual(msCrypto, globalThis.crypto); + assert.strictEqual(typeof msCrypto.getRandomValues, 'function'); + assert.strictEqual(window.open('https://example.test/').closed, false); + assert.strictEqual(window.prompt('question'), null); + assert.strictEqual(typeof webkitRequestFileSystem, 'function'); + + assert.strictEqual(document.all[0], document.documentElement); + assert.strictEqual(document.all.form, form); + })().catch((error) => { + console.error(error.stack); + process.exitCode = 1; + }); + `, +], { status: 0, stderr: '' }); + +spawnSyncAndAssert(process.execPath, [ + '-e', + ` + const assert = require('node:assert'); + const { install } = require('node:browser-env'); + install({ url: 'https://example.test/', hideNodeGlobals: true }); + assert.deepStrictEqual( + new Function('return [typeof global, typeof process, typeof require, typeof module, typeof exports, typeof __dirname, typeof __filename, typeof setImmediate, typeof clearImmediate].join(\",\")')(), + 'undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined', + ); + assert.strictEqual(typeof Buffer, 'function'); + for (const name of ['global', 'process', 'require', 'module', 'exports', '__dirname', '__filename', 'setImmediate', 'clearImmediate']) { + assert.strictEqual(Object.getOwnPropertyDescriptor(globalThis, name), undefined); + } + `, +], { status: 0, stderr: '' }); + +spawnSyncAndAssert(process.execPath, [ + '-e', + ` + const assert = require('node:assert'); + const { install } = require('node:browser-env'); + assert.throws( + () => install({ url: 'https://example.test/', document: { properties: { all: null } } }), + /protected browser environment property/, + ); + assert.strictEqual(typeof globalThis.document, 'undefined'); + assert.throws( + () => install({ url: 'https://example.test/', document: { descriptors: { createElement: { value: null } } } }), + /protected browser environment property/, + ); + assert.throws( + () => install({ url: 'https://example.test/', window: { properties: { location: null } } }), + /protected browser environment property/, + ); + `, +], { status: 0, stderr: '' }); + +spawnSyncAndAssert(process.execPath, [ + '--input-type=module', + '-e', + ` + import assert from 'node:assert'; + import { install, isInstalled } from 'node:browser-env'; + assert.strictEqual(isInstalled(), false); + install({ url: 'https://esm.example.test/' }); + assert.strictEqual(document.location.hostname, 'esm.example.test'); + `, +], { status: 0, stderr: '' }); + +const profile = fixtures.path('browser-env', 'profile.json'); +spawnSyncAndAssert(process.execPath, [ + `--browser-env-profile=${profile}`, + '-e', + ` + const assert = require('node:assert'); + const env = require('node:browser-env'); + assert.strictEqual(env.isInstalled(), true); + assert.throws(() => env.install({ url: 'https://again.example.test/' }), /already installed/); + assert.strictEqual(location.hostname, 'profile.example.test'); + assert.strictEqual(document.querySelector('#app').textContent, 'ok'); + assert.strictEqual(document.visibilityState, 'hidden'); + assert.strictEqual(window.profileEnabled, true); + assert.strictEqual(navigator.userAgent, 'ProfileBrowser/1.0'); + `, +], { status: 0, stderr: '' }); + +spawnSyncAndAssert(process.execPath, [ + '--browser-env-profile=/definitely/missing/profile.json', + '-e', '', +], { status: 1, stderr: /Unable to load --browser-env-profile/ }); diff --git a/tools/browser-env-source-runner.js b/tools/browser-env-source-runner.js new file mode 100644 index 000000000000..702e97fc0f11 --- /dev/null +++ b/tools/browser-env-source-runner.js @@ -0,0 +1,177 @@ +'use strict'; + +// Executes the browser environment JavaScript from the working tree without +// rebuilding Node. The running Mode binary supplies only the native +// `document.all` binding; `lib/internal/browser_env.js` is loaded from disk. +// +// Run with a previously built Mode binary: +// mode --expose-internals tools/browser-env-source-runner.js \ +// js_reverse_cache/.../first_412.html --deterministic +// Add --trace only when inspecting environment accesses. It instruments DOM +// methods and is therefore deliberately not part of the production-equivalent +// default execution. + +const fs = require('node:fs'); +const path = require('node:path'); +const vm = require('node:vm'); +const { internalBinding, primordials } = require('internal/test/binding'); +const nodeProcess = process; + +const [htmlPath, ...flags] = nodeProcess.argv.slice(2); +if (!htmlPath || flags.some((flag) => !['--deterministic', '--trace'].includes(flag))) { + throw new Error('usage: mode --expose-internals tools/browser-env-source-runner.js [--deterministic] [--trace]'); +} + +const root = path.resolve(__dirname, '..'); +const url = 'https://etax.qingdao.chinatax.gov.cn:8443/'; +const ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' + + '(KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'; +const html = fs.readFileSync(htmlPath, 'utf8'); + +function loadBrowserEnvironmentFromSource() { + const sourcePath = path.join(root, 'lib', 'internal', 'browser_env.js'); + const source = fs.readFileSync(sourcePath, 'utf8'); + const module = { exports: {} }; + const factory = vm.runInThisContext( + `(function(exports, module, require, primordials, internalBinding) {\n${source}\n})`, + { filename: sourcePath }, + ); + factory(module.exports, module, require, primordials, internalBinding); + return module.exports; +} + +function parseAttributes(source) { + const attributes = {}; + const matcher = /([^\s=/>]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g; + let match; + while ((match = matcher.exec(source)) !== null) { + attributes[match[1].toLowerCase()] = match[2] ?? match[3] ?? match[4] ?? ''; + } + return attributes; +} + +function parseChallenge(source) { + const metas = Array.from(source.matchAll(/]*)>/gi), (match) => parseAttributes(match[1])); + const scripts = Array.from(source.matchAll(/]*)>([\s\S]*?)<\/script\s*>/gi), (match) => ({ + attributes: parseAttributes(match[1]), + content: match[2], + })); + const meta = metas.find((tag) => tag.r === 'm'); + const inline = scripts.find((tag) => tag.attributes.r === 'm' && !tag.attributes.src && tag.content.includes('$_')); + const external = scripts.find((tag) => tag.attributes.r === 'm' && tag.attributes.src); + if (!meta || !inline || !external) throw new Error('missing r=m challenge parts'); + return { external: external.attributes.src, inline: inline.content }; +} + +function deterministicPrelude() { + if (!flags.includes('--deterministic')) return ''; + return ` + var __modeSeed = 0x13579bdf; + Math.random = function random() { + __modeSeed = (__modeSeed * 1664525 + 1013904223) >>> 0; + return __modeSeed / 0x100000000; + }; + var __modeRealDate = Date; + var __modeTime = 1700000000000; + Date = function Date() { + if (!new.target) return __modeRealDate(); + return arguments.length ? Reflect.construct(__modeRealDate, arguments) : new __modeRealDate(__modeTime++); + }; + Date.now = function now() { return __modeTime++; }; + Date.parse = __modeRealDate.parse; + Date.UTC = __modeRealDate.UTC; + Date.prototype = __modeRealDate.prototype; + `; +} + +function cookieSummary(cookieHeader) { + return String(cookieHeader).split(';').flatMap((part) => { + const separator = part.indexOf('='); + if (separator <= 0) return []; + const value = part.slice(separator + 1).trim(); + let hash = 2166136261; + for (let index = 0; index < value.length; index++) { + hash = Math.imul(hash ^ value.charCodeAt(index), 16777619); + } + return [{ + name: part.slice(0, separator).trim(), + valueHash: (hash >>> 0).toString(16), + valueLength: value.length, + }]; + }); +} + +const traceEnabled = flags.includes('--trace'); +const hook = traceEnabled ? + fs.readFileSync(path.join(root, 'js_reverse_cache', 'compare_browser_env_hook.js'), 'utf8') : ''; +const challenge = parseChallenge(html); +const fileName = path.basename(new URL(challenge.external, url).pathname); +const outJs = fs.readFileSync(path.join(root, 'rs_mode_server', 'out_js', fileName), 'utf8'); +const { install } = loadBrowserEnvironmentFromSource(); + +install({ + url, + html, + hideNodeGlobals: true, + navigator: { + appName: 'Netscape', + appVersion: ua.replace(/^Mozilla\//, ''), + language: 'zh-CN', + languages: ['zh-CN', 'zh'], + maxTouchPoints: 0, + platform: 'Win32', + userAgent: ua, + vendor: 'Google Inc.', + webdriver: false, + }, + screen: { + availHeight: 1040, + availLeft: 0, + availTop: 0, + availWidth: 1920, + colorDepth: 24, + height: 1080, + pixelDepth: 24, + width: 1920, + }, + window: { + properties: { + innerHeight: 1080, + innerWidth: 1920, + outerHeight: 1080, + outerWidth: 1920, + screenLeft: 0, + screenTop: 0, + screenX: 0, + screenY: 0, + }, + }, + document: { properties: { visibilityState: 'hidden' } }, +}); + +const originalConsole = globalThis.console; +globalThis.console = { debug() {}, error() {}, info() {}, log() {}, warn() {} }; +try { + const result = new Function(`${deterministicPrelude()}\n${hook}\n${challenge.inline}\n${outJs} + const result = { + label: 'source', + source: 'lib/internal/browser_env.js', + traceEnabled: ${traceEnabled}, + nodeGlobals: { + Buffer: typeof Buffer, + clearImmediate: typeof clearImmediate, + exports: typeof exports, + global: typeof global, + module: typeof module, + process: typeof process, + require: typeof require, + setImmediate: typeof setImmediate, + }, + cookies: (${cookieSummary.toString()})(document.cookie), + }; + if (${traceEnabled}) Object.assign(result, __modeEnvTrace.dump('source')); + return result;`)(); + nodeProcess.stdout.write(JSON.stringify(result), () => nodeProcess.exit(0)); +} finally { + globalThis.console = originalConsole; +} diff --git a/tools/gyp/pylib/gyp/generator/msvs.py b/tools/gyp/pylib/gyp/generator/msvs.py index 42bee33d9b63..858bd230ab22 100644 --- a/tools/gyp/pylib/gyp/generator/msvs.py +++ b/tools/gyp/pylib/gyp/generator/msvs.py @@ -2896,7 +2896,7 @@ def _GenerateMSBuildRuleXmlFile(xml_path, msbuild_rules): def _GetConfigurationAndPlatform(name, settings, spec): configuration = name.rsplit("_", 1)[0] platform = settings.get("msvs_configuration_platform", "Win32") - if spec["toolset"] == "host" and platform == "arm64": + if spec["toolset"] == "host" and platform in ("Win32", "arm64"): platform = "x64" # Host-only tools are always built for x64 return (configuration, platform) diff --git a/vcbuild.bat b/vcbuild.bat index 28494815df1d..4799ffca014c 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -275,11 +275,6 @@ if defined cfg set configure_flags=%configure_flags% --control-flow if defined v8temporal set configure_flags=%configure_flags% --v8-enable-temporal-support if defined v8windbg set configure_flags=%configure_flags% --enable-v8windbg -if "%target_arch%"=="x86" ( - echo "32-bit Windows builds are not supported anymore." - exit /b 1 -) - if not exist "%~dp0deps\icu" goto no-depsicu if "%target%"=="Clean" echo deleting %~dp0deps\icu if "%target%"=="Clean" rmdir /S /Q %~dp0deps\icu @@ -457,6 +452,7 @@ if defined nobuild goto :after-build set "msbcpu=/m:2" if "%NUMBER_OF_PROCESSORS%"=="1" set "msbcpu=/m:1" set "msbplatform=x64" +if "%target_arch%"=="x86" set "msbplatform=Win32" if "%target_arch%"=="arm64" set "msbplatform=ARM64" if "%target%"=="Build" ( if defined no_cctest set target=node