preact-sigma 3.1.0 → 5.0.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.
@@ -0,0 +1,101 @@
1
+ import { A as Immutable, _ as SigmaDefinition, y as SigmaState } from "./framework-GgPzRfff.mjs";
2
+
3
+ //#region src/persist.d.ts
4
+ type MaybePromise<T> = T | Promise<T>;
5
+ type Snapshot<T extends SigmaDefinition> = Immutable<T["state"]>;
6
+ type CodecContext<TState extends object> = {
7
+ key: string;
8
+ storedVersion: number;
9
+ baseState: Readonly<TState>;
10
+ };
11
+ /** A stored persistence record with version and save-time metadata. */
12
+ interface PersistRecord<TStored = unknown> {
13
+ version: number;
14
+ savedAt: number;
15
+ value: TStored;
16
+ }
17
+ /** Storage adapter used by persistence helpers. */
18
+ interface PersistStore<TRecord> {
19
+ read(key: string): MaybePromise<TRecord | undefined>;
20
+ write(key: string, record: TRecord): MaybePromise<void>;
21
+ remove(key: string): MaybePromise<void>;
22
+ }
23
+ /** Synchronous storage adapter used by sync restore helpers. */
24
+ interface SyncPersistStore<TRecord> extends PersistStore<TRecord> {
25
+ read(key: string): TRecord | undefined;
26
+ write(key: string, record: TRecord): void;
27
+ remove(key: string): void;
28
+ }
29
+ /** Codec that maps between in-memory sigma state and stored payloads. */
30
+ interface PersistCodec<TState extends object, TStored = TState> {
31
+ version: number;
32
+ encode(state: Readonly<TState>): TStored;
33
+ decode(stored: unknown, context: CodecContext<TState>): TState;
34
+ }
35
+ /** Scheduling policy for persistence writes. */
36
+ type PersistSchedule = "immediate" | "microtask" | {
37
+ debounceMs: number;
38
+ };
39
+ /** Options shared by restore and persistence helpers. */
40
+ interface PersistOptions<T extends SigmaDefinition, TStored = Snapshot<T>> {
41
+ /** Storage key used for reads, writes, and removals. */
42
+ key: string;
43
+ /** Store adapter that owns persistence I/O. */
44
+ store: PersistStore<PersistRecord<TStored>>;
45
+ /** Codec that maps committed snapshots to stored payloads. Defaults to an identity codec with version 1. */
46
+ codec?: PersistCodec<Snapshot<T>, TStored>;
47
+ /** Scheduling policy for future writes. Defaults to `"microtask"`. */
48
+ schedule?: PersistSchedule;
49
+ /** Writes the current committed snapshot once after persistence becomes active. */
50
+ writeInitial?: boolean;
51
+ /** Receives background write failures without stopping persistence automatically. */
52
+ onWriteError?: (error: unknown, context: {
53
+ instance: SigmaState<T>;
54
+ key: string;
55
+ }) => void;
56
+ }
57
+ /** Options that require a synchronous store. */
58
+ interface SyncPersistOptions<T extends SigmaDefinition, TStored = Snapshot<T>> extends PersistOptions<T, TStored> {
59
+ store: SyncPersistStore<PersistRecord<TStored>>;
60
+ }
61
+ /** Result returned by restore helpers. */
62
+ type RestoreResult = {
63
+ status: "missing";
64
+ } | {
65
+ status: "restored";
66
+ savedAt: number;
67
+ storedVersion: number;
68
+ };
69
+ /** Handle returned by persistence bindings. */
70
+ interface PersistenceHandle {
71
+ /** Waits for any scheduled or active write for this binding to finish. */
72
+ flush(): Promise<void>;
73
+ /** Removes the stored record and keeps the binding active for later writes. */
74
+ clear(): Promise<void>;
75
+ /** Stops future persistence and waits for any active write to settle. */
76
+ stop(): Promise<void>;
77
+ }
78
+ /** Async restore-plus-persist binding result. */
79
+ interface BoundPersistence extends PersistenceHandle {
80
+ /** Resolves when restore finishes and reports whether a record was applied. */
81
+ readonly restored: Promise<RestoreResult>;
82
+ }
83
+ /** Sync restore-plus-persist binding result. */
84
+ interface SyncBoundPersistence extends PersistenceHandle {
85
+ /** Reports the synchronous restore result that ran before persistence started. */
86
+ readonly restored: RestoreResult;
87
+ }
88
+ /** Restores committed state from a persisted record through an async store. */
89
+ declare function restoreState<T extends SigmaDefinition, TStored = Snapshot<T>>(instance: SigmaState<T>, options: PersistOptions<T, TStored>): Promise<RestoreResult>;
90
+ /** Restores committed state from a persisted record through a sync store. */
91
+ declare function restoreStateSync<T extends SigmaDefinition, TStored = Snapshot<T>>(instance: SigmaState<T>, options: SyncPersistOptions<T, TStored>): RestoreResult;
92
+ /** Creates a codec that persists selected top-level state keys and reconstructs a full snapshot on decode. */
93
+ declare function pickStateCodec<TState extends object, TKey extends keyof TState>(keys: readonly TKey[]): PersistCodec<TState, Pick<TState, TKey>>;
94
+ /** Persists future committed state changes for one sigma-state instance. */
95
+ declare function persistState<T extends SigmaDefinition, TStored = Snapshot<T>>(instance: SigmaState<T>, options: PersistOptions<T, TStored>): PersistenceHandle;
96
+ /** Restores state, then begins persisting future committed changes. */
97
+ declare function bindPersistence<T extends SigmaDefinition, TStored = Snapshot<T>>(instance: SigmaState<T>, options: PersistOptions<T, TStored>): BoundPersistence;
98
+ /** Restores state synchronously, then begins persisting future committed changes. */
99
+ declare function bindPersistenceSync<T extends SigmaDefinition, TStored = Snapshot<T>>(instance: SigmaState<T>, options: SyncPersistOptions<T, TStored>): SyncBoundPersistence;
100
+ //#endregion
101
+ export { BoundPersistence, PersistCodec, PersistOptions, PersistRecord, PersistSchedule, PersistStore, PersistenceHandle, RestoreResult, SyncBoundPersistence, SyncPersistOptions, SyncPersistStore, bindPersistence, bindPersistenceSync, persistState, pickStateCodec, restoreState, restoreStateSync };
@@ -0,0 +1,248 @@
1
+ import { c as sigma } from "./runtime-nX4Aygb8.mjs";
2
+ //#region src/persist.ts
3
+ function createIdentityCodec() {
4
+ return {
5
+ version: 1,
6
+ encode(state) {
7
+ return state;
8
+ },
9
+ decode(stored) {
10
+ return stored;
11
+ }
12
+ };
13
+ }
14
+ function getCodec(options) {
15
+ return options.codec ?? createIdentityCodec();
16
+ }
17
+ function applyRecord(instance, key, record, codec) {
18
+ if (!record) return { status: "missing" };
19
+ const baseState = sigma.getState(instance);
20
+ const nextState = codec.decode(record.value, {
21
+ baseState,
22
+ key,
23
+ storedVersion: record.version
24
+ });
25
+ sigma.replaceState(instance, nextState);
26
+ return {
27
+ status: "restored",
28
+ savedAt: record.savedAt,
29
+ storedVersion: record.version
30
+ };
31
+ }
32
+ function isPlainObject(value) {
33
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
34
+ const prototype = Object.getPrototypeOf(value);
35
+ return prototype === Object.prototype || prototype === null;
36
+ }
37
+ /** Restores committed state from a persisted record through an async store. */
38
+ async function restoreState(instance, options) {
39
+ const codec = getCodec(options);
40
+ const record = await options.store.read(options.key);
41
+ return applyRecord(instance, options.key, record, codec);
42
+ }
43
+ /** Restores committed state from a persisted record through a sync store. */
44
+ function restoreStateSync(instance, options) {
45
+ const codec = getCodec(options);
46
+ const record = options.store.read(options.key);
47
+ return applyRecord(instance, options.key, record, codec);
48
+ }
49
+ /** Creates a codec that persists selected top-level state keys and reconstructs a full snapshot on decode. */
50
+ function pickStateCodec(keys) {
51
+ return {
52
+ version: 1,
53
+ encode(state) {
54
+ const stored = {};
55
+ for (const key of keys) stored[key] = state[key];
56
+ return stored;
57
+ },
58
+ decode(stored, context) {
59
+ if (!isPlainObject(stored)) throw new Error("[preact-sigma/persist] pickStateCodec() requires a plain object payload");
60
+ const partialStored = stored;
61
+ const restored = { ...context.baseState };
62
+ for (const key of keys) if (key in partialStored) restored[key] = partialStored[key];
63
+ return restored;
64
+ }
65
+ };
66
+ }
67
+ /** Persists future committed state changes for one sigma-state instance. */
68
+ function persistState(instance, options) {
69
+ const codec = getCodec(options);
70
+ const schedule = options.schedule ?? "microtask";
71
+ const key = options.key;
72
+ let stopped = false;
73
+ let suspended = false;
74
+ let hasPendingState = false;
75
+ let pendingState;
76
+ let microtaskScheduled = false;
77
+ let debounceTimer;
78
+ let runningWrite;
79
+ let backgroundWrite;
80
+ const cancelScheduledWrite = () => {
81
+ microtaskScheduled = false;
82
+ if (debounceTimer !== void 0) {
83
+ clearTimeout(debounceTimer);
84
+ debounceTimer = void 0;
85
+ }
86
+ };
87
+ const createRecord = (state) => ({
88
+ version: codec.version,
89
+ savedAt: Date.now(),
90
+ value: codec.encode(state)
91
+ });
92
+ const startBackgroundWrite = () => {
93
+ if (backgroundWrite) return;
94
+ backgroundWrite = drainPendingWrites().catch((error) => {
95
+ options.onWriteError?.(error, {
96
+ instance,
97
+ key
98
+ });
99
+ }).finally(() => {
100
+ backgroundWrite = void 0;
101
+ });
102
+ };
103
+ const scheduleWrite = () => {
104
+ if (stopped || suspended) return;
105
+ if (schedule === "immediate") {
106
+ startBackgroundWrite();
107
+ return;
108
+ }
109
+ if (schedule === "microtask") {
110
+ if (microtaskScheduled) return;
111
+ microtaskScheduled = true;
112
+ queueMicrotask(() => {
113
+ microtaskScheduled = false;
114
+ if (stopped || suspended) return;
115
+ startBackgroundWrite();
116
+ });
117
+ return;
118
+ }
119
+ if (debounceTimer !== void 0) clearTimeout(debounceTimer);
120
+ debounceTimer = setTimeout(() => {
121
+ debounceTimer = void 0;
122
+ if (stopped || suspended) return;
123
+ startBackgroundWrite();
124
+ }, schedule.debounceMs);
125
+ };
126
+ const queueStateWrite = () => {
127
+ pendingState = sigma.getState(instance);
128
+ hasPendingState = true;
129
+ scheduleWrite();
130
+ };
131
+ async function drainPendingWrites() {
132
+ if (runningWrite) return runningWrite;
133
+ cancelScheduledWrite();
134
+ runningWrite = (async () => {
135
+ while (hasPendingState && !stopped && !suspended) {
136
+ const state = pendingState;
137
+ hasPendingState = false;
138
+ await options.store.write(key, createRecord(state));
139
+ }
140
+ })();
141
+ try {
142
+ await runningWrite;
143
+ } finally {
144
+ runningWrite = void 0;
145
+ if (hasPendingState && !stopped && !suspended) startBackgroundWrite();
146
+ }
147
+ }
148
+ const stopSubscription = sigma.subscribe(instance, queueStateWrite);
149
+ if (options.writeInitial) queueStateWrite();
150
+ return {
151
+ async flush() {
152
+ cancelScheduledWrite();
153
+ if (!hasPendingState) {
154
+ await runningWrite;
155
+ return;
156
+ }
157
+ await drainPendingWrites();
158
+ },
159
+ async clear() {
160
+ suspended = true;
161
+ cancelScheduledWrite();
162
+ hasPendingState = false;
163
+ pendingState = void 0;
164
+ try {
165
+ await runningWrite;
166
+ await options.store.remove(key);
167
+ } finally {
168
+ suspended = false;
169
+ if (hasPendingState && !stopped) scheduleWrite();
170
+ }
171
+ },
172
+ async stop() {
173
+ if (stopped) {
174
+ await runningWrite;
175
+ return;
176
+ }
177
+ stopped = true;
178
+ suspended = true;
179
+ stopSubscription();
180
+ cancelScheduledWrite();
181
+ hasPendingState = false;
182
+ pendingState = void 0;
183
+ await runningWrite;
184
+ }
185
+ };
186
+ }
187
+ /** Restores state, then begins persisting future committed changes. */
188
+ function bindPersistence(instance, options) {
189
+ let stopped = false;
190
+ let handle;
191
+ const restored = (async () => {
192
+ const result = await restoreState(instance, options);
193
+ if (!stopped) handle = persistState(instance, options);
194
+ return result;
195
+ })();
196
+ return {
197
+ restored,
198
+ async flush() {
199
+ await restored;
200
+ await handle?.flush();
201
+ },
202
+ async clear() {
203
+ try {
204
+ await restored;
205
+ } catch {
206
+ await options.store.remove(options.key);
207
+ return;
208
+ }
209
+ if (handle) {
210
+ await handle.clear();
211
+ return;
212
+ }
213
+ await options.store.remove(options.key);
214
+ },
215
+ async stop() {
216
+ if (stopped) {
217
+ await handle?.stop();
218
+ return;
219
+ }
220
+ stopped = true;
221
+ try {
222
+ await restored;
223
+ } catch {
224
+ return;
225
+ }
226
+ await handle?.stop();
227
+ }
228
+ };
229
+ }
230
+ /** Restores state synchronously, then begins persisting future committed changes. */
231
+ function bindPersistenceSync(instance, options) {
232
+ const restored = restoreStateSync(instance, options);
233
+ const handle = persistState(instance, options);
234
+ return {
235
+ restored,
236
+ clear() {
237
+ return handle.clear();
238
+ },
239
+ flush() {
240
+ return handle.flush();
241
+ },
242
+ stop() {
243
+ return handle.stop();
244
+ }
245
+ };
246
+ }
247
+ //#endregion
248
+ export { bindPersistence, bindPersistenceSync, persistState, pickStateCodec, restoreState, restoreStateSync };