@kuindji/reactive 1.0.24 → 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.
- package/README.md +34 -11
- package/dist/action.d.ts +12 -10
- package/dist/action.js +20 -10
- package/dist/actionBus.d.ts +8 -4
- package/dist/actionBus.js +34 -2
- package/dist/actionMap.d.ts +21 -19
- package/dist/actionMap.js +10 -4
- package/dist/event.d.ts +3 -2
- package/dist/event.js +112 -58
- package/dist/eventBus.d.ts +5 -3
- package/dist/eventBus.js +88 -31
- 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/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/useActionMap.d.ts +4 -4
- package/dist/react/useActionMap.js +40 -7
- 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/useStoreState.d.ts +2 -2
- package/dist/react/useStoreState.js +14 -21
- package/dist/react.d.ts +13 -13
- package/dist/react.js +13 -13
- package/dist/store.d.ts +9 -8
- package/dist/store.js +91 -24
- package/package.json +13 -3
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { useCallback,
|
|
1
|
+
import { useCallback, useRef } from "react";
|
|
2
|
+
import { useReconciledListener } from "./useReconciledListener.js";
|
|
2
3
|
export function useListenToStoreChanges(store, key, listener, options) {
|
|
3
4
|
const listenerRef = useRef(listener);
|
|
4
5
|
listenerRef.current = listener;
|
|
5
6
|
const genericHandler = useCallback((value, previousValue) => {
|
|
6
7
|
return listenerRef.current(value, previousValue);
|
|
7
8
|
}, []);
|
|
8
|
-
|
|
9
|
-
store
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
useReconciledListener({
|
|
10
|
+
keyDeps: [store, key],
|
|
11
|
+
options,
|
|
12
|
+
subscribe: (opts) => store.onChange(key, genericHandler, opts !== null && opts !== void 0 ? opts : undefined),
|
|
13
|
+
unsubscribe: (ctx) => store.removeOnChange(key, genericHandler, ctx),
|
|
14
|
+
update: (ctx, opts) => store.updateOnChangeOptions(key, genericHandler, ctx, opts !== null && opts !== void 0 ? opts : undefined),
|
|
15
|
+
});
|
|
14
16
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ListenerOptions } from "../event.js";
|
|
2
|
+
type ListenerOps = {
|
|
3
|
+
/**
|
|
4
|
+
* Identity dependencies. When any element changes (reference equality) the
|
|
5
|
+
* listener is fully resubscribed: the old registration is removed using the
|
|
6
|
+
* previous closure (previous target + previous context) and a fresh one is
|
|
7
|
+
* added. `context` is appended automatically because it is part of listener
|
|
8
|
+
* identity. The array length must stay constant across renders.
|
|
9
|
+
*/
|
|
10
|
+
keyDeps: ReadonlyArray<unknown>;
|
|
11
|
+
options?: ListenerOptions | null;
|
|
12
|
+
/** Add the listener to the current target with the given options. */
|
|
13
|
+
subscribe: (options?: ListenerOptions | null) => void;
|
|
14
|
+
/** Remove the listener from the current target using the given context. */
|
|
15
|
+
unsubscribe: (context: object | null) => void;
|
|
16
|
+
/** Update soft options on the live listener in place (counters preserved). */
|
|
17
|
+
update: (context: object | null, options?: ListenerOptions | null) => void;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Reconciles a single reactive listener across renders without relying on the
|
|
21
|
+
* identity of the options object.
|
|
22
|
+
*
|
|
23
|
+
* Two effects cooperate:
|
|
24
|
+
* - an identity effect keyed by `[...keyDeps, context]` performs the classic
|
|
25
|
+
* add-on-mount / remove-on-cleanup cycle, so target/context changes (and
|
|
26
|
+
* React StrictMode remounts) resubscribe correctly using the OLD context on
|
|
27
|
+
* cleanup;
|
|
28
|
+
* - a reconciliation effect runs every render and, when only soft options
|
|
29
|
+
* changed, updates the live listener in place instead of resubscribing, so
|
|
30
|
+
* per-listener counters are preserved.
|
|
31
|
+
*/
|
|
32
|
+
export declare function useReconciledListener({ keyDeps, options, subscribe, unsubscribe, update, }: ListenerOps): void;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { areListenerOptionsEqual } from "./listenerOptionsEqual.js";
|
|
3
|
+
/**
|
|
4
|
+
* Reconciles a single reactive listener across renders without relying on the
|
|
5
|
+
* identity of the options object.
|
|
6
|
+
*
|
|
7
|
+
* Two effects cooperate:
|
|
8
|
+
* - an identity effect keyed by `[...keyDeps, context]` performs the classic
|
|
9
|
+
* add-on-mount / remove-on-cleanup cycle, so target/context changes (and
|
|
10
|
+
* React StrictMode remounts) resubscribe correctly using the OLD context on
|
|
11
|
+
* cleanup;
|
|
12
|
+
* - a reconciliation effect runs every render and, when only soft options
|
|
13
|
+
* changed, updates the live listener in place instead of resubscribing, so
|
|
14
|
+
* per-listener counters are preserved.
|
|
15
|
+
*/
|
|
16
|
+
export function useReconciledListener({ keyDeps, options, subscribe, unsubscribe, update, }) {
|
|
17
|
+
var _a;
|
|
18
|
+
const context = (_a = options === null || options === void 0 ? void 0 : options.context) !== null && _a !== void 0 ? _a : null;
|
|
19
|
+
const committedRef = useRef(undefined);
|
|
20
|
+
const registeredRef = useRef(false);
|
|
21
|
+
// Identity effect: (re)subscribe on target/context change.
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
subscribe(options);
|
|
24
|
+
committedRef.current = options;
|
|
25
|
+
registeredRef.current = true;
|
|
26
|
+
return () => {
|
|
27
|
+
unsubscribe(context);
|
|
28
|
+
registeredRef.current = false;
|
|
29
|
+
};
|
|
30
|
+
}, [...keyDeps, context]);
|
|
31
|
+
// Reconciliation effect: in-place soft-option updates every render.
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!registeredRef.current) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (committedRef.current === options) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!areListenerOptionsEqual(committedRef.current, options)) {
|
|
40
|
+
update(context, options);
|
|
41
|
+
}
|
|
42
|
+
committedRef.current = options;
|
|
43
|
+
});
|
|
44
|
+
}
|
package/dist/react/useStore.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createStore } from "../store";
|
|
2
|
-
import type { BasePropMap, BeforeChangeEventName, ChangeEventName, ErrorEventName, ResetEventName, StoreDefinitionHelper } from "../store";
|
|
1
|
+
import { createStore } from "../store.js";
|
|
2
|
+
import type { BasePropMap, BeforeChangeEventName, ChangeEventName, ErrorEventName, ResetEventName, StoreDefinitionHelper } from "../store.js";
|
|
3
3
|
export type { BasePropMap, BeforeChangeEventName, ChangeEventName, ErrorEventName, ResetEventName, StoreDefinitionHelper, };
|
|
4
4
|
export declare function useStore<PropMap extends BasePropMap, Store extends StoreDefinitionHelper<PropMap> = StoreDefinitionHelper<PropMap>, Config extends {
|
|
5
5
|
onChange?: Partial<Store["changeEvents"]>;
|
package/dist/react/useStore.js
CHANGED
|
@@ -1,26 +1,78 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
2
|
-
import { createStore } from "../store";
|
|
1
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { createStore } from "../store.js";
|
|
3
3
|
export function useStore(initialData = {}, config) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
// initialData is seed-only (captured once); later changes are ignored.
|
|
5
|
+
const store = useMemo(() => createStore(initialData), []);
|
|
6
|
+
// Track only the handlers we added (per category + key) and compare by
|
|
7
|
+
// reference, so consumer listeners added outside the hook are never
|
|
8
|
+
// touched and inline-equal config maps do not duplicate or churn
|
|
9
|
+
// subscriptions.
|
|
10
|
+
const appliedRef = useRef({
|
|
11
|
+
onChange: {},
|
|
12
|
+
pipes: {},
|
|
13
|
+
control: {},
|
|
14
|
+
});
|
|
15
|
+
const add = (category, key, fn) => {
|
|
16
|
+
if (category === "onChange") {
|
|
17
|
+
store.onChange(key, fn);
|
|
10
18
|
}
|
|
11
|
-
if (
|
|
12
|
-
|
|
13
|
-
// @ts-expect-error - TS widens for-in key to string; types are correct
|
|
14
|
-
store.pipe(key, config.pipes[key]);
|
|
15
|
-
}
|
|
19
|
+
else if (category === "pipes") {
|
|
20
|
+
store.pipe(key, fn);
|
|
16
21
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
else {
|
|
23
|
+
store.control(key, fn);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const remove = (category, key, fn) => {
|
|
27
|
+
if (category === "onChange") {
|
|
28
|
+
store.removeOnChange(key, fn);
|
|
22
29
|
}
|
|
23
|
-
|
|
30
|
+
else if (category === "pipes") {
|
|
31
|
+
store.removePipe(key, fn);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
store.removeControl(key, fn);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
// Reconcile config handlers every render (no cleanup here, so equal config
|
|
38
|
+
// never causes remove/add churn).
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const categories = {
|
|
41
|
+
onChange: config === null || config === void 0 ? void 0 : config.onChange,
|
|
42
|
+
pipes: config === null || config === void 0 ? void 0 : config.pipes,
|
|
43
|
+
control: config === null || config === void 0 ? void 0 : config.control,
|
|
44
|
+
};
|
|
45
|
+
Object.keys(categories).forEach((category) => {
|
|
46
|
+
var _a;
|
|
47
|
+
const next = (_a = categories[category]) !== null && _a !== void 0 ? _a : {};
|
|
48
|
+
const prev = appliedRef.current[category];
|
|
49
|
+
// Remove stale/changed handlers before adding (matters for pipes).
|
|
50
|
+
for (const key in prev) {
|
|
51
|
+
if (next[key] !== prev[key]) {
|
|
52
|
+
remove(category, key, prev[key]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for (const key in next) {
|
|
56
|
+
if (next[key] !== prev[key]) {
|
|
57
|
+
add(category, key, next[key]);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
appliedRef.current[category] = Object.assign({}, next);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
// Unmount cleanup: detach everything we applied (also makes StrictMode
|
|
64
|
+
// remount re-subscribe cleanly via the reconcile effect above).
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
return () => {
|
|
67
|
+
const applied = appliedRef.current;
|
|
68
|
+
Object.keys(applied).forEach((category) => {
|
|
69
|
+
const map = applied[category];
|
|
70
|
+
for (const key in map) {
|
|
71
|
+
remove(category, key, map[key]);
|
|
72
|
+
}
|
|
73
|
+
applied[category] = {};
|
|
74
|
+
});
|
|
75
|
+
};
|
|
24
76
|
}, []);
|
|
25
77
|
return store;
|
|
26
78
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { KeyOf } from "../lib/types";
|
|
2
|
-
import { BaseStore } from "../store";
|
|
1
|
+
import { KeyOf } from "../lib/types.js";
|
|
2
|
+
import { BaseStore } from "../store.js";
|
|
3
3
|
export declare function useStoreState<TStore extends BaseStore, TKey extends KeyOf<TStore["__type"]["propTypes"]>>(store: TStore, key: TKey): readonly [TStore["__type"]["propTypes"][TKey], (value: TStore["__type"]["propTypes"][TKey] | ((previousValue?: TStore["__type"]["propTypes"][TKey]) => TStore["__type"]["propTypes"][TKey])) => void];
|
|
@@ -1,30 +1,23 @@
|
|
|
1
|
-
import { useCallback,
|
|
1
|
+
import { useCallback, useSyncExternalStore } from "react";
|
|
2
2
|
export function useStoreState(store, key) {
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
const subscribe = useCallback((onStoreChange) => {
|
|
4
|
+
const listener = () => {
|
|
5
|
+
onStoreChange();
|
|
6
|
+
};
|
|
7
|
+
store.onChange(key, listener);
|
|
8
|
+
return () => {
|
|
9
|
+
store.removeOnChange(key, listener);
|
|
10
|
+
};
|
|
11
|
+
}, [store, key]);
|
|
12
|
+
const getSnapshot = useCallback(() => store.get(key), [store, key]);
|
|
13
|
+
const value = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
9
14
|
const setter = useCallback((value) => {
|
|
10
15
|
if (typeof value === "function") {
|
|
11
|
-
|
|
16
|
+
store.set(key, value(store.get(key)));
|
|
12
17
|
}
|
|
13
18
|
else {
|
|
14
|
-
|
|
19
|
+
store.set(key, value);
|
|
15
20
|
}
|
|
16
|
-
}, []);
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
return () => {
|
|
19
|
-
storeRef.current.removeOnChange(keyRef.current, onChange);
|
|
20
|
-
};
|
|
21
|
-
}, []);
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
storeRef.current.removeOnChange(keyRef.current, onChange);
|
|
24
|
-
storeRef.current = store;
|
|
25
|
-
keyRef.current = key;
|
|
26
|
-
storeRef.current.onChange(keyRef.current, onChange);
|
|
27
|
-
setValue(store.get(key));
|
|
28
21
|
}, [store, key]);
|
|
29
22
|
return [value, setter];
|
|
30
23
|
}
|
package/dist/react.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export * from "./react/ErrorBoundary";
|
|
2
|
-
export * from "./react/useAction";
|
|
3
|
-
export * from "./react/useActionBus";
|
|
4
|
-
export * from "./react/useActionMap";
|
|
5
|
-
export * from "./react/useEvent";
|
|
6
|
-
export * from "./react/useEventBus";
|
|
7
|
-
export * from "./react/useListenToAction";
|
|
8
|
-
export * from "./react/useListenToActionBus";
|
|
9
|
-
export * from "./react/useListenToEvent";
|
|
10
|
-
export * from "./react/useListenToEventBus";
|
|
11
|
-
export * from "./react/useListenToStoreChanges";
|
|
12
|
-
export * from "./react/useStore";
|
|
13
|
-
export * from "./react/useStoreState";
|
|
1
|
+
export * from "./react/ErrorBoundary.js";
|
|
2
|
+
export * from "./react/useAction.js";
|
|
3
|
+
export * from "./react/useActionBus.js";
|
|
4
|
+
export * from "./react/useActionMap.js";
|
|
5
|
+
export * from "./react/useEvent.js";
|
|
6
|
+
export * from "./react/useEventBus.js";
|
|
7
|
+
export * from "./react/useListenToAction.js";
|
|
8
|
+
export * from "./react/useListenToActionBus.js";
|
|
9
|
+
export * from "./react/useListenToEvent.js";
|
|
10
|
+
export * from "./react/useListenToEventBus.js";
|
|
11
|
+
export * from "./react/useListenToStoreChanges.js";
|
|
12
|
+
export * from "./react/useStore.js";
|
|
13
|
+
export * from "./react/useStoreState.js";
|
package/dist/react.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export * from "./react/ErrorBoundary";
|
|
2
|
-
export * from "./react/useAction";
|
|
3
|
-
export * from "./react/useActionBus";
|
|
4
|
-
export * from "./react/useActionMap";
|
|
5
|
-
export * from "./react/useEvent";
|
|
6
|
-
export * from "./react/useEventBus";
|
|
7
|
-
export * from "./react/useListenToAction";
|
|
8
|
-
export * from "./react/useListenToActionBus";
|
|
9
|
-
export * from "./react/useListenToEvent";
|
|
10
|
-
export * from "./react/useListenToEventBus";
|
|
11
|
-
export * from "./react/useListenToStoreChanges";
|
|
12
|
-
export * from "./react/useStore";
|
|
13
|
-
export * from "./react/useStoreState";
|
|
1
|
+
export * from "./react/ErrorBoundary.js";
|
|
2
|
+
export * from "./react/useAction.js";
|
|
3
|
+
export * from "./react/useActionBus.js";
|
|
4
|
+
export * from "./react/useActionMap.js";
|
|
5
|
+
export * from "./react/useEvent.js";
|
|
6
|
+
export * from "./react/useEventBus.js";
|
|
7
|
+
export * from "./react/useListenToAction.js";
|
|
8
|
+
export * from "./react/useListenToActionBus.js";
|
|
9
|
+
export * from "./react/useListenToEvent.js";
|
|
10
|
+
export * from "./react/useListenToEventBus.js";
|
|
11
|
+
export * from "./react/useListenToStoreChanges.js";
|
|
12
|
+
export * from "./react/useStore.js";
|
|
13
|
+
export * from "./react/useStoreState.js";
|
package/dist/store.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EventBusDefinitionHelper } from "./eventBus";
|
|
2
|
-
import type { ApiType, ErrorListenerSignature, KeyOf, MapKey } from "./lib/types";
|
|
1
|
+
import { EventBusDefinitionHelper } from "./eventBus.js";
|
|
2
|
+
import type { ApiType, ErrorListenerSignature, KeyOf, MapKey } from "./lib/types.js";
|
|
3
3
|
export interface BasePropMap {
|
|
4
4
|
[key: MapKey]: any;
|
|
5
5
|
}
|
|
@@ -44,12 +44,13 @@ export declare function createStore<PropMap extends BasePropMap = BasePropMap>(i
|
|
|
44
44
|
};
|
|
45
45
|
readonly isEmpty: () => boolean;
|
|
46
46
|
readonly reset: () => void;
|
|
47
|
-
readonly onChange: <K extends KeyOf<import("./eventBus").GetEventsMap<StoreChangeEvents<PropMap>>>, H extends import("./eventBus").GetEventsMap<StoreChangeEvents<PropMap>>[K]["signature"]>(name: K, handler: H, options?: import("./event").ListenerOptions) => void;
|
|
48
|
-
readonly removeOnChange: <K extends KeyOf<import("./eventBus").GetEventsMap<StoreChangeEvents<PropMap>>>, H extends import("./eventBus").GetEventsMap<StoreChangeEvents<PropMap>>[K]["signature"]>(name: K, handler: H, context?: object | null, tag?: string | null) => void;
|
|
49
|
-
readonly
|
|
50
|
-
readonly
|
|
51
|
-
readonly
|
|
52
|
-
readonly
|
|
47
|
+
readonly onChange: <K extends KeyOf<import("./eventBus.js").GetEventsMap<StoreChangeEvents<PropMap>>>, H extends import("./eventBus.js").GetEventsMap<StoreChangeEvents<PropMap>>[K]["signature"]>(name: K, handler: H, options?: import("./event.js").ListenerOptions) => void;
|
|
48
|
+
readonly removeOnChange: <K extends KeyOf<import("./eventBus.js").GetEventsMap<StoreChangeEvents<PropMap>>>, H extends import("./eventBus.js").GetEventsMap<StoreChangeEvents<PropMap>>[K]["signature"]>(name: K, handler: H, context?: object | null, tag?: string | null) => void;
|
|
49
|
+
readonly updateOnChangeOptions: <K extends KeyOf<import("./eventBus.js").GetEventsMap<StoreChangeEvents<PropMap>>>, H extends import("./eventBus.js").GetEventsMap<StoreChangeEvents<PropMap>>[K]["signature"]>(name: K, handler: H, context?: object | null, nextOptions?: import("./event.js").ListenerOptions) => boolean;
|
|
50
|
+
readonly control: <K extends KeyOf<import("./eventBus.js").GetEventsMap<StoreControlEvents<PropMap>>>, H extends import("./eventBus.js").GetEventsMap<StoreControlEvents<PropMap>>[K]["signature"]>(name: K, handler: H, options?: import("./event.js").ListenerOptions) => void;
|
|
51
|
+
readonly removeControl: <K extends KeyOf<import("./eventBus.js").GetEventsMap<StoreControlEvents<PropMap>>>, H extends import("./eventBus.js").GetEventsMap<StoreControlEvents<PropMap>>[K]["signature"]>(name: K, handler: H, context?: object | null, tag?: string | null) => void;
|
|
52
|
+
readonly pipe: <K extends KeyOf<import("./eventBus.js").GetEventsMap<StorePipeEvents<PropMap>>>, H extends import("./eventBus.js").GetEventsMap<StorePipeEvents<PropMap>>[K]["signature"]>(name: K, handler: H, options?: import("./event.js").ListenerOptions) => void;
|
|
53
|
+
readonly removePipe: <K extends KeyOf<import("./eventBus.js").GetEventsMap<StorePipeEvents<PropMap>>>, H extends import("./eventBus.js").GetEventsMap<StorePipeEvents<PropMap>>[K]["signature"]>(name: K, handler: H, context?: object | null, tag?: string | null) => void;
|
|
53
54
|
}>;
|
|
54
55
|
export type BaseStoreDefinition = StoreDefinitionHelper<BasePropMap>;
|
|
55
56
|
export type BaseStore = ReturnType<typeof createStore<any>>;
|
package/dist/store.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createEventBus } from "./eventBus";
|
|
1
|
+
import { createEventBus } from "./eventBus.js";
|
|
2
2
|
export const BeforeChangeEventName = "before";
|
|
3
3
|
export const ChangeEventName = "change";
|
|
4
4
|
export const ResetEventName = "reset";
|
|
@@ -21,8 +21,8 @@ export function createStore(initialData = {}) {
|
|
|
21
21
|
var _a, _b, _c, _d, _e;
|
|
22
22
|
const prev = data.get(name);
|
|
23
23
|
if (prev !== value) {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const beforeChangeResults = control.all(BeforeChangeEventName, name, value);
|
|
25
|
+
if (beforeChangeResults.some((result) => result === false)) {
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
const pipeArgs = [value];
|
|
@@ -73,12 +73,16 @@ export function createStore(initialData = {}) {
|
|
|
73
73
|
if ((_c = control.get(EffectEventName)) === null || _c === void 0 ? void 0 : _c.hasListener()) {
|
|
74
74
|
try {
|
|
75
75
|
const isIntercepting = control.isIntercepting();
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
try {
|
|
77
|
+
if (!isIntercepting) {
|
|
78
|
+
control.intercept(effectInterceptor);
|
|
79
|
+
}
|
|
80
|
+
control.trigger(EffectEventName, name, value);
|
|
78
81
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
finally {
|
|
83
|
+
if (!isIntercepting) {
|
|
84
|
+
control.stopIntercepting();
|
|
85
|
+
}
|
|
82
86
|
}
|
|
83
87
|
}
|
|
84
88
|
catch (error) {
|
|
@@ -143,36 +147,48 @@ export function createStore(initialData = {}) {
|
|
|
143
147
|
const changedKeys = [];
|
|
144
148
|
const isIntercepting = control.isIntercepting();
|
|
145
149
|
const hasEffectListener = (_a = control.get(EffectEventName)) === null || _a === void 0 ? void 0 : _a.hasListener();
|
|
146
|
-
|
|
150
|
+
const shouldInterceptEffects = hasEffectListener && !isIntercepting;
|
|
151
|
+
let controlError = null;
|
|
152
|
+
if (shouldInterceptEffects) {
|
|
147
153
|
control.intercept(effectInterceptor);
|
|
148
154
|
}
|
|
149
|
-
Object.entries(name).forEach(([k, v]) => {
|
|
150
|
-
if (_set(k, v, false)) {
|
|
151
|
-
changedKeys.push(k);
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
155
|
try {
|
|
155
|
-
|
|
156
|
+
Object.entries(name).forEach(([k, v]) => {
|
|
157
|
+
if (_set(k, v, false)) {
|
|
158
|
+
changedKeys.push(k);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
const allChangedKeys = [
|
|
156
162
|
...changedKeys,
|
|
157
163
|
...effectKeys,
|
|
158
|
-
]
|
|
159
|
-
if (
|
|
164
|
+
];
|
|
165
|
+
if (allChangedKeys.length > 0) {
|
|
166
|
+
try {
|
|
167
|
+
control.trigger(ChangeEventName, allChangedKeys);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
controlError = error instanceof Error
|
|
171
|
+
? error
|
|
172
|
+
: new Error(String(error));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
if (shouldInterceptEffects) {
|
|
160
178
|
effectKeys = [];
|
|
161
179
|
control.stopIntercepting();
|
|
162
180
|
}
|
|
163
181
|
}
|
|
164
|
-
|
|
182
|
+
if (controlError) {
|
|
165
183
|
control.trigger(ErrorEventName, {
|
|
166
|
-
error:
|
|
167
|
-
? error
|
|
168
|
-
: new Error(String(error)),
|
|
184
|
+
error: controlError,
|
|
169
185
|
args: [name],
|
|
170
186
|
type: "store-control",
|
|
171
187
|
});
|
|
172
188
|
if ((_b = control.get(ErrorEventName)) === null || _b === void 0 ? void 0 : _b.hasListener()) {
|
|
173
189
|
return;
|
|
174
190
|
}
|
|
175
|
-
throw
|
|
191
|
+
throw controlError;
|
|
176
192
|
}
|
|
177
193
|
}
|
|
178
194
|
else {
|
|
@@ -207,6 +223,7 @@ export function createStore(initialData = {}) {
|
|
|
207
223
|
};
|
|
208
224
|
let batching = false;
|
|
209
225
|
const batch = (fn) => {
|
|
226
|
+
var _a, _b;
|
|
210
227
|
if (batching) {
|
|
211
228
|
throw new Error("Nested batch() calls are not supported");
|
|
212
229
|
}
|
|
@@ -226,9 +243,15 @@ export function createStore(initialData = {}) {
|
|
|
226
243
|
};
|
|
227
244
|
changes.intercept(changeInterceptor);
|
|
228
245
|
control.intercept(controlInterceptor);
|
|
246
|
+
let callbackError;
|
|
247
|
+
let hasCallbackError = false;
|
|
229
248
|
try {
|
|
230
249
|
fn();
|
|
231
250
|
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
callbackError = error;
|
|
253
|
+
hasCallbackError = true;
|
|
254
|
+
}
|
|
232
255
|
finally {
|
|
233
256
|
control.stopIntercepting();
|
|
234
257
|
changes.stopIntercepting();
|
|
@@ -239,10 +262,53 @@ export function createStore(initialData = {}) {
|
|
|
239
262
|
value,
|
|
240
263
|
prev,
|
|
241
264
|
];
|
|
242
|
-
|
|
265
|
+
try {
|
|
266
|
+
changes.trigger(propName, ...changeArgs);
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
control.trigger(ErrorEventName, {
|
|
270
|
+
error: error instanceof Error
|
|
271
|
+
? error
|
|
272
|
+
: new Error(String(error)),
|
|
273
|
+
args: changeArgs,
|
|
274
|
+
type: "store-change",
|
|
275
|
+
name: propName,
|
|
276
|
+
});
|
|
277
|
+
if ((_a = control.get(ErrorEventName)) === null || _a === void 0 ? void 0 : _a.hasListener()) {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (hasCallbackError) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
243
285
|
}
|
|
244
286
|
if (allChangedKeys.length > 0) {
|
|
245
|
-
|
|
287
|
+
try {
|
|
288
|
+
control.trigger(ChangeEventName, allChangedKeys);
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
control.trigger(ErrorEventName, {
|
|
292
|
+
error: error instanceof Error
|
|
293
|
+
? error
|
|
294
|
+
: new Error(String(error)),
|
|
295
|
+
args: [allChangedKeys],
|
|
296
|
+
type: "store-control",
|
|
297
|
+
});
|
|
298
|
+
if ((_b = control.get(ErrorEventName)) === null || _b === void 0 ? void 0 : _b.hasListener()) {
|
|
299
|
+
if (hasCallbackError) {
|
|
300
|
+
throw callbackError;
|
|
301
|
+
}
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (hasCallbackError) {
|
|
305
|
+
throw callbackError;
|
|
306
|
+
}
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (hasCallbackError) {
|
|
311
|
+
throw callbackError;
|
|
246
312
|
}
|
|
247
313
|
};
|
|
248
314
|
const reset = () => {
|
|
@@ -259,6 +325,7 @@ export function createStore(initialData = {}) {
|
|
|
259
325
|
reset,
|
|
260
326
|
onChange: changes.addListener,
|
|
261
327
|
removeOnChange: changes.removeListener,
|
|
328
|
+
updateOnChangeOptions: changes.updateListenerOptions,
|
|
262
329
|
control: control.addListener,
|
|
263
330
|
removeControl: control.removeListener,
|
|
264
331
|
pipe: pipe.addListener,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kuindji/reactive",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"author": "Ivan Kuindzhi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -22,6 +22,14 @@
|
|
|
22
22
|
"typescript": "^5.9.3",
|
|
23
23
|
"typescript-eslint": "^8.48.0"
|
|
24
24
|
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": ">=18.0.0"
|
|
27
|
+
},
|
|
28
|
+
"peerDependenciesMeta": {
|
|
29
|
+
"react": {
|
|
30
|
+
"optional": true
|
|
31
|
+
}
|
|
32
|
+
},
|
|
25
33
|
"exports": {
|
|
26
34
|
".": {
|
|
27
35
|
"types": "./dist/index.d.ts",
|
|
@@ -62,11 +70,13 @@
|
|
|
62
70
|
"license": "ISC",
|
|
63
71
|
"scripts": {
|
|
64
72
|
"build": "tsc -p ./tsconfig-build.json",
|
|
73
|
+
"prepublishOnly": "bun run build",
|
|
65
74
|
"lint": "bun eslint .",
|
|
75
|
+
"type-check": "tsc --noEmit",
|
|
66
76
|
"test": "bun test tests/**/*.spec.ts*",
|
|
67
77
|
"test:types": "tsc -p ./tests/types/tsconfig.json",
|
|
68
|
-
"test:all": "bun run test:types && bun run test"
|
|
78
|
+
"test:all": "bun run type-check && bun run test:types && bun run test"
|
|
69
79
|
},
|
|
70
80
|
"sideEffects": false,
|
|
71
81
|
"types": "dist/index.d.ts"
|
|
72
|
-
}
|
|
82
|
+
}
|