@bquery/bquery 1.2.0 → 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 (305) hide show
  1. package/README.md +501 -427
  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 +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-Dz_ryNuC.js +278 -0
  81. package/dist/persisted-Dz_ryNuC.js.map +1 -0
  82. package/dist/reactive/batch.d.ts +13 -0
  83. package/dist/reactive/batch.d.ts.map +1 -0
  84. package/dist/reactive/computed.d.ts +50 -0
  85. package/dist/reactive/computed.d.ts.map +1 -0
  86. package/dist/reactive/core.d.ts +60 -0
  87. package/dist/reactive/core.d.ts.map +1 -0
  88. package/dist/reactive/effect.d.ts +15 -0
  89. package/dist/reactive/effect.d.ts.map +1 -0
  90. package/dist/reactive/index.d.ts +2 -2
  91. package/dist/reactive/index.d.ts.map +1 -1
  92. package/dist/reactive/internals.d.ts +36 -0
  93. package/dist/reactive/internals.d.ts.map +1 -0
  94. package/dist/reactive/linked.d.ts +36 -0
  95. package/dist/reactive/linked.d.ts.map +1 -0
  96. package/dist/reactive/persisted.d.ts +14 -0
  97. package/dist/reactive/persisted.d.ts.map +1 -0
  98. package/dist/reactive/readonly.d.ts +26 -0
  99. package/dist/reactive/readonly.d.ts.map +1 -0
  100. package/dist/reactive/signal.d.ts +13 -312
  101. package/dist/reactive/signal.d.ts.map +1 -1
  102. package/dist/reactive/type-guards.d.ts +20 -0
  103. package/dist/reactive/type-guards.d.ts.map +1 -0
  104. package/dist/reactive/untrack.d.ts +29 -0
  105. package/dist/reactive/untrack.d.ts.map +1 -0
  106. package/dist/reactive/watch.d.ts +42 -0
  107. package/dist/reactive/watch.d.ts.map +1 -0
  108. package/dist/reactive.es.mjs +30 -163
  109. package/dist/reactive.es.mjs.map +1 -1
  110. package/dist/router/index.d.ts +6 -252
  111. package/dist/router/index.d.ts.map +1 -1
  112. package/dist/router/links.d.ts +44 -0
  113. package/dist/router/links.d.ts.map +1 -0
  114. package/dist/router/match.d.ts +20 -0
  115. package/dist/router/match.d.ts.map +1 -0
  116. package/dist/router/navigation.d.ts +45 -0
  117. package/dist/router/navigation.d.ts.map +1 -0
  118. package/dist/router/query.d.ts +16 -0
  119. package/dist/router/query.d.ts.map +1 -0
  120. package/dist/router/router.d.ts +34 -0
  121. package/dist/router/router.d.ts.map +1 -0
  122. package/dist/router/state.d.ts +27 -0
  123. package/dist/router/state.d.ts.map +1 -0
  124. package/dist/router/types.d.ts +88 -0
  125. package/dist/router/types.d.ts.map +1 -0
  126. package/dist/router/utils.d.ts +65 -0
  127. package/dist/router/utils.d.ts.map +1 -0
  128. package/dist/router.es.mjs +168 -132
  129. package/dist/router.es.mjs.map +1 -1
  130. package/dist/sanitize-1FBEPAFH.js +272 -0
  131. package/dist/sanitize-1FBEPAFH.js.map +1 -0
  132. package/dist/security/constants.d.ts +42 -0
  133. package/dist/security/constants.d.ts.map +1 -0
  134. package/dist/security/csp.d.ts +24 -0
  135. package/dist/security/csp.d.ts.map +1 -0
  136. package/dist/security/index.d.ts +4 -2
  137. package/dist/security/index.d.ts.map +1 -1
  138. package/dist/security/sanitize-core.d.ts +13 -0
  139. package/dist/security/sanitize-core.d.ts.map +1 -0
  140. package/dist/security/sanitize.d.ts +5 -57
  141. package/dist/security/sanitize.d.ts.map +1 -1
  142. package/dist/security/trusted-types.d.ts +25 -0
  143. package/dist/security/trusted-types.d.ts.map +1 -0
  144. package/dist/security/types.d.ts +36 -0
  145. package/dist/security/types.d.ts.map +1 -0
  146. package/dist/security.es.mjs +50 -277
  147. package/dist/security.es.mjs.map +1 -1
  148. package/dist/store/create-store.d.ts +15 -0
  149. package/dist/store/create-store.d.ts.map +1 -0
  150. package/dist/store/define-store.d.ts +28 -0
  151. package/dist/store/define-store.d.ts.map +1 -0
  152. package/dist/store/devtools.d.ts +22 -0
  153. package/dist/store/devtools.d.ts.map +1 -0
  154. package/dist/store/index.d.ts +10 -286
  155. package/dist/store/index.d.ts.map +1 -1
  156. package/dist/store/mapping.d.ts +28 -0
  157. package/dist/store/mapping.d.ts.map +1 -0
  158. package/dist/store/persisted.d.ts +13 -0
  159. package/dist/store/persisted.d.ts.map +1 -0
  160. package/dist/store/plugins.d.ts +13 -0
  161. package/dist/store/plugins.d.ts.map +1 -0
  162. package/dist/store/registry.d.ts +28 -0
  163. package/dist/store/registry.d.ts.map +1 -0
  164. package/dist/store/types.d.ts +71 -0
  165. package/dist/store/types.d.ts.map +1 -0
  166. package/dist/store/utils.d.ts +28 -0
  167. package/dist/store/utils.d.ts.map +1 -0
  168. package/dist/store/watch.d.ts +23 -0
  169. package/dist/store/watch.d.ts.map +1 -0
  170. package/dist/store.es.mjs +22 -224
  171. package/dist/store.es.mjs.map +1 -1
  172. package/dist/type-guards-DRma3-Kc.js +16 -0
  173. package/dist/type-guards-DRma3-Kc.js.map +1 -0
  174. package/dist/untrack-BuEQKH7_.js +6 -0
  175. package/dist/untrack-BuEQKH7_.js.map +1 -0
  176. package/dist/view/directives/bind.d.ts +7 -0
  177. package/dist/view/directives/bind.d.ts.map +1 -0
  178. package/dist/view/directives/class.d.ts +8 -0
  179. package/dist/view/directives/class.d.ts.map +1 -0
  180. package/dist/view/directives/for.d.ts +23 -0
  181. package/dist/view/directives/for.d.ts.map +1 -0
  182. package/dist/view/directives/html.d.ts +7 -0
  183. package/dist/view/directives/html.d.ts.map +1 -0
  184. package/dist/view/directives/if.d.ts +7 -0
  185. package/dist/view/directives/if.d.ts.map +1 -0
  186. package/dist/view/directives/index.d.ts +12 -0
  187. package/dist/view/directives/index.d.ts.map +1 -0
  188. package/dist/view/directives/model.d.ts +7 -0
  189. package/dist/view/directives/model.d.ts.map +1 -0
  190. package/dist/view/directives/on.d.ts +7 -0
  191. package/dist/view/directives/on.d.ts.map +1 -0
  192. package/dist/view/directives/ref.d.ts +7 -0
  193. package/dist/view/directives/ref.d.ts.map +1 -0
  194. package/dist/view/directives/show.d.ts +7 -0
  195. package/dist/view/directives/show.d.ts.map +1 -0
  196. package/dist/view/directives/style.d.ts +7 -0
  197. package/dist/view/directives/style.d.ts.map +1 -0
  198. package/dist/view/directives/text.d.ts +7 -0
  199. package/dist/view/directives/text.d.ts.map +1 -0
  200. package/dist/view/evaluate.d.ts +43 -0
  201. package/dist/view/evaluate.d.ts.map +1 -0
  202. package/dist/view/index.d.ts +3 -93
  203. package/dist/view/index.d.ts.map +1 -1
  204. package/dist/view/mount.d.ts +69 -0
  205. package/dist/view/mount.d.ts.map +1 -0
  206. package/dist/view/process.d.ts +26 -0
  207. package/dist/view/process.d.ts.map +1 -0
  208. package/dist/view/types.d.ts +36 -0
  209. package/dist/view/types.d.ts.map +1 -0
  210. package/dist/view.es.mjs +368 -267
  211. package/dist/view.es.mjs.map +1 -1
  212. package/dist/watch-CXyaBC_9.js +58 -0
  213. package/dist/watch-CXyaBC_9.js.map +1 -0
  214. package/package.json +132 -132
  215. package/src/component/component.ts +289 -0
  216. package/src/component/html.ts +53 -0
  217. package/src/component/index.ts +40 -414
  218. package/src/component/props.ts +116 -0
  219. package/src/component/types.ts +85 -0
  220. package/src/core/collection.ts +588 -454
  221. package/src/core/dom.ts +38 -0
  222. package/src/core/element.ts +746 -740
  223. package/src/core/index.ts +43 -0
  224. package/src/core/utils/array.ts +102 -0
  225. package/src/core/utils/function.ts +110 -0
  226. package/src/core/utils/index.ts +83 -0
  227. package/src/core/utils/misc.ts +82 -0
  228. package/src/core/utils/number.ts +78 -0
  229. package/src/core/utils/object.ts +206 -0
  230. package/src/core/utils/string.ts +112 -0
  231. package/src/core/utils/type-guards.ts +112 -0
  232. package/src/full.ts +187 -150
  233. package/src/index.ts +36 -36
  234. package/src/motion/animate.ts +113 -0
  235. package/src/motion/easing.ts +40 -0
  236. package/src/motion/flip.ts +176 -0
  237. package/src/motion/index.ts +41 -358
  238. package/src/motion/keyframes.ts +46 -0
  239. package/src/motion/reduced-motion.ts +17 -0
  240. package/src/motion/scroll.ts +57 -0
  241. package/src/motion/spring.ts +150 -0
  242. package/src/motion/stagger.ts +43 -0
  243. package/src/motion/timeline.ts +246 -0
  244. package/src/motion/transition.ts +51 -0
  245. package/src/motion/types.ts +198 -0
  246. package/src/reactive/batch.ts +22 -0
  247. package/src/reactive/computed.ts +92 -0
  248. package/src/reactive/core.ts +93 -0
  249. package/src/reactive/effect.ts +43 -0
  250. package/src/reactive/index.ts +23 -22
  251. package/src/reactive/internals.ts +105 -0
  252. package/src/reactive/linked.ts +56 -0
  253. package/src/reactive/persisted.ts +74 -0
  254. package/src/reactive/readonly.ts +35 -0
  255. package/src/reactive/signal.ts +20 -520
  256. package/src/reactive/type-guards.ts +22 -0
  257. package/src/reactive/untrack.ts +31 -0
  258. package/src/reactive/watch.ts +73 -0
  259. package/src/router/index.ts +41 -718
  260. package/src/router/links.ts +130 -0
  261. package/src/router/match.ts +106 -0
  262. package/src/router/navigation.ts +71 -0
  263. package/src/router/query.ts +35 -0
  264. package/src/router/router.ts +211 -0
  265. package/src/router/state.ts +46 -0
  266. package/src/router/types.ts +93 -0
  267. package/src/router/utils.ts +116 -0
  268. package/src/security/constants.ts +209 -0
  269. package/src/security/csp.ts +77 -0
  270. package/src/security/index.ts +4 -12
  271. package/src/security/sanitize-core.ts +343 -0
  272. package/src/security/sanitize.ts +66 -625
  273. package/src/security/trusted-types.ts +69 -0
  274. package/src/security/types.ts +40 -0
  275. package/src/store/create-store.ts +329 -0
  276. package/src/store/define-store.ts +48 -0
  277. package/src/store/devtools.ts +45 -0
  278. package/src/store/index.ts +22 -848
  279. package/src/store/mapping.ts +73 -0
  280. package/src/store/persisted.ts +61 -0
  281. package/src/store/plugins.ts +32 -0
  282. package/src/store/registry.ts +51 -0
  283. package/src/store/types.ts +94 -0
  284. package/src/store/utils.ts +141 -0
  285. package/src/store/watch.ts +52 -0
  286. package/src/view/directives/bind.ts +23 -0
  287. package/src/view/directives/class.ts +70 -0
  288. package/src/view/directives/for.ts +275 -0
  289. package/src/view/directives/html.ts +19 -0
  290. package/src/view/directives/if.ts +30 -0
  291. package/src/view/directives/index.ts +11 -0
  292. package/src/view/directives/model.ts +56 -0
  293. package/src/view/directives/on.ts +41 -0
  294. package/src/view/directives/ref.ts +41 -0
  295. package/src/view/directives/show.ts +26 -0
  296. package/src/view/directives/style.ts +47 -0
  297. package/src/view/directives/text.ts +15 -0
  298. package/src/view/evaluate.ts +274 -0
  299. package/src/view/index.ts +112 -1041
  300. package/src/view/mount.ts +200 -0
  301. package/src/view/process.ts +92 -0
  302. package/src/view/types.ts +44 -0
  303. package/dist/core/utils.d.ts +0 -313
  304. package/dist/core/utils.d.ts.map +0 -1
  305. package/src/core/utils.ts +0 -444
@@ -0,0 +1,200 @@
1
+ import type { CleanupFn } from '../reactive/index';
2
+ import {
3
+ createForHandler,
4
+ handleBind,
5
+ handleClass,
6
+ handleHtml,
7
+ handleIf,
8
+ handleModel,
9
+ handleOn,
10
+ handleRef,
11
+ handleShow,
12
+ handleStyle,
13
+ handleText,
14
+ } from './directives/index';
15
+ import { processChildren, processElement, type DirectiveHandlers } from './process';
16
+ import type { BindingContext, MountOptions, View } from './types';
17
+
18
+ /**
19
+ * Mounts a reactive view to an element.
20
+ *
21
+ * @param selector - CSS selector or Element
22
+ * @param context - Binding context with signals, computed, and functions
23
+ * @param options - Mount options
24
+ * @returns The mounted View instance
25
+ *
26
+ * @security **WARNING:** Directive expressions (bq-text, bq-if, bq-on, etc.) are evaluated
27
+ * using `new Function()` at runtime. This means:
28
+ * - Template attributes must come from trusted sources only
29
+ * - NEVER load templates containing bq-* attributes from user input or untrusted APIs
30
+ * - If you must use external templates, validate/sanitize attribute values first
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * import { mount } from 'bquery/view';
35
+ * import { signal, computed } from 'bquery/reactive';
36
+ *
37
+ * const name = signal('World');
38
+ * const greeting = computed(() => `Hello, ${name.value}!`);
39
+ * const items = signal([
40
+ * { id: 1, text: 'Item 1' },
41
+ * { id: 2, text: 'Item 2' },
42
+ * ]);
43
+ *
44
+ * const view = mount('#app', {
45
+ * name,
46
+ * greeting,
47
+ * items,
48
+ * addItem: () => {
49
+ * items.value = [...items.value, { id: Date.now(), text: 'New Item' }];
50
+ * },
51
+ * });
52
+ *
53
+ * // Later, cleanup
54
+ * view.destroy();
55
+ * ```
56
+ */
57
+ export const mount = (
58
+ selector: string | Element,
59
+ context: BindingContext,
60
+ options: MountOptions = {}
61
+ ): View => {
62
+ const { prefix = 'bq', sanitize = true } = options;
63
+
64
+ const el = typeof selector === 'string' ? document.querySelector(selector) : selector;
65
+
66
+ if (!el) {
67
+ throw new Error(`bQuery view: Element "${selector}" not found.`);
68
+ }
69
+
70
+ // Reject if root element has bq-for directive
71
+ // bq-for replaces the element with a placeholder comment, which would leave View.el detached
72
+ if (el.hasAttribute(`${prefix}-for`)) {
73
+ throw new Error(
74
+ `bQuery view: Cannot mount on element with ${prefix}-for directive. ` +
75
+ `Wrap the ${prefix}-for element in a container instead.`
76
+ );
77
+ }
78
+
79
+ const cleanups: CleanupFn[] = [];
80
+
81
+ const handlers: DirectiveHandlers = {
82
+ text: handleText,
83
+ html: handleHtml(sanitize),
84
+ if: handleIf,
85
+ show: handleShow,
86
+ class: handleClass,
87
+ style: handleStyle,
88
+ model: handleModel,
89
+ ref: handleRef,
90
+ for: createForHandler({
91
+ prefix,
92
+ processElement: (node, nodeContext, nodePrefix, nodeCleanups) =>
93
+ processElement(node, nodeContext, nodePrefix, nodeCleanups, handlers),
94
+ processChildren: (node, nodeContext, nodePrefix, nodeCleanups) =>
95
+ processChildren(node, nodeContext, nodePrefix, nodeCleanups, handlers),
96
+ }),
97
+ bind: handleBind,
98
+ on: handleOn,
99
+ };
100
+
101
+ const processWithHandlers = (
102
+ node: Element,
103
+ nodeContext: BindingContext,
104
+ nodeCleanups: CleanupFn[]
105
+ ) => {
106
+ // Check if element has bq-for before processing
107
+ // bq-for replaces the element and handles its children internally
108
+ const hasFor = node.hasAttribute(`${prefix}-for`);
109
+
110
+ processElement(node, nodeContext, prefix, nodeCleanups, handlers);
111
+
112
+ // Skip processChildren if bq-for was on this element - it handles children itself
113
+ if (!hasFor) {
114
+ processChildren(node, nodeContext, prefix, nodeCleanups, handlers);
115
+ }
116
+ };
117
+
118
+ // Process the root element and its children
119
+ processWithHandlers(el, context, cleanups);
120
+
121
+ return {
122
+ el,
123
+ context,
124
+
125
+ update: (newContext: Partial<BindingContext>) => {
126
+ Object.assign(context, newContext);
127
+ },
128
+
129
+ destroy: () => {
130
+ for (const cleanup of cleanups) {
131
+ cleanup();
132
+ }
133
+ cleanups.length = 0;
134
+ },
135
+ };
136
+ };
137
+
138
+ /**
139
+ * Creates a reactive template function.
140
+ *
141
+ * @param template - HTML template string
142
+ * @returns A function that creates a mounted element with the given context
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * import { createTemplate } from 'bquery/view';
147
+ * import { signal } from 'bquery/reactive';
148
+ *
149
+ * const TodoItem = createTemplate(`
150
+ * <li bq-class="{ completed: done }">
151
+ * <input type="checkbox" bq-model="done" />
152
+ * <span bq-text="text"></span>
153
+ * </li>
154
+ * `);
155
+ *
156
+ * const item = TodoItem({
157
+ * done: signal(false),
158
+ * text: 'Buy groceries',
159
+ * });
160
+ *
161
+ * document.querySelector('#list').append(item.el);
162
+ * ```
163
+ */
164
+ export const createTemplate = (
165
+ template: string,
166
+ options: MountOptions = {}
167
+ ): ((context: BindingContext) => View) => {
168
+ return (context: BindingContext) => {
169
+ const container = document.createElement('div');
170
+ container.innerHTML = template.trim();
171
+
172
+ const el = container.firstElementChild;
173
+ if (!el) {
174
+ throw new Error('bQuery view: Template must contain a single root element.');
175
+ }
176
+
177
+ // We know at least one element exists (firstElementChild is not null above)
178
+ // Reject if there are multiple root elements
179
+ if (container.childElementCount > 1) {
180
+ throw new Error(
181
+ `bQuery view: Template must contain exactly one root element, found ${container.childElementCount}.`
182
+ );
183
+ }
184
+
185
+ const { prefix = 'bq' } = options;
186
+ // Reject templates with bq-for or bq-if on the root element
187
+ // These directives replace the element with a placeholder comment, which would leave View.el detached
188
+ // Since processing happens while el is still in the temporary container, the placeholder
189
+ // would remain there while view.el is inserted elsewhere, causing desync on future toggles
190
+ if (el.hasAttribute(`${prefix}-for`) || el.hasAttribute(`${prefix}-if`)) {
191
+ const directive = el.hasAttribute(`${prefix}-for`) ? 'for' : 'if';
192
+ throw new Error(
193
+ `bQuery view: Template root element cannot have ${prefix}-${directive} directive. ` +
194
+ `Wrap the ${prefix}-${directive} element in a container instead.`
195
+ );
196
+ }
197
+
198
+ return mount(el, context, options);
199
+ };
200
+ };
@@ -0,0 +1,92 @@
1
+ import type { CleanupFn } from '../reactive/index';
2
+ import type { BindingContext, DirectiveHandler } from './types';
3
+
4
+ export type DirectiveHandlers = {
5
+ text: DirectiveHandler;
6
+ html: DirectiveHandler;
7
+ if: DirectiveHandler;
8
+ show: DirectiveHandler;
9
+ class: DirectiveHandler;
10
+ style: DirectiveHandler;
11
+ model: DirectiveHandler;
12
+ ref: DirectiveHandler;
13
+ for: DirectiveHandler;
14
+ bind: (attrName: string) => DirectiveHandler;
15
+ on: (eventName: string) => DirectiveHandler;
16
+ };
17
+
18
+ /**
19
+ * Processes a single element for directives.
20
+ * @internal
21
+ */
22
+ export const processElement = (
23
+ el: Element,
24
+ context: BindingContext,
25
+ prefix: string,
26
+ cleanups: CleanupFn[],
27
+ handlers: DirectiveHandlers
28
+ ): void => {
29
+ const attributes = Array.from(el.attributes);
30
+
31
+ for (const attr of attributes) {
32
+ const { name, value } = attr;
33
+
34
+ if (!name.startsWith(`${prefix}-`)) continue;
35
+
36
+ const directive = name.slice(prefix.length + 1); // Remove prefix and dash
37
+
38
+ // Handle bq-for specially (creates new scope)
39
+ if (directive === 'for') {
40
+ handlers.for(el, value, context, cleanups);
41
+ return; // Don't process children, bq-for handles it
42
+ }
43
+
44
+ // Handle other directives
45
+ if (directive === 'text') {
46
+ handlers.text(el, value, context, cleanups);
47
+ } else if (directive === 'html') {
48
+ handlers.html(el, value, context, cleanups);
49
+ } else if (directive === 'if') {
50
+ handlers.if(el, value, context, cleanups);
51
+ } else if (directive === 'show') {
52
+ handlers.show(el, value, context, cleanups);
53
+ } else if (directive === 'class') {
54
+ handlers.class(el, value, context, cleanups);
55
+ } else if (directive === 'style') {
56
+ handlers.style(el, value, context, cleanups);
57
+ } else if (directive === 'model') {
58
+ handlers.model(el, value, context, cleanups);
59
+ } else if (directive === 'ref') {
60
+ handlers.ref(el, value, context, cleanups);
61
+ } else if (directive.startsWith('bind:')) {
62
+ const attrName = directive.slice(5);
63
+ handlers.bind(attrName)(el, value, context, cleanups);
64
+ } else if (directive.startsWith('on:')) {
65
+ const eventName = directive.slice(3);
66
+ handlers.on(eventName)(el, value, context, cleanups);
67
+ }
68
+ }
69
+ };
70
+
71
+ /**
72
+ * Recursively processes children of an element.
73
+ * @internal
74
+ */
75
+ export const processChildren = (
76
+ el: Element,
77
+ context: BindingContext,
78
+ prefix: string,
79
+ cleanups: CleanupFn[],
80
+ handlers: DirectiveHandlers
81
+ ): void => {
82
+ const children = Array.from(el.children);
83
+ for (const child of children) {
84
+ // Skip if element has bq-for (handled separately)
85
+ if (!child.hasAttribute(`${prefix}-for`)) {
86
+ processElement(child, context, prefix, cleanups, handlers);
87
+ processChildren(child, context, prefix, cleanups, handlers);
88
+ } else {
89
+ processElement(child, context, prefix, cleanups, handlers);
90
+ }
91
+ }
92
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Public types for the view module.
3
+ */
4
+ import type { CleanupFn } from '../reactive/index';
5
+
6
+ /**
7
+ * Context object passed to binding expressions.
8
+ */
9
+ export type BindingContext = Record<string, unknown>;
10
+
11
+ /**
12
+ * Configuration options for mount.
13
+ */
14
+ export type MountOptions = {
15
+ /** Prefix for directive attributes (default: 'bq') */
16
+ prefix?: string;
17
+ /** Whether to sanitize bq-html content (default: true) */
18
+ sanitize?: boolean;
19
+ };
20
+
21
+ /**
22
+ * Mounted view instance.
23
+ */
24
+ export type View = {
25
+ /** The root element */
26
+ el: Element;
27
+ /** The binding context */
28
+ context: BindingContext;
29
+ /** Update the context object. Note: this only mutates the context; reactive re-rendering happens automatically when signals/computed values change. */
30
+ update: (newContext: Partial<BindingContext>) => void;
31
+ /** Destroy the view and cleanup effects */
32
+ destroy: () => void;
33
+ };
34
+
35
+ /**
36
+ * Internal directive handler type.
37
+ * @internal
38
+ */
39
+ export type DirectiveHandler = (
40
+ el: Element,
41
+ expression: string,
42
+ context: BindingContext,
43
+ cleanups: CleanupFn[]
44
+ ) => void;
@@ -1,313 +0,0 @@
1
- /**
2
- * Utility helpers used across the framework.
3
- * These are intentionally small and framework-agnostic to keep the core tiny.
4
- *
5
- * @module bquery/core/utils
6
- */
7
- /**
8
- * Utility object containing common helper functions.
9
- * All utilities are designed to be tree-shakeable and have zero dependencies.
10
- */
11
- export declare const utils: {
12
- /**
13
- * Creates a deep clone using structuredClone if available, otherwise fallback to JSON.
14
- *
15
- * @template T - The type of value being cloned
16
- * @param value - The value to clone
17
- * @returns A deep copy of the value
18
- *
19
- * @example
20
- * ```ts
21
- * const original = { nested: { value: 1 } };
22
- * const copy = utils.clone(original);
23
- * copy.nested.value = 2;
24
- * console.log(original.nested.value); // 1
25
- * ```
26
- */
27
- clone<T>(value: T): T;
28
- /**
29
- * Deep-merges plain objects into a new object.
30
- * Later sources override earlier ones for primitive values.
31
- * Objects are recursively merged.
32
- *
33
- * @template T - The type of the merged object
34
- * @param sources - Objects to merge
35
- * @returns A new object with all sources merged
36
- *
37
- * @example
38
- * ```ts
39
- * const result = utils.merge(
40
- * { a: 1, nested: { x: 1 } },
41
- * { b: 2, nested: { y: 2 } }
42
- * );
43
- * // Result: { a: 1, b: 2, nested: { x: 1, y: 2 } }
44
- * ```
45
- *
46
- * @security This method is protected against prototype pollution attacks.
47
- * Keys like `__proto__`, `constructor`, and `prototype` are ignored.
48
- */
49
- merge<T extends Record<string, unknown>>(...sources: T[]): T;
50
- /**
51
- * Checks if a key could cause prototype pollution.
52
- * These keys are dangerous when used in object merging operations.
53
- *
54
- * @param key - The key to check
55
- * @returns True if the key is a prototype pollution vector
56
- *
57
- * @internal
58
- */
59
- isPrototypePollutionKey(key: string): boolean;
60
- /**
61
- * Creates a debounced function that delays execution until after
62
- * the specified delay has elapsed since the last call.
63
- *
64
- * @template TArgs - The argument types of the function
65
- * @param fn - The function to debounce
66
- * @param delayMs - Delay in milliseconds
67
- * @returns A debounced version of the function
68
- *
69
- * @example
70
- * ```ts
71
- * const search = utils.debounce((query: string) => {
72
- * console.log('Searching:', query);
73
- * }, 300);
74
- *
75
- * search('h');
76
- * search('he');
77
- * search('hello'); // Only this call executes after 300ms
78
- * ```
79
- */
80
- debounce<TArgs extends unknown[]>(fn: (...args: TArgs) => void, delayMs: number): (...args: TArgs) => void;
81
- /**
82
- * Creates a throttled function that runs at most once per interval.
83
- *
84
- * @template TArgs - The argument types of the function
85
- * @param fn - The function to throttle
86
- * @param intervalMs - Minimum interval between calls in milliseconds
87
- * @returns A throttled version of the function
88
- *
89
- * @example
90
- * ```ts
91
- * const handleScroll = utils.throttle(() => {
92
- * console.log('Scroll position:', window.scrollY);
93
- * }, 100);
94
- *
95
- * window.addEventListener('scroll', handleScroll);
96
- * ```
97
- */
98
- throttle<TArgs extends unknown[]>(fn: (...args: TArgs) => void, intervalMs: number): (...args: TArgs) => void;
99
- /**
100
- * Creates a stable unique ID for DOM usage.
101
- *
102
- * @param prefix - Optional prefix for the ID (default: 'bQuery')
103
- * @returns A unique identifier string
104
- *
105
- * @example
106
- * ```ts
107
- * const id = utils.uid('modal'); // 'modal_x7k2m9p'
108
- * ```
109
- */
110
- uid(prefix?: string): string;
111
- /**
112
- * Checks if a value is a DOM Element.
113
- *
114
- * @param value - The value to check
115
- * @returns True if the value is an Element
116
- */
117
- isElement(value: unknown): value is Element;
118
- /**
119
- * Checks if a value is a BQueryCollection-like object.
120
- *
121
- * @param value - The value to check
122
- * @returns True if the value has an elements array property
123
- */
124
- isCollection(value: unknown): value is {
125
- elements: Element[];
126
- };
127
- /**
128
- * Checks for emptiness across common value types.
129
- *
130
- * @param value - The value to check
131
- * @returns True if the value is empty (null, undefined, empty string, empty array, or empty object)
132
- *
133
- * @example
134
- * ```ts
135
- * utils.isEmpty(''); // true
136
- * utils.isEmpty([]); // true
137
- * utils.isEmpty({}); // true
138
- * utils.isEmpty(null); // true
139
- * utils.isEmpty('hello'); // false
140
- * utils.isEmpty([1, 2]); // false
141
- * ```
142
- */
143
- isEmpty(value: unknown): boolean;
144
- /**
145
- * Checks if a value is a plain object (not null, array, or class instance).
146
- *
147
- * @param value - The value to check
148
- * @returns True if the value is a plain object
149
- */
150
- isPlainObject(value: unknown): value is Record<string, unknown>;
151
- /**
152
- * Checks if a value is a function.
153
- *
154
- * @param value - The value to check
155
- * @returns True if the value is a function
156
- */
157
- isFunction(value: unknown): value is (...args: unknown[]) => unknown;
158
- /**
159
- * Checks if a value is a string.
160
- *
161
- * @param value - The value to check
162
- * @returns True if the value is a string
163
- */
164
- isString(value: unknown): value is string;
165
- /**
166
- * Checks if a value is a number (excluding NaN).
167
- *
168
- * @param value - The value to check
169
- * @returns True if the value is a valid number
170
- */
171
- isNumber(value: unknown): value is number;
172
- /**
173
- * Checks if a value is a boolean.
174
- *
175
- * @param value - The value to check
176
- * @returns True if the value is a boolean
177
- */
178
- isBoolean(value: unknown): value is boolean;
179
- /**
180
- * Checks if a value is an array.
181
- *
182
- * @template T - The type of array elements
183
- * @param value - The value to check
184
- * @returns True if the value is an array
185
- */
186
- isArray<T = unknown>(value: unknown): value is T[];
187
- /**
188
- * Safely parses a JSON string, returning a default value on error.
189
- *
190
- * @template T - The expected type of the parsed value
191
- * @param json - The JSON string to parse
192
- * @param fallback - The default value if parsing fails
193
- * @returns The parsed value or the fallback
194
- *
195
- * @example
196
- * ```ts
197
- * utils.parseJson('{"name":"bQuery"}', {}); // { name: 'bQuery' }
198
- * utils.parseJson('invalid', {}); // {}
199
- * ```
200
- */
201
- parseJson<T>(json: string, fallback: T): T;
202
- /**
203
- * Picks specified keys from an object.
204
- *
205
- * @template T - The object type
206
- * @template K - The key type
207
- * @param obj - The source object
208
- * @param keys - Keys to pick
209
- * @returns A new object with only the specified keys
210
- *
211
- * @example
212
- * ```ts
213
- * const user = { name: 'John', age: 30, email: 'john@example.com' };
214
- * utils.pick(user, ['name', 'email']); // { name: 'John', email: 'john@example.com' }
215
- * ```
216
- */
217
- pick<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>;
218
- /**
219
- * Omits specified keys from an object.
220
- *
221
- * @template T - The object type
222
- * @template K - The key type
223
- * @param obj - The source object
224
- * @param keys - Keys to omit
225
- * @returns A new object without the specified keys
226
- *
227
- * @example
228
- * ```ts
229
- * const user = { name: 'John', age: 30, password: 'secret' };
230
- * utils.omit(user, ['password']); // { name: 'John', age: 30 }
231
- * ```
232
- */
233
- omit<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>;
234
- /**
235
- * Delays execution for a specified number of milliseconds.
236
- *
237
- * @param ms - Milliseconds to delay
238
- * @returns A promise that resolves after the delay
239
- *
240
- * @example
241
- * ```ts
242
- * await utils.sleep(1000); // Wait 1 second
243
- * console.log('Done!');
244
- * ```
245
- */
246
- sleep(ms: number): Promise<void>;
247
- /**
248
- * Generates a random integer between min and max (inclusive).
249
- *
250
- * @param min - Minimum value
251
- * @param max - Maximum value
252
- * @returns A random integer in the range [min, max]
253
- *
254
- * @example
255
- * ```ts
256
- * const roll = utils.randomInt(1, 6); // Random dice roll
257
- * ```
258
- */
259
- randomInt(min: number, max: number): number;
260
- /**
261
- * Clamps a number between a minimum and maximum value.
262
- *
263
- * @param value - The value to clamp
264
- * @param min - Minimum value
265
- * @param max - Maximum value
266
- * @returns The clamped value
267
- *
268
- * @example
269
- * ```ts
270
- * utils.clamp(150, 0, 100); // 100
271
- * utils.clamp(-10, 0, 100); // 0
272
- * utils.clamp(50, 0, 100); // 50
273
- * ```
274
- */
275
- clamp(value: number, min: number, max: number): number;
276
- /**
277
- * Capitalizes the first letter of a string.
278
- *
279
- * @param str - The string to capitalize
280
- * @returns The capitalized string
281
- *
282
- * @example
283
- * ```ts
284
- * utils.capitalize('hello'); // 'Hello'
285
- * ```
286
- */
287
- capitalize(str: string): string;
288
- /**
289
- * Converts a string to kebab-case.
290
- *
291
- * @param str - The string to convert
292
- * @returns The kebab-cased string
293
- *
294
- * @example
295
- * ```ts
296
- * utils.toKebabCase('myVariableName'); // 'my-variable-name'
297
- * ```
298
- */
299
- toKebabCase(str: string): string;
300
- /**
301
- * Converts a string to camelCase.
302
- *
303
- * @param str - The string to convert
304
- * @returns The camelCased string
305
- *
306
- * @example
307
- * ```ts
308
- * utils.toCamelCase('my-variable-name'); // 'myVariableName'
309
- * ```
310
- */
311
- toCamelCase(str: string): string;
312
- };
313
- //# sourceMappingURL=utils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/core/utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,eAAO,MAAM,KAAK;IAChB;;;;;;;;;;;;;;OAcG;UACG,CAAC,SAAS,CAAC,GAAG,CAAC;IAOrB;;;;;;;;;;;;;;;;;;;;OAoBG;UACG,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,EAAE,GAAG,CAAC;IAoB5D;;;;;;;;OAQG;iCAC0B,MAAM,GAAG,OAAO;IAI7C;;;;;;;;;;;;;;;;;;;OAmBG;aACM,KAAK,SAAS,OAAO,EAAE,MAC1B,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,IAAI,WACnB,MAAM,GACd,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,IAAI;IAU3B;;;;;;;;;;;;;;;;OAgBG;aACM,KAAK,SAAS,OAAO,EAAE,MAC1B,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,IAAI,cAChB,MAAM,GACjB,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,IAAI;IAW3B;;;;;;;;;;OAUG;0BACqB,MAAM;IAI9B;;;;;OAKG;qBACc,OAAO,GAAG,KAAK,IAAI,OAAO;IAI3C;;;;;OAKG;wBACiB,OAAO,GAAG,KAAK,IAAI;QAAE,QAAQ,EAAE,OAAO,EAAE,CAAA;KAAE;IAI9D;;;;;;;;;;;;;;;OAeG;mBACY,OAAO,GAAG,OAAO;IAQhC;;;;;OAKG;yBACkB,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAI/D;;;;;OAKG;sBACe,OAAO,GAAG,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO;IAIpE;;;;;OAKG;oBACa,OAAO,GAAG,KAAK,IAAI,MAAM;IAIzC;;;;;OAKG;oBACa,OAAO,GAAG,KAAK,IAAI,MAAM;IAIzC;;;;;OAKG;qBACc,OAAO,GAAG,KAAK,IAAI,OAAO;IAI3C;;;;;;OAMG;YACK,CAAC,mBAAmB,OAAO,GAAG,KAAK,IAAI,CAAC,EAAE;IAIlD;;;;;;;;;;;;;OAaG;cACO,CAAC,QAAQ,MAAM,YAAY,CAAC,GAAG,CAAC;IAQ1C;;;;;;;;;;;;;;OAcG;SACE,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IAUzF;;;;;;;;;;;;;;OAcG;SACE,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IAQzF;;;;;;;;;;;OAWG;cACO,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhC;;;;;;;;;;;OAWG;mBACY,MAAM,OAAO,MAAM,GAAG,MAAM;IAI3C;;;;;;;;;;;;;;OAcG;iBACU,MAAM,OAAO,MAAM,OAAO,MAAM,GAAG,MAAM;IAItD;;;;;;;;;;OAUG;oBACa,MAAM,GAAG,MAAM;IAK/B;;;;;;;;;;OAUG;qBACc,MAAM,GAAG,MAAM;IAOhC;;;;;;;;;;OAUG;qBACc,MAAM,GAAG,MAAM;CAKjC,CAAC"}