@getforma/core 0.3.1 → 0.8.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 (52) hide show
  1. package/README.md +279 -20
  2. package/dist/chunk-3U57L2TY.cjs +38 -0
  3. package/dist/chunk-3U57L2TY.cjs.map +1 -0
  4. package/dist/{chunk-5H52PKGF.js → chunk-N522P3G6.js} +37 -19
  5. package/dist/chunk-N522P3G6.js.map +1 -0
  6. package/dist/chunk-OZCHIVAZ.js +35 -0
  7. package/dist/chunk-OZCHIVAZ.js.map +1 -0
  8. package/dist/{chunk-TSQ7AKFT.cjs → chunk-YMIMKO4W.cjs} +66 -25
  9. package/dist/chunk-YMIMKO4W.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 +142 -118
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +70 -29
  21. package/dist/index.d.ts +70 -29
  22. package/dist/index.js +64 -60
  23. package/dist/index.js.map +1 -1
  24. package/dist/runtime-hardened.cjs +166 -39
  25. package/dist/runtime-hardened.cjs.map +1 -1
  26. package/dist/runtime-hardened.js +167 -40
  27. package/dist/runtime-hardened.js.map +1 -1
  28. package/dist/runtime.cjs +184 -57
  29. package/dist/runtime.cjs.map +1 -1
  30. package/dist/runtime.js +162 -35
  31. package/dist/runtime.js.map +1 -1
  32. package/dist/signal-B4_wQJHs.d.cts +53 -0
  33. package/dist/signal-B4_wQJHs.d.ts +53 -0
  34. package/dist/ssr/index.cjs +37 -12
  35. package/dist/ssr/index.cjs.map +1 -1
  36. package/dist/ssr/index.js +37 -12
  37. package/dist/ssr/index.js.map +1 -1
  38. package/dist/tc39-compat.cjs +4 -12
  39. package/dist/tc39-compat.cjs.map +1 -1
  40. package/dist/tc39-compat.d.cts +1 -1
  41. package/dist/tc39-compat.d.ts +1 -1
  42. package/dist/tc39-compat.js +3 -11
  43. package/dist/tc39-compat.js.map +1 -1
  44. package/package.json +2 -2
  45. package/dist/chunk-5H52PKGF.js.map +0 -1
  46. package/dist/chunk-FPSLC62A.cjs +0 -31
  47. package/dist/chunk-FPSLC62A.cjs.map +0 -1
  48. package/dist/chunk-KX5WRZH7.js +0 -27
  49. package/dist/chunk-KX5WRZH7.js.map +0 -1
  50. package/dist/chunk-TSQ7AKFT.cjs.map +0 -1
  51. package/dist/signal-CfLDwMyg.d.cts +0 -29
  52. package/dist/signal-CfLDwMyg.d.ts +0 -29
package/dist/runtime.js CHANGED
@@ -1,5 +1,5 @@
1
- import { internalEffect, reconcileList, batch } from './chunk-5H52PKGF.js';
2
- import { createValueSignal, createComputed } from './chunk-KX5WRZH7.js';
1
+ import { internalEffect, reconcileList, batch } from './chunk-N522P3G6.js';
2
+ import { createSignal, createComputed } from './chunk-OZCHIVAZ.js';
3
3
 
4
4
  // src/dom/reconcile.ts
5
5
  function getBindTargets(el) {
@@ -576,12 +576,11 @@ var RE_TOGGLE = /^(\w+)\s*=\s*!(\w+)$/;
576
576
  var RE_ASSIGN = /^(\w+)\s*=\s*(.+)$/;
577
577
  var RE_COMPOUND = /^(\w+)\s*(\+=|-=|\*=|\/=)\s*(.+)$/;
578
578
  var RE_IF_PREFIX = /^if\b/;
579
- var RE_UNQUOTED_KEYS = /(\w+)\s*:/g;
580
579
  var RE_COMPUTED = /^(\w+)\s*=\s*(.+)$/;
581
580
  var RE_FETCH = /^(.+?)(?:→|->)\s*(\S+)(.*)$/;
582
581
  var RE_FETCH_METHOD = /^(GET|POST|PUT|PATCH|DELETE)\s+(.+)$/i;
583
582
  var RE_STRIP_ITEM_BRACES = /^\{item\.?|\}$/g;
584
- var RE_EVENT_REF = /\bevent\b|\$event\b/;
583
+ var RE_EVENT_REF = /\bevent\s*[.([]|\$event\b/;
585
584
  var RE_REFETCH_CALL = /^\$refetch\(\s*['"]([^'"]+)['"]\s*\)$/;
586
585
  var TRANSITION_STATE_SYM = /* @__PURE__ */ Symbol.for("forma-transition-state");
587
586
  var EXPRESSION_CACHE_MAX = 2048;
@@ -596,6 +595,14 @@ function cacheExpression(key, factory) {
596
595
  var scopeExpressionCache = /* @__PURE__ */ new WeakMap();
597
596
  var scopeHandlerCache = /* @__PURE__ */ new WeakMap();
598
597
  var compiledTemplateCache = /* @__PURE__ */ new Map();
598
+ var COMPILED_TEMPLATE_CACHE_MAX = 2048;
599
+ function cacheCompiledTemplate(key, template) {
600
+ if (compiledTemplateCache.size >= COMPILED_TEMPLATE_CACHE_MAX) {
601
+ const first = compiledTemplateCache.keys().next().value;
602
+ if (first !== void 0) compiledTemplateCache.delete(first);
603
+ }
604
+ compiledTemplateCache.set(key, template);
605
+ }
599
606
  var UNSAFE_METHOD_NAMES = /* @__PURE__ */ new Set([
600
607
  "constructor",
601
608
  "__proto__",
@@ -604,7 +611,8 @@ var UNSAFE_METHOD_NAMES = /* @__PURE__ */ new Set([
604
611
  "__defineSetter__",
605
612
  "__lookupGetter__",
606
613
  "__lookupSetter__",
607
- "eval"
614
+ "eval",
615
+ "Function"
608
616
  ]);
609
617
  var BLOCKED_METHOD_REGEXES = (() => {
610
618
  const result = [];
@@ -628,8 +636,36 @@ function findBlockedMethod(expr) {
628
636
  if (dotRe.test(cleaned)) return name;
629
637
  if (bracketRe.test(cleaned)) return name;
630
638
  }
639
+ if (cleaned.includes("[")) {
640
+ const bracketContents = extractBracketContents(cleaned);
641
+ for (const content of bracketContents) {
642
+ if (!content.includes("+")) continue;
643
+ const fragments = content.match(/['"`]([^'"`]*?)['"`]/g);
644
+ if (!fragments) continue;
645
+ const joined = fragments.map((f) => f.slice(1, -1)).join("");
646
+ if (UNSAFE_METHOD_NAMES.has(joined)) return joined;
647
+ }
648
+ }
631
649
  return null;
632
650
  }
651
+ function extractBracketContents(expr) {
652
+ const results = [];
653
+ let depth = 0;
654
+ let start = -1;
655
+ for (let i = 0; i < expr.length; i++) {
656
+ if (expr[i] === "[") {
657
+ if (depth === 0) start = i + 1;
658
+ depth++;
659
+ } else if (expr[i] === "]") {
660
+ depth--;
661
+ if (depth === 0 && start >= 0) {
662
+ results.push(expr.slice(start, i));
663
+ start = -1;
664
+ }
665
+ }
666
+ }
667
+ return results;
668
+ }
633
669
  var TEXT_BINDING_SYM = /* @__PURE__ */ Symbol.for("forma-text-binding-cache");
634
670
  function toTextValue(value) {
635
671
  if (value == null) return "";
@@ -965,7 +1001,7 @@ function compileTemplate(text) {
965
1001
  dynamics,
966
1002
  hasItemRef: dynamics.length > 0
967
1003
  };
968
- compiledTemplateCache.set(text, result);
1004
+ cacheCompiledTemplate(text, result);
969
1005
  return result;
970
1006
  }
971
1007
  var templateTexts = /* @__PURE__ */ new WeakMap();
@@ -1502,14 +1538,6 @@ function parseExpressionUncached(expr, scope) {
1502
1538
  return () => left() ?? right();
1503
1539
  }
1504
1540
  }
1505
- const andMatch = expr.match(RE_AND);
1506
- if (andMatch) {
1507
- const left = parseExpression(andMatch[1].trim(), scope);
1508
- const right = parseExpression(andMatch[2].trim(), scope);
1509
- if (left && right) {
1510
- return () => left() && right();
1511
- }
1512
- }
1513
1541
  const orMatch = expr.match(RE_OR);
1514
1542
  if (orMatch) {
1515
1543
  const left = parseExpression(orMatch[1].trim(), scope);
@@ -1518,6 +1546,14 @@ function parseExpressionUncached(expr, scope) {
1518
1546
  return () => left() || right();
1519
1547
  }
1520
1548
  }
1549
+ const andMatch = expr.match(RE_AND);
1550
+ if (andMatch) {
1551
+ const left = parseExpression(andMatch[1].trim(), scope);
1552
+ const right = parseExpression(andMatch[2].trim(), scope);
1553
+ if (left && right) {
1554
+ return () => left() && right();
1555
+ }
1556
+ }
1521
1557
  const compMatch = expr.match(RE_COMPARISON);
1522
1558
  if (compMatch) {
1523
1559
  const left = parseExpression(compMatch[1].trim(), scope);
@@ -1547,6 +1583,19 @@ function parseExpressionUncached(expr, scope) {
1547
1583
  };
1548
1584
  }
1549
1585
  }
1586
+ const addMatch = expr.match(RE_ADD);
1587
+ if (addMatch) {
1588
+ const left = parseExpression(addMatch[1].trim(), scope);
1589
+ const right = parseExpression(addMatch[3].trim(), scope);
1590
+ if (left && right) {
1591
+ const op = addMatch[2];
1592
+ return () => {
1593
+ const l = left(), r = right();
1594
+ if (op === "+") return l + r;
1595
+ return l - r;
1596
+ };
1597
+ }
1598
+ }
1550
1599
  const mulMatch = expr.match(RE_MUL);
1551
1600
  if (mulMatch) {
1552
1601
  const left = parseExpression(mulMatch[1].trim(), scope);
@@ -1566,19 +1615,6 @@ function parseExpressionUncached(expr, scope) {
1566
1615
  };
1567
1616
  }
1568
1617
  }
1569
- const addMatch = expr.match(RE_ADD);
1570
- if (addMatch) {
1571
- const left = parseExpression(addMatch[1].trim(), scope);
1572
- const right = parseExpression(addMatch[3].trim(), scope);
1573
- if (left && right) {
1574
- const op = addMatch[2];
1575
- return () => {
1576
- const l = left(), r = right();
1577
- if (op === "+") return l + r;
1578
- return l - r;
1579
- };
1580
- }
1581
- }
1582
1618
  const tmplMatch = expr.match(RE_TEMPLATE_LIT);
1583
1619
  if (tmplMatch) {
1584
1620
  const raw = tmplMatch[1];
@@ -1653,6 +1689,7 @@ function buildEvaluator(expr, scope) {
1653
1689
  return key in scope.getters;
1654
1690
  },
1655
1691
  get(_, key) {
1692
+ if (UNSAFE_METHOD_NAMES.has(key)) return void 0;
1656
1693
  const g = scope.getters[key];
1657
1694
  return g ? g() : void 0;
1658
1695
  }
@@ -1808,6 +1845,7 @@ function buildHandler(expr, scope) {
1808
1845
  return key in scope.getters || key in scope.setters;
1809
1846
  },
1810
1847
  get(_, key) {
1848
+ if (UNSAFE_METHOD_NAMES.has(key)) return void 0;
1811
1849
  const g = scope.getters[key];
1812
1850
  return g ? g() : void 0;
1813
1851
  },
@@ -1843,11 +1881,10 @@ function parseState(raw) {
1843
1881
  try {
1844
1882
  parsed = JSON.parse(raw);
1845
1883
  } catch {
1846
- try {
1847
- parsed = JSON.parse(raw.replace(RE_UNQUOTED_KEYS, '"$1":'));
1848
- } catch {
1849
- return {};
1884
+ if (_debug) {
1885
+ dbg("parseState: Invalid JSON in data-forma-state \u2014 use valid JSON with quoted keys. Got:", raw.slice(0, 200));
1850
1886
  }
1887
+ return {};
1851
1888
  }
1852
1889
  for (const key of FORBIDDEN_STATE_KEYS) {
1853
1890
  if (key in parsed) delete parsed[key];
@@ -1867,14 +1904,104 @@ function initScope(stateEl) {
1867
1904
  const getters = {};
1868
1905
  const setters = {};
1869
1906
  for (const [key, initial] of Object.entries(state)) {
1870
- const [get, set] = createValueSignal(initial);
1907
+ const [get, set] = createSignal(initial);
1871
1908
  getters[key] = get;
1872
1909
  setters[key] = set;
1873
1910
  }
1874
1911
  getters["$refetch"] = () => $refetch;
1875
1912
  return { getters, setters };
1876
1913
  }
1914
+ var SAFE_EL_PROPS = /* @__PURE__ */ new Set([
1915
+ // Identity & attributes
1916
+ "id",
1917
+ "className",
1918
+ "tagName",
1919
+ "nodeName",
1920
+ "getAttribute",
1921
+ "setAttribute",
1922
+ "removeAttribute",
1923
+ "hasAttribute",
1924
+ "toggleAttribute",
1925
+ "dataset",
1926
+ "classList",
1927
+ // Content
1928
+ "textContent",
1929
+ "innerText",
1930
+ // Form elements
1931
+ "value",
1932
+ "checked",
1933
+ "disabled",
1934
+ "selected",
1935
+ "type",
1936
+ "name",
1937
+ "placeholder",
1938
+ "readOnly",
1939
+ "required",
1940
+ "min",
1941
+ "max",
1942
+ "step",
1943
+ "pattern",
1944
+ // Dimensions & position
1945
+ "getBoundingClientRect",
1946
+ "offsetWidth",
1947
+ "offsetHeight",
1948
+ "offsetTop",
1949
+ "offsetLeft",
1950
+ "clientWidth",
1951
+ "clientHeight",
1952
+ "scrollWidth",
1953
+ "scrollHeight",
1954
+ "scrollTop",
1955
+ "scrollLeft",
1956
+ // Style
1957
+ "style",
1958
+ "hidden",
1959
+ // Focus & interaction
1960
+ "focus",
1961
+ "blur",
1962
+ "click",
1963
+ "scrollIntoView",
1964
+ "scrollTo",
1965
+ // Traversal (safe — returns elements, not window/document)
1966
+ "closest",
1967
+ "matches",
1968
+ "querySelector",
1969
+ "querySelectorAll",
1970
+ "children",
1971
+ "childElementCount",
1972
+ "firstElementChild",
1973
+ "lastElementChild",
1974
+ "nextElementSibling",
1975
+ "previousElementSibling"
1976
+ ]);
1977
+ function createSafeElProxy(el) {
1978
+ return new Proxy(el, {
1979
+ get(target, prop) {
1980
+ if (typeof prop === "symbol") return Reflect.get(target, prop);
1981
+ if (!SAFE_EL_PROPS.has(prop)) return void 0;
1982
+ const val = Reflect.get(target, prop);
1983
+ return typeof val === "function" ? val.bind(target) : val;
1984
+ },
1985
+ set(target, prop, value) {
1986
+ if (typeof prop === "symbol") return false;
1987
+ if (!SAFE_EL_PROPS.has(prop)) return false;
1988
+ return Reflect.set(target, prop, value);
1989
+ }
1990
+ });
1991
+ }
1877
1992
  function bindElement(el, scope, disposers) {
1993
+ const elMagics = {
1994
+ $el: createSafeElProxy(el),
1995
+ $dispatch: (name, detail) => {
1996
+ el.dispatchEvent(new CustomEvent(name, {
1997
+ bubbles: true,
1998
+ composed: true,
1999
+ // crosses Shadow DOM boundaries (important for <forma-stage>)
2000
+ detail
2001
+ }));
2002
+ }
2003
+ };
2004
+ scope = createChildScope(scope, elMagics);
1878
2005
  const known = getDirectives(el);
1879
2006
  const computedAttr = !known || known.has("data-computed") ? el.getAttribute("data-computed") : null;
1880
2007
  if (computedAttr) {
@@ -2254,16 +2381,16 @@ function bindElement(el, scope, disposers) {
2254
2381
  else if (k === "error") errorTarget = v;
2255
2382
  else if (k === "poll") interval = parseInt(v ?? "0", 10);
2256
2383
  }
2257
- const [getTarget, setTarget] = createValueSignal(null);
2384
+ const [getTarget, setTarget] = createSignal(null);
2258
2385
  scope.getters[target] = getTarget;
2259
2386
  scope.setters[target] = setTarget;
2260
2387
  if (loadingTarget) {
2261
- const [gl, sl] = createValueSignal(false);
2388
+ const [gl, sl] = createSignal(false);
2262
2389
  scope.getters[loadingTarget] = gl;
2263
2390
  scope.setters[loadingTarget] = sl;
2264
2391
  }
2265
2392
  if (errorTarget) {
2266
- const [ge, se] = createValueSignal(null);
2393
+ const [ge, se] = createSignal(null);
2267
2394
  scope.getters[errorTarget] = ge;
2268
2395
  scope.setters[errorTarget] = se;
2269
2396
  }