@tanstack/start-server-core 1.167.9 → 1.167.11
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 +48 -32
- 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 +21 -21
- 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 +106 -56
- package/src/tanstack-start.d.ts +3 -1
- package/src/transformAssetUrls.ts +13 -18
- package/src/virtual-modules.ts +1 -0
- package/LICENSE +0 -21
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.)
|
|
@@ -117,8 +115,8 @@ export const handleServerAction = async ({
|
|
|
117
115
|
deserializedContext
|
|
118
116
|
) {
|
|
119
117
|
params.context = safeObjectMerge(
|
|
120
|
-
context,
|
|
121
118
|
deserializedContext as Record<string, unknown>,
|
|
119
|
+
context,
|
|
122
120
|
)
|
|
123
121
|
}
|
|
124
122
|
} catch (e) {
|
|
@@ -144,7 +142,7 @@ export const handleServerAction = async ({
|
|
|
144
142
|
const payload: any = payloadParam
|
|
145
143
|
? parsePayload(JSON.parse(payloadParam))
|
|
146
144
|
: {}
|
|
147
|
-
payload.context = safeObjectMerge(context,
|
|
145
|
+
payload.context = safeObjectMerge(payload.context, context)
|
|
148
146
|
payload.method = methodUpper
|
|
149
147
|
// Send it through!
|
|
150
148
|
return await action(payload)
|
|
@@ -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
|
+
})
|
|
298
318
|
}
|
|
299
|
-
|
|
300
|
-
|
|
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
|
+
})
|
|
330
|
+
}
|
|
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
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2021-present Tanner Linsley
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|