@qwik.dev/router 2.0.0-beta.35 → 2.0.0-beta.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,50 @@
1
+ import { e as ensureSlash } from './pathname.mjs';
2
+ import { isDev, _deserialize, _verifySerializable, _serialize, _getAsyncLocalStorage } from '@qwik.dev/core/internal';
1
3
  import { parentPort } from 'node:worker_threads';
2
- import { isDev, _UNINITIALIZED, _deserialize, _verifySerializable, _serialize, _getAsyncLocalStorage } from '@qwik.dev/core/internal';
3
- import { withLocale, inlinedQrl } from '@qwik.dev/core';
4
- import { A as AbortMessage$1 } from './redirect-handler.mjs';
4
+ import { v as deepFreeze, o as getRouteLoaderValues, p as loadRouteLoader, n as getRouteLoaderCtx, q as setRouteLoaders, A as AbortMessage$1, w as getRouteLoaders } from './deepFreeze.qwik.mjs';
5
+ import { Q as QLoaderId$1, I as IsQLoader$1 } from './request-path.mjs';
6
+ import { withLocale, isDev as isDev$1, inlinedQrl } from '@qwik.dev/core';
7
+ import { j as jsonRequestWrapper, l as loaderHandler, f as actionHandler, h as hash, n as normalizeETag, p as performETagMatch, s as setETagHeader } from './action-handler.mjs';
5
8
  import { isServer } from '@qwik.dev/core/build';
6
- import { d as deepFreeze } from './deepFreeze.qwik.mjs';
9
+
10
+ const IsQLoader = "@isQLoader";
11
+ const IsQAction = "@isQAction";
12
+ const QLoaderId = "@loaderId";
13
+ const QActionId = "@actionId";
14
+ const LOADER_REGEX = /\/(q-loader-([^.]+)\.([^.]+)\.json)$/;
15
+ function recognizeRequest(pathname) {
16
+ if (pathname.length < 10) {
17
+ return null;
18
+ }
19
+ const loaderMatch = pathname.match(LOADER_REGEX);
20
+ if (loaderMatch) {
21
+ return {
22
+ type: IsQLoader,
23
+ trimLength: loaderMatch[1].length + 1,
24
+ data: {
25
+ loaderId: loaderMatch[2],
26
+ manifestHash: loaderMatch[3]
27
+ }
28
+ };
29
+ }
30
+ return null;
31
+ }
32
+ function trimRecognizedInternalPathname(pathname, recognized) {
33
+ let trimmed = pathname.slice(0, pathname.length - recognized.trimLength) || "/";
34
+ if (!globalThis.__NO_TRAILING_SLASH__ && !trimmed.endsWith("/")) {
35
+ trimmed = ensureSlash(trimmed);
36
+ }
37
+ return trimmed;
38
+ }
39
+ function trimInternalPathname(pathname) {
40
+ const recognized = recognizeRequest(pathname);
41
+ if (recognized) {
42
+ return trimRecognizedInternalPathname(pathname, recognized);
43
+ }
44
+ return pathname;
45
+ }
7
46
 
8
47
  const QACTION_KEY = "qaction";
9
- const QLOADER_KEY = "qloaders";
10
48
  const QFN_KEY = "qfunc";
11
49
  const QDATA_KEY = "qdata";
12
50
  const Q_ROUTE = "q:route";
@@ -156,10 +194,12 @@ function walkTrieKeys(root, keys) {
156
194
  if (node._L) {
157
195
  layouts.push(node._L);
158
196
  }
159
- for (const key of keys) {
197
+ for (let i = 0; i < keys.length; i++) {
198
+ const key = keys[i];
160
199
  let next = node[key];
161
200
  if (!next && node._M) {
162
- for (const group of node._M) {
201
+ for (let j = 0; j < node._M.length; j++) {
202
+ const group = node._M[j];
163
203
  next = group[key];
164
204
  if (next) {
165
205
  if (group._L) {
@@ -182,11 +222,21 @@ function walkTrieKeys(root, keys) {
182
222
  layouts
183
223
  };
184
224
  }
185
- function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef) {
186
- for (const g of groups) {
225
+ function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, matchedPathname = "/") {
226
+ for (let j = 0; j < groups.length; j++) {
227
+ const g = groups[j];
187
228
  if (g._L) {
188
229
  layouts.push(g._L);
189
230
  }
231
+ if (g._R && loaderHashes) {
232
+ loaderHashes.push(...g._R);
233
+ if (loaderPathsByHash) {
234
+ for (let i = 0; i < g._R.length; i++) {
235
+ const hash = g._R[i];
236
+ loaderPathsByHash[hash] = matchedPathname;
237
+ }
238
+ }
239
+ }
190
240
  if (g._E) {
191
241
  errorLoaderRef.v = g._E;
192
242
  }
@@ -200,6 +250,15 @@ function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRe
200
250
  if (node._L) {
201
251
  layouts.push(node._L);
202
252
  }
253
+ if (node._R && loaderHashes) {
254
+ loaderHashes.push(...node._R);
255
+ if (loaderPathsByHash) {
256
+ for (let i = 0; i < node._R.length; i++) {
257
+ const hash = node._R[i];
258
+ loaderPathsByHash[hash] = matchedPathname;
259
+ }
260
+ }
261
+ }
203
262
  if (node._E) {
204
263
  errorLoaderRef.v = node._E;
205
264
  }
@@ -210,7 +269,7 @@ function collectNodeMeta(node, groups, layouts, errorLoaderRef, notFoundLoaderRe
210
269
  menuLoaderRef.v = node._N;
211
270
  }
212
271
  }
213
- function tryWildcardMatch(node, part, partLower, parts, partIndex, params) {
272
+ function tryWildcardMatch(node, part, partLower, parts, partIndex) {
214
273
  let next = node._W;
215
274
  if (next) {
216
275
  const prefix = next._0;
@@ -221,20 +280,24 @@ function tryWildcardMatch(node, part, partLower, parts, partIndex, params) {
221
280
  if (partLower.length > pre.length + suf.length && (!pre || partLower.startsWith(pre.toLowerCase())) && (!suf || partLower.endsWith(suf.toLowerCase()))) {
222
281
  const paramName = next._P;
223
282
  const value = part.slice(pre.length, suf ? part.length - suf.length : void 0);
224
- params[paramName] = value;
225
283
  return {
226
284
  next,
227
285
  routePart: `${pre}[${paramName}]${suf}`,
228
- done: false
286
+ done: false,
287
+ kind: 1,
288
+ paramName,
289
+ paramValue: value
229
290
  };
230
291
  }
231
292
  } else {
232
293
  const paramName = next._P;
233
- params[paramName] = part;
234
294
  return {
235
295
  next,
236
296
  routePart: `[${paramName}]`,
237
- done: false
297
+ done: false,
298
+ kind: 1,
299
+ paramName,
300
+ paramValue: part
238
301
  };
239
302
  }
240
303
  }
@@ -242,35 +305,39 @@ function tryWildcardMatch(node, part, partLower, parts, partIndex, params) {
242
305
  if (next) {
243
306
  const paramName = next._P;
244
307
  const restValue = parts.slice(partIndex).join("/");
245
- params[paramName] = restValue;
246
308
  return {
247
309
  next,
248
310
  routePart: `[...${paramName}]`,
249
- done: true
311
+ done: true,
312
+ kind: 2,
313
+ paramName,
314
+ paramValue: restValue
250
315
  };
251
316
  }
252
317
  return void 0;
253
318
  }
254
- function findChild(node, part, partLower, parts, partIndex, params) {
319
+ function findChild(node, part, partLower, parts, partIndex) {
255
320
  const exact = node[partLower];
256
321
  if (exact) {
257
322
  return {
258
323
  next: exact,
259
324
  groups: [],
260
325
  routePart: part,
261
- done: false
326
+ done: false,
327
+ kind: 0
262
328
  };
263
329
  }
264
330
  if (node._M) {
265
- for (const group of node._M) {
266
- const groupResult = findChild(group, part, partLower, parts, partIndex, params);
331
+ for (let j = 0; j < node._M.length; j++) {
332
+ const group = node._M[j];
333
+ const groupResult = findChild(group, part, partLower, parts, partIndex);
267
334
  if (groupResult) {
268
335
  groupResult.groups.unshift(group);
269
336
  return groupResult;
270
337
  }
271
338
  }
272
339
  }
273
- const wildcard = tryWildcardMatch(node, part, partLower, parts, partIndex, params);
340
+ const wildcard = tryWildcardMatch(node, part, partLower, parts, partIndex);
274
341
  if (wildcard) {
275
342
  return {
276
343
  ...wildcard,
@@ -287,7 +354,8 @@ function findIndexNode(node) {
287
354
  };
288
355
  }
289
356
  if (node._M) {
290
- for (const group of node._M) {
357
+ for (let j = 0; j < node._M.length; j++) {
358
+ const group = node._M[j];
291
359
  const result = findIndexNode(group);
292
360
  if (result) {
293
361
  if (result.target !== group) {
@@ -311,7 +379,8 @@ function resolveLoaders(root, node, gatheredLayouts) {
311
379
  if (!targetNode._I && targetNode._G == null) {
312
380
  const indexResult = findIndexNode(targetNode);
313
381
  if (indexResult) {
314
- for (const g of indexResult.groups) {
382
+ for (let j = 0; j < indexResult.groups.length; j++) {
383
+ const g = indexResult.groups[j];
315
384
  if (g._L) {
316
385
  targetLayouts.push(g._L);
317
386
  }
@@ -344,7 +413,8 @@ function findRestNode(node) {
344
413
  };
345
414
  }
346
415
  if (node._M) {
347
- for (const group of node._M) {
416
+ for (let j = 0; j < node._M.length; j++) {
417
+ const group = node._M[j];
348
418
  const result = findRestNode(group);
349
419
  if (result) {
350
420
  result.groups.unshift(group);
@@ -359,6 +429,8 @@ function matchRouteTree(root = {}, pathname) {
359
429
  const params = {};
360
430
  const routeParts = [];
361
431
  const layouts = [];
432
+ const loaderHashes = [];
433
+ const loaderPathsByHash = {};
362
434
  const errorLoaderRef = {
363
435
  v: void 0
364
436
  };
@@ -371,6 +443,13 @@ function matchRouteTree(root = {}, pathname) {
371
443
  if (root._L) {
372
444
  layouts.push(root._L);
373
445
  }
446
+ if (root._R) {
447
+ loaderHashes.push(...root._R);
448
+ for (let i2 = 0; i2 < root._R.length; i2++) {
449
+ const hash = root._R[i2];
450
+ loaderPathsByHash[hash] = "/";
451
+ }
452
+ }
374
453
  if (root._E) {
375
454
  errorLoaderRef.v = root._E;
376
455
  }
@@ -389,7 +468,12 @@ function matchRouteTree(root = {}, pathname) {
389
468
  for (; !done && i < len; i++) {
390
469
  const part = parts[i];
391
470
  const partLower = part.toLowerCase();
392
- const restInfo = findRestNode(node);
471
+ const found = findChild(node, part, partLower, parts, i);
472
+ if (!found) {
473
+ matched = false;
474
+ break;
475
+ }
476
+ const restInfo = found.kind === 1 ? findRestNode(node) : void 0;
393
477
  if (restInfo) {
394
478
  restFallback = {
395
479
  aNode: restInfo.next,
@@ -405,26 +489,28 @@ function matchRouteTree(root = {}, pathname) {
405
489
  layouts: [
406
490
  ...layouts
407
491
  ],
492
+ loaderPathsByHash: {
493
+ ...loaderPathsByHash
494
+ },
408
495
  errorLoader: errorLoaderRef.v,
409
496
  notFoundLoader: notFoundLoaderRef.v,
410
497
  menuLoader: menuLoaderRef.v
411
498
  };
412
499
  }
413
- const found = findChild(node, part, partLower, parts, i, params);
414
- if (!found) {
415
- matched = false;
416
- break;
500
+ if (found.paramName) {
501
+ params[found.paramName] = found.paramValue;
417
502
  }
418
503
  routeParts.push(found.routePart);
419
504
  done = found.done;
420
505
  node = found.next;
421
- collectNodeMeta(node, found.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef);
506
+ const matchedPathname = `/${parts.slice(0, found.done ? len : i + 1).join("/")}/`;
507
+ collectNodeMeta(node, found.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, matchedPathname);
422
508
  }
423
509
  if (matched && !done && i === len) {
424
510
  if (!node._I && node._G == null) {
425
511
  const indexResult = findIndexNode(node);
426
512
  if (indexResult) {
427
- collectNodeMeta(indexResult.target, indexResult.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef);
513
+ collectNodeMeta(indexResult.target, indexResult.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, pathname);
428
514
  node = indexResult.target;
429
515
  }
430
516
  }
@@ -448,7 +534,7 @@ function matchRouteTree(root = {}, pathname) {
448
534
  const paramName = next._P;
449
535
  params[paramName] = "";
450
536
  routeParts.push(`[...${paramName}]`);
451
- collectNodeMeta(next, restInfo.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef);
537
+ collectNodeMeta(next, restInfo.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef, loaderHashes, loaderPathsByHash, pathname);
452
538
  node = next;
453
539
  }
454
540
  }
@@ -467,6 +553,9 @@ function matchRouteTree(root = {}, pathname) {
467
553
  const fbLayouts = [
468
554
  ...fb.layouts
469
555
  ];
556
+ const fbLoaderPathsByHash = {
557
+ ...fb.loaderPathsByHash
558
+ };
470
559
  const fbErrorRef = {
471
560
  v: fb.errorLoader
472
561
  };
@@ -476,7 +565,8 @@ function matchRouteTree(root = {}, pathname) {
476
565
  const fbMenuRef = {
477
566
  v: fb.menuLoader
478
567
  };
479
- collectNodeMeta(fb.aNode, fb.groups, fbLayouts, fbErrorRef, fbNotFoundRef, fbMenuRef);
568
+ const fbLoaderHashes = [];
569
+ collectNodeMeta(fb.aNode, fb.groups, fbLayouts, fbErrorRef, fbNotFoundRef, fbMenuRef, fbLoaderHashes, fbLoaderPathsByHash, pathname);
480
570
  const fbLoaders = resolveLoaders(root, fb.aNode, fbLayouts);
481
571
  if (fbLoaders) {
482
572
  return {
@@ -485,6 +575,8 @@ function matchRouteTree(root = {}, pathname) {
485
575
  routeParts: fbRouteParts,
486
576
  notFound: false,
487
577
  routeBundleNames: fb.aNode._B,
578
+ loaderHashes: fbLoaderHashes.length > 0 ? fbLoaderHashes : void 0,
579
+ loaderPathsByHash: Object.keys(fbLoaderPathsByHash).length > 0 ? fbLoaderPathsByHash : void 0,
488
580
  menuLoader: fbMenuRef.v,
489
581
  errorLoader: fbErrorRef.v,
490
582
  notFoundLoader: fbNotFoundRef.v
@@ -504,17 +596,29 @@ function matchRouteTree(root = {}, pathname) {
504
596
  routeParts,
505
597
  notFound: true,
506
598
  routeBundleNames: void 0,
599
+ loaderHashes: void 0,
600
+ loaderPathsByHash: void 0,
507
601
  menuLoader: menuLoaderRef.v,
508
602
  errorLoader: errorLoaderRef.v,
509
603
  notFoundLoader: notFoundLoaderRef.v
510
604
  };
511
605
  }
606
+ if (node._R) {
607
+ loaderHashes.push(...node._R);
608
+ const matchedPathname = ensureSlash(pathname);
609
+ for (let i2 = 0; i2 < node._R.length; i2++) {
610
+ const hash = node._R[i2];
611
+ loaderPathsByHash[hash] = matchedPathname;
612
+ }
613
+ }
512
614
  return {
513
615
  loaders,
514
616
  params,
515
617
  routeParts,
516
618
  notFound: false,
517
619
  routeBundleNames: node._B,
620
+ loaderHashes: loaderHashes.length > 0 ? loaderHashes : void 0,
621
+ loaderPathsByHash: Object.keys(loaderPathsByHash).length > 0 ? loaderPathsByHash : void 0,
518
622
  menuLoader: menuLoaderRef.v,
519
623
  errorLoader: errorLoaderRef.v,
520
624
  notFoundLoader: notFoundLoaderRef.v
@@ -540,19 +644,18 @@ const loadModule = (moduleLoader, pendingLoads, moduleSetter, cacheModules) => {
540
644
  }
541
645
  }
542
646
  };
543
- const loadRoute = async (routes, cacheModules, pathname, isInternal) => {
647
+ const loadRoute = async (routes, cacheModules, pathname) => {
544
648
  const result = matchRouteTree(routes, pathname);
545
- const { loaders, params, routeParts, notFound, routeBundleNames, menuLoader, errorLoader, notFoundLoader } = result;
649
+ const { loaders, params, routeParts, notFound, routeBundleNames, loaderHashes, loaderPathsByHash, menuLoader, errorLoader, notFoundLoader } = result;
546
650
  const routeName = "/" + routeParts.join("/");
547
651
  const modules = new Array(loaders.length);
548
652
  const pendingLoads = [];
549
- loaders.forEach((moduleLoader, i) => {
653
+ for (let i = 0; i < loaders.length; i++) {
654
+ const moduleLoader = loaders[i];
550
655
  loadModule(moduleLoader, pendingLoads, (routeModule) => modules[i] = routeModule, cacheModules);
551
- });
552
- let menu = void 0;
553
- if (!isInternal) {
554
- loadModule(menuLoader, pendingLoads, (menuModule) => menu = menuModule?.default, cacheModules);
555
656
  }
657
+ let menu = void 0;
658
+ loadModule(menuLoader, pendingLoads, (menuModule) => menu = menuModule?.default, cacheModules);
556
659
  if (notFound && notFoundLoader) {
557
660
  let notFoundMod;
558
661
  let errorMod;
@@ -572,7 +675,9 @@ const loadRoute = async (routes, cacheModules, pathname, isInternal) => {
572
675
  $menu$: deepFreeze(menu),
573
676
  $routeBundleNames$: routeBundleNames,
574
677
  $notFound$: notFound,
575
- $errorLoader$: errorLoader
678
+ $errorLoader$: errorLoader,
679
+ $loaders$: loaderHashes,
680
+ $loaderPaths$: loaderPathsByHash
576
681
  };
577
682
  }
578
683
  if (pendingLoads.length > 0) {
@@ -585,7 +690,9 @@ const loadRoute = async (routes, cacheModules, pathname, isInternal) => {
585
690
  $menu$: deepFreeze(menu),
586
691
  $routeBundleNames$: routeBundleNames,
587
692
  $notFound$: notFound,
588
- $errorLoader$: errorLoader
693
+ $errorLoader$: errorLoader,
694
+ $loaders$: loaderHashes,
695
+ $loaderPaths$: loaderPathsByHash
589
696
  };
590
697
  };
591
698
 
@@ -594,7 +701,38 @@ const isPromise = (value) => {
594
701
  };
595
702
 
596
703
  const MAX_CACHE_SIZE = typeof globalThis.__SSR_CACHE_SIZE__ === "number" ? globalThis.__SSR_CACHE_SIZE__ : 50;
597
- const ssrCache = /* @__PURE__ */ new Map();
704
+ function createLruCache() {
705
+ const cache = /* @__PURE__ */ new Map();
706
+ return {
707
+ get(key) {
708
+ const entry = cache.get(key);
709
+ if (entry !== void 0) {
710
+ cache.delete(key);
711
+ cache.set(key, entry);
712
+ }
713
+ return entry;
714
+ },
715
+ set(key, entry) {
716
+ if (MAX_CACHE_SIZE <= 0) {
717
+ return;
718
+ }
719
+ if (cache.size >= MAX_CACHE_SIZE) {
720
+ const firstKey = cache.keys().next().value;
721
+ cache.delete(firstKey);
722
+ }
723
+ cache.set(key, entry);
724
+ },
725
+ clear(key) {
726
+ if (key != null) {
727
+ cache.delete(key);
728
+ } else {
729
+ cache.clear();
730
+ }
731
+ }
732
+ };
733
+ }
734
+ const ssrCache = createLruCache();
735
+ const loaderCache = createLruCache();
598
736
  function resolveETag(eTagExport, headProps) {
599
737
  if (eTagExport === void 0) {
600
738
  return null;
@@ -604,32 +742,36 @@ function resolveETag(eTagExport, headProps) {
604
742
  }
605
743
  return eTagExport;
606
744
  }
607
- function resolveCacheKey(cacheKeyExport, status, eTag, pathname) {
745
+ function resolveCacheKey(cacheKeyExport, defaultKey, requestEv, eTag) {
608
746
  if (cacheKeyExport === true) {
609
- return `${status}|${eTag}|${pathname}`;
747
+ return defaultKey(requestEv, eTag);
610
748
  }
611
749
  if (typeof cacheKeyExport !== "function") {
612
- return null;
750
+ return "";
613
751
  }
614
- return cacheKeyExport(status, eTag, pathname);
752
+ return cacheKeyExport(requestEv, eTag) || "";
615
753
  }
616
- function getCachedHtml(key) {
617
- const html = ssrCache.get(key);
618
- if (html !== void 0) {
619
- ssrCache.delete(key);
620
- ssrCache.set(key, html);
621
- }
622
- return html;
754
+ function defaultSsrCacheKey(requestEv, eTag) {
755
+ const status = requestEv.status();
756
+ const pathname = requestEv.url.pathname;
757
+ return eTag ? `${status}|${eTag}|${pathname}` : `${status}|${pathname}`;
623
758
  }
624
- function setCachedHtml(key, html) {
625
- if (MAX_CACHE_SIZE <= 0) {
626
- return;
627
- }
628
- if (ssrCache.size >= MAX_CACHE_SIZE) {
629
- const firstKey = ssrCache.keys().next().value;
630
- ssrCache.delete(firstKey);
631
- }
632
- ssrCache.set(key, html);
759
+ function defaultLoaderCacheKey(requestEv, eTag) {
760
+ const loaderId = requestEv.sharedMap.get(QLoaderId$1);
761
+ const base = `${requestEv.url.pathname}|${requestEv.url.search}|${loaderId}`;
762
+ return eTag ? `${base}|${eTag}` : base;
763
+ }
764
+ function getCachedSsr(key) {
765
+ return ssrCache.get(key);
766
+ }
767
+ function setCachedSsr(key, entry) {
768
+ ssrCache.set(key, entry);
769
+ }
770
+ function getCachedLoader(key) {
771
+ return loaderCache.get(key);
772
+ }
773
+ function setCachedLoader(key, entry) {
774
+ loaderCache.set(key, entry);
633
775
  }
634
776
 
635
777
  var HttpStatus = /* @__PURE__ */ (function(HttpStatus2) {
@@ -697,10 +839,8 @@ var HttpStatus = /* @__PURE__ */ (function(HttpStatus2) {
697
839
  return HttpStatus2;
698
840
  })({});
699
841
 
700
- const RequestEvLoaders = /* @__PURE__ */ Symbol("RequestEvLoaders");
701
842
  const RequestEvMode = /* @__PURE__ */ Symbol("RequestEvMode");
702
843
  const RequestEvRoute = /* @__PURE__ */ Symbol("RequestEvRoute");
703
- const RequestEvLoaderSerializationStrategyMap = /* @__PURE__ */ Symbol("RequestEvLoaderSerializationStrategyMap");
704
844
  const RequestRouteName = "@routeName";
705
845
  const RequestEvSharedActionId = "@actionId";
706
846
  const RequestEvSharedActionFormData = "@actionFormData";
@@ -709,13 +849,6 @@ const RequestEvIsRewrite = "@rewrite";
709
849
  const RequestEvShareServerTiming = "@serverTiming";
710
850
  const RequestEvETagCacheKey = "@eTagCacheKey";
711
851
  const RequestEvHttpStatusMessage = "@httpStatusMessage";
712
- const RequestEvShareQData = "qData";
713
- function getRequestLoaders(requestEv) {
714
- return requestEv[RequestEvLoaders];
715
- }
716
- function getRequestLoaderSerializationStrategyMap(requestEv) {
717
- return requestEv[RequestEvLoaderSerializationStrategyMap];
718
- }
719
852
  function getRequestRoute(requestEv) {
720
853
  return requestEv[RequestEvRoute];
721
854
  }
@@ -835,10 +968,23 @@ function createRequestEventWithDeps(deps, serverRequestEv, loadedRoute, requestH
835
968
  const cookie = new deps.Cookie(request.headers.get("cookie"));
836
969
  const headers = new Headers();
837
970
  const url = new URL(request.url);
838
- const { pathname, isInternal } = deps.getRouteMatchPathname(url.pathname);
839
- if (isInternal) {
840
- url.pathname = pathname;
841
- sharedMap.set(deps.IsQData, true);
971
+ let internalRequest = false;
972
+ const recognized = deps.recognizeRequest(url.pathname);
973
+ if (recognized) {
974
+ url.pathname = deps.trimRecognizedInternalPathname(url.pathname, recognized);
975
+ sharedMap.set(recognized.type, true);
976
+ internalRequest = "loader";
977
+ if (recognized.data?.loaderId) {
978
+ sharedMap.set(deps.QLoaderId, recognized.data.loaderId);
979
+ }
980
+ }
981
+ const actionId = url.searchParams.get(deps.QACTION_KEY);
982
+ if (actionId) {
983
+ sharedMap.set(deps.IsQAction, true);
984
+ sharedMap.set(deps.QActionId, actionId);
985
+ if (request.method === "POST" && request.headers.get("accept")?.includes("application/json")) {
986
+ internalRequest = "action";
987
+ }
842
988
  }
843
989
  let routeModuleIndex = -1;
844
990
  let writableStream = null;
@@ -907,10 +1053,7 @@ function createRequestEventWithDeps(deps, serverRequestEv, loadedRoute, requestH
907
1053
  routeModuleIndex = ABORT_INDEX;
908
1054
  return message;
909
1055
  };
910
- const loaders = {};
911
1056
  const requestEv = {
912
- [RequestEvLoaders]: loaders,
913
- [RequestEvLoaderSerializationStrategyMap]: /* @__PURE__ */ new Map(),
914
1057
  [RequestEvMode]: serverRequestEv.mode,
915
1058
  get [RequestEvRoute]() {
916
1059
  return loadedRoute;
@@ -933,6 +1076,7 @@ function createRequestEventWithDeps(deps, serverRequestEv, loadedRoute, requestH
933
1076
  },
934
1077
  request,
935
1078
  url,
1079
+ internalRequest,
936
1080
  basePathname,
937
1081
  sharedMap,
938
1082
  get headersSent() {
@@ -952,16 +1096,14 @@ function createRequestEventWithDeps(deps, serverRequestEv, loadedRoute, requestH
952
1096
  headers.set(target, deps.createCacheControl(cacheControl));
953
1097
  },
954
1098
  resolveValue: async (loaderOrAction) => {
955
- const id = loaderOrAction.__id;
956
1099
  if (loaderOrAction.__brand === "server_loader") {
957
- if (!(id in loaders)) {
958
- throw new Error("You can not get the returned data of a loader that has not been executed for this request.");
959
- }
960
- if (loaders[id] === _UNINITIALIZED) {
961
- await deps.getRouteLoaderPromise(loaderOrAction, loaders, requestEv[RequestEvLoaderSerializationStrategyMap], requestEv);
1100
+ const loaderValues = getRouteLoaderValues(requestEv);
1101
+ if (loaderOrAction.__id in loaderValues) {
1102
+ return loaderValues[loaderOrAction.__id];
962
1103
  }
1104
+ return loadRouteLoader(loaderOrAction, requestEv);
963
1105
  }
964
- return loaders[id];
1106
+ return void 0;
965
1107
  },
966
1108
  status: (statusCode) => {
967
1109
  if (typeof statusCode === "number") {
@@ -1001,15 +1143,18 @@ function createRequestEventWithDeps(deps, serverRequestEv, loadedRoute, requestH
1001
1143
  if (statusCode > 301) {
1002
1144
  headers.set("Cache-Control", "no-store");
1003
1145
  }
1146
+ if (internalRequest === "loader") {
1147
+ return new deps.RedirectMessage();
1148
+ }
1004
1149
  return exit(new deps.RedirectMessage());
1005
1150
  },
1006
- rewrite: (pathname2) => {
1151
+ rewrite: (pathname) => {
1007
1152
  check();
1008
- if (pathname2.startsWith("http")) {
1153
+ if (pathname.startsWith("http")) {
1009
1154
  throw new deps.ServerError(400, isDev ? "Rewrite does not support absolute urls" : "Bad Request");
1010
1155
  }
1011
1156
  sharedMap.set(RequestEvIsRewrite, true);
1012
- return exit(new deps.RewriteMessage(pathname2.replace(/\/+/g, "/")));
1157
+ return exit(new deps.RewriteMessage(pathname.replace(/\/+/g, "/")));
1013
1158
  },
1014
1159
  defer: (returnData) => {
1015
1160
  return typeof returnData === "function" ? returnData : () => returnData;
@@ -1061,105 +1206,6 @@ function createRequestEventWithDeps(deps, serverRequestEv, loadedRoute, requestH
1061
1206
  return requestEv;
1062
1207
  }
1063
1208
 
1064
- function verifySerializable(data, qrl) {
1065
- try {
1066
- _verifySerializable(data, void 0);
1067
- } catch (e) {
1068
- if (e instanceof Error && qrl.dev) {
1069
- e.loc = qrl.dev;
1070
- }
1071
- throw e;
1072
- }
1073
- }
1074
- function now() {
1075
- return typeof performance !== "undefined" ? performance.now() : 0;
1076
- }
1077
- async function measure(requestEv, name, fn) {
1078
- const start = now();
1079
- try {
1080
- return await fn();
1081
- } finally {
1082
- const duration = now() - start;
1083
- let measurements = requestEv.sharedMap.get("@serverTiming");
1084
- if (!measurements) {
1085
- requestEv.sharedMap.set("@serverTiming", measurements = []);
1086
- }
1087
- measurements.push([
1088
- name,
1089
- duration
1090
- ]);
1091
- }
1092
- }
1093
- async function runValidators(requestEv, validators, data) {
1094
- let lastResult = {
1095
- success: true,
1096
- data
1097
- };
1098
- if (validators) {
1099
- for (const validator of validators) {
1100
- if (isDev) {
1101
- lastResult = await measure(requestEv, `validator$`, () => validator.validate(requestEv, data));
1102
- } else {
1103
- lastResult = await validator.validate(requestEv, data);
1104
- }
1105
- if (!lastResult.success) {
1106
- return lastResult;
1107
- } else {
1108
- data = lastResult.data;
1109
- }
1110
- }
1111
- }
1112
- return lastResult;
1113
- }
1114
- async function getRouteLoaderPromise(loader, loaders, loadersSerializationStrategy, requestEv) {
1115
- const loaderId = loader.__id;
1116
- loaders[loaderId] = runValidators(
1117
- requestEv,
1118
- loader.__validators,
1119
- void 0
1120
- // data
1121
- ).then((res) => {
1122
- if (res.success) {
1123
- if (isDev) {
1124
- return measure(requestEv, loader.__qrl.getHash(), () => loader.__qrl.call(requestEv, requestEv));
1125
- } else {
1126
- return loader.__qrl.call(requestEv, requestEv);
1127
- }
1128
- } else {
1129
- return requestEv.fail(res.status ?? 500, res.error);
1130
- }
1131
- }).then((resolvedLoader) => {
1132
- if (typeof resolvedLoader === "function") {
1133
- loaders[loaderId] = resolvedLoader();
1134
- } else {
1135
- if (isDev) {
1136
- verifySerializable(resolvedLoader, loader.__qrl);
1137
- }
1138
- loaders[loaderId] = resolvedLoader;
1139
- }
1140
- return resolvedLoader;
1141
- });
1142
- loadersSerializationStrategy.set(loaderId, loader.__serializationStrategy);
1143
- return loaders[loaderId];
1144
- }
1145
-
1146
- const IsQData = "@isQData";
1147
- const QDATA_JSON = "/q-data.json";
1148
- function getRouteMatchPathname(pathname) {
1149
- const isInternal = pathname.endsWith(QDATA_JSON);
1150
- if (isInternal) {
1151
- const trimEnd = pathname.length - QDATA_JSON.length + (globalThis.__NO_TRAILING_SLASH__ ? 0 : 1);
1152
- pathname = pathname.slice(0, trimEnd);
1153
- if (pathname === "") {
1154
- pathname = "/";
1155
- }
1156
- }
1157
- return {
1158
- pathname,
1159
- isInternal
1160
- };
1161
- }
1162
-
1163
1209
  const encoder = /* @__PURE__ */ new TextEncoder();
1164
1210
  function getContentType(headers) {
1165
1211
  return (headers.get("content-type")?.split(/[;,]/, 1)[0].trim() ?? "").toLowerCase();
@@ -1180,22 +1226,20 @@ class RedirectMessage extends AbortMessage {
1180
1226
  }
1181
1227
 
1182
1228
  function createResolveRequestHandlers(deps) {
1183
- const resolveRequestHandlers = (serverPlugins, route, method, checkOrigin, renderHandler, isInternal) => {
1229
+ const resolveRequestHandlers = (serverPlugins, route, method, checkOrigin, renderHandler) => {
1184
1230
  const routeLoaders = [];
1185
1231
  const routeActions = [];
1186
1232
  const requestHandlers = [];
1187
1233
  const isPageRoute = !!isLastModulePageRoute(route.$mods$);
1188
- if (isInternal) {
1189
- requestHandlers.push(handleQDataRedirect);
1190
- }
1191
1234
  if (isPageRoute) {
1235
+ requestHandlers.push(jsonRequestWrapper());
1192
1236
  requestHandlers.push(serverErrorMiddleware(route, renderHandler));
1193
1237
  }
1194
1238
  if (serverPlugins) {
1195
1239
  _resolveRequestHandlers(routeLoaders, routeActions, requestHandlers, serverPlugins, isPageRoute, method);
1196
1240
  }
1197
1241
  const routeModules = route.$mods$;
1198
- _resolveRequestHandlers(routeLoaders, routeActions, requestHandlers, routeModules, isPageRoute, method);
1242
+ _resolveRequestHandlers(routeLoaders, routeActions, requestHandlers, routeModules, isPageRoute, method, isPageRoute);
1199
1243
  const routeName = route.$routeName$;
1200
1244
  if (checkOrigin && (method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE")) {
1201
1245
  if (checkOrigin === "lax-proto") {
@@ -1205,70 +1249,32 @@ function createResolveRequestHandlers(deps) {
1205
1249
  }
1206
1250
  }
1207
1251
  if (isPageRoute) {
1252
+ requestHandlers.push(loaderHandler(routeLoaders, route.$loaderPaths$));
1253
+ requestHandlers.push(actionHandler(routeActions));
1208
1254
  if (method === "POST" || method === "GET") {
1209
1255
  requestHandlers.push(runServerFunction);
1210
1256
  }
1211
1257
  if (!route.$notFound$) {
1212
1258
  requestHandlers.push(fixTrailingSlash);
1213
1259
  }
1214
- if (isInternal) {
1215
- requestHandlers.push(renderQData);
1216
- }
1217
1260
  }
1218
1261
  if (isPageRoute) {
1219
1262
  requestHandlers.push((ev) => {
1220
1263
  ev.sharedMap.set(deps.RequestRouteName, routeName);
1221
1264
  });
1222
1265
  requestHandlers.push(actionsMiddleware(routeActions));
1223
- requestHandlers.push(loadersMiddleware(routeLoaders));
1266
+ requestHandlers.push(loadersMiddleware(routeLoaders, route));
1224
1267
  requestHandlers.push(eTagMiddleware(route));
1225
1268
  requestHandlers.push(renderHandler);
1226
1269
  }
1227
1270
  return requestHandlers;
1228
1271
  };
1229
- const _resolveRequestHandlers = (routeLoaders, routeActions, requestHandlers, routeModules, collectActions, method) => {
1230
- for (const routeModule of routeModules) {
1231
- if (typeof routeModule.onRequest === "function") {
1232
- requestHandlers.push(routeModule.onRequest);
1233
- } else if (Array.isArray(routeModule.onRequest)) {
1234
- requestHandlers.push(...routeModule.onRequest);
1235
- }
1236
- let methodReqHandler;
1237
- switch (method) {
1238
- case "GET": {
1239
- methodReqHandler = routeModule.onGet;
1240
- break;
1241
- }
1242
- case "POST": {
1243
- methodReqHandler = routeModule.onPost;
1244
- break;
1245
- }
1246
- case "PUT": {
1247
- methodReqHandler = routeModule.onPut;
1248
- break;
1249
- }
1250
- case "PATCH": {
1251
- methodReqHandler = routeModule.onPatch;
1252
- break;
1253
- }
1254
- case "DELETE": {
1255
- methodReqHandler = routeModule.onDelete;
1256
- break;
1257
- }
1258
- case "OPTIONS": {
1259
- methodReqHandler = routeModule.onOptions;
1260
- break;
1261
- }
1262
- case "HEAD": {
1263
- methodReqHandler = routeModule.onHead;
1264
- break;
1265
- }
1266
- }
1267
- if (typeof methodReqHandler === "function") {
1268
- requestHandlers.push(methodReqHandler);
1269
- } else if (Array.isArray(methodReqHandler)) {
1270
- requestHandlers.push(...methodReqHandler);
1271
- }
1272
+ function _resolveRequestHandlers(routeLoaders, routeActions, requestHandlers, routeModules, collectActions, method, guardPageHandlersForLoader = false) {
1273
+ for (let i = 0; i < routeModules.length; i++) {
1274
+ const routeModule = routeModules[i];
1275
+ const moduleHandlers = getModuleRequestHandlers(routeModule, method);
1276
+ const shouldGuardPageHandlers = guardPageHandlersForLoader && i === routeModules.length - 1;
1277
+ requestHandlers.push(...shouldGuardPageHandlers ? moduleHandlers.map((handler) => guardPageHandlerForLoader(routeModule, handler)) : moduleHandlers);
1272
1278
  if (collectActions) {
1273
1279
  for (const module of Object.values(routeModule)) {
1274
1280
  if (typeof module === "function") {
@@ -1281,7 +1287,73 @@ function createResolveRequestHandlers(deps) {
1281
1287
  }
1282
1288
  }
1283
1289
  }
1284
- };
1290
+ }
1291
+ function getModuleRequestHandlers(routeModule, method) {
1292
+ const handlers = [];
1293
+ addRequestHandlers(handlers, routeModule.onRequest);
1294
+ let methodReqHandler;
1295
+ switch (method) {
1296
+ case "GET": {
1297
+ methodReqHandler = routeModule.onGet;
1298
+ break;
1299
+ }
1300
+ case "POST": {
1301
+ methodReqHandler = routeModule.onPost;
1302
+ break;
1303
+ }
1304
+ case "PUT": {
1305
+ methodReqHandler = routeModule.onPut;
1306
+ break;
1307
+ }
1308
+ case "PATCH": {
1309
+ methodReqHandler = routeModule.onPatch;
1310
+ break;
1311
+ }
1312
+ case "DELETE": {
1313
+ methodReqHandler = routeModule.onDelete;
1314
+ break;
1315
+ }
1316
+ case "OPTIONS": {
1317
+ methodReqHandler = routeModule.onOptions;
1318
+ break;
1319
+ }
1320
+ case "HEAD": {
1321
+ methodReqHandler = routeModule.onHead;
1322
+ break;
1323
+ }
1324
+ }
1325
+ addRequestHandlers(handlers, methodReqHandler);
1326
+ return handlers;
1327
+ }
1328
+ function addRequestHandlers(handlers, handler) {
1329
+ if (typeof handler === "function") {
1330
+ handlers.push(handler);
1331
+ } else if (Array.isArray(handler)) {
1332
+ handlers.push(...handler);
1333
+ }
1334
+ }
1335
+ function guardPageHandlerForLoader(routeModule, handler) {
1336
+ return (requestEv) => {
1337
+ if (requestEv.sharedMap.has(IsQLoader$1)) {
1338
+ const loaderId = requestEv.sharedMap.get(QLoaderId$1);
1339
+ if (!moduleHasLoader(routeModule, loaderId)) {
1340
+ return;
1341
+ }
1342
+ }
1343
+ return handler(requestEv);
1344
+ };
1345
+ }
1346
+ function moduleHasLoader(routeModule, loaderId) {
1347
+ if (typeof loaderId !== "string") {
1348
+ return false;
1349
+ }
1350
+ for (const value of Object.values(routeModule)) {
1351
+ if (checkBrand(value, "server_loader") && value.__id === loaderId) {
1352
+ return true;
1353
+ }
1354
+ }
1355
+ return false;
1356
+ }
1285
1357
  const checkBrand = (obj, brand) => {
1286
1358
  return obj && typeof obj === "function" && obj.__brand === brand;
1287
1359
  };
@@ -1293,8 +1365,7 @@ function createResolveRequestHandlers(deps) {
1293
1365
  return;
1294
1366
  }
1295
1367
  const { method } = requestEv;
1296
- const loaders = deps.getRequestLoaders(requestEv);
1297
- if (isDev && method === "GET") {
1368
+ if (isDev$1 && method === "GET") {
1298
1369
  if (requestEv.query.has(deps.QACTION_KEY)) {
1299
1370
  console.warn('Seems like you are submitting a Qwik Action via GET request. Qwik Actions should be submitted via POST request.\nMake sure your <form> has method="POST" attribute, like this: <form method="POST">');
1300
1371
  }
@@ -1311,66 +1382,79 @@ function createResolveRequestHandlers(deps) {
1311
1382
  throw new Error(`Expected request data for the action id ${selectedActionId} to be an object`);
1312
1383
  }
1313
1384
  const result = await runValidators(requestEv, action.__validators, data);
1385
+ let actionResult;
1314
1386
  if (!result.success) {
1315
- loaders[selectedActionId] = requestEv.fail(result.status ?? 500, result.error);
1387
+ actionResult = requestEv.fail(result.status ?? 500, result.error);
1316
1388
  } else {
1317
- const actionResolved = isDev ? await measure(requestEv, action.__qrl.getHash(), () => action.__qrl.call(requestEv, result.data, requestEv)) : await action.__qrl.call(requestEv, result.data, requestEv);
1318
- if (isDev) {
1389
+ const actionResolved = isDev$1 ? await measure(requestEv, action.__qrl.getHash(), () => action.__qrl.call(requestEv, result.data, requestEv)) : await action.__qrl.call(requestEv, result.data, requestEv);
1390
+ if (isDev$1) {
1319
1391
  verifySerializable(actionResolved, action.__qrl);
1320
1392
  }
1321
- loaders[selectedActionId] = actionResolved;
1393
+ actionResult = actionResolved;
1322
1394
  }
1395
+ requestEv.sharedMap.set("@actionResult", actionResult);
1323
1396
  }
1324
1397
  }
1325
1398
  }
1326
1399
  };
1327
1400
  }
1328
- function loadersMiddleware(routeLoaders) {
1401
+ function loadersMiddleware(routeLoaders, route) {
1329
1402
  return async (requestEvent) => {
1330
1403
  const requestEv = requestEvent;
1331
1404
  if (requestEv.headersSent) {
1332
1405
  requestEv.exit();
1333
1406
  return;
1334
1407
  }
1335
- const loaders = deps.getRequestLoaders(requestEv);
1336
- const loadersSerializationStrategy = deps.getRequestLoaderSerializationStrategyMap(requestEv);
1337
1408
  if (routeLoaders.length > 0) {
1338
- const resolvedLoadersPromises = routeLoaders.map((loader) => deps.getRouteLoaderPromise(loader, loaders, loadersSerializationStrategy, requestEv));
1339
- await Promise.all(resolvedLoadersPromises);
1409
+ setLoaderData(requestEv, routeLoaders, route);
1410
+ const loaderValues = getRouteLoaderValues(requestEv);
1411
+ await Promise.all(routeLoaders.map(async (loader) => {
1412
+ loaderValues[loader.__id] = await loadRouteLoader(loader, requestEv);
1413
+ }));
1340
1414
  }
1341
1415
  };
1342
1416
  }
1417
+ function setLoaderData(requestEv, routeLoaders, route) {
1418
+ if (routeLoaders.length === 0) {
1419
+ return;
1420
+ }
1421
+ const routeLoaderCtx = getRouteLoaderCtx(requestEv);
1422
+ if (route.$loaderPaths$) {
1423
+ Object.assign(routeLoaderCtx.loaderPaths, route.$loaderPaths$);
1424
+ }
1425
+ setRouteLoaders(requestEv, routeLoaders);
1426
+ }
1343
1427
  function eTagMiddleware(route) {
1344
1428
  return (requestEv) => {
1345
1429
  if (requestEv.headersSent) {
1346
1430
  return;
1347
1431
  }
1348
- if (requestEv.method !== "GET" || requestEv.sharedMap.has(deps.IsQData)) {
1432
+ if (requestEv.method !== "GET") {
1349
1433
  return;
1350
1434
  }
1351
1435
  const mods = route.$mods$;
1352
- const hasETag = mods.some((m) => {
1436
+ const hasCachingConfig = mods.some((m) => {
1353
1437
  if (!m) {
1354
1438
  return false;
1355
1439
  }
1356
1440
  if (m.routeConfig) {
1357
- return typeof m.routeConfig === "function" || m.routeConfig.eTag !== void 0;
1441
+ return typeof m.routeConfig === "function" || m.routeConfig.eTag !== void 0 || m.routeConfig.cacheKey !== void 0;
1358
1442
  }
1359
- return m.eTag !== void 0;
1443
+ return m.eTag !== void 0 || m.cacheKey !== void 0;
1360
1444
  });
1361
- if (!hasETag) {
1445
+ if (!hasCachingConfig) {
1362
1446
  return;
1363
1447
  }
1364
- const loaders = deps.getRequestLoaders(requestEv);
1448
+ const loaderValues = getRouteLoaderValues(requestEv);
1365
1449
  const getData = (loaderOrAction) => {
1366
1450
  const id = loaderOrAction.__id;
1367
- if (loaderOrAction.__brand === "server_loader" && !(id in loaders)) {
1368
- throw new Error("Loader not executed for this request.");
1369
- }
1370
- if (loaders[id] instanceof Promise) {
1371
- throw new Error("Loaders returning a promise cannot be resolved for the eTag function.");
1451
+ if (loaderOrAction.__brand === "server_loader") {
1452
+ if (!(id in loaderValues)) {
1453
+ throw new Error("Loader not executed for this request.");
1454
+ }
1455
+ return loaderValues[id];
1372
1456
  }
1373
- return loaders[id];
1457
+ return requestEv.sharedMap.get(deps.RequestEvSharedActionId) === id ? requestEv.sharedMap.get("@actionResult") : void 0;
1374
1458
  };
1375
1459
  const routeLocation = {
1376
1460
  params: requestEv.params,
@@ -1387,32 +1471,36 @@ function createResolveRequestHandlers(deps) {
1387
1471
  resolveValue: getData,
1388
1472
  ...routeLocation
1389
1473
  };
1390
- const eTag = deps.resolveETag(config.eTag, headProps);
1391
- if (!eTag) {
1392
- return;
1393
- }
1394
- requestEv.headers.set("ETag", eTag);
1395
- const ifNoneMatch = requestEv.request.headers.get("If-None-Match");
1396
- if (ifNoneMatch && (ifNoneMatch === eTag || ifNoneMatch === `W/${eTag}` || `W/${ifNoneMatch}` === eTag)) {
1397
- requestEv.status(304);
1398
- requestEv.send(304, "");
1474
+ const resolvedETag = deps.resolveETag(config.eTag, headProps);
1475
+ const normalizedETag = resolvedETag !== null ? normalizeETag(resolvedETag) : "";
1476
+ if (normalizedETag && performETagMatch(requestEv, normalizedETag)) {
1399
1477
  return;
1400
1478
  }
1401
1479
  if (deps.MAX_CACHE_SIZE <= 0) {
1402
1480
  return;
1403
1481
  }
1404
- const cacheKey = deps.resolveCacheKey(config.cacheKey, status, eTag, requestEv.url.pathname);
1482
+ const cacheKey = deps.resolveCacheKey(config.cacheKey, deps.defaultSsrCacheKey, requestEv, normalizedETag);
1405
1483
  if (!cacheKey) {
1406
1484
  return;
1407
1485
  }
1408
- const cachedHtml = deps.getCachedHtml(cacheKey);
1409
- if (cachedHtml) {
1486
+ const cached = deps.getCachedSsr(cacheKey);
1487
+ if (cached) {
1410
1488
  requestEv.headers.set("Content-Type", "text/html; charset=utf-8");
1411
1489
  requestEv.headers.set("X-SSR-Cache", "HIT");
1412
- requestEv.send(status, cachedHtml);
1490
+ if (!normalizedETag) {
1491
+ if (performETagMatch(requestEv, cached.eTag)) {
1492
+ return;
1493
+ }
1494
+ } else {
1495
+ setETagHeader(requestEv, cached.eTag);
1496
+ }
1497
+ requestEv.send(status, cached.body);
1413
1498
  return;
1414
1499
  }
1415
- requestEv.sharedMap.set(deps.RequestEvETagCacheKey, cacheKey);
1500
+ requestEv.sharedMap.set(deps.RequestEvETagCacheKey, {
1501
+ key: cacheKey,
1502
+ eTag: normalizedETag
1503
+ });
1416
1504
  };
1417
1505
  }
1418
1506
  function serverErrorMiddleware(route, renderHandler) {
@@ -1423,9 +1511,6 @@ function createResolveRequestHandlers(deps) {
1423
1511
  if (!(e instanceof deps.ServerError) || requestEv.headersSent) {
1424
1512
  throw e;
1425
1513
  }
1426
- if (requestEv.sharedMap.has(deps.IsQData)) {
1427
- throw e;
1428
- }
1429
1514
  const accept = requestEv.request.headers.get("Accept");
1430
1515
  if (accept && !accept.includes("text/html")) {
1431
1516
  throw e;
@@ -1449,7 +1534,7 @@ function createResolveRequestHandlers(deps) {
1449
1534
  };
1450
1535
  if (validators) {
1451
1536
  for (const validator of validators) {
1452
- if (isDev) {
1537
+ if (isDev$1) {
1453
1538
  lastResult = await measure(requestEv, `validator$`, () => validator.validate(requestEv, data));
1454
1539
  } else {
1455
1540
  lastResult = await validator.validate(requestEv, data);
@@ -1477,7 +1562,7 @@ function createResolveRequestHandlers(deps) {
1477
1562
  const qrl = inlinedQrl(null, serverFnHash, data.slice(1));
1478
1563
  let result;
1479
1564
  try {
1480
- if (isDev) {
1565
+ if (isDev$1) {
1481
1566
  result = await measure(ev, `server_${serverFnHash}`, () => qrl.apply(ev, data[0]));
1482
1567
  } else {
1483
1568
  result = await qrl.apply(ev, data[0]);
@@ -1494,7 +1579,7 @@ function createResolveRequestHandlers(deps) {
1494
1579
  const writable = ev.getWritableStream();
1495
1580
  const stream = writable.getWriter();
1496
1581
  for await (const item of result) {
1497
- if (isDev) {
1582
+ if (isDev$1) {
1498
1583
  verifySerializable(item, qrl);
1499
1584
  }
1500
1585
  const message = await _serialize(item);
@@ -1515,16 +1600,15 @@ function createResolveRequestHandlers(deps) {
1515
1600
  }
1516
1601
  }
1517
1602
  function fixTrailingSlash(ev) {
1518
- const { basePathname, originalUrl, sharedMap } = ev;
1603
+ const { basePathname, originalUrl } = ev;
1519
1604
  const { pathname, search } = originalUrl;
1520
- const isQData = sharedMap.has(deps.IsQData);
1521
1605
  if (!pathname.startsWith("/") || pathname.startsWith("//")) {
1522
1606
  return;
1523
1607
  }
1524
- if (!isQData && pathname !== basePathname && !pathname.endsWith(".html")) {
1608
+ if (pathname !== basePathname && !pathname.endsWith(".html")) {
1525
1609
  if (!globalThis.__NO_TRAILING_SLASH__) {
1526
1610
  if (!pathname.endsWith("/")) {
1527
- throw ev.redirect(deps.HttpStatus.MovedPermanently, pathname + "/" + search);
1611
+ throw ev.redirect(deps.HttpStatus.MovedPermanently, ensureSlash(pathname) + search);
1528
1612
  }
1529
1613
  } else {
1530
1614
  if (pathname.endsWith("/")) {
@@ -1552,12 +1636,9 @@ function createResolveRequestHandlers(deps) {
1552
1636
  }
1553
1637
  function getPathname(url) {
1554
1638
  url = new URL(url);
1555
- if (url.pathname.endsWith(deps.QDATA_JSON)) {
1556
- url.pathname = url.pathname.slice(0, -deps.QDATA_JSON.length);
1557
- }
1558
1639
  if (!globalThis.__NO_TRAILING_SLASH__) {
1559
1640
  if (!url.pathname.endsWith("/")) {
1560
- url.pathname += "/";
1641
+ url.pathname = ensureSlash(url.pathname);
1561
1642
  }
1562
1643
  } else {
1563
1644
  if (url.pathname.endsWith("/")) {
@@ -1594,19 +1675,16 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
1594
1675
  if (requestEv.headersSent) {
1595
1676
  return;
1596
1677
  }
1597
- if (requestEv.sharedMap.has(deps.IsQData)) {
1598
- return;
1599
- }
1600
1678
  const responseHeaders = requestEv.headers;
1601
1679
  if (!responseHeaders.has("Content-Type")) {
1602
1680
  responseHeaders.set("Content-Type", "text/html; charset=utf-8");
1603
1681
  }
1604
- const eTagCacheKey = requestEv.sharedMap.get(deps.RequestEvETagCacheKey);
1682
+ const cachePlan = requestEv.sharedMap.get(deps.RequestEvETagCacheKey);
1605
1683
  const { readable, writable } = new TextEncoderStream();
1606
1684
  const writableStream = requestEv.getWritableStream();
1607
1685
  let cacheChunks;
1608
1686
  let pipeSource = readable;
1609
- if (eTagCacheKey) {
1687
+ if (cachePlan) {
1610
1688
  cacheChunks = [];
1611
1689
  const capture = new TransformStream({
1612
1690
  transform(chunk, controller) {
@@ -1623,29 +1701,24 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
1623
1701
  pipeError = error;
1624
1702
  });
1625
1703
  const stream = writable.getWriter();
1626
- const status = requestEv.status();
1627
1704
  try {
1628
1705
  const isStatic = deps.getRequestMode(requestEv) === "static";
1629
1706
  const serverData = deps.getQwikRouterServerData(requestEv);
1630
1707
  const result = await render({
1631
1708
  base: requestEv.basePathname + "build/",
1632
1709
  stream,
1710
+ streaming: isStatic ? {
1711
+ outOfOrder: false
1712
+ } : void 0,
1633
1713
  serverData,
1634
1714
  containerAttributes: {
1635
1715
  ["q:render"]: isStatic ? "static" : "",
1636
1716
  ...serverData.containerAttributes
1637
1717
  }
1638
1718
  });
1639
- const qData = {
1640
- loaders: deps.getRequestLoaders(requestEv),
1641
- action: requestEv.sharedMap.get(deps.RequestEvSharedActionId),
1642
- status: status !== 200 ? status : 200,
1643
- href: getPathname(requestEv.url)
1644
- };
1645
1719
  if (typeof result.html === "string") {
1646
1720
  await stream.write(result.html);
1647
1721
  }
1648
- requestEv.sharedMap.set(deps.RequestEvShareQData, qData);
1649
1722
  } finally {
1650
1723
  await stream.ready;
1651
1724
  await stream.close();
@@ -1654,7 +1727,7 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
1654
1727
  if (pipeError) {
1655
1728
  throw pipeError;
1656
1729
  }
1657
- if (eTagCacheKey && cacheChunks && cacheChunks.length > 0) {
1730
+ if (cachePlan && cacheChunks && cacheChunks.length > 0) {
1658
1731
  const totalLength = cacheChunks.reduce((sum, chunk) => sum + chunk.length, 0);
1659
1732
  const combined = new Uint8Array(totalLength);
1660
1733
  let offset = 0;
@@ -1662,86 +1735,16 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
1662
1735
  combined.set(chunk, offset);
1663
1736
  offset += chunk.length;
1664
1737
  }
1665
- deps.setCachedHtml(eTagCacheKey, new TextDecoder().decode(combined));
1738
+ const html = new TextDecoder().decode(combined);
1739
+ const cachedETag = cachePlan.eTag || hash(html);
1740
+ deps.setCachedSsr(cachePlan.key, {
1741
+ eTag: cachedETag,
1742
+ body: html
1743
+ });
1666
1744
  }
1667
1745
  await writableStream.close();
1668
1746
  };
1669
1747
  }
1670
- async function handleQDataRedirect(requestEv) {
1671
- try {
1672
- await requestEv.next();
1673
- } catch (err) {
1674
- if (!(err instanceof deps.RedirectMessage)) {
1675
- throw err;
1676
- }
1677
- }
1678
- if (requestEv.headersSent) {
1679
- return;
1680
- }
1681
- const status = requestEv.status();
1682
- const location = requestEv.headers.get("Location");
1683
- const isRedirect = status >= 301 && status <= 308 && location;
1684
- if (isRedirect) {
1685
- const adaptedLocation = makeQDataPath(location);
1686
- if (adaptedLocation) {
1687
- requestEv.headers.set("Location", adaptedLocation);
1688
- requestEv.getWritableStream().close();
1689
- return;
1690
- } else {
1691
- requestEv.status(200);
1692
- requestEv.headers.delete("Location");
1693
- }
1694
- }
1695
- }
1696
- async function renderQData(requestEv) {
1697
- await requestEv.next();
1698
- if (requestEv.headersSent || requestEv.exited) {
1699
- return;
1700
- }
1701
- const status = requestEv.status();
1702
- const redirectLocation = requestEv.headers.get("Location");
1703
- requestEv.headers.set("Content-Type", "application/json; charset=utf-8");
1704
- let loaders = deps.getRequestLoaders(requestEv);
1705
- const selectedLoaderIds = requestEv.query.getAll(deps.QLOADER_KEY);
1706
- const hasCustomLoaders = selectedLoaderIds.length > 0;
1707
- if (hasCustomLoaders) {
1708
- const selectedLoaders = {};
1709
- for (const loaderId of selectedLoaderIds) {
1710
- const loader = loaders[loaderId];
1711
- selectedLoaders[loaderId] = loader;
1712
- }
1713
- loaders = selectedLoaders;
1714
- }
1715
- const qData = hasCustomLoaders ? {
1716
- loaders,
1717
- status: status !== 200 ? status : 200,
1718
- href: getPathname(requestEv.url)
1719
- } : {
1720
- loaders,
1721
- action: requestEv.sharedMap.get(deps.RequestEvSharedActionId),
1722
- status: status !== 200 ? status : 200,
1723
- href: getPathname(requestEv.url),
1724
- redirect: redirectLocation ?? void 0,
1725
- isRewrite: requestEv.sharedMap.get(deps.RequestEvIsRewrite)
1726
- };
1727
- const writer = requestEv.getWritableStream().getWriter();
1728
- const data = await _serialize(qData);
1729
- writer.write(deps.encoder.encode(data));
1730
- requestEv.sharedMap.set(deps.RequestEvShareQData, qData);
1731
- writer.close();
1732
- }
1733
- function makeQDataPath(href) {
1734
- if (href.startsWith("/")) {
1735
- if (!href.includes(deps.QDATA_JSON)) {
1736
- const url = new URL(href, "http://localhost");
1737
- const pathname = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
1738
- return pathname + deps.QDATA_JSON + url.search;
1739
- }
1740
- return href;
1741
- } else {
1742
- return void 0;
1743
- }
1744
- }
1745
1748
  function now() {
1746
1749
  return typeof performance !== "undefined" ? performance.now() : 0;
1747
1750
  }
@@ -1790,6 +1793,7 @@ function getQwikRouterServerDataWithDeps(deps, requestEv) {
1790
1793
  const requestHeaders = {};
1791
1794
  request.headers.forEach((value, key) => requestHeaders[key] = value);
1792
1795
  const action = requestEv.sharedMap.get(deps.RequestEvSharedActionId);
1796
+ const actionResult = requestEv.sharedMap.get("@actionResult");
1793
1797
  const formData = requestEv.sharedMap.get(deps.RequestEvSharedActionFormData);
1794
1798
  const routeName = requestEv.sharedMap.get(deps.RequestRouteName);
1795
1799
  const nonce = requestEv.sharedMap.get(deps.RequestEvSharedNonce);
@@ -1804,11 +1808,12 @@ function getQwikRouterServerDataWithDeps(deps, requestEv) {
1804
1808
  if (protocol) {
1805
1809
  reconstructedUrl.protocol = protocol;
1806
1810
  }
1807
- const loaders = deps.getRequestLoaders(requestEv);
1808
- const loadersSerializationStrategy = deps.getRequestLoaderSerializationStrategyMap(requestEv);
1811
+ const routeLoaderCtx = getRouteLoaderCtx(requestEv);
1812
+ const loaderValues = getRouteLoaderValues(requestEv);
1809
1813
  return {
1810
1814
  url: reconstructedUrl.href,
1811
1815
  requestHeaders,
1816
+ renderMode: deps.getRequestMode(requestEv),
1812
1817
  locale: locale(),
1813
1818
  nonce,
1814
1819
  containerAttributes: {
@@ -1821,12 +1826,13 @@ function getQwikRouterServerDataWithDeps(deps, requestEv) {
1821
1826
  ...params
1822
1827
  },
1823
1828
  loadedRoute: deps.getRequestRoute(requestEv),
1829
+ routeLoaderCtx,
1830
+ loaderValues,
1824
1831
  response: {
1825
1832
  status: status(),
1826
1833
  statusMessage: requestEv.sharedMap.get(deps.RequestEvHttpStatusMessage),
1827
- loaders,
1828
- loadersSerializationStrategy,
1829
1834
  action,
1835
+ actionResult,
1830
1836
  formData
1831
1837
  }
1832
1838
  }
@@ -1840,8 +1846,7 @@ const responsePageDeps = {
1840
1846
  RequestEvSharedActionId,
1841
1847
  RequestEvSharedNonce,
1842
1848
  RequestRouteName,
1843
- getRequestLoaders,
1844
- getRequestLoaderSerializationStrategyMap,
1849
+ getRequestMode,
1845
1850
  getRequestRoute
1846
1851
  };
1847
1852
  function getQwikRouterServerData(...args) {
@@ -1851,13 +1856,8 @@ function getQwikRouterServerData(...args) {
1851
1856
  const requestHandlers = createResolveRequestHandlers({
1852
1857
  QACTION_KEY,
1853
1858
  QFN_KEY,
1854
- QLOADER_KEY,
1855
- QDATA_JSON,
1856
- IsQData,
1857
1859
  RequestEvETagCacheKey,
1858
1860
  RequestEvHttpStatusMessage,
1859
- RequestEvIsRewrite,
1860
- RequestEvShareQData,
1861
1861
  RequestEvShareServerTiming,
1862
1862
  RequestEvSharedActionId,
1863
1863
  RequestRouteName,
@@ -1866,18 +1866,19 @@ const requestHandlers = createResolveRequestHandlers({
1866
1866
  HttpStatus,
1867
1867
  encoder,
1868
1868
  isContentType,
1869
- getCachedHtml,
1869
+ defaultSsrCacheKey,
1870
+ defaultLoaderCacheKey,
1871
+ getCachedSsr,
1872
+ getCachedLoader,
1870
1873
  getQwikRouterServerData,
1871
- getRequestLoaderSerializationStrategyMap,
1872
- getRequestLoaders,
1873
1874
  getRequestMode,
1874
- getRouteLoaderPromise,
1875
1875
  loadHttpError: () => import('./http-error.qwik.mjs'),
1876
1876
  MAX_CACHE_SIZE,
1877
1877
  resolveCacheKey,
1878
1878
  resolveETag,
1879
1879
  resolveRouteConfig,
1880
- setCachedHtml
1880
+ setCachedSsr,
1881
+ setCachedLoader
1881
1882
  });
1882
1883
  const { renderQwikMiddleware, resolveRequestHandlers} = requestHandlers;
1883
1884
 
@@ -2090,6 +2091,7 @@ class RewriteMessage extends AbortMessage$1 {
2090
2091
 
2091
2092
  const requestEventDeps = {
2092
2093
  QDATA_KEY,
2094
+ QACTION_KEY,
2093
2095
  isPromise,
2094
2096
  createCacheControl,
2095
2097
  Cookie,
@@ -2097,9 +2099,12 @@ const requestEventDeps = {
2097
2099
  RedirectMessage,
2098
2100
  RewriteMessage,
2099
2101
  ServerError,
2100
- getRouteLoaderPromise,
2101
- getRouteMatchPathname,
2102
- IsQData,
2102
+ recognizeRequest,
2103
+ trimRecognizedInternalPathname,
2104
+ IsQLoader,
2105
+ IsQAction,
2106
+ QLoaderId,
2107
+ QActionId,
2103
2108
  encoder,
2104
2109
  getContentType
2105
2110
  };
@@ -2271,18 +2276,13 @@ function runQwikRouter(serverRequestEv, loadedRoute, requestHandlers, rebuildRou
2271
2276
  }
2272
2277
 
2273
2278
  const staticWorkerThreadDeps = {
2274
- RequestEvShareQData,
2275
2279
  loadRoute,
2276
2280
  renderQwikMiddleware,
2277
2281
  resolveRequestHandlers,
2278
- getRouteMatchPathname,
2282
+ trimInternalPathname,
2279
2283
  runQwikRouter
2280
2284
  };
2281
2285
  const createNoopWritableStream = () => new WritableStream();
2282
- async function loadSerialize() {
2283
- const { _serialize: serialize } = await import('@qwik.dev/core/internal');
2284
- return serialize;
2285
- }
2286
2286
  function isRedirectMessage(value) {
2287
2287
  return typeof value === "object" && value !== null && value.constructor?.name === "RedirectMessage";
2288
2288
  }
@@ -2291,20 +2291,20 @@ async function requestHandlerForSsg(serverRequestEv, opts, deps) {
2291
2291
  if (!qwikRouterConfig) {
2292
2292
  throw new Error("qwikRouterConfig is required.");
2293
2293
  }
2294
- const { pathname, isInternal } = deps.getRouteMatchPathname(serverRequestEv.url.pathname);
2294
+ const pathname = deps.trimInternalPathname(serverRequestEv.url.pathname);
2295
2295
  if (pathname === "/.well-known" || pathname.startsWith("/.well-known/")) {
2296
2296
  return null;
2297
2297
  }
2298
2298
  const { routes, serverPlugins, cacheModules } = qwikRouterConfig;
2299
- const loadedRoute = await deps.loadRoute(routes, cacheModules, pathname, isInternal);
2300
- const requestHandlers = deps.resolveRequestHandlers(serverPlugins, loadedRoute, serverRequestEv.request.method, checkOrigin ?? true, deps.renderQwikMiddleware(render), isInternal);
2299
+ const loadedRoute = await deps.loadRoute(routes, cacheModules, pathname);
2300
+ const requestHandlers = deps.resolveRequestHandlers(serverPlugins, loadedRoute, serverRequestEv.request.method, checkOrigin ?? true, deps.renderQwikMiddleware(render));
2301
2301
  if (qwikRouterConfig.fallthrough && loadedRoute.$notFound$) {
2302
2302
  return null;
2303
2303
  }
2304
2304
  const rebuildRouteInfo = async (url) => {
2305
- const { pathname: pathname2 } = deps.getRouteMatchPathname(url.pathname);
2306
- const loadedRoute2 = await deps.loadRoute(routes, cacheModules, pathname2, isInternal);
2307
- const requestHandlers2 = deps.resolveRequestHandlers(serverPlugins, loadedRoute2, serverRequestEv.request.method, checkOrigin ?? true, deps.renderQwikMiddleware(render), isInternal);
2305
+ const cleanPathname = deps.trimInternalPathname(url.pathname);
2306
+ const loadedRoute2 = await deps.loadRoute(routes, cacheModules, cleanPathname);
2307
+ const requestHandlers2 = deps.resolveRequestHandlers(serverPlugins, loadedRoute2, serverRequestEv.request.method, checkOrigin ?? true, deps.renderQwikMiddleware(render));
2308
2308
  return {
2309
2309
  loadedRoute: loadedRoute2,
2310
2310
  requestHandlers: requestHandlers2
@@ -2360,11 +2360,11 @@ async function workerRender(sys, opts, staticRoute, pendingPromises, callback, d
2360
2360
  result.resourceType = "page";
2361
2361
  }
2362
2362
  const hasRouteWriter = isHtml ? opts.emitHtml !== false : true;
2363
- const writeQDataEnabled = isHtml && opts.emitData !== false;
2363
+ const writeLoaderDataEnabled = isHtml && opts.emitData !== false;
2364
2364
  const stream = new WritableStream({
2365
2365
  async start() {
2366
2366
  try {
2367
- if (hasRouteWriter || writeQDataEnabled) {
2367
+ if (hasRouteWriter || writeLoaderDataEnabled) {
2368
2368
  await sys.ensureDir(routeFilePath);
2369
2369
  }
2370
2370
  if (hasRouteWriter) {
@@ -2404,23 +2404,34 @@ async function workerRender(sys, opts, staticRoute, pendingPromises, callback, d
2404
2404
  async close() {
2405
2405
  const writePromises = [];
2406
2406
  try {
2407
- if (writeQDataEnabled) {
2408
- const qData = requestEv.sharedMap.get(deps.RequestEvShareQData);
2409
- if (qData && !is404ErrorPage) {
2410
- const qDataFilePath = sys.getDataFilePath(url.pathname);
2411
- const dataWriter = sys.createWriteStream(qDataFilePath);
2412
- dataWriter.on("error", (e) => {
2407
+ if (writeLoaderDataEnabled && !is404ErrorPage) {
2408
+ const routeLoaders = getRouteLoaders(requestEv);
2409
+ const loaderValues = getRouteLoaderValues(requestEv);
2410
+ const manifestHash = opts.manifest?.manifestHash || "dev";
2411
+ for (const loader of routeLoaders) {
2412
+ if (loader.__expires !== 0) {
2413
+ continue;
2414
+ }
2415
+ const data = loaderValues[loader.__id];
2416
+ if (data === void 0) {
2417
+ continue;
2418
+ }
2419
+ const loaderFilePath = sys.getLoaderFilePath(url.pathname, loader.__id, manifestHash);
2420
+ const loaderWriter = sys.createWriteStream(loaderFilePath);
2421
+ loaderWriter.on("error", (e) => {
2413
2422
  console.error(e);
2414
2423
  result.error = {
2415
2424
  message: e.message,
2416
2425
  stack: e.stack
2417
2426
  };
2418
2427
  });
2419
- const serialized = await deps.serialize(qData);
2420
- dataWriter.write(serialized);
2428
+ const serialized = await deps.serialize({
2429
+ d: data
2430
+ });
2431
+ loaderWriter.write(serialized);
2421
2432
  writePromises.push(new Promise((resolve) => {
2422
2433
  result.filePath = routeFilePath;
2423
- dataWriter.end(resolve);
2434
+ loaderWriter.end(resolve);
2424
2435
  }));
2425
2436
  }
2426
2437
  }
@@ -2506,7 +2517,7 @@ async function workerThread(sys) {
2506
2517
  const pendingPromises = /* @__PURE__ */ new Set();
2507
2518
  const deps = {
2508
2519
  ...staticWorkerThreadDeps,
2509
- serialize: await loadSerialize()
2520
+ serialize: _serialize
2510
2521
  };
2511
2522
  process.on("uncaughtException", (e) => {
2512
2523
  console.error("Worker uncaught exception (suppressed):", e.message);