@bquery/bquery 1.1.2 → 1.3.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 (307) hide show
  1. package/README.md +501 -323
  2. package/dist/batch-4LAvfLE7.js +13 -0
  3. package/dist/batch-4LAvfLE7.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 +36 -0
  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 +8 -0
  21. package/dist/core/element.d.ts.map +1 -1
  22. package/dist/core/index.d.ts +1 -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 +70 -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-COenAZjD.js +145 -0
  41. package/dist/core-COenAZjD.js.map +1 -0
  42. package/dist/core.es.mjs +411 -448
  43. package/dist/core.es.mjs.map +1 -1
  44. package/dist/full.d.ts +8 -2
  45. package/dist/full.d.ts.map +1 -1
  46. package/dist/full.es.mjs +86 -40
  47. package/dist/full.es.mjs.map +1 -1
  48. package/dist/full.iife.js +6 -1
  49. package/dist/full.iife.js.map +1 -1
  50. package/dist/full.umd.js +6 -1
  51. package/dist/full.umd.js.map +1 -1
  52. package/dist/index.d.ts +3 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.es.mjs +137 -44
  55. package/dist/index.es.mjs.map +1 -1
  56. package/dist/motion/animate.d.ts +25 -0
  57. package/dist/motion/animate.d.ts.map +1 -0
  58. package/dist/motion/easing.d.ts +30 -0
  59. package/dist/motion/easing.d.ts.map +1 -0
  60. package/dist/motion/flip.d.ts +55 -0
  61. package/dist/motion/flip.d.ts.map +1 -0
  62. package/dist/motion/index.d.ts +11 -138
  63. package/dist/motion/index.d.ts.map +1 -1
  64. package/dist/motion/keyframes.d.ts +21 -0
  65. package/dist/motion/keyframes.d.ts.map +1 -0
  66. package/dist/motion/reduced-motion.d.ts +12 -0
  67. package/dist/motion/reduced-motion.d.ts.map +1 -0
  68. package/dist/motion/scroll.d.ts +15 -0
  69. package/dist/motion/scroll.d.ts.map +1 -0
  70. package/dist/motion/spring.d.ts +42 -0
  71. package/dist/motion/spring.d.ts.map +1 -0
  72. package/dist/motion/stagger.d.ts +22 -0
  73. package/dist/motion/stagger.d.ts.map +1 -0
  74. package/dist/motion/timeline.d.ts +21 -0
  75. package/dist/motion/timeline.d.ts.map +1 -0
  76. package/dist/motion/transition.d.ts +22 -0
  77. package/dist/motion/transition.d.ts.map +1 -0
  78. package/dist/motion/types.d.ts +182 -0
  79. package/dist/motion/types.d.ts.map +1 -0
  80. package/dist/motion.es.mjs +320 -61
  81. package/dist/motion.es.mjs.map +1 -1
  82. package/dist/persisted-Dz_ryNuC.js +278 -0
  83. package/dist/persisted-Dz_ryNuC.js.map +1 -0
  84. package/dist/reactive/batch.d.ts +13 -0
  85. package/dist/reactive/batch.d.ts.map +1 -0
  86. package/dist/reactive/computed.d.ts +50 -0
  87. package/dist/reactive/computed.d.ts.map +1 -0
  88. package/dist/reactive/core.d.ts +60 -0
  89. package/dist/reactive/core.d.ts.map +1 -0
  90. package/dist/reactive/effect.d.ts +15 -0
  91. package/dist/reactive/effect.d.ts.map +1 -0
  92. package/dist/reactive/index.d.ts +2 -2
  93. package/dist/reactive/index.d.ts.map +1 -1
  94. package/dist/reactive/internals.d.ts +36 -0
  95. package/dist/reactive/internals.d.ts.map +1 -0
  96. package/dist/reactive/linked.d.ts +36 -0
  97. package/dist/reactive/linked.d.ts.map +1 -0
  98. package/dist/reactive/persisted.d.ts +14 -0
  99. package/dist/reactive/persisted.d.ts.map +1 -0
  100. package/dist/reactive/readonly.d.ts +26 -0
  101. package/dist/reactive/readonly.d.ts.map +1 -0
  102. package/dist/reactive/signal.d.ts +13 -305
  103. package/dist/reactive/signal.d.ts.map +1 -1
  104. package/dist/reactive/type-guards.d.ts +20 -0
  105. package/dist/reactive/type-guards.d.ts.map +1 -0
  106. package/dist/reactive/untrack.d.ts +29 -0
  107. package/dist/reactive/untrack.d.ts.map +1 -0
  108. package/dist/reactive/watch.d.ts +42 -0
  109. package/dist/reactive/watch.d.ts.map +1 -0
  110. package/dist/reactive.es.mjs +30 -154
  111. package/dist/reactive.es.mjs.map +1 -1
  112. package/dist/router/index.d.ts +41 -0
  113. package/dist/router/index.d.ts.map +1 -0
  114. package/dist/router/links.d.ts +44 -0
  115. package/dist/router/links.d.ts.map +1 -0
  116. package/dist/router/match.d.ts +20 -0
  117. package/dist/router/match.d.ts.map +1 -0
  118. package/dist/router/navigation.d.ts +45 -0
  119. package/dist/router/navigation.d.ts.map +1 -0
  120. package/dist/router/query.d.ts +16 -0
  121. package/dist/router/query.d.ts.map +1 -0
  122. package/dist/router/router.d.ts +34 -0
  123. package/dist/router/router.d.ts.map +1 -0
  124. package/dist/router/state.d.ts +27 -0
  125. package/dist/router/state.d.ts.map +1 -0
  126. package/dist/router/types.d.ts +88 -0
  127. package/dist/router/types.d.ts.map +1 -0
  128. package/dist/router/utils.d.ts +65 -0
  129. package/dist/router/utils.d.ts.map +1 -0
  130. package/dist/router.es.mjs +202 -0
  131. package/dist/router.es.mjs.map +1 -0
  132. package/dist/sanitize-1FBEPAFH.js +272 -0
  133. package/dist/sanitize-1FBEPAFH.js.map +1 -0
  134. package/dist/security/constants.d.ts +42 -0
  135. package/dist/security/constants.d.ts.map +1 -0
  136. package/dist/security/csp.d.ts +24 -0
  137. package/dist/security/csp.d.ts.map +1 -0
  138. package/dist/security/index.d.ts +4 -2
  139. package/dist/security/index.d.ts.map +1 -1
  140. package/dist/security/sanitize-core.d.ts +13 -0
  141. package/dist/security/sanitize-core.d.ts.map +1 -0
  142. package/dist/security/sanitize.d.ts +5 -57
  143. package/dist/security/sanitize.d.ts.map +1 -1
  144. package/dist/security/trusted-types.d.ts +25 -0
  145. package/dist/security/trusted-types.d.ts.map +1 -0
  146. package/dist/security/types.d.ts +36 -0
  147. package/dist/security/types.d.ts.map +1 -0
  148. package/dist/security.es.mjs +50 -277
  149. package/dist/security.es.mjs.map +1 -1
  150. package/dist/store/create-store.d.ts +15 -0
  151. package/dist/store/create-store.d.ts.map +1 -0
  152. package/dist/store/define-store.d.ts +28 -0
  153. package/dist/store/define-store.d.ts.map +1 -0
  154. package/dist/store/devtools.d.ts +22 -0
  155. package/dist/store/devtools.d.ts.map +1 -0
  156. package/dist/store/index.d.ts +12 -0
  157. package/dist/store/index.d.ts.map +1 -0
  158. package/dist/store/mapping.d.ts +28 -0
  159. package/dist/store/mapping.d.ts.map +1 -0
  160. package/dist/store/persisted.d.ts +13 -0
  161. package/dist/store/persisted.d.ts.map +1 -0
  162. package/dist/store/plugins.d.ts +13 -0
  163. package/dist/store/plugins.d.ts.map +1 -0
  164. package/dist/store/registry.d.ts +28 -0
  165. package/dist/store/registry.d.ts.map +1 -0
  166. package/dist/store/types.d.ts +71 -0
  167. package/dist/store/types.d.ts.map +1 -0
  168. package/dist/store/utils.d.ts +28 -0
  169. package/dist/store/utils.d.ts.map +1 -0
  170. package/dist/store/watch.d.ts +23 -0
  171. package/dist/store/watch.d.ts.map +1 -0
  172. package/dist/store.es.mjs +27 -0
  173. package/dist/store.es.mjs.map +1 -0
  174. package/dist/type-guards-DRma3-Kc.js +16 -0
  175. package/dist/type-guards-DRma3-Kc.js.map +1 -0
  176. package/dist/untrack-BuEQKH7_.js +6 -0
  177. package/dist/untrack-BuEQKH7_.js.map +1 -0
  178. package/dist/view/directives/bind.d.ts +7 -0
  179. package/dist/view/directives/bind.d.ts.map +1 -0
  180. package/dist/view/directives/class.d.ts +8 -0
  181. package/dist/view/directives/class.d.ts.map +1 -0
  182. package/dist/view/directives/for.d.ts +23 -0
  183. package/dist/view/directives/for.d.ts.map +1 -0
  184. package/dist/view/directives/html.d.ts +7 -0
  185. package/dist/view/directives/html.d.ts.map +1 -0
  186. package/dist/view/directives/if.d.ts +7 -0
  187. package/dist/view/directives/if.d.ts.map +1 -0
  188. package/dist/view/directives/index.d.ts +12 -0
  189. package/dist/view/directives/index.d.ts.map +1 -0
  190. package/dist/view/directives/model.d.ts +7 -0
  191. package/dist/view/directives/model.d.ts.map +1 -0
  192. package/dist/view/directives/on.d.ts +7 -0
  193. package/dist/view/directives/on.d.ts.map +1 -0
  194. package/dist/view/directives/ref.d.ts +7 -0
  195. package/dist/view/directives/ref.d.ts.map +1 -0
  196. package/dist/view/directives/show.d.ts +7 -0
  197. package/dist/view/directives/show.d.ts.map +1 -0
  198. package/dist/view/directives/style.d.ts +7 -0
  199. package/dist/view/directives/style.d.ts.map +1 -0
  200. package/dist/view/directives/text.d.ts +7 -0
  201. package/dist/view/directives/text.d.ts.map +1 -0
  202. package/dist/view/evaluate.d.ts +43 -0
  203. package/dist/view/evaluate.d.ts.map +1 -0
  204. package/dist/view/index.d.ts +111 -0
  205. package/dist/view/index.d.ts.map +1 -0
  206. package/dist/view/mount.d.ts +69 -0
  207. package/dist/view/mount.d.ts.map +1 -0
  208. package/dist/view/process.d.ts +26 -0
  209. package/dist/view/process.d.ts.map +1 -0
  210. package/dist/view/types.d.ts +36 -0
  211. package/dist/view/types.d.ts.map +1 -0
  212. package/dist/view.es.mjs +426 -0
  213. package/dist/view.es.mjs.map +1 -0
  214. package/dist/watch-CXyaBC_9.js +58 -0
  215. package/dist/watch-CXyaBC_9.js.map +1 -0
  216. package/package.json +26 -14
  217. package/src/component/component.ts +289 -0
  218. package/src/component/html.ts +53 -0
  219. package/src/component/index.ts +40 -414
  220. package/src/component/props.ts +116 -0
  221. package/src/component/types.ts +85 -0
  222. package/src/core/collection.ts +588 -454
  223. package/src/core/dom.ts +38 -0
  224. package/src/core/element.ts +746 -740
  225. package/src/core/index.ts +43 -0
  226. package/src/core/utils/array.ts +102 -0
  227. package/src/core/utils/function.ts +110 -0
  228. package/src/core/utils/index.ts +83 -0
  229. package/src/core/utils/misc.ts +82 -0
  230. package/src/core/utils/number.ts +78 -0
  231. package/src/core/utils/object.ts +206 -0
  232. package/src/core/utils/string.ts +112 -0
  233. package/src/core/utils/type-guards.ts +112 -0
  234. package/src/full.ts +187 -106
  235. package/src/index.ts +36 -27
  236. package/src/motion/animate.ts +113 -0
  237. package/src/motion/easing.ts +40 -0
  238. package/src/motion/flip.ts +176 -0
  239. package/src/motion/index.ts +41 -358
  240. package/src/motion/keyframes.ts +46 -0
  241. package/src/motion/reduced-motion.ts +17 -0
  242. package/src/motion/scroll.ts +57 -0
  243. package/src/motion/spring.ts +150 -0
  244. package/src/motion/stagger.ts +43 -0
  245. package/src/motion/timeline.ts +246 -0
  246. package/src/motion/transition.ts +51 -0
  247. package/src/motion/types.ts +198 -0
  248. package/src/reactive/batch.ts +22 -0
  249. package/src/reactive/computed.ts +92 -0
  250. package/src/reactive/core.ts +93 -0
  251. package/src/reactive/effect.ts +43 -0
  252. package/src/reactive/index.ts +23 -22
  253. package/src/reactive/internals.ts +105 -0
  254. package/src/reactive/linked.ts +56 -0
  255. package/src/reactive/persisted.ts +74 -0
  256. package/src/reactive/readonly.ts +35 -0
  257. package/src/reactive/signal.ts +20 -506
  258. package/src/reactive/type-guards.ts +22 -0
  259. package/src/reactive/untrack.ts +31 -0
  260. package/src/reactive/watch.ts +73 -0
  261. package/src/router/index.ts +41 -0
  262. package/src/router/links.ts +130 -0
  263. package/src/router/match.ts +106 -0
  264. package/src/router/navigation.ts +71 -0
  265. package/src/router/query.ts +35 -0
  266. package/src/router/router.ts +211 -0
  267. package/src/router/state.ts +46 -0
  268. package/src/router/types.ts +93 -0
  269. package/src/router/utils.ts +116 -0
  270. package/src/security/constants.ts +209 -0
  271. package/src/security/csp.ts +77 -0
  272. package/src/security/index.ts +4 -12
  273. package/src/security/sanitize-core.ts +343 -0
  274. package/src/security/sanitize.ts +66 -625
  275. package/src/security/trusted-types.ts +69 -0
  276. package/src/security/types.ts +40 -0
  277. package/src/store/create-store.ts +329 -0
  278. package/src/store/define-store.ts +48 -0
  279. package/src/store/devtools.ts +45 -0
  280. package/src/store/index.ts +22 -0
  281. package/src/store/mapping.ts +73 -0
  282. package/src/store/persisted.ts +61 -0
  283. package/src/store/plugins.ts +32 -0
  284. package/src/store/registry.ts +51 -0
  285. package/src/store/types.ts +94 -0
  286. package/src/store/utils.ts +141 -0
  287. package/src/store/watch.ts +52 -0
  288. package/src/view/directives/bind.ts +23 -0
  289. package/src/view/directives/class.ts +70 -0
  290. package/src/view/directives/for.ts +275 -0
  291. package/src/view/directives/html.ts +19 -0
  292. package/src/view/directives/if.ts +30 -0
  293. package/src/view/directives/index.ts +11 -0
  294. package/src/view/directives/model.ts +56 -0
  295. package/src/view/directives/on.ts +41 -0
  296. package/src/view/directives/ref.ts +41 -0
  297. package/src/view/directives/show.ts +26 -0
  298. package/src/view/directives/style.ts +47 -0
  299. package/src/view/directives/text.ts +15 -0
  300. package/src/view/evaluate.ts +274 -0
  301. package/src/view/index.ts +112 -0
  302. package/src/view/mount.ts +200 -0
  303. package/src/view/process.ts +92 -0
  304. package/src/view/types.ts +44 -0
  305. package/dist/core/utils.d.ts +0 -313
  306. package/dist/core/utils.d.ts.map +0 -1
  307. package/src/core/utils.ts +0 -444
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Store plugins API.
3
+ */
4
+
5
+ import type { Store, StoreDefinition, StorePlugin } from './types';
6
+
7
+ /** @internal Registered plugins */
8
+ const plugins: StorePlugin[] = [];
9
+
10
+ /**
11
+ * Registers a plugin that extends all stores.
12
+ *
13
+ * @param plugin - The plugin function
14
+ */
15
+ export const registerPlugin = (plugin: StorePlugin): void => {
16
+ plugins.push(plugin);
17
+ };
18
+
19
+ /** @internal */
20
+ export const applyPlugins = (
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ store: Store<any, any, any>,
23
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
+ options: StoreDefinition<any, any, any>
25
+ ): void => {
26
+ for (const plugin of plugins) {
27
+ const extension = plugin({ store, options });
28
+ if (extension) {
29
+ Object.assign(store, extension);
30
+ }
31
+ }
32
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Store registry utilities.
3
+ */
4
+
5
+ import { unregisterDevtoolsStore } from './devtools';
6
+ import type { Store } from './types';
7
+
8
+ /** @internal Registry of all stores for devtools */
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ const storeRegistry = new Map<string, Store<any, any, any>>();
11
+
12
+ /** @internal */
13
+ export const hasStore = (id: string): boolean => storeRegistry.has(id);
14
+
15
+ /** @internal */
16
+ export const registerStore = (
17
+ id: string,
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
+ store: Store<any, any, any>
20
+ ): void => {
21
+ storeRegistry.set(id, store);
22
+ };
23
+
24
+ /**
25
+ * Retrieves an existing store by its ID.
26
+ *
27
+ * @param id - The store identifier
28
+ * @returns The store instance or undefined if not found
29
+ */
30
+ export const getStore = <T = unknown>(id: string): T | undefined => {
31
+ return storeRegistry.get(id) as T | undefined;
32
+ };
33
+
34
+ /**
35
+ * Lists all registered store IDs.
36
+ *
37
+ * @returns Array of store IDs
38
+ */
39
+ export const listStores = (): string[] => {
40
+ return Array.from(storeRegistry.keys());
41
+ };
42
+
43
+ /**
44
+ * Removes a store from the registry.
45
+ *
46
+ * @param id - The store identifier
47
+ */
48
+ export const destroyStore = (id: string): void => {
49
+ storeRegistry.delete(id);
50
+ unregisterDevtoolsStore(id);
51
+ };
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Store types for bQuery's state module.
3
+ * @module bquery/store
4
+ */
5
+
6
+ /**
7
+ * Store state factory function.
8
+ */
9
+ export type StateFactory<S> = () => S;
10
+
11
+ /**
12
+ * Getter definition - derives computed values from state.
13
+ */
14
+ export type Getters<S, G> = {
15
+ [K in keyof G]: (state: S, getters: G) => G[K];
16
+ };
17
+
18
+ /**
19
+ * Action definition - methods that can modify state.
20
+ * The `this` context includes state, getters, and other actions.
21
+ */
22
+ export type Actions<S, G, A> = {
23
+ [K in keyof A]: A[K] extends (...args: infer P) => infer R
24
+ ? (this: S & G & A, ...args: P) => R
25
+ : never;
26
+ };
27
+
28
+ /**
29
+ * Store definition for createStore.
30
+ */
31
+ export type StoreDefinition<
32
+ S extends Record<string, unknown> = Record<string, unknown>,
33
+ G extends Record<string, unknown> = Record<string, unknown>,
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ A extends Record<string, (...args: any[]) => any> = Record<string, never>,
36
+ > = {
37
+ /** Unique store identifier for devtools */
38
+ id: string;
39
+ /** State factory function */
40
+ state: StateFactory<S>;
41
+ /** Computed getters */
42
+ getters?: Getters<S, G>;
43
+ /** Action methods */
44
+ actions?: Actions<S, G, A>;
45
+ };
46
+
47
+ /**
48
+ * Store subscriber callback.
49
+ */
50
+ export type StoreSubscriber<S> = (state: S) => void;
51
+
52
+ /**
53
+ * Patch payload for store updates.
54
+ */
55
+ export type StorePatch<S> = Partial<S> | ((state: S) => void);
56
+
57
+ /**
58
+ * The returned store instance with state, getters, and actions merged.
59
+ */
60
+ export type Store<
61
+ S extends Record<string, unknown>,
62
+ G extends Record<string, unknown>,
63
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
+ A extends Record<string, (...args: any[]) => any>,
65
+ > = S &
66
+ G &
67
+ A & {
68
+ /** Store identifier */
69
+ $id: string;
70
+ /** Reset state to initial values */
71
+ $reset: () => void;
72
+ /** Subscribe to state changes */
73
+ $subscribe: (callback: StoreSubscriber<S>) => () => void;
74
+ /** Patch multiple state properties at once (shallow) */
75
+ $patch: (partial: StorePatch<S>) => void;
76
+ /**
77
+ * Patch with deep reactivity support.
78
+ * Unlike $patch, this method deep-clones nested objects before mutation,
79
+ * ensuring that all changes trigger reactive updates.
80
+ */
81
+ $patchDeep: (partial: StorePatch<S>) => void;
82
+ /** Get raw state object (non-reactive snapshot) */
83
+ $state: S;
84
+ };
85
+
86
+ /**
87
+ * Plugin that can extend store functionality.
88
+ */
89
+ export type StorePlugin<S = unknown> = (context: {
90
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
91
+ store: Store<any, any, any>;
92
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
+ options: StoreDefinition<any, any, any>;
94
+ }) => Partial<S> | void;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Internal utilities for the store module.
3
+ * @internal
4
+ */
5
+
6
+ /**
7
+ * Check if a value is a plain object (not array, null, Date, etc.).
8
+ * @internal
9
+ */
10
+ export const isPlainObject = (value: unknown): value is Record<string, unknown> => {
11
+ return (
12
+ value !== null && typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype
13
+ );
14
+ };
15
+
16
+ /**
17
+ * Deep clones an object. Used for deep reactivity support.
18
+ * @internal
19
+ */
20
+ export const deepClone = <T>(obj: T): T => {
21
+ if (obj === null || typeof obj !== 'object') {
22
+ return obj;
23
+ }
24
+
25
+ if (Array.isArray(obj)) {
26
+ return obj.map(deepClone) as T;
27
+ }
28
+
29
+ if (obj instanceof Date) {
30
+ return new Date(obj.getTime()) as T;
31
+ }
32
+
33
+ if (obj instanceof Map) {
34
+ return new Map(Array.from(obj.entries()).map(([k, v]) => [k, deepClone(v)])) as T;
35
+ }
36
+
37
+ if (obj instanceof Set) {
38
+ return new Set(Array.from(obj).map(deepClone)) as T;
39
+ }
40
+
41
+ const cloned = {} as T;
42
+ for (const key of Object.keys(obj)) {
43
+ (cloned as Record<string, unknown>)[key] = deepClone((obj as Record<string, unknown>)[key]);
44
+ }
45
+ return cloned;
46
+ };
47
+
48
+ /**
49
+ * Compares two values for deep equality.
50
+ * @internal
51
+ */
52
+ export const deepEqual = (a: unknown, b: unknown): boolean => {
53
+ if (a === b) return true;
54
+ if (a === null || b === null) return false;
55
+ if (typeof a !== 'object' || typeof b !== 'object') return false;
56
+
57
+ if (a instanceof Date && b instanceof Date) {
58
+ return a.getTime() === b.getTime();
59
+ }
60
+
61
+ if (a instanceof Map && b instanceof Map) {
62
+ if (a.size !== b.size) return false;
63
+ for (const [key, value] of a.entries()) {
64
+ if (!b.has(key) || !deepEqual(value, b.get(key))) return false;
65
+ }
66
+ return true;
67
+ }
68
+
69
+ if (a instanceof Set && b instanceof Set) {
70
+ if (a.size !== b.size) return false;
71
+ for (const value of a.values()) {
72
+ let found = false;
73
+ for (const candidate of b.values()) {
74
+ if (deepEqual(value, candidate)) {
75
+ found = true;
76
+ break;
77
+ }
78
+ }
79
+ if (!found) return false;
80
+ }
81
+ return true;
82
+ }
83
+
84
+ if (Array.isArray(a) && Array.isArray(b)) {
85
+ if (a.length !== b.length) return false;
86
+ return a.every((item, i) => deepEqual(item, b[i]));
87
+ }
88
+
89
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
90
+
91
+ const keysA = Object.keys(a as object);
92
+ const keysB = Object.keys(b as object);
93
+
94
+ if (keysA.length !== keysB.length) return false;
95
+
96
+ return keysA.every((key) =>
97
+ deepEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])
98
+ );
99
+ };
100
+
101
+ /**
102
+ * Detects if nested objects were mutated but the reference stayed the same.
103
+ * Returns the keys where nested mutations were detected.
104
+ * @internal
105
+ */
106
+ export const detectNestedMutations = <S extends Record<string, unknown>>(
107
+ before: S,
108
+ after: S,
109
+ signalValues: Map<keyof S, unknown>
110
+ ): Array<keyof S> => {
111
+ const mutatedKeys: Array<keyof S> = [];
112
+
113
+ for (const key of Object.keys(after) as Array<keyof S>) {
114
+ const beforeValue = before[key];
115
+ const afterValue = after[key];
116
+ const signalValue = signalValues.get(key);
117
+
118
+ // Check if it's the same reference but content changed
119
+ if (
120
+ signalValue === afterValue &&
121
+ isPlainObject(beforeValue) &&
122
+ isPlainObject(afterValue) &&
123
+ !deepEqual(beforeValue, afterValue)
124
+ ) {
125
+ mutatedKeys.push(key);
126
+ }
127
+ }
128
+
129
+ return mutatedKeys;
130
+ };
131
+
132
+ /** @internal Flag to enable/disable development warnings */
133
+ export const isDev = (() => {
134
+ try {
135
+ const globalProcess = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process;
136
+ // Default to dev mode unless explicitly set to production
137
+ return !(typeof globalProcess !== 'undefined' && globalProcess.env?.NODE_ENV === 'production');
138
+ } catch {
139
+ return true;
140
+ }
141
+ })();
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Store watch helpers.
3
+ */
4
+
5
+ import type { Store } from './types';
6
+ import { deepEqual } from './utils';
7
+
8
+ export type WatchStoreOptions<T> = {
9
+ /** Call the callback immediately with the current value. */
10
+ immediate?: boolean;
11
+ /** Use deep comparison when determining changes. */
12
+ deep?: boolean;
13
+ /** Custom equality check for selected values. */
14
+ equals?: (a: T, b: T) => boolean;
15
+ };
16
+
17
+ /**
18
+ * Watch a selected slice of store state.
19
+ *
20
+ * @param store - The store instance
21
+ * @param selector - Function to select the watched value
22
+ * @param callback - Called when the selected value changes
23
+ * @param options - Watch options
24
+ * @returns Unsubscribe function
25
+ */
26
+ export const watchStore = <
27
+ S extends Record<string, unknown>,
28
+ G extends Record<string, unknown>,
29
+ A extends Record<string, (...args: unknown[]) => unknown>,
30
+ T,
31
+ >(
32
+ store: Store<S, G, A>,
33
+ selector: (state: S) => T,
34
+ callback: (value: T, previous: T | undefined) => void,
35
+ options: WatchStoreOptions<T> = {}
36
+ ): (() => void) => {
37
+ const equals = options.equals ?? (options.deep ? deepEqual : Object.is);
38
+ let previous = selector(store.$state);
39
+
40
+ if (options.immediate) {
41
+ callback(previous, undefined);
42
+ }
43
+
44
+ return store.$subscribe((state) => {
45
+ const current = selector(state);
46
+ if (!equals(current, previous)) {
47
+ const prev = previous;
48
+ previous = current;
49
+ callback(current, prev);
50
+ }
51
+ });
52
+ };
@@ -0,0 +1,23 @@
1
+ import { effect } from '../../reactive/index';
2
+ import { evaluate } from '../evaluate';
3
+ import type { DirectiveHandler } from '../types';
4
+
5
+ /**
6
+ * Handles bq-bind:attr directive - attribute binding.
7
+ * @internal
8
+ */
9
+ export const handleBind = (attrName: string): DirectiveHandler => {
10
+ return (el, expression, context, cleanups) => {
11
+ const cleanup = effect(() => {
12
+ const value = evaluate(expression, context);
13
+ if (value == null || value === false) {
14
+ el.removeAttribute(attrName);
15
+ } else if (value === true) {
16
+ el.setAttribute(attrName, '');
17
+ } else {
18
+ el.setAttribute(attrName, String(value));
19
+ }
20
+ });
21
+ cleanups.push(cleanup);
22
+ };
23
+ };
@@ -0,0 +1,70 @@
1
+ import { effect } from '../../reactive/index';
2
+ import { evaluate, parseObjectExpression } from '../evaluate';
3
+ import type { DirectiveHandler } from '../types';
4
+
5
+ /**
6
+ * Handles bq-class directive - dynamic class binding.
7
+ * Tracks previously added classes to ensure proper cleanup when expressions change.
8
+ * @internal
9
+ */
10
+ export const handleClass: DirectiveHandler = (el, expression, context, cleanups) => {
11
+ // Track classes added by this directive to clean them up on re-evaluation
12
+ let previousClasses: Set<string> = new Set();
13
+
14
+ const cleanup = effect(() => {
15
+ const newClasses: Set<string> = new Set();
16
+
17
+ if (expression.trimStart().startsWith('{')) {
18
+ // Object syntax: { active: isActive, disabled: !enabled }
19
+ const classMap = parseObjectExpression(expression);
20
+ for (const [className, conditionExpr] of Object.entries(classMap)) {
21
+ const condition = evaluate<boolean>(conditionExpr, context);
22
+ el.classList.toggle(className, Boolean(condition));
23
+ // Track class regardless of condition - toggle handles add/remove
24
+ newClasses.add(className);
25
+ }
26
+ } else if (/^\s*\[/.test(expression)) {
27
+ // Array literal syntax: [class1, class2]
28
+ const classes = evaluate<string[]>(expression, context);
29
+ if (Array.isArray(classes)) {
30
+ for (const cls of classes) {
31
+ if (cls) {
32
+ el.classList.add(cls);
33
+ newClasses.add(cls);
34
+ }
35
+ }
36
+ }
37
+ } else {
38
+ // Single expression returning string or array
39
+ const result = evaluate<string | string[]>(expression, context);
40
+ if (typeof result === 'string') {
41
+ result.split(/\s+/).forEach((cls) => {
42
+ if (cls) {
43
+ el.classList.add(cls);
44
+ newClasses.add(cls);
45
+ }
46
+ });
47
+ } else if (Array.isArray(result)) {
48
+ result.forEach((cls) => {
49
+ if (cls) {
50
+ el.classList.add(cls);
51
+ newClasses.add(cls);
52
+ }
53
+ });
54
+ }
55
+ }
56
+
57
+ // Remove classes that were previously added but are no longer in the new set
58
+ // This keeps directive-managed classes in sync across all syntax forms and provides
59
+ // defensive cleanup behavior for edge cases (e.g. external classList changes)
60
+ for (const cls of previousClasses) {
61
+ if (!newClasses.has(cls)) {
62
+ el.classList.remove(cls);
63
+ }
64
+ }
65
+
66
+ previousClasses = newClasses;
67
+ });
68
+
69
+ cleanups.push(cleanup);
70
+ };