@timber-js/app 0.2.0-alpha.96 → 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.
Files changed (104) hide show
  1. package/dist/_chunks/{metadata-routes-DS3eKNmf.js → metadata-routes-BU684ls2.js} +1 -1
  2. package/dist/_chunks/{metadata-routes-DS3eKNmf.js.map → metadata-routes-BU684ls2.js.map} +1 -1
  3. package/dist/_chunks/segment-classify-BjfuctV2.js +137 -0
  4. package/dist/_chunks/segment-classify-BjfuctV2.js.map +1 -0
  5. package/dist/_chunks/{interception-BsLCA9gk.js → walkers-VOXgavMF.js} +66 -92
  6. package/dist/_chunks/walkers-VOXgavMF.js.map +1 -0
  7. package/dist/adapters/nitro.d.ts.map +1 -1
  8. package/dist/adapters/nitro.js +55 -5
  9. package/dist/adapters/nitro.js.map +1 -1
  10. package/dist/client/index.js +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +189 -62
  13. package/dist/index.js.map +1 -1
  14. package/dist/plugins/build-report.d.ts +6 -4
  15. package/dist/plugins/build-report.d.ts.map +1 -1
  16. package/dist/plugins/dev-404-page.d.ts +8 -18
  17. package/dist/plugins/dev-404-page.d.ts.map +1 -1
  18. package/dist/routing/index.d.ts +5 -3
  19. package/dist/routing/index.d.ts.map +1 -1
  20. package/dist/routing/index.js +3 -3
  21. package/dist/routing/link-codegen.d.ts.map +1 -1
  22. package/dist/routing/scanner.d.ts +1 -10
  23. package/dist/routing/scanner.d.ts.map +1 -1
  24. package/dist/routing/segment-classify.d.ts +37 -8
  25. package/dist/routing/segment-classify.d.ts.map +1 -1
  26. package/dist/routing/types.d.ts +63 -23
  27. package/dist/routing/types.d.ts.map +1 -1
  28. package/dist/routing/walkers.d.ts +51 -0
  29. package/dist/routing/walkers.d.ts.map +1 -0
  30. package/dist/server/action-handler.d.ts.map +1 -1
  31. package/dist/server/dev-holding-server.d.ts +4 -2
  32. package/dist/server/dev-holding-server.d.ts.map +1 -1
  33. package/dist/server/html-injector-core.d.ts +212 -0
  34. package/dist/server/html-injector-core.d.ts.map +1 -0
  35. package/dist/server/html-injectors.d.ts +59 -59
  36. package/dist/server/html-injectors.d.ts.map +1 -1
  37. package/dist/server/internal.js +710 -563
  38. package/dist/server/internal.js.map +1 -1
  39. package/dist/server/node-stream-transforms.d.ts +46 -49
  40. package/dist/server/node-stream-transforms.d.ts.map +1 -1
  41. package/dist/server/pipeline-helpers.d.ts +88 -0
  42. package/dist/server/pipeline-helpers.d.ts.map +1 -0
  43. package/dist/server/pipeline-phases.d.ts +97 -0
  44. package/dist/server/pipeline-phases.d.ts.map +1 -0
  45. package/dist/server/pipeline.d.ts +53 -32
  46. package/dist/server/pipeline.d.ts.map +1 -1
  47. package/dist/server/port-resolution.d.ts +117 -0
  48. package/dist/server/port-resolution.d.ts.map +1 -0
  49. package/dist/server/route-matcher.d.ts +20 -47
  50. package/dist/server/route-matcher.d.ts.map +1 -1
  51. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  52. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +74 -0
  53. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -0
  54. package/dist/server/status-code-resolver.d.ts +16 -11
  55. package/dist/server/status-code-resolver.d.ts.map +1 -1
  56. package/dist/server/tree-builder.d.ts.map +1 -1
  57. package/dist/utils/directive-parser.d.ts +0 -45
  58. package/dist/utils/directive-parser.d.ts.map +1 -1
  59. package/package.json +7 -6
  60. package/src/adapters/nitro.ts +55 -5
  61. package/src/cli.ts +0 -0
  62. package/src/index.ts +84 -31
  63. package/src/plugins/build-report.ts +13 -22
  64. package/src/plugins/dev-404-page.ts +15 -41
  65. package/src/plugins/routing.ts +14 -12
  66. package/src/routing/codegen.ts +1 -1
  67. package/src/routing/convention-lint.ts +4 -4
  68. package/src/routing/index.ts +5 -3
  69. package/src/routing/interception.ts +1 -1
  70. package/src/routing/link-codegen.ts +25 -13
  71. package/src/routing/scanner.ts +17 -93
  72. package/src/routing/segment-classify.ts +107 -8
  73. package/src/routing/status-file-lint.ts +3 -3
  74. package/src/routing/types.ts +63 -23
  75. package/src/routing/walkers.ts +90 -0
  76. package/src/server/action-handler.ts +6 -0
  77. package/src/server/deny-renderer.ts +5 -5
  78. package/src/server/dev-holding-server.ts +4 -2
  79. package/src/server/fallback-error.ts +1 -1
  80. package/src/server/html-injector-core.ts +403 -0
  81. package/src/server/html-injectors.ts +158 -297
  82. package/src/server/node-stream-transforms.ts +108 -248
  83. package/src/server/pipeline-helpers.ts +180 -0
  84. package/src/server/pipeline-phases.ts +591 -0
  85. package/src/server/pipeline.ts +76 -539
  86. package/src/server/port-resolution.ts +215 -0
  87. package/src/server/route-element-builder.ts +1 -1
  88. package/src/server/route-matcher.ts +28 -60
  89. package/src/server/rsc-entry/api-handler.ts +2 -2
  90. package/src/server/rsc-entry/error-renderer.ts +1 -1
  91. package/src/server/rsc-entry/index.ts +52 -98
  92. package/src/server/rsc-entry/wrap-action-dispatch.ts +156 -0
  93. package/src/server/sitemap-generator.ts +1 -1
  94. package/src/server/slot-resolver.ts +1 -1
  95. package/src/server/status-code-resolver.ts +112 -128
  96. package/src/server/tree-builder.ts +6 -4
  97. package/src/utils/directive-parser.ts +0 -392
  98. package/LICENSE +0 -8
  99. package/dist/_chunks/interception-BsLCA9gk.js.map +0 -1
  100. package/dist/_chunks/segment-classify-BDNn6EzD.js +0 -65
  101. package/dist/_chunks/segment-classify-BDNn6EzD.js.map +0 -1
  102. package/dist/server/manifest-status-resolver.d.ts +0 -58
  103. package/dist/server/manifest-status-resolver.d.ts.map +0 -1
  104. package/src/server/manifest-status-resolver.ts +0 -215
@@ -0,0 +1,212 @@
1
+ /**
2
+ * HTML injector core — pure stateful helpers for streaming HTML post-processing.
3
+ *
4
+ * These helpers contain the actual byte/string logic for the held-flush
5
+ * streaming pipeline (buffered transforms, suffix moving, head injection,
6
+ * RSC flight interleaving). They are stream-shape agnostic: they know
7
+ * nothing about Web `TransformStream` or Node.js `Transform` and contain
8
+ * no `setImmediate` scheduling or controller plumbing.
9
+ *
10
+ * The Web Stream wrapper (`html-injectors.ts`) and the Node Stream wrapper
11
+ * (`node-stream-transforms.ts`) compose these helpers behind whichever
12
+ * stream shape their target runtime needs:
13
+ *
14
+ * - On Cloudflare/edge: Web `TransformStream` wraps the helpers.
15
+ * - On Node.js/Bun: Node `Transform` wraps the helpers (faster — C++ streams).
16
+ *
17
+ * Keeping this module split per-shape (rather than one file exporting both)
18
+ * preserves the build-time tree-shake: Cloudflare bundles never import
19
+ * `node:stream` even transitively. Only the helpers below are shared.
20
+ *
21
+ * **Pure functions only.** Anything that touches a stream API, schedules
22
+ * a tick, or owns a controller belongs in the wrapper, not here.
23
+ *
24
+ * Design docs: 02-rendering-pipeline.md §"Streaming Constraints", 18-build-system.md §"Entry Files"
25
+ */
26
+ import { type Machine } from '../utils/state-machine.js';
27
+ import { type FlightInjectionState, type FlightInjectionEvent } from './flight-injection-state.js';
28
+ /** Closing tags React Fizz emits inside the shell chunk. */
29
+ export declare const SUFFIX = "</body></html>";
30
+ /** UTF-8 bytes of {@link SUFFIX}. Cached so wrappers don't re-encode per request. */
31
+ export declare const SUFFIX_BYTES: Uint8Array;
32
+ /** Encode a UTF-8 string. Convenience for wrappers that need a single source of truth. */
33
+ export declare function encodeUtf8(text: string): Uint8Array;
34
+ /**
35
+ * Pure buffer state for the buffered transform.
36
+ *
37
+ * Both Web and Node wrappers want the same behaviour: collect chunks that
38
+ * arrive in the same event-loop tick, decide whether the buffer has grown
39
+ * beyond `maxBufferByteLength` and must flush synchronously, then emit a
40
+ * single concatenated chunk on the next tick.
41
+ *
42
+ * The only thing the two wrappers disagree on is *who schedules the tick*
43
+ * (`setImmediate` callback vs Promise-based) and *how to push the merged
44
+ * chunk* (`controller.enqueue` vs `transform.push`). This class owns
45
+ * everything else.
46
+ */
47
+ export declare class BufferAggregator {
48
+ private readonly maxBufferByteLength;
49
+ private chunks;
50
+ private byteLength;
51
+ constructor(maxBufferByteLength?: number);
52
+ /**
53
+ * Append a chunk. Returns `true` if the caller should flush synchronously
54
+ * right now (the buffer has reached the byte cap), `false` if a deferred
55
+ * tick-end flush is sufficient.
56
+ */
57
+ append(chunk: Uint8Array): boolean;
58
+ /** Drain the buffer into a single concatenated chunk, or `null` if empty. */
59
+ drain(): Uint8Array | null;
60
+ get bufferedByteLength(): number;
61
+ get isEmpty(): boolean;
62
+ }
63
+ /**
64
+ * Result of feeding one chunk through the suffix-mover state.
65
+ *
66
+ * - `passthrough` — already found and removed the suffix on a previous chunk.
67
+ * Caller emits the chunk unchanged.
68
+ * - `noSuffix` — current chunk does not contain the suffix. Caller emits unchanged.
69
+ * - `suffixFound` — caller emits `before` and `after` (skipping the suffix
70
+ * itself). The suffix will be re-emitted later by {@link suffixFlush}.
71
+ */
72
+ export type SuffixChunkResult = {
73
+ kind: 'passthrough';
74
+ } | {
75
+ kind: 'noSuffix';
76
+ } | {
77
+ kind: 'suffixFound';
78
+ before: Uint8Array | null;
79
+ after: Uint8Array | null;
80
+ };
81
+ export interface SuffixState {
82
+ found: boolean;
83
+ }
84
+ export declare function createSuffixState(): SuffixState;
85
+ /**
86
+ * Search a chunk for {@link SUFFIX}. If found, mark state and return the
87
+ * surrounding bytes (the caller emits them; the suffix is held for the
88
+ * end of the stream). The bytes-vs-text dance matches what both Web and
89
+ * Node implementations did before extraction — search via decoded UTF-8
90
+ * to avoid splitting multi-byte characters at the suffix boundary.
91
+ */
92
+ export declare function processSuffixChunk(state: SuffixState, chunk: Uint8Array): SuffixChunkResult;
93
+ /**
94
+ * Final emit for the suffix mover. Always returns the suffix bytes — even
95
+ * if no suffix was ever observed in the input — so output is well-formed.
96
+ */
97
+ export declare function suffixFlush(_state: SuffixState): Uint8Array;
98
+ /**
99
+ * Pure state for `injectHead`-style transforms: scan for a target tag,
100
+ * inject `content` once at the matching position, then pass everything
101
+ * else through. We keep a small trailing string buffer (just under the
102
+ * length of the target tag) so the tag can be detected even when split
103
+ * across chunk boundaries.
104
+ *
105
+ * The `decoder` is a long-lived `TextDecoder` reused across every chunk
106
+ * with `{ stream: true }`. This is required so a UTF-8 codepoint that
107
+ * spans two chunks (e.g. the bytes of `é` arriving in two halves) is
108
+ * reassembled correctly. A fresh decoder per chunk would drop or
109
+ * replace the partial codepoint and corrupt streamed HTML.
110
+ */
111
+ export interface InjectorState {
112
+ injected: boolean;
113
+ tail: string;
114
+ readonly content: string;
115
+ readonly targetTag: string;
116
+ readonly position: 'before' | 'after';
117
+ readonly tailLen: number;
118
+ readonly decoder: TextDecoder;
119
+ }
120
+ export interface InjectorOptions {
121
+ content: string;
122
+ targetTag: string;
123
+ position?: 'before' | 'after';
124
+ }
125
+ export declare function createInjectorState(options: InjectorOptions): InjectorState;
126
+ /**
127
+ * Result of feeding one chunk through {@link processInjectorChunk}.
128
+ *
129
+ * - `passthrough` — already injected. Emit `chunk` unchanged.
130
+ * - `emit` — emit the returned bytes (or nothing if `null`).
131
+ *
132
+ * Note: when not yet injected and the target tag is not present, the
133
+ * helper still emits the *safe head* of the buffer — everything except
134
+ * the trailing `tailLen` characters that might be the start of the
135
+ * target tag spilled into the next chunk. The unsafe tail is held in
136
+ * `state.tail` for the next call.
137
+ */
138
+ export type InjectorChunkResult = {
139
+ kind: 'passthrough';
140
+ } | {
141
+ kind: 'emit';
142
+ bytes: Uint8Array | null;
143
+ };
144
+ export declare function processInjectorChunk(state: InjectorState, chunk: Uint8Array): InjectorChunkResult;
145
+ /**
146
+ * Final emit for the injector. If the target tag never appeared, the
147
+ * leftover tail bytes are returned so they aren't lost. (The injection
148
+ * itself is silently skipped — same behaviour as before extraction.)
149
+ */
150
+ export declare function injectorFlush(state: InjectorState): Uint8Array | null;
151
+ /**
152
+ * Pure async core of the RSC flight-injection transform.
153
+ *
154
+ * Both wrappers (Web `TransformStream` and Node `Transform`) implement the
155
+ * exact same protocol around an RSC reader:
156
+ *
157
+ * 1. On the first HTML chunk, send `FIRST_CHUNK` and start the pull loop.
158
+ * 2. The pull loop reads from `rscReader` (timeout-guarded), wraps each
159
+ * chunk in a `<script>` via `flightChunkScript`, and pushes it onto a
160
+ * pending queue. Between reads, it yields with a single-shot
161
+ * `setImmediate` so HTML chunks get event-loop priority — and after
162
+ * yielding, it drains the queue (via the wrapper's `onYieldDrain`)
163
+ * so flight data reaches the client at shell-flush time.
164
+ * 3. After every emitted HTML chunk, the wrapper drains the queue.
165
+ * 4. On `flush()` the wrapper sends `HTML_DONE` and awaits `pullPromise`.
166
+ * No polling — just `.then()`.
167
+ *
168
+ * The wrappers only differ in:
169
+ * - How they push bytes (controller.enqueue vs transform.push).
170
+ * - How they signal a terminal error (controller.error vs transform.destroy).
171
+ *
172
+ * This class owns the state machine, the pending queue, and the pull
173
+ * loop itself. The wrapper supplies the drain callback and consumes the
174
+ * pending queue + machine state to decide what to push.
175
+ *
176
+ * **No polling.** See design/02-rendering-pipeline.md §"No Polling".
177
+ * **Always timeout-guarded reads.** Per design/02 §"Streaming Constraints".
178
+ */
179
+ export declare class FlightInjectorCore {
180
+ readonly machine: Machine<FlightInjectionState, FlightInjectionEvent>;
181
+ /** Script chunks waiting to be drained between HTML chunks. */
182
+ readonly pending: Uint8Array[];
183
+ private readonly rscReader;
184
+ private readonly timeoutMs;
185
+ private readonly decoder;
186
+ private pullPromise;
187
+ constructor(rscStream: ReadableStream<Uint8Array>, renderTimeoutMs?: number);
188
+ /** Tell the machine the first HTML chunk has arrived. Idempotent-ish: caller checks. */
189
+ notifyFirstChunk(): void;
190
+ /** Tell the machine HTML output is finished — pull loop will stop yielding. */
191
+ notifyHtmlDone(): void;
192
+ get isInit(): boolean;
193
+ get isPullDone(): boolean;
194
+ get hasPullPromise(): boolean;
195
+ /**
196
+ * Get (or start) the pull-loop promise. The wrapper calls this from
197
+ * `flush()` and chains `.then()` onto it — never `await` without an
198
+ * existing finish callback (we don't want to block transform()).
199
+ *
200
+ * `onYieldDrain` is invoked between RSC reads while HTML is still
201
+ * streaming. It must drain the pending queue into the output stream.
202
+ */
203
+ ensurePullLoop(onYieldDrain: () => void): Promise<void>;
204
+ /**
205
+ * Get the current terminal error if the machine is in the error phase,
206
+ * else `null`. Wrappers use this after a drain to decide whether to
207
+ * `controller.error()` / `transform.destroy()`.
208
+ */
209
+ get terminalError(): unknown;
210
+ private runPullLoop;
211
+ }
212
+ //# sourceMappingURL=html-injector-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-injector-core.d.ts","sourceRoot":"","sources":["../../src/server/html-injector-core.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,22 @@
1
1
  /**
2
- * HTML stream injectors — TransformStreams that modify streamed HTML.
3
- *
4
- * These are extracted into a separate module so they can be tested
5
- * independently of rsc-entry.ts (which imports virtual modules).
2
+ * HTML stream injectors — Web `TransformStream` wrappers around the
3
+ * pure helpers in `html-injector-core.ts`.
4
+ *
5
+ * Used on edge runtimes (Cloudflare Workers, Deno, browser SSR) where
6
+ * Web Streams are the native shape. The Node.js / Bun adapters live in
7
+ * `node-stream-transforms.ts` and wrap the same core in Node `Transform`s.
8
+ * Both wrappers must stay byte-for-byte equivalent — the core enforces
9
+ * that. If you fix a streaming bug here, you almost certainly need to
10
+ * fix it (or its cause) in the core, not in the Node wrapper.
11
+ *
12
+ * Also exports the `buildClientScripts` helper used by RSC entry to
13
+ * resolve bootstrap script URLs from the build manifest. That helper
14
+ * is unrelated to streaming but lives here for historical reasons (it
15
+ * shares no code with the core and was previously co-located with the
16
+ * Web stream transforms).
6
17
  *
7
18
  * Design docs: 02-rendering-pipeline.md, 18-build-system.md §"Entry Files"
8
19
  */
9
- /**
10
- * Options for the buffered transform stream.
11
- */
12
20
  export interface BufferedTransformOptions {
13
21
  /**
14
22
  * Flush synchronously once the buffer reaches this many bytes.
@@ -20,49 +28,38 @@ export interface BufferedTransformOptions {
20
28
  /**
21
29
  * Buffer incoming chunks and coalesce them within a single event loop tick.
22
30
  *
23
- * React Fizz may emit multiple micro-chunks within a single flush (e.g.,
24
- * opening tags, attributes, closing tags as separate writes). Without
25
- * buffering, downstream transforms (especially flight injection) could
26
- * see chunk boundaries in the middle of HTML tags or attribute values.
27
- *
28
- * This transform collects all chunks that arrive in the same tick and
29
- * emits them as a single concatenated chunk on the next `setImmediate`.
30
- * This ensures each output chunk represents a coherent HTML fragment
31
- * from a single Fizz flush — safe for downstream script injection at
32
- * chunk boundaries.
33
- *
34
- * **Not a polling loop.** Uses a single-shot `setImmediate` per flush
35
- * cycle — no recursive scheduling, no busy-wait. See design/02 §"No Polling".
31
+ * React Fizz may emit multiple micro-chunks within a single flush. Without
32
+ * coalescing, downstream transforms (especially flight injection) could
33
+ * see chunk boundaries in the middle of HTML tags. This transform collects
34
+ * everything that arrives in the same tick and emits it as one concatenated
35
+ * chunk on the next `setImmediate` — a single-shot schedule, not a poll.
36
36
  *
37
- * Inspired by Next.js `createBufferedTransformStream`.
37
+ * See design/02 §"No Polling" and §"Scripts at Chunk Boundaries Only".
38
38
  */
39
39
  export declare function createBufferedTransformStream(options?: BufferedTransformOptions): TransformStream<Uint8Array, Uint8Array>;
40
40
  /**
41
41
  * Move `</body></html>` to the end of the stream.
42
42
  *
43
- * React's renderToReadableStream emits `</body></html>` as part of the
44
- * shell chunk. Content that arrives after the shell (Suspense resolutions,
45
- * RSC script tags) would appear after the closing tags, producing invalid
46
- * HTML like `</body></html><script>...</script>`.
47
- *
48
- * This transform strips the suffix when first encountered and re-emits
49
- * it in `flush()` — after all content has passed through. If no suffix
50
- * is found, it's appended anyway to ensure well-formed HTML.
43
+ * React's renderToReadableStream emits the closing tags as part of the
44
+ * shell chunk. Content arriving after the shell (Suspense resolutions,
45
+ * RSC scripts) would otherwise appear after `</html>` and produce invalid
46
+ * HTML. This transform strips the suffix on first sight and re-emits it
47
+ * from `flush()` — after every other byte has passed through. If the
48
+ * suffix never appeared in the input, it is appended anyway so output
49
+ * is well-formed.
51
50
  *
52
51
  * Equivalent to Next.js's `createMoveSuffixStream`.
53
52
  */
54
53
  export declare function createMoveSuffixStream(): TransformStream<Uint8Array, Uint8Array>;
55
54
  /**
56
- * Inject metadata elements before </head> in the HTML stream.
57
- *
58
- * If no </head> is found, the buffer is emitted as-is.
55
+ * Inject metadata elements before `</head>` in the HTML stream.
56
+ * If no `</head>` is found, the buffer is emitted as-is.
59
57
  */
60
58
  export declare function injectHead(stream: ReadableStream<Uint8Array>, headHtml: string): ReadableStream<Uint8Array>;
61
59
  /**
62
- * Inject client bootstrap scripts before </body> in the HTML stream.
63
- *
64
- * Returns the stream unchanged if scriptsHtml is empty (client JS disabled mode).
65
- * If no </body> is found, the buffer is emitted as-is.
60
+ * Inject client bootstrap scripts before `</body>` in the HTML stream.
61
+ * Returns the stream unchanged if `scriptsHtml` is empty (client JS
62
+ * disabled mode). If no `</body>` is found, the buffer is emitted as-is.
66
63
  */
67
64
  export declare function injectScripts(stream: ReadableStream<Uint8Array>, scriptsHtml: string): ReadableStream<Uint8Array>;
68
65
  /**
@@ -70,29 +67,32 @@ export declare function injectScripts(stream: ReadableStream<Uint8Array>, script
70
67
  *
71
68
  * Uses a **pull-based** ReadableStream — the consumer (the injection
72
69
  * transform) drives reads from the RSC stream on demand. No background
73
- * reader, no shared mutable arrays, no race conditions.
74
- *
75
- * Each RSC chunk becomes a `<script>self.__timber_f.push([1,"data"])</script>`.
76
- * The init script (which creates __timber_f) is in `<head>` via
77
- * flightInitScript() — see flight-scripts.ts.
70
+ * reader, no shared mutable arrays, no race conditions. Each RSC chunk
71
+ * becomes `<script>self.__timber_f.push([1,"data"])</script>`. The
72
+ * init script (which creates `__timber_f`) is in `<head>` via
73
+ * `flightInitScript()` see flight-scripts.ts.
78
74
  */
79
75
  export declare function createInlinedRscStream(rscStream: ReadableStream<Uint8Array>, renderTimeoutMs?: number): ReadableStream<Uint8Array>;
80
76
  /**
81
77
  * Progressively inline RSC Flight payload chunks into the HTML stream.
82
78
  *
83
- * Architecture (3 TransformStream pipeline):
84
- * 1. HTML stream → moveSuffix (captures </body></html>, re-emits at end)
85
- * 2. → flightInjection (merges RSC <script> tags between HTML chunks)
86
- * 3. output (well-formed HTML with interleaved RSC data)
79
+ * Architecture (2 TransformStream pipeline):
80
+ * HTML stream → flightInjection moveSuffix output
81
+ *
82
+ * 1. flightInjection wraps each RSC chunk in a `<script>` and interleaves
83
+ * them between HTML chunks.
84
+ * 2. moveSuffix strips `</body></html>` and re-emits at end so injected
85
+ * scripts appear before the closing tags.
87
86
  *
88
- * The RSC stream is transformed into <script> tags via createInlinedRscStream
89
- * (pull-based, no shared mutable state) and merged into the HTML pipeline
90
- * via createFlightInjectionTransform.
87
+ * Wrapping happens inside `FlightInjectorCore` (via `flightChunkScript`).
88
+ * The raw RSC stream is passed straight to `createFlightInjectionTransform`
89
+ * do not pre-wrap with `createInlinedRscStream` (that helper still
90
+ * exists for callers that want a standalone scripted stream).
91
91
  *
92
92
  * The client reads these script tags via `self.__timber_f` and feeds
93
- * them to `createFromReadableStream` for progressive hydration.
94
- * Stream completion is signaled by the DOMContentLoaded event on the
95
- * client side — no custom done flag needed.
93
+ * them to `createFromReadableStream` for progressive hydration. Stream
94
+ * completion is signaled by the `DOMContentLoaded` event on the client
95
+ * side — no custom done flag needed.
96
96
  */
97
97
  export declare function injectRscPayload(htmlStream: ReadableStream<Uint8Array>, rscStream: ReadableStream<Uint8Array> | undefined, renderTimeoutMs?: number): ReadableStream<Uint8Array>;
98
98
  /**
@@ -115,19 +115,19 @@ export interface ClientBootstrapConfig {
115
115
  /**
116
116
  * Build client bootstrap configuration based on runtime config.
117
117
  *
118
- * Returns empty strings when client JavaScript is disabled,
119
- * which produces zero-JS output. When `enableHMRInDev` is true and
120
- * running in dev mode, injects only the Vite HMR client (no app
121
- * bootstrap) so hot reloading works during development.
118
+ * Returns empty strings when client JavaScript is disabled, which produces
119
+ * zero-JS output. When `enableHMRInDev` is true and running in dev mode,
120
+ * injects only the Vite HMR client (no app bootstrap) so hot reloading
121
+ * works during development.
122
122
  *
123
123
  * In production, uses hashed chunk URLs from the build manifest.
124
124
  *
125
125
  * The bootstrap uses dynamic `import()` inside a regular (non-module)
126
126
  * inline script so it executes immediately during HTML parsing. This
127
- * is critical for streaming: `<script type="module">` is deferred
128
- * until the document finishes parsing, which blocks hydration behind
129
- * Suspense boundaries. Dynamic `import()` starts module loading and
130
- * execution as soon as the shell HTML is parsed.
127
+ * is critical for streaming: `<script type="module">` is deferred until
128
+ * the document finishes parsing, which blocks hydration behind Suspense
129
+ * boundaries. Dynamic `import()` starts module loading and execution as
130
+ * soon as the shell HTML is parsed.
131
131
  */
132
132
  export declare function buildClientScripts(runtimeConfig: {
133
133
  output: string;
@@ -1 +1 @@
1
- {"version":3,"file":"html-injectors.d.ts","sourceRoot":"","sources":["../../src/server/html-injectors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,wBAA6B,GACrC,eAAe,CAAC,UAAU,EAAE,UAAU,CAAC,CAgEzC;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,IAAI,eAAe,CAAC,UAAU,EAAE,UAAU,CAAC,CAuChF;AA8ED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,QAAQ,EAAE,MAAM,GACf,cAAc,CAAC,UAAU,CAAC,CAE5B;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,WAAW,EAAE,MAAM,GAClB,cAAc,CAAC,UAAU,CAAC,CAE5B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,EACrC,eAAe,CAAC,EAAE,MAAM,GACvB,cAAc,CAAC,UAAU,CAAC,CAiC5B;AAmID;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,cAAc,CAAC,UAAU,CAAC,EACtC,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,SAAS,EACjD,eAAe,CAAC,EAAE,MAAM,GACvB,cAAc,CAAC,UAAU,CAAC,CAiB5B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;CACtB;AAqBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAC;IACjE,GAAG,EAAE,OAAO,CAAC;IACb,aAAa,CAAC,EAAE,OAAO,qBAAqB,EAAE,aAAa,CAAC;CAC7D,GAAG,qBAAqB,CA8DxB"}
1
+ {"version":3,"file":"html-injectors.d.ts","sourceRoot":"","sources":["../../src/server/html-injectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAkBH,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CACvC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,wBAA6B,GACrC,eAAe,CAAC,UAAU,EAAE,UAAU,CAAC,CA0CzC;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,IAAI,eAAe,CAAC,UAAU,EAAE,UAAU,CAAC,CAiBhF;AAuCD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,QAAQ,EAAE,MAAM,GACf,cAAc,CAAC,UAAU,CAAC,CAE5B;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,WAAW,EAAE,MAAM,GAClB,cAAc,CAAC,UAAU,CAAC,CAE5B;AAID;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,EACrC,eAAe,CAAC,EAAE,MAAM,GACvB,cAAc,CAAC,UAAU,CAAC,CA6B5B;AAuED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,cAAc,CAAC,UAAU,CAAC,EACtC,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,SAAS,EACjD,eAAe,CAAC,EAAE,MAAM,GACvB,cAAc,CAAC,UAAU,CAAC,CAS5B;AAID;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;CACtB;AAqBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAC;IACjE,GAAG,EAAE,OAAO,CAAC;IACb,aAAa,CAAC,EAAE,OAAO,qBAAqB,EAAE,aAAa,CAAC;CAC7D,GAAG,qBAAqB,CA8DxB"}