@bquery/bquery 1.7.0 → 1.8.1

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 (262) hide show
  1. package/README.md +760 -716
  2. package/dist/{a11y-C5QOVvRn.js → a11y-DVBCy09c.js} +3 -3
  3. package/dist/a11y-DVBCy09c.js.map +1 -0
  4. package/dist/a11y.es.mjs +1 -1
  5. package/dist/component/library.d.ts.map +1 -1
  6. package/dist/{component-CuuTijA6.js → component-L3-JfOFz.js} +5 -5
  7. package/dist/component-L3-JfOFz.js.map +1 -0
  8. package/dist/component.es.mjs +1 -1
  9. package/dist/{config-BW35FKuA.js → config-DhT9auRm.js} +1 -1
  10. package/dist/{config-BW35FKuA.js.map → config-DhT9auRm.js.map} +1 -1
  11. package/dist/{constraints-3lV9yyBw.js → constraints-D5RHQLmP.js} +1 -1
  12. package/dist/constraints-D5RHQLmP.js.map +1 -0
  13. package/dist/core/collection.d.ts +86 -0
  14. package/dist/core/collection.d.ts.map +1 -1
  15. package/dist/core/element.d.ts +28 -0
  16. package/dist/core/element.d.ts.map +1 -1
  17. package/dist/core/shared.d.ts +6 -0
  18. package/dist/core/shared.d.ts.map +1 -1
  19. package/dist/core-DdtZHzsS.js +168 -0
  20. package/dist/core-DdtZHzsS.js.map +1 -0
  21. package/dist/{core-Cjl7GUu8.js → core-EMYSLzaT.js} +289 -259
  22. package/dist/core-EMYSLzaT.js.map +1 -0
  23. package/dist/core.es.mjs +48 -47
  24. package/dist/{custom-directives-7wAShnnd.js → custom-directives-Dr4C5lVV.js} +1 -1
  25. package/dist/custom-directives-Dr4C5lVV.js.map +1 -0
  26. package/dist/{devtools-D2fQLhDN.js → devtools-BhB2iDPT.js} +2 -2
  27. package/dist/devtools-BhB2iDPT.js.map +1 -0
  28. package/dist/devtools.es.mjs +1 -1
  29. package/dist/{dnd-B8EgyzaI.js → dnd-NwZBYh4l.js} +1 -1
  30. package/dist/dnd-NwZBYh4l.js.map +1 -0
  31. package/dist/dnd.es.mjs +1 -1
  32. package/dist/{env-NeVmr4Gf.js → env-CTdvLaH2.js} +1 -1
  33. package/dist/env-CTdvLaH2.js.map +1 -0
  34. package/dist/forms/create-form.d.ts.map +1 -1
  35. package/dist/forms/index.d.ts +3 -2
  36. package/dist/forms/index.d.ts.map +1 -1
  37. package/dist/forms/types.d.ts +46 -0
  38. package/dist/forms/types.d.ts.map +1 -1
  39. package/dist/forms/use-field.d.ts +34 -0
  40. package/dist/forms/use-field.d.ts.map +1 -0
  41. package/dist/forms/validators.d.ts +25 -0
  42. package/dist/forms/validators.d.ts.map +1 -1
  43. package/dist/forms-UcRHsYxC.js +227 -0
  44. package/dist/forms-UcRHsYxC.js.map +1 -0
  45. package/dist/forms.es.mjs +14 -12
  46. package/dist/full.d.ts +17 -26
  47. package/dist/full.d.ts.map +1 -1
  48. package/dist/full.es.mjs +206 -181
  49. package/dist/full.iife.js +33 -33
  50. package/dist/full.iife.js.map +1 -1
  51. package/dist/full.umd.js +33 -33
  52. package/dist/full.umd.js.map +1 -1
  53. package/dist/function-Cybd57JV.js +33 -0
  54. package/dist/function-Cybd57JV.js.map +1 -0
  55. package/dist/{i18n-BnnhTFOS.js → i18n-kuF6Ekj6.js} +3 -3
  56. package/dist/i18n-kuF6Ekj6.js.map +1 -0
  57. package/dist/i18n.es.mjs +1 -1
  58. package/dist/index.es.mjs +251 -228
  59. package/dist/media/breakpoints.d.ts.map +1 -1
  60. package/dist/media/types.d.ts +2 -2
  61. package/dist/media/types.d.ts.map +1 -1
  62. package/dist/{media-Di2Ta22s.js → media-i-fB5WxI.js} +3 -3
  63. package/dist/media-i-fB5WxI.js.map +1 -0
  64. package/dist/media.es.mjs +1 -1
  65. package/dist/{motion-qPj_TYGv.js → motion-BJsAuULb.js} +2 -2
  66. package/dist/motion-BJsAuULb.js.map +1 -0
  67. package/dist/motion.es.mjs +1 -1
  68. package/dist/{mount-SM07RUa6.js → mount-B4Y8bk8Z.js} +5 -5
  69. package/dist/mount-B4Y8bk8Z.js.map +1 -0
  70. package/dist/{platform-CPbCprb6.js → platform-Dw2gE3zI.js} +3 -3
  71. package/dist/{platform-CPbCprb6.js.map → platform-Dw2gE3zI.js.map} +1 -1
  72. package/dist/platform.es.mjs +2 -2
  73. package/dist/plugin/registry.d.ts.map +1 -1
  74. package/dist/{plugin-cPoOHFLY.js → plugin-C2WuC8SF.js} +20 -18
  75. package/dist/plugin-C2WuC8SF.js.map +1 -0
  76. package/dist/plugin.es.mjs +1 -1
  77. package/dist/reactive/async-data.d.ts +28 -3
  78. package/dist/reactive/async-data.d.ts.map +1 -1
  79. package/dist/reactive/computed.d.ts +3 -0
  80. package/dist/reactive/computed.d.ts.map +1 -1
  81. package/dist/reactive/effect.d.ts +3 -0
  82. package/dist/reactive/effect.d.ts.map +1 -1
  83. package/dist/reactive/http.d.ts +194 -0
  84. package/dist/reactive/http.d.ts.map +1 -0
  85. package/dist/reactive/index.d.ts +2 -2
  86. package/dist/reactive/index.d.ts.map +1 -1
  87. package/dist/reactive/pagination.d.ts +126 -0
  88. package/dist/reactive/pagination.d.ts.map +1 -0
  89. package/dist/reactive/polling.d.ts +55 -0
  90. package/dist/reactive/polling.d.ts.map +1 -0
  91. package/dist/reactive/readonly.d.ts +20 -1
  92. package/dist/reactive/readonly.d.ts.map +1 -1
  93. package/dist/reactive/rest.d.ts +293 -0
  94. package/dist/reactive/rest.d.ts.map +1 -0
  95. package/dist/reactive/scope.d.ts +140 -0
  96. package/dist/reactive/scope.d.ts.map +1 -0
  97. package/dist/reactive/signal.d.ts +16 -2
  98. package/dist/reactive/signal.d.ts.map +1 -1
  99. package/dist/reactive/to-value.d.ts +57 -0
  100. package/dist/reactive/to-value.d.ts.map +1 -0
  101. package/dist/reactive/websocket.d.ts +285 -0
  102. package/dist/reactive/websocket.d.ts.map +1 -0
  103. package/dist/reactive-DwkhUJfP.js +1148 -0
  104. package/dist/reactive-DwkhUJfP.js.map +1 -0
  105. package/dist/reactive.es.mjs +38 -19
  106. package/dist/{registry-CWf368tT.js → registry-B08iilIh.js} +1 -1
  107. package/dist/{registry-CWf368tT.js.map → registry-B08iilIh.js.map} +1 -1
  108. package/dist/router/constraints.d.ts.map +1 -1
  109. package/dist/router/index.d.ts +1 -1
  110. package/dist/router/index.d.ts.map +1 -1
  111. package/dist/router/router.d.ts.map +1 -1
  112. package/dist/router/state.d.ts +25 -2
  113. package/dist/router/state.d.ts.map +1 -1
  114. package/dist/router-CQikC9Ed.js +492 -0
  115. package/dist/router-CQikC9Ed.js.map +1 -0
  116. package/dist/router.es.mjs +9 -8
  117. package/dist/ssr/hydrate.d.ts.map +1 -1
  118. package/dist/{ssr-B2qd_WBB.js → ssr-_dAcGdzu.js} +4 -4
  119. package/dist/ssr-_dAcGdzu.js.map +1 -0
  120. package/dist/ssr.es.mjs +1 -1
  121. package/dist/store/persisted.d.ts.map +1 -1
  122. package/dist/{store-DWpyH6p5.js → store-Cb3gPRve.js} +7 -7
  123. package/dist/store-Cb3gPRve.js.map +1 -0
  124. package/dist/store.es.mjs +2 -2
  125. package/dist/storybook.es.mjs.map +1 -1
  126. package/dist/{testing-CsqjNUyy.js → testing-C5Sjfsna.js} +8 -8
  127. package/dist/testing-C5Sjfsna.js.map +1 -0
  128. package/dist/testing.es.mjs +1 -1
  129. package/dist/{type-guards-Do9DWgNp.js → type-guards-BMX2c0LP.js} +1 -1
  130. package/dist/{type-guards-Do9DWgNp.js.map → type-guards-BMX2c0LP.js.map} +1 -1
  131. package/dist/untrack-D0fnO5k2.js +36 -0
  132. package/dist/untrack-D0fnO5k2.js.map +1 -0
  133. package/dist/view/custom-directives.d.ts.map +1 -1
  134. package/dist/view.es.mjs +4 -4
  135. package/package.json +177 -177
  136. package/src/a11y/announce.ts +131 -131
  137. package/src/a11y/audit.ts +314 -314
  138. package/src/a11y/index.ts +68 -68
  139. package/src/a11y/media-preferences.ts +255 -255
  140. package/src/a11y/roving-tab-index.ts +164 -164
  141. package/src/a11y/skip-link.ts +255 -255
  142. package/src/a11y/trap-focus.ts +184 -184
  143. package/src/a11y/types.ts +183 -183
  144. package/src/component/component.ts +599 -599
  145. package/src/component/html.ts +153 -153
  146. package/src/component/index.ts +52 -52
  147. package/src/component/library.ts +540 -542
  148. package/src/component/scope.ts +212 -212
  149. package/src/component/types.ts +310 -310
  150. package/src/core/collection.ts +876 -707
  151. package/src/core/element.ts +1015 -981
  152. package/src/core/env.ts +60 -60
  153. package/src/core/index.ts +49 -49
  154. package/src/core/shared.ts +77 -62
  155. package/src/core/utils/index.ts +148 -148
  156. package/src/devtools/devtools.ts +410 -410
  157. package/src/devtools/index.ts +48 -48
  158. package/src/devtools/types.ts +104 -104
  159. package/src/dnd/draggable.ts +296 -296
  160. package/src/dnd/droppable.ts +228 -228
  161. package/src/dnd/index.ts +62 -62
  162. package/src/dnd/sortable.ts +307 -307
  163. package/src/dnd/types.ts +293 -293
  164. package/src/forms/create-form.ts +320 -278
  165. package/src/forms/index.ts +70 -65
  166. package/src/forms/types.ts +203 -154
  167. package/src/forms/use-field.ts +231 -0
  168. package/src/forms/validators.ts +294 -265
  169. package/src/full.ts +554 -480
  170. package/src/i18n/formatting.ts +67 -67
  171. package/src/i18n/i18n.ts +200 -200
  172. package/src/i18n/index.ts +67 -67
  173. package/src/i18n/translate.ts +182 -182
  174. package/src/i18n/types.ts +171 -171
  175. package/src/index.ts +108 -108
  176. package/src/media/battery.ts +116 -116
  177. package/src/media/breakpoints.ts +129 -131
  178. package/src/media/clipboard.ts +80 -80
  179. package/src/media/device-sensors.ts +158 -158
  180. package/src/media/geolocation.ts +119 -119
  181. package/src/media/index.ts +76 -76
  182. package/src/media/media-query.ts +92 -92
  183. package/src/media/network.ts +115 -115
  184. package/src/media/types.ts +177 -177
  185. package/src/media/viewport.ts +84 -84
  186. package/src/motion/index.ts +57 -57
  187. package/src/motion/morph.ts +151 -151
  188. package/src/motion/parallax.ts +120 -120
  189. package/src/motion/reduced-motion.ts +66 -66
  190. package/src/motion/types.ts +271 -271
  191. package/src/motion/typewriter.ts +164 -164
  192. package/src/plugin/index.ts +37 -37
  193. package/src/plugin/registry.ts +284 -269
  194. package/src/plugin/types.ts +137 -137
  195. package/src/reactive/async-data.ts +250 -29
  196. package/src/reactive/computed.ts +144 -130
  197. package/src/reactive/effect.ts +29 -6
  198. package/src/reactive/http.ts +790 -0
  199. package/src/reactive/index.ts +60 -0
  200. package/src/reactive/pagination.ts +317 -0
  201. package/src/reactive/polling.ts +179 -0
  202. package/src/reactive/readonly.ts +52 -8
  203. package/src/reactive/rest.ts +859 -0
  204. package/src/reactive/scope.ts +276 -0
  205. package/src/reactive/signal.ts +61 -1
  206. package/src/reactive/to-value.ts +71 -0
  207. package/src/reactive/websocket.ts +849 -0
  208. package/src/router/bq-link.ts +279 -279
  209. package/src/router/constraints.ts +204 -201
  210. package/src/router/index.ts +49 -49
  211. package/src/router/match.ts +312 -312
  212. package/src/router/path-pattern.ts +52 -52
  213. package/src/router/query.ts +38 -38
  214. package/src/router/router.ts +421 -402
  215. package/src/router/state.ts +51 -3
  216. package/src/router/types.ts +139 -139
  217. package/src/router/use-route.ts +68 -68
  218. package/src/router/utils.ts +157 -157
  219. package/src/security/index.ts +12 -12
  220. package/src/ssr/hydrate.ts +84 -82
  221. package/src/ssr/index.ts +70 -70
  222. package/src/ssr/render.ts +508 -508
  223. package/src/ssr/serialize.ts +296 -296
  224. package/src/ssr/types.ts +81 -81
  225. package/src/store/create-store.ts +467 -467
  226. package/src/store/index.ts +27 -27
  227. package/src/store/persisted.ts +245 -249
  228. package/src/store/types.ts +247 -247
  229. package/src/store/utils.ts +135 -135
  230. package/src/storybook/index.ts +480 -480
  231. package/src/testing/index.ts +42 -42
  232. package/src/testing/testing.ts +593 -593
  233. package/src/testing/types.ts +170 -170
  234. package/src/view/custom-directives.ts +28 -30
  235. package/src/view/evaluate.ts +292 -292
  236. package/src/view/process.ts +108 -108
  237. package/dist/a11y-C5QOVvRn.js.map +0 -1
  238. package/dist/component-CuuTijA6.js.map +0 -1
  239. package/dist/constraints-3lV9yyBw.js.map +0 -1
  240. package/dist/core-Cjl7GUu8.js.map +0 -1
  241. package/dist/core-DnlyjbF2.js +0 -112
  242. package/dist/core-DnlyjbF2.js.map +0 -1
  243. package/dist/custom-directives-7wAShnnd.js.map +0 -1
  244. package/dist/devtools-D2fQLhDN.js.map +0 -1
  245. package/dist/dnd-B8EgyzaI.js.map +0 -1
  246. package/dist/env-NeVmr4Gf.js.map +0 -1
  247. package/dist/forms-C3yovgH9.js +0 -141
  248. package/dist/forms-C3yovgH9.js.map +0 -1
  249. package/dist/i18n-BnnhTFOS.js.map +0 -1
  250. package/dist/media-Di2Ta22s.js.map +0 -1
  251. package/dist/motion-qPj_TYGv.js.map +0 -1
  252. package/dist/mount-SM07RUa6.js.map +0 -1
  253. package/dist/plugin-cPoOHFLY.js.map +0 -1
  254. package/dist/reactive-Cfv0RK6x.js +0 -233
  255. package/dist/reactive-Cfv0RK6x.js.map +0 -1
  256. package/dist/router-BrthaP_z.js +0 -473
  257. package/dist/router-BrthaP_z.js.map +0 -1
  258. package/dist/ssr-B2qd_WBB.js.map +0 -1
  259. package/dist/store-DWpyH6p5.js.map +0 -1
  260. package/dist/testing-CsqjNUyy.js.map +0 -1
  261. package/dist/untrack-DJVQQ2WM.js +0 -33
  262. package/dist/untrack-DJVQQ2WM.js.map +0 -1
package/README.md CHANGED
@@ -1,716 +1,760 @@
1
- <p align="center">
2
- <img src="assets/bquerry-logo.svg" alt="bQuery.js Logo" width="120" />
3
- </p>
4
-
5
- <h1 align="center">bQuery.js</h1>
6
-
7
- <p align="center">
8
-
9
- [![Repo](https://img.shields.io/badge/github-bquery%2Fbquery-24292f?logo=github)](https://github.com/bQuery/bQuery)
10
- [![Stars](https://img.shields.io/github/stars/bquery/bquery?style=flat&logo=github)](https://github.com/bQuery/bQuery/stargazers)
11
- [![Issues](https://img.shields.io/github/issues/bquery/bquery?style=flat&logo=github)](https://github.com/bQuery/bQuery/issues)
12
- [![License](https://img.shields.io/github/license/bquery/bquery?style=flat)](https://github.com/bQuery/bQuery/blob/main/LICENSE.md)
13
- [![npm](https://img.shields.io/npm/v/@bquery/bquery)](https://www.npmjs.com/package/@bquery/bquery)
14
- [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@bquery/bquery)](https://bundlephobia.com/package/@bquery/bquery)
15
- [![unpkg](https://img.shields.io/badge/unpkg-browse-blue?logo=unpkg)](https://unpkg.com/@bquery/bquery)
16
- [![CodeFactor](https://www.codefactor.io/repository/github/bquery/bquery/badge)](https://www.codefactor.io/repository/github/bquery/bquery)
17
- [![JsDelivr](https://data.jsdelivr.com/v1/package/npm/@bquery/bquery/badge)](https://www.jsdelivr.com/package/npm/@bquery/bquery)
18
-
19
- </p>
20
-
21
- **The jQuery for the modern Web Platform.**
22
-
23
- bQuery.js is a slim, TypeScript-first library that combines jQuery's direct DOM workflow with modern features like reactivity, async data composables, Web Components, motion utilities, routing, stores, declarative views, accessibility helpers, forms, i18n, media signals, drag-and-drop, plugins, devtools, testing utilities, and SSR — without a mandatory build step.
24
-
25
- ## Highlights
26
-
27
- - **Zero-build capable**: runs directly in the browser; build tools are optional.
28
- - **Async data built-in**: fetch and async state composables integrate directly with signals.
29
- - **Security-focused**: DOM writes are sanitized by default; Trusted Types supported.
30
- - **Modular**: the core stays small; extra modules are opt-in.
31
- - **TypeScript-first**: clear types and strong IDE support.
32
- - **Tree-shakeable**: import only what you need.
33
- - **Storybook-ready**: default components can be previewed and developed in Storybook with dedicated story template helpers.
34
-
35
- ## Installation
36
-
37
- ### Via npm/bun/pnpm
38
-
39
- ```bash
40
- # npm
41
- npm install @bquery/bquery
42
-
43
- # bun
44
- bun add @bquery/bquery
45
-
46
- # pnpm
47
- pnpm add @bquery/bquery
48
- ```
49
-
50
- ### Via CDN (Zero-build)
51
-
52
- #### ES Modules (recommended)
53
-
54
- ```html
55
- <script type="module">
56
- import { $, signal } from 'https://unpkg.com/@bquery/bquery@1/dist/full.es.mjs';
57
-
58
- const count = signal(0);
59
- $('#counter').text(`Count: ${count.value}`);
60
- </script>
61
- ```
62
-
63
- #### UMD (global variable)
64
-
65
- ```html
66
- <script src="https://unpkg.com/@bquery/bquery@1/dist/full.umd.js"></script>
67
- <script>
68
- const { $, signal } = bQuery;
69
- const count = signal(0);
70
- </script>
71
- ```
72
-
73
- #### IIFE (self-executing)
74
-
75
- ```html
76
- <script src="https://unpkg.com/@bquery/bquery@1/dist/full.iife.js"></script>
77
- <script>
78
- const { $, $$ } = bQuery;
79
- $$('.items').addClass('loaded');
80
- </script>
81
- ```
82
-
83
- ## Import Strategies
84
-
85
- ```ts
86
- // Full bundle (all modules)
87
- import {
88
- $,
89
- signal,
90
- component,
91
- registerDefaultComponents,
92
- defineBqueryConfig,
93
- } from '@bquery/bquery';
94
-
95
- // Core only
96
- import { $, $$ } from '@bquery/bquery/core';
97
-
98
- // Core utilities (named exports, tree-shakeable)
99
- import { debounce, merge, uid, once, utils } from '@bquery/bquery/core';
100
-
101
- // Reactive only
102
- import {
103
- signal,
104
- computed,
105
- effect,
106
- linkedSignal,
107
- persistedSignal,
108
- useAsyncData,
109
- useFetch,
110
- createUseFetch,
111
- } from '@bquery/bquery/reactive';
112
-
113
- // Components only
114
- import {
115
- bool,
116
- component,
117
- defineComponent,
118
- html,
119
- registerDefaultComponents,
120
- } from '@bquery/bquery/component';
121
-
122
- // Motion only
123
- import { transition, spring, animate, timeline } from '@bquery/bquery/motion';
124
-
125
- // Security only
126
- import { sanitize, sanitizeHtml, trusted } from '@bquery/bquery/security';
127
-
128
- // Platform only
129
- import { storage, cache, useCookie, definePageMeta, useAnnouncer } from '@bquery/bquery/platform';
130
-
131
- // Router, Store, View
132
- import { createRouter, navigate } from '@bquery/bquery/router';
133
- import { createStore, defineStore } from '@bquery/bquery/store';
134
- import { mount, createTemplate } from '@bquery/bquery/view';
135
-
136
- // Forms, i18n, accessibility, drag & drop, media
137
- import { createForm, required, email } from '@bquery/bquery/forms';
138
- import { createI18n } from '@bquery/bquery/i18n';
139
- import { trapFocus, rovingTabIndex } from '@bquery/bquery/a11y';
140
- import { draggable, droppable, sortable } from '@bquery/bquery/dnd';
141
- import { mediaQuery, useViewport, clipboard } from '@bquery/bquery/media';
142
-
143
- // Plugins, devtools, testing, SSR
144
- import { use } from '@bquery/bquery/plugin';
145
- import { enableDevtools, inspectSignals } from '@bquery/bquery/devtools';
146
- import { renderComponent, fireEvent, waitFor } from '@bquery/bquery/testing';
147
- import { renderToString, hydrateMount, serializeStoreState } from '@bquery/bquery/ssr';
148
-
149
- // Storybook helpers
150
- import { storyHtml, when } from '@bquery/bquery/storybook';
151
- ```
152
-
153
- ## Modules at a glance
154
-
155
- | Module | Description |
156
- | ------------- | ------------------------------------------------------------------------------------- |
157
- | **Core** | Selectors, DOM manipulation, events, traversal, and typed utilities |
158
- | **Reactive** | `signal`, `computed`, `effect`, batching, async data, and fetch composables |
159
- | **Component** | Typed Web Components with scoped reactivity and configurable Shadow DOM |
160
- | **Storybook** | Safe story template helpers with boolean-attribute shorthand |
161
- | **Motion** | View transitions, FLIP, morphing, parallax, typewriter, springs, and timelines |
162
- | **Security** | HTML sanitization, Trusted Types, CSP helpers, and trusted fragment composition |
163
- | **Platform** | Storage, cache, cookies, page metadata, announcers, and shared runtime config |
164
- | **Router** | SPA routing, constrained params, redirects, guards, `useRoute()`, and `<bq-link>` |
165
- | **Store** | Signal-based state management, persistence, migrations, and action hooks |
166
- | **View** | Declarative DOM bindings, directives, and plugin-provided custom directives |
167
- | **Forms** | Reactive form state with sync/async validation and submit handling |
168
- | **i18n** | Reactive locales, interpolation, pluralization, lazy loading, and Intl formatting |
169
- | **A11y** | Focus traps, live-region announcements, roving tabindex, skip links, and audits |
170
- | **DnD** | Draggable elements, droppable zones, and sortable lists |
171
- | **Media** | Reactive browser/device signals for viewport, network, battery, geolocation, and more |
172
- | **Plugin** | Global plugin registration for custom directives and Web Components |
173
- | **Devtools** | Runtime inspection helpers for signals, stores, components, and timelines |
174
- | **Testing** | Component mounting, mock signals/router helpers, and async test utilities |
175
- | **SSR** | Server-side rendering, hydration, and store-state serialization |
176
-
177
- Storybook authoring helpers are also available as a dedicated entry point via `@bquery/bquery/storybook`.
178
-
179
- ## Quick examples
180
-
181
- ### Core DOM & events
182
-
183
- ```ts
184
- import { $, $$ } from '@bquery/bquery/core';
185
-
186
- $('#save').on('click', (event) => {
187
- console.log('Saved', event.type);
188
- });
189
-
190
- $('#list').delegate('click', '.item', (event, target) => {
191
- console.log('Item clicked', target.textContent);
192
- });
193
-
194
- $('#box').addClass('active').css({ opacity: '0.8' }).attr('data-state', 'ready');
195
-
196
- const color = $('#box').css('color');
197
-
198
- if ($('#el').is('.active')) {
199
- console.log('Element is active');
200
- }
201
-
202
- $$('.container').find('.item').addClass('found');
203
- ```
204
-
205
- ### Reactive – signals
206
-
207
- ```ts
208
- import {
209
- signal,
210
- computed,
211
- effect,
212
- batch,
213
- watch,
214
- readonly,
215
- linkedSignal,
216
- } from '@bquery/bquery/reactive';
217
-
218
- const count = signal(0);
219
- const doubled = computed(() => count.value * 2);
220
-
221
- effect(() => {
222
- console.log('Count changed', count.value);
223
- });
224
-
225
- watch(count, (newVal, oldVal) => {
226
- console.log(`Changed from ${oldVal} to ${newVal}`);
227
- });
228
-
229
- const readOnlyCount = readonly(count);
230
-
231
- batch(() => {
232
- count.value++;
233
- count.value++;
234
- });
235
-
236
- count.dispose();
237
-
238
- const first = signal('Ada');
239
- const last = signal('Lovelace');
240
- const fullName = linkedSignal(
241
- () => `${first.value} ${last.value}`,
242
- (next) => {
243
- const [nextFirst, nextLast] = next.split(' ');
244
- first.value = nextFirst ?? '';
245
- last.value = nextLast ?? '';
246
- }
247
- );
248
-
249
- fullName.value = 'Grace Hopper';
250
- ```
251
-
252
- ### Reactive – async data & fetch
253
-
254
- ```ts
255
- import { signal, useFetch, createUseFetch } from '@bquery/bquery/reactive';
256
-
257
- const userId = signal(1);
258
-
259
- const user = useFetch<{ id: number; name: string }>(() => `/users/${userId.value}`, {
260
- baseUrl: 'https://api.example.com',
261
- watch: [userId],
262
- query: { include: 'profile' },
263
- });
264
-
265
- const useApiFetch = createUseFetch({
266
- baseUrl: 'https://api.example.com',
267
- headers: { 'x-client': 'bquery-readme' },
268
- });
269
-
270
- const settings = useApiFetch<{ theme: string }>('/settings');
271
-
272
- console.log(user.pending.value, user.data.value, settings.error.value);
273
- ```
274
-
275
- ### Components Web Components
276
-
277
- ```ts
278
- import {
279
- bool,
280
- component,
281
- defineComponent,
282
- html,
283
- registerDefaultComponents,
284
- safeHtml,
285
- } from '@bquery/bquery/component';
286
- import { sanitizeHtml, trusted } from '@bquery/bquery/security';
287
-
288
- const badge = trusted(sanitizeHtml('<span class="badge">Active</span>'));
289
-
290
- component('user-card', {
291
- props: {
292
- username: { type: String, required: true },
293
- age: { type: Number, validator: (v) => v >= 0 && v <= 150 },
294
- },
295
- state: { count: 0 },
296
- beforeMount() {
297
- console.log('About to mount');
298
- },
299
- connected() {
300
- console.log('Mounted');
301
- },
302
- beforeUpdate(newProps, oldProps) {
303
- return newProps.username !== oldProps.username;
304
- },
305
- updated(change) {
306
- console.log('Updated because of', change?.name ?? 'state/signal change');
307
- },
308
- onError(error) {
309
- console.error('Component error:', error);
310
- },
311
- render({ props, state }) {
312
- return safeHtml`
313
- <button class="user-card" ${bool('disabled', state.count > 3)}>
314
- ${badge}
315
- <span>Hello ${props.username}</span>
316
- </button>
317
- `;
318
- },
319
- });
320
-
321
- const UserCard = defineComponent('user-card-manual', {
322
- props: { username: { type: String, required: true } },
323
- render: ({ props }) => html`<div>Hello ${props.username}</div>`,
324
- });
325
-
326
- customElements.define('user-card-manual', UserCard);
327
-
328
- const tags = registerDefaultComponents({ prefix: 'ui' });
329
- console.log(tags.button); // ui-button
330
- ```
331
-
332
- ### Storybook authoring helpers
333
-
334
- ```ts
335
- import { storyHtml, when } from '@bquery/bquery/storybook';
336
-
337
- export const Primary = {
338
- args: { disabled: false, label: 'Save' },
339
- render: ({ disabled, label }) =>
340
- storyHtml`
341
- <ui-card>
342
- <ui-button ?disabled=${disabled}>${label}</ui-button>
343
- ${when(!disabled, '<small>Ready to submit</small>')}
344
- </ui-card>
345
- `,
346
- };
347
- ```
348
-
349
- ### Motion – animations
350
-
351
- ```ts
352
- import { animate, keyframePresets, spring, transition } from '@bquery/bquery/motion';
353
-
354
- await transition({
355
- update: () => {
356
- $('#content').text('Updated');
357
- },
358
- classes: ['page-transition'],
359
- types: ['navigation'],
360
- skipOnReducedMotion: true,
361
- });
362
-
363
- await animate(card, {
364
- keyframes: keyframePresets.pop(),
365
- options: { duration: 240, easing: 'ease-out' },
366
- });
367
-
368
- const x = spring(0, { stiffness: 120, damping: 14 });
369
- x.onChange((value) => {
370
- element.style.transform = `translateX(${value}px)`;
371
- });
372
- await x.to(100);
373
- ```
374
-
375
- ### Security – sanitizing
376
-
377
- ```ts
378
- import { sanitize, escapeHtml, sanitizeHtml, trusted } from '@bquery/bquery/security';
379
- import { safeHtml } from '@bquery/bquery/component';
380
-
381
- const safeMarkup = sanitize(userInput);
382
- const safe = sanitize('<form id="cookie">...</form>');
383
- const urlSafe = sanitize('<a href="java\u200Bscript:alert(1)">click</a>');
384
- const secureLink = sanitize('<a href="https://external.com" target="_blank">Link</a>');
385
- const safeSrcset = sanitize('<img srcset="safe.jpg 1x, javascript:alert(1) 2x">');
386
- const safeForm = sanitize('<form action="javascript:alert(1)">...</form>');
387
- const escaped = escapeHtml('<script>alert(1)</script>');
388
- const icon = trusted(sanitizeHtml('<span class="icon">♥</span>'));
389
- const button = safeHtml`<button>${icon}<span>Save</span></button>`;
390
- ```
391
-
392
- ### Platform – config, cookies & accessibility
393
-
394
- ```ts
395
- import {
396
- defineBqueryConfig,
397
- useCookie,
398
- definePageMeta,
399
- useAnnouncer,
400
- storage,
401
- notifications,
402
- } from '@bquery/bquery/platform';
403
-
404
- defineBqueryConfig({
405
- fetch: { baseUrl: 'https://api.example.com' },
406
- transitions: { skipOnReducedMotion: true, classes: ['page-transition'] },
407
- components: { prefix: 'ui' },
408
- });
409
-
410
- const theme = useCookie<'light' | 'dark'>('theme', { defaultValue: 'light' });
411
- const cleanupMeta = definePageMeta({ title: 'Dashboard' });
412
- const announcer = useAnnouncer();
413
-
414
- theme.value = 'dark';
415
- announcer.announce('Preferences saved');
416
- cleanupMeta();
417
-
418
- const local = storage.local();
419
- await local.set('theme', theme.value);
420
-
421
- const permission = await notifications.requestPermission();
422
- if (permission === 'granted') {
423
- notifications.send('Build complete', {
424
- body: 'Your docs are ready.',
425
- });
426
- }
427
- ```
428
-
429
- ### Router SPA navigation
430
-
431
- ```ts
432
- import { effect } from '@bquery/bquery/reactive';
433
- import { createRouter, navigate, currentRoute } from '@bquery/bquery/router';
434
-
435
- const router = createRouter({
436
- routes: [
437
- { path: '/', name: 'home', component: HomePage },
438
- { path: '/user/:id', name: 'user', component: UserPage },
439
- { path: '*', component: NotFound },
440
- ],
441
- });
442
-
443
- router.beforeEach(async (to) => {
444
- if (to.path === '/admin' && !isAuthenticated()) {
445
- await navigate('/login');
446
- return false;
447
- }
448
- });
449
-
450
- effect(() => {
451
- console.log('Current path:', currentRoute.value.path);
452
- });
453
- ```
454
-
455
- ### Forms reactive validation
456
-
457
- ```ts
458
- import { createForm, email, required } from '@bquery/bquery/forms';
459
-
460
- const form = createForm({
461
- fields: {
462
- name: { initialValue: '', validators: [required()] },
463
- email: { initialValue: '', validators: [required(), email()] },
464
- },
465
- onSubmit: async (values) => {
466
- await fetch('/api/signup', {
467
- method: 'POST',
468
- body: JSON.stringify(values),
469
- });
470
- },
471
- });
472
-
473
- await form.handleSubmit();
474
- console.log(form.isValid.value, form.fields.email.error.value);
475
- ```
476
-
477
- ### i18n locale-aware content
478
-
479
- ```ts
480
- import { createI18n } from '@bquery/bquery/i18n';
481
-
482
- const i18n = createI18n({
483
- locale: 'en',
484
- fallbackLocale: 'en',
485
- messages: {
486
- en: { greeting: 'Hello, {name}!' },
487
- de: { greeting: 'Hallo, {name}!' },
488
- },
489
- });
490
-
491
- console.log(i18n.t('greeting', { name: 'Ada' }));
492
- i18n.$locale.value = 'de';
493
- ```
494
-
495
- ### Accessibility, media, and drag & drop
496
-
497
- ```ts
498
- import { trapFocus, announceToScreenReader } from '@bquery/bquery/a11y';
499
- import { mediaQuery, useViewport } from '@bquery/bquery/media';
500
- import { draggable } from '@bquery/bquery/dnd';
501
-
502
- const modalTrap = trapFocus(document.querySelector('#dialog')!);
503
- announceToScreenReader('Dialog opened');
504
-
505
- const isDark = mediaQuery('(prefers-color-scheme: dark)');
506
- const viewport = useViewport();
507
- const drag = draggable(document.querySelector('#card')!, { bounds: 'parent' });
508
-
509
- console.log(isDark.value, viewport.value.width);
510
-
511
- drag.destroy();
512
- modalTrap.release();
513
- ```
514
-
515
- ### Plugins, devtools, testing, and SSR
516
-
517
- ```ts
518
- import { use } from '@bquery/bquery/plugin';
519
- import { enableDevtools, getTimeline } from '@bquery/bquery/devtools';
520
- import { renderComponent, fireEvent } from '@bquery/bquery/testing';
521
- import { renderToString } from '@bquery/bquery/ssr';
522
-
523
- use({
524
- name: 'focus-plugin',
525
- install(ctx) {
526
- ctx.directive('focus', (el) => (el as HTMLElement).focus());
527
- },
528
- });
529
-
530
- enableDevtools(true, { logToConsole: true });
531
- console.log(getTimeline());
532
-
533
- const mounted = renderComponent('ui-button', { props: { variant: 'primary' } });
534
- fireEvent(mounted.el, 'click');
535
-
536
- const { html } = renderToString('<p bq-text="label"></p>', { label: 'Hello SSR' });
537
- console.log(html);
538
-
539
- mounted.unmount();
540
- ```
541
-
542
- ### Store state management
543
-
544
- ```ts
545
- import {
546
- createStore,
547
- createPersistedStore,
548
- defineStore,
549
- mapGetters,
550
- watchStore,
551
- } from '@bquery/bquery/store';
552
-
553
- const counterStore = createStore({
554
- id: 'counter',
555
- state: () => ({ count: 0, name: 'Counter' }),
556
- getters: {
557
- doubled: (state) => state.count * 2,
558
- },
559
- actions: {
560
- increment() {
561
- this.count++;
562
- },
563
- },
564
- });
565
-
566
- const settingsStore = createPersistedStore({
567
- id: 'settings',
568
- state: () => ({ theme: 'dark', language: 'en' }),
569
- });
570
-
571
- const useCounter = defineStore('counter', {
572
- state: () => ({ count: 0 }),
573
- getters: {
574
- doubled: (state) => state.count * 2,
575
- },
576
- actions: {
577
- increment() {
578
- this.count++;
579
- },
580
- },
581
- });
582
-
583
- const counter = useCounter();
584
- const getters = mapGetters(counter, ['doubled']);
585
-
586
- watchStore(
587
- counter,
588
- (state) => state.count,
589
- (value) => {
590
- console.log('Count changed:', value, getters.doubled);
591
- }
592
- );
593
- ```
594
-
595
- ### View – declarative bindings
596
-
597
- ```ts
598
- import { mount, createTemplate } from '@bquery/bquery/view';
599
- import { signal } from '@bquery/bquery/reactive';
600
-
601
- const count = signal(0);
602
- const items = signal(['Apple', 'Banana', 'Cherry']);
603
-
604
- mount('#app', {
605
- count,
606
- items,
607
- increment: () => count.value++,
608
- });
609
- ```
610
-
611
- ## Browser Support
612
-
613
- | Browser | Version | Support |
614
- | ------- | ------- | ------- |
615
- | Chrome | 90+ | Full |
616
- | Firefox | 90+ | Full |
617
- | Safari | 15+ | ✅ Full |
618
- | Edge | 90+ | Full |
619
-
620
- > **No IE support** by design.
621
-
622
- ## Documentation
623
-
624
- - **Getting Started**: [docs/guide/getting-started.md](docs/guide/getting-started.md)
625
- - **Core API**: [docs/guide/api-core.md](docs/guide/api-core.md)
626
- - **Agents**: [docs/guide/agents.md](docs/guide/agents.md)
627
- - **Components**: [docs/guide/components.md](docs/guide/components.md)
628
- - **Storybook**: [docs/guide/storybook.md](docs/guide/storybook.md)
629
- - **Reactivity**: [docs/guide/reactive.md](docs/guide/reactive.md)
630
- - **Motion**: [docs/guide/motion.md](docs/guide/motion.md)
631
- - **Security**: [docs/guide/security.md](docs/guide/security.md)
632
- - **Platform**: [docs/guide/platform.md](docs/guide/platform.md)
633
- - **Router**: [docs/guide/router.md](docs/guide/router.md)
634
- - **Store**: [docs/guide/store.md](docs/guide/store.md)
635
- - **View**: [docs/guide/view.md](docs/guide/view.md)
636
- - **Forms**: [docs/guide/forms.md](docs/guide/forms.md)
637
- - **i18n**: [docs/guide/i18n.md](docs/guide/i18n.md)
638
- - **Accessibility**: [docs/guide/a11y.md](docs/guide/a11y.md)
639
- - **Drag & Drop**: [docs/guide/dnd.md](docs/guide/dnd.md)
640
- - **Media**: [docs/guide/media.md](docs/guide/media.md)
641
- - **Plugin System**: [docs/guide/plugin.md](docs/guide/plugin.md)
642
- - **Devtools**: [docs/guide/devtools.md](docs/guide/devtools.md)
643
- - **Testing Utilities**: [docs/guide/testing.md](docs/guide/testing.md)
644
- - **SSR / Hydration**: [docs/guide/ssr.md](docs/guide/ssr.md)
645
-
646
- ## Local Development
647
-
648
- ```bash
649
- # Install dependencies
650
- bun install
651
-
652
- # Start VitePress docs
653
- bun run dev
654
-
655
- # Run Storybook
656
- bun run storybook
657
-
658
- # Run tests
659
- bun test
660
-
661
- # Build library
662
- bun run build
663
-
664
- # Build docs
665
- bun run build:docs
666
-
667
- # Generate API documentation
668
- bun run docs:api
669
- ```
670
-
671
- ## Project Structure
672
-
673
- ```text
674
- bQuery.js
675
- ├── src/
676
- │ ├── core/ # Selectors, DOM ops, events, utils
677
- │ ├── reactive/ # Signals, computed, effects, async data
678
- │ ├── component/ # Web Components helper + default library
679
- │ ├── storybook/ # Story template helpers
680
- │ ├── motion/ # View transitions, FLIP, springs
681
- │ ├── security/ # Sanitizer, CSP, Trusted Types
682
- │ ├── platform/ # Storage, cache, cookies, meta, config
683
- │ ├── router/ # SPA routing, navigation guards
684
- │ ├── store/ # State management, persistence
685
- │ ├── view/ # Declarative DOM bindings
686
- │ ├── forms/ # Reactive forms + validators
687
- │ ├── i18n/ # Internationalization + formatting
688
- │ ├── a11y/ # Accessibility utilities
689
- │ ├── dnd/ # Drag & drop helpers
690
- │ ├── media/ # Browser and device reactive signals
691
- │ ├── plugin/ # Global plugin system
692
- │ ├── devtools/ # Runtime inspection helpers
693
- │ ├── testing/ # Test utilities
694
- │ └── ssr/ # Server-side rendering + hydration
695
- ├── docs/ # VitePress documentation
696
- ├── .storybook/ # Storybook config
697
- ├── stories/ # Component stories
698
- ├── tests/ # bun:test suites
699
- └── dist/ # Built files (ESM, UMD, IIFE)
700
- ```
701
-
702
- ## Contributing
703
-
704
- See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
705
-
706
- ## AI Agent Support
707
-
708
- This project provides dedicated context files for AI coding agents:
709
-
710
- - **[AGENT.md](AGENT.md)** — Architecture, module API reference, coding conventions, common tasks
711
- - **[llms.txt](llms.txt)** Compact LLM-optimized project summary
712
- - **[.github/copilot-instructions.md](.github/copilot-instructions.md)** — GitHub Copilot context
713
-
714
- ## License
715
-
716
- MIT – See [LICENSE.md](LICENSE.md) for details.
1
+ <p align="center">
2
+ <img src="assets/bquerry-logo.svg" alt="bQuery.js Logo" width="120" />
3
+ </p>
4
+
5
+ <h1 align="center">bQuery.js</h1>
6
+
7
+ <p align="center">
8
+
9
+ [![Repo](https://img.shields.io/badge/github-bquery%2Fbquery-24292f?logo=github)](https://github.com/bQuery/bQuery)
10
+ [![Stars](https://img.shields.io/github/stars/bquery/bquery?style=flat&logo=github)](https://github.com/bQuery/bQuery/stargazers)
11
+ [![Issues](https://img.shields.io/github/issues/bquery/bquery?style=flat&logo=github)](https://github.com/bQuery/bQuery/issues)
12
+ [![License](https://img.shields.io/github/license/bquery/bquery?style=flat)](https://github.com/bQuery/bQuery/blob/main/LICENSE.md)
13
+ [![npm](https://img.shields.io/npm/v/@bquery/bquery)](https://www.npmjs.com/package/@bquery/bquery)
14
+ [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@bquery/bquery)](https://bundlephobia.com/package/@bquery/bquery)
15
+ [![unpkg](https://img.shields.io/badge/unpkg-browse-blue?logo=unpkg)](https://unpkg.com/@bquery/bquery)
16
+ [![CodeFactor](https://www.codefactor.io/repository/github/bquery/bquery/badge)](https://www.codefactor.io/repository/github/bquery/bquery)
17
+ [![JsDelivr](https://data.jsdelivr.com/v1/package/npm/@bquery/bquery/badge)](https://www.jsdelivr.com/package/npm/@bquery/bquery)
18
+
19
+ </p>
20
+
21
+ **The jQuery for the modern Web Platform.**
22
+
23
+ bQuery.js is a slim, TypeScript-first library that combines jQuery's direct DOM workflow with modern features like reactivity, async data composables, HTTP clients, polling and pagination helpers, realtime transports, REST workflows, Web Components, motion utilities, routing, stores, declarative views, accessibility helpers, forms, i18n, media signals, drag-and-drop, plugins, devtools, testing utilities, and SSR — without a mandatory build step.
24
+
25
+ > **New in 1.8.0:** the Reactive module now also covers transport-ready workflows such as `createHttp()`, `usePolling()`, `usePaginatedFetch()`, `useInfiniteFetch()`, `useWebSocket()`, `useEventSource()`, `useResource()`, `useSubmit()`, `createRequestQueue()`, and `deduplicateRequest()`.
26
+
27
+ ## Highlights
28
+
29
+ - **Zero-build capable**: runs directly in the browser; build tools are optional.
30
+ - **Transport-ready reactive data**: fetch composables, HTTP clients, polling, pagination, WebSocket / SSE, REST helpers, and request coordination plug directly into signals.
31
+ - **Security-focused**: DOM writes are sanitized by default; Trusted Types supported.
32
+ - **Modular**: the core stays small; extra modules are opt-in.
33
+ - **TypeScript-first**: clear types and strong IDE support.
34
+ - **Tree-shakeable**: import only what you need.
35
+ - **Storybook-ready**: default components can be previewed and developed in Storybook with dedicated story template helpers.
36
+
37
+ ## Installation
38
+
39
+ ### Via npm/bun/pnpm
40
+
41
+ ```bash
42
+ # npm
43
+ npm install @bquery/bquery
44
+
45
+ # bun
46
+ bun add @bquery/bquery
47
+
48
+ # pnpm
49
+ pnpm add @bquery/bquery
50
+ ```
51
+
52
+ ### Via CDN (Zero-build)
53
+
54
+ #### ES Modules (recommended)
55
+
56
+ ```html
57
+ <script type="module">
58
+ import { $, signal } from 'https://unpkg.com/@bquery/bquery@1/dist/full.es.mjs';
59
+
60
+ const count = signal(0);
61
+ $('#counter').text(`Count: ${count.value}`);
62
+ </script>
63
+ ```
64
+
65
+ #### UMD (global variable)
66
+
67
+ ```html
68
+ <script src="https://unpkg.com/@bquery/bquery@1/dist/full.umd.js"></script>
69
+ <script>
70
+ const { $, signal } = bQuery;
71
+ const count = signal(0);
72
+ </script>
73
+ ```
74
+
75
+ #### IIFE (self-executing)
76
+
77
+ ```html
78
+ <script src="https://unpkg.com/@bquery/bquery@1/dist/full.iife.js"></script>
79
+ <script>
80
+ const { $, $$ } = bQuery;
81
+ $$('.items').addClass('loaded');
82
+ </script>
83
+ ```
84
+
85
+ ## Import Strategies
86
+
87
+ ```ts
88
+ // Full bundle (all modules)
89
+ import {
90
+ $,
91
+ signal,
92
+ component,
93
+ registerDefaultComponents,
94
+ defineBqueryConfig,
95
+ } from '@bquery/bquery';
96
+
97
+ // Core only
98
+ import { $, $$ } from '@bquery/bquery/core';
99
+
100
+ // Core utilities (named exports, tree-shakeable)
101
+ import { debounce, merge, uid, once, utils } from '@bquery/bquery/core';
102
+
103
+ // Reactive only
104
+ import {
105
+ signal,
106
+ computed,
107
+ effect,
108
+ linkedSignal,
109
+ persistedSignal,
110
+ useAsyncData,
111
+ useFetch,
112
+ createUseFetch,
113
+ createHttp,
114
+ http,
115
+ usePolling,
116
+ usePaginatedFetch,
117
+ useInfiniteFetch,
118
+ useWebSocket,
119
+ useWebSocketChannel,
120
+ useEventSource,
121
+ useResource,
122
+ useResourceList,
123
+ useSubmit,
124
+ createRestClient,
125
+ createRequestQueue,
126
+ deduplicateRequest,
127
+ } from '@bquery/bquery/reactive';
128
+
129
+ // Components only
130
+ import {
131
+ bool,
132
+ component,
133
+ defineComponent,
134
+ html,
135
+ registerDefaultComponents,
136
+ } from '@bquery/bquery/component';
137
+
138
+ // Motion only
139
+ import { transition, spring, animate, timeline } from '@bquery/bquery/motion';
140
+
141
+ // Security only
142
+ import { sanitize, sanitizeHtml, trusted } from '@bquery/bquery/security';
143
+
144
+ // Platform only
145
+ import { storage, cache, useCookie, definePageMeta, useAnnouncer } from '@bquery/bquery/platform';
146
+
147
+ // Router, Store, View
148
+ import { createRouter, navigate } from '@bquery/bquery/router';
149
+ import { createStore, defineStore } from '@bquery/bquery/store';
150
+ import { mount, createTemplate } from '@bquery/bquery/view';
151
+
152
+ // Forms, i18n, accessibility, drag & drop, media
153
+ import { createForm, required, email } from '@bquery/bquery/forms';
154
+ import { createI18n } from '@bquery/bquery/i18n';
155
+ import { trapFocus, rovingTabIndex } from '@bquery/bquery/a11y';
156
+ import { draggable, droppable, sortable } from '@bquery/bquery/dnd';
157
+ import { mediaQuery, useViewport, clipboard } from '@bquery/bquery/media';
158
+
159
+ // Plugins, devtools, testing, SSR
160
+ import { use } from '@bquery/bquery/plugin';
161
+ import { enableDevtools, inspectSignals } from '@bquery/bquery/devtools';
162
+ import { renderComponent, fireEvent, waitFor } from '@bquery/bquery/testing';
163
+ import { renderToString, hydrateMount, serializeStoreState } from '@bquery/bquery/ssr';
164
+
165
+ // Storybook helpers
166
+ import { storyHtml, when } from '@bquery/bquery/storybook';
167
+ ```
168
+
169
+ ## Modules at a glance
170
+
171
+ | Module | Description |
172
+ | ------------- | ---------------------------------------------------------------------------------------------------------------- |
173
+ | **Core** | Selectors, DOM manipulation, events, traversal, and typed utilities |
174
+ | **Reactive** | `signal`, `computed`, `effect`, async data, HTTP clients, polling, pagination, WebSocket / SSE, and REST helpers |
175
+ | **Component** | Typed Web Components with scoped reactivity and configurable Shadow DOM |
176
+ | **Storybook** | Safe story template helpers with boolean-attribute shorthand |
177
+ | **Motion** | View transitions, FLIP, morphing, parallax, typewriter, springs, and timelines |
178
+ | **Security** | HTML sanitization, Trusted Types, CSP helpers, and trusted fragment composition |
179
+ | **Platform** | Storage, cache, cookies, page metadata, announcers, and shared runtime config |
180
+ | **Router** | SPA routing, constrained params, redirects, guards, `useRoute()`, and `<bq-link>` |
181
+ | **Store** | Signal-based state management, persistence, migrations, and action hooks |
182
+ | **View** | Declarative DOM bindings, directives, and plugin-provided custom directives |
183
+ | **Forms** | Reactive form state with sync/async validation and submit handling |
184
+ | **i18n** | Reactive locales, interpolation, pluralization, lazy loading, and Intl formatting |
185
+ | **A11y** | Focus traps, live-region announcements, roving tabindex, skip links, and audits |
186
+ | **DnD** | Draggable elements, droppable zones, and sortable lists |
187
+ | **Media** | Reactive browser/device signals for viewport, network, battery, geolocation, and more |
188
+ | **Plugin** | Global plugin registration for custom directives and Web Components |
189
+ | **Devtools** | Runtime inspection helpers for signals, stores, components, and timelines |
190
+ | **Testing** | Component mounting, mock signals/router helpers, and async test utilities |
191
+ | **SSR** | Server-side rendering, hydration, and store-state serialization |
192
+
193
+ Storybook authoring helpers are also available as a dedicated entry point via `@bquery/bquery/storybook`.
194
+
195
+ ## Quick examples
196
+
197
+ ### Core – DOM & events
198
+
199
+ ```ts
200
+ import { $, $$ } from '@bquery/bquery/core';
201
+
202
+ $('#save').on('click', (event) => {
203
+ console.log('Saved', event.type);
204
+ });
205
+
206
+ $('#list').delegate('click', '.item', (event, target) => {
207
+ console.log('Item clicked', target.textContent);
208
+ });
209
+
210
+ $('#box').addClass('active').css({ opacity: '0.8' }).attr('data-state', 'ready');
211
+
212
+ const color = $('#box').css('color');
213
+
214
+ if ($('#el').is('.active')) {
215
+ console.log('Element is active');
216
+ }
217
+
218
+ $$('.container').find('.item').addClass('found');
219
+ ```
220
+
221
+ ### Reactive – signals
222
+
223
+ ```ts
224
+ import {
225
+ signal,
226
+ computed,
227
+ effect,
228
+ batch,
229
+ watch,
230
+ readonly,
231
+ linkedSignal,
232
+ } from '@bquery/bquery/reactive';
233
+
234
+ const count = signal(0);
235
+ const doubled = computed(() => count.value * 2);
236
+
237
+ effect(() => {
238
+ console.log('Count changed', count.value);
239
+ });
240
+
241
+ watch(count, (newVal, oldVal) => {
242
+ console.log(`Changed from ${oldVal} to ${newVal}`);
243
+ });
244
+
245
+ const readOnlyCount = readonly(count);
246
+
247
+ batch(() => {
248
+ count.value++;
249
+ count.value++;
250
+ });
251
+
252
+ count.dispose();
253
+
254
+ const first = signal('Ada');
255
+ const last = signal('Lovelace');
256
+ const fullName = linkedSignal(
257
+ () => `${first.value} ${last.value}`,
258
+ (next) => {
259
+ const [nextFirst, nextLast] = next.split(' ');
260
+ first.value = nextFirst ?? '';
261
+ last.value = nextLast ?? '';
262
+ }
263
+ );
264
+
265
+ fullName.value = 'Grace Hopper';
266
+ ```
267
+
268
+ ### Reactive – async data & fetch
269
+
270
+ ```ts
271
+ import { signal, useFetch, createUseFetch } from '@bquery/bquery/reactive';
272
+
273
+ const userId = signal(1);
274
+
275
+ const user = useFetch<{ id: number; name: string }>(() => `/users/${userId.value}`, {
276
+ baseUrl: 'https://api.example.com',
277
+ watch: [userId],
278
+ query: { include: 'profile' },
279
+ });
280
+
281
+ const useApiFetch = createUseFetch({
282
+ baseUrl: 'https://api.example.com',
283
+ headers: { 'x-client': 'bquery-readme' },
284
+ });
285
+
286
+ const settings = useApiFetch<{ theme: string }>('/settings');
287
+
288
+ console.log(user.pending.value, user.data.value, settings.error.value);
289
+ ```
290
+
291
+ ### Reactive – HTTP, streaming & request coordination
292
+
293
+ ```ts
294
+ import {
295
+ createHttp,
296
+ createRequestQueue,
297
+ deduplicateRequest,
298
+ useEventSource,
299
+ useWebSocket,
300
+ } from '@bquery/bquery/reactive';
301
+
302
+ const api = createHttp({
303
+ baseUrl: 'https://api.example.com',
304
+ retry: {
305
+ count: 2,
306
+ onRetry: (error, attempt) => console.warn(`Retry #${attempt}`, error.message),
307
+ },
308
+ });
309
+
310
+ const queue = createRequestQueue({ concurrency: 4 });
311
+ const ws = useWebSocket<{ type: string; payload: unknown }>('wss://api.example.com/live');
312
+ const sse = useEventSource<{ token: string }>('/api/stream');
313
+
314
+ const users = await deduplicateRequest('/users', () => queue.add(() => api.get('/users')));
315
+
316
+ console.log(users.data, ws.status.value, sse.eventName.value);
317
+ ```
318
+
319
+ ### Components – Web Components
320
+
321
+ ```ts
322
+ import {
323
+ bool,
324
+ component,
325
+ defineComponent,
326
+ html,
327
+ registerDefaultComponents,
328
+ safeHtml,
329
+ } from '@bquery/bquery/component';
330
+ import { sanitizeHtml, trusted } from '@bquery/bquery/security';
331
+
332
+ const badge = trusted(sanitizeHtml('<span class="badge">Active</span>'));
333
+
334
+ component('user-card', {
335
+ props: {
336
+ username: { type: String, required: true },
337
+ age: { type: Number, validator: (v) => v >= 0 && v <= 150 },
338
+ },
339
+ state: { count: 0 },
340
+ beforeMount() {
341
+ console.log('About to mount');
342
+ },
343
+ connected() {
344
+ console.log('Mounted');
345
+ },
346
+ beforeUpdate(newProps, oldProps) {
347
+ return newProps.username !== oldProps.username;
348
+ },
349
+ updated(change) {
350
+ console.log('Updated because of', change?.name ?? 'state/signal change');
351
+ },
352
+ onError(error) {
353
+ console.error('Component error:', error);
354
+ },
355
+ render({ props, state }) {
356
+ return safeHtml`
357
+ <button class="user-card" ${bool('disabled', state.count > 3)}>
358
+ ${badge}
359
+ <span>Hello ${props.username}</span>
360
+ </button>
361
+ `;
362
+ },
363
+ });
364
+
365
+ const UserCard = defineComponent('user-card-manual', {
366
+ props: { username: { type: String, required: true } },
367
+ render: ({ props }) => html`<div>Hello ${props.username}</div>`,
368
+ });
369
+
370
+ customElements.define('user-card-manual', UserCard);
371
+
372
+ const tags = registerDefaultComponents({ prefix: 'ui' });
373
+ console.log(tags.button); // ui-button
374
+ ```
375
+
376
+ ### Storybook – authoring helpers
377
+
378
+ ```ts
379
+ import { storyHtml, when } from '@bquery/bquery/storybook';
380
+
381
+ export const Primary = {
382
+ args: { disabled: false, label: 'Save' },
383
+ render: ({ disabled, label }) =>
384
+ storyHtml`
385
+ <ui-card>
386
+ <ui-button ?disabled=${disabled}>${label}</ui-button>
387
+ ${when(!disabled, '<small>Ready to submit</small>')}
388
+ </ui-card>
389
+ `,
390
+ };
391
+ ```
392
+
393
+ ### Motion – animations
394
+
395
+ ```ts
396
+ import { animate, keyframePresets, spring, transition } from '@bquery/bquery/motion';
397
+
398
+ await transition({
399
+ update: () => {
400
+ $('#content').text('Updated');
401
+ },
402
+ classes: ['page-transition'],
403
+ types: ['navigation'],
404
+ skipOnReducedMotion: true,
405
+ });
406
+
407
+ await animate(card, {
408
+ keyframes: keyframePresets.pop(),
409
+ options: { duration: 240, easing: 'ease-out' },
410
+ });
411
+
412
+ const x = spring(0, { stiffness: 120, damping: 14 });
413
+ x.onChange((value) => {
414
+ element.style.transform = `translateX(${value}px)`;
415
+ });
416
+ await x.to(100);
417
+ ```
418
+
419
+ ### Security – sanitizing
420
+
421
+ ```ts
422
+ import { sanitize, escapeHtml, sanitizeHtml, trusted } from '@bquery/bquery/security';
423
+ import { safeHtml } from '@bquery/bquery/component';
424
+
425
+ const safeMarkup = sanitize(userInput);
426
+ const safe = sanitize('<form id="cookie">...</form>');
427
+ const urlSafe = sanitize('<a href="java\u200Bscript:alert(1)">click</a>');
428
+ const secureLink = sanitize('<a href="https://external.com" target="_blank">Link</a>');
429
+ const safeSrcset = sanitize('<img srcset="safe.jpg 1x, javascript:alert(1) 2x">');
430
+ const safeForm = sanitize('<form action="javascript:alert(1)">...</form>');
431
+ const escaped = escapeHtml('<script>alert(1)</script>');
432
+ const icon = trusted(sanitizeHtml('<span class="icon">♥</span>'));
433
+ const button = safeHtml`<button>${icon}<span>Save</span></button>`;
434
+ ```
435
+
436
+ ### Platform – config, cookies & accessibility
437
+
438
+ ```ts
439
+ import {
440
+ defineBqueryConfig,
441
+ useCookie,
442
+ definePageMeta,
443
+ useAnnouncer,
444
+ storage,
445
+ notifications,
446
+ } from '@bquery/bquery/platform';
447
+
448
+ defineBqueryConfig({
449
+ fetch: { baseUrl: 'https://api.example.com' },
450
+ transitions: { skipOnReducedMotion: true, classes: ['page-transition'] },
451
+ components: { prefix: 'ui' },
452
+ });
453
+
454
+ const theme = useCookie<'light' | 'dark'>('theme', { defaultValue: 'light' });
455
+ const cleanupMeta = definePageMeta({ title: 'Dashboard' });
456
+ const announcer = useAnnouncer();
457
+
458
+ theme.value = 'dark';
459
+ announcer.announce('Preferences saved');
460
+ cleanupMeta();
461
+
462
+ const local = storage.local();
463
+ await local.set('theme', theme.value);
464
+
465
+ const permission = await notifications.requestPermission();
466
+ if (permission === 'granted') {
467
+ notifications.send('Build complete', {
468
+ body: 'Your docs are ready.',
469
+ });
470
+ }
471
+ ```
472
+
473
+ ### Router – SPA navigation
474
+
475
+ ```ts
476
+ import { effect } from '@bquery/bquery/reactive';
477
+ import { createRouter, navigate, currentRoute } from '@bquery/bquery/router';
478
+
479
+ const router = createRouter({
480
+ routes: [
481
+ { path: '/', name: 'home', component: HomePage },
482
+ { path: '/user/:id', name: 'user', component: UserPage },
483
+ { path: '*', component: NotFound },
484
+ ],
485
+ });
486
+
487
+ router.beforeEach(async (to) => {
488
+ if (to.path === '/admin' && !isAuthenticated()) {
489
+ await navigate('/login');
490
+ return false;
491
+ }
492
+ });
493
+
494
+ effect(() => {
495
+ console.log('Current path:', currentRoute.value.path);
496
+ });
497
+ ```
498
+
499
+ ### Forms reactive validation
500
+
501
+ ```ts
502
+ import { createForm, email, required } from '@bquery/bquery/forms';
503
+
504
+ const form = createForm({
505
+ fields: {
506
+ name: { initialValue: '', validators: [required()] },
507
+ email: { initialValue: '', validators: [required(), email()] },
508
+ },
509
+ onSubmit: async (values) => {
510
+ await fetch('/api/signup', {
511
+ method: 'POST',
512
+ body: JSON.stringify(values),
513
+ });
514
+ },
515
+ });
516
+
517
+ await form.handleSubmit();
518
+ console.log(form.isValid.value, form.fields.email.error.value);
519
+ ```
520
+
521
+ ### i18n locale-aware content
522
+
523
+ ```ts
524
+ import { createI18n } from '@bquery/bquery/i18n';
525
+
526
+ const i18n = createI18n({
527
+ locale: 'en',
528
+ fallbackLocale: 'en',
529
+ messages: {
530
+ en: { greeting: 'Hello, {name}!' },
531
+ de: { greeting: 'Hallo, {name}!' },
532
+ },
533
+ });
534
+
535
+ console.log(i18n.t('greeting', { name: 'Ada' }));
536
+ i18n.$locale.value = 'de';
537
+ ```
538
+
539
+ ### Accessibility, media, and drag & drop
540
+
541
+ ```ts
542
+ import { trapFocus, announceToScreenReader } from '@bquery/bquery/a11y';
543
+ import { mediaQuery, useViewport } from '@bquery/bquery/media';
544
+ import { draggable } from '@bquery/bquery/dnd';
545
+
546
+ const modalTrap = trapFocus(document.querySelector('#dialog')!);
547
+ announceToScreenReader('Dialog opened');
548
+
549
+ const isDark = mediaQuery('(prefers-color-scheme: dark)');
550
+ const viewport = useViewport();
551
+ const drag = draggable(document.querySelector('#card')!, { bounds: 'parent' });
552
+
553
+ console.log(isDark.value, viewport.value.width);
554
+
555
+ drag.destroy();
556
+ modalTrap.release();
557
+ ```
558
+
559
+ ### Plugins, devtools, testing, and SSR
560
+
561
+ ```ts
562
+ import { use } from '@bquery/bquery/plugin';
563
+ import { enableDevtools, getTimeline } from '@bquery/bquery/devtools';
564
+ import { renderComponent, fireEvent } from '@bquery/bquery/testing';
565
+ import { renderToString } from '@bquery/bquery/ssr';
566
+
567
+ use({
568
+ name: 'focus-plugin',
569
+ install(ctx) {
570
+ ctx.directive('focus', (el) => (el as HTMLElement).focus());
571
+ },
572
+ });
573
+
574
+ enableDevtools(true, { logToConsole: true });
575
+ console.log(getTimeline());
576
+
577
+ const mounted = renderComponent('ui-button', { props: { variant: 'primary' } });
578
+ fireEvent(mounted.el, 'click');
579
+
580
+ const { html } = renderToString('<p bq-text="label"></p>', { label: 'Hello SSR' });
581
+ console.log(html);
582
+
583
+ mounted.unmount();
584
+ ```
585
+
586
+ ### Store – state management
587
+
588
+ ```ts
589
+ import {
590
+ createStore,
591
+ createPersistedStore,
592
+ defineStore,
593
+ mapGetters,
594
+ watchStore,
595
+ } from '@bquery/bquery/store';
596
+
597
+ const counterStore = createStore({
598
+ id: 'counter',
599
+ state: () => ({ count: 0, name: 'Counter' }),
600
+ getters: {
601
+ doubled: (state) => state.count * 2,
602
+ },
603
+ actions: {
604
+ increment() {
605
+ this.count++;
606
+ },
607
+ },
608
+ });
609
+
610
+ const settingsStore = createPersistedStore({
611
+ id: 'settings',
612
+ state: () => ({ theme: 'dark', language: 'en' }),
613
+ });
614
+
615
+ const useCounter = defineStore('counter', {
616
+ state: () => ({ count: 0 }),
617
+ getters: {
618
+ doubled: (state) => state.count * 2,
619
+ },
620
+ actions: {
621
+ increment() {
622
+ this.count++;
623
+ },
624
+ },
625
+ });
626
+
627
+ const counter = useCounter();
628
+ const getters = mapGetters(counter, ['doubled']);
629
+
630
+ watchStore(
631
+ counter,
632
+ (state) => state.count,
633
+ (value) => {
634
+ console.log('Count changed:', value, getters.doubled);
635
+ }
636
+ );
637
+ ```
638
+
639
+ ### View declarative bindings
640
+
641
+ ```ts
642
+ import { mount, createTemplate } from '@bquery/bquery/view';
643
+ import { signal } from '@bquery/bquery/reactive';
644
+
645
+ const count = signal(0);
646
+ const items = signal(['Apple', 'Banana', 'Cherry']);
647
+
648
+ mount('#app', {
649
+ count,
650
+ items,
651
+ increment: () => count.value++,
652
+ });
653
+ ```
654
+
655
+ ## Browser Support
656
+
657
+ | Browser | Version | Support |
658
+ | ------- | ------- | ------- |
659
+ | Chrome | 90+ | ✅ Full |
660
+ | Firefox | 90+ | ✅ Full |
661
+ | Safari | 15+ | ✅ Full |
662
+ | Edge | 90+ | ✅ Full |
663
+
664
+ > **No IE support** by design.
665
+
666
+ ## Documentation
667
+
668
+ - **Getting Started**: [docs/guide/getting-started.md](docs/guide/getting-started.md)
669
+ - **Core API**: [docs/guide/api-core.md](docs/guide/api-core.md)
670
+ - **Agents**: [docs/guide/agents.md](docs/guide/agents.md)
671
+ - **Components**: [docs/guide/components.md](docs/guide/components.md)
672
+ - **Storybook**: [docs/guide/storybook.md](docs/guide/storybook.md)
673
+ - **Reactivity**: [docs/guide/reactive.md](docs/guide/reactive.md)
674
+ - **Motion**: [docs/guide/motion.md](docs/guide/motion.md)
675
+ - **Security**: [docs/guide/security.md](docs/guide/security.md)
676
+ - **Platform**: [docs/guide/platform.md](docs/guide/platform.md)
677
+ - **Router**: [docs/guide/router.md](docs/guide/router.md)
678
+ - **Store**: [docs/guide/store.md](docs/guide/store.md)
679
+ - **View**: [docs/guide/view.md](docs/guide/view.md)
680
+ - **Forms**: [docs/guide/forms.md](docs/guide/forms.md)
681
+ - **i18n**: [docs/guide/i18n.md](docs/guide/i18n.md)
682
+ - **Accessibility**: [docs/guide/a11y.md](docs/guide/a11y.md)
683
+ - **Drag & Drop**: [docs/guide/dnd.md](docs/guide/dnd.md)
684
+ - **Media**: [docs/guide/media.md](docs/guide/media.md)
685
+ - **Plugin System**: [docs/guide/plugin.md](docs/guide/plugin.md)
686
+ - **Devtools**: [docs/guide/devtools.md](docs/guide/devtools.md)
687
+ - **Testing Utilities**: [docs/guide/testing.md](docs/guide/testing.md)
688
+ - **SSR / Hydration**: [docs/guide/ssr.md](docs/guide/ssr.md)
689
+
690
+ ## Local Development
691
+
692
+ ```bash
693
+ # Install dependencies
694
+ bun install
695
+
696
+ # Start VitePress docs
697
+ bun run dev
698
+
699
+ # Run Storybook
700
+ bun run storybook
701
+
702
+ # Run tests
703
+ bun test
704
+
705
+ # Build library
706
+ bun run build
707
+
708
+ # Build docs
709
+ bun run build:docs
710
+
711
+ # Generate API documentation
712
+ bun run docs:api
713
+ ```
714
+
715
+ ## Project Structure
716
+
717
+ ```text
718
+ bQuery.js
719
+ ├── src/
720
+ │ ├── core/ # Selectors, DOM ops, events, utils
721
+ │ ├── reactive/ # Signals, computed, effects, async data
722
+ │ ├── component/ # Web Components helper + default library
723
+ │ ├── storybook/ # Story template helpers
724
+ │ ├── motion/ # View transitions, FLIP, springs
725
+ │ ├── security/ # Sanitizer, CSP, Trusted Types
726
+ │ ├── platform/ # Storage, cache, cookies, meta, config
727
+ │ ├── router/ # SPA routing, navigation guards
728
+ │ ├── store/ # State management, persistence
729
+ │ ├── view/ # Declarative DOM bindings
730
+ │ ├── forms/ # Reactive forms + validators
731
+ │ ├── i18n/ # Internationalization + formatting
732
+ │ ├── a11y/ # Accessibility utilities
733
+ │ ├── dnd/ # Drag & drop helpers
734
+ │ ├── media/ # Browser and device reactive signals
735
+ │ ├── plugin/ # Global plugin system
736
+ │ ├── devtools/ # Runtime inspection helpers
737
+ │ ├── testing/ # Test utilities
738
+ │ └── ssr/ # Server-side rendering + hydration
739
+ ├── docs/ # VitePress documentation
740
+ ├── .storybook/ # Storybook config
741
+ ├── stories/ # Component stories
742
+ ├── tests/ # bun:test suites
743
+ └── dist/ # Built files (ESM, UMD, IIFE)
744
+ ```
745
+
746
+ ## Contributing
747
+
748
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
749
+
750
+ ## AI Agent Support
751
+
752
+ This project provides dedicated context files for AI coding agents:
753
+
754
+ - **[AGENT.md](AGENT.md)** — Architecture, module API reference, coding conventions, common tasks
755
+ - **[llms.txt](llms.txt)** — Compact LLM-optimized project summary
756
+ - **[.github/copilot-instructions.md](.github/copilot-instructions.md)** — GitHub Copilot context
757
+
758
+ ## License
759
+
760
+ MIT – See [LICENSE.md](LICENSE.md) for details.