preact-sigma 6.2.0 → 6.3.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 CHANGED
@@ -49,7 +49,4 @@ console.log(counter.doubled); // 2
49
49
  - Persistence-specific guidance lives in [`docs/persist.md`](./docs/persist.md).
50
50
  - Migration guidance from v5 lives in [`docs/migrations/v5-to-v6.md`](./docs/migrations/v5-to-v6.md).
51
51
  - Runnable usage patterns live in [`examples/`](./examples/).
52
- - [`examples/basic-counter.ts`](./examples/basic-counter.ts)
53
- - [`examples/command-palette.tsx`](./examples/command-palette.tsx)
54
- - [`examples/external-query-sync.tsx`](./examples/external-query-sync.tsx)
55
52
  - Exact exported signatures and public API comments live in `dist/index.d.mts` and `dist/persist.d.mts` after `pnpm build`.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as SigmaRef, c as castProtected, d as sigma, f as Draft, h as Cleanup, i as SigmaDefinition, l as query, m as typeSymbol, n as ReadableSigma, o as SigmaState, p as Immutable, r as Sigma, s as SigmaTarget, t as Protected, u as setAutoFreeze } from "./sigma-BfaJUPWw.mjs";
1
+ import { a as SigmaRef, c as castProtected, d as sigma, f as Draft, g as Cleanup, h as AnyResource, i as SigmaDefinition, l as query, m as typeSymbol, n as ReadableSigma, o as SigmaState, p as Immutable, r as Sigma, s as SigmaTarget, t as Protected, u as setAutoFreeze } from "./sigma-CCBzhGdp.mjs";
2
2
 
3
3
  //#region src/defaults.d.ts
4
4
  /**
@@ -54,7 +54,8 @@ type UseSigmaArgs<T extends Sigma<any>> = T extends {
54
54
  /**
55
55
  * Creates or reuses a sigma instance for a component and returns its protected consumer view.
56
56
  *
57
- * Classes with `onSetup(...)` run setup in an effect and clean it up on unmount.
57
+ * Classes with `onSetup(...)` run setup in an effect and clean it up on unmount,
58
+ * so the returned instance does not need a separate `useSetup(...)` call.
58
59
  */
59
60
  declare function useSigma<T extends Sigma<any>>(...args: UseSigmaArgs<T>): Protected<T>;
60
61
  //#endregion
@@ -72,4 +73,17 @@ type PlainObject = Record<string, unknown>;
72
73
  */
73
74
  declare function useSigmaSync<TInstance extends Sigma<any>, TInput extends PlainObject>(instance: TInstance | Protected<TInstance>, input: TInput, sync: (input: Readonly<TInput>) => void): void;
74
75
  //#endregion
75
- export { type Draft, type Immutable, InferEventType, InferListener, Listenable, Protected, ReadableSigma, Sigma, SigmaDefinition, SigmaListenable, SigmaRef, SigmaState, SigmaTarget, UseSigmaArgs, UseSigmaOptions, castProtected, listen, mergeDefaults, query, setAutoFreeze, sigma, useListener, useSigma, useSigmaSync };
76
+ //#region src/hooks/useSetup.d.ts
77
+ /** Setup callback that returns resources owned by one effect run. */
78
+ type SetupCallback = () => readonly AnyResource[];
79
+ /**
80
+ * Runs a component setup effect and disposes returned resources in reverse order.
81
+ *
82
+ * The returned resources use the same cleanup protocol as `Sigma.setup(...)`.
83
+ * Use this for resources owned directly by a component. Components that create
84
+ * a sigma instance with `useSigma(...)` do not need `useSetup(...)` for that
85
+ * instance, because `useSigma(...)` runs `onSetup(...)` automatically.
86
+ */
87
+ declare function useSetup(setup: SetupCallback, deps?: readonly any[]): void;
88
+ //#endregion
89
+ export { type AnyResource, type Cleanup, type Draft, type Immutable, InferEventType, InferListener, Listenable, Protected, ReadableSigma, SetupCallback, Sigma, SigmaDefinition, SigmaListenable, SigmaRef, SigmaState, SigmaTarget, UseSigmaArgs, UseSigmaOptions, castProtected, listen, mergeDefaults, query, setAutoFreeze, sigma, useListener, useSetup, useSigma, useSigmaSync };
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, s as isPlainObject, t as Sigma } from "./sigma-DEKK3bHd.mjs";
1
+ import { a as setAutoFreeze, c as isPlainObject, i as query, l as listenersSymbol, n as SigmaTarget, o as sigma, r as castProtected, s as disposeResources, t as Sigma } from "./sigma-BH9WolKc.mjs";
2
2
  import { useEffect, useRef } from "preact/hooks";
3
3
  //#region src/defaults.ts
4
4
  /**
@@ -115,4 +115,20 @@ function useSigmaSync(instance, input, sync) {
115
115
  });
116
116
  }
117
117
  //#endregion
118
- export { Sigma, SigmaTarget, castProtected, listen, mergeDefaults, query, setAutoFreeze, sigma, useListener, useSigma, useSigmaSync };
118
+ //#region src/hooks/useSetup.ts
119
+ /**
120
+ * Runs a component setup effect and disposes returned resources in reverse order.
121
+ *
122
+ * The returned resources use the same cleanup protocol as `Sigma.setup(...)`.
123
+ * Use this for resources owned directly by a component. Components that create
124
+ * a sigma instance with `useSigma(...)` do not need `useSetup(...)` for that
125
+ * instance, because `useSigma(...)` runs `onSetup(...)` automatically.
126
+ */
127
+ function useSetup(setup, deps) {
128
+ useEffect(() => {
129
+ const resources = setup();
130
+ return () => disposeResources(resources);
131
+ }, deps);
132
+ }
133
+ //#endregion
134
+ export { Sigma, SigmaTarget, castProtected, listen, mergeDefaults, query, setAutoFreeze, sigma, useListener, useSetup, useSigma, useSigmaSync };
@@ -1,4 +1,4 @@
1
- import { n as ReadableSigma, p as Immutable, r as Sigma } from "./sigma-BfaJUPWw.mjs";
1
+ import { n as ReadableSigma, p as Immutable, r as Sigma } from "./sigma-CCBzhGdp.mjs";
2
2
 
3
3
  //#region src/persist.d.ts
4
4
  type MaybePromise<T> = T | Promise<T>;
package/dist/persist.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { o as sigma, s as isPlainObject } from "./sigma-DEKK3bHd.mjs";
1
+ import { c as isPlainObject, o as sigma } from "./sigma-BH9WolKc.mjs";
2
2
  //#region src/persist.ts
3
3
  function createIdentityCodec() {
4
4
  return {
@@ -33,6 +33,14 @@ var SigmaListenerMap = class extends Map {
33
33
  };
34
34
  //#endregion
35
35
  //#region src/internal/utils.ts
36
+ function disposeResource(resource) {
37
+ if (typeof resource === "function") resource();
38
+ else if ("dispose" in resource) resource.dispose();
39
+ else resource[Symbol.dispose]();
40
+ }
41
+ function disposeResources(resources) {
42
+ for (let i = resources.length - 1; i >= 0; i--) disposeResource(resources[i]);
43
+ }
36
44
  function isPlainObject(value) {
37
45
  if (typeof value !== "object" || value === null) return false;
38
46
  const prototype = Object.getPrototypeOf(value);
@@ -303,11 +311,6 @@ function initializeType(type) {
303
311
  initializedPrototypes.add(prototype);
304
312
  }
305
313
  }
306
- function disposeCleanupResource(resource) {
307
- if (typeof resource === "function") resource();
308
- else if ("dispose" in resource) resource.dispose();
309
- else resource[Symbol.dispose]();
310
- }
311
314
  function defineSignalProperty(instance, key, value) {
312
315
  Object.defineProperty(instance, key + signalSuffix, { value: signal(value) });
313
316
  if (!Object.hasOwn(instance.constructor.prototype, key)) Object.defineProperty(instance.constructor.prototype, key, {
@@ -355,7 +358,7 @@ var Sigma = class {
355
358
  activeSetupInstance = previousSetupInstance;
356
359
  }
357
360
  return () => {
358
- for (let i = resources.length - 1; i >= 0; i--) disposeCleanupResource(resources[i]);
361
+ disposeResources(resources);
359
362
  };
360
363
  }
361
364
  /**
@@ -418,22 +421,23 @@ var SigmaTarget = class SigmaTarget extends Sigma {
418
421
  /** Helpers for observing, accessing, capturing, and replacing committed sigma state. */
419
422
  const sigma = /* @__PURE__ */ Object.freeze({
420
423
  subscribe: ((instance, keyOrListener, listenerOrOptions) => {
424
+ const source = instance;
421
425
  if (typeof keyOrListener === "string") {
422
- const signal = getStateSignal(instance, keyOrListener);
426
+ const signal = getStateSignal(source, keyOrListener);
423
427
  if (!signal) throw new Error(`[preact-sigma] Property named "${keyOrListener}" is not signal-backed.`);
424
428
  return signal.subscribe(listenerOrOptions);
425
429
  }
426
430
  const listener = keyOrListener;
427
431
  if (listenerOrOptions?.patches) patchListeners.add(listener);
428
- let subscriptions = changeListenersMap.get(instance);
432
+ let subscriptions = changeListenersMap.get(source);
429
433
  if (!subscriptions) {
430
434
  subscriptions = /* @__PURE__ */ new Set();
431
- changeListenersMap.set(instance, subscriptions);
435
+ changeListenersMap.set(source, subscriptions);
432
436
  }
433
437
  subscriptions.add(listener);
434
438
  return () => {
435
439
  subscriptions.delete(listener);
436
- if (!subscriptions.size) changeListenersMap.delete(instance);
440
+ if (!subscriptions.size) changeListenersMap.delete(source);
437
441
  };
438
442
  }),
439
443
  getSignal(instance, key) {
@@ -471,4 +475,4 @@ function query(method) {
471
475
  return queryMethod;
472
476
  }
473
477
  //#endregion
474
- export { setAutoFreeze as a, listenersSymbol as c, query as i, SigmaTarget as n, sigma as o, castProtected as r, isPlainObject as s, Sigma as t };
478
+ export { setAutoFreeze as a, isPlainObject as c, query as i, listenersSymbol as l, SigmaTarget as n, sigma as o, castProtected as r, disposeResources as s, Sigma as t };
@@ -3,7 +3,9 @@ import { Patch } from "immer";
3
3
 
4
4
  //#region src/internal/utils.d.ts
5
5
  type AnyFunction = (...args: any[]) => any;
6
+ /** Function cleanup returned from setup-owned resources. */
6
7
  type Cleanup = () => void;
8
+ /** Cleanup resource returned from setup hooks and disposed during teardown. */
7
9
  type AnyResource = Cleanup | {
8
10
  dispose(): void;
9
11
  } | {
@@ -141,14 +143,14 @@ declare class SigmaTarget<TEvents extends object = {}, TState extends object = {
141
143
  /** Helpers for observing, accessing, capturing, and replacing committed sigma state. */
142
144
  declare const sigma: Readonly<{
143
145
  /** Subscribes to committed state publishes or to one signal-backed top-level state key. */subscribe: {
144
- <TState extends object>(instance: Sigma<TState>, listener: (nextState: Immutable<TState>, baseState: Immutable<TState>, patches: Patch[], inversePatches: Patch[]) => void, options: {
146
+ <TState extends object>(instance: ReadableSigma<TState>, listener: (nextState: Immutable<TState>, baseState: Immutable<TState>, patches: Patch[], inversePatches: Patch[]) => void, options: {
145
147
  patches: true;
146
148
  }): Cleanup;
147
- <TState extends object>(instance: Sigma<TState>, listener: (nextState: Immutable<TState>, baseState: Immutable<TState>, patches: Patch[] | undefined, inversePatches: Patch[] | undefined) => void, options: {
149
+ <TState extends object>(instance: ReadableSigma<TState>, listener: (nextState: Immutable<TState>, baseState: Immutable<TState>, patches: Patch[] | undefined, inversePatches: Patch[] | undefined) => void, options: {
148
150
  patches: boolean;
149
151
  }): Cleanup;
150
- <TState extends object>(instance: Sigma<TState>, listener: (nextState: Immutable<TState>, baseState: Immutable<TState>) => void): Cleanup;
151
- <TState extends object, TKey extends Extract<keyof TState, string>>(instance: Sigma<TState>, key: TKey, listener: (value: Immutable<TState[TKey]>) => void): Cleanup;
152
+ <TState extends object>(instance: ReadableSigma<TState>, listener: (nextState: Immutable<TState>, baseState: Immutable<TState>) => void): Cleanup;
153
+ <TState extends object, TKey extends Extract<keyof TState, string>>(instance: ReadableSigma<TState>, key: TKey, listener: (value: Immutable<TState[TKey]>) => void): Cleanup;
152
154
  }; /** Returns the readonly signal backing one top-level state key. */
153
155
  getSignal<TState extends object, TKey extends Extract<keyof TState, string>>(instance: Sigma<TState>, key: TKey): ReadonlySignal<Immutable<TState[TKey]>>; /** Captures the current committed top-level state snapshot. */
154
156
  captureState<TState extends object>(instance: ReadableSigma<TState>): Immutable<TState>; /** Publishes a plain-object snapshot, including readonly captured snapshots, as committed state. */
@@ -172,4 +174,4 @@ type Protected<T extends Sigma<any>> = BrandProtected<T extends {
172
174
  };
173
175
  } ? { readonly [K in keyof T as K extends ProtectedKey ? never : K]: K extends typeof typeSymbol ? T[K] : K extends keyof TState ? Immutable<T[K]> : T[K] extends AnyFunction ? (...params: Parameters<T[K]>) => Immutable<ReturnType<T[K]>> : Immutable<T[K]> } : never>;
174
176
  //#endregion
175
- export { SigmaRef as a, castProtected as c, sigma as d, Draft as f, Cleanup as h, SigmaDefinition as i, query as l, typeSymbol as m, ReadableSigma as n, SigmaState as o, Immutable as p, Sigma as r, SigmaTarget as s, Protected as t, setAutoFreeze as u };
177
+ export { SigmaRef as a, castProtected as c, sigma as d, Draft as f, Cleanup as g, AnyResource as h, SigmaDefinition as i, query as l, typeSymbol as m, ReadableSigma as n, SigmaState as o, Immutable as p, Sigma as r, SigmaTarget as s, Protected as t, setAutoFreeze as u };
package/docs/context.md CHANGED
@@ -53,9 +53,10 @@
53
53
  - Publish unpublished changes before `await`, `emit(...)`, promise resolution, or another instance's action: `this.commit()`.
54
54
  - React to committed state changes: `sigma.subscribe(instance, handler)` or `sigma.subscribe(instance, key, handler)`.
55
55
  - Read one top-level state property as a `ReadonlySignal`: `sigma.getSignal(instance, key)`.
56
- - Own timers, listeners, subscriptions, or nested setup: `onSetup(...)` plus `setup(...)`.
57
- - Use a sigma instance inside a component: `useSigma(...)`.
56
+ - Own model lifecycle resources: `onSetup(...)` plus `setup(...)`.
57
+ - Use a component-owned sigma instance: `useSigma(...)`, which runs `onSetup(...)` automatically.
58
58
  - Synchronize changed component data into a sigma instance after the initial render: `useSigmaSync(instance, input, sync)`.
59
+ - Own component-local setup resources: `useSetup(...)`.
59
60
  - Cast an instance to its readonly consumer view outside a component: `castProtected(instance)`.
60
61
  - Subscribe to sigma or DOM events in a component: `useListener(...)`.
61
62
  - Subscribe outside components: `listen(instance, ...)`.
@@ -72,7 +73,9 @@
72
73
  - Use ordinary actions for routine writes. Reserve `sigma.captureState(...)` and `sigma.replaceState(...)` for replay, reset, restore, or undo-like flows on committed top-level state.
73
74
  - Emit directly from standalone `SigmaTarget` instances. In subclasses, emit from actions that have no unpublished draft changes. After mutating state, publish first with `this.commit(); this.emit(...)`.
74
75
  - Prefer `listen(...)` for external event subscriptions. It works with sigma targets and DOM targets.
75
- - Put owned side effects in `onSetup(...)`.
76
+ - Put model-owned side effects in `onSetup(...)`.
77
+ - Use `useSetup(...)` when a component owns setup resources directly. The callback returns cleanup resources, and teardown disposes them in reverse order.
78
+ - A sigma instance created with `useSigma(...)` already runs its `onSetup(...)` method. Do not call `instance.setup(...)` for that same instance from `useSetup(...)`, because that starts a second setup lifecycle.
76
79
  - Use `useSigmaSync(...)` when a component-owned sigma instance is initialized from external props or hook data and needs to receive later changes through an action. Pass a plain object with stable keys; values are compared with `Object.is(...)`, and a recreated instance treats the current input as its new baseline.
77
80
  - Use `sigma.subscribe(this, ...)` inside `onSetup(...)` when a setup-owned side effect should react to future committed publishes. Return that cleanup so the subscription stops with setup.
78
81
  ```ts
@@ -91,6 +94,7 @@
91
94
  - Reaching for `sigma.getSignal(instance, key)` when direct property reads already cover the use case.
92
95
  - Crossing `emit(...)`, `await`, promise resolution, or another instance's action with unpublished changes. Publish them first with `this.commit()`.
93
96
  - Starting side effects during construction instead of through explicit `setup(...)`.
97
+ - Calling `setup(...)` manually for an instance created by `useSigma(...)`.
94
98
  - Encoding storage, hydration, or migration policy directly into model classes.
95
99
  - Relying on computeds or queries to observe unpublished draft changes inside actions.
96
100
  - Treating query calls as memoized across invocations.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "preact-sigma",
3
- "version": "6.2.0",
3
+ "version": "6.3.0",
4
4
  "keywords": [],
5
5
  "license": "MIT",
6
6
  "author": "Alec Larson",