@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
@@ -0,0 +1,289 @@
1
+ /**
2
+ * Web Component factory and registry.
3
+ *
4
+ * @module bquery/component
5
+ */
6
+
7
+ import { sanitizeHtml } from '../security/sanitize';
8
+ import { coercePropValue } from './props';
9
+ import type { ComponentDefinition, PropDefinition } from './types';
10
+
11
+ /**
12
+ * Creates a custom element class for a component definition.
13
+ *
14
+ * This is useful when you want to extend or register the class manually
15
+ * (e.g. with different tag names in tests or custom registries).
16
+ *
17
+ * @template TProps - Type of the component's props
18
+ * @param tagName - The custom element tag name (used for diagnostics)
19
+ * @param definition - The component configuration
20
+ */
21
+ export const defineComponent = <TProps extends Record<string, unknown>>(
22
+ tagName: string,
23
+ definition: ComponentDefinition<TProps>
24
+ ): typeof HTMLElement => {
25
+ class BQueryComponent extends HTMLElement {
26
+ /** Internal state object for the component */
27
+ private readonly state = { ...(definition.state ?? {}) };
28
+ /** Typed props object populated from attributes */
29
+ private props = {} as TProps;
30
+ /** Tracks missing required props for validation during connectedCallback */
31
+ private missingRequiredProps = new Set<string>();
32
+ /** Tracks whether the component has completed its initial mount */
33
+ private hasMounted = false;
34
+
35
+ constructor() {
36
+ super();
37
+ this.attachShadow({ mode: 'open' });
38
+ this.syncProps();
39
+ }
40
+
41
+ /**
42
+ * Returns the list of attributes to observe for changes.
43
+ */
44
+ static get observedAttributes(): string[] {
45
+ return Object.keys(definition.props ?? {});
46
+ }
47
+
48
+ /**
49
+ * Called when the element is added to the DOM.
50
+ */
51
+ connectedCallback(): void {
52
+ try {
53
+ // Defer initial render until all required props are present
54
+ // This allows attributes to be set after element creation
55
+ if (this.missingRequiredProps.size > 0) {
56
+ // Component will mount once all required props are satisfied
57
+ // via attributeChangedCallback
58
+ return;
59
+ }
60
+ this.mount();
61
+ } catch (error) {
62
+ this.handleError(error as Error);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Performs the initial mount of the component.
68
+ * Called when the element is connected and all required props are present.
69
+ * @internal
70
+ */
71
+ private mount(): void {
72
+ if (this.hasMounted) return;
73
+ definition.beforeMount?.call(this);
74
+ definition.connected?.call(this);
75
+ this.render();
76
+ this.hasMounted = true;
77
+ }
78
+
79
+ /**
80
+ * Called when the element is removed from the DOM.
81
+ */
82
+ disconnectedCallback(): void {
83
+ try {
84
+ definition.disconnected?.call(this);
85
+ } catch (error) {
86
+ this.handleError(error as Error);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Called when an observed attribute changes.
92
+ */
93
+ attributeChangedCallback(
94
+ _name: string,
95
+ _oldValue: string | null,
96
+ _newValue: string | null
97
+ ): void {
98
+ try {
99
+ this.syncProps();
100
+
101
+ if (this.hasMounted) {
102
+ // Component already mounted - trigger update render
103
+ this.render(true);
104
+ } else if (this.isConnected && this.missingRequiredProps.size === 0) {
105
+ // All required props are now satisfied and element is connected
106
+ // Trigger the deferred initial mount
107
+ this.mount();
108
+ }
109
+ } catch (error) {
110
+ this.handleError(error as Error);
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Handles errors during component lifecycle.
116
+ * @internal
117
+ */
118
+ private handleError(error: Error): void {
119
+ if (definition.onError) {
120
+ definition.onError.call(this, error);
121
+ } else {
122
+ console.error(`bQuery component error in <${tagName}>:`, error);
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Updates a state property and triggers a re-render.
128
+ *
129
+ * @param key - The state property key
130
+ * @param value - The new value
131
+ */
132
+ setState(key: string, value: unknown): void {
133
+ this.state[key] = value;
134
+ this.render(true);
135
+ }
136
+
137
+ /**
138
+ * Gets a state property value.
139
+ *
140
+ * @param key - The state property key
141
+ * @returns The current value
142
+ */
143
+ getState<T = unknown>(key: string): T {
144
+ return this.state[key] as T;
145
+ }
146
+
147
+ /**
148
+ * Synchronizes props from attributes.
149
+ * @internal
150
+ */
151
+ private syncProps(): void {
152
+ const props = definition.props ?? {};
153
+ for (const [key, config] of Object.entries(props) as [string, PropDefinition][]) {
154
+ const attrValue = this.getAttribute(key);
155
+ let value: unknown;
156
+
157
+ if (attrValue == null) {
158
+ if (config.required && config.default === undefined) {
159
+ // Mark as missing instead of throwing - validate during connectedCallback
160
+ this.missingRequiredProps.add(key);
161
+ value = undefined;
162
+ } else {
163
+ value = config.default ?? undefined;
164
+ }
165
+ } else {
166
+ // Attribute is present, remove from missing set if it was there
167
+ if (this.missingRequiredProps.has(key)) {
168
+ this.missingRequiredProps.delete(key);
169
+ }
170
+ value = coercePropValue(attrValue, config);
171
+ }
172
+
173
+ if (config.validator && value !== undefined) {
174
+ const isValid = config.validator(value);
175
+ if (!isValid) {
176
+ throw new Error(
177
+ `bQuery component: validation failed for prop "${key}" with value ${JSON.stringify(value)}`
178
+ );
179
+ }
180
+ }
181
+
182
+ (this.props as Record<string, unknown>)[key] = value;
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Renders the component to its shadow root.
188
+ * @internal
189
+ */
190
+ private render(triggerUpdated = false): void {
191
+ try {
192
+ if (triggerUpdated && definition.beforeUpdate) {
193
+ const shouldUpdate = definition.beforeUpdate.call(this, this.props);
194
+ if (shouldUpdate === false) return;
195
+ }
196
+
197
+ const emit = (event: string, detail?: unknown): void => {
198
+ this.dispatchEvent(new CustomEvent(event, { detail, bubbles: true, composed: true }));
199
+ };
200
+
201
+ if (!this.shadowRoot) return;
202
+
203
+ const markup = definition.render({
204
+ props: this.props,
205
+ state: this.state,
206
+ emit,
207
+ });
208
+
209
+ const sanitizedMarkup = sanitizeHtml(markup);
210
+ this.shadowRoot.innerHTML = sanitizedMarkup;
211
+
212
+ if (definition.styles) {
213
+ const styleElement = document.createElement('style');
214
+ styleElement.textContent = definition.styles;
215
+ this.shadowRoot.prepend(styleElement);
216
+ }
217
+
218
+ if (triggerUpdated) {
219
+ definition.updated?.call(this);
220
+ }
221
+ } catch (error) {
222
+ this.handleError(error as Error);
223
+ }
224
+ }
225
+ }
226
+
227
+ return BQueryComponent;
228
+ };
229
+
230
+ /**
231
+ * Defines and registers a custom Web Component.
232
+ *
233
+ * This function creates a new custom element with the given tag name
234
+ * and configuration. The component uses Shadow DOM for encapsulation
235
+ * and automatically re-renders when observed attributes change.
236
+ *
237
+ * @template TProps - Type of the component's props
238
+ * @param tagName - The custom element tag name (must contain a hyphen)
239
+ * @param definition - The component configuration
240
+ *
241
+ * @example
242
+ * ```ts
243
+ * component('counter-button', {
244
+ * props: {
245
+ * start: { type: Number, default: 0 },
246
+ * },
247
+ * state: { count: 0 },
248
+ * styles: `
249
+ * button { padding: 0.5rem 1rem; }
250
+ * `,
251
+ * connected() {
252
+ * // Use event delegation on shadow root so handler survives re-renders
253
+ * const handleClick = (event: Event) => {
254
+ * const target = event.target as HTMLElement | null;
255
+ * if (target?.matches('button')) {
256
+ * this.setState('count', (this.getState('count') as number) + 1);
257
+ * }
258
+ * };
259
+ * this.shadowRoot?.addEventListener('click', handleClick);
260
+ * // Store handler for cleanup
261
+ * (this as any)._handleClick = handleClick;
262
+ * },
263
+ * disconnected() {
264
+ * // Clean up event listener to prevent memory leaks
265
+ * const handleClick = (this as any)._handleClick;
266
+ * if (handleClick) {
267
+ * this.shadowRoot?.removeEventListener('click', handleClick);
268
+ * }
269
+ * },
270
+ * render({ props, state }) {
271
+ * return html`
272
+ * <button>
273
+ * Count: ${state.count}
274
+ * </button>
275
+ * `;
276
+ * },
277
+ * });
278
+ * ```
279
+ */
280
+ export const component = <TProps extends Record<string, unknown>>(
281
+ tagName: string,
282
+ definition: ComponentDefinition<TProps>
283
+ ): void => {
284
+ const elementClass = defineComponent(tagName, definition);
285
+
286
+ if (!customElements.get(tagName)) {
287
+ customElements.define(tagName, elementClass);
288
+ }
289
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Tagged template literal for creating HTML strings.
3
+ *
4
+ * This function handles interpolation of values into HTML templates,
5
+ * converting null/undefined to empty strings.
6
+ *
7
+ * @param strings - Template literal string parts
8
+ * @param values - Interpolated values
9
+ * @returns Combined HTML string
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const name = 'World';
14
+ * const greeting = html`<h1>Hello, ${name}!</h1>`;
15
+ * // Result: '<h1>Hello, World!</h1>'
16
+ * ```
17
+ */
18
+ export const html = (strings: TemplateStringsArray, ...values: unknown[]): string => {
19
+ return strings.reduce((acc, part, index) => `${acc}${part}${values[index] ?? ''}`, '');
20
+ };
21
+
22
+ /**
23
+ * Escapes HTML entities in interpolated values for XSS prevention.
24
+ * Use this when you need to safely embed user content in templates.
25
+ *
26
+ * @param strings - Template literal string parts
27
+ * @param values - Interpolated values to escape
28
+ * @returns Combined HTML string with escaped values
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const userInput = '<script>alert("xss")</script>';
33
+ * const safe = safeHtml`<div>${userInput}</div>`;
34
+ * // Result: '<div>&lt;script&gt;alert("xss")&lt;/script&gt;</div>'
35
+ * ```
36
+ */
37
+ export const safeHtml = (strings: TemplateStringsArray, ...values: unknown[]): string => {
38
+ const escapeMap: Record<string, string> = {
39
+ '&': '&amp;',
40
+ '<': '&lt;',
41
+ '>': '&gt;',
42
+ '"': '&quot;',
43
+ "'": '&#x27;',
44
+ '`': '&#x60;',
45
+ };
46
+
47
+ const escape = (value: unknown): string => {
48
+ const str = String(value ?? '');
49
+ return str.replace(/[&<>"'`]/g, (char) => escapeMap[char]);
50
+ };
51
+
52
+ return strings.reduce((acc, part, index) => `${acc}${part}${escape(values[index])}`, '');
53
+ };