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.
- package/README.md +4 -5
- package/dist/framework-GgPzRfff.d.mts +331 -0
- package/dist/index.d.mts +3 -328
- package/dist/index.mjs +6 -585
- package/dist/persist.d.mts +101 -0
- package/dist/persist.mjs +248 -0
- package/dist/runtime-nX4Aygb8.mjs +595 -0
- package/docs/context.md +33 -14
- package/docs/persist.md +66 -0
- package/examples/async-commit.ts +2 -2
- package/examples/observe-and-restore.ts +9 -7
- package/examples/persist-search-draft.ts +67 -0
- package/examples/sigma-target.ts +1 -1
- package/examples/signal-access.ts +5 -2
- package/package.json +17 -12
package/dist/index.mjs
CHANGED
|
@@ -1,540 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import { a as initializeSigmaInstance, c as sigma, d as listen, f as sigmaStateBrand, i as buildQueryMethod, l as SigmaListenerMap, n as assertDefinitionKeyAvailable, o as setAutoFreeze, r as buildActionMethod, s as shouldSetup, t as Sigma, u as SigmaTarget } from "./runtime-nX4Aygb8.mjs";
|
|
2
|
+
import { action, computed, effect } from "@preact/signals";
|
|
3
|
+
import { immerable } from "immer";
|
|
4
4
|
import { useEffect, useRef, useState } from "preact/hooks";
|
|
5
|
-
//#region src/internal/context.ts
|
|
6
|
-
const disabledContextOptions = {
|
|
7
|
-
allowAct: false,
|
|
8
|
-
allowActions: false,
|
|
9
|
-
allowCommit: false,
|
|
10
|
-
allowEmit: false,
|
|
11
|
-
allowQueries: false,
|
|
12
|
-
allowWrites: false,
|
|
13
|
-
draftAware: false,
|
|
14
|
-
draftOnRead: false,
|
|
15
|
-
liveComputeds: false,
|
|
16
|
-
reactiveReads: false
|
|
17
|
-
};
|
|
18
|
-
const publicContextOptions = {
|
|
19
|
-
computedReadonly: {
|
|
20
|
-
...disabledContextOptions,
|
|
21
|
-
reactiveReads: true
|
|
22
|
-
},
|
|
23
|
-
observe: {
|
|
24
|
-
...disabledContextOptions,
|
|
25
|
-
allowQueries: true
|
|
26
|
-
},
|
|
27
|
-
queryCommitted: {
|
|
28
|
-
...disabledContextOptions,
|
|
29
|
-
allowQueries: true,
|
|
30
|
-
reactiveReads: true
|
|
31
|
-
},
|
|
32
|
-
setup: {
|
|
33
|
-
...disabledContextOptions,
|
|
34
|
-
allowAct: true,
|
|
35
|
-
allowActions: true,
|
|
36
|
-
allowEmit: true,
|
|
37
|
-
allowQueries: true
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
const ownerContextOptions = {
|
|
41
|
-
action: {
|
|
42
|
-
...disabledContextOptions,
|
|
43
|
-
allowActions: true,
|
|
44
|
-
allowCommit: true,
|
|
45
|
-
allowEmit: true,
|
|
46
|
-
allowQueries: true,
|
|
47
|
-
allowWrites: true,
|
|
48
|
-
draftAware: true,
|
|
49
|
-
draftOnRead: true,
|
|
50
|
-
liveComputeds: true
|
|
51
|
-
},
|
|
52
|
-
computedDraftAware: {
|
|
53
|
-
...disabledContextOptions,
|
|
54
|
-
draftAware: true,
|
|
55
|
-
liveComputeds: true
|
|
56
|
-
},
|
|
57
|
-
queryDraftAware: {
|
|
58
|
-
...disabledContextOptions,
|
|
59
|
-
allowQueries: true,
|
|
60
|
-
draftAware: true,
|
|
61
|
-
liveComputeds: true
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
const dirtyContexts = {
|
|
65
|
-
computedReadonly: /* @__PURE__ */ new Set(),
|
|
66
|
-
observe: /* @__PURE__ */ new Set(),
|
|
67
|
-
queryCommitted: /* @__PURE__ */ new Set(),
|
|
68
|
-
setup: /* @__PURE__ */ new Set()
|
|
69
|
-
};
|
|
70
|
-
const contextKinds = Object.keys(dirtyContexts);
|
|
71
|
-
const contextCache = {
|
|
72
|
-
computedReadonly: /* @__PURE__ */ new WeakMap(),
|
|
73
|
-
observe: /* @__PURE__ */ new WeakMap(),
|
|
74
|
-
queryCommitted: /* @__PURE__ */ new WeakMap(),
|
|
75
|
-
setup: /* @__PURE__ */ new WeakMap()
|
|
76
|
-
};
|
|
77
|
-
const contextOwnerMap = /* @__PURE__ */ new WeakMap();
|
|
78
|
-
let contextCacheFlushScheduled = false;
|
|
79
|
-
function getContext(target, kind) {
|
|
80
|
-
if (isOwnerContextKind(kind)) return getOwnerContext(target, kind);
|
|
81
|
-
return getPublicContext(target, kind);
|
|
82
|
-
}
|
|
83
|
-
function getContextOwner(context) {
|
|
84
|
-
return contextOwnerMap.get(context);
|
|
85
|
-
}
|
|
86
|
-
function registerContextOwner(context, owner) {
|
|
87
|
-
contextOwnerMap.set(context, owner);
|
|
88
|
-
}
|
|
89
|
-
function createContext(instance, options, owner) {
|
|
90
|
-
const publicPrototype = Object.getPrototypeOf(instance.publicInstance);
|
|
91
|
-
return new Proxy(publicPrototype, {
|
|
92
|
-
get(_target, key, receiver) {
|
|
93
|
-
if (typeof key !== "string") return Reflect.get(publicPrototype, key, owner?.actionContext ?? instance.publicInstance);
|
|
94
|
-
if (key === "act") return options.allowAct ? (actionFn) => {
|
|
95
|
-
if (typeof actionFn !== "function") throw new Error("[preact-sigma] act() requires a function");
|
|
96
|
-
return runAdHocAction(receiver, actionFn);
|
|
97
|
-
} : void 0;
|
|
98
|
-
if (key === "commit") return options.allowCommit && owner ? () => commitActionOwner(owner) : void 0;
|
|
99
|
-
if (key === "emit") return options.allowEmit && owner ? (name, detail) => {
|
|
100
|
-
handleActionBoundary(owner, "emit");
|
|
101
|
-
instance.publicInstance.dispatchEvent(new CustomEvent(name, { detail }));
|
|
102
|
-
} : void 0;
|
|
103
|
-
if (instance.stateKeys.has(key)) {
|
|
104
|
-
if (owner && options.draftAware) return readActionStateValue(owner, key, options);
|
|
105
|
-
const signal = getSignal(instance, key);
|
|
106
|
-
return options.reactiveReads ? signal.value : signal.peek();
|
|
107
|
-
}
|
|
108
|
-
if (key in instance.type._computeFunctions) {
|
|
109
|
-
if (owner && options.liveComputeds) return readActionComputedValue(owner, key);
|
|
110
|
-
const signal = getSignal(instance, key);
|
|
111
|
-
return options.reactiveReads ? signal.value : signal.peek();
|
|
112
|
-
}
|
|
113
|
-
if (options.allowQueries && key in instance.type._queryFunctions) return Reflect.get(instance.publicInstance, key);
|
|
114
|
-
if (options.allowActions && key in instance.type._actionFunctions) return Reflect.get(instance.publicInstance, key);
|
|
115
|
-
if (Reflect.has(publicPrototype, key)) return Reflect.get(publicPrototype, key, owner?.actionContext ?? instance.publicInstance);
|
|
116
|
-
},
|
|
117
|
-
set(_target, key, value) {
|
|
118
|
-
if (!owner || !options.allowWrites || typeof key !== "string" || !instance.stateKeys.has(key)) return false;
|
|
119
|
-
setActionStateValue(owner, key, value);
|
|
120
|
-
return true;
|
|
121
|
-
},
|
|
122
|
-
apply: unsupportedOperation,
|
|
123
|
-
construct: unsupportedOperation,
|
|
124
|
-
defineProperty: unsupportedOperation,
|
|
125
|
-
deleteProperty: unsupportedOperation,
|
|
126
|
-
getOwnPropertyDescriptor: unsupportedOperation,
|
|
127
|
-
has: unsupportedOperation,
|
|
128
|
-
isExtensible: unsupportedOperation,
|
|
129
|
-
ownKeys: unsupportedOperation,
|
|
130
|
-
preventExtensions: unsupportedOperation,
|
|
131
|
-
setPrototypeOf: unsupportedOperation
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
function unsupportedOperation() {
|
|
135
|
-
throw new Error("[preact-sigma] This operation is not supported by context proxies");
|
|
136
|
-
}
|
|
137
|
-
const kindToOwnerContextKey = {
|
|
138
|
-
action: "actionContext",
|
|
139
|
-
computedDraftAware: "computedContext",
|
|
140
|
-
queryDraftAware: "queryContext"
|
|
141
|
-
};
|
|
142
|
-
function isOwnerContextKind(kind) {
|
|
143
|
-
return kind in kindToOwnerContextKey;
|
|
144
|
-
}
|
|
145
|
-
function getOwnerContext(owner, kind) {
|
|
146
|
-
const contextKey = kindToOwnerContextKey[kind];
|
|
147
|
-
if (owner[contextKey]) return owner[contextKey];
|
|
148
|
-
const context = createContext(owner.instance, ownerContextOptions[kind], owner);
|
|
149
|
-
registerSigmaInternals(context, owner.instance);
|
|
150
|
-
registerContextOwner(context, owner);
|
|
151
|
-
owner[contextKey] = context;
|
|
152
|
-
return context;
|
|
153
|
-
}
|
|
154
|
-
function getPublicContext(instance, kind) {
|
|
155
|
-
const cachedContext = contextCache[kind].get(instance);
|
|
156
|
-
if (cachedContext) return cachedContext;
|
|
157
|
-
const context = createContext(instance, publicContextOptions[kind], void 0);
|
|
158
|
-
registerSigmaInternals(context, instance);
|
|
159
|
-
contextCache[kind].set(instance, context);
|
|
160
|
-
dirtyContexts[kind].add(instance);
|
|
161
|
-
if (!contextCacheFlushScheduled) {
|
|
162
|
-
contextCacheFlushScheduled = true;
|
|
163
|
-
setTimeout(() => {
|
|
164
|
-
for (const queuedKind of contextKinds) {
|
|
165
|
-
for (const queuedInstance of dirtyContexts[queuedKind]) contextCache[queuedKind].delete(queuedInstance);
|
|
166
|
-
dirtyContexts[queuedKind].clear();
|
|
167
|
-
}
|
|
168
|
-
contextCacheFlushScheduled = false;
|
|
169
|
-
}, 0);
|
|
170
|
-
}
|
|
171
|
-
return context;
|
|
172
|
-
}
|
|
173
|
-
//#endregion
|
|
174
|
-
//#region src/internal/symbols.ts
|
|
175
|
-
const sigmaStateBrand = Symbol("sigma.state");
|
|
176
|
-
const reservedKeys = new Set([
|
|
177
|
-
"act",
|
|
178
|
-
"get",
|
|
179
|
-
"emit",
|
|
180
|
-
"commit",
|
|
181
|
-
"on",
|
|
182
|
-
"setup"
|
|
183
|
-
]);
|
|
184
|
-
//#endregion
|
|
185
|
-
//#region src/internal/runtime.ts
|
|
186
|
-
const sigmaInternalsMap = /* @__PURE__ */ new WeakMap();
|
|
187
|
-
let autoFreezeEnabled = true;
|
|
188
|
-
let nextActionOwnerId = 1;
|
|
189
|
-
let currentDraftOwner;
|
|
190
|
-
function registerSigmaInternals(context, instance) {
|
|
191
|
-
sigmaInternalsMap.set(context, instance);
|
|
192
|
-
}
|
|
193
|
-
function getSigmaInternals(context) {
|
|
194
|
-
const instance = sigmaInternalsMap.get(context);
|
|
195
|
-
if (!instance) throw new Error("[preact-sigma] Invalid sigma context");
|
|
196
|
-
return instance;
|
|
197
|
-
}
|
|
198
|
-
function isAutoFreeze() {
|
|
199
|
-
return autoFreezeEnabled;
|
|
200
|
-
}
|
|
201
|
-
/** Controls whether sigma deep-freezes published public state. Auto-freezing starts enabled and the setting is shared across instances. */
|
|
202
|
-
function setAutoFreeze(autoFreeze) {
|
|
203
|
-
autoFreezeEnabled = autoFreeze;
|
|
204
|
-
immer.setAutoFreeze(autoFreeze);
|
|
205
|
-
}
|
|
206
|
-
function isPlainObject(value) {
|
|
207
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
208
|
-
const prototype = Object.getPrototypeOf(value);
|
|
209
|
-
return prototype === Object.prototype || prototype === null;
|
|
210
|
-
}
|
|
211
|
-
function getSignal(instance, key) {
|
|
212
|
-
return instance.publicInstance["#" + key];
|
|
213
|
-
}
|
|
214
|
-
function initializeSigmaInstance(publicInstance, type, initialState) {
|
|
215
|
-
const stateKeys = new Set(type._defaultStateKeys);
|
|
216
|
-
if (initialState) for (const key in initialState) stateKeys.add(key);
|
|
217
|
-
const instance = {
|
|
218
|
-
currentSetupCleanup: void 0,
|
|
219
|
-
publicInstance,
|
|
220
|
-
stateKeys,
|
|
221
|
-
type,
|
|
222
|
-
disposed: false
|
|
223
|
-
};
|
|
224
|
-
for (const key of stateKeys) {
|
|
225
|
-
if (reservedKeys.has(key)) throw new Error(`[preact-sigma] Reserved property name: ${key}`);
|
|
226
|
-
let value = initialState?.[key];
|
|
227
|
-
if (value === void 0) value = typeof type._defaultState[key] === "function" ? type._defaultState[key].call(void 0) : type._defaultState[key];
|
|
228
|
-
const container = signal(value);
|
|
229
|
-
Object.defineProperty(publicInstance, "#" + key, { value: container });
|
|
230
|
-
Object.defineProperty(publicInstance, key, {
|
|
231
|
-
get: () => container.value,
|
|
232
|
-
enumerable: true
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
for (const key in type._computeFunctions) Object.defineProperty(publicInstance, "#" + key, { value: computed$1(() => type._computeFunctions[key].call(getContext(instance, "computedReadonly"))) });
|
|
236
|
-
registerSigmaInternals(publicInstance, instance);
|
|
237
|
-
}
|
|
238
|
-
function buildQueryMethod(queryFunction) {
|
|
239
|
-
return function(...args) {
|
|
240
|
-
const instance = getSigmaInternals(this);
|
|
241
|
-
const owner = getContextOwner(this);
|
|
242
|
-
if (owner) return queryFunction.apply(getContext(owner, "queryDraftAware"), args);
|
|
243
|
-
return computed$1(() => queryFunction.apply(getContext(instance, "queryCommitted"), args)).value;
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
function buildActionMethod(actionName, actionFn) {
|
|
247
|
-
return function(...args) {
|
|
248
|
-
return runActionInvocation(this, actionName, actionFn, args);
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
function runAdHocAction(context, actionFn) {
|
|
252
|
-
return runActionInvocation(context, "act()", actionFn, []);
|
|
253
|
-
}
|
|
254
|
-
function readActionStateValue(owner, key, options) {
|
|
255
|
-
if (owner.currentDraft) return owner.currentDraft[key];
|
|
256
|
-
const signal = getSignal(owner.instance, key);
|
|
257
|
-
const committedValue = options.reactiveReads ? signal.value : signal.peek();
|
|
258
|
-
if (options.draftOnRead && immer.isDraftable(committedValue)) return ensureOwnerDraft(owner)[key];
|
|
259
|
-
return committedValue;
|
|
260
|
-
}
|
|
261
|
-
function readActionComputedValue(owner, key) {
|
|
262
|
-
return owner.instance.type._computeFunctions[key].call(getContext(owner, "computedDraftAware"));
|
|
263
|
-
}
|
|
264
|
-
function setActionStateValue(owner, key, value) {
|
|
265
|
-
ensureOwnerDraft(owner)[key] = value;
|
|
266
|
-
}
|
|
267
|
-
function commitActionOwner(owner) {
|
|
268
|
-
const finalized = finalizeOwnerDraft(owner);
|
|
269
|
-
if (finalized?.changed) publishState(owner.instance, finalized);
|
|
270
|
-
}
|
|
271
|
-
function handleActionBoundary(owner, boundary, actionName) {
|
|
272
|
-
const draftOwner = currentDraftOwner;
|
|
273
|
-
if (!draftOwner?.currentDraft) return;
|
|
274
|
-
if (!finalizeOwnerDraft(draftOwner)?.changed) return;
|
|
275
|
-
if (draftOwner === owner) {
|
|
276
|
-
const message = boundary === "emit" ? `[preact-sigma] Action "${draftOwner.actionName}" has unpublished changes. Call this.commit() before emit().` : `[preact-sigma] Action "${draftOwner.actionName}" has unpublished changes. Call this.commit() before calling another action.`;
|
|
277
|
-
throw new Error(message);
|
|
278
|
-
}
|
|
279
|
-
if (boundary === "emit") throw new Error("[preact-sigma] Unexpected emit boundary. This is a bug.");
|
|
280
|
-
console.warn(`[preact-sigma] Discarded unpublished action changes from "${draftOwner.actionName}" before running "${actionName ?? "another action"}".`, createDraftMetadata(draftOwner));
|
|
281
|
-
}
|
|
282
|
-
function assertDefinitionKeyAvailable(builder, key, kind) {
|
|
283
|
-
if (reservedKeys.has(key)) throw new Error(`[preact-sigma] Reserved property name: ${key}`);
|
|
284
|
-
if (key in builder._computeFunctions || key in builder._queryFunctions || key in builder._actionFunctions) throw new Error(`[preact-sigma] Duplicate key for ${kind}: ${key}`);
|
|
285
|
-
}
|
|
286
|
-
function shouldSetup(publicInstance) {
|
|
287
|
-
return getSigmaInternals(publicInstance).type._setupFunction !== null;
|
|
288
|
-
}
|
|
289
|
-
function clearCurrentDraft(owner) {
|
|
290
|
-
owner.currentDraft = void 0;
|
|
291
|
-
owner.currentBase = void 0;
|
|
292
|
-
if (currentDraftOwner === owner) currentDraftOwner = void 0;
|
|
293
|
-
}
|
|
294
|
-
function createActionOwner(instance, actionName, actionFn, args) {
|
|
295
|
-
const owner = {
|
|
296
|
-
actionFn,
|
|
297
|
-
actionName,
|
|
298
|
-
args,
|
|
299
|
-
id: nextActionOwnerId++,
|
|
300
|
-
instance,
|
|
301
|
-
publicInstance: instance.publicInstance
|
|
302
|
-
};
|
|
303
|
-
owner.actionContext = getContext(owner, "action");
|
|
304
|
-
registerSigmaInternals(owner.actionContext, instance);
|
|
305
|
-
registerContextOwner(owner.actionContext, owner);
|
|
306
|
-
return owner;
|
|
307
|
-
}
|
|
308
|
-
function runActionInvocation(context, actionName, actionFn, args) {
|
|
309
|
-
const instance = getSigmaInternals(context);
|
|
310
|
-
if (instance.disposed) throw new Error("[preact-sigma] Cannot run an action on a disposed sigma state");
|
|
311
|
-
const isAdHocAction = actionName === "act()";
|
|
312
|
-
const actionIsAsync = isAsyncFunction(actionFn);
|
|
313
|
-
if (actionIsAsync && isAdHocAction) throw new Error("[preact-sigma] act() callbacks must stay synchronous");
|
|
314
|
-
return untracked$1(() => {
|
|
315
|
-
let owner;
|
|
316
|
-
const callerOwner = getContextOwner(context);
|
|
317
|
-
if (callerOwner && callerOwner.instance === instance && !actionIsAsync) owner = callerOwner;
|
|
318
|
-
else {
|
|
319
|
-
handleActionBoundary(callerOwner, "action", actionName);
|
|
320
|
-
owner = createActionOwner(instance, actionName, actionFn, args);
|
|
321
|
-
}
|
|
322
|
-
let result;
|
|
323
|
-
try {
|
|
324
|
-
result = actionFn.apply(owner.actionContext, args);
|
|
325
|
-
} catch (error) {
|
|
326
|
-
clearCurrentDraft(owner);
|
|
327
|
-
throw error;
|
|
328
|
-
}
|
|
329
|
-
if (isAdHocAction && isPromiseLike(result)) {
|
|
330
|
-
clearCurrentDraft(owner);
|
|
331
|
-
Promise.resolve(result).catch(() => {});
|
|
332
|
-
throw new Error("[preact-sigma] act() callbacks must stay synchronous");
|
|
333
|
-
}
|
|
334
|
-
if (!actionIsAsync && isPromiseLike(result)) {
|
|
335
|
-
clearCurrentDraft(owner);
|
|
336
|
-
Promise.resolve(result).catch(() => {});
|
|
337
|
-
throw new Error(`[preact-sigma] Action "${actionName}" must use native async-await syntax to return a promise.`);
|
|
338
|
-
}
|
|
339
|
-
if (owner === callerOwner) return result;
|
|
340
|
-
const finalized = finalizeOwnerDraft(owner);
|
|
341
|
-
if (finalized?.changed) publishState(instance, finalized);
|
|
342
|
-
if (isPromiseLike(result)) return resolveAsyncActionResult(owner, result);
|
|
343
|
-
return result;
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
function createCommitError(owner) {
|
|
347
|
-
return /* @__PURE__ */ new Error(`[preact-sigma] Async action "${owner.actionName}" finished with unpublished changes. Call this.commit() before await or return.`);
|
|
348
|
-
}
|
|
349
|
-
function createDraftMetadata(owner) {
|
|
350
|
-
return {
|
|
351
|
-
action: owner.actionFn,
|
|
352
|
-
actionArgs: owner.args,
|
|
353
|
-
actionId: owner.id,
|
|
354
|
-
actionName: owner.actionName,
|
|
355
|
-
draftedInstance: currentDraftOwner?.publicInstance ?? owner.publicInstance,
|
|
356
|
-
instance: owner.instance.publicInstance
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
function disposeCleanupResource(resource) {
|
|
360
|
-
if (typeof resource === "function") resource();
|
|
361
|
-
else if (resource instanceof AbortController) resource.abort();
|
|
362
|
-
else if ("dispose" in resource) resource.dispose();
|
|
363
|
-
else resource[Symbol.dispose]();
|
|
364
|
-
}
|
|
365
|
-
function assertExactStateKeys(instance, nextState) {
|
|
366
|
-
const extraKeys = Object.keys(nextState).filter((key) => !instance.stateKeys.has(key));
|
|
367
|
-
const missingKeys = [...instance.stateKeys].filter((key) => !Object.prototype.hasOwnProperty.call(nextState, key));
|
|
368
|
-
if (!extraKeys.length && !missingKeys.length) return;
|
|
369
|
-
let message = "[preact-sigma] replaceState() requires exactly the instance's state keys";
|
|
370
|
-
if (missingKeys.length) message += `. Missing: ${missingKeys.join(", ")}`;
|
|
371
|
-
if (extraKeys.length) message += `. Extra: ${extraKeys.join(", ")}`;
|
|
372
|
-
throw new Error(message);
|
|
373
|
-
}
|
|
374
|
-
function assertNoPendingDraft(operationName) {
|
|
375
|
-
const owner = currentDraftOwner;
|
|
376
|
-
if (!owner?.currentDraft) return;
|
|
377
|
-
throw new Error(`[preact-sigma] ${operationName}() cannot run while action "${owner.actionName}" has unpublished changes. Call this.commit() before ${operationName}().`);
|
|
378
|
-
}
|
|
379
|
-
function snapshotState(instance) {
|
|
380
|
-
const snapshot = Object.create(null);
|
|
381
|
-
for (const key of instance.stateKeys) snapshot[key] = getSignal(instance, key).peek();
|
|
382
|
-
return snapshot;
|
|
383
|
-
}
|
|
384
|
-
function ensureOwnerDraft(owner) {
|
|
385
|
-
if (owner.currentDraft) return owner.currentDraft;
|
|
386
|
-
handleActionBoundary(owner, "action", owner.actionName);
|
|
387
|
-
owner.currentBase = snapshotState(owner.instance);
|
|
388
|
-
owner.currentDraft = immer.createDraft(owner.currentBase);
|
|
389
|
-
currentDraftOwner = owner;
|
|
390
|
-
return owner.currentDraft;
|
|
391
|
-
}
|
|
392
|
-
function finalizeOwnerDraft(owner) {
|
|
393
|
-
const currentDraft = owner.currentDraft;
|
|
394
|
-
const oldState = owner.currentBase;
|
|
395
|
-
if (!currentDraft || !oldState) return;
|
|
396
|
-
clearCurrentDraft(owner);
|
|
397
|
-
let patches;
|
|
398
|
-
let inversePatches;
|
|
399
|
-
const newState = owner.instance.type._patchesEnabled ? immer.finishDraft(currentDraft, (nextPatches, nextInversePatches) => {
|
|
400
|
-
patches = nextPatches;
|
|
401
|
-
inversePatches = nextInversePatches;
|
|
402
|
-
}) : immer.finishDraft(currentDraft);
|
|
403
|
-
return {
|
|
404
|
-
changed: newState !== oldState,
|
|
405
|
-
inversePatches,
|
|
406
|
-
newState,
|
|
407
|
-
oldState,
|
|
408
|
-
patches
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
function finalizeReplacementState(instance, oldState, nextState) {
|
|
412
|
-
const draft = immer.createDraft(oldState);
|
|
413
|
-
for (const key of instance.stateKeys) draft[key] = nextState[key];
|
|
414
|
-
let patches;
|
|
415
|
-
let inversePatches;
|
|
416
|
-
const newState = instance.type._patchesEnabled ? immer.finishDraft(draft, (nextPatches, nextInversePatches) => {
|
|
417
|
-
patches = nextPatches;
|
|
418
|
-
inversePatches = nextInversePatches;
|
|
419
|
-
}) : immer.finishDraft(draft);
|
|
420
|
-
return {
|
|
421
|
-
changed: newState !== oldState,
|
|
422
|
-
inversePatches,
|
|
423
|
-
newState,
|
|
424
|
-
oldState,
|
|
425
|
-
patches
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
function isAsyncFunction(fn) {
|
|
429
|
-
return fn.constructor.name === "AsyncFunction";
|
|
430
|
-
}
|
|
431
|
-
function isPromiseLike(value) {
|
|
432
|
-
return value != null && typeof value.then === "function";
|
|
433
|
-
}
|
|
434
|
-
function publishState(instance, finalized) {
|
|
435
|
-
batch$1(() => {
|
|
436
|
-
for (const key of instance.stateKeys) {
|
|
437
|
-
const nextValue = finalized.newState[key];
|
|
438
|
-
if (isAutoFreeze()) immer.freeze(nextValue, true);
|
|
439
|
-
const signal = getSignal(instance, key);
|
|
440
|
-
signal.value = nextValue;
|
|
441
|
-
}
|
|
442
|
-
});
|
|
443
|
-
instance.type._observeFunction?.call(getContext(instance, "observe"), finalized);
|
|
444
|
-
}
|
|
445
|
-
/**
|
|
446
|
-
* Returns a shallow snapshot of an instance's committed public state.
|
|
447
|
-
*
|
|
448
|
-
* The snapshot includes one own property for each top-level state key and reads
|
|
449
|
-
* the current committed value for that key. Nested sigma states remain as
|
|
450
|
-
* referenced values. Its type is inferred from the instance's sigma-state
|
|
451
|
-
* definition.
|
|
452
|
-
*/
|
|
453
|
-
function snapshot(publicInstance) {
|
|
454
|
-
return snapshotState(getSigmaInternals(publicInstance));
|
|
455
|
-
}
|
|
456
|
-
/**
|
|
457
|
-
* Replaces an instance's committed public state from a snapshot object.
|
|
458
|
-
*
|
|
459
|
-
* The replacement snapshot must be a plain object with exactly the instance's
|
|
460
|
-
* top-level state keys. It updates committed state outside action semantics and
|
|
461
|
-
* notifies observers when the committed state changes. Its type is inferred
|
|
462
|
-
* from the instance's sigma-state definition.
|
|
463
|
-
*/
|
|
464
|
-
function replaceState(publicInstance, nextState) {
|
|
465
|
-
const instance = getSigmaInternals(publicInstance);
|
|
466
|
-
if (!isPlainObject(nextState)) throw new Error("[preact-sigma] replaceState() requires a plain object snapshot");
|
|
467
|
-
assertNoPendingDraft("replaceState");
|
|
468
|
-
assertExactStateKeys(instance, nextState);
|
|
469
|
-
const finalized = finalizeReplacementState(instance, snapshot(publicInstance), nextState);
|
|
470
|
-
if (finalized.changed) publishState(instance, finalized);
|
|
471
|
-
}
|
|
472
|
-
async function resolveAsyncActionResult(owner, result) {
|
|
473
|
-
let settledValue;
|
|
474
|
-
let settledError;
|
|
475
|
-
let rejected = false;
|
|
476
|
-
try {
|
|
477
|
-
settledValue = await result;
|
|
478
|
-
} catch (error) {
|
|
479
|
-
rejected = true;
|
|
480
|
-
settledError = error;
|
|
481
|
-
}
|
|
482
|
-
if (currentDraftOwner === owner && owner.currentDraft) {
|
|
483
|
-
if (finalizeOwnerDraft(owner)?.changed) {
|
|
484
|
-
const commitError = createCommitError(owner);
|
|
485
|
-
if (rejected) throw new AggregateError([settledError, commitError], `[preact-sigma] Async action "${owner.actionName}" rejected and left unpublished changes`);
|
|
486
|
-
throw commitError;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
if (rejected) throw settledError;
|
|
490
|
-
return settledValue;
|
|
491
|
-
}
|
|
492
|
-
var Sigma = class extends EventTarget {
|
|
493
|
-
setup(...args) {
|
|
494
|
-
const instance = getSigmaInternals(this);
|
|
495
|
-
if (!instance.type._setupFunction) throw new Error("[preact-sigma] Setup is undefined for this sigma state");
|
|
496
|
-
if (instance.disposed) throw new Error("[preact-sigma] Cannot set up a disposed sigma state");
|
|
497
|
-
instance.currentSetupCleanup?.();
|
|
498
|
-
instance.currentSetupCleanup = void 0;
|
|
499
|
-
const resources = instance.type._setupFunction.apply(getContext(instance, "setup"), args);
|
|
500
|
-
if (!Array.isArray(resources)) throw new Error("[preact-sigma] Sigma setup handlers must return an array");
|
|
501
|
-
let cleanup;
|
|
502
|
-
if (resources.length) {
|
|
503
|
-
let cleaned = false;
|
|
504
|
-
cleanup = () => {
|
|
505
|
-
if (instance.currentSetupCleanup === cleanup) instance.currentSetupCleanup = void 0;
|
|
506
|
-
if (cleaned) return;
|
|
507
|
-
cleaned = true;
|
|
508
|
-
let errors;
|
|
509
|
-
for (let index = resources.length - 1; index >= 0; index -= 1) try {
|
|
510
|
-
disposeCleanupResource(resources[index]);
|
|
511
|
-
} catch (error) {
|
|
512
|
-
errors ||= [];
|
|
513
|
-
errors.push(error);
|
|
514
|
-
}
|
|
515
|
-
if (errors) throw new AggregateError(errors, "Failed to dispose one or more sigma resources");
|
|
516
|
-
};
|
|
517
|
-
instance.currentSetupCleanup = cleanup;
|
|
518
|
-
} else cleanup = () => {};
|
|
519
|
-
return cleanup;
|
|
520
|
-
}
|
|
521
|
-
get(key) {
|
|
522
|
-
return getSignal(getSigmaInternals(this), key);
|
|
523
|
-
}
|
|
524
|
-
on(name, listener) {
|
|
525
|
-
const adapter = (event) => {
|
|
526
|
-
const payload = event.detail;
|
|
527
|
-
if (payload === void 0) listener();
|
|
528
|
-
else listener(payload);
|
|
529
|
-
};
|
|
530
|
-
this.addEventListener(name, adapter);
|
|
531
|
-
return () => {
|
|
532
|
-
this.removeEventListener(name, adapter);
|
|
533
|
-
};
|
|
534
|
-
}
|
|
535
|
-
};
|
|
536
|
-
Object.defineProperty(Sigma.prototype, sigmaStateBrand, { value: true });
|
|
537
|
-
//#endregion
|
|
538
5
|
//#region src/framework.ts
|
|
539
6
|
/** Checks whether a value is an instance created by a configured sigma type. */
|
|
540
7
|
function isSigmaState(value) {
|
|
@@ -548,11 +15,11 @@ function isSigmaState(value) {
|
|
|
548
15
|
* that do not need to live on the sigma-state instance.
|
|
549
16
|
*/
|
|
550
17
|
function query(fn) {
|
|
551
|
-
return ((...args) => computed
|
|
18
|
+
return ((...args) => computed(() => fn(...args)).value);
|
|
552
19
|
}
|
|
553
20
|
/**
|
|
554
21
|
* Builds sigma-state constructors by accumulating default state, computeds,
|
|
555
|
-
* queries,
|
|
22
|
+
* queries, actions, and setup handlers.
|
|
556
23
|
*
|
|
557
24
|
* State and event inference starts from `new SigmaType<TState, TEvents>()`.
|
|
558
25
|
* Later builder methods infer names and types from the objects you pass to them.
|
|
@@ -565,8 +32,6 @@ var SigmaType = class SigmaType extends Function {
|
|
|
565
32
|
static _computeFunctions = Object.create(null);
|
|
566
33
|
static _defaultState = Object.create(null);
|
|
567
34
|
static _defaultStateKeys = [];
|
|
568
|
-
static _observeFunction = null;
|
|
569
|
-
static _patchesEnabled = false;
|
|
570
35
|
static _queryFunctions = Object.create(null);
|
|
571
36
|
static _setupFunction = null;
|
|
572
37
|
constructor(initialState) {
|
|
@@ -626,12 +91,6 @@ var SigmaType = class SigmaType extends Function {
|
|
|
626
91
|
}
|
|
627
92
|
return this;
|
|
628
93
|
}
|
|
629
|
-
observe(listener, options) {
|
|
630
|
-
const type = getTypeInternals(this);
|
|
631
|
-
type._observeFunction = listener;
|
|
632
|
-
if (options?.patches) type._patchesEnabled = true;
|
|
633
|
-
return this;
|
|
634
|
-
}
|
|
635
94
|
/**
|
|
636
95
|
* Adds action methods whose `this` receives draft state, typed events, `commit()`,
|
|
637
96
|
* and the computeds, queries, and actions already defined on the builder.
|
|
@@ -669,44 +128,6 @@ function getTypeInternals(type) {
|
|
|
669
128
|
return type;
|
|
670
129
|
}
|
|
671
130
|
//#endregion
|
|
672
|
-
//#region src/listener.ts
|
|
673
|
-
/**
|
|
674
|
-
* A standalone typed event hub with `emit(...)` and `on(...)` methods and full
|
|
675
|
-
* `EventTarget`, `listen(...)`, and `useListener(...)` compatibility.
|
|
676
|
-
*/
|
|
677
|
-
var SigmaTarget = class extends EventTarget {
|
|
678
|
-
/**
|
|
679
|
-
* Emits a typed event from the hub.
|
|
680
|
-
*
|
|
681
|
-
* Void events dispatch a plain `Event`. Payload events dispatch a
|
|
682
|
-
* `CustomEvent` whose `detail` holds the payload.
|
|
683
|
-
*/
|
|
684
|
-
emit(name, ...[detail]) {
|
|
685
|
-
this.dispatchEvent(detail === void 0 ? new Event(name) : new CustomEvent(name, { detail }));
|
|
686
|
-
}
|
|
687
|
-
/**
|
|
688
|
-
* Registers a typed event listener and returns an unsubscribe function.
|
|
689
|
-
*
|
|
690
|
-
* Payload events pass their payload directly to the listener. Void events
|
|
691
|
-
* call the listener with no arguments.
|
|
692
|
-
*/
|
|
693
|
-
on(name, listener) {
|
|
694
|
-
const adapter = (event) => listener(event.detail);
|
|
695
|
-
this.addEventListener(name, adapter);
|
|
696
|
-
return () => {
|
|
697
|
-
this.removeEventListener(name, adapter);
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
};
|
|
701
|
-
/** Adds a listener to a sigma state or DOM target and returns a cleanup function that removes it. */
|
|
702
|
-
function listen(target, name, listener) {
|
|
703
|
-
const adapter = isSigmaState(target) || target instanceof SigmaTarget ? (event) => listener(event.detail) : listener;
|
|
704
|
-
target.addEventListener(name, adapter);
|
|
705
|
-
return () => {
|
|
706
|
-
target.removeEventListener(name, adapter);
|
|
707
|
-
};
|
|
708
|
-
}
|
|
709
|
-
//#endregion
|
|
710
131
|
//#region src/hooks.ts
|
|
711
132
|
/**
|
|
712
133
|
* Creates one sigma-state instance for a component.
|
|
@@ -738,4 +159,4 @@ function useListener(target, name, listener) {
|
|
|
738
159
|
}, [target, name]);
|
|
739
160
|
}
|
|
740
161
|
//#endregion
|
|
741
|
-
export { SigmaTarget, SigmaType, action,
|
|
162
|
+
export { SigmaListenerMap, SigmaTarget, SigmaType, action, effect, immerable, isSigmaState, listen, query, setAutoFreeze, sigma, useListener, useSigma };
|