@reckona/mreact-router 0.0.137 → 0.0.139
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/README.md +1 -1
- package/dist/actions.d.ts +1 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +32 -12
- package/dist/actions.js.map +1 -1
- package/dist/adapters/static.js +10 -4
- package/dist/adapters/static.js.map +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +28 -21
- package/dist/build.js.map +1 -1
- package/dist/cache.d.ts +2 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +23 -1
- package/dist/cache.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +49 -12
- package/dist/client.js.map +1 -1
- package/dist/dev-server.d.ts +1 -0
- package/dist/dev-server.d.ts.map +1 -1
- package/dist/dev-server.js +6 -4
- package/dist/dev-server.js.map +1 -1
- package/dist/link.js +17 -3
- package/dist/link.js.map +1 -1
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +10 -7
- package/dist/middleware.js.map +1 -1
- package/dist/multipart.d.ts +5 -0
- package/dist/multipart.d.ts.map +1 -1
- package/dist/multipart.js +24 -1
- package/dist/multipart.js.map +1 -1
- package/dist/native-route-matcher.d.ts +9 -0
- package/dist/native-route-matcher.d.ts.map +1 -1
- package/dist/native-route-matcher.js +23 -11
- package/dist/native-route-matcher.js.map +1 -1
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +8 -1
- package/dist/navigation.js.map +1 -1
- package/dist/render.d.ts +5 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +239 -95
- package/dist/render.js.map +1 -1
- package/dist/route-source.d.ts +3 -1
- package/dist/route-source.d.ts.map +1 -1
- package/dist/route-source.js +13 -11
- package/dist/route-source.js.map +1 -1
- package/dist/route-styles.d.ts +4 -0
- package/dist/route-styles.d.ts.map +1 -1
- package/dist/route-styles.js +15 -3
- package/dist/route-styles.js.map +1 -1
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +39 -9
- package/dist/serve.js.map +1 -1
- package/dist/server-action-inference.d.ts.map +1 -1
- package/dist/server-action-inference.js +57 -0
- package/dist/server-action-inference.js.map +1 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +47 -11
- package/dist/vite.js.map +1 -1
- package/package.json +11 -11
- package/src/actions.ts +44 -10
- package/src/adapters/static.ts +11 -4
- package/src/build.ts +28 -17
- package/src/cache.ts +33 -1
- package/src/client.ts +49 -12
- package/src/dev-server.ts +11 -4
- package/src/link.ts +24 -3
- package/src/middleware.ts +12 -7
- package/src/multipart.ts +32 -1
- package/src/native-route-matcher.ts +34 -13
- package/src/navigation.ts +11 -2
- package/src/render.ts +321 -113
- package/src/route-source.ts +14 -10
- package/src/route-styles.ts +28 -3
- package/src/serve.ts +68 -15
- package/src/server-action-inference.ts +86 -0
- package/src/vite.ts +59 -12
package/src/client.ts
CHANGED
|
@@ -2240,6 +2240,7 @@ export async function buildClientRouteEntrySource(
|
|
|
2240
2240
|
},
|
|
2241
2241
|
fetchRevalidationInstalled: false,
|
|
2242
2242
|
installed: false,
|
|
2243
|
+
prefetchedUrls: new Set(),
|
|
2243
2244
|
prefetchedScripts: new Set(),
|
|
2244
2245
|
reloadNextNavigationFetch: false,
|
|
2245
2246
|
routePrefetchManifest: undefined,
|
|
@@ -2593,6 +2594,12 @@ export async function __mreactPrefetch(url) {
|
|
|
2593
2594
|
return false;
|
|
2594
2595
|
}
|
|
2595
2596
|
|
|
2597
|
+
if (__mreactNavigationState.prefetchedUrls.has(href)) {
|
|
2598
|
+
return true;
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
__mreactNavigationState.prefetchedUrls.add(href);
|
|
2602
|
+
|
|
2596
2603
|
const script = __mreactRouteScriptForNavigationUrl(href);
|
|
2597
2604
|
|
|
2598
2605
|
if (script === undefined) {
|
|
@@ -2690,6 +2697,10 @@ export async function __mreactNavigate(url, options = {}) {
|
|
|
2690
2697
|
|
|
2691
2698
|
try {
|
|
2692
2699
|
const cachedHtml = __mreactCachedNavigationHtml(href);
|
|
2700
|
+
const script = __mreactRouteScriptForNavigationUrl(href);
|
|
2701
|
+
if (script !== undefined) {
|
|
2702
|
+
void __mreactPrefetchRouteScript(script);
|
|
2703
|
+
}
|
|
2693
2704
|
const html = cachedHtml ?? await __mreactFetchNavigationHtml(href);
|
|
2694
2705
|
|
|
2695
2706
|
if (typeof html !== "string") {
|
|
@@ -2842,12 +2853,14 @@ export function __mreactInvalidateNavigationCache(path) {
|
|
|
2842
2853
|
for (const href of Array.from(__mreactNavigationState.cache.keys())) {
|
|
2843
2854
|
if (__mreactNormalizeNavigationPath(href) === normalizedPath) {
|
|
2844
2855
|
__mreactNavigationState.cache.delete(href);
|
|
2856
|
+
__mreactNavigationState.prefetchedUrls.delete(href);
|
|
2845
2857
|
}
|
|
2846
2858
|
}
|
|
2847
2859
|
}
|
|
2848
2860
|
|
|
2849
2861
|
function __mreactInvalidateAllNavigationCache() {
|
|
2850
2862
|
__mreactNavigationState.cache.clear();
|
|
2863
|
+
__mreactNavigationState.prefetchedUrls.clear();
|
|
2851
2864
|
}
|
|
2852
2865
|
|
|
2853
2866
|
function __mreactFetchNavigationHtml(href) {
|
|
@@ -2903,11 +2916,19 @@ function __mreactNormalizeNavigationPath(path) {
|
|
|
2903
2916
|
}
|
|
2904
2917
|
|
|
2905
2918
|
export function __mreactRestoreHistoryState(state) {
|
|
2906
|
-
if (state === null || state === undefined || state.__mreact !== true
|
|
2919
|
+
if (state === null || state === undefined || state.__mreact !== true) {
|
|
2907
2920
|
return false;
|
|
2908
2921
|
}
|
|
2909
2922
|
|
|
2910
|
-
const
|
|
2923
|
+
const html = typeof state.html === "string"
|
|
2924
|
+
? state.html
|
|
2925
|
+
: typeof state.url === "string" ? __mreactNavigationState.cache.get(state.url) : undefined;
|
|
2926
|
+
|
|
2927
|
+
if (typeof html !== "string") {
|
|
2928
|
+
return false;
|
|
2929
|
+
}
|
|
2930
|
+
|
|
2931
|
+
const applied = __mreactApplyNavigationHtml(html, state.url);
|
|
2911
2932
|
|
|
2912
2933
|
if (!applied) {
|
|
2913
2934
|
return false;
|
|
@@ -3120,15 +3141,14 @@ function __mreactSyncRouteDataScripts(root, currentRouteId, nextRouteId) {
|
|
|
3120
3141
|
return;
|
|
3121
3142
|
}
|
|
3122
3143
|
|
|
3123
|
-
for (const
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3144
|
+
for (const id of managedIds) {
|
|
3145
|
+
document.getElementById(id)?.remove();
|
|
3146
|
+
const next = typeof root.getElementById === "function"
|
|
3147
|
+
? root.getElementById(id)
|
|
3148
|
+
: root.querySelector(\`#\${id}\`);
|
|
3128
3149
|
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
document.body.appendChild(element);
|
|
3150
|
+
if (next !== null && next !== undefined) {
|
|
3151
|
+
document.body.appendChild(next);
|
|
3132
3152
|
}
|
|
3133
3153
|
}
|
|
3134
3154
|
}
|
|
@@ -3155,13 +3175,29 @@ function __mreactRouteDataScriptSelector() {
|
|
|
3155
3175
|
function __mreactCurrentHistoryState(url) {
|
|
3156
3176
|
return {
|
|
3157
3177
|
__mreact: true,
|
|
3158
|
-
html:
|
|
3178
|
+
html: __mreactCurrentDocumentRouteHtml(),
|
|
3159
3179
|
scrollX: Number(globalThis.scrollX ?? 0),
|
|
3160
3180
|
scrollY: Number(globalThis.scrollY ?? 0),
|
|
3161
3181
|
url,
|
|
3162
3182
|
};
|
|
3163
3183
|
}
|
|
3164
3184
|
|
|
3185
|
+
function __mreactCurrentDocumentRouteHtml() {
|
|
3186
|
+
if (typeof document === "undefined") {
|
|
3187
|
+
return undefined;
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
const marker = document.querySelector("[" + __mreactRouteMarkerAttribute + "]");
|
|
3191
|
+
if (marker === null) {
|
|
3192
|
+
return undefined;
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
const routeDataScripts = Array.from(document.querySelectorAll(__mreactRouteDataScriptSelector()))
|
|
3196
|
+
.map((script) => script.outerHTML)
|
|
3197
|
+
.join("");
|
|
3198
|
+
return marker.outerHTML + routeDataScripts;
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3165
3201
|
function __mreactPushHistoryState(url) {
|
|
3166
3202
|
if (typeof history === "undefined" || url === undefined) {
|
|
3167
3203
|
return;
|
|
@@ -4245,7 +4281,8 @@ export function cell(initial) {
|
|
|
4245
4281
|
buildApi.onLoad({ filter: /^devtools$/, namespace: "mreact-devtools-stub" }, () => ({
|
|
4246
4282
|
contents: `export function emitReactiveDevtoolsEvent() {}
|
|
4247
4283
|
export function hasReactiveDevtoolsEmitter() { return false; }
|
|
4248
|
-
export function currentDevtoolsEmitter() { return undefined; }
|
|
4284
|
+
export function currentDevtoolsEmitter() { return undefined; }
|
|
4285
|
+
export function currentReactiveDevtools() { return undefined; }`,
|
|
4249
4286
|
loader: "ts",
|
|
4250
4287
|
}));
|
|
4251
4288
|
buildApi.onLoad({ filter: /\.(?:mreact\.)?[cm]?[jt]sx$/ }, async (args) => {
|
package/src/dev-server.ts
CHANGED
|
@@ -28,6 +28,7 @@ export interface StartDevServerOptions extends AppRouterProjectOptions {
|
|
|
28
28
|
logger?: AppRouterLogger | undefined;
|
|
29
29
|
routeCache?: AppRouterCache | undefined;
|
|
30
30
|
serverActions?: AppRouterServerActionOptions | undefined;
|
|
31
|
+
verboseErrors?: boolean | undefined;
|
|
31
32
|
viteConfig?: UserConfig | undefined;
|
|
32
33
|
onUpgrade?: HttpUpgradeHandler | undefined;
|
|
33
34
|
}
|
|
@@ -87,7 +88,7 @@ export async function startDevServer(
|
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
if (vite === undefined) {
|
|
90
|
-
sendDevServerError(outgoing, new Error("Vite dev server is not initialized."));
|
|
91
|
+
sendDevServerError(outgoing, new Error("Vite dev server is not initialized."), options);
|
|
91
92
|
return;
|
|
92
93
|
}
|
|
93
94
|
|
|
@@ -96,7 +97,7 @@ export async function startDevServer(
|
|
|
96
97
|
if (error instanceof Error) {
|
|
97
98
|
vite?.ssrFixStacktrace(error);
|
|
98
99
|
}
|
|
99
|
-
sendDevServerError(outgoing, error);
|
|
100
|
+
sendDevServerError(outgoing, error, options);
|
|
100
101
|
return;
|
|
101
102
|
}
|
|
102
103
|
|
|
@@ -263,8 +264,14 @@ function definedProjectOptions(options: StartDevServerOptions): AppRouterProject
|
|
|
263
264
|
};
|
|
264
265
|
}
|
|
265
266
|
|
|
266
|
-
function sendDevServerError(
|
|
267
|
+
function sendDevServerError(
|
|
268
|
+
outgoing: ServerResponse,
|
|
269
|
+
error: unknown,
|
|
270
|
+
options: Pick<StartDevServerOptions, "verboseErrors">,
|
|
271
|
+
): void {
|
|
267
272
|
outgoing.statusCode = 500;
|
|
268
273
|
outgoing.setHeader("content-type", "text/plain; charset=utf-8");
|
|
269
|
-
outgoing.end(
|
|
274
|
+
outgoing.end(options.verboseErrors === true
|
|
275
|
+
? error instanceof Error ? error.stack : String(error)
|
|
276
|
+
: "Internal Server Error");
|
|
270
277
|
}
|
package/src/link.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { escapeHtmlAttribute } from "@reckona/mreact-shared/html-escape";
|
|
1
|
+
import { escapeHtmlAttribute, escapeHtmlText } from "@reckona/mreact-shared/html-escape";
|
|
2
2
|
import type { HtmlSink } from "@reckona/mreact-shared/compiler-contract";
|
|
3
3
|
import type { ReactCompatElement, ReactCompatNode } from "@reckona/mreact-compat";
|
|
4
4
|
import { safeUrlAttributeValue } from "@reckona/mreact-shared/url-safety";
|
|
@@ -87,7 +87,14 @@ function createAnchorElement(props: Record<string, unknown>): HTMLAnchorElement
|
|
|
87
87
|
continue;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
const attrName = attributeName(name);
|
|
91
|
+
const safeValue = safeUrlAttributeValue(attrName, String(value));
|
|
92
|
+
|
|
93
|
+
if (safeValue === undefined) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
anchor.setAttribute(attrName, safeValue);
|
|
91
98
|
}
|
|
92
99
|
|
|
93
100
|
return anchor;
|
|
@@ -164,5 +171,19 @@ function renderChildren(child: LinkChild): string {
|
|
|
164
171
|
return child.map(renderChildren).join("");
|
|
165
172
|
}
|
|
166
173
|
|
|
167
|
-
|
|
174
|
+
if (typeof child === "string" && isPreRenderedSafeHtmlChild(child)) {
|
|
175
|
+
return child;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return escapeHtmlText(child);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function isPreRenderedSafeHtmlChild(value: string): boolean {
|
|
182
|
+
if (!/[<&]/.test(value)) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return !/(?:<\s*\/?\s*(?:script|style|iframe|object|embed|link|meta|base)\b|\son[a-z]+\s*=|javascript\s*:)/iu.test(
|
|
187
|
+
value,
|
|
188
|
+
);
|
|
168
189
|
}
|
package/src/middleware.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { normalizeRoutePath } from "./route-path.js";
|
|
2
|
+
|
|
1
3
|
export interface RouteMiddlewareControl {
|
|
2
4
|
skip?: boolean | readonly string[];
|
|
3
5
|
}
|
|
@@ -133,20 +135,23 @@ export function mergeRouteMiddlewareControls(
|
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
function middlewarePatternMatches(pattern: string, pathname: string): boolean {
|
|
136
|
-
|
|
138
|
+
const normalizedPattern = normalizeRoutePath(pattern);
|
|
139
|
+
const normalizedPathname = normalizeRoutePath(pathname);
|
|
140
|
+
|
|
141
|
+
if (normalizedPattern === normalizedPathname) {
|
|
137
142
|
return true;
|
|
138
143
|
}
|
|
139
144
|
|
|
140
|
-
if (
|
|
141
|
-
const prefix =
|
|
145
|
+
if (normalizedPattern.endsWith("/:path*")) {
|
|
146
|
+
const prefix = normalizedPattern.slice(0, -"/:path*".length);
|
|
142
147
|
|
|
143
|
-
return
|
|
148
|
+
return normalizedPathname === prefix || normalizedPathname.startsWith(`${prefix}/`);
|
|
144
149
|
}
|
|
145
150
|
|
|
146
|
-
if (
|
|
147
|
-
const prefix =
|
|
151
|
+
if (normalizedPattern.endsWith("*")) {
|
|
152
|
+
const prefix = normalizedPattern.slice(0, -1);
|
|
148
153
|
|
|
149
|
-
return
|
|
154
|
+
return normalizedPathname.startsWith(prefix);
|
|
150
155
|
}
|
|
151
156
|
|
|
152
157
|
return false;
|
package/src/multipart.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export interface MultipartStreamParseOptions {
|
|
2
2
|
fields?: Readonly<Record<string, MultipartStreamFieldOptions>>;
|
|
3
|
+
maxParts?: number;
|
|
3
4
|
maxBytes?: number;
|
|
4
5
|
}
|
|
5
6
|
|
|
@@ -11,6 +12,7 @@ export interface MultipartStreamFieldOptions {
|
|
|
11
12
|
export interface MultipartStreamPart {
|
|
12
13
|
name: string;
|
|
13
14
|
filename?: string;
|
|
15
|
+
safeFilename?: string;
|
|
14
16
|
contentType?: string;
|
|
15
17
|
headers: Headers;
|
|
16
18
|
body: ReadableStream<Uint8Array<ArrayBufferLike>>;
|
|
@@ -34,6 +36,8 @@ interface PartWriter {
|
|
|
34
36
|
const crlf = "\r\n";
|
|
35
37
|
const headerSeparator = "\r\n\r\n";
|
|
36
38
|
const maxHeaderBytes = 64 * 1024;
|
|
39
|
+
export const defaultMultipartMaxBytes = 10 * 1024 * 1024;
|
|
40
|
+
export const defaultMultipartMaxParts = 1_000;
|
|
37
41
|
|
|
38
42
|
export function parseMultipartStream(
|
|
39
43
|
request: Request,
|
|
@@ -73,6 +77,7 @@ async function parseMultipartRequest(
|
|
|
73
77
|
const keepTailBytes = delimiter.length + 4;
|
|
74
78
|
let buffer: Uint8Array<ArrayBufferLike> = new Uint8Array();
|
|
75
79
|
let totalBytes = 0;
|
|
80
|
+
let totalParts = 0;
|
|
76
81
|
let started = false;
|
|
77
82
|
let currentPart: PartWriter | undefined;
|
|
78
83
|
|
|
@@ -87,7 +92,7 @@ async function parseMultipartRequest(
|
|
|
87
92
|
|
|
88
93
|
if (!next.done) {
|
|
89
94
|
totalBytes += next.value.byteLength;
|
|
90
|
-
enforceByteLimit("multipart request", totalBytes, options.maxBytes);
|
|
95
|
+
enforceByteLimit("multipart request", totalBytes, options.maxBytes ?? defaultMultipartMaxBytes);
|
|
91
96
|
buffer = concatBytes(buffer, next.value);
|
|
92
97
|
}
|
|
93
98
|
|
|
@@ -136,6 +141,8 @@ async function parseMultipartRequest(
|
|
|
136
141
|
break;
|
|
137
142
|
}
|
|
138
143
|
|
|
144
|
+
totalParts += 1;
|
|
145
|
+
enforcePartLimit(totalParts, options.maxParts ?? defaultMultipartMaxParts);
|
|
139
146
|
currentPart = createPartWriter(
|
|
140
147
|
decodeAscii(buffer.slice(0, headerEnd)),
|
|
141
148
|
options,
|
|
@@ -266,6 +273,7 @@ function createPart(options: {
|
|
|
266
273
|
|
|
267
274
|
if (options.filename !== undefined) {
|
|
268
275
|
part.filename = options.filename;
|
|
276
|
+
part.safeFilename = sanitizeMultipartFilename(options.filename);
|
|
269
277
|
}
|
|
270
278
|
|
|
271
279
|
const contentType = options.headers.get("content-type");
|
|
@@ -330,6 +338,29 @@ function enforceByteLimit(label: string, bytes: number, maxBytes: number | undef
|
|
|
330
338
|
}
|
|
331
339
|
}
|
|
332
340
|
|
|
341
|
+
function enforcePartLimit(parts: number, maxParts: number | undefined): void {
|
|
342
|
+
if (maxParts !== undefined && parts > maxParts) {
|
|
343
|
+
throw new Error(`multipart request exceeded ${maxParts} parts.`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export function sanitizeMultipartFilename(filename: string): string {
|
|
348
|
+
const leaf = filename.split(/[\\/]/u).at(-1) ?? "";
|
|
349
|
+
let sanitized = "";
|
|
350
|
+
|
|
351
|
+
for (const char of leaf) {
|
|
352
|
+
sanitized += isControlCharacter(char) ? "_" : char;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return sanitized === "" || sanitized === "." || sanitized === ".." ? "upload.bin" : sanitized;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function isControlCharacter(char: string): boolean {
|
|
359
|
+
const code = char.charCodeAt(0);
|
|
360
|
+
|
|
361
|
+
return code < 0x20 || code === 0x7f;
|
|
362
|
+
}
|
|
363
|
+
|
|
333
364
|
function parseMultipartBoundary(contentType: string | null): string | undefined {
|
|
334
365
|
if (contentType === null) {
|
|
335
366
|
return undefined;
|
|
@@ -52,22 +52,43 @@ export function createNativeRouteMatcher(
|
|
|
52
52
|
|
|
53
53
|
return {
|
|
54
54
|
match(pathname): MatchedRoute | undefined {
|
|
55
|
-
|
|
55
|
+
return matchNativeRoute(matcher, sortedRoutes, pathname);
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
export function __matchNativeRouteForTesting(
|
|
61
|
+
matcher: NativeRouteMatcherInstance,
|
|
62
|
+
sortedRoutes: readonly AppRoute[],
|
|
63
|
+
pathname: string,
|
|
64
|
+
): MatchedRoute | undefined {
|
|
65
|
+
return matchNativeRoute(matcher, sortedRoutes, pathname);
|
|
66
|
+
}
|
|
60
67
|
|
|
61
|
-
|
|
68
|
+
function matchNativeRoute(
|
|
69
|
+
matcher: NativeRouteMatcherInstance,
|
|
70
|
+
sortedRoutes: readonly AppRoute[],
|
|
71
|
+
pathname: string,
|
|
72
|
+
): MatchedRoute | undefined {
|
|
73
|
+
let output: NativeMatchOutput | null | undefined;
|
|
74
|
+
try {
|
|
75
|
+
output = matcher.matchRoute(pathname);
|
|
76
|
+
} catch {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
62
79
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
80
|
+
if (output == null) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const route = sortedRoutes[output.index];
|
|
85
|
+
|
|
86
|
+
return route === undefined
|
|
87
|
+
? undefined
|
|
88
|
+
: {
|
|
89
|
+
route,
|
|
90
|
+
params: normalizeNativeParams(route, output.params),
|
|
91
|
+
};
|
|
71
92
|
}
|
|
72
93
|
|
|
73
94
|
export function normalizeNativeParams(
|
package/src/navigation.ts
CHANGED
|
@@ -57,6 +57,10 @@ function throwUnsafeRedirect(location: string): never {
|
|
|
57
57
|
);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
function throwUnsafeRewrite(location: string): never {
|
|
61
|
+
throw new TypeError(`unsafe rewrite target: ${JSON.stringify(location)}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
60
64
|
export function redirect(location: string, options: RedirectOptions = {}): never {
|
|
61
65
|
if (!isSafeInternalRedirect(location)) {
|
|
62
66
|
throwUnsafeRedirect(location);
|
|
@@ -134,6 +138,10 @@ export function next(): MiddlewareNext {
|
|
|
134
138
|
}
|
|
135
139
|
|
|
136
140
|
export function rewrite(location: string, init: ResponseInit = {}): Response {
|
|
141
|
+
if (!isSafeInternalRedirect(location)) {
|
|
142
|
+
throwUnsafeRewrite(location);
|
|
143
|
+
}
|
|
144
|
+
|
|
137
145
|
const response = new Response(null, {
|
|
138
146
|
...init,
|
|
139
147
|
status: init.status ?? 200,
|
|
@@ -198,10 +206,11 @@ export function cookies(request: Request): RequestCookies {
|
|
|
198
206
|
|
|
199
207
|
export function rewriteLocation(response: Response): string | undefined {
|
|
200
208
|
const marked = (response as { [rewriteLocationSymbol]?: unknown })[rewriteLocationSymbol];
|
|
201
|
-
|
|
202
|
-
return typeof marked === "string"
|
|
209
|
+
const candidate = typeof marked === "string"
|
|
203
210
|
? marked
|
|
204
211
|
: response.headers.get(rewriteHeaderName) ?? undefined;
|
|
212
|
+
|
|
213
|
+
return candidate !== undefined && isSafeInternalRedirect(candidate) ? candidate : undefined;
|
|
205
214
|
}
|
|
206
215
|
|
|
207
216
|
export function isRedirectError(error: unknown): error is Error & { location: string; status: number } {
|