@getforma/core 0.3.1 → 0.6.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 (42) hide show
  1. package/README.md +190 -16
  2. package/dist/{chunk-KX5WRZH7.js → chunk-DPSAVBCP.js} +3 -4
  3. package/dist/chunk-DPSAVBCP.js.map +1 -0
  4. package/dist/{chunk-FPSLC62A.cjs → chunk-KH3F5NRU.cjs} +2 -4
  5. package/dist/chunk-KH3F5NRU.cjs.map +1 -0
  6. package/dist/{chunk-5H52PKGF.js → chunk-TKGUHASG.js} +20 -10
  7. package/dist/chunk-TKGUHASG.js.map +1 -0
  8. package/dist/{chunk-TSQ7AKFT.cjs → chunk-YSKF3VRA.cjs} +27 -17
  9. package/dist/chunk-YSKF3VRA.cjs.map +1 -0
  10. package/dist/forma-runtime-csp.js +1 -1
  11. package/dist/forma-runtime.js +1 -1
  12. package/dist/formajs-runtime-hardened.global.js +1 -1
  13. package/dist/formajs-runtime-hardened.global.js.map +1 -1
  14. package/dist/formajs-runtime.global.js +1 -1
  15. package/dist/formajs-runtime.global.js.map +1 -1
  16. package/dist/formajs.global.js +1 -1
  17. package/dist/formajs.global.js.map +1 -1
  18. package/dist/index.cjs +102 -75
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +41 -9
  21. package/dist/index.d.ts +41 -9
  22. package/dist/index.js +46 -17
  23. package/dist/index.js.map +1 -1
  24. package/dist/runtime-hardened.cjs +61 -13
  25. package/dist/runtime-hardened.cjs.map +1 -1
  26. package/dist/runtime-hardened.js +62 -14
  27. package/dist/runtime-hardened.js.map +1 -1
  28. package/dist/runtime.cjs +85 -36
  29. package/dist/runtime.cjs.map +1 -1
  30. package/dist/runtime.js +63 -14
  31. package/dist/runtime.js.map +1 -1
  32. package/dist/ssr/index.cjs +18 -10
  33. package/dist/ssr/index.cjs.map +1 -1
  34. package/dist/ssr/index.js +18 -10
  35. package/dist/ssr/index.js.map +1 -1
  36. package/dist/tc39-compat.cjs +3 -3
  37. package/dist/tc39-compat.js +1 -1
  38. package/package.json +1 -1
  39. package/dist/chunk-5H52PKGF.js.map +0 -1
  40. package/dist/chunk-FPSLC62A.cjs.map +0 -1
  41. package/dist/chunk-KX5WRZH7.js.map +0 -1
  42. package/dist/chunk-TSQ7AKFT.cjs.map +0 -1
package/dist/runtime.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkTSQ7AKFT_cjs = require('./chunk-TSQ7AKFT.cjs');
4
- var chunkFPSLC62A_cjs = require('./chunk-FPSLC62A.cjs');
3
+ var chunkYSKF3VRA_cjs = require('./chunk-YSKF3VRA.cjs');
4
+ var chunkKH3F5NRU_cjs = require('./chunk-KH3F5NRU.cjs');
5
5
 
6
6
  // src/dom/reconcile.ts
7
7
  function getBindTargets(el) {
@@ -578,12 +578,11 @@ var RE_TOGGLE = /^(\w+)\s*=\s*!(\w+)$/;
578
578
  var RE_ASSIGN = /^(\w+)\s*=\s*(.+)$/;
579
579
  var RE_COMPOUND = /^(\w+)\s*(\+=|-=|\*=|\/=)\s*(.+)$/;
580
580
  var RE_IF_PREFIX = /^if\b/;
581
- var RE_UNQUOTED_KEYS = /(\w+)\s*:/g;
582
581
  var RE_COMPUTED = /^(\w+)\s*=\s*(.+)$/;
583
582
  var RE_FETCH = /^(.+?)(?:→|->)\s*(\S+)(.*)$/;
584
583
  var RE_FETCH_METHOD = /^(GET|POST|PUT|PATCH|DELETE)\s+(.+)$/i;
585
584
  var RE_STRIP_ITEM_BRACES = /^\{item\.?|\}$/g;
586
- var RE_EVENT_REF = /\bevent\b|\$event\b/;
585
+ var RE_EVENT_REF = /\bevent\s*[.([]|\$event\b/;
587
586
  var RE_REFETCH_CALL = /^\$refetch\(\s*['"]([^'"]+)['"]\s*\)$/;
588
587
  var TRANSITION_STATE_SYM = /* @__PURE__ */ Symbol.for("forma-transition-state");
589
588
  var EXPRESSION_CACHE_MAX = 2048;
@@ -598,6 +597,14 @@ function cacheExpression(key, factory) {
598
597
  var scopeExpressionCache = /* @__PURE__ */ new WeakMap();
599
598
  var scopeHandlerCache = /* @__PURE__ */ new WeakMap();
600
599
  var compiledTemplateCache = /* @__PURE__ */ new Map();
600
+ var COMPILED_TEMPLATE_CACHE_MAX = 2048;
601
+ function cacheCompiledTemplate(key, template) {
602
+ if (compiledTemplateCache.size >= COMPILED_TEMPLATE_CACHE_MAX) {
603
+ const first = compiledTemplateCache.keys().next().value;
604
+ if (first !== void 0) compiledTemplateCache.delete(first);
605
+ }
606
+ compiledTemplateCache.set(key, template);
607
+ }
601
608
  var UNSAFE_METHOD_NAMES = /* @__PURE__ */ new Set([
602
609
  "constructor",
603
610
  "__proto__",
@@ -606,7 +613,8 @@ var UNSAFE_METHOD_NAMES = /* @__PURE__ */ new Set([
606
613
  "__defineSetter__",
607
614
  "__lookupGetter__",
608
615
  "__lookupSetter__",
609
- "eval"
616
+ "eval",
617
+ "Function"
610
618
  ]);
611
619
  var BLOCKED_METHOD_REGEXES = (() => {
612
620
  const result = [];
@@ -630,8 +638,36 @@ function findBlockedMethod(expr) {
630
638
  if (dotRe.test(cleaned)) return name;
631
639
  if (bracketRe.test(cleaned)) return name;
632
640
  }
641
+ if (cleaned.includes("[")) {
642
+ const bracketContents = extractBracketContents(cleaned);
643
+ for (const content of bracketContents) {
644
+ if (!content.includes("+")) continue;
645
+ const fragments = content.match(/['"`]([^'"`]*?)['"`]/g);
646
+ if (!fragments) continue;
647
+ const joined = fragments.map((f) => f.slice(1, -1)).join("");
648
+ if (UNSAFE_METHOD_NAMES.has(joined)) return joined;
649
+ }
650
+ }
633
651
  return null;
634
652
  }
653
+ function extractBracketContents(expr) {
654
+ const results = [];
655
+ let depth = 0;
656
+ let start = -1;
657
+ for (let i = 0; i < expr.length; i++) {
658
+ if (expr[i] === "[") {
659
+ if (depth === 0) start = i + 1;
660
+ depth++;
661
+ } else if (expr[i] === "]") {
662
+ depth--;
663
+ if (depth === 0 && start >= 0) {
664
+ results.push(expr.slice(start, i));
665
+ start = -1;
666
+ }
667
+ }
668
+ }
669
+ return results;
670
+ }
635
671
  var TEXT_BINDING_SYM = /* @__PURE__ */ Symbol.for("forma-text-binding-cache");
636
672
  function toTextValue(value) {
637
673
  if (value == null) return "";
@@ -931,7 +967,7 @@ function parseIfHandler(expr, scope) {
931
967
  }
932
968
  if (rest.length > 0) return null;
933
969
  return (e) => {
934
- chunkTSQ7AKFT_cjs.batch(() => {
970
+ chunkYSKF3VRA_cjs.batch(() => {
935
971
  if (condExpr()) thenHandler(e);
936
972
  else elseHandler?.(e);
937
973
  });
@@ -967,7 +1003,7 @@ function compileTemplate(text) {
967
1003
  dynamics,
968
1004
  hasItemRef: dynamics.length > 0
969
1005
  };
970
- compiledTemplateCache.set(text, result);
1006
+ cacheCompiledTemplate(text, result);
971
1007
  return result;
972
1008
  }
973
1009
  var templateTexts = /* @__PURE__ */ new WeakMap();
@@ -1655,6 +1691,7 @@ function buildEvaluator(expr, scope) {
1655
1691
  return key in scope.getters;
1656
1692
  },
1657
1693
  get(_, key) {
1694
+ if (UNSAFE_METHOD_NAMES.has(key)) return void 0;
1658
1695
  const g = scope.getters[key];
1659
1696
  return g ? g() : void 0;
1660
1697
  }
@@ -1679,7 +1716,7 @@ function parseHandler(expr, scope) {
1679
1716
  const handlers = stmts.map((s) => parseHandler(s, scope));
1680
1717
  if (handlers.every((h) => h !== null)) {
1681
1718
  return (e) => {
1682
- chunkTSQ7AKFT_cjs.batch(() => {
1719
+ chunkYSKF3VRA_cjs.batch(() => {
1683
1720
  for (const h of handlers) h(e);
1684
1721
  });
1685
1722
  };
@@ -1692,7 +1729,7 @@ function parseHandler(expr, scope) {
1692
1729
  const name = incrMatch[1];
1693
1730
  const op = incrMatch[2];
1694
1731
  return () => {
1695
- chunkTSQ7AKFT_cjs.batch(() => {
1732
+ chunkYSKF3VRA_cjs.batch(() => {
1696
1733
  const val = scope.getters[name]?.() ?? 0;
1697
1734
  scope.setters[name]?.(op === "++" ? val + 1 : val - 1);
1698
1735
  });
@@ -1703,7 +1740,7 @@ function parseHandler(expr, scope) {
1703
1740
  const op = preIncrMatch[1];
1704
1741
  const name = preIncrMatch[2];
1705
1742
  return () => {
1706
- chunkTSQ7AKFT_cjs.batch(() => {
1743
+ chunkYSKF3VRA_cjs.batch(() => {
1707
1744
  const val = scope.getters[name]?.() ?? 0;
1708
1745
  scope.setters[name]?.(op === "++" ? val + 1 : val - 1);
1709
1746
  });
@@ -1713,7 +1750,7 @@ function parseHandler(expr, scope) {
1713
1750
  if (toggleMatch && toggleMatch[1] === toggleMatch[2]) {
1714
1751
  const name = toggleMatch[1];
1715
1752
  return () => {
1716
- chunkTSQ7AKFT_cjs.batch(() => {
1753
+ chunkYSKF3VRA_cjs.batch(() => {
1717
1754
  scope.setters[name]?.(!scope.getters[name]?.());
1718
1755
  });
1719
1756
  };
@@ -1725,7 +1762,7 @@ function parseHandler(expr, scope) {
1725
1762
  if (valExpr) {
1726
1763
  if (_debug) dbg(`parseHandler: assignment "${name} = ..." \u2014 setter exists:`, !!scope.setters[name], ", getter exists:", !!scope.getters[name]);
1727
1764
  return () => {
1728
- chunkTSQ7AKFT_cjs.batch(() => {
1765
+ chunkYSKF3VRA_cjs.batch(() => {
1729
1766
  const val = valExpr();
1730
1767
  if (_debug) dbg(`SETTER: ${name} = ${val} (was: ${scope.getters[name]?.()})`);
1731
1768
  scope.setters[name]?.(val);
@@ -1740,7 +1777,7 @@ function parseHandler(expr, scope) {
1740
1777
  const valExpr = parseExpression(compoundMatch[3].trim(), scope);
1741
1778
  if (valExpr) {
1742
1779
  return () => {
1743
- chunkTSQ7AKFT_cjs.batch(() => {
1780
+ chunkYSKF3VRA_cjs.batch(() => {
1744
1781
  const current = scope.getters[name]?.() ?? 0;
1745
1782
  const val = valExpr();
1746
1783
  switch (op) {
@@ -1810,6 +1847,7 @@ function buildHandler(expr, scope) {
1810
1847
  return key in scope.getters || key in scope.setters;
1811
1848
  },
1812
1849
  get(_, key) {
1850
+ if (UNSAFE_METHOD_NAMES.has(key)) return void 0;
1813
1851
  const g = scope.getters[key];
1814
1852
  return g ? g() : void 0;
1815
1853
  },
@@ -1820,7 +1858,7 @@ function buildHandler(expr, scope) {
1820
1858
  }
1821
1859
  });
1822
1860
  const unsafeHandler = (e) => {
1823
- chunkTSQ7AKFT_cjs.batch(() => fn(proxy, e, e));
1861
+ chunkYSKF3VRA_cjs.batch(() => fn(proxy, e, e));
1824
1862
  };
1825
1863
  const result = {
1826
1864
  handler: unsafeHandler,
@@ -1845,11 +1883,10 @@ function parseState(raw) {
1845
1883
  try {
1846
1884
  parsed = JSON.parse(raw);
1847
1885
  } catch {
1848
- try {
1849
- parsed = JSON.parse(raw.replace(RE_UNQUOTED_KEYS, '"$1":'));
1850
- } catch {
1851
- return {};
1886
+ if (_debug) {
1887
+ dbg("parseState: Invalid JSON in data-forma-state \u2014 use valid JSON with quoted keys. Got:", raw.slice(0, 200));
1852
1888
  }
1889
+ return {};
1853
1890
  }
1854
1891
  for (const key of FORBIDDEN_STATE_KEYS) {
1855
1892
  if (key in parsed) delete parsed[key];
@@ -1869,7 +1906,7 @@ function initScope(stateEl) {
1869
1906
  const getters = {};
1870
1907
  const setters = {};
1871
1908
  for (const [key, initial] of Object.entries(state)) {
1872
- const [get, set] = chunkFPSLC62A_cjs.createValueSignal(initial);
1909
+ const [get, set] = chunkKH3F5NRU_cjs.createSignal(initial);
1873
1910
  getters[key] = get;
1874
1911
  setters[key] = set;
1875
1912
  }
@@ -1877,6 +1914,18 @@ function initScope(stateEl) {
1877
1914
  return { getters, setters };
1878
1915
  }
1879
1916
  function bindElement(el, scope, disposers) {
1917
+ const elMagics = {
1918
+ $el: el,
1919
+ $dispatch: (name, detail) => {
1920
+ el.dispatchEvent(new CustomEvent(name, {
1921
+ bubbles: true,
1922
+ composed: true,
1923
+ // crosses Shadow DOM boundaries (important for <forma-stage>)
1924
+ detail
1925
+ }));
1926
+ }
1927
+ };
1928
+ scope = createChildScope(scope, elMagics);
1880
1929
  const known = getDirectives(el);
1881
1930
  const computedAttr = !known || known.has("data-computed") ? el.getAttribute("data-computed") : null;
1882
1931
  if (computedAttr) {
@@ -1891,7 +1940,7 @@ function bindElement(el, scope, disposers) {
1891
1940
  const prevGetter = scope.getters[name];
1892
1941
  delete scope.getters[name];
1893
1942
  const evaluate = buildEvaluator(`{${expr}}`, scope);
1894
- const getter = chunkFPSLC62A_cjs.createComputed(evaluate);
1943
+ const getter = chunkKH3F5NRU_cjs.createComputed(evaluate);
1895
1944
  scope.getters[name] = getter;
1896
1945
  if (!prevGetter) {
1897
1946
  delete scope.setters[name];
@@ -1902,7 +1951,7 @@ function bindElement(el, scope, disposers) {
1902
1951
  const textExpr = !known || known.has("data-text") ? el.getAttribute("data-text") : null;
1903
1952
  if (textExpr) {
1904
1953
  const evaluate = buildEvaluator(textExpr, scope);
1905
- const dispose = chunkTSQ7AKFT_cjs.internalEffect(() => {
1954
+ const dispose = chunkYSKF3VRA_cjs.internalEffect(() => {
1906
1955
  setElementTextFast(el, toTextValue(evaluate()));
1907
1956
  });
1908
1957
  disposers.push(dispose);
@@ -1917,7 +1966,7 @@ function bindElement(el, scope, disposers) {
1917
1966
  dbg(`bindElement: data-show="${showExpr}" on <${tag}${cls}>`);
1918
1967
  }
1919
1968
  let initialized = false;
1920
- const dispose = chunkTSQ7AKFT_cjs.internalEffect(() => {
1969
+ const dispose = chunkYSKF3VRA_cjs.internalEffect(() => {
1921
1970
  const visible = !!evaluate();
1922
1971
  if (_debug) dbg(`data-show effect: "${showExpr}" \u2192 ${visible}`);
1923
1972
  applyShowVisibility(el, visible, transition, !initialized);
@@ -1936,7 +1985,7 @@ function bindElement(el, scope, disposers) {
1936
1985
  const parent = el.parentNode;
1937
1986
  let inserted = true;
1938
1987
  let initialized = false;
1939
- const dispose = chunkTSQ7AKFT_cjs.internalEffect(() => {
1988
+ const dispose = chunkYSKF3VRA_cjs.internalEffect(() => {
1940
1989
  const show = !!evaluate();
1941
1990
  if (show && !inserted) {
1942
1991
  clearTransitionState(el);
@@ -1975,7 +2024,7 @@ function bindElement(el, scope, disposers) {
1975
2024
  const setter = scope.setters[prop];
1976
2025
  if (getter && setter) {
1977
2026
  const input = el;
1978
- const dispose = chunkTSQ7AKFT_cjs.internalEffect(() => {
2027
+ const dispose = chunkYSKF3VRA_cjs.internalEffect(() => {
1979
2028
  const val = getter();
1980
2029
  if (input.type === "checkbox") {
1981
2030
  input.checked = !!val;
@@ -2039,14 +2088,14 @@ function bindElement(el, scope, disposers) {
2039
2088
  } else if (name.startsWith("data-class:")) {
2040
2089
  const cls = name.slice(11);
2041
2090
  const evaluate = buildEvaluator(attr.value, scope);
2042
- const dispose = chunkTSQ7AKFT_cjs.internalEffect(() => {
2091
+ const dispose = chunkYSKF3VRA_cjs.internalEffect(() => {
2043
2092
  el.classList.toggle(cls, !!evaluate());
2044
2093
  });
2045
2094
  disposers.push(dispose);
2046
2095
  } else if (name.startsWith("data-bind:")) {
2047
2096
  const attrName = name.slice(10);
2048
2097
  const evaluate = buildEvaluator(attr.value, scope);
2049
- const dispose = chunkTSQ7AKFT_cjs.internalEffect(() => {
2098
+ const dispose = chunkYSKF3VRA_cjs.internalEffect(() => {
2050
2099
  const val = evaluate();
2051
2100
  if (val == null || val === false) {
2052
2101
  el.removeAttribute(attrName);
@@ -2069,7 +2118,7 @@ function bindElement(el, scope, disposers) {
2069
2118
  if (saved !== null) setter(JSON.parse(saved));
2070
2119
  } catch {
2071
2120
  }
2072
- const dispose = chunkTSQ7AKFT_cjs.internalEffect(() => {
2121
+ const dispose = chunkYSKF3VRA_cjs.internalEffect(() => {
2073
2122
  try {
2074
2123
  localStorage.setItem(key, JSON.stringify(getter()));
2075
2124
  } catch {
@@ -2156,7 +2205,7 @@ function bindElement(el, scope, disposers) {
2156
2205
  });
2157
2206
  }
2158
2207
  } : void 0;
2159
- const dispose = chunkTSQ7AKFT_cjs.internalEffect(() => {
2208
+ const dispose = chunkYSKF3VRA_cjs.internalEffect(() => {
2160
2209
  const rawItems = evaluate();
2161
2210
  if (!Array.isArray(rawItems)) {
2162
2211
  for (const n of oldNodes) {
@@ -2177,7 +2226,7 @@ function bindElement(el, scope, disposers) {
2177
2226
  }
2178
2227
  const prevNodes = new Set(oldNodes);
2179
2228
  if (keyProp) {
2180
- const result = chunkTSQ7AKFT_cjs.reconcileList(
2229
+ const result = chunkYSKF3VRA_cjs.reconcileList(
2181
2230
  el,
2182
2231
  oldItems,
2183
2232
  rawItems,
@@ -2207,7 +2256,7 @@ function bindElement(el, scope, disposers) {
2207
2256
  } else {
2208
2257
  const wrapped = rawItems.map((item, i) => ({ __idx: i, __item: item }));
2209
2258
  const oldWrapped = oldItems;
2210
- const result = chunkTSQ7AKFT_cjs.reconcileList(
2259
+ const result = chunkYSKF3VRA_cjs.reconcileList(
2211
2260
  el,
2212
2261
  oldWrapped,
2213
2262
  wrapped,
@@ -2256,16 +2305,16 @@ function bindElement(el, scope, disposers) {
2256
2305
  else if (k === "error") errorTarget = v;
2257
2306
  else if (k === "poll") interval = parseInt(v ?? "0", 10);
2258
2307
  }
2259
- const [getTarget, setTarget] = chunkFPSLC62A_cjs.createValueSignal(null);
2308
+ const [getTarget, setTarget] = chunkKH3F5NRU_cjs.createSignal(null);
2260
2309
  scope.getters[target] = getTarget;
2261
2310
  scope.setters[target] = setTarget;
2262
2311
  if (loadingTarget) {
2263
- const [gl, sl] = chunkFPSLC62A_cjs.createValueSignal(false);
2312
+ const [gl, sl] = chunkKH3F5NRU_cjs.createSignal(false);
2264
2313
  scope.getters[loadingTarget] = gl;
2265
2314
  scope.setters[loadingTarget] = sl;
2266
2315
  }
2267
2316
  if (errorTarget) {
2268
- const [ge, se] = chunkFPSLC62A_cjs.createValueSignal(null);
2317
+ const [ge, se] = chunkKH3F5NRU_cjs.createSignal(null);
2269
2318
  scope.getters[errorTarget] = ge;
2270
2319
  scope.setters[errorTarget] = se;
2271
2320
  }
@@ -2577,7 +2626,7 @@ function getScopes() {
2577
2626
  function setScopeValue(element, key, value) {
2578
2627
  const scope = element.__formaScope;
2579
2628
  if (!scope?.setters[key]) return;
2580
- chunkTSQ7AKFT_cjs.batch(() => {
2629
+ chunkYSKF3VRA_cjs.batch(() => {
2581
2630
  scope.setters[key](value);
2582
2631
  });
2583
2632
  }
@@ -2586,7 +2635,7 @@ function resetScope(element) {
2586
2635
  const initialJSON = element.__formaInitialState;
2587
2636
  if (!scope || !initialJSON) return;
2588
2637
  const initial = parseState(initialJSON);
2589
- chunkTSQ7AKFT_cjs.batch(() => {
2638
+ chunkYSKF3VRA_cjs.batch(() => {
2590
2639
  for (const [key, val] of Object.entries(initial)) {
2591
2640
  scope.setters[key]?.(val);
2592
2641
  }
@@ -2616,7 +2665,7 @@ function getReconciler() {
2616
2665
  }
2617
2666
  }
2618
2667
  },
2619
- batch: chunkTSQ7AKFT_cjs.batch
2668
+ batch: chunkYSKF3VRA_cjs.batch
2620
2669
  });
2621
2670
  }
2622
2671
  return _reconciler;