orpc-fn 0.1.0 → 0.2.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/README.md +48 -32
- package/dist/assemble-procedure.d.ts +13 -0
- package/dist/assemble-procedure.d.ts.map +1 -0
- package/dist/assemble-procedure.js +29 -0
- package/dist/assemble-procedure.js.map +1 -0
- package/dist/bound-call.d.ts +1 -1
- package/dist/bound-call.d.ts.map +1 -1
- package/dist/bound-call.js.map +1 -1
- package/dist/client.d.ts +4 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -2
- package/dist/client.js.map +1 -1
- package/dist/compatibility.d.ts +16 -0
- package/dist/compatibility.d.ts.map +1 -0
- package/dist/compatibility.js +4 -0
- package/dist/compatibility.js.map +1 -0
- package/dist/create-fn.d.ts +30 -34
- package/dist/create-fn.d.ts.map +1 -1
- package/dist/create-fn.js +177 -121
- package/dist/create-fn.js.map +1 -1
- package/dist/expo.d.ts +11 -25
- package/dist/expo.d.ts.map +1 -1
- package/dist/expo.js +25 -30
- package/dist/expo.js.map +1 -1
- package/dist/hono.d.ts +12 -4
- package/dist/hono.d.ts.map +1 -1
- package/dist/hono.js +29 -12
- package/dist/hono.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/json-schema.d.ts +2 -0
- package/dist/json-schema.d.ts.map +1 -1
- package/dist/json-schema.js +11 -0
- package/dist/json-schema.js.map +1 -1
- package/dist/live/codec.d.ts +6 -1
- package/dist/live/codec.d.ts.map +1 -1
- package/dist/live/codec.js +14 -3
- package/dist/live/codec.js.map +1 -1
- package/dist/live/fn-live.d.ts +63 -22
- package/dist/live/fn-live.d.ts.map +1 -1
- package/dist/live/fn-live.js +98 -16
- package/dist/live/fn-live.js.map +1 -1
- package/dist/live/index.d.ts +1 -1
- package/dist/live/index.d.ts.map +1 -1
- package/dist/live/index.js.map +1 -1
- package/dist/live/ioredis.d.ts +4 -1
- package/dist/live/ioredis.d.ts.map +1 -1
- package/dist/live/ioredis.js +21 -2
- package/dist/live/ioredis.js.map +1 -1
- package/dist/live/lifecycle.d.ts +7 -0
- package/dist/live/lifecycle.d.ts.map +1 -0
- package/dist/live/lifecycle.js +36 -0
- package/dist/live/lifecycle.js.map +1 -0
- package/dist/live/memory.d.ts.map +1 -1
- package/dist/live/memory.js +4 -0
- package/dist/live/memory.js.map +1 -1
- package/dist/live/pub-sub.d.ts +76 -12
- package/dist/live/pub-sub.d.ts.map +1 -1
- package/dist/live/pub-sub.js +429 -125
- package/dist/live/pub-sub.js.map +1 -1
- package/dist/live/redis-bun.d.ts +1 -1
- package/dist/live/redis-bun.d.ts.map +1 -1
- package/dist/live/redis-bun.js +3 -5
- package/dist/live/redis-bun.js.map +1 -1
- package/dist/live/redis.d.ts +2 -2
- package/dist/live/redis.d.ts.map +1 -1
- package/dist/live/redis.js +36 -9
- package/dist/live/redis.js.map +1 -1
- package/dist/live/transport.d.ts +5 -1
- package/dist/live/transport.d.ts.map +1 -1
- package/dist/live/transport.js.map +1 -1
- package/dist/logger.d.ts +2 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +21 -0
- package/dist/logger.js.map +1 -1
- package/dist/mastra.d.ts.map +1 -1
- package/dist/mastra.js +16 -7
- package/dist/mastra.js.map +1 -1
- package/dist/mcp/sdk.d.ts +20 -0
- package/dist/mcp/sdk.d.ts.map +1 -0
- package/dist/mcp/sdk.js +82 -0
- package/dist/mcp/sdk.js.map +1 -0
- package/dist/mcp.d.ts +3 -2
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +22 -7
- package/dist/mcp.js.map +1 -1
- package/dist/meta.d.ts +15 -7
- package/dist/meta.d.ts.map +1 -1
- package/dist/meta.js +9 -2
- package/dist/meta.js.map +1 -1
- package/dist/otel.d.ts.map +1 -1
- package/dist/otel.js +77 -6
- package/dist/otel.js.map +1 -1
- package/dist/route-options.d.ts +22 -0
- package/dist/route-options.d.ts.map +1 -0
- package/dist/route-options.js +67 -0
- package/dist/route-options.js.map +1 -0
- package/dist/router.d.ts +5 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +22 -0
- package/dist/router.js.map +1 -1
- package/dist/timing.d.ts +8 -0
- package/dist/timing.d.ts.map +1 -0
- package/dist/timing.js +4 -0
- package/dist/timing.js.map +1 -0
- package/dist/types.d.ts +86 -41
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +6 -2
- package/dist/types.js.map +1 -1
- package/package.json +15 -3
- package/src/assemble-procedure.ts +42 -0
- package/src/bound-call.ts +4 -3
- package/src/client.ts +9 -2
- package/src/compatibility.ts +39 -0
- package/src/create-fn.ts +297 -183
- package/src/expo.ts +49 -44
- package/src/hono.ts +36 -17
- package/src/index.ts +6 -2
- package/src/json-schema.ts +16 -0
- package/src/live/codec.ts +22 -3
- package/src/live/fn-live.ts +244 -69
- package/src/live/index.ts +2 -0
- package/src/live/ioredis.ts +22 -2
- package/src/live/lifecycle.ts +44 -0
- package/src/live/memory.ts +4 -0
- package/src/live/pub-sub.ts +608 -166
- package/src/live/redis-bun.ts +7 -5
- package/src/live/redis.ts +37 -11
- package/src/live/transport.ts +4 -1
- package/src/logger.ts +20 -0
- package/src/mastra.ts +15 -7
- package/src/mcp/sdk.ts +118 -0
- package/src/mcp.ts +48 -20
- package/src/meta.ts +40 -24
- package/src/otel.ts +77 -10
- package/src/route-options.ts +100 -0
- package/src/router.ts +39 -1
- package/src/timing.ts +5 -0
- package/src/types.ts +204 -62
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AnySchema,
|
|
3
|
+
type ErrorMap,
|
|
4
|
+
getEventIteratorSchemaDetails
|
|
5
|
+
} from '@orpc/contract'
|
|
6
|
+
import { ROUTE_OPTION_KEYS } from './types.js'
|
|
7
|
+
|
|
8
|
+
type Resolver = (params: {
|
|
9
|
+
input: unknown
|
|
10
|
+
context: unknown
|
|
11
|
+
signal: AbortSignal | undefined
|
|
12
|
+
}) => unknown
|
|
13
|
+
function schemaOption(schema: unknown, name: string): AnySchema | undefined {
|
|
14
|
+
if (schema === undefined) return undefined
|
|
15
|
+
if (
|
|
16
|
+
typeof schema !== 'object' ||
|
|
17
|
+
schema === null ||
|
|
18
|
+
!('~standard' in schema) ||
|
|
19
|
+
typeof (schema as AnySchema)['~standard']?.validate !== 'function'
|
|
20
|
+
)
|
|
21
|
+
throw new TypeError(`orpc-fn: ${name} must be a Standard Schema`)
|
|
22
|
+
return schema as AnySchema
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Runtime validation for JavaScript callers and spread route options. */
|
|
26
|
+
export function normalizeRoute(
|
|
27
|
+
options: Record<string, unknown>,
|
|
28
|
+
isGuard: (key: string) => boolean
|
|
29
|
+
) {
|
|
30
|
+
const {
|
|
31
|
+
handler,
|
|
32
|
+
input: rawInput,
|
|
33
|
+
output: rawOutput,
|
|
34
|
+
name,
|
|
35
|
+
procedure,
|
|
36
|
+
meta: appMeta = {},
|
|
37
|
+
stream,
|
|
38
|
+
errors,
|
|
39
|
+
guardResolvers = {},
|
|
40
|
+
...rest
|
|
41
|
+
} = options
|
|
42
|
+
if (typeof name !== 'string' || !name || typeof handler !== 'function')
|
|
43
|
+
throw new TypeError('orpc-fn: routes require a name and handler')
|
|
44
|
+
if (procedure !== undefined && typeof procedure !== 'string')
|
|
45
|
+
throw new TypeError('orpc-fn: procedure must be a configured key')
|
|
46
|
+
if (typeof appMeta !== 'object' || appMeta === null || Array.isArray(appMeta))
|
|
47
|
+
throw new TypeError('orpc-fn: meta must be an object')
|
|
48
|
+
if (stream !== undefined && typeof stream !== 'boolean')
|
|
49
|
+
throw new TypeError('orpc-fn: stream must be boolean')
|
|
50
|
+
if (
|
|
51
|
+
typeof guardResolvers !== 'object' ||
|
|
52
|
+
guardResolvers === null ||
|
|
53
|
+
Array.isArray(guardResolvers)
|
|
54
|
+
)
|
|
55
|
+
throw new TypeError('orpc-fn: guardResolvers must be an object')
|
|
56
|
+
const input = schemaOption(rawInput, 'input')
|
|
57
|
+
const output = schemaOption(rawOutput, 'output')
|
|
58
|
+
const route: Record<string, unknown> = {}
|
|
59
|
+
const guardChecks: Array<[string, unknown]> = []
|
|
60
|
+
for (const [option, value] of Object.entries(rest)) {
|
|
61
|
+
if ((ROUTE_OPTION_KEYS as readonly string[]).includes(option))
|
|
62
|
+
route[option] = value
|
|
63
|
+
else if (isGuard(option)) {
|
|
64
|
+
if (value !== undefined) guardChecks.push([option, value])
|
|
65
|
+
} else
|
|
66
|
+
throw new TypeError(
|
|
67
|
+
`orpc-fn: unknown route option "${option}"; put application metadata in meta`
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
const iterator = getEventIteratorSchemaDetails(output) !== undefined
|
|
71
|
+
if (stream === false && iterator)
|
|
72
|
+
throw new TypeError(
|
|
73
|
+
'orpc-fn: an event iterator schema cannot declare stream: false'
|
|
74
|
+
)
|
|
75
|
+
for (const [guard, resolver] of Object.entries(guardResolvers)) {
|
|
76
|
+
if (!isGuard(guard))
|
|
77
|
+
throw new TypeError(`orpc-fn: unknown guard resolver "${guard}"`)
|
|
78
|
+
if (typeof resolver !== 'function')
|
|
79
|
+
throw new TypeError(
|
|
80
|
+
`orpc-fn: guard resolver "${guard}" must be a function`
|
|
81
|
+
)
|
|
82
|
+
if (guardChecks.some(([key]) => key === guard))
|
|
83
|
+
throw new TypeError(
|
|
84
|
+
`orpc-fn: guard "${guard}" has both a value and resolver`
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
handler: handler as (params: Record<PropertyKey, unknown>) => unknown,
|
|
89
|
+
input,
|
|
90
|
+
output,
|
|
91
|
+
name,
|
|
92
|
+
procedure,
|
|
93
|
+
meta: appMeta as Record<string, unknown>,
|
|
94
|
+
stream: stream === true || iterator,
|
|
95
|
+
errors: errors as ErrorMap | undefined,
|
|
96
|
+
guardResolvers: guardResolvers as Record<string, Resolver>,
|
|
97
|
+
route,
|
|
98
|
+
guardChecks
|
|
99
|
+
}
|
|
100
|
+
}
|
package/src/router.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
type AnyRouter,
|
|
3
|
+
type AnyProcedure,
|
|
4
|
+
resolveContractProcedures,
|
|
5
|
+
traverseContractProcedures
|
|
6
|
+
} from '@orpc/server'
|
|
7
|
+
import { readFnMeta } from './meta.js'
|
|
2
8
|
|
|
3
9
|
/**
|
|
4
10
|
* Creates a router object from routes or nested routers.
|
|
@@ -13,3 +19,35 @@ import type { AnyRouter } from '@orpc/server'
|
|
|
13
19
|
export function createRouter<T extends AnyRouter>(routes: T): Readonly<T> {
|
|
14
20
|
return routes
|
|
15
21
|
}
|
|
22
|
+
|
|
23
|
+
/** Serialize these paths to a frontend module; this helper belongs on the server. */
|
|
24
|
+
export function createStreamManifest(
|
|
25
|
+
router: AnyRouter
|
|
26
|
+
): readonly (readonly string[])[] {
|
|
27
|
+
const paths: string[][] = []
|
|
28
|
+
const unresolved = traverseContractProcedures(
|
|
29
|
+
{ router, path: [] },
|
|
30
|
+
({ contract, path }) => {
|
|
31
|
+
if (readFnMeta(contract as AnyProcedure).stream) paths.push([...path])
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
if (unresolved.length)
|
|
35
|
+
throw new TypeError(
|
|
36
|
+
'orpc-fn: lazy routers require createStreamManifestAsync'
|
|
37
|
+
)
|
|
38
|
+
return paths
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Resolve lazy routers before generating the same serializable path manifest. */
|
|
42
|
+
export async function createStreamManifestAsync(
|
|
43
|
+
router: AnyRouter
|
|
44
|
+
): Promise<readonly (readonly string[])[]> {
|
|
45
|
+
const paths: string[][] = []
|
|
46
|
+
await resolveContractProcedures(
|
|
47
|
+
{ router, path: [] },
|
|
48
|
+
({ contract, path }) => {
|
|
49
|
+
if (readFnMeta(contract as AnyProcedure).stream) paths.push([...path])
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
return paths
|
|
53
|
+
}
|
package/src/timing.ts
ADDED
package/src/types.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type {
|
|
|
3
3
|
ErrorMap,
|
|
4
4
|
InferSchemaInput,
|
|
5
5
|
InferSchemaOutput,
|
|
6
|
-
|
|
6
|
+
MergedErrorMap,
|
|
7
7
|
Schema
|
|
8
8
|
} from '@orpc/contract'
|
|
9
9
|
import type {
|
|
@@ -12,7 +12,9 @@ import type {
|
|
|
12
12
|
Procedure,
|
|
13
13
|
Route
|
|
14
14
|
} from '@orpc/server'
|
|
15
|
-
import type {
|
|
15
|
+
import type { createORPCErrorConstructorMap } from '@orpc/server'
|
|
16
|
+
import type { StoredFnMeta } from './meta.js'
|
|
17
|
+
import type { NativeErrors, NativeMeta } from './compatibility.js'
|
|
16
18
|
import type { BoundCall } from './bound-call.js'
|
|
17
19
|
import type { FnLogger } from './logger.js'
|
|
18
20
|
import type { SpanLike } from './otel.js'
|
|
@@ -93,7 +95,7 @@ export const ROUTE_OPTION_KEYS = [
|
|
|
93
95
|
] as const satisfies readonly (keyof Route)[]
|
|
94
96
|
|
|
95
97
|
/**
|
|
96
|
-
* `fn()` option names owned by the route itself. Guard
|
|
98
|
+
* `fn()` option names owned by the route itself. Guard keys must not
|
|
97
99
|
* reuse them: such an option would be read as the route option and the guard
|
|
98
100
|
* would never run.
|
|
99
101
|
*/
|
|
@@ -105,12 +107,16 @@ export const RESERVED_OPTION_KEYS = [
|
|
|
105
107
|
'output',
|
|
106
108
|
'handler',
|
|
107
109
|
'live',
|
|
108
|
-
'operationId'
|
|
110
|
+
'operationId',
|
|
111
|
+
'meta',
|
|
112
|
+
'stream',
|
|
113
|
+
'errors',
|
|
114
|
+
'guardResolvers'
|
|
109
115
|
] as const
|
|
110
116
|
|
|
111
117
|
export type ReservedOptionKey = (typeof RESERVED_OPTION_KEYS)[number]
|
|
112
118
|
|
|
113
|
-
/** @internal Builds one route from a
|
|
119
|
+
/** @internal Builds one route from a selected native procedure builder. */
|
|
114
120
|
export type BuildProcedure = (options: {
|
|
115
121
|
name: string
|
|
116
122
|
procedure: string | undefined
|
|
@@ -118,10 +124,14 @@ export type BuildProcedure = (options: {
|
|
|
118
124
|
meta: Record<string, unknown>
|
|
119
125
|
input?: AnySchema | undefined
|
|
120
126
|
output?: AnySchema | undefined
|
|
127
|
+
stream?: boolean
|
|
128
|
+
errors?: ErrorMap
|
|
121
129
|
handler: (options: {
|
|
122
130
|
input: unknown
|
|
123
|
-
context: Record<string, unknown>
|
|
131
|
+
context: Record<string | symbol, unknown>
|
|
124
132
|
signal?: AbortSignal
|
|
133
|
+
lastEventId?: string
|
|
134
|
+
errors: ReturnType<typeof createORPCErrorConstructorMap>
|
|
125
135
|
}) => unknown
|
|
126
136
|
}) => AnyProcedure
|
|
127
137
|
|
|
@@ -134,11 +144,25 @@ export type RejectKeys<T, K extends PropertyKey, TMessage extends string> = {
|
|
|
134
144
|
* Handler extras must have known keys: an index signature would claim every
|
|
135
145
|
* handler param. Intersected into `createFn`'s options to reject it.
|
|
136
146
|
*/
|
|
137
|
-
export type FiniteExtras<T> =
|
|
138
|
-
?
|
|
139
|
-
: number extends keyof T
|
|
147
|
+
export type FiniteExtras<T> = T extends unknown
|
|
148
|
+
? string extends keyof T
|
|
140
149
|
? { extras: 'orpc-fn: extras need known keys, not an index signature' }
|
|
141
|
-
:
|
|
150
|
+
: number extends keyof T
|
|
151
|
+
? { extras: 'orpc-fn: extras need known keys, not an index signature' }
|
|
152
|
+
: Extract<
|
|
153
|
+
keyof T,
|
|
154
|
+
| 'input'
|
|
155
|
+
| 'context'
|
|
156
|
+
| 'call'
|
|
157
|
+
| 'signal'
|
|
158
|
+
| 'span'
|
|
159
|
+
| 'logger'
|
|
160
|
+
| 'errors'
|
|
161
|
+
| 'lastEventId'
|
|
162
|
+
> extends never
|
|
163
|
+
? unknown
|
|
164
|
+
: { extras: 'orpc-fn: extras cannot shadow handler parameters' }
|
|
165
|
+
: never
|
|
142
166
|
|
|
143
167
|
type ProcedureOf<TBuilder> = TBuilder extends {
|
|
144
168
|
handler(handler: never): infer TProcedure
|
|
@@ -162,16 +186,8 @@ export type CurrentContextOf<TBuilder> =
|
|
|
162
186
|
? TContext
|
|
163
187
|
: never
|
|
164
188
|
|
|
165
|
-
type ErrorMapOf<TBuilder> =
|
|
166
|
-
|
|
167
|
-
'~orpc': { errorMap: infer T extends ErrorMap }
|
|
168
|
-
}
|
|
169
|
-
? T
|
|
170
|
-
: ErrorMap
|
|
171
|
-
type MetaOf<TBuilder> =
|
|
172
|
-
ProcedureOf<TBuilder> extends { '~orpc': { meta: infer T extends Meta } }
|
|
173
|
-
? T
|
|
174
|
-
: Meta
|
|
189
|
+
export type ErrorMapOf<TBuilder> = NativeErrors<ProcedureOf<TBuilder>>
|
|
190
|
+
type MetaOf<TBuilder> = NativeMeta<ProcedureOf<TBuilder>>
|
|
175
191
|
|
|
176
192
|
/**
|
|
177
193
|
* Everything `createFn` inferred from its options. Route types read from it;
|
|
@@ -181,6 +197,7 @@ export type FnDefinition = {
|
|
|
181
197
|
procedures: Record<string, BuilderLike>
|
|
182
198
|
default: string
|
|
183
199
|
extras: object
|
|
200
|
+
extrasByProcedure?: Partial<Record<string, object>>
|
|
184
201
|
guards: object
|
|
185
202
|
meta: object
|
|
186
203
|
tag: string
|
|
@@ -218,7 +235,12 @@ export type GuardParams<TContext, TMeta> = {
|
|
|
218
235
|
* `fn({...})`; the guard runs only on routes that set it. Throw (usually an
|
|
219
236
|
* `ORPCError`) to refuse the call.
|
|
220
237
|
*/
|
|
221
|
-
|
|
238
|
+
/* biome-ignore-start lint/suspicious/noConfusingVoidType: union prevents accidental value returns while accepting assertion-style guards */
|
|
239
|
+
export type Guard<TValue, TParams> = (
|
|
240
|
+
value: TValue,
|
|
241
|
+
params: TParams
|
|
242
|
+
) => MaybePromise<void | boolean>
|
|
243
|
+
/* biome-ignore-end lint/suspicious/noConfusingVoidType: assertion-style guards */
|
|
222
244
|
|
|
223
245
|
export type GuardOptions<TGuards> = {
|
|
224
246
|
[K in keyof TGuards]?: TGuards[K] extends (
|
|
@@ -229,13 +251,35 @@ export type GuardOptions<TGuards> = {
|
|
|
229
251
|
: never
|
|
230
252
|
}
|
|
231
253
|
|
|
254
|
+
/** Match object spread: scoped extras replace shared keys, preserving unions. */
|
|
255
|
+
type MergeExtras<TShared, TScoped> = TShared extends unknown
|
|
256
|
+
? TScoped extends unknown
|
|
257
|
+
? Omit<TShared, keyof TScoped> & TScoped
|
|
258
|
+
: never
|
|
259
|
+
: never
|
|
260
|
+
|
|
232
261
|
export type HandlerParams<
|
|
233
262
|
TDef extends FnDefinition,
|
|
234
263
|
TInput,
|
|
235
|
-
TKey extends ProcedureKey<TDef
|
|
264
|
+
TKey extends ProcedureKey<TDef>,
|
|
265
|
+
TErrors extends ErrorMap = Record<never, never>
|
|
236
266
|
> = DistributiveOmit<
|
|
237
|
-
|
|
238
|
-
|
|
267
|
+
MergeExtras<
|
|
268
|
+
TDef['extras'],
|
|
269
|
+
TDef extends { extrasByProcedure: infer E }
|
|
270
|
+
? TKey extends keyof E
|
|
271
|
+
? E[TKey]
|
|
272
|
+
: object
|
|
273
|
+
: object
|
|
274
|
+
>,
|
|
275
|
+
| 'input'
|
|
276
|
+
| 'context'
|
|
277
|
+
| 'call'
|
|
278
|
+
| 'signal'
|
|
279
|
+
| 'span'
|
|
280
|
+
| 'logger'
|
|
281
|
+
| 'errors'
|
|
282
|
+
| 'lastEventId'
|
|
239
283
|
> & {
|
|
240
284
|
input: TInput
|
|
241
285
|
context: FnContext<TDef, TKey>
|
|
@@ -246,6 +290,12 @@ export type HandlerParams<
|
|
|
246
290
|
/** Active OpenTelemetry span, undefined when `createFn({ otel })` is not set. */
|
|
247
291
|
span: TDef['span'] | undefined
|
|
248
292
|
logger: TDef['logger']
|
|
293
|
+
lastEventId: string | undefined
|
|
294
|
+
errors: ReturnType<
|
|
295
|
+
typeof createORPCErrorConstructorMap<
|
|
296
|
+
MergedErrorMap<ErrorMapOf<TDef['procedures'][TKey]>, TErrors>
|
|
297
|
+
>
|
|
298
|
+
>
|
|
249
299
|
}
|
|
250
300
|
|
|
251
301
|
/** Route options shared by every `fn()` overload. */
|
|
@@ -260,11 +310,22 @@ export type RouteConfig<TTag extends string = string> = Pick<
|
|
|
260
310
|
|
|
261
311
|
export type FnRouteOptions<
|
|
262
312
|
TDef extends FnDefinition,
|
|
263
|
-
TKey extends ProcedureKey<TDef
|
|
313
|
+
TKey extends ProcedureKey<TDef>,
|
|
314
|
+
TInput = unknown,
|
|
315
|
+
TErrors extends ErrorMap = Record<never, never>
|
|
264
316
|
> = RouteConfig<TDef['tag']> &
|
|
265
|
-
GuardOptions<TDef['guards']> &
|
|
266
|
-
|
|
267
|
-
|
|
317
|
+
GuardOptions<TDef['guards']> & {
|
|
318
|
+
meta?: TDef['meta']
|
|
319
|
+
stream?: boolean
|
|
320
|
+
errors?: TErrors
|
|
321
|
+
guardResolvers?: {
|
|
322
|
+
[K in keyof TDef['guards']]?: (params: {
|
|
323
|
+
input: TInput
|
|
324
|
+
context: FnContext<TDef, TKey>
|
|
325
|
+
signal: AbortSignal | undefined
|
|
326
|
+
}) => MaybePromise<GuardOptions<TDef['guards']>[K]>
|
|
327
|
+
}
|
|
328
|
+
} & ProcedureOption<TDef, TKey>
|
|
268
329
|
|
|
269
330
|
/**
|
|
270
331
|
* Which `createFn({ procedures })` builder a route uses. Optional when
|
|
@@ -281,97 +342,128 @@ export type FnProcedure<
|
|
|
281
342
|
TDef extends FnDefinition,
|
|
282
343
|
TKey extends ProcedureKey<TDef>,
|
|
283
344
|
TInputSchema extends Schema<unknown, unknown>,
|
|
284
|
-
TOutputSchema extends Schema<unknown, unknown
|
|
345
|
+
TOutputSchema extends Schema<unknown, unknown>,
|
|
346
|
+
TErrors extends ErrorMap = Record<never, never>
|
|
285
347
|
> = DecoratedProcedure<
|
|
286
348
|
InitialContextOf<TDef['procedures'][TKey]>,
|
|
287
349
|
FnContext<TDef, TKey>,
|
|
288
350
|
TInputSchema,
|
|
289
351
|
TOutputSchema,
|
|
290
|
-
ErrorMapOf<TDef['procedures'][TKey]>,
|
|
291
|
-
MetaOf<TDef['procedures'][TKey]>
|
|
352
|
+
MergedErrorMap<ErrorMapOf<TDef['procedures'][TKey]>, TErrors>,
|
|
353
|
+
MetaOf<TDef['procedures'][TKey]> & {
|
|
354
|
+
'orpc-fn': StoredFnMeta<TDef['meta'], TKey>
|
|
355
|
+
}
|
|
292
356
|
>
|
|
293
357
|
|
|
294
|
-
type InputOf<T> = [T] extends [
|
|
295
|
-
type ReturnOf<TOut, TReturn> = [TOut] extends [
|
|
296
|
-
?
|
|
358
|
+
type InputOf<T> = [T] extends [AnySchema] ? InferSchemaOutput<T> : unknown
|
|
359
|
+
type ReturnOf<TOut, TReturn> = [TOut] extends [AnySchema]
|
|
360
|
+
? InferSchemaInput<TOut>
|
|
297
361
|
: TReturn
|
|
298
362
|
|
|
299
363
|
/** `fn()`: four overloads for input/output schema × inferred. */
|
|
300
364
|
export interface Fn<TDef extends FnDefinition> {
|
|
301
365
|
// 1. With input + output schema
|
|
302
366
|
<
|
|
303
|
-
TInput extends
|
|
304
|
-
TOut extends
|
|
305
|
-
TKey extends ProcedureKey<TDef> = TDef['default']
|
|
367
|
+
TInput extends AnySchema,
|
|
368
|
+
TOut extends AnySchema,
|
|
369
|
+
TKey extends ProcedureKey<TDef> = TDef['default'],
|
|
370
|
+
TErrors extends ErrorMap = Record<never, never>
|
|
306
371
|
>(
|
|
307
372
|
options: {
|
|
308
373
|
input: TInput
|
|
309
374
|
output: TOut
|
|
310
375
|
// The output schema parses what the handler returns: its input type.
|
|
311
376
|
handler: (
|
|
312
|
-
params: HandlerParams<
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
377
|
+
params: HandlerParams<
|
|
378
|
+
TDef,
|
|
379
|
+
InferSchemaOutput<TInput>,
|
|
380
|
+
NoInfer<TKey>,
|
|
381
|
+
TErrors
|
|
382
|
+
>
|
|
383
|
+
) => MaybePromise<InferSchemaInput<TOut>>
|
|
384
|
+
} & FnRouteOptions<TDef, TKey, InferSchemaOutput<TInput>, TErrors>
|
|
385
|
+
): FnProcedure<TDef, TKey, TInput, TOut, TErrors>
|
|
316
386
|
|
|
317
387
|
// 2. With input, inferred output
|
|
318
388
|
<
|
|
319
|
-
TInput extends
|
|
389
|
+
TInput extends AnySchema,
|
|
320
390
|
TReturn,
|
|
321
|
-
TKey extends ProcedureKey<TDef> = TDef['default']
|
|
391
|
+
TKey extends ProcedureKey<TDef> = TDef['default'],
|
|
392
|
+
TErrors extends ErrorMap = Record<never, never>
|
|
322
393
|
>(
|
|
323
394
|
options: {
|
|
324
395
|
input: TInput
|
|
325
396
|
output?: undefined
|
|
326
397
|
handler: (
|
|
327
|
-
params: HandlerParams<
|
|
398
|
+
params: HandlerParams<
|
|
399
|
+
TDef,
|
|
400
|
+
InferSchemaOutput<TInput>,
|
|
401
|
+
NoInfer<TKey>,
|
|
402
|
+
TErrors
|
|
403
|
+
>
|
|
328
404
|
) => MaybePromise<TReturn>
|
|
329
|
-
} & FnRouteOptions<TDef, TKey>
|
|
330
|
-
): FnProcedure<TDef, TKey, TInput, Schema<TReturn, TReturn
|
|
405
|
+
} & FnRouteOptions<TDef, TKey, InferSchemaOutput<TInput>, TErrors>
|
|
406
|
+
): FnProcedure<TDef, TKey, TInput, Schema<TReturn, TReturn>, TErrors>
|
|
331
407
|
|
|
332
408
|
// 3. No input, with output schema
|
|
333
|
-
<
|
|
409
|
+
<
|
|
410
|
+
TOut extends AnySchema,
|
|
411
|
+
TKey extends ProcedureKey<TDef> = TDef['default'],
|
|
412
|
+
TErrors extends ErrorMap = Record<never, never>
|
|
413
|
+
>(
|
|
334
414
|
options: {
|
|
335
415
|
input?: undefined
|
|
336
416
|
output: TOut
|
|
337
417
|
handler: (
|
|
338
|
-
params: HandlerParams<TDef, unknown, NoInfer<TKey
|
|
339
|
-
) => MaybePromise<
|
|
340
|
-
} & FnRouteOptions<TDef, TKey>
|
|
341
|
-
): FnProcedure<TDef, TKey, Schema<unknown, unknown>, TOut>
|
|
418
|
+
params: HandlerParams<TDef, unknown, NoInfer<TKey>, TErrors>
|
|
419
|
+
) => MaybePromise<InferSchemaInput<TOut>>
|
|
420
|
+
} & FnRouteOptions<TDef, TKey, unknown, TErrors>
|
|
421
|
+
): FnProcedure<TDef, TKey, Schema<unknown, unknown>, TOut, TErrors>
|
|
342
422
|
|
|
343
423
|
// 4. No input, inferred output
|
|
344
|
-
<
|
|
424
|
+
<
|
|
425
|
+
TReturn,
|
|
426
|
+
TKey extends ProcedureKey<TDef> = TDef['default'],
|
|
427
|
+
TErrors extends ErrorMap = Record<never, never>
|
|
428
|
+
>(
|
|
345
429
|
options: {
|
|
346
430
|
input?: undefined
|
|
347
431
|
output?: undefined
|
|
348
432
|
handler: (
|
|
349
|
-
params: HandlerParams<TDef, unknown, NoInfer<TKey
|
|
433
|
+
params: HandlerParams<TDef, unknown, NoInfer<TKey>, TErrors>
|
|
350
434
|
) => MaybePromise<TReturn>
|
|
351
|
-
} & FnRouteOptions<TDef, TKey>
|
|
352
|
-
): FnProcedure<
|
|
435
|
+
} & FnRouteOptions<TDef, TKey, unknown, TErrors>
|
|
436
|
+
): FnProcedure<
|
|
437
|
+
TDef,
|
|
438
|
+
TKey,
|
|
439
|
+
Schema<unknown, unknown>,
|
|
440
|
+
Schema<TReturn, TReturn>,
|
|
441
|
+
TErrors
|
|
442
|
+
>
|
|
353
443
|
|
|
354
444
|
// 5. Never matches valid code first; it only words the type errors, which
|
|
355
445
|
// TypeScript reports against the last overload.
|
|
356
446
|
<
|
|
357
447
|
TReturn,
|
|
358
|
-
TInput extends
|
|
359
|
-
TOut extends
|
|
360
|
-
TKey extends ProcedureKey<TDef> = TDef['default']
|
|
448
|
+
TInput extends AnySchema | undefined = undefined,
|
|
449
|
+
TOut extends AnySchema | undefined = undefined,
|
|
450
|
+
TKey extends ProcedureKey<TDef> = TDef['default'],
|
|
451
|
+
TErrors extends ErrorMap = Record<never, never>
|
|
361
452
|
>(
|
|
362
453
|
options: {
|
|
363
454
|
input?: TInput
|
|
364
455
|
output?: TOut
|
|
365
456
|
// The output schema parses what the handler returns: its input type.
|
|
366
457
|
handler: (
|
|
367
|
-
params: HandlerParams<TDef, InputOf<TInput>, NoInfer<TKey
|
|
458
|
+
params: HandlerParams<TDef, InputOf<TInput>, NoInfer<TKey>, TErrors>
|
|
368
459
|
) => MaybePromise<ReturnOf<TOut, TReturn>>
|
|
369
|
-
} & FnRouteOptions<TDef, TKey>
|
|
460
|
+
} & FnRouteOptions<TDef, TKey, InputOf<TInput>, TErrors>
|
|
370
461
|
): FnProcedure<
|
|
371
462
|
TDef,
|
|
372
463
|
TKey,
|
|
373
|
-
[TInput] extends [
|
|
374
|
-
[TOut] extends [
|
|
464
|
+
[TInput] extends [AnySchema] ? TInput : Schema<unknown, unknown>,
|
|
465
|
+
[TOut] extends [AnySchema] ? TOut : Schema<TReturn, TReturn>,
|
|
466
|
+
TErrors
|
|
375
467
|
>
|
|
376
468
|
}
|
|
377
469
|
|
|
@@ -381,9 +473,59 @@ export type FnCompletedEvent<TContext = unknown, TSpan = SpanLike> = {
|
|
|
381
473
|
procedure: string
|
|
382
474
|
durationMs: number
|
|
383
475
|
success: boolean
|
|
476
|
+
/** False when authentication or input validation prevented handler execution. */
|
|
477
|
+
handlerStarted: boolean
|
|
384
478
|
error: unknown
|
|
385
479
|
input: unknown
|
|
386
480
|
context: TContext
|
|
387
481
|
span: TSpan | undefined
|
|
388
482
|
meta: Record<string, unknown>
|
|
389
483
|
}
|
|
484
|
+
|
|
485
|
+
/** Factory hooks retain the builder key/context relationship. */
|
|
486
|
+
export type ProcedureHookParams<
|
|
487
|
+
TProcedures extends Record<string, BuilderLike>,
|
|
488
|
+
TExtra = object
|
|
489
|
+
> = {
|
|
490
|
+
[K in keyof TProcedures & string]: TExtra & {
|
|
491
|
+
procedure: K
|
|
492
|
+
context: CurrentContextOf<TProcedures[K]>
|
|
493
|
+
}
|
|
494
|
+
}[keyof TProcedures & string]
|
|
495
|
+
|
|
496
|
+
export type CompletedHookEvent<
|
|
497
|
+
TProcedures extends Record<string, BuilderLike>,
|
|
498
|
+
TSpan
|
|
499
|
+
> = {
|
|
500
|
+
[K in keyof TProcedures & string]: Omit<
|
|
501
|
+
FnCompletedEvent<unknown, TSpan>,
|
|
502
|
+
'context' | 'procedure' | 'handlerStarted'
|
|
503
|
+
> & { procedure: K } & (
|
|
504
|
+
| { handlerStarted: true; context: CurrentContextOf<TProcedures[K]> }
|
|
505
|
+
| { handlerStarted: false; context: InitialContextOf<TProcedures[K]> }
|
|
506
|
+
)
|
|
507
|
+
}[keyof TProcedures & string]
|
|
508
|
+
|
|
509
|
+
export type FiniteScopedExtras<T> = {
|
|
510
|
+
[K in keyof T]: string extends keyof T[K]
|
|
511
|
+
? K
|
|
512
|
+
: number extends keyof T[K]
|
|
513
|
+
? K
|
|
514
|
+
: Extract<
|
|
515
|
+
keyof T[K],
|
|
516
|
+
| 'input'
|
|
517
|
+
| 'context'
|
|
518
|
+
| 'call'
|
|
519
|
+
| 'signal'
|
|
520
|
+
| 'span'
|
|
521
|
+
| 'logger'
|
|
522
|
+
| 'errors'
|
|
523
|
+
| 'lastEventId'
|
|
524
|
+
> extends never
|
|
525
|
+
? never
|
|
526
|
+
: K
|
|
527
|
+
}[keyof T] extends never
|
|
528
|
+
? unknown
|
|
529
|
+
: {
|
|
530
|
+
extrasByProcedure: 'orpc-fn: scoped extras need finite keys and cannot shadow handler parameters'
|
|
531
|
+
}
|