@qwik.dev/router 2.0.0-beta.41 → 2.0.0-beta.43
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/url.mjs
CHANGED
|
@@ -225,6 +225,7 @@ var RequestEvSharedNonce = "@nonce";
|
|
|
225
225
|
var RequestEvIsRewrite = "@rewrite";
|
|
226
226
|
var RequestEvShareServerTiming = "@serverTiming";
|
|
227
227
|
var RequestEvETagCacheKey = "@eTagCacheKey";
|
|
228
|
+
var RequestEvErrorBoundaryCaught = "@errorBoundaryCaught";
|
|
228
229
|
var RequestEvHttpStatusMessage = "@httpStatusMessage";
|
|
229
230
|
function getRequestRoute(requestEv) {
|
|
230
231
|
return requestEv[RequestEvRoute];
|
|
@@ -851,7 +852,27 @@ async function runValidators(requestEv, validators, data, devMode) {
|
|
|
851
852
|
requestEv.send(requestEv.status(), serialized);
|
|
852
853
|
};
|
|
853
854
|
}
|
|
854
|
-
|
|
855
|
+
/**
|
|
856
|
+
* A set of static paths generated by the SSG, including loader json. Routes always have a trailing
|
|
857
|
+
* slash, short for `.../index.html`
|
|
858
|
+
*/ var staticPaths = /* @__PURE__ */ new Set(["__QWIK_ROUTER_STATIC_PATHS_ARRAY__"]);
|
|
859
|
+
/**
|
|
860
|
+
* Check if the current pathname needs a redirect due to trailing slash mismatch. If we're not sure,
|
|
861
|
+
* we fall through.
|
|
862
|
+
*
|
|
863
|
+
* Note that we ignore build/ and assets/, those never need trailing slash redirects.
|
|
864
|
+
*
|
|
865
|
+
* @returns The canonical path with the correct trailing slash if a redirect is needed, otherwise
|
|
866
|
+
* `undefined`.
|
|
867
|
+
*/ function getStaticPathRedirect(method, url) {
|
|
868
|
+
const { pathname, search } = url;
|
|
869
|
+
if (method.toUpperCase() !== "GET" || pathname === "/" || !pathname.startsWith("/") || pathname.startsWith("//")) return;
|
|
870
|
+
const hasTrailingSlash = pathname.endsWith("/");
|
|
871
|
+
const routesHaveTrailingSlash = !globalThis.__NO_TRAILING_SLASH__;
|
|
872
|
+
if (hasTrailingSlash === routesHaveTrailingSlash) return;
|
|
873
|
+
const canonicalPathname = hasTrailingSlash ? pathname.slice(0, -1) : pathname + "/";
|
|
874
|
+
if (staticPaths.has(canonicalPathname) || !routesHaveTrailingSlash && staticPaths.has(pathname)) return canonicalPathname + search;
|
|
875
|
+
}
|
|
855
876
|
/**
|
|
856
877
|
* Decide whether a given request path is a static path, for optimizing file access.
|
|
857
878
|
*
|
|
@@ -862,6 +883,7 @@ var staticPaths = /* @__PURE__ */ new Set(["__QWIK_ROUTER_STATIC_PATHS_ARRAY__"]
|
|
|
862
883
|
const p = url.pathname;
|
|
863
884
|
if (p.startsWith("/" + (globalThis.__QWIK_BUILD_DIR__ || "build") + "/")) return true;
|
|
864
885
|
if (p.startsWith("/" + (globalThis.__QWIK_ASSETS_DIR__ || "assets") + "/")) return true;
|
|
886
|
+
if (globalThis.__NO_TRAILING_SLASH__ && p !== "/") return !p.endsWith("/") && (staticPaths.has(p) || staticPaths.has(p + "/"));
|
|
865
887
|
return staticPaths.has(p);
|
|
866
888
|
}
|
|
867
889
|
/**
|
|
@@ -1380,6 +1402,7 @@ function createResolveRequestHandlers() {
|
|
|
1380
1402
|
if (accept && !accept.includes("text/html")) throw e;
|
|
1381
1403
|
const status = e.status;
|
|
1382
1404
|
requestEv.status(status);
|
|
1405
|
+
requestEv.headers.set("Cache-Control", "no-store");
|
|
1383
1406
|
const errorLoader = route.$errorLoader$;
|
|
1384
1407
|
route.$mods$ = errorLoader ? await Promise.all(errorLoader.map((load) => load())) : [await loadHttpError()];
|
|
1385
1408
|
requestEv.sharedMap.set(RequestEvHttpStatusMessage, typeof e.data === "string" ? e.data : "Server Error");
|
|
@@ -1393,6 +1416,7 @@ function createResolveRequestHandlers() {
|
|
|
1393
1416
|
requestEv.sharedMap.delete(RequestEvSharedActionId);
|
|
1394
1417
|
requestEv.sharedMap.delete(RequestEvSharedActionFormData);
|
|
1395
1418
|
requestEv.sharedMap.delete("@actionResult");
|
|
1419
|
+
requestEv.sharedMap.delete(RequestEvETagCacheKey);
|
|
1396
1420
|
}
|
|
1397
1421
|
async function runValidators(requestEv, validators, data) {
|
|
1398
1422
|
let lastResult = {
|
|
@@ -1517,7 +1541,11 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
|
|
|
1517
1541
|
if (!responseHeaders.has("Content-Type")) responseHeaders.set("Content-Type", "text/html; charset=utf-8");
|
|
1518
1542
|
const cachePlan = requestEv.sharedMap.get(RequestEvETagCacheKey);
|
|
1519
1543
|
const { readable, writable } = new TextEncoderStream();
|
|
1520
|
-
|
|
1544
|
+
let responseWriter;
|
|
1545
|
+
const lazyResponseSink = new WritableStream({ write(chunk) {
|
|
1546
|
+
responseWriter ||= requestEv.getWritableStream().getWriter();
|
|
1547
|
+
return responseWriter.write(chunk);
|
|
1548
|
+
} });
|
|
1521
1549
|
let cacheChunks;
|
|
1522
1550
|
let pipeSource = readable;
|
|
1523
1551
|
if (cachePlan) {
|
|
@@ -1529,7 +1557,10 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
|
|
|
1529
1557
|
pipeSource = readable.pipeThrough(capture);
|
|
1530
1558
|
}
|
|
1531
1559
|
let pipeError;
|
|
1532
|
-
|
|
1560
|
+
let boundaryErrored = false;
|
|
1561
|
+
let noStoreSent = false;
|
|
1562
|
+
let overrodeCacheControl = false;
|
|
1563
|
+
const pipe = pipeSource.pipeTo(lazyResponseSink, { preventClose: true }).catch((error) => {
|
|
1533
1564
|
pipeError = error;
|
|
1534
1565
|
});
|
|
1535
1566
|
const stream = writable.getWriter();
|
|
@@ -1544,16 +1575,31 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
|
|
|
1544
1575
|
containerAttributes: {
|
|
1545
1576
|
["q:render"]: isStatic ? "static" : "",
|
|
1546
1577
|
...serverData.containerAttributes
|
|
1578
|
+
},
|
|
1579
|
+
onBeforeFirstFlush: (info) => {
|
|
1580
|
+
if (info.errorBoundaryCaught) {
|
|
1581
|
+
boundaryErrored = true;
|
|
1582
|
+
noStoreSent = true;
|
|
1583
|
+
overrodeCacheControl = responseHeaders.has("Cache-Control");
|
|
1584
|
+
responseHeaders.set("Cache-Control", "no-store");
|
|
1585
|
+
}
|
|
1547
1586
|
}
|
|
1548
1587
|
});
|
|
1549
1588
|
if (typeof result.html === "string") await stream.write(result.html);
|
|
1589
|
+
boundaryErrored ||= result.errorBoundaryCaught === true;
|
|
1590
|
+
if (boundaryErrored) requestEv.sharedMap.set(RequestEvErrorBoundaryCaught, true);
|
|
1550
1591
|
} finally {
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1592
|
+
try {
|
|
1593
|
+
await stream.ready;
|
|
1594
|
+
await stream.close();
|
|
1595
|
+
} finally {
|
|
1596
|
+
await pipe;
|
|
1597
|
+
responseWriter?.releaseLock();
|
|
1598
|
+
}
|
|
1554
1599
|
}
|
|
1555
1600
|
if (pipeError) throw pipeError;
|
|
1556
|
-
if (
|
|
1601
|
+
if (boundaryErrored && (noStoreSent ? cachePlan || overrodeCacheControl : !!cachePlan)) console.warn(`An <ErrorBoundary> caught during SSR of ${requestEv.url.pathname} — configured caching disabled: ` + (noStoreSent ? "the response was sent with Cache-Control: no-store." : "the SSR cache was skipped (response headers were already sent)."));
|
|
1602
|
+
if (cachePlan && cacheChunks && cacheChunks.length > 0 && !boundaryErrored) {
|
|
1557
1603
|
const totalLength = cacheChunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
1558
1604
|
const combined = new Uint8Array(totalLength);
|
|
1559
1605
|
let offset = 0;
|
|
@@ -1568,7 +1614,7 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
|
|
|
1568
1614
|
body: html
|
|
1569
1615
|
});
|
|
1570
1616
|
}
|
|
1571
|
-
await
|
|
1617
|
+
if (responseWriter) await requestEv.getWritableStream().close();
|
|
1572
1618
|
};
|
|
1573
1619
|
}
|
|
1574
1620
|
function now() {
|
|
@@ -1727,4 +1773,4 @@ function runQwikRouter(serverRequestEv, loadedRoute, requestHandlers, rebuildRou
|
|
|
1727
1773
|
};
|
|
1728
1774
|
}
|
|
1729
1775
|
}
|
|
1730
|
-
export {
|
|
1776
|
+
export { getStaticPathRedirect as a, clearSsrCache as c, mergeHeadersCookies as d, resolveRequestHandlers as i, RequestEvErrorBoundaryCaught as l, getErrorHtml as n, isStaticPath as o, renderQwikMiddleware as r, clearLoaderCache as s, runQwikRouter as t, RewriteMessage as u };
|
|
@@ -155,8 +155,16 @@ async function workerRender(sys, opts, staticRoute, pendingPromises, callback, d
|
|
|
155
155
|
}
|
|
156
156
|
}, opts, deps).then((rsp) => {
|
|
157
157
|
if (rsp != null) return rsp.completion.then((r) => {
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
const failRouteOnCaughtBoundary = (completion) => {
|
|
159
|
+
if (completion === void 0 && rsp.requestEv.sharedMap.get("@errorBoundaryCaught")) {
|
|
160
|
+
result.ok = false;
|
|
161
|
+
result.filePath = null;
|
|
162
|
+
return /* @__PURE__ */ new Error(`An <ErrorBoundary> caught an error while rendering ${staticRoute.pathname}`);
|
|
163
|
+
}
|
|
164
|
+
return completion;
|
|
165
|
+
};
|
|
166
|
+
if (routeWriter) return closePromise.then(() => failRouteOnCaughtBoundary(r));
|
|
167
|
+
return failRouteOnCaughtBoundary(r);
|
|
160
168
|
});
|
|
161
169
|
}).then((e) => {
|
|
162
170
|
if (e !== void 0) {
|
package/lib/index.d.ts
CHANGED
|
@@ -391,14 +391,6 @@ declare interface EndpointResponse {
|
|
|
391
391
|
loaderHashes?: string[];
|
|
392
392
|
}
|
|
393
393
|
|
|
394
|
-
/** @public */
|
|
395
|
-
export declare const ErrorBoundary: Component<ErrorBoundaryProps>;
|
|
396
|
-
|
|
397
|
-
/** @public */
|
|
398
|
-
declare interface ErrorBoundaryProps {
|
|
399
|
-
fallback$?: QRL<(error: any) => any>;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
394
|
/**
|
|
403
395
|
* Drops control-flow signals (`ev.redirect()`, `ev.error()`, etc.) from a loader/action return
|
|
404
396
|
* type: those are thrown, not surfaced as data. `ev.fail()` is plain data and is kept.
|
package/lib/index.qwik.mjs
CHANGED
|
@@ -1,24 +1,15 @@
|
|
|
1
1
|
import { t as ensureSlash } from "./chunks/pathname.mjs";
|
|
2
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
|
-
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,
|
|
4
|
-
import { Fragment } from "@qwik.dev/core/jsx-runtime";
|
|
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";
|
|
5
4
|
import * as qwikRouterConfig from "@qwik-router-config";
|
|
6
5
|
import { p } from "@qwik.dev/core/preloader";
|
|
7
6
|
import { _deserialize, _getContextContainer, _getContextHostElement, _hasStoreEffects, _serialize, _waitUntilRendered, forceStoreEffects } from "@qwik.dev/core/internal";
|
|
7
|
+
import { Fragment } from "@qwik.dev/core/jsx-runtime";
|
|
8
8
|
import * as v from "valibot";
|
|
9
9
|
import * as z$1 from "zod";
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
import swRegister from "@qwik-router-sw-register";
|
|
12
12
|
import { renderToStream } from "@qwik.dev/core/server";
|
|
13
|
-
/** @public */ var ErrorBoundary = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((props) => {
|
|
14
|
-
const store = useErrorBoundary();
|
|
15
|
-
useOnWindow("qerror", /*#__PURE__*/ inlinedQrl((e) => {
|
|
16
|
-
const store = _captures[0];
|
|
17
|
-
store.error = e.detail.error;
|
|
18
|
-
}, "ErrorBoundary_component_useOnWindow_G0jFRpoNY0M", [store]));
|
|
19
|
-
if (store.error && props.fallback$) return /*#__PURE__*/ _jsxSorted(Fragment, null, null, props.fallback$(store.error), 1, "bA_0");
|
|
20
|
-
return /*#__PURE__*/ _jsxSorted(Slot, null, null, null, 3, "bA_1");
|
|
21
|
-
}, "ErrorBoundary_component_pOa6vjtC7ik"));
|
|
22
13
|
/**
|
|
23
14
|
* Prefetch a route's loader data in parallel (fire-and-forget). The per-nav fetch map dedupes
|
|
24
15
|
* repeat hovers and shares the promise with the navigation's own fetch; the browser HTTP cache
|
|
@@ -838,7 +829,7 @@ function initializeSPA(goto, scroller) {
|
|
|
838
829
|
scroll,
|
|
839
830
|
historyUpdated
|
|
840
831
|
};
|
|
841
|
-
if (isBrowser) prefetchRoute(dest,
|
|
832
|
+
if (isBrowser) prefetchRoute(dest, false, .8, manifestHash, true);
|
|
842
833
|
navResolver.p = new Promise((resolve) => {
|
|
843
834
|
navResolver.r = () => {
|
|
844
835
|
navResolver.r = void 0;
|
|
@@ -933,8 +924,8 @@ function initializeSPA(goto, scroller) {
|
|
|
933
924
|
const contentModules = $mods$;
|
|
934
925
|
if (!isServer) routeLoaderCtx.goto = noSerialize(goto);
|
|
935
926
|
updateRouteLoaderPaths(routeLoaderCtx, loadedRoute.$loaderPaths$, trackUrl);
|
|
936
|
-
const routeLoaders = ensureRouteLoaderSignals(contentModules, loaderState, routeLoaderCtx);
|
|
937
927
|
if (!isServer) invalidateNavRouteLoaders(loaderState);
|
|
928
|
+
const routeLoaders = ensureRouteLoaderSignals(contentModules, loaderState, routeLoaderCtx);
|
|
938
929
|
if (shouldInvalidateActionLoaders) {
|
|
939
930
|
if (actionLoaderHashes !== void 0) for (const hash of actionLoaderHashes) loaderState[hash]?.invalidate(true);
|
|
940
931
|
else for (const loader of routeLoaders) loaderState[loader.__id].invalidate(true);
|
|
@@ -1692,4 +1683,4 @@ var GetForm = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((_rawProps) =>
|
|
|
1692
1683
|
})
|
|
1693
1684
|
], 1, "r5_3");
|
|
1694
1685
|
}, "DocumentHeadTags_component_9CrWYOoCpgY"));
|
|
1695
|
-
export { DocumentHeadTags,
|
|
1686
|
+
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 };
|
|
@@ -2,7 +2,7 @@ import { Y as _asyncRequestStore, d as loadRoute } from "../../chunks/head.qwik.
|
|
|
2
2
|
import { c as resolveValidInternalFullPathname, l as trimInternalPathname, s as recognizeRequest, u as trimRecognizedInternalPathname } from "../../chunks/request-path.mjs";
|
|
3
3
|
import { i as RedirectMessage, r as AbortMessage, t as ServerError } from "../../chunks/server-error.mjs";
|
|
4
4
|
import { t as devPreloadedRouteLoaders } from "../../chunks/dev-preloaded-route-loader.mjs";
|
|
5
|
-
import { a as
|
|
5
|
+
import { a as getStaticPathRedirect, c as clearSsrCache, d as mergeHeadersCookies, i as resolveRequestHandlers, n as getErrorHtml, o as isStaticPath, r as renderQwikMiddleware, s as clearLoaderCache, t as runQwikRouter, u as RewriteMessage } from "../../chunks/user-response.mjs";
|
|
6
6
|
import { isDev } from "@qwik.dev/core";
|
|
7
7
|
var qwikRouterConfig;
|
|
8
8
|
async function getConfig() {
|
|
@@ -38,12 +38,20 @@ async function loadRequestHandlers(qwikRouterConfig, pathname, method, checkOrig
|
|
|
38
38
|
const config = await getConfig();
|
|
39
39
|
const pathname = getRequestHandlerPathname(serverRequestEv);
|
|
40
40
|
if (pathname === "/.well-known" || pathname.startsWith("/.well-known/")) return null;
|
|
41
|
-
const
|
|
42
|
-
if (config.fallthrough && loadedRoute.$notFound$) return null;
|
|
41
|
+
const staticPathRedirect = getStaticPathRedirect(serverRequestEv.request.method, serverRequestEv.url);
|
|
43
42
|
const rebuildRouteInfo = async (url) => {
|
|
44
43
|
const cleanPathname = trimInternalPathname(url.pathname);
|
|
45
44
|
return loadRequestHandlers(config, cleanPathname, serverRequestEv.request.method, checkOrigin ?? true, render, serverRequestEv);
|
|
46
45
|
};
|
|
46
|
+
if (staticPathRedirect) return runQwikRouter(serverRequestEv, {
|
|
47
|
+
$routeName$: pathname,
|
|
48
|
+
$params$: {},
|
|
49
|
+
$mods$: []
|
|
50
|
+
}, [(event) => {
|
|
51
|
+
throw event.redirect(301, staticPathRedirect);
|
|
52
|
+
}], rebuildRouteInfo, config.basePathname);
|
|
53
|
+
const { loadedRoute, requestHandlers } = await loadRequestHandlers(config, pathname, serverRequestEv.request.method, checkOrigin ?? true, render, serverRequestEv);
|
|
54
|
+
if (config.fallthrough && loadedRoute.$notFound$) return null;
|
|
47
55
|
return runQwikRouter(serverRequestEv, loadedRoute, requestHandlers, rebuildRouteInfo, config.basePathname);
|
|
48
56
|
}
|
|
49
57
|
/**
|
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.43",
|
|
5
5
|
"bugs": "https://github.com/QwikDev/qwik/issues",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@azure/functions": "3.5.1",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"typescript": "5.9.3",
|
|
42
42
|
"uvu": "0.5.6",
|
|
43
43
|
"vite": "8.2.1",
|
|
44
|
-
"@qwik.dev/core": "2.0.0-beta.
|
|
44
|
+
"@qwik.dev/core": "2.0.0-beta.43"
|
|
45
45
|
},
|
|
46
46
|
"engines": {
|
|
47
47
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
|
@@ -166,7 +166,7 @@
|
|
|
166
166
|
"main": "./lib/index.qwik.mjs",
|
|
167
167
|
"peerDependencies": {
|
|
168
168
|
"vite": ">=8 <9",
|
|
169
|
-
"@qwik.dev/core": "2.0.0-beta.
|
|
169
|
+
"@qwik.dev/core": "2.0.0-beta.43"
|
|
170
170
|
},
|
|
171
171
|
"publishConfig": {
|
|
172
172
|
"access": "public"
|