@bakery-framework/core 1.2.3 → 2.0.0-alpha.11

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.
@@ -4,7 +4,18 @@ import { Try } from '../common/try'
4
4
  import { DOMTools, headBodyCache } from './dom'
5
5
  import { ETag } from './etag'
6
6
 
7
- function injectBrand(res: Response) {
7
+ /**
8
+ * Mark `res` as already carrying the head/body injects, so `injectIfHtml` —
9
+ * and therefore `processResponse`, which every response funnels through —
10
+ * hands it back untouched.
11
+ *
12
+ * Exported for the one caller that means "never", not "already":
13
+ * `DefaultErrorHandler` brands its production error page so the import map is
14
+ * not spliced into it. That map names every installed package — an inventory
15
+ * of the app's module layout — and the page carries no scripts for it (or the
16
+ * client bundle) to serve anyway.
17
+ */
18
+ export function injectBrand(res: Response) {
8
19
  return Object.defineProperty(res, '__injected__', {
9
20
  value: true,
10
21
  enumerable: false,
@@ -80,7 +91,7 @@ export const STREAM_THRESHOLD_BYTES = 64 * 1024
80
91
 
81
92
  export async function injectIfHtml(
82
93
  data: string | Response | Blob,
83
- params?: MapOf<string>,
94
+ params?: MapOf<unknown>,
84
95
  injects?: HtmlInjects,
85
96
  ): Promise<Response | null> {
86
97
  if (data instanceof Response && isInjected(data)) return data
@@ -127,7 +138,7 @@ export async function injectIfHtml(
127
138
  * streamed path's fallback both come through here. */
128
139
  function bufferedResponse(
129
140
  content: string,
130
- params: MapOf<string> | undefined,
141
+ params: MapOf<unknown> | undefined,
131
142
  injects: HtmlInjects | undefined,
132
143
  responseInit: ResponseInit & { headers?: any },
133
144
  ): Response {
@@ -182,7 +193,7 @@ function getConfigInjects() {
182
193
  * into a pull callback, where the AsyncLocalStorage host context that
183
194
  * `getConfigInjects` and `DOMTools.importMap` read may no longer be live.
184
195
  */
185
- function computeInjects(params: MapOf<string>, injects: HtmlInjects) {
196
+ function computeInjects(params: MapOf<unknown>, injects: HtmlInjects) {
186
197
  const configInjects = getConfigInjects()
187
198
  const paramsStr = DOMTools.params(params)
188
199
 
@@ -235,7 +246,7 @@ function rewriteFontsUrls(html: string): string {
235
246
 
236
247
  export function assembleHtml(
237
248
  content: string,
238
- params: MapOf<string> = {},
249
+ params: MapOf<unknown> = {},
239
250
  injects: HtmlInjects = {},
240
251
  ) {
241
252
  const frags = computeInjects(params, injects)
@@ -1,4 +1,6 @@
1
+ export * from './authorize'
1
2
  export * from './body'
3
+ export * from './credential'
2
4
  export * from './csrf'
3
5
  export * from './dom'
4
6
  export * from './escape'
@@ -7,3 +9,4 @@ export * from './html'
7
9
  export * from './ip'
8
10
  export * from './response'
9
11
  export * from './sse'
12
+ export * from './url'
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The request's parsed URL, parsed at most once.
3
+ *
4
+ * Why memoize at all: `new URL` measures ~1.7µs, and the router, the body
5
+ * parser and the proxy handler all want the same parse of the same request —
6
+ * so the parse was hoisted into the server's `fetch` in `packages/cli/src/worker.ts`
7
+ * and shared.
8
+ *
9
+ * Why a `WeakMap` rather than the property it replaces. The memo used to be
10
+ * `(req as any).__parsedUrl`: two writers, six readers across three packages,
11
+ * every reader spelled `(req as any).__parsedUrl || new URL(req.url)` so that
12
+ * it silently re-parsed whenever the writer had not run first. That is a
13
+ * writer/reader ordering hazard with no way to observe it going wrong — a
14
+ * missed write costs microseconds, not correctness, so nothing ever fails.
15
+ * One function collapses both roles: there is no ordering left to get wrong,
16
+ * no `any` cast anywhere, and no writer to forget. The map also keeps the
17
+ * framework from mutating a `Request` object it does not own — a caller's
18
+ * `Request` goes in and comes back unchanged — and holds its keys weakly, so
19
+ * an entry dies with the request rather than needing eviction (convention 6).
20
+ *
21
+ * There is deliberately no setter. Nothing outside this module needs to say
22
+ * what a request's URL is; `req.url` already does.
23
+ */
24
+
25
+ const cache = new WeakMap<Request, URL>()
26
+
27
+ /** The parsed `req.url`, cached per request. */
28
+ export function parsedUrl(req: Request): URL {
29
+ const cached = cache.get(req)
30
+ if (cached) return cached
31
+
32
+ const url = new URL(req.url)
33
+ cache.set(req, url)
34
+ return url
35
+ }
@@ -15,6 +15,22 @@ export function any<T = any>(value: any): T {
15
15
  return value
16
16
  }
17
17
 
18
+ /**
19
+ * A hex id of `length` characters, from `crypto.getRandomValues`.
20
+ *
21
+ * Isomorphic, and moving it here is what made it so: it lived in
22
+ * `client/utils.ts` as a browser global only, so the same call in a server
23
+ * block was a ReferenceError — reported from an app. `crypto` is a global in
24
+ * both runtimes; nothing here is browser-specific.
25
+ */
26
+ export function randomId(length = 8) {
27
+ const arr = new Uint8Array(Math.ceil(length / 2))
28
+ crypto.getRandomValues(arr)
29
+ return Array.from(arr, dec => dec.toString(16).padStart(2, '0'))
30
+ .join('')
31
+ .slice(0, length)
32
+ }
33
+
18
34
  export function repeat(n: number): number[]
19
35
  export function repeat<T>(n: number, fn: (i: number) => T): T[]
20
36
  export function repeat<T>(n: number, fn?: (i: number) => T): unknown[] {