@orpc/server 0.0.0-next.b15d206 → 0.0.0-next.c29cb6d

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 (80) hide show
  1. package/dist/chunk-YAVPFNPF.js +182 -0
  2. package/dist/fetch.js +237 -632
  3. package/dist/index.js +402 -278
  4. package/dist/src/builder.d.ts +26 -40
  5. package/dist/src/fetch/composite-handler.d.ts +8 -0
  6. package/dist/src/fetch/index.d.ts +6 -0
  7. package/dist/src/fetch/orpc-handler.d.ts +20 -0
  8. package/dist/src/fetch/orpc-payload-codec.d.ts +9 -0
  9. package/dist/src/fetch/orpc-procedure-matcher.d.ts +12 -0
  10. package/dist/src/fetch/super-json.d.ts +12 -0
  11. package/dist/src/fetch/types.d.ts +16 -0
  12. package/dist/src/hidden.d.ts +6 -0
  13. package/dist/src/implementer-chainable.d.ts +10 -0
  14. package/dist/src/index.d.ts +11 -3
  15. package/dist/src/lazy-decorated.d.ts +10 -0
  16. package/dist/src/lazy-utils.d.ts +4 -0
  17. package/dist/src/lazy.d.ts +18 -0
  18. package/dist/src/middleware-decorated.d.ts +8 -0
  19. package/dist/src/middleware.d.ts +3 -6
  20. package/dist/src/procedure-builder.d.ts +15 -24
  21. package/dist/src/procedure-client.d.ts +34 -0
  22. package/dist/src/procedure-decorated.d.ts +14 -0
  23. package/dist/src/procedure-implementer.d.ts +13 -13
  24. package/dist/src/procedure.d.ts +18 -24
  25. package/dist/src/router-builder.d.ts +24 -17
  26. package/dist/src/router-client.d.ts +25 -0
  27. package/dist/src/router-implementer.d.ts +18 -17
  28. package/dist/src/router.d.ts +11 -15
  29. package/dist/src/types.d.ts +8 -4
  30. package/package.json +13 -21
  31. package/dist/chunk-TDFYNRZV.js +0 -190
  32. package/dist/chunk-TDFYNRZV.js.map +0 -1
  33. package/dist/fetch.js.map +0 -1
  34. package/dist/index.js.map +0 -1
  35. package/dist/src/adapters/fetch.d.ts +0 -42
  36. package/dist/src/adapters/fetch.d.ts.map +0 -1
  37. package/dist/src/builder.d.ts.map +0 -1
  38. package/dist/src/index.d.ts.map +0 -1
  39. package/dist/src/middleware.d.ts.map +0 -1
  40. package/dist/src/procedure-builder.d.ts.map +0 -1
  41. package/dist/src/procedure-caller.d.ts +0 -19
  42. package/dist/src/procedure-caller.d.ts.map +0 -1
  43. package/dist/src/procedure-implementer.d.ts.map +0 -1
  44. package/dist/src/procedure.d.ts.map +0 -1
  45. package/dist/src/router-builder.d.ts.map +0 -1
  46. package/dist/src/router-caller.d.ts +0 -22
  47. package/dist/src/router-caller.d.ts.map +0 -1
  48. package/dist/src/router-implementer.d.ts.map +0 -1
  49. package/dist/src/router.d.ts.map +0 -1
  50. package/dist/src/types.d.ts.map +0 -1
  51. package/dist/src/utils.d.ts.map +0 -1
  52. package/dist/tsconfig.tsbuildinfo +0 -1
  53. package/src/adapters/fetch.test.ts +0 -629
  54. package/src/adapters/fetch.ts +0 -290
  55. package/src/builder.test.ts +0 -371
  56. package/src/builder.ts +0 -238
  57. package/src/index.ts +0 -16
  58. package/src/middleware.test.ts +0 -260
  59. package/src/middleware.ts +0 -136
  60. package/src/procedure-builder.test.ts +0 -223
  61. package/src/procedure-builder.ts +0 -158
  62. package/src/procedure-caller.test.ts +0 -171
  63. package/src/procedure-caller.ts +0 -138
  64. package/src/procedure-implementer.test.ts +0 -220
  65. package/src/procedure-implementer.ts +0 -102
  66. package/src/procedure.test.ts +0 -317
  67. package/src/procedure.ts +0 -237
  68. package/src/router-builder.test.ts +0 -106
  69. package/src/router-builder.ts +0 -122
  70. package/src/router-caller.test.ts +0 -126
  71. package/src/router-caller.ts +0 -64
  72. package/src/router-implementer.test.ts +0 -116
  73. package/src/router-implementer.ts +0 -113
  74. package/src/router.test-d.ts +0 -48
  75. package/src/router.test.ts +0 -142
  76. package/src/router.ts +0 -91
  77. package/src/types.test.ts +0 -18
  78. package/src/types.ts +0 -13
  79. package/src/utils.test.ts +0 -16
  80. package/src/utils.ts +0 -16
@@ -1,629 +0,0 @@
1
- import { ORPC_HEADER, ORPC_HEADER_VALUE } from '@orpc/contract'
2
- import { oz } from '@orpc/zod'
3
- import { describe, expect, it } from 'vitest'
4
- import { z } from 'zod'
5
- import { ORPCError, os } from '..'
6
- import { createFetchHandler } from './fetch'
7
-
8
- const router = os.router({
9
- throw: os.func(() => {
10
- throw new Error('test')
11
- }),
12
- ping: os.func(() => {
13
- return 'ping'
14
- }),
15
- ping2: os.route({ method: 'GET', path: '/ping2' }).func(() => {
16
- return 'ping2'
17
- }),
18
- })
19
-
20
- const handler = createFetchHandler({ router })
21
-
22
- describe('simple', () => {
23
- const osw = os.context<{ auth?: boolean }>()
24
- const router = osw.router({
25
- ping: osw.func(async () => 'pong'),
26
- ping2: osw
27
- .route({ method: 'GET', path: '/ping2' })
28
- .func(async () => 'pong2'),
29
- })
30
- const handler = createFetchHandler({
31
- router,
32
- })
33
-
34
- it('200: public url', async () => {
35
- const response = await handler({
36
- prefix: '/orpc',
37
- request: new Request('http://localhost/orpc/ping', {
38
- method: 'POST',
39
- }),
40
- context: () => ({ auth: true }),
41
- })
42
-
43
- expect(response.status).toBe(200)
44
- expect(await response.json()).toEqual('pong')
45
-
46
- const response2 = await handler({
47
- prefix: '/orpc',
48
- request: new Request('http://localhost/orpc/ping2', {
49
- method: 'GET',
50
- }),
51
- context: { auth: true },
52
- })
53
-
54
- expect(response2.status).toBe(200)
55
- expect(await response2.json()).toEqual('pong2')
56
- })
57
-
58
- it('200: internal url', async () => {
59
- const response = await handler({
60
- request: new Request('http://localhost/ping'),
61
- context: { auth: true },
62
- })
63
-
64
- expect(response.status).toBe(200)
65
- expect(await response.json()).toEqual('pong')
66
-
67
- const response2 = await handler({
68
- prefix: '/orpc',
69
- request: new Request('http://localhost/orpc/ping2'),
70
- context: { auth: true },
71
- })
72
-
73
- expect(response2.status).toBe(200)
74
- expect(await response2.json()).toEqual('pong2')
75
- })
76
-
77
- it('404', async () => {
78
- const response = await handler({
79
- prefix: '/orpc',
80
- request: new Request('http://localhost/orpc/pingp', {
81
- method: 'POST',
82
- }),
83
- context: { auth: true },
84
- })
85
-
86
- expect(response.status).toBe(404)
87
- })
88
- })
89
-
90
- describe('procedure throw error', () => {
91
- it('unknown error', async () => {
92
- const response = await handler({
93
- request: new Request('https://local.com/throw', { method: 'POST' }),
94
- })
95
-
96
- expect(response.status).toBe(500)
97
- expect(await response.json()).toEqual({
98
- code: 'INTERNAL_SERVER_ERROR',
99
- status: 500,
100
- message: 'Internal server error',
101
- })
102
- })
103
-
104
- it('orpc error', async () => {
105
- const router = os.router({
106
- ping: os.func(() => {
107
- throw new ORPCError({ code: 'TIMEOUT' })
108
- }),
109
- })
110
-
111
- const handler = createFetchHandler({ router })
112
-
113
- const response = await handler({
114
- request: new Request('https://local.com/ping', { method: 'POST' }),
115
- })
116
-
117
- expect(response.status).toBe(408)
118
- expect(await response.json()).toEqual({
119
- code: 'TIMEOUT',
120
- status: 408,
121
- message: '',
122
- })
123
- })
124
-
125
- it('orpc error with data', async () => {
126
- const router = os.router({
127
- ping: os.func(() => {
128
- throw new ORPCError({
129
- code: 'PAYLOAD_TOO_LARGE',
130
- message: 'test',
131
- data: { max: '10mb' },
132
- })
133
- }),
134
- })
135
-
136
- const handler = createFetchHandler({ router })
137
-
138
- const response = await handler({
139
- request: new Request('https://local.com/ping', { method: 'POST' }),
140
- })
141
-
142
- expect(response.status).toBe(413)
143
- expect(await response.json()).toEqual({
144
- code: 'PAYLOAD_TOO_LARGE',
145
- status: 413,
146
- message: 'test',
147
- data: { max: '10mb' },
148
- })
149
- })
150
-
151
- it('orpc error with custom status', async () => {
152
- const router = os.router({
153
- ping: os.func(() => {
154
- throw new ORPCError({
155
- code: 'PAYLOAD_TOO_LARGE',
156
- status: 100,
157
- })
158
- }),
159
-
160
- ping2: os.func(() => {
161
- throw new ORPCError({
162
- code: 'PAYLOAD_TOO_LARGE',
163
- status: 488,
164
- })
165
- }),
166
- })
167
-
168
- const handler = createFetchHandler({ router })
169
-
170
- const response = await handler({
171
- request: new Request('https://local.com/ping', { method: 'POST' }),
172
- })
173
-
174
- expect(response.status).toBe(500)
175
- expect(await response.json()).toEqual({
176
- code: 'INTERNAL_SERVER_ERROR',
177
- status: 500,
178
- message: 'Internal server error',
179
- })
180
-
181
- const response2 = await handler({
182
- request: new Request('https://local.com/ping2', { method: 'POST' }),
183
- })
184
-
185
- expect(response2.status).toBe(488)
186
- expect(await response2.json()).toEqual({
187
- code: 'PAYLOAD_TOO_LARGE',
188
- status: 488,
189
- message: '',
190
- })
191
- })
192
-
193
- it('input validation error', async () => {
194
- const router = os.router({
195
- ping: os
196
- .input(z.object({}))
197
- .output(z.string())
198
- .func(() => {
199
- return 'unnoq'
200
- }),
201
- })
202
-
203
- const handler = createFetchHandler({ router })
204
-
205
- const response = await handler({
206
- request: new Request('https://local.com/ping', { method: 'POST' }),
207
- })
208
-
209
- expect(response.status).toBe(400)
210
- expect(await response.json()).toEqual({
211
- code: 'BAD_REQUEST',
212
- status: 400,
213
- message: 'Validation input failed',
214
- issues: [
215
- {
216
- code: 'invalid_type',
217
- expected: 'object',
218
- message: 'Required',
219
- path: [],
220
- received: 'undefined',
221
- },
222
- ],
223
- })
224
- })
225
-
226
- it('output validation error', async () => {
227
- const router = os.router({
228
- ping: os
229
- .input(z.string())
230
- .output(z.string())
231
- .func(() => {
232
- return 12344 as any
233
- }),
234
- })
235
-
236
- const handler = createFetchHandler({ router })
237
-
238
- const response = await handler({
239
- request: new Request('https://local.com/ping', {
240
- method: 'POST',
241
- body: '"hi"',
242
- }),
243
- })
244
-
245
- expect(response.status).toBe(500)
246
- expect(await response.json()).toEqual({
247
- code: 'INTERNAL_SERVER_ERROR',
248
- status: 500,
249
- message: 'Validation output failed',
250
- })
251
- })
252
- })
253
-
254
- describe('hooks', () => {
255
- it('on success', async () => {
256
- const onSuccess = vi.fn()
257
- const onError = vi.fn()
258
- const onFinish = vi.fn()
259
-
260
- const handler = createFetchHandler({
261
- router,
262
- hooks: async (context, hooks) => {
263
- try {
264
- const response = await hooks.next()
265
- onSuccess(response)
266
- return response
267
- }
268
- catch (e) {
269
- onError(e)
270
- throw e
271
- }
272
- finally {
273
- onFinish()
274
- }
275
- },
276
- })
277
-
278
- await handler({
279
- prefix: '/orpc',
280
- request: new Request('http://localhost/orpc/ping', {
281
- method: 'POST',
282
- }),
283
- })
284
-
285
- expect(onSuccess).toHaveBeenCalledTimes(1)
286
- expect(onError).toHaveBeenCalledTimes(0)
287
- expect(onFinish).toHaveBeenCalledTimes(1)
288
-
289
- expect(onSuccess.mock.calls[0]?.[0]).toBeInstanceOf(Response)
290
- expect(onFinish.mock.calls[0]?.[1]).toBe(undefined)
291
- })
292
-
293
- it('on failed', async () => {
294
- const onSuccess = vi.fn()
295
- const onError = vi.fn()
296
- const onFinish = vi.fn()
297
-
298
- const handler = createFetchHandler({
299
- router,
300
- hooks: async (context, hooks) => {
301
- try {
302
- const response = await hooks.next()
303
- onSuccess(response)
304
- return response
305
- }
306
- catch (e) {
307
- onError(e)
308
- throw e
309
- }
310
- finally {
311
- onFinish()
312
- }
313
- },
314
- })
315
-
316
- await handler({
317
- prefix: '/orpc',
318
- request: new Request('http://localhost/orpc/throw', {
319
- method: 'POST',
320
- }),
321
- })
322
-
323
- expect(onSuccess).toHaveBeenCalledTimes(0)
324
- expect(onError).toHaveBeenCalledTimes(1)
325
- expect(onFinish).toHaveBeenCalledTimes(1)
326
-
327
- expect(onError.mock.calls[0]?.[0]).toBeInstanceOf(Error)
328
- expect(onError.mock.calls[0]?.[0]?.message).toBe('test')
329
- expect(onFinish.mock.calls[0]?.[0]).toBe(undefined)
330
- })
331
- })
332
-
333
- describe('file upload', () => {
334
- const router = os.router({
335
- signal: os.input(z.instanceof(Blob)).func((input) => {
336
- return input
337
- }),
338
- multiple: os
339
- .input(
340
- z.object({ first: z.instanceof(Blob), second: z.instanceof(Blob) }),
341
- )
342
- .func((input) => {
343
- return input
344
- }),
345
- })
346
-
347
- const handler = createFetchHandler({ router })
348
-
349
- const blob1 = new Blob(['hello'], { type: 'text/plain;charset=utf-8' })
350
- const blob2 = new Blob(['"world"'], { type: 'image/png' })
351
- const blob3 = new Blob(['unnoq'], { type: 'application/octet-stream' })
352
-
353
- it('single file', async () => {
354
- const rForm = new FormData()
355
- rForm.set('meta', JSON.stringify([]))
356
- rForm.set('maps', JSON.stringify([[]]))
357
- rForm.set('0', blob3)
358
-
359
- const response = await handler({
360
- prefix: '/orpc',
361
- request: new Request('http://localhost/orpc/signal', {
362
- method: 'POST',
363
- body: rForm,
364
- headers: {
365
- [ORPC_HEADER]: ORPC_HEADER_VALUE,
366
- },
367
- }),
368
- })
369
-
370
- expect(response.status).toBe(200)
371
- const form = await response.formData()
372
-
373
- const file0 = form.get('0') as File
374
- expect(file0).toBeInstanceOf(File)
375
- expect(file0.name).toBe('blob')
376
- expect(file0.type).toBe('application/octet-stream')
377
- expect(await file0.text()).toBe('unnoq')
378
- })
379
-
380
- it('multiple file', async () => {
381
- const form = new FormData()
382
- form.set('data', JSON.stringify({ first: blob1, second: blob2 }))
383
- form.set('meta', JSON.stringify([]))
384
- form.set('maps', JSON.stringify([['first'], ['second']]))
385
- form.set('0', blob1)
386
- form.set('1', blob2)
387
-
388
- const response = await handler({
389
- prefix: '/orpc',
390
- request: new Request('http://localhost/orpc/multiple', {
391
- method: 'POST',
392
- body: form,
393
- headers: {
394
- [ORPC_HEADER]: ORPC_HEADER_VALUE,
395
- },
396
- }),
397
- })
398
-
399
- expect(response.status).toBe(200)
400
-
401
- const form_ = await response.formData()
402
- const file0 = form_.get('0') as File
403
- const file1 = form_.get('1') as File
404
-
405
- expect(file0).toBeInstanceOf(File)
406
- expect(file0.name).toBe('blob')
407
- expect(file0.type).toBe('text/plain;charset=utf-8')
408
- expect(await file0.text()).toBe('hello')
409
-
410
- expect(file1).toBeInstanceOf(File)
411
- expect(file1.name).toBe('blob')
412
- expect(file1.type).toBe('image/png')
413
- expect(await file1.text()).toBe('"world"')
414
- })
415
- })
416
-
417
- describe('accept header', () => {
418
- const router = os.router({
419
- ping: os.func(async () => 'pong'),
420
- })
421
- const handler = createFetchHandler({
422
- router,
423
- })
424
-
425
- it('application/json', async () => {
426
- const response = await handler({
427
- prefix: '/orpc',
428
- request: new Request('http://localhost/orpc/ping', {
429
- method: 'POST',
430
- headers: {
431
- Accept: 'application/json',
432
- },
433
- }),
434
- })
435
-
436
- expect(response.headers.get('Content-Type')).toEqual('application/json')
437
-
438
- expect(await response.json()).toEqual('pong')
439
- })
440
-
441
- it('multipart/form-data', async () => {
442
- const response = await handler({
443
- prefix: '/orpc',
444
- request: new Request('http://localhost/orpc/ping', {
445
- method: 'POST',
446
- headers: {
447
- Accept: 'multipart/form-data',
448
- },
449
- }),
450
- })
451
-
452
- expect(response.headers.get('Content-Type')).toContain(
453
- 'multipart/form-data',
454
- )
455
-
456
- const form = await response.formData()
457
- expect(form.get('')).toEqual('pong')
458
- })
459
-
460
- it('application/x-www-form-urlencoded', async () => {
461
- const response = await handler({
462
- prefix: '/orpc',
463
- request: new Request('http://localhost/orpc/ping', {
464
- method: 'POST',
465
- headers: {
466
- Accept: 'application/x-www-form-urlencoded',
467
- },
468
- }),
469
- })
470
-
471
- expect(response.headers.get('Content-Type')).toEqual(
472
- 'application/x-www-form-urlencoded',
473
- )
474
-
475
- const params = new URLSearchParams(await response.text())
476
- expect(params.get('')).toEqual('pong')
477
- })
478
-
479
- it('*/*', async () => {
480
- const response = await handler({
481
- prefix: '/orpc',
482
- request: new Request('http://localhost/orpc/ping', {
483
- method: 'POST',
484
- headers: {
485
- Accept: '*/*',
486
- },
487
- }),
488
- })
489
-
490
- expect(response.headers.get('Content-Type')).toEqual('application/json')
491
- expect(await response.json()).toEqual('pong')
492
- })
493
-
494
- it('invalid', async () => {
495
- const response = await handler({
496
- prefix: '/orpc',
497
- request: new Request('http://localhost/orpc/ping', {
498
- method: 'POST',
499
- headers: {
500
- Accept: 'invalid',
501
- },
502
- }),
503
- })
504
-
505
- expect(response.headers.get('Content-Type')).toEqual('application/json')
506
- expect(await response.json()).toEqual({
507
- code: 'NOT_ACCEPTABLE',
508
- message: 'Unsupported content-type: invalid',
509
- status: 406,
510
- })
511
- })
512
- })
513
-
514
- describe('dynamic params', () => {
515
- const router = os.router({
516
- deep: os
517
- .route({
518
- method: 'POST',
519
- path: '/{id}/{id2}',
520
- })
521
- .input(
522
- z.object({
523
- id: z.number(),
524
- id2: z.string(),
525
- file: oz.file(),
526
- }),
527
- )
528
- .func(input => input),
529
-
530
- find: os
531
- .route({
532
- method: 'GET',
533
- path: '/{id}',
534
- })
535
- .input(
536
- z.object({
537
- id: z.number(),
538
- }),
539
- )
540
- .func(input => input),
541
- })
542
-
543
- const handlers = [
544
- createFetchHandler({
545
- router,
546
- }),
547
- createFetchHandler({
548
- router,
549
- serverless: true,
550
- }),
551
- ]
552
-
553
- it.each(handlers)('should handle dynamic params', async (handler) => {
554
- const response = await handler({
555
- request: new Request('http://localhost/123'),
556
- })
557
-
558
- expect(response.status).toEqual(200)
559
- expect(response.headers.get('Content-Type')).toEqual('application/json')
560
- expect(await response.json()).toEqual({ id: 123 })
561
- })
562
-
563
- it.each(handlers)('should handle deep dynamic params', async (handler) => {
564
- const form = new FormData()
565
- form.append('file', new Blob(['hello']), 'hello.txt')
566
-
567
- const response = await handler({
568
- request: new Request('http://localhost/123/dfdsfds', {
569
- method: 'POST',
570
- body: form,
571
- }),
572
- })
573
-
574
- expect(response.status).toEqual(200)
575
- const rForm = await response.formData()
576
- expect(rForm.get('id')).toEqual('123')
577
- expect(rForm.get('id2')).toEqual('dfdsfds')
578
- })
579
- })
580
-
581
- describe('can control method on POST request', () => {
582
- const router = os.router({
583
- update: os
584
- .route({
585
- method: 'PUT',
586
- path: '/{id}',
587
- })
588
- .input(
589
- z.object({
590
- id: z.number(),
591
- file: oz.file(),
592
- }),
593
- )
594
- .func(input => input),
595
- })
596
-
597
- const handlers = [
598
- createFetchHandler({
599
- router,
600
- }),
601
- createFetchHandler({
602
- router,
603
- serverless: true,
604
- }),
605
- ]
606
-
607
- it.each(handlers)('work', async (handler) => {
608
- const form = new FormData()
609
- form.set('file', new File(['hello'], 'hello.txt'))
610
-
611
- const response = await handler({
612
- request: new Request('http://localhost/123', {
613
- method: 'POST',
614
- body: form,
615
- }),
616
- })
617
-
618
- expect(response.status).toEqual(404)
619
-
620
- const response2 = await handler({
621
- request: new Request('http://localhost/123?method=PUT', {
622
- method: 'POST',
623
- body: form,
624
- }),
625
- })
626
-
627
- expect(response2.status).toEqual(200)
628
- })
629
- })