chizu 0.4.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 +63 -65
  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 +55 -306
  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 -82
  24. package/dist/boundary/components/scope/types.d.ts +0 -28
  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
  /**
@@ -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,32 +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.Scope}>
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.Scope });
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
- * Options for multicast dispatch.
336
- * Required when dispatching a multicast action.
337
- */
338
- export type MulticastOptions = {
339
- /**
340
- * Scope name. By convention this is a `static Scope` literal co-located
341
- * with the multicast action declarations (e.g. `Actions.Multicast.Scope`),
342
- * giving every call site a single source of truth.
343
- */
344
- scope: string;
345
- };
346
312
  /**
347
313
  * Extracts the payload type `P` from a `HandlerPayload<P>` or `ChanneledAction<P, C>`.
348
314
  * Use this in handler signatures to get the action's payload type.
@@ -423,108 +389,6 @@ type AssertSync<F> = IsAsync<F> extends true ? "Error: async functions are not a
423
389
  * Represents any object that can be captured as reactive data.
424
390
  */
425
391
  export type Props = Record<string, unknown>;
426
- /**
427
- * Extracts the nodes type from a Model.
428
- * If the model has a `meta.nodes` property, returns its type.
429
- * Otherwise returns an empty record.
430
- *
431
- * @example
432
- * ```ts
433
- * type Model = {
434
- * count: number;
435
- * meta: { nodes: { container: HTMLButtonElement | null } };
436
- * };
437
- *
438
- * type N = ExtractNodes<Model>; // { container: HTMLButtonElement | null }
439
- * ```
440
- */
441
- export type ExtractNodes<M> = M extends {
442
- meta: {
443
- nodes: infer N extends Record<string, unknown>;
444
- };
445
- } ? N : Record<never, unknown>;
446
- /**
447
- * Transforms `meta.nodes` values to include `| null`, reflecting
448
- * that node refs are initially `null` until a DOM element is captured.
449
- * Models without `meta.nodes` pass through unchanged.
450
- *
451
- * @internal
452
- */
453
- export type NullableNodes<M> = M extends {
454
- meta: infer Meta & {
455
- nodes: infer N extends Record<string, unknown>;
456
- };
457
- } ? Omit<M, "meta"> & {
458
- meta: Omit<Meta, "nodes"> & {
459
- nodes: {
460
- [K in keyof N]: N[K] | null;
461
- };
462
- };
463
- } : M;
464
- /**
465
- * Extracts the `meta.features` property from a Model type.
466
- * If the model has a `meta.features` property whose values are all booleans,
467
- * returns its type. Otherwise returns an empty record, making the
468
- * `features` methods effectively uncallable.
469
- *
470
- * @example
471
- * ```ts
472
- * type Model = { count: number; meta: { features: { sidebar: boolean; modal: boolean } } };
473
- * type F = FeatureFlags<Model>; // { sidebar: boolean; modal: boolean }
474
- * ```
475
- */
476
- export type FeatureFlags<M> = M extends {
477
- meta: {
478
- features: infer F extends Record<string, boolean>;
479
- };
480
- } ? F : Record<never, boolean>;
481
- /**
482
- * Resolves to `unknown` when the model has no `meta.features` or all
483
- * feature values are booleans. Resolves to a descriptive string literal when
484
- * any value is not a boolean, causing an intersection with the model type
485
- * to become `never` and producing a compile-time error.
486
- *
487
- * @internal
488
- */
489
- export type ValidateFeatures<M> = M extends {
490
- meta: {
491
- features: infer F;
492
- };
493
- } ? F extends Record<string, boolean> ? unknown : "meta.features values must all be boolean" : unknown;
494
- /**
495
- * Utility type for the `meta` model property. Combines optional `nodes`
496
- * and `features` sub-objects into a single meta shape.
497
- *
498
- * - When `N` is provided, produces `{ nodes: { [K in keyof N]: N[K] | null } }`.
499
- * - When `F` is provided, produces `{ features: F }`.
500
- * - Pass `void` to omit either sub-object.
501
- *
502
- * @template N - Node type record (e.g. `{ input: HTMLInputElement }`)
503
- * @template F - Feature flags record (e.g. `{ sidebar: boolean }`)
504
- *
505
- * @example
506
- * ```ts
507
- * import type { Meta } from "chizu";
508
- *
509
- * type Model = {
510
- * count: number;
511
- * meta: Meta<{ input: HTMLInputElement }, { sidebar: boolean }>;
512
- * };
513
- * // Equivalent to:
514
- * // meta: { nodes: { input: HTMLInputElement | null }; features: { sidebar: boolean } }
515
- * ```
516
- */
517
- export type Meta<N extends Record<string, unknown> | void = void, F extends Record<string, boolean> | void = void> = ([N] extends [void] ? unknown : {
518
- nodes: {
519
- [K in keyof N]: N[K] | null;
520
- };
521
- }) & ([F] extends [void] ? unknown : {
522
- features: F;
523
- });
524
- export declare namespace Meta {
525
- type Nodes<N extends Record<string, unknown>> = Meta<N>;
526
- type Features<F extends Record<string, boolean>> = Meta<void, F>;
527
- }
528
392
  /**
529
393
  * Constraint type for action containers.
530
394
  * Actions are symbols grouped in an object (typically a class with static properties).
@@ -626,64 +490,13 @@ export type HandlerContext<M extends Model | void, _AC extends Actions | void, D
626
490
  * ```
627
491
  */
628
492
  readonly tasks: ReadonlySet<Task>;
629
- /**
630
- * Meta properties including captured DOM nodes and feature flags.
631
- *
632
- * @example
633
- * ```ts
634
- * actions.useAction(Actions.Focus, (context) => {
635
- * context.meta.nodes.input?.focus();
636
- * });
637
- * ```
638
- */
639
- readonly meta: {
640
- readonly nodes: {
641
- [K in keyof ExtractNodes<M>]: ExtractNodes<M>[K] | null;
642
- };
643
- readonly features: Readonly<FeatureFlags<M>>;
644
- };
645
- /**
646
- * The regulator API for controlling which actions may be dispatched.
647
- *
648
- * Each call replaces the previous policy entirely (last-write-wins).
649
- * The policy is shared across all components within the same `<Boundary>`.
650
- *
651
- * @example
652
- * ```ts
653
- * actions.useAction(Actions.Mount, (context) => {
654
- * // Block all actions except Critical
655
- * context.regulator.allow(Actions.Critical);
656
- * });
657
- *
658
- * actions.useAction(Actions.Unlock, (context) => {
659
- * // Re-allow all actions
660
- * context.regulator.allow();
661
- * });
662
- * ```
663
- */
664
- readonly regulator: Regulator;
665
493
  readonly actions: {
666
494
  produce<F extends (draft: {
667
- model: Omit<M, "meta">;
495
+ model: M;
668
496
  readonly inspect: Readonly<Inspect<M>>;
669
497
  }) => void>(ƒ: F & AssertSync<F>): void;
670
- dispatch(action: ActionOrChanneled, payload?: unknown, options?: MulticastOptions): Promise<void>;
498
+ dispatch(action: ActionOrChanneled, payload?: unknown): Promise<void>;
671
499
  annotate<T>(operation: Operation, value: T): T;
672
- /**
673
- * Feature toggle methods for mutating boolean flags on the model.
674
- *
675
- * @example
676
- * ```ts
677
- * context.actions.features.on("sidebar");
678
- * context.actions.features.off("sidebar");
679
- * context.actions.features.invert("sidebar");
680
- * ```
681
- */
682
- features: {
683
- on<K extends keyof FeatureFlags<M>>(name: K): void;
684
- off<K extends keyof FeatureFlags<M>>(name: K): void;
685
- invert<K extends keyof FeatureFlags<M>>(name: K): void;
686
- };
687
500
  /**
688
501
  * Returns the resolved broadcast or multicast value, waiting for any
689
502
  * pending annotations to settle before resolving.
@@ -692,8 +505,8 @@ export type HandlerContext<M extends Model | void, _AC extends Actions | void, D
692
505
  * Otherwise it waits until the next dispatch of the action.
693
506
  * Resolves with `null` if the task is aborted before a value arrives.
694
507
  *
695
- * @param action - The broadcast or multicast action to resolve.
696
- * @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.
697
510
  * @returns The dispatched value, or `null` if aborted.
698
511
  *
699
512
  * @example
@@ -708,15 +521,14 @@ export type HandlerContext<M extends Model | void, _AC extends Actions | void, D
708
521
  * });
709
522
  * ```
710
523
  */
711
- resolution<T>(action: BroadcastPayload<T>): Promise<T | null>;
712
- resolution<T>(action: MulticastPayload<T>, options: MulticastOptions): Promise<T | null>;
524
+ resolution<T>(action: BroadcastPayload<T> | MulticastPayload<T>): Promise<T | null>;
713
525
  /**
714
526
  * Returns the latest broadcast or multicast value immediately without
715
527
  * waiting for annotations to settle. Use this when you need the current
716
528
  * cached value and do not need to wait for pending operations to complete.
717
529
  *
718
- * @param action - The broadcast or multicast action to peek at.
719
- * @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.
720
532
  * @returns The cached value, or `null` if no value has been dispatched.
721
533
  *
722
534
  * @example
@@ -728,8 +540,7 @@ export type HandlerContext<M extends Model | void, _AC extends Actions | void, D
728
540
  * });
729
541
  * ```
730
542
  */
731
- peek<T>(action: BroadcastPayload<T>): T | null;
732
- peek<T>(action: MulticastPayload<T>, options: MulticastOptions): T | null;
543
+ peek<T>(action: BroadcastPayload<T> | MulticastPayload<T>): T | null;
733
544
  };
734
545
  };
735
546
  /**
@@ -815,80 +626,18 @@ export type Handlers<M extends Model | void, AC extends Actions | void, D extend
815
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>;
816
627
  };
817
628
  export type UseActions<M extends Model | void, AC extends Actions | void, D extends Props = Props> = [
818
- Readonly<NullableNodes<M>>,
629
+ Readonly<M>,
819
630
  {
820
631
  /**
821
- * Dispatches an action with an optional payload.
822
- *
823
- * For multicast actions, you MUST provide the scope carrier as the third argument:
824
- * ```ts
825
- * actions.dispatch(Actions.Multicast.Update, payload, { scope: Actions.Multicast });
826
- * ```
827
- *
828
- * @param action - The action to dispatch
829
- * @param payload - The payload to send with the action
830
- * @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.
831
635
  */
832
- dispatch<P>(action: HandlerPayload<P>, payload?: P, options?: MulticastOptions): Promise<void>;
833
- dispatch<P>(action: BroadcastPayload<P>, payload?: P, options?: MulticastOptions): Promise<void>;
834
- dispatch<P>(action: MulticastPayload<P>, payload: P, options: MulticastOptions): Promise<void>;
835
- 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>;
836
640
  inspect: Inspect<M>;
837
- /**
838
- * Meta properties including captured DOM nodes and feature flags.
839
- *
840
- * @example
841
- * ```tsx
842
- * actions.meta.nodes.input?.focus();
843
- * actions.meta.features.sidebar; // boolean
844
- * ```
845
- */
846
- meta: {
847
- readonly nodes: {
848
- [K in keyof ExtractNodes<M>]: ExtractNodes<M>[K] | null;
849
- };
850
- readonly features: Readonly<FeatureFlags<M>>;
851
- };
852
- /**
853
- * Captures a DOM node for later access via `meta.nodes`.
854
- * Use as a ref callback on JSX elements.
855
- *
856
- * @param name - The node key (must match a key in the model's `meta.nodes`)
857
- * @param node - The DOM node or null (when unmounting)
858
- *
859
- * @example
860
- * ```tsx
861
- * type Model = {
862
- * count: number;
863
- * meta: Meta<{ container: HTMLDivElement; input: HTMLInputElement }>;
864
- * };
865
- *
866
- * const [model, actions] = useActions<Model, typeof Actions>(model);
867
- *
868
- * return (
869
- * <div ref={node => actions.node('container', node)}>
870
- * <input ref={node => actions.node('input', node)} />
871
- * </div>
872
- * );
873
- * ```
874
- */
875
- node<K extends keyof ExtractNodes<M>>(name: K, node: ExtractNodes<M>[K] | null): void;
876
- /**
877
- * Feature toggle methods for mutating boolean flags on the model.
878
- *
879
- * @example
880
- * ```tsx
881
- * actions.features.on("sidebar");
882
- * actions.features.invert("sidebar");
883
- *
884
- * {model.meta.features.sidebar && <Sidebar />}
885
- * ```
886
- */
887
- features: {
888
- on<K extends keyof FeatureFlags<M>>(name: K): void;
889
- off<K extends keyof FeatureFlags<M>>(name: K): void;
890
- invert<K extends keyof FeatureFlags<M>>(name: K): void;
891
- };
892
641
  /**
893
642
  * Streams broadcast values declaratively in JSX using a render-prop pattern.
894
643
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chizu",
3
- "version": "0.4.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;