@bquery/bquery 1.2.0 → 1.4.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 +127 -27
- package/dist/batch-x7b2eZST.js +13 -0
- package/dist/batch-x7b2eZST.js.map +1 -0
- package/dist/component/component.d.ts +69 -0
- package/dist/component/component.d.ts.map +1 -0
- package/dist/component/html.d.ts +35 -0
- package/dist/component/html.d.ts.map +1 -0
- package/dist/component/index.d.ts +3 -126
- package/dist/component/index.d.ts.map +1 -1
- package/dist/component/props.d.ts +18 -0
- package/dist/component/props.d.ts.map +1 -0
- package/dist/component/types.d.ts +77 -0
- package/dist/component/types.d.ts.map +1 -0
- package/dist/component.es.mjs +90 -59
- package/dist/component.es.mjs.map +1 -1
- package/dist/core/collection.d.ts +55 -3
- package/dist/core/collection.d.ts.map +1 -1
- package/dist/core/dom.d.ts +6 -0
- package/dist/core/dom.d.ts.map +1 -0
- package/dist/core/element.d.ts +31 -4
- package/dist/core/element.d.ts.map +1 -1
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/utils/array.d.ts +74 -0
- package/dist/core/utils/array.d.ts.map +1 -0
- package/dist/core/utils/function.d.ts +87 -0
- package/dist/core/utils/function.d.ts.map +1 -0
- package/dist/core/utils/index.d.ts +70 -0
- package/dist/core/utils/index.d.ts.map +1 -0
- package/dist/core/utils/misc.d.ts +63 -0
- package/dist/core/utils/misc.d.ts.map +1 -0
- package/dist/core/utils/number.d.ts +65 -0
- package/dist/core/utils/number.d.ts.map +1 -0
- package/dist/core/utils/object.d.ts +133 -0
- package/dist/core/utils/object.d.ts.map +1 -0
- package/dist/core/utils/string.d.ts +80 -0
- package/dist/core/utils/string.d.ts.map +1 -0
- package/dist/core/utils/type-guards.d.ts +79 -0
- package/dist/core/utils/type-guards.d.ts.map +1 -0
- package/dist/core-BhpuvPhy.js +170 -0
- package/dist/core-BhpuvPhy.js.map +1 -0
- package/dist/core.es.mjs +495 -489
- package/dist/core.es.mjs.map +1 -1
- package/dist/full.d.ts +2 -2
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +87 -64
- package/dist/full.es.mjs.map +1 -1
- package/dist/full.iife.js +2 -2
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +2 -2
- package/dist/full.umd.js.map +1 -1
- package/dist/index.es.mjs +138 -68
- package/dist/index.es.mjs.map +1 -1
- package/dist/motion/animate.d.ts +25 -0
- package/dist/motion/animate.d.ts.map +1 -0
- package/dist/motion/easing.d.ts +30 -0
- package/dist/motion/easing.d.ts.map +1 -0
- package/dist/motion/flip.d.ts +55 -0
- package/dist/motion/flip.d.ts.map +1 -0
- package/dist/motion/index.d.ts +11 -138
- package/dist/motion/index.d.ts.map +1 -1
- package/dist/motion/keyframes.d.ts +21 -0
- package/dist/motion/keyframes.d.ts.map +1 -0
- package/dist/motion/reduced-motion.d.ts +12 -0
- package/dist/motion/reduced-motion.d.ts.map +1 -0
- package/dist/motion/scroll.d.ts +15 -0
- package/dist/motion/scroll.d.ts.map +1 -0
- package/dist/motion/spring.d.ts +42 -0
- package/dist/motion/spring.d.ts.map +1 -0
- package/dist/motion/stagger.d.ts +22 -0
- package/dist/motion/stagger.d.ts.map +1 -0
- package/dist/motion/timeline.d.ts +21 -0
- package/dist/motion/timeline.d.ts.map +1 -0
- package/dist/motion/transition.d.ts +22 -0
- package/dist/motion/transition.d.ts.map +1 -0
- package/dist/motion/types.d.ts +182 -0
- package/dist/motion/types.d.ts.map +1 -0
- package/dist/motion.es.mjs +320 -61
- package/dist/motion.es.mjs.map +1 -1
- package/dist/persisted-DHoi3uEs.js +278 -0
- package/dist/persisted-DHoi3uEs.js.map +1 -0
- package/dist/platform/storage.d.ts.map +1 -1
- package/dist/platform.es.mjs +12 -7
- package/dist/platform.es.mjs.map +1 -1
- package/dist/reactive/batch.d.ts +13 -0
- package/dist/reactive/batch.d.ts.map +1 -0
- package/dist/reactive/computed.d.ts +50 -0
- package/dist/reactive/computed.d.ts.map +1 -0
- package/dist/reactive/core.d.ts +72 -0
- package/dist/reactive/core.d.ts.map +1 -0
- package/dist/reactive/effect.d.ts +15 -0
- package/dist/reactive/effect.d.ts.map +1 -0
- package/dist/reactive/index.d.ts +2 -2
- package/dist/reactive/index.d.ts.map +1 -1
- package/dist/reactive/internals.d.ts +42 -0
- package/dist/reactive/internals.d.ts.map +1 -0
- package/dist/reactive/linked.d.ts +36 -0
- package/dist/reactive/linked.d.ts.map +1 -0
- package/dist/reactive/persisted.d.ts +14 -0
- package/dist/reactive/persisted.d.ts.map +1 -0
- package/dist/reactive/readonly.d.ts +26 -0
- package/dist/reactive/readonly.d.ts.map +1 -0
- package/dist/reactive/signal.d.ts +13 -312
- package/dist/reactive/signal.d.ts.map +1 -1
- package/dist/reactive/type-guards.d.ts +20 -0
- package/dist/reactive/type-guards.d.ts.map +1 -0
- package/dist/reactive/untrack.d.ts +29 -0
- package/dist/reactive/untrack.d.ts.map +1 -0
- package/dist/reactive/watch.d.ts +42 -0
- package/dist/reactive/watch.d.ts.map +1 -0
- package/dist/reactive.es.mjs +30 -163
- package/dist/reactive.es.mjs.map +1 -1
- package/dist/router/index.d.ts +6 -252
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/links.d.ts +44 -0
- package/dist/router/links.d.ts.map +1 -0
- package/dist/router/match.d.ts +20 -0
- package/dist/router/match.d.ts.map +1 -0
- package/dist/router/navigation.d.ts +45 -0
- package/dist/router/navigation.d.ts.map +1 -0
- package/dist/router/query.d.ts +16 -0
- package/dist/router/query.d.ts.map +1 -0
- package/dist/router/router.d.ts +34 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/state.d.ts +27 -0
- package/dist/router/state.d.ts.map +1 -0
- package/dist/router/types.d.ts +88 -0
- package/dist/router/types.d.ts.map +1 -0
- package/dist/router/utils.d.ts +65 -0
- package/dist/router/utils.d.ts.map +1 -0
- package/dist/router.es.mjs +168 -132
- package/dist/router.es.mjs.map +1 -1
- package/dist/sanitize-Cxvxa-DX.js +283 -0
- package/dist/sanitize-Cxvxa-DX.js.map +1 -0
- package/dist/security/constants.d.ts +42 -0
- package/dist/security/constants.d.ts.map +1 -0
- package/dist/security/csp.d.ts +24 -0
- package/dist/security/csp.d.ts.map +1 -0
- package/dist/security/index.d.ts +4 -2
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/sanitize-core.d.ts +13 -0
- package/dist/security/sanitize-core.d.ts.map +1 -0
- package/dist/security/sanitize.d.ts +5 -57
- package/dist/security/sanitize.d.ts.map +1 -1
- package/dist/security/trusted-types.d.ts +25 -0
- package/dist/security/trusted-types.d.ts.map +1 -0
- package/dist/security/types.d.ts +36 -0
- package/dist/security/types.d.ts.map +1 -0
- package/dist/security.es.mjs +50 -277
- package/dist/security.es.mjs.map +1 -1
- package/dist/store/create-store.d.ts +15 -0
- package/dist/store/create-store.d.ts.map +1 -0
- package/dist/store/define-store.d.ts +28 -0
- package/dist/store/define-store.d.ts.map +1 -0
- package/dist/store/devtools.d.ts +22 -0
- package/dist/store/devtools.d.ts.map +1 -0
- package/dist/store/index.d.ts +10 -286
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/mapping.d.ts +28 -0
- package/dist/store/mapping.d.ts.map +1 -0
- package/dist/store/persisted.d.ts +13 -0
- package/dist/store/persisted.d.ts.map +1 -0
- package/dist/store/plugins.d.ts +13 -0
- package/dist/store/plugins.d.ts.map +1 -0
- package/dist/store/registry.d.ts +28 -0
- package/dist/store/registry.d.ts.map +1 -0
- package/dist/store/types.d.ts +71 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/utils.d.ts +28 -0
- package/dist/store/utils.d.ts.map +1 -0
- package/dist/store/watch.d.ts +23 -0
- package/dist/store/watch.d.ts.map +1 -0
- package/dist/store.es.mjs +22 -224
- package/dist/store.es.mjs.map +1 -1
- package/dist/type-guards-BdKlYYlS.js +32 -0
- package/dist/type-guards-BdKlYYlS.js.map +1 -0
- package/dist/untrack-DNnnqdlR.js +6 -0
- package/dist/untrack-DNnnqdlR.js.map +1 -0
- package/dist/view/directives/bind.d.ts +7 -0
- package/dist/view/directives/bind.d.ts.map +1 -0
- package/dist/view/directives/class.d.ts +8 -0
- package/dist/view/directives/class.d.ts.map +1 -0
- package/dist/view/directives/for.d.ts +23 -0
- package/dist/view/directives/for.d.ts.map +1 -0
- package/dist/view/directives/html.d.ts +7 -0
- package/dist/view/directives/html.d.ts.map +1 -0
- package/dist/view/directives/if.d.ts +7 -0
- package/dist/view/directives/if.d.ts.map +1 -0
- package/dist/view/directives/index.d.ts +12 -0
- package/dist/view/directives/index.d.ts.map +1 -0
- package/dist/view/directives/model.d.ts +7 -0
- package/dist/view/directives/model.d.ts.map +1 -0
- package/dist/view/directives/on.d.ts +7 -0
- package/dist/view/directives/on.d.ts.map +1 -0
- package/dist/view/directives/ref.d.ts +7 -0
- package/dist/view/directives/ref.d.ts.map +1 -0
- package/dist/view/directives/show.d.ts +7 -0
- package/dist/view/directives/show.d.ts.map +1 -0
- package/dist/view/directives/style.d.ts +7 -0
- package/dist/view/directives/style.d.ts.map +1 -0
- package/dist/view/directives/text.d.ts +7 -0
- package/dist/view/directives/text.d.ts.map +1 -0
- package/dist/view/evaluate.d.ts +43 -0
- package/dist/view/evaluate.d.ts.map +1 -0
- package/dist/view/index.d.ts +3 -93
- package/dist/view/index.d.ts.map +1 -1
- package/dist/view/mount.d.ts +69 -0
- package/dist/view/mount.d.ts.map +1 -0
- package/dist/view/process.d.ts +26 -0
- package/dist/view/process.d.ts.map +1 -0
- package/dist/view/types.d.ts +36 -0
- package/dist/view/types.d.ts.map +1 -0
- package/dist/view.es.mjs +358 -251
- package/dist/view.es.mjs.map +1 -1
- package/dist/watch-DXXv3iAI.js +58 -0
- package/dist/watch-DXXv3iAI.js.map +1 -0
- package/package.json +14 -14
- package/src/component/component.ts +289 -0
- package/src/component/html.ts +53 -0
- package/src/component/index.ts +40 -414
- package/src/component/props.ts +116 -0
- package/src/component/types.ts +85 -0
- package/src/core/collection.ts +181 -7
- package/src/core/dom.ts +38 -0
- package/src/core/element.ts +59 -25
- package/src/core/index.ts +48 -4
- package/src/core/utils/array.ts +102 -0
- package/src/core/utils/function.ts +151 -0
- package/src/core/utils/index.ts +83 -0
- package/src/core/utils/misc.ts +82 -0
- package/src/core/utils/number.ts +78 -0
- package/src/core/utils/object.ts +206 -0
- package/src/core/utils/string.ts +112 -0
- package/src/core/utils/type-guards.ts +112 -0
- package/src/full.ts +187 -150
- package/src/index.ts +36 -36
- package/src/motion/animate.ts +113 -0
- package/src/motion/easing.ts +40 -0
- package/src/motion/flip.ts +176 -0
- package/src/motion/index.ts +41 -358
- package/src/motion/keyframes.ts +46 -0
- package/src/motion/reduced-motion.ts +17 -0
- package/src/motion/scroll.ts +57 -0
- package/src/motion/spring.ts +150 -0
- package/src/motion/stagger.ts +43 -0
- package/src/motion/timeline.ts +246 -0
- package/src/motion/transition.ts +51 -0
- package/src/motion/types.ts +198 -0
- package/src/platform/storage.ts +215 -208
- package/src/reactive/batch.ts +22 -0
- package/src/reactive/computed.ts +92 -0
- package/src/reactive/core.ts +114 -0
- package/src/reactive/effect.ts +54 -0
- package/src/reactive/index.ts +23 -22
- package/src/reactive/internals.ts +122 -0
- package/src/reactive/linked.ts +56 -0
- package/src/reactive/persisted.ts +74 -0
- package/src/reactive/readonly.ts +35 -0
- package/src/reactive/signal.ts +20 -520
- package/src/reactive/type-guards.ts +22 -0
- package/src/reactive/untrack.ts +31 -0
- package/src/reactive/watch.ts +73 -0
- package/src/router/index.ts +41 -718
- package/src/router/links.ts +130 -0
- package/src/router/match.ts +106 -0
- package/src/router/navigation.ts +71 -0
- package/src/router/query.ts +35 -0
- package/src/router/router.ts +211 -0
- package/src/router/state.ts +46 -0
- package/src/router/types.ts +93 -0
- package/src/router/utils.ts +116 -0
- package/src/security/constants.ts +209 -0
- package/src/security/csp.ts +77 -0
- package/src/security/index.ts +4 -12
- package/src/security/sanitize-core.ts +364 -0
- package/src/security/sanitize.ts +66 -625
- package/src/security/trusted-types.ts +69 -0
- package/src/security/types.ts +40 -0
- package/src/store/create-store.ts +329 -0
- package/src/store/define-store.ts +48 -0
- package/src/store/devtools.ts +45 -0
- package/src/store/index.ts +22 -848
- package/src/store/mapping.ts +73 -0
- package/src/store/persisted.ts +61 -0
- package/src/store/plugins.ts +32 -0
- package/src/store/registry.ts +51 -0
- package/src/store/types.ts +94 -0
- package/src/store/utils.ts +141 -0
- package/src/store/watch.ts +52 -0
- package/src/view/directives/bind.ts +23 -0
- package/src/view/directives/class.ts +70 -0
- package/src/view/directives/for.ts +275 -0
- package/src/view/directives/html.ts +19 -0
- package/src/view/directives/if.ts +30 -0
- package/src/view/directives/index.ts +11 -0
- package/src/view/directives/model.ts +56 -0
- package/src/view/directives/on.ts +41 -0
- package/src/view/directives/ref.ts +41 -0
- package/src/view/directives/show.ts +26 -0
- package/src/view/directives/style.ts +47 -0
- package/src/view/directives/text.ts +15 -0
- package/src/view/evaluate.ts +290 -0
- package/src/view/index.ts +112 -1041
- package/src/view/mount.ts +200 -0
- package/src/view/process.ts +92 -0
- package/src/view/types.ts +44 -0
- package/dist/core/utils.d.ts +0 -313
- package/dist/core/utils.d.ts.map +0 -1
- package/src/core/utils.ts +0 -444
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mapping helpers for store state and actions.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Maps store state properties to a reactive object for use in components.
|
|
7
|
+
*
|
|
8
|
+
* @param store - The store instance
|
|
9
|
+
* @param keys - State keys to map
|
|
10
|
+
* @returns Object with mapped properties
|
|
11
|
+
*/
|
|
12
|
+
export const mapState = <S extends Record<string, unknown>, K extends keyof S>(
|
|
13
|
+
store: S,
|
|
14
|
+
keys: K[]
|
|
15
|
+
): Pick<S, K> => {
|
|
16
|
+
const mapped = {} as Pick<S, K>;
|
|
17
|
+
|
|
18
|
+
for (const key of keys) {
|
|
19
|
+
Object.defineProperty(mapped, key, {
|
|
20
|
+
get: () => store[key],
|
|
21
|
+
enumerable: true,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return mapped;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Maps store getters to a reactive object for use in components.
|
|
30
|
+
*
|
|
31
|
+
* @param store - The store instance
|
|
32
|
+
* @param keys - Getter keys to map
|
|
33
|
+
* @returns Object with mapped getters
|
|
34
|
+
*/
|
|
35
|
+
export const mapGetters = <G extends Record<string, unknown>, K extends keyof G>(
|
|
36
|
+
store: G,
|
|
37
|
+
keys: K[]
|
|
38
|
+
): Pick<G, K> => {
|
|
39
|
+
const mapped = {} as Pick<G, K>;
|
|
40
|
+
|
|
41
|
+
for (const key of keys) {
|
|
42
|
+
Object.defineProperty(mapped, key, {
|
|
43
|
+
get: () => store[key],
|
|
44
|
+
enumerable: true,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return mapped;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Maps store actions to an object for easier destructuring.
|
|
53
|
+
*
|
|
54
|
+
* @param store - The store instance
|
|
55
|
+
* @param keys - Action keys to map
|
|
56
|
+
* @returns Object with mapped actions
|
|
57
|
+
*/
|
|
58
|
+
export const mapActions = <
|
|
59
|
+
A extends Record<string, (...args: unknown[]) => unknown>,
|
|
60
|
+
K extends keyof A,
|
|
61
|
+
>(
|
|
62
|
+
store: A,
|
|
63
|
+
keys: K[]
|
|
64
|
+
): Pick<A, K> => {
|
|
65
|
+
const mapped = {} as Pick<A, K>;
|
|
66
|
+
|
|
67
|
+
for (const key of keys) {
|
|
68
|
+
(mapped as Record<string, unknown>)[key as string] = (...args: unknown[]) =>
|
|
69
|
+
(store[key] as (...args: unknown[]) => unknown)(...args);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return mapped;
|
|
73
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store persistence helpers.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createStore } from './create-store';
|
|
6
|
+
import type { Store, StoreDefinition } from './types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a store with automatic persistence to localStorage.
|
|
10
|
+
*
|
|
11
|
+
* @param definition - Store definition
|
|
12
|
+
* @param storageKey - Optional custom storage key
|
|
13
|
+
* @returns The reactive store instance
|
|
14
|
+
*/
|
|
15
|
+
export const createPersistedStore = <
|
|
16
|
+
S extends Record<string, unknown>,
|
|
17
|
+
G extends Record<string, unknown> = Record<string, never>,
|
|
18
|
+
A extends Record<string, (...args: unknown[]) => unknown> = Record<string, never>,
|
|
19
|
+
>(
|
|
20
|
+
definition: StoreDefinition<S, G, A>,
|
|
21
|
+
storageKey?: string
|
|
22
|
+
): Store<S, G, A> => {
|
|
23
|
+
const key = storageKey ?? `bquery-store-${definition.id}`;
|
|
24
|
+
|
|
25
|
+
const originalStateFactory = definition.state;
|
|
26
|
+
|
|
27
|
+
const wrappedDefinition: StoreDefinition<S, G, A> = {
|
|
28
|
+
...definition,
|
|
29
|
+
state: () => {
|
|
30
|
+
const defaultState = originalStateFactory();
|
|
31
|
+
|
|
32
|
+
if (typeof window !== 'undefined') {
|
|
33
|
+
try {
|
|
34
|
+
const saved = localStorage.getItem(key);
|
|
35
|
+
if (saved) {
|
|
36
|
+
return { ...defaultState, ...JSON.parse(saved) } as S;
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
// Ignore parse errors
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return defaultState;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const store = createStore(wrappedDefinition);
|
|
48
|
+
|
|
49
|
+
// Subscribe to save changes
|
|
50
|
+
store.$subscribe((state) => {
|
|
51
|
+
if (typeof window !== 'undefined') {
|
|
52
|
+
try {
|
|
53
|
+
localStorage.setItem(key, JSON.stringify(state));
|
|
54
|
+
} catch {
|
|
55
|
+
// Ignore quota errors
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return store;
|
|
61
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store plugins API.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Store, StoreDefinition, StorePlugin } from './types';
|
|
6
|
+
|
|
7
|
+
/** @internal Registered plugins */
|
|
8
|
+
const plugins: StorePlugin[] = [];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Registers a plugin that extends all stores.
|
|
12
|
+
*
|
|
13
|
+
* @param plugin - The plugin function
|
|
14
|
+
*/
|
|
15
|
+
export const registerPlugin = (plugin: StorePlugin): void => {
|
|
16
|
+
plugins.push(plugin);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** @internal */
|
|
20
|
+
export const applyPlugins = (
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
store: Store<any, any, any>,
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
+
options: StoreDefinition<any, any, any>
|
|
25
|
+
): void => {
|
|
26
|
+
for (const plugin of plugins) {
|
|
27
|
+
const extension = plugin({ store, options });
|
|
28
|
+
if (extension) {
|
|
29
|
+
Object.assign(store, extension);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store registry utilities.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { unregisterDevtoolsStore } from './devtools';
|
|
6
|
+
import type { Store } from './types';
|
|
7
|
+
|
|
8
|
+
/** @internal Registry of all stores for devtools */
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
const storeRegistry = new Map<string, Store<any, any, any>>();
|
|
11
|
+
|
|
12
|
+
/** @internal */
|
|
13
|
+
export const hasStore = (id: string): boolean => storeRegistry.has(id);
|
|
14
|
+
|
|
15
|
+
/** @internal */
|
|
16
|
+
export const registerStore = (
|
|
17
|
+
id: string,
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
store: Store<any, any, any>
|
|
20
|
+
): void => {
|
|
21
|
+
storeRegistry.set(id, store);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Retrieves an existing store by its ID.
|
|
26
|
+
*
|
|
27
|
+
* @param id - The store identifier
|
|
28
|
+
* @returns The store instance or undefined if not found
|
|
29
|
+
*/
|
|
30
|
+
export const getStore = <T = unknown>(id: string): T | undefined => {
|
|
31
|
+
return storeRegistry.get(id) as T | undefined;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Lists all registered store IDs.
|
|
36
|
+
*
|
|
37
|
+
* @returns Array of store IDs
|
|
38
|
+
*/
|
|
39
|
+
export const listStores = (): string[] => {
|
|
40
|
+
return Array.from(storeRegistry.keys());
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Removes a store from the registry.
|
|
45
|
+
*
|
|
46
|
+
* @param id - The store identifier
|
|
47
|
+
*/
|
|
48
|
+
export const destroyStore = (id: string): void => {
|
|
49
|
+
storeRegistry.delete(id);
|
|
50
|
+
unregisterDevtoolsStore(id);
|
|
51
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store types for bQuery's state module.
|
|
3
|
+
* @module bquery/store
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Store state factory function.
|
|
8
|
+
*/
|
|
9
|
+
export type StateFactory<S> = () => S;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Getter definition - derives computed values from state.
|
|
13
|
+
*/
|
|
14
|
+
export type Getters<S, G> = {
|
|
15
|
+
[K in keyof G]: (state: S, getters: G) => G[K];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Action definition - methods that can modify state.
|
|
20
|
+
* The `this` context includes state, getters, and other actions.
|
|
21
|
+
*/
|
|
22
|
+
export type Actions<S, G, A> = {
|
|
23
|
+
[K in keyof A]: A[K] extends (...args: infer P) => infer R
|
|
24
|
+
? (this: S & G & A, ...args: P) => R
|
|
25
|
+
: never;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Store definition for createStore.
|
|
30
|
+
*/
|
|
31
|
+
export type StoreDefinition<
|
|
32
|
+
S extends Record<string, unknown> = Record<string, unknown>,
|
|
33
|
+
G extends Record<string, unknown> = Record<string, unknown>,
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
A extends Record<string, (...args: any[]) => any> = Record<string, never>,
|
|
36
|
+
> = {
|
|
37
|
+
/** Unique store identifier for devtools */
|
|
38
|
+
id: string;
|
|
39
|
+
/** State factory function */
|
|
40
|
+
state: StateFactory<S>;
|
|
41
|
+
/** Computed getters */
|
|
42
|
+
getters?: Getters<S, G>;
|
|
43
|
+
/** Action methods */
|
|
44
|
+
actions?: Actions<S, G, A>;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Store subscriber callback.
|
|
49
|
+
*/
|
|
50
|
+
export type StoreSubscriber<S> = (state: S) => void;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Patch payload for store updates.
|
|
54
|
+
*/
|
|
55
|
+
export type StorePatch<S> = Partial<S> | ((state: S) => void);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The returned store instance with state, getters, and actions merged.
|
|
59
|
+
*/
|
|
60
|
+
export type Store<
|
|
61
|
+
S extends Record<string, unknown>,
|
|
62
|
+
G extends Record<string, unknown>,
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
|
+
A extends Record<string, (...args: any[]) => any>,
|
|
65
|
+
> = S &
|
|
66
|
+
G &
|
|
67
|
+
A & {
|
|
68
|
+
/** Store identifier */
|
|
69
|
+
$id: string;
|
|
70
|
+
/** Reset state to initial values */
|
|
71
|
+
$reset: () => void;
|
|
72
|
+
/** Subscribe to state changes */
|
|
73
|
+
$subscribe: (callback: StoreSubscriber<S>) => () => void;
|
|
74
|
+
/** Patch multiple state properties at once (shallow) */
|
|
75
|
+
$patch: (partial: StorePatch<S>) => void;
|
|
76
|
+
/**
|
|
77
|
+
* Patch with deep reactivity support.
|
|
78
|
+
* Unlike $patch, this method deep-clones nested objects before mutation,
|
|
79
|
+
* ensuring that all changes trigger reactive updates.
|
|
80
|
+
*/
|
|
81
|
+
$patchDeep: (partial: StorePatch<S>) => void;
|
|
82
|
+
/** Get raw state object (non-reactive snapshot) */
|
|
83
|
+
$state: S;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Plugin that can extend store functionality.
|
|
88
|
+
*/
|
|
89
|
+
export type StorePlugin<S = unknown> = (context: {
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
|
+
store: Store<any, any, any>;
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
|
+
options: StoreDefinition<any, any, any>;
|
|
94
|
+
}) => Partial<S> | void;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal utilities for the store module.
|
|
3
|
+
* @internal
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if a value is a plain object (not array, null, Date, etc.).
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export const isPlainObject = (value: unknown): value is Record<string, unknown> => {
|
|
11
|
+
return (
|
|
12
|
+
value !== null && typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype
|
|
13
|
+
);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Deep clones an object. Used for deep reactivity support.
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
export const deepClone = <T>(obj: T): T => {
|
|
21
|
+
if (obj === null || typeof obj !== 'object') {
|
|
22
|
+
return obj;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (Array.isArray(obj)) {
|
|
26
|
+
return obj.map(deepClone) as T;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (obj instanceof Date) {
|
|
30
|
+
return new Date(obj.getTime()) as T;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (obj instanceof Map) {
|
|
34
|
+
return new Map(Array.from(obj.entries()).map(([k, v]) => [k, deepClone(v)])) as T;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (obj instanceof Set) {
|
|
38
|
+
return new Set(Array.from(obj).map(deepClone)) as T;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const cloned = {} as T;
|
|
42
|
+
for (const key of Object.keys(obj)) {
|
|
43
|
+
(cloned as Record<string, unknown>)[key] = deepClone((obj as Record<string, unknown>)[key]);
|
|
44
|
+
}
|
|
45
|
+
return cloned;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Compares two values for deep equality.
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
export const deepEqual = (a: unknown, b: unknown): boolean => {
|
|
53
|
+
if (a === b) return true;
|
|
54
|
+
if (a === null || b === null) return false;
|
|
55
|
+
if (typeof a !== 'object' || typeof b !== 'object') return false;
|
|
56
|
+
|
|
57
|
+
if (a instanceof Date && b instanceof Date) {
|
|
58
|
+
return a.getTime() === b.getTime();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (a instanceof Map && b instanceof Map) {
|
|
62
|
+
if (a.size !== b.size) return false;
|
|
63
|
+
for (const [key, value] of a.entries()) {
|
|
64
|
+
if (!b.has(key) || !deepEqual(value, b.get(key))) return false;
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (a instanceof Set && b instanceof Set) {
|
|
70
|
+
if (a.size !== b.size) return false;
|
|
71
|
+
for (const value of a.values()) {
|
|
72
|
+
let found = false;
|
|
73
|
+
for (const candidate of b.values()) {
|
|
74
|
+
if (deepEqual(value, candidate)) {
|
|
75
|
+
found = true;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!found) return false;
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
85
|
+
if (a.length !== b.length) return false;
|
|
86
|
+
return a.every((item, i) => deepEqual(item, b[i]));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
90
|
+
|
|
91
|
+
const keysA = Object.keys(a as object);
|
|
92
|
+
const keysB = Object.keys(b as object);
|
|
93
|
+
|
|
94
|
+
if (keysA.length !== keysB.length) return false;
|
|
95
|
+
|
|
96
|
+
return keysA.every((key) =>
|
|
97
|
+
deepEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Detects if nested objects were mutated but the reference stayed the same.
|
|
103
|
+
* Returns the keys where nested mutations were detected.
|
|
104
|
+
* @internal
|
|
105
|
+
*/
|
|
106
|
+
export const detectNestedMutations = <S extends Record<string, unknown>>(
|
|
107
|
+
before: S,
|
|
108
|
+
after: S,
|
|
109
|
+
signalValues: Map<keyof S, unknown>
|
|
110
|
+
): Array<keyof S> => {
|
|
111
|
+
const mutatedKeys: Array<keyof S> = [];
|
|
112
|
+
|
|
113
|
+
for (const key of Object.keys(after) as Array<keyof S>) {
|
|
114
|
+
const beforeValue = before[key];
|
|
115
|
+
const afterValue = after[key];
|
|
116
|
+
const signalValue = signalValues.get(key);
|
|
117
|
+
|
|
118
|
+
// Check if it's the same reference but content changed
|
|
119
|
+
if (
|
|
120
|
+
signalValue === afterValue &&
|
|
121
|
+
isPlainObject(beforeValue) &&
|
|
122
|
+
isPlainObject(afterValue) &&
|
|
123
|
+
!deepEqual(beforeValue, afterValue)
|
|
124
|
+
) {
|
|
125
|
+
mutatedKeys.push(key);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return mutatedKeys;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/** @internal Flag to enable/disable development warnings */
|
|
133
|
+
export const isDev = (() => {
|
|
134
|
+
try {
|
|
135
|
+
const globalProcess = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process;
|
|
136
|
+
// Default to dev mode unless explicitly set to production
|
|
137
|
+
return !(typeof globalProcess !== 'undefined' && globalProcess.env?.NODE_ENV === 'production');
|
|
138
|
+
} catch {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
})();
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store watch helpers.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Store } from './types';
|
|
6
|
+
import { deepEqual } from './utils';
|
|
7
|
+
|
|
8
|
+
export type WatchStoreOptions<T> = {
|
|
9
|
+
/** Call the callback immediately with the current value. */
|
|
10
|
+
immediate?: boolean;
|
|
11
|
+
/** Use deep comparison when determining changes. */
|
|
12
|
+
deep?: boolean;
|
|
13
|
+
/** Custom equality check for selected values. */
|
|
14
|
+
equals?: (a: T, b: T) => boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Watch a selected slice of store state.
|
|
19
|
+
*
|
|
20
|
+
* @param store - The store instance
|
|
21
|
+
* @param selector - Function to select the watched value
|
|
22
|
+
* @param callback - Called when the selected value changes
|
|
23
|
+
* @param options - Watch options
|
|
24
|
+
* @returns Unsubscribe function
|
|
25
|
+
*/
|
|
26
|
+
export const watchStore = <
|
|
27
|
+
S extends Record<string, unknown>,
|
|
28
|
+
G extends Record<string, unknown>,
|
|
29
|
+
A extends Record<string, (...args: unknown[]) => unknown>,
|
|
30
|
+
T,
|
|
31
|
+
>(
|
|
32
|
+
store: Store<S, G, A>,
|
|
33
|
+
selector: (state: S) => T,
|
|
34
|
+
callback: (value: T, previous: T | undefined) => void,
|
|
35
|
+
options: WatchStoreOptions<T> = {}
|
|
36
|
+
): (() => void) => {
|
|
37
|
+
const equals = options.equals ?? (options.deep ? deepEqual : Object.is);
|
|
38
|
+
let previous = selector(store.$state);
|
|
39
|
+
|
|
40
|
+
if (options.immediate) {
|
|
41
|
+
callback(previous, undefined);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return store.$subscribe((state) => {
|
|
45
|
+
const current = selector(state);
|
|
46
|
+
if (!equals(current, previous)) {
|
|
47
|
+
const prev = previous;
|
|
48
|
+
previous = current;
|
|
49
|
+
callback(current, prev);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { effect } from '../../reactive/index';
|
|
2
|
+
import { evaluate } from '../evaluate';
|
|
3
|
+
import type { DirectiveHandler } from '../types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handles bq-bind:attr directive - attribute binding.
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export const handleBind = (attrName: string): DirectiveHandler => {
|
|
10
|
+
return (el, expression, context, cleanups) => {
|
|
11
|
+
const cleanup = effect(() => {
|
|
12
|
+
const value = evaluate(expression, context);
|
|
13
|
+
if (value == null || value === false) {
|
|
14
|
+
el.removeAttribute(attrName);
|
|
15
|
+
} else if (value === true) {
|
|
16
|
+
el.setAttribute(attrName, '');
|
|
17
|
+
} else {
|
|
18
|
+
el.setAttribute(attrName, String(value));
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
cleanups.push(cleanup);
|
|
22
|
+
};
|
|
23
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { effect } from '../../reactive/index';
|
|
2
|
+
import { evaluate, parseObjectExpression } from '../evaluate';
|
|
3
|
+
import type { DirectiveHandler } from '../types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handles bq-class directive - dynamic class binding.
|
|
7
|
+
* Tracks previously added classes to ensure proper cleanup when expressions change.
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export const handleClass: DirectiveHandler = (el, expression, context, cleanups) => {
|
|
11
|
+
// Track classes added by this directive to clean them up on re-evaluation
|
|
12
|
+
let previousClasses: Set<string> = new Set();
|
|
13
|
+
|
|
14
|
+
const cleanup = effect(() => {
|
|
15
|
+
const newClasses: Set<string> = new Set();
|
|
16
|
+
|
|
17
|
+
if (expression.trimStart().startsWith('{')) {
|
|
18
|
+
// Object syntax: { active: isActive, disabled: !enabled }
|
|
19
|
+
const classMap = parseObjectExpression(expression);
|
|
20
|
+
for (const [className, conditionExpr] of Object.entries(classMap)) {
|
|
21
|
+
const condition = evaluate<boolean>(conditionExpr, context);
|
|
22
|
+
el.classList.toggle(className, Boolean(condition));
|
|
23
|
+
// Track class regardless of condition - toggle handles add/remove
|
|
24
|
+
newClasses.add(className);
|
|
25
|
+
}
|
|
26
|
+
} else if (/^\s*\[/.test(expression)) {
|
|
27
|
+
// Array literal syntax: [class1, class2]
|
|
28
|
+
const classes = evaluate<string[]>(expression, context);
|
|
29
|
+
if (Array.isArray(classes)) {
|
|
30
|
+
for (const cls of classes) {
|
|
31
|
+
if (cls) {
|
|
32
|
+
el.classList.add(cls);
|
|
33
|
+
newClasses.add(cls);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
// Single expression returning string or array
|
|
39
|
+
const result = evaluate<string | string[]>(expression, context);
|
|
40
|
+
if (typeof result === 'string') {
|
|
41
|
+
result.split(/\s+/).forEach((cls) => {
|
|
42
|
+
if (cls) {
|
|
43
|
+
el.classList.add(cls);
|
|
44
|
+
newClasses.add(cls);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
} else if (Array.isArray(result)) {
|
|
48
|
+
result.forEach((cls) => {
|
|
49
|
+
if (cls) {
|
|
50
|
+
el.classList.add(cls);
|
|
51
|
+
newClasses.add(cls);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Remove classes that were previously added but are no longer in the new set
|
|
58
|
+
// This keeps directive-managed classes in sync across all syntax forms and provides
|
|
59
|
+
// defensive cleanup behavior for edge cases (e.g. external classList changes)
|
|
60
|
+
for (const cls of previousClasses) {
|
|
61
|
+
if (!newClasses.has(cls)) {
|
|
62
|
+
el.classList.remove(cls);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
previousClasses = newClasses;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
cleanups.push(cleanup);
|
|
70
|
+
};
|