@orpc/server 0.0.0-unsafe-pr-2-20241118033608

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,165 @@
1
+ import type { SchemaInput, SchemaOutput } from '@orpc/contract'
2
+ import { ORPCError } from '@orpc/shared/error'
3
+ import type { Middleware } from './middleware'
4
+ import type { Procedure } from './procedure'
5
+ import type { Context, Hooks, Meta, Promisable } from './types'
6
+ import { hook, mergeContext } from './utils'
7
+
8
+ export interface CreateProcedureCallerOptions<
9
+ TProcedure extends Procedure<any, any, any, any, any>,
10
+ TValidate extends boolean,
11
+ > {
12
+ procedure: TProcedure
13
+
14
+ /**
15
+ * The context used when calling the procedure.
16
+ */
17
+ context: TProcedure extends Procedure<infer UContext, any, any, any, any>
18
+ ? UContext
19
+ : never
20
+
21
+ /**
22
+ * Helpful hooks to do some logics on specific time.
23
+ */
24
+ hooks?: (
25
+ context: TProcedure extends Procedure<infer UContext, any, any, any, any>
26
+ ? UContext
27
+ : never,
28
+ meta: Meta<unknown>,
29
+ ) => Promisable<void>
30
+
31
+ /**
32
+ * This is helpful for logging and analytics.
33
+ */
34
+ path?: string[]
35
+
36
+ /**
37
+ * This flag helpful when you want bypass some logics not necessary to internal server calls.
38
+ *
39
+ * @default true
40
+ */
41
+ internal?: boolean
42
+
43
+ /**
44
+ * Indicate whether validate input and output.
45
+ *
46
+ * @default true
47
+ */
48
+ validate?: TValidate
49
+ }
50
+
51
+ export type ProcedureCaller<
52
+ TProcedure extends Procedure<any, any, any, any, any>,
53
+ TValidate extends boolean,
54
+ > = TProcedure extends Procedure<
55
+ any,
56
+ any,
57
+ infer UInputSchema,
58
+ infer UOutputSchema,
59
+ infer UHandlerOutput
60
+ >
61
+ ? (
62
+ input: TValidate extends true
63
+ ? SchemaInput<UInputSchema>
64
+ : SchemaOutput<UInputSchema>,
65
+ ) => Promise<
66
+ TValidate extends true
67
+ ? SchemaOutput<UOutputSchema, UHandlerOutput>
68
+ : SchemaInput<UOutputSchema, UHandlerOutput>
69
+ >
70
+ : never
71
+
72
+ export function createProcedureCaller<
73
+ TProcedure extends Procedure<any, any, any, any, any>,
74
+ TValidate extends boolean = true,
75
+ >(
76
+ options: CreateProcedureCallerOptions<TProcedure, TValidate>,
77
+ ): ProcedureCaller<TProcedure, TValidate> {
78
+ const internal = options.internal ?? true
79
+ const path = options.path ?? []
80
+ const procedure = options.procedure
81
+ const validate = options.validate ?? true
82
+
83
+ const caller = async (input: unknown) => {
84
+ const handler = async (
85
+ input: unknown,
86
+ context: Context,
87
+ partialMeta: Omit<Meta<unknown>, keyof Hooks<unknown>>,
88
+ middlewares: Middleware<any, any, any, any>[],
89
+ ): Promise<unknown> => {
90
+ if (middlewares[0]) {
91
+ const [middleware, ...rest] = middlewares
92
+
93
+ return await hook(async (hooks) => {
94
+ const mid = await middleware(input, context, {
95
+ ...partialMeta,
96
+ ...hooks,
97
+ })
98
+ return await handler(
99
+ input,
100
+ mergeContext(context, mid?.context),
101
+ partialMeta,
102
+ rest,
103
+ )
104
+ })
105
+ }
106
+
107
+ return await hook(async (hooks) => {
108
+ const output = await procedure.zz$p.handler(input, context, {
109
+ ...partialMeta,
110
+ ...hooks,
111
+ })
112
+
113
+ const validOutput = await (async () => {
114
+ if (!validate) return output
115
+ const schema = procedure.zz$p.contract.zz$cp.OutputSchema
116
+ if (!schema) return output
117
+ const result = await schema.safeParseAsync(output)
118
+ if (result.error)
119
+ throw new ORPCError({
120
+ message: 'Validation output failed',
121
+ code: 'INTERNAL_SERVER_ERROR',
122
+ cause: result.error,
123
+ })
124
+ return result.data
125
+ })()
126
+
127
+ return validOutput
128
+ })
129
+ }
130
+
131
+ return await hook(async (hooks) => {
132
+ options.hooks?.(options.context, {
133
+ ...hooks,
134
+ path,
135
+ procedure,
136
+ internal,
137
+ })
138
+
139
+ const validInput = (() => {
140
+ if (!validate) return input
141
+ const schema = procedure.zz$p.contract.zz$cp.InputSchema
142
+ if (!schema) return input
143
+
144
+ try {
145
+ return schema.parse(input)
146
+ } catch (e) {
147
+ throw new ORPCError({
148
+ message: 'Validation input failed',
149
+ code: 'BAD_REQUEST',
150
+ cause: e,
151
+ })
152
+ }
153
+ })()
154
+
155
+ return await handler(
156
+ validInput,
157
+ options.context,
158
+ { path, procedure, internal },
159
+ procedure.zz$p.middlewares ?? [],
160
+ )
161
+ })
162
+ }
163
+
164
+ return caller as ProcedureCaller<TProcedure, TValidate>
165
+ }
@@ -0,0 +1,215 @@
1
+ import { DecoratedContractProcedure } from '@orpc/contract'
2
+ import { z } from 'zod'
3
+ import { os, type DecoratedProcedure, type Meta, isProcedure } from '.'
4
+ import { ProcedureImplementer } from './procedure-implementer'
5
+
6
+ const p1 = new DecoratedContractProcedure({
7
+ InputSchema: undefined,
8
+ OutputSchema: undefined,
9
+ method: undefined,
10
+ path: undefined,
11
+ })
12
+ const implementer1 = new ProcedureImplementer<
13
+ { auth: boolean },
14
+ undefined,
15
+ undefined,
16
+ undefined
17
+ >({ contract: p1 })
18
+
19
+ const schema1 = z.object({ id: z.string() })
20
+ const schema2 = z.object({ name: z.string() })
21
+
22
+ const p2 = new DecoratedContractProcedure({
23
+ InputSchema: schema1,
24
+ OutputSchema: schema2,
25
+ method: 'GET',
26
+ path: '/test',
27
+ })
28
+
29
+ const implementer2 = new ProcedureImplementer<
30
+ { auth: boolean },
31
+ undefined,
32
+ typeof schema1,
33
+ typeof schema2
34
+ >({ contract: p2 })
35
+
36
+ describe('use middleware', () => {
37
+ it('infer types', () => {
38
+ const i = implementer1
39
+ .use((input, context, meta) => {
40
+ expectTypeOf(input).toEqualTypeOf<unknown>()
41
+ expectTypeOf(context).toEqualTypeOf<{ auth: boolean }>()
42
+ expectTypeOf(meta).toEqualTypeOf<Meta<unknown>>()
43
+
44
+ return {
45
+ context: {
46
+ userId: '1',
47
+ },
48
+ }
49
+ })
50
+ .use((input, context, meta) => {
51
+ expectTypeOf(input).toEqualTypeOf<unknown>()
52
+ expectTypeOf(context).toEqualTypeOf<
53
+ { userId: string } & { auth: boolean }
54
+ >()
55
+ expectTypeOf(meta).toEqualTypeOf<Meta<unknown>>()
56
+ })
57
+
58
+ expectTypeOf(i).toEqualTypeOf<
59
+ ProcedureImplementer<
60
+ { auth: boolean },
61
+ { userId: string },
62
+ undefined,
63
+ undefined
64
+ >
65
+ >()
66
+ })
67
+
68
+ it('map middleware input', () => {
69
+ // @ts-expect-error mismatch input
70
+ implementer2.use((input: { postId: string }) => {
71
+ return { context: { a: 'a' } }
72
+ })
73
+
74
+ implementer2.use(
75
+ (input: { postId: string }) => {
76
+ return { context: { a: 'a' } }
77
+ },
78
+ // @ts-expect-error mismatch input
79
+ (input) => ({ postId: 12455 }),
80
+ )
81
+
82
+ implementer2.use(
83
+ (input: { postId: string }) => {},
84
+ (input) => ({ postId: '12455' }),
85
+ )
86
+
87
+ const i = implementer2.use(
88
+ (input: { id: number }) => {
89
+ return {
90
+ context: {
91
+ userIdd: '1',
92
+ },
93
+ }
94
+ },
95
+ (input) => ({ id: Number.parseInt(input.id) }),
96
+ )
97
+
98
+ expectTypeOf(i).toEqualTypeOf<
99
+ ProcedureImplementer<
100
+ { auth: boolean },
101
+ { userIdd: string },
102
+ typeof schema1,
103
+ typeof schema2
104
+ >
105
+ >()
106
+ })
107
+ })
108
+
109
+ describe('output schema', () => {
110
+ it('auto infer output schema if output schema is not specified', async () => {
111
+ const sr = os.handler(() => ({ a: 1 }))
112
+
113
+ const result = await sr.zz$p.handler({}, undefined, {
114
+ method: 'GET',
115
+ path: '/',
116
+ } as any)
117
+
118
+ expectTypeOf(result).toEqualTypeOf<{ a: number }>()
119
+ })
120
+
121
+ it('not infer output schema if output schema is specified', async () => {
122
+ const srb1 = new ProcedureImplementer({
123
+ contract: new DecoratedContractProcedure({
124
+ OutputSchema: z.unknown(),
125
+ InputSchema: undefined,
126
+ }),
127
+ })
128
+
129
+ const sr = srb1.handler(() => ({ b: 1 }))
130
+
131
+ const result = await sr.zz$p.handler({}, {}, {
132
+ method: 'GET',
133
+ path: '/',
134
+ } as any)
135
+
136
+ expectTypeOf(result).toEqualTypeOf<unknown>()
137
+ })
138
+ })
139
+
140
+ describe('handler', () => {
141
+ it('infer types', () => {
142
+ const handler = implementer1.handler((input, context, meta) => {
143
+ expectTypeOf(input).toEqualTypeOf<unknown>()
144
+ expectTypeOf(context).toEqualTypeOf<{ auth: boolean }>()
145
+ expectTypeOf(meta).toEqualTypeOf<Meta<unknown>>()
146
+
147
+ return {
148
+ name: 'unnoq',
149
+ }
150
+ })
151
+
152
+ expectTypeOf(handler).toEqualTypeOf<
153
+ DecoratedProcedure<
154
+ { auth: boolean },
155
+ undefined,
156
+ undefined,
157
+ undefined,
158
+ { name: string }
159
+ >
160
+ >()
161
+ expect(isProcedure(handler)).toBe(true)
162
+
163
+ implementer2.handler((input, context, meta) => {
164
+ expectTypeOf(input).toEqualTypeOf<{ id: string }>()
165
+ expectTypeOf(context).toEqualTypeOf<{ auth: boolean }>()
166
+ expectTypeOf(meta).toEqualTypeOf<Meta<unknown>>()
167
+
168
+ return {
169
+ name: 'unnoq',
170
+ }
171
+ })
172
+
173
+ // @ts-expect-error mismatch output
174
+ implementer2.handler(() => {})
175
+ })
176
+
177
+ it('combine middlewares', () => {
178
+ const mid1 = () => {
179
+ return {
180
+ context: {
181
+ userId: '1',
182
+ },
183
+ }
184
+ }
185
+
186
+ const mid2 = () => {}
187
+
188
+ const handler = implementer2
189
+ .use(mid1)
190
+ .use(mid2)
191
+ .handler((input, context, meta) => {
192
+ expectTypeOf(input).toEqualTypeOf<{ id: string }>()
193
+ expectTypeOf(context).toEqualTypeOf<
194
+ { auth: boolean } & { userId: string }
195
+ >()
196
+ expectTypeOf(meta).toEqualTypeOf<Meta<unknown>>()
197
+
198
+ return {
199
+ name: 'unnoq',
200
+ }
201
+ })
202
+
203
+ expectTypeOf(handler).toEqualTypeOf<
204
+ DecoratedProcedure<
205
+ { auth: boolean },
206
+ { userId: string },
207
+ typeof schema1,
208
+ typeof schema2,
209
+ { name: string }
210
+ >
211
+ >()
212
+
213
+ expect(handler.zz$p.middlewares).toEqual([mid1, mid2])
214
+ })
215
+ })
@@ -0,0 +1,103 @@
1
+ import type { ContractProcedure, SchemaOutput } from '@orpc/contract'
2
+ import type { Schema } from '@orpc/contract'
3
+ import {
4
+ type MapInputMiddleware,
5
+ type Middleware,
6
+ decorateMiddleware,
7
+ } from './middleware'
8
+ import {
9
+ type DecoratedProcedure,
10
+ type ProcedureHandler,
11
+ decorateProcedure,
12
+ } from './procedure'
13
+ import type { Context, MergeContext } from './types'
14
+
15
+ export class ProcedureImplementer<
16
+ TContext extends Context,
17
+ TExtraContext extends Context,
18
+ TInputSchema extends Schema,
19
+ TOutputSchema extends Schema,
20
+ > {
21
+ constructor(
22
+ public zz$pi: {
23
+ contract: ContractProcedure<TInputSchema, TOutputSchema>
24
+ middlewares?: Middleware<any, any, any, any>[]
25
+ },
26
+ ) {}
27
+
28
+ use<
29
+ UExtraContext extends
30
+ | Partial<MergeContext<Context, MergeContext<TContext, TExtraContext>>>
31
+ | undefined = undefined,
32
+ >(
33
+ middleware: Middleware<
34
+ MergeContext<TContext, TExtraContext>,
35
+ UExtraContext,
36
+ SchemaOutput<TInputSchema>,
37
+ SchemaOutput<TOutputSchema>
38
+ >,
39
+ ): ProcedureImplementer<
40
+ TContext,
41
+ MergeContext<TExtraContext, UExtraContext>,
42
+ TInputSchema,
43
+ TOutputSchema
44
+ >
45
+
46
+ use<
47
+ UExtraContext extends
48
+ | Partial<MergeContext<Context, MergeContext<TContext, TExtraContext>>>
49
+ | undefined = undefined,
50
+ UMappedInput = unknown,
51
+ >(
52
+ middleware: Middleware<
53
+ MergeContext<TContext, TExtraContext>,
54
+ UExtraContext,
55
+ UMappedInput,
56
+ SchemaOutput<TOutputSchema>
57
+ >,
58
+ mapInput: MapInputMiddleware<SchemaOutput<TInputSchema>, UMappedInput>,
59
+ ): ProcedureImplementer<
60
+ TContext,
61
+ MergeContext<TExtraContext, UExtraContext>,
62
+ TInputSchema,
63
+ TOutputSchema
64
+ >
65
+
66
+ use(
67
+ middleware: Middleware<any, any, any, any>,
68
+ mapInput?: MapInputMiddleware<any, any>,
69
+ ): ProcedureImplementer<any, any, any, any> {
70
+ const middleware_ = mapInput
71
+ ? decorateMiddleware(middleware).mapInput(mapInput)
72
+ : middleware
73
+
74
+ return new ProcedureImplementer({
75
+ ...this.zz$pi,
76
+ middlewares: [...(this.zz$pi.middlewares ?? []), middleware_],
77
+ })
78
+ }
79
+
80
+ handler<UHandlerOutput extends SchemaOutput<TOutputSchema>>(
81
+ handler: ProcedureHandler<
82
+ TContext,
83
+ TExtraContext,
84
+ TInputSchema,
85
+ TOutputSchema,
86
+ UHandlerOutput
87
+ >,
88
+ ): DecoratedProcedure<
89
+ TContext,
90
+ TExtraContext,
91
+ TInputSchema,
92
+ TOutputSchema,
93
+ UHandlerOutput
94
+ > {
95
+ return decorateProcedure({
96
+ zz$p: {
97
+ middlewares: this.zz$pi.middlewares,
98
+ contract: this.zz$pi.contract,
99
+ handler,
100
+ },
101
+ })
102
+ }
103
+ }