@orpc/openapi 0.0.0-next.e7d7758 → 0.0.0-next.e7ee5a9
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 +91 -0
- package/dist/adapters/fetch/index.d.mts +14 -0
- package/dist/adapters/fetch/index.d.ts +14 -0
- package/dist/adapters/fetch/index.mjs +10 -0
- package/dist/adapters/hono/index.d.mts +7 -0
- package/dist/adapters/hono/index.d.ts +7 -0
- package/dist/adapters/hono/index.mjs +10 -0
- package/dist/adapters/next/index.d.mts +7 -0
- package/dist/adapters/next/index.d.ts +7 -0
- package/dist/adapters/next/index.mjs +10 -0
- package/dist/adapters/node/index.d.mts +14 -0
- package/dist/adapters/node/index.d.ts +14 -0
- package/dist/adapters/node/index.mjs +29 -0
- package/dist/adapters/standard/index.d.mts +24 -0
- package/dist/adapters/standard/index.d.ts +24 -0
- package/dist/adapters/standard/index.mjs +7 -0
- package/dist/index.d.mts +169 -0
- package/dist/index.d.ts +169 -0
- package/dist/{index.js → index.mjs} +271 -100
- package/dist/shared/openapi.BHG_gu5Z.mjs +8 -0
- package/dist/shared/openapi.CDsfPHgw.mjs +148 -0
- package/dist/shared/openapi.D0VMNR6V.mjs +25 -0
- package/dist/shared/openapi.Dz_6xooR.d.mts +7 -0
- package/dist/shared/openapi.Dz_6xooR.d.ts +7 -0
- package/package.json +35 -28
- package/dist/chunk-KNYXLM77.js +0 -107
- package/dist/chunk-YXHH6XHB.js +0 -642
- package/dist/fetch.js +0 -46
- package/dist/node.js +0 -46
- package/dist/src/adapters/fetch/bracket-notation.d.ts +0 -84
- package/dist/src/adapters/fetch/index.d.ts +0 -10
- package/dist/src/adapters/fetch/input-structure-compact.d.ts +0 -6
- package/dist/src/adapters/fetch/input-structure-detailed.d.ts +0 -11
- package/dist/src/adapters/fetch/openapi-handler-server.d.ts +0 -7
- package/dist/src/adapters/fetch/openapi-handler-serverless.d.ts +0 -7
- package/dist/src/adapters/fetch/openapi-handler.d.ts +0 -33
- package/dist/src/adapters/fetch/openapi-payload-codec.d.ts +0 -15
- package/dist/src/adapters/fetch/openapi-procedure-matcher.d.ts +0 -19
- package/dist/src/adapters/fetch/schema-coercer.d.ts +0 -10
- package/dist/src/adapters/node/index.d.ts +0 -5
- package/dist/src/adapters/node/openapi-handler-server.d.ts +0 -7
- package/dist/src/adapters/node/openapi-handler-serverless.d.ts +0 -7
- package/dist/src/adapters/node/openapi-handler.d.ts +0 -12
- package/dist/src/adapters/node/types.d.ts +0 -2
- package/dist/src/index.d.ts +0 -12
- package/dist/src/json-serializer.d.ts +0 -5
- package/dist/src/openapi-content-builder.d.ts +0 -10
- package/dist/src/openapi-error.d.ts +0 -3
- package/dist/src/openapi-generator.d.ts +0 -60
- package/dist/src/openapi-input-structure-parser.d.ts +0 -22
- package/dist/src/openapi-output-structure-parser.d.ts +0 -18
- package/dist/src/openapi-parameters-builder.d.ts +0 -12
- package/dist/src/openapi-path-parser.d.ts +0 -8
- package/dist/src/openapi.d.ts +0 -3
- package/dist/src/schema-converter.d.ts +0 -16
- package/dist/src/schema-utils.d.ts +0 -11
- package/dist/src/schema.d.ts +0 -12
- package/dist/src/utils.d.ts +0 -18
|
@@ -1,16 +1,61 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from
|
|
1
|
+
import { isProcedure, eachAllContractProcedure } from '@orpc/server';
|
|
2
|
+
import { OpenApiBuilder } from 'openapi3-ts/oas31';
|
|
3
|
+
export { OpenApiBuilder } from 'openapi3-ts/oas31';
|
|
4
|
+
import { findDeepMatches, isObject, get, omit, group } from '@orpc/shared';
|
|
5
|
+
import { fallbackORPCErrorStatus } from '@orpc/client';
|
|
6
|
+
import { fallbackContractConfig, getEventIteratorSchemaDetails } from '@orpc/contract';
|
|
7
|
+
import { OpenAPIJsonSerializer } from '@orpc/openapi-client/standard';
|
|
8
|
+
import * as draft202012 from 'json-schema-typed/draft-2020-12';
|
|
9
|
+
export { draft202012 as JSONSchema };
|
|
10
|
+
export { Format as JSONSchemaFormat } from 'json-schema-typed/draft-2020-12';
|
|
11
|
+
import { t as toOpenAPI31RoutePattern } from './shared/openapi.BHG_gu5Z.mjs';
|
|
12
|
+
export { s as standardizeHTTPPath } from './shared/openapi.BHG_gu5Z.mjs';
|
|
7
13
|
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
const OPERATION_EXTENDER_SYMBOL = Symbol("ORPC_OPERATION_EXTENDER");
|
|
15
|
+
function setOperationExtender(o, extend) {
|
|
16
|
+
return new Proxy(o, {
|
|
17
|
+
get(target, prop, receiver) {
|
|
18
|
+
if (prop === OPERATION_EXTENDER_SYMBOL) {
|
|
19
|
+
return extend;
|
|
20
|
+
}
|
|
21
|
+
return Reflect.get(target, prop, receiver);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function getOperationExtender(o) {
|
|
26
|
+
return o[OPERATION_EXTENDER_SYMBOL];
|
|
27
|
+
}
|
|
28
|
+
function extendOperation(operation, procedure) {
|
|
29
|
+
const operationExtenders = [];
|
|
30
|
+
for (const errorItem of Object.values(procedure["~orpc"].errorMap)) {
|
|
31
|
+
const maybeExtender = getOperationExtender(errorItem);
|
|
32
|
+
if (maybeExtender) {
|
|
33
|
+
operationExtenders.push(maybeExtender);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (isProcedure(procedure)) {
|
|
37
|
+
for (const middleware of procedure["~orpc"].middlewares) {
|
|
38
|
+
const maybeExtender = getOperationExtender(middleware);
|
|
39
|
+
if (maybeExtender) {
|
|
40
|
+
operationExtenders.push(maybeExtender);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
let currentOperation = operation;
|
|
45
|
+
for (const extender of operationExtenders) {
|
|
46
|
+
if (typeof extender === "function") {
|
|
47
|
+
currentOperation = extender(currentOperation, procedure);
|
|
48
|
+
} else {
|
|
49
|
+
currentOperation = {
|
|
50
|
+
...currentOperation,
|
|
51
|
+
...extender
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return currentOperation;
|
|
56
|
+
}
|
|
10
57
|
|
|
11
|
-
|
|
12
|
-
import { findDeepMatches } from "@orpc/shared";
|
|
13
|
-
var OpenAPIContentBuilder = class {
|
|
58
|
+
class OpenAPIContentBuilder {
|
|
14
59
|
constructor(schemaUtils) {
|
|
15
60
|
this.schemaUtils = schemaUtils;
|
|
16
61
|
}
|
|
@@ -33,22 +78,20 @@ var OpenAPIContentBuilder = class {
|
|
|
33
78
|
}
|
|
34
79
|
return content;
|
|
35
80
|
}
|
|
36
|
-
}
|
|
81
|
+
}
|
|
37
82
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
};
|
|
83
|
+
class OpenAPIError extends Error {
|
|
84
|
+
}
|
|
41
85
|
|
|
42
|
-
|
|
43
|
-
var OpenAPIInputStructureParser = class {
|
|
86
|
+
class OpenAPIInputStructureParser {
|
|
44
87
|
constructor(schemaConverter, schemaUtils, pathParser) {
|
|
45
88
|
this.schemaConverter = schemaConverter;
|
|
46
89
|
this.schemaUtils = schemaUtils;
|
|
47
90
|
this.pathParser = pathParser;
|
|
48
91
|
}
|
|
49
92
|
parse(contract, structure) {
|
|
50
|
-
const inputSchema = this.schemaConverter.convert(contract["~orpc"].
|
|
51
|
-
const method = contract["~orpc"].route?.method
|
|
93
|
+
const inputSchema = this.schemaConverter.convert(contract["~orpc"].inputSchema, { strategy: "input" });
|
|
94
|
+
const method = fallbackContractConfig("defaultMethod", contract["~orpc"].route?.method);
|
|
52
95
|
const httpPath = contract["~orpc"].route?.path;
|
|
53
96
|
if (this.schemaUtils.isAnySchema(inputSchema)) {
|
|
54
97
|
return {
|
|
@@ -131,16 +174,15 @@ var OpenAPIInputStructureParser = class {
|
|
|
131
174
|
bodySchema: method !== "GET" ? rest : void 0
|
|
132
175
|
};
|
|
133
176
|
}
|
|
134
|
-
}
|
|
177
|
+
}
|
|
135
178
|
|
|
136
|
-
|
|
137
|
-
var OpenAPIOutputStructureParser = class {
|
|
179
|
+
class OpenAPIOutputStructureParser {
|
|
138
180
|
constructor(schemaConverter, schemaUtils) {
|
|
139
181
|
this.schemaConverter = schemaConverter;
|
|
140
182
|
this.schemaUtils = schemaUtils;
|
|
141
183
|
}
|
|
142
184
|
parse(contract, structure) {
|
|
143
|
-
const outputSchema = this.schemaConverter.convert(contract["~orpc"].
|
|
185
|
+
const outputSchema = this.schemaConverter.convert(contract["~orpc"].outputSchema, { strategy: "output" });
|
|
144
186
|
if (this.schemaUtils.isAnySchema(outputSchema)) {
|
|
145
187
|
return {
|
|
146
188
|
headersSchema: void 0,
|
|
@@ -176,17 +218,15 @@ var OpenAPIOutputStructureParser = class {
|
|
|
176
218
|
bodySchema: outputSchema
|
|
177
219
|
};
|
|
178
220
|
}
|
|
179
|
-
}
|
|
221
|
+
}
|
|
180
222
|
|
|
181
|
-
|
|
182
|
-
import { get, isPlainObject, omit } from "@orpc/shared";
|
|
183
|
-
var OpenAPIParametersBuilder = class {
|
|
223
|
+
class OpenAPIParametersBuilder {
|
|
184
224
|
build(paramIn, jsonSchema, options) {
|
|
185
225
|
const parameters = [];
|
|
186
226
|
for (const name in jsonSchema.properties) {
|
|
187
227
|
const schema = jsonSchema.properties[name];
|
|
188
228
|
const paramExamples = jsonSchema.examples?.filter((example) => {
|
|
189
|
-
return
|
|
229
|
+
return isObject(example) && name in example;
|
|
190
230
|
}).map((example) => {
|
|
191
231
|
return example[name];
|
|
192
232
|
});
|
|
@@ -214,10 +254,9 @@ var OpenAPIParametersBuilder = class {
|
|
|
214
254
|
}
|
|
215
255
|
return headersObject;
|
|
216
256
|
}
|
|
217
|
-
}
|
|
257
|
+
}
|
|
218
258
|
|
|
219
|
-
|
|
220
|
-
var OpenAPIPathParser = class {
|
|
259
|
+
class OpenAPIPathParser {
|
|
221
260
|
parseDynamicParams(path) {
|
|
222
261
|
const raws = path.match(/\{([^}]+)\}/g) ?? [];
|
|
223
262
|
return raws.map((raw) => {
|
|
@@ -225,10 +264,9 @@ var OpenAPIPathParser = class {
|
|
|
225
264
|
return { name, raw };
|
|
226
265
|
});
|
|
227
266
|
}
|
|
228
|
-
}
|
|
267
|
+
}
|
|
229
268
|
|
|
230
|
-
|
|
231
|
-
var CompositeSchemaConverter = class {
|
|
269
|
+
class CompositeSchemaConverter {
|
|
232
270
|
converters;
|
|
233
271
|
constructor(converters) {
|
|
234
272
|
this.converters = converters;
|
|
@@ -244,15 +282,9 @@ var CompositeSchemaConverter = class {
|
|
|
244
282
|
}
|
|
245
283
|
return {};
|
|
246
284
|
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// src/schema-utils.ts
|
|
250
|
-
import { isPlainObject as isPlainObject2 } from "@orpc/shared";
|
|
285
|
+
}
|
|
251
286
|
|
|
252
|
-
|
|
253
|
-
import * as JSONSchema from "json-schema-typed/draft-2020-12";
|
|
254
|
-
import { Format } from "json-schema-typed/draft-2020-12";
|
|
255
|
-
var NON_LOGIC_KEYWORDS = [
|
|
287
|
+
const NON_LOGIC_KEYWORDS = [
|
|
256
288
|
// Core Documentation Keywords
|
|
257
289
|
"$anchor",
|
|
258
290
|
"$comment",
|
|
@@ -280,23 +312,22 @@ var NON_LOGIC_KEYWORDS = [
|
|
|
280
312
|
"$dynamicRef"
|
|
281
313
|
];
|
|
282
314
|
|
|
283
|
-
|
|
284
|
-
var SchemaUtils = class {
|
|
315
|
+
class SchemaUtils {
|
|
285
316
|
isFileSchema(schema) {
|
|
286
|
-
return
|
|
317
|
+
return isObject(schema) && schema.type === "string" && typeof schema.contentMediaType === "string";
|
|
287
318
|
}
|
|
288
319
|
isObjectSchema(schema) {
|
|
289
|
-
return
|
|
320
|
+
return isObject(schema) && schema.type === "object";
|
|
290
321
|
}
|
|
291
322
|
isAnySchema(schema) {
|
|
292
|
-
return schema === true || Object.keys(schema).length === 0;
|
|
323
|
+
return schema === true || Object.keys(schema).filter((key) => !NON_LOGIC_KEYWORDS.includes(key)).length === 0;
|
|
293
324
|
}
|
|
294
325
|
isUndefinableSchema(schema) {
|
|
295
326
|
const [matches] = this.filterSchemaBranches(schema, (schema2) => {
|
|
296
327
|
if (typeof schema2 === "boolean") {
|
|
297
328
|
return schema2;
|
|
298
329
|
}
|
|
299
|
-
return Object.keys(schema2).length === 0;
|
|
330
|
+
return Object.keys(schema2).filter((key) => !NON_LOGIC_KEYWORDS.includes(key)).length === 0;
|
|
300
331
|
});
|
|
301
332
|
return matches.length > 0;
|
|
302
333
|
}
|
|
@@ -309,7 +340,7 @@ var SchemaUtils = class {
|
|
|
309
340
|
}, {});
|
|
310
341
|
matched.required = schema.required?.filter((key) => separatedProperties.includes(key));
|
|
311
342
|
matched.examples = schema.examples?.map((example) => {
|
|
312
|
-
if (!
|
|
343
|
+
if (!isObject(example)) {
|
|
313
344
|
return example;
|
|
314
345
|
}
|
|
315
346
|
return Object.entries(example).reduce((acc, [key, value]) => {
|
|
@@ -325,7 +356,7 @@ var SchemaUtils = class {
|
|
|
325
356
|
}, {});
|
|
326
357
|
rest.required = schema.required?.filter((key) => !separatedProperties.includes(key));
|
|
327
358
|
rest.examples = schema.examples?.map((example) => {
|
|
328
|
-
if (!
|
|
359
|
+
if (!isObject(example)) {
|
|
329
360
|
return example;
|
|
330
361
|
}
|
|
331
362
|
return Object.entries(example).reduce((acc, [key, value]) => {
|
|
@@ -365,10 +396,9 @@ var SchemaUtils = class {
|
|
|
365
396
|
}
|
|
366
397
|
return [matches, schema];
|
|
367
398
|
}
|
|
368
|
-
}
|
|
399
|
+
}
|
|
369
400
|
|
|
370
|
-
|
|
371
|
-
var OpenAPIGenerator = class {
|
|
401
|
+
class OpenAPIGenerator {
|
|
372
402
|
contentBuilder;
|
|
373
403
|
parametersBuilder;
|
|
374
404
|
schemaConverter;
|
|
@@ -380,11 +410,12 @@ var OpenAPIGenerator = class {
|
|
|
380
410
|
errorHandlerStrategy;
|
|
381
411
|
ignoreUndefinedPathProcedures;
|
|
382
412
|
considerMissingTagDefinitionAsError;
|
|
413
|
+
strictErrorResponses;
|
|
383
414
|
constructor(options) {
|
|
384
415
|
this.parametersBuilder = options?.parametersBuilder ?? new OpenAPIParametersBuilder();
|
|
385
416
|
this.schemaConverter = new CompositeSchemaConverter(options?.schemaConverters ?? []);
|
|
386
417
|
this.schemaUtils = options?.schemaUtils ?? new SchemaUtils();
|
|
387
|
-
this.jsonSerializer = options?.jsonSerializer ?? new
|
|
418
|
+
this.jsonSerializer = options?.jsonSerializer ?? new OpenAPIJsonSerializer();
|
|
388
419
|
this.contentBuilder = options?.contentBuilder ?? new OpenAPIContentBuilder(this.schemaUtils);
|
|
389
420
|
this.pathParser = new OpenAPIPathParser();
|
|
390
421
|
this.inputStructureParser = options?.inputStructureParser ?? new OpenAPIInputStructureParser(this.schemaConverter, this.schemaUtils, this.pathParser);
|
|
@@ -392,6 +423,7 @@ var OpenAPIGenerator = class {
|
|
|
392
423
|
this.errorHandlerStrategy = options?.errorHandlerStrategy ?? "throw";
|
|
393
424
|
this.ignoreUndefinedPathProcedures = options?.ignoreUndefinedPathProcedures ?? false;
|
|
394
425
|
this.considerMissingTagDefinitionAsError = options?.considerMissingTagDefinitionAsError ?? false;
|
|
426
|
+
this.strictErrorResponses = options?.strictErrorResponses ?? true;
|
|
395
427
|
}
|
|
396
428
|
async generate(router, doc) {
|
|
397
429
|
const builder = new OpenApiBuilder({
|
|
@@ -399,35 +431,186 @@ var OpenAPIGenerator = class {
|
|
|
399
431
|
openapi: "3.1.1"
|
|
400
432
|
});
|
|
401
433
|
const rootTags = doc.tags?.map((tag) => tag.name) ?? [];
|
|
402
|
-
await
|
|
434
|
+
await eachAllContractProcedure({
|
|
435
|
+
path: [],
|
|
436
|
+
router
|
|
437
|
+
}, ({ contract, path }) => {
|
|
403
438
|
try {
|
|
404
439
|
const def = contract["~orpc"];
|
|
405
440
|
if (this.ignoreUndefinedPathProcedures && def.route?.path === void 0) {
|
|
406
441
|
return;
|
|
407
442
|
}
|
|
408
|
-
const method = def.route?.method
|
|
409
|
-
const httpPath = def.route?.path ?
|
|
410
|
-
const {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
443
|
+
const method = fallbackContractConfig("defaultMethod", def.route?.method);
|
|
444
|
+
const httpPath = def.route?.path ? toOpenAPI31RoutePattern(def.route?.path) : `/${path.map(encodeURIComponent).join("/")}`;
|
|
445
|
+
const { parameters, requestBody } = (() => {
|
|
446
|
+
const eventIteratorSchemaDetails = getEventIteratorSchemaDetails(def.inputSchema);
|
|
447
|
+
if (eventIteratorSchemaDetails) {
|
|
448
|
+
const requestBody3 = {
|
|
449
|
+
required: true,
|
|
450
|
+
content: {
|
|
451
|
+
"text/event-stream": {
|
|
452
|
+
schema: {
|
|
453
|
+
oneOf: [
|
|
454
|
+
{
|
|
455
|
+
type: "object",
|
|
456
|
+
properties: {
|
|
457
|
+
event: { type: "string", const: "message" },
|
|
458
|
+
data: this.schemaConverter.convert(eventIteratorSchemaDetails.yields, { strategy: "input" }),
|
|
459
|
+
id: { type: "string" },
|
|
460
|
+
retry: { type: "number" }
|
|
461
|
+
},
|
|
462
|
+
required: ["event", "data"]
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
type: "object",
|
|
466
|
+
properties: {
|
|
467
|
+
event: { type: "string", const: "done" },
|
|
468
|
+
data: this.schemaConverter.convert(eventIteratorSchemaDetails.returns, { strategy: "input" }),
|
|
469
|
+
id: { type: "string" },
|
|
470
|
+
retry: { type: "number" }
|
|
471
|
+
},
|
|
472
|
+
required: ["event", "data"]
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
type: "object",
|
|
476
|
+
properties: {
|
|
477
|
+
event: { type: "string", const: "error" },
|
|
478
|
+
data: {},
|
|
479
|
+
id: { type: "string" },
|
|
480
|
+
retry: { type: "number" }
|
|
481
|
+
},
|
|
482
|
+
required: ["event", "data"]
|
|
483
|
+
}
|
|
484
|
+
]
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
return { requestBody: requestBody3, parameters: [] };
|
|
490
|
+
}
|
|
491
|
+
const inputStructure = fallbackContractConfig("defaultInputStructure", def.route?.inputStructure);
|
|
492
|
+
const { paramsSchema, querySchema, headersSchema, bodySchema } = this.inputStructureParser.parse(contract, inputStructure);
|
|
493
|
+
const params = paramsSchema ? this.parametersBuilder.build("path", paramsSchema, {
|
|
494
|
+
required: true
|
|
495
|
+
}) : [];
|
|
496
|
+
const query = querySchema ? this.parametersBuilder.build("query", querySchema) : [];
|
|
497
|
+
const headers = headersSchema ? this.parametersBuilder.build("header", headersSchema) : [];
|
|
498
|
+
const parameters2 = [...params, ...query, ...headers];
|
|
499
|
+
const requestBody2 = bodySchema !== void 0 ? {
|
|
500
|
+
required: this.schemaUtils.isUndefinableSchema(bodySchema),
|
|
501
|
+
content: this.contentBuilder.build(bodySchema)
|
|
502
|
+
} : void 0;
|
|
503
|
+
return { parameters: parameters2, requestBody: requestBody2 };
|
|
504
|
+
})();
|
|
505
|
+
const { responses } = (() => {
|
|
506
|
+
const eventIteratorSchemaDetails = getEventIteratorSchemaDetails(def.outputSchema);
|
|
507
|
+
if (eventIteratorSchemaDetails) {
|
|
508
|
+
const responses3 = {};
|
|
509
|
+
responses3[fallbackContractConfig("defaultSuccessStatus", def.route?.successStatus)] = {
|
|
510
|
+
description: fallbackContractConfig("defaultSuccessDescription", def.route?.successDescription),
|
|
511
|
+
content: {
|
|
512
|
+
"text/event-stream": {
|
|
513
|
+
schema: {
|
|
514
|
+
oneOf: [
|
|
515
|
+
{
|
|
516
|
+
type: "object",
|
|
517
|
+
properties: {
|
|
518
|
+
event: { type: "string", const: "message" },
|
|
519
|
+
data: this.schemaConverter.convert(eventIteratorSchemaDetails.yields, { strategy: "input" }),
|
|
520
|
+
id: { type: "string" },
|
|
521
|
+
retry: { type: "number" }
|
|
522
|
+
},
|
|
523
|
+
required: ["event", "data"]
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
type: "object",
|
|
527
|
+
properties: {
|
|
528
|
+
event: { type: "string", const: "done" },
|
|
529
|
+
data: this.schemaConverter.convert(eventIteratorSchemaDetails.returns, { strategy: "input" }),
|
|
530
|
+
id: { type: "string" },
|
|
531
|
+
retry: { type: "number" }
|
|
532
|
+
},
|
|
533
|
+
required: ["event", "data"]
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
type: "object",
|
|
537
|
+
properties: {
|
|
538
|
+
event: { type: "string", const: "error" },
|
|
539
|
+
data: {},
|
|
540
|
+
id: { type: "string" },
|
|
541
|
+
retry: { type: "number" }
|
|
542
|
+
},
|
|
543
|
+
required: ["event", "data"]
|
|
544
|
+
}
|
|
545
|
+
]
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
return { responses: responses3 };
|
|
551
|
+
}
|
|
552
|
+
const outputStructure = fallbackContractConfig("defaultOutputStructure", def.route?.outputStructure);
|
|
553
|
+
const { headersSchema: resHeadersSchema, bodySchema: resBodySchema } = this.outputStructureParser.parse(contract, outputStructure);
|
|
554
|
+
const responses2 = {};
|
|
555
|
+
responses2[fallbackContractConfig("defaultSuccessStatus", def.route?.successStatus)] = {
|
|
556
|
+
description: fallbackContractConfig("defaultSuccessDescription", def.route?.successDescription),
|
|
557
|
+
content: resBodySchema !== void 0 ? this.contentBuilder.build(resBodySchema) : void 0,
|
|
558
|
+
headers: resHeadersSchema !== void 0 ? this.parametersBuilder.buildHeadersObject(resHeadersSchema) : void 0
|
|
559
|
+
};
|
|
560
|
+
return { responses: responses2 };
|
|
561
|
+
})();
|
|
562
|
+
const errors = group(Object.entries(def.errorMap ?? {}).filter(([_, config]) => config).map(([code, config]) => ({
|
|
563
|
+
...config,
|
|
564
|
+
code,
|
|
565
|
+
status: fallbackORPCErrorStatus(code, config?.status)
|
|
566
|
+
})), (error) => error.status);
|
|
567
|
+
for (const status in errors) {
|
|
568
|
+
const configs = errors[status];
|
|
569
|
+
if (!configs || configs.length === 0) {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
const schemas = configs.map(({ data, code, message }) => {
|
|
573
|
+
const json = {
|
|
574
|
+
type: "object",
|
|
575
|
+
properties: {
|
|
576
|
+
defined: { const: true },
|
|
577
|
+
code: { const: code },
|
|
578
|
+
status: { const: Number(status) },
|
|
579
|
+
message: { type: "string", default: message },
|
|
580
|
+
data: {}
|
|
581
|
+
},
|
|
582
|
+
required: ["defined", "code", "status", "message"]
|
|
583
|
+
};
|
|
584
|
+
if (data) {
|
|
585
|
+
const dataJson = this.schemaConverter.convert(data, { strategy: "output" });
|
|
586
|
+
json.properties.data = dataJson;
|
|
587
|
+
if (!this.schemaUtils.isUndefinableSchema(dataJson)) {
|
|
588
|
+
json.required.push("data");
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return json;
|
|
592
|
+
});
|
|
593
|
+
if (this.strictErrorResponses) {
|
|
594
|
+
schemas.push({
|
|
595
|
+
type: "object",
|
|
596
|
+
properties: {
|
|
597
|
+
defined: { const: false },
|
|
598
|
+
code: { type: "string" },
|
|
599
|
+
status: { type: "number" },
|
|
600
|
+
message: { type: "string" },
|
|
601
|
+
data: {}
|
|
602
|
+
},
|
|
603
|
+
required: ["defined", "code", "status", "message"]
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
const contentSchema = schemas.length === 1 ? schemas[0] : {
|
|
607
|
+
oneOf: schemas
|
|
608
|
+
};
|
|
609
|
+
responses[status] = {
|
|
610
|
+
description: status,
|
|
611
|
+
content: this.contentBuilder.build(contentSchema)
|
|
612
|
+
};
|
|
613
|
+
}
|
|
431
614
|
if (this.considerMissingTagDefinitionAsError && def.route?.tags) {
|
|
432
615
|
const missingTag = def.route?.tags.find((tag) => !rootTags.includes(tag));
|
|
433
616
|
if (missingTag !== void 0) {
|
|
@@ -444,12 +627,11 @@ var OpenAPIGenerator = class {
|
|
|
444
627
|
operationId: path.join("."),
|
|
445
628
|
parameters: parameters.length ? parameters : void 0,
|
|
446
629
|
requestBody,
|
|
447
|
-
responses
|
|
448
|
-
[def.route?.successStatus ?? 200]: successResponse
|
|
449
|
-
}
|
|
630
|
+
responses
|
|
450
631
|
};
|
|
632
|
+
const extendedOperation = extendOperation(operation, contract);
|
|
451
633
|
builder.addPath(httpPath, {
|
|
452
|
-
[method.toLocaleLowerCase()]:
|
|
634
|
+
[method.toLocaleLowerCase()]: extendedOperation
|
|
453
635
|
});
|
|
454
636
|
} catch (e) {
|
|
455
637
|
if (e instanceof OpenAPIError) {
|
|
@@ -468,23 +650,12 @@ var OpenAPIGenerator = class {
|
|
|
468
650
|
}
|
|
469
651
|
}
|
|
470
652
|
});
|
|
471
|
-
return this.jsonSerializer.serialize(builder.getSpec());
|
|
653
|
+
return this.jsonSerializer.serialize(builder.getSpec())[0];
|
|
472
654
|
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const oo = {
|
|
658
|
+
spec: setOperationExtender
|
|
473
659
|
};
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
JSONSchema,
|
|
477
|
-
Format as JSONSchemaFormat,
|
|
478
|
-
JSONSerializer,
|
|
479
|
-
NON_LOGIC_KEYWORDS,
|
|
480
|
-
OpenAPIContentBuilder,
|
|
481
|
-
OpenAPIGenerator,
|
|
482
|
-
OpenAPIParametersBuilder,
|
|
483
|
-
OpenAPIPathParser,
|
|
484
|
-
OpenApiBuilder,
|
|
485
|
-
SchemaUtils,
|
|
486
|
-
forEachAllContractProcedure,
|
|
487
|
-
forEachContractProcedure,
|
|
488
|
-
standardizeHTTPPath
|
|
489
|
-
};
|
|
490
|
-
//# sourceMappingURL=index.js.map
|
|
660
|
+
|
|
661
|
+
export { CompositeSchemaConverter, NON_LOGIC_KEYWORDS, OpenAPIContentBuilder, OpenAPIGenerator, OpenAPIParametersBuilder, OpenAPIPathParser, SchemaUtils, extendOperation, getOperationExtender, oo, setOperationExtender, toOpenAPI31RoutePattern };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
function standardizeHTTPPath(path) {
|
|
2
|
+
return `/${path.replace(/\/{2,}/g, "/").replace(/^\/|\/$/g, "")}`;
|
|
3
|
+
}
|
|
4
|
+
function toOpenAPI31RoutePattern(path) {
|
|
5
|
+
return standardizeHTTPPath(path).replace(/\{\+([^}]+)\}/g, "{$1}");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export { standardizeHTTPPath as s, toOpenAPI31RoutePattern as t };
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { fallbackContractConfig } from '@orpc/contract';
|
|
2
|
+
import { OpenAPISerializer } from '@orpc/openapi-client/standard';
|
|
3
|
+
import { isObject } from '@orpc/shared';
|
|
4
|
+
import { eachContractProcedure, convertPathToHttpPath, isProcedure, getLazyRouterPrefix, unlazy, getRouterChild, createContractedProcedure } from '@orpc/server';
|
|
5
|
+
import { createRouter, addRoute, findRoute } from 'rou3';
|
|
6
|
+
import { s as standardizeHTTPPath } from './openapi.BHG_gu5Z.mjs';
|
|
7
|
+
|
|
8
|
+
class OpenAPICodec {
|
|
9
|
+
constructor(serializer = new OpenAPISerializer()) {
|
|
10
|
+
this.serializer = serializer;
|
|
11
|
+
}
|
|
12
|
+
async decode(request, params, procedure) {
|
|
13
|
+
const inputStructure = fallbackContractConfig("defaultInputStructure", procedure["~orpc"].route.inputStructure);
|
|
14
|
+
if (inputStructure === "compact") {
|
|
15
|
+
const data = request.method === "GET" ? this.serializer.deserialize(request.url.searchParams) : this.serializer.deserialize(await request.body());
|
|
16
|
+
if (data === void 0) {
|
|
17
|
+
return params;
|
|
18
|
+
}
|
|
19
|
+
if (isObject(data)) {
|
|
20
|
+
return {
|
|
21
|
+
...params,
|
|
22
|
+
...data
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return data;
|
|
26
|
+
}
|
|
27
|
+
const deserializeSearchParams = () => {
|
|
28
|
+
return this.serializer.deserialize(request.url.searchParams);
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
params,
|
|
32
|
+
get query() {
|
|
33
|
+
const value = deserializeSearchParams();
|
|
34
|
+
Object.defineProperty(this, "query", { value, writable: true });
|
|
35
|
+
return value;
|
|
36
|
+
},
|
|
37
|
+
set query(value) {
|
|
38
|
+
Object.defineProperty(this, "query", { value, writable: true });
|
|
39
|
+
},
|
|
40
|
+
headers: request.headers,
|
|
41
|
+
body: this.serializer.deserialize(await request.body())
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
encode(output, procedure) {
|
|
45
|
+
const successStatus = fallbackContractConfig("defaultSuccessStatus", procedure["~orpc"].route.successStatus);
|
|
46
|
+
const outputStructure = fallbackContractConfig("defaultOutputStructure", procedure["~orpc"].route.outputStructure);
|
|
47
|
+
if (outputStructure === "compact") {
|
|
48
|
+
return {
|
|
49
|
+
status: successStatus,
|
|
50
|
+
headers: {},
|
|
51
|
+
body: this.serializer.serialize(output)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (!isObject(output)) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
'Invalid output structure for "detailed" output. Expected format: { body: any, headers?: Record<string, string | string[] | undefined> }'
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
status: successStatus,
|
|
61
|
+
headers: output.headers ?? {},
|
|
62
|
+
body: this.serializer.serialize(output.body)
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
encodeError(error) {
|
|
66
|
+
return {
|
|
67
|
+
status: error.status,
|
|
68
|
+
headers: {},
|
|
69
|
+
body: this.serializer.serialize(error.toJSON())
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
class OpenAPIMatcher {
|
|
75
|
+
tree = createRouter();
|
|
76
|
+
pendingRouters = [];
|
|
77
|
+
init(router, path = []) {
|
|
78
|
+
const laziedOptions = eachContractProcedure({
|
|
79
|
+
router,
|
|
80
|
+
path
|
|
81
|
+
}, ({ path: path2, contract }) => {
|
|
82
|
+
const method = fallbackContractConfig("defaultMethod", contract["~orpc"].route.method);
|
|
83
|
+
const httpPath = contract["~orpc"].route.path ? toRou3Pattern(contract["~orpc"].route.path) : convertPathToHttpPath(path2);
|
|
84
|
+
if (isProcedure(contract)) {
|
|
85
|
+
addRoute(this.tree, method, httpPath, {
|
|
86
|
+
path: path2,
|
|
87
|
+
contract,
|
|
88
|
+
procedure: contract,
|
|
89
|
+
// this mean dev not used contract-first so we can used contract as procedure directly
|
|
90
|
+
router
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
addRoute(this.tree, method, httpPath, {
|
|
94
|
+
path: path2,
|
|
95
|
+
contract,
|
|
96
|
+
procedure: void 0,
|
|
97
|
+
router
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
this.pendingRouters.push(...laziedOptions.map((option) => ({
|
|
102
|
+
...option,
|
|
103
|
+
httpPathPrefix: convertPathToHttpPath(option.path),
|
|
104
|
+
laziedPrefix: getLazyRouterPrefix(option.lazied)
|
|
105
|
+
})));
|
|
106
|
+
}
|
|
107
|
+
async match(method, pathname) {
|
|
108
|
+
if (this.pendingRouters.length) {
|
|
109
|
+
const newPendingRouters = [];
|
|
110
|
+
for (const pendingRouter of this.pendingRouters) {
|
|
111
|
+
if (!pendingRouter.laziedPrefix || pathname.startsWith(pendingRouter.laziedPrefix) || pathname.startsWith(pendingRouter.httpPathPrefix)) {
|
|
112
|
+
const { default: router } = await unlazy(pendingRouter.lazied);
|
|
113
|
+
this.init(router, pendingRouter.path);
|
|
114
|
+
} else {
|
|
115
|
+
newPendingRouters.push(pendingRouter);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
this.pendingRouters = newPendingRouters;
|
|
119
|
+
}
|
|
120
|
+
const match = findRoute(this.tree, method, pathname);
|
|
121
|
+
if (!match) {
|
|
122
|
+
return void 0;
|
|
123
|
+
}
|
|
124
|
+
if (!match.data.procedure) {
|
|
125
|
+
const { default: maybeProcedure } = await unlazy(getRouterChild(match.data.router, ...match.data.path));
|
|
126
|
+
if (!isProcedure(maybeProcedure)) {
|
|
127
|
+
throw new Error(`
|
|
128
|
+
[Contract-First] Missing or invalid implementation for procedure at path: ${convertPathToHttpPath(match.data.path)}.
|
|
129
|
+
Ensure that the procedure is correctly defined and matches the expected contract.
|
|
130
|
+
`);
|
|
131
|
+
}
|
|
132
|
+
match.data.procedure = createContractedProcedure(match.data.contract, maybeProcedure);
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
path: match.data.path,
|
|
136
|
+
procedure: match.data.procedure,
|
|
137
|
+
params: match.params ? decodeParams(match.params) : void 0
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function toRou3Pattern(path) {
|
|
142
|
+
return standardizeHTTPPath(path).replace(/\{\+([^}]+)\}/g, "**:$1").replace(/\{([^}]+)\}/g, ":$1");
|
|
143
|
+
}
|
|
144
|
+
function decodeParams(params) {
|
|
145
|
+
return Object.fromEntries(Object.entries(params).map(([key, value]) => [key, decodeURIComponent(value)]));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export { OpenAPICodec as O, OpenAPIMatcher as a };
|