@tanstack/start-client-core 1.132.0-alpha.2 → 1.132.0-alpha.20

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 (53) hide show
  1. package/dist/esm/constants.d.ts +5 -0
  2. package/dist/esm/constants.js +13 -0
  3. package/dist/esm/constants.js.map +1 -0
  4. package/dist/esm/createClientRpc.d.ts +6 -0
  5. package/dist/esm/createClientRpc.js +26 -0
  6. package/dist/esm/createClientRpc.js.map +1 -0
  7. package/dist/esm/createMiddleware.d.ts +42 -42
  8. package/dist/esm/createMiddleware.js.map +1 -1
  9. package/dist/esm/createServerFn.d.ts +57 -61
  10. package/dist/esm/createServerFn.js +40 -46
  11. package/dist/esm/createServerFn.js.map +1 -1
  12. package/dist/esm/envOnly.d.ts +2 -2
  13. package/dist/esm/envOnly.js +4 -4
  14. package/dist/esm/envOnly.js.map +1 -1
  15. package/dist/esm/getRouterInstance.d.ts +1 -0
  16. package/dist/esm/getRouterInstance.js +7 -0
  17. package/dist/esm/getRouterInstance.js.map +1 -0
  18. package/dist/esm/index.d.ts +6 -5
  19. package/dist/esm/index.js +13 -8
  20. package/dist/esm/index.js.map +1 -1
  21. package/dist/esm/serializer/ServerFunctionSerializationAdapter.d.ts +5 -0
  22. package/dist/esm/serializer/ServerFunctionSerializationAdapter.js +17 -0
  23. package/dist/esm/serializer/ServerFunctionSerializationAdapter.js.map +1 -0
  24. package/dist/esm/serializer/getClientSerovalPlugins.d.ts +3 -0
  25. package/dist/esm/serializer/getClientSerovalPlugins.js +13 -0
  26. package/dist/esm/serializer/getClientSerovalPlugins.js.map +1 -0
  27. package/dist/esm/serializer/getDefaultSerovalPlugins.d.ts +1 -0
  28. package/dist/esm/serializer/getDefaultSerovalPlugins.js +16 -0
  29. package/dist/esm/serializer/getDefaultSerovalPlugins.js.map +1 -0
  30. package/dist/esm/serverFnFetcher.d.ts +1 -0
  31. package/dist/esm/serverFnFetcher.js +217 -0
  32. package/dist/esm/serverFnFetcher.js.map +1 -0
  33. package/package.json +5 -4
  34. package/src/constants.ts +8 -0
  35. package/src/createClientRpc.ts +26 -0
  36. package/src/createMiddleware.ts +113 -95
  37. package/src/createServerFn.ts +230 -208
  38. package/src/envOnly.ts +2 -2
  39. package/src/getRouterInstance.ts +7 -0
  40. package/src/index.tsx +11 -15
  41. package/src/serializer/ServerFunctionSerializationAdapter.ts +16 -0
  42. package/src/serializer/getClientSerovalPlugins.ts +10 -0
  43. package/src/serializer/getDefaultSerovalPlugins.ts +19 -0
  44. package/src/serverFnFetcher.ts +299 -0
  45. package/src/tests/createServerFn.test-d.ts +134 -108
  46. package/src/tests/createServerMiddleware.test-d.ts +16 -9
  47. package/src/tests/envOnly.test-d.ts +9 -9
  48. package/dist/esm/serializer.d.ts +0 -23
  49. package/dist/esm/serializer.js +0 -162
  50. package/dist/esm/serializer.js.map +0 -1
  51. package/dist/esm/tests/serializer.test.d.ts +0 -1
  52. package/src/serializer.ts +0 -219
  53. package/src/tests/serializer.test.tsx +0 -151
@@ -1,7 +1,7 @@
1
1
  import { describe, expectTypeOf, test } from 'vitest'
2
2
  import { createMiddleware } from '../createMiddleware'
3
3
  import { createServerFn } from '../createServerFn'
4
- import type { Constrain, Validator } from '@tanstack/router-core'
4
+ import type { Constrain, Register, Validator } from '@tanstack/router-core'
5
5
  import type { ConstrainValidator } from '../createServerFn'
6
6
 
7
7
  test('createServerFn method with autocomplete', () => {
@@ -21,7 +21,6 @@ test('createServerFn without middleware', () => {
21
21
  context: undefined
22
22
  data: undefined
23
23
  signal: AbortSignal
24
- response: 'data'
25
24
  }>()
26
25
  })
27
26
  })
@@ -45,7 +44,6 @@ test('createServerFn with validator', () => {
45
44
  a: string
46
45
  }
47
46
  signal: AbortSignal
48
- response: 'data'
49
47
  }>()
50
48
  })
51
49
 
@@ -97,7 +95,6 @@ test('createServerFn with middleware and context', () => {
97
95
 
98
96
  expectTypeOf(fnWithMiddleware).toHaveProperty('handler')
99
97
  expectTypeOf(fnWithMiddleware).toHaveProperty('validator')
100
- expectTypeOf(fnWithMiddleware).not.toHaveProperty('middleware')
101
98
 
102
99
  fnWithMiddleware.handler((options) => {
103
100
  expectTypeOf(options).toEqualTypeOf<{
@@ -110,7 +107,6 @@ test('createServerFn with middleware and context', () => {
110
107
  }
111
108
  data: undefined
112
109
  signal: AbortSignal
113
- response: 'data'
114
110
  }>()
115
111
  })
116
112
  })
@@ -135,8 +131,8 @@ describe('createServerFn with middleware and validator', () => {
135
131
  middleware2,
136
132
  ])
137
133
 
138
- test(`response: 'data'`, () => {
139
- const fn = createServerFn({ method: 'GET', response: 'data' })
134
+ test(`response`, () => {
135
+ const fn = createServerFn({ method: 'GET' })
140
136
  .middleware([middleware3])
141
137
  .validator(
142
138
  (input: { readonly inputC: 'inputC' }) =>
@@ -154,7 +150,6 @@ describe('createServerFn with middleware and validator', () => {
154
150
  readonly outputC: 'outputC'
155
151
  }
156
152
  signal: AbortSignal
157
- response: 'data'
158
153
  }>()
159
154
 
160
155
  return 'some-data' as const
@@ -177,52 +172,6 @@ describe('createServerFn with middleware and validator', () => {
177
172
  }),
178
173
  ).returns.resolves.toEqualTypeOf<'some-data'>()
179
174
  })
180
-
181
- test(`response: 'full'`, () => {
182
- const fn = createServerFn({ method: 'GET', response: 'full' })
183
- .middleware([middleware3])
184
- .validator(
185
- (input: { readonly inputC: 'inputC' }) =>
186
- ({
187
- outputC: 'outputC',
188
- }) as const,
189
- )
190
- .handler((options) => {
191
- expectTypeOf(options).toEqualTypeOf<{
192
- method: 'GET'
193
- context: undefined
194
- data: {
195
- readonly outputA: 'outputA'
196
- readonly outputB: 'outputB'
197
- readonly outputC: 'outputC'
198
- }
199
- signal: AbortSignal
200
- response: 'full'
201
- }>()
202
-
203
- return 'some-data' as const
204
- })
205
-
206
- expectTypeOf(fn).parameter(0).toEqualTypeOf<{
207
- data: {
208
- readonly inputA: 'inputA'
209
- readonly inputB: 'inputB'
210
- readonly inputC: 'inputC'
211
- }
212
- headers?: HeadersInit
213
- signal?: AbortSignal
214
- }>()
215
-
216
- expectTypeOf(() =>
217
- fn({
218
- data: { inputA: 'inputA', inputB: 'inputB', inputC: 'inputC' },
219
- }),
220
- ).returns.resolves.toEqualTypeOf<{
221
- result: 'some-data'
222
- context: undefined
223
- error: unknown
224
- }>()
225
- })
226
175
  })
227
176
 
228
177
  test('createServerFn overrides properties', () => {
@@ -298,7 +247,6 @@ test('createServerFn where validator is a primitive', () => {
298
247
  context: undefined
299
248
  data: 'c'
300
249
  signal: AbortSignal
301
- response: 'data'
302
250
  }>()
303
251
  })
304
252
  })
@@ -312,7 +260,6 @@ test('createServerFn where validator is optional if object is optional', () => {
312
260
  context: undefined
313
261
  data: 'c' | undefined
314
262
  signal: AbortSignal
315
- response: 'data'
316
263
  }>()
317
264
  })
318
265
 
@@ -335,7 +282,6 @@ test('createServerFn where data is optional if there is no validator', () => {
335
282
  context: undefined
336
283
  data: undefined
337
284
  signal: AbortSignal
338
- response: 'data'
339
285
  }>()
340
286
  })
341
287
 
@@ -371,6 +317,7 @@ test('createServerFn cannot return function', () => {
371
317
  expectTypeOf(createServerFn().handler<{ func: () => 'func' }>)
372
318
  .parameter(0)
373
319
  .returns.toEqualTypeOf<
320
+ | Response
374
321
  | { func: 'Function is not serializable' }
375
322
  | Promise<{ func: 'Function is not serializable' }>
376
323
  >()
@@ -398,74 +345,38 @@ test('createServerFn can validate Date', () => {
398
345
 
399
346
  expectTypeOf(validator)
400
347
  .parameter(0)
401
- .toEqualTypeOf<ConstrainValidator<(input: Date) => { output: 'string' }>>()
348
+ .toEqualTypeOf<
349
+ ConstrainValidator<Register, 'GET', (input: Date) => { output: 'string' }>
350
+ >()
402
351
  })
403
352
 
404
353
  test('createServerFn can validate FormData', () => {
405
- const validator = createServerFn().validator<
354
+ const validator = createServerFn({ method: 'POST' }).validator<
355
+ (input: FormData) => { output: 'string' }
356
+ >
357
+
358
+ expectTypeOf(validator).parameter(0).parameter(0).toEqualTypeOf<FormData>()
359
+ })
360
+
361
+ test('createServerFn cannot validate FormData for GET', () => {
362
+ const validator = createServerFn({ method: 'GET' }).validator<
406
363
  (input: FormData) => { output: 'string' }
407
364
  >
408
365
 
409
366
  expectTypeOf(validator)
410
367
  .parameter(0)
411
- .toEqualTypeOf<
412
- ConstrainValidator<(input: FormData) => { output: 'string' }>
413
- >()
368
+ .parameter(0)
369
+ .not.toEqualTypeOf<FormData>()
414
370
  })
415
371
 
416
372
  describe('response', () => {
417
- describe('data', () => {
418
- test(`response: 'data' is passed into handler without response being set`, () => {
419
- createServerFn().handler((options) => {
420
- expectTypeOf(options.response).toEqualTypeOf<'data'>()
421
- })
422
- })
423
-
424
- test(`response: 'data' is passed into handler with explicit response: 'data'`, () => {
425
- createServerFn({ response: 'data' }).handler((options) => {
426
- expectTypeOf(options.response).toEqualTypeOf<'data'>()
427
- })
428
- })
429
- })
430
- describe('full', () => {
431
- test(`response: 'full' is passed into handler`, () => {
432
- createServerFn({ response: 'full' }).handler((options) => {
433
- expectTypeOf(options.response).toEqualTypeOf<'full'>()
434
- })
435
- })
436
- })
437
-
438
- describe('raw', () => {
439
- test(`response: 'raw' is passed into handler`, () => {
440
- createServerFn({ response: 'raw' }).handler((options) => {
441
- expectTypeOf(options.response).toEqualTypeOf<'raw'>()
442
- return null
443
- })
444
- })
445
- })
446
373
  test(`client receives Response when Response is returned`, () => {
447
- const fn = createServerFn({ response: 'raw' }).handler(() => {
374
+ const fn = createServerFn().handler(() => {
448
375
  return new Response('Hello World')
449
376
  })
450
377
 
451
378
  expectTypeOf(fn()).toEqualTypeOf<Promise<Response>>()
452
379
  })
453
-
454
- test(`client receives Response when ReadableStream is returned`, () => {
455
- const fn = createServerFn({ response: 'raw' }).handler(() => {
456
- return new ReadableStream()
457
- })
458
-
459
- expectTypeOf(fn()).toEqualTypeOf<Promise<Response>>()
460
- })
461
-
462
- test(`client receives Response when string is returned`, () => {
463
- const fn = createServerFn({ response: 'raw' }).handler(() => {
464
- return 'hello'
465
- })
466
-
467
- expectTypeOf(fn()).toEqualTypeOf<Promise<Response>>()
468
- })
469
380
  })
470
381
 
471
382
  test('createServerFn can be used as a mutation function', () => {
@@ -511,3 +422,118 @@ test('createServerFn validator infers unknown for default input type', () => {
511
422
 
512
423
  expectTypeOf(fn()).toEqualTypeOf<Promise<'failed' | 'success'>>()
513
424
  })
425
+
426
+ test('incrementally building createServerFn with multiple middleware calls', () => {
427
+ const middleware1 = createMiddleware({ type: 'function' }).server(
428
+ ({ next }) => {
429
+ return next({ context: { a: 'a' } as const })
430
+ },
431
+ )
432
+
433
+ const middleware2 = createMiddleware({ type: 'function' }).server(
434
+ ({ next }) => {
435
+ return next({ context: { b: 'b' } as const })
436
+ },
437
+ )
438
+
439
+ const middleware3 = createMiddleware({ type: 'function' }).server(
440
+ ({ next }) => {
441
+ return next({ context: { c: 'c' } as const })
442
+ },
443
+ )
444
+
445
+ const builderWithMw1 = createServerFn({ method: 'GET' }).middleware([
446
+ middleware1,
447
+ ])
448
+
449
+ expectTypeOf(builderWithMw1).toHaveProperty('handler')
450
+ expectTypeOf(builderWithMw1).toHaveProperty('validator')
451
+ expectTypeOf(builderWithMw1).toHaveProperty('middleware')
452
+
453
+ builderWithMw1.handler((options) => {
454
+ expectTypeOf(options).toEqualTypeOf<{
455
+ method: 'GET'
456
+ context: {
457
+ readonly a: 'a'
458
+ }
459
+ data: undefined
460
+ signal: AbortSignal
461
+ }>()
462
+ })
463
+
464
+ // overrides method
465
+ const builderWithMw2 = builderWithMw1({ method: 'POST' }).middleware([
466
+ middleware2,
467
+ ])
468
+
469
+ expectTypeOf(builderWithMw2).toHaveProperty('handler')
470
+ expectTypeOf(builderWithMw2).toHaveProperty('validator')
471
+ expectTypeOf(builderWithMw2).toHaveProperty('middleware')
472
+
473
+ builderWithMw2.handler((options) => {
474
+ expectTypeOf(options).toEqualTypeOf<{
475
+ method: 'POST'
476
+ context: {
477
+ readonly a: 'a'
478
+ readonly b: 'b'
479
+ }
480
+ data: undefined
481
+ signal: AbortSignal
482
+ }>()
483
+ })
484
+
485
+ // overrides method again
486
+ const builderWithMw3 = builderWithMw2({ method: 'GET' }).middleware([
487
+ middleware3,
488
+ ])
489
+
490
+ expectTypeOf(builderWithMw3).toHaveProperty('handler')
491
+ expectTypeOf(builderWithMw3).toHaveProperty('validator')
492
+ expectTypeOf(builderWithMw3).toHaveProperty('middleware')
493
+
494
+ builderWithMw3.handler((options) => {
495
+ expectTypeOf(options).toEqualTypeOf<{
496
+ method: 'GET'
497
+ context: {
498
+ readonly a: 'a'
499
+ readonly b: 'b'
500
+ readonly c: 'c'
501
+ }
502
+ data: undefined
503
+ signal: AbortSignal
504
+ }>()
505
+ })
506
+ })
507
+
508
+ test('compose middlewares and server function factories', () => {
509
+ const middleware1 = createMiddleware({ type: 'function' }).server(
510
+ ({ next }) => {
511
+ return next({ context: { a: 'a' } as const })
512
+ },
513
+ )
514
+
515
+ const middleware2 = createMiddleware({ type: 'function' }).server(
516
+ ({ next }) => {
517
+ return next({ context: { b: 'b' } as const })
518
+ },
519
+ )
520
+
521
+ const builderWithMw1 = createServerFn().middleware([middleware1])
522
+
523
+ const composedBuilder = createServerFn({ method: 'GET' }).middleware([
524
+ middleware2,
525
+ builderWithMw1,
526
+ ])
527
+
528
+ composedBuilder.handler((options) => {
529
+ expectTypeOf(options).toEqualTypeOf<{
530
+ method: 'GET'
531
+ context: {
532
+ readonly a: 'a'
533
+ readonly b: 'b'
534
+ }
535
+ data: undefined
536
+ signal: AbortSignal
537
+ }>()
538
+ })
539
+ })
@@ -1,8 +1,8 @@
1
1
  import { expectTypeOf, test } from 'vitest'
2
2
  import { createMiddleware } from '../createMiddleware'
3
3
  import type { RequestServerNextFn } from '../createMiddleware'
4
- import type { Constrain, Validator } from '@tanstack/router-core'
5
4
  import type { ConstrainValidator } from '../createServerFn'
5
+ import type { Register } from '@tanstack/router-core'
6
6
 
7
7
  test('createServeMiddleware removes middleware after middleware,', () => {
8
8
  const middleware = createMiddleware({ type: 'function' })
@@ -211,7 +211,7 @@ test('createMiddleware merges client context and sends to the server', () => {
211
211
  expectTypeOf(result).toEqualTypeOf<{
212
212
  'use functions must return the result of next()': true
213
213
  context: { a: boolean; b: string; c: number }
214
- sendContext: { a: boolean; b: string; c: number; d: number }
214
+ sendContext: { a: boolean; b: string; c: number; d: 5 }
215
215
  headers: HeadersInit
216
216
  }>()
217
217
 
@@ -225,7 +225,7 @@ test('createMiddleware merges client context and sends to the server', () => {
225
225
  a: boolean
226
226
  b: string
227
227
  c: number
228
- d: number
228
+ d: 5
229
229
  }>()
230
230
 
231
231
  const result = await options.next({
@@ -242,7 +242,7 @@ test('createMiddleware merges client context and sends to the server', () => {
242
242
  }
243
243
  sendContext: undefined
244
244
  }
245
- context: { a: boolean; b: string; c: number; d: number; e: string }
245
+ context: { a: boolean; b: string; c: number; d: 5; e: string }
246
246
  sendContext: undefined
247
247
  }>()
248
248
 
@@ -586,9 +586,10 @@ test('createMiddleware cannot validate function', () => {
586
586
  expectTypeOf(validator)
587
587
  .parameter(0)
588
588
  .toEqualTypeOf<
589
- Constrain<
590
- (input: { func: () => 'string' }) => { output: 'string' },
591
- Validator<{ func: 'Function is not serializable' }, any>
589
+ ConstrainValidator<
590
+ Register,
591
+ 'GET',
592
+ (input: { func: () => 'string' }) => { output: 'string' }
592
593
  >
593
594
  >()
594
595
  })
@@ -600,7 +601,9 @@ test('createMiddleware can validate Date', () => {
600
601
 
601
602
  expectTypeOf(validator)
602
603
  .parameter(0)
603
- .toEqualTypeOf<ConstrainValidator<(input: Date) => { output: 'string' }>>()
604
+ .toEqualTypeOf<
605
+ ConstrainValidator<Register, 'GET', (input: Date) => { output: 'string' }>
606
+ >()
604
607
  })
605
608
 
606
609
  test('createMiddleware can validate FormData', () => {
@@ -611,7 +614,11 @@ test('createMiddleware can validate FormData', () => {
611
614
  expectTypeOf(validator)
612
615
  .parameter(0)
613
616
  .toEqualTypeOf<
614
- ConstrainValidator<(input: FormData) => { output: 'string' }>
617
+ ConstrainValidator<
618
+ Register,
619
+ 'GET',
620
+ (input: FormData) => { output: 'string' }
621
+ >
615
622
  >()
616
623
  })
617
624
 
@@ -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
  })
@@ -1,23 +0,0 @@
1
- export interface StartSerializer {
2
- stringify: (obj: unknown) => string;
3
- parse: (str: string) => unknown;
4
- encode: <T>(value: T) => T;
5
- decode: <T>(value: T) => T;
6
- }
7
- export type SerializerStringifyBy<T, TSerializable> = T extends TSerializable ? T : T extends (...args: Array<any>) => any ? 'Function is not serializable' : {
8
- [K in keyof T]: SerializerStringifyBy<T[K], TSerializable>;
9
- };
10
- export type SerializerParseBy<T, TSerializable> = T extends TSerializable ? T : unknown extends SerializerExtensions['ReadableStream'] ? {
11
- [K in keyof T]: SerializerParseBy<T[K], TSerializable>;
12
- } : T extends SerializerExtensions['ReadableStream'] ? ReadableStream : {
13
- [K in keyof T]: SerializerParseBy<T[K], TSerializable>;
14
- };
15
- export interface DefaultSerializerExtensions {
16
- ReadableStream: unknown;
17
- }
18
- export interface SerializerExtensions extends DefaultSerializerExtensions {
19
- }
20
- export type Serializable = Date | undefined | Error | FormData | bigint;
21
- export type SerializerStringify<T> = SerializerStringifyBy<T, Serializable>;
22
- export type SerializerParse<T> = SerializerParseBy<T, Serializable>;
23
- export declare const startSerializer: StartSerializer;
@@ -1,162 +0,0 @@
1
- import { isPlainObject } from "@tanstack/router-core";
2
- const startSerializer = {
3
- stringify: (value) => JSON.stringify(value, function replacer(key, val) {
4
- const ogVal = this[key];
5
- const serializer = serializers.find((t) => t.stringifyCondition(ogVal));
6
- if (serializer) {
7
- return serializer.stringify(ogVal);
8
- }
9
- return val;
10
- }),
11
- parse: (value) => JSON.parse(value, function parser(key, val) {
12
- const ogVal = this[key];
13
- if (isPlainObject(ogVal)) {
14
- const serializer = serializers.find((t) => t.parseCondition(ogVal));
15
- if (serializer) {
16
- return serializer.parse(ogVal);
17
- }
18
- }
19
- return val;
20
- }),
21
- encode: (value) => {
22
- if (Array.isArray(value)) {
23
- return value.map((v) => startSerializer.encode(v));
24
- }
25
- if (isPlainObject(value)) {
26
- return Object.fromEntries(
27
- Object.entries(value).map(([key, v]) => [
28
- key,
29
- startSerializer.encode(v)
30
- ])
31
- );
32
- }
33
- const serializer = serializers.find((t) => t.stringifyCondition(value));
34
- if (serializer) {
35
- return serializer.stringify(value);
36
- }
37
- return value;
38
- },
39
- decode: (value) => {
40
- if (isPlainObject(value)) {
41
- const serializer = serializers.find((t) => t.parseCondition(value));
42
- if (serializer) {
43
- return serializer.parse(value);
44
- }
45
- }
46
- if (Array.isArray(value)) {
47
- return value.map((v) => startSerializer.decode(v));
48
- }
49
- if (isPlainObject(value)) {
50
- return Object.fromEntries(
51
- Object.entries(value).map(([key, v]) => [
52
- key,
53
- startSerializer.decode(v)
54
- ])
55
- );
56
- }
57
- return value;
58
- }
59
- };
60
- const createSerializer = (key, check, toValue, fromValue) => ({
61
- key,
62
- stringifyCondition: check,
63
- stringify: (value) => ({ [`$${key}`]: toValue(value) }),
64
- parseCondition: (value) => Object.hasOwn(value, `$${key}`),
65
- parse: (value) => fromValue(value[`$${key}`])
66
- });
67
- const serializers = [
68
- createSerializer(
69
- // Key
70
- "undefined",
71
- // Check
72
- (v) => v === void 0,
73
- // To
74
- () => 0,
75
- // From
76
- () => void 0
77
- ),
78
- createSerializer(
79
- // Key
80
- "date",
81
- // Check
82
- (v) => v instanceof Date,
83
- // To
84
- (v) => v.toISOString(),
85
- // From
86
- (v) => new Date(v)
87
- ),
88
- createSerializer(
89
- // Key
90
- "error",
91
- // Check
92
- (v) => v instanceof Error,
93
- // To
94
- (v) => ({
95
- ...v,
96
- message: v.message,
97
- stack: process.env.NODE_ENV === "development" ? v.stack : void 0,
98
- cause: v.cause
99
- }),
100
- // From
101
- (v) => Object.assign(new Error(v.message), v)
102
- ),
103
- createSerializer(
104
- // Key
105
- "formData",
106
- // Check
107
- (v) => v instanceof FormData,
108
- // To
109
- (v) => {
110
- const entries = {};
111
- v.forEach((value, key) => {
112
- const entry = entries[key];
113
- if (entry !== void 0) {
114
- if (Array.isArray(entry)) {
115
- entry.push(value);
116
- } else {
117
- entries[key] = [entry, value];
118
- }
119
- } else {
120
- entries[key] = value;
121
- }
122
- });
123
- return entries;
124
- },
125
- // From
126
- (v) => {
127
- const formData = new FormData();
128
- Object.entries(v).forEach(([key, value]) => {
129
- if (Array.isArray(value)) {
130
- value.forEach((val) => formData.append(key, val));
131
- } else {
132
- formData.append(key, value);
133
- }
134
- });
135
- return formData;
136
- }
137
- ),
138
- createSerializer(
139
- // Key
140
- "bigint",
141
- // Check
142
- (v) => typeof v === "bigint",
143
- // To
144
- (v) => v.toString(),
145
- // From
146
- (v) => BigInt(v)
147
- ),
148
- createSerializer(
149
- // Key
150
- "server-function",
151
- // Check
152
- (v) => typeof v === "function" && "functionId" in v && typeof v.functionId === "string",
153
- // To
154
- ({ functionId }) => ({ functionId, __serverFn: true }),
155
- // From, dummy impl. the actual server function lookup is done on the server in packages/start-server-core/src/server-functions-handler.ts
156
- (v) => v
157
- )
158
- ];
159
- export {
160
- startSerializer
161
- };
162
- //# sourceMappingURL=serializer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"serializer.js","sources":["../../src/serializer.ts"],"sourcesContent":["import { isPlainObject } from '@tanstack/router-core'\n\nexport interface StartSerializer {\n stringify: (obj: unknown) => string\n parse: (str: string) => unknown\n encode: <T>(value: T) => T\n decode: <T>(value: T) => T\n}\n\nexport type SerializerStringifyBy<T, TSerializable> = T extends TSerializable\n ? T\n : T extends (...args: Array<any>) => any\n ? 'Function is not serializable'\n : { [K in keyof T]: SerializerStringifyBy<T[K], TSerializable> }\n\nexport type SerializerParseBy<T, TSerializable> = T extends TSerializable\n ? T\n : unknown extends SerializerExtensions['ReadableStream']\n ? { [K in keyof T]: SerializerParseBy<T[K], TSerializable> }\n : T extends SerializerExtensions['ReadableStream']\n ? ReadableStream\n : { [K in keyof T]: SerializerParseBy<T[K], TSerializable> }\n\nexport interface DefaultSerializerExtensions {\n ReadableStream: unknown\n}\n\nexport interface SerializerExtensions extends DefaultSerializerExtensions {}\n\nexport type Serializable = Date | undefined | Error | FormData | bigint\n\nexport type SerializerStringify<T> = SerializerStringifyBy<T, Serializable>\n\nexport type SerializerParse<T> = SerializerParseBy<T, Serializable>\nexport const startSerializer: StartSerializer = {\n stringify: (value: any) =>\n JSON.stringify(value, function replacer(key, val) {\n const ogVal = this[key]\n const serializer = serializers.find((t) => t.stringifyCondition(ogVal))\n\n if (serializer) {\n return serializer.stringify(ogVal)\n }\n\n return val\n }),\n parse: (value: string) =>\n JSON.parse(value, function parser(key, val) {\n const ogVal = this[key]\n if (isPlainObject(ogVal)) {\n const serializer = serializers.find((t) => t.parseCondition(ogVal))\n\n if (serializer) {\n return serializer.parse(ogVal)\n }\n }\n\n return val\n }),\n encode: (value: any) => {\n // When encoding, dive first\n if (Array.isArray(value)) {\n return value.map((v) => startSerializer.encode(v))\n }\n\n if (isPlainObject(value)) {\n return Object.fromEntries(\n Object.entries(value).map(([key, v]) => [\n key,\n startSerializer.encode(v),\n ]),\n )\n }\n\n const serializer = serializers.find((t) => t.stringifyCondition(value))\n if (serializer) {\n return serializer.stringify(value)\n }\n\n return value\n },\n decode: (value: any) => {\n // Attempt transform first\n if (isPlainObject(value)) {\n const serializer = serializers.find((t) => t.parseCondition(value))\n if (serializer) {\n return serializer.parse(value)\n }\n }\n\n if (Array.isArray(value)) {\n return value.map((v) => startSerializer.decode(v))\n }\n\n if (isPlainObject(value)) {\n return Object.fromEntries(\n Object.entries(value).map(([key, v]) => [\n key,\n startSerializer.decode(v),\n ]),\n )\n }\n\n return value\n },\n}\nconst createSerializer = <TKey extends string, TInput, TSerialized>(\n key: TKey,\n check: (value: any) => value is TInput,\n toValue: (value: TInput) => TSerialized,\n fromValue: (value: TSerialized) => TInput,\n) => ({\n key,\n stringifyCondition: check,\n stringify: (value: any) => ({ [`$${key}`]: toValue(value) }),\n parseCondition: (value: any) => Object.hasOwn(value, `$${key}`),\n parse: (value: any) => fromValue(value[`$${key}`]),\n})\n// Keep these ordered by predicted frequency\n// Make sure to keep DefaultSerializable in sync with these serializers\n// Also, make sure that they are unit tested in serializer.test.tsx\nconst serializers = [\n createSerializer(\n // Key\n 'undefined',\n // Check\n (v): v is undefined => v === undefined,\n // To\n () => 0,\n // From\n () => undefined,\n ),\n createSerializer(\n // Key\n 'date',\n // Check\n (v): v is Date => v instanceof Date,\n // To\n (v) => v.toISOString(),\n // From\n (v) => new Date(v),\n ),\n createSerializer(\n // Key\n 'error',\n // Check\n (v): v is Error => v instanceof Error,\n // To\n (v) => ({\n ...v,\n message: v.message,\n stack: process.env.NODE_ENV === 'development' ? v.stack : undefined,\n cause: v.cause,\n }),\n // From\n (v) => Object.assign(new Error(v.message), v),\n ),\n createSerializer(\n // Key\n 'formData',\n // Check\n (v): v is FormData => v instanceof FormData,\n // To\n (v) => {\n const entries: Record<\n string,\n Array<FormDataEntryValue> | FormDataEntryValue\n > = {}\n v.forEach((value, key) => {\n const entry = entries[key]\n if (entry !== undefined) {\n if (Array.isArray(entry)) {\n entry.push(value)\n } else {\n entries[key] = [entry, value]\n }\n } else {\n entries[key] = value\n }\n })\n return entries\n },\n // From\n (v) => {\n const formData = new FormData()\n Object.entries(v).forEach(([key, value]) => {\n if (Array.isArray(value)) {\n value.forEach((val) => formData.append(key, val))\n } else {\n formData.append(key, value)\n }\n })\n return formData\n },\n ),\n createSerializer(\n // Key\n 'bigint',\n // Check\n (v): v is bigint => typeof v === 'bigint',\n // To\n (v) => v.toString(),\n // From\n (v) => BigInt(v),\n ),\n createSerializer(\n // Key\n 'server-function',\n // Check\n (v): v is { functionId: string } =>\n typeof v === 'function' &&\n 'functionId' in v &&\n typeof v.functionId === 'string',\n // To\n ({ functionId }) => ({ functionId, __serverFn: true }),\n // From, dummy impl. the actual server function lookup is done on the server in packages/start-server-core/src/server-functions-handler.ts\n (v) => v,\n ),\n] as const\n"],"names":[],"mappings":";AAkCO,MAAM,kBAAmC;AAAA,EAC9C,WAAW,CAAC,UACV,KAAK,UAAU,OAAO,SAAS,SAAS,KAAK,KAAK;AAChD,UAAM,QAAQ,KAAK,GAAG;AACtB,UAAM,aAAa,YAAY,KAAK,CAAC,MAAM,EAAE,mBAAmB,KAAK,CAAC;AAEtE,QAAI,YAAY;AACd,aAAO,WAAW,UAAU,KAAK;AAAA,IACnC;AAEA,WAAO;AAAA,EACT,CAAC;AAAA,EACH,OAAO,CAAC,UACN,KAAK,MAAM,OAAO,SAAS,OAAO,KAAK,KAAK;AAC1C,UAAM,QAAQ,KAAK,GAAG;AACtB,QAAI,cAAc,KAAK,GAAG;AACxB,YAAM,aAAa,YAAY,KAAK,CAAC,MAAM,EAAE,eAAe,KAAK,CAAC;AAElE,UAAI,YAAY;AACd,eAAO,WAAW,MAAM,KAAK;AAAA,MAC/B;AAAA,IACF;AAEA,WAAO;AAAA,EACT,CAAC;AAAA,EACH,QAAQ,CAAC,UAAe;AAEtB,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,aAAO,MAAM,IAAI,CAAC,MAAM,gBAAgB,OAAO,CAAC,CAAC;AAAA,IACnD;AAEA,QAAI,cAAc,KAAK,GAAG;AACxB,aAAO,OAAO;AAAA,QACZ,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM;AAAA,UACtC;AAAA,UACA,gBAAgB,OAAO,CAAC;AAAA,QAAA,CACzB;AAAA,MAAA;AAAA,IAEL;AAEA,UAAM,aAAa,YAAY,KAAK,CAAC,MAAM,EAAE,mBAAmB,KAAK,CAAC;AACtE,QAAI,YAAY;AACd,aAAO,WAAW,UAAU,KAAK;AAAA,IACnC;AAEA,WAAO;AAAA,EACT;AAAA,EACA,QAAQ,CAAC,UAAe;AAEtB,QAAI,cAAc,KAAK,GAAG;AACxB,YAAM,aAAa,YAAY,KAAK,CAAC,MAAM,EAAE,eAAe,KAAK,CAAC;AAClE,UAAI,YAAY;AACd,eAAO,WAAW,MAAM,KAAK;AAAA,MAC/B;AAAA,IACF;AAEA,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,aAAO,MAAM,IAAI,CAAC,MAAM,gBAAgB,OAAO,CAAC,CAAC;AAAA,IACnD;AAEA,QAAI,cAAc,KAAK,GAAG;AACxB,aAAO,OAAO;AAAA,QACZ,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM;AAAA,UACtC;AAAA,UACA,gBAAgB,OAAO,CAAC;AAAA,QAAA,CACzB;AAAA,MAAA;AAAA,IAEL;AAEA,WAAO;AAAA,EACT;AACF;AACA,MAAM,mBAAmB,CACvB,KACA,OACA,SACA,eACI;AAAA,EACJ;AAAA,EACA,oBAAoB;AAAA,EACpB,WAAW,CAAC,WAAgB,EAAE,CAAC,IAAI,GAAG,EAAE,GAAG,QAAQ,KAAK;EACxD,gBAAgB,CAAC,UAAe,OAAO,OAAO,OAAO,IAAI,GAAG,EAAE;AAAA,EAC9D,OAAO,CAAC,UAAe,UAAU,MAAM,IAAI,GAAG,EAAE,CAAC;AACnD;AAIA,MAAM,cAAc;AAAA,EAClB;AAAA;AAAA,IAEE;AAAA;AAAA,IAEA,CAAC,MAAsB,MAAM;AAAA;AAAA,IAE7B,MAAM;AAAA;AAAA,IAEN,MAAM;AAAA,EAAA;AAAA,EAER;AAAA;AAAA,IAEE;AAAA;AAAA,IAEA,CAAC,MAAiB,aAAa;AAAA;AAAA,IAE/B,CAAC,MAAM,EAAE,YAAA;AAAA;AAAA,IAET,CAAC,MAAM,IAAI,KAAK,CAAC;AAAA,EAAA;AAAA,EAEnB;AAAA;AAAA,IAEE;AAAA;AAAA,IAEA,CAAC,MAAkB,aAAa;AAAA;AAAA,IAEhC,CAAC,OAAO;AAAA,MACN,GAAG;AAAA,MACH,SAAS,EAAE;AAAA,MACX,OAAO,QAAQ,IAAI,aAAa,gBAAgB,EAAE,QAAQ;AAAA,MAC1D,OAAO,EAAE;AAAA,IAAA;AAAA;AAAA,IAGX,CAAC,MAAM,OAAO,OAAO,IAAI,MAAM,EAAE,OAAO,GAAG,CAAC;AAAA,EAAA;AAAA,EAE9C;AAAA;AAAA,IAEE;AAAA;AAAA,IAEA,CAAC,MAAqB,aAAa;AAAA;AAAA,IAEnC,CAAC,MAAM;AACL,YAAM,UAGF,CAAA;AACJ,QAAE,QAAQ,CAAC,OAAO,QAAQ;AACxB,cAAM,QAAQ,QAAQ,GAAG;AACzB,YAAI,UAAU,QAAW;AACvB,cAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,kBAAM,KAAK,KAAK;AAAA,UAClB,OAAO;AACL,oBAAQ,GAAG,IAAI,CAAC,OAAO,KAAK;AAAA,UAC9B;AAAA,QACF,OAAO;AACL,kBAAQ,GAAG,IAAI;AAAA,QACjB;AAAA,MACF,CAAC;AACD,aAAO;AAAA,IACT;AAAA;AAAA,IAEA,CAAC,MAAM;AACL,YAAM,WAAW,IAAI,SAAA;AACrB,aAAO,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AAC1C,YAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,gBAAM,QAAQ,CAAC,QAAQ,SAAS,OAAO,KAAK,GAAG,CAAC;AAAA,QAClD,OAAO;AACL,mBAAS,OAAO,KAAK,KAAK;AAAA,QAC5B;AAAA,MACF,CAAC;AACD,aAAO;AAAA,IACT;AAAA,EAAA;AAAA,EAEF;AAAA;AAAA,IAEE;AAAA;AAAA,IAEA,CAAC,MAAmB,OAAO,MAAM;AAAA;AAAA,IAEjC,CAAC,MAAM,EAAE,SAAA;AAAA;AAAA,IAET,CAAC,MAAM,OAAO,CAAC;AAAA,EAAA;AAAA,EAEjB;AAAA;AAAA,IAEE;AAAA;AAAA,IAEA,CAAC,MACC,OAAO,MAAM,cACb,gBAAgB,KAChB,OAAO,EAAE,eAAe;AAAA;AAAA,IAE1B,CAAC,EAAE,WAAA,OAAkB,EAAE,YAAY,YAAY,KAAA;AAAA;AAAA,IAE/C,CAAC,MAAM;AAAA,EAAA;AAEX;"}
@@ -1 +0,0 @@
1
- export {};