@getforma/core 1.1.0 → 1.3.0

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 (45) hide show
  1. package/dist/{chunk-MIOMT2CB.js → chunk-AJP4OVCN.js} +29 -9
  2. package/dist/chunk-AJP4OVCN.js.map +1 -0
  3. package/dist/{chunk-XLVBYXOU.js → chunk-ETPJTPYD.js} +6 -3
  4. package/dist/chunk-ETPJTPYD.js.map +1 -0
  5. package/dist/{chunk-JRQNDXX7.cjs → chunk-FBM7V4NE.cjs} +68 -47
  6. package/dist/chunk-FBM7V4NE.cjs.map +1 -0
  7. package/dist/{chunk-W7OUWVRA.cjs → chunk-H7MDUHS5.cjs} +6 -2
  8. package/dist/chunk-H7MDUHS5.cjs.map +1 -0
  9. package/dist/{chunk-2Y5US35K.cjs → chunk-KUXNZ5MG.cjs} +12 -12
  10. package/dist/{chunk-2Y5US35K.cjs.map → chunk-KUXNZ5MG.cjs.map} +1 -1
  11. package/dist/{chunk-INNOI6TG.js → chunk-Y3A2EVVS.js} +3 -3
  12. package/dist/{chunk-INNOI6TG.js.map → chunk-Y3A2EVVS.js.map} +1 -1
  13. package/dist/forma-runtime-csp.js +178 -79
  14. package/dist/forma-runtime.js +178 -79
  15. package/dist/formajs-runtime-hardened.global.js +178 -79
  16. package/dist/formajs-runtime-hardened.global.js.map +1 -1
  17. package/dist/formajs-runtime.global.js +178 -79
  18. package/dist/formajs-runtime.global.js.map +1 -1
  19. package/dist/formajs.global.js +245 -61
  20. package/dist/formajs.global.js.map +1 -1
  21. package/dist/http.cjs +11 -11
  22. package/dist/http.js +2 -2
  23. package/dist/index.cjs +307 -143
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +34 -2
  26. package/dist/index.d.ts +34 -2
  27. package/dist/index.js +248 -88
  28. package/dist/index.js.map +1 -1
  29. package/dist/runtime-hardened.cjs +178 -79
  30. package/dist/runtime-hardened.cjs.map +1 -1
  31. package/dist/runtime-hardened.js +178 -79
  32. package/dist/runtime-hardened.js.map +1 -1
  33. package/dist/runtime.cjs +198 -103
  34. package/dist/runtime.cjs.map +1 -1
  35. package/dist/runtime.js +174 -79
  36. package/dist/runtime.js.map +1 -1
  37. package/dist/server.cjs +7 -7
  38. package/dist/server.js +2 -2
  39. package/dist/tc39-compat.cjs +3 -3
  40. package/dist/tc39-compat.js +1 -1
  41. package/package.json +1 -1
  42. package/dist/chunk-JRQNDXX7.cjs.map +0 -1
  43. package/dist/chunk-MIOMT2CB.js.map +0 -1
  44. package/dist/chunk-W7OUWVRA.cjs.map +0 -1
  45. package/dist/chunk-XLVBYXOU.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,19 +1,19 @@
1
1
  'use strict';
2
2
 
3
- var chunkJRQNDXX7_cjs = require('./chunk-JRQNDXX7.cjs');
4
- var chunk2Y5US35K_cjs = require('./chunk-2Y5US35K.cjs');
5
- var chunkW7OUWVRA_cjs = require('./chunk-W7OUWVRA.cjs');
3
+ var chunkFBM7V4NE_cjs = require('./chunk-FBM7V4NE.cjs');
4
+ var chunkKUXNZ5MG_cjs = require('./chunk-KUXNZ5MG.cjs');
5
+ var chunkH7MDUHS5_cjs = require('./chunk-H7MDUHS5.cjs');
6
6
 
7
7
  // src/dom/text.ts
8
- function createText(value) {
9
- if (typeof value === "function") {
8
+ function createText(value2) {
9
+ if (typeof value2 === "function") {
10
10
  const node = new Text("");
11
- chunk2Y5US35K_cjs.internalEffect(() => {
12
- node.data = value();
11
+ chunkKUXNZ5MG_cjs.internalEffect(() => {
12
+ node.data = value2();
13
13
  });
14
14
  return node;
15
15
  }
16
- return new Text(value);
16
+ return new Text(value2);
17
17
  }
18
18
 
19
19
  // src/dom/mount.ts
@@ -24,12 +24,12 @@ function mount(component, container) {
24
24
  }
25
25
  let disposeRoot;
26
26
  if (target.hasAttribute("data-forma-ssr")) {
27
- chunk2Y5US35K_cjs.createUnownedRoot((dispose) => {
27
+ chunkKUXNZ5MG_cjs.createUnownedRoot((dispose) => {
28
28
  disposeRoot = dispose;
29
- chunkJRQNDXX7_cjs.hydrateIsland(component, target);
29
+ chunkFBM7V4NE_cjs.hydrateIsland(component, target);
30
30
  });
31
31
  } else {
32
- const dom = chunk2Y5US35K_cjs.createUnownedRoot((dispose) => {
32
+ const dom = chunkKUXNZ5MG_cjs.createUnownedRoot((dispose) => {
33
33
  disposeRoot = dispose;
34
34
  return component();
35
35
  });
@@ -46,7 +46,7 @@ function mount(component, container) {
46
46
  }
47
47
 
48
48
  // src/dom/switch.ts
49
- function createSwitch(value, cases, fallback) {
49
+ function createSwitch(value2, cases, fallback) {
50
50
  const startMarker = document.createComment("forma-switch");
51
51
  const endMarker = document.createComment("/forma-switch");
52
52
  const fragment2 = document.createDocumentFragment();
@@ -55,8 +55,8 @@ function createSwitch(value, cases, fallback) {
55
55
  const cache2 = /* @__PURE__ */ new Map();
56
56
  let currentNode = null;
57
57
  let currentMatch = UNSET;
58
- const switchDispose = chunk2Y5US35K_cjs.internalEffect(() => {
59
- const val = value();
58
+ const switchDispose = chunkKUXNZ5MG_cjs.internalEffect(() => {
59
+ const val = value2();
60
60
  if (val === currentMatch) return;
61
61
  const DEBUG = typeof globalThis.__FORMA_DEBUG__ !== "undefined";
62
62
  if (DEBUG) console.log("[forma:switch] transition", String(currentMatch), "\u2192", String(val));
@@ -90,9 +90,9 @@ function createSwitch(value, cases, fallback) {
90
90
  let entry = cache2.get(val);
91
91
  if (!entry) {
92
92
  let branchDispose;
93
- const node = chunk2Y5US35K_cjs.createRoot((dispose) => {
93
+ const node = chunkKUXNZ5MG_cjs.createRoot((dispose) => {
94
94
  branchDispose = dispose;
95
- return chunk2Y5US35K_cjs.untrack(() => matchedCase.render());
95
+ return chunkKUXNZ5MG_cjs.untrack(() => matchedCase.render());
96
96
  });
97
97
  entry = { node, dispose: branchDispose };
98
98
  cache2.set(val, entry);
@@ -110,7 +110,7 @@ function createSwitch(value, cases, fallback) {
110
110
  if (DEBUG) console.log("[forma:switch] inserted", currentNode.nodeName, "before end marker");
111
111
  }
112
112
  });
113
- chunk2Y5US35K_cjs.registerDisposer(() => {
113
+ chunkKUXNZ5MG_cjs.registerDisposer(() => {
114
114
  for (const entry of cache2.values()) {
115
115
  entry.dispose();
116
116
  }
@@ -142,7 +142,7 @@ function createPortal(children2, target) {
142
142
  }
143
143
  mountedNode = null;
144
144
  };
145
- chunk2Y5US35K_cjs.createEffect(() => {
145
+ chunkKUXNZ5MG_cjs.createEffect(() => {
146
146
  const node = children2();
147
147
  removeMountedNode();
148
148
  mountedNode = node;
@@ -161,9 +161,9 @@ function createErrorBoundary(tryFn, catchFn) {
161
161
  const fragment2 = document.createDocumentFragment();
162
162
  fragment2.appendChild(startMarker);
163
163
  fragment2.appendChild(endMarker);
164
- const [retryCount, setRetryCount] = chunkW7OUWVRA_cjs.createSignal(0);
164
+ const [retryCount, setRetryCount] = chunkH7MDUHS5_cjs.createSignal(0);
165
165
  let currentNode = null;
166
- chunk2Y5US35K_cjs.internalEffect(() => {
166
+ chunkKUXNZ5MG_cjs.internalEffect(() => {
167
167
  retryCount();
168
168
  const parent2 = startMarker.parentNode;
169
169
  if (!parent2) return;
@@ -191,7 +191,7 @@ function createSuspense(fallback, children2) {
191
191
  const fragment2 = document.createDocumentFragment();
192
192
  fragment2.appendChild(startMarker);
193
193
  fragment2.appendChild(endMarker);
194
- const [pending, setPending] = chunkW7OUWVRA_cjs.createSignal(0);
194
+ const [pending, setPending] = chunkH7MDUHS5_cjs.createSignal(0);
195
195
  let currentNode = null;
196
196
  let resolvedNode = null;
197
197
  let fallbackNode = null;
@@ -203,20 +203,26 @@ function createSuspense(fallback, children2) {
203
203
  setPending((p) => Math.max(0, p - 1));
204
204
  }
205
205
  };
206
- chunk2Y5US35K_cjs.pushSuspenseContext(ctx);
206
+ chunkKUXNZ5MG_cjs.pushSuspenseContext(ctx);
207
207
  try {
208
208
  resolvedNode = children2();
209
209
  } finally {
210
- chunk2Y5US35K_cjs.popSuspenseContext();
210
+ chunkKUXNZ5MG_cjs.popSuspenseContext();
211
211
  }
212
- chunk2Y5US35K_cjs.internalEffect(() => {
212
+ chunkKUXNZ5MG_cjs.internalEffect(() => {
213
213
  const parent2 = startMarker.parentNode;
214
214
  if (!parent2) return;
215
215
  const isPending = pending() > 0;
216
216
  const newNode = isPending ? fallbackNode ??= fallback() : resolvedNode;
217
217
  if (newNode === currentNode) return;
218
- if (currentNode && currentNode.parentNode === parent2) {
219
- parent2.removeChild(currentNode);
218
+ if (currentNode) {
219
+ if (currentNode.parentNode === parent2) {
220
+ parent2.removeChild(currentNode);
221
+ } else if (currentNode.nodeType === 11) {
222
+ while (startMarker.nextSibling && startMarker.nextSibling !== endMarker) {
223
+ currentNode.appendChild(startMarker.nextSibling);
224
+ }
225
+ }
220
226
  }
221
227
  if (newNode) {
222
228
  parent2.insertBefore(newNode, endMarker);
@@ -272,40 +278,53 @@ function activateIslands(registry) {
272
278
  const sharedProps = scriptBlock ? JSON.parse(scriptBlock.textContent) : null;
273
279
  const islands = document.querySelectorAll("[data-forma-island]");
274
280
  for (const root of islands) {
281
+ const status = root.getAttribute("data-forma-status");
282
+ if (status === "active" || status === "hydrating" || status === "disposed" || status === "error") continue;
283
+ if (root.__formaScheduled) continue;
284
+ delete root.__formaDisposed;
275
285
  const id = parseInt(root.getAttribute("data-forma-island"), 10);
276
286
  const componentName = root.getAttribute("data-forma-component");
277
287
  const hydrateFn = registry[componentName];
278
288
  if (!hydrateFn) {
279
- if (chunkW7OUWVRA_cjs.__DEV__) console.warn(`[forma] No hydrate function for island "${componentName}" (id=${id})`);
289
+ if (chunkH7MDUHS5_cjs.__DEV__) console.warn(`[forma] No hydrate function for island "${componentName}" (id=${id})`);
280
290
  root.setAttribute("data-forma-status", "error");
281
291
  continue;
282
292
  }
283
293
  const trigger2 = root.getAttribute("data-forma-hydrate") || "load";
284
294
  if (trigger2 === "visible") {
295
+ root.__formaScheduled = true;
285
296
  const observer = new IntersectionObserver(
286
297
  (entries) => {
287
298
  for (const entry of entries) {
288
299
  if (!entry.isIntersecting) continue;
289
300
  observer.disconnect();
301
+ delete root.__formaObserver;
290
302
  hydrateIslandRoot(root, id, componentName, hydrateFn, sharedProps);
291
303
  }
292
304
  },
293
305
  { rootMargin: "200px" }
294
306
  );
307
+ root.__formaObserver = observer;
295
308
  observer.observe(root);
296
309
  } else if (trigger2 === "idle") {
310
+ root.__formaScheduled = true;
297
311
  const hydrate = () => hydrateIslandRoot(root, id, componentName, hydrateFn, sharedProps);
298
312
  if (typeof requestIdleCallback === "function") {
299
- requestIdleCallback(hydrate);
313
+ const handle = requestIdleCallback(hydrate);
314
+ root.__formaIdleCancel = () => cancelIdleCallback(handle);
300
315
  } else {
301
- setTimeout(hydrate, 200);
316
+ const handle = setTimeout(hydrate, 200);
317
+ root.__formaIdleCancel = () => clearTimeout(handle);
302
318
  }
303
319
  } else if (trigger2 === "interaction") {
320
+ root.__formaScheduled = true;
304
321
  const hydrate = () => {
305
322
  root.removeEventListener("pointerdown", hydrate, true);
306
323
  root.removeEventListener("focusin", hydrate, true);
324
+ delete root.__formaInteractionHandler;
307
325
  hydrateIslandRoot(root, id, componentName, hydrateFn, sharedProps);
308
326
  };
327
+ root.__formaInteractionHandler = hydrate;
309
328
  root.addEventListener("pointerdown", hydrate, { capture: true, once: true });
310
329
  root.addEventListener("focusin", hydrate, { capture: true, once: true });
311
330
  } else {
@@ -314,6 +333,24 @@ function activateIslands(registry) {
314
333
  }
315
334
  }
316
335
  function deactivateIsland(el) {
336
+ const observer = el.__formaObserver;
337
+ if (observer) {
338
+ observer.disconnect();
339
+ delete el.__formaObserver;
340
+ }
341
+ const interactionHandler = el.__formaInteractionHandler;
342
+ if (interactionHandler) {
343
+ el.removeEventListener("pointerdown", interactionHandler, true);
344
+ el.removeEventListener("focusin", interactionHandler, true);
345
+ delete el.__formaInteractionHandler;
346
+ }
347
+ const idleCancel = el.__formaIdleCancel;
348
+ if (idleCancel) {
349
+ idleCancel();
350
+ delete el.__formaIdleCancel;
351
+ }
352
+ delete el.__formaScheduled;
353
+ el.__formaDisposed = true;
317
354
  const dispose = el.__formaDispose;
318
355
  if (typeof dispose === "function") {
319
356
  dispose();
@@ -322,23 +359,25 @@ function deactivateIsland(el) {
322
359
  }
323
360
  }
324
361
  function deactivateAllIslands(root = document) {
325
- const islands = root.querySelectorAll('[data-forma-status="active"]');
362
+ const islands = root.querySelectorAll("[data-forma-island]");
326
363
  for (const island of islands) {
327
364
  deactivateIsland(island);
328
365
  }
329
366
  }
330
367
  function hydrateIslandRoot(root, id, componentName, hydrateFn, sharedProps) {
368
+ if (root.__formaDisposed) return;
331
369
  try {
370
+ delete root.__formaScheduled;
332
371
  const props = loadIslandProps(root, id, sharedProps);
333
372
  root.setAttribute("data-forma-status", "hydrating");
334
373
  let activeRoot = root;
335
- chunk2Y5US35K_cjs.createUnownedRoot((dispose) => {
336
- activeRoot = chunkJRQNDXX7_cjs.hydrateIsland(() => hydrateFn(root, props), root);
374
+ chunkKUXNZ5MG_cjs.createUnownedRoot((dispose) => {
375
+ activeRoot = chunkFBM7V4NE_cjs.hydrateIsland(() => hydrateFn(root, props), root);
337
376
  activeRoot.__formaDispose = dispose;
338
377
  });
339
378
  activeRoot.setAttribute("data-forma-status", "active");
340
379
  } catch (err) {
341
- if (chunkW7OUWVRA_cjs.__DEV__) console.error(`[forma] Island "${componentName}" (id=${id}) failed:`, err);
380
+ if (chunkH7MDUHS5_cjs.__DEV__) console.error(`[forma] Island "${componentName}" (id=${id}) failed:`, err);
342
381
  root.setAttribute("data-forma-status", "error");
343
382
  }
344
383
  }
@@ -387,14 +426,14 @@ function defineComponent(setupOrDef) {
387
426
  try {
388
427
  cb();
389
428
  } catch (e) {
390
- chunkW7OUWVRA_cjs.reportError(e, "onUnmount");
429
+ chunkH7MDUHS5_cjs.reportError(e, "onUnmount");
391
430
  }
392
431
  }
393
432
  for (const d of ctx.disposers) {
394
433
  try {
395
434
  d();
396
435
  } catch (e) {
397
- chunkW7OUWVRA_cjs.reportError(e, "component disposer");
436
+ chunkH7MDUHS5_cjs.reportError(e, "component disposer");
398
437
  }
399
438
  }
400
439
  ctx.disposers.length = 0;
@@ -409,7 +448,7 @@ function defineComponent(setupOrDef) {
409
448
  ctx.unmountCallbacks.push(cleanup2);
410
449
  }
411
450
  } catch (e) {
412
- chunkW7OUWVRA_cjs.reportError(e, "onMount");
451
+ chunkH7MDUHS5_cjs.reportError(e, "onMount");
413
452
  }
414
453
  }
415
454
  return dom;
@@ -436,13 +475,13 @@ function createContext(defaultValue) {
436
475
  defaultValue
437
476
  };
438
477
  }
439
- function provide(ctx, value) {
478
+ function provide(ctx, value2) {
440
479
  let stack = contextStacks.get(ctx.id);
441
480
  if (stack === void 0) {
442
481
  stack = [];
443
482
  contextStacks.set(ctx.id, stack);
444
483
  }
445
- stack.push(value);
484
+ stack.push(value2);
446
485
  }
447
486
  function inject(ctx) {
448
487
  const stack = contextStacks.get(ctx.id);
@@ -498,6 +537,19 @@ function deepClone(obj, seen) {
498
537
  function createStore(initial) {
499
538
  const signals = /* @__PURE__ */ new Map();
500
539
  const children2 = /* @__PURE__ */ new Map();
540
+ const arrayVersions = /* @__PURE__ */ new Map();
541
+ function getArrayVersion(path) {
542
+ let p = arrayVersions.get(path);
543
+ if (!p) {
544
+ p = chunkH7MDUHS5_cjs.createSignal(0);
545
+ arrayVersions.set(path, p);
546
+ }
547
+ return p;
548
+ }
549
+ function bumpArrayVersion(path) {
550
+ const p = arrayVersions.get(path);
551
+ if (p) p[1]((n) => n + 1);
552
+ }
501
553
  function registerChild(path) {
502
554
  const lastDot = path.lastIndexOf(".");
503
555
  if (lastDot === -1) return;
@@ -512,7 +564,7 @@ function createStore(initial) {
512
564
  function getSignal(path, initialValue) {
513
565
  let pair = signals.get(path);
514
566
  if (!pair) {
515
- pair = chunkW7OUWVRA_cjs.createSignal(initialValue);
567
+ pair = chunkH7MDUHS5_cjs.createSignal(initialValue);
516
568
  signals.set(path, pair);
517
569
  registerChild(path);
518
570
  }
@@ -529,14 +581,37 @@ function createStore(initial) {
529
581
  }
530
582
  childSet.clear();
531
583
  }
584
+ function lastSegment(path) {
585
+ const d = path.lastIndexOf(".");
586
+ return d === -1 ? path : path.substring(d + 1);
587
+ }
588
+ function setLiteral(pair, v) {
589
+ pair[1](typeof v === "function" ? chunkH7MDUHS5_cjs.value(v) : v);
590
+ }
591
+ function reconcileChildren(parentPath, rawParent) {
592
+ const set = children2.get(parentPath);
593
+ if (!set) return;
594
+ for (const childPath of set) {
595
+ const key = lastSegment(childPath);
596
+ const nv = rawParent[key];
597
+ const pair = signals.get(childPath);
598
+ if (pair) setLiteral(pair, nv);
599
+ if (nv != null && typeof nv === "object") {
600
+ reconcileChildren(childPath, nv);
601
+ } else {
602
+ invalidateChildren(childPath);
603
+ }
604
+ }
605
+ }
532
606
  function wrap(raw, basePath) {
533
607
  if (!shouldWrap(raw)) return raw;
534
- const cached = proxyCache.get(raw);
535
- if (cached) return cached;
608
+ let byPath = proxyCache.get(raw);
609
+ if (byPath) {
610
+ const hit = byPath.get(basePath);
611
+ if (hit) return hit;
612
+ }
536
613
  const isArr = Array.isArray(raw);
537
614
  const basePrefix = basePath ? basePath + "." : "";
538
- let lastKey = "";
539
- let lastSignal;
540
615
  const proxy = new Proxy(raw, {
541
616
  // -------------------------------------------------------------------
542
617
  // GET
@@ -552,17 +627,15 @@ function createStore(initial) {
552
627
  if (isArr && ARRAY_MUTATORS.has(key)) {
553
628
  return (...args) => {
554
629
  let result;
555
- chunk2Y5US35K_cjs.batch(() => {
630
+ chunkKUXNZ5MG_cjs.batch(() => {
556
631
  const rawArgs = args.map(
557
632
  (a) => a != null && typeof a === "object" && a[RAW] ? a[RAW] : a
558
633
  );
559
634
  result = target[key].apply(target, rawArgs);
560
- invalidateChildren(basePath);
561
- const [, setLen] = getSignal(
562
- basePrefix + "length",
563
- target.length
564
- );
565
- setLen(target.length);
635
+ reconcileChildren(basePath, target);
636
+ const lenPair = signals.get(basePrefix + "length");
637
+ if (lenPair) lenPair[1](target.length);
638
+ if (basePath) bumpArrayVersion(basePath);
566
639
  });
567
640
  return result;
568
641
  };
@@ -572,34 +645,32 @@ function createStore(initial) {
572
645
  getter();
573
646
  return target.length;
574
647
  }
575
- const value = Reflect.get(target, prop);
576
- let pair;
577
- if (key === lastKey && lastSignal) {
578
- pair = lastSignal;
579
- } else {
580
- pair = getSignal(childPath, value);
581
- lastKey = key;
582
- lastSignal = pair;
583
- }
648
+ const value2 = Reflect.get(target, prop);
649
+ const pair = getSignal(childPath, value2);
584
650
  pair[0]();
585
- if (shouldWrap(value)) {
586
- return wrap(value, childPath);
651
+ if (Array.isArray(value2)) {
652
+ getArrayVersion(childPath)[0]();
587
653
  }
588
- return value;
654
+ if (shouldWrap(value2)) {
655
+ return wrap(value2, childPath);
656
+ }
657
+ return value2;
589
658
  },
590
659
  // -------------------------------------------------------------------
591
660
  // SET
592
661
  // -------------------------------------------------------------------
593
- set(target, prop, value) {
662
+ set(target, prop, value2) {
594
663
  if (typeof prop === "symbol") {
595
- return Reflect.set(target, prop, value);
664
+ return Reflect.set(target, prop, value2);
596
665
  }
597
666
  const key = String(prop);
598
667
  const childPath = basePrefix + key;
599
- const rawValue = value != null && typeof value === "object" && value[RAW] ? value[RAW] : value;
668
+ const rawValue = value2 != null && typeof value2 === "object" && value2[RAW] ? value2[RAW] : value2;
669
+ const oldRaw = Reflect.get(target, prop);
600
670
  Reflect.set(target, prop, rawValue);
601
- if (rawValue != null && typeof rawValue === "object") {
671
+ if (rawValue != null && typeof rawValue === "object" && oldRaw !== rawValue) {
602
672
  invalidateChildren(childPath);
673
+ evictProxy(oldRaw, childPath);
603
674
  }
604
675
  if (isArr && key !== "length") {
605
676
  const lengthPath = basePrefix + "length";
@@ -608,6 +679,12 @@ function createStore(initial) {
608
679
  lenPair[1](target.length);
609
680
  }
610
681
  }
682
+ if (isArr && key === "length") {
683
+ chunkKUXNZ5MG_cjs.batch(() => {
684
+ reconcileChildren(basePath, target);
685
+ if (basePath) bumpArrayVersion(basePath);
686
+ });
687
+ }
611
688
  const [, setter2] = getSignal(childPath, rawValue);
612
689
  setter2(rawValue);
613
690
  return true;
@@ -646,31 +723,37 @@ function createStore(initial) {
646
723
  }
647
724
  const key = String(prop);
648
725
  const childPath = basePrefix + key;
726
+ const oldRaw = Reflect.get(target, prop);
649
727
  const result = Reflect.deleteProperty(target, prop);
728
+ const delPair = signals.get(childPath);
729
+ if (delPair) delPair[1](void 0);
730
+ evictProxy(oldRaw, childPath);
650
731
  invalidateChildren(childPath);
651
- signals.delete(childPath);
652
- const parentPath = basePath;
653
- if (parentPath !== void 0) {
654
- const parentSet = children2.get(parentPath);
655
- if (parentSet) {
656
- parentSet.delete(childPath);
657
- if (parentSet.size === 0) children2.delete(parentPath);
658
- }
659
- }
660
- children2.delete(childPath);
661
732
  return result;
662
733
  }
663
734
  });
664
- proxyCache.set(raw, proxy);
735
+ if (!byPath) {
736
+ byPath = /* @__PURE__ */ new Map();
737
+ proxyCache.set(raw, byPath);
738
+ }
739
+ byPath.set(basePath, proxy);
665
740
  return proxy;
666
741
  }
742
+ function evictProxy(oldRaw, path) {
743
+ if (oldRaw == null || typeof oldRaw !== "object") return;
744
+ const om = proxyCache.get(oldRaw);
745
+ if (om) {
746
+ om.delete(path);
747
+ if (om.size === 0) proxyCache.delete(oldRaw);
748
+ }
749
+ }
667
750
  const rootProxy = wrap(initial, "");
668
751
  function getCurrentSnapshot() {
669
- return chunk2Y5US35K_cjs.untrack(() => deepClone(initial));
752
+ return chunkKUXNZ5MG_cjs.untrack(() => deepClone(initial));
670
753
  }
671
754
  const setter = (partial) => {
672
755
  const updates = typeof partial === "function" ? partial(getCurrentSnapshot()) : partial;
673
- chunk2Y5US35K_cjs.batch(() => {
756
+ chunkKUXNZ5MG_cjs.batch(() => {
674
757
  for (const key of Object.keys(updates)) {
675
758
  rootProxy[key] = updates[key];
676
759
  }
@@ -680,60 +763,81 @@ function createStore(initial) {
680
763
  }
681
764
 
682
765
  // src/state/history.ts
766
+ function cloneEntry(v, seen) {
767
+ if (v === null || typeof v !== "object") return v;
768
+ const proto = Object.getPrototypeOf(v);
769
+ if (!Array.isArray(v) && proto !== Object.prototype && proto !== null) return v;
770
+ if (!seen) seen = /* @__PURE__ */ new WeakSet();
771
+ if (seen.has(v)) return v;
772
+ seen.add(v);
773
+ if (Array.isArray(v)) return v.map((i) => cloneEntry(i, seen));
774
+ const out = {};
775
+ for (const k of Object.keys(v)) {
776
+ out[k] = cloneEntry(v[k], seen);
777
+ }
778
+ return out;
779
+ }
683
780
  function createHistory(source, options) {
684
781
  const [sourceGet, sourceSet] = source;
685
- const maxLength = options?.maxLength ?? 100;
686
- let _stack = [sourceGet()];
782
+ const maxLength = Math.max(1, options?.maxLength ?? 100);
783
+ let _stack = [cloneEntry(sourceGet())];
687
784
  let _cursor = 0;
688
- const [stackSignal, setStackSignal] = chunkW7OUWVRA_cjs.createSignal([..._stack]);
689
- const [cursorSignal, setCursorSignal] = chunkW7OUWVRA_cjs.createSignal(_cursor);
690
- const [stackLenSignal, setStackLenSignal] = chunkW7OUWVRA_cjs.createSignal(_stack.length);
785
+ const [stackSignal, setStackSignal] = chunkH7MDUHS5_cjs.createSignal([..._stack]);
786
+ const [cursorSignal, setCursorSignal] = chunkH7MDUHS5_cjs.createSignal(_cursor);
787
+ const [stackLenSignal, setStackLenSignal] = chunkH7MDUHS5_cjs.createSignal(_stack.length);
691
788
  function syncSignals() {
692
- chunk2Y5US35K_cjs.batch(() => {
789
+ chunkKUXNZ5MG_cjs.batch(() => {
693
790
  setStackSignal([..._stack]);
694
791
  setCursorSignal(_cursor);
695
792
  setStackLenSignal(_stack.length);
696
793
  });
697
794
  }
698
- let ignoreNext = false;
795
+ const NONE = /* @__PURE__ */ Symbol("none");
796
+ let _expected = NONE;
699
797
  let isFirstRun = true;
700
- chunk2Y5US35K_cjs.internalEffect(() => {
701
- const value = sourceGet();
798
+ const disposeEffect = chunkKUXNZ5MG_cjs.internalEffect(() => {
799
+ const value2 = sourceGet();
702
800
  if (isFirstRun) {
703
801
  isFirstRun = false;
704
802
  return;
705
803
  }
706
- if (ignoreNext) {
707
- ignoreNext = false;
804
+ if (_expected !== NONE && Object.is(value2, _expected)) {
805
+ _expected = NONE;
708
806
  return;
709
807
  }
808
+ _expected = NONE;
710
809
  _stack = _stack.slice(0, _cursor + 1);
711
- _stack.push(value);
810
+ _stack.push(cloneEntry(value2));
712
811
  if (_stack.length > maxLength) {
713
812
  _stack.splice(0, _stack.length - maxLength);
714
813
  }
715
814
  _cursor = _stack.length - 1;
716
815
  syncSignals();
717
816
  });
817
+ const destroy = () => {
818
+ disposeEffect();
819
+ };
718
820
  const canUndo = () => cursorSignal() > 0;
719
821
  const canRedo = () => cursorSignal() < stackLenSignal() - 1;
720
822
  const undo = () => {
721
823
  if (_cursor <= 0) return;
722
824
  _cursor--;
723
- ignoreNext = true;
724
- sourceSet(_stack[_cursor]);
825
+ const restored = cloneEntry(_stack[_cursor]);
826
+ _expected = restored;
827
+ sourceSet(restored);
725
828
  syncSignals();
726
829
  };
727
830
  const redo = () => {
728
831
  if (_cursor >= _stack.length - 1) return;
729
832
  _cursor++;
730
- ignoreNext = true;
731
- sourceSet(_stack[_cursor]);
833
+ const restored = cloneEntry(_stack[_cursor]);
834
+ _expected = restored;
835
+ sourceSet(restored);
732
836
  syncSignals();
733
837
  };
734
838
  const clear = () => {
735
839
  const currentValue = sourceGet();
736
- _stack = [currentValue];
840
+ _stack = [cloneEntry(currentValue)];
737
841
  _cursor = 0;
738
842
  syncSignals();
739
843
  };
@@ -744,35 +848,91 @@ function createHistory(source, options) {
744
848
  canRedo,
745
849
  history: () => stackSignal(),
746
850
  cursor: () => cursorSignal(),
747
- clear
851
+ clear,
852
+ destroy
748
853
  };
749
854
  }
750
855
 
751
856
  // src/state/persist.ts
857
+ var ENVELOPE_TAG = "$forma:v";
752
858
  function persist(source, key, options) {
753
859
  const [sourceGet, sourceSet] = source;
754
860
  const storage = options?.storage ?? globalThis.localStorage;
755
861
  const serialize = options?.serialize ?? JSON.stringify;
756
862
  const deserialize = options?.deserialize ?? JSON.parse;
757
863
  const validate = options?.validate;
758
- try {
759
- const stored = storage.getItem(key);
760
- if (stored !== null) {
761
- const value = deserialize(stored);
762
- if (!validate || validate(value)) {
763
- sourceSet(value);
864
+ const version = options?.version;
865
+ const migrate = options?.migrate;
866
+ const onError2 = options?.onError;
867
+ let writing = false;
868
+ function unwrap(stored) {
869
+ const parsed = deserialize(stored);
870
+ if (parsed != null && typeof parsed === "object" && Object.prototype.hasOwnProperty.call(parsed, ENVELOPE_TAG)) {
871
+ const env = parsed;
872
+ return { value: env.value, version: Number(env[ENVELOPE_TAG]) };
873
+ }
874
+ return { value: parsed, version: 0 };
875
+ }
876
+ function hydrate() {
877
+ let stored;
878
+ try {
879
+ stored = storage.getItem(key);
880
+ } catch (err) {
881
+ onError2?.(err, "hydrate");
882
+ return;
883
+ }
884
+ if (stored === null) return;
885
+ try {
886
+ const { value: raw, version: storedVersion } = unwrap(stored);
887
+ let value2 = raw;
888
+ if (version !== void 0 && storedVersion < version) {
889
+ if (!migrate) return;
890
+ try {
891
+ value2 = migrate(raw, storedVersion);
892
+ } catch (err) {
893
+ onError2?.(err, "migrate");
894
+ return;
895
+ }
896
+ }
897
+ if (!validate || validate(value2)) {
898
+ writing = true;
899
+ try {
900
+ sourceSet(value2);
901
+ } finally {
902
+ writing = false;
903
+ }
764
904
  }
905
+ } catch (err) {
906
+ onError2?.(err, "hydrate");
765
907
  }
766
- } catch {
767
908
  }
768
- chunk2Y5US35K_cjs.internalEffect(() => {
769
- const value = sourceGet();
909
+ hydrate();
910
+ const stopEffect = chunkKUXNZ5MG_cjs.internalEffect(() => {
911
+ const value2 = sourceGet();
912
+ if (writing) return;
770
913
  try {
771
- const serialized = serialize(value);
914
+ const serialized = version !== void 0 ? serialize({ [ENVELOPE_TAG]: version, value: value2 }) : serialize(value2);
772
915
  storage.setItem(key, serialized);
773
- } catch {
916
+ } catch (err) {
917
+ onError2?.(err, err?.name === "QuotaExceededError" ? "write" : "serialize");
774
918
  }
775
919
  });
920
+ const enableSync = options?.syncTabs ?? (typeof window !== "undefined" && storage === globalThis.localStorage);
921
+ let onStorage;
922
+ if (enableSync && typeof window !== "undefined") {
923
+ onStorage = (e) => {
924
+ if (e.storageArea !== storage) return;
925
+ if (e.key !== null && e.key !== key) return;
926
+ hydrate();
927
+ };
928
+ window.addEventListener("storage", onStorage);
929
+ }
930
+ return () => {
931
+ stopEffect();
932
+ if (onStorage && typeof window !== "undefined") {
933
+ window.removeEventListener("storage", onStorage);
934
+ }
935
+ };
776
936
  }
777
937
 
778
938
  // src/events/bus.ts
@@ -919,20 +1079,20 @@ function toggleClass(el, className, force) {
919
1079
  return el.classList.toggle(className, force);
920
1080
  }
921
1081
  function setStyle(el, styles) {
922
- for (const [key, value] of Object.entries(styles)) {
923
- if (value !== void 0) {
924
- el.style[key] = value;
1082
+ for (const [key, value2] of Object.entries(styles)) {
1083
+ if (value2 !== void 0) {
1084
+ el.style[key] = value2;
925
1085
  }
926
1086
  }
927
1087
  }
928
1088
  function setAttr(el, attrs) {
929
- for (const [name, value] of Object.entries(attrs)) {
930
- if (value === false || value === null) {
1089
+ for (const [name, value2] of Object.entries(attrs)) {
1090
+ if (value2 === false || value2 === null) {
931
1091
  el.removeAttribute(name);
932
- } else if (value === true) {
1092
+ } else if (value2 === true) {
933
1093
  el.setAttribute(name, "");
934
1094
  } else {
935
- el.setAttribute(name, value);
1095
+ el.setAttribute(name, value2);
936
1096
  }
937
1097
  }
938
1098
  }
@@ -1023,115 +1183,119 @@ function onMutation(el, handler, options) {
1023
1183
 
1024
1184
  Object.defineProperty(exports, "Fragment", {
1025
1185
  enumerable: true,
1026
- get: function () { return chunkJRQNDXX7_cjs.Fragment; }
1186
+ get: function () { return chunkFBM7V4NE_cjs.Fragment; }
1027
1187
  });
1028
1188
  Object.defineProperty(exports, "cleanup", {
1029
1189
  enumerable: true,
1030
- get: function () { return chunkJRQNDXX7_cjs.cleanup; }
1190
+ get: function () { return chunkFBM7V4NE_cjs.cleanup; }
1031
1191
  });
1032
1192
  Object.defineProperty(exports, "createList", {
1033
1193
  enumerable: true,
1034
- get: function () { return chunkJRQNDXX7_cjs.createList; }
1194
+ get: function () { return chunkFBM7V4NE_cjs.createList; }
1035
1195
  });
1036
1196
  Object.defineProperty(exports, "createShow", {
1037
1197
  enumerable: true,
1038
- get: function () { return chunkJRQNDXX7_cjs.createShow; }
1198
+ get: function () { return chunkFBM7V4NE_cjs.createShow; }
1039
1199
  });
1040
1200
  Object.defineProperty(exports, "fragment", {
1041
1201
  enumerable: true,
1042
- get: function () { return chunkJRQNDXX7_cjs.fragment; }
1202
+ get: function () { return chunkFBM7V4NE_cjs.fragment; }
1043
1203
  });
1044
1204
  Object.defineProperty(exports, "h", {
1045
1205
  enumerable: true,
1046
- get: function () { return chunkJRQNDXX7_cjs.h; }
1206
+ get: function () { return chunkFBM7V4NE_cjs.h; }
1047
1207
  });
1048
1208
  Object.defineProperty(exports, "hydrateIsland", {
1049
1209
  enumerable: true,
1050
- get: function () { return chunkJRQNDXX7_cjs.hydrateIsland; }
1210
+ get: function () { return chunkFBM7V4NE_cjs.hydrateIsland; }
1051
1211
  });
1052
1212
  Object.defineProperty(exports, "reconcileList", {
1053
1213
  enumerable: true,
1054
- get: function () { return chunkJRQNDXX7_cjs.reconcileList; }
1214
+ get: function () { return chunkFBM7V4NE_cjs.reconcileList; }
1215
+ });
1216
+ Object.defineProperty(exports, "svg", {
1217
+ enumerable: true,
1218
+ get: function () { return chunkFBM7V4NE_cjs.svg; }
1055
1219
  });
1056
1220
  Object.defineProperty(exports, "batch", {
1057
1221
  enumerable: true,
1058
- get: function () { return chunk2Y5US35K_cjs.batch; }
1222
+ get: function () { return chunkKUXNZ5MG_cjs.batch; }
1059
1223
  });
1060
1224
  Object.defineProperty(exports, "createEffect", {
1061
1225
  enumerable: true,
1062
- get: function () { return chunk2Y5US35K_cjs.createEffect; }
1226
+ get: function () { return chunkKUXNZ5MG_cjs.createEffect; }
1063
1227
  });
1064
1228
  Object.defineProperty(exports, "createMemo", {
1065
1229
  enumerable: true,
1066
- get: function () { return chunk2Y5US35K_cjs.createMemo; }
1230
+ get: function () { return chunkKUXNZ5MG_cjs.createMemo; }
1067
1231
  });
1068
1232
  Object.defineProperty(exports, "createReducer", {
1069
1233
  enumerable: true,
1070
- get: function () { return chunk2Y5US35K_cjs.createReducer; }
1234
+ get: function () { return chunkKUXNZ5MG_cjs.createReducer; }
1071
1235
  });
1072
1236
  Object.defineProperty(exports, "createRef", {
1073
1237
  enumerable: true,
1074
- get: function () { return chunk2Y5US35K_cjs.createRef; }
1238
+ get: function () { return chunkKUXNZ5MG_cjs.createRef; }
1075
1239
  });
1076
1240
  Object.defineProperty(exports, "createResource", {
1077
1241
  enumerable: true,
1078
- get: function () { return chunk2Y5US35K_cjs.createResource; }
1242
+ get: function () { return chunkKUXNZ5MG_cjs.createResource; }
1079
1243
  });
1080
1244
  Object.defineProperty(exports, "createRoot", {
1081
1245
  enumerable: true,
1082
- get: function () { return chunk2Y5US35K_cjs.createRoot; }
1246
+ get: function () { return chunkKUXNZ5MG_cjs.createRoot; }
1083
1247
  });
1084
1248
  Object.defineProperty(exports, "createUnownedRoot", {
1085
1249
  enumerable: true,
1086
- get: function () { return chunk2Y5US35K_cjs.createUnownedRoot; }
1250
+ get: function () { return chunkKUXNZ5MG_cjs.createUnownedRoot; }
1087
1251
  });
1088
1252
  Object.defineProperty(exports, "getBatchDepth", {
1089
1253
  enumerable: true,
1090
- get: function () { return chunk2Y5US35K_cjs.getBatchDepth; }
1254
+ get: function () { return chunkKUXNZ5MG_cjs.getBatchDepth; }
1091
1255
  });
1092
1256
  Object.defineProperty(exports, "isComputed", {
1093
1257
  enumerable: true,
1094
- get: function () { return chunk2Y5US35K_cjs.isComputed; }
1258
+ get: function () { return chunkKUXNZ5MG_cjs.isComputed; }
1095
1259
  });
1096
1260
  Object.defineProperty(exports, "isEffect", {
1097
1261
  enumerable: true,
1098
- get: function () { return chunk2Y5US35K_cjs.isEffect; }
1262
+ get: function () { return chunkKUXNZ5MG_cjs.isEffect; }
1099
1263
  });
1100
1264
  Object.defineProperty(exports, "isEffectScope", {
1101
1265
  enumerable: true,
1102
- get: function () { return chunk2Y5US35K_cjs.isEffectScope; }
1266
+ get: function () { return chunkKUXNZ5MG_cjs.isEffectScope; }
1103
1267
  });
1104
1268
  Object.defineProperty(exports, "isSignal", {
1105
1269
  enumerable: true,
1106
- get: function () { return chunk2Y5US35K_cjs.isSignal; }
1270
+ get: function () { return chunkKUXNZ5MG_cjs.isSignal; }
1107
1271
  });
1108
1272
  Object.defineProperty(exports, "on", {
1109
1273
  enumerable: true,
1110
- get: function () { return chunk2Y5US35K_cjs.on; }
1274
+ get: function () { return chunkKUXNZ5MG_cjs.on; }
1111
1275
  });
1112
1276
  Object.defineProperty(exports, "onCleanup", {
1113
1277
  enumerable: true,
1114
- get: function () { return chunk2Y5US35K_cjs.onCleanup; }
1278
+ get: function () { return chunkKUXNZ5MG_cjs.onCleanup; }
1115
1279
  });
1116
1280
  Object.defineProperty(exports, "trigger", {
1117
1281
  enumerable: true,
1118
- get: function () { return chunk2Y5US35K_cjs.trigger; }
1282
+ get: function () { return chunkKUXNZ5MG_cjs.trigger; }
1119
1283
  });
1120
1284
  Object.defineProperty(exports, "untrack", {
1121
1285
  enumerable: true,
1122
- get: function () { return chunk2Y5US35K_cjs.untrack; }
1286
+ get: function () { return chunkKUXNZ5MG_cjs.untrack; }
1123
1287
  });
1124
1288
  Object.defineProperty(exports, "createComputed", {
1125
1289
  enumerable: true,
1126
- get: function () { return chunkW7OUWVRA_cjs.createComputed; }
1290
+ get: function () { return chunkH7MDUHS5_cjs.createComputed; }
1127
1291
  });
1128
1292
  Object.defineProperty(exports, "createSignal", {
1129
1293
  enumerable: true,
1130
- get: function () { return chunkW7OUWVRA_cjs.createSignal; }
1294
+ get: function () { return chunkH7MDUHS5_cjs.createSignal; }
1131
1295
  });
1132
1296
  Object.defineProperty(exports, "onError", {
1133
1297
  enumerable: true,
1134
- get: function () { return chunkW7OUWVRA_cjs.onError; }
1298
+ get: function () { return chunkH7MDUHS5_cjs.onError; }
1135
1299
  });
1136
1300
  exports.$ = $;
1137
1301
  exports.$$ = $$;