@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,21 +1,34 @@
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, withLocale } from "@qwik.dev/core";
5
- import * as qwikRouterConfig from "@qwik-router-config";
6
- import { SerializerSymbol, _UNINITIALIZED, _deserialize, _getAsyncLocalStorage, _getContextEvent, _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";
7
7
  import { isServer as isServer$1 } from "@qwik.dev/core/build";
8
- /** @internal */ var _asyncRequestStore;
9
- if (isServer$1) {
8
+ var createRequestStore = () => {
10
9
  const AsyncLocalStorage = _getAsyncLocalStorage();
11
- if (AsyncLocalStorage) _asyncRequestStore = new AsyncLocalStorage();
12
- 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");
13
- }
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];
14
24
  var QACTION_KEY = "qaction";
15
25
  var QFN_KEY = "qfunc";
16
26
  var QDATA_KEY = "qdata";
17
- /** @public */ var Q_ROUTE = "q:route";
18
- var DEFAULT_LOADERS_SERIALIZATION_STRATEGY = globalThis.__DEFAULT_LOADERS_SERIALIZATION_STRATEGY__ || "never";
27
+ /** @public */
28
+ var Q_ROUTE = "q:route";
29
+ function DEFAULT_LOADERS_SERIALIZATION_STRATEGY() {
30
+ return globalThis.__DEFAULT_LOADERS_SERIALIZATION_STRATEGY__ || "never";
31
+ }
19
32
  var RouteStateContext = /*#__PURE__*/ createContextId("qr-s");
20
33
  var RouteLoaderCtxContext = /*#__PURE__*/ createContextId("qr-lc");
21
34
  var ContentContext = /*#__PURE__*/ createContextId("qr-c");
@@ -26,28 +39,97 @@ var RouteNavigateContext = /*#__PURE__*/ createContextId("qr-n");
26
39
  var RouteActionContext = /*#__PURE__*/ createContextId("qr-a");
27
40
  var RoutePreventNavigateContext = /*#__PURE__*/ createContextId("qr-p");
28
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;
29
103
  /**
30
104
  * Route loaders read data before the route rendering starts, based on the route being navigated to.
31
105
  * They automatically update when the route changes on the client.
32
106
  *
33
107
  * They are represented by a ComputedSignal.
34
- */ var REQUEST_ROUTE_LOADER_STATE = "@routeLoaderState";
108
+ */
109
+ var REQUEST_ROUTE_LOADER_STATE = "@routeLoaderState";
35
110
  var REQUEST_LOADER_PATHS_STORE = "@loaderPathsStore";
111
+ var REQUEST_LOADER_PARAMS_STORE = "@loaderParamsStore";
36
112
  var REQUEST_ROUTE_LOADERS = "@routeLoaders";
37
113
  var REQUEST_ROUTE_LOADER_PROMISES = "@routeLoaderPromises";
38
114
  var REQUEST_ROUTE_LOADER_EVENTS = "@routeLoaderEvents";
39
115
  var REQUEST_ROUTE_LOADER_ROOT_EVENT = "@routeLoaderRootEvent";
40
116
  var ROUTE_LOADER_VALUE_PREFIX = "__qwik_route_loader_value__";
41
- /** Header name sent by client to tell the server the actual page URL for loader requests. */ var FULLPATH_HEADER = "X-Qwik-fullpath";
42
- /** Dev route hint that preserves the loader URL. */ var ROUTE_PATH_HEADER = "X-Qwik-route-path";
117
+ /** Header name sent by client to tell the server the actual page URL for loader requests. */
118
+ var FULLPATH_HEADER = "X-Qwik-fullpath";
119
+ /** Dev route hint that preserves the loader URL. */
120
+ var ROUTE_PATH_HEADER = "X-Qwik-route-path";
43
121
  /**
44
122
  * Per-navigation fetch dedupe: shares in-flight and completed fetches (hover prefetch → click nav)
45
123
  * and prevents repeat hover fetches. Cleared after each navigation kicks off its loader fetches;
46
124
  * across navigations the browser HTTP cache is the freshness authority.
47
- */ var navFetchCache = /* @__PURE__ */ new Map();
48
- var clearNavFetchCache = () => navFetchCache.clear();
125
+ */
126
+ var navFetchCache = /* @__PURE__ */ new Map();
127
+ var clearNavFetchCache = () => {
128
+ navFetchCache = /* @__PURE__ */ new Map();
129
+ };
49
130
  var isRedirectStatus = (status) => status >= 300 && status < 400;
50
- /** We don't have aborts when preloading so we just pretend we do */ var wrapWithAbort = (promise, signal) => {
131
+ /** We don't have aborts when preloading so we just pretend we do */
132
+ var wrapWithAbort = (promise, signal) => {
51
133
  if (signal.aborted) return Promise.reject(signal.reason);
52
134
  return new Promise((resolve, reject) => {
53
135
  const abort = () => reject(signal.reason);
@@ -55,14 +137,79 @@ var isRedirectStatus = (status) => status >= 300 && status < 400;
55
137
  promise.finally(() => signal.removeEventListener("abort", abort)).then(resolve, reject);
56
138
  });
57
139
  };
140
+ var clientRouteLoaders = /* @__PURE__ */ new WeakMap();
141
+ function getClientRouteLoaders(ctx, pageUrl) {
142
+ let client = clientRouteLoaders.get(ctx);
143
+ if (!client) {
144
+ const current = {
145
+ requests: /* @__PURE__ */ new Map(),
146
+ paths: { ...ctx.loaderPaths },
147
+ pageUrl: pageUrl || location.href
148
+ };
149
+ client = {
150
+ current,
151
+ committed: current
152
+ };
153
+ clientRouteLoaders.set(ctx, client);
154
+ }
155
+ return client;
156
+ }
157
+ function getLoaderRequest(ctx, state, id, hash1) {
158
+ return untrack(() => {
159
+ const client = getClientRouteLoaders(ctx);
160
+ const { requests, paths, pageUrl } = client.current;
161
+ let request = requests.get(state[id]);
162
+ if (!request) {
163
+ const path = paths[id] || paths[hash1];
164
+ request = {
165
+ routePath: path || new URL(pageUrl).pathname,
166
+ pageUrl,
167
+ active: client.navCount === void 0 || !!path,
168
+ hash: hash1
169
+ };
170
+ requests.set(state[id], request);
171
+ }
172
+ return request;
173
+ });
174
+ }
175
+ function assertCurrentLoaderRequest(ctx, signal, request, abortSignal) {
176
+ abortSignal.throwIfAborted();
177
+ if (!request.active || clientRouteLoaders.get(ctx)?.current.requests.get(signal) !== request) throw new DOMException(isDev ? "Route loader navigation was superseded" : "", "AbortError");
178
+ }
58
179
  var getRouteLoaderValueStateKey = (loaderId) => `${ROUTE_LOADER_VALUE_PREFIX}${loaderId}`;
180
+ var ServerRouteLoaderCapture = class {
181
+ hash;
182
+ qrl;
183
+ validators;
184
+ blockSSR;
185
+ requestEv;
186
+ constructor(hash2, qrl1, validators1, blockSSR1, requestEv1) {
187
+ this.hash = hash2;
188
+ this.qrl = qrl1;
189
+ this.validators = validators1;
190
+ this.blockSSR = blockSSR1;
191
+ this.requestEv = requestEv1;
192
+ }
193
+ load() {
194
+ const requestEv2 = this.requestEv;
195
+ if (!requestEv2) throw new Error("Unable to determine the current RequestEvent.");
196
+ const values = getRouteLoaderValues(requestEv2);
197
+ if (this.hash in values) return values[this.hash];
198
+ const ev = this.blockSSR ? requestEv2 : detachResponseFromEvent(requestEv2);
199
+ return loadRouteLoaderByQrl(this.hash, this.qrl, this.validators, ev);
200
+ }
201
+ [SerializerSymbol]() {
202
+ return this.hash;
203
+ }
204
+ };
59
205
  var isRequestEvent = (value) => !!value && typeof value === "object" && Object.prototype.hasOwnProperty.call(value, "sharedMap") && Object.prototype.hasOwnProperty.call(value, "cookie");
60
206
  var isLoaderInternal = (value) => typeof value === "function" && value.__brand === "server_loader";
61
207
  /**
62
208
  * Fetch a single loader's raw JSON response from the server.
63
209
  *
64
210
  * URL pattern: `{basePath}{routePath}/q-loader-{loaderId}.{manifestHash}.json`
65
- */ var fetchRouteLoaderData = async (loaderId, routePath, manifestHash, opts) => {
211
+ */
212
+ var fetchRouteLoaderData = async (loaderId, routePath, manifestHash, opts) => {
66
213
  if (!routePath) return;
67
214
  let resolvedPath = routePath;
68
215
  const basePath = opts?.basePath ?? "/";
@@ -77,40 +224,120 @@ var isLoaderInternal = (value) => typeof value === "function" && value.__brand =
77
224
  else if (isDev) headers[ROUTE_PATH_HEADER] = pageUrl.pathname;
78
225
  }
79
226
  const cacheKey = `${url}\n${headers["X-Qwik-fullpath"] ?? ""}`;
227
+ const cache = navFetchCache;
80
228
  if (!opts?.ignoreCache) {
81
- const entry = navFetchCache.get(cacheKey);
229
+ const entry = cache.get(cacheKey);
82
230
  if (entry) return opts?.signal ? wrapWithAbort(entry, opts.signal) : entry;
83
231
  }
84
232
  const request = async () => {
233
+ opts?.signal?.throwIfAborted();
85
234
  const response = await fetch(url, {
86
235
  signal: opts?.signal,
87
236
  cache: opts?.ignoreCache ? "reload" : "default",
88
237
  headers
89
238
  });
239
+ opts?.signal?.throwIfAborted();
90
240
  if (response.redirected) return { r: response.url };
91
241
  if (isRedirectStatus(response.status)) {
92
242
  const location1 = response.headers.get("Location");
93
243
  if (location1) return { r: location1 };
94
244
  }
95
245
  if (!response.ok) return;
96
- return { raw: await response.text() };
246
+ const raw = await response.text();
247
+ opts?.signal?.throwIfAborted();
248
+ return { raw };
97
249
  };
98
250
  if (opts?.ignoreCache) return request();
99
251
  if (opts?.signal) return request().then((value) => {
100
- if (value !== void 0 && !navFetchCache.has(cacheKey)) navFetchCache.set(cacheKey, Promise.resolve(value));
252
+ opts.signal.throwIfAborted();
253
+ if (value !== void 0 && !cache.has(cacheKey)) cache.set(cacheKey, Promise.resolve(value));
101
254
  return value;
102
255
  });
103
256
  const promise = request().then((value) => {
104
- if (value === void 0) navFetchCache.delete(cacheKey);
257
+ if (value === void 0) cache.delete(cacheKey);
105
258
  return value;
106
259
  }, (err) => {
107
- navFetchCache.delete(cacheKey);
260
+ cache.delete(cacheKey);
108
261
  throw err;
109
262
  });
110
- navFetchCache.set(cacheKey, promise);
263
+ cache.set(cacheKey, promise);
111
264
  return promise;
112
265
  };
113
- /** Build a sorted, stable search string from only the allowed param names. */ var filterSearchParams = (params, allowed) => {
266
+ var createRouteLoaderSignal = (loader, routeLoaderCtx, state, requestEv3) => {
267
+ const id = loader.__id;
268
+ const stateValues = state;
269
+ const resumeValueKey = getRouteLoaderValueStateKey(id);
270
+ const capture = isServer ? new ServerRouteLoaderCapture(id, loader.__qrl, loader.__validators, loader.__blockSSR, requestEv3 ?? useServerData("qwikrouter")?.ev) : id;
271
+ const searchFilter = loader.__search;
272
+ const loaderHash = isDev ? loader.__qrl.getHash() : id;
273
+ const signal = createComputedQrl(/*#__PURE__*/ inlinedQrl(async (ctx) => {
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];
275
+ if (!!ctx.info && typeof ctx.info === "object" && "__v" in ctx.info) {
276
+ const value = ctx.info.__v;
277
+ if (!isServer && resumeValueKey in stateValues) stateValues[resumeValueKey] = value;
278
+ lastFetch.raw = void 0;
279
+ return value;
280
+ }
281
+ if (isServer) return capture.load();
282
+ const request = getLoaderRequest(routeLoaderCtx, state, id, loaderHash);
283
+ if (!request.active) {
284
+ if (ctx.previous === void 0) return new Promise((_, reject) => {
285
+ ctx.abortSignal.throwIfAborted();
286
+ ctx.abortSignal.addEventListener("abort", () => reject(ctx.abortSignal.reason), { once: true });
287
+ });
288
+ return ctx.previous;
289
+ }
290
+ const pageUrl = new URL(request.pageUrl);
291
+ const mHash = untrack(() => routeLoaderCtx.manifestHash) || "dev";
292
+ const basePath = getBasePathname();
293
+ const needsResumeFetch = stateValues[resumeValueKey] === _UNINITIALIZED;
294
+ let fetchUrl = pageUrl;
295
+ if (searchFilter) {
296
+ fetchUrl = new URL(pageUrl.href);
297
+ fetchUrl.search = filterSearchParams(pageUrl.searchParams, searchFilter);
298
+ }
299
+ const result = await fetchRouteLoaderData(id, request.routePath, mHash, {
300
+ pageUrl: fetchUrl,
301
+ basePath,
302
+ ignoreCache: ctx.info === true,
303
+ signal: ctx.abortSignal
304
+ });
305
+ assertCurrentLoaderRequest(routeLoaderCtx, state[id], request, ctx.abortSignal);
306
+ if (!result) throw new Error(`Loader ${id} returned empty response`);
307
+ let response;
308
+ if (result.raw === void 0) response = result;
309
+ else {
310
+ if (result.raw === lastFetch.raw && ctx.previous !== void 0) return ctx.previous;
311
+ response = await _deserialize(result.raw);
312
+ if (!response) throw new Error(`Loader ${id} returned empty response`);
313
+ }
314
+ assertCurrentLoaderRequest(routeLoaderCtx, state[id], request, ctx.abortSignal);
315
+ if (response.r) {
316
+ const goto = routeLoaderCtx.goto;
317
+ if (goto) goto(response.r, { replaceState: true });
318
+ else location.href = response.r;
319
+ return ctx.previous;
320
+ }
321
+ if (response.e) throw response.e;
322
+ lastFetch.raw = result.raw;
323
+ if (needsResumeFetch) stateValues[resumeValueKey] = response.d;
324
+ return response.d;
325
+ }, "createRouteLoaderSignal_signal_createComputed_4s4ZDCYhDOA", [
326
+ capture,
327
+ id,
328
+ {},
329
+ loaderHash,
330
+ resumeValueKey,
331
+ routeLoaderCtx,
332
+ searchFilter,
333
+ state,
334
+ stateValues
335
+ ]), { serializationStrategy: loader.__serializationStrategy });
336
+ _markSignalAsExternallyOwned(signal);
337
+ return signal;
338
+ };
339
+ /** Build a sorted, stable search string from only the allowed param names. */
340
+ var filterSearchParams = (params, allowed) => {
114
341
  const filtered = new URLSearchParams();
115
342
  for (let i = 0; i < allowed.length; i++) {
116
343
  const name = allowed[i];
@@ -120,23 +347,23 @@ var isLoaderInternal = (value) => typeof value === "function" && value.__brand =
120
347
  filtered.sort();
121
348
  return filtered.toString() ? `?${filtered.toString()}` : "";
122
349
  };
123
- var getLoaderOptions = (rest) => {
350
+ function getLoaderOptions(rest) {
124
351
  let id;
125
- let serializationStrategy = DEFAULT_LOADERS_SERIALIZATION_STRATEGY;
352
+ let serializationStrategy = DEFAULT_LOADERS_SERIALIZATION_STRATEGY();
126
353
  let cacheControl;
127
354
  let eTag;
128
355
  let cacheKey;
129
356
  let search;
130
- let blockSSR = true;
131
- const validators = [];
357
+ let blockSSR2 = true;
358
+ const validators2 = [];
132
359
  if (rest.length === 1) {
133
360
  const options = rest[0];
134
361
  if (options && typeof options === "object") {
135
- if ("validate" in options) validators.push(options);
362
+ if ("validate" in options) validators2.push(options);
136
363
  else {
137
364
  if (options.id) id = options.id;
138
365
  if (options.serializationStrategy) serializationStrategy = options.serializationStrategy;
139
- if (options.validation) validators.push(...options.validation);
366
+ if (options.validation) validators2.push(...options.validation);
140
367
  if ("cacheControl" in options) cacheControl = options.cacheControl;
141
368
  if ("eTag" in options) eTag = options.eTag;
142
369
  if ("cacheKey" in options) cacheKey = options.cacheKey;
@@ -144,77 +371,84 @@ var getLoaderOptions = (rest) => {
144
371
  else if (globalThis.__STRICT_LOADERS__) search = [];
145
372
  if (options.blockSSR === false) {
146
373
  if (!__EXPERIMENTAL__.blockSSR) throw new Error("`blockSSR: false` is an experimental feature and is not enabled. Please enable the feature flag by adding `experimental: [\"blockSSR\"]` to your qwikVite plugin options.");
147
- blockSSR = false;
374
+ blockSSR2 = false;
148
375
  }
149
376
  }
150
377
  }
151
- } else if (rest.length > 1) validators.push(...rest.filter(Boolean));
378
+ } else if (rest.length > 1) validators2.push(...rest.filter(Boolean));
152
379
  return {
153
380
  id,
154
- validators: validators.reverse(),
381
+ validators: validators2.reverse(),
155
382
  serializationStrategy,
156
383
  cacheControl,
157
384
  eTag,
158
385
  cacheKey,
159
386
  search,
160
- blockSSR
387
+ blockSSR: blockSSR2
161
388
  };
162
- };
389
+ }
163
390
  /**
164
391
  * Returns the current RequestEvent if possible. Only usable on the server, and only during request
165
392
  * processing.
166
393
  *
167
394
  * @public
168
- */ var getRequestEvent = (thisArg) => {
395
+ */
396
+ var getRequestEvent = (thisArg) => {
169
397
  if (!isServer) throw new Error("getRequestEvent() can only be used on the server.");
170
- return _asyncRequestStore?.getStore() || [thisArg, _getContextEvent()].find(isRequestEvent);
398
+ return _asyncRequestStore?.getStore() ?? (isRequestEvent(thisArg) ? thisArg : void 0);
171
399
  };
172
400
  var REQUEST_ROUTE_LOADER_VALUES = "@routeLoaderValues";
173
- /** Get/create the record of pre-loaded loader values (used by middleware before component). */ function getRouteLoaderValues(requestEv) {
174
- let values = requestEv.sharedMap.get(REQUEST_ROUTE_LOADER_VALUES);
401
+ /** Get/create the record of pre-loaded loader values (used by middleware before component). */
402
+ function getRouteLoaderValues(requestEv5) {
403
+ let values = requestEv5.sharedMap.get(REQUEST_ROUTE_LOADER_VALUES);
175
404
  if (!values) {
176
405
  values = {};
177
- requestEv.sharedMap.set(REQUEST_ROUTE_LOADER_VALUES, values);
406
+ requestEv5.sharedMap.set(REQUEST_ROUTE_LOADER_VALUES, values);
178
407
  }
179
408
  return values;
180
409
  }
181
- function getRouteLoaderPromises(requestEv) {
182
- let promises = requestEv.sharedMap.get(REQUEST_ROUTE_LOADER_PROMISES);
410
+ function getRouteLoaderPromises(requestEv6) {
411
+ let promises = requestEv6.sharedMap.get(REQUEST_ROUTE_LOADER_PROMISES);
183
412
  if (!promises) {
184
413
  promises = {};
185
- requestEv.sharedMap.set(REQUEST_ROUTE_LOADER_PROMISES, promises);
414
+ requestEv6.sharedMap.set(REQUEST_ROUTE_LOADER_PROMISES, promises);
186
415
  }
187
416
  return promises;
188
417
  }
189
- /** Clear loader state before rendering a route-independent error boundary. */ function clearRouteLoaderData(requestEv) {
190
- requestEv.sharedMap.delete(REQUEST_ROUTE_LOADER_STATE);
191
- requestEv.sharedMap.delete(REQUEST_ROUTE_LOADER_VALUES);
192
- requestEv.sharedMap.delete(REQUEST_ROUTE_LOADER_PROMISES);
193
- requestEv.sharedMap.delete(REQUEST_ROUTE_LOADER_EVENTS);
194
- requestEv.sharedMap.delete(REQUEST_ROUTE_LOADER_ROOT_EVENT);
195
- requestEv.sharedMap.delete(REQUEST_ROUTE_LOADERS);
418
+ /** Clear loader state before rendering a route-independent error boundary. */
419
+ function clearRouteLoaderData(requestEv7) {
420
+ requestEv7.sharedMap.delete(REQUEST_ROUTE_LOADER_STATE);
421
+ requestEv7.sharedMap.delete(REQUEST_ROUTE_LOADER_VALUES);
422
+ requestEv7.sharedMap.delete(REQUEST_ROUTE_LOADER_PROMISES);
423
+ requestEv7.sharedMap.delete(REQUEST_ROUTE_LOADER_EVENTS);
424
+ requestEv7.sharedMap.delete(REQUEST_ROUTE_LOADER_ROOT_EVENT);
425
+ requestEv7.sharedMap.delete(REQUEST_ROUTE_LOADERS);
196
426
  }
197
- /** Store the route loader internals on the request for SSG to read. */ function setRouteLoaders(requestEv, loaders) {
198
- requestEv.sharedMap.set(REQUEST_ROUTE_LOADERS, loaders);
427
+ /** Store the route loader internals on the request for SSG to read. */
428
+ function setRouteLoaders(requestEv8, loaders) {
429
+ requestEv8.sharedMap.set(REQUEST_ROUTE_LOADERS, loaders);
199
430
  }
200
- /** Get the route loader internals stored on the request. */ function getRouteLoaders(requestEv) {
201
- return requestEv.sharedMap.get(REQUEST_ROUTE_LOADERS) ?? [];
431
+ /** Get the route loader internals stored on the request. */
432
+ function getRouteLoaders(requestEv9) {
433
+ return requestEv9.sharedMap.get(REQUEST_ROUTE_LOADERS) ?? [];
202
434
  }
203
- function getRouteLoaderCtx(requestEv) {
204
- let ctx = requestEv.sharedMap.get(REQUEST_LOADER_PATHS_STORE);
435
+ function getRouteLoaderCtx(requestEv10) {
436
+ let ctx = requestEv10.sharedMap.get(REQUEST_LOADER_PATHS_STORE);
205
437
  if (!ctx) {
206
438
  ctx = { loaderPaths: {} };
207
- requestEv.sharedMap.set(REQUEST_LOADER_PATHS_STORE, ctx);
439
+ requestEv10.sharedMap.set(REQUEST_LOADER_PATHS_STORE, ctx);
208
440
  }
209
441
  return ctx;
210
442
  }
211
- /** Update route loader matching state on navigation. */ var updateRouteLoaderPaths = (ctx, loaderPaths, pageUrl) => {
212
- if (!isServer) {
213
- ctx.pagePathname = pageUrl.pathname;
214
- ctx.pageSearch = pageUrl.search;
215
- ctx.routeLoaderCandidates = loaderPaths;
216
- } else if (loaderPaths) for (const key in loaderPaths) ctx.loaderPaths[key] = loaderPaths[key];
217
- };
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
+ }
218
452
  var getModuleRouteLoaders = (mods) => {
219
453
  const routeLoaders = [];
220
454
  const seen = /* @__PURE__ */ new Map();
@@ -238,22 +472,109 @@ var getModuleRouteLoaders = (mods) => {
238
472
  };
239
473
  /**
240
474
  * Loader ids declared `cacheControl: 'immutable'`. Their data cannot change until a rebuild, so
241
- * nav-wide invalidation skips them; they still re-fetch when their tracked URL inputs change.
242
- * Registered on every nav via ensureRouteLoaderSignal, so resumed signals are covered too.
243
- */ var immutableLoaderIds = /* @__PURE__ */ new Set();
475
+ * navigation skips them unless their request inputs change. Registered on every nav via
476
+ * ensureRouteLoaderSignal, so resumed signals are covered too.
477
+ */
478
+ var immutableLoaderIds = /* @__PURE__ */ new Set();
244
479
  var isImmutableLoader = (loaderId) => immutableLoaderIds.has(loaderId);
245
- /**
246
- * Invalidate every loader signal on navigation: the browser HTTP cache (driven by each loader's
247
- * cacheControl) decides freshness. Unsubscribed signals only mark stale and refetch lazily when
248
- * read again. Immutable loaders are skipped — their data cannot change until a rebuild; tracked URL
249
- * inputs still invalidate them.
250
- */ var invalidateNavRouteLoaders = (state) => {
251
- for (const id in state) if (!id.startsWith(ROUTE_LOADER_VALUE_PREFIX) && !immutableLoaderIds.has(id)) state[id].invalidate();
480
+ function abortRouteLoaderNavigation(ctx) {
481
+ for (const [signal, request] of clientRouteLoaders.get(ctx)?.current.requests ?? []) {
482
+ if (signal.untrackedPending) request.isAborted = true;
483
+ signal.abort();
484
+ }
485
+ }
486
+ /** Search-filtered loaders ignore changes to unlisted params. */
487
+ function hasSameListedSearch(previous, next, search) {
488
+ return !!search && previous.pathname === next.pathname && filterSearchParams(previous.searchParams, search) === filterSearchParams(next.searchParams, search);
489
+ }
490
+ function prepareRouteLoaders(mods, state, ctx, paths, pageUrl, previousUrl, navCount, forceIds, navigationKey) {
491
+ const client = getClientRouteLoaders(ctx, previousUrl.href);
492
+ if (navigationKey && client.navigationKey === navigationKey) return ensureRouteLoaderSignals(mods, state, ctx);
493
+ const previous = client.current;
494
+ if (client.navCount === void 0) {
495
+ for (const id in state) if (!id.startsWith(ROUTE_LOADER_VALUE_PREFIX)) getLoaderRequest(ctx, state, id, id);
496
+ }
497
+ client.navCount = navCount;
498
+ client.navigationKey = navigationKey;
499
+ const current = client.current = {
500
+ requests: /* @__PURE__ */ new Map(),
501
+ paths: { ...paths },
502
+ pageUrl: pageUrl.href
503
+ };
504
+ const loaders = ensureRouteLoaderSignals(mods, state, ctx);
505
+ const routeLoaders = /* @__PURE__ */ new Map();
506
+ for (const loader of loaders) {
507
+ routeLoaders.set(loader.__id, loader);
508
+ current.paths[loader.__id] ||= current.paths[loader.__qrl.getHash()] || pageUrl.pathname;
509
+ }
510
+ for (const id in state) {
511
+ if (id.startsWith(ROUTE_LOADER_VALUE_PREFIX)) continue;
512
+ const signal = state[id];
513
+ const old = previous.requests.get(signal);
514
+ const loader = routeLoaders.get(id);
515
+ const hash3 = loader?.__qrl.getHash() || old?.hash;
516
+ const routePath = current.paths[id] || hash3 && current.paths[hash3];
517
+ if (!routePath) {
518
+ if (old) current.requests.set(signal, {
519
+ ...old,
520
+ active: false
521
+ });
522
+ signal.abort();
523
+ continue;
524
+ }
525
+ const keepRequest = old?.active && !old.isAborted && old.routePath === routePath && (old.pageUrl === pageUrl.href ? isImmutableLoader(id) : hasSameListedSearch(new URL(old.pageUrl), pageUrl, loader?.__search));
526
+ current.requests.set(signal, keepRequest ? old : {
527
+ routePath,
528
+ pageUrl: pageUrl.href,
529
+ active: true,
530
+ hash: hash3
531
+ });
532
+ ctx.loaderPaths[id] = routePath;
533
+ if (forceIds === null || forceIds?.some((value) => value === id || value === hash3)) signal.invalidate(true);
534
+ else if (old && !keepRequest) signal.invalidate();
535
+ }
536
+ return loaders;
537
+ }
538
+ function pruneRouteLoaders(state, ctx) {
539
+ const { requests } = clientRouteLoaders.get(ctx).current;
540
+ const paths = {};
541
+ for (const id in state) {
542
+ if (id.startsWith(ROUTE_LOADER_VALUE_PREFIX)) continue;
543
+ const signal = state[id];
544
+ const request = requests.get(signal);
545
+ if (request?.active) paths[id] = request.routePath;
546
+ else {
547
+ signal.$dispose();
548
+ requests.delete(signal);
549
+ delete state[id];
550
+ delete state[getRouteLoaderValueStateKey(id)];
551
+ }
552
+ }
553
+ ctx.loaderPaths = paths;
554
+ }
555
+ function commitRouteLoaders(state, ctx, navCount) {
556
+ const client = clientRouteLoaders.get(ctx);
557
+ if (!client || client.navCount !== navCount) return;
558
+ pruneRouteLoaders(state, ctx);
559
+ client.committed = client.current;
560
+ }
561
+ function restoreRouteLoaders(state, ctx, navCount) {
562
+ const client = clientRouteLoaders.get(ctx);
563
+ if (!client || client.navCount !== navCount) return;
564
+ client.navigationKey = void 0;
565
+ client.current = client.committed;
566
+ pruneRouteLoaders(state, ctx);
567
+ for (const signal of client.current.requests.keys()) signal.invalidate();
568
+ }
569
+ var ensureRouteLoaderSignal = (loader, state, routeLoaderCtx, requestEv12) => {
570
+ if (loader.__cacheControl === "immutable") immutableLoaderIds.add(loader.__id);
571
+ if (isServer && loader.__serializationStrategy === "never") state[getRouteLoaderValueStateKey(loader.__id)] = _UNINITIALIZED;
572
+ return state[loader.__id] ||= createRouteLoaderSignal(loader, routeLoaderCtx, state, requestEv12);
252
573
  };
253
- var applyClientRouteLoaderPath = (loaderId, ctx) => {
254
- const currentPath = ctx.routeLoaderCandidates?.[loaderId];
255
- if (currentPath) ctx.loaderPaths[loaderId] = currentPath;
256
- else if (isDev && ctx.pagePathname) ctx.loaderPaths[loaderId] = ctx.pagePathname;
574
+ var ensureRouteLoaderSignals = (mods, state, routeLoaderCtx, requestEv13) => {
575
+ const loaders = getModuleRouteLoaders(mods);
576
+ for (let i = 0; i < loaders.length; i++) ensureRouteLoaderSignal(loaders[i], state, routeLoaderCtx, requestEv13);
577
+ return loaders;
257
578
  };
258
579
  /**
259
580
  * Inject a pre-loaded value into a ComputedSignal while preserving subscriptions. Delegates to the
@@ -261,25 +582,56 @@ var applyClientRouteLoaderPath = (loaderId, ctx) => {
261
582
  * synchronously and returns the pre-loaded value without fetching. Must go through the core helper
262
583
  * because `$computeIfNeeded$` is mangled in core builds and not directly callable from this
263
584
  * package.
264
- */ var setLoaderSignalValue = (signal, value) => {
585
+ */
586
+ var setLoaderSignalValue = (signal, value) => {
265
587
  _injectAsyncSignalValue(signal, value);
266
588
  };
589
+ var resolveRouteLoaderByHash = (routeLoaders, loaderId) => {
590
+ return routeLoaders.find((loader) => matchesRouteLoaderId(loader, loaderId));
591
+ };
267
592
  var matchesRouteLoaderId = (loader, loaderId) => {
268
593
  return typeof loaderId === "string" && (loader.__id === loaderId || isDev && loader.__qrl.getHash() === loaderId);
269
594
  };
270
- var resolveRouteLoaderByHash = (routeLoaders, loaderId) => {
271
- return routeLoaders.find((loader) => matchesRouteLoaderId(loader, loaderId));
595
+ /** Run a loader and return its raw value. Errors/redirects propagate as exceptions. */
596
+ var getRouteLoaderData = async (loaderQrl, validators3, requestEv14) => {
597
+ const loaderRequestEv = requestEv14;
598
+ const result = await runValidators(requestEv14, validators3, void 0);
599
+ if (!result.success) return loaderRequestEv.fail(result.status ?? 500, result.error);
600
+ const resolved = await loaderQrl.call(loaderRequestEv, loaderRequestEv);
601
+ const value = typeof resolved === "function" ? resolved() : resolved;
602
+ throwIfControlFlowSignal(value);
603
+ if (isDev) verifySerializable(value, loaderQrl);
604
+ return value;
272
605
  };
273
- /** @internal */ var getLoaderRequestEvent = (loader, requestEv) => {
274
- let rootRequestEv = requestEv.sharedMap.get(REQUEST_ROUTE_LOADER_ROOT_EVENT);
606
+ var loadRouteLoaderByQrl = (loaderId, loaderQrl, validators4, requestEv15) => {
607
+ const values = getRouteLoaderValues(requestEv15);
608
+ if (loaderId in values) return Promise.resolve(values[loaderId]);
609
+ const promises = getRouteLoaderPromises(requestEv15);
610
+ let promise = promises[loaderId];
611
+ if (!promise) {
612
+ promise = getRouteLoaderData(loaderQrl, validators4, requestEv15).then((value) => {
613
+ values[loaderId] = value;
614
+ return value;
615
+ }, (err) => {
616
+ delete promises[loaderId];
617
+ throw err;
618
+ });
619
+ promises[loaderId] = promise;
620
+ }
621
+ return promise;
622
+ };
623
+ /** @internal */
624
+ var getLoaderRequestEvent = (loader, requestEv16) => {
625
+ let rootRequestEv = requestEv16.sharedMap.get(REQUEST_ROUTE_LOADER_ROOT_EVENT);
275
626
  if (!rootRequestEv) {
276
- rootRequestEv = requestEv;
277
- requestEv.sharedMap.set(REQUEST_ROUTE_LOADER_ROOT_EVENT, rootRequestEv);
627
+ rootRequestEv = requestEv16;
628
+ requestEv16.sharedMap.set(REQUEST_ROUTE_LOADER_ROOT_EVENT, rootRequestEv);
278
629
  }
279
630
  const url = new URL(rootRequestEv.url);
280
631
  const pathname = globalThis.__STRICT_LOADERS__ ? getRouteLoaderCtx(rootRequestEv).loaderPaths[loader.__id] || rootRequestEv.url.pathname : rootRequestEv.url.pathname;
281
632
  const filteredSearch = loader.__search ? filterSearchParams(url.searchParams, loader.__search) : rootRequestEv.url.search;
282
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] || {};
283
635
  let events = rootRequestEv.sharedMap.get(REQUEST_ROUTE_LOADER_EVENTS);
284
636
  if (!events) {
285
637
  events = /* @__PURE__ */ new Map();
@@ -297,7 +649,7 @@ var resolveRouteLoaderByHash = (routeLoaders, loaderId) => {
297
649
  enumerable: true
298
650
  },
299
651
  params: {
300
- value: rootRequestEv.params,
652
+ value: params,
301
653
  enumerable: true
302
654
  },
303
655
  pathname: {
@@ -326,10 +678,11 @@ var resolveRouteLoaderByHash = (routeLoaders, loaderId) => {
326
678
  * are captured locally, so a background (`blockSSR: false`) loader's status/redirect/error surfaces
327
679
  * on its own signal instead of mutating the page response. The originals also call `check()`, which
328
680
  * throws once SSR streaming has started — a background loader must fail on its signal, not crash.
329
- */ var detachResponseFromEvent = (requestEv) => {
681
+ */
682
+ var detachResponseFromEvent = (requestEv17) => {
330
683
  let status = 200;
331
684
  const headers = new Headers();
332
- return Object.create(requestEv, {
685
+ return Object.create(requestEv17, {
333
686
  headers: {
334
687
  value: headers,
335
688
  enumerable: true
@@ -372,174 +725,33 @@ var resolveRouteLoaderByHash = (routeLoaders, loaderId) => {
372
725
  }
373
726
  });
374
727
  };
375
- async function runValidators(requestEv, validators, data) {
376
- let lastResult = {
377
- success: true,
378
- data
379
- };
380
- if (validators) for (let i = 0; i < validators.length; i++) {
381
- lastResult = await validators[i].validate(requestEv, data);
382
- if (!lastResult.success) return lastResult;
383
- data = lastResult.data;
384
- }
385
- return lastResult;
386
- }
387
- function verifySerializable(data, qrl) {
388
- try {
389
- _verifySerializable(data, void 0);
390
- } catch (error) {
391
- if (error instanceof Error && qrl.dev) error.loc = qrl.dev;
392
- throw error;
393
- }
394
- }
395
- /** Run a loader and return its raw value. Errors/redirects propagate as exceptions. */ var getRouteLoaderData = async (loaderQrl, validators, requestEv) => {
396
- const loaderRequestEv = requestEv;
397
- const result = await runValidators(requestEv, validators, void 0);
398
- if (!result.success) return loaderRequestEv.fail(result.status ?? 500, result.error);
399
- const resolved = await loaderQrl.call(loaderRequestEv, loaderRequestEv);
400
- const value = typeof resolved === "function" ? resolved() : resolved;
401
- throwIfControlFlowSignal(value);
402
- if (isDev) verifySerializable(value, loaderQrl);
403
- return value;
404
- };
405
- var loadRouteLoaderByQrl = (loaderId, loaderQrl, validators, requestEv) => {
406
- const values = getRouteLoaderValues(requestEv);
407
- if (loaderId in values) return Promise.resolve(values[loaderId]);
408
- const promises = getRouteLoaderPromises(requestEv);
409
- let promise = promises[loaderId];
410
- if (!promise) {
411
- promise = getRouteLoaderData(loaderQrl, validators, requestEv).then((value) => {
412
- values[loaderId] = value;
413
- return value;
414
- }, (err) => {
415
- delete promises[loaderId];
416
- throw err;
417
- });
418
- promises[loaderId] = promise;
419
- }
420
- return promise;
421
- };
422
- var ServerRouteLoaderCapture = class {
423
- hash;
424
- qrl;
425
- validators;
426
- blockSSR;
427
- constructor(hash, qrl, validators, blockSSR) {
428
- this.hash = hash;
429
- this.qrl = qrl;
430
- this.validators = validators;
431
- this.blockSSR = blockSSR;
432
- }
433
- load() {
434
- const requestEv = getRequestEvent();
435
- if (!requestEv) throw new Error("Unable to determine the current RequestEvent.");
436
- const values = getRouteLoaderValues(requestEv);
437
- if (this.hash in values) return values[this.hash];
438
- const ev = this.blockSSR ? requestEv : detachResponseFromEvent(requestEv);
439
- return loadRouteLoaderByQrl(this.hash, this.qrl, this.validators, ev);
440
- }
441
- [SerializerSymbol]() {
442
- return this.hash;
443
- }
444
- };
445
- var createRouteLoaderSignal = (loader, routeLoaderCtx, state) => {
446
- const id = loader.__id;
447
- const stateValues = state;
448
- const resumeValueKey = getRouteLoaderValueStateKey(id);
449
- const capture = isServer ? new ServerRouteLoaderCapture(id, loader.__qrl, loader.__validators, loader.__blockSSR) : id;
450
- const searchFilter = loader.__search;
451
- const signal = createComputedQrl(/*#__PURE__*/ inlinedQrl(async (ctx) => {
452
- const capture = _captures[0], id = _captures[1], lastFetch = _captures[2], resumeValueKey = _captures[3], routeLoaderCtx = _captures[4], searchFilter = _captures[5], stateValues = _captures[6];
453
- const hasInjectedValue = !!ctx.info && typeof ctx.info === "object" && "__v" in ctx.info;
454
- const trackedRoutePath = ctx.track(routeLoaderCtx.loaderPaths, id);
455
- const trackedPagePathname = ctx.track(routeLoaderCtx, "pagePathname");
456
- const trackedPageSearch = ctx.track(routeLoaderCtx, "pageSearch");
457
- if (hasInjectedValue) {
458
- const value = ctx.info.__v;
459
- if (!isServer && resumeValueKey in stateValues) stateValues[resumeValueKey] = value;
460
- lastFetch.raw = void 0;
461
- return value;
462
- }
463
- if (isServer) return capture.load();
464
- const pagePathname = trackedPagePathname || location.pathname;
465
- const pageSearch = trackedPageSearch || location.search;
466
- const pageUrl = new URL(pagePathname + pageSearch, location.href);
467
- const mHash = routeLoaderCtx.manifestHash || "dev";
468
- const basePath = qwikRouterConfig.basePathname ?? "/";
469
- const needsResumeFetch = stateValues[resumeValueKey] === _UNINITIALIZED;
470
- const fetchRoutePath = trackedRoutePath || (needsResumeFetch ? pageUrl.pathname : void 0);
471
- if (!fetchRoutePath) return ctx.previous;
472
- let fetchUrl = pageUrl;
473
- if (searchFilter) {
474
- fetchUrl = new URL(pageUrl.href);
475
- fetchUrl.search = filterSearchParams(pageUrl.searchParams, searchFilter);
476
- }
477
- const result = await fetchRouteLoaderData(id, fetchRoutePath, mHash, {
478
- pageUrl: fetchUrl,
479
- basePath,
480
- ignoreCache: ctx.info === true,
481
- signal: ctx.abortSignal
482
- });
483
- if (!result) throw new Error(`Loader ${id} returned empty response`);
484
- let response;
485
- if (result.raw === void 0) response = result;
486
- else {
487
- if (result.raw === lastFetch.raw && ctx.previous !== void 0) return ctx.previous;
488
- response = await _deserialize(result.raw);
489
- if (!response) throw new Error(`Loader ${id} returned empty response`);
490
- }
491
- if (response.r) {
492
- const goto = routeLoaderCtx.goto;
493
- if (goto) goto(response.r, { replaceState: true });
494
- else location.href = response.r;
495
- return ctx.previous;
496
- }
497
- if (response.e) throw response.e;
498
- lastFetch.raw = result.raw;
499
- if (needsResumeFetch) stateValues[resumeValueKey] = response.d;
500
- return response.d;
501
- }, "createRouteLoaderSignal_signal_createComputed_4s4ZDCYhDOA", [
502
- capture,
503
- id,
504
- {},
505
- resumeValueKey,
506
- routeLoaderCtx,
507
- searchFilter,
508
- stateValues
509
- ]), { serializationStrategy: loader.__serializationStrategy });
510
- _markSignalAsExternallyOwned(signal);
511
- return signal;
512
- };
513
- var ensureRouteLoaderSignal = (loader, state, routeLoaderCtx) => {
514
- if (!isServer && routeLoaderCtx.pagePathname) applyClientRouteLoaderPath(loader.__id, routeLoaderCtx);
515
- if (loader.__cacheControl === "immutable") immutableLoaderIds.add(loader.__id);
516
- if (isServer && loader.__serializationStrategy === "never") state[getRouteLoaderValueStateKey(loader.__id)] = _UNINITIALIZED;
517
- return state[loader.__id] ||= createRouteLoaderSignal(loader, routeLoaderCtx, state);
518
- };
519
- var ensureRouteLoaderSignals = (mods, state, routeLoaderCtx) => {
520
- const loaders = getModuleRouteLoaders(mods);
521
- for (let i = 0; i < loaders.length; i++) ensureRouteLoaderSignal(loaders[i], state, routeLoaderCtx);
522
- return loaders;
523
- };
524
- var loadRouteLoader = (loader, requestEv) => {
525
- const loaderEv = getLoaderRequestEvent(loader, requestEv);
728
+ var loadRouteLoader = (loader, requestEv18) => {
729
+ const loaderEv = getLoaderRequestEvent(loader, requestEv18);
526
730
  return loadRouteLoaderByQrl(loader.__id, loader.__qrl, loader.__validators, loader.__blockSSR ? loaderEv : detachResponseFromEvent(loaderEv));
527
731
  };
528
- /** Run a loader and wrap the result in a LoaderResponse envelope. Catches redirects/errors. */ var getRouteLoaderResponse = async (loaderQrl, validators, requestEv) => {
732
+ /** Run a loader and wrap the result in a LoaderResponse envelope. Catches redirects/errors. */
733
+ var getRouteLoaderResponse = async (loaderQrl, validators5, requestEv19) => {
529
734
  try {
530
- return { d: await getRouteLoaderData(loaderQrl, validators, requestEv) };
735
+ return { d: await getRouteLoaderData(loaderQrl, validators5, requestEv19) };
531
736
  } catch (err) {
532
737
  if (err instanceof RedirectMessage) {
533
- const location1 = requestEv.headers.get("Location") || "/";
534
- requestEv.headers.delete("Location");
535
- return { r: location1 };
738
+ const location2 = requestEv19.headers.get("Location") || "/";
739
+ requestEv19.headers.delete("Location");
740
+ return { r: location2 };
536
741
  }
537
742
  if (err instanceof ServerError) return { e: err };
538
743
  throw err;
539
744
  }
540
745
  };
541
- /** @internal */ var routeLoaderQrl = (loaderQrl, ...rest) => {
542
- const { id, validators, serializationStrategy, cacheControl, eTag, cacheKey, search, blockSSR } = getLoaderOptions(rest);
746
+ /**
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.
750
+ *
751
+ * @internal
752
+ */
753
+ function routeLoaderQrl(loaderQrl, ...rest) {
754
+ const { id, validators: validators6, serializationStrategy, cacheControl, eTag, cacheKey, search, blockSSR: blockSSR3 } = getLoaderOptions(rest);
543
755
  function loader() {
544
756
  const signal = ensureRouteLoaderSignal(loader, _resolveContextWithoutSequentialScope(RouteStateContext), _resolveContextWithoutSequentialScope(RouteLoaderCtxContext));
545
757
  signal.promise();
@@ -547,17 +759,17 @@ var loadRouteLoader = (loader, requestEv) => {
547
759
  }
548
760
  loader.__brand = "server_loader";
549
761
  loader.__qrl = loaderQrl;
550
- loader.__validators = validators;
762
+ loader.__validators = validators6;
551
763
  loader.__id = id ?? loaderQrl.getHash();
552
764
  loader.__serializationStrategy = serializationStrategy;
553
765
  loader.__cacheControl = cacheControl;
554
766
  loader.__eTag = eTag;
555
767
  loader.__cacheKey = cacheKey;
556
768
  loader.__search = search;
557
- loader.__blockSSR = blockSSR;
769
+ loader.__blockSSR = blockSSR3;
558
770
  Object.freeze(loader);
559
771
  return loader;
560
- };
772
+ }
561
773
  /**
562
774
  * Define a route loader that fetches data before the route renders.
563
775
  *
@@ -584,7 +796,28 @@ var loadRouteLoader = (loader, requestEv) => {
584
796
  * specify an explicit `search` option.
585
797
  *
586
798
  * @public
587
- */ var routeLoader$ = /*#__PURE__*/ implicit$FirstArg(routeLoaderQrl);
799
+ */
800
+ var routeLoader$ = /*#__PURE__*/ implicit$FirstArg(routeLoaderQrl);
801
+ async function runValidators(requestEv20, validators7, data) {
802
+ let lastResult = {
803
+ success: true,
804
+ data
805
+ };
806
+ if (validators7) for (let i = 0; i < validators7.length; i++) {
807
+ lastResult = await validators7[i].validate(requestEv20, data);
808
+ if (!lastResult.success) return lastResult;
809
+ data = lastResult.data;
810
+ }
811
+ return lastResult;
812
+ }
813
+ function verifySerializable(data, qrl2) {
814
+ try {
815
+ _verifySerializable(data, void 0);
816
+ } catch (error) {
817
+ if (error instanceof Error && qrl2.dev) error.loc = qrl2.dev;
818
+ throw error;
819
+ }
820
+ }
588
821
  var deepFreeze = (obj) => {
589
822
  if (obj == null) return obj;
590
823
  Object.getOwnPropertyNames(obj).forEach((prop) => {
@@ -597,52 +830,119 @@ var deepFreeze = (obj) => {
597
830
  });
598
831
  return Object.freeze(obj);
599
832
  };
600
- /** LoadRoute() runs in both client and server. */ var MODULE_CACHE = /*#__PURE__*/ new WeakMap();
601
- /** Built-in fallback error component loader */ var httpErrorLoader = () => import("./http-error.qwik.mjs");
602
- /**
603
- * Walk trie keys from root, gathering `_L` layouts along the way. Returns the final node and
604
- * collected layouts, or undefined if any key is missing.
605
- */ function walkTrieKeys(root, keys) {
833
+ /** LoadRoute() runs in both client and server. */
834
+ var MODULE_CACHE = /*#__PURE__*/ new WeakMap();
835
+ var loadRoute = async (routes, cacheModules, pathname) => {
836
+ const { loaders, params, routeParts, notFound, routeBundleNames, loaderPathsByHash, loaderParamsByHash, menuLoader, errorLoader } = matchRouteTree(routes, pathname);
837
+ const routeName = "/" + routeParts.join("/");
838
+ const modules = new Array(loaders.length);
839
+ const pendingLoads = [];
840
+ for (let i = 0; i < loaders.length; i++) {
841
+ const moduleLoader = loaders[i];
842
+ loadModule(moduleLoader, pendingLoads, (routeModule) => modules[i] = routeModule, cacheModules);
843
+ }
844
+ let menu = void 0;
845
+ loadModule(menuLoader, pendingLoads, (menuModule) => menu = menuModule?.default, cacheModules);
846
+ if (pendingLoads.length > 0) await Promise.all(pendingLoads);
847
+ return {
848
+ $routeName$: routeName,
849
+ $params$: params,
850
+ $mods$: modules,
851
+ $menu$: deepFreeze(menu),
852
+ $routeBundleNames$: routeBundleNames,
853
+ $notFound$: notFound,
854
+ $errorLoader$: errorLoader,
855
+ $loaders$: loaderPathsByHash && Object.keys(loaderPathsByHash),
856
+ $loaderPaths$: loaderPathsByHash,
857
+ $loaderParams$: loaderParamsByHash
858
+ };
859
+ };
860
+ /** Built-in fallback error component loader */
861
+ var httpErrorLoader = (() => import("./http-error.qwik.mjs"));
862
+ /** Collect the rewrite target's layouts, loader endpoint paths, and params at those paths. */
863
+ function walkTrieKeys(root, keys, params) {
606
864
  let node = root;
865
+ let pathname = "/";
866
+ let pathParams = {};
607
867
  const layouts = [];
608
- if (node._L) layouts.push(node._L);
609
- for (let i = 0; i < keys.length; i++) {
610
- const key = keys[i];
611
- let next = node[key];
612
- if (!next && node._M) for (let j = 0; j < node._M.length; j++) {
613
- const group = node._M[j];
614
- next = group[key];
615
- if (next) {
616
- if (group._L) layouts.push(group._L);
617
- break;
618
- }
619
- }
620
- if (!next) return;
621
- node = next;
868
+ const loaderPaths = {};
869
+ const loaderParams = {};
870
+ const collect = (node) => {
622
871
  if (node._L) layouts.push(node._L);
872
+ for (const hash of node._R ?? []) {
873
+ loaderPaths[hash] = pathname;
874
+ loaderParams[hash] = pathParams;
875
+ }
876
+ };
877
+ collect(root);
878
+ for (const key of keys) {
879
+ const match = descendGroups(node, (candidate) => candidate[key]);
880
+ if (!match) return;
881
+ for (const group of match.groups) collect(group);
882
+ node = match.value;
883
+ const segment = node._P ? `${node._0 ?? ""}${params[node._P] ?? ""}${node._9 ?? ""}` : unescapeStaticTrieKey(key);
884
+ const segments = key === "_A" ? segment.split("/") : [segment];
885
+ pathname = ensureSlash(pathname + segments.map(encodeURIComponent).join("/"));
886
+ if (node._P) pathParams = {
887
+ ...pathParams,
888
+ [node._P]: params[node._P] ?? ""
889
+ };
890
+ collect(node);
891
+ }
892
+ if (!node._I && node._G == null) {
893
+ const index = findIndexNode(node);
894
+ if (index) {
895
+ for (const group of index.groups) collect(group);
896
+ node = index.target;
897
+ collect(node);
898
+ }
623
899
  }
624
900
  return {
625
901
  node,
626
- layouts
902
+ layouts,
903
+ loaderPaths,
904
+ loaderParams
905
+ };
906
+ }
907
+ /** Resolve the page and its modules, following rewrite targets. */
908
+ function resolveLoaders(root, node, gatheredLayouts, params) {
909
+ if (node._G != null) {
910
+ const target = walkTrieKeys(root, node._G.split("/").filter((p) => p.length > 0), params);
911
+ if (!target) return;
912
+ const resolved = resolveLoaders(root, target.node, target.layouts, params);
913
+ if (resolved && !resolved.loaderPaths) {
914
+ resolved.loaderPaths = target.loaderPaths;
915
+ resolved.loaderParams = target.loaderParams;
916
+ }
917
+ return resolved;
918
+ }
919
+ const index = node._I;
920
+ if (!index) return;
921
+ if (Array.isArray(index)) return {
922
+ loaders: index,
923
+ node
924
+ };
925
+ return {
926
+ loaders: [...gatheredLayouts, index],
927
+ node
627
928
  };
628
929
  }
629
930
  /**
630
931
  * The full chain to render for a boundary: a bare loader in its snapshot layouts, an override
631
932
  * as-is.
632
- */ var boundaryChain = (ref) => ref.v ? Array.isArray(ref.v) ? ref.v : [...ref.layouts, ref.v] : void 0;
933
+ */
934
+ var boundaryChain = (ref) => ref.v ? Array.isArray(ref.v) ? ref.v : [...ref.layouts, ref.v] : void 0;
633
935
  /**
634
936
  * Collect layouts, the nearest error (`_E`) and not-found (`_4`) boundaries, and the menu from a
635
937
  * node and the group ancestors entered to reach it.
636
- */ function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, matchedPathname = "/") {
938
+ */
939
+ function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, matchedPathname = "/", loaderParamsByHash, matchedParams = {}) {
637
940
  for (let j = 0; j < groups.length; j++) {
638
941
  const g = groups[j];
639
942
  if (g._L) layouts.push(g._L);
640
- if (g._R && loaderHashes) {
641
- loaderHashes.push(...g._R);
642
- if (loaderPathsByHash) for (let i = 0; i < g._R.length; i++) {
643
- const hash = g._R[i];
644
- loaderPathsByHash[hash] = matchedPathname;
645
- }
943
+ if (g._R && loaderPathsByHash) for (const hash of g._R) {
944
+ loaderPathsByHash[hash] = matchedPathname;
945
+ if (loaderParamsByHash) loaderParamsByHash[hash] = matchedParams;
646
946
  }
647
947
  if (g._E) {
648
948
  errorLoaderRef.v = g._E;
@@ -655,12 +955,9 @@ var deepFreeze = (obj) => {
655
955
  if (g._N) menuLoaderRef.v = g._N;
656
956
  }
657
957
  if (node._L) layouts.push(node._L);
658
- if (node._R && loaderHashes) {
659
- loaderHashes.push(...node._R);
660
- if (loaderPathsByHash) for (let i = 0; i < node._R.length; i++) {
661
- const hash = node._R[i];
662
- loaderPathsByHash[hash] = matchedPathname;
663
- }
958
+ if (node._R && loaderPathsByHash) for (const hash of node._R) {
959
+ loaderPathsByHash[hash] = matchedPathname;
960
+ if (loaderParamsByHash) loaderParamsByHash[hash] = matchedParams;
664
961
  }
665
962
  if (node._E) {
666
963
  errorLoaderRef.v = node._E;
@@ -672,7 +969,45 @@ var deepFreeze = (obj) => {
672
969
  }
673
970
  if (node._N) menuLoaderRef.v = node._N;
674
971
  }
675
- /** Try to match a segment against a node's wildcard children (_W, _A). */ function tryWildcardMatch(node, part, partLower, parts, partIndex) {
972
+ var ChildMatchKind = /* @__PURE__ */ function(ChildMatchKind) {
973
+ ChildMatchKind[ChildMatchKind["Exact"] = 0] = "Exact";
974
+ ChildMatchKind[ChildMatchKind["Wildcard"] = 1] = "Wildcard";
975
+ ChildMatchKind[ChildMatchKind["Rest"] = 2] = "Rest";
976
+ return ChildMatchKind;
977
+ }(ChildMatchKind || {});
978
+ /**
979
+ * Try to find a child matching `partLower` in `node`, including inside group children (_M). Returns
980
+ * the matched child node, the groups entered to reach it, what to push to routeParts, and which
981
+ * kind of edge matched. Returns undefined if no match.
982
+ *
983
+ * Priority: exact match → _M groups (recursively) → _W wildcard → _A rest wildcard. This ensures
984
+ * exact matches inside nested groups take precedence over wildcards at parent level.
985
+ */
986
+ function findChild(node, part, partLower, parts, partIndex) {
987
+ const exact = node[escapeStaticTrieKey(partLower)];
988
+ if (exact) return {
989
+ next: exact,
990
+ groups: [],
991
+ routePart: part,
992
+ done: false,
993
+ kind: ChildMatchKind.Exact
994
+ };
995
+ if (node._M) for (let j = 0; j < node._M.length; j++) {
996
+ const group = node._M[j];
997
+ const groupResult = findChild(group, part, partLower, parts, partIndex);
998
+ if (groupResult) {
999
+ groupResult.groups.unshift(group);
1000
+ return groupResult;
1001
+ }
1002
+ }
1003
+ const wildcard = tryWildcardMatch(node, part, partLower, parts, partIndex);
1004
+ if (wildcard) return {
1005
+ ...wildcard,
1006
+ groups: []
1007
+ };
1008
+ }
1009
+ /** Try to match a segment against a node's wildcard children (_W, _A). */
1010
+ function tryWildcardMatch(node, part, partLower, parts, partIndex) {
676
1011
  let next = node._W;
677
1012
  if (next) {
678
1013
  const prefix = next._0;
@@ -687,7 +1022,7 @@ var deepFreeze = (obj) => {
687
1022
  next,
688
1023
  routePart: `${pre}[${paramName}]${suf}`,
689
1024
  done: false,
690
- kind: 1,
1025
+ kind: ChildMatchKind.Wildcard,
691
1026
  paramName,
692
1027
  paramValue: value
693
1028
  };
@@ -698,7 +1033,7 @@ var deepFreeze = (obj) => {
698
1033
  next,
699
1034
  routePart: `[${paramName}]`,
700
1035
  done: false,
701
- kind: 1,
1036
+ kind: ChildMatchKind.Wildcard,
702
1037
  paramName,
703
1038
  paramValue: part
704
1039
  };
@@ -712,47 +1047,18 @@ var deepFreeze = (obj) => {
712
1047
  next,
713
1048
  routePart: `[...${paramName}]`,
714
1049
  done: true,
715
- kind: 2,
1050
+ kind: ChildMatchKind.Rest,
716
1051
  paramName,
717
1052
  paramValue: restValue
718
1053
  };
719
1054
  }
720
1055
  }
721
1056
  /**
722
- * Try to find a child matching `partLower` in `node`, including inside group children (_M). Returns
723
- * the matched child node, the groups entered to reach it, what to push to routeParts, and which
724
- * kind of edge matched. Returns undefined if no match.
725
- *
726
- * Priority: exact match → _M groups (recursively) → _W wildcard → _A rest wildcard. This ensures
727
- * exact matches inside nested groups take precedence over wildcards at parent level.
728
- */ function findChild(node, part, partLower, parts, partIndex) {
729
- const exact = node[partLower];
730
- if (exact) return {
731
- next: exact,
732
- groups: [],
733
- routePart: part,
734
- done: false,
735
- kind: 0
736
- };
737
- if (node._M) for (let j = 0; j < node._M.length; j++) {
738
- const group = node._M[j];
739
- const groupResult = findChild(group, part, partLower, parts, partIndex);
740
- if (groupResult) {
741
- groupResult.groups.unshift(group);
742
- return groupResult;
743
- }
744
- }
745
- const wildcard = tryWildcardMatch(node, part, partLower, parts, partIndex);
746
- if (wildcard) return {
747
- ...wildcard,
748
- groups: []
749
- };
750
- }
751
- /**
752
1057
  * Descend through a node and its pathless `_M` groups (depth-first, in `_M` order), returning the
753
1058
  * first node for which `hit` yields a value, plus the chain of groups entered to reach it. The
754
1059
  * shared traversal behind the index / rest / boundary lookups inside groups.
755
- */ function descendGroups(node, hit) {
1060
+ */
1061
+ function descendGroups(node, hit) {
756
1062
  const value = hit(node);
757
1063
  if (value !== void 0) return {
758
1064
  value,
@@ -772,7 +1078,8 @@ var deepFreeze = (obj) => {
772
1078
  * Find a node with `_I`/`_G` at the current level, including inside `_M` groups. Returns it and the
773
1079
  * groups entered to reach it — excluding the target itself, whose `_L` the caller applies (so it
774
1080
  * isn't double-counted).
775
- */ function findIndexNode(node) {
1081
+ */
1082
+ function findIndexNode(node) {
776
1083
  const r = descendGroups(node, (n) => n._I || n._G != null ? n : void 0);
777
1084
  return r ? {
778
1085
  target: r.node,
@@ -780,39 +1087,10 @@ var deepFreeze = (obj) => {
780
1087
  } : void 0;
781
1088
  }
782
1089
  /**
783
- * Given a matched node that has `_I` or `_G`, resolve the final loaders array.
784
- *
785
- * - `_G`: re-walk from root to gather target's layouts and `_I`
786
- * - `_I` array: use as-is (override, e.g. layout stop)
787
- * - `_I` function: prepend gathered layouts
788
- */ function resolveLoaders(root, node, gatheredLayouts) {
789
- if (node._G != null) {
790
- const target = walkTrieKeys(root, node._G.split("/").filter((p) => p.length > 0));
791
- if (!target) return;
792
- let targetNode = target.node;
793
- const targetLayouts = target.layouts;
794
- if (!targetNode._I && targetNode._G == null) {
795
- const indexResult = findIndexNode(targetNode);
796
- if (indexResult) {
797
- for (let j = 0; j < indexResult.groups.length; j++) {
798
- const g = indexResult.groups[j];
799
- if (g._L) targetLayouts.push(g._L);
800
- }
801
- targetNode = indexResult.target;
802
- if (targetNode._L) targetLayouts.push(targetNode._L);
803
- }
804
- }
805
- return resolveLoaders(root, targetNode, targetLayouts);
806
- }
807
- const index = node._I;
808
- if (!index) return;
809
- if (Array.isArray(index)) return index;
810
- return [...gatheredLayouts, index];
811
- }
812
- /**
813
1090
  * Find a rest wildcard (`_A`) at the current node or inside its `_M` groups, with the groups
814
1091
  * entered to reach it. Used to save fallback checkpoints during matching.
815
- */ function findRestNode(node) {
1092
+ */
1093
+ function findRestNode(node) {
816
1094
  const r = descendGroups(node, (n) => n._A);
817
1095
  return r ? {
818
1096
  next: r.value,
@@ -822,7 +1100,8 @@ var deepFreeze = (obj) => {
822
1100
  /**
823
1101
  * Find a `_4`/`_E` boundary inside a node's pathless `_M` groups (first in `_M` order), wrapped in
824
1102
  * those groups' layouts.
825
- */ function findGroupBoundary(node, layouts, kind) {
1103
+ */
1104
+ function findGroupBoundary(node, layouts, kind) {
826
1105
  const r = descendGroups(node, (n) => n[kind]);
827
1106
  if (!r) return;
828
1107
  const groupLayouts = [...layouts];
@@ -841,13 +1120,14 @@ var deepFreeze = (obj) => {
841
1120
  * Group nodes (in `_M` arrays) are pathless — they don't consume a URL segment but contribute their
842
1121
  * `_L` layout to the chain. The matcher transparently enters groups when searching for a matching
843
1122
  * child.
844
- */ function matchRouteTree(root = {}, pathname) {
1123
+ */
1124
+ function matchRouteTree(root = {}, pathname) {
845
1125
  let node = root;
846
1126
  const params = {};
847
1127
  const routeParts = [];
848
1128
  const layouts = [];
849
- const loaderHashes = [];
850
1129
  const loaderPathsByHash = {};
1130
+ const loaderParamsByHash = {};
851
1131
  const errorLoaderRef = {
852
1132
  v: void 0,
853
1133
  layouts: []
@@ -858,7 +1138,7 @@ var deepFreeze = (obj) => {
858
1138
  };
859
1139
  const menuLoaderRef = { v: void 0 };
860
1140
  const groupNodes = [];
861
- collectNodeMeta(root, [], layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash);
1141
+ collectNodeMeta(root, [], layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, "/", loaderParamsByHash);
862
1142
  if (root._M) groupNodes.push({
863
1143
  node: root,
864
1144
  depth: layouts.length
@@ -877,7 +1157,7 @@ var deepFreeze = (obj) => {
877
1157
  matched = false;
878
1158
  break;
879
1159
  }
880
- const restInfo = found.kind === 1 ? findRestNode(node) : void 0;
1160
+ const restInfo = found.kind === ChildMatchKind.Wildcard ? findRestNode(node) : void 0;
881
1161
  if (restInfo) restFallback = {
882
1162
  aNode: restInfo.next,
883
1163
  groups: restInfo.groups,
@@ -887,6 +1167,7 @@ var deepFreeze = (obj) => {
887
1167
  params: { ...params },
888
1168
  layouts: [...layouts],
889
1169
  loaderPathsByHash: { ...loaderPathsByHash },
1170
+ loaderParamsByHash: { ...loaderParamsByHash },
890
1171
  errorLoader: errorLoaderRef.v,
891
1172
  errorLayouts: errorLoaderRef.layouts,
892
1173
  notFoundLoader: notFoundLoaderRef.v,
@@ -898,7 +1179,7 @@ var deepFreeze = (obj) => {
898
1179
  done = found.done;
899
1180
  node = found.next;
900
1181
  const matchedPathname = `/${parts.slice(0, found.done ? len : i + 1).join("/")}/`;
901
- collectNodeMeta(node, found.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, matchedPathname);
1182
+ collectNodeMeta(node, found.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, matchedPathname, loaderParamsByHash, { ...params });
902
1183
  if (node._M) groupNodes.push({
903
1184
  node,
904
1185
  depth: layouts.length
@@ -908,7 +1189,7 @@ var deepFreeze = (obj) => {
908
1189
  if (!node._I && node._G == null) {
909
1190
  const indexResult = findIndexNode(node);
910
1191
  if (indexResult) {
911
- collectNodeMeta(indexResult.target, indexResult.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, pathname);
1192
+ collectNodeMeta(indexResult.target, indexResult.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, pathname, loaderParamsByHash, { ...params });
912
1193
  node = indexResult.target;
913
1194
  }
914
1195
  }
@@ -919,12 +1200,13 @@ var deepFreeze = (obj) => {
919
1200
  const paramName = next._P;
920
1201
  params[paramName] = "";
921
1202
  routeParts.push(`[...${paramName}]`);
922
- collectNodeMeta(next, restInfo.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, pathname);
1203
+ collectNodeMeta(next, restInfo.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, pathname, loaderParamsByHash, { ...params });
923
1204
  node = next;
924
1205
  }
925
1206
  }
926
1207
  }
927
- const loaders = matched && (done || i >= len) ? resolveLoaders(root, node, layouts) : void 0;
1208
+ const resolved = matched && (done || i >= len) ? resolveLoaders(root, node, layouts, params) : void 0;
1209
+ const loaders = resolved?.loaders;
928
1210
  if (!loaders && restFallback) {
929
1211
  const fb = restFallback;
930
1212
  const fbParams = {
@@ -934,6 +1216,7 @@ var deepFreeze = (obj) => {
934
1216
  const fbRouteParts = [...fb.routeParts, `[...${fb.paramName}]`];
935
1217
  const fbLayouts = [...fb.layouts];
936
1218
  const fbLoaderPathsByHash = { ...fb.loaderPathsByHash };
1219
+ const fbLoaderParamsByHash = { ...fb.loaderParamsByHash };
937
1220
  const fbErrorRef = {
938
1221
  v: fb.errorLoader,
939
1222
  layouts: fb.errorLayouts
@@ -943,20 +1226,22 @@ var deepFreeze = (obj) => {
943
1226
  layouts: fb.notFoundLayouts
944
1227
  };
945
1228
  const fbMenuRef = { v: fb.menuLoader };
946
- const fbLoaderHashes = [];
947
- collectNodeMeta(fb.aNode, fb.groups, fbLayouts, fbErrorRef, fbNotFoundRef, fbMenuRef, fbLoaderHashes, fbLoaderPathsByHash, pathname);
948
- const fbLoaders = resolveLoaders(root, fb.aNode, fbLayouts);
949
- if (fbLoaders) return {
950
- loaders: fbLoaders,
951
- params: fbParams,
952
- routeParts: fbRouteParts,
953
- notFound: false,
954
- routeBundleNames: fb.aNode._B,
955
- loaderHashes: fbLoaderHashes.length > 0 ? fbLoaderHashes : void 0,
956
- loaderPathsByHash: Object.keys(fbLoaderPathsByHash).length > 0 ? fbLoaderPathsByHash : void 0,
957
- menuLoader: fbMenuRef.v,
958
- errorLoader: boundaryChain(fbErrorRef)
959
- };
1229
+ collectNodeMeta(fb.aNode, fb.groups, fbLayouts, fbErrorRef, fbNotFoundRef, fbMenuRef, fbLoaderPathsByHash, pathname, fbLoaderParamsByHash, fbParams);
1230
+ const fallback = resolveLoaders(root, fb.aNode, fbLayouts, fbParams);
1231
+ if (fallback) {
1232
+ collectPageLoaders(fallback, fbLoaderPathsByHash, fbLoaderParamsByHash, pathname, fbParams);
1233
+ return {
1234
+ loaders: fallback.loaders,
1235
+ params: fbParams,
1236
+ routeParts: fbRouteParts,
1237
+ notFound: false,
1238
+ routeBundleNames: fb.aNode._B,
1239
+ loaderPathsByHash: Object.keys(fbLoaderPathsByHash).length > 0 ? fbLoaderPathsByHash : void 0,
1240
+ loaderParamsByHash: Object.keys(fbLoaderParamsByHash).length > 0 ? fbLoaderParamsByHash : void 0,
1241
+ menuLoader: fbMenuRef.v,
1242
+ errorLoader: boundaryChain(fbErrorRef)
1243
+ };
1244
+ }
960
1245
  errorLoaderRef.v = fbErrorRef.v;
961
1246
  errorLoaderRef.layouts = fbErrorRef.layouts;
962
1247
  notFoundLoaderRef.v = fbNotFoundRef.v;
@@ -993,28 +1278,21 @@ var deepFreeze = (obj) => {
993
1278
  routeParts,
994
1279
  notFound: true,
995
1280
  routeBundleNames: void 0,
996
- loaderHashes: void 0,
997
1281
  loaderPathsByHash: void 0,
1282
+ loaderParamsByHash: void 0,
998
1283
  menuLoader: menuLoaderRef.v,
999
1284
  errorLoader: boundaryChain(errorLoaderRef)
1000
1285
  };
1001
1286
  }
1002
- if (node._R) {
1003
- loaderHashes.push(...node._R);
1004
- const matchedPathname = ensureSlash(pathname);
1005
- for (let i = 0; i < node._R.length; i++) {
1006
- const hash = node._R[i];
1007
- loaderPathsByHash[hash] = matchedPathname;
1008
- }
1009
- }
1287
+ collectPageLoaders(resolved, loaderPathsByHash, loaderParamsByHash, pathname, params);
1010
1288
  return {
1011
1289
  loaders,
1012
1290
  params,
1013
1291
  routeParts,
1014
1292
  notFound: false,
1015
1293
  routeBundleNames: node._B,
1016
- loaderHashes: loaderHashes.length > 0 ? loaderHashes : void 0,
1017
1294
  loaderPathsByHash: Object.keys(loaderPathsByHash).length > 0 ? loaderPathsByHash : void 0,
1295
+ loaderParamsByHash: Object.keys(loaderParamsByHash).length > 0 ? loaderParamsByHash : void 0,
1018
1296
  menuLoader: menuLoaderRef.v,
1019
1297
  errorLoader: boundaryChain(errorLoaderRef)
1020
1298
  };
@@ -1033,39 +1311,42 @@ var loadModule = (moduleLoader, pendingLoads, moduleSetter, cacheModules) => {
1033
1311
  }
1034
1312
  }
1035
1313
  };
1036
- var loadRoute = async (routes, cacheModules, pathname) => {
1037
- const { loaders, params, routeParts, notFound, routeBundleNames, loaderHashes, loaderPathsByHash, menuLoader, errorLoader } = matchRouteTree(routes, pathname);
1038
- const routeName = "/" + routeParts.join("/");
1039
- const modules = new Array(loaders.length);
1040
- const pendingLoads = [];
1041
- for (let i = 0; i < loaders.length; i++) {
1042
- const moduleLoader = loaders[i];
1043
- loadModule(moduleLoader, pendingLoads, (routeModule) => modules[i] = routeModule, cacheModules);
1314
+ function collectPageLoaders({ node, loaderPaths, loaderParams }, paths, pathParams, pathname, params) {
1315
+ if (loaderPaths) {
1316
+ for (const hash in paths) {
1317
+ delete paths[hash];
1318
+ delete pathParams[hash];
1319
+ }
1320
+ Object.assign(paths, loaderPaths);
1321
+ Object.assign(pathParams, loaderParams);
1044
1322
  }
1045
- let menu = void 0;
1046
- loadModule(menuLoader, pendingLoads, (menuModule) => menu = menuModule?.default, cacheModules);
1047
- if (pendingLoads.length > 0) await Promise.all(pendingLoads);
1048
- return {
1049
- $routeName$: routeName,
1050
- $params$: params,
1051
- $mods$: modules,
1052
- $menu$: deepFreeze(menu),
1053
- $routeBundleNames$: routeBundleNames,
1054
- $notFound$: notFound,
1055
- $errorLoader$: errorLoader,
1056
- $loaders$: loaderHashes,
1057
- $loaderPaths$: loaderPathsByHash
1058
- };
1059
- };
1060
- /** Gets an absolute url path string (url.pathname + url.search + url.hash) */ var toPath = (url) => url.pathname + url.search + url.hash;
1061
- /** Create a URL from a string and baseUrl */ var toUrl = (url, baseUrl) => new URL(url, baseUrl.href);
1062
- /** Checks only if the origins are the same. */ var isSameOrigin = (a, b) => a.origin === b.origin;
1063
- /** Checks only if the pathnames are the same for the URLs (doesn't include search and hash) */ var isSamePathname = ({ pathname: a }, { pathname: b }) => {
1323
+ const pageHashes = node._D ?? [];
1324
+ if (Array.isArray(node._I)) {
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 };
1333
+ }
1334
+ }
1335
+ /** Gets an absolute url path string (url.pathname + url.search + url.hash) */
1336
+ var toPath = (url) => url.pathname + url.search + url.hash;
1337
+ /** Create a URL from a string and baseUrl */
1338
+ var toUrl = (url, baseUrl) => new URL(url, baseUrl.href);
1339
+ /** Checks only if the origins are the same. */
1340
+ var isSameOrigin = (a, b) => a.origin === b.origin;
1341
+ /** Checks only if the pathnames are the same for the URLs (doesn't include search and hash) */
1342
+ var isSamePathname = ({ pathname: a }, { pathname: b }) => {
1064
1343
  const lDiff = Math.abs(a.length - b.length);
1065
1344
  return lDiff === 0 ? a === b : lDiff === 1 && ensureSlash(a) === ensureSlash(b);
1066
1345
  };
1067
- /** Checks only if the search query strings are the same for the URLs */ var isSameSearchQuery = (a, b) => a.search === b.search;
1068
- /** Checks only if the pathname + search are the same for the URLs. */ var isSamePath = (a, b) => isSameSearchQuery(a, b) && isSamePathname(a, b);
1346
+ /** Checks only if the search query strings are the same for the URLs */
1347
+ var isSameSearchQuery = (a, b) => a.search === b.search;
1348
+ /** Checks only if the pathname + search are the same for the URLs. */
1349
+ var isSamePath = (a, b) => isSameSearchQuery(a, b) && isSamePathname(a, b);
1069
1350
  var getClientNavPath = (props, baseUrl) => {
1070
1351
  const href = props.href;
1071
1352
  if (typeof href === "string" && typeof props.target !== "string" && !props.reload) try {
@@ -1084,40 +1365,12 @@ var shouldPreload = (clientNavPath, currentLoc) => {
1084
1365
  var isPromise = (value) => {
1085
1366
  return value && typeof value.then === "function";
1086
1367
  };
1087
- var mergeArray = (existingArr, newArr) => {
1088
- if (Array.isArray(newArr)) for (const newItem of newArr) {
1089
- if (typeof newItem.key === "string") {
1090
- const existingIndex = existingArr.findIndex((i) => i.key === newItem.key);
1091
- if (existingIndex > -1) {
1092
- existingArr[existingIndex] = newItem;
1093
- continue;
1094
- }
1095
- }
1096
- existingArr.push(newItem);
1097
- }
1098
- };
1099
- var resolveDocumentHead = (resolvedHead, updatedHead) => {
1100
- if (typeof updatedHead.title === "string") resolvedHead.title = updatedHead.title;
1101
- mergeArray(resolvedHead.meta, updatedHead.meta);
1102
- mergeArray(resolvedHead.links, updatedHead.links);
1103
- mergeArray(resolvedHead.styles, updatedHead.styles);
1104
- mergeArray(resolvedHead.scripts, updatedHead.scripts);
1105
- Object.assign(resolvedHead.frontmatter, updatedHead.frontmatter);
1106
- };
1107
- var createDocumentHead = (defaults, manifestHash) => ({
1108
- title: defaults?.title || "",
1109
- meta: [...defaults?.meta || []],
1110
- links: [...defaults?.links || []],
1111
- styles: [...defaults?.styles || []],
1112
- scripts: [...defaults?.scripts || []],
1113
- frontmatter: { ...defaults?.frontmatter },
1114
- manifestHash: manifestHash || "dev"
1115
- });
1116
1368
  /**
1117
1369
  * Resolve the full route config (head + eTag + cacheKey) from all content modules. Uses the same
1118
1370
  * resolution rules as DocumentHead: objects are merged immediately, functions are queued and
1119
1371
  * executed inner-before-outer.
1120
- */ var resolveRouteConfig = (resolveValue, routeLocation, contentModules, locale, status, defaults) => withLocale(locale, () => {
1372
+ */
1373
+ var resolveRouteConfig = (resolveValue, routeLocation, contentModules, locale, status, defaults) => withLocale(locale, () => {
1121
1374
  const head = createDocumentHead(defaults);
1122
1375
  let eTag;
1123
1376
  let cacheKey;
@@ -1190,13 +1443,43 @@ var createDocumentHead = (defaults, manifestHash) => ({
1190
1443
  *
1191
1444
  * Signal values may throw a promise on the client when still loading — callers should wrap this in
1192
1445
  * `retryOnPromise` to handle that.
1193
- */ var resolveHead = (actionData, loaderState, routeLocation, contentModules, locale, defaults) => {
1194
- const getData = (loaderOrAction) => {
1446
+ */
1447
+ var resolveHead = (actionData, loaderState, routeLocation, contentModules, locale, defaults) => {
1448
+ const getData = ((loaderOrAction) => {
1195
1449
  const id = loaderOrAction.__id;
1196
1450
  const signal = loaderState?.[id];
1197
1451
  if (signal) return signal.value;
1198
1452
  if (actionData?.action === id) return actionData.actionResult;
1199
- };
1453
+ });
1200
1454
  return resolveRouteConfig(getData, routeLocation, contentModules, locale, actionData?.status ?? 200, defaults).head;
1201
1455
  };
1202
- export { routeLoader$ as A, RouteLoaderCtxContext as B, getRouteLoaderValues as C, loadRouteLoader as D, isImmutableLoader as E, ContentContext as F, QACTION_KEY as G, RouteNavigateContext as H, ContentInternalContext as I, Q_ROUTE as J, QDATA_KEY as K, DocumentHeadContext as L, setLoaderSignalValue as M, setRouteLoaders as N, matchesRouteLoaderId as O, updateRouteLoaderPaths as P, HttpStatusContext as R, getRouteLoaderResponse as S, invalidateNavRouteLoaders as T, RoutePreventNavigateContext as U, RouteLocationContext as V, RouteStateContext as W, _asyncRequestStore as Y, fetchRouteLoaderData as _, isPromise as a, getRequestEvent as b, shouldPreload as c, loadRoute as d, FULLPATH_HEADER as f, ensureRouteLoaderSignals as g, clearRouteLoaderData as h, getClientNavPath as i, routeLoaderQrl as j, resolveRouteLoaderByHash as k, toPath as l, clearNavFetchCache as m, resolveHead as n, isSameOrigin as o, ROUTE_PATH_HEADER as p, QFN_KEY as q, resolveRouteConfig as r, isSamePath as s, createDocumentHead as t, toUrl as u, getLoaderRequestEvent as v, getRouteLoaders as w, getRouteLoaderCtx as x, getModuleRouteLoaders as y, RouteActionContext as z };
1456
+ var resolveDocumentHead = (resolvedHead, updatedHead) => {
1457
+ if (typeof updatedHead.title === "string") resolvedHead.title = updatedHead.title;
1458
+ mergeArray(resolvedHead.meta, updatedHead.meta);
1459
+ mergeArray(resolvedHead.links, updatedHead.links);
1460
+ mergeArray(resolvedHead.styles, updatedHead.styles);
1461
+ mergeArray(resolvedHead.scripts, updatedHead.scripts);
1462
+ Object.assign(resolvedHead.frontmatter, updatedHead.frontmatter);
1463
+ };
1464
+ var mergeArray = (existingArr, newArr) => {
1465
+ if (Array.isArray(newArr)) for (const newItem of newArr) {
1466
+ if (typeof newItem.key === "string") {
1467
+ const existingIndex = existingArr.findIndex((i) => i.key === newItem.key);
1468
+ if (existingIndex > -1) {
1469
+ existingArr[existingIndex] = newItem;
1470
+ continue;
1471
+ }
1472
+ }
1473
+ existingArr.push(newItem);
1474
+ }
1475
+ };
1476
+ var createDocumentHead = (defaults, manifestHash) => ({
1477
+ title: defaults?.title || "",
1478
+ meta: [...defaults?.meta || []],
1479
+ links: [...defaults?.links || []],
1480
+ styles: [...defaults?.styles || []],
1481
+ scripts: [...defaults?.scripts || []],
1482
+ frontmatter: { ...defaults?.frontmatter },
1483
+ manifestHash: manifestHash || "dev"
1484
+ });
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 };