@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.
- package/README.md +279 -20
- package/dist/chunk-3U57L2TY.cjs +38 -0
- package/dist/chunk-3U57L2TY.cjs.map +1 -0
- package/dist/{chunk-5H52PKGF.js → chunk-N522P3G6.js} +37 -19
- package/dist/chunk-N522P3G6.js.map +1 -0
- package/dist/chunk-OZCHIVAZ.js +35 -0
- package/dist/chunk-OZCHIVAZ.js.map +1 -0
- package/dist/{chunk-TSQ7AKFT.cjs → chunk-YMIMKO4W.cjs} +66 -25
- package/dist/chunk-YMIMKO4W.cjs.map +1 -0
- package/dist/forma-runtime-csp.js +1 -1
- package/dist/forma-runtime.js +1 -1
- 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 +142 -118
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -29
- package/dist/index.d.ts +70 -29
- package/dist/index.js +64 -60
- package/dist/index.js.map +1 -1
- package/dist/runtime-hardened.cjs +166 -39
- package/dist/runtime-hardened.cjs.map +1 -1
- package/dist/runtime-hardened.js +167 -40
- package/dist/runtime-hardened.js.map +1 -1
- package/dist/runtime.cjs +184 -57
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +162 -35
- package/dist/runtime.js.map +1 -1
- package/dist/signal-B4_wQJHs.d.cts +53 -0
- package/dist/signal-B4_wQJHs.d.ts +53 -0
- package/dist/ssr/index.cjs +37 -12
- package/dist/ssr/index.cjs.map +1 -1
- package/dist/ssr/index.js +37 -12
- package/dist/ssr/index.js.map +1 -1
- package/dist/tc39-compat.cjs +4 -12
- package/dist/tc39-compat.cjs.map +1 -1
- package/dist/tc39-compat.d.cts +1 -1
- package/dist/tc39-compat.d.ts +1 -1
- package/dist/tc39-compat.js +3 -11
- package/dist/tc39-compat.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-5H52PKGF.js.map +0 -1
- package/dist/chunk-FPSLC62A.cjs +0 -31
- package/dist/chunk-FPSLC62A.cjs.map +0 -1
- package/dist/chunk-KX5WRZH7.js +0 -27
- package/dist/chunk-KX5WRZH7.js.map +0 -1
- package/dist/chunk-TSQ7AKFT.cjs.map +0 -1
- package/dist/signal-CfLDwMyg.d.cts +0 -29
- package/dist/signal-CfLDwMyg.d.ts +0 -29
package/dist/runtime-hardened.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { computed, effect,
|
|
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
|
-
|
|
9
|
+
const prevSub = setActiveSub(void 0);
|
|
10
10
|
const prev = s();
|
|
11
|
-
|
|
12
|
-
|
|
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\
|
|
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
|
-
|
|
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
|
-
|
|
2121
|
-
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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] =
|
|
2667
|
+
const [ge, se] = createSignal(null);
|
|
2541
2668
|
scope.getters[errorTarget] = ge;
|
|
2542
2669
|
scope.setters[errorTarget] = se;
|
|
2543
2670
|
}
|