@miaskiewicz/turbo-dom 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/package.json +1 -1
- package/pkg/turbo_dom_parser_bg.wasm +0 -0
- package/pkg-web/turbo_dom_parser_bg.wasm +0 -0
- package/src/runtime/index.mjs +4 -0
- package/src/runtime/window.mjs +52 -4
- package/turbo-dom-parser.darwin-arm64.node +0 -0
- package/turbo-dom-parser.linux-arm64-gnu.node +0 -0
- package/turbo-dom-parser.linux-x64-gnu.node +0 -0
- package/turbo-dom-parser.linux-x64-musl.node +0 -0
- package/turbo-dom-parser.win32-x64-msvc.node +0 -0
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miaskiewicz/turbo-dom",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Faster, more spec-correct DOM for test runners — native html5ever (Rust/WASM) parser + lazy copy-on-write DOM. A drop-in-style alternative to jsdom/happy-dom for vitest & jest.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "index.js",
|
|
Binary file
|
|
Binary file
|
package/src/runtime/index.mjs
CHANGED
|
@@ -18,6 +18,10 @@ export * from './dom.mjs';
|
|
|
18
18
|
// or force 'wasm' | 'native' | 'auto'. See parser.mjs.
|
|
19
19
|
export { setParser, setParserMode, getParser } from './parser.mjs';
|
|
20
20
|
|
|
21
|
+
// Clock seam (public API): install a virtual clock so time-gated rAF loops (MUI
|
|
22
|
+
// transitions) terminate under a synchronous timer-drain render tier. See window.mjs.
|
|
23
|
+
export { setClock } from './window.mjs';
|
|
24
|
+
|
|
21
25
|
// Parse cache: the SoA buffer is READ-ONLY (every mutation goes to a Document's
|
|
22
26
|
// own __kids/__attrs/__cache overlay, never the buffer), so the same buffer can
|
|
23
27
|
// back many Documents. Test suites call setup with the SAME html per file
|
package/src/runtime/window.mjs
CHANGED
|
@@ -81,6 +81,38 @@ function tagClass(matcher) {
|
|
|
81
81
|
const hostSetTimeout = globalThis.setTimeout;
|
|
82
82
|
const hostClearTimeout = globalThis.clearTimeout;
|
|
83
83
|
|
|
84
|
+
// MessageChannel/MessagePort — React 19's scheduler posts its work loop through a
|
|
85
|
+
// MessagePort hop (falling back to setTimeout only if absent). We ship a real,
|
|
86
|
+
// self-contained polyfill whose delivery is scheduled via the LIVE globalThis.
|
|
87
|
+
// setTimeout (read at call time, like rAF), so a render tier that owns setTimeout
|
|
88
|
+
// (a virtual-clock pump) catches every scheduler hop → React runs in virtual time,
|
|
89
|
+
// bounded by the drain/deadline — and it exists in the bare V8 isolate, which has
|
|
90
|
+
// no host MessageChannel. See MESSAGECHANNEL-VIRTUALIZE.md + the clock note in
|
|
91
|
+
// CLAUDE.md. Delivery uses delay 0 (a yield, not elapsed time — animations get
|
|
92
|
+
// their time from rAF's 16ms frame).
|
|
93
|
+
class MessagePort extends EventTarget {
|
|
94
|
+
constructor() { super(); this.onmessage = null; this.onmessageerror = null; this.__other = null; }
|
|
95
|
+
start() {}
|
|
96
|
+
close() { this.__other = null; }
|
|
97
|
+
postMessage(data) {
|
|
98
|
+
const target = this.__other;
|
|
99
|
+
if (!target) return;
|
|
100
|
+
(globalThis.setTimeout || hostSetTimeout)(() => {
|
|
101
|
+
const ev = new Event('message'); ev.data = data;
|
|
102
|
+
if (typeof target.onmessage === 'function') target.onmessage(ev);
|
|
103
|
+
target.dispatchEvent(ev);
|
|
104
|
+
}, 0);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
class MessageChannel {
|
|
108
|
+
constructor() {
|
|
109
|
+
this.port1 = new MessagePort();
|
|
110
|
+
this.port2 = new MessagePort();
|
|
111
|
+
this.port1.__other = this.port2;
|
|
112
|
+
this.port2.__other = this.port1;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
84
116
|
// Env-independent lazy global factories — built ONCE at module load, shared by
|
|
85
117
|
// every window (none capture document/url/windowProxy). Materialization still
|
|
86
118
|
// self-replaces onto the per-env base, so each env gets its own instance; only
|
|
@@ -92,8 +124,13 @@ const SHARED_LAZY = {
|
|
|
92
124
|
getComputedStyle: () => makeGetComputedStyle(),
|
|
93
125
|
IntersectionObserver: () => IntersectionObserver,
|
|
94
126
|
ResizeObserver: () => ResizeObserver,
|
|
95
|
-
|
|
96
|
-
|
|
127
|
+
// rAF: schedule via the LIVE setTimeout (so a render tier that swaps in a timer
|
|
128
|
+
// queue catches the reschedule) with a non-zero frame delay, and stamp the
|
|
129
|
+
// callback with the injectable clock now() — a virtual clock that ADVANCES per
|
|
130
|
+
// frame lets time-gated transitions (MUI Fade/Grow, react-transition-group:
|
|
131
|
+
// progress = (now-start)/duration) reach progress>=1 and stop rescheduling.
|
|
132
|
+
requestAnimationFrame: () => (cb) => (globalThis.setTimeout || hostSetTimeout)(() => cb(now()), 16),
|
|
133
|
+
cancelAnimationFrame: () => (id) => (globalThis.clearTimeout || hostClearTimeout)(id),
|
|
97
134
|
navigator: () => ({
|
|
98
135
|
userAgent: 'Mozilla/5.0 (turbo-dom) AppleWebKit/537.36',
|
|
99
136
|
platform: 'turbo-dom', vendor: '', language: 'en-US', languages: ['en-US'],
|
|
@@ -103,7 +140,7 @@ const SHARED_LAZY = {
|
|
|
103
140
|
permissions: { query: async () => ({ state: 'prompt', addEventListener() {}, removeEventListener() {} }) },
|
|
104
141
|
sendBeacon: () => true, vibrate: () => false,
|
|
105
142
|
}),
|
|
106
|
-
performance: () => ({ now
|
|
143
|
+
performance: () => ({ now, timeOrigin: 0, mark() {}, measure() {}, getEntriesByName: () => [], getEntriesByType: () => [], clearMarks() {}, clearMeasures() {} }),
|
|
107
144
|
Storage: () => Storage,
|
|
108
145
|
devicePixelRatio: () => 1,
|
|
109
146
|
innerWidth: () => 1024,
|
|
@@ -211,6 +248,17 @@ const performanceNow = typeof hostPerformance?.now === 'function'
|
|
|
211
248
|
? () => hostPerformance.now()
|
|
212
249
|
: () => Date.now();
|
|
213
250
|
|
|
251
|
+
// Injectable clock. `window.performance.now()` and the rAF callback timestamp both
|
|
252
|
+
// read through now(), so a single setClock() drives both. Default (null) → real
|
|
253
|
+
// host clock, no behavior change for vitest/jest. The render tier installs a VIRTUAL
|
|
254
|
+
// clock that advances per drained frame so time-gated rAF loops terminate (see the
|
|
255
|
+
// synthetic-geometry/clock note in CLAUDE.md). Resolves performanceNow lazily, so it
|
|
256
|
+
// stays defined regardless of where setClock is called. (Date.now is the embedder's
|
|
257
|
+
// to override — turbo-dom never shadows it.)
|
|
258
|
+
let __clock = null;
|
|
259
|
+
function now() { return __clock ? __clock() : performanceNow(); }
|
|
260
|
+
export function setClock(fn) { __clock = typeof fn === 'function' ? fn : null; }
|
|
261
|
+
|
|
214
262
|
// base64 without depending on the Node `Buffer` global — prefer the platform
|
|
215
263
|
// btoa/atob, then Buffer (Node), then a pure-JS fallback so a bare-isolate page
|
|
216
264
|
// that calls btoa/atob doesn't throw.
|
|
@@ -386,7 +434,7 @@ const STATIC_BASE = {
|
|
|
386
434
|
crypto: globalThis.crypto, Crypto: globalThis.Crypto, SubtleCrypto: globalThis.SubtleCrypto,
|
|
387
435
|
btoa: (s) => turboBtoa(String(s)),
|
|
388
436
|
atob: (s) => turboAtob(String(s)),
|
|
389
|
-
MessageChannel
|
|
437
|
+
MessageChannel, MessagePort,
|
|
390
438
|
BroadcastChannel: globalThis.BroadcastChannel, EventSource: globalThis.EventSource,
|
|
391
439
|
reportError: (e) => { void e; },
|
|
392
440
|
requestIdleCallback: (cb) => hostSetTimeout(() => cb({ didTimeout: false, timeRemaining: () => 50 }), 0),
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|