@qwik.dev/router 2.0.0-beta.39 → 2.0.0-beta.40

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 (40) hide show
  1. package/lib/adapters/azure-swa/vite/index.mjs +45 -54
  2. package/lib/adapters/bun-server/vite/index.mjs +16 -22
  3. package/lib/adapters/cloud-run/vite/index.mjs +15 -19
  4. package/lib/adapters/cloudflare-pages/vite/index.mjs +46 -67
  5. package/lib/adapters/deno-server/vite/index.mjs +25 -39
  6. package/lib/adapters/netlify-edge/vite/index.mjs +50 -76
  7. package/lib/adapters/node-server/vite/index.mjs +16 -22
  8. package/lib/adapters/shared/vite/index.mjs +271 -360
  9. package/lib/adapters/ssg/vite/index.mjs +11 -15
  10. package/lib/adapters/vercel-edge/vite/index.mjs +74 -95
  11. package/lib/chunks/dev-preloaded-route-loader.mjs +2 -3
  12. package/lib/chunks/format.mjs +16 -23
  13. package/lib/chunks/fs.mjs +93 -106
  14. package/lib/chunks/head.qwik.mjs +1174 -1382
  15. package/lib/chunks/http-error.qwik.mjs +20 -28
  16. package/lib/chunks/pathname.mjs +50 -69
  17. package/lib/chunks/request-body-limit.mjs +25 -35
  18. package/lib/chunks/request-path.mjs +44 -55
  19. package/lib/chunks/routes.mjs +20 -41
  20. package/lib/chunks/server-error.mjs +15 -18
  21. package/lib/chunks/system.mjs +258 -326
  22. package/lib/chunks/url.mjs +56 -58
  23. package/lib/chunks/user-response.mjs +1616 -1804
  24. package/lib/chunks/worker-thread.mjs +254 -307
  25. package/lib/index.qwik.mjs +1637 -1918
  26. package/lib/middleware/aws-lambda/index.mjs +44 -52
  27. package/lib/middleware/azure-swa/index.mjs +85 -94
  28. package/lib/middleware/bun/index.mjs +118 -138
  29. package/lib/middleware/cloudflare-pages/index.mjs +83 -96
  30. package/lib/middleware/deno/index.mjs +104 -125
  31. package/lib/middleware/firebase/index.mjs +21 -25
  32. package/lib/middleware/netlify-edge/index.mjs +63 -71
  33. package/lib/middleware/node/index.mjs +297 -357
  34. package/lib/middleware/request-handler/index.mjs +110 -133
  35. package/lib/middleware/vercel-edge/index.mjs +82 -98
  36. package/lib/service-worker/index.mjs +12 -3
  37. package/lib/ssg/index.mjs +249 -289
  38. package/lib/vite/index.mjs +2344 -3030
  39. package/package.json +5 -5
  40. package/lib/chunks/use-functions.qwik.mjs +0 -23
@@ -1,1425 +1,1217 @@
1
- import { e as ensureSlash } from './pathname.mjs';
2
- import { createContextId, isServer as isServer$1, implicit$FirstArg, isDev, createComputedQrl, inlinedQrl, _captures, withLocale } from '@qwik.dev/core';
3
- import * as qwikRouterConfig from '@qwik-router-config';
4
- import { _getAsyncLocalStorage, _getContextEvent, _resolveContextWithoutSequentialScope, _injectAsyncSignalValue, _UNINITIALIZED, _deserialize, _markSignalAsExternallyOwned, SerializerSymbol, _verifySerializable } from '@qwik.dev/core/internal';
5
- import { isServer } from '@qwik.dev/core/build';
6
- import { g as getLoaderName } from './request-path.mjs';
7
- import { R as RedirectMessage, S as ServerError, t as throwIfControlFlowSignal } from './server-error.mjs';
8
-
9
- let _asyncRequestStore;
10
- if (isServer) {
11
- const AsyncLocalStorage = _getAsyncLocalStorage();
12
- if (AsyncLocalStorage) {
13
- _asyncRequestStore = new AsyncLocalStorage();
14
- } else {
15
- 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");
16
- }
1
+ import { t as ensureSlash } from "./pathname.mjs";
2
+ import { o as getLoaderName } from "./request-path.mjs";
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";
7
+ import { isServer as isServer$1 } from "@qwik.dev/core/build";
8
+ /** @internal */ var _asyncRequestStore;
9
+ if (isServer$1) {
10
+ 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");
17
13
  }
18
-
19
- const deepFreeze = (obj) => {
20
- if (obj == null) {
21
- return obj;
22
- }
23
- Object.getOwnPropertyNames(obj).forEach((prop) => {
24
- const value = obj[prop];
25
- if (value && typeof value === "object" && !Object.isFrozen(value)) {
26
- try {
27
- deepFreeze(value);
28
- } catch {
29
- return obj;
30
- }
31
- }
32
- });
33
- return Object.freeze(obj);
14
+ var QACTION_KEY = "qaction";
15
+ var QFN_KEY = "qfunc";
16
+ var QDATA_KEY = "qdata";
17
+ /** @public */ var Q_ROUTE = "q:route";
18
+ var DEFAULT_LOADERS_SERIALIZATION_STRATEGY = globalThis.__DEFAULT_LOADERS_SERIALIZATION_STRATEGY__ || "never";
19
+ var RouteStateContext = /*#__PURE__*/ createContextId("qr-s");
20
+ var RouteLoaderCtxContext = /*#__PURE__*/ createContextId("qr-lc");
21
+ var ContentContext = /*#__PURE__*/ createContextId("qr-c");
22
+ var ContentInternalContext = /*#__PURE__*/ createContextId("qr-ic");
23
+ var DocumentHeadContext = /*#__PURE__*/ createContextId("qr-h");
24
+ var RouteLocationContext = /*#__PURE__*/ createContextId("qr-l");
25
+ var RouteNavigateContext = /*#__PURE__*/ createContextId("qr-n");
26
+ var RouteActionContext = /*#__PURE__*/ createContextId("qr-a");
27
+ var RoutePreventNavigateContext = /*#__PURE__*/ createContextId("qr-p");
28
+ var HttpStatusContext = /*#__PURE__*/ createContextId("qr-hs");
29
+ /**
30
+ * Route loaders read data before the route rendering starts, based on the route being navigated to.
31
+ * They automatically update when the route changes on the client.
32
+ *
33
+ * They are represented by a ComputedSignal.
34
+ */ var REQUEST_ROUTE_LOADER_STATE = "@routeLoaderState";
35
+ var REQUEST_LOADER_PATHS_STORE = "@loaderPathsStore";
36
+ var REQUEST_ROUTE_LOADERS = "@routeLoaders";
37
+ var REQUEST_ROUTE_LOADER_PROMISES = "@routeLoaderPromises";
38
+ var REQUEST_ROUTE_LOADER_EVENTS = "@routeLoaderEvents";
39
+ var REQUEST_ROUTE_LOADER_ROOT_EVENT = "@routeLoaderRootEvent";
40
+ 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";
43
+ /**
44
+ * Per-navigation fetch dedupe: shares in-flight and completed fetches (hover prefetch → click nav)
45
+ * and prevents repeat hover fetches. Cleared after each navigation kicks off its loader fetches;
46
+ * across navigations the browser HTTP cache is the freshness authority.
47
+ */ var navFetchCache = /* @__PURE__ */ new Map();
48
+ var clearNavFetchCache = () => navFetchCache.clear();
49
+ 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) => {
51
+ if (signal.aborted) return Promise.reject(signal.reason);
52
+ return new Promise((resolve, reject) => {
53
+ const abort = () => reject(signal.reason);
54
+ signal.addEventListener("abort", abort, { once: true });
55
+ promise.finally(() => signal.removeEventListener("abort", abort)).then(resolve, reject);
56
+ });
34
57
  };
35
-
36
- const MODULE_CACHE = /* @__PURE__ */ new WeakMap();
37
- const httpErrorLoader = () => import('./http-error.qwik.mjs');
38
- function walkTrieKeys(root, keys) {
39
- let node = root;
40
- const layouts = [];
41
- if (node._L) {
42
- layouts.push(node._L);
43
- }
44
- for (let i = 0; i < keys.length; i++) {
45
- const key = keys[i];
46
- let next = node[key];
47
- if (!next && node._M) {
48
- for (let j = 0; j < node._M.length; j++) {
49
- const group = node._M[j];
50
- next = group[key];
51
- if (next) {
52
- if (group._L) {
53
- layouts.push(group._L);
54
- }
55
- break;
56
- }
57
- }
58
- }
59
- if (!next) {
60
- return void 0;
61
- }
62
- node = next;
63
- if (node._L) {
64
- layouts.push(node._L);
65
- }
66
- }
67
- return {
68
- node,
69
- layouts
70
- };
71
- }
72
- const boundaryChain = (ref) => ref.v ? Array.isArray(ref.v) ? ref.v : [
73
- ...ref.layouts,
74
- ref.v
75
- ] : void 0;
76
- function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, matchedPathname = "/") {
77
- for (let j = 0; j < groups.length; j++) {
78
- const g = groups[j];
79
- if (g._L) {
80
- layouts.push(g._L);
81
- }
82
- if (g._R && loaderHashes) {
83
- loaderHashes.push(...g._R);
84
- if (loaderPathsByHash) {
85
- for (let i = 0; i < g._R.length; i++) {
86
- const hash = g._R[i];
87
- loaderPathsByHash[hash] = matchedPathname;
88
- }
89
- }
90
- }
91
- if (g._E) {
92
- errorLoaderRef.v = g._E;
93
- errorLoaderRef.layouts = [
94
- ...layouts
95
- ];
96
- }
97
- if (g._4) {
98
- notFoundLoaderRef.v = g._4;
99
- notFoundLoaderRef.layouts = [
100
- ...layouts
101
- ];
102
- }
103
- if (g._N) {
104
- menuLoaderRef.v = g._N;
105
- }
106
- }
107
- if (node._L) {
108
- layouts.push(node._L);
109
- }
110
- if (node._R && loaderHashes) {
111
- loaderHashes.push(...node._R);
112
- if (loaderPathsByHash) {
113
- for (let i = 0; i < node._R.length; i++) {
114
- const hash = node._R[i];
115
- loaderPathsByHash[hash] = matchedPathname;
116
- }
117
- }
118
- }
119
- if (node._E) {
120
- errorLoaderRef.v = node._E;
121
- errorLoaderRef.layouts = [
122
- ...layouts
123
- ];
124
- }
125
- if (node._4) {
126
- notFoundLoaderRef.v = node._4;
127
- notFoundLoaderRef.layouts = [
128
- ...layouts
129
- ];
130
- }
131
- if (node._N) {
132
- menuLoaderRef.v = node._N;
133
- }
134
- }
135
- function tryWildcardMatch(node, part, partLower, parts, partIndex) {
136
- let next = node._W;
137
- if (next) {
138
- const prefix = next._0;
139
- const suffix = next._9;
140
- if (prefix || suffix) {
141
- const pre = prefix || "";
142
- const suf = suffix || "";
143
- if (partLower.length > pre.length + suf.length && (!pre || partLower.startsWith(pre.toLowerCase())) && (!suf || partLower.endsWith(suf.toLowerCase()))) {
144
- const paramName = next._P;
145
- const value = part.slice(pre.length, suf ? part.length - suf.length : void 0);
146
- return {
147
- next,
148
- routePart: `${pre}[${paramName}]${suf}`,
149
- done: false,
150
- kind: 1,
151
- paramName,
152
- paramValue: value
153
- };
154
- }
155
- } else {
156
- const paramName = next._P;
157
- return {
158
- next,
159
- routePart: `[${paramName}]`,
160
- done: false,
161
- kind: 1,
162
- paramName,
163
- paramValue: part
164
- };
165
- }
166
- }
167
- next = node._A;
168
- if (next) {
169
- const paramName = next._P;
170
- const restValue = parts.slice(partIndex).join("/");
171
- return {
172
- next,
173
- routePart: `[...${paramName}]`,
174
- done: true,
175
- kind: 2,
176
- paramName,
177
- paramValue: restValue
178
- };
179
- }
180
- return void 0;
181
- }
182
- function findChild(node, part, partLower, parts, partIndex) {
183
- const exact = node[partLower];
184
- if (exact) {
185
- return {
186
- next: exact,
187
- groups: [],
188
- routePart: part,
189
- done: false,
190
- kind: 0
191
- };
192
- }
193
- if (node._M) {
194
- for (let j = 0; j < node._M.length; j++) {
195
- const group = node._M[j];
196
- const groupResult = findChild(group, part, partLower, parts, partIndex);
197
- if (groupResult) {
198
- groupResult.groups.unshift(group);
199
- return groupResult;
200
- }
201
- }
202
- }
203
- const wildcard = tryWildcardMatch(node, part, partLower, parts, partIndex);
204
- if (wildcard) {
205
- return {
206
- ...wildcard,
207
- groups: []
208
- };
209
- }
210
- return void 0;
211
- }
212
- function descendGroups(node, hit) {
213
- const value = hit(node);
214
- if (value !== void 0) {
215
- return {
216
- value,
217
- node,
218
- groups: []
219
- };
220
- }
221
- if (node._M) {
222
- for (let j = 0; j < node._M.length; j++) {
223
- const group = node._M[j];
224
- const result = descendGroups(group, hit);
225
- if (result) {
226
- result.groups.unshift(group);
227
- return result;
228
- }
229
- }
230
- }
231
- return void 0;
232
- }
233
- function findIndexNode(node) {
234
- const r = descendGroups(node, (n) => n._I || n._G != null ? n : void 0);
235
- return r ? {
236
- target: r.node,
237
- groups: r.groups.filter((g) => g !== r.node)
238
- } : void 0;
239
- }
240
- function resolveLoaders(root, node, gatheredLayouts) {
241
- if (node._G != null) {
242
- const keys = node._G.split("/").filter((p) => p.length > 0);
243
- const target = walkTrieKeys(root, keys);
244
- if (!target) {
245
- return void 0;
246
- }
247
- let targetNode = target.node;
248
- const targetLayouts = target.layouts;
249
- if (!targetNode._I && targetNode._G == null) {
250
- const indexResult = findIndexNode(targetNode);
251
- if (indexResult) {
252
- for (let j = 0; j < indexResult.groups.length; j++) {
253
- const g = indexResult.groups[j];
254
- if (g._L) {
255
- targetLayouts.push(g._L);
256
- }
257
- }
258
- targetNode = indexResult.target;
259
- if (targetNode._L) {
260
- targetLayouts.push(targetNode._L);
261
- }
262
- }
263
- }
264
- return resolveLoaders(root, targetNode, targetLayouts);
265
- }
266
- const index = node._I;
267
- if (!index) {
268
- return void 0;
269
- }
270
- if (Array.isArray(index)) {
271
- return index;
272
- }
273
- return [
274
- ...gatheredLayouts,
275
- index
276
- ];
277
- }
278
- function findRestNode(node) {
279
- const r = descendGroups(node, (n) => n._A);
280
- return r ? {
281
- next: r.value,
282
- groups: r.groups
283
- } : void 0;
284
- }
285
- function findGroupBoundary(node, layouts, kind) {
286
- const r = descendGroups(node, (n) => n[kind]);
287
- if (!r) {
288
- return void 0;
289
- }
290
- const groupLayouts = [
291
- ...layouts
292
- ];
293
- for (const g of r.groups) {
294
- if (g._L) {
295
- groupLayouts.push(g._L);
296
- }
297
- }
298
- return {
299
- v: r.value,
300
- layouts: groupLayouts
301
- };
302
- }
303
- function matchRouteTree(root = {}, pathname) {
304
- let node = root;
305
- const params = {};
306
- const routeParts = [];
307
- const layouts = [];
308
- const loaderHashes = [];
309
- const loaderPathsByHash = {};
310
- const errorLoaderRef = {
311
- v: void 0,
312
- layouts: []
313
- };
314
- const notFoundLoaderRef = {
315
- v: void 0,
316
- layouts: []
317
- };
318
- const menuLoaderRef = {
319
- v: void 0
320
- };
321
- const groupNodes = [];
322
- collectNodeMeta(root, [], layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash);
323
- if (root._M) {
324
- groupNodes.push({
325
- node: root,
326
- depth: layouts.length
327
- });
328
- }
329
- let done = false;
330
- let matched = true;
331
- const parts = pathname.split("/").filter((p) => p.length > 0).map(decodeURIComponent);
332
- let restFallback;
333
- let i = 0;
334
- const len = parts.length;
335
- for (; !done && i < len; i++) {
336
- const part = parts[i];
337
- const partLower = part.toLowerCase();
338
- const found = findChild(node, part, partLower, parts, i);
339
- if (!found) {
340
- matched = false;
341
- break;
342
- }
343
- const restInfo = found.kind === 1 ? findRestNode(node) : void 0;
344
- if (restInfo) {
345
- restFallback = {
346
- aNode: restInfo.next,
347
- groups: restInfo.groups,
348
- paramName: restInfo.next._P,
349
- restValue: parts.slice(i).join("/"),
350
- routeParts: [
351
- ...routeParts
352
- ],
353
- params: {
354
- ...params
355
- },
356
- layouts: [
357
- ...layouts
358
- ],
359
- loaderPathsByHash: {
360
- ...loaderPathsByHash
361
- },
362
- errorLoader: errorLoaderRef.v,
363
- errorLayouts: errorLoaderRef.layouts,
364
- notFoundLoader: notFoundLoaderRef.v,
365
- notFoundLayouts: notFoundLoaderRef.layouts,
366
- menuLoader: menuLoaderRef.v
367
- };
368
- }
369
- if (found.paramName) {
370
- params[found.paramName] = found.paramValue;
371
- }
372
- routeParts.push(found.routePart);
373
- done = found.done;
374
- node = found.next;
375
- const matchedPathname = `/${parts.slice(0, found.done ? len : i + 1).join("/")}/`;
376
- collectNodeMeta(node, found.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, matchedPathname);
377
- if (node._M) {
378
- groupNodes.push({
379
- node,
380
- depth: layouts.length
381
- });
382
- }
383
- }
384
- if (matched && !done && i === len) {
385
- if (!node._I && node._G == null) {
386
- const indexResult = findIndexNode(node);
387
- if (indexResult) {
388
- collectNodeMeta(indexResult.target, indexResult.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, pathname);
389
- node = indexResult.target;
390
- }
391
- }
392
- if (!node._I && node._G == null) {
393
- const restInfo = findRestNode(node);
394
- if (restInfo) {
395
- const next = restInfo.next;
396
- const paramName = next._P;
397
- params[paramName] = "";
398
- routeParts.push(`[...${paramName}]`);
399
- collectNodeMeta(next, restInfo.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, pathname);
400
- node = next;
401
- }
402
- }
403
- }
404
- const loaders = matched && (done || i >= len) ? resolveLoaders(root, node, layouts) : void 0;
405
- if (!loaders && restFallback) {
406
- const fb = restFallback;
407
- const fbParams = {
408
- ...fb.params,
409
- [fb.paramName]: fb.restValue
410
- };
411
- const fbRouteParts = [
412
- ...fb.routeParts,
413
- `[...${fb.paramName}]`
414
- ];
415
- const fbLayouts = [
416
- ...fb.layouts
417
- ];
418
- const fbLoaderPathsByHash = {
419
- ...fb.loaderPathsByHash
420
- };
421
- const fbErrorRef = {
422
- v: fb.errorLoader,
423
- layouts: fb.errorLayouts
424
- };
425
- const fbNotFoundRef = {
426
- v: fb.notFoundLoader,
427
- layouts: fb.notFoundLayouts
428
- };
429
- const fbMenuRef = {
430
- v: fb.menuLoader
431
- };
432
- const fbLoaderHashes = [];
433
- collectNodeMeta(fb.aNode, fb.groups, fbLayouts, fbErrorRef, fbNotFoundRef, fbMenuRef, fbLoaderHashes, fbLoaderPathsByHash, pathname);
434
- const fbLoaders = resolveLoaders(root, fb.aNode, fbLayouts);
435
- if (fbLoaders) {
436
- return {
437
- loaders: fbLoaders,
438
- params: fbParams,
439
- routeParts: fbRouteParts,
440
- notFound: false,
441
- routeBundleNames: fb.aNode._B,
442
- loaderHashes: fbLoaderHashes.length > 0 ? fbLoaderHashes : void 0,
443
- loaderPathsByHash: Object.keys(fbLoaderPathsByHash).length > 0 ? fbLoaderPathsByHash : void 0,
444
- menuLoader: fbMenuRef.v,
445
- errorLoader: boundaryChain(fbErrorRef)
446
- };
447
- }
448
- errorLoaderRef.v = fbErrorRef.v;
449
- errorLoaderRef.layouts = fbErrorRef.layouts;
450
- notFoundLoaderRef.v = fbNotFoundRef.v;
451
- notFoundLoaderRef.layouts = fbNotFoundRef.layouts;
452
- menuLoaderRef.v = fbMenuRef.v;
453
- }
454
- if (!loaders) {
455
- const recover = (kind) => {
456
- for (let k = groupNodes.length - 1; k >= 0; k--) {
457
- const found = findGroupBoundary(groupNodes[k].node, layouts.slice(0, groupNodes[k].depth), kind);
458
- if (found) {
459
- return found;
460
- }
461
- }
462
- return void 0;
463
- };
464
- if (!notFoundLoaderRef.v) {
465
- const g = recover("_4");
466
- if (g) {
467
- notFoundLoaderRef.v = g.v;
468
- notFoundLoaderRef.layouts = g.layouts;
469
- }
470
- }
471
- if (!errorLoaderRef.v) {
472
- const g = recover("_E");
473
- if (g) {
474
- errorLoaderRef.v = g.v;
475
- errorLoaderRef.layouts = g.layouts;
476
- }
477
- }
478
- const ref = notFoundLoaderRef.v ? notFoundLoaderRef : errorLoaderRef.v ? errorLoaderRef : null;
479
- const boundary = ref?.v ?? httpErrorLoader;
480
- const boundaryLayouts = ref ? ref.layouts : layouts;
481
- return {
482
- loaders: Array.isArray(boundary) ? boundary : [
483
- ...boundaryLayouts,
484
- boundary
485
- ],
486
- params,
487
- routeParts,
488
- notFound: true,
489
- routeBundleNames: void 0,
490
- loaderHashes: void 0,
491
- loaderPathsByHash: void 0,
492
- menuLoader: menuLoaderRef.v,
493
- errorLoader: boundaryChain(errorLoaderRef)
494
- };
495
- }
496
- if (node._R) {
497
- loaderHashes.push(...node._R);
498
- const matchedPathname = ensureSlash(pathname);
499
- for (let i2 = 0; i2 < node._R.length; i2++) {
500
- const hash = node._R[i2];
501
- loaderPathsByHash[hash] = matchedPathname;
502
- }
503
- }
504
- return {
505
- loaders,
506
- params,
507
- routeParts,
508
- notFound: false,
509
- routeBundleNames: node._B,
510
- loaderHashes: loaderHashes.length > 0 ? loaderHashes : void 0,
511
- loaderPathsByHash: Object.keys(loaderPathsByHash).length > 0 ? loaderPathsByHash : void 0,
512
- menuLoader: menuLoaderRef.v,
513
- errorLoader: boundaryChain(errorLoaderRef)
514
- };
515
- }
516
- const loadModule = (moduleLoader, pendingLoads, moduleSetter, cacheModules) => {
517
- if (typeof moduleLoader === "function") {
518
- const loadedModule = MODULE_CACHE.get(moduleLoader);
519
- if (loadedModule) {
520
- moduleSetter(loadedModule);
521
- } else {
522
- const moduleOrPromise = moduleLoader();
523
- if (typeof moduleOrPromise.then === "function") {
524
- pendingLoads.push(moduleOrPromise.then((loadedModule2) => {
525
- if (cacheModules !== false) {
526
- MODULE_CACHE.set(moduleLoader, loadedModule2);
527
- }
528
- moduleSetter(loadedModule2);
529
- }));
530
- } else if (moduleOrPromise) {
531
- moduleSetter(moduleOrPromise);
532
- }
533
- }
534
- }
535
- };
536
- const loadRoute = async (routes, cacheModules, pathname) => {
537
- const result = matchRouteTree(routes, pathname);
538
- const { loaders, params, routeParts, notFound, routeBundleNames, loaderHashes, loaderPathsByHash, menuLoader, errorLoader } = result;
539
- const routeName = "/" + routeParts.join("/");
540
- const modules = new Array(loaders.length);
541
- const pendingLoads = [];
542
- for (let i = 0; i < loaders.length; i++) {
543
- const moduleLoader = loaders[i];
544
- loadModule(moduleLoader, pendingLoads, (routeModule) => modules[i] = routeModule, cacheModules);
545
- }
546
- let menu = void 0;
547
- loadModule(menuLoader, pendingLoads, (menuModule) => menu = menuModule?.default, cacheModules);
548
- if (pendingLoads.length > 0) {
549
- await Promise.all(pendingLoads);
550
- }
551
- return {
552
- $routeName$: routeName,
553
- $params$: params,
554
- $mods$: modules,
555
- $menu$: deepFreeze(menu),
556
- $routeBundleNames$: routeBundleNames,
557
- $notFound$: notFound,
558
- $errorLoader$: errorLoader,
559
- $loaders$: loaderHashes,
560
- $loaderPaths$: loaderPathsByHash
561
- };
58
+ var getRouteLoaderValueStateKey = (loaderId) => `${ROUTE_LOADER_VALUE_PREFIX}${loaderId}`;
59
+ var isRequestEvent = (value) => !!value && typeof value === "object" && Object.prototype.hasOwnProperty.call(value, "sharedMap") && Object.prototype.hasOwnProperty.call(value, "cookie");
60
+ var isLoaderInternal = (value) => typeof value === "function" && value.__brand === "server_loader";
61
+ /**
62
+ * Fetch a single loader's raw JSON response from the server.
63
+ *
64
+ * URL pattern: `{basePath}{routePath}/q-loader-{loaderId}.{manifestHash}.json`
65
+ */ var fetchRouteLoaderData = async (loaderId, routePath, manifestHash, opts) => {
66
+ if (!routePath) return;
67
+ let resolvedPath = routePath;
68
+ const basePath = opts?.basePath ?? "/";
69
+ if (basePath !== "/" && !resolvedPath.startsWith(basePath)) resolvedPath = basePath + resolvedPath.slice(1);
70
+ const pathBase = ensureSlash(resolvedPath);
71
+ const pageUrl = opts?.pageUrl;
72
+ const search = pageUrl?.search ?? "";
73
+ const url = `${pathBase}${getLoaderName(loaderId, manifestHash)}${search}`;
74
+ const headers = {};
75
+ if (pageUrl && pageUrl.pathname !== pathBase) {
76
+ if (!globalThis.__STRICT_LOADERS__) headers[FULLPATH_HEADER] = pageUrl.pathname;
77
+ else if (isDev) headers[ROUTE_PATH_HEADER] = pageUrl.pathname;
78
+ }
79
+ const cacheKey = `${url}\n${headers["X-Qwik-fullpath"] ?? ""}`;
80
+ if (!opts?.ignoreCache) {
81
+ const entry = navFetchCache.get(cacheKey);
82
+ if (entry) return opts?.signal ? wrapWithAbort(entry, opts.signal) : entry;
83
+ }
84
+ const request = async () => {
85
+ const response = await fetch(url, {
86
+ signal: opts?.signal,
87
+ cache: opts?.ignoreCache ? "reload" : "default",
88
+ headers
89
+ });
90
+ if (response.redirected) return { r: response.url };
91
+ if (isRedirectStatus(response.status)) {
92
+ const location1 = response.headers.get("Location");
93
+ if (location1) return { r: location1 };
94
+ }
95
+ if (!response.ok) return;
96
+ return { raw: await response.text() };
97
+ };
98
+ if (opts?.ignoreCache) return request();
99
+ if (opts?.signal) return request().then((value) => {
100
+ if (value !== void 0 && !navFetchCache.has(cacheKey)) navFetchCache.set(cacheKey, Promise.resolve(value));
101
+ return value;
102
+ });
103
+ const promise = request().then((value) => {
104
+ if (value === void 0) navFetchCache.delete(cacheKey);
105
+ return value;
106
+ }, (err) => {
107
+ navFetchCache.delete(cacheKey);
108
+ throw err;
109
+ });
110
+ navFetchCache.set(cacheKey, promise);
111
+ return promise;
562
112
  };
563
-
564
- const QACTION_KEY = "qaction";
565
- const QFN_KEY = "qfunc";
566
- const QDATA_KEY = "qdata";
567
- const Q_ROUTE = "q:route";
568
- const DEFAULT_LOADERS_SERIALIZATION_STRATEGY = globalThis.__DEFAULT_LOADERS_SERIALIZATION_STRATEGY__ || "never";
569
-
570
- const RouteStateContext = /* @__PURE__ */ createContextId("qr-s");
571
- const RouteLoaderCtxContext = /* @__PURE__ */ createContextId("qr-lc");
572
- const ContentContext = /* @__PURE__ */ createContextId("qr-c");
573
- const ContentInternalContext = /* @__PURE__ */ createContextId("qr-ic");
574
- const DocumentHeadContext = /* @__PURE__ */ createContextId("qr-h");
575
- const RouteLocationContext = /* @__PURE__ */ createContextId("qr-l");
576
- const RouteNavigateContext = /* @__PURE__ */ createContextId("qr-n");
577
- const RouteActionContext = /* @__PURE__ */ createContextId("qr-a");
578
- const RoutePreventNavigateContext = /* @__PURE__ */ createContextId("qr-p");
579
- const HttpStatusContext = /* @__PURE__ */ createContextId("qr-hs");
580
-
581
- const REQUEST_ROUTE_LOADER_STATE = "@routeLoaderState";
582
- const REQUEST_LOADER_PATHS_STORE = "@loaderPathsStore";
583
- const REQUEST_ROUTE_LOADERS = "@routeLoaders";
584
- const REQUEST_ROUTE_LOADER_PROMISES = "@routeLoaderPromises";
585
- const REQUEST_ROUTE_LOADER_EVENTS = "@routeLoaderEvents";
586
- const REQUEST_ROUTE_LOADER_ROOT_EVENT = "@routeLoaderRootEvent";
587
- const ROUTE_LOADER_VALUE_PREFIX = "__qwik_route_loader_value__";
588
- const FULLPATH_HEADER = "X-Qwik-fullpath";
589
- const ROUTE_PATH_HEADER = "X-Qwik-route-path";
590
- const navFetchCache = /* @__PURE__ */ new Map();
591
- const clearNavFetchCache = () => navFetchCache.clear();
592
- const isRedirectStatus = (status) => status >= 300 && status < 400;
593
- const wrapWithAbort = (promise, signal) => {
594
- if (signal.aborted) {
595
- return Promise.reject(signal.reason);
596
- }
597
- return new Promise((resolve, reject) => {
598
- const abort = () => reject(signal.reason);
599
- signal.addEventListener("abort", abort, {
600
- once: true
601
- });
602
- promise.finally(() => signal.removeEventListener("abort", abort)).then(resolve, reject);
603
- });
113
+ /** Build a sorted, stable search string from only the allowed param names. */ var filterSearchParams = (params, allowed) => {
114
+ const filtered = new URLSearchParams();
115
+ for (let i = 0; i < allowed.length; i++) {
116
+ const name = allowed[i];
117
+ const values = params.getAll(name);
118
+ for (let j = 0; j < values.length; j++) filtered.append(name, values[j]);
119
+ }
120
+ filtered.sort();
121
+ return filtered.toString() ? `?${filtered.toString()}` : "";
604
122
  };
605
- const getRouteLoaderValueStateKey = (loaderId) => `${ROUTE_LOADER_VALUE_PREFIX}${loaderId}`;
606
- const isRequestEvent = (value) => !!value && typeof value === "object" && Object.prototype.hasOwnProperty.call(value, "sharedMap") && Object.prototype.hasOwnProperty.call(value, "cookie");
607
- const isLoaderInternal = (value) => typeof value === "function" && value.__brand === "server_loader";
608
- const fetchRouteLoaderData = async (loaderId, routePath, manifestHash, opts) => {
609
- if (!routePath) {
610
- return void 0;
611
- }
612
- let resolvedPath = routePath;
613
- const basePath = opts?.basePath ?? "/";
614
- if (basePath !== "/" && !resolvedPath.startsWith(basePath)) {
615
- resolvedPath = basePath + resolvedPath.slice(1);
616
- }
617
- const pathBase = ensureSlash(resolvedPath);
618
- const pageUrl = opts?.pageUrl;
619
- const search = pageUrl?.search ?? "";
620
- const url = `${pathBase}${getLoaderName(loaderId, manifestHash)}${search}`;
621
- const headers = {};
622
- if (pageUrl && pageUrl.pathname !== pathBase) {
623
- if (!globalThis.__STRICT_LOADERS__) {
624
- headers[FULLPATH_HEADER] = pageUrl.pathname;
625
- } else if (isDev) {
626
- headers[ROUTE_PATH_HEADER] = pageUrl.pathname;
627
- }
628
- }
629
- const cacheKey = `${url}
630
- ${headers[FULLPATH_HEADER] ?? ""}`;
631
- if (!opts?.ignoreCache) {
632
- const entry = navFetchCache.get(cacheKey);
633
- if (entry) {
634
- return opts?.signal ? wrapWithAbort(entry, opts.signal) : entry;
635
- }
636
- }
637
- const request = async () => {
638
- const response = await fetch(url, {
639
- signal: opts?.signal,
640
- cache: opts?.ignoreCache ? "reload" : "default",
641
- headers
642
- });
643
- if (response.redirected) {
644
- return {
645
- r: response.url
646
- };
647
- }
648
- if (isRedirectStatus(response.status)) {
649
- const location1 = response.headers.get("Location");
650
- if (location1) {
651
- return {
652
- r: location1
653
- };
654
- }
655
- }
656
- if (!response.ok) {
657
- return void 0;
658
- }
659
- return {
660
- raw: await response.text()
661
- };
662
- };
663
- if (opts?.ignoreCache) {
664
- return request();
665
- }
666
- if (opts?.signal) {
667
- return request().then((value) => {
668
- if (value !== void 0 && !navFetchCache.has(cacheKey)) {
669
- navFetchCache.set(cacheKey, Promise.resolve(value));
670
- }
671
- return value;
672
- });
673
- }
674
- const promise = request().then((value) => {
675
- if (value === void 0) {
676
- navFetchCache.delete(cacheKey);
677
- }
678
- return value;
679
- }, (err) => {
680
- navFetchCache.delete(cacheKey);
681
- throw err;
682
- });
683
- navFetchCache.set(cacheKey, promise);
684
- return promise;
123
+ var getLoaderOptions = (rest) => {
124
+ let id;
125
+ let serializationStrategy = DEFAULT_LOADERS_SERIALIZATION_STRATEGY;
126
+ let cacheControl;
127
+ let eTag;
128
+ let cacheKey;
129
+ let search;
130
+ let blockSSR = true;
131
+ const validators = [];
132
+ if (rest.length === 1) {
133
+ const options = rest[0];
134
+ if (options && typeof options === "object") {
135
+ if ("validate" in options) validators.push(options);
136
+ else {
137
+ if (options.id) id = options.id;
138
+ if (options.serializationStrategy) serializationStrategy = options.serializationStrategy;
139
+ if (options.validation) validators.push(...options.validation);
140
+ if ("cacheControl" in options) cacheControl = options.cacheControl;
141
+ if ("eTag" in options) eTag = options.eTag;
142
+ if ("cacheKey" in options) cacheKey = options.cacheKey;
143
+ if (options.search) search = options.search;
144
+ else if (globalThis.__STRICT_LOADERS__) search = [];
145
+ if (options.blockSSR === false) {
146
+ 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;
148
+ }
149
+ }
150
+ }
151
+ } else if (rest.length > 1) validators.push(...rest.filter(Boolean));
152
+ return {
153
+ id,
154
+ validators: validators.reverse(),
155
+ serializationStrategy,
156
+ cacheControl,
157
+ eTag,
158
+ cacheKey,
159
+ search,
160
+ blockSSR
161
+ };
685
162
  };
686
- const filterSearchParams = (params, allowed) => {
687
- const filtered = new URLSearchParams();
688
- for (let i = 0; i < allowed.length; i++) {
689
- const name = allowed[i];
690
- const values = params.getAll(name);
691
- for (let j = 0; j < values.length; j++) {
692
- filtered.append(name, values[j]);
693
- }
694
- }
695
- filtered.sort();
696
- return filtered.toString() ? `?${filtered.toString()}` : "";
163
+ /**
164
+ * Returns the current RequestEvent if possible. Only usable on the server, and only during request
165
+ * processing.
166
+ *
167
+ * @public
168
+ */ var getRequestEvent = (thisArg) => {
169
+ if (!isServer) throw new Error("getRequestEvent() can only be used on the server.");
170
+ return _asyncRequestStore?.getStore() || [thisArg, _getContextEvent()].find(isRequestEvent);
697
171
  };
698
- const getLoaderOptions = (rest) => {
699
- let id;
700
- let serializationStrategy = DEFAULT_LOADERS_SERIALIZATION_STRATEGY;
701
- let cacheControl;
702
- let eTag;
703
- let cacheKey;
704
- let search;
705
- let blockSSR = true;
706
- const validators = [];
707
- if (rest.length === 1) {
708
- const options = rest[0];
709
- if (options && typeof options === "object") {
710
- if ("validate" in options) {
711
- validators.push(options);
712
- } else {
713
- if (options.id) {
714
- id = options.id;
715
- }
716
- if (options.serializationStrategy) {
717
- serializationStrategy = options.serializationStrategy;
718
- }
719
- if (options.validation) {
720
- validators.push(...options.validation);
721
- }
722
- if ("cacheControl" in options) {
723
- cacheControl = options.cacheControl;
724
- }
725
- if ("eTag" in options) {
726
- eTag = options.eTag;
727
- }
728
- if ("cacheKey" in options) {
729
- cacheKey = options.cacheKey;
730
- }
731
- if (options.search) {
732
- search = options.search;
733
- } else if (globalThis.__STRICT_LOADERS__) {
734
- search = [];
735
- }
736
- if (options.blockSSR === false) {
737
- if (!__EXPERIMENTAL__.blockSSR) {
738
- 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.');
739
- }
740
- blockSSR = false;
741
- }
742
- }
743
- }
744
- } else if (rest.length > 1) {
745
- validators.push(...rest.filter(Boolean));
746
- }
747
- return {
748
- id,
749
- validators: validators.reverse(),
750
- serializationStrategy,
751
- cacheControl,
752
- eTag,
753
- cacheKey,
754
- search,
755
- blockSSR
756
- };
757
- };
758
- const getRequestEvent = (thisArg) => {
759
- if (!isServer$1) {
760
- throw new Error("getRequestEvent() can only be used on the server.");
761
- }
762
- return _asyncRequestStore?.getStore() || [
763
- thisArg,
764
- _getContextEvent()
765
- ].find(isRequestEvent);
766
- };
767
- const REQUEST_ROUTE_LOADER_VALUES = "@routeLoaderValues";
768
- function getRouteLoaderValues(requestEv) {
769
- let values = requestEv.sharedMap.get(REQUEST_ROUTE_LOADER_VALUES);
770
- if (!values) {
771
- values = {};
772
- requestEv.sharedMap.set(REQUEST_ROUTE_LOADER_VALUES, values);
773
- }
774
- return values;
172
+ 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);
175
+ if (!values) {
176
+ values = {};
177
+ requestEv.sharedMap.set(REQUEST_ROUTE_LOADER_VALUES, values);
178
+ }
179
+ return values;
775
180
  }
776
181
  function getRouteLoaderPromises(requestEv) {
777
- let promises = requestEv.sharedMap.get(REQUEST_ROUTE_LOADER_PROMISES);
778
- if (!promises) {
779
- promises = {};
780
- requestEv.sharedMap.set(REQUEST_ROUTE_LOADER_PROMISES, promises);
781
- }
782
- return promises;
182
+ let promises = requestEv.sharedMap.get(REQUEST_ROUTE_LOADER_PROMISES);
183
+ if (!promises) {
184
+ promises = {};
185
+ requestEv.sharedMap.set(REQUEST_ROUTE_LOADER_PROMISES, promises);
186
+ }
187
+ return promises;
783
188
  }
784
- function clearRouteLoaderData(requestEv) {
785
- requestEv.sharedMap.delete(REQUEST_ROUTE_LOADER_STATE);
786
- requestEv.sharedMap.delete(REQUEST_ROUTE_LOADER_VALUES);
787
- requestEv.sharedMap.delete(REQUEST_ROUTE_LOADER_PROMISES);
788
- requestEv.sharedMap.delete(REQUEST_ROUTE_LOADER_EVENTS);
789
- requestEv.sharedMap.delete(REQUEST_ROUTE_LOADER_ROOT_EVENT);
790
- requestEv.sharedMap.delete(REQUEST_ROUTE_LOADERS);
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);
791
196
  }
792
- function setRouteLoaders(requestEv, loaders) {
793
- requestEv.sharedMap.set(REQUEST_ROUTE_LOADERS, loaders);
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);
794
199
  }
795
- function getRouteLoaders(requestEv) {
796
- return requestEv.sharedMap.get(REQUEST_ROUTE_LOADERS) ?? [];
200
+ /** Get the route loader internals stored on the request. */ function getRouteLoaders(requestEv) {
201
+ return requestEv.sharedMap.get(REQUEST_ROUTE_LOADERS) ?? [];
797
202
  }
798
203
  function getRouteLoaderCtx(requestEv) {
799
- let ctx = requestEv.sharedMap.get(REQUEST_LOADER_PATHS_STORE);
800
- if (!ctx) {
801
- ctx = {
802
- loaderPaths: {}
803
- };
804
- requestEv.sharedMap.set(REQUEST_LOADER_PATHS_STORE, ctx);
805
- }
806
- return ctx;
204
+ let ctx = requestEv.sharedMap.get(REQUEST_LOADER_PATHS_STORE);
205
+ if (!ctx) {
206
+ ctx = { loaderPaths: {} };
207
+ requestEv.sharedMap.set(REQUEST_LOADER_PATHS_STORE, ctx);
208
+ }
209
+ return ctx;
807
210
  }
808
- const updateRouteLoaderPaths = (ctx, loaderPaths, pageUrl) => {
809
- if (!isServer$1) {
810
- ctx.pagePathname = pageUrl.pathname;
811
- ctx.pageSearch = pageUrl.search;
812
- if (isDev) {
813
- ctx.devRouteLoaderPaths = {};
814
- }
815
- }
816
- if (loaderPaths) {
817
- for (const key in loaderPaths) {
818
- ctx.loaderPaths[key] = loaderPaths[key];
819
- if (!isServer$1 && isDev) {
820
- ctx.devRouteLoaderPaths[key] = true;
821
- }
822
- }
823
- }
211
+ /**
212
+ * Update the loader paths store on client-side navigation.
213
+ *
214
+ * Only adds/updates entries for loaders present on the new route. Entries for loaders that are NOT
215
+ * on the new route are left untouched — their ComputedSignals keep their prior values, and no
216
+ * track() fires to invalidate them. If the user navigates back to a route where the loader IS
217
+ * present, the path updates and the signal re-fetches. This is the "stale is fine by default"
218
+ * contract: readers see old data until new data arrives.
219
+ */ var updateRouteLoaderPaths = (ctx, loaderPaths, pageUrl) => {
220
+ if (!isServer) {
221
+ ctx.pagePathname = pageUrl.pathname;
222
+ ctx.pageSearch = pageUrl.search;
223
+ if (isDev) ctx.devRouteLoaderPaths = {};
224
+ }
225
+ if (loaderPaths) for (const key in loaderPaths) {
226
+ ctx.loaderPaths[key] = loaderPaths[key];
227
+ if (!isServer && isDev) ctx.devRouteLoaderPaths[key] = true;
228
+ }
824
229
  };
825
- const getModuleRouteLoaders = (mods) => {
826
- const routeLoaders = [];
827
- const seen = /* @__PURE__ */ new Map();
828
- for (let i = 0; i < mods.length; i++) {
829
- const mod = mods[i];
830
- if (!mod) {
831
- continue;
832
- }
833
- for (const key in mod) {
834
- const value = mod[key];
835
- if (isLoaderInternal(value)) {
836
- const existing = seen.get(value.__id);
837
- if (existing) {
838
- if (isDev && existing !== value) {
839
- console.warn(`Two route loaders share the same id "${value.__id}". Only the first will run; the others are ignored. If they are created by a shared wrapper around routeLoader$, give each loader a distinct \`id\` option.`);
840
- }
841
- continue;
842
- }
843
- seen.set(value.__id, value);
844
- routeLoaders.push(value);
845
- }
846
- }
847
- }
848
- return routeLoaders;
230
+ var getModuleRouteLoaders = (mods) => {
231
+ const routeLoaders = [];
232
+ const seen = /* @__PURE__ */ new Map();
233
+ for (let i = 0; i < mods.length; i++) {
234
+ const mod = mods[i];
235
+ if (!mod) continue;
236
+ for (const key in mod) {
237
+ const value = mod[key];
238
+ if (isLoaderInternal(value)) {
239
+ const existing = seen.get(value.__id);
240
+ if (existing) {
241
+ if (isDev && existing !== value) console.warn(`Two route loaders share the same id "${value.__id}". Only the first will run; the others are ignored. If they are created by a shared wrapper around routeLoader$, give each loader a distinct \`id\` option.`);
242
+ continue;
243
+ }
244
+ seen.set(value.__id, value);
245
+ routeLoaders.push(value);
246
+ }
247
+ }
248
+ }
249
+ return routeLoaders;
849
250
  };
850
- const immutableLoaderIds = /* @__PURE__ */ new Set();
851
- const isImmutableLoader = (loaderId) => immutableLoaderIds.has(loaderId);
852
- const invalidateNavRouteLoaders = (state) => {
853
- for (const id in state) {
854
- if (!id.startsWith(ROUTE_LOADER_VALUE_PREFIX) && !immutableLoaderIds.has(id)) {
855
- state[id].invalidate();
856
- }
857
- }
251
+ /**
252
+ * Loader ids declared `cacheControl: 'immutable'`. Their data cannot change until a rebuild, so
253
+ * nav-wide invalidation skips them; they still re-fetch when their tracked URL inputs change.
254
+ * Registered on every nav via ensureRouteLoaderSignal, so resumed signals are covered too.
255
+ */ var immutableLoaderIds = /* @__PURE__ */ new Set();
256
+ var isImmutableLoader = (loaderId) => immutableLoaderIds.has(loaderId);
257
+ /**
258
+ * Invalidate every loader signal on navigation: the browser HTTP cache (driven by each loader's
259
+ * cacheControl) decides freshness. Unsubscribed signals only mark stale and refetch lazily when
260
+ * read again. Immutable loaders are skipped — their data cannot change until a rebuild; tracked URL
261
+ * inputs still invalidate them.
262
+ */ var invalidateNavRouteLoaders = (state) => {
263
+ for (const id in state) if (!id.startsWith(ROUTE_LOADER_VALUE_PREFIX) && !immutableLoaderIds.has(id)) state[id].invalidate();
858
264
  };
859
- const setLoaderSignalValue = (signal, value) => {
860
- _injectAsyncSignalValue(signal, value);
265
+ /**
266
+ * Inject a pre-loaded value into a ComputedSignal while preserving subscriptions. Delegates to the
267
+ * core helper which calls `invalidate({__v})` + `$computeIfNeeded$()` so the compute function runs
268
+ * synchronously and returns the pre-loaded value without fetching. Must go through the core helper
269
+ * because `$computeIfNeeded$` is mangled in core builds and not directly callable from this
270
+ * package.
271
+ */ var setLoaderSignalValue = (signal, value) => {
272
+ _injectAsyncSignalValue(signal, value);
861
273
  };
862
- const matchesRouteLoaderId = (loader, loaderId) => {
863
- return typeof loaderId === "string" && (loader.__id === loaderId || isDev && loader.__qrl.getHash() === loaderId);
274
+ var matchesRouteLoaderId = (loader, loaderId) => {
275
+ return typeof loaderId === "string" && (loader.__id === loaderId || isDev && loader.__qrl.getHash() === loaderId);
864
276
  };
865
- const resolveRouteLoaderByHash = (routeLoaders, loaderId) => {
866
- return routeLoaders.find((loader) => matchesRouteLoaderId(loader, loaderId));
277
+ var resolveRouteLoaderByHash = (routeLoaders, loaderId) => {
278
+ return routeLoaders.find((loader) => matchesRouteLoaderId(loader, loaderId));
867
279
  };
868
- const getLoaderRequestEvent = (loader, requestEv) => {
869
- let rootRequestEv = requestEv.sharedMap.get(REQUEST_ROUTE_LOADER_ROOT_EVENT);
870
- if (!rootRequestEv) {
871
- rootRequestEv = requestEv;
872
- requestEv.sharedMap.set(REQUEST_ROUTE_LOADER_ROOT_EVENT, rootRequestEv);
873
- }
874
- const url = new URL(rootRequestEv.url);
875
- const pathname = globalThis.__STRICT_LOADERS__ ? getRouteLoaderCtx(rootRequestEv).loaderPaths[loader.__id] || rootRequestEv.url.pathname : rootRequestEv.url.pathname;
876
- const filteredSearch = loader.__search ? filterSearchParams(url.searchParams, loader.__search) : rootRequestEv.url.search;
877
- if (pathname === rootRequestEv.url.pathname && filteredSearch === rootRequestEv.url.search) {
878
- return rootRequestEv;
879
- }
880
- let events = rootRequestEv.sharedMap.get(REQUEST_ROUTE_LOADER_EVENTS);
881
- if (!events) {
882
- events = /* @__PURE__ */ new Map();
883
- rootRequestEv.sharedMap.set(REQUEST_ROUTE_LOADER_EVENTS, events);
884
- }
885
- const eventKey = `${pathname}
886
- ${filteredSearch}`;
887
- let loaderRequestEv = events.get(eventKey);
888
- if (!loaderRequestEv) {
889
- url.pathname = pathname;
890
- url.search = filteredSearch;
891
- const request = new Request(url, rootRequestEv.request);
892
- loaderRequestEv = Object.create(rootRequestEv, {
893
- originalUrl: {
894
- value: new URL(url),
895
- enumerable: true
896
- },
897
- params: {
898
- value: {},
899
- enumerable: true
900
- },
901
- pathname: {
902
- get: () => url.pathname,
903
- enumerable: true
904
- },
905
- query: {
906
- get: () => url.searchParams,
907
- enumerable: true
908
- },
909
- request: {
910
- value: request,
911
- enumerable: true
912
- },
913
- url: {
914
- value: url,
915
- enumerable: true
916
- }
917
- });
918
- events.set(eventKey, loaderRequestEv);
919
- }
920
- return loaderRequestEv;
280
+ /** @internal */ var getLoaderRequestEvent = (loader, requestEv) => {
281
+ let rootRequestEv = requestEv.sharedMap.get(REQUEST_ROUTE_LOADER_ROOT_EVENT);
282
+ if (!rootRequestEv) {
283
+ rootRequestEv = requestEv;
284
+ requestEv.sharedMap.set(REQUEST_ROUTE_LOADER_ROOT_EVENT, rootRequestEv);
285
+ }
286
+ const url = new URL(rootRequestEv.url);
287
+ const pathname = globalThis.__STRICT_LOADERS__ ? getRouteLoaderCtx(rootRequestEv).loaderPaths[loader.__id] || rootRequestEv.url.pathname : rootRequestEv.url.pathname;
288
+ const filteredSearch = loader.__search ? filterSearchParams(url.searchParams, loader.__search) : rootRequestEv.url.search;
289
+ if (pathname === rootRequestEv.url.pathname && filteredSearch === rootRequestEv.url.search) return rootRequestEv;
290
+ let events = rootRequestEv.sharedMap.get(REQUEST_ROUTE_LOADER_EVENTS);
291
+ if (!events) {
292
+ events = /* @__PURE__ */ new Map();
293
+ rootRequestEv.sharedMap.set(REQUEST_ROUTE_LOADER_EVENTS, events);
294
+ }
295
+ const eventKey = `${pathname}\n${filteredSearch}`;
296
+ let loaderRequestEv = events.get(eventKey);
297
+ if (!loaderRequestEv) {
298
+ url.pathname = pathname;
299
+ url.search = filteredSearch;
300
+ const request = new Request(url, rootRequestEv.request);
301
+ loaderRequestEv = Object.create(rootRequestEv, {
302
+ originalUrl: {
303
+ value: new URL(url),
304
+ enumerable: true
305
+ },
306
+ params: {
307
+ value: {},
308
+ enumerable: true
309
+ },
310
+ pathname: {
311
+ get: () => url.pathname,
312
+ enumerable: true
313
+ },
314
+ query: {
315
+ get: () => url.searchParams,
316
+ enumerable: true
317
+ },
318
+ request: {
319
+ value: request,
320
+ enumerable: true
321
+ },
322
+ url: {
323
+ value: url,
324
+ enumerable: true
325
+ }
326
+ });
327
+ events.set(eventKey, loaderRequestEv);
328
+ }
329
+ return loaderRequestEv;
921
330
  };
922
- const detachResponseFromEvent = (requestEv) => {
923
- let status = 200;
924
- const headers = new Headers();
925
- return Object.create(requestEv, {
926
- headers: {
927
- value: headers,
928
- enumerable: true
929
- },
930
- status: {
931
- value: (statusCode) => {
932
- if (typeof statusCode === "number") {
933
- status = statusCode;
934
- }
935
- return status;
936
- },
937
- enumerable: true
938
- },
939
- error: {
940
- value: (statusCode, message) => {
941
- status = statusCode;
942
- return new ServerError(statusCode, message);
943
- },
944
- enumerable: true
945
- },
946
- redirect: {
947
- value: (statusCode, url) => {
948
- status = statusCode;
949
- if (url) {
950
- headers.set("Location", url);
951
- }
952
- return new RedirectMessage();
953
- },
954
- enumerable: true
955
- },
956
- fail: {
957
- value: (statusCode, data) => {
958
- status = statusCode;
959
- return {
960
- failed: true,
961
- ...data
962
- };
963
- },
964
- enumerable: true
965
- },
966
- // A background loader cannot set the page's cache headers.
967
- cacheControl: {
968
- value: () => {
969
- },
970
- enumerable: true
971
- }
972
- });
331
+ /**
332
+ * View of a request event whose response controls (status/headers/redirect/error/fail/cacheControl)
333
+ * are captured locally, so a background (`blockSSR: false`) loader's status/redirect/error surfaces
334
+ * on its own signal instead of mutating the page response. The originals also call `check()`, which
335
+ * throws once SSR streaming has started — a background loader must fail on its signal, not crash.
336
+ */ var detachResponseFromEvent = (requestEv) => {
337
+ let status = 200;
338
+ const headers = new Headers();
339
+ return Object.create(requestEv, {
340
+ headers: {
341
+ value: headers,
342
+ enumerable: true
343
+ },
344
+ status: {
345
+ value: (statusCode) => {
346
+ if (typeof statusCode === "number") status = statusCode;
347
+ return status;
348
+ },
349
+ enumerable: true
350
+ },
351
+ error: {
352
+ value: (statusCode, message) => {
353
+ status = statusCode;
354
+ return new ServerError(statusCode, message);
355
+ },
356
+ enumerable: true
357
+ },
358
+ redirect: {
359
+ value: (statusCode, url) => {
360
+ status = statusCode;
361
+ if (url) headers.set("Location", url);
362
+ return new RedirectMessage();
363
+ },
364
+ enumerable: true
365
+ },
366
+ fail: {
367
+ value: (statusCode, data) => {
368
+ status = statusCode;
369
+ return {
370
+ failed: true,
371
+ ...data
372
+ };
373
+ },
374
+ enumerable: true
375
+ },
376
+ cacheControl: {
377
+ value: () => {},
378
+ enumerable: true
379
+ }
380
+ });
973
381
  };
974
382
  async function runValidators(requestEv, validators, data) {
975
- let lastResult = {
976
- success: true,
977
- data
978
- };
979
- if (validators) {
980
- for (let i = 0; i < validators.length; i++) {
981
- const validator = validators[i];
982
- lastResult = await validator.validate(requestEv, data);
983
- if (!lastResult.success) {
984
- return lastResult;
985
- }
986
- data = lastResult.data;
987
- }
988
- }
989
- return lastResult;
383
+ let lastResult = {
384
+ success: true,
385
+ data
386
+ };
387
+ if (validators) for (let i = 0; i < validators.length; i++) {
388
+ lastResult = await validators[i].validate(requestEv, data);
389
+ if (!lastResult.success) return lastResult;
390
+ data = lastResult.data;
391
+ }
392
+ return lastResult;
990
393
  }
991
394
  function verifySerializable(data, qrl) {
992
- try {
993
- _verifySerializable(data, void 0);
994
- } catch (error) {
995
- if (error instanceof Error && qrl.dev) {
996
- error.loc = qrl.dev;
997
- }
998
- throw error;
999
- }
395
+ try {
396
+ _verifySerializable(data, void 0);
397
+ } catch (error) {
398
+ if (error instanceof Error && qrl.dev) error.loc = qrl.dev;
399
+ throw error;
400
+ }
1000
401
  }
1001
- const getRouteLoaderData = async (loaderQrl, validators, requestEv) => {
1002
- const loaderRequestEv = requestEv;
1003
- const result = await runValidators(requestEv, validators, void 0);
1004
- if (!result.success) {
1005
- return loaderRequestEv.fail(result.status ?? 500, result.error);
1006
- }
1007
- const resolved = await loaderQrl.call(loaderRequestEv, loaderRequestEv);
1008
- const value = typeof resolved === "function" ? resolved() : resolved;
1009
- throwIfControlFlowSignal(value);
1010
- if (isDev) {
1011
- verifySerializable(value, loaderQrl);
1012
- }
1013
- return value;
402
+ /** Run a loader and return its raw value. Errors/redirects propagate as exceptions. */ var getRouteLoaderData = async (loaderQrl, validators, requestEv) => {
403
+ const loaderRequestEv = requestEv;
404
+ const result = await runValidators(requestEv, validators, void 0);
405
+ if (!result.success) return loaderRequestEv.fail(result.status ?? 500, result.error);
406
+ const resolved = await loaderQrl.call(loaderRequestEv, loaderRequestEv);
407
+ const value = typeof resolved === "function" ? resolved() : resolved;
408
+ throwIfControlFlowSignal(value);
409
+ if (isDev) verifySerializable(value, loaderQrl);
410
+ return value;
1014
411
  };
1015
- const loadRouteLoaderByQrl = (loaderId, loaderQrl, validators, requestEv) => {
1016
- const values = getRouteLoaderValues(requestEv);
1017
- if (loaderId in values) {
1018
- return Promise.resolve(values[loaderId]);
1019
- }
1020
- const promises = getRouteLoaderPromises(requestEv);
1021
- let promise = promises[loaderId];
1022
- if (!promise) {
1023
- promise = getRouteLoaderData(loaderQrl, validators, requestEv).then((value) => {
1024
- values[loaderId] = value;
1025
- return value;
1026
- }, (err) => {
1027
- delete promises[loaderId];
1028
- throw err;
1029
- });
1030
- promises[loaderId] = promise;
1031
- }
1032
- return promise;
412
+ var loadRouteLoaderByQrl = (loaderId, loaderQrl, validators, requestEv) => {
413
+ const values = getRouteLoaderValues(requestEv);
414
+ if (loaderId in values) return Promise.resolve(values[loaderId]);
415
+ const promises = getRouteLoaderPromises(requestEv);
416
+ let promise = promises[loaderId];
417
+ if (!promise) {
418
+ promise = getRouteLoaderData(loaderQrl, validators, requestEv).then((value) => {
419
+ values[loaderId] = value;
420
+ return value;
421
+ }, (err) => {
422
+ delete promises[loaderId];
423
+ throw err;
424
+ });
425
+ promises[loaderId] = promise;
426
+ }
427
+ return promise;
1033
428
  };
1034
- class ServerRouteLoaderCapture {
1035
- hash;
1036
- qrl;
1037
- validators;
1038
- blockSSR;
1039
- constructor(hash, qrl, validators, blockSSR) {
1040
- this.hash = hash;
1041
- this.qrl = qrl;
1042
- this.validators = validators;
1043
- this.blockSSR = blockSSR;
1044
- }
1045
- load() {
1046
- const requestEv = getRequestEvent();
1047
- if (!requestEv) {
1048
- throw new Error("Unable to determine the current RequestEvent.");
1049
- }
1050
- const values = getRouteLoaderValues(requestEv);
1051
- if (this.hash in values) {
1052
- return values[this.hash];
1053
- }
1054
- const ev = this.blockSSR ? requestEv : detachResponseFromEvent(requestEv);
1055
- return loadRouteLoaderByQrl(this.hash, this.qrl, this.validators, ev);
1056
- }
1057
- [SerializerSymbol]() {
1058
- return this.hash;
1059
- }
1060
- }
1061
- const createRouteLoaderSignal = (loader, routeLoaderCtx, state) => {
1062
- const id = loader.__id;
1063
- const stateValues = state;
1064
- const resumeValueKey = getRouteLoaderValueStateKey(id);
1065
- const capture = isServer$1 ? new ServerRouteLoaderCapture(id, loader.__qrl, loader.__validators, loader.__blockSSR) : id;
1066
- const searchFilter = loader.__search;
1067
- const lastFetch = {};
1068
- const signal = createComputedQrl(/* @__PURE__ */ inlinedQrl(async (ctx) => {
1069
- const capture2 = _captures[0], id2 = _captures[1], lastFetch2 = _captures[2], resumeValueKey2 = _captures[3], routeLoaderCtx2 = _captures[4], searchFilter2 = _captures[5], stateValues2 = _captures[6];
1070
- const hasInjectedValue = !!ctx.info && typeof ctx.info === "object" && "__v" in ctx.info;
1071
- const trackedRoutePath = ctx.track(routeLoaderCtx2.loaderPaths, id2);
1072
- const trackedPagePathname = ctx.track(routeLoaderCtx2, "pagePathname");
1073
- const trackedPageSearch = ctx.track(routeLoaderCtx2, "pageSearch");
1074
- if (hasInjectedValue) {
1075
- const value = ctx.info.__v;
1076
- if (!isServer$1 && resumeValueKey2 in stateValues2) {
1077
- stateValues2[resumeValueKey2] = value;
1078
- }
1079
- lastFetch2.raw = void 0;
1080
- return value;
1081
- }
1082
- if (isServer$1) {
1083
- return capture2.load();
1084
- }
1085
- const pagePathname = trackedPagePathname || location.pathname;
1086
- const pageSearch = trackedPageSearch || location.search;
1087
- const pageUrl = new URL(pagePathname + pageSearch, location.href);
1088
- const mHash = routeLoaderCtx2.manifestHash || "dev";
1089
- const basePath = qwikRouterConfig.basePathname ?? "/";
1090
- const needsResumeFetch = stateValues2[resumeValueKey2] === _UNINITIALIZED;
1091
- const fetchRoutePath = trackedRoutePath || (needsResumeFetch ? pageUrl.pathname : void 0);
1092
- if (!fetchRoutePath) {
1093
- return ctx.previous;
1094
- }
1095
- let fetchUrl = pageUrl;
1096
- if (searchFilter2) {
1097
- fetchUrl = new URL(pageUrl.href);
1098
- fetchUrl.search = filterSearchParams(pageUrl.searchParams, searchFilter2);
1099
- }
1100
- const result = await fetchRouteLoaderData(id2, fetchRoutePath, mHash, {
1101
- pageUrl: fetchUrl,
1102
- basePath,
1103
- ignoreCache: ctx.info === true,
1104
- signal: ctx.abortSignal
1105
- });
1106
- if (!result) {
1107
- throw new Error(`Loader ${id2} returned empty response`);
1108
- }
1109
- let response;
1110
- if (result.raw === void 0) {
1111
- response = result;
1112
- } else {
1113
- if (result.raw === lastFetch2.raw && ctx.previous !== void 0) {
1114
- return ctx.previous;
1115
- }
1116
- response = await _deserialize(result.raw);
1117
- if (!response) {
1118
- throw new Error(`Loader ${id2} returned empty response`);
1119
- }
1120
- }
1121
- if (response.r) {
1122
- const goto = routeLoaderCtx2.goto;
1123
- if (goto) {
1124
- goto(response.r, {
1125
- replaceState: true
1126
- });
1127
- } else {
1128
- location.href = response.r;
1129
- }
1130
- return ctx.previous;
1131
- }
1132
- if (response.e) {
1133
- throw response.e;
1134
- }
1135
- lastFetch2.raw = result.raw;
1136
- if (needsResumeFetch) {
1137
- stateValues2[resumeValueKey2] = response.d;
1138
- }
1139
- return response.d;
1140
- }, "createRouteLoaderSignal_signal_createComputed_4s4ZDCYhDOA", [
1141
- capture,
1142
- id,
1143
- lastFetch,
1144
- resumeValueKey,
1145
- routeLoaderCtx,
1146
- searchFilter,
1147
- stateValues
1148
- ]), {
1149
- serializationStrategy: loader.__serializationStrategy
1150
- });
1151
- _markSignalAsExternallyOwned(signal);
1152
- return signal;
429
+ var ServerRouteLoaderCapture = class {
430
+ hash;
431
+ qrl;
432
+ validators;
433
+ blockSSR;
434
+ constructor(hash, qrl, validators, blockSSR) {
435
+ this.hash = hash;
436
+ this.qrl = qrl;
437
+ this.validators = validators;
438
+ this.blockSSR = blockSSR;
439
+ }
440
+ load() {
441
+ const requestEv = getRequestEvent();
442
+ if (!requestEv) throw new Error("Unable to determine the current RequestEvent.");
443
+ const values = getRouteLoaderValues(requestEv);
444
+ if (this.hash in values) return values[this.hash];
445
+ const ev = this.blockSSR ? requestEv : detachResponseFromEvent(requestEv);
446
+ return loadRouteLoaderByQrl(this.hash, this.qrl, this.validators, ev);
447
+ }
448
+ [SerializerSymbol]() {
449
+ return this.hash;
450
+ }
1153
451
  };
1154
- const ensureRouteLoaderSignal = (loader, state, routeLoaderCtx) => {
1155
- if (loader.__cacheControl === "immutable") {
1156
- immutableLoaderIds.add(loader.__id);
1157
- }
1158
- if (isServer$1 && loader.__serializationStrategy === "never") {
1159
- state[getRouteLoaderValueStateKey(loader.__id)] = _UNINITIALIZED;
1160
- }
1161
- return state[loader.__id] ||= createRouteLoaderSignal(loader, routeLoaderCtx, state);
452
+ var createRouteLoaderSignal = (loader, routeLoaderCtx, state) => {
453
+ const id = loader.__id;
454
+ const stateValues = state;
455
+ const resumeValueKey = getRouteLoaderValueStateKey(id);
456
+ const capture = isServer ? new ServerRouteLoaderCapture(id, loader.__qrl, loader.__validators, loader.__blockSSR) : id;
457
+ const searchFilter = loader.__search;
458
+ const signal = createComputedQrl(/*#__PURE__*/ inlinedQrl(async (ctx) => {
459
+ const capture = _captures[0], id = _captures[1], lastFetch = _captures[2], resumeValueKey = _captures[3], routeLoaderCtx = _captures[4], searchFilter = _captures[5], stateValues = _captures[6];
460
+ const hasInjectedValue = !!ctx.info && typeof ctx.info === "object" && "__v" in ctx.info;
461
+ const trackedRoutePath = ctx.track(routeLoaderCtx.loaderPaths, id);
462
+ const trackedPagePathname = ctx.track(routeLoaderCtx, "pagePathname");
463
+ const trackedPageSearch = ctx.track(routeLoaderCtx, "pageSearch");
464
+ if (hasInjectedValue) {
465
+ const value = ctx.info.__v;
466
+ if (!isServer && resumeValueKey in stateValues) stateValues[resumeValueKey] = value;
467
+ lastFetch.raw = void 0;
468
+ return value;
469
+ }
470
+ if (isServer) return capture.load();
471
+ const pagePathname = trackedPagePathname || location.pathname;
472
+ const pageSearch = trackedPageSearch || location.search;
473
+ const pageUrl = new URL(pagePathname + pageSearch, location.href);
474
+ const mHash = routeLoaderCtx.manifestHash || "dev";
475
+ const basePath = qwikRouterConfig.basePathname ?? "/";
476
+ const needsResumeFetch = stateValues[resumeValueKey] === _UNINITIALIZED;
477
+ const fetchRoutePath = trackedRoutePath || (needsResumeFetch ? pageUrl.pathname : void 0);
478
+ if (!fetchRoutePath) return ctx.previous;
479
+ let fetchUrl = pageUrl;
480
+ if (searchFilter) {
481
+ fetchUrl = new URL(pageUrl.href);
482
+ fetchUrl.search = filterSearchParams(pageUrl.searchParams, searchFilter);
483
+ }
484
+ const result = await fetchRouteLoaderData(id, fetchRoutePath, mHash, {
485
+ pageUrl: fetchUrl,
486
+ basePath,
487
+ ignoreCache: ctx.info === true,
488
+ signal: ctx.abortSignal
489
+ });
490
+ if (!result) throw new Error(`Loader ${id} returned empty response`);
491
+ let response;
492
+ if (result.raw === void 0) response = result;
493
+ else {
494
+ if (result.raw === lastFetch.raw && ctx.previous !== void 0) return ctx.previous;
495
+ response = await _deserialize(result.raw);
496
+ if (!response) throw new Error(`Loader ${id} returned empty response`);
497
+ }
498
+ if (response.r) {
499
+ const goto = routeLoaderCtx.goto;
500
+ if (goto) goto(response.r, { replaceState: true });
501
+ else location.href = response.r;
502
+ return ctx.previous;
503
+ }
504
+ if (response.e) throw response.e;
505
+ lastFetch.raw = result.raw;
506
+ if (needsResumeFetch) stateValues[resumeValueKey] = response.d;
507
+ return response.d;
508
+ }, "createRouteLoaderSignal_signal_createComputed_4s4ZDCYhDOA", [
509
+ capture,
510
+ id,
511
+ {},
512
+ resumeValueKey,
513
+ routeLoaderCtx,
514
+ searchFilter,
515
+ stateValues
516
+ ]), { serializationStrategy: loader.__serializationStrategy });
517
+ _markSignalAsExternallyOwned(signal);
518
+ return signal;
1162
519
  };
1163
- const ensureRouteLoaderSignals = (mods, state, routeLoaderCtx) => {
1164
- const loaders = getModuleRouteLoaders(mods);
1165
- for (let i = 0; i < loaders.length; i++) {
1166
- const loader = loaders[i];
1167
- if (isDev && !isServer$1) {
1168
- const devCtx = routeLoaderCtx;
1169
- if (devCtx.pagePathname && !devCtx.devRouteLoaderPaths?.[loader.__id]) {
1170
- devCtx.loaderPaths[loader.__id] = devCtx.pagePathname;
1171
- }
1172
- }
1173
- ensureRouteLoaderSignal(loader, state, routeLoaderCtx);
1174
- }
1175
- return loaders;
520
+ var ensureRouteLoaderSignal = (loader, state, routeLoaderCtx) => {
521
+ if (loader.__cacheControl === "immutable") immutableLoaderIds.add(loader.__id);
522
+ if (isServer && loader.__serializationStrategy === "never") state[getRouteLoaderValueStateKey(loader.__id)] = _UNINITIALIZED;
523
+ return state[loader.__id] ||= createRouteLoaderSignal(loader, routeLoaderCtx, state);
1176
524
  };
1177
- const loadRouteLoader = (loader, requestEv) => {
1178
- const loaderEv = getLoaderRequestEvent(loader, requestEv);
1179
- return loadRouteLoaderByQrl(
1180
- loader.__id,
1181
- loader.__qrl,
1182
- loader.__validators,
1183
- // A background (blockSSR:false) loader must not touch the page response.
1184
- loader.__blockSSR ? loaderEv : detachResponseFromEvent(loaderEv)
1185
- );
525
+ var ensureRouteLoaderSignals = (mods, state, routeLoaderCtx) => {
526
+ const loaders = getModuleRouteLoaders(mods);
527
+ for (let i = 0; i < loaders.length; i++) {
528
+ const loader = loaders[i];
529
+ if (isDev && !isServer) {
530
+ const devCtx = routeLoaderCtx;
531
+ if (devCtx.pagePathname && !devCtx.devRouteLoaderPaths?.[loader.__id]) devCtx.loaderPaths[loader.__id] = devCtx.pagePathname;
532
+ }
533
+ ensureRouteLoaderSignal(loader, state, routeLoaderCtx);
534
+ }
535
+ return loaders;
1186
536
  };
1187
- const getRouteLoaderResponse = async (loaderQrl, validators, requestEv) => {
1188
- try {
1189
- const value = await getRouteLoaderData(loaderQrl, validators, requestEv);
1190
- return {
1191
- d: value
1192
- };
1193
- } catch (err) {
1194
- if (err instanceof RedirectMessage) {
1195
- const location1 = requestEv.headers.get("Location") || "/";
1196
- requestEv.headers.delete("Location");
1197
- return {
1198
- r: location1
1199
- };
1200
- }
1201
- if (err instanceof ServerError) {
1202
- return {
1203
- e: err
1204
- };
1205
- }
1206
- throw err;
1207
- }
537
+ var loadRouteLoader = (loader, requestEv) => {
538
+ const loaderEv = getLoaderRequestEvent(loader, requestEv);
539
+ return loadRouteLoaderByQrl(loader.__id, loader.__qrl, loader.__validators, loader.__blockSSR ? loaderEv : detachResponseFromEvent(loaderEv));
540
+ };
541
+ /** Run a loader and wrap the result in a LoaderResponse envelope. Catches redirects/errors. */ var getRouteLoaderResponse = async (loaderQrl, validators, requestEv) => {
542
+ try {
543
+ return { d: await getRouteLoaderData(loaderQrl, validators, requestEv) };
544
+ } catch (err) {
545
+ if (err instanceof RedirectMessage) {
546
+ const location1 = requestEv.headers.get("Location") || "/";
547
+ requestEv.headers.delete("Location");
548
+ return { r: location1 };
549
+ }
550
+ if (err instanceof ServerError) return { e: err };
551
+ throw err;
552
+ }
553
+ };
554
+ /** @internal */ var routeLoaderQrl = (loaderQrl, ...rest) => {
555
+ const { id, validators, serializationStrategy, cacheControl, eTag, cacheKey, search, blockSSR } = getLoaderOptions(rest);
556
+ function loader() {
557
+ const state = _resolveContextWithoutSequentialScope(RouteStateContext);
558
+ let signal = state[loader.__id];
559
+ if (!signal) signal = ensureRouteLoaderSignal(loader, state, _resolveContextWithoutSequentialScope(RouteLoaderCtxContext));
560
+ signal.promise();
561
+ return signal;
562
+ }
563
+ loader.__brand = "server_loader";
564
+ loader.__qrl = loaderQrl;
565
+ loader.__validators = validators;
566
+ loader.__id = id ?? loaderQrl.getHash();
567
+ loader.__serializationStrategy = serializationStrategy;
568
+ loader.__cacheControl = cacheControl;
569
+ loader.__eTag = eTag;
570
+ loader.__cacheKey = cacheKey;
571
+ loader.__search = search;
572
+ loader.__blockSSR = blockSSR;
573
+ Object.freeze(loader);
574
+ return loader;
575
+ };
576
+ /**
577
+ * Define a route loader that fetches data before the route renders.
578
+ *
579
+ * Route loaders run on the server during SSR and return data as a `ComputedSignal`. On the client,
580
+ * loaders automatically re-fetch when the route changes (SPA navigation). Each loader gets its own
581
+ * JSON endpoint (`q-loader-{id}.{hash}.json`), so only the loaders present on the target route are
582
+ * fetched.
583
+ *
584
+ * **Important:** Route loader data uses Qwik's custom serialization format, not standard JSON. This
585
+ * means the data supports features like circular references, Dates, and other non-JSON types, but
586
+ * it cannot be consumed by external clients expecting plain JSON.
587
+ *
588
+ * ## Options
589
+ *
590
+ * - `search: string[]`: Allowlist of URL search params the loader depends on. Only listed params are
591
+ * sent in the request and changes to other params are ignored. During SSR and loader JSON
592
+ * requests, the loader's request event is filtered to those params too. `search: []` means no
593
+ * search params are sent and only route path changes trigger a re-fetch.
594
+ * - `eTag`: Enable ETag-based caching. Can be `true` (auto-hash), a string, or a function.
595
+ * - `cacheControl`: Cache-Control for loader JSON responses; the browser HTTP cache controls
596
+ * client-side freshness. `'immutable'` additionally lets SSG write the loader file.
597
+ *
598
+ * The `strictLoaders` Vite plugin option applies `search: []` globally for all loaders that don't
599
+ * specify an explicit `search` option.
600
+ *
601
+ * @public
602
+ */ var routeLoader$ = /*#__PURE__*/ implicit$FirstArg(routeLoaderQrl);
603
+ var deepFreeze = (obj) => {
604
+ if (obj == null) return obj;
605
+ Object.getOwnPropertyNames(obj).forEach((prop) => {
606
+ const value = obj[prop];
607
+ if (value && typeof value === "object" && !Object.isFrozen(value)) try {
608
+ deepFreeze(value);
609
+ } catch {
610
+ return obj;
611
+ }
612
+ });
613
+ return Object.freeze(obj);
614
+ };
615
+ /** LoadRoute() runs in both client and server. */ var MODULE_CACHE = /*#__PURE__*/ new WeakMap();
616
+ /** Built-in fallback error component loader */ var httpErrorLoader = () => import("./http-error.qwik.mjs");
617
+ /**
618
+ * Walk trie keys from root, gathering `_L` layouts along the way. Returns the final node and
619
+ * collected layouts, or undefined if any key is missing.
620
+ */ function walkTrieKeys(root, keys) {
621
+ let node = root;
622
+ const layouts = [];
623
+ if (node._L) layouts.push(node._L);
624
+ for (let i = 0; i < keys.length; i++) {
625
+ const key = keys[i];
626
+ let next = node[key];
627
+ if (!next && node._M) for (let j = 0; j < node._M.length; j++) {
628
+ const group = node._M[j];
629
+ next = group[key];
630
+ if (next) {
631
+ if (group._L) layouts.push(group._L);
632
+ break;
633
+ }
634
+ }
635
+ if (!next) return;
636
+ node = next;
637
+ if (node._L) layouts.push(node._L);
638
+ }
639
+ return {
640
+ node,
641
+ layouts
642
+ };
643
+ }
644
+ /**
645
+ * The full chain to render for a boundary: a bare loader in its snapshot layouts, an override
646
+ * as-is.
647
+ */ var boundaryChain = (ref) => ref.v ? Array.isArray(ref.v) ? ref.v : [...ref.layouts, ref.v] : void 0;
648
+ /**
649
+ * Collect layouts, the nearest error (`_E`) and not-found (`_4`) boundaries, and the menu from a
650
+ * node and the group ancestors entered to reach it.
651
+ */ function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, matchedPathname = "/") {
652
+ for (let j = 0; j < groups.length; j++) {
653
+ const g = groups[j];
654
+ if (g._L) layouts.push(g._L);
655
+ if (g._R && loaderHashes) {
656
+ loaderHashes.push(...g._R);
657
+ if (loaderPathsByHash) for (let i = 0; i < g._R.length; i++) {
658
+ const hash = g._R[i];
659
+ loaderPathsByHash[hash] = matchedPathname;
660
+ }
661
+ }
662
+ if (g._E) {
663
+ errorLoaderRef.v = g._E;
664
+ errorLoaderRef.layouts = [...layouts];
665
+ }
666
+ if (g._4) {
667
+ notFoundLoaderRef.v = g._4;
668
+ notFoundLoaderRef.layouts = [...layouts];
669
+ }
670
+ if (g._N) menuLoaderRef.v = g._N;
671
+ }
672
+ if (node._L) layouts.push(node._L);
673
+ if (node._R && loaderHashes) {
674
+ loaderHashes.push(...node._R);
675
+ if (loaderPathsByHash) for (let i = 0; i < node._R.length; i++) {
676
+ const hash = node._R[i];
677
+ loaderPathsByHash[hash] = matchedPathname;
678
+ }
679
+ }
680
+ if (node._E) {
681
+ errorLoaderRef.v = node._E;
682
+ errorLoaderRef.layouts = [...layouts];
683
+ }
684
+ if (node._4) {
685
+ notFoundLoaderRef.v = node._4;
686
+ notFoundLoaderRef.layouts = [...layouts];
687
+ }
688
+ if (node._N) menuLoaderRef.v = node._N;
689
+ }
690
+ /** Try to match a segment against a node's wildcard children (_W, _A). */ function tryWildcardMatch(node, part, partLower, parts, partIndex) {
691
+ let next = node._W;
692
+ if (next) {
693
+ const prefix = next._0;
694
+ const suffix = next._9;
695
+ if (prefix || suffix) {
696
+ const pre = prefix || "";
697
+ const suf = suffix || "";
698
+ if (partLower.length > pre.length + suf.length && (!pre || partLower.startsWith(pre.toLowerCase())) && (!suf || partLower.endsWith(suf.toLowerCase()))) {
699
+ const paramName = next._P;
700
+ const value = part.slice(pre.length, suf ? part.length - suf.length : void 0);
701
+ return {
702
+ next,
703
+ routePart: `${pre}[${paramName}]${suf}`,
704
+ done: false,
705
+ kind: 1,
706
+ paramName,
707
+ paramValue: value
708
+ };
709
+ }
710
+ } else {
711
+ const paramName = next._P;
712
+ return {
713
+ next,
714
+ routePart: `[${paramName}]`,
715
+ done: false,
716
+ kind: 1,
717
+ paramName,
718
+ paramValue: part
719
+ };
720
+ }
721
+ }
722
+ next = node._A;
723
+ if (next) {
724
+ const paramName = next._P;
725
+ const restValue = parts.slice(partIndex).join("/");
726
+ return {
727
+ next,
728
+ routePart: `[...${paramName}]`,
729
+ done: true,
730
+ kind: 2,
731
+ paramName,
732
+ paramValue: restValue
733
+ };
734
+ }
735
+ }
736
+ /**
737
+ * Try to find a child matching `partLower` in `node`, including inside group children (_M). Returns
738
+ * the matched child node, the groups entered to reach it, what to push to routeParts, and which
739
+ * kind of edge matched. Returns undefined if no match.
740
+ *
741
+ * Priority: exact match → _M groups (recursively) → _W wildcard → _A rest wildcard. This ensures
742
+ * exact matches inside nested groups take precedence over wildcards at parent level.
743
+ */ function findChild(node, part, partLower, parts, partIndex) {
744
+ const exact = node[partLower];
745
+ if (exact) return {
746
+ next: exact,
747
+ groups: [],
748
+ routePart: part,
749
+ done: false,
750
+ kind: 0
751
+ };
752
+ if (node._M) for (let j = 0; j < node._M.length; j++) {
753
+ const group = node._M[j];
754
+ const groupResult = findChild(group, part, partLower, parts, partIndex);
755
+ if (groupResult) {
756
+ groupResult.groups.unshift(group);
757
+ return groupResult;
758
+ }
759
+ }
760
+ const wildcard = tryWildcardMatch(node, part, partLower, parts, partIndex);
761
+ if (wildcard) return {
762
+ ...wildcard,
763
+ groups: []
764
+ };
765
+ }
766
+ /**
767
+ * Descend through a node and its pathless `_M` groups (depth-first, in `_M` order), returning the
768
+ * first node for which `hit` yields a value, plus the chain of groups entered to reach it. The
769
+ * shared traversal behind the index / rest / boundary lookups inside groups.
770
+ */ function descendGroups(node, hit) {
771
+ const value = hit(node);
772
+ if (value !== void 0) return {
773
+ value,
774
+ node,
775
+ groups: []
776
+ };
777
+ if (node._M) for (let j = 0; j < node._M.length; j++) {
778
+ const group = node._M[j];
779
+ const result = descendGroups(group, hit);
780
+ if (result) {
781
+ result.groups.unshift(group);
782
+ return result;
783
+ }
784
+ }
785
+ }
786
+ /**
787
+ * Find a node with `_I`/`_G` at the current level, including inside `_M` groups. Returns it and the
788
+ * groups entered to reach it — excluding the target itself, whose `_L` the caller applies (so it
789
+ * isn't double-counted).
790
+ */ function findIndexNode(node) {
791
+ const r = descendGroups(node, (n) => n._I || n._G != null ? n : void 0);
792
+ return r ? {
793
+ target: r.node,
794
+ groups: r.groups.filter((g) => g !== r.node)
795
+ } : void 0;
796
+ }
797
+ /**
798
+ * Given a matched node that has `_I` or `_G`, resolve the final loaders array.
799
+ *
800
+ * - `_G`: re-walk from root to gather target's layouts and `_I`
801
+ * - `_I` array: use as-is (override, e.g. layout stop)
802
+ * - `_I` function: prepend gathered layouts
803
+ */ function resolveLoaders(root, node, gatheredLayouts) {
804
+ if (node._G != null) {
805
+ const target = walkTrieKeys(root, node._G.split("/").filter((p) => p.length > 0));
806
+ if (!target) return;
807
+ let targetNode = target.node;
808
+ const targetLayouts = target.layouts;
809
+ if (!targetNode._I && targetNode._G == null) {
810
+ const indexResult = findIndexNode(targetNode);
811
+ if (indexResult) {
812
+ for (let j = 0; j < indexResult.groups.length; j++) {
813
+ const g = indexResult.groups[j];
814
+ if (g._L) targetLayouts.push(g._L);
815
+ }
816
+ targetNode = indexResult.target;
817
+ if (targetNode._L) targetLayouts.push(targetNode._L);
818
+ }
819
+ }
820
+ return resolveLoaders(root, targetNode, targetLayouts);
821
+ }
822
+ const index = node._I;
823
+ if (!index) return;
824
+ if (Array.isArray(index)) return index;
825
+ return [...gatheredLayouts, index];
826
+ }
827
+ /**
828
+ * Find a rest wildcard (`_A`) at the current node or inside its `_M` groups, with the groups
829
+ * entered to reach it. Used to save fallback checkpoints during matching.
830
+ */ function findRestNode(node) {
831
+ const r = descendGroups(node, (n) => n._A);
832
+ return r ? {
833
+ next: r.value,
834
+ groups: r.groups
835
+ } : void 0;
836
+ }
837
+ /**
838
+ * Find a `_4`/`_E` boundary inside a node's pathless `_M` groups (first in `_M` order), wrapped in
839
+ * those groups' layouts.
840
+ */ function findGroupBoundary(node, layouts, kind) {
841
+ const r = descendGroups(node, (n) => n[kind]);
842
+ if (!r) return;
843
+ const groupLayouts = [...layouts];
844
+ for (const g of r.groups) if (g._L) groupLayouts.push(g._L);
845
+ return {
846
+ v: r.value,
847
+ layouts: groupLayouts
848
+ };
849
+ }
850
+ /**
851
+ * Match a URL pathname against the route trie.
852
+ *
853
+ * Always returns a result. When no route matches, `notFound` is true and `loaders` contains only
854
+ * the error component (from the nearest `_E` or `_4` ancestor, or the built-in fallback).
855
+ *
856
+ * Group nodes (in `_M` arrays) are pathless — they don't consume a URL segment but contribute their
857
+ * `_L` layout to the chain. The matcher transparently enters groups when searching for a matching
858
+ * child.
859
+ */ function matchRouteTree(root = {}, pathname) {
860
+ let node = root;
861
+ const params = {};
862
+ const routeParts = [];
863
+ const layouts = [];
864
+ const loaderHashes = [];
865
+ const loaderPathsByHash = {};
866
+ const errorLoaderRef = {
867
+ v: void 0,
868
+ layouts: []
869
+ };
870
+ const notFoundLoaderRef = {
871
+ v: void 0,
872
+ layouts: []
873
+ };
874
+ const menuLoaderRef = { v: void 0 };
875
+ const groupNodes = [];
876
+ collectNodeMeta(root, [], layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash);
877
+ if (root._M) groupNodes.push({
878
+ node: root,
879
+ depth: layouts.length
880
+ });
881
+ let done = false;
882
+ let matched = true;
883
+ const parts = pathname.split("/").filter((p) => p.length > 0).map(decodeURIComponent);
884
+ let restFallback;
885
+ let i = 0;
886
+ const len = parts.length;
887
+ for (; !done && i < len; i++) {
888
+ const part = parts[i];
889
+ const partLower = part.toLowerCase();
890
+ const found = findChild(node, part, partLower, parts, i);
891
+ if (!found) {
892
+ matched = false;
893
+ break;
894
+ }
895
+ const restInfo = found.kind === 1 ? findRestNode(node) : void 0;
896
+ if (restInfo) restFallback = {
897
+ aNode: restInfo.next,
898
+ groups: restInfo.groups,
899
+ paramName: restInfo.next._P,
900
+ restValue: parts.slice(i).join("/"),
901
+ routeParts: [...routeParts],
902
+ params: { ...params },
903
+ layouts: [...layouts],
904
+ loaderPathsByHash: { ...loaderPathsByHash },
905
+ errorLoader: errorLoaderRef.v,
906
+ errorLayouts: errorLoaderRef.layouts,
907
+ notFoundLoader: notFoundLoaderRef.v,
908
+ notFoundLayouts: notFoundLoaderRef.layouts,
909
+ menuLoader: menuLoaderRef.v
910
+ };
911
+ if (found.paramName) params[found.paramName] = found.paramValue;
912
+ routeParts.push(found.routePart);
913
+ done = found.done;
914
+ node = found.next;
915
+ const matchedPathname = `/${parts.slice(0, found.done ? len : i + 1).join("/")}/`;
916
+ collectNodeMeta(node, found.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, matchedPathname);
917
+ if (node._M) groupNodes.push({
918
+ node,
919
+ depth: layouts.length
920
+ });
921
+ }
922
+ if (matched && !done && i === len) {
923
+ if (!node._I && node._G == null) {
924
+ const indexResult = findIndexNode(node);
925
+ if (indexResult) {
926
+ collectNodeMeta(indexResult.target, indexResult.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, pathname);
927
+ node = indexResult.target;
928
+ }
929
+ }
930
+ if (!node._I && node._G == null) {
931
+ const restInfo = findRestNode(node);
932
+ if (restInfo) {
933
+ const next = restInfo.next;
934
+ const paramName = next._P;
935
+ params[paramName] = "";
936
+ routeParts.push(`[...${paramName}]`);
937
+ collectNodeMeta(next, restInfo.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, pathname);
938
+ node = next;
939
+ }
940
+ }
941
+ }
942
+ const loaders = matched && (done || i >= len) ? resolveLoaders(root, node, layouts) : void 0;
943
+ if (!loaders && restFallback) {
944
+ const fb = restFallback;
945
+ const fbParams = {
946
+ ...fb.params,
947
+ [fb.paramName]: fb.restValue
948
+ };
949
+ const fbRouteParts = [...fb.routeParts, `[...${fb.paramName}]`];
950
+ const fbLayouts = [...fb.layouts];
951
+ const fbLoaderPathsByHash = { ...fb.loaderPathsByHash };
952
+ const fbErrorRef = {
953
+ v: fb.errorLoader,
954
+ layouts: fb.errorLayouts
955
+ };
956
+ const fbNotFoundRef = {
957
+ v: fb.notFoundLoader,
958
+ layouts: fb.notFoundLayouts
959
+ };
960
+ const fbMenuRef = { v: fb.menuLoader };
961
+ const fbLoaderHashes = [];
962
+ collectNodeMeta(fb.aNode, fb.groups, fbLayouts, fbErrorRef, fbNotFoundRef, fbMenuRef, fbLoaderHashes, fbLoaderPathsByHash, pathname);
963
+ const fbLoaders = resolveLoaders(root, fb.aNode, fbLayouts);
964
+ if (fbLoaders) return {
965
+ loaders: fbLoaders,
966
+ params: fbParams,
967
+ routeParts: fbRouteParts,
968
+ notFound: false,
969
+ routeBundleNames: fb.aNode._B,
970
+ loaderHashes: fbLoaderHashes.length > 0 ? fbLoaderHashes : void 0,
971
+ loaderPathsByHash: Object.keys(fbLoaderPathsByHash).length > 0 ? fbLoaderPathsByHash : void 0,
972
+ menuLoader: fbMenuRef.v,
973
+ errorLoader: boundaryChain(fbErrorRef)
974
+ };
975
+ errorLoaderRef.v = fbErrorRef.v;
976
+ errorLoaderRef.layouts = fbErrorRef.layouts;
977
+ notFoundLoaderRef.v = fbNotFoundRef.v;
978
+ notFoundLoaderRef.layouts = fbNotFoundRef.layouts;
979
+ menuLoaderRef.v = fbMenuRef.v;
980
+ }
981
+ if (!loaders) {
982
+ const recover = (kind) => {
983
+ for (let k = groupNodes.length - 1; k >= 0; k--) {
984
+ const found = findGroupBoundary(groupNodes[k].node, layouts.slice(0, groupNodes[k].depth), kind);
985
+ if (found) return found;
986
+ }
987
+ };
988
+ if (!notFoundLoaderRef.v) {
989
+ const g = recover("_4");
990
+ if (g) {
991
+ notFoundLoaderRef.v = g.v;
992
+ notFoundLoaderRef.layouts = g.layouts;
993
+ }
994
+ }
995
+ if (!errorLoaderRef.v) {
996
+ const g = recover("_E");
997
+ if (g) {
998
+ errorLoaderRef.v = g.v;
999
+ errorLoaderRef.layouts = g.layouts;
1000
+ }
1001
+ }
1002
+ const ref = notFoundLoaderRef.v ? notFoundLoaderRef : errorLoaderRef.v ? errorLoaderRef : null;
1003
+ const boundary = ref?.v ?? httpErrorLoader;
1004
+ const boundaryLayouts = ref ? ref.layouts : layouts;
1005
+ return {
1006
+ loaders: Array.isArray(boundary) ? boundary : [...boundaryLayouts, boundary],
1007
+ params,
1008
+ routeParts,
1009
+ notFound: true,
1010
+ routeBundleNames: void 0,
1011
+ loaderHashes: void 0,
1012
+ loaderPathsByHash: void 0,
1013
+ menuLoader: menuLoaderRef.v,
1014
+ errorLoader: boundaryChain(errorLoaderRef)
1015
+ };
1016
+ }
1017
+ if (node._R) {
1018
+ loaderHashes.push(...node._R);
1019
+ const matchedPathname = ensureSlash(pathname);
1020
+ for (let i = 0; i < node._R.length; i++) {
1021
+ const hash = node._R[i];
1022
+ loaderPathsByHash[hash] = matchedPathname;
1023
+ }
1024
+ }
1025
+ return {
1026
+ loaders,
1027
+ params,
1028
+ routeParts,
1029
+ notFound: false,
1030
+ routeBundleNames: node._B,
1031
+ loaderHashes: loaderHashes.length > 0 ? loaderHashes : void 0,
1032
+ loaderPathsByHash: Object.keys(loaderPathsByHash).length > 0 ? loaderPathsByHash : void 0,
1033
+ menuLoader: menuLoaderRef.v,
1034
+ errorLoader: boundaryChain(errorLoaderRef)
1035
+ };
1036
+ }
1037
+ var loadModule = (moduleLoader, pendingLoads, moduleSetter, cacheModules) => {
1038
+ if (typeof moduleLoader === "function") {
1039
+ const loadedModule = MODULE_CACHE.get(moduleLoader);
1040
+ if (loadedModule) moduleSetter(loadedModule);
1041
+ else {
1042
+ const moduleOrPromise = moduleLoader();
1043
+ if (typeof moduleOrPromise.then === "function") pendingLoads.push(moduleOrPromise.then((loadedModule) => {
1044
+ if (cacheModules !== false) MODULE_CACHE.set(moduleLoader, loadedModule);
1045
+ moduleSetter(loadedModule);
1046
+ }));
1047
+ else if (moduleOrPromise) moduleSetter(moduleOrPromise);
1048
+ }
1049
+ }
1208
1050
  };
1209
- const routeLoaderQrl = (loaderQrl, ...rest) => {
1210
- const { id, validators, serializationStrategy, cacheControl, eTag, cacheKey, search, blockSSR } = getLoaderOptions(rest);
1211
- function loader() {
1212
- const state = _resolveContextWithoutSequentialScope(RouteStateContext);
1213
- let signal = state[loader.__id];
1214
- if (!signal) {
1215
- const routeLoaderCtx = _resolveContextWithoutSequentialScope(RouteLoaderCtxContext);
1216
- signal = ensureRouteLoaderSignal(loader, state, routeLoaderCtx);
1217
- }
1218
- void signal.promise();
1219
- return signal;
1220
- }
1221
- loader.__brand = "server_loader";
1222
- loader.__qrl = loaderQrl;
1223
- loader.__validators = validators;
1224
- loader.__id = id ?? loaderQrl.getHash();
1225
- loader.__serializationStrategy = serializationStrategy;
1226
- loader.__cacheControl = cacheControl;
1227
- loader.__eTag = eTag;
1228
- loader.__cacheKey = cacheKey;
1229
- loader.__search = search;
1230
- loader.__blockSSR = blockSSR;
1231
- Object.freeze(loader);
1232
- return loader;
1051
+ var loadRoute = async (routes, cacheModules, pathname) => {
1052
+ const { loaders, params, routeParts, notFound, routeBundleNames, loaderHashes, loaderPathsByHash, menuLoader, errorLoader } = matchRouteTree(routes, pathname);
1053
+ const routeName = "/" + routeParts.join("/");
1054
+ const modules = new Array(loaders.length);
1055
+ const pendingLoads = [];
1056
+ for (let i = 0; i < loaders.length; i++) {
1057
+ const moduleLoader = loaders[i];
1058
+ loadModule(moduleLoader, pendingLoads, (routeModule) => modules[i] = routeModule, cacheModules);
1059
+ }
1060
+ let menu = void 0;
1061
+ loadModule(menuLoader, pendingLoads, (menuModule) => menu = menuModule?.default, cacheModules);
1062
+ if (pendingLoads.length > 0) await Promise.all(pendingLoads);
1063
+ return {
1064
+ $routeName$: routeName,
1065
+ $params$: params,
1066
+ $mods$: modules,
1067
+ $menu$: deepFreeze(menu),
1068
+ $routeBundleNames$: routeBundleNames,
1069
+ $notFound$: notFound,
1070
+ $errorLoader$: errorLoader,
1071
+ $loaders$: loaderHashes,
1072
+ $loaderPaths$: loaderPathsByHash
1073
+ };
1233
1074
  };
1234
- const routeLoader$ = /* @__PURE__ */ implicit$FirstArg(routeLoaderQrl);
1235
-
1236
- const toPath = (url) => url.pathname + url.search + url.hash;
1237
- const toUrl = (url, baseUrl) => new URL(url, baseUrl.href);
1238
- const isSameOrigin = (a, b) => a.origin === b.origin;
1239
- const isSamePathname = ({ pathname: a }, { pathname: b }) => {
1240
- const lDiff = Math.abs(a.length - b.length);
1241
- return lDiff === 0 ? a === b : lDiff === 1 && ensureSlash(a) === ensureSlash(b);
1075
+ /** Gets an absolute url path string (url.pathname + url.search + url.hash) */ var toPath = (url) => url.pathname + url.search + url.hash;
1076
+ /** Create a URL from a string and baseUrl */ var toUrl = (url, baseUrl) => new URL(url, baseUrl.href);
1077
+ /** Checks only if the origins are the same. */ var isSameOrigin = (a, b) => a.origin === b.origin;
1078
+ /** Checks only if the pathnames are the same for the URLs (doesn't include search and hash) */ var isSamePathname = ({ pathname: a }, { pathname: b }) => {
1079
+ const lDiff = Math.abs(a.length - b.length);
1080
+ return lDiff === 0 ? a === b : lDiff === 1 && ensureSlash(a) === ensureSlash(b);
1242
1081
  };
1243
- const isSameSearchQuery = (a, b) => a.search === b.search;
1244
- const isSamePath = (a, b) => isSameSearchQuery(a, b) && isSamePathname(a, b);
1245
- const getClientNavPath = (props, baseUrl) => {
1246
- const href = props.href;
1247
- if (typeof href === "string" && typeof props.target !== "string" && !props.reload) {
1248
- try {
1249
- const linkUrl = toUrl(href.trim(), baseUrl.url);
1250
- const currentUrl = toUrl("", baseUrl.url);
1251
- if (isSameOrigin(linkUrl, currentUrl)) {
1252
- return toPath(linkUrl);
1253
- }
1254
- } catch (e) {
1255
- console.error(e);
1256
- }
1257
- } else if (props.reload) {
1258
- return toPath(toUrl("", baseUrl.url));
1259
- }
1260
- return null;
1082
+ /** Checks only if the search query strings are the same for the URLs */ var isSameSearchQuery = (a, b) => a.search === b.search;
1083
+ /** Checks only if the pathname + search are the same for the URLs. */ var isSamePath = (a, b) => isSameSearchQuery(a, b) && isSamePathname(a, b);
1084
+ var getClientNavPath = (props, baseUrl) => {
1085
+ const href = props.href;
1086
+ if (typeof href === "string" && typeof props.target !== "string" && !props.reload) try {
1087
+ const linkUrl = toUrl(href.trim(), baseUrl.url);
1088
+ if (isSameOrigin(linkUrl, toUrl("", baseUrl.url))) return toPath(linkUrl);
1089
+ } catch (e) {
1090
+ console.error(e);
1091
+ }
1092
+ else if (props.reload) return toPath(toUrl("", baseUrl.url));
1093
+ return null;
1261
1094
  };
1262
- const shouldPreload = (clientNavPath, currentLoc) => {
1263
- if (clientNavPath) {
1264
- const prefetchUrl = toUrl(clientNavPath, currentLoc.url);
1265
- const currentUrl = toUrl("", currentLoc.url);
1266
- return !isSamePathname(prefetchUrl, currentUrl);
1267
- }
1268
- return false;
1095
+ var shouldPreload = (clientNavPath, currentLoc) => {
1096
+ if (clientNavPath) return !isSamePathname(toUrl(clientNavPath, currentLoc.url), toUrl("", currentLoc.url));
1097
+ return false;
1269
1098
  };
1270
- const isPromise = (value) => {
1271
- return value && typeof value.then === "function";
1099
+ var isPromise = (value) => {
1100
+ return value && typeof value.then === "function";
1272
1101
  };
1273
-
1274
- const mergeArray = (existingArr, newArr) => {
1275
- if (Array.isArray(newArr)) {
1276
- for (const newItem of newArr) {
1277
- if (typeof newItem.key === "string") {
1278
- const existingIndex = existingArr.findIndex((i) => i.key === newItem.key);
1279
- if (existingIndex > -1) {
1280
- existingArr[existingIndex] = newItem;
1281
- continue;
1282
- }
1283
- }
1284
- existingArr.push(newItem);
1285
- }
1286
- }
1102
+ var mergeArray = (existingArr, newArr) => {
1103
+ if (Array.isArray(newArr)) for (const newItem of newArr) {
1104
+ if (typeof newItem.key === "string") {
1105
+ const existingIndex = existingArr.findIndex((i) => i.key === newItem.key);
1106
+ if (existingIndex > -1) {
1107
+ existingArr[existingIndex] = newItem;
1108
+ continue;
1109
+ }
1110
+ }
1111
+ existingArr.push(newItem);
1112
+ }
1287
1113
  };
1288
- const resolveDocumentHead = (resolvedHead, updatedHead) => {
1289
- if (typeof updatedHead.title === "string") {
1290
- resolvedHead.title = updatedHead.title;
1291
- }
1292
- mergeArray(resolvedHead.meta, updatedHead.meta);
1293
- mergeArray(resolvedHead.links, updatedHead.links);
1294
- mergeArray(resolvedHead.styles, updatedHead.styles);
1295
- mergeArray(resolvedHead.scripts, updatedHead.scripts);
1296
- Object.assign(resolvedHead.frontmatter, updatedHead.frontmatter);
1114
+ var resolveDocumentHead = (resolvedHead, updatedHead) => {
1115
+ if (typeof updatedHead.title === "string") resolvedHead.title = updatedHead.title;
1116
+ mergeArray(resolvedHead.meta, updatedHead.meta);
1117
+ mergeArray(resolvedHead.links, updatedHead.links);
1118
+ mergeArray(resolvedHead.styles, updatedHead.styles);
1119
+ mergeArray(resolvedHead.scripts, updatedHead.scripts);
1120
+ Object.assign(resolvedHead.frontmatter, updatedHead.frontmatter);
1297
1121
  };
1298
- const createDocumentHead = (defaults, manifestHash) => ({
1299
- title: defaults?.title || "",
1300
- meta: [
1301
- ...defaults?.meta || []
1302
- ],
1303
- links: [
1304
- ...defaults?.links || []
1305
- ],
1306
- styles: [
1307
- ...defaults?.styles || []
1308
- ],
1309
- scripts: [
1310
- ...defaults?.scripts || []
1311
- ],
1312
- frontmatter: {
1313
- ...defaults?.frontmatter
1314
- },
1315
- manifestHash: manifestHash || "dev"
1122
+ var createDocumentHead = (defaults, manifestHash) => ({
1123
+ title: defaults?.title || "",
1124
+ meta: [...defaults?.meta || []],
1125
+ links: [...defaults?.links || []],
1126
+ styles: [...defaults?.styles || []],
1127
+ scripts: [...defaults?.scripts || []],
1128
+ frontmatter: { ...defaults?.frontmatter },
1129
+ manifestHash: manifestHash || "dev"
1316
1130
  });
1317
- const resolveRouteConfig = (resolveValue, routeLocation, contentModules, locale, status, defaults) => withLocale(locale, () => {
1318
- const head = createDocumentHead(defaults);
1319
- let eTag;
1320
- let cacheKey;
1321
- const fns = [];
1322
- for (let i = 0; i < contentModules.length; i++) {
1323
- const contentModule = contentModules[i];
1324
- if (!contentModule) {
1325
- continue;
1326
- }
1327
- const isLast = i === contentModules.length - 1;
1328
- let config;
1329
- if (contentModule.routeConfig) {
1330
- config = contentModule.routeConfig;
1331
- } else if (contentModule.head) {
1332
- const synthetic = {
1333
- head: void 0
1334
- };
1335
- const headExport = contentModule.head;
1336
- if (typeof headExport === "function") {
1337
- const mod = contentModule;
1338
- config = (props) => ({
1339
- head: headExport(props),
1340
- eTag: mod.eTag,
1341
- cacheKey: mod.cacheKey
1342
- });
1343
- } else {
1344
- synthetic.head = headExport;
1345
- const mod = contentModule;
1346
- if (mod.eTag !== void 0) {
1347
- synthetic.eTag = mod.eTag;
1348
- }
1349
- if (mod.cacheKey !== void 0) {
1350
- synthetic.cacheKey = mod.cacheKey;
1351
- }
1352
- config = synthetic;
1353
- }
1354
- } else {
1355
- const mod = contentModule;
1356
- if (mod.eTag !== void 0 || mod.cacheKey !== void 0) {
1357
- config = {
1358
- eTag: mod.eTag,
1359
- cacheKey: mod.cacheKey
1360
- };
1361
- }
1362
- }
1363
- if (!config) {
1364
- continue;
1365
- }
1366
- if (typeof config === "function") {
1367
- fns.unshift({
1368
- fn: config,
1369
- isLast
1370
- });
1371
- } else {
1372
- if (config.head) {
1373
- resolveDocumentHead(head, config.head);
1374
- }
1375
- if (config.eTag !== void 0) {
1376
- eTag = config.eTag;
1377
- }
1378
- if (config.cacheKey !== void 0) {
1379
- cacheKey = config.cacheKey;
1380
- }
1381
- }
1382
- }
1383
- if (fns.length) {
1384
- const headProps = {
1385
- head,
1386
- status,
1387
- withLocale: (fn) => fn(),
1388
- resolveValue,
1389
- ...routeLocation
1390
- };
1391
- for (const { fn } of fns) {
1392
- const result = fn(headProps);
1393
- if (result.head) {
1394
- resolveDocumentHead(head, result.head);
1395
- }
1396
- if (result.eTag !== void 0) {
1397
- eTag = result.eTag;
1398
- }
1399
- if (result.cacheKey !== void 0) {
1400
- cacheKey = result.cacheKey;
1401
- }
1402
- }
1403
- }
1404
- return {
1405
- head,
1406
- eTag,
1407
- cacheKey
1408
- };
1131
+ /**
1132
+ * Resolve the full route config (head + eTag + cacheKey) from all content modules. Uses the same
1133
+ * resolution rules as DocumentHead: objects are merged immediately, functions are queued and
1134
+ * executed inner-before-outer.
1135
+ */ var resolveRouteConfig = (resolveValue, routeLocation, contentModules, locale, status, defaults) => withLocale(locale, () => {
1136
+ const head = createDocumentHead(defaults);
1137
+ let eTag;
1138
+ let cacheKey;
1139
+ const fns = [];
1140
+ for (let i = 0; i < contentModules.length; i++) {
1141
+ const contentModule = contentModules[i];
1142
+ if (!contentModule) continue;
1143
+ const isLast = i === contentModules.length - 1;
1144
+ let config;
1145
+ if (contentModule.routeConfig) config = contentModule.routeConfig;
1146
+ else if (contentModule.head) {
1147
+ const synthetic = { head: void 0 };
1148
+ const headExport = contentModule.head;
1149
+ if (typeof headExport === "function") {
1150
+ const mod = contentModule;
1151
+ config = (props) => ({
1152
+ head: headExport(props),
1153
+ eTag: mod.eTag,
1154
+ cacheKey: mod.cacheKey
1155
+ });
1156
+ } else {
1157
+ synthetic.head = headExport;
1158
+ const mod = contentModule;
1159
+ if (mod.eTag !== void 0) synthetic.eTag = mod.eTag;
1160
+ if (mod.cacheKey !== void 0) synthetic.cacheKey = mod.cacheKey;
1161
+ config = synthetic;
1162
+ }
1163
+ } else {
1164
+ const mod = contentModule;
1165
+ if (mod.eTag !== void 0 || mod.cacheKey !== void 0) config = {
1166
+ eTag: mod.eTag,
1167
+ cacheKey: mod.cacheKey
1168
+ };
1169
+ }
1170
+ if (!config) continue;
1171
+ if (typeof config === "function") fns.unshift({
1172
+ fn: config,
1173
+ isLast
1174
+ });
1175
+ else {
1176
+ if (config.head) resolveDocumentHead(head, config.head);
1177
+ if (config.eTag !== void 0) eTag = config.eTag;
1178
+ if (config.cacheKey !== void 0) cacheKey = config.cacheKey;
1179
+ }
1180
+ }
1181
+ if (fns.length) {
1182
+ const headProps = {
1183
+ head,
1184
+ status,
1185
+ withLocale: (fn) => fn(),
1186
+ resolveValue,
1187
+ ...routeLocation
1188
+ };
1189
+ for (const { fn } of fns) {
1190
+ const result = fn(headProps);
1191
+ if (result.head) resolveDocumentHead(head, result.head);
1192
+ if (result.eTag !== void 0) eTag = result.eTag;
1193
+ if (result.cacheKey !== void 0) cacheKey = result.cacheKey;
1194
+ }
1195
+ }
1196
+ return {
1197
+ head,
1198
+ eTag,
1199
+ cacheKey
1200
+ };
1409
1201
  });
1410
- const resolveHead = (actionData, loaderState, routeLocation, contentModules, locale, defaults) => {
1411
- const getData = (loaderOrAction) => {
1412
- const id = loaderOrAction.__id;
1413
- const signal = loaderState?.[id];
1414
- if (signal) {
1415
- return signal.value;
1416
- }
1417
- if (actionData?.action === id) {
1418
- return actionData.actionResult;
1419
- }
1420
- return void 0;
1421
- };
1422
- return resolveRouteConfig(getData, routeLocation, contentModules, locale, actionData?.status ?? 200, defaults).head;
1202
+ /**
1203
+ * Resolve only the document head from all content modules. This is the browser-side entry point
1204
+ * that ignores eTag/cacheKey (server-only concerns).
1205
+ *
1206
+ * Signal values may throw a promise on the client when still loading — callers should wrap this in
1207
+ * `retryOnPromise` to handle that.
1208
+ */ var resolveHead = (actionData, loaderState, routeLocation, contentModules, locale, defaults) => {
1209
+ const getData = (loaderOrAction) => {
1210
+ const id = loaderOrAction.__id;
1211
+ const signal = loaderState?.[id];
1212
+ if (signal) return signal.value;
1213
+ if (actionData?.action === id) return actionData.actionResult;
1214
+ };
1215
+ return resolveRouteConfig(getData, routeLocation, contentModules, locale, actionData?.status ?? 200, defaults).head;
1423
1216
  };
1424
-
1425
- export { QFN_KEY as A, QDATA_KEY as B, ContentContext as C, DocumentHeadContext as D, routeLoader$ as E, FULLPATH_HEADER as F, routeLoaderQrl as G, HttpStatusContext as H, getRouteLoaders as I, getRouteLoaderValues as J, loadRouteLoader as K, isPromise as L, getLoaderRequestEvent as M, resolveRouteLoaderByHash as N, getRouteLoaderResponse as O, getRouteLoaderCtx as P, QACTION_KEY as Q, ROUTE_PATH_HEADER as R, setRouteLoaders as S, resolveRouteConfig as T, clearRouteLoaderData as U, matchesRouteLoaderId as V, _asyncRequestStore as _, getClientNavPath as a, isSameOrigin as b, setLoaderSignalValue as c, createDocumentHead as d, ensureRouteLoaderSignals as e, fetchRouteLoaderData as f, getModuleRouteLoaders as g, ContentInternalContext as h, isSamePath as i, RouteLocationContext as j, RouteNavigateContext as k, loadRoute as l, RouteStateContext as m, RouteLoaderCtxContext as n, RouteActionContext as o, RoutePreventNavigateContext as p, invalidateNavRouteLoaders as q, isImmutableLoader as r, shouldPreload as s, toPath as t, updateRouteLoaderPaths as u, clearNavFetchCache as v, resolveHead as w, Q_ROUTE as x, toUrl as y, getRequestEvent as z };
1217
+ 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 };