@orpc/openapi 2.0.0-beta.20 → 2.0.0-beta.21
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 +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.mjs +40 -103
- package/package.json +8 -8
package/dist/index.d.mts
CHANGED
|
@@ -64,11 +64,16 @@ interface OpenAPIGeneratorOptions {
|
|
|
64
64
|
interface OpenAPIGeneratorGenerateOptions {
|
|
65
65
|
base?: Partial<OpenAPIDocument> | undefined;
|
|
66
66
|
/**
|
|
67
|
-
*
|
|
67
|
+
* Root-level `$defs` are always moved into `components.schemas`.
|
|
68
|
+
* Use this to customize the component name of a hoisted def.
|
|
68
69
|
*
|
|
69
|
-
* @
|
|
70
|
+
* @remarks
|
|
71
|
+
* - The returned name is a preference, conflicting names are still postfixed (`Planet`, `PlanetInput`, `Planet2`, ...).
|
|
72
|
+
* - Return `undefined` to keep the original def name.
|
|
73
|
+
*
|
|
74
|
+
* @default defName => defName
|
|
70
75
|
*/
|
|
71
|
-
|
|
76
|
+
customComponentName?: (defName: string, defSchema: JsonSchema) => string | undefined;
|
|
72
77
|
/**
|
|
73
78
|
* Filter procedures. Return `false` to exclude a procedure from the OpenAPI specification.
|
|
74
79
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -64,11 +64,16 @@ interface OpenAPIGeneratorOptions {
|
|
|
64
64
|
interface OpenAPIGeneratorGenerateOptions {
|
|
65
65
|
base?: Partial<OpenAPIDocument> | undefined;
|
|
66
66
|
/**
|
|
67
|
-
*
|
|
67
|
+
* Root-level `$defs` are always moved into `components.schemas`.
|
|
68
|
+
* Use this to customize the component name of a hoisted def.
|
|
68
69
|
*
|
|
69
|
-
* @
|
|
70
|
+
* @remarks
|
|
71
|
+
* - The returned name is a preference, conflicting names are still postfixed (`Planet`, `PlanetInput`, `Planet2`, ...).
|
|
72
|
+
* - Return `undefined` to keep the original def name.
|
|
73
|
+
*
|
|
74
|
+
* @default defName => defName
|
|
70
75
|
*/
|
|
71
|
-
|
|
76
|
+
customComponentName?: (defName: string, defSchema: JsonSchema) => string | undefined;
|
|
72
77
|
/**
|
|
73
78
|
* Filter procedures. Return `false` to exclude a procedure from the OpenAPI specification.
|
|
74
79
|
*
|
package/dist/index.mjs
CHANGED
|
@@ -5,9 +5,9 @@ export { d as OpenAPIJsonSerializer } from './shared/openapi.zZH_UksW.mjs';
|
|
|
5
5
|
import { g as getOpenAPIMeta, o as openapi } from './shared/openapi.B9PQzqBn.mjs';
|
|
6
6
|
import { COMMON_ERROR_STATUS_MAP } from '@orpc/client';
|
|
7
7
|
export { COMMON_ERROR_STATUS_MAP } from '@orpc/client';
|
|
8
|
-
import { encodeJsonPointerSegment, ensureJsonSchemaObject, mapJsonSchemaRefs,
|
|
8
|
+
import { encodeJsonPointerSegment, ensureJsonSchemaObject, mapJsonSchemaRefs, decodeJsonPointerSegment, isUnconstrainedSchema, combineJsonSchemasWithComposition, extractJsonObjectSchemaEntries, combineJsonObjectSchemaEntries, flattenJsonUnionSchema, isJsonPrimitiveSchema, matchArrayableJsonSchema, isJsonFileSchema, DelegatingJsonSchemaConverter, StandardJsonSchemaConverter } from '@orpc/json-schema';
|
|
9
9
|
import { DEFAULT_SUCCESS_STATUS, DEFAULT_ERROR_STATUS, walkProcedureContractsAsync } from '@orpc/server';
|
|
10
|
-
import {
|
|
10
|
+
import { isDeepEqual, value, findDeepMatches, isPlainObject, stringifyJSON, toArray, clone, pathToHttpPath, mergeHttpPath, isTypescriptObject } from '@orpc/shared';
|
|
11
11
|
import '@standardserver/core';
|
|
12
12
|
|
|
13
13
|
function createContractJsonifiedClientFactory(link, options = {}) {
|
|
@@ -15,14 +15,13 @@ function createContractJsonifiedClientFactory(link, options = {}) {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
class OpenAPIComponentRegistry {
|
|
18
|
-
constructor(doc,
|
|
18
|
+
constructor(doc, customComponentName) {
|
|
19
19
|
this.doc = doc;
|
|
20
|
-
this.
|
|
20
|
+
this.customComponentName = customComponentName;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
23
|
* Registers `schema` as a component under `preferredName` (or an equivalent/postfixed name)
|
|
24
|
-
* and returns a `$ref` to it.
|
|
25
|
-
* returned in its local `$defs` form instead.
|
|
24
|
+
* and returns a `$ref` to it.
|
|
26
25
|
*/
|
|
27
26
|
register(preferredName, schema) {
|
|
28
27
|
const { $defs, ...body } = schema;
|
|
@@ -39,59 +38,53 @@ class OpenAPIComponentRegistry {
|
|
|
39
38
|
}
|
|
40
39
|
/**
|
|
41
40
|
* Moves a schema's root-level `$defs` into `doc.components.schemas` and rewrites
|
|
42
|
-
* its refs accordingly.
|
|
43
|
-
* hoisted def references them.
|
|
41
|
+
* its refs accordingly.
|
|
44
42
|
*/
|
|
45
43
|
hoistDefs(schema, direction) {
|
|
46
44
|
if (typeof schema !== "object" || !schema.$defs) {
|
|
47
45
|
return schema;
|
|
48
46
|
}
|
|
49
47
|
const { $defs, ...rest } = schema;
|
|
50
|
-
const
|
|
51
|
-
const
|
|
48
|
+
const defs = {};
|
|
49
|
+
const preferredNames = {};
|
|
52
50
|
for (const defName of Object.keys($defs)) {
|
|
53
51
|
const defSchema = $defs[defName];
|
|
54
52
|
if (defSchema === void 0) {
|
|
55
53
|
continue;
|
|
56
54
|
}
|
|
57
55
|
const normalized = ensureJsonSchemaObject(defSchema);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
} else {
|
|
61
|
-
localDefs[defName] = normalized;
|
|
62
|
-
}
|
|
56
|
+
defs[defName] = normalized;
|
|
57
|
+
preferredNames[defName] = this.customComponentName?.(defName, normalized) ?? defName;
|
|
63
58
|
}
|
|
64
|
-
|
|
65
|
-
if (
|
|
59
|
+
const defNames = Object.keys(defs);
|
|
60
|
+
if (defNames.length === 0) {
|
|
66
61
|
return schema;
|
|
67
62
|
}
|
|
68
63
|
this.doc.components ??= {};
|
|
69
64
|
this.doc.components.schemas ??= {};
|
|
70
65
|
const componentsSchemas = this.doc.components.schemas;
|
|
71
66
|
const identityRenameMap = Object.fromEntries(
|
|
72
|
-
|
|
67
|
+
defNames.map((defName) => [defName, preferredNames[defName]])
|
|
73
68
|
);
|
|
74
69
|
const renameMap = {};
|
|
75
70
|
const pendingSchemas = [];
|
|
76
|
-
for (const defName of
|
|
77
|
-
const cleanSchema =
|
|
71
|
+
for (const defName of defNames) {
|
|
72
|
+
const cleanSchema = defs[defName];
|
|
78
73
|
const candidateSchemas = Object.fromEntries(
|
|
79
|
-
|
|
80
|
-
currentDefName,
|
|
81
|
-
rewriteComponentSchemaRefs(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
...renameMap
|
|
86
|
-
}
|
|
87
|
-
)
|
|
74
|
+
defNames.map((currentDefName) => [
|
|
75
|
+
preferredNames[currentDefName],
|
|
76
|
+
rewriteComponentSchemaRefs(defs[currentDefName], {
|
|
77
|
+
...identityRenameMap,
|
|
78
|
+
...renameMap
|
|
79
|
+
})
|
|
88
80
|
])
|
|
89
81
|
);
|
|
90
|
-
const
|
|
82
|
+
const preferredName = preferredNames[defName];
|
|
83
|
+
const prelimSchema = candidateSchemas[preferredName];
|
|
91
84
|
const [componentName, reuseExisting] = resolveComponentName(
|
|
92
85
|
componentsSchemas,
|
|
93
86
|
new Set(Object.values(renameMap)),
|
|
94
|
-
|
|
87
|
+
preferredName,
|
|
95
88
|
prelimSchema,
|
|
96
89
|
candidateSchemas,
|
|
97
90
|
direction
|
|
@@ -103,77 +96,20 @@ class OpenAPIComponentRegistry {
|
|
|
103
96
|
}
|
|
104
97
|
for (const { cleanSchema, componentName } of pendingSchemas) {
|
|
105
98
|
componentsSchemas[componentName] = rewriteComponentSchemaRefs(
|
|
106
|
-
|
|
99
|
+
cleanSchema,
|
|
107
100
|
renameMap
|
|
108
101
|
);
|
|
109
102
|
}
|
|
110
|
-
return rewriteComponentSchemaRefs(
|
|
103
|
+
return rewriteComponentSchemaRefs(rest, renameMap);
|
|
111
104
|
}
|
|
112
105
|
toOpenAPISchema(schema, direction) {
|
|
113
106
|
return ensureJsonSchemaObject(this.hoistDefs(schema, direction));
|
|
114
107
|
}
|
|
115
108
|
}
|
|
116
|
-
function
|
|
117
|
-
visitJsonSchemaRefs(schema, (ref) => {
|
|
118
|
-
const refName = parseLocalDefRefName(ref);
|
|
119
|
-
if (refName !== void 0) {
|
|
120
|
-
onRef(refName);
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
function hoistReferencedLocalDefs(hoistedDefs, localDefs) {
|
|
125
|
-
const queue = Object.values(hoistedDefs);
|
|
126
|
-
while (queue.length > 0) {
|
|
127
|
-
const current = queue.shift();
|
|
128
|
-
visitLocalDefRefs(current, (refName) => {
|
|
129
|
-
const referenced = localDefs[refName];
|
|
130
|
-
if (referenced === void 0) {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
hoistedDefs[refName] = referenced;
|
|
134
|
-
delete localDefs[refName];
|
|
135
|
-
queue.push(referenced);
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
function withReferencedLocalDefs(schema, localDefs) {
|
|
140
|
-
const referencedLocalDefs = collectReferencedLocalDefNames(schema, localDefs);
|
|
141
|
-
if (referencedLocalDefs.length === 0) {
|
|
142
|
-
return schema;
|
|
143
|
-
}
|
|
144
|
-
const mergedDefs = {
|
|
145
|
-
...schema.$defs
|
|
146
|
-
};
|
|
147
|
-
for (const defName of referencedLocalDefs) {
|
|
148
|
-
mergedDefs[defName] = localDefs[defName];
|
|
149
|
-
}
|
|
150
|
-
return {
|
|
151
|
-
...schema,
|
|
152
|
-
$defs: mergedDefs
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
function collectReferencedLocalDefNames(schema, localDefs) {
|
|
156
|
-
if (Object.keys(localDefs).length === 0) {
|
|
157
|
-
return [];
|
|
158
|
-
}
|
|
159
|
-
const referenced = /* @__PURE__ */ new Set();
|
|
160
|
-
const queue = [schema];
|
|
161
|
-
while (queue.length > 0) {
|
|
162
|
-
const current = queue.shift();
|
|
163
|
-
visitLocalDefRefs(current, (refName) => {
|
|
164
|
-
if (localDefs[refName] === void 0 || referenced.has(refName)) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
referenced.add(refName);
|
|
168
|
-
queue.push(localDefs[refName]);
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
return [...referenced];
|
|
172
|
-
}
|
|
173
|
-
function resolveComponentName(componentsSchemas, claimedNames, defName, schema, candidateSchemas, direction) {
|
|
109
|
+
function resolveComponentName(componentsSchemas, claimedNames, preferredName, schema, candidateSchemas, direction) {
|
|
174
110
|
let mintName;
|
|
175
111
|
for (let i = 1; ; i++) {
|
|
176
|
-
const [componentName, mintable, tail] = componentNameCandidate(
|
|
112
|
+
const [componentName, mintable, tail] = componentNameCandidate(preferredName, direction, i);
|
|
177
113
|
const existingSchema = componentsSchemas[componentName];
|
|
178
114
|
if (existingSchema === void 0) {
|
|
179
115
|
if (mintable && !claimedNames.has(componentName)) {
|
|
@@ -191,27 +127,27 @@ function resolveComponentName(componentsSchemas, claimedNames, defName, schema,
|
|
|
191
127
|
existingSchema,
|
|
192
128
|
candidateSchemas,
|
|
193
129
|
componentsSchemas,
|
|
194
|
-
/* @__PURE__ */ new Map([[
|
|
195
|
-
/* @__PURE__ */ new Map([[componentName,
|
|
130
|
+
/* @__PURE__ */ new Map([[preferredName, componentName]]),
|
|
131
|
+
/* @__PURE__ */ new Map([[componentName, preferredName]])
|
|
196
132
|
)) {
|
|
197
133
|
return [componentName, true];
|
|
198
134
|
}
|
|
199
135
|
}
|
|
200
136
|
}
|
|
201
|
-
function componentNameCandidate(
|
|
137
|
+
function componentNameCandidate(preferredName, direction, attempt) {
|
|
202
138
|
if (attempt === 1) {
|
|
203
|
-
return [
|
|
139
|
+
return [preferredName, true, false];
|
|
204
140
|
}
|
|
205
141
|
if (direction !== void 0) {
|
|
206
142
|
if (attempt === 2) {
|
|
207
|
-
return [`${
|
|
143
|
+
return [`${preferredName}${direction === "input" ? "Input" : "Output"}`, true, false];
|
|
208
144
|
}
|
|
209
145
|
if (attempt === 3) {
|
|
210
|
-
return [`${
|
|
146
|
+
return [`${preferredName}${direction === "input" ? "Output" : "Input"}`, false, false];
|
|
211
147
|
}
|
|
212
|
-
return [`${
|
|
148
|
+
return [`${preferredName}${attempt - 2}`, true, true];
|
|
213
149
|
}
|
|
214
|
-
return [`${
|
|
150
|
+
return [`${preferredName}${attempt}`, true, true];
|
|
215
151
|
}
|
|
216
152
|
function definedKeysOf(object) {
|
|
217
153
|
return Object.keys(object).filter((key) => object[key] !== void 0).sort();
|
|
@@ -678,13 +614,14 @@ function buildErrorResponse(ctx, operation, def) {
|
|
|
678
614
|
status
|
|
679
615
|
);
|
|
680
616
|
const responseSchema = customBodySchema ?? combineJsonSchemasWithComposition("oneOf", [
|
|
681
|
-
...definitions.map(({ code, dataJsonSchema, dataOptional
|
|
617
|
+
...definitions.map(({ code, dataJsonSchema, dataOptional }) => {
|
|
682
618
|
return ctx.registry.register(toErrorComponentName(code), combineJsonObjectSchemaEntries([
|
|
683
619
|
["defined", { const: true }, false],
|
|
684
620
|
["inferable", { type: "boolean" }, false],
|
|
685
621
|
["code", { const: code }, false],
|
|
686
622
|
["status", { const: status }, false],
|
|
687
|
-
|
|
623
|
+
// avoid using the defaultMessage here to improve component reusability
|
|
624
|
+
["message", { type: "string" }, false],
|
|
688
625
|
["data", ctx.registry.hoistDefs(dataJsonSchema, "output"), dataOptional]
|
|
689
626
|
]));
|
|
690
627
|
}),
|
|
@@ -811,7 +748,7 @@ class OpenAPIGenerator {
|
|
|
811
748
|
info: options.base?.info ?? { title: "API Reference", version: "0.0.0" }
|
|
812
749
|
};
|
|
813
750
|
const ctx = {
|
|
814
|
-
registry: new OpenAPIComponentRegistry(doc, options.
|
|
751
|
+
registry: new OpenAPIComponentRegistry(doc, options.customComponentName),
|
|
815
752
|
convertSchemas: (schemas, direction) => this.convertSchemas(schemas, direction),
|
|
816
753
|
errorStatusMap: options.errorStatusMap ?? COMMON_ERROR_STATUS_MAP,
|
|
817
754
|
customErrorResponseBodySchema: options.customErrorResponseBodySchema
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orpc/openapi",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.0.0-beta.
|
|
4
|
+
"version": "2.0.0-beta.21",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://orpc.dev",
|
|
7
7
|
"repository": {
|
|
@@ -78,17 +78,17 @@
|
|
|
78
78
|
"@standardserver/core": "^0.5.0",
|
|
79
79
|
"@standardserver/fetch": "^0.5.0",
|
|
80
80
|
"rou3": "^0.9.1",
|
|
81
|
-
"@orpc/client": "2.0.0-beta.
|
|
82
|
-
"@orpc/
|
|
83
|
-
"@orpc/
|
|
84
|
-
"@orpc/shared": "2.0.0-beta.
|
|
85
|
-
"@orpc/json-schema": "2.0.0-beta.
|
|
81
|
+
"@orpc/client": "2.0.0-beta.21",
|
|
82
|
+
"@orpc/server": "2.0.0-beta.21",
|
|
83
|
+
"@orpc/contract": "2.0.0-beta.21",
|
|
84
|
+
"@orpc/shared": "2.0.0-beta.21",
|
|
85
|
+
"@orpc/json-schema": "2.0.0-beta.21"
|
|
86
86
|
},
|
|
87
87
|
"devDependencies": {
|
|
88
|
-
"@scalar/api-reference": "^1.
|
|
88
|
+
"@scalar/api-reference": "^1.63.0",
|
|
89
89
|
"@types/swagger-ui": "^5.32.0",
|
|
90
90
|
"fastify": "^5.8.5",
|
|
91
|
-
"swagger-ui": "^5.32.
|
|
91
|
+
"swagger-ui": "^5.32.11",
|
|
92
92
|
"zod": "^4.4.3"
|
|
93
93
|
},
|
|
94
94
|
"scripts": {
|