@bquery/bquery 1.1.1 → 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 +113 -6
- package/dist/full.d.ts +6 -0
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +58 -35
- package/dist/full.es.mjs.map +1 -1
- package/dist/full.iife.js +6 -1
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +6 -1
- package/dist/full.umd.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.mjs +58 -35
- package/dist/index.es.mjs.map +1 -1
- package/dist/reactive/signal.d.ts +7 -0
- package/dist/reactive/signal.d.ts.map +1 -1
- package/dist/reactive.es.mjs +34 -25
- package/dist/reactive.es.mjs.map +1 -1
- package/dist/router/index.d.ts +287 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router.es.mjs +166 -0
- package/dist/router.es.mjs.map +1 -0
- package/dist/security/sanitize.d.ts.map +1 -1
- package/dist/security.es.mjs +95 -74
- package/dist/security.es.mjs.map +1 -1
- package/dist/store/index.d.ts +288 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store.es.mjs +229 -0
- package/dist/store.es.mjs.map +1 -0
- package/dist/view/index.d.ts +201 -0
- package/dist/view/index.d.ts.map +1 -0
- package/dist/view.es.mjs +325 -0
- package/dist/view.es.mjs.map +1 -0
- package/package.json +132 -120
- package/src/full.ts +44 -0
- package/src/index.ts +9 -0
- package/src/reactive/signal.ts +14 -0
- package/src/router/index.ts +718 -0
- package/src/security/sanitize.ts +72 -0
- package/src/store/index.ts +848 -0
- package/src/view/index.ts +1041 -0
package/dist/index.d.ts
CHANGED
|
@@ -13,4 +13,7 @@ export * from './component/index';
|
|
|
13
13
|
export * from './motion/index';
|
|
14
14
|
export * from './security/index';
|
|
15
15
|
export * from './platform/index';
|
|
16
|
+
export * from './router/index';
|
|
17
|
+
export * from './store/index';
|
|
18
|
+
export * from './view/index';
|
|
16
19
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,cAAc,cAAc,CAAC;AAG7B,cAAc,kBAAkB,CAAC;AAGjC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,kBAAkB,CAAC;AAGjC,cAAc,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,cAAc,cAAc,CAAC;AAG7B,cAAc,kBAAkB,CAAC;AAGjC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,kBAAkB,CAAC;AAGjC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,eAAe,CAAC;AAG9B,cAAc,cAAc,CAAC"}
|
package/dist/index.es.mjs
CHANGED
|
@@ -1,48 +1,71 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import { Computed as p, Signal as c, batch as l, computed as m, effect as u, isComputed as f, isSignal as g, persistedSignal as d, readonly as
|
|
3
|
-
import { component as
|
|
4
|
-
import { capturePosition as
|
|
5
|
-
import { createTrustedHtml as
|
|
6
|
-
import { buckets as
|
|
1
|
+
import { $ as r, $$ as o, BQueryCollection as i, BQueryElement as s, utils as a } from "./core.es.mjs";
|
|
2
|
+
import { Computed as p, Signal as c, batch as l, computed as m, effect as u, isComputed as f, isSignal as g, persistedSignal as d, readonly as S, signal as x, untrack as y, watch as T } from "./reactive.es.mjs";
|
|
3
|
+
import { component as h, html as k, safeHtml as v } from "./component.es.mjs";
|
|
4
|
+
import { capturePosition as H, flip as b, flipList as z, spring as A, springPresets as $, transition as w } from "./motion.es.mjs";
|
|
5
|
+
import { createTrustedHtml as L, escapeHtml as Q, generateNonce as R, getTrustedTypesPolicy as D, hasCSPDirective as E, isTrustedTypesSupported as N, sanitize as j, sanitize as q, stripTags as F } from "./security.es.mjs";
|
|
6
|
+
import { buckets as I, cache as J, notifications as K, storage as M } from "./platform.es.mjs";
|
|
7
|
+
import { back as U, createRouter as V, currentRoute as W, forward as X, interceptLinks as Y, isActive as Z, isActiveSignal as _, link as ee, navigate as te, resolve as re } from "./router.es.mjs";
|
|
8
|
+
import { createPersistedStore as ie, createStore as se, destroyStore as ae, getStore as ne, listStores as pe, mapActions as ce, mapState as le, registerPlugin as me } from "./store.es.mjs";
|
|
9
|
+
import { createTemplate as fe, mount as ge } from "./view.es.mjs";
|
|
7
10
|
export {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
r as $,
|
|
12
|
+
o as $$,
|
|
13
|
+
i as BQueryCollection,
|
|
14
|
+
s as BQueryElement,
|
|
12
15
|
p as Computed,
|
|
13
16
|
c as Signal,
|
|
17
|
+
U as back,
|
|
14
18
|
l as batch,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
I as buckets,
|
|
20
|
+
J as cache,
|
|
21
|
+
H as capturePosition,
|
|
22
|
+
h as component,
|
|
19
23
|
m as computed,
|
|
20
|
-
|
|
24
|
+
ie as createPersistedStore,
|
|
25
|
+
V as createRouter,
|
|
26
|
+
se as createStore,
|
|
27
|
+
fe as createTemplate,
|
|
28
|
+
L as createTrustedHtml,
|
|
29
|
+
W as currentRoute,
|
|
30
|
+
ae as destroyStore,
|
|
21
31
|
u as effect,
|
|
22
|
-
|
|
32
|
+
Q as escapeHtml,
|
|
23
33
|
b as flip,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
z as flipList,
|
|
35
|
+
X as forward,
|
|
36
|
+
R as generateNonce,
|
|
37
|
+
ne as getStore,
|
|
38
|
+
D as getTrustedTypesPolicy,
|
|
39
|
+
E as hasCSPDirective,
|
|
40
|
+
k as html,
|
|
41
|
+
Y as interceptLinks,
|
|
42
|
+
Z as isActive,
|
|
43
|
+
_ as isActiveSignal,
|
|
29
44
|
f as isComputed,
|
|
30
45
|
g as isSignal,
|
|
31
|
-
|
|
32
|
-
|
|
46
|
+
N as isTrustedTypesSupported,
|
|
47
|
+
ee as link,
|
|
48
|
+
pe as listStores,
|
|
49
|
+
ce as mapActions,
|
|
50
|
+
le as mapState,
|
|
51
|
+
ge as mount,
|
|
52
|
+
te as navigate,
|
|
53
|
+
K as notifications,
|
|
33
54
|
d as persistedSignal,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
S as readonly,
|
|
56
|
+
me as registerPlugin,
|
|
57
|
+
re as resolve,
|
|
58
|
+
v as safeHtml,
|
|
59
|
+
j as sanitize,
|
|
60
|
+
q as sanitizeHtml,
|
|
61
|
+
x as signal,
|
|
62
|
+
A as spring,
|
|
63
|
+
$ as springPresets,
|
|
64
|
+
M as storage,
|
|
65
|
+
F as stripTags,
|
|
66
|
+
w as transition,
|
|
67
|
+
y as untrack,
|
|
45
68
|
a as utils,
|
|
46
|
-
|
|
69
|
+
T as watch
|
|
47
70
|
};
|
|
48
71
|
//# sourceMappingURL=index.es.mjs.map
|
package/dist/index.es.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.es.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.es.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
|
|
@@ -118,6 +118,13 @@ export declare class Computed<T> {
|
|
|
118
118
|
* Gets the computed value, recomputing if dependencies changed.
|
|
119
119
|
*/
|
|
120
120
|
get value(): T;
|
|
121
|
+
/**
|
|
122
|
+
* Reads the current computed value without tracking.
|
|
123
|
+
* Useful when you need the value but don't want to create a dependency.
|
|
124
|
+
*
|
|
125
|
+
* @returns The current cached value (recomputes if dirty)
|
|
126
|
+
*/
|
|
127
|
+
peek(): T;
|
|
121
128
|
}
|
|
122
129
|
/**
|
|
123
130
|
* Creates a new reactive signal.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../../src/reactive/signal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC;AAElC;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC;AA+CnC;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,MAAM,CAAC,CAAC;IAOP,OAAO,CAAC,MAAM;IAN1B,OAAO,CAAC,WAAW,CAAuB;IAE1C;;;OAGG;gBACiB,MAAM,EAAE,CAAC;IAE7B;;;OAGG;IACH,IAAI,KAAK,IAAI,CAAC,CAQb;IAED;;;OAGG;IACH,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,EAMhB;IAED;;;;;OAKG;IACH,IAAI,IAAI,CAAC;IAIT;;;;;OAKG;IACH,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI;CAGzC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,QAAQ,CAAC,CAAC;IAeT,OAAO,CAAC,QAAQ,CAAC,OAAO;IAdpC,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAKxB;IAEF;;;OAGG;gBAC0B,OAAO,EAAE,MAAM,CAAC;IAE7C;;OAEG;IACH,IAAI,KAAK,IAAI,CAAC,CAUb;
|
|
1
|
+
{"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../../src/reactive/signal.ts"],"names":[],"mappings":"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"}
|
package/dist/reactive.es.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const i = [];
|
|
2
|
-
let
|
|
3
|
-
const
|
|
4
|
-
let
|
|
5
|
-
const
|
|
2
|
+
let a = 0;
|
|
3
|
+
const l = /* @__PURE__ */ new Set();
|
|
4
|
+
let u = !0;
|
|
5
|
+
const o = (t, e) => {
|
|
6
6
|
i.push(t);
|
|
7
7
|
try {
|
|
8
8
|
return e();
|
|
@@ -10,14 +10,14 @@ const l = (t, e) => {
|
|
|
10
10
|
i.pop();
|
|
11
11
|
}
|
|
12
12
|
}, h = (t) => {
|
|
13
|
-
if (
|
|
14
|
-
|
|
13
|
+
if (a > 0) {
|
|
14
|
+
l.add(t);
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
17
|
t();
|
|
18
|
-
},
|
|
19
|
-
for (const t of Array.from(
|
|
20
|
-
|
|
18
|
+
}, p = () => {
|
|
19
|
+
for (const t of Array.from(l))
|
|
20
|
+
l.delete(t), t();
|
|
21
21
|
};
|
|
22
22
|
class f {
|
|
23
23
|
/**
|
|
@@ -32,7 +32,7 @@ class f {
|
|
|
32
32
|
* Respects the global tracking state (disabled during untrack calls).
|
|
33
33
|
*/
|
|
34
34
|
get value() {
|
|
35
|
-
if (
|
|
35
|
+
if (u) {
|
|
36
36
|
const e = i[i.length - 1];
|
|
37
37
|
e && this.subscribers.add(e);
|
|
38
38
|
}
|
|
@@ -85,23 +85,32 @@ class d {
|
|
|
85
85
|
*/
|
|
86
86
|
get value() {
|
|
87
87
|
const e = i[i.length - 1];
|
|
88
|
-
return e && this.subscribers.add(e), this.dirty && (this.dirty = !1, this.cachedValue =
|
|
88
|
+
return e && this.subscribers.add(e), this.dirty && (this.dirty = !1, this.cachedValue = o(this.markDirty, this.compute)), this.cachedValue;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Reads the current computed value without tracking.
|
|
92
|
+
* Useful when you need the value but don't want to create a dependency.
|
|
93
|
+
*
|
|
94
|
+
* @returns The current cached value (recomputes if dirty)
|
|
95
|
+
*/
|
|
96
|
+
peek() {
|
|
97
|
+
return this.dirty && (this.dirty = !1, this.cachedValue = o(this.markDirty, this.compute)), this.cachedValue;
|
|
89
98
|
}
|
|
90
99
|
}
|
|
91
|
-
const
|
|
100
|
+
const v = (t) => new f(t), y = (t) => new d(t), b = (t) => {
|
|
92
101
|
let e, s = !1;
|
|
93
102
|
const r = () => {
|
|
94
|
-
s || (e && e(), e =
|
|
103
|
+
s || (e && e(), e = o(r, t));
|
|
95
104
|
};
|
|
96
105
|
return r(), () => {
|
|
97
106
|
s = !0, e && e();
|
|
98
107
|
};
|
|
99
|
-
},
|
|
100
|
-
|
|
108
|
+
}, g = (t) => {
|
|
109
|
+
a += 1;
|
|
101
110
|
try {
|
|
102
111
|
t();
|
|
103
112
|
} finally {
|
|
104
|
-
|
|
113
|
+
a -= 1, a === 0 && p();
|
|
105
114
|
}
|
|
106
115
|
}, m = (t, e) => {
|
|
107
116
|
let s = e;
|
|
@@ -110,7 +119,7 @@ const p = (t) => new f(t), g = (t) => new d(t), b = (t) => {
|
|
|
110
119
|
n !== null && (s = JSON.parse(n));
|
|
111
120
|
} catch {
|
|
112
121
|
}
|
|
113
|
-
const r =
|
|
122
|
+
const r = v(s);
|
|
114
123
|
return b(() => {
|
|
115
124
|
try {
|
|
116
125
|
localStorage.setItem(t, JSON.stringify(r.value));
|
|
@@ -135,25 +144,25 @@ const p = (t) => new f(t), g = (t) => new d(t), b = (t) => {
|
|
|
135
144
|
e(c, r), r = c;
|
|
136
145
|
});
|
|
137
146
|
}, w = (t) => {
|
|
138
|
-
const e =
|
|
139
|
-
|
|
147
|
+
const e = u;
|
|
148
|
+
u = !1;
|
|
140
149
|
try {
|
|
141
150
|
return t();
|
|
142
151
|
} finally {
|
|
143
|
-
|
|
152
|
+
u = e;
|
|
144
153
|
}
|
|
145
|
-
}, O = (t) => t instanceof f,
|
|
154
|
+
}, O = (t) => t instanceof f, V = (t) => t instanceof d;
|
|
146
155
|
export {
|
|
147
156
|
d as Computed,
|
|
148
157
|
f as Signal,
|
|
149
|
-
|
|
150
|
-
|
|
158
|
+
g as batch,
|
|
159
|
+
y as computed,
|
|
151
160
|
b as effect,
|
|
152
|
-
|
|
161
|
+
V as isComputed,
|
|
153
162
|
O as isSignal,
|
|
154
163
|
m as persistedSignal,
|
|
155
164
|
S as readonly,
|
|
156
|
-
|
|
165
|
+
v as signal,
|
|
157
166
|
w as untrack,
|
|
158
167
|
k as watch
|
|
159
168
|
};
|
package/dist/reactive.es.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactive.es.mjs","sources":["../src/reactive/signal.ts"],"sourcesContent":["/**\r\n * Reactive primitives inspired by fine-grained reactivity.\r\n *\r\n * This module provides a minimal but powerful reactive system:\r\n * - Signal: A reactive value that notifies subscribers when changed\r\n * - Computed: A derived value that automatically updates when dependencies change\r\n * - Effect: A side effect that re-runs when its dependencies change\r\n * - Batch: Group multiple updates to prevent intermediate re-renders\r\n *\r\n * @module bquery/reactive\r\n *\r\n * @example\r\n * ```ts\r\n * const count = signal(0);\r\n * const doubled = computed(() => count.value * 2);\r\n *\r\n * effect(() => {\r\n * console.log(`Count: ${count.value}, Doubled: ${doubled.value}`);\r\n * });\r\n *\r\n * batch(() => {\r\n * count.value = 1;\r\n * count.value = 2;\r\n * });\r\n * // Logs: \"Count: 2, Doubled: 4\" (only once due to batching)\r\n * ```\r\n */\r\n\r\n/**\r\n * Observer function type used internally for tracking reactivity.\r\n */\r\nexport type Observer = () => void;\r\n\r\n/**\r\n * Cleanup function returned by effects for disposal.\r\n */\r\nexport type CleanupFn = () => void;\r\n\r\n// Internal state for tracking the current observer context\r\nconst observerStack: Observer[] = [];\r\nlet batchDepth = 0;\r\nconst pendingObservers = new Set<Observer>();\r\n\r\n// Flag to disable tracking temporarily (for untrack)\r\nlet trackingEnabled = true;\r\n\r\n/**\r\n * Tracks dependencies during a function execution.\r\n * Uses direct push/pop for O(1) operations instead of array copying.\r\n * @internal\r\n */\r\nconst track = <T>(observer: Observer, fn: () => T): T => {\r\n observerStack.push(observer);\r\n try {\r\n return fn();\r\n } finally {\r\n observerStack.pop();\r\n }\r\n};\r\n\r\n/**\r\n * Schedules an observer to run, respecting batch mode.\r\n * @internal\r\n */\r\nconst scheduleObserver = (observer: Observer) => {\r\n if (batchDepth > 0) {\r\n pendingObservers.add(observer);\r\n return;\r\n }\r\n observer();\r\n};\r\n\r\n/**\r\n * Flushes all pending observers after a batch completes.\r\n * @internal\r\n */\r\nconst flushObservers = () => {\r\n for (const observer of Array.from(pendingObservers)) {\r\n pendingObservers.delete(observer);\r\n observer();\r\n }\r\n};\r\n\r\n/**\r\n * A reactive value container that notifies subscribers on change.\r\n *\r\n * Signals are the foundational primitive of the reactive system.\r\n * Reading a signal's value inside an effect or computed automatically\r\n * establishes a reactive dependency.\r\n *\r\n * @template T - The type of the stored value\r\n *\r\n * @example\r\n * ```ts\r\n * const name = signal('World');\r\n * console.log(name.value); // 'World'\r\n *\r\n * name.value = 'bQuery';\r\n * console.log(name.value); // 'bQuery'\r\n * ```\r\n */\r\nexport class Signal<T> {\r\n private subscribers = new Set<Observer>();\r\n\r\n /**\r\n * Creates a new signal with an initial value.\r\n * @param _value - The initial value\r\n */\r\n constructor(private _value: T) {}\r\n\r\n /**\r\n * Gets the current value and tracks the read if inside an observer.\r\n * Respects the global tracking state (disabled during untrack calls).\r\n */\r\n get value(): T {\r\n if (trackingEnabled) {\r\n const current = observerStack[observerStack.length - 1];\r\n if (current) {\r\n this.subscribers.add(current);\r\n }\r\n }\r\n return this._value;\r\n }\r\n\r\n /**\r\n * Sets a new value and notifies all subscribers if the value changed.\r\n * Uses Object.is for equality comparison.\r\n */\r\n set value(next: T) {\r\n if (Object.is(this._value, next)) return;\r\n this._value = next;\r\n for (const subscriber of this.subscribers) {\r\n scheduleObserver(subscriber);\r\n }\r\n }\r\n\r\n /**\r\n * Reads the current value without tracking.\r\n * Useful when you need the value but don't want to create a dependency.\r\n *\r\n * @returns The current value\r\n */\r\n peek(): T {\r\n return this._value;\r\n }\r\n\r\n /**\r\n * Updates the value using a function.\r\n * Useful for updates based on the current value.\r\n *\r\n * @param updater - Function that receives current value and returns new value\r\n */\r\n update(updater: (current: T) => T): void {\r\n this.value = updater(this._value);\r\n }\r\n}\r\n\r\n/**\r\n * A computed value that derives from other reactive sources.\r\n *\r\n * Computed values are lazily evaluated and cached. They only\r\n * recompute when their dependencies change.\r\n *\r\n * @template T - The type of the computed value\r\n *\r\n * @example\r\n * ```ts\r\n * const price = signal(100);\r\n * const quantity = signal(2);\r\n * const total = computed(() => price.value * quantity.value);\r\n *\r\n * console.log(total.value); // 200\r\n * price.value = 150;\r\n * console.log(total.value); // 300\r\n * ```\r\n */\r\nexport class Computed<T> {\r\n private cachedValue!: T;\r\n private dirty = true;\r\n private subscribers = new Set<Observer>();\r\n private readonly markDirty = () => {\r\n this.dirty = true;\r\n for (const subscriber of this.subscribers) {\r\n scheduleObserver(subscriber);\r\n }\r\n };\r\n\r\n /**\r\n * Creates a new computed value.\r\n * @param compute - Function that computes the value\r\n */\r\n constructor(private readonly compute: () => T) {}\r\n\r\n /**\r\n * Gets the computed value, recomputing if dependencies changed.\r\n */\r\n get value(): T {\r\n const current = observerStack[observerStack.length - 1];\r\n if (current) {\r\n this.subscribers.add(current);\r\n }\r\n if (this.dirty) {\r\n this.dirty = false;\r\n this.cachedValue = track(this.markDirty, this.compute);\r\n }\r\n return this.cachedValue;\r\n }\r\n}\r\n\r\n/**\r\n * Creates a new reactive signal.\r\n *\r\n * @template T - The type of the signal value\r\n * @param value - The initial value\r\n * @returns A new Signal instance\r\n *\r\n * @example\r\n * ```ts\r\n * const count = signal(0);\r\n * count.value++; // Triggers subscribers\r\n * ```\r\n */\r\nexport const signal = <T>(value: T): Signal<T> => new Signal(value);\r\n\r\n/**\r\n * Creates a new computed value.\r\n *\r\n * @template T - The type of the computed value\r\n * @param fn - Function that computes the value from reactive sources\r\n * @returns A new Computed instance\r\n *\r\n * @example\r\n * ```ts\r\n * const doubled = computed(() => count.value * 2);\r\n * ```\r\n */\r\nexport const computed = <T>(fn: () => T): Computed<T> => new Computed(fn);\r\n\r\n/**\r\n * Creates a side effect that automatically re-runs when dependencies change.\r\n *\r\n * The effect runs immediately upon creation and then re-runs whenever\r\n * any signal or computed value read inside it changes.\r\n *\r\n * @param fn - The effect function to run\r\n * @returns A cleanup function to stop the effect\r\n *\r\n * @example\r\n * ```ts\r\n * const count = signal(0);\r\n *\r\n * const cleanup = effect(() => {\r\n * document.title = `Count: ${count.value}`;\r\n * });\r\n *\r\n * // Later, to stop the effect:\r\n * cleanup();\r\n * ```\r\n */\r\nexport const effect = (fn: () => void | CleanupFn): CleanupFn => {\r\n let cleanupFn: CleanupFn | void;\r\n let isDisposed = false;\r\n\r\n const observer: Observer = () => {\r\n if (isDisposed) return;\r\n\r\n // Run previous cleanup if exists\r\n if (cleanupFn) {\r\n cleanupFn();\r\n }\r\n\r\n // Run effect and capture cleanup\r\n cleanupFn = track(observer, fn);\r\n };\r\n\r\n observer();\r\n\r\n return () => {\r\n isDisposed = true;\r\n if (cleanupFn) {\r\n cleanupFn();\r\n }\r\n };\r\n};\r\n\r\n/**\r\n * Batches multiple signal updates into a single notification cycle.\r\n *\r\n * Updates made inside the batch function are deferred until the batch\r\n * completes, preventing intermediate re-renders and improving performance.\r\n *\r\n * @param fn - Function containing multiple signal updates\r\n *\r\n * @example\r\n * ```ts\r\n * batch(() => {\r\n * firstName.value = 'John';\r\n * lastName.value = 'Doe';\r\n * age.value = 30;\r\n * });\r\n * // Effects only run once with all three updates\r\n * ```\r\n */\r\nexport const batch = (fn: () => void): void => {\r\n batchDepth += 1;\r\n try {\r\n fn();\r\n } finally {\r\n batchDepth -= 1;\r\n if (batchDepth === 0) {\r\n flushObservers();\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Creates a signal that persists to localStorage.\r\n *\r\n * @template T - The type of the signal value\r\n * @param key - The localStorage key\r\n * @param initialValue - The initial value if not found in storage\r\n * @returns A Signal that syncs with localStorage\r\n *\r\n * @example\r\n * ```ts\r\n * const theme = persistedSignal('theme', 'light');\r\n * theme.value = 'dark'; // Automatically saved to localStorage\r\n * ```\r\n */\r\nexport const persistedSignal = <T>(key: string, initialValue: T): Signal<T> => {\r\n let stored: T = initialValue;\r\n\r\n try {\r\n const raw = localStorage.getItem(key);\r\n if (raw !== null) {\r\n stored = JSON.parse(raw) as T;\r\n }\r\n } catch {\r\n // Use initial value on parse error\r\n }\r\n\r\n const sig = signal(stored);\r\n\r\n // Create an effect to persist changes\r\n effect(() => {\r\n try {\r\n localStorage.setItem(key, JSON.stringify(sig.value));\r\n } catch {\r\n // Ignore storage errors\r\n }\r\n });\r\n\r\n return sig;\r\n};\r\n\r\n// ============================================================================\r\n// Extended Reactive Utilities\r\n// ============================================================================\r\n\r\n/**\r\n * A readonly wrapper around a signal that prevents writes.\r\n * Provides read-only access to a signal's value while maintaining reactivity.\r\n *\r\n * @template T - The type of the wrapped value\r\n */\r\nexport interface ReadonlySignal<T> {\r\n /** Gets the current value with dependency tracking. */\r\n readonly value: T;\r\n /** Gets the current value without dependency tracking. */\r\n peek(): T;\r\n}\r\n\r\n/**\r\n * Creates a read-only view of a signal.\r\n * Useful for exposing reactive state without allowing modifications.\r\n *\r\n * @template T - The type of the signal value\r\n * @param sig - The signal to wrap\r\n * @returns A readonly signal wrapper\r\n *\r\n * @example\r\n * ```ts\r\n * const _count = signal(0);\r\n * const count = readonly(_count); // Expose read-only version\r\n *\r\n * console.log(count.value); // 0\r\n * count.value = 1; // TypeScript error: Cannot assign to 'value'\r\n * ```\r\n */\r\nexport const readonly = <T>(sig: Signal<T>): ReadonlySignal<T> => ({\r\n get value(): T {\r\n return sig.value;\r\n },\r\n peek(): T {\r\n return sig.peek();\r\n },\r\n});\r\n\r\n/**\r\n * Watches a signal or computed value and calls a callback with old and new values.\r\n * Unlike effect, watch provides access to the previous value.\r\n *\r\n * @template T - The type of the watched value\r\n * @param source - The signal or computed to watch\r\n * @param callback - Function called with (newValue, oldValue) on changes\r\n * @param options - Watch options\r\n * @returns A cleanup function to stop watching\r\n *\r\n * @example\r\n * ```ts\r\n * const count = signal(0);\r\n *\r\n * const cleanup = watch(count, (newVal, oldVal) => {\r\n * console.log(`Changed from ${oldVal} to ${newVal}`);\r\n * });\r\n *\r\n * count.value = 5; // Logs: \"Changed from 0 to 5\"\r\n * cleanup();\r\n * ```\r\n */\r\nexport const watch = <T>(\r\n source: Signal<T> | Computed<T>,\r\n callback: (newValue: T, oldValue: T | undefined) => void,\r\n options: { immediate?: boolean } = {}\r\n): CleanupFn => {\r\n let oldValue: T | undefined;\r\n let isFirst = true;\r\n\r\n return effect(() => {\r\n const newValue = source.value;\r\n\r\n if (isFirst) {\r\n isFirst = false;\r\n oldValue = newValue;\r\n if (options.immediate) {\r\n callback(newValue, undefined);\r\n }\r\n return;\r\n }\r\n\r\n callback(newValue, oldValue);\r\n oldValue = newValue;\r\n });\r\n};\r\n\r\n/**\r\n * Executes a function without tracking any signal dependencies.\r\n * Useful when reading a signal value without creating a reactive dependency.\r\n *\r\n * @template T - The return type of the function\r\n * @param fn - The function to execute without tracking\r\n * @returns The result of the function\r\n *\r\n * @example\r\n * ```ts\r\n * const count = signal(0);\r\n *\r\n * effect(() => {\r\n * // This creates a dependency\r\n * console.log('Tracked:', count.value);\r\n *\r\n * // This does NOT create a dependency\r\n * const untracked = untrack(() => otherSignal.value);\r\n * });\r\n * ```\r\n */\r\nexport const untrack = <T>(fn: () => T): T => {\r\n const prevTracking = trackingEnabled;\r\n trackingEnabled = false;\r\n try {\r\n return fn();\r\n } finally {\r\n trackingEnabled = prevTracking;\r\n }\r\n};\r\n\r\n/**\r\n * Type guard to check if a value is a Signal instance.\r\n *\r\n * @param value - The value to check\r\n * @returns True if the value is a Signal\r\n *\r\n * @example\r\n * ```ts\r\n * const count = signal(0);\r\n * const num = 42;\r\n *\r\n * isSignal(count); // true\r\n * isSignal(num); // false\r\n * ```\r\n */\r\nexport const isSignal = (value: unknown): value is Signal<unknown> => value instanceof Signal;\r\n\r\n/**\r\n * Type guard to check if a value is a Computed instance.\r\n *\r\n * @param value - The value to check\r\n * @returns True if the value is a Computed\r\n *\r\n * @example\r\n * ```ts\r\n * const doubled = computed(() => count.value * 2);\r\n * isComputed(doubled); // true\r\n * ```\r\n */\r\nexport const isComputed = (value: unknown): value is Computed<unknown> => value instanceof Computed;\r\n"],"names":["observerStack","batchDepth","pendingObservers","trackingEnabled","track","observer","fn","scheduleObserver","flushObservers","Signal","_value","current","next","subscriber","updater","Computed","compute","signal","value","computed","effect","cleanupFn","isDisposed","batch","persistedSignal","key","initialValue","stored","raw","sig","readonly","watch","source","callback","options","oldValue","isFirst","newValue","untrack","prevTracking","isSignal","isComputed"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"reactive.es.mjs","sources":["../src/reactive/signal.ts"],"sourcesContent":["/**\r\n * Reactive primitives inspired by fine-grained reactivity.\r\n *\r\n * This module provides a minimal but powerful reactive system:\r\n * - Signal: A reactive value that notifies subscribers when changed\r\n * - Computed: A derived value that automatically updates when dependencies change\r\n * - Effect: A side effect that re-runs when its dependencies change\r\n * - Batch: Group multiple updates to prevent intermediate re-renders\r\n *\r\n * @module bquery/reactive\r\n *\r\n * @example\r\n * ```ts\r\n * const count = signal(0);\r\n * const doubled = computed(() => count.value * 2);\r\n *\r\n * effect(() => {\r\n * console.log(`Count: ${count.value}, Doubled: ${doubled.value}`);\r\n * });\r\n *\r\n * batch(() => {\r\n * count.value = 1;\r\n * count.value = 2;\r\n * });\r\n * // Logs: \"Count: 2, Doubled: 4\" (only once due to batching)\r\n * ```\r\n */\r\n\r\n/**\r\n * Observer function type used internally for tracking reactivity.\r\n */\r\nexport type Observer = () => void;\r\n\r\n/**\r\n * Cleanup function returned by effects for disposal.\r\n */\r\nexport type CleanupFn = () => void;\r\n\r\n// Internal state for tracking the current observer context\r\nconst observerStack: Observer[] = [];\r\nlet batchDepth = 0;\r\nconst pendingObservers = new Set<Observer>();\r\n\r\n// Flag to disable tracking temporarily (for untrack)\r\nlet trackingEnabled = true;\r\n\r\n/**\r\n * Tracks dependencies during a function execution.\r\n * Uses direct push/pop for O(1) operations instead of array copying.\r\n * @internal\r\n */\r\nconst track = <T>(observer: Observer, fn: () => T): T => {\r\n observerStack.push(observer);\r\n try {\r\n return fn();\r\n } finally {\r\n observerStack.pop();\r\n }\r\n};\r\n\r\n/**\r\n * Schedules an observer to run, respecting batch mode.\r\n * @internal\r\n */\r\nconst scheduleObserver = (observer: Observer) => {\r\n if (batchDepth > 0) {\r\n pendingObservers.add(observer);\r\n return;\r\n }\r\n observer();\r\n};\r\n\r\n/**\r\n * Flushes all pending observers after a batch completes.\r\n * @internal\r\n */\r\nconst flushObservers = () => {\r\n for (const observer of Array.from(pendingObservers)) {\r\n pendingObservers.delete(observer);\r\n observer();\r\n }\r\n};\r\n\r\n/**\r\n * A reactive value container that notifies subscribers on change.\r\n *\r\n * Signals are the foundational primitive of the reactive system.\r\n * Reading a signal's value inside an effect or computed automatically\r\n * establishes a reactive dependency.\r\n *\r\n * @template T - The type of the stored value\r\n *\r\n * @example\r\n * ```ts\r\n * const name = signal('World');\r\n * console.log(name.value); // 'World'\r\n *\r\n * name.value = 'bQuery';\r\n * console.log(name.value); // 'bQuery'\r\n * ```\r\n */\r\nexport class Signal<T> {\r\n private subscribers = new Set<Observer>();\r\n\r\n /**\r\n * Creates a new signal with an initial value.\r\n * @param _value - The initial value\r\n */\r\n constructor(private _value: T) {}\r\n\r\n /**\r\n * Gets the current value and tracks the read if inside an observer.\r\n * Respects the global tracking state (disabled during untrack calls).\r\n */\r\n get value(): T {\r\n if (trackingEnabled) {\r\n const current = observerStack[observerStack.length - 1];\r\n if (current) {\r\n this.subscribers.add(current);\r\n }\r\n }\r\n return this._value;\r\n }\r\n\r\n /**\r\n * Sets a new value and notifies all subscribers if the value changed.\r\n * Uses Object.is for equality comparison.\r\n */\r\n set value(next: T) {\r\n if (Object.is(this._value, next)) return;\r\n this._value = next;\r\n for (const subscriber of this.subscribers) {\r\n scheduleObserver(subscriber);\r\n }\r\n }\r\n\r\n /**\r\n * Reads the current value without tracking.\r\n * Useful when you need the value but don't want to create a dependency.\r\n *\r\n * @returns The current value\r\n */\r\n peek(): T {\r\n return this._value;\r\n }\r\n\r\n /**\r\n * Updates the value using a function.\r\n * Useful for updates based on the current value.\r\n *\r\n * @param updater - Function that receives current value and returns new value\r\n */\r\n update(updater: (current: T) => T): void {\r\n this.value = updater(this._value);\r\n }\r\n}\r\n\r\n/**\r\n * A computed value that derives from other reactive sources.\r\n *\r\n * Computed values are lazily evaluated and cached. They only\r\n * recompute when their dependencies change.\r\n *\r\n * @template T - The type of the computed value\r\n *\r\n * @example\r\n * ```ts\r\n * const price = signal(100);\r\n * const quantity = signal(2);\r\n * const total = computed(() => price.value * quantity.value);\r\n *\r\n * console.log(total.value); // 200\r\n * price.value = 150;\r\n * console.log(total.value); // 300\r\n * ```\r\n */\r\nexport class Computed<T> {\r\n private cachedValue!: T;\r\n private dirty = true;\r\n private subscribers = new Set<Observer>();\r\n private readonly markDirty = () => {\r\n this.dirty = true;\r\n for (const subscriber of this.subscribers) {\r\n scheduleObserver(subscriber);\r\n }\r\n };\r\n\r\n /**\r\n * Creates a new computed value.\r\n * @param compute - Function that computes the value\r\n */\r\n constructor(private readonly compute: () => T) {}\r\n\r\n /**\r\n * Gets the computed value, recomputing if dependencies changed.\r\n */\r\n get value(): T {\r\n const current = observerStack[observerStack.length - 1];\r\n if (current) {\r\n this.subscribers.add(current);\r\n }\r\n if (this.dirty) {\r\n this.dirty = false;\r\n this.cachedValue = track(this.markDirty, this.compute);\r\n }\r\n return this.cachedValue;\r\n }\r\n\r\n /**\r\n * Reads the current computed value without tracking.\r\n * Useful when you need the value but don't want to create a dependency.\r\n *\r\n * @returns The current cached value (recomputes if dirty)\r\n */\r\n peek(): T {\r\n if (this.dirty) {\r\n this.dirty = false;\r\n this.cachedValue = track(this.markDirty, this.compute);\r\n }\r\n return this.cachedValue;\r\n }\r\n}\r\n\r\n/**\r\n * Creates a new reactive signal.\r\n *\r\n * @template T - The type of the signal value\r\n * @param value - The initial value\r\n * @returns A new Signal instance\r\n *\r\n * @example\r\n * ```ts\r\n * const count = signal(0);\r\n * count.value++; // Triggers subscribers\r\n * ```\r\n */\r\nexport const signal = <T>(value: T): Signal<T> => new Signal(value);\r\n\r\n/**\r\n * Creates a new computed value.\r\n *\r\n * @template T - The type of the computed value\r\n * @param fn - Function that computes the value from reactive sources\r\n * @returns A new Computed instance\r\n *\r\n * @example\r\n * ```ts\r\n * const doubled = computed(() => count.value * 2);\r\n * ```\r\n */\r\nexport const computed = <T>(fn: () => T): Computed<T> => new Computed(fn);\r\n\r\n/**\r\n * Creates a side effect that automatically re-runs when dependencies change.\r\n *\r\n * The effect runs immediately upon creation and then re-runs whenever\r\n * any signal or computed value read inside it changes.\r\n *\r\n * @param fn - The effect function to run\r\n * @returns A cleanup function to stop the effect\r\n *\r\n * @example\r\n * ```ts\r\n * const count = signal(0);\r\n *\r\n * const cleanup = effect(() => {\r\n * document.title = `Count: ${count.value}`;\r\n * });\r\n *\r\n * // Later, to stop the effect:\r\n * cleanup();\r\n * ```\r\n */\r\nexport const effect = (fn: () => void | CleanupFn): CleanupFn => {\r\n let cleanupFn: CleanupFn | void;\r\n let isDisposed = false;\r\n\r\n const observer: Observer = () => {\r\n if (isDisposed) return;\r\n\r\n // Run previous cleanup if exists\r\n if (cleanupFn) {\r\n cleanupFn();\r\n }\r\n\r\n // Run effect and capture cleanup\r\n cleanupFn = track(observer, fn);\r\n };\r\n\r\n observer();\r\n\r\n return () => {\r\n isDisposed = true;\r\n if (cleanupFn) {\r\n cleanupFn();\r\n }\r\n };\r\n};\r\n\r\n/**\r\n * Batches multiple signal updates into a single notification cycle.\r\n *\r\n * Updates made inside the batch function are deferred until the batch\r\n * completes, preventing intermediate re-renders and improving performance.\r\n *\r\n * @param fn - Function containing multiple signal updates\r\n *\r\n * @example\r\n * ```ts\r\n * batch(() => {\r\n * firstName.value = 'John';\r\n * lastName.value = 'Doe';\r\n * age.value = 30;\r\n * });\r\n * // Effects only run once with all three updates\r\n * ```\r\n */\r\nexport const batch = (fn: () => void): void => {\r\n batchDepth += 1;\r\n try {\r\n fn();\r\n } finally {\r\n batchDepth -= 1;\r\n if (batchDepth === 0) {\r\n flushObservers();\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Creates a signal that persists to localStorage.\r\n *\r\n * @template T - The type of the signal value\r\n * @param key - The localStorage key\r\n * @param initialValue - The initial value if not found in storage\r\n * @returns A Signal that syncs with localStorage\r\n *\r\n * @example\r\n * ```ts\r\n * const theme = persistedSignal('theme', 'light');\r\n * theme.value = 'dark'; // Automatically saved to localStorage\r\n * ```\r\n */\r\nexport const persistedSignal = <T>(key: string, initialValue: T): Signal<T> => {\r\n let stored: T = initialValue;\r\n\r\n try {\r\n const raw = localStorage.getItem(key);\r\n if (raw !== null) {\r\n stored = JSON.parse(raw) as T;\r\n }\r\n } catch {\r\n // Use initial value on parse error\r\n }\r\n\r\n const sig = signal(stored);\r\n\r\n // Create an effect to persist changes\r\n effect(() => {\r\n try {\r\n localStorage.setItem(key, JSON.stringify(sig.value));\r\n } catch {\r\n // Ignore storage errors\r\n }\r\n });\r\n\r\n return sig;\r\n};\r\n\r\n// ============================================================================\r\n// Extended Reactive Utilities\r\n// ============================================================================\r\n\r\n/**\r\n * A readonly wrapper around a signal that prevents writes.\r\n * Provides read-only access to a signal's value while maintaining reactivity.\r\n *\r\n * @template T - The type of the wrapped value\r\n */\r\nexport interface ReadonlySignal<T> {\r\n /** Gets the current value with dependency tracking. */\r\n readonly value: T;\r\n /** Gets the current value without dependency tracking. */\r\n peek(): T;\r\n}\r\n\r\n/**\r\n * Creates a read-only view of a signal.\r\n * Useful for exposing reactive state without allowing modifications.\r\n *\r\n * @template T - The type of the signal value\r\n * @param sig - The signal to wrap\r\n * @returns A readonly signal wrapper\r\n *\r\n * @example\r\n * ```ts\r\n * const _count = signal(0);\r\n * const count = readonly(_count); // Expose read-only version\r\n *\r\n * console.log(count.value); // 0\r\n * count.value = 1; // TypeScript error: Cannot assign to 'value'\r\n * ```\r\n */\r\nexport const readonly = <T>(sig: Signal<T>): ReadonlySignal<T> => ({\r\n get value(): T {\r\n return sig.value;\r\n },\r\n peek(): T {\r\n return sig.peek();\r\n },\r\n});\r\n\r\n/**\r\n * Watches a signal or computed value and calls a callback with old and new values.\r\n * Unlike effect, watch provides access to the previous value.\r\n *\r\n * @template T - The type of the watched value\r\n * @param source - The signal or computed to watch\r\n * @param callback - Function called with (newValue, oldValue) on changes\r\n * @param options - Watch options\r\n * @returns A cleanup function to stop watching\r\n *\r\n * @example\r\n * ```ts\r\n * const count = signal(0);\r\n *\r\n * const cleanup = watch(count, (newVal, oldVal) => {\r\n * console.log(`Changed from ${oldVal} to ${newVal}`);\r\n * });\r\n *\r\n * count.value = 5; // Logs: \"Changed from 0 to 5\"\r\n * cleanup();\r\n * ```\r\n */\r\nexport const watch = <T>(\r\n source: Signal<T> | Computed<T>,\r\n callback: (newValue: T, oldValue: T | undefined) => void,\r\n options: { immediate?: boolean } = {}\r\n): CleanupFn => {\r\n let oldValue: T | undefined;\r\n let isFirst = true;\r\n\r\n return effect(() => {\r\n const newValue = source.value;\r\n\r\n if (isFirst) {\r\n isFirst = false;\r\n oldValue = newValue;\r\n if (options.immediate) {\r\n callback(newValue, undefined);\r\n }\r\n return;\r\n }\r\n\r\n callback(newValue, oldValue);\r\n oldValue = newValue;\r\n });\r\n};\r\n\r\n/**\r\n * Executes a function without tracking any signal dependencies.\r\n * Useful when reading a signal value without creating a reactive dependency.\r\n *\r\n * @template T - The return type of the function\r\n * @param fn - The function to execute without tracking\r\n * @returns The result of the function\r\n *\r\n * @example\r\n * ```ts\r\n * const count = signal(0);\r\n *\r\n * effect(() => {\r\n * // This creates a dependency\r\n * console.log('Tracked:', count.value);\r\n *\r\n * // This does NOT create a dependency\r\n * const untracked = untrack(() => otherSignal.value);\r\n * });\r\n * ```\r\n */\r\nexport const untrack = <T>(fn: () => T): T => {\r\n const prevTracking = trackingEnabled;\r\n trackingEnabled = false;\r\n try {\r\n return fn();\r\n } finally {\r\n trackingEnabled = prevTracking;\r\n }\r\n};\r\n\r\n/**\r\n * Type guard to check if a value is a Signal instance.\r\n *\r\n * @param value - The value to check\r\n * @returns True if the value is a Signal\r\n *\r\n * @example\r\n * ```ts\r\n * const count = signal(0);\r\n * const num = 42;\r\n *\r\n * isSignal(count); // true\r\n * isSignal(num); // false\r\n * ```\r\n */\r\nexport const isSignal = (value: unknown): value is Signal<unknown> => value instanceof Signal;\r\n\r\n/**\r\n * Type guard to check if a value is a Computed instance.\r\n *\r\n * @param value - The value to check\r\n * @returns True if the value is a Computed\r\n *\r\n * @example\r\n * ```ts\r\n * const doubled = computed(() => count.value * 2);\r\n * isComputed(doubled); // true\r\n * ```\r\n */\r\nexport const isComputed = (value: unknown): value is Computed<unknown> => value instanceof Computed;\r\n"],"names":["observerStack","batchDepth","pendingObservers","trackingEnabled","track","observer","fn","scheduleObserver","flushObservers","Signal","_value","current","next","subscriber","updater","Computed","compute","signal","value","computed","effect","cleanupFn","isDisposed","batch","persistedSignal","key","initialValue","stored","raw","sig","readonly","watch","source","callback","options","oldValue","isFirst","newValue","untrack","prevTracking","isSignal","isComputed"],"mappings":"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"}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal SPA router with History API integration.
|
|
3
|
+
*
|
|
4
|
+
* This module provides a lightweight, signal-based router for single-page
|
|
5
|
+
* applications. Features include:
|
|
6
|
+
* - History API navigation
|
|
7
|
+
* - Route matching with params and wildcards
|
|
8
|
+
* - Lazy route loading
|
|
9
|
+
* - Navigation guards (beforeEach, afterEach)
|
|
10
|
+
* - Reactive current route via signals
|
|
11
|
+
* - Multi-value query params (e.g., `?tag=a&tag=b` → `{ tag: ['a', 'b'] }`)
|
|
12
|
+
*
|
|
13
|
+
* @module bquery/router
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { createRouter, navigate, currentRoute } from 'bquery/router';
|
|
18
|
+
* import { effect } from 'bquery/reactive';
|
|
19
|
+
*
|
|
20
|
+
* const router = createRouter({
|
|
21
|
+
* routes: [
|
|
22
|
+
* { path: '/', component: () => import('./Home') },
|
|
23
|
+
* { path: '/user/:id', component: () => import('./User') },
|
|
24
|
+
* { path: '*', component: () => import('./NotFound') },
|
|
25
|
+
* ],
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* effect(() => {
|
|
29
|
+
* console.log('Route changed:', currentRoute.value);
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* navigate('/user/42');
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
import { type ReadonlySignal } from '../reactive/index';
|
|
36
|
+
/**
|
|
37
|
+
* Represents a parsed route with matched params.
|
|
38
|
+
*/
|
|
39
|
+
export type Route = {
|
|
40
|
+
/** The current path (e.g., '/user/42') */
|
|
41
|
+
path: string;
|
|
42
|
+
/** Extracted route params (e.g., { id: '42' }) */
|
|
43
|
+
params: Record<string, string>;
|
|
44
|
+
/**
|
|
45
|
+
* Query string params.
|
|
46
|
+
* Each key maps to a single string value by default.
|
|
47
|
+
* Only keys that appear multiple times in the query string become arrays.
|
|
48
|
+
* @example
|
|
49
|
+
* // ?foo=1 → { foo: '1' }
|
|
50
|
+
* // ?tag=a&tag=b → { tag: ['a', 'b'] }
|
|
51
|
+
* // ?x=1&y=2&x=3 → { x: ['1', '3'], y: '2' }
|
|
52
|
+
*/
|
|
53
|
+
query: Record<string, string | string[]>;
|
|
54
|
+
/** The matched route definition */
|
|
55
|
+
matched: RouteDefinition | null;
|
|
56
|
+
/** Hash fragment without # */
|
|
57
|
+
hash: string;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Route definition for configuration.
|
|
61
|
+
*/
|
|
62
|
+
export type RouteDefinition = {
|
|
63
|
+
/** Path pattern (e.g., '/user/:id', '/posts/*') */
|
|
64
|
+
path: string;
|
|
65
|
+
/** Component loader (sync or async) */
|
|
66
|
+
component: () => unknown | Promise<unknown>;
|
|
67
|
+
/** Optional route name for programmatic navigation */
|
|
68
|
+
name?: string;
|
|
69
|
+
/** Optional metadata */
|
|
70
|
+
meta?: Record<string, unknown>;
|
|
71
|
+
/** Nested child routes */
|
|
72
|
+
children?: RouteDefinition[];
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Router configuration options.
|
|
76
|
+
*/
|
|
77
|
+
export type RouterOptions = {
|
|
78
|
+
/** Array of route definitions */
|
|
79
|
+
routes: RouteDefinition[];
|
|
80
|
+
/** Base path for all routes (default: '') */
|
|
81
|
+
base?: string;
|
|
82
|
+
/** Use hash-based routing instead of history (default: false) */
|
|
83
|
+
hash?: boolean;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Navigation guard function type.
|
|
87
|
+
*/
|
|
88
|
+
export type NavigationGuard = (to: Route, from: Route) => boolean | void | Promise<boolean | void>;
|
|
89
|
+
/**
|
|
90
|
+
* Router instance returned by createRouter.
|
|
91
|
+
*/
|
|
92
|
+
export type Router = {
|
|
93
|
+
/** Navigate to a path */
|
|
94
|
+
push: (path: string) => Promise<void>;
|
|
95
|
+
/** Replace current history entry */
|
|
96
|
+
replace: (path: string) => Promise<void>;
|
|
97
|
+
/** Go back in history */
|
|
98
|
+
back: () => void;
|
|
99
|
+
/** Go forward in history */
|
|
100
|
+
forward: () => void;
|
|
101
|
+
/** Go to a specific history entry */
|
|
102
|
+
go: (delta: number) => void;
|
|
103
|
+
/** Add a beforeEach guard */
|
|
104
|
+
beforeEach: (guard: NavigationGuard) => () => void;
|
|
105
|
+
/** Add an afterEach hook */
|
|
106
|
+
afterEach: (hook: (to: Route, from: Route) => void) => () => void;
|
|
107
|
+
/** Current route (reactive) */
|
|
108
|
+
currentRoute: ReadonlySignal<Route>;
|
|
109
|
+
/** All route definitions */
|
|
110
|
+
routes: RouteDefinition[];
|
|
111
|
+
/** Destroy the router and cleanup listeners */
|
|
112
|
+
destroy: () => void;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Reactive signal containing the current route.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* import { currentRoute } from 'bquery/router';
|
|
120
|
+
* import { effect } from 'bquery/reactive';
|
|
121
|
+
*
|
|
122
|
+
* effect(() => {
|
|
123
|
+
* document.title = `Page: ${currentRoute.value.path}`;
|
|
124
|
+
* });
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export declare const currentRoute: ReadonlySignal<Route>;
|
|
128
|
+
/**
|
|
129
|
+
* Navigates to a new path.
|
|
130
|
+
*
|
|
131
|
+
* @param path - The path to navigate to
|
|
132
|
+
* @param options - Navigation options
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* import { navigate } from 'bquery/router';
|
|
137
|
+
*
|
|
138
|
+
* // Push to history
|
|
139
|
+
* await navigate('/dashboard');
|
|
140
|
+
*
|
|
141
|
+
* // Replace current entry
|
|
142
|
+
* await navigate('/login', { replace: true });
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
export declare const navigate: (path: string, options?: {
|
|
146
|
+
replace?: boolean;
|
|
147
|
+
}) => Promise<void>;
|
|
148
|
+
/**
|
|
149
|
+
* Programmatically go back in history.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```ts
|
|
153
|
+
* import { back } from 'bquery/router';
|
|
154
|
+
* back();
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export declare const back: () => void;
|
|
158
|
+
/**
|
|
159
|
+
* Programmatically go forward in history.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```ts
|
|
163
|
+
* import { forward } from 'bquery/router';
|
|
164
|
+
* forward();
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
export declare const forward: () => void;
|
|
168
|
+
/**
|
|
169
|
+
* Creates and initializes a router instance.
|
|
170
|
+
*
|
|
171
|
+
* @param options - Router configuration
|
|
172
|
+
* @returns The router instance
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```ts
|
|
176
|
+
* import { createRouter } from 'bquery/router';
|
|
177
|
+
*
|
|
178
|
+
* const router = createRouter({
|
|
179
|
+
* routes: [
|
|
180
|
+
* { path: '/', component: () => import('./pages/Home') },
|
|
181
|
+
* { path: '/about', component: () => import('./pages/About') },
|
|
182
|
+
* { path: '/user/:id', component: () => import('./pages/User') },
|
|
183
|
+
* { path: '*', component: () => import('./pages/NotFound') },
|
|
184
|
+
* ],
|
|
185
|
+
* base: '/app',
|
|
186
|
+
* });
|
|
187
|
+
*
|
|
188
|
+
* router.beforeEach((to, from) => {
|
|
189
|
+
* if (to.path === '/admin' && !isAuthenticated()) {
|
|
190
|
+
* return false; // Cancel navigation
|
|
191
|
+
* }
|
|
192
|
+
* });
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
export declare const createRouter: (options: RouterOptions) => Router;
|
|
196
|
+
/**
|
|
197
|
+
* Resolves a route by name and params.
|
|
198
|
+
*
|
|
199
|
+
* @param name - The route name
|
|
200
|
+
* @param params - Route params to interpolate
|
|
201
|
+
* @returns The resolved path
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```ts
|
|
205
|
+
* import { resolve } from 'bquery/router';
|
|
206
|
+
*
|
|
207
|
+
* const path = resolve('user', { id: '42' });
|
|
208
|
+
* // Returns '/user/42' if route is defined as { name: 'user', path: '/user/:id' }
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
export declare const resolve: (name: string, params?: Record<string, string>) => string;
|
|
212
|
+
/**
|
|
213
|
+
* Checks if a path matches the current route.
|
|
214
|
+
*
|
|
215
|
+
* @param path - Path to check
|
|
216
|
+
* @param exact - Whether to match exactly (default: false)
|
|
217
|
+
* @returns True if the path matches
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```ts
|
|
221
|
+
* import { isActive } from 'bquery/router';
|
|
222
|
+
*
|
|
223
|
+
* if (isActive('/dashboard')) {
|
|
224
|
+
* // Highlight nav item
|
|
225
|
+
* }
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
export declare const isActive: (path: string, exact?: boolean) => boolean;
|
|
229
|
+
/**
|
|
230
|
+
* Creates a computed signal that checks if a path is active.
|
|
231
|
+
*
|
|
232
|
+
* @param path - Path to check
|
|
233
|
+
* @param exact - Whether to match exactly
|
|
234
|
+
* @returns A reactive signal
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```ts
|
|
238
|
+
* import { isActiveSignal } from 'bquery/router';
|
|
239
|
+
* import { effect } from 'bquery/reactive';
|
|
240
|
+
*
|
|
241
|
+
* const dashboardActive = isActiveSignal('/dashboard');
|
|
242
|
+
* effect(() => {
|
|
243
|
+
* navItem.classList.toggle('active', dashboardActive.value);
|
|
244
|
+
* });
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
export declare const isActiveSignal: (path: string, exact?: boolean) => ReadonlySignal<boolean>;
|
|
248
|
+
/**
|
|
249
|
+
* Creates click handler for router links.
|
|
250
|
+
* Attach to anchor elements to enable client-side navigation.
|
|
251
|
+
*
|
|
252
|
+
* @param path - Target path
|
|
253
|
+
* @param options - Navigation options
|
|
254
|
+
* @returns Click event handler
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```ts
|
|
258
|
+
* import { link } from 'bquery/router';
|
|
259
|
+
* import { $ } from 'bquery/core';
|
|
260
|
+
*
|
|
261
|
+
* $('#nav-home').on('click', link('/'));
|
|
262
|
+
* $('#nav-about').on('click', link('/about'));
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
export declare const link: (path: string, options?: {
|
|
266
|
+
replace?: boolean;
|
|
267
|
+
}) => ((e: Event) => void);
|
|
268
|
+
/**
|
|
269
|
+
* Intercepts all link clicks within a container for client-side routing.
|
|
270
|
+
* Only intercepts links with matching origins and no target attribute.
|
|
271
|
+
*
|
|
272
|
+
* @param container - The container element to intercept links in
|
|
273
|
+
* @returns Cleanup function to remove the listener
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```ts
|
|
277
|
+
* import { interceptLinks } from 'bquery/router';
|
|
278
|
+
*
|
|
279
|
+
* // Intercept all links in the app
|
|
280
|
+
* const cleanup = interceptLinks(document.body);
|
|
281
|
+
*
|
|
282
|
+
* // Later, remove the interceptor
|
|
283
|
+
* cleanup();
|
|
284
|
+
* ```
|
|
285
|
+
*/
|
|
286
|
+
export declare const interceptLinks: (container?: Element) => (() => void);
|
|
287
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAoB,KAAK,cAAc,EAAe,MAAM,mBAAmB,CAAC;AAMvF;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG;IAClB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;;;;;;;;OAQG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IACzC,mCAAmC;IACnC,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;IAChC,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,SAAS,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;CAC9B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,iCAAiC;IACjC,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,KAAK,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;AAEnG;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG;IACnB,yBAAyB;IACzB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,oCAAoC;IACpC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,yBAAyB;IACzB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,4BAA4B;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,qCAAqC;IACrC,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,6BAA6B;IAC7B,UAAU,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,MAAM,IAAI,CAAC;IACnD,4BAA4B;IAC5B,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAClE,+BAA+B;IAC/B,YAAY,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;IACpC,4BAA4B;IAC5B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,+CAA+C;IAC/C,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAkBF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,KAAK,CAAqC,CAAC;AAgJrF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,QAAQ,GACnB,MAAM,MAAM,EACZ,UAAS;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,KAClC,OAAO,CAAC,IAAI,CAMd,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,IAAI,QAAO,IAMvB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,QAAO,IAM1B,CAAC;AAMF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,aAAa,KAAG,MA2JrD,CAAC;AA6BF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,EAAE,SAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,KAAG,MAgB3E,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,EAAE,eAAa,KAAG,OAGtD,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,EAAE,eAAa,KAAG,cAAc,CAAC,OAAO,CAKlF,CAAC;AAMF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,EAAE,UAAS;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,KAAG,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAK3F,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,cAAc,GAAI,YAAW,OAAuB,KAAG,CAAC,MAAM,IAAI,CAkB9E,CAAC"}
|