@timber-js/app 0.2.0-alpha.204 → 0.2.0-alpha.206

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 (111) hide show
  1. package/dist/_chunks/{actions-HUdJADAD.js → actions-jNrXdzgs.js} +4 -3
  2. package/dist/_chunks/{actions-HUdJADAD.js.map → actions-jNrXdzgs.js.map} +1 -1
  3. package/dist/_chunks/als-registry-C6kcfprT.js.map +1 -1
  4. package/dist/_chunks/{build-manifest-DWppEdLB.js → build-manifest-DTmSGLRz.js} +51 -2
  5. package/dist/_chunks/build-manifest-DTmSGLRz.js.map +1 -0
  6. package/dist/_chunks/{cache-api-CAPbZTga.js → cache-api-CUD9Ezaq.js} +3 -2
  7. package/dist/_chunks/{cache-api-CAPbZTga.js.map → cache-api-CUD9Ezaq.js.map} +1 -1
  8. package/dist/_chunks/{chains-C9Y7Oyno.js → chains-B4bgOl2g.js} +2 -2
  9. package/dist/_chunks/{chains-C9Y7Oyno.js.map → chains-B4bgOl2g.js.map} +1 -1
  10. package/dist/_chunks/{cli-check-BzMGuIH6.js → cli-check-BqOylm-3.js} +3 -3
  11. package/dist/_chunks/{cli-check-BzMGuIH6.js.map → cli-check-BqOylm-3.js.map} +1 -1
  12. package/dist/_chunks/{cli-schema-sync-DnXqcIIj.js → cli-schema-sync--LZKs8Zn.js} +2 -2
  13. package/dist/_chunks/{cli-schema-sync-DnXqcIIj.js.map → cli-schema-sync--LZKs8Zn.js.map} +1 -1
  14. package/dist/_chunks/{cloudflare-DxX1SU0g.js → cloudflare-BFb__LYG.js} +3 -3
  15. package/dist/_chunks/{cloudflare-DxX1SU0g.js.map → cloudflare-BFb__LYG.js.map} +1 -1
  16. package/dist/_chunks/{convention-lint-DHOFvX5s.js → convention-lint-BnO5TyHD.js} +2 -2
  17. package/dist/_chunks/{convention-lint-DHOFvX5s.js.map → convention-lint-BnO5TyHD.js.map} +1 -1
  18. package/dist/_chunks/{dev-server-BV01iyUs.js → dev-server-BzBxgYAV.js} +9 -196
  19. package/dist/_chunks/dev-server-BzBxgYAV.js.map +1 -0
  20. package/dist/_chunks/{error-boundary-BQKxl6EX.js → error-boundary-BndF-3Td.js} +2 -2
  21. package/dist/_chunks/{error-boundary-BQKxl6EX.js.map → error-boundary-BndF-3Td.js.map} +1 -1
  22. package/dist/_chunks/{graph-cache-Cv3njEH8.js → graph-cache-CP4GEmf9.js} +2 -2
  23. package/dist/_chunks/{graph-cache-Cv3njEH8.js.map → graph-cache-CP4GEmf9.js.map} +1 -1
  24. package/dist/_chunks/{live-graph-DCLQzV6m.js → live-graph-Bm5WTvcr.js} +3 -3
  25. package/dist/_chunks/{live-graph-DCLQzV6m.js.map → live-graph-Bm5WTvcr.js.map} +1 -1
  26. package/dist/_chunks/logger-k2DQ4EUf.js +506 -0
  27. package/dist/_chunks/logger-k2DQ4EUf.js.map +1 -0
  28. package/dist/_chunks/{poison-scan-CX_NaNDi.js → poison-scan-DG5eI2_z.js} +2 -2
  29. package/dist/_chunks/{poison-scan-CX_NaNDi.js.map → poison-scan-DG5eI2_z.js.map} +1 -1
  30. package/dist/_chunks/{logger-DqJ2VoAY.js → primitives-DLAnvsrk.js} +17 -518
  31. package/dist/_chunks/primitives-DLAnvsrk.js.map +1 -0
  32. package/dist/_chunks/{scanner-B_tnqFcF.js → scanner-BQ5sPIJ7.js} +4 -4
  33. package/dist/_chunks/{scanner-B_tnqFcF.js.map → scanner-BQ5sPIJ7.js.map} +1 -1
  34. package/dist/_chunks/{tree-match-CdbvYTBz.js → tree-match-6yihorhk.js} +49 -2
  35. package/dist/_chunks/tree-match-6yihorhk.js.map +1 -0
  36. package/dist/_chunks/{walkers-Cm3PC5JT.js → walkers-CN2N_bA-.js} +2 -2
  37. package/dist/_chunks/{walkers-Cm3PC5JT.js.map → walkers-CN2N_bA-.js.map} +1 -1
  38. package/dist/adapters/cloudflare-dev.js +1 -1
  39. package/dist/adapters/cloudflare-kv-cache.js +1 -1
  40. package/dist/adapters/cloudflare.js +1 -1
  41. package/dist/analyze/crawl-entry.js +2 -2
  42. package/dist/analyze/graph-command.js +3 -3
  43. package/dist/cache/index.js +1 -1
  44. package/dist/cli.js +3 -3
  45. package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -1
  46. package/dist/client/error-boundary.js +1 -1
  47. package/dist/client/internal.js +1 -1
  48. package/dist/dev-tools/index.d.ts +1 -1
  49. package/dist/dev-tools/index.d.ts.map +1 -1
  50. package/dist/dev-tools/overlay.d.ts +1 -21
  51. package/dist/dev-tools/overlay.d.ts.map +1 -1
  52. package/dist/index.js +6 -7
  53. package/dist/index.js.map +1 -1
  54. package/dist/plugins/dev-server.d.ts.map +1 -1
  55. package/dist/routing/index.js +2 -2
  56. package/dist/server/als-registry.d.ts +0 -7
  57. package/dist/server/als-registry.d.ts.map +1 -1
  58. package/dist/server/deny-renderer.d.ts +2 -6
  59. package/dist/server/deny-renderer.d.ts.map +1 -1
  60. package/dist/server/index.js +3 -2
  61. package/dist/server/index.js.map +1 -1
  62. package/dist/server/internal.js +180 -7
  63. package/dist/server/internal.js.map +1 -1
  64. package/dist/server/pipeline.d.ts +1 -13
  65. package/dist/server/pipeline.d.ts.map +1 -1
  66. package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -1
  67. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  68. package/dist/server/rsc-entry/helpers.d.ts +0 -38
  69. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  70. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  71. package/dist/server/rsc-entry/render-route.d.ts +0 -1
  72. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  73. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  74. package/dist/server/rsc-entry/rsc-stream.d.ts +1 -7
  75. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  76. package/dist/server/rsc-entry/ssr-renderer.d.ts +0 -1
  77. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  78. package/dist/server/slot-resolver.d.ts.map +1 -1
  79. package/dist/server/slot-subtree-contain.d.ts +1 -1
  80. package/dist/server/slot-subtree-contain.d.ts.map +1 -1
  81. package/dist/server/tree-match.d.ts +10 -0
  82. package/dist/server/tree-match.d.ts.map +1 -1
  83. package/package.json +1 -1
  84. package/src/client/browser-entry/rsc-stream.ts +1 -82
  85. package/src/dev-tools/index.ts +0 -2
  86. package/src/dev-tools/overlay.ts +1 -65
  87. package/src/plugins/dev-server.ts +4 -45
  88. package/src/server/als-registry.ts +0 -8
  89. package/src/server/deny-renderer.ts +0 -7
  90. package/src/server/pipeline.ts +1 -14
  91. package/src/server/route-matcher.ts +73 -20
  92. package/src/server/rsc-entry/deny-fallback.ts +1 -10
  93. package/src/server/rsc-entry/error-renderer.ts +1 -11
  94. package/src/server/rsc-entry/helpers.ts +0 -92
  95. package/src/server/rsc-entry/index.ts +1 -14
  96. package/src/server/rsc-entry/render-route.ts +2 -23
  97. package/src/server/rsc-entry/rsc-payload.ts +1 -2
  98. package/src/server/rsc-entry/rsc-stream.ts +3 -29
  99. package/src/server/rsc-entry/ssr-renderer.ts +2 -15
  100. package/src/server/slot-resolver.ts +7 -29
  101. package/src/server/slot-subtree-contain.ts +22 -14
  102. package/src/server/tree-match.ts +250 -18
  103. package/dist/_chunks/build-manifest-DWppEdLB.js.map +0 -1
  104. package/dist/_chunks/csp-nonce-hOGniaG4.js +0 -227
  105. package/dist/_chunks/csp-nonce-hOGniaG4.js.map +0 -1
  106. package/dist/_chunks/dev-server-BV01iyUs.js.map +0 -1
  107. package/dist/_chunks/logger-DqJ2VoAY.js.map +0 -1
  108. package/dist/_chunks/tree-match-CdbvYTBz.js.map +0 -1
  109. package/dist/dev-tools/debug-channel.d.ts +0 -55
  110. package/dist/dev-tools/debug-channel.d.ts.map +0 -1
  111. package/src/dev-tools/debug-channel.ts +0 -151
@@ -18,7 +18,7 @@ import {
18
18
  resolveServePathForFile,
19
19
  type MetadataRouteType,
20
20
  } from './metadata-routes.ts';
21
- import { matchUrlParts } from './tree-match.ts';
21
+ import { matchUrlParts, matchUrlPartsAll } from './tree-match.ts';
22
22
 
23
23
  // ─── Manifest Types ───────────────────────────────────────────────────────
24
24
  //
@@ -190,30 +190,44 @@ function matchMetadataPathname(
190
190
 
191
191
  let segment: ManifestSegmentNode;
192
192
  let params: Record<string, string | string[]>;
193
+ let file: ManifestFile | null = null;
193
194
 
194
- if (prefixParts.length === 0) {
195
- segment = root;
196
- params = {};
197
- } else {
198
- const result = matchUrlParts(root, prefixParts, { requireTerminal: false });
199
- if (!result) return null;
200
- segment = result.chain[result.chain.length - 1];
201
- params = result.params;
202
- }
195
+ // Find the metadata file owner. For prefix matches, iterate all matching
196
+ // segments in priority order — when competing bare-dynamic groups match
197
+ // the same prefix, the first may not own the file. For root-level matches,
198
+ // search all group children since findMetadataRouteInSegment may return
199
+ // a file with the wrong extension when multiple groups own the same
200
+ // convention with different static types.
201
+ segment = root;
202
+ params = {};
203
+ const candidates = [...metadataOwnerCandidates(root, prefixParts)];
203
204
 
204
- // Non-nestable routes (robots, manifest, favicon) only serve from root
205
- if (!convention.nestable && segment !== root) return null;
205
+ // Two passes: direct owners first (node itself + groups), then
206
+ // zero-segment optional-catch-all descent. A direct owner across any
207
+ // candidate must beat a zero-segment catch-all on an earlier candidate.
208
+ for (const allowCatchAll of [false, true]) {
209
+ for (const candidate of candidates) {
210
+ if (!convention.nestable && candidate.segment !== root) continue;
211
+ const found = findMetadataRouteInSegment(
212
+ candidate.segment,
213
+ baseName,
214
+ allowCatchAll && convention.nestable
215
+ );
216
+ if (!found) continue;
217
+ if (convention.serveExtension) {
218
+ const resolvedServePath = resolveServePathForFile(baseName, found.filePath);
219
+ if (lastPart !== resolvedServePath) continue;
220
+ }
221
+ segment = candidate.segment;
222
+ params = candidate.params;
223
+ file = found;
224
+ break;
225
+ }
226
+ if (file) break;
227
+ }
206
228
 
207
- const file = findMetadataRouteInSegment(segment, baseName, convention.nestable);
208
229
  if (!file) return null;
209
230
 
210
- // Validate the requested URL extension matches the file's resolved serve path.
211
- // Prevents /icon.svg matching when only icon.png exists.
212
- if (convention.serveExtension) {
213
- const resolvedServePath = resolveServePathForFile(baseName, file.filePath);
214
- if (lastPart !== resolvedServePath) return null;
215
- }
216
-
217
231
  const ext = file.filePath.slice(file.filePath.lastIndexOf('.') + 1);
218
232
  const isStatic = isStaticMetadataExtension(baseName, ext);
219
233
  const contentType = isStatic
@@ -265,3 +279,42 @@ function findMetadataRouteInSegment(
265
279
  }
266
280
  return null;
267
281
  }
282
+
283
+ /**
284
+ * Yield candidate owner segments for a metadata route.
285
+ *
286
+ * For root-level metadata (empty prefix), yields the root and then each
287
+ * transparent group child — so extension validation can skip a group that
288
+ * owns the wrong static type and find the right one. For prefix matches,
289
+ * delegates to `matchUrlPartsAll` which handles all segment kinds.
290
+ */
291
+ function* metadataOwnerCandidates(
292
+ root: ManifestSegmentNode,
293
+ prefixParts: string[]
294
+ ): Generator<{ segment: ManifestSegmentNode; params: Record<string, string | string[]> }> {
295
+ if (prefixParts.length === 0) {
296
+ yield { segment: root, params: {} };
297
+ // Also yield each group child individually so the caller can validate
298
+ // extensions per-group rather than taking the first file found.
299
+ for (const child of root.children) {
300
+ if (child.segmentType === 'group') {
301
+ yield* groupDescendants(child);
302
+ }
303
+ }
304
+ } else {
305
+ for (const result of matchUrlPartsAll(root, prefixParts, { requireTerminal: false })) {
306
+ yield { segment: result.chain[result.chain.length - 1], params: result.params };
307
+ }
308
+ }
309
+ }
310
+
311
+ function* groupDescendants(
312
+ node: ManifestSegmentNode
313
+ ): Generator<{ segment: ManifestSegmentNode; params: Record<string, string | string[]> }> {
314
+ yield { segment: node, params: {} };
315
+ for (const child of node.children) {
316
+ if (child.segmentType === 'group') {
317
+ yield* groupDescendants(child);
318
+ }
319
+ }
320
+ }
@@ -17,7 +17,6 @@ import { logRenderError } from '../logger.ts';
17
17
  import type { PipelineConfig, RouteMatch } from '../pipeline.ts';
18
18
  import type { ManifestSegmentNode } from '../route-matcher.ts';
19
19
  import { loadModule } from '../safe-load.ts';
20
- import { createDebugChannelSink } from './helpers.ts';
21
20
  import { isRscRequest } from '../../shared/rsc-media-type.ts';
22
21
  import { callSsr } from './ssr-bridge.ts';
23
22
 
@@ -69,14 +68,7 @@ export function createDenyFallbackRenderer(options: {
69
68
  // request is an RSC payload request — mirroring the normal render path.
70
69
  // See design/19-client-navigation.md §"RSC Payload Handling" and TIM-823.
71
70
  if (isRscRequest(req)) {
72
- return renderDenyPageAsRsc(
73
- deny,
74
- chain,
75
- layoutComponents,
76
- req,
77
- responseHeaders,
78
- createDebugChannelSink
79
- );
71
+ return renderDenyPageAsRsc(deny, chain, layoutComponents, req, responseHeaders);
80
72
  }
81
73
  return renderDenyPage(
82
74
  deny,
@@ -86,7 +78,6 @@ export function createDenyFallbackRenderer(options: {
86
78
  match,
87
79
  responseHeaders,
88
80
  clientBootstrap,
89
- createDebugChannelSink,
90
81
  callSsr
91
82
  );
92
83
  };
@@ -23,7 +23,6 @@ import { getCspNonce } from '../csp-nonce.ts';
23
23
  import { renderDenyPage, renderDenyPageAsRsc } from '../deny-renderer.ts';
24
24
  import type { DefaultPageRenderer, LayoutEntry } from '../deny-renderer.ts';
25
25
  import type { NavContext } from '../ssr-bridge-types.ts';
26
- import { createDebugChannelSink } from './helpers.ts';
27
26
  import { isRscRequest } from '../../shared/rsc-media-type.ts';
28
27
  import { withPublishedParams } from '../publish-params.ts';
29
28
  import { getCookiesForSsr } from '../cookie-context.ts';
@@ -291,7 +290,6 @@ async function renderErrorPageViaRsc(
291
290
  onError(err: unknown) {
292
291
  logRenderError({ method: req.method, path: url.pathname, error: err });
293
292
  },
294
- debugChannel: createDebugChannelSink(),
295
293
  });
296
294
 
297
295
  const [ssrStream, inlineStream] = teeWithErrorPropagation(rscStream);
@@ -402,14 +400,7 @@ export async function renderNoMatchPage(
402
400
  const match: RouteMatch = { segments, segmentParams: {}, middlewareChain: [] };
403
401
 
404
402
  if (isRscRequest(req)) {
405
- return renderDenyPageAsRsc(
406
- deny,
407
- segments,
408
- layoutComponents,
409
- req,
410
- responseHeaders,
411
- createDebugChannelSink
412
- );
403
+ return renderDenyPageAsRsc(deny, segments, layoutComponents, req, responseHeaders);
413
404
  }
414
405
 
415
406
  return renderDenyPage(
@@ -420,7 +411,6 @@ export async function renderNoMatchPage(
420
411
  match,
421
412
  responseHeaders,
422
413
  clientBootstrap,
423
- createDebugChannelSink,
424
414
  callSsr,
425
415
  defaultPage
426
416
  );
@@ -5,104 +5,12 @@
5
5
  */
6
6
 
7
7
  import type { ManifestSegmentNode } from '../route-matcher.ts';
8
- import { swallow } from '../logger.ts';
9
8
  import { computeSegmentKeys, computeSegmentTreePaths } from '../../routing/segment-keys.ts';
10
9
  import type { SegmentInfo } from '../../shared/segment-info.ts';
11
10
  import type { SlotSkipEntry } from '../slot-resolver.ts';
12
11
  import { isClientReference } from '../utils/element-type.ts';
13
12
  import { isStaticRequestDependent } from '../request-dep.ts';
14
13
 
15
- /**
16
- * No-op — returns undefined so no debugChannel is passed to Flight.
17
- * Debug rows ride the main stream inline (React's default behavior).
18
- *
19
- * Production Flight emits no debug rows. In dev, inline debug rows
20
- * are handled by the Flight client for owner stack resolution.
21
- *
22
- * A writable-only debugChannel triggers a react-server-dom-webpack
23
- * >=19.3.0 Flight client regression (owner/stack refs parsed after
24
- * stream close produce "Connection closed" errors).
25
- */
26
- export function createDebugChannelSink(): undefined {
27
- return undefined;
28
- }
29
-
30
- // ─── Debug Channel Collector (dev mode only) ────────────────────────────
31
-
32
- /**
33
- * Parsed component debug info extracted from the Flight debug channel.
34
- *
35
- * Contains only component names, environment labels, and stack frames —
36
- * never source code or props. See design/13-security.md §"Server source
37
- * never reaches the client".
38
- */
39
- export interface DebugComponentEntry {
40
- name: string;
41
- env: string | null;
42
- key: string | null;
43
- stack: unknown[] | null;
44
- }
45
-
46
- /**
47
- * Parse React Flight debug rows into component entries.
48
- *
49
- * The Flight debug channel writes rows in `hexId:json\n` format. Each row
50
- * with a JSON object containing a `name` field is a component debug info
51
- * entry. Rows without `name` (timing rows, reference rows like `D"$id"`)
52
- * are skipped.
53
- *
54
- * Security: `props` are explicitly stripped from parsed entries — they may
55
- * contain rendered output or user data. Only `name`, `env`, `key`, and
56
- * `stack` are retained.
57
- */
58
- export function parseDebugRows(text: string): DebugComponentEntry[] {
59
- if (!text) return [];
60
-
61
- const entries: DebugComponentEntry[] = [];
62
- const lines = text.split('\n');
63
-
64
- for (const line of lines) {
65
- if (!line) continue;
66
-
67
- // Flight row format: hexId:payload
68
- const colonIdx = line.indexOf(':');
69
- if (colonIdx === -1) continue;
70
-
71
- let payload = line.slice(colonIdx + 1);
72
-
73
- // React Flight debug rows may have a type-tag prefix before the JSON
74
- // object (e.g., "D{...}" for debug info rows). Strip the single-char
75
- // tag so the JSON parser sees the object. Non-JSON payloads like
76
- // D"$a" reference rows are caught by the JSON.parse try/catch below.
77
- // See TIM-556.
78
- if (payload.length > 0 && payload[0] !== '{' && payload[0] !== '[' && payload[0] !== '"') {
79
- payload = payload.slice(1);
80
- }
81
-
82
- // Skip payloads that still aren't JSON objects after tag stripping
83
- if (!payload.startsWith('{')) continue;
84
-
85
- try {
86
- const parsed = JSON.parse(payload);
87
- if (typeof parsed !== 'object' || parsed === null) continue;
88
- if (typeof parsed.name !== 'string') continue;
89
-
90
- // Strip props — may contain source-derived data or user data.
91
- // Only retain: name, env, key, stack.
92
- entries.push({
93
- name: parsed.name,
94
- env: parsed.env ?? null,
95
- key: parsed.key ?? null,
96
- stack: Array.isArray(parsed.stack) ? parsed.stack : null,
97
- });
98
- } catch (e) {
99
- swallow(e, 'malformed JSON in RSC client reference row');
100
- }
101
- }
102
-
103
- return entries;
104
- }
105
-
106
14
  /**
107
15
  * Build segment metadata for the X-Timber-Segments response header.
108
16
  * Describes the rendered segment chain with request-dependency status,
@@ -60,7 +60,6 @@ import { renderNoMatchPage } from './error-renderer.ts';
60
60
  import { renderRoute } from './render-route.ts';
61
61
  import { isDebug, isDevMode, setDebugFromConfig } from '../debug.ts';
62
62
  import { setSourceMapCallback } from '../dev-source-map.ts';
63
- import { requestContextAls } from '../als-registry.ts';
64
63
  import { capturePrebuiltPayloads } from '../prebuilt-builder.ts';
65
64
  import { setPrebuiltPayloadSource } from '../prebuilt/payload-source.ts';
66
65
  import { setRscFunctions, setComponentRefreshTimeout } from '../prebuilt-runtime.ts';
@@ -97,10 +96,6 @@ export function setDevSourceMapHandler(handler: (error: Error) => void): void {
97
96
  setSourceMapCallback(handler);
98
97
  }
99
98
 
100
- // Dev-only: debug components getter is stored per-request in ALS
101
- // (RequestContextStore.debugComponentsGetter) to avoid cross-request
102
- // race conditions. See TIM-557.
103
-
104
99
  /**
105
100
  * Create the RSC request handler from the route manifest.
106
101
  *
@@ -299,7 +294,6 @@ async function createRequestHandler(manifest: typeof routeManifest, runtimeConfi
299
294
  rootSegment: manifest.root,
300
295
  buildManifest: typedBuildManifest,
301
296
  globalError: manifest.globalError,
302
- devDebugRegistry: pipelineConfig.devDebugRegistry,
303
297
  },
304
298
  interception
305
299
  );
@@ -351,14 +345,7 @@ async function createRequestHandler(manifest: typeof routeManifest, runtimeConfi
351
345
  onPipelineError: isDev
352
346
  ? (error: Error, phase: string) => {
353
347
  if (pipelineConfig.devPipelineErrorHandler) {
354
- // For render-phase errors, include RSC debug component data
355
- // from the Flight debug channel (if available from the current request).
356
- const store = requestContextAls.getStore();
357
- const debugComponents =
358
- phase === 'render' && store?.debugComponentsGetter
359
- ? store.debugComponentsGetter()
360
- : undefined;
361
- pipelineConfig.devPipelineErrorHandler(error, phase, debugComponents);
348
+ pipelineConfig.devPipelineErrorHandler(error, phase);
362
349
  }
363
350
  }
364
351
  : undefined,
@@ -35,13 +35,12 @@ import { buildRouteElement, NoPageComponentError } from '../route-element-builde
35
35
  import { ParamCoercionError } from '../param-coercion.ts';
36
36
  import type { ManifestSegmentNode } from '../route-matcher.ts';
37
37
  import { recordTiming } from '../server-timing.ts';
38
- import { requestContextAls } from '../als-registry.ts';
39
38
  import { parseClientStateTree } from '../state-tree-diff.ts';
40
39
 
41
40
  import { getFirstDeniedSegmentIndex } from '../deny-boundary.ts';
42
41
  import { handleApiRoute } from './api-handler.ts';
43
42
  import { renderErrorPage, renderNoMatchPage } from './error-renderer.ts';
44
- import { buildRedirectResponse, createDebugChannelSink } from './helpers.ts';
43
+ import { buildRedirectResponse } from './helpers.ts';
45
44
  import { isRscRequest } from '../../shared/rsc-media-type.ts';
46
45
  import { buildRscPayloadResponse } from './rsc-payload.ts';
47
46
  import { renderRscStream } from './rsc-stream.ts';
@@ -60,7 +59,6 @@ export interface RenderRouteDeps {
60
59
  rootSegment: ManifestSegmentNode;
61
60
  buildManifest: BuildManifest;
62
61
  globalError?: { load: () => Promise<unknown>; filePath: string };
63
- devDebugRegistry?: import('../../dev-tools/debug-channel.ts').DebugChannelRegistry;
64
62
  }
65
63
 
66
64
  /**
@@ -199,24 +197,8 @@ export async function renderRoute(
199
197
 
200
198
  // Render to RSC Flight stream with signal tracking.
201
199
  const _rscStart = performance.now();
202
- // Only wire the HMR debug transport for HTML requests. RSC payload
203
- // requests (client navigations) don't serve HTML with __timber_debug_id,
204
- // so the browser can't subscribe — skip to avoid orphaned sessions.
205
- const debugRegistry = isRscRequest(req) || clientJsDisabled ? undefined : deps.devDebugRegistry;
206
- const { rscStream, signals, getDebugComponents, debugRequestId } = renderRscStream(
207
- payload,
208
- req,
209
- leafDenyChain,
210
- debugRegistry
211
- );
200
+ const { rscStream, signals } = renderRscStream(payload, req, leafDenyChain);
212
201
 
213
- // Store the debug components getter in ALS so onPipelineError can
214
- // include component tree context for render-phase errors (dev mode only).
215
- // Per-request via ALS — no cross-request race. See TIM-557.
216
- const alsStore = requestContextAls.getStore();
217
- if (alsStore) {
218
- alsStore.debugComponentsGetter = getDebugComponents;
219
- }
220
202
  recordTiming({
221
203
  name: 'rsc-init',
222
204
  dur: Math.round(performance.now() - _rscStart),
@@ -239,7 +221,6 @@ export async function renderRoute(
239
221
  layoutComponents as LayoutEntry[],
240
222
  req,
241
223
  responseHeaders,
242
- createDebugChannelSink,
243
224
  getFirstDeniedSegmentIndex()
244
225
  );
245
226
  }
@@ -251,7 +232,6 @@ export async function renderRoute(
251
232
  match,
252
233
  responseHeaders,
253
234
  clientBootstrap,
254
- createDebugChannelSink,
255
235
  callSsr,
256
236
  undefined,
257
237
  getFirstDeniedSegmentIndex()
@@ -310,6 +290,5 @@ export async function renderRoute(
310
290
  deferSuspenseFor,
311
291
  globalError,
312
292
  slotSkipInfo,
313
- debugRequestId,
314
293
  });
315
294
  }
@@ -26,7 +26,7 @@ import type { LayoutComponentEntry } from '../route-element-builder.ts';
26
26
  import type { ManifestSegmentNode } from '../route-matcher.ts';
27
27
  import type { SlotSkipEntry } from '../slot-resolver.ts';
28
28
 
29
- import { buildRedirectResponse, buildSegmentInfo, createDebugChannelSink } from './helpers.ts';
29
+ import { buildRedirectResponse, buildSegmentInfo } from './helpers.ts';
30
30
  import { RSC_CONTENT_TYPE } from '../../shared/rsc-media-type.ts';
31
31
 
32
32
  import { rscErrorEnvelope } from '../rsc-error-envelope.ts';
@@ -178,7 +178,6 @@ export async function buildRscPayloadResponse(
178
178
  layoutComponents as LayoutEntry[],
179
179
  req,
180
180
  responseHeaders,
181
- createDebugChannelSink,
182
181
  getDeniedIdx()
183
182
  );
184
183
  }
@@ -27,8 +27,7 @@ import {
27
27
  } from '../primitives.ts';
28
28
  import { checkAndWarnRscPropError } from '../rsc-prop-warnings.ts';
29
29
 
30
- import { isAbortError, type DebugComponentEntry } from './helpers.ts';
31
- import type { DebugChannelRegistry } from '../../dev-tools/debug-channel.ts';
30
+ import { isAbortError } from './helpers.ts';
32
31
  import { isDebug } from '../debug.ts';
33
32
  import { isDevMode } from '../debug.ts';
34
33
 
@@ -78,10 +77,6 @@ export interface RenderSignals {
78
77
  export interface RscStreamResult {
79
78
  rscStream: ReadableStream<Uint8Array> | undefined;
80
79
  signals: RenderSignals;
81
- /** Dev-only: server component debug info from the Flight debug channel. */
82
- getDebugComponents?: () => DebugComponentEntry[];
83
- /** Dev-only: request ID for the debug channel HMR transport. */
84
- debugRequestId?: string;
85
80
  }
86
81
 
87
82
  /**
@@ -98,8 +93,7 @@ export interface RscStreamResult {
98
93
  export function renderRscStream(
99
94
  payload: RscPayloadRoot,
100
95
  req: Request,
101
- leafDenyChain?: DenyPageEntry[],
102
- devDebugRegistry?: DebugChannelRegistry
96
+ leafDenyChain?: DenyPageEntry[]
103
97
  ): RscStreamResult {
104
98
  const signals: RenderSignals = {
105
99
  denySignal: null,
@@ -111,14 +105,6 @@ export function renderRscStream(
111
105
 
112
106
  let rscStream: ReadableStream<Uint8Array> | undefined;
113
107
 
114
- // No debugChannel — debug rows ride the main RSC stream inline.
115
- // Production Flight emits none. In dev, the browser Flight client
116
- // handles inline debug rows for owner stack resolution (React's
117
- // default behavior). A writable-only debugChannel triggers a
118
- // react-server-dom-webpack >=19.3.0 Flight client regression.
119
- const debugChannel = undefined;
120
- const debugRequestId = undefined;
121
-
122
108
  try {
123
109
  rscStream = renderToReadableStream(
124
110
  payload,
@@ -243,7 +229,6 @@ export function renderRscStream(
243
229
  errorId,
244
230
  });
245
231
  },
246
- debugChannel,
247
232
  },
248
233
  {
249
234
  onClientReference(info: { id: string; name: string; deps: unknown }) {
@@ -272,16 +257,5 @@ export function renderRscStream(
272
257
  }
273
258
  }
274
259
 
275
- return {
276
- rscStream,
277
- signals,
278
- // In production, the debug channel collector's getComponents is available.
279
- // In dev mode, debug rows ride the main stream (no separate channel), so
280
- // this is undefined — the error overlay uses stack traces directly.
281
- getDebugComponents:
282
- debugChannel && 'getComponents' in debugChannel
283
- ? (debugChannel as { getComponents: () => DebugComponentEntry[] }).getComponents
284
- : undefined,
285
- debugRequestId,
286
- };
260
+ return { rscStream, signals };
287
261
  }
@@ -27,12 +27,7 @@ import type { SlotSkipEntry } from '../slot-resolver.ts';
27
27
  import type { NavContext } from '../ssr-bridge-types.ts';
28
28
 
29
29
  import { htmlEscapeJsonString } from '../flight-scripts.ts';
30
- import {
31
- buildRedirectResponse,
32
- buildSegmentInfo,
33
- createDebugChannelSink,
34
- isAbortError,
35
- } from './helpers.ts';
30
+ import { buildRedirectResponse, buildSegmentInfo, isAbortError } from './helpers.ts';
36
31
  import { getCookiesForSsr } from '../cookie-context.ts';
37
32
  import { getDenyStatus, getFirstDeniedSegmentIndex } from '../deny-boundary.ts';
38
33
  import { renderErrorPage, type GlobalErrorFile } from './error-renderer.ts';
@@ -105,7 +100,6 @@ interface SsrRenderOptions {
105
100
  /** Tier 2 global-error.tsx file, if present in app/. */
106
101
  globalError?: GlobalErrorFile;
107
102
  slotSkipInfo?: SlotSkipEntry[];
108
- debugRequestId?: string;
109
103
  }
110
104
 
111
105
  /**
@@ -198,12 +192,7 @@ export async function renderSsrResponse(opts: SsrRenderOptions): Promise<Respons
198
192
  clientBootstrap.preloadLinks +
199
193
  // Initialize __timber_f in <head> so it exists before any streaming
200
194
  // chunk scripts arrive in <body>. See flight-scripts.ts, LOCAL-415.
201
- (clientJsDisabled ? '' : flightInitScript(nonce)) +
202
- // Embed the debug channel request ID so the browser can subscribe
203
- // to the correct HMR debug stream for owner stack resolution.
204
- (opts.debugRequestId && !clientJsDisabled
205
- ? `<script${nonceAttr(nonce)}>self.__timber_debug_id=${htmlEscapeJsonString(JSON.stringify(opts.debugRequestId))}</script>`
206
- : ''),
195
+ (clientJsDisabled ? '' : flightInitScript(nonce)),
207
196
  getHeadSegmentScript: clientJsDisabled
208
197
  ? undefined
209
198
  : () =>
@@ -250,7 +239,6 @@ export async function renderSsrResponse(opts: SsrRenderOptions): Promise<Respons
250
239
  match,
251
240
  responseHeaders,
252
241
  clientBootstrap,
253
- createDebugChannelSink,
254
242
  callSsr,
255
243
  undefined,
256
244
  resolvedDeniedIndex()
@@ -437,7 +425,6 @@ export async function renderSsrResponse(opts: SsrRenderOptions): Promise<Respons
437
425
  match,
438
426
  responseHeaders,
439
427
  clientBootstrap,
440
- createDebugChannelSink,
441
428
  callSsr,
442
429
  undefined,
443
430
  resolvedDeniedIndex()
@@ -156,12 +156,7 @@ export async function resolveSlotElement(
156
156
  const rendered = await scope(() =>
157
157
  (SlotPage as (props: Record<string, unknown>) => unknown)(props)
158
158
  );
159
- return containSlotSubtree(
160
- rendered as React.ReactNode,
161
- denyFallback,
162
- undefined,
163
- slotNode.segmentName
164
- );
159
+ return containSlotSubtree(rendered as React.ReactNode, denyFallback);
165
160
  } catch (error) {
166
161
  // RedirectSignal must propagate — the pipeline handles redirects
167
162
  // at the top level. Swallowing it here would silently return
@@ -194,20 +189,12 @@ export async function resolveSlotElement(
194
189
  element,
195
190
  h,
196
191
  scope,
197
- denyFallback,
198
- slotNode.segmentName
192
+ denyFallback
199
193
  );
200
194
 
201
195
  // Wrap with slot root's layout — INSIDE the access gate, so the layout
202
196
  // server component never executes when access.ts denies. See TIM-1074.
203
- element = await wrapWithLayout(
204
- slotNode,
205
- element,
206
- h,
207
- scope,
208
- denyFallback,
209
- slotNode.segmentName
210
- );
197
+ element = await wrapWithLayout(slotNode, element, h, scope, denyFallback);
211
198
 
212
199
  // Wrap in SlotAccessGate if slot root has access.ts. The gate is
213
200
  // OUTSIDE the layout, matching the segment model in
@@ -303,8 +290,7 @@ async function wrapWithIntermediateSegments(
303
290
  element: React.ReactElement,
304
291
  h: CreateElementFn,
305
292
  scope?: SlotParamScope,
306
- denyFallback?: React.ReactNode,
307
- slotName?: string
293
+ denyFallback?: React.ReactNode
308
294
  ): Promise<React.ReactElement> {
309
295
  for (let i = chain.length - 1; i > 0; i--) {
310
296
  const seg = chain[i];
@@ -314,7 +300,7 @@ async function wrapWithIntermediateSegments(
314
300
  errorBoundaryComponent: TimberErrorBoundary,
315
301
  isSlotBoundary: true,
316
302
  });
317
- element = await wrapWithLayout(seg, element, h, scope, denyFallback, slotName);
303
+ element = await wrapWithLayout(seg, element, h, scope, denyFallback);
318
304
  element = await wrapWithAccessGate(seg, element, h, scope);
319
305
  }
320
306
  return element;
@@ -355,16 +341,10 @@ function ScopedSlotLayout(props: {
355
341
  layout: (props: Record<string, unknown>) => unknown;
356
342
  scope: SlotParamScope;
357
343
  denyFallback: React.ReactNode;
358
- slotName: string | undefined;
359
344
  children: React.ReactElement;
360
345
  }): unknown {
361
346
  const rendered = props.scope(() => props.layout({ children: props.children }));
362
- return containSlotSubtree(
363
- rendered as React.ReactNode,
364
- props.denyFallback,
365
- props.children,
366
- props.slotName
367
- );
347
+ return containSlotSubtree(rendered as React.ReactNode, props.denyFallback, props.children);
368
348
  }
369
349
 
370
350
  /**
@@ -382,8 +362,7 @@ async function wrapWithLayout(
382
362
  element: React.ReactElement,
383
363
  h: CreateElementFn,
384
364
  scope?: SlotParamScope,
385
- denyFallback: React.ReactNode = null,
386
- slotName?: string
365
+ denyFallback: React.ReactNode = null
387
366
  ): Promise<React.ReactElement> {
388
367
  if (!node.layout) return element;
389
368
  const Layout = await loadComponent(node.layout);
@@ -394,7 +373,6 @@ async function wrapWithLayout(
394
373
  layout: Layout as (props: Record<string, unknown>) => unknown,
395
374
  scope,
396
375
  denyFallback,
397
- slotName,
398
376
  children: element,
399
377
  });
400
378
  }