ctrl-fx 0.1.0 → 0.1.1

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 (152) hide show
  1. package/README.md +13 -0
  2. package/dist/chunk-DZAR6PTR.js +3966 -0
  3. package/dist/chunk-KNHJPAIU.js +1800 -0
  4. package/dist/chunk-NSWOTCDU.js +916 -0
  5. package/dist/chunk-PKBMQBKP.js +7 -0
  6. package/dist/chunk-XICUXW4T.js +252 -0
  7. package/dist/db/index.d.ts +218 -16
  8. package/dist/db/index.js +53 -19
  9. package/dist/dom/index.d.ts +2 -248
  10. package/dist/dom/index.js +30 -266
  11. package/dist/effects.d.ts +2 -580
  12. package/dist/effects.js +111 -1556
  13. package/dist/index-fpCmXVEu.d.ts +1391 -0
  14. package/dist/index.d.ts +2 -41
  15. package/dist/index.js +18 -78
  16. package/dist/router.d.ts +11 -12
  17. package/dist/router.js +57 -68
  18. package/dist/testing.d.ts +66 -60
  19. package/dist/testing.js +39 -158
  20. package/dist/webcomponent.d.ts +7 -4
  21. package/dist/webcomponent.js +48 -40
  22. package/package.json +4 -3
  23. package/dist/clipboard.d.ts +0 -7
  24. package/dist/clipboard.d.ts.map +0 -1
  25. package/dist/clipboard.js +0 -3
  26. package/dist/db/effects.d.ts +0 -78
  27. package/dist/db/effects.d.ts.map +0 -1
  28. package/dist/db/effects.js +0 -98
  29. package/dist/db/errors.d.ts +0 -21
  30. package/dist/db/errors.d.ts.map +0 -1
  31. package/dist/db/errors.js +0 -13
  32. package/dist/db/index.d.ts.map +0 -1
  33. package/dist/db/keypath.d.ts +0 -40
  34. package/dist/db/keypath.d.ts.map +0 -1
  35. package/dist/db/keypath.js +0 -20
  36. package/dist/db/setup.d.ts +0 -83
  37. package/dist/db/setup.d.ts.map +0 -1
  38. package/dist/db/setup.js +0 -100
  39. package/dist/db/testdb.d.ts +0 -19
  40. package/dist/db/testdb.d.ts.map +0 -1
  41. package/dist/db/testdb.js +0 -34
  42. package/dist/dom/attrs.d.ts +0 -67
  43. package/dist/dom/attrs.d.ts.map +0 -1
  44. package/dist/dom/attrs.js +0 -154
  45. package/dist/dom/components.d.ts +0 -43
  46. package/dist/dom/components.d.ts.map +0 -1
  47. package/dist/dom/components.js +0 -86
  48. package/dist/dom/elems.d.ts +0 -121
  49. package/dist/dom/elems.d.ts.map +0 -1
  50. package/dist/dom/elems.js +0 -52
  51. package/dist/dom/events.d.ts +0 -232
  52. package/dist/dom/events.d.ts.map +0 -1
  53. package/dist/dom/events.js +0 -289
  54. package/dist/dom/index.d.ts.map +0 -1
  55. package/dist/dom/nodeid.d.ts +0 -9
  56. package/dist/dom/nodeid.d.ts.map +0 -1
  57. package/dist/dom/nodeid.js +0 -27
  58. package/dist/dom/svg.d.ts +0 -23
  59. package/dist/dom/svg.d.ts.map +0 -1
  60. package/dist/dom/svg.js +0 -1
  61. package/dist/dom/views.d.ts +0 -60
  62. package/dist/dom/views.d.ts.map +0 -1
  63. package/dist/dom/views.js +0 -154
  64. package/dist/effects.d.ts.map +0 -1
  65. package/dist/index.d.ts.map +0 -1
  66. package/dist/internal/clipboard.d.ts +0 -5
  67. package/dist/internal/clipboard.d.ts.map +0 -1
  68. package/dist/internal/clipboard.js +0 -33
  69. package/dist/internal/cmpmgr.d.ts +0 -46
  70. package/dist/internal/cmpmgr.d.ts.map +0 -1
  71. package/dist/internal/cmpmgr.js +0 -561
  72. package/dist/internal/dbmgr.d.ts +0 -11
  73. package/dist/internal/dbmgr.d.ts.map +0 -1
  74. package/dist/internal/dbmgr.js +0 -200
  75. package/dist/internal/dominterpreter.d.ts +0 -5
  76. package/dist/internal/dominterpreter.d.ts.map +0 -1
  77. package/dist/internal/dominterpreter.js +0 -337
  78. package/dist/internal/eventmgr.d.ts +0 -41
  79. package/dist/internal/eventmgr.d.ts.map +0 -1
  80. package/dist/internal/eventmgr.js +0 -696
  81. package/dist/internal/index.d.ts +0 -19
  82. package/dist/internal/index.d.ts.map +0 -1
  83. package/dist/internal/index.js +0 -38
  84. package/dist/internal/interpreter.d.ts +0 -10
  85. package/dist/internal/interpreter.d.ts.map +0 -1
  86. package/dist/internal/interpreter.js +0 -1
  87. package/dist/internal/locutils.d.ts +0 -4
  88. package/dist/internal/locutils.d.ts.map +0 -1
  89. package/dist/internal/locutils.js +0 -22
  90. package/dist/internal/net.d.ts +0 -4
  91. package/dist/internal/net.d.ts.map +0 -1
  92. package/dist/internal/net.js +0 -64
  93. package/dist/internal/realdom.d.ts +0 -249
  94. package/dist/internal/realdom.d.ts.map +0 -1
  95. package/dist/internal/realdom.js +0 -1
  96. package/dist/internal/styleregistry.d.ts +0 -6
  97. package/dist/internal/styleregistry.d.ts.map +0 -1
  98. package/dist/internal/styleregistry.js +0 -18
  99. package/dist/internal/taskreg.d.ts +0 -9
  100. package/dist/internal/taskreg.d.ts.map +0 -1
  101. package/dist/internal/taskreg.js +0 -28
  102. package/dist/internal/testing.d.ts +0 -12
  103. package/dist/internal/testing.d.ts.map +0 -1
  104. package/dist/internal/testing.js +0 -974
  105. package/dist/internal/testinginterpreter.d.ts +0 -10
  106. package/dist/internal/testinginterpreter.d.ts.map +0 -1
  107. package/dist/internal/testinginterpreter.js +0 -646
  108. package/dist/internal/vdom.d.ts +0 -93
  109. package/dist/internal/vdom.d.ts.map +0 -1
  110. package/dist/internal/vdom.js +0 -470
  111. package/dist/json/index.d.ts +0 -5
  112. package/dist/json/index.d.ts.map +0 -1
  113. package/dist/json/index.js +0 -2
  114. package/dist/json/validation.d.ts +0 -28
  115. package/dist/json/validation.d.ts.map +0 -1
  116. package/dist/json/validation.js +0 -8
  117. package/dist/net/fragment.d.ts +0 -10
  118. package/dist/net/fragment.d.ts.map +0 -1
  119. package/dist/net/fragment.js +0 -27
  120. package/dist/net/index.d.ts +0 -42
  121. package/dist/net/index.d.ts.map +0 -1
  122. package/dist/net/index.js +0 -60
  123. package/dist/net/location.d.ts +0 -21
  124. package/dist/net/location.d.ts.map +0 -1
  125. package/dist/net/location.js +0 -41
  126. package/dist/net/path.d.ts +0 -12
  127. package/dist/net/path.d.ts.map +0 -1
  128. package/dist/net/path.js +0 -29
  129. package/dist/net/queryparam.d.ts +0 -10
  130. package/dist/net/queryparam.d.ts.map +0 -1
  131. package/dist/net/queryparam.js +0 -34
  132. package/dist/router.d.ts.map +0 -1
  133. package/dist/scroll.d.ts +0 -7
  134. package/dist/scroll.d.ts.map +0 -1
  135. package/dist/scroll.js +0 -3
  136. package/dist/testing.d.ts.map +0 -1
  137. package/dist/utils/apply.d.ts +0 -9
  138. package/dist/utils/apply.d.ts.map +0 -1
  139. package/dist/utils/apply.js +0 -10
  140. package/dist/utils/index.d.ts +0 -22
  141. package/dist/utils/index.d.ts.map +0 -1
  142. package/dist/utils/index.js +0 -138
  143. package/dist/utils/lens.d.ts +0 -18
  144. package/dist/utils/lens.d.ts.map +0 -1
  145. package/dist/utils/lens.js +0 -27
  146. package/dist/utils/match.d.ts +0 -17
  147. package/dist/utils/match.d.ts.map +0 -1
  148. package/dist/utils/match.js +0 -28
  149. package/dist/utils/result.d.ts +0 -47
  150. package/dist/utils/result.d.ts.map +0 -1
  151. package/dist/utils/result.js +0 -97
  152. package/dist/webcomponent.d.ts.map +0 -1
@@ -0,0 +1,3966 @@
1
+ import {
2
+ containerEventTypes,
3
+ eventTypes,
4
+ foldNode,
5
+ getId,
6
+ isNodeGroup,
7
+ nodeGroup,
8
+ nodeId
9
+ } from "./chunk-NSWOTCDU.js";
10
+ import {
11
+ ResponseBody,
12
+ emptyLocation,
13
+ emptyPath,
14
+ eq,
15
+ exhaustivenessCheck,
16
+ failure,
17
+ filterNot,
18
+ formatLocation,
19
+ internalLocationFromLocation,
20
+ isEffect,
21
+ makeEffects,
22
+ parseAllQueryParams,
23
+ parseFragment,
24
+ parseInternalLocation,
25
+ parsePath,
26
+ pure,
27
+ stringToInt,
28
+ success,
29
+ taskId
30
+ } from "./chunk-KNHJPAIU.js";
31
+ import {
32
+ dbVersion,
33
+ run,
34
+ run2
35
+ } from "./chunk-XICUXW4T.js";
36
+ import {
37
+ __publicField
38
+ } from "./chunk-PKBMQBKP.js";
39
+
40
+ // src/internal/locutils.ts
41
+ function currentLocation(window2) {
42
+ return {
43
+ protocol: filterNot(window2.location.protocol?.trim(), (p) => p.length <= 0),
44
+ hostname: filterNot(window2.location.hostname?.trim(), (h) => h.length <= 0),
45
+ port: window2.location.port ? stringToInt(window2.location.port.trim()) : void 0,
46
+ path: window2.location.pathname ? parsePath(window2.location.pathname) : emptyPath,
47
+ queryParams: window2.location.search?.trim() ? parseAllQueryParams(window2.location.search?.trim()) : [],
48
+ fragment: window2.location.hash ? parseFragment(window2.location.hash) : void 0
49
+ };
50
+ }
51
+
52
+ // src/internal/eventmgr.ts
53
+ function toNativeEventType(type) {
54
+ return type === "textinput" ? "input" : type;
55
+ }
56
+ function makeEventTypeKeyedRecord(eventTypes2, defaultValue) {
57
+ const record = {};
58
+ for (const eventType of eventTypes2) {
59
+ record[eventType] = defaultValue();
60
+ }
61
+ return record;
62
+ }
63
+ function makeListenerCounts(eventTypes2) {
64
+ return makeEventTypeKeyedRecord(eventTypes2, () => ({
65
+ regular: 0,
66
+ capture: 0
67
+ }));
68
+ }
69
+ function updateListenerChangeMap(map, eventType, capture) {
70
+ const existing = map.get(eventType);
71
+ if (existing) {
72
+ if (capture) {
73
+ existing.capture = true;
74
+ } else {
75
+ existing.regular = true;
76
+ }
77
+ } else {
78
+ map.set(eventType, {
79
+ capture,
80
+ regular: !capture
81
+ });
82
+ }
83
+ }
84
+ function adjustCountsAndGetListenerTypesToRemoveAndAdd(counts, oldListeners, newListeners) {
85
+ const countDiffs = {};
86
+ oldListeners.forEach((listener) => {
87
+ const capture = listener.options && listener.options.capture ? true : false;
88
+ const totals = countDiffs[listener._type];
89
+ const currDiff = capture ? totals?.capture : totals?.regular;
90
+ const newDiff = currDiff ? currDiff - 1 : -1;
91
+ if (totals) {
92
+ if (capture) {
93
+ totals.capture = newDiff;
94
+ } else {
95
+ totals.regular = newDiff;
96
+ }
97
+ } else {
98
+ countDiffs[listener._type] = {
99
+ regular: capture ? 0 : newDiff,
100
+ capture: capture ? newDiff : 0
101
+ };
102
+ }
103
+ });
104
+ const allNewEvents = /* @__PURE__ */ new Map();
105
+ newListeners.forEach((listener) => {
106
+ const capture = listener.options && listener.options.capture ? true : false;
107
+ const totals = countDiffs[listener._type];
108
+ const currDiff = capture ? totals?.capture : totals?.regular;
109
+ const newDiff = currDiff ? currDiff + 1 : 1;
110
+ if (totals) {
111
+ if (capture) {
112
+ totals.capture = newDiff;
113
+ } else {
114
+ totals.regular = newDiff;
115
+ }
116
+ } else {
117
+ countDiffs[listener._type] = {
118
+ regular: capture ? 0 : newDiff,
119
+ capture: capture ? newDiff : 0
120
+ };
121
+ }
122
+ updateListenerChangeMap(allNewEvents, listener._type, capture);
123
+ });
124
+ const toAdd = /* @__PURE__ */ new Map();
125
+ const toRemove = /* @__PURE__ */ new Map();
126
+ for (const [k, v] of Object.entries(countDiffs)) {
127
+ const tpe = k;
128
+ const diffTotals = v;
129
+ const currCount = counts[tpe];
130
+ currCount.capture = currCount.capture + diffTotals.capture;
131
+ currCount.regular = currCount.regular + diffTotals.regular;
132
+ if (currCount.capture === 0) {
133
+ if (diffTotals.capture < 0) {
134
+ updateListenerChangeMap(toRemove, tpe, true);
135
+ }
136
+ } else if (currCount.capture > 0) {
137
+ if (diffTotals.capture === currCount.capture) {
138
+ updateListenerChangeMap(toAdd, tpe, true);
139
+ }
140
+ } else {
141
+ throw new Error(
142
+ "EventListener counts out of sync (this indicates a ctrl-fx framework bug)"
143
+ );
144
+ }
145
+ if (currCount.regular === 0) {
146
+ if (diffTotals.regular < 0) {
147
+ updateListenerChangeMap(toRemove, tpe, false);
148
+ }
149
+ } else if (currCount.regular > 0) {
150
+ if (diffTotals.regular === currCount.regular) {
151
+ updateListenerChangeMap(toAdd, tpe, false);
152
+ }
153
+ } else {
154
+ throw new Error(
155
+ "EventListener counts out of sync (this indicates a ctrl-fx framework bug)"
156
+ );
157
+ }
158
+ }
159
+ return { toRemove, toAdd, allNewEvents };
160
+ }
161
+ var EventManagerDelegate = class {
162
+ constructor(window2, runEffect) {
163
+ __publicField(this, "window");
164
+ __publicField(this, "runEffect");
165
+ this.window = window2;
166
+ this.runEffect = runEffect;
167
+ }
168
+ runListeners(ev, listeners) {
169
+ if (listeners.length) {
170
+ const listener = listeners[0];
171
+ if (toNativeEventType(listener.listener._type) === ev.type.toLowerCase() && (ev.eventPhase !== 1 || listener.listener.options.capture)) {
172
+ this.runListener(listener, ev, (keepGoing) => {
173
+ if (keepGoing) {
174
+ this.runListeners(ev, listeners.slice(1));
175
+ } else {
176
+ return;
177
+ }
178
+ });
179
+ } else {
180
+ this.runListeners(ev, listeners.slice(1));
181
+ }
182
+ } else {
183
+ return;
184
+ }
185
+ }
186
+ runListener(listenerAndNode, ev, onComplete) {
187
+ let effect;
188
+ const listener = listenerAndNode.listener;
189
+ switch (listener._type) {
190
+ case "focus":
191
+ case "blur": {
192
+ effect = listener.effect;
193
+ break;
194
+ }
195
+ case "change": {
196
+ let value = "";
197
+ let checked = false;
198
+ if (ev.target && "tagName" in ev.target) {
199
+ switch (ev.target.tagName) {
200
+ case "INPUT": {
201
+ value = ev.target.value;
202
+ checked = ev.target.checked;
203
+ break;
204
+ }
205
+ case "SELECT":
206
+ case "TEXTAREA": {
207
+ value = ev.target.value;
208
+ break;
209
+ }
210
+ }
211
+ }
212
+ const changeData = { value, checked };
213
+ effect = listener.effect(changeData);
214
+ break;
215
+ }
216
+ case "click":
217
+ case "dblclick":
218
+ case "mouseenter":
219
+ case "mouseleave":
220
+ case "submit": {
221
+ effect = listener.effect;
222
+ break;
223
+ }
224
+ case "mousemove": {
225
+ assertMouseMoveEvent(ev);
226
+ const mouseMoveData = {
227
+ clientX: ev.clientX,
228
+ clientY: ev.clientY,
229
+ pageX: ev.pageX,
230
+ pageY: ev.pageY,
231
+ movementX: ev.movementX,
232
+ movementY: ev.movementY
233
+ };
234
+ effect = listener.effect(mouseMoveData);
235
+ break;
236
+ }
237
+ case "wheel": {
238
+ assertWheelEvent(ev);
239
+ const wheelData = {
240
+ deltaX: ev.deltaX,
241
+ deltaY: ev.deltaY,
242
+ deltaZ: ev.deltaZ,
243
+ deltaMode: ev.deltaMode
244
+ };
245
+ effect = listener.effect(wheelData);
246
+ break;
247
+ }
248
+ case "scroll":
249
+ case "scrollend": {
250
+ const owningNode = listenerAndNode.owningNode;
251
+ const scrollData = owningNode._type === "InternalElementNode" ? {
252
+ scrollTop: owningNode.realNode.scrollTop,
253
+ scrollLeft: owningNode.realNode.scrollLeft
254
+ } : { scrollTop: 0, scrollLeft: 0 };
255
+ effect = listener.effect(scrollData);
256
+ break;
257
+ }
258
+ //console.log(ev.target && 'tagName' in ev.target && ev.target.tagName === 'INPUT')
259
+ case "textinput": {
260
+ let textState;
261
+ if (ev.target && "tagName" in ev.target) {
262
+ switch (ev.target.tagName) {
263
+ case "INPUT":
264
+ case "TEXTAREA": {
265
+ textState = {
266
+ value: ev.target.value,
267
+ selectionStart: ev.target.selectionStart,
268
+ selectionEnd: ev.target.selectionEnd
269
+ };
270
+ break;
271
+ }
272
+ case "DIV": {
273
+ textState = {
274
+ value: ev.target.innerText,
275
+ selectionStart: 0,
276
+ selectionEnd: 0
277
+ };
278
+ break;
279
+ }
280
+ default: {
281
+ textState = {
282
+ value: "",
283
+ selectionStart: 0,
284
+ selectionEnd: 0
285
+ };
286
+ }
287
+ }
288
+ } else {
289
+ textState = {
290
+ value: "",
291
+ selectionStart: 0,
292
+ selectionEnd: 0
293
+ };
294
+ }
295
+ effect = listener.effect(textState);
296
+ break;
297
+ }
298
+ case "touchmove":
299
+ case "touchstart":
300
+ case "touchend":
301
+ case "touchcancel": {
302
+ assertTouchEvent(ev);
303
+ const touchData = [];
304
+ for (let i = 0; i < ev.touches.length; i++) {
305
+ touchData.push({
306
+ clientX: ev.touches[i].clientX,
307
+ clientY: ev.touches[i].clientY,
308
+ pageX: ev.touches[i].pageX,
309
+ pageY: ev.touches[i].pageY
310
+ });
311
+ }
312
+ effect = listener.effect(touchData);
313
+ break;
314
+ }
315
+ case "keydown":
316
+ case "keyup": {
317
+ assertKeyEvent(ev);
318
+ const keyData = { key: ev.key, code: ev.code };
319
+ effect = listener.effect(keyData);
320
+ break;
321
+ }
322
+ case "resize": {
323
+ const dimensions = {
324
+ width: this.window.innerWidth,
325
+ height: this.window.innerHeight
326
+ };
327
+ effect = listener.onChange(dimensions);
328
+ break;
329
+ }
330
+ case "popstate":
331
+ case "locationchange": {
332
+ const loc = internalLocationFromLocation(currentLocation(this.window));
333
+ effect = listener.onChange(loc);
334
+ break;
335
+ }
336
+ case "visibilitychange": {
337
+ const state = this.window.document.visibilityState;
338
+ effect = listener.onChange(state);
339
+ break;
340
+ }
341
+ case "online":
342
+ case "offline": {
343
+ effect = listener.effect;
344
+ break;
345
+ }
346
+ case "custom": {
347
+ effect = listener.effect(ev.detail);
348
+ break;
349
+ }
350
+ default:
351
+ exhaustivenessCheck(listener);
352
+ }
353
+ this.runEffect(effect, listenerAndNode.owningNode, (result) => {
354
+ if (result !== void 0) {
355
+ if (result.preventDefault) {
356
+ ev.preventDefault();
357
+ }
358
+ if (result.stopPropagation) {
359
+ ev.stopImmediatePropagation();
360
+ onComplete(false);
361
+ } else {
362
+ onComplete(true);
363
+ }
364
+ } else {
365
+ onComplete(true);
366
+ }
367
+ });
368
+ }
369
+ };
370
+ function assertMouseMoveEvent(event) {
371
+ if (!("clientX" in event)) {
372
+ throw new Error("Event is not a mousemove event");
373
+ }
374
+ }
375
+ function assertWheelEvent(event) {
376
+ if (!("deltaX" in event)) {
377
+ throw new Error("Event is not a wheel event");
378
+ }
379
+ }
380
+ function assertTouchEvent(event) {
381
+ if (!(event.type.toLowerCase().startsWith("touch") && "touches" in event)) {
382
+ throw new Error("Event is not a touch event");
383
+ }
384
+ }
385
+ function assertKeyEvent(event) {
386
+ if (!(event.type.toLowerCase().startsWith("key") && "key" in event && "code" in event)) {
387
+ throw new Error("Event is not a key event");
388
+ }
389
+ }
390
+ var ContainerEventManagerDelegate = class extends EventManagerDelegate {
391
+ constructor(window2, runEffect) {
392
+ super(window2, runEffect);
393
+ __publicField(this, "nodeLookupByEvent", makeEventTypeKeyedRecord(
394
+ containerEventTypes,
395
+ () => /* @__PURE__ */ new Set()
396
+ ));
397
+ __publicField(this, "eventListeners", /* @__PURE__ */ new Map());
398
+ __publicField(this, "windowEventListener", (event) => {
399
+ this.onEvent(event);
400
+ });
401
+ __publicField(this, "windowCaptureEventListener", (event) => {
402
+ this.onEvent(event);
403
+ });
404
+ __publicField(this, "documentEventListener", (event) => {
405
+ this.onEvent(event);
406
+ });
407
+ __publicField(this, "documentCaptureEventListener", (event) => {
408
+ this.onEvent(event);
409
+ });
410
+ __publicField(this, "counts");
411
+ this.counts = makeListenerCounts(containerEventTypes);
412
+ }
413
+ onEvent(event) {
414
+ const eventType = event.type;
415
+ const nodeSet = this.nodeLookupByEvent[eventType];
416
+ if (!nodeSet) {
417
+ return;
418
+ }
419
+ const listenersToRun = [];
420
+ for (const node of nodeSet) {
421
+ const listeners = this.eventListeners.get(node);
422
+ if (listeners) {
423
+ listeners.forEach((listener) => {
424
+ if (listener._type === eventType && (event.eventPhase !== 1 || listener.options.capture)) {
425
+ listenersToRun.push({ listener, owningNode: node });
426
+ }
427
+ });
428
+ }
429
+ }
430
+ this.runListeners(event, listenersToRun);
431
+ if (eventType === "popstate") {
432
+ this.window.dispatchEvent(new Event("locationchange"));
433
+ }
434
+ }
435
+ setContainerListeners(node) {
436
+ if (node.containerListeners.length === 0) {
437
+ this.removeContainerListeners(node);
438
+ return;
439
+ }
440
+ const existingListeners = this.eventListeners.get(node);
441
+ const passive = node.containerListeners.reduce(
442
+ (acc, listener) => acc && (listener.options.passive ? true : false),
443
+ true
444
+ );
445
+ const adjustments = adjustCountsAndGetListenerTypesToRemoveAndAdd(
446
+ this.counts,
447
+ existingListeners || [],
448
+ node.containerListeners
449
+ );
450
+ containerEventTypes.forEach((ev) => {
451
+ if (adjustments.allNewEvents.has(ev)) {
452
+ this.nodeLookupByEvent[ev].add(node);
453
+ } else {
454
+ this.nodeLookupByEvent[ev].delete(node);
455
+ }
456
+ });
457
+ if (node.containerListeners.length > 0) {
458
+ this.eventListeners.set(node, [...node.containerListeners]);
459
+ } else {
460
+ this.eventListeners.delete(node);
461
+ }
462
+ this.processAdjustments(adjustments, passive);
463
+ }
464
+ processAdjustments(adjustments, passive) {
465
+ for (const [ev, changes] of adjustments.toAdd) {
466
+ switch (ev) {
467
+ case "resize":
468
+ case "popstate":
469
+ case "locationchange":
470
+ case "online":
471
+ case "offline": {
472
+ if (changes.capture) {
473
+ this.window.addEventListener(ev, this.windowCaptureEventListener, {
474
+ capture: true,
475
+ passive
476
+ });
477
+ }
478
+ if (changes.regular) {
479
+ this.window.addEventListener(ev, this.windowEventListener, {
480
+ capture: false,
481
+ passive
482
+ });
483
+ }
484
+ break;
485
+ }
486
+ case "keydown":
487
+ case "keyup":
488
+ case "visibilitychange": {
489
+ if (changes.capture) {
490
+ this.window.document.addEventListener(
491
+ ev,
492
+ this.documentCaptureEventListener,
493
+ { capture: true, passive }
494
+ );
495
+ }
496
+ if (changes.regular) {
497
+ this.window.document.addEventListener(
498
+ ev,
499
+ this.documentEventListener,
500
+ { capture: false, passive }
501
+ );
502
+ }
503
+ break;
504
+ }
505
+ default: {
506
+ exhaustivenessCheck(ev);
507
+ }
508
+ }
509
+ }
510
+ }
511
+ removeContainerListeners(node) {
512
+ const existingListeners = this.eventListeners.get(node);
513
+ if (!existingListeners) {
514
+ return;
515
+ }
516
+ const adjustments = adjustCountsAndGetListenerTypesToRemoveAndAdd(
517
+ this.counts,
518
+ existingListeners,
519
+ []
520
+ );
521
+ this.processAdjustments(adjustments, false);
522
+ existingListeners.forEach((listener) => {
523
+ this.nodeLookupByEvent[listener._type].delete(node);
524
+ });
525
+ this.eventListeners.delete(node);
526
+ }
527
+ };
528
+ var ElementEventManagerDelegate = class extends EventManagerDelegate {
529
+ constructor(window2, runEffect) {
530
+ super(window2, runEffect);
531
+ __publicField(this, "listeners", /* @__PURE__ */ new Map());
532
+ __publicField(this, "customHandlers", /* @__PURE__ */ new Map());
533
+ }
534
+ setElementListeners(node) {
535
+ const standardListeners = node.eventListeners.filter(
536
+ (l) => l._type !== "custom"
537
+ );
538
+ const customListeners = node.eventListeners.filter(
539
+ (l) => l._type === "custom"
540
+ );
541
+ this.updateCustomListeners(node, customListeners);
542
+ if (standardListeners.length === 0) {
543
+ this.removeStandardListeners(node);
544
+ return;
545
+ }
546
+ let listenerInfo = this.listeners.get(node);
547
+ if (!listenerInfo) {
548
+ const that = this;
549
+ listenerInfo = {
550
+ counts: makeListenerCounts(eventTypes),
551
+ listeners: [],
552
+ realListener(event) {
553
+ const listeners = that.listeners.get(node)?.listeners;
554
+ if (listeners) {
555
+ that.runListeners(
556
+ event,
557
+ listeners.map((listener) => ({
558
+ listener,
559
+ owningNode: node
560
+ }))
561
+ );
562
+ }
563
+ }
564
+ };
565
+ this.listeners.set(node, listenerInfo);
566
+ }
567
+ const passive = standardListeners.reduce(
568
+ (acc, listener) => acc && (listener.options.passive ? true : false),
569
+ true
570
+ );
571
+ const adjustments = adjustCountsAndGetListenerTypesToRemoveAndAdd(
572
+ listenerInfo.counts,
573
+ listenerInfo.listeners,
574
+ standardListeners
575
+ );
576
+ listenerInfo.listeners = [...standardListeners];
577
+ for (const [tpe, info] of adjustments.toAdd) {
578
+ const domType = toNativeEventType(tpe);
579
+ if (info.regular) {
580
+ node.realNode.addEventListener(domType, listenerInfo.realListener, {
581
+ capture: false,
582
+ passive
583
+ });
584
+ }
585
+ if (info.capture) {
586
+ node.realNode.addEventListener(domType, listenerInfo.realListener, {
587
+ capture: true,
588
+ passive
589
+ });
590
+ }
591
+ }
592
+ for (const [tpe, info] of adjustments.toRemove) {
593
+ const domType = toNativeEventType(tpe);
594
+ if (info.regular) {
595
+ node.realNode.removeEventListener(domType, listenerInfo.realListener, false);
596
+ }
597
+ if (info.capture) {
598
+ node.realNode.removeEventListener(domType, listenerInfo.realListener, true);
599
+ }
600
+ }
601
+ }
602
+ updateCustomListeners(node, customListeners) {
603
+ const existingHandlers = this.customHandlers.get(node) ?? /* @__PURE__ */ new Map();
604
+ const byName = /* @__PURE__ */ new Map();
605
+ for (const l of customListeners) {
606
+ const arr = byName.get(l.eventName) ?? [];
607
+ arr.push(l);
608
+ byName.set(l.eventName, arr);
609
+ }
610
+ for (const [name, { realListener }] of existingHandlers) {
611
+ if (!byName.has(name)) {
612
+ node.realNode.removeEventListener(name, realListener, false);
613
+ existingHandlers.delete(name);
614
+ }
615
+ }
616
+ for (const [name, listeners] of byName) {
617
+ const existing = existingHandlers.get(name);
618
+ if (existing) {
619
+ existing.listeners = listeners;
620
+ } else {
621
+ const that = this;
622
+ const info = { listeners, realListener: null };
623
+ info.realListener = (ev) => {
624
+ const detail = ev.detail;
625
+ for (const l of info.listeners) {
626
+ that.runEffect(l.effect(detail), node, (result) => {
627
+ if (result?.stopPropagation) ev.stopImmediatePropagation();
628
+ if (result?.preventDefault) ev.preventDefault();
629
+ });
630
+ }
631
+ };
632
+ existingHandlers.set(name, info);
633
+ node.realNode.addEventListener(name, info.realListener, {
634
+ capture: false,
635
+ passive: false
636
+ });
637
+ }
638
+ }
639
+ if (existingHandlers.size > 0) {
640
+ this.customHandlers.set(node, existingHandlers);
641
+ } else {
642
+ this.customHandlers.delete(node);
643
+ }
644
+ }
645
+ removeStandardListeners(node) {
646
+ const listenerInfo = this.listeners.get(node);
647
+ if (!listenerInfo) {
648
+ return;
649
+ }
650
+ for (const [eventType, info] of Object.entries(listenerInfo.counts)) {
651
+ const domType = toNativeEventType(eventType);
652
+ if (info.regular) {
653
+ node.realNode.removeEventListener(
654
+ domType,
655
+ listenerInfo.realListener,
656
+ false
657
+ );
658
+ }
659
+ if (info.capture) {
660
+ node.realNode.removeEventListener(
661
+ domType,
662
+ listenerInfo.realListener,
663
+ true
664
+ );
665
+ }
666
+ }
667
+ this.listeners.delete(node);
668
+ }
669
+ };
670
+ var EventManager = class {
671
+ constructor(window2, runEffect) {
672
+ __publicField(this, "containerDelegate");
673
+ __publicField(this, "elementDelegate");
674
+ this.containerDelegate = new ContainerEventManagerDelegate(
675
+ window2,
676
+ runEffect
677
+ );
678
+ this.elementDelegate = new ElementEventManagerDelegate(window2, runEffect);
679
+ }
680
+ setContainerListeners(node) {
681
+ this.containerDelegate.setContainerListeners(node);
682
+ }
683
+ setElementListeners(node) {
684
+ this.elementDelegate.setElementListeners(node);
685
+ }
686
+ };
687
+
688
+ // src/internal/taskreg.ts
689
+ var TaskRegistry = class {
690
+ constructor() {
691
+ __publicField(this, "tasks", {});
692
+ }
693
+ set(taskId2, cleanup) {
694
+ this.tasks[taskId2] = cleanup;
695
+ }
696
+ cancel(taskId2) {
697
+ const cleanup = this.tasks[taskId2];
698
+ if (cleanup) cleanup();
699
+ delete this.tasks[taskId2];
700
+ }
701
+ remove(taskId2) {
702
+ delete this.tasks[taskId2];
703
+ }
704
+ cancelAll() {
705
+ for (const cleanup of Object.values(this.tasks)) {
706
+ cleanup();
707
+ }
708
+ this.tasks = {};
709
+ }
710
+ };
711
+
712
+ // src/internal/vdom.ts
713
+ function rootNode(realNode) {
714
+ return {
715
+ _type: "RootNode",
716
+ realNode,
717
+ prepend(node) {
718
+ addNewChild(this, node, "left");
719
+ },
720
+ append(node) {
721
+ addNewChild(this, node, "right");
722
+ }
723
+ };
724
+ }
725
+ function componentNode(component, nodeId2) {
726
+ return {
727
+ _type: "ComponentNode",
728
+ component,
729
+ nodeId: nodeId2,
730
+ state: { _type: "Uninitialized" },
731
+ containerListeners: [],
732
+ componentEventListeners: [],
733
+ before(node) {
734
+ moveNodeNextTo(node, this, "left");
735
+ },
736
+ after(node) {
737
+ moveNodeNextTo(node, this, "right");
738
+ },
739
+ prepend(node) {
740
+ addNewChild(this, node, "left");
741
+ },
742
+ append(node) {
743
+ addNewChild(this, node, "right");
744
+ }
745
+ };
746
+ }
747
+ function viewNode(view) {
748
+ return {
749
+ _type: "ViewNode",
750
+ nodeId: view.nodeId,
751
+ params: view.params,
752
+ containerListeners: [],
753
+ after(nodeToMove) {
754
+ moveNodeNextTo(nodeToMove, this, "right");
755
+ },
756
+ before(nodeToMove) {
757
+ moveNodeNextTo(nodeToMove, this, "left");
758
+ },
759
+ prepend(node) {
760
+ addNewChild(this, node, "left");
761
+ },
762
+ append(node) {
763
+ addNewChild(this, node, "right");
764
+ }
765
+ };
766
+ }
767
+ function internalTextNode(document, text) {
768
+ return {
769
+ _type: "InternalTextNode",
770
+ text,
771
+ realNode: document.createTextNode(text),
772
+ after(nodeToMove) {
773
+ moveNodeNextTo(nodeToMove, this, "right");
774
+ },
775
+ before(nodeToMove) {
776
+ moveNodeNextTo(nodeToMove, this, "left");
777
+ }
778
+ };
779
+ }
780
+ function internalElementNode(document, tag, nodeId2, attrs, eventListeners, props = []) {
781
+ const realNode = document.createElement(tag);
782
+ attrs.forEach((a) => {
783
+ realNode.setAttribute(a.name, a.value ? a.value : "");
784
+ });
785
+ props.forEach((p) => {
786
+ ;
787
+ realNode[p.name] = p.value;
788
+ });
789
+ return {
790
+ _type: "InternalElementNode",
791
+ tag,
792
+ nodeId: nodeId2,
793
+ attrs,
794
+ props,
795
+ eventListeners,
796
+ realNode,
797
+ after(nodeToMove) {
798
+ moveNodeNextTo(nodeToMove, this, "right");
799
+ },
800
+ before(nodeToMove) {
801
+ moveNodeNextTo(nodeToMove, this, "left");
802
+ },
803
+ prepend(node) {
804
+ addNewChild(this, node, "left");
805
+ },
806
+ append(node) {
807
+ addNewChild(this, node, "right");
808
+ }
809
+ };
810
+ }
811
+ function detach(node, leaveRealNodeAttached = false) {
812
+ const origPrevSibling = node.prevSibling;
813
+ const origNextSibling = node.nextSibling;
814
+ if (!origPrevSibling) {
815
+ if (node.parent) {
816
+ node.parent.firstChild = origNextSibling;
817
+ }
818
+ }
819
+ if (origPrevSibling) {
820
+ origPrevSibling.nextSibling = origNextSibling;
821
+ }
822
+ if (origNextSibling) {
823
+ origNextSibling.prevSibling = origPrevSibling;
824
+ }
825
+ if (!leaveRealNodeAttached) {
826
+ switch (node._type) {
827
+ case "InternalElementNode":
828
+ case "InternalTextNode": {
829
+ node.realNode.remove();
830
+ break;
831
+ }
832
+ case "ComponentNode":
833
+ case "ViewNode":
834
+ break;
835
+ default:
836
+ exhaustivenessCheck(node);
837
+ }
838
+ }
839
+ node.prevSibling = void 0;
840
+ node.nextSibling = void 0;
841
+ node.parent = void 0;
842
+ }
843
+ function moveNodeNextTo(nodeToMove, anchorNode, direction) {
844
+ detach(nodeToMove, true);
845
+ switch (direction) {
846
+ case "right": {
847
+ nodeToMove.nextSibling = anchorNode.nextSibling;
848
+ anchorNode.nextSibling = nodeToMove;
849
+ nodeToMove.prevSibling = anchorNode;
850
+ if (nodeToMove.nextSibling) {
851
+ nodeToMove.nextSibling.prevSibling = nodeToMove;
852
+ }
853
+ break;
854
+ }
855
+ case "left": {
856
+ nodeToMove.prevSibling = anchorNode.prevSibling;
857
+ anchorNode.prevSibling = nodeToMove;
858
+ nodeToMove.nextSibling = anchorNode;
859
+ if (nodeToMove.prevSibling) {
860
+ nodeToMove.prevSibling.nextSibling = nodeToMove;
861
+ } else if (anchorNode.parent) {
862
+ anchorNode.parent.firstChild = nodeToMove;
863
+ }
864
+ break;
865
+ }
866
+ default: {
867
+ exhaustivenessCheck(direction);
868
+ }
869
+ }
870
+ nodeToMove.parent = anchorNode.parent;
871
+ const realNodesToMove = realNodesFor(nodeToMove);
872
+ let realAnchorNode = getRealAnchorNode(anchorNode, direction);
873
+ realNodesToMove.forEach((node) => {
874
+ switch (direction) {
875
+ case "right": {
876
+ if (!realAnchorNode) {
877
+ const realParent = realParentNode(anchorNode);
878
+ if (realParent) {
879
+ realParent.append(node);
880
+ }
881
+ } else {
882
+ realAnchorNode.after(node);
883
+ }
884
+ realAnchorNode = node;
885
+ break;
886
+ }
887
+ case "left": {
888
+ if (!realAnchorNode) {
889
+ const realParent = realParentNode(anchorNode);
890
+ if (realParent) {
891
+ realParent.prepend(node);
892
+ }
893
+ } else {
894
+ realAnchorNode.before(node);
895
+ }
896
+ realAnchorNode = node;
897
+ break;
898
+ }
899
+ default: {
900
+ exhaustivenessCheck(direction);
901
+ }
902
+ }
903
+ });
904
+ }
905
+ function addNewChild(parent, child, position) {
906
+ detach(child, true);
907
+ child.parent = parent;
908
+ if (position === "left") {
909
+ child.nextSibling = parent.firstChild;
910
+ parent.firstChild = child;
911
+ } else {
912
+ if (parent.firstChild) {
913
+ const curLastChild = lastNode(parent.firstChild);
914
+ curLastChild.nextSibling = child;
915
+ child.prevSibling = curLastChild;
916
+ } else {
917
+ parent.firstChild = child;
918
+ }
919
+ }
920
+ const realChildNodes = realNodesFor(child);
921
+ if (parent._type === "RootNode") {
922
+ if (position === "left") {
923
+ parent.realNode.prepend(...realChildNodes);
924
+ } else {
925
+ parent.realNode.append(...realChildNodes);
926
+ }
927
+ return;
928
+ }
929
+ if (parent._type === "InternalElementNode") {
930
+ if (position === "left") {
931
+ parent.realNode.prepend(...realChildNodes);
932
+ } else {
933
+ parent.realNode.append(...realChildNodes);
934
+ }
935
+ return;
936
+ }
937
+ const realSibling = getRealSibling(parent, position);
938
+ if (realSibling) {
939
+ if (position === "left") {
940
+ realSibling.realNode.before(...realChildNodes);
941
+ } else {
942
+ realSibling.realNode.after(...realChildNodes);
943
+ }
944
+ } else {
945
+ const realParent = realParentNode(parent);
946
+ if (realParent) {
947
+ if (position === "left") {
948
+ realParent.prepend(...realChildNodes);
949
+ } else {
950
+ realParent.append(...realChildNodes);
951
+ }
952
+ }
953
+ }
954
+ }
955
+ function realNodesFor(node) {
956
+ switch (node._type) {
957
+ case "InternalElementNode":
958
+ case "InternalTextNode": {
959
+ return [node.realNode];
960
+ }
961
+ case "ComponentNode":
962
+ case "ViewNode": {
963
+ const realNodes = [];
964
+ let next = node.firstChild;
965
+ while (next) {
966
+ realNodes.push(...realNodesFor(next));
967
+ next = next.nextSibling;
968
+ }
969
+ return realNodes;
970
+ }
971
+ default: {
972
+ exhaustivenessCheck(node);
973
+ }
974
+ }
975
+ }
976
+ function getRealAnchorNode(anchorNode, insertionDirection) {
977
+ let curInitialNodeForLevel = anchorNode;
978
+ while (curInitialNodeForLevel) {
979
+ let curNode = curInitialNodeForLevel;
980
+ while (curNode) {
981
+ if (curNode._type === "InternalTextNode" || curNode._type === "InternalElementNode") {
982
+ return curNode.realNode;
983
+ }
984
+ let curChild = insertionDirection === "right" ? curNode.firstChild ? lastNode(curNode.firstChild) : null : curNode.firstChild;
985
+ while (curChild) {
986
+ if (curChild._type === "InternalElementNode" || curChild._type === "InternalTextNode") {
987
+ return curChild.realNode;
988
+ }
989
+ curChild = insertionDirection === "right" ? curChild.prevSibling : curChild.nextSibling;
990
+ }
991
+ curNode = insertionDirection === "right" ? curNode.prevSibling : curNode.nextSibling;
992
+ }
993
+ switch (curInitialNodeForLevel.parent?._type) {
994
+ case "InternalElementNode":
995
+ case "RootNode":
996
+ return void 0;
997
+ case "ComponentNode":
998
+ case "ViewNode": {
999
+ curInitialNodeForLevel = curInitialNodeForLevel.parent;
1000
+ }
1001
+ }
1002
+ }
1003
+ return void 0;
1004
+ }
1005
+ function lastNode(node) {
1006
+ let result = node;
1007
+ while (result.nextSibling) {
1008
+ result = result.nextSibling;
1009
+ }
1010
+ return result;
1011
+ }
1012
+ function realParentNode(node) {
1013
+ return realClosestAncestor(node)?.realNode;
1014
+ }
1015
+ function realClosestAncestor(node) {
1016
+ const parent = node.parent;
1017
+ if (!parent) {
1018
+ return void 0;
1019
+ }
1020
+ switch (parent._type) {
1021
+ case "InternalElementNode":
1022
+ return parent;
1023
+ case "ComponentNode":
1024
+ return realClosestAncestor(parent);
1025
+ case "RootNode":
1026
+ return parent;
1027
+ case "ViewNode":
1028
+ return realClosestAncestor(parent);
1029
+ }
1030
+ }
1031
+ function getRealSibling(node, position) {
1032
+ let startNodeForCurrentLevel = node;
1033
+ while (startNodeForCurrentLevel) {
1034
+ const sibling = position === "left" ? startNodeForCurrentLevel.prevSibling : startNodeForCurrentLevel.nextSibling;
1035
+ if (sibling) {
1036
+ const realSibling = getRealSiblingLookingLaterally(sibling, position);
1037
+ if (realSibling) {
1038
+ return realSibling;
1039
+ }
1040
+ }
1041
+ const parent = startNodeForCurrentLevel.parent;
1042
+ if (!parent) {
1043
+ return void 0;
1044
+ }
1045
+ switch (parent._type) {
1046
+ case "InternalElementNode":
1047
+ case "RootNode": {
1048
+ return void 0;
1049
+ }
1050
+ case "ComponentNode":
1051
+ case "ViewNode": {
1052
+ startNodeForCurrentLevel = parent;
1053
+ continue;
1054
+ }
1055
+ default:
1056
+ exhaustivenessCheck(parent);
1057
+ }
1058
+ }
1059
+ return void 0;
1060
+ }
1061
+ function getRealSiblingLookingLaterally(nextNode, position) {
1062
+ while (nextNode) {
1063
+ switch (nextNode._type) {
1064
+ case "InternalElementNode":
1065
+ case "InternalTextNode":
1066
+ return nextNode;
1067
+ case "ComponentNode":
1068
+ case "ViewNode":
1069
+ if (nextNode.firstChild) {
1070
+ const child = position === "left" ? lastNode(nextNode.firstChild) : nextNode.firstChild;
1071
+ const rslt = getRealSiblingLookingLaterally(child, position);
1072
+ if (rslt) {
1073
+ return rslt;
1074
+ }
1075
+ }
1076
+ nextNode = position === "left" ? nextNode.prevSibling : nextNode.nextSibling;
1077
+ continue;
1078
+ }
1079
+ }
1080
+ return void 0;
1081
+ }
1082
+ function updateText(node, newText) {
1083
+ if (node.text === newText) {
1084
+ return;
1085
+ }
1086
+ node.text = newText;
1087
+ node.realNode.nodeValue = newText;
1088
+ }
1089
+ function updateAttrs(node, newAttrs) {
1090
+ const newAttrsMap = new Map(newAttrs.map((a) => [a.name, a]));
1091
+ node.attrs.forEach((oldAttr) => {
1092
+ const newAttr = newAttrsMap.get(oldAttr.name);
1093
+ if (newAttr) {
1094
+ if (newAttr.value || oldAttr.value) {
1095
+ if (newAttr.value !== oldAttr.value) {
1096
+ node.realNode.setAttribute(
1097
+ newAttr.name,
1098
+ newAttr.value ? newAttr.value : ""
1099
+ );
1100
+ }
1101
+ }
1102
+ } else {
1103
+ node.realNode.removeAttribute(oldAttr.name);
1104
+ }
1105
+ });
1106
+ node.attrs = newAttrs;
1107
+ }
1108
+ function updateProps(node, newProps) {
1109
+ const newPropsMap = new Map(newProps.map((p) => [p.name, p.value]));
1110
+ node.props.forEach((oldProp) => {
1111
+ if (!newPropsMap.has(oldProp.name)) {
1112
+ ;
1113
+ node.realNode[oldProp.name] = void 0;
1114
+ }
1115
+ });
1116
+ newProps.forEach((p) => {
1117
+ ;
1118
+ node.realNode[p.name] = p.value;
1119
+ });
1120
+ node.props = newProps;
1121
+ }
1122
+ function closestComponentPath(node) {
1123
+ const closestForParent = node.parent && node.parent._type !== "RootNode" ? closestComponentPath(node.parent) : void 0;
1124
+ switch (node._type) {
1125
+ case "InternalElementNode":
1126
+ case "InternalTextNode":
1127
+ case "ViewNode":
1128
+ return closestForParent;
1129
+ case "ComponentNode":
1130
+ return componentPath(node.nodeId, closestForParent);
1131
+ }
1132
+ }
1133
+
1134
+ // src/internal/cmpmgr.ts
1135
+ var mountCount = 0;
1136
+ function manageComponent(component, mountId, window2, interpreter, styleRegistry) {
1137
+ const mountNode = window2.document.getElementById(mountId);
1138
+ if (!mountNode) {
1139
+ throw new Error(
1140
+ `Cannot attach component tree. Root node with id ${mountId} not found`
1141
+ );
1142
+ }
1143
+ const mgr = new ComponentManager(
1144
+ component,
1145
+ mountNode,
1146
+ window2,
1147
+ interpreter,
1148
+ styleRegistry
1149
+ );
1150
+ return {
1151
+ dispatchEffect: (effect) => mgr.dispatchEffect(effect)
1152
+ };
1153
+ }
1154
+ var ComponentManager = class {
1155
+ constructor(component, mountNode, window2, interpreter, styleRegistry, onRootEvent) {
1156
+ __publicField(this, "rootCompPath");
1157
+ __publicField(this, "components", {});
1158
+ __publicField(this, "removedComponents", {});
1159
+ __publicField(this, "addedComponents", []);
1160
+ __publicField(this, "interpreter");
1161
+ __publicField(this, "window");
1162
+ __publicField(this, "document");
1163
+ __publicField(this, "eventManager");
1164
+ __publicField(this, "taskRegistry", new TaskRegistry());
1165
+ __publicField(this, "styleRegistry");
1166
+ __publicField(this, "onRootEvent");
1167
+ this.window = window2;
1168
+ this.document = this.window.document;
1169
+ this.interpreter = interpreter;
1170
+ this.styleRegistry = styleRegistry;
1171
+ this.onRootEvent = onRootEvent;
1172
+ this.eventManager = new EventManager(
1173
+ this.window,
1174
+ (effect, node, onResult) => {
1175
+ const componentPath2 = closestComponentPath(node);
1176
+ if (componentPath2) {
1177
+ this.runEffect(effect, componentPath2, onResult);
1178
+ }
1179
+ }
1180
+ );
1181
+ const rootCompPath = componentPath(nodeId(`__root_component:${++mountCount}`));
1182
+ this.rootCompPath = rootCompPath;
1183
+ const compNode = componentNode(component, rootCompPath.nodeId);
1184
+ const root = rootNode(mountNode);
1185
+ root.append(compNode);
1186
+ this.components[componentPath(compNode.nodeId, void 0).format()] = compNode;
1187
+ this.injectComponentCss(component);
1188
+ const initStateEffect = component.initialState(component.params);
1189
+ this.runEffect(initStateEffect, rootCompPath, (state) => {
1190
+ this.renderComponent(rootCompPath, state);
1191
+ });
1192
+ }
1193
+ injectComponentCss(component) {
1194
+ if (!component.css) return;
1195
+ const css = typeof component.css === "function" ? component.css(component.params) : component.css;
1196
+ this.styleRegistry.injectStyle(css);
1197
+ }
1198
+ getRootState() {
1199
+ return this.components[this.rootCompPath.format()]?.state ?? {
1200
+ _type: "Uninitialized"
1201
+ };
1202
+ }
1203
+ dispatchEffect(effect) {
1204
+ this.runEffect(effect, this.rootCompPath, () => {
1205
+ });
1206
+ }
1207
+ destroy() {
1208
+ this.taskRegistry.cancelAll();
1209
+ }
1210
+ runEffect(effect, componentPath2, f) {
1211
+ const that = this;
1212
+ this.interpreter(
1213
+ effect,
1214
+ {
1215
+ onComplete: f,
1216
+ onFireEvent: (event) => {
1217
+ const component = this.components[componentPath2.format()];
1218
+ if (component) {
1219
+ const parentCompPath = componentPath2.parentComponentPath;
1220
+ if (parentCompPath) {
1221
+ component.componentEventListeners.forEach((listener) => {
1222
+ const effect2 = listener(event);
1223
+ this.runEffect(effect2, parentCompPath, () => {
1224
+ });
1225
+ });
1226
+ } else {
1227
+ this.onRootEvent?.(event);
1228
+ }
1229
+ }
1230
+ },
1231
+ getState: () => {
1232
+ return that.components[componentPath2.format()].state.value;
1233
+ },
1234
+ setState: (state) => {
1235
+ that.renderComponent(componentPath2, state);
1236
+ }
1237
+ },
1238
+ this.taskRegistry
1239
+ );
1240
+ }
1241
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1242
+ renderComponent(componentPath2, state) {
1243
+ const component = this.components[componentPath2.format()];
1244
+ if (!component) {
1245
+ return;
1246
+ }
1247
+ if (component.state._type === "Ready" && eq(component.state.value, state)) {
1248
+ return;
1249
+ }
1250
+ component.state = { _type: "Ready", value: state };
1251
+ const newNodeGroup = component.component.view(
1252
+ state,
1253
+ component.component.params
1254
+ );
1255
+ component.containerListeners = newNodeGroup.containerListeners;
1256
+ this.eventManager.setContainerListeners(component);
1257
+ const oldNodes = component.firstChild;
1258
+ this.compareNodeSequences(
1259
+ oldNodes,
1260
+ newNodeGroup.nodes,
1261
+ component,
1262
+ componentPath2
1263
+ );
1264
+ const added = this.addedComponents.splice(0);
1265
+ added.forEach((childPath) => {
1266
+ const cNode = this.components[childPath.format()];
1267
+ if (cNode) {
1268
+ const initEffect = cNode.component.initialState(cNode.component.params);
1269
+ this.runEffect(initEffect, childPath, (state2) => {
1270
+ this.renderComponent(childPath, state2);
1271
+ });
1272
+ }
1273
+ });
1274
+ }
1275
+ compareNodeSequences(oldNodes, newNodes, parent, ancestorComponentPath) {
1276
+ const mutNewNodes = [...newNodes];
1277
+ while (mutNewNodes.length > 0) {
1278
+ const newNode = mutNewNodes.shift();
1279
+ const correspondingOldNode = this.findCorrespondingOldNode(
1280
+ newNode,
1281
+ oldNodes,
1282
+ ancestorComponentPath
1283
+ );
1284
+ if (correspondingOldNode) {
1285
+ if (correspondingOldNode !== oldNodes) {
1286
+ oldNodes?.before(correspondingOldNode);
1287
+ }
1288
+ this.compareNodes(correspondingOldNode, newNode, ancestorComponentPath);
1289
+ oldNodes = correspondingOldNode.nextSibling;
1290
+ } else {
1291
+ const newInternalNode = this.convertNodeToInternalNode(
1292
+ newNode,
1293
+ ancestorComponentPath
1294
+ );
1295
+ if (oldNodes) {
1296
+ oldNodes.before(newInternalNode);
1297
+ } else {
1298
+ parent.append(newInternalNode);
1299
+ }
1300
+ }
1301
+ }
1302
+ let oldNode = oldNodes;
1303
+ while (oldNode) {
1304
+ const next = oldNode.nextSibling;
1305
+ this.removeNode(oldNode, ancestorComponentPath);
1306
+ oldNode = next;
1307
+ }
1308
+ }
1309
+ removeNode(oldNode, ancestorComponentPath) {
1310
+ detach(oldNode);
1311
+ const that = this;
1312
+ function removeNodeRecords(node, ancestorComponentPath2) {
1313
+ switch (node._type) {
1314
+ case "InternalTextNode":
1315
+ return;
1316
+ case "InternalElementNode": {
1317
+ that.eventManager.setElementListeners(node);
1318
+ return;
1319
+ }
1320
+ case "ViewNode": {
1321
+ that.eventManager.setContainerListeners(node);
1322
+ return;
1323
+ }
1324
+ case "ComponentNode": {
1325
+ that.eventManager.setContainerListeners(node);
1326
+ const compPath = componentPath(
1327
+ node.nodeId,
1328
+ ancestorComponentPath2
1329
+ ).format();
1330
+ delete that.components[compPath];
1331
+ that.removedComponents[compPath] = node;
1332
+ return;
1333
+ }
1334
+ }
1335
+ }
1336
+ foldTree(
1337
+ oldNode,
1338
+ ancestorComponentPath,
1339
+ void 0,
1340
+ (node, ancestorComponentId) => {
1341
+ removeNodeRecords(node, ancestorComponentId);
1342
+ }
1343
+ );
1344
+ }
1345
+ compareNodes(oldNode, newNode, ancestorComponentPath) {
1346
+ const parent = oldNode.parent;
1347
+ const removeOldAndAddNew = () => {
1348
+ const nextSibling = oldNode.nextSibling;
1349
+ this.removeNode(oldNode, ancestorComponentPath);
1350
+ const newInternalNode = this.convertNodeToInternalNode(
1351
+ newNode,
1352
+ ancestorComponentPath
1353
+ );
1354
+ if (nextSibling) {
1355
+ nextSibling.after(newInternalNode);
1356
+ } else if (parent) {
1357
+ parent.append(newInternalNode);
1358
+ }
1359
+ };
1360
+ const that = this;
1361
+ foldNode(newNode, {
1362
+ onText(text) {
1363
+ if (oldNode._type !== "InternalTextNode") {
1364
+ removeOldAndAddNew();
1365
+ return;
1366
+ }
1367
+ updateText(oldNode, text);
1368
+ },
1369
+ onVoidElement(elem) {
1370
+ if (oldNode._type !== "InternalElementNode" || oldNode.tag !== elem.tag) {
1371
+ removeOldAndAddNew();
1372
+ return;
1373
+ }
1374
+ updateAttrs(oldNode, elem.attrs);
1375
+ updateProps(oldNode, elem.props);
1376
+ if (oldNode.eventListeners.length > 0 || elem.eventListeners.length > 0) {
1377
+ oldNode.eventListeners = elem.eventListeners;
1378
+ that.eventManager.setElementListeners(oldNode);
1379
+ }
1380
+ },
1381
+ onNonVoidElement(elem) {
1382
+ if (oldNode._type !== "InternalElementNode" || oldNode.tag !== elem.tag) {
1383
+ removeOldAndAddNew();
1384
+ return;
1385
+ }
1386
+ updateAttrs(oldNode, elem.attrs);
1387
+ updateProps(oldNode, elem.props);
1388
+ if (oldNode.eventListeners.length > 0) {
1389
+ oldNode.eventListeners = elem.eventListeners;
1390
+ that.eventManager.setElementListeners(oldNode);
1391
+ }
1392
+ that.compareNodeSequences(
1393
+ oldNode.firstChild,
1394
+ elem.children,
1395
+ oldNode,
1396
+ ancestorComponentPath
1397
+ );
1398
+ },
1399
+ onView(view) {
1400
+ if (oldNode._type !== "ViewNode" || !oldNode.nodeId.eq(view.nodeId)) {
1401
+ removeOldAndAddNew();
1402
+ return;
1403
+ }
1404
+ if (eq(oldNode.params, view.params)) {
1405
+ return;
1406
+ }
1407
+ const newNodeGroup = view.nodes(view.params);
1408
+ oldNode.params = view.params;
1409
+ oldNode.containerListeners = newNodeGroup.containerListeners;
1410
+ that.eventManager.setContainerListeners(oldNode);
1411
+ that.compareNodeSequences(
1412
+ oldNode.firstChild,
1413
+ newNodeGroup.nodes,
1414
+ oldNode,
1415
+ ancestorComponentPath
1416
+ );
1417
+ },
1418
+ onComponent(comp) {
1419
+ if (oldNode._type !== "ComponentNode" || !oldNode.nodeId.eq(comp.nodeId)) {
1420
+ removeOldAndAddNew();
1421
+ return;
1422
+ }
1423
+ if (oldNode.state._type === "Ready") {
1424
+ if (oldNode.componentEventListeners.length > 0 || comp.componentEventListeners.length > 0) {
1425
+ oldNode.componentEventListeners = comp.componentEventListeners;
1426
+ that.eventManager.setContainerListeners(oldNode);
1427
+ }
1428
+ if (eq(oldNode.component.params, comp.component.params)) {
1429
+ oldNode.component = comp.component;
1430
+ } else {
1431
+ that.injectComponentCss(comp.component);
1432
+ const newNodeGroup = comp.component.view(
1433
+ oldNode.state.value,
1434
+ comp.component.params
1435
+ );
1436
+ oldNode.containerListeners = newNodeGroup.containerListeners;
1437
+ that.eventManager.setContainerListeners(oldNode);
1438
+ oldNode.component = comp.component;
1439
+ that.compareNodeSequences(
1440
+ oldNode.firstChild,
1441
+ newNodeGroup.nodes,
1442
+ oldNode,
1443
+ componentPath(comp.nodeId, ancestorComponentPath)
1444
+ );
1445
+ }
1446
+ } else {
1447
+ oldNode.component = comp.component;
1448
+ }
1449
+ }
1450
+ });
1451
+ }
1452
+ convertNodeToInternalNode(node, ancestorComponentId) {
1453
+ const that = this;
1454
+ return foldNode(node, {
1455
+ onText(text) {
1456
+ return internalTextNode(that.document, text);
1457
+ },
1458
+ onVoidElement(elem) {
1459
+ const internalElem = internalElementNode(
1460
+ that.document,
1461
+ elem.tag,
1462
+ elem.nodeId,
1463
+ elem.attrs,
1464
+ elem.eventListeners,
1465
+ elem.props
1466
+ );
1467
+ if (elem.eventListeners.length > 0) {
1468
+ that.eventManager.setElementListeners(internalElem);
1469
+ }
1470
+ return internalElem;
1471
+ },
1472
+ onNonVoidElement(elem) {
1473
+ const internalElement = internalElementNode(
1474
+ that.document,
1475
+ elem.tag,
1476
+ elem.nodeId,
1477
+ elem.attrs,
1478
+ elem.eventListeners,
1479
+ elem.props
1480
+ );
1481
+ if (elem.eventListeners.length > 0) {
1482
+ that.eventManager.setElementListeners(internalElement);
1483
+ }
1484
+ elem.children.forEach((childNode) => {
1485
+ internalElement.append(
1486
+ that.convertNodeToInternalNode(childNode, ancestorComponentId)
1487
+ );
1488
+ });
1489
+ return internalElement;
1490
+ },
1491
+ onView(view) {
1492
+ const vNode = viewNode(view);
1493
+ const nodeGroup2 = view.nodes(view.params);
1494
+ vNode.params = view.params;
1495
+ vNode.containerListeners = nodeGroup2.containerListeners;
1496
+ nodeGroup2.nodes.forEach((childNode) => {
1497
+ vNode.append(
1498
+ that.convertNodeToInternalNode(childNode, ancestorComponentId)
1499
+ );
1500
+ });
1501
+ that.eventManager.setContainerListeners(vNode);
1502
+ return vNode;
1503
+ },
1504
+ onComponent(comp) {
1505
+ const compId = componentPath(comp.nodeId, ancestorComponentId).format();
1506
+ const existingComp = that.components[compId];
1507
+ if (existingComp) {
1508
+ detach(existingComp);
1509
+ that.compareNodes(existingComp, comp, ancestorComponentId);
1510
+ return existingComp;
1511
+ }
1512
+ const cNode = componentNode(comp.component, comp.nodeId);
1513
+ if (comp.componentEventListeners.length > 0) {
1514
+ cNode.componentEventListeners = comp.componentEventListeners;
1515
+ that.eventManager.setContainerListeners(cNode);
1516
+ }
1517
+ that.injectComponentCss(comp.component);
1518
+ that.components[compId] = cNode;
1519
+ that.addedComponents.push(
1520
+ componentPath(comp.nodeId, ancestorComponentId)
1521
+ );
1522
+ return cNode;
1523
+ }
1524
+ });
1525
+ }
1526
+ // private compareNodes(
1527
+ // oldNode: InternalNode,
1528
+ // newNode: Node<unknown, unknown>,
1529
+ // parent: RealElement,
1530
+ // prevSibling: RealNode | null,
1531
+ // ancestorComponentId: ComponentId
1532
+ // ): RealNode | null {
1533
+ // if (typeof (newNode) === 'string') {
1534
+ // if (oldNode._type === 'InternalTextNode') {
1535
+ // if (oldNode.text !== newNode) {
1536
+ // oldNode.realNode.nodeValue = newNode
1537
+ // }
1538
+ // return oldNode.realNode
1539
+ // }
1540
+ // this.removeNode(oldNode)
1541
+ // return this.addNode1(newNode, parent, prevSibling, ancestorComponentId)
1542
+ // }
1543
+ // switch (newNode._type) {
1544
+ // case 'Element': {
1545
+ // if (
1546
+ // oldNode._type === 'InternalElementNode' &&
1547
+ // newNode.tag === oldNode.tag
1548
+ // ) {
1549
+ // this.compareElements(oldNode, newNode)
1550
+ // return oldNode.realNode
1551
+ // } else {
1552
+ // this.removeNode(oldNode)
1553
+ // return this.addNode1(newNode, parent, prevSibling, ancestorComponentId)
1554
+ // }
1555
+ // }
1556
+ // case 'Component': {
1557
+ // return todo()
1558
+ // }
1559
+ // case 'View': {
1560
+ // return todo()
1561
+ // }
1562
+ // }
1563
+ // }
1564
+ /**
1565
+ * Return an old node that can be compared to `newNode`.
1566
+ *
1567
+ * If the old node represent the same node as `newNode`, return it.
1568
+ *
1569
+ * If the same node isn't found, return the first node if it is `comparable`,
1570
+ * otherwise return undefined. (We don't move nodes if they are comparable but
1571
+ * not the same).
1572
+ */
1573
+ findCorrespondingOldNode(newNode, oldNodes, ancestorComponentPath) {
1574
+ if (typeof newNode !== "string" && newNode._type === "Component") {
1575
+ const registeredComponent = this.components[componentPath(newNode.nodeId, ancestorComponentPath).format()];
1576
+ if (registeredComponent) {
1577
+ return registeredComponent;
1578
+ }
1579
+ }
1580
+ let next = oldNodes;
1581
+ while (next) {
1582
+ if (getNodesRelationship(next, newNode) === "same") {
1583
+ return next;
1584
+ }
1585
+ next = next.nextSibling;
1586
+ }
1587
+ if (oldNodes) {
1588
+ if (getNodesRelationship(oldNodes, newNode) === "comparable") {
1589
+ return oldNodes;
1590
+ }
1591
+ }
1592
+ return void 0;
1593
+ }
1594
+ };
1595
+ function getNodesRelationship(oldNode, newNode) {
1596
+ if (typeof newNode === "string") {
1597
+ return oldNode._type === "InternalTextNode" ? "comparable" : "unrelated";
1598
+ }
1599
+ if (oldNode._type === "InternalTextNode") {
1600
+ return "unrelated";
1601
+ }
1602
+ switch (newNode._type) {
1603
+ case "Element": {
1604
+ if (oldNode._type === "InternalElementNode") {
1605
+ if (newNode.nodeId) {
1606
+ if (oldNode.nodeId) {
1607
+ return newNode.nodeId.eq(oldNode.nodeId) ? "same" : "unrelated";
1608
+ }
1609
+ } else if (oldNode.nodeId) {
1610
+ return "unrelated";
1611
+ }
1612
+ const oldId = getId(oldNode.attrs);
1613
+ const newId = getId(newNode.attrs);
1614
+ if (newId) {
1615
+ if (oldId) {
1616
+ return oldId === newId ? "same" : "unrelated";
1617
+ } else {
1618
+ return "unrelated";
1619
+ }
1620
+ } else if (oldId) {
1621
+ return "unrelated";
1622
+ }
1623
+ return oldNode.tag === newNode.tag ? "comparable" : "unrelated";
1624
+ } else {
1625
+ return "unrelated";
1626
+ }
1627
+ }
1628
+ case "View":
1629
+ if (oldNode._type === "ViewNode") {
1630
+ return newNode.nodeId.eq(oldNode.nodeId) ? "same" : "unrelated";
1631
+ } else {
1632
+ return "unrelated";
1633
+ }
1634
+ case "Component":
1635
+ if (oldNode._type === "ComponentNode") {
1636
+ return newNode.nodeId.eq(oldNode.nodeId) ? "same" : "unrelated";
1637
+ } else {
1638
+ return "unrelated";
1639
+ }
1640
+ }
1641
+ }
1642
+ function foldTree(root, ancestorComponentPath, init, f) {
1643
+ let acc = init;
1644
+ function visit(n, ancestorComponentPath2, moveThroushSiblings) {
1645
+ acc = f(n, ancestorComponentPath2, acc);
1646
+ switch (n._type) {
1647
+ case "InternalTextNode": {
1648
+ break;
1649
+ }
1650
+ case "InternalElementNode":
1651
+ case "ViewNode": {
1652
+ if (n.firstChild) {
1653
+ visit(n.firstChild, ancestorComponentPath2, true);
1654
+ }
1655
+ break;
1656
+ }
1657
+ case "ComponentNode": {
1658
+ if (n.firstChild) {
1659
+ visit(
1660
+ n.firstChild,
1661
+ componentPath(n.nodeId, ancestorComponentPath2),
1662
+ true
1663
+ );
1664
+ }
1665
+ break;
1666
+ }
1667
+ default: {
1668
+ exhaustivenessCheck(n);
1669
+ }
1670
+ }
1671
+ if (moveThroushSiblings) {
1672
+ let next = n.nextSibling;
1673
+ while (next) {
1674
+ visit(next, ancestorComponentPath2, false);
1675
+ next = next.nextSibling;
1676
+ }
1677
+ }
1678
+ }
1679
+ visit(root, ancestorComponentPath, false);
1680
+ return acc;
1681
+ }
1682
+
1683
+ // src/internal/styleregistry.ts
1684
+ function realStyleRegistry() {
1685
+ const injected = /* @__PURE__ */ new Set();
1686
+ return {
1687
+ injectStyle(css) {
1688
+ if (injected.has(css)) return;
1689
+ injected.add(css);
1690
+ const el = globalThis.document.createElement("style");
1691
+ el.textContent = css;
1692
+ globalThis.document.head.appendChild(el);
1693
+ }
1694
+ };
1695
+ }
1696
+ function noopStyleRegistry() {
1697
+ return {
1698
+ injectStyle(_css) {
1699
+ }
1700
+ };
1701
+ }
1702
+
1703
+ // src/db/testdb.ts
1704
+ function serializeIdbKey(key) {
1705
+ if (key instanceof Date) return key.toISOString();
1706
+ return JSON.stringify(key);
1707
+ }
1708
+ var TestDatabaseData = class {
1709
+ constructor(databases) {
1710
+ __publicField(this, "_databases");
1711
+ this._databases = databases;
1712
+ }
1713
+ records(db, store) {
1714
+ return [...this._databases.get(db)?.get(store)?.records.values() ?? []];
1715
+ }
1716
+ getByKey(db, store, key) {
1717
+ return this._databases.get(db)?.get(store)?.records.get(serializeIdbKey(key));
1718
+ }
1719
+ size(db, store) {
1720
+ return this._databases.get(db)?.get(store)?.records.size ?? 0;
1721
+ }
1722
+ hasDatabase(db) {
1723
+ return this._databases.has(db);
1724
+ }
1725
+ hasStore(db, store) {
1726
+ return this._databases.get(db)?.has(store) ?? false;
1727
+ }
1728
+ };
1729
+
1730
+ // src/scroll.ts
1731
+ function scrollElementError(selector) {
1732
+ return { _type: "ScrollElementError", cause: "ElementNotFound", selector };
1733
+ }
1734
+
1735
+ // src/internal/testinginterpreter.ts
1736
+ function seededRandom(callIndex) {
1737
+ let h = callIndex + 1 | 0;
1738
+ h = Math.imul(h ^ h >>> 16, 73244475);
1739
+ h = Math.imul(h ^ h >>> 16, 73244475);
1740
+ h ^= h >>> 16;
1741
+ return (h >>> 0) / 4294967296;
1742
+ }
1743
+ function snapshotTestData(acc) {
1744
+ return {
1745
+ window: {
1746
+ alerts: [...acc.window.alerts],
1747
+ confirms: [...acc.window.confirms],
1748
+ confirmResponses: [...acc.window.confirmResponses],
1749
+ prompts: [...acc.window.prompts],
1750
+ promptResponses: [...acc.window.promptResponses]
1751
+ },
1752
+ navigator: { appBadgeOperations: [...acc.navigator.appBadgeOperations] },
1753
+ clipboard: {
1754
+ content: acc.clipboard.content,
1755
+ writes: [...acc.clipboard.writes]
1756
+ },
1757
+ document: { title: acc.document.title },
1758
+ http: { interactions: [...acc.http.interactions] },
1759
+ clock: { calls: [...acc.clock.calls] },
1760
+ random: { calls: [...acc.random.calls] },
1761
+ console: { logs: [...acc.console.logs] },
1762
+ db: new TestDatabaseData(
1763
+ new Map(
1764
+ [...acc.db.databases.entries()].map(([dbName, db]) => [
1765
+ dbName,
1766
+ new Map(
1767
+ [...db.stores.entries()].map(([storeName, store]) => [
1768
+ storeName,
1769
+ {
1770
+ keyPath: store.keyPath,
1771
+ records: new Map(store.records),
1772
+ autoIncrementCounter: store.autoIncrementCounter
1773
+ }
1774
+ ])
1775
+ )
1776
+ ])
1777
+ )
1778
+ ),
1779
+ storage: {
1780
+ local: new Map(acc.storage.local),
1781
+ session: new Map(acc.storage.session)
1782
+ },
1783
+ navigation: {
1784
+ locationHistory: [...acc.navigation.locationHistory]
1785
+ },
1786
+ scroll: {
1787
+ windowScrolls: [...acc.scroll.windowScrolls],
1788
+ elementScrolls: [...acc.scroll.elementScrolls]
1789
+ },
1790
+ downloads: [...acc.downloads],
1791
+ broadcasts: {
1792
+ sent: [...acc.broadcasts.sent]
1793
+ },
1794
+ custom: acc.custom
1795
+ };
1796
+ }
1797
+ var TestScheduler = class {
1798
+ constructor() {
1799
+ __publicField(this, "virtualNow", 0);
1800
+ __publicField(this, "pending", []);
1801
+ }
1802
+ schedule(task, taskCallbacks, tskId, initialDelayInMillis, repeatIntervalInMillis) {
1803
+ this.pending.push({
1804
+ task,
1805
+ taskCallbacks,
1806
+ taskId: tskId,
1807
+ nextFireAtMillis: this.virtualNow + initialDelayInMillis,
1808
+ repeatIntervalInMillis
1809
+ });
1810
+ }
1811
+ cancel(tskId) {
1812
+ this.pending = this.pending.filter((t) => t.taskId !== tskId);
1813
+ }
1814
+ advance(ms, acc, broadcastReg, window2) {
1815
+ const targetTime = this.virtualNow + ms;
1816
+ while (true) {
1817
+ const next = this.pending.filter((t) => t.nextFireAtMillis <= targetTime).sort((a, b) => a.nextFireAtMillis - b.nextFireAtMillis)[0];
1818
+ if (!next) break;
1819
+ this.virtualNow = next.nextFireAtMillis;
1820
+ if (next.repeatIntervalInMillis !== void 0) {
1821
+ next.nextFireAtMillis = this.virtualNow + next.repeatIntervalInMillis;
1822
+ } else {
1823
+ this.pending = this.pending.filter((t) => t !== next);
1824
+ }
1825
+ testingInterpreter(
1826
+ next.task,
1827
+ next.taskCallbacks,
1828
+ new TaskRegistry(),
1829
+ acc,
1830
+ broadcastReg,
1831
+ this,
1832
+ window2
1833
+ );
1834
+ }
1835
+ this.virtualNow = targetTime;
1836
+ }
1837
+ };
1838
+ var BroadcastChannelRegistry = class {
1839
+ constructor() {
1840
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1841
+ __publicField(this, "channels", /* @__PURE__ */ new Map());
1842
+ }
1843
+ subscribe(channel, handler, tskId) {
1844
+ if (!this.channels.has(channel)) this.channels.set(channel, []);
1845
+ this.channels.get(channel).push({ handler, taskId: tskId });
1846
+ }
1847
+ unsubscribe(tskId) {
1848
+ for (const [ch, subs] of this.channels) {
1849
+ this.channels.set(ch, subs.filter((s) => s.taskId !== tskId));
1850
+ }
1851
+ }
1852
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1853
+ deliver(channel, message, acc, broadcastReg, scheduler, window2) {
1854
+ const subs = this.channels.get(channel) ?? [];
1855
+ for (const sub of subs) {
1856
+ testingInterpreter(
1857
+ sub.handler(message),
1858
+ {
1859
+ onComplete() {
1860
+ },
1861
+ onFireEvent() {
1862
+ },
1863
+ getState() {
1864
+ return void 0;
1865
+ },
1866
+ setState() {
1867
+ }
1868
+ },
1869
+ new TaskRegistry(),
1870
+ acc,
1871
+ broadcastReg,
1872
+ scheduler,
1873
+ window2
1874
+ );
1875
+ }
1876
+ }
1877
+ };
1878
+ function makeTestingInterpreter(initialData, window2, config) {
1879
+ const acc = {
1880
+ window: {
1881
+ alerts: [...initialData.window.alerts],
1882
+ confirms: [...initialData.window.confirms],
1883
+ confirmResponses: [...initialData.window.confirmResponses],
1884
+ prompts: [...initialData.window.prompts],
1885
+ promptResponses: [...initialData.window.promptResponses]
1886
+ },
1887
+ navigator: {
1888
+ appBadgeOperations: [...initialData.navigator.appBadgeOperations]
1889
+ },
1890
+ clipboard: {
1891
+ content: initialData.clipboard.content,
1892
+ writes: [...initialData.clipboard.writes]
1893
+ },
1894
+ document: { title: initialData.document.title },
1895
+ http: { interactions: [...initialData.http.interactions] },
1896
+ clock: { calls: [...initialData.clock.calls] },
1897
+ random: { calls: [...initialData.random.calls] },
1898
+ console: { logs: [...initialData.console.logs] },
1899
+ storage: {
1900
+ local: new Map(initialData.storage.local),
1901
+ session: new Map(initialData.storage.session)
1902
+ },
1903
+ navigation: {
1904
+ locationHistory: [...initialData.navigation.locationHistory]
1905
+ },
1906
+ scroll: {
1907
+ windowScrolls: [...initialData.scroll.windowScrolls],
1908
+ elementScrolls: [...initialData.scroll.elementScrolls]
1909
+ },
1910
+ downloads: [...initialData.downloads],
1911
+ broadcasts: {
1912
+ sent: [...initialData.broadcasts.sent]
1913
+ },
1914
+ db: {
1915
+ databases: new Map(
1916
+ [...initialData.db._databases.entries()].map(([dbName, dbSnap]) => [
1917
+ dbName,
1918
+ {
1919
+ stores: new Map(
1920
+ [...dbSnap.entries()].map(([storeName, storeSnap]) => [
1921
+ storeName,
1922
+ {
1923
+ keyPath: storeSnap.keyPath,
1924
+ records: new Map(storeSnap.records),
1925
+ autoIncrementCounter: storeSnap.autoIncrementCounter
1926
+ }
1927
+ ])
1928
+ )
1929
+ }
1930
+ ])
1931
+ )
1932
+ },
1933
+ uuidSequence: [],
1934
+ custom: initialData.custom
1935
+ };
1936
+ const scheduler = new TestScheduler();
1937
+ const broadcastReg = new BroadcastChannelRegistry();
1938
+ const interpreter = (effect, callbacks, taskRegistry) => {
1939
+ testingInterpreter(effect, callbacks, taskRegistry, acc, broadcastReg, scheduler, window2, config);
1940
+ };
1941
+ return {
1942
+ interpreter,
1943
+ getData: () => snapshotTestData(acc),
1944
+ advanceTime: (ms) => scheduler.advance(ms, acc, broadcastReg, window2),
1945
+ fireBroadcast: (channel, message) => broadcastReg.deliver(channel, message, acc, broadcastReg, scheduler, window2)
1946
+ };
1947
+ }
1948
+ function testingInterpreter(effect, callbacks, taskRegistry, acc, broadcastReg, scheduler, window2, config) {
1949
+ switch (effect._type) {
1950
+ case "Return": {
1951
+ callbacks.onComplete(effect.value);
1952
+ return;
1953
+ }
1954
+ case "FlatMap": {
1955
+ testingInterpreter(
1956
+ effect.effect,
1957
+ {
1958
+ ...callbacks,
1959
+ onComplete(result) {
1960
+ testingInterpreter(
1961
+ effect.next(result),
1962
+ callbacks,
1963
+ taskRegistry,
1964
+ acc,
1965
+ broadcastReg,
1966
+ scheduler,
1967
+ window2,
1968
+ config
1969
+ );
1970
+ }
1971
+ },
1972
+ taskRegistry,
1973
+ acc,
1974
+ broadcastReg,
1975
+ scheduler,
1976
+ window2,
1977
+ config
1978
+ );
1979
+ return;
1980
+ }
1981
+ case "Suspend": {
1982
+ switch (effect.operation._type) {
1983
+ case "Alert": {
1984
+ acc.window.alerts.push(effect.operation.input);
1985
+ callbacks.onComplete(void 0);
1986
+ return;
1987
+ }
1988
+ case "Confirm": {
1989
+ const response = acc.window.confirmResponses.shift() ?? false;
1990
+ acc.window.confirms.push({
1991
+ message: effect.operation.input,
1992
+ response
1993
+ });
1994
+ callbacks.onComplete(response);
1995
+ return;
1996
+ }
1997
+ case "Prompt": {
1998
+ const response = acc.window.promptResponses.shift() ?? null;
1999
+ acc.window.prompts.push({
2000
+ message: effect.operation.input.message,
2001
+ default: effect.operation.input.default,
2002
+ response
2003
+ });
2004
+ callbacks.onComplete(response);
2005
+ return;
2006
+ }
2007
+ case "GetNotificationPermission": {
2008
+ callbacks.onComplete(permission("default"));
2009
+ return;
2010
+ }
2011
+ case "RequestNotificationPermission": {
2012
+ callbacks.onComplete(permission("granted"));
2013
+ return;
2014
+ }
2015
+ case "GetLocation": {
2016
+ const history2 = acc.navigation.locationHistory;
2017
+ callbacks.onComplete(
2018
+ history2.length > 0 ? history2[history2.length - 1] : emptyLocation
2019
+ );
2020
+ return;
2021
+ }
2022
+ case "Async": {
2023
+ testingInterpreter(
2024
+ effect.operation.input.effect,
2025
+ {
2026
+ ...callbacks,
2027
+ onComplete() {
2028
+ callbacks.onComplete(void 0);
2029
+ }
2030
+ },
2031
+ taskRegistry,
2032
+ acc,
2033
+ broadcastReg,
2034
+ scheduler,
2035
+ window2,
2036
+ config
2037
+ );
2038
+ return;
2039
+ }
2040
+ case "CancelTask": {
2041
+ scheduler.cancel(effect.operation.input);
2042
+ broadcastReg.unsubscribe(effect.operation.input);
2043
+ taskRegistry.remove(effect.operation.input);
2044
+ callbacks.onComplete(void 0);
2045
+ return;
2046
+ }
2047
+ case "ClearAppBadge": {
2048
+ acc.navigator.appBadgeOperations.push({ _type: "ClearAppBadge" });
2049
+ callbacks.onComplete(void 0);
2050
+ return;
2051
+ }
2052
+ case "SetAppBadge": {
2053
+ acc.navigator.appBadgeOperations.push({
2054
+ _type: "SetAppBadge",
2055
+ count: effect.operation.input
2056
+ });
2057
+ callbacks.onComplete(void 0);
2058
+ return;
2059
+ }
2060
+ case "SetDocumentTitle": {
2061
+ acc.document.title = effect.operation.input;
2062
+ callbacks.onComplete(void 0);
2063
+ return;
2064
+ }
2065
+ case "ReadClipboard": {
2066
+ callbacks.onComplete(success(acc.clipboard.content));
2067
+ return;
2068
+ }
2069
+ case "WriteClipboard": {
2070
+ acc.clipboard.content = effect.operation.input;
2071
+ acc.clipboard.writes.push(effect.operation.input);
2072
+ callbacks.onComplete(success(void 0));
2073
+ return;
2074
+ }
2075
+ case "Download": {
2076
+ acc.downloads.push(effect.operation.input);
2077
+ callbacks.onComplete(void 0);
2078
+ return;
2079
+ }
2080
+ case "PostBroadcastMessage": {
2081
+ acc.broadcasts.sent.push(effect.operation.input);
2082
+ callbacks.onComplete(void 0);
2083
+ return;
2084
+ }
2085
+ case "SubscribeToBroadcastChannel": {
2086
+ const { channel, handler, taskId: tskIdInput } = effect.operation.input;
2087
+ const tskId = tskIdInput ?? taskId(generateUuid());
2088
+ if (tskIdInput) {
2089
+ broadcastReg.unsubscribe(tskIdInput);
2090
+ }
2091
+ broadcastReg.subscribe(channel, handler, tskId);
2092
+ callbacks.onComplete(tskId);
2093
+ return;
2094
+ }
2095
+ case "ScrollWindow": {
2096
+ acc.scroll.windowScrolls.push(effect.operation.input);
2097
+ callbacks.onComplete(void 0);
2098
+ return;
2099
+ }
2100
+ case "ScrollElement": {
2101
+ const { selector, options } = effect.operation.input;
2102
+ const el = window2.document.querySelector(selector);
2103
+ if (el === null) {
2104
+ callbacks.onComplete(failure(scrollElementError(selector)));
2105
+ } else {
2106
+ el.scrollTo(options);
2107
+ acc.scroll.elementScrolls.push({ selector, options });
2108
+ callbacks.onComplete(success(void 0));
2109
+ }
2110
+ return;
2111
+ }
2112
+ case "FireEvent": {
2113
+ callbacks.onFireEvent(effect.operation.input);
2114
+ callbacks.onComplete(void 0);
2115
+ return;
2116
+ }
2117
+ case "GetState": {
2118
+ callbacks.onComplete(callbacks.getState());
2119
+ return;
2120
+ }
2121
+ case "GetTime": {
2122
+ const callIndex = acc.clock.calls.length;
2123
+ const scripted = config?.clock.values ?? [];
2124
+ const value = callIndex < scripted.length ? scripted[callIndex] : /* @__PURE__ */ new Date(0);
2125
+ acc.clock.calls.push(value);
2126
+ callbacks.onComplete(value);
2127
+ return;
2128
+ }
2129
+ case "GetRandom": {
2130
+ const callIndex = acc.random.calls.length;
2131
+ const scripted = config?.random.values ?? [];
2132
+ const value = callIndex < scripted.length ? scripted[callIndex] : seededRandom(callIndex);
2133
+ acc.random.calls.push(value);
2134
+ callbacks.onComplete(value);
2135
+ return;
2136
+ }
2137
+ case "Log": {
2138
+ acc.console.logs.push(effect.operation.input);
2139
+ callbacks.onComplete(void 0);
2140
+ return;
2141
+ }
2142
+ case "MakeHttpRequest": {
2143
+ callbacks.onComplete(void 0);
2144
+ return;
2145
+ }
2146
+ case "Product": {
2147
+ let resultA = "__ctrl_fx_unset_result";
2148
+ let resultB = "__ctrl_fx_unset_result";
2149
+ testingInterpreter(
2150
+ effect.operation.input[0],
2151
+ {
2152
+ ...callbacks,
2153
+ onComplete(a) {
2154
+ resultA = a;
2155
+ if (resultB !== "__ctrl_fx_unset_result") {
2156
+ callbacks.onComplete([resultA, resultB]);
2157
+ }
2158
+ }
2159
+ },
2160
+ taskRegistry,
2161
+ acc,
2162
+ broadcastReg,
2163
+ scheduler,
2164
+ window2,
2165
+ config
2166
+ );
2167
+ testingInterpreter(
2168
+ effect.operation.input[1],
2169
+ {
2170
+ ...callbacks,
2171
+ onComplete(b) {
2172
+ resultB = b;
2173
+ if (resultA !== "__ctrl_fx_unset_result") {
2174
+ callbacks.onComplete([resultA, resultB]);
2175
+ }
2176
+ }
2177
+ },
2178
+ taskRegistry,
2179
+ acc,
2180
+ broadcastReg,
2181
+ scheduler,
2182
+ window2,
2183
+ config
2184
+ );
2185
+ return;
2186
+ }
2187
+ case "ScheduleTask": {
2188
+ const tskId = effect.operation.input.taskId ?? taskId(generateUuid());
2189
+ if (effect.operation.input.taskId) {
2190
+ scheduler.cancel(effect.operation.input.taskId);
2191
+ }
2192
+ scheduler.schedule(
2193
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2194
+ effect.operation.input.task,
2195
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2196
+ { ...callbacks, onComplete() {
2197
+ } },
2198
+ tskId,
2199
+ effect.operation.input.initialDelayInMillis,
2200
+ effect.operation.input.repeatIntervalInMillis
2201
+ );
2202
+ callbacks.onComplete(tskId);
2203
+ return;
2204
+ }
2205
+ case "SetTimeout": {
2206
+ callbacks.onComplete(void 0);
2207
+ return;
2208
+ }
2209
+ case "PushState": {
2210
+ acc.navigation.locationHistory.push(effect.operation.input);
2211
+ callbacks.onComplete(void 0);
2212
+ return;
2213
+ }
2214
+ case "ReplaceState": {
2215
+ if (acc.navigation.locationHistory.length > 0) {
2216
+ acc.navigation.locationHistory[acc.navigation.locationHistory.length - 1] = effect.operation.input;
2217
+ } else {
2218
+ acc.navigation.locationHistory.push(effect.operation.input);
2219
+ }
2220
+ callbacks.onComplete(void 0);
2221
+ return;
2222
+ }
2223
+ case "Go": {
2224
+ const delta = effect.operation.input;
2225
+ const newLen = acc.navigation.locationHistory.length + delta;
2226
+ if (newLen > 0) {
2227
+ acc.navigation.locationHistory.length = newLen;
2228
+ }
2229
+ callbacks.onComplete(void 0);
2230
+ return;
2231
+ }
2232
+ case "GenerateUuid": {
2233
+ const uuidLen = acc.uuidSequence.length;
2234
+ const nextUuid = uuidFromString(`${uuidLen}`);
2235
+ acc.uuidSequence.push(nextUuid);
2236
+ callbacks.onComplete(nextUuid);
2237
+ return;
2238
+ }
2239
+ case "GetStorageItem": {
2240
+ const store = acc.storage[effect.operation.input.storage];
2241
+ callbacks.onComplete(
2242
+ store.get(effect.operation.input.key) ?? null
2243
+ );
2244
+ return;
2245
+ }
2246
+ case "SetStorageItem": {
2247
+ acc.storage[effect.operation.input.storage].set(
2248
+ effect.operation.input.key,
2249
+ effect.operation.input.value
2250
+ );
2251
+ callbacks.onComplete(void 0);
2252
+ return;
2253
+ }
2254
+ case "RemoveStorageItem": {
2255
+ acc.storage[effect.operation.input.storage].delete(
2256
+ effect.operation.input.key
2257
+ );
2258
+ callbacks.onComplete(void 0);
2259
+ return;
2260
+ }
2261
+ case "ClearStorage": {
2262
+ acc.storage[effect.operation.input.storage].clear();
2263
+ callbacks.onComplete(void 0);
2264
+ return;
2265
+ }
2266
+ case "OpenDatabase": {
2267
+ const { db, setup } = effect.operation.input;
2268
+ if (!acc.db.databases.has(db)) {
2269
+ const mutableDb = { stores: /* @__PURE__ */ new Map() };
2270
+ acc.db.databases.set(db, mutableDb);
2271
+ run2(setup(dbVersion(0)), makeSetupInterpreter(mutableDb), (_r) => {
2272
+ callbacks.onComplete(success(void 0));
2273
+ });
2274
+ } else {
2275
+ callbacks.onComplete(success(void 0));
2276
+ }
2277
+ return;
2278
+ }
2279
+ case "RunDbTransaction": {
2280
+ const { db, effect: dbEffect } = effect.operation.input;
2281
+ const mutableDb = acc.db.databases.get(db);
2282
+ if (!mutableDb) {
2283
+ callbacks.onComplete(
2284
+ failure({
2285
+ _type: "UnexpectedError",
2286
+ message: `Database '${db}' is not open`
2287
+ })
2288
+ );
2289
+ return;
2290
+ }
2291
+ run(dbEffect, makeDbInterpreter(mutableDb), (_r) => {
2292
+ callbacks.onComplete(success(void 0));
2293
+ });
2294
+ return;
2295
+ }
2296
+ case "UpdateState": {
2297
+ callbacks.setState(effect.operation.input(callbacks.getState()));
2298
+ callbacks.onComplete(void 0);
2299
+ return;
2300
+ }
2301
+ case "CustomEffect": {
2302
+ const { args, test } = effect.operation.input;
2303
+ const result = test(args, acc.custom);
2304
+ acc.custom = result.data;
2305
+ callbacks.onComplete(result.out);
2306
+ return;
2307
+ }
2308
+ default: {
2309
+ exhaustivenessCheck(effect.operation);
2310
+ }
2311
+ }
2312
+ break;
2313
+ }
2314
+ default: {
2315
+ exhaustivenessCheck(effect);
2316
+ }
2317
+ }
2318
+ }
2319
+ function makeSetupInterpreter(mutableDb) {
2320
+ return (op, onComplete) => {
2321
+ switch (op._type) {
2322
+ case "CreateObjectStore": {
2323
+ mutableDb.stores.set(op.objectStore, {
2324
+ keyPath: op.keyPath,
2325
+ records: /* @__PURE__ */ new Map(),
2326
+ autoIncrementCounter: 1
2327
+ });
2328
+ onComplete(void 0);
2329
+ return;
2330
+ }
2331
+ case "CreateIndex": {
2332
+ onComplete(void 0);
2333
+ return;
2334
+ }
2335
+ case "DeleteObjectStore": {
2336
+ mutableDb.stores.delete(op.objectStore);
2337
+ onComplete(void 0);
2338
+ return;
2339
+ }
2340
+ case "DeleteIndex": {
2341
+ onComplete(void 0);
2342
+ return;
2343
+ }
2344
+ case "GetObjectStoreNames": {
2345
+ onComplete([...mutableDb.stores.keys()]);
2346
+ return;
2347
+ }
2348
+ case "GetIndexNames": {
2349
+ onComplete([]);
2350
+ return;
2351
+ }
2352
+ case "RunDbEffect": {
2353
+ run(
2354
+ op.effect,
2355
+ makeDbInterpreter(mutableDb),
2356
+ (_r) => onComplete(void 0)
2357
+ );
2358
+ return;
2359
+ }
2360
+ }
2361
+ };
2362
+ }
2363
+ function makeDbInterpreter(mutableDb) {
2364
+ return (op, onComplete) => {
2365
+ switch (op._type) {
2366
+ case "Add":
2367
+ case "Put": {
2368
+ const store = requireStore(mutableDb, op.objectStore);
2369
+ store.records.set(extractKey(op.value, store), op.value);
2370
+ onComplete(void 0);
2371
+ return;
2372
+ }
2373
+ case "Get": {
2374
+ const store = requireStore(mutableDb, op.objectStore);
2375
+ onComplete(store.records.get(serializeIdbKey(op.key)));
2376
+ return;
2377
+ }
2378
+ case "GetAll": {
2379
+ const store = requireStore(mutableDb, op.objectStore);
2380
+ onComplete([...store.records.values()]);
2381
+ return;
2382
+ }
2383
+ case "Delete": {
2384
+ const store = requireStore(mutableDb, op.objectStore);
2385
+ store.records.delete(serializeIdbKey(op.key));
2386
+ onComplete(void 0);
2387
+ return;
2388
+ }
2389
+ case "Count": {
2390
+ const store = requireStore(mutableDb, op.objectStore);
2391
+ onComplete(store.records.size);
2392
+ return;
2393
+ }
2394
+ }
2395
+ };
2396
+ }
2397
+ function requireStore(mutableDb, storeName) {
2398
+ const store = mutableDb.stores.get(storeName);
2399
+ if (!store) throw new Error(`Object store '${storeName}' not found`);
2400
+ return store;
2401
+ }
2402
+ function extractKey(value, store) {
2403
+ const { keyPath } = store;
2404
+ switch (keyPath._type) {
2405
+ case "AutoIncrement":
2406
+ return String(store.autoIncrementCounter++);
2407
+ case "Manual": {
2408
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
2409
+ throw new Error(`Cannot extract key '${keyPath.value}' from non-object`);
2410
+ }
2411
+ return JSON.stringify(value[keyPath.value]);
2412
+ }
2413
+ case "Multi": {
2414
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
2415
+ throw new Error("Cannot extract compound key from non-object");
2416
+ }
2417
+ const obj = value;
2418
+ return JSON.stringify(
2419
+ [keyPath.first, keyPath.second, ...keyPath.rest].map((f) => obj[f])
2420
+ );
2421
+ }
2422
+ }
2423
+ }
2424
+
2425
+ // src/internal/testing.ts
2426
+ var Node = class {
2427
+ constructor() {
2428
+ __publicField(this, "previousSibling", null);
2429
+ __publicField(this, "nextSibling", null);
2430
+ __publicField(this, "parentNode", null);
2431
+ }
2432
+ remove() {
2433
+ if (this.previousSibling) {
2434
+ this.previousSibling.nextSibling = this.nextSibling;
2435
+ } else {
2436
+ if (this.parentNode) {
2437
+ this.parentNode.firstChild = this.nextSibling;
2438
+ }
2439
+ }
2440
+ if (this.nextSibling) {
2441
+ this.nextSibling.previousSibling = this.previousSibling;
2442
+ }
2443
+ this.parentNode = null;
2444
+ }
2445
+ insertBefore(node) {
2446
+ const newNode = typeof node === "string" ? new TextNode(node) : node;
2447
+ if (this.previousSibling) {
2448
+ this.previousSibling.nextSibling = newNode;
2449
+ newNode.previousSibling = this.previousSibling;
2450
+ } else {
2451
+ if (this.parentNode) {
2452
+ this.parentNode.firstChild = newNode;
2453
+ }
2454
+ }
2455
+ newNode.nextSibling = this;
2456
+ newNode.parentNode = this.parentNode;
2457
+ this.previousSibling = newNode;
2458
+ return newNode;
2459
+ }
2460
+ insertAfter(node) {
2461
+ const newNode = typeof node === "string" ? new TextNode(node) : node;
2462
+ if (this.nextSibling) {
2463
+ this.nextSibling.previousSibling = newNode;
2464
+ newNode.nextSibling = this.nextSibling;
2465
+ }
2466
+ newNode.previousSibling = this;
2467
+ newNode.parentNode = this.parentNode;
2468
+ this.nextSibling = newNode;
2469
+ return newNode;
2470
+ }
2471
+ before(...nodes) {
2472
+ let current = this;
2473
+ for (let i = nodes.length - 1; i >= 0; i--) {
2474
+ current = current.insertBefore(nodes[i]);
2475
+ }
2476
+ }
2477
+ after(...nodes) {
2478
+ let current = this;
2479
+ for (let i = 0; i < nodes.length; i++) {
2480
+ current = current.insertAfter(nodes[i]);
2481
+ }
2482
+ }
2483
+ };
2484
+ var TextNode = class extends Node {
2485
+ constructor(nodeValue) {
2486
+ super();
2487
+ __publicField(this, "nodeValue");
2488
+ this.nodeValue = nodeValue;
2489
+ }
2490
+ };
2491
+ var ElementNode = class _ElementNode extends Node {
2492
+ constructor(tagName) {
2493
+ super();
2494
+ __publicField(this, "tagName");
2495
+ __publicField(this, "attributes", {});
2496
+ __publicField(this, "firstChild", null);
2497
+ __publicField(this, "scrollTop", 0);
2498
+ __publicField(this, "scrollLeft", 0);
2499
+ __publicField(this, "eventListeners", []);
2500
+ this.tagName = tagName;
2501
+ }
2502
+ scrollTo(options) {
2503
+ if (options.top !== void 0) this.scrollTop = options.top;
2504
+ if (options.left !== void 0) this.scrollLeft = options.left;
2505
+ }
2506
+ removeAttribute(name) {
2507
+ delete this.attributes[name];
2508
+ }
2509
+ setAttribute(name, value) {
2510
+ this.attributes[name] = value;
2511
+ }
2512
+ lastChild() {
2513
+ let child = this.firstChild;
2514
+ while (child && child.nextSibling) {
2515
+ child = child.nextSibling;
2516
+ }
2517
+ return child;
2518
+ }
2519
+ append(...nodes) {
2520
+ if (nodes.length === 0) {
2521
+ return;
2522
+ }
2523
+ let last = this.lastChild();
2524
+ if (!last) {
2525
+ this.firstChild = nodes[0];
2526
+ this.firstChild.parentNode = this;
2527
+ nodes = nodes.slice(1);
2528
+ last = this.firstChild;
2529
+ }
2530
+ last.after(...nodes);
2531
+ }
2532
+ prepend(...nodes) {
2533
+ if (this.firstChild) {
2534
+ this.firstChild.before(...nodes);
2535
+ } else {
2536
+ this.append(...nodes);
2537
+ }
2538
+ }
2539
+ addEventListener(type, listener, options) {
2540
+ this.eventListeners.push({
2541
+ type,
2542
+ listener,
2543
+ options
2544
+ });
2545
+ }
2546
+ removeEventListener(type, listener, useCapture) {
2547
+ this.eventListeners = this.eventListeners.filter((el) => {
2548
+ return !(el.type === type && el.listener === listener && (useCapture ? el.options.capture : !el.options.capture));
2549
+ });
2550
+ }
2551
+ matchesSelector(selector) {
2552
+ const parts = parseSelector(selector);
2553
+ return matchesSelectorSequence(this, parts);
2554
+ }
2555
+ traverseAndCollect(selector, result) {
2556
+ if (this.matchesSelector(selector)) {
2557
+ result.push(this);
2558
+ }
2559
+ let child = this.firstChild;
2560
+ while (child) {
2561
+ if (child instanceof _ElementNode) {
2562
+ child.traverseAndCollect(selector, result);
2563
+ }
2564
+ child = child.nextSibling;
2565
+ }
2566
+ }
2567
+ querySelector(selector) {
2568
+ const results = this.querySelectorAll(selector);
2569
+ return results.length > 0 ? results[0] : null;
2570
+ }
2571
+ querySelectorAll(selector) {
2572
+ const result = [];
2573
+ this.traverseAndCollect(selector, result);
2574
+ return result;
2575
+ }
2576
+ };
2577
+ var Document = class {
2578
+ constructor() {
2579
+ __publicField(this, "head", this.createElement("head"));
2580
+ __publicField(this, "body", this.createElement("body"));
2581
+ __publicField(this, "visibilityState", "visible");
2582
+ __publicField(this, "documentEventListeners", []);
2583
+ }
2584
+ createTextNode(text) {
2585
+ return new TextNode(text);
2586
+ }
2587
+ getElementById(id) {
2588
+ return this.body.querySelector("#" + id);
2589
+ }
2590
+ querySelector(selector) {
2591
+ return this.body.querySelector(selector);
2592
+ }
2593
+ createElement(tag) {
2594
+ return new ElementNode(tag);
2595
+ }
2596
+ addEventListener(type, listener, options) {
2597
+ this.documentEventListeners.push({ type, listener, options });
2598
+ }
2599
+ removeEventListener(type, listener, useCapture) {
2600
+ this.documentEventListeners = this.documentEventListeners.filter(
2601
+ (el) => !(el.type === type && el.listener === listener && (useCapture ? el.options.capture : !el.options.capture))
2602
+ );
2603
+ }
2604
+ };
2605
+ var Window = class {
2606
+ constructor() {
2607
+ __publicField(this, "document", new Document());
2608
+ __publicField(this, "innerWidth", 1024);
2609
+ __publicField(this, "innerHeight", 768);
2610
+ __publicField(this, "location", {
2611
+ protocol: "http",
2612
+ hostname: "localhost",
2613
+ port: "8080",
2614
+ pathname: "/",
2615
+ search: "",
2616
+ hash: ""
2617
+ });
2618
+ __publicField(this, "eventListeners", []);
2619
+ }
2620
+ addEventListener(type, listener, options) {
2621
+ this.eventListeners.push({
2622
+ type,
2623
+ listener,
2624
+ options
2625
+ });
2626
+ }
2627
+ removeEventListener(type, listener, useCapture) {
2628
+ this.eventListeners = this.eventListeners.filter((el) => {
2629
+ return !(el.type === type && el.listener === listener && (useCapture ? el.options.capture : !el.options.capture));
2630
+ });
2631
+ }
2632
+ };
2633
+ function serializeElement(elem) {
2634
+ const attrs = Object.entries(elem.attributes).map(
2635
+ ([name, value]) => ({ name, value })
2636
+ );
2637
+ const childNodes = [];
2638
+ let child = elem.firstChild;
2639
+ while (child) {
2640
+ if (child instanceof TextNode) {
2641
+ childNodes.push(child.nodeValue);
2642
+ } else if (child instanceof ElementNode) {
2643
+ childNodes.push(serializeElement(child));
2644
+ }
2645
+ child = child.nextSibling;
2646
+ }
2647
+ return { tag: elem.tagName, attrs, childNodes };
2648
+ }
2649
+ function serializeDom(doc) {
2650
+ return {
2651
+ head: serializeElement(doc.head),
2652
+ body: serializeElement(doc.body)
2653
+ };
2654
+ }
2655
+ function dispatchClick(element) {
2656
+ const syntheticEvent = {
2657
+ type: "click",
2658
+ target: null,
2659
+ eventPhase: 2,
2660
+ defaultPrevented: false,
2661
+ propagationStopped: false,
2662
+ preventDefault() {
2663
+ this.defaultPrevented = true;
2664
+ },
2665
+ stopImmediatePropagation() {
2666
+ this.propagationStopped = true;
2667
+ }
2668
+ };
2669
+ for (const entry of element.eventListeners) {
2670
+ if (entry.type === "click") {
2671
+ entry.listener(syntheticEvent);
2672
+ if (syntheticEvent.propagationStopped) {
2673
+ break;
2674
+ }
2675
+ }
2676
+ }
2677
+ }
2678
+ function dispatchSubmit(element) {
2679
+ const syntheticEvent = {
2680
+ type: "submit",
2681
+ target: null,
2682
+ eventPhase: 2,
2683
+ defaultPrevented: false,
2684
+ propagationStopped: false,
2685
+ preventDefault() {
2686
+ this.defaultPrevented = true;
2687
+ },
2688
+ stopImmediatePropagation() {
2689
+ this.propagationStopped = true;
2690
+ }
2691
+ };
2692
+ for (const entry of element.eventListeners) {
2693
+ if (entry.type === "submit") {
2694
+ entry.listener(syntheticEvent);
2695
+ if (syntheticEvent.propagationStopped) {
2696
+ break;
2697
+ }
2698
+ }
2699
+ }
2700
+ }
2701
+ function dispatchCustomEvent(element, eventName, detail) {
2702
+ const syntheticEvent = {
2703
+ type: eventName,
2704
+ detail,
2705
+ target: null,
2706
+ eventPhase: 2,
2707
+ defaultPrevented: false,
2708
+ propagationStopped: false,
2709
+ preventDefault() {
2710
+ this.defaultPrevented = true;
2711
+ },
2712
+ stopImmediatePropagation() {
2713
+ this.propagationStopped = true;
2714
+ }
2715
+ };
2716
+ for (const entry of element.eventListeners) {
2717
+ if (entry.type === eventName) {
2718
+ entry.listener(syntheticEvent);
2719
+ if (syntheticEvent.propagationStopped) {
2720
+ break;
2721
+ }
2722
+ }
2723
+ }
2724
+ }
2725
+ function dispatchTextInput(element, value) {
2726
+ const syntheticEvent = {
2727
+ type: "input",
2728
+ target: { tagName: "INPUT", value, selectionStart: value.length, selectionEnd: value.length },
2729
+ eventPhase: 2,
2730
+ defaultPrevented: false,
2731
+ propagationStopped: false,
2732
+ preventDefault() {
2733
+ this.defaultPrevented = true;
2734
+ },
2735
+ stopImmediatePropagation() {
2736
+ this.propagationStopped = true;
2737
+ }
2738
+ };
2739
+ for (const entry of element.eventListeners) {
2740
+ if (entry.type === "input") {
2741
+ entry.listener(syntheticEvent);
2742
+ if (syntheticEvent.propagationStopped) {
2743
+ break;
2744
+ }
2745
+ }
2746
+ }
2747
+ }
2748
+ function querySelectorAllInApp(window2, selector) {
2749
+ const appRoot = window2.document.getElementById("app");
2750
+ if (!appRoot) return [];
2751
+ const results = [];
2752
+ let child = appRoot.firstChild;
2753
+ while (child) {
2754
+ if (child instanceof ElementNode) {
2755
+ results.push(...child.querySelectorAll(selector));
2756
+ }
2757
+ child = child.nextSibling;
2758
+ }
2759
+ return results;
2760
+ }
2761
+ function makeWindow() {
2762
+ const win = new Window();
2763
+ const root = win.document.createElement("div");
2764
+ root.setAttribute("id", "app");
2765
+ win.document.body.append(root);
2766
+ return win;
2767
+ }
2768
+ var TestableComponentImpl = class _TestableComponentImpl {
2769
+ constructor(component, state, config, data, dom, window2) {
2770
+ __publicField(this, "config");
2771
+ __publicField(this, "data");
2772
+ __publicField(this, "dom");
2773
+ __publicField(this, "_component");
2774
+ __publicField(this, "state");
2775
+ __publicField(this, "_window");
2776
+ this._component = component;
2777
+ this.state = state;
2778
+ this.config = config;
2779
+ this.data = data;
2780
+ this.dom = dom;
2781
+ this._window = window2;
2782
+ }
2783
+ withConfig(config) {
2784
+ return new _TestableComponentImpl(
2785
+ this._component,
2786
+ this.state,
2787
+ config,
2788
+ this.data,
2789
+ this.dom,
2790
+ this._window
2791
+ );
2792
+ }
2793
+ withData(data) {
2794
+ return new _TestableComponentImpl(
2795
+ this._component,
2796
+ this.state,
2797
+ this.config,
2798
+ data,
2799
+ this.dom,
2800
+ this._window
2801
+ );
2802
+ }
2803
+ run(...interactions) {
2804
+ const win = makeWindow();
2805
+ const { interpreter, getData, advanceTime: advanceTime2, fireBroadcast } = makeTestingInterpreter(
2806
+ this.data,
2807
+ win,
2808
+ this.config
2809
+ );
2810
+ const currentState = this.state;
2811
+ const wrappedComponent = {
2812
+ ...this._component,
2813
+ initialState: (_params) => pure(currentState)
2814
+ };
2815
+ const appNode = win.document.getElementById("app");
2816
+ const mgr = new ComponentManager(
2817
+ wrappedComponent,
2818
+ appNode,
2819
+ win,
2820
+ interpreter,
2821
+ noopStyleRegistry()
2822
+ );
2823
+ for (const interaction of interactions) {
2824
+ if ("_type" in interaction) {
2825
+ switch (interaction._type) {
2826
+ case "AdvanceTime":
2827
+ advanceTime2(interaction.milliseconds);
2828
+ break;
2829
+ case "ReceiveBroadcast":
2830
+ fireBroadcast(interaction.channel, interaction.message);
2831
+ break;
2832
+ case "FireCustomEvent": {
2833
+ const qs = interaction.selector;
2834
+ const cssSelector = qs.selector;
2835
+ const results = win.document.body.querySelectorAll(cssSelector);
2836
+ results.forEach(
2837
+ (el) => dispatchCustomEvent(el, interaction.eventName, interaction.detail)
2838
+ );
2839
+ break;
2840
+ }
2841
+ case "Submit": {
2842
+ const qs = interaction.selector;
2843
+ const cssSelector = qs.selector;
2844
+ const results = win.document.body.querySelectorAll(cssSelector);
2845
+ results.forEach((el) => dispatchSubmit(el));
2846
+ break;
2847
+ }
2848
+ case "TextInput": {
2849
+ const qs = interaction.selector;
2850
+ const cssSelector = qs.selector;
2851
+ const results = win.document.body.querySelectorAll(cssSelector);
2852
+ results.forEach((el) => dispatchTextInput(el, interaction.value));
2853
+ break;
2854
+ }
2855
+ default:
2856
+ exhaustivenessCheck(interaction);
2857
+ }
2858
+ } else {
2859
+ switch (interaction.type) {
2860
+ case "Click": {
2861
+ const qs = interaction.selector;
2862
+ const cssSelector = qs.selector;
2863
+ const results = win.document.body.querySelectorAll(cssSelector);
2864
+ switch (qs._type) {
2865
+ case "ExactlyOne":
2866
+ if (results.length !== 1)
2867
+ throw new Error(
2868
+ `Expected exactly one element matching '${cssSelector}', found ${results.length}`
2869
+ );
2870
+ break;
2871
+ case "OneOrMore":
2872
+ if (results.length === 0)
2873
+ throw new Error(
2874
+ `Expected at least one element matching '${cssSelector}', found 0`
2875
+ );
2876
+ break;
2877
+ case "ZeroOrOne":
2878
+ if (results.length > 1)
2879
+ throw new Error(
2880
+ `Expected zero or one element matching '${cssSelector}', found ${results.length}`
2881
+ );
2882
+ break;
2883
+ case "ZeroOrMore":
2884
+ break;
2885
+ default:
2886
+ exhaustivenessCheck(qs);
2887
+ }
2888
+ results.forEach((el) => dispatchClick(el));
2889
+ break;
2890
+ }
2891
+ default:
2892
+ exhaustivenessCheck(interaction.type);
2893
+ }
2894
+ }
2895
+ }
2896
+ const stateAfter = mgr.getRootState();
2897
+ const newState = stateAfter._type === "Ready" ? stateAfter.value : currentState;
2898
+ return new _TestableComponentImpl(
2899
+ this._component,
2900
+ newState,
2901
+ this.config,
2902
+ getData(),
2903
+ serializeDom(win.document),
2904
+ win
2905
+ );
2906
+ }
2907
+ find(selector) {
2908
+ return querySelectorAllInApp(this._window, selector).map(serializeElement);
2909
+ }
2910
+ findOne(selector) {
2911
+ const results = querySelectorAllInApp(this._window, selector);
2912
+ if (results.length !== 1)
2913
+ throw new Error(
2914
+ `findOne('${selector}'): expected 1 element, found ${results.length}`
2915
+ );
2916
+ return serializeElement(results[0]);
2917
+ }
2918
+ findOneOrMore(selector) {
2919
+ const results = querySelectorAllInApp(this._window, selector);
2920
+ if (results.length === 0)
2921
+ throw new Error(
2922
+ `findOneOrMore('${selector}'): expected at least 1 element, found 0`
2923
+ );
2924
+ return results.map(serializeElement);
2925
+ }
2926
+ findMaybeOne(selector) {
2927
+ const results = querySelectorAllInApp(this._window, selector);
2928
+ if (results.length > 1)
2929
+ throw new Error(
2930
+ `findMaybeOne('${selector}'): expected 0 or 1 elements, found ${results.length}`
2931
+ );
2932
+ return results.length === 0 ? void 0 : serializeElement(results[0]);
2933
+ }
2934
+ };
2935
+ function testableComponent(component, config, data) {
2936
+ const initialData = data || newTestData();
2937
+ const win = makeWindow();
2938
+ const { interpreter, getData } = makeTestingInterpreter(
2939
+ initialData,
2940
+ win,
2941
+ config
2942
+ );
2943
+ const appNode = win.document.getElementById("app");
2944
+ const mgr = new ComponentManager(
2945
+ component,
2946
+ appNode,
2947
+ win,
2948
+ interpreter,
2949
+ noopStyleRegistry()
2950
+ );
2951
+ const stateAfter = mgr.getRootState();
2952
+ const currentState = stateAfter._type === "Ready" ? stateAfter.value : void 0;
2953
+ return new TestableComponentImpl(
2954
+ component,
2955
+ currentState,
2956
+ config,
2957
+ getData(),
2958
+ serializeDom(win.document),
2959
+ win
2960
+ );
2961
+ }
2962
+ function parseSelector(selector) {
2963
+ const parts = selector.trim().split(/\s+/);
2964
+ return parts.map(parsePart);
2965
+ }
2966
+ function parsePart(part) {
2967
+ const attrs = {};
2968
+ const classes = [];
2969
+ let tag = void 0;
2970
+ if (part.startsWith("#")) {
2971
+ attrs["id"] = part.substring(1);
2972
+ return { tag, classes, attrs };
2973
+ }
2974
+ if (part.startsWith(".")) {
2975
+ classes.push(part.substring(1));
2976
+ return { tag, classes, attrs };
2977
+ }
2978
+ const attrRegex = /\[([^\]=]+)=([^\]]+)\]/g;
2979
+ let match;
2980
+ while ((match = attrRegex.exec(part)) !== null) {
2981
+ attrs[match[1]] = match[2];
2982
+ }
2983
+ const withoutAttrs = part.replace(attrRegex, "").trim();
2984
+ const dotParts = withoutAttrs.split(".");
2985
+ tag = dotParts[0] || void 0;
2986
+ classes.push(...dotParts.slice(1));
2987
+ return { tag, classes, attrs };
2988
+ }
2989
+ function matchesPart(element, part) {
2990
+ if (part.tag && element.tagName.toLowerCase() !== part.tag.toLowerCase()) {
2991
+ return false;
2992
+ }
2993
+ for (const cls of part.classes) {
2994
+ const elementClasses = (element.attributes["class"] || "").split(/\s+/);
2995
+ if (!elementClasses.includes(cls)) {
2996
+ return false;
2997
+ }
2998
+ }
2999
+ for (const [attr2, value] of Object.entries(part.attrs)) {
3000
+ if (element.attributes[attr2] !== value) {
3001
+ return false;
3002
+ }
3003
+ }
3004
+ return true;
3005
+ }
3006
+ function matchesSelectorSequence(element, parts) {
3007
+ if (parts.length === 0) return true;
3008
+ if (!matchesPart(element, parts[parts.length - 1])) {
3009
+ return false;
3010
+ }
3011
+ if (parts.length === 1) return true;
3012
+ let ancestor = element.parentNode;
3013
+ for (let i = parts.length - 2; i >= 0; i--) {
3014
+ let found = false;
3015
+ while (ancestor) {
3016
+ if (matchesPart(ancestor, parts[i])) {
3017
+ found = true;
3018
+ ancestor = ancestor.parentNode;
3019
+ break;
3020
+ }
3021
+ ancestor = ancestor.parentNode;
3022
+ }
3023
+ if (!found) return false;
3024
+ }
3025
+ return true;
3026
+ }
3027
+
3028
+ // src/internal/index.ts
3029
+ function componentPath(id, parentComponentPath) {
3030
+ return {
3031
+ parentComponentPath,
3032
+ nodeId: typeof id === "string" ? nodeId(id) : id,
3033
+ format() {
3034
+ return formatComponentPath(this);
3035
+ }
3036
+ };
3037
+ }
3038
+ function formatComponentPath(path) {
3039
+ const parentStr = path.parentComponentPath ? formatComponentPath(path.parentComponentPath) : "";
3040
+ return parentStr + "/" + encodeURIComponent(path.nodeId.value);
3041
+ }
3042
+ function generateUuid() {
3043
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
3044
+ return crypto.randomUUID();
3045
+ } else {
3046
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
3047
+ const r = Math.random() * 16 | 0;
3048
+ const v = c == "x" ? r : r & 3 | 8;
3049
+ return v.toString(16);
3050
+ });
3051
+ }
3052
+ }
3053
+ function testComponent(component, config, data) {
3054
+ return testableComponent(component, config ?? defaultTestConfig(), data);
3055
+ }
3056
+
3057
+ // src/clipboard.ts
3058
+ function clipboardError(cause, message) {
3059
+ return { _type: "ClipboardError", cause, message };
3060
+ }
3061
+
3062
+ // src/internal/clipboard.ts
3063
+ function writeClipboard(text, callback) {
3064
+ if (!globalThis.navigator?.clipboard?.writeText) {
3065
+ callback(
3066
+ failure(clipboardError("NotSupported", "Clipboard API not available"))
3067
+ );
3068
+ return;
3069
+ }
3070
+ globalThis.navigator.clipboard.writeText(text).then(() => callback(success(void 0))).catch((err) => callback(failure(toClipboardError(err))));
3071
+ }
3072
+ function readClipboard(callback) {
3073
+ if (!globalThis.navigator?.clipboard?.readText) {
3074
+ callback(
3075
+ failure(clipboardError("NotSupported", "Clipboard API not available"))
3076
+ );
3077
+ return;
3078
+ }
3079
+ globalThis.navigator.clipboard.readText().then((text) => callback(success(text))).catch((err) => callback(failure(toClipboardError(err))));
3080
+ }
3081
+ function toClipboardError(err) {
3082
+ if (err instanceof DOMException && err.name === "NotAllowedError") {
3083
+ return clipboardError("NotAllowed", err.message);
3084
+ } else if (err instanceof Error) {
3085
+ return clipboardError("UnexpectedError", err.message);
3086
+ } else {
3087
+ return clipboardError("UnexpectedError", `Unexpected clipboard error: ${err}`);
3088
+ }
3089
+ }
3090
+
3091
+ // src/db/errors.ts
3092
+ function unexpectedError(params) {
3093
+ return {
3094
+ _type: "UnexpectedError",
3095
+ cause: params.cause,
3096
+ message: params.message
3097
+ };
3098
+ }
3099
+ function notFoundError(cause) {
3100
+ return { _type: "NotFoundError", cause };
3101
+ }
3102
+ function constraintError(cause) {
3103
+ return { _type: "ConstraintError", cause };
3104
+ }
3105
+
3106
+ // src/internal/dbmgr.ts
3107
+ var DbManager = class {
3108
+ constructor() {
3109
+ __publicField(this, "openDatabases", /* @__PURE__ */ new Map());
3110
+ }
3111
+ open(db, version, setup, callback) {
3112
+ const idbFactory = globalThis.indexedDB;
3113
+ if (!idbFactory) {
3114
+ return callback(
3115
+ failure(unexpectedError({ message: "IndexedDB not supported" }))
3116
+ );
3117
+ }
3118
+ const openRequest = idbFactory.open(db, version);
3119
+ openRequest.onblocked = (_event) => {
3120
+ window.alert(
3121
+ "A database upgrade is blocked. Please close other tabs with this app."
3122
+ );
3123
+ };
3124
+ let upgradeError = void 0;
3125
+ openRequest.onupgradeneeded = (versionChangeEvent) => {
3126
+ const idbDb = openRequest.result;
3127
+ const tx = openRequest.transaction;
3128
+ if (!tx) {
3129
+ console.warn("Expected idb transaction to be non-null during upgrade");
3130
+ return;
3131
+ }
3132
+ const oldVersion = dbVersion(versionChangeEvent.oldVersion);
3133
+ const effect = setup(oldVersion);
3134
+ try {
3135
+ run2(effect, setupInterpreter(idbDb, tx), (_result) => {
3136
+ });
3137
+ } catch (err) {
3138
+ if (err instanceof Error) upgradeError = err;
3139
+ throw err;
3140
+ }
3141
+ };
3142
+ openRequest.onerror = (event) => {
3143
+ if (upgradeError instanceof DOMException) {
3144
+ if (upgradeError.name === "ConstraintError") {
3145
+ return callback(failure(constraintError(upgradeError)));
3146
+ }
3147
+ if (upgradeError.name === "NotFoundError") {
3148
+ return callback(failure(notFoundError(upgradeError)));
3149
+ }
3150
+ }
3151
+ return callback(
3152
+ failure(
3153
+ unexpectedError({
3154
+ message: extractMessage(event),
3155
+ cause: upgradeError
3156
+ })
3157
+ )
3158
+ );
3159
+ };
3160
+ openRequest.onsuccess = (_event) => {
3161
+ this.openDatabases.set(db, openRequest.result);
3162
+ callback(success(void 0));
3163
+ };
3164
+ }
3165
+ runTransaction(db, objectStores, mode, effect, callback) {
3166
+ const idbDb = this.openDatabases.get(db);
3167
+ if (!idbDb) {
3168
+ return callback(
3169
+ failure(unexpectedError({ message: `Database '${db}' is not open` }))
3170
+ );
3171
+ }
3172
+ const tx = idbDb.transaction([...objectStores], mode);
3173
+ let txError = null;
3174
+ tx.onerror = (_event) => {
3175
+ txError = tx.error;
3176
+ };
3177
+ tx.onabort = (_event) => {
3178
+ if (txError !== null) {
3179
+ if (txError.name === "ConstraintError") {
3180
+ return callback(failure(constraintError(txError)));
3181
+ }
3182
+ if (txError.name === "NotFoundError") {
3183
+ return callback(failure(notFoundError(txError)));
3184
+ }
3185
+ return callback(failure(unexpectedError({ cause: txError })));
3186
+ }
3187
+ return callback(
3188
+ failure(unexpectedError({ message: "Transaction aborted" }))
3189
+ );
3190
+ };
3191
+ tx.oncomplete = (_event) => {
3192
+ callback(success(void 0));
3193
+ };
3194
+ run(effect, dbInterpreter(tx), (_result) => {
3195
+ });
3196
+ }
3197
+ };
3198
+ function setupInterpreter(db, tx) {
3199
+ return (op, onComplete) => {
3200
+ switch (op._type) {
3201
+ case "CreateObjectStore": {
3202
+ db.createObjectStore(op.objectStore, objectStoreKeyOptions(op.keyPath));
3203
+ onComplete(void 0);
3204
+ return;
3205
+ }
3206
+ case "CreateIndex": {
3207
+ const store = tx.objectStore(op.objectStore);
3208
+ store.createIndex(op.index, indexKeyPathValue(op.keyPath), {
3209
+ unique: op.unique
3210
+ });
3211
+ onComplete(void 0);
3212
+ return;
3213
+ }
3214
+ case "DeleteObjectStore": {
3215
+ db.deleteObjectStore(op.objectStore);
3216
+ onComplete(void 0);
3217
+ return;
3218
+ }
3219
+ case "DeleteIndex": {
3220
+ const store = tx.objectStore(op.objectStore);
3221
+ store.deleteIndex(op.index);
3222
+ onComplete(void 0);
3223
+ return;
3224
+ }
3225
+ case "GetObjectStoreNames": {
3226
+ onComplete(Array.from(db.objectStoreNames));
3227
+ return;
3228
+ }
3229
+ case "GetIndexNames": {
3230
+ const store = tx.objectStore(op.objectStore);
3231
+ onComplete(Array.from(store.indexNames));
3232
+ return;
3233
+ }
3234
+ case "RunDbEffect": {
3235
+ run(
3236
+ op.effect,
3237
+ dbInterpreter(tx),
3238
+ (_result) => onComplete(void 0)
3239
+ );
3240
+ return;
3241
+ }
3242
+ }
3243
+ };
3244
+ }
3245
+ function dbInterpreter(tx) {
3246
+ return (op, onComplete) => {
3247
+ switch (op._type) {
3248
+ case "Add": {
3249
+ const req = tx.objectStore(op.objectStore).add(op.value);
3250
+ req.onsuccess = () => onComplete(void 0);
3251
+ return;
3252
+ }
3253
+ case "Put": {
3254
+ const req = tx.objectStore(op.objectStore).put(op.value);
3255
+ req.onsuccess = () => onComplete(void 0);
3256
+ return;
3257
+ }
3258
+ case "Get": {
3259
+ const req = tx.objectStore(op.objectStore).get(op.key);
3260
+ req.onsuccess = () => onComplete(req.result ?? void 0);
3261
+ return;
3262
+ }
3263
+ case "GetAll": {
3264
+ const req = tx.objectStore(op.objectStore).getAll();
3265
+ req.onsuccess = () => onComplete(req.result);
3266
+ return;
3267
+ }
3268
+ case "Delete": {
3269
+ const req = tx.objectStore(op.objectStore).delete(op.key);
3270
+ req.onsuccess = () => onComplete(void 0);
3271
+ return;
3272
+ }
3273
+ case "Count": {
3274
+ const req = tx.objectStore(op.objectStore).count();
3275
+ req.onsuccess = () => onComplete(req.result);
3276
+ return;
3277
+ }
3278
+ }
3279
+ };
3280
+ }
3281
+ function objectStoreKeyOptions(keyPath) {
3282
+ switch (keyPath._type) {
3283
+ case "AutoIncrement":
3284
+ return { keyPath: keyPath.value, autoIncrement: true };
3285
+ case "Manual":
3286
+ return { keyPath: keyPath.value, autoIncrement: false };
3287
+ case "Multi":
3288
+ return {
3289
+ keyPath: [keyPath.first, keyPath.second, ...keyPath.rest],
3290
+ autoIncrement: false
3291
+ };
3292
+ }
3293
+ }
3294
+ function indexKeyPathValue(keyPath) {
3295
+ switch (keyPath._type) {
3296
+ case "SingleIndex":
3297
+ return keyPath.value;
3298
+ case "MultiIndex":
3299
+ return [keyPath.first, keyPath.second, ...keyPath.rest];
3300
+ }
3301
+ }
3302
+ function extractMessage(event) {
3303
+ const m = event.message;
3304
+ if (typeof m === "string") return m;
3305
+ if (m != null) return `${m}`;
3306
+ return void 0;
3307
+ }
3308
+
3309
+ // src/internal/net.ts
3310
+ function makeHttpRequest(request, callback) {
3311
+ console.log("About to fetch " + request.uri);
3312
+ globalThis.fetch(request.uri, {
3313
+ method: request.method,
3314
+ headers: request.headers,
3315
+ body: request.body
3316
+ }).catch((err) => handleError(err, callback)).then((response) => {
3317
+ if (response) {
3318
+ response.arrayBuffer().catch((err) => handleError(err, callback)).then((ab) => {
3319
+ if (ab) {
3320
+ callback(
3321
+ success({
3322
+ headers: convertHeaders(response.headers),
3323
+ body: new ResponseBody(ab)
3324
+ })
3325
+ );
3326
+ }
3327
+ });
3328
+ }
3329
+ });
3330
+ }
3331
+ function handleError(err, callback) {
3332
+ if (err instanceof DOMException && err.name === "AbortError") {
3333
+ callback(
3334
+ failure({
3335
+ _type: "RequestError",
3336
+ cause: "RequestAborted",
3337
+ message: err.message
3338
+ })
3339
+ );
3340
+ } else if (err instanceof TypeError) {
3341
+ callback(
3342
+ failure({
3343
+ _type: "RequestError",
3344
+ cause: "NetworkFailure",
3345
+ message: err.message
3346
+ })
3347
+ );
3348
+ } else if (err instanceof Error) {
3349
+ callback(
3350
+ failure({
3351
+ _type: "RequestError",
3352
+ cause: "UnexpectedError",
3353
+ message: err.message
3354
+ })
3355
+ );
3356
+ } else {
3357
+ callback(
3358
+ failure({
3359
+ _type: "RequestError",
3360
+ cause: "UnexpectedError",
3361
+ message: `Unexpected request error: ${err}`
3362
+ })
3363
+ );
3364
+ }
3365
+ }
3366
+ function convertHeaders(headers) {
3367
+ const result = {};
3368
+ for (const [key, value] of headers) {
3369
+ result[key] = value;
3370
+ }
3371
+ return result;
3372
+ }
3373
+
3374
+ // src/internal/dominterpreter.ts
3375
+ var dbManager = new DbManager();
3376
+ function domInterpreter(effect, callbacks, taskRegistry) {
3377
+ switch (effect._type) {
3378
+ case "Return": {
3379
+ callbacks.onComplete(effect.value);
3380
+ return;
3381
+ }
3382
+ case "FlatMap": {
3383
+ domInterpreter(
3384
+ effect.effect,
3385
+ {
3386
+ ...callbacks,
3387
+ onComplete(result) {
3388
+ domInterpreter(effect.next(result), callbacks, taskRegistry);
3389
+ }
3390
+ },
3391
+ taskRegistry
3392
+ );
3393
+ return;
3394
+ }
3395
+ case "Suspend": {
3396
+ switch (effect.operation._type) {
3397
+ case "Alert": {
3398
+ globalThis.window.alert(effect.operation.input);
3399
+ callbacks.onComplete(void 0);
3400
+ return;
3401
+ }
3402
+ case "Confirm": {
3403
+ callbacks.onComplete(
3404
+ globalThis.window.confirm(effect.operation.input)
3405
+ );
3406
+ return;
3407
+ }
3408
+ case "Prompt": {
3409
+ callbacks.onComplete(
3410
+ globalThis.window.prompt(
3411
+ effect.operation.input.message,
3412
+ effect.operation.input.default
3413
+ )
3414
+ );
3415
+ return;
3416
+ }
3417
+ case "GetNotificationPermission": {
3418
+ callbacks.onComplete(
3419
+ permission(globalThis.Notification.permission)
3420
+ );
3421
+ return;
3422
+ }
3423
+ case "RequestNotificationPermission": {
3424
+ globalThis.Notification.requestPermission().then((perm) => {
3425
+ callbacks.onComplete(permission(perm));
3426
+ });
3427
+ return;
3428
+ }
3429
+ case "GetLocation": {
3430
+ const loc = globalThis.window.location;
3431
+ callbacks.onComplete(
3432
+ parseInternalLocation(loc.pathname + loc.search + loc.hash)
3433
+ );
3434
+ return;
3435
+ }
3436
+ case "Async": {
3437
+ const { effect: innerEffect, delayInMillis } = effect.operation.input;
3438
+ setTimeout(() => {
3439
+ domInterpreter(
3440
+ innerEffect,
3441
+ { ...callbacks, onComplete() {
3442
+ } },
3443
+ taskRegistry
3444
+ );
3445
+ }, delayInMillis);
3446
+ callbacks.onComplete(void 0);
3447
+ return;
3448
+ }
3449
+ case "CancelTask": {
3450
+ taskRegistry.cancel(effect.operation.input);
3451
+ callbacks.onComplete(void 0);
3452
+ return;
3453
+ }
3454
+ case "ClearAppBadge": {
3455
+ if (globalThis.navigator.clearAppBadge) {
3456
+ globalThis.navigator.clearAppBadge();
3457
+ }
3458
+ callbacks.onComplete(void 0);
3459
+ return;
3460
+ }
3461
+ case "SetAppBadge": {
3462
+ if (globalThis.navigator.setAppBadge) {
3463
+ globalThis.navigator.setAppBadge(effect.operation.input);
3464
+ }
3465
+ callbacks.onComplete(void 0);
3466
+ return;
3467
+ }
3468
+ case "SetDocumentTitle": {
3469
+ globalThis.document.title = effect.operation.input;
3470
+ callbacks.onComplete(void 0);
3471
+ return;
3472
+ }
3473
+ case "FireEvent": {
3474
+ callbacks.onFireEvent(effect.operation.input);
3475
+ callbacks.onComplete(void 0);
3476
+ return;
3477
+ }
3478
+ case "GetState": {
3479
+ callbacks.onComplete(callbacks.getState());
3480
+ return;
3481
+ }
3482
+ case "GetTime": {
3483
+ callbacks.onComplete(/* @__PURE__ */ new Date());
3484
+ return;
3485
+ }
3486
+ case "GetRandom": {
3487
+ callbacks.onComplete(Math.random());
3488
+ return;
3489
+ }
3490
+ case "MakeHttpRequest": {
3491
+ makeHttpRequest(
3492
+ effect.operation.input,
3493
+ (result) => callbacks.onComplete(result)
3494
+ );
3495
+ return;
3496
+ }
3497
+ case "Log": {
3498
+ console.log(effect.operation.input);
3499
+ callbacks.onComplete(void 0);
3500
+ return;
3501
+ }
3502
+ case "Product": {
3503
+ let resultA = "__ctrl_fx_unset_result";
3504
+ let resultB = "__ctrl_fx_unset_result";
3505
+ domInterpreter(
3506
+ effect.operation.input[0],
3507
+ {
3508
+ ...callbacks,
3509
+ onComplete(a) {
3510
+ resultA = a;
3511
+ if (resultB !== "__ctrl_fx_unset_result") {
3512
+ callbacks.onComplete([resultA, resultB]);
3513
+ }
3514
+ }
3515
+ },
3516
+ taskRegistry
3517
+ );
3518
+ domInterpreter(
3519
+ effect.operation.input[1],
3520
+ {
3521
+ ...callbacks,
3522
+ onComplete(b) {
3523
+ resultB = b;
3524
+ if (resultA !== "__ctrl_fx_unset_result") {
3525
+ callbacks.onComplete([resultA, resultB]);
3526
+ }
3527
+ }
3528
+ },
3529
+ taskRegistry
3530
+ );
3531
+ return;
3532
+ }
3533
+ case "ScheduleTask": {
3534
+ let tskId;
3535
+ if (effect.operation.input.taskId) {
3536
+ taskRegistry.cancel(effect.operation.input.taskId);
3537
+ tskId = effect.operation.input.taskId;
3538
+ }
3539
+ if (effect.operation.input.repeatIntervalInMillis === void 0) {
3540
+ const timerId = globalThis.setTimeout(() => {
3541
+ domInterpreter(
3542
+ effect.operation.input.task,
3543
+ { ...callbacks, onComplete() {
3544
+ } },
3545
+ taskRegistry
3546
+ );
3547
+ }, effect.operation.input.initialDelayInMillis);
3548
+ tskId ||= taskId(generateUuid());
3549
+ taskRegistry.set(tskId, () => clearTimeout(timerId));
3550
+ } else if (effect.operation.input.initialDelayInMillis === effect.operation.input.repeatIntervalInMillis) {
3551
+ const timerId = globalThis.setInterval(() => {
3552
+ domInterpreter(
3553
+ effect.operation.input.task,
3554
+ { ...callbacks, onComplete() {
3555
+ } },
3556
+ taskRegistry
3557
+ );
3558
+ }, effect.operation.input.repeatIntervalInMillis);
3559
+ tskId ||= taskId(generateUuid());
3560
+ taskRegistry.set(tskId, () => clearInterval(timerId));
3561
+ } else {
3562
+ tskId ||= taskId(generateUuid());
3563
+ const timerId = globalThis.setTimeout(() => {
3564
+ domInterpreter(
3565
+ effect.operation.input.task,
3566
+ {
3567
+ ...callbacks,
3568
+ onComplete() {
3569
+ const newTimerId = globalThis.setInterval(() => {
3570
+ domInterpreter(
3571
+ effect.operation.input.task,
3572
+ { ...callbacks, onComplete() {
3573
+ } },
3574
+ taskRegistry
3575
+ );
3576
+ }, effect.operation.input.repeatIntervalInMillis);
3577
+ taskRegistry.set(tskId, () => clearInterval(newTimerId));
3578
+ }
3579
+ },
3580
+ taskRegistry
3581
+ );
3582
+ }, effect.operation.input.initialDelayInMillis);
3583
+ taskRegistry.set(tskId, () => clearTimeout(timerId));
3584
+ }
3585
+ callbacks.onComplete(tskId);
3586
+ return;
3587
+ }
3588
+ case "SetTimeout": {
3589
+ setTimeout(() => {
3590
+ callbacks.onComplete(void 0);
3591
+ }, effect.operation.input);
3592
+ return;
3593
+ }
3594
+ case "PushState": {
3595
+ const url = formatLocation(effect.operation.input);
3596
+ history.pushState(null, "", url);
3597
+ window.dispatchEvent(new Event("locationchange"));
3598
+ callbacks.onComplete(void 0);
3599
+ return;
3600
+ }
3601
+ case "ReplaceState": {
3602
+ const url = formatLocation(effect.operation.input);
3603
+ history.replaceState(null, "", url);
3604
+ window.dispatchEvent(new Event("locationchange"));
3605
+ callbacks.onComplete(void 0);
3606
+ return;
3607
+ }
3608
+ case "Go": {
3609
+ history.go(effect.operation.input);
3610
+ callbacks.onComplete(void 0);
3611
+ return;
3612
+ }
3613
+ case "GetStorageItem": {
3614
+ const store = effect.operation.input.storage === "local" ? localStorage : sessionStorage;
3615
+ callbacks.onComplete(store.getItem(effect.operation.input.key));
3616
+ return;
3617
+ }
3618
+ case "SetStorageItem": {
3619
+ const store = effect.operation.input.storage === "local" ? localStorage : sessionStorage;
3620
+ store.setItem(
3621
+ effect.operation.input.key,
3622
+ effect.operation.input.value
3623
+ );
3624
+ callbacks.onComplete(void 0);
3625
+ return;
3626
+ }
3627
+ case "RemoveStorageItem": {
3628
+ const store = effect.operation.input.storage === "local" ? localStorage : sessionStorage;
3629
+ store.removeItem(effect.operation.input.key);
3630
+ callbacks.onComplete(void 0);
3631
+ return;
3632
+ }
3633
+ case "ClearStorage": {
3634
+ const store = effect.operation.input.storage === "local" ? localStorage : sessionStorage;
3635
+ store.clear();
3636
+ callbacks.onComplete(void 0);
3637
+ return;
3638
+ }
3639
+ case "OpenDatabase": {
3640
+ const { db, version, setup } = effect.operation.input;
3641
+ dbManager.open(db, version, setup, (result) => {
3642
+ callbacks.onComplete(result);
3643
+ });
3644
+ return;
3645
+ }
3646
+ case "RunDbTransaction": {
3647
+ const {
3648
+ db,
3649
+ objectStores,
3650
+ mode,
3651
+ effect: dbEffect
3652
+ } = effect.operation.input;
3653
+ dbManager.runTransaction(db, objectStores, mode, dbEffect, (result) => {
3654
+ callbacks.onComplete(result);
3655
+ });
3656
+ return;
3657
+ }
3658
+ case "UpdateState": {
3659
+ callbacks.setState(effect.operation.input(callbacks.getState()));
3660
+ callbacks.onComplete(void 0);
3661
+ return;
3662
+ }
3663
+ case "GenerateUuid": {
3664
+ callbacks.onComplete(generateUuid());
3665
+ return;
3666
+ }
3667
+ case "ReadClipboard": {
3668
+ readClipboard((result) => callbacks.onComplete(result));
3669
+ return;
3670
+ }
3671
+ case "WriteClipboard": {
3672
+ writeClipboard(
3673
+ effect.operation.input,
3674
+ (result) => callbacks.onComplete(result)
3675
+ );
3676
+ return;
3677
+ }
3678
+ case "Download": {
3679
+ const { filename, content, contentType } = effect.operation.input;
3680
+ const blob = new Blob([content], { type: contentType });
3681
+ const url = URL.createObjectURL(blob);
3682
+ const a = globalThis.document.createElement("a");
3683
+ a.href = url;
3684
+ a.download = filename;
3685
+ globalThis.document.body.appendChild(a);
3686
+ a.click();
3687
+ globalThis.document.body.removeChild(a);
3688
+ URL.revokeObjectURL(url);
3689
+ callbacks.onComplete(void 0);
3690
+ return;
3691
+ }
3692
+ case "PostBroadcastMessage": {
3693
+ const bc = new BroadcastChannel(effect.operation.input.channel);
3694
+ bc.postMessage(effect.operation.input.message);
3695
+ bc.close();
3696
+ callbacks.onComplete(void 0);
3697
+ return;
3698
+ }
3699
+ case "SubscribeToBroadcastChannel": {
3700
+ const { channel, handler, taskId: tskIdInput } = effect.operation.input;
3701
+ const tskId = tskIdInput ?? taskId(generateUuid());
3702
+ const bc = new BroadcastChannel(channel);
3703
+ bc.onmessage = (event) => {
3704
+ domInterpreter(
3705
+ handler(event.data),
3706
+ { ...callbacks, onComplete() {
3707
+ } },
3708
+ taskRegistry
3709
+ );
3710
+ };
3711
+ taskRegistry.set(tskId, () => bc.close());
3712
+ callbacks.onComplete(tskId);
3713
+ return;
3714
+ }
3715
+ case "ScrollWindow": {
3716
+ globalThis.window.scrollTo(effect.operation.input);
3717
+ callbacks.onComplete(void 0);
3718
+ return;
3719
+ }
3720
+ case "ScrollElement": {
3721
+ const { selector, options } = effect.operation.input;
3722
+ const el = globalThis.document.querySelector(selector);
3723
+ if (el === null) {
3724
+ callbacks.onComplete(failure(scrollElementError(selector)));
3725
+ } else {
3726
+ el.scrollTo(options);
3727
+ callbacks.onComplete(success(void 0));
3728
+ }
3729
+ return;
3730
+ }
3731
+ case "CustomEffect": {
3732
+ const { args, f } = effect.operation.input;
3733
+ const result = f(args);
3734
+ if (result instanceof Promise) {
3735
+ result.then((out) => callbacks.onComplete(out));
3736
+ } else {
3737
+ callbacks.onComplete(result);
3738
+ }
3739
+ return;
3740
+ }
3741
+ default:
3742
+ exhaustivenessCheck(effect.operation);
3743
+ }
3744
+ break;
3745
+ }
3746
+ default:
3747
+ exhaustivenessCheck(effect);
3748
+ }
3749
+ }
3750
+
3751
+ // src/index.ts
3752
+ function isState(effectOrState) {
3753
+ return !isEffect(effectOrState);
3754
+ }
3755
+ function toComponent(app, initialState) {
3756
+ const { pure: pure2 } = makeEffects();
3757
+ const initialStateEffect = isState(initialState) ? pure2(initialState) : initialState;
3758
+ const adjustedApp = (state) => {
3759
+ const nodeOrNodeGroup = app(state);
3760
+ if (isNodeGroup(nodeOrNodeGroup)) {
3761
+ return nodeOrNodeGroup;
3762
+ } else {
3763
+ return nodeGroup(nodeOrNodeGroup);
3764
+ }
3765
+ };
3766
+ return {
3767
+ _type: "Component",
3768
+ params: void 0,
3769
+ view: adjustedApp,
3770
+ initialState() {
3771
+ return initialStateEffect;
3772
+ }
3773
+ };
3774
+ }
3775
+ function manageApplication(app, initialState, mountId = "app") {
3776
+ const component = toComponent(app, initialState);
3777
+ return manageComponent(
3778
+ component,
3779
+ mountId,
3780
+ globalThis.window,
3781
+ domInterpreter,
3782
+ realStyleRegistry()
3783
+ );
3784
+ }
3785
+ function permission(value) {
3786
+ return {
3787
+ value,
3788
+ isGranted() {
3789
+ return value === "granted";
3790
+ },
3791
+ isDefault() {
3792
+ return value === "default";
3793
+ },
3794
+ isDenied() {
3795
+ return value === "denied";
3796
+ }
3797
+ };
3798
+ }
3799
+ function uuidFromString(input) {
3800
+ let hash = 0;
3801
+ for (let i = 0; i < input.length; i++) {
3802
+ hash = (hash << 5) - hash + input.charCodeAt(i) | 0;
3803
+ }
3804
+ const hex = (hash >>> 0).toString(16).padStart(8, "0");
3805
+ const repeated = hex.repeat(4);
3806
+ return [
3807
+ repeated.slice(0, 8),
3808
+ repeated.slice(8, 12),
3809
+ repeated.slice(12, 16),
3810
+ repeated.slice(16, 20),
3811
+ repeated.slice(20, 32)
3812
+ ].join("-");
3813
+ }
3814
+
3815
+ // src/testing.ts
3816
+ var clearAppBadge = {
3817
+ _type: "ClearAppBadge"
3818
+ };
3819
+ function setAppBadge(count) {
3820
+ return { _type: "SetAppBadge", count };
3821
+ }
3822
+ function appBadgeCount(operations) {
3823
+ if (operations.length > 0) {
3824
+ const op = operations[operations.length - 1];
3825
+ switch (op._type) {
3826
+ case "ClearAppBadge":
3827
+ return 0;
3828
+ case "SetAppBadge":
3829
+ return op.count;
3830
+ }
3831
+ } else {
3832
+ return 0;
3833
+ }
3834
+ }
3835
+ function defaultTestConfig() {
3836
+ return {
3837
+ http: {
3838
+ handler: (_request, _prior) => {
3839
+ throw new Error("No HTTP handler configured in TestConfig.");
3840
+ }
3841
+ },
3842
+ clock: {
3843
+ values: []
3844
+ },
3845
+ random: {
3846
+ values: []
3847
+ }
3848
+ };
3849
+ }
3850
+ function newTestData(custom) {
3851
+ return {
3852
+ window: {
3853
+ alerts: [],
3854
+ confirms: [],
3855
+ confirmResponses: [],
3856
+ prompts: [],
3857
+ promptResponses: []
3858
+ },
3859
+ navigator: {
3860
+ appBadgeOperations: []
3861
+ },
3862
+ clipboard: {
3863
+ content: "",
3864
+ writes: []
3865
+ },
3866
+ document: {
3867
+ title: null
3868
+ },
3869
+ http: {
3870
+ interactions: []
3871
+ },
3872
+ clock: {
3873
+ calls: []
3874
+ },
3875
+ random: {
3876
+ calls: []
3877
+ },
3878
+ console: {
3879
+ logs: []
3880
+ },
3881
+ db: new TestDatabaseData(/* @__PURE__ */ new Map()),
3882
+ storage: {
3883
+ local: /* @__PURE__ */ new Map(),
3884
+ session: /* @__PURE__ */ new Map()
3885
+ },
3886
+ navigation: {
3887
+ locationHistory: []
3888
+ },
3889
+ scroll: {
3890
+ windowScrolls: [],
3891
+ elementScrolls: []
3892
+ },
3893
+ downloads: [],
3894
+ broadcasts: {
3895
+ sent: []
3896
+ },
3897
+ custom: custom ?? {}
3898
+ };
3899
+ }
3900
+ function click(selector) {
3901
+ return {
3902
+ type: "Click",
3903
+ selector
3904
+ };
3905
+ }
3906
+ function advanceTime(milliseconds) {
3907
+ return {
3908
+ _type: "AdvanceTime",
3909
+ milliseconds
3910
+ };
3911
+ }
3912
+ function receiveBroadcast(channel, message) {
3913
+ return { _type: "ReceiveBroadcast", channel, message };
3914
+ }
3915
+ function fireCustomEvent(selector, eventName, detail) {
3916
+ return { _type: "FireCustomEvent", selector, eventName, detail };
3917
+ }
3918
+ function submit(selector) {
3919
+ return { _type: "Submit", selector };
3920
+ }
3921
+ function textInput(selector, value) {
3922
+ return { _type: "TextInput", selector, value };
3923
+ }
3924
+ function attr(name, value) {
3925
+ return { name, value };
3926
+ }
3927
+ function testComponent2(component, config, data) {
3928
+ return testComponent(component, config, data);
3929
+ }
3930
+ function testApplication(app, initialState, config, data) {
3931
+ const component = toComponent(app, initialState);
3932
+ return testComponent2(component, config, data);
3933
+ }
3934
+ function one(selector) {
3935
+ return {
3936
+ _type: "OneOrMore",
3937
+ selector
3938
+ };
3939
+ }
3940
+
3941
+ export {
3942
+ TestDatabaseData,
3943
+ clearAppBadge,
3944
+ setAppBadge,
3945
+ appBadgeCount,
3946
+ defaultTestConfig,
3947
+ newTestData,
3948
+ click,
3949
+ advanceTime,
3950
+ receiveBroadcast,
3951
+ fireCustomEvent,
3952
+ submit,
3953
+ textInput,
3954
+ attr,
3955
+ testComponent2 as testComponent,
3956
+ testApplication,
3957
+ one,
3958
+ ComponentManager,
3959
+ realStyleRegistry,
3960
+ domInterpreter,
3961
+ isState,
3962
+ toComponent,
3963
+ manageApplication,
3964
+ permission,
3965
+ uuidFromString
3966
+ };