@benjavicente/start-client-core 1.167.9 → 1.168.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/esm/client/hydrateStart.js +4 -2
  2. package/dist/esm/client/hydrateStart.js.map +1 -1
  3. package/dist/esm/client-rpc/serverFnFetcher.d.ts +21 -0
  4. package/dist/esm/client-rpc/serverFnFetcher.js +94 -71
  5. package/dist/esm/client-rpc/serverFnFetcher.js.map +1 -1
  6. package/dist/esm/createCsrfMiddleware.d.ts +46 -0
  7. package/dist/esm/createCsrfMiddleware.js +63 -0
  8. package/dist/esm/createCsrfMiddleware.js.map +1 -0
  9. package/dist/esm/createMiddleware.d.ts +4 -0
  10. package/dist/esm/createMiddleware.js.map +1 -1
  11. package/dist/esm/createServerFn.d.ts +44 -31
  12. package/dist/esm/createServerFn.js +1 -1
  13. package/dist/esm/createServerFn.js.map +1 -1
  14. package/dist/esm/fake-entries/plugin-adapters.d.ts +3 -0
  15. package/dist/esm/fake-entries/plugin-adapters.js +7 -0
  16. package/dist/esm/fake-entries/plugin-adapters.js.map +1 -0
  17. package/dist/esm/fake-entries/router.d.ts +1 -0
  18. package/dist/esm/fake-entries/router.js +6 -0
  19. package/dist/esm/fake-entries/router.js.map +1 -0
  20. package/dist/esm/{fake-start-entry.d.ts → fake-entries/start.d.ts} +0 -1
  21. package/dist/esm/fake-entries/start.js +6 -0
  22. package/dist/esm/fake-entries/start.js.map +1 -0
  23. package/dist/esm/getDefaultSerovalPlugins.d.ts +2 -1
  24. package/dist/esm/getDefaultSerovalPlugins.js.map +1 -1
  25. package/dist/esm/index.d.ts +4 -1
  26. package/dist/esm/index.js +3 -1
  27. package/dist/esm/tests/createCsrfMiddleware.test.d.ts +1 -0
  28. package/package.json +9 -10
  29. package/skills/start-core/SKILL.md +11 -7
  30. package/skills/start-core/auth-server-primitives/SKILL.md +410 -0
  31. package/skills/start-core/deployment/SKILL.md +9 -0
  32. package/skills/start-core/execution-model/SKILL.md +68 -19
  33. package/skills/start-core/middleware/SKILL.md +42 -9
  34. package/skills/start-core/server-functions/SKILL.md +115 -17
  35. package/src/client/hydrateStart.ts +12 -6
  36. package/src/client-rpc/serverFnFetcher.ts +132 -103
  37. package/src/createCsrfMiddleware.ts +197 -0
  38. package/src/createMiddleware.ts +4 -0
  39. package/src/createServerFn.ts +192 -63
  40. package/src/fake-entries/plugin-adapters.ts +4 -0
  41. package/src/fake-entries/router.ts +1 -0
  42. package/src/{fake-start-entry.ts → fake-entries/start.ts} +0 -1
  43. package/src/getDefaultSerovalPlugins.ts +2 -1
  44. package/src/index.tsx +16 -0
  45. package/src/start-entry.d.ts +9 -2
  46. package/src/tests/createCsrfMiddleware.test.ts +290 -0
  47. package/src/tests/createServerFn.test-d.ts +152 -2
  48. package/src/tests/createServerMiddleware.test-d.ts +16 -3
  49. package/bin/intent.js +0 -25
  50. package/dist/esm/fake-start-entry.js +0 -7
  51. package/dist/esm/fake-start-entry.js.map +0 -1
@@ -1,7 +1,8 @@
1
1
  import { ServerFunctionSerializationAdapter } from "./ServerFunctionSerializationAdapter.js";
2
2
  import { hydrate } from "@benjavicente/router-core/ssr/client";
3
- import { getRouter } from "#tanstack-router-entry";
4
3
  import { startInstance } from "#tanstack-start-entry";
4
+ import { hasPluginAdapters, pluginSerializationAdapters } from "#tanstack-start-plugin-adapters";
5
+ import { getRouter } from "#tanstack-router-entry";
5
6
  //#region src/client/hydrateStart.ts
6
7
  async function hydrateStart() {
7
8
  const router = await getRouter();
@@ -16,13 +17,14 @@ async function hydrateStart() {
16
17
  serializationAdapters = [];
17
18
  window.__TSS_START_OPTIONS__ = { serializationAdapters };
18
19
  }
20
+ if (hasPluginAdapters) serializationAdapters.push(...pluginSerializationAdapters);
19
21
  serializationAdapters.push(ServerFunctionSerializationAdapter);
20
22
  if (router.options.serializationAdapters) serializationAdapters.push(...router.options.serializationAdapters);
21
23
  router.update({
22
24
  basepath: process.env.TSS_ROUTER_BASEPATH,
23
25
  serializationAdapters
24
26
  });
25
- if (!router.stores.matchesId.state.length) await hydrate(router);
27
+ if (!router.stores.matchesId.get().length) await hydrate(router);
26
28
  return router;
27
29
  }
28
30
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"hydrateStart.js","names":[],"sources":["../../../src/client/hydrateStart.ts"],"sourcesContent":["import { hydrate } from '@benjavicente/router-core/ssr/client'\n\nimport { ServerFunctionSerializationAdapter } from './ServerFunctionSerializationAdapter'\nimport type { AnyStartInstanceOptions } from '../createStart'\nimport type { AnyRouter, AnySerializationAdapter } from '@benjavicente/router-core'\n// eslint-disable-next-line import/no-duplicates,import/order\nimport { getRouter } from '#tanstack-router-entry'\n// eslint-disable-next-line import/no-duplicates,import/order\nimport { startInstance } from '#tanstack-start-entry'\n\nexport async function hydrateStart(): Promise<AnyRouter> {\n const router = await getRouter()\n\n let serializationAdapters: Array<AnySerializationAdapter>\n if (startInstance) {\n const startOptions = await startInstance.getOptions()\n startOptions.serializationAdapters =\n startOptions.serializationAdapters ?? []\n window.__TSS_START_OPTIONS__ = startOptions as AnyStartInstanceOptions\n serializationAdapters = startOptions.serializationAdapters\n router.options.defaultSsr = startOptions.defaultSsr\n } else {\n serializationAdapters = []\n window.__TSS_START_OPTIONS__ = {\n serializationAdapters,\n } as AnyStartInstanceOptions\n }\n\n serializationAdapters.push(ServerFunctionSerializationAdapter)\n if (router.options.serializationAdapters) {\n serializationAdapters.push(...router.options.serializationAdapters)\n }\n\n router.update({\n basepath: process.env.TSS_ROUTER_BASEPATH,\n ...{ serializationAdapters },\n })\n if (!router.stores.matchesId.state.length) {\n await hydrate(router)\n }\n\n return router\n}\n"],"mappings":";;;;;AAUA,eAAsB,eAAmC;CACvD,MAAM,SAAS,MAAM,WAAW;CAEhC,IAAI;AACJ,KAAI,eAAe;EACjB,MAAM,eAAe,MAAM,cAAc,YAAY;AACrD,eAAa,wBACX,aAAa,yBAAyB,EAAE;AAC1C,SAAO,wBAAwB;AAC/B,0BAAwB,aAAa;AACrC,SAAO,QAAQ,aAAa,aAAa;QACpC;AACL,0BAAwB,EAAE;AAC1B,SAAO,wBAAwB,EAC7B,uBACD;;AAGH,uBAAsB,KAAK,mCAAmC;AAC9D,KAAI,OAAO,QAAQ,sBACjB,uBAAsB,KAAK,GAAG,OAAO,QAAQ,sBAAsB;AAGrE,QAAO,OAAO;EACZ,UAAU,QAAQ,IAAI;EACjB;EACN,CAAC;AACF,KAAI,CAAC,OAAO,OAAO,UAAU,MAAM,OACjC,OAAM,QAAQ,OAAO;AAGvB,QAAO"}
1
+ {"version":3,"file":"hydrateStart.js","names":[],"sources":["../../../src/client/hydrateStart.ts"],"sourcesContent":["import { hydrate } from '@benjavicente/router-core/ssr/client'\n\nimport { startInstance } from '#tanstack-start-entry'\nimport {\n hasPluginAdapters,\n pluginSerializationAdapters,\n} from '#tanstack-start-plugin-adapters'\nimport { getRouter } from '#tanstack-router-entry'\nimport { ServerFunctionSerializationAdapter } from './ServerFunctionSerializationAdapter'\nimport type { AnyRouter, AnySerializationAdapter } from '@benjavicente/router-core'\nimport type { AnyStartInstanceOptions } from '../createStart'\n\nexport async function hydrateStart(): Promise<AnyRouter> {\n const router = await getRouter()\n\n let serializationAdapters: Array<AnySerializationAdapter>\n if (startInstance) {\n const startOptions = await startInstance.getOptions()\n startOptions.serializationAdapters =\n startOptions.serializationAdapters ?? []\n window.__TSS_START_OPTIONS__ = startOptions as AnyStartInstanceOptions\n serializationAdapters = startOptions.serializationAdapters\n router.options.defaultSsr = startOptions.defaultSsr\n } else {\n serializationAdapters = []\n window.__TSS_START_OPTIONS__ = {\n serializationAdapters,\n } as AnyStartInstanceOptions\n }\n\n // Only spread plugin adapters if any are configured (this will tree-shake away otherwise)\n if (hasPluginAdapters) {\n serializationAdapters.push(...pluginSerializationAdapters)\n }\n serializationAdapters.push(ServerFunctionSerializationAdapter)\n if (router.options.serializationAdapters) {\n serializationAdapters.push(...router.options.serializationAdapters)\n }\n\n router.update({\n basepath: process.env.TSS_ROUTER_BASEPATH,\n ...{ serializationAdapters },\n })\n if (!router.stores.matchesId.get().length) {\n await hydrate(router)\n }\n\n return router\n}\n"],"mappings":";;;;;;AAYA,eAAsB,eAAmC;CACvD,MAAM,SAAS,MAAM,WAAW;CAEhC,IAAI;AACJ,KAAI,eAAe;EACjB,MAAM,eAAe,MAAM,cAAc,YAAY;AACrD,eAAa,wBACX,aAAa,yBAAyB,EAAE;AAC1C,SAAO,wBAAwB;AAC/B,0BAAwB,aAAa;AACrC,SAAO,QAAQ,aAAa,aAAa;QACpC;AACL,0BAAwB,EAAE;AAC1B,SAAO,wBAAwB,EAC7B,uBACD;;AAIH,KAAI,kBACF,uBAAsB,KAAK,GAAG,4BAA4B;AAE5D,uBAAsB,KAAK,mCAAmC;AAC9D,KAAI,OAAO,QAAQ,sBACjB,uBAAsB,KAAK,GAAG,OAAO,QAAQ,sBAAsB;AAGrE,QAAO,OAAO;EACZ,UAAU,QAAQ,IAAI;EACjB;EACN,CAAC;AACF,KAAI,CAAC,OAAO,OAAO,UAAU,KAAK,CAAC,OACjC,OAAM,QAAQ,OAAO;AAGvB,QAAO"}
@@ -1 +1,22 @@
1
+ /**
2
+ * Set the current post-processing context for async deserialization work.
3
+ * Called before deserialization starts.
4
+ *
5
+ * @param ctx - Array to collect async work promises, or null to clear
6
+ */
7
+ export declare function setPostProcessContext(ctx: Array<Promise<unknown>> | null): void;
8
+ /**
9
+ * Get the current post-processing context.
10
+ * Returns null if no deserialization is in progress.
11
+ */
12
+ export declare function getPostProcessContext(): Array<Promise<unknown>> | null;
13
+ /**
14
+ * Track an async post-processing promise in the current deserialization context.
15
+ * Called by deserializers that need to perform async work after sync deserialization.
16
+ *
17
+ * If no context is active (e.g., on server), this is a no-op.
18
+ *
19
+ * @param promise - The async work promise to track
20
+ */
21
+ export declare function trackPostProcessPromise(promise: Promise<unknown>): void;
1
22
  export declare function serverFnFetcher(url: string, args: Array<any>, handler: (url: string, requestInit: RequestInit) => Promise<Response>): Promise<any>;
@@ -6,6 +6,50 @@ import { fromCrossJSON, toJSONAsync } from "seroval";
6
6
  //#region src/client-rpc/serverFnFetcher.ts
7
7
  var serovalPlugins = null;
8
8
  /**
9
+ * Current async post-processing context for deserialization.
10
+ *
11
+ * Some deserializers need to perform async work after synchronous deserialization
12
+ * (e.g., decoding RSC payloads, fetching remote data). This context allows them
13
+ * to register promises that must complete before the deserialized value is used.
14
+ *
15
+ * This uses a synchronous execution context pattern:
16
+ * - Each call to `fromCrossJSON` is synchronous
17
+ * - Within that synchronous execution, all `fromSerializable` calls happen
18
+ * - We set the context before `fromCrossJSON`, clear it after
19
+ * - For streaming chunks, we set/clear context around each `onMessage` call
20
+ *
21
+ * Even with concurrent server function calls, each individual deserialization
22
+ * is atomic (synchronous), so promises are correctly scoped to their call.
23
+ */
24
+ var currentPostProcessContext = null;
25
+ /**
26
+ * Set the current post-processing context for async deserialization work.
27
+ * Called before deserialization starts.
28
+ *
29
+ * @param ctx - Array to collect async work promises, or null to clear
30
+ */
31
+ function setPostProcessContext(ctx) {
32
+ currentPostProcessContext = ctx;
33
+ }
34
+ /**
35
+ * Track an async post-processing promise in the current deserialization context.
36
+ * Called by deserializers that need to perform async work after sync deserialization.
37
+ *
38
+ * If no context is active (e.g., on server), this is a no-op.
39
+ *
40
+ * @param promise - The async work promise to track
41
+ */
42
+ function trackPostProcessPromise(promise) {
43
+ if (currentPostProcessContext) currentPostProcessContext.push(promise);
44
+ }
45
+ /**
46
+ * Helper to await all post-processing promises.
47
+ * Uses Promise.allSettled to ensure all promises complete even if some reject.
48
+ */
49
+ async function awaitPostProcessPromises(promises) {
50
+ if (promises.length > 0) await Promise.allSettled(promises);
51
+ }
52
+ /**
9
53
  * Checks if an object has at least one own enumerable property.
10
54
  * More efficient than Object.keys(obj).length > 0 as it short-circuits on first property.
11
55
  */
@@ -116,19 +160,17 @@ async function getResponse(fn) {
116
160
  console.error(msg, error);
117
161
  }
118
162
  });
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 });
163
+ } else if (contentType.includes("application/json")) {
164
+ const jsonPayload = await response.json();
165
+ const postProcessPromises = [];
166
+ setPostProcessContext(postProcessPromises);
167
+ try {
168
+ result = fromCrossJSON(jsonPayload, { plugins: serovalPlugins });
169
+ } finally {
170
+ setPostProcessContext(null);
171
+ }
172
+ await awaitPostProcessPromises(postProcessPromises);
173
+ }
132
174
  if (!result) {
133
175
  if (process.env.NODE_ENV !== "production") throw new Error("Invariant failed: expected result to be resolved");
134
176
  invariant();
@@ -146,86 +188,67 @@ async function getResponse(fn) {
146
188
  if (!response.ok) throw new Error(await response.text());
147
189
  return response;
148
190
  }
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
191
  /**
203
192
  * Processes a framed response where each JSON chunk is a complete JSON string
204
193
  * (already decoded by frame decoder).
194
+ *
195
+ * Uses per-chunk post-processing context to ensure async deserialization work
196
+ * completes before the next chunk is processed. This prevents issues when
197
+ * streaming values require async post-processing (e.g., RSC decoding).
205
198
  */
206
199
  async function processFramedResponse({ jsonStream, onMessage, onError }) {
207
200
  const reader = jsonStream.getReader();
208
201
  const { value: firstValue, done: firstDone } = await reader.read();
209
202
  if (firstDone || !firstValue) throw new Error("Stream ended before first object");
210
203
  const firstObject = JSON.parse(firstValue);
211
- (async () => {
204
+ let drainCancelled = false;
205
+ const drain = (async () => {
212
206
  try {
213
207
  while (true) {
214
208
  const { value, done } = await reader.read();
215
209
  if (done) break;
216
210
  if (value) try {
217
- onMessage(JSON.parse(value));
211
+ const chunkPostProcessPromises = [];
212
+ setPostProcessContext(chunkPostProcessPromises);
213
+ try {
214
+ onMessage(JSON.parse(value));
215
+ } finally {
216
+ setPostProcessContext(null);
217
+ }
218
+ await awaitPostProcessPromises(chunkPostProcessPromises);
218
219
  } catch (e) {
219
220
  onError?.(`Invalid JSON: ${value}`, e);
220
221
  }
221
222
  }
222
223
  } catch (err) {
223
- onError?.("Stream processing error:", err);
224
+ if (!drainCancelled) onError?.("Stream processing error:", err);
224
225
  }
225
226
  })();
226
- return onMessage(firstObject);
227
+ let result;
228
+ const initialPostProcessPromises = [];
229
+ setPostProcessContext(initialPostProcessPromises);
230
+ try {
231
+ result = onMessage(firstObject);
232
+ } catch (err) {
233
+ setPostProcessContext(null);
234
+ drainCancelled = true;
235
+ reader.cancel().catch(() => {});
236
+ throw err;
237
+ }
238
+ setPostProcessContext(null);
239
+ await awaitPostProcessPromises(initialPostProcessPromises);
240
+ Promise.resolve(result).catch(() => {
241
+ drainCancelled = true;
242
+ reader.cancel().catch(() => {});
243
+ });
244
+ drain.finally(() => {
245
+ try {
246
+ reader.releaseLock();
247
+ } catch {}
248
+ });
249
+ return result;
227
250
  }
228
251
  //#endregion
229
- export { serverFnFetcher };
252
+ export { serverFnFetcher, trackPostProcessPromise };
230
253
 
231
254
  //# sourceMappingURL=serverFnFetcher.js.map
@@ -1 +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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"}
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 * Current async post-processing context for deserialization.\n *\n * Some deserializers need to perform async work after synchronous deserialization\n * (e.g., decoding RSC payloads, fetching remote data). This context allows them\n * to register promises that must complete before the deserialized value is used.\n *\n * This uses a synchronous execution context pattern:\n * - Each call to `fromCrossJSON` is synchronous\n * - Within that synchronous execution, all `fromSerializable` calls happen\n * - We set the context before `fromCrossJSON`, clear it after\n * - For streaming chunks, we set/clear context around each `onMessage` call\n *\n * Even with concurrent server function calls, each individual deserialization\n * is atomic (synchronous), so promises are correctly scoped to their call.\n */\nlet currentPostProcessContext: Array<Promise<unknown>> | null = null\n\n/**\n * Set the current post-processing context for async deserialization work.\n * Called before deserialization starts.\n *\n * @param ctx - Array to collect async work promises, or null to clear\n */\nexport function setPostProcessContext(\n ctx: Array<Promise<unknown>> | null,\n): void {\n currentPostProcessContext = ctx\n}\n\n/**\n * Get the current post-processing context.\n * Returns null if no deserialization is in progress.\n */\nexport function getPostProcessContext(): Array<Promise<unknown>> | null {\n return currentPostProcessContext\n}\n\n/**\n * Track an async post-processing promise in the current deserialization context.\n * Called by deserializers that need to perform async work after sync deserialization.\n *\n * If no context is active (e.g., on server), this is a no-op.\n *\n * @param promise - The async work promise to track\n */\nexport function trackPostProcessPromise(promise: Promise<unknown>): void {\n if (currentPostProcessContext) {\n currentPostProcessContext.push(promise)\n }\n}\n\n/**\n * Helper to await all post-processing promises.\n * Uses Promise.allSettled to ensure all promises complete even if some reject.\n */\nasync function awaitPostProcessPromises(\n promises: Array<Promise<unknown>>,\n): Promise<void> {\n if (promises.length > 0) {\n await Promise.allSettled(promises)\n }\n}\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 JSON response, it can be simpler\n else if (contentType.includes('application/json')) {\n const jsonPayload = await response.json()\n // Track async post-processing work for this deserialization\n const postProcessPromises: Array<Promise<unknown>> = []\n setPostProcessContext(postProcessPromises)\n try {\n result = fromCrossJSON(jsonPayload, { plugins: serovalPlugins! })\n } finally {\n setPostProcessContext(null)\n }\n // Await any async post-processing before returning\n await awaitPostProcessPromises(postProcessPromises)\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\n/**\n * Processes a framed response where each JSON chunk is a complete JSON string\n * (already decoded by frame decoder).\n *\n * Uses per-chunk post-processing context to ensure async deserialization work\n * completes before the next chunk is processed. This prevents issues when\n * streaming values require async post-processing (e.g., RSC decoding).\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 for streaming refs like RawStream.\n // Keep draining until the server closes the stream.\n // Each chunk gets its own post-processing context to properly scope async work.\n let drainCancelled = false as boolean\n const drain = (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 // Set up post-processing context for this chunk\n const chunkPostProcessPromises: Array<Promise<unknown>> = []\n setPostProcessContext(chunkPostProcessPromises)\n try {\n onMessage(JSON.parse(value))\n } finally {\n setPostProcessContext(null)\n }\n // Await any async post-processing from this chunk before processing next.\n // This ensures values requiring async work are ready before their\n // containing Promise/Stream resolves/emits to consumers.\n await awaitPostProcessPromises(chunkPostProcessPromises)\n } catch (e) {\n onError?.(`Invalid JSON: ${value}`, e)\n }\n }\n }\n } catch (err) {\n if (!drainCancelled) {\n onError?.('Stream processing error:', err)\n }\n }\n })()\n\n // Process first object with its own post-processing context\n let result: any\n const initialPostProcessPromises: Array<Promise<unknown>> = []\n setPostProcessContext(initialPostProcessPromises)\n try {\n result = onMessage(firstObject)\n } catch (err) {\n setPostProcessContext(null)\n drainCancelled = true\n reader.cancel().catch(() => {})\n throw err\n }\n setPostProcessContext(null)\n\n // Await initial post-processing promises before returning result\n await awaitPostProcessPromises(initialPostProcessPromises)\n\n // If the initial decode fails async, stop draining to avoid holding\n // onto the response body and raw stream buffers unnecessarily.\n Promise.resolve(result).catch(() => {\n drainCancelled = true\n reader.cancel().catch(() => {})\n })\n\n // Detach reader once draining completes.\n drain.finally(() => {\n try {\n reader.releaseLock()\n } catch {\n // Ignore\n }\n })\n\n return result\n}\n"],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { RequestMiddlewareAfterServer, RequestServerOptions } from './createMiddleware.js';
2
+ import { Register } from '@benjavicente/router-core';
3
+ export declare const csrfSymbol: unique symbol;
4
+ export type CsrfSecFetchSite = 'same-origin' | 'same-site' | 'cross-site' | 'none';
5
+ export type CsrfMatcher<TValue, TRegister = Register, TMiddlewares = unknown> = TValue | Array<TValue> | ((value: TValue | (string & {}), ctx: RequestServerOptions<TRegister, TMiddlewares>) => boolean | Promise<boolean>);
6
+ export interface CsrfMiddlewareOptions<TRegister = Register, TMiddlewares = unknown> {
7
+ /**
8
+ * Return `true` to validate this request, or `false` to skip validation.
9
+ *
10
+ * @default undefined, which validates every request handled by this middleware.
11
+ */
12
+ filter?: (ctx: RequestServerOptions<TRegister, TMiddlewares>) => boolean | Promise<boolean>;
13
+ /**
14
+ * Allowed Origin values. Defaults to the trusted request origin.
15
+ */
16
+ origin?: CsrfMatcher<string, TRegister, TMiddlewares>;
17
+ /**
18
+ * Allowed Sec-Fetch-Site values.
19
+ *
20
+ * @default 'same-origin'
21
+ */
22
+ secFetchSite?: CsrfMatcher<CsrfSecFetchSite, TRegister, TMiddlewares>;
23
+ /**
24
+ * Whether to use Referer as a fallback when Sec-Fetch-Site and Origin are absent.
25
+ *
26
+ * @default true
27
+ */
28
+ referer?: boolean | ((referer: string, ctx: RequestServerOptions<TRegister, TMiddlewares>) => boolean | Promise<boolean>);
29
+ /**
30
+ * Allow requests when Sec-Fetch-Site, Origin, and Referer are all missing.
31
+ *
32
+ * @default false
33
+ */
34
+ allowRequestsWithoutOriginCheck?: boolean;
35
+ /**
36
+ * Optional response returned when CSRF validation fails.
37
+ *
38
+ * @default new Response('Forbidden', { status: 403 })
39
+ */
40
+ failureResponse?: Response | ((ctx: RequestServerOptions<TRegister, TMiddlewares>) => Response | Promise<Response>);
41
+ }
42
+ type CreateCsrfMiddleware = <TRegister, TMiddlewares>(opts?: CsrfMiddlewareOptions<TRegister, TMiddlewares>) => RequestMiddlewareAfterServer<{}, undefined, undefined>;
43
+ export declare const createCsrfMiddleware: CreateCsrfMiddleware;
44
+ export declare function isCsrfRequestAllowed<TRegister, TMiddlewares>(opts: CsrfMiddlewareOptions<TRegister, TMiddlewares>, ctx: RequestServerOptions<TRegister, TMiddlewares>): Promise<boolean>;
45
+ export declare function getCsrfRequestValidationResult<TRegister, TMiddlewares>(opts: CsrfMiddlewareOptions<TRegister, TMiddlewares>, ctx: RequestServerOptions<TRegister, TMiddlewares>): Promise<boolean | undefined>;
46
+ export {};
@@ -0,0 +1,63 @@
1
+ import { createMiddleware } from "./createMiddleware.js";
2
+ import { createIsomorphicFn } from "@benjavicente/start-fn-stubs";
3
+ //#region src/createCsrfMiddleware.ts
4
+ var csrfSymbol = Symbol.for("tanstack-start:csrf-middleware");
5
+ var innerCreateCsrfMiddleware = (opts = {}) => {
6
+ const middleware = createMiddleware().server(async (ctx) => {
7
+ const csrfCtx = ctx;
8
+ if (opts.filter && !await opts.filter(csrfCtx)) return ctx.next();
9
+ if (await isCsrfRequestAllowed(opts, csrfCtx)) return ctx.next();
10
+ return getFailureResponse(opts, csrfCtx);
11
+ });
12
+ if (process.env.NODE_ENV !== "production") Object.defineProperty(middleware, csrfSymbol, { value: true });
13
+ return middleware;
14
+ };
15
+ var createCsrfMiddleware = createIsomorphicFn().server(innerCreateCsrfMiddleware);
16
+ async function isCsrfRequestAllowed(opts, ctx) {
17
+ const result = await getCsrfRequestValidationResult(opts, ctx);
18
+ return result === true || result === void 0 && opts.allowRequestsWithoutOriginCheck === true;
19
+ }
20
+ async function getCsrfRequestValidationResult(opts, ctx) {
21
+ const fetchSite = ctx.request.headers.get("Sec-Fetch-Site");
22
+ if (fetchSite !== null) return matchValue(opts.secFetchSite ?? "same-origin", fetchSite, ctx);
23
+ const origin = ctx.request.headers.get("Origin");
24
+ if (origin !== null) {
25
+ if (opts.origin) return matchValue(opts.origin, origin, ctx);
26
+ return origin === new URL(ctx.request.url).origin;
27
+ }
28
+ const referer = ctx.request.headers.get("Referer");
29
+ if (referer === null || opts.referer === false) return;
30
+ if (typeof opts.referer === "function") return opts.referer(referer, ctx);
31
+ if (opts.origin) {
32
+ const refererOrigin = getOriginFromUrl(referer);
33
+ return refererOrigin !== void 0 && matchValue(opts.origin, refererOrigin, ctx);
34
+ }
35
+ return isRefererSameOrigin(referer, new URL(ctx.request.url).origin);
36
+ }
37
+ async function matchValue(matcher, value, ctx) {
38
+ if (typeof matcher === "function") return matcher(value, ctx);
39
+ if (Array.isArray(matcher)) return matcher.includes(value);
40
+ return value === matcher;
41
+ }
42
+ function getOriginFromUrl(url) {
43
+ try {
44
+ return new URL(url).origin;
45
+ } catch {
46
+ return;
47
+ }
48
+ }
49
+ function isRefererSameOrigin(referer, requestOrigin) {
50
+ if (referer === requestOrigin) return true;
51
+ if (!referer.startsWith(requestOrigin)) return false;
52
+ if (referer.length === requestOrigin.length) return true;
53
+ const code = referer.charCodeAt(requestOrigin.length);
54
+ return code === 47 || code === 63 || code === 35;
55
+ }
56
+ async function getFailureResponse(opts, ctx) {
57
+ if (typeof opts.failureResponse === "function") return opts.failureResponse(ctx);
58
+ return opts.failureResponse?.clone() ?? new Response("Forbidden", { status: 403 });
59
+ }
60
+ //#endregion
61
+ export { createCsrfMiddleware, csrfSymbol, getCsrfRequestValidationResult, isCsrfRequestAllowed };
62
+
63
+ //# sourceMappingURL=createCsrfMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createCsrfMiddleware.js","names":[],"sources":["../../src/createCsrfMiddleware.ts"],"sourcesContent":["import { createIsomorphicFn } from '@benjavicente/start-fn-stubs'\nimport { createMiddleware } from './createMiddleware'\nimport type {\n RequestMiddlewareAfterServer,\n RequestServerOptions,\n} from './createMiddleware'\nimport type { Register } from '@benjavicente/router-core'\n\nexport const csrfSymbol = Symbol.for('tanstack-start:csrf-middleware')\n\nexport type CsrfSecFetchSite =\n | 'same-origin'\n | 'same-site'\n | 'cross-site'\n | 'none'\n\nexport type CsrfMatcher<TValue, TRegister = Register, TMiddlewares = unknown> =\n | TValue\n | Array<TValue>\n | ((\n value: TValue | (string & {}),\n ctx: RequestServerOptions<TRegister, TMiddlewares>,\n ) => boolean | Promise<boolean>)\n\nexport interface CsrfMiddlewareOptions<\n TRegister = Register,\n TMiddlewares = unknown,\n> {\n /**\n * Return `true` to validate this request, or `false` to skip validation.\n *\n * @default undefined, which validates every request handled by this middleware.\n */\n filter?: (\n ctx: RequestServerOptions<TRegister, TMiddlewares>,\n ) => boolean | Promise<boolean>\n /**\n * Allowed Origin values. Defaults to the trusted request origin.\n */\n origin?: CsrfMatcher<string, TRegister, TMiddlewares>\n /**\n * Allowed Sec-Fetch-Site values.\n *\n * @default 'same-origin'\n */\n secFetchSite?: CsrfMatcher<CsrfSecFetchSite, TRegister, TMiddlewares>\n /**\n * Whether to use Referer as a fallback when Sec-Fetch-Site and Origin are absent.\n *\n * @default true\n */\n referer?:\n | boolean\n | ((\n referer: string,\n ctx: RequestServerOptions<TRegister, TMiddlewares>,\n ) => boolean | Promise<boolean>)\n /**\n * Allow requests when Sec-Fetch-Site, Origin, and Referer are all missing.\n *\n * @default false\n */\n allowRequestsWithoutOriginCheck?: boolean\n /**\n * Optional response returned when CSRF validation fails.\n *\n * @default new Response('Forbidden', { status: 403 })\n */\n failureResponse?:\n | Response\n | ((\n ctx: RequestServerOptions<TRegister, TMiddlewares>,\n ) => Response | Promise<Response>)\n}\n\ntype CreateCsrfMiddleware = <TRegister, TMiddlewares>(\n opts?: CsrfMiddlewareOptions<TRegister, TMiddlewares>,\n) => RequestMiddlewareAfterServer<{}, undefined, undefined>\n\nconst innerCreateCsrfMiddleware: CreateCsrfMiddleware = (opts = {}) => {\n const middleware = createMiddleware().server(async (ctx) => {\n const csrfCtx = ctx as RequestServerOptions<any, any> & typeof ctx\n\n if (opts.filter && !(await opts.filter(csrfCtx))) {\n return ctx.next()\n }\n\n if (await isCsrfRequestAllowed(opts, csrfCtx)) {\n return ctx.next()\n }\n\n return getFailureResponse(opts, csrfCtx)\n })\n\n if (process.env.NODE_ENV !== 'production') {\n Object.defineProperty(middleware, csrfSymbol, { value: true })\n }\n\n return middleware\n}\n\nexport const createCsrfMiddleware: CreateCsrfMiddleware =\n createIsomorphicFn().server(innerCreateCsrfMiddleware) as CreateCsrfMiddleware\n\nexport async function isCsrfRequestAllowed<TRegister, TMiddlewares>(\n opts: CsrfMiddlewareOptions<TRegister, TMiddlewares>,\n ctx: RequestServerOptions<TRegister, TMiddlewares>,\n): Promise<boolean> {\n const result = await getCsrfRequestValidationResult(opts, ctx)\n return (\n result === true ||\n (result === undefined && opts.allowRequestsWithoutOriginCheck === true)\n )\n}\n\nexport async function getCsrfRequestValidationResult<TRegister, TMiddlewares>(\n opts: CsrfMiddlewareOptions<TRegister, TMiddlewares>,\n ctx: RequestServerOptions<TRegister, TMiddlewares>,\n): Promise<boolean | undefined> {\n const fetchSite = ctx.request.headers.get('Sec-Fetch-Site')\n if (fetchSite !== null) {\n return matchValue(opts.secFetchSite ?? 'same-origin', fetchSite, ctx)\n }\n\n const origin = ctx.request.headers.get('Origin')\n if (origin !== null) {\n if (opts.origin) {\n return matchValue(opts.origin, origin, ctx)\n }\n\n return origin === new URL(ctx.request.url).origin\n }\n\n const referer = ctx.request.headers.get('Referer')\n if (referer === null || opts.referer === false) {\n return undefined\n }\n\n if (typeof opts.referer === 'function') {\n return opts.referer(referer, ctx)\n }\n\n if (opts.origin) {\n const refererOrigin = getOriginFromUrl(referer)\n return (\n refererOrigin !== undefined && matchValue(opts.origin, refererOrigin, ctx)\n )\n }\n\n return isRefererSameOrigin(referer, new URL(ctx.request.url).origin)\n}\n\nasync function matchValue<TValue extends string, TRegister, TMiddlewares>(\n matcher: CsrfMatcher<TValue, TRegister, TMiddlewares>,\n value: string,\n ctx: RequestServerOptions<TRegister, TMiddlewares>,\n): Promise<boolean> {\n if (typeof matcher === 'function') {\n return matcher(value, ctx)\n }\n\n if (Array.isArray(matcher)) {\n // typescript is dumb for array.includes()\n return matcher.includes(value as TValue)\n }\n\n return value === matcher\n}\n\nfunction getOriginFromUrl(url: string): string | undefined {\n try {\n return new URL(url).origin\n } catch {\n return undefined\n }\n}\n\nfunction isRefererSameOrigin(referer: string, requestOrigin: string): boolean {\n if (referer === requestOrigin) return true\n if (!referer.startsWith(requestOrigin)) return false\n if (referer.length === requestOrigin.length) return true\n const code = referer.charCodeAt(requestOrigin.length)\n return code === 47 /* '/' */ || code === 63 /* '?' */ || code === 35 /* '#' */\n}\n\nasync function getFailureResponse<TRegister, TMiddlewares>(\n opts: CsrfMiddlewareOptions<TRegister, TMiddlewares>,\n ctx: RequestServerOptions<TRegister, TMiddlewares>,\n): Promise<Response> {\n if (typeof opts.failureResponse === 'function') {\n return opts.failureResponse(ctx)\n }\n\n return (\n opts.failureResponse?.clone() ?? new Response('Forbidden', { status: 403 })\n )\n}\n"],"mappings":";;;AAQA,IAAa,aAAa,OAAO,IAAI,iCAAiC;AAuEtE,IAAM,6BAAmD,OAAO,EAAE,KAAK;CACrE,MAAM,aAAa,kBAAkB,CAAC,OAAO,OAAO,QAAQ;EAC1D,MAAM,UAAU;AAEhB,MAAI,KAAK,UAAU,CAAE,MAAM,KAAK,OAAO,QAAQ,CAC7C,QAAO,IAAI,MAAM;AAGnB,MAAI,MAAM,qBAAqB,MAAM,QAAQ,CAC3C,QAAO,IAAI,MAAM;AAGnB,SAAO,mBAAmB,MAAM,QAAQ;GACxC;AAEF,KAAA,QAAA,IAAA,aAA6B,aAC3B,QAAO,eAAe,YAAY,YAAY,EAAE,OAAO,MAAM,CAAC;AAGhE,QAAO;;AAGT,IAAa,uBACX,oBAAoB,CAAC,OAAO,0BAA0B;AAExD,eAAsB,qBACpB,MACA,KACkB;CAClB,MAAM,SAAS,MAAM,+BAA+B,MAAM,IAAI;AAC9D,QACE,WAAW,QACV,WAAW,KAAA,KAAa,KAAK,oCAAoC;;AAItE,eAAsB,+BACpB,MACA,KAC8B;CAC9B,MAAM,YAAY,IAAI,QAAQ,QAAQ,IAAI,iBAAiB;AAC3D,KAAI,cAAc,KAChB,QAAO,WAAW,KAAK,gBAAgB,eAAe,WAAW,IAAI;CAGvE,MAAM,SAAS,IAAI,QAAQ,QAAQ,IAAI,SAAS;AAChD,KAAI,WAAW,MAAM;AACnB,MAAI,KAAK,OACP,QAAO,WAAW,KAAK,QAAQ,QAAQ,IAAI;AAG7C,SAAO,WAAW,IAAI,IAAI,IAAI,QAAQ,IAAI,CAAC;;CAG7C,MAAM,UAAU,IAAI,QAAQ,QAAQ,IAAI,UAAU;AAClD,KAAI,YAAY,QAAQ,KAAK,YAAY,MACvC;AAGF,KAAI,OAAO,KAAK,YAAY,WAC1B,QAAO,KAAK,QAAQ,SAAS,IAAI;AAGnC,KAAI,KAAK,QAAQ;EACf,MAAM,gBAAgB,iBAAiB,QAAQ;AAC/C,SACE,kBAAkB,KAAA,KAAa,WAAW,KAAK,QAAQ,eAAe,IAAI;;AAI9E,QAAO,oBAAoB,SAAS,IAAI,IAAI,IAAI,QAAQ,IAAI,CAAC,OAAO;;AAGtE,eAAe,WACb,SACA,OACA,KACkB;AAClB,KAAI,OAAO,YAAY,WACrB,QAAO,QAAQ,OAAO,IAAI;AAG5B,KAAI,MAAM,QAAQ,QAAQ,CAExB,QAAO,QAAQ,SAAS,MAAgB;AAG1C,QAAO,UAAU;;AAGnB,SAAS,iBAAiB,KAAiC;AACzD,KAAI;AACF,SAAO,IAAI,IAAI,IAAI,CAAC;SACd;AACN;;;AAIJ,SAAS,oBAAoB,SAAiB,eAAgC;AAC5E,KAAI,YAAY,cAAe,QAAO;AACtC,KAAI,CAAC,QAAQ,WAAW,cAAc,CAAE,QAAO;AAC/C,KAAI,QAAQ,WAAW,cAAc,OAAQ,QAAO;CACpD,MAAM,OAAO,QAAQ,WAAW,cAAc,OAAO;AACrD,QAAO,SAAS,MAAgB,SAAS,MAAgB,SAAS;;AAGpE,eAAe,mBACb,MACA,KACmB;AACnB,KAAI,OAAO,KAAK,oBAAoB,WAClC,QAAO,KAAK,gBAAgB,IAAI;AAGlC,QACE,KAAK,iBAAiB,OAAO,IAAI,IAAI,SAAS,aAAa,EAAE,QAAQ,KAAK,CAAC"}
@@ -171,6 +171,10 @@ export interface RequestServerOptions<TRegister, TMiddlewares> {
171
171
  pathname: string;
172
172
  context: Expand<AssignAllServerRequestContext<TRegister, TMiddlewares>>;
173
173
  next: RequestServerNextFn<TRegister, TMiddlewares>;
174
+ /**
175
+ * Type of Start handler currently processing this request.
176
+ */
177
+ handlerType: 'serverFn' | 'router';
174
178
  /**
175
179
  * Metadata about the server function being invoked.
176
180
  * This is only present when the request is handling a server function call.
@@ -1 +1 @@
1
- {"version":3,"file":"createMiddleware.js","names":[],"sources":["../../src/createMiddleware.ts"],"sourcesContent":["import type { StartInstanceOptions } from './createStart'\nimport type {\n AnyServerFn,\n ConstrainValidator,\n CustomFetch,\n Method,\n} from './createServerFn'\nimport type { ClientFnMeta, ServerFnMeta } from './constants'\nimport type {\n AnyContext,\n Assign,\n Constrain,\n Expand,\n IntersectAssign,\n Register,\n ResolveValidatorInput,\n ResolveValidatorOutput,\n ValidateSerializableInput,\n} from '@benjavicente/router-core'\n\nexport type CreateMiddlewareFn<TRegister> = <TType extends MiddlewareType>(\n options?: {\n type?: TType\n },\n __opts?: FunctionMiddlewareOptions<\n TRegister,\n unknown,\n undefined,\n undefined,\n undefined\n >,\n) => CreateMiddlewareResult<TRegister, TType>\n\nexport const createMiddleware: CreateMiddlewareFn<{}> = (options, __opts) => {\n const resolvedOptions = {\n type: 'request',\n ...(__opts || options),\n }\n\n return {\n options: resolvedOptions,\n middleware: (middleware: any) => {\n return createMiddleware(\n {} as any,\n Object.assign(resolvedOptions, { middleware }),\n ) as any\n },\n inputValidator: (inputValidator: any) => {\n return createMiddleware(\n {} as any,\n Object.assign(resolvedOptions, { inputValidator }),\n ) as any\n },\n client: (client: any) => {\n return createMiddleware(\n {} as any,\n Object.assign(resolvedOptions, { client }),\n ) as any\n },\n server: (server: any) => {\n return createMiddleware(\n {} as any,\n Object.assign(resolvedOptions, { server }),\n ) as any\n },\n } as any\n}\n\nexport type MiddlewareType = 'request' | 'function'\n\nexport type CreateMiddlewareResult<\n TRegister,\n TType extends MiddlewareType,\n> = 'request' extends TType\n ? RequestMiddleware<TRegister>\n : FunctionMiddleware<TRegister>\n\nexport interface FunctionMiddleware<\n TRegister,\n> extends FunctionMiddlewareAfterMiddleware<TRegister, unknown> {\n middleware: <const TNewMiddlewares = undefined>(\n middlewares: Constrain<\n TNewMiddlewares,\n ReadonlyArray<AnyRequestMiddleware | AnyFunctionMiddleware>\n >,\n ) => FunctionMiddlewareAfterMiddleware<TRegister, TNewMiddlewares>\n}\n\nexport interface FunctionMiddlewareAfterMiddleware<TRegister, TMiddlewares>\n extends\n FunctionMiddlewareWithTypes<\n TRegister,\n TMiddlewares,\n undefined,\n undefined,\n undefined,\n undefined,\n undefined\n >,\n FunctionMiddlewareServer<\n TRegister,\n TMiddlewares,\n undefined,\n undefined,\n undefined\n >,\n FunctionMiddlewareClient<TRegister, TMiddlewares, undefined>,\n FunctionMiddlewareValidator<TRegister, TMiddlewares> {}\n\nexport interface FunctionMiddlewareWithTypes<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerContext,\n TServerSendContext,\n TClientContext,\n TClientSendContext,\n> {\n '~types': FunctionMiddlewareTypes<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerContext,\n TServerSendContext,\n TClientContext,\n TClientSendContext\n >\n options: FunctionMiddlewareOptions<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerContext,\n TClientContext\n >\n}\n\nexport interface FunctionMiddlewareTypes<\n in out TRegister,\n in out TMiddlewares,\n in out TInputValidator,\n in out TServerContext,\n in out TServerSendContext,\n in out TClientContext,\n in out TClientSendContext,\n> {\n type: 'function'\n middlewares: TMiddlewares\n input: ResolveValidatorInput<TInputValidator>\n allInput: IntersectAllValidatorInputs<TMiddlewares, TInputValidator>\n output: ResolveValidatorOutput<TInputValidator>\n allOutput: IntersectAllValidatorOutputs<TMiddlewares, TInputValidator>\n clientContext: TClientContext\n allClientContextBeforeNext: AssignAllClientContextBeforeNext<\n TMiddlewares,\n TClientContext\n >\n allClientContextAfterNext: AssignAllClientContextAfterNext<\n TMiddlewares,\n TClientContext,\n TClientSendContext\n >\n serverContext: TServerContext\n serverSendContext: TServerSendContext\n allServerSendContext: AssignAllServerSendContext<\n TMiddlewares,\n TServerSendContext\n >\n allServerContext: AssignAllServerFnContext<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n TServerContext\n >\n clientSendContext: TClientSendContext\n allClientSendContext: AssignAllClientSendContext<\n TMiddlewares,\n TClientSendContext\n >\n inputValidator: TInputValidator\n}\n\n/**\n * Recursively resolve the input type produced by a sequence of middleware\n */\nexport type IntersectAllValidatorInputs<TMiddlewares, TInputValidator> =\n unknown extends TInputValidator\n ? TInputValidator\n : TInputValidator extends undefined\n ? IntersectAllMiddleware<TMiddlewares, 'allInput'>\n : IntersectAssign<\n IntersectAllMiddleware<TMiddlewares, 'allInput'>,\n ResolveValidatorInput<TInputValidator>\n >\n\nexport type IntersectAllMiddleware<\n TMiddlewares,\n TType extends\n | keyof AnyFunctionMiddleware['~types']\n | keyof AnyRequestMiddleware['~types']\n | keyof AnyServerFn['~types'],\n TAcc = undefined,\n> = TMiddlewares extends readonly [infer TMiddleware, ...infer TRest]\n ? TMiddleware extends\n | AnyFunctionMiddleware\n | AnyRequestMiddleware\n | AnyServerFn\n ? IntersectAllMiddleware<\n TRest,\n TType,\n IntersectAssign<\n TAcc,\n TMiddleware['~types'][TType & keyof TMiddleware['~types']]\n >\n >\n : TAcc\n : TAcc\n\nexport type AnyFunctionMiddleware = FunctionMiddlewareWithTypes<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n>\n\n/**\n * Recursively merge the output type produced by a sequence of middleware\n */\nexport type IntersectAllValidatorOutputs<TMiddlewares, TInputValidator> =\n unknown extends TInputValidator\n ? TInputValidator\n : TInputValidator extends undefined\n ? IntersectAllMiddleware<TMiddlewares, 'allOutput'>\n : IntersectAssign<\n IntersectAllMiddleware<TMiddlewares, 'allOutput'>,\n Awaited<ResolveValidatorOutput<TInputValidator>>\n >\n\n/**\n * Recursively resolve the client context type produced by a sequence of middleware\n */\nexport type AssignAllClientContextBeforeNext<\n TMiddlewares,\n TClientContext = undefined,\n> = unknown extends TClientContext\n ? TClientContext\n : Assign<\n AssignAllMiddleware<TMiddlewares, 'allClientContextBeforeNext'>,\n TClientContext\n >\n\nexport type AssignAllMiddleware<\n TMiddlewares,\n TType extends\n | keyof AnyFunctionMiddleware['~types']\n | keyof AnyRequestMiddleware['~types']\n | keyof AnyServerFn['~types'],\n TAcc = undefined,\n> = TMiddlewares extends readonly [infer TMiddleware, ...infer TRest]\n ? TMiddleware extends\n | AnyFunctionMiddleware\n | AnyRequestMiddleware\n | AnyServerFn\n ? AssignAllMiddleware<\n TRest,\n TType,\n Assign<TAcc, TMiddleware['~types'][TType & keyof TMiddleware['~types']]>\n >\n : TAcc\n : TAcc\n\nexport type AssignAllClientContextAfterNext<\n TMiddlewares,\n TClientContext = undefined,\n TSendContext = undefined,\n> = unknown extends TClientContext\n ? Assign<TClientContext, TSendContext>\n : Assign<\n AssignAllMiddleware<TMiddlewares, 'allClientContextAfterNext'>,\n Assign<TClientContext, TSendContext>\n >\n\nexport type AssignAllServerSendContext<\n TMiddlewares,\n TSendContext = undefined,\n> = unknown extends TSendContext\n ? TSendContext\n : Assign<\n AssignAllMiddleware<TMiddlewares, 'allServerSendContext'>,\n TSendContext\n >\n\nexport type AssignAllServerRequestContext<\n TRegister,\n TMiddlewares,\n TSendContext = undefined,\n TServerContext = undefined,\n> = Assign<\n // Fetch Request Context\n GlobalFetchRequestContext,\n Assign<\n GlobalServerRequestContext<TRegister>,\n __AssignAllServerRequestContext<TMiddlewares, TSendContext, TServerContext>\n >\n>\n\n// export type GlobalFetchRequestContext<TRegister> = AnyContext\nexport type GlobalFetchRequestContext = Register extends {\n server: { requestContext: infer TRequestContext }\n}\n ? TRequestContext\n : AnyContext\n\nexport type GlobalServerRequestContext<TRegister> = TRegister extends {\n config: StartInstanceOptions<any, any, infer TRequestMiddlewares, any>\n}\n ? AssignAllMiddleware<TRequestMiddlewares, 'allServerContext'>\n : AnyContext\n\ntype __AssignAllServerRequestContext<\n TMiddlewares,\n TSendContext = undefined,\n TServerContext = undefined,\n> = unknown extends TSendContext\n ? Assign<TSendContext, TServerContext>\n : Assign<\n AssignAllMiddleware<TMiddlewares, 'allServerContext'>,\n Assign<TSendContext, TServerContext>\n >\n\nexport type AssignAllServerFnContext<\n TRegister,\n TMiddlewares,\n TSendContext = undefined,\n TServerContext = undefined,\n> = Assign<\n GlobalFetchRequestContext,\n Assign<\n GlobalServerRequestContext<TRegister>, // TODO: This enabled global middleware\n // type inference, but creates a circular types issue. No idea how to fix this.\n // AnyContext,\n Assign<\n GlobalServerFnContext<TRegister>, // TODO: This enabled global middleware\n // type inference, but creates a circular types issue. No idea how to fix this.\n // AnyContext,/\n __AssignAllServerFnContext<TMiddlewares, TSendContext, TServerContext>\n >\n >\n>\n\ntype GlobalServerFnContext<TRegister> = TRegister extends {\n config: StartInstanceOptions<any, any, any, infer TFunctionMiddlewares>\n}\n ? AssignAllMiddleware<TFunctionMiddlewares, 'allServerContext'>\n : AnyContext\n\ntype __AssignAllServerFnContext<\n TMiddlewares,\n TSendContext = undefined,\n TServerContext = undefined,\n> = unknown extends TSendContext\n ? Assign<TSendContext, TServerContext>\n : Assign<\n AssignAllMiddleware<TMiddlewares, 'allServerContext'>,\n Assign<TSendContext, TServerContext>\n >\n\nexport type AssignAllClientSendContext<\n TMiddlewares,\n TSendContext = undefined,\n> = unknown extends TSendContext\n ? TSendContext\n : Assign<\n AssignAllMiddleware<TMiddlewares, 'allClientSendContext'>,\n TSendContext\n >\n\nexport interface FunctionMiddlewareOptions<\n in out TRegister,\n in out TMiddlewares,\n in out TInputValidator,\n in out TServerContext,\n in out TClientContext,\n> {\n middleware?: TMiddlewares\n inputValidator?: ConstrainValidator<TRegister, 'GET', TInputValidator>\n client?: FunctionMiddlewareClientFn<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerContext,\n TClientContext\n >\n server?: FunctionMiddlewareServerFn<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerContext,\n unknown,\n unknown\n >\n}\n\nexport type FunctionMiddlewareClientNextFn<TRegister, TMiddlewares> = <\n TSendContext = undefined,\n TNewClientContext = undefined,\n>(ctx?: {\n context?: TNewClientContext\n sendContext?: ValidateSerializableInput<TRegister, TSendContext>\n headers?: HeadersInit\n fetch?: CustomFetch\n}) => Promise<\n FunctionClientResultWithContext<TMiddlewares, TSendContext, TNewClientContext>\n>\n\nexport interface FunctionMiddlewareServer<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerSendContext,\n TClientContext,\n> {\n server: <TNewServerContext = undefined, TSendContext = undefined>(\n server: FunctionMiddlewareServerFn<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerSendContext,\n TNewServerContext,\n TSendContext\n >,\n ) => FunctionMiddlewareAfterServer<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TNewServerContext,\n TServerSendContext,\n TClientContext,\n TSendContext\n >\n}\n\nexport type FunctionMiddlewareServerFn<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerSendContext,\n TNewServerContext,\n TSendContext,\n> = (\n options: FunctionMiddlewareServerFnOptions<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerSendContext\n >,\n) => FunctionMiddlewareServerFnResult<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n TNewServerContext,\n TSendContext\n>\n\nexport type FunctionMiddlewareServerNextFn<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n> = <TNewServerContext = undefined, TSendContext = undefined>(ctx?: {\n context?: TNewServerContext\n sendContext?: ValidateSerializableInput<TRegister, TSendContext>\n}) => Promise<\n FunctionServerResultWithContext<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n TNewServerContext,\n TSendContext\n >\n>\n\nexport type FunctionServerResultWithContext<\n in out TRegister,\n in out TMiddlewares,\n in out TServerSendContext,\n in out TServerContext,\n in out TSendContext,\n> = {\n 'use functions must return the result of next()': true\n '~types': {\n context: TServerContext\n sendContext: TSendContext\n }\n context: Expand<\n AssignAllServerFnContext<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n TServerContext\n >\n >\n sendContext: Expand<AssignAllClientSendContext<TMiddlewares, TSendContext>>\n}\n\nexport interface FunctionMiddlewareServerFnOptions<\n in out TRegister,\n in out TMiddlewares,\n in out TInputValidator,\n in out TServerSendContext,\n> {\n data: Expand<IntersectAllValidatorOutputs<TMiddlewares, TInputValidator>>\n context: Expand<\n AssignAllServerFnContext<TRegister, TMiddlewares, TServerSendContext>\n >\n next: FunctionMiddlewareServerNextFn<\n TRegister,\n TMiddlewares,\n TServerSendContext\n >\n method: Method\n serverFnMeta: ServerFnMeta\n signal: AbortSignal\n}\n\nexport type FunctionMiddlewareServerFnResult<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n TServerContext,\n TSendContext,\n> =\n | Promise<\n FunctionServerResultWithContext<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n TServerContext,\n TSendContext\n >\n >\n | FunctionServerResultWithContext<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n TServerContext,\n TSendContext\n >\n\nexport interface FunctionMiddlewareAfterServer<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerContext,\n TServerSendContext,\n TClientContext,\n TClientSendContext,\n> extends FunctionMiddlewareWithTypes<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerContext,\n TServerSendContext,\n TClientContext,\n TClientSendContext\n> {}\n\nexport interface FunctionMiddlewareClient<\n TRegister,\n TMiddlewares,\n TInputValidator,\n> {\n client: <TSendServerContext = undefined, TNewClientContext = undefined>(\n client: FunctionMiddlewareClientFn<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TSendServerContext,\n TNewClientContext\n >,\n ) => FunctionMiddlewareAfterClient<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TSendServerContext,\n TNewClientContext\n >\n}\n\nexport type FunctionMiddlewareClientFn<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TSendContext,\n TClientContext,\n> = (\n options: FunctionMiddlewareClientFnOptions<\n TRegister,\n TMiddlewares,\n TInputValidator\n >,\n) => FunctionMiddlewareClientFnResult<\n TMiddlewares,\n TSendContext,\n TClientContext\n>\n\nexport interface FunctionMiddlewareClientFnOptions<\n in out TRegister,\n in out TMiddlewares,\n in out TInputValidator,\n> {\n data: Expand<IntersectAllValidatorInputs<TMiddlewares, TInputValidator>>\n context: Expand<AssignAllClientContextBeforeNext<TMiddlewares>>\n sendContext: Expand<AssignAllServerSendContext<TMiddlewares>>\n method: Method\n signal: AbortSignal\n serverFnMeta: ClientFnMeta\n next: FunctionMiddlewareClientNextFn<TRegister, TMiddlewares>\n filename: string\n fetch?: CustomFetch\n}\n\nexport type FunctionMiddlewareClientFnResult<\n TMiddlewares,\n TSendContext,\n TClientContext,\n> =\n | Promise<\n FunctionClientResultWithContext<\n TMiddlewares,\n TSendContext,\n TClientContext\n >\n >\n | FunctionClientResultWithContext<TMiddlewares, TSendContext, TClientContext>\n\nexport type FunctionClientResultWithContext<\n in out TMiddlewares,\n in out TSendContext,\n in out TClientContext,\n> = {\n 'use functions must return the result of next()': true\n context: Expand<AssignAllClientContextAfterNext<TMiddlewares, TClientContext>>\n sendContext: Expand<AssignAllServerSendContext<TMiddlewares, TSendContext>>\n headers: HeadersInit\n fetch?: CustomFetch\n}\n\nexport interface FunctionMiddlewareAfterClient<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerSendContext,\n TClientContext,\n>\n extends\n FunctionMiddlewareWithTypes<\n TRegister,\n TMiddlewares,\n TInputValidator,\n undefined,\n TServerSendContext,\n TClientContext,\n undefined\n >,\n FunctionMiddlewareServer<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerSendContext,\n TClientContext\n > {}\n\nexport interface FunctionMiddlewareValidator<TRegister, TMiddlewares> {\n inputValidator: <TNewValidator>(\n inputValidator: ConstrainValidator<TRegister, 'GET', TNewValidator>,\n ) => FunctionMiddlewareAfterValidator<TRegister, TMiddlewares, TNewValidator>\n}\n\nexport interface FunctionMiddlewareAfterValidator<\n TRegister,\n TMiddlewares,\n TInputValidator,\n>\n extends\n FunctionMiddlewareWithTypes<\n TRegister,\n TMiddlewares,\n TInputValidator,\n undefined,\n undefined,\n undefined,\n undefined\n >,\n FunctionMiddlewareServer<\n TRegister,\n TMiddlewares,\n TInputValidator,\n undefined,\n undefined\n >,\n FunctionMiddlewareClient<TRegister, TMiddlewares, TInputValidator> {}\n\nexport interface RequestMiddleware<\n TRegister,\n> extends RequestMiddlewareAfterMiddleware<TRegister, undefined> {\n middleware: <const TMiddlewares = undefined>(\n middlewares: Constrain<TMiddlewares, ReadonlyArray<AnyRequestMiddleware>>,\n ) => RequestMiddlewareAfterMiddleware<TRegister, TMiddlewares>\n}\n\nexport type AnyRequestMiddleware = RequestMiddlewareWithTypes<any, any, any>\n\nexport interface RequestMiddlewareWithTypes<\n TRegister,\n TMiddlewares,\n TServerContext,\n> {\n '~types': RequestMiddlewareTypes<TRegister, TMiddlewares, TServerContext>\n options: RequestMiddlewareOptions<TRegister, TMiddlewares, TServerContext>\n}\n\nexport interface RequestMiddlewareOptions<\n in out TRegister,\n in out TMiddlewares,\n in out TServerContext,\n> {\n middleware?: TMiddlewares\n server?: RequestServerFn<TRegister, TMiddlewares, TServerContext>\n}\nexport interface RequestMiddlewareTypes<\n TRegister,\n TMiddlewares,\n TServerContext,\n> {\n type: 'request'\n // this only exists so we can use request middlewares in server functions\n allInput: undefined\n // this only exists so we can use request middlewares in server functions\n allOutput: undefined\n middlewares: TMiddlewares\n serverContext: TServerContext\n allServerContext: AssignAllServerRequestContext<\n TRegister,\n TMiddlewares,\n undefined,\n TServerContext\n >\n}\n\nexport interface RequestMiddlewareAfterMiddleware<TRegister, TMiddlewares>\n extends\n RequestMiddlewareWithTypes<TRegister, TMiddlewares, undefined>,\n RequestMiddlewareServer<TRegister, TMiddlewares> {}\n\nexport interface RequestMiddlewareServer<TRegister, TMiddlewares> {\n server: <TServerContext = undefined>(\n fn: RequestServerFn<TRegister, TMiddlewares, TServerContext>,\n ) => RequestMiddlewareAfterServer<TRegister, TMiddlewares, TServerContext>\n}\n\nexport type RequestServerFn<TRegister, TMiddlewares, TServerContext> = (\n options: RequestServerOptions<TRegister, TMiddlewares>,\n) => RequestMiddlewareServerFnResult<TRegister, TMiddlewares, TServerContext>\n\nexport interface RequestServerOptions<TRegister, TMiddlewares> {\n request: Request\n pathname: string\n context: Expand<AssignAllServerRequestContext<TRegister, TMiddlewares>>\n next: RequestServerNextFn<TRegister, TMiddlewares>\n /**\n * Metadata about the server function being invoked.\n * This is only present when the request is handling a server function call.\n * For regular page requests, this will be undefined.\n */\n serverFnMeta?: ServerFnMeta\n}\n\nexport type RequestServerNextFn<TRegister, TMiddlewares> = <\n TServerContext = undefined,\n>(\n options?: RequestServerNextFnOptions<TServerContext>,\n) => RequestServerNextFnResult<TRegister, TMiddlewares, TServerContext>\n\nexport interface RequestServerNextFnOptions<TServerContext> {\n context?: TServerContext\n}\n\nexport type RequestServerNextFnResult<TRegister, TMiddlewares, TServerContext> =\n | Promise<RequestServerResult<TRegister, TMiddlewares, TServerContext>>\n | RequestServerResult<TRegister, TMiddlewares, TServerContext>\n\nexport type RequestMiddlewareServerFnResult<\n TRegister,\n TMiddlewares,\n TServerContext,\n> =\n | Promise<\n RequestServerResult<TRegister, TMiddlewares, TServerContext> | Response\n >\n | RequestServerResult<TRegister, TMiddlewares, TServerContext>\n | Response\n\nexport interface RequestServerResult<TRegister, TMiddlewares, TServerContext> {\n request: Request\n pathname: string\n context: Expand<\n AssignAllServerRequestContext<\n TRegister,\n TMiddlewares,\n undefined,\n TServerContext\n >\n >\n response: Response\n}\n\nexport interface RequestMiddlewareAfterServer<\n TRegister,\n TMiddlewares,\n TServerContext,\n> extends RequestMiddlewareWithTypes<TRegister, TMiddlewares, TServerContext> {}\n"],"mappings":";AAiCA,IAAa,oBAA4C,SAAS,WAAW;CAC3E,MAAM,kBAAkB;EACtB,MAAM;EACN,GAAI,UAAU;EACf;AAED,QAAO;EACL,SAAS;EACT,aAAa,eAAoB;AAC/B,UAAO,iBACL,EAAE,EACF,OAAO,OAAO,iBAAiB,EAAE,YAAY,CAAC,CAC/C;;EAEH,iBAAiB,mBAAwB;AACvC,UAAO,iBACL,EAAE,EACF,OAAO,OAAO,iBAAiB,EAAE,gBAAgB,CAAC,CACnD;;EAEH,SAAS,WAAgB;AACvB,UAAO,iBACL,EAAE,EACF,OAAO,OAAO,iBAAiB,EAAE,QAAQ,CAAC,CAC3C;;EAEH,SAAS,WAAgB;AACvB,UAAO,iBACL,EAAE,EACF,OAAO,OAAO,iBAAiB,EAAE,QAAQ,CAAC,CAC3C;;EAEJ"}
1
+ {"version":3,"file":"createMiddleware.js","names":[],"sources":["../../src/createMiddleware.ts"],"sourcesContent":["import type { StartInstanceOptions } from './createStart'\nimport type {\n AnyServerFn,\n ConstrainValidator,\n CustomFetch,\n Method,\n} from './createServerFn'\nimport type { ClientFnMeta, ServerFnMeta } from './constants'\nimport type {\n AnyContext,\n Assign,\n Constrain,\n Expand,\n IntersectAssign,\n Register,\n ResolveValidatorInput,\n ResolveValidatorOutput,\n ValidateSerializableInput,\n} from '@benjavicente/router-core'\n\nexport type CreateMiddlewareFn<TRegister> = <TType extends MiddlewareType>(\n options?: {\n type?: TType\n },\n __opts?: FunctionMiddlewareOptions<\n TRegister,\n unknown,\n undefined,\n undefined,\n undefined\n >,\n) => CreateMiddlewareResult<TRegister, TType>\n\nexport const createMiddleware: CreateMiddlewareFn<{}> = (options, __opts) => {\n const resolvedOptions = {\n type: 'request',\n ...(__opts || options),\n }\n\n return {\n options: resolvedOptions,\n middleware: (middleware: any) => {\n return createMiddleware(\n {} as any,\n Object.assign(resolvedOptions, { middleware }),\n ) as any\n },\n inputValidator: (inputValidator: any) => {\n return createMiddleware(\n {} as any,\n Object.assign(resolvedOptions, { inputValidator }),\n ) as any\n },\n client: (client: any) => {\n return createMiddleware(\n {} as any,\n Object.assign(resolvedOptions, { client }),\n ) as any\n },\n server: (server: any) => {\n return createMiddleware(\n {} as any,\n Object.assign(resolvedOptions, { server }),\n ) as any\n },\n } as any\n}\n\nexport type MiddlewareType = 'request' | 'function'\n\nexport type CreateMiddlewareResult<\n TRegister,\n TType extends MiddlewareType,\n> = 'request' extends TType\n ? RequestMiddleware<TRegister>\n : FunctionMiddleware<TRegister>\n\nexport interface FunctionMiddleware<\n TRegister,\n> extends FunctionMiddlewareAfterMiddleware<TRegister, unknown> {\n middleware: <const TNewMiddlewares = undefined>(\n middlewares: Constrain<\n TNewMiddlewares,\n ReadonlyArray<AnyRequestMiddleware | AnyFunctionMiddleware>\n >,\n ) => FunctionMiddlewareAfterMiddleware<TRegister, TNewMiddlewares>\n}\n\nexport interface FunctionMiddlewareAfterMiddleware<TRegister, TMiddlewares>\n extends\n FunctionMiddlewareWithTypes<\n TRegister,\n TMiddlewares,\n undefined,\n undefined,\n undefined,\n undefined,\n undefined\n >,\n FunctionMiddlewareServer<\n TRegister,\n TMiddlewares,\n undefined,\n undefined,\n undefined\n >,\n FunctionMiddlewareClient<TRegister, TMiddlewares, undefined>,\n FunctionMiddlewareValidator<TRegister, TMiddlewares> {}\n\nexport interface FunctionMiddlewareWithTypes<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerContext,\n TServerSendContext,\n TClientContext,\n TClientSendContext,\n> {\n '~types': FunctionMiddlewareTypes<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerContext,\n TServerSendContext,\n TClientContext,\n TClientSendContext\n >\n options: FunctionMiddlewareOptions<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerContext,\n TClientContext\n >\n}\n\nexport interface FunctionMiddlewareTypes<\n in out TRegister,\n in out TMiddlewares,\n in out TInputValidator,\n in out TServerContext,\n in out TServerSendContext,\n in out TClientContext,\n in out TClientSendContext,\n> {\n type: 'function'\n middlewares: TMiddlewares\n input: ResolveValidatorInput<TInputValidator>\n allInput: IntersectAllValidatorInputs<TMiddlewares, TInputValidator>\n output: ResolveValidatorOutput<TInputValidator>\n allOutput: IntersectAllValidatorOutputs<TMiddlewares, TInputValidator>\n clientContext: TClientContext\n allClientContextBeforeNext: AssignAllClientContextBeforeNext<\n TMiddlewares,\n TClientContext\n >\n allClientContextAfterNext: AssignAllClientContextAfterNext<\n TMiddlewares,\n TClientContext,\n TClientSendContext\n >\n serverContext: TServerContext\n serverSendContext: TServerSendContext\n allServerSendContext: AssignAllServerSendContext<\n TMiddlewares,\n TServerSendContext\n >\n allServerContext: AssignAllServerFnContext<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n TServerContext\n >\n clientSendContext: TClientSendContext\n allClientSendContext: AssignAllClientSendContext<\n TMiddlewares,\n TClientSendContext\n >\n inputValidator: TInputValidator\n}\n\n/**\n * Recursively resolve the input type produced by a sequence of middleware\n */\nexport type IntersectAllValidatorInputs<TMiddlewares, TInputValidator> =\n unknown extends TInputValidator\n ? TInputValidator\n : TInputValidator extends undefined\n ? IntersectAllMiddleware<TMiddlewares, 'allInput'>\n : IntersectAssign<\n IntersectAllMiddleware<TMiddlewares, 'allInput'>,\n ResolveValidatorInput<TInputValidator>\n >\n\nexport type IntersectAllMiddleware<\n TMiddlewares,\n TType extends\n | keyof AnyFunctionMiddleware['~types']\n | keyof AnyRequestMiddleware['~types']\n | keyof AnyServerFn['~types'],\n TAcc = undefined,\n> = TMiddlewares extends readonly [infer TMiddleware, ...infer TRest]\n ? TMiddleware extends\n | AnyFunctionMiddleware\n | AnyRequestMiddleware\n | AnyServerFn\n ? IntersectAllMiddleware<\n TRest,\n TType,\n IntersectAssign<\n TAcc,\n TMiddleware['~types'][TType & keyof TMiddleware['~types']]\n >\n >\n : TAcc\n : TAcc\n\nexport type AnyFunctionMiddleware = FunctionMiddlewareWithTypes<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n>\n\n/**\n * Recursively merge the output type produced by a sequence of middleware\n */\nexport type IntersectAllValidatorOutputs<TMiddlewares, TInputValidator> =\n unknown extends TInputValidator\n ? TInputValidator\n : TInputValidator extends undefined\n ? IntersectAllMiddleware<TMiddlewares, 'allOutput'>\n : IntersectAssign<\n IntersectAllMiddleware<TMiddlewares, 'allOutput'>,\n Awaited<ResolveValidatorOutput<TInputValidator>>\n >\n\n/**\n * Recursively resolve the client context type produced by a sequence of middleware\n */\nexport type AssignAllClientContextBeforeNext<\n TMiddlewares,\n TClientContext = undefined,\n> = unknown extends TClientContext\n ? TClientContext\n : Assign<\n AssignAllMiddleware<TMiddlewares, 'allClientContextBeforeNext'>,\n TClientContext\n >\n\nexport type AssignAllMiddleware<\n TMiddlewares,\n TType extends\n | keyof AnyFunctionMiddleware['~types']\n | keyof AnyRequestMiddleware['~types']\n | keyof AnyServerFn['~types'],\n TAcc = undefined,\n> = TMiddlewares extends readonly [infer TMiddleware, ...infer TRest]\n ? TMiddleware extends\n | AnyFunctionMiddleware\n | AnyRequestMiddleware\n | AnyServerFn\n ? AssignAllMiddleware<\n TRest,\n TType,\n Assign<TAcc, TMiddleware['~types'][TType & keyof TMiddleware['~types']]>\n >\n : TAcc\n : TAcc\n\nexport type AssignAllClientContextAfterNext<\n TMiddlewares,\n TClientContext = undefined,\n TSendContext = undefined,\n> = unknown extends TClientContext\n ? Assign<TClientContext, TSendContext>\n : Assign<\n AssignAllMiddleware<TMiddlewares, 'allClientContextAfterNext'>,\n Assign<TClientContext, TSendContext>\n >\n\nexport type AssignAllServerSendContext<\n TMiddlewares,\n TSendContext = undefined,\n> = unknown extends TSendContext\n ? TSendContext\n : Assign<\n AssignAllMiddleware<TMiddlewares, 'allServerSendContext'>,\n TSendContext\n >\n\nexport type AssignAllServerRequestContext<\n TRegister,\n TMiddlewares,\n TSendContext = undefined,\n TServerContext = undefined,\n> = Assign<\n // Fetch Request Context\n GlobalFetchRequestContext,\n Assign<\n GlobalServerRequestContext<TRegister>,\n __AssignAllServerRequestContext<TMiddlewares, TSendContext, TServerContext>\n >\n>\n\n// export type GlobalFetchRequestContext<TRegister> = AnyContext\nexport type GlobalFetchRequestContext = Register extends {\n server: { requestContext: infer TRequestContext }\n}\n ? TRequestContext\n : AnyContext\n\nexport type GlobalServerRequestContext<TRegister> = TRegister extends {\n config: StartInstanceOptions<any, any, infer TRequestMiddlewares, any>\n}\n ? AssignAllMiddleware<TRequestMiddlewares, 'allServerContext'>\n : AnyContext\n\ntype __AssignAllServerRequestContext<\n TMiddlewares,\n TSendContext = undefined,\n TServerContext = undefined,\n> = unknown extends TSendContext\n ? Assign<TSendContext, TServerContext>\n : Assign<\n AssignAllMiddleware<TMiddlewares, 'allServerContext'>,\n Assign<TSendContext, TServerContext>\n >\n\nexport type AssignAllServerFnContext<\n TRegister,\n TMiddlewares,\n TSendContext = undefined,\n TServerContext = undefined,\n> = Assign<\n GlobalFetchRequestContext,\n Assign<\n GlobalServerRequestContext<TRegister>, // TODO: This enabled global middleware\n // type inference, but creates a circular types issue. No idea how to fix this.\n // AnyContext,\n Assign<\n GlobalServerFnContext<TRegister>, // TODO: This enabled global middleware\n // type inference, but creates a circular types issue. No idea how to fix this.\n // AnyContext,/\n __AssignAllServerFnContext<TMiddlewares, TSendContext, TServerContext>\n >\n >\n>\n\ntype GlobalServerFnContext<TRegister> = TRegister extends {\n config: StartInstanceOptions<any, any, any, infer TFunctionMiddlewares>\n}\n ? AssignAllMiddleware<TFunctionMiddlewares, 'allServerContext'>\n : AnyContext\n\ntype __AssignAllServerFnContext<\n TMiddlewares,\n TSendContext = undefined,\n TServerContext = undefined,\n> = unknown extends TSendContext\n ? Assign<TSendContext, TServerContext>\n : Assign<\n AssignAllMiddleware<TMiddlewares, 'allServerContext'>,\n Assign<TSendContext, TServerContext>\n >\n\nexport type AssignAllClientSendContext<\n TMiddlewares,\n TSendContext = undefined,\n> = unknown extends TSendContext\n ? TSendContext\n : Assign<\n AssignAllMiddleware<TMiddlewares, 'allClientSendContext'>,\n TSendContext\n >\n\nexport interface FunctionMiddlewareOptions<\n in out TRegister,\n in out TMiddlewares,\n in out TInputValidator,\n in out TServerContext,\n in out TClientContext,\n> {\n middleware?: TMiddlewares\n inputValidator?: ConstrainValidator<TRegister, 'GET', TInputValidator>\n client?: FunctionMiddlewareClientFn<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerContext,\n TClientContext\n >\n server?: FunctionMiddlewareServerFn<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerContext,\n unknown,\n unknown\n >\n}\n\nexport type FunctionMiddlewareClientNextFn<TRegister, TMiddlewares> = <\n TSendContext = undefined,\n TNewClientContext = undefined,\n>(ctx?: {\n context?: TNewClientContext\n sendContext?: ValidateSerializableInput<TRegister, TSendContext>\n headers?: HeadersInit\n fetch?: CustomFetch\n}) => Promise<\n FunctionClientResultWithContext<TMiddlewares, TSendContext, TNewClientContext>\n>\n\nexport interface FunctionMiddlewareServer<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerSendContext,\n TClientContext,\n> {\n server: <TNewServerContext = undefined, TSendContext = undefined>(\n server: FunctionMiddlewareServerFn<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerSendContext,\n TNewServerContext,\n TSendContext\n >,\n ) => FunctionMiddlewareAfterServer<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TNewServerContext,\n TServerSendContext,\n TClientContext,\n TSendContext\n >\n}\n\nexport type FunctionMiddlewareServerFn<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerSendContext,\n TNewServerContext,\n TSendContext,\n> = (\n options: FunctionMiddlewareServerFnOptions<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerSendContext\n >,\n) => FunctionMiddlewareServerFnResult<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n TNewServerContext,\n TSendContext\n>\n\nexport type FunctionMiddlewareServerNextFn<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n> = <TNewServerContext = undefined, TSendContext = undefined>(ctx?: {\n context?: TNewServerContext\n sendContext?: ValidateSerializableInput<TRegister, TSendContext>\n}) => Promise<\n FunctionServerResultWithContext<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n TNewServerContext,\n TSendContext\n >\n>\n\nexport type FunctionServerResultWithContext<\n in out TRegister,\n in out TMiddlewares,\n in out TServerSendContext,\n in out TServerContext,\n in out TSendContext,\n> = {\n 'use functions must return the result of next()': true\n '~types': {\n context: TServerContext\n sendContext: TSendContext\n }\n context: Expand<\n AssignAllServerFnContext<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n TServerContext\n >\n >\n sendContext: Expand<AssignAllClientSendContext<TMiddlewares, TSendContext>>\n}\n\nexport interface FunctionMiddlewareServerFnOptions<\n in out TRegister,\n in out TMiddlewares,\n in out TInputValidator,\n in out TServerSendContext,\n> {\n data: Expand<IntersectAllValidatorOutputs<TMiddlewares, TInputValidator>>\n context: Expand<\n AssignAllServerFnContext<TRegister, TMiddlewares, TServerSendContext>\n >\n next: FunctionMiddlewareServerNextFn<\n TRegister,\n TMiddlewares,\n TServerSendContext\n >\n method: Method\n serverFnMeta: ServerFnMeta\n signal: AbortSignal\n}\n\nexport type FunctionMiddlewareServerFnResult<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n TServerContext,\n TSendContext,\n> =\n | Promise<\n FunctionServerResultWithContext<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n TServerContext,\n TSendContext\n >\n >\n | FunctionServerResultWithContext<\n TRegister,\n TMiddlewares,\n TServerSendContext,\n TServerContext,\n TSendContext\n >\n\nexport interface FunctionMiddlewareAfterServer<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerContext,\n TServerSendContext,\n TClientContext,\n TClientSendContext,\n> extends FunctionMiddlewareWithTypes<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerContext,\n TServerSendContext,\n TClientContext,\n TClientSendContext\n> {}\n\nexport interface FunctionMiddlewareClient<\n TRegister,\n TMiddlewares,\n TInputValidator,\n> {\n client: <TSendServerContext = undefined, TNewClientContext = undefined>(\n client: FunctionMiddlewareClientFn<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TSendServerContext,\n TNewClientContext\n >,\n ) => FunctionMiddlewareAfterClient<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TSendServerContext,\n TNewClientContext\n >\n}\n\nexport type FunctionMiddlewareClientFn<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TSendContext,\n TClientContext,\n> = (\n options: FunctionMiddlewareClientFnOptions<\n TRegister,\n TMiddlewares,\n TInputValidator\n >,\n) => FunctionMiddlewareClientFnResult<\n TMiddlewares,\n TSendContext,\n TClientContext\n>\n\nexport interface FunctionMiddlewareClientFnOptions<\n in out TRegister,\n in out TMiddlewares,\n in out TInputValidator,\n> {\n data: Expand<IntersectAllValidatorInputs<TMiddlewares, TInputValidator>>\n context: Expand<AssignAllClientContextBeforeNext<TMiddlewares>>\n sendContext: Expand<AssignAllServerSendContext<TMiddlewares>>\n method: Method\n signal: AbortSignal\n serverFnMeta: ClientFnMeta\n next: FunctionMiddlewareClientNextFn<TRegister, TMiddlewares>\n filename: string\n fetch?: CustomFetch\n}\n\nexport type FunctionMiddlewareClientFnResult<\n TMiddlewares,\n TSendContext,\n TClientContext,\n> =\n | Promise<\n FunctionClientResultWithContext<\n TMiddlewares,\n TSendContext,\n TClientContext\n >\n >\n | FunctionClientResultWithContext<TMiddlewares, TSendContext, TClientContext>\n\nexport type FunctionClientResultWithContext<\n in out TMiddlewares,\n in out TSendContext,\n in out TClientContext,\n> = {\n 'use functions must return the result of next()': true\n context: Expand<AssignAllClientContextAfterNext<TMiddlewares, TClientContext>>\n sendContext: Expand<AssignAllServerSendContext<TMiddlewares, TSendContext>>\n headers: HeadersInit\n fetch?: CustomFetch\n}\n\nexport interface FunctionMiddlewareAfterClient<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerSendContext,\n TClientContext,\n>\n extends\n FunctionMiddlewareWithTypes<\n TRegister,\n TMiddlewares,\n TInputValidator,\n undefined,\n TServerSendContext,\n TClientContext,\n undefined\n >,\n FunctionMiddlewareServer<\n TRegister,\n TMiddlewares,\n TInputValidator,\n TServerSendContext,\n TClientContext\n > {}\n\nexport interface FunctionMiddlewareValidator<TRegister, TMiddlewares> {\n inputValidator: <TNewValidator>(\n inputValidator: ConstrainValidator<TRegister, 'GET', TNewValidator>,\n ) => FunctionMiddlewareAfterValidator<TRegister, TMiddlewares, TNewValidator>\n}\n\nexport interface FunctionMiddlewareAfterValidator<\n TRegister,\n TMiddlewares,\n TInputValidator,\n>\n extends\n FunctionMiddlewareWithTypes<\n TRegister,\n TMiddlewares,\n TInputValidator,\n undefined,\n undefined,\n undefined,\n undefined\n >,\n FunctionMiddlewareServer<\n TRegister,\n TMiddlewares,\n TInputValidator,\n undefined,\n undefined\n >,\n FunctionMiddlewareClient<TRegister, TMiddlewares, TInputValidator> {}\n\nexport interface RequestMiddleware<\n TRegister,\n> extends RequestMiddlewareAfterMiddleware<TRegister, undefined> {\n middleware: <const TMiddlewares = undefined>(\n middlewares: Constrain<TMiddlewares, ReadonlyArray<AnyRequestMiddleware>>,\n ) => RequestMiddlewareAfterMiddleware<TRegister, TMiddlewares>\n}\n\nexport type AnyRequestMiddleware = RequestMiddlewareWithTypes<any, any, any>\n\nexport interface RequestMiddlewareWithTypes<\n TRegister,\n TMiddlewares,\n TServerContext,\n> {\n '~types': RequestMiddlewareTypes<TRegister, TMiddlewares, TServerContext>\n options: RequestMiddlewareOptions<TRegister, TMiddlewares, TServerContext>\n}\n\nexport interface RequestMiddlewareOptions<\n in out TRegister,\n in out TMiddlewares,\n in out TServerContext,\n> {\n middleware?: TMiddlewares\n server?: RequestServerFn<TRegister, TMiddlewares, TServerContext>\n}\nexport interface RequestMiddlewareTypes<\n TRegister,\n TMiddlewares,\n TServerContext,\n> {\n type: 'request'\n // this only exists so we can use request middlewares in server functions\n allInput: undefined\n // this only exists so we can use request middlewares in server functions\n allOutput: undefined\n middlewares: TMiddlewares\n serverContext: TServerContext\n allServerContext: AssignAllServerRequestContext<\n TRegister,\n TMiddlewares,\n undefined,\n TServerContext\n >\n}\n\nexport interface RequestMiddlewareAfterMiddleware<TRegister, TMiddlewares>\n extends\n RequestMiddlewareWithTypes<TRegister, TMiddlewares, undefined>,\n RequestMiddlewareServer<TRegister, TMiddlewares> {}\n\nexport interface RequestMiddlewareServer<TRegister, TMiddlewares> {\n server: <TServerContext = undefined>(\n fn: RequestServerFn<TRegister, TMiddlewares, TServerContext>,\n ) => RequestMiddlewareAfterServer<TRegister, TMiddlewares, TServerContext>\n}\n\nexport type RequestServerFn<TRegister, TMiddlewares, TServerContext> = (\n options: RequestServerOptions<TRegister, TMiddlewares>,\n) => RequestMiddlewareServerFnResult<TRegister, TMiddlewares, TServerContext>\n\nexport interface RequestServerOptions<TRegister, TMiddlewares> {\n request: Request\n pathname: string\n context: Expand<AssignAllServerRequestContext<TRegister, TMiddlewares>>\n next: RequestServerNextFn<TRegister, TMiddlewares>\n /**\n * Type of Start handler currently processing this request.\n */\n handlerType: 'serverFn' | 'router'\n /**\n * Metadata about the server function being invoked.\n * This is only present when the request is handling a server function call.\n * For regular page requests, this will be undefined.\n */\n serverFnMeta?: ServerFnMeta\n}\n\nexport type RequestServerNextFn<TRegister, TMiddlewares> = <\n TServerContext = undefined,\n>(\n options?: RequestServerNextFnOptions<TServerContext>,\n) => RequestServerNextFnResult<TRegister, TMiddlewares, TServerContext>\n\nexport interface RequestServerNextFnOptions<TServerContext> {\n context?: TServerContext\n}\n\nexport type RequestServerNextFnResult<TRegister, TMiddlewares, TServerContext> =\n | Promise<RequestServerResult<TRegister, TMiddlewares, TServerContext>>\n | RequestServerResult<TRegister, TMiddlewares, TServerContext>\n\nexport type RequestMiddlewareServerFnResult<\n TRegister,\n TMiddlewares,\n TServerContext,\n> =\n | Promise<\n RequestServerResult<TRegister, TMiddlewares, TServerContext> | Response\n >\n | RequestServerResult<TRegister, TMiddlewares, TServerContext>\n | Response\n\nexport interface RequestServerResult<TRegister, TMiddlewares, TServerContext> {\n request: Request\n pathname: string\n context: Expand<\n AssignAllServerRequestContext<\n TRegister,\n TMiddlewares,\n undefined,\n TServerContext\n >\n >\n response: Response\n}\n\nexport interface RequestMiddlewareAfterServer<\n TRegister,\n TMiddlewares,\n TServerContext,\n> extends RequestMiddlewareWithTypes<TRegister, TMiddlewares, TServerContext> {}\n"],"mappings":";AAiCA,IAAa,oBAA4C,SAAS,WAAW;CAC3E,MAAM,kBAAkB;EACtB,MAAM;EACN,GAAI,UAAU;EACf;AAED,QAAO;EACL,SAAS;EACT,aAAa,eAAoB;AAC/B,UAAO,iBACL,EAAE,EACF,OAAO,OAAO,iBAAiB,EAAE,YAAY,CAAC,CAC/C;;EAEH,iBAAiB,mBAAwB;AACvC,UAAO,iBACL,EAAE,EACF,OAAO,OAAO,iBAAiB,EAAE,gBAAgB,CAAC,CACnD;;EAEH,SAAS,WAAgB;AACvB,UAAO,iBACL,EAAE,EACF,OAAO,OAAO,iBAAiB,EAAE,QAAQ,CAAC,CAC3C;;EAEH,SAAS,WAAgB;AACvB,UAAO,iBACL,EAAE,EACF,OAAO,OAAO,iBAAiB,EAAE,QAAQ,CAAC,CAC3C;;EAEJ"}