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