@orpc/openapi 2.0.0-beta.4 → 2.0.0-beta.41
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 +97 -94
- package/dist/adapters/aws-lambda/index.d.mts +26 -0
- package/dist/adapters/aws-lambda/index.d.ts +26 -0
- package/dist/adapters/aws-lambda/index.mjs +21 -0
- package/dist/adapters/fastify/index.d.mts +23 -0
- package/dist/adapters/fastify/index.d.ts +23 -0
- package/dist/adapters/fastify/index.mjs +21 -0
- package/dist/adapters/fetch/index.d.mts +15 -5
- package/dist/adapters/fetch/index.d.ts +15 -5
- package/dist/adapters/fetch/index.mjs +6 -6
- package/dist/adapters/node/index.d.mts +9 -4
- package/dist/adapters/node/index.d.ts +9 -4
- package/dist/adapters/node/index.mjs +4 -4
- package/dist/adapters/standard/index.d.mts +10 -46
- package/dist/adapters/standard/index.d.ts +10 -46
- package/dist/adapters/standard/index.mjs +6 -6
- package/dist/extensions/route.d.mts +5 -7
- package/dist/extensions/route.d.ts +5 -7
- package/dist/helpers/index.d.mts +9 -1
- package/dist/helpers/index.d.ts +9 -1
- package/dist/helpers/index.mjs +1 -1
- package/dist/index.d.mts +77 -45
- package/dist/index.d.ts +77 -45
- package/dist/index.mjs +614 -765
- package/dist/plugins/index.d.mts +35 -6
- package/dist/plugins/index.d.ts +35 -6
- package/dist/plugins/index.mjs +15 -10
- package/dist/shared/{openapi.B2SK0ZAr.mjs → openapi.8cM6P94D.mjs} +32 -36
- package/dist/shared/openapi.B2G-HeFn.mjs +123 -0
- package/dist/shared/{openapi.7vgmPxca.d.ts → openapi.BCNfESWr.d.ts} +12 -9
- package/dist/shared/{openapi.CX6Ri5dP.d.mts → openapi.BgpM--nz.d.mts} +12 -9
- package/dist/shared/{openapi.CTlpLuKN.mjs → openapi.Bi1qxGF9.mjs} +102 -54
- package/dist/shared/{openapi.DmAa7YPO.mjs → openapi.Bu_PfTtX.mjs} +140 -61
- package/dist/shared/{openapi.C7m7NAmH.d.mts → openapi.C2LayTNZ.d.mts} +16 -5
- package/dist/shared/{openapi.C7m7NAmH.d.ts → openapi.C2LayTNZ.d.ts} +16 -5
- package/dist/shared/openapi.C6FFC29v.d.mts +31 -0
- package/dist/shared/openapi.C6FFC29v.d.ts +31 -0
- package/dist/shared/{openapi.BQzzr4-4.d.ts → openapi.Cem81Zhm.d.mts} +41 -12
- package/dist/shared/{openapi.BcEtAxQj.d.mts → openapi.Cem81Zhm.d.ts} +41 -12
- package/dist/shared/openapi.Cg9h6jpm.d.mts +45 -0
- package/dist/shared/openapi.D0yWs-uW.d.ts +45 -0
- package/package.json +57 -17
- package/dist/shared/openapi.Bt87OzTt.mjs +0 -131
- package/dist/shared/openapi.CYgMBSUF.d.mts +0 -18
- package/dist/shared/openapi.CYgMBSUF.d.ts +0 -18
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { COMMON_ERROR_STATUS_MAP } from '@orpc/client';
|
|
2
2
|
import { walkProcedureContractsSync, Procedure, unlazy, getRouter, createContractProcedure, DEFAULT_SUCCESS_STATUS, DEFAULT_ERROR_STATUS } from '@orpc/server';
|
|
3
|
-
import { value, pathToHttpPath, mergeHttpPath, normalizeHttpPath,
|
|
4
|
-
import { parseStandardUrl
|
|
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.
|
|
3
|
+
import { value, pathToHttpPath, mergeHttpPath, normalizeHttpPath, safeDecodeURIComponent, NullProtoObj, isTypescriptObject, isPlainObject, stringifyJSON, getOwn, parseEmptyableJSON } from '@orpc/shared';
|
|
4
|
+
import { parseStandardUrl } from '@standard-server/core';
|
|
5
|
+
import { D as DEFAULT_OPENAPI_METHOD, g as getDynamicPathParams, O as OpenAPISerializer, a as DEFAULT_OPENAPI_INPUT_STRUCTURE, i as isBodylessMethod, b as DEFAULT_OPENAPI_OUTPUT_STRUCTURE } from './openapi.Bu_PfTtX.mjs';
|
|
6
6
|
import { g as getOpenAPIMeta } from './openapi.B9PQzqBn.mjs';
|
|
7
7
|
import { createRouter, addRoute, findRoute, routeToRegExp } from 'rou3';
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ class OpenAPIMatcher {
|
|
|
10
10
|
filter;
|
|
11
11
|
rootRouter;
|
|
12
12
|
tree = createRouter();
|
|
13
|
-
pendingLazyRouters =
|
|
13
|
+
pendingLazyRouters = /* @__PURE__ */ new Set();
|
|
14
14
|
constructor(router, options = {}) {
|
|
15
15
|
this.filter = options.filter ?? true;
|
|
16
16
|
this.rootRouter = router;
|
|
@@ -32,13 +32,13 @@ class OpenAPIMatcher {
|
|
|
32
32
|
procedure: contract instanceof Procedure ? contract : void 0
|
|
33
33
|
});
|
|
34
34
|
}, path);
|
|
35
|
-
|
|
35
|
+
for (const result of lazyResults) {
|
|
36
36
|
const prefix = getOpenAPIMeta(result.router)?.prefix;
|
|
37
|
-
|
|
37
|
+
this.pendingLazyRouters.add({
|
|
38
38
|
...result,
|
|
39
39
|
matcher: prefix ? toRou3PrefixMatcher(prefix) : void 0
|
|
40
|
-
};
|
|
41
|
-
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
42
|
}
|
|
43
43
|
async match(method, pathname, prefix) {
|
|
44
44
|
if (prefix) {
|
|
@@ -56,44 +56,58 @@ class OpenAPIMatcher {
|
|
|
56
56
|
return void 0;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
61
|
-
|
|
59
|
+
const loading = this.resolvePendingLazyRouters(pathname);
|
|
60
|
+
if (loading !== void 0) {
|
|
61
|
+
await loading;
|
|
62
62
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
let match = findRoute(this.tree, method, pathname);
|
|
64
|
+
if (match === void 0 && pathname.includes("%")) {
|
|
65
|
+
const normalizedPathname = normalizeHttpPath(pathname);
|
|
66
|
+
const normalizedLoading = this.resolvePendingLazyRouters(normalizedPathname);
|
|
67
|
+
if (normalizedLoading !== void 0) {
|
|
68
|
+
await normalizedLoading;
|
|
69
|
+
}
|
|
70
|
+
match = findRoute(this.tree, method, normalizedPathname);
|
|
71
|
+
}
|
|
72
|
+
if (match === void 0) {
|
|
69
73
|
return void 0;
|
|
70
74
|
}
|
|
71
|
-
const
|
|
75
|
+
const entry = match.data;
|
|
72
76
|
return {
|
|
73
|
-
path:
|
|
74
|
-
procedure,
|
|
77
|
+
path: entry.path,
|
|
78
|
+
procedure: entry.procedure ?? await this.resolveProcedure(entry),
|
|
75
79
|
params: match.params ? decodeParams(match.params) : void 0
|
|
76
80
|
};
|
|
77
81
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
resolvePendingLazyRouters(pathname) {
|
|
83
|
+
for (const pending of this.pendingLazyRouters) {
|
|
84
|
+
if (pending.matcher === void 0 || pending.matcher.test(pathname)) {
|
|
85
|
+
return this.loadPendingLazyRouters(pathname);
|
|
86
|
+
}
|
|
81
87
|
}
|
|
82
|
-
|
|
88
|
+
}
|
|
89
|
+
async loadPendingLazyRouters(pathname) {
|
|
83
90
|
for (const pending of this.pendingLazyRouters) {
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
this.index(router, pending.path);
|
|
87
|
-
} else {
|
|
88
|
-
stillPending.push(pending);
|
|
91
|
+
if (pending.matcher === void 0 || pending.matcher.test(pathname)) {
|
|
92
|
+
await this.loadPendingLazyRouter(pending);
|
|
89
93
|
}
|
|
90
94
|
}
|
|
91
|
-
this.pendingLazyRouters = stillPending;
|
|
92
95
|
}
|
|
93
|
-
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
+
loadPendingLazyRouter(pending) {
|
|
97
|
+
if (pending.loading === void 0) {
|
|
98
|
+
pending.loading = this.indexPendingLazyRouter(pending).catch((error) => {
|
|
99
|
+
pending.loading = void 0;
|
|
100
|
+
throw error;
|
|
101
|
+
});
|
|
96
102
|
}
|
|
103
|
+
return pending.loading;
|
|
104
|
+
}
|
|
105
|
+
async indexPendingLazyRouter(pending) {
|
|
106
|
+
const { default: router } = await unlazy(pending.router);
|
|
107
|
+
this.index(router, pending.path);
|
|
108
|
+
this.pendingLazyRouters.delete(pending);
|
|
109
|
+
}
|
|
110
|
+
async resolveProcedure(entry) {
|
|
97
111
|
const { default: maybeProcedure } = await unlazy(getRouter(this.rootRouter, entry.path));
|
|
98
112
|
if (!(maybeProcedure instanceof Procedure)) {
|
|
99
113
|
throw new TypeError(
|
|
@@ -121,7 +135,42 @@ function toRou3PrefixMatcher(path) {
|
|
|
121
135
|
return routeToRegExp(pattern === "/" ? "/**" : `${pattern}/**`);
|
|
122
136
|
}
|
|
123
137
|
function decodeParams(params) {
|
|
124
|
-
return Object.fromEntries(Object.entries(params).map(([key, val]) => [key,
|
|
138
|
+
return Object.fromEntries(Object.entries(params).map(([key, val]) => [key, safeDecodeURIComponent(val)]));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function serializeHeaders(headers, serializer) {
|
|
142
|
+
const result = new NullProtoObj();
|
|
143
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
144
|
+
if (Array.isArray(value)) {
|
|
145
|
+
const lines = [];
|
|
146
|
+
for (const item of value) {
|
|
147
|
+
const line2 = serializeHeaderValue(item, serializer);
|
|
148
|
+
if (line2 !== void 0) {
|
|
149
|
+
lines.push(line2);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
result[key] = lines;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const line = serializeHeaderValue(value, serializer);
|
|
156
|
+
if (line !== void 0) {
|
|
157
|
+
result[key] = line;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
function serializeHeaderValue(value, serializer) {
|
|
163
|
+
const serialized = serializer.serialize(value);
|
|
164
|
+
if (Array.isArray(serialized)) {
|
|
165
|
+
return serialized.filter((item) => item !== void 0 && item !== null).map(String).join(",");
|
|
166
|
+
}
|
|
167
|
+
if (isTypescriptObject(serialized)) {
|
|
168
|
+
return Object.entries(serialized).filter(([, val]) => val !== void 0 && val !== null).map(([key, val]) => `${String(key)},${String(val)}`).join(",");
|
|
169
|
+
}
|
|
170
|
+
if (serialized !== void 0 && serialized !== null) {
|
|
171
|
+
return String(serialized);
|
|
172
|
+
}
|
|
173
|
+
return void 0;
|
|
125
174
|
}
|
|
126
175
|
|
|
127
176
|
class OpenAPIHandlerCodecCore {
|
|
@@ -134,30 +183,28 @@ class OpenAPIHandlerCodecCore {
|
|
|
134
183
|
this.customErrorResponseBodySerializer = options.customErrorResponseBodyEncoder;
|
|
135
184
|
}
|
|
136
185
|
async decodeInput(matched, request) {
|
|
137
|
-
const [_, search] = parseStandardUrl(request.url);
|
|
138
186
|
const meta = getOpenAPIMeta(matched.procedure);
|
|
139
187
|
const inputStructure = meta?.inputStructure ?? DEFAULT_OPENAPI_INPUT_STRUCTURE;
|
|
140
188
|
const params = this.deserializeParams(matched.params, meta?.paramsStyles);
|
|
141
|
-
const query = this.deserializeQuery(search, meta?.queryStyles);
|
|
142
189
|
if (inputStructure === "compact") {
|
|
143
|
-
const data = request.method
|
|
190
|
+
const data = isBodylessMethod(request.method) ? this.deserializeQuery(request.url, meta?.queryStyles) : this.serializer.deserialize(await request.resolveBody(meta?.requestBodyHint));
|
|
144
191
|
if (data === void 0) {
|
|
145
192
|
return params;
|
|
146
193
|
}
|
|
147
194
|
if (!params || Object.keys(params).length < 1) {
|
|
148
195
|
return data;
|
|
149
196
|
}
|
|
150
|
-
if (isPlainObject(data)) {
|
|
151
|
-
return
|
|
152
|
-
...params,
|
|
153
|
-
...data
|
|
154
|
-
};
|
|
197
|
+
if (!isPlainObject(data)) {
|
|
198
|
+
return params;
|
|
155
199
|
}
|
|
156
|
-
return
|
|
200
|
+
return {
|
|
201
|
+
...data,
|
|
202
|
+
...params
|
|
203
|
+
};
|
|
157
204
|
}
|
|
158
205
|
return {
|
|
159
206
|
params,
|
|
160
|
-
query,
|
|
207
|
+
query: this.deserializeQuery(request.url, meta?.queryStyles),
|
|
161
208
|
headers: request.headers,
|
|
162
209
|
body: this.serializer.deserialize(await request.resolveBody(meta?.requestBodyHint))
|
|
163
210
|
};
|
|
@@ -180,8 +227,8 @@ class OpenAPIHandlerCodecCore {
|
|
|
180
227
|
throw new TypeError(`
|
|
181
228
|
Invalid "detailed" output structure returned by procedure (${path.join(".")}):
|
|
182
229
|
\u2022 Expected an object with optional properties:
|
|
183
|
-
- status (number
|
|
184
|
-
- headers (
|
|
230
|
+
- status (number <400)
|
|
231
|
+
- headers (object)
|
|
185
232
|
- body (any)
|
|
186
233
|
\u2022 No extra keys allowed.
|
|
187
234
|
|
|
@@ -191,19 +238,20 @@ class OpenAPIHandlerCodecCore {
|
|
|
191
238
|
}
|
|
192
239
|
return {
|
|
193
240
|
status: output.status ?? successStatus,
|
|
194
|
-
headers: output.headers
|
|
241
|
+
headers: output.headers !== void 0 ? serializeHeaders(output.headers, this.serializer) : {},
|
|
195
242
|
body: this.serializer.serialize(output.body)
|
|
196
243
|
};
|
|
197
244
|
}
|
|
198
245
|
encodeError(error) {
|
|
199
|
-
const status = this.errorStatusMap
|
|
246
|
+
const status = getOwn(this.errorStatusMap, error.code) ?? DEFAULT_ERROR_STATUS;
|
|
200
247
|
return {
|
|
201
248
|
status,
|
|
202
249
|
headers: {},
|
|
203
250
|
body: this.serializer.serialize(this.customErrorResponseBodySerializer?.(error) ?? error.toJSON())
|
|
204
251
|
};
|
|
205
252
|
}
|
|
206
|
-
deserializeQuery(
|
|
253
|
+
deserializeQuery(url, styles) {
|
|
254
|
+
const [, search] = parseStandardUrl(url);
|
|
207
255
|
const searchParams = new URLSearchParams(search);
|
|
208
256
|
const parsed = this.serializer.deserialize(searchParams);
|
|
209
257
|
if (!styles || !isPlainObject(parsed)) {
|
|
@@ -249,10 +297,10 @@ class OpenAPIHandlerCodecCore {
|
|
|
249
297
|
}
|
|
250
298
|
const parsed = { ...params };
|
|
251
299
|
Object.entries(styles).forEach(([key, hint]) => {
|
|
252
|
-
|
|
300
|
+
const value = getOwn(params, key);
|
|
301
|
+
if (hint === void 0 || hint === "primitive" || value === void 0) {
|
|
253
302
|
return;
|
|
254
303
|
}
|
|
255
|
-
const value = params[key];
|
|
256
304
|
if (hint === "comma-delimited-array") {
|
|
257
305
|
parsed[key] = decodeDelimitedArray(value, ",");
|
|
258
306
|
} else {
|
|
@@ -288,10 +336,10 @@ function isValidDetailedOutput(output) {
|
|
|
288
336
|
if (Object.keys(output).some((key) => key !== "status" && key !== "headers" && key !== "body")) {
|
|
289
337
|
return false;
|
|
290
338
|
}
|
|
291
|
-
if (output.status !== void 0 && (typeof output.status !== "number" || !Number.isInteger(output.status) || output.status
|
|
339
|
+
if (output.status !== void 0 && (typeof output.status !== "number" || !Number.isInteger(output.status) || output.status > 399)) {
|
|
292
340
|
return false;
|
|
293
341
|
}
|
|
294
|
-
if (output.headers !== void 0 && !
|
|
342
|
+
if (output.headers !== void 0 && !isTypescriptObject(output.headers)) {
|
|
295
343
|
return false;
|
|
296
344
|
}
|
|
297
345
|
return true;
|
|
@@ -315,4 +363,4 @@ function decodeDelimitedObject(value, delimiter) {
|
|
|
315
363
|
return obj;
|
|
316
364
|
}
|
|
317
365
|
|
|
318
|
-
export { OpenAPIHandlerCodec as O, OpenAPIHandlerCodecCore as a, OpenAPIMatcher as b };
|
|
366
|
+
export { OpenAPIHandlerCodec as O, OpenAPIHandlerCodecCore as a, OpenAPIMatcher as b, serializeHeaders as s };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isPlainObject, isAsyncIteratorObject } from '@orpc/shared';
|
|
3
|
-
import { ErrorEvent } from '@
|
|
4
|
-
import { B as BracketNotationSerializer } from './openapi.
|
|
1
|
+
import { wrapAsyncIteratorPreservingEventMeta, toORPCError, isORPCErrorJson, createORPCErrorFromJson } from '@orpc/client';
|
|
2
|
+
import { isPlainObject, NullProtoObj, copyOnWrite, isAsyncIteratorObject } from '@orpc/shared';
|
|
3
|
+
import { ErrorEvent } from '@standard-server/core';
|
|
4
|
+
import { B as BracketNotationSerializer } from './openapi.B2G-HeFn.mjs';
|
|
5
5
|
|
|
6
6
|
const DEFAULT_OPENAPI_METHOD = "POST";
|
|
7
7
|
const DEFAULT_OPENAPI_SUCCESS_DESCRIPTION = "OK";
|
|
@@ -48,18 +48,18 @@ const DEFAULT_OPEN_API_JSON_SERIALIZER_HANDLERS = {
|
|
|
48
48
|
},
|
|
49
49
|
isTerminal: true
|
|
50
50
|
},
|
|
51
|
-
|
|
51
|
+
infinity: {
|
|
52
52
|
condition(data) {
|
|
53
|
-
return data
|
|
53
|
+
return data === Number.POSITIVE_INFINITY || data === Number.NEGATIVE_INFINITY;
|
|
54
54
|
},
|
|
55
|
-
serialize(
|
|
56
|
-
return
|
|
55
|
+
serialize() {
|
|
56
|
+
return null;
|
|
57
57
|
},
|
|
58
58
|
isTerminal: true
|
|
59
59
|
},
|
|
60
|
-
|
|
60
|
+
url: {
|
|
61
61
|
condition(data) {
|
|
62
|
-
return data instanceof
|
|
62
|
+
return data instanceof URL;
|
|
63
63
|
},
|
|
64
64
|
serialize(data) {
|
|
65
65
|
return data.toString();
|
|
@@ -84,45 +84,114 @@ const DEFAULT_OPEN_API_JSON_SERIALIZER_HANDLERS = {
|
|
|
84
84
|
}
|
|
85
85
|
};
|
|
86
86
|
class OpenAPIJsonSerializer {
|
|
87
|
-
|
|
87
|
+
inlineBuiltInHandlers;
|
|
88
|
+
handlerEntries;
|
|
88
89
|
omitUndefinedProperties;
|
|
89
90
|
constructor(options = {}) {
|
|
90
|
-
this.handlers = {
|
|
91
|
-
...DEFAULT_OPEN_API_JSON_SERIALIZER_HANDLERS,
|
|
92
|
-
...options.handlers
|
|
93
|
-
};
|
|
94
91
|
this.omitUndefinedProperties = options.omitUndefinedProperties !== false;
|
|
92
|
+
const customHandlers = options.handlers;
|
|
93
|
+
if (customHandlers === void 0) {
|
|
94
|
+
this.inlineBuiltInHandlers = true;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let inlineBuiltInHandlers = true;
|
|
98
|
+
let handlerEntries = [];
|
|
99
|
+
for (const key of Object.keys(customHandlers)) {
|
|
100
|
+
const handler = customHandlers[key];
|
|
101
|
+
if (inlineBuiltInHandlers && key in DEFAULT_OPEN_API_JSON_SERIALIZER_HANDLERS) {
|
|
102
|
+
inlineBuiltInHandlers = false;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
if (handler !== void 0) {
|
|
106
|
+
handlerEntries.push(handler);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (!inlineBuiltInHandlers) {
|
|
110
|
+
handlerEntries = [];
|
|
111
|
+
for (const handler of Object.values({ ...DEFAULT_OPEN_API_JSON_SERIALIZER_HANDLERS, ...customHandlers })) {
|
|
112
|
+
if (handler !== void 0) {
|
|
113
|
+
handlerEntries.push(handler);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
this.inlineBuiltInHandlers = inlineBuiltInHandlers;
|
|
118
|
+
this.handlerEntries = handlerEntries;
|
|
95
119
|
}
|
|
96
120
|
serialize(data) {
|
|
97
|
-
const
|
|
121
|
+
const maps = [];
|
|
122
|
+
const blobs = [];
|
|
123
|
+
const json = this.serializeValue(data, [], maps, blobs);
|
|
98
124
|
return { json, maps, blobs };
|
|
99
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* `segments` is a shared mutable stack (push/pop while walking),
|
|
128
|
+
* so it must be copied before being stored in `maps`.
|
|
129
|
+
*/
|
|
100
130
|
serializeValue(data, segments, maps, blobs) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
131
|
+
if (this.inlineBuiltInHandlers) {
|
|
132
|
+
switch (typeof data) {
|
|
133
|
+
case "string":
|
|
134
|
+
case "boolean":
|
|
135
|
+
return data;
|
|
136
|
+
case "number":
|
|
137
|
+
return Number.isFinite(data) ? data : null;
|
|
138
|
+
case "undefined":
|
|
139
|
+
return null;
|
|
140
|
+
case "bigint":
|
|
141
|
+
return data.toString();
|
|
142
|
+
case "object": {
|
|
143
|
+
if (data === null) {
|
|
144
|
+
return data;
|
|
145
|
+
}
|
|
146
|
+
if (data instanceof Date) {
|
|
147
|
+
return Number.isNaN(data.getTime()) ? null : data.toISOString();
|
|
148
|
+
}
|
|
149
|
+
if (data instanceof URL) {
|
|
150
|
+
return data.toString();
|
|
151
|
+
}
|
|
152
|
+
if (data instanceof Set) {
|
|
153
|
+
return this.serializeValue(Array.from(data), segments, maps, blobs);
|
|
154
|
+
}
|
|
155
|
+
if (data instanceof Map) {
|
|
156
|
+
return this.serializeValue(Array.from(data.entries()), segments, maps, blobs);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const handlerEntries = this.handlerEntries;
|
|
162
|
+
if (handlerEntries) {
|
|
163
|
+
for (let i = 0; i < handlerEntries.length; i++) {
|
|
164
|
+
const handler = handlerEntries[i];
|
|
165
|
+
if (handler.condition(data)) {
|
|
166
|
+
const serialized = handler.serialize(data);
|
|
167
|
+
if (handler.isTerminal) {
|
|
168
|
+
if (serialized instanceof Blob) {
|
|
169
|
+
maps.push(segments.slice());
|
|
170
|
+
blobs.push(serialized);
|
|
171
|
+
}
|
|
172
|
+
return serialized;
|
|
173
|
+
}
|
|
174
|
+
return this.serializeValue(serialized, segments, maps, blobs);
|
|
107
175
|
}
|
|
108
|
-
const result = this.serializeValue(serialized, segments, maps, blobs);
|
|
109
|
-
return result;
|
|
110
176
|
}
|
|
111
177
|
}
|
|
112
178
|
if (data instanceof Blob) {
|
|
113
|
-
maps.push(segments);
|
|
179
|
+
maps.push(segments.slice());
|
|
114
180
|
blobs.push(data);
|
|
115
|
-
return
|
|
181
|
+
return data;
|
|
116
182
|
}
|
|
117
183
|
if (Array.isArray(data)) {
|
|
118
|
-
const json =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
184
|
+
const json = [];
|
|
185
|
+
for (let i = 0; i < data.length; i++) {
|
|
186
|
+
segments.push(i);
|
|
187
|
+
json.push(this.serializeValue(data[i], segments, maps, blobs));
|
|
188
|
+
segments.pop();
|
|
189
|
+
}
|
|
190
|
+
return json;
|
|
122
191
|
}
|
|
123
192
|
if (isPlainObject(data)) {
|
|
124
|
-
const json =
|
|
125
|
-
for (const k
|
|
193
|
+
const json = new NullProtoObj();
|
|
194
|
+
for (const k of Object.keys(data)) {
|
|
126
195
|
const v = data[k];
|
|
127
196
|
if (k === "toJSON" && typeof v === "function") {
|
|
128
197
|
continue;
|
|
@@ -130,29 +199,38 @@ class OpenAPIJsonSerializer {
|
|
|
130
199
|
if (v === void 0 && this.omitUndefinedProperties) {
|
|
131
200
|
continue;
|
|
132
201
|
}
|
|
133
|
-
|
|
202
|
+
segments.push(k);
|
|
203
|
+
json[k] = this.serializeValue(v, segments, maps, blobs);
|
|
204
|
+
segments.pop();
|
|
134
205
|
}
|
|
135
|
-
return
|
|
206
|
+
return json;
|
|
136
207
|
}
|
|
137
|
-
return
|
|
208
|
+
return data;
|
|
138
209
|
}
|
|
139
210
|
deserialize(serialized) {
|
|
140
|
-
const ref = {
|
|
211
|
+
const ref = { json: serialized.json };
|
|
141
212
|
if (serialized.blobs?.length) {
|
|
142
|
-
serialized.maps.
|
|
213
|
+
for (let i = 0; i < serialized.maps.length; i++) {
|
|
214
|
+
const blob = serialized.blobs[i];
|
|
215
|
+
if (!(blob instanceof Blob)) {
|
|
216
|
+
throw new TypeError(`Invalid OpenAPI serialized data: blob ${i} is not a Blob.`);
|
|
217
|
+
}
|
|
218
|
+
const segments = serialized.maps[i];
|
|
219
|
+
let original = serialized;
|
|
143
220
|
let currentRef = ref;
|
|
144
|
-
let preSegment = "
|
|
145
|
-
segments.
|
|
146
|
-
|
|
147
|
-
|
|
221
|
+
let preSegment = "json";
|
|
222
|
+
for (let j = 0; j < segments.length; j++) {
|
|
223
|
+
original = original[preSegment];
|
|
224
|
+
currentRef = copyOnWrite(currentRef, preSegment, original);
|
|
225
|
+
preSegment = segments[j];
|
|
148
226
|
if (!Object.hasOwn(currentRef, preSegment)) {
|
|
149
|
-
throw new
|
|
227
|
+
throw new TypeError(`Invalid OpenAPI serialized data: segment "${preSegment}" does not exist.`);
|
|
150
228
|
}
|
|
151
|
-
}
|
|
152
|
-
currentRef[preSegment] =
|
|
153
|
-
}
|
|
229
|
+
}
|
|
230
|
+
currentRef[preSegment] = blob;
|
|
231
|
+
}
|
|
154
232
|
}
|
|
155
|
-
return ref.
|
|
233
|
+
return ref.json;
|
|
156
234
|
}
|
|
157
235
|
}
|
|
158
236
|
|
|
@@ -166,34 +244,32 @@ class OpenAPISerializer {
|
|
|
166
244
|
this.defaultSerializeOptions = serialize;
|
|
167
245
|
}
|
|
168
246
|
serialize(data, options = {}) {
|
|
169
|
-
const useFormDataForBlobFields = options.useFormDataForBlobFields ?? this.defaultSerializeOptions?.useFormDataForBlobFields ?? true;
|
|
170
|
-
const asFormData = options.asFormData ?? this.defaultSerializeOptions?.asFormData ?? false;
|
|
171
247
|
if (!options.asFormData) {
|
|
172
248
|
if (data === void 0 || data instanceof ReadableStream || data instanceof Blob) {
|
|
173
249
|
return data;
|
|
174
250
|
}
|
|
175
251
|
if (isAsyncIteratorObject(data)) {
|
|
176
|
-
return
|
|
252
|
+
return wrapAsyncIteratorPreservingEventMeta(data, {
|
|
177
253
|
mapResult: (result) => {
|
|
178
254
|
if (result.value === void 0) {
|
|
179
255
|
return result;
|
|
180
256
|
}
|
|
181
|
-
return { done: result.done, value: this.serializeValue(result.value,
|
|
257
|
+
return { done: result.done, value: this.serializeValue(result.value, false, false) };
|
|
182
258
|
},
|
|
183
|
-
mapError: (e) =>
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
});
|
|
188
|
-
}
|
|
259
|
+
mapError: (e) => new ErrorEvent(
|
|
260
|
+
this.serializeValue(toORPCError(e).toJSON(), false, false),
|
|
261
|
+
{ cause: e }
|
|
262
|
+
)
|
|
189
263
|
});
|
|
190
264
|
}
|
|
191
265
|
}
|
|
192
|
-
|
|
266
|
+
const useFormDataForBlobFields = options.useFormDataForBlobFields ?? this.defaultSerializeOptions?.useFormDataForBlobFields ?? true;
|
|
267
|
+
const asFormData = options.asFormData ?? this.defaultSerializeOptions?.asFormData ?? false;
|
|
268
|
+
return this.serializeValue(data, useFormDataForBlobFields, asFormData);
|
|
193
269
|
}
|
|
194
|
-
serializeValue(value,
|
|
270
|
+
serializeValue(value, useFormDataForBlobFields, asFormData) {
|
|
195
271
|
const { json, blobs } = this.jsonSerializer.serialize(value);
|
|
196
|
-
if (!
|
|
272
|
+
if (!asFormData && (json instanceof Blob || json === void 0 || !blobs?.length || !useFormDataForBlobFields)) {
|
|
197
273
|
return json;
|
|
198
274
|
}
|
|
199
275
|
const form = new FormData();
|
|
@@ -211,7 +287,7 @@ class OpenAPISerializer {
|
|
|
211
287
|
return data;
|
|
212
288
|
}
|
|
213
289
|
if (isAsyncIteratorObject(data)) {
|
|
214
|
-
return
|
|
290
|
+
return wrapAsyncIteratorPreservingEventMeta(data, {
|
|
215
291
|
mapResult: (result) => {
|
|
216
292
|
if (result.value === void 0) {
|
|
217
293
|
return result;
|
|
@@ -236,6 +312,9 @@ class OpenAPISerializer {
|
|
|
236
312
|
}
|
|
237
313
|
}
|
|
238
314
|
|
|
315
|
+
function isBodylessMethod(method) {
|
|
316
|
+
return method === "GET" || method === "HEAD";
|
|
317
|
+
}
|
|
239
318
|
const PARAMETER_NAME_REGEX = /^[\w-]+$/;
|
|
240
319
|
function getDynamicPathParams(path) {
|
|
241
320
|
if (!path.includes("{")) {
|
|
@@ -272,4 +351,4 @@ function getDynamicPathParams(path) {
|
|
|
272
351
|
return params;
|
|
273
352
|
}
|
|
274
353
|
|
|
275
|
-
export { DEFAULT_OPENAPI_METHOD as D, OpenAPISerializer as O, DEFAULT_OPENAPI_INPUT_STRUCTURE as a, DEFAULT_OPENAPI_OUTPUT_STRUCTURE as b, DEFAULT_OPENAPI_SUCCESS_DESCRIPTION as c, OpenAPIJsonSerializer as d, getDynamicPathParams as g };
|
|
354
|
+
export { DEFAULT_OPENAPI_METHOD as D, OpenAPISerializer as O, DEFAULT_OPENAPI_INPUT_STRUCTURE as a, DEFAULT_OPENAPI_OUTPUT_STRUCTURE as b, DEFAULT_OPENAPI_SUCCESS_DESCRIPTION as c, OpenAPIJsonSerializer as d, getDynamicPathParams as g, isBodylessMethod as i };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StandardBody } from '@
|
|
1
|
+
import { StandardBody } from '@standard-server/core';
|
|
2
2
|
import { Segment } from '@orpc/shared';
|
|
3
3
|
|
|
4
4
|
type BracketNotationSerializeResult = [string, unknown][];
|
|
@@ -81,10 +81,10 @@ interface OpenAPIJsonSerializerOptions {
|
|
|
81
81
|
*
|
|
82
82
|
* **Disabling:** Set a key to `undefined` to remove a built-in handler:
|
|
83
83
|
* ```ts
|
|
84
|
-
* handlers: {
|
|
84
|
+
* handlers: { url: undefined }
|
|
85
85
|
* ```
|
|
86
86
|
*
|
|
87
|
-
* Built-in type keys: `undefined`, `bigint`, `date`, `nan`, `
|
|
87
|
+
* Built-in type keys: `undefined`, `bigint`, `date`, `nan`, `infinity`, `url`, `set`, `map`.
|
|
88
88
|
*/
|
|
89
89
|
handlers?: Record<string, undefined | OpenAPIJsonSerializerHandler> | undefined;
|
|
90
90
|
/**
|
|
@@ -95,10 +95,15 @@ interface OpenAPIJsonSerializerOptions {
|
|
|
95
95
|
omitUndefinedProperties?: boolean | undefined;
|
|
96
96
|
}
|
|
97
97
|
declare class OpenAPIJsonSerializer {
|
|
98
|
-
private readonly
|
|
98
|
+
private readonly inlineBuiltInHandlers;
|
|
99
|
+
private readonly handlerEntries;
|
|
99
100
|
private readonly omitUndefinedProperties;
|
|
100
101
|
constructor(options?: OpenAPIJsonSerializerOptions);
|
|
101
102
|
serialize(data: unknown): OpenAPIJsonSerialization;
|
|
103
|
+
/**
|
|
104
|
+
* `segments` is a shared mutable stack (push/pop while walking),
|
|
105
|
+
* so it must be copied before being stored in `maps`.
|
|
106
|
+
*/
|
|
102
107
|
private serializeValue;
|
|
103
108
|
deserialize(serialized: OpenAPIJsonSerialization): unknown;
|
|
104
109
|
}
|
|
@@ -118,7 +123,7 @@ interface OpenAPISerializerSerializeOptions {
|
|
|
118
123
|
*/
|
|
119
124
|
asFormData?: boolean | undefined;
|
|
120
125
|
}
|
|
121
|
-
interface OpenAPISerializerOptions extends OpenAPIJsonSerializerOptions
|
|
126
|
+
interface OpenAPISerializerOptions extends OpenAPIJsonSerializerOptions {
|
|
122
127
|
/**
|
|
123
128
|
* Options for bracket notation serializer, like maxExplicitDeserializingArrayIndex
|
|
124
129
|
*/
|
|
@@ -128,6 +133,12 @@ interface OpenAPISerializerOptions extends OpenAPIJsonSerializerOptions, OpenAPI
|
|
|
128
133
|
*/
|
|
129
134
|
serialize?: OpenAPISerializerSerializeOptions | undefined;
|
|
130
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Handles one-way serialization of oRPC payloads into JSON-friendly formats,
|
|
138
|
+
* partially supporting complex data types beyond plain JSON such as `Date`, `BigInt`, and `Set`.
|
|
139
|
+
*
|
|
140
|
+
* @see {@link https://orpc.dev/docs/openapi/serializer | OpenAPI Serializer}
|
|
141
|
+
*/
|
|
131
142
|
declare class OpenAPISerializer {
|
|
132
143
|
private readonly jsonSerializer;
|
|
133
144
|
private readonly bracketNotation;
|