@canonical/react-ssr 0.27.1-experimental.0 → 0.28.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 (64) hide show
  1. package/README.md +74 -27
  2. package/dist/esm/bin/serve-bun.js +77 -0
  3. package/dist/esm/bin/serve-bun.js.map +1 -0
  4. package/dist/esm/bin/serve-express.js +19 -36
  5. package/dist/esm/bin/serve-express.js.map +1 -1
  6. package/dist/esm/lib/adapter/index.js +1 -1
  7. package/dist/esm/lib/adapter/index.js.map +1 -1
  8. package/dist/esm/lib/adapter/mime.js +7 -0
  9. package/dist/esm/lib/adapter/mime.js.map +1 -1
  10. package/dist/esm/lib/renderer/JSXRenderer.js +2 -0
  11. package/dist/esm/lib/renderer/JSXRenderer.js.map +1 -1
  12. package/dist/esm/lib/renderer/SitemapRenderer.js +2 -0
  13. package/dist/esm/lib/renderer/SitemapRenderer.js.map +1 -1
  14. package/dist/esm/lib/server/StaticMount.js +2 -0
  15. package/dist/esm/lib/server/StaticMount.js.map +1 -0
  16. package/dist/esm/lib/server/getRequestUrl.js +41 -0
  17. package/dist/esm/lib/server/getRequestUrl.js.map +1 -0
  18. package/dist/esm/lib/server/index.js +6 -0
  19. package/dist/esm/lib/server/index.js.map +1 -1
  20. package/dist/esm/lib/server/matchStaticRoute.js +14 -0
  21. package/dist/esm/lib/server/matchStaticRoute.js.map +1 -0
  22. package/dist/esm/lib/server/parseStaticPair.js +20 -0
  23. package/dist/esm/lib/server/parseStaticPair.js.map +1 -0
  24. package/dist/esm/lib/server/resolvePort.js +29 -0
  25. package/dist/esm/lib/server/resolvePort.js.map +1 -0
  26. package/dist/esm/lib/server/resolveStaticFile.js +61 -0
  27. package/dist/esm/lib/server/resolveStaticFile.js.map +1 -0
  28. package/dist/esm/lib/server/serveStream.js +1 -1
  29. package/dist/esm/lib/server/serveStream.js.map +1 -1
  30. package/dist/esm/lib/server/viteFetchMiddleware.js +152 -0
  31. package/dist/esm/lib/server/viteFetchMiddleware.js.map +1 -0
  32. package/dist/types/bin/serve-bun.d.ts +25 -0
  33. package/dist/types/bin/serve-bun.d.ts.map +1 -0
  34. package/dist/types/bin/serve-express.d.ts +6 -3
  35. package/dist/types/bin/serve-express.d.ts.map +1 -1
  36. package/dist/types/lib/adapter/index.d.ts +1 -1
  37. package/dist/types/lib/adapter/index.d.ts.map +1 -1
  38. package/dist/types/lib/adapter/mime.d.ts +7 -0
  39. package/dist/types/lib/adapter/mime.d.ts.map +1 -1
  40. package/dist/types/lib/adapter/types.d.ts +8 -10
  41. package/dist/types/lib/adapter/types.d.ts.map +1 -1
  42. package/dist/types/lib/renderer/JSXRenderer.d.ts +2 -0
  43. package/dist/types/lib/renderer/JSXRenderer.d.ts.map +1 -1
  44. package/dist/types/lib/renderer/SitemapRenderer.d.ts +2 -0
  45. package/dist/types/lib/renderer/SitemapRenderer.d.ts.map +1 -1
  46. package/dist/types/lib/server/StaticMount.d.ts +11 -0
  47. package/dist/types/lib/server/StaticMount.d.ts.map +1 -0
  48. package/dist/types/lib/server/getRequestUrl.d.ts +29 -0
  49. package/dist/types/lib/server/getRequestUrl.d.ts.map +1 -0
  50. package/dist/types/lib/server/index.d.ts +7 -0
  51. package/dist/types/lib/server/index.d.ts.map +1 -1
  52. package/dist/types/lib/server/matchStaticRoute.d.ts +12 -0
  53. package/dist/types/lib/server/matchStaticRoute.d.ts.map +1 -0
  54. package/dist/types/lib/server/parseStaticPair.d.ts +12 -0
  55. package/dist/types/lib/server/parseStaticPair.d.ts.map +1 -0
  56. package/dist/types/lib/server/resolvePort.d.ts +20 -0
  57. package/dist/types/lib/server/resolvePort.d.ts.map +1 -0
  58. package/dist/types/lib/server/resolveStaticFile.d.ts +20 -0
  59. package/dist/types/lib/server/resolveStaticFile.d.ts.map +1 -0
  60. package/dist/types/lib/server/serveStream.d.ts +1 -0
  61. package/dist/types/lib/server/serveStream.d.ts.map +1 -1
  62. package/dist/types/lib/server/viteFetchMiddleware.d.ts +81 -0
  63. package/dist/types/lib/server/viteFetchMiddleware.d.ts.map +1 -0
  64. package/package.json +11 -7
@@ -0,0 +1,61 @@
1
+ import path from "node:path";
2
+ import { matchStaticRoute } from "./matchStaticRoute.js";
3
+ /** True when the path's last segment carries a file extension (e.g. `.js`, `.txt`). */
4
+ function hasFileExtension(pathname) {
5
+ return /\.[a-z0-9]+$/i.test(pathname);
6
+ }
7
+ /**
8
+ * Resolve the on-disk path for a request under a static mount, or `null` if the
9
+ * request does not match the mount, is not a static-file request, or attempts to
10
+ * escape the mount directory.
11
+ *
12
+ * Only paths that carry a **file extension** are treated as static, so a root
13
+ * mount (`/`) serves `/robots.txt`, `/sitemap.xml`, `/favicon.ico`, and the
14
+ * hashed `/assets/*` while extensionless routes (`/`, `/about`) fall through to
15
+ * be server-rendered — `index.html` is never served in place of the SSR page.
16
+ *
17
+ * Guards against path traversal: the URL tail is decoded (so `%2e%2e`/`%2f`
18
+ * forms are caught) and rejected if it contains a `..` segment; as defence in
19
+ * depth the resolved path must remain within the mount directory. This mirrors
20
+ * the Deno adapter's guard so the bins cannot drift from it.
21
+ *
22
+ * @returns The absolute file path to serve, or `null` to skip this mount.
23
+ */
24
+ export function resolveStaticFile(pathname, mount) {
25
+ if (!matchStaticRoute(pathname, mount.route))
26
+ return null;
27
+ if (!hasFileExtension(pathname))
28
+ return null;
29
+ // A root mount serves from the mount dir itself; a prefixed mount strips the
30
+ // prefix so the remainder resolves under the dir.
31
+ const tail = mount.route === "/" ? pathname : pathname.slice(mount.route.length);
32
+ let decoded;
33
+ try {
34
+ decoded = decodeURIComponent(tail);
35
+ }
36
+ catch {
37
+ // Malformed percent-encoding — reject.
38
+ return null;
39
+ }
40
+ // Reject any traversal segment (covers `..`, `%2e%2e`, and `%2f`-smuggled
41
+ // separators since `tail` is decoded above).
42
+ if (decoded.split(/[/\\]/).includes(".."))
43
+ return null;
44
+ // Strip the leading separator so `path.join` cannot treat an absolute-looking
45
+ // tail (e.g. `/etc/passwd`) as a root and discard the mount directory. Resolve
46
+ // against the mount and verify the result stays within it — a path that
47
+ // escapes (via absolute tail or otherwise) is rejected. This containment check
48
+ // is the real guard; the `..` rejection above is defence in depth.
49
+ const relative = decoded.replace(/^[/\\]+/, "");
50
+ const resolvedDir = path.resolve(mount.dir);
51
+ const resolved = path.resolve(resolvedDir, relative);
52
+ /* v8 ignore next 4 -- defensive backstop: the `..` rejection and leading-separator
53
+ strip above already prevent every known escape, so this containment check is
54
+ not reachable in tests; it stays as a last line of defence against future drift. */
55
+ if (resolved !== resolvedDir &&
56
+ !resolved.startsWith(`${resolvedDir}${path.sep}`)) {
57
+ return null;
58
+ }
59
+ return resolved;
60
+ }
61
+ //# sourceMappingURL=resolveStaticFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveStaticFile.js","sourceRoot":"","sources":["../../../../src/lib/server/resolveStaticFile.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGzD,uFAAuF;AACvF,SAAS,gBAAgB,CAAC,QAAgB;IACxC,OAAO,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAgB,EAChB,KAAkB;IAElB,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1D,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAE7C,6EAA6E;IAC7E,kDAAkD;IAClD,MAAM,IAAI,GACR,KAAK,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAEtE,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,uCAAuC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,0EAA0E;IAC1E,6CAA6C;IAC7C,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvD,8EAA8E;IAC9E,+EAA+E;IAC/E,wEAAwE;IACxE,+EAA+E;IAC/E,mEAAmE;IACnE,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACrD;;0FAEsF;IACtF,IACE,QAAQ,KAAK,WAAW;QACxB,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,EACjD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -37,7 +37,7 @@ export function serveStream(factory) {
37
37
  const result = renderer.renderToPipeableStream();
38
38
  await renderer.statusReady;
39
39
  res.writeHead(renderer.statusCode, {
40
- "Content-Type": "text/html; charset=utf-8",
40
+ "Content-Type": renderer.contentType ?? "text/html; charset=utf-8",
41
41
  });
42
42
  result.pipe(res);
43
43
  /* v8 ignore next -- finish event fires after stream completes; not triggered in unit tests */
@@ -1 +1 @@
1
- {"version":3,"file":"serveStream.js","sourceRoot":"","sources":["../../../../src/lib/server/serveStream.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,WAAW,CACzB,OAMC;IAED,OAAO,KAAK,EAAE,GAAoB,EAAE,GAAmB,EAAE,EAAE;QACzD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;YAC9B,MAAM,MAAM,GAAG,QAAQ,CAAC,sBAAsB,EAAE,CAAC;YACjD,MAAM,QAAQ,CAAC,WAAW,CAAC;YAC3B,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE;gBACjC,cAAc,EAAE,0BAA0B;aAC3C,CAAC,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjB,8FAA8F;YAC9F,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;YAChD,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;YACrB,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QACnC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"serveStream.js","sourceRoot":"","sources":["../../../../src/lib/server/serveStream.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,WAAW,CACzB,OAOC;IAED,OAAO,KAAK,EAAE,GAAoB,EAAE,GAAmB,EAAE,EAAE;QACzD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;YAC9B,MAAM,MAAM,GAAG,QAAQ,CAAC,sBAAsB,EAAE,CAAC;YACjD,MAAM,QAAQ,CAAC,WAAW,CAAC;YAC3B,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE;gBACjC,cAAc,EAAE,QAAQ,CAAC,WAAW,IAAI,0BAA0B;aACnE,CAAC,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjB,8FAA8F;YAC9F,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;YAChD,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;YACrB,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QACnC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,152 @@
1
+ import { IncomingMessage, ServerResponse } from "node:http";
2
+ import { Socket } from "node:net";
3
+ /**
4
+ * Bridge Vite's connect-style middleware into a `fetch`-style handler.
5
+ *
6
+ * Runtime SSR dev servers built on `Bun.serve` (or any Web `fetch` server)
7
+ * cannot mount Vite's `(req, res, next)` middleware directly: that stack
8
+ * expects Node's `http.IncomingMessage` / `ServerResponse`, while `fetch`
9
+ * deals in Web `Request` / `Response`. Without delegating to Vite, every
10
+ * request — including `/@vite/client`, `/src/**`, `/@id/**`, `/@fs/**`,
11
+ * `/@react-refresh`, and `/node_modules/.vite/**` — would have to be
12
+ * server-rendered, so client modules and HMR assets would be returned as the
13
+ * SSR HTML page with the wrong `Content-Type` (browsers then block them as
14
+ * modules and the page never hydrates).
15
+ *
16
+ * This helper adapts a Web `Request` into a Node `req`/`res` pair, runs Vite's
17
+ * middleware against it, and returns a Web `Response` if the middleware handled
18
+ * the request — or `null` if the stack called `next()` (a page route the
19
+ * caller should server-render).
20
+ *
21
+ * The bridge is **runtime-agnostic**: it depends only on `node:http` /
22
+ * `node:net` and Web globals (`Request`, `Response`), so it works under Bun,
23
+ * Node's own `fetch` servers, Deno, and Workers — not just Bun. The `vite`
24
+ * instance is injected, not imported, keeping this module free of a runtime
25
+ * Vite dependency.
26
+ *
27
+ * The response body is **buffered**: chunks written by the middleware are
28
+ * collected and the `Response` resolves on `end`, with `status` and `headers`
29
+ * read from the (by-then-final) `ServerResponse`. This is correct and robust
30
+ * for dev assets — transformed JS/CSS modules and source maps, which are small
31
+ * and already in memory when Vite writes them. Streaming would buy near-zero
32
+ * latency here (SSR render streaming goes through the renderer's
33
+ * `renderToReadableStream`, never this bridge) while introducing header-timing
34
+ * and backpressure hazards, so it is deliberately avoided.
35
+ *
36
+ * Headers set via `res.writeHead(status, headers)` are captured in addition to
37
+ * `res.getHeaders()` (Vite's static-file middleware sets `Content-Type` etc.
38
+ * that way for non-JS assets), and multi-value headers such as `set-cookie` are
39
+ * preserved as separate entries rather than comma-folded.
40
+ *
41
+ * Only `GET`/`HEAD` requests are bridged. A bare `IncomingMessage` carries no
42
+ * request body, so any middleware that reads one would hang; requests with a
43
+ * body (and other methods) return `null` so the caller handles them. This
44
+ * matches Vite's asset/module/HMR surface, which is entirely `GET`/`HEAD`.
45
+ *
46
+ * @param vite - A Vite dev server created with `middlewareMode: true`.
47
+ * @returns A function mapping a Web `Request` to a `Response` (Vite handled it)
48
+ * or `null` (a page route, a non-`GET`/`HEAD` request — pass through to SSR).
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * import { createServer as createViteServer } from "vite";
53
+ * import { viteFetchMiddleware } from "@canonical/react-ssr/server";
54
+ *
55
+ * const vite = await createViteServer({
56
+ * server: { middlewareMode: true },
57
+ * appType: "custom",
58
+ * });
59
+ * const handleAsset = viteFetchMiddleware(vite);
60
+ *
61
+ * Bun.serve({
62
+ * async fetch(req) {
63
+ * const asset = await handleAsset(req);
64
+ * if (asset) return asset; // /@vite/client, /src/**, CSS, HMR …
65
+ * return ssrRender(req); // page route
66
+ * },
67
+ * });
68
+ * ```
69
+ */
70
+ export function viteFetchMiddleware(vite) {
71
+ return (request) => {
72
+ // Asset-only contract: a bare IncomingMessage carries no request body, so a
73
+ // middleware that reads one (e.g. Vite's proxy when `server.proxy` is set)
74
+ // would hang. Asset/module/HMR requests are all GET/HEAD; pass anything else
75
+ // through to the caller (page POSTs are server-rendered, not bridged).
76
+ if (request.method !== "GET" && request.method !== "HEAD") {
77
+ return Promise.resolve(null);
78
+ }
79
+ return new Promise((resolve, reject) => {
80
+ const url = new URL(request.url);
81
+ // A bare IncomingMessage needs a socket and a populated, lower-cased
82
+ // headers object before Vite's middleware runs — Vite reads navigation
83
+ // hints (e.g. `sec-fetch-mode`) and will throw on an empty header bag.
84
+ const socket = new Socket();
85
+ const req = new IncomingMessage(socket);
86
+ req.url = url.pathname + url.search;
87
+ req.method = request.method;
88
+ const headers = {};
89
+ request.headers.forEach((value, key) => {
90
+ headers[key.toLowerCase()] = value;
91
+ });
92
+ if (!headers.host)
93
+ headers.host = url.host;
94
+ req.headers = headers;
95
+ const res = new ServerResponse(req);
96
+ const chunks = [];
97
+ const originalWrite = res.write.bind(res);
98
+ const originalEnd = res.end.bind(res);
99
+ const originalWriteHead = res.writeHead.bind(res);
100
+ // Headers passed to writeHead(status, [statusMessage,] headers) are NOT
101
+ // returned by res.getHeaders() unless also set via setHeader, so capture
102
+ // them here. Vite's static (sirv) middleware sets Content-Type, ETag,
103
+ // Cache-Control this way for non-JS assets (svg, fonts, images, maps).
104
+ let writeHeadHeaders;
105
+ res.writeHead = ((statusCode, ...rest) => {
106
+ const last = rest[rest.length - 1];
107
+ if (last && typeof last === "object" && !Array.isArray(last)) {
108
+ writeHeadHeaders = last;
109
+ }
110
+ return originalWriteHead(statusCode, ...rest);
111
+ });
112
+ res.write = ((chunk, ...rest) => {
113
+ chunks.push(Buffer.from(chunk));
114
+ return originalWrite(chunk, ...rest);
115
+ });
116
+ res.end = ((chunk, ...rest) => {
117
+ if (chunk != null && typeof chunk !== "function") {
118
+ chunks.push(Buffer.from(chunk));
119
+ }
120
+ const result = originalEnd(chunk, ...rest);
121
+ // Merge setHeader() headers with writeHead() headers (the latter win),
122
+ // and preserve multi-value headers (e.g. set-cookie) as separate
123
+ // entries rather than comma-folding them via String().
124
+ const responseHeaders = new Headers();
125
+ const merged = {
126
+ ...res.getHeaders(),
127
+ ...writeHeadHeaders,
128
+ };
129
+ for (const [key, value] of Object.entries(merged)) {
130
+ if (value == null)
131
+ continue;
132
+ if (Array.isArray(value)) {
133
+ for (const item of value)
134
+ responseHeaders.append(key, String(item));
135
+ }
136
+ else {
137
+ responseHeaders.set(key, String(value));
138
+ }
139
+ }
140
+ resolve(new Response(chunks.length ? Buffer.concat(chunks) : null, {
141
+ status: res.statusCode,
142
+ headers: responseHeaders,
143
+ }));
144
+ return result;
145
+ });
146
+ res.on("error", reject);
147
+ // `next()` → Vite did not handle it (a page route); the caller renders it.
148
+ vite.middlewares(req, res, () => resolve(null));
149
+ });
150
+ };
151
+ }
152
+ //# sourceMappingURL=viteFetchMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viteFetchMiddleware.js","sourceRoot":"","sources":["../../../../src/lib/server/viteFetchMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAkBlC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAA0B;IAE1B,OAAO,CAAC,OAAO,EAAE,EAAE;QACjB,4EAA4E;QAC5E,2EAA2E;QAC3E,6EAA6E;QAC7E,uEAAuE;QACvE,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC1D,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;QAED,OAAO,IAAI,OAAO,CAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACtD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAEjC,qEAAqE;YACrE,uEAAuE;YACvE,uEAAuE;YACvE,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;YACxC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC;YACpC,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAE5B,MAAM,OAAO,GAA2B,EAAE,CAAC;YAC3C,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;gBACrC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC;YACrC,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,IAAI;gBAAE,OAAO,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;YAC3C,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC;YAEtB,MAAM,GAAG,GAAG,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC;YAEpC,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,MAAM,aAAa,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC1C,MAAM,WAAW,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,iBAAiB,GAAG,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAElD,wEAAwE;YACxE,yEAAyE;YACzE,sEAAsE;YACtE,uEAAuE;YACvE,IAAI,gBAES,CAAC;YAEd,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,UAAkB,EAAE,GAAG,IAAe,EAAE,EAAE;gBAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBACnC,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC7D,gBAAgB,GAAG,IAAgD,CAAC;gBACtE,CAAC;gBACD,OACE,iBACD,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,CAAC;YACzB,CAAC,CAAgC,CAAC;YAElC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,KAAc,EAAE,GAAG,IAAe,EAAE,EAAE;gBAClD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAmB,CAAC,CAAC,CAAC;gBAC9C,OAAQ,aAAiD,CACvD,KAAK,EACL,GAAG,IAAI,CACR,CAAC;YACJ,CAAC,CAA4B,CAAC;YAE9B,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,KAAc,EAAE,GAAG,IAAe,EAAE,EAAE;gBAChD,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;oBACjD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAmB,CAAC,CAAC,CAAC;gBAChD,CAAC;gBACD,MAAM,MAAM,GAAI,WAAsD,CACpE,KAAK,EACL,GAAG,IAAI,CACR,CAAC;gBAEF,uEAAuE;gBACvE,iEAAiE;gBACjE,uDAAuD;gBACvD,MAAM,eAAe,GAAG,IAAI,OAAO,EAAE,CAAC;gBACtC,MAAM,MAAM,GAA2D;oBACrE,GAAG,GAAG,CAAC,UAAU,EAAE;oBACnB,GAAG,gBAAgB;iBACpB,CAAC;gBACF,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;oBAClD,IAAI,KAAK,IAAI,IAAI;wBAAE,SAAS;oBAC5B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;wBACzB,KAAK,MAAM,IAAI,IAAI,KAAK;4BAAE,eAAe,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;oBACtE,CAAC;yBAAM,CAAC;wBACN,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;oBAC1C,CAAC;gBACH,CAAC;gBAED,OAAO,CACL,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;oBACzD,MAAM,EAAE,GAAG,CAAC,UAAU;oBACtB,OAAO,EAAE,eAAe;iBACzB,CAAC,CACH,CAAC;gBACF,OAAO,MAAM,CAAC;YAChB,CAAC,CAA0B,CAAC;YAE5B,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAExB,2EAA2E;YAC3E,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Standalone Bun server for serving an SSR application with stream rendering.
4
+ *
5
+ * Uses `Bun.serve()` for the HTTP layer and `Bun.file()` for zero-copy static
6
+ * file serving. The renderer module must default-export a factory that accepts a
7
+ * `Request` and returns a renderer with `renderToReadableStream`.
8
+ *
9
+ * Argument parsing, port resolution, and static-file matching live in
10
+ * `@canonical/react-ssr/server` (covered by unit tests); this bin is the thin
11
+ * Bun-specific shell around them.
12
+ *
13
+ * @example
14
+ * ```sh
15
+ * # Single static directory
16
+ * serve-bun dist/server/renderer.js --static assets:dist/client/assets
17
+ *
18
+ * # Multiple static directories, custom port
19
+ * serve-bun dist/server/renderer.js -p 3000 \
20
+ * --static assets:dist/client/assets \
21
+ * --static public:dist/client/public
22
+ * ```
23
+ */
24
+ export {};
25
+ //# sourceMappingURL=serve-bun.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-bun.d.ts","sourceRoot":"","sources":["../../../src/bin/serve-bun.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG"}
@@ -2,9 +2,12 @@
2
2
  /**
3
3
  * Standalone Express server for serving an SSR application.
4
4
  *
5
- * Dynamically imports a renderer module that exports a factory function.
6
- * The factory receives a Node `IncomingMessage` and returns a renderer.
7
- * Supports both streaming and string rendering modes.
5
+ * Dynamically imports a renderer module that default-exports a factory. The
6
+ * factory receives a Node `IncomingMessage` and returns a renderer. Supports
7
+ * both streaming (`--streaming`) and string rendering modes.
8
+ *
9
+ * Argument parsing and port resolution live in `@canonical/react-ssr/server`
10
+ * (covered by unit tests); this bin is the thin Express-specific shell.
8
11
  *
9
12
  * @example
10
13
  * ```sh
@@ -1 +1 @@
1
- {"version":3,"file":"serve-express.d.ts","sourceRoot":"","sources":["../../../src/bin/serve-express.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;GAoBG"}
1
+ {"version":3,"file":"serve-express.d.ts","sourceRoot":"","sources":["../../../src/bin/serve-express.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBG"}
@@ -1,3 +1,3 @@
1
- export { buildCacheControl, getMimeType, matchPattern } from "./mime.js";
1
+ export { buildCacheControl, getMimeType, IMMUTABLE_ASSET_CACHE_CONTROL, matchPattern, } from "./mime.js";
2
2
  export type { CacheConfig, RendererFactory, RendererResult, RouteDefinition, StaticAssetConfig, } from "./types.js";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapter/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzE,YAAY,EACV,WAAW,EACX,eAAe,EACf,cAAc,EACd,eAAe,EACf,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapter/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,6BAA6B,EAC7B,YAAY,GACb,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,WAAW,EACX,eAAe,EACf,cAAc,EACd,eAAe,EACf,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
@@ -7,6 +7,13 @@
7
7
  *
8
8
  * No external dependencies — the lookup is a plain record.
9
9
  */
10
+ /**
11
+ * `Cache-Control` value for immutable, content-hashed static assets (e.g. Vite
12
+ * build output). One year, immutable — the canonical policy every adapter
13
+ * applies when serving fingerprinted assets. Centralised here so the three
14
+ * deployment adapters cannot drift.
15
+ */
16
+ export declare const IMMUTABLE_ASSET_CACHE_CONTROL = "public, max-age=31536000, immutable";
10
17
  /**
11
18
  * Look up the MIME type for a file path based on its extension.
12
19
  *
@@ -1 +1 @@
1
- {"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapter/mime.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA0BH;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,GAAG,MAAM,CAOT;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOvE"}
1
+ {"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapter/mime.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,wCACH,CAAC;AA0BxC;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,GAAG,MAAM,CAOT;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOvE"}
@@ -65,21 +65,19 @@ export interface CacheConfig {
65
65
  staleWhileRevalidate?: number;
66
66
  }
67
67
  /**
68
- * Configuration for serving static assets (CSS, JS, images, fonts).
68
+ * Configuration for serving static assets (CSS, JS, images, fonts) from a
69
+ * runtime that has no static layer in front of the SSR handler.
69
70
  *
70
- * Each entry maps a URL prefix to a source directory. The adapter serves
71
- * matching requests from the source using the platform's optimal mechanism
72
- * (R2 on Cloudflare, CDN on Vercel, filesystem on Deno/Bun).
71
+ * Only the Deno/Bun adapters use this: their runtimes serve the SSR handler
72
+ * directly, so the handler reads matching files from the filesystem. Cloudflare
73
+ * and Vercel do NOT use it those platforms serve static assets at the edge
74
+ * before the SSR function runs (Workers Static Assets / the Vercel CDN), so
75
+ * their adapters take no `staticAssets` config. See each adapter's README.
73
76
  */
74
77
  export interface StaticAssetConfig {
75
78
  /** URL path prefix (e.g. `"/assets"`). */
76
79
  urlPrefix: string;
77
- /**
78
- * Source directory or key prefix.
79
- * - Cloudflare: R2 key prefix (e.g. `"assets"`)
80
- * - Vercel: not used (static assets served by CDN from `.vercel/output/static/`)
81
- * - Deno/Bun: filesystem path (e.g. `"dist/client/assets"`)
82
- */
80
+ /** Filesystem path the prefix maps to (e.g. `"dist/client/assets"`). */
83
81
  directory: string;
84
82
  }
85
83
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapter/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,qFAAqF;IACrF,sBAAsB,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAE1E,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IAEnB,gDAAgD;IAChD,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,cAAc,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAEhB,4DAA4D;IAC5D,OAAO,EAAE,eAAe,CAAC;IAEzB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,yDAAyD;IACzD,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,wFAAwF;IACxF,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAElB;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC;CACnB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapter/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,qFAAqF;IACrF,sBAAsB,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAE1E,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IAEnB,gDAAgD;IAChD,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,cAAc,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAEhB,4DAA4D;IAC5D,OAAO,EAAE,eAAe,CAAC;IAEzB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,yDAAyD;IACzD,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,wFAAwF;IACxF,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAElB,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -49,6 +49,8 @@ export default class JSXRenderer<TComponent extends ServerEntrypoint<InitialData
49
49
  * `renderToString`) or after awaiting `statusReady` (for `renderToPipeableStream`).
50
50
  */
51
51
  statusCode: number;
52
+ /** MIME type of the rendered output, for the consumer's `Content-Type` header. */
53
+ readonly contentType = "text/html; charset=utf-8";
52
54
  /**
53
55
  * Resolves when `statusCode` is determined.
54
56
  *
@@ -1 +1 @@
1
- {"version":3,"file":"JSXRenderer.d.ts","sourceRoot":"","sources":["../../../../src/lib/renderer/JSXRenderer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAGtE,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,KAAK,EACV,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAmBpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,CAAC,OAAO,OAAO,WAAW,CAC9B,UAAU,SAAS,gBAAgB,CAAC,WAAW,CAAC,EAChD,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAoCzC,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,UAAU;IACxC,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW;IAC3C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,eAAe;IApC7C,SAAS,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC;IAE3C;;;;;;;;OAQG;IACI,UAAU,SAAO;IAExB;;;;;;OAMG;IACI,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAqB;IAEtD;;;;;;;;;;OAUG;gBAEkB,SAAS,EAAE,UAAU,EACrB,WAAW,GAAE,WAA+B,EAC5C,OAAO,GAAE,eAAoB;IAOlD;;;;;;OAMG;IACI,SAAS,IAAI,MAAM;IAI1B;;;;;;OAMG;IACH,SAAS,CAAC,iBAAiB,IAAI,qBAAqB,CAAC,WAAW,CAAC;IAUjE;;;;;;;;;;OAUG;IACH,SAAS,CAAC,sBAAsB,CAC9B,OAAO,EAAE,KAAK,CAAC,YAAY,EAAE,EAC7B,IAAI,EAAE,QAAQ,GAAG,SAAS,GACzB,MAAM,EAAE;IAmBX;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,qBAAqB,CAC7B,KAAK,EAAE,qBAAqB,CAAC,WAAW,CAAC,GACxC,6BAA6B;IA8BhC;;;;;;;;;;;;;;OAcG;IACH,sBAAsB,GACpB,SAAS,WAAW,KACnB,OAAO,CAAC,cAAc,CAAC,CAuCxB;IAEF;;;;;;;;;;;;;;OAcG;IACH,sBAAsB,QAAO,oBAAoB,CAkD/C;IAEF;;;;;;;;;;;;OAYG;IACH,cAAc,QAAO,MAAM,CAOzB;CACH"}
1
+ {"version":3,"file":"JSXRenderer.d.ts","sourceRoot":"","sources":["../../../../src/lib/renderer/JSXRenderer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAGtE,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,KAAK,EACV,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAmBpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,CAAC,OAAO,OAAO,WAAW,CAC9B,UAAU,SAAS,gBAAgB,CAAC,WAAW,CAAC,EAChD,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAuCzC,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,UAAU;IACxC,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW;IAC3C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,eAAe;IAvC7C,SAAS,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC;IAE3C;;;;;;;;OAQG;IACI,UAAU,SAAO;IAExB,kFAAkF;IAClF,SAAgB,WAAW,8BAA8B;IAEzD;;;;;;OAMG;IACI,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAqB;IAEtD;;;;;;;;;;OAUG;gBAEkB,SAAS,EAAE,UAAU,EACrB,WAAW,GAAE,WAA+B,EAC5C,OAAO,GAAE,eAAoB;IAOlD;;;;;;OAMG;IACI,SAAS,IAAI,MAAM;IAI1B;;;;;;OAMG;IACH,SAAS,CAAC,iBAAiB,IAAI,qBAAqB,CAAC,WAAW,CAAC;IAUjE;;;;;;;;;;OAUG;IACH,SAAS,CAAC,sBAAsB,CAC9B,OAAO,EAAE,KAAK,CAAC,YAAY,EAAE,EAC7B,IAAI,EAAE,QAAQ,GAAG,SAAS,GACzB,MAAM,EAAE;IAmBX;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,qBAAqB,CAC7B,KAAK,EAAE,qBAAqB,CAAC,WAAW,CAAC,GACxC,6BAA6B;IA8BhC;;;;;;;;;;;;;;OAcG;IACH,sBAAsB,GACpB,SAAS,WAAW,KACnB,OAAO,CAAC,cAAc,CAAC,CAuCxB;IAEF;;;;;;;;;;;;;;OAcG;IACH,sBAAsB,QAAO,oBAAoB,CAkD/C;IAEF;;;;;;;;;;;;OAYG;IACH,cAAc,QAAO,MAAM,CAOzB;CACH"}
@@ -37,6 +37,8 @@ export default class SitemapRenderer {
37
37
  * error handler decides the status code for unexpected failures.
38
38
  */
39
39
  statusCode: number;
40
+ /** MIME type of the rendered output, for the consumer's `Content-Type` header. */
41
+ readonly contentType = "application/xml; charset=utf-8";
40
42
  /**
41
43
  * Resolves when `statusCode` is determined.
42
44
  *
@@ -1 +1 @@
1
- {"version":3,"file":"SitemapRenderer.d.ts","sourceRoot":"","sources":["../../../../src/lib/renderer/SitemapRenderer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EACb,aAAa,EACb,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,OAAO,OAAO,eAAe;IA2BhC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE;IACpD,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,aAAa;IA3B1C;;;;;;OAMG;IACI,UAAU,SAAO;IAExB;;;;;;OAMG;IACI,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAqB;IAEtD;;;;;;OAMG;gBAEkB,OAAO,EAAE,SAAS,aAAa,EAAE,EACjC,MAAM,EAAE,aAAa;IAG1C;;;;;;;;OAQG;cACa,SAAS,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAKnD;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,EAAE;IAcnE;;;;;;;;OAQG;IACH,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM;IAKxD;;;;;;;;OAQG;IACH,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IASjD;;;;;;;;;OASG;IACH,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM;IA6BtD;;;OAGG;cACa,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAM3C;;;;;;;;;;OAUG;IACH,sBAAsB,GACpB,UAAU,WAAW,KACpB,OAAO,CAAC,cAAc,CAAC,CAWxB;IAEF;;;;;;;;;;OAUG;IACH,sBAAsB,QAAO,oBAAoB,CAqB/C;IAEF;;;;;;;;;;;OAWG;IACH,cAAc,QAAa,OAAO,CAAC,MAAM,CAAC,CAKxC;CACH"}
1
+ {"version":3,"file":"SitemapRenderer.d.ts","sourceRoot":"","sources":["../../../../src/lib/renderer/SitemapRenderer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EACb,aAAa,EACb,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,OAAO,OAAO,eAAe;IA8BhC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE;IACpD,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,aAAa;IA9B1C;;;;;;OAMG;IACI,UAAU,SAAO;IAExB,kFAAkF;IAClF,SAAgB,WAAW,oCAAoC;IAE/D;;;;;;OAMG;IACI,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAqB;IAEtD;;;;;;OAMG;gBAEkB,OAAO,EAAE,SAAS,aAAa,EAAE,EACjC,MAAM,EAAE,aAAa;IAG1C;;;;;;;;OAQG;cACa,SAAS,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAKnD;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,EAAE;IAcnE;;;;;;;;OAQG;IACH,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM;IAKxD;;;;;;;;OAQG;IACH,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IASjD;;;;;;;;;OASG;IACH,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM;IA6BtD;;;OAGG;cACa,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAM3C;;;;;;;;;;OAUG;IACH,sBAAsB,GACpB,UAAU,WAAW,KACpB,OAAO,CAAC,cAAc,CAAC,CAWxB;IAEF;;;;;;;;;;OAUG;IACH,sBAAsB,QAAO,oBAAoB,CAqB/C;IAEF;;;;;;;;;;;OAWG;IACH,cAAc,QAAa,OAAO,CAAC,MAAM,CAAC,CAKxC;CACH"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * A static mount: a URL route prefix mapped to a filesystem directory.
3
+ */
4
+ export interface StaticMount {
5
+ /** URL prefix, always leading-slashed, e.g. `/assets`. The root mount `/`
6
+ * serves a whole directory. */
7
+ route: string;
8
+ /** Absolute directory the route serves from. */
9
+ dir: string;
10
+ }
11
+ //# sourceMappingURL=StaticMount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StaticMount.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/StaticMount.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;mCAC+B;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;CACb"}
@@ -0,0 +1,29 @@
1
+ import type { IncomingMessage } from "node:http";
2
+ /**
3
+ * Extract the request path (`pathname` + `search`) from either a Web `Request`
4
+ * or a Node `IncomingMessage`.
5
+ *
6
+ * The `serve-bun` and `serve-express` bins call a renderer factory with
7
+ * different request shapes — `serve-bun` passes a Web `Request` (whose `url` is
8
+ * absolute, e.g. `https://host/a/b?x=1`), while `serve-express` (via
9
+ * `serveStream`) passes a Node `IncomingMessage` (whose `url` is already a path,
10
+ * e.g. `/a/b?x=1`). A renderer factory that needs the URL for routing must
11
+ * handle both; this helper normalises them to a single path string.
12
+ *
13
+ * @param request - A Web `Request` or a Node `IncomingMessage`.
14
+ * @returns The request path including query string, e.g. `/a/b?x=1`. Defaults to
15
+ * `/` when no URL is present.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { JSXRenderer } from "@canonical/react-ssr/renderer";
20
+ * import { getRequestUrl } from "@canonical/react-ssr/server";
21
+ *
22
+ * export default function createRenderer(request: Request | IncomingMessage) {
23
+ * const url = getRequestUrl(request);
24
+ * return new JSXRenderer(EntryServer, { url }, { htmlString });
25
+ * }
26
+ * ```
27
+ */
28
+ export declare function getRequestUrl(request: Request | IncomingMessage): string;
29
+ //# sourceMappingURL=getRequestUrl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getRequestUrl.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/getRequestUrl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,eAAe,GAAG,MAAM,CAWxE"}
@@ -1,3 +1,10 @@
1
+ export { getRequestUrl } from "./getRequestUrl.js";
2
+ export { matchStaticRoute } from "./matchStaticRoute.js";
3
+ export { parseStaticPair } from "./parseStaticPair.js";
4
+ export { resolvePort } from "./resolvePort.js";
5
+ export { resolveStaticFile } from "./resolveStaticFile.js";
6
+ export type { StaticMount } from "./StaticMount.js";
1
7
  export { serveStream } from "./serveStream.js";
2
8
  export { serveString } from "./serveString.js";
9
+ export { type ViteMiddlewareServer, viteFetchMiddleware, } from "./viteFetchMiddleware.js";
3
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EACL,KAAK,oBAAoB,EACzB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Does `pathname` fall under `route`, matched on a path-segment boundary?
3
+ *
4
+ * `/assets` matches `/assets` and `/assets/x` but NOT `/assetsfoo/x` — a plain
5
+ * `startsWith` would wrongly match the latter. The root mount `/` matches every
6
+ * path, so a single `:dir` mount can serve a whole built client directory.
7
+ *
8
+ * @param pathname - The request pathname (no query string).
9
+ * @param route - The mount's route prefix.
10
+ */
11
+ export declare function matchStaticRoute(pathname: string, route: string): boolean;
12
+ //# sourceMappingURL=matchStaticRoute.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matchStaticRoute.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/matchStaticRoute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAIzE"}
@@ -0,0 +1,12 @@
1
+ import type { StaticMount } from "./StaticMount.js";
2
+ /**
3
+ * Parse a `route:filepath` pair (e.g. `"assets:dist/client/assets"`) into a
4
+ * {@link StaticMount}. With no separator the whole string is both the route and
5
+ * the (cwd-relative) directory; an empty route (`":dist/client"`) is the root
6
+ * mount. The directory is resolved against `cwd`.
7
+ *
8
+ * @param pair - A `"route:filepath"` or bare `"name"` string.
9
+ * @param cwd - Base directory for resolving the filepath (default `process.cwd()`).
10
+ */
11
+ export declare function parseStaticPair(pair: string, cwd?: string): StaticMount;
12
+ //# sourceMappingURL=parseStaticPair.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseStaticPair.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/parseStaticPair.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,MAAsB,GAC1B,WAAW,CAQb"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Resolve the port a `serve-*` bin should listen on.
3
+ *
4
+ * Precedence: an explicit `--port`/`-p` flag, then the `PORT` env var, then the
5
+ * default `5174`. A value that is *present but not a valid TCP port* (non-integer
6
+ * or outside `1..65535`, including `"0"`) is a user error — it throws rather than
7
+ * silently falling back to the default, so a typo'd port fails loudly instead of
8
+ * binding the wrong one. Only an absent value falls through to the default.
9
+ *
10
+ * The `5174` default matches the SSR dev servers (`PORT || 5174`), so every SSR
11
+ * target — dev and preview, Bun and Node — lands on the same port by default.
12
+ *
13
+ * @param flag - The `--port`/`-p` value, if supplied.
14
+ * @param env - The `PORT` env var, if set.
15
+ * @param fallback - Port to use when neither is supplied (default `5174`).
16
+ * @returns The resolved port number.
17
+ * @throws If a supplied value is not an integer in `1..65535`.
18
+ */
19
+ export declare function resolvePort(flag: string | undefined, env: string | undefined, fallback?: number): number;
20
+ //# sourceMappingURL=resolvePort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolvePort.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/resolvePort.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,QAAQ,SAAO,GACd,MAAM,CAWR"}
@@ -0,0 +1,20 @@
1
+ import type { StaticMount } from "./StaticMount.js";
2
+ /**
3
+ * Resolve the on-disk path for a request under a static mount, or `null` if the
4
+ * request does not match the mount, is not a static-file request, or attempts to
5
+ * escape the mount directory.
6
+ *
7
+ * Only paths that carry a **file extension** are treated as static, so a root
8
+ * mount (`/`) serves `/robots.txt`, `/sitemap.xml`, `/favicon.ico`, and the
9
+ * hashed `/assets/*` while extensionless routes (`/`, `/about`) fall through to
10
+ * be server-rendered — `index.html` is never served in place of the SSR page.
11
+ *
12
+ * Guards against path traversal: the URL tail is decoded (so `%2e%2e`/`%2f`
13
+ * forms are caught) and rejected if it contains a `..` segment; as defence in
14
+ * depth the resolved path must remain within the mount directory. This mirrors
15
+ * the Deno adapter's guard so the bins cannot drift from it.
16
+ *
17
+ * @returns The absolute file path to serve, or `null` to skip this mount.
18
+ */
19
+ export declare function resolveStaticFile(pathname: string, mount: StaticMount): string | null;
20
+ //# sourceMappingURL=resolveStaticFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveStaticFile.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/resolveStaticFile.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAOpD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,WAAW,GACjB,MAAM,GAAG,IAAI,CAuCf"}
@@ -37,5 +37,6 @@ export declare function serveStream(factory: (req: IncomingMessage) => {
37
37
  };
38
38
  statusCode: number;
39
39
  statusReady: Promise<void>;
40
+ contentType?: string;
40
41
  }): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
41
42
  //# sourceMappingURL=serveStream.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serveStream.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/serveStream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK;IACjC,sBAAsB,EAAE,MAAM;QAC5B,IAAI,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,cAAc,EAAE,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC;KAC9D,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,IAEa,KAAK,eAAe,EAAE,KAAK,cAAc,mBAiBxD"}
1
+ {"version":3,"file":"serveStream.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/serveStream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK;IACjC,sBAAsB,EAAE,MAAM;QAC5B,IAAI,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,cAAc,EAAE,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC;KAC9D,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,IAEa,KAAK,eAAe,EAAE,KAAK,cAAc,mBAiBxD"}