@orpc/openapi 2.0.0-beta.4 → 2.0.0-beta.40
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 +694 -750
- package/dist/plugins/index.d.mts +21 -4
- package/dist/plugins/index.d.ts +21 -4
- package/dist/plugins/index.mjs +7 -2
- package/dist/shared/{openapi.DmAa7YPO.mjs → openapi.B1LWf28E.mjs} +134 -64
- package/dist/shared/openapi.B2G-HeFn.mjs +123 -0
- package/dist/shared/{openapi.B2SK0ZAr.mjs → openapi.BdClbrwk.mjs} +27 -35
- package/dist/shared/openapi.BwNINoeN.d.ts +45 -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.DE5kXinS.d.mts +45 -0
- package/dist/shared/openapi.DI21x5Yo.d.mts +31 -0
- package/dist/shared/openapi.DI21x5Yo.d.ts +31 -0
- package/dist/shared/{openapi.C7m7NAmH.d.mts → openapi.Dl3gew1x.d.mts} +16 -5
- package/dist/shared/{openapi.C7m7NAmH.d.ts → openapi.Dl3gew1x.d.ts} +16 -5
- package/dist/shared/{openapi.7vgmPxca.d.ts → openapi.EM8eC1Xn.d.ts} +12 -9
- package/dist/shared/{openapi.CX6Ri5dP.d.mts → openapi.fnBK1ELE.d.mts} +12 -9
- package/dist/shared/{openapi.CTlpLuKN.mjs → openapi.wQKxWEI2.mjs} +97 -48
- 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,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`, `url`, `
|
|
87
|
+
* Built-in type keys: `undefined`, `bigint`, `date`, `nan`, `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;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { AnyORPCError } from '@orpc/client';
|
|
2
2
|
import { AnyProcedure, AnyRouter, Context } from '@orpc/server';
|
|
3
|
-
import { StandardHandlerCodec,
|
|
4
|
-
import { Value, Promisable } from '@orpc/shared';
|
|
5
|
-
import { StandardLazyRequest, StandardResponse } from '@
|
|
3
|
+
import { StandardHandlerCodec, ResolvedStandardHandlerHandleOptions, StandardHandlerCodecResolvedProcedure } from '@orpc/server/standard';
|
|
4
|
+
import { Value, Public, Promisable } from '@orpc/shared';
|
|
5
|
+
import { StandardLazyRequest, StandardResponse } from '@standard-server/core';
|
|
6
6
|
import { AnyProcedureContract } from '@orpc/contract';
|
|
7
|
-
import { O as OpenAPISerializer } from './openapi.
|
|
7
|
+
import { O as OpenAPISerializer } from './openapi.Dl3gew1x.js';
|
|
8
8
|
|
|
9
9
|
interface OpenAPIMatcherOptions {
|
|
10
10
|
/**
|
|
@@ -18,7 +18,7 @@ declare class OpenAPIMatcher {
|
|
|
18
18
|
private readonly filter;
|
|
19
19
|
private readonly rootRouter;
|
|
20
20
|
private readonly tree;
|
|
21
|
-
private pendingLazyRouters;
|
|
21
|
+
private readonly pendingLazyRouters;
|
|
22
22
|
constructor(router: AnyRouter, options?: OpenAPIMatcherOptions);
|
|
23
23
|
private index;
|
|
24
24
|
match(method: string, pathname: `/${string}`, prefix: `/${string}` | undefined): Promise<{
|
|
@@ -26,8 +26,10 @@ declare class OpenAPIMatcher {
|
|
|
26
26
|
procedure: AnyProcedure;
|
|
27
27
|
params?: Record<string, string> | undefined;
|
|
28
28
|
} | undefined>;
|
|
29
|
-
private matchPathname;
|
|
30
29
|
private resolvePendingLazyRouters;
|
|
30
|
+
private loadPendingLazyRouters;
|
|
31
|
+
private loadPendingLazyRouter;
|
|
32
|
+
private indexPendingLazyRouter;
|
|
31
33
|
private resolveProcedure;
|
|
32
34
|
}
|
|
33
35
|
|
|
@@ -35,11 +37,12 @@ interface OpenAPIHandlerCodecCoreOptions<_T extends Context> {
|
|
|
35
37
|
/**
|
|
36
38
|
* Override the default OpenAPI serializer.
|
|
37
39
|
*/
|
|
38
|
-
serializer?:
|
|
40
|
+
serializer?: Public<OpenAPISerializer>;
|
|
39
41
|
/**
|
|
40
42
|
* Mapping ORPCError Code -> HTTP Status Code
|
|
43
|
+
* The status code should be in the `4xx` or `5xx` range (must be greater than or equal to `400`).
|
|
41
44
|
*
|
|
42
|
-
* @default COMMON_ERROR_STATUS_MAP
|
|
45
|
+
* @default COMMON_ERROR_STATUS_MAP
|
|
43
46
|
*/
|
|
44
47
|
errorStatusMap?: Record<string, number> | undefined;
|
|
45
48
|
/**
|
|
@@ -75,7 +78,7 @@ interface OpenAPIHandlerCodecOptions<T extends Context> extends OpenAPIHandlerCo
|
|
|
75
78
|
declare class OpenAPIHandlerCodec<T extends Context> extends OpenAPIHandlerCodecCore<T> implements StandardHandlerCodec<T> {
|
|
76
79
|
private readonly matcher;
|
|
77
80
|
constructor(router: AnyRouter, options?: OpenAPIHandlerCodecOptions<T>);
|
|
78
|
-
resolveProcedure(request: StandardLazyRequest, options:
|
|
81
|
+
resolveProcedure(request: StandardLazyRequest, options: ResolvedStandardHandlerHandleOptions<T>): Promise<StandardHandlerCodecResolvedProcedure | undefined>;
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
export { OpenAPIHandlerCodec as a, OpenAPIHandlerCodecCore as b, OpenAPIMatcher as d };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { AnyORPCError } from '@orpc/client';
|
|
2
2
|
import { AnyProcedure, AnyRouter, Context } from '@orpc/server';
|
|
3
|
-
import { StandardHandlerCodec,
|
|
4
|
-
import { Value, Promisable } from '@orpc/shared';
|
|
5
|
-
import { StandardLazyRequest, StandardResponse } from '@
|
|
3
|
+
import { StandardHandlerCodec, ResolvedStandardHandlerHandleOptions, StandardHandlerCodecResolvedProcedure } from '@orpc/server/standard';
|
|
4
|
+
import { Value, Public, Promisable } from '@orpc/shared';
|
|
5
|
+
import { StandardLazyRequest, StandardResponse } from '@standard-server/core';
|
|
6
6
|
import { AnyProcedureContract } from '@orpc/contract';
|
|
7
|
-
import { O as OpenAPISerializer } from './openapi.
|
|
7
|
+
import { O as OpenAPISerializer } from './openapi.Dl3gew1x.mjs';
|
|
8
8
|
|
|
9
9
|
interface OpenAPIMatcherOptions {
|
|
10
10
|
/**
|
|
@@ -18,7 +18,7 @@ declare class OpenAPIMatcher {
|
|
|
18
18
|
private readonly filter;
|
|
19
19
|
private readonly rootRouter;
|
|
20
20
|
private readonly tree;
|
|
21
|
-
private pendingLazyRouters;
|
|
21
|
+
private readonly pendingLazyRouters;
|
|
22
22
|
constructor(router: AnyRouter, options?: OpenAPIMatcherOptions);
|
|
23
23
|
private index;
|
|
24
24
|
match(method: string, pathname: `/${string}`, prefix: `/${string}` | undefined): Promise<{
|
|
@@ -26,8 +26,10 @@ declare class OpenAPIMatcher {
|
|
|
26
26
|
procedure: AnyProcedure;
|
|
27
27
|
params?: Record<string, string> | undefined;
|
|
28
28
|
} | undefined>;
|
|
29
|
-
private matchPathname;
|
|
30
29
|
private resolvePendingLazyRouters;
|
|
30
|
+
private loadPendingLazyRouters;
|
|
31
|
+
private loadPendingLazyRouter;
|
|
32
|
+
private indexPendingLazyRouter;
|
|
31
33
|
private resolveProcedure;
|
|
32
34
|
}
|
|
33
35
|
|
|
@@ -35,11 +37,12 @@ interface OpenAPIHandlerCodecCoreOptions<_T extends Context> {
|
|
|
35
37
|
/**
|
|
36
38
|
* Override the default OpenAPI serializer.
|
|
37
39
|
*/
|
|
38
|
-
serializer?:
|
|
40
|
+
serializer?: Public<OpenAPISerializer>;
|
|
39
41
|
/**
|
|
40
42
|
* Mapping ORPCError Code -> HTTP Status Code
|
|
43
|
+
* The status code should be in the `4xx` or `5xx` range (must be greater than or equal to `400`).
|
|
41
44
|
*
|
|
42
|
-
* @default COMMON_ERROR_STATUS_MAP
|
|
45
|
+
* @default COMMON_ERROR_STATUS_MAP
|
|
43
46
|
*/
|
|
44
47
|
errorStatusMap?: Record<string, number> | undefined;
|
|
45
48
|
/**
|
|
@@ -75,7 +78,7 @@ interface OpenAPIHandlerCodecOptions<T extends Context> extends OpenAPIHandlerCo
|
|
|
75
78
|
declare class OpenAPIHandlerCodec<T extends Context> extends OpenAPIHandlerCodecCore<T> implements StandardHandlerCodec<T> {
|
|
76
79
|
private readonly matcher;
|
|
77
80
|
constructor(router: AnyRouter, options?: OpenAPIHandlerCodecOptions<T>);
|
|
78
|
-
resolveProcedure(request: StandardLazyRequest, options:
|
|
81
|
+
resolveProcedure(request: StandardLazyRequest, options: ResolvedStandardHandlerHandleOptions<T>): Promise<StandardHandlerCodecResolvedProcedure | undefined>;
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
export { OpenAPIHandlerCodec as a, OpenAPIHandlerCodecCore as b, OpenAPIMatcher as d };
|
|
@@ -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.B1LWf28E.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 {
|
|
@@ -140,20 +189,20 @@ class OpenAPIHandlerCodecCore {
|
|
|
140
189
|
const params = this.deserializeParams(matched.params, meta?.paramsStyles);
|
|
141
190
|
const query = this.deserializeQuery(search, meta?.queryStyles);
|
|
142
191
|
if (inputStructure === "compact") {
|
|
143
|
-
const data = request.method
|
|
192
|
+
const data = isBodylessMethod(request.method) ? query : this.serializer.deserialize(await request.resolveBody(meta?.requestBodyHint));
|
|
144
193
|
if (data === void 0) {
|
|
145
194
|
return params;
|
|
146
195
|
}
|
|
147
196
|
if (!params || Object.keys(params).length < 1) {
|
|
148
197
|
return data;
|
|
149
198
|
}
|
|
150
|
-
if (isPlainObject(data)) {
|
|
151
|
-
return
|
|
152
|
-
...params,
|
|
153
|
-
...data
|
|
154
|
-
};
|
|
199
|
+
if (!isPlainObject(data)) {
|
|
200
|
+
return params;
|
|
155
201
|
}
|
|
156
|
-
return
|
|
202
|
+
return {
|
|
203
|
+
...data,
|
|
204
|
+
...params
|
|
205
|
+
};
|
|
157
206
|
}
|
|
158
207
|
return {
|
|
159
208
|
params,
|
|
@@ -180,8 +229,8 @@ class OpenAPIHandlerCodecCore {
|
|
|
180
229
|
throw new TypeError(`
|
|
181
230
|
Invalid "detailed" output structure returned by procedure (${path.join(".")}):
|
|
182
231
|
\u2022 Expected an object with optional properties:
|
|
183
|
-
- status (number
|
|
184
|
-
- headers (
|
|
232
|
+
- status (number <400)
|
|
233
|
+
- headers (object)
|
|
185
234
|
- body (any)
|
|
186
235
|
\u2022 No extra keys allowed.
|
|
187
236
|
|
|
@@ -191,12 +240,12 @@ class OpenAPIHandlerCodecCore {
|
|
|
191
240
|
}
|
|
192
241
|
return {
|
|
193
242
|
status: output.status ?? successStatus,
|
|
194
|
-
headers: output.headers
|
|
243
|
+
headers: output.headers !== void 0 ? serializeHeaders(output.headers, this.serializer) : {},
|
|
195
244
|
body: this.serializer.serialize(output.body)
|
|
196
245
|
};
|
|
197
246
|
}
|
|
198
247
|
encodeError(error) {
|
|
199
|
-
const status = this.errorStatusMap
|
|
248
|
+
const status = getOwn(this.errorStatusMap, error.code) ?? DEFAULT_ERROR_STATUS;
|
|
200
249
|
return {
|
|
201
250
|
status,
|
|
202
251
|
headers: {},
|
|
@@ -288,10 +337,10 @@ function isValidDetailedOutput(output) {
|
|
|
288
337
|
if (Object.keys(output).some((key) => key !== "status" && key !== "headers" && key !== "body")) {
|
|
289
338
|
return false;
|
|
290
339
|
}
|
|
291
|
-
if (output.status !== void 0 && (typeof output.status !== "number" || !Number.isInteger(output.status) || output.status
|
|
340
|
+
if (output.status !== void 0 && (typeof output.status !== "number" || !Number.isInteger(output.status) || output.status > 399)) {
|
|
292
341
|
return false;
|
|
293
342
|
}
|
|
294
|
-
if (output.headers !== void 0 && !
|
|
343
|
+
if (output.headers !== void 0 && !isTypescriptObject(output.headers)) {
|
|
295
344
|
return false;
|
|
296
345
|
}
|
|
297
346
|
return true;
|
|
@@ -315,4 +364,4 @@ function decodeDelimitedObject(value, delimiter) {
|
|
|
315
364
|
return obj;
|
|
316
365
|
}
|
|
317
366
|
|
|
318
|
-
export { OpenAPIHandlerCodec as O, OpenAPIHandlerCodecCore as a, OpenAPIMatcher as b };
|
|
367
|
+
export { OpenAPIHandlerCodec as O, OpenAPIHandlerCodecCore as a, OpenAPIMatcher as b, serializeHeaders as s };
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orpc/openapi",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.0.0-beta.
|
|
4
|
+
"version": "2.0.0-beta.40",
|
|
5
|
+
"description": "Generate OpenAPI specs from oRPC routers and serve OpenAPI-compliant APIs with end-to-end type safety",
|
|
5
6
|
"license": "MIT",
|
|
7
|
+
"funding": [
|
|
8
|
+
"https://github.com/sponsors/dinwwwh",
|
|
9
|
+
"https://opencollective.com/middleapi"
|
|
10
|
+
],
|
|
6
11
|
"homepage": "https://orpc.dev",
|
|
7
12
|
"repository": {
|
|
8
13
|
"type": "git",
|
|
@@ -11,7 +16,15 @@
|
|
|
11
16
|
},
|
|
12
17
|
"keywords": [
|
|
13
18
|
"orpc",
|
|
14
|
-
"
|
|
19
|
+
"rpc",
|
|
20
|
+
"api",
|
|
21
|
+
"typescript",
|
|
22
|
+
"typesafe",
|
|
23
|
+
"openapi",
|
|
24
|
+
"swagger",
|
|
25
|
+
"scalar",
|
|
26
|
+
"json-schema",
|
|
27
|
+
"rest"
|
|
15
28
|
],
|
|
16
29
|
"sideEffects": [
|
|
17
30
|
"./dist/extensions/route.mjs"
|
|
@@ -48,6 +61,16 @@
|
|
|
48
61
|
"import": "./dist/adapters/node/index.mjs",
|
|
49
62
|
"default": "./dist/adapters/node/index.mjs"
|
|
50
63
|
},
|
|
64
|
+
"./aws-lambda": {
|
|
65
|
+
"types": "./dist/adapters/aws-lambda/index.d.mts",
|
|
66
|
+
"import": "./dist/adapters/aws-lambda/index.mjs",
|
|
67
|
+
"default": "./dist/adapters/aws-lambda/index.mjs"
|
|
68
|
+
},
|
|
69
|
+
"./fastify": {
|
|
70
|
+
"types": "./dist/adapters/fastify/index.d.mts",
|
|
71
|
+
"import": "./dist/adapters/fastify/index.mjs",
|
|
72
|
+
"default": "./dist/adapters/fastify/index.mjs"
|
|
73
|
+
},
|
|
51
74
|
"./extensions/route": {
|
|
52
75
|
"types": "./dist/extensions/route.d.mts",
|
|
53
76
|
"import": "./dist/extensions/route.mjs",
|
|
@@ -57,26 +80,43 @@
|
|
|
57
80
|
"files": [
|
|
58
81
|
"dist"
|
|
59
82
|
],
|
|
83
|
+
"peerDependencies": {
|
|
84
|
+
"@scalar/api-reference": ">=1.57.2",
|
|
85
|
+
"@types/swagger-ui": ">=5.32.0",
|
|
86
|
+
"swagger-ui": ">=5.32.6"
|
|
87
|
+
},
|
|
88
|
+
"peerDependenciesMeta": {
|
|
89
|
+
"@scalar/api-reference": {
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
92
|
+
"@types/swagger-ui": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"swagger-ui": {
|
|
96
|
+
"optional": true
|
|
97
|
+
}
|
|
98
|
+
},
|
|
60
99
|
"dependencies": {
|
|
61
|
-
"@
|
|
62
|
-
"@
|
|
63
|
-
"@
|
|
64
|
-
"@
|
|
65
|
-
"@
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"@
|
|
69
|
-
"@
|
|
70
|
-
"
|
|
71
|
-
"@orpc/json-schema": "2.0.0-beta.4",
|
|
72
|
-
"@orpc/shared": "2.0.0-beta.4"
|
|
100
|
+
"@openapi-spec/downgrader": "~0.1.0",
|
|
101
|
+
"@openapi-spec/types": "~0.1.0",
|
|
102
|
+
"@orpc/client": "2.0.0-beta.40",
|
|
103
|
+
"@orpc/contract": "2.0.0-beta.40",
|
|
104
|
+
"@orpc/json-schema": "2.0.0-beta.40",
|
|
105
|
+
"@orpc/server": "2.0.0-beta.40",
|
|
106
|
+
"@orpc/shared": "2.0.0-beta.40",
|
|
107
|
+
"@standard-server/core": "~0.9.2",
|
|
108
|
+
"@standard-server/fetch": "~0.9.2",
|
|
109
|
+
"rou3": "^0.9.1"
|
|
73
110
|
},
|
|
74
111
|
"devDependencies": {
|
|
75
|
-
"
|
|
76
|
-
"
|
|
112
|
+
"@scalar/api-reference": "^1.65.1",
|
|
113
|
+
"@standard-server/aws-lambda": "~0.9.2",
|
|
114
|
+
"@types/swagger-ui": "^5.32.0",
|
|
115
|
+
"fastify": "^5.12.1",
|
|
116
|
+
"swagger-ui": "^5.32.14",
|
|
117
|
+
"zod": "^4.5.4"
|
|
77
118
|
},
|
|
78
119
|
"scripts": {
|
|
79
|
-
"build": "unbuild",
|
|
80
120
|
"type:check": "tsc -b"
|
|
81
121
|
}
|
|
82
122
|
}
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { isPlainObject, NullProtoObj } from '@orpc/shared';
|
|
2
|
-
|
|
3
|
-
class BracketNotationSerializer {
|
|
4
|
-
maxExplicitDeserializingArrayIndex;
|
|
5
|
-
constructor(options = {}) {
|
|
6
|
-
this.maxExplicitDeserializingArrayIndex = options.maxExplicitDeserializingArrayIndex ?? 999;
|
|
7
|
-
}
|
|
8
|
-
serialize(data) {
|
|
9
|
-
return this.internalSerialize(data, [], []);
|
|
10
|
-
}
|
|
11
|
-
internalSerialize(data, segments, result) {
|
|
12
|
-
if (Array.isArray(data)) {
|
|
13
|
-
data.forEach((item, i) => {
|
|
14
|
-
this.internalSerialize(item, [...segments, i], result);
|
|
15
|
-
});
|
|
16
|
-
} else if (isPlainObject(data)) {
|
|
17
|
-
for (const key in data) {
|
|
18
|
-
this.internalSerialize(data[key], [...segments, key], result);
|
|
19
|
-
}
|
|
20
|
-
} else {
|
|
21
|
-
result.push([this.stringifyPath(segments), data]);
|
|
22
|
-
}
|
|
23
|
-
return result;
|
|
24
|
-
}
|
|
25
|
-
deserialize(serialized) {
|
|
26
|
-
if (serialized.length === 0) {
|
|
27
|
-
return new NullProtoObj();
|
|
28
|
-
}
|
|
29
|
-
const arrayPushStyles = /* @__PURE__ */ new WeakSet();
|
|
30
|
-
const ref = { value: new NullProtoObj() };
|
|
31
|
-
for (const [path, value] of serialized) {
|
|
32
|
-
const segments = this.parsePath(path);
|
|
33
|
-
let currentRef = ref;
|
|
34
|
-
let nextSegment = "value";
|
|
35
|
-
segments.forEach((segment, i) => {
|
|
36
|
-
if (!Array.isArray(currentRef[nextSegment]) && !isPlainObject(currentRef[nextSegment])) {
|
|
37
|
-
currentRef[nextSegment] = [];
|
|
38
|
-
}
|
|
39
|
-
if (i !== segments.length - 1) {
|
|
40
|
-
if (Array.isArray(currentRef[nextSegment]) && !internalIsValidArrayIndex(segment, this.maxExplicitDeserializingArrayIndex)) {
|
|
41
|
-
if (arrayPushStyles.has(currentRef[nextSegment])) {
|
|
42
|
-
arrayPushStyles.delete(currentRef[nextSegment]);
|
|
43
|
-
currentRef[nextSegment] = internalPushStyleArrayToObject(currentRef[nextSegment]);
|
|
44
|
-
} else {
|
|
45
|
-
currentRef[nextSegment] = internalArrayToObject(currentRef[nextSegment]);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
} else {
|
|
49
|
-
if (Array.isArray(currentRef[nextSegment])) {
|
|
50
|
-
if (segment === "") {
|
|
51
|
-
if (currentRef[nextSegment].length && !arrayPushStyles.has(currentRef[nextSegment])) {
|
|
52
|
-
currentRef[nextSegment] = internalArrayToObject(currentRef[nextSegment]);
|
|
53
|
-
}
|
|
54
|
-
} else {
|
|
55
|
-
if (arrayPushStyles.has(currentRef[nextSegment])) {
|
|
56
|
-
arrayPushStyles.delete(currentRef[nextSegment]);
|
|
57
|
-
currentRef[nextSegment] = internalPushStyleArrayToObject(currentRef[nextSegment]);
|
|
58
|
-
} else if (!internalIsValidArrayIndex(segment, this.maxExplicitDeserializingArrayIndex)) {
|
|
59
|
-
currentRef[nextSegment] = internalArrayToObject(currentRef[nextSegment]);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
currentRef = currentRef[nextSegment];
|
|
65
|
-
nextSegment = segment;
|
|
66
|
-
});
|
|
67
|
-
if (Array.isArray(currentRef) && nextSegment === "") {
|
|
68
|
-
arrayPushStyles.add(currentRef);
|
|
69
|
-
currentRef.push(value);
|
|
70
|
-
} else if (nextSegment in currentRef) {
|
|
71
|
-
if (Array.isArray(currentRef[nextSegment])) {
|
|
72
|
-
currentRef[nextSegment].push(value);
|
|
73
|
-
} else {
|
|
74
|
-
currentRef[nextSegment] = [currentRef[nextSegment], value];
|
|
75
|
-
}
|
|
76
|
-
} else {
|
|
77
|
-
currentRef[nextSegment] = value;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return ref.value;
|
|
81
|
-
}
|
|
82
|
-
stringifyPath(segments) {
|
|
83
|
-
return segments.reduce((result, segment, i) => {
|
|
84
|
-
if (i === 0) {
|
|
85
|
-
return segment.toString();
|
|
86
|
-
}
|
|
87
|
-
return `${result}[${segment}]`;
|
|
88
|
-
}, "");
|
|
89
|
-
}
|
|
90
|
-
parsePath(path) {
|
|
91
|
-
const segments = [];
|
|
92
|
-
let inBrackets = false;
|
|
93
|
-
let currentSegment = "";
|
|
94
|
-
for (let i = 0; i < path.length; i++) {
|
|
95
|
-
const char = path[i];
|
|
96
|
-
const nextChar = path[i + 1];
|
|
97
|
-
if (inBrackets && char === "]" && (nextChar === void 0 || nextChar === "[")) {
|
|
98
|
-
if (nextChar === void 0) {
|
|
99
|
-
inBrackets = false;
|
|
100
|
-
}
|
|
101
|
-
segments.push(currentSegment);
|
|
102
|
-
currentSegment = "";
|
|
103
|
-
i++;
|
|
104
|
-
} else if (segments.length === 0 && char === "[") {
|
|
105
|
-
inBrackets = true;
|
|
106
|
-
segments.push(currentSegment);
|
|
107
|
-
currentSegment = "";
|
|
108
|
-
} else {
|
|
109
|
-
currentSegment += char;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return inBrackets || segments.length === 0 ? [path] : segments;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function internalIsValidArrayIndex(value, maxIndex) {
|
|
116
|
-
return /^0$|^[1-9]\d*$/.test(value) && Number(value) <= maxIndex;
|
|
117
|
-
}
|
|
118
|
-
function internalArrayToObject(array) {
|
|
119
|
-
const obj = new NullProtoObj();
|
|
120
|
-
array.forEach((item, i) => {
|
|
121
|
-
obj[i] = item;
|
|
122
|
-
});
|
|
123
|
-
return obj;
|
|
124
|
-
}
|
|
125
|
-
function internalPushStyleArrayToObject(array) {
|
|
126
|
-
const obj = new NullProtoObj();
|
|
127
|
-
obj[""] = array.length === 1 ? array[0] : array;
|
|
128
|
-
return obj;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export { BracketNotationSerializer as B };
|
|
@@ -1,18 +0,0 @@
|
|
|
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 };
|