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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/lib/adapters/azure-swa/vite/index.mjs +2 -1
  2. package/lib/adapters/bun-server/vite/index.mjs +2 -1
  3. package/lib/adapters/cloud-run/vite/index.mjs +2 -1
  4. package/lib/adapters/cloudflare-pages/vite/index.mjs +2 -1
  5. package/lib/adapters/deno-server/vite/index.mjs +2 -1
  6. package/lib/adapters/netlify-edge/vite/index.mjs +2 -1
  7. package/lib/adapters/node-server/vite/index.mjs +2 -1
  8. package/lib/adapters/shared/vite/index.mjs +35 -31
  9. package/lib/adapters/ssg/vite/index.mjs +4 -2
  10. package/lib/adapters/vercel-edge/vite/index.mjs +18 -15
  11. package/lib/chunks/fs.mjs +26 -23
  12. package/lib/chunks/head.qwik.mjs +611 -445
  13. package/lib/chunks/http-error.qwik.mjs +8 -7
  14. package/lib/chunks/pathname.mjs +9 -8
  15. package/lib/chunks/request-path.mjs +8 -4
  16. package/lib/chunks/routes.mjs +14 -14
  17. package/lib/chunks/server-error.mjs +11 -5
  18. package/lib/chunks/{url.mjs → static-file.mjs} +12 -2
  19. package/lib/chunks/system.mjs +18 -17
  20. package/lib/chunks/user-response.mjs +419 -323
  21. package/lib/chunks/worker-thread.mjs +87 -86
  22. package/lib/index.d.ts +47 -59
  23. package/lib/index.qwik.mjs +550 -496
  24. package/lib/middleware/aws-lambda/index.mjs +6 -3
  25. package/lib/middleware/azure-swa/index.mjs +4 -2
  26. package/lib/middleware/bun/index.mjs +12 -12
  27. package/lib/middleware/cloudflare-pages/index.mjs +19 -4
  28. package/lib/middleware/deno/index.mjs +12 -12
  29. package/lib/middleware/firebase/index.mjs +4 -2
  30. package/lib/middleware/netlify-edge/index.mjs +4 -2
  31. package/lib/middleware/node/index.mjs +16 -15
  32. package/lib/middleware/request-handler/index.mjs +35 -24
  33. package/lib/middleware/vercel-edge/index.mjs +4 -2
  34. package/lib/service-worker/index.mjs +2 -1
  35. package/lib/ssg/index.mjs +20 -16
  36. package/lib/vite/index.d.ts +1 -1
  37. package/lib/vite/index.mjs +596 -586
  38. package/package.json +5 -5
@@ -1,7 +1,6 @@
1
+ import { $ as basePathname, B as DocumentHeadContext, C as getRequestEvent, F as routeLoaderQrl, G as RouteNavigateContext, H as RouteActionContext, I as setLoaderSignalValue, J as QACTION_KEY, K as RoutePreventNavigateContext, N as restoreRouteLoaders, O as isImmutableLoader, P as routeLoader$, R as ContentContext, S as getModuleRouteLoaders, U as RouteLoaderCtxContext, V as HttpStatusContext, W as RouteLocationContext, X as QFN_KEY, Y as QDATA_KEY, Z as Q_ROUTE, _ as commitRouteLoaders, a as isPromise, b as getClientRouteLoaders, c as shouldPreload, d as loadRoute, h as clearNavFetchCache, i as getClientNavPath, j as prepareRouteLoaders, l as toPath, m as abortRouteLoaderNavigation, n as resolveHead, o as isSameOrigin, q as RouteStateContext, s as isSamePath, t as createDocumentHead, u as toUrl, v as ensureRouteLoaderSignals, y as fetchRouteLoaderData, z as ContentInternalContext } from "./chunks/head.qwik.mjs";
1
2
  import { t as ensureSlash } from "./chunks/pathname.mjs";
2
- import { A as routeLoader$, B as RouteLoaderCtxContext, E as isImmutableLoader, F as ContentContext, G as QACTION_KEY, H as RouteNavigateContext, I as ContentInternalContext, J as Q_ROUTE, K as QDATA_KEY, L as DocumentHeadContext, M as setLoaderSignalValue, P as updateRouteLoaderPaths, R as HttpStatusContext, T as invalidateNavRouteLoaders, U as RoutePreventNavigateContext, V as RouteLocationContext, W as RouteStateContext, _ as fetchRouteLoaderData, a as isPromise, b as getRequestEvent, c as shouldPreload, d as loadRoute, g as ensureRouteLoaderSignals, i as getClientNavPath, j as routeLoaderQrl, l as toPath, m as clearNavFetchCache, n as resolveHead, o as isSameOrigin, q as QFN_KEY, s as isSamePath, t as createDocumentHead, u as toUrl, y as getModuleRouteLoaders, z as RouteActionContext } from "./chunks/head.qwik.mjs";
3
3
  import { SkipRender, Slot, _captures, _fnSignal, _getConstProps, _getVarProps, _jsxSorted, _jsxSplit, _qrlSync, _restProps, _wrapProp, componentQrl, createComputedQrl, createElement, eventQrl, getLocale, implicit$FirstArg, inlinedQrl, isBrowser, isDev, isServer, noSerialize, untrack, useContext, useContextProvider, useServerData, useSignal, useStore, useTaskQrl, useVisibleTaskQrl, withLocale } from "@qwik.dev/core";
4
- import * as qwikRouterConfig from "@qwik-router-config";
5
4
  import { p } from "@qwik.dev/core/preloader";
6
5
  import { _deserialize, _getContextContainer, _getContextHostElement, _hasStoreEffects, _serialize, _waitUntilRendered, forceStoreEffects } from "@qwik.dev/core/internal";
7
6
  import { Fragment } from "@qwik.dev/core/jsx-runtime";
@@ -11,26 +10,6 @@ import { z } from "zod";
11
10
  import swRegister from "@qwik-router-sw-register";
12
11
  import { renderToStream } from "@qwik.dev/core/server";
13
12
  /**
14
- * Prefetch a route's loader data in parallel (fire-and-forget). The per-nav fetch map dedupes
15
- * repeat hovers and shares the promise with the navigation's own fetch; the browser HTTP cache
16
- * handles freshness. Immutable loaders are skipped: they download lazily on first actual read and
17
- * are then browser-cached for the life of the deploy.
18
- */ var prefetchLoaderData = (loadedRoute, url, manifestHash) => {
19
- if (!loadedRoute.$loaders$?.length || !loadedRoute.$loaderPaths$) return;
20
- const immutableIds = new Set(getModuleRouteLoaders(loadedRoute.$mods$).filter((loader) => loader.__cacheControl === "immutable").map((loader) => loader.__id));
21
- const basePath = qwikRouterConfig.basePathname ?? "/";
22
- for (const hash of loadedRoute.$loaders$) {
23
- if (immutableIds.has(hash)) continue;
24
- let loaderPath = loadedRoute.$loaderPaths$[hash];
25
- if (!loaderPath) continue;
26
- if (basePath !== "/" && !loaderPath.startsWith(basePath)) loaderPath = basePath + loaderPath.slice(1);
27
- fetchRouteLoaderData(hash, loaderPath, manifestHash, {
28
- pageUrl: url,
29
- basePath
30
- }).catch(() => {});
31
- }
32
- };
33
- /**
34
13
  * Prefetch a route's JS bundles and optionally its loader data.
35
14
  *
36
15
  * Resolves the route from the trie to get the routeName (for the bundle graph preloader) and
@@ -42,9 +21,11 @@ import { renderToStream } from "@qwik.dev/core/server";
42
21
  * @param probability - Bundle preload probability (0-1, default 0.8)
43
22
  * @param manifestHash - Build manifest hash for loader URLs (from `useDocumentHead().manifestHash`)
44
23
  * @param prefetchBundle - Whether to prefetch route JS bundles
45
- */ async function prefetchRoute(url, prefetchData, probability = .8, manifestHash, prefetchBundle = true) {
24
+ */
25
+ async function prefetchRoute(url, prefetchData, probability = .8, manifestHash, prefetchBundle = true) {
46
26
  if (!isBrowser || isDev) return;
47
27
  try {
28
+ const qwikRouterConfig = await import("@qwik-router-config");
48
29
  const loadedRoute = await loadRoute(qwikRouterConfig.routes, qwikRouterConfig.cacheModules, url.pathname);
49
30
  if (!loadedRoute) return;
50
31
  if (prefetchBundle) {
@@ -57,16 +38,43 @@ import { renderToStream } from "@qwik.dev/core/server";
57
38
  prefetchLoaderData(loadedRoute, url, manifestHash);
58
39
  } catch {}
59
40
  }
60
- /** @public */ var useHttpStatus = () => useContext(HttpStatusContext).value;
61
- /** @public */ var useContent = () => useContext(ContentContext);
41
+ /**
42
+ * Prefetch a route's loader data in parallel (fire-and-forget). The per-nav fetch map dedupes
43
+ * repeat hovers and shares the promise with the navigation's own fetch; the browser HTTP cache
44
+ * handles freshness. Immutable loaders are skipped: they download lazily on first actual read and
45
+ * are then browser-cached for the life of the deploy.
46
+ */
47
+ var prefetchLoaderData = (loadedRoute, url, manifestHash) => {
48
+ if (!loadedRoute.$loaders$?.length || !loadedRoute.$loaderPaths$) return;
49
+ const immutableIds = new Set(getModuleRouteLoaders(loadedRoute.$mods$).filter((loader) => loader.__cacheControl === "immutable").map((loader) => loader.__id));
50
+ const basePath = basePathname;
51
+ for (const hash of loadedRoute.$loaders$) {
52
+ if (immutableIds.has(hash)) continue;
53
+ let loaderPath = loadedRoute.$loaderPaths$[hash];
54
+ if (!loaderPath) continue;
55
+ if (basePath !== "/" && !loaderPath.startsWith(basePath)) loaderPath = basePath + loaderPath.slice(1);
56
+ fetchRouteLoaderData(hash, loaderPath, manifestHash, {
57
+ pageUrl: url,
58
+ basePath
59
+ }).catch(() => {});
60
+ }
61
+ };
62
+ /** @public */
63
+ var useHttpStatus = () => useContext(HttpStatusContext).value;
64
+ /** @public */
65
+ var useContent = () => useContext(ContentContext);
62
66
  /**
63
67
  * Returns the document head for the current page. The generic type describes the front matter.
64
68
  *
65
69
  * @public
66
- */ var useDocumentHead = () => useContext(DocumentHeadContext);
67
- /** @public */ var useLocation = () => useContext(RouteLocationContext);
68
- /** @public */ var useNavigate = () => useContext(RouteNavigateContext);
69
- /** @internal Implementation of usePreventNavigate$ */ var usePreventNavigateQrl = (fn) => {
70
+ */
71
+ var useDocumentHead = () => useContext(DocumentHeadContext);
72
+ /** @public */
73
+ var useLocation = () => useContext(RouteLocationContext);
74
+ /** @public */
75
+ var useNavigate = () => useContext(RouteNavigateContext);
76
+ /** @internal Implementation of usePreventNavigate$ */
77
+ var usePreventNavigateQrl = (fn) => {
70
78
  const registerPreventNav = useContext(RoutePreventNavigateContext);
71
79
  useVisibleTaskQrl(/*#__PURE__*/ inlinedQrl(() => {
72
80
  const fn = _captures[0], registerPreventNav = _captures[1];
@@ -106,20 +114,21 @@ import { renderToStream } from "@qwik.dev/core/server";
106
114
  * you can show a dialog or just return `true` to prevent the navigation.
107
115
  *
108
116
  * @public
109
- */ var usePreventNavigate$ = implicit$FirstArg(usePreventNavigateQrl);
117
+ */
118
+ var usePreventNavigate$ = implicit$FirstArg(usePreventNavigateQrl);
110
119
  var useAction = () => useContext(RouteActionContext);
111
120
  var useQwikRouterEnv = () => noSerialize(useServerData("qwikrouter"));
112
- /** @public */ var Link = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((props) => {
121
+ /** @public */
122
+ var Link = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((props) => {
113
123
  const nav = useNavigate();
114
124
  const loc = useLocation();
115
125
  const head = useDocumentHead();
116
- const originalHref = props.href;
117
126
  const { onClick$, prefetch: prefetchProp, reload, replaceState, scroll, prefetchBundles: prefetchBundlesProp = "visible", prefetchData: prefetchDataProp = prefetchProp === "js" ? "off" : "intent", ...linkProps } = props;
118
127
  const clientNavPath = untrack(getClientNavPath, {
119
128
  ...linkProps,
120
129
  reload
121
130
  }, loc);
122
- linkProps.href = clientNavPath || originalHref;
131
+ linkProps.href = clientNavPath || props.href;
123
132
  const isDepratedPrefetchDisabled = prefetchProp === false;
124
133
  const shouldPrefetch = untrack(shouldPreload, clientNavPath, loc);
125
134
  const shouldVisiblePrefetchBundles = !!clientNavPath && shouldPrefetch && !isDepratedPrefetchDisabled && (prefetchBundlesProp === "visible" || prefetchProp === "js" || prefetchProp === true);
@@ -160,17 +169,16 @@ var useQwikRouterEnv = () => noSerialize(useServerData("qwikrouter"));
160
169
  replaceState,
161
170
  scroll
162
171
  ]) : null;
163
- const handlePreload = /*#__PURE__*/ inlinedQrl((_, elm) => {
164
- prefetchRoute(new URL(elm.href), false, 1);
165
- }, "Link_component_handlePreload_AAemwtuBjsE");
166
172
  return /*#__PURE__*/ _jsxSplit("a", {
167
- "q:link": !!clientNavPath,
168
- ..._getVarProps(linkProps),
169
- ..._getConstProps(linkProps),
173
+ ..._getVarProps({ "q:link": !!clientNavPath }),
174
+ ..._getConstProps({ "q:link": !!clientNavPath }),
175
+ ...linkProps,
170
176
  "data-q-prefetch": shouldVisiblePrefetchBundles && shouldVisiblePrefetchData ? "bd" : shouldVisiblePrefetchBundles ? "b" : shouldVisiblePrefetchData ? "d" : null,
171
177
  "q-e:click": [
172
178
  preventDefault,
173
- handlePreload,
179
+ /*#__PURE__*/ inlinedQrl((_, elm) => {
180
+ prefetchRoute(new URL(elm.href), false, 1);
181
+ }, "Link_component_handlePreload_AAemwtuBjsE"),
174
182
  onClick$,
175
183
  handleClientSideNavigation
176
184
  ],
@@ -178,16 +186,8 @@ var useQwikRouterEnv = () => noSerialize(useServerData("qwikrouter"));
178
186
  "q-e:focus": [linkProps.onFocus$, prefetchDataProp === "intent" ? prefetchData : null],
179
187
  "q-e:pointerdown": [linkProps.onPointerDown$, prefetchDataProp === "commit" ? prefetchData : null],
180
188
  "q-e:keydown": [linkProps.onKeyDown$, prefetchDataProp === "commit" ? onEnterKeyDown : null]
181
- }, null, /*#__PURE__*/ _jsxSorted(Slot, null, null, null, 3, "jO_0"), 0, "jO_1");
189
+ }, null, /*#__PURE__*/ _jsxSorted(Slot, null, null, null, 3, "xa_0"), 0, "xa_1");
182
190
  }, "Link_component_VPmar9tb3t4"));
183
- var newScrollState = () => {
184
- return {
185
- x: 0,
186
- y: 0,
187
- w: 0,
188
- h: 0
189
- };
190
- };
191
191
  var clientNavigate = (win, navType, fromURL, toURL, replaceState = false) => {
192
192
  if (navType !== "popstate") {
193
193
  const samePath = isSamePath(fromURL, toURL);
@@ -199,6 +199,14 @@ var clientNavigate = (win, navType, fromURL, toURL, replaceState = false) => {
199
199
  }
200
200
  }
201
201
  };
202
+ var newScrollState = () => {
203
+ return {
204
+ x: 0,
205
+ y: 0,
206
+ w: 0,
207
+ h: 0
208
+ };
209
+ };
202
210
  var preloadRouteBundles = (path, probability = .8) => {
203
211
  if (isBrowser) {
204
212
  path = ensureSlash(path);
@@ -211,6 +219,14 @@ var cleanupPrefetchObserver;
211
219
  var resetLinkPrefetchState = () => {
212
220
  prefetchedLinks = /* @__PURE__ */ new WeakSet();
213
221
  };
222
+ var refreshLinkPrefetchObserver = (manifestHash) => {
223
+ resetLinkPrefetchState();
224
+ cleanupPrefetchObserver?.();
225
+ cleanupPrefetchObserver = createLinkPrefetchObserver(manifestHash);
226
+ };
227
+ var linkPrefetchInit = eventQrl(/*#__PURE__*/ inlinedQrl((_, element) => {
228
+ refreshLinkPrefetchObserver(element.closest("[q\\:manifest-hash]")?.getAttribute("q:manifest-hash") || void 0);
229
+ }, "linkPrefetchInit_event_hoZr9O26Irg"));
214
230
  var createLinkPrefetchObserver = (manifestHash) => {
215
231
  const anchors = document.querySelectorAll("a[q\\:link][data-q-prefetch]");
216
232
  const prefetchAnchor = (anchor, observer) => {
@@ -242,20 +258,18 @@ var createLinkPrefetchObserver = (manifestHash) => {
242
258
  observer.disconnect();
243
259
  };
244
260
  };
245
- var refreshLinkPrefetchObserver = (manifestHash) => {
246
- resetLinkPrefetchState();
247
- cleanupPrefetchObserver?.();
248
- cleanupPrefetchObserver = createLinkPrefetchObserver(manifestHash);
249
- };
250
- var linkPrefetchInit = eventQrl(/*#__PURE__*/ inlinedQrl((_, element) => {
251
- refreshLinkPrefetchObserver(element.closest("[q\\:manifest-hash]")?.getAttribute("q:manifest-hash") || void 0);
252
- }, "linkPrefetchInit_event_hoZr9O26Irg"));
253
261
  function callRestoreScrollOnDocument() {
254
262
  if (document.__q_scroll_restore__) {
255
263
  document.__q_scroll_restore__();
256
264
  document.__q_scroll_restore__ = void 0;
257
265
  }
258
266
  }
267
+ var restoreScroll = (type, toUrl, fromUrl, scroller, scrollState) => {
268
+ if (type === "popstate" && scrollState) scroller.scrollTo(scrollState.x, scrollState.y);
269
+ else if (type === "link" || type === "form") {
270
+ if (!hashScroll(toUrl, fromUrl)) scroller.scrollTo(0, 0);
271
+ }
272
+ };
259
273
  var hashScroll = (toUrl, fromUrl) => {
260
274
  const elmId = toUrl.hash.slice(1);
261
275
  const elm = elmId && document.getElementById(elmId);
@@ -265,12 +279,6 @@ var hashScroll = (toUrl, fromUrl) => {
265
279
  } else if (!elm && toUrl.hash && isSamePath(toUrl, fromUrl)) return true;
266
280
  return false;
267
281
  };
268
- var restoreScroll = (type, toUrl, fromUrl, scroller, scrollState) => {
269
- if (type === "popstate" && scrollState) scroller.scrollTo(scrollState.x, scrollState.y);
270
- else if (type === "link" || type === "form") {
271
- if (!hashScroll(toUrl, fromUrl)) scroller.scrollTo(0, 0);
272
- }
273
- };
274
282
  var currentScrollState = (elm) => {
275
283
  return {
276
284
  x: elm.scrollLeft,
@@ -411,7 +419,8 @@ var spa_init_default = eventQrl(/*#__PURE__*/ inlinedQrl((_, el) => {
411
419
  * POSTs to the route URL with `qaction={actionId}` appended and `Accept: application/json`. The
412
420
  * server runs the action and returns the action result, optionally with the loader hashes that
413
421
  * should be invalidated.
414
- */ async function submitAction(action, routeUrl) {
422
+ */
423
+ async function submitAction(action, routeUrl) {
415
424
  const pathBase = ensureSlash(routeUrl.pathname);
416
425
  const searchParams = new URLSearchParams(routeUrl.search);
417
426
  searchParams.set(QACTION_KEY, action.id);
@@ -433,15 +442,11 @@ var spa_init_default = eventQrl(/*#__PURE__*/ inlinedQrl((_, el) => {
433
442
  }
434
443
  };
435
444
  const response = await fetch(url, fetchOptions);
436
- if (response.redirected) {
437
- const redirectedURL = new URL(response.url);
438
- if (redirectedURL.origin !== location.origin) {
439
- location.href = redirectedURL.href;
440
- return;
441
- }
442
- location.href = redirectedURL.href;
443
- return;
444
- }
445
+ if (response.redirected) return {
446
+ status: response.status,
447
+ result: void 0,
448
+ redirect: new URL(response.url)
449
+ };
445
450
  if ((response.headers.get("content-type") || "").includes("json")) {
446
451
  const text = await response.text();
447
452
  const data = await _deserialize(text);
@@ -454,9 +459,11 @@ var spa_init_default = eventQrl(/*#__PURE__*/ inlinedQrl((_, el) => {
454
459
  }
455
460
  }
456
461
  var qwik_view_transition_default = "@layer qwik{@supports selector(html:active-view-transition-type(type)){html:active-view-transition-type(qwik-navigation){& :root{view-transition-name:none}}}@supports not selector(html:active-view-transition-type(type)){:root{view-transition-name:none}}}";
457
- /** View transitions are opt-in and require browser support. */ var shouldStartViewTransition = (viewTransition) => viewTransition === true && "startViewTransition" in document;
462
+ /** View transitions are opt-in and require browser support. */
463
+ var shouldStartViewTransition = (viewTransition) => viewTransition === true && "startViewTransition" in document;
458
464
  var VIEW_TRANSITION_STYLE_ID = "qwik-view-transition";
459
- /** Inject the view-transition stylesheet once, only when a transition actually runs. */ var ensureViewTransitionStyles = () => {
465
+ /** Inject the view-transition stylesheet once, only when a transition actually runs. */
466
+ var ensureViewTransitionStyles = () => {
460
467
  if (document.getElementById(VIEW_TRANSITION_STYLE_ID)) return;
461
468
  const style = document.createElement("style");
462
469
  style.id = VIEW_TRANSITION_STYLE_ID;
@@ -481,44 +488,17 @@ var startViewTransition = (params) => {
481
488
  } else return { ready: params.update() };
482
489
  };
483
490
  /**
484
- * Qwik Router Component
485
- *
486
- * This file contains the main Qwik Router component, which initializes the router and provides the
487
- * necessary context for it to work. It also contains the logic for handling navigation, including
488
- * updating the URL, managing scroll restoration, and resolving document head changes.
489
- *
490
- * Note: This component is designed to work both on the server and the client. During server-side
491
- * rendering (SSR), it initializes the router state based on the URL and route data provided by the
492
- * server environment. On the client, it handles navigation events and updates the router state
493
- * accordingly.
494
- *
495
- * SSR is _required_ for the initial load.
496
- *
497
- * The flow of navigation is as follows:
498
- *
499
- * 1. During SSR, the server environment parses the initial URL and collects the route data.
500
- * 2. It runs the middleware hooks (`onRequest`, `onGet`, `onPost`, etc.), and responds to q-loader,
501
- * action$ and server$ requests.
502
- * 3. If SSR is deemed appropriate, the route data is provided via `useServerData` and this component
503
- * uses it to initialize the router contexts, register Tasks, and render the Slot. This component
504
- * will never render again.
505
- * 4. Then, slotted components like `<DocumentHeadTags />` and `<RouterOutlet />` can consume the route
506
- * data to render the page.
507
- * 5. On the client, when a navigation event occurs, this calls `goto()`, which updates the URL in the
508
- * same tick, loads the route data and adjusts the router context.
509
- * 6. The changed contexts trigger the slotted components to re-render with the new route data.
510
- *
511
- * Since the head data can depend on route loaders and they get their data asynchronously and can
512
- * update without navigation, the head is resolved in a separate Task that tracks the relevant
513
- * signals.
514
- */
515
- /**
516
491
  * @deprecated Use `QWIK_ROUTER_SCROLLER` instead (will be removed in V3)
517
492
  * @public
518
- */ var QWIK_CITY_SCROLLER = "_qCityScroller";
519
- /** @public */ var QWIK_ROUTER_SCROLLER = "_qRouterScroller";
493
+ */
494
+ var QWIK_CITY_SCROLLER = "_qCityScroller";
495
+ /** @public */
496
+ var QWIK_ROUTER_SCROLLER = "_qRouterScroller";
520
497
  var preventNav = {};
521
- var internalState = { navCount: 0 };
498
+ var internalState = {
499
+ navCount: 0,
500
+ attemptCount: 0
501
+ };
522
502
  var getScroller = () => {
523
503
  let scroller = document.getElementById(QWIK_ROUTER_SCROLLER);
524
504
  if (!scroller) {
@@ -527,154 +507,13 @@ var getScroller = () => {
527
507
  }
528
508
  return scroller ?? document.documentElement;
529
509
  };
530
- /** @public */ var useQwikMockRouter = (props) => {
531
- const urlEnv = props.url ?? "http://localhost/";
532
- const url = new URL(urlEnv);
533
- const routeLocation = useStore({
534
- url,
535
- params: props.params ?? {},
536
- isNavigating: false,
537
- prevUrl: void 0
538
- }, { deep: false });
539
- const loadersData = props.loaders?.reduce((acc, { loader, data }) => {
540
- acc[loader.__id] = data;
541
- return acc;
542
- }, {});
543
- const loadersState = useStore({}, { deep: false });
544
- for (const [loaderId, data] of Object.entries(loadersData ?? {})) loadersState[loaderId] ||= createComputedQrl(/*#__PURE__*/ inlinedQrl(() => {
545
- return _captures[0];
546
- }, "useQwikMockRouter_createComputed_JL6bChMwwyc", [data]));
547
- const goto = props.goto ?? /*#__PURE__*/ inlinedQrl(async () => {
548
- console.warn("QwikRouterMockProvider: goto not provided");
549
- }, "useQwikMockRouter_goto_aViHFxQ1a3s");
550
- const documentHead = useStore(createDocumentHead, { deep: false });
551
- const content = useStore({
552
- headings: void 0,
553
- menu: void 0
554
- }, { deep: false });
555
- const contentInternal = useSignal();
556
- const actionState = useSignal();
557
- const httpStatus = useSignal({
558
- status: 200,
559
- message: ""
560
- });
561
- useContextProvider(ContentContext, content);
562
- useContextProvider(ContentInternalContext, contentInternal);
563
- useContextProvider(DocumentHeadContext, documentHead);
564
- useContextProvider(HttpStatusContext, httpStatus);
565
- useContextProvider(RouteLocationContext, routeLocation);
566
- useContextProvider(RouteNavigateContext, goto);
567
- useContextProvider(RouteStateContext, loadersState);
568
- useContextProvider(RouteActionContext, actionState);
569
- const actionsMocks = props.actions?.reduce((acc, { action, handler }) => {
570
- acc[action.__id] = handler;
571
- return acc;
572
- }, {});
573
- useTaskQrl(/*#__PURE__*/ inlinedQrl(async ({ track }) => {
574
- const actionState = _captures[0], actionsMocks = _captures[1];
575
- const action = track(actionState);
576
- if (!action?.resolve) return;
577
- const mock = actionsMocks?.[action.id];
578
- if (mock) {
579
- const actionResult = await mock(action.data);
580
- action.resolve(actionResult);
581
- }
582
- }, "useQwikMockRouter_useTask_tXTLR4tzCy0", [actionState, actionsMocks]));
583
- };
584
- /** @public */ var QwikRouterMockProvider = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((props) => {
585
- useQwikMockRouter(props);
586
- return /*#__PURE__*/ _jsxSorted(Slot, null, null, null, 3, "5y_1");
587
- }, "QwikRouterMockProvider_component_IN4dVpT0x74"));
588
- /**
589
- * @deprecated Use `useQwikMockRouter()` instead. Will be removed in V3
590
- * @public
591
- */ var QwikCityMockProvider = QwikRouterMockProvider;
592
- function initializeSPA(goto, scroller) {
593
- if (!window._qRouterSPA) {
594
- window._qRouterSPA = true;
595
- history.scrollRestoration = "manual";
596
- window.addEventListener("popstate", () => {
597
- window._qRouterScrollEnabled = false;
598
- clearTimeout(window._qRouterScrollDebounce);
599
- goto(location.href, { type: "popstate" });
600
- });
601
- window.removeEventListener("popstate", window._qRouterInitPopstate);
602
- window._qRouterInitPopstate = void 0;
603
- if (!window._qRouterHistoryPatch) {
604
- window._qRouterHistoryPatch = true;
605
- const pushState = history.pushState;
606
- const replaceState = history.replaceState;
607
- const prepareState = (state) => {
608
- if (state === null || typeof state === "undefined") state = {};
609
- else if (state?.constructor !== Object) {
610
- state = { _data: state };
611
- if (isDev) console.warn("In a Qwik SPA context, `history.state` is used to store scroll state. Direct calls to `pushState()` and `replaceState()` must supply an actual Object type. We need to be able to automatically attach the scroll state to your state object. A new state object has been created, your data has been moved to: `history.state._data`");
612
- }
613
- state._qRouterScroll = state._qRouterScroll || currentScrollState(scroller);
614
- return state;
615
- };
616
- history.pushState = (state, title, url) => {
617
- state = prepareState(state);
618
- return pushState.call(history, state, title, url);
619
- };
620
- history.replaceState = (state, title, url) => {
621
- state = prepareState(state);
622
- return replaceState.call(history, state, title, url);
623
- };
624
- }
625
- document.addEventListener("click", (event) => {
626
- if (event.defaultPrevented) return;
627
- const target = event.target.closest("a[href]");
628
- if (target && !target.hasAttribute("preventdefault:click")) {
629
- const href = target.getAttribute("href");
630
- const prev = new URL(location.href);
631
- const dest = new URL(href, prev);
632
- if (isSameOrigin(dest, prev) && isSamePath(dest, prev)) {
633
- event.preventDefault();
634
- if (!dest.hash && !dest.href.endsWith("#")) {
635
- if (dest.href !== prev.href) history.pushState(null, "", dest);
636
- window._qRouterScrollEnabled = false;
637
- clearTimeout(window._qRouterScrollDebounce);
638
- saveScrollHistory({
639
- ...currentScrollState(scroller),
640
- x: 0,
641
- y: 0
642
- });
643
- location.reload();
644
- return;
645
- }
646
- goto(target.getAttribute("href"));
647
- }
648
- }
649
- });
650
- document.removeEventListener("click", window._qRouterInitAnchors);
651
- window._qRouterInitAnchors = void 0;
652
- if (!window.navigation) {
653
- document.addEventListener("visibilitychange", () => {
654
- if (window._qRouterScrollEnabled && document.visibilityState === "hidden") saveScrollHistory(currentScrollState(scroller));
655
- }, { passive: true });
656
- document.removeEventListener("visibilitychange", window._qRouterInitVisibility);
657
- window._qRouterInitVisibility = void 0;
658
- }
659
- window.addEventListener("scroll", () => {
660
- if (!window._qRouterScrollEnabled) return;
661
- clearTimeout(window._qRouterScrollDebounce);
662
- window._qRouterScrollDebounce = setTimeout(() => {
663
- saveScrollHistory(currentScrollState(scroller));
664
- window._qRouterScrollDebounce = void 0;
665
- }, 200);
666
- }, { passive: true });
667
- removeEventListener("scroll", window._qRouterInitScroll);
668
- window._qRouterInitScroll = void 0;
669
- spa_init_default.resolve();
670
- }
671
- }
672
510
  /**
673
511
  * @public
674
512
  * This hook initializes Qwik Router, providing the necessary context for it to work.
675
513
  *
676
514
  * This hook should be used once, at the root of your application.
677
- */ var useQwikRouter = (props) => {
515
+ */
516
+ var useQwikRouter = (props) => {
678
517
  if (!isServer) throw new Error("useQwikRouter can only run during SSR on the server. If you are seeing this, it means you are re-rendering the root of your application. Fix that or use the <QwikRouterProvider> component around the root of your application.");
679
518
  const env = useQwikRouterEnv();
680
519
  if (!env?.params) throw new Error(`Missing Qwik Router Env Data for help visit https://github.com/QwikDev/qwik/issues/6237`);
@@ -695,7 +534,7 @@ function initializeSPA(goto, scroller) {
695
534
  routeLoaderCtx.manifestHash = manifestHash;
696
535
  const loaderState = {};
697
536
  const contentModulesForInit = env.loadedRoute.$mods$;
698
- const loaders = ensureRouteLoaderSignals(contentModulesForInit, loaderState, routeLoaderCtx);
537
+ const loaders = ensureRouteLoaderSignals(contentModulesForInit, loaderState, routeLoaderCtx, env.ev);
699
538
  for (const loader of loaders) if (loader.__id in env.loaderValues) {
700
539
  const value = env.loaderValues[loader.__id];
701
540
  setLoaderSignalValue(loaderState[loader.__id], value);
@@ -713,7 +552,8 @@ function initializeSPA(goto, scroller) {
713
552
  /**
714
553
  * Non-serializable navigation context passed from the nav task to the head+commit task. Only the
715
554
  * data that can't be derived from existing stores/signals.
716
- */ const navContext = useSignal();
555
+ */
556
+ const navContext = useSignal();
717
557
  const httpStatus = useSignal({
718
558
  status: env.response.status,
719
559
  message: env.loadedRoute.$notFound$ ? "Not Found" : env.response.statusMessage ?? ""
@@ -736,7 +576,7 @@ function initializeSPA(goto, scroller) {
736
576
  const registerPreventNav = /*#__PURE__*/ inlinedQrl((fn$) => {
737
577
  if (!isBrowser) return;
738
578
  preventNav.$handler$ ||= (event) => {
739
- internalState.navCount++;
579
+ internalState.attemptCount++;
740
580
  if (!preventNav.$cbs$) return;
741
581
  if ([...preventNav.$cbs$.values()].map((cb) => cb.resolved ? cb.resolved() : cb()).some(Boolean)) {
742
582
  event.preventDefault();
@@ -763,8 +603,9 @@ function initializeSPA(goto, scroller) {
763
603
  * Note: when goto is called, the address bar change must happen in the same tick for Safari to
764
604
  * treat it as a user-initiated navigation and allow scroll restoration to work. For this reason,
765
605
  * make sure to change the address bar before awaiting anything.
766
- */ const goto = /*#__PURE__*/ inlinedQrl(async (path, opt) => {
767
- const actionState = _captures[0], manifestHash = _captures[1], navResolver = _captures[2], routeInternal = _captures[3], routeLocation = _captures[4];
606
+ */
607
+ const goto = /*#__PURE__*/ inlinedQrl(async (path, opt) => {
608
+ const actionState = _captures[0], manifestHash = _captures[1], navResolver = _captures[2], routeInternal = _captures[3], routeLoaderCtx = _captures[4], routeLocation = _captures[5];
768
609
  const { type = "link", forceReload = path === void 0, replaceState = false, scroll = true } = typeof opt === "object" ? opt : { forceReload: opt };
769
610
  if (isBrowser && type === "link" && routeInternal.value.type === "initial") {
770
611
  const url = new URL(window.location.href);
@@ -774,12 +615,11 @@ function initializeSPA(goto, scroller) {
774
615
  const lastDest = routeInternal.value.dest;
775
616
  const dest = path === void 0 ? lastDest : typeof path === "number" ? path : toUrl(path, routeLocation.url);
776
617
  if (navResolver.p && !forceReload && typeof dest !== "number" && isSamePath(dest, lastDest) && dest.href === lastDest.href) return navResolver.p;
777
- const navCount = ++internalState.navCount;
778
- internalState.currentTransition?.skipTransition();
618
+ const attemptCount = ++internalState.attemptCount;
779
619
  if (preventNav.$cbs$ && (forceReload || typeof dest === "number" || !isSamePath(dest, lastDest) || !isSameOrigin(dest, lastDest))) {
780
620
  const prevents = await Promise.all([...preventNav.$cbs$.values()].map((cb) => cb(dest)));
781
- if (navCount !== internalState.navCount || prevents.some(Boolean)) {
782
- if (navCount === internalState.navCount && type === "popstate") history.pushState(null, "", lastDest);
621
+ if (attemptCount !== internalState.attemptCount || prevents.some(Boolean)) {
622
+ if (attemptCount === internalState.attemptCount && type === "popstate") history.pushState(null, "", lastDest);
783
623
  return;
784
624
  }
785
625
  }
@@ -803,9 +643,15 @@ function initializeSPA(goto, scroller) {
803
643
  routeInternal.value.dest = newUrl;
804
644
  routeLocation.url = newUrl;
805
645
  }
806
- return;
646
+ return navResolver.p;
807
647
  }
648
+ const navCount = ++internalState.navCount;
649
+ internalState.currentTransition?.skipTransition();
650
+ navResolver.cancel?.();
651
+ navResolver.cancel = void 0;
652
+ navResolver.r?.();
808
653
  let historyUpdated = false;
654
+ if (isBrowser) getClientRouteLoaders(routeLoaderCtx, routeLocation.url.href);
809
655
  if (isBrowser && type === "link" && !forceReload) {
810
656
  const scroller = getScroller();
811
657
  window._qRouterScrollEnabled = false;
@@ -814,9 +660,10 @@ function initializeSPA(goto, scroller) {
814
660
  clientNavigate(window, type, new URL(location.href), dest, replaceState);
815
661
  historyUpdated = true;
816
662
  }
663
+ const wasNavigating = routeLocation.isNavigating;
817
664
  routeLocation.isNavigating = true;
818
665
  const container = isBrowser ? _getContextContainer() : void 0;
819
- if (container) {
666
+ if (container && !wasNavigating) {
820
667
  await _waitUntilRendered(container);
821
668
  if (navCount !== internalState.navCount) return;
822
669
  }
@@ -829,6 +676,7 @@ function initializeSPA(goto, scroller) {
829
676
  scroll,
830
677
  historyUpdated
831
678
  };
679
+ if (wasNavigating) abortRouteLoaderNavigation(routeLoaderCtx);
832
680
  if (isBrowser) prefetchRoute(dest, false, .8, manifestHash, true);
833
681
  navResolver.p = new Promise((resolve) => {
834
682
  navResolver.r = () => {
@@ -843,6 +691,7 @@ function initializeSPA(goto, scroller) {
843
691
  manifestHash,
844
692
  navResolver,
845
693
  routeInternal,
694
+ routeLoaderCtx,
846
695
  routeLocation
847
696
  ]);
848
697
  useContextProvider(ContentContext, content);
@@ -858,8 +707,9 @@ function initializeSPA(goto, scroller) {
858
707
  /**
859
708
  * This is split in 3 tasks because we need to update the head once we figured out the route, and
860
709
  * before we trigger the render, and we need to subscribe only head to loader signal updates
861
- */ useTaskQrl(/*#__PURE__*/ inlinedQrl(async ({ track }) => {
862
- const actionDataSignal = _captures[0], actionState = _captures[1], content = _captures[2], contentInternal = _captures[3], env = _captures[4], goto = _captures[5], httpStatus = _captures[6], loaderState = _captures[7], navContext = _captures[8], routeInternal = _captures[9], routeLoaderCtx = _captures[10], routeLocation = _captures[11], routeLocationTarget = _captures[12];
710
+ */
711
+ useTaskQrl(/*#__PURE__*/ inlinedQrl(async ({ track }) => {
712
+ const actionDataSignal = _captures[0], actionState = _captures[1], content = _captures[2], contentInternal = _captures[3], env = _captures[4], goto = _captures[5], httpStatus = _captures[6], loaderState = _captures[7], navContext = _captures[8], navResolver = _captures[9], routeInternal = _captures[10], routeLoaderCtx = _captures[11], routeLocation = _captures[12], routeLocationTarget = _captures[13];
863
713
  const navigation = track(routeInternal);
864
714
  const action = track(actionState);
865
715
  const prevUrl = routeLocation.url;
@@ -870,7 +720,6 @@ function initializeSPA(goto, scroller) {
870
720
  let endpointResponse;
871
721
  let actionData;
872
722
  let actionLoaderHashes;
873
- let shouldInvalidateActionLoaders = false;
874
723
  let loadedRoute;
875
724
  if (isServer) {
876
725
  trackUrl = new URL(navigation.dest, routeLocation.url);
@@ -879,20 +728,42 @@ function initializeSPA(goto, scroller) {
879
728
  actionData = endpointResponse;
880
729
  } else {
881
730
  trackUrl = new URL(navigation.dest, location);
731
+ const canceled = new Promise((resolve) => {
732
+ navResolver.cancel = () => resolve(void 0);
733
+ });
882
734
  if (trackUrl.pathname.endsWith("/")) {
883
735
  if (globalThis.__NO_TRAILING_SLASH__) trackUrl.pathname = trackUrl.pathname.slice(0, -1);
884
736
  } else if (!globalThis.__NO_TRAILING_SLASH__) trackUrl.pathname = ensureSlash(trackUrl.pathname);
885
- const loadRoutePromise = loadRoute(qwikRouterConfig.routes, qwikRouterConfig.cacheModules, trackUrl.pathname);
737
+ const loadRoutePromise = import("@qwik-router-config").then((config) => {
738
+ if (internalState.navCount !== navCountBefore) return;
739
+ return loadRoute(config.routes, config.cacheModules, trackUrl.pathname);
740
+ });
886
741
  try {
887
- loadedRoute = await loadRoutePromise;
742
+ const route = await Promise.race([loadRoutePromise, canceled]);
743
+ if (!route) return;
744
+ loadedRoute = route;
888
745
  } catch (e) {
746
+ if (internalState.navCount !== navCountBefore) return;
747
+ const preparedNavCount = getClientRouteLoaders(routeLoaderCtx).navCount;
748
+ if (preparedNavCount !== void 0) restoreRouteLoaders(loaderState, routeLoaderCtx, preparedNavCount);
889
749
  console.error(`Could not load route ${trackUrl.pathname}, reloading:`, e);
890
750
  window.location.href = trackUrl.href;
891
751
  return;
892
752
  }
893
753
  if (internalState.navCount !== navCountBefore) return;
894
754
  if (action) {
895
- const result = await submitAction(action, trackUrl);
755
+ const result = await Promise.race([submitAction(action, trackUrl).then((result) => {
756
+ if (result && action.resolve) {
757
+ action.resolve({
758
+ status: result.status,
759
+ result: result.result
760
+ });
761
+ action.resolve = void 0;
762
+ }
763
+ return result;
764
+ }), canceled]);
765
+ if (internalState.navCount !== navCountBefore || action !== actionState.untrackedValue) return;
766
+ navResolver.cancel = void 0;
896
767
  if (!result) {
897
768
  routeInternal.untrackedValue = {
898
769
  type: navType,
@@ -900,15 +771,9 @@ function initializeSPA(goto, scroller) {
900
771
  };
901
772
  return;
902
773
  }
903
- if (action.resolve) {
904
- action.resolve({
905
- status: result.status,
906
- result: result.result
907
- });
908
- action.resolve = void 0;
909
- }
910
774
  if (result.redirect) {
911
- goto(result.redirect, { replaceState: true });
775
+ if (result.redirect instanceof URL) location.href = result.redirect.href;
776
+ else goto(result.redirect, { replaceState: true });
912
777
  return;
913
778
  }
914
779
  actionData = {
@@ -917,19 +782,17 @@ function initializeSPA(goto, scroller) {
917
782
  actionResult: result.result
918
783
  };
919
784
  actionLoaderHashes = result.loaderHashes;
920
- shouldInvalidateActionLoaders = true;
921
785
  }
786
+ navResolver.cancel = void 0;
922
787
  }
923
788
  const { $routeName$, $params$, $mods$, $menu$, $notFound$ } = loadedRoute;
924
789
  const contentModules = $mods$;
925
790
  if (!isServer) routeLoaderCtx.goto = noSerialize(goto);
926
- updateRouteLoaderPaths(routeLoaderCtx, loadedRoute.$loaderPaths$, trackUrl);
927
- if (!isServer) invalidateNavRouteLoaders(loaderState);
928
- const routeLoaders = ensureRouteLoaderSignals(contentModules, loaderState, routeLoaderCtx);
929
- if (shouldInvalidateActionLoaders) {
930
- if (actionLoaderHashes !== void 0) for (const hash of actionLoaderHashes) loaderState[hash]?.invalidate(true);
931
- else for (const loader of routeLoaders) loaderState[loader.__id].invalidate(true);
932
- }
791
+ let routeLoaders;
792
+ if (isServer) {
793
+ Object.assign(routeLoaderCtx.loaderPaths, loadedRoute.$loaderPaths$);
794
+ routeLoaders = ensureRouteLoaderSignals(contentModules, loaderState, routeLoaderCtx, env.ev);
795
+ } else routeLoaders = prepareRouteLoaders(contentModules, loaderState, routeLoaderCtx, loadedRoute.$loaderPaths$, trackUrl, prevUrl, navCountBefore, action ? actionLoaderHashes ?? null : void 0, action ?? navigation);
933
796
  if (routeLoaders.length > 0) for (let i = 0; i < routeLoaders.length; i++) {
934
797
  const loader = routeLoaders[i];
935
798
  if (!isImmutableLoader(loader.__id)) loaderState[loader.__id].untrackedPending;
@@ -953,6 +816,7 @@ function initializeSPA(goto, scroller) {
953
816
  message: "OK"
954
817
  };
955
818
  const pageModule = contentModules[contentModules.length - 1];
819
+ if (isSamePath(trackUrl, navigation.dest)) trackUrl.hash = navigation.dest.hash;
956
820
  if (navigation.dest.search && !!isSamePath(trackUrl, prevUrl)) trackUrl.search = navigation.dest.search;
957
821
  let shouldForcePrevUrl = false;
958
822
  let shouldForceUrl = false;
@@ -1002,6 +866,7 @@ function initializeSPA(goto, scroller) {
1002
866
  httpStatus,
1003
867
  loaderState,
1004
868
  navContext,
869
+ navResolver,
1005
870
  routeInternal,
1006
871
  routeLoaderCtx,
1007
872
  routeLocation,
@@ -1012,9 +877,12 @@ function initializeSPA(goto, scroller) {
1012
877
  * without re-running the navigation logic.
1013
878
  *
1014
879
  * Note that on the server these tasks run sequentially.
1015
- */ useTaskQrl(/*#__PURE__*/ inlinedQrl(({ track }) => {
1016
- const actionDataSignal = _captures[0], contentInternal = _captures[1], documentHead = _captures[2], loaderState = _captures[3], routeLocation = _captures[4], serverHead = _captures[5];
880
+ */
881
+ useTaskQrl(/*#__PURE__*/ inlinedQrl(({ track }) => {
882
+ const actionDataSignal = _captures[0], contentInternal = _captures[1], documentHead = _captures[2], loaderState = _captures[3], navContext = _captures[4], routeLocation = _captures[5], serverHead = _captures[6];
1017
883
  const contentModules = track(contentInternal);
884
+ const nav = navContext.untrackedValue;
885
+ if (!isServer && routeLocation.isNavigating && nav && nav.navCount !== internalState.navCount) return;
1018
886
  if (!contentModules) return;
1019
887
  const actionData = track(actionDataSignal);
1020
888
  let head;
@@ -1036,11 +904,13 @@ function initializeSPA(goto, scroller) {
1036
904
  contentInternal,
1037
905
  documentHead,
1038
906
  loaderState,
907
+ navContext,
1039
908
  routeLocation,
1040
909
  serverHead
1041
910
  ]), { deferUpdates: isServer });
1042
- /** Actual navigation */ useTaskQrl(/*#__PURE__*/ inlinedQrl(({ track }) => {
1043
- const contentInternal = _captures[0], goto = _captures[1], manifestHash = _captures[2], navContext = _captures[3], navResolver = _captures[4], props = _captures[5], routeInternal = _captures[6], routeLocation = _captures[7];
911
+ /** Actual navigation */
912
+ useTaskQrl(/*#__PURE__*/ inlinedQrl(({ track }) => {
913
+ const contentInternal = _captures[0], goto = _captures[1], loaderState = _captures[2], manifestHash = _captures[3], navContext = _captures[4], navResolver = _captures[5], props = _captures[6], routeInternal = _captures[7], routeLoaderCtx = _captures[8], routeLocation = _captures[9];
1044
914
  const nav = track(navContext);
1045
915
  if (isServer || !nav) return;
1046
916
  if (nav.navCount !== internalState.navCount) return;
@@ -1071,7 +941,12 @@ function initializeSPA(goto, scroller) {
1071
941
  if (currentPath !== nextPath) history.replaceState(history.state, "", nextPath);
1072
942
  } else clientNavigate(window, navType, prevUrl, trackUrl, replaceState);
1073
943
  contentInternal.trigger();
1074
- return navigatePromise = _waitUntilRendered(container);
944
+ return navigatePromise = _waitUntilRendered(container).then(() => {
945
+ if (nav.navCount === internalState.navCount) commitRouteLoaders(loaderState, routeLoaderCtx, nav.navCount);
946
+ }, (error) => {
947
+ if (nav.navCount === internalState.navCount) restoreRouteLoaders(loaderState, routeLoaderCtx, nav.navCount);
948
+ throw error;
949
+ });
1075
950
  };
1076
951
  const _waitNextPage = () => {
1077
952
  if (!shouldStartViewTransition(props?.viewTransition)) return navigate().then(() => void 0);
@@ -1104,40 +979,190 @@ function initializeSPA(goto, scroller) {
1104
979
  }, "useQwikRouter_useTask_2_yw17chooOac", [
1105
980
  contentInternal,
1106
981
  goto,
982
+ loaderState,
1107
983
  manifestHash,
1108
984
  navContext,
1109
985
  navResolver,
1110
986
  props,
1111
987
  routeInternal,
988
+ routeLoaderCtx,
1112
989
  routeLocation
1113
990
  ]), { deferUpdates: false });
1114
991
  };
1115
- /** @public This is a wrapper around the `useQwikRouter()` hook. We recommend using the hook instead of this component, unless you have a good reason to make your root component reactive. */ var QwikRouterProvider = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((props) => {
992
+ /** @public This is a wrapper around the `useQwikRouter()` hook. We recommend using the hook instead of this component, unless you have a good reason to make your root component reactive. */
993
+ function initializeSPA(goto, scroller) {
994
+ if (!window._qRouterSPA) {
995
+ window._qRouterSPA = true;
996
+ history.scrollRestoration = "manual";
997
+ window.addEventListener("popstate", () => {
998
+ window._qRouterScrollEnabled = false;
999
+ clearTimeout(window._qRouterScrollDebounce);
1000
+ goto(location.href, { type: "popstate" });
1001
+ });
1002
+ window.removeEventListener("popstate", window._qRouterInitPopstate);
1003
+ window._qRouterInitPopstate = void 0;
1004
+ if (!window._qRouterHistoryPatch) {
1005
+ window._qRouterHistoryPatch = true;
1006
+ const pushState = history.pushState;
1007
+ const replaceState = history.replaceState;
1008
+ const prepareState = (state) => {
1009
+ if (state === null || typeof state === "undefined") state = {};
1010
+ else if (state?.constructor !== Object) {
1011
+ state = { _data: state };
1012
+ if (isDev) console.warn("In a Qwik SPA context, `history.state` is used to store scroll state. Direct calls to `pushState()` and `replaceState()` must supply an actual Object type. We need to be able to automatically attach the scroll state to your state object. A new state object has been created, your data has been moved to: `history.state._data`");
1013
+ }
1014
+ state._qRouterScroll = state._qRouterScroll || currentScrollState(scroller);
1015
+ return state;
1016
+ };
1017
+ history.pushState = (state, title, url) => {
1018
+ state = prepareState(state);
1019
+ return pushState.call(history, state, title, url);
1020
+ };
1021
+ history.replaceState = (state, title, url) => {
1022
+ state = prepareState(state);
1023
+ return replaceState.call(history, state, title, url);
1024
+ };
1025
+ }
1026
+ document.addEventListener("click", (event) => {
1027
+ if (event.defaultPrevented) return;
1028
+ const target = event.target.closest("a[href]");
1029
+ if (target && !target.hasAttribute("preventdefault:click")) {
1030
+ const href = target.getAttribute("href");
1031
+ const prev = new URL(location.href);
1032
+ const dest = new URL(href, prev);
1033
+ if (isSameOrigin(dest, prev) && isSamePath(dest, prev)) {
1034
+ event.preventDefault();
1035
+ if (!dest.hash && !dest.href.endsWith("#")) {
1036
+ if (dest.href !== prev.href) history.pushState(null, "", dest);
1037
+ window._qRouterScrollEnabled = false;
1038
+ clearTimeout(window._qRouterScrollDebounce);
1039
+ saveScrollHistory({
1040
+ ...currentScrollState(scroller),
1041
+ x: 0,
1042
+ y: 0
1043
+ });
1044
+ location.reload();
1045
+ return;
1046
+ }
1047
+ goto(target.getAttribute("href"));
1048
+ }
1049
+ }
1050
+ });
1051
+ document.removeEventListener("click", window._qRouterInitAnchors);
1052
+ window._qRouterInitAnchors = void 0;
1053
+ if (!window.navigation) {
1054
+ document.addEventListener("visibilitychange", () => {
1055
+ if (window._qRouterScrollEnabled && document.visibilityState === "hidden") saveScrollHistory(currentScrollState(scroller));
1056
+ }, { passive: true });
1057
+ document.removeEventListener("visibilitychange", window._qRouterInitVisibility);
1058
+ window._qRouterInitVisibility = void 0;
1059
+ }
1060
+ window.addEventListener("scroll", () => {
1061
+ if (!window._qRouterScrollEnabled) return;
1062
+ clearTimeout(window._qRouterScrollDebounce);
1063
+ window._qRouterScrollDebounce = setTimeout(() => {
1064
+ saveScrollHistory(currentScrollState(scroller));
1065
+ window._qRouterScrollDebounce = void 0;
1066
+ }, 200);
1067
+ }, { passive: true });
1068
+ removeEventListener("scroll", window._qRouterInitScroll);
1069
+ window._qRouterInitScroll = void 0;
1070
+ spa_init_default.resolve();
1071
+ }
1072
+ }
1073
+ var QwikRouterMockProvider = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((props) => {
1074
+ useQwikMockRouter(props);
1075
+ return /*#__PURE__*/ _jsxSorted(Slot, null, null, null, 3, "dU_1");
1076
+ }, "QwikRouterMockProvider_component_IN4dVpT0x74"));
1077
+ var useQwikMockRouter = (props) => {
1078
+ const urlEnv = props.url ?? "http://localhost/";
1079
+ const url = new URL(urlEnv);
1080
+ const routeLocation = useStore({
1081
+ url,
1082
+ params: props.params ?? {},
1083
+ isNavigating: false,
1084
+ prevUrl: void 0
1085
+ }, { deep: false });
1086
+ const loadersData = props.loaders?.reduce((acc, { loader, data }) => {
1087
+ acc[loader.__id] = data;
1088
+ return acc;
1089
+ }, {});
1090
+ const loadersState = useStore({}, { deep: false });
1091
+ for (const [loaderId, data] of Object.entries(loadersData ?? {})) loadersState[loaderId] ||= createComputedQrl(/*#__PURE__*/ inlinedQrl(() => {
1092
+ return _captures[0];
1093
+ }, "useQwikMockRouter_createComputed_JL6bChMwwyc", [data]));
1094
+ const goto = props.goto ?? /*#__PURE__*/ inlinedQrl(async () => {
1095
+ console.warn("QwikRouterMockProvider: goto not provided");
1096
+ }, "useQwikMockRouter_goto_aViHFxQ1a3s");
1097
+ const documentHead = useStore(createDocumentHead, { deep: false });
1098
+ const content = useStore({
1099
+ headings: void 0,
1100
+ menu: void 0
1101
+ }, { deep: false });
1102
+ const contentInternal = useSignal();
1103
+ const actionState = useSignal();
1104
+ const httpStatus = useSignal({
1105
+ status: 200,
1106
+ message: ""
1107
+ });
1108
+ useContextProvider(ContentContext, content);
1109
+ useContextProvider(ContentInternalContext, contentInternal);
1110
+ useContextProvider(DocumentHeadContext, documentHead);
1111
+ useContextProvider(HttpStatusContext, httpStatus);
1112
+ useContextProvider(RouteLocationContext, routeLocation);
1113
+ useContextProvider(RouteNavigateContext, goto);
1114
+ useContextProvider(RouteStateContext, loadersState);
1115
+ useContextProvider(RouteActionContext, actionState);
1116
+ const actionsMocks = props.actions?.reduce((acc, { action, handler }) => {
1117
+ acc[action.__id] = handler;
1118
+ return acc;
1119
+ }, {});
1120
+ useTaskQrl(/*#__PURE__*/ inlinedQrl(async ({ track }) => {
1121
+ const actionState = _captures[0], actionsMocks = _captures[1];
1122
+ const action = track(actionState);
1123
+ if (!action?.resolve) return;
1124
+ const mock = actionsMocks?.[action.id];
1125
+ if (mock) {
1126
+ const actionResult = await mock(action.data);
1127
+ action.resolve(actionResult);
1128
+ }
1129
+ }, "useQwikMockRouter_useTask_tXTLR4tzCy0", [actionState, actionsMocks]));
1130
+ };
1131
+ var QwikRouterProvider = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((props) => {
1116
1132
  useQwikRouter(props);
1117
- return /*#__PURE__*/ _jsxSorted(Slot, null, null, null, 3, "5y_0");
1133
+ return /*#__PURE__*/ _jsxSorted(Slot, null, null, null, 3, "dU_0");
1118
1134
  }, "QwikRouterProvider_component_6Kjfa79mqlY"));
1119
1135
  /**
1120
1136
  * @deprecated Use `useQwikRouter()` instead. Will be removed in v3.
1121
1137
  * @public
1122
- */ var QwikCityProvider = QwikRouterProvider;
1138
+ */
1139
+ var QwikCityProvider = QwikRouterProvider;
1140
+ /** @public */
1141
+ /** @public */
1142
+ /**
1143
+ * @deprecated Use `useQwikMockRouter()` instead. Will be removed in V3
1144
+ * @public
1145
+ */
1146
+ var QwikCityMockProvider = QwikRouterMockProvider;
1123
1147
  var handleRouterPopstate = (nav, event) => {
1124
1148
  const href = event.detail?.href;
1125
1149
  if (href) return nav(href, { type: "popstate" });
1126
1150
  };
1127
- /** @public */ var RouterOutlet = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl(() => {
1151
+ /** @public */
1152
+ var RouterOutlet = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl(() => {
1128
1153
  if (!useServerData("containerAttributes")) throw new Error("PrefetchServiceWorker component must be rendered on the server.");
1129
1154
  const internalContext = useContext(ContentInternalContext);
1130
1155
  const nav = useNavigate();
1131
1156
  const contents = internalContext.value;
1132
1157
  if (contents && contents.length > 0) {
1133
- const contentsLen = contents.length;
1134
1158
  let cmp = null;
1135
- for (let i = contentsLen - 1; i >= 0; i--) if (contents[i].default) cmp = _jsxSorted(contents[i].default, null, null, cmp, 1, "Fn_0");
1159
+ for (let i = contents.length - 1; i >= 0; i--) if (contents[i].default) cmp = /*#__PURE__*/ _jsxSorted(contents[i].default, null, null, cmp, 1, "yd_0");
1136
1160
  return /*#__PURE__*/ _jsxSorted(Fragment, null, null, [cmp, !__EXPERIMENTAL__.noSPA && /*#__PURE__*/ _jsxSorted("script", {
1161
+ "q-d:qcinit": [spa_init_default, linkPrefetchInit],
1137
1162
  "q-d:qinit": _qrlSync(() => {
1138
1163
  ((w, h) => {
1139
1164
  if (!w._qcs) {
1140
- w._qcs = !0;
1165
+ w._qcs = true;
1141
1166
  const s = h.state?._qRouterScroll;
1142
1167
  if (s) {
1143
1168
  h.scrollRestoration = "manual";
@@ -1147,126 +1172,19 @@ var handleRouterPopstate = (nav, event) => {
1147
1172
  }
1148
1173
  })(window, history);
1149
1174
  }, "()=>{((w,h)=>{if(!w._qcs){w._qcs=!0;const s=h.state?._qRouterScroll;if(s){h.scrollRestoration=\"manual\";w.scrollTo(s.x,s.y);}document.dispatchEvent(new Event(\"qcinit\"));}})(window,history);}"),
1150
- "q-d:qrouterpopstate": /*#__PURE__*/ inlinedQrl((event, _1, nav) => handleRouterPopstate(nav, event), "RouterOutlet_component_Fragment_script_q_d_qrouterpopstate_aRL6GvFyews").m(),
1151
1175
  "q:p": nav
1152
- }, { "q-d:qcinit": [spa_init_default, linkPrefetchInit] }, null, 6, "Fn_1")], 1, "Fn_2");
1176
+ }, { "q-d:qrouterpopstate": /*#__PURE__*/ inlinedQrl((event, _1, nav) => handleRouterPopstate(nav, event), "RouterOutlet_component_Fragment_script_q_d_qrouterpopstate_aRL6GvFyews").m() }, null, 6, "yd_0")], 1, "yd_1");
1153
1177
  }
1154
1178
  return SkipRender;
1155
1179
  }, "RouterOutlet_component_QwONcWD5gIg"));
1156
- /** @internal */ var validatorQrl = (validator) => {
1157
- if (isServer) return { validate: validator };
1158
- };
1159
- /** @public */ var validator$ = /*#__PURE__*/ implicit$FirstArg(validatorQrl);
1160
- var flattenValibotIssues = (issues) => {
1161
- return issues.reduce((acc, issue) => {
1162
- if (issue.path) {
1163
- if (issue.path.some((path) => path.type === "array")) {
1164
- const keySuffix = issue.expected === "Array" ? "[]" : "";
1165
- const key = issue.path.map((item) => item.type === "array" ? "*" : item.key).join(".").replace(/\.\*/g, "[]") + keySuffix;
1166
- acc[key] = acc[key] || [];
1167
- if (Array.isArray(acc[key])) acc[key].push(issue.message);
1168
- return acc;
1169
- } else acc[issue.path.map((item) => item.key).join(".")] = issue.message;
1170
- }
1171
- return acc;
1172
- }, {});
1173
- };
1174
- /** @internal */ var valibotQrl = (qrl) => {
1175
- if (!__EXPERIMENTAL__.valibot) throw new Error("Valibot is an experimental feature and is not enabled. Please enable the feature flag by adding `experimental: [\"valibot\"]` to your qwikVite plugin options.");
1176
- if (isServer) return {
1177
- __brand: "valibot",
1178
- async validate(ev, inputData) {
1179
- const schema = await qrl.resolve().then((obj) => typeof obj === "function" ? obj(ev) : obj);
1180
- const data = inputData ?? await ev.parseBody();
1181
- const result = await v.safeParseAsync(schema, data);
1182
- if (result.success) return {
1183
- success: true,
1184
- data: result.output
1185
- };
1186
- else {
1187
- if (isDev) console.error("ERROR: Valibot validation failed", result.issues);
1188
- return {
1189
- success: false,
1190
- status: 400,
1191
- error: {
1192
- formErrors: v.flatten(result.issues).root ?? [],
1193
- fieldErrors: flattenValibotIssues(result.issues)
1194
- }
1195
- };
1196
- }
1197
- }
1198
- };
1199
- };
1200
- /** @beta */ var valibot$ = /*#__PURE__*/ implicit$FirstArg(valibotQrl);
1201
- var flattenZodIssues = (issues) => {
1202
- issues = Array.isArray(issues) ? issues : [issues];
1203
- return issues.reduce((acc, issue) => {
1204
- const isExpectingArray = "expected" in issue && issue.expected === "array";
1205
- if (issue.path.some((path) => typeof path === "number") || isExpectingArray) {
1206
- const keySuffix = "expected" in issue && issue.expected === "array" ? "[]" : "";
1207
- const key = issue.path.map((path) => typeof path === "number" ? "*" : path).join(".").replace(/\.\*/g, "[]") + keySuffix;
1208
- acc[key] = acc[key] || [];
1209
- if (Array.isArray(acc[key])) acc[key].push(issue.message);
1210
- return acc;
1211
- } else acc[issue.path.join(".")] = issue.message;
1212
- return acc;
1213
- }, {});
1214
- };
1215
- /** @internal */ var zodQrl = (qrl) => {
1216
- if (isServer) return {
1217
- __brand: "zod",
1218
- async validate(ev, inputData) {
1219
- const schema = await qrl.resolve().then((obj) => {
1220
- if (typeof obj === "function") obj = obj(z$1, ev);
1221
- if (obj instanceof z$1.ZodType) return obj;
1222
- else return z$1.object(obj);
1223
- });
1224
- const data = inputData ?? await ev.parseBody();
1225
- const result = await withLocale(ev.locale(), () => schema.safeParseAsync(data));
1226
- if (result.success) return result;
1227
- else {
1228
- if (isDev) console.error("ERROR: Zod validation failed", result.error.issues);
1229
- return {
1230
- success: false,
1231
- status: 400,
1232
- error: {
1233
- formErrors: result.error.flatten().formErrors,
1234
- fieldErrors: flattenZodIssues(result.error.issues)
1235
- }
1236
- };
1237
- }
1238
- }
1239
- };
1240
- };
1241
- /** @public */ var zod$ = /*#__PURE__*/ implicit$FirstArg(zodQrl);
1242
- var getValidators = (rest, qrl) => {
1243
- let id;
1244
- let invalidate;
1245
- const validators = [];
1246
- if (rest.length === 1) {
1247
- const options = rest[0];
1248
- if (options && typeof options === "object") {
1249
- if ("validate" in options) validators.push(options);
1250
- else {
1251
- id = options.id;
1252
- if (options.validation) validators.push(...options.validation);
1253
- if (options.invalidate) invalidate = options.invalidate.map((loader) => loader.__id);
1254
- }
1255
- }
1256
- } else if (rest.length > 1) validators.push(...rest.filter((v) => !!v));
1257
- if (typeof id === "string") {
1258
- if (isDev) {
1259
- if (!/^[\w/.-]+$/.test(id)) throw new Error(`Invalid id: ${id}, id can only contain [a-zA-Z0-9_.-]`);
1260
- }
1261
- id = `id_${id}`;
1262
- } else id = qrl.getHash();
1263
- return {
1264
- validators: validators.reverse(),
1265
- id,
1266
- invalidate
1267
- };
1268
- };
1269
- /** @public */ var routeActionQrl = (actionQrl, ...rest) => {
1180
+ /**
1181
+ * Hoisted function declarations (not consts) on purpose, like `routeLoaderQrl`: generated route
1182
+ * modules call these factories back during their own evaluation via the `@qwik-router-config`
1183
+ * cycle, where a `const` binding would throw a TDZ ReferenceError.
1184
+ *
1185
+ * @public
1186
+ */
1187
+ function routeActionQrl(actionQrl, ...rest) {
1270
1188
  const { id, validators, invalidate } = getValidators(rest, actionQrl);
1271
1189
  function action() {
1272
1190
  const loc = useLocation();
@@ -1293,7 +1211,7 @@ var getValidators = (rest, qrl) => {
1293
1211
  return initialState;
1294
1212
  });
1295
1213
  initialState.submit = /* @__PURE__ */ inlinedQrl((input = {}) => {
1296
- const currentAction = _captures[0], id = _captures[1], loc = _captures[2], state = _captures[3];
1214
+ const currentAction = _captures[0], id = _captures[1], latestSubmission = _captures[2], loc = _captures[3], state = _captures[4];
1297
1215
  if (isServer) throw new Error(`Actions can not be invoked within the server during SSR.
1298
1216
  Action.run() can only be called on the browser, for example when a user clicks a button, or submits a form.`);
1299
1217
  let data;
@@ -1305,6 +1223,8 @@ Action.run() can only be called on the browser, for example when a user clicks a
1305
1223
  if (input.submitter.name) data.append(input.submitter.name, input.submitter.value);
1306
1224
  }
1307
1225
  } else data = input;
1226
+ const submission = {};
1227
+ latestSubmission.current = submission;
1308
1228
  return new Promise((resolve) => {
1309
1229
  if (data instanceof FormData) state.formData = data;
1310
1230
  state.submitted = true;
@@ -1315,15 +1235,18 @@ Action.run() can only be called on the browser, for example when a user clicks a
1315
1235
  id,
1316
1236
  resolve: noSerialize(resolve)
1317
1237
  };
1318
- }).then((_rawProps) => {
1319
- state.isRunning = false;
1320
- state.status = _rawProps.status;
1321
- state.value = _rawProps.result;
1238
+ }).then(({ result, status }) => {
1239
+ const isLatestSubmission = latestSubmission.current === submission;
1240
+ if (isLatestSubmission) {
1241
+ state.isRunning = false;
1242
+ state.status = status;
1243
+ state.value = result;
1244
+ }
1322
1245
  if (form) {
1323
- if (form.getAttribute("data-spa-reset") === "true") form.reset();
1246
+ if (isLatestSubmission && form.getAttribute("data-spa-reset") === "true") form.reset();
1324
1247
  const detail = {
1325
- status: _rawProps.status,
1326
- value: _rawProps.result
1248
+ status,
1249
+ value: result
1327
1250
  };
1328
1251
  form.dispatchEvent(new CustomEvent("submitcompleted", {
1329
1252
  bubbles: false,
@@ -1333,13 +1256,14 @@ Action.run() can only be called on the browser, for example when a user clicks a
1333
1256
  }));
1334
1257
  }
1335
1258
  return {
1336
- status: _rawProps.status,
1337
- value: _rawProps.result
1259
+ status,
1260
+ value: result
1338
1261
  };
1339
1262
  });
1340
1263
  }, "routeActionQrl_action_submit_YuS5bpdQ360", [
1341
1264
  currentAction,
1342
1265
  id,
1266
+ { current: void 0 },
1343
1267
  loc,
1344
1268
  state
1345
1269
  ]);
@@ -1352,15 +1276,16 @@ Action.run() can only be called on the browser, for example when a user clicks a
1352
1276
  action.__invalidate = invalidate;
1353
1277
  Object.freeze(action);
1354
1278
  return action;
1355
- };
1356
- /** @internal */ var globalActionQrl = (actionQrl, ...rest) => {
1279
+ }
1280
+ /** @internal */
1281
+ function globalActionQrl(actionQrl, ...rest) {
1357
1282
  const action = routeActionQrl(actionQrl, ...rest);
1358
1283
  if (isServer) {
1359
1284
  if (typeof globalThis._qwikActionsMap === "undefined") globalThis._qwikActionsMap = /* @__PURE__ */ new Map();
1360
1285
  globalThis._qwikActionsMap.set(action.__id, action);
1361
1286
  }
1362
1287
  return action;
1363
- };
1288
+ }
1364
1289
  /**
1365
1290
  * Define a route action that handles form submissions or programmatic invocations.
1366
1291
  *
@@ -1380,26 +1305,104 @@ Action.run() can only be called on the browser, for example when a user clicks a
1380
1305
  * don't specify an explicit `invalidate` option.
1381
1306
  *
1382
1307
  * @public
1383
- */ var routeAction$ = /*#__PURE__*/ implicit$FirstArg(routeActionQrl);
1384
- /** @public */ var globalAction$ = /*#__PURE__*/ implicit$FirstArg(globalActionQrl);
1385
- var deserializeStream = async function* (stream, abortSignal) {
1386
- const reader = stream.getReader();
1387
- try {
1388
- let buffer = "";
1389
- const decoder = new TextDecoder();
1390
- while (!abortSignal?.aborted) {
1391
- const result = await reader.read();
1392
- if (result.done) break;
1393
- buffer += decoder.decode(result.value, { stream: true });
1394
- const lines = buffer.split(/\n/);
1395
- buffer = lines.pop();
1396
- for (const line of lines) yield await _deserialize(line);
1308
+ */
1309
+ var routeAction$ = /*#__PURE__*/ implicit$FirstArg(routeActionQrl);
1310
+ /** @public */
1311
+ var globalAction$ = /*#__PURE__*/ implicit$FirstArg(globalActionQrl);
1312
+ /** @internal */
1313
+ function validatorQrl(validator) {
1314
+ if (isServer) return { validate: validator };
1315
+ }
1316
+ /** @public */
1317
+ var validator$ = /*#__PURE__*/ implicit$FirstArg(validatorQrl);
1318
+ var flattenValibotIssues = (issues) => {
1319
+ return issues.reduce((acc, issue) => {
1320
+ if (issue.path) {
1321
+ if (issue.path.some((path) => path.type === "array")) {
1322
+ const keySuffix = issue.expected === "Array" ? "[]" : "";
1323
+ const key = issue.path.map((item) => item.type === "array" ? "*" : item.key).join(".").replace(/\.\*/g, "[]") + keySuffix;
1324
+ acc[key] = acc[key] || [];
1325
+ if (Array.isArray(acc[key])) acc[key].push(issue.message);
1326
+ return acc;
1327
+ } else acc[issue.path.map((item) => item.key).join(".")] = issue.message;
1397
1328
  }
1398
- } finally {
1399
- reader.releaseLock();
1400
- }
1329
+ return acc;
1330
+ }, {});
1331
+ };
1332
+ /** @internal */
1333
+ function valibotQrl(qrl) {
1334
+ if (!__EXPERIMENTAL__.valibot) throw new Error("Valibot is an experimental feature and is not enabled. Please enable the feature flag by adding `experimental: [\"valibot\"]` to your qwikVite plugin options.");
1335
+ if (isServer) return {
1336
+ __brand: "valibot",
1337
+ async validate(ev, inputData) {
1338
+ const schema = await qrl.resolve().then((obj) => typeof obj === "function" ? obj(ev) : obj);
1339
+ const data = inputData ?? await ev.parseBody();
1340
+ const result = await v.safeParseAsync(schema, data);
1341
+ if (result.success) return {
1342
+ success: true,
1343
+ data: result.output
1344
+ };
1345
+ else {
1346
+ if (isDev) console.error("ERROR: Valibot validation failed", result.issues);
1347
+ return {
1348
+ success: false,
1349
+ status: 400,
1350
+ error: {
1351
+ formErrors: v.flatten(result.issues).root ?? [],
1352
+ fieldErrors: flattenValibotIssues(result.issues)
1353
+ }
1354
+ };
1355
+ }
1356
+ }
1357
+ };
1358
+ }
1359
+ /** @beta */
1360
+ var valibot$ = /*#__PURE__*/ implicit$FirstArg(valibotQrl);
1361
+ var flattenZodIssues = (issues) => {
1362
+ issues = Array.isArray(issues) ? issues : [issues];
1363
+ return issues.reduce((acc, issue) => {
1364
+ const isExpectingArray = "expected" in issue && issue.expected === "array";
1365
+ if (issue.path.some((path) => typeof path === "number") || isExpectingArray) {
1366
+ const keySuffix = "expected" in issue && issue.expected === "array" ? "[]" : "";
1367
+ const key = issue.path.map((path) => typeof path === "number" ? "*" : path).join(".").replace(/\.\*/g, "[]") + keySuffix;
1368
+ acc[key] = acc[key] || [];
1369
+ if (Array.isArray(acc[key])) acc[key].push(issue.message);
1370
+ return acc;
1371
+ } else acc[issue.path.join(".")] = issue.message;
1372
+ return acc;
1373
+ }, {});
1401
1374
  };
1402
- /** @internal */ var serverQrl = (qrl, options) => {
1375
+ /** @internal */
1376
+ function zodQrl(qrl) {
1377
+ if (isServer) return {
1378
+ __brand: "zod",
1379
+ async validate(ev, inputData) {
1380
+ const schema = await qrl.resolve().then((obj) => {
1381
+ if (typeof obj === "function") obj = obj(z$1, ev);
1382
+ if (obj instanceof z$1.ZodType) return obj;
1383
+ else return z$1.object(obj);
1384
+ });
1385
+ const data = inputData ?? await ev.parseBody();
1386
+ const result = await withLocale(ev.locale(), () => schema.safeParseAsync(data));
1387
+ if (result.success) return result;
1388
+ else {
1389
+ if (isDev) console.error("ERROR: Zod validation failed", result.error.issues);
1390
+ return {
1391
+ success: false,
1392
+ status: 400,
1393
+ error: {
1394
+ formErrors: result.error.flatten().formErrors,
1395
+ fieldErrors: flattenZodIssues(result.error.issues)
1396
+ }
1397
+ };
1398
+ }
1399
+ }
1400
+ };
1401
+ }
1402
+ /** @public */
1403
+ var zod$ = /*#__PURE__*/ implicit$FirstArg(zodQrl);
1404
+ /** @internal */
1405
+ function serverQrl(qrl, options) {
1403
1406
  if (isServer) {
1404
1407
  const captured = qrl.getCaptured();
1405
1408
  if (captured && captured.length > 0 && !_getContextHostElement()) throw new Error("For security reasons, we cannot serialize QRLs that capture lexical scope.");
@@ -1442,13 +1445,13 @@ var deserializeStream = async function* (stream, abortSignal) {
1442
1445
  else config.body = body;
1443
1446
  const res = await fetch(`${origin}?${QFN_KEY}=${qrlHash}${query}`, config);
1444
1447
  const contentType = res.headers.get("Content-Type");
1445
- if (res.ok && contentType === "text/qwik-json-stream" && res.body) return async function* () {
1448
+ if (res.ok && contentType === "text/qwik-json-stream" && res.body) return (async function* () {
1446
1449
  try {
1447
1450
  for await (const result of deserializeStream(res.body, abortSignal)) yield result;
1448
1451
  } finally {
1449
1452
  if (!abortSignal?.aborted) await res.body.cancel();
1450
1453
  }
1451
- }();
1454
+ })();
1452
1455
  else if (contentType === "application/qwik-json") {
1453
1456
  const str = await res.text();
1454
1457
  const obj = await _deserialize(str);
@@ -1471,8 +1474,53 @@ var deserializeStream = async function* (stream, abortSignal) {
1471
1474
  origin,
1472
1475
  qrl
1473
1476
  ]);
1477
+ }
1478
+ /** @public */
1479
+ var server$ = /*#__PURE__*/ implicit$FirstArg(serverQrl);
1480
+ function getValidators(rest, qrl) {
1481
+ let id;
1482
+ let invalidate;
1483
+ const validators = [];
1484
+ if (rest.length === 1) {
1485
+ const options = rest[0];
1486
+ if (options && typeof options === "object") {
1487
+ if ("validate" in options) validators.push(options);
1488
+ else {
1489
+ id = options.id;
1490
+ if (options.validation) validators.push(...options.validation);
1491
+ if (options.invalidate) invalidate = options.invalidate.map((loader) => loader.__id);
1492
+ }
1493
+ }
1494
+ } else if (rest.length > 1) validators.push(...rest.filter((v) => !!v));
1495
+ if (typeof id === "string") {
1496
+ if (isDev) {
1497
+ if (!/^[\w/.-]+$/.test(id)) throw new Error(`Invalid id: ${id}, id can only contain [a-zA-Z0-9_.-]`);
1498
+ }
1499
+ id = `id_${id}`;
1500
+ } else id = qrl.getHash();
1501
+ return {
1502
+ validators: validators.reverse(),
1503
+ id,
1504
+ invalidate
1505
+ };
1506
+ }
1507
+ var deserializeStream = async function* (stream, abortSignal) {
1508
+ const reader = stream.getReader();
1509
+ try {
1510
+ let buffer = "";
1511
+ const decoder = new TextDecoder();
1512
+ while (!abortSignal?.aborted) {
1513
+ const result = await reader.read();
1514
+ if (result.done) break;
1515
+ buffer += decoder.decode(result.value, { stream: true });
1516
+ const lines = buffer.split(/\n/);
1517
+ buffer = lines.pop();
1518
+ for (const line of lines) yield await _deserialize(line);
1519
+ }
1520
+ } finally {
1521
+ reader.releaseLock();
1522
+ }
1474
1523
  };
1475
- /** @public */ var server$ = /*#__PURE__*/ implicit$FirstArg(serverQrl);
1476
1524
  /**
1477
1525
  * Loads the service workers that are defined in the routes. Any file named `service-worker.*` (all
1478
1526
  * JS extensions are allowed) will be picked up, bundled into a separate file, and registered as a
@@ -1485,14 +1533,43 @@ var deserializeStream = async function* (stream, abortSignal) {
1485
1533
  * service-worker.ts file) as long as you want to unregister the service-worker.js for your users.
1486
1534
  *
1487
1535
  * @public
1488
- */ var ServiceWorkerRegister = (props) => /*#__PURE__*/ _jsxSorted("script", { nonce: _wrapProp(props, "nonce") }, {
1536
+ */
1537
+ var ServiceWorkerRegister = (props) => /*#__PURE__*/ _jsxSorted("script", { nonce: _wrapProp(props, "nonce") }, {
1489
1538
  type: "module",
1490
1539
  dangerouslySetInnerHTML: swRegister
1491
- }, null, 3, "1x_0");
1492
- var _hf0 = (p0) => !p0.reloadDocument;
1493
- var _hf0_str = "!p0.reloadDocument";
1494
- var _hf1 = (p0) => p0.spaReset ? "true" : void 0;
1495
- var _hf1_str = "p0.spaReset?\"true\":undefined";
1540
+ }, null, 3, "UM_0");
1541
+ var _hf0 = (p0) => p0.spaReset ? "true" : void 0;
1542
+ var _hf0_str = "p0.spaReset?\"true\":undefined";
1543
+ /** @public */
1544
+ var Form = ({ action, spaReset, reloadDocument, onSubmit$, ...rest }, key) => {
1545
+ if (action) {
1546
+ if (Array.isArray(onSubmit$)) return /*#__PURE__*/ _jsxSplit("form", {
1547
+ ..._getVarProps(rest),
1548
+ ..._getConstProps(rest),
1549
+ action: _wrapProp(action, "actionPath"),
1550
+ "preventdefault:submit": !reloadDocument,
1551
+ "q-e:submit": [...onSubmit$, !reloadDocument ? /*#__PURE__*/ inlinedQrl((evt) => {
1552
+ const action = _captures[0];
1553
+ if (!action.submitted) return action.submit(evt);
1554
+ }, "Form_form_q_e_submit_6i0Jq5q8JFg", [action]) : void 0],
1555
+ ["data-spa-reset"]: spaReset ? "true" : void 0
1556
+ }, { method: "post" }, null, 0, key);
1557
+ return /*#__PURE__*/ _jsxSplit("form", {
1558
+ ..._getVarProps(rest),
1559
+ ..._getConstProps(rest),
1560
+ action: _wrapProp(action, "actionPath"),
1561
+ "preventdefault:submit": !reloadDocument,
1562
+ "q-e:submit": [onSubmit$, !reloadDocument ? action.submit : void 0],
1563
+ ["data-spa-reset"]: spaReset ? "true" : void 0
1564
+ }, { method: "post" }, null, 0, key);
1565
+ } else return /*#__PURE__*/ createElement(GetForm, {
1566
+ ...rest,
1567
+ spaReset,
1568
+ reloadDocument,
1569
+ onSubmit$,
1570
+ key
1571
+ });
1572
+ };
1496
1573
  var GetForm = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((_rawProps) => {
1497
1574
  const rest = _restProps(_rawProps, [
1498
1575
  "action",
@@ -1503,10 +1580,8 @@ var GetForm = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((_rawProps) =>
1503
1580
  const nav = useNavigate();
1504
1581
  return /*#__PURE__*/ _jsxSplit("form", {
1505
1582
  action: "get",
1506
- "preventdefault:submit": _fnSignal(_hf0, [_rawProps], _hf0_str),
1507
- "data-spa-reset": _fnSignal(_hf1, [_rawProps], _hf1_str),
1583
+ "data-spa-reset": _fnSignal(_hf0, [_rawProps], _hf0_str),
1508
1584
  ..._getVarProps(rest),
1509
- ..._getConstProps(rest),
1510
1585
  "q-e:submit": [
1511
1586
  ...Array.isArray(_rawProps.onSubmit$) ? _rawProps.onSubmit$ : [_rawProps.onSubmit$],
1512
1587
  /*#__PURE__*/ inlinedQrl(async (_evt, form) => {
@@ -1531,40 +1606,16 @@ var GetForm = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((_rawProps) =>
1531
1606
  }));
1532
1607
  }, "GetForm_component_form_q_e_submit_1_cuYklZAOHrA")
1533
1608
  ]
1534
- }, null, /*#__PURE__*/ _jsxSorted(Slot, null, null, null, 3, "Q4_0"), 0, "Q4_1");
1609
+ }, {
1610
+ ..._getConstProps(rest),
1611
+ "preventdefault:submit": true
1612
+ }, /*#__PURE__*/ _jsxSorted(Slot, null, null, null, 3, "TF_0"), 0, "TF_1");
1535
1613
  }, "GetForm_component_2U5Z2Z8ryc0"));
1536
- /** @public */ var Form = ({ action, spaReset, reloadDocument, onSubmit$, ...rest }, key) => {
1537
- if (action) {
1538
- if (Array.isArray(onSubmit$)) return _jsxSplit("form", {
1539
- ..._getVarProps(rest),
1540
- ..._getConstProps(rest),
1541
- action: _wrapProp(action, "actionPath"),
1542
- "preventdefault:submit": !reloadDocument,
1543
- "q-e:submit": [...onSubmit$, !reloadDocument ? /*#__PURE__*/ inlinedQrl((evt) => {
1544
- const action = _captures[0];
1545
- if (!action.submitted) return action.submit(evt);
1546
- }, "Form_form_q_e_submit_6i0Jq5q8JFg", [action]) : void 0],
1547
- ["data-spa-reset"]: spaReset ? "true" : void 0
1548
- }, { method: "post" }, null, 0, key);
1549
- return _jsxSplit("form", {
1550
- ..._getVarProps(rest),
1551
- ..._getConstProps(rest),
1552
- action: _wrapProp(action, "actionPath"),
1553
- "preventdefault:submit": !reloadDocument,
1554
- "q-e:submit": [onSubmit$, !reloadDocument ? action.submit : void 0],
1555
- ["data-spa-reset"]: spaReset ? "true" : void 0
1556
- }, { method: "post" }, null, 0, key);
1557
- } else return /*#__PURE__*/ _jsxSplit(GetForm, {
1558
- spaReset,
1559
- reloadDocument,
1560
- onSubmit$,
1561
- ...rest
1562
- }, null, null, 0, key);
1563
- };
1564
1614
  /**
1565
1615
  * @beta
1566
1616
  * @experimental
1567
- */ var untypedAppUrl = function appUrl(route, params, paramsPrefix = "") {
1617
+ */
1618
+ var untypedAppUrl = function appUrl(route, params, paramsPrefix = "") {
1568
1619
  const path = route.split("/");
1569
1620
  for (let i = 0; i < path.length; i++) {
1570
1621
  const segment = path[i];
@@ -1590,7 +1641,8 @@ var GetForm = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((_rawProps) =>
1590
1641
  *
1591
1642
  * @beta
1592
1643
  * @experimental
1593
- */ function omitProps(obj, keys) {
1644
+ */
1645
+ function omitProps(obj, keys) {
1594
1646
  const omittedObj = {};
1595
1647
  for (const key in obj) if (!key.startsWith("param:") && !keys.includes(key)) omittedObj[key] = obj[key];
1596
1648
  return omittedObj;
@@ -1621,8 +1673,9 @@ var GetForm = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((_rawProps) =>
1621
1673
  * ```
1622
1674
  *
1623
1675
  * @public
1624
- */ var createRenderer = (getOptions) => {
1625
- return (opts) => {
1676
+ */
1677
+ var createRenderer = (getOptions) => {
1678
+ return ((opts) => {
1626
1679
  const { jsx, options } = getOptions(opts);
1627
1680
  const renderOptions = opts.serverData.renderMode === "static" ? {
1628
1681
  ...options,
@@ -1632,7 +1685,7 @@ var GetForm = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((_rawProps) =>
1632
1685
  }
1633
1686
  } : options;
1634
1687
  return renderToStream(jsx, renderOptions);
1635
- };
1688
+ });
1636
1689
  };
1637
1690
  /**
1638
1691
  * This renders all the tags collected from `head`.
@@ -1655,16 +1708,17 @@ var GetForm = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((_rawProps) =>
1655
1708
  * `src/root.tsx` to make runtime changes to `<head>` if needed.
1656
1709
  *
1657
1710
  * @public
1658
- */ var DocumentHeadTags = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((props) => {
1711
+ */
1712
+ var DocumentHeadTags = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((props) => {
1659
1713
  let head = useDocumentHead();
1660
1714
  if (props) head = {
1661
1715
  ...head,
1662
1716
  ...props
1663
1717
  };
1664
1718
  return /*#__PURE__*/ _jsxSorted(Fragment, null, null, [
1665
- head.title && /*#__PURE__*/ _jsxSorted("title", null, null, head.title, 1, "r5_0"),
1666
- head.meta.map((m) => /*#__PURE__*/ _jsxSplit("meta", { ..._getVarProps(m) }, _getConstProps(m), null, 0, "r5_1")),
1667
- head.links.map((l) => /*#__PURE__*/ _jsxSplit("link", { ..._getVarProps(l) }, _getConstProps(l), null, 0, "r5_2")),
1719
+ head.title && /*#__PURE__*/ _jsxSorted("title", null, null, head.title, 1, "eL_0"),
1720
+ head.meta.map((m) => /*#__PURE__*/ _jsxSplit("meta", { ..._getVarProps(m) }, _getConstProps(m), null, 0, "eL_1")),
1721
+ head.links.map((l) => /*#__PURE__*/ _jsxSplit("link", { ..._getVarProps(l) }, _getConstProps(l), null, 0, "eL_2")),
1668
1722
  head.styles.map((s) => {
1669
1723
  const props = s.props || s;
1670
1724
  return /*#__PURE__*/ createElement("style", {
@@ -1681,6 +1735,6 @@ var GetForm = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((_rawProps) =>
1681
1735
  key: s.key
1682
1736
  });
1683
1737
  })
1684
- ], 1, "r5_3");
1738
+ ], 1, "eL_3");
1685
1739
  }, "DocumentHeadTags_component_9CrWYOoCpgY"));
1686
1740
  export { DocumentHeadTags, Form, Link, QWIK_CITY_SCROLLER, QWIK_ROUTER_SCROLLER, QwikCityMockProvider, QwikCityProvider, QwikRouterMockProvider, QwikRouterProvider, RouterOutlet, ServiceWorkerRegister, createRenderer, getRequestEvent, globalAction$, globalActionQrl, omitProps, routeAction$, routeActionQrl, routeLoader$, routeLoaderQrl, server$, serverQrl, untypedAppUrl, useContent, useDocumentHead, useHttpStatus, useLocation, useNavigate, usePreventNavigate$, usePreventNavigateQrl, useQwikRouter, valibot$, valibotQrl, validator$, validatorQrl, z, zod$, zodQrl };