Skip to content

Commit 4555a17

Browse files
authored
feat(contract)!: rewrite and more tests (#51)
BREAKING CHANGE: `.tags` -> `.tag`
1 parent e8d4b28 commit 4555a17

45 files changed

Lines changed: 1107 additions & 781 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/client/src/procedure.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33

44
import type { Caller } from '@orpc/server'
55
import type { Promisable } from '@orpc/shared'
6-
import { ORPC_HEADER, ORPC_HEADER_VALUE } from '@orpc/contract'
7-
import { trim } from '@orpc/shared'
6+
import { ORPC_PROTOCOL_HEADER, ORPC_PROTOCOL_VALUE, trim } from '@orpc/shared'
87
import { ORPCError } from '@orpc/shared/error'
98
import { ORPCDeserializer, ORPCSerializer } from '@orpc/transformer'
109

@@ -43,21 +42,27 @@ export function createProcedureClient<TInput, TOutput>(
4342

4443
const fetch_ = options.fetch ?? fetch
4544
const url = `${trim(options.baseURL, '/')}/${options.path.map(encodeURIComponent).join('/')}`
46-
let headers = await options.headers?.(input)
47-
headers = headers instanceof Headers ? headers : new Headers(headers)
4845

49-
const { body, headers: headers_ } = serializer.serialize(input)
46+
const headers = new Headers({
47+
[ORPC_PROTOCOL_HEADER]: ORPC_PROTOCOL_VALUE,
48+
})
5049

51-
for (const [key, value] of headers_.entries()) {
50+
let customHeaders = await options.headers?.(input)
51+
customHeaders = customHeaders instanceof Headers ? customHeaders : new Headers(customHeaders)
52+
for (const [key, value] of customHeaders.entries()) {
5253
headers.append(key, value)
5354
}
5455

55-
headers.set(ORPC_HEADER, ORPC_HEADER_VALUE)
56+
const serialized = serializer.serialize(input)
57+
58+
for (const [key, value] of serialized.headers.entries()) {
59+
headers.append(key, value)
60+
}
5661

5762
const response = await fetch_(url, {
5863
method: 'POST',
5964
headers,
60-
body,
65+
body: serialized.body,
6166
signal: callerOptions?.signal,
6267
})
6368

packages/contract/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,10 @@
4444
"dependencies": {
4545
"@orpc/shared": "workspace:*",
4646
"@standard-schema/spec": "1.0.0-beta.4"
47+
},
48+
"devDependencies": {
49+
"arktype": "2.0.0-rc.26",
50+
"valibot": "1.0.0-beta.9",
51+
"zod": "3.24.1"
4752
}
4853
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import type { DecoratedContractProcedure } from './procedure-decorated'
2+
import type { ContractRouterBuilder } from './router-builder'
3+
import { z } from 'zod'
4+
import { ContractBuilder } from './builder'
5+
import { ContractProcedure } from './procedure'
6+
7+
const builder = new ContractBuilder()
8+
9+
describe('to ContractRouterBuilder', () => {
10+
it('prefix', () => {
11+
expectTypeOf(builder.prefix('/prefix')).toEqualTypeOf<
12+
ContractRouterBuilder
13+
>()
14+
15+
// @ts-expect-error - invalid prefix
16+
builder.prefix(1)
17+
// @ts-expect-error - invalid prefix
18+
builder.prefix('')
19+
})
20+
21+
it('tags', () => {
22+
expectTypeOf(builder.tag('tag1', 'tag2')).toEqualTypeOf<
23+
ContractRouterBuilder
24+
>()
25+
26+
// @ts-expect-error - invalid tag
27+
builder.tag(1)
28+
// @ts-expect-error - invalid tag
29+
builder.tag({})
30+
})
31+
})
32+
33+
describe('to DecoratedContractProcedure', () => {
34+
it('route', () => {
35+
expectTypeOf(builder.route({ method: 'GET', path: '/path' })).toEqualTypeOf<
36+
DecoratedContractProcedure<undefined, undefined>
37+
>()
38+
39+
expectTypeOf(builder.route({ })).toEqualTypeOf<
40+
DecoratedContractProcedure<undefined, undefined>
41+
>()
42+
43+
// @ts-expect-error - invalid method
44+
builder.route({ method: 'HE' })
45+
// @ts-expect-error - invalid path
46+
builder.route({ method: 'GET', path: '' })
47+
})
48+
49+
const schema = z.object({
50+
value: z.string(),
51+
})
52+
53+
it('input', () => {
54+
expectTypeOf(builder.input(schema)).toEqualTypeOf<
55+
DecoratedContractProcedure<typeof schema, undefined>
56+
>()
57+
58+
expectTypeOf(builder.input(schema, { value: 'example' })).toEqualTypeOf<
59+
DecoratedContractProcedure<typeof schema, undefined>
60+
>()
61+
62+
// @ts-expect-error - invalid schema
63+
builder.input({})
64+
65+
// @ts-expect-error - invalid example
66+
builder.input(schema, { })
67+
})
68+
69+
it('output', () => {
70+
expectTypeOf(builder.output(schema)).toEqualTypeOf<
71+
DecoratedContractProcedure<undefined, typeof schema>
72+
>()
73+
74+
expectTypeOf(builder.output(schema, { value: 'example' })).toEqualTypeOf<
75+
DecoratedContractProcedure<undefined, typeof schema>
76+
>()
77+
78+
// @ts-expect-error - invalid schema
79+
builder.output({})
80+
81+
// @ts-expect-error - invalid example
82+
builder.output(schema, {})
83+
})
84+
})
85+
86+
describe('to router', () => {
87+
const router = {
88+
a: {
89+
b: {
90+
c: new ContractProcedure({ InputSchema: undefined, OutputSchema: undefined }),
91+
},
92+
},
93+
}
94+
95+
const emptyRouter = {
96+
97+
}
98+
99+
const invalidRouter = {
100+
a: 1,
101+
}
102+
103+
it('router', () => {
104+
expectTypeOf(builder.router(router)).toEqualTypeOf<typeof router>()
105+
expectTypeOf(builder.router(emptyRouter)).toEqualTypeOf<typeof emptyRouter>()
106+
107+
// @ts-expect-error - invalid router
108+
builder.router(invalidRouter)
109+
})
110+
})

0 commit comments

Comments
 (0)