@getforma/core 0.2.0 → 0.3.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.
- package/README.md +265 -13
- package/dist/{chunk-VKKPX4YU.js → chunk-5H52PKGF.js} +62 -12
- package/dist/chunk-5H52PKGF.js.map +1 -0
- package/dist/{chunk-VP5EOGM5.cjs → chunk-TSQ7AKFT.cjs} +62 -11
- package/dist/chunk-TSQ7AKFT.cjs.map +1 -0
- package/dist/forma-runtime-csp.js +2 -0
- package/dist/forma-runtime.js +2 -0
- package/dist/formajs-runtime-hardened.global.js +1 -1
- package/dist/formajs-runtime-hardened.global.js.map +1 -1
- package/dist/formajs-runtime.global.js +1 -1
- package/dist/formajs-runtime.global.js.map +1 -1
- package/dist/formajs.global.js +1 -1
- package/dist/formajs.global.js.map +1 -1
- package/dist/index.cjs +100 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -27
- package/dist/index.d.ts +43 -27
- package/dist/index.js +57 -27
- package/dist/index.js.map +1 -1
- package/dist/runtime-hardened.cjs +166 -50
- package/dist/runtime-hardened.cjs.map +1 -1
- package/dist/runtime-hardened.d.cts +86 -0
- package/dist/runtime-hardened.d.ts +86 -0
- package/dist/runtime-hardened.js +166 -50
- package/dist/runtime-hardened.js.map +1 -1
- package/dist/runtime.cjs +188 -72
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +167 -51
- package/dist/runtime.js.map +1 -1
- package/dist/ssr/index.cjs +59 -71
- package/dist/ssr/index.cjs.map +1 -1
- package/dist/ssr/index.d.cts +4 -0
- package/dist/ssr/index.d.ts +4 -0
- package/dist/ssr/index.js +59 -71
- package/dist/ssr/index.js.map +1 -1
- package/package.json +26 -3
- package/dist/chunk-VKKPX4YU.js.map +0 -1
- package/dist/chunk-VP5EOGM5.cjs.map +0 -1
|
@@ -755,6 +755,7 @@ if (buildUnsafeEvalMode) {
|
|
|
755
755
|
_unsafeEvalMode = buildUnsafeEvalMode;
|
|
756
756
|
if (_unsafeEvalMode === "locked-off") _allowUnsafeEval = false;
|
|
757
757
|
if (_unsafeEvalMode === "locked-on") _allowUnsafeEval = true;
|
|
758
|
+
if (_unsafeEvalMode === "mutable") _allowUnsafeEval = true;
|
|
758
759
|
}
|
|
759
760
|
var runtimeConfig = readRuntimeConfig();
|
|
760
761
|
var configUnsafeMode = runtimeConfig.lockUnsafeEval ? "locked-off" : runtimeConfig.unsafeEvalMode;
|
|
@@ -832,7 +833,6 @@ var RE_STRING_DOUBLE = /^"[^"]*"$/;
|
|
|
832
833
|
var RE_NUMBER = /^-?\d+(\.\d+)?$/;
|
|
833
834
|
var RE_IDENTIFIER = /^[a-zA-Z_$]\w*$/;
|
|
834
835
|
var RE_DOT_ACCESS = /^(\w+)\.(\w+)$/;
|
|
835
|
-
var RE_DEEP_DOT = /^(\w+)\.(\w+)\.(\w+)(?:\.(\w+))?$/;
|
|
836
836
|
var RE_BRACKET = /^(\w+)\[(\d+|'[^']*'|"[^"]*")\]$/;
|
|
837
837
|
var RE_TERNARY = /^(.+?)\s*\?\s*(.+?)\s*:\s*(.+)$/;
|
|
838
838
|
var RE_NULLISH = /^(.+?)\s*\?\?\s*(.+)$/;
|
|
@@ -843,7 +843,6 @@ var RE_MUL = /^(.+?)\s*([*/%])\s*(.+)$/;
|
|
|
843
843
|
var RE_ADD = /^(.+?)\s*([+-])\s*(.+)$/;
|
|
844
844
|
var RE_TEMPLATE_LIT = /^`([^`]*)`$/;
|
|
845
845
|
var RE_TEMPLATE_INTERP = /\$\{([^}]+)\}/g;
|
|
846
|
-
var RE_METHOD_CALL = /^(\w+)\.(\w+)\((.*)\)$/;
|
|
847
846
|
var RE_GROUP_METHOD_CALL = /^\((.+)\)\.(\w+)\((.*)\)$/;
|
|
848
847
|
var RE_STRIP_BRACES = /^\{|\}$/g;
|
|
849
848
|
var RE_DIGIT_ONLY = /^\d+$/;
|
|
@@ -858,6 +857,7 @@ var RE_COMPUTED = /^(\w+)\s*=\s*(.+)$/;
|
|
|
858
857
|
var RE_FETCH = /^(.+?)(?:→|->)\s*(\S+)(.*)$/;
|
|
859
858
|
var RE_FETCH_METHOD = /^(GET|POST|PUT|PATCH|DELETE)\s+(.+)$/i;
|
|
860
859
|
var RE_STRIP_ITEM_BRACES = /^\{item\.?|\}$/g;
|
|
860
|
+
var RE_EVENT_REF = /\bevent\b|\$event\b/;
|
|
861
861
|
var RE_REFETCH_CALL = /^\$refetch\(\s*['"]([^'"]+)['"]\s*\)$/;
|
|
862
862
|
var TRANSITION_STATE_SYM = /* @__PURE__ */ Symbol.for("forma-transition-state");
|
|
863
863
|
var EXPRESSION_CACHE_MAX = 2048;
|
|
@@ -872,7 +872,40 @@ function cacheExpression(key, factory) {
|
|
|
872
872
|
var scopeExpressionCache = /* @__PURE__ */ new WeakMap();
|
|
873
873
|
var scopeHandlerCache = /* @__PURE__ */ new WeakMap();
|
|
874
874
|
var compiledTemplateCache = /* @__PURE__ */ new Map();
|
|
875
|
-
var UNSAFE_METHOD_NAMES = /* @__PURE__ */ new Set([
|
|
875
|
+
var UNSAFE_METHOD_NAMES = /* @__PURE__ */ new Set([
|
|
876
|
+
"constructor",
|
|
877
|
+
"__proto__",
|
|
878
|
+
"prototype",
|
|
879
|
+
"__defineGetter__",
|
|
880
|
+
"__defineSetter__",
|
|
881
|
+
"__lookupGetter__",
|
|
882
|
+
"__lookupSetter__",
|
|
883
|
+
"eval"
|
|
884
|
+
]);
|
|
885
|
+
var BLOCKED_METHOD_REGEXES = (() => {
|
|
886
|
+
const result = [];
|
|
887
|
+
for (const name of UNSAFE_METHOD_NAMES) {
|
|
888
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
889
|
+
result.push({
|
|
890
|
+
name,
|
|
891
|
+
// Match as property access (.name) or bare identifier at start
|
|
892
|
+
dotRe: new RegExp(`(?:^|\\.)${escaped}(?:\\s*\\(|\\s*$|[^\\w$])`, "m"),
|
|
893
|
+
// Match bracket access with single quotes, double quotes, or backticks
|
|
894
|
+
bracketRe: new RegExp(`\\[\\s*(?:'${escaped}'|"${escaped}"|\`` + escaped + `\`)\\s*\\]`)
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
return result;
|
|
898
|
+
})();
|
|
899
|
+
function findBlockedMethod(expr) {
|
|
900
|
+
let cleaned = expr.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
901
|
+
cleaned = cleaned.replace(/\/\/[^\n]*/g, "");
|
|
902
|
+
cleaned = cleaned.replace(/\s*\.\s*/g, ".");
|
|
903
|
+
for (const { name, dotRe, bracketRe } of BLOCKED_METHOD_REGEXES) {
|
|
904
|
+
if (dotRe.test(cleaned)) return name;
|
|
905
|
+
if (bracketRe.test(cleaned)) return name;
|
|
906
|
+
}
|
|
907
|
+
return null;
|
|
908
|
+
}
|
|
876
909
|
var TEXT_BINDING_SYM = /* @__PURE__ */ Symbol.for("forma-text-binding-cache");
|
|
877
910
|
function toTextValue(value2) {
|
|
878
911
|
if (value2 == null) return "";
|
|
@@ -1147,6 +1180,7 @@ function consumeStatement(raw) {
|
|
|
1147
1180
|
function parseIfHandler(expr, scope) {
|
|
1148
1181
|
const input = expr.trim();
|
|
1149
1182
|
if (!RE_IF_PREFIX.test(input)) return null;
|
|
1183
|
+
if (RE_EVENT_REF.test(input)) return null;
|
|
1150
1184
|
let idx = 2;
|
|
1151
1185
|
while (idx < input.length && /\s/.test(input[idx])) idx++;
|
|
1152
1186
|
if (input[idx] !== "(") return null;
|
|
@@ -1548,6 +1582,68 @@ function cloneAttributeTemplates(el, item) {
|
|
|
1548
1582
|
}
|
|
1549
1583
|
}
|
|
1550
1584
|
}
|
|
1585
|
+
function parseChainedAccess(expr, scope) {
|
|
1586
|
+
let pos = 0;
|
|
1587
|
+
const identMatch = expr.match(/^[a-zA-Z_$]\w*/);
|
|
1588
|
+
if (!identMatch) return null;
|
|
1589
|
+
const rootName = identMatch[0];
|
|
1590
|
+
pos = rootName.length;
|
|
1591
|
+
if (pos >= expr.length) return null;
|
|
1592
|
+
if (expr[pos] !== "." && !(expr[pos] === "?" && expr[pos + 1] === ".")) return null;
|
|
1593
|
+
const steps = [];
|
|
1594
|
+
while (pos < expr.length) {
|
|
1595
|
+
let optional = false;
|
|
1596
|
+
if (expr[pos] === "?" && expr[pos + 1] === ".") {
|
|
1597
|
+
optional = true;
|
|
1598
|
+
pos += 2;
|
|
1599
|
+
} else if (expr[pos] === ".") {
|
|
1600
|
+
pos += 1;
|
|
1601
|
+
} else {
|
|
1602
|
+
return null;
|
|
1603
|
+
}
|
|
1604
|
+
const nameMatch = expr.slice(pos).match(/^\w+/);
|
|
1605
|
+
if (!nameMatch) return null;
|
|
1606
|
+
const name = nameMatch[0];
|
|
1607
|
+
pos += name.length;
|
|
1608
|
+
if (UNSAFE_METHOD_NAMES.has(name)) return () => void 0;
|
|
1609
|
+
if (pos < expr.length && expr[pos] === "(") {
|
|
1610
|
+
const balanced = readBalancedSegment(expr, pos, "(", ")");
|
|
1611
|
+
if (!balanced) return null;
|
|
1612
|
+
const argsRaw = balanced.inner.trim();
|
|
1613
|
+
const argFns = [];
|
|
1614
|
+
for (const arg of splitCallArgs(argsRaw)) {
|
|
1615
|
+
const parsed = parseExpression(arg, scope);
|
|
1616
|
+
if (!parsed) return null;
|
|
1617
|
+
argFns.push(parsed);
|
|
1618
|
+
}
|
|
1619
|
+
steps.push({ type: "call", name, optional, argFns });
|
|
1620
|
+
pos = balanced.end + 1;
|
|
1621
|
+
} else {
|
|
1622
|
+
steps.push({ type: "prop", name, optional });
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
if (pos !== expr.length) return null;
|
|
1626
|
+
if (steps.length === 0) return null;
|
|
1627
|
+
const rootExpr = rootName === "Math" ? (() => Math) : (() => scope.getters[rootName]?.());
|
|
1628
|
+
return () => {
|
|
1629
|
+
let val = rootExpr();
|
|
1630
|
+
for (const step of steps) {
|
|
1631
|
+
if (val == null) {
|
|
1632
|
+
if (step.optional) return void 0;
|
|
1633
|
+
return void 0;
|
|
1634
|
+
}
|
|
1635
|
+
if (step.type === "prop") {
|
|
1636
|
+
val = val[step.name];
|
|
1637
|
+
} else {
|
|
1638
|
+
const method = val[step.name];
|
|
1639
|
+
if (typeof method !== "function") return void 0;
|
|
1640
|
+
const args = step.argFns.map((fn) => fn());
|
|
1641
|
+
val = method.apply(val, args);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
return val;
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1551
1647
|
function parseExpression(expr, scope) {
|
|
1552
1648
|
const cachedFactory = expressionCache.get(expr);
|
|
1553
1649
|
if (cachedFactory) return cachedFactory(scope);
|
|
@@ -1598,24 +1694,9 @@ function parseExpressionUncached(expr, scope) {
|
|
|
1598
1694
|
if (RE_IDENTIFIER.test(expr)) {
|
|
1599
1695
|
return () => scope.getters[expr]?.();
|
|
1600
1696
|
}
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
return () => {
|
|
1605
|
-
const obj = scope.getters[p1]?.();
|
|
1606
|
-
return obj?.[p2];
|
|
1607
|
-
};
|
|
1608
|
-
}
|
|
1609
|
-
const deepDotMatch = expr.match(RE_DEEP_DOT);
|
|
1610
|
-
if (deepDotMatch) {
|
|
1611
|
-
const p1 = deepDotMatch[1], p2 = deepDotMatch[2], p3 = deepDotMatch[3], p4 = deepDotMatch[4];
|
|
1612
|
-
return () => {
|
|
1613
|
-
let val = scope.getters[p1]?.();
|
|
1614
|
-
val = val?.[p2];
|
|
1615
|
-
val = val?.[p3];
|
|
1616
|
-
if (p4) val = val?.[p4];
|
|
1617
|
-
return val;
|
|
1618
|
-
};
|
|
1697
|
+
{
|
|
1698
|
+
const chainResult = parseChainedAccess(expr, scope);
|
|
1699
|
+
if (chainResult) return chainResult;
|
|
1619
1700
|
}
|
|
1620
1701
|
const groupedCallMatch = expr.match(RE_GROUP_METHOD_CALL);
|
|
1621
1702
|
if (groupedCallMatch) {
|
|
@@ -1639,28 +1720,6 @@ function parseExpressionUncached(expr, scope) {
|
|
|
1639
1720
|
return method.apply(base, args);
|
|
1640
1721
|
};
|
|
1641
1722
|
}
|
|
1642
|
-
const callMatch = expr.match(RE_METHOD_CALL);
|
|
1643
|
-
if (callMatch) {
|
|
1644
|
-
const baseName = callMatch[1];
|
|
1645
|
-
const methodName = callMatch[2];
|
|
1646
|
-
const argsRaw = callMatch[3].trim();
|
|
1647
|
-
if (UNSAFE_METHOD_NAMES.has(methodName)) return () => void 0;
|
|
1648
|
-
const baseExpr = baseName === "Math" ? (() => Math) : parseExpression(baseName, scope);
|
|
1649
|
-
if (!baseExpr) return null;
|
|
1650
|
-
const argFns = [];
|
|
1651
|
-
for (const arg of splitCallArgs(argsRaw)) {
|
|
1652
|
-
const parsed = parseExpression(arg, scope);
|
|
1653
|
-
if (!parsed) return null;
|
|
1654
|
-
argFns.push(parsed);
|
|
1655
|
-
}
|
|
1656
|
-
return () => {
|
|
1657
|
-
const base = baseExpr();
|
|
1658
|
-
const method = base?.[methodName];
|
|
1659
|
-
if (typeof method !== "function") return void 0;
|
|
1660
|
-
const args = argFns.map((fn) => fn());
|
|
1661
|
-
return method.apply(base, args);
|
|
1662
|
-
};
|
|
1663
|
-
}
|
|
1664
1723
|
if (expr.startsWith("!")) {
|
|
1665
1724
|
const inner = parseExpression(expr.slice(1).trim(), scope);
|
|
1666
1725
|
if (inner) return () => !inner();
|
|
@@ -1679,6 +1738,29 @@ function parseExpressionUncached(expr, scope) {
|
|
|
1679
1738
|
return () => objExpr()?.[key];
|
|
1680
1739
|
}
|
|
1681
1740
|
}
|
|
1741
|
+
if (expr.startsWith("[")) {
|
|
1742
|
+
const balanced = readBalancedSegment(expr, 0, "[", "]");
|
|
1743
|
+
if (balanced && balanced.end === expr.length - 1) {
|
|
1744
|
+
const inner = balanced.inner.trim();
|
|
1745
|
+
if (inner === "") {
|
|
1746
|
+
return () => [];
|
|
1747
|
+
}
|
|
1748
|
+
const elements = splitCallArgs(inner);
|
|
1749
|
+
const elementFns = [];
|
|
1750
|
+
let allParsed = true;
|
|
1751
|
+
for (const el of elements) {
|
|
1752
|
+
const parsed = parseExpression(el.trim(), scope);
|
|
1753
|
+
if (!parsed) {
|
|
1754
|
+
allParsed = false;
|
|
1755
|
+
break;
|
|
1756
|
+
}
|
|
1757
|
+
elementFns.push(parsed);
|
|
1758
|
+
}
|
|
1759
|
+
if (allParsed) {
|
|
1760
|
+
return () => elementFns.map((fn) => fn());
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1682
1764
|
const ternaryMatch = expr.match(RE_TERNARY);
|
|
1683
1765
|
if (ternaryMatch) {
|
|
1684
1766
|
const cond = parseExpression(ternaryMatch[1].trim(), scope);
|
|
@@ -1808,6 +1890,15 @@ function getScopeCache(cache, scope) {
|
|
|
1808
1890
|
}
|
|
1809
1891
|
return scoped;
|
|
1810
1892
|
}
|
|
1893
|
+
function cspExpressionHint(expr) {
|
|
1894
|
+
if (expr.includes("...")) {
|
|
1895
|
+
return `Unsupported expression in CSP-safe mode: spread syntax detected. Use .concat() instead, or enable unsafe-eval via setUnsafeEval(true).`;
|
|
1896
|
+
}
|
|
1897
|
+
if (expr.includes("=>")) {
|
|
1898
|
+
return `Unsupported expression in CSP-safe mode: arrow function detected. Extract logic to a data-computed attribute, or enable unsafe-eval via setUnsafeEval(true).`;
|
|
1899
|
+
}
|
|
1900
|
+
return `Unsupported expression in CSP-safe mode. Simplify the expression or enable unsafe-eval via setUnsafeEval(true).`;
|
|
1901
|
+
}
|
|
1811
1902
|
function buildEvaluator(expr, scope) {
|
|
1812
1903
|
const cleaned = expr.replace(RE_STRIP_BRACES, "").trim();
|
|
1813
1904
|
const cache = getScopeCache(scopeExpressionCache, scope);
|
|
@@ -1820,11 +1911,17 @@ function buildEvaluator(expr, scope) {
|
|
|
1820
1911
|
}
|
|
1821
1912
|
if (!_allowUnsafeEval) {
|
|
1822
1913
|
dbg("buildEvaluator: blocked unsafe eval fallback for expression:", cleaned);
|
|
1823
|
-
reportDiagnostic("expression-unsupported", cleaned,
|
|
1914
|
+
reportDiagnostic("expression-unsupported", cleaned, cspExpressionHint(cleaned));
|
|
1824
1915
|
const blocked = () => void 0;
|
|
1825
1916
|
cache.set(cleaned, blocked);
|
|
1826
1917
|
return blocked;
|
|
1827
1918
|
}
|
|
1919
|
+
const blockedMethod = findBlockedMethod(cleaned);
|
|
1920
|
+
if (blockedMethod) {
|
|
1921
|
+
const msg = `Blocked unsafe method "${blockedMethod}" in expression`;
|
|
1922
|
+
reportDiagnostic("expression-unsupported", cleaned, msg);
|
|
1923
|
+
throw new Error(`[FormaJS] ${msg}: ${cleaned}`);
|
|
1924
|
+
}
|
|
1828
1925
|
try {
|
|
1829
1926
|
const fn = new Function("__scope", `with(__scope) { return (${cleaned}); }`);
|
|
1830
1927
|
const proxy = new Proxy(/* @__PURE__ */ Object.create(null), {
|
|
@@ -1840,7 +1937,7 @@ function buildEvaluator(expr, scope) {
|
|
|
1840
1937
|
cache.set(cleaned, unsafe);
|
|
1841
1938
|
return unsafe;
|
|
1842
1939
|
} catch {
|
|
1843
|
-
reportDiagnostic("expression-unsupported", cleaned, "Expression
|
|
1940
|
+
reportDiagnostic("expression-unsupported", cleaned, "Expression too complex for CSP-safe mode. Enable unsafe-eval via FormaRuntime.unsafeEval = true, or use the standard (non-hardened) build.");
|
|
1844
1941
|
const failed = () => void 0;
|
|
1845
1942
|
cache.set(cleaned, failed);
|
|
1846
1943
|
return failed;
|
|
@@ -1946,7 +2043,13 @@ function parseHandler(expr, scope) {
|
|
|
1946
2043
|
return null;
|
|
1947
2044
|
}
|
|
1948
2045
|
function buildHandler(expr, scope) {
|
|
1949
|
-
|
|
2046
|
+
let cleaned = expr.trim();
|
|
2047
|
+
if (cleaned.startsWith("{")) {
|
|
2048
|
+
const seg = readBalancedSegment(cleaned, 0, "{", "}");
|
|
2049
|
+
if (seg && seg.end === cleaned.length - 1) {
|
|
2050
|
+
cleaned = seg.inner.trim();
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
1950
2053
|
const cache = getScopeCache(scopeHandlerCache, scope);
|
|
1951
2054
|
const cached = cache.get(cleaned);
|
|
1952
2055
|
if (cached) return cached;
|
|
@@ -1958,7 +2061,7 @@ function buildHandler(expr, scope) {
|
|
|
1958
2061
|
}
|
|
1959
2062
|
if (!_allowUnsafeEval) {
|
|
1960
2063
|
dbg("buildHandler: blocked unsafe eval fallback for expression:", cleaned);
|
|
1961
|
-
reportDiagnostic("handler-unsupported", cleaned,
|
|
2064
|
+
reportDiagnostic("handler-unsupported", cleaned, cspExpressionHint(cleaned));
|
|
1962
2065
|
const result = {
|
|
1963
2066
|
handler: () => {
|
|
1964
2067
|
},
|
|
@@ -1967,6 +2070,12 @@ function buildHandler(expr, scope) {
|
|
|
1967
2070
|
cache.set(cleaned, result);
|
|
1968
2071
|
return result;
|
|
1969
2072
|
}
|
|
2073
|
+
const blockedMethod = findBlockedMethod(cleaned);
|
|
2074
|
+
if (blockedMethod) {
|
|
2075
|
+
const msg = `Blocked unsafe method "${blockedMethod}" in handler`;
|
|
2076
|
+
reportDiagnostic("handler-unsupported", cleaned, msg);
|
|
2077
|
+
throw new Error(`[FormaJS] ${msg}: ${cleaned}`);
|
|
2078
|
+
}
|
|
1970
2079
|
try {
|
|
1971
2080
|
const fn = new Function("__scope", "$event", "event", `with(__scope) { ${cleaned} }`);
|
|
1972
2081
|
const proxy = new Proxy(/* @__PURE__ */ Object.create(null), {
|
|
@@ -1994,7 +2103,7 @@ function buildHandler(expr, scope) {
|
|
|
1994
2103
|
cache.set(cleaned, result);
|
|
1995
2104
|
return result;
|
|
1996
2105
|
} catch {
|
|
1997
|
-
reportDiagnostic("handler-unsupported", cleaned, "
|
|
2106
|
+
reportDiagnostic("handler-unsupported", cleaned, "Expression too complex for CSP-safe mode. Enable unsafe-eval via FormaRuntime.unsafeEval = true, or use the standard (non-hardened) build.");
|
|
1998
2107
|
const result = {
|
|
1999
2108
|
handler: () => {
|
|
2000
2109
|
},
|
|
@@ -2004,16 +2113,22 @@ function buildHandler(expr, scope) {
|
|
|
2004
2113
|
return result;
|
|
2005
2114
|
}
|
|
2006
2115
|
}
|
|
2116
|
+
var FORBIDDEN_STATE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
2007
2117
|
function parseState(raw) {
|
|
2118
|
+
let parsed;
|
|
2008
2119
|
try {
|
|
2009
|
-
|
|
2120
|
+
parsed = JSON.parse(raw);
|
|
2010
2121
|
} catch {
|
|
2011
2122
|
try {
|
|
2012
|
-
|
|
2123
|
+
parsed = JSON.parse(raw.replace(RE_UNQUOTED_KEYS, '"$1":'));
|
|
2013
2124
|
} catch {
|
|
2014
2125
|
return {};
|
|
2015
2126
|
}
|
|
2016
2127
|
}
|
|
2128
|
+
for (const key of FORBIDDEN_STATE_KEYS) {
|
|
2129
|
+
if (key in parsed) delete parsed[key];
|
|
2130
|
+
}
|
|
2131
|
+
return parsed;
|
|
2017
2132
|
}
|
|
2018
2133
|
function initScope(stateEl) {
|
|
2019
2134
|
const raw = stateEl.getAttribute("data-forma-state") ?? "{}";
|
|
@@ -2683,6 +2798,7 @@ function setUnsafeEvalMode(mode) {
|
|
|
2683
2798
|
_unsafeEvalMode = mode;
|
|
2684
2799
|
if (mode === "locked-off") _allowUnsafeEval = false;
|
|
2685
2800
|
if (mode === "locked-on") _allowUnsafeEval = true;
|
|
2801
|
+
if (mode === "mutable") _allowUnsafeEval = true;
|
|
2686
2802
|
scopeExpressionCache = /* @__PURE__ */ new WeakMap();
|
|
2687
2803
|
scopeHandlerCache = /* @__PURE__ */ new WeakMap();
|
|
2688
2804
|
}
|