@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.
- package/dist/chunk-ACLC6USM.js +262 -0
- package/dist/chunk-ACLC6USM.js.map +1 -0
- package/dist/fetch.js +648 -0
- package/dist/fetch.js.map +1 -0
- package/dist/index.js +403 -0
- package/dist/index.js.map +1 -0
- package/dist/src/adapters/fetch.d.ts +36 -0
- package/dist/src/adapters/fetch.d.ts.map +1 -0
- package/dist/src/builder.d.ts +49 -0
- package/dist/src/builder.d.ts.map +1 -0
- package/dist/src/index.d.ts +15 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/middleware.d.ts +16 -0
- package/dist/src/middleware.d.ts.map +1 -0
- package/dist/src/procedure-builder.d.ts +31 -0
- package/dist/src/procedure-builder.d.ts.map +1 -0
- package/dist/src/procedure-caller.d.ts +33 -0
- package/dist/src/procedure-caller.d.ts.map +1 -0
- package/dist/src/procedure-implementer.d.ts +19 -0
- package/dist/src/procedure-implementer.d.ts.map +1 -0
- package/dist/src/procedure.d.ts +29 -0
- package/dist/src/procedure.d.ts.map +1 -0
- package/dist/src/router-builder.d.ts +22 -0
- package/dist/src/router-builder.d.ts.map +1 -0
- package/dist/src/router-caller.d.ts +36 -0
- package/dist/src/router-caller.d.ts.map +1 -0
- package/dist/src/router-implementer.d.ts +20 -0
- package/dist/src/router-implementer.d.ts.map +1 -0
- package/dist/src/router.d.ts +14 -0
- package/dist/src/router.d.ts.map +1 -0
- package/dist/src/types.d.ts +18 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/utils.d.ts +4 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +57 -0
- package/src/adapters/fetch.test.ts +399 -0
- package/src/adapters/fetch.ts +228 -0
- package/src/builder.test.ts +362 -0
- package/src/builder.ts +236 -0
- package/src/index.ts +16 -0
- package/src/middleware.test.ts +279 -0
- package/src/middleware.ts +119 -0
- package/src/procedure-builder.test.ts +219 -0
- package/src/procedure-builder.ts +158 -0
- package/src/procedure-caller.test.ts +210 -0
- package/src/procedure-caller.ts +165 -0
- package/src/procedure-implementer.test.ts +215 -0
- package/src/procedure-implementer.ts +103 -0
- package/src/procedure.test.ts +312 -0
- package/src/procedure.ts +251 -0
- package/src/router-builder.test.ts +106 -0
- package/src/router-builder.ts +120 -0
- package/src/router-caller.test.ts +173 -0
- package/src/router-caller.ts +95 -0
- package/src/router-implementer.test.ts +116 -0
- package/src/router-implementer.ts +110 -0
- package/src/router.test.ts +142 -0
- package/src/router.ts +69 -0
- package/src/types.test.ts +18 -0
- package/src/types.ts +28 -0
- package/src/utils.test.ts +243 -0
- package/src/utils.ts +95 -0
@@ -0,0 +1,228 @@
|
|
1
|
+
/// <reference lib="dom" />
|
2
|
+
|
3
|
+
import {
|
4
|
+
type HTTPPath,
|
5
|
+
ORPC_HEADER,
|
6
|
+
ORPC_HEADER_VALUE,
|
7
|
+
standardizeHTTPPath,
|
8
|
+
} from '@orpc/contract'
|
9
|
+
import {
|
10
|
+
type PartialOnUndefinedDeep,
|
11
|
+
get,
|
12
|
+
isPlainObject,
|
13
|
+
trim,
|
14
|
+
} from '@orpc/shared'
|
15
|
+
import { ORPCError } from '@orpc/shared/error'
|
16
|
+
import {
|
17
|
+
ORPCDeserializer,
|
18
|
+
ORPCSerializer,
|
19
|
+
OpenAPIDeserializer,
|
20
|
+
OpenAPISerializer,
|
21
|
+
} from '@orpc/transformer'
|
22
|
+
import { LinearRouter } from 'hono/router/linear-router'
|
23
|
+
import { RegExpRouter } from 'hono/router/reg-exp-router'
|
24
|
+
import { type WELL_DEFINED_PROCEDURE, isProcedure } from '../procedure'
|
25
|
+
import { createProcedureCaller } from '../procedure-caller'
|
26
|
+
import type { Router } from '../router'
|
27
|
+
import type { Meta, Promisable } from '../types'
|
28
|
+
import { hook } from '../utils'
|
29
|
+
|
30
|
+
export interface CreateFetchHandlerOptions<TRouter extends Router<any>> {
|
31
|
+
router: TRouter
|
32
|
+
|
33
|
+
hooks?: (
|
34
|
+
context: TRouter extends Router<infer UContext> ? UContext : never,
|
35
|
+
meta: Meta<unknown>,
|
36
|
+
) => Promisable<void>
|
37
|
+
|
38
|
+
/**
|
39
|
+
* It will help improve the cold start time. But it will increase the performance.
|
40
|
+
*
|
41
|
+
* @default false
|
42
|
+
*/
|
43
|
+
serverless?: boolean
|
44
|
+
}
|
45
|
+
|
46
|
+
export function createFetchHandler<TRouter extends Router<any>>(
|
47
|
+
options: CreateFetchHandlerOptions<TRouter>,
|
48
|
+
): FetchHandler<TRouter> {
|
49
|
+
const routing = options.serverless
|
50
|
+
? new LinearRouter<[string[], WELL_DEFINED_PROCEDURE]>()
|
51
|
+
: new RegExpRouter<[string[], WELL_DEFINED_PROCEDURE]>()
|
52
|
+
|
53
|
+
const addRouteRecursively = (router: Router<any>, basePath: string[]) => {
|
54
|
+
for (const key in router) {
|
55
|
+
const currentPath = [...basePath, key]
|
56
|
+
const item = router[key] as WELL_DEFINED_PROCEDURE | Router<any>
|
57
|
+
|
58
|
+
if (isProcedure(item)) {
|
59
|
+
if (item.zz$p.contract.zz$cp.path) {
|
60
|
+
const method = item.zz$p.contract.zz$cp.method ?? 'POST'
|
61
|
+
const path = openAPIPathToRouterPath(item.zz$p.contract.zz$cp.path)
|
62
|
+
|
63
|
+
routing.add(method, path, [currentPath, item])
|
64
|
+
}
|
65
|
+
} else {
|
66
|
+
addRouteRecursively(item, currentPath)
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
addRouteRecursively(options.router, [])
|
72
|
+
|
73
|
+
return async (requestOptions) => {
|
74
|
+
const isORPCTransformer =
|
75
|
+
requestOptions.request.headers.get(ORPC_HEADER) === ORPC_HEADER_VALUE
|
76
|
+
const accept = requestOptions.request.headers.get('Accept') ?? undefined
|
77
|
+
|
78
|
+
const serializer = isORPCTransformer
|
79
|
+
? new ORPCSerializer()
|
80
|
+
: new OpenAPISerializer({ accept })
|
81
|
+
|
82
|
+
try {
|
83
|
+
return await hook(async (hooks) => {
|
84
|
+
const url = new URL(requestOptions.request.url)
|
85
|
+
const pathname = `/${trim(url.pathname.replace(requestOptions.prefix ?? '', ''), '/')}`
|
86
|
+
|
87
|
+
let path: string[] | undefined
|
88
|
+
let procedure: WELL_DEFINED_PROCEDURE | undefined
|
89
|
+
let params: Record<string, string | number> | undefined
|
90
|
+
|
91
|
+
if (isORPCTransformer) {
|
92
|
+
path = trim(pathname, '/').split('/').map(decodeURIComponent)
|
93
|
+
const val = get(options.router, path)
|
94
|
+
|
95
|
+
if (isProcedure(val)) {
|
96
|
+
procedure = val
|
97
|
+
}
|
98
|
+
} else {
|
99
|
+
const [[match]] = routing.match(
|
100
|
+
requestOptions.request.method,
|
101
|
+
pathname,
|
102
|
+
)
|
103
|
+
path = match?.[0][0]
|
104
|
+
procedure = match?.[0][1]
|
105
|
+
params = match?.[1]
|
106
|
+
|
107
|
+
if (!path || !procedure) {
|
108
|
+
path = trim(pathname, '/').split('/').map(decodeURIComponent)
|
109
|
+
|
110
|
+
const val = get(options.router, path)
|
111
|
+
|
112
|
+
if (isProcedure(val)) {
|
113
|
+
procedure = val
|
114
|
+
}
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
if (!path || !procedure) {
|
119
|
+
throw new ORPCError({ code: 'NOT_FOUND', message: 'Not found' })
|
120
|
+
}
|
121
|
+
|
122
|
+
const meta: Meta<unknown> = {
|
123
|
+
...hooks,
|
124
|
+
procedure,
|
125
|
+
path: path,
|
126
|
+
internal: false,
|
127
|
+
}
|
128
|
+
|
129
|
+
await options.hooks?.(requestOptions.context as any, meta)
|
130
|
+
|
131
|
+
const deserializer = isORPCTransformer
|
132
|
+
? new ORPCDeserializer()
|
133
|
+
: new OpenAPIDeserializer({
|
134
|
+
schema: procedure.zz$p.contract.zz$cp.InputSchema,
|
135
|
+
})
|
136
|
+
|
137
|
+
const input_ = await (async () => {
|
138
|
+
try {
|
139
|
+
return await deserializer.deserialize(requestOptions.request)
|
140
|
+
} catch (e) {
|
141
|
+
throw new ORPCError({
|
142
|
+
code: 'BAD_REQUEST',
|
143
|
+
message:
|
144
|
+
'Cannot parse request. Please check the request body and Content-Type header.',
|
145
|
+
cause: e,
|
146
|
+
})
|
147
|
+
}
|
148
|
+
})()
|
149
|
+
|
150
|
+
const input = (() => {
|
151
|
+
if (
|
152
|
+
params &&
|
153
|
+
Object.keys(params).length > 0 &&
|
154
|
+
(input_ === undefined || isPlainObject(input_))
|
155
|
+
) {
|
156
|
+
return {
|
157
|
+
...params,
|
158
|
+
...input_,
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
return input_
|
163
|
+
})()
|
164
|
+
|
165
|
+
const caller = createProcedureCaller({
|
166
|
+
context: requestOptions.context,
|
167
|
+
internal: false,
|
168
|
+
validate: true,
|
169
|
+
procedure,
|
170
|
+
path,
|
171
|
+
})
|
172
|
+
|
173
|
+
const output = await caller(input)
|
174
|
+
|
175
|
+
const { body, headers } = serializer.serialize(output)
|
176
|
+
|
177
|
+
return new Response(body, {
|
178
|
+
status: 200,
|
179
|
+
headers,
|
180
|
+
})
|
181
|
+
})
|
182
|
+
} catch (e) {
|
183
|
+
const error =
|
184
|
+
e instanceof ORPCError
|
185
|
+
? e
|
186
|
+
: new ORPCError({
|
187
|
+
code: 'INTERNAL_SERVER_ERROR',
|
188
|
+
message: 'Internal server error',
|
189
|
+
cause: e,
|
190
|
+
})
|
191
|
+
|
192
|
+
const { body, headers } = serializer.serialize(error.toJSON())
|
193
|
+
|
194
|
+
return new Response(body, {
|
195
|
+
status: error.status,
|
196
|
+
headers: headers,
|
197
|
+
})
|
198
|
+
}
|
199
|
+
}
|
200
|
+
}
|
201
|
+
|
202
|
+
function openAPIPathToRouterPath(path: HTTPPath): string {
|
203
|
+
return standardizeHTTPPath(path).replace(/\{([^}]+)\}/g, ':$1')
|
204
|
+
}
|
205
|
+
|
206
|
+
export type FetchHandlerOptions<TRouter extends Router<any>> = {
|
207
|
+
/**
|
208
|
+
* The request need to be handled.
|
209
|
+
*/
|
210
|
+
request: Request
|
211
|
+
|
212
|
+
/**
|
213
|
+
* Remove the prefix from the request path.
|
214
|
+
*
|
215
|
+
* @example /orpc
|
216
|
+
* @example /api
|
217
|
+
*/
|
218
|
+
prefix?: string
|
219
|
+
} & PartialOnUndefinedDeep<{
|
220
|
+
/**
|
221
|
+
* The context used to handle the request.
|
222
|
+
*/
|
223
|
+
context: TRouter extends Router<infer UContext> ? UContext : never
|
224
|
+
}>
|
225
|
+
|
226
|
+
export interface FetchHandler<TRouter extends Router<any>> {
|
227
|
+
(options: FetchHandlerOptions<TRouter>): Promise<Response>
|
228
|
+
}
|
@@ -0,0 +1,362 @@
|
|
1
|
+
import { oc } from '@orpc/contract'
|
2
|
+
import { z } from 'zod'
|
3
|
+
import {
|
4
|
+
os,
|
5
|
+
type Builder,
|
6
|
+
type DecoratedMiddleware,
|
7
|
+
type DecoratedProcedure,
|
8
|
+
type Meta,
|
9
|
+
ProcedureBuilder,
|
10
|
+
ProcedureImplementer,
|
11
|
+
RouterImplementer,
|
12
|
+
isProcedure,
|
13
|
+
} from '.'
|
14
|
+
import { RouterBuilder } from './router-builder'
|
15
|
+
|
16
|
+
test('context method', () => {
|
17
|
+
expectTypeOf<
|
18
|
+
typeof os extends Builder<infer TContext, any> ? TContext : never
|
19
|
+
>().toEqualTypeOf<undefined | Record<string, unknown>>()
|
20
|
+
|
21
|
+
const os2 = os.context<{ foo: 'bar' }>()
|
22
|
+
|
23
|
+
expectTypeOf<
|
24
|
+
typeof os2 extends Builder<infer TContext, any> ? TContext : never
|
25
|
+
>().toEqualTypeOf<{ foo: 'bar' }>()
|
26
|
+
|
27
|
+
const os3 = os.context<{ foo: 'bar' }>().context()
|
28
|
+
|
29
|
+
expectTypeOf<
|
30
|
+
typeof os3 extends Builder<infer TContext, any> ? TContext : never
|
31
|
+
>().toEqualTypeOf<{ foo: 'bar' }>()
|
32
|
+
})
|
33
|
+
|
34
|
+
describe('use middleware', () => {
|
35
|
+
type Context = { auth: boolean }
|
36
|
+
|
37
|
+
const osw = os.context<Context>()
|
38
|
+
|
39
|
+
it('infer types', () => {
|
40
|
+
osw.use((input, context, meta) => {
|
41
|
+
expectTypeOf(input).toEqualTypeOf<unknown>()
|
42
|
+
expectTypeOf(context).toEqualTypeOf<Context>()
|
43
|
+
expectTypeOf(meta).toEqualTypeOf<Meta<unknown>>()
|
44
|
+
})
|
45
|
+
})
|
46
|
+
|
47
|
+
it('can map context', () => {
|
48
|
+
osw
|
49
|
+
.use(() => {
|
50
|
+
return { context: { userId: '1' } }
|
51
|
+
})
|
52
|
+
.use((_, context) => {
|
53
|
+
expectTypeOf(context).toMatchTypeOf<Context & { userId: string }>()
|
54
|
+
})
|
55
|
+
})
|
56
|
+
|
57
|
+
it('can map input', () => {
|
58
|
+
osw
|
59
|
+
// @ts-expect-error mismatch input
|
60
|
+
.use((input: { postId: string }) => {})
|
61
|
+
.use(
|
62
|
+
(input: { postId: string }) => {
|
63
|
+
return { context: { user: '1' } }
|
64
|
+
},
|
65
|
+
(input) => {
|
66
|
+
expectTypeOf(input).toEqualTypeOf<unknown>()
|
67
|
+
return { postId: '1' }
|
68
|
+
},
|
69
|
+
)
|
70
|
+
.handler((_, context) => {
|
71
|
+
expectTypeOf(context).toMatchTypeOf<{ user: string }>()
|
72
|
+
})
|
73
|
+
})
|
74
|
+
})
|
75
|
+
|
76
|
+
describe('create middleware', () => {
|
77
|
+
it('infer types', () => {
|
78
|
+
const mid = os
|
79
|
+
.context<{ auth: boolean }>()
|
80
|
+
.middleware((input, context, meta) => {
|
81
|
+
expectTypeOf(input).toEqualTypeOf<unknown>()
|
82
|
+
expectTypeOf(context).toEqualTypeOf<{ auth: boolean }>()
|
83
|
+
expectTypeOf(meta).toEqualTypeOf<Meta<unknown>>()
|
84
|
+
})
|
85
|
+
|
86
|
+
expectTypeOf(mid).toEqualTypeOf<
|
87
|
+
DecoratedMiddleware<{ auth: boolean }, undefined, unknown, unknown>
|
88
|
+
>()
|
89
|
+
})
|
90
|
+
|
91
|
+
it('map context', () => {
|
92
|
+
const mid = os.context<{ auth: boolean }>().middleware(() => {
|
93
|
+
return { context: { userId: '1' } }
|
94
|
+
})
|
95
|
+
|
96
|
+
expectTypeOf(mid).toEqualTypeOf<
|
97
|
+
DecoratedMiddleware<
|
98
|
+
{ auth: boolean },
|
99
|
+
{ userId: string },
|
100
|
+
unknown,
|
101
|
+
unknown
|
102
|
+
>
|
103
|
+
>()
|
104
|
+
})
|
105
|
+
})
|
106
|
+
|
107
|
+
test('router method', () => {
|
108
|
+
const pingContract = oc.input(z.string()).output(z.string())
|
109
|
+
const userFindContract = oc
|
110
|
+
.input(z.object({ id: z.string() }))
|
111
|
+
.output(z.object({ name: z.string() }))
|
112
|
+
|
113
|
+
const contract = oc.router({
|
114
|
+
ping: pingContract,
|
115
|
+
user: {
|
116
|
+
find: userFindContract,
|
117
|
+
},
|
118
|
+
|
119
|
+
user2: oc.router({
|
120
|
+
find: userFindContract,
|
121
|
+
}),
|
122
|
+
|
123
|
+
router: userFindContract,
|
124
|
+
})
|
125
|
+
|
126
|
+
const osw = os.contract(contract)
|
127
|
+
|
128
|
+
expect(osw.ping).instanceOf(ProcedureImplementer)
|
129
|
+
expect(osw.ping.zz$pi.contract).toEqual(pingContract)
|
130
|
+
|
131
|
+
expect(osw.user).instanceOf(RouterImplementer)
|
132
|
+
|
133
|
+
expect(osw.user.find).instanceOf(ProcedureImplementer)
|
134
|
+
expect(osw.user.find.zz$pi.contract).toEqual(userFindContract)
|
135
|
+
|
136
|
+
// Because of the router keyword is special, we can't use instanceof
|
137
|
+
expect(osw.router.zz$pi.contract).toEqual(userFindContract)
|
138
|
+
expect(
|
139
|
+
osw.router.handler(() => {
|
140
|
+
return { name: '' }
|
141
|
+
}),
|
142
|
+
).toSatisfy(isProcedure)
|
143
|
+
})
|
144
|
+
|
145
|
+
describe('define procedure builder', () => {
|
146
|
+
const osw = os.context<{ auth: boolean }>()
|
147
|
+
const schema1 = z.object({})
|
148
|
+
const example1 = {}
|
149
|
+
const schema2 = z.object({ a: z.string() })
|
150
|
+
const example2 = { a: '' }
|
151
|
+
|
152
|
+
test('input method', () => {
|
153
|
+
const builder = osw.input(schema1, example1)
|
154
|
+
|
155
|
+
expectTypeOf(builder).toEqualTypeOf<
|
156
|
+
ProcedureBuilder<{ auth: boolean }, undefined, typeof schema1, undefined>
|
157
|
+
>()
|
158
|
+
|
159
|
+
expect(builder).instanceOf(ProcedureBuilder)
|
160
|
+
expect(builder.zz$pb.middlewares).toBe(undefined)
|
161
|
+
expect(builder.zz$pb).toMatchObject({
|
162
|
+
contract: {
|
163
|
+
zz$cp: {
|
164
|
+
InputSchema: schema1,
|
165
|
+
inputExample: example1,
|
166
|
+
},
|
167
|
+
},
|
168
|
+
})
|
169
|
+
})
|
170
|
+
|
171
|
+
test('output method', () => {
|
172
|
+
const builder = osw.output(schema2, example2)
|
173
|
+
|
174
|
+
expectTypeOf(builder).toEqualTypeOf<
|
175
|
+
ProcedureBuilder<{ auth: boolean }, undefined, undefined, typeof schema2>
|
176
|
+
>()
|
177
|
+
|
178
|
+
expect(builder).instanceOf(ProcedureBuilder)
|
179
|
+
expect(builder.zz$pb.middlewares).toBe(undefined)
|
180
|
+
expect(builder.zz$pb).toMatchObject({
|
181
|
+
contract: {
|
182
|
+
zz$cp: {
|
183
|
+
OutputSchema: schema2,
|
184
|
+
outputExample: example2,
|
185
|
+
},
|
186
|
+
},
|
187
|
+
})
|
188
|
+
})
|
189
|
+
|
190
|
+
test('route method', () => {
|
191
|
+
const builder = osw.route({
|
192
|
+
method: 'GET',
|
193
|
+
path: '/test',
|
194
|
+
deprecated: true,
|
195
|
+
description: 'des',
|
196
|
+
summary: 'sum',
|
197
|
+
tags: ['cccc'],
|
198
|
+
})
|
199
|
+
|
200
|
+
expectTypeOf(builder).toEqualTypeOf<
|
201
|
+
ProcedureBuilder<{ auth: boolean }, undefined, undefined, undefined>
|
202
|
+
>()
|
203
|
+
|
204
|
+
expect(builder).instanceOf(ProcedureBuilder)
|
205
|
+
expect(builder.zz$pb.middlewares).toBe(undefined)
|
206
|
+
expect(builder.zz$pb).toMatchObject({
|
207
|
+
contract: {
|
208
|
+
zz$cp: {
|
209
|
+
method: 'GET',
|
210
|
+
path: '/test',
|
211
|
+
deprecated: true,
|
212
|
+
description: 'des',
|
213
|
+
summary: 'sum',
|
214
|
+
tags: ['cccc'],
|
215
|
+
},
|
216
|
+
},
|
217
|
+
})
|
218
|
+
})
|
219
|
+
|
220
|
+
test('with middlewares', () => {
|
221
|
+
const mid = os.middleware(() => {
|
222
|
+
return {
|
223
|
+
context: {
|
224
|
+
userId: 'string',
|
225
|
+
},
|
226
|
+
}
|
227
|
+
})
|
228
|
+
|
229
|
+
const mid2 = os.middleware(() => {
|
230
|
+
return {
|
231
|
+
context: {
|
232
|
+
mid2: true,
|
233
|
+
},
|
234
|
+
}
|
235
|
+
})
|
236
|
+
|
237
|
+
const osw = os.context<{ auth: boolean }>().use(mid).use(mid2)
|
238
|
+
|
239
|
+
const builder1 = osw.input(schema1)
|
240
|
+
const builder2 = osw.output(schema2)
|
241
|
+
const builder3 = osw.route({ method: 'GET', path: '/test' })
|
242
|
+
|
243
|
+
expectTypeOf(builder1).toEqualTypeOf<
|
244
|
+
ProcedureBuilder<
|
245
|
+
{ auth: boolean },
|
246
|
+
{ userId: string } & { mid2: boolean },
|
247
|
+
typeof schema1,
|
248
|
+
undefined
|
249
|
+
>
|
250
|
+
>()
|
251
|
+
|
252
|
+
expectTypeOf(builder2).toEqualTypeOf<
|
253
|
+
ProcedureBuilder<
|
254
|
+
{ auth: boolean },
|
255
|
+
{ userId: string } & { mid2: boolean },
|
256
|
+
undefined,
|
257
|
+
typeof schema2
|
258
|
+
>
|
259
|
+
>()
|
260
|
+
|
261
|
+
expectTypeOf(builder3).toEqualTypeOf<
|
262
|
+
ProcedureBuilder<
|
263
|
+
{ auth: boolean },
|
264
|
+
{ userId: string } & { mid2: boolean },
|
265
|
+
undefined,
|
266
|
+
undefined
|
267
|
+
>
|
268
|
+
>()
|
269
|
+
|
270
|
+
expect(builder1.zz$pb.middlewares).toEqual([mid, mid2])
|
271
|
+
expect(builder2.zz$pb.middlewares).toEqual([mid, mid2])
|
272
|
+
expect(builder3.zz$pb.middlewares).toEqual([mid, mid2])
|
273
|
+
})
|
274
|
+
})
|
275
|
+
|
276
|
+
describe('handler method', () => {
|
277
|
+
it('without middlewares', () => {
|
278
|
+
const osw = os.context<{ auth: boolean }>()
|
279
|
+
|
280
|
+
const procedure = osw.handler((input, context, meta) => {
|
281
|
+
expectTypeOf(input).toEqualTypeOf<unknown>()
|
282
|
+
expectTypeOf(context).toEqualTypeOf<{ auth: boolean }>()
|
283
|
+
expectTypeOf(meta).toEqualTypeOf<Meta<unknown>>()
|
284
|
+
})
|
285
|
+
|
286
|
+
expectTypeOf(procedure).toEqualTypeOf<
|
287
|
+
DecoratedProcedure<
|
288
|
+
{ auth: boolean },
|
289
|
+
undefined,
|
290
|
+
undefined,
|
291
|
+
undefined,
|
292
|
+
void
|
293
|
+
>
|
294
|
+
>()
|
295
|
+
|
296
|
+
expect(isProcedure(procedure)).toBe(true)
|
297
|
+
expect(procedure.zz$p.middlewares).toBe(undefined)
|
298
|
+
})
|
299
|
+
|
300
|
+
it('with middlewares', () => {
|
301
|
+
const mid = os.middleware(() => {
|
302
|
+
return {
|
303
|
+
context: {
|
304
|
+
userId: 'string',
|
305
|
+
},
|
306
|
+
}
|
307
|
+
})
|
308
|
+
|
309
|
+
const osw = os.context<{ auth: boolean }>().use(mid)
|
310
|
+
|
311
|
+
const procedure = osw.handler((input, context, meta) => {
|
312
|
+
expectTypeOf(input).toEqualTypeOf<unknown>()
|
313
|
+
expectTypeOf(context).toMatchTypeOf<{ auth: boolean }>()
|
314
|
+
expectTypeOf(meta).toEqualTypeOf<Meta<unknown>>()
|
315
|
+
})
|
316
|
+
|
317
|
+
expectTypeOf(procedure).toEqualTypeOf<
|
318
|
+
DecoratedProcedure<
|
319
|
+
{ auth: boolean },
|
320
|
+
{ userId: string },
|
321
|
+
undefined,
|
322
|
+
undefined,
|
323
|
+
void
|
324
|
+
>
|
325
|
+
>()
|
326
|
+
|
327
|
+
expect(isProcedure(procedure)).toBe(true)
|
328
|
+
expect(procedure.zz$p.middlewares).toEqual([mid])
|
329
|
+
})
|
330
|
+
})
|
331
|
+
|
332
|
+
test('prefix', () => {
|
333
|
+
const builder = os
|
334
|
+
.context<{ auth: boolean }>()
|
335
|
+
.use(() => {
|
336
|
+
return { context: { userId: '1' } }
|
337
|
+
})
|
338
|
+
.prefix('/api')
|
339
|
+
|
340
|
+
expectTypeOf(builder).toEqualTypeOf<
|
341
|
+
RouterBuilder<{ auth: boolean }, { userId: string }>
|
342
|
+
>()
|
343
|
+
|
344
|
+
expect(builder).instanceOf(RouterBuilder)
|
345
|
+
expect(builder.zz$rb.prefix).toEqual('/api')
|
346
|
+
})
|
347
|
+
|
348
|
+
test('tags', () => {
|
349
|
+
const builder = os
|
350
|
+
.context<{ auth: boolean }>()
|
351
|
+
.use(() => {
|
352
|
+
return { context: { userId: '1' } }
|
353
|
+
})
|
354
|
+
.tags('user', 'user2')
|
355
|
+
|
356
|
+
expectTypeOf(builder).toEqualTypeOf<
|
357
|
+
RouterBuilder<{ auth: boolean }, { userId: string }>
|
358
|
+
>()
|
359
|
+
|
360
|
+
expect(builder).instanceOf(RouterBuilder)
|
361
|
+
expect(builder.zz$rb.tags).toEqual(['user', 'user2'])
|
362
|
+
})
|