@getforma/core 1.0.10 → 1.2.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 (55) hide show
  1. package/dist/{chunk-T33QUD2Y.cjs → chunk-E3FV2VSU.cjs} +43 -43
  2. package/dist/{chunk-T33QUD2Y.cjs.map → chunk-E3FV2VSU.cjs.map} +1 -1
  3. package/dist/chunk-ETPJTPYD.js +103 -0
  4. package/dist/chunk-ETPJTPYD.js.map +1 -0
  5. package/dist/chunk-H7MDUHS5.cjs +110 -0
  6. package/dist/chunk-H7MDUHS5.cjs.map +1 -0
  7. package/dist/{chunk-6FW5E54W.cjs → chunk-KUXNZ5MG.cjs} +127 -103
  8. package/dist/chunk-KUXNZ5MG.cjs.map +1 -0
  9. package/dist/{chunk-3G7ET4O5.js → chunk-Y3A2EVVS.js} +121 -94
  10. package/dist/chunk-Y3A2EVVS.js.map +1 -0
  11. package/dist/{chunk-AFRFF7XL.js → chunk-YRNYOZF3.js} +4 -4
  12. package/dist/{chunk-AFRFF7XL.js.map → chunk-YRNYOZF3.js.map} +1 -1
  13. package/dist/forma-runtime-csp.js +52 -4
  14. package/dist/forma-runtime.js +98 -10
  15. package/dist/formajs-runtime-hardened.global.js +52 -4
  16. package/dist/formajs-runtime-hardened.global.js.map +1 -1
  17. package/dist/formajs-runtime.global.js +98 -10
  18. package/dist/formajs-runtime.global.js.map +1 -1
  19. package/dist/formajs.global.js +368 -147
  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 +262 -141
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +32 -27
  26. package/dist/index.d.ts +32 -27
  27. package/dist/index.js +204 -83
  28. package/dist/index.js.map +1 -1
  29. package/dist/{resource-DeEzxUz6.d.cts → resource-D6HfVgiA.d.cts} +7 -2
  30. package/dist/{resource-BHsgURy0.d.ts → resource-ljs2X5NM.d.ts} +7 -2
  31. package/dist/runtime-hardened.cjs +53 -3
  32. package/dist/runtime-hardened.cjs.map +1 -1
  33. package/dist/runtime-hardened.js +53 -3
  34. package/dist/runtime-hardened.js.map +1 -1
  35. package/dist/runtime.cjs +29 -29
  36. package/dist/runtime.js +3 -3
  37. package/dist/server.cjs +7 -7
  38. package/dist/server.d.cts +2 -2
  39. package/dist/server.d.ts +2 -2
  40. package/dist/server.js +2 -2
  41. package/dist/{signal-C9v4akyJ.d.cts → signal-CRBJYQ6B.d.cts} +0 -8
  42. package/dist/{signal-C9v4akyJ.d.ts → signal-CRBJYQ6B.d.ts} +0 -8
  43. package/dist/tc39-compat.cjs +3 -6
  44. package/dist/tc39-compat.cjs.map +1 -1
  45. package/dist/tc39-compat.d.cts +1 -1
  46. package/dist/tc39-compat.d.ts +1 -1
  47. package/dist/tc39-compat.js +1 -4
  48. package/dist/tc39-compat.js.map +1 -1
  49. package/package.json +1 -1
  50. package/dist/chunk-3G7ET4O5.js.map +0 -1
  51. package/dist/chunk-6FW5E54W.cjs.map +0 -1
  52. package/dist/chunk-HLM5BZZQ.js +0 -35
  53. package/dist/chunk-HLM5BZZQ.js.map +0 -1
  54. package/dist/chunk-QLPCVK7C.cjs +0 -38
  55. package/dist/chunk-QLPCVK7C.cjs.map +0 -1
package/dist/index.js CHANGED
@@ -1,20 +1,20 @@
1
- import { hydrateIsland } from './chunk-AFRFF7XL.js';
2
- export { Fragment, cleanup, createList, createShow, fragment, h, hydrateIsland, reconcileList } from './chunk-AFRFF7XL.js';
3
- import { internalEffect, createUnownedRoot, createRoot, untrack, registerDisposer, createEffect, pushSuspenseContext, popSuspenseContext, __DEV__, reportError, batch } from './chunk-3G7ET4O5.js';
4
- export { batch, createEffect, createMemo, createReducer, createRef, createResource, createRoot, createUnownedRoot, getBatchDepth, isComputed, isEffect, isEffectScope, isSignal, on, onCleanup, onError, trigger, untrack } from './chunk-3G7ET4O5.js';
5
- import { createSignal } from './chunk-HLM5BZZQ.js';
6
- export { createComputed, createSignal } from './chunk-HLM5BZZQ.js';
1
+ import { hydrateIsland } from './chunk-YRNYOZF3.js';
2
+ export { Fragment, cleanup, createList, createShow, fragment, h, hydrateIsland, reconcileList } from './chunk-YRNYOZF3.js';
3
+ import { internalEffect, createUnownedRoot, createRoot, untrack, registerDisposer, createEffect, pushSuspenseContext, popSuspenseContext, batch } from './chunk-Y3A2EVVS.js';
4
+ export { batch, createEffect, createMemo, createReducer, createRef, createResource, createRoot, createUnownedRoot, getBatchDepth, isComputed, isEffect, isEffectScope, isSignal, on, onCleanup, trigger, untrack } from './chunk-Y3A2EVVS.js';
5
+ import { createSignal, __DEV__, reportError, value } from './chunk-ETPJTPYD.js';
6
+ export { createComputed, createSignal, onError } from './chunk-ETPJTPYD.js';
7
7
 
8
8
  // src/dom/text.ts
9
- function createText(value) {
10
- if (typeof value === "function") {
9
+ function createText(value2) {
10
+ if (typeof value2 === "function") {
11
11
  const node = new Text("");
12
12
  internalEffect(() => {
13
- node.data = value();
13
+ node.data = value2();
14
14
  });
15
15
  return node;
16
16
  }
17
- return new Text(value);
17
+ return new Text(value2);
18
18
  }
19
19
 
20
20
  // src/dom/mount.ts
@@ -47,7 +47,7 @@ function mount(component, container) {
47
47
  }
48
48
 
49
49
  // src/dom/switch.ts
50
- function createSwitch(value, cases, fallback) {
50
+ function createSwitch(value2, cases, fallback) {
51
51
  const startMarker = document.createComment("forma-switch");
52
52
  const endMarker = document.createComment("/forma-switch");
53
53
  const fragment2 = document.createDocumentFragment();
@@ -57,7 +57,7 @@ function createSwitch(value, cases, fallback) {
57
57
  let currentNode = null;
58
58
  let currentMatch = UNSET;
59
59
  const switchDispose = internalEffect(() => {
60
- const val = value();
60
+ const val = value2();
61
61
  if (val === currentMatch) return;
62
62
  const DEBUG = typeof globalThis.__FORMA_DEBUG__ !== "undefined";
63
63
  if (DEBUG) console.log("[forma:switch] transition", String(currentMatch), "\u2192", String(val));
@@ -437,13 +437,13 @@ function createContext(defaultValue) {
437
437
  defaultValue
438
438
  };
439
439
  }
440
- function provide(ctx, value) {
440
+ function provide(ctx, value2) {
441
441
  let stack = contextStacks.get(ctx.id);
442
442
  if (stack === void 0) {
443
443
  stack = [];
444
444
  contextStacks.set(ctx.id, stack);
445
445
  }
446
- stack.push(value);
446
+ stack.push(value2);
447
447
  }
448
448
  function inject(ctx) {
449
449
  const stack = contextStacks.get(ctx.id);
@@ -499,6 +499,19 @@ function deepClone(obj, seen) {
499
499
  function createStore(initial) {
500
500
  const signals = /* @__PURE__ */ new Map();
501
501
  const children2 = /* @__PURE__ */ new Map();
502
+ const arrayVersions = /* @__PURE__ */ new Map();
503
+ function getArrayVersion(path) {
504
+ let p = arrayVersions.get(path);
505
+ if (!p) {
506
+ p = createSignal(0);
507
+ arrayVersions.set(path, p);
508
+ }
509
+ return p;
510
+ }
511
+ function bumpArrayVersion(path) {
512
+ const p = arrayVersions.get(path);
513
+ if (p) p[1]((n) => n + 1);
514
+ }
502
515
  function registerChild(path) {
503
516
  const lastDot = path.lastIndexOf(".");
504
517
  if (lastDot === -1) return;
@@ -530,14 +543,37 @@ function createStore(initial) {
530
543
  }
531
544
  childSet.clear();
532
545
  }
546
+ function lastSegment(path) {
547
+ const d = path.lastIndexOf(".");
548
+ return d === -1 ? path : path.substring(d + 1);
549
+ }
550
+ function setLiteral(pair, v) {
551
+ pair[1](typeof v === "function" ? value(v) : v);
552
+ }
553
+ function reconcileChildren(parentPath, rawParent) {
554
+ const set = children2.get(parentPath);
555
+ if (!set) return;
556
+ for (const childPath of set) {
557
+ const key = lastSegment(childPath);
558
+ const nv = rawParent[key];
559
+ const pair = signals.get(childPath);
560
+ if (pair) setLiteral(pair, nv);
561
+ if (nv != null && typeof nv === "object") {
562
+ reconcileChildren(childPath, nv);
563
+ } else {
564
+ invalidateChildren(childPath);
565
+ }
566
+ }
567
+ }
533
568
  function wrap(raw, basePath) {
534
569
  if (!shouldWrap(raw)) return raw;
535
- const cached = proxyCache.get(raw);
536
- if (cached) return cached;
570
+ let byPath = proxyCache.get(raw);
571
+ if (byPath) {
572
+ const hit = byPath.get(basePath);
573
+ if (hit) return hit;
574
+ }
537
575
  const isArr = Array.isArray(raw);
538
576
  const basePrefix = basePath ? basePath + "." : "";
539
- let lastKey = "";
540
- let lastSignal;
541
577
  const proxy = new Proxy(raw, {
542
578
  // -------------------------------------------------------------------
543
579
  // GET
@@ -558,12 +594,10 @@ function createStore(initial) {
558
594
  (a) => a != null && typeof a === "object" && a[RAW] ? a[RAW] : a
559
595
  );
560
596
  result = target[key].apply(target, rawArgs);
561
- invalidateChildren(basePath);
562
- const [, setLen] = getSignal(
563
- basePrefix + "length",
564
- target.length
565
- );
566
- setLen(target.length);
597
+ reconcileChildren(basePath, target);
598
+ const lenPair = signals.get(basePrefix + "length");
599
+ if (lenPair) lenPair[1](target.length);
600
+ if (basePath) bumpArrayVersion(basePath);
567
601
  });
568
602
  return result;
569
603
  };
@@ -573,34 +607,32 @@ function createStore(initial) {
573
607
  getter();
574
608
  return target.length;
575
609
  }
576
- const value = Reflect.get(target, prop);
577
- let pair;
578
- if (key === lastKey && lastSignal) {
579
- pair = lastSignal;
580
- } else {
581
- pair = getSignal(childPath, value);
582
- lastKey = key;
583
- lastSignal = pair;
584
- }
610
+ const value2 = Reflect.get(target, prop);
611
+ const pair = getSignal(childPath, value2);
585
612
  pair[0]();
586
- if (shouldWrap(value)) {
587
- return wrap(value, childPath);
613
+ if (Array.isArray(value2)) {
614
+ getArrayVersion(childPath)[0]();
615
+ }
616
+ if (shouldWrap(value2)) {
617
+ return wrap(value2, childPath);
588
618
  }
589
- return value;
619
+ return value2;
590
620
  },
591
621
  // -------------------------------------------------------------------
592
622
  // SET
593
623
  // -------------------------------------------------------------------
594
- set(target, prop, value) {
624
+ set(target, prop, value2) {
595
625
  if (typeof prop === "symbol") {
596
- return Reflect.set(target, prop, value);
626
+ return Reflect.set(target, prop, value2);
597
627
  }
598
628
  const key = String(prop);
599
629
  const childPath = basePrefix + key;
600
- const rawValue = value != null && typeof value === "object" && value[RAW] ? value[RAW] : value;
630
+ const rawValue = value2 != null && typeof value2 === "object" && value2[RAW] ? value2[RAW] : value2;
631
+ const oldRaw = Reflect.get(target, prop);
601
632
  Reflect.set(target, prop, rawValue);
602
- if (rawValue != null && typeof rawValue === "object") {
633
+ if (rawValue != null && typeof rawValue === "object" && oldRaw !== rawValue) {
603
634
  invalidateChildren(childPath);
635
+ evictProxy(oldRaw, childPath);
604
636
  }
605
637
  if (isArr && key !== "length") {
606
638
  const lengthPath = basePrefix + "length";
@@ -609,6 +641,12 @@ function createStore(initial) {
609
641
  lenPair[1](target.length);
610
642
  }
611
643
  }
644
+ if (isArr && key === "length") {
645
+ batch(() => {
646
+ reconcileChildren(basePath, target);
647
+ if (basePath) bumpArrayVersion(basePath);
648
+ });
649
+ }
612
650
  const [, setter2] = getSignal(childPath, rawValue);
613
651
  setter2(rawValue);
614
652
  return true;
@@ -647,24 +685,30 @@ function createStore(initial) {
647
685
  }
648
686
  const key = String(prop);
649
687
  const childPath = basePrefix + key;
688
+ const oldRaw = Reflect.get(target, prop);
650
689
  const result = Reflect.deleteProperty(target, prop);
690
+ const delPair = signals.get(childPath);
691
+ if (delPair) delPair[1](void 0);
692
+ evictProxy(oldRaw, childPath);
651
693
  invalidateChildren(childPath);
652
- signals.delete(childPath);
653
- const parentPath = basePath;
654
- if (parentPath !== void 0) {
655
- const parentSet = children2.get(parentPath);
656
- if (parentSet) {
657
- parentSet.delete(childPath);
658
- if (parentSet.size === 0) children2.delete(parentPath);
659
- }
660
- }
661
- children2.delete(childPath);
662
694
  return result;
663
695
  }
664
696
  });
665
- proxyCache.set(raw, proxy);
697
+ if (!byPath) {
698
+ byPath = /* @__PURE__ */ new Map();
699
+ proxyCache.set(raw, byPath);
700
+ }
701
+ byPath.set(basePath, proxy);
666
702
  return proxy;
667
703
  }
704
+ function evictProxy(oldRaw, path) {
705
+ if (oldRaw == null || typeof oldRaw !== "object") return;
706
+ const om = proxyCache.get(oldRaw);
707
+ if (om) {
708
+ om.delete(path);
709
+ if (om.size === 0) proxyCache.delete(oldRaw);
710
+ }
711
+ }
668
712
  const rootProxy = wrap(initial, "");
669
713
  function getCurrentSnapshot() {
670
714
  return untrack(() => deepClone(initial));
@@ -681,10 +725,24 @@ function createStore(initial) {
681
725
  }
682
726
 
683
727
  // src/state/history.ts
728
+ function cloneEntry(v, seen) {
729
+ if (v === null || typeof v !== "object") return v;
730
+ const proto = Object.getPrototypeOf(v);
731
+ if (!Array.isArray(v) && proto !== Object.prototype && proto !== null) return v;
732
+ if (!seen) seen = /* @__PURE__ */ new WeakSet();
733
+ if (seen.has(v)) return v;
734
+ seen.add(v);
735
+ if (Array.isArray(v)) return v.map((i) => cloneEntry(i, seen));
736
+ const out = {};
737
+ for (const k of Object.keys(v)) {
738
+ out[k] = cloneEntry(v[k], seen);
739
+ }
740
+ return out;
741
+ }
684
742
  function createHistory(source, options) {
685
743
  const [sourceGet, sourceSet] = source;
686
- const maxLength = options?.maxLength ?? 100;
687
- let _stack = [sourceGet()];
744
+ const maxLength = Math.max(1, options?.maxLength ?? 100);
745
+ let _stack = [cloneEntry(sourceGet())];
688
746
  let _cursor = 0;
689
747
  const [stackSignal, setStackSignal] = createSignal([..._stack]);
690
748
  const [cursorSignal, setCursorSignal] = createSignal(_cursor);
@@ -696,45 +754,52 @@ function createHistory(source, options) {
696
754
  setStackLenSignal(_stack.length);
697
755
  });
698
756
  }
699
- let ignoreNext = false;
757
+ const NONE = /* @__PURE__ */ Symbol("none");
758
+ let _expected = NONE;
700
759
  let isFirstRun = true;
701
- internalEffect(() => {
702
- const value = sourceGet();
760
+ const disposeEffect = internalEffect(() => {
761
+ const value2 = sourceGet();
703
762
  if (isFirstRun) {
704
763
  isFirstRun = false;
705
764
  return;
706
765
  }
707
- if (ignoreNext) {
708
- ignoreNext = false;
766
+ if (_expected !== NONE && Object.is(value2, _expected)) {
767
+ _expected = NONE;
709
768
  return;
710
769
  }
770
+ _expected = NONE;
711
771
  _stack = _stack.slice(0, _cursor + 1);
712
- _stack.push(value);
772
+ _stack.push(cloneEntry(value2));
713
773
  if (_stack.length > maxLength) {
714
774
  _stack.splice(0, _stack.length - maxLength);
715
775
  }
716
776
  _cursor = _stack.length - 1;
717
777
  syncSignals();
718
778
  });
779
+ const destroy = () => {
780
+ disposeEffect();
781
+ };
719
782
  const canUndo = () => cursorSignal() > 0;
720
783
  const canRedo = () => cursorSignal() < stackLenSignal() - 1;
721
784
  const undo = () => {
722
785
  if (_cursor <= 0) return;
723
786
  _cursor--;
724
- ignoreNext = true;
725
- sourceSet(_stack[_cursor]);
787
+ const restored = cloneEntry(_stack[_cursor]);
788
+ _expected = restored;
789
+ sourceSet(restored);
726
790
  syncSignals();
727
791
  };
728
792
  const redo = () => {
729
793
  if (_cursor >= _stack.length - 1) return;
730
794
  _cursor++;
731
- ignoreNext = true;
732
- sourceSet(_stack[_cursor]);
795
+ const restored = cloneEntry(_stack[_cursor]);
796
+ _expected = restored;
797
+ sourceSet(restored);
733
798
  syncSignals();
734
799
  };
735
800
  const clear = () => {
736
801
  const currentValue = sourceGet();
737
- _stack = [currentValue];
802
+ _stack = [cloneEntry(currentValue)];
738
803
  _cursor = 0;
739
804
  syncSignals();
740
805
  };
@@ -745,35 +810,91 @@ function createHistory(source, options) {
745
810
  canRedo,
746
811
  history: () => stackSignal(),
747
812
  cursor: () => cursorSignal(),
748
- clear
813
+ clear,
814
+ destroy
749
815
  };
750
816
  }
751
817
 
752
818
  // src/state/persist.ts
819
+ var ENVELOPE_TAG = "$forma:v";
753
820
  function persist(source, key, options) {
754
821
  const [sourceGet, sourceSet] = source;
755
822
  const storage = options?.storage ?? globalThis.localStorage;
756
823
  const serialize = options?.serialize ?? JSON.stringify;
757
824
  const deserialize = options?.deserialize ?? JSON.parse;
758
825
  const validate = options?.validate;
759
- try {
760
- const stored = storage.getItem(key);
761
- if (stored !== null) {
762
- const value = deserialize(stored);
763
- if (!validate || validate(value)) {
764
- sourceSet(value);
826
+ const version = options?.version;
827
+ const migrate = options?.migrate;
828
+ const onError2 = options?.onError;
829
+ let writing = false;
830
+ function unwrap(stored) {
831
+ const parsed = deserialize(stored);
832
+ if (parsed != null && typeof parsed === "object" && Object.prototype.hasOwnProperty.call(parsed, ENVELOPE_TAG)) {
833
+ const env = parsed;
834
+ return { value: env.value, version: Number(env[ENVELOPE_TAG]) };
835
+ }
836
+ return { value: parsed, version: 0 };
837
+ }
838
+ function hydrate() {
839
+ let stored;
840
+ try {
841
+ stored = storage.getItem(key);
842
+ } catch (err) {
843
+ onError2?.(err, "hydrate");
844
+ return;
845
+ }
846
+ if (stored === null) return;
847
+ try {
848
+ const { value: raw, version: storedVersion } = unwrap(stored);
849
+ let value2 = raw;
850
+ if (version !== void 0 && storedVersion < version) {
851
+ if (!migrate) return;
852
+ try {
853
+ value2 = migrate(raw, storedVersion);
854
+ } catch (err) {
855
+ onError2?.(err, "migrate");
856
+ return;
857
+ }
858
+ }
859
+ if (!validate || validate(value2)) {
860
+ writing = true;
861
+ try {
862
+ sourceSet(value2);
863
+ } finally {
864
+ writing = false;
865
+ }
765
866
  }
867
+ } catch (err) {
868
+ onError2?.(err, "hydrate");
766
869
  }
767
- } catch {
768
870
  }
769
- internalEffect(() => {
770
- const value = sourceGet();
871
+ hydrate();
872
+ const stopEffect = internalEffect(() => {
873
+ const value2 = sourceGet();
874
+ if (writing) return;
771
875
  try {
772
- const serialized = serialize(value);
876
+ const serialized = version !== void 0 ? serialize({ [ENVELOPE_TAG]: version, value: value2 }) : serialize(value2);
773
877
  storage.setItem(key, serialized);
774
- } catch {
878
+ } catch (err) {
879
+ onError2?.(err, err?.name === "QuotaExceededError" ? "write" : "serialize");
775
880
  }
776
881
  });
882
+ const enableSync = options?.syncTabs ?? (typeof window !== "undefined" && storage === globalThis.localStorage);
883
+ let onStorage;
884
+ if (enableSync && typeof window !== "undefined") {
885
+ onStorage = (e) => {
886
+ if (e.storageArea !== storage) return;
887
+ if (e.key !== null && e.key !== key) return;
888
+ hydrate();
889
+ };
890
+ window.addEventListener("storage", onStorage);
891
+ }
892
+ return () => {
893
+ stopEffect();
894
+ if (onStorage && typeof window !== "undefined") {
895
+ window.removeEventListener("storage", onStorage);
896
+ }
897
+ };
777
898
  }
778
899
 
779
900
  // src/events/bus.ts
@@ -920,20 +1041,20 @@ function toggleClass(el, className, force) {
920
1041
  return el.classList.toggle(className, force);
921
1042
  }
922
1043
  function setStyle(el, styles) {
923
- for (const [key, value] of Object.entries(styles)) {
924
- if (value !== void 0) {
925
- el.style[key] = value;
1044
+ for (const [key, value2] of Object.entries(styles)) {
1045
+ if (value2 !== void 0) {
1046
+ el.style[key] = value2;
926
1047
  }
927
1048
  }
928
1049
  }
929
1050
  function setAttr(el, attrs) {
930
- for (const [name, value] of Object.entries(attrs)) {
931
- if (value === false || value === null) {
1051
+ for (const [name, value2] of Object.entries(attrs)) {
1052
+ if (value2 === false || value2 === null) {
932
1053
  el.removeAttribute(name);
933
- } else if (value === true) {
1054
+ } else if (value2 === true) {
934
1055
  el.setAttribute(name, "");
935
1056
  } else {
936
- el.setAttribute(name, value);
1057
+ el.setAttribute(name, value2);
937
1058
  }
938
1059
  }
939
1060
  }