preact-sigma 6.0.0 → 6.0.1
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-D1V3m1xk.d.mts} +1 -0
- package/dist/{sigma-CJibGQ6g.mjs → sigma-DTMODzf8.mjs} +159 -85
- 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-D1V3m1xk.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-DTMODzf8.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,7 @@ 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, or persisted.
|
|
91
92
|
* Merge a same-named interface with the class when direct property reads should be typed on the instance.
|
|
92
93
|
*/
|
|
93
94
|
declare abstract class Sigma<TState extends object> {
|
|
@@ -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,7 @@ 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, or persisted.
|
|
264
326
|
* Merge a same-named interface with the class when direct property reads should be typed on the instance.
|
|
265
327
|
*/
|
|
266
328
|
var Sigma = class {
|
|
@@ -278,12 +340,15 @@ var Sigma = class {
|
|
|
278
340
|
}
|
|
279
341
|
/** Runs `onSetup(...)` and returns a cleanup that disposes returned resources in reverse order. */
|
|
280
342
|
setup(...args) {
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
343
|
+
const instance = getActionInstance(this);
|
|
344
|
+
const previousSetupInstance = activeSetupInstance;
|
|
345
|
+
activeSetupInstance = instance;
|
|
346
|
+
let resources;
|
|
347
|
+
try {
|
|
348
|
+
resources = this.onSetup.apply(instance, args);
|
|
349
|
+
} finally {
|
|
350
|
+
activeSetupInstance = previousSetupInstance;
|
|
351
|
+
}
|
|
287
352
|
return () => {
|
|
288
353
|
for (let i = resources.length - 1; i >= 0; i--) disposeCleanupResource(resources[i]);
|
|
289
354
|
};
|
|
@@ -296,19 +361,24 @@ var Sigma = class {
|
|
|
296
361
|
*/
|
|
297
362
|
commit(callback) {
|
|
298
363
|
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
|
-
}
|
|
364
|
+
assertActionContext(instance, "Cannot commit() from outside an action.");
|
|
365
|
+
commitDraft(instance);
|
|
366
|
+
if (callback) return callback.call(instance);
|
|
308
367
|
}
|
|
309
368
|
/** Runs a synchronous setup-owned callback with action semantics from an `onSetup(...)` context. */
|
|
310
369
|
act(fn) {
|
|
311
|
-
|
|
370
|
+
const instance = getActionInstance(this);
|
|
371
|
+
if (activeSetupInstance !== instance) throw new Error("Cannot act() from outside an onSetup() context.");
|
|
372
|
+
if (activeActionInstance === instance) throw new Error("Cannot act() from inside an action.");
|
|
373
|
+
beginActionContext(instance);
|
|
374
|
+
try {
|
|
375
|
+
if (isPromiseLike(action(fn).call(instance))) throw new Error("[preact-sigma] act() callbacks must be synchronous");
|
|
376
|
+
commitDraft(instance);
|
|
377
|
+
} catch (error) {
|
|
378
|
+
clearActiveAction();
|
|
379
|
+
throw error;
|
|
380
|
+
}
|
|
381
|
+
endActionContext(instance);
|
|
312
382
|
}
|
|
313
383
|
};
|
|
314
384
|
/** Casts a sigma instance to its readonly public consumer view. */
|
|
@@ -328,8 +398,8 @@ var SigmaTarget = class extends Sigma {
|
|
|
328
398
|
/** Emits a typed event from an action after unpublished draft changes are committed. */
|
|
329
399
|
emit(name, ...[detail]) {
|
|
330
400
|
const instance = getActionInstance(this);
|
|
331
|
-
|
|
332
|
-
if (instance ===
|
|
401
|
+
assertActionContext(instance, "Cannot emit() from outside an action.");
|
|
402
|
+
if (instance === activeDraftInstance && activeDraft) throw new Error("Cannot emit() until you commit() your draft.");
|
|
333
403
|
this[listenersSymbol].emit(name, detail);
|
|
334
404
|
}
|
|
335
405
|
};
|
|
@@ -365,8 +435,12 @@ const sigma = /* @__PURE__ */ Object.freeze({
|
|
|
365
435
|
if (activeDraft) throw new Error(`[preact-sigma] replaceState() cannot run while an action has unpublished changes.`);
|
|
366
436
|
const instance = getActionInstance(target);
|
|
367
437
|
const baseState = createSnapshot(instance);
|
|
368
|
-
|
|
369
|
-
|
|
438
|
+
if (!hasStateChanges(baseState, nextState)) return;
|
|
439
|
+
const { inversePatches, patches } = hasPatchListeners(instance) ? createReplacementPatches(baseState, nextState) : {
|
|
440
|
+
inversePatches: void 0,
|
|
441
|
+
patches: void 0
|
|
442
|
+
};
|
|
443
|
+
publishState(instance, nextState, baseState, patches, inversePatches);
|
|
370
444
|
}
|
|
371
445
|
});
|
|
372
446
|
/** Marks a class method as a committed-state reactive read with arguments instead of an action. */
|
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, not signal-backed state.
|
|
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 that should not be captured, restored, persisted, or used as subscription keys.
|
|
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.
|
|
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.
|