@getforma/core 1.1.0 → 1.2.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-JRQNDXX7.cjs → chunk-E3FV2VSU.cjs} +43 -43
- package/dist/{chunk-JRQNDXX7.cjs.map → chunk-E3FV2VSU.cjs.map} +1 -1
- package/dist/{chunk-XLVBYXOU.js → chunk-ETPJTPYD.js} +6 -3
- package/dist/chunk-ETPJTPYD.js.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/{chunk-MIOMT2CB.js → chunk-YRNYOZF3.js} +4 -4
- package/dist/{chunk-MIOMT2CB.js.map → chunk-YRNYOZF3.js.map} +1 -1
- package/dist/formajs.global.js +176 -52
- package/dist/formajs.global.js.map +1 -1
- package/dist/http.cjs +11 -11
- package/dist/http.js +2 -2
- package/dist/index.cjs +259 -138
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +204 -83
- package/dist/index.js.map +1 -1
- package/dist/runtime.cjs +29 -29
- package/dist/runtime.js +3 -3
- 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-W7OUWVRA.cjs.map +0 -1
- package/dist/chunk-XLVBYXOU.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -943,6 +943,8 @@ interface HistoryControls<T> {
|
|
|
943
943
|
cursor: () => number;
|
|
944
944
|
/** Clear all history, keeping only the current value. */
|
|
945
945
|
clear: () => void;
|
|
946
|
+
/** Stop tracking the source signal and release the history stack. */
|
|
947
|
+
destroy: () => void;
|
|
946
948
|
}
|
|
947
949
|
/**
|
|
948
950
|
* Create undo/redo history tracking for a signal.
|
|
@@ -957,6 +959,13 @@ interface HistoryControls<T> {
|
|
|
957
959
|
* h.redo(); // count() === 2
|
|
958
960
|
* h.canUndo(); // true (reactive)
|
|
959
961
|
* ```
|
|
962
|
+
*
|
|
963
|
+
* **Limitation:** the source must be a signal whose getter returns a stable
|
|
964
|
+
* value (a primitive or a stable object reference). A `createStore` slice whose
|
|
965
|
+
* getter returns a *fresh proxy on every read* is not supported: the undo/redo
|
|
966
|
+
* echo guard compares by identity, so a new proxy each read makes every
|
|
967
|
+
* undo/redo look like an external change and clears the redo history. Track
|
|
968
|
+
* store state with `setState`/direct mutation, not `createHistory`.
|
|
960
969
|
*/
|
|
961
970
|
declare function createHistory<T>(source: [get: () => T, set: (v: T) => void], options?: {
|
|
962
971
|
maxLength?: number;
|
|
@@ -969,6 +978,8 @@ declare function createHistory<T>(source: [get: () => T, set: (v: T) => void], o
|
|
|
969
978
|
* Reads stored value on creation; writes on every signal change.
|
|
970
979
|
* Zero dependencies -- native browser APIs only.
|
|
971
980
|
*/
|
|
981
|
+
/** The phase in which a persist operation failed, passed to {@link PersistOptions.onError}. */
|
|
982
|
+
type PersistErrorPhase = 'hydrate' | 'serialize' | 'write' | 'migrate';
|
|
972
983
|
/** Options for {@link persist} — storage backend, serialization, and validation. */
|
|
973
984
|
interface PersistOptions<T> {
|
|
974
985
|
/** Storage backend. Defaults to localStorage. */
|
|
@@ -979,6 +990,14 @@ interface PersistOptions<T> {
|
|
|
979
990
|
deserialize?: (s: string) => T;
|
|
980
991
|
/** Optional validator — return true if the deserialized value is valid. Rejects corrupt/tampered data. */
|
|
981
992
|
validate?: (v: unknown) => v is T;
|
|
993
|
+
/** Current schema version. When set, values are stored in a versioned envelope. */
|
|
994
|
+
version?: number;
|
|
995
|
+
/** Migrate stored data whose version is older than {@link version}. Runs before validate. */
|
|
996
|
+
migrate?: (oldValue: unknown, oldVersion: number) => T;
|
|
997
|
+
/** Re-hydrate when another tab writes this key. Defaults to true for localStorage. */
|
|
998
|
+
syncTabs?: boolean;
|
|
999
|
+
/** Report hydrate/serialize/write/migrate failures instead of swallowing them. */
|
|
1000
|
+
onError?: (err: unknown, phase: PersistErrorPhase) => void;
|
|
982
1001
|
}
|
|
983
1002
|
/**
|
|
984
1003
|
* Persist a signal's value to storage.
|
|
@@ -993,7 +1012,7 @@ interface PersistOptions<T> {
|
|
|
993
1012
|
* setTheme('dark'); // auto-saved to localStorage under key 'app:theme'
|
|
994
1013
|
* ```
|
|
995
1014
|
*/
|
|
996
|
-
declare function persist<T>(source: [get: () => T, set: (v: T) => void], key: string, options?: PersistOptions<T>): void;
|
|
1015
|
+
declare function persist<T>(source: [get: () => T, set: (v: T) => void], key: string, options?: PersistOptions<T>): () => void;
|
|
997
1016
|
|
|
998
1017
|
/**
|
|
999
1018
|
* A typed publish/subscribe event bus.
|
|
@@ -1156,4 +1175,4 @@ declare function onIntersect(el: HTMLElement, handler: (entry: IntersectionObser
|
|
|
1156
1175
|
*/
|
|
1157
1176
|
declare function onMutation(el: HTMLElement, handler: (mutations: MutationRecord[]) => void, options?: MutationObserverInit): () => void;
|
|
1158
1177
|
|
|
1159
|
-
export { $, $$, type CleanupFn, type ComponentDef, type Context, type CreateListOptions, type Dispatch, type ErrorHandler, type EventBus, Fragment, type HistoryControls, type IslandHydrateFn, type KeyOptions, type ListTransitionHooks, type PersistOptions, type ReconcileResult, type Ref, type SetupFn, SignalGetter, type StoreSetter, type SwitchCase, activateIslands, addClass, batch, children, cleanup, closest, createBus, createComputed, createContext, createEffect, createErrorBoundary, createHistory, createList, createMemo, createPortal, createReducer, createRef, createRoot, createShow, createStore, createSuspense, createSwitch, createText, createUnownedRoot, deactivateAllIslands, deactivateIsland, defineComponent, delegate, disposeComponent, fragment, h, hydrateIsland, inject, mount, nextSibling, on, onCleanup, onError, onIntersect, onKey, onMount, onMutation, onResize, onUnmount, parent, persist, prevSibling, provide, reconcileList, removeClass, setAttr, setHTMLUnsafe, setStyle, setText, siblings, template, templateMany, toggleClass, trackDisposer, unprovide, untrack };
|
|
1178
|
+
export { $, $$, type CleanupFn, type ComponentDef, type Context, type CreateListOptions, type Dispatch, type ErrorHandler, type EventBus, Fragment, type HistoryControls, type IslandHydrateFn, type KeyOptions, type ListTransitionHooks, type PersistErrorPhase, type PersistOptions, type ReconcileResult, type Ref, type SetupFn, SignalGetter, type StoreSetter, type SwitchCase, activateIslands, addClass, batch, children, cleanup, closest, createBus, createComputed, createContext, createEffect, createErrorBoundary, createHistory, createList, createMemo, createPortal, createReducer, createRef, createRoot, createShow, createStore, createSuspense, createSwitch, createText, createUnownedRoot, deactivateAllIslands, deactivateIsland, defineComponent, delegate, disposeComponent, fragment, h, hydrateIsland, inject, mount, nextSibling, on, onCleanup, onError, onIntersect, onKey, onMount, onMutation, onResize, onUnmount, parent, persist, prevSibling, provide, reconcileList, removeClass, setAttr, setHTMLUnsafe, setStyle, setText, siblings, template, templateMany, toggleClass, trackDisposer, unprovide, untrack };
|
package/dist/index.d.ts
CHANGED
|
@@ -943,6 +943,8 @@ interface HistoryControls<T> {
|
|
|
943
943
|
cursor: () => number;
|
|
944
944
|
/** Clear all history, keeping only the current value. */
|
|
945
945
|
clear: () => void;
|
|
946
|
+
/** Stop tracking the source signal and release the history stack. */
|
|
947
|
+
destroy: () => void;
|
|
946
948
|
}
|
|
947
949
|
/**
|
|
948
950
|
* Create undo/redo history tracking for a signal.
|
|
@@ -957,6 +959,13 @@ interface HistoryControls<T> {
|
|
|
957
959
|
* h.redo(); // count() === 2
|
|
958
960
|
* h.canUndo(); // true (reactive)
|
|
959
961
|
* ```
|
|
962
|
+
*
|
|
963
|
+
* **Limitation:** the source must be a signal whose getter returns a stable
|
|
964
|
+
* value (a primitive or a stable object reference). A `createStore` slice whose
|
|
965
|
+
* getter returns a *fresh proxy on every read* is not supported: the undo/redo
|
|
966
|
+
* echo guard compares by identity, so a new proxy each read makes every
|
|
967
|
+
* undo/redo look like an external change and clears the redo history. Track
|
|
968
|
+
* store state with `setState`/direct mutation, not `createHistory`.
|
|
960
969
|
*/
|
|
961
970
|
declare function createHistory<T>(source: [get: () => T, set: (v: T) => void], options?: {
|
|
962
971
|
maxLength?: number;
|
|
@@ -969,6 +978,8 @@ declare function createHistory<T>(source: [get: () => T, set: (v: T) => void], o
|
|
|
969
978
|
* Reads stored value on creation; writes on every signal change.
|
|
970
979
|
* Zero dependencies -- native browser APIs only.
|
|
971
980
|
*/
|
|
981
|
+
/** The phase in which a persist operation failed, passed to {@link PersistOptions.onError}. */
|
|
982
|
+
type PersistErrorPhase = 'hydrate' | 'serialize' | 'write' | 'migrate';
|
|
972
983
|
/** Options for {@link persist} — storage backend, serialization, and validation. */
|
|
973
984
|
interface PersistOptions<T> {
|
|
974
985
|
/** Storage backend. Defaults to localStorage. */
|
|
@@ -979,6 +990,14 @@ interface PersistOptions<T> {
|
|
|
979
990
|
deserialize?: (s: string) => T;
|
|
980
991
|
/** Optional validator — return true if the deserialized value is valid. Rejects corrupt/tampered data. */
|
|
981
992
|
validate?: (v: unknown) => v is T;
|
|
993
|
+
/** Current schema version. When set, values are stored in a versioned envelope. */
|
|
994
|
+
version?: number;
|
|
995
|
+
/** Migrate stored data whose version is older than {@link version}. Runs before validate. */
|
|
996
|
+
migrate?: (oldValue: unknown, oldVersion: number) => T;
|
|
997
|
+
/** Re-hydrate when another tab writes this key. Defaults to true for localStorage. */
|
|
998
|
+
syncTabs?: boolean;
|
|
999
|
+
/** Report hydrate/serialize/write/migrate failures instead of swallowing them. */
|
|
1000
|
+
onError?: (err: unknown, phase: PersistErrorPhase) => void;
|
|
982
1001
|
}
|
|
983
1002
|
/**
|
|
984
1003
|
* Persist a signal's value to storage.
|
|
@@ -993,7 +1012,7 @@ interface PersistOptions<T> {
|
|
|
993
1012
|
* setTheme('dark'); // auto-saved to localStorage under key 'app:theme'
|
|
994
1013
|
* ```
|
|
995
1014
|
*/
|
|
996
|
-
declare function persist<T>(source: [get: () => T, set: (v: T) => void], key: string, options?: PersistOptions<T>): void;
|
|
1015
|
+
declare function persist<T>(source: [get: () => T, set: (v: T) => void], key: string, options?: PersistOptions<T>): () => void;
|
|
997
1016
|
|
|
998
1017
|
/**
|
|
999
1018
|
* A typed publish/subscribe event bus.
|
|
@@ -1156,4 +1175,4 @@ declare function onIntersect(el: HTMLElement, handler: (entry: IntersectionObser
|
|
|
1156
1175
|
*/
|
|
1157
1176
|
declare function onMutation(el: HTMLElement, handler: (mutations: MutationRecord[]) => void, options?: MutationObserverInit): () => void;
|
|
1158
1177
|
|
|
1159
|
-
export { $, $$, type CleanupFn, type ComponentDef, type Context, type CreateListOptions, type Dispatch, type ErrorHandler, type EventBus, Fragment, type HistoryControls, type IslandHydrateFn, type KeyOptions, type ListTransitionHooks, type PersistOptions, type ReconcileResult, type Ref, type SetupFn, SignalGetter, type StoreSetter, type SwitchCase, activateIslands, addClass, batch, children, cleanup, closest, createBus, createComputed, createContext, createEffect, createErrorBoundary, createHistory, createList, createMemo, createPortal, createReducer, createRef, createRoot, createShow, createStore, createSuspense, createSwitch, createText, createUnownedRoot, deactivateAllIslands, deactivateIsland, defineComponent, delegate, disposeComponent, fragment, h, hydrateIsland, inject, mount, nextSibling, on, onCleanup, onError, onIntersect, onKey, onMount, onMutation, onResize, onUnmount, parent, persist, prevSibling, provide, reconcileList, removeClass, setAttr, setHTMLUnsafe, setStyle, setText, siblings, template, templateMany, toggleClass, trackDisposer, unprovide, untrack };
|
|
1178
|
+
export { $, $$, type CleanupFn, type ComponentDef, type Context, type CreateListOptions, type Dispatch, type ErrorHandler, type EventBus, Fragment, type HistoryControls, type IslandHydrateFn, type KeyOptions, type ListTransitionHooks, type PersistErrorPhase, type PersistOptions, type ReconcileResult, type Ref, type SetupFn, SignalGetter, type StoreSetter, type SwitchCase, activateIslands, addClass, batch, children, cleanup, closest, createBus, createComputed, createContext, createEffect, createErrorBoundary, createHistory, createList, createMemo, createPortal, createReducer, createRef, createRoot, createShow, createStore, createSuspense, createSwitch, createText, createUnownedRoot, deactivateAllIslands, deactivateIsland, defineComponent, delegate, disposeComponent, fragment, h, hydrateIsland, inject, mount, nextSibling, on, onCleanup, onError, onIntersect, onKey, onMount, onMutation, onResize, onUnmount, parent, persist, prevSibling, provide, reconcileList, removeClass, setAttr, setHTMLUnsafe, setStyle, setText, siblings, template, templateMany, toggleClass, trackDisposer, unprovide, untrack };
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { hydrateIsland } from './chunk-
|
|
2
|
-
export { Fragment, cleanup, createList, createShow, fragment, h, hydrateIsland, reconcileList } from './chunk-
|
|
3
|
-
import { internalEffect, createUnownedRoot, createRoot, untrack, registerDisposer, createEffect, pushSuspenseContext, popSuspenseContext, batch } from './chunk-
|
|
4
|
-
export { batch, createEffect, createMemo, createReducer, createRef, createResource, createRoot, createUnownedRoot, getBatchDepth, isComputed, isEffect, isEffectScope, isSignal, on, onCleanup, trigger, untrack } from './chunk-
|
|
5
|
-
import { createSignal, __DEV__, reportError } from './chunk-
|
|
6
|
-
export { createComputed, createSignal, onError } from './chunk-
|
|
1
|
+
import { hydrateIsland } from './chunk-YRNYOZF3.js';
|
|
2
|
+
export { Fragment, cleanup, createList, createShow, fragment, h, hydrateIsland, reconcileList } from './chunk-YRNYOZF3.js';
|
|
3
|
+
import { internalEffect, createUnownedRoot, createRoot, untrack, registerDisposer, createEffect, pushSuspenseContext, popSuspenseContext, batch } from './chunk-Y3A2EVVS.js';
|
|
4
|
+
export { batch, createEffect, createMemo, createReducer, createRef, createResource, createRoot, createUnownedRoot, getBatchDepth, isComputed, isEffect, isEffectScope, isSignal, on, onCleanup, trigger, untrack } from './chunk-Y3A2EVVS.js';
|
|
5
|
+
import { createSignal, __DEV__, reportError, value } from './chunk-ETPJTPYD.js';
|
|
6
|
+
export { createComputed, createSignal, onError } from './chunk-ETPJTPYD.js';
|
|
7
7
|
|
|
8
8
|
// src/dom/text.ts
|
|
9
|
-
function createText(
|
|
10
|
-
if (typeof
|
|
9
|
+
function createText(value2) {
|
|
10
|
+
if (typeof value2 === "function") {
|
|
11
11
|
const node = new Text("");
|
|
12
12
|
internalEffect(() => {
|
|
13
|
-
node.data =
|
|
13
|
+
node.data = value2();
|
|
14
14
|
});
|
|
15
15
|
return node;
|
|
16
16
|
}
|
|
17
|
-
return new Text(
|
|
17
|
+
return new Text(value2);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// src/dom/mount.ts
|
|
@@ -47,7 +47,7 @@ function mount(component, container) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
// src/dom/switch.ts
|
|
50
|
-
function createSwitch(
|
|
50
|
+
function createSwitch(value2, cases, fallback) {
|
|
51
51
|
const startMarker = document.createComment("forma-switch");
|
|
52
52
|
const endMarker = document.createComment("/forma-switch");
|
|
53
53
|
const fragment2 = document.createDocumentFragment();
|
|
@@ -57,7 +57,7 @@ function createSwitch(value, cases, fallback) {
|
|
|
57
57
|
let currentNode = null;
|
|
58
58
|
let currentMatch = UNSET;
|
|
59
59
|
const switchDispose = internalEffect(() => {
|
|
60
|
-
const val =
|
|
60
|
+
const val = value2();
|
|
61
61
|
if (val === currentMatch) return;
|
|
62
62
|
const DEBUG = typeof globalThis.__FORMA_DEBUG__ !== "undefined";
|
|
63
63
|
if (DEBUG) console.log("[forma:switch] transition", String(currentMatch), "\u2192", String(val));
|
|
@@ -437,13 +437,13 @@ function createContext(defaultValue) {
|
|
|
437
437
|
defaultValue
|
|
438
438
|
};
|
|
439
439
|
}
|
|
440
|
-
function provide(ctx,
|
|
440
|
+
function provide(ctx, value2) {
|
|
441
441
|
let stack = contextStacks.get(ctx.id);
|
|
442
442
|
if (stack === void 0) {
|
|
443
443
|
stack = [];
|
|
444
444
|
contextStacks.set(ctx.id, stack);
|
|
445
445
|
}
|
|
446
|
-
stack.push(
|
|
446
|
+
stack.push(value2);
|
|
447
447
|
}
|
|
448
448
|
function inject(ctx) {
|
|
449
449
|
const stack = contextStacks.get(ctx.id);
|
|
@@ -499,6 +499,19 @@ function deepClone(obj, seen) {
|
|
|
499
499
|
function createStore(initial) {
|
|
500
500
|
const signals = /* @__PURE__ */ new Map();
|
|
501
501
|
const children2 = /* @__PURE__ */ new Map();
|
|
502
|
+
const arrayVersions = /* @__PURE__ */ new Map();
|
|
503
|
+
function getArrayVersion(path) {
|
|
504
|
+
let p = arrayVersions.get(path);
|
|
505
|
+
if (!p) {
|
|
506
|
+
p = createSignal(0);
|
|
507
|
+
arrayVersions.set(path, p);
|
|
508
|
+
}
|
|
509
|
+
return p;
|
|
510
|
+
}
|
|
511
|
+
function bumpArrayVersion(path) {
|
|
512
|
+
const p = arrayVersions.get(path);
|
|
513
|
+
if (p) p[1]((n) => n + 1);
|
|
514
|
+
}
|
|
502
515
|
function registerChild(path) {
|
|
503
516
|
const lastDot = path.lastIndexOf(".");
|
|
504
517
|
if (lastDot === -1) return;
|
|
@@ -530,14 +543,37 @@ function createStore(initial) {
|
|
|
530
543
|
}
|
|
531
544
|
childSet.clear();
|
|
532
545
|
}
|
|
546
|
+
function lastSegment(path) {
|
|
547
|
+
const d = path.lastIndexOf(".");
|
|
548
|
+
return d === -1 ? path : path.substring(d + 1);
|
|
549
|
+
}
|
|
550
|
+
function setLiteral(pair, v) {
|
|
551
|
+
pair[1](typeof v === "function" ? value(v) : v);
|
|
552
|
+
}
|
|
553
|
+
function reconcileChildren(parentPath, rawParent) {
|
|
554
|
+
const set = children2.get(parentPath);
|
|
555
|
+
if (!set) return;
|
|
556
|
+
for (const childPath of set) {
|
|
557
|
+
const key = lastSegment(childPath);
|
|
558
|
+
const nv = rawParent[key];
|
|
559
|
+
const pair = signals.get(childPath);
|
|
560
|
+
if (pair) setLiteral(pair, nv);
|
|
561
|
+
if (nv != null && typeof nv === "object") {
|
|
562
|
+
reconcileChildren(childPath, nv);
|
|
563
|
+
} else {
|
|
564
|
+
invalidateChildren(childPath);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
533
568
|
function wrap(raw, basePath) {
|
|
534
569
|
if (!shouldWrap(raw)) return raw;
|
|
535
|
-
|
|
536
|
-
if (
|
|
570
|
+
let byPath = proxyCache.get(raw);
|
|
571
|
+
if (byPath) {
|
|
572
|
+
const hit = byPath.get(basePath);
|
|
573
|
+
if (hit) return hit;
|
|
574
|
+
}
|
|
537
575
|
const isArr = Array.isArray(raw);
|
|
538
576
|
const basePrefix = basePath ? basePath + "." : "";
|
|
539
|
-
let lastKey = "";
|
|
540
|
-
let lastSignal;
|
|
541
577
|
const proxy = new Proxy(raw, {
|
|
542
578
|
// -------------------------------------------------------------------
|
|
543
579
|
// GET
|
|
@@ -558,12 +594,10 @@ function createStore(initial) {
|
|
|
558
594
|
(a) => a != null && typeof a === "object" && a[RAW] ? a[RAW] : a
|
|
559
595
|
);
|
|
560
596
|
result = target[key].apply(target, rawArgs);
|
|
561
|
-
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
);
|
|
566
|
-
setLen(target.length);
|
|
597
|
+
reconcileChildren(basePath, target);
|
|
598
|
+
const lenPair = signals.get(basePrefix + "length");
|
|
599
|
+
if (lenPair) lenPair[1](target.length);
|
|
600
|
+
if (basePath) bumpArrayVersion(basePath);
|
|
567
601
|
});
|
|
568
602
|
return result;
|
|
569
603
|
};
|
|
@@ -573,34 +607,32 @@ function createStore(initial) {
|
|
|
573
607
|
getter();
|
|
574
608
|
return target.length;
|
|
575
609
|
}
|
|
576
|
-
const
|
|
577
|
-
|
|
578
|
-
if (key === lastKey && lastSignal) {
|
|
579
|
-
pair = lastSignal;
|
|
580
|
-
} else {
|
|
581
|
-
pair = getSignal(childPath, value);
|
|
582
|
-
lastKey = key;
|
|
583
|
-
lastSignal = pair;
|
|
584
|
-
}
|
|
610
|
+
const value2 = Reflect.get(target, prop);
|
|
611
|
+
const pair = getSignal(childPath, value2);
|
|
585
612
|
pair[0]();
|
|
586
|
-
if (
|
|
587
|
-
|
|
613
|
+
if (Array.isArray(value2)) {
|
|
614
|
+
getArrayVersion(childPath)[0]();
|
|
615
|
+
}
|
|
616
|
+
if (shouldWrap(value2)) {
|
|
617
|
+
return wrap(value2, childPath);
|
|
588
618
|
}
|
|
589
|
-
return
|
|
619
|
+
return value2;
|
|
590
620
|
},
|
|
591
621
|
// -------------------------------------------------------------------
|
|
592
622
|
// SET
|
|
593
623
|
// -------------------------------------------------------------------
|
|
594
|
-
set(target, prop,
|
|
624
|
+
set(target, prop, value2) {
|
|
595
625
|
if (typeof prop === "symbol") {
|
|
596
|
-
return Reflect.set(target, prop,
|
|
626
|
+
return Reflect.set(target, prop, value2);
|
|
597
627
|
}
|
|
598
628
|
const key = String(prop);
|
|
599
629
|
const childPath = basePrefix + key;
|
|
600
|
-
const rawValue =
|
|
630
|
+
const rawValue = value2 != null && typeof value2 === "object" && value2[RAW] ? value2[RAW] : value2;
|
|
631
|
+
const oldRaw = Reflect.get(target, prop);
|
|
601
632
|
Reflect.set(target, prop, rawValue);
|
|
602
|
-
if (rawValue != null && typeof rawValue === "object") {
|
|
633
|
+
if (rawValue != null && typeof rawValue === "object" && oldRaw !== rawValue) {
|
|
603
634
|
invalidateChildren(childPath);
|
|
635
|
+
evictProxy(oldRaw, childPath);
|
|
604
636
|
}
|
|
605
637
|
if (isArr && key !== "length") {
|
|
606
638
|
const lengthPath = basePrefix + "length";
|
|
@@ -609,6 +641,12 @@ function createStore(initial) {
|
|
|
609
641
|
lenPair[1](target.length);
|
|
610
642
|
}
|
|
611
643
|
}
|
|
644
|
+
if (isArr && key === "length") {
|
|
645
|
+
batch(() => {
|
|
646
|
+
reconcileChildren(basePath, target);
|
|
647
|
+
if (basePath) bumpArrayVersion(basePath);
|
|
648
|
+
});
|
|
649
|
+
}
|
|
612
650
|
const [, setter2] = getSignal(childPath, rawValue);
|
|
613
651
|
setter2(rawValue);
|
|
614
652
|
return true;
|
|
@@ -647,24 +685,30 @@ function createStore(initial) {
|
|
|
647
685
|
}
|
|
648
686
|
const key = String(prop);
|
|
649
687
|
const childPath = basePrefix + key;
|
|
688
|
+
const oldRaw = Reflect.get(target, prop);
|
|
650
689
|
const result = Reflect.deleteProperty(target, prop);
|
|
690
|
+
const delPair = signals.get(childPath);
|
|
691
|
+
if (delPair) delPair[1](void 0);
|
|
692
|
+
evictProxy(oldRaw, childPath);
|
|
651
693
|
invalidateChildren(childPath);
|
|
652
|
-
signals.delete(childPath);
|
|
653
|
-
const parentPath = basePath;
|
|
654
|
-
if (parentPath !== void 0) {
|
|
655
|
-
const parentSet = children2.get(parentPath);
|
|
656
|
-
if (parentSet) {
|
|
657
|
-
parentSet.delete(childPath);
|
|
658
|
-
if (parentSet.size === 0) children2.delete(parentPath);
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
children2.delete(childPath);
|
|
662
694
|
return result;
|
|
663
695
|
}
|
|
664
696
|
});
|
|
665
|
-
|
|
697
|
+
if (!byPath) {
|
|
698
|
+
byPath = /* @__PURE__ */ new Map();
|
|
699
|
+
proxyCache.set(raw, byPath);
|
|
700
|
+
}
|
|
701
|
+
byPath.set(basePath, proxy);
|
|
666
702
|
return proxy;
|
|
667
703
|
}
|
|
704
|
+
function evictProxy(oldRaw, path) {
|
|
705
|
+
if (oldRaw == null || typeof oldRaw !== "object") return;
|
|
706
|
+
const om = proxyCache.get(oldRaw);
|
|
707
|
+
if (om) {
|
|
708
|
+
om.delete(path);
|
|
709
|
+
if (om.size === 0) proxyCache.delete(oldRaw);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
668
712
|
const rootProxy = wrap(initial, "");
|
|
669
713
|
function getCurrentSnapshot() {
|
|
670
714
|
return untrack(() => deepClone(initial));
|
|
@@ -681,10 +725,24 @@ function createStore(initial) {
|
|
|
681
725
|
}
|
|
682
726
|
|
|
683
727
|
// src/state/history.ts
|
|
728
|
+
function cloneEntry(v, seen) {
|
|
729
|
+
if (v === null || typeof v !== "object") return v;
|
|
730
|
+
const proto = Object.getPrototypeOf(v);
|
|
731
|
+
if (!Array.isArray(v) && proto !== Object.prototype && proto !== null) return v;
|
|
732
|
+
if (!seen) seen = /* @__PURE__ */ new WeakSet();
|
|
733
|
+
if (seen.has(v)) return v;
|
|
734
|
+
seen.add(v);
|
|
735
|
+
if (Array.isArray(v)) return v.map((i) => cloneEntry(i, seen));
|
|
736
|
+
const out = {};
|
|
737
|
+
for (const k of Object.keys(v)) {
|
|
738
|
+
out[k] = cloneEntry(v[k], seen);
|
|
739
|
+
}
|
|
740
|
+
return out;
|
|
741
|
+
}
|
|
684
742
|
function createHistory(source, options) {
|
|
685
743
|
const [sourceGet, sourceSet] = source;
|
|
686
|
-
const maxLength = options?.maxLength ?? 100;
|
|
687
|
-
let _stack = [sourceGet()];
|
|
744
|
+
const maxLength = Math.max(1, options?.maxLength ?? 100);
|
|
745
|
+
let _stack = [cloneEntry(sourceGet())];
|
|
688
746
|
let _cursor = 0;
|
|
689
747
|
const [stackSignal, setStackSignal] = createSignal([..._stack]);
|
|
690
748
|
const [cursorSignal, setCursorSignal] = createSignal(_cursor);
|
|
@@ -696,45 +754,52 @@ function createHistory(source, options) {
|
|
|
696
754
|
setStackLenSignal(_stack.length);
|
|
697
755
|
});
|
|
698
756
|
}
|
|
699
|
-
|
|
757
|
+
const NONE = /* @__PURE__ */ Symbol("none");
|
|
758
|
+
let _expected = NONE;
|
|
700
759
|
let isFirstRun = true;
|
|
701
|
-
internalEffect(() => {
|
|
702
|
-
const
|
|
760
|
+
const disposeEffect = internalEffect(() => {
|
|
761
|
+
const value2 = sourceGet();
|
|
703
762
|
if (isFirstRun) {
|
|
704
763
|
isFirstRun = false;
|
|
705
764
|
return;
|
|
706
765
|
}
|
|
707
|
-
if (
|
|
708
|
-
|
|
766
|
+
if (_expected !== NONE && Object.is(value2, _expected)) {
|
|
767
|
+
_expected = NONE;
|
|
709
768
|
return;
|
|
710
769
|
}
|
|
770
|
+
_expected = NONE;
|
|
711
771
|
_stack = _stack.slice(0, _cursor + 1);
|
|
712
|
-
_stack.push(
|
|
772
|
+
_stack.push(cloneEntry(value2));
|
|
713
773
|
if (_stack.length > maxLength) {
|
|
714
774
|
_stack.splice(0, _stack.length - maxLength);
|
|
715
775
|
}
|
|
716
776
|
_cursor = _stack.length - 1;
|
|
717
777
|
syncSignals();
|
|
718
778
|
});
|
|
779
|
+
const destroy = () => {
|
|
780
|
+
disposeEffect();
|
|
781
|
+
};
|
|
719
782
|
const canUndo = () => cursorSignal() > 0;
|
|
720
783
|
const canRedo = () => cursorSignal() < stackLenSignal() - 1;
|
|
721
784
|
const undo = () => {
|
|
722
785
|
if (_cursor <= 0) return;
|
|
723
786
|
_cursor--;
|
|
724
|
-
|
|
725
|
-
|
|
787
|
+
const restored = cloneEntry(_stack[_cursor]);
|
|
788
|
+
_expected = restored;
|
|
789
|
+
sourceSet(restored);
|
|
726
790
|
syncSignals();
|
|
727
791
|
};
|
|
728
792
|
const redo = () => {
|
|
729
793
|
if (_cursor >= _stack.length - 1) return;
|
|
730
794
|
_cursor++;
|
|
731
|
-
|
|
732
|
-
|
|
795
|
+
const restored = cloneEntry(_stack[_cursor]);
|
|
796
|
+
_expected = restored;
|
|
797
|
+
sourceSet(restored);
|
|
733
798
|
syncSignals();
|
|
734
799
|
};
|
|
735
800
|
const clear = () => {
|
|
736
801
|
const currentValue = sourceGet();
|
|
737
|
-
_stack = [currentValue];
|
|
802
|
+
_stack = [cloneEntry(currentValue)];
|
|
738
803
|
_cursor = 0;
|
|
739
804
|
syncSignals();
|
|
740
805
|
};
|
|
@@ -745,35 +810,91 @@ function createHistory(source, options) {
|
|
|
745
810
|
canRedo,
|
|
746
811
|
history: () => stackSignal(),
|
|
747
812
|
cursor: () => cursorSignal(),
|
|
748
|
-
clear
|
|
813
|
+
clear,
|
|
814
|
+
destroy
|
|
749
815
|
};
|
|
750
816
|
}
|
|
751
817
|
|
|
752
818
|
// src/state/persist.ts
|
|
819
|
+
var ENVELOPE_TAG = "$forma:v";
|
|
753
820
|
function persist(source, key, options) {
|
|
754
821
|
const [sourceGet, sourceSet] = source;
|
|
755
822
|
const storage = options?.storage ?? globalThis.localStorage;
|
|
756
823
|
const serialize = options?.serialize ?? JSON.stringify;
|
|
757
824
|
const deserialize = options?.deserialize ?? JSON.parse;
|
|
758
825
|
const validate = options?.validate;
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
826
|
+
const version = options?.version;
|
|
827
|
+
const migrate = options?.migrate;
|
|
828
|
+
const onError2 = options?.onError;
|
|
829
|
+
let writing = false;
|
|
830
|
+
function unwrap(stored) {
|
|
831
|
+
const parsed = deserialize(stored);
|
|
832
|
+
if (parsed != null && typeof parsed === "object" && Object.prototype.hasOwnProperty.call(parsed, ENVELOPE_TAG)) {
|
|
833
|
+
const env = parsed;
|
|
834
|
+
return { value: env.value, version: Number(env[ENVELOPE_TAG]) };
|
|
835
|
+
}
|
|
836
|
+
return { value: parsed, version: 0 };
|
|
837
|
+
}
|
|
838
|
+
function hydrate() {
|
|
839
|
+
let stored;
|
|
840
|
+
try {
|
|
841
|
+
stored = storage.getItem(key);
|
|
842
|
+
} catch (err) {
|
|
843
|
+
onError2?.(err, "hydrate");
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
if (stored === null) return;
|
|
847
|
+
try {
|
|
848
|
+
const { value: raw, version: storedVersion } = unwrap(stored);
|
|
849
|
+
let value2 = raw;
|
|
850
|
+
if (version !== void 0 && storedVersion < version) {
|
|
851
|
+
if (!migrate) return;
|
|
852
|
+
try {
|
|
853
|
+
value2 = migrate(raw, storedVersion);
|
|
854
|
+
} catch (err) {
|
|
855
|
+
onError2?.(err, "migrate");
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
if (!validate || validate(value2)) {
|
|
860
|
+
writing = true;
|
|
861
|
+
try {
|
|
862
|
+
sourceSet(value2);
|
|
863
|
+
} finally {
|
|
864
|
+
writing = false;
|
|
865
|
+
}
|
|
765
866
|
}
|
|
867
|
+
} catch (err) {
|
|
868
|
+
onError2?.(err, "hydrate");
|
|
766
869
|
}
|
|
767
|
-
} catch {
|
|
768
870
|
}
|
|
769
|
-
|
|
770
|
-
|
|
871
|
+
hydrate();
|
|
872
|
+
const stopEffect = internalEffect(() => {
|
|
873
|
+
const value2 = sourceGet();
|
|
874
|
+
if (writing) return;
|
|
771
875
|
try {
|
|
772
|
-
const serialized = serialize(value);
|
|
876
|
+
const serialized = version !== void 0 ? serialize({ [ENVELOPE_TAG]: version, value: value2 }) : serialize(value2);
|
|
773
877
|
storage.setItem(key, serialized);
|
|
774
|
-
} catch {
|
|
878
|
+
} catch (err) {
|
|
879
|
+
onError2?.(err, err?.name === "QuotaExceededError" ? "write" : "serialize");
|
|
775
880
|
}
|
|
776
881
|
});
|
|
882
|
+
const enableSync = options?.syncTabs ?? (typeof window !== "undefined" && storage === globalThis.localStorage);
|
|
883
|
+
let onStorage;
|
|
884
|
+
if (enableSync && typeof window !== "undefined") {
|
|
885
|
+
onStorage = (e) => {
|
|
886
|
+
if (e.storageArea !== storage) return;
|
|
887
|
+
if (e.key !== null && e.key !== key) return;
|
|
888
|
+
hydrate();
|
|
889
|
+
};
|
|
890
|
+
window.addEventListener("storage", onStorage);
|
|
891
|
+
}
|
|
892
|
+
return () => {
|
|
893
|
+
stopEffect();
|
|
894
|
+
if (onStorage && typeof window !== "undefined") {
|
|
895
|
+
window.removeEventListener("storage", onStorage);
|
|
896
|
+
}
|
|
897
|
+
};
|
|
777
898
|
}
|
|
778
899
|
|
|
779
900
|
// src/events/bus.ts
|
|
@@ -920,20 +1041,20 @@ function toggleClass(el, className, force) {
|
|
|
920
1041
|
return el.classList.toggle(className, force);
|
|
921
1042
|
}
|
|
922
1043
|
function setStyle(el, styles) {
|
|
923
|
-
for (const [key,
|
|
924
|
-
if (
|
|
925
|
-
el.style[key] =
|
|
1044
|
+
for (const [key, value2] of Object.entries(styles)) {
|
|
1045
|
+
if (value2 !== void 0) {
|
|
1046
|
+
el.style[key] = value2;
|
|
926
1047
|
}
|
|
927
1048
|
}
|
|
928
1049
|
}
|
|
929
1050
|
function setAttr(el, attrs) {
|
|
930
|
-
for (const [name,
|
|
931
|
-
if (
|
|
1051
|
+
for (const [name, value2] of Object.entries(attrs)) {
|
|
1052
|
+
if (value2 === false || value2 === null) {
|
|
932
1053
|
el.removeAttribute(name);
|
|
933
|
-
} else if (
|
|
1054
|
+
} else if (value2 === true) {
|
|
934
1055
|
el.setAttribute(name, "");
|
|
935
1056
|
} else {
|
|
936
|
-
el.setAttribute(name,
|
|
1057
|
+
el.setAttribute(name, value2);
|
|
937
1058
|
}
|
|
938
1059
|
}
|
|
939
1060
|
}
|