preact-sigma 2.0.1 → 2.1.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/README.md CHANGED
@@ -164,5 +164,6 @@ In Preact, the same constructor can be used with `useSigma(() => new TodoList(),
164
164
  - Use `computed(...)` for argument-free derived state, and use queries for reactive reads that need parameters.
165
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.
166
166
  - 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 `ref(value)` when a value should stay by reference in sigma's `Draft` and `Immutable` types. It only changes typing and does not change Immer's runtime drafting or freezing behavior.
167
168
  - 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.
168
169
  - Use `setup(...)` for owned side effects, and always return cleanup resources for anything the instance starts.
package/dist/index.d.mts CHANGED
@@ -1,6 +1,11 @@
1
1
  import { ReadonlySignal, action, batch, computed, effect, untracked } from "@preact/signals";
2
2
  import { Patch, freeze, immerable } from "immer";
3
3
 
4
+ //#region src/internal/symbols.d.ts
5
+ declare const sigmaStateBrand: unique symbol;
6
+ declare const sigmaEventsBrand: unique symbol;
7
+ declare const sigmaRefBrand: unique symbol;
8
+ //#endregion
4
9
  //#region src/immer.d.ts
5
10
  type PrimitiveType = number | string | boolean;
6
11
  /** Object types that should never be mapped */
@@ -18,6 +23,7 @@ type IfAvailable<T, Fallback = void> = true | false extends (T extends never ? t
18
23
  * Set
19
24
  */
20
25
  type WeakReferences = IfAvailable<WeakMap<any, any>> | IfAvailable<WeakSet<any>>;
26
+ type HasSigmaRefBrand<T> = [T] extends [object] ? typeof sigmaRefBrand extends keyof T ? true : false : false;
21
27
  type WritableDraft<T> = T extends any[] ? number extends T["length"] ? Draft<T[number]>[] : WritableNonArrayDraft<T> : WritableNonArrayDraft<T>;
22
28
  type WritableNonArrayDraft<T> = { -readonly [K in keyof T]: T[K] extends infer V ? (V extends object ? Draft<V> : V) : never };
23
29
  /**
@@ -25,17 +31,13 @@ type WritableNonArrayDraft<T> = { -readonly [K in keyof T]: T[K] extends infer V
25
31
  *
26
32
  * Use this instead of `immer.Draft`
27
33
  */
28
- type Draft<T> = T extends PrimitiveType ? T : T extends AtomicObject ? T : T extends ReadonlyMap<infer K, infer V> ? Map<Draft<K>, Draft<V>> : T extends ReadonlySet<infer V> ? Set<Draft<V>> : T extends WeakReferences ? T : T extends object ? WritableDraft<T> : T;
34
+ type Draft<T> = T extends PrimitiveType ? T : T extends AtomicObject ? T : HasSigmaRefBrand<T> extends true ? T : T extends ReadonlyMap<infer K, infer V> ? Map<Draft<K>, Draft<V>> : T extends ReadonlySet<infer V> ? Set<Draft<V>> : T extends WeakReferences ? T : T extends object ? WritableDraft<T> : T;
29
35
  /**
30
36
  * Convert a mutable type into a readonly type.
31
37
  *
32
38
  * Use this instead of `immer.Immutable`
33
39
  */
34
- type Immutable<T> = T extends PrimitiveType ? T : T extends AtomicObject ? T : T extends ReadonlyMap<infer K, infer V> ? ReadonlyMap<Immutable<K>, Immutable<V>> : T extends ReadonlySet<infer V> ? ReadonlySet<Immutable<V>> : T extends WeakReferences ? T : T extends object ? { readonly [K in keyof T]: Immutable<T[K]> } : T;
35
- //#endregion
36
- //#region src/internal/symbols.d.ts
37
- declare const sigmaStateBrand: unique symbol;
38
- declare const sigmaEventsBrand: unique symbol;
40
+ type Immutable<T> = T extends PrimitiveType ? T : T extends AtomicObject ? T : HasSigmaRefBrand<T> extends true ? T : T extends ReadonlyMap<infer K, infer V> ? ReadonlyMap<Immutable<K>, Immutable<V>> : T extends ReadonlySet<infer V> ? ReadonlySet<Immutable<V>> : T extends WeakReferences ? T : T extends object ? { readonly [K in keyof T]: Immutable<T[K]> } : T;
39
41
  //#endregion
40
42
  //#region src/internal/types.d.ts
41
43
  type AnyFunction = (...args: any[]) => any;
@@ -45,6 +47,11 @@ type DefaultStateValue<TValue> = TValue | DefaultStateInitializer<TValue>;
45
47
  type Disposable = {
46
48
  [Symbol.dispose](): void;
47
49
  };
50
+ interface SigmaRefBrand {
51
+ [sigmaRefBrand]?: true;
52
+ }
53
+ /** A type brand added by `ref(...)`. */
54
+ type SigmaRef<T = unknown> = T & SigmaRefBrand;
48
55
  /** The event map shape used by sigma types. */
49
56
  type AnyEvents = Record<string, object | void>;
50
57
  /** The top-level state object shape used by sigma types. */
@@ -143,6 +150,11 @@ declare function replaceState<T extends AnySigmaState>(publicInstance: T, nextSt
143
150
  //#region src/framework.d.ts
144
151
  /** Checks whether a value is a sigma-state instance. */
145
152
  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>;
146
158
  /** Creates a standalone tracked query function with the same signature as `fn`. */
147
159
  declare function query<TArgs extends any[], TResult>(fn: (this: void, ...args: TArgs) => TResult): typeof fn;
148
160
  /**
@@ -226,4 +238,4 @@ declare function useSigma<T extends AnySigmaState>(create: () => T, setupArgs?:
226
238
  /** Attaches an event listener in a component and cleans it up when dependencies change. */
227
239
  declare function useListener<TTarget extends EventTarget | AnySigmaState, TEvent extends InferEventType<TTarget>>(target: TTarget | null, name: TEvent, listener: InferListener<TTarget, TEvent>): void;
228
240
  //#endregion
229
- export { type AnyDefaultState, type AnyEvents, type AnyResource, type AnySigmaState, type AnySigmaStateWithEvents, type AnyState, InferEventType, InferListener, type InferSetupArgs, type SigmaObserveChange, type SigmaObserveOptions, type SigmaState, SigmaType, action, batch, computed, effect, freeze, immerable, isSigmaState, listen, query, replaceState, setAutoFreeze, snapshot, untracked, useListener, useSigma };
241
+ export { type AnyDefaultState, type AnyEvents, type AnyResource, type AnySigmaState, type AnySigmaStateWithEvents, type AnyState, InferEventType, InferListener, type InferSetupArgs, type SigmaObserveChange, type SigmaObserveOptions, type SigmaState, SigmaType, action, batch, computed, effect, freeze, immerable, isSigmaState, listen, query, ref, replaceState, setAutoFreeze, snapshot, untracked, useListener, useSigma };
package/dist/index.mjs CHANGED
@@ -520,6 +520,13 @@ Object.defineProperty(Sigma.prototype, sigmaStateBrand, { value: true });
520
520
  function isSigmaState(value) {
521
521
  return Boolean(value && typeof value === "object" && value[sigmaStateBrand]);
522
522
  }
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
+ }
523
530
  /** Creates a standalone tracked query function with the same signature as `fn`. */
524
531
  function query(fn) {
525
532
  return ((...args) => computed$1(() => fn(...args)).value);
@@ -626,4 +633,4 @@ function useListener(target, name, listener) {
626
633
  }, [target, name]);
627
634
  }
628
635
  //#endregion
629
- export { SigmaType, action, batch, computed, effect, freeze, immerable, isSigmaState, listen, query, replaceState, setAutoFreeze, snapshot, untracked, useListener, useSigma };
636
+ export { SigmaType, action, batch, computed, effect, freeze, immerable, isSigmaState, listen, query, ref, replaceState, setAutoFreeze, snapshot, untracked, useListener, useSigma };
package/llms.txt CHANGED
@@ -7,6 +7,7 @@
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.
10
11
  - `action`: A method declared with `.actions({ ... })` that reads and writes through one Immer draft for one synchronous call.
11
12
  - `draft boundary`: Any point where sigma cannot keep reusing the current draft.
12
13
  - `setup handler`: A function declared with `.setup(fn)` that returns an array of cleanup resources.
@@ -17,6 +18,7 @@
17
18
 
18
19
  - For state shape, inference, and instance shape, read `Start Here`, `Inference`, `SigmaType`, and `Public Instance Shape`.
19
20
  - For mutation semantics, read `Critical Rules`, `actions`, `immerable`, and `setAutoFreeze`.
21
+ - For type-level by-reference values, read `ref`.
20
22
  - For side effects and events, read `setup`, `Events`, `listen`, `useListener`, and `useSigma`.
21
23
  - For committed-state utilities, read `observe`, `snapshot`, and `replaceState`.
22
24
 
@@ -60,6 +62,7 @@ import {
60
62
  isSigmaState,
61
63
  listen,
62
64
  query,
65
+ ref,
63
66
  replaceState,
64
67
  setAutoFreeze,
65
68
  snapshot,
@@ -325,6 +328,17 @@ Behavior:
325
328
  - custom class instances without a true `[immerable]` property stay outside that freeze path
326
329
  - plain objects, arrays, `Map`, and `Set` already participate in normal Immer drafting without extra markers
327
330
 
331
+ ## `ref(value)`
332
+
333
+ `ref(value)` returns `value` unchanged and marks its type so sigma's `Draft` and `Immutable` helpers keep that value by reference.
334
+
335
+ Behavior:
336
+
337
+ - it has no runtime effect
338
+ - it only changes sigma's local `Draft` and `Immutable` typing
339
+ - it prevents type-level recursive immerization for that value
340
+ - it does not change whether Immer drafts or freezes the value at runtime
341
+
328
342
  ## `query(fn)`
329
343
 
330
344
  `query(fn)` creates a standalone tracked query helper.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "preact-sigma",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "keywords": [],
5
5
  "license": "MIT",
6
6
  "author": "Alec Larson",