@benjavicente/start-client-core 1.167.9
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/LICENSE +21 -0
- package/README.md +12 -0
- package/bin/intent.js +25 -0
- package/dist/esm/client/ServerFunctionSerializationAdapter.d.ts +7 -0
- package/dist/esm/client/ServerFunctionSerializationAdapter.js +18 -0
- package/dist/esm/client/ServerFunctionSerializationAdapter.js.map +1 -0
- package/dist/esm/client/hydrateStart.d.ts +2 -0
- package/dist/esm/client/hydrateStart.js +31 -0
- package/dist/esm/client/hydrateStart.js.map +1 -0
- package/dist/esm/client/index.d.ts +2 -0
- package/dist/esm/client/index.js +2 -0
- package/dist/esm/client-rpc/createClientRpc.d.ts +6 -0
- package/dist/esm/client-rpc/createClientRpc.js +21 -0
- package/dist/esm/client-rpc/createClientRpc.js.map +1 -0
- package/dist/esm/client-rpc/frame-decoder.d.ts +23 -0
- package/dist/esm/client-rpc/frame-decoder.js +231 -0
- package/dist/esm/client-rpc/frame-decoder.js.map +1 -0
- package/dist/esm/client-rpc/index.d.ts +1 -0
- package/dist/esm/client-rpc/index.js +2 -0
- package/dist/esm/client-rpc/serverFnFetcher.d.ts +1 -0
- package/dist/esm/client-rpc/serverFnFetcher.js +231 -0
- package/dist/esm/client-rpc/serverFnFetcher.js.map +1 -0
- package/dist/esm/constants.d.ts +53 -0
- package/dist/esm/constants.js +46 -0
- package/dist/esm/constants.js.map +1 -0
- package/dist/esm/createMiddleware.d.ts +195 -0
- package/dist/esm/createMiddleware.js +26 -0
- package/dist/esm/createMiddleware.js.map +1 -0
- package/dist/esm/createServerFn.d.ts +131 -0
- package/dist/esm/createServerFn.js +200 -0
- package/dist/esm/createServerFn.js.map +1 -0
- package/dist/esm/createStart.d.ts +50 -0
- package/dist/esm/createStart.js +29 -0
- package/dist/esm/createStart.js.map +1 -0
- package/dist/esm/fake-start-entry.d.ts +2 -0
- package/dist/esm/fake-start-entry.js +7 -0
- package/dist/esm/fake-start-entry.js.map +1 -0
- package/dist/esm/getDefaultSerovalPlugins.d.ts +1 -0
- package/dist/esm/getDefaultSerovalPlugins.js +10 -0
- package/dist/esm/getDefaultSerovalPlugins.js.map +1 -0
- package/dist/esm/getGlobalStartContext.d.ts +3 -0
- package/dist/esm/getGlobalStartContext.js +12 -0
- package/dist/esm/getGlobalStartContext.js.map +1 -0
- package/dist/esm/getRouterInstance.d.ts +2 -0
- package/dist/esm/getRouterInstance.js +8 -0
- package/dist/esm/getRouterInstance.js.map +1 -0
- package/dist/esm/getStartContextServerOnly.d.ts +2 -0
- package/dist/esm/getStartContextServerOnly.js +8 -0
- package/dist/esm/getStartContextServerOnly.js.map +1 -0
- package/dist/esm/getStartOptions.d.ts +2 -0
- package/dist/esm/getStartOptions.js +8 -0
- package/dist/esm/getStartOptions.js.map +1 -0
- package/dist/esm/global.d.ts +7 -0
- package/dist/esm/index.d.ts +20 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/safeObjectMerge.d.ts +10 -0
- package/dist/esm/safeObjectMerge.js +30 -0
- package/dist/esm/safeObjectMerge.js.map +1 -0
- package/dist/esm/serverRoute.d.ts +65 -0
- package/dist/esm/startEntry.d.ts +8 -0
- package/dist/esm/tests/createServerFn.test-d.d.ts +1 -0
- package/dist/esm/tests/createServerMiddleware.test-d.d.ts +1 -0
- package/package.json +98 -0
- package/skills/start-core/SKILL.md +210 -0
- package/skills/start-core/deployment/SKILL.md +306 -0
- package/skills/start-core/execution-model/SKILL.md +302 -0
- package/skills/start-core/middleware/SKILL.md +365 -0
- package/skills/start-core/server-functions/SKILL.md +335 -0
- package/skills/start-core/server-routes/SKILL.md +280 -0
- package/src/client/ServerFunctionSerializationAdapter.ts +16 -0
- package/src/client/hydrateStart.ts +43 -0
- package/src/client/index.ts +2 -0
- package/src/client-rpc/createClientRpc.ts +20 -0
- package/src/client-rpc/frame-decoder.ts +389 -0
- package/src/client-rpc/index.ts +1 -0
- package/src/client-rpc/serverFnFetcher.ts +416 -0
- package/src/constants.ts +90 -0
- package/src/createMiddleware.ts +824 -0
- package/src/createServerFn.ts +813 -0
- package/src/createStart.ts +166 -0
- package/src/fake-start-entry.ts +2 -0
- package/src/getDefaultSerovalPlugins.ts +17 -0
- package/src/getGlobalStartContext.ts +18 -0
- package/src/getRouterInstance.ts +8 -0
- package/src/getStartContextServerOnly.ts +4 -0
- package/src/getStartOptions.ts +8 -0
- package/src/global.ts +9 -0
- package/src/index.tsx +119 -0
- package/src/safeObjectMerge.ts +38 -0
- package/src/serverRoute.ts +509 -0
- package/src/start-entry.d.ts +11 -0
- package/src/startEntry.ts +10 -0
- package/src/tests/createServerFn.test-d.ts +866 -0
- package/src/tests/createServerMiddleware.test-d.ts +810 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { TSS_CONTENT_TYPE_FRAMED, TSS_FORMDATA_CONTEXT, validateFramedProtocolVersion } from "../constants.js";
|
|
2
|
+
import { getDefaultSerovalPlugins } from "../getDefaultSerovalPlugins.js";
|
|
3
|
+
import { createFrameDecoder } from "./frame-decoder.js";
|
|
4
|
+
import { createRawStreamDeserializePlugin, encode, invariant, isNotFound, parseRedirect } from "@benjavicente/router-core";
|
|
5
|
+
import { fromCrossJSON, toJSONAsync } from "seroval";
|
|
6
|
+
//#region src/client-rpc/serverFnFetcher.ts
|
|
7
|
+
var serovalPlugins = null;
|
|
8
|
+
/**
|
|
9
|
+
* Checks if an object has at least one own enumerable property.
|
|
10
|
+
* More efficient than Object.keys(obj).length > 0 as it short-circuits on first property.
|
|
11
|
+
*/
|
|
12
|
+
var hop = Object.prototype.hasOwnProperty;
|
|
13
|
+
function hasOwnProperties(obj) {
|
|
14
|
+
for (const _ in obj) if (hop.call(obj, _)) return true;
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
async function serverFnFetcher(url, args, handler) {
|
|
18
|
+
if (!serovalPlugins) serovalPlugins = getDefaultSerovalPlugins();
|
|
19
|
+
const first = args[0];
|
|
20
|
+
const fetchImpl = first.fetch ?? handler;
|
|
21
|
+
const type = first.data instanceof FormData ? "formData" : "payload";
|
|
22
|
+
const headers = first.headers ? new Headers(first.headers) : new Headers();
|
|
23
|
+
headers.set("x-tsr-serverFn", "true");
|
|
24
|
+
if (type === "payload") headers.set("accept", `${TSS_CONTENT_TYPE_FRAMED}, application/x-ndjson, application/json`);
|
|
25
|
+
if (first.method === "GET") {
|
|
26
|
+
if (type === "formData") throw new Error("FormData is not supported with GET requests");
|
|
27
|
+
const serializedPayload = await serializePayload(first);
|
|
28
|
+
if (serializedPayload !== void 0) {
|
|
29
|
+
const encodedPayload = encode({ payload: serializedPayload });
|
|
30
|
+
if (url.includes("?")) url += `&${encodedPayload}`;
|
|
31
|
+
else url += `?${encodedPayload}`;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
let body = void 0;
|
|
35
|
+
if (first.method === "POST") {
|
|
36
|
+
const fetchBody = await getFetchBody(first);
|
|
37
|
+
if (fetchBody?.contentType) headers.set("content-type", fetchBody.contentType);
|
|
38
|
+
body = fetchBody?.body;
|
|
39
|
+
}
|
|
40
|
+
return await getResponse(async () => fetchImpl(url, {
|
|
41
|
+
method: first.method,
|
|
42
|
+
headers,
|
|
43
|
+
signal: first.signal,
|
|
44
|
+
body
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
async function serializePayload(opts) {
|
|
48
|
+
let payloadAvailable = false;
|
|
49
|
+
const payloadToSerialize = {};
|
|
50
|
+
if (opts.data !== void 0) {
|
|
51
|
+
payloadAvailable = true;
|
|
52
|
+
payloadToSerialize["data"] = opts.data;
|
|
53
|
+
}
|
|
54
|
+
if (opts.context && hasOwnProperties(opts.context)) {
|
|
55
|
+
payloadAvailable = true;
|
|
56
|
+
payloadToSerialize["context"] = opts.context;
|
|
57
|
+
}
|
|
58
|
+
if (payloadAvailable) return serialize(payloadToSerialize);
|
|
59
|
+
}
|
|
60
|
+
async function serialize(data) {
|
|
61
|
+
return JSON.stringify(await Promise.resolve(toJSONAsync(data, { plugins: serovalPlugins })));
|
|
62
|
+
}
|
|
63
|
+
async function getFetchBody(opts) {
|
|
64
|
+
if (opts.data instanceof FormData) {
|
|
65
|
+
let serializedContext = void 0;
|
|
66
|
+
if (opts.context && hasOwnProperties(opts.context)) serializedContext = await serialize(opts.context);
|
|
67
|
+
if (serializedContext !== void 0) opts.data.set(TSS_FORMDATA_CONTEXT, serializedContext);
|
|
68
|
+
return { body: opts.data };
|
|
69
|
+
}
|
|
70
|
+
const serializedBody = await serializePayload(opts);
|
|
71
|
+
if (serializedBody) return {
|
|
72
|
+
body: serializedBody,
|
|
73
|
+
contentType: "application/json"
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Retrieves a response from a given function and manages potential errors
|
|
78
|
+
* and special response types including redirects and not found errors.
|
|
79
|
+
*
|
|
80
|
+
* @param fn - The function to execute for obtaining the response.
|
|
81
|
+
* @returns The processed response from the function.
|
|
82
|
+
* @throws If the response is invalid or an error occurs during processing.
|
|
83
|
+
*/
|
|
84
|
+
async function getResponse(fn) {
|
|
85
|
+
let response;
|
|
86
|
+
try {
|
|
87
|
+
response = await fn();
|
|
88
|
+
} catch (error) {
|
|
89
|
+
if (error instanceof Response) response = error;
|
|
90
|
+
else {
|
|
91
|
+
console.log(error);
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (response.headers.get("x-tss-raw") === "true") return response;
|
|
96
|
+
const contentType = response.headers.get("content-type");
|
|
97
|
+
if (!contentType) {
|
|
98
|
+
if (process.env.NODE_ENV !== "production") throw new Error("Invariant failed: expected content-type header to be set");
|
|
99
|
+
invariant();
|
|
100
|
+
}
|
|
101
|
+
if (!!response.headers.get("x-tss-serialized")) {
|
|
102
|
+
let result;
|
|
103
|
+
if (contentType.includes("application/x-tss-framed")) {
|
|
104
|
+
validateFramedProtocolVersion(contentType);
|
|
105
|
+
if (!response.body) throw new Error("No response body for framed response");
|
|
106
|
+
const { getOrCreateStream, jsonChunks } = createFrameDecoder(response.body);
|
|
107
|
+
const plugins = [createRawStreamDeserializePlugin(getOrCreateStream), ...serovalPlugins || []];
|
|
108
|
+
const refs = /* @__PURE__ */ new Map();
|
|
109
|
+
result = await processFramedResponse({
|
|
110
|
+
jsonStream: jsonChunks,
|
|
111
|
+
onMessage: (msg) => fromCrossJSON(msg, {
|
|
112
|
+
refs,
|
|
113
|
+
plugins
|
|
114
|
+
}),
|
|
115
|
+
onError(msg, error) {
|
|
116
|
+
console.error(msg, error);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
} else if (contentType.includes("application/x-ndjson")) {
|
|
120
|
+
const refs = /* @__PURE__ */ new Map();
|
|
121
|
+
result = await processServerFnResponse({
|
|
122
|
+
response,
|
|
123
|
+
onMessage: (msg) => fromCrossJSON(msg, {
|
|
124
|
+
refs,
|
|
125
|
+
plugins: serovalPlugins
|
|
126
|
+
}),
|
|
127
|
+
onError(msg, error) {
|
|
128
|
+
console.error(msg, error);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
} else if (contentType.includes("application/json")) result = fromCrossJSON(await response.json(), { plugins: serovalPlugins });
|
|
132
|
+
if (!result) {
|
|
133
|
+
if (process.env.NODE_ENV !== "production") throw new Error("Invariant failed: expected result to be resolved");
|
|
134
|
+
invariant();
|
|
135
|
+
}
|
|
136
|
+
if (result instanceof Error) throw result;
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
if (contentType.includes("application/json")) {
|
|
140
|
+
const jsonPayload = await response.json();
|
|
141
|
+
const redirect = parseRedirect(jsonPayload);
|
|
142
|
+
if (redirect) throw redirect;
|
|
143
|
+
if (isNotFound(jsonPayload)) throw jsonPayload;
|
|
144
|
+
return jsonPayload;
|
|
145
|
+
}
|
|
146
|
+
if (!response.ok) throw new Error(await response.text());
|
|
147
|
+
return response;
|
|
148
|
+
}
|
|
149
|
+
async function processServerFnResponse({ response, onMessage, onError }) {
|
|
150
|
+
if (!response.body) throw new Error("No response body");
|
|
151
|
+
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
|
|
152
|
+
let buffer = "";
|
|
153
|
+
let firstRead = false;
|
|
154
|
+
let firstObject;
|
|
155
|
+
while (!firstRead) {
|
|
156
|
+
const { value, done } = await reader.read();
|
|
157
|
+
if (value) buffer += value;
|
|
158
|
+
if (buffer.length === 0 && done) throw new Error("Stream ended before first object");
|
|
159
|
+
if (buffer.endsWith("\n")) {
|
|
160
|
+
const lines = buffer.split("\n").filter(Boolean);
|
|
161
|
+
const firstLine = lines[0];
|
|
162
|
+
if (!firstLine) throw new Error("No JSON line in the first chunk");
|
|
163
|
+
firstObject = JSON.parse(firstLine);
|
|
164
|
+
firstRead = true;
|
|
165
|
+
buffer = lines.slice(1).join("\n");
|
|
166
|
+
} else {
|
|
167
|
+
const newlineIndex = buffer.indexOf("\n");
|
|
168
|
+
if (newlineIndex >= 0) {
|
|
169
|
+
const line = buffer.slice(0, newlineIndex).trim();
|
|
170
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
171
|
+
if (line.length > 0) {
|
|
172
|
+
firstObject = JSON.parse(line);
|
|
173
|
+
firstRead = true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
(async () => {
|
|
179
|
+
try {
|
|
180
|
+
while (true) {
|
|
181
|
+
const { value, done } = await reader.read();
|
|
182
|
+
if (value) buffer += value;
|
|
183
|
+
const lastNewline = buffer.lastIndexOf("\n");
|
|
184
|
+
if (lastNewline >= 0) {
|
|
185
|
+
const chunk = buffer.slice(0, lastNewline);
|
|
186
|
+
buffer = buffer.slice(lastNewline + 1);
|
|
187
|
+
const lines = chunk.split("\n").filter(Boolean);
|
|
188
|
+
for (const line of lines) try {
|
|
189
|
+
onMessage(JSON.parse(line));
|
|
190
|
+
} catch (e) {
|
|
191
|
+
onError?.(`Invalid JSON line: ${line}`, e);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (done) break;
|
|
195
|
+
}
|
|
196
|
+
} catch (err) {
|
|
197
|
+
onError?.("Stream processing error:", err);
|
|
198
|
+
}
|
|
199
|
+
})();
|
|
200
|
+
return onMessage(firstObject);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Processes a framed response where each JSON chunk is a complete JSON string
|
|
204
|
+
* (already decoded by frame decoder).
|
|
205
|
+
*/
|
|
206
|
+
async function processFramedResponse({ jsonStream, onMessage, onError }) {
|
|
207
|
+
const reader = jsonStream.getReader();
|
|
208
|
+
const { value: firstValue, done: firstDone } = await reader.read();
|
|
209
|
+
if (firstDone || !firstValue) throw new Error("Stream ended before first object");
|
|
210
|
+
const firstObject = JSON.parse(firstValue);
|
|
211
|
+
(async () => {
|
|
212
|
+
try {
|
|
213
|
+
while (true) {
|
|
214
|
+
const { value, done } = await reader.read();
|
|
215
|
+
if (done) break;
|
|
216
|
+
if (value) try {
|
|
217
|
+
onMessage(JSON.parse(value));
|
|
218
|
+
} catch (e) {
|
|
219
|
+
onError?.(`Invalid JSON: ${value}`, e);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
} catch (err) {
|
|
223
|
+
onError?.("Stream processing error:", err);
|
|
224
|
+
}
|
|
225
|
+
})();
|
|
226
|
+
return onMessage(firstObject);
|
|
227
|
+
}
|
|
228
|
+
//#endregion
|
|
229
|
+
export { serverFnFetcher };
|
|
230
|
+
|
|
231
|
+
//# sourceMappingURL=serverFnFetcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serverFnFetcher.js","names":[],"sources":["../../../src/client-rpc/serverFnFetcher.ts"],"sourcesContent":["import {\n createRawStreamDeserializePlugin,\n encode,\n invariant,\n isNotFound,\n parseRedirect,\n} from '@benjavicente/router-core'\nimport { fromCrossJSON, toJSONAsync } from 'seroval'\nimport { getDefaultSerovalPlugins } from '../getDefaultSerovalPlugins'\nimport {\n TSS_CONTENT_TYPE_FRAMED,\n TSS_FORMDATA_CONTEXT,\n X_TSS_RAW_RESPONSE,\n X_TSS_SERIALIZED,\n validateFramedProtocolVersion,\n} from '../constants'\nimport { createFrameDecoder } from './frame-decoder'\nimport type { FunctionMiddlewareClientFnOptions } from '../createMiddleware'\nimport type { Plugin as SerovalPlugin } from 'seroval'\n\nlet serovalPlugins: Array<SerovalPlugin<any, any>> | null = null\n\n/**\n * Checks if an object has at least one own enumerable property.\n * More efficient than Object.keys(obj).length > 0 as it short-circuits on first property.\n */\nconst hop = Object.prototype.hasOwnProperty\nfunction hasOwnProperties(obj: object): boolean {\n for (const _ in obj) {\n if (hop.call(obj, _)) {\n return true\n }\n }\n return false\n}\n// caller =>\n// serverFnFetcher =>\n// client =>\n// server =>\n// fn =>\n// seroval =>\n// client middleware =>\n// serverFnFetcher =>\n// caller\n\nexport async function serverFnFetcher(\n url: string,\n args: Array<any>,\n handler: (url: string, requestInit: RequestInit) => Promise<Response>,\n) {\n if (!serovalPlugins) {\n serovalPlugins = getDefaultSerovalPlugins()\n }\n const _first = args[0]\n\n const first = _first as FunctionMiddlewareClientFnOptions<any, any, any> & {\n headers?: HeadersInit\n }\n\n // Use custom fetch if provided, otherwise fall back to the passed handler (global fetch)\n const fetchImpl = first.fetch ?? handler\n\n const type = first.data instanceof FormData ? 'formData' : 'payload'\n\n // Arrange the headers\n const headers = first.headers ? new Headers(first.headers) : new Headers()\n headers.set('x-tsr-serverFn', 'true')\n\n if (type === 'payload') {\n headers.set(\n 'accept',\n `${TSS_CONTENT_TYPE_FRAMED}, application/x-ndjson, application/json`,\n )\n }\n\n // If the method is GET, we need to move the payload to the query string\n if (first.method === 'GET') {\n if (type === 'formData') {\n throw new Error('FormData is not supported with GET requests')\n }\n const serializedPayload = await serializePayload(first)\n if (serializedPayload !== undefined) {\n const encodedPayload = encode({\n payload: serializedPayload,\n })\n if (url.includes('?')) {\n url += `&${encodedPayload}`\n } else {\n url += `?${encodedPayload}`\n }\n }\n }\n\n let body = undefined\n if (first.method === 'POST') {\n const fetchBody = await getFetchBody(first)\n if (fetchBody?.contentType) {\n headers.set('content-type', fetchBody.contentType)\n }\n body = fetchBody?.body\n }\n\n return await getResponse(async () =>\n fetchImpl(url, {\n method: first.method,\n headers,\n signal: first.signal,\n body,\n }),\n )\n}\n\nasync function serializePayload(\n opts: FunctionMiddlewareClientFnOptions<any, any, any>,\n): Promise<string | undefined> {\n let payloadAvailable = false\n const payloadToSerialize: any = {}\n if (opts.data !== undefined) {\n payloadAvailable = true\n payloadToSerialize['data'] = opts.data\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (opts.context && hasOwnProperties(opts.context)) {\n payloadAvailable = true\n payloadToSerialize['context'] = opts.context\n }\n\n if (payloadAvailable) {\n return serialize(payloadToSerialize)\n }\n return undefined\n}\n\nasync function serialize(data: any) {\n return JSON.stringify(\n await Promise.resolve(toJSONAsync(data, { plugins: serovalPlugins! })),\n )\n}\n\nasync function getFetchBody(\n opts: FunctionMiddlewareClientFnOptions<any, any, any>,\n): Promise<{ body: FormData | string; contentType?: string } | undefined> {\n if (opts.data instanceof FormData) {\n let serializedContext = undefined\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (opts.context && hasOwnProperties(opts.context)) {\n serializedContext = await serialize(opts.context)\n }\n if (serializedContext !== undefined) {\n opts.data.set(TSS_FORMDATA_CONTEXT, serializedContext)\n }\n return { body: opts.data }\n }\n const serializedBody = await serializePayload(opts)\n if (serializedBody) {\n return { body: serializedBody, contentType: 'application/json' }\n }\n return undefined\n}\n\n/**\n * Retrieves a response from a given function and manages potential errors\n * and special response types including redirects and not found errors.\n *\n * @param fn - The function to execute for obtaining the response.\n * @returns The processed response from the function.\n * @throws If the response is invalid or an error occurs during processing.\n */\nasync function getResponse(fn: () => Promise<Response>) {\n let response: Response\n try {\n response = await fn() // client => server => fn => server => client\n } catch (error) {\n if (error instanceof Response) {\n response = error\n } else {\n console.log(error)\n throw error\n }\n }\n\n if (response.headers.get(X_TSS_RAW_RESPONSE) === 'true') {\n return response\n }\n\n const contentType = response.headers.get('content-type')\n if (!contentType) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n 'Invariant failed: expected content-type header to be set',\n )\n }\n\n invariant()\n }\n const serializedByStart = !!response.headers.get(X_TSS_SERIALIZED)\n\n // If the response is serialized by the start server, we need to process it\n // differently than a normal response.\n if (serializedByStart) {\n let result\n\n // If it's a framed response (contains RawStream), use frame decoder\n if (contentType.includes(TSS_CONTENT_TYPE_FRAMED)) {\n // Validate protocol version compatibility\n validateFramedProtocolVersion(contentType)\n\n if (!response.body) {\n throw new Error('No response body for framed response')\n }\n\n const { getOrCreateStream, jsonChunks } = createFrameDecoder(\n response.body,\n )\n\n // Create deserialize plugin that wires up the raw streams\n const rawStreamPlugin =\n createRawStreamDeserializePlugin(getOrCreateStream)\n const plugins = [rawStreamPlugin, ...(serovalPlugins || [])]\n\n const refs = new Map()\n result = await processFramedResponse({\n jsonStream: jsonChunks,\n onMessage: (msg: any) => fromCrossJSON(msg, { refs, plugins }),\n onError(msg, error) {\n console.error(msg, error)\n },\n })\n }\n // If it's a stream from the start serializer, process it as such\n else if (contentType.includes('application/x-ndjson')) {\n const refs = new Map()\n result = await processServerFnResponse({\n response,\n onMessage: (msg) =>\n fromCrossJSON(msg, { refs, plugins: serovalPlugins! }),\n onError(msg, error) {\n // TODO how could we notify consumer that an error occurred?\n console.error(msg, error)\n },\n })\n }\n // If it's a JSON response, it can be simpler\n else if (contentType.includes('application/json')) {\n const jsonPayload = await response.json()\n result = fromCrossJSON(jsonPayload, { plugins: serovalPlugins! })\n }\n\n if (!result) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error('Invariant failed: expected result to be resolved')\n }\n\n invariant()\n }\n if (result instanceof Error) {\n throw result\n }\n\n return result\n }\n\n // If it wasn't processed by the start serializer, check\n // if it's JSON\n if (contentType.includes('application/json')) {\n const jsonPayload = await response.json()\n const redirect = parseRedirect(jsonPayload)\n if (redirect) {\n throw redirect\n }\n if (isNotFound(jsonPayload)) {\n throw jsonPayload\n }\n return jsonPayload\n }\n\n // Otherwise, if it's not OK, throw the content\n if (!response.ok) {\n throw new Error(await response.text())\n }\n\n // Or return the response itself\n return response\n}\n\nasync function processServerFnResponse({\n response,\n onMessage,\n onError,\n}: {\n response: Response\n onMessage: (msg: any) => any\n onError?: (msg: string, error?: any) => void\n}) {\n if (!response.body) {\n throw new Error('No response body')\n }\n\n const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()\n\n let buffer = ''\n let firstRead = false\n let firstObject\n\n while (!firstRead) {\n const { value, done } = await reader.read()\n if (value) buffer += value\n\n if (buffer.length === 0 && done) {\n throw new Error('Stream ended before first object')\n }\n\n // common case: buffer ends with newline\n if (buffer.endsWith('\\n')) {\n const lines = buffer.split('\\n').filter(Boolean)\n const firstLine = lines[0]\n if (!firstLine) throw new Error('No JSON line in the first chunk')\n firstObject = JSON.parse(firstLine)\n firstRead = true\n buffer = lines.slice(1).join('\\n')\n } else {\n // fallback: wait for a newline to parse first object safely\n const newlineIndex = buffer.indexOf('\\n')\n if (newlineIndex >= 0) {\n const line = buffer.slice(0, newlineIndex).trim()\n buffer = buffer.slice(newlineIndex + 1)\n if (line.length > 0) {\n firstObject = JSON.parse(line)\n firstRead = true\n }\n }\n }\n }\n\n // process rest of the stream asynchronously\n ;(async () => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n while (true) {\n const { value, done } = await reader.read()\n if (value) buffer += value\n\n const lastNewline = buffer.lastIndexOf('\\n')\n if (lastNewline >= 0) {\n const chunk = buffer.slice(0, lastNewline)\n buffer = buffer.slice(lastNewline + 1)\n const lines = chunk.split('\\n').filter(Boolean)\n\n for (const line of lines) {\n try {\n onMessage(JSON.parse(line))\n } catch (e) {\n onError?.(`Invalid JSON line: ${line}`, e)\n }\n }\n }\n\n if (done) {\n break\n }\n }\n } catch (err) {\n onError?.('Stream processing error:', err)\n }\n })()\n\n return onMessage(firstObject)\n}\n\n/**\n * Processes a framed response where each JSON chunk is a complete JSON string\n * (already decoded by frame decoder).\n */\nasync function processFramedResponse({\n jsonStream,\n onMessage,\n onError,\n}: {\n jsonStream: ReadableStream<string>\n onMessage: (msg: any) => any\n onError?: (msg: string, error?: any) => void\n}) {\n const reader = jsonStream.getReader()\n\n // Read first JSON frame - this is the main result\n const { value: firstValue, done: firstDone } = await reader.read()\n if (firstDone || !firstValue) {\n throw new Error('Stream ended before first object')\n }\n\n // Each frame is a complete JSON string\n const firstObject = JSON.parse(firstValue)\n\n // Process remaining frames asynchronously (for streaming refs like RawStream)\n ;(async () => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n while (true) {\n const { value, done } = await reader.read()\n if (done) break\n if (value) {\n try {\n onMessage(JSON.parse(value))\n } catch (e) {\n onError?.(`Invalid JSON: ${value}`, e)\n }\n }\n }\n } catch (err) {\n onError?.('Stream processing error:', err)\n }\n })()\n\n return onMessage(firstObject)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export declare const TSS_FORMDATA_CONTEXT = "__TSS_CONTEXT";
|
|
2
|
+
export declare const TSS_SERVER_FUNCTION: unique symbol;
|
|
3
|
+
export declare const TSS_SERVER_FUNCTION_FACTORY: unique symbol;
|
|
4
|
+
export declare const X_TSS_SERIALIZED = "x-tss-serialized";
|
|
5
|
+
export declare const X_TSS_RAW_RESPONSE = "x-tss-raw";
|
|
6
|
+
export declare const X_TSS_CONTEXT = "x-tss-context";
|
|
7
|
+
/** Content-Type for multiplexed framed responses (RawStream support) */
|
|
8
|
+
export declare const TSS_CONTENT_TYPE_FRAMED = "application/x-tss-framed";
|
|
9
|
+
/**
|
|
10
|
+
* Frame types for binary multiplexing protocol.
|
|
11
|
+
*/
|
|
12
|
+
export declare const FrameType: {
|
|
13
|
+
/** Seroval JSON chunk (NDJSON line) */
|
|
14
|
+
readonly JSON: 0;
|
|
15
|
+
/** Raw stream data chunk */
|
|
16
|
+
readonly CHUNK: 1;
|
|
17
|
+
/** Raw stream end (EOF) */
|
|
18
|
+
readonly END: 2;
|
|
19
|
+
/** Raw stream error */
|
|
20
|
+
readonly ERROR: 3;
|
|
21
|
+
};
|
|
22
|
+
export type FrameType = (typeof FrameType)[keyof typeof FrameType];
|
|
23
|
+
/** Header size in bytes: type(1) + streamId(4) + length(4) */
|
|
24
|
+
export declare const FRAME_HEADER_SIZE = 9;
|
|
25
|
+
/** Current protocol version for framed responses */
|
|
26
|
+
export declare const TSS_FRAMED_PROTOCOL_VERSION = 1;
|
|
27
|
+
/** Full Content-Type header value with version parameter */
|
|
28
|
+
export declare const TSS_CONTENT_TYPE_FRAMED_VERSIONED = "application/x-tss-framed; v=1";
|
|
29
|
+
export declare function parseFramedProtocolVersion(contentType: string): number | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Validates that the server's protocol version is compatible with this client.
|
|
32
|
+
* Throws an error if versions are incompatible.
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateFramedProtocolVersion(contentType: string): void;
|
|
35
|
+
/**
|
|
36
|
+
* Minimal metadata about a server function, available to client middleware.
|
|
37
|
+
* Only contains the function ID since name/filename may expose server internals.
|
|
38
|
+
*/
|
|
39
|
+
export interface ClientFnMeta {
|
|
40
|
+
/** The unique identifier for this server function */
|
|
41
|
+
id: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Full metadata about a server function, available to server middleware and server functions.
|
|
45
|
+
* This information is embedded at compile time by the TanStack Start compiler.
|
|
46
|
+
*/
|
|
47
|
+
export interface ServerFnMeta extends ClientFnMeta {
|
|
48
|
+
/** The original variable name of the server function (e.g., "myServerFn") */
|
|
49
|
+
name: string;
|
|
50
|
+
/** The source file path relative to the project root (e.g., "src/routes/api.ts") */
|
|
51
|
+
filename: string;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//#region src/constants.ts
|
|
2
|
+
var TSS_FORMDATA_CONTEXT = "__TSS_CONTEXT";
|
|
3
|
+
var TSS_SERVER_FUNCTION = Symbol.for("TSS_SERVER_FUNCTION");
|
|
4
|
+
var TSS_SERVER_FUNCTION_FACTORY = Symbol.for("TSS_SERVER_FUNCTION_FACTORY");
|
|
5
|
+
var X_TSS_SERIALIZED = "x-tss-serialized";
|
|
6
|
+
var X_TSS_RAW_RESPONSE = "x-tss-raw";
|
|
7
|
+
var X_TSS_CONTEXT = "x-tss-context";
|
|
8
|
+
/** Content-Type for multiplexed framed responses (RawStream support) */
|
|
9
|
+
var TSS_CONTENT_TYPE_FRAMED = "application/x-tss-framed";
|
|
10
|
+
/**
|
|
11
|
+
* Frame types for binary multiplexing protocol.
|
|
12
|
+
*/
|
|
13
|
+
var FrameType = {
|
|
14
|
+
JSON: 0,
|
|
15
|
+
CHUNK: 1,
|
|
16
|
+
END: 2,
|
|
17
|
+
ERROR: 3
|
|
18
|
+
};
|
|
19
|
+
/** Header size in bytes: type(1) + streamId(4) + length(4) */
|
|
20
|
+
var FRAME_HEADER_SIZE = 9;
|
|
21
|
+
/** Current protocol version for framed responses */
|
|
22
|
+
var TSS_FRAMED_PROTOCOL_VERSION = 1;
|
|
23
|
+
/** Full Content-Type header value with version parameter */
|
|
24
|
+
var TSS_CONTENT_TYPE_FRAMED_VERSIONED = `${TSS_CONTENT_TYPE_FRAMED}; v=1`;
|
|
25
|
+
/**
|
|
26
|
+
* Parses the version parameter from a framed Content-Type header.
|
|
27
|
+
* Returns undefined if no version parameter is present.
|
|
28
|
+
*/
|
|
29
|
+
var FRAMED_VERSION_REGEX = /;\s*v=(\d+)/;
|
|
30
|
+
function parseFramedProtocolVersion(contentType) {
|
|
31
|
+
const match = contentType.match(FRAMED_VERSION_REGEX);
|
|
32
|
+
return match ? parseInt(match[1], 10) : void 0;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Validates that the server's protocol version is compatible with this client.
|
|
36
|
+
* Throws an error if versions are incompatible.
|
|
37
|
+
*/
|
|
38
|
+
function validateFramedProtocolVersion(contentType) {
|
|
39
|
+
const serverVersion = parseFramedProtocolVersion(contentType);
|
|
40
|
+
if (serverVersion === void 0) return;
|
|
41
|
+
if (serverVersion !== 1) throw new Error(`Incompatible framed protocol version: server=${serverVersion}, client=1. Please ensure client and server are using compatible versions.`);
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { FRAME_HEADER_SIZE, FrameType, TSS_CONTENT_TYPE_FRAMED, TSS_CONTENT_TYPE_FRAMED_VERSIONED, TSS_FORMDATA_CONTEXT, TSS_FRAMED_PROTOCOL_VERSION, TSS_SERVER_FUNCTION, TSS_SERVER_FUNCTION_FACTORY, X_TSS_CONTEXT, X_TSS_RAW_RESPONSE, X_TSS_SERIALIZED, validateFramedProtocolVersion };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","names":[],"sources":["../../src/constants.ts"],"sourcesContent":["export const TSS_FORMDATA_CONTEXT = '__TSS_CONTEXT'\nexport const TSS_SERVER_FUNCTION = Symbol.for('TSS_SERVER_FUNCTION')\nexport const TSS_SERVER_FUNCTION_FACTORY = Symbol.for(\n 'TSS_SERVER_FUNCTION_FACTORY',\n)\n\nexport const X_TSS_SERIALIZED = 'x-tss-serialized'\nexport const X_TSS_RAW_RESPONSE = 'x-tss-raw'\nexport const X_TSS_CONTEXT = 'x-tss-context'\n\n/** Content-Type for multiplexed framed responses (RawStream support) */\nexport const TSS_CONTENT_TYPE_FRAMED = 'application/x-tss-framed'\n\n/**\n * Frame types for binary multiplexing protocol.\n */\nexport const FrameType = {\n /** Seroval JSON chunk (NDJSON line) */\n JSON: 0,\n /** Raw stream data chunk */\n CHUNK: 1,\n /** Raw stream end (EOF) */\n END: 2,\n /** Raw stream error */\n ERROR: 3,\n} as const\n\nexport type FrameType = (typeof FrameType)[keyof typeof FrameType]\n\n/** Header size in bytes: type(1) + streamId(4) + length(4) */\nexport const FRAME_HEADER_SIZE = 9\n\n/** Current protocol version for framed responses */\nexport const TSS_FRAMED_PROTOCOL_VERSION = 1\n\n/** Full Content-Type header value with version parameter */\nexport const TSS_CONTENT_TYPE_FRAMED_VERSIONED = `${TSS_CONTENT_TYPE_FRAMED}; v=${TSS_FRAMED_PROTOCOL_VERSION}`\n\n/**\n * Parses the version parameter from a framed Content-Type header.\n * Returns undefined if no version parameter is present.\n */\nconst FRAMED_VERSION_REGEX = /;\\s*v=(\\d+)/\nexport function parseFramedProtocolVersion(\n contentType: string,\n): number | undefined {\n // Match \"v=<number>\" in the content-type parameters\n const match = contentType.match(FRAMED_VERSION_REGEX)\n return match ? parseInt(match[1]!, 10) : undefined\n}\n\n/**\n * Validates that the server's protocol version is compatible with this client.\n * Throws an error if versions are incompatible.\n */\nexport function validateFramedProtocolVersion(contentType: string): void {\n const serverVersion = parseFramedProtocolVersion(contentType)\n if (serverVersion === undefined) {\n // No version specified - assume compatible (backwards compat)\n return\n }\n if (serverVersion !== TSS_FRAMED_PROTOCOL_VERSION) {\n throw new Error(\n `Incompatible framed protocol version: server=${serverVersion}, client=${TSS_FRAMED_PROTOCOL_VERSION}. ` +\n `Please ensure client and server are using compatible versions.`,\n )\n }\n}\n\n/**\n * Minimal metadata about a server function, available to client middleware.\n * Only contains the function ID since name/filename may expose server internals.\n */\nexport interface ClientFnMeta {\n /** The unique identifier for this server function */\n id: string\n}\n\n/**\n * Full metadata about a server function, available to server middleware and server functions.\n * This information is embedded at compile time by the TanStack Start compiler.\n */\nexport interface ServerFnMeta extends ClientFnMeta {\n /** The original variable name of the server function (e.g., \"myServerFn\") */\n name: string\n /** The source file path relative to the project root (e.g., \"src/routes/api.ts\") */\n filename: string\n}\n\nexport {}\n"],"mappings":";AAAA,IAAa,uBAAuB;AACpC,IAAa,sBAAsB,OAAO,IAAI,sBAAsB;AACpE,IAAa,8BAA8B,OAAO,IAChD,8BACD;AAED,IAAa,mBAAmB;AAChC,IAAa,qBAAqB;AAClC,IAAa,gBAAgB;;AAG7B,IAAa,0BAA0B;;;;AAKvC,IAAa,YAAY;CAEvB,MAAM;CAEN,OAAO;CAEP,KAAK;CAEL,OAAO;CACR;;AAKD,IAAa,oBAAoB;;AAGjC,IAAa,8BAA8B;;AAG3C,IAAa,oCAAoC,GAAG,wBAAwB;;;;;AAM5E,IAAM,uBAAuB;AAC7B,SAAgB,2BACd,aACoB;CAEpB,MAAM,QAAQ,YAAY,MAAM,qBAAqB;AACrD,QAAO,QAAQ,SAAS,MAAM,IAAK,GAAG,GAAG,KAAA;;;;;;AAO3C,SAAgB,8BAA8B,aAA2B;CACvE,MAAM,gBAAgB,2BAA2B,YAAY;AAC7D,KAAI,kBAAkB,KAAA,EAEpB;AAEF,KAAI,kBAAA,EACF,OAAM,IAAI,MACR,gDAAgD,cAAc,4EAE/D"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { StartInstanceOptions } from './createStart.js';
|
|
2
|
+
import { AnyServerFn, ConstrainValidator, CustomFetch, Method } from './createServerFn.js';
|
|
3
|
+
import { ClientFnMeta, ServerFnMeta } from './constants.js';
|
|
4
|
+
import { AnyContext, Assign, Constrain, Expand, IntersectAssign, Register, ResolveValidatorInput, ResolveValidatorOutput, ValidateSerializableInput } from '@benjavicente/router-core';
|
|
5
|
+
export type CreateMiddlewareFn<TRegister> = <TType extends MiddlewareType>(options?: {
|
|
6
|
+
type?: TType;
|
|
7
|
+
}, __opts?: FunctionMiddlewareOptions<TRegister, unknown, undefined, undefined, undefined>) => CreateMiddlewareResult<TRegister, TType>;
|
|
8
|
+
export declare const createMiddleware: CreateMiddlewareFn<{}>;
|
|
9
|
+
export type MiddlewareType = 'request' | 'function';
|
|
10
|
+
export type CreateMiddlewareResult<TRegister, TType extends MiddlewareType> = 'request' extends TType ? RequestMiddleware<TRegister> : FunctionMiddleware<TRegister>;
|
|
11
|
+
export interface FunctionMiddleware<TRegister> extends FunctionMiddlewareAfterMiddleware<TRegister, unknown> {
|
|
12
|
+
middleware: <const TNewMiddlewares = undefined>(middlewares: Constrain<TNewMiddlewares, ReadonlyArray<AnyRequestMiddleware | AnyFunctionMiddleware>>) => FunctionMiddlewareAfterMiddleware<TRegister, TNewMiddlewares>;
|
|
13
|
+
}
|
|
14
|
+
export interface FunctionMiddlewareAfterMiddleware<TRegister, TMiddlewares> extends FunctionMiddlewareWithTypes<TRegister, TMiddlewares, undefined, undefined, undefined, undefined, undefined>, FunctionMiddlewareServer<TRegister, TMiddlewares, undefined, undefined, undefined>, FunctionMiddlewareClient<TRegister, TMiddlewares, undefined>, FunctionMiddlewareValidator<TRegister, TMiddlewares> {
|
|
15
|
+
}
|
|
16
|
+
export interface FunctionMiddlewareWithTypes<TRegister, TMiddlewares, TInputValidator, TServerContext, TServerSendContext, TClientContext, TClientSendContext> {
|
|
17
|
+
'~types': FunctionMiddlewareTypes<TRegister, TMiddlewares, TInputValidator, TServerContext, TServerSendContext, TClientContext, TClientSendContext>;
|
|
18
|
+
options: FunctionMiddlewareOptions<TRegister, TMiddlewares, TInputValidator, TServerContext, TClientContext>;
|
|
19
|
+
}
|
|
20
|
+
export interface FunctionMiddlewareTypes<in out TRegister, in out TMiddlewares, in out TInputValidator, in out TServerContext, in out TServerSendContext, in out TClientContext, in out TClientSendContext> {
|
|
21
|
+
type: 'function';
|
|
22
|
+
middlewares: TMiddlewares;
|
|
23
|
+
input: ResolveValidatorInput<TInputValidator>;
|
|
24
|
+
allInput: IntersectAllValidatorInputs<TMiddlewares, TInputValidator>;
|
|
25
|
+
output: ResolveValidatorOutput<TInputValidator>;
|
|
26
|
+
allOutput: IntersectAllValidatorOutputs<TMiddlewares, TInputValidator>;
|
|
27
|
+
clientContext: TClientContext;
|
|
28
|
+
allClientContextBeforeNext: AssignAllClientContextBeforeNext<TMiddlewares, TClientContext>;
|
|
29
|
+
allClientContextAfterNext: AssignAllClientContextAfterNext<TMiddlewares, TClientContext, TClientSendContext>;
|
|
30
|
+
serverContext: TServerContext;
|
|
31
|
+
serverSendContext: TServerSendContext;
|
|
32
|
+
allServerSendContext: AssignAllServerSendContext<TMiddlewares, TServerSendContext>;
|
|
33
|
+
allServerContext: AssignAllServerFnContext<TRegister, TMiddlewares, TServerSendContext, TServerContext>;
|
|
34
|
+
clientSendContext: TClientSendContext;
|
|
35
|
+
allClientSendContext: AssignAllClientSendContext<TMiddlewares, TClientSendContext>;
|
|
36
|
+
inputValidator: TInputValidator;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Recursively resolve the input type produced by a sequence of middleware
|
|
40
|
+
*/
|
|
41
|
+
export type IntersectAllValidatorInputs<TMiddlewares, TInputValidator> = unknown extends TInputValidator ? TInputValidator : TInputValidator extends undefined ? IntersectAllMiddleware<TMiddlewares, 'allInput'> : IntersectAssign<IntersectAllMiddleware<TMiddlewares, 'allInput'>, ResolveValidatorInput<TInputValidator>>;
|
|
42
|
+
export type IntersectAllMiddleware<TMiddlewares, TType extends keyof AnyFunctionMiddleware['~types'] | keyof AnyRequestMiddleware['~types'] | keyof AnyServerFn['~types'], TAcc = undefined> = TMiddlewares extends readonly [infer TMiddleware, ...infer TRest] ? TMiddleware extends AnyFunctionMiddleware | AnyRequestMiddleware | AnyServerFn ? IntersectAllMiddleware<TRest, TType, IntersectAssign<TAcc, TMiddleware['~types'][TType & keyof TMiddleware['~types']]>> : TAcc : TAcc;
|
|
43
|
+
export type AnyFunctionMiddleware = FunctionMiddlewareWithTypes<any, any, any, any, any, any, any>;
|
|
44
|
+
/**
|
|
45
|
+
* Recursively merge the output type produced by a sequence of middleware
|
|
46
|
+
*/
|
|
47
|
+
export type IntersectAllValidatorOutputs<TMiddlewares, TInputValidator> = unknown extends TInputValidator ? TInputValidator : TInputValidator extends undefined ? IntersectAllMiddleware<TMiddlewares, 'allOutput'> : IntersectAssign<IntersectAllMiddleware<TMiddlewares, 'allOutput'>, Awaited<ResolveValidatorOutput<TInputValidator>>>;
|
|
48
|
+
/**
|
|
49
|
+
* Recursively resolve the client context type produced by a sequence of middleware
|
|
50
|
+
*/
|
|
51
|
+
export type AssignAllClientContextBeforeNext<TMiddlewares, TClientContext = undefined> = unknown extends TClientContext ? TClientContext : Assign<AssignAllMiddleware<TMiddlewares, 'allClientContextBeforeNext'>, TClientContext>;
|
|
52
|
+
export type AssignAllMiddleware<TMiddlewares, TType extends keyof AnyFunctionMiddleware['~types'] | keyof AnyRequestMiddleware['~types'] | keyof AnyServerFn['~types'], TAcc = undefined> = TMiddlewares extends readonly [infer TMiddleware, ...infer TRest] ? TMiddleware extends AnyFunctionMiddleware | AnyRequestMiddleware | AnyServerFn ? AssignAllMiddleware<TRest, TType, Assign<TAcc, TMiddleware['~types'][TType & keyof TMiddleware['~types']]>> : TAcc : TAcc;
|
|
53
|
+
export type AssignAllClientContextAfterNext<TMiddlewares, TClientContext = undefined, TSendContext = undefined> = unknown extends TClientContext ? Assign<TClientContext, TSendContext> : Assign<AssignAllMiddleware<TMiddlewares, 'allClientContextAfterNext'>, Assign<TClientContext, TSendContext>>;
|
|
54
|
+
export type AssignAllServerSendContext<TMiddlewares, TSendContext = undefined> = unknown extends TSendContext ? TSendContext : Assign<AssignAllMiddleware<TMiddlewares, 'allServerSendContext'>, TSendContext>;
|
|
55
|
+
export type AssignAllServerRequestContext<TRegister, TMiddlewares, TSendContext = undefined, TServerContext = undefined> = Assign<GlobalFetchRequestContext, Assign<GlobalServerRequestContext<TRegister>, __AssignAllServerRequestContext<TMiddlewares, TSendContext, TServerContext>>>;
|
|
56
|
+
export type GlobalFetchRequestContext = Register extends {
|
|
57
|
+
server: {
|
|
58
|
+
requestContext: infer TRequestContext;
|
|
59
|
+
};
|
|
60
|
+
} ? TRequestContext : AnyContext;
|
|
61
|
+
export type GlobalServerRequestContext<TRegister> = TRegister extends {
|
|
62
|
+
config: StartInstanceOptions<any, any, infer TRequestMiddlewares, any>;
|
|
63
|
+
} ? AssignAllMiddleware<TRequestMiddlewares, 'allServerContext'> : AnyContext;
|
|
64
|
+
type __AssignAllServerRequestContext<TMiddlewares, TSendContext = undefined, TServerContext = undefined> = unknown extends TSendContext ? Assign<TSendContext, TServerContext> : Assign<AssignAllMiddleware<TMiddlewares, 'allServerContext'>, Assign<TSendContext, TServerContext>>;
|
|
65
|
+
export type AssignAllServerFnContext<TRegister, TMiddlewares, TSendContext = undefined, TServerContext = undefined> = Assign<GlobalFetchRequestContext, Assign<GlobalServerRequestContext<TRegister>, // TODO: This enabled global middleware
|
|
66
|
+
Assign<GlobalServerFnContext<TRegister>, // TODO: This enabled global middleware
|
|
67
|
+
__AssignAllServerFnContext<TMiddlewares, TSendContext, TServerContext>>>>;
|
|
68
|
+
type GlobalServerFnContext<TRegister> = TRegister extends {
|
|
69
|
+
config: StartInstanceOptions<any, any, any, infer TFunctionMiddlewares>;
|
|
70
|
+
} ? AssignAllMiddleware<TFunctionMiddlewares, 'allServerContext'> : AnyContext;
|
|
71
|
+
type __AssignAllServerFnContext<TMiddlewares, TSendContext = undefined, TServerContext = undefined> = unknown extends TSendContext ? Assign<TSendContext, TServerContext> : Assign<AssignAllMiddleware<TMiddlewares, 'allServerContext'>, Assign<TSendContext, TServerContext>>;
|
|
72
|
+
export type AssignAllClientSendContext<TMiddlewares, TSendContext = undefined> = unknown extends TSendContext ? TSendContext : Assign<AssignAllMiddleware<TMiddlewares, 'allClientSendContext'>, TSendContext>;
|
|
73
|
+
export interface FunctionMiddlewareOptions<in out TRegister, in out TMiddlewares, in out TInputValidator, in out TServerContext, in out TClientContext> {
|
|
74
|
+
middleware?: TMiddlewares;
|
|
75
|
+
inputValidator?: ConstrainValidator<TRegister, 'GET', TInputValidator>;
|
|
76
|
+
client?: FunctionMiddlewareClientFn<TRegister, TMiddlewares, TInputValidator, TServerContext, TClientContext>;
|
|
77
|
+
server?: FunctionMiddlewareServerFn<TRegister, TMiddlewares, TInputValidator, TServerContext, unknown, unknown>;
|
|
78
|
+
}
|
|
79
|
+
export type FunctionMiddlewareClientNextFn<TRegister, TMiddlewares> = <TSendContext = undefined, TNewClientContext = undefined>(ctx?: {
|
|
80
|
+
context?: TNewClientContext;
|
|
81
|
+
sendContext?: ValidateSerializableInput<TRegister, TSendContext>;
|
|
82
|
+
headers?: HeadersInit;
|
|
83
|
+
fetch?: CustomFetch;
|
|
84
|
+
}) => Promise<FunctionClientResultWithContext<TMiddlewares, TSendContext, TNewClientContext>>;
|
|
85
|
+
export interface FunctionMiddlewareServer<TRegister, TMiddlewares, TInputValidator, TServerSendContext, TClientContext> {
|
|
86
|
+
server: <TNewServerContext = undefined, TSendContext = undefined>(server: FunctionMiddlewareServerFn<TRegister, TMiddlewares, TInputValidator, TServerSendContext, TNewServerContext, TSendContext>) => FunctionMiddlewareAfterServer<TRegister, TMiddlewares, TInputValidator, TNewServerContext, TServerSendContext, TClientContext, TSendContext>;
|
|
87
|
+
}
|
|
88
|
+
export type FunctionMiddlewareServerFn<TRegister, TMiddlewares, TInputValidator, TServerSendContext, TNewServerContext, TSendContext> = (options: FunctionMiddlewareServerFnOptions<TRegister, TMiddlewares, TInputValidator, TServerSendContext>) => FunctionMiddlewareServerFnResult<TRegister, TMiddlewares, TServerSendContext, TNewServerContext, TSendContext>;
|
|
89
|
+
export type FunctionMiddlewareServerNextFn<TRegister, TMiddlewares, TServerSendContext> = <TNewServerContext = undefined, TSendContext = undefined>(ctx?: {
|
|
90
|
+
context?: TNewServerContext;
|
|
91
|
+
sendContext?: ValidateSerializableInput<TRegister, TSendContext>;
|
|
92
|
+
}) => Promise<FunctionServerResultWithContext<TRegister, TMiddlewares, TServerSendContext, TNewServerContext, TSendContext>>;
|
|
93
|
+
export type FunctionServerResultWithContext<in out TRegister, in out TMiddlewares, in out TServerSendContext, in out TServerContext, in out TSendContext> = {
|
|
94
|
+
'use functions must return the result of next()': true;
|
|
95
|
+
'~types': {
|
|
96
|
+
context: TServerContext;
|
|
97
|
+
sendContext: TSendContext;
|
|
98
|
+
};
|
|
99
|
+
context: Expand<AssignAllServerFnContext<TRegister, TMiddlewares, TServerSendContext, TServerContext>>;
|
|
100
|
+
sendContext: Expand<AssignAllClientSendContext<TMiddlewares, TSendContext>>;
|
|
101
|
+
};
|
|
102
|
+
export interface FunctionMiddlewareServerFnOptions<in out TRegister, in out TMiddlewares, in out TInputValidator, in out TServerSendContext> {
|
|
103
|
+
data: Expand<IntersectAllValidatorOutputs<TMiddlewares, TInputValidator>>;
|
|
104
|
+
context: Expand<AssignAllServerFnContext<TRegister, TMiddlewares, TServerSendContext>>;
|
|
105
|
+
next: FunctionMiddlewareServerNextFn<TRegister, TMiddlewares, TServerSendContext>;
|
|
106
|
+
method: Method;
|
|
107
|
+
serverFnMeta: ServerFnMeta;
|
|
108
|
+
signal: AbortSignal;
|
|
109
|
+
}
|
|
110
|
+
export type FunctionMiddlewareServerFnResult<TRegister, TMiddlewares, TServerSendContext, TServerContext, TSendContext> = Promise<FunctionServerResultWithContext<TRegister, TMiddlewares, TServerSendContext, TServerContext, TSendContext>> | FunctionServerResultWithContext<TRegister, TMiddlewares, TServerSendContext, TServerContext, TSendContext>;
|
|
111
|
+
export interface FunctionMiddlewareAfterServer<TRegister, TMiddlewares, TInputValidator, TServerContext, TServerSendContext, TClientContext, TClientSendContext> extends FunctionMiddlewareWithTypes<TRegister, TMiddlewares, TInputValidator, TServerContext, TServerSendContext, TClientContext, TClientSendContext> {
|
|
112
|
+
}
|
|
113
|
+
export interface FunctionMiddlewareClient<TRegister, TMiddlewares, TInputValidator> {
|
|
114
|
+
client: <TSendServerContext = undefined, TNewClientContext = undefined>(client: FunctionMiddlewareClientFn<TRegister, TMiddlewares, TInputValidator, TSendServerContext, TNewClientContext>) => FunctionMiddlewareAfterClient<TRegister, TMiddlewares, TInputValidator, TSendServerContext, TNewClientContext>;
|
|
115
|
+
}
|
|
116
|
+
export type FunctionMiddlewareClientFn<TRegister, TMiddlewares, TInputValidator, TSendContext, TClientContext> = (options: FunctionMiddlewareClientFnOptions<TRegister, TMiddlewares, TInputValidator>) => FunctionMiddlewareClientFnResult<TMiddlewares, TSendContext, TClientContext>;
|
|
117
|
+
export interface FunctionMiddlewareClientFnOptions<in out TRegister, in out TMiddlewares, in out TInputValidator> {
|
|
118
|
+
data: Expand<IntersectAllValidatorInputs<TMiddlewares, TInputValidator>>;
|
|
119
|
+
context: Expand<AssignAllClientContextBeforeNext<TMiddlewares>>;
|
|
120
|
+
sendContext: Expand<AssignAllServerSendContext<TMiddlewares>>;
|
|
121
|
+
method: Method;
|
|
122
|
+
signal: AbortSignal;
|
|
123
|
+
serverFnMeta: ClientFnMeta;
|
|
124
|
+
next: FunctionMiddlewareClientNextFn<TRegister, TMiddlewares>;
|
|
125
|
+
filename: string;
|
|
126
|
+
fetch?: CustomFetch;
|
|
127
|
+
}
|
|
128
|
+
export type FunctionMiddlewareClientFnResult<TMiddlewares, TSendContext, TClientContext> = Promise<FunctionClientResultWithContext<TMiddlewares, TSendContext, TClientContext>> | FunctionClientResultWithContext<TMiddlewares, TSendContext, TClientContext>;
|
|
129
|
+
export type FunctionClientResultWithContext<in out TMiddlewares, in out TSendContext, in out TClientContext> = {
|
|
130
|
+
'use functions must return the result of next()': true;
|
|
131
|
+
context: Expand<AssignAllClientContextAfterNext<TMiddlewares, TClientContext>>;
|
|
132
|
+
sendContext: Expand<AssignAllServerSendContext<TMiddlewares, TSendContext>>;
|
|
133
|
+
headers: HeadersInit;
|
|
134
|
+
fetch?: CustomFetch;
|
|
135
|
+
};
|
|
136
|
+
export interface FunctionMiddlewareAfterClient<TRegister, TMiddlewares, TInputValidator, TServerSendContext, TClientContext> extends FunctionMiddlewareWithTypes<TRegister, TMiddlewares, TInputValidator, undefined, TServerSendContext, TClientContext, undefined>, FunctionMiddlewareServer<TRegister, TMiddlewares, TInputValidator, TServerSendContext, TClientContext> {
|
|
137
|
+
}
|
|
138
|
+
export interface FunctionMiddlewareValidator<TRegister, TMiddlewares> {
|
|
139
|
+
inputValidator: <TNewValidator>(inputValidator: ConstrainValidator<TRegister, 'GET', TNewValidator>) => FunctionMiddlewareAfterValidator<TRegister, TMiddlewares, TNewValidator>;
|
|
140
|
+
}
|
|
141
|
+
export interface FunctionMiddlewareAfterValidator<TRegister, TMiddlewares, TInputValidator> extends FunctionMiddlewareWithTypes<TRegister, TMiddlewares, TInputValidator, undefined, undefined, undefined, undefined>, FunctionMiddlewareServer<TRegister, TMiddlewares, TInputValidator, undefined, undefined>, FunctionMiddlewareClient<TRegister, TMiddlewares, TInputValidator> {
|
|
142
|
+
}
|
|
143
|
+
export interface RequestMiddleware<TRegister> extends RequestMiddlewareAfterMiddleware<TRegister, undefined> {
|
|
144
|
+
middleware: <const TMiddlewares = undefined>(middlewares: Constrain<TMiddlewares, ReadonlyArray<AnyRequestMiddleware>>) => RequestMiddlewareAfterMiddleware<TRegister, TMiddlewares>;
|
|
145
|
+
}
|
|
146
|
+
export type AnyRequestMiddleware = RequestMiddlewareWithTypes<any, any, any>;
|
|
147
|
+
export interface RequestMiddlewareWithTypes<TRegister, TMiddlewares, TServerContext> {
|
|
148
|
+
'~types': RequestMiddlewareTypes<TRegister, TMiddlewares, TServerContext>;
|
|
149
|
+
options: RequestMiddlewareOptions<TRegister, TMiddlewares, TServerContext>;
|
|
150
|
+
}
|
|
151
|
+
export interface RequestMiddlewareOptions<in out TRegister, in out TMiddlewares, in out TServerContext> {
|
|
152
|
+
middleware?: TMiddlewares;
|
|
153
|
+
server?: RequestServerFn<TRegister, TMiddlewares, TServerContext>;
|
|
154
|
+
}
|
|
155
|
+
export interface RequestMiddlewareTypes<TRegister, TMiddlewares, TServerContext> {
|
|
156
|
+
type: 'request';
|
|
157
|
+
allInput: undefined;
|
|
158
|
+
allOutput: undefined;
|
|
159
|
+
middlewares: TMiddlewares;
|
|
160
|
+
serverContext: TServerContext;
|
|
161
|
+
allServerContext: AssignAllServerRequestContext<TRegister, TMiddlewares, undefined, TServerContext>;
|
|
162
|
+
}
|
|
163
|
+
export interface RequestMiddlewareAfterMiddleware<TRegister, TMiddlewares> extends RequestMiddlewareWithTypes<TRegister, TMiddlewares, undefined>, RequestMiddlewareServer<TRegister, TMiddlewares> {
|
|
164
|
+
}
|
|
165
|
+
export interface RequestMiddlewareServer<TRegister, TMiddlewares> {
|
|
166
|
+
server: <TServerContext = undefined>(fn: RequestServerFn<TRegister, TMiddlewares, TServerContext>) => RequestMiddlewareAfterServer<TRegister, TMiddlewares, TServerContext>;
|
|
167
|
+
}
|
|
168
|
+
export type RequestServerFn<TRegister, TMiddlewares, TServerContext> = (options: RequestServerOptions<TRegister, TMiddlewares>) => RequestMiddlewareServerFnResult<TRegister, TMiddlewares, TServerContext>;
|
|
169
|
+
export interface RequestServerOptions<TRegister, TMiddlewares> {
|
|
170
|
+
request: Request;
|
|
171
|
+
pathname: string;
|
|
172
|
+
context: Expand<AssignAllServerRequestContext<TRegister, TMiddlewares>>;
|
|
173
|
+
next: RequestServerNextFn<TRegister, TMiddlewares>;
|
|
174
|
+
/**
|
|
175
|
+
* Metadata about the server function being invoked.
|
|
176
|
+
* This is only present when the request is handling a server function call.
|
|
177
|
+
* For regular page requests, this will be undefined.
|
|
178
|
+
*/
|
|
179
|
+
serverFnMeta?: ServerFnMeta;
|
|
180
|
+
}
|
|
181
|
+
export type RequestServerNextFn<TRegister, TMiddlewares> = <TServerContext = undefined>(options?: RequestServerNextFnOptions<TServerContext>) => RequestServerNextFnResult<TRegister, TMiddlewares, TServerContext>;
|
|
182
|
+
export interface RequestServerNextFnOptions<TServerContext> {
|
|
183
|
+
context?: TServerContext;
|
|
184
|
+
}
|
|
185
|
+
export type RequestServerNextFnResult<TRegister, TMiddlewares, TServerContext> = Promise<RequestServerResult<TRegister, TMiddlewares, TServerContext>> | RequestServerResult<TRegister, TMiddlewares, TServerContext>;
|
|
186
|
+
export type RequestMiddlewareServerFnResult<TRegister, TMiddlewares, TServerContext> = Promise<RequestServerResult<TRegister, TMiddlewares, TServerContext> | Response> | RequestServerResult<TRegister, TMiddlewares, TServerContext> | Response;
|
|
187
|
+
export interface RequestServerResult<TRegister, TMiddlewares, TServerContext> {
|
|
188
|
+
request: Request;
|
|
189
|
+
pathname: string;
|
|
190
|
+
context: Expand<AssignAllServerRequestContext<TRegister, TMiddlewares, undefined, TServerContext>>;
|
|
191
|
+
response: Response;
|
|
192
|
+
}
|
|
193
|
+
export interface RequestMiddlewareAfterServer<TRegister, TMiddlewares, TServerContext> extends RequestMiddlewareWithTypes<TRegister, TMiddlewares, TServerContext> {
|
|
194
|
+
}
|
|
195
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/createMiddleware.ts
|
|
2
|
+
var createMiddleware = (options, __opts) => {
|
|
3
|
+
const resolvedOptions = {
|
|
4
|
+
type: "request",
|
|
5
|
+
...__opts || options
|
|
6
|
+
};
|
|
7
|
+
return {
|
|
8
|
+
options: resolvedOptions,
|
|
9
|
+
middleware: (middleware) => {
|
|
10
|
+
return createMiddleware({}, Object.assign(resolvedOptions, { middleware }));
|
|
11
|
+
},
|
|
12
|
+
inputValidator: (inputValidator) => {
|
|
13
|
+
return createMiddleware({}, Object.assign(resolvedOptions, { inputValidator }));
|
|
14
|
+
},
|
|
15
|
+
client: (client) => {
|
|
16
|
+
return createMiddleware({}, Object.assign(resolvedOptions, { client }));
|
|
17
|
+
},
|
|
18
|
+
server: (server) => {
|
|
19
|
+
return createMiddleware({}, Object.assign(resolvedOptions, { server }));
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
export { createMiddleware };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=createMiddleware.js.map
|