@tanstack/start-client-core 1.132.0-alpha.3 → 1.132.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,4 @@
1
- export declare const TSR_FORMDATA_CONTEXT = "__TSR_CONTEXT";
1
+ export declare const TSS_FORMDATA_CONTEXT = "__TSS_CONTEXT";
2
+ export declare const TSS_SERVER_FUNCTION: unique symbol;
3
+ export declare const X_TSS_SERIALIZED = "x-tss-serialized";
2
4
  export {};
@@ -1,5 +1,9 @@
1
- const TSR_FORMDATA_CONTEXT = "__TSR_CONTEXT";
1
+ const TSS_FORMDATA_CONTEXT = "__TSS_CONTEXT";
2
+ const TSS_SERVER_FUNCTION = Symbol.for("TSS_SERVER_FUNCTION");
3
+ const X_TSS_SERIALIZED = "x-tss-serialized";
2
4
  export {
3
- TSR_FORMDATA_CONTEXT
5
+ TSS_FORMDATA_CONTEXT,
6
+ TSS_SERVER_FUNCTION,
7
+ X_TSS_SERIALIZED
4
8
  };
5
9
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sources":["../../src/constants.ts"],"sourcesContent":["export const TSR_FORMDATA_CONTEXT = '__TSR_CONTEXT'\nexport {}\n"],"names":[],"mappings":"AAAO,MAAM,uBAAuB;"}
1
+ {"version":3,"file":"constants.js","sources":["../../src/constants.ts"],"sourcesContent":["export const TSS_FORMDATA_CONTEXT = '__TSS_CONTEXT'\nexport const TSS_SERVER_FUNCTION = Symbol.for('TSS_SERVER_FUNCTION')\n\nexport const X_TSS_SERIALIZED = 'x-tss-serialized'\nexport {}\n"],"names":[],"mappings":"AAAO,MAAM,uBAAuB;AAC7B,MAAM,sBAAsB,OAAO,IAAI,qBAAqB;AAE5D,MAAM,mBAAmB;"}
@@ -1,4 +1,6 @@
1
+ import { TSS_SERVER_FUNCTION } from './constants.js';
1
2
  export declare const createClientRpc: (functionId: string) => ((...args: Array<any>) => Promise<any>) & {
2
3
  url: string;
3
4
  functionId: string;
5
+ [TSS_SERVER_FUNCTION]: boolean;
4
6
  };
@@ -1,3 +1,4 @@
1
+ import { TSS_SERVER_FUNCTION } from "./constants.js";
1
2
  import { serverFnFetcher } from "./serverFnFetcher.js";
2
3
  let baseUrl;
3
4
  function sanitizeBase(base) {
@@ -15,7 +16,8 @@ const createClientRpc = (functionId) => {
15
16
  };
16
17
  return Object.assign(clientFn, {
17
18
  url,
18
- functionId
19
+ functionId,
20
+ [TSS_SERVER_FUNCTION]: true
19
21
  });
20
22
  };
21
23
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"createClientRpc.js","sources":["../../src/createClientRpc.ts"],"sourcesContent":["import { serverFnFetcher } from './serverFnFetcher'\n\nlet baseUrl: string\nfunction sanitizeBase(base: string) {\n return base.replace(/^\\/|\\/$/g, '')\n}\n\nexport const createClientRpc = (functionId: string) => {\n if (!baseUrl) {\n const sanitizedAppBase = sanitizeBase(process.env.TSS_APP_BASE || '/')\n const sanitizedServerBase = sanitizeBase(process.env.TSS_SERVER_FN_BASE!)\n baseUrl = `${sanitizedAppBase ? `/${sanitizedAppBase}` : ''}/${sanitizedServerBase}/`\n }\n const url = baseUrl + functionId\n\n const clientFn = (...args: Array<any>) => {\n return serverFnFetcher(url, args, fetch)\n }\n\n return Object.assign(clientFn, {\n url,\n functionId,\n })\n}\n"],"names":[],"mappings":";AAEA,IAAI;AACJ,SAAS,aAAa,MAAc;AAClC,SAAO,KAAK,QAAQ,YAAY,EAAE;AACpC;AAEO,MAAM,kBAAkB,CAAC,eAAuB;AACrD,MAAI,CAAC,SAAS;AACZ,UAAM,mBAAmB,aAAa,QAAQ,IAAI,gBAAgB,GAAG;AACrE,UAAM,sBAAsB,aAAa,QAAQ,IAAI,kBAAmB;AACxE,cAAU,GAAG,mBAAmB,IAAI,gBAAgB,KAAK,EAAE,IAAI,mBAAmB;AAAA,EACpF;AACA,QAAM,MAAM,UAAU;AAEtB,QAAM,WAAW,IAAI,SAAqB;AACxC,WAAO,gBAAgB,KAAK,MAAM,KAAK;AAAA,EACzC;AAEA,SAAO,OAAO,OAAO,UAAU;AAAA,IAC7B;AAAA,IACA;AAAA,EAAA,CACD;AACH;"}
1
+ {"version":3,"file":"createClientRpc.js","sources":["../../src/createClientRpc.ts"],"sourcesContent":["import { TSS_SERVER_FUNCTION } from './constants'\nimport { serverFnFetcher } from './serverFnFetcher'\n\nlet baseUrl: string\nfunction sanitizeBase(base: string) {\n return base.replace(/^\\/|\\/$/g, '')\n}\n\nexport const createClientRpc = (functionId: string) => {\n if (!baseUrl) {\n const sanitizedAppBase = sanitizeBase(process.env.TSS_APP_BASE || '/')\n const sanitizedServerBase = sanitizeBase(process.env.TSS_SERVER_FN_BASE!)\n baseUrl = `${sanitizedAppBase ? `/${sanitizedAppBase}` : ''}/${sanitizedServerBase}/`\n }\n const url = baseUrl + functionId\n\n const clientFn = (...args: Array<any>) => {\n return serverFnFetcher(url, args, fetch)\n }\n\n return Object.assign(clientFn, {\n url,\n functionId,\n [TSS_SERVER_FUNCTION]: true,\n })\n}\n"],"names":[],"mappings":";;AAGA,IAAI;AACJ,SAAS,aAAa,MAAc;AAClC,SAAO,KAAK,QAAQ,YAAY,EAAE;AACpC;AAEO,MAAM,kBAAkB,CAAC,eAAuB;AACrD,MAAI,CAAC,SAAS;AACZ,UAAM,mBAAmB,aAAa,QAAQ,IAAI,gBAAgB,GAAG;AACrE,UAAM,sBAAsB,aAAa,QAAQ,IAAI,kBAAmB;AACxE,cAAU,GAAG,mBAAmB,IAAI,gBAAgB,KAAK,EAAE,IAAI,mBAAmB;AAAA,EACpF;AACA,QAAM,MAAM,UAAU;AAEtB,QAAM,WAAW,IAAI,SAAqB;AACxC,WAAO,gBAAgB,KAAK,MAAM,KAAK;AAAA,EACzC;AAEA,SAAO,OAAO,OAAO,UAAU;AAAA,IAC7B;AAAA,IACA;AAAA,IACA,CAAC,mBAAmB,GAAG;AAAA,EAAA,CACxB;AACH;"}
@@ -1,4 +1,4 @@
1
1
  type EnvOnlyFn = <TFn extends (...args: Array<any>) => any>(fn: TFn) => TFn;
2
- export declare const serverOnly: EnvOnlyFn;
3
- export declare const clientOnly: EnvOnlyFn;
2
+ export declare const createServerOnlyFn: EnvOnlyFn;
3
+ export declare const createClientOnlyFn: EnvOnlyFn;
4
4
  export {};
@@ -1,7 +1,7 @@
1
- const serverOnly = (fn) => fn;
2
- const clientOnly = (fn) => fn;
1
+ const createServerOnlyFn = (fn) => fn;
2
+ const createClientOnlyFn = (fn) => fn;
3
3
  export {
4
- clientOnly,
5
- serverOnly
4
+ createClientOnlyFn,
5
+ createServerOnlyFn
6
6
  };
7
7
  //# sourceMappingURL=envOnly.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"envOnly.js","sources":["../../src/envOnly.ts"],"sourcesContent":["type EnvOnlyFn = <TFn extends (...args: Array<any>) => any>(fn: TFn) => TFn\n\n// A function that will only be available in the server build\n// If called on the client, it will throw an error\nexport const serverOnly: EnvOnlyFn = (fn) => fn\n\n// A function that will only be available in the client build\n// If called on the server, it will throw an error\nexport const clientOnly: EnvOnlyFn = (fn) => fn\n"],"names":[],"mappings":"AAIO,MAAM,aAAwB,CAAC,OAAO;AAItC,MAAM,aAAwB,CAAC,OAAO;"}
1
+ {"version":3,"file":"envOnly.js","sources":["../../src/envOnly.ts"],"sourcesContent":["type EnvOnlyFn = <TFn extends (...args: Array<any>) => any>(fn: TFn) => TFn\n\n// A function that will only be available in the server build\n// If called on the client, it will throw an error\nexport const createServerOnlyFn: EnvOnlyFn = (fn) => fn\n\n// A function that will only be available in the client build\n// If called on the server, it will throw an error\nexport const createClientOnlyFn: EnvOnlyFn = (fn) => fn\n"],"names":[],"mappings":"AAIO,MAAM,qBAAgC,CAAC,OAAO;AAI9C,MAAM,qBAAgC,CAAC,OAAO;"}
@@ -2,7 +2,7 @@ export type { DehydratedRouter, JsonResponse, } from '@tanstack/router-core/ssr/
2
2
  export { hydrate, json, mergeHeaders } from '@tanstack/router-core/ssr/client';
3
3
  export type { Serializable, SerializerParse, SerializerParseBy, SerializerStringify, SerializerStringifyBy, SerializerExtensions, } from './serializer.js';
4
4
  export { createIsomorphicFn, type IsomorphicFn, type ServerOnlyFn, type ClientOnlyFn, type IsomorphicFnBase, } from './createIsomorphicFn.js';
5
- export { serverOnly, clientOnly } from './envOnly.js';
5
+ export { createServerOnlyFn, createClientOnlyFn } from './envOnly.js';
6
6
  export { createServerFn } from './createServerFn.js';
7
7
  export { createMiddleware, type IntersectAllValidatorInputs, type IntersectAllValidatorOutputs, type FunctionMiddlewareServerFn, type AnyFunctionMiddleware, type FunctionMiddlewareOptions, type FunctionMiddlewareWithTypes, type FunctionMiddlewareValidator, type FunctionMiddlewareServer, type FunctionMiddlewareAfterClient, type FunctionMiddlewareAfterServer, type FunctionMiddleware, type FunctionMiddlewareClientFnOptions, type FunctionMiddlewareClientFnResult, type FunctionMiddlewareClientNextFn, type FunctionClientResultWithContext, type AssignAllClientContextBeforeNext, type AssignAllMiddleware, type AssignAllServerContext, type FunctionMiddlewareAfterValidator, type FunctionMiddlewareClientFn, type FunctionMiddlewareServerFnResult, type FunctionMiddlewareClient, type FunctionMiddlewareServerFnOptions, type FunctionMiddlewareServerNextFn, type FunctionServerResultWithContext, type AnyRequestMiddleware, } from './createMiddleware.js';
8
8
  export { registerGlobalMiddleware, globalMiddleware, } from './registerGlobalMiddleware.js';
@@ -10,4 +10,4 @@ export type { CompiledFetcherFnOptions, CompiledFetcherFn, Fetcher, RscStream, F
10
10
  export { applyMiddleware, execValidator, serverFnBaseToMiddleware, flattenMiddlewares, executeMiddleware, } from './createServerFn.js';
11
11
  export { createClientRpc } from './createClientRpc.js';
12
12
  export { getDefaultSerovalPlugins } from './serializer/getDefaultSerovalPlugins.js';
13
- export { TSR_FORMDATA_CONTEXT } from './constants.js';
13
+ export { TSS_FORMDATA_CONTEXT, TSS_SERVER_FUNCTION, X_TSS_SERIALIZED, } from './constants.js';
package/dist/esm/index.js CHANGED
@@ -1,20 +1,23 @@
1
1
  import { hydrate, json, mergeHeaders } from "@tanstack/router-core/ssr/client";
2
2
  import { createIsomorphicFn } from "./createIsomorphicFn.js";
3
- import { clientOnly, serverOnly } from "./envOnly.js";
3
+ import { createClientOnlyFn, createServerOnlyFn } from "./envOnly.js";
4
4
  import { applyMiddleware, createServerFn, execValidator, executeMiddleware, flattenMiddlewares, serverFnBaseToMiddleware } from "./createServerFn.js";
5
5
  import { createMiddleware } from "./createMiddleware.js";
6
6
  import { globalMiddleware, registerGlobalMiddleware } from "./registerGlobalMiddleware.js";
7
7
  import { createClientRpc } from "./createClientRpc.js";
8
8
  import { getDefaultSerovalPlugins } from "./serializer/getDefaultSerovalPlugins.js";
9
- import { TSR_FORMDATA_CONTEXT } from "./constants.js";
9
+ import { TSS_FORMDATA_CONTEXT, TSS_SERVER_FUNCTION, X_TSS_SERIALIZED } from "./constants.js";
10
10
  export {
11
- TSR_FORMDATA_CONTEXT,
11
+ TSS_FORMDATA_CONTEXT,
12
+ TSS_SERVER_FUNCTION,
13
+ X_TSS_SERIALIZED,
12
14
  applyMiddleware,
13
- clientOnly,
15
+ createClientOnlyFn,
14
16
  createClientRpc,
15
17
  createIsomorphicFn,
16
18
  createMiddleware,
17
19
  createServerFn,
20
+ createServerOnlyFn,
18
21
  execValidator,
19
22
  executeMiddleware,
20
23
  flattenMiddlewares,
@@ -24,7 +27,6 @@ export {
24
27
  json,
25
28
  mergeHeaders,
26
29
  registerGlobalMiddleware,
27
- serverFnBaseToMiddleware,
28
- serverOnly
30
+ serverFnBaseToMiddleware
29
31
  };
30
32
  //# sourceMappingURL=index.js.map
@@ -1,8 +1,9 @@
1
1
  import { createSerializationAdapter } from "@tanstack/router-core";
2
2
  import { createClientRpc } from "../createClientRpc.js";
3
+ import { TSS_SERVER_FUNCTION } from "../constants.js";
3
4
  const ServerFunctionSerializationAdapter = createSerializationAdapter({
4
5
  key: "$TSS/serverfn",
5
- test: (v) => typeof v == "function" && "functionId" in v,
6
+ test: (v) => v[TSS_SERVER_FUNCTION],
6
7
  toSerializable: ({ functionId }) => ({ functionId }),
7
8
  fromSerializable: ({ functionId }) => createClientRpc(functionId)
8
9
  });
@@ -1 +1 @@
1
- {"version":3,"file":"ServerFunctionSerializationAdapter.js","sources":["../../../src/serializer/ServerFunctionSerializationAdapter.ts"],"sourcesContent":["import { createSerializationAdapter } from '@tanstack/router-core'\nimport { createClientRpc } from '../createClientRpc'\n\nexport const ServerFunctionSerializationAdapter = createSerializationAdapter({\n key: '$TSS/serverfn',\n test: (v): v is { functionId: string } =>\n typeof v == 'function' && 'functionId' in v,\n toSerializable: ({ functionId }) => ({ functionId }),\n fromSerializable: ({ functionId }) => createClientRpc(functionId),\n})\n"],"names":[],"mappings":";;AAGO,MAAM,qCAAqC,2BAA2B;AAAA,EAC3E,KAAK;AAAA,EACL,MAAM,CAAC,MACL,OAAO,KAAK,cAAc,gBAAgB;AAAA,EAC5C,gBAAgB,CAAC,EAAE,kBAAkB,EAAE,WAAA;AAAA,EACvC,kBAAkB,CAAC,EAAE,WAAA,MAAiB,gBAAgB,UAAU;AAClE,CAAC;"}
1
+ {"version":3,"file":"ServerFunctionSerializationAdapter.js","sources":["../../../src/serializer/ServerFunctionSerializationAdapter.ts"],"sourcesContent":["import { createSerializationAdapter } from '@tanstack/router-core'\nimport { createClientRpc } from '../createClientRpc'\nimport { TSS_SERVER_FUNCTION } from '../constants'\n\nexport const ServerFunctionSerializationAdapter = createSerializationAdapter({\n key: '$TSS/serverfn',\n test: (v): v is { functionId: string } => v[TSS_SERVER_FUNCTION],\n toSerializable: ({ functionId }) => ({ functionId }),\n fromSerializable: ({ functionId }) => createClientRpc(functionId),\n})\n"],"names":[],"mappings":";;;AAIO,MAAM,qCAAqC,2BAA2B;AAAA,EAC3E,KAAK;AAAA,EACL,MAAM,CAAC,MAAmC,EAAE,mBAAmB;AAAA,EAC/D,gBAAgB,CAAC,EAAE,kBAAkB,EAAE,WAAA;AAAA,EACvC,kBAAkB,CAAC,EAAE,WAAA,MAAiB,gBAAgB,UAAU;AAClE,CAAC;"}
@@ -10,7 +10,7 @@ function getDefaultSerovalPlugins() {
10
10
  const router = getRouterInstance();
11
11
  invariant(router, "Expected router instance to be available");
12
12
  const adapters = router.options.serializationAdapters;
13
- return [...defaultSerovalPlugins, ...adapters?.map(makeSerovalPlugin) ?? []];
13
+ return [...adapters?.map(makeSerovalPlugin) ?? [], ...defaultSerovalPlugins];
14
14
  }
15
15
  export {
16
16
  defaultSerovalPlugins,
@@ -1 +1 @@
1
- {"version":3,"file":"getDefaultSerovalPlugins.js","sources":["../../../src/serializer/getDefaultSerovalPlugins.ts"],"sourcesContent":["import invariant from 'tiny-invariant'\nimport {\n makeSerovalPlugin,\n defaultSerovalPlugins as routerDefaultSerovalPlugins,\n} from '@tanstack/router-core'\nimport { FormDataPlugin } from 'seroval-plugins/web'\nimport { getRouterInstance } from '../getRouterInstance'\nimport type { AnyTransformer } from '@tanstack/router-core'\n\nimport type { Plugin } from 'seroval'\n\nexport const defaultSerovalPlugins = [\n ...routerDefaultSerovalPlugins,\n FormDataPlugin as Plugin<FormData, any>,\n]\n\nexport function getDefaultSerovalPlugins() {\n const router = getRouterInstance()\n invariant(router, 'Expected router instance to be available')\n const adapters = router.options.serializationAdapters as\n | Array<AnyTransformer>\n | undefined\n return [...defaultSerovalPlugins, ...(adapters?.map(makeSerovalPlugin) ?? [])]\n}\n"],"names":["routerDefaultSerovalPlugins"],"mappings":";;;;AAWO,MAAM,wBAAwB;AAAA,EACnC,GAAGA;AAAAA,EACH;AACF;AAEO,SAAS,2BAA2B;AACzC,QAAM,SAAS,kBAAA;AACf,YAAU,QAAQ,0CAA0C;AAC5D,QAAM,WAAW,OAAO,QAAQ;AAGhC,SAAO,CAAC,GAAG,uBAAuB,GAAI,UAAU,IAAI,iBAAiB,KAAK,EAAG;AAC/E;"}
1
+ {"version":3,"file":"getDefaultSerovalPlugins.js","sources":["../../../src/serializer/getDefaultSerovalPlugins.ts"],"sourcesContent":["import invariant from 'tiny-invariant'\nimport {\n makeSerovalPlugin,\n defaultSerovalPlugins as routerDefaultSerovalPlugins,\n} from '@tanstack/router-core'\nimport { FormDataPlugin } from 'seroval-plugins/web'\nimport { getRouterInstance } from '../getRouterInstance'\nimport type { AnyTransformer } from '@tanstack/router-core'\n\nimport type { Plugin } from 'seroval'\n\nexport const defaultSerovalPlugins = [\n ...routerDefaultSerovalPlugins,\n FormDataPlugin as Plugin<FormData, any>,\n]\n\nexport function getDefaultSerovalPlugins() {\n const router = getRouterInstance()\n invariant(router, 'Expected router instance to be available')\n const adapters = router.options.serializationAdapters as\n | Array<AnyTransformer>\n | undefined\n return [...(adapters?.map(makeSerovalPlugin) ?? []), ...defaultSerovalPlugins]\n}\n"],"names":["routerDefaultSerovalPlugins"],"mappings":";;;;AAWO,MAAM,wBAAwB;AAAA,EACnC,GAAGA;AAAAA,EACH;AACF;AAEO,SAAS,2BAA2B;AACzC,QAAM,SAAS,kBAAA;AACf,YAAU,QAAQ,0CAA0C;AAC5D,QAAM,WAAW,OAAO,QAAQ;AAGhC,SAAO,CAAC,GAAI,UAAU,IAAI,iBAAiB,KAAK,CAAA,GAAK,GAAG,qBAAqB;AAC/E;"}
@@ -1,8 +1,8 @@
1
1
  import { isPlainObject, encode, parseRedirect, isNotFound } from "@tanstack/router-core";
2
- import { fromJSON, toJSONAsync, fromCrossJSON } from "seroval";
2
+ import { fromCrossJSON, toJSONAsync } from "seroval";
3
3
  import invariant from "tiny-invariant";
4
4
  import { getClientSerovalPlugins } from "./serializer/getClientSerovalPlugins.js";
5
- import { TSR_FORMDATA_CONTEXT } from "./constants.js";
5
+ import { X_TSS_SERIALIZED, TSS_FORMDATA_CONTEXT } from "./constants.js";
6
6
  let serovalPlugins = null;
7
7
  async function serverFnFetcher(url, args, handler) {
8
8
  if (!serovalPlugins) {
@@ -78,7 +78,7 @@ async function serialize(data) {
78
78
  async function getFetcherRequestOptions(opts) {
79
79
  if (opts.method === "POST") {
80
80
  if (opts.data instanceof FormData) {
81
- opts.data.set(TSR_FORMDATA_CONTEXT, await serialize(opts.context));
81
+ opts.data.set(TSS_FORMDATA_CONTEXT, await serialize(opts.context));
82
82
  return {
83
83
  body: opts.data
84
84
  };
@@ -102,11 +102,11 @@ async function getResponse(fn) {
102
102
  })();
103
103
  const contentType = response.headers.get("content-type");
104
104
  invariant(contentType, "expected content-type header to be set");
105
- const serializedByStart = !!response.headers.get("x-tss-serialized");
105
+ const serializedByStart = !!response.headers.get(X_TSS_SERIALIZED);
106
106
  if (!response.ok) {
107
107
  if (serializedByStart && contentType.includes("application/json")) {
108
108
  const jsonPayload = await response.json();
109
- const result = fromJSON(jsonPayload, { plugins: serovalPlugins });
109
+ const result = fromCrossJSON(jsonPayload, { plugins: serovalPlugins });
110
110
  throw result;
111
111
  }
112
112
  throw new Error(await response.text());
@@ -125,7 +125,7 @@ async function getResponse(fn) {
125
125
  }
126
126
  if (contentType.includes("application/json")) {
127
127
  const jsonPayload = await response.json();
128
- result = fromJSON(jsonPayload, { plugins: serovalPlugins });
128
+ result = fromCrossJSON(jsonPayload, { plugins: serovalPlugins });
129
129
  }
130
130
  invariant(result, "expected result to be resolved");
131
131
  if (result instanceof Error) {
@@ -1 +1 @@
1
- {"version":3,"file":"serverFnFetcher.js","sources":["../../src/serverFnFetcher.ts"],"sourcesContent":["import {\n encode,\n isNotFound,\n isPlainObject,\n parseRedirect,\n} from '@tanstack/router-core'\nimport { fromCrossJSON, fromJSON, toJSONAsync } from 'seroval'\nimport invariant from 'tiny-invariant'\nimport { getClientSerovalPlugins } from './serializer/getClientSerovalPlugins'\nimport { TSR_FORMDATA_CONTEXT } from './constants'\nimport type { FunctionMiddlewareClientFnOptions } from './createMiddleware'\nimport type { Plugin as SerovalPlugin } from 'seroval'\n\nlet serovalPlugins: Array<SerovalPlugin<any, any>> | null = null\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 = getClientSerovalPlugins()\n }\n const _first = args[0]\n\n // If createServerFn was used to wrap the fetcher,\n // We need to handle the arguments differently\n if (isPlainObject(_first) && _first.method) {\n const first = _first as FunctionMiddlewareClientFnOptions<\n any,\n any,\n any,\n any\n > & {\n headers: HeadersInit\n }\n const type = first.data instanceof FormData ? 'formData' : 'payload'\n\n // Arrange the headers\n const headers = new Headers({\n 'x-tsr-redirect': 'manual',\n ...(type === 'payload'\n ? {\n 'content-type': 'application/json',\n accept: 'application/x-ndjson, application/json',\n }\n : {}),\n ...(first.headers instanceof Headers\n ? Object.fromEntries(first.headers.entries())\n : first.headers),\n })\n\n // If the method is GET, we need to move the payload to the query string\n if (first.method === 'GET') {\n const encodedPayload = encode({\n payload: await serializePayload(first),\n })\n\n if (encodedPayload) {\n if (url.includes('?')) {\n url += `&${encodedPayload}`\n } else {\n url += `?${encodedPayload}`\n }\n }\n }\n\n if (url.includes('?')) {\n url += `&createServerFn`\n } else {\n url += `?createServerFn`\n }\n if (first.response === 'raw') {\n url += `&raw`\n }\n\n return await getResponse(async () =>\n handler(url, {\n method: first.method,\n headers,\n signal: first.signal,\n ...(await getFetcherRequestOptions(first)),\n }),\n )\n }\n\n // If not a custom fetcher, it was probably\n // a `use server` function, so just proxy the arguments\n // through as a POST request\n return await getResponse(() =>\n handler(url, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(args),\n }),\n )\n}\n\nasync function serializePayload(\n opts: FunctionMiddlewareClientFnOptions<any, any, any, any>,\n) {\n const payloadToSerialize: any = {}\n if (opts.data) {\n payloadToSerialize['data'] = opts.data\n }\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (opts.context && Object.keys(opts.context).length > 0) {\n payloadToSerialize['context'] = opts.context\n }\n\n return serialize(payloadToSerialize)\n}\n\nasync function serialize(data: any) {\n return JSON.stringify(\n await Promise.resolve(toJSONAsync(data, { plugins: serovalPlugins! })),\n )\n}\n\nasync function getFetcherRequestOptions(\n opts: FunctionMiddlewareClientFnOptions<any, any, any, any>,\n) {\n if (opts.method === 'POST') {\n if (opts.data instanceof FormData) {\n opts.data.set(TSR_FORMDATA_CONTEXT, await serialize(opts.context))\n return {\n body: opts.data,\n }\n }\n\n return {\n body: await serializePayload(opts),\n }\n }\n\n return {}\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 const response = await (async () => {\n try {\n return await fn()\n } catch (error) {\n if (error instanceof Response) {\n return error\n }\n\n throw error\n }\n })()\n\n const contentType = response.headers.get('content-type')\n invariant(contentType, 'expected content-type header to be set')\n const serializedByStart = !!response.headers.get('x-tss-serialized')\n // If the response is not ok, throw an error\n if (!response.ok) {\n if (serializedByStart && contentType.includes('application/json')) {\n const jsonPayload = await response.json()\n const result = fromJSON(jsonPayload, { plugins: serovalPlugins! })\n throw result\n }\n\n throw new Error(await response.text())\n }\n\n if (serializedByStart) {\n let result\n 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 occured?\n console.error(msg, error)\n },\n })\n }\n if (contentType.includes('application/json')) {\n const jsonPayload = await response.json()\n result = fromJSON(jsonPayload, { plugins: serovalPlugins! })\n }\n invariant(result, 'expected result to be resolved')\n if (result instanceof Error) {\n throw result\n }\n return result\n }\n\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 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"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"serverFnFetcher.js","sources":["../../src/serverFnFetcher.ts"],"sourcesContent":["import {\n encode,\n isNotFound,\n isPlainObject,\n parseRedirect,\n} from '@tanstack/router-core'\nimport { fromCrossJSON, toJSONAsync } from 'seroval'\nimport invariant from 'tiny-invariant'\nimport { getClientSerovalPlugins } from './serializer/getClientSerovalPlugins'\nimport { TSS_FORMDATA_CONTEXT, X_TSS_SERIALIZED } from './constants'\nimport type { FunctionMiddlewareClientFnOptions } from './createMiddleware'\nimport type { Plugin as SerovalPlugin } from 'seroval'\n\nlet serovalPlugins: Array<SerovalPlugin<any, any>> | null = null\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 = getClientSerovalPlugins()\n }\n const _first = args[0]\n\n // If createServerFn was used to wrap the fetcher,\n // We need to handle the arguments differently\n if (isPlainObject(_first) && _first.method) {\n const first = _first as FunctionMiddlewareClientFnOptions<\n any,\n any,\n any,\n any\n > & {\n headers: HeadersInit\n }\n const type = first.data instanceof FormData ? 'formData' : 'payload'\n\n // Arrange the headers\n const headers = new Headers({\n 'x-tsr-redirect': 'manual',\n ...(type === 'payload'\n ? {\n 'content-type': 'application/json',\n accept: 'application/x-ndjson, application/json',\n }\n : {}),\n ...(first.headers instanceof Headers\n ? Object.fromEntries(first.headers.entries())\n : first.headers),\n })\n\n // If the method is GET, we need to move the payload to the query string\n if (first.method === 'GET') {\n const encodedPayload = encode({\n payload: await serializePayload(first),\n })\n\n if (encodedPayload) {\n if (url.includes('?')) {\n url += `&${encodedPayload}`\n } else {\n url += `?${encodedPayload}`\n }\n }\n }\n\n if (url.includes('?')) {\n url += `&createServerFn`\n } else {\n url += `?createServerFn`\n }\n if (first.response === 'raw') {\n url += `&raw`\n }\n\n return await getResponse(async () =>\n handler(url, {\n method: first.method,\n headers,\n signal: first.signal,\n ...(await getFetcherRequestOptions(first)),\n }),\n )\n }\n\n // If not a custom fetcher, it was probably\n // a `use server` function, so just proxy the arguments\n // through as a POST request\n return await getResponse(() =>\n handler(url, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(args),\n }),\n )\n}\n\nasync function serializePayload(\n opts: FunctionMiddlewareClientFnOptions<any, any, any, any>,\n) {\n const payloadToSerialize: any = {}\n if (opts.data) {\n payloadToSerialize['data'] = opts.data\n }\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (opts.context && Object.keys(opts.context).length > 0) {\n payloadToSerialize['context'] = opts.context\n }\n\n return serialize(payloadToSerialize)\n}\n\nasync function serialize(data: any) {\n return JSON.stringify(\n await Promise.resolve(toJSONAsync(data, { plugins: serovalPlugins! })),\n )\n}\n\nasync function getFetcherRequestOptions(\n opts: FunctionMiddlewareClientFnOptions<any, any, any, any>,\n) {\n if (opts.method === 'POST') {\n if (opts.data instanceof FormData) {\n opts.data.set(TSS_FORMDATA_CONTEXT, await serialize(opts.context))\n return {\n body: opts.data,\n }\n }\n\n return {\n body: await serializePayload(opts),\n }\n }\n\n return {}\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 const response = await (async () => {\n try {\n return await fn()\n } catch (error) {\n if (error instanceof Response) {\n return error\n }\n\n throw error\n }\n })()\n\n const contentType = response.headers.get('content-type')\n invariant(contentType, 'expected content-type header to be set')\n const serializedByStart = !!response.headers.get(X_TSS_SERIALIZED)\n // If the response is not ok, throw an error\n if (!response.ok) {\n if (serializedByStart && contentType.includes('application/json')) {\n const jsonPayload = await response.json()\n const result = fromCrossJSON(jsonPayload, { plugins: serovalPlugins! })\n throw result\n }\n\n throw new Error(await response.text())\n }\n\n if (serializedByStart) {\n let result\n 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 (contentType.includes('application/json')) {\n const jsonPayload = await response.json()\n result = fromCrossJSON(jsonPayload, { plugins: serovalPlugins! })\n }\n invariant(result, 'expected result to be resolved')\n if (result instanceof Error) {\n throw result\n }\n return result\n }\n\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 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"],"names":[],"mappings":";;;;;AAaA,IAAI,iBAAwD;AAE5D,eAAsB,gBACpB,KACA,MACA,SACA;AACA,MAAI,CAAC,gBAAgB;AACnB,qBAAiB,wBAAA;AAAA,EACnB;AACA,QAAM,SAAS,KAAK,CAAC;AAIrB,MAAI,cAAc,MAAM,KAAK,OAAO,QAAQ;AAC1C,UAAM,QAAQ;AAQd,UAAM,OAAO,MAAM,gBAAgB,WAAW,aAAa;AAG3D,UAAM,UAAU,IAAI,QAAQ;AAAA,MAC1B,kBAAkB;AAAA,MAClB,GAAI,SAAS,YACT;AAAA,QACE,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MAAA,IAEV,CAAA;AAAA,MACJ,GAAI,MAAM,mBAAmB,UACzB,OAAO,YAAY,MAAM,QAAQ,SAAS,IAC1C,MAAM;AAAA,IAAA,CACX;AAGD,QAAI,MAAM,WAAW,OAAO;AAC1B,YAAM,iBAAiB,OAAO;AAAA,QAC5B,SAAS,MAAM,iBAAiB,KAAK;AAAA,MAAA,CACtC;AAED,UAAI,gBAAgB;AAClB,YAAI,IAAI,SAAS,GAAG,GAAG;AACrB,iBAAO,IAAI,cAAc;AAAA,QAC3B,OAAO;AACL,iBAAO,IAAI,cAAc;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AAEA,QAAI,IAAI,SAAS,GAAG,GAAG;AACrB,aAAO;AAAA,IACT,OAAO;AACL,aAAO;AAAA,IACT;AACA,QAAI,MAAM,aAAa,OAAO;AAC5B,aAAO;AAAA,IACT;AAEA,WAAO,MAAM;AAAA,MAAY,YACvB,QAAQ,KAAK;AAAA,QACX,QAAQ,MAAM;AAAA,QACd;AAAA,QACA,QAAQ,MAAM;AAAA,QACd,GAAI,MAAM,yBAAyB,KAAK;AAAA,MAAA,CACzC;AAAA,IAAA;AAAA,EAEL;AAKA,SAAO,MAAM;AAAA,IAAY,MACvB,QAAQ,KAAK;AAAA,MACX,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,QAAQ;AAAA,QACR,gBAAgB;AAAA,MAAA;AAAA,MAElB,MAAM,KAAK,UAAU,IAAI;AAAA,IAAA,CAC1B;AAAA,EAAA;AAEL;AAEA,eAAe,iBACb,MACA;AACA,QAAM,qBAA0B,CAAA;AAChC,MAAI,KAAK,MAAM;AACb,uBAAmB,MAAM,IAAI,KAAK;AAAA,EACpC;AAEA,MAAI,KAAK,WAAW,OAAO,KAAK,KAAK,OAAO,EAAE,SAAS,GAAG;AACxD,uBAAmB,SAAS,IAAI,KAAK;AAAA,EACvC;AAEA,SAAO,UAAU,kBAAkB;AACrC;AAEA,eAAe,UAAU,MAAW;AAClC,SAAO,KAAK;AAAA,IACV,MAAM,QAAQ,QAAQ,YAAY,MAAM,EAAE,SAAS,gBAAiB,CAAC;AAAA,EAAA;AAEzE;AAEA,eAAe,yBACb,MACA;AACA,MAAI,KAAK,WAAW,QAAQ;AAC1B,QAAI,KAAK,gBAAgB,UAAU;AACjC,WAAK,KAAK,IAAI,sBAAsB,MAAM,UAAU,KAAK,OAAO,CAAC;AACjE,aAAO;AAAA,QACL,MAAM,KAAK;AAAA,MAAA;AAAA,IAEf;AAEA,WAAO;AAAA,MACL,MAAM,MAAM,iBAAiB,IAAI;AAAA,IAAA;AAAA,EAErC;AAEA,SAAO,CAAA;AACT;AAUA,eAAe,YAAY,IAA6B;AACtD,QAAM,WAAW,OAAO,YAAY;AAClC,QAAI;AACF,aAAO,MAAM,GAAA;AAAA,IACf,SAAS,OAAO;AACd,UAAI,iBAAiB,UAAU;AAC7B,eAAO;AAAA,MACT;AAEA,YAAM;AAAA,IACR;AAAA,EACF,GAAA;AAEA,QAAM,cAAc,SAAS,QAAQ,IAAI,cAAc;AACvD,YAAU,aAAa,wCAAwC;AAC/D,QAAM,oBAAoB,CAAC,CAAC,SAAS,QAAQ,IAAI,gBAAgB;AAEjE,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,qBAAqB,YAAY,SAAS,kBAAkB,GAAG;AACjE,YAAM,cAAc,MAAM,SAAS,KAAA;AACnC,YAAM,SAAS,cAAc,aAAa,EAAE,SAAS,gBAAiB;AACtE,YAAM;AAAA,IACR;AAEA,UAAM,IAAI,MAAM,MAAM,SAAS,MAAM;AAAA,EACvC;AAEA,MAAI,mBAAmB;AACrB,QAAI;AACJ,QAAI,YAAY,SAAS,sBAAsB,GAAG;AAChD,YAAM,2BAAW,IAAA;AACjB,eAAS,MAAM,wBAAwB;AAAA,QACrC;AAAA,QACA,WAAW,CAAC,QACV,cAAc,KAAK,EAAE,MAAM,SAAS,gBAAiB;AAAA,QACvD,QAAQ,KAAK,OAAO;AAElB,kBAAQ,MAAM,KAAK,KAAK;AAAA,QAC1B;AAAA,MAAA,CACD;AAAA,IACH;AACA,QAAI,YAAY,SAAS,kBAAkB,GAAG;AAC5C,YAAM,cAAc,MAAM,SAAS,KAAA;AACnC,eAAS,cAAc,aAAa,EAAE,SAAS,gBAAiB;AAAA,IAClE;AACA,cAAU,QAAQ,gCAAgC;AAClD,QAAI,kBAAkB,OAAO;AAC3B,YAAM;AAAA,IACR;AACA,WAAO;AAAA,EACT;AAEA,MAAI,YAAY,SAAS,kBAAkB,GAAG;AAC5C,UAAM,cAAc,MAAM,SAAS,KAAA;AACnC,UAAM,WAAW,cAAc,WAAW;AAC1C,QAAI,UAAU;AACZ,YAAM;AAAA,IACR;AACA,QAAI,WAAW,WAAW,GAAG;AAC3B,YAAM;AAAA,IACR;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,eAAe,wBAAwB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,MAAI,CAAC,SAAS,MAAM;AAClB,UAAM,IAAI,MAAM,kBAAkB;AAAA,EACpC;AAEA,QAAM,SAAS,SAAS,KAAK,YAAY,IAAI,kBAAA,CAAmB,EAAE,UAAA;AAElE,MAAI,SAAS;AACb,MAAI,YAAY;AAChB,MAAI;AAEJ,SAAO,CAAC,WAAW;AACjB,UAAM,EAAE,OAAO,KAAA,IAAS,MAAM,OAAO,KAAA;AACrC,QAAI,MAAO,WAAU;AAErB,QAAI,OAAO,WAAW,KAAK,MAAM;AAC/B,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AAGA,QAAI,OAAO,SAAS,IAAI,GAAG;AACzB,YAAM,QAAQ,OAAO,MAAM,IAAI,EAAE,OAAO,OAAO;AAC/C,YAAM,YAAY,MAAM,CAAC;AACzB,UAAI,CAAC,UAAW,OAAM,IAAI,MAAM,iCAAiC;AACjE,oBAAc,KAAK,MAAM,SAAS;AAClC,kBAAY;AACZ,eAAS,MAAM,MAAM,CAAC,EAAE,KAAK,IAAI;AAAA,IACnC,OAAO;AAEL,YAAM,eAAe,OAAO,QAAQ,IAAI;AACxC,UAAI,gBAAgB,GAAG;AACrB,cAAM,OAAO,OAAO,MAAM,GAAG,YAAY,EAAE,KAAA;AAC3C,iBAAS,OAAO,MAAM,eAAe,CAAC;AACtC,YAAI,KAAK,SAAS,GAAG;AACnB,wBAAc,KAAK,MAAM,IAAI;AAC7B,sBAAY;AAAA,QACd;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGC,GAAC,YAAY;AACZ,QAAI;AAEF,aAAO,MAAM;AACX,cAAM,EAAE,OAAO,KAAA,IAAS,MAAM,OAAO,KAAA;AACrC,YAAI,MAAO,WAAU;AAErB,cAAM,cAAc,OAAO,YAAY,IAAI;AAC3C,YAAI,eAAe,GAAG;AACpB,gBAAM,QAAQ,OAAO,MAAM,GAAG,WAAW;AACzC,mBAAS,OAAO,MAAM,cAAc,CAAC;AACrC,gBAAM,QAAQ,MAAM,MAAM,IAAI,EAAE,OAAO,OAAO;AAE9C,qBAAW,QAAQ,OAAO;AACxB,gBAAI;AACF,wBAAU,KAAK,MAAM,IAAI,CAAC;AAAA,YAC5B,SAAS,GAAG;AACV,wBAAU,sBAAsB,IAAI,IAAI,CAAC;AAAA,YAC3C;AAAA,UACF;AAAA,QACF;AAEA,YAAI,MAAM;AACR;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,gBAAU,4BAA4B,GAAG;AAAA,IAC3C;AAAA,EACF,GAAA;AAEA,SAAO,UAAU,WAAW;AAC9B;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-client-core",
3
- "version": "1.132.0-alpha.3",
3
+ "version": "1.132.0-alpha.7",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -48,8 +48,8 @@
48
48
  "seroval-plugins": "^1.3.2",
49
49
  "tiny-invariant": "^1.3.3",
50
50
  "tiny-warning": "^1.0.3",
51
- "@tanstack/router-core": "1.132.0-alpha.3",
52
- "@tanstack/start-storage-context": "1.132.0-alpha.3"
51
+ "@tanstack/start-storage-context": "1.132.0-alpha.4",
52
+ "@tanstack/router-core": "1.132.0-alpha.4"
53
53
  },
54
54
  "scripts": {}
55
55
  }
package/src/constants.ts CHANGED
@@ -1,2 +1,5 @@
1
- export const TSR_FORMDATA_CONTEXT = '__TSR_CONTEXT'
1
+ export const TSS_FORMDATA_CONTEXT = '__TSS_CONTEXT'
2
+ export const TSS_SERVER_FUNCTION = Symbol.for('TSS_SERVER_FUNCTION')
3
+
4
+ export const X_TSS_SERIALIZED = 'x-tss-serialized'
2
5
  export {}
@@ -1,3 +1,4 @@
1
+ import { TSS_SERVER_FUNCTION } from './constants'
1
2
  import { serverFnFetcher } from './serverFnFetcher'
2
3
 
3
4
  let baseUrl: string
@@ -20,5 +21,6 @@ export const createClientRpc = (functionId: string) => {
20
21
  return Object.assign(clientFn, {
21
22
  url,
22
23
  functionId,
24
+ [TSS_SERVER_FUNCTION]: true,
23
25
  })
24
26
  }
package/src/envOnly.ts CHANGED
@@ -2,8 +2,8 @@ type EnvOnlyFn = <TFn extends (...args: Array<any>) => any>(fn: TFn) => TFn
2
2
 
3
3
  // A function that will only be available in the server build
4
4
  // If called on the client, it will throw an error
5
- export const serverOnly: EnvOnlyFn = (fn) => fn
5
+ export const createServerOnlyFn: EnvOnlyFn = (fn) => fn
6
6
 
7
7
  // A function that will only be available in the client build
8
8
  // If called on the server, it will throw an error
9
- export const clientOnly: EnvOnlyFn = (fn) => fn
9
+ export const createClientOnlyFn: EnvOnlyFn = (fn) => fn
package/src/index.tsx CHANGED
@@ -21,7 +21,7 @@ export {
21
21
  type ClientOnlyFn,
22
22
  type IsomorphicFnBase,
23
23
  } from './createIsomorphicFn'
24
- export { serverOnly, clientOnly } from './envOnly'
24
+ export { createServerOnlyFn, createClientOnlyFn } from './envOnly'
25
25
  export { createServerFn } from './createServerFn'
26
26
  export {
27
27
  createMiddleware,
@@ -88,4 +88,8 @@ export { createClientRpc } from './createClientRpc'
88
88
 
89
89
  export { getDefaultSerovalPlugins } from './serializer/getDefaultSerovalPlugins'
90
90
 
91
- export { TSR_FORMDATA_CONTEXT } from './constants'
91
+ export {
92
+ TSS_FORMDATA_CONTEXT,
93
+ TSS_SERVER_FUNCTION,
94
+ X_TSS_SERIALIZED,
95
+ } from './constants'
@@ -1,10 +1,10 @@
1
1
  import { createSerializationAdapter } from '@tanstack/router-core'
2
2
  import { createClientRpc } from '../createClientRpc'
3
+ import { TSS_SERVER_FUNCTION } from '../constants'
3
4
 
4
5
  export const ServerFunctionSerializationAdapter = createSerializationAdapter({
5
6
  key: '$TSS/serverfn',
6
- test: (v): v is { functionId: string } =>
7
- typeof v == 'function' && 'functionId' in v,
7
+ test: (v): v is { functionId: string } => v[TSS_SERVER_FUNCTION],
8
8
  toSerializable: ({ functionId }) => ({ functionId }),
9
9
  fromSerializable: ({ functionId }) => createClientRpc(functionId),
10
10
  })
@@ -20,5 +20,5 @@ export function getDefaultSerovalPlugins() {
20
20
  const adapters = router.options.serializationAdapters as
21
21
  | Array<AnyTransformer>
22
22
  | undefined
23
- return [...defaultSerovalPlugins, ...(adapters?.map(makeSerovalPlugin) ?? [])]
23
+ return [...(adapters?.map(makeSerovalPlugin) ?? []), ...defaultSerovalPlugins]
24
24
  }
@@ -4,10 +4,10 @@ import {
4
4
  isPlainObject,
5
5
  parseRedirect,
6
6
  } from '@tanstack/router-core'
7
- import { fromCrossJSON, fromJSON, toJSONAsync } from 'seroval'
7
+ import { fromCrossJSON, toJSONAsync } from 'seroval'
8
8
  import invariant from 'tiny-invariant'
9
9
  import { getClientSerovalPlugins } from './serializer/getClientSerovalPlugins'
10
- import { TSR_FORMDATA_CONTEXT } from './constants'
10
+ import { TSS_FORMDATA_CONTEXT, X_TSS_SERIALIZED } from './constants'
11
11
  import type { FunctionMiddlewareClientFnOptions } from './createMiddleware'
12
12
  import type { Plugin as SerovalPlugin } from 'seroval'
13
13
 
@@ -125,7 +125,7 @@ async function getFetcherRequestOptions(
125
125
  ) {
126
126
  if (opts.method === 'POST') {
127
127
  if (opts.data instanceof FormData) {
128
- opts.data.set(TSR_FORMDATA_CONTEXT, await serialize(opts.context))
128
+ opts.data.set(TSS_FORMDATA_CONTEXT, await serialize(opts.context))
129
129
  return {
130
130
  body: opts.data,
131
131
  }
@@ -162,12 +162,12 @@ async function getResponse(fn: () => Promise<Response>) {
162
162
 
163
163
  const contentType = response.headers.get('content-type')
164
164
  invariant(contentType, 'expected content-type header to be set')
165
- const serializedByStart = !!response.headers.get('x-tss-serialized')
165
+ const serializedByStart = !!response.headers.get(X_TSS_SERIALIZED)
166
166
  // If the response is not ok, throw an error
167
167
  if (!response.ok) {
168
168
  if (serializedByStart && contentType.includes('application/json')) {
169
169
  const jsonPayload = await response.json()
170
- const result = fromJSON(jsonPayload, { plugins: serovalPlugins! })
170
+ const result = fromCrossJSON(jsonPayload, { plugins: serovalPlugins! })
171
171
  throw result
172
172
  }
173
173
 
@@ -183,14 +183,14 @@ async function getResponse(fn: () => Promise<Response>) {
183
183
  onMessage: (msg) =>
184
184
  fromCrossJSON(msg, { refs, plugins: serovalPlugins! }),
185
185
  onError(msg, error) {
186
- // TODO how could we notify consumer that an error occured?
186
+ // TODO how could we notify consumer that an error occurred?
187
187
  console.error(msg, error)
188
188
  },
189
189
  })
190
190
  }
191
191
  if (contentType.includes('application/json')) {
192
192
  const jsonPayload = await response.json()
193
- result = fromJSON(jsonPayload, { plugins: serovalPlugins! })
193
+ result = fromCrossJSON(jsonPayload, { plugins: serovalPlugins! })
194
194
  }
195
195
  invariant(result, 'expected result to be resolved')
196
196
  if (result instanceof Error) {
@@ -1,5 +1,5 @@
1
1
  import { expectTypeOf, test } from 'vitest'
2
- import { clientOnly, serverOnly } from '../envOnly'
2
+ import { createClientOnlyFn, createServerOnlyFn } from '../envOnly'
3
3
 
4
4
  const inputFn = () => 'output'
5
5
 
@@ -11,24 +11,24 @@ function overloadedFn(input: any) {
11
11
  return input
12
12
  }
13
13
 
14
- test("clientOnly returns the function it's given", () => {
15
- const outputFn = clientOnly(inputFn)
14
+ test("createClientOnlyFn returns the function it's given", () => {
15
+ const outputFn = createClientOnlyFn(inputFn)
16
16
  expectTypeOf(outputFn).toEqualTypeOf<typeof inputFn>()
17
17
 
18
- const genericOutputFn = clientOnly(genericInputFn)
18
+ const genericOutputFn = createClientOnlyFn(genericInputFn)
19
19
  expectTypeOf(genericOutputFn).toEqualTypeOf<typeof genericInputFn>()
20
20
 
21
- const overloadedOutputFn = clientOnly(overloadedFn)
21
+ const overloadedOutputFn = createClientOnlyFn(overloadedFn)
22
22
  expectTypeOf(overloadedOutputFn).toEqualTypeOf<typeof overloadedFn>()
23
23
  })
24
24
 
25
- test("serverOnly returns the function it's given", () => {
26
- const outputFn = serverOnly(inputFn)
25
+ test("createServerOnlyFn returns the function it's given", () => {
26
+ const outputFn = createServerOnlyFn(inputFn)
27
27
  expectTypeOf(outputFn).toEqualTypeOf<typeof inputFn>()
28
28
 
29
- const genericOutputFn = serverOnly(genericInputFn)
29
+ const genericOutputFn = createServerOnlyFn(genericInputFn)
30
30
  expectTypeOf(genericOutputFn).toEqualTypeOf<typeof genericInputFn>()
31
31
 
32
- const overloadedOutputFn = serverOnly(overloadedFn)
32
+ const overloadedOutputFn = createServerOnlyFn(overloadedFn)
33
33
  expectTypeOf(overloadedOutputFn).toEqualTypeOf<typeof overloadedFn>()
34
34
  })