@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
package/dist/plugins/index.d.mts
CHANGED
|
@@ -2,10 +2,10 @@ import { Context } from '@orpc/server';
|
|
|
2
2
|
import { StandardHandlerPlugin, StandardHandlerRoutingInterceptorOptions, StandardHandlerOptions } from '@orpc/server/standard';
|
|
3
3
|
import { Value, Promisable } from '@orpc/shared';
|
|
4
4
|
import { ApiReferenceConfiguration } from '@scalar/api-reference';
|
|
5
|
-
import { StandardUrl } from '@
|
|
5
|
+
import { StandardUrl } from '@standard-server/core';
|
|
6
6
|
import { SwaggerUIOptions } from 'swagger-ui';
|
|
7
|
-
import {
|
|
8
|
-
import '@
|
|
7
|
+
import { OpenAPIDocument, OpenAPIVersion } from '../index.mjs';
|
|
8
|
+
import '@openapi-spec/types';
|
|
9
9
|
import '@orpc/client';
|
|
10
10
|
|
|
11
11
|
type OpenAPIReferenceHandlerPluginProvider = 'scalar' | 'swagger';
|
|
@@ -21,7 +21,14 @@ interface OpenAPIReferenceHandlerPluginOptions<T extends Context, TProvider exte
|
|
|
21
21
|
* A static or dynamic OpenAPI document to serve.
|
|
22
22
|
* Receives routing interceptor options when provided as a function.
|
|
23
23
|
*/
|
|
24
|
-
spec: Value<Promisable<OpenAPIDocument
|
|
24
|
+
spec: Value<Promisable<OpenAPIDocument<OpenAPIVersion>>, [StandardHandlerRoutingInterceptorOptions<T>]>;
|
|
25
|
+
/**
|
|
26
|
+
* Determines whether the docs UI and OpenAPI JSON are allowed to be served for a request.
|
|
27
|
+
* When it resolves to `false`, the request falls through as unmatched,
|
|
28
|
+
* as if the plugin were not installed. Useful for restricting access
|
|
29
|
+
* to authenticated users.
|
|
30
|
+
*/
|
|
31
|
+
allow?: Value<Promisable<boolean>, [StandardHandlerRoutingInterceptorOptions<T>]>;
|
|
25
32
|
/**
|
|
26
33
|
* The URL path at which to serve the OpenAPI JSON.
|
|
27
34
|
*
|
|
@@ -71,9 +78,19 @@ interface OpenAPIReferenceHandlerPluginOptions<T extends Context, TProvider exte
|
|
|
71
78
|
*/
|
|
72
79
|
docsHead?: Value<Promisable<string>, [StandardHandlerRoutingInterceptorOptions<T>]>;
|
|
73
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Serves API reference documentation powered by Scalar or Swagger UI,
|
|
83
|
+
* and exposes the OpenAPI specification as JSON.
|
|
84
|
+
*
|
|
85
|
+
* @remarks
|
|
86
|
+
* **Note**: By default, the API reference UI is served from `/` and the OpenAPI specification from `/spec.json`.
|
|
87
|
+
*
|
|
88
|
+
* @see {@link https://orpc.dev/docs/plugins/openapi-reference | OpenAPI Reference Plugin (Swagger/Scalar)}
|
|
89
|
+
*/
|
|
74
90
|
declare class OpenAPIReferenceHandlerPlugin<T extends Context, TProvider extends OpenAPIReferenceHandlerPluginProvider> implements StandardHandlerPlugin<T> {
|
|
75
91
|
name: string;
|
|
76
92
|
private readonly spec;
|
|
93
|
+
private readonly allow;
|
|
77
94
|
private readonly specPath;
|
|
78
95
|
private readonly provider;
|
|
79
96
|
private readonly providerConfig;
|
package/dist/plugins/index.d.ts
CHANGED
|
@@ -2,10 +2,10 @@ import { Context } from '@orpc/server';
|
|
|
2
2
|
import { StandardHandlerPlugin, StandardHandlerRoutingInterceptorOptions, StandardHandlerOptions } from '@orpc/server/standard';
|
|
3
3
|
import { Value, Promisable } from '@orpc/shared';
|
|
4
4
|
import { ApiReferenceConfiguration } from '@scalar/api-reference';
|
|
5
|
-
import { StandardUrl } from '@
|
|
5
|
+
import { StandardUrl } from '@standard-server/core';
|
|
6
6
|
import { SwaggerUIOptions } from 'swagger-ui';
|
|
7
|
-
import {
|
|
8
|
-
import '@
|
|
7
|
+
import { OpenAPIDocument, OpenAPIVersion } from '../index.js';
|
|
8
|
+
import '@openapi-spec/types';
|
|
9
9
|
import '@orpc/client';
|
|
10
10
|
|
|
11
11
|
type OpenAPIReferenceHandlerPluginProvider = 'scalar' | 'swagger';
|
|
@@ -21,7 +21,14 @@ interface OpenAPIReferenceHandlerPluginOptions<T extends Context, TProvider exte
|
|
|
21
21
|
* A static or dynamic OpenAPI document to serve.
|
|
22
22
|
* Receives routing interceptor options when provided as a function.
|
|
23
23
|
*/
|
|
24
|
-
spec: Value<Promisable<OpenAPIDocument
|
|
24
|
+
spec: Value<Promisable<OpenAPIDocument<OpenAPIVersion>>, [StandardHandlerRoutingInterceptorOptions<T>]>;
|
|
25
|
+
/**
|
|
26
|
+
* Determines whether the docs UI and OpenAPI JSON are allowed to be served for a request.
|
|
27
|
+
* When it resolves to `false`, the request falls through as unmatched,
|
|
28
|
+
* as if the plugin were not installed. Useful for restricting access
|
|
29
|
+
* to authenticated users.
|
|
30
|
+
*/
|
|
31
|
+
allow?: Value<Promisable<boolean>, [StandardHandlerRoutingInterceptorOptions<T>]>;
|
|
25
32
|
/**
|
|
26
33
|
* The URL path at which to serve the OpenAPI JSON.
|
|
27
34
|
*
|
|
@@ -71,9 +78,19 @@ interface OpenAPIReferenceHandlerPluginOptions<T extends Context, TProvider exte
|
|
|
71
78
|
*/
|
|
72
79
|
docsHead?: Value<Promisable<string>, [StandardHandlerRoutingInterceptorOptions<T>]>;
|
|
73
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Serves API reference documentation powered by Scalar or Swagger UI,
|
|
83
|
+
* and exposes the OpenAPI specification as JSON.
|
|
84
|
+
*
|
|
85
|
+
* @remarks
|
|
86
|
+
* **Note**: By default, the API reference UI is served from `/` and the OpenAPI specification from `/spec.json`.
|
|
87
|
+
*
|
|
88
|
+
* @see {@link https://orpc.dev/docs/plugins/openapi-reference | OpenAPI Reference Plugin (Swagger/Scalar)}
|
|
89
|
+
*/
|
|
74
90
|
declare class OpenAPIReferenceHandlerPlugin<T extends Context, TProvider extends OpenAPIReferenceHandlerPluginProvider> implements StandardHandlerPlugin<T> {
|
|
75
91
|
name: string;
|
|
76
92
|
private readonly spec;
|
|
93
|
+
private readonly allow;
|
|
77
94
|
private readonly specPath;
|
|
78
95
|
private readonly provider;
|
|
79
96
|
private readonly providerConfig;
|
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { toArray, matchesHttpPath, mergeHttpPath,
|
|
1
|
+
import { toArray, matchesHttpPath, mergeHttpPath, value, getTracer, stringifyJSON } from '@orpc/shared';
|
|
2
2
|
|
|
3
3
|
class OpenAPIReferenceHandlerPlugin {
|
|
4
4
|
name = "~openapi-reference";
|
|
5
5
|
spec;
|
|
6
|
+
allow;
|
|
6
7
|
specPath;
|
|
7
8
|
provider;
|
|
8
9
|
providerConfig;
|
|
@@ -13,6 +14,7 @@ class OpenAPIReferenceHandlerPlugin {
|
|
|
13
14
|
docsHead;
|
|
14
15
|
constructor(options) {
|
|
15
16
|
this.spec = options.spec;
|
|
17
|
+
this.allow = options.allow;
|
|
16
18
|
this.specPath = options.specPath ?? "/spec.json";
|
|
17
19
|
this.provider = options.provider ?? "scalar";
|
|
18
20
|
this.providerConfig = options.providerConfig;
|
|
@@ -44,7 +46,10 @@ class OpenAPIReferenceHandlerPlugin {
|
|
|
44
46
|
if (!isSpecPath && !isDocsPath) {
|
|
45
47
|
return result;
|
|
46
48
|
}
|
|
47
|
-
|
|
49
|
+
if (await value(this.allow, routingInterceptorOptions) === false) {
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
const span = getTracer()?.getActiveSpan();
|
|
48
53
|
const spec = await value(this.spec, routingInterceptorOptions);
|
|
49
54
|
if (isSpecPath) {
|
|
50
55
|
span?.updateName(`${routingInterceptorOptions.request.method} ${routingInterceptorOptions.request.url} (openapi spec)`);
|
|
@@ -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";
|
|
@@ -57,15 +57,6 @@ const DEFAULT_OPEN_API_JSON_SERIALIZER_HANDLERS = {
|
|
|
57
57
|
},
|
|
58
58
|
isTerminal: true
|
|
59
59
|
},
|
|
60
|
-
regexp: {
|
|
61
|
-
condition(data) {
|
|
62
|
-
return data instanceof RegExp;
|
|
63
|
-
},
|
|
64
|
-
serialize(data) {
|
|
65
|
-
return data.toString();
|
|
66
|
-
},
|
|
67
|
-
isTerminal: true
|
|
68
|
-
},
|
|
69
60
|
set: {
|
|
70
61
|
condition(data) {
|
|
71
62
|
return data instanceof Set;
|
|
@@ -84,45 +75,114 @@ const DEFAULT_OPEN_API_JSON_SERIALIZER_HANDLERS = {
|
|
|
84
75
|
}
|
|
85
76
|
};
|
|
86
77
|
class OpenAPIJsonSerializer {
|
|
87
|
-
|
|
78
|
+
inlineBuiltInHandlers;
|
|
79
|
+
handlerEntries;
|
|
88
80
|
omitUndefinedProperties;
|
|
89
81
|
constructor(options = {}) {
|
|
90
|
-
this.handlers = {
|
|
91
|
-
...DEFAULT_OPEN_API_JSON_SERIALIZER_HANDLERS,
|
|
92
|
-
...options.handlers
|
|
93
|
-
};
|
|
94
82
|
this.omitUndefinedProperties = options.omitUndefinedProperties !== false;
|
|
83
|
+
const customHandlers = options.handlers;
|
|
84
|
+
if (customHandlers === void 0) {
|
|
85
|
+
this.inlineBuiltInHandlers = true;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
let inlineBuiltInHandlers = true;
|
|
89
|
+
let handlerEntries = [];
|
|
90
|
+
for (const key of Object.keys(customHandlers)) {
|
|
91
|
+
const handler = customHandlers[key];
|
|
92
|
+
if (inlineBuiltInHandlers && key in DEFAULT_OPEN_API_JSON_SERIALIZER_HANDLERS) {
|
|
93
|
+
inlineBuiltInHandlers = false;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
if (handler !== void 0) {
|
|
97
|
+
handlerEntries.push(handler);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!inlineBuiltInHandlers) {
|
|
101
|
+
handlerEntries = [];
|
|
102
|
+
for (const handler of Object.values({ ...DEFAULT_OPEN_API_JSON_SERIALIZER_HANDLERS, ...customHandlers })) {
|
|
103
|
+
if (handler !== void 0) {
|
|
104
|
+
handlerEntries.push(handler);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
this.inlineBuiltInHandlers = inlineBuiltInHandlers;
|
|
109
|
+
this.handlerEntries = handlerEntries;
|
|
95
110
|
}
|
|
96
111
|
serialize(data) {
|
|
97
|
-
const
|
|
112
|
+
const maps = [];
|
|
113
|
+
const blobs = [];
|
|
114
|
+
const json = this.serializeValue(data, [], maps, blobs);
|
|
98
115
|
return { json, maps, blobs };
|
|
99
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* `segments` is a shared mutable stack (push/pop while walking),
|
|
119
|
+
* so it must be copied before being stored in `maps`.
|
|
120
|
+
*/
|
|
100
121
|
serializeValue(data, segments, maps, blobs) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
122
|
+
if (this.inlineBuiltInHandlers) {
|
|
123
|
+
switch (typeof data) {
|
|
124
|
+
case "string":
|
|
125
|
+
case "boolean":
|
|
126
|
+
return data;
|
|
127
|
+
case "number":
|
|
128
|
+
return Number.isNaN(data) ? null : data;
|
|
129
|
+
case "undefined":
|
|
130
|
+
return null;
|
|
131
|
+
case "bigint":
|
|
132
|
+
return data.toString();
|
|
133
|
+
case "object": {
|
|
134
|
+
if (data === null) {
|
|
135
|
+
return data;
|
|
136
|
+
}
|
|
137
|
+
if (data instanceof Date) {
|
|
138
|
+
return Number.isNaN(data.getTime()) ? null : data.toISOString();
|
|
139
|
+
}
|
|
140
|
+
if (data instanceof URL) {
|
|
141
|
+
return data.toString();
|
|
142
|
+
}
|
|
143
|
+
if (data instanceof Set) {
|
|
144
|
+
return this.serializeValue(Array.from(data), segments, maps, blobs);
|
|
145
|
+
}
|
|
146
|
+
if (data instanceof Map) {
|
|
147
|
+
return this.serializeValue(Array.from(data.entries()), segments, maps, blobs);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const handlerEntries = this.handlerEntries;
|
|
153
|
+
if (handlerEntries) {
|
|
154
|
+
for (let i = 0; i < handlerEntries.length; i++) {
|
|
155
|
+
const handler = handlerEntries[i];
|
|
156
|
+
if (handler.condition(data)) {
|
|
157
|
+
const serialized = handler.serialize(data);
|
|
158
|
+
if (handler.isTerminal) {
|
|
159
|
+
if (serialized instanceof Blob) {
|
|
160
|
+
maps.push(segments.slice());
|
|
161
|
+
blobs.push(serialized);
|
|
162
|
+
}
|
|
163
|
+
return serialized;
|
|
164
|
+
}
|
|
165
|
+
return this.serializeValue(serialized, segments, maps, blobs);
|
|
107
166
|
}
|
|
108
|
-
const result = this.serializeValue(serialized, segments, maps, blobs);
|
|
109
|
-
return result;
|
|
110
167
|
}
|
|
111
168
|
}
|
|
112
169
|
if (data instanceof Blob) {
|
|
113
|
-
maps.push(segments);
|
|
170
|
+
maps.push(segments.slice());
|
|
114
171
|
blobs.push(data);
|
|
115
|
-
return
|
|
172
|
+
return data;
|
|
116
173
|
}
|
|
117
174
|
if (Array.isArray(data)) {
|
|
118
|
-
const json =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
175
|
+
const json = [];
|
|
176
|
+
for (let i = 0; i < data.length; i++) {
|
|
177
|
+
segments.push(i);
|
|
178
|
+
json.push(this.serializeValue(data[i], segments, maps, blobs));
|
|
179
|
+
segments.pop();
|
|
180
|
+
}
|
|
181
|
+
return json;
|
|
122
182
|
}
|
|
123
183
|
if (isPlainObject(data)) {
|
|
124
|
-
const json =
|
|
125
|
-
for (const k
|
|
184
|
+
const json = new NullProtoObj();
|
|
185
|
+
for (const k of Object.keys(data)) {
|
|
126
186
|
const v = data[k];
|
|
127
187
|
if (k === "toJSON" && typeof v === "function") {
|
|
128
188
|
continue;
|
|
@@ -130,29 +190,38 @@ class OpenAPIJsonSerializer {
|
|
|
130
190
|
if (v === void 0 && this.omitUndefinedProperties) {
|
|
131
191
|
continue;
|
|
132
192
|
}
|
|
133
|
-
|
|
193
|
+
segments.push(k);
|
|
194
|
+
json[k] = this.serializeValue(v, segments, maps, blobs);
|
|
195
|
+
segments.pop();
|
|
134
196
|
}
|
|
135
|
-
return
|
|
197
|
+
return json;
|
|
136
198
|
}
|
|
137
|
-
return
|
|
199
|
+
return data;
|
|
138
200
|
}
|
|
139
201
|
deserialize(serialized) {
|
|
140
|
-
const ref = {
|
|
202
|
+
const ref = { json: serialized.json };
|
|
141
203
|
if (serialized.blobs?.length) {
|
|
142
|
-
serialized.maps.
|
|
204
|
+
for (let i = 0; i < serialized.maps.length; i++) {
|
|
205
|
+
const blob = serialized.blobs[i];
|
|
206
|
+
if (!(blob instanceof Blob)) {
|
|
207
|
+
throw new TypeError(`Invalid OpenAPI serialized data: blob ${i} is not a Blob.`);
|
|
208
|
+
}
|
|
209
|
+
const segments = serialized.maps[i];
|
|
210
|
+
let original = serialized;
|
|
143
211
|
let currentRef = ref;
|
|
144
|
-
let preSegment = "
|
|
145
|
-
segments.
|
|
146
|
-
|
|
147
|
-
|
|
212
|
+
let preSegment = "json";
|
|
213
|
+
for (let j = 0; j < segments.length; j++) {
|
|
214
|
+
original = original[preSegment];
|
|
215
|
+
currentRef = copyOnWrite(currentRef, preSegment, original);
|
|
216
|
+
preSegment = segments[j];
|
|
148
217
|
if (!Object.hasOwn(currentRef, preSegment)) {
|
|
149
|
-
throw new
|
|
218
|
+
throw new TypeError(`Invalid OpenAPI serialized data: segment "${preSegment}" does not exist.`);
|
|
150
219
|
}
|
|
151
|
-
}
|
|
152
|
-
currentRef[preSegment] =
|
|
153
|
-
}
|
|
220
|
+
}
|
|
221
|
+
currentRef[preSegment] = blob;
|
|
222
|
+
}
|
|
154
223
|
}
|
|
155
|
-
return ref.
|
|
224
|
+
return ref.json;
|
|
156
225
|
}
|
|
157
226
|
}
|
|
158
227
|
|
|
@@ -166,34 +235,32 @@ class OpenAPISerializer {
|
|
|
166
235
|
this.defaultSerializeOptions = serialize;
|
|
167
236
|
}
|
|
168
237
|
serialize(data, options = {}) {
|
|
169
|
-
const useFormDataForBlobFields = options.useFormDataForBlobFields ?? this.defaultSerializeOptions?.useFormDataForBlobFields ?? true;
|
|
170
|
-
const asFormData = options.asFormData ?? this.defaultSerializeOptions?.asFormData ?? false;
|
|
171
238
|
if (!options.asFormData) {
|
|
172
239
|
if (data === void 0 || data instanceof ReadableStream || data instanceof Blob) {
|
|
173
240
|
return data;
|
|
174
241
|
}
|
|
175
242
|
if (isAsyncIteratorObject(data)) {
|
|
176
|
-
return
|
|
243
|
+
return wrapAsyncIteratorPreservingEventMeta(data, {
|
|
177
244
|
mapResult: (result) => {
|
|
178
245
|
if (result.value === void 0) {
|
|
179
246
|
return result;
|
|
180
247
|
}
|
|
181
|
-
return { done: result.done, value: this.serializeValue(result.value,
|
|
248
|
+
return { done: result.done, value: this.serializeValue(result.value, false, false) };
|
|
182
249
|
},
|
|
183
|
-
mapError: (e) =>
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
});
|
|
188
|
-
}
|
|
250
|
+
mapError: (e) => new ErrorEvent(
|
|
251
|
+
this.serializeValue(toORPCError(e).toJSON(), false, false),
|
|
252
|
+
{ cause: e }
|
|
253
|
+
)
|
|
189
254
|
});
|
|
190
255
|
}
|
|
191
256
|
}
|
|
192
|
-
|
|
257
|
+
const useFormDataForBlobFields = options.useFormDataForBlobFields ?? this.defaultSerializeOptions?.useFormDataForBlobFields ?? true;
|
|
258
|
+
const asFormData = options.asFormData ?? this.defaultSerializeOptions?.asFormData ?? false;
|
|
259
|
+
return this.serializeValue(data, useFormDataForBlobFields, asFormData);
|
|
193
260
|
}
|
|
194
|
-
serializeValue(value,
|
|
261
|
+
serializeValue(value, useFormDataForBlobFields, asFormData) {
|
|
195
262
|
const { json, blobs } = this.jsonSerializer.serialize(value);
|
|
196
|
-
if (!
|
|
263
|
+
if (!asFormData && (json instanceof Blob || json === void 0 || !blobs?.length || !useFormDataForBlobFields)) {
|
|
197
264
|
return json;
|
|
198
265
|
}
|
|
199
266
|
const form = new FormData();
|
|
@@ -211,7 +278,7 @@ class OpenAPISerializer {
|
|
|
211
278
|
return data;
|
|
212
279
|
}
|
|
213
280
|
if (isAsyncIteratorObject(data)) {
|
|
214
|
-
return
|
|
281
|
+
return wrapAsyncIteratorPreservingEventMeta(data, {
|
|
215
282
|
mapResult: (result) => {
|
|
216
283
|
if (result.value === void 0) {
|
|
217
284
|
return result;
|
|
@@ -236,6 +303,9 @@ class OpenAPISerializer {
|
|
|
236
303
|
}
|
|
237
304
|
}
|
|
238
305
|
|
|
306
|
+
function isBodylessMethod(method) {
|
|
307
|
+
return method === "GET" || method === "HEAD";
|
|
308
|
+
}
|
|
239
309
|
const PARAMETER_NAME_REGEX = /^[\w-]+$/;
|
|
240
310
|
function getDynamicPathParams(path) {
|
|
241
311
|
if (!path.includes("{")) {
|
|
@@ -272,4 +342,4 @@ function getDynamicPathParams(path) {
|
|
|
272
342
|
return params;
|
|
273
343
|
}
|
|
274
344
|
|
|
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 };
|
|
345
|
+
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 };
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { isPlainObject, NullProtoObj, getOwn, setOwn } from '@orpc/shared';
|
|
2
|
+
|
|
3
|
+
class BracketNotationSerializer {
|
|
4
|
+
maxExplicitDeserializingArrayIndex;
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.maxExplicitDeserializingArrayIndex = options.maxExplicitDeserializingArrayIndex ?? 999;
|
|
7
|
+
}
|
|
8
|
+
serialize(data) {
|
|
9
|
+
const result = [];
|
|
10
|
+
this.internalSerialize(data, "", true, result);
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
internalSerialize(data, path, isRoot, result) {
|
|
14
|
+
if (Array.isArray(data)) {
|
|
15
|
+
data.forEach((item, i) => {
|
|
16
|
+
this.internalSerialize(item, isRoot ? i.toString() : `${path}[${i}]`, false, result);
|
|
17
|
+
});
|
|
18
|
+
} else if (isPlainObject(data)) {
|
|
19
|
+
for (const key of Object.keys(data)) {
|
|
20
|
+
this.internalSerialize(data[key], isRoot ? key : `${path}[${key}]`, false, result);
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
result.push([path, data]);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
deserialize(serialized) {
|
|
27
|
+
const arrayPushStyles = /* @__PURE__ */ new WeakSet();
|
|
28
|
+
const root = new NullProtoObj();
|
|
29
|
+
for (const [path, value] of serialized) {
|
|
30
|
+
const segments = this.parsePath(path);
|
|
31
|
+
let currentRef = root;
|
|
32
|
+
let nextSegment = segments[0];
|
|
33
|
+
for (let i = 1; i < segments.length; i++) {
|
|
34
|
+
const segment = segments[i];
|
|
35
|
+
const isLast = i === segments.length - 1;
|
|
36
|
+
const existing = getOwn(currentRef, nextSegment);
|
|
37
|
+
let child = existing;
|
|
38
|
+
if (!Array.isArray(child) && !isPlainObject(child)) {
|
|
39
|
+
child = [];
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(child)) {
|
|
42
|
+
const isPushStyle = arrayPushStyles.has(child);
|
|
43
|
+
const canStayArray = segment === "" ? isLast && (isPushStyle || child.length === 0) : internalIsValidArrayIndex(segment, this.maxExplicitDeserializingArrayIndex) && !(isLast && isPushStyle);
|
|
44
|
+
if (!canStayArray) {
|
|
45
|
+
arrayPushStyles.delete(child);
|
|
46
|
+
child = isPushStyle ? internalPushStyleArrayToObject(child) : internalArrayToObject(child);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (child !== existing) {
|
|
50
|
+
setOwn(currentRef, nextSegment, child);
|
|
51
|
+
}
|
|
52
|
+
currentRef = child;
|
|
53
|
+
nextSegment = segment;
|
|
54
|
+
}
|
|
55
|
+
if (Array.isArray(currentRef) && nextSegment === "") {
|
|
56
|
+
arrayPushStyles.add(currentRef);
|
|
57
|
+
currentRef.push(value);
|
|
58
|
+
} else if (Object.hasOwn(currentRef, nextSegment)) {
|
|
59
|
+
const current = currentRef[nextSegment];
|
|
60
|
+
if (Array.isArray(current)) {
|
|
61
|
+
current.push(value);
|
|
62
|
+
} else {
|
|
63
|
+
setOwn(currentRef, nextSegment, [current, value]);
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
setOwn(currentRef, nextSegment, value);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return root;
|
|
70
|
+
}
|
|
71
|
+
stringifyPath(segments) {
|
|
72
|
+
if (segments.length === 0) {
|
|
73
|
+
return "";
|
|
74
|
+
}
|
|
75
|
+
let result = segments[0].toString();
|
|
76
|
+
for (let i = 1; i < segments.length; i++) {
|
|
77
|
+
result += `[${segments[i]}]`;
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
parsePath(path) {
|
|
82
|
+
const segments = [];
|
|
83
|
+
let inBrackets = false;
|
|
84
|
+
let currentSegment = "";
|
|
85
|
+
for (let i = 0; i < path.length; i++) {
|
|
86
|
+
const char = path[i];
|
|
87
|
+
const nextChar = path[i + 1];
|
|
88
|
+
if (inBrackets && char === "]" && (nextChar === void 0 || nextChar === "[")) {
|
|
89
|
+
if (nextChar === void 0) {
|
|
90
|
+
inBrackets = false;
|
|
91
|
+
}
|
|
92
|
+
segments.push(currentSegment);
|
|
93
|
+
currentSegment = "";
|
|
94
|
+
i++;
|
|
95
|
+
} else if (segments.length === 0 && char === "[") {
|
|
96
|
+
inBrackets = true;
|
|
97
|
+
segments.push(currentSegment);
|
|
98
|
+
currentSegment = "";
|
|
99
|
+
} else {
|
|
100
|
+
currentSegment += char;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return inBrackets || segments.length === 0 ? [path] : segments;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const INTEGER_PATTERN = /^0$|^[1-9]\d*$/;
|
|
107
|
+
function internalIsValidArrayIndex(value, maxIndex) {
|
|
108
|
+
return INTEGER_PATTERN.test(value) && Number(value) <= maxIndex;
|
|
109
|
+
}
|
|
110
|
+
function internalArrayToObject(array) {
|
|
111
|
+
const obj = new NullProtoObj();
|
|
112
|
+
array.forEach((item, i) => {
|
|
113
|
+
obj[i] = item;
|
|
114
|
+
});
|
|
115
|
+
return obj;
|
|
116
|
+
}
|
|
117
|
+
function internalPushStyleArrayToObject(array) {
|
|
118
|
+
const obj = new NullProtoObj();
|
|
119
|
+
obj[""] = array.length === 1 ? array[0] : array;
|
|
120
|
+
return obj;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export { BracketNotationSerializer as B };
|