preact-sigma 2.3.0 → 3.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 CHANGED
@@ -2,9 +2,55 @@ import { ReadonlySignal, action, batch, computed, effect, untracked } from "@pre
2
2
  import { Patch, freeze, immerable } from "immer";
3
3
 
4
4
  //#region src/internal/symbols.d.ts
5
+ declare const signalPrefix = "#";
6
+ declare const sigmaTypeBrand: unique symbol;
5
7
  declare const sigmaStateBrand: unique symbol;
6
8
  declare const sigmaEventsBrand: unique symbol;
7
9
  declare const sigmaRefBrand: unique symbol;
10
+ declare const reservedKeys: Set<string>;
11
+ //#endregion
12
+ //#region src/listener.d.ts
13
+ type InferEventMap<TTarget extends EventTarget> = TTarget extends {
14
+ [sigmaEventsBrand]: infer TEvents extends AnyEvents;
15
+ } ? TEvents : TTarget extends Window ? WindowEventMap : TTarget extends Document ? DocumentEventMap : TTarget extends HTMLBodyElement ? HTMLBodyElementEventMap : TTarget extends HTMLMediaElement ? HTMLMediaElementEventMap : TTarget extends HTMLElement ? HTMLElementEventMap : TTarget extends SVGSVGElement ? SVGSVGElementEventMap : TTarget extends SVGElement ? SVGElementEventMap : never;
16
+ type InferListenerArgs<TEvents extends object, TTarget extends EventTarget, TEvent extends string> = [(TEvent extends keyof TEvents ? TEvents[TEvent] : never) extends infer TPayload ? TTarget extends {
17
+ [sigmaEventsBrand]: TEvents;
18
+ } ? [TPayload] extends [never] ? never : [TPayload] extends [void] ? undefined : TPayload : ([TPayload] extends [never] ? CustomEvent : Extract<TPayload, Event>) & {
19
+ readonly currentTarget: TTarget;
20
+ } : never];
21
+ /** Infers the listener callback shape for a target and event name. Sigma states receive payloads directly, while DOM targets receive typed events. */
22
+ type InferListener<TTarget extends EventTarget, TEvent extends string = string> = InferEventMap<TTarget> extends infer TEvents extends object ? ((...args: InferListenerArgs<TEvents, TTarget, TEvent>) => void) & {
23
+ __eventType?: TEvent;
24
+ } : never;
25
+ /** Infers the event names accepted by `listen(...)` or `useListener(...)` for a target. */
26
+ type InferEventType<TTarget extends EventTarget> = (InferListener<TTarget> extends {
27
+ __eventType?: infer TEvent;
28
+ } ? string & TEvent : never) | (string & {});
29
+ /** Infers the detail parameter for a typed emit. */
30
+ type EventParameters<T> = [void] extends [T] ? [detail?: T extends void ? undefined : T] : [undefined] extends [T] ? [detail?: T] : [detail: T];
31
+ /**
32
+ * A standalone typed event hub with `emit(...)` and `on(...)` methods and full
33
+ * `EventTarget`, `listen(...)`, and `useListener(...)` compatibility.
34
+ */
35
+ declare class SigmaTarget<TEvents extends AnyEvents = {}> extends EventTarget {
36
+ readonly [sigmaEventsBrand]: TEvents;
37
+ /**
38
+ * Emits a typed event from the hub.
39
+ *
40
+ * Void events dispatch a plain `Event`. Payload events dispatch a
41
+ * `CustomEvent` whose `detail` holds the payload.
42
+ */
43
+ emit<TEvent extends string & keyof TEvents>(name: TEvent, ...[detail]: EventParameters<TEvents[TEvent]>): void;
44
+ /**
45
+ * Registers a typed event listener and returns an unsubscribe function.
46
+ *
47
+ * Payload events pass their payload directly to the listener. Void events
48
+ * call the listener with no arguments.
49
+ */
50
+ on<TEvent extends string & keyof TEvents>(name: TEvent, listener: (...args: InferListenerArgs<TEvents, this, TEvent>) => void): () => void;
51
+ }
52
+ /** Adds a listener to a sigma state or DOM target and returns a cleanup function that removes it. */
53
+ declare function listen<TTarget extends EventTarget, TEvent extends InferEventType<TTarget>>(target: TTarget, name: TEvent, listener: InferListener<TTarget, TEvent>): () => void;
8
54
  //#endregion
9
55
  //#region src/immer.d.ts
10
56
  type PrimitiveType = number | string | boolean;
@@ -40,6 +86,7 @@ type Draft<T> = T extends PrimitiveType ? T : T extends AtomicObject ? T : HasSi
40
86
  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;
41
87
  //#endregion
42
88
  //#region src/internal/types.d.ts
89
+ type Def = typeof sigmaTypeBrand;
43
90
  type AnyFunction = (...args: any[]) => any;
44
91
  type Cleanup = () => void;
45
92
  type DefaultStateInitializer<TValue> = (this: void) => TValue;
@@ -60,30 +107,35 @@ type AnyEvents = Record<string, object | void>;
60
107
  /** The top-level state object shape used by sigma types. */
61
108
  type AnyState = Record<string, unknown>;
62
109
  /** The object accepted by `.defaultState(...)`, where each property may be a value or a zero-argument initializer. */
63
- type AnyDefaultState<TState extends AnyState> = { [K in keyof TState]?: DefaultStateValue<TState[K]> };
110
+ type AnyDefaultState<TState extends object> = { [K in keyof TState]?: DefaultStateValue<TState[K]> };
64
111
  /** A cleanup resource supported by `.setup(...)`, including function, `dispose()`, and `Symbol.dispose` cleanup. */
65
112
  type AnyResource = Cleanup | Disposable | DisposableLike | AbortController;
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 };
67
- type ComputedContext<TState extends AnyState, TComputeds extends object> = Immutable<TState> & ComputedValues<TComputeds>;
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 };
69
- type ActionMethods<TActions extends object | undefined> = [undefined] extends [TActions] ? never : { [K in keyof TActions]: TActions[K] extends AnyFunction ? (...args: Parameters<TActions[K]>) => ReturnType<TActions[K]> : never };
70
- type EventMethods<TEvents extends AnyEvents | undefined> = [undefined] extends [TEvents] ? never : {
71
- readonly [sigmaEventsBrand]: TEvents; /** Registers a typed event listener and returns an unsubscribe function. */
72
- on<TEvent extends string & keyof TEvents>(name: TEvent, listener: [TEvents[TEvent]] extends [void] ? () => void : (payload: TEvents[TEvent]) => void): Cleanup;
73
- };
74
- type SetupMethods<TSetupArgs extends any[] | undefined> = [TSetupArgs] extends [undefined] ? never : {
75
- /** Runs every registered setup handler and returns one cleanup function for the active setup. */setup(...args: Extract<TSetupArgs, any[]>): Cleanup;
76
- };
77
- type ReadonlyContext<TState extends AnyState, TComputeds extends object, TQueries extends object> = Immutable<TState> & ComputedValues<TComputeds> & QueryMethods<TQueries>;
78
- type Emit<TEvents extends AnyEvents> = <TEvent extends string & keyof TEvents>(name: TEvent, ...args: [TEvents[TEvent]] extends [void] ? [] : [payload: TEvents[TEvent]]) => void;
79
- type ActionContext<TState extends AnyState, TEvents extends AnyEvents, TComputeds extends object, TQueries extends object, TActions extends object> = Draft<TState> & ComputedValues<TComputeds> & QueryMethods<TQueries> & ActionMethods<TActions> & {
113
+ type ComputedValues<TComputeds> = { readonly [K in keyof TComputeds]: TComputeds[K] extends AnyFunction ? Immutable<ReturnType<TComputeds[K]>> : never };
114
+ type QueryMethods<TQueries> = { [K in keyof TQueries]: TQueries[K] extends AnyFunction ? (...args: Parameters<TQueries[K]>) => ReturnType<TQueries[K]> : never };
115
+ type ActionMethods<TActions> = { [K in keyof TActions]: TActions[K] extends AnyFunction ? (...args: Parameters<TActions[K]>) => ReturnType<TActions[K]> : never };
116
+ type MergeObjects<TLeft, TRight, TConstraint extends object = object> = TRight extends TConstraint ? TLeft extends TConstraint ? Simplify<Omit<TLeft, keyof TRight> & TRight> : TRight : TLeft extends TConstraint ? TLeft : {};
117
+ type Simplify<T> = {} & { [K in keyof T]: T[K] };
118
+ type ComputedContext<T extends AnySigmaType, TOverrides extends Partial<SigmaDefinition> = {}> = Simplify<Immutable<MergeObjects<T[Def]["state"], TOverrides["state"]>> & ComputedValues<MergeObjects<T[Def]["computeds"], TOverrides["computeds"]>>>;
119
+ type ReadonlyContext<T extends AnySigmaType, TOverrides extends Partial<SigmaDefinition> = {}> = Simplify<Immutable<MergeObjects<T[Def]["state"], TOverrides["state"]>> & ComputedValues<MergeObjects<T[Def]["computeds"], TOverrides["computeds"]>> & QueryMethods<MergeObjects<T[Def]["queries"], TOverrides["queries"]>>>;
120
+ type Emit<T extends AnySigmaType, TOverrides extends Partial<SigmaDefinition> = {}> = MergeObjects<T[Def]["events"], TOverrides["events"], AnyEvents> extends infer TEvents ? [TEvents] extends [AnyEvents] ? <TEvent extends string & keyof TEvents>(name: TEvent, ...[detail]: EventParameters<TEvents[TEvent]>) => void : never : never;
121
+ type ActionContext<T extends AnySigmaType, TOverrides extends Partial<SigmaDefinition> = {}> = Simplify<Draft<MergeObjects<T[Def]["state"], TOverrides["state"]>> & ComputedValues<MergeObjects<T[Def]["computeds"], TOverrides["computeds"]>> & QueryMethods<MergeObjects<T[Def]["queries"], TOverrides["queries"]>> & ActionMethods<MergeObjects<T[Def]["actions"], TOverrides["actions"]>> & {
80
122
  /** Publishes the current action draft immediately so later boundaries use committed state. */commit(): void; /** Emits a typed event from the current action. */
81
- emit: Emit<TEvents>;
123
+ emit: Emit<T, TOverrides>;
124
+ }>;
125
+ type SetupContext<T extends AnySigmaType, TOverrides extends Partial<SigmaDefinition> = {}> = Simplify<SigmaState<T[Def] & TOverrides> & {
126
+ /** Runs a synchronous anonymous action from setup so reads and writes use normal action semantics. */act<TResult>(fn: (this: ActionContext<T>) => TResult): TResult; /** Emits a typed event from setup. */
127
+ emit: Emit<T, TOverrides>;
128
+ }>;
129
+ type AnySigmaType = SigmaType<any, any, any> & {
130
+ readonly [sigmaTypeBrand]: {
131
+ state: object;
132
+ events?: object;
133
+ computeds?: object;
134
+ queries?: object;
135
+ actions?: object;
136
+ setupArgs?: any[];
137
+ };
82
138
  };
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"] : {};
87
139
  /** The public shape shared by all sigma-state instances. */
88
140
  interface AnySigmaState extends EventTarget {
89
141
  readonly [sigmaStateBrand]: true;
@@ -116,21 +168,25 @@ interface SignalAccessors<T extends object> {
116
168
  /** Returns the underlying signal for a top-level state property or computed. */
117
169
  get<K extends keyof T>(key: K): ReadonlySignal<T[K]>;
118
170
  }
119
- type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
120
- type Simplify<T> = {} & { [K in keyof T]: T[K] };
171
+ type EventMethods<TEvents extends AnyEvents | undefined> = [undefined] extends [TEvents] ? never : {
172
+ readonly [sigmaEventsBrand]: TEvents; /** Registers a typed event listener and returns an unsubscribe function. */
173
+ on<TEvent extends string & keyof TEvents>(name: TEvent, listener: (...[detail]: EventParameters<TEvents[TEvent]>) => void): Cleanup;
174
+ };
175
+ type SetupMethods<TSetupArgs extends any[] | undefined> = [TSetupArgs] extends [undefined] ? never : {
176
+ /** Runs every registered setup handler and returns one cleanup function for the active setup. */setup(...args: Extract<TSetupArgs, any[]>): Cleanup;
177
+ };
121
178
  type MapSigmaDefinition<T extends SigmaDefinition> = keyof T extends infer K ? K extends "state" ? Immutable<T[K]> & SignalAccessors<Immutable<T[K]>> : K extends "computeds" ? ComputedValues<T[K]> & SignalAccessors<ComputedValues<T[K]>> : K extends "queries" ? QueryMethods<T[K]> : K extends "actions" ? ActionMethods<T[K]> : K extends "events" ? EventMethods<T[K]> : K extends "setupArgs" ? SetupMethods<T[K]> : never : never;
179
+ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
122
180
  /** The public instance shape produced by a configured sigma type, including signal access inferred from the definition. */
123
181
  type SigmaState<T extends SigmaDefinition = SigmaDefinition> = AnySigmaState & Simplify<UnionToIntersection<MapSigmaDefinition<T>>>;
124
- type SetupContext<T extends SigmaDefinition> = SigmaState<T> & {
125
- /** Runs a synchronous anonymous action from setup so reads and writes use normal action semantics. */act<TResult>(fn: (this: ActionContext<T["state"], DefinitionEvents<T>, DefinitionComputeds<T>, DefinitionQueries<T>, DefinitionActions<T>>) => TResult): TResult; /** Emits a typed event from setup. */
126
- emit: T["events"] extends object ? Emit<T["events"]> : never;
127
- };
128
- type MergeObjects<TLeft extends object, TRight> = [TRight] extends [object] ? Extract<Simplify<Omit<TLeft, keyof TRight> & TRight>, TLeft> : TLeft;
129
182
  type RequiredKeys<TObject extends object> = { [K in keyof TObject]-?: {} extends Pick<TObject, K> ? never : K }[keyof TObject];
130
- type MissingInitialKeys<TState extends AnyState, TDefaults extends AnyDefaultState<TState> | undefined> = Exclude<RequiredKeys<TState>, keyof NonNullable<TDefaults>>;
131
- type InitialStateInput<TState extends AnyState, TDefaults extends AnyDefaultState<TState> | undefined> = [MissingInitialKeys<TState, TDefaults>] extends [never] ? [initialState?: Partial<TState>] : [initialState: Pick<TState, MissingInitialKeys<TState, TDefaults>> & Partial<TState>];
132
- type Omit<T, K> = {} & { [P in Exclude<keyof T, K>]: T[P] };
133
- type OmitEmpty<T extends object> = {} & Omit<T, { [K in keyof T]: [undefined] extends [T[K]] ? K : [{}] extends [T[K]] ? K : never }[keyof T]>;
183
+ type MissingInitialKeys<TState extends AnyState, TDefaults extends object> = Exclude<RequiredKeys<TState>, keyof TDefaults>;
184
+ type InitialStateInput<TState extends AnyState, TDefaults extends object> = [MissingInitialKeys<TState, TDefaults>] extends [never] ? [initialState?: Partial<TState>] : [initialState: Pick<TState, MissingInitialKeys<TState, TDefaults>> & Partial<TState>];
185
+ type ExtendSigmaType<T extends SigmaType<any, any, any>, TExtension extends Partial<SigmaDefinition>> = T & {
186
+ readonly [sigmaTypeBrand]: T[Def] & TExtension;
187
+ };
188
+ type OmitEmpty<T extends object> = Omit<T, { [K in keyof T]: [undefined] extends [T[K]] ? K : [{}] extends [T[K]] ? K : never }[keyof T]>;
189
+ type InferSigmaDefinition<T extends SigmaType<any, any, any>> = Extract<Simplify<OmitEmpty<T[Def]>>, SigmaDefinition>;
134
190
  /** Infers the `setup(...)` argument list for a sigma-state instance. */
135
191
  type InferSetupArgs<T extends AnySigmaState> = T extends {
136
192
  setup(...args: infer TArgs extends any[]): Cleanup;
@@ -176,53 +232,30 @@ declare function query<TArgs extends any[], TResult>(fn: (this: void, ...args: T
176
232
  * State and event inference starts from `new SigmaType<TState, TEvents>()`.
177
233
  * Later builder methods infer names and types from the objects you pass to them.
178
234
  */
179
- declare class SigmaType<TState extends AnyState, TEvents extends AnyEvents = {}, TDefaults extends AnyDefaultState<TState> = {}, TComputeds extends object = {}, TQueries extends object = {}, TActions extends object = {}, TSetupArgs extends any[] = never> extends Function {
180
- constructor(name?: string);
181
- }
182
- /** The constructor shape exposed by a configured sigma type. */
183
- interface SigmaType<TState extends AnyState, TEvents extends AnyEvents, TDefaults extends AnyDefaultState<TState>, TComputeds extends object, TQueries extends object, TActions extends object, TSetupArgs extends any[]> {
184
- /**
185
- * Creates a sigma-state instance.
186
- *
187
- * Constructor input shallowly overrides `defaultState(...)`. Required keys are
188
- * inferred from whichever state properties still do not have defaults.
189
- */
190
- new (...args: InitialStateInput<TState, TDefaults>): SigmaState<Extract<OmitEmpty<{
235
+ declare class SigmaType<TState extends AnyState, TEvents extends AnyEvents = {}, TDefaults extends AnyDefaultState<TState> = {}> extends Function {
236
+ readonly [sigmaTypeBrand]: {
191
237
  state: TState;
192
238
  events: TEvents;
193
- computeds: TComputeds;
194
- queries: TQueries;
195
- actions: TActions;
196
- setupArgs: TSetupArgs;
197
- }>, SigmaDefinition>>;
198
- /**
199
- * Type-only access to the configured instance shape.
200
- *
201
- * This property does not exist at runtime. Its type is inferred from the
202
- * generics on `new SigmaType<TState, TEvents>()` plus the later builder inputs.
203
- */
204
- get Instance(): SigmaState<Extract<OmitEmpty<{
205
- state: TState;
206
- events: TEvents;
207
- computeds: TComputeds;
208
- queries: TQueries;
209
- actions: TActions;
210
- setupArgs: TSetupArgs;
211
- }>, SigmaDefinition>>;
239
+ };
240
+ constructor(name?: string);
212
241
  /**
213
242
  * Adds top-level public state and default values to the builder.
214
243
  *
215
244
  * Each property becomes a reactive public state property on instances. Use a
216
245
  * zero-argument function when each instance needs a fresh object or array.
217
246
  */
218
- defaultState<TNextDefaults extends AnyDefaultState<TState>>(defaultState: TNextDefaults): SigmaType<TState, TEvents, MergeObjects<TDefaults, TNextDefaults>, TComputeds, TQueries, TActions, TSetupArgs>;
247
+ defaultState<TDefaults extends AnyDefaultState<TState>>(defaultState: TDefaults): SigmaType<TState, TEvents, TDefaults>;
219
248
  /**
220
249
  * Adds reactive getter properties for derived values that take no arguments.
221
250
  *
222
251
  * Computed names and return types are inferred from the object you pass.
223
252
  * `this` exposes readonly state plus computeds that are already on the builder.
224
253
  */
225
- computed<TNextComputeds extends object>(computeds: TNextComputeds & ThisType<ComputedContext<TState, MergeObjects<TComputeds, TNextComputeds>>>): SigmaType<TState, TEvents, TDefaults, MergeObjects<TComputeds, TNextComputeds>, TQueries, TActions, TSetupArgs>;
254
+ computed<TComputeds extends object>(computeds: TComputeds & ThisType<ComputedContext<this, {
255
+ computeds: TComputeds;
256
+ }>>): ExtendSigmaType<this, {
257
+ computeds: TComputeds;
258
+ }>;
226
259
  /**
227
260
  * Adds reactive read methods that accept arguments.
228
261
  *
@@ -230,17 +263,21 @@ interface SigmaType<TState extends AnyState, TEvents extends AnyEvents, TDefault
230
263
  * pass. Each call tracks reactively at the call site and does not memoize
231
264
  * results across invocations.
232
265
  */
233
- queries<TNextQueries extends object>(queries: TNextQueries & ThisType<ReadonlyContext<TState, TComputeds, MergeObjects<TQueries, TNextQueries>>>): SigmaType<TState, TEvents, TDefaults, TComputeds, MergeObjects<TQueries, TNextQueries>, TActions, TSetupArgs>;
266
+ queries<TQueries extends object>(queries: TQueries & ThisType<ReadonlyContext<this, {
267
+ queries: TQueries;
268
+ }>>): ExtendSigmaType<this, {
269
+ queries: TQueries;
270
+ }>;
234
271
  /**
235
272
  * Adds a committed-state observer.
236
273
  *
237
274
  * Observers run after successful publishes and can opt into Immer patches
238
275
  * with `{ patches: true }`.
239
276
  */
240
- observe(listener: (this: ReadonlyContext<TState, TComputeds, TQueries>, change: SigmaObserveChange<TState>) => void, options?: SigmaObserveOptions & {
277
+ observe(listener: (this: ReadonlyContext<this>, change: SigmaObserveChange<TState>) => void, options?: SigmaObserveOptions & {
241
278
  patches?: false | undefined;
242
279
  }): this;
243
- observe(listener: (this: ReadonlyContext<TState, TComputeds, TQueries>, change: SigmaObserveChange<TState, true>) => void, options: SigmaObserveOptions & {
280
+ observe(listener: (this: ReadonlyContext<this>, change: SigmaObserveChange<TState, true>) => void, options: SigmaObserveOptions & {
244
281
  patches: true;
245
282
  }): this;
246
283
  /**
@@ -254,63 +291,39 @@ interface SigmaType<TState extends AnyState, TEvents extends AnyEvents, TDefault
254
291
  * writes made after `await`. Non-async actions stay synchronous; if one
255
292
  * returns a promise, sigma throws so async boundaries stay explicit.
256
293
  */
257
- actions<TNextActions extends object>(actions: TNextActions & ThisType<ActionContext<TState, TEvents, TComputeds, TQueries, MergeObjects<TActions, TNextActions>>>): SigmaType<TState, TEvents, TDefaults, TComputeds, TQueries, MergeObjects<TActions, TNextActions>, TSetupArgs>;
294
+ actions<TActions extends object>(actions: TActions & ThisType<ActionContext<this, {
295
+ actions: TActions;
296
+ }>>): ExtendSigmaType<this, {
297
+ actions: TActions;
298
+ }>;
258
299
  /**
259
300
  * Adds an explicit setup handler for side effects and owned resources.
260
301
  *
261
302
  * Every registered handler runs when `instance.setup(...)` is called, and the
262
303
  * setup argument list is inferred from the first handler you add.
263
304
  */
264
- setup<TNextSetupArgs extends ([TSetupArgs] extends [never] ? any[] : NonNullable<TSetupArgs>)>(setup: (this: SetupContext<{
265
- state: TState;
266
- events: TEvents;
267
- computeds: TComputeds;
268
- queries: TQueries;
269
- actions: TActions;
270
- setupArgs: TNextSetupArgs;
271
- }>, ...args: TNextSetupArgs) => readonly AnyResource[]): SigmaType<TState, TEvents, TDefaults, TComputeds, TQueries, TActions, TNextSetupArgs>;
305
+ setup<TSetupArgs extends any[]>(setup: (this: SetupContext<this, {
306
+ setupArgs: TSetupArgs;
307
+ }>, ...args: TSetupArgs) => readonly AnyResource[]): ExtendSigmaType<this, {
308
+ setupArgs: TSetupArgs;
309
+ }>;
272
310
  }
273
- //#endregion
274
- //#region src/listener.d.ts
275
- type InferEventMap<TTarget extends EventTarget> = TTarget extends {
276
- [sigmaEventsBrand]: infer TEvents extends AnyEvents;
277
- } ? TEvents : TTarget extends Window ? WindowEventMap : TTarget extends Document ? DocumentEventMap : TTarget extends HTMLBodyElement ? HTMLBodyElementEventMap : TTarget extends HTMLMediaElement ? HTMLMediaElementEventMap : TTarget extends HTMLElement ? HTMLElementEventMap : TTarget extends SVGSVGElement ? SVGSVGElementEventMap : TTarget extends SVGElement ? SVGElementEventMap : never;
278
- type InferListenerArgs<TEvents extends object, TTarget extends EventTarget, TEvent extends string> = [(TEvent extends keyof TEvents ? TEvents[TEvent] : never) extends infer TPayload ? TTarget extends {
279
- [sigmaEventsBrand]: TEvents;
280
- } ? [TPayload] extends [never] ? never : [TPayload] extends [void] ? undefined : TPayload : ([TPayload] extends [never] ? CustomEvent : Extract<TPayload, Event>) & {
281
- readonly currentTarget: TTarget;
282
- } : never];
283
- /** Infers the listener callback shape for a target and event name. Sigma states receive payloads directly, while DOM targets receive typed events. */
284
- type InferListener<TTarget extends EventTarget, TEvent extends string = string> = InferEventMap<TTarget> extends infer TEvents extends object ? ((...args: InferListenerArgs<TEvents, TTarget, TEvent>) => void) & {
285
- __eventType?: TEvent;
286
- } : never;
287
- /** Infers the event names accepted by `listen(...)` or `useListener(...)` for a target. */
288
- type InferEventType<TTarget extends EventTarget> = (InferListener<TTarget> extends {
289
- __eventType?: infer TEvent;
290
- } ? string & TEvent : never) | (string & {});
291
- /**
292
- * A standalone typed event hub with `emit(...)` and `on(...)` methods and full
293
- * `EventTarget`, `listen(...)`, and `useListener(...)` compatibility.
294
- */
295
- declare class SigmaTarget<TEvents extends AnyEvents = {}> extends EventTarget {
296
- readonly [sigmaEventsBrand]: TEvents;
311
+ interface SigmaType<TState extends AnyState, TEvents extends AnyEvents, TDefaults extends AnyDefaultState<TState>> {
297
312
  /**
298
- * Emits a typed event from the hub.
313
+ * Type-only access to the configured instance shape.
299
314
  *
300
- * Void events dispatch a plain `Event`. Payload events dispatch a
301
- * `CustomEvent` whose `detail` holds the payload.
315
+ * This property does not exist at runtime. Its type is inferred from the
316
+ * generics on `new SigmaType<TState, TEvents>()` plus the later builder inputs.
302
317
  */
303
- emit<TEvent extends string & keyof TEvents>(name: TEvent, ...args: [TEvents[TEvent]] extends [void] ? [] : [payload: TEvents[TEvent]]): void;
318
+ get Instance(): SigmaState<InferSigmaDefinition<this>>;
304
319
  /**
305
- * Registers a typed event listener and returns an unsubscribe function.
320
+ * Creates a sigma-state instance.
306
321
  *
307
- * Payload events pass their payload directly to the listener. Void events
308
- * call the listener with no arguments.
322
+ * Constructor input shallowly overrides `defaultState(...)`. Required keys are
323
+ * inferred from whichever state properties still do not have defaults.
309
324
  */
310
- on<TEvent extends string & keyof TEvents>(name: TEvent, listener: (...args: InferListenerArgs<TEvents, this, TEvent>) => void): () => void;
325
+ new (...args: InitialStateInput<TState, TDefaults>): this["Instance"];
311
326
  }
312
- /** Adds a listener to a sigma state or DOM target and returns a cleanup function that removes it. */
313
- declare function listen<TTarget extends EventTarget, TEvent extends InferEventType<TTarget>>(target: TTarget, name: TEvent, listener: InferListener<TTarget, TEvent>): () => void;
314
327
  //#endregion
315
328
  //#region src/hooks.d.ts
316
329
  /**
@@ -329,4 +342,4 @@ declare function useSigma<T extends AnySigmaState>(create: () => T, setupArgs?:
329
342
  */
330
343
  declare function useListener<TTarget extends EventTarget | AnySigmaState, TEvent extends InferEventType<TTarget>>(target: TTarget | null, name: TEvent, listener: InferListener<TTarget, TEvent>): void;
331
344
  //#endregion
332
- 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, SigmaTarget, SigmaType, action, batch, computed, effect, freeze, immerable, isSigmaState, listen, query, replaceState, setAutoFreeze, snapshot, untracked, useListener, useSigma };
345
+ export { type AnyDefaultState, type AnyEvents, type AnyResource, type AnySigmaState, type AnySigmaStateWithEvents, type AnyState, EventParameters, InferEventType, InferListener, type InferSetupArgs, type SigmaObserveChange, type SigmaObserveOptions, type SigmaRef, type SigmaState, SigmaTarget, SigmaType, action, batch, computed, effect, freeze, immerable, isSigmaState, listen, query, replaceState, reservedKeys, setAutoFreeze, sigmaEventsBrand, sigmaRefBrand, sigmaStateBrand, sigmaTypeBrand, signalPrefix, snapshot, untracked, useListener, useSigma };
package/dist/index.mjs CHANGED
@@ -105,13 +105,13 @@ function createContext(instance, options, owner) {
105
105
  const signal = getSignal(instance, key);
106
106
  return options.reactiveReads ? signal.value : signal.peek();
107
107
  }
108
- if (key in instance.type.computeFunctions) {
108
+ if (key in instance.type._computeFunctions) {
109
109
  if (owner && options.liveComputeds) return readActionComputedValue(owner, key);
110
110
  const signal = getSignal(instance, key);
111
111
  return options.reactiveReads ? signal.value : signal.peek();
112
112
  }
113
- if (options.allowQueries && key in instance.type.queryFunctions) return Reflect.get(instance.publicInstance, key);
114
- if (options.allowActions && key in instance.type.actionFunctions) return Reflect.get(instance.publicInstance, key);
113
+ if (options.allowQueries && key in instance.type._queryFunctions) return Reflect.get(instance.publicInstance, key);
114
+ if (options.allowActions && key in instance.type._actionFunctions) return Reflect.get(instance.publicInstance, key);
115
115
  if (Reflect.has(publicPrototype, key)) return Reflect.get(publicPrototype, key, owner?.actionContext ?? instance.publicInstance);
116
116
  },
117
117
  set(_target, key, value) {
@@ -172,7 +172,7 @@ function getPublicContext(instance, kind) {
172
172
  }
173
173
  //#endregion
174
174
  //#region src/internal/symbols.ts
175
- const sigmaStateBrand = Symbol("sigma.v2.state");
175
+ const sigmaStateBrand = Symbol("sigma.state");
176
176
  const reservedKeys = new Set([
177
177
  "act",
178
178
  "get",
@@ -212,7 +212,7 @@ function getSignal(instance, key) {
212
212
  return instance.publicInstance["#" + key];
213
213
  }
214
214
  function initializeSigmaInstance(publicInstance, type, initialState) {
215
- const stateKeys = new Set(type.defaultStateKeys);
215
+ const stateKeys = new Set(type._defaultStateKeys);
216
216
  if (initialState) for (const key in initialState) stateKeys.add(key);
217
217
  const instance = {
218
218
  currentSetupCleanup: void 0,
@@ -224,7 +224,7 @@ function initializeSigmaInstance(publicInstance, type, initialState) {
224
224
  for (const key of stateKeys) {
225
225
  if (reservedKeys.has(key)) throw new Error(`[preact-sigma] Reserved property name: ${key}`);
226
226
  let value = initialState?.[key];
227
- if (value === void 0) value = typeof type.defaultState[key] === "function" ? type.defaultState[key].call(void 0) : type.defaultState[key];
227
+ if (value === void 0) value = typeof type._defaultState[key] === "function" ? type._defaultState[key].call(void 0) : type._defaultState[key];
228
228
  const container = signal(value);
229
229
  Object.defineProperty(publicInstance, "#" + key, { value: container });
230
230
  Object.defineProperty(publicInstance, key, {
@@ -232,7 +232,7 @@ function initializeSigmaInstance(publicInstance, type, initialState) {
232
232
  enumerable: true
233
233
  });
234
234
  }
235
- for (const key in type.computeFunctions) Object.defineProperty(publicInstance, "#" + key, { value: computed$1(() => type.computeFunctions[key].call(getContext(instance, "computedReadonly"))) });
235
+ for (const key in type._computeFunctions) Object.defineProperty(publicInstance, "#" + key, { value: computed$1(() => type._computeFunctions[key].call(getContext(instance, "computedReadonly"))) });
236
236
  registerSigmaInternals(publicInstance, instance);
237
237
  }
238
238
  function buildQueryMethod(queryFunction) {
@@ -259,7 +259,7 @@ function readActionStateValue(owner, key, options) {
259
259
  return committedValue;
260
260
  }
261
261
  function readActionComputedValue(owner, key) {
262
- return owner.instance.type.computeFunctions[key].call(getContext(owner, "computedDraftAware"));
262
+ return owner.instance.type._computeFunctions[key].call(getContext(owner, "computedDraftAware"));
263
263
  }
264
264
  function setActionStateValue(owner, key, value) {
265
265
  ensureOwnerDraft(owner)[key] = value;
@@ -281,10 +281,10 @@ function handleActionBoundary(owner, boundary, actionName) {
281
281
  }
282
282
  function assertDefinitionKeyAvailable(builder, key, kind) {
283
283
  if (reservedKeys.has(key)) throw new Error(`[preact-sigma] Reserved property name: ${key}`);
284
- if (key in builder.computeFunctions || key in builder.queryFunctions || key in builder.actionFunctions) throw new Error(`[preact-sigma] Duplicate key for ${kind}: ${key}`);
284
+ if (key in builder._computeFunctions || key in builder._queryFunctions || key in builder._actionFunctions) throw new Error(`[preact-sigma] Duplicate key for ${kind}: ${key}`);
285
285
  }
286
286
  function shouldSetup(publicInstance) {
287
- return getSigmaInternals(publicInstance).type.setupFunctions.length > 0;
287
+ return getSigmaInternals(publicInstance).type._setupFunction !== null;
288
288
  }
289
289
  function clearCurrentDraft(owner) {
290
290
  owner.currentDraft = void 0;
@@ -396,7 +396,7 @@ function finalizeOwnerDraft(owner) {
396
396
  clearCurrentDraft(owner);
397
397
  let patches;
398
398
  let inversePatches;
399
- const newState = owner.instance.type.patchesEnabled ? immer.finishDraft(currentDraft, (nextPatches, nextInversePatches) => {
399
+ const newState = owner.instance.type._patchesEnabled ? immer.finishDraft(currentDraft, (nextPatches, nextInversePatches) => {
400
400
  patches = nextPatches;
401
401
  inversePatches = nextInversePatches;
402
402
  }) : immer.finishDraft(currentDraft);
@@ -413,7 +413,7 @@ function finalizeReplacementState(instance, oldState, nextState) {
413
413
  for (const key of instance.stateKeys) draft[key] = nextState[key];
414
414
  let patches;
415
415
  let inversePatches;
416
- const newState = instance.type.patchesEnabled ? immer.finishDraft(draft, (nextPatches, nextInversePatches) => {
416
+ const newState = instance.type._patchesEnabled ? immer.finishDraft(draft, (nextPatches, nextInversePatches) => {
417
417
  patches = nextPatches;
418
418
  inversePatches = nextInversePatches;
419
419
  }) : immer.finishDraft(draft);
@@ -440,7 +440,7 @@ function publishState(instance, finalized) {
440
440
  signal.value = nextValue;
441
441
  }
442
442
  });
443
- for (const observer of instance.type.observeFunctions) observer.call(getContext(instance, "observe"), finalized);
443
+ instance.type._observeFunction?.call(getContext(instance, "observe"), finalized);
444
444
  }
445
445
  /**
446
446
  * Returns a shallow snapshot of an instance's committed public state.
@@ -492,15 +492,12 @@ async function resolveAsyncActionResult(owner, result) {
492
492
  var Sigma = class extends EventTarget {
493
493
  setup(...args) {
494
494
  const instance = getSigmaInternals(this);
495
- if (!instance.type.setupFunctions.length) throw new Error("[preact-sigma] Setup is undefined for this sigma state");
495
+ if (!instance.type._setupFunction) throw new Error("[preact-sigma] Setup is undefined for this sigma state");
496
496
  if (instance.disposed) throw new Error("[preact-sigma] Cannot set up a disposed sigma state");
497
497
  instance.currentSetupCleanup?.();
498
498
  instance.currentSetupCleanup = void 0;
499
- const resources = instance.type.setupFunctions.flatMap((setup) => {
500
- const result = setup.apply(getContext(instance, "setup"), args);
501
- if (!Array.isArray(result)) throw new Error("[preact-sigma] Sigma setup handlers must return an array");
502
- return result;
503
- });
499
+ const resources = instance.type._setupFunction.apply(getContext(instance, "setup"), args);
500
+ if (!Array.isArray(resources)) throw new Error("[preact-sigma] Sigma setup handlers must return an array");
504
501
  let cleanup;
505
502
  if (resources.length) {
506
503
  let cleaned = false;
@@ -560,73 +557,117 @@ function query(fn) {
560
557
  * State and event inference starts from `new SigmaType<TState, TEvents>()`.
561
558
  * Later builder methods infer names and types from the objects you pass to them.
562
559
  */
563
- var SigmaType = class extends Function {
560
+ var SigmaType = class SigmaType extends Function {
564
561
  constructor(name = "Sigma") {
565
562
  super();
566
- const type = {
567
- actionFunctions: Object.create(null),
568
- computeFunctions: Object.create(null),
569
- defaultState: Object.create(null),
570
- defaultStateKeys: [],
571
- observeFunctions: [],
572
- patchesEnabled: false,
573
- queryFunctions: Object.create(null),
574
- setupFunctions: []
575
- };
576
- const { [name]: SigmaTypeBuilder } = { [name]: class extends Sigma {
563
+ const { [name]: type } = { [name]: class extends Sigma {
564
+ static _actionFunctions = Object.create(null);
565
+ static _computeFunctions = Object.create(null);
566
+ static _defaultState = Object.create(null);
567
+ static _defaultStateKeys = [];
568
+ static _observeFunction = null;
569
+ static _patchesEnabled = false;
570
+ static _queryFunctions = Object.create(null);
571
+ static _setupFunction = null;
577
572
  constructor(initialState) {
578
573
  super();
579
574
  initializeSigmaInstance(this, type, initialState);
580
575
  }
581
576
  } };
582
- SigmaTypeBuilder.defaultState = function(defaultState) {
583
- for (const key in defaultState) {
584
- if (defaultState[key] === void 0) continue;
585
- type.defaultState[key] = defaultState[key];
586
- type.defaultStateKeys.push(key);
587
- }
588
- return this;
589
- };
590
- SigmaTypeBuilder.computed = function(computeFunctions) {
591
- for (const key in computeFunctions) {
592
- assertDefinitionKeyAvailable(type, key, "computed");
593
- type.computeFunctions[key] = computeFunctions[key];
594
- Object.defineProperty(this.prototype, key, { get: function() {
595
- return this["#" + key].value;
596
- } });
597
- }
598
- return this;
599
- };
600
- SigmaTypeBuilder.queries = function(queryFunctions) {
601
- for (const key in queryFunctions) {
602
- assertDefinitionKeyAvailable(type, key, "query");
603
- const queryFunction = queryFunctions[key];
604
- type.queryFunctions[key] = queryFunction;
605
- Object.defineProperty(this.prototype, key, { value: buildQueryMethod(queryFunction) });
606
- }
607
- return this;
608
- };
609
- SigmaTypeBuilder.observe = function(listener, options) {
610
- type.observeFunctions.push(listener);
611
- if (options?.patches) type.patchesEnabled = true;
612
- return this;
613
- };
614
- SigmaTypeBuilder.actions = function(actionFunctions) {
615
- for (const key in actionFunctions) {
616
- assertDefinitionKeyAvailable(type, key, "action");
617
- const actionFunction = actionFunctions[key];
618
- type.actionFunctions[key] = actionFunction;
619
- Object.defineProperty(this.prototype, key, { value: buildActionMethod(key, actionFunction) });
620
- }
621
- return this;
622
- };
623
- SigmaTypeBuilder.setup = function(setup) {
624
- type.setupFunctions.push(setup);
625
- return this;
626
- };
627
- return SigmaTypeBuilder;
577
+ Object.defineProperties(type, Object.getOwnPropertyDescriptors(SigmaType.prototype));
578
+ return type;
579
+ }
580
+ /**
581
+ * Adds top-level public state and default values to the builder.
582
+ *
583
+ * Each property becomes a reactive public state property on instances. Use a
584
+ * zero-argument function when each instance needs a fresh object or array.
585
+ */
586
+ defaultState(defaultState) {
587
+ const type = getTypeInternals(this);
588
+ for (const key in defaultState) {
589
+ if (defaultState[key] === void 0) continue;
590
+ type._defaultState[key] = defaultState[key];
591
+ type._defaultStateKeys.push(key);
592
+ }
593
+ return this;
594
+ }
595
+ /**
596
+ * Adds reactive getter properties for derived values that take no arguments.
597
+ *
598
+ * Computed names and return types are inferred from the object you pass.
599
+ * `this` exposes readonly state plus computeds that are already on the builder.
600
+ */
601
+ computed(computeds) {
602
+ const type = getTypeInternals(this);
603
+ for (const key in computeds) {
604
+ assertDefinitionKeyAvailable(type, key, "computed");
605
+ type._computeFunctions[key] = computeds[key];
606
+ Object.defineProperty(this.prototype, key, { get: function() {
607
+ return this["#" + key].value;
608
+ } });
609
+ }
610
+ return this;
611
+ }
612
+ /**
613
+ * Adds reactive read methods that accept arguments.
614
+ *
615
+ * Query names, parameters, and return types are inferred from the object you
616
+ * pass. Each call tracks reactively at the call site and does not memoize
617
+ * results across invocations.
618
+ */
619
+ queries(queries) {
620
+ const type = getTypeInternals(this);
621
+ for (const key in queries) {
622
+ assertDefinitionKeyAvailable(type, key, "query");
623
+ const queryFunction = queries[key];
624
+ type._queryFunctions[key] = queryFunction;
625
+ Object.defineProperty(this.prototype, key, { value: buildQueryMethod(queryFunction) });
626
+ }
627
+ return this;
628
+ }
629
+ observe(listener, options) {
630
+ const type = getTypeInternals(this);
631
+ type._observeFunction = listener;
632
+ if (options?.patches) type._patchesEnabled = true;
633
+ return this;
634
+ }
635
+ /**
636
+ * Adds action methods whose `this` receives draft state, typed events, `commit()`,
637
+ * and the computeds, queries, and actions already defined on the builder.
638
+ *
639
+ * Actions create drafts lazily as they need them. Sync actions on the same
640
+ * instance reuse the current draft, so they can compose and publish once when
641
+ * the outer action returns. Declared async actions publish their initial
642
+ * synchronous work on return, then require `this.commit()` to publish later
643
+ * writes made after `await`. Non-async actions stay synchronous; if one
644
+ * returns a promise, sigma throws so async boundaries stay explicit.
645
+ */
646
+ actions(actions) {
647
+ const type = getTypeInternals(this);
648
+ for (const key in actions) {
649
+ assertDefinitionKeyAvailable(type, key, "action");
650
+ const actionFunction = actions[key];
651
+ type._actionFunctions[key] = actionFunction;
652
+ Object.defineProperty(this.prototype, key, { value: buildActionMethod(key, actionFunction) });
653
+ }
654
+ return this;
655
+ }
656
+ /**
657
+ * Adds an explicit setup handler for side effects and owned resources.
658
+ *
659
+ * Every registered handler runs when `instance.setup(...)` is called, and the
660
+ * setup argument list is inferred from the first handler you add.
661
+ */
662
+ setup(setup) {
663
+ const type = getTypeInternals(this);
664
+ type._setupFunction = setup;
665
+ return this;
628
666
  }
629
667
  };
668
+ function getTypeInternals(type) {
669
+ return type;
670
+ }
630
671
  //#endregion
631
672
  //#region src/listener.ts
632
673
  /**
@@ -640,8 +681,8 @@ var SigmaTarget = class extends EventTarget {
640
681
  * Void events dispatch a plain `Event`. Payload events dispatch a
641
682
  * `CustomEvent` whose `detail` holds the payload.
642
683
  */
643
- emit(name, ...args) {
644
- this.dispatchEvent(args.length === 0 ? new Event(name) : new CustomEvent(name, { detail: args[0] }));
684
+ emit(name, ...[detail]) {
685
+ this.dispatchEvent(detail === void 0 ? new Event(name) : new CustomEvent(name, { detail }));
645
686
  }
646
687
  /**
647
688
  * Registers a typed event listener and returns an unsubscribe function.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "preact-sigma",
3
- "version": "2.3.0",
3
+ "version": "3.0.2",
4
4
  "keywords": [],
5
5
  "license": "MIT",
6
6
  "author": "Alec Larson",