@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.
- package/dist/{chunk-MIOMT2CB.js → chunk-AJP4OVCN.js} +29 -9
- package/dist/chunk-AJP4OVCN.js.map +1 -0
- package/dist/{chunk-XLVBYXOU.js → chunk-ETPJTPYD.js} +6 -3
- package/dist/chunk-ETPJTPYD.js.map +1 -0
- package/dist/{chunk-JRQNDXX7.cjs → chunk-FBM7V4NE.cjs} +68 -47
- package/dist/chunk-FBM7V4NE.cjs.map +1 -0
- package/dist/{chunk-W7OUWVRA.cjs → chunk-H7MDUHS5.cjs} +6 -2
- package/dist/chunk-H7MDUHS5.cjs.map +1 -0
- package/dist/{chunk-2Y5US35K.cjs → chunk-KUXNZ5MG.cjs} +12 -12
- package/dist/{chunk-2Y5US35K.cjs.map → chunk-KUXNZ5MG.cjs.map} +1 -1
- package/dist/{chunk-INNOI6TG.js → chunk-Y3A2EVVS.js} +3 -3
- package/dist/{chunk-INNOI6TG.js.map → chunk-Y3A2EVVS.js.map} +1 -1
- package/dist/forma-runtime-csp.js +178 -79
- package/dist/forma-runtime.js +178 -79
- package/dist/formajs-runtime-hardened.global.js +178 -79
- package/dist/formajs-runtime-hardened.global.js.map +1 -1
- package/dist/formajs-runtime.global.js +178 -79
- package/dist/formajs-runtime.global.js.map +1 -1
- package/dist/formajs.global.js +245 -61
- package/dist/formajs.global.js.map +1 -1
- package/dist/http.cjs +11 -11
- package/dist/http.js +2 -2
- package/dist/index.cjs +307 -143
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -2
- package/dist/index.d.ts +34 -2
- package/dist/index.js +248 -88
- package/dist/index.js.map +1 -1
- package/dist/runtime-hardened.cjs +178 -79
- package/dist/runtime-hardened.cjs.map +1 -1
- package/dist/runtime-hardened.js +178 -79
- package/dist/runtime-hardened.js.map +1 -1
- package/dist/runtime.cjs +198 -103
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +174 -79
- package/dist/runtime.js.map +1 -1
- package/dist/server.cjs +7 -7
- package/dist/server.js +2 -2
- package/dist/tc39-compat.cjs +3 -3
- package/dist/tc39-compat.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-JRQNDXX7.cjs.map +0 -1
- package/dist/chunk-MIOMT2CB.js.map +0 -1
- package/dist/chunk-W7OUWVRA.cjs.map +0 -1
- package/dist/chunk-XLVBYXOU.js.map +0 -1
package/dist/formajs.global.js
CHANGED
|
@@ -86,6 +86,7 @@ var FormaJS = (() => {
|
|
|
86
86
|
setStyle: () => setStyle,
|
|
87
87
|
setText: () => setText,
|
|
88
88
|
siblings: () => siblings,
|
|
89
|
+
svg: () => svg,
|
|
89
90
|
template: () => template,
|
|
90
91
|
templateMany: () => templateMany,
|
|
91
92
|
toggleClass: () => toggleClass,
|
|
@@ -625,6 +626,9 @@ var FormaJS = (() => {
|
|
|
625
626
|
|
|
626
627
|
// src/reactive/signal.ts
|
|
627
628
|
var signalNames = /* @__PURE__ */ new WeakMap();
|
|
629
|
+
function value(v) {
|
|
630
|
+
return () => v;
|
|
631
|
+
}
|
|
628
632
|
function applySignalSet(s, v, equals) {
|
|
629
633
|
if (typeof v !== "function") {
|
|
630
634
|
if (!equals) {
|
|
@@ -1283,14 +1287,18 @@ var FormaJS = (() => {
|
|
|
1283
1287
|
}
|
|
1284
1288
|
const oldKeyMap = /* @__PURE__ */ new Map();
|
|
1285
1289
|
for (let i = 0; i < oldLen; i++) {
|
|
1286
|
-
|
|
1290
|
+
const k = keyFn(oldItems[i]);
|
|
1291
|
+
const bucket = oldKeyMap.get(k);
|
|
1292
|
+
if (bucket) bucket.push(i);
|
|
1293
|
+
else oldKeyMap.set(k, [i]);
|
|
1287
1294
|
}
|
|
1288
1295
|
const oldIndices = new Array(newLen);
|
|
1289
1296
|
const oldUsed = new Uint8Array(oldLen);
|
|
1290
1297
|
for (let i = 0; i < newLen; i++) {
|
|
1291
1298
|
const key = keyFn(newItems[i]);
|
|
1292
|
-
const
|
|
1293
|
-
if (
|
|
1299
|
+
const bucket = oldKeyMap.get(key);
|
|
1300
|
+
if (bucket && bucket.length > 0) {
|
|
1301
|
+
const oldIdx = bucket.shift();
|
|
1294
1302
|
oldIndices[i] = oldIdx;
|
|
1295
1303
|
oldUsed[oldIdx] = 1;
|
|
1296
1304
|
} else {
|
|
@@ -2074,6 +2082,17 @@ var FormaJS = (() => {
|
|
|
2074
2082
|
var Fragment = /* @__PURE__ */ Symbol.for("forma.fragment");
|
|
2075
2083
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
2076
2084
|
var XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
2085
|
+
var DUAL_USE_SVG_TAGS = /* @__PURE__ */ new Set(["a", "title", "script", "style", "font"]);
|
|
2086
|
+
var currentNamespace = null;
|
|
2087
|
+
function svg(build) {
|
|
2088
|
+
const prev = currentNamespace;
|
|
2089
|
+
currentNamespace = SVG_NS;
|
|
2090
|
+
try {
|
|
2091
|
+
return build();
|
|
2092
|
+
} finally {
|
|
2093
|
+
currentNamespace = prev;
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2077
2096
|
var SVG_TAGS = /* @__PURE__ */ new Set([
|
|
2078
2097
|
"svg",
|
|
2079
2098
|
"path",
|
|
@@ -2607,7 +2626,12 @@ var FormaJS = (() => {
|
|
|
2607
2626
|
return { type: "element", tag: tagName, props: props ?? null, children: children2 };
|
|
2608
2627
|
}
|
|
2609
2628
|
let el;
|
|
2610
|
-
|
|
2629
|
+
const svgCtx = currentNamespace === SVG_NS;
|
|
2630
|
+
if (svgCtx && (SVG_TAGS.has(tagName) || DUAL_USE_SVG_TAGS.has(tagName))) {
|
|
2631
|
+
el = document.createElementNS(SVG_NS, tagName);
|
|
2632
|
+
} else if (!svgCtx && DUAL_USE_SVG_TAGS.has(tagName)) {
|
|
2633
|
+
el = getProto(tagName).cloneNode(false);
|
|
2634
|
+
} else if (ELEMENT_PROTOS && ELEMENT_PROTOS[tagName]) {
|
|
2611
2635
|
el = ELEMENT_PROTOS[tagName].cloneNode(false);
|
|
2612
2636
|
} else if (SVG_TAGS.has(tagName)) {
|
|
2613
2637
|
el = document.createElementNS(SVG_NS, tagName);
|
|
@@ -2873,8 +2897,14 @@ var FormaJS = (() => {
|
|
|
2873
2897
|
const isPending = pending() > 0;
|
|
2874
2898
|
const newNode = isPending ? fallbackNode ??= fallback() : resolvedNode;
|
|
2875
2899
|
if (newNode === currentNode) return;
|
|
2876
|
-
if (currentNode
|
|
2877
|
-
|
|
2900
|
+
if (currentNode) {
|
|
2901
|
+
if (currentNode.parentNode === parent2) {
|
|
2902
|
+
parent2.removeChild(currentNode);
|
|
2903
|
+
} else if (currentNode.nodeType === 11) {
|
|
2904
|
+
while (startMarker.nextSibling && startMarker.nextSibling !== endMarker) {
|
|
2905
|
+
currentNode.appendChild(startMarker.nextSibling);
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2878
2908
|
}
|
|
2879
2909
|
if (newNode) {
|
|
2880
2910
|
parent2.insertBefore(newNode, endMarker);
|
|
@@ -2930,6 +2960,10 @@ var FormaJS = (() => {
|
|
|
2930
2960
|
const sharedProps = scriptBlock ? JSON.parse(scriptBlock.textContent) : null;
|
|
2931
2961
|
const islands = document.querySelectorAll("[data-forma-island]");
|
|
2932
2962
|
for (const root of islands) {
|
|
2963
|
+
const status = root.getAttribute("data-forma-status");
|
|
2964
|
+
if (status === "active" || status === "hydrating" || status === "disposed" || status === "error") continue;
|
|
2965
|
+
if (root.__formaScheduled) continue;
|
|
2966
|
+
delete root.__formaDisposed;
|
|
2933
2967
|
const id = parseInt(root.getAttribute("data-forma-island"), 10);
|
|
2934
2968
|
const componentName = root.getAttribute("data-forma-component");
|
|
2935
2969
|
const hydrateFn = registry[componentName];
|
|
@@ -2940,30 +2974,39 @@ var FormaJS = (() => {
|
|
|
2940
2974
|
}
|
|
2941
2975
|
const trigger2 = root.getAttribute("data-forma-hydrate") || "load";
|
|
2942
2976
|
if (trigger2 === "visible") {
|
|
2977
|
+
root.__formaScheduled = true;
|
|
2943
2978
|
const observer = new IntersectionObserver(
|
|
2944
2979
|
(entries) => {
|
|
2945
2980
|
for (const entry of entries) {
|
|
2946
2981
|
if (!entry.isIntersecting) continue;
|
|
2947
2982
|
observer.disconnect();
|
|
2983
|
+
delete root.__formaObserver;
|
|
2948
2984
|
hydrateIslandRoot(root, id, componentName, hydrateFn, sharedProps);
|
|
2949
2985
|
}
|
|
2950
2986
|
},
|
|
2951
2987
|
{ rootMargin: "200px" }
|
|
2952
2988
|
);
|
|
2989
|
+
root.__formaObserver = observer;
|
|
2953
2990
|
observer.observe(root);
|
|
2954
2991
|
} else if (trigger2 === "idle") {
|
|
2992
|
+
root.__formaScheduled = true;
|
|
2955
2993
|
const hydrate = () => hydrateIslandRoot(root, id, componentName, hydrateFn, sharedProps);
|
|
2956
2994
|
if (typeof requestIdleCallback === "function") {
|
|
2957
|
-
requestIdleCallback(hydrate);
|
|
2995
|
+
const handle = requestIdleCallback(hydrate);
|
|
2996
|
+
root.__formaIdleCancel = () => cancelIdleCallback(handle);
|
|
2958
2997
|
} else {
|
|
2959
|
-
setTimeout(hydrate, 200);
|
|
2998
|
+
const handle = setTimeout(hydrate, 200);
|
|
2999
|
+
root.__formaIdleCancel = () => clearTimeout(handle);
|
|
2960
3000
|
}
|
|
2961
3001
|
} else if (trigger2 === "interaction") {
|
|
3002
|
+
root.__formaScheduled = true;
|
|
2962
3003
|
const hydrate = () => {
|
|
2963
3004
|
root.removeEventListener("pointerdown", hydrate, true);
|
|
2964
3005
|
root.removeEventListener("focusin", hydrate, true);
|
|
3006
|
+
delete root.__formaInteractionHandler;
|
|
2965
3007
|
hydrateIslandRoot(root, id, componentName, hydrateFn, sharedProps);
|
|
2966
3008
|
};
|
|
3009
|
+
root.__formaInteractionHandler = hydrate;
|
|
2967
3010
|
root.addEventListener("pointerdown", hydrate, { capture: true, once: true });
|
|
2968
3011
|
root.addEventListener("focusin", hydrate, { capture: true, once: true });
|
|
2969
3012
|
} else {
|
|
@@ -2972,6 +3015,24 @@ var FormaJS = (() => {
|
|
|
2972
3015
|
}
|
|
2973
3016
|
}
|
|
2974
3017
|
function deactivateIsland(el) {
|
|
3018
|
+
const observer = el.__formaObserver;
|
|
3019
|
+
if (observer) {
|
|
3020
|
+
observer.disconnect();
|
|
3021
|
+
delete el.__formaObserver;
|
|
3022
|
+
}
|
|
3023
|
+
const interactionHandler = el.__formaInteractionHandler;
|
|
3024
|
+
if (interactionHandler) {
|
|
3025
|
+
el.removeEventListener("pointerdown", interactionHandler, true);
|
|
3026
|
+
el.removeEventListener("focusin", interactionHandler, true);
|
|
3027
|
+
delete el.__formaInteractionHandler;
|
|
3028
|
+
}
|
|
3029
|
+
const idleCancel = el.__formaIdleCancel;
|
|
3030
|
+
if (idleCancel) {
|
|
3031
|
+
idleCancel();
|
|
3032
|
+
delete el.__formaIdleCancel;
|
|
3033
|
+
}
|
|
3034
|
+
delete el.__formaScheduled;
|
|
3035
|
+
el.__formaDisposed = true;
|
|
2975
3036
|
const dispose = el.__formaDispose;
|
|
2976
3037
|
if (typeof dispose === "function") {
|
|
2977
3038
|
dispose();
|
|
@@ -2980,13 +3041,15 @@ var FormaJS = (() => {
|
|
|
2980
3041
|
}
|
|
2981
3042
|
}
|
|
2982
3043
|
function deactivateAllIslands(root = document) {
|
|
2983
|
-
const islands = root.querySelectorAll(
|
|
3044
|
+
const islands = root.querySelectorAll("[data-forma-island]");
|
|
2984
3045
|
for (const island of islands) {
|
|
2985
3046
|
deactivateIsland(island);
|
|
2986
3047
|
}
|
|
2987
3048
|
}
|
|
2988
3049
|
function hydrateIslandRoot(root, id, componentName, hydrateFn, sharedProps) {
|
|
3050
|
+
if (root.__formaDisposed) return;
|
|
2989
3051
|
try {
|
|
3052
|
+
delete root.__formaScheduled;
|
|
2990
3053
|
const props = loadIslandProps(root, id, sharedProps);
|
|
2991
3054
|
root.setAttribute("data-forma-status", "hydrating");
|
|
2992
3055
|
let activeRoot = root;
|
|
@@ -3156,6 +3219,19 @@ var FormaJS = (() => {
|
|
|
3156
3219
|
function createStore(initial) {
|
|
3157
3220
|
const signals = /* @__PURE__ */ new Map();
|
|
3158
3221
|
const children2 = /* @__PURE__ */ new Map();
|
|
3222
|
+
const arrayVersions = /* @__PURE__ */ new Map();
|
|
3223
|
+
function getArrayVersion(path) {
|
|
3224
|
+
let p = arrayVersions.get(path);
|
|
3225
|
+
if (!p) {
|
|
3226
|
+
p = createSignal(0);
|
|
3227
|
+
arrayVersions.set(path, p);
|
|
3228
|
+
}
|
|
3229
|
+
return p;
|
|
3230
|
+
}
|
|
3231
|
+
function bumpArrayVersion(path) {
|
|
3232
|
+
const p = arrayVersions.get(path);
|
|
3233
|
+
if (p) p[1]((n) => n + 1);
|
|
3234
|
+
}
|
|
3159
3235
|
function registerChild(path) {
|
|
3160
3236
|
const lastDot = path.lastIndexOf(".");
|
|
3161
3237
|
if (lastDot === -1) return;
|
|
@@ -3187,14 +3263,37 @@ var FormaJS = (() => {
|
|
|
3187
3263
|
}
|
|
3188
3264
|
childSet.clear();
|
|
3189
3265
|
}
|
|
3266
|
+
function lastSegment(path) {
|
|
3267
|
+
const d = path.lastIndexOf(".");
|
|
3268
|
+
return d === -1 ? path : path.substring(d + 1);
|
|
3269
|
+
}
|
|
3270
|
+
function setLiteral(pair, v) {
|
|
3271
|
+
pair[1](typeof v === "function" ? value(v) : v);
|
|
3272
|
+
}
|
|
3273
|
+
function reconcileChildren(parentPath, rawParent) {
|
|
3274
|
+
const set = children2.get(parentPath);
|
|
3275
|
+
if (!set) return;
|
|
3276
|
+
for (const childPath of set) {
|
|
3277
|
+
const key = lastSegment(childPath);
|
|
3278
|
+
const nv = rawParent[key];
|
|
3279
|
+
const pair = signals.get(childPath);
|
|
3280
|
+
if (pair) setLiteral(pair, nv);
|
|
3281
|
+
if (nv != null && typeof nv === "object") {
|
|
3282
|
+
reconcileChildren(childPath, nv);
|
|
3283
|
+
} else {
|
|
3284
|
+
invalidateChildren(childPath);
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3190
3288
|
function wrap(raw, basePath) {
|
|
3191
3289
|
if (!shouldWrap(raw)) return raw;
|
|
3192
|
-
|
|
3193
|
-
if (
|
|
3290
|
+
let byPath = proxyCache.get(raw);
|
|
3291
|
+
if (byPath) {
|
|
3292
|
+
const hit = byPath.get(basePath);
|
|
3293
|
+
if (hit) return hit;
|
|
3294
|
+
}
|
|
3194
3295
|
const isArr = Array.isArray(raw);
|
|
3195
3296
|
const basePrefix = basePath ? basePath + "." : "";
|
|
3196
|
-
let lastKey = "";
|
|
3197
|
-
let lastSignal;
|
|
3198
3297
|
const proxy = new Proxy(raw, {
|
|
3199
3298
|
// -------------------------------------------------------------------
|
|
3200
3299
|
// GET
|
|
@@ -3215,12 +3314,10 @@ var FormaJS = (() => {
|
|
|
3215
3314
|
(a) => a != null && typeof a === "object" && a[RAW] ? a[RAW] : a
|
|
3216
3315
|
);
|
|
3217
3316
|
result = target[key].apply(target, rawArgs);
|
|
3218
|
-
|
|
3219
|
-
const
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
);
|
|
3223
|
-
setLen(target.length);
|
|
3317
|
+
reconcileChildren(basePath, target);
|
|
3318
|
+
const lenPair = signals.get(basePrefix + "length");
|
|
3319
|
+
if (lenPair) lenPair[1](target.length);
|
|
3320
|
+
if (basePath) bumpArrayVersion(basePath);
|
|
3224
3321
|
});
|
|
3225
3322
|
return result;
|
|
3226
3323
|
};
|
|
@@ -3231,15 +3328,11 @@ var FormaJS = (() => {
|
|
|
3231
3328
|
return target.length;
|
|
3232
3329
|
}
|
|
3233
3330
|
const value2 = Reflect.get(target, prop);
|
|
3234
|
-
|
|
3235
|
-
if (key === lastKey && lastSignal) {
|
|
3236
|
-
pair = lastSignal;
|
|
3237
|
-
} else {
|
|
3238
|
-
pair = getSignal(childPath, value2);
|
|
3239
|
-
lastKey = key;
|
|
3240
|
-
lastSignal = pair;
|
|
3241
|
-
}
|
|
3331
|
+
const pair = getSignal(childPath, value2);
|
|
3242
3332
|
pair[0]();
|
|
3333
|
+
if (Array.isArray(value2)) {
|
|
3334
|
+
getArrayVersion(childPath)[0]();
|
|
3335
|
+
}
|
|
3243
3336
|
if (shouldWrap(value2)) {
|
|
3244
3337
|
return wrap(value2, childPath);
|
|
3245
3338
|
}
|
|
@@ -3255,9 +3348,11 @@ var FormaJS = (() => {
|
|
|
3255
3348
|
const key = String(prop);
|
|
3256
3349
|
const childPath = basePrefix + key;
|
|
3257
3350
|
const rawValue = value2 != null && typeof value2 === "object" && value2[RAW] ? value2[RAW] : value2;
|
|
3351
|
+
const oldRaw = Reflect.get(target, prop);
|
|
3258
3352
|
Reflect.set(target, prop, rawValue);
|
|
3259
|
-
if (rawValue != null && typeof rawValue === "object") {
|
|
3353
|
+
if (rawValue != null && typeof rawValue === "object" && oldRaw !== rawValue) {
|
|
3260
3354
|
invalidateChildren(childPath);
|
|
3355
|
+
evictProxy(oldRaw, childPath);
|
|
3261
3356
|
}
|
|
3262
3357
|
if (isArr && key !== "length") {
|
|
3263
3358
|
const lengthPath = basePrefix + "length";
|
|
@@ -3266,6 +3361,12 @@ var FormaJS = (() => {
|
|
|
3266
3361
|
lenPair[1](target.length);
|
|
3267
3362
|
}
|
|
3268
3363
|
}
|
|
3364
|
+
if (isArr && key === "length") {
|
|
3365
|
+
batch(() => {
|
|
3366
|
+
reconcileChildren(basePath, target);
|
|
3367
|
+
if (basePath) bumpArrayVersion(basePath);
|
|
3368
|
+
});
|
|
3369
|
+
}
|
|
3269
3370
|
const [, setter2] = getSignal(childPath, rawValue);
|
|
3270
3371
|
setter2(rawValue);
|
|
3271
3372
|
return true;
|
|
@@ -3304,24 +3405,30 @@ var FormaJS = (() => {
|
|
|
3304
3405
|
}
|
|
3305
3406
|
const key = String(prop);
|
|
3306
3407
|
const childPath = basePrefix + key;
|
|
3408
|
+
const oldRaw = Reflect.get(target, prop);
|
|
3307
3409
|
const result = Reflect.deleteProperty(target, prop);
|
|
3410
|
+
const delPair = signals.get(childPath);
|
|
3411
|
+
if (delPair) delPair[1](void 0);
|
|
3412
|
+
evictProxy(oldRaw, childPath);
|
|
3308
3413
|
invalidateChildren(childPath);
|
|
3309
|
-
signals.delete(childPath);
|
|
3310
|
-
const parentPath = basePath;
|
|
3311
|
-
if (parentPath !== void 0) {
|
|
3312
|
-
const parentSet = children2.get(parentPath);
|
|
3313
|
-
if (parentSet) {
|
|
3314
|
-
parentSet.delete(childPath);
|
|
3315
|
-
if (parentSet.size === 0) children2.delete(parentPath);
|
|
3316
|
-
}
|
|
3317
|
-
}
|
|
3318
|
-
children2.delete(childPath);
|
|
3319
3414
|
return result;
|
|
3320
3415
|
}
|
|
3321
3416
|
});
|
|
3322
|
-
|
|
3417
|
+
if (!byPath) {
|
|
3418
|
+
byPath = /* @__PURE__ */ new Map();
|
|
3419
|
+
proxyCache.set(raw, byPath);
|
|
3420
|
+
}
|
|
3421
|
+
byPath.set(basePath, proxy);
|
|
3323
3422
|
return proxy;
|
|
3324
3423
|
}
|
|
3424
|
+
function evictProxy(oldRaw, path) {
|
|
3425
|
+
if (oldRaw == null || typeof oldRaw !== "object") return;
|
|
3426
|
+
const om = proxyCache.get(oldRaw);
|
|
3427
|
+
if (om) {
|
|
3428
|
+
om.delete(path);
|
|
3429
|
+
if (om.size === 0) proxyCache.delete(oldRaw);
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3325
3432
|
const rootProxy = wrap(initial, "");
|
|
3326
3433
|
function getCurrentSnapshot() {
|
|
3327
3434
|
return untrack(() => deepClone(initial));
|
|
@@ -3338,10 +3445,24 @@ var FormaJS = (() => {
|
|
|
3338
3445
|
}
|
|
3339
3446
|
|
|
3340
3447
|
// src/state/history.ts
|
|
3448
|
+
function cloneEntry(v, seen) {
|
|
3449
|
+
if (v === null || typeof v !== "object") return v;
|
|
3450
|
+
const proto = Object.getPrototypeOf(v);
|
|
3451
|
+
if (!Array.isArray(v) && proto !== Object.prototype && proto !== null) return v;
|
|
3452
|
+
if (!seen) seen = /* @__PURE__ */ new WeakSet();
|
|
3453
|
+
if (seen.has(v)) return v;
|
|
3454
|
+
seen.add(v);
|
|
3455
|
+
if (Array.isArray(v)) return v.map((i) => cloneEntry(i, seen));
|
|
3456
|
+
const out = {};
|
|
3457
|
+
for (const k of Object.keys(v)) {
|
|
3458
|
+
out[k] = cloneEntry(v[k], seen);
|
|
3459
|
+
}
|
|
3460
|
+
return out;
|
|
3461
|
+
}
|
|
3341
3462
|
function createHistory(source, options) {
|
|
3342
3463
|
const [sourceGet, sourceSet] = source;
|
|
3343
|
-
const maxLength = options?.maxLength ?? 100;
|
|
3344
|
-
let _stack = [sourceGet()];
|
|
3464
|
+
const maxLength = Math.max(1, options?.maxLength ?? 100);
|
|
3465
|
+
let _stack = [cloneEntry(sourceGet())];
|
|
3345
3466
|
let _cursor = 0;
|
|
3346
3467
|
const [stackSignal, setStackSignal] = createSignal([..._stack]);
|
|
3347
3468
|
const [cursorSignal, setCursorSignal] = createSignal(_cursor);
|
|
@@ -3353,45 +3474,52 @@ var FormaJS = (() => {
|
|
|
3353
3474
|
setStackLenSignal(_stack.length);
|
|
3354
3475
|
});
|
|
3355
3476
|
}
|
|
3356
|
-
|
|
3477
|
+
const NONE = /* @__PURE__ */ Symbol("none");
|
|
3478
|
+
let _expected = NONE;
|
|
3357
3479
|
let isFirstRun = true;
|
|
3358
|
-
internalEffect(() => {
|
|
3480
|
+
const disposeEffect = internalEffect(() => {
|
|
3359
3481
|
const value2 = sourceGet();
|
|
3360
3482
|
if (isFirstRun) {
|
|
3361
3483
|
isFirstRun = false;
|
|
3362
3484
|
return;
|
|
3363
3485
|
}
|
|
3364
|
-
if (
|
|
3365
|
-
|
|
3486
|
+
if (_expected !== NONE && Object.is(value2, _expected)) {
|
|
3487
|
+
_expected = NONE;
|
|
3366
3488
|
return;
|
|
3367
3489
|
}
|
|
3490
|
+
_expected = NONE;
|
|
3368
3491
|
_stack = _stack.slice(0, _cursor + 1);
|
|
3369
|
-
_stack.push(value2);
|
|
3492
|
+
_stack.push(cloneEntry(value2));
|
|
3370
3493
|
if (_stack.length > maxLength) {
|
|
3371
3494
|
_stack.splice(0, _stack.length - maxLength);
|
|
3372
3495
|
}
|
|
3373
3496
|
_cursor = _stack.length - 1;
|
|
3374
3497
|
syncSignals();
|
|
3375
3498
|
});
|
|
3499
|
+
const destroy = () => {
|
|
3500
|
+
disposeEffect();
|
|
3501
|
+
};
|
|
3376
3502
|
const canUndo = () => cursorSignal() > 0;
|
|
3377
3503
|
const canRedo = () => cursorSignal() < stackLenSignal() - 1;
|
|
3378
3504
|
const undo = () => {
|
|
3379
3505
|
if (_cursor <= 0) return;
|
|
3380
3506
|
_cursor--;
|
|
3381
|
-
|
|
3382
|
-
|
|
3507
|
+
const restored = cloneEntry(_stack[_cursor]);
|
|
3508
|
+
_expected = restored;
|
|
3509
|
+
sourceSet(restored);
|
|
3383
3510
|
syncSignals();
|
|
3384
3511
|
};
|
|
3385
3512
|
const redo = () => {
|
|
3386
3513
|
if (_cursor >= _stack.length - 1) return;
|
|
3387
3514
|
_cursor++;
|
|
3388
|
-
|
|
3389
|
-
|
|
3515
|
+
const restored = cloneEntry(_stack[_cursor]);
|
|
3516
|
+
_expected = restored;
|
|
3517
|
+
sourceSet(restored);
|
|
3390
3518
|
syncSignals();
|
|
3391
3519
|
};
|
|
3392
3520
|
const clear = () => {
|
|
3393
3521
|
const currentValue = sourceGet();
|
|
3394
|
-
_stack = [currentValue];
|
|
3522
|
+
_stack = [cloneEntry(currentValue)];
|
|
3395
3523
|
_cursor = 0;
|
|
3396
3524
|
syncSignals();
|
|
3397
3525
|
};
|
|
@@ -3402,35 +3530,91 @@ var FormaJS = (() => {
|
|
|
3402
3530
|
canRedo,
|
|
3403
3531
|
history: () => stackSignal(),
|
|
3404
3532
|
cursor: () => cursorSignal(),
|
|
3405
|
-
clear
|
|
3533
|
+
clear,
|
|
3534
|
+
destroy
|
|
3406
3535
|
};
|
|
3407
3536
|
}
|
|
3408
3537
|
|
|
3409
3538
|
// src/state/persist.ts
|
|
3539
|
+
var ENVELOPE_TAG = "$forma:v";
|
|
3410
3540
|
function persist(source, key, options) {
|
|
3411
3541
|
const [sourceGet, sourceSet] = source;
|
|
3412
3542
|
const storage = options?.storage ?? globalThis.localStorage;
|
|
3413
3543
|
const serialize = options?.serialize ?? JSON.stringify;
|
|
3414
3544
|
const deserialize = options?.deserialize ?? JSON.parse;
|
|
3415
3545
|
const validate = options?.validate;
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3546
|
+
const version = options?.version;
|
|
3547
|
+
const migrate = options?.migrate;
|
|
3548
|
+
const onError2 = options?.onError;
|
|
3549
|
+
let writing = false;
|
|
3550
|
+
function unwrap(stored) {
|
|
3551
|
+
const parsed = deserialize(stored);
|
|
3552
|
+
if (parsed != null && typeof parsed === "object" && Object.prototype.hasOwnProperty.call(parsed, ENVELOPE_TAG)) {
|
|
3553
|
+
const env = parsed;
|
|
3554
|
+
return { value: env.value, version: Number(env[ENVELOPE_TAG]) };
|
|
3555
|
+
}
|
|
3556
|
+
return { value: parsed, version: 0 };
|
|
3557
|
+
}
|
|
3558
|
+
function hydrate() {
|
|
3559
|
+
let stored;
|
|
3560
|
+
try {
|
|
3561
|
+
stored = storage.getItem(key);
|
|
3562
|
+
} catch (err) {
|
|
3563
|
+
onError2?.(err, "hydrate");
|
|
3564
|
+
return;
|
|
3565
|
+
}
|
|
3566
|
+
if (stored === null) return;
|
|
3567
|
+
try {
|
|
3568
|
+
const { value: raw, version: storedVersion } = unwrap(stored);
|
|
3569
|
+
let value2 = raw;
|
|
3570
|
+
if (version !== void 0 && storedVersion < version) {
|
|
3571
|
+
if (!migrate) return;
|
|
3572
|
+
try {
|
|
3573
|
+
value2 = migrate(raw, storedVersion);
|
|
3574
|
+
} catch (err) {
|
|
3575
|
+
onError2?.(err, "migrate");
|
|
3576
|
+
return;
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3420
3579
|
if (!validate || validate(value2)) {
|
|
3421
|
-
|
|
3580
|
+
writing = true;
|
|
3581
|
+
try {
|
|
3582
|
+
sourceSet(value2);
|
|
3583
|
+
} finally {
|
|
3584
|
+
writing = false;
|
|
3585
|
+
}
|
|
3422
3586
|
}
|
|
3587
|
+
} catch (err) {
|
|
3588
|
+
onError2?.(err, "hydrate");
|
|
3423
3589
|
}
|
|
3424
|
-
} catch {
|
|
3425
3590
|
}
|
|
3426
|
-
|
|
3591
|
+
hydrate();
|
|
3592
|
+
const stopEffect = internalEffect(() => {
|
|
3427
3593
|
const value2 = sourceGet();
|
|
3594
|
+
if (writing) return;
|
|
3428
3595
|
try {
|
|
3429
|
-
const serialized = serialize(value2);
|
|
3596
|
+
const serialized = version !== void 0 ? serialize({ [ENVELOPE_TAG]: version, value: value2 }) : serialize(value2);
|
|
3430
3597
|
storage.setItem(key, serialized);
|
|
3431
|
-
} catch {
|
|
3598
|
+
} catch (err) {
|
|
3599
|
+
onError2?.(err, err?.name === "QuotaExceededError" ? "write" : "serialize");
|
|
3432
3600
|
}
|
|
3433
3601
|
});
|
|
3602
|
+
const enableSync = options?.syncTabs ?? (typeof window !== "undefined" && storage === globalThis.localStorage);
|
|
3603
|
+
let onStorage;
|
|
3604
|
+
if (enableSync && typeof window !== "undefined") {
|
|
3605
|
+
onStorage = (e) => {
|
|
3606
|
+
if (e.storageArea !== storage) return;
|
|
3607
|
+
if (e.key !== null && e.key !== key) return;
|
|
3608
|
+
hydrate();
|
|
3609
|
+
};
|
|
3610
|
+
window.addEventListener("storage", onStorage);
|
|
3611
|
+
}
|
|
3612
|
+
return () => {
|
|
3613
|
+
stopEffect();
|
|
3614
|
+
if (onStorage && typeof window !== "undefined") {
|
|
3615
|
+
window.removeEventListener("storage", onStorage);
|
|
3616
|
+
}
|
|
3617
|
+
};
|
|
3434
3618
|
}
|
|
3435
3619
|
|
|
3436
3620
|
// src/events/bus.ts
|