@tanstack/start-server-core 1.166.9 → 1.166.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.
@@ -1,24 +1,23 @@
1
- import { createSerializationAdapter } from "@tanstack/router-core";
1
+ import { getServerFnById } from "../getServerFnById.js";
2
2
  import { TSS_SERVER_FUNCTION } from "@tanstack/start-client-core";
3
- import { getServerFnById } from "#tanstack-start-server-fn-resolver";
4
- const ServerFunctionSerializationAdapter = createSerializationAdapter({
5
- key: "$TSS/serverfn",
6
- test: (v) => {
7
- if (typeof v !== "function") return false;
8
- if (!(TSS_SERVER_FUNCTION in v)) return false;
9
- return !!v[TSS_SERVER_FUNCTION];
10
- },
11
- toSerializable: ({ serverFnMeta }) => ({ functionId: serverFnMeta.id }),
12
- fromSerializable: ({ functionId }) => {
13
- const fn = async (opts, signal) => {
14
- const serverFn = await getServerFnById(functionId, { fromClient: true });
15
- const result = await serverFn(opts ?? {}, signal);
16
- return result.result;
17
- };
18
- return fn;
19
- }
3
+ import { createSerializationAdapter } from "@tanstack/router-core";
4
+ //#region src/serializer/ServerFunctionSerializationAdapter.ts
5
+ var ServerFunctionSerializationAdapter = createSerializationAdapter({
6
+ key: "$TSS/serverfn",
7
+ test: (v) => {
8
+ if (typeof v !== "function") return false;
9
+ if (!(TSS_SERVER_FUNCTION in v)) return false;
10
+ return !!v[TSS_SERVER_FUNCTION];
11
+ },
12
+ toSerializable: ({ serverFnMeta }) => ({ functionId: serverFnMeta.id }),
13
+ fromSerializable: ({ functionId }) => {
14
+ const fn = async (opts, signal) => {
15
+ return (await (await getServerFnById(functionId, { fromClient: true }))(opts ?? {}, signal)).result;
16
+ };
17
+ return fn;
18
+ }
20
19
  });
21
- export {
22
- ServerFunctionSerializationAdapter
23
- };
24
- //# sourceMappingURL=ServerFunctionSerializationAdapter.js.map
20
+ //#endregion
21
+ export { ServerFunctionSerializationAdapter };
22
+
23
+ //# sourceMappingURL=ServerFunctionSerializationAdapter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ServerFunctionSerializationAdapter.js","sources":["../../../src/serializer/ServerFunctionSerializationAdapter.ts"],"sourcesContent":["import { createSerializationAdapter } from '@tanstack/router-core'\nimport { TSS_SERVER_FUNCTION } from '@tanstack/start-client-core'\nimport { getServerFnById } from '../getServerFnById'\n\nexport const ServerFunctionSerializationAdapter = createSerializationAdapter({\n key: '$TSS/serverfn',\n test: (v): v is { serverFnMeta: { id: string } } => {\n if (typeof v !== 'function') return false\n\n if (!(TSS_SERVER_FUNCTION in v)) return false\n\n return !!v[TSS_SERVER_FUNCTION]\n },\n toSerializable: ({ serverFnMeta }) => ({ functionId: serverFnMeta.id }),\n fromSerializable: ({ functionId }) => {\n const fn = async (opts: any, signal: any): Promise<any> => {\n // When a function ID is received through serialization (e.g., as a parameter\n // to another server function), it originates from the client and must be\n // validated the same way as direct HTTP calls to server functions.\n const serverFn = await getServerFnById(functionId, { fromClient: true })\n const result = await serverFn(opts ?? {}, signal)\n return result.result\n }\n return fn as never\n },\n})\n"],"names":[],"mappings":";;;AAIO,MAAM,qCAAqC,2BAA2B;AAAA,EAC3E,KAAK;AAAA,EACL,MAAM,CAAC,MAA6C;AAClD,QAAI,OAAO,MAAM,WAAY,QAAO;AAEpC,QAAI,EAAE,uBAAuB,GAAI,QAAO;AAExC,WAAO,CAAC,CAAC,EAAE,mBAAmB;AAAA,EAChC;AAAA,EACA,gBAAgB,CAAC,EAAE,aAAA,OAAoB,EAAE,YAAY,aAAa;EAClE,kBAAkB,CAAC,EAAE,iBAAiB;AACpC,UAAM,KAAK,OAAO,MAAW,WAA8B;AAIzD,YAAM,WAAW,MAAM,gBAAgB,YAAY,EAAE,YAAY,MAAM;AACvE,YAAM,SAAS,MAAM,SAAS,QAAQ,CAAA,GAAI,MAAM;AAChD,aAAO,OAAO;AAAA,IAChB;AACA,WAAO;AAAA,EACT;AACF,CAAC;"}
1
+ {"version":3,"file":"ServerFunctionSerializationAdapter.js","names":[],"sources":["../../../src/serializer/ServerFunctionSerializationAdapter.ts"],"sourcesContent":["import { createSerializationAdapter } from '@tanstack/router-core'\nimport { TSS_SERVER_FUNCTION } from '@tanstack/start-client-core'\nimport { getServerFnById } from '../getServerFnById'\n\nexport const ServerFunctionSerializationAdapter = createSerializationAdapter({\n key: '$TSS/serverfn',\n test: (v): v is { serverFnMeta: { id: string } } => {\n if (typeof v !== 'function') return false\n\n if (!(TSS_SERVER_FUNCTION in v)) return false\n\n return !!v[TSS_SERVER_FUNCTION]\n },\n toSerializable: ({ serverFnMeta }) => ({ functionId: serverFnMeta.id }),\n fromSerializable: ({ functionId }) => {\n const fn = async (opts: any, signal: any): Promise<any> => {\n // When a function ID is received through serialization (e.g., as a parameter\n // to another server function), it originates from the client and must be\n // validated the same way as direct HTTP calls to server functions.\n const serverFn = await getServerFnById(functionId, { fromClient: true })\n const result = await serverFn(opts ?? {}, signal)\n return result.result\n }\n return fn as never\n },\n})\n"],"mappings":";;;;AAIA,IAAa,qCAAqC,2BAA2B;CAC3E,KAAK;CACL,OAAO,MAA6C;AAClD,MAAI,OAAO,MAAM,WAAY,QAAO;AAEpC,MAAI,EAAE,uBAAuB,GAAI,QAAO;AAExC,SAAO,CAAC,CAAC,EAAE;;CAEb,iBAAiB,EAAE,oBAAoB,EAAE,YAAY,aAAa,IAAI;CACtE,mBAAmB,EAAE,iBAAiB;EACpC,MAAM,KAAK,OAAO,MAAW,WAA8B;AAMzD,WADe,OADE,MAAM,gBAAgB,YAAY,EAAE,YAAY,MAAM,CAAC,EAC1C,QAAQ,EAAE,EAAE,OAAO,EACnC;;AAEhB,SAAO;;CAEV,CAAC"}
@@ -1,274 +1,198 @@
1
+ import { getResponse } from "./request-response.js";
2
+ import { getServerFnById } from "./getServerFnById.js";
3
+ import { TSS_CONTENT_TYPE_FRAMED_VERSIONED, createMultiplexedStream } from "./frame-protocol.js";
4
+ import { TSS_FORMDATA_CONTEXT, X_TSS_RAW_RESPONSE, X_TSS_SERIALIZED, getDefaultSerovalPlugins, safeObjectMerge } from "@tanstack/start-client-core";
1
5
  import { createRawStreamRPCPlugin, isNotFound, isRedirect } from "@tanstack/router-core";
2
6
  import invariant from "tiny-invariant";
3
- import { getDefaultSerovalPlugins, X_TSS_SERIALIZED, TSS_CONTENT_TYPE_FRAMED_VERSIONED, TSS_FORMDATA_CONTEXT, safeObjectMerge, X_TSS_RAW_RESPONSE } from "@tanstack/start-client-core";
4
- import { toCrossJSONStream, fromJSON, toCrossJSONAsync } from "seroval";
5
- import { getResponse } from "./request-response.js";
6
- import { createMultiplexedStream } from "./frame-protocol.js";
7
- import { getServerFnById } from "#tanstack-start-server-fn-resolver";
8
- let serovalPlugins = void 0;
9
- const textEncoder = new TextEncoder();
10
- const FORM_DATA_CONTENT_TYPES = [
11
- "multipart/form-data",
12
- "application/x-www-form-urlencoded"
13
- ];
14
- const MAX_PAYLOAD_SIZE = 1e6;
15
- const handleServerAction = async ({
16
- request,
17
- context,
18
- serverFnId
19
- }) => {
20
- const method = request.method;
21
- const methodUpper = method.toUpperCase();
22
- const url = new URL(request.url);
23
- const action = await getServerFnById(serverFnId, { fromClient: true });
24
- if (action.method && methodUpper !== action.method) {
25
- return new Response(
26
- `expected ${action.method} method. Got ${methodUpper}`,
27
- {
28
- status: 405,
29
- headers: {
30
- Allow: action.method
31
- }
32
- }
33
- );
34
- }
35
- const isServerFn = request.headers.get("x-tsr-serverFn") === "true";
36
- if (!serovalPlugins) {
37
- serovalPlugins = getDefaultSerovalPlugins();
38
- }
39
- const contentType = request.headers.get("Content-Type");
40
- function parsePayload(payload) {
41
- const parsedPayload = fromJSON(payload, { plugins: serovalPlugins });
42
- return parsedPayload;
43
- }
44
- const response = await (async () => {
45
- try {
46
- let serializeResult = function(res2) {
47
- let nonStreamingBody = void 0;
48
- const alsResponse = getResponse();
49
- if (res2 !== void 0) {
50
- const rawStreams = /* @__PURE__ */ new Map();
51
- const rawStreamPlugin = createRawStreamRPCPlugin(
52
- (id, stream2) => {
53
- rawStreams.set(id, stream2);
54
- }
55
- );
56
- const plugins = [rawStreamPlugin, ...serovalPlugins || []];
57
- let done = false;
58
- const callbacks = {
59
- onParse: (value) => {
60
- nonStreamingBody = value;
61
- },
62
- onDone: () => {
63
- done = true;
64
- },
65
- onError: (error) => {
66
- throw error;
67
- }
68
- };
69
- toCrossJSONStream(res2, {
70
- refs: /* @__PURE__ */ new Map(),
71
- plugins,
72
- onParse(value) {
73
- callbacks.onParse(value);
74
- },
75
- onDone() {
76
- callbacks.onDone();
77
- },
78
- onError: (error) => {
79
- callbacks.onError(error);
80
- }
81
- });
82
- if (done && rawStreams.size === 0) {
83
- return new Response(
84
- nonStreamingBody ? JSON.stringify(nonStreamingBody) : void 0,
85
- {
86
- status: alsResponse.status,
87
- statusText: alsResponse.statusText,
88
- headers: {
89
- "Content-Type": "application/json",
90
- [X_TSS_SERIALIZED]: "true"
91
- }
92
- }
93
- );
94
- }
95
- if (rawStreams.size > 0) {
96
- const jsonStream = new ReadableStream({
97
- start(controller) {
98
- callbacks.onParse = (value) => {
99
- controller.enqueue(JSON.stringify(value) + "\n");
100
- };
101
- callbacks.onDone = () => {
102
- try {
103
- controller.close();
104
- } catch {
105
- }
106
- };
107
- callbacks.onError = (error) => controller.error(error);
108
- if (nonStreamingBody !== void 0) {
109
- callbacks.onParse(nonStreamingBody);
110
- }
111
- }
112
- });
113
- const multiplexedStream = createMultiplexedStream(
114
- jsonStream,
115
- rawStreams
116
- );
117
- return new Response(multiplexedStream, {
118
- status: alsResponse.status,
119
- statusText: alsResponse.statusText,
120
- headers: {
121
- "Content-Type": TSS_CONTENT_TYPE_FRAMED_VERSIONED,
122
- [X_TSS_SERIALIZED]: "true"
123
- }
124
- });
125
- }
126
- const stream = new ReadableStream({
127
- start(controller) {
128
- callbacks.onParse = (value) => controller.enqueue(
129
- textEncoder.encode(JSON.stringify(value) + "\n")
130
- );
131
- callbacks.onDone = () => {
132
- try {
133
- controller.close();
134
- } catch (error) {
135
- controller.error(error);
136
- }
137
- };
138
- callbacks.onError = (error) => controller.error(error);
139
- if (nonStreamingBody !== void 0) {
140
- callbacks.onParse(nonStreamingBody);
141
- }
142
- }
143
- });
144
- return new Response(stream, {
145
- status: alsResponse.status,
146
- statusText: alsResponse.statusText,
147
- headers: {
148
- "Content-Type": "application/x-ndjson",
149
- [X_TSS_SERIALIZED]: "true"
150
- }
151
- });
152
- }
153
- return new Response(void 0, {
154
- status: alsResponse.status,
155
- statusText: alsResponse.statusText
156
- });
157
- };
158
- let res = await (async () => {
159
- if (FORM_DATA_CONTENT_TYPES.some(
160
- (type) => contentType && contentType.includes(type)
161
- )) {
162
- invariant(
163
- methodUpper !== "GET",
164
- "GET requests with FormData payloads are not supported"
165
- );
166
- const formData = await request.formData();
167
- const serializedContext = formData.get(TSS_FORMDATA_CONTEXT);
168
- formData.delete(TSS_FORMDATA_CONTEXT);
169
- const params = {
170
- context,
171
- data: formData,
172
- method: methodUpper
173
- };
174
- if (typeof serializedContext === "string") {
175
- try {
176
- const parsedContext = JSON.parse(serializedContext);
177
- const deserializedContext = fromJSON(parsedContext, {
178
- plugins: serovalPlugins
179
- });
180
- if (typeof deserializedContext === "object" && deserializedContext) {
181
- params.context = safeObjectMerge(
182
- context,
183
- deserializedContext
184
- );
185
- }
186
- } catch (e) {
187
- if (process.env.NODE_ENV === "development") {
188
- console.warn("Failed to parse FormData context:", e);
189
- }
190
- }
191
- }
192
- return await action(params);
193
- }
194
- if (methodUpper === "GET") {
195
- const payloadParam = url.searchParams.get("payload");
196
- if (payloadParam && payloadParam.length > MAX_PAYLOAD_SIZE) {
197
- throw new Error("Payload too large");
198
- }
199
- const payload2 = payloadParam ? parsePayload(JSON.parse(payloadParam)) : {};
200
- payload2.context = safeObjectMerge(context, payload2.context);
201
- payload2.method = methodUpper;
202
- return await action(payload2);
203
- }
204
- let jsonPayload;
205
- if (contentType?.includes("application/json")) {
206
- jsonPayload = await request.json();
207
- }
208
- const payload = jsonPayload ? parsePayload(jsonPayload) : {};
209
- payload.context = safeObjectMerge(payload.context, context);
210
- payload.method = methodUpper;
211
- return await action(payload);
212
- })();
213
- const unwrapped = res.result || res.error;
214
- if (isNotFound(res)) {
215
- res = isNotFoundResponse(res);
216
- }
217
- if (!isServerFn) {
218
- return unwrapped;
219
- }
220
- if (unwrapped instanceof Response) {
221
- if (isRedirect(unwrapped)) {
222
- return unwrapped;
223
- }
224
- unwrapped.headers.set(X_TSS_RAW_RESPONSE, "true");
225
- return unwrapped;
226
- }
227
- return serializeResult(res);
228
- } catch (error) {
229
- if (error instanceof Response) {
230
- return error;
231
- }
232
- if (isNotFound(error)) {
233
- return isNotFoundResponse(error);
234
- }
235
- console.info();
236
- console.info("Server Fn Error!");
237
- console.info();
238
- console.error(error);
239
- console.info();
240
- const serializedError = JSON.stringify(
241
- await Promise.resolve(
242
- toCrossJSONAsync(error, {
243
- refs: /* @__PURE__ */ new Map(),
244
- plugins: serovalPlugins
245
- })
246
- )
247
- );
248
- const response2 = getResponse();
249
- return new Response(serializedError, {
250
- status: response2.status ?? 500,
251
- statusText: response2.statusText,
252
- headers: {
253
- "Content-Type": "application/json",
254
- [X_TSS_SERIALIZED]: "true"
255
- }
256
- });
257
- }
258
- })();
259
- return response;
7
+ import { fromJSON, toCrossJSONAsync, toCrossJSONStream } from "seroval";
8
+ //#region src/server-functions-handler.ts
9
+ var serovalPlugins = void 0;
10
+ var textEncoder = new TextEncoder();
11
+ var FORM_DATA_CONTENT_TYPES = ["multipart/form-data", "application/x-www-form-urlencoded"];
12
+ var MAX_PAYLOAD_SIZE = 1e6;
13
+ var handleServerAction = async ({ request, context, serverFnId }) => {
14
+ const methodUpper = request.method.toUpperCase();
15
+ const url = new URL(request.url);
16
+ const action = await getServerFnById(serverFnId, { fromClient: true });
17
+ if (action.method && methodUpper !== action.method) return new Response(`expected ${action.method} method. Got ${methodUpper}`, {
18
+ status: 405,
19
+ headers: { Allow: action.method }
20
+ });
21
+ const isServerFn = request.headers.get("x-tsr-serverFn") === "true";
22
+ if (!serovalPlugins) serovalPlugins = getDefaultSerovalPlugins();
23
+ const contentType = request.headers.get("Content-Type");
24
+ function parsePayload(payload) {
25
+ return fromJSON(payload, { plugins: serovalPlugins });
26
+ }
27
+ return await (async () => {
28
+ try {
29
+ let res = await (async () => {
30
+ if (FORM_DATA_CONTENT_TYPES.some((type) => contentType && contentType.includes(type))) {
31
+ invariant(methodUpper !== "GET", "GET requests with FormData payloads are not supported");
32
+ const formData = await request.formData();
33
+ const serializedContext = formData.get(TSS_FORMDATA_CONTEXT);
34
+ formData.delete(TSS_FORMDATA_CONTEXT);
35
+ const params = {
36
+ context,
37
+ data: formData,
38
+ method: methodUpper
39
+ };
40
+ if (typeof serializedContext === "string") try {
41
+ const deserializedContext = fromJSON(JSON.parse(serializedContext), { plugins: serovalPlugins });
42
+ if (typeof deserializedContext === "object" && deserializedContext) params.context = safeObjectMerge(context, deserializedContext);
43
+ } catch (e) {
44
+ if (process.env.NODE_ENV === "development") console.warn("Failed to parse FormData context:", e);
45
+ }
46
+ return await action(params);
47
+ }
48
+ if (methodUpper === "GET") {
49
+ const payloadParam = url.searchParams.get("payload");
50
+ if (payloadParam && payloadParam.length > MAX_PAYLOAD_SIZE) throw new Error("Payload too large");
51
+ const payload = payloadParam ? parsePayload(JSON.parse(payloadParam)) : {};
52
+ payload.context = safeObjectMerge(context, payload.context);
53
+ payload.method = methodUpper;
54
+ return await action(payload);
55
+ }
56
+ let jsonPayload;
57
+ if (contentType?.includes("application/json")) jsonPayload = await request.json();
58
+ const payload = jsonPayload ? parsePayload(jsonPayload) : {};
59
+ payload.context = safeObjectMerge(payload.context, context);
60
+ payload.method = methodUpper;
61
+ return await action(payload);
62
+ })();
63
+ const unwrapped = res.result || res.error;
64
+ if (isNotFound(res)) res = isNotFoundResponse(res);
65
+ if (!isServerFn) return unwrapped;
66
+ if (unwrapped instanceof Response) {
67
+ if (isRedirect(unwrapped)) return unwrapped;
68
+ unwrapped.headers.set(X_TSS_RAW_RESPONSE, "true");
69
+ return unwrapped;
70
+ }
71
+ return serializeResult(res);
72
+ function serializeResult(res) {
73
+ let nonStreamingBody = void 0;
74
+ const alsResponse = getResponse();
75
+ if (res !== void 0) {
76
+ const rawStreams = /* @__PURE__ */ new Map();
77
+ const plugins = [createRawStreamRPCPlugin((id, stream) => {
78
+ rawStreams.set(id, stream);
79
+ }), ...serovalPlugins || []];
80
+ let done = false;
81
+ const callbacks = {
82
+ onParse: (value) => {
83
+ nonStreamingBody = value;
84
+ },
85
+ onDone: () => {
86
+ done = true;
87
+ },
88
+ onError: (error) => {
89
+ throw error;
90
+ }
91
+ };
92
+ toCrossJSONStream(res, {
93
+ refs: /* @__PURE__ */ new Map(),
94
+ plugins,
95
+ onParse(value) {
96
+ callbacks.onParse(value);
97
+ },
98
+ onDone() {
99
+ callbacks.onDone();
100
+ },
101
+ onError: (error) => {
102
+ callbacks.onError(error);
103
+ }
104
+ });
105
+ if (done && rawStreams.size === 0) return new Response(nonStreamingBody ? JSON.stringify(nonStreamingBody) : void 0, {
106
+ status: alsResponse.status,
107
+ statusText: alsResponse.statusText,
108
+ headers: {
109
+ "Content-Type": "application/json",
110
+ [X_TSS_SERIALIZED]: "true"
111
+ }
112
+ });
113
+ if (rawStreams.size > 0) {
114
+ const multiplexedStream = createMultiplexedStream(new ReadableStream({ start(controller) {
115
+ callbacks.onParse = (value) => {
116
+ controller.enqueue(JSON.stringify(value) + "\n");
117
+ };
118
+ callbacks.onDone = () => {
119
+ try {
120
+ controller.close();
121
+ } catch {}
122
+ };
123
+ callbacks.onError = (error) => controller.error(error);
124
+ if (nonStreamingBody !== void 0) callbacks.onParse(nonStreamingBody);
125
+ } }), rawStreams);
126
+ return new Response(multiplexedStream, {
127
+ status: alsResponse.status,
128
+ statusText: alsResponse.statusText,
129
+ headers: {
130
+ "Content-Type": TSS_CONTENT_TYPE_FRAMED_VERSIONED,
131
+ [X_TSS_SERIALIZED]: "true"
132
+ }
133
+ });
134
+ }
135
+ const stream = new ReadableStream({ start(controller) {
136
+ callbacks.onParse = (value) => controller.enqueue(textEncoder.encode(JSON.stringify(value) + "\n"));
137
+ callbacks.onDone = () => {
138
+ try {
139
+ controller.close();
140
+ } catch (error) {
141
+ controller.error(error);
142
+ }
143
+ };
144
+ callbacks.onError = (error) => controller.error(error);
145
+ if (nonStreamingBody !== void 0) callbacks.onParse(nonStreamingBody);
146
+ } });
147
+ return new Response(stream, {
148
+ status: alsResponse.status,
149
+ statusText: alsResponse.statusText,
150
+ headers: {
151
+ "Content-Type": "application/x-ndjson",
152
+ [X_TSS_SERIALIZED]: "true"
153
+ }
154
+ });
155
+ }
156
+ return new Response(void 0, {
157
+ status: alsResponse.status,
158
+ statusText: alsResponse.statusText
159
+ });
160
+ }
161
+ } catch (error) {
162
+ if (error instanceof Response) return error;
163
+ if (isNotFound(error)) return isNotFoundResponse(error);
164
+ console.info();
165
+ console.info("Server Fn Error!");
166
+ console.info();
167
+ console.error(error);
168
+ console.info();
169
+ const serializedError = JSON.stringify(await Promise.resolve(toCrossJSONAsync(error, {
170
+ refs: /* @__PURE__ */ new Map(),
171
+ plugins: serovalPlugins
172
+ })));
173
+ const response = getResponse();
174
+ return new Response(serializedError, {
175
+ status: response.status ?? 500,
176
+ statusText: response.statusText,
177
+ headers: {
178
+ "Content-Type": "application/json",
179
+ [X_TSS_SERIALIZED]: "true"
180
+ }
181
+ });
182
+ }
183
+ })();
260
184
  };
261
185
  function isNotFoundResponse(error) {
262
- const { headers, ...rest } = error;
263
- return new Response(JSON.stringify(rest), {
264
- status: 404,
265
- headers: {
266
- "Content-Type": "application/json",
267
- ...headers || {}
268
- }
269
- });
186
+ const { headers, ...rest } = error;
187
+ return new Response(JSON.stringify(rest), {
188
+ status: 404,
189
+ headers: {
190
+ "Content-Type": "application/json",
191
+ ...headers || {}
192
+ }
193
+ });
270
194
  }
271
- export {
272
- handleServerAction
273
- };
274
- //# sourceMappingURL=server-functions-handler.js.map
195
+ //#endregion
196
+ export { handleServerAction };
197
+
198
+ //# sourceMappingURL=server-functions-handler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server-functions-handler.js","sources":["../../src/server-functions-handler.ts"],"sourcesContent":["import {\n createRawStreamRPCPlugin,\n isNotFound,\n isRedirect,\n} from '@tanstack/router-core'\nimport invariant from 'tiny-invariant'\nimport {\n TSS_FORMDATA_CONTEXT,\n X_TSS_RAW_RESPONSE,\n X_TSS_SERIALIZED,\n getDefaultSerovalPlugins,\n safeObjectMerge,\n} from '@tanstack/start-client-core'\nimport { fromJSON, toCrossJSONAsync, toCrossJSONStream } from 'seroval'\nimport { getResponse } from './request-response'\nimport { getServerFnById } from './getServerFnById'\nimport {\n TSS_CONTENT_TYPE_FRAMED_VERSIONED,\n createMultiplexedStream,\n} from './frame-protocol'\nimport type { Plugin as SerovalPlugin } from 'seroval'\n\n// Cache serovalPlugins at module level to avoid repeated calls\nlet serovalPlugins: Array<SerovalPlugin<any, any>> | undefined = undefined\n\n// Cache TextEncoder for NDJSON serialization\nconst textEncoder = new TextEncoder()\n\n// Known FormData 'Content-Type' header values - module-level constant\nconst FORM_DATA_CONTENT_TYPES = [\n 'multipart/form-data',\n 'application/x-www-form-urlencoded',\n]\n\n// Maximum payload size for GET requests (1MB)\nconst MAX_PAYLOAD_SIZE = 1_000_000\n\nexport const handleServerAction = async ({\n request,\n context,\n serverFnId,\n}: {\n request: Request\n context: any\n serverFnId: string\n}) => {\n const method = request.method\n const methodUpper = method.toUpperCase()\n const url = new URL(request.url)\n\n const action = await getServerFnById(serverFnId, { fromClient: true })\n\n // Early method check: reject mismatched HTTP methods before parsing\n // the request payload (FormData, JSON, query string, etc.)\n if (action.method && methodUpper !== action.method) {\n return new Response(\n `expected ${action.method} method. Got ${methodUpper}`,\n {\n status: 405,\n headers: {\n Allow: action.method,\n },\n },\n )\n }\n\n const isServerFn = request.headers.get('x-tsr-serverFn') === 'true'\n\n // Initialize serovalPlugins lazily (cached at module level)\n if (!serovalPlugins) {\n serovalPlugins = getDefaultSerovalPlugins()\n }\n\n const contentType = request.headers.get('Content-Type')\n\n function parsePayload(payload: any) {\n const parsedPayload = fromJSON(payload, { plugins: serovalPlugins })\n return parsedPayload as any\n }\n\n const response = await (async () => {\n try {\n let res = await (async () => {\n // FormData\n if (\n FORM_DATA_CONTENT_TYPES.some(\n (type) => contentType && contentType.includes(type),\n )\n ) {\n // We don't support GET requests with FormData payloads... that seems impossible\n invariant(\n methodUpper !== 'GET',\n 'GET requests with FormData payloads are not supported',\n )\n const formData = await request.formData()\n const serializedContext = formData.get(TSS_FORMDATA_CONTEXT)\n formData.delete(TSS_FORMDATA_CONTEXT)\n\n const params = {\n context,\n data: formData,\n method: methodUpper,\n }\n if (typeof serializedContext === 'string') {\n try {\n const parsedContext = JSON.parse(serializedContext)\n const deserializedContext = fromJSON(parsedContext, {\n plugins: serovalPlugins,\n })\n if (\n typeof deserializedContext === 'object' &&\n deserializedContext\n ) {\n params.context = safeObjectMerge(\n context,\n deserializedContext as Record<string, unknown>,\n )\n }\n } catch (e) {\n // Log warning for debugging but don't expose to client\n if (process.env.NODE_ENV === 'development') {\n console.warn('Failed to parse FormData context:', e)\n }\n }\n }\n\n return await action(params)\n }\n\n // Get requests use the query string\n if (methodUpper === 'GET') {\n // Get payload directly from searchParams\n const payloadParam = url.searchParams.get('payload')\n // Reject oversized payloads to prevent DoS\n if (payloadParam && payloadParam.length > MAX_PAYLOAD_SIZE) {\n throw new Error('Payload too large')\n }\n // If there's a payload, we should try to parse it\n const payload: any = payloadParam\n ? parsePayload(JSON.parse(payloadParam))\n : {}\n payload.context = safeObjectMerge(context, payload.context)\n payload.method = methodUpper\n // Send it through!\n return await action(payload)\n }\n\n let jsonPayload\n if (contentType?.includes('application/json')) {\n jsonPayload = await request.json()\n }\n\n const payload = jsonPayload ? parsePayload(jsonPayload) : {}\n payload.context = safeObjectMerge(payload.context, context)\n payload.method = methodUpper\n return await action(payload)\n })()\n\n const unwrapped = res.result || res.error\n\n if (isNotFound(res)) {\n res = isNotFoundResponse(res)\n }\n\n if (!isServerFn) {\n return unwrapped\n }\n\n if (unwrapped instanceof Response) {\n if (isRedirect(unwrapped)) {\n return unwrapped\n }\n unwrapped.headers.set(X_TSS_RAW_RESPONSE, 'true')\n return unwrapped\n }\n\n return serializeResult(res)\n\n function serializeResult(res: unknown): Response {\n let nonStreamingBody: any = undefined\n\n const alsResponse = getResponse()\n if (res !== undefined) {\n // Collect raw streams encountered during serialization\n const rawStreams = new Map<number, ReadableStream<Uint8Array>>()\n const rawStreamPlugin = createRawStreamRPCPlugin(\n (id: number, stream: ReadableStream<Uint8Array>) => {\n rawStreams.set(id, stream)\n },\n )\n\n // Build plugins with RawStreamRPCPlugin first (before default SSR plugin)\n const plugins = [rawStreamPlugin, ...(serovalPlugins || [])]\n\n // first run without the stream in case `result` does not need streaming\n let done = false as boolean\n const callbacks: {\n onParse: (value: any) => void\n onDone: () => void\n onError: (error: any) => void\n } = {\n onParse: (value) => {\n nonStreamingBody = value\n },\n onDone: () => {\n done = true\n },\n onError: (error) => {\n throw error\n },\n }\n toCrossJSONStream(res, {\n refs: new Map(),\n plugins,\n onParse(value) {\n callbacks.onParse(value)\n },\n onDone() {\n callbacks.onDone()\n },\n onError: (error) => {\n callbacks.onError(error)\n },\n })\n\n // If no raw streams and done synchronously, return simple JSON\n if (done && rawStreams.size === 0) {\n return new Response(\n nonStreamingBody ? JSON.stringify(nonStreamingBody) : undefined,\n {\n status: alsResponse.status,\n statusText: alsResponse.statusText,\n headers: {\n 'Content-Type': 'application/json',\n [X_TSS_SERIALIZED]: 'true',\n },\n },\n )\n }\n\n // If we have raw streams, use framed protocol\n if (rawStreams.size > 0) {\n // Create a stream of JSON chunks (NDJSON style)\n const jsonStream = new ReadableStream<string>({\n start(controller) {\n callbacks.onParse = (value) => {\n controller.enqueue(JSON.stringify(value) + '\\n')\n }\n callbacks.onDone = () => {\n try {\n controller.close()\n } catch {\n // Already closed\n }\n }\n callbacks.onError = (error) => controller.error(error)\n // Emit initial body if we have one\n if (nonStreamingBody !== undefined) {\n callbacks.onParse(nonStreamingBody)\n }\n },\n })\n\n // Create multiplexed stream with JSON and raw streams\n const multiplexedStream = createMultiplexedStream(\n jsonStream,\n rawStreams,\n )\n\n return new Response(multiplexedStream, {\n status: alsResponse.status,\n statusText: alsResponse.statusText,\n headers: {\n 'Content-Type': TSS_CONTENT_TYPE_FRAMED_VERSIONED,\n [X_TSS_SERIALIZED]: 'true',\n },\n })\n }\n\n // No raw streams but not done yet - use standard NDJSON streaming\n const stream = new ReadableStream({\n start(controller) {\n callbacks.onParse = (value) =>\n controller.enqueue(\n textEncoder.encode(JSON.stringify(value) + '\\n'),\n )\n callbacks.onDone = () => {\n try {\n controller.close()\n } catch (error) {\n controller.error(error)\n }\n }\n callbacks.onError = (error) => controller.error(error)\n // stream initial body\n if (nonStreamingBody !== undefined) {\n callbacks.onParse(nonStreamingBody)\n }\n },\n })\n return new Response(stream, {\n status: alsResponse.status,\n statusText: alsResponse.statusText,\n headers: {\n 'Content-Type': 'application/x-ndjson',\n [X_TSS_SERIALIZED]: 'true',\n },\n })\n }\n\n return new Response(undefined, {\n status: alsResponse.status,\n statusText: alsResponse.statusText,\n })\n }\n } catch (error: any) {\n if (error instanceof Response) {\n return error\n }\n // else if (\n // isPlainObject(error) &&\n // 'result' in error &&\n // error.result instanceof Response\n // ) {\n // return error.result\n // }\n\n // Currently this server-side context has no idea how to\n // build final URLs, so we need to defer that to the client.\n // The client will check for __redirect and __notFound keys,\n // and if they exist, it will handle them appropriately.\n\n if (isNotFound(error)) {\n return isNotFoundResponse(error)\n }\n\n console.info()\n console.info('Server Fn Error!')\n console.info()\n console.error(error)\n console.info()\n\n const serializedError = JSON.stringify(\n await Promise.resolve(\n toCrossJSONAsync(error, {\n refs: new Map(),\n plugins: serovalPlugins,\n }),\n ),\n )\n const response = getResponse()\n return new Response(serializedError, {\n status: response.status ?? 500,\n statusText: response.statusText,\n headers: {\n 'Content-Type': 'application/json',\n [X_TSS_SERIALIZED]: 'true',\n },\n })\n }\n })()\n\n return response\n}\n\nfunction isNotFoundResponse(error: any) {\n const { headers, ...rest } = error\n\n return new Response(JSON.stringify(rest), {\n status: 404,\n headers: {\n 'Content-Type': 'application/json',\n ...(headers || {}),\n },\n })\n}\n"],"names":["res","stream","payload","response"],"mappings":"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"}
1
+ {"version":3,"file":"server-functions-handler.js","names":[],"sources":["../../src/server-functions-handler.ts"],"sourcesContent":["import {\n createRawStreamRPCPlugin,\n isNotFound,\n isRedirect,\n} from '@tanstack/router-core'\nimport invariant from 'tiny-invariant'\nimport {\n TSS_FORMDATA_CONTEXT,\n X_TSS_RAW_RESPONSE,\n X_TSS_SERIALIZED,\n getDefaultSerovalPlugins,\n safeObjectMerge,\n} from '@tanstack/start-client-core'\nimport { fromJSON, toCrossJSONAsync, toCrossJSONStream } from 'seroval'\nimport { getResponse } from './request-response'\nimport { getServerFnById } from './getServerFnById'\nimport {\n TSS_CONTENT_TYPE_FRAMED_VERSIONED,\n createMultiplexedStream,\n} from './frame-protocol'\nimport type { Plugin as SerovalPlugin } from 'seroval'\n\n// Cache serovalPlugins at module level to avoid repeated calls\nlet serovalPlugins: Array<SerovalPlugin<any, any>> | undefined = undefined\n\n// Cache TextEncoder for NDJSON serialization\nconst textEncoder = new TextEncoder()\n\n// Known FormData 'Content-Type' header values - module-level constant\nconst FORM_DATA_CONTENT_TYPES = [\n 'multipart/form-data',\n 'application/x-www-form-urlencoded',\n]\n\n// Maximum payload size for GET requests (1MB)\nconst MAX_PAYLOAD_SIZE = 1_000_000\n\nexport const handleServerAction = async ({\n request,\n context,\n serverFnId,\n}: {\n request: Request\n context: any\n serverFnId: string\n}) => {\n const method = request.method\n const methodUpper = method.toUpperCase()\n const url = new URL(request.url)\n\n const action = await getServerFnById(serverFnId, { fromClient: true })\n\n // Early method check: reject mismatched HTTP methods before parsing\n // the request payload (FormData, JSON, query string, etc.)\n if (action.method && methodUpper !== action.method) {\n return new Response(\n `expected ${action.method} method. Got ${methodUpper}`,\n {\n status: 405,\n headers: {\n Allow: action.method,\n },\n },\n )\n }\n\n const isServerFn = request.headers.get('x-tsr-serverFn') === 'true'\n\n // Initialize serovalPlugins lazily (cached at module level)\n if (!serovalPlugins) {\n serovalPlugins = getDefaultSerovalPlugins()\n }\n\n const contentType = request.headers.get('Content-Type')\n\n function parsePayload(payload: any) {\n const parsedPayload = fromJSON(payload, { plugins: serovalPlugins })\n return parsedPayload as any\n }\n\n const response = await (async () => {\n try {\n let res = await (async () => {\n // FormData\n if (\n FORM_DATA_CONTENT_TYPES.some(\n (type) => contentType && contentType.includes(type),\n )\n ) {\n // We don't support GET requests with FormData payloads... that seems impossible\n invariant(\n methodUpper !== 'GET',\n 'GET requests with FormData payloads are not supported',\n )\n const formData = await request.formData()\n const serializedContext = formData.get(TSS_FORMDATA_CONTEXT)\n formData.delete(TSS_FORMDATA_CONTEXT)\n\n const params = {\n context,\n data: formData,\n method: methodUpper,\n }\n if (typeof serializedContext === 'string') {\n try {\n const parsedContext = JSON.parse(serializedContext)\n const deserializedContext = fromJSON(parsedContext, {\n plugins: serovalPlugins,\n })\n if (\n typeof deserializedContext === 'object' &&\n deserializedContext\n ) {\n params.context = safeObjectMerge(\n context,\n deserializedContext as Record<string, unknown>,\n )\n }\n } catch (e) {\n // Log warning for debugging but don't expose to client\n if (process.env.NODE_ENV === 'development') {\n console.warn('Failed to parse FormData context:', e)\n }\n }\n }\n\n return await action(params)\n }\n\n // Get requests use the query string\n if (methodUpper === 'GET') {\n // Get payload directly from searchParams\n const payloadParam = url.searchParams.get('payload')\n // Reject oversized payloads to prevent DoS\n if (payloadParam && payloadParam.length > MAX_PAYLOAD_SIZE) {\n throw new Error('Payload too large')\n }\n // If there's a payload, we should try to parse it\n const payload: any = payloadParam\n ? parsePayload(JSON.parse(payloadParam))\n : {}\n payload.context = safeObjectMerge(context, payload.context)\n payload.method = methodUpper\n // Send it through!\n return await action(payload)\n }\n\n let jsonPayload\n if (contentType?.includes('application/json')) {\n jsonPayload = await request.json()\n }\n\n const payload = jsonPayload ? parsePayload(jsonPayload) : {}\n payload.context = safeObjectMerge(payload.context, context)\n payload.method = methodUpper\n return await action(payload)\n })()\n\n const unwrapped = res.result || res.error\n\n if (isNotFound(res)) {\n res = isNotFoundResponse(res)\n }\n\n if (!isServerFn) {\n return unwrapped\n }\n\n if (unwrapped instanceof Response) {\n if (isRedirect(unwrapped)) {\n return unwrapped\n }\n unwrapped.headers.set(X_TSS_RAW_RESPONSE, 'true')\n return unwrapped\n }\n\n return serializeResult(res)\n\n function serializeResult(res: unknown): Response {\n let nonStreamingBody: any = undefined\n\n const alsResponse = getResponse()\n if (res !== undefined) {\n // Collect raw streams encountered during serialization\n const rawStreams = new Map<number, ReadableStream<Uint8Array>>()\n const rawStreamPlugin = createRawStreamRPCPlugin(\n (id: number, stream: ReadableStream<Uint8Array>) => {\n rawStreams.set(id, stream)\n },\n )\n\n // Build plugins with RawStreamRPCPlugin first (before default SSR plugin)\n const plugins = [rawStreamPlugin, ...(serovalPlugins || [])]\n\n // first run without the stream in case `result` does not need streaming\n let done = false as boolean\n const callbacks: {\n onParse: (value: any) => void\n onDone: () => void\n onError: (error: any) => void\n } = {\n onParse: (value) => {\n nonStreamingBody = value\n },\n onDone: () => {\n done = true\n },\n onError: (error) => {\n throw error\n },\n }\n toCrossJSONStream(res, {\n refs: new Map(),\n plugins,\n onParse(value) {\n callbacks.onParse(value)\n },\n onDone() {\n callbacks.onDone()\n },\n onError: (error) => {\n callbacks.onError(error)\n },\n })\n\n // If no raw streams and done synchronously, return simple JSON\n if (done && rawStreams.size === 0) {\n return new Response(\n nonStreamingBody ? JSON.stringify(nonStreamingBody) : undefined,\n {\n status: alsResponse.status,\n statusText: alsResponse.statusText,\n headers: {\n 'Content-Type': 'application/json',\n [X_TSS_SERIALIZED]: 'true',\n },\n },\n )\n }\n\n // If we have raw streams, use framed protocol\n if (rawStreams.size > 0) {\n // Create a stream of JSON chunks (NDJSON style)\n const jsonStream = new ReadableStream<string>({\n start(controller) {\n callbacks.onParse = (value) => {\n controller.enqueue(JSON.stringify(value) + '\\n')\n }\n callbacks.onDone = () => {\n try {\n controller.close()\n } catch {\n // Already closed\n }\n }\n callbacks.onError = (error) => controller.error(error)\n // Emit initial body if we have one\n if (nonStreamingBody !== undefined) {\n callbacks.onParse(nonStreamingBody)\n }\n },\n })\n\n // Create multiplexed stream with JSON and raw streams\n const multiplexedStream = createMultiplexedStream(\n jsonStream,\n rawStreams,\n )\n\n return new Response(multiplexedStream, {\n status: alsResponse.status,\n statusText: alsResponse.statusText,\n headers: {\n 'Content-Type': TSS_CONTENT_TYPE_FRAMED_VERSIONED,\n [X_TSS_SERIALIZED]: 'true',\n },\n })\n }\n\n // No raw streams but not done yet - use standard NDJSON streaming\n const stream = new ReadableStream({\n start(controller) {\n callbacks.onParse = (value) =>\n controller.enqueue(\n textEncoder.encode(JSON.stringify(value) + '\\n'),\n )\n callbacks.onDone = () => {\n try {\n controller.close()\n } catch (error) {\n controller.error(error)\n }\n }\n callbacks.onError = (error) => controller.error(error)\n // stream initial body\n if (nonStreamingBody !== undefined) {\n callbacks.onParse(nonStreamingBody)\n }\n },\n })\n return new Response(stream, {\n status: alsResponse.status,\n statusText: alsResponse.statusText,\n headers: {\n 'Content-Type': 'application/x-ndjson',\n [X_TSS_SERIALIZED]: 'true',\n },\n })\n }\n\n return new Response(undefined, {\n status: alsResponse.status,\n statusText: alsResponse.statusText,\n })\n }\n } catch (error: any) {\n if (error instanceof Response) {\n return error\n }\n // else if (\n // isPlainObject(error) &&\n // 'result' in error &&\n // error.result instanceof Response\n // ) {\n // return error.result\n // }\n\n // Currently this server-side context has no idea how to\n // build final URLs, so we need to defer that to the client.\n // The client will check for __redirect and __notFound keys,\n // and if they exist, it will handle them appropriately.\n\n if (isNotFound(error)) {\n return isNotFoundResponse(error)\n }\n\n console.info()\n console.info('Server Fn Error!')\n console.info()\n console.error(error)\n console.info()\n\n const serializedError = JSON.stringify(\n await Promise.resolve(\n toCrossJSONAsync(error, {\n refs: new Map(),\n plugins: serovalPlugins,\n }),\n ),\n )\n const response = getResponse()\n return new Response(serializedError, {\n status: response.status ?? 500,\n statusText: response.statusText,\n headers: {\n 'Content-Type': 'application/json',\n [X_TSS_SERIALIZED]: 'true',\n },\n })\n }\n })()\n\n return response\n}\n\nfunction isNotFoundResponse(error: any) {\n const { headers, ...rest } = error\n\n return new Response(JSON.stringify(rest), {\n status: 404,\n headers: {\n 'Content-Type': 'application/json',\n ...(headers || {}),\n },\n })\n}\n"],"mappings":"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"}