@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,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive effects.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { CleanupFn, Observer, track, clearDependencies } from './internals';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates a side effect that automatically re-runs when dependencies change.
|
|
9
|
+
*
|
|
10
|
+
* The effect runs immediately upon creation and then re-runs whenever
|
|
11
|
+
* any signal or computed value read inside it changes.
|
|
12
|
+
*
|
|
13
|
+
* @param fn - The effect function to run
|
|
14
|
+
* @returns A cleanup function to stop the effect
|
|
15
|
+
*/
|
|
16
|
+
export const effect = (fn: () => void | CleanupFn): CleanupFn => {
|
|
17
|
+
let cleanupFn: CleanupFn | void;
|
|
18
|
+
let isDisposed = false;
|
|
19
|
+
|
|
20
|
+
const runCleanup = (): void => {
|
|
21
|
+
if (cleanupFn) {
|
|
22
|
+
try {
|
|
23
|
+
cleanupFn();
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error('bQuery reactive: Error in effect cleanup', error);
|
|
26
|
+
}
|
|
27
|
+
cleanupFn = undefined;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const observer: Observer = () => {
|
|
32
|
+
if (isDisposed) return;
|
|
33
|
+
|
|
34
|
+
runCleanup();
|
|
35
|
+
|
|
36
|
+
// Clear old dependencies before running to avoid stale subscriptions
|
|
37
|
+
clearDependencies(observer);
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
cleanupFn = track(observer, fn);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error('bQuery reactive: Error in effect', error);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
observer();
|
|
47
|
+
|
|
48
|
+
return () => {
|
|
49
|
+
isDisposed = true;
|
|
50
|
+
runCleanup();
|
|
51
|
+
// Clean up all dependencies when effect is disposed
|
|
52
|
+
clearDependencies(observer);
|
|
53
|
+
};
|
|
54
|
+
};
|
package/src/reactive/index.ts
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reactive module providing fine-grained reactivity primitives.
|
|
3
|
-
*
|
|
4
|
-
* @module bquery/reactive
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export {
|
|
8
|
-
Computed,
|
|
9
|
-
Signal,
|
|
10
|
-
batch,
|
|
11
|
-
computed,
|
|
12
|
-
effect,
|
|
13
|
-
isComputed,
|
|
14
|
-
isSignal,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Reactive module providing fine-grained reactivity primitives.
|
|
3
|
+
*
|
|
4
|
+
* @module bquery/reactive
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
Computed,
|
|
9
|
+
Signal,
|
|
10
|
+
batch,
|
|
11
|
+
computed,
|
|
12
|
+
effect,
|
|
13
|
+
isComputed,
|
|
14
|
+
isSignal,
|
|
15
|
+
linkedSignal,
|
|
16
|
+
persistedSignal,
|
|
17
|
+
readonly,
|
|
18
|
+
signal,
|
|
19
|
+
untrack,
|
|
20
|
+
watch,
|
|
21
|
+
} from './signal';
|
|
22
|
+
|
|
23
|
+
export type { CleanupFn, LinkedSignal, Observer, ReadonlySignal } from './signal';
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal reactive plumbing shared across primitives.
|
|
3
|
+
* @internal
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type Observer = () => void;
|
|
7
|
+
export type CleanupFn = () => void;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Interface for reactive sources (Signals, Computed) that can unsubscribe observers.
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
export interface ReactiveSource {
|
|
14
|
+
unsubscribe(observer: Observer): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const observerStack: Observer[] = [];
|
|
18
|
+
let batchDepth = 0;
|
|
19
|
+
const pendingObservers = new Set<Observer>();
|
|
20
|
+
|
|
21
|
+
// Track dependencies for each observer to enable cleanup
|
|
22
|
+
const observerDependencies = new WeakMap<Observer, Set<ReactiveSource>>();
|
|
23
|
+
|
|
24
|
+
export const track = <T>(observer: Observer, fn: () => T): T => {
|
|
25
|
+
observerStack.push(observer);
|
|
26
|
+
try {
|
|
27
|
+
return fn();
|
|
28
|
+
} finally {
|
|
29
|
+
observerStack.pop();
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const getCurrentObserver = (): Observer | undefined =>
|
|
34
|
+
observerStack[observerStack.length - 1];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Executes a function without exposing the current observer to dependencies.
|
|
38
|
+
* Unlike disabling tracking globally, this still allows nested reactive internals
|
|
39
|
+
* (e.g., computed recomputation) to track their own dependencies.
|
|
40
|
+
* @internal
|
|
41
|
+
*/
|
|
42
|
+
export const withoutCurrentObserver = <T>(fn: () => T): T => {
|
|
43
|
+
// Push undefined to temporarily "hide" the current observer
|
|
44
|
+
// This way, Signal.value reads won't link to the previous observer,
|
|
45
|
+
// but nested track() calls (e.g., computed recompute) still work normally.
|
|
46
|
+
observerStack.push(undefined as unknown as Observer);
|
|
47
|
+
try {
|
|
48
|
+
return fn();
|
|
49
|
+
} finally {
|
|
50
|
+
observerStack.pop();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const scheduleObserver = (observer: Observer): void => {
|
|
55
|
+
if (batchDepth > 0) {
|
|
56
|
+
pendingObservers.add(observer);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
observer();
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const flushObservers = (): void => {
|
|
63
|
+
for (const observer of Array.from(pendingObservers)) {
|
|
64
|
+
pendingObservers.delete(observer);
|
|
65
|
+
try {
|
|
66
|
+
observer();
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error('bQuery reactive: Error in observer during batch flush', error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const beginBatch = (): void => {
|
|
74
|
+
batchDepth += 1;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const endBatch = (): void => {
|
|
78
|
+
if (batchDepth <= 0) return;
|
|
79
|
+
batchDepth -= 1;
|
|
80
|
+
if (batchDepth === 0) {
|
|
81
|
+
flushObservers();
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Registers a dependency between an observer and a reactive source.
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
89
|
+
export const registerDependency = (observer: Observer, source: ReactiveSource): void => {
|
|
90
|
+
let deps = observerDependencies.get(observer);
|
|
91
|
+
if (!deps) {
|
|
92
|
+
deps = new Set();
|
|
93
|
+
observerDependencies.set(observer, deps);
|
|
94
|
+
}
|
|
95
|
+
deps.add(source);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Removes a specific source from an observer's dependency set.
|
|
100
|
+
* Used when a source (e.g. Signal) is disposed to prevent stale references.
|
|
101
|
+
* @internal
|
|
102
|
+
*/
|
|
103
|
+
export const removeDependency = (observer: Observer, source: ReactiveSource): void => {
|
|
104
|
+
const deps = observerDependencies.get(observer);
|
|
105
|
+
if (deps) {
|
|
106
|
+
deps.delete(source);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Clears all dependencies for an observer, unsubscribing from all sources.
|
|
112
|
+
* @internal
|
|
113
|
+
*/
|
|
114
|
+
export const clearDependencies = (observer: Observer): void => {
|
|
115
|
+
const deps = observerDependencies.get(observer);
|
|
116
|
+
if (deps) {
|
|
117
|
+
for (const source of deps) {
|
|
118
|
+
source.unsubscribe(observer);
|
|
119
|
+
}
|
|
120
|
+
deps.clear();
|
|
121
|
+
}
|
|
122
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linked (writable) computed helpers.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { computed, Computed } from './computed';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A writable computed-like signal.
|
|
9
|
+
*/
|
|
10
|
+
export interface LinkedSignal<T> {
|
|
11
|
+
/** Gets or sets the current value with dependency tracking. */
|
|
12
|
+
value: T;
|
|
13
|
+
/** Gets the current value without dependency tracking. */
|
|
14
|
+
peek(): T;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates a writable computed signal by linking a getter and setter.
|
|
19
|
+
*
|
|
20
|
+
* @template T - The derived value type
|
|
21
|
+
* @param getValue - Getter that derives the current value
|
|
22
|
+
* @param setValue - Setter that writes back to underlying signals
|
|
23
|
+
* @returns A writable computed-like signal
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const first = signal('Ada');
|
|
28
|
+
* const last = signal('Lovelace');
|
|
29
|
+
* const fullName = linkedSignal(
|
|
30
|
+
* () => `${first.value} ${last.value}`,
|
|
31
|
+
* (next) => {
|
|
32
|
+
* const [a, b] = next.split(' ');
|
|
33
|
+
* first.value = a ?? '';
|
|
34
|
+
* last.value = b ?? '';
|
|
35
|
+
* }
|
|
36
|
+
* );
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export const linkedSignal = <T>(
|
|
40
|
+
getValue: () => T,
|
|
41
|
+
setValue: (value: T) => void
|
|
42
|
+
): LinkedSignal<T> => {
|
|
43
|
+
const derived: Computed<T> = computed(getValue);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
get value(): T {
|
|
47
|
+
return derived.value;
|
|
48
|
+
},
|
|
49
|
+
set value(next: T) {
|
|
50
|
+
setValue(next);
|
|
51
|
+
},
|
|
52
|
+
peek(): T {
|
|
53
|
+
return derived.peek();
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocalStorage-backed signals.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { signal, Signal } from './core';
|
|
6
|
+
import { effect } from './effect';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a signal that persists to localStorage.
|
|
10
|
+
*
|
|
11
|
+
* @template T - The type of the signal value
|
|
12
|
+
* @param key - The localStorage key
|
|
13
|
+
* @param initialValue - The initial value if not found in storage
|
|
14
|
+
* @returns A Signal that syncs with localStorage (falls back to in-memory if unavailable)
|
|
15
|
+
*/
|
|
16
|
+
export const persistedSignal = <T>(key: string, initialValue: T): Signal<T> => {
|
|
17
|
+
// Check if localStorage is available and accessible
|
|
18
|
+
let hasLocalStorage = false;
|
|
19
|
+
let storage: Storage | null = null;
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// In Safari private mode, accessing localStorage can throw SecurityError
|
|
23
|
+
storage = globalThis.localStorage;
|
|
24
|
+
if (storage) {
|
|
25
|
+
// Test actual access to ensure it's not just present but usable
|
|
26
|
+
// Use a randomized test key to avoid overwriting real user data
|
|
27
|
+
const testKey = `__bquery_test_${Math.random().toString(36).slice(2, 9)}__`;
|
|
28
|
+
const testValue = '__test__';
|
|
29
|
+
try {
|
|
30
|
+
storage.setItem(testKey, testValue);
|
|
31
|
+
storage.getItem(testKey);
|
|
32
|
+
hasLocalStorage = true;
|
|
33
|
+
} finally {
|
|
34
|
+
// Ensure we don't leave any test data behind
|
|
35
|
+
try {
|
|
36
|
+
storage.removeItem(testKey);
|
|
37
|
+
} catch {
|
|
38
|
+
// Ignore cleanup errors (e.g., storage becoming unavailable)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
// localStorage unavailable or access denied (Safari private mode, sandboxed iframes, etc.)
|
|
44
|
+
hasLocalStorage = false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let stored: T = initialValue;
|
|
48
|
+
|
|
49
|
+
if (hasLocalStorage && storage) {
|
|
50
|
+
try {
|
|
51
|
+
const raw = storage.getItem(key);
|
|
52
|
+
if (raw !== null) {
|
|
53
|
+
stored = JSON.parse(raw) as T;
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
// Use initial value on parse error or access denial
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const sig = signal(stored);
|
|
61
|
+
|
|
62
|
+
// Only set up persistence effect if localStorage is available
|
|
63
|
+
if (hasLocalStorage && storage) {
|
|
64
|
+
effect(() => {
|
|
65
|
+
try {
|
|
66
|
+
storage!.setItem(key, JSON.stringify(sig.value));
|
|
67
|
+
} catch {
|
|
68
|
+
// Ignore storage errors (quota exceeded, sandboxed iframes, etc.)
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return sig;
|
|
74
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only signal wrappers.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Signal } from './core';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A readonly wrapper around a signal that prevents writes.
|
|
9
|
+
* Provides read-only access to a signal's value while maintaining reactivity.
|
|
10
|
+
*
|
|
11
|
+
* @template T - The type of the wrapped value
|
|
12
|
+
*/
|
|
13
|
+
export interface ReadonlySignal<T> {
|
|
14
|
+
/** Gets the current value with dependency tracking. */
|
|
15
|
+
readonly value: T;
|
|
16
|
+
/** Gets the current value without dependency tracking. */
|
|
17
|
+
peek(): T;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Creates a read-only view of a signal.
|
|
22
|
+
* Useful for exposing reactive state without allowing modifications.
|
|
23
|
+
*
|
|
24
|
+
* @template T - The type of the signal value
|
|
25
|
+
* @param sig - The signal to wrap
|
|
26
|
+
* @returns A readonly signal wrapper
|
|
27
|
+
*/
|
|
28
|
+
export const readonly = <T>(sig: Signal<T>): ReadonlySignal<T> => ({
|
|
29
|
+
get value(): T {
|
|
30
|
+
return sig.value;
|
|
31
|
+
},
|
|
32
|
+
peek(): T {
|
|
33
|
+
return sig.peek();
|
|
34
|
+
},
|
|
35
|
+
});
|