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