@orpc/client 0.0.0-next.b15d206 → 0.0.0-next.b2e67f7

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.
@@ -1,245 +0,0 @@
1
- import { ORPCError, os } from '@orpc/server'
2
- import { createFetchHandler } from '@orpc/server/fetch'
3
- import { z } from 'zod'
4
- import { createProcedureClient } from './procedure'
5
-
6
- describe('createProcedureClient', () => {
7
- const schema = z.object({
8
- value: z.string(),
9
- })
10
- const ping = os.input(schema).func((_, __, { path }) => path)
11
- const router = os.router({
12
- ping,
13
- nested: {
14
- ping,
15
- },
16
- })
17
- const handler = createFetchHandler({
18
- router,
19
- })
20
- const orpcFetch: typeof fetch = async (...args) => {
21
- const request = new Request(...args)
22
- const response = await handler({
23
- prefix: '/orpc',
24
- request,
25
- context: {},
26
- })
27
- return response
28
- }
29
-
30
- it('types', () => {
31
- const schema = z.object({
32
- value: z.string(),
33
- })
34
- const client = createProcedureClient<
35
- typeof schema,
36
- undefined,
37
- { age: number }
38
- >({} as any)
39
-
40
- expectTypeOf(client).toEqualTypeOf<
41
- (input: { value: string }) => Promise<{ age: number }>
42
- >()
43
-
44
- const client2 = createProcedureClient<
45
- undefined,
46
- typeof schema,
47
- { value: string }
48
- >({} as any)
49
-
50
- expectTypeOf(client2).toEqualTypeOf<
51
- (input: unknown) => Promise<{ value: string }>
52
- >()
53
- })
54
-
55
- it('simple', async () => {
56
- const client = createProcedureClient({
57
- baseURL: 'http://localhost:3000/orpc',
58
- fetch: orpcFetch,
59
- path: ['ping'],
60
- })
61
-
62
- const result = await client({ value: 'hello' })
63
-
64
- expect(result).toEqual(['ping'])
65
-
66
- const client2 = createProcedureClient({
67
- baseURL: 'http://localhost:3000/orpc',
68
- fetch: orpcFetch,
69
- path: ['nested', 'ping'],
70
- })
71
-
72
- const result2 = await client2({ value: 'hello' })
73
-
74
- expect(result2).toEqual(['nested', 'ping'])
75
- })
76
-
77
- it('on known error', () => {
78
- const client = createProcedureClient({
79
- baseURL: 'http://localhost:3000/orpc',
80
- fetch: orpcFetch,
81
- path: ['ping'],
82
- })
83
-
84
- expect(client({ value: {} })).rejects.toThrowError(
85
- 'Validation input failed',
86
- )
87
- })
88
-
89
- it('on unknown error', () => {
90
- const orpcFetch: typeof fetch = async () => {
91
- return new Response(JSON.stringify({}), {
92
- status: 400,
93
- headers: {
94
- 'Content-Type': 'application/json',
95
- },
96
- })
97
- }
98
-
99
- const client = createProcedureClient({
100
- baseURL: 'http://localhost:3000/orpc',
101
- fetch: orpcFetch,
102
- path: ['ping'],
103
- })
104
-
105
- expect(client({ value: 'hello' })).rejects.toThrowError(
106
- 'Cannot parse response.',
107
- )
108
- })
109
-
110
- it('transformer', async () => {
111
- const router = os.router({
112
- ping: os
113
- .input(z.object({ value: z.date() }))
114
- .func(input => input.value),
115
- })
116
-
117
- const handler = createFetchHandler({
118
- router,
119
- })
120
-
121
- const client = createProcedureClient({
122
- path: ['ping'],
123
- baseURL: 'http://localhost:3000/orpc',
124
- fetch: (...args) => {
125
- const request = new Request(...args)
126
- return handler({
127
- prefix: '/orpc',
128
- request,
129
- context: {},
130
- })
131
- },
132
- })
133
-
134
- const now = new Date()
135
- expect(await client({ value: now })).toEqual(now)
136
- })
137
-
138
- it('error include data', async () => {
139
- const router = os.router({
140
- ping: os.func((input) => {
141
- throw new ORPCError({
142
- code: 'BAD_GATEWAY',
143
- data: {
144
- value: 'from error',
145
- },
146
- })
147
- }),
148
- })
149
-
150
- const handler = createFetchHandler({
151
- router,
152
- })
153
-
154
- const client = createProcedureClient({
155
- path: ['ping'],
156
- baseURL: 'http://localhost:3000/orpc',
157
- fetch: (...args) => {
158
- const request = new Request(...args)
159
- return handler({
160
- prefix: '/orpc',
161
- request,
162
- context: {},
163
- })
164
- },
165
- })
166
-
167
- let error: any
168
- try {
169
- await client(undefined)
170
- }
171
- catch (e) {
172
- error = e
173
- }
174
-
175
- expect(error).toBeInstanceOf(ORPCError)
176
- expect(error.code).toEqual('BAD_GATEWAY')
177
- expect(error.data).toEqual({ value: 'from error' })
178
- })
179
- })
180
-
181
- describe('upload file', () => {
182
- const router = os.router({
183
- signal: os.input(z.instanceof(Blob)).func((input) => {
184
- return input
185
- }),
186
- multiple: os
187
- .input(
188
- z.object({ first: z.instanceof(Blob), second: z.instanceof(Blob) }),
189
- )
190
- .func((input) => {
191
- return input
192
- }),
193
- })
194
-
195
- const handler = createFetchHandler({ router })
196
-
197
- const orpcFetch: typeof fetch = async (...args) => {
198
- const request = new Request(...args)
199
- const response = await handler({
200
- prefix: '/orpc',
201
- request,
202
- context: {},
203
- })
204
- return response
205
- }
206
-
207
- const blob1 = new Blob(['hello'], { type: 'text/plain;charset=utf-8' })
208
- const blob2 = new Blob(['"world"'], { type: 'image/png' })
209
- const blob3 = new Blob(['unnoq'], { type: 'application/octet-stream' })
210
-
211
- it('single file', async () => {
212
- const client = createProcedureClient({
213
- baseURL: 'http://localhost:3000/orpc',
214
- fetch: orpcFetch,
215
- path: ['signal'],
216
- })
217
-
218
- const output = await client(blob1)
219
-
220
- expect(output).toBeInstanceOf(Blob)
221
- expect(output.type).toBe('text/plain;charset=utf-8')
222
- expect(await output.text()).toBe('hello')
223
- })
224
-
225
- it('multiple file', async () => {
226
- const client = createProcedureClient({
227
- baseURL: 'http://localhost:3000/orpc',
228
- fetch: orpcFetch,
229
- path: ['multiple'],
230
- })
231
-
232
- const output = await client({ first: blob3, second: blob2 })
233
-
234
- const file0 = output.first
235
- const file1 = output.second
236
-
237
- expect(file0).toBeInstanceOf(Blob)
238
- expect(file0.type).toBe('application/octet-stream')
239
- expect(await file0.text()).toBe('unnoq')
240
-
241
- expect(file1).toBeInstanceOf(Blob)
242
- expect(file1.type).toBe('image/png')
243
- expect(await file1.text()).toBe('"world"')
244
- })
245
- })
package/src/procedure.ts DELETED
@@ -1,108 +0,0 @@
1
- /// <reference lib="dom" />
2
- /// <reference lib="dom.iterable" />
3
-
4
- import type { Promisable } from '@orpc/shared'
5
- import {
6
- ORPC_HEADER,
7
- ORPC_HEADER_VALUE,
8
- type Schema,
9
- type SchemaInput,
10
- type SchemaOutput,
11
- } from '@orpc/contract'
12
- import { trim } from '@orpc/shared'
13
- import { ORPCError } from '@orpc/shared/error'
14
- import { ORPCDeserializer, ORPCSerializer } from '@orpc/transformer'
15
-
16
- export interface ProcedureClient<
17
- TInputSchema extends Schema,
18
- TOutputSchema extends Schema,
19
- TFuncOutput extends SchemaOutput<TOutputSchema>,
20
- > {
21
- (
22
- input: SchemaInput<TInputSchema>,
23
- ): Promise<SchemaOutput<TOutputSchema, TFuncOutput>>
24
- }
25
-
26
- export interface CreateProcedureClientOptions {
27
- /**
28
- * The base url of the server.
29
- */
30
- baseURL: string
31
-
32
- /**
33
- * The fetch function used to make the request.
34
- * @default global fetch
35
- */
36
- fetch?: typeof fetch
37
-
38
- /**
39
- * The headers used to make the request.
40
- * Invoked before the request is made.
41
- */
42
- headers?: (input: unknown) => Promisable<Headers | Record<string, string>>
43
-
44
- /**
45
- * The path of the procedure on server.
46
- */
47
- path: string[]
48
- }
49
-
50
- export function createProcedureClient<
51
- TInputSchema extends Schema,
52
- TOutputSchema extends Schema,
53
- TFuncOutput extends SchemaOutput<TOutputSchema>,
54
- >(
55
- options: CreateProcedureClientOptions,
56
- ): ProcedureClient<TInputSchema, TOutputSchema, TFuncOutput> {
57
- const serializer = new ORPCSerializer()
58
- const deserializer = new ORPCDeserializer()
59
-
60
- const client = async (input: unknown): Promise<unknown> => {
61
- const fetch_ = options.fetch ?? fetch
62
- const url = `${trim(options.baseURL, '/')}/${options.path.map(encodeURIComponent).join('/')}`
63
- let headers = await options.headers?.(input)
64
- headers = headers instanceof Headers ? headers : new Headers(headers)
65
-
66
- const { body, headers: headers_ } = serializer.serialize(input)
67
-
68
- for (const [key, value] of headers_.entries()) {
69
- headers.set(key, value)
70
- }
71
-
72
- headers.set(ORPC_HEADER, ORPC_HEADER_VALUE)
73
-
74
- const response = await fetch_(url, {
75
- method: 'POST',
76
- headers,
77
- body,
78
- })
79
-
80
- const json = await (async () => {
81
- try {
82
- return await deserializer.deserialize(response)
83
- }
84
- catch (e) {
85
- throw new ORPCError({
86
- code: 'INTERNAL_SERVER_ERROR',
87
- message: 'Cannot parse response.',
88
- cause: e,
89
- })
90
- }
91
- })()
92
-
93
- if (!response.ok) {
94
- throw (
95
- ORPCError.fromJSON(json)
96
- ?? new ORPCError({
97
- status: response.status,
98
- code: 'INTERNAL_SERVER_ERROR',
99
- message: 'Internal server error',
100
- })
101
- )
102
- }
103
-
104
- return json
105
- }
106
-
107
- return client as any
108
- }
@@ -1,148 +0,0 @@
1
- import { oc } from '@orpc/contract'
2
- import { os } from '@orpc/server'
3
- import { createFetchHandler } from '@orpc/server/fetch'
4
- import { z } from 'zod'
5
- import { createRouterClient } from './router'
6
-
7
- describe('createRouterClient', () => {
8
- const schema = z.object({
9
- value: z.string(),
10
- })
11
- const ping = os.input(schema).func((_, __, { path }) => path)
12
- const router = os.router({
13
- ping,
14
- nested: {
15
- unique: ping,
16
- },
17
- })
18
- const handler = createFetchHandler({
19
- router,
20
- })
21
- const orpcFetch: typeof fetch = async (...args) => {
22
- const request = new Request(...args)
23
- return await handler({
24
- prefix: '/orpc',
25
- request,
26
- context: {},
27
- })
28
- }
29
-
30
- it('types with contract router', () => {
31
- const schema = z.object({
32
- value: z.string(),
33
- })
34
-
35
- const ping = oc.input(schema)
36
- const pong = oc.output(schema)
37
- const peng = oc.route({})
38
- const router = oc.router({
39
- ping,
40
- pong,
41
- peng,
42
- nested: {
43
- unique: ping,
44
- },
45
- })
46
-
47
- const client = createRouterClient<typeof router>({} as any)
48
-
49
- expectTypeOf(client.ping).toEqualTypeOf<
50
- (input: { value: string }) => Promise<unknown>
51
- >()
52
- expectTypeOf(client.pong).toEqualTypeOf<
53
- (input: unknown) => Promise<{ value: string }>
54
- >()
55
- expectTypeOf(client.peng).toEqualTypeOf<
56
- (input: unknown) => Promise<unknown>
57
- >()
58
-
59
- expectTypeOf(client.nested.unique).toEqualTypeOf<
60
- (input: { value: string }) => Promise<unknown>
61
- >()
62
- })
63
-
64
- it('types with router', () => {
65
- const schema = z.object({
66
- value: z.string(),
67
- })
68
- const ping = os.input(schema).func(() => '')
69
- const pong = os.output(schema).func(() => ({ value: 'string' }))
70
- const peng = os.route({}).func(() => ({ age: 1244 }))
71
-
72
- const router = os.router({
73
- ping,
74
- pong,
75
- peng,
76
- nested: {
77
- unique: ping,
78
- },
79
- })
80
-
81
- const client = createRouterClient<typeof router>({} as any)
82
-
83
- expectTypeOf(client.ping).toEqualTypeOf<
84
- (input: { value: string }) => Promise<string>
85
- >()
86
- expectTypeOf(client.pong).toEqualTypeOf<
87
- (input: unknown) => Promise<{ value: string }>
88
- >()
89
- expectTypeOf(client.peng).toEqualTypeOf<
90
- (input: unknown) => Promise<{ age: number }>
91
- >()
92
- expectTypeOf(client.nested.unique).toEqualTypeOf<
93
- (input: { value: string }) => Promise<string>
94
- >()
95
- })
96
-
97
- it('simple', async () => {
98
- const client = createRouterClient<typeof router>({
99
- baseURL: 'http://localhost:3000/orpc',
100
- fetch: orpcFetch,
101
- })
102
-
103
- const result = await client.ping({ value: 'hello' })
104
- expect(result).toEqual(['ping'])
105
-
106
- const result2 = await client.nested.unique({ value: 'hello' })
107
- expect(result2).toEqual(['nested', 'unique'])
108
- })
109
-
110
- it('on error', () => {
111
- const client = createRouterClient<typeof router>({
112
- baseURL: 'http://localhost:3000/orpc',
113
- fetch: orpcFetch,
114
- })
115
-
116
- // @ts-expect-error - invalid input
117
- expect(client.ping({ value: {} })).rejects.toThrowError(
118
- 'Validation input failed',
119
- )
120
- })
121
-
122
- it('transformer', async () => {
123
- const router = os.router({
124
- ping: os
125
- .input(z.object({ value: z.date() }))
126
- .func(input => input.value),
127
- })
128
-
129
- const handler = createFetchHandler({
130
- router,
131
- })
132
-
133
- const client = createRouterClient<typeof router>({
134
- baseURL: 'http://localhost:3000/orpc',
135
- fetch: (...args) => {
136
- const request = new Request(...args)
137
- return handler({
138
- prefix: '/orpc',
139
- request,
140
- context: {},
141
- })
142
- },
143
- })
144
-
145
- const now = new Date()
146
- expect(await client.ping({ value: now })).toEqual(now)
147
- })
148
- })
package/src/router.ts DELETED
@@ -1,96 +0,0 @@
1
- /// <reference lib="dom" />
2
-
3
- import type {
4
- ContractProcedure,
5
- ContractRouter,
6
- SchemaOutput,
7
- } from '@orpc/contract'
8
- import type { Procedure, Router } from '@orpc/server'
9
- import type { Promisable } from '@orpc/shared'
10
- import { createProcedureClient, type ProcedureClient } from './procedure'
11
-
12
- export type RouterClientWithContractRouter<TRouter extends ContractRouter> = {
13
- [K in keyof TRouter]: TRouter[K] extends ContractProcedure<
14
- infer UInputSchema,
15
- infer UOutputSchema
16
- >
17
- ? ProcedureClient<UInputSchema, UOutputSchema, SchemaOutput<UOutputSchema>>
18
- : TRouter[K] extends ContractRouter
19
- ? RouterClientWithContractRouter<TRouter[K]>
20
- : never
21
- }
22
-
23
- export type RouterClientWithRouter<TRouter extends Router<any>> = {
24
- [K in keyof TRouter]: TRouter[K] extends Procedure<
25
- any,
26
- any,
27
- infer UInputSchema,
28
- infer UOutputSchema,
29
- infer UFuncOutput
30
- >
31
- ? ProcedureClient<UInputSchema, UOutputSchema, UFuncOutput>
32
- : TRouter[K] extends Router<any>
33
- ? RouterClientWithRouter<TRouter[K]>
34
- : never
35
- }
36
-
37
- export interface CreateRouterClientOptions {
38
- /**
39
- * The base url of the server.
40
- */
41
- baseURL: string
42
-
43
- /**
44
- * The fetch function used to make the request.
45
- * @default global fetch
46
- */
47
- fetch?: typeof fetch
48
-
49
- /**
50
- * The headers used to make the request.
51
- * Invoked before the request is made.
52
- */
53
- headers?: (input: unknown) => Promisable<Headers | Record<string, string>>
54
-
55
- /**
56
- * This used for internal purpose only.
57
- *
58
- * @internal
59
- */
60
- path?: string[]
61
- }
62
-
63
- export function createRouterClient<
64
- TRouter extends Router<any> | ContractRouter,
65
- >(
66
- options: CreateRouterClientOptions,
67
- ): TRouter extends Router<any>
68
- ? RouterClientWithRouter<TRouter>
69
- : TRouter extends ContractRouter
70
- ? RouterClientWithContractRouter<TRouter>
71
- : never {
72
- const path = options?.path ?? []
73
-
74
- const client = new Proxy(
75
- createProcedureClient({
76
- baseURL: options.baseURL,
77
- fetch: options.fetch,
78
- headers: options.headers,
79
- path,
80
- }),
81
- {
82
- get(target, key) {
83
- if (typeof key !== 'string') {
84
- return Reflect.get(target, key)
85
- }
86
-
87
- return createRouterClient({
88
- ...options,
89
- path: [...path, key],
90
- })
91
- },
92
- },
93
- )
94
-
95
- return client as any
96
- }