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

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 (38) hide show
  1. package/lib/adapters/azure-swa/vite/index.mjs +2 -1
  2. package/lib/adapters/bun-server/vite/index.mjs +2 -1
  3. package/lib/adapters/cloud-run/vite/index.mjs +2 -1
  4. package/lib/adapters/cloudflare-pages/vite/index.mjs +2 -1
  5. package/lib/adapters/deno-server/vite/index.mjs +2 -1
  6. package/lib/adapters/netlify-edge/vite/index.mjs +2 -1
  7. package/lib/adapters/node-server/vite/index.mjs +2 -1
  8. package/lib/adapters/shared/vite/index.mjs +35 -31
  9. package/lib/adapters/ssg/vite/index.mjs +4 -2
  10. package/lib/adapters/vercel-edge/vite/index.mjs +18 -15
  11. package/lib/chunks/fs.mjs +26 -23
  12. package/lib/chunks/head.qwik.mjs +611 -445
  13. package/lib/chunks/http-error.qwik.mjs +8 -7
  14. package/lib/chunks/pathname.mjs +9 -8
  15. package/lib/chunks/request-path.mjs +8 -4
  16. package/lib/chunks/routes.mjs +14 -14
  17. package/lib/chunks/server-error.mjs +11 -5
  18. package/lib/chunks/{url.mjs → static-file.mjs} +12 -2
  19. package/lib/chunks/system.mjs +18 -17
  20. package/lib/chunks/user-response.mjs +419 -323
  21. package/lib/chunks/worker-thread.mjs +87 -86
  22. package/lib/index.d.ts +47 -59
  23. package/lib/index.qwik.mjs +550 -496
  24. package/lib/middleware/aws-lambda/index.mjs +6 -3
  25. package/lib/middleware/azure-swa/index.mjs +4 -2
  26. package/lib/middleware/bun/index.mjs +12 -12
  27. package/lib/middleware/cloudflare-pages/index.mjs +19 -4
  28. package/lib/middleware/deno/index.mjs +12 -12
  29. package/lib/middleware/firebase/index.mjs +4 -2
  30. package/lib/middleware/netlify-edge/index.mjs +4 -2
  31. package/lib/middleware/node/index.mjs +16 -15
  32. package/lib/middleware/request-handler/index.mjs +35 -24
  33. package/lib/middleware/vercel-edge/index.mjs +4 -2
  34. package/lib/service-worker/index.mjs +2 -1
  35. package/lib/ssg/index.mjs +20 -16
  36. package/lib/vite/index.d.ts +1 -1
  37. package/lib/vite/index.mjs +596 -586
  38. package/package.json +5 -5
@@ -1,11 +1,13 @@
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 { _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";
7
6
  import { isServer as isServer$1 } from "@qwik.dev/core/build";
8
- /** @internal */ var _asyncRequestStore;
7
+ globalThis.__NO_TRAILING_SLASH__;
8
+ var basePathname = globalThis.__QWIK_ROUTER_BASE_PATHNAME__ ?? "/";
9
+ /** @internal */
10
+ var _asyncRequestStore;
9
11
  if (isServer$1) {
10
12
  const AsyncLocalStorage = _getAsyncLocalStorage();
11
13
  if (AsyncLocalStorage) _asyncRequestStore = new AsyncLocalStorage();
@@ -14,8 +16,11 @@ if (isServer$1) {
14
16
  var QACTION_KEY = "qaction";
15
17
  var QFN_KEY = "qfunc";
16
18
  var QDATA_KEY = "qdata";
17
- /** @public */ var Q_ROUTE = "q:route";
18
- var DEFAULT_LOADERS_SERIALIZATION_STRATEGY = globalThis.__DEFAULT_LOADERS_SERIALIZATION_STRATEGY__ || "never";
19
+ /** @public */
20
+ var Q_ROUTE = "q:route";
21
+ function DEFAULT_LOADERS_SERIALIZATION_STRATEGY() {
22
+ return globalThis.__DEFAULT_LOADERS_SERIALIZATION_STRATEGY__ || "never";
23
+ }
19
24
  var RouteStateContext = /*#__PURE__*/ createContextId("qr-s");
20
25
  var RouteLoaderCtxContext = /*#__PURE__*/ createContextId("qr-lc");
21
26
  var ContentContext = /*#__PURE__*/ createContextId("qr-c");
@@ -31,23 +36,30 @@ var HttpStatusContext = /*#__PURE__*/ createContextId("qr-hs");
31
36
  * They automatically update when the route changes on the client.
32
37
  *
33
38
  * They are represented by a ComputedSignal.
34
- */ var REQUEST_ROUTE_LOADER_STATE = "@routeLoaderState";
39
+ */
40
+ var REQUEST_ROUTE_LOADER_STATE = "@routeLoaderState";
35
41
  var REQUEST_LOADER_PATHS_STORE = "@loaderPathsStore";
36
42
  var REQUEST_ROUTE_LOADERS = "@routeLoaders";
37
43
  var REQUEST_ROUTE_LOADER_PROMISES = "@routeLoaderPromises";
38
44
  var REQUEST_ROUTE_LOADER_EVENTS = "@routeLoaderEvents";
39
45
  var REQUEST_ROUTE_LOADER_ROOT_EVENT = "@routeLoaderRootEvent";
40
46
  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";
47
+ /** Header name sent by client to tell the server the actual page URL for loader requests. */
48
+ var FULLPATH_HEADER = "X-Qwik-fullpath";
49
+ /** Dev route hint that preserves the loader URL. */
50
+ var ROUTE_PATH_HEADER = "X-Qwik-route-path";
43
51
  /**
44
52
  * Per-navigation fetch dedupe: shares in-flight and completed fetches (hover prefetch → click nav)
45
53
  * and prevents repeat hover fetches. Cleared after each navigation kicks off its loader fetches;
46
54
  * across navigations the browser HTTP cache is the freshness authority.
47
- */ var navFetchCache = /* @__PURE__ */ new Map();
48
- var clearNavFetchCache = () => navFetchCache.clear();
55
+ */
56
+ var navFetchCache = /* @__PURE__ */ new Map();
57
+ var clearNavFetchCache = () => {
58
+ navFetchCache = /* @__PURE__ */ new Map();
59
+ };
49
60
  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) => {
61
+ /** We don't have aborts when preloading so we just pretend we do */
62
+ var wrapWithAbort = (promise, signal) => {
51
63
  if (signal.aborted) return Promise.reject(signal.reason);
52
64
  return new Promise((resolve, reject) => {
53
65
  const abort = () => reject(signal.reason);
@@ -55,14 +67,79 @@ var isRedirectStatus = (status) => status >= 300 && status < 400;
55
67
  promise.finally(() => signal.removeEventListener("abort", abort)).then(resolve, reject);
56
68
  });
57
69
  };
70
+ var clientRouteLoaders = /* @__PURE__ */ new WeakMap();
71
+ function getClientRouteLoaders(ctx, pageUrl) {
72
+ let client = clientRouteLoaders.get(ctx);
73
+ if (!client) {
74
+ const current = {
75
+ requests: /* @__PURE__ */ new Map(),
76
+ paths: { ...ctx.loaderPaths },
77
+ pageUrl: pageUrl || location.href
78
+ };
79
+ client = {
80
+ current,
81
+ committed: current
82
+ };
83
+ clientRouteLoaders.set(ctx, client);
84
+ }
85
+ return client;
86
+ }
87
+ function getLoaderRequest(ctx, state, id, hash1) {
88
+ return untrack(() => {
89
+ const client = getClientRouteLoaders(ctx);
90
+ const { requests, paths, pageUrl } = client.current;
91
+ let request = requests.get(state[id]);
92
+ if (!request) {
93
+ const path = paths[id] || paths[hash1];
94
+ request = {
95
+ routePath: path || new URL(pageUrl).pathname,
96
+ pageUrl,
97
+ active: client.navCount === void 0 || !!path,
98
+ hash: hash1
99
+ };
100
+ requests.set(state[id], request);
101
+ }
102
+ return request;
103
+ });
104
+ }
105
+ function assertCurrentLoaderRequest(ctx, signal, request, abortSignal) {
106
+ abortSignal.throwIfAborted();
107
+ if (!request.active || clientRouteLoaders.get(ctx)?.current.requests.get(signal) !== request) throw new DOMException(isDev ? "Route loader navigation was superseded" : "", "AbortError");
108
+ }
58
109
  var getRouteLoaderValueStateKey = (loaderId) => `${ROUTE_LOADER_VALUE_PREFIX}${loaderId}`;
110
+ var ServerRouteLoaderCapture = class {
111
+ hash;
112
+ qrl;
113
+ validators;
114
+ blockSSR;
115
+ requestEv;
116
+ constructor(hash2, qrl1, validators1, blockSSR1, requestEv1) {
117
+ this.hash = hash2;
118
+ this.qrl = qrl1;
119
+ this.validators = validators1;
120
+ this.blockSSR = blockSSR1;
121
+ this.requestEv = requestEv1;
122
+ }
123
+ load() {
124
+ const requestEv2 = this.requestEv;
125
+ if (!requestEv2) throw new Error("Unable to determine the current RequestEvent.");
126
+ const values = getRouteLoaderValues(requestEv2);
127
+ if (this.hash in values) return values[this.hash];
128
+ const ev = this.blockSSR ? requestEv2 : detachResponseFromEvent(requestEv2);
129
+ return loadRouteLoaderByQrl(this.hash, this.qrl, this.validators, ev);
130
+ }
131
+ [SerializerSymbol]() {
132
+ return this.hash;
133
+ }
134
+ };
59
135
  var isRequestEvent = (value) => !!value && typeof value === "object" && Object.prototype.hasOwnProperty.call(value, "sharedMap") && Object.prototype.hasOwnProperty.call(value, "cookie");
60
136
  var isLoaderInternal = (value) => typeof value === "function" && value.__brand === "server_loader";
61
137
  /**
62
138
  * Fetch a single loader's raw JSON response from the server.
63
139
  *
64
140
  * URL pattern: `{basePath}{routePath}/q-loader-{loaderId}.{manifestHash}.json`
65
- */ var fetchRouteLoaderData = async (loaderId, routePath, manifestHash, opts) => {
141
+ */
142
+ var fetchRouteLoaderData = async (loaderId, routePath, manifestHash, opts) => {
66
143
  if (!routePath) return;
67
144
  let resolvedPath = routePath;
68
145
  const basePath = opts?.basePath ?? "/";
@@ -77,40 +154,119 @@ var isLoaderInternal = (value) => typeof value === "function" && value.__brand =
77
154
  else if (isDev) headers[ROUTE_PATH_HEADER] = pageUrl.pathname;
78
155
  }
79
156
  const cacheKey = `${url}\n${headers["X-Qwik-fullpath"] ?? ""}`;
157
+ const cache = navFetchCache;
80
158
  if (!opts?.ignoreCache) {
81
- const entry = navFetchCache.get(cacheKey);
159
+ const entry = cache.get(cacheKey);
82
160
  if (entry) return opts?.signal ? wrapWithAbort(entry, opts.signal) : entry;
83
161
  }
84
162
  const request = async () => {
163
+ opts?.signal?.throwIfAborted();
85
164
  const response = await fetch(url, {
86
165
  signal: opts?.signal,
87
166
  cache: opts?.ignoreCache ? "reload" : "default",
88
167
  headers
89
168
  });
169
+ opts?.signal?.throwIfAborted();
90
170
  if (response.redirected) return { r: response.url };
91
171
  if (isRedirectStatus(response.status)) {
92
172
  const location1 = response.headers.get("Location");
93
173
  if (location1) return { r: location1 };
94
174
  }
95
175
  if (!response.ok) return;
96
- return { raw: await response.text() };
176
+ const raw = await response.text();
177
+ opts?.signal?.throwIfAborted();
178
+ return { raw };
97
179
  };
98
180
  if (opts?.ignoreCache) return request();
99
181
  if (opts?.signal) return request().then((value) => {
100
- if (value !== void 0 && !navFetchCache.has(cacheKey)) navFetchCache.set(cacheKey, Promise.resolve(value));
182
+ opts.signal.throwIfAborted();
183
+ if (value !== void 0 && !cache.has(cacheKey)) cache.set(cacheKey, Promise.resolve(value));
101
184
  return value;
102
185
  });
103
186
  const promise = request().then((value) => {
104
- if (value === void 0) navFetchCache.delete(cacheKey);
187
+ if (value === void 0) cache.delete(cacheKey);
105
188
  return value;
106
189
  }, (err) => {
107
- navFetchCache.delete(cacheKey);
190
+ cache.delete(cacheKey);
108
191
  throw err;
109
192
  });
110
- navFetchCache.set(cacheKey, promise);
193
+ cache.set(cacheKey, promise);
111
194
  return promise;
112
195
  };
113
- /** Build a sorted, stable search string from only the allowed param names. */ var filterSearchParams = (params, allowed) => {
196
+ var createRouteLoaderSignal = (loader, routeLoaderCtx, state, requestEv3) => {
197
+ const id = loader.__id;
198
+ const stateValues = state;
199
+ const resumeValueKey = getRouteLoaderValueStateKey(id);
200
+ const capture = isServer ? new ServerRouteLoaderCapture(id, loader.__qrl, loader.__validators, loader.__blockSSR, requestEv3 ?? useServerData("qwikrouter")?.ev) : id;
201
+ const searchFilter = loader.__search;
202
+ const loaderHash = isDev ? loader.__qrl.getHash() : id;
203
+ 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];
205
+ if (!!ctx.info && typeof ctx.info === "object" && "__v" in ctx.info) {
206
+ const value = ctx.info.__v;
207
+ if (!isServer && resumeValueKey in stateValues) stateValues[resumeValueKey] = value;
208
+ lastFetch.raw = void 0;
209
+ return value;
210
+ }
211
+ if (isServer) return capture.load();
212
+ const request = getLoaderRequest(routeLoaderCtx, state, id, loaderHash);
213
+ if (!request.active) {
214
+ if (ctx.previous === void 0) return new Promise((_, reject) => {
215
+ ctx.abortSignal.throwIfAborted();
216
+ ctx.abortSignal.addEventListener("abort", () => reject(ctx.abortSignal.reason), { once: true });
217
+ });
218
+ return ctx.previous;
219
+ }
220
+ const pageUrl = new URL(request.pageUrl);
221
+ const mHash = untrack(() => routeLoaderCtx.manifestHash) || "dev";
222
+ const needsResumeFetch = stateValues[resumeValueKey] === _UNINITIALIZED;
223
+ let fetchUrl = pageUrl;
224
+ if (searchFilter) {
225
+ fetchUrl = new URL(pageUrl.href);
226
+ fetchUrl.search = filterSearchParams(pageUrl.searchParams, searchFilter);
227
+ }
228
+ const result = await fetchRouteLoaderData(id, request.routePath, mHash, {
229
+ pageUrl: fetchUrl,
230
+ basePath: basePathname,
231
+ ignoreCache: ctx.info === true,
232
+ signal: ctx.abortSignal
233
+ });
234
+ assertCurrentLoaderRequest(routeLoaderCtx, state[id], request, ctx.abortSignal);
235
+ if (!result) throw new Error(`Loader ${id} returned empty response`);
236
+ let response;
237
+ if (result.raw === void 0) response = result;
238
+ else {
239
+ if (result.raw === lastFetch.raw && ctx.previous !== void 0) return ctx.previous;
240
+ response = await _deserialize(result.raw);
241
+ if (!response) throw new Error(`Loader ${id} returned empty response`);
242
+ }
243
+ assertCurrentLoaderRequest(routeLoaderCtx, state[id], request, ctx.abortSignal);
244
+ if (response.r) {
245
+ const goto = routeLoaderCtx.goto;
246
+ if (goto) goto(response.r, { replaceState: true });
247
+ else location.href = response.r;
248
+ return ctx.previous;
249
+ }
250
+ if (response.e) throw response.e;
251
+ lastFetch.raw = result.raw;
252
+ if (needsResumeFetch) stateValues[resumeValueKey] = response.d;
253
+ return response.d;
254
+ }, "createRouteLoaderSignal_signal_createComputed_4s4ZDCYhDOA", [
255
+ capture,
256
+ id,
257
+ {},
258
+ loaderHash,
259
+ resumeValueKey,
260
+ routeLoaderCtx,
261
+ searchFilter,
262
+ state,
263
+ stateValues
264
+ ]), { serializationStrategy: loader.__serializationStrategy });
265
+ _markSignalAsExternallyOwned(signal);
266
+ return signal;
267
+ };
268
+ /** Build a sorted, stable search string from only the allowed param names. */
269
+ var filterSearchParams = (params, allowed) => {
114
270
  const filtered = new URLSearchParams();
115
271
  for (let i = 0; i < allowed.length; i++) {
116
272
  const name = allowed[i];
@@ -120,23 +276,23 @@ var isLoaderInternal = (value) => typeof value === "function" && value.__brand =
120
276
  filtered.sort();
121
277
  return filtered.toString() ? `?${filtered.toString()}` : "";
122
278
  };
123
- var getLoaderOptions = (rest) => {
279
+ function getLoaderOptions(rest) {
124
280
  let id;
125
- let serializationStrategy = DEFAULT_LOADERS_SERIALIZATION_STRATEGY;
281
+ let serializationStrategy = DEFAULT_LOADERS_SERIALIZATION_STRATEGY();
126
282
  let cacheControl;
127
283
  let eTag;
128
284
  let cacheKey;
129
285
  let search;
130
- let blockSSR = true;
131
- const validators = [];
286
+ let blockSSR2 = true;
287
+ const validators2 = [];
132
288
  if (rest.length === 1) {
133
289
  const options = rest[0];
134
290
  if (options && typeof options === "object") {
135
- if ("validate" in options) validators.push(options);
291
+ if ("validate" in options) validators2.push(options);
136
292
  else {
137
293
  if (options.id) id = options.id;
138
294
  if (options.serializationStrategy) serializationStrategy = options.serializationStrategy;
139
- if (options.validation) validators.push(...options.validation);
295
+ if (options.validation) validators2.push(...options.validation);
140
296
  if ("cacheControl" in options) cacheControl = options.cacheControl;
141
297
  if ("eTag" in options) eTag = options.eTag;
142
298
  if ("cacheKey" in options) cacheKey = options.cacheKey;
@@ -144,77 +300,75 @@ var getLoaderOptions = (rest) => {
144
300
  else if (globalThis.__STRICT_LOADERS__) search = [];
145
301
  if (options.blockSSR === false) {
146
302
  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;
303
+ blockSSR2 = false;
148
304
  }
149
305
  }
150
306
  }
151
- } else if (rest.length > 1) validators.push(...rest.filter(Boolean));
307
+ } else if (rest.length > 1) validators2.push(...rest.filter(Boolean));
152
308
  return {
153
309
  id,
154
- validators: validators.reverse(),
310
+ validators: validators2.reverse(),
155
311
  serializationStrategy,
156
312
  cacheControl,
157
313
  eTag,
158
314
  cacheKey,
159
315
  search,
160
- blockSSR
316
+ blockSSR: blockSSR2
161
317
  };
162
- };
318
+ }
163
319
  /**
164
320
  * Returns the current RequestEvent if possible. Only usable on the server, and only during request
165
321
  * processing.
166
322
  *
167
323
  * @public
168
- */ var getRequestEvent = (thisArg) => {
324
+ */
325
+ var getRequestEvent = (thisArg) => {
169
326
  if (!isServer) throw new Error("getRequestEvent() can only be used on the server.");
170
- return _asyncRequestStore?.getStore() || [thisArg, _getContextEvent()].find(isRequestEvent);
327
+ return _asyncRequestStore?.getStore() ?? (isRequestEvent(thisArg) ? thisArg : void 0);
171
328
  };
172
329
  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);
330
+ /** Get/create the record of pre-loaded loader values (used by middleware before component). */
331
+ function getRouteLoaderValues(requestEv5) {
332
+ let values = requestEv5.sharedMap.get(REQUEST_ROUTE_LOADER_VALUES);
175
333
  if (!values) {
176
334
  values = {};
177
- requestEv.sharedMap.set(REQUEST_ROUTE_LOADER_VALUES, values);
335
+ requestEv5.sharedMap.set(REQUEST_ROUTE_LOADER_VALUES, values);
178
336
  }
179
337
  return values;
180
338
  }
181
- function getRouteLoaderPromises(requestEv) {
182
- let promises = requestEv.sharedMap.get(REQUEST_ROUTE_LOADER_PROMISES);
339
+ function getRouteLoaderPromises(requestEv6) {
340
+ let promises = requestEv6.sharedMap.get(REQUEST_ROUTE_LOADER_PROMISES);
183
341
  if (!promises) {
184
342
  promises = {};
185
- requestEv.sharedMap.set(REQUEST_ROUTE_LOADER_PROMISES, promises);
343
+ requestEv6.sharedMap.set(REQUEST_ROUTE_LOADER_PROMISES, promises);
186
344
  }
187
345
  return promises;
188
346
  }
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);
347
+ /** Clear loader state before rendering a route-independent error boundary. */
348
+ function clearRouteLoaderData(requestEv7) {
349
+ requestEv7.sharedMap.delete(REQUEST_ROUTE_LOADER_STATE);
350
+ requestEv7.sharedMap.delete(REQUEST_ROUTE_LOADER_VALUES);
351
+ requestEv7.sharedMap.delete(REQUEST_ROUTE_LOADER_PROMISES);
352
+ requestEv7.sharedMap.delete(REQUEST_ROUTE_LOADER_EVENTS);
353
+ requestEv7.sharedMap.delete(REQUEST_ROUTE_LOADER_ROOT_EVENT);
354
+ requestEv7.sharedMap.delete(REQUEST_ROUTE_LOADERS);
196
355
  }
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);
356
+ /** Store the route loader internals on the request for SSG to read. */
357
+ function setRouteLoaders(requestEv8, loaders) {
358
+ requestEv8.sharedMap.set(REQUEST_ROUTE_LOADERS, loaders);
199
359
  }
200
- /** Get the route loader internals stored on the request. */ function getRouteLoaders(requestEv) {
201
- return requestEv.sharedMap.get(REQUEST_ROUTE_LOADERS) ?? [];
360
+ /** Get the route loader internals stored on the request. */
361
+ function getRouteLoaders(requestEv9) {
362
+ return requestEv9.sharedMap.get(REQUEST_ROUTE_LOADERS) ?? [];
202
363
  }
203
- function getRouteLoaderCtx(requestEv) {
204
- let ctx = requestEv.sharedMap.get(REQUEST_LOADER_PATHS_STORE);
364
+ function getRouteLoaderCtx(requestEv10) {
365
+ let ctx = requestEv10.sharedMap.get(REQUEST_LOADER_PATHS_STORE);
205
366
  if (!ctx) {
206
367
  ctx = { loaderPaths: {} };
207
- requestEv.sharedMap.set(REQUEST_LOADER_PATHS_STORE, ctx);
368
+ requestEv10.sharedMap.set(REQUEST_LOADER_PATHS_STORE, ctx);
208
369
  }
209
370
  return ctx;
210
371
  }
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
- };
218
372
  var getModuleRouteLoaders = (mods) => {
219
373
  const routeLoaders = [];
220
374
  const seen = /* @__PURE__ */ new Map();
@@ -238,22 +392,109 @@ var getModuleRouteLoaders = (mods) => {
238
392
  };
239
393
  /**
240
394
  * 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();
395
+ * navigation skips them unless their request inputs change. Registered on every nav via
396
+ * ensureRouteLoaderSignal, so resumed signals are covered too.
397
+ */
398
+ var immutableLoaderIds = /* @__PURE__ */ new Set();
244
399
  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();
400
+ function abortRouteLoaderNavigation(ctx) {
401
+ for (const [signal, request] of clientRouteLoaders.get(ctx)?.current.requests ?? []) {
402
+ if (signal.untrackedPending) request.isAborted = true;
403
+ signal.abort();
404
+ }
405
+ }
406
+ /** Search-filtered loaders ignore changes to unlisted params. */
407
+ function hasSameListedSearch(previous, next, search) {
408
+ return !!search && previous.pathname === next.pathname && filterSearchParams(previous.searchParams, search) === filterSearchParams(next.searchParams, search);
409
+ }
410
+ function prepareRouteLoaders(mods, state, ctx, paths, pageUrl, previousUrl, navCount, forceIds, navigationKey) {
411
+ const client = getClientRouteLoaders(ctx, previousUrl.href);
412
+ if (navigationKey && client.navigationKey === navigationKey) return ensureRouteLoaderSignals(mods, state, ctx);
413
+ const previous = client.current;
414
+ if (client.navCount === void 0) {
415
+ for (const id in state) if (!id.startsWith(ROUTE_LOADER_VALUE_PREFIX)) getLoaderRequest(ctx, state, id, id);
416
+ }
417
+ client.navCount = navCount;
418
+ client.navigationKey = navigationKey;
419
+ const current = client.current = {
420
+ requests: /* @__PURE__ */ new Map(),
421
+ paths: { ...paths },
422
+ pageUrl: pageUrl.href
423
+ };
424
+ const loaders = ensureRouteLoaderSignals(mods, state, ctx);
425
+ const routeLoaders = /* @__PURE__ */ new Map();
426
+ for (const loader of loaders) {
427
+ routeLoaders.set(loader.__id, loader);
428
+ current.paths[loader.__id] ||= current.paths[loader.__qrl.getHash()] || pageUrl.pathname;
429
+ }
430
+ for (const id in state) {
431
+ if (id.startsWith(ROUTE_LOADER_VALUE_PREFIX)) continue;
432
+ const signal = state[id];
433
+ const old = previous.requests.get(signal);
434
+ const loader = routeLoaders.get(id);
435
+ const hash3 = loader?.__qrl.getHash() || old?.hash;
436
+ const routePath = current.paths[id] || hash3 && current.paths[hash3];
437
+ if (!routePath) {
438
+ if (old) current.requests.set(signal, {
439
+ ...old,
440
+ active: false
441
+ });
442
+ signal.abort();
443
+ continue;
444
+ }
445
+ const keepRequest = old?.active && !old.isAborted && old.routePath === routePath && (old.pageUrl === pageUrl.href ? isImmutableLoader(id) : hasSameListedSearch(new URL(old.pageUrl), pageUrl, loader?.__search));
446
+ current.requests.set(signal, keepRequest ? old : {
447
+ routePath,
448
+ pageUrl: pageUrl.href,
449
+ active: true,
450
+ hash: hash3
451
+ });
452
+ ctx.loaderPaths[id] = routePath;
453
+ if (forceIds === null || forceIds?.some((value) => value === id || value === hash3)) signal.invalidate(true);
454
+ else if (old && !keepRequest) signal.invalidate();
455
+ }
456
+ return loaders;
457
+ }
458
+ function pruneRouteLoaders(state, ctx) {
459
+ const { requests } = clientRouteLoaders.get(ctx).current;
460
+ const paths = {};
461
+ for (const id in state) {
462
+ if (id.startsWith(ROUTE_LOADER_VALUE_PREFIX)) continue;
463
+ const signal = state[id];
464
+ const request = requests.get(signal);
465
+ if (request?.active) paths[id] = request.routePath;
466
+ else {
467
+ signal.$dispose();
468
+ requests.delete(signal);
469
+ delete state[id];
470
+ delete state[getRouteLoaderValueStateKey(id)];
471
+ }
472
+ }
473
+ ctx.loaderPaths = paths;
474
+ }
475
+ function commitRouteLoaders(state, ctx, navCount) {
476
+ const client = clientRouteLoaders.get(ctx);
477
+ if (!client || client.navCount !== navCount) return;
478
+ pruneRouteLoaders(state, ctx);
479
+ client.committed = client.current;
480
+ }
481
+ function restoreRouteLoaders(state, ctx, navCount) {
482
+ const client = clientRouteLoaders.get(ctx);
483
+ if (!client || client.navCount !== navCount) return;
484
+ client.navigationKey = void 0;
485
+ client.current = client.committed;
486
+ pruneRouteLoaders(state, ctx);
487
+ for (const signal of client.current.requests.keys()) signal.invalidate();
488
+ }
489
+ var ensureRouteLoaderSignal = (loader, state, routeLoaderCtx, requestEv11) => {
490
+ if (loader.__cacheControl === "immutable") immutableLoaderIds.add(loader.__id);
491
+ if (isServer && loader.__serializationStrategy === "never") state[getRouteLoaderValueStateKey(loader.__id)] = _UNINITIALIZED;
492
+ return state[loader.__id] ||= createRouteLoaderSignal(loader, routeLoaderCtx, state, requestEv11);
252
493
  };
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;
494
+ var ensureRouteLoaderSignals = (mods, state, routeLoaderCtx, requestEv12) => {
495
+ const loaders = getModuleRouteLoaders(mods);
496
+ for (let i = 0; i < loaders.length; i++) ensureRouteLoaderSignal(loaders[i], state, routeLoaderCtx, requestEv12);
497
+ return loaders;
257
498
  };
258
499
  /**
259
500
  * Inject a pre-loaded value into a ComputedSignal while preserving subscriptions. Delegates to the
@@ -261,20 +502,50 @@ var applyClientRouteLoaderPath = (loaderId, ctx) => {
261
502
  * synchronously and returns the pre-loaded value without fetching. Must go through the core helper
262
503
  * because `$computeIfNeeded$` is mangled in core builds and not directly callable from this
263
504
  * package.
264
- */ var setLoaderSignalValue = (signal, value) => {
505
+ */
506
+ var setLoaderSignalValue = (signal, value) => {
265
507
  _injectAsyncSignalValue(signal, value);
266
508
  };
509
+ var resolveRouteLoaderByHash = (routeLoaders, loaderId) => {
510
+ return routeLoaders.find((loader) => matchesRouteLoaderId(loader, loaderId));
511
+ };
267
512
  var matchesRouteLoaderId = (loader, loaderId) => {
268
513
  return typeof loaderId === "string" && (loader.__id === loaderId || isDev && loader.__qrl.getHash() === loaderId);
269
514
  };
270
- var resolveRouteLoaderByHash = (routeLoaders, loaderId) => {
271
- return routeLoaders.find((loader) => matchesRouteLoaderId(loader, loaderId));
515
+ /** 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);
519
+ if (!result.success) return loaderRequestEv.fail(result.status ?? 500, result.error);
520
+ const resolved = await loaderQrl.call(loaderRequestEv, loaderRequestEv);
521
+ const value = typeof resolved === "function" ? resolved() : resolved;
522
+ throwIfControlFlowSignal(value);
523
+ if (isDev) verifySerializable(value, loaderQrl);
524
+ return value;
272
525
  };
273
- /** @internal */ var getLoaderRequestEvent = (loader, requestEv) => {
274
- let rootRequestEv = requestEv.sharedMap.get(REQUEST_ROUTE_LOADER_ROOT_EVENT);
526
+ var loadRouteLoaderByQrl = (loaderId, loaderQrl, validators4, requestEv14) => {
527
+ const values = getRouteLoaderValues(requestEv14);
528
+ if (loaderId in values) return Promise.resolve(values[loaderId]);
529
+ const promises = getRouteLoaderPromises(requestEv14);
530
+ let promise = promises[loaderId];
531
+ if (!promise) {
532
+ promise = getRouteLoaderData(loaderQrl, validators4, requestEv14).then((value) => {
533
+ values[loaderId] = value;
534
+ return value;
535
+ }, (err) => {
536
+ delete promises[loaderId];
537
+ throw err;
538
+ });
539
+ promises[loaderId] = promise;
540
+ }
541
+ return promise;
542
+ };
543
+ /** @internal */
544
+ var getLoaderRequestEvent = (loader, requestEv15) => {
545
+ let rootRequestEv = requestEv15.sharedMap.get(REQUEST_ROUTE_LOADER_ROOT_EVENT);
275
546
  if (!rootRequestEv) {
276
- rootRequestEv = requestEv;
277
- requestEv.sharedMap.set(REQUEST_ROUTE_LOADER_ROOT_EVENT, rootRequestEv);
547
+ rootRequestEv = requestEv15;
548
+ requestEv15.sharedMap.set(REQUEST_ROUTE_LOADER_ROOT_EVENT, rootRequestEv);
278
549
  }
279
550
  const url = new URL(rootRequestEv.url);
280
551
  const pathname = globalThis.__STRICT_LOADERS__ ? getRouteLoaderCtx(rootRequestEv).loaderPaths[loader.__id] || rootRequestEv.url.pathname : rootRequestEv.url.pathname;
@@ -326,10 +597,11 @@ var resolveRouteLoaderByHash = (routeLoaders, loaderId) => {
326
597
  * are captured locally, so a background (`blockSSR: false`) loader's status/redirect/error surfaces
327
598
  * on its own signal instead of mutating the page response. The originals also call `check()`, which
328
599
  * throws once SSR streaming has started — a background loader must fail on its signal, not crash.
329
- */ var detachResponseFromEvent = (requestEv) => {
600
+ */
601
+ var detachResponseFromEvent = (requestEv16) => {
330
602
  let status = 200;
331
603
  const headers = new Headers();
332
- return Object.create(requestEv, {
604
+ return Object.create(requestEv16, {
333
605
  headers: {
334
606
  value: headers,
335
607
  enumerable: true
@@ -372,174 +644,33 @@ var resolveRouteLoaderByHash = (routeLoaders, loaderId) => {
372
644
  }
373
645
  });
374
646
  };
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);
647
+ var loadRouteLoader = (loader, requestEv17) => {
648
+ const loaderEv = getLoaderRequestEvent(loader, requestEv17);
526
649
  return loadRouteLoaderByQrl(loader.__id, loader.__qrl, loader.__validators, loader.__blockSSR ? loaderEv : detachResponseFromEvent(loaderEv));
527
650
  };
528
- /** Run a loader and wrap the result in a LoaderResponse envelope. Catches redirects/errors. */ var getRouteLoaderResponse = async (loaderQrl, validators, requestEv) => {
651
+ /** Run a loader and wrap the result in a LoaderResponse envelope. Catches redirects/errors. */
652
+ var getRouteLoaderResponse = async (loaderQrl, validators5, requestEv18) => {
529
653
  try {
530
- return { d: await getRouteLoaderData(loaderQrl, validators, requestEv) };
654
+ return { d: await getRouteLoaderData(loaderQrl, validators5, requestEv18) };
531
655
  } catch (err) {
532
656
  if (err instanceof RedirectMessage) {
533
- const location1 = requestEv.headers.get("Location") || "/";
534
- requestEv.headers.delete("Location");
535
- return { r: location1 };
657
+ const location2 = requestEv18.headers.get("Location") || "/";
658
+ requestEv18.headers.delete("Location");
659
+ return { r: location2 };
536
660
  }
537
661
  if (err instanceof ServerError) return { e: err };
538
662
  throw err;
539
663
  }
540
664
  };
541
- /** @internal */ var routeLoaderQrl = (loaderQrl, ...rest) => {
542
- const { id, validators, serializationStrategy, cacheControl, eTag, cacheKey, search, blockSSR } = getLoaderOptions(rest);
665
+ /**
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.
669
+ *
670
+ * @internal
671
+ */
672
+ function routeLoaderQrl(loaderQrl, ...rest) {
673
+ const { id, validators: validators6, serializationStrategy, cacheControl, eTag, cacheKey, search, blockSSR: blockSSR3 } = getLoaderOptions(rest);
543
674
  function loader() {
544
675
  const signal = ensureRouteLoaderSignal(loader, _resolveContextWithoutSequentialScope(RouteStateContext), _resolveContextWithoutSequentialScope(RouteLoaderCtxContext));
545
676
  signal.promise();
@@ -547,17 +678,17 @@ var loadRouteLoader = (loader, requestEv) => {
547
678
  }
548
679
  loader.__brand = "server_loader";
549
680
  loader.__qrl = loaderQrl;
550
- loader.__validators = validators;
681
+ loader.__validators = validators6;
551
682
  loader.__id = id ?? loaderQrl.getHash();
552
683
  loader.__serializationStrategy = serializationStrategy;
553
684
  loader.__cacheControl = cacheControl;
554
685
  loader.__eTag = eTag;
555
686
  loader.__cacheKey = cacheKey;
556
687
  loader.__search = search;
557
- loader.__blockSSR = blockSSR;
688
+ loader.__blockSSR = blockSSR3;
558
689
  Object.freeze(loader);
559
690
  return loader;
560
- };
691
+ }
561
692
  /**
562
693
  * Define a route loader that fetches data before the route renders.
563
694
  *
@@ -584,7 +715,28 @@ var loadRouteLoader = (loader, requestEv) => {
584
715
  * specify an explicit `search` option.
585
716
  *
586
717
  * @public
587
- */ var routeLoader$ = /*#__PURE__*/ implicit$FirstArg(routeLoaderQrl);
718
+ */
719
+ var routeLoader$ = /*#__PURE__*/ implicit$FirstArg(routeLoaderQrl);
720
+ async function runValidators(requestEv19, validators7, data) {
721
+ let lastResult = {
722
+ success: true,
723
+ data
724
+ };
725
+ if (validators7) for (let i = 0; i < validators7.length; i++) {
726
+ lastResult = await validators7[i].validate(requestEv19, data);
727
+ if (!lastResult.success) return lastResult;
728
+ data = lastResult.data;
729
+ }
730
+ return lastResult;
731
+ }
732
+ function verifySerializable(data, qrl2) {
733
+ try {
734
+ _verifySerializable(data, void 0);
735
+ } catch (error) {
736
+ if (error instanceof Error && qrl2.dev) error.loc = qrl2.dev;
737
+ throw error;
738
+ }
739
+ }
588
740
  var deepFreeze = (obj) => {
589
741
  if (obj == null) return obj;
590
742
  Object.getOwnPropertyNames(obj).forEach((prop) => {
@@ -597,53 +749,103 @@ var deepFreeze = (obj) => {
597
749
  });
598
750
  return Object.freeze(obj);
599
751
  };
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) {
752
+ /** LoadRoute() runs in both client and server. */
753
+ var MODULE_CACHE = /*#__PURE__*/ new WeakMap();
754
+ var loadRoute = async (routes, cacheModules, pathname) => {
755
+ const { loaders, params, routeParts, notFound, routeBundleNames, loaderPathsByHash, menuLoader, errorLoader } = matchRouteTree(routes, pathname);
756
+ const routeName = "/" + routeParts.join("/");
757
+ const modules = new Array(loaders.length);
758
+ const pendingLoads = [];
759
+ for (let i = 0; i < loaders.length; i++) {
760
+ const moduleLoader = loaders[i];
761
+ loadModule(moduleLoader, pendingLoads, (routeModule) => modules[i] = routeModule, cacheModules);
762
+ }
763
+ let menu = void 0;
764
+ loadModule(menuLoader, pendingLoads, (menuModule) => menu = menuModule?.default, cacheModules);
765
+ if (pendingLoads.length > 0) await Promise.all(pendingLoads);
766
+ return {
767
+ $routeName$: routeName,
768
+ $params$: params,
769
+ $mods$: modules,
770
+ $menu$: deepFreeze(menu),
771
+ $routeBundleNames$: routeBundleNames,
772
+ $notFound$: notFound,
773
+ $errorLoader$: errorLoader,
774
+ $loaders$: loaderPathsByHash && Object.keys(loaderPathsByHash),
775
+ $loaderPaths$: loaderPathsByHash
776
+ };
777
+ };
778
+ /** Built-in fallback error component loader */
779
+ var httpErrorLoader = (() => import("./http-error.qwik.mjs"));
780
+ /** Collect the rewrite target's layouts and loader endpoint paths. */
781
+ function walkTrieKeys(root, keys, params) {
606
782
  let node = root;
783
+ let pathname = "/";
607
784
  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;
785
+ const loaderPaths = {};
786
+ const collect = (node) => {
622
787
  if (node._L) layouts.push(node._L);
788
+ for (const hash of node._R ?? []) loaderPaths[hash] = pathname;
789
+ };
790
+ collect(root);
791
+ for (const key of keys) {
792
+ const match = descendGroups(node, (candidate) => candidate[key]);
793
+ if (!match) return;
794
+ for (const group of match.groups) collect(group);
795
+ node = match.value;
796
+ const segment = node._P ? `${node._0 ?? ""}${params[node._P] ?? ""}${node._9 ?? ""}` : key;
797
+ const segments = key === "_A" ? segment.split("/") : [segment];
798
+ pathname = ensureSlash(pathname + segments.map(encodeURIComponent).join("/"));
799
+ collect(node);
800
+ }
801
+ if (!node._I && node._G == null) {
802
+ const index = findIndexNode(node);
803
+ if (index) {
804
+ for (const group of index.groups) collect(group);
805
+ node = index.target;
806
+ collect(node);
807
+ }
623
808
  }
624
809
  return {
625
810
  node,
626
- layouts
811
+ layouts,
812
+ loaderPaths
813
+ };
814
+ }
815
+ /** Resolve the page and its modules, following rewrite targets. */
816
+ function resolveLoaders(root, node, gatheredLayouts, params) {
817
+ if (node._G != null) {
818
+ const target = walkTrieKeys(root, node._G.split("/").filter((p) => p.length > 0), params);
819
+ if (!target) return;
820
+ const resolved = resolveLoaders(root, target.node, target.layouts, params);
821
+ if (resolved) resolved.loaderPaths ??= target.loaderPaths;
822
+ return resolved;
823
+ }
824
+ const index = node._I;
825
+ if (!index) return;
826
+ if (Array.isArray(index)) return {
827
+ loaders: index,
828
+ node
829
+ };
830
+ return {
831
+ loaders: [...gatheredLayouts, index],
832
+ node
627
833
  };
628
834
  }
629
835
  /**
630
836
  * The full chain to render for a boundary: a bare loader in its snapshot layouts, an override
631
837
  * as-is.
632
- */ var boundaryChain = (ref) => ref.v ? Array.isArray(ref.v) ? ref.v : [...ref.layouts, ref.v] : void 0;
838
+ */
839
+ var boundaryChain = (ref) => ref.v ? Array.isArray(ref.v) ? ref.v : [...ref.layouts, ref.v] : void 0;
633
840
  /**
634
841
  * Collect layouts, the nearest error (`_E`) and not-found (`_4`) boundaries, and the menu from a
635
842
  * node and the group ancestors entered to reach it.
636
- */ function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, matchedPathname = "/") {
843
+ */
844
+ function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, matchedPathname = "/") {
637
845
  for (let j = 0; j < groups.length; j++) {
638
846
  const g = groups[j];
639
847
  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
- }
646
- }
848
+ if (g._R && loaderPathsByHash) for (const hash of g._R) loaderPathsByHash[hash] = matchedPathname;
647
849
  if (g._E) {
648
850
  errorLoaderRef.v = g._E;
649
851
  errorLoaderRef.layouts = [...layouts];
@@ -655,13 +857,7 @@ var deepFreeze = (obj) => {
655
857
  if (g._N) menuLoaderRef.v = g._N;
656
858
  }
657
859
  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
- }
664
- }
860
+ if (node._R && loaderPathsByHash) for (const hash of node._R) loaderPathsByHash[hash] = matchedPathname;
665
861
  if (node._E) {
666
862
  errorLoaderRef.v = node._E;
667
863
  errorLoaderRef.layouts = [...layouts];
@@ -672,7 +868,45 @@ var deepFreeze = (obj) => {
672
868
  }
673
869
  if (node._N) menuLoaderRef.v = node._N;
674
870
  }
675
- /** Try to match a segment against a node's wildcard children (_W, _A). */ function tryWildcardMatch(node, part, partLower, parts, partIndex) {
871
+ var ChildMatchKind = /* @__PURE__ */ function(ChildMatchKind) {
872
+ ChildMatchKind[ChildMatchKind["Exact"] = 0] = "Exact";
873
+ ChildMatchKind[ChildMatchKind["Wildcard"] = 1] = "Wildcard";
874
+ ChildMatchKind[ChildMatchKind["Rest"] = 2] = "Rest";
875
+ return ChildMatchKind;
876
+ }(ChildMatchKind || {});
877
+ /**
878
+ * Try to find a child matching `partLower` in `node`, including inside group children (_M). Returns
879
+ * the matched child node, the groups entered to reach it, what to push to routeParts, and which
880
+ * kind of edge matched. Returns undefined if no match.
881
+ *
882
+ * Priority: exact match → _M groups (recursively) → _W wildcard → _A rest wildcard. This ensures
883
+ * exact matches inside nested groups take precedence over wildcards at parent level.
884
+ */
885
+ function findChild(node, part, partLower, parts, partIndex) {
886
+ const exact = node[partLower];
887
+ if (exact) return {
888
+ next: exact,
889
+ groups: [],
890
+ routePart: part,
891
+ done: false,
892
+ kind: ChildMatchKind.Exact
893
+ };
894
+ if (node._M) for (let j = 0; j < node._M.length; j++) {
895
+ const group = node._M[j];
896
+ const groupResult = findChild(group, part, partLower, parts, partIndex);
897
+ if (groupResult) {
898
+ groupResult.groups.unshift(group);
899
+ return groupResult;
900
+ }
901
+ }
902
+ const wildcard = tryWildcardMatch(node, part, partLower, parts, partIndex);
903
+ if (wildcard) return {
904
+ ...wildcard,
905
+ groups: []
906
+ };
907
+ }
908
+ /** Try to match a segment against a node's wildcard children (_W, _A). */
909
+ function tryWildcardMatch(node, part, partLower, parts, partIndex) {
676
910
  let next = node._W;
677
911
  if (next) {
678
912
  const prefix = next._0;
@@ -687,7 +921,7 @@ var deepFreeze = (obj) => {
687
921
  next,
688
922
  routePart: `${pre}[${paramName}]${suf}`,
689
923
  done: false,
690
- kind: 1,
924
+ kind: ChildMatchKind.Wildcard,
691
925
  paramName,
692
926
  paramValue: value
693
927
  };
@@ -698,7 +932,7 @@ var deepFreeze = (obj) => {
698
932
  next,
699
933
  routePart: `[${paramName}]`,
700
934
  done: false,
701
- kind: 1,
935
+ kind: ChildMatchKind.Wildcard,
702
936
  paramName,
703
937
  paramValue: part
704
938
  };
@@ -712,47 +946,18 @@ var deepFreeze = (obj) => {
712
946
  next,
713
947
  routePart: `[...${paramName}]`,
714
948
  done: true,
715
- kind: 2,
949
+ kind: ChildMatchKind.Rest,
716
950
  paramName,
717
951
  paramValue: restValue
718
952
  };
719
953
  }
720
954
  }
721
955
  /**
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
956
  * Descend through a node and its pathless `_M` groups (depth-first, in `_M` order), returning the
753
957
  * first node for which `hit` yields a value, plus the chain of groups entered to reach it. The
754
958
  * shared traversal behind the index / rest / boundary lookups inside groups.
755
- */ function descendGroups(node, hit) {
959
+ */
960
+ function descendGroups(node, hit) {
756
961
  const value = hit(node);
757
962
  if (value !== void 0) return {
758
963
  value,
@@ -772,7 +977,8 @@ var deepFreeze = (obj) => {
772
977
  * Find a node with `_I`/`_G` at the current level, including inside `_M` groups. Returns it and the
773
978
  * groups entered to reach it — excluding the target itself, whose `_L` the caller applies (so it
774
979
  * isn't double-counted).
775
- */ function findIndexNode(node) {
980
+ */
981
+ function findIndexNode(node) {
776
982
  const r = descendGroups(node, (n) => n._I || n._G != null ? n : void 0);
777
983
  return r ? {
778
984
  target: r.node,
@@ -780,39 +986,10 @@ var deepFreeze = (obj) => {
780
986
  } : void 0;
781
987
  }
782
988
  /**
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
989
  * Find a rest wildcard (`_A`) at the current node or inside its `_M` groups, with the groups
814
990
  * entered to reach it. Used to save fallback checkpoints during matching.
815
- */ function findRestNode(node) {
991
+ */
992
+ function findRestNode(node) {
816
993
  const r = descendGroups(node, (n) => n._A);
817
994
  return r ? {
818
995
  next: r.value,
@@ -822,7 +999,8 @@ var deepFreeze = (obj) => {
822
999
  /**
823
1000
  * Find a `_4`/`_E` boundary inside a node's pathless `_M` groups (first in `_M` order), wrapped in
824
1001
  * those groups' layouts.
825
- */ function findGroupBoundary(node, layouts, kind) {
1002
+ */
1003
+ function findGroupBoundary(node, layouts, kind) {
826
1004
  const r = descendGroups(node, (n) => n[kind]);
827
1005
  if (!r) return;
828
1006
  const groupLayouts = [...layouts];
@@ -841,12 +1019,12 @@ var deepFreeze = (obj) => {
841
1019
  * Group nodes (in `_M` arrays) are pathless — they don't consume a URL segment but contribute their
842
1020
  * `_L` layout to the chain. The matcher transparently enters groups when searching for a matching
843
1021
  * child.
844
- */ function matchRouteTree(root = {}, pathname) {
1022
+ */
1023
+ function matchRouteTree(root = {}, pathname) {
845
1024
  let node = root;
846
1025
  const params = {};
847
1026
  const routeParts = [];
848
1027
  const layouts = [];
849
- const loaderHashes = [];
850
1028
  const loaderPathsByHash = {};
851
1029
  const errorLoaderRef = {
852
1030
  v: void 0,
@@ -858,7 +1036,7 @@ var deepFreeze = (obj) => {
858
1036
  };
859
1037
  const menuLoaderRef = { v: void 0 };
860
1038
  const groupNodes = [];
861
- collectNodeMeta(root, [], layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash);
1039
+ collectNodeMeta(root, [], layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash);
862
1040
  if (root._M) groupNodes.push({
863
1041
  node: root,
864
1042
  depth: layouts.length
@@ -877,7 +1055,7 @@ var deepFreeze = (obj) => {
877
1055
  matched = false;
878
1056
  break;
879
1057
  }
880
- const restInfo = found.kind === 1 ? findRestNode(node) : void 0;
1058
+ const restInfo = found.kind === ChildMatchKind.Wildcard ? findRestNode(node) : void 0;
881
1059
  if (restInfo) restFallback = {
882
1060
  aNode: restInfo.next,
883
1061
  groups: restInfo.groups,
@@ -898,7 +1076,7 @@ var deepFreeze = (obj) => {
898
1076
  done = found.done;
899
1077
  node = found.next;
900
1078
  const matchedPathname = `/${parts.slice(0, found.done ? len : i + 1).join("/")}/`;
901
- collectNodeMeta(node, found.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, matchedPathname);
1079
+ collectNodeMeta(node, found.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, matchedPathname);
902
1080
  if (node._M) groupNodes.push({
903
1081
  node,
904
1082
  depth: layouts.length
@@ -908,7 +1086,7 @@ var deepFreeze = (obj) => {
908
1086
  if (!node._I && node._G == null) {
909
1087
  const indexResult = findIndexNode(node);
910
1088
  if (indexResult) {
911
- collectNodeMeta(indexResult.target, indexResult.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, pathname);
1089
+ collectNodeMeta(indexResult.target, indexResult.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, pathname);
912
1090
  node = indexResult.target;
913
1091
  }
914
1092
  }
@@ -919,12 +1097,13 @@ var deepFreeze = (obj) => {
919
1097
  const paramName = next._P;
920
1098
  params[paramName] = "";
921
1099
  routeParts.push(`[...${paramName}]`);
922
- collectNodeMeta(next, restInfo.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, pathname);
1100
+ collectNodeMeta(next, restInfo.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderPathsByHash, pathname);
923
1101
  node = next;
924
1102
  }
925
1103
  }
926
1104
  }
927
- const loaders = matched && (done || i >= len) ? resolveLoaders(root, node, layouts) : void 0;
1105
+ const resolved = matched && (done || i >= len) ? resolveLoaders(root, node, layouts, params) : void 0;
1106
+ const loaders = resolved?.loaders;
928
1107
  if (!loaders && restFallback) {
929
1108
  const fb = restFallback;
930
1109
  const fbParams = {
@@ -943,20 +1122,21 @@ var deepFreeze = (obj) => {
943
1122
  layouts: fb.notFoundLayouts
944
1123
  };
945
1124
  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
- };
1125
+ collectNodeMeta(fb.aNode, fb.groups, fbLayouts, fbErrorRef, fbNotFoundRef, fbMenuRef, fbLoaderPathsByHash, pathname);
1126
+ const fallback = resolveLoaders(root, fb.aNode, fbLayouts, fbParams);
1127
+ if (fallback) {
1128
+ collectPageLoaders(fallback, fbLoaderPathsByHash, pathname);
1129
+ return {
1130
+ loaders: fallback.loaders,
1131
+ params: fbParams,
1132
+ routeParts: fbRouteParts,
1133
+ notFound: false,
1134
+ routeBundleNames: fb.aNode._B,
1135
+ loaderPathsByHash: Object.keys(fbLoaderPathsByHash).length > 0 ? fbLoaderPathsByHash : void 0,
1136
+ menuLoader: fbMenuRef.v,
1137
+ errorLoader: boundaryChain(fbErrorRef)
1138
+ };
1139
+ }
960
1140
  errorLoaderRef.v = fbErrorRef.v;
961
1141
  errorLoaderRef.layouts = fbErrorRef.layouts;
962
1142
  notFoundLoaderRef.v = fbNotFoundRef.v;
@@ -993,27 +1173,18 @@ var deepFreeze = (obj) => {
993
1173
  routeParts,
994
1174
  notFound: true,
995
1175
  routeBundleNames: void 0,
996
- loaderHashes: void 0,
997
1176
  loaderPathsByHash: void 0,
998
1177
  menuLoader: menuLoaderRef.v,
999
1178
  errorLoader: boundaryChain(errorLoaderRef)
1000
1179
  };
1001
1180
  }
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
- }
1181
+ collectPageLoaders(resolved, loaderPathsByHash, pathname);
1010
1182
  return {
1011
1183
  loaders,
1012
1184
  params,
1013
1185
  routeParts,
1014
1186
  notFound: false,
1015
1187
  routeBundleNames: node._B,
1016
- loaderHashes: loaderHashes.length > 0 ? loaderHashes : void 0,
1017
1188
  loaderPathsByHash: Object.keys(loaderPathsByHash).length > 0 ? loaderPathsByHash : void 0,
1018
1189
  menuLoader: menuLoaderRef.v,
1019
1190
  errorLoader: boundaryChain(errorLoaderRef)
@@ -1033,39 +1204,32 @@ var loadModule = (moduleLoader, pendingLoads, moduleSetter, cacheModules) => {
1033
1204
  }
1034
1205
  }
1035
1206
  };
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);
1207
+ function collectPageLoaders({ node, loaderPaths }, paths, pathname) {
1208
+ if (loaderPaths) {
1209
+ for (const hash in paths) delete paths[hash];
1210
+ Object.assign(paths, loaderPaths);
1044
1211
  }
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 }) => {
1212
+ const pageHashes = node._D ?? [];
1213
+ if (Array.isArray(node._I)) {
1214
+ for (const hash in paths) if (!pageHashes.includes(hash)) delete paths[hash];
1215
+ }
1216
+ for (const hash of pageHashes) paths[hash] ||= ensureSlash(pathname);
1217
+ }
1218
+ /** Gets an absolute url path string (url.pathname + url.search + url.hash) */
1219
+ var toPath = (url) => url.pathname + url.search + url.hash;
1220
+ /** Create a URL from a string and baseUrl */
1221
+ var toUrl = (url, baseUrl) => new URL(url, baseUrl.href);
1222
+ /** Checks only if the origins are the same. */
1223
+ var isSameOrigin = (a, b) => a.origin === b.origin;
1224
+ /** Checks only if the pathnames are the same for the URLs (doesn't include search and hash) */
1225
+ var isSamePathname = ({ pathname: a }, { pathname: b }) => {
1064
1226
  const lDiff = Math.abs(a.length - b.length);
1065
1227
  return lDiff === 0 ? a === b : lDiff === 1 && ensureSlash(a) === ensureSlash(b);
1066
1228
  };
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);
1229
+ /** Checks only if the search query strings are the same for the URLs */
1230
+ var isSameSearchQuery = (a, b) => a.search === b.search;
1231
+ /** Checks only if the pathname + search are the same for the URLs. */
1232
+ var isSamePath = (a, b) => isSameSearchQuery(a, b) && isSamePathname(a, b);
1069
1233
  var getClientNavPath = (props, baseUrl) => {
1070
1234
  const href = props.href;
1071
1235
  if (typeof href === "string" && typeof props.target !== "string" && !props.reload) try {
@@ -1084,40 +1248,12 @@ var shouldPreload = (clientNavPath, currentLoc) => {
1084
1248
  var isPromise = (value) => {
1085
1249
  return value && typeof value.then === "function";
1086
1250
  };
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
1251
  /**
1117
1252
  * Resolve the full route config (head + eTag + cacheKey) from all content modules. Uses the same
1118
1253
  * resolution rules as DocumentHead: objects are merged immediately, functions are queued and
1119
1254
  * executed inner-before-outer.
1120
- */ var resolveRouteConfig = (resolveValue, routeLocation, contentModules, locale, status, defaults) => withLocale(locale, () => {
1255
+ */
1256
+ var resolveRouteConfig = (resolveValue, routeLocation, contentModules, locale, status, defaults) => withLocale(locale, () => {
1121
1257
  const head = createDocumentHead(defaults);
1122
1258
  let eTag;
1123
1259
  let cacheKey;
@@ -1190,13 +1326,43 @@ var createDocumentHead = (defaults, manifestHash) => ({
1190
1326
  *
1191
1327
  * Signal values may throw a promise on the client when still loading — callers should wrap this in
1192
1328
  * `retryOnPromise` to handle that.
1193
- */ var resolveHead = (actionData, loaderState, routeLocation, contentModules, locale, defaults) => {
1194
- const getData = (loaderOrAction) => {
1329
+ */
1330
+ var resolveHead = (actionData, loaderState, routeLocation, contentModules, locale, defaults) => {
1331
+ const getData = ((loaderOrAction) => {
1195
1332
  const id = loaderOrAction.__id;
1196
1333
  const signal = loaderState?.[id];
1197
1334
  if (signal) return signal.value;
1198
1335
  if (actionData?.action === id) return actionData.actionResult;
1199
- };
1336
+ });
1200
1337
  return resolveRouteConfig(getData, routeLocation, contentModules, locale, actionData?.status ?? 200, defaults).head;
1201
1338
  };
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 };
1339
+ var resolveDocumentHead = (resolvedHead, updatedHead) => {
1340
+ if (typeof updatedHead.title === "string") resolvedHead.title = updatedHead.title;
1341
+ mergeArray(resolvedHead.meta, updatedHead.meta);
1342
+ mergeArray(resolvedHead.links, updatedHead.links);
1343
+ mergeArray(resolvedHead.styles, updatedHead.styles);
1344
+ mergeArray(resolvedHead.scripts, updatedHead.scripts);
1345
+ Object.assign(resolvedHead.frontmatter, updatedHead.frontmatter);
1346
+ };
1347
+ var mergeArray = (existingArr, newArr) => {
1348
+ if (Array.isArray(newArr)) for (const newItem of newArr) {
1349
+ if (typeof newItem.key === "string") {
1350
+ const existingIndex = existingArr.findIndex((i) => i.key === newItem.key);
1351
+ if (existingIndex > -1) {
1352
+ existingArr[existingIndex] = newItem;
1353
+ continue;
1354
+ }
1355
+ }
1356
+ existingArr.push(newItem);
1357
+ }
1358
+ };
1359
+ var createDocumentHead = (defaults, manifestHash) => ({
1360
+ title: defaults?.title || "",
1361
+ meta: [...defaults?.meta || []],
1362
+ links: [...defaults?.links || []],
1363
+ styles: [...defaults?.styles || []],
1364
+ scripts: [...defaults?.scripts || []],
1365
+ frontmatter: { ...defaults?.frontmatter },
1366
+ manifestHash: manifestHash || "dev"
1367
+ });
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 };