@rangojs/router 0.0.0-experimental.259 → 0.0.0-experimental.26
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 +294 -28
- package/dist/bin/rango.js +355 -47
- package/dist/vite/index.js +1658 -1239
- package/package.json +3 -3
- package/skills/cache-guide/SKILL.md +9 -5
- package/skills/caching/SKILL.md +4 -4
- package/skills/document-cache/SKILL.md +2 -2
- package/skills/hooks/SKILL.md +40 -29
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +79 -0
- package/skills/layout/SKILL.md +62 -2
- package/skills/loader/SKILL.md +229 -15
- package/skills/middleware/SKILL.md +109 -30
- package/skills/parallel/SKILL.md +57 -2
- package/skills/prerender/SKILL.md +189 -19
- package/skills/rango/SKILL.md +1 -2
- package/skills/response-routes/SKILL.md +3 -3
- package/skills/route/SKILL.md +44 -3
- package/skills/router-setup/SKILL.md +80 -3
- package/skills/theme/SKILL.md +5 -4
- package/skills/typesafety/SKILL.md +59 -16
- package/skills/use-cache/SKILL.md +16 -2
- package/src/__internal.ts +1 -1
- package/src/bin/rango.ts +56 -19
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/event-controller.ts +29 -48
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +1 -1
- package/src/browser/link-interceptor.ts +19 -3
- package/src/browser/merge-segment-loaders.ts +9 -2
- package/src/browser/navigation-bridge.ts +66 -443
- package/src/browser/navigation-client.ts +34 -62
- package/src/browser/navigation-store.ts +4 -33
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/partial-update.ts +103 -151
- package/src/browser/prefetch/cache.ts +67 -0
- package/src/browser/prefetch/fetch.ts +137 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +154 -44
- package/src/browser/react/NavigationProvider.tsx +32 -0
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +2 -6
- package/src/browser/react/location-state-shared.ts +29 -11
- package/src/browser/react/location-state.ts +6 -4
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +23 -45
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +21 -64
- package/src/browser/react/use-navigation.ts +7 -32
- package/src/browser/react/use-params.ts +5 -34
- package/src/browser/react/use-pathname.ts +2 -3
- package/src/browser/react/use-router.ts +3 -6
- package/src/browser/react/use-search-params.ts +2 -1
- package/src/browser/react/use-segments.ts +75 -114
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +46 -22
- package/src/browser/scroll-restoration.ts +10 -7
- package/src/browser/server-action-bridge.ts +458 -405
- package/src/browser/types.ts +21 -35
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +38 -13
- package/src/build/generate-route-types.ts +4 -0
- package/src/build/index.ts +1 -0
- package/src/build/route-trie.ts +19 -3
- package/src/build/route-types/codegen.ts +13 -4
- package/src/build/route-types/include-resolution.ts +13 -0
- package/src/build/route-types/per-module-writer.ts +15 -3
- package/src/build/route-types/router-processing.ts +170 -18
- package/src/build/runtime-discovery.ts +13 -1
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +136 -123
- package/src/cache/cache-scope.ts +76 -83
- package/src/cache/cf/cf-cache-store.ts +12 -7
- package/src/cache/document-cache.ts +93 -69
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/index.ts +0 -15
- package/src/cache/memory-segment-store.ts +43 -69
- package/src/cache/profile-registry.ts +43 -8
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +140 -117
- package/src/cache/taint.ts +30 -3
- package/src/cache/types.ts +1 -115
- package/src/client.rsc.tsx +0 -1
- package/src/client.tsx +53 -76
- package/src/errors.ts +6 -1
- package/src/handle.ts +1 -1
- package/src/handles/MetaTags.tsx +5 -2
- package/src/host/cookie-handler.ts +8 -3
- package/src/host/index.ts +0 -3
- package/src/host/router.ts +14 -1
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +53 -10
- package/src/index.ts +73 -43
- package/src/loader.rsc.ts +12 -4
- package/src/loader.ts +8 -0
- package/src/prerender/store.ts +60 -18
- package/src/prerender.ts +76 -18
- package/src/reverse.ts +11 -7
- package/src/root-error-boundary.tsx +30 -26
- package/src/route-definition/dsl-helpers.ts +9 -6
- package/src/route-definition/index.ts +0 -3
- package/src/route-definition/redirect.ts +15 -3
- package/src/route-map-builder.ts +38 -2
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +7 -0
- package/src/router/content-negotiation.ts +1 -1
- package/src/router/debug-manifest.ts +16 -3
- package/src/router/handler-context.ts +96 -17
- package/src/router/intercept-resolution.ts +6 -4
- package/src/router/lazy-includes.ts +4 -0
- package/src/router/loader-resolution.ts +6 -11
- package/src/router/logging.ts +100 -3
- package/src/router/manifest.ts +32 -3
- package/src/router/match-api.ts +62 -54
- package/src/router/match-context.ts +3 -0
- package/src/router/match-handlers.ts +185 -11
- package/src/router/match-middleware/background-revalidation.ts +65 -85
- package/src/router/match-middleware/cache-lookup.ts +78 -10
- package/src/router/match-middleware/cache-store.ts +2 -0
- package/src/router/match-pipelines.ts +8 -43
- package/src/router/match-result.ts +0 -9
- package/src/router/metrics.ts +233 -13
- package/src/router/middleware-types.ts +34 -39
- package/src/router/middleware.ts +290 -130
- package/src/router/pattern-matching.ts +61 -10
- package/src/router/prerender-match.ts +36 -6
- package/src/router/preview-match.ts +7 -1
- package/src/router/revalidation.ts +61 -2
- package/src/router/router-context.ts +15 -0
- package/src/router/router-interfaces.ts +158 -40
- package/src/router/router-options.ts +223 -1
- package/src/router/router-registry.ts +5 -2
- package/src/router/segment-resolution/fresh.ts +165 -242
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +102 -98
- package/src/router/segment-resolution/revalidation.ts +394 -272
- package/src/router/segment-resolution/static-store.ts +2 -2
- package/src/router/segment-resolution.ts +1 -3
- package/src/router/segment-wrappers.ts +3 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +20 -2
- package/src/router/types.ts +7 -1
- package/src/router.ts +203 -18
- package/src/rsc/handler-context.ts +13 -2
- package/src/rsc/handler.ts +489 -438
- package/src/rsc/helpers.ts +125 -5
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +84 -42
- package/src/rsc/manifest-init.ts +3 -2
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +245 -19
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +47 -43
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +166 -66
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +20 -2
- package/src/search-params.ts +38 -23
- package/src/server/context.ts +61 -7
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +84 -12
- package/src/server/loader-registry.ts +11 -46
- package/src/server/request-context.ts +275 -49
- package/src/server.ts +6 -0
- package/src/ssr/index.tsx +67 -28
- package/src/static-handler.ts +7 -0
- package/src/theme/ThemeProvider.tsx +6 -1
- package/src/theme/index.ts +4 -18
- package/src/theme/theme-context.ts +1 -28
- package/src/theme/theme-script.ts +2 -1
- package/src/types/cache-types.ts +6 -1
- package/src/types/error-types.ts +3 -0
- package/src/types/global-namespace.ts +22 -0
- package/src/types/handler-context.ts +103 -16
- package/src/types/index.ts +1 -1
- package/src/types/loader-types.ts +9 -6
- package/src/types/route-config.ts +17 -26
- package/src/types/route-entry.ts +28 -0
- package/src/types/segments.ts +0 -5
- package/src/urls/include-helper.ts +49 -8
- package/src/urls/index.ts +1 -0
- package/src/urls/path-helper-types.ts +30 -12
- package/src/urls/path-helper.ts +17 -2
- package/src/urls/pattern-types.ts +21 -1
- package/src/urls/response-types.ts +29 -7
- package/src/urls/type-extraction.ts +23 -15
- package/src/use-loader.tsx +27 -9
- package/src/vite/discovery/bundle-postprocess.ts +32 -52
- package/src/vite/discovery/discover-routers.ts +52 -26
- package/src/vite/discovery/prerender-collection.ts +58 -41
- package/src/vite/discovery/route-types-writer.ts +7 -7
- package/src/vite/discovery/state.ts +7 -7
- package/src/vite/discovery/virtual-module-codegen.ts +5 -2
- package/src/vite/index.ts +10 -51
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +3 -3
- package/src/vite/plugins/expose-internal-ids.ts +4 -3
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +91 -3
- package/src/vite/plugins/version-plugin.ts +188 -18
- package/src/vite/rango.ts +61 -36
- package/src/vite/router-discovery.ts +173 -100
- package/src/vite/utils/prerender-utils.ts +81 -0
- package/src/vite/utils/shared-utils.ts +19 -9
- package/skills/testing/SKILL.md +0 -226
- package/src/browser/lru-cache.ts +0 -61
- package/src/browser/react/prefetch.ts +0 -27
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/route-definition/route-function.ts +0 -119
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- /package/{CLAUDE.md → AGENTS.md} +0 -0
package/src/router/metrics.ts
CHANGED
|
@@ -4,24 +4,212 @@
|
|
|
4
4
|
* Performance metrics collection and reporting for RSC Router.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { MetricsStore } from "../server/context";
|
|
7
|
+
import type { MetricsStore, PerformanceMetric } from "../server/context";
|
|
8
|
+
|
|
9
|
+
const BASE_INDENT = 2;
|
|
10
|
+
const DEPTH_INDENT = 2;
|
|
11
|
+
const TIMELINE_WIDTH = 40;
|
|
12
|
+
|
|
13
|
+
function formatMs(value: number): string {
|
|
14
|
+
return `${value.toFixed(2)}ms`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function sortMetrics(metrics: PerformanceMetric[]): PerformanceMetric[] {
|
|
18
|
+
return [...metrics].sort((a, b) => a.startTime - b.startTime);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface Span {
|
|
22
|
+
startTime: number;
|
|
23
|
+
duration: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function renderTimeline(spans: Span[], total: number): string {
|
|
27
|
+
if (TIMELINE_WIDTH <= 0) {
|
|
28
|
+
return "||";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const cells = Array(TIMELINE_WIDTH).fill(".");
|
|
32
|
+
|
|
33
|
+
if (!(total > 0)) {
|
|
34
|
+
cells[0] = "#";
|
|
35
|
+
return `|${cells.join("")}|`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const span of spans) {
|
|
39
|
+
const start = Math.max(0, span.startTime);
|
|
40
|
+
const end = Math.max(start, span.startTime + span.duration);
|
|
41
|
+
const startColumn = Math.min(
|
|
42
|
+
TIMELINE_WIDTH - 1,
|
|
43
|
+
Math.floor((start / total) * TIMELINE_WIDTH),
|
|
44
|
+
);
|
|
45
|
+
const endColumn = Math.max(
|
|
46
|
+
startColumn + 1,
|
|
47
|
+
Math.min(
|
|
48
|
+
TIMELINE_WIDTH,
|
|
49
|
+
Math.ceil((Math.min(total, end) / total) * TIMELINE_WIDTH),
|
|
50
|
+
),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
cells.fill("#", startColumn, endColumn);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return `|${cells.join("")}|`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function createTimelineAxis(total: number): string {
|
|
60
|
+
const totalLabel = formatMs(total);
|
|
61
|
+
return `0ms${" ".repeat(
|
|
62
|
+
Math.max(1, TIMELINE_WIDTH - "0ms".length - totalLabel.length),
|
|
63
|
+
)}${totalLabel}`;
|
|
64
|
+
}
|
|
8
65
|
|
|
9
66
|
/**
|
|
10
|
-
* Create a metrics store for the request if debugPerformance is enabled
|
|
67
|
+
* Create a metrics store for the request if debugPerformance is enabled.
|
|
68
|
+
* An optional `requestStart` timestamp can anchor the store to an earlier
|
|
69
|
+
* point (e.g. handler start) so that handler:total has startTime=0.
|
|
11
70
|
*/
|
|
12
71
|
export function createMetricsStore(
|
|
13
72
|
debugPerformance: boolean,
|
|
73
|
+
requestStart?: number,
|
|
14
74
|
): MetricsStore | undefined {
|
|
15
75
|
if (!debugPerformance) return undefined;
|
|
16
76
|
return {
|
|
17
77
|
enabled: true,
|
|
18
|
-
requestStart: performance.now(),
|
|
78
|
+
requestStart: requestStart ?? performance.now(),
|
|
19
79
|
metrics: [],
|
|
20
80
|
};
|
|
21
81
|
}
|
|
22
82
|
|
|
23
83
|
/**
|
|
24
|
-
*
|
|
84
|
+
* Append a metric to the request store using an absolute start timestamp.
|
|
85
|
+
*/
|
|
86
|
+
export function appendMetric(
|
|
87
|
+
metricsStore: MetricsStore | undefined,
|
|
88
|
+
label: string,
|
|
89
|
+
start: number,
|
|
90
|
+
duration: number,
|
|
91
|
+
depth?: number,
|
|
92
|
+
): void {
|
|
93
|
+
if (!metricsStore) return;
|
|
94
|
+
metricsStore.metrics.push({
|
|
95
|
+
label,
|
|
96
|
+
duration,
|
|
97
|
+
startTime: start - metricsStore.requestStart,
|
|
98
|
+
depth,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Log the current request metrics and return the corresponding Server-Timing value.
|
|
104
|
+
*/
|
|
105
|
+
export function buildMetricsTiming(
|
|
106
|
+
method: string,
|
|
107
|
+
pathname: string,
|
|
108
|
+
metricsStore: MetricsStore | undefined,
|
|
109
|
+
): string | undefined {
|
|
110
|
+
if (!metricsStore) return undefined;
|
|
111
|
+
logMetrics(method, pathname, metricsStore);
|
|
112
|
+
return generateServerTiming(metricsStore) || undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Display row produced by merging :pre/:post metric pairs. */
|
|
116
|
+
interface DisplayRow {
|
|
117
|
+
label: string;
|
|
118
|
+
startTime: number;
|
|
119
|
+
duration: number;
|
|
120
|
+
depth: number | undefined;
|
|
121
|
+
spans: Span[];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Build display rows from sorted metrics, merging :pre/:post pairs into
|
|
126
|
+
* a single row with disjoint timeline segments.
|
|
127
|
+
*/
|
|
128
|
+
function buildDisplayRows(sorted: PerformanceMetric[]): DisplayRow[] {
|
|
129
|
+
// Index :pre and :post metrics by their base label
|
|
130
|
+
const preMap = new Map<string, PerformanceMetric>();
|
|
131
|
+
const postMap = new Map<string, PerformanceMetric>();
|
|
132
|
+
const consumed = new Set<PerformanceMetric>();
|
|
133
|
+
|
|
134
|
+
for (const m of sorted) {
|
|
135
|
+
if (m.label.endsWith(":pre")) {
|
|
136
|
+
preMap.set(m.label.slice(0, -4), m);
|
|
137
|
+
} else if (m.label.endsWith(":post")) {
|
|
138
|
+
postMap.set(m.label.slice(0, -5), m);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const rows: DisplayRow[] = [];
|
|
143
|
+
|
|
144
|
+
for (const m of sorted) {
|
|
145
|
+
if (consumed.has(m)) continue;
|
|
146
|
+
|
|
147
|
+
if (m.label.endsWith(":pre")) {
|
|
148
|
+
const base = m.label.slice(0, -4);
|
|
149
|
+
const post = postMap.get(base);
|
|
150
|
+
if (post) {
|
|
151
|
+
// Merge into a single row with two disjoint spans
|
|
152
|
+
consumed.add(m);
|
|
153
|
+
consumed.add(post);
|
|
154
|
+
rows.push({
|
|
155
|
+
label: base,
|
|
156
|
+
startTime: m.startTime,
|
|
157
|
+
duration: m.duration + post.duration,
|
|
158
|
+
depth: m.depth,
|
|
159
|
+
spans: [
|
|
160
|
+
{ startTime: m.startTime, duration: m.duration },
|
|
161
|
+
{ startTime: post.startTime, duration: post.duration },
|
|
162
|
+
],
|
|
163
|
+
});
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
// Lone :pre — display with base label
|
|
167
|
+
consumed.add(m);
|
|
168
|
+
rows.push({
|
|
169
|
+
label: base,
|
|
170
|
+
startTime: m.startTime,
|
|
171
|
+
duration: m.duration,
|
|
172
|
+
depth: m.depth,
|
|
173
|
+
spans: [{ startTime: m.startTime, duration: m.duration }],
|
|
174
|
+
});
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (m.label.endsWith(":post")) {
|
|
179
|
+
const base = m.label.slice(0, -5);
|
|
180
|
+
if (preMap.has(base)) {
|
|
181
|
+
// Already consumed as part of the pair above
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
// Lone :post — display with base label
|
|
185
|
+
consumed.add(m);
|
|
186
|
+
rows.push({
|
|
187
|
+
label: base,
|
|
188
|
+
startTime: m.startTime,
|
|
189
|
+
duration: m.duration,
|
|
190
|
+
depth: m.depth,
|
|
191
|
+
spans: [{ startTime: m.startTime, duration: m.duration }],
|
|
192
|
+
});
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Regular metric
|
|
197
|
+
rows.push({
|
|
198
|
+
label: m.label,
|
|
199
|
+
startTime: m.startTime,
|
|
200
|
+
duration: m.duration,
|
|
201
|
+
depth: m.depth,
|
|
202
|
+
spans: [{ startTime: m.startTime, duration: m.duration }],
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return rows;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Log metrics to console in a formatted way.
|
|
211
|
+
* Uses a shared-axis timeline so overlapping work stays visible.
|
|
212
|
+
* Merges :pre/:post pairs onto one row with disjoint timeline segments.
|
|
25
213
|
*/
|
|
26
214
|
export function logMetrics(
|
|
27
215
|
method: string,
|
|
@@ -30,32 +218,64 @@ export function logMetrics(
|
|
|
30
218
|
): void {
|
|
31
219
|
const total = performance.now() - metricsStore.requestStart;
|
|
32
220
|
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
221
|
+
const sorted = sortMetrics(metricsStore.metrics);
|
|
222
|
+
const displayRows = buildDisplayRows(sorted);
|
|
223
|
+
|
|
224
|
+
const labels = displayRows.map(
|
|
225
|
+
(r) =>
|
|
226
|
+
`${" ".repeat(BASE_INDENT + (r.depth ?? 0) * DEPTH_INDENT)}${r.label}`,
|
|
227
|
+
);
|
|
228
|
+
const startValues = displayRows.map((r) => formatMs(r.startTime));
|
|
229
|
+
const durationValues = displayRows.map((r) => formatMs(r.duration));
|
|
230
|
+
const startWidth = Math.max(
|
|
231
|
+
"start".length,
|
|
232
|
+
...startValues.map((v) => v.length),
|
|
233
|
+
);
|
|
234
|
+
const durationWidth = Math.max(
|
|
235
|
+
"dur".length,
|
|
236
|
+
...durationValues.map((v) => v.length),
|
|
237
|
+
);
|
|
238
|
+
const spanWidth = Math.max(
|
|
239
|
+
"span".length,
|
|
240
|
+
...labels.map((label) => label.length),
|
|
241
|
+
22,
|
|
242
|
+
);
|
|
243
|
+
const timelinePadding = " ".repeat(
|
|
244
|
+
startWidth + 2 + durationWidth + 2 + spanWidth + 2,
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
console.log(`[RSC Perf] ${method} ${pathname} (${total.toFixed(2)}ms)`);
|
|
248
|
+
console.log(
|
|
249
|
+
`${"start".padStart(startWidth)} ${"dur".padStart(durationWidth)} ${"span".padEnd(spanWidth)} timeline`,
|
|
37
250
|
);
|
|
251
|
+
console.log(`${timelinePadding}${createTimelineAxis(total)}`);
|
|
38
252
|
|
|
39
|
-
|
|
253
|
+
for (let index = 0; index < displayRows.length; index++) {
|
|
254
|
+
const row = displayRows[index];
|
|
255
|
+
const label = labels[index].padEnd(spanWidth);
|
|
256
|
+
const start = formatMs(row.startTime).padStart(startWidth);
|
|
257
|
+
const duration = formatMs(row.duration).padStart(durationWidth);
|
|
40
258
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
259
|
+
console.log(
|
|
260
|
+
`${start} ${duration} ${label} ${renderTimeline(row.spans, total)}`,
|
|
261
|
+
);
|
|
44
262
|
}
|
|
45
263
|
}
|
|
46
264
|
|
|
47
265
|
/**
|
|
48
266
|
* Generate Server-Timing header value from metrics
|
|
49
267
|
* Format: metric-name;dur=X.XX
|
|
268
|
+
* Depth is encoded as a "d{N}-" prefix for nested metrics.
|
|
50
269
|
*/
|
|
51
270
|
export function generateServerTiming(metricsStore: MetricsStore): string {
|
|
52
271
|
return metricsStore.metrics
|
|
53
272
|
.map((m) => {
|
|
54
273
|
// Convert label to valid Server-Timing name (alphanumeric and hyphens)
|
|
55
|
-
const
|
|
274
|
+
const base = m.label
|
|
56
275
|
.replace(/:/g, "-")
|
|
57
276
|
.replace(/[^a-zA-Z0-9-]/g, "")
|
|
58
277
|
.toLowerCase();
|
|
278
|
+
const name = m.depth ? `d${m.depth}-${base}` : base;
|
|
59
279
|
return `${name};dur=${m.duration.toFixed(2)}`;
|
|
60
280
|
})
|
|
61
281
|
.join(", ");
|
|
@@ -6,7 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { ContextVar } from "../context-var.js";
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
DefaultReverseRouteMap,
|
|
11
|
+
DefaultRouteName,
|
|
12
|
+
DefaultVars,
|
|
13
|
+
} from "../types/global-namespace.js";
|
|
14
|
+
import type { ScopedReverseFunction } from "../reverse.js";
|
|
10
15
|
|
|
11
16
|
/**
|
|
12
17
|
* Get variable function type
|
|
@@ -66,41 +71,13 @@ export interface MiddlewareContext<
|
|
|
66
71
|
params: TParams;
|
|
67
72
|
|
|
68
73
|
/**
|
|
69
|
-
* Response
|
|
74
|
+
* Response stub (read-only). Before `next()`, returns the shared response stub
|
|
75
|
+
* where headers and cookies accumulate. After `next()`, returns the downstream response.
|
|
70
76
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* @example
|
|
75
|
-
* ```typescript
|
|
76
|
-
* middleware(async (ctx, next) => {
|
|
77
|
-
* // Set headers BEFORE next() - will be merged into final response
|
|
78
|
-
* ctx.res.headers.set('X-Request-Id', generateId());
|
|
79
|
-
*
|
|
80
|
-
* await next();
|
|
81
|
-
*
|
|
82
|
-
* // Set headers AFTER next() - applied directly
|
|
83
|
-
* ctx.res.headers.set('X-Custom', 'value');
|
|
84
|
-
* // No return needed!
|
|
85
|
-
* });
|
|
86
|
-
* ```
|
|
77
|
+
* Use `ctx.header()` to set response headers, or `cookies()` for cookie mutations.
|
|
78
|
+
* To replace the response entirely, return a new `Response` from the middleware.
|
|
87
79
|
*/
|
|
88
|
-
res: Response;
|
|
89
|
-
|
|
90
|
-
/** Get a cookie value */
|
|
91
|
-
cookie(name: string): string | undefined;
|
|
92
|
-
|
|
93
|
-
/** Get all cookies as object */
|
|
94
|
-
cookies(): Record<string, string>;
|
|
95
|
-
|
|
96
|
-
/** Set a cookie on the response */
|
|
97
|
-
setCookie(name: string, value: string, options?: CookieOptions): void;
|
|
98
|
-
|
|
99
|
-
/** Delete a cookie */
|
|
100
|
-
deleteCookie(
|
|
101
|
-
name: string,
|
|
102
|
-
options?: Pick<CookieOptions, "domain" | "path">,
|
|
103
|
-
): void;
|
|
80
|
+
readonly res: Response;
|
|
104
81
|
|
|
105
82
|
/** Get a context variable (shared with route handlers) */
|
|
106
83
|
get: GetVariableFn;
|
|
@@ -117,15 +94,33 @@ export interface MiddlewareContext<
|
|
|
117
94
|
*/
|
|
118
95
|
header(name: string, value: string): void;
|
|
119
96
|
|
|
97
|
+
/**
|
|
98
|
+
* The matched route name, if available and the route has an explicit name.
|
|
99
|
+
* Undefined for global middleware (runs before route matching) or unnamed routes.
|
|
100
|
+
*/
|
|
101
|
+
routeName?: DefaultRouteName;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Enable performance metrics for this request.
|
|
105
|
+
* When called, granular timing breakdown is logged to console and
|
|
106
|
+
* included in the Server-Timing response header, regardless of the
|
|
107
|
+
* router-level `debugPerformance` option.
|
|
108
|
+
*
|
|
109
|
+
* Call **before** `await next()` so the metrics store exists when
|
|
110
|
+
* downstream phases (route matching, rendering, SSR) record their
|
|
111
|
+
* spans. Calling after `next()` returns still emits `handler:total`
|
|
112
|
+
* but misses all upstream metrics.
|
|
113
|
+
*/
|
|
114
|
+
debugPerformance(): void;
|
|
115
|
+
|
|
120
116
|
/**
|
|
121
117
|
* Generate URLs from route names.
|
|
122
118
|
* - `name` — global route, from the named-routes definition
|
|
123
119
|
*/
|
|
124
|
-
reverse
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
): string;
|
|
120
|
+
reverse: ScopedReverseFunction<
|
|
121
|
+
Record<string, string>,
|
|
122
|
+
DefaultReverseRouteMap
|
|
123
|
+
>;
|
|
129
124
|
}
|
|
130
125
|
|
|
131
126
|
/**
|