@kuindji/reactive 1.0.23 → 1.1.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 (58) hide show
  1. package/README.md +34 -11
  2. package/dist/action.d.ts +12 -10
  3. package/dist/action.js +23 -16
  4. package/dist/actionBus.d.ts +8 -4
  5. package/dist/actionBus.js +37 -8
  6. package/dist/actionMap.d.ts +21 -19
  7. package/dist/actionMap.js +12 -9
  8. package/dist/event.d.ts +3 -2
  9. package/dist/event.js +160 -109
  10. package/dist/eventBus.d.ts +5 -3
  11. package/dist/eventBus.js +158 -104
  12. package/dist/index.d.ts +7 -7
  13. package/dist/index.js +7 -23
  14. package/dist/lib/actionMapInternal.d.ts +8 -0
  15. package/dist/lib/actionMapInternal.js +8 -0
  16. package/dist/lib/asyncCall.js +1 -4
  17. package/dist/lib/isPromiseLike.d.ts +1 -0
  18. package/dist/lib/isPromiseLike.js +5 -0
  19. package/dist/lib/listenerSorter.js +1 -4
  20. package/dist/lib/normalizeEventOptions.d.ts +13 -0
  21. package/dist/lib/normalizeEventOptions.js +21 -0
  22. package/dist/lib/tagsIntersect.js +1 -4
  23. package/dist/lib/types.js +4 -7
  24. package/dist/react/ErrorBoundary.d.ts +1 -1
  25. package/dist/react/ErrorBoundary.js +10 -13
  26. package/dist/react/listenerOptionsEqual.d.ts +27 -0
  27. package/dist/react/listenerOptionsEqual.js +121 -0
  28. package/dist/react/useAction.d.ts +3 -3
  29. package/dist/react/useAction.js +25 -25
  30. package/dist/react/useActionBus.d.ts +4 -4
  31. package/dist/react/useActionBus.js +41 -14
  32. package/dist/react/useActionMap.d.ts +4 -4
  33. package/dist/react/useActionMap.js +46 -16
  34. package/dist/react/useEvent.d.ts +2 -2
  35. package/dist/react/useEvent.js +30 -17
  36. package/dist/react/useEventBus.d.ts +2 -2
  37. package/dist/react/useEventBus.js +27 -26
  38. package/dist/react/useListenToAction.d.ts +1 -1
  39. package/dist/react/useListenToAction.js +24 -48
  40. package/dist/react/useListenToActionBus.d.ts +3 -3
  41. package/dist/react/useListenToActionBus.js +22 -19
  42. package/dist/react/useListenToEvent.d.ts +2 -2
  43. package/dist/react/useListenToEvent.js +13 -14
  44. package/dist/react/useListenToEventBus.d.ts +3 -3
  45. package/dist/react/useListenToEventBus.js +14 -15
  46. package/dist/react/useListenToStoreChanges.d.ts +3 -3
  47. package/dist/react/useListenToStoreChanges.js +12 -13
  48. package/dist/react/useReconciledListener.d.ts +33 -0
  49. package/dist/react/useReconciledListener.js +44 -0
  50. package/dist/react/useStore.d.ts +2 -2
  51. package/dist/react/useStore.js +72 -23
  52. package/dist/react/useStoreState.d.ts +2 -2
  53. package/dist/react/useStoreState.js +16 -26
  54. package/dist/react.d.ts +13 -13
  55. package/dist/react.js +13 -29
  56. package/dist/store.d.ts +9 -8
  57. package/dist/store.js +116 -53
  58. package/package.json +13 -3
@@ -1,18 +1,14 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ErrorBoundaryContext = void 0;
4
- exports.ErrorBoundary = ErrorBoundary;
5
- const jsx_runtime_1 = require("react/jsx-runtime");
6
- const react_1 = require("react");
7
- exports.ErrorBoundaryContext = (0, react_1.createContext)(null);
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useCallback, useContext, useRef } from "react";
3
+ export const ErrorBoundaryContext = createContext(null);
8
4
  function ErrorBoundary({ children, listener }) {
9
- const boundaryErrorListener = (0, react_1.useContext)(exports.ErrorBoundaryContext);
10
- const thisRef = (0, react_1.useRef)(listener);
11
- const outerRef = (0, react_1.useRef)(boundaryErrorListener);
5
+ const boundaryErrorListener = useContext(ErrorBoundaryContext);
6
+ const thisRef = useRef(listener);
7
+ const outerRef = useRef(boundaryErrorListener);
12
8
  // Keep refs in sync with props
13
9
  thisRef.current = listener;
14
10
  outerRef.current = boundaryErrorListener;
15
- const thisErrorListener = (0, react_1.useCallback)((errorResponse) => {
11
+ const thisErrorListener = useCallback((errorResponse) => {
16
12
  if (thisRef.current) {
17
13
  thisRef.current(errorResponse);
18
14
  }
@@ -23,6 +19,7 @@ function ErrorBoundary({ children, listener }) {
23
19
  throw errorResponse.error;
24
20
  }
25
21
  }, []);
26
- return ((0, jsx_runtime_1.jsx)(exports.ErrorBoundaryContext.Provider, { value: thisErrorListener, children: children }));
22
+ return (_jsx(ErrorBoundaryContext.Provider, { value: thisErrorListener, children: children }));
27
23
  }
28
- exports.default = ErrorBoundary;
24
+ export default ErrorBoundary;
25
+ export { ErrorBoundary };
@@ -0,0 +1,27 @@
1
+ import type { EventOptions, ListenerOptions } from "../event.js";
2
+ import type { BaseEventMap, EventBusOptions } from "../eventBus.js";
3
+ import type { BaseHandler } from "../lib/types.js";
4
+ /**
5
+ * Order-insensitive set comparison for listener tags.
6
+ * `undefined`, `[]` and missing all compare equal. Order and duplicates do
7
+ * not matter because the core only ever uses tags via membership and
8
+ * intersection checks.
9
+ */
10
+ export declare function areTagsEqual(a?: string[], b?: string[]): boolean;
11
+ /**
12
+ * Domain-specific comparator for {@link ListenerOptions}. Avoids generic deep
13
+ * equality: primitives compare after default semantics, `context`/`extraData`
14
+ * compare by reference, and `tags` use order-insensitive set comparison.
15
+ */
16
+ export declare function areListenerOptionsEqual(a?: ListenerOptions | null, b?: ListenerOptions | null): boolean;
17
+ /**
18
+ * Domain-specific comparator for {@link EventOptions}. Primitives compare after
19
+ * default semantics; `filter`/`filterContext` compare by reference.
20
+ */
21
+ export declare function areEventOptionsEqual(a?: EventOptions<BaseHandler> | null, b?: EventOptions<BaseHandler> | null): boolean;
22
+ /**
23
+ * Compares the per-event `eventOptions` maps of two {@link EventBusOptions}.
24
+ * Equal when every event name present in either map has semantically equal
25
+ * {@link EventOptions} (missing entries compare as default options).
26
+ */
27
+ export declare function areEventBusOptionsEqual(a?: EventBusOptions<BaseEventMap> | null, b?: EventBusOptions<BaseEventMap> | null): boolean;
@@ -0,0 +1,121 @@
1
+ function normalizeAsync(value) {
2
+ if (value === undefined || value === null) {
3
+ return null;
4
+ }
5
+ if (value === true) {
6
+ return 1;
7
+ }
8
+ return value;
9
+ }
10
+ /**
11
+ * Order-insensitive set comparison for listener tags.
12
+ * `undefined`, `[]` and missing all compare equal. Order and duplicates do
13
+ * not matter because the core only ever uses tags via membership and
14
+ * intersection checks.
15
+ */
16
+ export function areTagsEqual(a, b) {
17
+ const aa = a !== null && a !== void 0 ? a : [];
18
+ const bb = b !== null && b !== void 0 ? b : [];
19
+ if (aa.length === 0 && bb.length === 0) {
20
+ return true;
21
+ }
22
+ const sa = new Set(aa);
23
+ const sb = new Set(bb);
24
+ if (sa.size !== sb.size) {
25
+ return false;
26
+ }
27
+ for (const tag of sa) {
28
+ if (!sb.has(tag)) {
29
+ return false;
30
+ }
31
+ }
32
+ return true;
33
+ }
34
+ /**
35
+ * Domain-specific comparator for {@link ListenerOptions}. Avoids generic deep
36
+ * equality: primitives compare after default semantics, `context`/`extraData`
37
+ * compare by reference, and `tags` use order-insensitive set comparison.
38
+ */
39
+ export function areListenerOptionsEqual(a, b) {
40
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
41
+ const aa = a !== null && a !== void 0 ? a : {};
42
+ const bb = b !== null && b !== void 0 ? b : {};
43
+ if (((_a = aa.limit) !== null && _a !== void 0 ? _a : 0) !== ((_b = bb.limit) !== null && _b !== void 0 ? _b : 0)) {
44
+ return false;
45
+ }
46
+ if (((_c = aa.start) !== null && _c !== void 0 ? _c : 1) !== ((_d = bb.start) !== null && _d !== void 0 ? _d : 1)) {
47
+ return false;
48
+ }
49
+ if (((_e = aa.first) !== null && _e !== void 0 ? _e : false) !== ((_f = bb.first) !== null && _f !== void 0 ? _f : false)) {
50
+ return false;
51
+ }
52
+ if (((_g = aa.alwaysFirst) !== null && _g !== void 0 ? _g : false) !== ((_h = bb.alwaysFirst) !== null && _h !== void 0 ? _h : false)) {
53
+ return false;
54
+ }
55
+ if (((_j = aa.alwaysLast) !== null && _j !== void 0 ? _j : false) !== ((_k = bb.alwaysLast) !== null && _k !== void 0 ? _k : false)) {
56
+ return false;
57
+ }
58
+ if (normalizeAsync(aa.async) !== normalizeAsync(bb.async)) {
59
+ return false;
60
+ }
61
+ if (((_l = aa.context) !== null && _l !== void 0 ? _l : null) !== ((_m = bb.context) !== null && _m !== void 0 ? _m : null)) {
62
+ return false;
63
+ }
64
+ // reference equality only; do not deep compare arbitrary values
65
+ if (aa.extraData !== bb.extraData) {
66
+ return false;
67
+ }
68
+ if (!areTagsEqual(aa.tags, bb.tags)) {
69
+ return false;
70
+ }
71
+ return true;
72
+ }
73
+ /**
74
+ * Domain-specific comparator for {@link EventOptions}. Primitives compare after
75
+ * default semantics; `filter`/`filterContext` compare by reference.
76
+ */
77
+ export function areEventOptionsEqual(a, b) {
78
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
79
+ const aa = a !== null && a !== void 0 ? a : {};
80
+ const bb = b !== null && b !== void 0 ? b : {};
81
+ if (normalizeAsync(aa.async) !== normalizeAsync(bb.async)) {
82
+ return false;
83
+ }
84
+ if (((_a = aa.limit) !== null && _a !== void 0 ? _a : null) !== ((_b = bb.limit) !== null && _b !== void 0 ? _b : null)) {
85
+ return false;
86
+ }
87
+ if (((_c = aa.autoTrigger) !== null && _c !== void 0 ? _c : null) !== ((_d = bb.autoTrigger) !== null && _d !== void 0 ? _d : null)) {
88
+ return false;
89
+ }
90
+ if (((_e = aa.maxListeners) !== null && _e !== void 0 ? _e : 0) !== ((_f = bb.maxListeners) !== null && _f !== void 0 ? _f : 0)) {
91
+ return false;
92
+ }
93
+ // reference equality
94
+ if (((_g = aa.filter) !== null && _g !== void 0 ? _g : null) !== ((_h = bb.filter) !== null && _h !== void 0 ? _h : null)) {
95
+ return false;
96
+ }
97
+ if (((_j = aa.filterContext) !== null && _j !== void 0 ? _j : null) !== ((_k = bb.filterContext) !== null && _k !== void 0 ? _k : null)) {
98
+ return false;
99
+ }
100
+ return true;
101
+ }
102
+ /**
103
+ * Compares the per-event `eventOptions` maps of two {@link EventBusOptions}.
104
+ * Equal when every event name present in either map has semantically equal
105
+ * {@link EventOptions} (missing entries compare as default options).
106
+ */
107
+ export function areEventBusOptionsEqual(a, b) {
108
+ var _a, _b;
109
+ const aMap = (_a = a === null || a === void 0 ? void 0 : a.eventOptions) !== null && _a !== void 0 ? _a : {};
110
+ const bMap = (_b = b === null || b === void 0 ? void 0 : b.eventOptions) !== null && _b !== void 0 ? _b : {};
111
+ const names = new Set([
112
+ ...Object.keys(aMap),
113
+ ...Object.keys(bMap),
114
+ ]);
115
+ for (const name of names) {
116
+ if (!areEventOptionsEqual(aMap[name], bMap[name])) {
117
+ return false;
118
+ }
119
+ }
120
+ return true;
121
+ }
@@ -1,5 +1,5 @@
1
- import { createAction } from "../action";
2
- import type { ActionResponse, BeforeActionSignature, ListenerSignature } from "../action";
3
- import type { BaseHandler, ErrorListenerSignature, ErrorResponse } from "../lib/types";
1
+ import { createAction } from "../action.js";
2
+ import type { ActionResponse, BeforeActionSignature, ListenerSignature } from "../action.js";
3
+ import type { BaseHandler, ErrorListenerSignature, ErrorResponse } from "../lib/types.js";
4
4
  export type { ActionResponse, BaseHandler, ErrorListenerSignature, ErrorResponse, ListenerSignature, };
5
5
  export declare function useAction<ActionSignature extends BaseHandler, Listener extends ListenerSignature<ActionSignature>, ErrorListener extends ErrorListenerSignature<Parameters<ActionSignature>>, BeforeActionListener extends BeforeActionSignature<ActionSignature>>(actionSignature: ActionSignature, listener?: Listener | null, errorListener?: ErrorListener | null, beforeActionListener?: BeforeActionListener | null): ReturnType<typeof createAction<ActionSignature>>;
@@ -1,18 +1,15 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useAction = useAction;
4
- const react_1 = require("react");
5
- const action_1 = require("../action");
6
- const ErrorBoundary_1 = require("./ErrorBoundary");
7
- function useAction(actionSignature, listener, errorListener, beforeActionListener) {
8
- const boundaryErrorListener = (0, react_1.useContext)(ErrorBoundary_1.ErrorBoundaryContext);
9
- const updateRef = (0, react_1.useRef)(0);
10
- const listenerRef = (0, react_1.useRef)(listener);
11
- const errorListenerRef = (0, react_1.useRef)(errorListener);
12
- const boundaryErrorListenerRef = (0, react_1.useRef)(boundaryErrorListener);
13
- const beforeActionListenerRef = (0, react_1.useRef)(beforeActionListener);
14
- const action = (0, react_1.useMemo)(() => {
15
- const action = (0, action_1.createAction)(actionSignature);
1
+ import { useContext, useEffect, useMemo, useRef } from "react";
2
+ import { createAction } from "../action.js";
3
+ import { ErrorBoundaryContext } from "./ErrorBoundary.js";
4
+ export function useAction(actionSignature, listener, errorListener, beforeActionListener) {
5
+ const boundaryErrorListener = useContext(ErrorBoundaryContext);
6
+ const actionSignatureRef = useRef(actionSignature);
7
+ const listenerRef = useRef(listener);
8
+ const errorListenerRef = useRef(errorListener);
9
+ const boundaryErrorListenerRef = useRef(boundaryErrorListener);
10
+ const beforeActionListenerRef = useRef(beforeActionListener);
11
+ const action = useMemo(() => {
12
+ const action = createAction(actionSignature);
16
13
  if (listenerRef.current) {
17
14
  action.addListener(listenerRef.current);
18
15
  }
@@ -27,13 +24,17 @@ function useAction(actionSignature, listener, errorListener, beforeActionListene
27
24
  }
28
25
  return action;
29
26
  }, []);
30
- (0, react_1.useEffect)(() => {
31
- if (updateRef.current > 0) {
32
- throw new Error("Action cannot be updated");
27
+ // Replace the action function in place when its reference changes,
28
+ // preserving all listeners and the action identity. A changed function
29
+ // must keep a compatible signature (TypeScript fixes the generic from the
30
+ // initial render).
31
+ useEffect(() => {
32
+ if (actionSignatureRef.current !== actionSignature) {
33
+ action.setAction(actionSignature);
34
+ actionSignatureRef.current = actionSignature;
33
35
  }
34
- updateRef.current++;
35
36
  }, [actionSignature]);
36
- (0, react_1.useEffect)(() => {
37
+ useEffect(() => {
37
38
  if (listenerRef.current !== listener) {
38
39
  if (listenerRef.current) {
39
40
  action.removeListener(listenerRef.current);
@@ -44,7 +45,7 @@ function useAction(actionSignature, listener, errorListener, beforeActionListene
44
45
  }
45
46
  }
46
47
  }, [listener]);
47
- (0, react_1.useEffect)(() => {
48
+ useEffect(() => {
48
49
  if (errorListenerRef.current !== errorListener) {
49
50
  if (errorListenerRef.current) {
50
51
  action.removeErrorListener(errorListenerRef.current);
@@ -55,7 +56,7 @@ function useAction(actionSignature, listener, errorListener, beforeActionListene
55
56
  }
56
57
  }
57
58
  }, [errorListener]);
58
- (0, react_1.useEffect)(() => {
59
+ useEffect(() => {
59
60
  if (boundaryErrorListenerRef.current !== boundaryErrorListener) {
60
61
  if (boundaryErrorListenerRef.current) {
61
62
  action.removeErrorListener(boundaryErrorListenerRef.current);
@@ -66,7 +67,7 @@ function useAction(actionSignature, listener, errorListener, beforeActionListene
66
67
  }
67
68
  }
68
69
  }, [boundaryErrorListener]);
69
- (0, react_1.useEffect)(() => {
70
+ useEffect(() => {
70
71
  if (beforeActionListenerRef.current !== beforeActionListener) {
71
72
  if (beforeActionListenerRef.current) {
72
73
  action.removeBeforeActionListener(beforeActionListenerRef.current);
@@ -77,7 +78,7 @@ function useAction(actionSignature, listener, errorListener, beforeActionListene
77
78
  }
78
79
  }
79
80
  }, [beforeActionListener]);
80
- (0, react_1.useEffect)(() => {
81
+ useEffect(() => {
81
82
  return () => {
82
83
  if (listenerRef.current) {
83
84
  listenerRef.current = null;
@@ -94,7 +95,6 @@ function useAction(actionSignature, listener, errorListener, beforeActionListene
94
95
  action.removeAllListeners();
95
96
  action.removeAllBeforeActionListeners();
96
97
  action.removeAllErrorListeners();
97
- updateRef.current = 0;
98
98
  };
99
99
  }, []);
100
100
  return action;
@@ -1,6 +1,6 @@
1
- import type { ActionResponse, ListenerSignature } from "../action";
2
- import type { BaseActionsMap } from "../actionBus";
3
- import { createActionBus } from "../actionBus";
4
- import type { ErrorListenerSignature, ErrorResponse } from "../lib/types";
1
+ import type { ActionResponse, ListenerSignature } from "../action.js";
2
+ import type { BaseActionsMap } from "../actionBus.js";
3
+ import { createActionBus } from "../actionBus.js";
4
+ import type { ErrorListenerSignature, ErrorResponse } from "../lib/types.js";
5
5
  export type { ActionResponse, BaseActionsMap, ErrorListenerSignature, ErrorResponse, ListenerSignature, };
6
6
  export declare function useActionBus<ActionsMap extends BaseActionsMap = BaseActionsMap>(initialActions?: ActionsMap, errorListener?: ErrorListenerSignature<any[]>): ReturnType<typeof createActionBus<ActionsMap>>;
@@ -1,15 +1,12 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useActionBus = useActionBus;
4
- const react_1 = require("react");
5
- const actionBus_1 = require("../actionBus");
6
- const ErrorBoundary_1 = require("./ErrorBoundary");
7
- function useActionBus(initialActions, errorListener) {
8
- const boundaryErrorListener = (0, react_1.useContext)(ErrorBoundary_1.ErrorBoundaryContext);
9
- const errorListenerRef = (0, react_1.useRef)(errorListener);
10
- const boundaryErrorListenerRef = (0, react_1.useRef)(boundaryErrorListener);
11
- const actionBus = (0, react_1.useMemo)(() => {
12
- const actionBus = (0, actionBus_1.createActionBus)(initialActions);
1
+ import { useContext, useEffect, useMemo, useRef } from "react";
2
+ import { createActionBus } from "../actionBus.js";
3
+ import { ErrorBoundaryContext } from "./ErrorBoundary.js";
4
+ export function useActionBus(initialActions, errorListener) {
5
+ const boundaryErrorListener = useContext(ErrorBoundaryContext);
6
+ const errorListenerRef = useRef(errorListener);
7
+ const boundaryErrorListenerRef = useRef(boundaryErrorListener);
8
+ const actionBus = useMemo(() => {
9
+ const actionBus = createActionBus(initialActions);
13
10
  if (errorListener) {
14
11
  actionBus.addErrorListener(errorListener);
15
12
  }
@@ -18,7 +15,37 @@ function useActionBus(initialActions, errorListener) {
18
15
  }
19
16
  return actionBus;
20
17
  }, []);
21
- (0, react_1.useEffect)(() => {
18
+ // Reconcile the actions map every render. Functions are compared by
19
+ // reference and never invoked.
20
+ const appliedActionsRef = useRef(Object.assign({}, initialActions));
21
+ const nextActions = (initialActions !== null && initialActions !== void 0 ? initialActions : {});
22
+ // Add newly-introduced actions during render (not in an effect): React runs
23
+ // child passive effects BEFORE parent passive effects, so a child rendered
24
+ // in the same pass that subscribes to a new action would otherwise throw
25
+ // "Action <name> not found". Parent render precedes child render, and
26
+ // add() is idempotent (a no-op if the action already exists).
27
+ for (const key in nextActions) {
28
+ actionBus.add(key, nextActions[key]);
29
+ }
30
+ // Replacements and removals can be deferred to a passive effect: a replaced
31
+ // action keeps its identity/listeners (so subscriptions are unaffected by
32
+ // timing), and removing late is harmless.
33
+ useEffect(() => {
34
+ const next = (initialActions !== null && initialActions !== void 0 ? initialActions : {});
35
+ const prev = appliedActionsRef.current;
36
+ for (const key in prev) {
37
+ if (!(key in next)) {
38
+ actionBus.removeAction(key);
39
+ }
40
+ }
41
+ for (const key in next) {
42
+ if (key in prev && next[key] !== prev[key]) {
43
+ actionBus.replace(key, next[key]);
44
+ }
45
+ }
46
+ appliedActionsRef.current = Object.assign({}, next);
47
+ });
48
+ useEffect(() => {
22
49
  if (errorListenerRef.current !== errorListener) {
23
50
  if (errorListenerRef.current) {
24
51
  actionBus.removeErrorListener(errorListenerRef.current);
@@ -29,7 +56,7 @@ function useActionBus(initialActions, errorListener) {
29
56
  }
30
57
  }
31
58
  }, [errorListener]);
32
- (0, react_1.useEffect)(() => {
59
+ useEffect(() => {
33
60
  if (boundaryErrorListenerRef.current !== boundaryErrorListener) {
34
61
  if (boundaryErrorListenerRef.current) {
35
62
  actionBus.removeErrorListener(boundaryErrorListenerRef.current);
@@ -1,6 +1,6 @@
1
- import type { ActionResponse, ListenerSignature } from "../action";
2
- import type { BaseActionsMap } from "../actionBus";
3
- import { createActionMap } from "../actionMap";
4
- import type { ErrorListenerSignature, ErrorResponse } from "../lib/types";
1
+ import type { ActionResponse, ListenerSignature } from "../action.js";
2
+ import type { BaseActionsMap } from "../actionBus.js";
3
+ import { createActionMap } from "../actionMap.js";
4
+ import type { ErrorListenerSignature, ErrorResponse } from "../lib/types.js";
5
5
  export type { ActionResponse, BaseActionsMap, ErrorListenerSignature, ErrorResponse, ListenerSignature, };
6
6
  export declare function useActionMap<M extends BaseActionsMap>(actions: M, errorListener?: ErrorListenerSignature<any[]>): ReturnType<typeof createActionMap<M>>;
@@ -1,25 +1,55 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useActionMap = useActionMap;
4
- const react_1 = require("react");
5
- const actionMap_1 = require("../actionMap");
6
- const ErrorBoundary_1 = require("./ErrorBoundary");
7
- function useActionMap(actions, errorListener) {
8
- const boundaryErrorListener = (0, react_1.useContext)(ErrorBoundary_1.ErrorBoundaryContext);
9
- const changeRef = (0, react_1.useRef)(0);
10
- const actionMap = (0, react_1.useMemo)(() => {
1
+ import { useContext, useEffect, useMemo, useRef } from "react";
2
+ import { createActionMap } from "../actionMap.js";
3
+ import { ActionMapSetErrorListeners } from "../lib/actionMapInternal.js";
4
+ import { ErrorBoundaryContext } from "./ErrorBoundary.js";
5
+ export function useActionMap(actions, errorListener) {
6
+ const boundaryErrorListener = useContext(ErrorBoundaryContext);
7
+ const committedActionsRef = useRef(actions);
8
+ const committedErrorListenerRef = useRef(errorListener !== null && errorListener !== void 0 ? errorListener : null);
9
+ const committedBoundaryErrorListenerRef = useRef(boundaryErrorListener !== null && boundaryErrorListener !== void 0 ? boundaryErrorListener : null);
10
+ const actionMap = useMemo(() => {
11
11
  const errorListeners = [
12
12
  ...(errorListener ? [errorListener] : []),
13
13
  ...(boundaryErrorListener ? [boundaryErrorListener] : []),
14
14
  ].filter(l => l !== undefined);
15
- const actionMap = (0, actionMap_1.createActionMap)(actions, errorListeners);
15
+ const actionMap = createActionMap(actions, errorListeners);
16
16
  return actionMap;
17
17
  }, []);
18
- (0, react_1.useEffect)(() => {
19
- if (changeRef.current > 0) {
20
- throw new Error("useActionMap() does not support changing actions or errorListener");
18
+ // The action map TYPE fixes the available keys, so the key set is static:
19
+ // reconcile values only (in-place setAction) and the forwarded error
20
+ // listeners. A runtime key-set change is a type-contract violation and
21
+ // keeps a defensive throw.
22
+ useEffect(() => {
23
+ const next = actions;
24
+ const prev = committedActionsRef.current;
25
+ const prevKeys = Object.keys(prev);
26
+ const nextKeys = Object.keys(next);
27
+ if (prevKeys.length !== nextKeys.length
28
+ || nextKeys.some((key) => !(key in prev))) {
29
+ throw new Error("useActionMap() does not support changing the set of action keys");
21
30
  }
22
- changeRef.current++;
23
- }, [actions, errorListener !== null && errorListener !== void 0 ? errorListener : null, boundaryErrorListener !== null && boundaryErrorListener !== void 0 ? boundaryErrorListener : null]);
31
+ for (const key of nextKeys) {
32
+ if (next[key] !== prev[key]) {
33
+ actionMap[key].setAction(next[key]);
34
+ }
35
+ }
36
+ committedActionsRef.current = next;
37
+ const nextErrorListener = errorListener !== null && errorListener !== void 0 ? errorListener : null;
38
+ const nextBoundaryErrorListener = boundaryErrorListener !== null && boundaryErrorListener !== void 0 ? boundaryErrorListener : null;
39
+ if (committedErrorListenerRef.current !== nextErrorListener
40
+ || committedBoundaryErrorListenerRef.current
41
+ !== nextBoundaryErrorListener) {
42
+ const errorListeners = [
43
+ ...(nextErrorListener ? [nextErrorListener] : []),
44
+ ...(nextBoundaryErrorListener
45
+ ? [nextBoundaryErrorListener]
46
+ : []),
47
+ ];
48
+ actionMap[ActionMapSetErrorListeners](errorListeners);
49
+ committedErrorListenerRef.current = nextErrorListener;
50
+ committedBoundaryErrorListenerRef.current =
51
+ nextBoundaryErrorListener;
52
+ }
53
+ });
24
54
  return actionMap;
25
55
  }
@@ -1,4 +1,4 @@
1
- import { createEvent, type EventOptions } from "../event";
2
- import type { BaseHandler, ErrorListenerSignature, ErrorResponse } from "../lib/types";
1
+ import { createEvent, type EventOptions } from "../event.js";
2
+ import type { BaseHandler, ErrorListenerSignature, ErrorResponse } from "../lib/types.js";
3
3
  export type { BaseHandler, ErrorListenerSignature, ErrorResponse, EventOptions, };
4
4
  export declare function useEvent<Listener extends BaseHandler = BaseHandler, ErrorListener extends ErrorListenerSignature<Parameters<Listener>> = ErrorListenerSignature<Parameters<Listener>>>(eventOptions?: EventOptions<Listener>, listener?: Listener | null, errorListener?: ErrorListener | null): ReturnType<typeof createEvent<Listener>>;
@@ -1,16 +1,15 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useEvent = useEvent;
4
- const react_1 = require("react");
5
- const event_1 = require("../event");
6
- const ErrorBoundary_1 = require("./ErrorBoundary");
7
- function useEvent(eventOptions = {}, listener, errorListener) {
8
- const boundaryErrorListener = (0, react_1.useContext)(ErrorBoundary_1.ErrorBoundaryContext);
9
- const listenerRef = (0, react_1.useRef)(listener);
10
- const errorListenerRef = (0, react_1.useRef)(errorListener);
11
- const boundaryErrorListenerRef = (0, react_1.useRef)(boundaryErrorListener);
12
- const event = (0, react_1.useMemo)(() => {
13
- const event = (0, event_1.createEvent)(eventOptions);
1
+ import { useContext, useEffect, useMemo, useRef } from "react";
2
+ import { createEvent } from "../event.js";
3
+ import { normalizeEventOptions } from "../lib/normalizeEventOptions.js";
4
+ import { ErrorBoundaryContext } from "./ErrorBoundary.js";
5
+ import { areEventOptionsEqual } from "./listenerOptionsEqual.js";
6
+ export function useEvent(eventOptions = {}, listener, errorListener) {
7
+ const boundaryErrorListener = useContext(ErrorBoundaryContext);
8
+ const listenerRef = useRef(listener);
9
+ const errorListenerRef = useRef(errorListener);
10
+ const boundaryErrorListenerRef = useRef(boundaryErrorListener);
11
+ const event = useMemo(() => {
12
+ const event = createEvent(eventOptions);
14
13
  if (listenerRef.current) {
15
14
  event.addListener(listenerRef.current);
16
15
  }
@@ -22,7 +21,21 @@ function useEvent(eventOptions = {}, listener, errorListener) {
22
21
  }
23
22
  return event;
24
23
  }, []);
25
- (0, react_1.useEffect)(() => {
24
+ // Reconcile event options across renders without relying on object
25
+ // identity. Applied in place via setOptions; triggered count is preserved.
26
+ const committedEventOptionsRef = useRef(eventOptions);
27
+ useEffect(() => {
28
+ if (committedEventOptionsRef.current === eventOptions) {
29
+ return;
30
+ }
31
+ if (!areEventOptionsEqual(committedEventOptionsRef.current, eventOptions)) {
32
+ // Normalize so fields removed since the last render reset to their
33
+ // defaults (event.setOptions merges, it does not reset).
34
+ event.setOptions(normalizeEventOptions(eventOptions));
35
+ }
36
+ committedEventOptionsRef.current = eventOptions;
37
+ });
38
+ useEffect(() => {
26
39
  if (listenerRef.current !== listener) {
27
40
  if (listenerRef.current) {
28
41
  event.removeListener(listenerRef.current);
@@ -33,7 +46,7 @@ function useEvent(eventOptions = {}, listener, errorListener) {
33
46
  }
34
47
  }
35
48
  }, [listener]);
36
- (0, react_1.useEffect)(() => {
49
+ useEffect(() => {
37
50
  if (errorListenerRef.current !== errorListener) {
38
51
  if (errorListenerRef.current) {
39
52
  event.removeErrorListener(errorListenerRef.current);
@@ -44,7 +57,7 @@ function useEvent(eventOptions = {}, listener, errorListener) {
44
57
  }
45
58
  }
46
59
  }, [errorListener]);
47
- (0, react_1.useEffect)(() => {
60
+ useEffect(() => {
48
61
  if (boundaryErrorListenerRef.current !== boundaryErrorListener) {
49
62
  if (boundaryErrorListenerRef.current) {
50
63
  event.removeErrorListener(boundaryErrorListenerRef.current);
@@ -55,7 +68,7 @@ function useEvent(eventOptions = {}, listener, errorListener) {
55
68
  }
56
69
  }
57
70
  }, [boundaryErrorListener]);
58
- (0, react_1.useEffect)(() => {
71
+ useEffect(() => {
59
72
  return () => {
60
73
  if (listenerRef.current) {
61
74
  event.removeListener(listenerRef.current);
@@ -1,4 +1,4 @@
1
- import { BaseEventMap, createEventBus, DefaultEventMap, EventBusOptions } from "../eventBus";
2
- import type { BaseHandler, ErrorListenerSignature, ErrorResponse } from "../lib/types";
1
+ import { BaseEventMap, createEventBus, DefaultEventMap, EventBusOptions } from "../eventBus.js";
2
+ import type { BaseHandler, ErrorListenerSignature, ErrorResponse } from "../lib/types.js";
3
3
  export type { BaseEventMap, BaseHandler, ErrorListenerSignature, ErrorResponse, EventBusOptions, };
4
4
  export declare function useEventBus<EventsMap extends BaseEventMap = DefaultEventMap>(eventBusOptions?: EventBusOptions<EventsMap>, allEventsListener?: BaseHandler, errorListener?: ErrorListenerSignature<any[]>): ReturnType<typeof createEventBus<EventsMap>>;