@bquery/bquery 1.10.0 → 1.11.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 (155) hide show
  1. package/README.md +91 -65
  2. package/dist/{a11y-DG2i4iZN.js → a11y-DgUQ8-fI.js} +1 -1
  3. package/dist/{a11y-DG2i4iZN.js.map → a11y-DgUQ8-fI.js.map} +1 -1
  4. package/dist/a11y.es.mjs +1 -1
  5. package/dist/{component-DRotf1hl.js → component-D8ydhe58.js} +2 -2
  6. package/dist/{component-DRotf1hl.js.map → component-D8ydhe58.js.map} +1 -1
  7. package/dist/component.es.mjs +1 -1
  8. package/dist/concurrency-BU1wPEsZ.js.map +1 -1
  9. package/dist/{constraints-CqjhmpZC.js → constraints-Dlbx_m1b.js} +1 -1
  10. package/dist/{constraints-CqjhmpZC.js.map → constraints-Dlbx_m1b.js.map} +1 -1
  11. package/dist/{core-EMYSLzaT.js → core-tOP6QOrY.js} +2 -2
  12. package/dist/{core-EMYSLzaT.js.map → core-tOP6QOrY.js.map} +1 -1
  13. package/dist/core.es.mjs +1 -1
  14. package/dist/{custom-directives-BjFzFhuf.js → custom-directives-5DlKqvd2.js} +1 -1
  15. package/dist/{custom-directives-BjFzFhuf.js.map → custom-directives-5DlKqvd2.js.map} +1 -1
  16. package/dist/{devtools-C5FExMwv.js → devtools-QosAqo0T.js} +2 -2
  17. package/dist/{devtools-C5FExMwv.js.map → devtools-QosAqo0T.js.map} +1 -1
  18. package/dist/devtools.es.mjs +1 -1
  19. package/dist/{dnd-BAqzPlSo.js → dnd-d2OU4len.js} +1 -1
  20. package/dist/{dnd-BAqzPlSo.js.map → dnd-d2OU4len.js.map} +1 -1
  21. package/dist/dnd.es.mjs +1 -1
  22. package/dist/{forms-Dx1Scvh0.js → forms-BLx4ZzT7.js} +1 -1
  23. package/dist/{forms-Dx1Scvh0.js.map → forms-BLx4ZzT7.js.map} +1 -1
  24. package/dist/forms.es.mjs +1 -1
  25. package/dist/full.d.ts +4 -2
  26. package/dist/full.d.ts.map +1 -1
  27. package/dist/full.es.mjs +258 -219
  28. package/dist/full.iife.js +41 -37
  29. package/dist/full.iife.js.map +1 -1
  30. package/dist/full.umd.js +41 -37
  31. package/dist/full.umd.js.map +1 -1
  32. package/dist/{i18n-Cazyk9RD.js → i18n--p7PM-9r.js} +1 -1
  33. package/dist/{i18n-Cazyk9RD.js.map → i18n--p7PM-9r.js.map} +1 -1
  34. package/dist/i18n.es.mjs +1 -1
  35. package/dist/index.d.ts +3 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.es.mjs +291 -252
  38. package/dist/match-CrZRVC4z.js +174 -0
  39. package/dist/match-CrZRVC4z.js.map +1 -0
  40. package/dist/{media-dAKIGPk3.js → media-gjbWNq50.js} +1 -1
  41. package/dist/{media-dAKIGPk3.js.map → media-gjbWNq50.js.map} +1 -1
  42. package/dist/media.es.mjs +1 -1
  43. package/dist/motion-BBMso9Ir.js.map +1 -1
  44. package/dist/{mount-C8O2vXkQ.js → mount-0A9qtcRJ.js} +3 -3
  45. package/dist/{mount-C8O2vXkQ.js.map → mount-0A9qtcRJ.js.map} +1 -1
  46. package/dist/platform-BPHIXbw8.js.map +1 -1
  47. package/dist/{plugin-DjTqWg-P.js → plugin-SZEirbwq.js} +2 -2
  48. package/dist/{plugin-DjTqWg-P.js.map → plugin-SZEirbwq.js.map} +1 -1
  49. package/dist/plugin.es.mjs +1 -1
  50. package/dist/reactive-BAd2hfl8.js.map +1 -1
  51. package/dist/{registry-Cr6VH8CR.js → registry-jpUQHf4E.js} +1 -1
  52. package/dist/{registry-Cr6VH8CR.js.map → registry-jpUQHf4E.js.map} +1 -1
  53. package/dist/router-C4weu0QL.js +333 -0
  54. package/dist/router-C4weu0QL.js.map +1 -0
  55. package/dist/router.es.mjs +1 -1
  56. package/dist/{sanitize-B1V4JswB.js → sanitize-DOMkRO9G.js} +12 -7
  57. package/dist/{sanitize-B1V4JswB.js.map → sanitize-DOMkRO9G.js.map} +1 -1
  58. package/dist/security.es.mjs +1 -1
  59. package/dist/server/create-server.d.ts +25 -0
  60. package/dist/server/create-server.d.ts.map +1 -0
  61. package/dist/server/index.d.ts +11 -0
  62. package/dist/server/index.d.ts.map +1 -0
  63. package/dist/server/types.d.ts +396 -0
  64. package/dist/server/types.d.ts.map +1 -0
  65. package/dist/server-QdyKtCS1.js +349 -0
  66. package/dist/server-QdyKtCS1.js.map +1 -0
  67. package/dist/server.es.mjs +6 -0
  68. package/dist/ssr/adapters.d.ts +74 -0
  69. package/dist/ssr/adapters.d.ts.map +1 -0
  70. package/dist/ssr/async.d.ts +40 -0
  71. package/dist/ssr/async.d.ts.map +1 -0
  72. package/dist/ssr/config.d.ts +60 -0
  73. package/dist/ssr/config.d.ts.map +1 -0
  74. package/dist/ssr/context.d.ts +73 -0
  75. package/dist/ssr/context.d.ts.map +1 -0
  76. package/dist/ssr/defer-brand.d.ts +5 -0
  77. package/dist/ssr/defer-brand.d.ts.map +1 -0
  78. package/dist/ssr/escape.d.ts +17 -0
  79. package/dist/ssr/escape.d.ts.map +1 -0
  80. package/dist/ssr/expression.d.ts +44 -0
  81. package/dist/ssr/expression.d.ts.map +1 -0
  82. package/dist/ssr/hash.d.ts +39 -0
  83. package/dist/ssr/hash.d.ts.map +1 -0
  84. package/dist/ssr/head.d.ts +102 -0
  85. package/dist/ssr/head.d.ts.map +1 -0
  86. package/dist/ssr/html-parser.d.ts +58 -0
  87. package/dist/ssr/html-parser.d.ts.map +1 -0
  88. package/dist/ssr/index.d.ts +49 -43
  89. package/dist/ssr/index.d.ts.map +1 -1
  90. package/dist/ssr/mismatch.d.ts +60 -0
  91. package/dist/ssr/mismatch.d.ts.map +1 -0
  92. package/dist/ssr/render-async.d.ts +84 -0
  93. package/dist/ssr/render-async.d.ts.map +1 -0
  94. package/dist/ssr/render.d.ts.map +1 -1
  95. package/dist/ssr/renderer.d.ts +25 -0
  96. package/dist/ssr/renderer.d.ts.map +1 -0
  97. package/dist/ssr/resumability.d.ts +65 -0
  98. package/dist/ssr/resumability.d.ts.map +1 -0
  99. package/dist/ssr/router-bridge.d.ts +101 -0
  100. package/dist/ssr/router-bridge.d.ts.map +1 -0
  101. package/dist/ssr/runtime.d.ts +63 -0
  102. package/dist/ssr/runtime.d.ts.map +1 -0
  103. package/dist/ssr/serialize.d.ts.map +1 -1
  104. package/dist/ssr/store-snapshot.d.ts +87 -0
  105. package/dist/ssr/store-snapshot.d.ts.map +1 -0
  106. package/dist/ssr/strategies.d.ts +43 -0
  107. package/dist/ssr/strategies.d.ts.map +1 -0
  108. package/dist/ssr/suspense.d.ts +47 -0
  109. package/dist/ssr/suspense.d.ts.map +1 -0
  110. package/dist/ssr/types.d.ts +17 -0
  111. package/dist/ssr/types.d.ts.map +1 -1
  112. package/dist/ssr-Bt6BQA3J.js +2127 -0
  113. package/dist/ssr-Bt6BQA3J.js.map +1 -0
  114. package/dist/ssr.es.mjs +42 -7
  115. package/dist/{store-CjmEeX9-.js → store-DnXuu6Li.js} +2 -2
  116. package/dist/{store-CjmEeX9-.js.map → store-DnXuu6Li.js.map} +1 -1
  117. package/dist/store.es.mjs +2 -2
  118. package/dist/storybook.es.mjs +1 -1
  119. package/dist/{testing-TdfaL7VE.js → testing-CeMUwrRD.js} +2 -2
  120. package/dist/{testing-TdfaL7VE.js.map → testing-CeMUwrRD.js.map} +1 -1
  121. package/dist/testing.es.mjs +1 -1
  122. package/dist/view.es.mjs +1 -1
  123. package/package.json +19 -14
  124. package/src/full.ts +99 -0
  125. package/src/index.ts +5 -2
  126. package/src/server/create-server.ts +754 -0
  127. package/src/server/index.ts +33 -0
  128. package/src/server/types.ts +490 -0
  129. package/src/ssr/adapters.ts +330 -0
  130. package/src/ssr/async.ts +125 -0
  131. package/src/ssr/config.ts +86 -0
  132. package/src/ssr/context.ts +245 -0
  133. package/src/ssr/defer-brand.ts +3 -0
  134. package/src/ssr/escape.ts +25 -0
  135. package/src/ssr/expression.ts +669 -0
  136. package/src/ssr/hash.ts +71 -0
  137. package/src/ssr/head.ts +240 -0
  138. package/src/ssr/html-parser.ts +387 -0
  139. package/src/ssr/index.ts +136 -43
  140. package/src/ssr/mismatch.ts +110 -0
  141. package/src/ssr/render-async.ts +286 -0
  142. package/src/ssr/render.ts +130 -59
  143. package/src/ssr/renderer.ts +453 -0
  144. package/src/ssr/resumability.ts +142 -0
  145. package/src/ssr/router-bridge.ts +177 -0
  146. package/src/ssr/runtime.ts +131 -0
  147. package/src/ssr/serialize.ts +1 -27
  148. package/src/ssr/store-snapshot.ts +209 -0
  149. package/src/ssr/strategies.ts +245 -0
  150. package/src/ssr/suspense.ts +504 -0
  151. package/src/ssr/types.ts +18 -0
  152. package/dist/router-CCepRMpC.js +0 -493
  153. package/dist/router-CCepRMpC.js.map +0 -1
  154. package/dist/ssr-D-1IPcfw.js +0 -248
  155. package/dist/ssr-D-1IPcfw.js.map +0 -1
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Server-side rendering context.
3
+ *
4
+ * Encapsulates everything a render path may need to know about the incoming
5
+ * request and to produce response-side metadata (head tags, asset hints,
6
+ * nonces, etc.). The context is propagated explicitly through the public
7
+ * async APIs (`renderToStringAsync`, `renderToStream`, `renderToResponse`)
8
+ * and is available to template loaders.
9
+ *
10
+ * @module bquery/ssr
11
+ */
12
+
13
+ import { generateNonce } from '../security/csp';
14
+ import { isPrototypePollutionKey } from '../core/utils/object';
15
+ import { createAssetManager, createHeadManager, type AssetManager, type HeadManager } from './head';
16
+
17
+ /** Options for `createSSRContext()`. */
18
+ export interface CreateSSRContextOptions {
19
+ /** Pre-built request to use as the source of URL/headers/cookies/etc. */
20
+ request?: Request;
21
+ /** Override the URL (defaults to `request.url` or `'http://localhost/'`). */
22
+ url?: string | URL;
23
+ /** Override the user agent string. */
24
+ userAgent?: string;
25
+ /** Override the request locale; defaults to `Accept-Language` parsing. */
26
+ locale?: string;
27
+ /** Provide an `AbortSignal` for cancellation. Default: `request.signal`. */
28
+ signal?: AbortSignal;
29
+ /** Pre-computed CSP nonce. If omitted and `crypto.getRandomValues` exists, a fresh nonce is generated. */
30
+ nonce?: string;
31
+ /** Render mode hint — used by streaming/string renderers for diagnostics. */
32
+ mode?: 'string' | 'stream';
33
+ /** Optional error sink invoked for non-fatal render errors. */
34
+ onError?: (error: unknown) => void;
35
+ }
36
+
37
+ /** Public SSR context shape. */
38
+ export interface SSRContext {
39
+ /** The originating `Request` (may be a synthetic one if none was provided). */
40
+ request: Request;
41
+ /** Parsed URL of the request. */
42
+ url: URL;
43
+ /** Request headers (via `Request.headers`). */
44
+ headers: Headers;
45
+ /** Resolved cookie map from `Cookie` header. */
46
+ cookies: Record<string, string>;
47
+ /** Best-effort user agent string. */
48
+ userAgent: string;
49
+ /** Best-effort locale parsed from `Accept-Language`. */
50
+ locale: string;
51
+ /** Cancellation signal — render paths must respect it. */
52
+ signal: AbortSignal;
53
+ /** CSP nonce applied to all generated `<script>` tags. */
54
+ nonce: string;
55
+ /** Render mode hint. */
56
+ mode: 'string' | 'stream';
57
+ /** Head manager — call `head.add(...)`, `head.render()`, or read `head.state()`. */
58
+ head: HeadManager;
59
+ /** Asset manifest — call `assets.preload()`/`module()`/`style()`. */
60
+ assets: AssetManager;
61
+ /** Status code suggested by render paths (loaders, error boundaries). */
62
+ status: number;
63
+ /** Outgoing response headers (used by `renderToResponse()`). */
64
+ responseHeaders: Headers;
65
+ /** Reports a non-fatal error. */
66
+ reportError(error: unknown): void;
67
+ }
68
+
69
+ const parseCookies = (header: string): Record<string, string> => {
70
+ const out = Object.create(null) as Record<string, string>;
71
+ if (!header) return out;
72
+ for (const pair of header.split(/;\s*/)) {
73
+ const idx = pair.indexOf('=');
74
+ if (idx === -1) continue;
75
+ const name = pair.slice(0, idx).trim();
76
+ const value = pair.slice(idx + 1).trim();
77
+ if (!name || isPrototypePollutionKey(name)) continue;
78
+ try {
79
+ out[name] = decodeURIComponent(value);
80
+ } catch {
81
+ out[name] = value;
82
+ }
83
+ }
84
+ return out;
85
+ };
86
+
87
+ const parseLocale = (acceptLanguage: string | null): string => {
88
+ if (!acceptLanguage) return 'en';
89
+ // Pick the highest-quality entry. Header parsing is intentionally minimal.
90
+ const entries = acceptLanguage
91
+ .split(',')
92
+ .map((entry) => {
93
+ const [tag, ...params] = entry.trim().split(';');
94
+ const qParam = params.find((p) => p.trim().startsWith('q='));
95
+ const q = qParam ? Number.parseFloat(qParam.split('=')[1]) : 1;
96
+ return { tag: tag.trim(), q: Number.isFinite(q) ? q : 0 };
97
+ })
98
+ .filter((e) => e.tag);
99
+ if (!entries.length) return 'en';
100
+ entries.sort((a, b) => b.q - a.q);
101
+ return entries[0].tag;
102
+ };
103
+
104
+ const safeNonce = (): string => {
105
+ try {
106
+ return generateNonce();
107
+ } catch {
108
+ // Runtime lacks `crypto.getRandomValues` / `btoa` — fall back to empty.
109
+ return '';
110
+ }
111
+ };
112
+
113
+ const createHeadersFallback = (): Headers => {
114
+ const store = new Map<string, string[]>();
115
+ const api = {
116
+ append(name: string, value: string): void {
117
+ const key = String(name).toLowerCase();
118
+ const existing = store.get(key);
119
+ if (existing) existing.push(String(value));
120
+ else store.set(key, [String(value)]);
121
+ },
122
+ delete(name: string): void {
123
+ store.delete(String(name).toLowerCase());
124
+ },
125
+ get(name: string): string | null {
126
+ const values = store.get(String(name).toLowerCase());
127
+ return values ? values.join(', ') : null;
128
+ },
129
+ has(name: string): boolean {
130
+ return store.has(String(name).toLowerCase());
131
+ },
132
+ set(name: string, value: string): void {
133
+ store.set(String(name).toLowerCase(), [String(value)]);
134
+ },
135
+ forEach(
136
+ callback: (value: string, key: string, parent: Headers) => void,
137
+ thisArg?: unknown
138
+ ): void {
139
+ for (const [key, values] of store) {
140
+ callback.call(thisArg, values.join(', '), key, api as unknown as Headers);
141
+ }
142
+ },
143
+ *entries(): IterableIterator<[string, string]> {
144
+ for (const [key, values] of store) {
145
+ yield [key, values.join(', ')];
146
+ }
147
+ },
148
+ [Symbol.iterator](): IterableIterator<[string, string]> {
149
+ return api.entries();
150
+ },
151
+ };
152
+ return api as unknown as Headers;
153
+ };
154
+
155
+ const createAbortSignalFallback = (): AbortSignal =>
156
+ ({
157
+ aborted: false,
158
+ onabort: null,
159
+ reason: undefined,
160
+ addEventListener() {
161
+ /* no-op */
162
+ },
163
+ removeEventListener() {
164
+ /* no-op */
165
+ },
166
+ dispatchEvent() {
167
+ return false;
168
+ },
169
+ throwIfAborted() {
170
+ /* no-op */
171
+ },
172
+ }) as unknown as AbortSignal;
173
+
174
+ const hasHeadersApi = (value: unknown): value is Headers =>
175
+ typeof value === 'object' && value !== null && typeof (value as Headers).get === 'function';
176
+
177
+ const hasAbortSignalApi = (value: unknown): value is AbortSignal =>
178
+ typeof value === 'object' && value !== null && typeof (value as AbortSignal).aborted === 'boolean';
179
+
180
+ const createHeadersLike = (): Headers =>
181
+ typeof Headers === 'function' ? new Headers() : createHeadersFallback();
182
+
183
+ /**
184
+ * Creates a fully populated SSR context.
185
+ *
186
+ * @example
187
+ * ```ts
188
+ * const ctx = createSSRContext({ request });
189
+ * const { html } = await renderToStringAsync(template, data, { context: ctx });
190
+ * ```
191
+ */
192
+ export const createSSRContext = (options: CreateSSRContextOptions = {}): SSRContext => {
193
+ // SSRContext relies on Web `Request`/`Headers`/`AbortSignal`. All target
194
+ // runtimes (Node ≥ 24, Deno, Bun, browsers) provide these natively. The
195
+ // structural fallback below only exists so the helper does not throw in
196
+ // exotic embedded runtimes; downstream code that expects real `Request`
197
+ // methods should ensure the runtime ships them.
198
+ const fallbackRequestUrl =
199
+ options.url instanceof URL
200
+ ? options.url.toString()
201
+ : typeof options.url === 'string'
202
+ ? new URL(options.url, 'http://localhost/').toString()
203
+ : 'http://localhost/';
204
+ const request =
205
+ options.request ??
206
+ (typeof Request === 'function'
207
+ ? new Request(fallbackRequestUrl)
208
+ : ({
209
+ url: fallbackRequestUrl,
210
+ headers: createHeadersFallback(),
211
+ signal: createAbortSignalFallback(),
212
+ } as unknown as Request));
213
+
214
+ const urlSource = options.url ?? request.url;
215
+ const url =
216
+ urlSource instanceof URL ? urlSource : new URL(String(urlSource), 'http://localhost/');
217
+
218
+ const headers = hasHeadersApi(request.headers) ? request.headers : createHeadersLike();
219
+ const cookies = parseCookies(headers.get('cookie') ?? '');
220
+ const userAgent = options.userAgent ?? headers.get('user-agent') ?? '';
221
+ const locale = options.locale ?? parseLocale(headers.get('accept-language'));
222
+ const signal = options.signal ?? (hasAbortSignalApi(request.signal) ? request.signal : createAbortSignalFallback());
223
+ const nonce = options.nonce ?? safeNonce();
224
+
225
+ const ctx: SSRContext = {
226
+ request,
227
+ url,
228
+ headers,
229
+ cookies,
230
+ userAgent,
231
+ locale,
232
+ signal,
233
+ nonce,
234
+ mode: options.mode ?? 'string',
235
+ head: createHeadManager(),
236
+ assets: createAssetManager(),
237
+ status: 200,
238
+ responseHeaders: createHeadersLike(),
239
+ reportError(error) {
240
+ options.onError?.(error);
241
+ },
242
+ };
243
+
244
+ return ctx;
245
+ };
@@ -0,0 +1,3 @@
1
+ /** Shared internal brand for SSR deferred values and tagged loaders. */
2
+ declare const _deferBrandSymbol: unique symbol;
3
+ export const DEFER_BRAND = Symbol.for('bquery.ssr.defer') as typeof _deferBrandSymbol;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Shared escaping helpers for SSR inline script payloads and HTML attributes.
3
+ *
4
+ * @module bquery/ssr
5
+ * @internal
6
+ */
7
+
8
+ /**
9
+ * Escapes a string for safe embedding in an inline `<script>` body.
10
+ * @internal
11
+ */
12
+ export const escapeForScript = (str: string): string =>
13
+ str
14
+ .replace(/</g, '\\u003c')
15
+ .replace(/>/g, '\\u003e')
16
+ .replace(/\//g, '\\u002f')
17
+ .replace(/\u2028/g, '\\u2028')
18
+ .replace(/\u2029/g, '\\u2029');
19
+
20
+ /**
21
+ * Escapes a string for safe embedding in an HTML attribute value.
22
+ * @internal
23
+ */
24
+ export const escapeForHtmlAttribute = (str: string): string =>
25
+ str.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');