@getforma/core 1.1.0 → 1.3.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 (45) hide show
  1. package/dist/{chunk-MIOMT2CB.js → chunk-AJP4OVCN.js} +29 -9
  2. package/dist/chunk-AJP4OVCN.js.map +1 -0
  3. package/dist/{chunk-XLVBYXOU.js → chunk-ETPJTPYD.js} +6 -3
  4. package/dist/chunk-ETPJTPYD.js.map +1 -0
  5. package/dist/{chunk-JRQNDXX7.cjs → chunk-FBM7V4NE.cjs} +68 -47
  6. package/dist/chunk-FBM7V4NE.cjs.map +1 -0
  7. package/dist/{chunk-W7OUWVRA.cjs → chunk-H7MDUHS5.cjs} +6 -2
  8. package/dist/chunk-H7MDUHS5.cjs.map +1 -0
  9. package/dist/{chunk-2Y5US35K.cjs → chunk-KUXNZ5MG.cjs} +12 -12
  10. package/dist/{chunk-2Y5US35K.cjs.map → chunk-KUXNZ5MG.cjs.map} +1 -1
  11. package/dist/{chunk-INNOI6TG.js → chunk-Y3A2EVVS.js} +3 -3
  12. package/dist/{chunk-INNOI6TG.js.map → chunk-Y3A2EVVS.js.map} +1 -1
  13. package/dist/forma-runtime-csp.js +178 -79
  14. package/dist/forma-runtime.js +178 -79
  15. package/dist/formajs-runtime-hardened.global.js +178 -79
  16. package/dist/formajs-runtime-hardened.global.js.map +1 -1
  17. package/dist/formajs-runtime.global.js +178 -79
  18. package/dist/formajs-runtime.global.js.map +1 -1
  19. package/dist/formajs.global.js +245 -61
  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 +307 -143
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +34 -2
  26. package/dist/index.d.ts +34 -2
  27. package/dist/index.js +248 -88
  28. package/dist/index.js.map +1 -1
  29. package/dist/runtime-hardened.cjs +178 -79
  30. package/dist/runtime-hardened.cjs.map +1 -1
  31. package/dist/runtime-hardened.js +178 -79
  32. package/dist/runtime-hardened.js.map +1 -1
  33. package/dist/runtime.cjs +198 -103
  34. package/dist/runtime.cjs.map +1 -1
  35. package/dist/runtime.js +174 -79
  36. package/dist/runtime.js.map +1 -1
  37. package/dist/server.cjs +7 -7
  38. package/dist/server.js +2 -2
  39. package/dist/tc39-compat.cjs +3 -3
  40. package/dist/tc39-compat.js +1 -1
  41. package/package.json +1 -1
  42. package/dist/chunk-JRQNDXX7.cjs.map +0 -1
  43. package/dist/chunk-MIOMT2CB.js.map +0 -1
  44. package/dist/chunk-W7OUWVRA.cjs.map +0 -1
  45. package/dist/chunk-XLVBYXOU.js.map +0 -1
@@ -239,14 +239,18 @@ function reconcileList(parent, oldItems, newItems, oldNodes, keyFn, createFn, up
239
239
  }
240
240
  const oldKeyMap = /* @__PURE__ */ new Map();
241
241
  for (let i = 0; i < oldLen; i++) {
242
- oldKeyMap.set(keyFn(oldItems[i]), i);
242
+ const k = keyFn(oldItems[i]);
243
+ const bucket = oldKeyMap.get(k);
244
+ if (bucket) bucket.push(i);
245
+ else oldKeyMap.set(k, [i]);
243
246
  }
244
247
  const oldIndices = new Array(newLen);
245
248
  const oldUsed = new Uint8Array(oldLen);
246
249
  for (let i = 0; i < newLen; i++) {
247
250
  const key = keyFn(newItems[i]);
248
- const oldIdx = oldKeyMap.get(key);
249
- if (oldIdx !== void 0) {
251
+ const bucket = oldKeyMap.get(key);
252
+ if (bucket && bucket.length > 0) {
253
+ const oldIdx = bucket.shift();
250
254
  oldIndices[i] = oldIdx;
251
255
  oldUsed[oldIdx] = 1;
252
256
  } else {
@@ -595,12 +599,12 @@ function findInsertionPoint(parent, newChild, newChildren) {
595
599
  return null;
596
600
  }
597
601
  function createReconciler(config) {
598
- let _lastHtml = "";
602
+ const lastHtmlByContainer = /* @__PURE__ */ new WeakMap();
599
603
  return function reconcile2(container, html) {
600
604
  const trimmed = html.trim();
601
605
  if (!trimmed) return;
602
- if (trimmed === _lastHtml && container.hasChildNodes()) return;
603
- _lastHtml = trimmed;
606
+ if (lastHtmlByContainer.get(container) === trimmed && container.hasChildNodes()) return;
607
+ lastHtmlByContainer.set(container, trimmed);
604
608
  config.disconnectObserver();
605
609
  try {
606
610
  if (!container.hasChildNodes() || container.children.length === 0) {
@@ -914,9 +918,97 @@ var RE_TERNARY = /^(.+?)\s*\?\s*(.+?)\s*:\s*(.+)$/;
914
918
  var RE_NULLISH = /^(.+?)\s*\?\?\s*(.+)$/;
915
919
  var RE_AND = /^(.+?)\s*&&\s*(.+)$/;
916
920
  var RE_OR = /^(.+?)\s*\|\|\s*(.+)$/;
917
- var RE_COMPARISON = /^(.+?)\s*(===|!==|==|!=|>=|<=|>|<)\s*(.+)$/;
918
- var RE_MUL = /^(.+?)\s*([*/%])\s*(.+)$/;
919
- var RE_ADD = /^(.+?)\s*([+-])\s*(.+)$/;
921
+ var UNARY_PRECEDERS = "+-*/%<>=!&|^~(,[{?:";
922
+ function matchBinaryOp(expr, ops) {
923
+ const SQ = String.fromCharCode(39);
924
+ const DQ = String.fromCharCode(34);
925
+ const BT = String.fromCharCode(96);
926
+ const BS = String.fromCharCode(92);
927
+ let depth = 0;
928
+ let inSingle = false, inDouble = false, inTemplate = false, escaped = false;
929
+ let prevSig = "";
930
+ let bestIdx = -1;
931
+ let bestOp = "";
932
+ for (let i = 0; i < expr.length; i++) {
933
+ const ch = expr[i];
934
+ if (escaped) {
935
+ escaped = false;
936
+ continue;
937
+ }
938
+ if (ch === BS && (inSingle || inDouble || inTemplate)) {
939
+ escaped = true;
940
+ continue;
941
+ }
942
+ if (inSingle) {
943
+ if (ch === SQ) {
944
+ inSingle = false;
945
+ prevSig = DQ;
946
+ }
947
+ continue;
948
+ }
949
+ if (inDouble) {
950
+ if (ch === DQ) {
951
+ inDouble = false;
952
+ prevSig = DQ;
953
+ }
954
+ continue;
955
+ }
956
+ if (inTemplate) {
957
+ if (ch === BT) {
958
+ inTemplate = false;
959
+ prevSig = DQ;
960
+ }
961
+ continue;
962
+ }
963
+ if (ch === SQ) {
964
+ inSingle = true;
965
+ continue;
966
+ }
967
+ if (ch === DQ) {
968
+ inDouble = true;
969
+ continue;
970
+ }
971
+ if (ch === BT) {
972
+ inTemplate = true;
973
+ continue;
974
+ }
975
+ if (ch === "(" || ch === "[" || ch === "{") {
976
+ depth++;
977
+ prevSig = ch;
978
+ continue;
979
+ }
980
+ if (ch === ")" || ch === "]" || ch === "}") {
981
+ if (depth > 0) depth--;
982
+ prevSig = ch;
983
+ continue;
984
+ }
985
+ if (depth !== 0) continue;
986
+ if (ch === " " || ch === " " || ch === "\n") continue;
987
+ if (i > 0) {
988
+ let matchedOp = "";
989
+ for (const op of ops) {
990
+ if (expr.startsWith(op, i)) {
991
+ matchedOp = op;
992
+ break;
993
+ }
994
+ }
995
+ if (matchedOp) {
996
+ const isPlusMinus = matchedOp === "+" || matchedOp === "-";
997
+ const unary = isPlusMinus && (prevSig === "" || UNARY_PRECEDERS.includes(prevSig));
998
+ if (!unary) {
999
+ bestIdx = i;
1000
+ bestOp = matchedOp;
1001
+ }
1002
+ i += matchedOp.length - 1;
1003
+ prevSig = matchedOp[matchedOp.length - 1];
1004
+ continue;
1005
+ }
1006
+ }
1007
+ prevSig = ch;
1008
+ }
1009
+ if (bestIdx === -1) return null;
1010
+ return { left: expr.slice(0, bestIdx).trim(), op: bestOp, right: expr.slice(bestIdx + bestOp.length).trim() };
1011
+ }
920
1012
  var RE_TEMPLATE_LIT = /^`([^`]*)`$/;
921
1013
  var RE_TEMPLATE_INTERP = /\$\{([^}]+)\}/g;
922
1014
  var RE_GROUP_METHOD_CALL = /^\((.+)\)\.(\w+)\((.*)\)$/;
@@ -1873,12 +1965,12 @@ function parseExpressionUncached(expr, scope) {
1873
1965
  return () => left() && right();
1874
1966
  }
1875
1967
  }
1876
- const compMatch = expr.match(RE_COMPARISON);
1968
+ const compMatch = matchBinaryOp(expr, ["===", "!==", "==", "!=", ">=", "<=", ">", "<"]);
1877
1969
  if (compMatch) {
1878
- const left = parseExpression(compMatch[1].trim(), scope);
1879
- const right = parseExpression(compMatch[3].trim(), scope);
1970
+ const left = parseExpression(compMatch.left, scope);
1971
+ const right = parseExpression(compMatch.right, scope);
1880
1972
  if (left && right) {
1881
- const op = compMatch[2];
1973
+ const op = compMatch.op;
1882
1974
  return () => {
1883
1975
  const l = left(), r = right();
1884
1976
  switch (op) {
@@ -1902,12 +1994,12 @@ function parseExpressionUncached(expr, scope) {
1902
1994
  };
1903
1995
  }
1904
1996
  }
1905
- const addMatch = expr.match(RE_ADD);
1997
+ const addMatch = matchBinaryOp(expr, ["+", "-"]);
1906
1998
  if (addMatch) {
1907
- const left = parseExpression(addMatch[1].trim(), scope);
1908
- const right = parseExpression(addMatch[3].trim(), scope);
1999
+ const left = parseExpression(addMatch.left, scope);
2000
+ const right = parseExpression(addMatch.right, scope);
1909
2001
  if (left && right) {
1910
- const op = addMatch[2];
2002
+ const op = addMatch.op;
1911
2003
  return () => {
1912
2004
  const l = left(), r = right();
1913
2005
  if (op === "+") return l + r;
@@ -1915,12 +2007,12 @@ function parseExpressionUncached(expr, scope) {
1915
2007
  };
1916
2008
  }
1917
2009
  }
1918
- const mulMatch = expr.match(RE_MUL);
2010
+ const mulMatch = matchBinaryOp(expr, ["*", "/", "%"]);
1919
2011
  if (mulMatch) {
1920
- const left = parseExpression(mulMatch[1].trim(), scope);
1921
- const right = parseExpression(mulMatch[3].trim(), scope);
2012
+ const left = parseExpression(mulMatch.left, scope);
2013
+ const right = parseExpression(mulMatch.right, scope);
1922
2014
  if (left && right) {
1923
- const op = mulMatch[2];
2015
+ const op = mulMatch.op;
1924
2016
  return () => {
1925
2017
  const l = left(), r = right();
1926
2018
  switch (op) {
@@ -2345,25 +2437,61 @@ function bindElement(el, scope, disposers) {
2345
2437
  const modelExpr = !known || known.has("data-model") ? el.getAttribute("data-model") : null;
2346
2438
  if (modelExpr) {
2347
2439
  const prop = modelExpr.replace(RE_STRIP_BRACES, "").trim();
2348
- const getter = scope.getters[prop];
2349
- const setter = scope.setters[prop];
2440
+ let getter = scope.getters[prop];
2441
+ let setter = scope.setters[prop];
2442
+ if ((!getter || !setter) && prop.includes(".")) {
2443
+ getter = buildEvaluator(prop, scope);
2444
+ const lastDot = prop.lastIndexOf(".");
2445
+ const basePath = prop.slice(0, lastDot);
2446
+ const key = prop.slice(lastDot + 1);
2447
+ const baseGet = buildEvaluator(basePath, scope);
2448
+ setter = (v) => {
2449
+ const base = baseGet();
2450
+ if (base != null && typeof base === "object") base[key] = v;
2451
+ };
2452
+ }
2350
2453
  if (getter && setter) {
2351
2454
  const input = el;
2455
+ const tag = input.tagName;
2352
2456
  const dispose = internalEffect(() => {
2353
2457
  const val = getter();
2354
- if (input.type === "checkbox") {
2458
+ const type = input.type;
2459
+ if (type === "checkbox") {
2355
2460
  input.checked = !!val;
2461
+ const indetExpr = el.getAttribute("data-model-indeterminate");
2462
+ if (indetExpr) {
2463
+ const indetGetter = scope.getters[indetExpr.replace(RE_STRIP_BRACES, "").trim()];
2464
+ if (indetGetter) input.indeterminate = !!indetGetter();
2465
+ }
2466
+ } else if (type === "radio") {
2467
+ input.checked = String(val) === input.value;
2468
+ } else if (tag === "SELECT" && input.multiple) {
2469
+ const sel = input;
2470
+ const arr = Array.isArray(val) ? val.map(String) : [];
2471
+ for (const opt of Array.from(sel.options)) opt.selected = arr.includes(opt.value);
2356
2472
  } else {
2357
2473
  input.value = String(val ?? "");
2358
2474
  }
2359
2475
  });
2360
2476
  disposers.push(dispose);
2361
- const event = input.type === "checkbox" ? "change" : "input";
2477
+ const event = input.type === "checkbox" || input.type === "radio" || tag === "SELECT" ? "change" : "input";
2362
2478
  const onModelInput = () => {
2363
- if (input.type === "checkbox") {
2479
+ const type = input.type;
2480
+ if (type === "checkbox") {
2364
2481
  setter(input.checked);
2365
- } else if (input.type === "number" || input.type === "range") {
2366
- setter(Number(input.value));
2482
+ } else if (type === "radio") {
2483
+ if (input.checked) setter(input.value);
2484
+ } else if (tag === "SELECT" && input.multiple) {
2485
+ const sel = input;
2486
+ setter(Array.from(sel.selectedOptions).map((o) => o.value));
2487
+ } else if (type === "number" || type === "range") {
2488
+ const raw = input.value;
2489
+ if (raw === "") {
2490
+ setter(null);
2491
+ } else {
2492
+ const n = Number(raw);
2493
+ if (!Number.isNaN(n)) setter(n);
2494
+ }
2367
2495
  } else {
2368
2496
  setter(input.value);
2369
2497
  }
@@ -2555,59 +2683,30 @@ function bindElement(el, scope, disposers) {
2555
2683
  }
2556
2684
  }
2557
2685
  const prevNodes = new Set(oldNodes);
2558
- if (keyProp) {
2559
- const result = reconcileList(
2560
- el,
2561
- oldItems,
2562
- rawItems,
2563
- oldNodes,
2564
- (item) => String(item?.[keyProp] ?? ""),
2565
- (item) => {
2566
- const idx = rawItems.indexOf(item);
2567
- return createBoundClone2(item, idx);
2568
- },
2569
- (node, item) => {
2570
- const idx = rawItems.indexOf(item);
2571
- updateBoundClone2(node, item, idx);
2572
- },
2573
- void 0,
2574
- // beforeNode
2575
- listHooks
2576
- );
2577
- const nextNodes = new Set(result.nodes);
2578
- for (const n of prevNodes) {
2579
- if (!nextNodes.has(n)) {
2580
- if (n.hasAttribute?.("data-forma-leaving")) continue;
2581
- disposeCloneBindings2(n);
2582
- }
2583
- }
2584
- oldItems = result.items;
2585
- oldNodes = result.nodes;
2586
- } else {
2587
- const wrapped = rawItems.map((item, i) => ({ __idx: i, __item: item }));
2588
- const oldWrapped = oldItems;
2589
- const result = reconcileList(
2590
- el,
2591
- oldWrapped,
2592
- wrapped,
2593
- oldNodes,
2594
- (w) => w.__idx,
2595
- (w) => createBoundClone2(w.__item, w.__idx),
2596
- (node, w) => updateBoundClone2(node, w.__item, w.__idx),
2597
- void 0,
2598
- // beforeNode
2599
- listHooks
2600
- );
2601
- const nextNodes = new Set(result.nodes);
2602
- for (const n of prevNodes) {
2603
- if (!nextNodes.has(n)) {
2604
- if (n.hasAttribute?.("data-forma-leaving")) continue;
2605
- disposeCloneBindings2(n);
2606
- }
2686
+ const wrapped = rawItems.map((item, i) => ({ __idx: i, __item: item }));
2687
+ const oldWrapped = oldItems;
2688
+ const keyFn = keyProp ? (w) => String(w.__item?.[keyProp] ?? "") : (w) => w.__idx;
2689
+ const result = reconcileList(
2690
+ el,
2691
+ oldWrapped,
2692
+ wrapped,
2693
+ oldNodes,
2694
+ keyFn,
2695
+ (w) => createBoundClone2(w.__item, w.__idx),
2696
+ (node, w) => updateBoundClone2(node, w.__item, w.__idx),
2697
+ void 0,
2698
+ // beforeNode
2699
+ listHooks
2700
+ );
2701
+ const nextNodes = new Set(result.nodes);
2702
+ for (const n of prevNodes) {
2703
+ if (!nextNodes.has(n)) {
2704
+ if (n.hasAttribute?.("data-forma-leaving")) continue;
2705
+ disposeCloneBindings2(n);
2607
2706
  }
2608
- oldItems = result.items;
2609
- oldNodes = result.nodes;
2610
2707
  }
2708
+ oldItems = result.items;
2709
+ oldNodes = result.nodes;
2611
2710
  });
2612
2711
  disposers.push(dispose);
2613
2712
  }