@orpc/server 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. package/dist/chunk-ACLC6USM.js +262 -0
  2. package/dist/chunk-ACLC6USM.js.map +1 -0
  3. package/dist/fetch.js +648 -0
  4. package/dist/fetch.js.map +1 -0
  5. package/dist/index.js +403 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/src/adapters/fetch.d.ts +36 -0
  8. package/dist/src/adapters/fetch.d.ts.map +1 -0
  9. package/dist/src/builder.d.ts +49 -0
  10. package/dist/src/builder.d.ts.map +1 -0
  11. package/dist/src/index.d.ts +15 -0
  12. package/dist/src/index.d.ts.map +1 -0
  13. package/dist/src/middleware.d.ts +16 -0
  14. package/dist/src/middleware.d.ts.map +1 -0
  15. package/dist/src/procedure-builder.d.ts +31 -0
  16. package/dist/src/procedure-builder.d.ts.map +1 -0
  17. package/dist/src/procedure-caller.d.ts +33 -0
  18. package/dist/src/procedure-caller.d.ts.map +1 -0
  19. package/dist/src/procedure-implementer.d.ts +19 -0
  20. package/dist/src/procedure-implementer.d.ts.map +1 -0
  21. package/dist/src/procedure.d.ts +29 -0
  22. package/dist/src/procedure.d.ts.map +1 -0
  23. package/dist/src/router-builder.d.ts +22 -0
  24. package/dist/src/router-builder.d.ts.map +1 -0
  25. package/dist/src/router-caller.d.ts +36 -0
  26. package/dist/src/router-caller.d.ts.map +1 -0
  27. package/dist/src/router-implementer.d.ts +20 -0
  28. package/dist/src/router-implementer.d.ts.map +1 -0
  29. package/dist/src/router.d.ts +14 -0
  30. package/dist/src/router.d.ts.map +1 -0
  31. package/dist/src/types.d.ts +18 -0
  32. package/dist/src/types.d.ts.map +1 -0
  33. package/dist/src/utils.d.ts +4 -0
  34. package/dist/src/utils.d.ts.map +1 -0
  35. package/dist/tsconfig.tsbuildinfo +1 -0
  36. package/package.json +57 -0
  37. package/src/adapters/fetch.test.ts +399 -0
  38. package/src/adapters/fetch.ts +228 -0
  39. package/src/builder.test.ts +362 -0
  40. package/src/builder.ts +236 -0
  41. package/src/index.ts +16 -0
  42. package/src/middleware.test.ts +279 -0
  43. package/src/middleware.ts +119 -0
  44. package/src/procedure-builder.test.ts +219 -0
  45. package/src/procedure-builder.ts +158 -0
  46. package/src/procedure-caller.test.ts +210 -0
  47. package/src/procedure-caller.ts +165 -0
  48. package/src/procedure-implementer.test.ts +215 -0
  49. package/src/procedure-implementer.ts +103 -0
  50. package/src/procedure.test.ts +312 -0
  51. package/src/procedure.ts +251 -0
  52. package/src/router-builder.test.ts +106 -0
  53. package/src/router-builder.ts +120 -0
  54. package/src/router-caller.test.ts +173 -0
  55. package/src/router-caller.ts +95 -0
  56. package/src/router-implementer.test.ts +116 -0
  57. package/src/router-implementer.ts +110 -0
  58. package/src/router.test.ts +142 -0
  59. package/src/router.ts +69 -0
  60. package/src/types.test.ts +18 -0
  61. package/src/types.ts +28 -0
  62. package/src/utils.test.ts +243 -0
  63. package/src/utils.ts +95 -0
@@ -0,0 +1,219 @@
1
+ import { ContractProcedure } from '@orpc/contract'
2
+ import { z } from 'zod'
3
+ import { type DecoratedProcedure, isProcedure } from './procedure'
4
+ import { ProcedureBuilder } from './procedure-builder'
5
+ import type { ProcedureImplementer } from './procedure-implementer'
6
+ import type { Meta } from './types'
7
+
8
+ const schema1 = z.object({ id: z.string() })
9
+ const example1 = { id: '1' }
10
+ const schema2 = z.object({ name: z.string() })
11
+ const example2 = { name: 'dinwwwh' }
12
+
13
+ const builder = new ProcedureBuilder<
14
+ { auth: boolean },
15
+ undefined,
16
+ undefined,
17
+ undefined
18
+ >({
19
+ contract: new ContractProcedure({
20
+ InputSchema: undefined,
21
+ OutputSchema: undefined,
22
+ }),
23
+ })
24
+
25
+ it('input', () => {
26
+ const builder2 = builder.input(schema1, example1)
27
+
28
+ expectTypeOf(builder2).toEqualTypeOf<
29
+ ProcedureBuilder<{ auth: boolean }, undefined, typeof schema1, undefined>
30
+ >()
31
+
32
+ expect(builder2.zz$pb).toMatchObject({
33
+ contract: {
34
+ zz$cp: {
35
+ InputSchema: schema1,
36
+ inputExample: example1,
37
+ },
38
+ },
39
+ })
40
+ })
41
+
42
+ it('output', () => {
43
+ const builder2 = builder.output(schema2, example2)
44
+
45
+ expectTypeOf(builder2).toEqualTypeOf<
46
+ ProcedureBuilder<{ auth: boolean }, undefined, undefined, typeof schema2>
47
+ >()
48
+
49
+ expect(builder2.zz$pb).toMatchObject({
50
+ contract: {
51
+ zz$cp: {
52
+ OutputSchema: schema2,
53
+ outputExample: example2,
54
+ },
55
+ },
56
+ })
57
+ })
58
+
59
+ it('route', () => {
60
+ const builder2 = builder.route({
61
+ method: 'GET',
62
+ path: '/test',
63
+ deprecated: true,
64
+ description: 'des',
65
+ summary: 'sum',
66
+ tags: ['hi'],
67
+ })
68
+
69
+ expectTypeOf(builder2).toEqualTypeOf<
70
+ ProcedureBuilder<{ auth: boolean }, undefined, undefined, undefined>
71
+ >()
72
+
73
+ expect(builder2.zz$pb).toMatchObject({
74
+ contract: {
75
+ zz$cp: {
76
+ method: 'GET',
77
+ path: '/test',
78
+ deprecated: true,
79
+ description: 'des',
80
+ summary: 'sum',
81
+ tags: ['hi'],
82
+ },
83
+ },
84
+ })
85
+ })
86
+
87
+ describe('use middleware', () => {
88
+ it('infer types', () => {
89
+ const implementer = builder
90
+ .use((input, context, meta) => {
91
+ expectTypeOf(input).toEqualTypeOf<unknown>()
92
+ expectTypeOf(context).toEqualTypeOf<{ auth: boolean }>()
93
+ expectTypeOf(meta).toEqualTypeOf<Meta<unknown>>()
94
+
95
+ return {
96
+ context: {
97
+ userId: '1',
98
+ },
99
+ }
100
+ })
101
+ .use((input, context, meta) => {
102
+ expectTypeOf(input).toEqualTypeOf<unknown>()
103
+ expectTypeOf(context).toEqualTypeOf<
104
+ { userId: string } & { auth: boolean }
105
+ >()
106
+ expectTypeOf(meta).toEqualTypeOf<Meta<unknown>>()
107
+ })
108
+
109
+ expectTypeOf(implementer).toEqualTypeOf<
110
+ ProcedureImplementer<
111
+ { auth: boolean },
112
+ { userId: string },
113
+ undefined,
114
+ undefined
115
+ >
116
+ >()
117
+ })
118
+
119
+ it('map middleware input', () => {
120
+ // @ts-expect-error mismatch input
121
+ builder.use((input: { postId: string }) => {
122
+ return { context: { a: 'a' } }
123
+ })
124
+
125
+ builder.use(
126
+ (input: { postId: string }) => {
127
+ return { context: { a: 'a' } }
128
+ },
129
+ // @ts-expect-error mismatch input
130
+ (input) => ({ postId: 12455 }),
131
+ )
132
+
133
+ builder.use(
134
+ (input: { postId: string }) => {},
135
+ (input) => ({ postId: '12455' }),
136
+ )
137
+
138
+ const implementer = builder.input(schema1).use(
139
+ (input: { id: number }) => {
140
+ return {
141
+ context: {
142
+ userId555: '1',
143
+ },
144
+ }
145
+ },
146
+ (input) => ({ id: Number.parseInt(input.id) }),
147
+ )
148
+
149
+ expectTypeOf(implementer).toEqualTypeOf<
150
+ ProcedureImplementer<
151
+ { auth: boolean },
152
+ { userId555: string },
153
+ typeof schema1,
154
+ undefined
155
+ >
156
+ >()
157
+ })
158
+ })
159
+
160
+ describe('handler', () => {
161
+ it('infer types', () => {
162
+ const handler = builder.handler((input, context, meta) => {
163
+ expectTypeOf(input).toEqualTypeOf<unknown>()
164
+ expectTypeOf(context).toEqualTypeOf<{ auth: boolean }>()
165
+ expectTypeOf(meta).toEqualTypeOf<Meta<unknown>>()
166
+ })
167
+
168
+ expectTypeOf(handler).toEqualTypeOf<
169
+ DecoratedProcedure<
170
+ { auth: boolean },
171
+ undefined,
172
+ undefined,
173
+ undefined,
174
+ void
175
+ >
176
+ >()
177
+
178
+ expect(isProcedure(handler)).toBe(true)
179
+ })
180
+
181
+ it('combine middlewares', () => {
182
+ const mid1 = () => {
183
+ return {
184
+ context: {
185
+ userId: '1',
186
+ },
187
+ }
188
+ }
189
+
190
+ const mid2 = () => {}
191
+
192
+ const handler = builder
193
+ .use(mid1)
194
+ .use(mid2)
195
+ .handler((input, context, meta) => {
196
+ expectTypeOf(input).toEqualTypeOf<unknown>()
197
+ expectTypeOf(context).toEqualTypeOf<
198
+ { userId: string } & { auth: boolean }
199
+ >()
200
+ expectTypeOf(meta).toEqualTypeOf<Meta<unknown>>()
201
+
202
+ return {
203
+ name: 'dinwwwh',
204
+ }
205
+ })
206
+
207
+ expectTypeOf(handler).toEqualTypeOf<
208
+ DecoratedProcedure<
209
+ { auth: boolean },
210
+ { userId: string },
211
+ undefined,
212
+ undefined,
213
+ { name: string }
214
+ >
215
+ >()
216
+
217
+ expect(handler.zz$p.middlewares).toEqual([mid1, mid2])
218
+ })
219
+ })
@@ -0,0 +1,158 @@
1
+ import {
2
+ type ContractProcedure,
3
+ DecoratedContractProcedure,
4
+ type RouteOptions,
5
+ type Schema,
6
+ type SchemaInput,
7
+ type SchemaOutput,
8
+ } from '@orpc/contract'
9
+ import type { MapInputMiddleware, Middleware } from './middleware'
10
+ import {
11
+ type DecoratedProcedure,
12
+ type ProcedureHandler,
13
+ decorateProcedure,
14
+ } from './procedure'
15
+ import { ProcedureImplementer } from './procedure-implementer'
16
+ import type { Context, MergeContext } from './types'
17
+
18
+ export class ProcedureBuilder<
19
+ TContext extends Context,
20
+ TExtraContext extends Context,
21
+ TInputSchema extends Schema,
22
+ TOutputSchema extends Schema,
23
+ > {
24
+ constructor(
25
+ public zz$pb: {
26
+ contract: ContractProcedure<TInputSchema, TOutputSchema>
27
+ middlewares?: Middleware<any, any, any, any>[]
28
+ },
29
+ ) {}
30
+
31
+ /**
32
+ * Self chainable
33
+ */
34
+
35
+ route(
36
+ opts: RouteOptions,
37
+ ): ProcedureBuilder<TContext, TExtraContext, TInputSchema, TOutputSchema> {
38
+ return new ProcedureBuilder({
39
+ ...this.zz$pb,
40
+ contract: DecoratedContractProcedure.decorate(this.zz$pb.contract).route(
41
+ opts,
42
+ ),
43
+ })
44
+ }
45
+
46
+ input<USchema extends Schema = undefined>(
47
+ schema: USchema,
48
+ example?: SchemaInput<USchema>,
49
+ ): ProcedureBuilder<TContext, TExtraContext, USchema, TOutputSchema> {
50
+ return new ProcedureBuilder({
51
+ ...this.zz$pb,
52
+ contract: DecoratedContractProcedure.decorate(this.zz$pb.contract).input(
53
+ schema,
54
+ example,
55
+ ),
56
+ })
57
+ }
58
+
59
+ output<USchema extends Schema = undefined>(
60
+ schema: USchema,
61
+ example?: SchemaOutput<USchema>,
62
+ ): ProcedureBuilder<TContext, TExtraContext, TInputSchema, USchema> {
63
+ return new ProcedureBuilder({
64
+ ...this.zz$pb,
65
+ contract: DecoratedContractProcedure.decorate(this.zz$pb.contract).output(
66
+ schema,
67
+ example,
68
+ ),
69
+ })
70
+ }
71
+
72
+ /**
73
+ * Convert to ProcedureBuilder
74
+ */
75
+
76
+ use<
77
+ UExtraContext extends
78
+ | Partial<MergeContext<Context, MergeContext<TContext, TExtraContext>>>
79
+ | undefined = undefined,
80
+ >(
81
+ middleware: Middleware<
82
+ MergeContext<TContext, TExtraContext>,
83
+ UExtraContext,
84
+ SchemaOutput<TInputSchema>,
85
+ SchemaOutput<TOutputSchema>
86
+ >,
87
+ ): ProcedureImplementer<
88
+ TContext,
89
+ MergeContext<TExtraContext, UExtraContext>,
90
+ TInputSchema,
91
+ TOutputSchema
92
+ >
93
+
94
+ use<
95
+ UExtraContext extends
96
+ | Partial<MergeContext<Context, MergeContext<TContext, TExtraContext>>>
97
+ | undefined = undefined,
98
+ UMappedInput = unknown,
99
+ >(
100
+ middleware: Middleware<
101
+ MergeContext<TContext, TExtraContext>,
102
+ UExtraContext,
103
+ UMappedInput,
104
+ SchemaOutput<TOutputSchema>
105
+ >,
106
+ mapInput: MapInputMiddleware<SchemaOutput<TInputSchema>, UMappedInput>,
107
+ ): ProcedureImplementer<
108
+ TContext,
109
+ MergeContext<TExtraContext, UExtraContext>,
110
+ TInputSchema,
111
+ TOutputSchema
112
+ >
113
+
114
+ use(
115
+ middleware: Middleware<any, any, any, any>,
116
+ mapInput?: MapInputMiddleware<any, any>,
117
+ ): ProcedureImplementer<any, any, any, any> {
118
+ if (!mapInput) {
119
+ return new ProcedureImplementer({
120
+ contract: this.zz$pb.contract,
121
+ middlewares: this.zz$pb.middlewares,
122
+ }).use(middleware)
123
+ }
124
+
125
+ return new ProcedureImplementer({
126
+ contract: this.zz$pb.contract,
127
+ middlewares: this.zz$pb.middlewares,
128
+ }).use(middleware, mapInput)
129
+ }
130
+
131
+ /**
132
+ * Convert to Procedure
133
+ */
134
+
135
+ handler<UHandlerOutput extends SchemaOutput<TOutputSchema>>(
136
+ handler: ProcedureHandler<
137
+ TContext,
138
+ TExtraContext,
139
+ TInputSchema,
140
+ TOutputSchema,
141
+ UHandlerOutput
142
+ >,
143
+ ): DecoratedProcedure<
144
+ TContext,
145
+ TExtraContext,
146
+ TInputSchema,
147
+ TOutputSchema,
148
+ UHandlerOutput
149
+ > {
150
+ return decorateProcedure({
151
+ zz$p: {
152
+ middlewares: this.zz$pb.middlewares,
153
+ contract: this.zz$pb.contract,
154
+ handler,
155
+ },
156
+ })
157
+ }
158
+ }
@@ -0,0 +1,210 @@
1
+ import { z } from 'zod'
2
+ import { os, createProcedureCaller } from '.'
3
+
4
+ describe('createProcedureCaller', () => {
5
+ let internal = false
6
+ let path = ['ping']
7
+ let context = { auth: true }
8
+
9
+ const osw = os.context<{ auth?: boolean }>()
10
+ const procedure = osw
11
+ .input(z.object({ value: z.string().transform((v) => Number(v)) }))
12
+ .output(z.object({ value: z.number().transform((v) => v.toString()) }))
13
+ .handler((input, context, meta) => {
14
+ expect(context).toEqual(context)
15
+ expect(meta.internal).toBe(internal)
16
+ expect(meta.path).toBe(path)
17
+
18
+ return input
19
+ })
20
+
21
+ it('infer context', () => {
22
+ createProcedureCaller({
23
+ procedure,
24
+ // @ts-expect-error invalid context
25
+ context: { auth: 123 },
26
+ })
27
+
28
+ createProcedureCaller({
29
+ procedure,
30
+ context,
31
+ })
32
+ })
33
+
34
+ it('with validate', async () => {
35
+ const caller = createProcedureCaller({
36
+ procedure,
37
+ context,
38
+ internal,
39
+ path,
40
+ })
41
+
42
+ expectTypeOf(caller).toMatchTypeOf<
43
+ (input: { value: string }) => Promise<{
44
+ value: string
45
+ }>
46
+ >()
47
+
48
+ expect(await caller({ value: '123' })).toEqual({ value: '123' })
49
+
50
+ // @ts-expect-error
51
+ expect(caller({ value: {} })).rejects.toThrowError(
52
+ 'Validation input failed',
53
+ )
54
+ })
55
+
56
+ it('without validate', async () => {
57
+ internal = true
58
+ path = []
59
+ context = { auth: false }
60
+
61
+ const caller = createProcedureCaller({
62
+ procedure,
63
+ context,
64
+ internal,
65
+ path,
66
+ validate: false,
67
+ })
68
+
69
+ expectTypeOf(caller).toMatchTypeOf<
70
+ (input: { value: number }) => Promise<{
71
+ value: number
72
+ }>
73
+ >()
74
+
75
+ expect(await caller({ value: 123 })).toEqual({ value: 123 })
76
+
77
+ // @ts-expect-error it's not validate so bellow still works
78
+ expect(await caller({ value: '123' })).toEqual({ value: '123' })
79
+ })
80
+
81
+ it('without validate and schema', () => {
82
+ const procedure = osw.handler(() => {
83
+ return { value: true }
84
+ })
85
+
86
+ const caller = createProcedureCaller({
87
+ procedure,
88
+ context,
89
+ internal,
90
+ validate: false,
91
+ })
92
+
93
+ expectTypeOf(caller).toMatchTypeOf<
94
+ (value: unknown) => Promise<{ value: boolean }>
95
+ >()
96
+
97
+ expect(caller({ value: 123 })).resolves.toEqual({ value: true })
98
+ })
99
+
100
+ it('middlewares', () => {
101
+ const ref = { value: 0 }
102
+
103
+ const mid1 = vi.fn(
104
+ osw.middleware((input: { id: string }, context, meta) => {
105
+ expect(input).toEqual({ id: '1' })
106
+
107
+ expect(ref.value).toBe(0)
108
+ ref.value++
109
+
110
+ meta.onSuccess(() => {
111
+ expect(ref.value).toBe(7)
112
+ ref.value++
113
+ })
114
+
115
+ meta.onFinish(() => {
116
+ expect(ref.value).toBe(8)
117
+ ref.value++
118
+ })
119
+
120
+ return {
121
+ context: {
122
+ userId: '1',
123
+ },
124
+ }
125
+ }),
126
+ )
127
+
128
+ const mid2 = vi.fn(
129
+ osw.middleware((input, context, meta) => {
130
+ expect(ref.value).toBe(1)
131
+ ref.value++
132
+
133
+ meta.onSuccess(() => {
134
+ expect(ref.value).toBe(5)
135
+ ref.value++
136
+ })
137
+
138
+ meta.onFinish(() => {
139
+ expect(ref.value).toBe(6)
140
+ ref.value++
141
+ })
142
+ }),
143
+ )
144
+
145
+ const ping = osw
146
+ .input(z.object({ id: z.string() }))
147
+ .use(mid1)
148
+ .use(mid2)
149
+ .handler((input, context, meta) => {
150
+ expect(context).toEqual({ userId: '1', auth: false })
151
+
152
+ expect(ref.value).toBe(2)
153
+ ref.value++
154
+
155
+ meta.onSuccess(() => {
156
+ expect(ref.value).toBe(3)
157
+ ref.value++
158
+ })
159
+
160
+ meta.onFinish(() => {
161
+ expect(ref.value).toBe(4)
162
+ ref.value++
163
+ })
164
+
165
+ return 'pong'
166
+ })
167
+
168
+ const caller = createProcedureCaller({
169
+ procedure: ping,
170
+ context: { auth: false },
171
+ })
172
+
173
+ expect(caller({ id: '1' })).resolves.toEqual('pong')
174
+ })
175
+
176
+ it('hooks', async () => {
177
+ const ref = { value: 0 }
178
+
179
+ const caller = createProcedureCaller({
180
+ procedure: procedure,
181
+ context: context,
182
+ internal: internal,
183
+ path: path,
184
+ hooks: (context, meta) => {
185
+ expect(context).toEqual(context)
186
+ expect(meta.internal).toBe(internal)
187
+ expect(meta.path).toBe(path)
188
+ expect(meta.procedure).toBe(procedure)
189
+
190
+ expect(ref.value).toBe(0)
191
+ ref.value++
192
+
193
+ meta.onSuccess(() => {
194
+ expect(ref.value).toBe(1)
195
+ ref.value++
196
+ })
197
+
198
+ meta.onFinish(() => {
199
+ expect(ref.value).toBe(2)
200
+ ref.value++
201
+
202
+ throw new Error('foo')
203
+ })
204
+ },
205
+ })
206
+
207
+ await expect(caller({ value: '1243' })).rejects.toThrow('foo')
208
+ expect(ref.value).toBe(3)
209
+ })
210
+ })