@orpc/openapi 0.0.0-next.3636544 → 0.0.0-next.36cf381
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 +134 -14
- package/dist/adapters/aws-lambda/index.d.mts +4 -3
- package/dist/adapters/aws-lambda/index.d.ts +4 -3
- package/dist/adapters/aws-lambda/index.mjs +3 -3
- package/dist/adapters/fastify/index.d.mts +23 -0
- package/dist/adapters/fastify/index.d.ts +23 -0
- package/dist/adapters/fastify/index.mjs +18 -0
- package/dist/adapters/fetch/index.d.mts +9 -5
- package/dist/adapters/fetch/index.d.ts +9 -5
- package/dist/adapters/fetch/index.mjs +3 -3
- package/dist/adapters/node/index.d.mts +9 -5
- package/dist/adapters/node/index.d.ts +9 -5
- package/dist/adapters/node/index.mjs +3 -3
- package/dist/adapters/standard/index.d.mts +7 -16
- package/dist/adapters/standard/index.d.ts +7 -16
- package/dist/adapters/standard/index.mjs +1 -1
- package/dist/index.d.mts +10 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.mjs +2 -2
- package/dist/plugins/index.d.mts +20 -3
- package/dist/plugins/index.d.ts +20 -3
- package/dist/plugins/index.mjs +69 -20
- package/dist/shared/{openapi.BkBlt1Qf.mjs → openapi.BB-W-NKv.mjs} +22 -5
- package/dist/shared/{openapi.DPAN3GVs.d.mts → openapi.BGy4N6eR.d.mts} +15 -3
- package/dist/shared/{openapi.DPAN3GVs.d.ts → openapi.BGy4N6eR.d.ts} +15 -3
- package/dist/shared/{openapi.BtoY8ZFF.mjs → openapi.BwdtJjDu.mjs} +185 -49
- package/dist/shared/openapi.DwaweYRb.d.mts +54 -0
- package/dist/shared/openapi.DwaweYRb.d.ts +54 -0
- package/package.json +20 -13
- package/dist/shared/openapi.CQmjvnb0.d.mts +0 -31
- package/dist/shared/openapi.CQmjvnb0.d.ts +0 -31
|
@@ -108,22 +108,47 @@ function isAnySchema(schema) {
|
|
|
108
108
|
if (schema === true) {
|
|
109
109
|
return true;
|
|
110
110
|
}
|
|
111
|
-
if (Object.keys(schema).every((k) => !LOGIC_KEYWORDS.includes(k))) {
|
|
111
|
+
if (Object.keys(schema).filter((v) => schema[v] !== void 0).every((k) => !LOGIC_KEYWORDS.includes(k))) {
|
|
112
112
|
return true;
|
|
113
113
|
}
|
|
114
114
|
return false;
|
|
115
115
|
}
|
|
116
|
+
function isNeverSchema(schema) {
|
|
117
|
+
if (schema === false) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
if (typeof schema === "object" && schema.not !== void 0) {
|
|
121
|
+
if (schema.not === true) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
if (typeof schema.not === "object" && Object.keys(schema.not).length === 0) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
116
130
|
function separateObjectSchema(schema, separatedProperties) {
|
|
117
|
-
if (Object.keys(schema).some(
|
|
131
|
+
if (Object.keys(schema).some(
|
|
132
|
+
(k) => !["type", "properties", "required", "additionalProperties"].includes(k) && LOGIC_KEYWORDS.includes(k) && schema[k] !== void 0
|
|
133
|
+
)) {
|
|
118
134
|
return [{ type: "object" }, schema];
|
|
119
135
|
}
|
|
120
136
|
const matched = { ...schema };
|
|
121
137
|
const rest = { ...schema };
|
|
122
|
-
matched.properties =
|
|
123
|
-
|
|
138
|
+
matched.properties = separatedProperties.reduce((acc, key) => {
|
|
139
|
+
const keySchema = schema.properties?.[key] ?? schema.additionalProperties;
|
|
140
|
+
if (keySchema !== void 0) {
|
|
141
|
+
acc[key] = keySchema;
|
|
142
|
+
}
|
|
124
143
|
return acc;
|
|
125
144
|
}, {});
|
|
145
|
+
if (Object.keys(matched.properties).length === 0) {
|
|
146
|
+
matched.properties = void 0;
|
|
147
|
+
}
|
|
126
148
|
matched.required = schema.required?.filter((key) => separatedProperties.includes(key));
|
|
149
|
+
if (matched.required?.length === 0) {
|
|
150
|
+
matched.required = void 0;
|
|
151
|
+
}
|
|
127
152
|
matched.examples = schema.examples?.map((example) => {
|
|
128
153
|
if (!isObject(example)) {
|
|
129
154
|
return example;
|
|
@@ -135,11 +160,14 @@ function separateObjectSchema(schema, separatedProperties) {
|
|
|
135
160
|
return acc;
|
|
136
161
|
}, {});
|
|
137
162
|
});
|
|
138
|
-
rest.properties = schema.properties && Object.entries(schema.properties).filter(([key]) => !separatedProperties.includes(key)).reduce((acc, [key, value]) => {
|
|
163
|
+
rest.properties = schema.properties && Object.entries(schema.properties).filter(([key]) => !separatedProperties.includes(key)).reduce((acc = {}, [key, value]) => {
|
|
139
164
|
acc[key] = value;
|
|
140
165
|
return acc;
|
|
141
|
-
},
|
|
166
|
+
}, void 0);
|
|
142
167
|
rest.required = schema.required?.filter((key) => !separatedProperties.includes(key));
|
|
168
|
+
if (rest.required?.length === 0) {
|
|
169
|
+
rest.required = void 0;
|
|
170
|
+
}
|
|
143
171
|
rest.examples = schema.examples?.map((example) => {
|
|
144
172
|
if (!isObject(example)) {
|
|
145
173
|
return example;
|
|
@@ -250,7 +278,7 @@ function toOpenAPIContent(schema) {
|
|
|
250
278
|
schema: toOpenAPISchema(file)
|
|
251
279
|
};
|
|
252
280
|
}
|
|
253
|
-
if (restSchema !== void 0) {
|
|
281
|
+
if (restSchema !== void 0 && !isAnySchema(restSchema) && !isNeverSchema(restSchema)) {
|
|
254
282
|
content["application/json"] = {
|
|
255
283
|
schema: toOpenAPISchema(restSchema)
|
|
256
284
|
};
|
|
@@ -354,6 +382,107 @@ function resolveOpenAPIJsonSchemaRef(doc, schema) {
|
|
|
354
382
|
const resolved = doc.components?.schemas?.[name];
|
|
355
383
|
return resolved ?? schema;
|
|
356
384
|
}
|
|
385
|
+
function simplifyComposedObjectJsonSchemasAndRefs(schema, doc) {
|
|
386
|
+
if (doc) {
|
|
387
|
+
schema = resolveOpenAPIJsonSchemaRef(doc, schema);
|
|
388
|
+
}
|
|
389
|
+
if (typeof schema !== "object" || !schema.anyOf && !schema.oneOf && !schema.allOf) {
|
|
390
|
+
return schema;
|
|
391
|
+
}
|
|
392
|
+
const unionSchemas = [
|
|
393
|
+
...toArray(schema.anyOf?.map((s) => simplifyComposedObjectJsonSchemasAndRefs(s, doc))),
|
|
394
|
+
...toArray(schema.oneOf?.map((s) => simplifyComposedObjectJsonSchemasAndRefs(s, doc)))
|
|
395
|
+
];
|
|
396
|
+
const objectUnionSchemas = [];
|
|
397
|
+
for (const u of unionSchemas) {
|
|
398
|
+
if (!isObjectSchema(u)) {
|
|
399
|
+
return schema;
|
|
400
|
+
}
|
|
401
|
+
objectUnionSchemas.push(u);
|
|
402
|
+
}
|
|
403
|
+
const mergedUnionPropertyMap = /* @__PURE__ */ new Map();
|
|
404
|
+
for (const u of objectUnionSchemas) {
|
|
405
|
+
if (u.properties) {
|
|
406
|
+
for (const [key, value] of Object.entries(u.properties)) {
|
|
407
|
+
let entry = mergedUnionPropertyMap.get(key);
|
|
408
|
+
if (!entry) {
|
|
409
|
+
const required = objectUnionSchemas.every((s) => s.required?.includes(key));
|
|
410
|
+
entry = { required, schemas: [] };
|
|
411
|
+
mergedUnionPropertyMap.set(key, entry);
|
|
412
|
+
}
|
|
413
|
+
entry.schemas.push(value);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
const intersectionSchemas = toArray(schema.allOf?.map((s) => simplifyComposedObjectJsonSchemasAndRefs(s, doc)));
|
|
418
|
+
const objectIntersectionSchemas = [];
|
|
419
|
+
for (const u of intersectionSchemas) {
|
|
420
|
+
if (!isObjectSchema(u)) {
|
|
421
|
+
return schema;
|
|
422
|
+
}
|
|
423
|
+
objectIntersectionSchemas.push(u);
|
|
424
|
+
}
|
|
425
|
+
if (isObjectSchema(schema)) {
|
|
426
|
+
objectIntersectionSchemas.push(schema);
|
|
427
|
+
}
|
|
428
|
+
const mergedInteractionPropertyMap = /* @__PURE__ */ new Map();
|
|
429
|
+
for (const u of objectIntersectionSchemas) {
|
|
430
|
+
if (u.properties) {
|
|
431
|
+
for (const [key, value] of Object.entries(u.properties)) {
|
|
432
|
+
let entry = mergedInteractionPropertyMap.get(key);
|
|
433
|
+
if (!entry) {
|
|
434
|
+
const required = objectIntersectionSchemas.some((s) => s.required?.includes(key));
|
|
435
|
+
entry = { required, schemas: [] };
|
|
436
|
+
mergedInteractionPropertyMap.set(key, entry);
|
|
437
|
+
}
|
|
438
|
+
entry.schemas.push(value);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
const resultObjectSchema = { type: "object", properties: {}, required: [] };
|
|
443
|
+
const keys = /* @__PURE__ */ new Set([
|
|
444
|
+
...mergedUnionPropertyMap.keys(),
|
|
445
|
+
...mergedInteractionPropertyMap.keys()
|
|
446
|
+
]);
|
|
447
|
+
if (keys.size === 0) {
|
|
448
|
+
return schema;
|
|
449
|
+
}
|
|
450
|
+
const deduplicateSchemas = (schemas) => {
|
|
451
|
+
const seen = /* @__PURE__ */ new Set();
|
|
452
|
+
const result = [];
|
|
453
|
+
for (const schema2 of schemas) {
|
|
454
|
+
const key = stringifyJSON(schema2);
|
|
455
|
+
if (!seen.has(key)) {
|
|
456
|
+
seen.add(key);
|
|
457
|
+
result.push(schema2);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return result;
|
|
461
|
+
};
|
|
462
|
+
for (const key of keys) {
|
|
463
|
+
const unionEntry = mergedUnionPropertyMap.get(key);
|
|
464
|
+
const intersectionEntry = mergedInteractionPropertyMap.get(key);
|
|
465
|
+
resultObjectSchema.properties[key] = (() => {
|
|
466
|
+
const dedupedUnionSchemas = unionEntry ? deduplicateSchemas(unionEntry.schemas) : [];
|
|
467
|
+
const dedupedIntersectionSchemas = intersectionEntry ? deduplicateSchemas(intersectionEntry.schemas) : [];
|
|
468
|
+
if (!dedupedUnionSchemas.length) {
|
|
469
|
+
return dedupedIntersectionSchemas.length === 1 ? dedupedIntersectionSchemas[0] : { allOf: dedupedIntersectionSchemas };
|
|
470
|
+
}
|
|
471
|
+
if (!dedupedIntersectionSchemas.length) {
|
|
472
|
+
return dedupedUnionSchemas.length === 1 ? dedupedUnionSchemas[0] : { anyOf: dedupedUnionSchemas };
|
|
473
|
+
}
|
|
474
|
+
const allOf = deduplicateSchemas([
|
|
475
|
+
...dedupedIntersectionSchemas,
|
|
476
|
+
dedupedUnionSchemas.length === 1 ? dedupedUnionSchemas[0] : { anyOf: dedupedUnionSchemas }
|
|
477
|
+
]);
|
|
478
|
+
return allOf.length === 1 ? allOf[0] : { allOf };
|
|
479
|
+
})();
|
|
480
|
+
if (unionEntry?.required || intersectionEntry?.required) {
|
|
481
|
+
resultObjectSchema.required.push(key);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return resultObjectSchema;
|
|
485
|
+
}
|
|
357
486
|
|
|
358
487
|
class CompositeSchemaConverter {
|
|
359
488
|
converters;
|
|
@@ -382,21 +511,18 @@ class OpenAPIGenerator {
|
|
|
382
511
|
/**
|
|
383
512
|
* Generates OpenAPI specifications from oRPC routers/contracts.
|
|
384
513
|
*
|
|
385
|
-
* @see {@link https://orpc.
|
|
514
|
+
* @see {@link https://orpc.dev/docs/openapi/openapi-specification OpenAPI Specification Docs}
|
|
386
515
|
*/
|
|
387
|
-
async generate(router,
|
|
388
|
-
const filter =
|
|
389
|
-
return !(
|
|
516
|
+
async generate(router, { customErrorResponseBodySchema, commonSchemas, filter: baseFilter, exclude, ...baseDoc } = {}) {
|
|
517
|
+
const filter = baseFilter ?? (({ contract, path }) => {
|
|
518
|
+
return !(exclude?.(contract, path) ?? false);
|
|
390
519
|
});
|
|
391
520
|
const doc = {
|
|
392
|
-
...clone(
|
|
393
|
-
info:
|
|
394
|
-
openapi: "3.1.1"
|
|
395
|
-
exclude: void 0,
|
|
396
|
-
filter: void 0,
|
|
397
|
-
commonSchemas: void 0
|
|
521
|
+
...clone(baseDoc),
|
|
522
|
+
info: baseDoc.info ?? { title: "API Reference", version: "0.0.0" },
|
|
523
|
+
openapi: "3.1.1"
|
|
398
524
|
};
|
|
399
|
-
const { baseSchemaConvertOptions, undefinedErrorJsonSchema } = await this.#resolveCommonSchemas(doc,
|
|
525
|
+
const { baseSchemaConvertOptions, undefinedErrorJsonSchema } = await this.#resolveCommonSchemas(doc, commonSchemas);
|
|
400
526
|
const contracts = [];
|
|
401
527
|
await resolveContractProcedures({ path: [], router }, (traverseOptions) => {
|
|
402
528
|
if (!value(filter, traverseOptions)) {
|
|
@@ -406,17 +532,17 @@ class OpenAPIGenerator {
|
|
|
406
532
|
});
|
|
407
533
|
const errors = [];
|
|
408
534
|
for (const { contract, path } of contracts) {
|
|
409
|
-
const
|
|
535
|
+
const stringPath = path.join(".");
|
|
410
536
|
try {
|
|
411
537
|
const def = contract["~orpc"];
|
|
412
538
|
const method = toOpenAPIMethod(fallbackContractConfig("defaultMethod", def.route.method));
|
|
413
539
|
const httpPath = toOpenAPIPath(def.route.path ?? toHttpPath(path));
|
|
414
540
|
let operationObjectRef;
|
|
415
|
-
if (def.route.spec !== void 0) {
|
|
541
|
+
if (def.route.spec !== void 0 && typeof def.route.spec !== "function") {
|
|
416
542
|
operationObjectRef = def.route.spec;
|
|
417
543
|
} else {
|
|
418
544
|
operationObjectRef = {
|
|
419
|
-
operationId,
|
|
545
|
+
operationId: def.route.operationId ?? stringPath,
|
|
420
546
|
summary: def.route.summary,
|
|
421
547
|
description: def.route.description,
|
|
422
548
|
deprecated: def.route.deprecated,
|
|
@@ -424,7 +550,10 @@ class OpenAPIGenerator {
|
|
|
424
550
|
};
|
|
425
551
|
await this.#request(doc, operationObjectRef, def, baseSchemaConvertOptions);
|
|
426
552
|
await this.#successResponse(doc, operationObjectRef, def, baseSchemaConvertOptions);
|
|
427
|
-
await this.#errorResponse(operationObjectRef, def, baseSchemaConvertOptions, undefinedErrorJsonSchema);
|
|
553
|
+
await this.#errorResponse(operationObjectRef, def, baseSchemaConvertOptions, undefinedErrorJsonSchema, customErrorResponseBodySchema);
|
|
554
|
+
}
|
|
555
|
+
if (typeof def.route.spec === "function") {
|
|
556
|
+
operationObjectRef = def.route.spec(operationObjectRef);
|
|
428
557
|
}
|
|
429
558
|
doc.paths ??= {};
|
|
430
559
|
doc.paths[httpPath] ??= {};
|
|
@@ -434,7 +563,7 @@ class OpenAPIGenerator {
|
|
|
434
563
|
throw e;
|
|
435
564
|
}
|
|
436
565
|
errors.push(
|
|
437
|
-
`[OpenAPIGenerator] Error occurred while generating OpenAPI for procedure at path: ${
|
|
566
|
+
`[OpenAPIGenerator] Error occurred while generating OpenAPI for procedure at path: ${stringPath}
|
|
438
567
|
${e.message}`
|
|
439
568
|
);
|
|
440
569
|
}
|
|
@@ -534,13 +663,16 @@ ${errors.join("\n\n")}`
|
|
|
534
663
|
def.inputSchema,
|
|
535
664
|
{
|
|
536
665
|
...baseSchemaConvertOptions,
|
|
537
|
-
strategy: "input"
|
|
538
|
-
minStructureDepthForRef: dynamicParams?.length || inputStructure === "detailed" ? 1 : 0
|
|
666
|
+
strategy: "input"
|
|
539
667
|
}
|
|
540
668
|
);
|
|
669
|
+
let omitResponseBody = false;
|
|
541
670
|
if (isAnySchema(schema) && !dynamicParams?.length) {
|
|
542
671
|
return;
|
|
543
672
|
}
|
|
673
|
+
if (inputStructure === "detailed" || inputStructure === "compact" && (dynamicParams?.length || method === "GET")) {
|
|
674
|
+
schema = simplifyComposedObjectJsonSchemasAndRefs(schema, doc);
|
|
675
|
+
}
|
|
544
676
|
if (inputStructure === "compact") {
|
|
545
677
|
if (dynamicParams?.length) {
|
|
546
678
|
const error2 = new OpenAPIGeneratorError(
|
|
@@ -552,6 +684,7 @@ ${errors.join("\n\n")}`
|
|
|
552
684
|
const [paramsSchema, rest] = separateObjectSchema(schema, dynamicParams);
|
|
553
685
|
schema = rest;
|
|
554
686
|
required = rest.required ? rest.required.length !== 0 : false;
|
|
687
|
+
omitResponseBody = !required && !rest.properties;
|
|
555
688
|
if (!checkParamsSchema(paramsSchema, dynamicParams)) {
|
|
556
689
|
throw error2;
|
|
557
690
|
}
|
|
@@ -566,7 +699,7 @@ ${errors.join("\n\n")}`
|
|
|
566
699
|
}
|
|
567
700
|
ref.parameters ??= [];
|
|
568
701
|
ref.parameters.push(...toOpenAPIParameters(schema, "query"));
|
|
569
|
-
} else {
|
|
702
|
+
} else if (!omitResponseBody) {
|
|
570
703
|
ref.requestBody = {
|
|
571
704
|
required,
|
|
572
705
|
content: toOpenAPIContent(schema)
|
|
@@ -580,7 +713,7 @@ ${errors.join("\n\n")}`
|
|
|
580
713
|
if (!isObjectSchema(schema)) {
|
|
581
714
|
throw error;
|
|
582
715
|
}
|
|
583
|
-
const resolvedParamSchema = schema.properties?.params !== void 0 ?
|
|
716
|
+
const resolvedParamSchema = schema.properties?.params !== void 0 ? simplifyComposedObjectJsonSchemasAndRefs(schema.properties.params, doc) : void 0;
|
|
584
717
|
if (dynamicParams?.length && (resolvedParamSchema === void 0 || !isObjectSchema(resolvedParamSchema) || !checkParamsSchema(resolvedParamSchema, dynamicParams))) {
|
|
585
718
|
throw new OpenAPIGeneratorError(
|
|
586
719
|
'When input structure is "detailed" and path has dynamic params, the "params" schema must be an object with all dynamic params as required.'
|
|
@@ -589,7 +722,7 @@ ${errors.join("\n\n")}`
|
|
|
589
722
|
for (const from of ["params", "query", "headers"]) {
|
|
590
723
|
const fromSchema = schema.properties?.[from];
|
|
591
724
|
if (fromSchema !== void 0) {
|
|
592
|
-
const resolvedSchema =
|
|
725
|
+
const resolvedSchema = simplifyComposedObjectJsonSchemasAndRefs(fromSchema, doc);
|
|
593
726
|
if (!isObjectSchema(resolvedSchema)) {
|
|
594
727
|
throw error;
|
|
595
728
|
}
|
|
@@ -650,13 +783,14 @@ ${errors.join("\n\n")}`
|
|
|
650
783
|
|
|
651
784
|
But got: ${stringifyJSON(item)}
|
|
652
785
|
`);
|
|
653
|
-
|
|
786
|
+
const simplifiedItem = simplifyComposedObjectJsonSchemasAndRefs(item, doc);
|
|
787
|
+
if (!isObjectSchema(simplifiedItem)) {
|
|
654
788
|
throw error;
|
|
655
789
|
}
|
|
656
790
|
let schemaStatus;
|
|
657
791
|
let schemaDescription;
|
|
658
|
-
if (
|
|
659
|
-
const statusSchema = resolveOpenAPIJsonSchemaRef(doc,
|
|
792
|
+
if (simplifiedItem.properties?.status !== void 0) {
|
|
793
|
+
const statusSchema = resolveOpenAPIJsonSchemaRef(doc, simplifiedItem.properties.status);
|
|
660
794
|
if (typeof statusSchema !== "object" || statusSchema.const === void 0 || typeof statusSchema.const !== "number" || !Number.isInteger(statusSchema.const) || isORPCErrorStatus(statusSchema.const)) {
|
|
661
795
|
throw error;
|
|
662
796
|
}
|
|
@@ -676,8 +810,8 @@ ${errors.join("\n\n")}`
|
|
|
676
810
|
ref.responses[itemStatus] = {
|
|
677
811
|
description: itemDescription
|
|
678
812
|
};
|
|
679
|
-
if (
|
|
680
|
-
const headersSchema =
|
|
813
|
+
if (simplifiedItem.properties?.headers !== void 0) {
|
|
814
|
+
const headersSchema = simplifyComposedObjectJsonSchemasAndRefs(simplifiedItem.properties.headers, doc);
|
|
681
815
|
if (!isObjectSchema(headersSchema)) {
|
|
682
816
|
throw error;
|
|
683
817
|
}
|
|
@@ -687,50 +821,52 @@ ${errors.join("\n\n")}`
|
|
|
687
821
|
ref.responses[itemStatus].headers ??= {};
|
|
688
822
|
ref.responses[itemStatus].headers[key] = {
|
|
689
823
|
schema: toOpenAPISchema(headerSchema),
|
|
690
|
-
required:
|
|
824
|
+
required: simplifiedItem.required?.includes("headers") && headersSchema.required?.includes(key)
|
|
691
825
|
};
|
|
692
826
|
}
|
|
693
827
|
}
|
|
694
828
|
}
|
|
695
|
-
if (
|
|
829
|
+
if (simplifiedItem.properties?.body !== void 0) {
|
|
696
830
|
ref.responses[itemStatus].content = toOpenAPIContent(
|
|
697
|
-
applySchemaOptionality(
|
|
831
|
+
applySchemaOptionality(simplifiedItem.required?.includes("body") ?? false, simplifiedItem.properties.body)
|
|
698
832
|
);
|
|
699
833
|
}
|
|
700
834
|
}
|
|
701
835
|
}
|
|
702
|
-
async #errorResponse(ref, def, baseSchemaConvertOptions, undefinedErrorSchema) {
|
|
836
|
+
async #errorResponse(ref, def, baseSchemaConvertOptions, undefinedErrorSchema, customErrorResponseBodySchema) {
|
|
703
837
|
const errorMap = def.errorMap;
|
|
704
|
-
const
|
|
838
|
+
const errorResponsesByStatus = {};
|
|
705
839
|
for (const code in errorMap) {
|
|
706
840
|
const config = errorMap[code];
|
|
707
841
|
if (!config) {
|
|
708
842
|
continue;
|
|
709
843
|
}
|
|
710
844
|
const status = fallbackORPCErrorStatus(code, config.status);
|
|
711
|
-
const
|
|
845
|
+
const defaultMessage = fallbackORPCErrorMessage(code, config.message);
|
|
846
|
+
errorResponsesByStatus[status] ??= { status, definedErrorDefinitions: [], errorSchemaVariants: [] };
|
|
712
847
|
const [dataRequired, dataSchema] = await this.converter.convert(config.data, { ...baseSchemaConvertOptions, strategy: "output" });
|
|
713
|
-
|
|
714
|
-
|
|
848
|
+
errorResponsesByStatus[status].definedErrorDefinitions.push([code, defaultMessage, dataRequired, dataSchema]);
|
|
849
|
+
errorResponsesByStatus[status].errorSchemaVariants.push({
|
|
715
850
|
type: "object",
|
|
716
851
|
properties: {
|
|
717
852
|
defined: { const: true },
|
|
718
853
|
code: { const: code },
|
|
719
854
|
status: { const: status },
|
|
720
|
-
message: { type: "string", default:
|
|
855
|
+
message: { type: "string", default: defaultMessage },
|
|
721
856
|
data: dataSchema
|
|
722
857
|
},
|
|
723
858
|
required: dataRequired ? ["defined", "code", "status", "message", "data"] : ["defined", "code", "status", "message"]
|
|
724
859
|
});
|
|
725
860
|
}
|
|
726
861
|
ref.responses ??= {};
|
|
727
|
-
for (const
|
|
728
|
-
const
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
862
|
+
for (const statusString in errorResponsesByStatus) {
|
|
863
|
+
const errorResponse = errorResponsesByStatus[statusString];
|
|
864
|
+
const customBodySchema = value(customErrorResponseBodySchema, errorResponse.definedErrorDefinitions, errorResponse.status);
|
|
865
|
+
ref.responses[statusString] = {
|
|
866
|
+
description: statusString,
|
|
867
|
+
content: toOpenAPIContent(customBodySchema ?? {
|
|
732
868
|
oneOf: [
|
|
733
|
-
...
|
|
869
|
+
...errorResponse.errorSchemaVariants,
|
|
734
870
|
undefinedErrorSchema
|
|
735
871
|
]
|
|
736
872
|
})
|
|
@@ -739,4 +875,4 @@ ${errors.join("\n\n")}`
|
|
|
739
875
|
}
|
|
740
876
|
}
|
|
741
877
|
|
|
742
|
-
export { CompositeSchemaConverter as C, LOGIC_KEYWORDS as L, OpenAPIGenerator as O, applyCustomOpenAPIOperation as a, toOpenAPIMethod as b, customOpenAPIOperation as c, toOpenAPIContent as d, toOpenAPIEventIteratorContent as e, toOpenAPIParameters as f, getCustomOpenAPIOperation as g, checkParamsSchema as h, toOpenAPISchema as i, isFileSchema as j, isObjectSchema as k, isAnySchema as l,
|
|
878
|
+
export { CompositeSchemaConverter as C, LOGIC_KEYWORDS as L, OpenAPIGenerator as O, applyCustomOpenAPIOperation as a, toOpenAPIMethod as b, customOpenAPIOperation as c, toOpenAPIContent as d, toOpenAPIEventIteratorContent as e, toOpenAPIParameters as f, getCustomOpenAPIOperation as g, checkParamsSchema as h, toOpenAPISchema as i, isFileSchema as j, isObjectSchema as k, isAnySchema as l, isNeverSchema as m, separateObjectSchema as n, filterSchemaBranches as o, applySchemaOptionality as p, expandUnionSchema as q, resolveOpenAPIJsonSchemaRef as r, simplifyComposedObjectJsonSchemasAndRefs as s, toOpenAPIPath as t, expandArrayableSchema as u, isPrimitiveSchema as v };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { StandardOpenAPISerializer, StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions } from '@orpc/openapi-client/standard';
|
|
2
|
+
import { AnyProcedure, TraverseContractProcedureCallbackOptions, AnyRouter, Context, Router } from '@orpc/server';
|
|
3
|
+
import { StandardCodec, StandardParams, StandardMatcher, StandardMatchResult, StandardHandlerOptions, StandardHandler } from '@orpc/server/standard';
|
|
4
|
+
import { ORPCError, HTTPPath } from '@orpc/client';
|
|
5
|
+
import { StandardLazyRequest, StandardResponse } from '@orpc/standard-server';
|
|
6
|
+
import { Value } from '@orpc/shared';
|
|
7
|
+
|
|
8
|
+
interface StandardOpenAPICodecOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Customize how an ORPC error is encoded into a response body.
|
|
11
|
+
* Use this if your API needs a different error output structure.
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* - Return `null | undefined` to fallback to default behavior
|
|
15
|
+
*
|
|
16
|
+
* @default ((e) => e.toJSON())
|
|
17
|
+
*/
|
|
18
|
+
customErrorResponseBodyEncoder?: (error: ORPCError<any, any>) => unknown;
|
|
19
|
+
}
|
|
20
|
+
declare class StandardOpenAPICodec implements StandardCodec {
|
|
21
|
+
#private;
|
|
22
|
+
private readonly serializer;
|
|
23
|
+
private readonly customErrorResponseBodyEncoder;
|
|
24
|
+
constructor(serializer: StandardOpenAPISerializer, options?: StandardOpenAPICodecOptions);
|
|
25
|
+
decode(request: StandardLazyRequest, params: StandardParams | undefined, procedure: AnyProcedure): Promise<unknown>;
|
|
26
|
+
encode(output: unknown, procedure: AnyProcedure): StandardResponse;
|
|
27
|
+
encodeError(error: ORPCError<any, any>): StandardResponse;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface StandardOpenAPIMatcherOptions {
|
|
31
|
+
/**
|
|
32
|
+
* Filter procedures. Return `false` to exclude a procedure from matching.
|
|
33
|
+
*
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
36
|
+
filter?: Value<boolean, [options: TraverseContractProcedureCallbackOptions]>;
|
|
37
|
+
}
|
|
38
|
+
declare class StandardOpenAPIMatcher implements StandardMatcher {
|
|
39
|
+
private readonly filter;
|
|
40
|
+
private readonly tree;
|
|
41
|
+
private pendingRouters;
|
|
42
|
+
constructor(options?: StandardOpenAPIMatcherOptions);
|
|
43
|
+
init(router: AnyRouter, path?: readonly string[]): void;
|
|
44
|
+
match(method: string, pathname: HTTPPath): Promise<StandardMatchResult>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface StandardOpenAPIHandlerOptions<T extends Context> extends StandardHandlerOptions<T>, StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions, StandardOpenAPIMatcherOptions, StandardOpenAPICodecOptions {
|
|
48
|
+
}
|
|
49
|
+
declare class StandardOpenAPIHandler<T extends Context> extends StandardHandler<T> {
|
|
50
|
+
constructor(router: Router<any, T>, options: NoInfer<StandardOpenAPIHandlerOptions<T>>);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { StandardOpenAPICodec as a, StandardOpenAPIHandler as c, StandardOpenAPIMatcher as e };
|
|
54
|
+
export type { StandardOpenAPICodecOptions as S, StandardOpenAPIHandlerOptions as b, StandardOpenAPIMatcherOptions as d };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { StandardOpenAPISerializer, StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions } from '@orpc/openapi-client/standard';
|
|
2
|
+
import { AnyProcedure, TraverseContractProcedureCallbackOptions, AnyRouter, Context, Router } from '@orpc/server';
|
|
3
|
+
import { StandardCodec, StandardParams, StandardMatcher, StandardMatchResult, StandardHandlerOptions, StandardHandler } from '@orpc/server/standard';
|
|
4
|
+
import { ORPCError, HTTPPath } from '@orpc/client';
|
|
5
|
+
import { StandardLazyRequest, StandardResponse } from '@orpc/standard-server';
|
|
6
|
+
import { Value } from '@orpc/shared';
|
|
7
|
+
|
|
8
|
+
interface StandardOpenAPICodecOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Customize how an ORPC error is encoded into a response body.
|
|
11
|
+
* Use this if your API needs a different error output structure.
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* - Return `null | undefined` to fallback to default behavior
|
|
15
|
+
*
|
|
16
|
+
* @default ((e) => e.toJSON())
|
|
17
|
+
*/
|
|
18
|
+
customErrorResponseBodyEncoder?: (error: ORPCError<any, any>) => unknown;
|
|
19
|
+
}
|
|
20
|
+
declare class StandardOpenAPICodec implements StandardCodec {
|
|
21
|
+
#private;
|
|
22
|
+
private readonly serializer;
|
|
23
|
+
private readonly customErrorResponseBodyEncoder;
|
|
24
|
+
constructor(serializer: StandardOpenAPISerializer, options?: StandardOpenAPICodecOptions);
|
|
25
|
+
decode(request: StandardLazyRequest, params: StandardParams | undefined, procedure: AnyProcedure): Promise<unknown>;
|
|
26
|
+
encode(output: unknown, procedure: AnyProcedure): StandardResponse;
|
|
27
|
+
encodeError(error: ORPCError<any, any>): StandardResponse;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface StandardOpenAPIMatcherOptions {
|
|
31
|
+
/**
|
|
32
|
+
* Filter procedures. Return `false` to exclude a procedure from matching.
|
|
33
|
+
*
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
36
|
+
filter?: Value<boolean, [options: TraverseContractProcedureCallbackOptions]>;
|
|
37
|
+
}
|
|
38
|
+
declare class StandardOpenAPIMatcher implements StandardMatcher {
|
|
39
|
+
private readonly filter;
|
|
40
|
+
private readonly tree;
|
|
41
|
+
private pendingRouters;
|
|
42
|
+
constructor(options?: StandardOpenAPIMatcherOptions);
|
|
43
|
+
init(router: AnyRouter, path?: readonly string[]): void;
|
|
44
|
+
match(method: string, pathname: HTTPPath): Promise<StandardMatchResult>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface StandardOpenAPIHandlerOptions<T extends Context> extends StandardHandlerOptions<T>, StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions, StandardOpenAPIMatcherOptions, StandardOpenAPICodecOptions {
|
|
48
|
+
}
|
|
49
|
+
declare class StandardOpenAPIHandler<T extends Context> extends StandardHandler<T> {
|
|
50
|
+
constructor(router: Router<any, T>, options: NoInfer<StandardOpenAPIHandlerOptions<T>>);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { StandardOpenAPICodec as a, StandardOpenAPIHandler as c, StandardOpenAPIMatcher as e };
|
|
54
|
+
export type { StandardOpenAPICodecOptions as S, StandardOpenAPIHandlerOptions as b, StandardOpenAPIMatcherOptions as d };
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orpc/openapi",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.0-next.
|
|
4
|
+
"version": "0.0.0-next.36cf381",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"homepage": "https://orpc.
|
|
6
|
+
"homepage": "https://orpc.dev",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/
|
|
9
|
+
"url": "git+https://github.com/middleapi/orpc.git",
|
|
10
10
|
"directory": "packages/openapi"
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|
|
13
|
-
"unnoq",
|
|
14
13
|
"orpc"
|
|
15
14
|
],
|
|
15
|
+
"sideEffects": false,
|
|
16
16
|
"exports": {
|
|
17
17
|
".": {
|
|
18
18
|
"types": "./dist/index.d.mts",
|
|
@@ -39,6 +39,11 @@
|
|
|
39
39
|
"import": "./dist/adapters/node/index.mjs",
|
|
40
40
|
"default": "./dist/adapters/node/index.mjs"
|
|
41
41
|
},
|
|
42
|
+
"./fastify": {
|
|
43
|
+
"types": "./dist/adapters/fastify/index.d.mts",
|
|
44
|
+
"import": "./dist/adapters/fastify/index.mjs",
|
|
45
|
+
"default": "./dist/adapters/fastify/index.mjs"
|
|
46
|
+
},
|
|
42
47
|
"./aws-lambda": {
|
|
43
48
|
"types": "./dist/adapters/aws-lambda/index.d.mts",
|
|
44
49
|
"import": "./dist/adapters/aws-lambda/index.mjs",
|
|
@@ -49,17 +54,19 @@
|
|
|
49
54
|
"dist"
|
|
50
55
|
],
|
|
51
56
|
"dependencies": {
|
|
52
|
-
"json-schema-typed": "^8.0.
|
|
53
|
-
"rou3": "^0.7.
|
|
54
|
-
"@orpc/
|
|
55
|
-
"@orpc/
|
|
56
|
-
"@orpc/
|
|
57
|
-
"@orpc/client": "0.0.0-next.
|
|
58
|
-
"@orpc/
|
|
59
|
-
"@orpc/shared": "0.0.0-next.
|
|
57
|
+
"json-schema-typed": "^8.0.2",
|
|
58
|
+
"rou3": "^0.7.12",
|
|
59
|
+
"@orpc/client": "0.0.0-next.36cf381",
|
|
60
|
+
"@orpc/interop": "0.0.0-next.36cf381",
|
|
61
|
+
"@orpc/contract": "0.0.0-next.36cf381",
|
|
62
|
+
"@orpc/openapi-client": "0.0.0-next.36cf381",
|
|
63
|
+
"@orpc/server": "0.0.0-next.36cf381",
|
|
64
|
+
"@orpc/shared": "0.0.0-next.36cf381",
|
|
65
|
+
"@orpc/standard-server": "0.0.0-next.36cf381"
|
|
60
66
|
},
|
|
61
67
|
"devDependencies": {
|
|
62
|
-
"
|
|
68
|
+
"fastify": "^5.8.3",
|
|
69
|
+
"zod": "^4.3.6"
|
|
63
70
|
},
|
|
64
71
|
"scripts": {
|
|
65
72
|
"build": "unbuild",
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions } from '@orpc/openapi-client/standard';
|
|
2
|
-
import { TraverseContractProcedureCallbackOptions, AnyRouter, Context, Router } from '@orpc/server';
|
|
3
|
-
import { StandardMatcher, StandardMatchResult, StandardHandlerOptions, StandardHandler } from '@orpc/server/standard';
|
|
4
|
-
import { HTTPPath } from '@orpc/client';
|
|
5
|
-
import { Value } from '@orpc/shared';
|
|
6
|
-
|
|
7
|
-
interface StandardOpenAPIMatcherOptions {
|
|
8
|
-
/**
|
|
9
|
-
* Filter procedures. Return `false` to exclude a procedure from matching.
|
|
10
|
-
*
|
|
11
|
-
* @default true
|
|
12
|
-
*/
|
|
13
|
-
filter?: Value<boolean, [options: TraverseContractProcedureCallbackOptions]>;
|
|
14
|
-
}
|
|
15
|
-
declare class StandardOpenAPIMatcher implements StandardMatcher {
|
|
16
|
-
private readonly filter;
|
|
17
|
-
private readonly tree;
|
|
18
|
-
private pendingRouters;
|
|
19
|
-
constructor(options?: StandardOpenAPIMatcherOptions);
|
|
20
|
-
init(router: AnyRouter, path?: readonly string[]): void;
|
|
21
|
-
match(method: string, pathname: HTTPPath): Promise<StandardMatchResult>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface StandardOpenAPIHandlerOptions<T extends Context> extends StandardHandlerOptions<T>, StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions, StandardOpenAPIMatcherOptions {
|
|
25
|
-
}
|
|
26
|
-
declare class StandardOpenAPIHandler<T extends Context> extends StandardHandler<T> {
|
|
27
|
-
constructor(router: Router<any, T>, options: NoInfer<StandardOpenAPIHandlerOptions<T>>);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export { StandardOpenAPIHandler as a, StandardOpenAPIMatcher as c };
|
|
31
|
-
export type { StandardOpenAPIHandlerOptions as S, StandardOpenAPIMatcherOptions as b };
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions } from '@orpc/openapi-client/standard';
|
|
2
|
-
import { TraverseContractProcedureCallbackOptions, AnyRouter, Context, Router } from '@orpc/server';
|
|
3
|
-
import { StandardMatcher, StandardMatchResult, StandardHandlerOptions, StandardHandler } from '@orpc/server/standard';
|
|
4
|
-
import { HTTPPath } from '@orpc/client';
|
|
5
|
-
import { Value } from '@orpc/shared';
|
|
6
|
-
|
|
7
|
-
interface StandardOpenAPIMatcherOptions {
|
|
8
|
-
/**
|
|
9
|
-
* Filter procedures. Return `false` to exclude a procedure from matching.
|
|
10
|
-
*
|
|
11
|
-
* @default true
|
|
12
|
-
*/
|
|
13
|
-
filter?: Value<boolean, [options: TraverseContractProcedureCallbackOptions]>;
|
|
14
|
-
}
|
|
15
|
-
declare class StandardOpenAPIMatcher implements StandardMatcher {
|
|
16
|
-
private readonly filter;
|
|
17
|
-
private readonly tree;
|
|
18
|
-
private pendingRouters;
|
|
19
|
-
constructor(options?: StandardOpenAPIMatcherOptions);
|
|
20
|
-
init(router: AnyRouter, path?: readonly string[]): void;
|
|
21
|
-
match(method: string, pathname: HTTPPath): Promise<StandardMatchResult>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface StandardOpenAPIHandlerOptions<T extends Context> extends StandardHandlerOptions<T>, StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions, StandardOpenAPIMatcherOptions {
|
|
25
|
-
}
|
|
26
|
-
declare class StandardOpenAPIHandler<T extends Context> extends StandardHandler<T> {
|
|
27
|
-
constructor(router: Router<any, T>, options: NoInfer<StandardOpenAPIHandlerOptions<T>>);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export { StandardOpenAPIHandler as a, StandardOpenAPIMatcher as c };
|
|
31
|
-
export type { StandardOpenAPIHandlerOptions as S, StandardOpenAPIMatcherOptions as b };
|