@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,954 @@
1
+ //#region src/core/constants.ts
2
+ /** Queue size at which all actions are force-applied */
3
+ const CRITICAL_QUEUE_SIZE = 1500;
4
+ /** Queue size that triggers increased actions-per-frame */
5
+ const MAX_QUEUE_BEFORE_FLUSH = 3e3;
6
+ //#endregion
7
+ //#region src/core/binary-codec.ts
8
+ /**
9
+ * Opcodes for mutation actions. Each maps to a DomMutation action type.
10
+ * Using plain numeric constants since `const enum` is not compatible
11
+ * with isolatedModules / verbatimModuleSyntax.
12
+ */
13
+ const MutOp = {
14
+ CreateNode: 0,
15
+ CreateComment: 1,
16
+ AppendChild: 2,
17
+ RemoveNode: 3,
18
+ RemoveChild: 4,
19
+ InsertBefore: 5,
20
+ SetAttribute: 6,
21
+ RemoveAttribute: 7,
22
+ SetStyle: 8,
23
+ SetProperty: 9,
24
+ SetTextContent: 10,
25
+ SetClassName: 11,
26
+ SetHTML: 12,
27
+ AddEventListener: 13,
28
+ HeadAppendChild: 14,
29
+ BodyAppendChild: 15,
30
+ PushState: 16,
31
+ ReplaceState: 17,
32
+ ScrollTo: 18,
33
+ InsertAdjacentHTML: 19,
34
+ ConfigureEvent: 20,
35
+ RemoveEventListener: 21,
36
+ CallMethod: 22
37
+ };
38
+ /**
39
+ * Encodes DomMutation objects into a compact binary format using DataView.
40
+ *
41
+ * Wire format per mutation:
42
+ * - uint8 opcode (1 byte)
43
+ * - uint32 for NodeIds (4 bytes each, little-endian)
44
+ * - uint16 for string store indices (2 bytes each, little-endian)
45
+ * - uint8 for booleans (1 byte)
46
+ *
47
+ * Strings are deduplicated via a shared StringStore — only their uint16
48
+ * index is written to the buffer.
49
+ */
50
+ var BinaryMutationEncoder = class {
51
+ buffer;
52
+ view;
53
+ offset = 0;
54
+ strings;
55
+ constructor(strings, initialSize = 4096) {
56
+ this.buffer = new ArrayBuffer(initialSize);
57
+ this.view = new DataView(this.buffer);
58
+ this.strings = strings;
59
+ }
60
+ ensureCapacity(bytes) {
61
+ if (this.offset + bytes <= this.buffer.byteLength) return;
62
+ const newSize = Math.max(this.buffer.byteLength * 2, this.offset + bytes);
63
+ const newBuffer = new ArrayBuffer(newSize);
64
+ new Uint8Array(newBuffer).set(new Uint8Array(this.buffer));
65
+ this.buffer = newBuffer;
66
+ this.view = new DataView(this.buffer);
67
+ }
68
+ writeU8(value) {
69
+ this.ensureCapacity(1);
70
+ this.view.setUint8(this.offset++, value);
71
+ }
72
+ writeU16(value) {
73
+ this.ensureCapacity(2);
74
+ this.view.setUint16(this.offset, value, true);
75
+ this.offset += 2;
76
+ }
77
+ writeU32(value) {
78
+ this.ensureCapacity(4);
79
+ this.view.setUint32(this.offset, value, true);
80
+ this.offset += 4;
81
+ }
82
+ writeStr(value) {
83
+ this.writeU16(this.strings.store(value));
84
+ }
85
+ writeNodeId(id) {
86
+ this.writeU32(id);
87
+ }
88
+ encode(mutation) {
89
+ switch (mutation.action) {
90
+ case "createNode":
91
+ this.writeU8(MutOp.CreateNode);
92
+ this.writeNodeId(mutation.id);
93
+ this.writeStr(mutation.tag);
94
+ this.writeStr(mutation.textContent ?? "");
95
+ break;
96
+ case "createComment":
97
+ this.writeU8(MutOp.CreateComment);
98
+ this.writeNodeId(mutation.id);
99
+ this.writeStr(mutation.textContent);
100
+ break;
101
+ case "appendChild":
102
+ this.writeU8(MutOp.AppendChild);
103
+ this.writeNodeId(mutation.id);
104
+ this.writeNodeId(mutation.childId);
105
+ break;
106
+ case "removeNode":
107
+ this.writeU8(MutOp.RemoveNode);
108
+ this.writeNodeId(mutation.id);
109
+ break;
110
+ case "removeChild":
111
+ this.writeU8(MutOp.RemoveChild);
112
+ this.writeNodeId(mutation.id);
113
+ this.writeNodeId(mutation.childId);
114
+ break;
115
+ case "insertBefore":
116
+ this.writeU8(MutOp.InsertBefore);
117
+ this.writeNodeId(mutation.id);
118
+ this.writeNodeId(mutation.newId);
119
+ this.writeU32(mutation.refId !== null ? mutation.refId : 4294967295);
120
+ break;
121
+ case "setAttribute":
122
+ this.writeU8(MutOp.SetAttribute);
123
+ this.writeNodeId(mutation.id);
124
+ this.writeStr(mutation.name);
125
+ this.writeStr(mutation.value);
126
+ this.writeU8(mutation.optional ? 1 : 0);
127
+ break;
128
+ case "removeAttribute":
129
+ this.writeU8(MutOp.RemoveAttribute);
130
+ this.writeNodeId(mutation.id);
131
+ this.writeStr(mutation.name);
132
+ break;
133
+ case "setStyle":
134
+ this.writeU8(MutOp.SetStyle);
135
+ this.writeNodeId(mutation.id);
136
+ this.writeStr(mutation.property);
137
+ this.writeStr(mutation.value);
138
+ this.writeU8(mutation.optional ? 1 : 0);
139
+ break;
140
+ case "setProperty":
141
+ this.writeU8(MutOp.SetProperty);
142
+ this.writeNodeId(mutation.id);
143
+ this.writeStr(mutation.property);
144
+ this.writeStr(JSON.stringify(mutation.value));
145
+ break;
146
+ case "setTextContent":
147
+ this.writeU8(MutOp.SetTextContent);
148
+ this.writeNodeId(mutation.id);
149
+ this.writeStr(mutation.textContent);
150
+ break;
151
+ case "setClassName":
152
+ this.writeU8(MutOp.SetClassName);
153
+ this.writeNodeId(mutation.id);
154
+ this.writeStr(mutation.name);
155
+ break;
156
+ case "setHTML":
157
+ this.writeU8(MutOp.SetHTML);
158
+ this.writeNodeId(mutation.id);
159
+ this.writeStr(mutation.html);
160
+ break;
161
+ case "addEventListener":
162
+ this.writeU8(MutOp.AddEventListener);
163
+ this.writeNodeId(mutation.id);
164
+ this.writeStr(mutation.name);
165
+ this.writeStr(mutation.listenerId);
166
+ break;
167
+ case "headAppendChild":
168
+ this.writeU8(MutOp.HeadAppendChild);
169
+ this.writeNodeId(mutation.id);
170
+ break;
171
+ case "bodyAppendChild":
172
+ this.writeU8(MutOp.BodyAppendChild);
173
+ this.writeNodeId(mutation.id);
174
+ break;
175
+ case "pushState":
176
+ this.writeU8(MutOp.PushState);
177
+ this.writeStr(JSON.stringify(mutation.state));
178
+ this.writeStr(mutation.title);
179
+ this.writeStr(mutation.url);
180
+ break;
181
+ case "replaceState":
182
+ this.writeU8(MutOp.ReplaceState);
183
+ this.writeStr(JSON.stringify(mutation.state));
184
+ this.writeStr(mutation.title);
185
+ this.writeStr(mutation.url);
186
+ break;
187
+ case "scrollTo":
188
+ this.writeU8(MutOp.ScrollTo);
189
+ this.writeU32(mutation.x);
190
+ this.writeU32(mutation.y);
191
+ break;
192
+ case "insertAdjacentHTML":
193
+ this.writeU8(MutOp.InsertAdjacentHTML);
194
+ this.writeNodeId(mutation.id);
195
+ this.writeStr(mutation.position);
196
+ this.writeStr(mutation.html);
197
+ break;
198
+ case "configureEvent":
199
+ this.writeU8(MutOp.ConfigureEvent);
200
+ this.writeNodeId(mutation.id);
201
+ this.writeStr(mutation.name);
202
+ this.writeU8(mutation.preventDefault ? 1 : 0);
203
+ this.writeU8(mutation.passive ? 1 : 0);
204
+ break;
205
+ case "removeEventListener":
206
+ this.writeU8(MutOp.RemoveEventListener);
207
+ this.writeNodeId(mutation.id);
208
+ this.writeStr(mutation.listenerId);
209
+ break;
210
+ case "callMethod":
211
+ this.writeU8(MutOp.CallMethod);
212
+ this.writeNodeId(mutation.id);
213
+ this.writeStr(mutation.method);
214
+ this.writeStr(JSON.stringify(mutation.args));
215
+ break;
216
+ }
217
+ }
218
+ /**
219
+ * Returns a trimmed copy of the internal buffer containing all encoded mutations.
220
+ */
221
+ finish() {
222
+ return this.buffer.slice(0, this.offset);
223
+ }
224
+ /**
225
+ * Reset the write offset so the encoder can be reused for the next batch.
226
+ */
227
+ reset() {
228
+ this.offset = 0;
229
+ }
230
+ };
231
+ /**
232
+ * Decodes a binary buffer produced by BinaryMutationEncoder back into
233
+ * DomMutation objects. Requires a synchronized StringStore to resolve
234
+ * string indices.
235
+ */
236
+ var BinaryMutationDecoder = class {
237
+ view;
238
+ offset = 0;
239
+ strings;
240
+ constructor(strings) {
241
+ this.strings = strings;
242
+ }
243
+ readU8() {
244
+ if (this.offset + 1 > this.view.byteLength) throw new Error("Binary decode: unexpected end of buffer");
245
+ return this.view.getUint8(this.offset++);
246
+ }
247
+ readU16() {
248
+ if (this.offset + 2 > this.view.byteLength) throw new Error("Binary decode: unexpected end of buffer");
249
+ const v = this.view.getUint16(this.offset, true);
250
+ this.offset += 2;
251
+ return v;
252
+ }
253
+ readU32() {
254
+ if (this.offset + 4 > this.view.byteLength) throw new Error("Binary decode: unexpected end of buffer");
255
+ const v = this.view.getUint32(this.offset, true);
256
+ this.offset += 4;
257
+ return v;
258
+ }
259
+ readStr() {
260
+ return this.strings.get(this.readU16());
261
+ }
262
+ readNodeId() {
263
+ return this.readU32();
264
+ }
265
+ decode(buffer) {
266
+ this.view = new DataView(buffer);
267
+ this.offset = 0;
268
+ const mutations = [];
269
+ while (this.offset < buffer.byteLength) {
270
+ const op = this.readU8();
271
+ mutations.push(this.decodeMutation(op));
272
+ }
273
+ return mutations;
274
+ }
275
+ decodeMutation(op) {
276
+ switch (op) {
277
+ case MutOp.CreateNode: {
278
+ const id = this.readNodeId();
279
+ const tag = this.readStr();
280
+ const textContent = this.readStr();
281
+ return {
282
+ action: "createNode",
283
+ id,
284
+ tag,
285
+ ...textContent ? { textContent } : {}
286
+ };
287
+ }
288
+ case MutOp.CreateComment: return {
289
+ action: "createComment",
290
+ id: this.readNodeId(),
291
+ textContent: this.readStr()
292
+ };
293
+ case MutOp.AppendChild: return {
294
+ action: "appendChild",
295
+ id: this.readNodeId(),
296
+ childId: this.readNodeId()
297
+ };
298
+ case MutOp.RemoveNode: return {
299
+ action: "removeNode",
300
+ id: this.readNodeId()
301
+ };
302
+ case MutOp.RemoveChild: return {
303
+ action: "removeChild",
304
+ id: this.readNodeId(),
305
+ childId: this.readNodeId()
306
+ };
307
+ case MutOp.InsertBefore: {
308
+ const id = this.readNodeId();
309
+ const newId = this.readNodeId();
310
+ const refRaw = this.readU32();
311
+ return {
312
+ action: "insertBefore",
313
+ id,
314
+ newId,
315
+ refId: refRaw === 4294967295 ? null : refRaw
316
+ };
317
+ }
318
+ case MutOp.SetAttribute: {
319
+ const id = this.readNodeId();
320
+ const name = this.readStr();
321
+ const value = this.readStr();
322
+ const optional = this.readU8() === 1;
323
+ return {
324
+ action: "setAttribute",
325
+ id,
326
+ name,
327
+ value,
328
+ ...optional ? { optional } : {}
329
+ };
330
+ }
331
+ case MutOp.RemoveAttribute: return {
332
+ action: "removeAttribute",
333
+ id: this.readNodeId(),
334
+ name: this.readStr()
335
+ };
336
+ case MutOp.SetStyle: {
337
+ const id = this.readNodeId();
338
+ const property = this.readStr();
339
+ const value = this.readStr();
340
+ const optional = this.readU8() === 1;
341
+ return {
342
+ action: "setStyle",
343
+ id,
344
+ property,
345
+ value,
346
+ ...optional ? { optional } : {}
347
+ };
348
+ }
349
+ case MutOp.SetProperty: {
350
+ const id = this.readNodeId();
351
+ const property = this.readStr();
352
+ const valueStr = this.readStr();
353
+ return {
354
+ action: "setProperty",
355
+ id,
356
+ property,
357
+ value: JSON.parse(valueStr)
358
+ };
359
+ }
360
+ case MutOp.SetTextContent: return {
361
+ action: "setTextContent",
362
+ id: this.readNodeId(),
363
+ textContent: this.readStr()
364
+ };
365
+ case MutOp.SetClassName: return {
366
+ action: "setClassName",
367
+ id: this.readNodeId(),
368
+ name: this.readStr()
369
+ };
370
+ case MutOp.SetHTML: return {
371
+ action: "setHTML",
372
+ id: this.readNodeId(),
373
+ html: this.readStr()
374
+ };
375
+ case MutOp.AddEventListener: return {
376
+ action: "addEventListener",
377
+ id: this.readNodeId(),
378
+ name: this.readStr(),
379
+ listenerId: this.readStr()
380
+ };
381
+ case MutOp.HeadAppendChild: return {
382
+ action: "headAppendChild",
383
+ id: this.readNodeId()
384
+ };
385
+ case MutOp.BodyAppendChild: return {
386
+ action: "bodyAppendChild",
387
+ id: this.readNodeId()
388
+ };
389
+ case MutOp.PushState: return {
390
+ action: "pushState",
391
+ state: JSON.parse(this.readStr()),
392
+ title: this.readStr(),
393
+ url: this.readStr()
394
+ };
395
+ case MutOp.ReplaceState: return {
396
+ action: "replaceState",
397
+ state: JSON.parse(this.readStr()),
398
+ title: this.readStr(),
399
+ url: this.readStr()
400
+ };
401
+ case MutOp.ScrollTo: return {
402
+ action: "scrollTo",
403
+ x: this.readU32(),
404
+ y: this.readU32()
405
+ };
406
+ case MutOp.InsertAdjacentHTML: return {
407
+ action: "insertAdjacentHTML",
408
+ id: this.readNodeId(),
409
+ position: this.readStr(),
410
+ html: this.readStr()
411
+ };
412
+ case MutOp.ConfigureEvent: {
413
+ const id = this.readNodeId();
414
+ const name = this.readStr();
415
+ const preventDefault = this.readU8() === 1;
416
+ const passive = this.readU8() === 1;
417
+ return {
418
+ action: "configureEvent",
419
+ id,
420
+ name,
421
+ preventDefault,
422
+ ...passive ? { passive } : {}
423
+ };
424
+ }
425
+ case MutOp.RemoveEventListener: return {
426
+ action: "removeEventListener",
427
+ id: this.readNodeId(),
428
+ listenerId: this.readStr()
429
+ };
430
+ case MutOp.CallMethod: {
431
+ const id = this.readNodeId();
432
+ const method = this.readStr();
433
+ const argsStr = this.readStr();
434
+ return {
435
+ action: "callMethod",
436
+ id,
437
+ method,
438
+ args: JSON.parse(argsStr)
439
+ };
440
+ }
441
+ default: throw new Error(`Unknown mutation opcode: ${op}`);
442
+ }
443
+ }
444
+ };
445
+ //#endregion
446
+ //#region src/core/string-store.ts
447
+ /**
448
+ * Bidirectional string-to-index store for wire format deduplication.
449
+ * Strings are assigned monotonic uint16 indices on first encounter.
450
+ * Both worker and main thread maintain synchronized copies.
451
+ */
452
+ var StringStore = class {
453
+ stringToIndex = /* @__PURE__ */ new Map();
454
+ indexToString = [];
455
+ pending = [];
456
+ /**
457
+ * Get or assign an index for a string. New strings are tracked as pending.
458
+ */
459
+ store(value) {
460
+ const existing = this.stringToIndex.get(value);
461
+ if (existing !== void 0) return existing;
462
+ const index = this.indexToString.length;
463
+ this.stringToIndex.set(value, index);
464
+ this.indexToString.push(value);
465
+ this.pending.push(value);
466
+ return index;
467
+ }
468
+ /**
469
+ * Get string by index.
470
+ */
471
+ get(index) {
472
+ return this.indexToString[index] ?? "";
473
+ }
474
+ /**
475
+ * Consume pending new strings (for sending to the other side).
476
+ */
477
+ consumePending() {
478
+ const p = this.pending;
479
+ this.pending = [];
480
+ return p;
481
+ }
482
+ /**
483
+ * Register strings from the other side (no pending tracking).
484
+ */
485
+ registerBulk(strings) {
486
+ for (const s of strings) if (!this.stringToIndex.has(s)) {
487
+ const index = this.indexToString.length;
488
+ this.stringToIndex.set(s, index);
489
+ this.indexToString.push(s);
490
+ }
491
+ }
492
+ get size() {
493
+ return this.indexToString.length;
494
+ }
495
+ };
496
+ //#endregion
497
+ //#region src/transport/binary-worker-transport.ts
498
+ const encoder = new TextEncoder();
499
+ const decoder = new TextDecoder();
500
+ /**
501
+ * Returns true if the value is an ArrayBuffer (or ArrayBuffer-like from Uint8Array.buffer).
502
+ */
503
+ function isArrayBuffer(value) {
504
+ return value instanceof ArrayBuffer || typeof value === "object" && value !== null && "byteLength" in value && "slice" in value && typeof value.slice === "function" && !ArrayBuffer.isView(value);
505
+ }
506
+ /**
507
+ * Marker byte for binary mutation messages.
508
+ * Used to distinguish binary-encoded mutations from legacy JSON-in-ArrayBuffer.
509
+ */
510
+ const BINARY_MUTATION_MARKER = 2;
511
+ /**
512
+ * Returns true if the incoming data is a binary mutation message (has the marker byte).
513
+ */
514
+ function isBinaryMutationMessage(data) {
515
+ if (data.byteLength < 1) return false;
516
+ return new DataView(data).getUint8(0) === BINARY_MUTATION_MARKER;
517
+ }
518
+ /**
519
+ * Encode a Message as a Transferable ArrayBuffer (legacy JSON format).
520
+ * Used for non-mutation messages.
521
+ */
522
+ function encodeBinaryMessage(message) {
523
+ const json = JSON.stringify(message);
524
+ const bytes = encoder.encode(json);
525
+ const buffer = new ArrayBuffer(bytes.byteLength);
526
+ new Uint8Array(buffer).set(bytes);
527
+ return buffer;
528
+ }
529
+ /**
530
+ * Decode a Message from an ArrayBuffer (inverse of encodeBinaryMessage).
531
+ */
532
+ function decodeBinaryMessage(buffer) {
533
+ return JSON.parse(decoder.decode(buffer));
534
+ }
535
+ /**
536
+ * Returns true if the message should be sent as a Transferable ArrayBuffer.
537
+ * Only mutation messages benefit from zero-copy transfer since they are
538
+ * the most frequent and largest messages.
539
+ */
540
+ function shouldUseBinaryTransfer(message) {
541
+ return message.type === "mutation";
542
+ }
543
+ const textEncoder = new TextEncoder();
544
+ const textDecoder = new TextDecoder();
545
+ /**
546
+ * Encode a binary mutation message with string table preamble.
547
+ *
548
+ * Wire format:
549
+ * [uint8: 0x02 marker]
550
+ * [uint32: uid]
551
+ * [uint16: appId byte length] [UTF-8 bytes: appId]
552
+ * [uint8: priority (0=normal, 1=high, 2=low)]
553
+ * [uint16: newStringCount]
554
+ * [...newStrings: each is uint16 byteLength + UTF-8 bytes]
555
+ * [...binaryMutations: the encoded buffer from BinaryMutationEncoder]
556
+ */
557
+ function encodeBinaryMutationMessage(message, strings, mutEncoder) {
558
+ mutEncoder.reset();
559
+ for (const mut of message.mutations) mutEncoder.encode(mut);
560
+ const mutBuffer = mutEncoder.finish();
561
+ const newStrings = strings.consumePending();
562
+ const appIdBytes = textEncoder.encode(message.appId);
563
+ let headerSize = 7 + appIdBytes.byteLength + 1 + 2;
564
+ const encodedStrings = [];
565
+ for (const s of newStrings) {
566
+ const encoded = textEncoder.encode(s);
567
+ encodedStrings.push(encoded);
568
+ headerSize += 2 + encoded.byteLength;
569
+ }
570
+ const totalSize = headerSize + mutBuffer.byteLength;
571
+ const buffer = new ArrayBuffer(totalSize);
572
+ const view = new DataView(buffer);
573
+ const bytes = new Uint8Array(buffer);
574
+ let offset = 0;
575
+ view.setUint8(offset++, BINARY_MUTATION_MARKER);
576
+ view.setUint32(offset, message.uid, true);
577
+ offset += 4;
578
+ view.setUint16(offset, appIdBytes.byteLength, true);
579
+ offset += 2;
580
+ bytes.set(appIdBytes, offset);
581
+ offset += appIdBytes.byteLength;
582
+ view.setUint8(offset++, {
583
+ normal: 0,
584
+ high: 1,
585
+ low: 2
586
+ }[message.priority ?? "normal"]);
587
+ view.setUint16(offset, newStrings.length, true);
588
+ offset += 2;
589
+ for (const encoded of encodedStrings) {
590
+ view.setUint16(offset, encoded.byteLength, true);
591
+ offset += 2;
592
+ bytes.set(encoded, offset);
593
+ offset += encoded.byteLength;
594
+ }
595
+ bytes.set(new Uint8Array(mutBuffer), offset);
596
+ return buffer;
597
+ }
598
+ /**
599
+ * Decode a binary mutation message from the wire format.
600
+ */
601
+ function decodeBinaryMutationMessage(buffer, strings, mutDecoder) {
602
+ const view = new DataView(buffer);
603
+ const bytes = new Uint8Array(buffer);
604
+ let offset = 0;
605
+ offset += 1;
606
+ const uid = view.getUint32(offset, true);
607
+ offset += 4;
608
+ const appIdLen = view.getUint16(offset, true);
609
+ offset += 2;
610
+ const appId = textDecoder.decode(bytes.slice(offset, offset + appIdLen));
611
+ offset += appIdLen;
612
+ const priority = [
613
+ "normal",
614
+ "high",
615
+ "low"
616
+ ][view.getUint8(offset++)] ?? "normal";
617
+ const newStringCount = view.getUint16(offset, true);
618
+ offset += 2;
619
+ const newStrings = [];
620
+ for (let i = 0; i < newStringCount; i++) {
621
+ const strLen = view.getUint16(offset, true);
622
+ offset += 2;
623
+ newStrings.push(textDecoder.decode(bytes.slice(offset, offset + strLen)));
624
+ offset += strLen;
625
+ }
626
+ strings.registerBulk(newStrings);
627
+ const mutPayload = buffer.slice(offset);
628
+ return {
629
+ type: "mutation",
630
+ appId,
631
+ uid,
632
+ mutations: mutDecoder.decode(mutPayload),
633
+ ...priority !== "normal" ? { priority } : {}
634
+ };
635
+ }
636
+ /**
637
+ * Worker transport that uses binary encoding for mutation messages.
638
+ *
639
+ * Mutation messages are encoded using BinaryMutationEncoder with string
640
+ * deduplication, providing ~10x smaller wire format compared to JSON.
641
+ * Non-mutation messages fall back to structured clone.
642
+ *
643
+ * Used on the main thread side to communicate with a dedicated worker.
644
+ */
645
+ var BinaryWorkerTransport = class {
646
+ handlers = [];
647
+ _readyState = "open";
648
+ strings = new StringStore();
649
+ mutDecoder = new BinaryMutationDecoder(this.strings);
650
+ _statsEnabled = false;
651
+ _stats = {
652
+ messageCount: 0,
653
+ totalBytes: 0,
654
+ largestMessageBytes: 0,
655
+ lastMessageBytes: 0
656
+ };
657
+ onError;
658
+ onClose;
659
+ constructor(worker) {
660
+ this.worker = worker;
661
+ worker.onmessage = (e) => {
662
+ if (this.handlers.length === 0) return;
663
+ let msg;
664
+ if (isArrayBuffer(e.data)) if (isBinaryMutationMessage(e.data)) msg = decodeBinaryMutationMessage(e.data, this.strings, this.mutDecoder);
665
+ else msg = decodeBinaryMessage(e.data);
666
+ else msg = e.data;
667
+ for (const h of this.handlers) try {
668
+ h(msg);
669
+ } catch (err) {
670
+ console.error("[async-dom] Handler error:", err);
671
+ }
672
+ };
673
+ worker.onerror = (e) => {
674
+ const error = new Error(e.message ?? "Worker error");
675
+ this.onError?.(error);
676
+ if (this._readyState !== "closed") {
677
+ this._readyState = "closed";
678
+ this.onClose?.();
679
+ }
680
+ };
681
+ worker.onmessageerror = () => {
682
+ const error = /* @__PURE__ */ new Error("Worker message deserialization failed");
683
+ this.onError?.(error);
684
+ };
685
+ }
686
+ enableStats(enabled) {
687
+ this._statsEnabled = enabled;
688
+ }
689
+ send(message) {
690
+ if (this._readyState !== "open") return;
691
+ if (shouldUseBinaryTransfer(message)) {
692
+ const buffer = encodeBinaryMessage(message);
693
+ if (this._statsEnabled) {
694
+ const bytes = buffer.byteLength;
695
+ this._stats.messageCount++;
696
+ this._stats.totalBytes += bytes;
697
+ this._stats.lastMessageBytes = bytes;
698
+ if (bytes > this._stats.largestMessageBytes) this._stats.largestMessageBytes = bytes;
699
+ }
700
+ this.worker.postMessage(buffer, [buffer]);
701
+ } else {
702
+ if (this._statsEnabled) {
703
+ const bytes = JSON.stringify(message).length;
704
+ this._stats.messageCount++;
705
+ this._stats.totalBytes += bytes;
706
+ this._stats.lastMessageBytes = bytes;
707
+ if (bytes > this._stats.largestMessageBytes) this._stats.largestMessageBytes = bytes;
708
+ }
709
+ this.worker.postMessage(message);
710
+ }
711
+ }
712
+ onMessage(handler) {
713
+ this.handlers.push(handler);
714
+ }
715
+ close() {
716
+ this._readyState = "closed";
717
+ this.worker.terminate();
718
+ }
719
+ get readyState() {
720
+ return this._readyState;
721
+ }
722
+ getStats() {
723
+ return { ...this._stats };
724
+ }
725
+ };
726
+ /**
727
+ * Worker-side binary transport (used inside the worker via self.postMessage).
728
+ *
729
+ * Mutation messages are encoded using BinaryMutationEncoder with string
730
+ * deduplication. The string table preamble is embedded in each message
731
+ * so the main thread can stay synchronized.
732
+ *
733
+ * Counterpart to BinaryWorkerTransport for use within the Web Worker.
734
+ */
735
+ var BinaryWorkerSelfTransport = class {
736
+ handlers = [];
737
+ _readyState = "open";
738
+ strings = new StringStore();
739
+ mutEncoder = new BinaryMutationEncoder(this.strings);
740
+ _statsEnabled = false;
741
+ _stats = {
742
+ messageCount: 0,
743
+ totalBytes: 0,
744
+ largestMessageBytes: 0,
745
+ lastMessageBytes: 0
746
+ };
747
+ onError;
748
+ onClose;
749
+ scope;
750
+ constructor(scope) {
751
+ this.scope = scope ?? self;
752
+ this.scope.onmessage = (e) => {
753
+ if (this.handlers.length === 0) return;
754
+ const msg = isArrayBuffer(e.data) ? decodeBinaryMessage(e.data) : e.data;
755
+ for (const h of this.handlers) try {
756
+ h(msg);
757
+ } catch (err) {
758
+ console.error("[async-dom] Handler error:", err);
759
+ }
760
+ };
761
+ }
762
+ enableStats(enabled) {
763
+ this._statsEnabled = enabled;
764
+ }
765
+ send(message) {
766
+ if (this._readyState !== "open") return;
767
+ if (shouldUseBinaryTransfer(message)) {
768
+ const buffer = encodeBinaryMutationMessage(message, this.strings, this.mutEncoder);
769
+ if (this._statsEnabled) {
770
+ const bytes = buffer.byteLength;
771
+ this._stats.messageCount++;
772
+ this._stats.totalBytes += bytes;
773
+ this._stats.lastMessageBytes = bytes;
774
+ if (bytes > this._stats.largestMessageBytes) this._stats.largestMessageBytes = bytes;
775
+ }
776
+ this.scope.postMessage(buffer, [buffer]);
777
+ } else {
778
+ if (this._statsEnabled) {
779
+ const bytes = JSON.stringify(message).length;
780
+ this._stats.messageCount++;
781
+ this._stats.totalBytes += bytes;
782
+ this._stats.lastMessageBytes = bytes;
783
+ if (bytes > this._stats.largestMessageBytes) this._stats.largestMessageBytes = bytes;
784
+ }
785
+ this.scope.postMessage(message);
786
+ }
787
+ }
788
+ onMessage(handler) {
789
+ this.handlers.push(handler);
790
+ }
791
+ close() {
792
+ this._readyState = "closed";
793
+ }
794
+ get readyState() {
795
+ return this._readyState;
796
+ }
797
+ getStats() {
798
+ return { ...this._stats };
799
+ }
800
+ };
801
+ //#endregion
802
+ //#region src/transport/ws-transport.ts
803
+ /**
804
+ * Transport implementation using WebSocket with automatic reconnection.
805
+ * Messages are queued while disconnected and flushed on reconnect.
806
+ */
807
+ var WebSocketTransport = class {
808
+ ws = null;
809
+ handlers = [];
810
+ _readyState = "connecting";
811
+ _stats = {
812
+ messageCount: 0,
813
+ totalBytes: 0,
814
+ largestMessageBytes: 0,
815
+ lastMessageBytes: 0
816
+ };
817
+ onError;
818
+ onClose;
819
+ attempt = 0;
820
+ messageQueue = [];
821
+ closed = false;
822
+ reconnectTimer = null;
823
+ maxRetries;
824
+ baseDelay;
825
+ maxDelay;
826
+ constructor(url, options) {
827
+ this.url = url;
828
+ this.maxRetries = options?.maxRetries ?? 10;
829
+ this.baseDelay = options?.baseDelay ?? 1e3;
830
+ this.maxDelay = options?.maxDelay ?? 3e4;
831
+ this.connect();
832
+ }
833
+ connect() {
834
+ if (this.closed) return;
835
+ this._readyState = "connecting";
836
+ this.ws = new WebSocket(this.url);
837
+ this.ws.onopen = () => {
838
+ this._readyState = "open";
839
+ this.attempt = 0;
840
+ this.flushQueue();
841
+ };
842
+ this.ws.onmessage = (e) => {
843
+ try {
844
+ const data = JSON.parse(e.data);
845
+ for (const h of this.handlers) try {
846
+ h(data);
847
+ } catch (err) {
848
+ console.error("[async-dom] Handler error:", err);
849
+ }
850
+ } catch {
851
+ console.error("[async-dom] Failed to parse WebSocket message");
852
+ }
853
+ };
854
+ this.ws.onclose = () => {
855
+ if (!this.closed) this.scheduleReconnect();
856
+ };
857
+ this.ws.onerror = () => {
858
+ this.ws?.close();
859
+ };
860
+ }
861
+ scheduleReconnect() {
862
+ if (this.attempt >= this.maxRetries) {
863
+ this._readyState = "closed";
864
+ console.error(`[async-dom] WebSocket reconnection failed after ${this.maxRetries} attempts`);
865
+ return;
866
+ }
867
+ const delay = Math.min(this.baseDelay * 2 ** this.attempt + Math.random() * 1e3, this.maxDelay);
868
+ this.attempt++;
869
+ this.reconnectTimer = setTimeout(() => {
870
+ this.connect();
871
+ }, delay);
872
+ }
873
+ flushQueue() {
874
+ while (this.messageQueue.length > 0) {
875
+ const msg = this.messageQueue.shift();
876
+ if (!msg) break;
877
+ this.sendRaw(msg);
878
+ }
879
+ }
880
+ sendRaw(message) {
881
+ const json = JSON.stringify(message);
882
+ const bytes = json.length;
883
+ this._stats.messageCount++;
884
+ this._stats.totalBytes += bytes;
885
+ this._stats.lastMessageBytes = bytes;
886
+ if (bytes > this._stats.largestMessageBytes) this._stats.largestMessageBytes = bytes;
887
+ this.ws?.send(json);
888
+ }
889
+ send(message) {
890
+ if (this._readyState === "open" && this.ws?.readyState === WebSocket.OPEN) this.sendRaw(message);
891
+ else if (this._readyState !== "closed") this.messageQueue.push(message);
892
+ }
893
+ onMessage(handler) {
894
+ this.handlers.push(handler);
895
+ }
896
+ close() {
897
+ this.closed = true;
898
+ this._readyState = "closed";
899
+ if (this.reconnectTimer !== null) clearTimeout(this.reconnectTimer);
900
+ this.ws?.close();
901
+ this.messageQueue.length = 0;
902
+ }
903
+ get readyState() {
904
+ return this._readyState;
905
+ }
906
+ getStats() {
907
+ return { ...this._stats };
908
+ }
909
+ };
910
+ //#endregion
911
+ Object.defineProperty(exports, "BinaryWorkerSelfTransport", {
912
+ enumerable: true,
913
+ get: function() {
914
+ return BinaryWorkerSelfTransport;
915
+ }
916
+ });
917
+ Object.defineProperty(exports, "BinaryWorkerTransport", {
918
+ enumerable: true,
919
+ get: function() {
920
+ return BinaryWorkerTransport;
921
+ }
922
+ });
923
+ Object.defineProperty(exports, "CRITICAL_QUEUE_SIZE", {
924
+ enumerable: true,
925
+ get: function() {
926
+ return CRITICAL_QUEUE_SIZE;
927
+ }
928
+ });
929
+ Object.defineProperty(exports, "MAX_QUEUE_BEFORE_FLUSH", {
930
+ enumerable: true,
931
+ get: function() {
932
+ return MAX_QUEUE_BEFORE_FLUSH;
933
+ }
934
+ });
935
+ Object.defineProperty(exports, "WebSocketTransport", {
936
+ enumerable: true,
937
+ get: function() {
938
+ return WebSocketTransport;
939
+ }
940
+ });
941
+ Object.defineProperty(exports, "decodeBinaryMessage", {
942
+ enumerable: true,
943
+ get: function() {
944
+ return decodeBinaryMessage;
945
+ }
946
+ });
947
+ Object.defineProperty(exports, "encodeBinaryMessage", {
948
+ enumerable: true,
949
+ get: function() {
950
+ return encodeBinaryMessage;
951
+ }
952
+ });
953
+
954
+ //# sourceMappingURL=ws-transport.cjs.map