@orpc/openapi 1.14.6 → 2.0.0-beta.10
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 +78 -110
- package/dist/adapters/fetch/index.d.mts +20 -16
- package/dist/adapters/fetch/index.d.ts +20 -16
- package/dist/adapters/fetch/index.mjs +24 -8
- package/dist/adapters/node/index.d.mts +8 -13
- package/dist/adapters/node/index.d.ts +8 -13
- package/dist/adapters/node/index.mjs +10 -7
- package/dist/adapters/standard/index.d.mts +46 -16
- package/dist/adapters/standard/index.d.ts +46 -16
- package/dist/adapters/standard/index.mjs +9 -6
- package/dist/extensions/route.d.mts +43 -0
- package/dist/extensions/route.d.ts +43 -0
- package/dist/extensions/route.mjs +14 -0
- package/dist/helpers/index.d.mts +51 -0
- package/dist/helpers/index.d.ts +51 -0
- package/dist/helpers/index.mjs +39 -0
- package/dist/index.d.mts +111 -108
- package/dist/index.d.ts +111 -108
- package/dist/index.mjs +922 -33
- package/dist/plugins/index.d.mts +55 -51
- package/dist/plugins/index.d.ts +55 -51
- package/dist/plugins/index.mjs +147 -142
- package/dist/shared/openapi.7vgmPxca.d.ts +82 -0
- package/dist/shared/openapi.B2SK0ZAr.mjs +359 -0
- package/dist/shared/openapi.B9PQzqBn.mjs +49 -0
- package/dist/shared/openapi.BQzzr4-4.d.ts +299 -0
- package/dist/shared/openapi.BcEtAxQj.d.mts +299 -0
- package/dist/shared/openapi.Bt87OzTt.mjs +131 -0
- package/dist/shared/openapi.C7m7NAmH.d.mts +142 -0
- package/dist/shared/openapi.C7m7NAmH.d.ts +142 -0
- package/dist/shared/openapi.CTlpLuKN.mjs +318 -0
- package/dist/shared/openapi.CX6Ri5dP.d.mts +82 -0
- package/dist/shared/openapi.CYgMBSUF.d.mts +18 -0
- package/dist/shared/openapi.CYgMBSUF.d.ts +18 -0
- package/dist/shared/openapi.DmAa7YPO.mjs +275 -0
- package/package.json +30 -24
- package/dist/adapters/aws-lambda/index.d.mts +0 -20
- package/dist/adapters/aws-lambda/index.d.ts +0 -20
- package/dist/adapters/aws-lambda/index.mjs +0 -18
- package/dist/adapters/fastify/index.d.mts +0 -23
- package/dist/adapters/fastify/index.d.ts +0 -23
- package/dist/adapters/fastify/index.mjs +0 -18
- package/dist/shared/openapi.BB-W-NKv.mjs +0 -204
- package/dist/shared/openapi.BGy4N6eR.d.mts +0 -120
- package/dist/shared/openapi.BGy4N6eR.d.ts +0 -120
- package/dist/shared/openapi.BwdtJjDu.mjs +0 -878
- package/dist/shared/openapi.DwaweYRb.d.mts +0 -54
- package/dist/shared/openapi.DwaweYRb.d.ts +0 -54
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { COMMON_ERROR_STATUS_MAP } from '@orpc/client';
|
|
2
|
+
import { walkProcedureContractsSync, Procedure, unlazy, getRouter, createContractProcedure, DEFAULT_SUCCESS_STATUS, DEFAULT_ERROR_STATUS } from '@orpc/server';
|
|
3
|
+
import { value, pathToHttpPath, mergeHttpPath, normalizeHttpPath, tryDecodeURIComponent, isPlainObject, stringifyJSON, parseEmptyableJSON, isTypescriptObject, NullProtoObj } from '@orpc/shared';
|
|
4
|
+
import { parseStandardUrl, isStandardHeaders } from '@standardserver/core';
|
|
5
|
+
import { D as DEFAULT_OPENAPI_METHOD, g as getDynamicPathParams, O as OpenAPISerializer, a as DEFAULT_OPENAPI_INPUT_STRUCTURE, b as DEFAULT_OPENAPI_OUTPUT_STRUCTURE } from './openapi.DmAa7YPO.mjs';
|
|
6
|
+
import { g as getOpenAPIMeta } from './openapi.B9PQzqBn.mjs';
|
|
7
|
+
import { createRouter, addRoute, findRoute, routeToRegExp } from 'rou3';
|
|
8
|
+
|
|
9
|
+
class OpenAPIMatcher {
|
|
10
|
+
filter;
|
|
11
|
+
rootRouter;
|
|
12
|
+
tree = createRouter();
|
|
13
|
+
pendingLazyRouters = [];
|
|
14
|
+
constructor(router, options = {}) {
|
|
15
|
+
this.filter = options.filter ?? true;
|
|
16
|
+
this.rootRouter = router;
|
|
17
|
+
this.index(router);
|
|
18
|
+
}
|
|
19
|
+
index(router, path = []) {
|
|
20
|
+
const lazyResults = walkProcedureContractsSync(router, (contract, path2) => {
|
|
21
|
+
if (!value(this.filter, contract, path2)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const meta = getOpenAPIMeta(contract);
|
|
25
|
+
const method = meta?.method ?? DEFAULT_OPENAPI_METHOD;
|
|
26
|
+
const postHttpPath = meta?.path ?? pathToHttpPath(path2);
|
|
27
|
+
const openapiPath = meta?.prefix ? mergeHttpPath(meta.prefix, postHttpPath) : postHttpPath;
|
|
28
|
+
const rou3Path = toRou3Pattern(openapiPath);
|
|
29
|
+
addRoute(this.tree, method, rou3Path, {
|
|
30
|
+
path: path2,
|
|
31
|
+
contract,
|
|
32
|
+
procedure: contract instanceof Procedure ? contract : void 0
|
|
33
|
+
});
|
|
34
|
+
}, path);
|
|
35
|
+
this.pendingLazyRouters.push(...lazyResults.map((result) => {
|
|
36
|
+
const prefix = getOpenAPIMeta(result.router)?.prefix;
|
|
37
|
+
return {
|
|
38
|
+
...result,
|
|
39
|
+
matcher: prefix ? toRou3PrefixMatcher(prefix) : void 0
|
|
40
|
+
};
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
async match(method, pathname, prefix) {
|
|
44
|
+
if (prefix) {
|
|
45
|
+
if (!pathname.startsWith(prefix)) {
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
const charAfterPrefix = pathname[prefix.length];
|
|
49
|
+
if (charAfterPrefix === "/") {
|
|
50
|
+
pathname = pathname.slice(prefix.length);
|
|
51
|
+
} else if (charAfterPrefix === void 0) {
|
|
52
|
+
pathname = "/";
|
|
53
|
+
} else if (prefix[prefix.length - 1] === "/") {
|
|
54
|
+
pathname = pathname.slice(prefix.length - 1);
|
|
55
|
+
} else {
|
|
56
|
+
return void 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const result = await this.matchPathname(method, pathname);
|
|
60
|
+
if (!result && pathname.includes("%")) {
|
|
61
|
+
return this.matchPathname(method, normalizeHttpPath(pathname));
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
async matchPathname(method, pathname) {
|
|
66
|
+
await this.resolvePendingLazyRouters(pathname);
|
|
67
|
+
const match = findRoute(this.tree, method, pathname);
|
|
68
|
+
if (!match) {
|
|
69
|
+
return void 0;
|
|
70
|
+
}
|
|
71
|
+
const procedure = await this.resolveProcedure(match.data);
|
|
72
|
+
return {
|
|
73
|
+
path: match.data.path,
|
|
74
|
+
procedure,
|
|
75
|
+
params: match.params ? decodeParams(match.params) : void 0
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async resolvePendingLazyRouters(pathname) {
|
|
79
|
+
if (!this.pendingLazyRouters.length) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const stillPending = [];
|
|
83
|
+
for (const pending of this.pendingLazyRouters) {
|
|
84
|
+
if (!pending.matcher || pending.matcher.test(pathname)) {
|
|
85
|
+
const { default: router } = await unlazy(pending.router);
|
|
86
|
+
this.index(router, pending.path);
|
|
87
|
+
} else {
|
|
88
|
+
stillPending.push(pending);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
this.pendingLazyRouters = stillPending;
|
|
92
|
+
}
|
|
93
|
+
async resolveProcedure(entry) {
|
|
94
|
+
if (entry.procedure) {
|
|
95
|
+
return entry.procedure;
|
|
96
|
+
}
|
|
97
|
+
const { default: maybeProcedure } = await unlazy(getRouter(this.rootRouter, entry.path));
|
|
98
|
+
if (!(maybeProcedure instanceof Procedure)) {
|
|
99
|
+
throw new TypeError(
|
|
100
|
+
`[Contract-First] Missing or invalid implementation for procedure at path: "${entry.path.join(".")}". Ensure the procedure is correctly implemented and matches its contract.`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
entry.procedure = createContractProcedure(maybeProcedure, entry.contract);
|
|
104
|
+
return entry.procedure;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function toRou3Pattern(path) {
|
|
108
|
+
const params = getDynamicPathParams(path);
|
|
109
|
+
if (!params?.length) {
|
|
110
|
+
return path;
|
|
111
|
+
}
|
|
112
|
+
for (let i = params.length - 1; i >= 0; i--) {
|
|
113
|
+
const param = params[i];
|
|
114
|
+
const pattern = param.allowsSlash ? `**:${param.parameterName}` : `:${param.parameterName}`;
|
|
115
|
+
path = path.slice(0, param.startIndex) + pattern + path.slice(param.startIndex + param.segment.length);
|
|
116
|
+
}
|
|
117
|
+
return path;
|
|
118
|
+
}
|
|
119
|
+
function toRou3PrefixMatcher(path) {
|
|
120
|
+
const pattern = toRou3Pattern(path);
|
|
121
|
+
return routeToRegExp(pattern === "/" ? "/**" : `${pattern}/**`);
|
|
122
|
+
}
|
|
123
|
+
function decodeParams(params) {
|
|
124
|
+
return Object.fromEntries(Object.entries(params).map(([key, val]) => [key, tryDecodeURIComponent(val)]));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
class OpenAPIHandlerCodecCore {
|
|
128
|
+
serializer;
|
|
129
|
+
errorStatusMap;
|
|
130
|
+
customErrorResponseBodySerializer;
|
|
131
|
+
constructor(options = {}) {
|
|
132
|
+
this.serializer = options.serializer ?? new OpenAPISerializer();
|
|
133
|
+
this.errorStatusMap = options.errorStatusMap ?? COMMON_ERROR_STATUS_MAP;
|
|
134
|
+
this.customErrorResponseBodySerializer = options.customErrorResponseBodyEncoder;
|
|
135
|
+
}
|
|
136
|
+
async decodeInput(matched, request) {
|
|
137
|
+
const [_, search] = parseStandardUrl(request.url);
|
|
138
|
+
const meta = getOpenAPIMeta(matched.procedure);
|
|
139
|
+
const inputStructure = meta?.inputStructure ?? DEFAULT_OPENAPI_INPUT_STRUCTURE;
|
|
140
|
+
const params = this.deserializeParams(matched.params, meta?.paramsStyles);
|
|
141
|
+
const query = this.deserializeQuery(search, meta?.queryStyles);
|
|
142
|
+
if (inputStructure === "compact") {
|
|
143
|
+
const data = request.method === "GET" ? query : this.serializer.deserialize(await request.resolveBody(meta?.requestBodyHint));
|
|
144
|
+
if (data === void 0) {
|
|
145
|
+
return params;
|
|
146
|
+
}
|
|
147
|
+
if (!params || Object.keys(params).length < 1) {
|
|
148
|
+
return data;
|
|
149
|
+
}
|
|
150
|
+
if (isPlainObject(data)) {
|
|
151
|
+
return {
|
|
152
|
+
...params,
|
|
153
|
+
...data
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
return data;
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
params,
|
|
160
|
+
query,
|
|
161
|
+
headers: request.headers,
|
|
162
|
+
body: this.serializer.deserialize(await request.resolveBody(meta?.requestBodyHint))
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* @throws {TypeError} If `outputStructure` is "detailed" and the output doesn't match the expected structure.
|
|
167
|
+
*/
|
|
168
|
+
encodeOutput(output, procedure, path) {
|
|
169
|
+
const meta = getOpenAPIMeta(procedure);
|
|
170
|
+
const successStatus = meta?.successStatus ?? DEFAULT_SUCCESS_STATUS;
|
|
171
|
+
const outputStructure = meta?.outputStructure ?? DEFAULT_OPENAPI_OUTPUT_STRUCTURE;
|
|
172
|
+
if (outputStructure === "compact") {
|
|
173
|
+
return {
|
|
174
|
+
status: successStatus,
|
|
175
|
+
headers: {},
|
|
176
|
+
body: this.serializer.serialize(output)
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if (!isValidDetailedOutput(output)) {
|
|
180
|
+
throw new TypeError(`
|
|
181
|
+
Invalid "detailed" output structure returned by procedure (${path.join(".")}):
|
|
182
|
+
\u2022 Expected an object with optional properties:
|
|
183
|
+
- status (number 200-399)
|
|
184
|
+
- headers (Record<string, string | string[] | undefined>)
|
|
185
|
+
- body (any)
|
|
186
|
+
\u2022 No extra keys allowed.
|
|
187
|
+
|
|
188
|
+
Actual value:
|
|
189
|
+
${stringifyJSON(output)}
|
|
190
|
+
`);
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
status: output.status ?? successStatus,
|
|
194
|
+
headers: output.headers ?? {},
|
|
195
|
+
body: this.serializer.serialize(output.body)
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
encodeError(error) {
|
|
199
|
+
const status = this.errorStatusMap[error.code] ?? DEFAULT_ERROR_STATUS;
|
|
200
|
+
return {
|
|
201
|
+
status,
|
|
202
|
+
headers: {},
|
|
203
|
+
body: this.serializer.serialize(this.customErrorResponseBodySerializer?.(error) ?? error.toJSON())
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
deserializeQuery(search, styles) {
|
|
207
|
+
const searchParams = new URLSearchParams(search);
|
|
208
|
+
const parsed = this.serializer.deserialize(searchParams);
|
|
209
|
+
if (!styles || !isPlainObject(parsed)) {
|
|
210
|
+
return parsed;
|
|
211
|
+
}
|
|
212
|
+
Object.entries(styles).forEach(([key, hint]) => {
|
|
213
|
+
if (hint === void 0) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const values = searchParams.getAll(key);
|
|
217
|
+
let parsedValue;
|
|
218
|
+
if (hint === "primitive") {
|
|
219
|
+
parsedValue = values.at(-1);
|
|
220
|
+
} else if (hint === "array") {
|
|
221
|
+
parsedValue = values;
|
|
222
|
+
} else if (hint === "comma-delimited-array") {
|
|
223
|
+
parsedValue = decodeDelimitedArray(values.at(-1), ",");
|
|
224
|
+
} else if (hint === "comma-delimited-object") {
|
|
225
|
+
parsedValue = decodeDelimitedObject(values.at(-1), ",");
|
|
226
|
+
} else if (hint === "space-delimited-array") {
|
|
227
|
+
parsedValue = decodeDelimitedArray(values.at(-1), " ");
|
|
228
|
+
} else if (hint === "space-delimited-object") {
|
|
229
|
+
parsedValue = decodeDelimitedObject(values.at(-1), " ");
|
|
230
|
+
} else if (hint === "pipe-delimited-array") {
|
|
231
|
+
parsedValue = decodeDelimitedArray(values.at(-1), "|");
|
|
232
|
+
} else if (hint === "pipe-delimited-object") {
|
|
233
|
+
parsedValue = decodeDelimitedObject(values.at(-1), "|");
|
|
234
|
+
} else {
|
|
235
|
+
const last = values.at(-1);
|
|
236
|
+
try {
|
|
237
|
+
parsedValue = parseEmptyableJSON(last);
|
|
238
|
+
} catch {
|
|
239
|
+
parsedValue = last;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
parsed[key] = this.serializer.deserialize(parsedValue);
|
|
243
|
+
});
|
|
244
|
+
return parsed;
|
|
245
|
+
}
|
|
246
|
+
deserializeParams(params, styles) {
|
|
247
|
+
if (!params || !styles) {
|
|
248
|
+
return params;
|
|
249
|
+
}
|
|
250
|
+
const parsed = { ...params };
|
|
251
|
+
Object.entries(styles).forEach(([key, hint]) => {
|
|
252
|
+
if (hint === void 0 || hint === "primitive") {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const value = params[key];
|
|
256
|
+
if (hint === "comma-delimited-array") {
|
|
257
|
+
parsed[key] = decodeDelimitedArray(value, ",");
|
|
258
|
+
} else {
|
|
259
|
+
parsed[key] = decodeDelimitedObject(value, ",");
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
return parsed;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
class OpenAPIHandlerCodec extends OpenAPIHandlerCodecCore {
|
|
266
|
+
matcher;
|
|
267
|
+
constructor(router, options = {}) {
|
|
268
|
+
super(options);
|
|
269
|
+
this.matcher = new OpenAPIMatcher(router, options);
|
|
270
|
+
}
|
|
271
|
+
async resolveProcedure(request, options) {
|
|
272
|
+
const [pathname] = parseStandardUrl(request.url);
|
|
273
|
+
const matched = await this.matcher.match(request.method, pathname, options.prefix);
|
|
274
|
+
if (!matched) {
|
|
275
|
+
return void 0;
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
procedure: matched.procedure,
|
|
279
|
+
path: matched.path,
|
|
280
|
+
decodeInput: () => this.decodeInput(matched, request)
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function isValidDetailedOutput(output) {
|
|
285
|
+
if (!isTypescriptObject(output)) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
if (Object.keys(output).some((key) => key !== "status" && key !== "headers" && key !== "body")) {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
if (output.status !== void 0 && (typeof output.status !== "number" || !Number.isInteger(output.status) || output.status < 200 || output.status > 399)) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
if (output.headers !== void 0 && !isStandardHeaders(output.headers)) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
function decodeDelimitedArray(value, delimiter) {
|
|
300
|
+
if (value === void 0) {
|
|
301
|
+
return void 0;
|
|
302
|
+
}
|
|
303
|
+
return value.split(delimiter);
|
|
304
|
+
}
|
|
305
|
+
function decodeDelimitedObject(value, delimiter) {
|
|
306
|
+
if (value === void 0) {
|
|
307
|
+
return void 0;
|
|
308
|
+
}
|
|
309
|
+
const obj = new NullProtoObj();
|
|
310
|
+
const parts = value.split(delimiter);
|
|
311
|
+
for (let i = 0; i < parts.length; i += 2) {
|
|
312
|
+
const key = parts[i];
|
|
313
|
+
obj[key] = parts[i + 1];
|
|
314
|
+
}
|
|
315
|
+
return obj;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export { OpenAPIHandlerCodec as O, OpenAPIHandlerCodecCore as a, OpenAPIMatcher as b };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { AnyORPCError } from '@orpc/client';
|
|
2
|
+
import { AnyProcedure, AnyRouter, Context } from '@orpc/server';
|
|
3
|
+
import { StandardHandlerCodec, StandardHandlerHandleOptions, StandardHandlerCodecResolvedProcedure } from '@orpc/server/standard';
|
|
4
|
+
import { Value, Promisable } from '@orpc/shared';
|
|
5
|
+
import { StandardLazyRequest, StandardResponse } from '@standardserver/core';
|
|
6
|
+
import { AnyProcedureContract } from '@orpc/contract';
|
|
7
|
+
import { O as OpenAPISerializer } from './openapi.C7m7NAmH.mjs';
|
|
8
|
+
|
|
9
|
+
interface OpenAPIMatcherOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Filter which procedures are exposed for matching. Return `false` to exclude.
|
|
12
|
+
*
|
|
13
|
+
* @default true
|
|
14
|
+
*/
|
|
15
|
+
filter?: Value<boolean, [contract: AnyProcedureContract | AnyProcedure, path: string[]]>;
|
|
16
|
+
}
|
|
17
|
+
declare class OpenAPIMatcher {
|
|
18
|
+
private readonly filter;
|
|
19
|
+
private readonly rootRouter;
|
|
20
|
+
private readonly tree;
|
|
21
|
+
private pendingLazyRouters;
|
|
22
|
+
constructor(router: AnyRouter, options?: OpenAPIMatcherOptions);
|
|
23
|
+
private index;
|
|
24
|
+
match(method: string, pathname: `/${string}`, prefix: `/${string}` | undefined): Promise<{
|
|
25
|
+
path: string[];
|
|
26
|
+
procedure: AnyProcedure;
|
|
27
|
+
params?: Record<string, string> | undefined;
|
|
28
|
+
} | undefined>;
|
|
29
|
+
private matchPathname;
|
|
30
|
+
private resolvePendingLazyRouters;
|
|
31
|
+
private resolveProcedure;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface OpenAPIHandlerCodecCoreOptions<_T extends Context> {
|
|
35
|
+
/**
|
|
36
|
+
* Override the default OpenAPI serializer.
|
|
37
|
+
*/
|
|
38
|
+
serializer?: Pick<OpenAPISerializer, keyof OpenAPISerializer>;
|
|
39
|
+
/**
|
|
40
|
+
* Mapping ORPCError Code -> HTTP Status Code
|
|
41
|
+
*
|
|
42
|
+
* @default COMMON_ERROR_STATUS_MAP, DEFAULT_ERROR_STATUS
|
|
43
|
+
*/
|
|
44
|
+
errorStatusMap?: Record<string, number> | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Customize how an ORPC error is serialized into a response body.
|
|
47
|
+
* Use this if your API needs a different error output structure.
|
|
48
|
+
*
|
|
49
|
+
* @remarks
|
|
50
|
+
* - Return `null | undefined` to fallback to default behavior
|
|
51
|
+
*
|
|
52
|
+
* @default ((e) => e.toJSON())
|
|
53
|
+
*/
|
|
54
|
+
customErrorResponseBodyEncoder?: (error: AnyORPCError) => unknown;
|
|
55
|
+
}
|
|
56
|
+
declare class OpenAPIHandlerCodecCore<T extends Context> {
|
|
57
|
+
private readonly serializer;
|
|
58
|
+
private readonly errorStatusMap;
|
|
59
|
+
private readonly customErrorResponseBodySerializer;
|
|
60
|
+
constructor(options?: OpenAPIHandlerCodecCoreOptions<T>);
|
|
61
|
+
decodeInput(matched: {
|
|
62
|
+
procedure: AnyProcedure;
|
|
63
|
+
params?: undefined | Record<string, string>;
|
|
64
|
+
}, request: StandardLazyRequest): Promise<unknown>;
|
|
65
|
+
/**
|
|
66
|
+
* @throws {TypeError} If `outputStructure` is "detailed" and the output doesn't match the expected structure.
|
|
67
|
+
*/
|
|
68
|
+
encodeOutput(output: unknown, procedure: AnyProcedure, path: string[]): Promisable<StandardResponse>;
|
|
69
|
+
encodeError(error: AnyORPCError): Promisable<StandardResponse>;
|
|
70
|
+
private deserializeQuery;
|
|
71
|
+
private deserializeParams;
|
|
72
|
+
}
|
|
73
|
+
interface OpenAPIHandlerCodecOptions<T extends Context> extends OpenAPIHandlerCodecCoreOptions<T>, OpenAPIMatcherOptions {
|
|
74
|
+
}
|
|
75
|
+
declare class OpenAPIHandlerCodec<T extends Context> extends OpenAPIHandlerCodecCore<T> implements StandardHandlerCodec<T> {
|
|
76
|
+
private readonly matcher;
|
|
77
|
+
constructor(router: AnyRouter, options?: OpenAPIHandlerCodecOptions<T>);
|
|
78
|
+
resolveProcedure(request: StandardLazyRequest, options: StandardHandlerHandleOptions<T>): Promise<StandardHandlerCodecResolvedProcedure | undefined>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { OpenAPIHandlerCodec as a, OpenAPIHandlerCodecCore as b, OpenAPIMatcher as d };
|
|
82
|
+
export type { OpenAPIHandlerCodecOptions as O, OpenAPIHandlerCodecCoreOptions as c, OpenAPIMatcherOptions as e };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { OpenAPIV3_1 } from '@hey-api/spec-types';
|
|
2
|
+
import { ORPCError, AnyNestedClient, Client } from '@orpc/client';
|
|
3
|
+
|
|
4
|
+
type OpenAPIDocument = OpenAPIV3_1.Document;
|
|
5
|
+
type OpenAPIOperationObject = OpenAPIV3_1.OperationObject;
|
|
6
|
+
type JsonifiedValue<T> = T extends string ? T : T extends number ? T : T extends boolean ? T : T extends null ? T : T extends undefined ? T : T extends Array<unknown> ? JsonifiedArray<T> : T extends Record<string, unknown> ? {
|
|
7
|
+
[K in keyof T]: JsonifiedValue<T[K]>;
|
|
8
|
+
} : T extends Date ? string : T extends bigint ? string : T extends File ? File : T extends Blob ? Blob : T extends RegExp ? string : T extends URL ? string : T extends Map<infer K, infer V> ? JsonifiedArray<[K, V][]> : T extends Set<infer U> ? JsonifiedArray<U[]> : T extends AsyncIteratorObject<infer U, infer V> ? AsyncIteratorObject<JsonifiedValue<U>, JsonifiedValue<V>> : unknown;
|
|
9
|
+
type JsonifiedArray<T extends Array<unknown>> = T extends readonly [] ? [] : T extends readonly [infer U, ...infer V] ? [U extends undefined ? null : JsonifiedValue<U>, ...JsonifiedArray<V>] : T extends Array<infer U> ? Array<JsonifiedValue<U>> : unknown;
|
|
10
|
+
type JsonifiedClientError<T> = T extends ORPCError<infer UCode, infer UData> ? ORPCError<UCode, JsonifiedValue<UData>> : T;
|
|
11
|
+
/**
|
|
12
|
+
* Convert types that JSON not support to corresponding json types
|
|
13
|
+
*/
|
|
14
|
+
type JsonifiedClient<T extends AnyNestedClient> = T extends Client<infer UClientContext, infer UInput, infer UOutput, infer UError> ? Client<UClientContext, UInput, JsonifiedValue<UOutput>, JsonifiedClientError<UError>> : {
|
|
15
|
+
[K in keyof T]: T[K] extends AnyNestedClient ? JsonifiedClient<T[K]> : T[K];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type { JsonifiedValue as J, OpenAPIOperationObject as O, OpenAPIDocument as a, JsonifiedClientError as b, JsonifiedClient as c, JsonifiedArray as d };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { OpenAPIV3_1 } from '@hey-api/spec-types';
|
|
2
|
+
import { ORPCError, AnyNestedClient, Client } from '@orpc/client';
|
|
3
|
+
|
|
4
|
+
type OpenAPIDocument = OpenAPIV3_1.Document;
|
|
5
|
+
type OpenAPIOperationObject = OpenAPIV3_1.OperationObject;
|
|
6
|
+
type JsonifiedValue<T> = T extends string ? T : T extends number ? T : T extends boolean ? T : T extends null ? T : T extends undefined ? T : T extends Array<unknown> ? JsonifiedArray<T> : T extends Record<string, unknown> ? {
|
|
7
|
+
[K in keyof T]: JsonifiedValue<T[K]>;
|
|
8
|
+
} : T extends Date ? string : T extends bigint ? string : T extends File ? File : T extends Blob ? Blob : T extends RegExp ? string : T extends URL ? string : T extends Map<infer K, infer V> ? JsonifiedArray<[K, V][]> : T extends Set<infer U> ? JsonifiedArray<U[]> : T extends AsyncIteratorObject<infer U, infer V> ? AsyncIteratorObject<JsonifiedValue<U>, JsonifiedValue<V>> : unknown;
|
|
9
|
+
type JsonifiedArray<T extends Array<unknown>> = T extends readonly [] ? [] : T extends readonly [infer U, ...infer V] ? [U extends undefined ? null : JsonifiedValue<U>, ...JsonifiedArray<V>] : T extends Array<infer U> ? Array<JsonifiedValue<U>> : unknown;
|
|
10
|
+
type JsonifiedClientError<T> = T extends ORPCError<infer UCode, infer UData> ? ORPCError<UCode, JsonifiedValue<UData>> : T;
|
|
11
|
+
/**
|
|
12
|
+
* Convert types that JSON not support to corresponding json types
|
|
13
|
+
*/
|
|
14
|
+
type JsonifiedClient<T extends AnyNestedClient> = T extends Client<infer UClientContext, infer UInput, infer UOutput, infer UError> ? Client<UClientContext, UInput, JsonifiedValue<UOutput>, JsonifiedClientError<UError>> : {
|
|
15
|
+
[K in keyof T]: T[K] extends AnyNestedClient ? JsonifiedClient<T[K]> : T[K];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type { JsonifiedValue as J, OpenAPIOperationObject as O, OpenAPIDocument as a, JsonifiedClientError as b, JsonifiedClient as c, JsonifiedArray as d };
|