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