@qwik.dev/router 2.0.0-beta.43 → 2.0.0-beta.47

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 (50) hide show
  1. package/lib/adapters/azure-swa/vite/index.d.ts +2 -2
  2. package/lib/adapters/azure-swa/vite/index.mjs +2 -1
  3. package/lib/adapters/bun-server/vite/index.d.ts +2 -2
  4. package/lib/adapters/bun-server/vite/index.mjs +2 -1
  5. package/lib/adapters/cloud-run/vite/index.d.ts +2 -2
  6. package/lib/adapters/cloud-run/vite/index.mjs +2 -1
  7. package/lib/adapters/cloudflare-pages/vite/index.d.ts +2 -2
  8. package/lib/adapters/cloudflare-pages/vite/index.mjs +2 -1
  9. package/lib/adapters/deno-server/vite/index.d.ts +2 -2
  10. package/lib/adapters/deno-server/vite/index.mjs +2 -1
  11. package/lib/adapters/netlify-edge/vite/index.d.ts +2 -2
  12. package/lib/adapters/netlify-edge/vite/index.mjs +2 -1
  13. package/lib/adapters/node-server/vite/index.d.ts +2 -2
  14. package/lib/adapters/node-server/vite/index.mjs +2 -1
  15. package/lib/adapters/shared/vite/index.d.ts +1 -1
  16. package/lib/adapters/shared/vite/index.mjs +36 -32
  17. package/lib/adapters/ssg/vite/index.d.ts +1 -1
  18. package/lib/adapters/ssg/vite/index.mjs +4 -2
  19. package/lib/adapters/vercel-edge/vite/index.d.ts +2 -2
  20. package/lib/adapters/vercel-edge/vite/index.mjs +18 -15
  21. package/lib/chunks/fs.mjs +28 -25
  22. package/lib/chunks/head.qwik.mjs +731 -448
  23. package/lib/chunks/http-error.qwik.mjs +8 -7
  24. package/lib/chunks/pathname.mjs +9 -8
  25. package/lib/chunks/request-path.mjs +8 -4
  26. package/lib/chunks/route-trie-key.mjs +10 -0
  27. package/lib/chunks/routes.mjs +14 -14
  28. package/lib/chunks/server-error.mjs +28 -6
  29. package/lib/chunks/{url.mjs → static-file.mjs} +12 -2
  30. package/lib/chunks/system.mjs +31 -25
  31. package/lib/chunks/user-response.mjs +455 -348
  32. package/lib/chunks/worker-thread.mjs +90 -88
  33. package/lib/index.d.ts +94 -63
  34. package/lib/index.qwik.mjs +615 -523
  35. package/lib/middleware/aws-lambda/index.mjs +6 -3
  36. package/lib/middleware/azure-swa/index.mjs +4 -2
  37. package/lib/middleware/bun/index.mjs +12 -12
  38. package/lib/middleware/cloudflare-pages/index.mjs +19 -4
  39. package/lib/middleware/deno/index.mjs +12 -12
  40. package/lib/middleware/firebase/index.mjs +4 -2
  41. package/lib/middleware/netlify-edge/index.mjs +4 -2
  42. package/lib/middleware/node/index.mjs +16 -15
  43. package/lib/middleware/request-handler/index.d.ts +102 -2
  44. package/lib/middleware/request-handler/index.mjs +30 -34
  45. package/lib/middleware/vercel-edge/index.mjs +4 -2
  46. package/lib/service-worker/index.mjs +2 -1
  47. package/lib/ssg/index.mjs +23 -18
  48. package/lib/vite/index.d.ts +1 -1
  49. package/lib/vite/index.mjs +669 -604
  50. package/package.json +5 -5
@@ -1,6 +1,7 @@
1
1
  import { t as ensureSlash } from "../../chunks/pathname.mjs";
2
2
  import { createQwikRouter as createQwikRouter$1 } from "@qwik.dev/router/middleware/node";
3
- /** @public */ function createQwikRouter(opts) {
3
+ /** @public */
4
+ function createQwikRouter(opts) {
4
5
  try {
5
6
  const { router, staticFile } = createQwikRouter$1({
6
7
  render: opts.render,
@@ -25,7 +26,8 @@ import { createQwikRouter as createQwikRouter$1 } from "@qwik.dev/router/middlew
25
26
  router(req, res, () => {});
26
27
  });
27
28
  };
28
- /** @deprecated `router` handles 404 responses. Will be removed in V3. */ const notFound = async (_req, _res, next) => {
29
+ /** @deprecated `router` handles 404 responses. Will be removed in V3. */
30
+ const notFound = async (_req, _res, next) => {
29
31
  next();
30
32
  };
31
33
  return {
@@ -42,5 +44,6 @@ import { createQwikRouter as createQwikRouter$1 } from "@qwik.dev/router/middlew
42
44
  /**
43
45
  * @deprecated Use `createQwikRouter` instead. Will be removed in V3
44
46
  * @public
45
- */ var createQwikCity = createQwikRouter;
47
+ */
48
+ var createQwikCity = createQwikRouter;
46
49
  export { createQwikCity, createQwikRouter };
@@ -1,7 +1,8 @@
1
1
  import { setServerPlatform } from "@qwik.dev/core/server";
2
2
  import { getErrorHtml, isStaticPath, requestHandler } from "@qwik.dev/router/middleware/request-handler";
3
3
  import { parseString } from "set-cookie-parser";
4
- /** @public */ function createQwikRouter(opts) {
4
+ /** @public */
5
+ function createQwikRouter(opts) {
5
6
  if (opts.manifest) setServerPlatform(opts.manifest);
6
7
  async function onAzureSwaRequest(context, req) {
7
8
  try {
@@ -83,5 +84,6 @@ import { parseString } from "set-cookie-parser";
83
84
  /**
84
85
  * @deprecated Use `createQwikRouter` instead. Will be removed in V3
85
86
  * @public
86
- */ var createQwikCity = createQwikRouter;
87
+ */
88
+ var createQwikCity = createQwikRouter;
87
89
  export { createQwikCity, createQwikRouter };
@@ -1,4 +1,4 @@
1
- import { n as MIME_TYPES, t as normalizeRequestUrl } from "../../chunks/url.mjs";
1
+ import { n as normalizeRequestUrl, r as MIME_TYPES, t as getStaticFilePath } from "../../chunks/static-file.mjs";
2
2
  import { isDev } from "@qwik.dev/core/build";
3
3
  import { setServerPlatform } from "@qwik.dev/core/server";
4
4
  import { extname, join } from "node:path";
@@ -9,7 +9,8 @@ function getRequestUrl(request, opts) {
9
9
  if (!origin) return url;
10
10
  return normalizeRequestUrl(`${url.pathname}${url.search}${url.hash}`, origin);
11
11
  }
12
- /** @public */ function createQwikRouter(opts) {
12
+ /** @public */
13
+ function createQwikRouter(opts) {
13
14
  globalThis.TextEncoderStream ||= _TextEncoderStream_polyfill;
14
15
  if (opts.manifest) setServerPlatform(opts.manifest);
15
16
  const staticFolder = opts.static?.root ?? join(Bun.fileURLToPath(import.meta.url), "..", "..", "dist");
@@ -61,16 +62,12 @@ function getRequestUrl(request, opts) {
61
62
  });
62
63
  }
63
64
  }
64
- /** @deprecated `router` handles 404 responses. Will be removed in V3. */ const notFound = async (_request) => {
65
+ /** @deprecated `router` handles 404 responses. Will be removed in V3. */
66
+ const notFound = async (_request) => {
65
67
  return null;
66
68
  };
67
- const openStaticFile = async (url) => {
68
- const pathname = url.pathname;
69
- const fileName = pathname.slice(url.pathname.lastIndexOf("/"));
70
- let filePath;
71
- if (fileName.includes(".")) filePath = join(staticFolder, pathname);
72
- else if (!globalThis.__NO_TRAILING_SLASH__) filePath = join(staticFolder, pathname + "index.html");
73
- else filePath = join(staticFolder, pathname, "index.html");
69
+ const openStaticFile = async (staticFilePath) => {
70
+ const filePath = join(staticFolder, staticFilePath);
74
71
  return {
75
72
  filePath,
76
73
  content: Bun.file(filePath)
@@ -80,7 +77,9 @@ function getRequestUrl(request, opts) {
80
77
  try {
81
78
  const url = getRequestUrl(request, opts);
82
79
  if (isStaticPath(request.method || "GET", url)) {
83
- const { filePath, content } = await openStaticFile(url);
80
+ const staticFilePath = getStaticFilePath(url.pathname);
81
+ if (staticFilePath === void 0) return null;
82
+ const { filePath, content } = await openStaticFile(staticFilePath);
84
83
  if (!await content.exists()) return new Response("Not Found", {
85
84
  status: 404,
86
85
  headers: {
@@ -118,5 +117,6 @@ function getRequestUrl(request, opts) {
118
117
  /**
119
118
  * @deprecated Use `createQwikRouter` instead. Will be removed in V3
120
119
  * @public
121
- */ var createQwikCity = createQwikRouter;
120
+ */
121
+ var createQwikCity = createQwikRouter;
122
122
  export { createQwikCity, createQwikRouter };
@@ -1,7 +1,8 @@
1
1
  import { isDev } from "@qwik.dev/core/build";
2
2
  import { setServerPlatform } from "@qwik.dev/core/server";
3
3
  import { _TextEncoderStream_polyfill, getErrorHtml, isStaticPath, mergeHeadersCookies, requestHandler } from "@qwik.dev/router/middleware/request-handler";
4
- /** @public */ function createQwikRouter(opts) {
4
+ /** @public */
5
+ function createQwikRouter(opts) {
5
6
  try {
6
7
  new globalThis.TextEncoderStream();
7
8
  } catch {
@@ -12,7 +13,7 @@ import { _TextEncoderStream_polyfill, getErrorHtml, isStaticPath, mergeHeadersCo
12
13
  try {
13
14
  const url = new URL(request.url);
14
15
  if (isStaticPath(request.method, url)) return env.ASSETS.fetch(request);
15
- const useCache = url.hostname !== "127.0.0.1" && url.hostname !== "localhost" && url.port === "" && request.method === "GET";
16
+ const useCache = url.hostname !== "127.0.0.1" && url.hostname !== "localhost" && url.port === "" && request.method === "GET" && !request.headers.has("Cookie") && !request.headers.has("Authorization");
16
17
  const cacheKey = new Request(url.href, request);
17
18
  const cache = useCache ? await caches.open("custom:qwikrouter") : null;
18
19
  if (cache) {
@@ -58,7 +59,7 @@ import { _TextEncoderStream_polyfill, getErrorHtml, isStaticPath, mergeHeadersCo
58
59
  });
59
60
  const response = await handledResponse.response;
60
61
  if (response) {
61
- if (response.ok && cache && response.headers.has("Cache-Control")) ctx.waitUntil(cache.put(cacheKey, response.clone()));
62
+ if (response.ok && cache && isSharedCacheable(response)) ctx.waitUntil(cache.put(cacheKey, response.clone()));
62
63
  return response;
63
64
  }
64
65
  }
@@ -83,8 +84,22 @@ import { _TextEncoderStream_polyfill, getErrorHtml, isStaticPath, mergeHeadersCo
83
84
  }
84
85
  return onCloudflarePagesFetch;
85
86
  }
87
+ var uncacheableDirectives = /* @__PURE__ */ new Set([
88
+ "private",
89
+ "no-store",
90
+ "no-cache"
91
+ ]);
92
+ var isSharedCacheable = (response) => {
93
+ const cacheControl = response.headers.get("Cache-Control");
94
+ if (!cacheControl || response.headers.has("Set-Cookie")) return false;
95
+ return !cacheControl.split(",").some((value) => {
96
+ const directive = value.trim().split("=", 1)[0].toLowerCase();
97
+ return uncacheableDirectives.has(directive);
98
+ });
99
+ };
86
100
  /**
87
101
  * @deprecated Use `createQwikRouter` instead. Will be removed in V3
88
102
  * @public
89
- */ var createQwikCity = createQwikRouter;
103
+ */
104
+ var createQwikCity = createQwikRouter;
90
105
  export { createQwikCity, createQwikRouter };
@@ -1,4 +1,4 @@
1
- import { n as MIME_TYPES, t as normalizeRequestUrl } from "../../chunks/url.mjs";
1
+ import { n as normalizeRequestUrl, r as MIME_TYPES, t as getStaticFilePath } from "../../chunks/static-file.mjs";
2
2
  import { r as limitRequestBody } from "../../chunks/request-body-limit.mjs";
3
3
  import { isDev } from "@qwik.dev/core/build";
4
4
  import { setServerPlatform } from "@qwik.dev/core/server";
@@ -10,7 +10,8 @@ function getRequestUrl(request, opts, info) {
10
10
  if (!origin) return url;
11
11
  return normalizeRequestUrl(`${url.pathname}${url.search}${url.hash}`, origin);
12
12
  }
13
- /** @public */ function createQwikRouter(opts) {
13
+ /** @public */
14
+ function createQwikRouter(opts) {
14
15
  if (opts.manifest) setServerPlatform(opts.manifest);
15
16
  const staticFolder = opts.static?.root ?? join(fromFileUrl(import.meta.url), "..", "..", "dist");
16
17
  async function router(request, info) {
@@ -54,16 +55,12 @@ function getRequestUrl(request, opts, info) {
54
55
  });
55
56
  }
56
57
  }
57
- /** @deprecated `router` handles 404 responses. Will be removed in V3. */ const notFound = async (_request) => {
58
+ /** @deprecated `router` handles 404 responses. Will be removed in V3. */
59
+ const notFound = async (_request) => {
58
60
  return null;
59
61
  };
60
- const openStaticFile = async (url) => {
61
- const pathname = url.pathname;
62
- const fileName = pathname.slice(url.pathname.lastIndexOf("/"));
63
- let filePath;
64
- if (fileName.includes(".")) filePath = join(staticFolder, pathname);
65
- else if (!globalThis.__NO_TRAILING_SLASH__) filePath = join(staticFolder, pathname + "index.html");
66
- else filePath = join(staticFolder, pathname, "index.html");
62
+ const openStaticFile = async (staticFilePath) => {
63
+ const filePath = join(staticFolder, staticFilePath);
67
64
  return {
68
65
  filePath,
69
66
  content: await Deno.open(filePath, { read: true })
@@ -73,7 +70,9 @@ function getRequestUrl(request, opts, info) {
73
70
  try {
74
71
  const url = getRequestUrl(request, opts);
75
72
  if (isStaticPath(request.method || "GET", url)) {
76
- const { filePath, content } = await openStaticFile(url);
73
+ const staticFilePath = getStaticFilePath(url.pathname);
74
+ if (staticFilePath === void 0) return null;
75
+ const { filePath, content } = await openStaticFile(staticFilePath);
77
76
  const ext = extname(filePath).replace(/^\./, "");
78
77
  return new Response(content.readable, {
79
78
  status: 200,
@@ -104,5 +103,6 @@ function getRequestUrl(request, opts, info) {
104
103
  /**
105
104
  * @deprecated Use `createQwikRouter` instead. Will be removed in V3
106
105
  * @public
107
- */ var createQwikCity = createQwikRouter;
106
+ */
107
+ var createQwikCity = createQwikRouter;
108
108
  export { createQwikCity, createQwikRouter };
@@ -1,5 +1,6 @@
1
1
  import { createQwikRouter as createQwikRouter$1 } from "@qwik.dev/router/middleware/node";
2
- /** @public */ function createQwikRouter(opts) {
2
+ /** @public */
3
+ function createQwikRouter(opts) {
3
4
  const { staticFile, router } = createQwikRouter$1({
4
5
  render: opts.render,
5
6
  manifest: opts.manifest,
@@ -19,5 +20,6 @@ import { createQwikRouter as createQwikRouter$1 } from "@qwik.dev/router/middlew
19
20
  /**
20
21
  * @deprecated Use `createQwikRouter` instead. Will be removed in V3
21
22
  * @public
22
- */ var createQwikCity = createQwikRouter;
23
+ */
24
+ var createQwikCity = createQwikRouter;
23
25
  export { createQwikCity, createQwikRouter };
@@ -1,7 +1,8 @@
1
1
  import { isDev } from "@qwik.dev/core/build";
2
2
  import { setServerPlatform } from "@qwik.dev/core/server";
3
3
  import { getErrorHtml, isStaticPath, mergeHeadersCookies, requestHandler } from "@qwik.dev/router/middleware/request-handler";
4
- /** @public */ function createQwikRouter(opts) {
4
+ /** @public */
5
+ function createQwikRouter(opts) {
5
6
  if (opts.manifest) setServerPlatform(opts.manifest);
6
7
  async function onNetlifyEdgeRequest(request, context) {
7
8
  try {
@@ -61,5 +62,6 @@ import { getErrorHtml, isStaticPath, mergeHeadersCookies, requestHandler } from
61
62
  /**
62
63
  * @deprecated Use `createQwikRouter` instead. Will be removed in V3
63
64
  * @public
64
- */ var createQwikCity = createQwikRouter;
65
+ */
66
+ var createQwikCity = createQwikRouter;
65
67
  export { createQwikCity, createQwikRouter };
@@ -1,20 +1,23 @@
1
- import { n as MIME_TYPES, t as normalizeRequestUrl } from "../../chunks/url.mjs";
1
+ import { n as normalizeRequestUrl, r as MIME_TYPES, t as getStaticFilePath } from "../../chunks/static-file.mjs";
2
2
  import { i as validateRequestBodyLimit, n as RequestBodyLimitError, t as DEFAULT_REQUEST_BODY_LIMIT } from "../../chunks/request-body-limit.mjs";
3
3
  import { t as devPreloadedRouteLoaders } from "../../chunks/dev-preloaded-route-loader.mjs";
4
4
  import { isDev } from "@qwik.dev/core";
5
5
  import { setServerPlatform } from "@qwik.dev/core/server";
6
6
  import { createReadStream } from "node:fs";
7
- import { basename, extname, join } from "node:path";
7
+ import { extname, join } from "node:path";
8
8
  import { isStaticPath, requestHandler } from "@qwik.dev/router/middleware/request-handler";
9
9
  import { fileURLToPath } from "node:url";
10
10
  import { Http2ServerRequest } from "node:http2";
11
+ function computeOrigin(req, opts) {
12
+ return opts?.getOrigin?.(req) ?? opts?.origin ?? process.env.ORIGIN ?? fallbackOrigin(req);
13
+ }
11
14
  function fallbackOrigin(req) {
12
15
  const { PROTOCOL_HEADER, HOST_HEADER } = process.env;
13
16
  const headers = req.headers;
14
17
  return `${PROTOCOL_HEADER && headers[PROTOCOL_HEADER] || (req.socket.encrypted || req.connection.encrypted ? "https" : "http")}://${headers[HOST_HEADER ?? (req instanceof Http2ServerRequest ? ":authority" : "host")]}`;
15
18
  }
16
- function computeOrigin(req, opts) {
17
- return opts?.getOrigin?.(req) ?? opts?.origin ?? process.env.ORIGIN ?? fallbackOrigin(req);
19
+ function getUrl(req, origin) {
20
+ return normalizeUrl(req.originalUrl || req.url || "/", origin);
18
21
  }
19
22
  function isClientAbortWriteError(error) {
20
23
  if (!(error instanceof Error)) return false;
@@ -26,9 +29,6 @@ var invalidHeadersPattern = /^:(method|scheme|authority|path)$/i;
26
29
  function normalizeUrl(url, base) {
27
30
  return normalizeRequestUrl(url, base);
28
31
  }
29
- function getUrl(req, origin) {
30
- return normalizeUrl(req.originalUrl || req.url || "/", origin);
31
- }
32
32
  function createNodeResponseSink(res) {
33
33
  let closed = res.closed || res.destroyed;
34
34
  let responseError;
@@ -243,7 +243,8 @@ async function fromNodeHttp(url, req, res, mode, getClientConn, requestBodyLimit
243
243
  locale: void 0
244
244
  };
245
245
  }
246
- /** @public */ function createQwikRouter(opts) {
246
+ /** @public */
247
+ function createQwikRouter(opts) {
247
248
  if (opts.manifest) setServerPlatform(opts.manifest);
248
249
  const staticFolder = opts.static?.root ?? join(fileURLToPath(import.meta.url), "..", "..", "dist");
249
250
  const router = async (req, res, next, limits = {}) => {
@@ -275,16 +276,15 @@ async function fromNodeHttp(url, req, res, mode, getClientConn, requestBodyLimit
275
276
  next(e);
276
277
  }
277
278
  };
278
- /** @deprecated `router` handles 404 responses. Will be removed in V3. */ const notFound = async (_req, _res, next) => next();
279
+ /** @deprecated `router` handles 404 responses. Will be removed in V3. */
280
+ const notFound = async (_req, _res, next) => next();
279
281
  const staticFile = async (req, res, next) => {
280
282
  try {
281
283
  const url = getUrl(req, computeOrigin(req, opts));
282
284
  if (isStaticPath(req.method || "GET", url)) {
283
- const pathname = url.pathname;
284
- let filePath;
285
- if (basename(pathname).includes(".")) filePath = join(staticFolder, pathname);
286
- else if (!globalThis.__NO_TRAILING_SLASH__) filePath = join(staticFolder, pathname + "index.html");
287
- else filePath = join(staticFolder, pathname, "index.html");
285
+ const staticFilePath = getStaticFilePath(url.pathname);
286
+ if (staticFilePath === void 0) return next();
287
+ const filePath = join(staticFolder, staticFilePath);
288
288
  const ext = extname(filePath).replace(/^\./, "");
289
289
  const stream = createReadStream(filePath);
290
290
  stream.on("error", next);
@@ -309,5 +309,6 @@ async function fromNodeHttp(url, req, res, mode, getClientConn, requestBodyLimit
309
309
  /**
310
310
  * @deprecated Use `createQwikRouter` instead. Will be removed in V3
311
311
  * @public
312
- */ var createQwikCity = createQwikRouter;
312
+ */
313
+ var createQwikCity = createQwikRouter;
313
314
  export { createQwikCity, createQwikRouter };
@@ -18,8 +18,13 @@ export declare class AbortMessage {
18
18
  readonly __controlFlow: true;
19
19
  }
20
20
 
21
- /** @internal */
22
- export declare let _asyncRequestStore: AsyncLocalStorage<RequestEventInternal> | undefined;
21
+ /**
22
+ * Shared with the router copy of a Qwik library kept external on the server, so its
23
+ * `getRequestEvent()` and `server$` see the request the app is serving.
24
+ *
25
+ * @internal
26
+ */
27
+ export declare const _asyncRequestStore: AsyncLocalStorage<RequestEventInternal> | undefined;
23
28
 
24
29
  /** @public */
25
30
  export declare type CacheControl = CacheControlOptions | number | 'day' | 'week' | 'month' | 'year' | 'no-cache' | 'immutable' | 'private';
@@ -399,8 +404,16 @@ declare interface LoadedRoute {
399
404
  $loaders$?: string[];
400
405
  /** Runtime-only mapping of routeLoader$ hashes to the matched pathname used for q-loader fetches */
401
406
  $loaderPaths$?: Record<string, string>;
407
+ /** Runtime-only mapping of routeLoader$ hashes to params matched at their loader path */
408
+ $loaderParams$?: Record<string, PathParams>;
409
+ }
410
+
411
+ declare interface MenuModule {
412
+ readonly default: ContentMenu;
402
413
  }
403
414
 
415
+ declare type MenuModuleLoader = () => Promise<MenuModule>;
416
+
404
417
  /** @public */
405
418
  export declare const mergeHeadersCookies: (headers: Headers, cookies: Cookie) => Headers;
406
419
 
@@ -422,6 +435,25 @@ declare type PathParams = Record<string, string>;
422
435
 
423
436
  declare type QwikRouterCompletion = Error | undefined | object;
424
437
 
438
+ /** @public */
439
+ declare interface QwikRouterConfig {
440
+ readonly routes: RouteData;
441
+ readonly serverPlugins?: RouteModule[];
442
+ readonly basePathname?: string;
443
+ readonly trailingSlash?: boolean;
444
+ readonly cacheModules?: boolean;
445
+ /** When true, return null instead of rendering the 404 page, letting the adapter handle it */
446
+ readonly fallthrough?: boolean;
447
+ /**
448
+ * Imports the modules containing `server$` functions so their registration side effects run.
449
+ * Called by the request handler before serving; deliberately async so the config module itself
450
+ * evaluates without touching the runtime (see the import-cycle notes in `route-loaders.ts`).
451
+ */
452
+ readonly importEagerModules?: () => Promise<unknown>;
453
+ /** URL of the app's service worker, when the routes define one. */
454
+ readonly serviceWorkerUrl?: string;
455
+ }
456
+
425
457
  declare interface QwikRouterRun<T> {
426
458
  response: Promise<T | null>;
427
459
  requestEv: RequestEvent_2;
@@ -632,6 +664,7 @@ export declare interface RequestEventCommon<PLATFORM = QwikRouterPlatform> exten
632
664
  declare interface RequestEventInternal extends Readonly<RequestEvent>, Readonly<RequestEventLoader> {
633
665
  readonly [RequestEvMode]: ServerRequestMode;
634
666
  readonly [RequestEvRoute]: LoadedRoute;
667
+ readonly [RequestEvRouterConfig]: QwikRouterConfig | undefined;
635
668
  /**
636
669
  * Check if this request is already written to.
637
670
  *
@@ -658,6 +691,9 @@ declare const RequestEvMode: unique symbol;
658
691
 
659
692
  declare const RequestEvRoute: unique symbol;
660
693
 
694
+ /** The request event's key for the config of the app serving it. */
695
+ declare const RequestEvRouterConfig: unique symbol;
696
+
661
697
  /** @public */
662
698
  export declare type RequestHandler<PLATFORM = QwikRouterPlatform> = (ev: RequestEvent<PLATFORM>) => Promise<void | AbortMessage | ServerError> | void | AbortMessage | ServerError;
663
699
 
@@ -714,6 +750,60 @@ declare interface RouteConfigValue {
714
750
  readonly cacheKey?: CacheKeyFn;
715
751
  }
716
752
 
753
+ /**
754
+ * A nested route trie structure. The root represents `/` and each level represents a URL segment.
755
+ *
756
+ * Keys starting with a single `_` are metadata; all other keys are child route segments. A static
757
+ * segment that starts with `_` is stored with an extra `_` (`_drafts` → `__drafts`).
758
+ *
759
+ * - Use `_W` as the key for a single dynamic segment (param); `_P` on that node names the param.
760
+ * - Use `_A` as the key for a rest/catch-all segment; `_P` on that node names the param.
761
+ * - For infix params like `pre[slug]post`, use `_W` with `_0` (prefix) and `_9` (suffix).
762
+ * - Use `_M` for an array of group (pathless layout) nodes, sorted by group name.
763
+ *
764
+ * When matching, exact segments are tried first (case-insensitive), then `_W` (with optional
765
+ * prefix/suffix), then `_A`. When no route matches, the closest `_E` (error.tsx) or `_4` (404.tsx)
766
+ * loader in the ancestor chain is used to render the error page.
767
+ *
768
+ * @public
769
+ */
770
+ declare interface RouteData {
771
+ /** This node's layout loader (single). Runtime accumulates these during trie traversal. */
772
+ _L?: ContentModuleLoader;
773
+ /**
774
+ * This node's index/page loader. Single = normal (runtime prepends gathered _L). Array = override
775
+ * (layout stop / named layout — IS the complete chain).
776
+ */
777
+ _I?: ContentModuleLoader | ModuleLoader[];
778
+ /** Rewrite/goto target path. Matcher re-walks trie from root using this path's keys. */
779
+ _G?: string;
780
+ /** The JS bundle names for this route (SSR only) */
781
+ _B?: string[];
782
+ /**
783
+ * Not-found (404) boundary: single loader (runtime prepends layouts) or override chain
784
+ * (`404@layout`/`!`).
785
+ */
786
+ _4?: ContentModuleLoader | ModuleLoader[];
787
+ /** Error (error.tsx) boundary, same single-or-override-chain shape as `_4`. */
788
+ _E?: ContentModuleLoader | ModuleLoader[];
789
+ /** The parameter name when this node is reached via `_W` or `_A` from the parent */
790
+ _P?: string;
791
+ /** Prefix for infix params (e.g. "pre" for `pre[slug]post`) — only on `_W` nodes */
792
+ _0?: string;
793
+ /** Suffix for infix params (e.g. "post" for `pre[slug]post`) — only on `_W` nodes */
794
+ _9?: string;
795
+ /** Group (pathless layout) nodes merged into this level, sorted by group name */
796
+ _M?: RouteData[];
797
+ /** Menu loader for this subtree (from menu.md). Runtime uses nearest ancestor during traversal. */
798
+ _N?: MenuModuleLoader;
799
+ /** Inherited layout and server plugin routeLoader$ hashes. */
800
+ _R?: string[];
801
+ /** Page loader hashes; override pages include their selected layouts and plugins. */
802
+ _D?: string[];
803
+ /** Child route segments (any key not starting with a single `_`) */
804
+ [part: string]: RouteData | RouteData[] | ModuleLoader[] | ContentModuleLoader | MenuModuleLoader | string[] | string | undefined;
805
+ }
806
+
717
807
  /** @public */
718
808
  declare interface RouteLocation {
719
809
  readonly params: Readonly<Record<string, string>>;
@@ -766,6 +856,16 @@ export declare interface ServerRenderOptions extends RenderOptions {
766
856
  * Defaults to `true`.
767
857
  */
768
858
  checkOrigin?: boolean | 'lax-proto';
859
+ /**
860
+ * Use the `X-Forwarded-Host` and `X-Forwarded-Proto` request headers to compute the URL exposed
861
+ * by `useLocation()` during SSR.
862
+ *
863
+ * Only enable this when the server is behind a proxy that overwrites these headers, otherwise
864
+ * clients can spoof the URL.
865
+ *
866
+ * Defaults to `false`.
867
+ */
868
+ trustForwardedHeaders?: boolean;
769
869
  }
770
870
 
771
871
  /**
@@ -1,47 +1,22 @@
1
- import { Y as _asyncRequestStore, d as loadRoute } from "../../chunks/head.qwik.mjs";
1
+ import { H as getRouterConfig, d as loadRoute, ot as _asyncRequestStore } from "../../chunks/head.qwik.mjs";
2
2
  import { c as resolveValidInternalFullPathname, l as trimInternalPathname, s as recognizeRequest, u as trimRecognizedInternalPathname } from "../../chunks/request-path.mjs";
3
3
  import { i as RedirectMessage, r as AbortMessage, t as ServerError } from "../../chunks/server-error.mjs";
4
4
  import { t as devPreloadedRouteLoaders } from "../../chunks/dev-preloaded-route-loader.mjs";
5
5
  import { a as getStaticPathRedirect, c as clearSsrCache, d as mergeHeadersCookies, i as resolveRequestHandlers, n as getErrorHtml, o as isStaticPath, r as renderQwikMiddleware, s as clearLoaderCache, t as runQwikRouter, u as RewriteMessage } from "../../chunks/user-response.mjs";
6
6
  import { isDev } from "@qwik.dev/core";
7
- var qwikRouterConfig;
8
- async function getConfig() {
9
- if (isDev) return await import("@qwik-router-config");
10
- if (!qwikRouterConfig) qwikRouterConfig = await import("@qwik-router-config");
11
- return qwikRouterConfig;
12
- }
13
- function getRequestHandlerPathname(serverRequestEv, isDevRequest = isDev) {
14
- const recognized = recognizeRequest(serverRequestEv.url.pathname);
15
- if (!recognized) return serverRequestEv.url.pathname;
16
- const loaderPathname = trimRecognizedInternalPathname(serverRequestEv.url.pathname, recognized);
17
- if (recognized.type !== "@isQLoader") return loaderPathname;
18
- const routePath = serverRequestEv.request.headers.get("X-Qwik-fullpath") ?? (isDevRequest ? serverRequestEv.request.headers.get("X-Qwik-route-path") : null);
19
- return resolveValidInternalFullPathname(loaderPathname, routePath) ?? loaderPathname;
20
- }
21
- async function loadRequestHandlers(qwikRouterConfig, pathname, method, checkOrigin, renderFn, serverRequestEv) {
22
- const { routes, serverPlugins, cacheModules } = qwikRouterConfig;
23
- const loadedRoute = await loadRoute(routes, cacheModules, pathname);
24
- const loader = isDev ? devPreloadedRouteLoaders.get(serverRequestEv.request) : void 0;
25
- const lastModule = loadedRoute.$mods$[loadedRoute.$mods$.length - 1];
26
- if (loader && lastModule) loadedRoute.$mods$[loadedRoute.$mods$.length - 1] = Object.assign({}, lastModule, { __preloadedRouteLoader: loader });
27
- return {
28
- loadedRoute,
29
- requestHandlers: resolveRequestHandlers(serverPlugins, loadedRoute, method, checkOrigin, renderQwikMiddleware(renderFn))
30
- };
31
- }
32
7
  /**
33
8
  * The request handler for QwikRouter. Called by every adapter.
34
9
  *
35
10
  * @public
36
- */ async function requestHandler(serverRequestEv, opts) {
37
- const { render, checkOrigin } = opts;
38
- const config = await getConfig();
11
+ */
12
+ async function requestHandler(serverRequestEv, opts) {
13
+ const config = await getRouterConfig();
39
14
  const pathname = getRequestHandlerPathname(serverRequestEv);
40
15
  if (pathname === "/.well-known" || pathname.startsWith("/.well-known/")) return null;
41
16
  const staticPathRedirect = getStaticPathRedirect(serverRequestEv.request.method, serverRequestEv.url);
42
17
  const rebuildRouteInfo = async (url) => {
43
18
  const cleanPathname = trimInternalPathname(url.pathname);
44
- return loadRequestHandlers(config, cleanPathname, serverRequestEv.request.method, checkOrigin ?? true, render, serverRequestEv);
19
+ return loadRequestHandlers(config, cleanPathname, serverRequestEv.request.method, opts, serverRequestEv);
45
20
  };
46
21
  if (staticPathRedirect) return runQwikRouter(serverRequestEv, {
47
22
  $routeName$: pathname,
@@ -49,16 +24,37 @@ async function loadRequestHandlers(qwikRouterConfig, pathname, method, checkOrig
49
24
  $mods$: []
50
25
  }, [(event) => {
51
26
  throw event.redirect(301, staticPathRedirect);
52
- }], rebuildRouteInfo, config.basePathname);
53
- const { loadedRoute, requestHandlers } = await loadRequestHandlers(config, pathname, serverRequestEv.request.method, checkOrigin ?? true, render, serverRequestEv);
27
+ }], rebuildRouteInfo, config.basePathname, config);
28
+ const { loadedRoute, requestHandlers } = await loadRequestHandlers(config, pathname, serverRequestEv.request.method, opts, serverRequestEv);
54
29
  if (config.fallthrough && loadedRoute.$notFound$) return null;
55
- return runQwikRouter(serverRequestEv, loadedRoute, requestHandlers, rebuildRouteInfo, config.basePathname);
30
+ return runQwikRouter(serverRequestEv, loadedRoute, requestHandlers, rebuildRouteInfo, config.basePathname, config);
31
+ }
32
+ function getRequestHandlerPathname(serverRequestEv, isDevRequest = isDev) {
33
+ const recognized = recognizeRequest(serverRequestEv.url.pathname);
34
+ if (!recognized) return serverRequestEv.url.pathname;
35
+ const loaderPathname = trimRecognizedInternalPathname(serverRequestEv.url.pathname, recognized);
36
+ if (recognized.type !== "@isQLoader") return loaderPathname;
37
+ const routePath = serverRequestEv.request.headers.get("X-Qwik-fullpath") ?? (isDevRequest ? serverRequestEv.request.headers.get("X-Qwik-route-path") : null);
38
+ return resolveValidInternalFullPathname(loaderPathname, routePath) ?? loaderPathname;
39
+ }
40
+ async function loadRequestHandlers(qwikRouterConfig, pathname, method, opts, serverRequestEv) {
41
+ const { routes, serverPlugins, cacheModules } = qwikRouterConfig;
42
+ const loadedRoute = await loadRoute(routes, cacheModules, pathname);
43
+ const loader = isDev ? devPreloadedRouteLoaders.get(serverRequestEv.request) : void 0;
44
+ const lastModule = loadedRoute.$mods$[loadedRoute.$mods$.length - 1];
45
+ if (loader && lastModule) loadedRoute.$mods$[loadedRoute.$mods$.length - 1] = Object.assign({}, lastModule, { __preloadedRouteLoader: loader });
46
+ return {
47
+ loadedRoute,
48
+ requestHandlers: resolveRequestHandlers(serverPlugins, loadedRoute, method, opts.checkOrigin ?? true, renderQwikMiddleware(opts.render, opts.trustForwardedHeaders))
49
+ };
56
50
  }
57
51
  /**
58
52
  * TextEncoderStream polyfill based on Node.js' implementation
59
53
  * https://github.com/nodejs/node/blob/3f3226c8e363a5f06c1e6a37abd59b6b8c1923f1/lib/internal/webstreams/encoding.js#L38-L119
60
54
  * (MIT License)
61
- */ /** @internal */ var _TextEncoderStream_polyfill = class {
55
+ */
56
+ /** @internal */
57
+ var _TextEncoderStream_polyfill = class {
62
58
  #pendingHighSurrogate = null;
63
59
  #handle = new TextEncoder();
64
60
  #transform = new TransformStream({
@@ -7,7 +7,8 @@ var VERCEL_COOKIE = "__vdpl";
7
7
  var VERCEL_SKEW_PROTECTION_ENABLED = "VERCEL_SKEW_PROTECTION_ENABLED";
8
8
  var VERCEL_DEPLOYMENT_ID = "VERCEL_DEPLOYMENT_ID";
9
9
  var BASE_URL = "BASE_URL";
10
- /** @public */ function createQwikRouter(opts) {
10
+ /** @public */
11
+ function createQwikRouter(opts) {
11
12
  if (opts.manifest) setServerPlatform(opts.manifest);
12
13
  async function onVercelEdgeRequest(request) {
13
14
  try {
@@ -80,5 +81,6 @@ var BASE_URL = "BASE_URL";
80
81
  /**
81
82
  * @deprecated Use `createQwikRouter` instead. Will be removed in V3
82
83
  * @public
83
- */ var createQwikCity = createQwikRouter;
84
+ */
85
+ var createQwikCity = createQwikRouter;
84
86
  export { createQwikCity, createQwikRouter };
@@ -9,5 +9,6 @@
9
9
  * If you do have custom service worker logic, you should keep the `service-worker.ts` file and
10
10
  * `<ServiceWorkerRegister />` component, but remove the `setupServiceWorker()` call.
11
11
  * @public
12
- */ var setupServiceWorker = () => {};
12
+ */
13
+ var setupServiceWorker = () => {};
13
14
  export { setupServiceWorker };