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

이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다. 통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다. 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