@orpc/openapi 1.4.5 → 1.5.1
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/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.mjs +2 -2
- package/dist/plugins/index.d.mts +1 -1
- package/dist/plugins/index.d.ts +1 -1
- package/dist/plugins/index.mjs +1 -1
- package/dist/shared/{openapi.DaYgbD_w.mjs → openapi.C_3bk7bB.mjs} +98 -25
- package/dist/shared/{openapi.qZLdpE0a.d.mts → openapi.CbIlrReM.d.mts} +48 -2
- package/dist/shared/{openapi.qZLdpE0a.d.ts → openapi.CbIlrReM.d.ts} +48 -2
- package/package.json +8 -8
package/dist/index.d.mts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { OpenAPI, AnyContractProcedure } from '@orpc/contract';
|
2
2
|
export { OpenAPI } from '@orpc/contract';
|
3
|
-
export {
|
3
|
+
export { e as CompositeSchemaConverter, C as ConditionalSchemaConverter, b as OpenAPIGenerator, a as OpenAPIGeneratorGenerateOptions, O as OpenAPIGeneratorOptions, c as SchemaConvertOptions, d as SchemaConverter, S as SchemaConverterComponent } from './shared/openapi.CbIlrReM.mjs';
|
4
4
|
import { HTTPPath, HTTPMethod } from '@orpc/client';
|
5
5
|
import { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
6
6
|
export { JSONSchema, ContentEncoding as JSONSchemaContentEncoding, Format as JSONSchemaFormat, TypeName as JSONSchemaTypeName } from 'json-schema-typed/draft-2020-12';
|
@@ -65,6 +65,7 @@ declare function checkParamsSchema(schema: ObjectSchema, params: string[]): bool
|
|
65
65
|
* @internal
|
66
66
|
*/
|
67
67
|
declare function toOpenAPISchema(schema: JSONSchema): OpenAPI.SchemaObject & object;
|
68
|
+
declare function resolveOpenAPIJsonSchemaRef(doc: OpenAPI.Document, schema: JSONSchema): JSONSchema;
|
68
69
|
|
69
70
|
declare function createJsonifiedRouterClient<T extends AnyRouter, TClientContext extends ClientContext>(router: Lazyable<T | undefined>, ...rest: MaybeOptionalOptions<CreateProcedureClientOptions<InferRouterInitialContext<T>, Schema<unknown, unknown>, ErrorMap, Meta, TClientContext>>): JsonifiedClient<RouterClient<T, TClientContext>>;
|
70
71
|
|
@@ -105,5 +106,5 @@ declare const oo: {
|
|
105
106
|
spec: typeof customOpenAPIOperation;
|
106
107
|
};
|
107
108
|
|
108
|
-
export { LOGIC_KEYWORDS, applyCustomOpenAPIOperation, applySchemaOptionality, checkParamsSchema, createJsonifiedRouterClient, customOpenAPIOperation, expandArrayableSchema, expandUnionSchema, filterSchemaBranches, getCustomOpenAPIOperation, isAnySchema, isFileSchema, isObjectSchema, isPrimitiveSchema, oo, separateObjectSchema, toOpenAPIContent, toOpenAPIEventIteratorContent, toOpenAPIMethod, toOpenAPIParameters, toOpenAPIPath, toOpenAPISchema };
|
109
|
+
export { LOGIC_KEYWORDS, applyCustomOpenAPIOperation, applySchemaOptionality, checkParamsSchema, createJsonifiedRouterClient, customOpenAPIOperation, expandArrayableSchema, expandUnionSchema, filterSchemaBranches, getCustomOpenAPIOperation, isAnySchema, isFileSchema, isObjectSchema, isPrimitiveSchema, oo, resolveOpenAPIJsonSchemaRef, separateObjectSchema, toOpenAPIContent, toOpenAPIEventIteratorContent, toOpenAPIMethod, toOpenAPIParameters, toOpenAPIPath, toOpenAPISchema };
|
109
110
|
export type { FileSchema, ObjectSchema, OverrideOperationValue };
|
package/dist/index.d.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { OpenAPI, AnyContractProcedure } from '@orpc/contract';
|
2
2
|
export { OpenAPI } from '@orpc/contract';
|
3
|
-
export {
|
3
|
+
export { e as CompositeSchemaConverter, C as ConditionalSchemaConverter, b as OpenAPIGenerator, a as OpenAPIGeneratorGenerateOptions, O as OpenAPIGeneratorOptions, c as SchemaConvertOptions, d as SchemaConverter, S as SchemaConverterComponent } from './shared/openapi.CbIlrReM.js';
|
4
4
|
import { HTTPPath, HTTPMethod } from '@orpc/client';
|
5
5
|
import { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
6
6
|
export { JSONSchema, ContentEncoding as JSONSchemaContentEncoding, Format as JSONSchemaFormat, TypeName as JSONSchemaTypeName } from 'json-schema-typed/draft-2020-12';
|
@@ -65,6 +65,7 @@ declare function checkParamsSchema(schema: ObjectSchema, params: string[]): bool
|
|
65
65
|
* @internal
|
66
66
|
*/
|
67
67
|
declare function toOpenAPISchema(schema: JSONSchema): OpenAPI.SchemaObject & object;
|
68
|
+
declare function resolveOpenAPIJsonSchemaRef(doc: OpenAPI.Document, schema: JSONSchema): JSONSchema;
|
68
69
|
|
69
70
|
declare function createJsonifiedRouterClient<T extends AnyRouter, TClientContext extends ClientContext>(router: Lazyable<T | undefined>, ...rest: MaybeOptionalOptions<CreateProcedureClientOptions<InferRouterInitialContext<T>, Schema<unknown, unknown>, ErrorMap, Meta, TClientContext>>): JsonifiedClient<RouterClient<T, TClientContext>>;
|
70
71
|
|
@@ -105,5 +106,5 @@ declare const oo: {
|
|
105
106
|
spec: typeof customOpenAPIOperation;
|
106
107
|
};
|
107
108
|
|
108
|
-
export { LOGIC_KEYWORDS, applyCustomOpenAPIOperation, applySchemaOptionality, checkParamsSchema, createJsonifiedRouterClient, customOpenAPIOperation, expandArrayableSchema, expandUnionSchema, filterSchemaBranches, getCustomOpenAPIOperation, isAnySchema, isFileSchema, isObjectSchema, isPrimitiveSchema, oo, separateObjectSchema, toOpenAPIContent, toOpenAPIEventIteratorContent, toOpenAPIMethod, toOpenAPIParameters, toOpenAPIPath, toOpenAPISchema };
|
109
|
+
export { LOGIC_KEYWORDS, applyCustomOpenAPIOperation, applySchemaOptionality, checkParamsSchema, createJsonifiedRouterClient, customOpenAPIOperation, expandArrayableSchema, expandUnionSchema, filterSchemaBranches, getCustomOpenAPIOperation, isAnySchema, isFileSchema, isObjectSchema, isPrimitiveSchema, oo, resolveOpenAPIJsonSchemaRef, separateObjectSchema, toOpenAPIContent, toOpenAPIEventIteratorContent, toOpenAPIMethod, toOpenAPIParameters, toOpenAPIPath, toOpenAPISchema };
|
109
110
|
export type { FileSchema, ObjectSchema, OverrideOperationValue };
|
package/dist/index.mjs
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { c as customOpenAPIOperation } from './shared/openapi.
|
2
|
-
export { C as CompositeSchemaConverter, L as LOGIC_KEYWORDS, O as OpenAPIGenerator, a as applyCustomOpenAPIOperation, n as applySchemaOptionality, h as checkParamsSchema, p as expandArrayableSchema, o as expandUnionSchema, m as filterSchemaBranches, g as getCustomOpenAPIOperation, l as isAnySchema, j as isFileSchema, k as isObjectSchema, q as isPrimitiveSchema, s as separateObjectSchema, d as toOpenAPIContent, e as toOpenAPIEventIteratorContent, b as toOpenAPIMethod, f as toOpenAPIParameters, t as toOpenAPIPath, i as toOpenAPISchema } from './shared/openapi.
|
1
|
+
import { c as customOpenAPIOperation } from './shared/openapi.C_3bk7bB.mjs';
|
2
|
+
export { C as CompositeSchemaConverter, L as LOGIC_KEYWORDS, O as OpenAPIGenerator, a as applyCustomOpenAPIOperation, n as applySchemaOptionality, h as checkParamsSchema, p as expandArrayableSchema, o as expandUnionSchema, m as filterSchemaBranches, g as getCustomOpenAPIOperation, l as isAnySchema, j as isFileSchema, k as isObjectSchema, q as isPrimitiveSchema, r as resolveOpenAPIJsonSchemaRef, s as separateObjectSchema, d as toOpenAPIContent, e as toOpenAPIEventIteratorContent, b as toOpenAPIMethod, f as toOpenAPIParameters, t as toOpenAPIPath, i as toOpenAPISchema } from './shared/openapi.C_3bk7bB.mjs';
|
3
3
|
import { createORPCErrorFromJson } from '@orpc/client';
|
4
4
|
import { StandardOpenAPISerializer, StandardOpenAPIJsonSerializer, StandardBracketNotationSerializer } from '@orpc/openapi-client/standard';
|
5
5
|
import { ORPCError, createRouterClient } from '@orpc/server';
|
package/dist/plugins/index.d.mts
CHANGED
@@ -2,7 +2,7 @@ import { OpenAPI } from '@orpc/contract';
|
|
2
2
|
import { Context, HTTPPath, Router } from '@orpc/server';
|
3
3
|
import { StandardHandlerInterceptorOptions, StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
|
4
4
|
import { Value, Promisable } from '@orpc/shared';
|
5
|
-
import { O as OpenAPIGeneratorOptions, a as OpenAPIGeneratorGenerateOptions } from '../shared/openapi.
|
5
|
+
import { O as OpenAPIGeneratorOptions, a as OpenAPIGeneratorGenerateOptions } from '../shared/openapi.CbIlrReM.mjs';
|
6
6
|
import '@orpc/openapi-client/standard';
|
7
7
|
import 'json-schema-typed/draft-2020-12';
|
8
8
|
|
package/dist/plugins/index.d.ts
CHANGED
@@ -2,7 +2,7 @@ import { OpenAPI } from '@orpc/contract';
|
|
2
2
|
import { Context, HTTPPath, Router } from '@orpc/server';
|
3
3
|
import { StandardHandlerInterceptorOptions, StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
|
4
4
|
import { Value, Promisable } from '@orpc/shared';
|
5
|
-
import { O as OpenAPIGeneratorOptions, a as OpenAPIGeneratorGenerateOptions } from '../shared/openapi.
|
5
|
+
import { O as OpenAPIGeneratorOptions, a as OpenAPIGeneratorGenerateOptions } from '../shared/openapi.CbIlrReM.js';
|
6
6
|
import '@orpc/openapi-client/standard';
|
7
7
|
import 'json-schema-typed/draft-2020-12';
|
8
8
|
|
package/dist/plugins/index.mjs
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { stringifyJSON, once, value } from '@orpc/shared';
|
2
|
-
import { O as OpenAPIGenerator } from '../shared/openapi.
|
2
|
+
import { O as OpenAPIGenerator } from '../shared/openapi.C_3bk7bB.mjs';
|
3
3
|
import '@orpc/client';
|
4
4
|
import '@orpc/client/standard';
|
5
5
|
import '@orpc/contract';
|
@@ -345,6 +345,15 @@ function checkParamsSchema(schema, params) {
|
|
345
345
|
function toOpenAPISchema(schema) {
|
346
346
|
return schema === true ? {} : schema === false ? { not: {} } : schema;
|
347
347
|
}
|
348
|
+
const OPENAPI_JSON_SCHEMA_REF_PREFIX = "#/components/schemas/";
|
349
|
+
function resolveOpenAPIJsonSchemaRef(doc, schema) {
|
350
|
+
if (typeof schema !== "object" || !schema.$ref?.startsWith(OPENAPI_JSON_SCHEMA_REF_PREFIX)) {
|
351
|
+
return schema;
|
352
|
+
}
|
353
|
+
const name = schema.$ref.slice(OPENAPI_JSON_SCHEMA_REF_PREFIX.length);
|
354
|
+
const resolved = doc.components?.schemas?.[name];
|
355
|
+
return resolved ?? schema;
|
356
|
+
}
|
348
357
|
|
349
358
|
class CompositeSchemaConverter {
|
350
359
|
converters;
|
@@ -381,8 +390,10 @@ class OpenAPIGenerator {
|
|
381
390
|
...clone(options),
|
382
391
|
info: options.info ?? { title: "API Reference", version: "0.0.0" },
|
383
392
|
openapi: "3.1.1",
|
384
|
-
exclude: void 0
|
393
|
+
exclude: void 0,
|
394
|
+
commonSchemas: void 0
|
385
395
|
};
|
396
|
+
const baseSchemaConvertOptions = await this.#resolveCommonSchemas(doc, options.commonSchemas);
|
386
397
|
const contracts = [];
|
387
398
|
await resolveContractProcedures({ path: [], router }, ({ contract, path }) => {
|
388
399
|
if (!exclude(contract, path)) {
|
@@ -407,9 +418,9 @@ class OpenAPIGenerator {
|
|
407
418
|
deprecated: def.route.deprecated,
|
408
419
|
tags: def.route.tags?.map((tag) => tag)
|
409
420
|
};
|
410
|
-
await this.#request(operationObjectRef, def);
|
411
|
-
await this.#successResponse(operationObjectRef, def);
|
412
|
-
await this.#errorResponse(operationObjectRef, def);
|
421
|
+
await this.#request(doc, operationObjectRef, def, baseSchemaConvertOptions);
|
422
|
+
await this.#successResponse(doc, operationObjectRef, def, baseSchemaConvertOptions);
|
423
|
+
await this.#errorResponse(operationObjectRef, def, baseSchemaConvertOptions);
|
413
424
|
}
|
414
425
|
doc.paths ??= {};
|
415
426
|
doc.paths[httpPath] ??= {};
|
@@ -433,22 +444,73 @@ ${errors.join("\n\n")}`
|
|
433
444
|
}
|
434
445
|
return this.serializer.serialize(doc)[0];
|
435
446
|
}
|
436
|
-
async #
|
447
|
+
async #resolveCommonSchemas(doc, commonSchemas) {
|
448
|
+
const baseOptions = {};
|
449
|
+
if (commonSchemas) {
|
450
|
+
baseOptions.components = [];
|
451
|
+
for (const key in commonSchemas) {
|
452
|
+
const { schema, strategy = "input" } = commonSchemas[key];
|
453
|
+
const [required, json] = await this.converter.convert(schema, { strategy });
|
454
|
+
const allowedStrategies = [strategy];
|
455
|
+
if (strategy === "input") {
|
456
|
+
const [outputRequired, outputJson] = await this.converter.convert(schema, { strategy: "output" });
|
457
|
+
if (outputRequired === required && stringifyJSON(outputJson) === stringifyJSON(json)) {
|
458
|
+
allowedStrategies.push("output");
|
459
|
+
}
|
460
|
+
} else if (strategy === "output") {
|
461
|
+
const [inputRequired, inputJson] = await this.converter.convert(schema, { strategy: "input" });
|
462
|
+
if (inputRequired === required && stringifyJSON(inputJson) === stringifyJSON(json)) {
|
463
|
+
allowedStrategies.push("input");
|
464
|
+
}
|
465
|
+
}
|
466
|
+
baseOptions.components.push({
|
467
|
+
schema,
|
468
|
+
required,
|
469
|
+
ref: `#/components/schemas/${key}`,
|
470
|
+
allowedStrategies
|
471
|
+
});
|
472
|
+
}
|
473
|
+
doc.components ??= {};
|
474
|
+
doc.components.schemas ??= {};
|
475
|
+
for (const key in commonSchemas) {
|
476
|
+
const { schema, strategy = "input" } = commonSchemas[key];
|
477
|
+
const [, json] = await this.converter.convert(
|
478
|
+
schema,
|
479
|
+
{
|
480
|
+
...baseOptions,
|
481
|
+
strategy,
|
482
|
+
minStructureDepthForRef: 1
|
483
|
+
// not allow use $ref for root schemas
|
484
|
+
}
|
485
|
+
);
|
486
|
+
doc.components.schemas[key] = toOpenAPISchema(json);
|
487
|
+
}
|
488
|
+
}
|
489
|
+
return baseOptions;
|
490
|
+
}
|
491
|
+
async #request(doc, ref, def, baseSchemaConvertOptions) {
|
437
492
|
const method = fallbackContractConfig("defaultMethod", def.route.method);
|
438
493
|
const details = getEventIteratorSchemaDetails(def.inputSchema);
|
439
494
|
if (details) {
|
440
495
|
ref.requestBody = {
|
441
496
|
required: true,
|
442
497
|
content: toOpenAPIEventIteratorContent(
|
443
|
-
await this.converter.convert(details.yields, { strategy: "input" }),
|
444
|
-
await this.converter.convert(details.returns, { strategy: "input" })
|
498
|
+
await this.converter.convert(details.yields, { ...baseSchemaConvertOptions, strategy: "input" }),
|
499
|
+
await this.converter.convert(details.returns, { ...baseSchemaConvertOptions, strategy: "input" })
|
445
500
|
)
|
446
501
|
};
|
447
502
|
return;
|
448
503
|
}
|
449
504
|
const dynamicParams = getDynamicParams(def.route.path)?.map((v) => v.name);
|
450
505
|
const inputStructure = fallbackContractConfig("defaultInputStructure", def.route.inputStructure);
|
451
|
-
let [required, schema] = await this.converter.convert(
|
506
|
+
let [required, schema] = await this.converter.convert(
|
507
|
+
def.inputSchema,
|
508
|
+
{
|
509
|
+
...baseSchemaConvertOptions,
|
510
|
+
strategy: "input",
|
511
|
+
minStructureDepthForRef: dynamicParams?.length || inputStructure === "detailed" ? 1 : 0
|
512
|
+
}
|
513
|
+
);
|
452
514
|
if (isAnySchema(schema) && !dynamicParams?.length) {
|
453
515
|
return;
|
454
516
|
}
|
@@ -491,7 +553,8 @@ ${errors.join("\n\n")}`
|
|
491
553
|
if (!isObjectSchema(schema)) {
|
492
554
|
throw error;
|
493
555
|
}
|
494
|
-
|
556
|
+
const resolvedParamSchema = schema.properties?.params !== void 0 ? resolveOpenAPIJsonSchemaRef(doc, schema.properties.params) : void 0;
|
557
|
+
if (dynamicParams?.length && (resolvedParamSchema === void 0 || !isObjectSchema(resolvedParamSchema) || !checkParamsSchema(resolvedParamSchema, dynamicParams))) {
|
495
558
|
throw new OpenAPIGeneratorError(
|
496
559
|
'When input structure is "detailed" and path has dynamic params, the "params" schema must be an object with all dynamic params as required.'
|
497
560
|
);
|
@@ -499,12 +562,13 @@ ${errors.join("\n\n")}`
|
|
499
562
|
for (const from of ["params", "query", "headers"]) {
|
500
563
|
const fromSchema = schema.properties?.[from];
|
501
564
|
if (fromSchema !== void 0) {
|
502
|
-
|
565
|
+
const resolvedSchema = resolveOpenAPIJsonSchemaRef(doc, fromSchema);
|
566
|
+
if (!isObjectSchema(resolvedSchema)) {
|
503
567
|
throw error;
|
504
568
|
}
|
505
569
|
const parameterIn = from === "params" ? "path" : from === "headers" ? "header" : "query";
|
506
570
|
ref.parameters ??= [];
|
507
|
-
ref.parameters.push(...toOpenAPIParameters(
|
571
|
+
ref.parameters.push(...toOpenAPIParameters(resolvedSchema, parameterIn));
|
508
572
|
}
|
509
573
|
}
|
510
574
|
if (schema.properties?.body !== void 0) {
|
@@ -514,7 +578,7 @@ ${errors.join("\n\n")}`
|
|
514
578
|
};
|
515
579
|
}
|
516
580
|
}
|
517
|
-
async #successResponse(ref, def) {
|
581
|
+
async #successResponse(doc, ref, def, baseSchemaConvertOptions) {
|
518
582
|
const outputSchema = def.outputSchema;
|
519
583
|
const status = fallbackContractConfig("defaultSuccessStatus", def.route.successStatus);
|
520
584
|
const description = fallbackContractConfig("defaultSuccessDescription", def.route?.successDescription);
|
@@ -525,13 +589,20 @@ ${errors.join("\n\n")}`
|
|
525
589
|
ref.responses[status] = {
|
526
590
|
description,
|
527
591
|
content: toOpenAPIEventIteratorContent(
|
528
|
-
await this.converter.convert(eventIteratorSchemaDetails.yields, { strategy: "output" }),
|
529
|
-
await this.converter.convert(eventIteratorSchemaDetails.returns, { strategy: "output" })
|
592
|
+
await this.converter.convert(eventIteratorSchemaDetails.yields, { ...baseSchemaConvertOptions, strategy: "output" }),
|
593
|
+
await this.converter.convert(eventIteratorSchemaDetails.returns, { ...baseSchemaConvertOptions, strategy: "output" })
|
530
594
|
)
|
531
595
|
};
|
532
596
|
return;
|
533
597
|
}
|
534
|
-
const [required, json] = await this.converter.convert(
|
598
|
+
const [required, json] = await this.converter.convert(
|
599
|
+
outputSchema,
|
600
|
+
{
|
601
|
+
...baseSchemaConvertOptions,
|
602
|
+
strategy: "output",
|
603
|
+
minStructureDepthForRef: outputStructure === "detailed" ? 1 : 0
|
604
|
+
}
|
605
|
+
);
|
535
606
|
if (outputStructure === "compact") {
|
536
607
|
ref.responses ??= {};
|
537
608
|
ref.responses[status] = {
|
@@ -558,11 +629,12 @@ ${errors.join("\n\n")}`
|
|
558
629
|
let schemaStatus;
|
559
630
|
let schemaDescription;
|
560
631
|
if (item.properties?.status !== void 0) {
|
561
|
-
|
632
|
+
const statusSchema = resolveOpenAPIJsonSchemaRef(doc, item.properties.status);
|
633
|
+
if (typeof statusSchema !== "object" || statusSchema.const === void 0 || typeof statusSchema.const !== "number" || !Number.isInteger(statusSchema.const) || isORPCErrorStatus(statusSchema.const)) {
|
562
634
|
throw error;
|
563
635
|
}
|
564
|
-
schemaStatus =
|
565
|
-
schemaDescription =
|
636
|
+
schemaStatus = statusSchema.const;
|
637
|
+
schemaDescription = statusSchema.description;
|
566
638
|
}
|
567
639
|
const itemStatus = schemaStatus ?? status;
|
568
640
|
const itemDescription = schemaDescription ?? description;
|
@@ -578,16 +650,17 @@ ${errors.join("\n\n")}`
|
|
578
650
|
description: itemDescription
|
579
651
|
};
|
580
652
|
if (item.properties?.headers !== void 0) {
|
581
|
-
|
653
|
+
const headersSchema = resolveOpenAPIJsonSchemaRef(doc, item.properties.headers);
|
654
|
+
if (!isObjectSchema(headersSchema)) {
|
582
655
|
throw error;
|
583
656
|
}
|
584
|
-
for (const key in
|
585
|
-
const headerSchema =
|
657
|
+
for (const key in headersSchema.properties) {
|
658
|
+
const headerSchema = headersSchema.properties[key];
|
586
659
|
if (headerSchema !== void 0) {
|
587
660
|
ref.responses[itemStatus].headers ??= {};
|
588
661
|
ref.responses[itemStatus].headers[key] = {
|
589
662
|
schema: toOpenAPISchema(headerSchema),
|
590
|
-
required: item.
|
663
|
+
required: item.required?.includes("headers") && headersSchema.required?.includes(key)
|
591
664
|
};
|
592
665
|
}
|
593
666
|
}
|
@@ -599,7 +672,7 @@ ${errors.join("\n\n")}`
|
|
599
672
|
}
|
600
673
|
}
|
601
674
|
}
|
602
|
-
async #errorResponse(ref, def) {
|
675
|
+
async #errorResponse(ref, def, baseSchemaConvertOptions) {
|
603
676
|
const errorMap = def.errorMap;
|
604
677
|
const errors = {};
|
605
678
|
for (const code in errorMap) {
|
@@ -609,7 +682,7 @@ ${errors.join("\n\n")}`
|
|
609
682
|
}
|
610
683
|
const status = fallbackORPCErrorStatus(code, config.status);
|
611
684
|
const message = fallbackORPCErrorMessage(code, config.message);
|
612
|
-
const [dataRequired, dataSchema] = await this.converter.convert(config.data, { strategy: "output" });
|
685
|
+
const [dataRequired, dataSchema] = await this.converter.convert(config.data, { ...baseSchemaConvertOptions, strategy: "output" });
|
613
686
|
errors[status] ??= [];
|
614
687
|
errors[status].push({
|
615
688
|
type: "object",
|
@@ -649,4 +722,4 @@ ${errors.join("\n\n")}`
|
|
649
722
|
}
|
650
723
|
}
|
651
724
|
|
652
|
-
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, filterSchemaBranches as m, applySchemaOptionality as n, expandUnionSchema as o, expandArrayableSchema as p, isPrimitiveSchema as q, separateObjectSchema as s, toOpenAPIPath as t };
|
725
|
+
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, filterSchemaBranches as m, applySchemaOptionality as n, expandUnionSchema as o, expandArrayableSchema as p, isPrimitiveSchema as q, resolveOpenAPIJsonSchemaRef as r, separateObjectSchema as s, toOpenAPIPath as t };
|
@@ -4,8 +4,26 @@ import { AnyProcedure, AnyRouter } from '@orpc/server';
|
|
4
4
|
import { Promisable } from '@orpc/shared';
|
5
5
|
import { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
6
6
|
|
7
|
+
interface SchemaConverterComponent {
|
8
|
+
allowedStrategies: SchemaConvertOptions['strategy'][];
|
9
|
+
schema: AnySchema;
|
10
|
+
required: boolean;
|
11
|
+
ref: string;
|
12
|
+
}
|
7
13
|
interface SchemaConvertOptions {
|
8
14
|
strategy: 'input' | 'output';
|
15
|
+
/**
|
16
|
+
* Common components should use `$ref` to represent themselves if matched.
|
17
|
+
*/
|
18
|
+
components?: SchemaConverterComponent[];
|
19
|
+
/**
|
20
|
+
* Minimum schema structure depth required before using `$ref` for components.
|
21
|
+
*
|
22
|
+
* For example, if set to 2, `$ref` will only be used for schemas nested at depth 2 or greater.
|
23
|
+
*
|
24
|
+
* @default 0 - No depth limit;
|
25
|
+
*/
|
26
|
+
minStructureDepthForRef?: number;
|
9
27
|
}
|
10
28
|
interface SchemaConverter {
|
11
29
|
convert(schema: AnySchema | undefined, options: SchemaConvertOptions): Promisable<[required: boolean, jsonSchema: JSONSchema]>;
|
@@ -29,6 +47,34 @@ interface OpenAPIGeneratorGenerateOptions extends Partial<Omit<OpenAPI.Document,
|
|
29
47
|
* @default () => false
|
30
48
|
*/
|
31
49
|
exclude?: (procedure: AnyProcedure | AnyContractProcedure, path: readonly string[]) => boolean;
|
50
|
+
/**
|
51
|
+
* Common schemas to be used for $ref resolution.
|
52
|
+
*/
|
53
|
+
commonSchemas?: Record<string, {
|
54
|
+
/**
|
55
|
+
* Determines which schema definition to use when input and output schemas differ.
|
56
|
+
* This is needed because some schemas transform data differently between input and output,
|
57
|
+
* making it impossible to use a single $ref for both cases.
|
58
|
+
*
|
59
|
+
* @example
|
60
|
+
* ```ts
|
61
|
+
* // This schema transforms a string input into a number output
|
62
|
+
* const Schema = z.string()
|
63
|
+
* .transform(v => Number(v))
|
64
|
+
* .pipe(z.number())
|
65
|
+
*
|
66
|
+
* // Input schema: { type: 'string' }
|
67
|
+
* // Output schema: { type: 'number' }
|
68
|
+
* ```
|
69
|
+
*
|
70
|
+
* When schemas differ between input and output, you must explicitly choose
|
71
|
+
* which version to use for the OpenAPI specification.
|
72
|
+
*
|
73
|
+
* @default 'input' - Uses the input schema definition by default
|
74
|
+
*/
|
75
|
+
strategy?: SchemaConvertOptions['strategy'];
|
76
|
+
schema: AnySchema;
|
77
|
+
}>;
|
32
78
|
}
|
33
79
|
/**
|
34
80
|
* The generator that converts oRPC routers/contracts to OpenAPI specifications.
|
@@ -48,5 +94,5 @@ declare class OpenAPIGenerator {
|
|
48
94
|
generate(router: AnyContractRouter | AnyRouter, options?: OpenAPIGeneratorGenerateOptions): Promise<OpenAPI.Document>;
|
49
95
|
}
|
50
96
|
|
51
|
-
export { OpenAPIGenerator as b, CompositeSchemaConverter as
|
52
|
-
export type { ConditionalSchemaConverter as C, OpenAPIGeneratorOptions as O,
|
97
|
+
export { OpenAPIGenerator as b, CompositeSchemaConverter as e };
|
98
|
+
export type { ConditionalSchemaConverter as C, OpenAPIGeneratorOptions as O, SchemaConverterComponent as S, OpenAPIGeneratorGenerateOptions as a, SchemaConvertOptions as c, SchemaConverter as d };
|
@@ -4,8 +4,26 @@ import { AnyProcedure, AnyRouter } from '@orpc/server';
|
|
4
4
|
import { Promisable } from '@orpc/shared';
|
5
5
|
import { JSONSchema } from 'json-schema-typed/draft-2020-12';
|
6
6
|
|
7
|
+
interface SchemaConverterComponent {
|
8
|
+
allowedStrategies: SchemaConvertOptions['strategy'][];
|
9
|
+
schema: AnySchema;
|
10
|
+
required: boolean;
|
11
|
+
ref: string;
|
12
|
+
}
|
7
13
|
interface SchemaConvertOptions {
|
8
14
|
strategy: 'input' | 'output';
|
15
|
+
/**
|
16
|
+
* Common components should use `$ref` to represent themselves if matched.
|
17
|
+
*/
|
18
|
+
components?: SchemaConverterComponent[];
|
19
|
+
/**
|
20
|
+
* Minimum schema structure depth required before using `$ref` for components.
|
21
|
+
*
|
22
|
+
* For example, if set to 2, `$ref` will only be used for schemas nested at depth 2 or greater.
|
23
|
+
*
|
24
|
+
* @default 0 - No depth limit;
|
25
|
+
*/
|
26
|
+
minStructureDepthForRef?: number;
|
9
27
|
}
|
10
28
|
interface SchemaConverter {
|
11
29
|
convert(schema: AnySchema | undefined, options: SchemaConvertOptions): Promisable<[required: boolean, jsonSchema: JSONSchema]>;
|
@@ -29,6 +47,34 @@ interface OpenAPIGeneratorGenerateOptions extends Partial<Omit<OpenAPI.Document,
|
|
29
47
|
* @default () => false
|
30
48
|
*/
|
31
49
|
exclude?: (procedure: AnyProcedure | AnyContractProcedure, path: readonly string[]) => boolean;
|
50
|
+
/**
|
51
|
+
* Common schemas to be used for $ref resolution.
|
52
|
+
*/
|
53
|
+
commonSchemas?: Record<string, {
|
54
|
+
/**
|
55
|
+
* Determines which schema definition to use when input and output schemas differ.
|
56
|
+
* This is needed because some schemas transform data differently between input and output,
|
57
|
+
* making it impossible to use a single $ref for both cases.
|
58
|
+
*
|
59
|
+
* @example
|
60
|
+
* ```ts
|
61
|
+
* // This schema transforms a string input into a number output
|
62
|
+
* const Schema = z.string()
|
63
|
+
* .transform(v => Number(v))
|
64
|
+
* .pipe(z.number())
|
65
|
+
*
|
66
|
+
* // Input schema: { type: 'string' }
|
67
|
+
* // Output schema: { type: 'number' }
|
68
|
+
* ```
|
69
|
+
*
|
70
|
+
* When schemas differ between input and output, you must explicitly choose
|
71
|
+
* which version to use for the OpenAPI specification.
|
72
|
+
*
|
73
|
+
* @default 'input' - Uses the input schema definition by default
|
74
|
+
*/
|
75
|
+
strategy?: SchemaConvertOptions['strategy'];
|
76
|
+
schema: AnySchema;
|
77
|
+
}>;
|
32
78
|
}
|
33
79
|
/**
|
34
80
|
* The generator that converts oRPC routers/contracts to OpenAPI specifications.
|
@@ -48,5 +94,5 @@ declare class OpenAPIGenerator {
|
|
48
94
|
generate(router: AnyContractRouter | AnyRouter, options?: OpenAPIGeneratorGenerateOptions): Promise<OpenAPI.Document>;
|
49
95
|
}
|
50
96
|
|
51
|
-
export { OpenAPIGenerator as b, CompositeSchemaConverter as
|
52
|
-
export type { ConditionalSchemaConverter as C, OpenAPIGeneratorOptions as O,
|
97
|
+
export { OpenAPIGenerator as b, CompositeSchemaConverter as e };
|
98
|
+
export type { ConditionalSchemaConverter as C, OpenAPIGeneratorOptions as O, SchemaConverterComponent as S, OpenAPIGeneratorGenerateOptions as a, SchemaConvertOptions as c, SchemaConverter as d };
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@orpc/openapi",
|
3
3
|
"type": "module",
|
4
|
-
"version": "1.
|
4
|
+
"version": "1.5.1",
|
5
5
|
"license": "MIT",
|
6
6
|
"homepage": "https://orpc.unnoq.com",
|
7
7
|
"repository": {
|
@@ -51,15 +51,15 @@
|
|
51
51
|
"dependencies": {
|
52
52
|
"json-schema-typed": "^8.0.1",
|
53
53
|
"rou3": "^0.6.0",
|
54
|
-
"@orpc/
|
55
|
-
"@orpc/
|
56
|
-
"@orpc/
|
57
|
-
"@orpc/
|
58
|
-
"@orpc/standard-server": "1.
|
59
|
-
"@orpc/server": "1.
|
54
|
+
"@orpc/contract": "1.5.1",
|
55
|
+
"@orpc/client": "1.5.1",
|
56
|
+
"@orpc/openapi-client": "1.5.1",
|
57
|
+
"@orpc/shared": "1.5.1",
|
58
|
+
"@orpc/standard-server": "1.5.1",
|
59
|
+
"@orpc/server": "1.5.1"
|
60
60
|
},
|
61
61
|
"devDependencies": {
|
62
|
-
"zod": "^3.25.
|
62
|
+
"zod": "^3.25.63"
|
63
63
|
},
|
64
64
|
"scripts": {
|
65
65
|
"build": "unbuild",
|