chizu 0.3.0 → 0.5.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.
Files changed (44) hide show
  1. package/README.md +61 -58
  2. package/dist/chizu.js +6 -6
  3. package/dist/chizu.umd.cjs +1 -1
  4. package/dist/{action → src/library/action}/utils.d.ts +3 -3
  5. package/dist/src/library/boundary/components/mode/index.d.ts +15 -0
  6. package/dist/src/library/boundary/components/mode/types.d.ts +7 -0
  7. package/dist/src/library/boundary/components/mode/utils.d.ts +55 -0
  8. package/dist/src/library/boundary/components/scope/index.d.ts +40 -0
  9. package/dist/src/library/boundary/components/scope/types.d.ts +20 -0
  10. package/dist/{boundary → src/library/boundary}/components/scope/utils.d.ts +3 -6
  11. package/dist/{boundary → src/library/boundary}/index.d.ts +1 -1
  12. package/dist/src/library/error/index.d.ts +2 -0
  13. package/dist/{error → src/library/error}/types.d.ts +1 -18
  14. package/dist/{hooks → src/library/hooks}/index.d.ts +2 -2
  15. package/dist/{hooks → src/library/hooks}/types.d.ts +1 -17
  16. package/dist/{hooks → src/library/hooks}/utils.d.ts +25 -25
  17. package/dist/{index.d.ts → src/library/index.d.ts} +5 -5
  18. package/dist/{types → src/library/types}/index.d.ts +57 -325
  19. package/package.json +5 -2
  20. package/dist/boundary/components/regulators/index.d.ts +0 -14
  21. package/dist/boundary/components/regulators/types.d.ts +0 -52
  22. package/dist/boundary/components/regulators/utils.d.ts +0 -21
  23. package/dist/boundary/components/scope/index.d.ts +0 -83
  24. package/dist/boundary/components/scope/types.d.ts +0 -29
  25. package/dist/error/index.d.ts +0 -2
  26. /package/dist/{action → src/library/action}/index.d.ts +0 -0
  27. /package/dist/{annotate → src/library/annotate}/index.d.ts +0 -0
  28. /package/dist/{boundary → src/library/boundary}/components/broadcast/index.d.ts +0 -0
  29. /package/dist/{boundary → src/library/boundary}/components/broadcast/types.d.ts +0 -0
  30. /package/dist/{boundary → src/library/boundary}/components/broadcast/utils.d.ts +0 -0
  31. /package/dist/{boundary → src/library/boundary}/components/consumer/components/partition/index.d.ts +0 -0
  32. /package/dist/{boundary → src/library/boundary}/components/consumer/components/partition/types.d.ts +0 -0
  33. /package/dist/{boundary → src/library/boundary}/components/consumer/index.d.ts +0 -0
  34. /package/dist/{boundary → src/library/boundary}/components/consumer/types.d.ts +0 -0
  35. /package/dist/{boundary → src/library/boundary}/components/consumer/utils.d.ts +0 -0
  36. /package/dist/{boundary → src/library/boundary}/components/tasks/index.d.ts +0 -0
  37. /package/dist/{boundary → src/library/boundary}/components/tasks/types.d.ts +0 -0
  38. /package/dist/{boundary → src/library/boundary}/components/tasks/utils.d.ts +0 -0
  39. /package/dist/{boundary → src/library/boundary}/types.d.ts +0 -0
  40. /package/dist/{error → src/library/error}/utils.d.ts +0 -0
  41. /package/dist/{resource → src/library/resource}/index.d.ts +0 -0
  42. /package/dist/{utils → src/library/utils}/index.d.ts +0 -0
  43. /package/dist/{utils → src/library/utils}/utils.d.ts +0 -0
  44. /package/dist/{utils.d.ts → src/library/utils.d.ts} +0 -0
@@ -1,6 +1,5 @@
1
1
  import { Operation, Process, Inspect, Box } from 'immertation';
2
2
  import { ActionId, Task, Tasks } from '../boundary/components/tasks/types.ts';
3
- import { Regulator } from '../boundary/components/regulators/types.ts';
4
3
  import { Fault } from '../error/types.ts';
5
4
  import * as React from "react";
6
5
  export type { ActionId, Box, Task, Tasks };
@@ -54,13 +53,11 @@ export declare class Brand {
54
53
  static readonly Action: unique symbol;
55
54
  /** Identifies channeled actions (result of calling Action(channel)) */
56
55
  static readonly Channel: unique symbol;
57
- /** Node capture events used by Lifecycle.Node */
58
- static readonly Node: unique symbol;
59
56
  }
60
57
  /**
61
58
  * Internal symbol for the global `Lifecycle.Fault` broadcast. Exposed so the
62
- * dispatch pipeline can fire faults and short-circuit the regulator policy
63
- * without depending on the `Lifecycle` class at runtime.
59
+ * dispatch pipeline can fire faults without depending on the `Lifecycle`
60
+ * class at runtime.
64
61
  *
65
62
  * @internal
66
63
  */
@@ -68,8 +65,9 @@ export declare const FaultSymbol: unique symbol;
68
65
  /**
69
66
  * Factory functions for lifecycle actions.
70
67
  *
71
- * Each call returns a **unique** action symbol, enabling per-component
72
- * regulation. Assign the result as a static property in your Actions class:
68
+ * Each call returns a **unique** action symbol so that each component can
69
+ * subscribe independently. Assign the result as a static property in your
70
+ * Actions class:
73
71
  *
74
72
  * @example
75
73
  * ```ts
@@ -78,13 +76,9 @@ export declare const FaultSymbol: unique symbol;
78
76
  * static Unmount = Lifecycle.Unmount();
79
77
  * static Error = Lifecycle.Error();
80
78
  * static Update = Lifecycle.Update();
81
- * static Node = Lifecycle.Node();
82
79
  *
83
80
  * static Increment = Action("Increment");
84
81
  * }
85
- *
86
- * // Now regulating Lifecycle.Mount only blocks THIS component's mount:
87
- * context.regulator.disallow(Actions.Mount);
88
82
  * ```
89
83
  *
90
84
  * `Lifecycle.Fault` is a singleton broadcast (not a factory). All components
@@ -99,40 +93,13 @@ export declare class Lifecycle {
99
93
  static Error(): HandlerPayload<Fault>;
100
94
  /** Creates an Update lifecycle action. Triggered when `context.data` changes (not on initial mount). */
101
95
  static Update(): HandlerPayload<Record<string, unknown>>;
102
- /**
103
- * Creates a Node lifecycle action. Triggered when a DOM node is captured
104
- * or released via `actions.node()`. Supports channeled subscriptions by
105
- * node name.
106
- *
107
- * @example
108
- * ```ts
109
- * export class Actions {
110
- * static Node = Lifecycle.Node();
111
- * }
112
- *
113
- * // Subscribe to ALL node changes
114
- * actions.useAction(Actions.Node, (context, node) => {
115
- * console.log("Node changed:", node);
116
- * });
117
- *
118
- * // Subscribe to a specific node by name (channeled)
119
- * actions.useAction(Actions.Node({ Name: "input" }), (context, node) => {
120
- * if (node) node.focus();
121
- * });
122
- * ```
123
- */
124
- static Node(): HandlerPayload<unknown, {
125
- Name: string;
126
- }>;
127
96
  /**
128
97
  * Global fault broadcast. Receives a `Fault` whenever any action in the
129
- * `<Boundary>` errors, times out, is supplanted, or is blocked by the
130
- * regulator. Subscribe via `actions.useAction(Lifecycle.Fault, handler)`.
98
+ * `<Boundary>` errors, times out, or is supplanted. Subscribe via
99
+ * `actions.useAction(Lifecycle.Fault, handler)`.
131
100
  *
132
101
  * Unlike the per-component `Lifecycle.Error()` factory, `Fault` is a single
133
- * shared broadcast — every subscriber points at the same symbol. The
134
- * regulator policy does not apply to `Fault`; faults always reach
135
- * subscribers so error visibility cannot be silenced.
102
+ * shared broadcast — every subscriber points at the same symbol.
136
103
  *
137
104
  * @example
138
105
  * ```tsx
@@ -152,18 +119,24 @@ export declare class Lifecycle {
152
119
  *
153
120
  * - **Unicast** &ndash; Action is scoped to the component that defines it and cannot be
154
121
  * consumed by other components. This is the default behaviour.
155
- * - **Broadcast** &ndash; Action is distributed to all mounted components that have defined
156
- * a handler for it. Values are cached for late-mounting components.
122
+ * - **Broadcast** &ndash; Action is distributed to all mounted components that have
123
+ * defined a handler for it. Values are cached for late-mounting components.
124
+ * - **Multicast** &ndash; Action defines its own scope. Components reach it by
125
+ * wrapping a subtree in `withScope(<theMulticastAction>, Component)`.
157
126
  *
158
127
  * @example
159
128
  * ```ts
160
- * export class Actions {
161
- * // Unicast action (default) - local to this component
162
- * static Increment = new Action("Increment");
163
- *
164
- * // Broadcast action - can be consumed across components
165
- * static Counter = new Action("Counter", Distribution.Broadcast);
129
+ * export class Scope {
130
+ * // The action itself acts as the scope identifier.
131
+ * static Mood = Action<Mood>("Mood", Distribution.Multicast);
166
132
  * }
133
+ *
134
+ * // Wrap the subtree where the scope applies.
135
+ * export default withScope(Scope.Mood, Component);
136
+ *
137
+ * // Dispatch / subscribe — no extra options.
138
+ * actions.dispatch(Scope.Mood, mood);
139
+ * actions.useAction(Scope.Mood, (context, mood) => { ... });
167
140
  * ```
168
141
  */
169
142
  export declare enum Distribution {
@@ -171,7 +144,7 @@ export declare enum Distribution {
171
144
  Unicast = "unicast",
172
145
  /** Action is broadcast to all mounted components and can be consumed. */
173
146
  Broadcast = "broadcast",
174
- /** Action is multicast to all components within a named scope boundary. */
147
+ /** Action is multicast to every component inside its `withScope` boundary. */
175
148
  Multicast = "multicast"
176
149
  }
177
150
  /**
@@ -294,9 +267,9 @@ export type BroadcastPayload<P = unknown, C extends Filter = never> = HandlerPay
294
267
  * Branded type for multicast action objects created with `Action()` and `Distribution.Multicast`.
295
268
  * Multicast actions are dispatched to all components within a named scope boundary.
296
269
  *
297
- * When dispatching a multicast action, you MUST provide the scope carrier as the third argument:
270
+ * When dispatching a multicast action, you MUST provide the scope name as the third argument:
298
271
  * ```ts
299
- * actions.dispatch(Actions.Multicast.Update, payload, { scope: Actions.Multicast });
272
+ * actions.dispatch(Actions.Multicast.Update, payload, { scope: Actions.Multicast.Scope });
300
273
  * ```
301
274
  *
302
275
  * Components receive multicast events only if they are descendants of a `<Scope of={...}>`.
@@ -306,10 +279,12 @@ export type BroadcastPayload<P = unknown, C extends Filter = never> = HandlerPay
306
279
  *
307
280
  * @example
308
281
  * ```tsx
309
- * // Define multicast actions and their scope together
282
+ * export enum Scope {
283
+ * Counter = "counter",
284
+ * }
285
+ *
310
286
  * class MulticastActions {
311
- * static Scope = "Counter" as const;
312
- * static Update = Action<number>("Update", Distribution.Multicast);
287
+ * static Update = Action<number>("Update", Distribution.Multicast(Scope.Counter));
313
288
  * }
314
289
  *
315
290
  * // Reference from component-level Actions
@@ -317,49 +292,23 @@ export type BroadcastPayload<P = unknown, C extends Filter = never> = HandlerPay
317
292
  * static Multicast = MulticastActions;
318
293
  * }
319
294
  *
320
- * // In JSX - create a named scope boundary
321
- * <Scope of={MulticastActions}>
322
- * <CounterA />
323
- * <CounterB />
324
- * </Scope>
295
+ * // Wrap the subtree where the scope applies via the withScope HOC.
296
+ * export default withScope(Scope.Counter, function Counters() {
297
+ * return (
298
+ * <>
299
+ * <CounterA />
300
+ * <CounterB />
301
+ * </>
302
+ * );
303
+ * });
325
304
  *
326
- * // Inside CounterA - dispatch to all components in the scope
327
- * actions.dispatch(Actions.Multicast.Update, 42, { scope: Actions.Multicast });
328
- * // CounterA and CounterB both receive the event
305
+ * // Dispatch the scope is read from the action itself.
306
+ * actions.dispatch(Actions.Multicast.Update, 42);
329
307
  * ```
330
308
  */
331
309
  export type MulticastPayload<P = unknown, C extends Filter = never> = HandlerPayload<P, C> & {
332
310
  readonly [Brand.Multicast]: true;
333
311
  };
334
- /**
335
- * A scope carrier — any object exposing a `Scope` string literal.
336
- *
337
- * By convention this is the feature's `MulticastActions` class, which owns
338
- * the scope name alongside its multicast action declarations:
339
- *
340
- * ```ts
341
- * export class MulticastActions {
342
- * static Scope = "my-feature" as const;
343
- * static Update = Action<T>("Update", Distribution.Multicast);
344
- * }
345
- * ```
346
- *
347
- * Requiring a carrier (rather than a bare string) prevents typos and enforces
348
- * a single source of truth for each scope name.
349
- *
350
- * @template S - The scope name literal type.
351
- */
352
- export type ScopeCarrier<S extends string = string> = {
353
- readonly Scope: S;
354
- };
355
- /**
356
- * Options for multicast dispatch.
357
- * Required when dispatching a multicast action.
358
- */
359
- export type MulticastOptions = {
360
- /** Carrier object exposing the scope name via `.Scope`. Typically the feature's `MulticastActions` class. */
361
- scope: ScopeCarrier;
362
- };
363
312
  /**
364
313
  * Extracts the payload type `P` from a `HandlerPayload<P>` or `ChanneledAction<P, C>`.
365
314
  * Use this in handler signatures to get the action's payload type.
@@ -440,108 +389,6 @@ type AssertSync<F> = IsAsync<F> extends true ? "Error: async functions are not a
440
389
  * Represents any object that can be captured as reactive data.
441
390
  */
442
391
  export type Props = Record<string, unknown>;
443
- /**
444
- * Extracts the nodes type from a Model.
445
- * If the model has a `meta.nodes` property, returns its type.
446
- * Otherwise returns an empty record.
447
- *
448
- * @example
449
- * ```ts
450
- * type Model = {
451
- * count: number;
452
- * meta: { nodes: { container: HTMLButtonElement | null } };
453
- * };
454
- *
455
- * type N = ExtractNodes<Model>; // { container: HTMLButtonElement | null }
456
- * ```
457
- */
458
- export type ExtractNodes<M> = M extends {
459
- meta: {
460
- nodes: infer N extends Record<string, unknown>;
461
- };
462
- } ? N : Record<never, unknown>;
463
- /**
464
- * Transforms `meta.nodes` values to include `| null`, reflecting
465
- * that node refs are initially `null` until a DOM element is captured.
466
- * Models without `meta.nodes` pass through unchanged.
467
- *
468
- * @internal
469
- */
470
- export type NullableNodes<M> = M extends {
471
- meta: infer Meta & {
472
- nodes: infer N extends Record<string, unknown>;
473
- };
474
- } ? Omit<M, "meta"> & {
475
- meta: Omit<Meta, "nodes"> & {
476
- nodes: {
477
- [K in keyof N]: N[K] | null;
478
- };
479
- };
480
- } : M;
481
- /**
482
- * Extracts the `meta.features` property from a Model type.
483
- * If the model has a `meta.features` property whose values are all booleans,
484
- * returns its type. Otherwise returns an empty record, making the
485
- * `features` methods effectively uncallable.
486
- *
487
- * @example
488
- * ```ts
489
- * type Model = { count: number; meta: { features: { sidebar: boolean; modal: boolean } } };
490
- * type F = FeatureFlags<Model>; // { sidebar: boolean; modal: boolean }
491
- * ```
492
- */
493
- export type FeatureFlags<M> = M extends {
494
- meta: {
495
- features: infer F extends Record<string, boolean>;
496
- };
497
- } ? F : Record<never, boolean>;
498
- /**
499
- * Resolves to `unknown` when the model has no `meta.features` or all
500
- * feature values are booleans. Resolves to a descriptive string literal when
501
- * any value is not a boolean, causing an intersection with the model type
502
- * to become `never` and producing a compile-time error.
503
- *
504
- * @internal
505
- */
506
- export type ValidateFeatures<M> = M extends {
507
- meta: {
508
- features: infer F;
509
- };
510
- } ? F extends Record<string, boolean> ? unknown : "meta.features values must all be boolean" : unknown;
511
- /**
512
- * Utility type for the `meta` model property. Combines optional `nodes`
513
- * and `features` sub-objects into a single meta shape.
514
- *
515
- * - When `N` is provided, produces `{ nodes: { [K in keyof N]: N[K] | null } }`.
516
- * - When `F` is provided, produces `{ features: F }`.
517
- * - Pass `void` to omit either sub-object.
518
- *
519
- * @template N - Node type record (e.g. `{ input: HTMLInputElement }`)
520
- * @template F - Feature flags record (e.g. `{ sidebar: boolean }`)
521
- *
522
- * @example
523
- * ```ts
524
- * import type { Meta } from "chizu";
525
- *
526
- * type Model = {
527
- * count: number;
528
- * meta: Meta<{ input: HTMLInputElement }, { sidebar: boolean }>;
529
- * };
530
- * // Equivalent to:
531
- * // meta: { nodes: { input: HTMLInputElement | null }; features: { sidebar: boolean } }
532
- * ```
533
- */
534
- export type Meta<N extends Record<string, unknown> | void = void, F extends Record<string, boolean> | void = void> = ([N] extends [void] ? unknown : {
535
- nodes: {
536
- [K in keyof N]: N[K] | null;
537
- };
538
- }) & ([F] extends [void] ? unknown : {
539
- features: F;
540
- });
541
- export declare namespace Meta {
542
- type Nodes<N extends Record<string, unknown>> = Meta<N>;
543
- type Features<F extends Record<string, boolean>> = Meta<void, F>;
544
- }
545
392
  /**
546
393
  * Constraint type for action containers.
547
394
  * Actions are symbols grouped in an object (typically a class with static properties).
@@ -643,64 +490,13 @@ export type HandlerContext<M extends Model | void, _AC extends Actions | void, D
643
490
  * ```
644
491
  */
645
492
  readonly tasks: ReadonlySet<Task>;
646
- /**
647
- * Meta properties including captured DOM nodes and feature flags.
648
- *
649
- * @example
650
- * ```ts
651
- * actions.useAction(Actions.Focus, (context) => {
652
- * context.meta.nodes.input?.focus();
653
- * });
654
- * ```
655
- */
656
- readonly meta: {
657
- readonly nodes: {
658
- [K in keyof ExtractNodes<M>]: ExtractNodes<M>[K] | null;
659
- };
660
- readonly features: Readonly<FeatureFlags<M>>;
661
- };
662
- /**
663
- * The regulator API for controlling which actions may be dispatched.
664
- *
665
- * Each call replaces the previous policy entirely (last-write-wins).
666
- * The policy is shared across all components within the same `<Boundary>`.
667
- *
668
- * @example
669
- * ```ts
670
- * actions.useAction(Actions.Mount, (context) => {
671
- * // Block all actions except Critical
672
- * context.regulator.allow(Actions.Critical);
673
- * });
674
- *
675
- * actions.useAction(Actions.Unlock, (context) => {
676
- * // Re-allow all actions
677
- * context.regulator.allow();
678
- * });
679
- * ```
680
- */
681
- readonly regulator: Regulator;
682
493
  readonly actions: {
683
494
  produce<F extends (draft: {
684
- model: Omit<M, "meta">;
495
+ model: M;
685
496
  readonly inspect: Readonly<Inspect<M>>;
686
497
  }) => void>(ƒ: F & AssertSync<F>): void;
687
- dispatch(action: ActionOrChanneled, payload?: unknown, options?: MulticastOptions): Promise<void>;
498
+ dispatch(action: ActionOrChanneled, payload?: unknown): Promise<void>;
688
499
  annotate<T>(operation: Operation, value: T): T;
689
- /**
690
- * Feature toggle methods for mutating boolean flags on the model.
691
- *
692
- * @example
693
- * ```ts
694
- * context.actions.features.on("sidebar");
695
- * context.actions.features.off("sidebar");
696
- * context.actions.features.invert("sidebar");
697
- * ```
698
- */
699
- features: {
700
- on<K extends keyof FeatureFlags<M>>(name: K): void;
701
- off<K extends keyof FeatureFlags<M>>(name: K): void;
702
- invert<K extends keyof FeatureFlags<M>>(name: K): void;
703
- };
704
500
  /**
705
501
  * Returns the resolved broadcast or multicast value, waiting for any
706
502
  * pending annotations to settle before resolving.
@@ -709,8 +505,8 @@ export type HandlerContext<M extends Model | void, _AC extends Actions | void, D
709
505
  * Otherwise it waits until the next dispatch of the action.
710
506
  * Resolves with `null` if the task is aborted before a value arrives.
711
507
  *
712
- * @param action - The broadcast or multicast action to resolve.
713
- * @param options - For multicast actions, must include `{ scope: MulticastActions }`.
508
+ * @param action - The broadcast or multicast action to resolve. Multicast
509
+ * actions read their scope from the action declaration.
714
510
  * @returns The dispatched value, or `null` if aborted.
715
511
  *
716
512
  * @example
@@ -725,15 +521,14 @@ export type HandlerContext<M extends Model | void, _AC extends Actions | void, D
725
521
  * });
726
522
  * ```
727
523
  */
728
- resolution<T>(action: BroadcastPayload<T>): Promise<T | null>;
729
- resolution<T>(action: MulticastPayload<T>, options: MulticastOptions): Promise<T | null>;
524
+ resolution<T>(action: BroadcastPayload<T> | MulticastPayload<T>): Promise<T | null>;
730
525
  /**
731
526
  * Returns the latest broadcast or multicast value immediately without
732
527
  * waiting for annotations to settle. Use this when you need the current
733
528
  * cached value and do not need to wait for pending operations to complete.
734
529
  *
735
- * @param action - The broadcast or multicast action to peek at.
736
- * @param options - For multicast actions, must include `{ scope: MulticastActions }`.
530
+ * @param action - The broadcast or multicast action to peek at. Multicast
531
+ * actions read their scope from the action declaration.
737
532
  * @returns The cached value, or `null` if no value has been dispatched.
738
533
  *
739
534
  * @example
@@ -745,8 +540,7 @@ export type HandlerContext<M extends Model | void, _AC extends Actions | void, D
745
540
  * });
746
541
  * ```
747
542
  */
748
- peek<T>(action: BroadcastPayload<T>): T | null;
749
- peek<T>(action: MulticastPayload<T>, options: MulticastOptions): T | null;
543
+ peek<T>(action: BroadcastPayload<T> | MulticastPayload<T>): T | null;
750
544
  };
751
545
  };
752
546
  /**
@@ -832,80 +626,18 @@ export type Handlers<M extends Model | void, AC extends Actions | void, D extend
832
626
  [K in OwnKeys<AC>]: OwnKeys<AC[K]> extends never ? (context: HandlerContext<M, AC, D>, ...args: [Payload<AC[K] & HandlerPayload<unknown>>] extends [never] ? [] : [payload: Payload<AC[K] & HandlerPayload<unknown>>]) => void | Promise<void> | AsyncGenerator | Generator : Handlers<M, AC[K] & Actions, D>;
833
627
  };
834
628
  export type UseActions<M extends Model | void, AC extends Actions | void, D extends Props = Props> = [
835
- Readonly<NullableNodes<M>>,
629
+ Readonly<M>,
836
630
  {
837
631
  /**
838
- * Dispatches an action with an optional payload.
839
- *
840
- * For multicast actions, you MUST provide the scope carrier as the third argument:
841
- * ```ts
842
- * actions.dispatch(Actions.Multicast.Update, payload, { scope: Actions.Multicast });
843
- * ```
844
- *
845
- * @param action - The action to dispatch
846
- * @param payload - The payload to send with the action
847
- * @param options - For multicast actions, must include `{ scope: MulticastActions }`
632
+ * Dispatches an action with an optional payload. Multicast actions read
633
+ * their scope from the action declaration, so no extra options are
634
+ * required at the call site.
848
635
  */
849
- dispatch<P>(action: HandlerPayload<P>, payload?: P, options?: MulticastOptions): Promise<void>;
850
- dispatch<P>(action: BroadcastPayload<P>, payload?: P, options?: MulticastOptions): Promise<void>;
851
- dispatch<P>(action: MulticastPayload<P>, payload: P, options: MulticastOptions): Promise<void>;
852
- dispatch<P, C extends Filter>(action: ChanneledAction<P, C>, payload?: P, options?: MulticastOptions): Promise<void>;
636
+ dispatch<P>(action: HandlerPayload<P>, payload?: P): Promise<void>;
637
+ dispatch<P>(action: BroadcastPayload<P>, payload?: P): Promise<void>;
638
+ dispatch<P>(action: MulticastPayload<P>, payload?: P): Promise<void>;
639
+ dispatch<P, C extends Filter>(action: ChanneledAction<P, C>, payload?: P): Promise<void>;
853
640
  inspect: Inspect<M>;
854
- /**
855
- * Meta properties including captured DOM nodes and feature flags.
856
- *
857
- * @example
858
- * ```tsx
859
- * actions.meta.nodes.input?.focus();
860
- * actions.meta.features.sidebar; // boolean
861
- * ```
862
- */
863
- meta: {
864
- readonly nodes: {
865
- [K in keyof ExtractNodes<M>]: ExtractNodes<M>[K] | null;
866
- };
867
- readonly features: Readonly<FeatureFlags<M>>;
868
- };
869
- /**
870
- * Captures a DOM node for later access via `meta.nodes`.
871
- * Use as a ref callback on JSX elements.
872
- *
873
- * @param name - The node key (must match a key in the model's `meta.nodes`)
874
- * @param node - The DOM node or null (when unmounting)
875
- *
876
- * @example
877
- * ```tsx
878
- * type Model = {
879
- * count: number;
880
- * meta: Meta<{ container: HTMLDivElement; input: HTMLInputElement }>;
881
- * };
882
- *
883
- * const [model, actions] = useActions<Model, typeof Actions>(model);
884
- *
885
- * return (
886
- * <div ref={node => actions.node('container', node)}>
887
- * <input ref={node => actions.node('input', node)} />
888
- * </div>
889
- * );
890
- * ```
891
- */
892
- node<K extends keyof ExtractNodes<M>>(name: K, node: ExtractNodes<M>[K] | null): void;
893
- /**
894
- * Feature toggle methods for mutating boolean flags on the model.
895
- *
896
- * @example
897
- * ```tsx
898
- * actions.features.on("sidebar");
899
- * actions.features.invert("sidebar");
900
- *
901
- * {model.meta.features.sidebar && <Sidebar />}
902
- * ```
903
- */
904
- features: {
905
- on<K extends keyof FeatureFlags<M>>(name: K): void;
906
- off<K extends keyof FeatureFlags<M>>(name: K): void;
907
- invert<K extends keyof FeatureFlags<M>>(name: K): void;
908
- };
909
641
  /**
910
642
  * Streams broadcast values declaratively in JSX using a render-prop pattern.
911
643
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chizu",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "packageManager": "yarn@1.22.22",
@@ -22,6 +22,9 @@
22
22
  "immer": "^10.0.0",
23
23
  "react": "^18.0.0 || ^19.0.0"
24
24
  },
25
+ "resolutions": {
26
+ "vitest/vite": "^7.3.1"
27
+ },
25
28
  "files": [
26
29
  "dist"
27
30
  ],
@@ -88,7 +91,7 @@
88
91
  "traverse": "^0.6.11",
89
92
  "ts-jest": "^29.4.6",
90
93
  "ts-node": "^10.9.2",
91
- "typescript": "^5.9.3",
94
+ "typescript": "^6.0.3",
92
95
  "typescript-eslint": "^8.55.0",
93
96
  "vite": "^7.3.1",
94
97
  "vite-plugin-dts": "^4.5.4",
@@ -1,14 +0,0 @@
1
- import { Props } from './types.ts';
2
- import * as React from "react";
3
- export { useRegulators, isAllowed } from './utils.ts';
4
- export type { Regulator, RegulatorPolicy } from './types.ts';
5
- /**
6
- * Provides a shared regulator policy to all descendant components.
7
- *
8
- * Wrap this around your component tree (or use `<Boundary>` which includes it)
9
- * to enable action regulation via `context.regulator`.
10
- *
11
- * @param props.children - The children to render within the regulator context.
12
- * @returns The children wrapped in a regulator context provider.
13
- */
14
- export declare function Regulators({ children }: Props): React.ReactNode;
@@ -1,52 +0,0 @@
1
- import { ActionId } from '../tasks/types.ts';
2
- import type * as React from "react";
3
- /**
4
- * The four policy modes that the regulator supports.
5
- *
6
- * - `allow-all` (default) &ndash; every action is permitted (`allow()` with no args).
7
- * - `disallow-all` &ndash; every action is blocked (`disallow()` with no args).
8
- * - `disallow-matching` &ndash; only the listed actions are blocked (`disallow(A, B)`).
9
- * - `allow-matching` &ndash; only the listed actions are permitted (`allow(A, B)`).
10
- *
11
- * For "except" patterns, compose two calls:
12
- * - Block all except X: `disallow()` then `allow(X)`.
13
- * - Allow all except X: `allow()` then `disallow(X)`.
14
- */
15
- export type RegulatorMode = "allow-all" | "disallow-all" | "disallow-matching" | "allow-matching";
16
- /**
17
- * Mutable policy object shared across all components in a `<Boundary>`.
18
- * Mutated in-place by the `context.regulator` API &ndash; last write wins.
19
- */
20
- export type RegulatorPolicy = {
21
- mode: RegulatorMode;
22
- actions: Set<ActionId>;
23
- };
24
- /**
25
- * The `context.regulator` API shape exposed to action handlers.
26
- *
27
- * - `disallow()` &ndash; block all actions.
28
- * - `disallow(A, B)` &ndash; block only the listed actions.
29
- * - `allow()` &ndash; allow all actions (reset to default).
30
- * - `allow(A, B)` &ndash; allow only the listed actions.
31
- *
32
- * Each call replaces the previous policy entirely (last-write-wins).
33
- */
34
- export type Regulator = {
35
- /**
36
- * Block actions. Called with no arguments, blocks everything.
37
- * Called with specific actions, blocks only those actions.
38
- */
39
- disallow(...actions: ReadonlyArray<{
40
- readonly [x: symbol]: unknown;
41
- }>): void;
42
- /**
43
- * Allow actions. Called with no arguments, allows everything (reset).
44
- * Called with specific actions, allows only those actions.
45
- */
46
- allow(...actions: ReadonlyArray<{
47
- readonly [x: symbol]: unknown;
48
- }>): void;
49
- };
50
- export type Props = {
51
- children: React.ReactNode;
52
- };
@@ -1,21 +0,0 @@
1
- import { ActionId } from '../tasks/types.ts';
2
- import { RegulatorPolicy } from './types.ts';
3
- import * as React from "react";
4
- /**
5
- * React context holding the shared, mutable regulator policy.
6
- */
7
- export declare const Context: React.Context<RegulatorPolicy>;
8
- /**
9
- * Hook to access the regulator policy from the nearest `<Regulators>` provider.
10
- *
11
- * @returns The mutable `RegulatorPolicy` object from context.
12
- */
13
- export declare function useRegulators(): RegulatorPolicy;
14
- /**
15
- * Determines whether a given action is allowed under the current policy.
16
- *
17
- * @param action - The action identifier (symbol) to check.
18
- * @param policy - The current regulator policy.
19
- * @returns `true` if the action may proceed, `false` if it should be blocked.
20
- */
21
- export declare function isAllowed(action: ActionId, policy: RegulatorPolicy): boolean;