@qwik.dev/router 2.0.0-beta.2 → 2.0.0-beta.4

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.
@@ -229,8 +229,11 @@ var RewriteMessage = class extends AbortMessage {
229
229
  // packages/qwik-router/src/runtime/src/constants.ts
230
230
  var MODULE_CACHE = /* @__PURE__ */ new WeakMap();
231
231
  var QACTION_KEY = "qaction";
232
+ var QLOADER_KEY = "qloaders";
232
233
  var QFN_KEY = "qfunc";
233
234
  var QDATA_KEY = "qdata";
235
+ var Q_ROUTE = "q:route";
236
+ var DEFAULT_LOADERS_SERIALIZATION_STRATEGY = globalThis.__DEFAULT_LOADERS_SERIALIZATION_STRATEGY__ || "never";
234
237
 
235
238
  // packages/qwik-router/src/runtime/src/route-matcher.ts
236
239
  function matchRoute(route, path) {
@@ -352,6 +355,19 @@ function lastIndexOf(text, start, match, searchIdx, notFoundIdx) {
352
355
  return idx > start ? idx : notFoundIdx;
353
356
  }
354
357
 
358
+ // packages/qwik-router/src/runtime/src/utils.ts
359
+ import { createAsyncComputed$, isBrowser as isBrowser2 } from "@qwik.dev/core";
360
+ import {
361
+ _UNINITIALIZED
362
+ } from "@qwik.dev/core/internal";
363
+
364
+ // packages/qwik-router/src/runtime/src/use-endpoint.ts
365
+ import { _deserialize } from "@qwik.dev/core/internal";
366
+
367
+ // packages/qwik-router/src/runtime/src/client-navigate.ts
368
+ import { isBrowser } from "@qwik.dev/core";
369
+ import { p as preload } from "@qwik.dev/core/preloader";
370
+
355
371
  // packages/qwik-router/src/runtime/src/utils.ts
356
372
  var isPromise = (value) => {
357
373
  return value && typeof value.then === "function";
@@ -375,13 +391,13 @@ var loadRoute = async (routes, menus, cacheModules, pathname) => {
375
391
  return null;
376
392
  }
377
393
  for (const routeData of routes) {
378
- const routeName = routeData[0];
394
+ const routeName = routeData[0 /* RouteName */];
379
395
  const params = matchRoute(routeName, pathname);
380
396
  if (!params) {
381
397
  continue;
382
398
  }
383
- const loaders = routeData[1];
384
- const routeBundleNames = routeData[3];
399
+ const loaders = routeData[1 /* Loaders */];
400
+ const routeBundleNames = routeData[3 /* RouteBundleNames */];
385
401
  const modules = new Array(loaders.length);
386
402
  const pendingLoads = [];
387
403
  loaders.forEach((moduleLoader, i) => {
@@ -433,10 +449,10 @@ var getMenuLoader = (menus, pathname) => {
433
449
  if (menus) {
434
450
  pathname = pathname.endsWith("/") ? pathname : pathname + "/";
435
451
  const menu = menus.find(
436
- (m) => m[0] === pathname || pathname.startsWith(m[0] + (pathname.endsWith("/") ? "" : "/"))
452
+ (m) => m[0 /* Pathname */] === pathname || pathname.startsWith(m[0 /* Pathname */] + (pathname.endsWith("/") ? "" : "/"))
437
453
  );
438
454
  if (menu) {
439
- return menu[1];
455
+ return menu[1 /* MenuLoader */];
440
456
  }
441
457
  }
442
458
  };
@@ -616,12 +632,17 @@ var RequestEvLoaders = Symbol("RequestEvLoaders");
616
632
  var RequestEvMode = Symbol("RequestEvMode");
617
633
  var RequestEvRoute = Symbol("RequestEvRoute");
618
634
  var RequestEvQwikSerializer = Symbol("RequestEvQwikSerializer");
635
+ var RequestEvLoaderSerializationStrategyMap = Symbol(
636
+ "RequestEvLoaderSerializationStrategyMap"
637
+ );
619
638
  var RequestEvTrailingSlash = Symbol("RequestEvTrailingSlash");
620
639
  var RequestRouteName = "@routeName";
621
640
  var RequestEvSharedActionId = "@actionId";
622
641
  var RequestEvSharedActionFormData = "@actionFormData";
623
642
  var RequestEvSharedNonce = "@nonce";
624
643
  var RequestEvIsRewrite = "@rewrite";
644
+ var RequestEvShareServerTiming = "@serverTiming";
645
+ var RequestEvShareQData = "qData";
625
646
  function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, trailingSlash, basePathname, qwikSerializer, resolved) {
626
647
  const { request, platform, env } = serverRequestEv;
627
648
  const sharedMap = /* @__PURE__ */ new Map();
@@ -705,6 +726,7 @@ function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, trail
705
726
  const loaders = {};
706
727
  const requestEv = {
707
728
  [RequestEvLoaders]: loaders,
729
+ [RequestEvLoaderSerializationStrategyMap]: /* @__PURE__ */ new Map(),
708
730
  [RequestEvMode]: serverRequestEv.mode,
709
731
  [RequestEvTrailingSlash]: trailingSlash,
710
732
  get [RequestEvRoute]() {
@@ -718,7 +740,7 @@ function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, trail
718
740
  signal: request.signal,
719
741
  originalUrl: new URL(url),
720
742
  get params() {
721
- return (loadedRoute == null ? void 0 : loadedRoute[1]) ?? {};
743
+ return (loadedRoute == null ? void 0 : loadedRoute[1 /* Params */]) ?? {};
722
744
  },
723
745
  get pathname() {
724
746
  return url.pathname;
@@ -836,9 +858,12 @@ function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, trail
836
858
  getWritableStream: () => {
837
859
  if (writableStream === null) {
838
860
  if (serverRequestEv.mode === "dev") {
839
- const serverTiming = sharedMap.get("@serverTiming");
861
+ const serverTiming = sharedMap.get(RequestEvShareServerTiming);
840
862
  if (serverTiming) {
841
- headers.set("Server-Timing", serverTiming.map((a) => `${a[0]};dur=${a[1]}`).join(","));
863
+ headers.set(
864
+ "Server-Timing",
865
+ serverTiming.map(([name, duration]) => `${name};dur=${duration}`).join(",")
866
+ );
842
867
  }
843
868
  }
844
869
  writableStream = serverRequestEv.getWritableStream(
@@ -857,6 +882,9 @@ function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, trail
857
882
  function getRequestLoaders(requestEv) {
858
883
  return requestEv[RequestEvLoaders];
859
884
  }
885
+ function getRequestLoaderSerializationStrategyMap(requestEv) {
886
+ return requestEv[RequestEvLoaderSerializationStrategyMap];
887
+ }
860
888
  function getRequestTrailingSlash(requestEv) {
861
889
  return requestEv[RequestEvTrailingSlash];
862
890
  }
@@ -929,13 +957,15 @@ function getQwikRouterServerData(requestEv) {
929
957
  if (protocol) {
930
958
  reconstructedUrl.protocol = protocol;
931
959
  }
960
+ const loaders = getRequestLoaders(requestEv);
961
+ const loadersSerializationStrategy = getRequestLoaderSerializationStrategyMap(requestEv);
932
962
  return {
933
963
  url: reconstructedUrl.href,
934
964
  requestHeaders,
935
965
  locale: locale(),
936
966
  nonce,
937
967
  containerAttributes: {
938
- "q:route": routeName
968
+ [Q_ROUTE]: routeName
939
969
  },
940
970
  qwikrouter: {
941
971
  routeName,
@@ -944,7 +974,8 @@ function getQwikRouterServerData(requestEv) {
944
974
  loadedRoute: getRequestRoute(requestEv),
945
975
  response: {
946
976
  status: status(),
947
- loaders: getRequestLoaders(requestEv),
977
+ loaders,
978
+ loadersSerializationStrategy,
948
979
  action,
949
980
  formData
950
981
  }
@@ -957,7 +988,7 @@ var resolveRequestHandlers = (serverPlugins, route, method, checkOrigin, renderH
957
988
  const routeLoaders = [];
958
989
  const routeActions = [];
959
990
  const requestHandlers = [];
960
- const isPageRoute = !!(route && isLastModulePageRoute(route[2]));
991
+ const isPageRoute = !!(route && isLastModulePageRoute(route[2 /* Mods */]));
961
992
  if (serverPlugins) {
962
993
  _resolveRequestHandlers(
963
994
  routeLoaders,
@@ -969,7 +1000,7 @@ var resolveRequestHandlers = (serverPlugins, route, method, checkOrigin, renderH
969
1000
  );
970
1001
  }
971
1002
  if (route) {
972
- const routeName = route[0];
1003
+ const routeName = route[0 /* RouteName */];
973
1004
  if (checkOrigin && (method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE")) {
974
1005
  requestHandlers.unshift(csrfCheckMiddleware);
975
1006
  }
@@ -980,7 +1011,7 @@ var resolveRequestHandlers = (serverPlugins, route, method, checkOrigin, renderH
980
1011
  requestHandlers.push(fixTrailingSlash);
981
1012
  requestHandlers.push(renderQData);
982
1013
  }
983
- const routeModules = route[2];
1014
+ const routeModules = route[2 /* Mods */];
984
1015
  requestHandlers.push(handleRedirect);
985
1016
  _resolveRequestHandlers(
986
1017
  routeLoaders,
@@ -994,7 +1025,8 @@ var resolveRequestHandlers = (serverPlugins, route, method, checkOrigin, renderH
994
1025
  requestHandlers.push((ev) => {
995
1026
  ev.sharedMap.set(RequestRouteName, routeName);
996
1027
  });
997
- requestHandlers.push(actionsMiddleware(routeActions, routeLoaders));
1028
+ requestHandlers.push(actionsMiddleware(routeActions));
1029
+ requestHandlers.push(loadersMiddleware(routeLoaders));
998
1030
  requestHandlers.push(renderHandler);
999
1031
  }
1000
1032
  }
@@ -1056,8 +1088,9 @@ var _resolveRequestHandlers = (routeLoaders, routeActions, requestHandlers, rout
1056
1088
  }
1057
1089
  }
1058
1090
  };
1059
- function actionsMiddleware(routeActions, routeLoaders) {
1060
- return async (requestEv) => {
1091
+ function actionsMiddleware(routeActions) {
1092
+ return async (requestEvent) => {
1093
+ const requestEv = requestEvent;
1061
1094
  if (requestEv.headersSent) {
1062
1095
  requestEv.exit();
1063
1096
  return;
@@ -1092,7 +1125,7 @@ function actionsMiddleware(routeActions, routeLoaders) {
1092
1125
  } else {
1093
1126
  const actionResolved = isDev ? await measure(
1094
1127
  requestEv,
1095
- action.__qrl.getSymbol().split("_", 1)[0],
1128
+ action.__qrl.getHash(),
1096
1129
  () => action.__qrl.call(requestEv, result.data, requestEv)
1097
1130
  ) : await action.__qrl.call(requestEv, result.data, requestEv);
1098
1131
  if (isDev) {
@@ -1103,46 +1136,74 @@ function actionsMiddleware(routeActions, routeLoaders) {
1103
1136
  }
1104
1137
  }
1105
1138
  }
1139
+ };
1140
+ }
1141
+ function loadersMiddleware(routeLoaders) {
1142
+ return async (requestEvent) => {
1143
+ const requestEv = requestEvent;
1144
+ if (requestEv.headersSent) {
1145
+ requestEv.exit();
1146
+ return;
1147
+ }
1148
+ const loaders = getRequestLoaders(requestEv);
1149
+ const isDev = getRequestMode(requestEv) === "dev";
1150
+ const qwikSerializer = requestEv[RequestEvQwikSerializer];
1106
1151
  if (routeLoaders.length > 0) {
1107
- const resolvedLoadersPromises = routeLoaders.map((loader) => {
1108
- const loaderId = loader.__id;
1109
- loaders[loaderId] = runValidators(
1110
- requestEv,
1111
- loader.__validators,
1112
- void 0,
1113
- // data
1114
- isDev
1115
- ).then((res) => {
1116
- if (res.success) {
1117
- if (isDev) {
1118
- return measure(
1119
- requestEv,
1120
- loader.__qrl.getSymbol().split("_", 1)[0],
1121
- () => loader.__qrl.call(requestEv, requestEv)
1122
- );
1123
- } else {
1124
- return loader.__qrl.call(requestEv, requestEv);
1125
- }
1126
- } else {
1127
- return requestEv.fail(res.status ?? 500, res.error);
1152
+ let currentLoaders = [];
1153
+ if (requestEv.query.has(QLOADER_KEY)) {
1154
+ const selectedLoaderIds = requestEv.query.getAll(QLOADER_KEY);
1155
+ for (const loader of routeLoaders) {
1156
+ if (selectedLoaderIds.includes(loader.__id)) {
1157
+ currentLoaders.push(loader);
1128
1158
  }
1129
- }).then((resolvedLoader) => {
1130
- if (typeof resolvedLoader === "function") {
1131
- loaders[loaderId] = resolvedLoader();
1132
- } else {
1133
- if (isDev) {
1134
- verifySerializable(qwikSerializer, resolvedLoader, loader.__qrl);
1135
- }
1136
- loaders[loaderId] = resolvedLoader;
1137
- }
1138
- return resolvedLoader;
1139
- });
1140
- return loaders[loaderId];
1141
- });
1159
+ }
1160
+ } else {
1161
+ currentLoaders = routeLoaders;
1162
+ }
1163
+ const resolvedLoadersPromises = currentLoaders.map(
1164
+ (loader) => getRouteLoaderPromise(loader, loaders, requestEv, isDev, qwikSerializer)
1165
+ );
1142
1166
  await Promise.all(resolvedLoadersPromises);
1143
1167
  }
1144
1168
  };
1145
1169
  }
1170
+ async function getRouteLoaderPromise(loader, loaders, requestEv, isDev, qwikSerializer) {
1171
+ const loaderId = loader.__id;
1172
+ loaders[loaderId] = runValidators(
1173
+ requestEv,
1174
+ loader.__validators,
1175
+ void 0,
1176
+ // data
1177
+ isDev
1178
+ ).then((res) => {
1179
+ if (res.success) {
1180
+ if (isDev) {
1181
+ return measure(
1182
+ requestEv,
1183
+ loader.__qrl.getHash(),
1184
+ () => loader.__qrl.call(requestEv, requestEv)
1185
+ );
1186
+ } else {
1187
+ return loader.__qrl.call(requestEv, requestEv);
1188
+ }
1189
+ } else {
1190
+ return requestEv.fail(res.status ?? 500, res.error);
1191
+ }
1192
+ }).then((resolvedLoader) => {
1193
+ if (typeof resolvedLoader === "function") {
1194
+ loaders[loaderId] = resolvedLoader();
1195
+ } else {
1196
+ if (isDev) {
1197
+ verifySerializable(qwikSerializer, resolvedLoader, loader.__qrl);
1198
+ }
1199
+ loaders[loaderId] = resolvedLoader;
1200
+ }
1201
+ return resolvedLoader;
1202
+ });
1203
+ const loadersSerializationStrategy = getRequestLoaderSerializationStrategyMap(requestEv);
1204
+ loadersSerializationStrategy.set(loaderId, loader.__serializationStrategy);
1205
+ return loaders[loaderId];
1206
+ }
1146
1207
  async function runValidators(requestEv, validators, data, isDev) {
1147
1208
  let lastResult = {
1148
1209
  success: true,
@@ -1277,7 +1338,7 @@ function getPathname(url, trailingSlash) {
1277
1338
  url.pathname = url.pathname.slice(0, -1);
1278
1339
  }
1279
1340
  }
1280
- const search = url.search.slice(1).replaceAll(/&?q(action|data|func)=[^&]+/g, "");
1341
+ const search = url.search.slice(1).replaceAll(/&?q(action|data|func|loaders)=[^&]+/g, "");
1281
1342
  return `${url.pathname}${search ? `?${search}` : ""}${url.hash}`;
1282
1343
  }
1283
1344
  var encoder = /* @__PURE__ */ new TextEncoder();
@@ -1343,7 +1404,7 @@ function renderQwikMiddleware(render) {
1343
1404
  if (typeof result.html === "string") {
1344
1405
  await stream.write(result.html);
1345
1406
  }
1346
- requestEv.sharedMap.set("qData", qData);
1407
+ requestEv.sharedMap.set(RequestEvShareQData, qData);
1347
1408
  } finally {
1348
1409
  await stream.ready;
1349
1410
  await stream.close();
@@ -1368,10 +1429,10 @@ async function handleRedirect(requestEv) {
1368
1429
  return;
1369
1430
  }
1370
1431
  const status = requestEv.status();
1371
- const location = requestEv.headers.get("Location");
1372
- const isRedirect = status >= 301 && status <= 308 && location;
1432
+ const location2 = requestEv.headers.get("Location");
1433
+ const isRedirect = status >= 301 && status <= 308 && location2;
1373
1434
  if (isRedirect) {
1374
- const adaptedLocation = makeQDataPath(location);
1435
+ const adaptedLocation = makeQDataPath(location2);
1375
1436
  if (adaptedLocation) {
1376
1437
  requestEv.headers.set("Location", adaptedLocation);
1377
1438
  requestEv.getWritableStream().close();
@@ -1397,8 +1458,24 @@ async function renderQData(requestEv) {
1397
1458
  const requestHeaders = {};
1398
1459
  requestEv.request.headers.forEach((value, key) => requestHeaders[key] = value);
1399
1460
  requestEv.headers.set("Content-Type", "application/json; charset=utf-8");
1400
- const qData = {
1401
- loaders: getRequestLoaders(requestEv),
1461
+ let loaders = getRequestLoaders(requestEv);
1462
+ const selectedLoaderIds = requestEv.query.getAll(QLOADER_KEY);
1463
+ const hasCustomLoaders = selectedLoaderIds.length > 0;
1464
+ if (hasCustomLoaders) {
1465
+ const selectedLoaders = {};
1466
+ for (const loaderId of selectedLoaderIds) {
1467
+ const loader = loaders[loaderId];
1468
+ selectedLoaders[loaderId] = loader;
1469
+ }
1470
+ loaders = selectedLoaders;
1471
+ }
1472
+ const qData = hasCustomLoaders ? {
1473
+ // send minimal data to the client
1474
+ loaders,
1475
+ status: status !== 200 ? status : 200,
1476
+ href: getPathname(requestEv.url, trailingSlash)
1477
+ } : {
1478
+ loaders,
1402
1479
  action: requestEv.sharedMap.get(RequestEvSharedActionId),
1403
1480
  status: status !== 200 ? status : 200,
1404
1481
  href: getPathname(requestEv.url, trailingSlash),
@@ -1409,7 +1486,7 @@ async function renderQData(requestEv) {
1409
1486
  const qwikSerializer = requestEv[RequestEvQwikSerializer];
1410
1487
  const data = await qwikSerializer._serialize([qData]);
1411
1488
  writer.write(encoder.encode(data));
1412
- requestEv.sharedMap.set("qData", qData);
1489
+ requestEv.sharedMap.set(RequestEvShareQData, qData);
1413
1490
  writer.close();
1414
1491
  }
1415
1492
  function makeQDataPath(href) {
@@ -1431,9 +1508,9 @@ async function measure(requestEv, name, fn) {
1431
1508
  return await fn();
1432
1509
  } finally {
1433
1510
  const duration = now() - start;
1434
- let measurements = requestEv.sharedMap.get("@serverTiming");
1511
+ let measurements = requestEv.sharedMap.get(RequestEvShareServerTiming);
1435
1512
  if (!measurements) {
1436
- requestEv.sharedMap.set("@serverTiming", measurements = []);
1513
+ requestEv.sharedMap.set(RequestEvShareServerTiming, measurements = []);
1437
1514
  }
1438
1515
  measurements.push([name, duration]);
1439
1516
  }
@@ -1561,6 +1638,7 @@ var _TextEncoderStream_polyfill = class {
1561
1638
  export {
1562
1639
  AbortMessage,
1563
1640
  RedirectMessage,
1641
+ RequestEvShareQData,
1564
1642
  RewriteMessage,
1565
1643
  ServerError,
1566
1644
  _TextEncoderStream_polyfill,
@@ -232,7 +232,7 @@ async function workerRender(sys, opts, staticRoute, pendingPromises, callback) {
232
232
  const writePromises = [];
233
233
  try {
234
234
  if (writeQDataEnabled) {
235
- const qData = requestEv.sharedMap.get("qData");
235
+ const qData = requestEv.sharedMap.get(import_request_handler.RequestEvShareQData);
236
236
  if (qData && !is404ErrorPage) {
237
237
  const qDataFilePath = sys.getDataFilePath(url.pathname);
238
238
  const dataWriter = sys.createWriteStream(qDataFilePath);
@@ -837,7 +837,9 @@ async function generateNotFoundPages(sys, opts, routes) {
837
837
  if (opts.emit404Pages !== false) {
838
838
  const basePathname = opts.basePathname || "/";
839
839
  const rootNotFoundPathname = basePathname + "404.html";
840
- const hasRootNotFound = routes.some((r) => r[2] === rootNotFoundPathname);
840
+ const hasRootNotFound = routes.some(
841
+ (r) => r[2 /* OriginalPathname */] === rootNotFoundPathname
842
+ );
841
843
  if (!hasRootNotFound) {
842
844
  const filePath = sys.getRouteFilePath(rootNotFoundPathname, true);
843
845
  const html = (0, import_request_handler2.getErrorHtml)(404, "Resource Not Found");
@@ -59,7 +59,7 @@ function normalizePathSlash(path) {
59
59
 
60
60
  // packages/qwik-router/src/static/worker-thread.ts
61
61
  import { _deserialize, _serialize, _verifySerializable } from "@qwik.dev/core/internal";
62
- import { requestHandler } from "../middleware/request-handler/index.mjs";
62
+ import { requestHandler, RequestEvShareQData } from "../middleware/request-handler/index.mjs";
63
63
  import { WritableStream } from "node:stream/web";
64
64
  import { pathToFileURL } from "node:url";
65
65
  async function workerThread(sys) {
@@ -196,7 +196,7 @@ async function workerRender(sys, opts, staticRoute, pendingPromises, callback) {
196
196
  const writePromises = [];
197
197
  try {
198
198
  if (writeQDataEnabled) {
199
- const qData = requestEv.sharedMap.get("qData");
199
+ const qData = requestEv.sharedMap.get(RequestEvShareQData);
200
200
  if (qData && !is404ErrorPage) {
201
201
  const qDataFilePath = sys.getDataFilePath(url.pathname);
202
202
  const dataWriter = sys.createWriteStream(qDataFilePath);
@@ -800,7 +800,9 @@ async function generateNotFoundPages(sys, opts, routes) {
800
800
  if (opts.emit404Pages !== false) {
801
801
  const basePathname = opts.basePathname || "/";
802
802
  const rootNotFoundPathname = basePathname + "404.html";
803
- const hasRootNotFound = routes.some((r) => r[2] === rootNotFoundPathname);
803
+ const hasRootNotFound = routes.some(
804
+ (r) => r[2 /* OriginalPathname */] === rootNotFoundPathname
805
+ );
804
806
  if (!hasRootNotFound) {
805
807
  const filePath = sys.getRouteFilePath(rootNotFoundPathname, true);
806
808
  const html = getErrorHtml(404, "Resource Not Found");