@orpc/server 0.2.1 → 0.3.1

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 (48) hide show
  1. package/dist/{chunk-26DTFWOI.js → chunk-YYGRQD4L.js} +26 -36
  2. package/dist/chunk-YYGRQD4L.js.map +1 -0
  3. package/dist/fetch.js +12 -12
  4. package/dist/fetch.js.map +1 -1
  5. package/dist/index.js +9 -15
  6. package/dist/index.js.map +1 -1
  7. package/dist/src/adapters/fetch.d.ts +2 -2
  8. package/dist/src/adapters/fetch.d.ts.map +1 -1
  9. package/dist/src/builder.d.ts +2 -2
  10. package/dist/src/builder.d.ts.map +1 -1
  11. package/dist/src/procedure-builder.d.ts +2 -2
  12. package/dist/src/procedure-builder.d.ts.map +1 -1
  13. package/dist/src/procedure-caller.d.ts +7 -16
  14. package/dist/src/procedure-caller.d.ts.map +1 -1
  15. package/dist/src/procedure-implementer.d.ts +2 -2
  16. package/dist/src/procedure-implementer.d.ts.map +1 -1
  17. package/dist/src/procedure.d.ts +11 -10
  18. package/dist/src/procedure.d.ts.map +1 -1
  19. package/dist/src/router-caller.d.ts +8 -17
  20. package/dist/src/router-caller.d.ts.map +1 -1
  21. package/dist/src/router.d.ts +2 -2
  22. package/dist/src/router.d.ts.map +1 -1
  23. package/dist/src/types.d.ts +0 -1
  24. package/dist/src/types.d.ts.map +1 -1
  25. package/dist/tsconfig.tsbuildinfo +1 -1
  26. package/package.json +8 -6
  27. package/src/adapters/fetch.test.ts +18 -18
  28. package/src/adapters/fetch.ts +9 -5
  29. package/src/builder.test.ts +4 -4
  30. package/src/builder.ts +6 -6
  31. package/src/middleware.test.ts +1 -1
  32. package/src/procedure-builder.test.ts +2 -2
  33. package/src/procedure-builder.ts +6 -6
  34. package/src/procedure-caller.test.ts +27 -36
  35. package/src/procedure-caller.ts +36 -44
  36. package/src/procedure-implementer.test.ts +8 -8
  37. package/src/procedure-implementer.ts +6 -6
  38. package/src/procedure.test.ts +20 -20
  39. package/src/procedure.ts +30 -45
  40. package/src/router-builder.test.ts +4 -4
  41. package/src/router-caller.test.ts +5 -52
  42. package/src/router-caller.ts +11 -31
  43. package/src/router-implementer.test.ts +6 -6
  44. package/src/router.test-d.ts +2 -2
  45. package/src/router.test.ts +10 -10
  46. package/src/router.ts +4 -4
  47. package/src/types.ts +0 -1
  48. package/dist/chunk-26DTFWOI.js.map +0 -1
@@ -3,6 +3,7 @@
3
3
  import type {
4
4
  PartialOnUndefinedDeep,
5
5
  Promisable,
6
+ Value,
6
7
  } from '@orpc/shared'
7
8
  import type { Router } from '../router'
8
9
  import {
@@ -16,6 +17,7 @@ import {
16
17
  isPlainObject,
17
18
  mapValues,
18
19
  trim,
20
+ value,
19
21
  } from '@orpc/shared'
20
22
  import { ORPCError } from '@orpc/shared/error'
21
23
  import {
@@ -91,6 +93,8 @@ export function createFetchHandler<TRouter extends Router<any>>(
91
93
  ? new ORPCSerializer()
92
94
  : new OpenAPISerializer({ accept })
93
95
 
96
+ const context = await value(requestOptions.context)
97
+
94
98
  const handler = async () => {
95
99
  const url = new URL(requestOptions.request.url)
96
100
  const pathname = `/${trim(url.pathname.replace(requestOptions.prefix ?? '', ''), '/')}`
@@ -196,9 +200,7 @@ export function createFetchHandler<TRouter extends Router<any>>(
196
200
  })()
197
201
 
198
202
  const caller = createProcedureCaller({
199
- context: requestOptions.context,
200
- internal: false,
201
- validate: true,
203
+ context,
202
204
  procedure,
203
205
  path,
204
206
  })
@@ -214,7 +216,7 @@ export function createFetchHandler<TRouter extends Router<any>>(
214
216
  }
215
217
 
216
218
  try {
217
- return await options.hooks?.(requestOptions.context as any, {
219
+ return await options.hooks?.(context as any, {
218
220
  next: handler,
219
221
  response: response => response,
220
222
  }) ?? await handler()
@@ -268,7 +270,9 @@ export type FetchHandlerOptions<TRouter extends Router<any>> = {
268
270
  /**
269
271
  * The context used to handle the request.
270
272
  */
271
- context: TRouter extends Router<infer UContext> ? UContext : never
273
+ context: Value<
274
+ TRouter extends Router<infer UContext> ? UContext : never
275
+ >
272
276
  }>
273
277
 
274
278
  export interface FetchHandler<TRouter extends Router<any>> {
@@ -74,7 +74,7 @@ describe('use middleware', () => {
74
74
  return { postId: '1' }
75
75
  },
76
76
  )
77
- .handler((_, context) => {
77
+ .func((_, context) => {
78
78
  expectTypeOf(context).toMatchTypeOf<{ user: string }>()
79
79
  })
80
80
  })
@@ -145,7 +145,7 @@ it('router method', () => {
145
145
  // Because of the router keyword is special, we can't use instanceof
146
146
  expect(osw.router.zz$pi.contract).toEqual(userFindContract)
147
147
  expect(
148
- osw.router.handler(() => {
148
+ osw.router.func(() => {
149
149
  return { name: '' }
150
150
  }),
151
151
  ).toSatisfy(isProcedure)
@@ -286,7 +286,7 @@ describe('handler method', () => {
286
286
  it('without middlewares', () => {
287
287
  const osw = os.context<{ auth: boolean }>()
288
288
 
289
- const procedure = osw.handler((input, context, meta) => {
289
+ const procedure = osw.func((input, context, meta) => {
290
290
  expectTypeOf(input).toEqualTypeOf<unknown>()
291
291
  expectTypeOf(context).toEqualTypeOf<{ auth: boolean }>()
292
292
  expectTypeOf(meta).toEqualTypeOf<Meta>()
@@ -317,7 +317,7 @@ describe('handler method', () => {
317
317
 
318
318
  const osw = os.context<{ auth: boolean }>().use(mid)
319
319
 
320
- const procedure = osw.handler((input, context, meta) => {
320
+ const procedure = osw.func((input, context, meta) => {
321
321
  expectTypeOf(input).toEqualTypeOf<unknown>()
322
322
  expectTypeOf(context).toMatchTypeOf<{ auth: boolean }>()
323
323
  expectTypeOf(meta).toEqualTypeOf<Meta>()
package/src/builder.ts CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  import {
21
21
  type DecoratedProcedure,
22
22
  decorateProcedure,
23
- type ProcedureHandler,
23
+ type ProcedureFunc,
24
24
  } from './procedure'
25
25
  import { ProcedureBuilder } from './procedure-builder'
26
26
  import { ProcedureImplementer } from './procedure-implementer'
@@ -137,20 +137,20 @@ export class Builder<TContext extends Context, TExtraContext extends Context> {
137
137
  /**
138
138
  * Convert to Procedure
139
139
  */
140
- handler<UHandlerOutput = undefined>(
141
- handler: ProcedureHandler<
140
+ func<UFuncOutput = undefined>(
141
+ func: ProcedureFunc<
142
142
  TContext,
143
143
  TExtraContext,
144
144
  undefined,
145
145
  undefined,
146
- UHandlerOutput
146
+ UFuncOutput
147
147
  >,
148
148
  ): DecoratedProcedure<
149
149
  TContext,
150
150
  TExtraContext,
151
151
  undefined,
152
152
  undefined,
153
- UHandlerOutput
153
+ UFuncOutput
154
154
  > {
155
155
  return decorateProcedure({
156
156
  zz$p: {
@@ -159,7 +159,7 @@ export class Builder<TContext extends Context, TExtraContext extends Context> {
159
159
  InputSchema: undefined,
160
160
  OutputSchema: undefined,
161
161
  }),
162
- handler,
162
+ func,
163
163
  },
164
164
  })
165
165
  }
@@ -249,7 +249,7 @@ it('middleware can output', async () => {
249
249
  mid2Called = true
250
250
  return meta.output('from middleware 2')
251
251
  })
252
- .handler(() => {
252
+ .func(() => {
253
253
  handlerCalled = true
254
254
  return 'from handler'
255
255
  })
@@ -163,7 +163,7 @@ describe('use middleware', () => {
163
163
 
164
164
  describe('handler', () => {
165
165
  it('infer types', () => {
166
- const handler = builder.handler((input, context, meta) => {
166
+ const handler = builder.func((input, context, meta) => {
167
167
  expectTypeOf(input).toEqualTypeOf<unknown>()
168
168
  expectTypeOf(context).toEqualTypeOf<{ auth: boolean }>()
169
169
  expectTypeOf(meta).toEqualTypeOf<Meta>()
@@ -196,7 +196,7 @@ describe('handler', () => {
196
196
  const handler = builder
197
197
  .use(mid1)
198
198
  .use(mid2)
199
- .handler((input, context, meta) => {
199
+ .func((input, context, meta) => {
200
200
  expectTypeOf(input).toEqualTypeOf<unknown>()
201
201
  expectTypeOf(context).toEqualTypeOf<
202
202
  { userId: string } & { auth: boolean }
@@ -11,7 +11,7 @@ import {
11
11
  import {
12
12
  type DecoratedProcedure,
13
13
  decorateProcedure,
14
- type ProcedureHandler,
14
+ type ProcedureFunc,
15
15
  } from './procedure'
16
16
  import { ProcedureImplementer } from './procedure-implementer'
17
17
 
@@ -132,26 +132,26 @@ export class ProcedureBuilder<
132
132
  * Convert to Procedure
133
133
  */
134
134
 
135
- handler<UHandlerOutput extends SchemaOutput<TOutputSchema>>(
136
- handler: ProcedureHandler<
135
+ func<UFuncOutput extends SchemaOutput<TOutputSchema>>(
136
+ func: ProcedureFunc<
137
137
  TContext,
138
138
  TExtraContext,
139
139
  TInputSchema,
140
140
  TOutputSchema,
141
- UHandlerOutput
141
+ UFuncOutput
142
142
  >,
143
143
  ): DecoratedProcedure<
144
144
  TContext,
145
145
  TExtraContext,
146
146
  TInputSchema,
147
147
  TOutputSchema,
148
- UHandlerOutput
148
+ UFuncOutput
149
149
  > {
150
150
  return decorateProcedure({
151
151
  zz$p: {
152
152
  middlewares: this.zz$pb.middlewares,
153
153
  contract: this.zz$pb.contract,
154
- handler,
154
+ func,
155
155
  },
156
156
  })
157
157
  }
@@ -2,17 +2,15 @@ import { z } from 'zod'
2
2
  import { createProcedureCaller, os } from '.'
3
3
 
4
4
  describe('createProcedureCaller', () => {
5
- let internal = false
6
- let path = ['ping']
7
- let context = { auth: true }
5
+ const path = ['ping']
6
+ const context = { auth: true }
8
7
 
9
8
  const osw = os.context<{ auth?: boolean }>()
10
9
  const procedure = osw
11
10
  .input(z.object({ value: z.string().transform(v => Number(v)) }))
12
11
  .output(z.object({ value: z.number().transform(v => v.toString()) }))
13
- .handler((input, context, meta) => {
12
+ .func((input, context, meta) => {
14
13
  expect(context).toEqual(context)
15
- expect(meta.internal).toBe(internal)
16
14
  expect(meta.path).toBe(path)
17
15
 
18
16
  return input
@@ -34,8 +32,7 @@ describe('createProcedureCaller', () => {
34
32
  it('with validate', async () => {
35
33
  const caller = createProcedureCaller({
36
34
  procedure,
37
- context,
38
- internal,
35
+ context: async () => context,
39
36
  path,
40
37
  })
41
38
 
@@ -53,41 +50,14 @@ describe('createProcedureCaller', () => {
53
50
  )
54
51
  })
55
52
 
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
53
  it('without validate and schema', () => {
82
- const procedure = osw.handler(() => {
54
+ const procedure = osw.func(() => {
83
55
  return { value: true }
84
56
  })
85
57
 
86
58
  const caller = createProcedureCaller({
87
59
  procedure,
88
60
  context,
89
- internal,
90
- validate: false,
91
61
  })
92
62
 
93
63
  expectTypeOf(caller).toMatchTypeOf<
@@ -146,7 +116,7 @@ describe('createProcedureCaller', () => {
146
116
  .input(z.object({ id: z.string() }))
147
117
  .use(mid1)
148
118
  .use(mid2)
149
- .handler((input, context, meta) => {
119
+ .func((input, context, meta) => {
150
120
  expect(context).toEqual({ userId: '1', auth: false })
151
121
 
152
122
  expect(ref.value).toBe(2)
@@ -162,4 +132,25 @@ describe('createProcedureCaller', () => {
162
132
 
163
133
  expect(caller({ id: '1' })).resolves.toEqual('pong')
164
134
  })
135
+
136
+ it('accept form data', async () => {
137
+ const ping = osw
138
+ .input(z.object({ id: z.number() }))
139
+ .output(z.object({ id: z.number() }))
140
+ .func((input, context, meta) => {
141
+ expect(context).toEqual(context)
142
+
143
+ return input
144
+ })
145
+
146
+ const caller = createProcedureCaller({
147
+ procedure: ping,
148
+ context,
149
+ })
150
+
151
+ const form = new FormData()
152
+ form.append('id', '1')
153
+
154
+ expect(await caller(form)).toEqual({ id: 1 })
155
+ })
165
156
  })
@@ -2,84 +2,78 @@ import type { SchemaInput, SchemaOutput } from '@orpc/contract'
2
2
  import type { MiddlewareMeta } from './middleware'
3
3
  import type { Procedure } from './procedure'
4
4
  import type { Context } from './types'
5
+ import { type Value, value } from '@orpc/shared'
5
6
  import { ORPCError } from '@orpc/shared/error'
7
+ import { OpenAPIDeserializer } from '@orpc/transformer'
6
8
  import { mergeContext } from './utils'
7
9
 
8
10
  export interface CreateProcedureCallerOptions<
9
11
  TProcedure extends Procedure<any, any, any, any, any>,
10
- TValidate extends boolean,
11
12
  > {
12
13
  procedure: TProcedure
13
14
 
14
15
  /**
15
16
  * The context used when calling the procedure.
16
17
  */
17
- context: TProcedure extends Procedure<infer UContext, any, any, any, any>
18
- ? UContext
19
- : never
18
+ context: Value<
19
+ TProcedure extends Procedure<infer UContext, any, any, any, any>
20
+ ? UContext
21
+ : never
22
+ >
20
23
 
21
24
  /**
22
25
  * This is helpful for logging and analytics.
23
- */
24
- path?: string[]
25
-
26
- /**
27
- * This flag helpful when you want bypass some logics not necessary to internal server calls.
28
26
  *
29
- * @default true
27
+ * @internal
30
28
  */
31
- internal?: boolean
32
-
33
- /**
34
- * Indicate whether validate input and output.
35
- *
36
- * @default true
37
- */
38
- validate?: TValidate
29
+ path?: string[]
39
30
  }
40
31
 
41
32
  export type ProcedureCaller<
42
33
  TProcedure extends Procedure<any, any, any, any, any>,
43
- TValidate extends boolean,
44
34
  > = TProcedure extends Procedure<
45
35
  any,
46
36
  any,
47
37
  infer UInputSchema,
48
38
  infer UOutputSchema,
49
- infer UHandlerOutput
39
+ infer UFuncOutput
50
40
  >
51
41
  ? (
52
- input: TValidate extends true
53
- ? SchemaInput<UInputSchema>
54
- : SchemaOutput<UInputSchema>,
42
+ input: SchemaInput<UInputSchema> | FormData,
55
43
  ) => Promise<
56
- TValidate extends true
57
- ? SchemaOutput<UOutputSchema, UHandlerOutput>
58
- : SchemaInput<UOutputSchema, UHandlerOutput>
44
+ SchemaOutput<UOutputSchema, UFuncOutput>
59
45
  >
60
46
  : never
61
47
 
62
48
  export function createProcedureCaller<
63
49
  TProcedure extends Procedure<any, any, any, any, any>,
64
- TValidate extends boolean = true,
65
50
  >(
66
- options: CreateProcedureCallerOptions<TProcedure, TValidate>,
67
- ): ProcedureCaller<TProcedure, TValidate> {
68
- const internal = options.internal ?? true
51
+ options: CreateProcedureCallerOptions<TProcedure>,
52
+ ): ProcedureCaller<TProcedure> {
69
53
  const path = options.path ?? []
70
54
  const procedure = options.procedure
71
- const validate = options.validate ?? true
72
55
 
73
56
  const caller = async (input: unknown): Promise<unknown> => {
74
- const validInput = (() => {
75
- if (!validate)
57
+ const input_ = (() => {
58
+ if (!(input instanceof FormData)) {
76
59
  return input
60
+ }
61
+
62
+ const transformer = new OpenAPIDeserializer({
63
+ schema: procedure.zz$p.contract.zz$cp.InputSchema,
64
+ })
65
+
66
+ return transformer.deserializeAsFormData(input)
67
+ })()
68
+
69
+ const validInput = (() => {
77
70
  const schema = procedure.zz$p.contract.zz$cp.InputSchema
78
- if (!schema)
79
- return input
71
+ if (!schema) {
72
+ return input_
73
+ }
80
74
 
81
75
  try {
82
- return schema.parse(input)
76
+ return schema.parse(input_)
83
77
  }
84
78
  catch (e) {
85
79
  throw new ORPCError({
@@ -92,7 +86,7 @@ export function createProcedureCaller<
92
86
 
93
87
  const middlewares = procedure.zz$p.middlewares ?? []
94
88
  let currentMidIndex = 0
95
- let currentContext: Context = options.context
89
+ let currentContext: Context = await value(options.context)
96
90
 
97
91
  const next: MiddlewareMeta<unknown>['next'] = async (nextOptions) => {
98
92
  const mid = middlewares[currentMidIndex]
@@ -103,17 +97,15 @@ export function createProcedureCaller<
103
97
  return await mid(validInput, currentContext, {
104
98
  path,
105
99
  procedure,
106
- internal,
107
100
  next,
108
101
  output: output => ({ output, context: undefined }),
109
102
  })
110
103
  }
111
104
  else {
112
105
  return {
113
- output: await await procedure.zz$p.handler(validInput, currentContext, {
106
+ output: await await procedure.zz$p.func(validInput, currentContext, {
114
107
  path,
115
108
  procedure,
116
- internal,
117
109
  }),
118
110
  context: currentContext,
119
111
  }
@@ -123,11 +115,11 @@ export function createProcedureCaller<
123
115
  const output = (await next({})).output
124
116
 
125
117
  const validOutput = await (async () => {
126
- if (!validate)
127
- return output
128
118
  const schema = procedure.zz$p.contract.zz$cp.OutputSchema
129
- if (!schema)
119
+ if (!schema) {
130
120
  return output
121
+ }
122
+
131
123
  const result = await schema.safeParseAsync(output)
132
124
  if (result.error) {
133
125
  throw new ORPCError({
@@ -142,5 +134,5 @@ export function createProcedureCaller<
142
134
  return validOutput
143
135
  }
144
136
 
145
- return caller as ProcedureCaller<TProcedure, TValidate>
137
+ return caller as ProcedureCaller<TProcedure>
146
138
  }
@@ -111,9 +111,9 @@ describe('use middleware', () => {
111
111
 
112
112
  describe('output schema', () => {
113
113
  it('auto infer output schema if output schema is not specified', async () => {
114
- const sr = os.handler(() => ({ a: 1 }))
114
+ const sr = os.func(() => ({ a: 1 }))
115
115
 
116
- const result = await sr.zz$p.handler({}, undefined, {
116
+ const result = await sr.zz$p.func({}, undefined, {
117
117
  method: 'GET',
118
118
  path: '/',
119
119
  } as any)
@@ -129,9 +129,9 @@ describe('output schema', () => {
129
129
  }),
130
130
  })
131
131
 
132
- const sr = srb1.handler(() => ({ b: 1 }))
132
+ const sr = srb1.func(() => ({ b: 1 }))
133
133
 
134
- const result = await sr.zz$p.handler({}, {}, {
134
+ const result = await sr.zz$p.func({}, {}, {
135
135
  method: 'GET',
136
136
  path: '/',
137
137
  } as any)
@@ -142,7 +142,7 @@ describe('output schema', () => {
142
142
 
143
143
  describe('handler', () => {
144
144
  it('infer types', () => {
145
- const handler = implementer1.handler((input, context, meta) => {
145
+ const handler = implementer1.func((input, context, meta) => {
146
146
  expectTypeOf(input).toEqualTypeOf<unknown>()
147
147
  expectTypeOf(context).toEqualTypeOf<{ auth: boolean }>()
148
148
  expectTypeOf(meta).toEqualTypeOf<Meta>()
@@ -163,7 +163,7 @@ describe('handler', () => {
163
163
  >()
164
164
  expect(isProcedure(handler)).toBe(true)
165
165
 
166
- implementer2.handler((input, context, meta) => {
166
+ implementer2.func((input, context, meta) => {
167
167
  expectTypeOf(input).toEqualTypeOf<{ id: string }>()
168
168
  expectTypeOf(context).toEqualTypeOf<{ auth: boolean }>()
169
169
  expectTypeOf(meta).toEqualTypeOf<Meta>()
@@ -174,7 +174,7 @@ describe('handler', () => {
174
174
  })
175
175
 
176
176
  // @ts-expect-error mismatch output
177
- implementer2.handler(() => {})
177
+ implementer2.func(() => {})
178
178
  })
179
179
 
180
180
  it('combine middlewares', () => {
@@ -193,7 +193,7 @@ describe('handler', () => {
193
193
  const handler = implementer2
194
194
  .use(mid1)
195
195
  .use(mid2)
196
- .handler((input, context, meta) => {
196
+ .func((input, context, meta) => {
197
197
  expectTypeOf(input).toEqualTypeOf<{ id: string }>()
198
198
  expectTypeOf(context).toEqualTypeOf<
199
199
  { auth: boolean } & { userId: string }
@@ -8,7 +8,7 @@ import {
8
8
  import {
9
9
  type DecoratedProcedure,
10
10
  decorateProcedure,
11
- type ProcedureHandler,
11
+ type ProcedureFunc,
12
12
  } from './procedure'
13
13
 
14
14
  export class ProcedureImplementer<
@@ -76,26 +76,26 @@ export class ProcedureImplementer<
76
76
  })
77
77
  }
78
78
 
79
- handler<UHandlerOutput extends SchemaOutput<TOutputSchema>>(
80
- handler: ProcedureHandler<
79
+ func<UFuncOutput extends SchemaOutput<TOutputSchema>>(
80
+ func: ProcedureFunc<
81
81
  TContext,
82
82
  TExtraContext,
83
83
  TInputSchema,
84
84
  TOutputSchema,
85
- UHandlerOutput
85
+ UFuncOutput
86
86
  >,
87
87
  ): DecoratedProcedure<
88
88
  TContext,
89
89
  TExtraContext,
90
90
  TInputSchema,
91
91
  TOutputSchema,
92
- UHandlerOutput
92
+ UFuncOutput
93
93
  > {
94
94
  return decorateProcedure({
95
95
  zz$p: {
96
96
  middlewares: this.zz$pi.middlewares,
97
97
  contract: this.zz$pi.contract,
98
- handler,
98
+ func,
99
99
  },
100
100
  })
101
101
  }