@qwik.dev/router 2.0.0-beta.32 → 2.0.0-beta.35
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.
- package/lib/chunks/worker-thread.qwik.mjs +13 -6
- package/lib/index.d.ts +49 -9
- package/lib/index.qwik.mjs +79 -31
- package/lib/middleware/node/index.mjs +173 -22
- package/lib/middleware/request-handler/index.mjs +13 -6
- package/lib/vite/index.mjs +36 -15
- package/package.json +3 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parentPort } from 'node:worker_threads';
|
|
2
|
-
import { isDev, _UNINITIALIZED, _deserialize, _verifySerializable, _serialize } from '@qwik.dev/core/internal';
|
|
2
|
+
import { isDev, _UNINITIALIZED, _deserialize, _verifySerializable, _serialize, _getAsyncLocalStorage } from '@qwik.dev/core/internal';
|
|
3
3
|
import { withLocale, inlinedQrl } from '@qwik.dev/core';
|
|
4
4
|
import { A as AbortMessage$1 } from './redirect-handler.mjs';
|
|
5
5
|
import { isServer } from '@qwik.dev/core/build';
|
|
@@ -1616,8 +1616,11 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
|
|
|
1616
1616
|
});
|
|
1617
1617
|
pipeSource = readable.pipeThrough(capture);
|
|
1618
1618
|
}
|
|
1619
|
+
let pipeError;
|
|
1619
1620
|
const pipe = pipeSource.pipeTo(writableStream, {
|
|
1620
1621
|
preventClose: true
|
|
1622
|
+
}).catch((error) => {
|
|
1623
|
+
pipeError = error;
|
|
1621
1624
|
});
|
|
1622
1625
|
const stream = writable.getWriter();
|
|
1623
1626
|
const status = requestEv.status();
|
|
@@ -1648,6 +1651,9 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
|
|
|
1648
1651
|
await stream.close();
|
|
1649
1652
|
await pipe;
|
|
1650
1653
|
}
|
|
1654
|
+
if (pipeError) {
|
|
1655
|
+
throw pipeError;
|
|
1656
|
+
}
|
|
1651
1657
|
if (eTagCacheKey && cacheChunks && cacheChunks.length > 0) {
|
|
1652
1658
|
const totalLength = cacheChunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
1653
1659
|
const combined = new Uint8Array(totalLength);
|
|
@@ -2103,11 +2109,12 @@ function createRequestEvent(...args) {
|
|
|
2103
2109
|
|
|
2104
2110
|
let _asyncRequestStore;
|
|
2105
2111
|
if (isServer) {
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2112
|
+
const AsyncLocalStorage = _getAsyncLocalStorage();
|
|
2113
|
+
if (AsyncLocalStorage) {
|
|
2114
|
+
_asyncRequestStore = new AsyncLocalStorage();
|
|
2115
|
+
} else {
|
|
2116
|
+
console.warn("\n=====================\n Qwik Router Warning:\n AsyncLocalStorage is not available, continuing without it.\n This impacts concurrent async server calls, where they lose access to the ServerRequestEv object.\n=====================\n\n");
|
|
2117
|
+
}
|
|
2111
2118
|
}
|
|
2112
2119
|
|
|
2113
2120
|
const ESCAPE_HTML = /[&<>]/g;
|
package/lib/index.d.ts
CHANGED
|
@@ -457,22 +457,39 @@ export declare const Link: Component<LinkProps>;
|
|
|
457
457
|
/** @public */
|
|
458
458
|
export declare interface LinkProps extends AnchorAttributes {
|
|
459
459
|
/**
|
|
460
|
-
*
|
|
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.
|
|
461
462
|
*
|
|
462
|
-
*
|
|
463
|
-
* invoking any **`routeLoader$`**, **`onGet`**, etc.
|
|
463
|
+
* Legacy prefetch control for this **`Link`**.
|
|
464
464
|
*
|
|
465
|
-
*
|
|
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.
|
|
466
474
|
*
|
|
467
|
-
*
|
|
468
|
-
* with **`mouseover`/`focus`** during dev.
|
|
475
|
+
* Defaults to **`"visible"`**.
|
|
469
476
|
*
|
|
470
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.
|
|
471
489
|
*
|
|
472
|
-
*
|
|
473
|
-
* page on the client, **`false`** will disable prefetching altogether.
|
|
490
|
+
* Prefetching will not occur if the user has the **data saver** setting enabled.
|
|
474
491
|
*/
|
|
475
|
-
|
|
492
|
+
prefetchData?: PrefetchStrategy;
|
|
476
493
|
reload?: boolean;
|
|
477
494
|
replaceState?: boolean;
|
|
478
495
|
scroll?: boolean;
|
|
@@ -562,6 +579,29 @@ export declare type PageModule = RouteModule & {
|
|
|
562
579
|
/** @public */
|
|
563
580
|
export declare type PathParams = Record<string, string>;
|
|
564
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
|
+
|
|
565
605
|
declare type Prettify<T> = {} & {
|
|
566
606
|
[K in keyof T]: T[K];
|
|
567
607
|
};
|
package/lib/index.qwik.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { componentQrl, inlinedQrl, useErrorBoundary, useOnWindow, _captures, _jsxSorted, Slot, useSignal, untrack, _qrlSync, useVisibleTaskQrl,
|
|
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';
|
|
@@ -26,39 +26,62 @@ const ErrorBoundary = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl((p
|
|
|
26
26
|
return /* @__PURE__ */ _jsxSorted(Slot, null, null, null, 3, "bA_1");
|
|
27
27
|
}, "ErrorBoundary_component_pOa6vjtC7ik"));
|
|
28
28
|
|
|
29
|
+
const __vite_import_meta_env__ = {};
|
|
29
30
|
const Link = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl((props) => {
|
|
30
31
|
const nav = useNavigate();
|
|
31
32
|
const loc = useLocation();
|
|
32
33
|
const originalHref = props.href;
|
|
33
34
|
const anchorRef = useSignal();
|
|
34
|
-
const { onClick$, prefetch: prefetchProp, reload, replaceState, scroll,
|
|
35
|
+
const { onClick$, prefetch: prefetchProp, reload, replaceState, scroll, prefetchBundle: prefetchBundleProp = "visible", prefetchData: prefetchDataProp = prefetchProp === "js" ? "off" : "intent", ...linkProps } = props;
|
|
35
36
|
const clientNavPath = untrack(getClientNavPath, {
|
|
36
37
|
...linkProps,
|
|
37
38
|
reload
|
|
38
39
|
}, loc);
|
|
39
40
|
linkProps.href = clientNavPath || originalHref;
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
41
|
+
const isDepratedPrefetchDisabled = prefetchProp === false;
|
|
42
|
+
const shouldPrefetch = untrack(shouldPreload, clientNavPath, loc);
|
|
43
|
+
const shouldPrefetchBundle = !!clientNavPath && prefetchBundleProp !== "off" && shouldPrefetch && !isDepratedPrefetchDisabled;
|
|
44
|
+
const shouldPrefetchData = !!clientNavPath && prefetchDataProp !== "off" && shouldPrefetch && !isDepratedPrefetchDisabled;
|
|
45
|
+
const handleBundlePrefetch = shouldPrefetchBundle ? /* @__PURE__ */ inlinedQrl((_, elm) => {
|
|
43
46
|
if (navigator.connection?.saveData) {
|
|
44
47
|
return;
|
|
45
48
|
}
|
|
46
49
|
if (elm && elm.href) {
|
|
47
50
|
const url = new URL(elm.href);
|
|
48
51
|
preloadRouteBundles(url.pathname);
|
|
49
|
-
if (elm.hasAttribute("data-prefetch")) {
|
|
50
|
-
loadClientData(url, {
|
|
51
|
-
preloadRouteBundles: false,
|
|
52
|
-
isPrefetch: true
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
52
|
}
|
|
56
|
-
}, "
|
|
53
|
+
}, "Link_component_handleBundlePrefetch_ZttUP0kLKj0") : null;
|
|
54
|
+
const handleDataPrefetch = shouldPrefetchData ? /* @__PURE__ */ inlinedQrl((_, elm) => {
|
|
55
|
+
if (navigator.connection?.saveData) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (elm && elm.href) {
|
|
59
|
+
const url = new URL(elm.href);
|
|
60
|
+
loadClientData(url, {
|
|
61
|
+
preloadRouteBundles: false,
|
|
62
|
+
isPrefetch: true
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}, "Link_component_handleDataPrefetch_lcd4O9LHbbA") : null;
|
|
57
66
|
const preventDefault = clientNavPath ? _qrlSync((event) => {
|
|
58
67
|
if (!(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) {
|
|
59
68
|
event.preventDefault();
|
|
60
69
|
}
|
|
61
|
-
}, "event=>{if(!(event.metaKey||event.ctrlKey||event.shiftKey||event.altKey)){event.preventDefault();}}") :
|
|
70
|
+
}, "event=>{if(!(event.metaKey||event.ctrlKey||event.shiftKey||event.altKey)){event.preventDefault();}}") : null;
|
|
71
|
+
const prefetchData = /* @__PURE__ */ inlinedQrl(async (_, elm) => {
|
|
72
|
+
const handleDataPrefetch2 = _captures[0];
|
|
73
|
+
handleDataPrefetch2?.(null, elm);
|
|
74
|
+
}, "Link_component_prefetchData_UwVx4NhHkSo", [
|
|
75
|
+
handleDataPrefetch
|
|
76
|
+
]);
|
|
77
|
+
const onEnterKeyDown = /* @__PURE__ */ inlinedQrl((event, element) => {
|
|
78
|
+
const prefetchData2 = _captures[0];
|
|
79
|
+
if (event.key === "Enter") {
|
|
80
|
+
prefetchData2(null, element);
|
|
81
|
+
}
|
|
82
|
+
}, "Link_component_onEnterKeyDown_z274ws00irY", [
|
|
83
|
+
prefetchData
|
|
84
|
+
]);
|
|
62
85
|
const handleClientSideNavigation = clientNavPath ? /* @__PURE__ */ inlinedQrl((event, elm) => {
|
|
63
86
|
const nav2 = _captures[0], reload2 = _captures[1], replaceState2 = _captures[2], scroll2 = _captures[3];
|
|
64
87
|
if (event.defaultPrevented) {
|
|
@@ -78,13 +101,13 @@ const Link = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl((props) =>
|
|
|
78
101
|
reload,
|
|
79
102
|
replaceState,
|
|
80
103
|
scroll
|
|
81
|
-
]) :
|
|
104
|
+
]) : null;
|
|
82
105
|
const handlePreload = /* @__PURE__ */ inlinedQrl((_, elm) => {
|
|
83
106
|
const url = new URL(elm.href);
|
|
84
107
|
preloadRouteBundles(url.pathname, 1);
|
|
85
108
|
}, "Link_component_handlePreload_AAemwtuBjsE");
|
|
86
109
|
useVisibleTaskQrl(/* @__PURE__ */ inlinedQrl(({ track }) => {
|
|
87
|
-
const anchorRef2 = _captures[0],
|
|
110
|
+
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
111
|
track(() => loc2.url.pathname);
|
|
89
112
|
const handler = linkProps2.onQVisible$;
|
|
90
113
|
if (handler) {
|
|
@@ -95,14 +118,26 @@ const Link = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl((props) =>
|
|
|
95
118
|
handler?.(event, anchorRef2.value);
|
|
96
119
|
}
|
|
97
120
|
}
|
|
98
|
-
|
|
99
|
-
|
|
121
|
+
const isProdOrTest = !isDev || __vite_import_meta_env__?.TEST;
|
|
122
|
+
if (isProdOrTest && anchorRef2.value) {
|
|
123
|
+
if (prefetchBundleProp2 === "visible" || // deprecated prop below, remove in favor of prefetchBundle
|
|
124
|
+
prefetchProp2 === "js" || prefetchProp2 === true) {
|
|
125
|
+
handleBundlePrefetch2?.(null, anchorRef2.value);
|
|
126
|
+
}
|
|
127
|
+
if (prefetchDataProp2 === "visible" || // deprecated prop below, remove in favor of prefetchData
|
|
128
|
+
prefetchProp2 === true) {
|
|
129
|
+
handleDataPrefetch2?.(null, anchorRef2.value);
|
|
130
|
+
}
|
|
100
131
|
}
|
|
101
132
|
}, "Link_component_useVisibleTask_xKeuRmnoNSA", [
|
|
102
133
|
anchorRef,
|
|
103
|
-
|
|
134
|
+
handleBundlePrefetch,
|
|
135
|
+
handleDataPrefetch,
|
|
104
136
|
linkProps,
|
|
105
|
-
loc
|
|
137
|
+
loc,
|
|
138
|
+
prefetchBundleProp,
|
|
139
|
+
prefetchDataProp,
|
|
140
|
+
prefetchProp
|
|
106
141
|
]));
|
|
107
142
|
return /* @__PURE__ */ _jsxSplit("a", {
|
|
108
143
|
ref: anchorRef,
|
|
@@ -115,14 +150,21 @@ const Link = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl((props) =>
|
|
|
115
150
|
onClick$,
|
|
116
151
|
handleClientSideNavigation
|
|
117
152
|
],
|
|
118
|
-
"
|
|
119
|
-
"q-e:mouseover": [
|
|
153
|
+
"q-e:pointerenter": [
|
|
120
154
|
linkProps.onMouseOver$,
|
|
121
|
-
|
|
155
|
+
prefetchDataProp === "intent" ? prefetchData : null
|
|
122
156
|
],
|
|
123
157
|
"q-e:focus": [
|
|
124
158
|
linkProps.onFocus$,
|
|
125
|
-
|
|
159
|
+
prefetchDataProp === "intent" ? prefetchData : null
|
|
160
|
+
],
|
|
161
|
+
"q-e:pointerdown": [
|
|
162
|
+
linkProps.onPointerDown$,
|
|
163
|
+
prefetchDataProp === "commit" ? prefetchData : null
|
|
164
|
+
],
|
|
165
|
+
"q-e:keydown": [
|
|
166
|
+
linkProps.onKeyDown$,
|
|
167
|
+
prefetchDataProp === "commit" ? onEnterKeyDown : null
|
|
126
168
|
]
|
|
127
169
|
}, {
|
|
128
170
|
// We need to prevent the onQVisible$ from being called twice since it is handled in the visible task
|
|
@@ -841,6 +883,9 @@ const useQwikRouter = (props) => {
|
|
|
841
883
|
};
|
|
842
884
|
_waitNextPage().catch((err) => {
|
|
843
885
|
navigate();
|
|
886
|
+
if (err instanceof DOMException && err.name === "TimeoutError") {
|
|
887
|
+
throw new Error('View transition timed out. This can happen if you have "disableCache" in your browser devtools enabled.');
|
|
888
|
+
}
|
|
844
889
|
throw err;
|
|
845
890
|
}).finally(() => {
|
|
846
891
|
container2.element.setAttribute?.(Q_ROUTE, $routeName$);
|
|
@@ -903,13 +948,16 @@ const useQwikMockRouter = (props) => {
|
|
|
903
948
|
}, {
|
|
904
949
|
deep: false
|
|
905
950
|
});
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
951
|
+
const container = _getContextContainer();
|
|
952
|
+
const loadersObject = {};
|
|
953
|
+
const loadersState = {};
|
|
954
|
+
if (props.loaders) {
|
|
955
|
+
for (const { loader, data } of props.loaders) {
|
|
956
|
+
const id = loader.__id;
|
|
957
|
+
loadersObject[id] = data;
|
|
958
|
+
loadersState[id] = createLoaderSignal(loadersObject, id, url, DEFAULT_LOADERS_SERIALIZATION_STRATEGY, container);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
913
961
|
const goto = props.goto ?? /* @__PURE__ */ inlinedQrl(async () => {
|
|
914
962
|
console.warn("QwikRouterMockProvider: goto not provided");
|
|
915
963
|
}, "useQwikMockRouter_goto_aViHFxQ1a3s");
|
|
@@ -934,7 +982,7 @@ const useQwikMockRouter = (props) => {
|
|
|
934
982
|
useContextProvider(HttpStatusContext, httpStatus);
|
|
935
983
|
useContextProvider(RouteLocationContext, routeLocation);
|
|
936
984
|
useContextProvider(RouteNavigateContext, goto);
|
|
937
|
-
useContextProvider(RouteStateContext,
|
|
985
|
+
useContextProvider(RouteStateContext, loadersState);
|
|
938
986
|
useContextProvider(RouteActionContext, actionState);
|
|
939
987
|
const actionsMocks = props.actions?.reduce((acc, { action, handler }) => {
|
|
940
988
|
acc[action.__id] = handler;
|
|
@@ -18,12 +18,15 @@ function fallbackOrigin(req) {
|
|
|
18
18
|
function computeOrigin(req, opts) {
|
|
19
19
|
return opts?.getOrigin?.(req) ?? opts?.origin ?? process.env.ORIGIN ?? fallbackOrigin(req);
|
|
20
20
|
}
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
function isClientAbortWriteError(error) {
|
|
22
|
+
if (!(error instanceof Error)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const code = error.code;
|
|
26
|
+
if (code === "EPIPE" || code === "ECONNRESET" || code === "ERR_STREAM_DESTROYED" || code === "ERR_STREAM_WRITE_AFTER_END") {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
27
30
|
}
|
|
28
31
|
const invalidHeadersPattern = /^:(method|scheme|authority|path)$/i;
|
|
29
32
|
function normalizeUrl(url, base) {
|
|
@@ -32,6 +35,155 @@ function normalizeUrl(url, base) {
|
|
|
32
35
|
function getUrl(req, origin) {
|
|
33
36
|
return normalizeUrl(req.originalUrl || req.url || "/", origin);
|
|
34
37
|
}
|
|
38
|
+
function createNodeResponseSink(res) {
|
|
39
|
+
let closed = res.closed || res.destroyed;
|
|
40
|
+
let responseError;
|
|
41
|
+
let resolveClosed;
|
|
42
|
+
const closedPromise = closed ? Promise.resolve() : new Promise((resolve) => {
|
|
43
|
+
resolveClosed = resolve;
|
|
44
|
+
res.once("close", () => {
|
|
45
|
+
closed = true;
|
|
46
|
+
resolve();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
res.on("error", (error) => {
|
|
50
|
+
if (responseError === void 0) {
|
|
51
|
+
responseError = error;
|
|
52
|
+
}
|
|
53
|
+
if (!closed) {
|
|
54
|
+
closed = true;
|
|
55
|
+
resolveClosed();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
const shouldPropagateResponseError = () => responseError !== void 0 && !isClientAbortWriteError(responseError);
|
|
59
|
+
const write = (chunk) => {
|
|
60
|
+
if (closed || res.closed || res.destroyed) {
|
|
61
|
+
if (shouldPropagateResponseError()) {
|
|
62
|
+
return Promise.reject(responseError);
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
let settled = false;
|
|
68
|
+
let onDrain;
|
|
69
|
+
const cleanupDrain = () => {
|
|
70
|
+
if (onDrain) {
|
|
71
|
+
res.off("drain", onDrain);
|
|
72
|
+
onDrain = void 0;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const finish = () => {
|
|
76
|
+
if (settled) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
settled = true;
|
|
80
|
+
cleanupDrain();
|
|
81
|
+
setImmediate(resolve);
|
|
82
|
+
};
|
|
83
|
+
const finishClosed = () => {
|
|
84
|
+
closed = true;
|
|
85
|
+
finish();
|
|
86
|
+
};
|
|
87
|
+
const fail = (error) => {
|
|
88
|
+
if (settled) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
settled = true;
|
|
92
|
+
closed = true;
|
|
93
|
+
cleanupDrain();
|
|
94
|
+
reject(error);
|
|
95
|
+
};
|
|
96
|
+
closedPromise.then(() => {
|
|
97
|
+
if (shouldPropagateResponseError()) {
|
|
98
|
+
fail(responseError);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
finish();
|
|
102
|
+
});
|
|
103
|
+
let ok;
|
|
104
|
+
try {
|
|
105
|
+
ok = res.write(chunk, (error) => {
|
|
106
|
+
if (error) {
|
|
107
|
+
if (isClientAbortWriteError(error)) {
|
|
108
|
+
finishClosed();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
fail(error);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
finish();
|
|
115
|
+
});
|
|
116
|
+
} catch (error) {
|
|
117
|
+
if (isClientAbortWriteError(error)) {
|
|
118
|
+
finishClosed();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
fail(error);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (settled) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (ok) {
|
|
128
|
+
finish();
|
|
129
|
+
} else {
|
|
130
|
+
onDrain = () => {
|
|
131
|
+
onDrain = void 0;
|
|
132
|
+
finish();
|
|
133
|
+
};
|
|
134
|
+
res.once("drain", onDrain);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
const close = () => {
|
|
139
|
+
if (closed || res.closed || res.destroyed) {
|
|
140
|
+
if (shouldPropagateResponseError()) {
|
|
141
|
+
return Promise.reject(responseError);
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
return new Promise((resolve, reject) => {
|
|
146
|
+
let settled = false;
|
|
147
|
+
const finish = () => {
|
|
148
|
+
if (settled) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
settled = true;
|
|
152
|
+
closed = true;
|
|
153
|
+
resolve();
|
|
154
|
+
};
|
|
155
|
+
const fail = (error) => {
|
|
156
|
+
if (settled) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
settled = true;
|
|
160
|
+
closed = true;
|
|
161
|
+
reject(error);
|
|
162
|
+
};
|
|
163
|
+
closedPromise.then(() => {
|
|
164
|
+
if (shouldPropagateResponseError()) {
|
|
165
|
+
fail(responseError);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
finish();
|
|
169
|
+
});
|
|
170
|
+
res.once("finish", finish);
|
|
171
|
+
try {
|
|
172
|
+
res.end();
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if (isClientAbortWriteError(error)) {
|
|
175
|
+
finish();
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
fail(error);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
return {
|
|
183
|
+
write,
|
|
184
|
+
close
|
|
185
|
+
};
|
|
186
|
+
}
|
|
35
187
|
async function fromNodeHttp(url, req, res, mode, getClientConn) {
|
|
36
188
|
const requestHeaders = new Headers();
|
|
37
189
|
const nodeRequestHeaders = req.headers;
|
|
@@ -58,6 +210,9 @@ async function fromNodeHttp(url, req, res, mode, getClientConn) {
|
|
|
58
210
|
};
|
|
59
211
|
const body = req.method === "HEAD" || req.method === "GET" ? void 0 : getRequestBody();
|
|
60
212
|
const controller = new AbortController();
|
|
213
|
+
const abort = () => {
|
|
214
|
+
controller.abort();
|
|
215
|
+
};
|
|
61
216
|
const options = {
|
|
62
217
|
method: req.method,
|
|
63
218
|
headers: requestHeaders,
|
|
@@ -65,9 +220,10 @@ async function fromNodeHttp(url, req, res, mode, getClientConn) {
|
|
|
65
220
|
signal: controller.signal,
|
|
66
221
|
duplex: "half"
|
|
67
222
|
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
223
|
+
req.on("aborted", abort);
|
|
224
|
+
req.on("error", abort);
|
|
225
|
+
res.on("close", abort);
|
|
226
|
+
res.on("error", abort);
|
|
71
227
|
const serverRequestEv = {
|
|
72
228
|
mode,
|
|
73
229
|
url,
|
|
@@ -77,8 +233,9 @@ async function fromNodeHttp(url, req, res, mode, getClientConn) {
|
|
|
77
233
|
return process.env[key];
|
|
78
234
|
}
|
|
79
235
|
},
|
|
80
|
-
getWritableStream: (status, headers, cookies) => {
|
|
236
|
+
getWritableStream: (status, headers, cookies, resolve) => {
|
|
81
237
|
res.statusCode = status;
|
|
238
|
+
const sink = createNodeResponseSink(res);
|
|
82
239
|
try {
|
|
83
240
|
for (const [key, value] of headers) {
|
|
84
241
|
if (invalidHeadersPattern.test(key)) {
|
|
@@ -93,19 +250,13 @@ async function fromNodeHttp(url, req, res, mode, getClientConn) {
|
|
|
93
250
|
} catch (err) {
|
|
94
251
|
console.error(err);
|
|
95
252
|
}
|
|
253
|
+
resolve(true);
|
|
96
254
|
return new WritableStream({
|
|
97
255
|
write(chunk) {
|
|
98
|
-
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
res.write(chunk, (error) => {
|
|
102
|
-
if (error && !isIgnoredError(error.message)) {
|
|
103
|
-
console.error(error);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
256
|
+
return sink.write(chunk);
|
|
106
257
|
},
|
|
107
258
|
close() {
|
|
108
|
-
|
|
259
|
+
return sink.close();
|
|
109
260
|
}
|
|
110
261
|
});
|
|
111
262
|
},
|
|
@@ -139,12 +290,12 @@ function createQwikRouter(opts) {
|
|
|
139
290
|
const handled = await requestHandler(serverRequestEv, opts);
|
|
140
291
|
if (handled) {
|
|
141
292
|
const err = await handled.completion;
|
|
142
|
-
if (err) {
|
|
143
|
-
throw err;
|
|
144
|
-
}
|
|
145
293
|
if (handled.requestEv.headersSent) {
|
|
146
294
|
return;
|
|
147
295
|
}
|
|
296
|
+
if (err) {
|
|
297
|
+
throw err;
|
|
298
|
+
}
|
|
148
299
|
}
|
|
149
300
|
next();
|
|
150
301
|
} catch (e) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Q as Q_ROUTE, m as resolveRouteConfig, n as QLOADER_KEY, h as QFN_KEY, j as QACTION_KEY, o as isPromise, k as QDATA_KEY, b as loadRoute } from '../../chunks/routing.qwik.mjs';
|
|
2
|
-
import { isDev, _UNINITIALIZED, _deserialize, _verifySerializable, _serialize } from '@qwik.dev/core/internal';
|
|
2
|
+
import { isDev, _UNINITIALIZED, _deserialize, _verifySerializable, _serialize, _getAsyncLocalStorage } from '@qwik.dev/core/internal';
|
|
3
3
|
import { inlinedQrl } from '@qwik.dev/core';
|
|
4
4
|
import { R as RedirectMessage, A as AbortMessage } from '../../chunks/redirect-handler.mjs';
|
|
5
5
|
import { isServer } from '@qwik.dev/core/build';
|
|
@@ -1079,8 +1079,11 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
|
|
|
1079
1079
|
});
|
|
1080
1080
|
pipeSource = readable.pipeThrough(capture);
|
|
1081
1081
|
}
|
|
1082
|
+
let pipeError;
|
|
1082
1083
|
const pipe = pipeSource.pipeTo(writableStream, {
|
|
1083
1084
|
preventClose: true
|
|
1085
|
+
}).catch((error) => {
|
|
1086
|
+
pipeError = error;
|
|
1084
1087
|
});
|
|
1085
1088
|
const stream = writable.getWriter();
|
|
1086
1089
|
const status = requestEv.status();
|
|
@@ -1111,6 +1114,9 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
|
|
|
1111
1114
|
await stream.close();
|
|
1112
1115
|
await pipe;
|
|
1113
1116
|
}
|
|
1117
|
+
if (pipeError) {
|
|
1118
|
+
throw pipeError;
|
|
1119
|
+
}
|
|
1114
1120
|
if (eTagCacheKey && cacheChunks && cacheChunks.length > 0) {
|
|
1115
1121
|
const totalLength = cacheChunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
1116
1122
|
const combined = new Uint8Array(totalLength);
|
|
@@ -1291,11 +1297,12 @@ const { renderQwikMiddleware, resolveRequestHandlers} = requestHandlers;
|
|
|
1291
1297
|
|
|
1292
1298
|
let _asyncRequestStore;
|
|
1293
1299
|
if (isServer) {
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1300
|
+
const AsyncLocalStorage = _getAsyncLocalStorage();
|
|
1301
|
+
if (AsyncLocalStorage) {
|
|
1302
|
+
_asyncRequestStore = new AsyncLocalStorage();
|
|
1303
|
+
} else {
|
|
1304
|
+
console.warn("\n=====================\n Qwik Router Warning:\n AsyncLocalStorage is not available, continuing without it.\n This impacts concurrent async server calls, where they lose access to the ServerRequestEv object.\n=====================\n\n");
|
|
1305
|
+
}
|
|
1299
1306
|
}
|
|
1300
1307
|
|
|
1301
1308
|
function createCacheControl(cacheControl) {
|
package/lib/vite/index.mjs
CHANGED
|
@@ -479,7 +479,7 @@ async function walkServerPlugins(opts) {
|
|
|
479
479
|
});
|
|
480
480
|
}
|
|
481
481
|
}));
|
|
482
|
-
return sourceFiles;
|
|
482
|
+
return sourceFiles.sort((a, b) => a.filePath < b.filePath ? -1 : 1);
|
|
483
483
|
}
|
|
484
484
|
|
|
485
485
|
function resolveEntry(opts, sourceFile) {
|
|
@@ -2010,13 +2010,13 @@ function imagePlugin(userOpts) {
|
|
|
2010
2010
|
];
|
|
2011
2011
|
}
|
|
2012
2012
|
|
|
2013
|
-
async function collectServerFnModuleIds(
|
|
2013
|
+
async function collectServerFnModuleIds(routingContext, resolvedVirtualId, pluginContext) {
|
|
2014
2014
|
const serverFnModules = /* @__PURE__ */ new Set();
|
|
2015
2015
|
const queuedModuleIds = /* @__PURE__ */ new Set();
|
|
2016
2016
|
const seenModuleIds = /* @__PURE__ */ new Set();
|
|
2017
|
-
const routes =
|
|
2018
|
-
const layouts =
|
|
2019
|
-
const serverPlugins =
|
|
2017
|
+
const routes = routingContext.routes;
|
|
2018
|
+
const layouts = routingContext.layouts;
|
|
2019
|
+
const serverPlugins = routingContext.serverPlugins;
|
|
2020
2020
|
for (let i = 0; i < routes.length; i++) {
|
|
2021
2021
|
const route = routes[i];
|
|
2022
2022
|
queuedModuleIds.add(route.filePath);
|
|
@@ -2038,15 +2038,26 @@ async function collectServerFnModuleIds(ctx, resolvedVirtualId, loadModule) {
|
|
|
2038
2038
|
continue;
|
|
2039
2039
|
}
|
|
2040
2040
|
seenModuleIds.add(id);
|
|
2041
|
-
const
|
|
2042
|
-
|
|
2041
|
+
const resolved = await pluginContext.resolve(id, void 0, {
|
|
2042
|
+
skipSelf: true
|
|
2043
|
+
});
|
|
2044
|
+
if (!resolved || resolved.external) {
|
|
2045
|
+
continue;
|
|
2046
|
+
}
|
|
2047
|
+
const moduleInfo = await pluginContext.load({
|
|
2048
|
+
id: resolved.id
|
|
2049
|
+
});
|
|
2050
|
+
if (moduleInfo.code == null) {
|
|
2051
|
+
continue;
|
|
2052
|
+
}
|
|
2053
|
+
if (moduleInfo.code.includes("serverQrl(")) {
|
|
2043
2054
|
serverFnModules.add(moduleInfo.id);
|
|
2044
2055
|
}
|
|
2045
|
-
const resolvedImports = moduleInfo.
|
|
2056
|
+
const resolvedImports = moduleInfo.importedIds.concat(moduleInfo.dynamicallyImportedIds);
|
|
2046
2057
|
for (let i = 0; i < resolvedImports.length; i++) {
|
|
2047
2058
|
const resolvedImport = resolvedImports[i];
|
|
2048
|
-
if (
|
|
2049
|
-
queuedModuleIds.add(resolvedImport
|
|
2059
|
+
if (resolvedImport && !seenModuleIds.has(resolvedImport)) {
|
|
2060
|
+
queuedModuleIds.add(resolvedImport);
|
|
2050
2061
|
}
|
|
2051
2062
|
}
|
|
2052
2063
|
}
|
|
@@ -2264,7 +2275,11 @@ class HtmlTransformPatcher {
|
|
|
2264
2275
|
}
|
|
2265
2276
|
}
|
|
2266
2277
|
async handleEnd(chunk, encoding, callback) {
|
|
2267
|
-
if (typeof
|
|
2278
|
+
if (typeof chunk === "function") {
|
|
2279
|
+
callback = chunk;
|
|
2280
|
+
chunk = void 0;
|
|
2281
|
+
encoding = void 0;
|
|
2282
|
+
} else if (typeof encoding === "function") {
|
|
2268
2283
|
callback = encoding;
|
|
2269
2284
|
encoding = void 0;
|
|
2270
2285
|
}
|
|
@@ -2282,7 +2297,15 @@ function wrapResponseForHtmlTransform(request, response, server) {
|
|
|
2282
2297
|
}
|
|
2283
2298
|
|
|
2284
2299
|
const makeRouterDevMiddleware = (server, ctx) => async (req, res, next) => {
|
|
2285
|
-
|
|
2300
|
+
let mod;
|
|
2301
|
+
try {
|
|
2302
|
+
mod = await server.ssrLoadModule("src/entry.ssr");
|
|
2303
|
+
} catch (e) {
|
|
2304
|
+
if (e instanceof Error) {
|
|
2305
|
+
server.ssrFixStacktrace(e);
|
|
2306
|
+
}
|
|
2307
|
+
return next();
|
|
2308
|
+
}
|
|
2286
2309
|
if (!mod.default) {
|
|
2287
2310
|
console.error("No default export found in src/entry.ssr");
|
|
2288
2311
|
return next();
|
|
@@ -2723,9 +2746,7 @@ function serverFnsPlugin(buildContextRef) {
|
|
|
2723
2746
|
if (!ctx) {
|
|
2724
2747
|
return;
|
|
2725
2748
|
}
|
|
2726
|
-
const moduleIds = await collectServerFnModuleIds(ctx, RESOLVED_ID,
|
|
2727
|
-
id
|
|
2728
|
-
}));
|
|
2749
|
+
const moduleIds = await collectServerFnModuleIds(ctx, RESOLVED_ID, this);
|
|
2729
2750
|
for (let i = 0; i < moduleIds.length; i++) {
|
|
2730
2751
|
serverFnModules.add(moduleIds[i]);
|
|
2731
2752
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qwik.dev/router",
|
|
3
3
|
"description": "The router for Qwik.",
|
|
4
|
-
"version": "2.0.0-beta.
|
|
4
|
+
"version": "2.0.0-beta.35",
|
|
5
5
|
"bugs": "https://github.com/QwikDev/qwik/issues",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@azure/functions": "3.5.1",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"tsm": "2.3.0",
|
|
41
41
|
"typescript": "5.9.3",
|
|
42
42
|
"uvu": "0.5.6",
|
|
43
|
-
"@qwik.dev/core": "2.0.0-beta.
|
|
43
|
+
"@qwik.dev/core": "2.0.0-beta.35"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
|
46
46
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
|
@@ -169,7 +169,7 @@
|
|
|
169
169
|
"main": "./lib/index.qwik.mjs",
|
|
170
170
|
"peerDependencies": {
|
|
171
171
|
"vite": ">=6 <9",
|
|
172
|
-
"@qwik.dev/core": "^2.0.0-beta.
|
|
172
|
+
"@qwik.dev/core": "^2.0.0-beta.35"
|
|
173
173
|
},
|
|
174
174
|
"publishConfig": {
|
|
175
175
|
"access": "public"
|