@bquery/bquery 1.3.0 → 1.5.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 (138) hide show
  1. package/README.md +546 -501
  2. package/dist/component/component.d.ts.map +1 -1
  3. package/dist/component/index.d.ts +2 -0
  4. package/dist/component/index.d.ts.map +1 -1
  5. package/dist/component/library.d.ts +34 -0
  6. package/dist/component/library.d.ts.map +1 -0
  7. package/dist/component/types.d.ts +10 -6
  8. package/dist/component/types.d.ts.map +1 -1
  9. package/dist/component-CY5MVoYN.js +531 -0
  10. package/dist/component-CY5MVoYN.js.map +1 -0
  11. package/dist/component.es.mjs +6 -184
  12. package/dist/config-DRmZZno3.js +40 -0
  13. package/dist/config-DRmZZno3.js.map +1 -0
  14. package/dist/core/collection.d.ts +19 -3
  15. package/dist/core/collection.d.ts.map +1 -1
  16. package/dist/core/element.d.ts +23 -4
  17. package/dist/core/element.d.ts.map +1 -1
  18. package/dist/core/index.d.ts +1 -0
  19. package/dist/core/index.d.ts.map +1 -1
  20. package/dist/core/utils/function.d.ts +21 -4
  21. package/dist/core/utils/function.d.ts.map +1 -1
  22. package/dist/core-CK2Mfpf4.js +648 -0
  23. package/dist/core-CK2Mfpf4.js.map +1 -0
  24. package/dist/core-DPdbItcq.js +112 -0
  25. package/dist/core-DPdbItcq.js.map +1 -0
  26. package/dist/core.es.mjs +45 -1218
  27. package/dist/full.d.ts +6 -6
  28. package/dist/full.d.ts.map +1 -1
  29. package/dist/full.es.mjs +98 -92
  30. package/dist/full.iife.js +173 -3
  31. package/dist/full.iife.js.map +1 -1
  32. package/dist/full.umd.js +173 -3
  33. package/dist/full.umd.js.map +1 -1
  34. package/dist/index.es.mjs +143 -139
  35. package/dist/motion/transition.d.ts +1 -1
  36. package/dist/motion/transition.d.ts.map +1 -1
  37. package/dist/motion/types.d.ts +11 -1
  38. package/dist/motion/types.d.ts.map +1 -1
  39. package/dist/motion-C5DRdPnO.js +415 -0
  40. package/dist/motion-C5DRdPnO.js.map +1 -0
  41. package/dist/motion.es.mjs +25 -361
  42. package/dist/object-qGpWr6-J.js +38 -0
  43. package/dist/object-qGpWr6-J.js.map +1 -0
  44. package/dist/platform/announcer.d.ts +59 -0
  45. package/dist/platform/announcer.d.ts.map +1 -0
  46. package/dist/platform/config.d.ts +92 -0
  47. package/dist/platform/config.d.ts.map +1 -0
  48. package/dist/platform/cookies.d.ts +45 -0
  49. package/dist/platform/cookies.d.ts.map +1 -0
  50. package/dist/platform/index.d.ts +8 -0
  51. package/dist/platform/index.d.ts.map +1 -1
  52. package/dist/platform/meta.d.ts +62 -0
  53. package/dist/platform/meta.d.ts.map +1 -0
  54. package/dist/platform/storage.d.ts.map +1 -1
  55. package/dist/platform-B7JhGBc7.js +361 -0
  56. package/dist/platform-B7JhGBc7.js.map +1 -0
  57. package/dist/platform.es.mjs +11 -243
  58. package/dist/reactive/async-data.d.ts +114 -0
  59. package/dist/reactive/async-data.d.ts.map +1 -0
  60. package/dist/reactive/core.d.ts +12 -0
  61. package/dist/reactive/core.d.ts.map +1 -1
  62. package/dist/reactive/effect.d.ts.map +1 -1
  63. package/dist/reactive/index.d.ts +2 -2
  64. package/dist/reactive/index.d.ts.map +1 -1
  65. package/dist/reactive/internals.d.ts +6 -0
  66. package/dist/reactive/internals.d.ts.map +1 -1
  67. package/dist/reactive/signal.d.ts +2 -0
  68. package/dist/reactive/signal.d.ts.map +1 -1
  69. package/dist/reactive-BDya-ia8.js +253 -0
  70. package/dist/reactive-BDya-ia8.js.map +1 -0
  71. package/dist/reactive.es.mjs +18 -34
  72. package/dist/router-CijiICxt.js +188 -0
  73. package/dist/router-CijiICxt.js.map +1 -0
  74. package/dist/router.es.mjs +11 -200
  75. package/dist/sanitize-jyJ2ryE2.js +302 -0
  76. package/dist/sanitize-jyJ2ryE2.js.map +1 -0
  77. package/dist/security/constants.d.ts.map +1 -1
  78. package/dist/security/sanitize-core.d.ts.map +1 -1
  79. package/dist/security.es.mjs +10 -56
  80. package/dist/store-CPK9E62U.js +262 -0
  81. package/dist/store-CPK9E62U.js.map +1 -0
  82. package/dist/store.es.mjs +12 -25
  83. package/dist/view/evaluate.d.ts.map +1 -1
  84. package/dist/view-Cdi0g-qo.js +396 -0
  85. package/dist/view-Cdi0g-qo.js.map +1 -0
  86. package/dist/view.es.mjs +10 -424
  87. package/package.json +136 -132
  88. package/src/component/component.ts +319 -289
  89. package/src/component/index.ts +42 -40
  90. package/src/component/library.ts +504 -0
  91. package/src/component/types.ts +91 -85
  92. package/src/core/collection.ts +44 -4
  93. package/src/core/element.ts +33 -5
  94. package/src/core/index.ts +1 -0
  95. package/src/core/utils/function.ts +56 -15
  96. package/src/full.ts +223 -187
  97. package/src/motion/transition.ts +97 -51
  98. package/src/motion/types.ts +208 -198
  99. package/src/platform/announcer.ts +208 -0
  100. package/src/platform/config.ts +163 -0
  101. package/src/platform/cookies.ts +165 -0
  102. package/src/platform/index.ts +39 -18
  103. package/src/platform/meta.ts +168 -0
  104. package/src/platform/storage.ts +8 -1
  105. package/src/reactive/async-data.ts +486 -0
  106. package/src/reactive/core.ts +21 -0
  107. package/src/reactive/effect.ts +18 -7
  108. package/src/reactive/index.ts +37 -23
  109. package/src/reactive/internals.ts +18 -1
  110. package/src/reactive/signal.ts +29 -20
  111. package/src/security/constants.ts +211 -209
  112. package/src/security/sanitize-core.ts +22 -1
  113. package/src/view/evaluate.ts +29 -13
  114. package/dist/batch-4LAvfLE7.js +0 -13
  115. package/dist/batch-4LAvfLE7.js.map +0 -1
  116. package/dist/component.es.mjs.map +0 -1
  117. package/dist/core-COenAZjD.js +0 -145
  118. package/dist/core-COenAZjD.js.map +0 -1
  119. package/dist/core.es.mjs.map +0 -1
  120. package/dist/full.es.mjs.map +0 -1
  121. package/dist/index.es.mjs.map +0 -1
  122. package/dist/motion.es.mjs.map +0 -1
  123. package/dist/persisted-Dz_ryNuC.js +0 -278
  124. package/dist/persisted-Dz_ryNuC.js.map +0 -1
  125. package/dist/platform.es.mjs.map +0 -1
  126. package/dist/reactive.es.mjs.map +0 -1
  127. package/dist/router.es.mjs.map +0 -1
  128. package/dist/sanitize-1FBEPAFH.js +0 -272
  129. package/dist/sanitize-1FBEPAFH.js.map +0 -1
  130. package/dist/security.es.mjs.map +0 -1
  131. package/dist/store.es.mjs.map +0 -1
  132. package/dist/type-guards-DRma3-Kc.js +0 -16
  133. package/dist/type-guards-DRma3-Kc.js.map +0 -1
  134. package/dist/untrack-BuEQKH7_.js +0 -6
  135. package/dist/untrack-BuEQKH7_.js.map +0 -1
  136. package/dist/view.es.mjs.map +0 -1
  137. package/dist/watch-CXyaBC_9.js +0 -58
  138. package/dist/watch-CXyaBC_9.js.map +0 -1
package/README.md CHANGED
@@ -1,501 +1,546 @@
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
- **The jQuery for the modern Web Platform.**
20
-
21
- bQuery.js is a slim, TypeScript-first library that combines jQuery's direct DOM workflow with modern features like reactivity, Web Components, and motion utilities — without a mandatory build step.
22
-
23
- ## Highlights
24
-
25
- - **Zero‑build capable**: runs directly in the browser; build tools are optional.
26
- - **Security‑focused**: DOM writes are sanitized by default; Trusted Types supported.
27
- - **Modular**: the core stays small; extra modules are opt‑in.
28
- - **TypeScript‑first**: clear types and strong IDE support.
29
- - **Tree-shakeable**: import only what you need.
30
-
31
- ## Installation
32
-
33
- ### Via npm/bun/pnpm
34
-
35
- ```bash
36
- # npm
37
- npm install @bquery/bquery
38
-
39
- # bun
40
- bun add @bquery/bquery
41
-
42
- # pnpm
43
- pnpm add @bquery/bquery
44
- ```
45
-
46
- ### Via CDN (Zero-build)
47
-
48
- #### ES Modules (recommended)
49
-
50
- ```html
51
- <script type="module">
52
- import { $, signal } from 'https://unpkg.com/@bquery/bquery@1/dist/full.es.mjs';
53
-
54
- const count = signal(0);
55
- $('#counter').text(`Count: ${count.value}`);
56
- </script>
57
- ```
58
-
59
- #### UMD (global variable)
60
-
61
- ```html
62
- <script src="https://unpkg.com/@bquery/bquery@1/dist/full.umd.js"></script>
63
- <script>
64
- const { $, signal } = bQuery;
65
- const count = signal(0);
66
- </script>
67
- ```
68
-
69
- #### IIFE (self-executing)
70
-
71
- ```html
72
- <script src="https://unpkg.com/@bquery/bquery@1/dist/full.iife.js"></script>
73
- <script>
74
- const { $, $$ } = bQuery;
75
- $$('.items').addClass('loaded');
76
- </script>
77
- ```
78
-
79
- ### Import Strategies
80
-
81
- ```ts
82
- // Full bundle (all modules)
83
- import { $, signal, component } from '@bquery/bquery';
84
-
85
- // Core only
86
- import { $, $$ } from '@bquery/bquery/core';
87
-
88
- // Core utilities (named exports, tree-shakeable)
89
- import { debounce, merge, uid, once, utils } from '@bquery/bquery/core';
90
-
91
- // À la carte (individual modules)
92
- import { signal, computed, effect, linkedSignal, persistedSignal } from '@bquery/bquery/reactive';
93
- import { component, defineComponent, html } from '@bquery/bquery/component';
94
- import { transition, spring, animate, timeline } from '@bquery/bquery/motion';
95
- import { sanitize } from '@bquery/bquery/security';
96
- import { storage, cache } from '@bquery/bquery/platform';
97
- import { createRouter, navigate } from '@bquery/bquery/router';
98
- import { createStore, defineStore } from '@bquery/bquery/store';
99
- import { mount, createTemplate } from '@bquery/bquery/view';
100
- ```
101
-
102
- ## Modules at a glance
103
-
104
- | Module | Description | Size (gzip) |
105
- | ------------- | -------------------------------------------------- | ----------- |
106
- | **Core** | Selectors, DOM manipulation, events, utilities | ~11.3 KB |
107
- | **Reactive** | `signal`, `computed`, `effect`, `batch` | ~0.3 KB |
108
- | **Component** | Lightweight Web Components with props | ~1.9 KB |
109
- | **Motion** | View transitions, FLIP, timelines, scroll, springs | ~4.0 KB |
110
- | **Security** | HTML sanitizing, Trusted Types, CSP | ~0.7 KB |
111
- | **Platform** | Storage, cache, notifications, buckets | ~2.2 KB |
112
- | **Router** | SPA routing, navigation guards, history API | ~2.2 KB |
113
- | **Store** | Signal-based state management, persistence | ~0.3 KB |
114
- | **View** | Declarative DOM bindings, directives | ~4.3 KB |
115
-
116
- ## Quick examples
117
-
118
- ### Core – DOM & events
119
-
120
- ```ts
121
- import { $, $$ } from '@bquery/bquery/core';
122
-
123
- // jQuery-style selectors
124
- $('#save').on('click', (event) => {
125
- console.log('Saved', event.type);
126
- });
127
-
128
- // Event delegation for dynamic content
129
- $('#list').delegate('click', '.item', (event, target) => {
130
- console.log('Item clicked', target.textContent);
131
- });
132
-
133
- // Method chaining
134
- $('#box').addClass('active').css({ opacity: '0.8' }).attr('data-state', 'ready');
135
-
136
- // DOM manipulation
137
- $('#content').wrap('div');
138
- $('#content').unwrap(); // Remove parent wrapper
139
-
140
- // Attribute helpers
141
- $('#dialog').toggleAttr('open');
142
-
143
- // Smooth scrolling
144
- $('#section').scrollTo({ behavior: 'smooth' });
145
-
146
- // Form serialization
147
- const formData = $('form').serialize(); // Returns object
148
- const queryString = $('form').serializeString(); // Returns URL-encoded string
149
-
150
- // Collections
151
- $$('.items').addClass('highlight');
152
- $$('.items').append('<li class="item">New</li>');
153
- ```
154
-
155
- ### Reactive signals
156
-
157
- ```ts
158
- import {
159
- signal,
160
- computed,
161
- effect,
162
- batch,
163
- watch,
164
- readonly,
165
- linkedSignal,
166
- } from '@bquery/bquery/reactive';
167
-
168
- const count = signal(0);
169
- const doubled = computed(() => count.value * 2);
170
-
171
- effect(() => {
172
- console.log('Count changed', count.value);
173
- });
174
-
175
- // Watch with cleanup support
176
- const stop = watch(count, (newVal, oldVal) => {
177
- console.log(`Changed from ${oldVal} to ${newVal}`);
178
- });
179
-
180
- // Read-only signal wrapper
181
- const readOnlyCount = readonly(count);
182
-
183
- // Batch updates for performance
184
- batch(() => {
185
- count.value++;
186
- count.value++;
187
- });
188
-
189
- // Writable computed (linked signal)
190
- const first = signal('Ada');
191
- const last = signal('Lovelace');
192
- const fullName = linkedSignal(
193
- () => `${first.value} ${last.value}`,
194
- (next) => {
195
- const [nextFirst, nextLast] = next.split(' ');
196
- first.value = nextFirst ?? '';
197
- last.value = nextLast ?? '';
198
- }
199
- );
200
-
201
- fullName.value = 'Grace Hopper';
202
- ```
203
-
204
- ### Components – Web Components
205
-
206
- ```ts
207
- import { component, defineComponent, html } from '@bquery/bquery/component';
208
-
209
- component('user-card', {
210
- props: {
211
- username: { type: String, required: true },
212
- age: { type: Number, validator: (v) => v >= 0 && v <= 150 },
213
- },
214
- // Extended lifecycle hooks
215
- beforeMount() {
216
- console.log('About to mount');
217
- },
218
- connected() {
219
- console.log('Mounted');
220
- },
221
- beforeUpdate(props) {
222
- // Return false to prevent update
223
- return props.username !== '';
224
- },
225
- onError(error) {
226
- console.error('Component error:', error);
227
- },
228
- render({ props }) {
229
- return html`<div>Hello ${props.username}</div>`;
230
- },
231
- });
232
-
233
- // Optional: create the class without auto-registration
234
- const UserCard = defineComponent('user-card', {
235
- props: { username: { type: String, required: true } },
236
- render: ({ props }) => html`<div>Hello ${props.username}</div>`,
237
- });
238
- customElements.define('user-card', UserCard);
239
- ```
240
-
241
- ### Motion animations
242
-
243
- ```ts
244
- import { animate, keyframePresets, spring, transition } from '@bquery/bquery/motion';
245
-
246
- // View transitions (with fallback)
247
- await transition(() => {
248
- $('#content').text('Updated');
249
- });
250
-
251
- // Web Animations helper
252
- await animate(card, {
253
- keyframes: keyframePresets.pop(),
254
- options: { duration: 240, easing: 'ease-out' },
255
- });
256
-
257
- // Spring physics
258
- const x = spring(0, { stiffness: 120, damping: 14 });
259
- x.onChange((value) => {
260
- element.style.transform = `translateX(${value}px)`;
261
- });
262
- await x.to(100);
263
- ```
264
-
265
- ### Security – sanitizing
266
-
267
- Internally modularized (sanitize core, Trusted Types, CSP helpers) — the public API remains unchanged. For legacy deep imports, `@bquery/bquery/security/sanitize` also re-exports `generateNonce()` and `isTrustedTypesSupported()`.
268
-
269
- ```ts
270
- import { sanitize, escapeHtml } from '@bquery/bquery/security';
271
-
272
- // Sanitize HTML (removes dangerous elements like script, iframe, svg)
273
- const safeHtml = sanitize(userInput);
274
-
275
- // DOM clobbering protection (reserved IDs are blocked)
276
- const safe = sanitize('<form id="cookie">...</form>'); // id stripped
277
-
278
- // Unicode bypass protection in URLs
279
- const urlSafe = sanitize('<a href="java\u200Bscript:alert(1)">click</a>');
280
-
281
- // Automatic link security (adds rel="noopener noreferrer" to external/target="_blank" links)
282
- const secureLink = sanitize('<a href="https://external.com" target="_blank">Link</a>');
283
-
284
- // Escape for text display
285
- const escaped = escapeHtml('<script>alert(1)</script>');
286
- ```
287
-
288
- ### Platform – storage & APIs
289
-
290
- ```ts
291
- import { storage, notifications } from '@bquery/bquery/platform';
292
-
293
- // Unified storage API
294
- const local = storage.local();
295
- await local.set('theme', 'dark');
296
- const theme = await local.get<string>('theme');
297
-
298
- // Notifications
299
- const permission = await notifications.requestPermission();
300
- if (permission === 'granted') {
301
- notifications.send('Build complete', {
302
- body: 'Your docs are ready.',
303
- });
304
- }
305
- ```
306
-
307
- ### Router – SPA navigation
308
-
309
- Internally, the router has been split into focused submodules (matching, navigation, state, links, utilities) with no public API changes.
310
-
311
- ```ts
312
- import { createRouter, navigate, currentRoute } from '@bquery/bquery/router';
313
-
314
- // Create router with routes
315
- const router = createRouter({
316
- routes: [
317
- { path: '/', name: 'home', component: HomePage },
318
- { path: '/user/:id', name: 'user', component: UserPage },
319
- { path: '*', component: NotFound },
320
- ],
321
- });
322
-
323
- // Navigation guards
324
- router.beforeEach(async (to, from) => {
325
- if (to.path === '/admin' && !isAuthenticated()) {
326
- await navigate('/login'); // Redirect
327
- return false; // Cancel original navigation
328
- }
329
- });
330
-
331
- // Navigate programmatically
332
- await navigate('/user/42');
333
- await navigate('/search?q=bquery'); // Query params in path
334
- await navigate('/login', { replace: true }); // Replace history entry
335
-
336
- // Reactive current route
337
- effect(() => {
338
- console.log('Current path:', currentRoute.value.path);
339
- });
340
- ```
341
-
342
- ### Store state management
343
-
344
- ```ts
345
- import { createStore, createPersistedStore } from '@bquery/bquery/store';
346
-
347
- // Create a store (returns the store instance directly)
348
- const counterStore = createStore({
349
- id: 'counter',
350
- state: () => ({ count: 0, name: 'Counter' }),
351
- getters: {
352
- doubled: (state) => state.count * 2,
353
- },
354
- actions: {
355
- increment() {
356
- this.count++;
357
- },
358
- async fetchCount() {
359
- this.count = await api.getCount();
360
- },
361
- },
362
- });
363
-
364
- // Use the store
365
- counterStore.increment();
366
- console.log(counterStore.doubled); // Reactive getter
367
-
368
- // Persisted store (localStorage)
369
- const settingsStore = createPersistedStore({
370
- id: 'settings',
371
- state: () => ({ theme: 'dark', language: 'en' }),
372
- });
373
-
374
- // Factory-style store definition (Pinia-style)
375
- import { defineStore, mapGetters, watchStore } from '@bquery/bquery/store';
376
-
377
- const useCounter = defineStore('counter', {
378
- state: () => ({ count: 0 }),
379
- getters: {
380
- doubled: (state) => state.count * 2,
381
- },
382
- actions: {
383
- increment() {
384
- this.count++;
385
- },
386
- },
387
- });
388
-
389
- const counter = useCounter();
390
- const getters = mapGetters(counter, ['doubled']);
391
-
392
- watchStore(
393
- counter,
394
- (state) => state.count,
395
- (value) => {
396
- console.log('Count changed:', value, getters.doubled);
397
- }
398
- );
399
- ```
400
-
401
- ### View – declarative bindings
402
-
403
- Internally modularized into focused submodules; the public API remains unchanged.
404
-
405
- ```ts
406
- import { mount, createTemplate } from '@bquery/bquery/view';
407
- import { signal } from '@bquery/bquery/reactive';
408
-
409
- // Mount reactive bindings to DOM
410
- const count = signal(0);
411
- const items = signal(['Apple', 'Banana', 'Cherry']);
412
-
413
- const app = mount('#app', {
414
- count,
415
- items,
416
- increment: () => count.value++,
417
- });
418
-
419
- // In HTML:
420
- // <p bq-text="count"></p>
421
- // <button bq-on:click="increment">+1</button>
422
- // <ul><li bq-for="item in items" bq-text="item"></li></ul>
423
- // <input bq-model="count" type="number" />
424
- // <div bq-if="count > 5">Count is high!</div>
425
- // <div bq-class="{ active: count > 0 }"></div>
426
- ```
427
-
428
- ## Browser Support
429
-
430
- | Browser | Version | Support |
431
- | ------- | ------- | ------- |
432
- | Chrome | 90+ | ✅ Full |
433
- | Firefox | 90+ | ✅ Full |
434
- | Safari | 15+ | Full |
435
- | Edge | 90+ | ✅ Full |
436
-
437
- > **No IE support** by design.
438
-
439
- ## Documentation
440
-
441
- - **Getting Started**: [docs/guide/getting-started.md](docs/guide/getting-started.md)
442
- - **Core API**: [docs/guide/api-core.md](docs/guide/api-core.md)
443
- - **Agents**: [docs/guide/agents.md](docs/guide/agents.md)
444
- - **Components**: [docs/guide/components.md](docs/guide/components.md)
445
- - **Reactivity**: [docs/guide/reactive.md](docs/guide/reactive.md)
446
- - **Motion**: [docs/guide/motion.md](docs/guide/motion.md)
447
- - **Security**: [docs/guide/security.md](docs/guide/security.md)
448
- - **Platform**: [docs/guide/platform.md](docs/guide/platform.md)
449
- - **Router**: [docs/guide/router.md](docs/guide/router.md)
450
- - **Store**: [docs/guide/store.md](docs/guide/store.md)
451
- - **View**: [docs/guide/view.md](docs/guide/view.md)
452
-
453
- ## Local Development
454
-
455
- ```bash
456
- # Install dependencies
457
- bun install
458
-
459
- # Start VitePress docs
460
- bun run dev
461
-
462
- # Run Vite playground
463
- bun run playground
464
-
465
- # Run tests
466
- bun test
467
-
468
- # Build library
469
- bun run build
470
-
471
- # Generate API documentation
472
- bun run docs:api
473
- ```
474
-
475
- ## Project Structure
476
-
477
- ```text
478
- bQuery.js
479
- ├── src/
480
- │ ├── core/ # Selectors, DOM ops, events, utils
481
- │ ├── reactive/ # Signals, computed, effects
482
- │ ├── component/ # Web Components helper
483
- │ ├── motion/ # View transitions, FLIP, springs
484
- │ ├── security/ # Sanitizer, CSP, Trusted Types
485
- │ ├── platform/ # Storage, cache, notifications
486
- │ ├── router/ # SPA routing, navigation guards
487
- │ ├── store/ # State management, persistence
488
- │ └── view/ # Declarative DOM bindings
489
- ├── docs/ # VitePress documentation
490
- ├── playground/ # Vite demo app
491
- ├── tests/ # bun:test suites
492
- └── dist/ # Built files (ESM, UMD, IIFE)
493
- ```
494
-
495
- ## Contributing
496
-
497
- See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
498
-
499
- ## License
500
-
501
- 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, Web Components, motion utilities, routing, stores, and declarative views — 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.
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
+ component,
116
+ defineComponent,
117
+ html,
118
+ registerDefaultComponents,
119
+ } from '@bquery/bquery/component';
120
+
121
+ // Motion only
122
+ import { transition, spring, animate, timeline } from '@bquery/bquery/motion';
123
+
124
+ // Security only
125
+ import { sanitize } from '@bquery/bquery/security';
126
+
127
+ // Platform only
128
+ import { storage, cache, useCookie, definePageMeta, useAnnouncer } from '@bquery/bquery/platform';
129
+
130
+ // Router, Store, View
131
+ import { createRouter, navigate } from '@bquery/bquery/router';
132
+ import { createStore, defineStore } from '@bquery/bquery/store';
133
+ import { mount, createTemplate } from '@bquery/bquery/view';
134
+ ```
135
+
136
+ ## Modules at a glance
137
+
138
+ | Module | Description | Size (gzip) |
139
+ | ------------- | -------------------------------------------------- | ----------- |
140
+ | **Core** | Selectors, DOM manipulation, events, utilities | ~11.3 KB |
141
+ | **Reactive** | `signal`, `computed`, `effect`, async data/fetch | ~0.3 KB |
142
+ | **Component** | Lightweight Web Components with props + defaults | ~1.9 KB |
143
+ | **Motion** | View transitions, FLIP, timelines, scroll, springs | ~4.0 KB |
144
+ | **Security** | HTML sanitizing, Trusted Types, CSP | ~0.7 KB |
145
+ | **Platform** | Storage, cache, cookies, meta, announcers, config | ~2.2 KB |
146
+ | **Router** | SPA routing, navigation guards, history API | ~2.2 KB |
147
+ | **Store** | Signal-based state management, persistence | ~0.3 KB |
148
+ | **View** | Declarative DOM bindings, directives | ~4.3 KB |
149
+
150
+ ## Quick examples
151
+
152
+ ### Core – DOM & events
153
+
154
+ ```ts
155
+ import { $, $$ } from '@bquery/bquery/core';
156
+
157
+ $('#save').on('click', (event) => {
158
+ console.log('Saved', event.type);
159
+ });
160
+
161
+ $('#list').delegate('click', '.item', (event, target) => {
162
+ console.log('Item clicked', target.textContent);
163
+ });
164
+
165
+ $('#box').addClass('active').css({ opacity: '0.8' }).attr('data-state', 'ready');
166
+
167
+ const color = $('#box').css('color');
168
+
169
+ if ($('#el').is('.active')) {
170
+ console.log('Element is active');
171
+ }
172
+
173
+ $$('.container').find('.item').addClass('found');
174
+ ```
175
+
176
+ ### Reactive signals
177
+
178
+ ```ts
179
+ import {
180
+ signal,
181
+ computed,
182
+ effect,
183
+ batch,
184
+ watch,
185
+ readonly,
186
+ linkedSignal,
187
+ } from '@bquery/bquery/reactive';
188
+
189
+ const count = signal(0);
190
+ const doubled = computed(() => count.value * 2);
191
+
192
+ effect(() => {
193
+ console.log('Count changed', count.value);
194
+ });
195
+
196
+ watch(count, (newVal, oldVal) => {
197
+ console.log(`Changed from ${oldVal} to ${newVal}`);
198
+ });
199
+
200
+ const readOnlyCount = readonly(count);
201
+
202
+ batch(() => {
203
+ count.value++;
204
+ count.value++;
205
+ });
206
+
207
+ count.dispose();
208
+
209
+ const first = signal('Ada');
210
+ const last = signal('Lovelace');
211
+ const fullName = linkedSignal(
212
+ () => `${first.value} ${last.value}`,
213
+ (next) => {
214
+ const [nextFirst, nextLast] = next.split(' ');
215
+ first.value = nextFirst ?? '';
216
+ last.value = nextLast ?? '';
217
+ }
218
+ );
219
+
220
+ fullName.value = 'Grace Hopper';
221
+ ```
222
+
223
+ ### Reactive async data & fetch
224
+
225
+ ```ts
226
+ import { signal, useFetch, createUseFetch } from '@bquery/bquery/reactive';
227
+
228
+ const userId = signal(1);
229
+
230
+ const user = useFetch<{ id: number; name: string }>(() => `/users/${userId.value}`, {
231
+ baseUrl: 'https://api.example.com',
232
+ watch: [userId],
233
+ query: { include: 'profile' },
234
+ });
235
+
236
+ const useApiFetch = createUseFetch({
237
+ baseUrl: 'https://api.example.com',
238
+ headers: { 'x-client': 'bquery-readme' },
239
+ });
240
+
241
+ const settings = useApiFetch<{ theme: string }>('/settings');
242
+
243
+ console.log(user.pending.value, user.data.value, settings.error.value);
244
+ ```
245
+
246
+ ### Components Web Components
247
+
248
+ ```ts
249
+ import {
250
+ component,
251
+ defineComponent,
252
+ html,
253
+ registerDefaultComponents,
254
+ } from '@bquery/bquery/component';
255
+
256
+ component('user-card', {
257
+ props: {
258
+ username: { type: String, required: true },
259
+ age: { type: Number, validator: (v) => v >= 0 && v <= 150 },
260
+ },
261
+ beforeMount() {
262
+ console.log('About to mount');
263
+ },
264
+ connected() {
265
+ console.log('Mounted');
266
+ },
267
+ beforeUpdate(props) {
268
+ return props.username !== '';
269
+ },
270
+ onError(error) {
271
+ console.error('Component error:', error);
272
+ },
273
+ render({ props }) {
274
+ return html`<div>Hello ${props.username}</div>`;
275
+ },
276
+ });
277
+
278
+ const UserCard = defineComponent('user-card', {
279
+ props: { username: { type: String, required: true } },
280
+ render: ({ props }) => html`<div>Hello ${props.username}</div>`,
281
+ });
282
+
283
+ customElements.define('user-card', UserCard);
284
+
285
+ const tags = registerDefaultComponents({ prefix: 'ui' });
286
+ console.log(tags.button); // ui-button
287
+ ```
288
+
289
+ ### Motion – animations
290
+
291
+ ```ts
292
+ import { animate, keyframePresets, spring, transition } from '@bquery/bquery/motion';
293
+
294
+ await transition({
295
+ update: () => {
296
+ $('#content').text('Updated');
297
+ },
298
+ classes: ['page-transition'],
299
+ types: ['navigation'],
300
+ skipOnReducedMotion: true,
301
+ });
302
+
303
+ await animate(card, {
304
+ keyframes: keyframePresets.pop(),
305
+ options: { duration: 240, easing: 'ease-out' },
306
+ });
307
+
308
+ const x = spring(0, { stiffness: 120, damping: 14 });
309
+ x.onChange((value) => {
310
+ element.style.transform = `translateX(${value}px)`;
311
+ });
312
+ await x.to(100);
313
+ ```
314
+
315
+ ### Security sanitizing
316
+
317
+ ```ts
318
+ import { sanitize, escapeHtml } from '@bquery/bquery/security';
319
+
320
+ const safeHtml = sanitize(userInput);
321
+ const safe = sanitize('<form id="cookie">...</form>');
322
+ const urlSafe = sanitize('<a href="java\u200Bscript:alert(1)">click</a>');
323
+ const secureLink = sanitize('<a href="https://external.com" target="_blank">Link</a>');
324
+ const safeSrcset = sanitize('<img srcset="safe.jpg 1x, javascript:alert(1) 2x">');
325
+ const safeForm = sanitize('<form action="javascript:alert(1)">...</form>');
326
+ const escaped = escapeHtml('<script>alert(1)</script>');
327
+ ```
328
+
329
+ ### Platform – config, cookies & accessibility
330
+
331
+ ```ts
332
+ import {
333
+ defineBqueryConfig,
334
+ useCookie,
335
+ definePageMeta,
336
+ useAnnouncer,
337
+ storage,
338
+ notifications,
339
+ } from '@bquery/bquery/platform';
340
+
341
+ defineBqueryConfig({
342
+ fetch: { baseUrl: 'https://api.example.com' },
343
+ transitions: { skipOnReducedMotion: true, classes: ['page-transition'] },
344
+ components: { prefix: 'ui' },
345
+ });
346
+
347
+ const theme = useCookie<'light' | 'dark'>('theme', { defaultValue: 'light' });
348
+ const cleanupMeta = definePageMeta({ title: 'Dashboard' });
349
+ const announcer = useAnnouncer();
350
+
351
+ theme.value = 'dark';
352
+ announcer.announce('Preferences saved');
353
+ cleanupMeta();
354
+
355
+ const local = storage.local();
356
+ await local.set('theme', theme.value);
357
+
358
+ const permission = await notifications.requestPermission();
359
+ if (permission === 'granted') {
360
+ notifications.send('Build complete', {
361
+ body: 'Your docs are ready.',
362
+ });
363
+ }
364
+ ```
365
+
366
+ ### Router SPA navigation
367
+
368
+ ```ts
369
+ import { effect } from '@bquery/bquery/reactive';
370
+ import { createRouter, navigate, currentRoute } from '@bquery/bquery/router';
371
+
372
+ const router = createRouter({
373
+ routes: [
374
+ { path: '/', name: 'home', component: HomePage },
375
+ { path: '/user/:id', name: 'user', component: UserPage },
376
+ { path: '*', component: NotFound },
377
+ ],
378
+ });
379
+
380
+ router.beforeEach(async (to) => {
381
+ if (to.path === '/admin' && !isAuthenticated()) {
382
+ await navigate('/login');
383
+ return false;
384
+ }
385
+ });
386
+
387
+ effect(() => {
388
+ console.log('Current path:', currentRoute.value.path);
389
+ });
390
+ ```
391
+
392
+ ### Store – state management
393
+
394
+ ```ts
395
+ import {
396
+ createStore,
397
+ createPersistedStore,
398
+ defineStore,
399
+ mapGetters,
400
+ watchStore,
401
+ } from '@bquery/bquery/store';
402
+
403
+ const counterStore = createStore({
404
+ id: 'counter',
405
+ state: () => ({ count: 0, name: 'Counter' }),
406
+ getters: {
407
+ doubled: (state) => state.count * 2,
408
+ },
409
+ actions: {
410
+ increment() {
411
+ this.count++;
412
+ },
413
+ },
414
+ });
415
+
416
+ const settingsStore = createPersistedStore({
417
+ id: 'settings',
418
+ state: () => ({ theme: 'dark', language: 'en' }),
419
+ });
420
+
421
+ const useCounter = defineStore('counter', {
422
+ state: () => ({ count: 0 }),
423
+ getters: {
424
+ doubled: (state) => state.count * 2,
425
+ },
426
+ actions: {
427
+ increment() {
428
+ this.count++;
429
+ },
430
+ },
431
+ });
432
+
433
+ const counter = useCounter();
434
+ const getters = mapGetters(counter, ['doubled']);
435
+
436
+ watchStore(
437
+ counter,
438
+ (state) => state.count,
439
+ (value) => {
440
+ console.log('Count changed:', value, getters.doubled);
441
+ }
442
+ );
443
+ ```
444
+
445
+ ### View – declarative bindings
446
+
447
+ ```ts
448
+ import { mount, createTemplate } from '@bquery/bquery/view';
449
+ import { signal } from '@bquery/bquery/reactive';
450
+
451
+ const count = signal(0);
452
+ const items = signal(['Apple', 'Banana', 'Cherry']);
453
+
454
+ mount('#app', {
455
+ count,
456
+ items,
457
+ increment: () => count.value++,
458
+ });
459
+ ```
460
+
461
+ ## Browser Support
462
+
463
+ | Browser | Version | Support |
464
+ | ------- | ------- | ------- |
465
+ | Chrome | 90+ | ✅ Full |
466
+ | Firefox | 90+ | ✅ Full |
467
+ | Safari | 15+ | ✅ Full |
468
+ | Edge | 90+ | ✅ Full |
469
+
470
+ > **No IE support** by design.
471
+
472
+ ## Documentation
473
+
474
+ - **Getting Started**: [docs/guide/getting-started.md](docs/guide/getting-started.md)
475
+ - **Core API**: [docs/guide/api-core.md](docs/guide/api-core.md)
476
+ - **Agents**: [docs/guide/agents.md](docs/guide/agents.md)
477
+ - **Components**: [docs/guide/components.md](docs/guide/components.md)
478
+ - **Reactivity**: [docs/guide/reactive.md](docs/guide/reactive.md)
479
+ - **Motion**: [docs/guide/motion.md](docs/guide/motion.md)
480
+ - **Security**: [docs/guide/security.md](docs/guide/security.md)
481
+ - **Platform**: [docs/guide/platform.md](docs/guide/platform.md)
482
+ - **Router**: [docs/guide/router.md](docs/guide/router.md)
483
+ - **Store**: [docs/guide/store.md](docs/guide/store.md)
484
+ - **View**: [docs/guide/view.md](docs/guide/view.md)
485
+
486
+ ## Local Development
487
+
488
+ ```bash
489
+ # Install dependencies
490
+ bun install
491
+
492
+ # Start VitePress docs
493
+ bun run dev
494
+
495
+ # Run Storybook
496
+ bun run storybook
497
+
498
+ # Run tests
499
+ bun test
500
+
501
+ # Build library
502
+ bun run build
503
+
504
+ # Build docs
505
+ bun run build:docs
506
+
507
+ # Generate API documentation
508
+ bun run docs:api
509
+ ```
510
+
511
+ ## Project Structure
512
+
513
+ ```text
514
+ bQuery.js
515
+ ├── src/
516
+ │ ├── core/ # Selectors, DOM ops, events, utils
517
+ │ ├── reactive/ # Signals, computed, effects, async data
518
+ │ ├── component/ # Web Components helper + default library
519
+ │ ├── motion/ # View transitions, FLIP, springs
520
+ │ ├── security/ # Sanitizer, CSP, Trusted Types
521
+ │ ├── platform/ # Storage, cache, cookies, meta, config
522
+ │ ├── router/ # SPA routing, navigation guards
523
+ │ ├── store/ # State management, persistence
524
+ │ └── view/ # Declarative DOM bindings
525
+ ├── docs/ # VitePress documentation
526
+ ├── .storybook/ # Storybook config
527
+ ├── stories/ # Component stories
528
+ ├── tests/ # bun:test suites
529
+ └── dist/ # Built files (ESM, UMD, IIFE)
530
+ ```
531
+
532
+ ## Contributing
533
+
534
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
535
+
536
+ ## AI Agent Support
537
+
538
+ This project provides dedicated context files for AI coding agents:
539
+
540
+ - **[AGENT.md](AGENT.md)** — Architecture, module API reference, coding conventions, common tasks
541
+ - **[llms.txt](llms.txt)** — Compact LLM-optimized project summary
542
+ - **[.github/copilot-instructions.md](.github/copilot-instructions.md)** — GitHub Copilot context
543
+
544
+ ## License
545
+
546
+ MIT – See [LICENSE.md](LICENSE.md) for details.