@tanstack/router-core 1.132.21 → 1.132.27

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 (36) hide show
  1. package/dist/cjs/rewrite.cjs +10 -5
  2. package/dist/cjs/rewrite.cjs.map +1 -1
  3. package/dist/cjs/router.cjs +5 -1
  4. package/dist/cjs/router.cjs.map +1 -1
  5. package/dist/cjs/ssr/createRequestHandler.cjs +3 -1
  6. package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
  7. package/dist/cjs/ssr/serializer/transformer.cjs +7 -3
  8. package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -1
  9. package/dist/cjs/ssr/serializer/transformer.d.cts +15 -14
  10. package/dist/cjs/ssr/server.cjs +1 -0
  11. package/dist/cjs/ssr/server.cjs.map +1 -1
  12. package/dist/cjs/ssr/server.d.cts +1 -1
  13. package/dist/cjs/ssr/ssr-server.cjs +16 -0
  14. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  15. package/dist/cjs/ssr/ssr-server.d.cts +1 -0
  16. package/dist/esm/rewrite.js +10 -5
  17. package/dist/esm/rewrite.js.map +1 -1
  18. package/dist/esm/router.js +5 -1
  19. package/dist/esm/router.js.map +1 -1
  20. package/dist/esm/ssr/createRequestHandler.js +4 -2
  21. package/dist/esm/ssr/createRequestHandler.js.map +1 -1
  22. package/dist/esm/ssr/serializer/transformer.d.ts +15 -14
  23. package/dist/esm/ssr/serializer/transformer.js +7 -3
  24. package/dist/esm/ssr/serializer/transformer.js.map +1 -1
  25. package/dist/esm/ssr/server.d.ts +1 -1
  26. package/dist/esm/ssr/server.js +2 -1
  27. package/dist/esm/ssr/ssr-server.d.ts +1 -0
  28. package/dist/esm/ssr/ssr-server.js +17 -1
  29. package/dist/esm/ssr/ssr-server.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/rewrite.ts +20 -5
  32. package/src/router.ts +5 -1
  33. package/src/ssr/createRequestHandler.ts +3 -2
  34. package/src/ssr/serializer/transformer.ts +61 -23
  35. package/src/ssr/server.ts +1 -1
  36. package/src/ssr/ssr-server.ts +14 -0
@@ -14,11 +14,13 @@ export interface DefaultSerializable {
14
14
  export interface SerializableExtensions extends DefaultSerializable {
15
15
  }
16
16
  export type Serializable = SerializableExtensions[keyof SerializableExtensions];
17
- export declare function createSerializationAdapter<TInput = unknown, TOutput = unknown>(opts: CreateSerializationAdapterOptions<TInput, TOutput>): SerializationAdapter<TInput, TOutput>;
18
- export interface CreateSerializationAdapterOptions<TInput, TOutput> {
17
+ export type UnionizeSerializationAdaptersInput<TAdapters extends ReadonlyArray<AnySerializationAdapter>> = TAdapters[number]['~types']['input'];
18
+ export declare function createSerializationAdapter<TInput = unknown, TOutput = unknown, const TExtendsAdapters extends ReadonlyArray<AnySerializationAdapter> | never = never>(opts: CreateSerializationAdapterOptions<TInput, TOutput, TExtendsAdapters>): SerializationAdapter<TInput, TOutput, TExtendsAdapters>;
19
+ export interface CreateSerializationAdapterOptions<TInput, TOutput, TExtendsAdapters extends ReadonlyArray<AnySerializationAdapter> | never> {
19
20
  key: string;
21
+ extends?: TExtendsAdapters;
20
22
  test: (value: unknown) => value is TInput;
21
- toSerializable: (value: TInput) => ValidateSerializable<TOutput, Serializable>;
23
+ toSerializable: (value: TInput) => ValidateSerializable<TOutput, Serializable | UnionizeSerializationAdaptersInput<TExtendsAdapters>>;
22
24
  fromSerializable: (value: TOutput) => TInput;
23
25
  }
24
26
  export type ValidateSerializable<T, TSerializable> = T extends ReadonlyArray<unknown> ? ResolveArrayShape<T, TSerializable, 'input'> : T extends TSerializable ? T : T extends (...args: Array<any>) => any ? 'Function is not serializable' : T extends Promise<any> ? ValidateSerializablePromise<T, TSerializable> : T extends ReadableStream<any> ? ValidateReadableStream<T, TSerializable> : T extends Set<any> ? ValidateSerializableSet<T, TSerializable> : T extends Map<any, any> ? ValidateSerializableMap<T, TSerializable> : {
@@ -34,25 +36,24 @@ export interface DefaultSerializerExtensions {
34
36
  }
35
37
  export interface SerializerExtensions extends DefaultSerializerExtensions {
36
38
  }
37
- export interface SerializationAdapter<TInput, TOutput> {
38
- '~types': SerializationAdapterTypes<TInput, TOutput>;
39
+ export interface SerializationAdapter<TInput, TOutput, TExtendsAdapters extends ReadonlyArray<AnySerializationAdapter>> {
40
+ '~types': SerializationAdapterTypes<TInput, TOutput, TExtendsAdapters>;
39
41
  key: string;
42
+ extends?: TExtendsAdapters;
40
43
  test: (value: unknown) => value is TInput;
41
44
  toSerializable: (value: TInput) => TOutput;
42
45
  fromSerializable: (value: TOutput) => TInput;
43
- makePlugin: (options: {
44
- didRun: boolean;
45
- }) => Plugin<TInput, SerovalNode>;
46
46
  }
47
- export interface SerializationAdapterTypes<TInput, TOutput> {
48
- input: TInput;
47
+ export interface SerializationAdapterTypes<TInput, TOutput, TExtendsAdapters extends ReadonlyArray<AnySerializationAdapter>> {
48
+ input: TInput | UnionizeSerializationAdaptersInput<TExtendsAdapters>;
49
49
  output: TOutput;
50
+ extends: TExtendsAdapters;
50
51
  }
51
- export type AnySerializationAdapter = SerializationAdapter<any, any>;
52
- export declare function makeSsrSerovalPlugin<TInput, TOutput>(serializationAdapter: SerializationAdapter<TInput, TOutput>, options: {
52
+ export type AnySerializationAdapter = SerializationAdapter<any, any, any>;
53
+ export declare function makeSsrSerovalPlugin(serializationAdapter: AnySerializationAdapter, options: {
53
54
  didRun: boolean;
54
- }): Plugin<TInput, SerovalNode>;
55
- export declare function makeSerovalPlugin<TInput, TOutput>(serializationAdapter: SerializationAdapter<TInput, TOutput>): Plugin<TInput, SerovalNode>;
55
+ }): Plugin<any, SerovalNode>;
56
+ export declare function makeSerovalPlugin(serializationAdapter: AnySerializationAdapter): Plugin<any, SerovalNode>;
56
57
  export type ValidateSerializableInput<TRegister, T> = ValidateSerializable<T, RegisteredSerializableInput<TRegister>>;
57
58
  export type RegisteredSerializableInput<TRegister> = (unknown extends RegisteredSerializationAdapters<TRegister> ? never : RegisteredSerializationAdapters<TRegister> extends ReadonlyArray<AnySerializationAdapter> ? RegisteredSerializationAdapters<TRegister>[number]['~types']['input'] : never) | Serializable;
58
59
  export type RegisteredSerializationAdapters<TRegister> = RegisteredConfigType<TRegister, 'serializationAdapters'>;
@@ -6,6 +6,9 @@ function createSerializationAdapter(opts) {
6
6
  function makeSsrSerovalPlugin(serializationAdapter, options) {
7
7
  return createPlugin({
8
8
  tag: "$TSR/t/" + serializationAdapter.key,
9
+ extends: serializationAdapter.extends ? serializationAdapter.extends.map(
10
+ (ext) => makeSsrSerovalPlugin(ext, options)
11
+ ) : void 0,
9
12
  test: serializationAdapter.test,
10
13
  parse: {
11
14
  stream(value, ctx) {
@@ -23,6 +26,9 @@ function makeSsrSerovalPlugin(serializationAdapter, options) {
23
26
  function makeSerovalPlugin(serializationAdapter) {
24
27
  return createPlugin({
25
28
  tag: "$TSR/t/" + serializationAdapter.key,
29
+ extends: serializationAdapter.extends ? serializationAdapter.extends.map(
30
+ makeSerovalPlugin
31
+ ) : void 0,
26
32
  test: serializationAdapter.test,
27
33
  parse: {
28
34
  sync(value, ctx) {
@@ -38,9 +44,7 @@ function makeSerovalPlugin(serializationAdapter) {
38
44
  // we don't generate JS code outside of SSR (for now)
39
45
  serialize: void 0,
40
46
  deserialize(node, ctx) {
41
- return serializationAdapter.fromSerializable(
42
- ctx.deserialize(node)
43
- );
47
+ return serializationAdapter.fromSerializable(ctx.deserialize(node));
44
48
  }
45
49
  });
46
50
  }
@@ -1 +1 @@
1
- {"version":3,"file":"transformer.js","sources":["../../../../src/ssr/serializer/transformer.ts"],"sourcesContent":["import { createPlugin } from 'seroval'\nimport { GLOBAL_TSR } from '../constants'\nimport type { Plugin, SerovalNode } from 'seroval'\nimport type {\n RegisteredConfigType,\n RegisteredSsr,\n SSROption,\n} from '../../router'\nimport type { LooseReturnType } from '../../utils'\nimport type { AnyRoute, ResolveAllSSR } from '../../route'\n\nexport interface DefaultSerializable {\n number: number\n string: string\n boolean: boolean\n null: null\n undefined: undefined\n bigint: bigint\n Date: Date\n}\n\nexport interface SerializableExtensions extends DefaultSerializable {}\n\nexport type Serializable = SerializableExtensions[keyof SerializableExtensions]\n\nexport function createSerializationAdapter<\n TInput = unknown,\n TOutput = unknown /* we need to check that this type is actually serializable taking into account all seroval native types and any custom plugin WE=router/start add!!! */,\n>(\n opts: CreateSerializationAdapterOptions<TInput, TOutput>,\n): SerializationAdapter<TInput, TOutput> {\n return opts as unknown as SerializationAdapter<TInput, TOutput>\n}\n\nexport interface CreateSerializationAdapterOptions<TInput, TOutput> {\n key: string\n test: (value: unknown) => value is TInput\n toSerializable: (value: TInput) => ValidateSerializable<TOutput, Serializable>\n fromSerializable: (value: TOutput) => TInput\n}\n\nexport type ValidateSerializable<T, TSerializable> =\n T extends ReadonlyArray<unknown>\n ? ResolveArrayShape<T, TSerializable, 'input'>\n : T extends TSerializable\n ? T\n : T extends (...args: Array<any>) => any\n ? 'Function is not serializable'\n : T extends Promise<any>\n ? ValidateSerializablePromise<T, TSerializable>\n : T extends ReadableStream<any>\n ? ValidateReadableStream<T, TSerializable>\n : T extends Set<any>\n ? ValidateSerializableSet<T, TSerializable>\n : T extends Map<any, any>\n ? ValidateSerializableMap<T, TSerializable>\n : { [K in keyof T]: ValidateSerializable<T[K], TSerializable> }\n\nexport type ValidateSerializablePromise<T, TSerializable> =\n T extends Promise<infer TAwaited>\n ? Promise<ValidateSerializable<TAwaited, TSerializable>>\n : never\n\nexport type ValidateReadableStream<T, TSerializable> =\n T extends ReadableStream<infer TStreamed>\n ? ReadableStream<ValidateSerializable<TStreamed, TSerializable>>\n : never\n\nexport type ValidateSerializableSet<T, TSerializable> =\n T extends Set<infer TItem>\n ? Set<ValidateSerializable<TItem, TSerializable>>\n : never\n\nexport type ValidateSerializableMap<T, TSerializable> =\n T extends Map<infer TKey, infer TValue>\n ? Map<\n ValidateSerializable<TKey, TSerializable>,\n ValidateSerializable<TValue, TSerializable>\n >\n : never\n\nexport type RegisteredReadableStream =\n unknown extends SerializerExtensions['ReadableStream']\n ? never\n : SerializerExtensions['ReadableStream']\n\nexport interface DefaultSerializerExtensions {\n ReadableStream: unknown\n}\n\nexport interface SerializerExtensions extends DefaultSerializerExtensions {}\n\nexport interface SerializationAdapter<TInput, TOutput> {\n '~types': SerializationAdapterTypes<TInput, TOutput>\n key: string\n test: (value: unknown) => value is TInput\n toSerializable: (value: TInput) => TOutput\n fromSerializable: (value: TOutput) => TInput\n makePlugin: (options: { didRun: boolean }) => Plugin<TInput, SerovalNode>\n}\n\nexport interface SerializationAdapterTypes<TInput, TOutput> {\n input: TInput\n output: TOutput\n}\n\nexport type AnySerializationAdapter = SerializationAdapter<any, any>\n\nexport function makeSsrSerovalPlugin<TInput, TOutput>(\n serializationAdapter: SerializationAdapter<TInput, TOutput>,\n options: { didRun: boolean },\n) {\n return createPlugin<TInput, SerovalNode>({\n tag: '$TSR/t/' + serializationAdapter.key,\n test: serializationAdapter.test,\n parse: {\n stream(value, ctx) {\n return ctx.parse(serializationAdapter.toSerializable(value))\n },\n },\n serialize(node, ctx) {\n options.didRun = true\n return (\n GLOBAL_TSR +\n '.t.get(\"' +\n serializationAdapter.key +\n '\")(' +\n ctx.serialize(node) +\n ')'\n )\n },\n // we never deserialize on the server during SSR\n deserialize: undefined as never,\n })\n}\n\nexport function makeSerovalPlugin<TInput, TOutput>(\n serializationAdapter: SerializationAdapter<TInput, TOutput>,\n) {\n return createPlugin<TInput, SerovalNode>({\n tag: '$TSR/t/' + serializationAdapter.key,\n test: serializationAdapter.test,\n parse: {\n sync(value, ctx) {\n return ctx.parse(serializationAdapter.toSerializable(value))\n },\n async async(value, ctx) {\n return await ctx.parse(serializationAdapter.toSerializable(value))\n },\n stream(value, ctx) {\n return ctx.parse(serializationAdapter.toSerializable(value))\n },\n },\n // we don't generate JS code outside of SSR (for now)\n serialize: undefined as never,\n deserialize(node, ctx) {\n return serializationAdapter.fromSerializable(\n ctx.deserialize(node) as TOutput,\n )\n },\n })\n}\n\nexport type ValidateSerializableInput<TRegister, T> = ValidateSerializable<\n T,\n RegisteredSerializableInput<TRegister>\n>\n\nexport type RegisteredSerializableInput<TRegister> =\n | (unknown extends RegisteredSerializationAdapters<TRegister>\n ? never\n : RegisteredSerializationAdapters<TRegister> extends ReadonlyArray<AnySerializationAdapter>\n ? RegisteredSerializationAdapters<TRegister>[number]['~types']['input']\n : never)\n | Serializable\n\nexport type RegisteredSerializationAdapters<TRegister> = RegisteredConfigType<\n TRegister,\n 'serializationAdapters'\n>\n\nexport type ValidateSerializableInputResult<TRegister, T> =\n ValidateSerializableResult<T, RegisteredSerializableInput<TRegister>>\n\nexport type ValidateSerializableResult<T, TSerializable> =\n T extends ReadonlyArray<unknown>\n ? ResolveArrayShape<T, TSerializable, 'result'>\n : T extends TSerializable\n ? T\n : unknown extends SerializerExtensions['ReadableStream']\n ? { [K in keyof T]: ValidateSerializableResult<T[K], TSerializable> }\n : T extends SerializerExtensions['ReadableStream']\n ? ReadableStream\n : { [K in keyof T]: ValidateSerializableResult<T[K], TSerializable> }\n\nexport type RegisteredSSROption<TRegister> =\n unknown extends RegisteredConfigType<TRegister, 'defaultSsr'>\n ? SSROption\n : RegisteredConfigType<TRegister, 'defaultSsr'>\n\nexport type ValidateSerializableLifecycleResult<\n TRegister,\n TParentRoute extends AnyRoute,\n TSSR,\n TFn,\n> =\n false extends RegisteredSsr<TRegister>\n ? any\n : ValidateSerializableLifecycleResultSSR<\n TRegister,\n TParentRoute,\n TSSR,\n TFn\n > extends infer TInput\n ? TInput\n : never\n\nexport type ValidateSerializableLifecycleResultSSR<\n TRegister,\n TParentRoute extends AnyRoute,\n TSSR,\n TFn,\n> =\n ResolveAllSSR<TParentRoute, TSSR> extends false\n ? any\n : RegisteredSSROption<TRegister> extends false\n ? any\n : ValidateSerializableInput<TRegister, LooseReturnType<TFn>>\n\ntype ResolveArrayShape<\n T extends ReadonlyArray<unknown>,\n TSerializable,\n TMode extends 'input' | 'result',\n> = number extends T['length']\n ? T extends Array<infer U>\n ? Array<ArrayModeResult<TMode, U, TSerializable>>\n : ReadonlyArray<ArrayModeResult<TMode, T[number], TSerializable>>\n : ResolveTupleShape<T, TSerializable, TMode>\n\ntype ResolveTupleShape<\n T extends ReadonlyArray<unknown>,\n TSerializable,\n TMode extends 'input' | 'result',\n> = T extends readonly [infer THead, ...infer TTail]\n ? readonly [\n ArrayModeResult<TMode, THead, TSerializable>,\n ...ResolveTupleShape<Readonly<TTail>, TSerializable, TMode>,\n ]\n : T\n\ntype ArrayModeResult<\n TMode extends 'input' | 'result',\n TValue,\n TSerializable,\n> = TMode extends 'input'\n ? ValidateSerializable<TValue, TSerializable>\n : ValidateSerializableResult<TValue, TSerializable>\n"],"names":[],"mappings":";;AAyBO,SAAS,2BAId,MACuC;AACvC,SAAO;AACT;AA4EO,SAAS,qBACd,sBACA,SACA;AACA,SAAO,aAAkC;AAAA,IACvC,KAAK,YAAY,qBAAqB;AAAA,IACtC,MAAM,qBAAqB;AAAA,IAC3B,OAAO;AAAA,MACL,OAAO,OAAO,KAAK;AACjB,eAAO,IAAI,MAAM,qBAAqB,eAAe,KAAK,CAAC;AAAA,MAC7D;AAAA,IAAA;AAAA,IAEF,UAAU,MAAM,KAAK;AACnB,cAAQ,SAAS;AACjB,aACE,aACA,aACA,qBAAqB,MACrB,QACA,IAAI,UAAU,IAAI,IAClB;AAAA,IAEJ;AAAA;AAAA,IAEA,aAAa;AAAA,EAAA,CACd;AACH;AAEO,SAAS,kBACd,sBACA;AACA,SAAO,aAAkC;AAAA,IACvC,KAAK,YAAY,qBAAqB;AAAA,IACtC,MAAM,qBAAqB;AAAA,IAC3B,OAAO;AAAA,MACL,KAAK,OAAO,KAAK;AACf,eAAO,IAAI,MAAM,qBAAqB,eAAe,KAAK,CAAC;AAAA,MAC7D;AAAA,MACA,MAAM,MAAM,OAAO,KAAK;AACtB,eAAO,MAAM,IAAI,MAAM,qBAAqB,eAAe,KAAK,CAAC;AAAA,MACnE;AAAA,MACA,OAAO,OAAO,KAAK;AACjB,eAAO,IAAI,MAAM,qBAAqB,eAAe,KAAK,CAAC;AAAA,MAC7D;AAAA,IAAA;AAAA;AAAA,IAGF,WAAW;AAAA,IACX,YAAY,MAAM,KAAK;AACrB,aAAO,qBAAqB;AAAA,QAC1B,IAAI,YAAY,IAAI;AAAA,MAAA;AAAA,IAExB;AAAA,EAAA,CACD;AACH;"}
1
+ {"version":3,"file":"transformer.js","sources":["../../../../src/ssr/serializer/transformer.ts"],"sourcesContent":["import { createPlugin } from 'seroval'\nimport { GLOBAL_TSR } from '../constants'\nimport type { Plugin, SerovalNode } from 'seroval'\nimport type {\n RegisteredConfigType,\n RegisteredSsr,\n SSROption,\n} from '../../router'\nimport type { LooseReturnType } from '../../utils'\nimport type { AnyRoute, ResolveAllSSR } from '../../route'\n\nexport interface DefaultSerializable {\n number: number\n string: string\n boolean: boolean\n null: null\n undefined: undefined\n bigint: bigint\n Date: Date\n}\n\nexport interface SerializableExtensions extends DefaultSerializable {}\n\nexport type Serializable = SerializableExtensions[keyof SerializableExtensions]\n\nexport type UnionizeSerializationAdaptersInput<\n TAdapters extends ReadonlyArray<AnySerializationAdapter>,\n> = TAdapters[number]['~types']['input']\n\nexport function createSerializationAdapter<\n TInput = unknown,\n TOutput = unknown,\n const TExtendsAdapters extends\n | ReadonlyArray<AnySerializationAdapter>\n | never = never,\n>(\n opts: CreateSerializationAdapterOptions<TInput, TOutput, TExtendsAdapters>,\n): SerializationAdapter<TInput, TOutput, TExtendsAdapters> {\n return opts as unknown as SerializationAdapter<\n TInput,\n TOutput,\n TExtendsAdapters\n >\n}\n\nexport interface CreateSerializationAdapterOptions<\n TInput,\n TOutput,\n TExtendsAdapters extends ReadonlyArray<AnySerializationAdapter> | never,\n> {\n key: string\n extends?: TExtendsAdapters\n test: (value: unknown) => value is TInput\n toSerializable: (\n value: TInput,\n ) => ValidateSerializable<\n TOutput,\n Serializable | UnionizeSerializationAdaptersInput<TExtendsAdapters>\n >\n fromSerializable: (value: TOutput) => TInput\n}\n\nexport type ValidateSerializable<T, TSerializable> =\n T extends ReadonlyArray<unknown>\n ? ResolveArrayShape<T, TSerializable, 'input'>\n : T extends TSerializable\n ? T\n : T extends (...args: Array<any>) => any\n ? 'Function is not serializable'\n : T extends Promise<any>\n ? ValidateSerializablePromise<T, TSerializable>\n : T extends ReadableStream<any>\n ? ValidateReadableStream<T, TSerializable>\n : T extends Set<any>\n ? ValidateSerializableSet<T, TSerializable>\n : T extends Map<any, any>\n ? ValidateSerializableMap<T, TSerializable>\n : { [K in keyof T]: ValidateSerializable<T[K], TSerializable> }\n\nexport type ValidateSerializablePromise<T, TSerializable> =\n T extends Promise<infer TAwaited>\n ? Promise<ValidateSerializable<TAwaited, TSerializable>>\n : never\n\nexport type ValidateReadableStream<T, TSerializable> =\n T extends ReadableStream<infer TStreamed>\n ? ReadableStream<ValidateSerializable<TStreamed, TSerializable>>\n : never\n\nexport type ValidateSerializableSet<T, TSerializable> =\n T extends Set<infer TItem>\n ? Set<ValidateSerializable<TItem, TSerializable>>\n : never\n\nexport type ValidateSerializableMap<T, TSerializable> =\n T extends Map<infer TKey, infer TValue>\n ? Map<\n ValidateSerializable<TKey, TSerializable>,\n ValidateSerializable<TValue, TSerializable>\n >\n : never\n\nexport type RegisteredReadableStream =\n unknown extends SerializerExtensions['ReadableStream']\n ? never\n : SerializerExtensions['ReadableStream']\n\nexport interface DefaultSerializerExtensions {\n ReadableStream: unknown\n}\n\nexport interface SerializerExtensions extends DefaultSerializerExtensions {}\n\nexport interface SerializationAdapter<\n TInput,\n TOutput,\n TExtendsAdapters extends ReadonlyArray<AnySerializationAdapter>,\n> {\n '~types': SerializationAdapterTypes<TInput, TOutput, TExtendsAdapters>\n key: string\n extends?: TExtendsAdapters\n test: (value: unknown) => value is TInput\n toSerializable: (value: TInput) => TOutput\n fromSerializable: (value: TOutput) => TInput\n}\n\nexport interface SerializationAdapterTypes<\n TInput,\n TOutput,\n TExtendsAdapters extends ReadonlyArray<AnySerializationAdapter>,\n> {\n input: TInput | UnionizeSerializationAdaptersInput<TExtendsAdapters>\n output: TOutput\n extends: TExtendsAdapters\n}\n\nexport type AnySerializationAdapter = SerializationAdapter<any, any, any>\n\nexport function makeSsrSerovalPlugin(\n serializationAdapter: AnySerializationAdapter,\n options: { didRun: boolean },\n): Plugin<any, SerovalNode> {\n return createPlugin<any, SerovalNode>({\n tag: '$TSR/t/' + serializationAdapter.key,\n extends: serializationAdapter.extends\n ? (serializationAdapter.extends as Array<AnySerializationAdapter>).map(\n (ext) => makeSsrSerovalPlugin(ext, options),\n )\n : undefined,\n test: serializationAdapter.test,\n parse: {\n stream(value, ctx) {\n return ctx.parse(serializationAdapter.toSerializable(value))\n },\n },\n serialize(node, ctx) {\n options.didRun = true\n return (\n GLOBAL_TSR +\n '.t.get(\"' +\n serializationAdapter.key +\n '\")(' +\n ctx.serialize(node) +\n ')'\n )\n },\n // we never deserialize on the server during SSR\n deserialize: undefined as never,\n })\n}\n\nexport function makeSerovalPlugin(\n serializationAdapter: AnySerializationAdapter,\n): Plugin<any, SerovalNode> {\n return createPlugin<any, SerovalNode>({\n tag: '$TSR/t/' + serializationAdapter.key,\n extends: serializationAdapter.extends\n ? (serializationAdapter.extends as Array<AnySerializationAdapter>).map(\n makeSerovalPlugin,\n )\n : undefined,\n test: serializationAdapter.test,\n parse: {\n sync(value, ctx) {\n return ctx.parse(serializationAdapter.toSerializable(value))\n },\n async async(value, ctx) {\n return await ctx.parse(serializationAdapter.toSerializable(value))\n },\n stream(value, ctx) {\n return ctx.parse(serializationAdapter.toSerializable(value))\n },\n },\n // we don't generate JS code outside of SSR (for now)\n serialize: undefined as never,\n deserialize(node, ctx) {\n return serializationAdapter.fromSerializable(ctx.deserialize(node))\n },\n })\n}\n\nexport type ValidateSerializableInput<TRegister, T> = ValidateSerializable<\n T,\n RegisteredSerializableInput<TRegister>\n>\n\nexport type RegisteredSerializableInput<TRegister> =\n | (unknown extends RegisteredSerializationAdapters<TRegister>\n ? never\n : RegisteredSerializationAdapters<TRegister> extends ReadonlyArray<AnySerializationAdapter>\n ? RegisteredSerializationAdapters<TRegister>[number]['~types']['input']\n : never)\n | Serializable\n\nexport type RegisteredSerializationAdapters<TRegister> = RegisteredConfigType<\n TRegister,\n 'serializationAdapters'\n>\n\nexport type ValidateSerializableInputResult<TRegister, T> =\n ValidateSerializableResult<T, RegisteredSerializableInput<TRegister>>\n\nexport type ValidateSerializableResult<T, TSerializable> =\n T extends ReadonlyArray<unknown>\n ? ResolveArrayShape<T, TSerializable, 'result'>\n : T extends TSerializable\n ? T\n : unknown extends SerializerExtensions['ReadableStream']\n ? { [K in keyof T]: ValidateSerializableResult<T[K], TSerializable> }\n : T extends SerializerExtensions['ReadableStream']\n ? ReadableStream\n : { [K in keyof T]: ValidateSerializableResult<T[K], TSerializable> }\n\nexport type RegisteredSSROption<TRegister> =\n unknown extends RegisteredConfigType<TRegister, 'defaultSsr'>\n ? SSROption\n : RegisteredConfigType<TRegister, 'defaultSsr'>\n\nexport type ValidateSerializableLifecycleResult<\n TRegister,\n TParentRoute extends AnyRoute,\n TSSR,\n TFn,\n> =\n false extends RegisteredSsr<TRegister>\n ? any\n : ValidateSerializableLifecycleResultSSR<\n TRegister,\n TParentRoute,\n TSSR,\n TFn\n > extends infer TInput\n ? TInput\n : never\n\nexport type ValidateSerializableLifecycleResultSSR<\n TRegister,\n TParentRoute extends AnyRoute,\n TSSR,\n TFn,\n> =\n ResolveAllSSR<TParentRoute, TSSR> extends false\n ? any\n : RegisteredSSROption<TRegister> extends false\n ? any\n : ValidateSerializableInput<TRegister, LooseReturnType<TFn>>\n\ntype ResolveArrayShape<\n T extends ReadonlyArray<unknown>,\n TSerializable,\n TMode extends 'input' | 'result',\n> = number extends T['length']\n ? T extends Array<infer U>\n ? Array<ArrayModeResult<TMode, U, TSerializable>>\n : ReadonlyArray<ArrayModeResult<TMode, T[number], TSerializable>>\n : ResolveTupleShape<T, TSerializable, TMode>\n\ntype ResolveTupleShape<\n T extends ReadonlyArray<unknown>,\n TSerializable,\n TMode extends 'input' | 'result',\n> = T extends readonly [infer THead, ...infer TTail]\n ? readonly [\n ArrayModeResult<TMode, THead, TSerializable>,\n ...ResolveTupleShape<Readonly<TTail>, TSerializable, TMode>,\n ]\n : T\n\ntype ArrayModeResult<\n TMode extends 'input' | 'result',\n TValue,\n TSerializable,\n> = TMode extends 'input'\n ? ValidateSerializable<TValue, TSerializable>\n : ValidateSerializableResult<TValue, TSerializable>\n"],"names":[],"mappings":";;AA6BO,SAAS,2BAOd,MACyD;AACzD,SAAO;AAKT;AA+FO,SAAS,qBACd,sBACA,SAC0B;AAC1B,SAAO,aAA+B;AAAA,IACpC,KAAK,YAAY,qBAAqB;AAAA,IACtC,SAAS,qBAAqB,UACzB,qBAAqB,QAA2C;AAAA,MAC/D,CAAC,QAAQ,qBAAqB,KAAK,OAAO;AAAA,IAAA,IAE5C;AAAA,IACJ,MAAM,qBAAqB;AAAA,IAC3B,OAAO;AAAA,MACL,OAAO,OAAO,KAAK;AACjB,eAAO,IAAI,MAAM,qBAAqB,eAAe,KAAK,CAAC;AAAA,MAC7D;AAAA,IAAA;AAAA,IAEF,UAAU,MAAM,KAAK;AACnB,cAAQ,SAAS;AACjB,aACE,aACA,aACA,qBAAqB,MACrB,QACA,IAAI,UAAU,IAAI,IAClB;AAAA,IAEJ;AAAA;AAAA,IAEA,aAAa;AAAA,EAAA,CACd;AACH;AAEO,SAAS,kBACd,sBAC0B;AAC1B,SAAO,aAA+B;AAAA,IACpC,KAAK,YAAY,qBAAqB;AAAA,IACtC,SAAS,qBAAqB,UACzB,qBAAqB,QAA2C;AAAA,MAC/D;AAAA,IAAA,IAEF;AAAA,IACJ,MAAM,qBAAqB;AAAA,IAC3B,OAAO;AAAA,MACL,KAAK,OAAO,KAAK;AACf,eAAO,IAAI,MAAM,qBAAqB,eAAe,KAAK,CAAC;AAAA,MAC7D;AAAA,MACA,MAAM,MAAM,OAAO,KAAK;AACtB,eAAO,MAAM,IAAI,MAAM,qBAAqB,eAAe,KAAK,CAAC;AAAA,MACnE;AAAA,MACA,OAAO,OAAO,KAAK;AACjB,eAAO,IAAI,MAAM,qBAAqB,eAAe,KAAK,CAAC;AAAA,MAC7D;AAAA,IAAA;AAAA;AAAA,IAGF,WAAW;AAAA,IACX,YAAY,MAAM,KAAK;AACrB,aAAO,qBAAqB,iBAAiB,IAAI,YAAY,IAAI,CAAC;AAAA,IACpE;AAAA,EAAA,CACD;AACH;"}
@@ -3,4 +3,4 @@ export type { RequestHandler } from './createRequestHandler.js';
3
3
  export { defineHandlerCallback } from './handlerCallback.js';
4
4
  export type { HandlerCallback } from './handlerCallback.js';
5
5
  export { transformPipeableStreamWithRouter, transformStreamWithRouter, transformReadableStreamWithRouter, } from './transformStreamWithRouter.js';
6
- export { attachRouterServerSsrUtils } from './ssr-server.js';
6
+ export { attachRouterServerSsrUtils, getOrigin } from './ssr-server.js';
@@ -1,11 +1,12 @@
1
1
  import { createRequestHandler } from "./createRequestHandler.js";
2
2
  import { defineHandlerCallback } from "./handlerCallback.js";
3
3
  import { transformPipeableStreamWithRouter, transformReadableStreamWithRouter, transformStreamWithRouter } from "./transformStreamWithRouter.js";
4
- import { attachRouterServerSsrUtils } from "./ssr-server.js";
4
+ import { attachRouterServerSsrUtils, getOrigin } from "./ssr-server.js";
5
5
  export {
6
6
  attachRouterServerSsrUtils,
7
7
  createRequestHandler,
8
8
  defineHandlerCallback,
9
+ getOrigin,
9
10
  transformPipeableStreamWithRouter,
10
11
  transformReadableStreamWithRouter,
11
12
  transformStreamWithRouter
@@ -18,3 +18,4 @@ export declare function attachRouterServerSsrUtils({ router, manifest, }: {
18
18
  router: AnyRouter;
19
19
  manifest: Manifest | undefined;
20
20
  }): void;
21
+ export declare function getOrigin(request: Request): string;
@@ -105,8 +105,24 @@ function attachRouterServerSsrUtils({
105
105
  }
106
106
  };
107
107
  }
108
+ function getOrigin(request) {
109
+ const originHeader = request.headers.get("Origin");
110
+ if (originHeader) {
111
+ try {
112
+ new URL(originHeader);
113
+ return originHeader;
114
+ } catch {
115
+ }
116
+ }
117
+ try {
118
+ return new URL(request.url).origin;
119
+ } catch {
120
+ }
121
+ return "http://localhost";
122
+ }
108
123
  export {
109
124
  attachRouterServerSsrUtils,
110
- dehydrateMatch
125
+ dehydrateMatch,
126
+ getOrigin
111
127
  };
112
128
  //# sourceMappingURL=ssr-server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-server.js","sources":["../../../src/ssr/ssr-server.ts"],"sourcesContent":["import { crossSerializeStream, getCrossReferenceHeader } from 'seroval'\nimport invariant from 'tiny-invariant'\nimport { createControlledPromise } from '../utils'\nimport minifiedTsrBootStrapScript from './tsrScript?script-string'\nimport { GLOBAL_TSR } from './constants'\nimport { defaultSerovalPlugins } from './serializer/seroval-plugins'\nimport { makeSsrSerovalPlugin } from './serializer/transformer'\nimport type { AnyRouter } from '../router'\nimport type { DehydratedMatch } from './ssr-client'\nimport type { DehydratedRouter } from './client'\nimport type { AnyRouteMatch } from '../Matches'\nimport type { Manifest } from '../manifest'\nimport type { AnySerializationAdapter } from './serializer/transformer'\n\ndeclare module '../router' {\n interface ServerSsr {\n setRenderFinished: () => void\n }\n interface RouterEvents {\n onInjectedHtml: {\n type: 'onInjectedHtml'\n promise: Promise<string>\n }\n }\n}\n\nconst SCOPE_ID = 'tsr'\n\nexport function dehydrateMatch(match: AnyRouteMatch): DehydratedMatch {\n const dehydratedMatch: DehydratedMatch = {\n i: match.id,\n u: match.updatedAt,\n s: match.status,\n }\n\n const properties = [\n ['__beforeLoadContext', 'b'],\n ['loaderData', 'l'],\n ['error', 'e'],\n ['ssr', 'ssr'],\n ] as const\n\n for (const [key, shorthand] of properties) {\n if (match[key] !== undefined) {\n dehydratedMatch[shorthand] = match[key]\n }\n }\n return dehydratedMatch\n}\n\nexport function attachRouterServerSsrUtils({\n router,\n manifest,\n}: {\n router: AnyRouter\n manifest: Manifest | undefined\n}) {\n router.ssr = {\n manifest,\n }\n let initialScriptSent = false\n const getInitialScript = () => {\n if (initialScriptSent) {\n return ''\n }\n initialScriptSent = true\n return `${getCrossReferenceHeader(SCOPE_ID)};${minifiedTsrBootStrapScript};`\n }\n let _dehydrated = false\n const listeners: Array<() => void> = []\n\n router.serverSsr = {\n injectedHtml: [],\n injectHtml: (getHtml) => {\n const promise = Promise.resolve().then(getHtml)\n router.serverSsr!.injectedHtml.push(promise)\n router.emit({\n type: 'onInjectedHtml',\n promise,\n })\n\n return promise.then(() => {})\n },\n injectScript: (getScript) => {\n return router.serverSsr!.injectHtml(async () => {\n const script = await getScript()\n return `<script ${router.options.ssr?.nonce ? `nonce='${router.options.ssr.nonce}'` : ''} class='$tsr'>${getInitialScript()}${script};$_TSR.c()</script>`\n })\n },\n dehydrate: async () => {\n invariant(!_dehydrated, 'router is already dehydrated!')\n let matchesToDehydrate = router.state.matches\n if (router.isShell()) {\n // In SPA mode we only want to dehydrate the root match\n matchesToDehydrate = matchesToDehydrate.slice(0, 1)\n }\n const matches = matchesToDehydrate.map(dehydrateMatch)\n\n const dehydratedRouter: DehydratedRouter = {\n manifest: router.ssr!.manifest,\n matches,\n }\n const lastMatchId = matchesToDehydrate[matchesToDehydrate.length - 1]?.id\n if (lastMatchId) {\n dehydratedRouter.lastMatchId = lastMatchId\n }\n dehydratedRouter.dehydratedData = await router.options.dehydrate?.()\n _dehydrated = true\n\n const p = createControlledPromise<string>()\n const trackPlugins = { didRun: false }\n const plugins =\n (\n router.options.serializationAdapters as\n | Array<AnySerializationAdapter>\n | undefined\n )?.map((t) => makeSsrSerovalPlugin(t, trackPlugins)) ?? []\n crossSerializeStream(dehydratedRouter, {\n refs: new Map(),\n plugins: [...plugins, ...defaultSerovalPlugins],\n onSerialize: (data, initial) => {\n let serialized = initial ? GLOBAL_TSR + '.router=' + data : data\n if (trackPlugins.didRun) {\n serialized = GLOBAL_TSR + '.p(()=>' + serialized + ')'\n }\n router.serverSsr!.injectScript(() => serialized)\n },\n scopeId: SCOPE_ID,\n onDone: () => p.resolve(''),\n onError: (err) => p.reject(err),\n })\n // make sure the stream is kept open until the promise is resolved\n router.serverSsr!.injectHtml(() => p)\n },\n isDehydrated() {\n return _dehydrated\n },\n onRenderFinished: (listener) => listeners.push(listener),\n setRenderFinished: () => {\n listeners.forEach((l) => l())\n },\n }\n}\n"],"names":[],"mappings":";;;;;;;AA0BA,MAAM,WAAW;AAEV,SAAS,eAAe,OAAuC;AACpE,QAAM,kBAAmC;AAAA,IACvC,GAAG,MAAM;AAAA,IACT,GAAG,MAAM;AAAA,IACT,GAAG,MAAM;AAAA,EAAA;AAGX,QAAM,aAAa;AAAA,IACjB,CAAC,uBAAuB,GAAG;AAAA,IAC3B,CAAC,cAAc,GAAG;AAAA,IAClB,CAAC,SAAS,GAAG;AAAA,IACb,CAAC,OAAO,KAAK;AAAA,EAAA;AAGf,aAAW,CAAC,KAAK,SAAS,KAAK,YAAY;AACzC,QAAI,MAAM,GAAG,MAAM,QAAW;AAC5B,sBAAgB,SAAS,IAAI,MAAM,GAAG;AAAA,IACxC;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,2BAA2B;AAAA,EACzC;AAAA,EACA;AACF,GAGG;AACD,SAAO,MAAM;AAAA,IACX;AAAA,EAAA;AAEF,MAAI,oBAAoB;AACxB,QAAM,mBAAmB,MAAM;AAC7B,QAAI,mBAAmB;AACrB,aAAO;AAAA,IACT;AACA,wBAAoB;AACpB,WAAO,GAAG,wBAAwB,QAAQ,CAAC,IAAI,0BAA0B;AAAA,EAC3E;AACA,MAAI,cAAc;AAClB,QAAM,YAA+B,CAAA;AAErC,SAAO,YAAY;AAAA,IACjB,cAAc,CAAA;AAAA,IACd,YAAY,CAAC,YAAY;AACvB,YAAM,UAAU,QAAQ,QAAA,EAAU,KAAK,OAAO;AAC9C,aAAO,UAAW,aAAa,KAAK,OAAO;AAC3C,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN;AAAA,MAAA,CACD;AAED,aAAO,QAAQ,KAAK,MAAM;AAAA,MAAC,CAAC;AAAA,IAC9B;AAAA,IACA,cAAc,CAAC,cAAc;AAC3B,aAAO,OAAO,UAAW,WAAW,YAAY;AAC9C,cAAM,SAAS,MAAM,UAAA;AACrB,eAAO,WAAW,OAAO,QAAQ,KAAK,QAAQ,UAAU,OAAO,QAAQ,IAAI,KAAK,MAAM,EAAE,iBAAiB,iBAAA,CAAkB,GAAG,MAAM;AAAA,MACtI,CAAC;AAAA,IACH;AAAA,IACA,WAAW,YAAY;AACrB,gBAAU,CAAC,aAAa,+BAA+B;AACvD,UAAI,qBAAqB,OAAO,MAAM;AACtC,UAAI,OAAO,WAAW;AAEpB,6BAAqB,mBAAmB,MAAM,GAAG,CAAC;AAAA,MACpD;AACA,YAAM,UAAU,mBAAmB,IAAI,cAAc;AAErD,YAAM,mBAAqC;AAAA,QACzC,UAAU,OAAO,IAAK;AAAA,QACtB;AAAA,MAAA;AAEF,YAAM,cAAc,mBAAmB,mBAAmB,SAAS,CAAC,GAAG;AACvE,UAAI,aAAa;AACf,yBAAiB,cAAc;AAAA,MACjC;AACA,uBAAiB,iBAAiB,MAAM,OAAO,QAAQ,YAAA;AACvD,oBAAc;AAEd,YAAM,IAAI,wBAAA;AACV,YAAM,eAAe,EAAE,QAAQ,MAAA;AAC/B,YAAM,UAEF,OAAO,QAAQ,uBAGd,IAAI,CAAC,MAAM,qBAAqB,GAAG,YAAY,CAAC,KAAK,CAAA;AAC1D,2BAAqB,kBAAkB;AAAA,QACrC,0BAAU,IAAA;AAAA,QACV,SAAS,CAAC,GAAG,SAAS,GAAG,qBAAqB;AAAA,QAC9C,aAAa,CAAC,MAAM,YAAY;AAC9B,cAAI,aAAa,UAAU,aAAa,aAAa,OAAO;AAC5D,cAAI,aAAa,QAAQ;AACvB,yBAAa,aAAa,YAAY,aAAa;AAAA,UACrD;AACA,iBAAO,UAAW,aAAa,MAAM,UAAU;AAAA,QACjD;AAAA,QACA,SAAS;AAAA,QACT,QAAQ,MAAM,EAAE,QAAQ,EAAE;AAAA,QAC1B,SAAS,CAAC,QAAQ,EAAE,OAAO,GAAG;AAAA,MAAA,CAC/B;AAED,aAAO,UAAW,WAAW,MAAM,CAAC;AAAA,IACtC;AAAA,IACA,eAAe;AACb,aAAO;AAAA,IACT;AAAA,IACA,kBAAkB,CAAC,aAAa,UAAU,KAAK,QAAQ;AAAA,IACvD,mBAAmB,MAAM;AACvB,gBAAU,QAAQ,CAAC,MAAM,EAAA,CAAG;AAAA,IAC9B;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"ssr-server.js","sources":["../../../src/ssr/ssr-server.ts"],"sourcesContent":["import { crossSerializeStream, getCrossReferenceHeader } from 'seroval'\nimport invariant from 'tiny-invariant'\nimport { createControlledPromise } from '../utils'\nimport minifiedTsrBootStrapScript from './tsrScript?script-string'\nimport { GLOBAL_TSR } from './constants'\nimport { defaultSerovalPlugins } from './serializer/seroval-plugins'\nimport { makeSsrSerovalPlugin } from './serializer/transformer'\nimport type { AnyRouter } from '../router'\nimport type { DehydratedMatch } from './ssr-client'\nimport type { DehydratedRouter } from './client'\nimport type { AnyRouteMatch } from '../Matches'\nimport type { Manifest } from '../manifest'\nimport type { AnySerializationAdapter } from './serializer/transformer'\n\ndeclare module '../router' {\n interface ServerSsr {\n setRenderFinished: () => void\n }\n interface RouterEvents {\n onInjectedHtml: {\n type: 'onInjectedHtml'\n promise: Promise<string>\n }\n }\n}\n\nconst SCOPE_ID = 'tsr'\n\nexport function dehydrateMatch(match: AnyRouteMatch): DehydratedMatch {\n const dehydratedMatch: DehydratedMatch = {\n i: match.id,\n u: match.updatedAt,\n s: match.status,\n }\n\n const properties = [\n ['__beforeLoadContext', 'b'],\n ['loaderData', 'l'],\n ['error', 'e'],\n ['ssr', 'ssr'],\n ] as const\n\n for (const [key, shorthand] of properties) {\n if (match[key] !== undefined) {\n dehydratedMatch[shorthand] = match[key]\n }\n }\n return dehydratedMatch\n}\n\nexport function attachRouterServerSsrUtils({\n router,\n manifest,\n}: {\n router: AnyRouter\n manifest: Manifest | undefined\n}) {\n router.ssr = {\n manifest,\n }\n let initialScriptSent = false\n const getInitialScript = () => {\n if (initialScriptSent) {\n return ''\n }\n initialScriptSent = true\n return `${getCrossReferenceHeader(SCOPE_ID)};${minifiedTsrBootStrapScript};`\n }\n let _dehydrated = false\n const listeners: Array<() => void> = []\n\n router.serverSsr = {\n injectedHtml: [],\n injectHtml: (getHtml) => {\n const promise = Promise.resolve().then(getHtml)\n router.serverSsr!.injectedHtml.push(promise)\n router.emit({\n type: 'onInjectedHtml',\n promise,\n })\n\n return promise.then(() => {})\n },\n injectScript: (getScript) => {\n return router.serverSsr!.injectHtml(async () => {\n const script = await getScript()\n return `<script ${router.options.ssr?.nonce ? `nonce='${router.options.ssr.nonce}'` : ''} class='$tsr'>${getInitialScript()}${script};$_TSR.c()</script>`\n })\n },\n dehydrate: async () => {\n invariant(!_dehydrated, 'router is already dehydrated!')\n let matchesToDehydrate = router.state.matches\n if (router.isShell()) {\n // In SPA mode we only want to dehydrate the root match\n matchesToDehydrate = matchesToDehydrate.slice(0, 1)\n }\n const matches = matchesToDehydrate.map(dehydrateMatch)\n\n const dehydratedRouter: DehydratedRouter = {\n manifest: router.ssr!.manifest,\n matches,\n }\n const lastMatchId = matchesToDehydrate[matchesToDehydrate.length - 1]?.id\n if (lastMatchId) {\n dehydratedRouter.lastMatchId = lastMatchId\n }\n dehydratedRouter.dehydratedData = await router.options.dehydrate?.()\n _dehydrated = true\n\n const p = createControlledPromise<string>()\n const trackPlugins = { didRun: false }\n const plugins =\n (\n router.options.serializationAdapters as\n | Array<AnySerializationAdapter>\n | undefined\n )?.map((t) => makeSsrSerovalPlugin(t, trackPlugins)) ?? []\n crossSerializeStream(dehydratedRouter, {\n refs: new Map(),\n plugins: [...plugins, ...defaultSerovalPlugins],\n onSerialize: (data, initial) => {\n let serialized = initial ? GLOBAL_TSR + '.router=' + data : data\n if (trackPlugins.didRun) {\n serialized = GLOBAL_TSR + '.p(()=>' + serialized + ')'\n }\n router.serverSsr!.injectScript(() => serialized)\n },\n scopeId: SCOPE_ID,\n onDone: () => p.resolve(''),\n onError: (err) => p.reject(err),\n })\n // make sure the stream is kept open until the promise is resolved\n router.serverSsr!.injectHtml(() => p)\n },\n isDehydrated() {\n return _dehydrated\n },\n onRenderFinished: (listener) => listeners.push(listener),\n setRenderFinished: () => {\n listeners.forEach((l) => l())\n },\n }\n}\n\nexport function getOrigin(request: Request) {\n const originHeader = request.headers.get('Origin')\n if (originHeader) {\n try {\n new URL(originHeader)\n return originHeader\n } catch {}\n }\n try {\n return new URL(request.url).origin\n } catch {}\n return 'http://localhost'\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/router-core",
3
- "version": "1.132.21",
3
+ "version": "1.132.27",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
package/src/rewrite.ts CHANGED
@@ -23,13 +23,28 @@ export function rewriteBasepath(opts: {
23
23
  caseSensitive?: boolean
24
24
  }) {
25
25
  const trimmedBasepath = trimPath(opts.basepath)
26
- const regex = new RegExp(
27
- `^/${trimmedBasepath}/`,
28
- opts.caseSensitive ? '' : 'i',
29
- )
26
+ const normalizedBasepath = `/${trimmedBasepath}`
27
+ const normalizedBasepathWithSlash = `${normalizedBasepath}/`
28
+ const checkBasepath = opts.caseSensitive
29
+ ? normalizedBasepath
30
+ : normalizedBasepath.toLowerCase()
31
+ const checkBasepathWithSlash = opts.caseSensitive
32
+ ? normalizedBasepathWithSlash
33
+ : normalizedBasepathWithSlash.toLowerCase()
34
+
30
35
  return {
31
36
  input: ({ url }) => {
32
- url.pathname = url.pathname.replace(regex, '/')
37
+ const pathname = opts.caseSensitive
38
+ ? url.pathname
39
+ : url.pathname.toLowerCase()
40
+
41
+ // Handle exact basepath match (e.g., /my-app -> /)
42
+ if (pathname === checkBasepath) {
43
+ url.pathname = '/'
44
+ } else if (pathname.startsWith(checkBasepathWithSlash)) {
45
+ // Handle basepath with trailing content (e.g., /my-app/users -> /users)
46
+ url.pathname = url.pathname.slice(normalizedBasepath.length)
47
+ }
33
48
  return url
34
49
  },
35
50
  output: ({ url }) => {
package/src/router.ts CHANGED
@@ -2230,7 +2230,11 @@ export class RouterCore<
2230
2230
 
2231
2231
  resolveRedirect = (redirect: AnyRedirect): AnyRedirect => {
2232
2232
  if (!redirect.options.href) {
2233
- redirect.options.href = this.buildLocation(redirect.options).href
2233
+ let href = this.buildLocation(redirect.options).url
2234
+ if (this.origin && href.startsWith(this.origin)) {
2235
+ href = href.replace(this.origin, '') || '/'
2236
+ }
2237
+ redirect.options.href = href
2234
2238
  redirect.headers.set('Location', redirect.options.href)
2235
2239
  }
2236
2240
 
@@ -1,6 +1,6 @@
1
1
  import { createMemoryHistory } from '@tanstack/history'
2
2
  import { mergeHeaders } from './headers'
3
- import { attachRouterServerSsrUtils } from './ssr-server'
3
+ import { attachRouterServerSsrUtils, getOrigin } from './ssr-server'
4
4
  import type { HandlerCallback } from './handlerCallback'
5
5
  import type { AnyRouter } from '../router'
6
6
  import type { Manifest } from '../manifest'
@@ -27,7 +27,7 @@ export function createRequestHandler<TRouter extends AnyRouter>({
27
27
  })
28
28
 
29
29
  const url = new URL(request.url, 'http://localhost')
30
-
30
+ const origin = getOrigin(request)
31
31
  const href = url.href.replace(url.origin, '')
32
32
 
33
33
  // Create a history for the router
@@ -38,6 +38,7 @@ export function createRequestHandler<TRouter extends AnyRouter>({
38
38
  // Update the router with the history and context
39
39
  router.update({
40
40
  history,
41
+ origin: router.options.origin ?? origin,
41
42
  })
42
43
 
43
44
  await router.load()
@@ -23,19 +23,40 @@ export interface SerializableExtensions extends DefaultSerializable {}
23
23
 
24
24
  export type Serializable = SerializableExtensions[keyof SerializableExtensions]
25
25
 
26
+ export type UnionizeSerializationAdaptersInput<
27
+ TAdapters extends ReadonlyArray<AnySerializationAdapter>,
28
+ > = TAdapters[number]['~types']['input']
29
+
26
30
  export function createSerializationAdapter<
27
31
  TInput = unknown,
28
- TOutput = unknown /* we need to check that this type is actually serializable taking into account all seroval native types and any custom plugin WE=router/start add!!! */,
32
+ TOutput = unknown,
33
+ const TExtendsAdapters extends
34
+ | ReadonlyArray<AnySerializationAdapter>
35
+ | never = never,
29
36
  >(
30
- opts: CreateSerializationAdapterOptions<TInput, TOutput>,
31
- ): SerializationAdapter<TInput, TOutput> {
32
- return opts as unknown as SerializationAdapter<TInput, TOutput>
37
+ opts: CreateSerializationAdapterOptions<TInput, TOutput, TExtendsAdapters>,
38
+ ): SerializationAdapter<TInput, TOutput, TExtendsAdapters> {
39
+ return opts as unknown as SerializationAdapter<
40
+ TInput,
41
+ TOutput,
42
+ TExtendsAdapters
43
+ >
33
44
  }
34
45
 
35
- export interface CreateSerializationAdapterOptions<TInput, TOutput> {
46
+ export interface CreateSerializationAdapterOptions<
47
+ TInput,
48
+ TOutput,
49
+ TExtendsAdapters extends ReadonlyArray<AnySerializationAdapter> | never,
50
+ > {
36
51
  key: string
52
+ extends?: TExtendsAdapters
37
53
  test: (value: unknown) => value is TInput
38
- toSerializable: (value: TInput) => ValidateSerializable<TOutput, Serializable>
54
+ toSerializable: (
55
+ value: TInput,
56
+ ) => ValidateSerializable<
57
+ TOutput,
58
+ Serializable | UnionizeSerializationAdaptersInput<TExtendsAdapters>
59
+ >
39
60
  fromSerializable: (value: TOutput) => TInput
40
61
  }
41
62
 
@@ -90,28 +111,42 @@ export interface DefaultSerializerExtensions {
90
111
 
91
112
  export interface SerializerExtensions extends DefaultSerializerExtensions {}
92
113
 
93
- export interface SerializationAdapter<TInput, TOutput> {
94
- '~types': SerializationAdapterTypes<TInput, TOutput>
114
+ export interface SerializationAdapter<
115
+ TInput,
116
+ TOutput,
117
+ TExtendsAdapters extends ReadonlyArray<AnySerializationAdapter>,
118
+ > {
119
+ '~types': SerializationAdapterTypes<TInput, TOutput, TExtendsAdapters>
95
120
  key: string
121
+ extends?: TExtendsAdapters
96
122
  test: (value: unknown) => value is TInput
97
123
  toSerializable: (value: TInput) => TOutput
98
124
  fromSerializable: (value: TOutput) => TInput
99
- makePlugin: (options: { didRun: boolean }) => Plugin<TInput, SerovalNode>
100
125
  }
101
126
 
102
- export interface SerializationAdapterTypes<TInput, TOutput> {
103
- input: TInput
127
+ export interface SerializationAdapterTypes<
128
+ TInput,
129
+ TOutput,
130
+ TExtendsAdapters extends ReadonlyArray<AnySerializationAdapter>,
131
+ > {
132
+ input: TInput | UnionizeSerializationAdaptersInput<TExtendsAdapters>
104
133
  output: TOutput
134
+ extends: TExtendsAdapters
105
135
  }
106
136
 
107
- export type AnySerializationAdapter = SerializationAdapter<any, any>
137
+ export type AnySerializationAdapter = SerializationAdapter<any, any, any>
108
138
 
109
- export function makeSsrSerovalPlugin<TInput, TOutput>(
110
- serializationAdapter: SerializationAdapter<TInput, TOutput>,
139
+ export function makeSsrSerovalPlugin(
140
+ serializationAdapter: AnySerializationAdapter,
111
141
  options: { didRun: boolean },
112
- ) {
113
- return createPlugin<TInput, SerovalNode>({
142
+ ): Plugin<any, SerovalNode> {
143
+ return createPlugin<any, SerovalNode>({
114
144
  tag: '$TSR/t/' + serializationAdapter.key,
145
+ extends: serializationAdapter.extends
146
+ ? (serializationAdapter.extends as Array<AnySerializationAdapter>).map(
147
+ (ext) => makeSsrSerovalPlugin(ext, options),
148
+ )
149
+ : undefined,
115
150
  test: serializationAdapter.test,
116
151
  parse: {
117
152
  stream(value, ctx) {
@@ -134,11 +169,16 @@ export function makeSsrSerovalPlugin<TInput, TOutput>(
134
169
  })
135
170
  }
136
171
 
137
- export function makeSerovalPlugin<TInput, TOutput>(
138
- serializationAdapter: SerializationAdapter<TInput, TOutput>,
139
- ) {
140
- return createPlugin<TInput, SerovalNode>({
172
+ export function makeSerovalPlugin(
173
+ serializationAdapter: AnySerializationAdapter,
174
+ ): Plugin<any, SerovalNode> {
175
+ return createPlugin<any, SerovalNode>({
141
176
  tag: '$TSR/t/' + serializationAdapter.key,
177
+ extends: serializationAdapter.extends
178
+ ? (serializationAdapter.extends as Array<AnySerializationAdapter>).map(
179
+ makeSerovalPlugin,
180
+ )
181
+ : undefined,
142
182
  test: serializationAdapter.test,
143
183
  parse: {
144
184
  sync(value, ctx) {
@@ -154,9 +194,7 @@ export function makeSerovalPlugin<TInput, TOutput>(
154
194
  // we don't generate JS code outside of SSR (for now)
155
195
  serialize: undefined as never,
156
196
  deserialize(node, ctx) {
157
- return serializationAdapter.fromSerializable(
158
- ctx.deserialize(node) as TOutput,
159
- )
197
+ return serializationAdapter.fromSerializable(ctx.deserialize(node))
160
198
  },
161
199
  })
162
200
  }
package/src/ssr/server.ts CHANGED
@@ -7,7 +7,7 @@ export {
7
7
  transformStreamWithRouter,
8
8
  transformReadableStreamWithRouter,
9
9
  } from './transformStreamWithRouter'
10
- export { attachRouterServerSsrUtils } from './ssr-server'
10
+ export { attachRouterServerSsrUtils, getOrigin } from './ssr-server'
11
11
 
12
12
  // declare module '../router' {
13
13
  // export interface RegisterSsr {
@@ -141,3 +141,17 @@ export function attachRouterServerSsrUtils({
141
141
  },
142
142
  }
143
143
  }
144
+
145
+ export function getOrigin(request: Request) {
146
+ const originHeader = request.headers.get('Origin')
147
+ if (originHeader) {
148
+ try {
149
+ new URL(originHeader)
150
+ return originHeader
151
+ } catch {}
152
+ }
153
+ try {
154
+ return new URL(request.url).origin
155
+ } catch {}
156
+ return 'http://localhost'
157
+ }