@orpc/server 0.0.0

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 (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
+ })