@qwik.dev/router 2.0.0-beta.31 → 2.0.0-beta.34

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.
@@ -509,7 +509,7 @@ function findChild(node, part, partLower, parts, partIndex, params) {
509
509
  return void 0;
510
510
  }
511
511
  function findIndexNode(node) {
512
- if (node._I || node._G) {
512
+ if (node._I || node._G != null) {
513
513
  return {
514
514
  target: node,
515
515
  groups: []
@@ -529,7 +529,7 @@ function findIndexNode(node) {
529
529
  return void 0;
530
530
  }
531
531
  function resolveLoaders(root, node, gatheredLayouts) {
532
- if (node._G) {
532
+ if (node._G != null) {
533
533
  const keys = node._G.split("/").filter((p) => p.length > 0);
534
534
  const target = walkTrieKeys(root, keys);
535
535
  if (!target) {
@@ -537,7 +537,7 @@ function resolveLoaders(root, node, gatheredLayouts) {
537
537
  }
538
538
  let targetNode = target.node;
539
539
  const targetLayouts = target.layouts;
540
- if (!targetNode._I && !targetNode._G) {
540
+ if (!targetNode._I && targetNode._G == null) {
541
541
  const indexResult = findIndexNode(targetNode);
542
542
  if (indexResult) {
543
543
  for (const g of indexResult.groups) {
@@ -650,14 +650,14 @@ function matchRouteTree(root = {}, pathname) {
650
650
  collectNodeMeta(node, found.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef);
651
651
  }
652
652
  if (matched && !done && i === len) {
653
- if (!node._I && !node._G) {
653
+ if (!node._I && node._G == null) {
654
654
  const indexResult = findIndexNode(node);
655
655
  if (indexResult) {
656
656
  collectNodeMeta(indexResult.target, indexResult.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef);
657
657
  node = indexResult.target;
658
658
  }
659
659
  }
660
- if (!node._I && !node._G && node._A) {
660
+ if (!node._I && node._G == null && node._A) {
661
661
  const next = node._A;
662
662
  const paramName = next._P;
663
663
  params[paramName] = "";
@@ -670,7 +670,7 @@ function matchRouteTree(root = {}, pathname) {
670
670
  menuLoaderRef.v = node._N;
671
671
  }
672
672
  }
673
- if (!node._I && !node._G) {
673
+ if (!node._I && node._G == null) {
674
674
  const restInfo = findRestNode(node);
675
675
  if (restInfo) {
676
676
  const next = restInfo.next;
@@ -280,7 +280,7 @@ function findChild(node, part, partLower, parts, partIndex, params) {
280
280
  return void 0;
281
281
  }
282
282
  function findIndexNode(node) {
283
- if (node._I || node._G) {
283
+ if (node._I || node._G != null) {
284
284
  return {
285
285
  target: node,
286
286
  groups: []
@@ -300,7 +300,7 @@ function findIndexNode(node) {
300
300
  return void 0;
301
301
  }
302
302
  function resolveLoaders(root, node, gatheredLayouts) {
303
- if (node._G) {
303
+ if (node._G != null) {
304
304
  const keys = node._G.split("/").filter((p) => p.length > 0);
305
305
  const target = walkTrieKeys(root, keys);
306
306
  if (!target) {
@@ -308,7 +308,7 @@ function resolveLoaders(root, node, gatheredLayouts) {
308
308
  }
309
309
  let targetNode = target.node;
310
310
  const targetLayouts = target.layouts;
311
- if (!targetNode._I && !targetNode._G) {
311
+ if (!targetNode._I && targetNode._G == null) {
312
312
  const indexResult = findIndexNode(targetNode);
313
313
  if (indexResult) {
314
314
  for (const g of indexResult.groups) {
@@ -421,14 +421,14 @@ function matchRouteTree(root = {}, pathname) {
421
421
  collectNodeMeta(node, found.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef);
422
422
  }
423
423
  if (matched && !done && i === len) {
424
- if (!node._I && !node._G) {
424
+ if (!node._I && node._G == null) {
425
425
  const indexResult = findIndexNode(node);
426
426
  if (indexResult) {
427
427
  collectNodeMeta(indexResult.target, indexResult.groups, layouts, errorLoaderRef, notFoundLoaderRef, menuLoaderRef);
428
428
  node = indexResult.target;
429
429
  }
430
430
  }
431
- if (!node._I && !node._G && node._A) {
431
+ if (!node._I && node._G == null && node._A) {
432
432
  const next = node._A;
433
433
  const paramName = next._P;
434
434
  params[paramName] = "";
@@ -441,7 +441,7 @@ function matchRouteTree(root = {}, pathname) {
441
441
  menuLoaderRef.v = node._N;
442
442
  }
443
443
  }
444
- if (!node._I && !node._G) {
444
+ if (!node._I && node._G == null) {
445
445
  const restInfo = findRestNode(node);
446
446
  if (restInfo) {
447
447
  const next = restInfo.next;
package/lib/index.d.ts CHANGED
@@ -400,8 +400,6 @@ export declare interface FormProps<O, I> extends Omit<QwikJSX.IntrinsicElements[
400
400
  * Defaults to `false`
401
401
  */
402
402
  spaReset?: boolean;
403
- /** Event handler executed right when the form is submitted. */
404
- onSubmit$?: QRLEventHandlerMulti<SubmitEvent, HTMLFormElement> | undefined;
405
403
  /** Event handler executed right after the action is executed successfully and returns some data. */
406
404
  onSubmitCompleted$?: QRLEventHandlerMulti<CustomEvent<FormSubmitSuccessDetail<O>>, HTMLFormElement> | undefined;
407
405
  key?: string | number | null;
@@ -459,22 +457,39 @@ export declare const Link: Component<LinkProps>;
459
457
  /** @public */
460
458
  export declare interface LinkProps extends AnchorAttributes {
461
459
  /**
462
- * **Defaults to _true_.**
460
+ * @deprecated Use `prefetchBundle` and `prefetchData` instead for more granular control over what
461
+ * is prefetched and when. This prop will be removed in a future major version.
463
462
  *
464
- * Whether Qwik should prefetch and cache the target page of this **`Link`**, this includes
465
- * invoking any **`routeLoader$`**, **`onGet`**, etc.
463
+ * Legacy prefetch control for this **`Link`**.
466
464
  *
467
- * This **improves UX performance** for client-side (**SPA**) navigations.
465
+ * Setting this value to **`"js"`** will prefetch only javascript bundles required to render this
466
+ * page on the client when the link becomes visible. Setting this value to **`true`** will
467
+ * prefetch both javascript bundles and route data when the link becomes visible. Setting this
468
+ * value to **`false`** will disable prefetching altogether.
469
+ */
470
+ prefetch?: boolean | 'js';
471
+ /**
472
+ * Controls when Qwik should prefetch the javascript bundles required to render this **`Link`**
473
+ * target during client-side navigation.
468
474
  *
469
- * Prefetching occurs when a the Link enters the viewport in production (**`q-e:qvisible`**), or
470
- * with **`mouseover`/`focus`** during dev.
475
+ * Defaults to **`"visible"`**.
471
476
  *
472
477
  * Prefetching will not occur if the user has the **data saver** setting enabled.
478
+ */
479
+ prefetchBundle?: PrefetchStrategy;
480
+ /**
481
+ * Controls when Qwik should prefetch and cache route data for this **`Link`** target, including
482
+ * invoking any **`routeLoader$`**, **`onGet`**, etc.
483
+ *
484
+ * Defaults to **`"intent"`**. When using the deprecated **`prefetch="js"`** prop, route data
485
+ * prefetching defaults to **`"off"`**.
486
+ *
487
+ * Prefetching route data can improve client-side navigation performance for pages that wait on
488
+ * loaders, server handlers, databases, or API calls.
473
489
  *
474
- * Setting this value to **`"js"`** will prefetch only javascript bundles required to render this
475
- * page on the client, **`false`** will disable prefetching altogether.
490
+ * Prefetching will not occur if the user has the **data saver** setting enabled.
476
491
  */
477
- prefetch?: boolean | 'js';
492
+ prefetchData?: PrefetchStrategy;
478
493
  reload?: boolean;
479
494
  replaceState?: boolean;
480
495
  scroll?: boolean;
@@ -564,6 +579,29 @@ export declare type PageModule = RouteModule & {
564
579
  /** @public */
565
580
  export declare type PathParams = Record<string, string>;
566
581
 
582
+ /**
583
+ * Defines when link prefetching should be triggered.
584
+ *
585
+ * @public
586
+ */
587
+ export declare type PrefetchStrategy =
588
+ /**
589
+ * Prefetch when the user commits to navigating.
590
+ *
591
+ * Triggered by `pointerdown` or the `Enter` key.
592
+ */
593
+ 'commit'
594
+ /**
595
+ * Prefetch when the user shows navigation intent.
596
+ *
597
+ * Triggered by `pointerenter`, hover, or focus.
598
+ */
599
+ | 'intent'
600
+ /** Prefetch when the link becomes visible in the viewport. */
601
+ | 'visible'
602
+ /** Disable link prefetching. */
603
+ | 'off';
604
+
567
605
  declare type Prettify<T> = {} & {
568
606
  [K in keyof T]: T[K];
569
607
  };
@@ -1,4 +1,4 @@
1
- import { componentQrl, inlinedQrl, useErrorBoundary, useOnWindow, _captures, _jsxSorted, Slot, useSignal, untrack, _qrlSync, useVisibleTaskQrl, isDev, _jsxSplit, _getConstProps, _getVarProps, eventQrl, isServer, useStylesQrl, useServerData, useStore, useContextProvider, useTaskQrl, getLocale, noSerialize, isBrowser, useContext, SkipRender, implicit$FirstArg, withLocale, _wrapProp, _restProps, _fnSignal, createElement } from '@qwik.dev/core';
1
+ import { componentQrl, inlinedQrl, useErrorBoundary, useOnWindow, _captures, _jsxSorted, Slot, useSignal, untrack, _qrlSync, useVisibleTaskQrl, _jsxSplit, _getConstProps, _getVarProps, isDev, eventQrl, isServer, useStylesQrl, useServerData, useStore, useContextProvider, useTaskQrl, getLocale, noSerialize, isBrowser, useContext, SkipRender, implicit$FirstArg, withLocale, _wrapProp, _restProps, _fnSignal, createElement } from '@qwik.dev/core';
2
2
  import { Fragment } from '@qwik.dev/core/jsx-runtime';
3
3
  import { g as getClientNavPath, s as shouldPreload, p as preloadRouteBundles, l as loadClientData, i as isSamePath, c as createLoaderSignal, a as createDocumentHead, b as loadRoute, r as resolveHead, C as CLIENT_DATA_CACHE, d as isSameOrigin, Q as Q_ROUTE, D as DEFAULT_LOADERS_SERIALIZATION_STRATEGY, t as toUrl, e as clientNavigate, f as toPath, h as QFN_KEY, j as QACTION_KEY, k as QDATA_KEY } from './chunks/routing.qwik.mjs';
4
4
  import { u as useNavigate, a as useLocation, b as useQwikRouterEnv, C as ContentContext, c as ContentInternalContext, D as DocumentHeadContext, H as HttpStatusContext, R as RouteLocationContext, d as RouteNavigateContext, e as RouteStateContext, f as RouteActionContext, g as RoutePreventNavigateContext, h as useAction, i as useDocumentHead } from './chunks/use-functions.qwik.mjs';
@@ -31,34 +31,56 @@ const Link = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl((props) =>
31
31
  const loc = useLocation();
32
32
  const originalHref = props.href;
33
33
  const anchorRef = useSignal();
34
- const { onClick$, prefetch: prefetchProp, reload, replaceState, scroll, ...linkProps } = /* @__PURE__ */ (() => props)();
34
+ const { onClick$, prefetch: prefetchProp, reload, replaceState, scroll, prefetchBundle: prefetchBundleProp = "visible", prefetchData: prefetchDataProp = prefetchProp === "js" ? "off" : "intent", ...linkProps } = props;
35
35
  const clientNavPath = untrack(getClientNavPath, {
36
36
  ...linkProps,
37
37
  reload
38
38
  }, loc);
39
39
  linkProps.href = clientNavPath || originalHref;
40
- const prefetchData = !!clientNavPath && prefetchProp !== false && prefetchProp !== "js" || void 0;
41
- const prefetch = prefetchData || !!clientNavPath && prefetchProp !== false && untrack(shouldPreload, clientNavPath, loc);
42
- const handlePrefetch = prefetch ? /* @__PURE__ */ inlinedQrl((_, elm) => {
40
+ const isDepratedPrefetchDisabled = prefetchProp === false;
41
+ const shouldPrefetch = untrack(shouldPreload, clientNavPath, loc);
42
+ const shouldPrefetchBundle = !!clientNavPath && prefetchBundleProp !== "off" && shouldPrefetch && !isDepratedPrefetchDisabled;
43
+ const shouldPrefetchData = !!clientNavPath && prefetchDataProp !== "off" && shouldPrefetch && !isDepratedPrefetchDisabled;
44
+ const handleBundlePrefetch = shouldPrefetchBundle ? /* @__PURE__ */ inlinedQrl((_, elm) => {
43
45
  if (navigator.connection?.saveData) {
44
46
  return;
45
47
  }
46
48
  if (elm && elm.href) {
47
49
  const url = new URL(elm.href);
48
50
  preloadRouteBundles(url.pathname);
49
- if (elm.hasAttribute("data-prefetch")) {
50
- loadClientData(url, {
51
- preloadRouteBundles: false,
52
- isPrefetch: true
53
- });
54
- }
55
51
  }
56
- }, "Link_component_handlePrefetch_AGvVXzXKbms") : void 0;
52
+ }, "Link_component_handleBundlePrefetch_ZttUP0kLKj0") : null;
53
+ const handleDataPrefetch = shouldPrefetchData ? /* @__PURE__ */ inlinedQrl((_, elm) => {
54
+ if (navigator.connection?.saveData) {
55
+ return;
56
+ }
57
+ if (elm && elm.href) {
58
+ const url = new URL(elm.href);
59
+ loadClientData(url, {
60
+ preloadRouteBundles: false,
61
+ isPrefetch: true
62
+ });
63
+ }
64
+ }, "Link_component_handleDataPrefetch_lcd4O9LHbbA") : null;
57
65
  const preventDefault = clientNavPath ? _qrlSync((event) => {
58
66
  if (!(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) {
59
67
  event.preventDefault();
60
68
  }
61
- }, "event=>{if(!(event.metaKey||event.ctrlKey||event.shiftKey||event.altKey)){event.preventDefault();}}") : void 0;
69
+ }, "event=>{if(!(event.metaKey||event.ctrlKey||event.shiftKey||event.altKey)){event.preventDefault();}}") : null;
70
+ const prefetchData = /* @__PURE__ */ inlinedQrl(async (_, elm) => {
71
+ const handleDataPrefetch2 = _captures[0];
72
+ handleDataPrefetch2?.(null, elm);
73
+ }, "Link_component_prefetchData_UwVx4NhHkSo", [
74
+ handleDataPrefetch
75
+ ]);
76
+ const onEnterKeyDown = /* @__PURE__ */ inlinedQrl((event, element) => {
77
+ const prefetchData2 = _captures[0];
78
+ if (event.key === "Enter") {
79
+ prefetchData2(null, element);
80
+ }
81
+ }, "Link_component_onEnterKeyDown_z274ws00irY", [
82
+ prefetchData
83
+ ]);
62
84
  const handleClientSideNavigation = clientNavPath ? /* @__PURE__ */ inlinedQrl((event, elm) => {
63
85
  const nav2 = _captures[0], reload2 = _captures[1], replaceState2 = _captures[2], scroll2 = _captures[3];
64
86
  if (event.defaultPrevented) {
@@ -78,13 +100,13 @@ const Link = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl((props) =>
78
100
  reload,
79
101
  replaceState,
80
102
  scroll
81
- ]) : void 0;
103
+ ]) : null;
82
104
  const handlePreload = /* @__PURE__ */ inlinedQrl((_, elm) => {
83
105
  const url = new URL(elm.href);
84
106
  preloadRouteBundles(url.pathname, 1);
85
107
  }, "Link_component_handlePreload_AAemwtuBjsE");
86
108
  useVisibleTaskQrl(/* @__PURE__ */ inlinedQrl(({ track }) => {
87
- const anchorRef2 = _captures[0], handlePrefetch2 = _captures[1], linkProps2 = _captures[2], loc2 = _captures[3];
109
+ const anchorRef2 = _captures[0], handleBundlePrefetch2 = _captures[1], handleDataPrefetch2 = _captures[2], linkProps2 = _captures[3], loc2 = _captures[4], prefetchBundleProp2 = _captures[5], prefetchDataProp2 = _captures[6], prefetchProp2 = _captures[7];
88
110
  track(() => loc2.url.pathname);
89
111
  const handler = linkProps2.onQVisible$;
90
112
  if (handler) {
@@ -95,14 +117,26 @@ const Link = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl((props) =>
95
117
  handler?.(event, anchorRef2.value);
96
118
  }
97
119
  }
98
- if (!isDev && anchorRef2.value) {
99
- handlePrefetch2?.(void 0, anchorRef2.value);
120
+ const isProdOrTest = !isDev || undefined ;
121
+ if (isProdOrTest && anchorRef2.value) {
122
+ if (prefetchBundleProp2 === "visible" || // deprecated prop below, remove in favor of prefetchBundle
123
+ prefetchProp2 === "js" || prefetchProp2 === true) {
124
+ handleBundlePrefetch2?.(null, anchorRef2.value);
125
+ }
126
+ if (prefetchDataProp2 === "visible" || // deprecated prop below, remove in favor of prefetchData
127
+ prefetchProp2 === true) {
128
+ handleDataPrefetch2?.(null, anchorRef2.value);
129
+ }
100
130
  }
101
131
  }, "Link_component_useVisibleTask_xKeuRmnoNSA", [
102
132
  anchorRef,
103
- handlePrefetch,
133
+ handleBundlePrefetch,
134
+ handleDataPrefetch,
104
135
  linkProps,
105
- loc
136
+ loc,
137
+ prefetchBundleProp,
138
+ prefetchDataProp,
139
+ prefetchProp
106
140
  ]));
107
141
  return /* @__PURE__ */ _jsxSplit("a", {
108
142
  ref: anchorRef,
@@ -115,14 +149,21 @@ const Link = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl((props) =>
115
149
  onClick$,
116
150
  handleClientSideNavigation
117
151
  ],
118
- "data-prefetch": prefetchData,
119
- "q-e:mouseover": [
152
+ "q-e:pointerenter": [
120
153
  linkProps.onMouseOver$,
121
- handlePrefetch
154
+ prefetchDataProp === "intent" ? prefetchData : null
122
155
  ],
123
156
  "q-e:focus": [
124
157
  linkProps.onFocus$,
125
- handlePrefetch
158
+ prefetchDataProp === "intent" ? prefetchData : null
159
+ ],
160
+ "q-e:pointerdown": [
161
+ linkProps.onPointerDown$,
162
+ prefetchDataProp === "commit" ? prefetchData : null
163
+ ],
164
+ "q-e:keydown": [
165
+ linkProps.onKeyDown$,
166
+ prefetchDataProp === "commit" ? onEnterKeyDown : null
126
167
  ]
127
168
  }, {
128
169
  // We need to prevent the onQVisible$ from being called twice since it is handled in the visible task
@@ -176,9 +217,27 @@ const saveScrollHistory = (scrollState) => {
176
217
  history.replaceState(state, "");
177
218
  };
178
219
 
220
+ const Q_ROUTER_POPSTATE_EVENT = "qrouterpopstate";
221
+ const createCurrentPathTracker = (initialPath) => {
222
+ let currentPath = initialPath;
223
+ return (nextPath) => {
224
+ if (currentPath !== nextPath) {
225
+ currentPath = nextPath;
226
+ return true;
227
+ }
228
+ return false;
229
+ };
230
+ };
231
+ const dispatchRouterPopstate = (document1, href) => {
232
+ document1.dispatchEvent(new CustomEvent(Q_ROUTER_POPSTATE_EVENT, {
233
+ detail: {
234
+ href
235
+ }
236
+ }));
237
+ };
179
238
  const spaInit = eventQrl(/* @__PURE__ */ inlinedQrl((_, el) => {
180
239
  if (!window._qRouterSPA && !window._qRouterInitPopstate) {
181
- const currentPath = location.pathname + location.search;
240
+ const hasPathChanged = createCurrentPathTracker(location.pathname + location.search);
182
241
  const checkAndScroll = (scrollState) => {
183
242
  if (scrollState) {
184
243
  window.scrollTo(scrollState.x, scrollState.y);
@@ -205,21 +264,8 @@ const spaInit = eventQrl(/* @__PURE__ */ inlinedQrl((_, el) => {
205
264
  }
206
265
  window._qRouterScrollEnabled = false;
207
266
  clearTimeout(window._qRouterScrollDebounce);
208
- if (currentPath !== location.pathname + location.search) {
209
- const getContainer = (el2) => el2.closest("[q\\:container]:not([q\\:container=html]):not([q\\:container=text])");
210
- const container = getContainer(el);
211
- const domContainer = container.qContainer;
212
- const hostElement = domContainer.vNodeLocate(el);
213
- const nav = domContainer?.resolveContext(hostElement, {
214
- id: "qr-n"
215
- });
216
- if (nav) {
217
- nav(location.href, {
218
- type: "popstate"
219
- });
220
- } else {
221
- location.reload();
222
- }
267
+ if (hasPathChanged(location.pathname + location.search)) {
268
+ dispatchRouterPopstate(document, location.href);
223
269
  } else {
224
270
  if (history.scrollRestoration === "manual") {
225
271
  const scrollState = history.state?._qRouterScroll;
@@ -836,6 +882,9 @@ const useQwikRouter = (props) => {
836
882
  };
837
883
  _waitNextPage().catch((err) => {
838
884
  navigate();
885
+ if (err instanceof DOMException && err.name === "TimeoutError") {
886
+ throw new Error('View transition timed out. This can happen if you have "disableCache" in your browser devtools enabled.');
887
+ }
839
888
  throw err;
840
889
  }).finally(() => {
841
890
  container2.element.setAttribute?.(Q_ROUTE, $routeName$);
@@ -898,13 +947,16 @@ const useQwikMockRouter = (props) => {
898
947
  }, {
899
948
  deep: false
900
949
  });
901
- const loadersData = props.loaders?.reduce((acc, { loader, data }) => {
902
- acc[loader.__id] = data;
903
- return acc;
904
- }, {});
905
- const loaderState = useStore(loadersData ?? {}, {
906
- deep: false
907
- });
950
+ const container = _getContextContainer();
951
+ const loadersObject = {};
952
+ const loadersState = {};
953
+ if (props.loaders) {
954
+ for (const { loader, data } of props.loaders) {
955
+ const id = loader.__id;
956
+ loadersObject[id] = data;
957
+ loadersState[id] = createLoaderSignal(loadersObject, id, url, DEFAULT_LOADERS_SERIALIZATION_STRATEGY, container);
958
+ }
959
+ }
908
960
  const goto = props.goto ?? /* @__PURE__ */ inlinedQrl(async () => {
909
961
  console.warn("QwikRouterMockProvider: goto not provided");
910
962
  }, "useQwikMockRouter_goto_aViHFxQ1a3s");
@@ -929,7 +981,7 @@ const useQwikMockRouter = (props) => {
929
981
  useContextProvider(HttpStatusContext, httpStatus);
930
982
  useContextProvider(RouteLocationContext, routeLocation);
931
983
  useContextProvider(RouteNavigateContext, goto);
932
- useContextProvider(RouteStateContext, loaderState);
984
+ useContextProvider(RouteStateContext, loadersState);
933
985
  useContextProvider(RouteActionContext, actionState);
934
986
  const actionsMocks = props.actions?.reduce((acc, { action, handler }) => {
935
987
  acc[action.__id] = handler;
@@ -957,12 +1009,21 @@ const QwikRouterMockProvider = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inli
957
1009
  }, "QwikRouterMockProvider_component_IN4dVpT0x74"));
958
1010
  const QwikCityMockProvider = QwikRouterMockProvider;
959
1011
 
1012
+ const handleRouterPopstate = (nav, event) => {
1013
+ const href = event.detail?.href;
1014
+ if (href) {
1015
+ return nav(href, {
1016
+ type: "popstate"
1017
+ });
1018
+ }
1019
+ };
960
1020
  const RouterOutlet = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl(() => {
961
1021
  const serverData = useServerData("containerAttributes");
962
1022
  if (!serverData) {
963
1023
  throw new Error("PrefetchServiceWorker component must be rendered on the server.");
964
1024
  }
965
1025
  const internalContext = useContext(ContentInternalContext);
1026
+ const nav = useNavigate();
966
1027
  const contents = internalContext.value;
967
1028
  if (contents && contents.length > 0) {
968
1029
  const contentsLen = contents.length;
@@ -977,19 +1038,22 @@ const RouterOutlet = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl(()
977
1038
  !__EXPERIMENTAL__.noSPA && /* @__PURE__ */ _jsxSorted("script", {
978
1039
  "q-d:qinit": _qrlSync(() => {
979
1040
  ((w, h) => {
980
- if (!w._qcs && h.scrollRestoration === "manual") {
1041
+ if (!w._qcs) {
981
1042
  w._qcs = true;
982
1043
  const s = h.state?._qRouterScroll;
983
1044
  if (s) {
1045
+ h.scrollRestoration = "manual";
984
1046
  w.scrollTo(s.x, s.y);
985
1047
  }
986
1048
  document.dispatchEvent(new Event("qcinit"));
987
1049
  }
988
1050
  })(window, history);
989
- }, '()=>{((w,h)=>{if(!w._qcs&&h.scrollRestoration==="manual"){w._qcs=!0;const s=h.state?._qRouterScroll;if(s){w.scrollTo(s.x,s.y);}document.dispatchEvent(new Event("qcinit"));}})(window,history);}')
1051
+ }, '()=>{((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);}'),
1052
+ "q-d:qrouterpopstate": /* @__PURE__ */ inlinedQrl((event, _1, nav2) => handleRouterPopstate(nav2, event), "RouterOutlet_component_Fragment_script_q_d_qrouterpopstate_aRL6GvFyews"),
1053
+ "q:p": nav
990
1054
  }, {
991
1055
  "q-d:qcinit": spaInit
992
- }, null, 2, "Fn_1")
1056
+ }, null, 6, "Fn_1")
993
1057
  ], 1, "Fn_2");
994
1058
  }
995
1059
  return SkipRender;
@@ -1,10 +1,10 @@
1
- import { Http2ServerRequest } from 'http2';
2
- import { IncomingMessage } from 'http';
3
- import { NodeRequestNextFunction } from '@qwik.dev/router/middleware/node';
1
+ import type { Http2ServerRequest } from 'node:http2';
2
+ import type { IncomingMessage } from 'node:http';
3
+ import type { NodeRequestNextFunction } from '@qwik.dev/router/middleware/node';
4
4
  import type { QwikManifest } from '@qwik.dev/core/optimizer';
5
5
  import type { Render } from '@qwik.dev/core/server';
6
6
  import type { ServerRenderOptions } from '@qwik.dev/router/middleware/request-handler';
7
- import { ServerResponse } from 'http';
7
+ import type { ServerResponse } from 'node:http';
8
8
 
9
9
  declare interface AwsOpt {
10
10
  render: Render;
@@ -18,13 +18,7 @@ declare interface AwsOpt {
18
18
  export declare const createQwikCity: typeof createQwikRouter;
19
19
 
20
20
  /** @public */
21
- export declare function createQwikRouter(opts: AwsOpt): {
22
- fixPath: (pathT: string) => string;
23
- router: (req: IncomingMessage | Http2ServerRequest, res: ServerResponse, next: NodeRequestNextFunction) => Promise<void>;
24
- staticFile: (req: IncomingMessage | Http2ServerRequest, res: ServerResponse, next: (e?: any) => void) => Promise<void>;
25
- notFound: (req: IncomingMessage | Http2ServerRequest, res: ServerResponse, next: (e: any) => void) => Promise<void>;
26
- handle: (req: any, res: any) => void;
27
- };
21
+ export declare function createQwikRouter(opts: AwsOpt): QwikRouterAwsLambdaMiddleware;
28
22
 
29
23
  /** @public */
30
24
  export declare interface PlatformAwsLambda extends Object {
@@ -36,6 +30,16 @@ export declare interface PlatformAwsLambda extends Object {
36
30
  */
37
31
  export declare type QwikCityAwsLambdaOptions = QwikRouterAwsLambdaOptions;
38
32
 
33
+ /** @public */
34
+ export declare interface QwikRouterAwsLambdaMiddleware {
35
+ fixPath: (pathT: string) => string;
36
+ router: (req: IncomingMessage | Http2ServerRequest, res: ServerResponse, next: NodeRequestNextFunction) => Promise<void>;
37
+ staticFile: (req: IncomingMessage | Http2ServerRequest, res: ServerResponse, next: (e?: any) => void) => Promise<void>;
38
+ /** @deprecated `router` handles 404 responses. Will be removed in V3. */
39
+ notFound: (req: IncomingMessage | Http2ServerRequest, res: ServerResponse, next: (e?: any) => void) => Promise<void>;
40
+ handle: (req: any, res: any) => void;
41
+ }
42
+
39
43
  /** @public */
40
44
  export declare interface QwikRouterAwsLambdaOptions extends ServerRenderOptions {
41
45
  }
@@ -2,7 +2,7 @@ import { createQwikRouter as createQwikRouter$1 } from '@qwik.dev/router/middlew
2
2
 
3
3
  function createQwikRouter(opts) {
4
4
  try {
5
- const { router, staticFile, notFound } = createQwikRouter$1({
5
+ const { router, staticFile } = createQwikRouter$1({
6
6
  render: opts.render,
7
7
  manifest: opts.manifest,
8
8
  static: {
@@ -31,11 +31,12 @@ function createQwikRouter(opts) {
31
31
  req.url = fixPath(req.url);
32
32
  staticFile(req, res, () => {
33
33
  router(req, res, () => {
34
- notFound(req, res, () => {
35
- });
36
34
  });
37
35
  });
38
36
  };
37
+ const notFound = async (_req, _res, next) => {
38
+ next();
39
+ };
39
40
  return {
40
41
  fixPath,
41
42
  router,
@@ -8,11 +8,7 @@ import type { ServerRenderOptions } from '@qwik.dev/router/middleware/request-ha
8
8
  export declare const createQwikCity: typeof createQwikRouter;
9
9
 
10
10
  /** @public */
11
- export declare function createQwikRouter(opts: QwikRouterBunOptions): {
12
- router: (request: Request) => Promise<Response | null>;
13
- notFound: (request: Request) => Promise<Response>;
14
- staticFile: (request: Request) => Promise<Response | null>;
15
- };
11
+ export declare function createQwikRouter(opts: QwikRouterBunOptions): QwikRouterBunMiddleware;
16
12
 
17
13
  /**
18
14
  * @deprecated Use `QwikRouterBunOptions` instead. Will be removed in V3
@@ -20,6 +16,14 @@ export declare function createQwikRouter(opts: QwikRouterBunOptions): {
20
16
  */
21
17
  export declare type QwikCityBunOptions = QwikRouterBunOptions;
22
18
 
19
+ /** @public */
20
+ export declare interface QwikRouterBunMiddleware {
21
+ router: (request: Request) => Promise<Response | null>;
22
+ /** @deprecated `router` handles 404 responses. Will be removed in V3. */
23
+ notFound: (request: Request) => Promise<Response>;
24
+ staticFile: (request: Request) => Promise<Response | null>;
25
+ }
26
+
23
27
  /** @public */
24
28
  export declare interface QwikRouterBunOptions extends ServerRenderOptions {
25
29
  /** Options for serving static files */
@@ -1,6 +1,6 @@
1
1
  import { isDev } from '@qwik.dev/core/build';
2
2
  import { setServerPlatform } from '@qwik.dev/core/server';
3
- import { _TextEncoderStream_polyfill, isStaticPath, getNotFound, mergeHeadersCookies, requestHandler } from '@qwik.dev/router/middleware/request-handler';
3
+ import { _TextEncoderStream_polyfill, isStaticPath, mergeHeadersCookies, requestHandler } from '@qwik.dev/router/middleware/request-handler';
4
4
  import { join, extname } from 'node:path';
5
5
  import { M as MIME_TYPES, n as normalizeRequestUrl } from '../../chunks/url.mjs';
6
6
 
@@ -77,27 +77,8 @@ function createQwikRouter(opts) {
77
77
  });
78
78
  }
79
79
  }
80
- const notFound = async (request) => {
81
- try {
82
- const url = getRequestUrl(request, opts);
83
- const notFoundHtml = !request.headers.get("accept")?.includes("text/html") || isStaticPath(request.method || "GET", url) ? "Not Found" : getNotFound(url.pathname);
84
- return new Response(notFoundHtml, {
85
- status: 404,
86
- headers: {
87
- "Content-Type": "text/html; charset=utf-8",
88
- "X-Not-Found": url.pathname
89
- }
90
- });
91
- } catch (e) {
92
- console.error(e);
93
- return new Response(isDev ? String(e || "Error") : "Internal Server Error", {
94
- status: 500,
95
- headers: {
96
- "Content-Type": "text/plain; charset=utf-8",
97
- "X-Error": "bun-server"
98
- }
99
- });
100
- }
80
+ const notFound = async (_request) => {
81
+ return null;
101
82
  };
102
83
  const openStaticFile = async (url) => {
103
84
  const pathname = url.pathname;
@@ -8,11 +8,7 @@ import type { ServerRenderOptions } from '@qwik.dev/router/middleware/request-ha
8
8
  export declare const createQwikCity: typeof createQwikRouter;
9
9
 
10
10
  /** @public */
11
- export declare function createQwikRouter(opts: QwikRouterDenoOptions): {
12
- router: (request: Request, info: ServeHandlerInfo) => Promise<Response | null>;
13
- notFound: (request: Request) => Promise<Response>;
14
- staticFile: (request: Request) => Promise<Response | null>;
15
- };
11
+ export declare function createQwikRouter(opts: QwikRouterDenoOptions): QwikRouterDenoMiddleware;
16
12
 
17
13
  /** @public */
18
14
  export declare interface NetAddr {
@@ -27,6 +23,14 @@ export declare interface NetAddr {
27
23
  */
28
24
  export declare type QwikCityDenoOptions = QwikRouterDenoOptions;
29
25
 
26
+ /** @public */
27
+ export declare interface QwikRouterDenoMiddleware {
28
+ router: (request: Request, info: ServeHandlerInfo) => Promise<Response | null>;
29
+ /** @deprecated `router` handles 404 responses. Will be removed in V3. */
30
+ notFound: (request: Request) => Promise<Response>;
31
+ staticFile: (request: Request) => Promise<Response | null>;
32
+ }
33
+
30
34
  /** @public */
31
35
  export declare interface QwikRouterDenoOptions extends ServerRenderOptions {
32
36
  /** Options for serving static files */