@orpc/server 0.0.0-next.68241f5 → 0.0.0-next.683f2ee
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 +131 -20
- package/dist/adapters/aws-lambda/index.d.mts +46 -0
- package/dist/adapters/aws-lambda/index.d.ts +46 -0
- package/dist/adapters/aws-lambda/index.mjs +40 -0
- package/dist/adapters/bun-ws/index.d.mts +36 -0
- package/dist/adapters/bun-ws/index.d.ts +36 -0
- package/dist/adapters/bun-ws/index.mjs +47 -0
- package/dist/adapters/crossws/index.d.mts +33 -0
- package/dist/adapters/crossws/index.d.ts +33 -0
- package/dist/adapters/crossws/index.mjs +45 -0
- package/dist/adapters/fastify/index.d.mts +53 -0
- package/dist/adapters/fastify/index.d.ts +53 -0
- package/dist/adapters/fastify/index.mjs +52 -0
- package/dist/adapters/fetch/index.d.mts +66 -12
- package/dist/adapters/fetch/index.d.ts +66 -12
- package/dist/adapters/fetch/index.mjs +81 -7
- package/dist/adapters/message-port/index.d.mts +57 -0
- package/dist/adapters/message-port/index.d.ts +57 -0
- package/dist/adapters/message-port/index.mjs +55 -0
- package/dist/adapters/node/index.d.mts +43 -13
- package/dist/adapters/node/index.d.ts +43 -13
- package/dist/adapters/node/index.mjs +76 -16
- package/dist/adapters/standard/index.d.mts +8 -13
- package/dist/adapters/standard/index.d.ts +8 -13
- package/dist/adapters/standard/index.mjs +5 -5
- package/dist/adapters/standard-peer/index.d.mts +18 -0
- package/dist/adapters/standard-peer/index.d.ts +18 -0
- package/dist/adapters/standard-peer/index.mjs +21 -0
- package/dist/adapters/websocket/index.d.mts +56 -0
- package/dist/adapters/websocket/index.d.ts +56 -0
- package/dist/adapters/websocket/index.mjs +67 -0
- package/dist/adapters/ws/index.d.mts +31 -0
- package/dist/adapters/ws/index.d.ts +31 -0
- package/dist/adapters/ws/index.mjs +37 -0
- package/dist/helpers/index.d.mts +149 -0
- package/dist/helpers/index.d.ts +149 -0
- package/dist/helpers/index.mjs +198 -0
- package/dist/hibernation/index.d.mts +44 -0
- package/dist/hibernation/index.d.ts +44 -0
- package/dist/hibernation/index.mjs +65 -0
- package/dist/index.d.mts +117 -143
- package/dist/index.d.ts +117 -143
- package/dist/index.mjs +56 -35
- package/dist/plugins/index.d.mts +67 -19
- package/dist/plugins/index.d.ts +67 -19
- package/dist/plugins/index.mjs +142 -66
- package/dist/shared/{server.D5fBlF9j.d.ts → server.7cEtMB30.d.ts} +11 -11
- package/dist/shared/server.B8gYOD5g.d.mts +12 -0
- package/dist/shared/{server.DG7Tamti.mjs → server.BOmqcs4W.mjs} +129 -75
- package/dist/shared/{server.DY7OKEoj.d.mts → server.BqadksTP.d.mts} +11 -11
- package/dist/shared/server.C8_sRzQB.d.mts +42 -0
- package/dist/shared/server.ChUyt5-i.d.mts +32 -0
- package/dist/shared/server.ChyoA9XY.d.ts +42 -0
- package/dist/shared/{server.BVwwTHyO.mjs → server.DZ5BIITo.mjs} +1 -1
- package/dist/shared/{server.qf03T-Xn.mjs → server.Dz5G6M6M.mjs} +87 -51
- package/dist/shared/server.EfTOZ2Q7.d.ts +12 -0
- package/dist/shared/{server.BW-nUGgA.mjs → server.TEVCLCFC.mjs} +3 -0
- package/dist/shared/{server.DPWk5pjW.d.mts → server.qKsRrdxW.d.mts} +18 -17
- package/dist/shared/{server.DPWk5pjW.d.ts → server.qKsRrdxW.d.ts} +18 -17
- package/dist/shared/server.yoEB3Fx4.d.ts +32 -0
- package/package.json +85 -11
- package/dist/shared/server.CN0534_m.d.mts +0 -18
- package/dist/shared/server.CjlA3NKP.d.ts +0 -10
- package/dist/shared/server.CuD15qZB.d.ts +0 -18
- package/dist/shared/server.DjgtLwKi.d.mts +0 -10
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { value, isAsyncIteratorObject } from '@orpc/shared';
|
|
1
|
+
import { runWithSpan, value, setSpanError, isAsyncIteratorObject, AsyncIteratorClass, clone } from '@orpc/shared';
|
|
2
|
+
import { flattenHeader } from '@orpc/standard-server';
|
|
2
3
|
import { parseBatchRequest, toBatchResponse } from '@orpc/standard-server/batch';
|
|
4
|
+
import { toFetchHeaders } from '@orpc/standard-server-fetch';
|
|
3
5
|
import { ORPCError } from '@orpc/client';
|
|
4
|
-
export { S as StrictGetMethodPlugin } from '../shared/server.
|
|
6
|
+
export { S as StrictGetMethodPlugin } from '../shared/server.TEVCLCFC.mjs';
|
|
5
7
|
import '@orpc/contract';
|
|
6
8
|
|
|
7
9
|
class BatchHandlerPlugin {
|
|
@@ -25,69 +27,82 @@ class BatchHandlerPlugin {
|
|
|
25
27
|
init(options) {
|
|
26
28
|
options.rootInterceptors ??= [];
|
|
27
29
|
options.rootInterceptors.unshift(async (options2) => {
|
|
28
|
-
|
|
30
|
+
const xHeader = flattenHeader(options2.request.headers["x-orpc-batch"]);
|
|
31
|
+
if (xHeader === void 0) {
|
|
29
32
|
return options2.next();
|
|
30
33
|
}
|
|
31
34
|
let isParsing = false;
|
|
32
35
|
try {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return options2.next({ ...options2, request: { ...mapped, body: () => Promise.resolve(mapped.body) } }).then(({ response: response2, matched }) => {
|
|
51
|
-
if (matched) {
|
|
52
|
-
if (response2.body instanceof Blob || response2.body instanceof FormData || isAsyncIteratorObject(response2.body)) {
|
|
53
|
-
return {
|
|
54
|
-
index,
|
|
55
|
-
status: 500,
|
|
56
|
-
headers: {},
|
|
57
|
-
body: "Batch responses do not support file/blob, or event-iterator. Please call this procedure separately outside of the batch request."
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
return { ...response2, index };
|
|
36
|
+
return await runWithSpan({ name: "handle_batch_request" }, async (span) => {
|
|
37
|
+
const mode = xHeader === "buffered" ? "buffered" : "streaming";
|
|
38
|
+
isParsing = true;
|
|
39
|
+
const parsed = parseBatchRequest({ ...options2.request, body: await options2.request.body() });
|
|
40
|
+
isParsing = false;
|
|
41
|
+
span?.setAttribute("batch.mode", mode);
|
|
42
|
+
span?.setAttribute("batch.size", parsed.length);
|
|
43
|
+
const maxSize = await value(this.maxSize, options2);
|
|
44
|
+
if (parsed.length > maxSize) {
|
|
45
|
+
const message = "Batch request size exceeds the maximum allowed size";
|
|
46
|
+
setSpanError(span, message);
|
|
47
|
+
return {
|
|
48
|
+
matched: true,
|
|
49
|
+
response: {
|
|
50
|
+
status: 413,
|
|
51
|
+
headers: {},
|
|
52
|
+
body: message
|
|
61
53
|
}
|
|
62
|
-
|
|
63
|
-
}).catch(() => {
|
|
64
|
-
return { index, status: 500, headers: {}, body: "Internal server error" };
|
|
65
|
-
});
|
|
54
|
+
};
|
|
66
55
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
56
|
+
const responses = parsed.map(
|
|
57
|
+
(request, index) => {
|
|
58
|
+
const mapped = this.mapRequestItem(request, options2);
|
|
59
|
+
return options2.next({ ...options2, request: { ...mapped, body: () => Promise.resolve(mapped.body) } }).then(({ response: response2, matched }) => {
|
|
60
|
+
span?.addEvent(`response.${index}.${matched ? "success" : "not_matched"}`);
|
|
61
|
+
if (matched) {
|
|
62
|
+
if (response2.body instanceof Blob || response2.body instanceof FormData || isAsyncIteratorObject(response2.body)) {
|
|
63
|
+
return {
|
|
64
|
+
index,
|
|
65
|
+
status: 500,
|
|
66
|
+
headers: {},
|
|
67
|
+
body: "Batch responses do not support file/blob, or event-iterator. Please call this procedure separately outside of the batch request."
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return { ...response2, index };
|
|
71
|
+
}
|
|
72
|
+
return { index, status: 404, headers: {}, body: "No procedure matched" };
|
|
73
|
+
}).catch((err) => {
|
|
74
|
+
Promise.reject(err);
|
|
75
|
+
return { index, status: 500, headers: {}, body: "Internal server error" };
|
|
76
|
+
});
|
|
84
77
|
}
|
|
85
|
-
|
|
78
|
+
);
|
|
79
|
+
await Promise.race(responses);
|
|
80
|
+
const status = await value(this.successStatus, responses, options2);
|
|
81
|
+
const headers = await value(this.headers, responses, options2);
|
|
82
|
+
const promises = [...responses];
|
|
83
|
+
const response = await toBatchResponse({
|
|
84
|
+
status,
|
|
85
|
+
headers,
|
|
86
|
+
mode,
|
|
87
|
+
body: new AsyncIteratorClass(
|
|
88
|
+
async () => {
|
|
89
|
+
const handling = promises.filter((p) => p !== void 0);
|
|
90
|
+
if (handling.length <= 0) {
|
|
91
|
+
return { done: true, value: void 0 };
|
|
92
|
+
}
|
|
93
|
+
const value2 = await Promise.race(handling);
|
|
94
|
+
promises[value2.index] = void 0;
|
|
95
|
+
return { done: false, value: value2 };
|
|
96
|
+
},
|
|
97
|
+
async () => {
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
matched: true,
|
|
103
|
+
response
|
|
104
|
+
};
|
|
86
105
|
});
|
|
87
|
-
return {
|
|
88
|
-
matched: true,
|
|
89
|
-
response
|
|
90
|
-
};
|
|
91
106
|
} catch (cause) {
|
|
92
107
|
if (isParsing) {
|
|
93
108
|
return {
|
|
@@ -123,13 +138,11 @@ class CORSPlugin {
|
|
|
123
138
|
resHeaders["access-control-max-age"] = this.options.maxAge.toString();
|
|
124
139
|
}
|
|
125
140
|
if (this.options.allowMethods?.length) {
|
|
126
|
-
resHeaders["access-control-allow-methods"] = this.options.allowMethods
|
|
141
|
+
resHeaders["access-control-allow-methods"] = flattenHeader(this.options.allowMethods);
|
|
127
142
|
}
|
|
128
143
|
const allowHeaders = this.options.allowHeaders ?? interceptorOptions.request.headers["access-control-request-headers"];
|
|
129
|
-
if (
|
|
130
|
-
resHeaders["access-control-allow-headers"] = allowHeaders
|
|
131
|
-
} else if (typeof allowHeaders === "string") {
|
|
132
|
-
resHeaders["access-control-allow-headers"] = allowHeaders;
|
|
144
|
+
if (typeof allowHeaders === "string" || allowHeaders?.length) {
|
|
145
|
+
resHeaders["access-control-allow-headers"] = flattenHeader(allowHeaders);
|
|
133
146
|
}
|
|
134
147
|
return {
|
|
135
148
|
matched: true,
|
|
@@ -147,7 +160,7 @@ class CORSPlugin {
|
|
|
147
160
|
if (!result.matched) {
|
|
148
161
|
return result;
|
|
149
162
|
}
|
|
150
|
-
const origin =
|
|
163
|
+
const origin = flattenHeader(interceptorOptions.request.headers.origin) ?? "";
|
|
151
164
|
const allowedOrigin = await value(this.options.origin, origin, interceptorOptions);
|
|
152
165
|
const allowedOriginArr = Array.isArray(allowedOrigin) ? allowedOrigin : [allowedOrigin];
|
|
153
166
|
if (allowedOriginArr.includes("*")) {
|
|
@@ -169,13 +182,29 @@ class CORSPlugin {
|
|
|
169
182
|
result.response.headers["access-control-allow-credentials"] = "true";
|
|
170
183
|
}
|
|
171
184
|
if (this.options.exposeHeaders?.length) {
|
|
172
|
-
result.response.headers["access-control-expose-headers"] = this.options.exposeHeaders
|
|
185
|
+
result.response.headers["access-control-expose-headers"] = flattenHeader(this.options.exposeHeaders);
|
|
173
186
|
}
|
|
174
187
|
return result;
|
|
175
188
|
});
|
|
176
189
|
}
|
|
177
190
|
}
|
|
178
191
|
|
|
192
|
+
class RequestHeadersPlugin {
|
|
193
|
+
init(options) {
|
|
194
|
+
options.rootInterceptors ??= [];
|
|
195
|
+
options.rootInterceptors.push((interceptorOptions) => {
|
|
196
|
+
const reqHeaders = interceptorOptions.context.reqHeaders ?? toFetchHeaders(interceptorOptions.request.headers);
|
|
197
|
+
return interceptorOptions.next({
|
|
198
|
+
...interceptorOptions,
|
|
199
|
+
context: {
|
|
200
|
+
...interceptorOptions.context,
|
|
201
|
+
reqHeaders
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
179
208
|
class ResponseHeadersPlugin {
|
|
180
209
|
init(options) {
|
|
181
210
|
options.rootInterceptors ??= [];
|
|
@@ -191,7 +220,7 @@ class ResponseHeadersPlugin {
|
|
|
191
220
|
if (!result.matched) {
|
|
192
221
|
return result;
|
|
193
222
|
}
|
|
194
|
-
const responseHeaders = result.response.headers;
|
|
223
|
+
const responseHeaders = clone(result.response.headers);
|
|
195
224
|
for (const [key, value] of resHeaders) {
|
|
196
225
|
if (Array.isArray(responseHeaders[key])) {
|
|
197
226
|
responseHeaders[key].push(value);
|
|
@@ -201,8 +230,55 @@ class ResponseHeadersPlugin {
|
|
|
201
230
|
responseHeaders[key] = value;
|
|
202
231
|
}
|
|
203
232
|
}
|
|
233
|
+
return {
|
|
234
|
+
...result,
|
|
235
|
+
response: {
|
|
236
|
+
...result.response,
|
|
237
|
+
headers: responseHeaders
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
class experimental_RethrowHandlerPlugin {
|
|
245
|
+
filter;
|
|
246
|
+
CONTEXT_SYMBOL = Symbol("ORPC_RETHROW_HANDLER_PLUGIN_CONTEXT");
|
|
247
|
+
constructor(options) {
|
|
248
|
+
this.filter = options.filter;
|
|
249
|
+
}
|
|
250
|
+
init(options) {
|
|
251
|
+
options.rootInterceptors ??= [];
|
|
252
|
+
options.interceptors ??= [];
|
|
253
|
+
options.rootInterceptors.push(async (options2) => {
|
|
254
|
+
const pluginContext = {};
|
|
255
|
+
const result = await options2.next({
|
|
256
|
+
...options2,
|
|
257
|
+
context: {
|
|
258
|
+
...options2.context,
|
|
259
|
+
[this.CONTEXT_SYMBOL]: pluginContext
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
if (pluginContext.error) {
|
|
263
|
+
throw pluginContext.error.value;
|
|
264
|
+
}
|
|
204
265
|
return result;
|
|
205
266
|
});
|
|
267
|
+
options.interceptors.unshift(async (options2) => {
|
|
268
|
+
const pluginContext = options2.context[this.CONTEXT_SYMBOL];
|
|
269
|
+
if (!pluginContext) {
|
|
270
|
+
throw new TypeError("[RethrowHandlerPlugin] Rethrow handler context has been corrupted or modified by another plugin or interceptor");
|
|
271
|
+
}
|
|
272
|
+
try {
|
|
273
|
+
return await options2.next();
|
|
274
|
+
} catch (error) {
|
|
275
|
+
if (value(this.filter, error, options2)) {
|
|
276
|
+
pluginContext.error = { value: error };
|
|
277
|
+
return { matched: false, response: void 0 };
|
|
278
|
+
}
|
|
279
|
+
throw error;
|
|
280
|
+
}
|
|
281
|
+
});
|
|
206
282
|
}
|
|
207
283
|
}
|
|
208
284
|
|
|
@@ -249,4 +325,4 @@ class SimpleCsrfProtectionHandlerPlugin {
|
|
|
249
325
|
}
|
|
250
326
|
}
|
|
251
327
|
|
|
252
|
-
export { BatchHandlerPlugin, CORSPlugin, ResponseHeadersPlugin, SimpleCsrfProtectionHandlerPlugin };
|
|
328
|
+
export { BatchHandlerPlugin, CORSPlugin, RequestHeadersPlugin, ResponseHeadersPlugin, SimpleCsrfProtectionHandlerPlugin, experimental_RethrowHandlerPlugin };
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { HTTPPath, ORPCError } from '@orpc/client';
|
|
2
|
-
import { Meta
|
|
3
|
-
import { Interceptor
|
|
2
|
+
import { Meta } from '@orpc/contract';
|
|
3
|
+
import { Interceptor } from '@orpc/shared';
|
|
4
4
|
import { StandardResponse, StandardLazyRequest } from '@orpc/standard-server';
|
|
5
|
-
import { C as Context,
|
|
5
|
+
import { C as Context, R as Router, b as AnyRouter, A as AnyProcedure, d as ProcedureClientInterceptorOptions } from './server.qKsRrdxW.js';
|
|
6
6
|
|
|
7
|
-
interface StandardHandlerPlugin<
|
|
7
|
+
interface StandardHandlerPlugin<T extends Context> {
|
|
8
8
|
order?: number;
|
|
9
|
-
init?(options: StandardHandlerOptions<
|
|
9
|
+
init?(options: StandardHandlerOptions<T>, router: Router<any, T>): void;
|
|
10
10
|
}
|
|
11
11
|
declare class CompositeStandardHandlerPlugin<T extends Context, TPlugin extends StandardHandlerPlugin<T>> implements StandardHandlerPlugin<T> {
|
|
12
12
|
protected readonly plugins: TPlugin[];
|
|
13
13
|
constructor(plugins?: readonly TPlugin[]);
|
|
14
|
-
init(options: StandardHandlerOptions<T>): void;
|
|
14
|
+
init(options: StandardHandlerOptions<T>, router: Router<any, T>): void;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
type StandardParams = Record<string, string>;
|
|
@@ -49,16 +49,16 @@ interface StandardHandlerOptions<TContext extends Context> {
|
|
|
49
49
|
/**
|
|
50
50
|
* Interceptors at the request level, helpful when you want catch errors
|
|
51
51
|
*/
|
|
52
|
-
interceptors?: Interceptor<StandardHandlerInterceptorOptions<TContext>, StandardHandleResult
|
|
52
|
+
interceptors?: Interceptor<StandardHandlerInterceptorOptions<TContext>, Promise<StandardHandleResult>>[];
|
|
53
53
|
/**
|
|
54
54
|
* Interceptors at the root level, helpful when you want override the request/response
|
|
55
55
|
*/
|
|
56
|
-
rootInterceptors?: Interceptor<StandardHandlerInterceptorOptions<TContext>, StandardHandleResult
|
|
56
|
+
rootInterceptors?: Interceptor<StandardHandlerInterceptorOptions<TContext>, Promise<StandardHandleResult>>[];
|
|
57
57
|
/**
|
|
58
58
|
*
|
|
59
59
|
* Interceptors for procedure client.
|
|
60
60
|
*/
|
|
61
|
-
clientInterceptors?: Interceptor<ProcedureClientInterceptorOptions<TContext, Record<never, never>, Meta>,
|
|
61
|
+
clientInterceptors?: Interceptor<ProcedureClientInterceptorOptions<TContext, Record<never, never>, Meta>, Promise<unknown>>[];
|
|
62
62
|
}
|
|
63
63
|
declare class StandardHandler<T extends Context> {
|
|
64
64
|
private readonly matcher;
|
|
@@ -70,5 +70,5 @@ declare class StandardHandler<T extends Context> {
|
|
|
70
70
|
handle(request: StandardLazyRequest, options: StandardHandleOptions<T>): Promise<StandardHandleResult>;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
export { CompositeStandardHandlerPlugin as C, StandardHandler as
|
|
74
|
-
export type {
|
|
73
|
+
export { CompositeStandardHandlerPlugin as C, StandardHandler as f };
|
|
74
|
+
export type { StandardCodec as S, StandardParams as a, StandardHandleOptions as b, StandardHandleResult as c, StandardHandlerInterceptorOptions as d, StandardHandlerOptions as e, StandardHandlerPlugin as g, StandardMatchResult as h, StandardMatcher as i };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { C as Context } from './server.qKsRrdxW.mjs';
|
|
2
|
+
import { b as StandardHandleOptions } from './server.BqadksTP.mjs';
|
|
3
|
+
|
|
4
|
+
type FriendlyStandardHandleOptions<T extends Context> = Omit<StandardHandleOptions<T>, 'context'> & (Record<never, never> extends T ? {
|
|
5
|
+
context?: T;
|
|
6
|
+
} : {
|
|
7
|
+
context: T;
|
|
8
|
+
});
|
|
9
|
+
declare function resolveFriendlyStandardHandleOptions<T extends Context>(options: FriendlyStandardHandleOptions<T>): StandardHandleOptions<T>;
|
|
10
|
+
|
|
11
|
+
export { resolveFriendlyStandardHandleOptions as r };
|
|
12
|
+
export type { FriendlyStandardHandleOptions as F };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { isContractProcedure, ValidationError, mergePrefix, mergeErrorMap, enhanceRoute } from '@orpc/contract';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { isContractProcedure, validateORPCError, ValidationError, mergePrefix, mergeErrorMap, enhanceRoute } from '@orpc/contract';
|
|
2
|
+
import { resolveMaybeOptionalOptions, toArray, value, runWithSpan, intercept, isAsyncIteratorObject, overlayProxy, asyncIteratorWithSpan } from '@orpc/shared';
|
|
3
|
+
import { ORPCError, mapEventIterator } from '@orpc/client';
|
|
4
|
+
import { HibernationEventIterator } from '@orpc/standard-server';
|
|
4
5
|
|
|
5
6
|
const LAZY_SYMBOL = Symbol("ORPC_LAZY_SYMBOL");
|
|
6
7
|
function lazy(loader, meta = {}) {
|
|
@@ -71,14 +72,15 @@ function createORPCErrorConstructorMap(errors) {
|
|
|
71
72
|
if (typeof code !== "string") {
|
|
72
73
|
return Reflect.get(target, code);
|
|
73
74
|
}
|
|
74
|
-
const item = (...
|
|
75
|
+
const item = (...rest) => {
|
|
76
|
+
const options = resolveMaybeOptionalOptions(rest);
|
|
75
77
|
const config = errors[code];
|
|
76
78
|
return new ORPCError(code, {
|
|
77
79
|
defined: Boolean(config),
|
|
78
80
|
status: config?.status,
|
|
79
|
-
message: options
|
|
80
|
-
data: options
|
|
81
|
-
cause: options
|
|
81
|
+
message: options.message ?? config?.message,
|
|
82
|
+
data: options.data,
|
|
83
|
+
cause: options.cause
|
|
82
84
|
});
|
|
83
85
|
};
|
|
84
86
|
return item;
|
|
@@ -86,54 +88,64 @@ function createORPCErrorConstructorMap(errors) {
|
|
|
86
88
|
});
|
|
87
89
|
return proxy;
|
|
88
90
|
}
|
|
89
|
-
async function validateORPCError(map, error) {
|
|
90
|
-
const { code, status, message, data, cause, defined } = error;
|
|
91
|
-
const config = map?.[error.code];
|
|
92
|
-
if (!config || fallbackORPCErrorStatus(error.code, config.status) !== error.status) {
|
|
93
|
-
return defined ? new ORPCError(code, { defined: false, status, message, data, cause }) : error;
|
|
94
|
-
}
|
|
95
|
-
if (!config.data) {
|
|
96
|
-
return defined ? error : new ORPCError(code, { defined: true, status, message, data, cause });
|
|
97
|
-
}
|
|
98
|
-
const validated = await config.data["~standard"].validate(error.data);
|
|
99
|
-
if (validated.issues) {
|
|
100
|
-
return defined ? new ORPCError(code, { defined: false, status, message, data, cause }) : error;
|
|
101
|
-
}
|
|
102
|
-
return new ORPCError(code, { defined: true, status, message, data: validated.value, cause });
|
|
103
|
-
}
|
|
104
91
|
|
|
105
92
|
function middlewareOutputFn(output) {
|
|
106
93
|
return { output, context: {} };
|
|
107
94
|
}
|
|
108
95
|
|
|
109
|
-
function createProcedureClient(lazyableProcedure, ...
|
|
96
|
+
function createProcedureClient(lazyableProcedure, ...rest) {
|
|
97
|
+
const options = resolveMaybeOptionalOptions(rest);
|
|
110
98
|
return async (...[input, callerOptions]) => {
|
|
111
|
-
const path = options
|
|
99
|
+
const path = toArray(options.path);
|
|
112
100
|
const { default: procedure } = await unlazy(lazyableProcedure);
|
|
113
101
|
const clientContext = callerOptions?.context ?? {};
|
|
114
|
-
const context = await value(options
|
|
102
|
+
const context = await value(options.context ?? {}, clientContext);
|
|
115
103
|
const errors = createORPCErrorConstructorMap(procedure["~orpc"].errorMap);
|
|
104
|
+
const validateError = async (e) => {
|
|
105
|
+
if (e instanceof ORPCError) {
|
|
106
|
+
return await validateORPCError(procedure["~orpc"].errorMap, e);
|
|
107
|
+
}
|
|
108
|
+
return e;
|
|
109
|
+
};
|
|
116
110
|
try {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
{
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
111
|
+
const output = await runWithSpan(
|
|
112
|
+
{ name: "call_procedure", signal: callerOptions?.signal },
|
|
113
|
+
(span) => {
|
|
114
|
+
span?.setAttribute("procedure.path", [...path]);
|
|
115
|
+
return intercept(
|
|
116
|
+
toArray(options.interceptors),
|
|
117
|
+
{
|
|
118
|
+
context,
|
|
119
|
+
input,
|
|
120
|
+
// input only optional when it undefinable so we can safely cast it
|
|
121
|
+
errors,
|
|
122
|
+
path,
|
|
123
|
+
procedure,
|
|
124
|
+
signal: callerOptions?.signal,
|
|
125
|
+
lastEventId: callerOptions?.lastEventId
|
|
126
|
+
},
|
|
127
|
+
(interceptorOptions) => executeProcedureInternal(interceptorOptions.procedure, interceptorOptions)
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
130
|
);
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
if (isAsyncIteratorObject(output)) {
|
|
132
|
+
if (output instanceof HibernationEventIterator) {
|
|
133
|
+
return output;
|
|
134
|
+
}
|
|
135
|
+
return overlayProxy(output, mapEventIterator(
|
|
136
|
+
asyncIteratorWithSpan(
|
|
137
|
+
{ name: "consume_event_iterator_output", signal: callerOptions?.signal },
|
|
138
|
+
output
|
|
139
|
+
),
|
|
140
|
+
{
|
|
141
|
+
value: (v) => v,
|
|
142
|
+
error: (e) => validateError(e)
|
|
143
|
+
}
|
|
144
|
+
));
|
|
134
145
|
}
|
|
135
|
-
|
|
136
|
-
|
|
146
|
+
return output;
|
|
147
|
+
} catch (e) {
|
|
148
|
+
throw await validateError(e);
|
|
137
149
|
}
|
|
138
150
|
};
|
|
139
151
|
}
|
|
@@ -142,31 +154,49 @@ async function validateInput(procedure, input) {
|
|
|
142
154
|
if (!schema) {
|
|
143
155
|
return input;
|
|
144
156
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
157
|
+
return runWithSpan(
|
|
158
|
+
{ name: "validate_input" },
|
|
159
|
+
async () => {
|
|
160
|
+
const result = await schema["~standard"].validate(input);
|
|
161
|
+
if (result.issues) {
|
|
162
|
+
throw new ORPCError("BAD_REQUEST", {
|
|
163
|
+
message: "Input validation failed",
|
|
164
|
+
data: {
|
|
165
|
+
issues: result.issues
|
|
166
|
+
},
|
|
167
|
+
cause: new ValidationError({
|
|
168
|
+
message: "Input validation failed",
|
|
169
|
+
issues: result.issues,
|
|
170
|
+
data: input
|
|
171
|
+
})
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return result.value;
|
|
175
|
+
}
|
|
176
|
+
);
|
|
156
177
|
}
|
|
157
178
|
async function validateOutput(procedure, output) {
|
|
158
179
|
const schema = procedure["~orpc"].outputSchema;
|
|
159
180
|
if (!schema) {
|
|
160
181
|
return output;
|
|
161
182
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
183
|
+
return runWithSpan(
|
|
184
|
+
{ name: "validate_output" },
|
|
185
|
+
async () => {
|
|
186
|
+
const result = await schema["~standard"].validate(output);
|
|
187
|
+
if (result.issues) {
|
|
188
|
+
throw new ORPCError("INTERNAL_SERVER_ERROR", {
|
|
189
|
+
message: "Output validation failed",
|
|
190
|
+
cause: new ValidationError({
|
|
191
|
+
message: "Output validation failed",
|
|
192
|
+
issues: result.issues,
|
|
193
|
+
data: output
|
|
194
|
+
})
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
return result.value;
|
|
198
|
+
}
|
|
199
|
+
);
|
|
170
200
|
}
|
|
171
201
|
async function executeProcedureInternal(procedure, options) {
|
|
172
202
|
const middlewares = procedure["~orpc"].middlewares;
|
|
@@ -178,17 +208,28 @@ async function executeProcedureInternal(procedure, options) {
|
|
|
178
208
|
currentInput = await validateInput(procedure, currentInput);
|
|
179
209
|
}
|
|
180
210
|
const mid = middlewares[index];
|
|
181
|
-
const output = mid ?
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
context
|
|
189
|
-
|
|
211
|
+
const output = mid ? await runWithSpan(
|
|
212
|
+
{ name: `middleware.${mid.name}`, signal: options.signal },
|
|
213
|
+
async (span) => {
|
|
214
|
+
span?.setAttribute("middleware.index", index);
|
|
215
|
+
span?.setAttribute("middleware.name", mid.name);
|
|
216
|
+
const result = await mid({
|
|
217
|
+
...options,
|
|
218
|
+
context,
|
|
219
|
+
next: async (...[nextOptions]) => {
|
|
220
|
+
const nextContext = nextOptions?.context ?? {};
|
|
221
|
+
return {
|
|
222
|
+
output: await next(index + 1, mergeCurrentContext(context, nextContext), currentInput),
|
|
223
|
+
context: nextContext
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}, currentInput, middlewareOutputFn);
|
|
227
|
+
return result.output;
|
|
190
228
|
}
|
|
191
|
-
|
|
229
|
+
) : await runWithSpan(
|
|
230
|
+
{ name: "handler", signal: options.signal },
|
|
231
|
+
() => procedure["~orpc"].handler({ ...options, context, input: currentInput })
|
|
232
|
+
);
|
|
192
233
|
if (index === outputValidationIndex) {
|
|
193
234
|
return await validateOutput(procedure, output);
|
|
194
235
|
}
|
|
@@ -222,6 +263,9 @@ function getRouter(router, path) {
|
|
|
222
263
|
if (isProcedure(current)) {
|
|
223
264
|
return void 0;
|
|
224
265
|
}
|
|
266
|
+
if (typeof current !== "object") {
|
|
267
|
+
return void 0;
|
|
268
|
+
}
|
|
225
269
|
if (!isLazy(current)) {
|
|
226
270
|
current = current[segment];
|
|
227
271
|
continue;
|
|
@@ -276,6 +320,9 @@ function enhanceRouter(router, options) {
|
|
|
276
320
|
});
|
|
277
321
|
return enhanced2;
|
|
278
322
|
}
|
|
323
|
+
if (typeof router !== "object" || router === null) {
|
|
324
|
+
return router;
|
|
325
|
+
}
|
|
279
326
|
const enhanced = {};
|
|
280
327
|
for (const key in router) {
|
|
281
328
|
enhanced[key] = enhanceRouter(router[key], options);
|
|
@@ -283,6 +330,9 @@ function enhanceRouter(router, options) {
|
|
|
283
330
|
return enhanced;
|
|
284
331
|
}
|
|
285
332
|
function traverseContractProcedures(options, callback, lazyOptions = []) {
|
|
333
|
+
if (typeof options.router !== "object" || options.router === null) {
|
|
334
|
+
return lazyOptions;
|
|
335
|
+
}
|
|
286
336
|
let currentRouter = options.router;
|
|
287
337
|
const hiddenContract = getHiddenRouterContract(options.router);
|
|
288
338
|
if (hiddenContract !== void 0) {
|
|
@@ -298,7 +348,7 @@ function traverseContractProcedures(options, callback, lazyOptions = []) {
|
|
|
298
348
|
contract: currentRouter,
|
|
299
349
|
path: options.path
|
|
300
350
|
});
|
|
301
|
-
} else {
|
|
351
|
+
} else if (typeof currentRouter === "object" && currentRouter !== null) {
|
|
302
352
|
for (const key in currentRouter) {
|
|
303
353
|
traverseContractProcedures(
|
|
304
354
|
{
|
|
@@ -329,6 +379,9 @@ async function unlazyRouter(router) {
|
|
|
329
379
|
if (isProcedure(router)) {
|
|
330
380
|
return router;
|
|
331
381
|
}
|
|
382
|
+
if (typeof router !== "object" || router === null) {
|
|
383
|
+
return router;
|
|
384
|
+
}
|
|
332
385
|
const unlazied = {};
|
|
333
386
|
for (const key in router) {
|
|
334
387
|
const item = router[key];
|
|
@@ -361,7 +414,8 @@ function createContractedProcedure(procedure, contract) {
|
|
|
361
414
|
});
|
|
362
415
|
}
|
|
363
416
|
function call(procedure, input, ...rest) {
|
|
364
|
-
|
|
417
|
+
const options = resolveMaybeOptionalOptions(rest);
|
|
418
|
+
return createProcedureClient(procedure, options)(input, options);
|
|
365
419
|
}
|
|
366
420
|
|
|
367
|
-
export { LAZY_SYMBOL as L, Procedure as P, createContractedProcedure as a, addMiddleware as b, createProcedureClient as c, isLazy as d, enhanceRouter as e, createAssertedLazyProcedure as f, getRouter as g, createORPCErrorConstructorMap as h, isProcedure as i, getLazyMeta as j, middlewareOutputFn as k, lazy as l, mergeCurrentContext as m, isStartWithMiddlewares as n, mergeMiddlewares as o, call as p, getHiddenRouterContract as q, createAccessibleLazyRouter as r, setHiddenRouterContract as s, traverseContractProcedures as t, unlazy as u,
|
|
421
|
+
export { LAZY_SYMBOL as L, Procedure as P, createContractedProcedure as a, addMiddleware as b, createProcedureClient as c, isLazy as d, enhanceRouter as e, createAssertedLazyProcedure as f, getRouter as g, createORPCErrorConstructorMap as h, isProcedure as i, getLazyMeta as j, middlewareOutputFn as k, lazy as l, mergeCurrentContext as m, isStartWithMiddlewares as n, mergeMiddlewares as o, call as p, getHiddenRouterContract as q, createAccessibleLazyRouter as r, setHiddenRouterContract as s, traverseContractProcedures as t, unlazy as u, resolveContractProcedures as v, unlazyRouter as w };
|