@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.
Files changed (309) hide show
  1. package/README.md +127 -27
  2. package/dist/batch-x7b2eZST.js +13 -0
  3. package/dist/batch-x7b2eZST.js.map +1 -0
  4. package/dist/component/component.d.ts +69 -0
  5. package/dist/component/component.d.ts.map +1 -0
  6. package/dist/component/html.d.ts +35 -0
  7. package/dist/component/html.d.ts.map +1 -0
  8. package/dist/component/index.d.ts +3 -126
  9. package/dist/component/index.d.ts.map +1 -1
  10. package/dist/component/props.d.ts +18 -0
  11. package/dist/component/props.d.ts.map +1 -0
  12. package/dist/component/types.d.ts +77 -0
  13. package/dist/component/types.d.ts.map +1 -0
  14. package/dist/component.es.mjs +90 -59
  15. package/dist/component.es.mjs.map +1 -1
  16. package/dist/core/collection.d.ts +55 -3
  17. package/dist/core/collection.d.ts.map +1 -1
  18. package/dist/core/dom.d.ts +6 -0
  19. package/dist/core/dom.d.ts.map +1 -0
  20. package/dist/core/element.d.ts +31 -4
  21. package/dist/core/element.d.ts.map +1 -1
  22. package/dist/core/index.d.ts +2 -0
  23. package/dist/core/index.d.ts.map +1 -1
  24. package/dist/core/utils/array.d.ts +74 -0
  25. package/dist/core/utils/array.d.ts.map +1 -0
  26. package/dist/core/utils/function.d.ts +87 -0
  27. package/dist/core/utils/function.d.ts.map +1 -0
  28. package/dist/core/utils/index.d.ts +70 -0
  29. package/dist/core/utils/index.d.ts.map +1 -0
  30. package/dist/core/utils/misc.d.ts +63 -0
  31. package/dist/core/utils/misc.d.ts.map +1 -0
  32. package/dist/core/utils/number.d.ts +65 -0
  33. package/dist/core/utils/number.d.ts.map +1 -0
  34. package/dist/core/utils/object.d.ts +133 -0
  35. package/dist/core/utils/object.d.ts.map +1 -0
  36. package/dist/core/utils/string.d.ts +80 -0
  37. package/dist/core/utils/string.d.ts.map +1 -0
  38. package/dist/core/utils/type-guards.d.ts +79 -0
  39. package/dist/core/utils/type-guards.d.ts.map +1 -0
  40. package/dist/core-BhpuvPhy.js +170 -0
  41. package/dist/core-BhpuvPhy.js.map +1 -0
  42. package/dist/core.es.mjs +495 -489
  43. package/dist/core.es.mjs.map +1 -1
  44. package/dist/full.d.ts +2 -2
  45. package/dist/full.d.ts.map +1 -1
  46. package/dist/full.es.mjs +87 -64
  47. package/dist/full.es.mjs.map +1 -1
  48. package/dist/full.iife.js +2 -2
  49. package/dist/full.iife.js.map +1 -1
  50. package/dist/full.umd.js +2 -2
  51. package/dist/full.umd.js.map +1 -1
  52. package/dist/index.es.mjs +138 -68
  53. package/dist/index.es.mjs.map +1 -1
  54. package/dist/motion/animate.d.ts +25 -0
  55. package/dist/motion/animate.d.ts.map +1 -0
  56. package/dist/motion/easing.d.ts +30 -0
  57. package/dist/motion/easing.d.ts.map +1 -0
  58. package/dist/motion/flip.d.ts +55 -0
  59. package/dist/motion/flip.d.ts.map +1 -0
  60. package/dist/motion/index.d.ts +11 -138
  61. package/dist/motion/index.d.ts.map +1 -1
  62. package/dist/motion/keyframes.d.ts +21 -0
  63. package/dist/motion/keyframes.d.ts.map +1 -0
  64. package/dist/motion/reduced-motion.d.ts +12 -0
  65. package/dist/motion/reduced-motion.d.ts.map +1 -0
  66. package/dist/motion/scroll.d.ts +15 -0
  67. package/dist/motion/scroll.d.ts.map +1 -0
  68. package/dist/motion/spring.d.ts +42 -0
  69. package/dist/motion/spring.d.ts.map +1 -0
  70. package/dist/motion/stagger.d.ts +22 -0
  71. package/dist/motion/stagger.d.ts.map +1 -0
  72. package/dist/motion/timeline.d.ts +21 -0
  73. package/dist/motion/timeline.d.ts.map +1 -0
  74. package/dist/motion/transition.d.ts +22 -0
  75. package/dist/motion/transition.d.ts.map +1 -0
  76. package/dist/motion/types.d.ts +182 -0
  77. package/dist/motion/types.d.ts.map +1 -0
  78. package/dist/motion.es.mjs +320 -61
  79. package/dist/motion.es.mjs.map +1 -1
  80. package/dist/persisted-DHoi3uEs.js +278 -0
  81. package/dist/persisted-DHoi3uEs.js.map +1 -0
  82. package/dist/platform/storage.d.ts.map +1 -1
  83. package/dist/platform.es.mjs +12 -7
  84. package/dist/platform.es.mjs.map +1 -1
  85. package/dist/reactive/batch.d.ts +13 -0
  86. package/dist/reactive/batch.d.ts.map +1 -0
  87. package/dist/reactive/computed.d.ts +50 -0
  88. package/dist/reactive/computed.d.ts.map +1 -0
  89. package/dist/reactive/core.d.ts +72 -0
  90. package/dist/reactive/core.d.ts.map +1 -0
  91. package/dist/reactive/effect.d.ts +15 -0
  92. package/dist/reactive/effect.d.ts.map +1 -0
  93. package/dist/reactive/index.d.ts +2 -2
  94. package/dist/reactive/index.d.ts.map +1 -1
  95. package/dist/reactive/internals.d.ts +42 -0
  96. package/dist/reactive/internals.d.ts.map +1 -0
  97. package/dist/reactive/linked.d.ts +36 -0
  98. package/dist/reactive/linked.d.ts.map +1 -0
  99. package/dist/reactive/persisted.d.ts +14 -0
  100. package/dist/reactive/persisted.d.ts.map +1 -0
  101. package/dist/reactive/readonly.d.ts +26 -0
  102. package/dist/reactive/readonly.d.ts.map +1 -0
  103. package/dist/reactive/signal.d.ts +13 -312
  104. package/dist/reactive/signal.d.ts.map +1 -1
  105. package/dist/reactive/type-guards.d.ts +20 -0
  106. package/dist/reactive/type-guards.d.ts.map +1 -0
  107. package/dist/reactive/untrack.d.ts +29 -0
  108. package/dist/reactive/untrack.d.ts.map +1 -0
  109. package/dist/reactive/watch.d.ts +42 -0
  110. package/dist/reactive/watch.d.ts.map +1 -0
  111. package/dist/reactive.es.mjs +30 -163
  112. package/dist/reactive.es.mjs.map +1 -1
  113. package/dist/router/index.d.ts +6 -252
  114. package/dist/router/index.d.ts.map +1 -1
  115. package/dist/router/links.d.ts +44 -0
  116. package/dist/router/links.d.ts.map +1 -0
  117. package/dist/router/match.d.ts +20 -0
  118. package/dist/router/match.d.ts.map +1 -0
  119. package/dist/router/navigation.d.ts +45 -0
  120. package/dist/router/navigation.d.ts.map +1 -0
  121. package/dist/router/query.d.ts +16 -0
  122. package/dist/router/query.d.ts.map +1 -0
  123. package/dist/router/router.d.ts +34 -0
  124. package/dist/router/router.d.ts.map +1 -0
  125. package/dist/router/state.d.ts +27 -0
  126. package/dist/router/state.d.ts.map +1 -0
  127. package/dist/router/types.d.ts +88 -0
  128. package/dist/router/types.d.ts.map +1 -0
  129. package/dist/router/utils.d.ts +65 -0
  130. package/dist/router/utils.d.ts.map +1 -0
  131. package/dist/router.es.mjs +168 -132
  132. package/dist/router.es.mjs.map +1 -1
  133. package/dist/sanitize-Cxvxa-DX.js +283 -0
  134. package/dist/sanitize-Cxvxa-DX.js.map +1 -0
  135. package/dist/security/constants.d.ts +42 -0
  136. package/dist/security/constants.d.ts.map +1 -0
  137. package/dist/security/csp.d.ts +24 -0
  138. package/dist/security/csp.d.ts.map +1 -0
  139. package/dist/security/index.d.ts +4 -2
  140. package/dist/security/index.d.ts.map +1 -1
  141. package/dist/security/sanitize-core.d.ts +13 -0
  142. package/dist/security/sanitize-core.d.ts.map +1 -0
  143. package/dist/security/sanitize.d.ts +5 -57
  144. package/dist/security/sanitize.d.ts.map +1 -1
  145. package/dist/security/trusted-types.d.ts +25 -0
  146. package/dist/security/trusted-types.d.ts.map +1 -0
  147. package/dist/security/types.d.ts +36 -0
  148. package/dist/security/types.d.ts.map +1 -0
  149. package/dist/security.es.mjs +50 -277
  150. package/dist/security.es.mjs.map +1 -1
  151. package/dist/store/create-store.d.ts +15 -0
  152. package/dist/store/create-store.d.ts.map +1 -0
  153. package/dist/store/define-store.d.ts +28 -0
  154. package/dist/store/define-store.d.ts.map +1 -0
  155. package/dist/store/devtools.d.ts +22 -0
  156. package/dist/store/devtools.d.ts.map +1 -0
  157. package/dist/store/index.d.ts +10 -286
  158. package/dist/store/index.d.ts.map +1 -1
  159. package/dist/store/mapping.d.ts +28 -0
  160. package/dist/store/mapping.d.ts.map +1 -0
  161. package/dist/store/persisted.d.ts +13 -0
  162. package/dist/store/persisted.d.ts.map +1 -0
  163. package/dist/store/plugins.d.ts +13 -0
  164. package/dist/store/plugins.d.ts.map +1 -0
  165. package/dist/store/registry.d.ts +28 -0
  166. package/dist/store/registry.d.ts.map +1 -0
  167. package/dist/store/types.d.ts +71 -0
  168. package/dist/store/types.d.ts.map +1 -0
  169. package/dist/store/utils.d.ts +28 -0
  170. package/dist/store/utils.d.ts.map +1 -0
  171. package/dist/store/watch.d.ts +23 -0
  172. package/dist/store/watch.d.ts.map +1 -0
  173. package/dist/store.es.mjs +22 -224
  174. package/dist/store.es.mjs.map +1 -1
  175. package/dist/type-guards-BdKlYYlS.js +32 -0
  176. package/dist/type-guards-BdKlYYlS.js.map +1 -0
  177. package/dist/untrack-DNnnqdlR.js +6 -0
  178. package/dist/untrack-DNnnqdlR.js.map +1 -0
  179. package/dist/view/directives/bind.d.ts +7 -0
  180. package/dist/view/directives/bind.d.ts.map +1 -0
  181. package/dist/view/directives/class.d.ts +8 -0
  182. package/dist/view/directives/class.d.ts.map +1 -0
  183. package/dist/view/directives/for.d.ts +23 -0
  184. package/dist/view/directives/for.d.ts.map +1 -0
  185. package/dist/view/directives/html.d.ts +7 -0
  186. package/dist/view/directives/html.d.ts.map +1 -0
  187. package/dist/view/directives/if.d.ts +7 -0
  188. package/dist/view/directives/if.d.ts.map +1 -0
  189. package/dist/view/directives/index.d.ts +12 -0
  190. package/dist/view/directives/index.d.ts.map +1 -0
  191. package/dist/view/directives/model.d.ts +7 -0
  192. package/dist/view/directives/model.d.ts.map +1 -0
  193. package/dist/view/directives/on.d.ts +7 -0
  194. package/dist/view/directives/on.d.ts.map +1 -0
  195. package/dist/view/directives/ref.d.ts +7 -0
  196. package/dist/view/directives/ref.d.ts.map +1 -0
  197. package/dist/view/directives/show.d.ts +7 -0
  198. package/dist/view/directives/show.d.ts.map +1 -0
  199. package/dist/view/directives/style.d.ts +7 -0
  200. package/dist/view/directives/style.d.ts.map +1 -0
  201. package/dist/view/directives/text.d.ts +7 -0
  202. package/dist/view/directives/text.d.ts.map +1 -0
  203. package/dist/view/evaluate.d.ts +43 -0
  204. package/dist/view/evaluate.d.ts.map +1 -0
  205. package/dist/view/index.d.ts +3 -93
  206. package/dist/view/index.d.ts.map +1 -1
  207. package/dist/view/mount.d.ts +69 -0
  208. package/dist/view/mount.d.ts.map +1 -0
  209. package/dist/view/process.d.ts +26 -0
  210. package/dist/view/process.d.ts.map +1 -0
  211. package/dist/view/types.d.ts +36 -0
  212. package/dist/view/types.d.ts.map +1 -0
  213. package/dist/view.es.mjs +358 -251
  214. package/dist/view.es.mjs.map +1 -1
  215. package/dist/watch-DXXv3iAI.js +58 -0
  216. package/dist/watch-DXXv3iAI.js.map +1 -0
  217. package/package.json +14 -14
  218. package/src/component/component.ts +289 -0
  219. package/src/component/html.ts +53 -0
  220. package/src/component/index.ts +40 -414
  221. package/src/component/props.ts +116 -0
  222. package/src/component/types.ts +85 -0
  223. package/src/core/collection.ts +181 -7
  224. package/src/core/dom.ts +38 -0
  225. package/src/core/element.ts +59 -25
  226. package/src/core/index.ts +48 -4
  227. package/src/core/utils/array.ts +102 -0
  228. package/src/core/utils/function.ts +151 -0
  229. package/src/core/utils/index.ts +83 -0
  230. package/src/core/utils/misc.ts +82 -0
  231. package/src/core/utils/number.ts +78 -0
  232. package/src/core/utils/object.ts +206 -0
  233. package/src/core/utils/string.ts +112 -0
  234. package/src/core/utils/type-guards.ts +112 -0
  235. package/src/full.ts +187 -150
  236. package/src/index.ts +36 -36
  237. package/src/motion/animate.ts +113 -0
  238. package/src/motion/easing.ts +40 -0
  239. package/src/motion/flip.ts +176 -0
  240. package/src/motion/index.ts +41 -358
  241. package/src/motion/keyframes.ts +46 -0
  242. package/src/motion/reduced-motion.ts +17 -0
  243. package/src/motion/scroll.ts +57 -0
  244. package/src/motion/spring.ts +150 -0
  245. package/src/motion/stagger.ts +43 -0
  246. package/src/motion/timeline.ts +246 -0
  247. package/src/motion/transition.ts +51 -0
  248. package/src/motion/types.ts +198 -0
  249. package/src/platform/storage.ts +215 -208
  250. package/src/reactive/batch.ts +22 -0
  251. package/src/reactive/computed.ts +92 -0
  252. package/src/reactive/core.ts +114 -0
  253. package/src/reactive/effect.ts +54 -0
  254. package/src/reactive/index.ts +23 -22
  255. package/src/reactive/internals.ts +122 -0
  256. package/src/reactive/linked.ts +56 -0
  257. package/src/reactive/persisted.ts +74 -0
  258. package/src/reactive/readonly.ts +35 -0
  259. package/src/reactive/signal.ts +20 -520
  260. package/src/reactive/type-guards.ts +22 -0
  261. package/src/reactive/untrack.ts +31 -0
  262. package/src/reactive/watch.ts +73 -0
  263. package/src/router/index.ts +41 -718
  264. package/src/router/links.ts +130 -0
  265. package/src/router/match.ts +106 -0
  266. package/src/router/navigation.ts +71 -0
  267. package/src/router/query.ts +35 -0
  268. package/src/router/router.ts +211 -0
  269. package/src/router/state.ts +46 -0
  270. package/src/router/types.ts +93 -0
  271. package/src/router/utils.ts +116 -0
  272. package/src/security/constants.ts +209 -0
  273. package/src/security/csp.ts +77 -0
  274. package/src/security/index.ts +4 -12
  275. package/src/security/sanitize-core.ts +364 -0
  276. package/src/security/sanitize.ts +66 -625
  277. package/src/security/trusted-types.ts +69 -0
  278. package/src/security/types.ts +40 -0
  279. package/src/store/create-store.ts +329 -0
  280. package/src/store/define-store.ts +48 -0
  281. package/src/store/devtools.ts +45 -0
  282. package/src/store/index.ts +22 -848
  283. package/src/store/mapping.ts +73 -0
  284. package/src/store/persisted.ts +61 -0
  285. package/src/store/plugins.ts +32 -0
  286. package/src/store/registry.ts +51 -0
  287. package/src/store/types.ts +94 -0
  288. package/src/store/utils.ts +141 -0
  289. package/src/store/watch.ts +52 -0
  290. package/src/view/directives/bind.ts +23 -0
  291. package/src/view/directives/class.ts +70 -0
  292. package/src/view/directives/for.ts +275 -0
  293. package/src/view/directives/html.ts +19 -0
  294. package/src/view/directives/if.ts +30 -0
  295. package/src/view/directives/index.ts +11 -0
  296. package/src/view/directives/model.ts +56 -0
  297. package/src/view/directives/on.ts +41 -0
  298. package/src/view/directives/ref.ts +41 -0
  299. package/src/view/directives/show.ts +26 -0
  300. package/src/view/directives/style.ts +47 -0
  301. package/src/view/directives/text.ts +15 -0
  302. package/src/view/evaluate.ts +290 -0
  303. package/src/view/index.ts +112 -1041
  304. package/src/view/mount.ts +200 -0
  305. package/src/view/process.ts +92 -0
  306. package/src/view/types.ts +44 -0
  307. package/dist/core/utils.d.ts +0 -313
  308. package/dist/core/utils.d.ts.map +0 -1
  309. package/src/core/utils.ts +0 -444
@@ -1,208 +1,215 @@
1
- /**
2
- * Unified storage adapters for web platform storage APIs.
3
- * Provides a consistent, promise-based interface with predictable errors.
4
- */
5
-
6
- /**
7
- * Common interface for all storage adapters.
8
- * All methods return promises for a unified async API.
9
- */
10
- export interface StorageAdapter {
11
- /**
12
- * Retrieve a value by key.
13
- * @param key - The storage key
14
- * @returns The stored value or null if not found
15
- */
16
- get<T>(key: string): Promise<T | null>;
17
-
18
- /**
19
- * Store a value by key.
20
- * @param key - The storage key
21
- * @param value - The value to store
22
- */
23
- set<T>(key: string, value: T): Promise<void>;
24
-
25
- /**
26
- * Remove a value by key.
27
- * @param key - The storage key
28
- */
29
- remove(key: string): Promise<void>;
30
-
31
- /**
32
- * Clear all stored values.
33
- */
34
- clear(): Promise<void>;
35
-
36
- /**
37
- * Get all storage keys.
38
- * @returns Array of all keys
39
- */
40
- keys(): Promise<string[]>;
41
- }
42
-
43
- /**
44
- * Abstract base class for web storage adapters (localStorage/sessionStorage).
45
- * Implements DRY principle by sharing common logic.
46
- */
47
- abstract class WebStorageAdapter implements StorageAdapter {
48
- constructor(protected readonly storage: Storage) {}
49
-
50
- async get<T>(key: string): Promise<T | null> {
51
- const raw = this.storage.getItem(key);
52
- if (raw === null) return null;
53
- try {
54
- return JSON.parse(raw) as T;
55
- } catch {
56
- return raw as unknown as T;
57
- }
58
- }
59
-
60
- async set<T>(key: string, value: T): Promise<void> {
61
- const serialized = typeof value === 'string' ? value : JSON.stringify(value);
62
- this.storage.setItem(key, serialized);
63
- }
64
-
65
- async remove(key: string): Promise<void> {
66
- this.storage.removeItem(key);
67
- }
68
-
69
- async clear(): Promise<void> {
70
- this.storage.clear();
71
- }
72
-
73
- async keys(): Promise<string[]> {
74
- return Object.keys(this.storage);
75
- }
76
- }
77
-
78
- /**
79
- * localStorage adapter with async interface.
80
- */
81
- class LocalStorageAdapter extends WebStorageAdapter {
82
- constructor() {
83
- super(localStorage);
84
- }
85
- }
86
-
87
- /**
88
- * sessionStorage adapter with async interface.
89
- */
90
- class SessionStorageAdapter extends WebStorageAdapter {
91
- constructor() {
92
- super(sessionStorage);
93
- }
94
- }
95
-
96
- /**
97
- * IndexedDB configuration options.
98
- */
99
- export interface IndexedDBOptions {
100
- /** Database name */
101
- name: string;
102
- /** Object store name */
103
- store: string;
104
- /** Database version (optional) */
105
- version?: number;
106
- }
107
-
108
- /**
109
- * IndexedDB key-value adapter.
110
- * Wraps IndexedDB with a simple key-value interface.
111
- */
112
- class IndexedDBAdapter implements StorageAdapter {
113
- private dbPromise: Promise<IDBDatabase> | null = null;
114
-
115
- constructor(private readonly options: IndexedDBOptions) {}
116
-
117
- /**
118
- * Opens or creates the IndexedDB database.
119
- */
120
- private openDB(): Promise<IDBDatabase> {
121
- if (this.dbPromise) return this.dbPromise;
122
-
123
- this.dbPromise = new Promise((resolve, reject) => {
124
- const request = indexedDB.open(this.options.name, this.options.version ?? 1);
125
-
126
- request.onupgradeneeded = () => {
127
- const db = request.result;
128
- if (!db.objectStoreNames.contains(this.options.store)) {
129
- db.createObjectStore(this.options.store);
130
- }
131
- };
132
-
133
- request.onsuccess = () => resolve(request.result);
134
- request.onerror = () => reject(request.error);
135
- });
136
-
137
- return this.dbPromise;
138
- }
139
-
140
- /**
141
- * Executes a transaction on the object store.
142
- */
143
- private async withStore<T>(
144
- mode: IDBTransactionMode,
145
- operation: (store: IDBObjectStore) => IDBRequest<T>
146
- ): Promise<T> {
147
- const db = await this.openDB();
148
- return new Promise((resolve, reject) => {
149
- const tx = db.transaction(this.options.store, mode);
150
- const store = tx.objectStore(this.options.store);
151
- const request = operation(store);
152
- request.onsuccess = () => resolve(request.result);
153
- request.onerror = () => reject(request.error);
154
- });
155
- }
156
-
157
- async get<T>(key: string): Promise<T | null> {
158
- const result = await this.withStore<T | undefined>('readonly', (store) => store.get(key));
159
- return result ?? null;
160
- }
161
-
162
- async set<T>(key: string, value: T): Promise<void> {
163
- await this.withStore('readwrite', (store) => store.put(value, key));
164
- }
165
-
166
- async remove(key: string): Promise<void> {
167
- await this.withStore('readwrite', (store) => store.delete(key));
168
- }
169
-
170
- async clear(): Promise<void> {
171
- await this.withStore('readwrite', (store) => store.clear());
172
- }
173
-
174
- async keys(): Promise<string[]> {
175
- const result = await this.withStore<IDBValidKey[]>('readonly', (store) => store.getAllKeys());
176
- return result.map((key) => String(key));
177
- }
178
- }
179
-
180
- /**
181
- * Storage factory providing access to different storage adapters.
182
- */
183
- export const storage = {
184
- /**
185
- * Create a localStorage adapter.
186
- * @returns StorageAdapter wrapping localStorage
187
- */
188
- local(): StorageAdapter {
189
- return new LocalStorageAdapter();
190
- },
191
-
192
- /**
193
- * Create a sessionStorage adapter.
194
- * @returns StorageAdapter wrapping sessionStorage
195
- */
196
- session(): StorageAdapter {
197
- return new SessionStorageAdapter();
198
- },
199
-
200
- /**
201
- * Create an IndexedDB adapter with key-value interface.
202
- * @param options - Database and store configuration
203
- * @returns StorageAdapter wrapping IndexedDB
204
- */
205
- indexedDB(options: IndexedDBOptions): StorageAdapter {
206
- return new IndexedDBAdapter(options);
207
- },
208
- };
1
+ /**
2
+ * Unified storage adapters for web platform storage APIs.
3
+ * Provides a consistent, promise-based interface with predictable errors.
4
+ */
5
+
6
+ /**
7
+ * Common interface for all storage adapters.
8
+ * All methods return promises for a unified async API.
9
+ */
10
+ export interface StorageAdapter {
11
+ /**
12
+ * Retrieve a value by key.
13
+ * @param key - The storage key
14
+ * @returns The stored value or null if not found
15
+ */
16
+ get<T>(key: string): Promise<T | null>;
17
+
18
+ /**
19
+ * Store a value by key.
20
+ * @param key - The storage key
21
+ * @param value - The value to store
22
+ */
23
+ set<T>(key: string, value: T): Promise<void>;
24
+
25
+ /**
26
+ * Remove a value by key.
27
+ * @param key - The storage key
28
+ */
29
+ remove(key: string): Promise<void>;
30
+
31
+ /**
32
+ * Clear all stored values.
33
+ */
34
+ clear(): Promise<void>;
35
+
36
+ /**
37
+ * Get all storage keys.
38
+ * @returns Array of all keys
39
+ */
40
+ keys(): Promise<string[]>;
41
+ }
42
+
43
+ /**
44
+ * Abstract base class for web storage adapters (localStorage/sessionStorage).
45
+ * Implements DRY principle by sharing common logic.
46
+ */
47
+ abstract class WebStorageAdapter implements StorageAdapter {
48
+ constructor(protected readonly storage: Storage) {}
49
+
50
+ async get<T>(key: string): Promise<T | null> {
51
+ const raw = this.storage.getItem(key);
52
+ if (raw === null) return null;
53
+ try {
54
+ return JSON.parse(raw) as T;
55
+ } catch {
56
+ return raw as unknown as T;
57
+ }
58
+ }
59
+
60
+ async set<T>(key: string, value: T): Promise<void> {
61
+ const serialized = typeof value === 'string' ? value : JSON.stringify(value);
62
+ this.storage.setItem(key, serialized);
63
+ }
64
+
65
+ async remove(key: string): Promise<void> {
66
+ this.storage.removeItem(key);
67
+ }
68
+
69
+ async clear(): Promise<void> {
70
+ this.storage.clear();
71
+ }
72
+
73
+ async keys(): Promise<string[]> {
74
+ const result: string[] = [];
75
+ for (let i = 0; i < this.storage.length; i++) {
76
+ const key = this.storage.key(i);
77
+ if (key !== null) {
78
+ result.push(key);
79
+ }
80
+ }
81
+ return result;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * localStorage adapter with async interface.
87
+ */
88
+ class LocalStorageAdapter extends WebStorageAdapter {
89
+ constructor() {
90
+ super(localStorage);
91
+ }
92
+ }
93
+
94
+ /**
95
+ * sessionStorage adapter with async interface.
96
+ */
97
+ class SessionStorageAdapter extends WebStorageAdapter {
98
+ constructor() {
99
+ super(sessionStorage);
100
+ }
101
+ }
102
+
103
+ /**
104
+ * IndexedDB configuration options.
105
+ */
106
+ export interface IndexedDBOptions {
107
+ /** Database name */
108
+ name: string;
109
+ /** Object store name */
110
+ store: string;
111
+ /** Database version (optional) */
112
+ version?: number;
113
+ }
114
+
115
+ /**
116
+ * IndexedDB key-value adapter.
117
+ * Wraps IndexedDB with a simple key-value interface.
118
+ */
119
+ class IndexedDBAdapter implements StorageAdapter {
120
+ private dbPromise: Promise<IDBDatabase> | null = null;
121
+
122
+ constructor(private readonly options: IndexedDBOptions) {}
123
+
124
+ /**
125
+ * Opens or creates the IndexedDB database.
126
+ */
127
+ private openDB(): Promise<IDBDatabase> {
128
+ if (this.dbPromise) return this.dbPromise;
129
+
130
+ this.dbPromise = new Promise((resolve, reject) => {
131
+ const request = indexedDB.open(this.options.name, this.options.version ?? 1);
132
+
133
+ request.onupgradeneeded = () => {
134
+ const db = request.result;
135
+ if (!db.objectStoreNames.contains(this.options.store)) {
136
+ db.createObjectStore(this.options.store);
137
+ }
138
+ };
139
+
140
+ request.onsuccess = () => resolve(request.result);
141
+ request.onerror = () => reject(request.error);
142
+ });
143
+
144
+ return this.dbPromise;
145
+ }
146
+
147
+ /**
148
+ * Executes a transaction on the object store.
149
+ */
150
+ private async withStore<T>(
151
+ mode: IDBTransactionMode,
152
+ operation: (store: IDBObjectStore) => IDBRequest<T>
153
+ ): Promise<T> {
154
+ const db = await this.openDB();
155
+ return new Promise((resolve, reject) => {
156
+ const tx = db.transaction(this.options.store, mode);
157
+ const store = tx.objectStore(this.options.store);
158
+ const request = operation(store);
159
+ request.onsuccess = () => resolve(request.result);
160
+ request.onerror = () => reject(request.error);
161
+ });
162
+ }
163
+
164
+ async get<T>(key: string): Promise<T | null> {
165
+ const result = await this.withStore<T | undefined>('readonly', (store) => store.get(key));
166
+ return result ?? null;
167
+ }
168
+
169
+ async set<T>(key: string, value: T): Promise<void> {
170
+ await this.withStore('readwrite', (store) => store.put(value, key));
171
+ }
172
+
173
+ async remove(key: string): Promise<void> {
174
+ await this.withStore('readwrite', (store) => store.delete(key));
175
+ }
176
+
177
+ async clear(): Promise<void> {
178
+ await this.withStore('readwrite', (store) => store.clear());
179
+ }
180
+
181
+ async keys(): Promise<string[]> {
182
+ const result = await this.withStore<IDBValidKey[]>('readonly', (store) => store.getAllKeys());
183
+ return result.map((key) => String(key));
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Storage factory providing access to different storage adapters.
189
+ */
190
+ export const storage = {
191
+ /**
192
+ * Create a localStorage adapter.
193
+ * @returns StorageAdapter wrapping localStorage
194
+ */
195
+ local(): StorageAdapter {
196
+ return new LocalStorageAdapter();
197
+ },
198
+
199
+ /**
200
+ * Create a sessionStorage adapter.
201
+ * @returns StorageAdapter wrapping sessionStorage
202
+ */
203
+ session(): StorageAdapter {
204
+ return new SessionStorageAdapter();
205
+ },
206
+
207
+ /**
208
+ * Create an IndexedDB adapter with key-value interface.
209
+ * @param options - Database and store configuration
210
+ * @returns StorageAdapter wrapping IndexedDB
211
+ */
212
+ indexedDB(options: IndexedDBOptions): StorageAdapter {
213
+ return new IndexedDBAdapter(options);
214
+ },
215
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Batched reactive updates.
3
+ */
4
+
5
+ import { beginBatch, endBatch } from './internals';
6
+
7
+ /**
8
+ * Batches multiple signal updates into a single notification cycle.
9
+ *
10
+ * Updates made inside the batch function are deferred until the batch
11
+ * completes, preventing intermediate re-renders and improving performance.
12
+ *
13
+ * @param fn - Function containing multiple signal updates
14
+ */
15
+ export const batch = (fn: () => void): void => {
16
+ beginBatch();
17
+ try {
18
+ fn();
19
+ } finally {
20
+ endBatch();
21
+ }
22
+ };
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Computed reactive values.
3
+ */
4
+
5
+ import {
6
+ clearDependencies,
7
+ getCurrentObserver,
8
+ registerDependency,
9
+ scheduleObserver,
10
+ track,
11
+ type ReactiveSource,
12
+ } from './internals';
13
+
14
+ /**
15
+ * A computed value that derives from other reactive sources.
16
+ *
17
+ * Computed values are lazily evaluated and cached. They only
18
+ * recompute when their dependencies change.
19
+ *
20
+ * @template T - The type of the computed value
21
+ */
22
+ export class Computed<T> implements ReactiveSource {
23
+ private cachedValue!: T;
24
+ private dirty = true;
25
+ private subscribers = new Set<() => void>();
26
+ private readonly markDirty = () => {
27
+ this.dirty = true;
28
+ // Create snapshot to avoid issues with subscribers modifying the set during iteration
29
+ const subscribersSnapshot = Array.from(this.subscribers);
30
+ for (const subscriber of subscribersSnapshot) {
31
+ scheduleObserver(subscriber);
32
+ }
33
+ };
34
+
35
+ /**
36
+ * Creates a new computed value.
37
+ * @param compute - Function that computes the value
38
+ */
39
+ constructor(private readonly compute: () => T) {}
40
+
41
+ /**
42
+ * Gets the computed value, recomputing if dependencies changed.
43
+ * During untrack calls, getCurrentObserver returns undefined, preventing dependency tracking.
44
+ */
45
+ get value(): T {
46
+ const current = getCurrentObserver();
47
+ if (current) {
48
+ this.subscribers.add(current);
49
+ registerDependency(current, this);
50
+ }
51
+ if (this.dirty) {
52
+ this.dirty = false;
53
+ // Clear old dependencies before recomputing
54
+ clearDependencies(this.markDirty);
55
+ this.cachedValue = track(this.markDirty, this.compute);
56
+ }
57
+ return this.cachedValue;
58
+ }
59
+
60
+ /**
61
+ * Reads the current computed value without tracking.
62
+ * Useful when you need the value but don't want to create a dependency.
63
+ *
64
+ * @returns The current cached value (recomputes if dirty)
65
+ */
66
+ peek(): T {
67
+ if (this.dirty) {
68
+ this.dirty = false;
69
+ // Clear old dependencies before recomputing
70
+ clearDependencies(this.markDirty);
71
+ this.cachedValue = track(this.markDirty, this.compute);
72
+ }
73
+ return this.cachedValue;
74
+ }
75
+
76
+ /**
77
+ * Removes an observer from this computed's subscriber set.
78
+ * @internal
79
+ */
80
+ unsubscribe(observer: () => void): void {
81
+ this.subscribers.delete(observer);
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Creates a new computed value.
87
+ *
88
+ * @template T - The type of the computed value
89
+ * @param fn - Function that computes the value from reactive sources
90
+ * @returns A new Computed instance
91
+ */
92
+ export const computed = <T>(fn: () => T): Computed<T> => new Computed(fn);
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Core reactive signals.
3
+ */
4
+
5
+ import {
6
+ getCurrentObserver,
7
+ registerDependency,
8
+ removeDependency,
9
+ scheduleObserver,
10
+ type ReactiveSource,
11
+ } from './internals';
12
+
13
+ /**
14
+ * A reactive value container that notifies subscribers on change.
15
+ *
16
+ * Signals are the foundational primitive of the reactive system.
17
+ * Reading a signal's value inside an effect or computed automatically
18
+ * establishes a reactive dependency.
19
+ *
20
+ * @template T - The type of the stored value
21
+ */
22
+ export class Signal<T> implements ReactiveSource {
23
+ private subscribers = new Set<() => void>();
24
+
25
+ /**
26
+ * Creates a new signal with an initial value.
27
+ * @param _value - The initial value
28
+ */
29
+ constructor(private _value: T) {}
30
+
31
+ /**
32
+ * Gets the current value and tracks the read if inside an observer.
33
+ * During untrack calls, getCurrentObserver returns undefined, preventing dependency tracking.
34
+ */
35
+ get value(): T {
36
+ const current = getCurrentObserver();
37
+ if (current) {
38
+ this.subscribers.add(current);
39
+ registerDependency(current, this);
40
+ }
41
+ return this._value;
42
+ }
43
+
44
+ /**
45
+ * Sets a new value and notifies all subscribers if the value changed.
46
+ * Uses Object.is for equality comparison.
47
+ */
48
+ set value(next: T) {
49
+ if (Object.is(this._value, next)) return;
50
+ this._value = next;
51
+ // Create snapshot to avoid issues with subscribers modifying the set during iteration
52
+ const subscribersSnapshot = Array.from(this.subscribers);
53
+ for (const subscriber of subscribersSnapshot) {
54
+ scheduleObserver(subscriber);
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Reads the current value without tracking.
60
+ * Useful when you need the value but don't want to create a dependency.
61
+ *
62
+ * @returns The current value
63
+ */
64
+ peek(): T {
65
+ return this._value;
66
+ }
67
+
68
+ /**
69
+ * Updates the value using a function.
70
+ * Useful for updates based on the current value.
71
+ *
72
+ * @param updater - Function that receives current value and returns new value
73
+ */
74
+ update(updater: (current: T) => T): void {
75
+ this.value = updater(this._value);
76
+ }
77
+
78
+ /**
79
+ * Removes all subscribers from this signal.
80
+ * Use this when a signal is no longer needed to prevent memory leaks.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * const count = signal(0);
85
+ * effect(() => console.log(count.value));
86
+ * count.dispose(); // All subscribers removed
87
+ * ```
88
+ */
89
+ dispose(): void {
90
+ // Remove this signal from each subscriber's dependency set
91
+ // so the observer no longer holds a strong reference to it
92
+ for (const subscriber of this.subscribers) {
93
+ removeDependency(subscriber, this);
94
+ }
95
+ this.subscribers.clear();
96
+ }
97
+
98
+ /**
99
+ * Removes an observer from this signal's subscriber set.
100
+ * @internal
101
+ */
102
+ unsubscribe(observer: () => void): void {
103
+ this.subscribers.delete(observer);
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Creates a new reactive signal.
109
+ *
110
+ * @template T - The type of the signal value
111
+ * @param value - The initial value
112
+ * @returns A new Signal instance
113
+ */
114
+ export const signal = <T>(value: T): Signal<T> => new Signal(value);