chizu 0.2.34 → 0.2.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,9 +14,10 @@ Strongly typed React framework using generators and efficiently updated views al
14
14
  1. [Benefits](#benefits)
15
15
  1. [Getting started](#getting-started)
16
16
  1. [Error handling](#error-handling)
17
- <!-- 1. [Distributed actions](#distributed-actions)
18
- 1. [Module dispatch](#module-dispatch)
19
- 1. [Associated context](#associated-context) -->
17
+ 1. [Model annotations](#model-annotations)
18
+ 1. [Lifecycle actions](#lifecycle-actions)
19
+ 1. [Distributed actions](#distributed-actions)
20
+ 1. [Action decorators](#action-decorators)
20
21
 
21
22
  ## Benefits
22
23
 
@@ -81,17 +82,19 @@ export class Actions {
81
82
  }
82
83
 
83
84
  export default function useNameActions() {
84
- const nameAction = useAction<Model, typeof Actions.Name>(async (context) => {
85
- context.actions.produce((draft) => {
86
- draft.name = null;
87
- });
85
+ const nameAction = useAction<Model, typeof Actions, "Name">(
86
+ async (context) => {
87
+ context.actions.produce((draft) => {
88
+ draft.name = null;
89
+ });
88
90
 
89
- const name = await fetch(/* ... */);
91
+ const name = await fetch(/* ... */);
90
92
 
91
- context.actions.produce((draft) => {
92
- draft.name = name;
93
- });
94
- });
93
+ context.actions.produce((draft) => {
94
+ draft.name = name;
95
+ });
96
+ },
97
+ );
95
98
 
96
99
  return useActions<Model, typeof Actions>(
97
100
  model,
@@ -120,160 +123,127 @@ export default function Profile(props: Props): React.ReactElement {
120
123
 
121
124
  ## Error handling
122
125
 
123
- Chizu provides a simple way to catch errors that occur within your actions. You can use the `ActionError` component to wrap your application and provide an error handler. This handler will be called whenever an error is thrown in an action.
126
+ Chizu provides a simple way to catch errors that occur within your actions. You can use the `Error` component to wrap your application and provide an error handler. This handler will be called whenever an error is thrown in an action.
124
127
 
125
128
  ```tsx
126
- import { ActionError } from "chizu";
129
+ import { Error } from "chizu";
127
130
 
128
131
  const App = () => (
129
- <ActionError handler={(error) => console.error(error)}>
132
+ <Error handler={(error) => console.error(error)}>
130
133
  <Profile />
131
- </ActionError>
134
+ </Error>
132
135
  );
133
136
  ```
134
137
 
135
- ## Handling states
138
+ ## Model annotations
139
+
140
+ Model annotations allow you to track the state of async operations on individual model fields. This is useful for showing loading indicators, optimistic updates, and tracking pending changes.
141
+
142
+ Use `context.actions.annotate` to mark a value with an operation type. The view can then inspect the field to check if it's pending, get the draft value, or check the operation type:
136
143
 
137
144
  ```ts
138
145
  import { Op } from "chizu";
139
146
 
140
- // Mark a value as pending with an operation
141
147
  context.actions.produce((model) => {
142
148
  model.name = context.actions.annotate(Op.Update, "New Name");
143
149
  });
150
+ ```
144
151
 
145
- // Check pending state
146
- actions.inspect.name.pending(); // true
147
-
148
- // Get remaining count of pending operations
149
- actions.inspect.name.remaining(); // 1 (next: actions.inspect.name.draft())
152
+ In the view, use `actions.inspect` to check the state of annotated fields:
150
153
 
151
- // Check specific operation
152
- actions.inspect.name.is(Op.Update); // true
154
+ ```ts
155
+ actions.inspect.name.pending(); // true if operation is in progress
156
+ actions.inspect.name.remaining(); // count of pending operations
157
+ actions.inspect.name.draft(); // the next value to be applied
158
+ actions.inspect.name.is(Op.Update); // check specific operation type
153
159
  ```
154
160
 
155
- <!-- However in the above example where the name is fetched asynchronously, there is no feedback to the user &ndash; we can improve that significantly by using the `module.actions.annotate` and `module.validate` helpers:
161
+ ## Lifecycle actions
156
162
 
157
- ```tsx
158
- export default <Actions<Module>>function Actions(module) {
159
- return {
160
- async *[Action.Name]() {
161
- yield module.actions.produce((draft) => {
162
- draft.name = module.actions.annotate(null);
163
- });
163
+ Chizu provides lifecycle actions that trigger at specific points in a component's lifecycle. Import `Lifecycle` from Chizu:
164
164
 
165
- const name = await fetch(/* ... */);
166
- return module.actions.produce((draft) => {
167
- draft.name = name;
168
- });
169
- },
170
- };
171
- };
172
- ```
165
+ ```ts
166
+ import { Lifecycle } from "chizu";
173
167
 
174
- ```tsx
175
- export default function ProfileView(props: Props): React.ReactElement {
176
- return (
177
- <Scope<Module> using={{ module, actions, props }}>
178
- {(module) => (
179
- <>
180
- <p>Hey {module.model.name}</p>
181
-
182
- {module.validate.name.pending() && <p>Switching profiles&hellip;</p>}
183
-
184
- <button
185
- disabled={module.validate.name.is(State.Op.Update)}
186
- onClick={() => module.actions.dispatch([Action.Name])}
187
- >
188
- Switch profile
189
- </button>
190
- </>
191
- )}
192
- </Scope>
193
- );
168
+ class {
169
+ [Lifecycle.Mount] = mountAction;
170
+ [Lifecycle.Node] = nodeAction;
171
+ [Lifecycle.Unmount] = unmountAction;
194
172
  }
195
173
  ```
196
174
 
197
-
175
+ - **`Lifecycle.Mount`** &ndash; Triggered once when the component mounts (`useLayoutEffect`).
176
+ - **`Lifecycle.Node`** &ndash; Triggered after the component renders (`useEffect`).
177
+ - **`Lifecycle.Unmount`** &ndash; Triggered when the component unmounts.
198
178
 
199
179
  ## Distributed actions
200
180
 
201
- Actions can communicate with other mounted actions using the `DistributedActions` approach. You can configure the enum and union type in the root of your application:
181
+ Distributed actions allow different components to communicate with each other. Unlike regular actions which are scoped to a single component, distributed actions are broadcast to all mounted components that have defined a handler for them.
182
+
183
+ To create a distributed action, use `createDistributedAction` instead of `createAction`. A good pattern is to define distributed actions in a shared class that other action classes can extend:
202
184
 
203
185
  ```ts
204
- export enum DistributedAction {
205
- SignedOut = "distributed/signed-out",
186
+ import { createAction, createDistributedAction } from "chizu";
187
+
188
+ export class DistributedActions {
189
+ static SignedOut = createDistributedAction();
206
190
  }
207
191
 
208
- export type DistributedActions = [DistributedAction.SignedOut];
192
+ export class Actions extends DistributedActions {
193
+ static Increment = createAction();
194
+ }
209
195
  ```
210
196
 
211
- Note that you must prefix the enum name with `distributed` for it to behave as a distributed event, otherwise it'll be considered a module event only. Once you have the distributed actions you simply need to augment the module actions union with the `DistributedActions` and use it as you do other actions:
197
+ Any component that defines a handler for `DistributedActions.SignedOut` will receive the action when it's dispatched from any other component. For direct access to the broadcast emitter, use `useBroadcast()`.
212
198
 
213
- ```ts
214
- export type Actions = DistributedActions | [Action.Task, string]; // etc...
215
- ```
216
-
217
- ## Module dispatch
199
+ ## Action decorators
218
200
 
219
- In the eventuality that you have a component but don't want associated actions, models, etc&hellip; but want to still fire actions either the closest module or a distributed action, you can use the `useScoped` hook:
201
+ Chizu provides decorators to add common functionality to your actions. Import `use` from Chizu and apply decorators to action properties:
220
202
 
221
203
  ```ts
222
- const module = useScoped<Module>();
223
-
224
- // ...
225
-
226
- module.actions.dispatch([Action.Task, "My task that needs to be done."]);
204
+ import { use } from "chizu";
227
205
  ```
228
206
 
229
- Alternatively you can pass the current module as a prop to your components using the `Scoped` helper:
207
+ ### `use.exclusive()`
208
+
209
+ Ensures only one instance of an action runs at a time. When a new action is dispatched, any previous running instance is automatically aborted. Use `context.signal` to cancel in-flight requests:
230
210
 
231
211
  ```ts
232
- export type Props = {
233
- module: Scoped<Module>;
234
- };
212
+ const searchAction = useAction<Model, typeof Actions, "Search">(
213
+ async (context, query) => {
214
+ const response = await fetch(`/search?q=${query}`, {
215
+ signal: context.signal,
216
+ });
217
+ },
218
+ );
219
+
220
+ return useActions<Model, typeof Actions>(
221
+ model,
222
+ class {
223
+ @use.exclusive()
224
+ [Actions.Search] = searchAction;
225
+ },
226
+ );
235
227
  ```
236
228
 
237
- ## Associated context
229
+ ### `use.reactive(() => [dependencies])`
238
230
 
239
- In many cases you'll still want to retrieve contextual values from within actions &ndash; which you can do by using the `module.actions.context` function:
231
+ Automatically triggers an action when its dependencies change. Dependencies must be primitives (strings, numbers, booleans, etc.) which means you never have to worry about referential equality:
240
232
 
241
- ```tsx
242
- export default <Actions<Module>>function Actions(module) {
243
- const context = module.actions.context({
244
- name: NameContext
245
- });
246
-
247
- return {
248
- [Action.Name](name) {
249
- return module.actions.produce((draft) => {
250
- draft.name = context.name;
251
- });
252
- },
253
- };
254
- };
233
+ ```ts
234
+ class {
235
+ @use.reactive(() => [props.userId])
236
+ [Actions.FetchUser] = fetchUserAction;
237
+ }
255
238
  ```
256
239
 
257
- If you need the context values to be reactive and fire the `Lifecycle.Derive` method then simply add it to your `props` definition when you initialise your scoped component:
240
+ ### `use.debug()`
258
241
 
259
- ```tsx
260
- export default function Profile(props: Props): React.ReactElement {
261
- const name = React.useContext(NameContext);
242
+ Logs detailed timing information for debugging, including when the action started, how many `produce` calls were made, and total duration:
262
243
 
263
- return (
264
- <Scope<Module> using={{ model, actions, props: { ...props, name } }}>
265
- {(module) => (
266
- <>
267
- <p>Hey {module.model.name}</p>
268
-
269
- <button
270
- onClick={() => module.actions.dispatch([Action.Name, randomName()])}
271
- >
272
- Switch profile
273
- </button>
274
- </>
275
- )}
276
- </Scope>
277
- );
244
+ ```ts
245
+ class {
246
+ @use.debug()
247
+ [Actions.Submit] = submitAction;
278
248
  }
279
- ``` -->
249
+ ```
@@ -1,19 +1,26 @@
1
- import { Action, Payload } from '../types';
1
+ import { Action, Payload } from '../types/index.ts';
2
2
  /**
3
3
  * Defines a new action with a given payload type.
4
4
  *
5
5
  * @template T The type of the payload that the action will carry.
6
- * @param {string} [name] An optional name for the action, used for debugging purposes.
7
- * @returns {Payload<T>} A new action object.
6
+ * @param name An optional name for the action, used for debugging purposes.
7
+ * @returns A new action symbol.
8
8
  */
9
9
  export declare function createAction<T = never>(name?: string): Payload<T>;
10
10
  /**
11
11
  * Defines a new distributed action with a given payload type.
12
- * Distributed actions can be shared across different modules.
12
+ * Distributed actions are broadcast to all mounted components that have defined a handler for them.
13
13
  *
14
14
  * @template T The type of the payload that the action will carry.
15
- * @param {string} [name] An optional name for the action, used for debugging purposes.
16
- * @returns {Payload<T>} A new distributed action object.
15
+ * @param name An optional name for the action, used for debugging purposes.
16
+ * @returns A new distributed action symbol.
17
17
  */
18
18
  export declare function createDistributedAction<T = never>(name?: string): Payload<T>;
19
+ /**
20
+ * Checks whether an action is a distributed action.
21
+ * Distributed actions are broadcast to all mounted components that have defined a handler for them.
22
+ *
23
+ * @param action The action to check.
24
+ * @returns True if the action is a distributed action, false otherwise.
25
+ */
19
26
  export declare function isDistributedAction(action: Action): boolean;
@@ -1,12 +1,12 @@
1
- import { BroadcastContext, Props } from './types.ts';
1
+ import { Props } from './types.ts';
2
2
  import * as React from "react";
3
- export declare function useBroadcast(): BroadcastContext;
3
+ export { useBroadcast } from './utils.ts';
4
4
  /**
5
- * Note: only needed if you want to create a new broadcast context, useful for
6
- * libraries that want to provide their own broadcast context without intefering
7
- * with the app's broadcast context.
5
+ * Creates a new broadcast context for distributed actions. Only needed if you
6
+ * want to isolate a broadcast context, useful for libraries that want to provide
7
+ * their own broadcast context without interfering with the app's broadcast context.
8
8
  *
9
- * @param param0 - { children }: Props
10
- * @returns {React.ReactNode}
9
+ * @param props.children - The children to render within the broadcast context.
10
+ * @returns The children wrapped in a broadcast context provider.
11
11
  */
12
12
  export declare function Broadcaster({ children }: Props): React.ReactNode;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,7 +1,12 @@
1
+ import { BroadcastContext } from './types.ts';
2
+ import * as React from "react";
1
3
  /**
2
- * Check if the name is a broadcast action.
4
+ * React context for broadcasting distributed actions across components.
5
+ */
6
+ export declare const Context: React.Context<BroadcastContext>;
7
+ /**
8
+ * Hook to access the broadcast context for emitting and listening to distributed actions.
3
9
  *
4
- * @param name {string}
5
- * @returns {boolean}
10
+ * @returns The broadcast context containing the EventEmitter instance.
6
11
  */
7
- export declare function isBroadcastAction(name: string): boolean;
12
+ export declare function useBroadcast(): BroadcastContext;
package/dist/chizu.js CHANGED
@@ -1,168 +1,252 @@
1
- import { jsx as h } from "react/jsx-runtime";
2
- import * as n from "react";
3
- import { createContext as A, useContext as O } from "react";
4
- import S from "eventemitter3";
5
- import { State as P } from "immertation";
6
- import { Op as Q, Operation as V, State as X } from "immertation";
7
- const E = A(void 0);
8
- function k() {
9
- return O(E);
10
- }
11
- function F({
1
+ import { jsx as x } from "react/jsx-runtime";
2
+ import * as i from "react";
3
+ import { createContext as P, useContext as j } from "react";
4
+ import v from "eventemitter3";
5
+ import { State as _ } from "immertation";
6
+ import { Op as te, Operation as ne, State as oe } from "immertation";
7
+ const C = P(void 0);
8
+ function A() {
9
+ return j(C);
10
+ }
11
+ function q({
12
12
  handler: e,
13
13
  children: t
14
14
  }) {
15
- return /* @__PURE__ */ h(E.Provider, { value: e, children: t });
15
+ return /* @__PURE__ */ x(C.Provider, { value: e, children: t });
16
16
  }
17
- function N(e = "anonymous") {
17
+ function H(e = "anonymous") {
18
18
  return Symbol(`chizu.action/${e}`);
19
19
  }
20
- function T(e = "anonymous") {
20
+ function J(e = "anonymous") {
21
21
  return Symbol(`chizu.action/distributed/${e}`);
22
22
  }
23
- function d(e) {
23
+ function w(e) {
24
24
  return e.toString().startsWith("Symbol(chizu.action/distributed/");
25
25
  }
26
- class p {
26
+ const $ = Symbol("chizu.action.context");
27
+ class h {
27
28
  static Mount = Symbol("lifecycle/mount");
28
29
  static Node = Symbol("lifecycle/node");
29
30
  static Derive = Symbol("lifecycle/derive");
30
31
  static Error = Symbol("lifecycle/error");
31
32
  static Unmount = Symbol("lifecycle/unmount");
32
33
  }
33
- function M(e) {
34
+ function D(e) {
34
35
  return new Promise((t) => setTimeout(t, e));
35
36
  }
36
- function g(e) {
37
+ function k(e) {
37
38
  return e ? !!(e && typeof e != "symbol") : Symbol(`pk.${Date.now()}.${crypto.randomUUID()}`);
38
39
  }
39
- const j = g;
40
- function x(e) {
41
- return (t, r) => {
40
+ const F = k;
41
+ function M(e) {
42
+ return (t, n) => {
42
43
  t.actions.produce((s) => {
43
- s[e] = r;
44
+ s[e] = n;
44
45
  });
45
46
  };
46
47
  }
47
- const z = x, I = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
48
+ const R = M, K = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
48
49
  __proto__: null,
49
- pk: g,
50
- set: x,
51
- sleep: M,
52
- κ: j,
53
- λ: z
54
- }, Symbol.toStringTag, { value: "Module" })), C = n.createContext({
55
- instance: new S()
50
+ pk: k,
51
+ set: M,
52
+ sleep: D,
53
+ κ: F,
54
+ λ: R
55
+ }, Symbol.toStringTag, { value: "Module" })), O = i.createContext({
56
+ instance: new v()
56
57
  });
57
- function D() {
58
- return n.useContext(C);
58
+ function G() {
59
+ return i.useContext(O);
59
60
  }
60
- function W({
61
+ function Q({
61
62
  children: e
62
63
  }) {
63
- const t = n.useMemo(() => ({
64
- instance: new S()
64
+ const t = i.useMemo(() => ({
65
+ instance: new v()
65
66
  }), []);
66
- return /* @__PURE__ */ h(C.Provider, { value: t, children: e });
67
+ return /* @__PURE__ */ x(O.Provider, { value: t, children: e });
67
68
  }
68
- function G(e, t) {
69
- return Object.keys(e).reduce((r, s) => (Object.defineProperty(r, s, {
69
+ function I(e, t) {
70
+ return Object.keys(e).reduce((n, s) => (Object.defineProperty(n, s, {
70
71
  get() {
71
72
  return t.current[s];
72
73
  },
73
74
  enumerable: !0
74
- }), r), {});
75
+ }), n), {});
75
76
  }
76
- const R = Symbol("chizu.action.context");
77
- function q(e) {
78
- const t = k();
79
- return n.useCallback(async (r, s) => {
77
+ const E = /* @__PURE__ */ new WeakMap(), S = /* @__PURE__ */ new WeakMap();
78
+ function L(e) {
79
+ return typeof e == "symbol" ? e.description ?? "unknown" : e;
80
+ }
81
+ const V = {
82
+ /**
83
+ * Ensures only one instance of an action runs at a time. When dispatched again,
84
+ * the previous instance is aborted via `context.signal`.
85
+ *
86
+ * @returns A decorator function for the action.
87
+ */
88
+ exclusive() {
89
+ return function(e, t) {
90
+ t.addInitializer(function() {
91
+ const n = this, s = n[t.name];
92
+ n[t.name] = async (u) => (E.get(n)?.controller.abort(), E.set(n, u[$]), await s.call(n, u));
93
+ });
94
+ };
95
+ },
96
+ /**
97
+ * Automatically triggers an action when its primitive dependencies change.
98
+ * Dependencies must be primitives (strings, numbers, booleans, etc.) to avoid
99
+ * referential equality issues.
100
+ *
101
+ * @param getDependencies A function returning an array of primitive dependencies.
102
+ * @returns A decorator function for the action.
103
+ */
104
+ reactive(e) {
105
+ return function(t, n) {
106
+ n.addInitializer(function() {
107
+ const s = this, u = S.get(s) ?? [];
108
+ u.push({
109
+ action: n.name,
110
+ getDependencies: e
111
+ }), S.set(s, u);
112
+ });
113
+ };
114
+ },
115
+ /**
116
+ * Logs detailed timing and debugging information for the action, including
117
+ * start time, produce call timings, and total duration.
118
+ *
119
+ * @returns A decorator function for the action.
120
+ */
121
+ debug() {
122
+ return function(e, t) {
123
+ t.addInitializer(function() {
124
+ const n = this, s = n[t.name], u = L(t.name);
125
+ n[t.name] = async (m) => {
126
+ let p = 0;
127
+ const f = performance.now(), y = [];
128
+ console.group(`🔧 Action: ${u}`), console.log("⏱️ Started at:", (/* @__PURE__ */ new Date()).toISOString());
129
+ const g = m.actions.produce, a = {
130
+ ...m,
131
+ actions: {
132
+ ...m.actions,
133
+ produce: (o) => {
134
+ p++;
135
+ const r = performance.now(), c = g(o), d = performance.now() - r;
136
+ return y.push(d), console.log(` 📝 produce #${p}: ${d.toFixed(2)}ms`), c;
137
+ }
138
+ }
139
+ };
140
+ try {
141
+ const o = await s.call(n, a), c = performance.now() - f;
142
+ return console.log("─".repeat(40)), console.log(`📊 Summary for ${u}:`), console.log(` Total produce calls: ${p}`), y.length > 0 && console.log(` Produce times: ${y.map((l) => l.toFixed(2) + "ms").join(", ")}`), console.log(` ⏱️ Total duration: ${c.toFixed(2)}ms`), console.groupEnd(), o;
143
+ } catch (o) {
144
+ const r = performance.now();
145
+ throw console.error(`❌ Error in ${u}:`, o), console.log(` ⏱️ Failed after: ${(r - f).toFixed(2)}ms`), console.groupEnd(), o;
146
+ }
147
+ };
148
+ });
149
+ };
150
+ }
151
+ };
152
+ function X(e) {
153
+ const t = A();
154
+ return i.useCallback(async (n, s) => {
80
155
  try {
81
156
  if (e.constructor.name === "GeneratorFunction" || e.constructor.name === "AsyncGeneratorFunction") {
82
- const a = e(r, s);
83
- for await (const w of a) ;
157
+ const m = e(n, s);
158
+ for await (const p of m) ;
84
159
  } else
85
- await e(r, s);
86
- } catch (l) {
160
+ await e(n, s);
161
+ } catch (u) {
87
162
  console.error(`Chizu
88
163
 
89
- `, l), t?.(l);
164
+ `, u), t?.(u);
90
165
  }
91
166
  }, [e, t]);
92
167
  }
93
- function H(e, t) {
94
- const r = D(), [s, l] = n.useState(e), a = n.useRef(new P(e)), w = _({
168
+ function Y(e, t) {
169
+ const n = G(), [s, u] = i.useState(e), m = i.useRef(new _(e)), p = T({
95
170
  model: s
96
- }), m = n.useMemo(() => new S(), []), v = n.useCallback((i) => {
171
+ }), f = i.useMemo(() => new v(), []), y = i.useRef({
172
+ previous: /* @__PURE__ */ new Map()
173
+ }), g = i.useCallback((a) => {
97
174
  const o = new AbortController();
98
175
  return {
99
176
  signal: o.signal,
100
177
  actions: {
101
- produce(c) {
178
+ produce(r) {
102
179
  if (o.signal.aborted) return;
103
- const u = a.current.mutate((f) => c(f));
104
- l(a.current.model), i.processes.add(u);
180
+ const c = m.current.mutate((l) => r(l));
181
+ u(m.current.model), a.processes.add(c);
105
182
  },
106
- dispatch(...[c, u]) {
107
- o.signal.aborted || (d(c) ? r.instance.emit(c, u) : m.emit(c, u));
183
+ dispatch(...[r, c]) {
184
+ o.signal.aborted || (w(r) ? n.instance.emit(r, c) : f.emit(r, c));
108
185
  },
109
- annotate(c, u) {
110
- return a.current.annotate(c, u);
186
+ annotate(r, c) {
187
+ return m.current.annotate(r, c);
111
188
  }
112
189
  },
113
- [R]: {
190
+ [$]: {
114
191
  controller: o
115
192
  }
116
193
  };
117
- }, [w.model]);
118
- return n.useLayoutEffect(() => {
119
- const i = new t();
120
- Object.getOwnPropertySymbols(i).forEach((o) => {
121
- const c = o;
122
- if (d(o))
123
- return void r.instance.on(o, async (u) => {
124
- const f = {
194
+ }, [p.model]);
195
+ return i.useLayoutEffect(() => {
196
+ const a = new t();
197
+ Object.getOwnPropertySymbols(a).forEach((o) => {
198
+ const r = o;
199
+ if (w(o))
200
+ return void n.instance.on(o, async (c) => {
201
+ const l = {
125
202
  processes: /* @__PURE__ */ new Set()
126
- }, y = Promise.withResolvers();
127
- await i[c](v(f), u), f.processes.forEach((b) => a.current.prune(b)), y.resolve();
203
+ }, d = Promise.withResolvers();
204
+ await a[r](g(l), c), l.processes.forEach((b) => m.current.prune(b)), d.resolve();
128
205
  });
129
- m.on(o, async (u) => {
130
- const f = {
206
+ f.on(o, async (c) => {
207
+ const l = {
131
208
  processes: /* @__PURE__ */ new Set()
132
- }, y = Promise.withResolvers();
133
- await i[c](v(f), u), f.processes.forEach((b) => a.current.prune(b)), y.resolve();
209
+ }, d = Promise.withResolvers();
210
+ await a[r](g(l), c), l.processes.forEach((b) => m.current.prune(b)), d.resolve();
134
211
  });
135
212
  });
136
- }, [m]), n.useLayoutEffect(() => (m.emit(p.Mount), () => void m.emit(p.Unmount)), []), n.useEffect(() => {
137
- m.emit(p.Node);
138
- }, []), n.useMemo(() => [s, {
139
- dispatch(...[i, o]) {
140
- d(i) ? r.instance.emit(i, o) : m.emit(i, o);
213
+ }, [f]), i.useEffect(() => {
214
+ const a = new t();
215
+ (S.get(a) ?? []).forEach((r) => {
216
+ const c = r.getDependencies(), l = y.current.previous.get(r.action) ?? [];
217
+ (c.length !== l.length || c.some((b, z) => !Object.is(b, l[z]))) && (y.current.previous.set(r.action, [...c]), f.emit(r.action));
218
+ });
219
+ }), i.useLayoutEffect(() => (f.emit(h.Mount), () => void f.emit(h.Unmount)), []), i.useEffect(() => {
220
+ f.emit(h.Node);
221
+ }, []), i.useMemo(() => [s, {
222
+ dispatch(...[a, o]) {
223
+ w(a) ? n.instance.emit(a, o) : f.emit(a, o);
141
224
  },
142
225
  consume() {
143
226
  },
144
227
  get inspect() {
145
- return a.current.inspect;
228
+ return m.current.inspect;
146
229
  }
147
- }], [s, m]);
230
+ }], [s, f]);
148
231
  }
149
- function _(e) {
150
- const t = n.useRef(e);
151
- return n.useLayoutEffect(() => {
232
+ function T(e) {
233
+ const t = i.useRef(e);
234
+ return i.useLayoutEffect(() => {
152
235
  t.current = e;
153
- }, [e]), n.useMemo(() => G(e, t), [e]);
236
+ }, [e]), i.useMemo(() => I(e, t), [e]);
154
237
  }
155
238
  export {
156
- F as ActionError,
157
- W as Broadcaster,
158
- p as Lifecycle,
159
- Q as Op,
160
- V as Operation,
161
- X as State,
162
- N as createAction,
163
- T as createDistributedAction,
164
- q as useAction,
165
- H as useActions,
166
- _ as useSnapshot,
167
- I as utils
239
+ Q as Broadcaster,
240
+ q as Error,
241
+ h as Lifecycle,
242
+ te as Op,
243
+ ne as Operation,
244
+ oe as State,
245
+ H as createAction,
246
+ J as createDistributedAction,
247
+ V as use,
248
+ X as useAction,
249
+ Y as useActions,
250
+ T as useSnapshot,
251
+ K as utils
168
252
  };
@@ -1,3 +1,3 @@
1
- (function(t,d){typeof exports=="object"&&typeof module<"u"?d(exports,require("react/jsx-runtime"),require("react"),require("eventemitter3"),require("immertation")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react","eventemitter3","immertation"],d):(t=typeof globalThis<"u"?globalThis:t||self,d(t.Chizu={},t.jsxRuntime,t.React,t.EventEmitter3,t.Immertation))})(this,(function(t,d,p,h,b){"use strict";function k(e){const n=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const r in e)if(r!=="default"){const c=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(n,r,c.get?c:{enumerable:!0,get:()=>e[r]})}}return n.default=e,Object.freeze(n)}const o=k(p),j=p.createContext(void 0);function z(){return p.useContext(j)}function D({handler:e,children:n}){return d.jsx(j.Provider,{value:e,children:n})}function _(e="anonymous"){return Symbol(`chizu.action/${e}`)}function T(e="anonymous"){return Symbol(`chizu.action/distributed/${e}`)}function w(e){return e.toString().startsWith("Symbol(chizu.action/distributed/")}class S{static Mount=Symbol("lifecycle/mount");static Node=Symbol("lifecycle/node");static Derive=Symbol("lifecycle/derive");static Error=Symbol("lifecycle/error");static Unmount=Symbol("lifecycle/unmount")}function G(e){return new Promise(n=>setTimeout(n,e))}function v(e){return e?!!(e&&typeof e!="symbol"):Symbol(`pk.${Date.now()}.${crypto.randomUUID()}`)}const L=v;function A(e){return(n,r)=>{n.actions.produce(c=>{c[e]=r})}}const q=Object.freeze(Object.defineProperty({__proto__:null,pk:v,set:A,sleep:G,κ:L,λ:A},Symbol.toStringTag,{value:"Module"})),P=o.createContext({instance:new h});function x(){return o.useContext(P)}function B({children:e}){const n=o.useMemo(()=>({instance:new h}),[]);return d.jsx(P.Provider,{value:n,children:e})}function R(e,n){return Object.keys(e).reduce((r,c)=>(Object.defineProperty(r,c,{get(){return n.current[c]},enumerable:!0}),r),{})}const U=Symbol("chizu.action.context");function $(e){const n=z();return o.useCallback(async(r,c)=>{try{if(e.constructor.name==="GeneratorFunction"||e.constructor.name==="AsyncGeneratorFunction"){const f=e(r,c);for await(const E of f);}else await e(r,c)}catch(y){console.error(`Chizu
1
+ (function(o,b){typeof exports=="object"&&typeof module<"u"?b(exports,require("react/jsx-runtime"),require("react"),require("eventemitter3"),require("immertation")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react","eventemitter3","immertation"],b):(o=typeof globalThis<"u"?globalThis:o||self,b(o.Chizu={},o.jsxRuntime,o.React,o.EventEmitter3,o.Immertation))})(this,(function(o,b,O,j,w){"use strict";function D(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const n in e)if(n!=="default"){const r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:()=>e[n]})}}return t.default=e,Object.freeze(t)}const u=D(O),C=O.createContext(void 0);function x(){return O.useContext(C)}function T({handler:e,children:t}){return b.jsx(C.Provider,{value:e,children:t})}function F(e="anonymous"){return Symbol(`chizu.action/${e}`)}function I(e="anonymous"){return Symbol(`chizu.action/distributed/${e}`)}function E(e){return e.toString().startsWith("Symbol(chizu.action/distributed/")}const $=Symbol("chizu.action.context");class S{static Mount=Symbol("lifecycle/mount");static Node=Symbol("lifecycle/node");static Derive=Symbol("lifecycle/derive");static Error=Symbol("lifecycle/error");static Unmount=Symbol("lifecycle/unmount")}function G(e){return new Promise(t=>setTimeout(t,e))}function M(e){return e?!!(e&&typeof e!="symbol"):Symbol(`pk.${Date.now()}.${crypto.randomUUID()}`)}const L=M;function k(e){return(t,n)=>{t.actions.produce(r=>{r[e]=n})}}const R=Object.freeze(Object.defineProperty({__proto__:null,pk:M,set:k,sleep:G,κ:L,λ:k},Symbol.toStringTag,{value:"Module"})),z=u.createContext({instance:new j});function q(){return u.useContext(z)}function B({children:e}){const t=u.useMemo(()=>({instance:new j}),[]);return b.jsx(z.Provider,{value:t,children:e})}function N(e,t){return Object.keys(e).reduce((n,r)=>(Object.defineProperty(n,r,{get(){return t.current[r]},enumerable:!0}),n),{})}const A=new WeakMap,P=new WeakMap;function U(e){return typeof e=="symbol"?e.description??"unknown":e}const W={exclusive(){return function(e,t){t.addInitializer(function(){const n=this,r=n[t.name];n[t.name]=async a=>(A.get(n)?.controller.abort(),A.set(n,a[$]),await r.call(n,a))})}},reactive(e){return function(t,n){n.addInitializer(function(){const r=this,a=P.get(r)??[];a.push({action:n.name,getDependencies:e}),P.set(r,a)})}},debug(){return function(e,t){t.addInitializer(function(){const n=this,r=n[t.name],a=U(t.name);n[t.name]=async m=>{let y=0;const d=performance.now(),g=[];console.group(`🔧 Action: ${a}`),console.log("⏱️ Started at:",new Date().toISOString());const v=m.actions.produce,l={...m,actions:{...m.actions,produce:c=>{y++;const s=performance.now(),i=v(c),p=performance.now()-s;return g.push(p),console.log(` 📝 produce #${y}: ${p.toFixed(2)}ms`),i}}};try{const c=await r.call(n,l),i=performance.now()-d;return console.log("─".repeat(40)),console.log(`📊 Summary for ${a}:`),console.log(` Total produce calls: ${y}`),g.length>0&&console.log(` Produce times: ${g.map(f=>f.toFixed(2)+"ms").join(", ")}`),console.log(` ⏱️ Total duration: ${i.toFixed(2)}ms`),console.groupEnd(),c}catch(c){const s=performance.now();throw console.error(`❌ Error in ${a}:`,c),console.log(` ⏱️ Failed after: ${(s-d).toFixed(2)}ms`),console.groupEnd(),c}}})}}};function H(e){const t=x();return u.useCallback(async(n,r)=>{try{if(e.constructor.name==="GeneratorFunction"||e.constructor.name==="AsyncGeneratorFunction"){const m=e(n,r);for await(const y of m);}else await e(n,r)}catch(a){console.error(`Chizu
2
2
 
3
- `,y),n?.(y)}},[e,n])}function N(e,n){const r=x(),[c,y]=o.useState(e),f=o.useRef(new b.State(e)),E=C({model:c}),l=o.useMemo(()=>new h,[]),M=o.useCallback(a=>{const s=new AbortController;return{signal:s.signal,actions:{produce(u){if(s.signal.aborted)return;const i=f.current.mutate(m=>u(m));y(f.current.model),a.processes.add(i)},dispatch(...[u,i]){s.signal.aborted||(w(u)?r.instance.emit(u,i):l.emit(u,i))},annotate(u,i){return f.current.annotate(u,i)}},[U]:{controller:s}}},[E.model]);return o.useLayoutEffect(()=>{const a=new n;Object.getOwnPropertySymbols(a).forEach(s=>{const u=s;if(w(s))return void r.instance.on(s,async i=>{const m={processes:new Set},O=Promise.withResolvers();await a[u](M(m),i),m.processes.forEach(g=>f.current.prune(g)),O.resolve()});l.on(s,async i=>{const m={processes:new Set},O=Promise.withResolvers();await a[u](M(m),i),m.processes.forEach(g=>f.current.prune(g)),O.resolve()})})},[l]),o.useLayoutEffect(()=>(l.emit(S.Mount),()=>void l.emit(S.Unmount)),[]),o.useEffect(()=>{l.emit(S.Node)},[]),o.useMemo(()=>[c,{dispatch(...[a,s]){w(a)?r.instance.emit(a,s):l.emit(a,s)},consume(){},get inspect(){return f.current.inspect}}],[c,l])}function C(e){const n=o.useRef(e);return o.useLayoutEffect(()=>{n.current=e},[e]),o.useMemo(()=>R(e,n),[e])}Object.defineProperty(t,"Op",{enumerable:!0,get:()=>b.Op}),Object.defineProperty(t,"Operation",{enumerable:!0,get:()=>b.Operation}),Object.defineProperty(t,"State",{enumerable:!0,get:()=>b.State}),t.ActionError=D,t.Broadcaster=B,t.Lifecycle=S,t.createAction=_,t.createDistributedAction=T,t.useAction=$,t.useActions=N,t.useSnapshot=C,t.utils=q,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})}));
3
+ `,a),t?.(a)}},[e,t])}function J(e,t){const n=q(),[r,a]=u.useState(e),m=u.useRef(new w.State(e)),y=_({model:r}),d=u.useMemo(()=>new j,[]),g=u.useRef({previous:new Map}),v=u.useCallback(l=>{const c=new AbortController;return{signal:c.signal,actions:{produce(s){if(c.signal.aborted)return;const i=m.current.mutate(f=>s(f));a(m.current.model),l.processes.add(i)},dispatch(...[s,i]){c.signal.aborted||(E(s)?n.instance.emit(s,i):d.emit(s,i))},annotate(s,i){return m.current.annotate(s,i)}},[$]:{controller:c}}},[y.model]);return u.useLayoutEffect(()=>{const l=new t;Object.getOwnPropertySymbols(l).forEach(c=>{const s=c;if(E(c))return void n.instance.on(c,async i=>{const f={processes:new Set},p=Promise.withResolvers();await l[s](v(f),i),f.processes.forEach(h=>m.current.prune(h)),p.resolve()});d.on(c,async i=>{const f={processes:new Set},p=Promise.withResolvers();await l[s](v(f),i),f.processes.forEach(h=>m.current.prune(h)),p.resolve()})})},[d]),u.useEffect(()=>{const l=new t;(P.get(l)??[]).forEach(s=>{const i=s.getDependencies(),f=g.current.previous.get(s.action)??[];(i.length!==f.length||i.some((h,K)=>!Object.is(h,f[K])))&&(g.current.previous.set(s.action,[...i]),d.emit(s.action))})}),u.useLayoutEffect(()=>(d.emit(S.Mount),()=>void d.emit(S.Unmount)),[]),u.useEffect(()=>{d.emit(S.Node)},[]),u.useMemo(()=>[r,{dispatch(...[l,c]){E(l)?n.instance.emit(l,c):d.emit(l,c)},consume(){},get inspect(){return m.current.inspect}}],[r,d])}function _(e){const t=u.useRef(e);return u.useLayoutEffect(()=>{t.current=e},[e]),u.useMemo(()=>N(e,t),[e])}Object.defineProperty(o,"Op",{enumerable:!0,get:()=>w.Op}),Object.defineProperty(o,"Operation",{enumerable:!0,get:()=>w.Operation}),Object.defineProperty(o,"State",{enumerable:!0,get:()=>w.State}),o.Broadcaster=B,o.Error=T,o.Lifecycle=S,o.createAction=F,o.createDistributedAction=I,o.use=W,o.useAction=H,o.useActions=J,o.useSnapshot=_,o.utils=R,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"})}));
@@ -1,4 +1,10 @@
1
- import { ErrorHandler, Props } from './types';
2
- export declare const ErrorContext: import('react').Context<ErrorHandler | undefined>;
3
- export declare function useActionError(): ErrorHandler | undefined;
4
- export declare function ActionError({ handler, children }: Props): import("react/jsx-runtime").JSX.Element;
1
+ import { Props } from './types';
2
+ export { useError } from './utils';
3
+ /**
4
+ * Error boundary component for catching and handling errors from actions.
5
+ *
6
+ * @param props.handler - The error handler function to call when an error occurs.
7
+ * @param props.children - The children to render within the error boundary.
8
+ * @returns The children wrapped in an error context provider.
9
+ */
10
+ export declare function Error({ handler, children }: Props): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ import { ErrorHandler } from './types.ts';
2
+ /**
3
+ * React context for handling errors that occur within actions.
4
+ */
5
+ export declare const ErrorContext: import('react').Context<ErrorHandler | undefined>;
6
+ /**
7
+ * Hook to access the error handler from the nearest Error provider.
8
+ *
9
+ * @returns The error handler function, or undefined if not within an Error provider.
10
+ */
11
+ export declare function useError(): ErrorHandler | undefined;
@@ -1,12 +1,12 @@
1
1
  import { Context, Model, Payload, Props, ActionsClass, Actions, UseActions } from '../types/index.ts';
2
2
  /**
3
- * Memoizes an action handler for performance optimization.
3
+ * Creates a memoized action handler.
4
4
  *
5
- * @template Model The type of the model.
6
- * @template Actions The type of the actions.
7
- * @template Action The specific action being handled.
8
- * @param {(context: Context<Model, Actions>, name: Action) => void} action The action handler function.
9
- * @returns {React.useCallback} The memoized action handler.
5
+ * @template M The type of the model.
6
+ * @template AC The type of the actions class.
7
+ * @template K The specific action key being handled.
8
+ * @param handler The action handler function that receives context and optional payload.
9
+ * @returns A memoized async function that executes the handler with error handling.
10
10
  */
11
11
  export declare function useAction<M extends Model, AC extends ActionsClass<any>, K extends Exclude<keyof AC, "prototype"> | never = never>(handler: (context: Context<M, AC>, payload: [K] extends [never] ? unknown : AC[K] extends Payload<infer P> ? P : unknown) => void | Promise<void> | AsyncGenerator | Generator): (context: Context<M, AC>, payload: [K] extends [never] ? unknown : AC[K] extends Payload<infer P> ? P : unknown) => Promise<void>;
12
12
  /**
@@ -44,7 +44,7 @@ export declare function useAction<M extends Model, AC extends ActionsClass<any>,
44
44
  *
45
45
  * // In your component
46
46
  * function Counter() {
47
- * const [model, actions] = useActions();
47
+ * const [model, actions] = useCounterActions();
48
48
  *
49
49
  * return (
50
50
  * <div>
@@ -1,5 +1,5 @@
1
1
  import { RefObject } from 'react';
2
- import { Props } from '../types';
2
+ import { Props } from '../types/index.ts';
3
3
  /**
4
4
  * @name withGetters
5
5
  * @description This function creates a new object with getters for each property of the input object.
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { ActionError } from './error/index.tsx';
1
+ export { Error } from './error/index.tsx';
2
2
  export { createAction, createDistributedAction } from './action/index.ts';
3
3
  export type { Pk, Context, ActionInstance, ActionsClass, UseActions, Actions, } from './types/index.ts';
4
4
  export { Lifecycle } from './types/index.ts';
@@ -7,3 +7,4 @@ export { Broadcaster } from './broadcast/index.tsx';
7
7
  export { useActions, useAction, useSnapshot } from './hooks/index.ts';
8
8
  export { Operation, Op, State } from 'immertation';
9
9
  export type { Box } from 'immertation';
10
+ export { use } from './use/index.ts';
@@ -1,5 +1,5 @@
1
1
  import { Operation, Process, Inspect } from 'immertation';
2
- import { context } from '../use';
2
+ export declare const context: unique symbol;
3
3
  export declare class Lifecycle {
4
4
  static Mount: symbol;
5
5
  static Node: symbol;
@@ -15,6 +15,8 @@ export type Payload<T = unknown> = symbol & {
15
15
  [PayloadKey]: T;
16
16
  };
17
17
  type PayloadType<A> = A extends Payload<infer P> ? P : never;
18
+ type IsAsync<F> = F extends (...args: any[]) => Promise<any> ? true : false;
19
+ type AssertSync<F> = IsAsync<F> extends true ? "Error: async functions are not allowed in produce" : F;
18
20
  type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
19
21
  export type Props = Record<string, unknown>;
20
22
  export type Action = symbol | string;
@@ -34,7 +36,7 @@ export type Context<M extends Model, AC extends ActionsClass<any>> = {
34
36
  model: M;
35
37
  signal: AbortSignal;
36
38
  actions: {
37
- produce(ƒ: (model: M) => void): M;
39
+ produce<F extends (model: M) => void>(ƒ: F & AssertSync<F>): M;
38
40
  dispatch<A extends AC[keyof AC] & Payload<any>>(...args: [PayloadType<A>] extends [never] ? [A] : [A, PayloadType<A>]): void;
39
41
  annotate<T>(operation: Operation, value: T): T;
40
42
  };
@@ -1,5 +1,30 @@
1
- import { Field } from './types';
2
- export { context } from './utils';
1
+ import { Field, Primitive } from './types.ts';
2
+ export { context, entries } from './utils.ts';
3
+ /**
4
+ * Action decorators for adding common functionality to action handlers.
5
+ */
3
6
  export declare const use: {
4
- serial(): (_: unknown, field: Field) => void;
7
+ /**
8
+ * Ensures only one instance of an action runs at a time. When dispatched again,
9
+ * the previous instance is aborted via `context.signal`.
10
+ *
11
+ * @returns A decorator function for the action.
12
+ */
13
+ exclusive(): (_: undefined, field: Field) => void;
14
+ /**
15
+ * Automatically triggers an action when its primitive dependencies change.
16
+ * Dependencies must be primitives (strings, numbers, booleans, etc.) to avoid
17
+ * referential equality issues.
18
+ *
19
+ * @param getDependencies A function returning an array of primitive dependencies.
20
+ * @returns A decorator function for the action.
21
+ */
22
+ reactive(getDependencies: () => Primitive[]): (_: undefined, field: Field) => void;
23
+ /**
24
+ * Logs detailed timing and debugging information for the action, including
25
+ * start time, produce call timings, and total duration.
26
+ *
27
+ * @returns A decorator function for the action.
28
+ */
29
+ debug(): (_: undefined, field: Field) => void;
5
30
  };
@@ -1,8 +1,14 @@
1
- import { ActionsClass, Context, Model, Payload } from '../types';
2
- export type Field = ClassFieldDecoratorContext<object, Context<Model, ActionsClass<Record<string, Payload>>>>;
1
+ import { ActionsClass, Context, Model, Payload } from '../types/index.ts';
2
+ export type Primitive = string | number | boolean | null | undefined | symbol | bigint;
3
+ export type Field = ClassFieldDecoratorContext<object, (...args: any[]) => any>;
3
4
  export type Args = Context<Model, ActionsClass<Record<string, Payload>>>;
4
5
  export type Instance = Record<string | symbol, unknown>;
5
6
  export type Method = (args: Args) => Promise<unknown>;
6
- export type ContextValue = {
7
+ export type Internals = {
7
8
  controller: AbortController;
8
9
  };
10
+ export type Entry = {
11
+ action: string | symbol;
12
+ getDependencies(): Primitive[];
13
+ };
14
+ export type Entries = Entry[];
@@ -1,3 +1,12 @@
1
- import { ContextValue } from './types';
2
- export declare const contexts: WeakMap<object, ContextValue>;
3
- export declare const context: unique symbol;
1
+ import { context } from '../types/index.ts';
2
+ import { Internals, Entries } from './types.ts';
3
+ export { context };
4
+ export declare const internals: WeakMap<object, Internals>;
5
+ export declare const entries: WeakMap<object, Entries>;
6
+ /**
7
+ * Extracts a readable name from an action symbol or string.
8
+ *
9
+ * @param name The action name as a symbol or string.
10
+ * @returns A human-readable string representation of the action name.
11
+ */
12
+ export declare function actionName(name: string | symbol): string;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,11 @@
1
1
  import { ActionsClass, Context, Model, Payload, Pk } from '../types/index.ts';
2
- export { default as sleep } from './sleep/index.ts';
2
+ /**
3
+ * Returns a promise that resolves after the specified number of milliseconds.
4
+ *
5
+ * @param ms The number of milliseconds to sleep.
6
+ * @returns A promise that resolves after the delay.
7
+ */
8
+ export declare function sleep(ms: number): Promise<void>;
3
9
  /**
4
10
  * Generates a unique primary key.
5
11
  * @returns A new unique symbol representing the primary key.
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chizu",
3
- "version": "0.2.34",
3
+ "version": "0.2.35",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "main": "./dist/chizu.js",
@@ -44,6 +44,7 @@
44
44
  "@faker-js/faker": "^10.1.0",
45
45
  "@jest/globals": "^30.2.0",
46
46
  "@mobily/ts-belt": "^3.13.1",
47
+ "@playwright/test": "^1.57.0",
47
48
  "@testing-library/dom": "^10.4.1",
48
49
  "@testing-library/jest-dom": "^6.9.1",
49
50
  "@testing-library/react": "^16.3.0",
@@ -64,6 +65,7 @@
64
65
  "jest": "^30.2.0",
65
66
  "jest-environment-jsdom": "^30.2.0",
66
67
  "lucide-react": "^0.555.0",
68
+ "madge": "^8.0.0",
67
69
  "prettier": "^3.7.4",
68
70
  "ramda": "^0.32.0",
69
71
  "react": "^19.2.1",
@@ -1,6 +0,0 @@
1
- export declare function Distributed(): (_value: unknown, context: ClassFieldDecoratorContext) => (initialValue: any) => any;
2
- declare const _default: {
3
- Synchronous(target: any, propertyKey: string): (...args: any[]) => any;
4
- Debounce(target: any, propertyKey: string): (...args: any[]) => any;
5
- };
6
- export default _default;
@@ -1 +0,0 @@
1
- export default function sleep(ms: number): Promise<void>;