@orpc/openapi 1.5.0 → 1.5.2
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.qZLdpE0a.d.mts → openapi.B3hexduL.d.mts} +51 -2
- package/dist/shared/{openapi.qZLdpE0a.d.ts → openapi.B3hexduL.d.ts} +51 -2
- package/dist/shared/{openapi.DaYgbD_w.mjs → openapi.DrrBsJ0w.mjs} +122 -36
- 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.B3hexduL.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.B3hexduL.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.DrrBsJ0w.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.DrrBsJ0w.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.B3hexduL.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.B3hexduL.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.DrrBsJ0w.mjs';
|
3
3
|
import '@orpc/client';
|
4
4
|
import '@orpc/client/standard';
|
5
5
|
import '@orpc/contract';
|
@@ -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: readonly 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?: readonly 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,37 @@ 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
|
+
} | {
|
78
|
+
error: 'UndefinedError';
|
79
|
+
schema?: never;
|
80
|
+
}>;
|
32
81
|
}
|
33
82
|
/**
|
34
83
|
* The generator that converts oRPC routers/contracts to OpenAPI specifications.
|
@@ -48,5 +97,5 @@ declare class OpenAPIGenerator {
|
|
48
97
|
generate(router: AnyContractRouter | AnyRouter, options?: OpenAPIGeneratorGenerateOptions): Promise<OpenAPI.Document>;
|
49
98
|
}
|
50
99
|
|
51
|
-
export { OpenAPIGenerator as b, CompositeSchemaConverter as
|
52
|
-
export type { ConditionalSchemaConverter as C, OpenAPIGeneratorOptions as O,
|
100
|
+
export { OpenAPIGenerator as b, CompositeSchemaConverter as e };
|
101
|
+
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: readonly 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?: readonly 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,37 @@ 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
|
+
} | {
|
78
|
+
error: 'UndefinedError';
|
79
|
+
schema?: never;
|
80
|
+
}>;
|
32
81
|
}
|
33
82
|
/**
|
34
83
|
* The generator that converts oRPC routers/contracts to OpenAPI specifications.
|
@@ -48,5 +97,5 @@ declare class OpenAPIGenerator {
|
|
48
97
|
generate(router: AnyContractRouter | AnyRouter, options?: OpenAPIGeneratorGenerateOptions): Promise<OpenAPI.Document>;
|
49
98
|
}
|
50
99
|
|
51
|
-
export { OpenAPIGenerator as b, CompositeSchemaConverter as
|
52
|
-
export type { ConditionalSchemaConverter as C, OpenAPIGeneratorOptions as O,
|
100
|
+
export { OpenAPIGenerator as b, CompositeSchemaConverter as e };
|
101
|
+
export type { ConditionalSchemaConverter as C, OpenAPIGeneratorOptions as O, SchemaConverterComponent as S, OpenAPIGeneratorGenerateOptions as a, SchemaConvertOptions as c, SchemaConverter as d };
|
@@ -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, undefinedErrorJsonSchema } = 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, undefinedErrorJsonSchema);
|
413
424
|
}
|
414
425
|
doc.paths ??= {};
|
415
426
|
doc.paths[httpPath] ??= {};
|
@@ -433,22 +444,96 @@ ${errors.join("\n\n")}`
|
|
433
444
|
}
|
434
445
|
return this.serializer.serialize(doc)[0];
|
435
446
|
}
|
436
|
-
async #
|
447
|
+
async #resolveCommonSchemas(doc, commonSchemas) {
|
448
|
+
let undefinedErrorJsonSchema = {
|
449
|
+
type: "object",
|
450
|
+
properties: {
|
451
|
+
defined: { const: false },
|
452
|
+
code: { type: "string" },
|
453
|
+
status: { type: "number" },
|
454
|
+
message: { type: "string" },
|
455
|
+
data: {}
|
456
|
+
},
|
457
|
+
required: ["defined", "code", "status", "message"]
|
458
|
+
};
|
459
|
+
const baseSchemaConvertOptions = {};
|
460
|
+
if (commonSchemas) {
|
461
|
+
baseSchemaConvertOptions.components = [];
|
462
|
+
for (const key in commonSchemas) {
|
463
|
+
const options = commonSchemas[key];
|
464
|
+
if (options.schema === void 0) {
|
465
|
+
continue;
|
466
|
+
}
|
467
|
+
const { schema, strategy = "input" } = options;
|
468
|
+
const [required, json] = await this.converter.convert(schema, { strategy });
|
469
|
+
const allowedStrategies = [strategy];
|
470
|
+
if (strategy === "input") {
|
471
|
+
const [outputRequired, outputJson] = await this.converter.convert(schema, { strategy: "output" });
|
472
|
+
if (outputRequired === required && stringifyJSON(outputJson) === stringifyJSON(json)) {
|
473
|
+
allowedStrategies.push("output");
|
474
|
+
}
|
475
|
+
} else if (strategy === "output") {
|
476
|
+
const [inputRequired, inputJson] = await this.converter.convert(schema, { strategy: "input" });
|
477
|
+
if (inputRequired === required && stringifyJSON(inputJson) === stringifyJSON(json)) {
|
478
|
+
allowedStrategies.push("input");
|
479
|
+
}
|
480
|
+
}
|
481
|
+
baseSchemaConvertOptions.components.push({
|
482
|
+
schema,
|
483
|
+
required,
|
484
|
+
ref: `#/components/schemas/${key}`,
|
485
|
+
allowedStrategies
|
486
|
+
});
|
487
|
+
}
|
488
|
+
doc.components ??= {};
|
489
|
+
doc.components.schemas ??= {};
|
490
|
+
for (const key in commonSchemas) {
|
491
|
+
const options = commonSchemas[key];
|
492
|
+
if (options.schema === void 0) {
|
493
|
+
if (options.error === "UndefinedError") {
|
494
|
+
doc.components.schemas[key] = toOpenAPISchema(undefinedErrorJsonSchema);
|
495
|
+
undefinedErrorJsonSchema = { $ref: `#/components/schemas/${key}` };
|
496
|
+
}
|
497
|
+
continue;
|
498
|
+
}
|
499
|
+
const { schema, strategy = "input" } = options;
|
500
|
+
const [, json] = await this.converter.convert(
|
501
|
+
schema,
|
502
|
+
{
|
503
|
+
...baseSchemaConvertOptions,
|
504
|
+
strategy,
|
505
|
+
minStructureDepthForRef: 1
|
506
|
+
// not allow use $ref for root schemas
|
507
|
+
}
|
508
|
+
);
|
509
|
+
doc.components.schemas[key] = toOpenAPISchema(json);
|
510
|
+
}
|
511
|
+
}
|
512
|
+
return { baseSchemaConvertOptions, undefinedErrorJsonSchema };
|
513
|
+
}
|
514
|
+
async #request(doc, ref, def, baseSchemaConvertOptions) {
|
437
515
|
const method = fallbackContractConfig("defaultMethod", def.route.method);
|
438
516
|
const details = getEventIteratorSchemaDetails(def.inputSchema);
|
439
517
|
if (details) {
|
440
518
|
ref.requestBody = {
|
441
519
|
required: true,
|
442
520
|
content: toOpenAPIEventIteratorContent(
|
443
|
-
await this.converter.convert(details.yields, { strategy: "input" }),
|
444
|
-
await this.converter.convert(details.returns, { strategy: "input" })
|
521
|
+
await this.converter.convert(details.yields, { ...baseSchemaConvertOptions, strategy: "input" }),
|
522
|
+
await this.converter.convert(details.returns, { ...baseSchemaConvertOptions, strategy: "input" })
|
445
523
|
)
|
446
524
|
};
|
447
525
|
return;
|
448
526
|
}
|
449
527
|
const dynamicParams = getDynamicParams(def.route.path)?.map((v) => v.name);
|
450
528
|
const inputStructure = fallbackContractConfig("defaultInputStructure", def.route.inputStructure);
|
451
|
-
let [required, schema] = await this.converter.convert(
|
529
|
+
let [required, schema] = await this.converter.convert(
|
530
|
+
def.inputSchema,
|
531
|
+
{
|
532
|
+
...baseSchemaConvertOptions,
|
533
|
+
strategy: "input",
|
534
|
+
minStructureDepthForRef: dynamicParams?.length || inputStructure === "detailed" ? 1 : 0
|
535
|
+
}
|
536
|
+
);
|
452
537
|
if (isAnySchema(schema) && !dynamicParams?.length) {
|
453
538
|
return;
|
454
539
|
}
|
@@ -491,7 +576,8 @@ ${errors.join("\n\n")}`
|
|
491
576
|
if (!isObjectSchema(schema)) {
|
492
577
|
throw error;
|
493
578
|
}
|
494
|
-
|
579
|
+
const resolvedParamSchema = schema.properties?.params !== void 0 ? resolveOpenAPIJsonSchemaRef(doc, schema.properties.params) : void 0;
|
580
|
+
if (dynamicParams?.length && (resolvedParamSchema === void 0 || !isObjectSchema(resolvedParamSchema) || !checkParamsSchema(resolvedParamSchema, dynamicParams))) {
|
495
581
|
throw new OpenAPIGeneratorError(
|
496
582
|
'When input structure is "detailed" and path has dynamic params, the "params" schema must be an object with all dynamic params as required.'
|
497
583
|
);
|
@@ -499,12 +585,13 @@ ${errors.join("\n\n")}`
|
|
499
585
|
for (const from of ["params", "query", "headers"]) {
|
500
586
|
const fromSchema = schema.properties?.[from];
|
501
587
|
if (fromSchema !== void 0) {
|
502
|
-
|
588
|
+
const resolvedSchema = resolveOpenAPIJsonSchemaRef(doc, fromSchema);
|
589
|
+
if (!isObjectSchema(resolvedSchema)) {
|
503
590
|
throw error;
|
504
591
|
}
|
505
592
|
const parameterIn = from === "params" ? "path" : from === "headers" ? "header" : "query";
|
506
593
|
ref.parameters ??= [];
|
507
|
-
ref.parameters.push(...toOpenAPIParameters(
|
594
|
+
ref.parameters.push(...toOpenAPIParameters(resolvedSchema, parameterIn));
|
508
595
|
}
|
509
596
|
}
|
510
597
|
if (schema.properties?.body !== void 0) {
|
@@ -514,7 +601,7 @@ ${errors.join("\n\n")}`
|
|
514
601
|
};
|
515
602
|
}
|
516
603
|
}
|
517
|
-
async #successResponse(ref, def) {
|
604
|
+
async #successResponse(doc, ref, def, baseSchemaConvertOptions) {
|
518
605
|
const outputSchema = def.outputSchema;
|
519
606
|
const status = fallbackContractConfig("defaultSuccessStatus", def.route.successStatus);
|
520
607
|
const description = fallbackContractConfig("defaultSuccessDescription", def.route?.successDescription);
|
@@ -525,13 +612,20 @@ ${errors.join("\n\n")}`
|
|
525
612
|
ref.responses[status] = {
|
526
613
|
description,
|
527
614
|
content: toOpenAPIEventIteratorContent(
|
528
|
-
await this.converter.convert(eventIteratorSchemaDetails.yields, { strategy: "output" }),
|
529
|
-
await this.converter.convert(eventIteratorSchemaDetails.returns, { strategy: "output" })
|
615
|
+
await this.converter.convert(eventIteratorSchemaDetails.yields, { ...baseSchemaConvertOptions, strategy: "output" }),
|
616
|
+
await this.converter.convert(eventIteratorSchemaDetails.returns, { ...baseSchemaConvertOptions, strategy: "output" })
|
530
617
|
)
|
531
618
|
};
|
532
619
|
return;
|
533
620
|
}
|
534
|
-
const [required, json] = await this.converter.convert(
|
621
|
+
const [required, json] = await this.converter.convert(
|
622
|
+
outputSchema,
|
623
|
+
{
|
624
|
+
...baseSchemaConvertOptions,
|
625
|
+
strategy: "output",
|
626
|
+
minStructureDepthForRef: outputStructure === "detailed" ? 1 : 0
|
627
|
+
}
|
628
|
+
);
|
535
629
|
if (outputStructure === "compact") {
|
536
630
|
ref.responses ??= {};
|
537
631
|
ref.responses[status] = {
|
@@ -558,11 +652,12 @@ ${errors.join("\n\n")}`
|
|
558
652
|
let schemaStatus;
|
559
653
|
let schemaDescription;
|
560
654
|
if (item.properties?.status !== void 0) {
|
561
|
-
|
655
|
+
const statusSchema = resolveOpenAPIJsonSchemaRef(doc, item.properties.status);
|
656
|
+
if (typeof statusSchema !== "object" || statusSchema.const === void 0 || typeof statusSchema.const !== "number" || !Number.isInteger(statusSchema.const) || isORPCErrorStatus(statusSchema.const)) {
|
562
657
|
throw error;
|
563
658
|
}
|
564
|
-
schemaStatus =
|
565
|
-
schemaDescription =
|
659
|
+
schemaStatus = statusSchema.const;
|
660
|
+
schemaDescription = statusSchema.description;
|
566
661
|
}
|
567
662
|
const itemStatus = schemaStatus ?? status;
|
568
663
|
const itemDescription = schemaDescription ?? description;
|
@@ -578,16 +673,17 @@ ${errors.join("\n\n")}`
|
|
578
673
|
description: itemDescription
|
579
674
|
};
|
580
675
|
if (item.properties?.headers !== void 0) {
|
581
|
-
|
676
|
+
const headersSchema = resolveOpenAPIJsonSchemaRef(doc, item.properties.headers);
|
677
|
+
if (!isObjectSchema(headersSchema)) {
|
582
678
|
throw error;
|
583
679
|
}
|
584
|
-
for (const key in
|
585
|
-
const headerSchema =
|
680
|
+
for (const key in headersSchema.properties) {
|
681
|
+
const headerSchema = headersSchema.properties[key];
|
586
682
|
if (headerSchema !== void 0) {
|
587
683
|
ref.responses[itemStatus].headers ??= {};
|
588
684
|
ref.responses[itemStatus].headers[key] = {
|
589
685
|
schema: toOpenAPISchema(headerSchema),
|
590
|
-
required: item.
|
686
|
+
required: item.required?.includes("headers") && headersSchema.required?.includes(key)
|
591
687
|
};
|
592
688
|
}
|
593
689
|
}
|
@@ -599,7 +695,7 @@ ${errors.join("\n\n")}`
|
|
599
695
|
}
|
600
696
|
}
|
601
697
|
}
|
602
|
-
async #errorResponse(ref, def) {
|
698
|
+
async #errorResponse(ref, def, baseSchemaConvertOptions, undefinedErrorSchema) {
|
603
699
|
const errorMap = def.errorMap;
|
604
700
|
const errors = {};
|
605
701
|
for (const code in errorMap) {
|
@@ -609,7 +705,7 @@ ${errors.join("\n\n")}`
|
|
609
705
|
}
|
610
706
|
const status = fallbackORPCErrorStatus(code, config.status);
|
611
707
|
const message = fallbackORPCErrorMessage(code, config.message);
|
612
|
-
const [dataRequired, dataSchema] = await this.converter.convert(config.data, { strategy: "output" });
|
708
|
+
const [dataRequired, dataSchema] = await this.converter.convert(config.data, { ...baseSchemaConvertOptions, strategy: "output" });
|
613
709
|
errors[status] ??= [];
|
614
710
|
errors[status].push({
|
615
711
|
type: "object",
|
@@ -631,17 +727,7 @@ ${errors.join("\n\n")}`
|
|
631
727
|
content: toOpenAPIContent({
|
632
728
|
oneOf: [
|
633
729
|
...schemas,
|
634
|
-
|
635
|
-
type: "object",
|
636
|
-
properties: {
|
637
|
-
defined: { const: false },
|
638
|
-
code: { type: "string" },
|
639
|
-
status: { type: "number" },
|
640
|
-
message: { type: "string" },
|
641
|
-
data: {}
|
642
|
-
},
|
643
|
-
required: ["defined", "code", "status", "message"]
|
644
|
-
}
|
730
|
+
undefinedErrorSchema
|
645
731
|
]
|
646
732
|
})
|
647
733
|
};
|
@@ -649,4 +735,4 @@ ${errors.join("\n\n")}`
|
|
649
735
|
}
|
650
736
|
}
|
651
737
|
|
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 };
|
738
|
+
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 };
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@orpc/openapi",
|
3
3
|
"type": "module",
|
4
|
-
"version": "1.5.
|
4
|
+
"version": "1.5.2",
|
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/client": "1.5.
|
55
|
-
"@orpc/
|
56
|
-
"@orpc/
|
57
|
-
"@orpc/server": "1.5.
|
58
|
-
"@orpc/
|
59
|
-
"@orpc/
|
54
|
+
"@orpc/openapi-client": "1.5.2",
|
55
|
+
"@orpc/server": "1.5.2",
|
56
|
+
"@orpc/shared": "1.5.2",
|
57
|
+
"@orpc/standard-server": "1.5.2",
|
58
|
+
"@orpc/client": "1.5.2",
|
59
|
+
"@orpc/contract": "1.5.2"
|
60
60
|
},
|
61
61
|
"devDependencies": {
|
62
|
-
"zod": "^3.25.
|
62
|
+
"zod": "^3.25.64"
|
63
63
|
},
|
64
64
|
"scripts": {
|
65
65
|
"build": "unbuild",
|