@tanstack/start-server-core 1.167.10 → 1.167.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/createSsrRpc.d.ts +1 -3
- package/dist/esm/createSsrRpc.js +2 -2
- package/dist/esm/createSsrRpc.js.map +1 -1
- package/dist/esm/createStartHandler.js +23 -9
- package/dist/esm/createStartHandler.js.map +1 -1
- package/dist/esm/fake-start-server-fn-resolver.d.ts +3 -1
- package/dist/esm/fake-start-server-fn-resolver.js +1 -1
- package/dist/esm/fake-start-server-fn-resolver.js.map +1 -1
- package/dist/esm/frame-protocol.d.ts +14 -2
- package/dist/esm/frame-protocol.js +83 -70
- package/dist/esm/frame-protocol.js.map +1 -1
- package/dist/esm/serializer/ServerFunctionSerializationAdapter.js +1 -1
- package/dist/esm/serializer/ServerFunctionSerializationAdapter.js.map +1 -1
- package/dist/esm/server-functions-handler.js +46 -30
- package/dist/esm/server-functions-handler.js.map +1 -1
- package/dist/esm/transformAssetUrls.js +6 -8
- package/dist/esm/transformAssetUrls.js.map +1 -1
- package/dist/esm/virtual-modules.d.ts +1 -0
- package/dist/esm/virtual-modules.js +2 -1
- package/dist/esm/virtual-modules.js.map +1 -1
- package/package.json +2 -2
- package/src/createSsrRpc.ts +2 -9
- package/src/createStartHandler.ts +44 -14
- package/src/fake-start-server-fn-resolver.ts +4 -1
- package/src/frame-protocol.ts +131 -89
- package/src/serializer/ServerFunctionSerializationAdapter.ts +1 -1
- package/src/server-functions-handler.ts +104 -54
- package/src/tanstack-start.d.ts +3 -1
- package/src/transformAssetUrls.ts +13 -18
- package/src/virtual-modules.ts +1 -0
package/src/frame-protocol.ts
CHANGED
|
@@ -82,135 +82,177 @@ export function encodeErrorFrame(streamId: number, error: unknown): Uint8Array {
|
|
|
82
82
|
return encodeFrame(FrameType.ERROR, streamId, textEncoder.encode(message))
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Late stream registration for RawStreams discovered after serialization starts.
|
|
87
|
+
* Used when Promise<RawStream> resolves after the initial synchronous pass.
|
|
88
|
+
*/
|
|
89
|
+
export interface LateStreamRegistration {
|
|
90
|
+
id: number
|
|
91
|
+
stream: ReadableStream<Uint8Array>
|
|
92
|
+
}
|
|
93
|
+
|
|
85
94
|
/**
|
|
86
95
|
* Creates a multiplexed ReadableStream from JSON stream and raw streams.
|
|
87
96
|
*
|
|
88
97
|
* The JSON stream emits NDJSON lines (from seroval's toCrossJSONStream).
|
|
89
98
|
* Raw streams are pumped concurrently, interleaved with JSON frames.
|
|
90
99
|
*
|
|
100
|
+
* Supports late stream registration for RawStreams discovered after initial
|
|
101
|
+
* serialization (e.g., from resolved Promises).
|
|
102
|
+
*
|
|
91
103
|
* @param jsonStream Stream of JSON strings (each string is one NDJSON line)
|
|
92
|
-
* @param rawStreams Map of stream IDs to raw binary streams
|
|
104
|
+
* @param rawStreams Map of stream IDs to raw binary streams (known at start)
|
|
105
|
+
* @param lateStreamSource Optional stream of late registrations for streams discovered later
|
|
93
106
|
*/
|
|
94
107
|
export function createMultiplexedStream(
|
|
95
108
|
jsonStream: ReadableStream<string>,
|
|
96
109
|
rawStreams: Map<number, ReadableStream<Uint8Array>>,
|
|
110
|
+
lateStreamSource?: ReadableStream<LateStreamRegistration>,
|
|
97
111
|
): ReadableStream<Uint8Array> {
|
|
98
|
-
//
|
|
99
|
-
let
|
|
100
|
-
let
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
if (cancelled
|
|
112
|
+
// Shared state for the multiplexed stream
|
|
113
|
+
let controller: ReadableStreamDefaultController<Uint8Array>
|
|
114
|
+
let cancelled = false
|
|
115
|
+
const readers: Array<ReadableStreamDefaultReader<any>> = []
|
|
116
|
+
|
|
117
|
+
// Helper to enqueue a frame, ignoring errors if stream is closed/cancelled
|
|
118
|
+
const enqueue = (frame: Uint8Array): boolean => {
|
|
119
|
+
if (cancelled) return false
|
|
106
120
|
try {
|
|
107
|
-
|
|
121
|
+
controller.enqueue(frame)
|
|
122
|
+
return true
|
|
108
123
|
} catch {
|
|
109
|
-
|
|
124
|
+
return false
|
|
110
125
|
}
|
|
111
126
|
}
|
|
112
127
|
|
|
113
|
-
|
|
114
|
-
|
|
128
|
+
// Helper to error the output stream (for fatal errors like JSON stream failure)
|
|
129
|
+
const errorOutput = (error: unknown): void => {
|
|
130
|
+
if (cancelled) return
|
|
131
|
+
cancelled = true
|
|
115
132
|
try {
|
|
116
|
-
|
|
133
|
+
controller.error(error)
|
|
117
134
|
} catch {
|
|
118
|
-
//
|
|
135
|
+
// Already errored
|
|
136
|
+
}
|
|
137
|
+
// Cancel all readers to stop other pumps
|
|
138
|
+
for (const reader of readers) {
|
|
139
|
+
reader.cancel().catch(() => {})
|
|
119
140
|
}
|
|
120
141
|
}
|
|
121
142
|
|
|
122
|
-
|
|
123
|
-
|
|
143
|
+
// Pumps a raw stream, sending CHUNK frames and END/ERROR on completion
|
|
144
|
+
async function pumpRawStream(
|
|
145
|
+
streamId: number,
|
|
146
|
+
stream: ReadableStream<Uint8Array>,
|
|
147
|
+
): Promise<void> {
|
|
148
|
+
const reader = stream.getReader()
|
|
149
|
+
readers.push(reader)
|
|
124
150
|
try {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
151
|
+
while (!cancelled) {
|
|
152
|
+
const { done, value } = await reader.read()
|
|
153
|
+
if (done) {
|
|
154
|
+
enqueue(encodeEndFrame(streamId))
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
if (!enqueue(encodeChunkFrame(streamId, value))) return
|
|
158
|
+
}
|
|
159
|
+
} catch (error) {
|
|
160
|
+
// Raw stream error - send ERROR frame, don't fail entire response
|
|
161
|
+
enqueue(encodeErrorFrame(streamId, error))
|
|
162
|
+
} finally {
|
|
163
|
+
reader.releaseLock()
|
|
128
164
|
}
|
|
129
165
|
}
|
|
130
166
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
167
|
+
// Pumps the JSON stream, sending JSON frames
|
|
168
|
+
// JSON stream errors are fatal - they error the entire output
|
|
169
|
+
async function pumpJSON(): Promise<void> {
|
|
170
|
+
const reader = jsonStream.getReader()
|
|
171
|
+
readers.push(reader)
|
|
172
|
+
try {
|
|
173
|
+
while (!cancelled) {
|
|
174
|
+
const { done, value } = await reader.read()
|
|
175
|
+
if (done) return
|
|
176
|
+
if (!enqueue(encodeJSONFrame(value))) return
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
// JSON stream error is fatal - error the entire output
|
|
180
|
+
errorOutput(error)
|
|
181
|
+
throw error // Re-throw to signal failure to Promise.all
|
|
182
|
+
} finally {
|
|
183
|
+
reader.releaseLock()
|
|
135
184
|
}
|
|
136
185
|
}
|
|
137
186
|
|
|
187
|
+
// Pumps late stream registrations, spawning raw stream pumps as they arrive
|
|
188
|
+
async function pumpLateStreams(): Promise<Array<Promise<void>>> {
|
|
189
|
+
if (!lateStreamSource) return []
|
|
190
|
+
|
|
191
|
+
const lateStreamPumps: Array<Promise<void>> = []
|
|
192
|
+
const reader = lateStreamSource.getReader()
|
|
193
|
+
readers.push(reader)
|
|
194
|
+
try {
|
|
195
|
+
while (!cancelled) {
|
|
196
|
+
const { done, value } = await reader.read()
|
|
197
|
+
if (done) break
|
|
198
|
+
// Start pumping this late stream and track it
|
|
199
|
+
lateStreamPumps.push(pumpRawStream(value.id, value.stream))
|
|
200
|
+
}
|
|
201
|
+
} finally {
|
|
202
|
+
reader.releaseLock()
|
|
203
|
+
}
|
|
204
|
+
return lateStreamPumps
|
|
205
|
+
}
|
|
206
|
+
|
|
138
207
|
return new ReadableStream<Uint8Array>({
|
|
139
|
-
start(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// Catch async rejection - reader may already be released
|
|
148
|
-
reader.cancel().catch(() => {})
|
|
149
|
-
})
|
|
150
|
-
try {
|
|
151
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
152
|
-
while (true) {
|
|
153
|
-
const { done, value } = await reader.read()
|
|
154
|
-
// Check cancelled after await - flag may have changed while waiting
|
|
155
|
-
if (cancelled) break
|
|
156
|
-
if (done) break
|
|
157
|
-
safeEnqueue(encodeJSONFrame(value))
|
|
158
|
-
}
|
|
159
|
-
} catch (error) {
|
|
160
|
-
// JSON stream error - fatal, error the whole response
|
|
161
|
-
safeError(error)
|
|
162
|
-
} finally {
|
|
163
|
-
reader.releaseLock()
|
|
164
|
-
checkComplete()
|
|
165
|
-
}
|
|
208
|
+
async start(ctrl) {
|
|
209
|
+
controller = ctrl
|
|
210
|
+
|
|
211
|
+
// Collect all pump promises
|
|
212
|
+
const pumps: Array<Promise<void | Array<Promise<void>>>> = [pumpJSON()]
|
|
213
|
+
|
|
214
|
+
for (const [streamId, stream] of rawStreams) {
|
|
215
|
+
pumps.push(pumpRawStream(streamId, stream))
|
|
166
216
|
}
|
|
167
217
|
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
stream: ReadableStream<Uint8Array>,
|
|
172
|
-
) => {
|
|
173
|
-
const reader = stream.getReader()
|
|
174
|
-
cancelReaders.push(() => {
|
|
175
|
-
// Catch async rejection - reader may already be released
|
|
176
|
-
reader.cancel().catch(() => {})
|
|
177
|
-
})
|
|
178
|
-
try {
|
|
179
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
180
|
-
while (true) {
|
|
181
|
-
const { done, value } = await reader.read()
|
|
182
|
-
// Check cancelled after await - flag may have changed while waiting
|
|
183
|
-
if (cancelled) break
|
|
184
|
-
if (done) {
|
|
185
|
-
safeEnqueue(encodeEndFrame(streamId))
|
|
186
|
-
break
|
|
187
|
-
}
|
|
188
|
-
safeEnqueue(encodeChunkFrame(streamId, value))
|
|
189
|
-
}
|
|
190
|
-
} catch (error) {
|
|
191
|
-
// Stream error - send ERROR frame (non-fatal, other streams continue)
|
|
192
|
-
safeEnqueue(encodeErrorFrame(streamId, error))
|
|
193
|
-
} finally {
|
|
194
|
-
reader.releaseLock()
|
|
195
|
-
checkComplete()
|
|
196
|
-
}
|
|
218
|
+
// Add late stream pump (returns array of spawned pump promises)
|
|
219
|
+
if (lateStreamSource) {
|
|
220
|
+
pumps.push(pumpLateStreams())
|
|
197
221
|
}
|
|
198
222
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
223
|
+
try {
|
|
224
|
+
// Wait for initial pumps to complete
|
|
225
|
+
const results = await Promise.all(pumps)
|
|
226
|
+
|
|
227
|
+
// Wait for any late stream pumps that were spawned
|
|
228
|
+
const latePumps = results.find(Array.isArray) as
|
|
229
|
+
| Array<Promise<void>>
|
|
230
|
+
| undefined
|
|
231
|
+
if (latePumps && latePumps.length > 0) {
|
|
232
|
+
await Promise.all(latePumps)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// All pumps done - close the output stream
|
|
236
|
+
if (!cancelled) {
|
|
237
|
+
try {
|
|
238
|
+
controller.close()
|
|
239
|
+
} catch {
|
|
240
|
+
// Already closed
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
} catch {
|
|
244
|
+
// Error already handled by errorOutput in pumpJSON
|
|
245
|
+
// or was a raw stream error (non-fatal, already sent ERROR frame)
|
|
203
246
|
}
|
|
204
247
|
},
|
|
205
248
|
|
|
206
249
|
cancel() {
|
|
207
250
|
cancelled = true
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
cancelReader()
|
|
251
|
+
// Cancel all readers to stop pumps quickly
|
|
252
|
+
for (const reader of readers) {
|
|
253
|
+
reader.cancel().catch(() => {})
|
|
212
254
|
}
|
|
213
|
-
|
|
255
|
+
readers.length = 0
|
|
214
256
|
},
|
|
215
257
|
})
|
|
216
258
|
}
|
|
@@ -17,7 +17,7 @@ export const ServerFunctionSerializationAdapter = createSerializationAdapter({
|
|
|
17
17
|
// When a function ID is received through serialization (e.g., as a parameter
|
|
18
18
|
// to another server function), it originates from the client and must be
|
|
19
19
|
// validated the same way as direct HTTP calls to server functions.
|
|
20
|
-
const serverFn = await getServerFnById(functionId, {
|
|
20
|
+
const serverFn = await getServerFnById(functionId, { origin: 'client' })
|
|
21
21
|
const result = await serverFn(opts ?? {}, signal)
|
|
22
22
|
return result.result
|
|
23
23
|
}
|
|
@@ -18,14 +18,12 @@ import {
|
|
|
18
18
|
TSS_CONTENT_TYPE_FRAMED_VERSIONED,
|
|
19
19
|
createMultiplexedStream,
|
|
20
20
|
} from './frame-protocol'
|
|
21
|
+
import type { LateStreamRegistration } from './frame-protocol'
|
|
21
22
|
import type { Plugin as SerovalPlugin } from 'seroval'
|
|
22
23
|
|
|
23
24
|
// Cache serovalPlugins at module level to avoid repeated calls
|
|
24
25
|
let serovalPlugins: Array<SerovalPlugin<any, any>> | undefined = undefined
|
|
25
26
|
|
|
26
|
-
// Cache TextEncoder for NDJSON serialization
|
|
27
|
-
const textEncoder = new TextEncoder()
|
|
28
|
-
|
|
29
27
|
// Known FormData 'Content-Type' header values - module-level constant
|
|
30
28
|
const FORM_DATA_CONTENT_TYPES = [
|
|
31
29
|
'multipart/form-data',
|
|
@@ -48,7 +46,7 @@ export const handleServerAction = async ({
|
|
|
48
46
|
const methodUpper = method.toUpperCase()
|
|
49
47
|
const url = new URL(request.url)
|
|
50
48
|
|
|
51
|
-
const action = await getServerFnById(serverFnId, {
|
|
49
|
+
const action = await getServerFnById(serverFnId, { origin: 'client' })
|
|
52
50
|
|
|
53
51
|
// Early method check: reject mismatched HTTP methods before parsing
|
|
54
52
|
// the request payload (FormData, JSON, query string, etc.)
|
|
@@ -186,11 +184,40 @@ export const handleServerAction = async ({
|
|
|
186
184
|
|
|
187
185
|
const alsResponse = getResponse()
|
|
188
186
|
if (res !== undefined) {
|
|
189
|
-
// Collect raw streams encountered during serialization
|
|
187
|
+
// Collect raw streams encountered during initial synchronous serialization
|
|
190
188
|
const rawStreams = new Map<number, ReadableStream<Uint8Array>>()
|
|
189
|
+
|
|
190
|
+
// Track whether we're still in the initial synchronous phase
|
|
191
|
+
// After initial phase, new RawStreams go to lateStreamWriter
|
|
192
|
+
let initialPhase = true
|
|
193
|
+
|
|
194
|
+
// Late stream registration for RawStreams discovered after initial pass
|
|
195
|
+
// (e.g., from resolved Promises)
|
|
196
|
+
let lateStreamWriter:
|
|
197
|
+
| WritableStreamDefaultWriter<LateStreamRegistration>
|
|
198
|
+
| undefined
|
|
199
|
+
let lateStreamReadable:
|
|
200
|
+
| ReadableStream<LateStreamRegistration>
|
|
201
|
+
| undefined = undefined
|
|
202
|
+
const pendingLateStreams: Array<LateStreamRegistration> = []
|
|
203
|
+
|
|
191
204
|
const rawStreamPlugin = createRawStreamRPCPlugin(
|
|
192
205
|
(id: number, stream: ReadableStream<Uint8Array>) => {
|
|
193
|
-
|
|
206
|
+
if (initialPhase) {
|
|
207
|
+
rawStreams.set(id, stream)
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (lateStreamWriter) {
|
|
212
|
+
// Late stream - write to the late stream channel
|
|
213
|
+
lateStreamWriter.write({ id, stream }).catch(() => {
|
|
214
|
+
// Ignore write errors - stream may be closed
|
|
215
|
+
})
|
|
216
|
+
return
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Discovered after initial phase but before writer exists.
|
|
220
|
+
pendingLateStreams.push({ id, stream })
|
|
194
221
|
},
|
|
195
222
|
)
|
|
196
223
|
|
|
@@ -228,6 +255,13 @@ export const handleServerAction = async ({
|
|
|
228
255
|
},
|
|
229
256
|
})
|
|
230
257
|
|
|
258
|
+
// End of initial synchronous phase - any new RawStreams are "late"
|
|
259
|
+
initialPhase = false
|
|
260
|
+
|
|
261
|
+
// If any RawStreams are discovered after this point but before the
|
|
262
|
+
// late-stream writer exists, we buffer them and flush once the writer
|
|
263
|
+
// is ready. This avoids an occasional missed-stream race.
|
|
264
|
+
|
|
231
265
|
// If no raw streams and done synchronously, return simple JSON
|
|
232
266
|
if (done && rawStreams.size === 0) {
|
|
233
267
|
return new Response(
|
|
@@ -243,71 +277,87 @@ export const handleServerAction = async ({
|
|
|
243
277
|
)
|
|
244
278
|
}
|
|
245
279
|
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// Already closed
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
callbacks.onError = (error) => controller.error(error)
|
|
262
|
-
// Emit initial body if we have one
|
|
263
|
-
if (nonStreamingBody !== undefined) {
|
|
264
|
-
callbacks.onParse(nonStreamingBody)
|
|
265
|
-
}
|
|
266
|
-
},
|
|
267
|
-
})
|
|
268
|
-
|
|
269
|
-
// Create multiplexed stream with JSON and raw streams
|
|
270
|
-
const multiplexedStream = createMultiplexedStream(
|
|
271
|
-
jsonStream,
|
|
272
|
-
rawStreams,
|
|
273
|
-
)
|
|
274
|
-
|
|
275
|
-
return new Response(multiplexedStream, {
|
|
276
|
-
status: alsResponse.status,
|
|
277
|
-
statusText: alsResponse.statusText,
|
|
278
|
-
headers: {
|
|
279
|
-
'Content-Type': TSS_CONTENT_TYPE_FRAMED_VERSIONED,
|
|
280
|
-
[X_TSS_SERIALIZED]: 'true',
|
|
281
|
-
},
|
|
280
|
+
// Not done synchronously or has raw streams - use framed protocol
|
|
281
|
+
// This supports late RawStreams from resolved Promises
|
|
282
|
+
const { readable, writable } =
|
|
283
|
+
new TransformStream<LateStreamRegistration>()
|
|
284
|
+
lateStreamReadable = readable
|
|
285
|
+
lateStreamWriter = writable.getWriter()
|
|
286
|
+
|
|
287
|
+
// Flush any late streams that were discovered in the small window
|
|
288
|
+
// between end of initial serialization and writer setup.
|
|
289
|
+
for (const registration of pendingLateStreams) {
|
|
290
|
+
lateStreamWriter.write(registration).catch(() => {
|
|
291
|
+
// Ignore write errors - stream may be closed
|
|
282
292
|
})
|
|
283
293
|
}
|
|
294
|
+
pendingLateStreams.length = 0
|
|
284
295
|
|
|
285
|
-
//
|
|
286
|
-
const
|
|
296
|
+
// Create a stream of JSON chunks
|
|
297
|
+
const jsonStream = new ReadableStream<string>({
|
|
287
298
|
start(controller) {
|
|
288
|
-
callbacks.onParse = (value) =>
|
|
289
|
-
controller.enqueue(
|
|
290
|
-
|
|
291
|
-
)
|
|
299
|
+
callbacks.onParse = (value) => {
|
|
300
|
+
controller.enqueue(JSON.stringify(value) + '\n')
|
|
301
|
+
}
|
|
292
302
|
callbacks.onDone = () => {
|
|
293
303
|
try {
|
|
294
304
|
controller.close()
|
|
295
|
-
} catch
|
|
296
|
-
|
|
305
|
+
} catch {
|
|
306
|
+
// Already closed
|
|
297
307
|
}
|
|
308
|
+
// Close late stream writer when JSON serialization is done
|
|
309
|
+
// Any RawStreams not yet discovered won't be sent
|
|
310
|
+
lateStreamWriter
|
|
311
|
+
?.close()
|
|
312
|
+
.catch(() => {
|
|
313
|
+
// Ignore close errors
|
|
314
|
+
})
|
|
315
|
+
.finally(() => {
|
|
316
|
+
lateStreamWriter = undefined
|
|
317
|
+
})
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
callbacks.onError = (error) => {
|
|
321
|
+
controller.error(error)
|
|
322
|
+
lateStreamWriter
|
|
323
|
+
?.abort(error)
|
|
324
|
+
.catch(() => {
|
|
325
|
+
// Ignore abort errors
|
|
326
|
+
})
|
|
327
|
+
.finally(() => {
|
|
328
|
+
lateStreamWriter = undefined
|
|
329
|
+
})
|
|
298
330
|
}
|
|
299
|
-
|
|
300
|
-
//
|
|
331
|
+
|
|
332
|
+
// Emit initial body if we have one
|
|
301
333
|
if (nonStreamingBody !== undefined) {
|
|
302
334
|
callbacks.onParse(nonStreamingBody)
|
|
303
335
|
}
|
|
336
|
+
// If serialization already completed synchronously, close now
|
|
337
|
+
// This handles the case where onDone was called during toCrossJSONStream
|
|
338
|
+
// before we overwrote callbacks.onDone
|
|
339
|
+
if (done) {
|
|
340
|
+
callbacks.onDone()
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
cancel() {
|
|
344
|
+
lateStreamWriter?.abort().catch(() => {})
|
|
345
|
+
lateStreamWriter = undefined
|
|
304
346
|
},
|
|
305
347
|
})
|
|
306
|
-
|
|
348
|
+
|
|
349
|
+
// Create multiplexed stream with JSON, initial raw streams, and late streams
|
|
350
|
+
const multiplexedStream = createMultiplexedStream(
|
|
351
|
+
jsonStream,
|
|
352
|
+
rawStreams,
|
|
353
|
+
lateStreamReadable,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
return new Response(multiplexedStream, {
|
|
307
357
|
status: alsResponse.status,
|
|
308
358
|
statusText: alsResponse.statusText,
|
|
309
359
|
headers: {
|
|
310
|
-
'Content-Type':
|
|
360
|
+
'Content-Type': TSS_CONTENT_TYPE_FRAMED_VERSIONED,
|
|
311
361
|
[X_TSS_SERIALIZED]: 'true',
|
|
312
362
|
},
|
|
313
363
|
})
|
package/src/tanstack-start.d.ts
CHANGED
|
@@ -11,12 +11,14 @@ declare module 'tanstack-start-route-tree:v' {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
declare module '#tanstack-start-server-fn-resolver' {
|
|
14
|
+
export type ServerFnLookupAccess = { origin: 'client' } | { origin: 'server' }
|
|
15
|
+
|
|
14
16
|
export type ServerFn = ((...args: Array<any>) => Promise<any>) & {
|
|
15
17
|
method?: 'GET' | 'POST'
|
|
16
18
|
}
|
|
17
19
|
export function getServerFnById(
|
|
18
20
|
id: string,
|
|
19
|
-
|
|
21
|
+
access: ServerFnLookupAccess,
|
|
20
22
|
): Promise<ServerFn>
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -445,16 +445,15 @@ export async function transformManifestAssets(
|
|
|
445
445
|
}),
|
|
446
446
|
)
|
|
447
447
|
|
|
448
|
-
const rootRoute = manifest.routes[rootRouteId]
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
}
|
|
448
|
+
const rootRoute = (manifest.routes[rootRouteId] =
|
|
449
|
+
manifest.routes[rootRouteId] || {})
|
|
450
|
+
rootRoute.assets = rootRoute.assets || []
|
|
451
|
+
rootRoute.assets.push(
|
|
452
|
+
buildClientEntryScriptTag(
|
|
453
|
+
transformedClientEntry.href,
|
|
454
|
+
source.injectedHeadScripts,
|
|
455
|
+
),
|
|
456
|
+
)
|
|
458
457
|
|
|
459
458
|
return manifest
|
|
460
459
|
}
|
|
@@ -476,14 +475,10 @@ export function buildManifestWithClientEntry(
|
|
|
476
475
|
const baseRootRoute = source.manifest.routes[rootRouteId]
|
|
477
476
|
const routes = {
|
|
478
477
|
...source.manifest.routes,
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
assets: [...(baseRootRoute.assets || []), scriptTag],
|
|
484
|
-
},
|
|
485
|
-
}
|
|
486
|
-
: {}),
|
|
478
|
+
[rootRouteId]: {
|
|
479
|
+
...baseRootRoute,
|
|
480
|
+
assets: [...(baseRootRoute?.assets || []), scriptTag],
|
|
481
|
+
},
|
|
487
482
|
}
|
|
488
483
|
|
|
489
484
|
return { routes }
|
package/src/virtual-modules.ts
CHANGED