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