@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.
Files changed (225) hide show
  1. package/README.md +294 -28
  2. package/dist/bin/rango.js +355 -47
  3. package/dist/vite/index.js +1658 -1239
  4. package/package.json +3 -3
  5. package/skills/cache-guide/SKILL.md +9 -5
  6. package/skills/caching/SKILL.md +4 -4
  7. package/skills/document-cache/SKILL.md +2 -2
  8. package/skills/hooks/SKILL.md +40 -29
  9. package/skills/host-router/SKILL.md +218 -0
  10. package/skills/intercept/SKILL.md +79 -0
  11. package/skills/layout/SKILL.md +62 -2
  12. package/skills/loader/SKILL.md +229 -15
  13. package/skills/middleware/SKILL.md +109 -30
  14. package/skills/parallel/SKILL.md +57 -2
  15. package/skills/prerender/SKILL.md +189 -19
  16. package/skills/rango/SKILL.md +1 -2
  17. package/skills/response-routes/SKILL.md +3 -3
  18. package/skills/route/SKILL.md +44 -3
  19. package/skills/router-setup/SKILL.md +80 -3
  20. package/skills/theme/SKILL.md +5 -4
  21. package/skills/typesafety/SKILL.md +59 -16
  22. package/skills/use-cache/SKILL.md +16 -2
  23. package/src/__internal.ts +1 -1
  24. package/src/bin/rango.ts +56 -19
  25. package/src/browser/action-coordinator.ts +97 -0
  26. package/src/browser/event-controller.ts +29 -48
  27. package/src/browser/history-state.ts +80 -0
  28. package/src/browser/intercept-utils.ts +1 -1
  29. package/src/browser/link-interceptor.ts +19 -3
  30. package/src/browser/merge-segment-loaders.ts +9 -2
  31. package/src/browser/navigation-bridge.ts +66 -443
  32. package/src/browser/navigation-client.ts +34 -62
  33. package/src/browser/navigation-store.ts +4 -33
  34. package/src/browser/navigation-transaction.ts +295 -0
  35. package/src/browser/partial-update.ts +103 -151
  36. package/src/browser/prefetch/cache.ts +67 -0
  37. package/src/browser/prefetch/fetch.ts +137 -0
  38. package/src/browser/prefetch/observer.ts +65 -0
  39. package/src/browser/prefetch/policy.ts +42 -0
  40. package/src/browser/prefetch/queue.ts +88 -0
  41. package/src/browser/rango-state.ts +112 -0
  42. package/src/browser/react/Link.tsx +154 -44
  43. package/src/browser/react/NavigationProvider.tsx +32 -0
  44. package/src/browser/react/context.ts +6 -0
  45. package/src/browser/react/filter-segment-order.ts +11 -0
  46. package/src/browser/react/index.ts +2 -6
  47. package/src/browser/react/location-state-shared.ts +29 -11
  48. package/src/browser/react/location-state.ts +6 -4
  49. package/src/browser/react/nonce-context.ts +23 -0
  50. package/src/browser/react/shallow-equal.ts +27 -0
  51. package/src/browser/react/use-action.ts +23 -45
  52. package/src/browser/react/use-client-cache.ts +5 -3
  53. package/src/browser/react/use-handle.ts +21 -64
  54. package/src/browser/react/use-navigation.ts +7 -32
  55. package/src/browser/react/use-params.ts +5 -34
  56. package/src/browser/react/use-pathname.ts +2 -3
  57. package/src/browser/react/use-router.ts +3 -6
  58. package/src/browser/react/use-search-params.ts +2 -1
  59. package/src/browser/react/use-segments.ts +75 -114
  60. package/src/browser/response-adapter.ts +73 -0
  61. package/src/browser/rsc-router.tsx +46 -22
  62. package/src/browser/scroll-restoration.ts +10 -7
  63. package/src/browser/server-action-bridge.ts +458 -405
  64. package/src/browser/types.ts +21 -35
  65. package/src/browser/validate-redirect-origin.ts +29 -0
  66. package/src/build/generate-manifest.ts +38 -13
  67. package/src/build/generate-route-types.ts +4 -0
  68. package/src/build/index.ts +1 -0
  69. package/src/build/route-trie.ts +19 -3
  70. package/src/build/route-types/codegen.ts +13 -4
  71. package/src/build/route-types/include-resolution.ts +13 -0
  72. package/src/build/route-types/per-module-writer.ts +15 -3
  73. package/src/build/route-types/router-processing.ts +170 -18
  74. package/src/build/runtime-discovery.ts +13 -1
  75. package/src/cache/background-task.ts +34 -0
  76. package/src/cache/cache-key-utils.ts +44 -0
  77. package/src/cache/cache-policy.ts +125 -0
  78. package/src/cache/cache-runtime.ts +136 -123
  79. package/src/cache/cache-scope.ts +76 -83
  80. package/src/cache/cf/cf-cache-store.ts +12 -7
  81. package/src/cache/document-cache.ts +93 -69
  82. package/src/cache/handle-capture.ts +81 -0
  83. package/src/cache/index.ts +0 -15
  84. package/src/cache/memory-segment-store.ts +43 -69
  85. package/src/cache/profile-registry.ts +43 -8
  86. package/src/cache/read-through-swr.ts +134 -0
  87. package/src/cache/segment-codec.ts +140 -117
  88. package/src/cache/taint.ts +30 -3
  89. package/src/cache/types.ts +1 -115
  90. package/src/client.rsc.tsx +0 -1
  91. package/src/client.tsx +53 -76
  92. package/src/errors.ts +6 -1
  93. package/src/handle.ts +1 -1
  94. package/src/handles/MetaTags.tsx +5 -2
  95. package/src/host/cookie-handler.ts +8 -3
  96. package/src/host/index.ts +0 -3
  97. package/src/host/router.ts +14 -1
  98. package/src/href-client.ts +3 -1
  99. package/src/index.rsc.ts +53 -10
  100. package/src/index.ts +73 -43
  101. package/src/loader.rsc.ts +12 -4
  102. package/src/loader.ts +8 -0
  103. package/src/prerender/store.ts +60 -18
  104. package/src/prerender.ts +76 -18
  105. package/src/reverse.ts +11 -7
  106. package/src/root-error-boundary.tsx +30 -26
  107. package/src/route-definition/dsl-helpers.ts +9 -6
  108. package/src/route-definition/index.ts +0 -3
  109. package/src/route-definition/redirect.ts +15 -3
  110. package/src/route-map-builder.ts +38 -2
  111. package/src/route-name.ts +53 -0
  112. package/src/route-types.ts +7 -0
  113. package/src/router/content-negotiation.ts +1 -1
  114. package/src/router/debug-manifest.ts +16 -3
  115. package/src/router/handler-context.ts +96 -17
  116. package/src/router/intercept-resolution.ts +6 -4
  117. package/src/router/lazy-includes.ts +4 -0
  118. package/src/router/loader-resolution.ts +6 -11
  119. package/src/router/logging.ts +100 -3
  120. package/src/router/manifest.ts +32 -3
  121. package/src/router/match-api.ts +62 -54
  122. package/src/router/match-context.ts +3 -0
  123. package/src/router/match-handlers.ts +185 -11
  124. package/src/router/match-middleware/background-revalidation.ts +65 -85
  125. package/src/router/match-middleware/cache-lookup.ts +78 -10
  126. package/src/router/match-middleware/cache-store.ts +2 -0
  127. package/src/router/match-pipelines.ts +8 -43
  128. package/src/router/match-result.ts +0 -9
  129. package/src/router/metrics.ts +233 -13
  130. package/src/router/middleware-types.ts +34 -39
  131. package/src/router/middleware.ts +290 -130
  132. package/src/router/pattern-matching.ts +61 -10
  133. package/src/router/prerender-match.ts +36 -6
  134. package/src/router/preview-match.ts +7 -1
  135. package/src/router/revalidation.ts +61 -2
  136. package/src/router/router-context.ts +15 -0
  137. package/src/router/router-interfaces.ts +158 -40
  138. package/src/router/router-options.ts +223 -1
  139. package/src/router/router-registry.ts +5 -2
  140. package/src/router/segment-resolution/fresh.ts +165 -242
  141. package/src/router/segment-resolution/helpers.ts +263 -0
  142. package/src/router/segment-resolution/loader-cache.ts +102 -98
  143. package/src/router/segment-resolution/revalidation.ts +394 -272
  144. package/src/router/segment-resolution/static-store.ts +2 -2
  145. package/src/router/segment-resolution.ts +1 -3
  146. package/src/router/segment-wrappers.ts +3 -0
  147. package/src/router/telemetry-otel.ts +299 -0
  148. package/src/router/telemetry.ts +300 -0
  149. package/src/router/timeout.ts +148 -0
  150. package/src/router/trie-matching.ts +20 -2
  151. package/src/router/types.ts +7 -1
  152. package/src/router.ts +203 -18
  153. package/src/rsc/handler-context.ts +13 -2
  154. package/src/rsc/handler.ts +489 -438
  155. package/src/rsc/helpers.ts +125 -5
  156. package/src/rsc/index.ts +0 -20
  157. package/src/rsc/loader-fetch.ts +84 -42
  158. package/src/rsc/manifest-init.ts +3 -2
  159. package/src/rsc/origin-guard.ts +141 -0
  160. package/src/rsc/progressive-enhancement.ts +245 -19
  161. package/src/rsc/response-route-handler.ts +347 -0
  162. package/src/rsc/rsc-rendering.ts +47 -43
  163. package/src/rsc/runtime-warnings.ts +42 -0
  164. package/src/rsc/server-action.ts +166 -66
  165. package/src/rsc/ssr-setup.ts +128 -0
  166. package/src/rsc/types.ts +20 -2
  167. package/src/search-params.ts +38 -23
  168. package/src/server/context.ts +61 -7
  169. package/src/server/cookie-store.ts +190 -0
  170. package/src/server/fetchable-loader-store.ts +11 -6
  171. package/src/server/handle-store.ts +84 -12
  172. package/src/server/loader-registry.ts +11 -46
  173. package/src/server/request-context.ts +275 -49
  174. package/src/server.ts +6 -0
  175. package/src/ssr/index.tsx +67 -28
  176. package/src/static-handler.ts +7 -0
  177. package/src/theme/ThemeProvider.tsx +6 -1
  178. package/src/theme/index.ts +4 -18
  179. package/src/theme/theme-context.ts +1 -28
  180. package/src/theme/theme-script.ts +2 -1
  181. package/src/types/cache-types.ts +6 -1
  182. package/src/types/error-types.ts +3 -0
  183. package/src/types/global-namespace.ts +22 -0
  184. package/src/types/handler-context.ts +103 -16
  185. package/src/types/index.ts +1 -1
  186. package/src/types/loader-types.ts +9 -6
  187. package/src/types/route-config.ts +17 -26
  188. package/src/types/route-entry.ts +28 -0
  189. package/src/types/segments.ts +0 -5
  190. package/src/urls/include-helper.ts +49 -8
  191. package/src/urls/index.ts +1 -0
  192. package/src/urls/path-helper-types.ts +30 -12
  193. package/src/urls/path-helper.ts +17 -2
  194. package/src/urls/pattern-types.ts +21 -1
  195. package/src/urls/response-types.ts +29 -7
  196. package/src/urls/type-extraction.ts +23 -15
  197. package/src/use-loader.tsx +27 -9
  198. package/src/vite/discovery/bundle-postprocess.ts +32 -52
  199. package/src/vite/discovery/discover-routers.ts +52 -26
  200. package/src/vite/discovery/prerender-collection.ts +58 -41
  201. package/src/vite/discovery/route-types-writer.ts +7 -7
  202. package/src/vite/discovery/state.ts +7 -7
  203. package/src/vite/discovery/virtual-module-codegen.ts +5 -2
  204. package/src/vite/index.ts +10 -51
  205. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  206. package/src/vite/plugins/client-ref-hashing.ts +3 -3
  207. package/src/vite/plugins/expose-internal-ids.ts +4 -3
  208. package/src/vite/plugins/refresh-cmd.ts +65 -0
  209. package/src/vite/plugins/use-cache-transform.ts +91 -3
  210. package/src/vite/plugins/version-plugin.ts +188 -18
  211. package/src/vite/rango.ts +61 -36
  212. package/src/vite/router-discovery.ts +173 -100
  213. package/src/vite/utils/prerender-utils.ts +81 -0
  214. package/src/vite/utils/shared-utils.ts +19 -9
  215. package/skills/testing/SKILL.md +0 -226
  216. package/src/browser/lru-cache.ts +0 -61
  217. package/src/browser/react/prefetch.ts +0 -27
  218. package/src/browser/request-controller.ts +0 -164
  219. package/src/cache/memory-store.ts +0 -253
  220. package/src/href-context.ts +0 -33
  221. package/src/route-definition/route-function.ts +0 -119
  222. package/src/router.gen.ts +0 -6
  223. package/src/static-handler.gen.ts +0 -5
  224. package/src/urls.gen.ts +0 -8
  225. /package/{CLAUDE.md → AGENTS.md} +0 -0
@@ -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
- * Log metrics to console in a formatted way
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
- // Find max label length for alignment
34
- const maxLabelLen = Math.max(
35
- ...metricsStore.metrics.map((m) => m.label.length),
36
- 20,
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
- console.log(`[RSC Perf] ${method} ${pathname} (${total.toFixed(1)}ms)`);
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
- for (const m of metricsStore.metrics) {
42
- const paddedLabel = m.label.padEnd(maxLabelLen);
43
- console.log(` ${paddedLabel} ${m.duration.toFixed(1)}ms`);
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 name = m.label
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 { DefaultVars } from "../types/global-namespace.js";
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 object - available immediately via stub, real response after `await next()`
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
- * Headers set before `next()` are merged into the final response.
72
- * Can be used to modify headers directly like Hono's `c.res`.
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
- name: string,
126
- params?: Record<string, string>,
127
- search?: Record<string, unknown>,
128
- ): string;
120
+ reverse: ScopedReverseFunction<
121
+ Record<string, string>,
122
+ DefaultReverseRouteMap
123
+ >;
129
124
  }
130
125
 
131
126
  /**