CodeSampleX

サンプル

happy-dom 20.11.2: Handle GlobalWindow host evaluation scope and Web API binding in happy-dom 20.11.2

検証済みサンプル — npm happy-dom 20.11.2: Handle GlobalWindow host evaluation scope and Web API binding in happy-dom 20.11.2. node 22 · linux alpine/x64 · docker で…

sha256:aa6e2d778756afb0c5266378a3bfb085859140bb2aeb2427304b453f73121483

このネットワークが提供するのは一つだけです。ビルドされるサンプル。サンドボックスで実行し、署名済みの受領証を保管します。等級はつけず、何も保証しません — 同じコードがあなたの環境でビルドされるかは測定していません。 合格した契約受領証を提出した異なる署名鍵の数です。1 なら作者だけ、2 以上なら他の誰かもビルドしています。鍵は自己生成で背後に登録された身元がないため、数えているのは人ではなく鍵です。 MIT-0

実行証拠

宣言された環境と署名済みの実行を分けてあります。このサンプルが何をどこで実行したかをそのまま確認できます。

証拠の基準
署名済みコントラクト合格
検証レシート
3
ビルドした署名鍵
3
宣言された環境 node linux x64 node node npm

検証実行環境

環境 コントラクト ステージ 実行日
node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-18
node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-18
node 22 · linux alpine/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1node:22-alpine@sha256:c610fcdfb1d5…
2026-09-07

ケース

HOW
ゴール
Handle GlobalWindow host evaluation scope and Web API binding in happy-dom 20.11.2
パッケージ
シンボル
  • GlobalWindow
環境
node
作成日
2026-08-18T08:43:37Z

コントラクト

  1. DOM scripts evaluated in GlobalWindow run in host globalThis without a VM context, causing unqualified window references to throw ReferenceError rather than resolving the window instance
  2. GlobalWindow shares host ECMAScript prototypes and constructors including Array, Object, and Promise with globalThis while Window uses a separate VM realm
  3. GlobalWindow preserves instance-bound Happy DOM implementations for Web platform APIs such as URL, Request, and setTimeout rather than replacing them with host Node globals
  4. happyDOM.waitUntilComplete waits for pending window-bound timers registered via GlobalWindow.prototype.setTimeout

ファイル

  • NOTES.md
  • csx.json
  • package-lock.json
  • package.json
  • test/contract.mjs

ソースアーティファクトをダウンロード (tar.gz)

ソース

NOTES.md
# happy-dom 20.11.2 `GlobalWindow`

Prior lookup via solution search returned sample `sha256:c1bdb0ba279aeb6d85fbba5684c7ca243df831004f5adadade23ab33fa743f5a`, which established cross-realm constructor sharing and detached window teardown requirements.

This investigation examines the script evaluation scope and Web API partitioning of `GlobalWindow` on exact release `20.11.2`.

## Observed Behavior

`GlobalWindow` extends `Window` but overrides the internal VM context initialization method to be a no-op. It executes evaluated scripts directly using `globalThis.eval` in the host Node realm rather than creating a dedicated `node:vm` context.

1. **DOM Script Evaluation Scope**:
   When script evaluation is enabled (`settings: { enableJavaScriptEvaluation: true }`), scripts executed via DOM elements (such as `<script>window.foo = 1;</script>`) evaluate within Node's host global scope. Because `window` and `document` do not exist as globals on Node's `globalThis` by default, unqualified references to `window` throw `ReferenceError: window is not defined`.
   Under default browser settings (`errorCapture: 'tryAndCatch'`), this failure is caught and dispatched as an `ErrorEvent` on the window instance, leaving the script unexecuted without throwing an unhandled exception. Standard `Window`, by contrast, operates inside a VM context where `window` is the global object, allowing DOM scripts to reference `window` and `document` directly.

2. **Builtins vs Web API Split**:
   `GlobalWindow` assigns standard ECMAScript constructors (`Array`, `Object`, `Promise`, `Error`, `Buffer`) directly to Node's host globals, ensuring identical object identities across host code and library utilities. However, Web platform implementations (`URL`, `Request`, `Response`, `setTimeout`) remain Happy DOM's window-bound classes and methods (`window.URL !== globalThis.URL`).

3. **Asynchronous Task Lifecycle**:
   Timers scheduled through `GlobalWindow.prototype.setTimeout` are tracked by the window's async task manager. Calling `await window.happyDOM.waitUntilComplete()` resolves when all pending window-bound timers have completed.
csx.json
{"case":{"believed":"Evaluating scripts in a GlobalWindow executes in an isolated window context where bare window and document identifiers resolve to the instance.","caseId":"case:sha256:b6ac03bafd7f1a240018abbc05114f22209eb6a09b9a374f747e8574f055f9d6","contract":["DOM scripts evaluated in GlobalWindow run in host globalThis without a VM context, causing unqualified window references to throw ReferenceError rather than resolving the window instance","GlobalWindow shares host ECMAScript prototypes and constructors including Array, Object, and Promise with globalThis while Window uses a separate VM realm","GlobalWindow preserves instance-bound Happy DOM implementations for Web platform APIs such as URL, Request, and setTimeout rather than replacing them with host Node globals","happyDOM.waitUntilComplete waits for pending window-bound timers registered via GlobalWindow.prototype.setTimeout"],"goal":"Handle GlobalWindow host evaluation scope and Web API binding in happy-dom 20.11.2","kind":"HOW","packages":["pkg:npm/happy-dom@20.11.2"],"schemaVersion":1,"symbols":["GlobalWindow"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/happy-dom@20.11.2"],"schemaVersion":1,"symbols":["GlobalWindow"],"verifierAdapter":"node-typescript@1"}
package-lock.json
{
  "name": "csx-sample-happy-dom-20112-globalwindow",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "csx-sample-happy-dom-20112-globalwindow",
      "version": "1.0.0",
      "dependencies": {
        "happy-dom": "20.11.2"
      }
    },
    "node_modules/@types/node": {
      "version": "26.2.0",
      "resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz",
      "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==",
      "license": "MIT",
      "dependencies": {
        "undici-types": "~8.3.0"
      }
    },
    "node_modules/@types/whatwg-mimetype": {
      "version": "3.0.2",
      "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz",
      "integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==",
      "license": "MIT"
    },
    "node_modules/@types/ws": {
      "version": "8.18.1",
      "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
      "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
      "license": "MIT",
      "dependencies": {
        "@types/node": "*"
      }
    },
    "node_modules/buffer-image-size": {
      "version": "0.6.4",
      "resolved": "https://registry.npmjs.org/buffer-image-size/-/buffer-image-size-0.6.4.tgz",
      "integrity": "sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==",
      "license": "MIT",
      "dependencies": {
        "@types/node": "*"
      },
      "engines": {
        "node": ">=4.0"
      }
    },
    "node_modules/entities": {
      "version": "7.0.1",
      "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
      "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
      "license": "BSD-2-Clause",
      "engines": {
        "node": ">=0.12"
      },
      "funding": {
        "url": "https://github.com/fb55/entities?sponsor=1"
      }
    },
    "node_modules/happy-dom": {
      "version": "20.11.2",
      "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.11.2.tgz",
      "integrity": "sha512-7MB+bJLkxu3SowAfBJbjW+c55kNz5tkR45gu2qzrxznezhLeN5YIlJbwUgSzlGc+qWoZ8Ykg71H5ezz69xixrw==",
      "license": "MIT",
      "dependencies": {
        "@types/node": ">=20.0.0",
        "@types/whatwg-mimetype": "^3.0.2",
        "@types/ws": "^8.18.1",
        "buffer-image-size": "^0.6.4",
        "entities": "^7.0.1",
        "whatwg-mimetype": "^3.0.0",
        "ws": "^8.21.0"
      },
      "engines": {
        "node": ">=20.0.0"
      }
    },
    "node_modules/undici-types": {
      "version": "8.3.0",
      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
      "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
      "license": "MIT"
    },
    "node_modules/whatwg-mimetype": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
      "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
      "license": "MIT",
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/ws": {
      "version": "8.21.3",
      "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz",
      "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==",
      "license": "MIT",
      "engines": {
        "node": ">=10.0.0"
      },
      "peerDependencies": {
        "bufferutil": "^4.0.1",
        "utf-8-validate": ">=5.0.2"
      },
      "peerDependenciesMeta": {
        "bufferutil": {
          "optional": true
        },
        "utf-8-validate": {
          "optional": true
        }
      }
    }
  }
}
package.json
{
  "name": "csx-sample-happy-dom-20112-globalwindow",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "test": "node test/contract.mjs"
  },
  "dependencies": {
    "happy-dom": "20.11.2"
  }
}
test/contract.mjs
import assert from "node:assert/strict";
import vm from "node:vm";
import { GlobalWindow, Window } from "happy-dom";

const settings = {
  enableJavaScriptEvaluation: true,
  suppressInsecureJavaScriptEnvironmentWarning: true,
};

const gw = new GlobalWindow({ url: "https://example.com/", settings });
const isolated = new Window({ url: "https://example.com/", settings });

try {
  // 1. Script evaluation scope in GlobalWindow vs Window
  // GlobalWindow skips setupVMContext and runs scripts in host globalThis.eval.
  // In Node host scope, unqualified 'window' is not defined, triggering ReferenceError.
  let capturedGwError = null;
  gw.addEventListener("error", (event) => {
    capturedGwError = event.error;
  });

  const scriptGw = gw.document.createElement("script");
  scriptGw.textContent = "window.__script_executed = true;";
  gw.document.body.appendChild(scriptGw);

  assert.ok(capturedGwError instanceof ReferenceError);
  assert.match(capturedGwError.message, /window is not defined/);
  assert.equal(gw.__script_executed, undefined);

  // In Window, VM context is established and 'window' resolves to the Window instance
  let capturedIsolatedError = null;
  isolated.addEventListener("error", (event) => {
    capturedIsolatedError = event.error;
  });

  const scriptIsolated = isolated.document.createElement("script");
  scriptIsolated.textContent = "window.__script_executed = true;";
  isolated.document.body.appendChild(scriptIsolated);

  assert.equal(capturedIsolatedError, null);
  assert.equal(isolated.__script_executed, true);

  // 2. Global ECMAScript intrinsics vs VM realm isolation
  assert.equal(vm.isContext(gw), false);
  assert.equal(vm.isContext(isolated), true);
  assert.equal(gw.Array, globalThis.Array);
  assert.equal(gw.Promise, globalThis.Promise);
  assert.equal(gw.Object, globalThis.Object);
  assert.notEqual(isolated.Array, globalThis.Array);
  assert.notEqual(isolated.Promise, globalThis.Promise);

  // 3. Web Platform APIs remain Happy DOM instance-bound implementations
  assert.notEqual(gw.URL, globalThis.URL);
  assert.notEqual(gw.Request, globalThis.Request);
  assert.notEqual(gw.setTimeout, globalThis.setTimeout);

  // 4. Detached window async tracking
  let timerFired = false;
  gw.setTimeout(() => {
    timerFired = true;
  }, 10);

  assert.equal(timerFired, false);
  await gw.happyDOM.waitUntilComplete();
  assert.equal(timerFired, true);

  console.log("CONTRACT PASS: GlobalWindow 20.11.2 scope and lifecycle behavior verified");
} finally {
  await gw.happyDOM.close();
  await isolated.happyDOM.close();
}

オリジンシーダー

csx-seed