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…

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