@tanstack/react-start-client 1.114.3 → 1.114.5

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 (97) hide show
  1. package/dist/cjs/StartClient.cjs +2 -2
  2. package/dist/cjs/StartClient.cjs.map +1 -1
  3. package/dist/cjs/StartClient.d.cts +1 -1
  4. package/dist/cjs/createServerFn.cjs +13 -230
  5. package/dist/cjs/createServerFn.cjs.map +1 -1
  6. package/dist/cjs/createServerFn.d.cts +1 -141
  7. package/dist/cjs/index.cjs +37 -16
  8. package/dist/cjs/index.cjs.map +1 -1
  9. package/dist/cjs/index.d.cts +11 -9
  10. package/dist/cjs/tests/setupTests.d.cts +0 -0
  11. package/dist/esm/StartClient.d.ts +1 -1
  12. package/dist/esm/StartClient.js +1 -1
  13. package/dist/esm/StartClient.js.map +1 -1
  14. package/dist/esm/createServerFn.d.ts +1 -141
  15. package/dist/esm/createServerFn.js +6 -201
  16. package/dist/esm/createServerFn.js.map +1 -1
  17. package/dist/esm/index.d.ts +11 -9
  18. package/dist/esm/index.js +1 -7
  19. package/dist/esm/index.js.map +1 -1
  20. package/dist/esm/tests/setupTests.d.ts +0 -0
  21. package/package.json +4 -3
  22. package/src/StartClient.tsx +2 -2
  23. package/src/createServerFn.ts +29 -692
  24. package/src/index.tsx +20 -20
  25. package/src/tests/createServerFn.test-d.tsx +1 -1
  26. package/src/tests/setupTests.tsx +1 -0
  27. package/dist/cjs/createIsomorphicFn.cjs +0 -7
  28. package/dist/cjs/createIsomorphicFn.cjs.map +0 -1
  29. package/dist/cjs/createIsomorphicFn.d.cts +0 -12
  30. package/dist/cjs/createMiddleware.cjs +0 -34
  31. package/dist/cjs/createMiddleware.cjs.map +0 -1
  32. package/dist/cjs/createMiddleware.d.cts +0 -131
  33. package/dist/cjs/envOnly.cjs +0 -7
  34. package/dist/cjs/envOnly.cjs.map +0 -1
  35. package/dist/cjs/envOnly.d.cts +0 -4
  36. package/dist/cjs/headers.cjs +0 -30
  37. package/dist/cjs/headers.cjs.map +0 -1
  38. package/dist/cjs/headers.d.cts +0 -5
  39. package/dist/cjs/json.cjs +0 -14
  40. package/dist/cjs/json.cjs.map +0 -1
  41. package/dist/cjs/json.d.cts +0 -2
  42. package/dist/cjs/registerGlobalMiddleware.cjs +0 -9
  43. package/dist/cjs/registerGlobalMiddleware.cjs.map +0 -1
  44. package/dist/cjs/registerGlobalMiddleware.d.cts +0 -5
  45. package/dist/cjs/serializer.cjs +0 -152
  46. package/dist/cjs/serializer.cjs.map +0 -1
  47. package/dist/cjs/serializer.d.cts +0 -2
  48. package/dist/cjs/ssr-client.cjs +0 -130
  49. package/dist/cjs/ssr-client.cjs.map +0 -1
  50. package/dist/cjs/ssr-client.d.cts +0 -65
  51. package/dist/cjs/tests/createIsomorphicFn.test-d.d.cts +0 -1
  52. package/dist/cjs/tests/createServerMiddleware.test-d.d.cts +0 -1
  53. package/dist/cjs/tests/envOnly.test-d.d.cts +0 -1
  54. package/dist/cjs/tests/json.test.d.cts +0 -1
  55. package/dist/cjs/tests/transformer.test.d.cts +0 -1
  56. package/dist/esm/createIsomorphicFn.d.ts +0 -12
  57. package/dist/esm/createIsomorphicFn.js +0 -7
  58. package/dist/esm/createIsomorphicFn.js.map +0 -1
  59. package/dist/esm/createMiddleware.d.ts +0 -131
  60. package/dist/esm/createMiddleware.js +0 -34
  61. package/dist/esm/createMiddleware.js.map +0 -1
  62. package/dist/esm/envOnly.d.ts +0 -4
  63. package/dist/esm/envOnly.js +0 -7
  64. package/dist/esm/envOnly.js.map +0 -1
  65. package/dist/esm/headers.d.ts +0 -5
  66. package/dist/esm/headers.js +0 -30
  67. package/dist/esm/headers.js.map +0 -1
  68. package/dist/esm/json.d.ts +0 -2
  69. package/dist/esm/json.js +0 -14
  70. package/dist/esm/json.js.map +0 -1
  71. package/dist/esm/registerGlobalMiddleware.d.ts +0 -5
  72. package/dist/esm/registerGlobalMiddleware.js +0 -9
  73. package/dist/esm/registerGlobalMiddleware.js.map +0 -1
  74. package/dist/esm/serializer.d.ts +0 -2
  75. package/dist/esm/serializer.js +0 -152
  76. package/dist/esm/serializer.js.map +0 -1
  77. package/dist/esm/ssr-client.d.ts +0 -65
  78. package/dist/esm/ssr-client.js +0 -130
  79. package/dist/esm/ssr-client.js.map +0 -1
  80. package/dist/esm/tests/createIsomorphicFn.test-d.d.ts +0 -1
  81. package/dist/esm/tests/createServerMiddleware.test-d.d.ts +0 -1
  82. package/dist/esm/tests/envOnly.test-d.d.ts +0 -1
  83. package/dist/esm/tests/json.test.d.ts +0 -1
  84. package/dist/esm/tests/transformer.test.d.ts +0 -1
  85. package/src/createIsomorphicFn.ts +0 -36
  86. package/src/createMiddleware.ts +0 -595
  87. package/src/envOnly.ts +0 -9
  88. package/src/headers.ts +0 -50
  89. package/src/json.ts +0 -15
  90. package/src/registerGlobalMiddleware.ts +0 -9
  91. package/src/serializer.ts +0 -177
  92. package/src/ssr-client.tsx +0 -246
  93. package/src/tests/createIsomorphicFn.test-d.ts +0 -72
  94. package/src/tests/createServerMiddleware.test-d.ts +0 -611
  95. package/src/tests/envOnly.test-d.ts +0 -34
  96. package/src/tests/json.test.ts +0 -37
  97. package/src/tests/transformer.test.tsx +0 -147
package/src/serializer.ts DELETED
@@ -1,177 +0,0 @@
1
- import { isPlainObject } from '@tanstack/router-core'
2
- import type { StartSerializer } from '@tanstack/router-core'
3
-
4
- export const startSerializer: StartSerializer = {
5
- stringify: (value: any) =>
6
- JSON.stringify(value, function replacer(key, val) {
7
- const ogVal = this[key]
8
- const serializer = serializers.find((t) => t.stringifyCondition(ogVal))
9
-
10
- if (serializer) {
11
- return serializer.stringify(ogVal)
12
- }
13
-
14
- return val
15
- }),
16
- parse: (value: string) =>
17
- JSON.parse(value, function parser(key, val) {
18
- const ogVal = this[key]
19
- if (isPlainObject(ogVal)) {
20
- const serializer = serializers.find((t) => t.parseCondition(ogVal))
21
-
22
- if (serializer) {
23
- return serializer.parse(ogVal)
24
- }
25
- }
26
-
27
- return val
28
- }),
29
- encode: (value: any) => {
30
- // When encoding, dive first
31
- if (Array.isArray(value)) {
32
- return value.map((v) => startSerializer.encode(v))
33
- }
34
-
35
- if (isPlainObject(value)) {
36
- return Object.fromEntries(
37
- Object.entries(value).map(([key, v]) => [
38
- key,
39
- startSerializer.encode(v),
40
- ]),
41
- )
42
- }
43
-
44
- const serializer = serializers.find((t) => t.stringifyCondition(value))
45
- if (serializer) {
46
- return serializer.stringify(value)
47
- }
48
-
49
- return value
50
- },
51
- decode: (value: any) => {
52
- // Attempt transform first
53
- if (isPlainObject(value)) {
54
- const serializer = serializers.find((t) => t.parseCondition(value))
55
- if (serializer) {
56
- return serializer.parse(value)
57
- }
58
- }
59
-
60
- if (Array.isArray(value)) {
61
- return value.map((v) => startSerializer.decode(v))
62
- }
63
-
64
- if (isPlainObject(value)) {
65
- return Object.fromEntries(
66
- Object.entries(value).map(([key, v]) => [
67
- key,
68
- startSerializer.decode(v),
69
- ]),
70
- )
71
- }
72
-
73
- return value
74
- },
75
- }
76
-
77
- const createSerializer = <TKey extends string, TInput, TSerialized>(
78
- key: TKey,
79
- check: (value: any) => value is TInput,
80
- toValue: (value: TInput) => TSerialized,
81
- fromValue: (value: TSerialized) => TInput,
82
- ) => ({
83
- key,
84
- stringifyCondition: check,
85
- stringify: (value: any) => ({ [`$${key}`]: toValue(value) }),
86
- parseCondition: (value: any) => Object.hasOwn(value, `$${key}`),
87
- parse: (value: any) => fromValue(value[`$${key}`]),
88
- })
89
-
90
- // Keep these ordered by predicted frequency
91
- // Make sure to keep DefaultSerializable in sync with these serializers
92
- // Also, make sure that they are unit tested in serializer.test.tsx
93
- const serializers = [
94
- createSerializer(
95
- // Key
96
- 'undefined',
97
- // Check
98
- (v): v is undefined => v === undefined,
99
- // To
100
- () => 0,
101
- // From
102
- () => undefined,
103
- ),
104
- createSerializer(
105
- // Key
106
- 'date',
107
- // Check
108
- (v): v is Date => v instanceof Date,
109
- // To
110
- (v) => v.toISOString(),
111
- // From
112
- (v) => new Date(v),
113
- ),
114
- createSerializer(
115
- // Key
116
- 'error',
117
- // Check
118
- (v): v is Error => v instanceof Error,
119
- // To
120
- (v) => ({
121
- ...v,
122
- message: v.message,
123
- stack: process.env.NODE_ENV === 'development' ? v.stack : undefined,
124
- cause: v.cause,
125
- }),
126
- // From
127
- (v) => Object.assign(new Error(v.message), v),
128
- ),
129
- createSerializer(
130
- // Key
131
- 'formData',
132
- // Check
133
- (v): v is FormData => v instanceof FormData,
134
- // To
135
- (v) => {
136
- const entries: Record<
137
- string,
138
- Array<FormDataEntryValue> | FormDataEntryValue
139
- > = {}
140
- v.forEach((value, key) => {
141
- const entry = entries[key]
142
- if (entry !== undefined) {
143
- if (Array.isArray(entry)) {
144
- entry.push(value)
145
- } else {
146
- entries[key] = [entry, value]
147
- }
148
- } else {
149
- entries[key] = value
150
- }
151
- })
152
- return entries
153
- },
154
- // From
155
- (v) => {
156
- const formData = new FormData()
157
- Object.entries(v).forEach(([key, value]) => {
158
- if (Array.isArray(value)) {
159
- value.forEach((val) => formData.append(key, val))
160
- } else {
161
- formData.append(key, value)
162
- }
163
- })
164
- return formData
165
- },
166
- ),
167
- createSerializer(
168
- // Key
169
- 'bigint',
170
- // Check
171
- (v): v is bigint => typeof v === 'bigint',
172
- // To
173
- (v) => v.toString(),
174
- // From
175
- (v) => BigInt(v),
176
- ),
177
- ] as const
@@ -1,246 +0,0 @@
1
- import { isPlainObject } from '@tanstack/router-core'
2
-
3
- import invariant from 'tiny-invariant'
4
-
5
- import { startSerializer } from './serializer'
6
- import type {
7
- AnyRouter,
8
- ControllablePromise,
9
- MakeRouteMatch,
10
- } from '@tanstack/react-router'
11
-
12
- import type {
13
- DeferredPromiseState,
14
- Manifest,
15
- RouteContextOptions,
16
- } from '@tanstack/router-core'
17
-
18
- declare global {
19
- interface Window {
20
- __TSR_SSR__?: StartSsrGlobal
21
- }
22
- }
23
-
24
- export interface StartSsrGlobal {
25
- matches: Array<SsrMatch>
26
- streamedValues: Record<
27
- string,
28
- {
29
- value: any
30
- parsed: any
31
- }
32
- >
33
- cleanScripts: () => void
34
- dehydrated?: any
35
- initMatch: (match: SsrMatch) => void
36
- resolvePromise: (opts: {
37
- matchId: string
38
- id: number
39
- promiseState: DeferredPromiseState<any>
40
- }) => void
41
- injectChunk: (opts: { matchId: string; id: number; chunk: string }) => void
42
- closeStream: (opts: { matchId: string; id: number }) => void
43
- }
44
-
45
- export interface SsrMatch {
46
- id: string
47
- __beforeLoadContext: string
48
- loaderData?: string
49
- error?: string
50
- extracted?: Array<ClientExtractedEntry>
51
- updatedAt: MakeRouteMatch['updatedAt']
52
- status: MakeRouteMatch['status']
53
- }
54
-
55
- export type ClientExtractedEntry =
56
- | ClientExtractedStream
57
- | ClientExtractedPromise
58
-
59
- export interface ClientExtractedPromise extends ClientExtractedBaseEntry {
60
- type: 'promise'
61
- value?: ControllablePromise<any>
62
- }
63
-
64
- export interface ClientExtractedStream extends ClientExtractedBaseEntry {
65
- type: 'stream'
66
- value?: ReadableStream & { controller?: ReadableStreamDefaultController }
67
- }
68
-
69
- export interface ClientExtractedBaseEntry {
70
- type: string
71
- path: Array<string>
72
- }
73
-
74
- export interface ResolvePromiseState {
75
- matchId: string
76
- id: number
77
- promiseState: DeferredPromiseState<any>
78
- }
79
-
80
- export interface DehydratedRouter {
81
- manifest: Manifest | undefined
82
- dehydratedData: any
83
- }
84
-
85
- export function hydrate(router: AnyRouter) {
86
- invariant(
87
- window.__TSR_SSR__?.dehydrated,
88
- 'Expected to find a dehydrated data on window.__TSR_SSR__.dehydrated... but we did not. Please file an issue!',
89
- )
90
-
91
- const { manifest, dehydratedData } = startSerializer.parse(
92
- window.__TSR_SSR__.dehydrated,
93
- ) as DehydratedRouter
94
-
95
- router.ssr = {
96
- manifest,
97
- serializer: startSerializer,
98
- }
99
-
100
- router.clientSsr = {
101
- getStreamedValue: <T,>(key: string): T | undefined => {
102
- if (router.isServer) {
103
- return undefined
104
- }
105
-
106
- const streamedValue = window.__TSR_SSR__?.streamedValues[key]
107
-
108
- if (!streamedValue) {
109
- return
110
- }
111
-
112
- if (!streamedValue.parsed) {
113
- streamedValue.parsed = router.ssr!.serializer.parse(streamedValue.value)
114
- }
115
-
116
- return streamedValue.parsed
117
- },
118
- }
119
-
120
- // Hydrate the router state
121
- const matches = router.matchRoutes(router.state.location)
122
- // kick off loading the route chunks
123
- const routeChunkPromise = Promise.all(
124
- matches.map((match) => {
125
- const route = router.looseRoutesById[match.routeId]!
126
- return router.loadRouteChunk(route)
127
- }),
128
- )
129
- // Right after hydration and before the first render, we need to rehydrate each match
130
- // First step is to reyhdrate loaderData and __beforeLoadContext
131
- matches.forEach((match) => {
132
- const dehydratedMatch = window.__TSR_SSR__!.matches.find(
133
- (d) => d.id === match.id,
134
- )
135
-
136
- if (dehydratedMatch) {
137
- Object.assign(match, dehydratedMatch)
138
-
139
- // Handle beforeLoadContext
140
- if (dehydratedMatch.__beforeLoadContext) {
141
- match.__beforeLoadContext = router.ssr!.serializer.parse(
142
- dehydratedMatch.__beforeLoadContext,
143
- ) as any
144
- }
145
-
146
- // Handle loaderData
147
- if (dehydratedMatch.loaderData) {
148
- match.loaderData = router.ssr!.serializer.parse(
149
- dehydratedMatch.loaderData,
150
- )
151
- }
152
-
153
- // Handle error
154
- if (dehydratedMatch.error) {
155
- match.error = router.ssr!.serializer.parse(dehydratedMatch.error)
156
- }
157
-
158
- // Handle extracted
159
- ;(match as unknown as SsrMatch).extracted?.forEach((ex) => {
160
- deepMutableSetByPath(match, ['loaderData', ...ex.path], ex.value)
161
- })
162
- } else {
163
- Object.assign(match, {
164
- status: 'success',
165
- updatedAt: Date.now(),
166
- })
167
- }
168
-
169
- return match
170
- })
171
-
172
- router.__store.setState((s) => {
173
- return {
174
- ...s,
175
- matches,
176
- }
177
- })
178
-
179
- // Allow the user to handle custom hydration data
180
- router.options.hydrate?.(dehydratedData)
181
-
182
- // now that all necessary data is hydrated:
183
- // 1) fully reconstruct the route context
184
- // 2) execute `head()` and `scripts()` for each match
185
- router.state.matches.forEach((match) => {
186
- const route = router.looseRoutesById[match.routeId]!
187
-
188
- const parentMatch = router.state.matches[match.index - 1]
189
- const parentContext = parentMatch?.context ?? router.options.context ?? {}
190
-
191
- // `context()` was already executed by `matchRoutes`, however route context was not yet fully reconstructed
192
- // so run it again and merge route context
193
- const contextFnContext: RouteContextOptions<any, any, any, any> = {
194
- deps: match.loaderDeps,
195
- params: match.params,
196
- context: parentContext,
197
- location: router.state.location,
198
- navigate: (opts: any) =>
199
- router.navigate({ ...opts, _fromLocation: router.state.location }),
200
- buildLocation: router.buildLocation,
201
- cause: match.cause,
202
- abortController: match.abortController,
203
- preload: false,
204
- matches,
205
- }
206
- match.__routeContext = route.options.context?.(contextFnContext) ?? {}
207
-
208
- match.context = {
209
- ...parentContext,
210
- ...match.__routeContext,
211
- ...match.__beforeLoadContext,
212
- }
213
-
214
- const assetContext = {
215
- matches: router.state.matches,
216
- match,
217
- params: match.params,
218
- loaderData: match.loaderData,
219
- }
220
- const headFnContent = route.options.head?.(assetContext)
221
-
222
- const scripts = route.options.scripts?.(assetContext)
223
-
224
- match.meta = headFnContent?.meta
225
- match.links = headFnContent?.links
226
- match.headScripts = headFnContent?.scripts
227
- match.scripts = scripts
228
- })
229
-
230
- return routeChunkPromise
231
- }
232
-
233
- function deepMutableSetByPath<T>(obj: T, path: Array<string>, value: any) {
234
- // mutable set by path retaining array and object references
235
- if (path.length === 1) {
236
- ;(obj as any)[path[0]!] = value
237
- }
238
-
239
- const [key, ...rest] = path
240
-
241
- if (Array.isArray(obj)) {
242
- deepMutableSetByPath(obj[Number(key)], rest, value)
243
- } else if (isPlainObject(obj)) {
244
- deepMutableSetByPath((obj as any)[key!], rest, value)
245
- }
246
- }
@@ -1,72 +0,0 @@
1
- import { expectTypeOf, test } from 'vitest'
2
- import { createIsomorphicFn } from '../createIsomorphicFn'
3
-
4
- test('createIsomorphicFn with no implementations', () => {
5
- const fn = createIsomorphicFn()
6
-
7
- expectTypeOf(fn).toBeCallableWith()
8
- expectTypeOf(fn).returns.toBeUndefined()
9
-
10
- expectTypeOf(fn).toHaveProperty('server')
11
- expectTypeOf(fn).toHaveProperty('client')
12
- })
13
-
14
- test('createIsomorphicFn with server implementation', () => {
15
- const fn = createIsomorphicFn().server(() => 'data')
16
-
17
- expectTypeOf(fn).toBeCallableWith()
18
- expectTypeOf(fn).returns.toEqualTypeOf<string | undefined>()
19
-
20
- expectTypeOf(fn).toHaveProperty('client')
21
- expectTypeOf(fn).not.toHaveProperty('server')
22
- })
23
-
24
- test('createIsomorphicFn with client implementation', () => {
25
- const fn = createIsomorphicFn().client(() => 'data')
26
-
27
- expectTypeOf(fn).toBeCallableWith()
28
- expectTypeOf(fn).returns.toEqualTypeOf<string | undefined>()
29
-
30
- expectTypeOf(fn).toHaveProperty('server')
31
- expectTypeOf(fn).not.toHaveProperty('client')
32
- })
33
-
34
- test('createIsomorphicFn with server and client implementation', () => {
35
- const fn = createIsomorphicFn()
36
- .server(() => 'data')
37
- .client(() => 'data')
38
-
39
- expectTypeOf(fn).toBeCallableWith()
40
- expectTypeOf(fn).returns.toEqualTypeOf<string>()
41
-
42
- expectTypeOf(fn).not.toHaveProperty('server')
43
- expectTypeOf(fn).not.toHaveProperty('client')
44
- })
45
-
46
- test('createIsomorphicFn with varying returns', () => {
47
- const fn = createIsomorphicFn()
48
- .server(() => 'data')
49
- .client(() => 1)
50
- expectTypeOf(fn).toBeCallableWith()
51
- expectTypeOf(fn).returns.toEqualTypeOf<string | number>()
52
- })
53
-
54
- test('createIsomorphicFn with arguments', () => {
55
- const fn = createIsomorphicFn()
56
- .server((a: number, b: string) => 'data')
57
- .client((...args) => {
58
- expectTypeOf(args).toEqualTypeOf<[number, string]>()
59
- return 1
60
- })
61
- expectTypeOf(fn).toBeCallableWith(1, 'a')
62
- expectTypeOf(fn).returns.toEqualTypeOf<string | number>()
63
-
64
- const fn2 = createIsomorphicFn()
65
- .client((a: number, b: string) => 'data')
66
- .server((...args) => {
67
- expectTypeOf(args).toEqualTypeOf<[number, string]>()
68
- return 1
69
- })
70
- expectTypeOf(fn2).toBeCallableWith(1, 'a')
71
- expectTypeOf(fn2).returns.toEqualTypeOf<string | number>()
72
- })