@timber-js/app 0.2.0-alpha.97 → 0.2.0-alpha.98
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/dist/_chunks/{metadata-routes-DS3eKNmf.js → metadata-routes-BU684ls2.js} +1 -1
- package/dist/_chunks/{metadata-routes-DS3eKNmf.js.map → metadata-routes-BU684ls2.js.map} +1 -1
- package/dist/_chunks/segment-classify-BjfuctV2.js +137 -0
- package/dist/_chunks/segment-classify-BjfuctV2.js.map +1 -0
- package/dist/_chunks/{interception-BbqMCVXa.js → walkers-VOXgavMF.js} +61 -85
- package/dist/_chunks/walkers-VOXgavMF.js.map +1 -0
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +55 -5
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +189 -62
- package/dist/index.js.map +1 -1
- package/dist/plugins/build-report.d.ts +6 -4
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/dev-404-page.d.ts +8 -18
- package/dist/plugins/dev-404-page.d.ts.map +1 -1
- package/dist/routing/index.d.ts +5 -3
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +3 -3
- package/dist/routing/scanner.d.ts +1 -10
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/segment-classify.d.ts +37 -8
- package/dist/routing/segment-classify.d.ts.map +1 -1
- package/dist/routing/types.d.ts +63 -23
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/routing/walkers.d.ts +51 -0
- package/dist/routing/walkers.d.ts.map +1 -0
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/dev-holding-server.d.ts +4 -2
- package/dist/server/dev-holding-server.d.ts.map +1 -1
- package/dist/server/html-injector-core.d.ts +212 -0
- package/dist/server/html-injector-core.d.ts.map +1 -0
- package/dist/server/html-injectors.d.ts +59 -59
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/internal.js +710 -563
- package/dist/server/internal.js.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +46 -49
- package/dist/server/node-stream-transforms.d.ts.map +1 -1
- package/dist/server/pipeline-helpers.d.ts +88 -0
- package/dist/server/pipeline-helpers.d.ts.map +1 -0
- package/dist/server/pipeline-phases.d.ts +97 -0
- package/dist/server/pipeline-phases.d.ts.map +1 -0
- package/dist/server/pipeline.d.ts +53 -32
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/port-resolution.d.ts +117 -0
- package/dist/server/port-resolution.d.ts.map +1 -0
- package/dist/server/route-matcher.d.ts +20 -47
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +74 -0
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -0
- package/dist/server/status-code-resolver.d.ts +16 -11
- package/dist/server/status-code-resolver.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/utils/directive-parser.d.ts +0 -45
- package/dist/utils/directive-parser.d.ts.map +1 -1
- package/package.json +7 -6
- package/src/adapters/nitro.ts +55 -5
- package/src/cli.ts +0 -0
- package/src/index.ts +84 -31
- package/src/plugins/build-report.ts +13 -22
- package/src/plugins/dev-404-page.ts +15 -41
- package/src/plugins/routing.ts +14 -12
- package/src/routing/codegen.ts +1 -1
- package/src/routing/convention-lint.ts +4 -4
- package/src/routing/index.ts +5 -3
- package/src/routing/interception.ts +1 -1
- package/src/routing/scanner.ts +17 -93
- package/src/routing/segment-classify.ts +107 -8
- package/src/routing/status-file-lint.ts +3 -3
- package/src/routing/types.ts +63 -23
- package/src/routing/walkers.ts +90 -0
- package/src/server/action-handler.ts +6 -0
- package/src/server/deny-renderer.ts +5 -5
- package/src/server/dev-holding-server.ts +4 -2
- package/src/server/fallback-error.ts +1 -1
- package/src/server/html-injector-core.ts +403 -0
- package/src/server/html-injectors.ts +158 -297
- package/src/server/node-stream-transforms.ts +108 -248
- package/src/server/pipeline-helpers.ts +180 -0
- package/src/server/pipeline-phases.ts +591 -0
- package/src/server/pipeline.ts +76 -539
- package/src/server/port-resolution.ts +215 -0
- package/src/server/route-element-builder.ts +1 -1
- package/src/server/route-matcher.ts +28 -60
- package/src/server/rsc-entry/api-handler.ts +2 -2
- package/src/server/rsc-entry/error-renderer.ts +1 -1
- package/src/server/rsc-entry/index.ts +52 -98
- package/src/server/rsc-entry/wrap-action-dispatch.ts +156 -0
- package/src/server/sitemap-generator.ts +1 -1
- package/src/server/slot-resolver.ts +1 -1
- package/src/server/status-code-resolver.ts +112 -128
- package/src/server/tree-builder.ts +6 -4
- package/src/utils/directive-parser.ts +0 -392
- package/LICENSE +0 -8
- package/dist/_chunks/interception-BbqMCVXa.js.map +0 -1
- package/dist/_chunks/segment-classify-BDNn6EzD.js +0 -65
- package/dist/_chunks/segment-classify-BDNn6EzD.js.map +0 -1
- package/dist/server/manifest-status-resolver.d.ts +0 -58
- package/dist/server/manifest-status-resolver.d.ts.map +0 -1
- package/src/server/manifest-status-resolver.ts +0 -215
package/src/routing/types.ts
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The route tree is built by scanning the app/ directory and recognizing
|
|
5
5
|
* file conventions (page.*, layout.*, middleware.ts, access.ts, route.ts, etc.).
|
|
6
|
+
*
|
|
7
|
+
* **Single shape, two specializations** (TIM-848):
|
|
8
|
+
*
|
|
9
|
+
* `SegmentNode<TFile>` is the one canonical in-memory shape for the
|
|
10
|
+
* timber route tree. The same interface is used at build time (with
|
|
11
|
+
* `TFile = RouteFile`) and at request time (with `TFile = ManifestFile`,
|
|
12
|
+
* see `server/route-matcher.ts`). Walkers parameterized over `TFile`
|
|
13
|
+
* work on either, eliminating the previous duplication between
|
|
14
|
+
* `SegmentNode` (Map-based) and `ManifestSegmentNode` (object-based).
|
|
15
|
+
*
|
|
16
|
+
* Keyed groups (`slots`, `statusFiles`, `jsonStatusFiles`,
|
|
17
|
+
* `legacyStatusFiles`, `metadataRoutes`) are plain `Record<string, …>`
|
|
18
|
+
* objects rather than `Map`s so that the build-time tree can be
|
|
19
|
+
* serialized into the virtual route manifest with no shape transform.
|
|
20
|
+
*
|
|
21
|
+
* See design/07-routing.md §"Route Tree Shape" and design/18-build-system.md
|
|
22
|
+
* §"Route Manifest Shape".
|
|
6
23
|
*/
|
|
7
24
|
|
|
8
25
|
/** Segment type classification */
|
|
@@ -27,7 +44,14 @@ export type InterceptionMarker = '(.)' | '(..)' | '(...)' | '(..)(..)';
|
|
|
27
44
|
/** All recognized interception markers, ordered longest-first for parsing. */
|
|
28
45
|
export const INTERCEPTION_MARKERS: InterceptionMarker[] = ['(..)(..)', '(.)', '(..)', '(...)'];
|
|
29
46
|
|
|
30
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* A single file discovered in a route segment at build time.
|
|
49
|
+
*
|
|
50
|
+
* The runtime equivalent (`ManifestFile`, defined in
|
|
51
|
+
* `server/route-matcher.ts`) replaces `extension` with a lazy `load`
|
|
52
|
+
* function. Walkers that only need `filePath` are parameterized over
|
|
53
|
+
* `TFile` and accept either.
|
|
54
|
+
*/
|
|
31
55
|
export interface RouteFile {
|
|
32
56
|
/** Absolute path to the file */
|
|
33
57
|
filePath: string;
|
|
@@ -35,8 +59,17 @@ export interface RouteFile {
|
|
|
35
59
|
extension: string;
|
|
36
60
|
}
|
|
37
61
|
|
|
38
|
-
/**
|
|
39
|
-
|
|
62
|
+
/**
|
|
63
|
+
* A node in the segment tree.
|
|
64
|
+
*
|
|
65
|
+
* Generic over `TFile` so the same interface describes both the
|
|
66
|
+
* build-time tree (`SegmentNode<RouteFile>`, the default) and the
|
|
67
|
+
* runtime manifest tree (`SegmentNode<ManifestFile>`, aliased as
|
|
68
|
+
* `ManifestSegmentNode`). All keyed groups use `Record` (not `Map`)
|
|
69
|
+
* so the build-time tree serializes to the virtual route manifest
|
|
70
|
+
* with no shape transform.
|
|
71
|
+
*/
|
|
72
|
+
export interface SegmentNode<TFile = RouteFile> {
|
|
40
73
|
/** The raw directory name (e.g. "dashboard", "[id]", "(auth)", "@sidebar") */
|
|
41
74
|
segmentName: string;
|
|
42
75
|
/** Classified segment type */
|
|
@@ -54,43 +87,50 @@ export interface SegmentNode {
|
|
|
54
87
|
interceptedSegmentName?: string;
|
|
55
88
|
|
|
56
89
|
// --- File conventions ---
|
|
57
|
-
page?:
|
|
58
|
-
layout?:
|
|
59
|
-
middleware?:
|
|
60
|
-
access?:
|
|
61
|
-
route?:
|
|
90
|
+
page?: TFile;
|
|
91
|
+
layout?: TFile;
|
|
92
|
+
middleware?: TFile;
|
|
93
|
+
access?: TFile;
|
|
94
|
+
route?: TFile;
|
|
62
95
|
/**
|
|
63
96
|
* params.ts — isomorphic convention file exporting segmentParams and/or searchParams.
|
|
64
97
|
* Discovered by the scanner like middleware.ts and access.ts.
|
|
65
98
|
* See design/07-routing.md §"params.ts Convention File"
|
|
66
99
|
*/
|
|
67
|
-
params?:
|
|
68
|
-
error?:
|
|
69
|
-
default?:
|
|
100
|
+
params?: TFile;
|
|
101
|
+
error?: TFile;
|
|
102
|
+
default?: TFile;
|
|
70
103
|
/** Status-code files: 4xx.tsx, 5xx.tsx, {status}.tsx (component format) */
|
|
71
|
-
statusFiles?:
|
|
104
|
+
statusFiles?: Record<string, TFile>;
|
|
72
105
|
/** JSON status-code files: 4xx.json, 5xx.json, {status}.json */
|
|
73
|
-
jsonStatusFiles?:
|
|
106
|
+
jsonStatusFiles?: Record<string, TFile>;
|
|
74
107
|
/** denied.tsx — slot-only denial rendering */
|
|
75
|
-
denied?:
|
|
108
|
+
denied?: TFile;
|
|
76
109
|
/** Legacy compat: not-found.tsx (maps to 404), forbidden.tsx (403), unauthorized.tsx (401) */
|
|
77
|
-
legacyStatusFiles?:
|
|
110
|
+
legacyStatusFiles?: Record<string, TFile>;
|
|
78
111
|
|
|
79
112
|
/** Metadata route files (sitemap.ts, robots.ts, icon.tsx, etc.) keyed by base name */
|
|
80
|
-
metadataRoutes?:
|
|
113
|
+
metadataRoutes?: Record<string, TFile>;
|
|
81
114
|
|
|
82
115
|
// --- Children ---
|
|
83
|
-
children: SegmentNode[];
|
|
116
|
+
children: SegmentNode<TFile>[];
|
|
84
117
|
/** Parallel route slots (keyed by slot name without @) */
|
|
85
|
-
slots:
|
|
118
|
+
slots: Record<string, SegmentNode<TFile>>;
|
|
86
119
|
}
|
|
87
120
|
|
|
88
|
-
/**
|
|
89
|
-
|
|
121
|
+
/**
|
|
122
|
+
* The full route tree output from the scanner (or the root of the
|
|
123
|
+
* runtime route manifest, when `TFile = ManifestFile`).
|
|
124
|
+
*
|
|
125
|
+
* Generic so the same wrapper carries app-root metadata for both
|
|
126
|
+
* shapes. The runtime manifest extends this with `viteRoot` (see
|
|
127
|
+
* `ManifestRoot` in `server/route-matcher.ts`).
|
|
128
|
+
*/
|
|
129
|
+
export interface RouteTree<TFile = RouteFile> {
|
|
90
130
|
/** The root segment node (representing app/) */
|
|
91
|
-
root: SegmentNode
|
|
131
|
+
root: SegmentNode<TFile>;
|
|
92
132
|
/** All discovered proxy.ts files (should be at most one, in app/) */
|
|
93
|
-
proxy?:
|
|
133
|
+
proxy?: TFile;
|
|
94
134
|
/**
|
|
95
135
|
* Global error page: app/global-error.{tsx,ts,jsx,js}
|
|
96
136
|
*
|
|
@@ -100,7 +140,7 @@ export interface RouteTree {
|
|
|
100
140
|
*
|
|
101
141
|
* See design/10-error-handling.md §"Tier 2 — Global Error Page"
|
|
102
142
|
*/
|
|
103
|
-
globalError?:
|
|
143
|
+
globalError?: TFile;
|
|
104
144
|
}
|
|
105
145
|
|
|
106
146
|
/** Configuration passed to the scanner */
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared route-tree walkers (TIM-848).
|
|
3
|
+
*
|
|
4
|
+
* Tiny helpers that walk a `SegmentNode<TFile>` tree generically. Both the
|
|
5
|
+
* build-time tree (`SegmentNode<RouteFile>`) and the runtime manifest
|
|
6
|
+
* tree (`SegmentNode<ManifestFile>`) flow through these helpers because
|
|
7
|
+
* the walker only reads the structural fields shared by both shapes
|
|
8
|
+
* (`children`, `slots`, `page`, `route`, `urlPath`).
|
|
9
|
+
*
|
|
10
|
+
* Before this module, three near-identical `collectRoutes` functions
|
|
11
|
+
* lived in `plugins/dev-404-page.ts`, `plugins/build-report.ts`, and
|
|
12
|
+
* `routing/codegen.ts`. The codegen one is special-purpose (it
|
|
13
|
+
* accumulates `ParamEntry[]` and resolves codec chains) and stays
|
|
14
|
+
* local; the other two now share `collectLeafRoutes` from this file.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { SegmentNode } from './types.js';
|
|
18
|
+
|
|
19
|
+
/** A leaf route discovered while walking the segment tree. */
|
|
20
|
+
export interface LeafRoute<TFile> {
|
|
21
|
+
/** URL path of the leaf (root is "/"). */
|
|
22
|
+
urlPath: string;
|
|
23
|
+
/** Segment chain from root to this leaf, inclusive. */
|
|
24
|
+
segments: SegmentNode<TFile>[];
|
|
25
|
+
/** The page file at this leaf, if any. */
|
|
26
|
+
page?: TFile;
|
|
27
|
+
/** The route handler file at this leaf, if any. */
|
|
28
|
+
route?: TFile;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Options for `collectLeafRoutes`. */
|
|
32
|
+
export interface CollectLeafRoutesOptions {
|
|
33
|
+
/**
|
|
34
|
+
* If true, recurse into parallel slots and emit slot leaves alongside
|
|
35
|
+
* the main route tree. Defaults to `false` because slots render
|
|
36
|
+
* alongside their parent at the same URL and are not separately
|
|
37
|
+
* URL-addressable. The build report excludes slots; route-listing
|
|
38
|
+
* UIs that want to show "all leaves with a page handler" can opt in.
|
|
39
|
+
*/
|
|
40
|
+
includeSlots?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Walk a segment tree and collect every leaf with a `page` or `route`
|
|
45
|
+
* handler. Generic over `TFile` so it works on both the build-time
|
|
46
|
+
* scanner output and the runtime manifest tree.
|
|
47
|
+
*
|
|
48
|
+
* - Pages and route handlers at the same URL produce two distinct
|
|
49
|
+
* entries (the build report deduplicates by URL afterward).
|
|
50
|
+
* - Parallel slots are skipped unless `includeSlots: true` (slots
|
|
51
|
+
* share their parent's URL and are not addressable on their own).
|
|
52
|
+
* - Result is sorted by `urlPath` for deterministic output.
|
|
53
|
+
*/
|
|
54
|
+
export function collectLeafRoutes<TFile>(
|
|
55
|
+
root: SegmentNode<TFile>,
|
|
56
|
+
options: CollectLeafRoutesOptions = {}
|
|
57
|
+
): LeafRoute<TFile>[] {
|
|
58
|
+
const { includeSlots = false } = options;
|
|
59
|
+
const result: LeafRoute<TFile>[] = [];
|
|
60
|
+
walk(root, [], result, includeSlots);
|
|
61
|
+
result.sort((a, b) => a.urlPath.localeCompare(b.urlPath));
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function walk<TFile>(
|
|
66
|
+
node: SegmentNode<TFile>,
|
|
67
|
+
chain: SegmentNode<TFile>[],
|
|
68
|
+
result: LeafRoute<TFile>[],
|
|
69
|
+
includeSlots: boolean
|
|
70
|
+
): void {
|
|
71
|
+
const currentChain = [...chain, node];
|
|
72
|
+
const path = node.urlPath || '/';
|
|
73
|
+
|
|
74
|
+
if (node.page) {
|
|
75
|
+
result.push({ urlPath: path, segments: currentChain, page: node.page });
|
|
76
|
+
}
|
|
77
|
+
if (node.route) {
|
|
78
|
+
result.push({ urlPath: path, segments: currentChain, route: node.route });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const child of node.children) {
|
|
82
|
+
walk(child, currentChain, result, includeSlots);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (includeSlots) {
|
|
86
|
+
for (const slotNode of Object.values(node.slots)) {
|
|
87
|
+
walk(slotNode, currentChain, result, includeSlots);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -158,6 +158,12 @@ export async function handleActionRequest(
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
// CSRF validation — reject cross-origin mutation requests.
|
|
161
|
+
//
|
|
162
|
+
// Defense-in-depth: the pipeline boundary in rsc-entry/index.ts already
|
|
163
|
+
// validates Origin on every unsafe-method request before dispatch reaches
|
|
164
|
+
// here, so this call is a no-op on the happy path. It is intentionally
|
|
165
|
+
// retained so that handleActionRequest remains safe to call from any
|
|
166
|
+
// future entry point that bypasses the wrapper. See LOCAL-773.
|
|
161
167
|
const csrfResult = validateCsrf(req, config.csrf);
|
|
162
168
|
if (!csrfResult.ok) {
|
|
163
169
|
return new Response(null, { status: csrfResult.status });
|
|
@@ -23,7 +23,7 @@ import { logRenderError } from './logger.js';
|
|
|
23
23
|
import { loadModule } from './safe-load.js';
|
|
24
24
|
import { isDebug } from './debug.js';
|
|
25
25
|
import { resolveMetadata, renderMetadataToElements } from './metadata.js';
|
|
26
|
-
import {
|
|
26
|
+
import { resolveStatusFile } from './status-code-resolver.js';
|
|
27
27
|
import type { ManifestSegmentNode } from './route-matcher.js';
|
|
28
28
|
import type { RouteMatch } from './pipeline.js';
|
|
29
29
|
import type { NavContext } from './ssr-entry.js';
|
|
@@ -87,7 +87,7 @@ export async function renderDenyPage(
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// Page routes → component chain first, JSON fallback only if no component found.
|
|
90
|
-
const resolution =
|
|
90
|
+
const resolution = resolveStatusFile(deny.status, segments, 'component');
|
|
91
91
|
|
|
92
92
|
// No component status file — try JSON chain before bare fallback
|
|
93
93
|
if (!resolution) {
|
|
@@ -99,7 +99,7 @@ export async function renderDenyPage(
|
|
|
99
99
|
// Dev warning: JSON status file exists but is shadowed by the component chain.
|
|
100
100
|
// This helps developers understand why their .json file isn't being served.
|
|
101
101
|
if (isDebug()) {
|
|
102
|
-
const jsonResolution =
|
|
102
|
+
const jsonResolution = resolveStatusFile(deny.status, segments, 'json');
|
|
103
103
|
if (jsonResolution) {
|
|
104
104
|
console.warn(
|
|
105
105
|
`[timber] ${jsonResolution.file.filePath} exists but is shadowed by ` +
|
|
@@ -190,7 +190,7 @@ export async function renderDenyPageAsRsc(
|
|
|
190
190
|
responseHeaders: Headers,
|
|
191
191
|
createDebugChannelSink: DebugChannelFactory
|
|
192
192
|
): Promise<Response> {
|
|
193
|
-
const resolution =
|
|
193
|
+
const resolution = resolveStatusFile(deny.status, segments, 'component');
|
|
194
194
|
|
|
195
195
|
if (!resolution) {
|
|
196
196
|
responseHeaders.set('content-type', `${RSC_CONTENT_TYPE}; charset=utf-8`);
|
|
@@ -249,7 +249,7 @@ async function renderDenyPageJson(
|
|
|
249
249
|
segments: ManifestSegmentNode[],
|
|
250
250
|
responseHeaders: Headers
|
|
251
251
|
): Promise<Response | null> {
|
|
252
|
-
const resolution =
|
|
252
|
+
const resolution = resolveStatusFile(deny.status, segments, 'json');
|
|
253
253
|
|
|
254
254
|
if (!resolution) {
|
|
255
255
|
return null;
|
|
@@ -119,9 +119,11 @@ const HOLDING_PAGE_HTML = [
|
|
|
119
119
|
*
|
|
120
120
|
* Usage (inside Vite plugin):
|
|
121
121
|
* ```ts
|
|
122
|
-
* // In config() hook — earliest point where port is known
|
|
122
|
+
* // In config() hook — earliest point where port is known.
|
|
123
|
+
* // Use bindWithBump() to honor the default-3000 + auto-bump policy
|
|
124
|
+
* // (see ../server/port-resolution.ts and TIM-842).
|
|
123
125
|
* const holding = createHoldingServer();
|
|
124
|
-
* holding.listen(
|
|
126
|
+
* await bindWithBump((p) => holding.listen(p), { startPort: 3000, autoBump: true });
|
|
125
127
|
*
|
|
126
128
|
* // In last plugin's configureServer() — wrap listen for seamless handoff
|
|
127
129
|
* const originalListen = server.listen.bind(server);
|
|
@@ -66,7 +66,7 @@ export async function renderFallbackError(
|
|
|
66
66
|
// then fall through to global-error.tsx if those also fail.
|
|
67
67
|
logRenderError({ method: req.method, path: new URL(req.url).pathname, error: layoutError });
|
|
68
68
|
}
|
|
69
|
-
const match: RouteMatch = { segments
|
|
69
|
+
const match: RouteMatch = { segments, segmentParams: {}, middlewareChain: [] };
|
|
70
70
|
return renderErrorPage(
|
|
71
71
|
error,
|
|
72
72
|
500,
|