@lifeart/async-dom 2.0.0-alpha.3

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.
Files changed (117) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +623 -0
  3. package/dist/base.d.cts +398 -0
  4. package/dist/base.d.cts.map +1 -0
  5. package/dist/base.d.ts +398 -0
  6. package/dist/base.d.ts.map +1 -0
  7. package/dist/cli.cjs +528 -0
  8. package/dist/cli.cjs.map +1 -0
  9. package/dist/cli.d.cts +1 -0
  10. package/dist/cli.d.ts +1 -0
  11. package/dist/cli.js +493 -0
  12. package/dist/cli.js.map +1 -0
  13. package/dist/debug.d.cts +145 -0
  14. package/dist/debug.d.cts.map +1 -0
  15. package/dist/debug.d.ts +145 -0
  16. package/dist/debug.d.ts.map +1 -0
  17. package/dist/index.cjs +26 -0
  18. package/dist/index.d.cts +560 -0
  19. package/dist/index.d.cts.map +1 -0
  20. package/dist/index.d.ts +560 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +5 -0
  23. package/dist/index2.d.cts +5 -0
  24. package/dist/index2.d.ts +5 -0
  25. package/dist/index3.d.cts +882 -0
  26. package/dist/index3.d.cts.map +1 -0
  27. package/dist/index3.d.ts +882 -0
  28. package/dist/index3.d.ts.map +1 -0
  29. package/dist/main-thread.cjs +5459 -0
  30. package/dist/main-thread.cjs.map +1 -0
  31. package/dist/main-thread.js +5429 -0
  32. package/dist/main-thread.js.map +1 -0
  33. package/dist/react.cjs +116 -0
  34. package/dist/react.cjs.map +1 -0
  35. package/dist/react.d.cts +91 -0
  36. package/dist/react.d.cts.map +1 -0
  37. package/dist/react.d.ts +91 -0
  38. package/dist/react.d.ts.map +1 -0
  39. package/dist/react.js +113 -0
  40. package/dist/react.js.map +1 -0
  41. package/dist/resolve-debug.cjs +24 -0
  42. package/dist/resolve-debug.cjs.map +1 -0
  43. package/dist/resolve-debug.js +19 -0
  44. package/dist/resolve-debug.js.map +1 -0
  45. package/dist/server.cjs +250 -0
  46. package/dist/server.cjs.map +1 -0
  47. package/dist/server.d.cts +127 -0
  48. package/dist/server.d.cts.map +1 -0
  49. package/dist/server.d.ts +127 -0
  50. package/dist/server.d.ts.map +1 -0
  51. package/dist/server.js +245 -0
  52. package/dist/server.js.map +1 -0
  53. package/dist/svelte.cjs +48 -0
  54. package/dist/svelte.cjs.map +1 -0
  55. package/dist/svelte.d.cts +38 -0
  56. package/dist/svelte.d.cts.map +1 -0
  57. package/dist/svelte.d.ts +38 -0
  58. package/dist/svelte.d.ts.map +1 -0
  59. package/dist/svelte.js +47 -0
  60. package/dist/svelte.js.map +1 -0
  61. package/dist/sync-channel.cjs +532 -0
  62. package/dist/sync-channel.cjs.map +1 -0
  63. package/dist/sync-channel.js +425 -0
  64. package/dist/sync-channel.js.map +1 -0
  65. package/dist/transport.cjs +213 -0
  66. package/dist/transport.cjs.map +1 -0
  67. package/dist/transport.d.cts +79 -0
  68. package/dist/transport.d.cts.map +1 -0
  69. package/dist/transport.d.ts +79 -0
  70. package/dist/transport.d.ts.map +1 -0
  71. package/dist/transport.js +202 -0
  72. package/dist/transport.js.map +1 -0
  73. package/dist/vite-plugin.cjs +112 -0
  74. package/dist/vite-plugin.cjs.map +1 -0
  75. package/dist/vite-plugin.d.cts +39 -0
  76. package/dist/vite-plugin.d.cts.map +1 -0
  77. package/dist/vite-plugin.d.ts +39 -0
  78. package/dist/vite-plugin.d.ts.map +1 -0
  79. package/dist/vite-plugin.js +107 -0
  80. package/dist/vite-plugin.js.map +1 -0
  81. package/dist/vue.cjs +123 -0
  82. package/dist/vue.cjs.map +1 -0
  83. package/dist/vue.d.cts +126 -0
  84. package/dist/vue.d.cts.map +1 -0
  85. package/dist/vue.d.ts +126 -0
  86. package/dist/vue.d.ts.map +1 -0
  87. package/dist/vue.js +120 -0
  88. package/dist/vue.js.map +1 -0
  89. package/dist/worker-thread.cjs +2751 -0
  90. package/dist/worker-thread.cjs.map +1 -0
  91. package/dist/worker-thread.js +2692 -0
  92. package/dist/worker-thread.js.map +1 -0
  93. package/dist/worker-transport.cjs +136 -0
  94. package/dist/worker-transport.cjs.map +1 -0
  95. package/dist/worker-transport.d.cts +162 -0
  96. package/dist/worker-transport.d.cts.map +1 -0
  97. package/dist/worker-transport.d.ts +162 -0
  98. package/dist/worker-transport.d.ts.map +1 -0
  99. package/dist/worker-transport.js +125 -0
  100. package/dist/worker-transport.js.map +1 -0
  101. package/dist/worker.cjs +12 -0
  102. package/dist/worker.d.cts +2 -0
  103. package/dist/worker.d.ts +2 -0
  104. package/dist/worker.js +2 -0
  105. package/dist/ws-server-transport.cjs +147 -0
  106. package/dist/ws-server-transport.cjs.map +1 -0
  107. package/dist/ws-server-transport.d.cts +64 -0
  108. package/dist/ws-server-transport.d.cts.map +1 -0
  109. package/dist/ws-server-transport.d.ts +64 -0
  110. package/dist/ws-server-transport.d.ts.map +1 -0
  111. package/dist/ws-server-transport.js +142 -0
  112. package/dist/ws-server-transport.js.map +1 -0
  113. package/dist/ws-transport.cjs +954 -0
  114. package/dist/ws-transport.cjs.map +1 -0
  115. package/dist/ws-transport.js +913 -0
  116. package/dist/ws-transport.js.map +1 -0
  117. package/package.json +145 -0
@@ -0,0 +1,2692 @@
1
+ import { c as createAppId, d as isEventMessage, n as SyncChannel, p as isSystemMessage, t as QueryType, u as createNodeId, v as resolveDebugHooks } from "./sync-channel.js";
2
+ import { t as WorkerSelfTransport } from "./worker-transport.js";
3
+ //#region src/platform.ts
4
+ /**
5
+ * Create a PlatformHost for Web Worker environments (uses `self`).
6
+ */
7
+ function createWorkerPlatform() {
8
+ return {
9
+ navigator: {
10
+ userAgent: self.navigator.userAgent,
11
+ language: self.navigator.language,
12
+ languages: self.navigator.languages,
13
+ hardwareConcurrency: self.navigator.hardwareConcurrency
14
+ },
15
+ installErrorHandlers(onError, onUnhandledRejection) {
16
+ const workerScope = self;
17
+ const prevOnError = workerScope.onerror;
18
+ const prevOnRejection = workerScope.onunhandledrejection;
19
+ workerScope.onerror = (event, source, lineno, colno, error) => {
20
+ onError(typeof event === "string" ? event : event.message ?? "Unknown worker error", error, source ?? (typeof event !== "string" ? event.filename : void 0), lineno ?? (typeof event !== "string" ? event.lineno : void 0), colno ?? (typeof event !== "string" ? event.colno : void 0));
21
+ };
22
+ workerScope.onunhandledrejection = (event) => {
23
+ onUnhandledRejection(event.reason);
24
+ };
25
+ return () => {
26
+ workerScope.onerror = prevOnError;
27
+ workerScope.onunhandledrejection = prevOnRejection;
28
+ };
29
+ },
30
+ onBeforeUnload(callback) {
31
+ if (typeof self !== "undefined" && "addEventListener" in self) {
32
+ self.addEventListener("beforeunload", callback);
33
+ return () => {
34
+ self.removeEventListener("beforeunload", callback);
35
+ };
36
+ }
37
+ return () => {};
38
+ }
39
+ };
40
+ }
41
+ /**
42
+ * Create a PlatformHost for Node.js environments (uses `process`).
43
+ */
44
+ function createNodePlatform() {
45
+ const os = typeof globalThis !== "undefined" ? globalThis : {};
46
+ return {
47
+ navigator: {
48
+ userAgent: `Node.js/${typeof process !== "undefined" ? process.version : "unknown"}`,
49
+ language: "en-US",
50
+ languages: ["en-US"],
51
+ hardwareConcurrency: typeof os.navigator === "object" && os.navigator !== null && "hardwareConcurrency" in os.navigator ? os.navigator.hardwareConcurrency ?? 1 : 1
52
+ },
53
+ installErrorHandlers(onError, onUnhandledRejection) {
54
+ if (typeof process === "undefined") return () => {};
55
+ const proc = process;
56
+ const onUncaught = (err) => {
57
+ onError(err.message, err, void 0, void 0, void 0);
58
+ };
59
+ const onRejection = (reason) => {
60
+ onUnhandledRejection(reason);
61
+ };
62
+ proc.on("uncaughtException", onUncaught);
63
+ proc.on("unhandledRejection", onRejection);
64
+ return () => {
65
+ proc.removeListener("uncaughtException", onUncaught);
66
+ proc.removeListener("unhandledRejection", onRejection);
67
+ };
68
+ },
69
+ onBeforeUnload(callback) {
70
+ if (typeof process === "undefined") return () => {};
71
+ const proc = process;
72
+ const handler = () => {
73
+ callback();
74
+ };
75
+ proc.on("beforeExit", handler);
76
+ return () => {
77
+ proc.removeListener("beforeExit", handler);
78
+ };
79
+ }
80
+ };
81
+ }
82
+ /**
83
+ * Auto-detect the current platform and create the appropriate PlatformHost.
84
+ */
85
+ function detectPlatform() {
86
+ if (typeof self !== "undefined" && typeof self.navigator !== "undefined") return createWorkerPlatform();
87
+ return createNodePlatform();
88
+ }
89
+ //#endregion
90
+ //#region src/worker-thread/selector-engine.ts
91
+ function parseSimpleSelector(input) {
92
+ const sel = {};
93
+ let i = 0;
94
+ const len = input.length;
95
+ while (i < len) {
96
+ const ch = input[i];
97
+ if (ch === "#") {
98
+ i++;
99
+ let id = "";
100
+ while (i < len && input[i] !== "." && input[i] !== "#" && input[i] !== "[" && input[i] !== ":") id += input[i++];
101
+ sel.id = id;
102
+ } else if (ch === ".") {
103
+ i++;
104
+ let cls = "";
105
+ while (i < len && input[i] !== "." && input[i] !== "#" && input[i] !== "[" && input[i] !== ":") cls += input[i++];
106
+ if (!sel.classes) sel.classes = [];
107
+ sel.classes.push(cls);
108
+ } else if (ch === "[") {
109
+ i++;
110
+ let name = "";
111
+ while (i < len && input[i] !== "]" && input[i] !== "=") name += input[i++];
112
+ name = name.trim();
113
+ let value;
114
+ if (i < len && input[i] === "=") {
115
+ i++;
116
+ let v = "";
117
+ const quote = input[i] === "\"" || input[i] === "'" ? input[i++] : "";
118
+ while (i < len && input[i] !== "]" && (quote ? input[i] !== quote : true)) v += input[i++];
119
+ if (quote && i < len) i++;
120
+ v = v.trim();
121
+ value = v;
122
+ }
123
+ if (i < len && input[i] === "]") i++;
124
+ if (!sel.attrs) sel.attrs = [];
125
+ sel.attrs.push({
126
+ name,
127
+ value
128
+ });
129
+ } else if (ch === ":") {
130
+ i++;
131
+ let pseudo = "";
132
+ while (i < len && input[i] !== "." && input[i] !== "#" && input[i] !== "[" && input[i] !== ":") pseudo += input[i++];
133
+ if (!sel.pseudos) sel.pseudos = [];
134
+ sel.pseudos.push(pseudo);
135
+ } else {
136
+ let tag = "";
137
+ while (i < len && input[i] !== "." && input[i] !== "#" && input[i] !== "[" && input[i] !== ":" && input[i] !== " " && input[i] !== ">") tag += input[i++];
138
+ if (tag) sel.tag = tag.toUpperCase();
139
+ }
140
+ }
141
+ return sel;
142
+ }
143
+ function parseSelectorGroup(input) {
144
+ const groups = [];
145
+ let current = "";
146
+ let inBracket = false;
147
+ let inQuote = "";
148
+ for (let i = 0; i < input.length; i++) {
149
+ const ch = input[i];
150
+ if (inQuote) {
151
+ current += ch;
152
+ if (ch === inQuote) inQuote = "";
153
+ } else if (ch === "\"" || ch === "'") {
154
+ current += ch;
155
+ inQuote = ch;
156
+ } else if (ch === "[") {
157
+ inBracket = true;
158
+ current += ch;
159
+ } else if (ch === "]") {
160
+ inBracket = false;
161
+ current += ch;
162
+ } else if (ch === "," && !inBracket) {
163
+ groups.push(current.trim());
164
+ current = "";
165
+ } else current += ch;
166
+ }
167
+ if (current.trim()) groups.push(current.trim());
168
+ return groups.map((group) => parseSelectorChain(group));
169
+ }
170
+ function parseSelectorChain(input) {
171
+ const parts = [];
172
+ const tokens = tokenize(input);
173
+ let combinator = "";
174
+ for (const token of tokens) if (token === ">") combinator = ">";
175
+ else if (token === " ") {
176
+ if (combinator !== ">") combinator = " ";
177
+ } else {
178
+ parts.push({
179
+ selector: parseSimpleSelector(token),
180
+ combinator
181
+ });
182
+ combinator = "";
183
+ }
184
+ return parts;
185
+ }
186
+ function tokenize(input) {
187
+ const tokens = [];
188
+ let current = "";
189
+ let inBracket = false;
190
+ for (let i = 0; i < input.length; i++) {
191
+ const ch = input[i];
192
+ if (ch === "[") inBracket = true;
193
+ if (ch === "]") inBracket = false;
194
+ if (!inBracket && (ch === " " || ch === ">")) {
195
+ if (current) {
196
+ tokens.push(current);
197
+ current = "";
198
+ }
199
+ if (ch === ">") tokens.push(">");
200
+ else if (tokens.length > 0 && tokens[tokens.length - 1] !== ">" && tokens[tokens.length - 1] !== " ") tokens.push(" ");
201
+ } else current += ch;
202
+ }
203
+ if (current) tokens.push(current);
204
+ return tokens;
205
+ }
206
+ function matchesSimple(el, sel) {
207
+ if (sel.tag && sel.tag !== "*" && el.tagName !== sel.tag) return false;
208
+ if (sel.id && el.getAttribute("id") !== sel.id) return false;
209
+ if (sel.classes) {
210
+ const elClasses = el.className.split(" ").filter(Boolean);
211
+ for (const cls of sel.classes) if (!elClasses.includes(cls)) return false;
212
+ }
213
+ if (sel.attrs) {
214
+ for (const attr of sel.attrs) if (attr.value !== void 0) {
215
+ if (el.getAttribute(attr.name) !== attr.value) return false;
216
+ } else if (!el.hasAttribute(attr.name)) return false;
217
+ }
218
+ if (sel.pseudos) {
219
+ for (const pseudo of sel.pseudos) if (pseudo === "first-child") {
220
+ if (!el.parentNode) return false;
221
+ if (el.parentNode.childNodes.filter((c) => c.nodeType === 1)[0] !== el) return false;
222
+ } else if (pseudo === "last-child") {
223
+ if (!el.parentNode) return false;
224
+ const siblings = el.parentNode.childNodes.filter((c) => c.nodeType === 1);
225
+ if (siblings[siblings.length - 1] !== el) return false;
226
+ }
227
+ }
228
+ return true;
229
+ }
230
+ function matchesChain(el, chain) {
231
+ if (chain.length === 0) return false;
232
+ let current = el;
233
+ for (let i = chain.length - 1; i >= 0; i--) {
234
+ const part = chain[i];
235
+ if (!current) return false;
236
+ if (i === chain.length - 1) {
237
+ if (!matchesSimple(current, part.selector)) return false;
238
+ } else if (chain[i + 1].combinator === ">") {
239
+ current = current.parentNode;
240
+ if (!current || !matchesSimple(current, part.selector)) return false;
241
+ } else {
242
+ current = current.parentNode;
243
+ while (current) {
244
+ if (matchesSimple(current, part.selector)) break;
245
+ current = current.parentNode;
246
+ }
247
+ if (!current) return false;
248
+ }
249
+ }
250
+ return true;
251
+ }
252
+ function matches(el, selector) {
253
+ return parseSelectorGroup(selector).some((chain) => matchesChain(el, chain));
254
+ }
255
+ function querySelectorAll(root, selector) {
256
+ const groups = parseSelectorGroup(selector);
257
+ const results = [];
258
+ walkElements(root, (el) => {
259
+ if (groups.some((chain) => matchesChain(el, chain))) results.push(el);
260
+ });
261
+ return results;
262
+ }
263
+ function querySelector(root, selector) {
264
+ const groups = parseSelectorGroup(selector);
265
+ let found = null;
266
+ walkElements(root, (el) => {
267
+ if (groups.some((chain) => matchesChain(el, chain))) {
268
+ found = el;
269
+ return true;
270
+ }
271
+ });
272
+ return found;
273
+ }
274
+ function walkElements(root, callback) {
275
+ for (const child of root.childNodes) if (child.nodeType === 1) {
276
+ const el = child;
277
+ if (callback(el) === true) return true;
278
+ if (walkElements(el, callback)) return true;
279
+ }
280
+ return false;
281
+ }
282
+ //#endregion
283
+ //#region src/worker-thread/style-proxy.ts
284
+ const KEBAB_REGEX = /[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g;
285
+ const kebabCache = /* @__PURE__ */ new Map();
286
+ function toKebabCase(str) {
287
+ let cached = kebabCache.get(str);
288
+ if (cached === void 0) {
289
+ cached = str.replace(KEBAB_REGEX, (match) => `-${match.toLowerCase()}`);
290
+ kebabCache.set(str, cached);
291
+ }
292
+ return cached;
293
+ }
294
+ /**
295
+ * Creates a Proxy-based style object that intercepts property sets
296
+ * and emits setStyle mutations.
297
+ */
298
+ function createStyleProxy(owner, collector, initialStyles = {}) {
299
+ const backing = { ...initialStyles };
300
+ return new Proxy(backing, {
301
+ get(target, prop) {
302
+ if (typeof prop !== "string") return "";
303
+ if (prop === "getPropertyValue") return (name) => target[toKebabCase(name)] ?? "";
304
+ if (prop === "removeProperty") return (name) => {
305
+ const key = toKebabCase(name);
306
+ const old = target[key] ?? "";
307
+ delete target[key];
308
+ const mutation = {
309
+ action: "setStyle",
310
+ id: owner._nodeId,
311
+ property: key,
312
+ value: ""
313
+ };
314
+ collector.add(mutation);
315
+ return old;
316
+ };
317
+ if (prop === "setProperty") return (name, value, _priority) => {
318
+ const key = toKebabCase(name);
319
+ target[key] = value;
320
+ const mutation = {
321
+ action: "setStyle",
322
+ id: owner._nodeId,
323
+ property: key,
324
+ value: String(value)
325
+ };
326
+ collector.add(mutation);
327
+ };
328
+ if (prop === "cssText") return Object.entries(target).map(([k, v]) => `${k}: ${v}`).join("; ");
329
+ return target[toKebabCase(prop)] ?? "";
330
+ },
331
+ set(target, prop, value) {
332
+ if (typeof prop !== "string") return true;
333
+ const key = toKebabCase(prop);
334
+ if (key === "css-text") {
335
+ parseStyleString(value).forEach(([k, v]) => {
336
+ target[k] = v;
337
+ const mutation = {
338
+ action: "setStyle",
339
+ id: owner._nodeId,
340
+ property: k,
341
+ value: v
342
+ };
343
+ collector.add(mutation);
344
+ });
345
+ return true;
346
+ }
347
+ target[key] = value;
348
+ const mutation = {
349
+ action: "setStyle",
350
+ id: owner._nodeId,
351
+ property: key,
352
+ value: String(value)
353
+ };
354
+ collector.add(mutation);
355
+ return true;
356
+ }
357
+ });
358
+ }
359
+ function parseStyleString(value) {
360
+ const result = [];
361
+ for (const part of value.split(";")) {
362
+ const colonIdx = part.indexOf(":");
363
+ if (colonIdx === -1) continue;
364
+ const key = part.slice(0, colonIdx).trim();
365
+ const val = part.slice(colonIdx + 1).trim();
366
+ if (key && val !== void 0) result.push([key, val]);
367
+ }
368
+ return result;
369
+ }
370
+ //#endregion
371
+ //#region src/worker-thread/element.ts
372
+ function kebabToCamel(str) {
373
+ return str.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
374
+ }
375
+ function escapeHtml(s) {
376
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
377
+ }
378
+ function escapeAttr(s) {
379
+ return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
380
+ }
381
+ let listenerCounter = 0;
382
+ const VOID_ELEMENTS = new Set([
383
+ "area",
384
+ "base",
385
+ "br",
386
+ "col",
387
+ "embed",
388
+ "hr",
389
+ "img",
390
+ "input",
391
+ "link",
392
+ "meta",
393
+ "param",
394
+ "source",
395
+ "track",
396
+ "wbr"
397
+ ]);
398
+ /**
399
+ * Virtual DOM element that records mutations via the MutationCollector
400
+ * instead of touching real DOM.
401
+ *
402
+ * Implements a broad subset of the HTMLElement API:
403
+ * - Attributes: setAttribute, getAttribute, removeAttribute, dataset
404
+ * - Children: appendChild, removeChild, insertBefore, replaceChild, etc.
405
+ * - Text/HTML: textContent, innerHTML, outerHTML, insertAdjacentHTML
406
+ * - Input state: value, checked, disabled, selectedIndex (with main-thread sync)
407
+ * - Media state: currentTime, duration, paused, ended, readyState
408
+ * - Events: addEventListener, removeEventListener, on* handlers, dispatchEvent
409
+ * - Layout queries: getBoundingClientRect, clientWidth/Height, etc. (via SyncChannel)
410
+ * - CSS: style proxy, classList, className
411
+ * - Traversal: querySelector, closest, contains, parentNode, children, etc.
412
+ *
413
+ * Every mutating operation emits a DomMutation to the collector. Layout queries
414
+ * use the SyncChannel to synchronously read values from the main thread's DOM.
415
+ */
416
+ var VirtualElement = class VirtualElement {
417
+ static ELEMENT_NODE = 1;
418
+ static TEXT_NODE = 3;
419
+ static COMMENT_NODE = 8;
420
+ static DOCUMENT_NODE = 9;
421
+ static DOCUMENT_FRAGMENT_NODE = 11;
422
+ _nodeId;
423
+ nodeName;
424
+ tagName;
425
+ nodeType = 1;
426
+ namespaceURI;
427
+ parentNode = null;
428
+ _ownerDocument = null;
429
+ childNodes = [];
430
+ _attributes = /* @__PURE__ */ new Map();
431
+ _classes = [];
432
+ _textContent = "";
433
+ _value = "";
434
+ _checked = false;
435
+ _disabled = false;
436
+ _selectedIndex = -1;
437
+ _datasetProxy = null;
438
+ style;
439
+ classList;
440
+ get id() {
441
+ return this.getAttribute("id") ?? "";
442
+ }
443
+ set id(value) {
444
+ this.setAttribute("id", value);
445
+ }
446
+ get children() {
447
+ return this.childNodes.filter((c) => c.nodeType === 1);
448
+ }
449
+ get childElementCount() {
450
+ return this.childNodes.filter((c) => c.nodeType === 1).length;
451
+ }
452
+ get firstElementChild() {
453
+ return this.childNodes.find((c) => c.nodeType === 1) ?? null;
454
+ }
455
+ get lastElementChild() {
456
+ for (let i = this.childNodes.length - 1; i >= 0; i--) if (this.childNodes[i].nodeType === 1) return this.childNodes[i];
457
+ return null;
458
+ }
459
+ get clientWidth() {
460
+ return this._readNodeProperty("clientWidth") ?? 0;
461
+ }
462
+ get clientHeight() {
463
+ return this._readNodeProperty("clientHeight") ?? 0;
464
+ }
465
+ get scrollWidth() {
466
+ return this._readNodeProperty("scrollWidth") ?? 0;
467
+ }
468
+ get scrollHeight() {
469
+ return this._readNodeProperty("scrollHeight") ?? 0;
470
+ }
471
+ get offsetWidth() {
472
+ return this._readNodeProperty("offsetWidth") ?? 0;
473
+ }
474
+ get offsetHeight() {
475
+ return this._readNodeProperty("offsetHeight") ?? 0;
476
+ }
477
+ get offsetTop() {
478
+ return this._readNodeProperty("offsetTop") ?? 0;
479
+ }
480
+ get offsetLeft() {
481
+ return this._readNodeProperty("offsetLeft") ?? 0;
482
+ }
483
+ get scrollTop() {
484
+ return this._readNodeProperty("scrollTop") ?? 0;
485
+ }
486
+ set scrollTop(v) {
487
+ const mutation = {
488
+ action: "setProperty",
489
+ id: this._nodeId,
490
+ property: "scrollTop",
491
+ value: v
492
+ };
493
+ this.collector.add(mutation);
494
+ }
495
+ get scrollLeft() {
496
+ return this._readNodeProperty("scrollLeft") ?? 0;
497
+ }
498
+ set scrollLeft(v) {
499
+ const mutation = {
500
+ action: "setProperty",
501
+ id: this._nodeId,
502
+ property: "scrollLeft",
503
+ value: v
504
+ };
505
+ this.collector.add(mutation);
506
+ }
507
+ /**
508
+ * Synchronously read a numeric DOM property from the main thread via SyncChannel.
509
+ * Returns null if no sync channel is available (e.g., no SharedArrayBuffer support).
510
+ */
511
+ _readNodeProperty(prop) {
512
+ const channel = this._ownerDocument?._syncChannel;
513
+ if (channel) {
514
+ const result = channel.request(QueryType.NodeProperty, JSON.stringify({
515
+ nodeId: this._nodeId,
516
+ property: prop
517
+ }));
518
+ if (typeof result === "number") return result;
519
+ }
520
+ return null;
521
+ }
522
+ constructor(tag, collector, id) {
523
+ this.collector = collector;
524
+ this.nodeName = tag.toUpperCase();
525
+ this.tagName = this.nodeName;
526
+ this._nodeId = id ?? createNodeId();
527
+ this.namespaceURI = "http://www.w3.org/1999/xhtml";
528
+ this.style = createStyleProxy(this, collector);
529
+ this.classList = new VirtualClassList(this);
530
+ }
531
+ _setNamespaceURI(ns) {
532
+ this.namespaceURI = ns;
533
+ }
534
+ setAttribute(name, value) {
535
+ if (name === "id") {
536
+ const oldId = this._attributes.get("id");
537
+ this._attributes.set(name, value);
538
+ if (this._ownerDocument) {
539
+ if (oldId) this._ownerDocument.unregisterElementById(oldId);
540
+ this._ownerDocument.registerElementById(value, this);
541
+ }
542
+ const mutation = {
543
+ action: "setAttribute",
544
+ id: this._nodeId,
545
+ name: "id",
546
+ value
547
+ };
548
+ this.collector.add(mutation);
549
+ return;
550
+ }
551
+ if (name === "class") {
552
+ this._classes = value ? value.split(/\s+/).filter(Boolean) : [];
553
+ this._attributes.set("class", value);
554
+ const mutation = {
555
+ action: "setClassName",
556
+ id: this._nodeId,
557
+ name: value
558
+ };
559
+ this.collector.add(mutation);
560
+ return;
561
+ }
562
+ if (name === "style") {
563
+ this._parseAndSetStyles(value);
564
+ const mutation = {
565
+ action: "setAttribute",
566
+ id: this._nodeId,
567
+ name: "style",
568
+ value,
569
+ optional: true
570
+ };
571
+ this.collector.add(mutation);
572
+ return;
573
+ }
574
+ this._attributes.set(name, value);
575
+ const mutation = {
576
+ action: "setAttribute",
577
+ id: this._nodeId,
578
+ name,
579
+ value
580
+ };
581
+ this.collector.add(mutation);
582
+ }
583
+ getAttribute(name) {
584
+ return this._attributes.get(name) ?? null;
585
+ }
586
+ hasAttribute(name) {
587
+ return this._attributes.has(name);
588
+ }
589
+ removeAttribute(name) {
590
+ if (name === "class") this._classes = [];
591
+ this._attributes.delete(name);
592
+ const mutation = {
593
+ action: "removeAttribute",
594
+ id: this._nodeId,
595
+ name
596
+ };
597
+ this.collector.add(mutation);
598
+ }
599
+ getAttributeNS(_ns, name) {
600
+ return this.getAttribute(name);
601
+ }
602
+ setAttributeNS(_ns, name, value) {
603
+ this.setAttribute(name, value);
604
+ }
605
+ removeAttributeNS(_ns, name) {
606
+ this.removeAttribute(name);
607
+ }
608
+ get attributes() {
609
+ const entries = [...this._attributes.entries()];
610
+ return {
611
+ length: entries.length,
612
+ item(index) {
613
+ const entry = entries[index];
614
+ return entry ? {
615
+ name: entry[0],
616
+ value: entry[1]
617
+ } : null;
618
+ }
619
+ };
620
+ }
621
+ appendChild(child) {
622
+ if (child instanceof VirtualElement && child.nodeName === "#DOCUMENT-FRAGMENT") {
623
+ const fragmentChildren = [...child.childNodes];
624
+ for (const fc of fragmentChildren) this._appendSingleChild(fc);
625
+ child.childNodes.length = 0;
626
+ return child;
627
+ }
628
+ this._appendSingleChild(child);
629
+ return child;
630
+ }
631
+ _appendSingleChild(child) {
632
+ if (child.parentNode) child.parentNode.childNodes = child.parentNode.childNodes.filter((c) => c !== child);
633
+ child.parentNode = this;
634
+ this.childNodes.push(child);
635
+ const mutation = {
636
+ action: "appendChild",
637
+ id: this._nodeId,
638
+ childId: child._nodeId
639
+ };
640
+ this.collector.add(mutation);
641
+ }
642
+ removeChild(child) {
643
+ if (child instanceof VirtualElement) child._cleanupFromDocument();
644
+ this.childNodes = this.childNodes.filter((c) => c !== child);
645
+ child.parentNode = null;
646
+ const mutation = {
647
+ action: "removeChild",
648
+ id: this._nodeId,
649
+ childId: child._nodeId
650
+ };
651
+ this.collector.add(mutation);
652
+ return child;
653
+ }
654
+ insertBefore(newChild, refChild) {
655
+ if (newChild instanceof VirtualElement && newChild.nodeName === "#DOCUMENT-FRAGMENT") {
656
+ const fragmentChildren = [...newChild.childNodes];
657
+ for (const fc of fragmentChildren) this.insertBefore(fc, refChild);
658
+ newChild.childNodes.length = 0;
659
+ return newChild;
660
+ }
661
+ if (newChild.parentNode) newChild.parentNode.childNodes = newChild.parentNode.childNodes.filter((c) => c !== newChild);
662
+ newChild.parentNode = this;
663
+ if (refChild === null) this.childNodes.push(newChild);
664
+ else {
665
+ const index = this.childNodes.indexOf(refChild);
666
+ if (index === -1) this.childNodes.push(newChild);
667
+ else this.childNodes.splice(index, 0, newChild);
668
+ }
669
+ const mutation = {
670
+ action: "insertBefore",
671
+ id: this._nodeId,
672
+ newId: newChild._nodeId,
673
+ refId: refChild?._nodeId ?? null
674
+ };
675
+ this.collector.add(mutation);
676
+ return newChild;
677
+ }
678
+ remove() {
679
+ this._cleanupFromDocument();
680
+ if (this.parentNode) this.parentNode.childNodes = this.parentNode.childNodes.filter((c) => c !== this);
681
+ this.parentNode = null;
682
+ const mutation = {
683
+ action: "removeNode",
684
+ id: this._nodeId
685
+ };
686
+ this.collector.add(mutation);
687
+ }
688
+ append(...nodes) {
689
+ for (const node of nodes) this.appendChild(node);
690
+ }
691
+ prepend(...nodes) {
692
+ const first = this.firstChild;
693
+ for (const node of nodes) this.insertBefore(node, first);
694
+ }
695
+ replaceWith(...nodes) {
696
+ const parent = this.parentNode;
697
+ if (!parent) return;
698
+ const nextSib = this.nextSibling;
699
+ this.remove();
700
+ for (const node of nodes) parent.insertBefore(node, nextSib);
701
+ }
702
+ before(...nodes) {
703
+ const parent = this.parentNode;
704
+ if (!parent) return;
705
+ for (const node of nodes) parent.insertBefore(node, this);
706
+ }
707
+ after(...nodes) {
708
+ const parent = this.parentNode;
709
+ if (!parent) return;
710
+ const nextSib = this.nextSibling;
711
+ for (const node of nodes) parent.insertBefore(node, nextSib);
712
+ }
713
+ replaceChildren(...nodes) {
714
+ while (this.childNodes.length > 0) this.removeChild(this.childNodes[0]);
715
+ for (const node of nodes) this.appendChild(node);
716
+ }
717
+ get textContent() {
718
+ if (this.childNodes.length === 0) return this._textContent;
719
+ let result = "";
720
+ for (const child of this.childNodes) if (child.nodeType === 3) result += child.nodeValue;
721
+ else if (child.nodeType === 1) result += child.textContent;
722
+ return result;
723
+ }
724
+ set textContent(value) {
725
+ for (const child of this.childNodes) {
726
+ if (child instanceof VirtualElement) child._cleanupFromDocument();
727
+ else if (this._ownerDocument) this._ownerDocument.unregisterElement(child._nodeId);
728
+ child.parentNode = null;
729
+ }
730
+ this.childNodes.length = 0;
731
+ this._textContent = value;
732
+ const mutation = {
733
+ action: "setTextContent",
734
+ id: this._nodeId,
735
+ textContent: value
736
+ };
737
+ this.collector.add(mutation);
738
+ }
739
+ get innerHTML() {
740
+ if (this.childNodes.length === 0) return this._textContent ? escapeHtml(this._textContent) : "";
741
+ return this.childNodes.map((child) => {
742
+ if (child.nodeType === 3) return escapeHtml(child.nodeValue);
743
+ if (child.nodeType === 8) return `<!--${child.nodeValue.replace(/--/g, "")}-->`;
744
+ if (child instanceof VirtualElement) return child.outerHTML;
745
+ return "";
746
+ }).join("");
747
+ }
748
+ set innerHTML(value) {
749
+ this._textContent = "";
750
+ for (const child of this.childNodes) {
751
+ if (child instanceof VirtualElement) child._cleanupFromDocument();
752
+ else if (this._ownerDocument) this._ownerDocument.unregisterElement(child._nodeId);
753
+ child.parentNode = null;
754
+ }
755
+ this.childNodes.length = 0;
756
+ const mutation = {
757
+ action: "setHTML",
758
+ id: this._nodeId,
759
+ html: value
760
+ };
761
+ this.collector.add(mutation);
762
+ }
763
+ get outerHTML() {
764
+ const tag = this.tagName.toLowerCase();
765
+ let attrs = "";
766
+ for (const [key, value] of this._attributes) attrs += ` ${key}="${escapeAttr(value)}"`;
767
+ if (this._classes.length > 0 && !this._attributes.has("class")) attrs += ` class="${escapeAttr(this._classes.join(" "))}"`;
768
+ const cssText = this.style.cssText;
769
+ if (cssText) attrs += ` style="${escapeAttr(cssText)}"`;
770
+ const inner = this.innerHTML;
771
+ if (VOID_ELEMENTS.has(tag)) return `<${tag}${attrs}>`;
772
+ return `<${tag}${attrs}>${inner}</${tag}>`;
773
+ }
774
+ get value() {
775
+ return this._value;
776
+ }
777
+ set value(v) {
778
+ this._value = v;
779
+ const mutation = {
780
+ action: "setProperty",
781
+ id: this._nodeId,
782
+ property: "value",
783
+ value: v
784
+ };
785
+ this.collector.add(mutation);
786
+ }
787
+ get checked() {
788
+ return this._checked;
789
+ }
790
+ set checked(v) {
791
+ this._checked = v;
792
+ const mutation = {
793
+ action: "setProperty",
794
+ id: this._nodeId,
795
+ property: "checked",
796
+ value: v
797
+ };
798
+ this.collector.add(mutation);
799
+ }
800
+ get disabled() {
801
+ return this._disabled;
802
+ }
803
+ set disabled(v) {
804
+ this._disabled = v;
805
+ const mutation = {
806
+ action: "setProperty",
807
+ id: this._nodeId,
808
+ property: "disabled",
809
+ value: v
810
+ };
811
+ this.collector.add(mutation);
812
+ }
813
+ get selectedIndex() {
814
+ return this._selectedIndex;
815
+ }
816
+ set selectedIndex(v) {
817
+ this._selectedIndex = v;
818
+ const mutation = {
819
+ action: "setProperty",
820
+ id: this._nodeId,
821
+ property: "selectedIndex",
822
+ value: v
823
+ };
824
+ this.collector.add(mutation);
825
+ }
826
+ /**
827
+ * Sync input state from a main-thread event without emitting mutations.
828
+ * Called by VirtualDocument.dispatchEvent to keep the worker's model in sync
829
+ * with real DOM input values after user interaction.
830
+ */
831
+ _updateInputState(state) {
832
+ if (state.value !== void 0) this._value = state.value;
833
+ if (state.checked !== void 0) this._checked = state.checked;
834
+ if (state.selectedIndex !== void 0) this._selectedIndex = state.selectedIndex;
835
+ }
836
+ _currentTime = 0;
837
+ _duration = 0;
838
+ _paused = true;
839
+ _ended = false;
840
+ _readyState = 0;
841
+ get currentTime() {
842
+ return this._currentTime;
843
+ }
844
+ set currentTime(v) {
845
+ this._currentTime = v;
846
+ this.collector.add({
847
+ action: "setProperty",
848
+ id: this._nodeId,
849
+ property: "currentTime",
850
+ value: v
851
+ });
852
+ }
853
+ get duration() {
854
+ return this._duration;
855
+ }
856
+ get paused() {
857
+ return this._paused;
858
+ }
859
+ get ended() {
860
+ return this._ended;
861
+ }
862
+ get readyState() {
863
+ return this._readyState;
864
+ }
865
+ /** Sync media element state from a main-thread event without emitting mutations. */
866
+ _updateMediaState(state) {
867
+ if (state.currentTime !== void 0) this._currentTime = state.currentTime;
868
+ if (state.duration !== void 0) this._duration = state.duration;
869
+ if (state.paused !== void 0) this._paused = state.paused;
870
+ if (state.ended !== void 0) this._ended = state.ended;
871
+ if (state.readyState !== void 0) this._readyState = state.readyState;
872
+ }
873
+ get className() {
874
+ return this._classes.join(" ");
875
+ }
876
+ set className(value) {
877
+ this._classes = value ? value.split(/\s+/).filter(Boolean) : [];
878
+ if (this._classes.length > 0) this._attributes.set("class", this._classes.join(" "));
879
+ else this._attributes.delete("class");
880
+ const mutation = {
881
+ action: "setClassName",
882
+ id: this._nodeId,
883
+ name: value
884
+ };
885
+ this.collector.add(mutation);
886
+ }
887
+ /** Map of listenerId -> callback for event dispatch. */
888
+ _eventListeners = /* @__PURE__ */ new Map();
889
+ /** Map of listenerId -> event name, used to match removeEventListener by name+callback. */
890
+ _listenerEventNames = /* @__PURE__ */ new Map();
891
+ /** Map of event name -> callback for on* property handlers (e.g., onclick). */
892
+ _onHandlers = /* @__PURE__ */ new Map();
893
+ /**
894
+ * Register an event listener. Emits an addEventListener mutation so the main thread
895
+ * attaches a real DOM listener that will serialize and forward events back.
896
+ * Supports the `once` option by wrapping the callback with auto-removal.
897
+ */
898
+ addEventListener(name, callback, options) {
899
+ if (!name) return;
900
+ const listenerId = `${this._nodeId}_${name}_${++listenerCounter}`;
901
+ const once = typeof options === "object" ? options?.once ?? false : false;
902
+ let effectiveCallback = callback;
903
+ if (once) {
904
+ const originalCb = callback;
905
+ effectiveCallback = (e) => {
906
+ originalCb(e);
907
+ this.removeEventListener(name, effectiveCallback);
908
+ };
909
+ }
910
+ this._eventListeners.set(listenerId, effectiveCallback);
911
+ this._listenerEventNames.set(listenerId, name);
912
+ this._ownerDocument?.registerListener(listenerId, this);
913
+ const mutation = {
914
+ action: "addEventListener",
915
+ id: this._nodeId,
916
+ name,
917
+ listenerId
918
+ };
919
+ this.collector.add(mutation);
920
+ }
921
+ getEventListener(listenerId) {
922
+ return this._eventListeners.get(listenerId);
923
+ }
924
+ /** Remove a listener by event name and callback reference. Emits a removeEventListener mutation. */
925
+ removeEventListener(_name, callback) {
926
+ for (const [listenerId, cb] of this._eventListeners.entries()) if (cb === callback && this._listenerEventNames.get(listenerId) === _name) {
927
+ this._eventListeners.delete(listenerId);
928
+ this._listenerEventNames.delete(listenerId);
929
+ this._ownerDocument?.unregisterListener(listenerId);
930
+ const mutation = {
931
+ action: "removeEventListener",
932
+ id: this._nodeId,
933
+ listenerId
934
+ };
935
+ this.collector.add(mutation);
936
+ break;
937
+ }
938
+ }
939
+ /** Invoke all listeners matching the event type. Called during bubbling by VirtualDocument.dispatchEvent. */
940
+ _dispatchBubbledEvent(event) {
941
+ for (const [listenerId, cb] of this._eventListeners.entries()) if (this._listenerEventNames.get(listenerId) === event.type) {
942
+ cb(event);
943
+ if (event.immediatePropagationStopped) break;
944
+ }
945
+ }
946
+ /**
947
+ * Recursively clean up this element and all children from the document's registries.
948
+ * Called before emitting removal mutations to prevent memory leaks.
949
+ */
950
+ _cleanupFromDocument() {
951
+ for (const listenerId of this._eventListeners.keys()) this._ownerDocument?.unregisterListener(listenerId);
952
+ this._eventListeners.clear();
953
+ this._listenerEventNames.clear();
954
+ this._onHandlers.clear();
955
+ if (this._ownerDocument) {
956
+ const elId = this._attributes.get("id");
957
+ if (elId) this._ownerDocument.unregisterElementById(elId);
958
+ this._ownerDocument.unregisterElement(this._nodeId);
959
+ }
960
+ for (const child of this.childNodes) if (child instanceof VirtualElement) child._cleanupFromDocument();
961
+ else if (this._ownerDocument) this._ownerDocument.unregisterElement(child._nodeId);
962
+ }
963
+ /** Emit a configureEvent mutation to tell the main thread to call preventDefault for this event. */
964
+ preventDefaultFor(eventName) {
965
+ const mutation = {
966
+ action: "configureEvent",
967
+ id: this._nodeId,
968
+ name: eventName,
969
+ preventDefault: true
970
+ };
971
+ this.collector.add(mutation);
972
+ }
973
+ /** Implement on* handler properties (onclick, etc.) by managing a single listener per event name. */
974
+ _setOnHandler(eventName, cb) {
975
+ const prev = this._onHandlers.get(eventName);
976
+ if (prev) this.removeEventListener(eventName, prev);
977
+ if (cb) {
978
+ this.addEventListener(eventName, cb);
979
+ this._onHandlers.set(eventName, cb);
980
+ } else this._onHandlers.delete(eventName);
981
+ }
982
+ set onclick(cb) {
983
+ this._setOnHandler("click", cb);
984
+ }
985
+ set ondblclick(cb) {
986
+ this._setOnHandler("dblclick", cb);
987
+ }
988
+ set onmouseenter(cb) {
989
+ this._setOnHandler("mouseenter", cb);
990
+ }
991
+ set onmouseleave(cb) {
992
+ this._setOnHandler("mouseleave", cb);
993
+ }
994
+ set onmousedown(cb) {
995
+ this._setOnHandler("mousedown", cb);
996
+ }
997
+ set onmouseup(cb) {
998
+ this._setOnHandler("mouseup", cb);
999
+ }
1000
+ set onmouseover(cb) {
1001
+ this._setOnHandler("mouseover", cb);
1002
+ }
1003
+ set onmousemove(cb) {
1004
+ this._setOnHandler("mousemove", cb);
1005
+ }
1006
+ set onkeydown(cb) {
1007
+ this._setOnHandler("keydown", cb);
1008
+ }
1009
+ set onkeyup(cb) {
1010
+ this._setOnHandler("keyup", cb);
1011
+ }
1012
+ set onkeypress(cb) {
1013
+ this._setOnHandler("keypress", cb);
1014
+ }
1015
+ set onchange(cb) {
1016
+ this._setOnHandler("change", cb);
1017
+ }
1018
+ set oncontextmenu(cb) {
1019
+ this._setOnHandler("contextmenu", cb);
1020
+ }
1021
+ set oninput(cb) {
1022
+ this._setOnHandler("input", cb);
1023
+ }
1024
+ set onfocus(cb) {
1025
+ this._setOnHandler("focus", cb);
1026
+ }
1027
+ set onblur(cb) {
1028
+ this._setOnHandler("blur", cb);
1029
+ }
1030
+ set onsubmit(cb) {
1031
+ this._setOnHandler("submit", cb);
1032
+ }
1033
+ get firstChild() {
1034
+ return this.childNodes[0] ?? null;
1035
+ }
1036
+ get lastChild() {
1037
+ return this.childNodes[this.childNodes.length - 1] ?? null;
1038
+ }
1039
+ get nextSibling() {
1040
+ if (!this.parentNode) return null;
1041
+ const idx = this.parentNode.childNodes.indexOf(this);
1042
+ return this.parentNode.childNodes[idx + 1] ?? null;
1043
+ }
1044
+ get previousSibling() {
1045
+ if (!this.parentNode) return null;
1046
+ const idx = this.parentNode.childNodes.indexOf(this);
1047
+ return this.parentNode.childNodes[idx - 1] ?? null;
1048
+ }
1049
+ get parentElement() {
1050
+ return this.parentNode;
1051
+ }
1052
+ get ownerDocument() {
1053
+ return this._ownerDocument;
1054
+ }
1055
+ get isConnected() {
1056
+ let current = this;
1057
+ while (current) {
1058
+ if (current._ownerDocument && current === current._ownerDocument.documentElement) return true;
1059
+ current = current.parentNode;
1060
+ }
1061
+ return false;
1062
+ }
1063
+ getRootNode() {
1064
+ let current = this;
1065
+ while (current.parentNode) current = current.parentNode;
1066
+ return current;
1067
+ }
1068
+ get nextElementSibling() {
1069
+ if (!this.parentNode) return null;
1070
+ const siblings = this.parentNode.childNodes;
1071
+ const idx = siblings.indexOf(this);
1072
+ for (let i = idx + 1; i < siblings.length; i++) if (siblings[i].nodeType === 1) return siblings[i];
1073
+ return null;
1074
+ }
1075
+ get previousElementSibling() {
1076
+ if (!this.parentNode) return null;
1077
+ const siblings = this.parentNode.childNodes;
1078
+ const idx = siblings.indexOf(this);
1079
+ for (let i = idx - 1; i >= 0; i--) if (siblings[i].nodeType === 1) return siblings[i];
1080
+ return null;
1081
+ }
1082
+ hasChildNodes() {
1083
+ return this.childNodes.length > 0;
1084
+ }
1085
+ replaceChild(newChild, oldChild) {
1086
+ if (this.childNodes.indexOf(oldChild) === -1) return oldChild;
1087
+ this.insertBefore(newChild, oldChild);
1088
+ this.removeChild(oldChild);
1089
+ return oldChild;
1090
+ }
1091
+ normalize() {}
1092
+ dispatchEvent(event) {
1093
+ const evt = event;
1094
+ if (evt.type) this._dispatchBubbledEvent(evt);
1095
+ return true;
1096
+ }
1097
+ cloneNode(deep) {
1098
+ const clone = new VirtualElement(this.nodeName, this.collector);
1099
+ const createMutation = {
1100
+ action: "createNode",
1101
+ id: clone._nodeId,
1102
+ tag: this.tagName,
1103
+ textContent: this._textContent || ""
1104
+ };
1105
+ this.collector.add(createMutation);
1106
+ for (const [k, v] of this._attributes) clone.setAttribute(k, v);
1107
+ clone._classes = [...this._classes];
1108
+ clone._ownerDocument = this._ownerDocument;
1109
+ if (deep) for (const child of this.childNodes) clone.appendChild(child.cloneNode(true));
1110
+ return clone;
1111
+ }
1112
+ get dataset() {
1113
+ if (this._datasetProxy) return this._datasetProxy;
1114
+ const el = this;
1115
+ this._datasetProxy = new Proxy({}, {
1116
+ get(_target, prop) {
1117
+ if (typeof prop !== "string") return void 0;
1118
+ const attrName = `data-${toKebabCase(prop)}`;
1119
+ return el.getAttribute(attrName) ?? void 0;
1120
+ },
1121
+ set(_target, prop, value) {
1122
+ if (typeof prop !== "string") return true;
1123
+ const attrName = `data-${toKebabCase(prop)}`;
1124
+ el.setAttribute(attrName, String(value));
1125
+ return true;
1126
+ },
1127
+ deleteProperty(_target, prop) {
1128
+ if (typeof prop !== "string") return true;
1129
+ const attrName = `data-${toKebabCase(prop)}`;
1130
+ el.removeAttribute(attrName);
1131
+ return true;
1132
+ },
1133
+ has(_target, prop) {
1134
+ if (typeof prop !== "string") return false;
1135
+ const attrName = `data-${toKebabCase(prop)}`;
1136
+ return el.hasAttribute(attrName);
1137
+ },
1138
+ ownKeys() {
1139
+ const keys = [];
1140
+ const attrs = el.attributes;
1141
+ for (let i = 0; i < attrs.length; i++) {
1142
+ const attr = attrs.item(i);
1143
+ if (attr?.name.startsWith("data-")) keys.push(kebabToCamel(attr.name.slice(5)));
1144
+ }
1145
+ return keys;
1146
+ },
1147
+ getOwnPropertyDescriptor(_target, prop) {
1148
+ if (typeof prop !== "string") return void 0;
1149
+ const attrName = `data-${toKebabCase(prop)}`;
1150
+ if (!el.hasAttribute(attrName)) return void 0;
1151
+ return {
1152
+ configurable: true,
1153
+ enumerable: true,
1154
+ writable: true,
1155
+ value: el.getAttribute(attrName)
1156
+ };
1157
+ }
1158
+ });
1159
+ return this._datasetProxy;
1160
+ }
1161
+ insertAdjacentHTML(position, html) {
1162
+ const mutation = {
1163
+ action: "insertAdjacentHTML",
1164
+ id: this._nodeId,
1165
+ position,
1166
+ html
1167
+ };
1168
+ this.collector.add(mutation);
1169
+ }
1170
+ contains(other) {
1171
+ if (!other) return false;
1172
+ if (other === this) return true;
1173
+ return this.childNodes.some((child) => child === other || child instanceof VirtualElement && child.contains(other));
1174
+ }
1175
+ querySelector(selector) {
1176
+ return querySelector(this, selector);
1177
+ }
1178
+ querySelectorAll(selector) {
1179
+ return querySelectorAll(this, selector);
1180
+ }
1181
+ matches(selector) {
1182
+ return matches(this, selector);
1183
+ }
1184
+ getElementsByTagName(tagName) {
1185
+ const upper = tagName.toUpperCase();
1186
+ return querySelectorAll(this, upper === "*" ? "*" : tagName);
1187
+ }
1188
+ getElementsByClassName(className) {
1189
+ const selector = className.split(/\s+/).filter(Boolean).map((c) => `.${c}`).join("");
1190
+ return querySelectorAll(this, selector);
1191
+ }
1192
+ closest(selector) {
1193
+ let current = this;
1194
+ while (current) {
1195
+ if (matches(current, selector)) return current;
1196
+ current = current.parentNode;
1197
+ }
1198
+ return null;
1199
+ }
1200
+ focus() {
1201
+ this.collector.add({
1202
+ action: "callMethod",
1203
+ id: this._nodeId,
1204
+ method: "focus",
1205
+ args: []
1206
+ });
1207
+ }
1208
+ blur() {
1209
+ this.collector.add({
1210
+ action: "callMethod",
1211
+ id: this._nodeId,
1212
+ method: "blur",
1213
+ args: []
1214
+ });
1215
+ }
1216
+ play() {
1217
+ this.collector.add({
1218
+ action: "callMethod",
1219
+ id: this._nodeId,
1220
+ method: "play",
1221
+ args: []
1222
+ });
1223
+ }
1224
+ pause() {
1225
+ this.collector.add({
1226
+ action: "callMethod",
1227
+ id: this._nodeId,
1228
+ method: "pause",
1229
+ args: []
1230
+ });
1231
+ }
1232
+ load() {
1233
+ this.collector.add({
1234
+ action: "callMethod",
1235
+ id: this._nodeId,
1236
+ method: "load",
1237
+ args: []
1238
+ });
1239
+ }
1240
+ click() {
1241
+ this.collector.add({
1242
+ action: "callMethod",
1243
+ id: this._nodeId,
1244
+ method: "click",
1245
+ args: []
1246
+ });
1247
+ }
1248
+ scrollIntoView(options) {
1249
+ this.collector.add({
1250
+ action: "callMethod",
1251
+ id: this._nodeId,
1252
+ method: "scrollIntoView",
1253
+ args: options ? [options] : []
1254
+ });
1255
+ }
1256
+ select() {
1257
+ this.collector.add({
1258
+ action: "callMethod",
1259
+ id: this._nodeId,
1260
+ method: "select",
1261
+ args: []
1262
+ });
1263
+ }
1264
+ showModal() {
1265
+ this.collector.add({
1266
+ action: "callMethod",
1267
+ id: this._nodeId,
1268
+ method: "showModal",
1269
+ args: []
1270
+ });
1271
+ }
1272
+ close() {
1273
+ this.collector.add({
1274
+ action: "callMethod",
1275
+ id: this._nodeId,
1276
+ method: "close",
1277
+ args: []
1278
+ });
1279
+ }
1280
+ /**
1281
+ * Synchronously query the main thread for this element's bounding rect via SyncChannel.
1282
+ * Returns a zero rect if no sync channel is available.
1283
+ */
1284
+ getBoundingClientRect() {
1285
+ const channel = this._ownerDocument?._syncChannel;
1286
+ if (channel) {
1287
+ const result = channel.request(QueryType.BoundingRect, JSON.stringify({ nodeId: this._nodeId }));
1288
+ if (result && typeof result === "object") {
1289
+ const r = result;
1290
+ return {
1291
+ top: r.top ?? 0,
1292
+ left: r.left ?? 0,
1293
+ right: r.right ?? 0,
1294
+ bottom: r.bottom ?? 0,
1295
+ width: r.width ?? 0,
1296
+ height: r.height ?? 0,
1297
+ x: r.x ?? r.left ?? 0,
1298
+ y: r.y ?? r.top ?? 0
1299
+ };
1300
+ }
1301
+ }
1302
+ return {
1303
+ top: 0,
1304
+ left: 0,
1305
+ right: 0,
1306
+ bottom: 0,
1307
+ width: 0,
1308
+ height: 0,
1309
+ x: 0,
1310
+ y: 0
1311
+ };
1312
+ }
1313
+ /** Parse a CSS style string (e.g., "color: red; font-size: 14px") and set each property individually. */
1314
+ _parseAndSetStyles(value) {
1315
+ for (const part of value.split(";")) {
1316
+ const colonIdx = part.indexOf(":");
1317
+ if (colonIdx === -1) continue;
1318
+ const key = part.slice(0, colonIdx).trim();
1319
+ const val = part.slice(colonIdx + 1).trim();
1320
+ if (key) this.style[key] = val;
1321
+ }
1322
+ }
1323
+ };
1324
+ /**
1325
+ * Virtual text node (nodeType 3). Setting nodeValue emits a setProperty mutation
1326
+ * so the main thread updates the real text node.
1327
+ */
1328
+ var VirtualTextNode = class VirtualTextNode {
1329
+ static ELEMENT_NODE = 1;
1330
+ static TEXT_NODE = 3;
1331
+ static COMMENT_NODE = 8;
1332
+ static DOCUMENT_NODE = 9;
1333
+ static DOCUMENT_FRAGMENT_NODE = 11;
1334
+ nodeType = 3;
1335
+ nodeName = "#text";
1336
+ parentNode = null;
1337
+ _ownerDocument = null;
1338
+ _nodeValue;
1339
+ constructor(text, _nodeId, collector) {
1340
+ this._nodeId = _nodeId;
1341
+ this.collector = collector;
1342
+ this._nodeValue = text;
1343
+ }
1344
+ get parentElement() {
1345
+ return this.parentNode;
1346
+ }
1347
+ get nodeValue() {
1348
+ return this._nodeValue;
1349
+ }
1350
+ set nodeValue(value) {
1351
+ this._nodeValue = value;
1352
+ const mutation = {
1353
+ action: "setProperty",
1354
+ id: this._nodeId,
1355
+ property: "nodeValue",
1356
+ value
1357
+ };
1358
+ this.collector.add(mutation);
1359
+ }
1360
+ get textContent() {
1361
+ return this._nodeValue;
1362
+ }
1363
+ set textContent(value) {
1364
+ this.nodeValue = value;
1365
+ }
1366
+ get nextSibling() {
1367
+ if (!this.parentNode) return null;
1368
+ const siblings = this.parentNode.childNodes;
1369
+ return siblings[siblings.indexOf(this) + 1] ?? null;
1370
+ }
1371
+ get previousSibling() {
1372
+ if (!this.parentNode) return null;
1373
+ const siblings = this.parentNode.childNodes;
1374
+ return siblings[siblings.indexOf(this) - 1] ?? null;
1375
+ }
1376
+ get childNodes() {
1377
+ return [];
1378
+ }
1379
+ remove() {
1380
+ if (this.parentNode) this.parentNode.childNodes = this.parentNode.childNodes.filter((c) => c !== this);
1381
+ this.parentNode = null;
1382
+ const mutation = {
1383
+ action: "removeNode",
1384
+ id: this._nodeId
1385
+ };
1386
+ this.collector.add(mutation);
1387
+ }
1388
+ cloneNode(_deep) {
1389
+ const id = createNodeId();
1390
+ const clone = new VirtualTextNode(this._nodeValue, id, this.collector);
1391
+ clone._ownerDocument = this._ownerDocument;
1392
+ const mutation = {
1393
+ action: "createNode",
1394
+ id,
1395
+ tag: "#text",
1396
+ textContent: this._nodeValue
1397
+ };
1398
+ this.collector.add(mutation);
1399
+ return clone;
1400
+ }
1401
+ };
1402
+ /**
1403
+ * Virtual comment node (nodeType 8). Used primarily as DOM markers by frameworks.
1404
+ * Comment nodes do not emit mutations on value changes.
1405
+ */
1406
+ var VirtualCommentNode = class VirtualCommentNode {
1407
+ static ELEMENT_NODE = 1;
1408
+ static TEXT_NODE = 3;
1409
+ static COMMENT_NODE = 8;
1410
+ static DOCUMENT_NODE = 9;
1411
+ static DOCUMENT_FRAGMENT_NODE = 11;
1412
+ nodeType = 8;
1413
+ nodeName = "#comment";
1414
+ parentNode = null;
1415
+ _ownerDocument = null;
1416
+ _nodeValue;
1417
+ constructor(text, _nodeId, collector) {
1418
+ this._nodeId = _nodeId;
1419
+ this.collector = collector;
1420
+ this._nodeValue = text;
1421
+ }
1422
+ get parentElement() {
1423
+ return this.parentNode;
1424
+ }
1425
+ get nodeValue() {
1426
+ return this._nodeValue;
1427
+ }
1428
+ set nodeValue(value) {
1429
+ this._nodeValue = value;
1430
+ }
1431
+ get textContent() {
1432
+ return this._nodeValue;
1433
+ }
1434
+ get nextSibling() {
1435
+ if (!this.parentNode) return null;
1436
+ const siblings = this.parentNode.childNodes;
1437
+ return siblings[siblings.indexOf(this) + 1] ?? null;
1438
+ }
1439
+ get previousSibling() {
1440
+ if (!this.parentNode) return null;
1441
+ const siblings = this.parentNode.childNodes;
1442
+ return siblings[siblings.indexOf(this) - 1] ?? null;
1443
+ }
1444
+ get childNodes() {
1445
+ return [];
1446
+ }
1447
+ remove() {
1448
+ if (this.parentNode) this.parentNode.childNodes = this.parentNode.childNodes.filter((c) => c !== this);
1449
+ this.parentNode = null;
1450
+ const mutation = {
1451
+ action: "removeNode",
1452
+ id: this._nodeId
1453
+ };
1454
+ this.collector.add(mutation);
1455
+ }
1456
+ cloneNode(_deep) {
1457
+ const id = createNodeId();
1458
+ const clone = new VirtualCommentNode(this._nodeValue, id, this.collector);
1459
+ clone._ownerDocument = this._ownerDocument;
1460
+ const mutation = {
1461
+ action: "createComment",
1462
+ id,
1463
+ textContent: this._nodeValue
1464
+ };
1465
+ this.collector.add(mutation);
1466
+ return clone;
1467
+ }
1468
+ };
1469
+ /** DOMTokenList-compatible classList implementation that delegates to VirtualElement.className. */
1470
+ var VirtualClassList = class {
1471
+ constructor(element) {
1472
+ this.element = element;
1473
+ }
1474
+ add(...names) {
1475
+ const classes = this.element.className.split(" ").filter(Boolean);
1476
+ for (const name of names) if (!classes.includes(name)) classes.push(name);
1477
+ this.element.className = classes.join(" ");
1478
+ }
1479
+ remove(...names) {
1480
+ const nameSet = new Set(names);
1481
+ const classes = this.element.className.split(" ").filter((c) => c !== "" && !nameSet.has(c));
1482
+ this.element.className = classes.join(" ");
1483
+ }
1484
+ contains(name) {
1485
+ return this.element.className.split(" ").includes(name);
1486
+ }
1487
+ toggle(name, force) {
1488
+ const has = this.contains(name);
1489
+ if (force !== void 0) {
1490
+ if (force && !has) this.add(name);
1491
+ else if (!force && has) this.remove(name);
1492
+ return force;
1493
+ }
1494
+ if (has) {
1495
+ this.remove(name);
1496
+ return false;
1497
+ }
1498
+ this.add(name);
1499
+ return true;
1500
+ }
1501
+ get length() {
1502
+ return this.element.className.split(" ").filter(Boolean).length;
1503
+ }
1504
+ };
1505
+ //#endregion
1506
+ //#region src/worker-thread/events.ts
1507
+ /**
1508
+ * Virtual event classes that simulate DOM event behavior
1509
+ * including bubbling, propagation control, and default prevention.
1510
+ */
1511
+ var VirtualEvent = class {
1512
+ type;
1513
+ target;
1514
+ currentTarget;
1515
+ bubbles;
1516
+ cancelable;
1517
+ defaultPrevented = false;
1518
+ timeStamp;
1519
+ isTrusted;
1520
+ eventPhase = 0;
1521
+ _stopPropagation = false;
1522
+ _stopImmediatePropagation = false;
1523
+ constructor(type, init) {
1524
+ this.type = type;
1525
+ this.target = init?.target ?? null;
1526
+ this.currentTarget = init?.currentTarget ?? null;
1527
+ this.bubbles = init?.bubbles ?? false;
1528
+ this.cancelable = init?.cancelable ?? true;
1529
+ this.timeStamp = init?.timeStamp ?? Date.now();
1530
+ this.isTrusted = init?.isTrusted ?? false;
1531
+ if (init) {
1532
+ for (const key of Object.keys(init)) if (!(key in this)) this[key] = init[key];
1533
+ }
1534
+ }
1535
+ preventDefault() {
1536
+ if (this.cancelable) this.defaultPrevented = true;
1537
+ }
1538
+ stopPropagation() {
1539
+ this._stopPropagation = true;
1540
+ }
1541
+ stopImmediatePropagation() {
1542
+ this._stopImmediatePropagation = true;
1543
+ this._stopPropagation = true;
1544
+ }
1545
+ get propagationStopped() {
1546
+ return this._stopPropagation;
1547
+ }
1548
+ get immediatePropagationStopped() {
1549
+ return this._stopImmediatePropagation;
1550
+ }
1551
+ };
1552
+ var VirtualCustomEvent = class extends VirtualEvent {
1553
+ detail;
1554
+ constructor(type, init) {
1555
+ super(type, init);
1556
+ this.detail = init?.detail ?? null;
1557
+ }
1558
+ };
1559
+ //#endregion
1560
+ //#region src/worker-thread/mutation-collector.ts
1561
+ const MAX_COALESCED_LOG = 50;
1562
+ /**
1563
+ * Collects DOM mutations during synchronous execution and flushes them
1564
+ * as a batched message at the end of the current microtask.
1565
+ */
1566
+ var MutationCollector = class {
1567
+ queue = [];
1568
+ scheduled = false;
1569
+ uidCounter = 0;
1570
+ transport = null;
1571
+ _coalesceEnabled = true;
1572
+ _stats = {
1573
+ added: 0,
1574
+ coalesced: 0,
1575
+ flushed: 0
1576
+ };
1577
+ _coalescedLog = [];
1578
+ _perTypeCoalesced = /* @__PURE__ */ new Map();
1579
+ /** Total mutations added (monotonically increasing counter for diff-based tracking). */
1580
+ get totalAdded() {
1581
+ return this._stats.added;
1582
+ }
1583
+ /** Feature 15: Current causal event tag for this flush cycle */
1584
+ _causalEvent = null;
1585
+ getStats() {
1586
+ return { ...this._stats };
1587
+ }
1588
+ getCoalescedLog() {
1589
+ return this._coalescedLog.slice();
1590
+ }
1591
+ getPerTypeCoalesced() {
1592
+ const result = {};
1593
+ for (const [action, counts] of this._perTypeCoalesced) result[action] = { ...counts };
1594
+ return result;
1595
+ }
1596
+ constructor(appId) {
1597
+ this.appId = appId;
1598
+ }
1599
+ /** Feature 15: Set the causal event for the current mutation cycle. */
1600
+ setCausalEvent(event) {
1601
+ this._causalEvent = event;
1602
+ }
1603
+ /** Feature 15: Get current causal event. */
1604
+ getCausalEvent() {
1605
+ return this._causalEvent;
1606
+ }
1607
+ enableCoalescing(enabled) {
1608
+ this._coalesceEnabled = enabled;
1609
+ }
1610
+ setTransport(transport) {
1611
+ this.transport = transport;
1612
+ }
1613
+ add(mutation) {
1614
+ this._stats.added++;
1615
+ const counts = this._perTypeCoalesced.get(mutation.action);
1616
+ if (counts) counts.added++;
1617
+ else this._perTypeCoalesced.set(mutation.action, {
1618
+ added: 1,
1619
+ coalesced: 0
1620
+ });
1621
+ this.queue.push(mutation);
1622
+ if (!this.scheduled) {
1623
+ this.scheduled = true;
1624
+ queueMicrotask(() => this.flush());
1625
+ }
1626
+ }
1627
+ coalesce(mutations) {
1628
+ if (mutations.length <= 1) return mutations;
1629
+ const lastIndex = /* @__PURE__ */ new Map();
1630
+ const toRemove = /* @__PURE__ */ new Set();
1631
+ const createdAt = /* @__PURE__ */ new Map();
1632
+ const attachedIds = /* @__PURE__ */ new Set();
1633
+ const eliminatedIds = /* @__PURE__ */ new Set();
1634
+ for (let i = 0; i < mutations.length; i++) {
1635
+ const m = mutations[i];
1636
+ let key = null;
1637
+ switch (m.action) {
1638
+ case "setStyle":
1639
+ key = `setStyle:${m.id}:${m.property}`;
1640
+ break;
1641
+ case "setAttribute":
1642
+ key = `setAttribute:${m.id}:${m.name}`;
1643
+ break;
1644
+ case "setClassName":
1645
+ key = `setClassName:${m.id}`;
1646
+ break;
1647
+ case "setTextContent":
1648
+ key = `setTextContent:${m.id}`;
1649
+ break;
1650
+ case "setProperty":
1651
+ key = `setProperty:${m.id}:${m.property}`;
1652
+ break;
1653
+ case "setHTML":
1654
+ key = `setHTML:${m.id}`;
1655
+ break;
1656
+ case "createNode":
1657
+ createdAt.set(m.id, i);
1658
+ break;
1659
+ case "appendChild":
1660
+ attachedIds.add(m.childId);
1661
+ break;
1662
+ case "insertBefore":
1663
+ attachedIds.add(m.newId);
1664
+ break;
1665
+ case "removeNode":
1666
+ if (createdAt.has(m.id) && !attachedIds.has(m.id)) {
1667
+ const createdIdx = createdAt.get(m.id);
1668
+ if (createdIdx !== void 0) toRemove.add(createdIdx);
1669
+ toRemove.add(i);
1670
+ createdAt.delete(m.id);
1671
+ eliminatedIds.add(m.id);
1672
+ }
1673
+ break;
1674
+ }
1675
+ if (key !== null) {
1676
+ const prev = lastIndex.get(key);
1677
+ if (prev !== void 0) toRemove.add(prev);
1678
+ lastIndex.set(key, i);
1679
+ }
1680
+ }
1681
+ if (eliminatedIds.size > 0) for (let j = 0; j < mutations.length; j++) {
1682
+ if (toRemove.has(j)) continue;
1683
+ const mut = mutations[j];
1684
+ if ("id" in mut && eliminatedIds.has(mut.id)) toRemove.add(j);
1685
+ }
1686
+ if (toRemove.size > 0) {
1687
+ const now = Date.now();
1688
+ for (const idx of toRemove) {
1689
+ const removed = mutations[idx];
1690
+ const action = removed.action;
1691
+ const entry = {
1692
+ action,
1693
+ key: this._buildKey(removed),
1694
+ timestamp: now
1695
+ };
1696
+ this._coalescedLog.push(entry);
1697
+ if (this._coalescedLog.length > MAX_COALESCED_LOG) this._coalescedLog.shift();
1698
+ const counts = this._perTypeCoalesced.get(action);
1699
+ if (counts) counts.coalesced++;
1700
+ }
1701
+ }
1702
+ if (toRemove.size === 0) return mutations;
1703
+ return mutations.filter((_, i) => !toRemove.has(i));
1704
+ }
1705
+ _buildKey(m) {
1706
+ switch (m.action) {
1707
+ case "setStyle": return `setStyle:${m.id}:${m.property}`;
1708
+ case "setAttribute": return `setAttribute:${m.id}:${m.name}`;
1709
+ case "setClassName": return `setClassName:${m.id}`;
1710
+ case "setTextContent": return `setTextContent:${m.id}`;
1711
+ case "setProperty": return `setProperty:${m.id}:${m.property}`;
1712
+ case "setHTML": return `setHTML:${m.id}`;
1713
+ default: return `${m.action}:${"id" in m ? m.id : "?"}`;
1714
+ }
1715
+ }
1716
+ flush() {
1717
+ if (this.queue.length === 0) {
1718
+ this.scheduled = false;
1719
+ return;
1720
+ }
1721
+ const perfMarkName = `async-dom:flush:${this.appId}`;
1722
+ if (typeof performance !== "undefined" && performance.mark) performance.mark(`${perfMarkName}:start`);
1723
+ const rawLength = this.queue.length;
1724
+ const batch = this._coalesceEnabled ? this.coalesce(this.queue.splice(0)) : this.queue.splice(0);
1725
+ this.scheduled = false;
1726
+ this._stats.coalesced += rawLength - batch.length;
1727
+ this._stats.flushed += batch.length;
1728
+ if (batch.length === 0) {
1729
+ this._causalEvent = null;
1730
+ return;
1731
+ }
1732
+ this.uidCounter++;
1733
+ if (this.transport?.readyState !== "open") {
1734
+ this._causalEvent = null;
1735
+ return;
1736
+ }
1737
+ const message = {
1738
+ type: "mutation",
1739
+ appId: this.appId,
1740
+ uid: this.uidCounter,
1741
+ mutations: batch,
1742
+ sentAt: Date.now()
1743
+ };
1744
+ if (this._causalEvent) {
1745
+ message.causalEvent = this._causalEvent;
1746
+ this._causalEvent = null;
1747
+ }
1748
+ this.transport.send(message);
1749
+ if (typeof performance !== "undefined" && performance.mark && performance.measure) {
1750
+ performance.mark(`${perfMarkName}:end`);
1751
+ try {
1752
+ performance.measure(perfMarkName, `${perfMarkName}:start`, `${perfMarkName}:end`);
1753
+ } catch {}
1754
+ }
1755
+ }
1756
+ /** Force-flush all pending mutations immediately */
1757
+ flushSync() {
1758
+ this.flush();
1759
+ }
1760
+ /** Get number of pending mutations (useful for testing) */
1761
+ get pendingCount() {
1762
+ return this.queue.length;
1763
+ }
1764
+ };
1765
+ //#endregion
1766
+ //#region src/worker-thread/document.ts
1767
+ /**
1768
+ * Virtual Document that exists in a worker thread.
1769
+ *
1770
+ * Mirrors a subset of the browser's Document API, allowing framework code
1771
+ * to call createElement, querySelector, addEventListener, etc. without
1772
+ * touching the real DOM. Every mutating operation records a DomMutation via
1773
+ * the MutationCollector, which batches and sends them to the main thread.
1774
+ *
1775
+ * Key subsystems:
1776
+ * - Element creation: createElement, createTextNode, createComment
1777
+ * - Event dispatch: routes serialized events from the main thread to the
1778
+ * correct VirtualElement listener using O(1) listenerId lookup
1779
+ * - ID registries: maintains both user-visible id attributes (_ids) and
1780
+ * internal NodeId -> VirtualElement mappings (_nodeIdToElement)
1781
+ * - Sync channel: optional SharedArrayBuffer channel for blocking DOM queries
1782
+ */
1783
+ var VirtualDocument = class {
1784
+ body;
1785
+ head;
1786
+ documentElement;
1787
+ nodeType = 9;
1788
+ nodeName = "#document";
1789
+ /** Collects mutations and batches them for transport to the main thread. */
1790
+ collector;
1791
+ /** Reference to the VirtualWindow, providing location/navigator access. */
1792
+ _defaultView = null;
1793
+ /** Optional sync channel for blocking DOM queries (e.g., getBoundingClientRect). */
1794
+ _syncChannel = null;
1795
+ _title = "";
1796
+ _cookie = "";
1797
+ /** Map of user-visible id attribute -> VirtualElement for getElementById. */
1798
+ _ids = /* @__PURE__ */ new Map();
1799
+ /** Map of internal NodeId -> VirtualElement for event target resolution. */
1800
+ _nodeIdToElement = /* @__PURE__ */ new Map();
1801
+ /** Document-level event listeners keyed by listenerId. */
1802
+ _listenerMap = /* @__PURE__ */ new Map();
1803
+ /** Map of listenerId -> owning VirtualElement for O(1) event dispatch routing. */
1804
+ _listenerToElement = /* @__PURE__ */ new Map();
1805
+ _listenerCounter = 0;
1806
+ constructor(appId) {
1807
+ this.collector = new MutationCollector(appId);
1808
+ this.documentElement = new VirtualElement("HTML", this.collector, 3);
1809
+ this.head = new VirtualElement("HEAD", this.collector, 2);
1810
+ this.body = new VirtualElement("BODY", this.collector, 1);
1811
+ this.documentElement._ownerDocument = this;
1812
+ this.head._ownerDocument = this;
1813
+ this.body._ownerDocument = this;
1814
+ this._nodeIdToElement.set(3, this.documentElement);
1815
+ this._nodeIdToElement.set(2, this.head);
1816
+ this._nodeIdToElement.set(1, this.body);
1817
+ this.documentElement.appendChild(this.head);
1818
+ this.documentElement.appendChild(this.body);
1819
+ this.collector.flush();
1820
+ }
1821
+ /**
1822
+ * Create a new virtual element and emit a createNode mutation.
1823
+ * The element is registered by NodeId for event target resolution.
1824
+ */
1825
+ createElement(tag) {
1826
+ const id = createNodeId();
1827
+ const element = new VirtualElement(tag, this.collector, id);
1828
+ element._ownerDocument = this;
1829
+ this._nodeIdToElement.set(id, element);
1830
+ const mutation = {
1831
+ action: "createNode",
1832
+ id,
1833
+ tag: element.tagName,
1834
+ textContent: ""
1835
+ };
1836
+ this.collector.add(mutation);
1837
+ return element;
1838
+ }
1839
+ /** Create a namespaced element (e.g., SVG). Delegates to createElement then sets the namespace. */
1840
+ createElementNS(ns, tag) {
1841
+ const el = this.createElement(tag);
1842
+ el._setNamespaceURI(ns);
1843
+ return el;
1844
+ }
1845
+ /** Create a virtual text node and emit a createNode mutation with tag "#text". */
1846
+ createTextNode(text) {
1847
+ const id = createNodeId();
1848
+ const node = new VirtualTextNode(text, id, this.collector);
1849
+ node._ownerDocument = this;
1850
+ const mutation = {
1851
+ action: "createNode",
1852
+ id,
1853
+ tag: "#text",
1854
+ textContent: text
1855
+ };
1856
+ this.collector.add(mutation);
1857
+ return node;
1858
+ }
1859
+ /** Create a virtual comment node and emit a createComment mutation. */
1860
+ createComment(text) {
1861
+ const id = createNodeId();
1862
+ const node = new VirtualCommentNode(text, id, this.collector);
1863
+ node._ownerDocument = this;
1864
+ const mutation = {
1865
+ action: "createComment",
1866
+ id,
1867
+ textContent: text
1868
+ };
1869
+ this.collector.add(mutation);
1870
+ return node;
1871
+ }
1872
+ createDocumentFragment() {
1873
+ const frag = new VirtualElement("#document-fragment", this.collector, createNodeId());
1874
+ frag._ownerDocument = this;
1875
+ return frag;
1876
+ }
1877
+ getElementById(id) {
1878
+ return this._ids.get(id) ?? null;
1879
+ }
1880
+ /**
1881
+ * Add a document-level event listener. The listener is stored locally and
1882
+ * a mutation is emitted to tell the main thread to attach a real listener.
1883
+ */
1884
+ addEventListener(name, callback) {
1885
+ if (!name) return;
1886
+ const listenerId = `document_${name}_${++this._listenerCounter}`;
1887
+ this._listenerMap.set(listenerId, callback);
1888
+ const mutation = {
1889
+ action: "addEventListener",
1890
+ id: 4,
1891
+ name,
1892
+ listenerId
1893
+ };
1894
+ this.collector.add(mutation);
1895
+ }
1896
+ /** Remove a document-level listener by callback reference. Emits a removeEventListener mutation. */
1897
+ removeEventListener(_name, callback) {
1898
+ for (const [listenerId, cb] of this._listenerMap.entries()) if (cb === callback) {
1899
+ this._listenerMap.delete(listenerId);
1900
+ const mutation = {
1901
+ action: "removeEventListener",
1902
+ id: 4,
1903
+ listenerId
1904
+ };
1905
+ this.collector.add(mutation);
1906
+ break;
1907
+ }
1908
+ }
1909
+ /**
1910
+ * Route an event from the main thread to the appropriate listener.
1911
+ * Resolves serialized target IDs to actual VirtualElement references.
1912
+ */
1913
+ _resolveTarget(value) {
1914
+ if (typeof value === "number") return this._nodeIdToElement.get(value) ?? null;
1915
+ if (typeof value === "string") {
1916
+ const num = Number(value);
1917
+ if (!Number.isNaN(num)) return this._nodeIdToElement.get(num) ?? null;
1918
+ return this._ids.get(value) ?? null;
1919
+ }
1920
+ return null;
1921
+ }
1922
+ /**
1923
+ * Route a serialized event from the main thread to the appropriate listener.
1924
+ *
1925
+ * Flow:
1926
+ * 1. Resolve serialized target/currentTarget/relatedTarget IDs to VirtualElement refs
1927
+ * 2. Wrap the event data in a VirtualEvent for bubbling support
1928
+ * 3. Tag the MutationCollector with causal event info for causality tracking
1929
+ * 4. Sync input/media state from the event to the target element
1930
+ * 5. Dispatch to document-level listeners, or bubble through the element tree
1931
+ */
1932
+ dispatchEvent(listenerId, event) {
1933
+ const evt = event;
1934
+ if (evt.target != null && typeof evt.target !== "object") evt.target = this._resolveTarget(evt.target);
1935
+ if (evt.currentTarget != null && typeof evt.currentTarget !== "object") evt.currentTarget = this._resolveTarget(evt.currentTarget);
1936
+ if (evt.relatedTarget != null && typeof evt.relatedTarget !== "object") evt.relatedTarget = this._resolveTarget(evt.relatedTarget);
1937
+ const virtualEvent = new VirtualEvent(evt.type, evt);
1938
+ const eventType = evt.type ?? "unknown";
1939
+ this.collector.setCausalEvent({
1940
+ eventType,
1941
+ listenerId,
1942
+ timestamp: Date.now()
1943
+ });
1944
+ const perfMarkName = `async-dom:event:${eventType}:${listenerId}`;
1945
+ if (typeof performance !== "undefined" && performance.mark) performance.mark(`${perfMarkName}:start`);
1946
+ const targetEl = virtualEvent.target;
1947
+ if (targetEl && typeof targetEl === "object" && "_updateInputState" in targetEl) {
1948
+ const inputState = {};
1949
+ if (evt.value !== void 0) inputState.value = evt.value;
1950
+ if (evt.checked !== void 0) inputState.checked = evt.checked;
1951
+ if (evt.selectedIndex !== void 0) inputState.selectedIndex = evt.selectedIndex;
1952
+ if (Object.keys(inputState).length > 0) targetEl._updateInputState(inputState);
1953
+ }
1954
+ if (targetEl && typeof targetEl === "object") {
1955
+ const mediaState = {};
1956
+ if (evt.currentTime !== void 0) mediaState.currentTime = evt.currentTime;
1957
+ if (evt.duration !== void 0) mediaState.duration = evt.duration;
1958
+ if (evt.paused !== void 0) mediaState.paused = evt.paused;
1959
+ if (evt.ended !== void 0) mediaState.ended = evt.ended;
1960
+ if (evt.readyState !== void 0) mediaState.readyState = evt.readyState;
1961
+ if (Object.keys(mediaState).length > 0 && "_updateMediaState" in targetEl) targetEl._updateMediaState(mediaState);
1962
+ }
1963
+ const docListener = this._listenerMap.get(listenerId);
1964
+ if (docListener) {
1965
+ docListener(virtualEvent);
1966
+ this._finishEventPerf(perfMarkName);
1967
+ return;
1968
+ }
1969
+ const targetElement = this._listenerToElement.get(listenerId) ?? null;
1970
+ if (targetElement) {
1971
+ virtualEvent.currentTarget = targetElement;
1972
+ targetElement._dispatchBubbledEvent(virtualEvent);
1973
+ if (virtualEvent.bubbles && !virtualEvent.propagationStopped) {
1974
+ let current = targetElement.parentNode;
1975
+ while (current && !virtualEvent.propagationStopped) {
1976
+ virtualEvent.currentTarget = current;
1977
+ current._dispatchBubbledEvent(virtualEvent);
1978
+ if (virtualEvent.propagationStopped) break;
1979
+ current = current.parentNode;
1980
+ }
1981
+ }
1982
+ }
1983
+ this._finishEventPerf(perfMarkName);
1984
+ }
1985
+ /** Feature 16: finish performance measurement for an event dispatch */
1986
+ _finishEventPerf(perfMarkName) {
1987
+ if (typeof performance !== "undefined" && performance.mark && performance.measure) {
1988
+ performance.mark(`${perfMarkName}:end`);
1989
+ try {
1990
+ performance.measure(perfMarkName, `${perfMarkName}:start`, `${perfMarkName}:end`);
1991
+ } catch {}
1992
+ }
1993
+ }
1994
+ /**
1995
+ * Register an element by its internal NodeId.
1996
+ */
1997
+ registerElement(id, element) {
1998
+ this._nodeIdToElement.set(id, element);
1999
+ }
2000
+ /**
2001
+ * Unregister an element by its internal NodeId (called during cleanup on removal).
2002
+ */
2003
+ unregisterElement(id) {
2004
+ this._nodeIdToElement.delete(id);
2005
+ }
2006
+ /**
2007
+ * Register an element by its user-visible id attribute (distinct from internal NodeId).
2008
+ */
2009
+ registerElementById(id, element) {
2010
+ this._ids.set(id, element);
2011
+ }
2012
+ /**
2013
+ * Unregister an element by its user-visible id attribute.
2014
+ */
2015
+ unregisterElementById(id) {
2016
+ this._ids.delete(id);
2017
+ }
2018
+ /**
2019
+ * Register a listener ID to its owning element for O(1) event dispatch.
2020
+ */
2021
+ registerListener(listenerId, element) {
2022
+ this._listenerToElement.set(listenerId, element);
2023
+ }
2024
+ /**
2025
+ * Unregister a listener ID (called on removeEventListener or element cleanup).
2026
+ */
2027
+ unregisterListener(listenerId) {
2028
+ this._listenerToElement.delete(listenerId);
2029
+ }
2030
+ /** Stub implementation of document.createEvent for legacy API compatibility. */
2031
+ createEvent(_type) {
2032
+ return {
2033
+ type: "",
2034
+ initEvent(type, bubbles, cancelable) {
2035
+ this.type = type;
2036
+ this.bubbles = bubbles ?? false;
2037
+ this.cancelable = cancelable ?? false;
2038
+ },
2039
+ bubbles: false,
2040
+ cancelable: false,
2041
+ preventDefault() {},
2042
+ stopPropagation() {},
2043
+ stopImmediatePropagation() {}
2044
+ };
2045
+ }
2046
+ get activeElement() {
2047
+ return this.body;
2048
+ }
2049
+ /** Stub implementation of document.createRange for framework compatibility. */
2050
+ createRange() {
2051
+ const doc = this;
2052
+ return {
2053
+ createContextualFragment(_html) {
2054
+ return doc.createDocumentFragment();
2055
+ },
2056
+ setStart() {},
2057
+ setEnd() {},
2058
+ collapse() {},
2059
+ selectNodeContents() {},
2060
+ cloneRange() {
2061
+ return doc.createRange();
2062
+ }
2063
+ };
2064
+ }
2065
+ /** Simplified TreeWalker that pre-collects all descendant nodes for sequential traversal. */
2066
+ createTreeWalker(root, _whatToShow) {
2067
+ const nodes = [];
2068
+ function collect(node) {
2069
+ nodes.push(node);
2070
+ if (node instanceof VirtualElement) for (const child of node.childNodes) collect(child);
2071
+ }
2072
+ collect(root);
2073
+ let idx = 0;
2074
+ return {
2075
+ currentNode: root,
2076
+ nextNode() {
2077
+ idx++;
2078
+ if (idx < nodes.length) {
2079
+ this.currentNode = nodes[idx];
2080
+ return nodes[idx];
2081
+ }
2082
+ return null;
2083
+ }
2084
+ };
2085
+ }
2086
+ querySelector(selector) {
2087
+ if (selector.startsWith("#")) {
2088
+ const found = this.getElementById(selector.slice(1));
2089
+ if (found) return found;
2090
+ }
2091
+ return querySelector(this.body, selector) ?? querySelector(this.head, selector);
2092
+ }
2093
+ querySelectorAll(selector) {
2094
+ return [...querySelectorAll(this.head, selector), ...querySelectorAll(this.body, selector)];
2095
+ }
2096
+ getElementsByTagName(tagName) {
2097
+ const upper = tagName.toUpperCase();
2098
+ return this.querySelectorAll(upper === "*" ? "*" : tagName);
2099
+ }
2100
+ getElementsByClassName(className) {
2101
+ const selector = className.split(/\s+/).filter(Boolean).map((c) => `.${c}`).join("");
2102
+ return this.querySelectorAll(selector);
2103
+ }
2104
+ get title() {
2105
+ return this._title;
2106
+ }
2107
+ set title(value) {
2108
+ this._title = value;
2109
+ }
2110
+ get URL() {
2111
+ return this._defaultView?.location?.href ?? "";
2112
+ }
2113
+ get location() {
2114
+ return this._defaultView?.location ?? null;
2115
+ }
2116
+ get cookie() {
2117
+ return this._cookie;
2118
+ }
2119
+ set cookie(value) {
2120
+ this._cookie = value;
2121
+ }
2122
+ get readyState() {
2123
+ return "complete";
2124
+ }
2125
+ get compatMode() {
2126
+ return "CSS1Compat";
2127
+ }
2128
+ get characterSet() {
2129
+ return "UTF-8";
2130
+ }
2131
+ get contentType() {
2132
+ return "text/html";
2133
+ }
2134
+ get visibilityState() {
2135
+ return "visible";
2136
+ }
2137
+ get hidden() {
2138
+ return false;
2139
+ }
2140
+ get childNodes() {
2141
+ return [this.documentElement];
2142
+ }
2143
+ get children() {
2144
+ return [this.documentElement];
2145
+ }
2146
+ get firstChild() {
2147
+ return this.documentElement;
2148
+ }
2149
+ contains(node) {
2150
+ if (node === this) return true;
2151
+ return this.documentElement.contains(node);
2152
+ }
2153
+ get implementation() {
2154
+ return { hasFeature() {
2155
+ return false;
2156
+ } };
2157
+ }
2158
+ get defaultView() {
2159
+ return this._defaultView;
2160
+ }
2161
+ get ownerDocument() {
2162
+ return this;
2163
+ }
2164
+ /**
2165
+ * Clean up all internal state. Called when the worker DOM instance is being destroyed.
2166
+ * Clears element registries, listener maps, and resets counters.
2167
+ */
2168
+ destroy() {
2169
+ this.collector.flushSync();
2170
+ this._ids.clear();
2171
+ this._nodeIdToElement.clear();
2172
+ this._listenerMap.clear();
2173
+ this._listenerToElement.clear();
2174
+ this._listenerCounter = 0;
2175
+ this._syncChannel = null;
2176
+ this._defaultView = null;
2177
+ }
2178
+ /** Serialize the entire virtual DOM tree to a JSON-compatible structure for debugging. */
2179
+ toJSON() {
2180
+ return this._serializeNode(this.documentElement);
2181
+ }
2182
+ _serializeNode(node) {
2183
+ if (node.nodeType === 3) return {
2184
+ type: "text",
2185
+ id: node._nodeId,
2186
+ text: node.nodeValue
2187
+ };
2188
+ if (node.nodeType === 8) return {
2189
+ type: "comment",
2190
+ id: node._nodeId,
2191
+ text: node.nodeValue
2192
+ };
2193
+ const el = node;
2194
+ const attrs = {};
2195
+ const a = el.attributes;
2196
+ for (let i = 0; i < a.length; i++) {
2197
+ const attr = a.item(i);
2198
+ if (attr) attrs[attr.name] = attr.value;
2199
+ }
2200
+ return {
2201
+ type: "element",
2202
+ id: el._nodeId,
2203
+ tag: el.tagName,
2204
+ ...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
2205
+ ...el.className ? { className: el.className } : {},
2206
+ children: el.childNodes.map((c) => this._serializeNode(c))
2207
+ };
2208
+ }
2209
+ };
2210
+ //#endregion
2211
+ //#region src/worker-thread/observers.ts
2212
+ /**
2213
+ * Stub observer classes that prevent crashes when frameworks
2214
+ * attempt to use browser observers in a worker context.
2215
+ */
2216
+ var VirtualMutationObserver = class {
2217
+ constructor(_callback) {}
2218
+ observe(_target, _options) {}
2219
+ disconnect() {}
2220
+ takeRecords() {
2221
+ return [];
2222
+ }
2223
+ };
2224
+ var VirtualResizeObserver = class {
2225
+ constructor(_callback) {}
2226
+ observe(_target, _options) {}
2227
+ unobserve(_target) {}
2228
+ disconnect() {}
2229
+ };
2230
+ var VirtualIntersectionObserver = class {
2231
+ root = null;
2232
+ rootMargin = "0px";
2233
+ thresholds = [0];
2234
+ constructor(_callback, _options) {}
2235
+ observe(_target) {}
2236
+ unobserve(_target) {}
2237
+ disconnect() {}
2238
+ takeRecords() {
2239
+ return [];
2240
+ }
2241
+ };
2242
+ //#endregion
2243
+ //#region src/worker-thread/storage.ts
2244
+ /**
2245
+ * Scoped Storage implementation that can optionally sync with
2246
+ * the main thread's real localStorage/sessionStorage via the sync channel.
2247
+ *
2248
+ * Each worker app gets its own isolated storage with a unique prefix.
2249
+ * When a sync channel is available, reads/writes are persisted to the
2250
+ * real browser storage on the main thread.
2251
+ */
2252
+ var ScopedStorage = class {
2253
+ cache = /* @__PURE__ */ new Map();
2254
+ prefix;
2255
+ storageType;
2256
+ getSyncChannel;
2257
+ queryType;
2258
+ constructor(prefix, storageType, getSyncChannel, queryType) {
2259
+ this.prefix = prefix;
2260
+ this.storageType = storageType;
2261
+ this.getSyncChannel = getSyncChannel;
2262
+ this.queryType = queryType;
2263
+ }
2264
+ syncCall(method, args) {
2265
+ const channel = this.getSyncChannel();
2266
+ if (!channel) return null;
2267
+ return channel.request(this.queryType, JSON.stringify({
2268
+ property: `${this.storageType}.${method}`,
2269
+ args
2270
+ }));
2271
+ }
2272
+ get length() {
2273
+ return this.cache.size;
2274
+ }
2275
+ key(index) {
2276
+ return [...this.cache.keys()][index] ?? null;
2277
+ }
2278
+ getItem(key) {
2279
+ const cached = this.cache.get(key);
2280
+ if (cached !== void 0) return cached;
2281
+ const result = this.syncCall("getItem", [this.prefix + key]);
2282
+ if (typeof result === "string") {
2283
+ this.cache.set(key, result);
2284
+ return result;
2285
+ }
2286
+ return null;
2287
+ }
2288
+ setItem(key, value) {
2289
+ const strValue = String(value);
2290
+ this.cache.set(key, strValue);
2291
+ this.syncCall("setItem", [this.prefix + key, strValue]);
2292
+ }
2293
+ removeItem(key) {
2294
+ this.cache.delete(key);
2295
+ this.syncCall("removeItem", [this.prefix + key]);
2296
+ }
2297
+ clear() {
2298
+ for (const key of this.cache.keys()) this.syncCall("removeItem", [this.prefix + key]);
2299
+ this.cache.clear();
2300
+ }
2301
+ };
2302
+ //#endregion
2303
+ //#region src/worker-thread/index.ts
2304
+ /**
2305
+ * Creates a virtual DOM environment inside a Web Worker.
2306
+ *
2307
+ * Returns a `document` and `window` that can be used by frameworks
2308
+ * or vanilla JS. All DOM mutations are automatically collected and
2309
+ * sent to the main thread for rendering.
2310
+ */
2311
+ function createWorkerDom(config) {
2312
+ const appId = config?.appId ?? createAppId("worker");
2313
+ const transport = config?.transport ?? new WorkerSelfTransport();
2314
+ const platform = config?.platform ?? detectPlatform();
2315
+ const doc = new VirtualDocument(appId);
2316
+ doc.collector.setTransport(transport);
2317
+ transport.onMessage((message) => {
2318
+ if (isSystemMessage(message) && message.type === "debugQuery") {
2319
+ const debugMsg = message;
2320
+ let result = null;
2321
+ if (debugMsg.query === "tree") result = doc.toJSON();
2322
+ else if (debugMsg.query === "stats") result = doc.collector.getStats();
2323
+ else if (debugMsg.query === "pendingCount") result = doc.collector.pendingCount;
2324
+ else if (debugMsg.query === "coalescedLog") result = doc.collector.getCoalescedLog();
2325
+ else if (debugMsg.query === "perTypeCoalesced") result = doc.collector.getPerTypeCoalesced();
2326
+ transport.send({
2327
+ type: "debugResult",
2328
+ query: debugMsg.query,
2329
+ result
2330
+ });
2331
+ return;
2332
+ }
2333
+ if (isSystemMessage(message) && message.type === "init" && "location" in message) {
2334
+ const initMsg = message;
2335
+ const initLoc = initMsg.location;
2336
+ if (initLoc) {
2337
+ location.href = initLoc.href;
2338
+ location.protocol = initLoc.protocol;
2339
+ location.hostname = initLoc.hostname;
2340
+ location.port = initLoc.port;
2341
+ location.host = initLoc.host;
2342
+ location.origin = initLoc.origin;
2343
+ location.pathname = initLoc.pathname;
2344
+ location.search = initLoc.search;
2345
+ location.hash = initLoc.hash;
2346
+ }
2347
+ if (initMsg.sharedBuffer) doc._syncChannel = SyncChannel.fromBuffer(initMsg.sharedBuffer);
2348
+ return;
2349
+ }
2350
+ if (isEventMessage(message)) {
2351
+ const eventMsg = message;
2352
+ const mutsBefore = doc.collector.totalAdded;
2353
+ const dispatchStart = performance.now();
2354
+ doc.dispatchEvent(eventMsg.listenerId, eventMsg.event);
2355
+ const dispatchMs = performance.now() - dispatchStart;
2356
+ const mutationCount = doc.collector.totalAdded - mutsBefore;
2357
+ const evt = eventMsg.event;
2358
+ const transportMs = evt.timeStamp != null ? dispatchStart - evt.timeStamp : void 0;
2359
+ transport.send({
2360
+ type: "eventTimingResult",
2361
+ listenerId: eventMsg.listenerId,
2362
+ eventType: evt.type ?? "",
2363
+ dispatchMs,
2364
+ mutationCount,
2365
+ transportMs: transportMs ?? 0
2366
+ });
2367
+ }
2368
+ });
2369
+ const cleanupErrorHandlers = platform.installErrorHandlers((message, error, filename, lineno, colno) => {
2370
+ const serializedError = {
2371
+ message,
2372
+ stack: error?.stack,
2373
+ name: error?.name,
2374
+ filename,
2375
+ lineno,
2376
+ colno
2377
+ };
2378
+ transport.send({
2379
+ type: "error",
2380
+ appId,
2381
+ error: serializedError
2382
+ });
2383
+ }, (reason) => {
2384
+ const serializedError = {
2385
+ message: reason instanceof Error ? reason.message : String(reason),
2386
+ stack: reason instanceof Error ? reason.stack : void 0,
2387
+ name: reason instanceof Error ? reason.name : "UnhandledRejection",
2388
+ isUnhandledRejection: true
2389
+ };
2390
+ transport.send({
2391
+ type: "error",
2392
+ appId,
2393
+ error: serializedError
2394
+ });
2395
+ });
2396
+ transport.send({
2397
+ type: "ready",
2398
+ appId
2399
+ });
2400
+ const perfEntriesInterval = setInterval(() => {
2401
+ if (typeof performance === "undefined" || !performance.getEntriesByType) return;
2402
+ const measures = performance.getEntriesByType("measure").filter((e) => e.name.startsWith("async-dom:"));
2403
+ if (measures.length === 0) return;
2404
+ const entries = measures.map((e) => ({
2405
+ name: e.name,
2406
+ startTime: e.startTime,
2407
+ duration: e.duration,
2408
+ entryType: e.entryType
2409
+ }));
2410
+ transport.send({
2411
+ type: "perfEntries",
2412
+ appId,
2413
+ entries
2414
+ });
2415
+ for (const e of measures) try {
2416
+ performance.clearMeasures(e.name);
2417
+ } catch {}
2418
+ }, 2e3);
2419
+ const cleanupBeforeUnload = platform.onBeforeUnload(() => clearInterval(perfEntriesInterval));
2420
+ const storagePrefix = `__async_dom_${appId}_`;
2421
+ const localStorage = new ScopedStorage(storagePrefix, "localStorage", () => doc._syncChannel, QueryType.WindowProperty);
2422
+ const sessionStorage = new ScopedStorage(`${storagePrefix}session_`, "sessionStorage", () => null, QueryType.WindowProperty);
2423
+ function updateLocationFromURL(loc, url) {
2424
+ try {
2425
+ const parsed = new URL(url, loc.href);
2426
+ loc.href = parsed.href;
2427
+ loc.protocol = parsed.protocol;
2428
+ loc.hostname = parsed.hostname;
2429
+ loc.port = parsed.port;
2430
+ loc.host = parsed.host;
2431
+ loc.origin = parsed.origin;
2432
+ loc.pathname = parsed.pathname;
2433
+ loc.search = parsed.search;
2434
+ loc.hash = parsed.hash;
2435
+ } catch {}
2436
+ }
2437
+ const location = {
2438
+ hash: "",
2439
+ href: "http://localhost/",
2440
+ port: "",
2441
+ host: "localhost",
2442
+ origin: "http://localhost",
2443
+ hostname: "localhost",
2444
+ pathname: "/",
2445
+ protocol: "http:",
2446
+ search: "",
2447
+ toString() {
2448
+ return this.href;
2449
+ },
2450
+ assign(url) {
2451
+ updateLocationFromURL(location, url);
2452
+ doc.collector.add({
2453
+ action: "pushState",
2454
+ state: null,
2455
+ title: "",
2456
+ url
2457
+ });
2458
+ },
2459
+ replace(url) {
2460
+ updateLocationFromURL(location, url);
2461
+ doc.collector.add({
2462
+ action: "replaceState",
2463
+ state: null,
2464
+ title: "",
2465
+ url
2466
+ });
2467
+ },
2468
+ reload() {}
2469
+ };
2470
+ const history = {
2471
+ state: null,
2472
+ length: 1,
2473
+ pushState(state, title, url) {
2474
+ history.state = state;
2475
+ updateLocationFromURL(location, url);
2476
+ doc.collector.add({
2477
+ action: "pushState",
2478
+ state,
2479
+ title,
2480
+ url
2481
+ });
2482
+ },
2483
+ replaceState(state, title, url) {
2484
+ history.state = state;
2485
+ updateLocationFromURL(location, url);
2486
+ doc.collector.add({
2487
+ action: "replaceState",
2488
+ state,
2489
+ title,
2490
+ url
2491
+ });
2492
+ },
2493
+ back() {},
2494
+ forward() {},
2495
+ go(_delta) {}
2496
+ };
2497
+ const win = {
2498
+ document: doc,
2499
+ location,
2500
+ history,
2501
+ screen: {
2502
+ get width() {
2503
+ if (doc._syncChannel) {
2504
+ const result = doc._syncChannel.request(QueryType.WindowProperty, JSON.stringify({ property: "screen.width" }));
2505
+ if (typeof result === "number") return result;
2506
+ }
2507
+ return 1280;
2508
+ },
2509
+ get height() {
2510
+ if (doc._syncChannel) {
2511
+ const result = doc._syncChannel.request(QueryType.WindowProperty, JSON.stringify({ property: "screen.height" }));
2512
+ if (typeof result === "number") return result;
2513
+ }
2514
+ return 720;
2515
+ }
2516
+ },
2517
+ innerWidth: 1280,
2518
+ innerHeight: 720,
2519
+ localStorage,
2520
+ sessionStorage,
2521
+ addEventListener(name, callback) {
2522
+ doc.addEventListener(name, callback);
2523
+ },
2524
+ removeEventListener(name, callback) {
2525
+ doc.removeEventListener(name, callback);
2526
+ },
2527
+ scrollTo(x, y) {
2528
+ doc.collector.add({
2529
+ action: "scrollTo",
2530
+ x,
2531
+ y
2532
+ });
2533
+ },
2534
+ getComputedStyle(el) {
2535
+ if (doc._syncChannel && el && typeof el === "object" && "_nodeId" in el) {
2536
+ const result = doc._syncChannel.request(QueryType.ComputedStyle, JSON.stringify({ nodeId: el._nodeId }));
2537
+ if (result && typeof result === "object") return result;
2538
+ }
2539
+ return {};
2540
+ },
2541
+ requestAnimationFrame(cb) {
2542
+ return setTimeout(() => cb(performance.now()), 16);
2543
+ },
2544
+ cancelAnimationFrame(id) {
2545
+ clearTimeout(id);
2546
+ },
2547
+ MutationObserver: VirtualMutationObserver,
2548
+ ResizeObserver: VirtualResizeObserver,
2549
+ IntersectionObserver: VirtualIntersectionObserver,
2550
+ setTimeout,
2551
+ setInterval,
2552
+ clearTimeout,
2553
+ clearInterval,
2554
+ queueMicrotask,
2555
+ performance,
2556
+ fetch: typeof fetch !== "undefined" ? fetch : void 0,
2557
+ URL,
2558
+ URLSearchParams,
2559
+ console,
2560
+ btoa,
2561
+ atob,
2562
+ navigator: platform.navigator,
2563
+ Event: VirtualEvent,
2564
+ CustomEvent: VirtualCustomEvent,
2565
+ Node: {
2566
+ ELEMENT_NODE: 1,
2567
+ TEXT_NODE: 3,
2568
+ COMMENT_NODE: 8,
2569
+ DOCUMENT_NODE: 9,
2570
+ DOCUMENT_FRAGMENT_NODE: 11
2571
+ },
2572
+ HTMLElement: VirtualElement,
2573
+ devicePixelRatio: 1,
2574
+ matchMedia: (query) => ({
2575
+ matches: false,
2576
+ media: query,
2577
+ addEventListener() {},
2578
+ removeEventListener() {}
2579
+ }),
2580
+ getSelection: () => ({
2581
+ rangeCount: 0,
2582
+ getRangeAt() {
2583
+ return null;
2584
+ },
2585
+ addRange() {},
2586
+ removeAllRanges() {}
2587
+ }),
2588
+ dispatchEvent: (event) => {
2589
+ doc.dispatchEvent("", event);
2590
+ return true;
2591
+ },
2592
+ eval: (_code) => {
2593
+ throw new Error("sandbox eval is not enabled — set sandbox: true or sandbox: 'eval'");
2594
+ }
2595
+ };
2596
+ Object.defineProperties(win, {
2597
+ innerWidth: {
2598
+ get() {
2599
+ if (doc._syncChannel) {
2600
+ const result = doc._syncChannel.request(QueryType.WindowProperty, JSON.stringify({ property: "innerWidth" }));
2601
+ if (typeof result === "number") return result;
2602
+ }
2603
+ return 1280;
2604
+ },
2605
+ configurable: true
2606
+ },
2607
+ innerHeight: {
2608
+ get() {
2609
+ if (doc._syncChannel) {
2610
+ const result = doc._syncChannel.request(QueryType.WindowProperty, JSON.stringify({ property: "innerHeight" }));
2611
+ if (typeof result === "number") return result;
2612
+ }
2613
+ return 720;
2614
+ },
2615
+ configurable: true
2616
+ }
2617
+ });
2618
+ const sandboxMode = config?.sandbox;
2619
+ if (sandboxMode === "eval" || sandboxMode === true) win.eval = (code) => {
2620
+ const sandbox = new Proxy(win, {
2621
+ has() {
2622
+ return true;
2623
+ },
2624
+ get(target, prop) {
2625
+ if (prop === Symbol.unscopables) return void 0;
2626
+ if (prop in target) return target[prop];
2627
+ if (prop in globalThis) return globalThis[prop];
2628
+ },
2629
+ set(target, prop, value) {
2630
+ target[prop] = value;
2631
+ return true;
2632
+ }
2633
+ });
2634
+ return new Function("window", "self", "globalThis", "document", `with(window) {\n\t\t\t\treturn (function() { ${code} }).call(window);\n\t\t\t}`)(sandbox, sandbox, sandbox, doc);
2635
+ };
2636
+ if (sandboxMode === "global" || sandboxMode === true) {
2637
+ const workerGlobal = globalThis;
2638
+ workerGlobal.document = doc;
2639
+ workerGlobal.window = win;
2640
+ workerGlobal.location = win.location;
2641
+ workerGlobal.history = win.history;
2642
+ workerGlobal.navigator = win.navigator;
2643
+ workerGlobal.screen = win.screen;
2644
+ workerGlobal.localStorage = win.localStorage;
2645
+ workerGlobal.sessionStorage = win.sessionStorage;
2646
+ workerGlobal.getComputedStyle = win.getComputedStyle.bind(win);
2647
+ workerGlobal.requestAnimationFrame = win.requestAnimationFrame.bind(win);
2648
+ workerGlobal.cancelAnimationFrame = win.cancelAnimationFrame.bind(win);
2649
+ workerGlobal.scrollTo = win.scrollTo.bind(win);
2650
+ workerGlobal.matchMedia = win.matchMedia;
2651
+ workerGlobal.getSelection = win.getSelection;
2652
+ workerGlobal.dispatchEvent = win.dispatchEvent;
2653
+ workerGlobal.MutationObserver = win.MutationObserver;
2654
+ workerGlobal.ResizeObserver = win.ResizeObserver;
2655
+ workerGlobal.IntersectionObserver = win.IntersectionObserver;
2656
+ workerGlobal.Event = win.Event;
2657
+ workerGlobal.CustomEvent = win.CustomEvent;
2658
+ workerGlobal.Node = win.Node;
2659
+ workerGlobal.HTMLElement = win.HTMLElement;
2660
+ workerGlobal.devicePixelRatio = win.devicePixelRatio;
2661
+ const innerWidthDesc = Object.getOwnPropertyDescriptor(win, "innerWidth");
2662
+ const innerHeightDesc = Object.getOwnPropertyDescriptor(win, "innerHeight");
2663
+ if (innerWidthDesc) Object.defineProperty(workerGlobal, "innerWidth", innerWidthDesc);
2664
+ if (innerHeightDesc) Object.defineProperty(workerGlobal, "innerHeight", innerHeightDesc);
2665
+ }
2666
+ doc._defaultView = win;
2667
+ if (config?.debug?.exposeDevtools) globalThis.__ASYNC_DOM_DEVTOOLS__ = {
2668
+ document: doc,
2669
+ tree: () => doc.toJSON(),
2670
+ findNode: (id) => doc.getElementById(id) ?? doc.querySelector(`[id="${id}"]`),
2671
+ stats: () => doc.collector.getStats(),
2672
+ mutations: () => ({ pending: doc.collector.pendingCount }),
2673
+ flush: () => doc.collector.flushSync()
2674
+ };
2675
+ if (config?.debug?.logMutations) resolveDebugHooks(config.debug);
2676
+ function destroy() {
2677
+ doc.destroy();
2678
+ clearInterval(perfEntriesInterval);
2679
+ cleanupErrorHandlers();
2680
+ cleanupBeforeUnload();
2681
+ transport.close();
2682
+ }
2683
+ return {
2684
+ document: doc,
2685
+ window: win,
2686
+ destroy
2687
+ };
2688
+ }
2689
+ //#endregion
2690
+ export { VirtualCommentNode as a, createNodePlatform as c, MutationCollector as i, createWorkerPlatform as l, ScopedStorage as n, VirtualElement as o, VirtualDocument as r, VirtualTextNode as s, createWorkerDom as t, detectPlatform as u };
2691
+
2692
+ //# sourceMappingURL=worker-thread.js.map