@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
@@ -3,23 +3,23 @@
3
3
  var alienSignals = require('alien-signals');
4
4
 
5
5
  // src/reactive/signal.ts
6
- function applySignalSet(s, v) {
6
+ function applySignalSet(s, v, equals) {
7
7
  if (typeof v !== "function") {
8
8
  s(v);
9
9
  return;
10
10
  }
11
- alienSignals.pauseTracking();
11
+ const prevSub = alienSignals.setActiveSub(void 0);
12
12
  const prev = s();
13
- alienSignals.resumeTracking();
14
- s(v(prev));
13
+ alienSignals.setActiveSub(prevSub);
14
+ const next = v(prev);
15
+ s(next);
15
16
  }
16
- function createSignal(initialValue) {
17
+ function createSignal(initialValue, options) {
17
18
  const s = alienSignals.signal(initialValue);
18
19
  const getter = s;
19
20
  const setter = (v) => applySignalSet(s, v);
20
21
  return [getter, setter];
21
22
  }
22
- var createValueSignal = createSignal;
23
23
  function internalEffect(fn) {
24
24
  const dispose = alienSignals.effect(fn);
25
25
  return dispose;
@@ -852,12 +852,11 @@ var RE_TOGGLE = /^(\w+)\s*=\s*!(\w+)$/;
852
852
  var RE_ASSIGN = /^(\w+)\s*=\s*(.+)$/;
853
853
  var RE_COMPOUND = /^(\w+)\s*(\+=|-=|\*=|\/=)\s*(.+)$/;
854
854
  var RE_IF_PREFIX = /^if\b/;
855
- var RE_UNQUOTED_KEYS = /(\w+)\s*:/g;
856
855
  var RE_COMPUTED = /^(\w+)\s*=\s*(.+)$/;
857
856
  var RE_FETCH = /^(.+?)(?:→|->)\s*(\S+)(.*)$/;
858
857
  var RE_FETCH_METHOD = /^(GET|POST|PUT|PATCH|DELETE)\s+(.+)$/i;
859
858
  var RE_STRIP_ITEM_BRACES = /^\{item\.?|\}$/g;
860
- var RE_EVENT_REF = /\bevent\b|\$event\b/;
859
+ var RE_EVENT_REF = /\bevent\s*[.([]|\$event\b/;
861
860
  var RE_REFETCH_CALL = /^\$refetch\(\s*['"]([^'"]+)['"]\s*\)$/;
862
861
  var TRANSITION_STATE_SYM = /* @__PURE__ */ Symbol.for("forma-transition-state");
863
862
  var EXPRESSION_CACHE_MAX = 2048;
@@ -872,6 +871,14 @@ function cacheExpression(key, factory) {
872
871
  var scopeExpressionCache = /* @__PURE__ */ new WeakMap();
873
872
  var scopeHandlerCache = /* @__PURE__ */ new WeakMap();
874
873
  var compiledTemplateCache = /* @__PURE__ */ new Map();
874
+ var COMPILED_TEMPLATE_CACHE_MAX = 2048;
875
+ function cacheCompiledTemplate(key, template) {
876
+ if (compiledTemplateCache.size >= COMPILED_TEMPLATE_CACHE_MAX) {
877
+ const first = compiledTemplateCache.keys().next().value;
878
+ if (first !== void 0) compiledTemplateCache.delete(first);
879
+ }
880
+ compiledTemplateCache.set(key, template);
881
+ }
875
882
  var UNSAFE_METHOD_NAMES = /* @__PURE__ */ new Set([
876
883
  "constructor",
877
884
  "__proto__",
@@ -880,7 +887,8 @@ var UNSAFE_METHOD_NAMES = /* @__PURE__ */ new Set([
880
887
  "__defineSetter__",
881
888
  "__lookupGetter__",
882
889
  "__lookupSetter__",
883
- "eval"
890
+ "eval",
891
+ "Function"
884
892
  ]);
885
893
  var BLOCKED_METHOD_REGEXES = (() => {
886
894
  const result = [];
@@ -904,8 +912,36 @@ function findBlockedMethod(expr) {
904
912
  if (dotRe.test(cleaned)) return name;
905
913
  if (bracketRe.test(cleaned)) return name;
906
914
  }
915
+ if (cleaned.includes("[")) {
916
+ const bracketContents = extractBracketContents(cleaned);
917
+ for (const content of bracketContents) {
918
+ if (!content.includes("+")) continue;
919
+ const fragments = content.match(/['"`]([^'"`]*?)['"`]/g);
920
+ if (!fragments) continue;
921
+ const joined = fragments.map((f) => f.slice(1, -1)).join("");
922
+ if (UNSAFE_METHOD_NAMES.has(joined)) return joined;
923
+ }
924
+ }
907
925
  return null;
908
926
  }
927
+ function extractBracketContents(expr) {
928
+ const results = [];
929
+ let depth = 0;
930
+ let start = -1;
931
+ for (let i = 0; i < expr.length; i++) {
932
+ if (expr[i] === "[") {
933
+ if (depth === 0) start = i + 1;
934
+ depth++;
935
+ } else if (expr[i] === "]") {
936
+ depth--;
937
+ if (depth === 0 && start >= 0) {
938
+ results.push(expr.slice(start, i));
939
+ start = -1;
940
+ }
941
+ }
942
+ }
943
+ return results;
944
+ }
909
945
  var TEXT_BINDING_SYM = /* @__PURE__ */ Symbol.for("forma-text-binding-cache");
910
946
  function toTextValue(value2) {
911
947
  if (value2 == null) return "";
@@ -1241,7 +1277,7 @@ function compileTemplate(text) {
1241
1277
  dynamics,
1242
1278
  hasItemRef: dynamics.length > 0
1243
1279
  };
1244
- compiledTemplateCache.set(text, result);
1280
+ cacheCompiledTemplate(text, result);
1245
1281
  return result;
1246
1282
  }
1247
1283
  var templateTexts = /* @__PURE__ */ new WeakMap();
@@ -1778,14 +1814,6 @@ function parseExpressionUncached(expr, scope) {
1778
1814
  return () => left() ?? right();
1779
1815
  }
1780
1816
  }
1781
- const andMatch = expr.match(RE_AND);
1782
- if (andMatch) {
1783
- const left = parseExpression(andMatch[1].trim(), scope);
1784
- const right = parseExpression(andMatch[2].trim(), scope);
1785
- if (left && right) {
1786
- return () => left() && right();
1787
- }
1788
- }
1789
1817
  const orMatch = expr.match(RE_OR);
1790
1818
  if (orMatch) {
1791
1819
  const left = parseExpression(orMatch[1].trim(), scope);
@@ -1794,6 +1822,14 @@ function parseExpressionUncached(expr, scope) {
1794
1822
  return () => left() || right();
1795
1823
  }
1796
1824
  }
1825
+ const andMatch = expr.match(RE_AND);
1826
+ if (andMatch) {
1827
+ const left = parseExpression(andMatch[1].trim(), scope);
1828
+ const right = parseExpression(andMatch[2].trim(), scope);
1829
+ if (left && right) {
1830
+ return () => left() && right();
1831
+ }
1832
+ }
1797
1833
  const compMatch = expr.match(RE_COMPARISON);
1798
1834
  if (compMatch) {
1799
1835
  const left = parseExpression(compMatch[1].trim(), scope);
@@ -1823,6 +1859,19 @@ function parseExpressionUncached(expr, scope) {
1823
1859
  };
1824
1860
  }
1825
1861
  }
1862
+ const addMatch = expr.match(RE_ADD);
1863
+ if (addMatch) {
1864
+ const left = parseExpression(addMatch[1].trim(), scope);
1865
+ const right = parseExpression(addMatch[3].trim(), scope);
1866
+ if (left && right) {
1867
+ const op = addMatch[2];
1868
+ return () => {
1869
+ const l = left(), r = right();
1870
+ if (op === "+") return l + r;
1871
+ return l - r;
1872
+ };
1873
+ }
1874
+ }
1826
1875
  const mulMatch = expr.match(RE_MUL);
1827
1876
  if (mulMatch) {
1828
1877
  const left = parseExpression(mulMatch[1].trim(), scope);
@@ -1842,19 +1891,6 @@ function parseExpressionUncached(expr, scope) {
1842
1891
  };
1843
1892
  }
1844
1893
  }
1845
- const addMatch = expr.match(RE_ADD);
1846
- if (addMatch) {
1847
- const left = parseExpression(addMatch[1].trim(), scope);
1848
- const right = parseExpression(addMatch[3].trim(), scope);
1849
- if (left && right) {
1850
- const op = addMatch[2];
1851
- return () => {
1852
- const l = left(), r = right();
1853
- if (op === "+") return l + r;
1854
- return l - r;
1855
- };
1856
- }
1857
- }
1858
1894
  const tmplMatch = expr.match(RE_TEMPLATE_LIT);
1859
1895
  if (tmplMatch) {
1860
1896
  const raw = tmplMatch[1];
@@ -1929,6 +1965,7 @@ function buildEvaluator(expr, scope) {
1929
1965
  return key in scope.getters;
1930
1966
  },
1931
1967
  get(_, key) {
1968
+ if (UNSAFE_METHOD_NAMES.has(key)) return void 0;
1932
1969
  const g = scope.getters[key];
1933
1970
  return g ? g() : void 0;
1934
1971
  }
@@ -2084,6 +2121,7 @@ function buildHandler(expr, scope) {
2084
2121
  return key in scope.getters || key in scope.setters;
2085
2122
  },
2086
2123
  get(_, key) {
2124
+ if (UNSAFE_METHOD_NAMES.has(key)) return void 0;
2087
2125
  const g = scope.getters[key];
2088
2126
  return g ? g() : void 0;
2089
2127
  },
@@ -2119,11 +2157,10 @@ function parseState(raw) {
2119
2157
  try {
2120
2158
  parsed = JSON.parse(raw);
2121
2159
  } catch {
2122
- try {
2123
- parsed = JSON.parse(raw.replace(RE_UNQUOTED_KEYS, '"$1":'));
2124
- } catch {
2125
- return {};
2160
+ if (_debug) {
2161
+ dbg("parseState: Invalid JSON in data-forma-state \u2014 use valid JSON with quoted keys. Got:", raw.slice(0, 200));
2126
2162
  }
2163
+ return {};
2127
2164
  }
2128
2165
  for (const key of FORBIDDEN_STATE_KEYS) {
2129
2166
  if (key in parsed) delete parsed[key];
@@ -2143,14 +2180,104 @@ function initScope(stateEl) {
2143
2180
  const getters = {};
2144
2181
  const setters = {};
2145
2182
  for (const [key, initial] of Object.entries(state)) {
2146
- const [get, set] = createValueSignal(initial);
2183
+ const [get, set] = createSignal(initial);
2147
2184
  getters[key] = get;
2148
2185
  setters[key] = set;
2149
2186
  }
2150
2187
  getters["$refetch"] = () => $refetch;
2151
2188
  return { getters, setters };
2152
2189
  }
2190
+ var SAFE_EL_PROPS = /* @__PURE__ */ new Set([
2191
+ // Identity & attributes
2192
+ "id",
2193
+ "className",
2194
+ "tagName",
2195
+ "nodeName",
2196
+ "getAttribute",
2197
+ "setAttribute",
2198
+ "removeAttribute",
2199
+ "hasAttribute",
2200
+ "toggleAttribute",
2201
+ "dataset",
2202
+ "classList",
2203
+ // Content
2204
+ "textContent",
2205
+ "innerText",
2206
+ // Form elements
2207
+ "value",
2208
+ "checked",
2209
+ "disabled",
2210
+ "selected",
2211
+ "type",
2212
+ "name",
2213
+ "placeholder",
2214
+ "readOnly",
2215
+ "required",
2216
+ "min",
2217
+ "max",
2218
+ "step",
2219
+ "pattern",
2220
+ // Dimensions & position
2221
+ "getBoundingClientRect",
2222
+ "offsetWidth",
2223
+ "offsetHeight",
2224
+ "offsetTop",
2225
+ "offsetLeft",
2226
+ "clientWidth",
2227
+ "clientHeight",
2228
+ "scrollWidth",
2229
+ "scrollHeight",
2230
+ "scrollTop",
2231
+ "scrollLeft",
2232
+ // Style
2233
+ "style",
2234
+ "hidden",
2235
+ // Focus & interaction
2236
+ "focus",
2237
+ "blur",
2238
+ "click",
2239
+ "scrollIntoView",
2240
+ "scrollTo",
2241
+ // Traversal (safe — returns elements, not window/document)
2242
+ "closest",
2243
+ "matches",
2244
+ "querySelector",
2245
+ "querySelectorAll",
2246
+ "children",
2247
+ "childElementCount",
2248
+ "firstElementChild",
2249
+ "lastElementChild",
2250
+ "nextElementSibling",
2251
+ "previousElementSibling"
2252
+ ]);
2253
+ function createSafeElProxy(el) {
2254
+ return new Proxy(el, {
2255
+ get(target, prop) {
2256
+ if (typeof prop === "symbol") return Reflect.get(target, prop);
2257
+ if (!SAFE_EL_PROPS.has(prop)) return void 0;
2258
+ const val = Reflect.get(target, prop);
2259
+ return typeof val === "function" ? val.bind(target) : val;
2260
+ },
2261
+ set(target, prop, value2) {
2262
+ if (typeof prop === "symbol") return false;
2263
+ if (!SAFE_EL_PROPS.has(prop)) return false;
2264
+ return Reflect.set(target, prop, value2);
2265
+ }
2266
+ });
2267
+ }
2153
2268
  function bindElement(el, scope, disposers) {
2269
+ const elMagics = {
2270
+ $el: createSafeElProxy(el),
2271
+ $dispatch: (name, detail) => {
2272
+ el.dispatchEvent(new CustomEvent(name, {
2273
+ bubbles: true,
2274
+ composed: true,
2275
+ // crosses Shadow DOM boundaries (important for <forma-stage>)
2276
+ detail
2277
+ }));
2278
+ }
2279
+ };
2280
+ scope = createChildScope(scope, elMagics);
2154
2281
  const known = getDirectives(el);
2155
2282
  const computedAttr = !known || known.has("data-computed") ? el.getAttribute("data-computed") : null;
2156
2283
  if (computedAttr) {
@@ -2530,16 +2657,16 @@ function bindElement(el, scope, disposers) {
2530
2657
  else if (k === "error") errorTarget = v;
2531
2658
  else if (k === "poll") interval = parseInt(v ?? "0", 10);
2532
2659
  }
2533
- const [getTarget, setTarget] = createValueSignal(null);
2660
+ const [getTarget, setTarget] = createSignal(null);
2534
2661
  scope.getters[target] = getTarget;
2535
2662
  scope.setters[target] = setTarget;
2536
2663
  if (loadingTarget) {
2537
- const [gl, sl] = createValueSignal(false);
2664
+ const [gl, sl] = createSignal(false);
2538
2665
  scope.getters[loadingTarget] = gl;
2539
2666
  scope.setters[loadingTarget] = sl;
2540
2667
  }
2541
2668
  if (errorTarget) {
2542
- const [ge, se] = createValueSignal(null);
2669
+ const [ge, se] = createSignal(null);
2543
2670
  scope.getters[errorTarget] = ge;
2544
2671
  scope.setters[errorTarget] = se;
2545
2672
  }