preact-sigma 2.1.2 → 2.2.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 +6 -2
- package/dist/index.d.mts +12 -9
- package/dist/index.mjs +52 -38
- package/llms.txt +15 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -157,13 +157,17 @@ cleanup();
|
|
|
157
157
|
|
|
158
158
|
In Preact, the same constructor can be used with `useSigma(() => new TodoList(), ["todos-demo"])` so the component owns one instance and `setup(...)` cleanup runs automatically. Use `useListener(...)` when you want component-scoped event subscriptions with automatic teardown.
|
|
159
159
|
|
|
160
|
+
Cleanup resources can be returned as functions, `AbortController`, objects with `dispose()`, or objects with `Symbol.dispose`.
|
|
161
|
+
|
|
162
|
+
Inside setup, `this` exposes the public instance plus `emit(...)` and `act(fn)`. Use `this.act(function () { ... })` when setup needs one synchronous anonymous action with normal draft, `commit()`, and `emit(...)` semantics, whether that work happens immediately in the setup body or later from a setup-owned callback, but should not become a public action method.
|
|
163
|
+
|
|
160
164
|
## Best Practices
|
|
161
165
|
|
|
162
166
|
- Let `new SigmaType<TState, TEvents>()` and the builder inputs drive inference. Avoid forcing extra type arguments onto builder methods.
|
|
163
167
|
- Keep top-level state properties meaningful. Each top-level property gets its own signal, so shape state around the reads you want to track.
|
|
164
168
|
- Use `computed(...)` for argument-free derived state, and use queries for reactive reads that need parameters.
|
|
165
|
-
- Put writes in actions. A draft boundary is any point where sigma cannot keep reusing the current draft. `emit()`, `await`, and any action call other than a same-instance sync nested action call are draft boundaries, so call `this.commit()` before those boundaries when pending writes should become public.
|
|
169
|
+
- Put writes in actions. A draft boundary is any point where sigma cannot keep reusing the current draft. `emit()`, `await`, and any action call other than a same-instance sync nested action call are draft boundaries, so call `this.commit()` before those boundaries when pending writes should become public. Setup can use `this.act(function () { ... })` to run one synchronous anonymous action for initialization work or setup-owned callbacks without adding a public action method.
|
|
166
170
|
- Use `snapshot(instance)` and `replaceState(instance, snapshot)` for committed-state replay. They work on top-level state keys and stay outside action semantics.
|
|
167
|
-
- Use `
|
|
171
|
+
- Use `SigmaRef<T>` when a value should stay by reference in sigma's `Draft` and `Immutable` types. A normal assignment to a `SigmaRef<T>`-typed value only changes typing and does not change Immer's runtime drafting or freezing behavior.
|
|
168
172
|
- Use `immerable` on custom classes only when they should participate in Immer drafting. `setAutoFreeze(false)` disables sigma's runtime deep-freezing when you need published state to stay unfrozen.
|
|
169
173
|
- Use `setup(...)` for owned side effects, and always return cleanup resources for anything the instance starts.
|
package/dist/index.d.mts
CHANGED
|
@@ -47,10 +47,13 @@ type DefaultStateValue<TValue> = TValue | DefaultStateInitializer<TValue>;
|
|
|
47
47
|
type Disposable = {
|
|
48
48
|
[Symbol.dispose](): void;
|
|
49
49
|
};
|
|
50
|
+
type DisposableLike = {
|
|
51
|
+
dispose(): void;
|
|
52
|
+
};
|
|
50
53
|
interface SigmaRefBrand {
|
|
51
54
|
[sigmaRefBrand]?: true;
|
|
52
55
|
}
|
|
53
|
-
/** A type brand
|
|
56
|
+
/** A type brand that keeps a value by reference in sigma's `Draft` and `Immutable` helpers. */
|
|
54
57
|
type SigmaRef<T = unknown> = T & SigmaRefBrand;
|
|
55
58
|
/** The event map shape used by sigma types. */
|
|
56
59
|
type AnyEvents = Record<string, object | void>;
|
|
@@ -58,8 +61,8 @@ type AnyEvents = Record<string, object | void>;
|
|
|
58
61
|
type AnyState = Record<string, unknown>;
|
|
59
62
|
/** The object accepted by `.defaultState(...)`. */
|
|
60
63
|
type AnyDefaultState<TState extends AnyState> = { [K in keyof TState]?: DefaultStateValue<TState[K]> };
|
|
61
|
-
/** A cleanup resource supported by `.setup(...)
|
|
62
|
-
type AnyResource = Cleanup | Disposable | AbortController;
|
|
64
|
+
/** A cleanup resource supported by `.setup(...)`, including function, `dispose()`, and `Symbol.dispose` cleanup. */
|
|
65
|
+
type AnyResource = Cleanup | Disposable | DisposableLike | AbortController;
|
|
63
66
|
type ComputedValues<TComputeds extends object | undefined> = [undefined] extends [TComputeds] ? never : { readonly [K in keyof TComputeds]: TComputeds[K] extends AnyFunction ? Immutable<ReturnType<TComputeds[K]>> : never };
|
|
64
67
|
type ComputedContext<TState extends AnyState, TComputeds extends object> = Immutable<TState> & ComputedValues<TComputeds>;
|
|
65
68
|
type QueryMethods<TQueries extends object | undefined> = [undefined] extends [TQueries] ? never : { [K in keyof TQueries]: TQueries[K] extends AnyFunction ? (...args: Parameters<TQueries[K]>) => ReturnType<TQueries[K]> : never };
|
|
@@ -77,6 +80,10 @@ type ActionContext<TState extends AnyState, TEvents extends AnyEvents, TComputed
|
|
|
77
80
|
/** Publishes the current action draft immediately so later boundaries use committed state. */commit(): void;
|
|
78
81
|
emit: Emit<TEvents>;
|
|
79
82
|
};
|
|
83
|
+
type DefinitionEvents<T extends SigmaDefinition> = T["events"] extends AnyEvents ? T["events"] : {};
|
|
84
|
+
type DefinitionComputeds<T extends SigmaDefinition> = T["computeds"] extends object ? T["computeds"] : {};
|
|
85
|
+
type DefinitionQueries<T extends SigmaDefinition> = T["queries"] extends object ? T["queries"] : {};
|
|
86
|
+
type DefinitionActions<T extends SigmaDefinition> = T["actions"] extends object ? T["actions"] : {};
|
|
80
87
|
/** The public shape shared by all sigma-state instances. */
|
|
81
88
|
interface AnySigmaState extends EventTarget {
|
|
82
89
|
readonly [sigmaStateBrand]: true;
|
|
@@ -114,6 +121,7 @@ type MapSigmaDefinition<T extends SigmaDefinition> = keyof T extends infer K ? K
|
|
|
114
121
|
/** The public instance shape produced by a configured sigma type. */
|
|
115
122
|
type SigmaState<T extends SigmaDefinition = SigmaDefinition> = AnySigmaState & Simplify<UnionToIntersection<MapSigmaDefinition<T>>>;
|
|
116
123
|
type SetupContext<T extends SigmaDefinition> = SigmaState<T> & {
|
|
124
|
+
act<TResult>(fn: (this: ActionContext<T["state"], DefinitionEvents<T>, DefinitionComputeds<T>, DefinitionQueries<T>, DefinitionActions<T>>) => TResult): TResult;
|
|
117
125
|
emit: T["events"] extends object ? Emit<T["events"]> : never;
|
|
118
126
|
};
|
|
119
127
|
type MergeObjects<TLeft extends object, TRight> = [TRight] extends [object] ? Extract<Simplify<Omit<TLeft, keyof TRight> & TRight>, TLeft> : TLeft;
|
|
@@ -150,11 +158,6 @@ declare function replaceState<T extends AnySigmaState>(publicInstance: T, nextSt
|
|
|
150
158
|
//#region src/framework.d.ts
|
|
151
159
|
/** Checks whether a value is a sigma-state instance. */
|
|
152
160
|
declare function isSigmaState(value: unknown): value is AnySigmaState;
|
|
153
|
-
/**
|
|
154
|
-
* Returns `value` unchanged and marks its type so sigma's Draft and Immutable
|
|
155
|
-
* helpers keep it by reference instead of recursively immerizing it.
|
|
156
|
-
*/
|
|
157
|
-
declare function ref<T extends object>(value: T): SigmaRef<T>;
|
|
158
161
|
/** Creates a standalone tracked query function with the same signature as `fn`. */
|
|
159
162
|
declare function query<TArgs extends any[], TResult>(fn: (this: void, ...args: TArgs) => TResult): typeof fn;
|
|
160
163
|
/**
|
|
@@ -238,4 +241,4 @@ declare function useSigma<T extends AnySigmaState>(create: () => T, setupArgs?:
|
|
|
238
241
|
/** Attaches an event listener in a component and cleans it up when dependencies change. */
|
|
239
242
|
declare function useListener<TTarget extends EventTarget | AnySigmaState, TEvent extends InferEventType<TTarget>>(target: TTarget | null, name: TEvent, listener: InferListener<TTarget, TEvent>): void;
|
|
240
243
|
//#endregion
|
|
241
|
-
export { type AnyDefaultState, type AnyEvents, type AnyResource, type AnySigmaState, type AnySigmaStateWithEvents, type AnyState, InferEventType, InferListener, type InferSetupArgs, type SigmaObserveChange, type SigmaObserveOptions, type SigmaRef, type SigmaState, SigmaType, action, batch, computed, effect, freeze, immerable, isSigmaState, listen, query,
|
|
244
|
+
export { type AnyDefaultState, type AnyEvents, type AnyResource, type AnySigmaState, type AnySigmaStateWithEvents, type AnyState, InferEventType, InferListener, type InferSetupArgs, type SigmaObserveChange, type SigmaObserveOptions, type SigmaRef, type SigmaState, SigmaType, action, batch, computed, effect, freeze, immerable, isSigmaState, listen, query, replaceState, setAutoFreeze, snapshot, untracked, useListener, useSigma };
|
package/dist/index.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { freeze, immerable } from "immer";
|
|
|
4
4
|
import { useEffect, useRef, useState } from "preact/hooks";
|
|
5
5
|
//#region src/internal/context.ts
|
|
6
6
|
const disabledContextOptions = {
|
|
7
|
+
allowAct: false,
|
|
7
8
|
allowActions: false,
|
|
8
9
|
allowCommit: false,
|
|
9
10
|
allowEmit: false,
|
|
@@ -30,6 +31,7 @@ const publicContextOptions = {
|
|
|
30
31
|
},
|
|
31
32
|
setup: {
|
|
32
33
|
...disabledContextOptions,
|
|
34
|
+
allowAct: true,
|
|
33
35
|
allowActions: true,
|
|
34
36
|
allowEmit: true,
|
|
35
37
|
allowQueries: true
|
|
@@ -87,8 +89,12 @@ function registerContextOwner(context, owner) {
|
|
|
87
89
|
function createContext(instance, options, owner) {
|
|
88
90
|
const publicPrototype = Object.getPrototypeOf(instance.publicInstance);
|
|
89
91
|
return new Proxy(publicPrototype, {
|
|
90
|
-
get(_target, key) {
|
|
92
|
+
get(_target, key, receiver) {
|
|
91
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;
|
|
92
98
|
if (key === "commit") return options.allowCommit && owner ? () => commitActionOwner(owner) : void 0;
|
|
93
99
|
if (key === "emit") return options.allowEmit && owner ? (name, detail) => {
|
|
94
100
|
handleActionBoundary(owner, "emit");
|
|
@@ -168,6 +174,7 @@ function getPublicContext(instance, kind) {
|
|
|
168
174
|
//#region src/internal/symbols.ts
|
|
169
175
|
const sigmaStateBrand = Symbol("sigma.v2.state");
|
|
170
176
|
const reservedKeys = new Set([
|
|
177
|
+
"act",
|
|
171
178
|
"get",
|
|
172
179
|
"emit",
|
|
173
180
|
"commit",
|
|
@@ -237,38 +244,13 @@ function buildQueryMethod(queryFunction) {
|
|
|
237
244
|
};
|
|
238
245
|
}
|
|
239
246
|
function buildActionMethod(actionName, actionFn) {
|
|
240
|
-
const actionIsAsync = isAsyncFunction(actionFn);
|
|
241
247
|
return function(...args) {
|
|
242
|
-
|
|
243
|
-
if (instance.disposed) throw new Error("[preact-sigma] Cannot run an action on a disposed sigma state");
|
|
244
|
-
return untracked$1(() => {
|
|
245
|
-
let owner;
|
|
246
|
-
const callerOwner = getContextOwner(this);
|
|
247
|
-
if (callerOwner && callerOwner.instance === instance && !actionIsAsync) owner = callerOwner;
|
|
248
|
-
else {
|
|
249
|
-
handleActionBoundary(callerOwner, "action", actionName);
|
|
250
|
-
owner = createActionOwner(instance, actionName, actionFn, args);
|
|
251
|
-
}
|
|
252
|
-
let result;
|
|
253
|
-
try {
|
|
254
|
-
result = actionFn.apply(owner.actionContext, [...args]);
|
|
255
|
-
} catch (error) {
|
|
256
|
-
clearCurrentDraft(owner);
|
|
257
|
-
throw error;
|
|
258
|
-
}
|
|
259
|
-
if (!actionIsAsync && isPromiseLike(result)) {
|
|
260
|
-
clearCurrentDraft(owner);
|
|
261
|
-
Promise.resolve(result).catch(() => {});
|
|
262
|
-
throw new Error(`[preact-sigma] Action "${actionName}" must use native async-await syntax to return a promise.`);
|
|
263
|
-
}
|
|
264
|
-
if (owner === callerOwner) return result;
|
|
265
|
-
const finalized = finalizeOwnerDraft(owner);
|
|
266
|
-
if (finalized?.changed) publishState(instance, finalized);
|
|
267
|
-
if (isPromiseLike(result)) return resolveAsyncActionResult(owner, result);
|
|
268
|
-
return result;
|
|
269
|
-
});
|
|
248
|
+
return runActionInvocation(this, actionName, actionFn, args);
|
|
270
249
|
};
|
|
271
250
|
}
|
|
251
|
+
function runAdHocAction(context, actionFn) {
|
|
252
|
+
return runActionInvocation(context, "act()", actionFn, []);
|
|
253
|
+
}
|
|
272
254
|
function readActionStateValue(owner, key, options) {
|
|
273
255
|
if (owner.currentDraft) return owner.currentDraft[key];
|
|
274
256
|
const signal = getSignal(owner.instance, key);
|
|
@@ -323,6 +305,44 @@ function createActionOwner(instance, actionName, actionFn, args) {
|
|
|
323
305
|
registerContextOwner(owner.actionContext, owner);
|
|
324
306
|
return owner;
|
|
325
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
|
+
}
|
|
326
346
|
function createCommitError(owner) {
|
|
327
347
|
return /* @__PURE__ */ new Error(`[preact-sigma] Async action "${owner.actionName}" finished with unpublished changes. Call this.commit() before await or return.`);
|
|
328
348
|
}
|
|
@@ -339,6 +359,7 @@ function createDraftMetadata(owner) {
|
|
|
339
359
|
function disposeCleanupResource(resource) {
|
|
340
360
|
if (typeof resource === "function") resource();
|
|
341
361
|
else if (resource instanceof AbortController) resource.abort();
|
|
362
|
+
else if ("dispose" in resource) resource.dispose();
|
|
342
363
|
else resource[Symbol.dispose]();
|
|
343
364
|
}
|
|
344
365
|
function assertExactStateKeys(instance, nextState) {
|
|
@@ -520,13 +541,6 @@ Object.defineProperty(Sigma.prototype, sigmaStateBrand, { value: true });
|
|
|
520
541
|
function isSigmaState(value) {
|
|
521
542
|
return Boolean(value && typeof value === "object" && value[sigmaStateBrand]);
|
|
522
543
|
}
|
|
523
|
-
/**
|
|
524
|
-
* Returns `value` unchanged and marks its type so sigma's Draft and Immutable
|
|
525
|
-
* helpers keep it by reference instead of recursively immerizing it.
|
|
526
|
-
*/
|
|
527
|
-
function ref(value) {
|
|
528
|
-
return value;
|
|
529
|
-
}
|
|
530
544
|
/** Creates a standalone tracked query function with the same signature as `fn`. */
|
|
531
545
|
function query(fn) {
|
|
532
546
|
return ((...args) => computed$1(() => fn(...args)).value);
|
|
@@ -633,4 +647,4 @@ function useListener(target, name, listener) {
|
|
|
633
647
|
}, [target, name]);
|
|
634
648
|
}
|
|
635
649
|
//#endregion
|
|
636
|
-
export { SigmaType, action, batch, computed, effect, freeze, immerable, isSigmaState, listen, query,
|
|
650
|
+
export { SigmaType, action, batch, computed, effect, freeze, immerable, isSigmaState, listen, query, replaceState, setAutoFreeze, snapshot, untracked, useListener, useSigma };
|
package/llms.txt
CHANGED
|
@@ -7,18 +7,18 @@
|
|
|
7
7
|
- `state property`: A top-level property from `TState`, such as `draft` in `{ draft: string }`.
|
|
8
8
|
- `computed`: A tracked getter declared with `.computed({ ... })`.
|
|
9
9
|
- `query`: A tracked method declared with `.queries({ ... })` or created with `query(fn)`.
|
|
10
|
-
- `ref`: A helper that keeps a value by reference in sigma's `Draft` and `Immutable` types without changing runtime behavior.
|
|
11
10
|
- `action`: A method declared with `.actions({ ... })` that reads and writes through one Immer draft for one synchronous call.
|
|
11
|
+
- `anonymous action`: A synchronous function passed to `this.act(...)` inside setup to run one anonymous action.
|
|
12
12
|
- `draft boundary`: Any point where sigma cannot keep reusing the current draft.
|
|
13
13
|
- `setup handler`: A function declared with `.setup(fn)` that returns an array of cleanup resources.
|
|
14
|
-
- `cleanup resource`: A cleanup function, an `AbortController`, or an object with `[Symbol.dispose]()`.
|
|
14
|
+
- `cleanup resource`: A cleanup function, an `AbortController`, an object with `dispose()`, or an object with `[Symbol.dispose]()`.
|
|
15
15
|
- `signal access`: Reading the underlying `ReadonlySignal` for a state property or computed through `instance.get(key)`.
|
|
16
16
|
|
|
17
17
|
## Navigation
|
|
18
18
|
|
|
19
19
|
- For state shape, inference, and instance shape, read `Start Here`, `Inference`, `SigmaType`, and `Public Instance Shape`.
|
|
20
20
|
- For mutation semantics, read `Critical Rules`, `actions`, `immerable`, and `setAutoFreeze`.
|
|
21
|
-
- For type-level by-reference values,
|
|
21
|
+
- For type-level by-reference values, see `SigmaRef<T>`.
|
|
22
22
|
- For side effects and events, read `setup`, `Events`, `listen`, `useListener`, and `useSigma`.
|
|
23
23
|
- For committed-state utilities, read `observe`, `snapshot`, and `replaceState`.
|
|
24
24
|
|
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
isSigmaState,
|
|
63
63
|
listen,
|
|
64
64
|
query,
|
|
65
|
-
|
|
65
|
+
type SigmaRef,
|
|
66
66
|
replaceState,
|
|
67
67
|
setAutoFreeze,
|
|
68
68
|
snapshot,
|
|
@@ -76,7 +76,7 @@ import {
|
|
|
76
76
|
|
|
77
77
|
- `AnyDefaultState`: Describes the object accepted by `.defaultState(...)`.
|
|
78
78
|
- `AnyEvents`: Describes an event map from event names to payload objects or `void`.
|
|
79
|
-
- `AnyResource`: Describes a supported setup cleanup resource.
|
|
79
|
+
- `AnyResource`: Describes a supported setup cleanup resource, including cleanup functions, `AbortController`, objects with `dispose()`, and objects with `Symbol.dispose`.
|
|
80
80
|
- `AnySigmaState`: Describes the public shape shared by all sigma-state instances.
|
|
81
81
|
- `AnySigmaStateWithEvents`: Describes a sigma-state instance with a typed event map.
|
|
82
82
|
- `AnyState`: Describes the top-level state object for a sigma type.
|
|
@@ -170,7 +170,7 @@ Behavior:
|
|
|
170
170
|
- Constructor input shallowly overrides `defaultState`.
|
|
171
171
|
- If every required state property is covered by `defaultState`, constructor input is optional.
|
|
172
172
|
- Duplicate names across state properties, computeds, queries, and actions are rejected at runtime.
|
|
173
|
-
- Reserved public names are `get`, `setup`, `on`, and `emit`.
|
|
173
|
+
- Reserved public names are `act`, `get`, `setup`, `on`, and `emit`.
|
|
174
174
|
|
|
175
175
|
## Public Instance Shape
|
|
176
176
|
|
|
@@ -290,13 +290,18 @@ Behavior:
|
|
|
290
290
|
- calling `.setup(...)` again cleans up the previous setup first
|
|
291
291
|
- one `.setup(...)` call runs every registered setup handler in definition order
|
|
292
292
|
- the public `.setup(...)` method always returns one cleanup function
|
|
293
|
-
- `this` inside a setup handler exposes the public instance plus `emit(...)`
|
|
293
|
+
- `this` inside a setup handler exposes the public instance plus `emit(...)` and `act(fn)`
|
|
294
|
+
- `this.act(fn)` inside setup runs `fn` with normal action semantics without adding a public action method
|
|
295
|
+
- use `this.act(fn)` for setup-time initialization work or setup-owned callbacks that need action semantics
|
|
296
|
+
- pass a normal `function () {}` to `this.act(...)` so callback `this` receives the action context
|
|
297
|
+
- `this.act(fn)` functions must stay synchronous
|
|
294
298
|
- each setup handler returns an array of cleanup resources
|
|
295
299
|
- setup typing only exposes computeds, queries, and actions that were already present when that `.setup(...)` call happened
|
|
296
300
|
|
|
297
301
|
Supported cleanup resources:
|
|
298
302
|
|
|
299
303
|
- cleanup functions
|
|
304
|
+
- objects with `dispose()`
|
|
300
305
|
- objects with `[Symbol.dispose]()`
|
|
301
306
|
- `AbortController`
|
|
302
307
|
|
|
@@ -328,13 +333,13 @@ Behavior:
|
|
|
328
333
|
- custom class instances without a true `[immerable]` property stay outside that freeze path
|
|
329
334
|
- plain objects, arrays, `Map`, and `Set` already participate in normal Immer drafting without extra markers
|
|
330
335
|
|
|
331
|
-
## `
|
|
336
|
+
## `SigmaRef<T>`
|
|
332
337
|
|
|
333
|
-
`
|
|
338
|
+
`SigmaRef<T>` marks a value's type so sigma's `Draft` and `Immutable` helpers keep that value by reference.
|
|
334
339
|
|
|
335
340
|
Behavior:
|
|
336
341
|
|
|
337
|
-
-
|
|
342
|
+
- assigning to a `SigmaRef<T>`-typed value has no runtime effect
|
|
338
343
|
- it only changes sigma's local `Draft` and `Immutable` typing
|
|
339
344
|
- it prevents type-level recursive immerization for that value
|
|
340
345
|
- it does not change whether Immer drafts or freezes the value at runtime
|