@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,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: 'dinwwwh',
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: 'dinwwwh',
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: 'dinwwwh',
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
+ }