@qwik.dev/router 2.0.0-beta.45 → 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.
@@ -1,18 +1,26 @@
1
1
  import { t as ensureSlash } from "./pathname.mjs";
2
2
  import { o as getLoaderName } from "./request-path.mjs";
3
3
  import { i as RedirectMessage, n as throwIfControlFlowSignal, t as ServerError } from "./server-error.mjs";
4
- import { _captures, createComputedQrl, createContextId, implicit$FirstArg, inlinedQrl, isDev, isServer, untrack, useServerData, withLocale } from "@qwik.dev/core";
5
- import { SerializerSymbol, _UNINITIALIZED, _deserialize, _getAsyncLocalStorage, _injectAsyncSignalValue, _markSignalAsExternallyOwned, _resolveContextWithoutSequentialScope, _verifySerializable } from "@qwik.dev/core/internal";
4
+ import { r as unescapeStaticTrieKey, t as escapeStaticTrieKey } from "./route-trie-key.mjs";
5
+ import { _capturesObj, createComputedQrl, createContextId, implicit$FirstArg, inlinedQrl, isBrowser, isDev, isServer, untrack, useServerData, withLocale } from "@qwik.dev/core";
6
+ import { SerializerSymbol, _UNINITIALIZED, _deserialize, _getAsyncLocalStorage, _injectAsyncSignalValue, _markSignalAsExternallyOwned, _qwikSymbol, _registerSingleton, _resolveContextWithoutSequentialScope, _verifySerializable } from "@qwik.dev/core/internal";
6
7
  import { isServer as isServer$1 } from "@qwik.dev/core/build";
7
- globalThis.__NO_TRAILING_SLASH__;
8
- var basePathname = globalThis.__QWIK_ROUTER_BASE_PATHNAME__ ?? "/";
9
- /** @internal */
10
- var _asyncRequestStore;
11
- if (isServer$1) {
8
+ var createRequestStore = () => {
12
9
  const AsyncLocalStorage = _getAsyncLocalStorage();
13
- if (AsyncLocalStorage) _asyncRequestStore = new AsyncLocalStorage();
14
- else console.warn("\n=====================\n Qwik Router Warning:\n AsyncLocalStorage is not available, continuing without it.\n This impacts concurrent async server calls, where they lose access to the ServerRequestEv object.\n=====================\n\n");
15
- }
10
+ if (AsyncLocalStorage) return new AsyncLocalStorage();
11
+ console.warn("\n=====================\n Qwik Router Warning:\n AsyncLocalStorage is not available, continuing without it.\n This impacts concurrent async server calls, where they lose access to the ServerRequestEv object.\n=====================\n\n");
12
+ };
13
+ /**
14
+ * Shared with the router copy of a Qwik library kept external on the server, so its
15
+ * `getRequestEvent()` and `server$` see the request the app is serving.
16
+ *
17
+ * @internal
18
+ */
19
+ var _asyncRequestStore = isServer$1 ? _registerSingleton("routerRequestStore", createRequestStore) : void 0;
20
+ /** The request event's key for the config of the app serving it. */
21
+ var RequestEvRouterConfig = /*#__PURE__*/ _qwikSymbol("router.config");
22
+ /** The config of the app serving the current request, for a router copy that holds none. */
23
+ var getServingRouterConfig = () => _asyncRequestStore?.getStore()?.[RequestEvRouterConfig];
16
24
  var QACTION_KEY = "qaction";
17
25
  var QFN_KEY = "qfunc";
18
26
  var QDATA_KEY = "qdata";
@@ -31,6 +39,67 @@ var RouteNavigateContext = /*#__PURE__*/ createContextId("qr-n");
31
39
  var RouteActionContext = /*#__PURE__*/ createContextId("qr-a");
32
40
  var RoutePreventNavigateContext = /*#__PURE__*/ createContextId("qr-p");
33
41
  var HttpStatusContext = /*#__PURE__*/ createContextId("qr-hs");
42
+ var holder = {};
43
+ /** The config this router copy holds, else the one of the app serving the current request. */
44
+ var findRouterConfig = () => holder.config ?? getServingRouterConfig();
45
+ /**
46
+ * Registers the app's route config with the router copy bundled into the app. The generated
47
+ * `@qwik-router-config` module calls this on the server; the client loads that module lazily
48
+ * instead, since it is a separate chunk.
49
+ *
50
+ * @internal
51
+ */
52
+ var _setRouterConfig = (config) => {
53
+ holder.config = config;
54
+ holder.ready = void 0;
55
+ };
56
+ /**
57
+ * The app's route config: the route trie, server plugins and build options that the Qwik Router
58
+ * Vite plugin generated. Resolves once the config is loaded and its `server$` modules are
59
+ * registered. On the server, the router copy of a Qwik library kept external reads the config of
60
+ * the app serving the current request.
61
+ *
62
+ * @public
63
+ */
64
+ var getRouterConfig = () => {
65
+ const servingConfig = !holder.config && getServingRouterConfig();
66
+ if (servingConfig) return Promise.resolve(servingConfig);
67
+ if (!holder.ready) holder.ready = loadRouterConfig().catch((error) => {
68
+ holder.ready = void 0;
69
+ throw error;
70
+ });
71
+ return holder.ready;
72
+ };
73
+ var loadRouterConfig = async () => {
74
+ if (!holder.config) {
75
+ const configModule = await import("@qwik-router-config").catch((error) => {
76
+ throw isBrowser ? error : new Error("Qwik Router config not found. Import \"@qwik-router-config\" in the server entry that the Qwik Router Vite plugin builds.", { cause: error });
77
+ });
78
+ holder.config ??= configModule;
79
+ }
80
+ await holder.config.importEagerModules?.();
81
+ return holder.config;
82
+ };
83
+ /**
84
+ * The route trie of the app, as generated by the Qwik Router Vite plugin.
85
+ *
86
+ * @public
87
+ */
88
+ var getRoutes = async () => (await getRouterConfig()).routes;
89
+ /**
90
+ * The base pathname the app is served under, such as `/` or `/docs/`.
91
+ *
92
+ * @public
93
+ */
94
+ var getBasePathname = () => findRouterConfig()?.basePathname ?? globalThis.__QWIK_ROUTER_BASE_PATHNAME__ ?? "/";
95
+ /**
96
+ * Whether the app's URLs end with a trailing slash.
97
+ *
98
+ * @public
99
+ */
100
+ var getTrailingSlash = () => findRouterConfig()?.trailingSlash ?? !globalThis.__NO_TRAILING_SLASH__;
101
+ /** @internal */
102
+ var _getServiceWorkerUrl = () => findRouterConfig()?.serviceWorkerUrl;
34
103
  /**
35
104
  * Route loaders read data before the route rendering starts, based on the route being navigated to.
36
105
  * They automatically update when the route changes on the client.
@@ -39,6 +108,7 @@ var HttpStatusContext = /*#__PURE__*/ createContextId("qr-hs");
39
108
  */
40
109
  var REQUEST_ROUTE_LOADER_STATE = "@routeLoaderState";
41
110
  var REQUEST_LOADER_PATHS_STORE = "@loaderPathsStore";
111
+ var REQUEST_LOADER_PARAMS_STORE = "@loaderParamsStore";
42
112
  var REQUEST_ROUTE_LOADERS = "@routeLoaders";
43
113
  var REQUEST_ROUTE_LOADER_PROMISES = "@routeLoaderPromises";
44
114
  var REQUEST_ROUTE_LOADER_EVENTS = "@routeLoaderEvents";
@@ -201,7 +271,7 @@ var createRouteLoaderSignal = (loader, routeLoaderCtx, state, requestEv3) => {
201
271
  const searchFilter = loader.__search;
202
272
  const loaderHash = isDev ? loader.__qrl.getHash() : id;
203
273
  const signal = createComputedQrl(/*#__PURE__*/ inlinedQrl(async (ctx) => {
204
- const capture = _captures[0], id = _captures[1], lastFetch = _captures[2], loaderHash = _captures[3], resumeValueKey = _captures[4], routeLoaderCtx = _captures[5], searchFilter = _captures[6], state = _captures[7], stateValues = _captures[8];
274
+ let capture = _capturesObj._[0], id = _capturesObj._[1], lastFetch = _capturesObj._[2], loaderHash = _capturesObj._[3], resumeValueKey = _capturesObj._[4], routeLoaderCtx = _capturesObj._[5], searchFilter = _capturesObj._[6], state = _capturesObj._[7], stateValues = _capturesObj._[8];
205
275
  if (!!ctx.info && typeof ctx.info === "object" && "__v" in ctx.info) {
206
276
  const value = ctx.info.__v;
207
277
  if (!isServer && resumeValueKey in stateValues) stateValues[resumeValueKey] = value;
@@ -219,6 +289,7 @@ var createRouteLoaderSignal = (loader, routeLoaderCtx, state, requestEv3) => {
219
289
  }
220
290
  const pageUrl = new URL(request.pageUrl);
221
291
  const mHash = untrack(() => routeLoaderCtx.manifestHash) || "dev";
292
+ const basePath = getBasePathname();
222
293
  const needsResumeFetch = stateValues[resumeValueKey] === _UNINITIALIZED;
223
294
  let fetchUrl = pageUrl;
224
295
  if (searchFilter) {
@@ -227,7 +298,7 @@ var createRouteLoaderSignal = (loader, routeLoaderCtx, state, requestEv3) => {
227
298
  }
228
299
  const result = await fetchRouteLoaderData(id, request.routePath, mHash, {
229
300
  pageUrl: fetchUrl,
230
- basePath: basePathname,
301
+ basePath,
231
302
  ignoreCache: ctx.info === true,
232
303
  signal: ctx.abortSignal
233
304
  });
@@ -369,6 +440,15 @@ function getRouteLoaderCtx(requestEv10) {
369
440
  }
370
441
  return ctx;
371
442
  }
443
+ /** Server-only loader ID → params matched at its loader path; never serialized to the client. */
444
+ function getRouteLoaderParams(requestEv11) {
445
+ let params = requestEv11.sharedMap.get(REQUEST_LOADER_PARAMS_STORE);
446
+ if (!params) {
447
+ params = {};
448
+ requestEv11.sharedMap.set(REQUEST_LOADER_PARAMS_STORE, params);
449
+ }
450
+ return params;
451
+ }
372
452
  var getModuleRouteLoaders = (mods) => {
373
453
  const routeLoaders = [];
374
454
  const seen = /* @__PURE__ */ new Map();
@@ -486,14 +566,14 @@ function restoreRouteLoaders(state, ctx, navCount) {
486
566
  pruneRouteLoaders(state, ctx);
487
567
  for (const signal of client.current.requests.keys()) signal.invalidate();
488
568
  }
489
- var ensureRouteLoaderSignal = (loader, state, routeLoaderCtx, requestEv11) => {
569
+ var ensureRouteLoaderSignal = (loader, state, routeLoaderCtx, requestEv12) => {
490
570
  if (loader.__cacheControl === "immutable") immutableLoaderIds.add(loader.__id);
491
571
  if (isServer && loader.__serializationStrategy === "never") state[getRouteLoaderValueStateKey(loader.__id)] = _UNINITIALIZED;
492
- return state[loader.__id] ||= createRouteLoaderSignal(loader, routeLoaderCtx, state, requestEv11);
572
+ return state[loader.__id] ||= createRouteLoaderSignal(loader, routeLoaderCtx, state, requestEv12);
493
573
  };
494
- var ensureRouteLoaderSignals = (mods, state, routeLoaderCtx, requestEv12) => {
574
+ var ensureRouteLoaderSignals = (mods, state, routeLoaderCtx, requestEv13) => {
495
575
  const loaders = getModuleRouteLoaders(mods);
496
- for (let i = 0; i < loaders.length; i++) ensureRouteLoaderSignal(loaders[i], state, routeLoaderCtx, requestEv12);
576
+ for (let i = 0; i < loaders.length; i++) ensureRouteLoaderSignal(loaders[i], state, routeLoaderCtx, requestEv13);
497
577
  return loaders;
498
578
  };
499
579
  /**
@@ -513,9 +593,9 @@ var matchesRouteLoaderId = (loader, loaderId) => {
513
593
  return typeof loaderId === "string" && (loader.__id === loaderId || isDev && loader.__qrl.getHash() === loaderId);
514
594
  };
515
595
  /** Run a loader and return its raw value. Errors/redirects propagate as exceptions. */
516
- var getRouteLoaderData = async (loaderQrl, validators3, requestEv13) => {
517
- const loaderRequestEv = requestEv13;
518
- const result = await runValidators(requestEv13, validators3, void 0);
596
+ var getRouteLoaderData = async (loaderQrl, validators3, requestEv14) => {
597
+ const loaderRequestEv = requestEv14;
598
+ const result = await runValidators(requestEv14, validators3, void 0);
519
599
  if (!result.success) return loaderRequestEv.fail(result.status ?? 500, result.error);
520
600
  const resolved = await loaderQrl.call(loaderRequestEv, loaderRequestEv);
521
601
  const value = typeof resolved === "function" ? resolved() : resolved;
@@ -523,13 +603,13 @@ var getRouteLoaderData = async (loaderQrl, validators3, requestEv13) => {
523
603
  if (isDev) verifySerializable(value, loaderQrl);
524
604
  return value;
525
605
  };
526
- var loadRouteLoaderByQrl = (loaderId, loaderQrl, validators4, requestEv14) => {
527
- const values = getRouteLoaderValues(requestEv14);
606
+ var loadRouteLoaderByQrl = (loaderId, loaderQrl, validators4, requestEv15) => {
607
+ const values = getRouteLoaderValues(requestEv15);
528
608
  if (loaderId in values) return Promise.resolve(values[loaderId]);
529
- const promises = getRouteLoaderPromises(requestEv14);
609
+ const promises = getRouteLoaderPromises(requestEv15);
530
610
  let promise = promises[loaderId];
531
611
  if (!promise) {
532
- promise = getRouteLoaderData(loaderQrl, validators4, requestEv14).then((value) => {
612
+ promise = getRouteLoaderData(loaderQrl, validators4, requestEv15).then((value) => {
533
613
  values[loaderId] = value;
534
614
  return value;
535
615
  }, (err) => {
@@ -541,16 +621,17 @@ var loadRouteLoaderByQrl = (loaderId, loaderQrl, validators4, requestEv14) => {
541
621
  return promise;
542
622
  };
543
623
  /** @internal */
544
- var getLoaderRequestEvent = (loader, requestEv15) => {
545
- let rootRequestEv = requestEv15.sharedMap.get(REQUEST_ROUTE_LOADER_ROOT_EVENT);
624
+ var getLoaderRequestEvent = (loader, requestEv16) => {
625
+ let rootRequestEv = requestEv16.sharedMap.get(REQUEST_ROUTE_LOADER_ROOT_EVENT);
546
626
  if (!rootRequestEv) {
547
- rootRequestEv = requestEv15;
548
- requestEv15.sharedMap.set(REQUEST_ROUTE_LOADER_ROOT_EVENT, rootRequestEv);
627
+ rootRequestEv = requestEv16;
628
+ requestEv16.sharedMap.set(REQUEST_ROUTE_LOADER_ROOT_EVENT, rootRequestEv);
549
629
  }
550
630
  const url = new URL(rootRequestEv.url);
551
631
  const pathname = globalThis.__STRICT_LOADERS__ ? getRouteLoaderCtx(rootRequestEv).loaderPaths[loader.__id] || rootRequestEv.url.pathname : rootRequestEv.url.pathname;
552
632
  const filteredSearch = loader.__search ? filterSearchParams(url.searchParams, loader.__search) : rootRequestEv.url.search;
553
633
  if (pathname === rootRequestEv.url.pathname && filteredSearch === rootRequestEv.url.search) return rootRequestEv;
634
+ const params = pathname === rootRequestEv.url.pathname ? rootRequestEv.params : getRouteLoaderParams(rootRequestEv)[loader.__id] || {};
554
635
  let events = rootRequestEv.sharedMap.get(REQUEST_ROUTE_LOADER_EVENTS);
555
636
  if (!events) {
556
637
  events = /* @__PURE__ */ new Map();
@@ -568,7 +649,7 @@ var getLoaderRequestEvent = (loader, requestEv15) => {
568
649
  enumerable: true
569
650
  },
570
651
  params: {
571
- value: rootRequestEv.params,
652
+ value: params,
572
653
  enumerable: true
573
654
  },
574
655
  pathname: {
@@ -598,10 +679,10 @@ var getLoaderRequestEvent = (loader, requestEv15) => {
598
679
  * on its own signal instead of mutating the page response. The originals also call `check()`, which
599
680
  * throws once SSR streaming has started — a background loader must fail on its signal, not crash.
600
681
  */
601
- var detachResponseFromEvent = (requestEv16) => {
682
+ var detachResponseFromEvent = (requestEv17) => {
602
683
  let status = 200;
603
684
  const headers = new Headers();
604
- return Object.create(requestEv16, {
685
+ return Object.create(requestEv17, {
605
686
  headers: {
606
687
  value: headers,
607
688
  enumerable: true
@@ -644,18 +725,18 @@ var detachResponseFromEvent = (requestEv16) => {
644
725
  }
645
726
  });
646
727
  };
647
- var loadRouteLoader = (loader, requestEv17) => {
648
- const loaderEv = getLoaderRequestEvent(loader, requestEv17);
728
+ var loadRouteLoader = (loader, requestEv18) => {
729
+ const loaderEv = getLoaderRequestEvent(loader, requestEv18);
649
730
  return loadRouteLoaderByQrl(loader.__id, loader.__qrl, loader.__validators, loader.__blockSSR ? loaderEv : detachResponseFromEvent(loaderEv));
650
731
  };
651
732
  /** Run a loader and wrap the result in a LoaderResponse envelope. Catches redirects/errors. */
652
- var getRouteLoaderResponse = async (loaderQrl, validators5, requestEv18) => {
733
+ var getRouteLoaderResponse = async (loaderQrl, validators5, requestEv19) => {
653
734
  try {
654
- return { d: await getRouteLoaderData(loaderQrl, validators5, requestEv18) };
735
+ return { d: await getRouteLoaderData(loaderQrl, validators5, requestEv19) };
655
736
  } catch (err) {
656
737
  if (err instanceof RedirectMessage) {
657
- const location2 = requestEv18.headers.get("Location") || "/";
658
- requestEv18.headers.delete("Location");
738
+ const location2 = requestEv19.headers.get("Location") || "/";
739
+ requestEv19.headers.delete("Location");
659
740
  return { r: location2 };
660
741
  }
661
742
  if (err instanceof ServerError) return { e: err };
@@ -663,9 +744,9 @@ var getRouteLoaderResponse = async (loaderQrl, validators5, requestEv18) => {
663
744
  }
664
745
  };
665
746
  /**
666
- * A hoisted function declaration on purpose: this module imports `@qwik-router-config`, whose route
667
- * modules call `routeLoaderQrl` back at their own eval — in a bundle that cycle executes the routes
668
- * first, and a `const` binding would throw a TDZ ReferenceError.
747
+ * A hoisted function declaration on purpose: the app's route modules call `routeLoaderQrl` at their
748
+ * own eval, and a bundle may execute them before this module, where a `const` binding would throw a
749
+ * TDZ ReferenceError.
669
750
  *
670
751
  * @internal
671
752
  */
@@ -717,13 +798,13 @@ function routeLoaderQrl(loaderQrl, ...rest) {
717
798
  * @public
718
799
  */
719
800
  var routeLoader$ = /*#__PURE__*/ implicit$FirstArg(routeLoaderQrl);
720
- async function runValidators(requestEv19, validators7, data) {
801
+ async function runValidators(requestEv20, validators7, data) {
721
802
  let lastResult = {
722
803
  success: true,
723
804
  data
724
805
  };
725
806
  if (validators7) for (let i = 0; i < validators7.length; i++) {
726
- lastResult = await validators7[i].validate(requestEv19, data);
807
+ lastResult = await validators7[i].validate(requestEv20, data);
727
808
  if (!lastResult.success) return lastResult;
728
809
  data = lastResult.data;
729
810
  }
@@ -752,7 +833,7 @@ var deepFreeze = (obj) => {
752
833
  /** LoadRoute() runs in both client and server. */
753
834
  var MODULE_CACHE = /*#__PURE__*/ new WeakMap();
754
835
  var loadRoute = async (routes, cacheModules, pathname) => {
755
- const { loaders, params, routeParts, notFound, routeBundleNames, loaderPathsByHash, menuLoader, errorLoader } = matchRouteTree(routes, pathname);
836
+ const { loaders, params, routeParts, notFound, routeBundleNames, loaderPathsByHash, loaderParamsByHash, menuLoader, errorLoader } = matchRouteTree(routes, pathname);
756
837
  const routeName = "/" + routeParts.join("/");
757
838
  const modules = new Array(loaders.length);
758
839
  const pendingLoads = [];
@@ -772,20 +853,26 @@ var loadRoute = async (routes, cacheModules, pathname) => {
772
853
  $notFound$: notFound,
773
854
  $errorLoader$: errorLoader,
774
855
  $loaders$: loaderPathsByHash && Object.keys(loaderPathsByHash),
775
- $loaderPaths$: loaderPathsByHash
856
+ $loaderPaths$: loaderPathsByHash,
857
+ $loaderParams$: loaderParamsByHash
776
858
  };
777
859
  };
778
860
  /** Built-in fallback error component loader */
779
861
  var httpErrorLoader = (() => import("./http-error.qwik.mjs"));
780
- /** Collect the rewrite target's layouts and loader endpoint paths. */
862
+ /** Collect the rewrite target's layouts, loader endpoint paths, and params at those paths. */
781
863
  function walkTrieKeys(root, keys, params) {
782
864
  let node = root;
783
865
  let pathname = "/";
866
+ let pathParams = {};
784
867
  const layouts = [];
785
868
  const loaderPaths = {};
869
+ const loaderParams = {};
786
870
  const collect = (node) => {
787
871
  if (node._L) layouts.push(node._L);
788
- for (const hash of node._R ?? []) loaderPaths[hash] = pathname;
872
+ for (const hash of node._R ?? []) {
873
+ loaderPaths[hash] = pathname;
874
+ loaderParams[hash] = pathParams;
875
+ }
789
876
  };
790
877
  collect(root);
791
878
  for (const key of keys) {
@@ -793,9 +880,13 @@ function walkTrieKeys(root, keys, params) {
793
880
  if (!match) return;
794
881
  for (const group of match.groups) collect(group);
795
882
  node = match.value;
796
- const segment = node._P ? `${node._0 ?? ""}${params[node._P] ?? ""}${node._9 ?? ""}` : key;
883
+ const segment = node._P ? `${node._0 ?? ""}${params[node._P] ?? ""}${node._9 ?? ""}` : unescapeStaticTrieKey(key);
797
884
  const segments = key === "_A" ? segment.split("/") : [segment];
798
885
  pathname = ensureSlash(pathname + segments.map(encodeURIComponent).join("/"));
886
+ if (node._P) pathParams = {
887
+ ...pathParams,
888
+ [node._P]: params[node._P] ?? ""
889
+ };
799
890
  collect(node);
800
891
  }
801
892
  if (!node._I && node._G == null) {
@@ -809,7 +900,8 @@ function walkTrieKeys(root, keys, params) {
809
900
  return {
810
901
  node,
811
902
  layouts,
812
- loaderPaths
903
+ loaderPaths,
904
+ loaderParams
813
905
  };
814
906
  }
815
907
  /** Resolve the page and its modules, following rewrite targets. */
@@ -818,7 +910,10 @@ function resolveLoaders(root, node, gatheredLayouts, params) {
818
910
  const target = walkTrieKeys(root, node._G.split("/").filter((p) => p.length > 0), params);
819
911
  if (!target) return;
820
912
  const resolved = resolveLoaders(root, target.node, target.layouts, params);
821
- if (resolved) resolved.loaderPaths ??= target.loaderPaths;
913
+ if (resolved && !resolved.loaderPaths) {
914
+ resolved.loaderPaths = target.loaderPaths;
915
+ resolved.loaderParams = target.loaderParams;
916
+ }
822
917
  return resolved;
823
918
  }
824
919
  const index = node._I;
@@ -841,11 +936,14 @@ var boundaryChain = (ref) => ref.v ? Array.isArray(ref.v) ? ref.v : [...ref.layo
841
936
  * Collect layouts, the nearest error (`_E`) and not-found (`_4`) boundaries, and the menu from a
842
937
  * node and the group ancestors entered to reach it.
843
938
  */
844
- function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, matchedPathname = "/") {
939
+ function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, matchedPathname = "/", loaderParamsByHash, matchedParams = {}) {
845
940
  for (let j = 0; j < groups.length; j++) {
846
941
  const g = groups[j];
847
942
  if (g._L) layouts.push(g._L);
848
- if (g._R && loaderPathsByHash) for (const hash of g._R) loaderPathsByHash[hash] = matchedPathname;
943
+ if (g._R && loaderPathsByHash) for (const hash of g._R) {
944
+ loaderPathsByHash[hash] = matchedPathname;
945
+ if (loaderParamsByHash) loaderParamsByHash[hash] = matchedParams;
946
+ }
849
947
  if (g._E) {
850
948
  errorLoaderRef.v = g._E;
851
949
  errorLoaderRef.layouts = [...layouts];
@@ -857,7 +955,10 @@ function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRe
857
955
  if (g._N) menuLoaderRef.v = g._N;
858
956
  }
859
957
  if (node._L) layouts.push(node._L);
860
- if (node._R && loaderPathsByHash) for (const hash of node._R) loaderPathsByHash[hash] = matchedPathname;
958
+ if (node._R && loaderPathsByHash) for (const hash of node._R) {
959
+ loaderPathsByHash[hash] = matchedPathname;
960
+ if (loaderParamsByHash) loaderParamsByHash[hash] = matchedParams;
961
+ }
861
962
  if (node._E) {
862
963
  errorLoaderRef.v = node._E;
863
964
  errorLoaderRef.layouts = [...layouts];
@@ -883,7 +984,7 @@ var ChildMatchKind = /* @__PURE__ */ function(ChildMatchKind) {
883
984
  * exact matches inside nested groups take precedence over wildcards at parent level.
884
985
  */
885
986
  function findChild(node, part, partLower, parts, partIndex) {
886
- const exact = node[partLower];
987
+ const exact = node[escapeStaticTrieKey(partLower)];
887
988
  if (exact) return {
888
989
  next: exact,
889
990
  groups: [],
@@ -1026,6 +1127,7 @@ function matchRouteTree(root = {}, pathname) {
1026
1127
  const routeParts = [];
1027
1128
  const layouts = [];
1028
1129
  const loaderPathsByHash = {};
1130
+ const loaderParamsByHash = {};
1029
1131
  const errorLoaderRef = {
1030
1132
  v: void 0,
1031
1133
  layouts: []
@@ -1036,7 +1138,7 @@ function matchRouteTree(root = {}, pathname) {
1036
1138
  };
1037
1139
  const menuLoaderRef = { v: void 0 };
1038
1140
  const groupNodes = [];
1039
- collectNodeMeta(root, [], layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash);
1141
+ collectNodeMeta(root, [], layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, "/", loaderParamsByHash);
1040
1142
  if (root._M) groupNodes.push({
1041
1143
  node: root,
1042
1144
  depth: layouts.length
@@ -1065,6 +1167,7 @@ function matchRouteTree(root = {}, pathname) {
1065
1167
  params: { ...params },
1066
1168
  layouts: [...layouts],
1067
1169
  loaderPathsByHash: { ...loaderPathsByHash },
1170
+ loaderParamsByHash: { ...loaderParamsByHash },
1068
1171
  errorLoader: errorLoaderRef.v,
1069
1172
  errorLayouts: errorLoaderRef.layouts,
1070
1173
  notFoundLoader: notFoundLoaderRef.v,
@@ -1076,7 +1179,7 @@ function matchRouteTree(root = {}, pathname) {
1076
1179
  done = found.done;
1077
1180
  node = found.next;
1078
1181
  const matchedPathname = `/${parts.slice(0, found.done ? len : i + 1).join("/")}/`;
1079
- collectNodeMeta(node, found.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, matchedPathname);
1182
+ collectNodeMeta(node, found.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, matchedPathname, loaderParamsByHash, { ...params });
1080
1183
  if (node._M) groupNodes.push({
1081
1184
  node,
1082
1185
  depth: layouts.length
@@ -1086,7 +1189,7 @@ function matchRouteTree(root = {}, pathname) {
1086
1189
  if (!node._I && node._G == null) {
1087
1190
  const indexResult = findIndexNode(node);
1088
1191
  if (indexResult) {
1089
- collectNodeMeta(indexResult.target, indexResult.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, pathname);
1192
+ collectNodeMeta(indexResult.target, indexResult.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, pathname, loaderParamsByHash, { ...params });
1090
1193
  node = indexResult.target;
1091
1194
  }
1092
1195
  }
@@ -1097,7 +1200,7 @@ function matchRouteTree(root = {}, pathname) {
1097
1200
  const paramName = next._P;
1098
1201
  params[paramName] = "";
1099
1202
  routeParts.push(`[...${paramName}]`);
1100
- collectNodeMeta(next, restInfo.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, pathname);
1203
+ collectNodeMeta(next, restInfo.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, pathname, loaderParamsByHash, { ...params });
1101
1204
  node = next;
1102
1205
  }
1103
1206
  }
@@ -1113,6 +1216,7 @@ function matchRouteTree(root = {}, pathname) {
1113
1216
  const fbRouteParts = [...fb.routeParts, `[...${fb.paramName}]`];
1114
1217
  const fbLayouts = [...fb.layouts];
1115
1218
  const fbLoaderPathsByHash = { ...fb.loaderPathsByHash };
1219
+ const fbLoaderParamsByHash = { ...fb.loaderParamsByHash };
1116
1220
  const fbErrorRef = {
1117
1221
  v: fb.errorLoader,
1118
1222
  layouts: fb.errorLayouts
@@ -1122,10 +1226,10 @@ function matchRouteTree(root = {}, pathname) {
1122
1226
  layouts: fb.notFoundLayouts
1123
1227
  };
1124
1228
  const fbMenuRef = { v: fb.menuLoader };
1125
- collectNodeMeta(fb.aNode, fb.groups, fbLayouts, fbErrorRef, fbNotFoundRef, fbMenuRef, fbLoaderPathsByHash, pathname);
1229
+ collectNodeMeta(fb.aNode, fb.groups, fbLayouts, fbErrorRef, fbNotFoundRef, fbMenuRef, fbLoaderPathsByHash, pathname, fbLoaderParamsByHash, fbParams);
1126
1230
  const fallback = resolveLoaders(root, fb.aNode, fbLayouts, fbParams);
1127
1231
  if (fallback) {
1128
- collectPageLoaders(fallback, fbLoaderPathsByHash, pathname);
1232
+ collectPageLoaders(fallback, fbLoaderPathsByHash, fbLoaderParamsByHash, pathname, fbParams);
1129
1233
  return {
1130
1234
  loaders: fallback.loaders,
1131
1235
  params: fbParams,
@@ -1133,6 +1237,7 @@ function matchRouteTree(root = {}, pathname) {
1133
1237
  notFound: false,
1134
1238
  routeBundleNames: fb.aNode._B,
1135
1239
  loaderPathsByHash: Object.keys(fbLoaderPathsByHash).length > 0 ? fbLoaderPathsByHash : void 0,
1240
+ loaderParamsByHash: Object.keys(fbLoaderParamsByHash).length > 0 ? fbLoaderParamsByHash : void 0,
1136
1241
  menuLoader: fbMenuRef.v,
1137
1242
  errorLoader: boundaryChain(fbErrorRef)
1138
1243
  };
@@ -1174,11 +1279,12 @@ function matchRouteTree(root = {}, pathname) {
1174
1279
  notFound: true,
1175
1280
  routeBundleNames: void 0,
1176
1281
  loaderPathsByHash: void 0,
1282
+ loaderParamsByHash: void 0,
1177
1283
  menuLoader: menuLoaderRef.v,
1178
1284
  errorLoader: boundaryChain(errorLoaderRef)
1179
1285
  };
1180
1286
  }
1181
- collectPageLoaders(resolved, loaderPathsByHash, pathname);
1287
+ collectPageLoaders(resolved, loaderPathsByHash, loaderParamsByHash, pathname, params);
1182
1288
  return {
1183
1289
  loaders,
1184
1290
  params,
@@ -1186,6 +1292,7 @@ function matchRouteTree(root = {}, pathname) {
1186
1292
  notFound: false,
1187
1293
  routeBundleNames: node._B,
1188
1294
  loaderPathsByHash: Object.keys(loaderPathsByHash).length > 0 ? loaderPathsByHash : void 0,
1295
+ loaderParamsByHash: Object.keys(loaderParamsByHash).length > 0 ? loaderParamsByHash : void 0,
1189
1296
  menuLoader: menuLoaderRef.v,
1190
1297
  errorLoader: boundaryChain(errorLoaderRef)
1191
1298
  };
@@ -1204,16 +1311,26 @@ var loadModule = (moduleLoader, pendingLoads, moduleSetter, cacheModules) => {
1204
1311
  }
1205
1312
  }
1206
1313
  };
1207
- function collectPageLoaders({ node, loaderPaths }, paths, pathname) {
1314
+ function collectPageLoaders({ node, loaderPaths, loaderParams }, paths, pathParams, pathname, params) {
1208
1315
  if (loaderPaths) {
1209
- for (const hash in paths) delete paths[hash];
1316
+ for (const hash in paths) {
1317
+ delete paths[hash];
1318
+ delete pathParams[hash];
1319
+ }
1210
1320
  Object.assign(paths, loaderPaths);
1321
+ Object.assign(pathParams, loaderParams);
1211
1322
  }
1212
1323
  const pageHashes = node._D ?? [];
1213
1324
  if (Array.isArray(node._I)) {
1214
- for (const hash in paths) if (!pageHashes.includes(hash)) delete paths[hash];
1325
+ for (const hash in paths) if (!pageHashes.includes(hash)) {
1326
+ delete paths[hash];
1327
+ delete pathParams[hash];
1328
+ }
1329
+ }
1330
+ for (const hash of pageHashes) if (!paths[hash]) {
1331
+ paths[hash] = ensureSlash(pathname);
1332
+ pathParams[hash] = { ...params };
1215
1333
  }
1216
- for (const hash of pageHashes) paths[hash] ||= ensureSlash(pathname);
1217
1334
  }
1218
1335
  /** Gets an absolute url path string (url.pathname + url.search + url.hash) */
1219
1336
  var toPath = (url) => url.pathname + url.search + url.hash;
@@ -1365,4 +1482,4 @@ var createDocumentHead = (defaults, manifestHash) => ({
1365
1482
  frontmatter: { ...defaults?.frontmatter },
1366
1483
  manifestHash: manifestHash || "dev"
1367
1484
  });
1368
- export { basePathname as $, matchesRouteLoaderId as A, DocumentHeadContext as B, getRequestEvent as C, getRouteLoaders as D, getRouteLoaderValues as E, routeLoaderQrl as F, RouteNavigateContext as G, RouteActionContext as H, setLoaderSignalValue as I, QACTION_KEY as J, RoutePreventNavigateContext as K, setRouteLoaders as L, resolveRouteLoaderByHash as M, restoreRouteLoaders as N, isImmutableLoader as O, routeLoader$ as P, _asyncRequestStore as Q, ContentContext as R, getModuleRouteLoaders as S, getRouteLoaderResponse as T, RouteLoaderCtxContext as U, HttpStatusContext as V, RouteLocationContext as W, QFN_KEY as X, QDATA_KEY as Y, Q_ROUTE as Z, commitRouteLoaders as _, isPromise as a, getClientRouteLoaders as b, shouldPreload as c, loadRoute as d, FULLPATH_HEADER as f, clearRouteLoaderData as g, clearNavFetchCache as h, getClientNavPath as i, prepareRouteLoaders as j, loadRouteLoader as k, toPath as l, abortRouteLoaderNavigation as m, resolveHead as n, isSameOrigin as o, ROUTE_PATH_HEADER as p, RouteStateContext as q, resolveRouteConfig as r, isSamePath as s, createDocumentHead as t, toUrl as u, ensureRouteLoaderSignals as v, getRouteLoaderCtx as w, getLoaderRequestEvent as x, fetchRouteLoaderData as y, ContentInternalContext as z };
1485
+ export { RoutePreventNavigateContext as $, loadRouteLoader as A, _setRouterConfig as B, getRequestEvent as C, getRouteLoaderValues as D, getRouteLoaderResponse as E, routeLoader$ as F, ContentContext as G, getRouterConfig as H, routeLoaderQrl as I, HttpStatusContext as J, ContentInternalContext as K, setLoaderSignalValue as L, prepareRouteLoaders as M, resolveRouteLoaderByHash as N, getRouteLoaders as O, restoreRouteLoaders as P, RouteNavigateContext as Q, setRouteLoaders as R, getModuleRouteLoaders as S, getRouteLoaderParams as T, getRoutes as U, getBasePathname as V, getTrailingSlash as W, RouteLoaderCtxContext as X, RouteActionContext as Y, RouteLocationContext as Z, commitRouteLoaders as _, isPromise as a, RequestEvRouterConfig as at, getClientRouteLoaders as b, shouldPreload as c, loadRoute as d, RouteStateContext as et, FULLPATH_HEADER as f, clearRouteLoaderData as g, clearNavFetchCache as h, getClientNavPath as i, Q_ROUTE as it, matchesRouteLoaderId as j, isImmutableLoader as k, toPath as l, abortRouteLoaderNavigation as m, resolveHead as n, QDATA_KEY as nt, isSameOrigin as o, _asyncRequestStore as ot, ROUTE_PATH_HEADER as p, DocumentHeadContext as q, resolveRouteConfig as r, QFN_KEY as rt, isSamePath as s, createDocumentHead as t, QACTION_KEY as tt, toUrl as u, ensureRouteLoaderSignals as v, getRouteLoaderCtx as w, getLoaderRequestEvent as x, fetchRouteLoaderData as y, _getServiceWorkerUrl as z };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Route trie metadata keys start with `_` (`_4`, `_W`, …), so static segments that start with `_`
3
+ * are stored with an extra `_` to keep the two apart.
4
+ */
5
+ var escapeStaticTrieKey = (segment) => segment.startsWith("_") ? "_" + segment : segment;
6
+ /** The URL segment for a static trie key. */
7
+ var unescapeStaticTrieKey = (key) => key.startsWith("_") ? key.slice(1) : key;
8
+ /** Whether a trie key is a static segment rather than metadata. */
9
+ var isStaticTrieKey = (key) => !key.startsWith("_") || key.startsWith("__");
10
+ export { isStaticTrieKey as n, unescapeStaticTrieKey as r, escapeStaticTrieKey as t };
@@ -1,7 +1,22 @@
1
+ import { _qwikSymbol } from "@qwik.dev/core/internal";
2
+ /**
3
+ * Makes `instanceof cls` also match instances made by another copy of the router, such as the one a
4
+ * Qwik library kept external on the server evaluates. Subclasses need their own call.
5
+ */
6
+ var shareClassIdentity = (cls, name) => {
7
+ const tag = _qwikSymbol(`router.${name}`);
8
+ cls.prototype[tag] = true;
9
+ Object.defineProperty(cls, Symbol.hasInstance, { value(value) {
10
+ if (this !== cls) return Function.prototype[Symbol.hasInstance].call(this, value);
11
+ return value != null && typeof value === "object" && tag in value;
12
+ } });
13
+ };
1
14
  /** @public */
2
15
  var AbortMessage = class {};
16
+ shareClassIdentity(AbortMessage, "AbortMessage");
3
17
  /** @public */
4
18
  var RedirectMessage = class extends AbortMessage {};
19
+ shareClassIdentity(RedirectMessage, "RedirectMessage");
5
20
  /** @public */
6
21
  var ServerError = class extends Error {
7
22
  status;
@@ -12,6 +27,7 @@ var ServerError = class extends Error {
12
27
  this.data = data;
13
28
  }
14
29
  };
30
+ shareClassIdentity(ServerError, "ServerError");
15
31
  /**
16
32
  * `ev.redirect()`, `ev.error()`, etc. return a control-flow signal meant to be thrown. Throw it for
17
33
  * the user when they return it instead, so returning and throwing behave the same.
@@ -19,4 +35,4 @@ var ServerError = class extends Error {
19
35
  var throwIfControlFlowSignal = (value) => {
20
36
  if (value instanceof AbortMessage || value instanceof ServerError) throw value;
21
37
  };
22
- export { RedirectMessage as i, throwIfControlFlowSignal as n, AbortMessage as r, ServerError as t };
38
+ export { shareClassIdentity as a, RedirectMessage as i, throwIfControlFlowSignal as n, AbortMessage as r, ServerError as t };
@@ -31,6 +31,10 @@ async function createWorkerPool(sys, opts) {
31
31
  let terminateResolve = null;
32
32
  const mainTasks = /* @__PURE__ */ new Map();
33
33
  let terminateTimeout = null;
34
+ let markWorkerReady;
35
+ const workerReady = new Promise((resolve) => {
36
+ markWorkerReady = resolve;
37
+ });
34
38
  const nodeWorker = new Worker(workerFilePath, { workerData });
35
39
  nodeWorker.unref();
36
40
  const ssgWorker = {
@@ -38,16 +42,14 @@ async function createWorkerPool(sys, opts) {
38
42
  totalTasks: 0,
39
43
  render: (staticRoute) => {
40
44
  return new Promise((resolve, reject) => {
41
- try {
42
- ssgWorker.activeTasks++;
43
- ssgWorker.totalTasks++;
44
- mainTasks.set(staticRoute.pathname, resolve);
45
- nodeWorker.postMessage(staticRoute);
46
- } catch (e) {
45
+ ssgWorker.activeTasks++;
46
+ ssgWorker.totalTasks++;
47
+ mainTasks.set(staticRoute.pathname, resolve);
48
+ workerReady.then(() => nodeWorker.postMessage(staticRoute)).catch((e) => {
47
49
  ssgWorker.activeTasks--;
48
50
  mainTasks.delete(staticRoute.pathname);
49
51
  reject(e);
50
- }
52
+ });
51
53
  });
52
54
  },
53
55
  terminate: async () => {
@@ -63,7 +65,7 @@ async function createWorkerPool(sys, opts) {
63
65
  await nodeWorker.terminate();
64
66
  resolve();
65
67
  }, 1e3);
66
- nodeWorker.postMessage(msg);
68
+ workerReady.then(() => nodeWorker.postMessage(msg));
67
69
  });
68
70
  if (terminateTimeout) {
69
71
  clearTimeout(terminateTimeout);
@@ -73,6 +75,9 @@ async function createWorkerPool(sys, opts) {
73
75
  };
74
76
  nodeWorker.on("message", (msg) => {
75
77
  switch (msg.type) {
78
+ case "ready":
79
+ markWorkerReady();
80
+ break;
76
81
  case "render": {
77
82
  const mainTask = mainTasks.get(msg.pathname);
78
83
  if (mainTask) {