@orpc/openapi 2.0.0-beta.3 → 2.0.0-beta.31
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 +71 -101
- 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 +12 -2
- package/dist/adapters/fetch/index.d.ts +12 -2
- package/dist/adapters/fetch/index.mjs +4 -4
- package/dist/adapters/node/index.d.mts +7 -2
- package/dist/adapters/node/index.d.ts +7 -2
- package/dist/adapters/node/index.mjs +3 -3
- package/dist/adapters/standard/index.d.mts +10 -46
- package/dist/adapters/standard/index.d.ts +10 -46
- package/dist/adapters/standard/index.mjs +4 -4
- package/dist/extensions/route.d.mts +2 -2
- package/dist/extensions/route.d.ts +2 -2
- 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 +70 -36
- package/dist/index.d.ts +70 -36
- package/dist/index.mjs +701 -738
- package/dist/plugins/index.d.mts +18 -1
- package/dist/plugins/index.d.ts +18 -1
- package/dist/plugins/index.mjs +6 -1
- package/dist/shared/{openapi.DmAa7YPO.mjs → openapi.0yE-t1W-.mjs} +120 -42
- package/dist/shared/{openapi.DuDd2iQz.d.mts → openapi.9smDzQwj.d.mts} +10 -7
- package/dist/shared/{openapi.B2SK0ZAr.mjs → openapi.B6jrmH-p.mjs} +21 -29
- package/dist/shared/openapi.Bd9icEUa.d.mts +45 -0
- package/dist/shared/openapi.D1CqEIRy.d.ts +45 -0
- package/dist/shared/{openapi.BQzzr4-4.d.ts → openapi.DTYZZ6Ph.d.ts} +36 -10
- package/dist/shared/{openapi.CYgMBSUF.d.mts → openapi.DfTTLtn5.d.mts} +19 -5
- package/dist/shared/{openapi.CYgMBSUF.d.ts → openapi.DfTTLtn5.d.ts} +19 -5
- package/dist/shared/{openapi.BcEtAxQj.d.mts → openapi.Drcd0PuL.d.mts} +36 -10
- package/dist/shared/{openapi.C7m7NAmH.d.mts → openapi.Dz-JaXHo.d.mts} +13 -2
- package/dist/shared/{openapi.C7m7NAmH.d.ts → openapi.Dz-JaXHo.d.ts} +13 -2
- package/dist/shared/{openapi.DBYxUpK8.d.ts → openapi.YCiBHaJ-.d.ts} +10 -7
- package/dist/shared/{openapi.B3H7yHQa.mjs → openapi.iEFYDEKB.mjs} +92 -43
- package/dist/shared/{openapi.Bt87OzTt.mjs → openapi.s_p5sN-P.mjs} +20 -15
- package/package.json +54 -14
package/dist/plugins/index.d.mts
CHANGED
|
@@ -4,7 +4,7 @@ import { Value, Promisable } from '@orpc/shared';
|
|
|
4
4
|
import { ApiReferenceConfiguration } from '@scalar/api-reference';
|
|
5
5
|
import { StandardUrl } from '@standardserver/core';
|
|
6
6
|
import { SwaggerUIOptions } from 'swagger-ui';
|
|
7
|
-
import {
|
|
7
|
+
import { OpenAPIDocument } from '../index.mjs';
|
|
8
8
|
import '@hey-api/spec-types';
|
|
9
9
|
import '@orpc/client';
|
|
10
10
|
|
|
@@ -22,6 +22,13 @@ interface OpenAPIReferenceHandlerPluginOptions<T extends Context, TProvider exte
|
|
|
22
22
|
* Receives routing interceptor options when provided as a function.
|
|
23
23
|
*/
|
|
24
24
|
spec: Value<Promisable<OpenAPIDocument>, [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
|
@@ -4,7 +4,7 @@ import { Value, Promisable } from '@orpc/shared';
|
|
|
4
4
|
import { ApiReferenceConfiguration } from '@scalar/api-reference';
|
|
5
5
|
import { StandardUrl } from '@standardserver/core';
|
|
6
6
|
import { SwaggerUIOptions } from 'swagger-ui';
|
|
7
|
-
import {
|
|
7
|
+
import { OpenAPIDocument } from '../index.js';
|
|
8
8
|
import '@hey-api/spec-types';
|
|
9
9
|
import '@orpc/client';
|
|
10
10
|
|
|
@@ -22,6 +22,13 @@ interface OpenAPIReferenceHandlerPluginOptions<T extends Context, TProvider exte
|
|
|
22
22
|
* Receives routing interceptor options when provided as a function.
|
|
23
23
|
*/
|
|
24
24
|
spec: Value<Promisable<OpenAPIDocument>, [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, getOpenTelemetryConfig, 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,6 +46,9 @@ class OpenAPIReferenceHandlerPlugin {
|
|
|
44
46
|
if (!isSpecPath && !isDocsPath) {
|
|
45
47
|
return result;
|
|
46
48
|
}
|
|
49
|
+
if (await value(this.allow, routingInterceptorOptions) === false) {
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
47
52
|
const span = getOpenTelemetryConfig()?.trace.getActiveSpan();
|
|
48
53
|
const spec = await value(this.spec, routingInterceptorOptions);
|
|
49
54
|
if (isSpecPath) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isPlainObject, isAsyncIteratorObject } from '@orpc/shared';
|
|
1
|
+
import { wrapAsyncIteratorPreservingEventMeta, toORPCError, isORPCErrorJson, createORPCErrorFromJson } from '@orpc/client';
|
|
2
|
+
import { isPlainObject, NullProtoObj, isAsyncIteratorObject } from '@orpc/shared';
|
|
3
3
|
import { ErrorEvent } from '@standardserver/core';
|
|
4
|
-
import { B as BracketNotationSerializer } from './openapi.
|
|
4
|
+
import { B as BracketNotationSerializer } from './openapi.s_p5sN-P.mjs';
|
|
5
5
|
|
|
6
6
|
const DEFAULT_OPENAPI_METHOD = "POST";
|
|
7
7
|
const DEFAULT_OPENAPI_SUCCESS_DESCRIPTION = "OK";
|
|
@@ -84,44 +84,116 @@ 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 in 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.isNaN(data) ? null : data;
|
|
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 RegExp) {
|
|
153
|
+
return data.toString();
|
|
154
|
+
}
|
|
155
|
+
if (data instanceof Set) {
|
|
156
|
+
return this.serializeValue(Array.from(data), segments, maps, blobs);
|
|
157
|
+
}
|
|
158
|
+
if (data instanceof Map) {
|
|
159
|
+
return this.serializeValue(Array.from(data.entries()), segments, maps, blobs);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const handlerEntries = this.handlerEntries;
|
|
165
|
+
if (handlerEntries) {
|
|
166
|
+
for (let i = 0; i < handlerEntries.length; i++) {
|
|
167
|
+
const handler = handlerEntries[i];
|
|
168
|
+
if (handler.condition(data)) {
|
|
169
|
+
const serialized = handler.serialize(data);
|
|
170
|
+
if (handler.isTerminal) {
|
|
171
|
+
if (serialized instanceof Blob) {
|
|
172
|
+
maps.push(segments.slice());
|
|
173
|
+
blobs.push(serialized);
|
|
174
|
+
}
|
|
175
|
+
return serialized;
|
|
176
|
+
}
|
|
177
|
+
return this.serializeValue(serialized, segments, maps, blobs);
|
|
107
178
|
}
|
|
108
|
-
const result = this.serializeValue(serialized, segments, maps, blobs);
|
|
109
|
-
return result;
|
|
110
179
|
}
|
|
111
180
|
}
|
|
112
181
|
if (data instanceof Blob) {
|
|
113
|
-
maps.push(segments);
|
|
182
|
+
maps.push(segments.slice());
|
|
114
183
|
blobs.push(data);
|
|
115
|
-
return
|
|
184
|
+
return data;
|
|
116
185
|
}
|
|
117
186
|
if (Array.isArray(data)) {
|
|
118
|
-
const json =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
187
|
+
const json = [];
|
|
188
|
+
for (let i = 0; i < data.length; i++) {
|
|
189
|
+
segments.push(i);
|
|
190
|
+
json.push(this.serializeValue(data[i], segments, maps, blobs));
|
|
191
|
+
segments.pop();
|
|
192
|
+
}
|
|
193
|
+
return json;
|
|
122
194
|
}
|
|
123
195
|
if (isPlainObject(data)) {
|
|
124
|
-
const json =
|
|
196
|
+
const json = new NullProtoObj();
|
|
125
197
|
for (const k in data) {
|
|
126
198
|
const v = data[k];
|
|
127
199
|
if (k === "toJSON" && typeof v === "function") {
|
|
@@ -130,27 +202,30 @@ class OpenAPIJsonSerializer {
|
|
|
130
202
|
if (v === void 0 && this.omitUndefinedProperties) {
|
|
131
203
|
continue;
|
|
132
204
|
}
|
|
133
|
-
|
|
205
|
+
segments.push(k);
|
|
206
|
+
json[k] = this.serializeValue(v, segments, maps, blobs);
|
|
207
|
+
segments.pop();
|
|
134
208
|
}
|
|
135
|
-
return
|
|
209
|
+
return json;
|
|
136
210
|
}
|
|
137
|
-
return
|
|
211
|
+
return data;
|
|
138
212
|
}
|
|
139
213
|
deserialize(serialized) {
|
|
140
214
|
const ref = { data: serialized.json };
|
|
141
215
|
if (serialized.blobs?.length) {
|
|
142
|
-
serialized.maps.
|
|
216
|
+
for (let i = 0; i < serialized.maps.length; i++) {
|
|
217
|
+
const segments = serialized.maps[i];
|
|
143
218
|
let currentRef = ref;
|
|
144
219
|
let preSegment = "data";
|
|
145
|
-
segments.
|
|
220
|
+
for (let j = 0; j < segments.length; j++) {
|
|
146
221
|
currentRef = currentRef[preSegment];
|
|
147
|
-
preSegment =
|
|
222
|
+
preSegment = segments[j];
|
|
148
223
|
if (!Object.hasOwn(currentRef, preSegment)) {
|
|
149
224
|
throw new Error(`Security error: Invalid serialized data. Segment "${preSegment}" does not exist.`);
|
|
150
225
|
}
|
|
151
|
-
}
|
|
226
|
+
}
|
|
152
227
|
currentRef[preSegment] = serialized.blobs[i];
|
|
153
|
-
}
|
|
228
|
+
}
|
|
154
229
|
}
|
|
155
230
|
return ref.data;
|
|
156
231
|
}
|
|
@@ -166,34 +241,34 @@ class OpenAPISerializer {
|
|
|
166
241
|
this.defaultSerializeOptions = serialize;
|
|
167
242
|
}
|
|
168
243
|
serialize(data, options = {}) {
|
|
169
|
-
const useFormDataForBlobFields = options.useFormDataForBlobFields ?? this.defaultSerializeOptions?.useFormDataForBlobFields ?? true;
|
|
170
|
-
const asFormData = options.asFormData ?? this.defaultSerializeOptions?.asFormData ?? false;
|
|
171
244
|
if (!options.asFormData) {
|
|
172
245
|
if (data === void 0 || data instanceof ReadableStream || data instanceof Blob) {
|
|
173
246
|
return data;
|
|
174
247
|
}
|
|
175
248
|
if (isAsyncIteratorObject(data)) {
|
|
176
|
-
return
|
|
249
|
+
return wrapAsyncIteratorPreservingEventMeta(data, {
|
|
177
250
|
mapResult: (result) => {
|
|
178
251
|
if (result.value === void 0) {
|
|
179
252
|
return result;
|
|
180
253
|
}
|
|
181
|
-
return { done: result.done, value: this.serializeValue(result.value,
|
|
254
|
+
return { done: result.done, value: this.serializeValue(result.value, false, false) };
|
|
182
255
|
},
|
|
183
256
|
mapError: (e) => {
|
|
184
257
|
return new ErrorEvent({
|
|
185
|
-
data: this.serializeValue(toORPCError(e).toJSON(),
|
|
258
|
+
data: this.serializeValue(toORPCError(e).toJSON(), false, false),
|
|
186
259
|
cause: e
|
|
187
260
|
});
|
|
188
261
|
}
|
|
189
262
|
});
|
|
190
263
|
}
|
|
191
264
|
}
|
|
192
|
-
|
|
265
|
+
const useFormDataForBlobFields = options.useFormDataForBlobFields ?? this.defaultSerializeOptions?.useFormDataForBlobFields ?? true;
|
|
266
|
+
const asFormData = options.asFormData ?? this.defaultSerializeOptions?.asFormData ?? false;
|
|
267
|
+
return this.serializeValue(data, useFormDataForBlobFields, asFormData);
|
|
193
268
|
}
|
|
194
|
-
serializeValue(value,
|
|
269
|
+
serializeValue(value, useFormDataForBlobFields, asFormData) {
|
|
195
270
|
const { json, blobs } = this.jsonSerializer.serialize(value);
|
|
196
|
-
if (!
|
|
271
|
+
if (!asFormData && (json instanceof Blob || json === void 0 || !blobs?.length || !useFormDataForBlobFields)) {
|
|
197
272
|
return json;
|
|
198
273
|
}
|
|
199
274
|
const form = new FormData();
|
|
@@ -211,7 +286,7 @@ class OpenAPISerializer {
|
|
|
211
286
|
return data;
|
|
212
287
|
}
|
|
213
288
|
if (isAsyncIteratorObject(data)) {
|
|
214
|
-
return
|
|
289
|
+
return wrapAsyncIteratorPreservingEventMeta(data, {
|
|
215
290
|
mapResult: (result) => {
|
|
216
291
|
if (result.value === void 0) {
|
|
217
292
|
return result;
|
|
@@ -236,6 +311,9 @@ class OpenAPISerializer {
|
|
|
236
311
|
}
|
|
237
312
|
}
|
|
238
313
|
|
|
314
|
+
function isBodylessMethod(method) {
|
|
315
|
+
return method === "GET" || method === "HEAD";
|
|
316
|
+
}
|
|
239
317
|
const PARAMETER_NAME_REGEX = /^[\w-]+$/;
|
|
240
318
|
function getDynamicPathParams(path) {
|
|
241
319
|
if (!path.includes("{")) {
|
|
@@ -272,4 +350,4 @@ function getDynamicPathParams(path) {
|
|
|
272
350
|
return params;
|
|
273
351
|
}
|
|
274
352
|
|
|
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 };
|
|
353
|
+
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,10 +1,10 @@
|
|
|
1
1
|
import { AnyORPCError } from '@orpc/client';
|
|
2
2
|
import { AnyProcedure, AnyRouter, Context } from '@orpc/server';
|
|
3
|
-
import {
|
|
3
|
+
import { StandardHandlerCodec, StandardHandlerHandleOptions, StandardHandlerCodecResolvedProcedure } from '@orpc/server/standard';
|
|
4
4
|
import { Value, Promisable } from '@orpc/shared';
|
|
5
5
|
import { StandardLazyRequest, StandardResponse } from '@standardserver/core';
|
|
6
6
|
import { AnyProcedureContract } from '@orpc/contract';
|
|
7
|
-
import { O as OpenAPISerializer } from './openapi.
|
|
7
|
+
import { O as OpenAPISerializer } from './openapi.Dz-JaXHo.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
|
|
|
@@ -38,8 +40,9 @@ interface OpenAPIHandlerCodecCoreOptions<_T extends Context> {
|
|
|
38
40
|
serializer?: Pick<OpenAPISerializer, keyof 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
|
/**
|
|
@@ -65,8 +68,8 @@ declare class OpenAPIHandlerCodecCore<T extends Context> {
|
|
|
65
68
|
/**
|
|
66
69
|
* @throws {TypeError} If `outputStructure` is "detailed" and the output doesn't match the expected structure.
|
|
67
70
|
*/
|
|
68
|
-
encodeOutput(output: unknown, procedure: AnyProcedure, path: string[]
|
|
69
|
-
encodeError(error: AnyORPCError
|
|
71
|
+
encodeOutput(output: unknown, procedure: AnyProcedure, path: string[]): Promisable<StandardResponse>;
|
|
72
|
+
encodeError(error: AnyORPCError): Promisable<StandardResponse>;
|
|
70
73
|
private deserializeQuery;
|
|
71
74
|
private deserializeParams;
|
|
72
75
|
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { isORPCErrorJson, createORPCErrorFromJson
|
|
1
|
+
import { createORPCErrorFromMalformedResponse, isORPCErrorJson, createORPCErrorFromJson } from '@orpc/client';
|
|
2
2
|
import { getRouterContract, ProcedureContract } from '@orpc/contract';
|
|
3
3
|
import { unlazy } from '@orpc/server';
|
|
4
4
|
import { value, pathToHttpPath, mergeHttpPath, isTypescriptObject, stringifyJSON } from '@orpc/shared';
|
|
5
|
-
import { mergeStandardHeaders, parseStandardUrl
|
|
5
|
+
import { mergeStandardHeaders, parseStandardUrl } from '@standardserver/core';
|
|
6
6
|
import { toStandardHeaders } from '@standardserver/fetch';
|
|
7
|
-
import { O as OpenAPISerializer, D as DEFAULT_OPENAPI_METHOD, a as DEFAULT_OPENAPI_INPUT_STRUCTURE, g as getDynamicPathParams, b as DEFAULT_OPENAPI_OUTPUT_STRUCTURE } from './openapi.
|
|
7
|
+
import { O as OpenAPISerializer, D as DEFAULT_OPENAPI_METHOD, a as DEFAULT_OPENAPI_INPUT_STRUCTURE, g as getDynamicPathParams, i as isBodylessMethod, b as DEFAULT_OPENAPI_OUTPUT_STRUCTURE } from './openapi.0yE-t1W-.mjs';
|
|
8
8
|
import { g as getOpenAPIMeta } from './openapi.B9PQzqBn.mjs';
|
|
9
|
+
import { s as serializeHeaders } from './openapi.iEFYDEKB.mjs';
|
|
9
10
|
|
|
10
|
-
class OpenAPILinkCodecError extends TypeError {
|
|
11
|
-
}
|
|
12
11
|
const END_SLASH_REGEX = /\/$/;
|
|
13
12
|
class OpenAPILinkCodec {
|
|
14
13
|
constructor(router, options = {}) {
|
|
@@ -42,7 +41,7 @@ class OpenAPILinkCodec {
|
|
|
42
41
|
let data = input;
|
|
43
42
|
if (dynamicParams?.length) {
|
|
44
43
|
if (!isTypescriptObject(input)) {
|
|
45
|
-
throw new
|
|
44
|
+
throw new TypeError(
|
|
46
45
|
`Input must be an object with "compact" input structure when the path has dynamic params (${dynamicParams.map((p) => p.parameterName).join(", ")}) in call to procedure (${path.join(".")}).`
|
|
47
46
|
);
|
|
48
47
|
}
|
|
@@ -56,7 +55,7 @@ class OpenAPILinkCodec {
|
|
|
56
55
|
data = Object.keys(remaining).length > 0 ? remaining : void 0;
|
|
57
56
|
}
|
|
58
57
|
pathname = `${basePathname.replace(END_SLASH_REGEX, "")}${pathname}`;
|
|
59
|
-
if (method
|
|
58
|
+
if (isBodylessMethod(method)) {
|
|
60
59
|
const queryString2 = this.serializeQueryString(data, meta?.queryStyles);
|
|
61
60
|
const search2 = combineSearch(baseSearch, queryString2);
|
|
62
61
|
const url3 = `${pathname}${search2 ?? ""}${baseHash ?? ""}`;
|
|
@@ -78,12 +77,12 @@ class OpenAPILinkCodec {
|
|
|
78
77
|
};
|
|
79
78
|
}
|
|
80
79
|
if (!isValidDetailedInput(input)) {
|
|
81
|
-
throw new
|
|
80
|
+
throw new TypeError(`
|
|
82
81
|
Invalid "detailed" input structure in call to procedure (${path.join(".")}):
|
|
83
82
|
\u2022 Expected an object or undefined with optional properties:
|
|
84
83
|
- params (object, required when the path has dynamic params)
|
|
85
84
|
- query (object)
|
|
86
|
-
- headers (
|
|
85
|
+
- headers (object)
|
|
87
86
|
- body (any)
|
|
88
87
|
|
|
89
88
|
Actual value:
|
|
@@ -92,7 +91,7 @@ class OpenAPILinkCodec {
|
|
|
92
91
|
}
|
|
93
92
|
if (dynamicParams?.length) {
|
|
94
93
|
if (!input?.params) {
|
|
95
|
-
throw new
|
|
94
|
+
throw new TypeError(
|
|
96
95
|
`The "params" property is required for "detailed" input when the path has dynamic params (${dynamicParams.map((p) => p.parameterName).join(", ")}) in call to procedure (${path.join(".")}).`
|
|
97
96
|
);
|
|
98
97
|
}
|
|
@@ -104,13 +103,13 @@ class OpenAPILinkCodec {
|
|
|
104
103
|
}
|
|
105
104
|
}
|
|
106
105
|
if (input?.headers) {
|
|
107
|
-
headers = mergeStandardHeaders(headers, input.headers);
|
|
106
|
+
headers = mergeStandardHeaders(headers, serializeHeaders(input.headers, this.serializer));
|
|
108
107
|
}
|
|
109
108
|
pathname = `${basePathname.replace(END_SLASH_REGEX, "")}${pathname}`;
|
|
110
109
|
const queryString = this.serializeQueryString(input?.query, meta?.queryStyles);
|
|
111
110
|
const search = combineSearch(baseSearch, queryString);
|
|
112
111
|
const url = `${pathname}${search ?? ""}${baseHash ?? ""}`;
|
|
113
|
-
if (method
|
|
112
|
+
if (isBodylessMethod(method)) {
|
|
114
113
|
return {
|
|
115
114
|
body: void 0,
|
|
116
115
|
method,
|
|
@@ -144,7 +143,7 @@ class OpenAPILinkCodec {
|
|
|
144
143
|
}
|
|
145
144
|
}
|
|
146
145
|
if (!encoded) {
|
|
147
|
-
throw new
|
|
146
|
+
throw new TypeError(`Path param "${param.parameterName}" cannot be empty in call to procedure (${path.join(".")}).`);
|
|
148
147
|
}
|
|
149
148
|
return encoded;
|
|
150
149
|
}
|
|
@@ -243,22 +242,17 @@ class OpenAPILinkCodec {
|
|
|
243
242
|
return query || void 0;
|
|
244
243
|
}
|
|
245
244
|
async decodeResponse(response, path, _options) {
|
|
246
|
-
const isOk = response.status
|
|
245
|
+
const isOk = response.status < 400;
|
|
247
246
|
const procedure = await this.resolveProcedure(path);
|
|
248
247
|
const meta = getOpenAPIMeta(procedure);
|
|
248
|
+
const body = await response.resolveBody(meta?.responseBodyHint);
|
|
249
249
|
const deserialized = await (async () => {
|
|
250
|
-
let isBodyOk = false;
|
|
251
250
|
try {
|
|
252
|
-
const body = await response.resolveBody(meta?.responseBodyHint);
|
|
253
|
-
isBodyOk = true;
|
|
254
251
|
return this.serializer.deserialize(body);
|
|
255
252
|
} catch (error) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
throw new Error("Invalid OpenAPI response format.", {
|
|
253
|
+
throw createORPCErrorFromMalformedResponse({
|
|
254
|
+
message: "Invalid OpenAPI response format.",
|
|
255
|
+
response: { status: response.status, headers: response.headers, body },
|
|
262
256
|
cause: error
|
|
263
257
|
});
|
|
264
258
|
}
|
|
@@ -273,9 +267,7 @@ class OpenAPILinkCodec {
|
|
|
273
267
|
}
|
|
274
268
|
return {
|
|
275
269
|
kind: "error",
|
|
276
|
-
error:
|
|
277
|
-
data: { headers: response.headers, status: response.status, body: deserialized }
|
|
278
|
-
})
|
|
270
|
+
error: createORPCErrorFromMalformedResponse({ response: { headers: response.headers, status: response.status, body } })
|
|
279
271
|
};
|
|
280
272
|
}
|
|
281
273
|
const outputStructure = meta?.outputStructure ?? DEFAULT_OPENAPI_OUTPUT_STRUCTURE;
|
|
@@ -291,7 +283,7 @@ class OpenAPILinkCodec {
|
|
|
291
283
|
async resolveProcedure(path) {
|
|
292
284
|
const { default: maybeProcedure } = await unlazy(getRouterContract(this.router, path));
|
|
293
285
|
if (!(maybeProcedure instanceof ProcedureContract)) {
|
|
294
|
-
throw new
|
|
286
|
+
throw new TypeError(`Expected a procedure or contract at path (${path.join(".")})`);
|
|
295
287
|
}
|
|
296
288
|
return maybeProcedure;
|
|
297
289
|
}
|
|
@@ -324,7 +316,7 @@ function isValidDetailedInput(input) {
|
|
|
324
316
|
if (input.query !== void 0 && !isTypescriptObject(input.query)) {
|
|
325
317
|
return false;
|
|
326
318
|
}
|
|
327
|
-
if (input.headers !== void 0 && !
|
|
319
|
+
if (input.headers !== void 0 && !isTypescriptObject(input.headers)) {
|
|
328
320
|
return false;
|
|
329
321
|
}
|
|
330
322
|
return true;
|
|
@@ -356,4 +348,4 @@ function encodeDelimitedObject(entries, encodedDelimiter) {
|
|
|
356
348
|
).join(encodedDelimiter);
|
|
357
349
|
}
|
|
358
350
|
|
|
359
|
-
export { OpenAPILinkCodec as O
|
|
351
|
+
export { OpenAPILinkCodec as O };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ClientContext, ClientOptions, AnyORPCError } from '@orpc/client';
|
|
2
|
+
import { StandardLinkCodec, StandardLinkCodecDecodedResponse } from '@orpc/client/standard';
|
|
3
|
+
import { RouterContract } from '@orpc/contract';
|
|
4
|
+
import { Value, Promisable } from '@orpc/shared';
|
|
5
|
+
import { StandardUrl, StandardHeaders, StandardLazyResponse, StandardRequest } from '@standardserver/core';
|
|
6
|
+
import { O as OpenAPISerializer } from './openapi.Dz-JaXHo.mjs';
|
|
7
|
+
|
|
8
|
+
interface OpenAPILinkCodecOptions<T extends ClientContext> {
|
|
9
|
+
/**
|
|
10
|
+
* Base URL for all requests, without origin. Should match the OpenAPI handler mount path.
|
|
11
|
+
*
|
|
12
|
+
* @example '/api'
|
|
13
|
+
* @default '/'
|
|
14
|
+
*/
|
|
15
|
+
url?: Value<Promisable<StandardUrl>, [options: ClientOptions<T>, path: string[], input: unknown]>;
|
|
16
|
+
/**
|
|
17
|
+
* Inject headers into the request.
|
|
18
|
+
*/
|
|
19
|
+
headers?: Value<Promisable<StandardHeaders | Headers>, [options: ClientOptions<T>, path: string[], input: unknown]>;
|
|
20
|
+
/**
|
|
21
|
+
* Override the default OpenAPI serializer.
|
|
22
|
+
*/
|
|
23
|
+
serializer?: Pick<OpenAPISerializer, keyof OpenAPISerializer>;
|
|
24
|
+
/**
|
|
25
|
+
* Customize how an error response body is converted into an ORPC error.
|
|
26
|
+
* Return `null` or `undefined` to fall back to the default decoding behavior.
|
|
27
|
+
*/
|
|
28
|
+
customErrorResponseBodyDecoder?: (deserializedBody: unknown, response: StandardLazyResponse) => AnyORPCError | null | undefined;
|
|
29
|
+
}
|
|
30
|
+
declare class OpenAPILinkCodec<T extends ClientContext> implements StandardLinkCodec<T> {
|
|
31
|
+
private readonly router;
|
|
32
|
+
private readonly baseUrl;
|
|
33
|
+
private readonly headers;
|
|
34
|
+
private readonly serializer;
|
|
35
|
+
private readonly customErrorResponseBodyDecoder;
|
|
36
|
+
constructor(router: RouterContract, options?: OpenAPILinkCodecOptions<T>);
|
|
37
|
+
encodeInput(input: unknown, path: string[], options: ClientOptions<T>): Promise<StandardRequest>;
|
|
38
|
+
private encodePathParam;
|
|
39
|
+
private serializeQueryString;
|
|
40
|
+
decodeResponse(response: StandardLazyResponse, path: string[], _options: ClientOptions<T>): Promise<StandardLinkCodecDecodedResponse>;
|
|
41
|
+
private resolveProcedure;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { OpenAPILinkCodec as a };
|
|
45
|
+
export type { OpenAPILinkCodecOptions as O };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ClientContext, ClientOptions, AnyORPCError } from '@orpc/client';
|
|
2
|
+
import { StandardLinkCodec, StandardLinkCodecDecodedResponse } from '@orpc/client/standard';
|
|
3
|
+
import { RouterContract } from '@orpc/contract';
|
|
4
|
+
import { Value, Promisable } from '@orpc/shared';
|
|
5
|
+
import { StandardUrl, StandardHeaders, StandardLazyResponse, StandardRequest } from '@standardserver/core';
|
|
6
|
+
import { O as OpenAPISerializer } from './openapi.Dz-JaXHo.js';
|
|
7
|
+
|
|
8
|
+
interface OpenAPILinkCodecOptions<T extends ClientContext> {
|
|
9
|
+
/**
|
|
10
|
+
* Base URL for all requests, without origin. Should match the OpenAPI handler mount path.
|
|
11
|
+
*
|
|
12
|
+
* @example '/api'
|
|
13
|
+
* @default '/'
|
|
14
|
+
*/
|
|
15
|
+
url?: Value<Promisable<StandardUrl>, [options: ClientOptions<T>, path: string[], input: unknown]>;
|
|
16
|
+
/**
|
|
17
|
+
* Inject headers into the request.
|
|
18
|
+
*/
|
|
19
|
+
headers?: Value<Promisable<StandardHeaders | Headers>, [options: ClientOptions<T>, path: string[], input: unknown]>;
|
|
20
|
+
/**
|
|
21
|
+
* Override the default OpenAPI serializer.
|
|
22
|
+
*/
|
|
23
|
+
serializer?: Pick<OpenAPISerializer, keyof OpenAPISerializer>;
|
|
24
|
+
/**
|
|
25
|
+
* Customize how an error response body is converted into an ORPC error.
|
|
26
|
+
* Return `null` or `undefined` to fall back to the default decoding behavior.
|
|
27
|
+
*/
|
|
28
|
+
customErrorResponseBodyDecoder?: (deserializedBody: unknown, response: StandardLazyResponse) => AnyORPCError | null | undefined;
|
|
29
|
+
}
|
|
30
|
+
declare class OpenAPILinkCodec<T extends ClientContext> implements StandardLinkCodec<T> {
|
|
31
|
+
private readonly router;
|
|
32
|
+
private readonly baseUrl;
|
|
33
|
+
private readonly headers;
|
|
34
|
+
private readonly serializer;
|
|
35
|
+
private readonly customErrorResponseBodyDecoder;
|
|
36
|
+
constructor(router: RouterContract, options?: OpenAPILinkCodecOptions<T>);
|
|
37
|
+
encodeInput(input: unknown, path: string[], options: ClientOptions<T>): Promise<StandardRequest>;
|
|
38
|
+
private encodePathParam;
|
|
39
|
+
private serializeQueryString;
|
|
40
|
+
decodeResponse(response: StandardLazyResponse, path: string[], _options: ClientOptions<T>): Promise<StandardLinkCodecDecodedResponse>;
|
|
41
|
+
private resolveProcedure;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { OpenAPILinkCodec as a };
|
|
45
|
+
export type { OpenAPILinkCodecOptions as O };
|