@kuindji/reactive 1.0.24 → 1.2.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.
- package/README.md +160 -14
- package/dist/action.d.ts +31 -10
- package/dist/action.js +156 -23
- package/dist/actionBus.d.ts +13 -4
- package/dist/actionBus.js +201 -5
- package/dist/actionMap.d.ts +26 -19
- package/dist/actionMap.js +10 -4
- package/dist/event.d.ts +37 -3
- package/dist/event.js +345 -78
- package/dist/eventBus.d.ts +7 -3
- package/dist/eventBus.js +194 -34
- package/dist/index.d.ts +7 -7
- package/dist/index.js +7 -7
- package/dist/lib/actionMapInternal.d.ts +8 -0
- package/dist/lib/actionMapInternal.js +8 -0
- package/dist/lib/isPromiseLike.d.ts +1 -0
- package/dist/lib/isPromiseLike.js +5 -0
- package/dist/lib/normalizeEventOptions.d.ts +13 -0
- package/dist/lib/normalizeEventOptions.js +21 -0
- package/dist/lib/types.d.ts +1 -1
- package/dist/react/ErrorBoundary.d.ts +1 -1
- package/dist/react/listenerOptionsEqual.d.ts +27 -0
- package/dist/react/listenerOptionsEqual.js +121 -0
- package/dist/react/useAction.d.ts +3 -3
- package/dist/react/useAction.js +10 -7
- package/dist/react/useActionBus.d.ts +4 -4
- package/dist/react/useActionBus.js +32 -2
- package/dist/react/useActionBusStatus.d.ts +13 -0
- package/dist/react/useActionBusStatus.js +26 -0
- package/dist/react/useActionMap.d.ts +4 -4
- package/dist/react/useActionMap.js +40 -7
- package/dist/react/useAsyncAction.d.ts +20 -0
- package/dist/react/useAsyncAction.js +53 -0
- package/dist/react/useEvent.d.ts +2 -2
- package/dist/react/useEvent.js +18 -2
- package/dist/react/useEventBus.d.ts +2 -2
- package/dist/react/useEventBus.js +14 -10
- package/dist/react/useListenToAction.d.ts +1 -1
- package/dist/react/useListenToAction.js +17 -38
- package/dist/react/useListenToActionBus.d.ts +3 -3
- package/dist/react/useListenToActionBus.js +15 -9
- package/dist/react/useListenToEvent.d.ts +2 -2
- package/dist/react/useListenToEvent.js +8 -6
- package/dist/react/useListenToEventBus.d.ts +3 -3
- package/dist/react/useListenToEventBus.js +9 -7
- package/dist/react/useListenToStoreChanges.d.ts +3 -3
- package/dist/react/useListenToStoreChanges.js +9 -7
- package/dist/react/useReconciledListener.d.ts +33 -0
- package/dist/react/useReconciledListener.js +44 -0
- package/dist/react/useStore.d.ts +2 -2
- package/dist/react/useStore.js +71 -19
- package/dist/react/useStoreSelector.d.ts +35 -0
- package/dist/react/useStoreSelector.js +144 -0
- package/dist/react/useStoreState.d.ts +2 -2
- package/dist/react/useStoreState.js +26 -21
- package/dist/react.d.ts +16 -13
- package/dist/react.js +16 -13
- package/dist/store.d.ts +12 -8
- package/dist/store.js +473 -39
- package/package.json +13 -3
package/README.md
CHANGED
|
@@ -9,8 +9,8 @@ A JavaScript/TypeScript utility library for building reactive applications with
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
11
|
- **Event System**: Event emitter with subscriber/dispatcher and collector modes
|
|
12
|
-
- **Action System**: Async action handling with error management and response
|
|
13
|
-
- **Store System**: Reactive state management with change tracking and
|
|
12
|
+
- **Action System**: Async action handling with error management, response tracking and loading/error/response status
|
|
13
|
+
- **Store System**: Reactive state management with change tracking, validation and computed/derived values
|
|
14
14
|
- **EventBus**: Centralized event management for complex applications
|
|
15
15
|
- **ActionBus & ActionMap**: Organized action management with error handling
|
|
16
16
|
- **React Integration**: Full React hooks support with error boundaries
|
|
@@ -99,9 +99,21 @@ event.addListener(handler, {
|
|
|
99
99
|
tags: string[], // Listener tags for filtering; default undefined
|
|
100
100
|
async: booleantrue, // Call this listener asynchronously; default false
|
|
101
101
|
extraData: object, // Custom data will be passed to filter()
|
|
102
|
+
signal: AbortSignal, // Auto-remove the listener when this signal aborts
|
|
102
103
|
});
|
|
103
104
|
```
|
|
104
105
|
|
|
106
|
+
When a `signal` is provided, the listener is removed automatically once the
|
|
107
|
+
signal aborts (and is not added at all if the signal is already aborted). The
|
|
108
|
+
abort subscription is cleaned up if the listener is removed first, so there is no
|
|
109
|
+
dangling reference into a still-live signal:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const controller = new AbortController();
|
|
113
|
+
event.addListener(handler, { signal: controller.signal });
|
|
114
|
+
controller.abort(); // handler is now removed
|
|
115
|
+
```
|
|
116
|
+
|
|
105
117
|
### Collector
|
|
106
118
|
|
|
107
119
|
Collector allows you to gather data from listeners.
|
|
@@ -159,13 +171,16 @@ const value = event.pipe(1); // value = 4
|
|
|
159
171
|
|
|
160
172
|
- `addListener(listener, options?)` - Add event listener
|
|
161
173
|
- **Aliases**: `on()`, `listen()`, `subscribe()`
|
|
174
|
+
- `once(listener, options?)` - Add a listener that is removed after a single call (sugar for `addListener(listener, { ...options, limit: 1 })`)
|
|
162
175
|
- `removeListener(listener, context?, tag?)` - Remove specific listener
|
|
163
176
|
- **Aliases**: `un()`, `off()`, `remove()`, `unsubscribe()`
|
|
177
|
+
- `updateListenerOptions(listener, context?, nextOptions?)` - Update a registered listener's soft options (`limit`, `start`, `async`, `tags`, `extraData`, `alwaysFirst`/`alwaysLast`, `signal`) **in place**, preserving its `called`/`count` counters. This is a **partial update**: only fields explicitly present in `nextOptions` change; any omitted field keeps its current value. Pass a field explicitly to clear it (e.g. `limit: 0` for unlimited, `signal: null` to drop abort wiring). Matches the listener by `listener` + `context`. Returns `true` if a listener was found. `context` is an identity field and is not updated here (resubscribe to change it); `first` is insertion-time only and ignored. Lowering `limit` to at/below the current `called` removes the listener immediately.
|
|
164
178
|
- `hasListener(listener?, context?, tag?)` - Check if listener exists
|
|
165
179
|
- **Aliases**: `has()`
|
|
166
180
|
- `removeAllListeners(tag?)` - Remove all listeners (optionally by tag)
|
|
167
181
|
- `trigger(...args)` - Trigger the event
|
|
168
182
|
- **Aliases**: `emit()`, `dispatch()`
|
|
183
|
+
- `setOptions(options)` - Update event options in place. Accepts any `EventOptions` field (`async`, `limit`, `autoTrigger`, `filter`, `filterContext`, `maxListeners`). Does not reset the internal `triggered` count.
|
|
169
184
|
|
|
170
185
|
#### Collector Methods
|
|
171
186
|
|
|
@@ -195,8 +210,17 @@ const value = event.pipe(1); // value = 4
|
|
|
195
210
|
- `suspend(withQueue?: boolean)` - Suspend event triggering; When `withQueue=true`, all trigger calls will be queued and replayed after resume()
|
|
196
211
|
- `resume()` - Resume event triggering
|
|
197
212
|
- `reset()` - Reset event state
|
|
213
|
+
- `destroy()` - Tear down the event: remove all listeners (unwinding any `AbortSignal` subscriptions) and mark it dead. After `destroy()`, `trigger()` and `addListener()` throw rather than silently no-op.
|
|
214
|
+
- `isDestroyed()` - Returns `true` once `destroy()` has been called
|
|
198
215
|
- `withTags(tags: string[], callback: () => CallbackResponse) => CallbackResponse` - Execute callback with specific tags
|
|
199
216
|
|
|
217
|
+
#### Introspection
|
|
218
|
+
|
|
219
|
+
- `listenerCount(tag?)` - Number of registered listeners, optionally filtered by tag
|
|
220
|
+
- `triggeredCount()` - How many times the event has been triggered
|
|
221
|
+
- `lastTriggerArgs()` - The most recent trigger arguments (a copy), or `null` if never triggered
|
|
222
|
+
- `getListeners()` - Read-only projection of registered listeners (`handler`, `context`, `tags`, `limit`, `start`, `called`, `count`, `async`, ordering flags, `extraData`). Mutating the returned objects does not affect the event.
|
|
223
|
+
|
|
200
224
|
## EventBus
|
|
201
225
|
|
|
202
226
|
### Description
|
|
@@ -481,10 +505,12 @@ customSource.trigger("appStart");
|
|
|
481
505
|
- `once(name, handler, options?)` - Add one-time listener
|
|
482
506
|
- `removeListener(name, handler, context?, tag?)` - Remove listener
|
|
483
507
|
- **Aliases**: `un()`, `off()`, `remove()`, `unsubscribe()`
|
|
508
|
+
- `updateListenerOptions(name, handler, context?, nextOptions?)` - Update a registered listener's soft options in place (see Event's `updateListenerOptions`). Returns `false` if the event does not exist.
|
|
484
509
|
- `trigger(name, ...args)` - Trigger specific event
|
|
485
510
|
- **Aliases**: `emit()`, `dispatch()`
|
|
486
511
|
- `get(name)` - Get event instance by name
|
|
487
512
|
- `add(name, options?)` - Add new event to bus
|
|
513
|
+
- `setOptions(options?)` - Update bus options. Present per-event entries in `eventOptions` are applied to already-created events via `event.setOptions`, and future events use the latest stored options. A removed event-name entry leaves the existing event unchanged.
|
|
488
514
|
|
|
489
515
|
#### Collector Methods
|
|
490
516
|
|
|
@@ -518,7 +544,9 @@ customSource.trigger("appStart");
|
|
|
518
544
|
- `removeEventSource(source)` - Remove event source
|
|
519
545
|
- `suspendAll(withQueue?)` - Suspend all events
|
|
520
546
|
- `resumeAll()` - Resume all events
|
|
521
|
-
- `reset()` - Reset all events
|
|
547
|
+
- `reset()` - Reset all events: unrelay all relays and remove all event sources (detaching their external listeners), then clear every owned event and interception/tag state. The bus stays usable afterwards.
|
|
548
|
+
- `destroy()` - Tear down the bus: unrelay all relays, remove all event sources (detaching their external listeners), destroy every owned event, and mark the bus dead. After `destroy()`, `trigger()`/`on()` throw.
|
|
549
|
+
- `isDestroyed()` - Returns `true` once `destroy()` has been called
|
|
522
550
|
- `withTags(tags, callback)` - Execute callback with specific tags
|
|
523
551
|
|
|
524
552
|
## Action
|
|
@@ -560,27 +588,57 @@ const result = await fetchUserAction.invoke("user123");
|
|
|
560
588
|
#### Core Methods
|
|
561
589
|
|
|
562
590
|
- `invoke(...args)` - Execute the action
|
|
591
|
+
- `setAction(fn)` - Replace the action function in place. Subsequent `invoke()` calls use the new function; all response, before-action and error listeners are preserved (they live in separate events). The replacement must keep a compatible signature.
|
|
563
592
|
- `addListener(handler, options?)` - Add response listener
|
|
564
593
|
- **Aliases**: `on()`, `listen()`, `subscribe()`
|
|
565
594
|
- `removeListener(handler, context?, tag?)` - Remove listener
|
|
566
595
|
- **Aliases**: `un()`, `off()`, `remove()`, `unsubscribe()`
|
|
567
|
-
- `
|
|
568
|
-
- **Aliases**: `has()`
|
|
596
|
+
- `updateListenerOptions(handler, context?, nextOptions?)` - Update a response listener's soft options in place (see Event's `updateListenerOptions`)
|
|
569
597
|
- `removeAllListeners(tag?)` - Remove all listeners
|
|
598
|
+
- `destroy()` - Tear down the action: destroy its response, before-action, error and status events and mark it dead. After `destroy()`, `invoke()`/`addListener()` throw.
|
|
599
|
+
- `isDestroyed()` - Returns `true` once `destroy()` has been called
|
|
570
600
|
|
|
571
601
|
#### Error Handling
|
|
572
602
|
|
|
573
603
|
- `addErrorListener(handler, context?)` - Add error listener
|
|
574
604
|
- `removeErrorListener(handler, context?)` - Remove error listener
|
|
575
|
-
- `hasErrorListeners()` - Check if error listeners exist
|
|
576
605
|
- `removeAllErrorListeners(tag?)` - Remove all error listeners
|
|
577
606
|
|
|
607
|
+
#### Status (loading / error / response)
|
|
608
|
+
|
|
609
|
+
An action tracks the status of its `invoke` lifecycle so UI can drive
|
|
610
|
+
`loading`/`disabled` without a hand-rolled `useState(false)`. `pending` is true
|
|
611
|
+
while one or more invocations are in flight; `response`/`error` hold the last
|
|
612
|
+
settled outcome (a before-action veto settles to neither). This is **not** a
|
|
613
|
+
cache — `response` is just the last value.
|
|
614
|
+
|
|
615
|
+
- `getStatus()` - Returns `{ pending: boolean, error: Error | null, response: T | null }`. The reference is stable while unchanged (safe for `useSyncExternalStore`).
|
|
616
|
+
- `onStatusChange(handler)` - Subscribe to status changes
|
|
617
|
+
- `removeStatusListener(handler)` - Remove a status listener
|
|
618
|
+
|
|
619
|
+
```typescript
|
|
620
|
+
const saveAction = createAction(async (data: FormData) => save(data));
|
|
621
|
+
|
|
622
|
+
saveAction.onStatusChange(({ pending, error }) => {
|
|
623
|
+
button.disabled = pending;
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
await saveAction.invoke(form); // pending -> true, then false on settle
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
In React, prefer the `useAsyncAction` / `useActionBusStatus` hooks (see React Hooks).
|
|
630
|
+
|
|
578
631
|
#### Utility Methods
|
|
579
632
|
|
|
580
633
|
- `promise(options?)` - Get promise for next invocation
|
|
581
|
-
- `
|
|
582
|
-
- `
|
|
583
|
-
|
|
634
|
+
- `beforeActionPromise(options?)` - Get promise for the next before-action call
|
|
635
|
+
- `errorPromise(options?)` - Get promise for the next action error
|
|
636
|
+
|
|
637
|
+
#### Before Action Methods
|
|
638
|
+
|
|
639
|
+
- `addBeforeActionListener(handler, options?)` - Add listener that runs before invocation
|
|
640
|
+
- `removeBeforeActionListener(handler, context?, tag?)` - Remove before-action listener
|
|
641
|
+
- `removeAllBeforeActionListeners(tag?)` - Remove all before-action listeners
|
|
584
642
|
|
|
585
643
|
## ActionMap
|
|
586
644
|
|
|
@@ -683,7 +741,10 @@ const user = await actionBus.invoke("fetchUser", "user123");
|
|
|
683
741
|
|
|
684
742
|
#### Core Methods
|
|
685
743
|
|
|
686
|
-
- `add(name, action)` - Add action to bus
|
|
744
|
+
- `add(name, action)` - Add action to bus (no-op if it already exists)
|
|
745
|
+
- `replace(name, action)` - Replace an existing action's function in place via `setAction` (preserving its listeners and the bus error-forwarding listener); adds it if the name is new
|
|
746
|
+
- `removeAction(name)` - Remove an action from the bus (named `removeAction` because `remove` is an alias for `removeListener`). Afterwards `invoke`/`on`/`un` for that name throw `Action <name> not found`.
|
|
747
|
+
- `has(name)` - Check if action exists
|
|
687
748
|
- `get(name)` - Get action by name
|
|
688
749
|
- `invoke(name, ...args)` - Invoke action by name
|
|
689
750
|
- `addListener(name, handler, options?)` - Add listener to action
|
|
@@ -691,8 +752,18 @@ const user = await actionBus.invoke("fetchUser", "user123");
|
|
|
691
752
|
- `once(name, handler, options?)` - Add one-time listener
|
|
692
753
|
- `removeListener(name, handler, context?, tag?)` - Remove listener
|
|
693
754
|
- **Aliases**: `un()`, `off()`, `remove()`, `unsubscribe()`
|
|
694
|
-
- `
|
|
695
|
-
- `
|
|
755
|
+
- `updateListenerOptions(name, handler, context?, nextOptions?)` - Update a response listener's soft options in place (see Event's `updateListenerOptions`)
|
|
756
|
+
- `destroy()` - Tear down the bus: destroy every owned action and the error event, then drop them all. After `destroy()`, `invoke()`/`on()` throw.
|
|
757
|
+
- `isDestroyed()` - Returns `true` once `destroy()` has been called
|
|
758
|
+
|
|
759
|
+
#### Status (loading / error / response)
|
|
760
|
+
|
|
761
|
+
Delegates to the underlying action's status (see Action → Status). This is the
|
|
762
|
+
primary path for apps that route mutations through one shared ActionBus.
|
|
763
|
+
|
|
764
|
+
- `getStatus(name)` - Status for a named action; an unregistered name reports an idle status
|
|
765
|
+
- `onStatusChange(name, handler)` - Subscribe to a named action's status. Subscribing before the action is registered is retained and attached automatically once it is added (and re-attached if the action is later removed and re-added)
|
|
766
|
+
- `removeStatusListener(name, handler)` - Remove a status listener (also clears a subscription retained before registration)
|
|
696
767
|
|
|
697
768
|
#### Error Handling
|
|
698
769
|
|
|
@@ -751,13 +822,18 @@ const userData = userStore.get([ "name", "email" ]); // { name: string, email: s
|
|
|
751
822
|
- `asyncSet(data)` - Async set multiple properties
|
|
752
823
|
- `get(key)` - Get single property
|
|
753
824
|
- `get(keys)` - Get multiple properties
|
|
825
|
+
- `computed(key, deps, fn)` - Register a derived value (see Computed values)
|
|
754
826
|
- `isEmpty()` - Check if store is empty
|
|
755
827
|
- `getData()` - Get all store data
|
|
756
|
-
- `reset()` -
|
|
828
|
+
- `reset()` - Clear store data. Computed keys are re-seeded from the cleared dependencies (so they stay consistent with `fn(deps)` rather than going stale) and remain live.
|
|
829
|
+
- `destroy()` - Tear down the store: destroy the underlying change/pipe/control buses and drop all data. After `destroy()`, `set()`/`get()` throw.
|
|
830
|
+
- `isDestroyed()` - Returns `true` once `destroy()` has been called
|
|
757
831
|
|
|
758
832
|
#### Event Methods
|
|
759
833
|
|
|
760
|
-
- `onChange(key, handler)` - Listen to property changes
|
|
834
|
+
- `onChange(key, handler, options?)` - Listen to property changes
|
|
835
|
+
- `removeOnChange(key, handler, context?, tag?)` - Remove a change listener
|
|
836
|
+
- `updateOnChangeOptions(key, handler, context?, nextOptions?)` - Update a change listener's soft options in place (see Event's `updateListenerOptions`)
|
|
761
837
|
- `pipe(key, handler)` - Add data transformation pipeline
|
|
762
838
|
- `control(event, handler)` - Control store events
|
|
763
839
|
|
|
@@ -772,6 +848,40 @@ const userData = userStore.get([ "name", "email" ]); // { name: string, email: s
|
|
|
772
848
|
|
|
773
849
|
- `batch(fn)` - Batch multiple changes
|
|
774
850
|
|
|
851
|
+
#### Computed values
|
|
852
|
+
|
|
853
|
+
Declare a derived key in the store type, then attach its derivation with
|
|
854
|
+
`computed(key, deps, fn)`. It recomputes automatically when any dependency
|
|
855
|
+
changes and notifies like any other key — `get`, `getData`, `onChange`,
|
|
856
|
+
`useStoreState` and `useStoreSelector` all see it transparently. Computed keys
|
|
857
|
+
are read-only: calling `set` on one throws. Computed-of-computed chains are
|
|
858
|
+
supported, and a cyclic computed throws rather than looping.
|
|
859
|
+
|
|
860
|
+
```typescript
|
|
861
|
+
type UserStore = {
|
|
862
|
+
first: string;
|
|
863
|
+
last: string;
|
|
864
|
+
fullName: string; // declared in the type, registered as computed
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
const store = createStore<UserStore>({ first: "Jane", last: "Doe" });
|
|
868
|
+
|
|
869
|
+
store.computed("fullName", [ "first", "last" ], (first, last) => `${first} ${last}`);
|
|
870
|
+
|
|
871
|
+
store.get("fullName"); // "Jane Doe"
|
|
872
|
+
store.onChange("fullName", (v) => console.log(v));
|
|
873
|
+
store.set("first", "John"); // fullName recomputes -> "John Doe"
|
|
874
|
+
store.set("fullName", "x"); // throws: computed is read-only
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
> **Note:** recompute is registration-order, not topologically sorted, so a
|
|
878
|
+
> chained or diamond-shaped computed may recompute internally more than once per
|
|
879
|
+
> change. This is invisible to consumers: a single `set(...)`/`set({...})`/`batch`
|
|
880
|
+
> coalesces the `onChange` stream, so each computed fires `onChange` once with
|
|
881
|
+
> its settled value and the correct previous value. The final value is always
|
|
882
|
+
> correct. Registering base computeds before dependents reduces redundant
|
|
883
|
+
> internal recomputes.
|
|
884
|
+
|
|
775
885
|
## React Hooks
|
|
776
886
|
|
|
777
887
|
### Description
|
|
@@ -953,6 +1063,42 @@ useListenToStoreChanges(
|
|
|
953
1063
|
)
|
|
954
1064
|
```
|
|
955
1065
|
|
|
1066
|
+
Select a derived slice with equality (bails out of re-renders while the result
|
|
1067
|
+
is unchanged). Two forms — a selector over the whole state, or a deps-keyed form
|
|
1068
|
+
that recomputes only when the listed keys change:
|
|
1069
|
+
|
|
1070
|
+
```typescript
|
|
1071
|
+
// selector form (default equality is Object.is)
|
|
1072
|
+
const label = useStoreSelector(store, (s) => `${s.first} ${s.last}`, shallowEqual?);
|
|
1073
|
+
|
|
1074
|
+
// deps-keyed form
|
|
1075
|
+
const anyLoading = useStoreSelector(store, [ "a", "b", "c" ], (a, b, c) => a || b || c);
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
Drive `loading`/`disabled` from an action's status. `useActionBusStatus` is the
|
|
1079
|
+
primary path for apps built around one shared ActionBus; `useAsyncAction` wraps a
|
|
1080
|
+
standalone function:
|
|
1081
|
+
|
|
1082
|
+
```typescript
|
|
1083
|
+
// shared ActionBus
|
|
1084
|
+
const { loading, error, response } = useActionBusStatus(appActions, "user/login");
|
|
1085
|
+
|
|
1086
|
+
// standalone action
|
|
1087
|
+
const [ submit, { loading, error } ] = useAsyncAction(saveProfileFn);
|
|
1088
|
+
// <Button loading={loading} disabled={loading} onClick={() => submit(form)} />
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
### Reconciliation across renders
|
|
1092
|
+
|
|
1093
|
+
Hook inputs are reconciled on every render using semantic comparison, so you
|
|
1094
|
+
can safely pass inline objects (e.g. `{ tags: [tag] }`) without object identity
|
|
1095
|
+
forcing a resubscribe:
|
|
1096
|
+
|
|
1097
|
+
- **Listener options** (`useListenToEvent`/`useListenToEventBus`/`useListenToActionBus`/`useListenToStoreChanges`) are compared field by field (`tags` is an order-insensitive set). A semantically equal object is a no-op. A changed soft option updates the live listener **in place**, preserving its `called`/`count` counters. Changing `context` (an identity field) resubscribes using the old context.
|
|
1098
|
+
- **`useEvent` event options** and **`useEventBus` options** are reconciled via `setOptions` instead of being ignored (and `useEventBus` no longer throws when options change).
|
|
1099
|
+
- **`useAction`/`useActionBus`/`useActionMap` action functions** are replaced in place via `setAction` (compared by reference), preserving all listeners; `useActionBus` also adds/removes actions as its map changes. The `useActionMap` key set is fixed by its type contract — a runtime key-set change throws.
|
|
1100
|
+
- **`useStore` config** (`onChange`/`pipes`/`control`) is reconciled by category + key (functions compared by reference); only handlers added by the hook are touched. **`initialData` is seed-only** — it initializes the store once and later changes are intentionally ignored (live data is owned by `set`/`useStoreState`).
|
|
1101
|
+
|
|
956
1102
|
## ErrorBoundary
|
|
957
1103
|
|
|
958
1104
|
### Description
|
package/dist/action.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ApiType, BaseHandler, ErrorListenerSignature, ErrorResponse } from "./lib/types";
|
|
1
|
+
import type { ApiType, BaseHandler, ErrorListenerSignature, ErrorResponse } from "./lib/types.js";
|
|
2
2
|
export type ActionResponse<Response = any, Args extends unknown[] = unknown[]> = {
|
|
3
3
|
response: Response;
|
|
4
4
|
error: null;
|
|
@@ -9,6 +9,18 @@ export type ActionResponse<Response = any, Args extends unknown[] = unknown[]> =
|
|
|
9
9
|
args: Args;
|
|
10
10
|
};
|
|
11
11
|
export type ListenerSignature<ActionSignature extends BaseHandler> = (arg: ActionResponse<Awaited<ReturnType<ActionSignature>>, Parameters<ActionSignature>>) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Status of an action's `invoke` lifecycle, suitable for driving
|
|
14
|
+
* `loading`/`disabled` UI. `pending` is true while one or more invocations are
|
|
15
|
+
* in flight; `response`/`error` hold the last settled outcome (a before-veto
|
|
16
|
+
* settles to neither). This is not a cache — `response` is just the last value.
|
|
17
|
+
*/
|
|
18
|
+
export type ActionStatus<Response = any> = {
|
|
19
|
+
pending: boolean;
|
|
20
|
+
error: Error | null;
|
|
21
|
+
response: Response | null;
|
|
22
|
+
};
|
|
23
|
+
export type StatusListenerSignature<ActionSignature extends BaseHandler> = (status: ActionStatus<Awaited<ReturnType<ActionSignature>>>) => void;
|
|
12
24
|
export type BeforeActionSignature<ActionSignature extends BaseHandler> = (...args: Parameters<ActionSignature>) => false | void | Promise<false | void>;
|
|
13
25
|
export type ActionDefinitionHelper<A extends BaseHandler> = {
|
|
14
26
|
actionSignature: A;
|
|
@@ -21,16 +33,24 @@ export type ActionDefinitionHelper<A extends BaseHandler> = {
|
|
|
21
33
|
beforeActionSignature: BeforeActionSignature<A>;
|
|
22
34
|
errorListenerArgument: ErrorResponse<Parameters<A>>;
|
|
23
35
|
errorListenerSignature: ErrorListenerSignature<Parameters<A>>;
|
|
36
|
+
statusType: ActionStatus<Awaited<ReturnType<A>>>;
|
|
37
|
+
statusListenerSignature: StatusListenerSignature<A>;
|
|
24
38
|
};
|
|
25
39
|
export declare function createAction<A extends BaseHandler>(action: A): ApiType<ActionDefinitionHelper<A>, {
|
|
26
40
|
readonly invoke: (...args: Parameters<A>) => Promise<ActionResponse<Awaited<ReturnType<A>>, Parameters<A>>>;
|
|
27
|
-
readonly
|
|
41
|
+
readonly setAction: (nextAction: A) => void;
|
|
42
|
+
readonly destroy: () => void;
|
|
43
|
+
readonly isDestroyed: () => boolean;
|
|
44
|
+
readonly getStatus: () => ActionStatus<Awaited<ReturnType<A>>>;
|
|
45
|
+
readonly onStatusChange: (handler: StatusListenerSignature<A>, listenerOptions?: import("./event.js").ListenerOptions) => void;
|
|
46
|
+
readonly removeStatusListener: (handler: StatusListenerSignature<A>, context?: object | null, tag?: string | null) => boolean;
|
|
47
|
+
readonly addListener: (handler: ListenerSignature<A>, listenerOptions?: import("./event.js").ListenerOptions) => void;
|
|
28
48
|
/** @alias addListener */
|
|
29
|
-
readonly on: (handler: ListenerSignature<A>, listenerOptions?: import("./event").ListenerOptions) => void;
|
|
49
|
+
readonly on: (handler: ListenerSignature<A>, listenerOptions?: import("./event.js").ListenerOptions) => void;
|
|
30
50
|
/** @alias addListener */
|
|
31
|
-
readonly subscribe: (handler: ListenerSignature<A>, listenerOptions?: import("./event").ListenerOptions) => void;
|
|
51
|
+
readonly subscribe: (handler: ListenerSignature<A>, listenerOptions?: import("./event.js").ListenerOptions) => void;
|
|
32
52
|
/** @alias addListener */
|
|
33
|
-
readonly listen: (handler: ListenerSignature<A>, listenerOptions?: import("./event").ListenerOptions) => void;
|
|
53
|
+
readonly listen: (handler: ListenerSignature<A>, listenerOptions?: import("./event.js").ListenerOptions) => void;
|
|
34
54
|
readonly removeAllListeners: (tag?: string) => void;
|
|
35
55
|
readonly removeListener: (handler: ListenerSignature<A>, context?: object | null, tag?: string | null) => boolean;
|
|
36
56
|
/** @alias removeListener */
|
|
@@ -41,15 +61,16 @@ export declare function createAction<A extends BaseHandler>(action: A): ApiType<
|
|
|
41
61
|
readonly remove: (handler: ListenerSignature<A>, context?: object | null, tag?: string | null) => boolean;
|
|
42
62
|
/** @alias removeListener */
|
|
43
63
|
readonly unsubscribe: (handler: ListenerSignature<A>, context?: object | null, tag?: string | null) => boolean;
|
|
44
|
-
readonly
|
|
45
|
-
readonly
|
|
64
|
+
readonly updateListenerOptions: (handler: ListenerSignature<A>, context?: object | null, nextOptions?: import("./event.js").ListenerOptions) => boolean;
|
|
65
|
+
readonly promise: (options?: import("./event.js").ListenerOptions) => Promise<[arg: ActionResponse<Awaited<ReturnType<A>>, Parameters<A>>]>;
|
|
66
|
+
readonly addErrorListener: (handler: ErrorListenerSignature<Parameters<A>>, listenerOptions?: import("./event.js").ListenerOptions) => void;
|
|
46
67
|
readonly removeAllErrorListeners: (tag?: string) => void;
|
|
47
68
|
readonly removeErrorListener: (handler: ErrorListenerSignature<Parameters<A>>, context?: object | null, tag?: string | null) => boolean;
|
|
48
|
-
readonly errorPromise: (options?: import("./event").ListenerOptions) => Promise<[errorResponse: ErrorResponse<Parameters<A>>]>;
|
|
49
|
-
readonly addBeforeActionListener: (handler: BeforeActionSignature<A>, listenerOptions?: import("./event").ListenerOptions) => void;
|
|
69
|
+
readonly errorPromise: (options?: import("./event.js").ListenerOptions) => Promise<[errorResponse: ErrorResponse<Parameters<A>>]>;
|
|
70
|
+
readonly addBeforeActionListener: (handler: BeforeActionSignature<A>, listenerOptions?: import("./event.js").ListenerOptions) => void;
|
|
50
71
|
readonly removeAllBeforeActionListeners: (tag?: string) => void;
|
|
51
72
|
readonly removeBeforeActionListener: (handler: BeforeActionSignature<A>, context?: object | null, tag?: string | null) => boolean;
|
|
52
|
-
readonly beforeActionPromise: (options?: import("./event").ListenerOptions) => Promise<Parameters<A>>;
|
|
73
|
+
readonly beforeActionPromise: (options?: import("./event.js").ListenerOptions) => Promise<Parameters<A>>;
|
|
53
74
|
}>;
|
|
54
75
|
export type BaseActionDefinition = ActionDefinitionHelper<(...args: [any]) => any>;
|
|
55
76
|
export type BaseAction = ReturnType<typeof createAction<(...args: [any]) => any>>;
|
package/dist/action.js
CHANGED
|
@@ -7,42 +7,150 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { createEvent } from "./event";
|
|
10
|
+
import { createEvent } from "./event.js";
|
|
11
|
+
import isPromiseLike from "./lib/isPromiseLike.js";
|
|
11
12
|
export function createAction(action) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
// The action function is held in a mutable variable so it can be swapped in
|
|
14
|
+
// place via setAction without disturbing any listeners (response, before
|
|
15
|
+
// and error listeners live in separate events independent of the function).
|
|
16
|
+
let actionFn = action;
|
|
17
|
+
const { trigger, addListener, removeAllListeners, removeListener, updateListenerOptions, promise, destroy: destroyResponseEvent, } = createEvent();
|
|
18
|
+
const { all: triggerBeforeAction, addListener: addBeforeActionListener, removeAllListeners: removeAllBeforeActionListeners, removeListener: removeBeforeActionListener, promise: beforeActionPromise, destroy: destroyBeforeEvent, } = createEvent();
|
|
19
|
+
const { trigger: triggerError, addListener: addErrorListener, removeAllListeners: removeAllErrorListeners, removeListener: removeErrorListener, promise: errorPromise, hasListener: hasErrorListeners, destroy: destroyErrorEvent, } = createEvent();
|
|
20
|
+
// Status is a side channel over the invoke lifecycle: a dedicated event so
|
|
21
|
+
// a React hook can subscribe through useSyncExternalStore. The status
|
|
22
|
+
// object reference is kept stable and only rebuilt when a field actually
|
|
23
|
+
// changes, which is required for useSyncExternalStore to bail out of
|
|
24
|
+
// redundant renders.
|
|
25
|
+
const { trigger: triggerStatus, addListener: addStatusListener, removeListener: removeStatusListener, destroy: destroyStatusEvent, } = createEvent();
|
|
26
|
+
let destroyed = false;
|
|
27
|
+
let inFlight = 0;
|
|
28
|
+
let lastResponse = null;
|
|
29
|
+
let lastError = null;
|
|
30
|
+
// Frozen so getStatus() can hand out the live reference (required for
|
|
31
|
+
// useSyncExternalStore to bail out of redundant renders) without a consumer
|
|
32
|
+
// being able to mutate it — a tampered `pending` would make updateStatus()
|
|
33
|
+
// believe nothing changed and suppress the next notification.
|
|
34
|
+
let currentStatus = Object.freeze({
|
|
35
|
+
pending: false,
|
|
36
|
+
error: null,
|
|
37
|
+
response: null,
|
|
38
|
+
});
|
|
39
|
+
const updateStatus = () => {
|
|
40
|
+
const pending = inFlight > 0;
|
|
41
|
+
if (currentStatus.pending === pending
|
|
42
|
+
&& currentStatus.error === lastError
|
|
43
|
+
&& currentStatus.response === lastResponse) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
currentStatus = Object.freeze({
|
|
47
|
+
pending,
|
|
48
|
+
error: lastError,
|
|
49
|
+
response: lastResponse,
|
|
50
|
+
});
|
|
51
|
+
// The status event may have been torn down while an invocation was in
|
|
52
|
+
// flight. Still reconcile `currentStatus` above (so getStatus() does not
|
|
53
|
+
// strand `pending: true` after a mid-flight destroy), but skip emitting
|
|
54
|
+
// onto the dead event, which would throw and mask the real outcome.
|
|
55
|
+
if (destroyed) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
// Status is a side channel. A throwing status listener must not corrupt
|
|
59
|
+
// the invoke lifecycle: if it propagated here it would, depending on the
|
|
60
|
+
// call site, abort execution or skip the inFlight decrement and strand
|
|
61
|
+
// `pending: true`. Isolate it and surface it via the error event.
|
|
62
|
+
try {
|
|
63
|
+
triggerStatus(currentStatus);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
// Surface the failure via the error event, but a throwing error
|
|
67
|
+
// listener must not re-escape either: this runs before invoke()
|
|
68
|
+
// enters its try/finally, so any escape would strand pending:true
|
|
69
|
+
// and skip execution entirely.
|
|
70
|
+
try {
|
|
71
|
+
triggerError({
|
|
72
|
+
error: error instanceof Error
|
|
73
|
+
? error
|
|
74
|
+
: new Error(String(error)),
|
|
75
|
+
args: [],
|
|
76
|
+
type: "action-status",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch (_a) {
|
|
80
|
+
// Nothing left to route to; swallow to protect the lifecycle.
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const getStatus = () => currentStatus;
|
|
15
85
|
const invoke = (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
86
|
+
if (destroyed) {
|
|
87
|
+
throw new Error("Action is destroyed");
|
|
88
|
+
}
|
|
89
|
+
// Snapshot whether error listeners existed at invocation start. The
|
|
90
|
+
// catch below ORs this with a live re-check: the snapshot guards against
|
|
91
|
+
// destroy() tearing listeners down mid-flight (which must not flip a
|
|
92
|
+
// handled failure into a rejection), while the live check still routes
|
|
93
|
+
// the error to a listener registered after invoke() began.
|
|
94
|
+
const handlesErrors = hasErrorListeners();
|
|
95
|
+
inFlight++;
|
|
96
|
+
updateStatus();
|
|
16
97
|
try {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
98
|
+
const beforeResponse = triggerBeforeAction(...args);
|
|
99
|
+
const beforeResults = isPromiseLike(beforeResponse)
|
|
100
|
+
? yield Promise.resolve(beforeResponse)
|
|
101
|
+
: beforeResponse;
|
|
102
|
+
for (const before of beforeResults) {
|
|
22
103
|
if (before === false) {
|
|
104
|
+
// A before-veto is a no-op for the caller, not a settlement:
|
|
105
|
+
// leave lastResponse/lastError untouched so a vetoed
|
|
106
|
+
// invocation cannot wipe the status of a concurrent (or
|
|
107
|
+
// prior) real invocation. A fresh action still reads idle
|
|
108
|
+
// because both start null.
|
|
23
109
|
const response = {
|
|
24
110
|
response: null,
|
|
25
111
|
error: "Action cancelled",
|
|
26
112
|
args: args,
|
|
27
113
|
};
|
|
28
|
-
|
|
114
|
+
// Skip emitting if destroyed mid-flight: the caller still
|
|
115
|
+
// gets its settled response, but the torn-down event is not
|
|
116
|
+
// triggered (which would throw "Event is destroyed").
|
|
117
|
+
if (!destroyed) {
|
|
118
|
+
trigger(response);
|
|
119
|
+
}
|
|
29
120
|
return response;
|
|
30
121
|
}
|
|
31
122
|
}
|
|
32
|
-
let result =
|
|
33
|
-
if (result
|
|
34
|
-
result = yield result;
|
|
123
|
+
let result = actionFn(...args);
|
|
124
|
+
if (isPromiseLike(result)) {
|
|
125
|
+
result = yield Promise.resolve(result);
|
|
35
126
|
}
|
|
127
|
+
lastResponse = result;
|
|
128
|
+
lastError = null;
|
|
36
129
|
const response = {
|
|
37
130
|
response: result,
|
|
38
131
|
error: null,
|
|
39
132
|
args: args,
|
|
40
133
|
};
|
|
41
|
-
|
|
134
|
+
// A successful invocation must still resolve with its result even if
|
|
135
|
+
// the action was destroyed while awaiting; only skip the emit.
|
|
136
|
+
if (!destroyed) {
|
|
137
|
+
trigger(response);
|
|
138
|
+
}
|
|
42
139
|
return response;
|
|
43
140
|
}
|
|
44
141
|
catch (error) {
|
|
45
|
-
|
|
142
|
+
// Record the failure before the re-throw branch so status is
|
|
143
|
+
// correct even when invoke re-throws (no error listener).
|
|
144
|
+
lastError = error instanceof Error
|
|
145
|
+
? error
|
|
146
|
+
: new Error(error);
|
|
147
|
+
lastResponse = null;
|
|
148
|
+
// Handle the error if listeners existed at invoke start OR were
|
|
149
|
+
// registered while the invocation was in flight. The start-of-invoke
|
|
150
|
+
// snapshot is retained (rather than relying solely on the live check)
|
|
151
|
+
// so that destroy() tearing the listeners down mid-flight cannot flip
|
|
152
|
+
// a previously-handled failure into a rejection.
|
|
153
|
+
if (!handlesErrors && !hasErrorListeners()) {
|
|
46
154
|
throw error;
|
|
47
155
|
}
|
|
48
156
|
const response = {
|
|
@@ -50,19 +158,43 @@ export function createAction(action) {
|
|
|
50
158
|
error: error instanceof Error ? error.message : error,
|
|
51
159
|
args: args,
|
|
52
160
|
};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
161
|
+
if (!destroyed) {
|
|
162
|
+
trigger(response);
|
|
163
|
+
triggerError({
|
|
164
|
+
error: lastError,
|
|
165
|
+
args: args,
|
|
166
|
+
type: "action",
|
|
167
|
+
});
|
|
168
|
+
}
|
|
61
169
|
return response;
|
|
62
170
|
}
|
|
171
|
+
finally {
|
|
172
|
+
inFlight--;
|
|
173
|
+
updateStatus();
|
|
174
|
+
}
|
|
63
175
|
});
|
|
176
|
+
const setAction = (nextAction) => {
|
|
177
|
+
actionFn = nextAction;
|
|
178
|
+
};
|
|
179
|
+
// One-call teardown: destroy the underlying response/before/error/status
|
|
180
|
+
// events and mark the action dead. Post-destroy invoke/addListener throw
|
|
181
|
+
// rather than silently no-op.
|
|
182
|
+
const destroy = () => {
|
|
183
|
+
destroyResponseEvent();
|
|
184
|
+
destroyBeforeEvent();
|
|
185
|
+
destroyErrorEvent();
|
|
186
|
+
destroyStatusEvent();
|
|
187
|
+
destroyed = true;
|
|
188
|
+
};
|
|
189
|
+
const isDestroyed = () => destroyed;
|
|
64
190
|
const api = {
|
|
65
191
|
invoke,
|
|
192
|
+
setAction,
|
|
193
|
+
destroy,
|
|
194
|
+
isDestroyed,
|
|
195
|
+
getStatus,
|
|
196
|
+
onStatusChange: addStatusListener,
|
|
197
|
+
removeStatusListener,
|
|
66
198
|
addListener,
|
|
67
199
|
/** @alias addListener */
|
|
68
200
|
on: addListener,
|
|
@@ -80,6 +212,7 @@ export function createAction(action) {
|
|
|
80
212
|
remove: removeListener,
|
|
81
213
|
/** @alias removeListener */
|
|
82
214
|
unsubscribe: removeListener,
|
|
215
|
+
updateListenerOptions,
|
|
83
216
|
promise,
|
|
84
217
|
addErrorListener,
|
|
85
218
|
removeAllErrorListeners,
|
package/dist/actionBus.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ActionDefinitionHelper, createAction } from "./action";
|
|
2
|
-
import { ListenerOptions } from "./event";
|
|
3
|
-
import type { ApiType, BaseHandler, ErrorListenerSignature, KeyOf, MapKey } from "./lib/types";
|
|
1
|
+
import { ActionDefinitionHelper, createAction } from "./action.js";
|
|
2
|
+
import { ListenerOptions } from "./event.js";
|
|
3
|
+
import type { ApiType, BaseHandler, ErrorListenerSignature, KeyOf, MapKey } from "./lib/types.js";
|
|
4
4
|
export interface BaseActionsMap {
|
|
5
5
|
[key: MapKey]: BaseHandler;
|
|
6
6
|
}
|
|
@@ -16,8 +16,16 @@ export type ActionBusDefinitionHelper<ActionsMap extends BaseActionsMap> = {
|
|
|
16
16
|
};
|
|
17
17
|
export declare function createActionBus<ActionsMap extends BaseActionsMap>(initialActions?: ActionsMap, errorListener?: ErrorListenerSignature<any[]>): ApiType<ActionBusDefinitionHelper<ActionsMap>, {
|
|
18
18
|
readonly add: (name: MapKey, action: BaseHandler) => void;
|
|
19
|
+
readonly replace: (name: MapKey, action: BaseHandler) => void;
|
|
20
|
+
readonly removeAction: (name: MapKey) => void;
|
|
21
|
+
readonly has: (name: MapKey) => boolean;
|
|
19
22
|
readonly get: <K extends KeyOf<GetActionDefinitionsMap<ActionsMap>>>(name: K) => GetActionTypesMap<ActionsMap>[K];
|
|
20
|
-
readonly invoke: <K extends KeyOf<GetActionDefinitionsMap<ActionsMap>>>(name: K, ...args: GetActionDefinitionsMap<ActionsMap>[K]["actionArguments"]) => Promise<import("./action").ActionResponse<Awaited<ReturnType<ActionsMap[K]>>, Parameters<ActionsMap[K]>>>;
|
|
23
|
+
readonly invoke: <K extends KeyOf<GetActionDefinitionsMap<ActionsMap>>>(name: K, ...args: GetActionDefinitionsMap<ActionsMap>[K]["actionArguments"]) => Promise<import("./action.js").ActionResponse<Awaited<ReturnType<ActionsMap[K]>>, Parameters<ActionsMap[K]>>>;
|
|
24
|
+
readonly destroy: () => void;
|
|
25
|
+
readonly isDestroyed: () => boolean;
|
|
26
|
+
readonly getStatus: <K extends KeyOf<GetActionDefinitionsMap<ActionsMap>>>(name: K) => GetActionDefinitionsMap<ActionsMap>[K]["statusType"];
|
|
27
|
+
readonly onStatusChange: <K extends KeyOf<GetActionDefinitionsMap<ActionsMap>>>(name: K, handler: GetActionDefinitionsMap<ActionsMap>[K]["statusListenerSignature"]) => void;
|
|
28
|
+
readonly removeStatusListener: <K extends KeyOf<GetActionDefinitionsMap<ActionsMap>>>(name: K, handler: GetActionDefinitionsMap<ActionsMap>[K]["statusListenerSignature"]) => boolean | undefined;
|
|
21
29
|
readonly addListener: <K extends KeyOf<GetActionDefinitionsMap<ActionsMap>>>(name: K, handler: GetActionDefinitionsMap<ActionsMap>[K]["listenerSignature"], options?: ListenerOptions) => void;
|
|
22
30
|
/** @alias addListener */
|
|
23
31
|
readonly on: <K extends KeyOf<GetActionDefinitionsMap<ActionsMap>>>(name: K, handler: GetActionDefinitionsMap<ActionsMap>[K]["listenerSignature"], options?: ListenerOptions) => void;
|
|
@@ -35,6 +43,7 @@ export declare function createActionBus<ActionsMap extends BaseActionsMap>(initi
|
|
|
35
43
|
readonly un: <K extends KeyOf<GetActionDefinitionsMap<ActionsMap>>>(name: K, handler: GetActionDefinitionsMap<ActionsMap>[K]["listenerSignature"], context?: object | null, tag?: string | null) => boolean;
|
|
36
44
|
/** @alias removeListener */
|
|
37
45
|
readonly unsubscribe: <K extends KeyOf<GetActionDefinitionsMap<ActionsMap>>>(name: K, handler: GetActionDefinitionsMap<ActionsMap>[K]["listenerSignature"], context?: object | null, tag?: string | null) => boolean;
|
|
46
|
+
readonly updateListenerOptions: <K extends KeyOf<GetActionDefinitionsMap<ActionsMap>>>(name: K, handler: GetActionDefinitionsMap<ActionsMap>[K]["listenerSignature"], context?: object | null, nextOptions?: ListenerOptions) => boolean;
|
|
38
47
|
readonly addErrorListener: (handler: ErrorListenerSignature<any[]>, listenerOptions?: ListenerOptions) => void;
|
|
39
48
|
readonly removeErrorListener: (handler: ErrorListenerSignature<any[]>, context?: object | null, tag?: string | null) => boolean;
|
|
40
49
|
}>;
|