preact-sigma 6.0.0 → 6.0.2
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/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/persist.d.mts +1 -1
- package/dist/persist.mjs +1 -1
- package/dist/{sigma-DD7HfTvw.d.mts → sigma-BhmvUxp9.d.mts} +7 -1
- package/dist/{sigma-CJibGQ6g.mjs → sigma-tZ0xhlG3.mjs} +165 -86
- package/docs/context.md +3 -0
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as SigmaState, c as query, d as Draft, f as Immutable, i as SigmaRef, l as setAutoFreeze, m as Cleanup, n as Sigma, o as SigmaTarget, p as typeSymbol, r as SigmaDefinition, s as castProtected, t as Protected, u as sigma } from "./sigma-
|
|
1
|
+
import { a as SigmaState, c as query, d as Draft, f as Immutable, i as SigmaRef, l as setAutoFreeze, m as Cleanup, n as Sigma, o as SigmaTarget, p as typeSymbol, r as SigmaDefinition, s as castProtected, t as Protected, u as sigma } from "./sigma-BhmvUxp9.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/defaults.d.ts
|
|
4
4
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as setAutoFreeze, c as listenersSymbol, i as query, n as SigmaTarget, o as sigma, r as castProtected, t as Sigma } from "./sigma-
|
|
1
|
+
import { a as setAutoFreeze, c as listenersSymbol, i as query, n as SigmaTarget, o as sigma, r as castProtected, t as Sigma } from "./sigma-tZ0xhlG3.mjs";
|
|
2
2
|
import { useEffect, useRef } from "preact/hooks";
|
|
3
3
|
//#region src/defaults.ts
|
|
4
4
|
/**
|
package/dist/persist.d.mts
CHANGED
package/dist/persist.mjs
CHANGED
|
@@ -88,6 +88,8 @@ type SigmaState<T extends SigmaDefinition> = Sigma<T["state"]> & {
|
|
|
88
88
|
* Base class for signal-backed state models.
|
|
89
89
|
*
|
|
90
90
|
* `TState` is the source of typing for top-level state keys, subscriptions, signals, and replacement snapshots.
|
|
91
|
+
* Private class fields stay ordinary instance storage and are not signal-backed, captured,
|
|
92
|
+
* persisted, or used for reactive invalidation by themselves.
|
|
91
93
|
* Merge a same-named interface with the class when direct property reads should be typed on the instance.
|
|
92
94
|
*/
|
|
93
95
|
declare abstract class Sigma<TState extends object> {
|
|
@@ -145,7 +147,11 @@ declare const sigma: Readonly<{
|
|
|
145
147
|
captureState<TState extends object>(instance: Sigma<TState>): Immutable<TState>; /** Publishes a plain-object snapshot as the current committed state. */
|
|
146
148
|
replaceState<TState extends object>(target: Sigma<TState>, nextState: TState): void;
|
|
147
149
|
}>;
|
|
148
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* Marks a class method as a committed-state reactive read with arguments instead of an action.
|
|
152
|
+
*
|
|
153
|
+
* Each call creates a reactive read at the call site. Query calls do not memoize results across invocations.
|
|
154
|
+
*/
|
|
149
155
|
declare function query<TThis extends object, TArgs extends any[], TReturn>(method: (this: TThis, ...args: TArgs) => TReturn): (this: TThis, ...args: TArgs) => TReturn;
|
|
150
156
|
declare const protectedSymbol: unique symbol;
|
|
151
157
|
type ProtectedKey = typeof listenersSymbol | typeof snapshotSymbol | "act" | "commit" | "emit" | "onSetup";
|
|
@@ -59,21 +59,54 @@ const patchListeners = /* @__PURE__ */ new WeakSet();
|
|
|
59
59
|
const initializedPrototypes = /* @__PURE__ */ new WeakSet();
|
|
60
60
|
const queries = /* @__PURE__ */ new WeakSet();
|
|
61
61
|
const emptySentinel = {};
|
|
62
|
-
let
|
|
62
|
+
let activeActionInstance = null;
|
|
63
|
+
let activeDraftInstance = null;
|
|
63
64
|
let activeDraft;
|
|
64
65
|
let activeDerivedReadDepth = 0;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
let activeSetupInstance = null;
|
|
67
|
+
const pendingAsyncActions = /* @__PURE__ */ new WeakMap();
|
|
68
|
+
function hasPendingAsyncAction(instance) {
|
|
69
|
+
return pendingAsyncActions.has(instance);
|
|
70
|
+
}
|
|
71
|
+
function addPendingAsyncAction(instance) {
|
|
72
|
+
pendingAsyncActions.set(instance, (pendingAsyncActions.get(instance) ?? 0) + 1);
|
|
73
|
+
}
|
|
74
|
+
function removePendingAsyncAction(instance) {
|
|
75
|
+
const count = pendingAsyncActions.get(instance);
|
|
76
|
+
if (!count) return;
|
|
77
|
+
if (count === 1) pendingAsyncActions.delete(instance);
|
|
78
|
+
else pendingAsyncActions.set(instance, count - 1);
|
|
79
|
+
}
|
|
80
|
+
function hasActionContext(instance) {
|
|
81
|
+
if (activeActionInstance) return activeActionInstance === instance;
|
|
82
|
+
return hasPendingAsyncAction(instance);
|
|
83
|
+
}
|
|
84
|
+
function createExternalActionError() {
|
|
85
|
+
const constructorName = (activeDraftInstance ?? activeActionInstance)?.constructor.name;
|
|
86
|
+
const owner = constructorName ? `Draft for ${constructorName}` : "Draft";
|
|
87
|
+
return /* @__PURE__ */ new Error(`[preact-sigma] ${owner} was not committed before an external action was invoked.`);
|
|
88
|
+
}
|
|
89
|
+
function beginActionContext(instance) {
|
|
90
|
+
if (!activeActionInstance) {
|
|
91
|
+
if (activeDraftInstance && activeDraftInstance !== instance) throw createExternalActionError();
|
|
92
|
+
activeActionInstance = instance;
|
|
68
93
|
return false;
|
|
69
94
|
}
|
|
70
|
-
if (instance !==
|
|
95
|
+
if (instance !== activeActionInstance) throw createExternalActionError();
|
|
71
96
|
return true;
|
|
72
97
|
}
|
|
73
|
-
function
|
|
74
|
-
|
|
98
|
+
function endActionContext(instance) {
|
|
99
|
+
if (activeActionInstance === instance) activeActionInstance = null;
|
|
100
|
+
}
|
|
101
|
+
function clearActiveAction() {
|
|
102
|
+
activeActionInstance = null;
|
|
103
|
+
activeDraftInstance = null;
|
|
75
104
|
activeDraft = null;
|
|
76
105
|
}
|
|
106
|
+
function assertActionContext(instance, message) {
|
|
107
|
+
if (activeActionInstance && activeActionInstance !== instance) throw createExternalActionError();
|
|
108
|
+
if (!hasActionContext(instance)) throw new Error(message);
|
|
109
|
+
}
|
|
77
110
|
function isStateKey(instance, key) {
|
|
78
111
|
return Object.hasOwn(instance, key + signalSuffix);
|
|
79
112
|
}
|
|
@@ -83,12 +116,30 @@ function getStateSignal(instance, key) {
|
|
|
83
116
|
function createSnapshot(instance) {
|
|
84
117
|
if (instance[snapshotSymbol]) return instance[snapshotSymbol];
|
|
85
118
|
const state = {};
|
|
86
|
-
for (const key in instance) if (isStateKey(instance, key)) state[key] = instance
|
|
119
|
+
for (const key in instance) if (isStateKey(instance, key)) state[key] = getStateSignal(instance, key).value;
|
|
87
120
|
return state;
|
|
88
121
|
}
|
|
89
122
|
function createDraft(instance) {
|
|
90
123
|
return createDraft$1(createSnapshot(instance));
|
|
91
124
|
}
|
|
125
|
+
function ensureDraft(instance) {
|
|
126
|
+
if (activeDraftInstance && activeDraftInstance !== instance) throw createExternalActionError();
|
|
127
|
+
activeDraftInstance = instance;
|
|
128
|
+
activeDraft ??= createDraft(instance);
|
|
129
|
+
return activeDraft;
|
|
130
|
+
}
|
|
131
|
+
function readStateProperty(instance, key) {
|
|
132
|
+
const signal = getStateSignal(instance, key);
|
|
133
|
+
if (!activeDerivedReadDepth && hasActionContext(instance)) {
|
|
134
|
+
if (activeDraftInstance === instance) return activeDraft[key];
|
|
135
|
+
if (isDraftable(signal.value)) return ensureDraft(instance)[key];
|
|
136
|
+
}
|
|
137
|
+
return signal.value;
|
|
138
|
+
}
|
|
139
|
+
function writeStateProperty(instance, key, value) {
|
|
140
|
+
assertActionContext(instance, `[preact-sigma] Cannot set state property "${key}" outside an action.`);
|
|
141
|
+
ensureDraft(instance)[key] = value;
|
|
142
|
+
}
|
|
92
143
|
function runDerivedRead(callback) {
|
|
93
144
|
activeDerivedReadDepth += 1;
|
|
94
145
|
try {
|
|
@@ -127,44 +178,15 @@ function publishState(instance, nextState, baseState, patches, inversePatches) {
|
|
|
127
178
|
listener(nextState, baseState, patches, inversePatches);
|
|
128
179
|
});
|
|
129
180
|
}
|
|
130
|
-
function createActionContext(instance) {
|
|
131
|
-
return new Proxy(instance, {
|
|
132
|
-
get(target, key, receiver) {
|
|
133
|
-
if (typeof key === "string" && isStateKey(target, key)) {
|
|
134
|
-
if (activeDraft) {
|
|
135
|
-
ensureActiveInstance(target);
|
|
136
|
-
return activeDraft[key];
|
|
137
|
-
}
|
|
138
|
-
const { value } = getStateSignal(target, key);
|
|
139
|
-
if (isDraftable(value)) {
|
|
140
|
-
activeDraft = createDraft(instance);
|
|
141
|
-
return activeDraft[key];
|
|
142
|
-
}
|
|
143
|
-
return value;
|
|
144
|
-
}
|
|
145
|
-
if (key === instanceSymbol) return instance;
|
|
146
|
-
return Reflect.get(target, key, receiver);
|
|
147
|
-
},
|
|
148
|
-
set(target, key, value) {
|
|
149
|
-
if (typeof key === "string" && isStateKey(target, key)) {
|
|
150
|
-
ensureActiveInstance(instance);
|
|
151
|
-
activeDraft ??= createDraft(instance);
|
|
152
|
-
activeDraft[key] = value;
|
|
153
|
-
return true;
|
|
154
|
-
}
|
|
155
|
-
return Reflect.set(target, key, value);
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
181
|
function getActionInstance(context) {
|
|
160
182
|
return context[instanceSymbol];
|
|
161
183
|
}
|
|
162
|
-
function
|
|
163
|
-
if (
|
|
164
|
-
activeInstance = null;
|
|
184
|
+
function commitDraft(instance) {
|
|
185
|
+
if (activeDraftInstance && instance !== activeDraftInstance) throw createExternalActionError();
|
|
165
186
|
if (!activeDraft) return false;
|
|
166
187
|
const draft = activeDraft;
|
|
167
188
|
activeDraft = null;
|
|
189
|
+
activeDraftInstance = null;
|
|
168
190
|
let patches;
|
|
169
191
|
let inversePatches;
|
|
170
192
|
let patchListener;
|
|
@@ -178,6 +200,32 @@ function ensureDraftCommitted(instance) {
|
|
|
178
200
|
if (changed) publishState(instance, nextState, baseState, patches, inversePatches);
|
|
179
201
|
return changed;
|
|
180
202
|
}
|
|
203
|
+
function hasStateChanges(baseState, nextState) {
|
|
204
|
+
const baseKeys = Object.keys(baseState);
|
|
205
|
+
const nextKeys = Object.keys(nextState);
|
|
206
|
+
if (baseKeys.length !== nextKeys.length) return true;
|
|
207
|
+
for (const key of nextKeys) if (!Object.hasOwn(baseState, key) || !Object.is(baseState[key], nextState[key])) return true;
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
function createReplacementPatches(baseState, nextState) {
|
|
211
|
+
let patches;
|
|
212
|
+
let inversePatches;
|
|
213
|
+
const draft = createDraft$1(baseState);
|
|
214
|
+
const missingKeys = new Set(Object.keys(baseState));
|
|
215
|
+
for (const key in nextState) {
|
|
216
|
+
draft[key] = nextState[key];
|
|
217
|
+
missingKeys.delete(key);
|
|
218
|
+
}
|
|
219
|
+
for (const key of missingKeys) delete draft[key];
|
|
220
|
+
finishDraft(draft, (nextPatches, nextInversePatches) => {
|
|
221
|
+
patches = nextPatches;
|
|
222
|
+
inversePatches = nextInversePatches;
|
|
223
|
+
});
|
|
224
|
+
return {
|
|
225
|
+
inversePatches,
|
|
226
|
+
patches
|
|
227
|
+
};
|
|
228
|
+
}
|
|
181
229
|
function initializePrototype(prototype) {
|
|
182
230
|
const descriptors = Object.getOwnPropertyDescriptors(prototype);
|
|
183
231
|
for (const key in descriptors) {
|
|
@@ -192,31 +240,54 @@ function initializePrototype(prototype) {
|
|
|
192
240
|
descriptors[key].value = function(...args) {
|
|
193
241
|
if (activeDerivedReadDepth) throw new Error("[preact-sigma] Computeds and queries cannot call actions.");
|
|
194
242
|
const instance = getActionInstance(this);
|
|
195
|
-
if (
|
|
196
|
-
const result = value.apply(
|
|
243
|
+
if (beginActionContext(instance)) {
|
|
244
|
+
const result = value.apply(instance, args);
|
|
197
245
|
assertActionResult(key, result);
|
|
198
246
|
return result;
|
|
199
247
|
}
|
|
200
248
|
let result;
|
|
201
249
|
try {
|
|
202
|
-
|
|
203
|
-
result = actionFn.apply(actionContext, args);
|
|
250
|
+
result = actionFn.apply(instance, args);
|
|
204
251
|
assertActionResult(key, result);
|
|
205
252
|
} catch (error) {
|
|
206
|
-
|
|
253
|
+
clearActiveAction();
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
let changed;
|
|
257
|
+
try {
|
|
258
|
+
changed = commitDraft(instance);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
clearActiveAction();
|
|
207
261
|
throw error;
|
|
208
262
|
}
|
|
209
|
-
const changed = ensureDraftCommitted(instance);
|
|
210
263
|
if (isPromiseLike(result)) {
|
|
211
|
-
if (changed)
|
|
264
|
+
if (changed) {
|
|
265
|
+
endActionContext(instance);
|
|
266
|
+
throw new Error(`[preact-sigma] Action named "${key}" forgot to commit() its draft before returning a promise.`);
|
|
267
|
+
}
|
|
268
|
+
addPendingAsyncAction(instance);
|
|
269
|
+
endActionContext(instance);
|
|
212
270
|
const onResolveAsyncAction = (promiseResult) => {
|
|
213
|
-
|
|
214
|
-
if (
|
|
271
|
+
try {
|
|
272
|
+
if (activeDraft && instance === activeDraftInstance) {
|
|
273
|
+
if (commitDraft(instance)) throw new Error(`[preact-sigma] Action named "${key}" forgot to commit() its draft before its promise resolved.`);
|
|
274
|
+
}
|
|
275
|
+
return promiseResult;
|
|
276
|
+
} finally {
|
|
277
|
+
removePendingAsyncAction(instance);
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
const onRejectAsyncAction = (error) => {
|
|
281
|
+
if (activeDraftInstance === instance) {
|
|
282
|
+
activeDraft = null;
|
|
283
|
+
activeDraftInstance = null;
|
|
215
284
|
}
|
|
216
|
-
|
|
285
|
+
removePendingAsyncAction(instance);
|
|
286
|
+
throw error;
|
|
217
287
|
};
|
|
218
|
-
return result.then(onResolveAsyncAction);
|
|
288
|
+
return result.then(onResolveAsyncAction, onRejectAsyncAction);
|
|
219
289
|
}
|
|
290
|
+
endActionContext(instance);
|
|
220
291
|
return result;
|
|
221
292
|
};
|
|
222
293
|
}
|
|
@@ -235,24 +306,14 @@ function disposeCleanupResource(resource) {
|
|
|
235
306
|
else if ("dispose" in resource) resource.dispose();
|
|
236
307
|
else resource[Symbol.dispose]();
|
|
237
308
|
}
|
|
238
|
-
function act(fn) {
|
|
239
|
-
const instance = getActionInstance(this);
|
|
240
|
-
if (instance !== this) throw new Error("Cannot act() from inside an action.");
|
|
241
|
-
ensureActiveInstance(instance);
|
|
242
|
-
try {
|
|
243
|
-
const context = createActionContext(instance);
|
|
244
|
-
if (isPromiseLike(action(fn).call(context))) throw new Error("[preact-sigma] act() callbacks must be synchronous");
|
|
245
|
-
} catch (error) {
|
|
246
|
-
clearActiveInstance();
|
|
247
|
-
throw error;
|
|
248
|
-
}
|
|
249
|
-
ensureDraftCommitted(instance);
|
|
250
|
-
}
|
|
251
309
|
function defineSignalProperty(instance, key, value) {
|
|
252
310
|
Object.defineProperty(instance, key + signalSuffix, { value: signal(value) });
|
|
253
311
|
if (!Object.hasOwn(instance.constructor.prototype, key)) Object.defineProperty(instance.constructor.prototype, key, {
|
|
254
312
|
get() {
|
|
255
|
-
return this
|
|
313
|
+
return readStateProperty(this, key);
|
|
314
|
+
},
|
|
315
|
+
set(value) {
|
|
316
|
+
writeStateProperty(this, key, value);
|
|
256
317
|
},
|
|
257
318
|
enumerable: true
|
|
258
319
|
});
|
|
@@ -261,6 +322,8 @@ function defineSignalProperty(instance, key, value) {
|
|
|
261
322
|
* Base class for signal-backed state models.
|
|
262
323
|
*
|
|
263
324
|
* `TState` is the source of typing for top-level state keys, subscriptions, signals, and replacement snapshots.
|
|
325
|
+
* Private class fields stay ordinary instance storage and are not signal-backed, captured,
|
|
326
|
+
* persisted, or used for reactive invalidation by themselves.
|
|
264
327
|
* Merge a same-named interface with the class when direct property reads should be typed on the instance.
|
|
265
328
|
*/
|
|
266
329
|
var Sigma = class {
|
|
@@ -278,12 +341,15 @@ var Sigma = class {
|
|
|
278
341
|
}
|
|
279
342
|
/** Runs `onSetup(...)` and returns a cleanup that disposes returned resources in reverse order. */
|
|
280
343
|
setup(...args) {
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
344
|
+
const instance = getActionInstance(this);
|
|
345
|
+
const previousSetupInstance = activeSetupInstance;
|
|
346
|
+
activeSetupInstance = instance;
|
|
347
|
+
let resources;
|
|
348
|
+
try {
|
|
349
|
+
resources = this.onSetup.apply(instance, args);
|
|
350
|
+
} finally {
|
|
351
|
+
activeSetupInstance = previousSetupInstance;
|
|
352
|
+
}
|
|
287
353
|
return () => {
|
|
288
354
|
for (let i = resources.length - 1; i >= 0; i--) disposeCleanupResource(resources[i]);
|
|
289
355
|
};
|
|
@@ -296,19 +362,24 @@ var Sigma = class {
|
|
|
296
362
|
*/
|
|
297
363
|
commit(callback) {
|
|
298
364
|
const instance = getActionInstance(this);
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
if (callback)
|
|
302
|
-
const context = new Proxy(instance, { get(target, key, receiver) {
|
|
303
|
-
if (key === instanceSymbol) return instance;
|
|
304
|
-
return Reflect.get(target, key, receiver);
|
|
305
|
-
} });
|
|
306
|
-
return callback.call(context);
|
|
307
|
-
}
|
|
365
|
+
assertActionContext(instance, "Cannot commit() from outside an action.");
|
|
366
|
+
commitDraft(instance);
|
|
367
|
+
if (callback) return callback.call(instance);
|
|
308
368
|
}
|
|
309
369
|
/** Runs a synchronous setup-owned callback with action semantics from an `onSetup(...)` context. */
|
|
310
370
|
act(fn) {
|
|
311
|
-
|
|
371
|
+
const instance = getActionInstance(this);
|
|
372
|
+
if (activeSetupInstance !== instance) throw new Error("Cannot act() from outside an onSetup() context.");
|
|
373
|
+
if (activeActionInstance === instance) throw new Error("Cannot act() from inside an action.");
|
|
374
|
+
beginActionContext(instance);
|
|
375
|
+
try {
|
|
376
|
+
if (isPromiseLike(action(fn).call(instance))) throw new Error("[preact-sigma] act() callbacks must be synchronous");
|
|
377
|
+
commitDraft(instance);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
clearActiveAction();
|
|
380
|
+
throw error;
|
|
381
|
+
}
|
|
382
|
+
endActionContext(instance);
|
|
312
383
|
}
|
|
313
384
|
};
|
|
314
385
|
/** Casts a sigma instance to its readonly public consumer view. */
|
|
@@ -328,8 +399,8 @@ var SigmaTarget = class extends Sigma {
|
|
|
328
399
|
/** Emits a typed event from an action after unpublished draft changes are committed. */
|
|
329
400
|
emit(name, ...[detail]) {
|
|
330
401
|
const instance = getActionInstance(this);
|
|
331
|
-
|
|
332
|
-
if (instance ===
|
|
402
|
+
assertActionContext(instance, "Cannot emit() from outside an action.");
|
|
403
|
+
if (instance === activeDraftInstance && activeDraft) throw new Error("Cannot emit() until you commit() your draft.");
|
|
333
404
|
this[listenersSymbol].emit(name, detail);
|
|
334
405
|
}
|
|
335
406
|
};
|
|
@@ -365,11 +436,19 @@ const sigma = /* @__PURE__ */ Object.freeze({
|
|
|
365
436
|
if (activeDraft) throw new Error(`[preact-sigma] replaceState() cannot run while an action has unpublished changes.`);
|
|
366
437
|
const instance = getActionInstance(target);
|
|
367
438
|
const baseState = createSnapshot(instance);
|
|
368
|
-
|
|
369
|
-
|
|
439
|
+
if (!hasStateChanges(baseState, nextState)) return;
|
|
440
|
+
const { inversePatches, patches } = hasPatchListeners(instance) ? createReplacementPatches(baseState, nextState) : {
|
|
441
|
+
inversePatches: void 0,
|
|
442
|
+
patches: void 0
|
|
443
|
+
};
|
|
444
|
+
publishState(instance, nextState, baseState, patches, inversePatches);
|
|
370
445
|
}
|
|
371
446
|
});
|
|
372
|
-
/**
|
|
447
|
+
/**
|
|
448
|
+
* Marks a class method as a committed-state reactive read with arguments instead of an action.
|
|
449
|
+
*
|
|
450
|
+
* Each call creates a reactive read at the call site. Query calls do not memoize results across invocations.
|
|
451
|
+
*/
|
|
373
452
|
function query(method) {
|
|
374
453
|
queries.add(method);
|
|
375
454
|
function queryMethod(...args) {
|
package/docs/context.md
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
- Sigma class: a class that extends `Sigma<TState>` and passes its initial top-level state to `super(...)`. The `TState` argument drives helper typing for subscriptions, signals, and replacement snapshots; a same-named merged interface gives direct property reads their instance types.
|
|
23
23
|
- Sigma target: a class that extends `SigmaTarget<TEvents, TState>` when it also emits typed events. Use `SigmaTarget<TEvents>` for event-only targets.
|
|
24
24
|
- State property: a top-level key from `TState`. Each key becomes a reactive public property and has its own signal.
|
|
25
|
+
- Private field: an ECMAScript `#field` on the model class. Private fields are ordinary instance storage. They can be read from model members, but they do not create signals or invalidate reactive reads by themselves.
|
|
25
26
|
- Computed: an argument-free derived getter on the class prototype that reads committed state.
|
|
26
27
|
- Query: a reactive read method that accepts arguments, is marked with the `query` decorator, and reads committed state.
|
|
27
28
|
- Action: a prototype method that is not marked as a query. Actions read and write state properties through sigma's draft and commit semantics.
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
|
|
63
64
|
- Put the state shape in a named `State` type, pass it to `Sigma<TState>` or `SigmaTarget<TEvents, TState>`, then merge a same-named interface with the class for direct property typing.
|
|
64
65
|
- Keep frequently read values as separate top-level state properties. Each top-level key gets its own signal.
|
|
66
|
+
- Use private fields for ephemeral caches, handles, or bookkeeping when private-only changes should not be captured, restored, persisted, used as subscription keys, or published reactively.
|
|
65
67
|
- Use getters for argument-free derived reads.
|
|
66
68
|
- Use `@query` for tracked reads with arguments.
|
|
67
69
|
- Derive directly from state properties inside an action when the calculation needs unpublished draft values.
|
|
@@ -94,6 +96,7 @@
|
|
|
94
96
|
# Invariants and Constraints
|
|
95
97
|
|
|
96
98
|
- Sigma tracks top-level state properties. Each top-level key gets its own signal.
|
|
99
|
+
- Private fields are not top-level state properties. They do not create signals, appear in committed snapshots, participate in persistence helpers, or drive subscriptions by themselves. Computeds and queries that read private fields update when their signal-backed state dependencies change; private-only changes do not invalidate those reads.
|
|
97
100
|
- Protected consumer views expose immutable state and callable actions.
|
|
98
101
|
- Published draftable public state is deep-frozen by default. `setAutoFreeze(false)` disables that behavior globally.
|
|
99
102
|
- Computeds and queries read committed state, including when called inside actions.
|