@typespec/openapi3 0.56.0-dev.2 → 0.56.0-dev.4
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/src/lib.d.ts +2 -23
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +0 -6
- package/dist/src/lib.js.map +1 -1
- package/dist/src/openapi.js +33 -36
- package/dist/src/openapi.js.map +1 -1
- package/dist/src/schema-emitter.d.ts.map +1 -1
- package/dist/src/schema-emitter.js +427 -425
- package/dist/src/schema-emitter.js.map +1 -1
- package/dist/src/visibility-usage.js +5 -4
- package/dist/src/visibility-usage.js.map +1 -1
- package/package.json +5 -5
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
-
};
|
|
12
|
-
var _OpenAPI3SchemaEmitter_instances, _OpenAPI3SchemaEmitter_metadataInfo, _OpenAPI3SchemaEmitter_visibilityUsage, _OpenAPI3SchemaEmitter_options, _OpenAPI3SchemaEmitter_applyExternalDocs, _OpenAPI3SchemaEmitter_typeNameOptions, _OpenAPI3SchemaEmitter_getVisibilityContext, _OpenAPI3SchemaEmitter_getContentType, _OpenAPI3SchemaEmitter_requiredModelProperties, _OpenAPI3SchemaEmitter_isBytesKeptRaw, _OpenAPI3SchemaEmitter_enumSchema, _OpenAPI3SchemaEmitter_unionSchema, _OpenAPI3SchemaEmitter_getDiscriminatorMapping, _OpenAPI3SchemaEmitter_attachExtensions, _OpenAPI3SchemaEmitter_getSchemaForScalar, _OpenAPI3SchemaEmitter_getSchemaForStdScalars, _OpenAPI3SchemaEmitter_applyConstraints, _OpenAPI3SchemaEmitter_inlineType, _OpenAPI3SchemaEmitter_createDeclaration, _OpenAPI3SchemaEmitter_isStdType, _OpenAPI3SchemaEmitter_applyEncoding, _OpenAPI3SchemaEmitter_mergeFormatAndEncoding;
|
|
13
1
|
import { compilerAssert, getDeprecated, getDiscriminatedUnion, getDiscriminator, getDoc, getEncode, getFormat, getKnownValues, getMaxItems, getMaxLength, getMaxValue, getMaxValueExclusive, getMinItems, getMinLength, getMinValue, getMinValueExclusive, getNamespaceFullName, getPattern, getSummary, getTypeName, ignoreDiagnostics, isArrayModelType, isNeverType, isNullType, isSecret, isTemplateDeclaration, resolveEncodedName, stringTemplateToString, } from "@typespec/compiler";
|
|
14
2
|
import { ArrayBuilder, ObjectBuilder, Placeholder, TypeEmitter, } from "@typespec/compiler/emitter-framework";
|
|
15
3
|
import { Visibility, getVisibilitySuffix } from "@typespec/http";
|
|
@@ -20,15 +8,14 @@ import { reportDiagnostic } from "./lib.js";
|
|
|
20
8
|
* OpenAPI3 schema emitter. Deals with emitting content of `components/schemas` section.
|
|
21
9
|
*/
|
|
22
10
|
export class OpenAPI3SchemaEmitter extends TypeEmitter {
|
|
11
|
+
#metadataInfo;
|
|
12
|
+
#visibilityUsage;
|
|
13
|
+
#options;
|
|
23
14
|
constructor(emitter, metadataInfo, visibilityUsage, options) {
|
|
24
15
|
super(emitter);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
_OpenAPI3SchemaEmitter_options.set(this, void 0);
|
|
29
|
-
__classPrivateFieldSet(this, _OpenAPI3SchemaEmitter_metadataInfo, metadataInfo, "f");
|
|
30
|
-
__classPrivateFieldSet(this, _OpenAPI3SchemaEmitter_visibilityUsage, visibilityUsage, "f");
|
|
31
|
-
__classPrivateFieldSet(this, _OpenAPI3SchemaEmitter_options, options, "f");
|
|
16
|
+
this.#metadataInfo = metadataInfo;
|
|
17
|
+
this.#visibilityUsage = visibilityUsage;
|
|
18
|
+
this.#options = options;
|
|
32
19
|
}
|
|
33
20
|
modelDeclarationReferenceContext(model, name) {
|
|
34
21
|
return this.reduceContext(model);
|
|
@@ -46,12 +33,12 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter {
|
|
|
46
33
|
return this.reduceContext(union);
|
|
47
34
|
}
|
|
48
35
|
reduceContext(type) {
|
|
49
|
-
const visibility =
|
|
36
|
+
const visibility = this.#getVisibilityContext();
|
|
50
37
|
const patch = {};
|
|
51
|
-
if (visibility !== Visibility.Read && !
|
|
38
|
+
if (visibility !== Visibility.Read && !this.#metadataInfo.isTransformed(type, visibility)) {
|
|
52
39
|
patch.visibility = Visibility.Read;
|
|
53
40
|
}
|
|
54
|
-
const contentType =
|
|
41
|
+
const contentType = this.#getContentType();
|
|
55
42
|
if (contentType === "application/json") {
|
|
56
43
|
patch.contentType = undefined;
|
|
57
44
|
}
|
|
@@ -59,10 +46,10 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter {
|
|
|
59
46
|
}
|
|
60
47
|
modelDeclaration(model, _) {
|
|
61
48
|
const program = this.emitter.getProgram();
|
|
62
|
-
const visibility =
|
|
49
|
+
const visibility = this.#getVisibilityContext();
|
|
63
50
|
const schema = new ObjectBuilder({
|
|
64
51
|
type: "object",
|
|
65
|
-
required:
|
|
52
|
+
required: this.#requiredModelProperties(model, visibility),
|
|
66
53
|
properties: this.emitter.emitModelProperties(model),
|
|
67
54
|
});
|
|
68
55
|
if (model.indexer) {
|
|
@@ -78,24 +65,49 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter {
|
|
|
78
65
|
const [union] = getDiscriminatedUnion(model, discriminator);
|
|
79
66
|
const openApiDiscriminator = { ...discriminator };
|
|
80
67
|
if (union.variants.size > 0) {
|
|
81
|
-
openApiDiscriminator.mapping =
|
|
68
|
+
openApiDiscriminator.mapping = this.#getDiscriminatorMapping(union);
|
|
82
69
|
}
|
|
83
70
|
schema.discriminator = openApiDiscriminator;
|
|
84
71
|
}
|
|
85
|
-
|
|
72
|
+
this.#applyExternalDocs(model, schema);
|
|
86
73
|
if (model.baseModel) {
|
|
87
74
|
schema.set("allOf", B.array([this.emitter.emitTypeReference(model.baseModel)]));
|
|
88
75
|
}
|
|
89
|
-
const baseName = getOpenAPITypeName(program, model,
|
|
90
|
-
const isMultipart =
|
|
76
|
+
const baseName = getOpenAPITypeName(program, model, this.#typeNameOptions());
|
|
77
|
+
const isMultipart = this.#getContentType().startsWith("multipart/");
|
|
91
78
|
const name = isMultipart ? baseName + "MultiPart" : baseName;
|
|
92
|
-
return
|
|
79
|
+
return this.#createDeclaration(model, name, this.#applyConstraints(model, schema));
|
|
80
|
+
}
|
|
81
|
+
#applyExternalDocs(typespecType, target) {
|
|
82
|
+
const externalDocs = getExternalDocs(this.emitter.getProgram(), typespecType);
|
|
83
|
+
if (externalDocs) {
|
|
84
|
+
target.externalDocs = externalDocs;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
#typeNameOptions() {
|
|
88
|
+
const serviceNamespaceName = this.emitter.getContext().serviceNamespaceName;
|
|
89
|
+
return {
|
|
90
|
+
// shorten type names by removing TypeSpec and service namespace
|
|
91
|
+
namespaceFilter(ns) {
|
|
92
|
+
const name = getNamespaceFullName(ns);
|
|
93
|
+
return name !== serviceNamespaceName;
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
#getVisibilityContext() {
|
|
98
|
+
return this.emitter.getContext().visibility ?? Visibility.Read;
|
|
99
|
+
}
|
|
100
|
+
#ignoreMetadataAnnotations() {
|
|
101
|
+
return this.emitter.getContext().ignoreMetadataAnnotations;
|
|
102
|
+
}
|
|
103
|
+
#getContentType() {
|
|
104
|
+
return this.emitter.getContext().contentType ?? "application/json";
|
|
93
105
|
}
|
|
94
106
|
modelLiteral(model) {
|
|
95
107
|
const schema = new ObjectBuilder({
|
|
96
108
|
type: "object",
|
|
97
109
|
properties: this.emitter.emitModelProperties(model),
|
|
98
|
-
required:
|
|
110
|
+
required: this.#requiredModelProperties(model, this.#getVisibilityContext()),
|
|
99
111
|
});
|
|
100
112
|
if (model.indexer) {
|
|
101
113
|
schema.set("additionalProperties", this.emitter.emitTypeReference(model.indexer.value));
|
|
@@ -103,7 +115,7 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter {
|
|
|
103
115
|
return schema;
|
|
104
116
|
}
|
|
105
117
|
modelInstantiation(model, name) {
|
|
106
|
-
name = name
|
|
118
|
+
name = name ?? getOpenAPITypeName(this.emitter.getProgram(), model, this.#typeNameOptions());
|
|
107
119
|
if (!name) {
|
|
108
120
|
return this.modelLiteral(model);
|
|
109
121
|
}
|
|
@@ -114,35 +126,52 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter {
|
|
|
114
126
|
type: "array",
|
|
115
127
|
items: this.emitter.emitTypeReference(elementType),
|
|
116
128
|
});
|
|
117
|
-
return
|
|
129
|
+
return this.#createDeclaration(array, name, this.#applyConstraints(array, schema));
|
|
118
130
|
}
|
|
119
131
|
arrayDeclarationReferenceContext(array, name, elementType) {
|
|
120
132
|
return {
|
|
121
|
-
visibility:
|
|
133
|
+
visibility: this.#getVisibilityContext() | Visibility.Item,
|
|
122
134
|
};
|
|
123
135
|
}
|
|
124
136
|
arrayLiteral(array, elementType) {
|
|
125
|
-
return
|
|
137
|
+
return this.#inlineType(array, new ObjectBuilder({
|
|
126
138
|
type: "array",
|
|
127
139
|
items: this.emitter.emitTypeReference(elementType),
|
|
128
140
|
}));
|
|
129
141
|
}
|
|
130
142
|
arrayLiteralReferenceContext(array, elementType) {
|
|
131
143
|
return {
|
|
132
|
-
visibility:
|
|
144
|
+
visibility: this.#getVisibilityContext() | Visibility.Item,
|
|
133
145
|
};
|
|
134
146
|
}
|
|
147
|
+
#requiredModelProperties(model, visibility) {
|
|
148
|
+
const requiredProps = [];
|
|
149
|
+
for (const prop of model.properties.values()) {
|
|
150
|
+
if (isNeverType(prop.type)) {
|
|
151
|
+
// If the property has a type of 'never', don't include it in the schema
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (!this.#metadataInfo.isPayloadProperty(prop, visibility, this.#ignoreMetadataAnnotations())) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (!this.#metadataInfo.isOptional(prop, visibility)) {
|
|
158
|
+
const encodedName = resolveEncodedName(this.emitter.getProgram(), prop, this.#getContentType());
|
|
159
|
+
requiredProps.push(encodedName);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return requiredProps.length > 0 ? requiredProps : undefined;
|
|
163
|
+
}
|
|
135
164
|
modelProperties(model) {
|
|
136
165
|
const program = this.emitter.getProgram();
|
|
137
166
|
const props = new ObjectBuilder();
|
|
138
167
|
const visibility = this.emitter.getContext().visibility;
|
|
139
|
-
const contentType =
|
|
168
|
+
const contentType = this.#getContentType();
|
|
140
169
|
for (const prop of model.properties.values()) {
|
|
141
170
|
if (isNeverType(prop.type)) {
|
|
142
171
|
// If the property has a type of 'never', don't include it in the schema
|
|
143
172
|
continue;
|
|
144
173
|
}
|
|
145
|
-
if (!
|
|
174
|
+
if (!this.#metadataInfo.isPayloadProperty(prop, visibility, this.#ignoreMetadataAnnotations())) {
|
|
146
175
|
continue;
|
|
147
176
|
}
|
|
148
177
|
const result = this.emitter.emitModelProperty(prop);
|
|
@@ -161,23 +190,27 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter {
|
|
|
161
190
|
}
|
|
162
191
|
return props;
|
|
163
192
|
}
|
|
193
|
+
#isBytesKeptRaw(type) {
|
|
194
|
+
const program = this.emitter.getProgram();
|
|
195
|
+
return (type.kind === "Scalar" && type.name === "bytes" && getEncode(program, type) === undefined);
|
|
196
|
+
}
|
|
164
197
|
modelPropertyLiteral(prop) {
|
|
165
198
|
const program = this.emitter.getProgram();
|
|
166
|
-
const isMultipart =
|
|
199
|
+
const isMultipart = this.#getContentType().startsWith("multipart/");
|
|
167
200
|
if (isMultipart) {
|
|
168
|
-
if (
|
|
201
|
+
if (this.#isBytesKeptRaw(prop.type) && getEncode(program, prop) === undefined) {
|
|
169
202
|
return { type: "string", format: "binary" };
|
|
170
203
|
}
|
|
171
204
|
if (prop.type.kind === "Model" &&
|
|
172
205
|
isArrayModelType(program, prop.type) &&
|
|
173
|
-
|
|
206
|
+
this.#isBytesKeptRaw(prop.type.indexer.value)) {
|
|
174
207
|
return { type: "array", items: { type: "string", format: "binary" } };
|
|
175
208
|
}
|
|
176
209
|
}
|
|
177
210
|
const refSchema = this.emitter.emitTypeReference(prop.type, {
|
|
178
211
|
referenceContext: isMultipart &&
|
|
179
212
|
(prop.type.kind !== "Union" ||
|
|
180
|
-
![...prop.type.variants.values()].some((x) =>
|
|
213
|
+
![...prop.type.variants.values()].some((x) => this.#isBytesKeptRaw(x.type)))
|
|
181
214
|
? { contentType: "application/json" }
|
|
182
215
|
: {},
|
|
183
216
|
});
|
|
@@ -185,9 +218,9 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter {
|
|
|
185
218
|
throw new Error("Unexpected non-code result from emit reference");
|
|
186
219
|
}
|
|
187
220
|
const isRef = refSchema.value instanceof Placeholder || "$ref" in refSchema.value;
|
|
188
|
-
const schema =
|
|
221
|
+
const schema = this.#applyEncoding(prop, refSchema.value);
|
|
189
222
|
// Apply decorators on the property to the type's schema
|
|
190
|
-
const additionalProps =
|
|
223
|
+
const additionalProps = this.#applyConstraints(prop, {});
|
|
191
224
|
if (prop.default) {
|
|
192
225
|
additionalProps.default = getDefaultValue(program, prop.type, prop.default);
|
|
193
226
|
}
|
|
@@ -195,7 +228,7 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter {
|
|
|
195
228
|
additionalProps.readOnly = true;
|
|
196
229
|
}
|
|
197
230
|
// Attach any additional OpenAPI extensions
|
|
198
|
-
|
|
231
|
+
this.#attachExtensions(program, prop, additionalProps);
|
|
199
232
|
if (schema && isRef && !(prop.type.kind === "Model" && isArrayModelType(program, prop.type))) {
|
|
200
233
|
if (Object.keys(additionalProps).length === 0) {
|
|
201
234
|
return schema;
|
|
@@ -239,8 +272,26 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter {
|
|
|
239
272
|
return { type: "number", enum: [number.value] };
|
|
240
273
|
}
|
|
241
274
|
enumDeclaration(en, name) {
|
|
242
|
-
const baseName = getOpenAPITypeName(this.emitter.getProgram(), en,
|
|
243
|
-
return
|
|
275
|
+
const baseName = getOpenAPITypeName(this.emitter.getProgram(), en, this.#typeNameOptions());
|
|
276
|
+
return this.#createDeclaration(en, baseName, new ObjectBuilder(this.#enumSchema(en)));
|
|
277
|
+
}
|
|
278
|
+
#enumSchema(en) {
|
|
279
|
+
const program = this.emitter.getProgram();
|
|
280
|
+
if (en.members.size === 0) {
|
|
281
|
+
reportDiagnostic(program, { code: "empty-enum", target: en });
|
|
282
|
+
return {};
|
|
283
|
+
}
|
|
284
|
+
const enumTypes = new Set();
|
|
285
|
+
const enumValues = new Set();
|
|
286
|
+
for (const member of en.members.values()) {
|
|
287
|
+
enumTypes.add(typeof member.value === "number" ? "number" : "string");
|
|
288
|
+
enumValues.add(member.value ?? member.name);
|
|
289
|
+
}
|
|
290
|
+
if (enumTypes.size > 1) {
|
|
291
|
+
reportDiagnostic(program, { code: "enum-unique-type", target: en });
|
|
292
|
+
}
|
|
293
|
+
const schema = { type: enumTypes.values().next().value, enum: [...enumValues] };
|
|
294
|
+
return this.#applyConstraints(en, schema);
|
|
244
295
|
}
|
|
245
296
|
enumMember(member) {
|
|
246
297
|
return this.enumMemberReference(member);
|
|
@@ -257,11 +308,127 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter {
|
|
|
257
308
|
}
|
|
258
309
|
}
|
|
259
310
|
unionDeclaration(union, name) {
|
|
260
|
-
const schema =
|
|
261
|
-
return
|
|
311
|
+
const schema = this.#unionSchema(union);
|
|
312
|
+
return this.#createDeclaration(union, name, schema);
|
|
313
|
+
}
|
|
314
|
+
#unionSchema(union) {
|
|
315
|
+
const program = this.emitter.getProgram();
|
|
316
|
+
if (union.variants.size === 0) {
|
|
317
|
+
reportDiagnostic(program, { code: "empty-union", target: union });
|
|
318
|
+
return new ObjectBuilder({});
|
|
319
|
+
}
|
|
320
|
+
const variants = Array.from(union.variants.values());
|
|
321
|
+
const literalVariantEnumByType = {};
|
|
322
|
+
const ofType = getOneOf(program, union) ? "oneOf" : "anyOf";
|
|
323
|
+
const schemaMembers = [];
|
|
324
|
+
let nullable = false;
|
|
325
|
+
const discriminator = getDiscriminator(program, union);
|
|
326
|
+
const isMultipart = this.#getContentType().startsWith("multipart/");
|
|
327
|
+
for (const variant of variants) {
|
|
328
|
+
if (isNullType(variant.type)) {
|
|
329
|
+
nullable = true;
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
if (isMultipart && this.#isBytesKeptRaw(variant.type)) {
|
|
333
|
+
schemaMembers.push({ schema: { type: "string", format: "binary" }, type: variant.type });
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
if (isLiteralType(variant.type)) {
|
|
337
|
+
if (!literalVariantEnumByType[variant.type.kind]) {
|
|
338
|
+
const enumValue = [variant.type.value];
|
|
339
|
+
literalVariantEnumByType[variant.type.kind] = enumValue;
|
|
340
|
+
schemaMembers.push({
|
|
341
|
+
schema: { type: literalType(variant.type), enum: enumValue },
|
|
342
|
+
type: null,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
literalVariantEnumByType[variant.type.kind].push(variant.type.value);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
const enumSchema = this.emitter.emitTypeReference(variant.type, {
|
|
351
|
+
referenceContext: isMultipart ? { contentType: "application/json" } : {},
|
|
352
|
+
});
|
|
353
|
+
compilerAssert(enumSchema.kind === "code", "Unexpected enum schema. Should be kind: code");
|
|
354
|
+
schemaMembers.push({ schema: enumSchema.value, type: variant.type });
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (schemaMembers.length === 0) {
|
|
358
|
+
if (nullable) {
|
|
359
|
+
// This union is equivalent to just `null` but OA3 has no way to specify
|
|
360
|
+
// null as a value, so we throw an error.
|
|
361
|
+
reportDiagnostic(program, { code: "union-null", target: union });
|
|
362
|
+
return new ObjectBuilder({});
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
// completely empty union can maybe only happen with bugs?
|
|
366
|
+
compilerAssert(false, "Attempting to emit an empty union");
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (schemaMembers.length === 1) {
|
|
370
|
+
// we can just return the single schema member after applying nullable
|
|
371
|
+
const schema = schemaMembers[0].schema;
|
|
372
|
+
const type = schemaMembers[0].type;
|
|
373
|
+
const additionalProps = this.#applyConstraints(union, {});
|
|
374
|
+
if (nullable) {
|
|
375
|
+
additionalProps.nullable = true;
|
|
376
|
+
}
|
|
377
|
+
if (Object.keys(additionalProps).length === 0) {
|
|
378
|
+
return new ObjectBuilder(schema);
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
if ((schema instanceof Placeholder || "$ref" in schema) &&
|
|
382
|
+
!(type && shouldInline(program, type))) {
|
|
383
|
+
if (type && type.kind === "Model") {
|
|
384
|
+
return new ObjectBuilder({
|
|
385
|
+
type: "object",
|
|
386
|
+
allOf: B.array([schema]),
|
|
387
|
+
...additionalProps,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
return new ObjectBuilder({ oneOf: B.array([schema]), ...additionalProps });
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
const merged = new ObjectBuilder(schema);
|
|
396
|
+
for (const [key, value] of Object.entries(additionalProps)) {
|
|
397
|
+
merged.set(key, value);
|
|
398
|
+
}
|
|
399
|
+
return merged;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const schema = {
|
|
404
|
+
[ofType]: schemaMembers.map((m) => m.schema),
|
|
405
|
+
};
|
|
406
|
+
if (nullable) {
|
|
407
|
+
schema.nullable = true;
|
|
408
|
+
}
|
|
409
|
+
if (discriminator) {
|
|
410
|
+
// the decorator validates that all the variants will be a model type
|
|
411
|
+
// with the discriminator field present.
|
|
412
|
+
schema.discriminator = { ...discriminator };
|
|
413
|
+
// Diagnostic already reported in compiler for unions
|
|
414
|
+
const discriminatedUnion = ignoreDiagnostics(getDiscriminatedUnion(union, discriminator));
|
|
415
|
+
if (discriminatedUnion.variants.size > 0) {
|
|
416
|
+
schema.discriminator.mapping = this.#getDiscriminatorMapping(discriminatedUnion);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return this.#applyConstraints(union, schema);
|
|
420
|
+
}
|
|
421
|
+
#getDiscriminatorMapping(union) {
|
|
422
|
+
const mapping = {};
|
|
423
|
+
for (const [key, model] of union.variants.entries()) {
|
|
424
|
+
const ref = this.emitter.emitTypeReference(model);
|
|
425
|
+
compilerAssert(ref.kind === "code", "Unexpected ref schema. Should be kind: code");
|
|
426
|
+
mapping[key] = ref.value.$ref;
|
|
427
|
+
}
|
|
428
|
+
return mapping;
|
|
262
429
|
}
|
|
263
430
|
unionLiteral(union) {
|
|
264
|
-
return
|
|
431
|
+
return this.#unionSchema(union);
|
|
265
432
|
}
|
|
266
433
|
unionVariants(union) {
|
|
267
434
|
const variants = new ArrayBuilder();
|
|
@@ -276,6 +443,15 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter {
|
|
|
276
443
|
modelPropertyReference(prop) {
|
|
277
444
|
return this.modelPropertyLiteral(prop);
|
|
278
445
|
}
|
|
446
|
+
#attachExtensions(program, type, emitObject) {
|
|
447
|
+
// Attach any OpenAPI extensions
|
|
448
|
+
const extensions = getExtensions(program, type);
|
|
449
|
+
if (extensions) {
|
|
450
|
+
for (const key of extensions.keys()) {
|
|
451
|
+
emitObject[key] = extensions.get(key);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
279
455
|
reference(targetDeclaration, pathUp, pathDown, commonScope) {
|
|
280
456
|
if (targetDeclaration.value instanceof Placeholder) {
|
|
281
457
|
// I don't think this is possible, confirm.
|
|
@@ -303,18 +479,212 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter {
|
|
|
303
479
|
return super.circularReference(target, scope, cycle);
|
|
304
480
|
}
|
|
305
481
|
scalarDeclaration(scalar, name) {
|
|
306
|
-
const isStd =
|
|
307
|
-
const schema =
|
|
308
|
-
const baseName = getOpenAPITypeName(this.emitter.getProgram(), scalar,
|
|
482
|
+
const isStd = this.#isStdType(scalar);
|
|
483
|
+
const schema = this.#getSchemaForScalar(scalar);
|
|
484
|
+
const baseName = getOpenAPITypeName(this.emitter.getProgram(), scalar, this.#typeNameOptions());
|
|
309
485
|
// Don't create a declaration for std types
|
|
310
|
-
return isStd ? schema :
|
|
486
|
+
return isStd ? schema : this.#createDeclaration(scalar, baseName, new ObjectBuilder(schema));
|
|
311
487
|
}
|
|
312
488
|
scalarInstantiation(scalar, name) {
|
|
313
|
-
return
|
|
489
|
+
return this.#getSchemaForScalar(scalar);
|
|
314
490
|
}
|
|
315
491
|
tupleLiteral(tuple) {
|
|
316
492
|
return { type: "array", items: {} };
|
|
317
493
|
}
|
|
494
|
+
#getSchemaForScalar(scalar) {
|
|
495
|
+
let result = {};
|
|
496
|
+
const isStd = this.#isStdType(scalar);
|
|
497
|
+
if (isStd) {
|
|
498
|
+
result = this.#getSchemaForStdScalars(scalar);
|
|
499
|
+
}
|
|
500
|
+
else if (scalar.baseScalar) {
|
|
501
|
+
result = this.#getSchemaForScalar(scalar.baseScalar);
|
|
502
|
+
}
|
|
503
|
+
const withDecorators = this.#applyEncoding(scalar, this.#applyConstraints(scalar, result));
|
|
504
|
+
if (isStd) {
|
|
505
|
+
// Standard types are going to be inlined in the spec and we don't want the description of the scalar to show up
|
|
506
|
+
delete withDecorators.description;
|
|
507
|
+
}
|
|
508
|
+
return withDecorators;
|
|
509
|
+
}
|
|
510
|
+
#getSchemaForStdScalars(scalar) {
|
|
511
|
+
switch (scalar.name) {
|
|
512
|
+
case "bytes":
|
|
513
|
+
return { type: "string", format: "byte" };
|
|
514
|
+
case "numeric":
|
|
515
|
+
return { type: "number" };
|
|
516
|
+
case "integer":
|
|
517
|
+
return { type: "integer" };
|
|
518
|
+
case "int8":
|
|
519
|
+
return { type: "integer", format: "int8" };
|
|
520
|
+
case "int16":
|
|
521
|
+
return { type: "integer", format: "int16" };
|
|
522
|
+
case "int32":
|
|
523
|
+
return { type: "integer", format: "int32" };
|
|
524
|
+
case "int64":
|
|
525
|
+
return { type: "integer", format: "int64" };
|
|
526
|
+
case "safeint":
|
|
527
|
+
switch (this.#options.safeintStrategy) {
|
|
528
|
+
case "double-int":
|
|
529
|
+
return { type: "integer", format: "double-int" };
|
|
530
|
+
case "int64":
|
|
531
|
+
default:
|
|
532
|
+
return { type: "integer", format: "int64" };
|
|
533
|
+
}
|
|
534
|
+
case "uint8":
|
|
535
|
+
return { type: "integer", format: "uint8" };
|
|
536
|
+
case "uint16":
|
|
537
|
+
return { type: "integer", format: "uint16" };
|
|
538
|
+
case "uint32":
|
|
539
|
+
return { type: "integer", format: "uint32" };
|
|
540
|
+
case "uint64":
|
|
541
|
+
return { type: "integer", format: "uint64" };
|
|
542
|
+
case "float":
|
|
543
|
+
return { type: "number" };
|
|
544
|
+
case "float64":
|
|
545
|
+
return { type: "number", format: "double" };
|
|
546
|
+
case "float32":
|
|
547
|
+
return { type: "number", format: "float" };
|
|
548
|
+
case "decimal":
|
|
549
|
+
return { type: "number", format: "decimal" };
|
|
550
|
+
case "decimal128":
|
|
551
|
+
return { type: "number", format: "decimal128" };
|
|
552
|
+
case "string":
|
|
553
|
+
return { type: "string" };
|
|
554
|
+
case "boolean":
|
|
555
|
+
return { type: "boolean" };
|
|
556
|
+
case "plainDate":
|
|
557
|
+
return { type: "string", format: "date" };
|
|
558
|
+
case "utcDateTime":
|
|
559
|
+
case "offsetDateTime":
|
|
560
|
+
return { type: "string", format: "date-time" };
|
|
561
|
+
case "plainTime":
|
|
562
|
+
return { type: "string", format: "time" };
|
|
563
|
+
case "duration":
|
|
564
|
+
return { type: "string", format: "duration" };
|
|
565
|
+
case "url":
|
|
566
|
+
return { type: "string", format: "uri" };
|
|
567
|
+
default:
|
|
568
|
+
const _assertNever = scalar.name;
|
|
569
|
+
return {};
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
#applyConstraints(type, original) {
|
|
573
|
+
const schema = new ObjectBuilder(original);
|
|
574
|
+
const program = this.emitter.getProgram();
|
|
575
|
+
const applyConstraint = (fn, key) => {
|
|
576
|
+
const value = fn(program, type);
|
|
577
|
+
if (value !== undefined) {
|
|
578
|
+
schema[key] = value;
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
applyConstraint(getMinLength, "minLength");
|
|
582
|
+
applyConstraint(getMaxLength, "maxLength");
|
|
583
|
+
applyConstraint(getMinValue, "minimum");
|
|
584
|
+
applyConstraint(getMaxValue, "maximum");
|
|
585
|
+
applyConstraint(getPattern, "pattern");
|
|
586
|
+
applyConstraint(getMinItems, "minItems");
|
|
587
|
+
applyConstraint(getMaxItems, "maxItems");
|
|
588
|
+
const minValueExclusive = getMinValueExclusive(program, type);
|
|
589
|
+
if (minValueExclusive !== undefined) {
|
|
590
|
+
schema.minimum = minValueExclusive;
|
|
591
|
+
schema.exclusiveMinimum = true;
|
|
592
|
+
}
|
|
593
|
+
const maxValueExclusive = getMaxValueExclusive(program, type);
|
|
594
|
+
if (maxValueExclusive !== undefined) {
|
|
595
|
+
schema.maximum = maxValueExclusive;
|
|
596
|
+
schema.exclusiveMaximum = true;
|
|
597
|
+
}
|
|
598
|
+
if (isSecret(program, type)) {
|
|
599
|
+
schema.format = "password";
|
|
600
|
+
}
|
|
601
|
+
// the stdlib applies a format of "url" but json schema wants "uri",
|
|
602
|
+
// so ignore this format if it's the built-in type.
|
|
603
|
+
if (!this.#isStdType(type) || type.name !== "url") {
|
|
604
|
+
applyConstraint(getFormat, "format");
|
|
605
|
+
}
|
|
606
|
+
applyConstraint(getDoc, "description");
|
|
607
|
+
applyConstraint(getSummary, "title");
|
|
608
|
+
applyConstraint((p, t) => (getDeprecated(p, t) !== undefined ? true : undefined), "deprecated");
|
|
609
|
+
this.#attachExtensions(program, type, schema);
|
|
610
|
+
const values = getKnownValues(program, type);
|
|
611
|
+
if (values) {
|
|
612
|
+
return new ObjectBuilder({
|
|
613
|
+
oneOf: [schema, this.#enumSchema(values)],
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
return new ObjectBuilder(schema);
|
|
617
|
+
}
|
|
618
|
+
#inlineType(type, schema) {
|
|
619
|
+
if (this.#options.includeXTypeSpecName !== "never") {
|
|
620
|
+
schema.set("x-typespec-name", getTypeName(type, this.#typeNameOptions()));
|
|
621
|
+
}
|
|
622
|
+
return schema;
|
|
623
|
+
}
|
|
624
|
+
#createDeclaration(type, name, schema) {
|
|
625
|
+
const refUrl = getRef(this.emitter.getProgram(), type);
|
|
626
|
+
if (refUrl) {
|
|
627
|
+
return {
|
|
628
|
+
$ref: refUrl,
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
if (shouldInline(this.emitter.getProgram(), type)) {
|
|
632
|
+
return this.#inlineType(type, schema);
|
|
633
|
+
}
|
|
634
|
+
const title = getSummary(this.emitter.getProgram(), type);
|
|
635
|
+
if (title) {
|
|
636
|
+
schema.set("title", title);
|
|
637
|
+
}
|
|
638
|
+
const usage = this.#visibilityUsage.getUsage(type);
|
|
639
|
+
const shouldAddSuffix = usage !== undefined && usage.size > 1;
|
|
640
|
+
const visibility = this.#getVisibilityContext();
|
|
641
|
+
const fullName = name + (shouldAddSuffix ? getVisibilitySuffix(visibility, Visibility.Read) : "");
|
|
642
|
+
const decl = this.emitter.result.declaration(fullName, schema);
|
|
643
|
+
checkDuplicateTypeName(this.emitter.getProgram(), type, fullName, Object.fromEntries(decl.scope.declarations.map((x) => [x.name, true])));
|
|
644
|
+
return decl;
|
|
645
|
+
}
|
|
646
|
+
#isStdType(type) {
|
|
647
|
+
return this.emitter.getProgram().checker.isStdType(type);
|
|
648
|
+
}
|
|
649
|
+
#applyEncoding(typespecType, target) {
|
|
650
|
+
const encodeData = getEncode(this.emitter.getProgram(), typespecType);
|
|
651
|
+
if (encodeData) {
|
|
652
|
+
const newTarget = new ObjectBuilder(target);
|
|
653
|
+
const newType = this.#getSchemaForStdScalars(encodeData.type);
|
|
654
|
+
newTarget.type = newType.type;
|
|
655
|
+
// If the target already has a format it takes priority. (e.g. int32)
|
|
656
|
+
newTarget.format = this.#mergeFormatAndEncoding(newTarget.format, encodeData.encoding, newType.format);
|
|
657
|
+
return newTarget;
|
|
658
|
+
}
|
|
659
|
+
const result = new ObjectBuilder(target);
|
|
660
|
+
return result;
|
|
661
|
+
}
|
|
662
|
+
#mergeFormatAndEncoding(format, encoding, encodeAsFormat) {
|
|
663
|
+
switch (format) {
|
|
664
|
+
case undefined:
|
|
665
|
+
return encodeAsFormat ?? encoding;
|
|
666
|
+
case "date-time":
|
|
667
|
+
switch (encoding) {
|
|
668
|
+
case "rfc3339":
|
|
669
|
+
return "date-time";
|
|
670
|
+
case "unixTimestamp":
|
|
671
|
+
return "unixtime";
|
|
672
|
+
case "rfc7231":
|
|
673
|
+
return "http-date";
|
|
674
|
+
default:
|
|
675
|
+
return encoding;
|
|
676
|
+
}
|
|
677
|
+
case "duration":
|
|
678
|
+
switch (encoding) {
|
|
679
|
+
case "ISO8601":
|
|
680
|
+
return "duration";
|
|
681
|
+
default:
|
|
682
|
+
return encodeAsFormat ?? encoding;
|
|
683
|
+
}
|
|
684
|
+
default:
|
|
685
|
+
return encodeAsFormat ?? encoding;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
318
688
|
intrinsic(intrinsic, name) {
|
|
319
689
|
switch (name) {
|
|
320
690
|
case "unknown":
|
|
@@ -334,372 +704,6 @@ export class OpenAPI3SchemaEmitter extends TypeEmitter {
|
|
|
334
704
|
return { scope: sourceFile.globalScope };
|
|
335
705
|
}
|
|
336
706
|
}
|
|
337
|
-
_OpenAPI3SchemaEmitter_metadataInfo = new WeakMap(), _OpenAPI3SchemaEmitter_visibilityUsage = new WeakMap(), _OpenAPI3SchemaEmitter_options = new WeakMap(), _OpenAPI3SchemaEmitter_instances = new WeakSet(), _OpenAPI3SchemaEmitter_applyExternalDocs = function _OpenAPI3SchemaEmitter_applyExternalDocs(typespecType, target) {
|
|
338
|
-
const externalDocs = getExternalDocs(this.emitter.getProgram(), typespecType);
|
|
339
|
-
if (externalDocs) {
|
|
340
|
-
target.externalDocs = externalDocs;
|
|
341
|
-
}
|
|
342
|
-
}, _OpenAPI3SchemaEmitter_typeNameOptions = function _OpenAPI3SchemaEmitter_typeNameOptions() {
|
|
343
|
-
const serviceNamespaceName = this.emitter.getContext().serviceNamespaceName;
|
|
344
|
-
return {
|
|
345
|
-
// shorten type names by removing TypeSpec and service namespace
|
|
346
|
-
namespaceFilter(ns) {
|
|
347
|
-
const name = getNamespaceFullName(ns);
|
|
348
|
-
return name !== serviceNamespaceName;
|
|
349
|
-
},
|
|
350
|
-
};
|
|
351
|
-
}, _OpenAPI3SchemaEmitter_getVisibilityContext = function _OpenAPI3SchemaEmitter_getVisibilityContext() {
|
|
352
|
-
var _a;
|
|
353
|
-
return (_a = this.emitter.getContext().visibility) !== null && _a !== void 0 ? _a : Visibility.Read;
|
|
354
|
-
}, _OpenAPI3SchemaEmitter_getContentType = function _OpenAPI3SchemaEmitter_getContentType() {
|
|
355
|
-
var _a;
|
|
356
|
-
return (_a = this.emitter.getContext().contentType) !== null && _a !== void 0 ? _a : "application/json";
|
|
357
|
-
}, _OpenAPI3SchemaEmitter_requiredModelProperties = function _OpenAPI3SchemaEmitter_requiredModelProperties(model, visibility) {
|
|
358
|
-
const requiredProps = [];
|
|
359
|
-
for (const prop of model.properties.values()) {
|
|
360
|
-
if (isNeverType(prop.type)) {
|
|
361
|
-
// If the property has a type of 'never', don't include it in the schema
|
|
362
|
-
continue;
|
|
363
|
-
}
|
|
364
|
-
if (!__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_metadataInfo, "f").isPayloadProperty(prop, visibility)) {
|
|
365
|
-
continue;
|
|
366
|
-
}
|
|
367
|
-
if (!__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_metadataInfo, "f").isOptional(prop, visibility)) {
|
|
368
|
-
const encodedName = resolveEncodedName(this.emitter.getProgram(), prop, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getContentType).call(this));
|
|
369
|
-
requiredProps.push(encodedName);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
return requiredProps.length > 0 ? requiredProps : undefined;
|
|
373
|
-
}, _OpenAPI3SchemaEmitter_isBytesKeptRaw = function _OpenAPI3SchemaEmitter_isBytesKeptRaw(type) {
|
|
374
|
-
const program = this.emitter.getProgram();
|
|
375
|
-
return (type.kind === "Scalar" && type.name === "bytes" && getEncode(program, type) === undefined);
|
|
376
|
-
}, _OpenAPI3SchemaEmitter_enumSchema = function _OpenAPI3SchemaEmitter_enumSchema(en) {
|
|
377
|
-
var _a;
|
|
378
|
-
const program = this.emitter.getProgram();
|
|
379
|
-
if (en.members.size === 0) {
|
|
380
|
-
reportDiagnostic(program, { code: "empty-enum", target: en });
|
|
381
|
-
return {};
|
|
382
|
-
}
|
|
383
|
-
const enumTypes = new Set();
|
|
384
|
-
const enumValues = new Set();
|
|
385
|
-
for (const member of en.members.values()) {
|
|
386
|
-
enumTypes.add(typeof member.value === "number" ? "number" : "string");
|
|
387
|
-
enumValues.add((_a = member.value) !== null && _a !== void 0 ? _a : member.name);
|
|
388
|
-
}
|
|
389
|
-
if (enumTypes.size > 1) {
|
|
390
|
-
reportDiagnostic(program, { code: "enum-unique-type", target: en });
|
|
391
|
-
}
|
|
392
|
-
const schema = { type: enumTypes.values().next().value, enum: [...enumValues] };
|
|
393
|
-
return __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyConstraints).call(this, en, schema);
|
|
394
|
-
}, _OpenAPI3SchemaEmitter_unionSchema = function _OpenAPI3SchemaEmitter_unionSchema(union) {
|
|
395
|
-
const program = this.emitter.getProgram();
|
|
396
|
-
if (union.variants.size === 0) {
|
|
397
|
-
reportDiagnostic(program, { code: "empty-union", target: union });
|
|
398
|
-
return new ObjectBuilder({});
|
|
399
|
-
}
|
|
400
|
-
const variants = Array.from(union.variants.values());
|
|
401
|
-
const literalVariantEnumByType = {};
|
|
402
|
-
const ofType = getOneOf(program, union) ? "oneOf" : "anyOf";
|
|
403
|
-
const schemaMembers = [];
|
|
404
|
-
let nullable = false;
|
|
405
|
-
const discriminator = getDiscriminator(program, union);
|
|
406
|
-
const isMultipart = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getContentType).call(this).startsWith("multipart/");
|
|
407
|
-
for (const variant of variants) {
|
|
408
|
-
if (isNullType(variant.type)) {
|
|
409
|
-
nullable = true;
|
|
410
|
-
continue;
|
|
411
|
-
}
|
|
412
|
-
if (isMultipart && __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_isBytesKeptRaw).call(this, variant.type)) {
|
|
413
|
-
schemaMembers.push({ schema: { type: "string", format: "binary" }, type: variant.type });
|
|
414
|
-
continue;
|
|
415
|
-
}
|
|
416
|
-
if (isLiteralType(variant.type)) {
|
|
417
|
-
if (!literalVariantEnumByType[variant.type.kind]) {
|
|
418
|
-
const enumValue = [variant.type.value];
|
|
419
|
-
literalVariantEnumByType[variant.type.kind] = enumValue;
|
|
420
|
-
schemaMembers.push({
|
|
421
|
-
schema: { type: literalType(variant.type), enum: enumValue },
|
|
422
|
-
type: null,
|
|
423
|
-
});
|
|
424
|
-
}
|
|
425
|
-
else {
|
|
426
|
-
literalVariantEnumByType[variant.type.kind].push(variant.type.value);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
else {
|
|
430
|
-
const enumSchema = this.emitter.emitTypeReference(variant.type, {
|
|
431
|
-
referenceContext: isMultipart ? { contentType: "application/json" } : {},
|
|
432
|
-
});
|
|
433
|
-
compilerAssert(enumSchema.kind === "code", "Unexpected enum schema. Should be kind: code");
|
|
434
|
-
schemaMembers.push({ schema: enumSchema.value, type: variant.type });
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
if (schemaMembers.length === 0) {
|
|
438
|
-
if (nullable) {
|
|
439
|
-
// This union is equivalent to just `null` but OA3 has no way to specify
|
|
440
|
-
// null as a value, so we throw an error.
|
|
441
|
-
reportDiagnostic(program, { code: "union-null", target: union });
|
|
442
|
-
return new ObjectBuilder({});
|
|
443
|
-
}
|
|
444
|
-
else {
|
|
445
|
-
// completely empty union can maybe only happen with bugs?
|
|
446
|
-
compilerAssert(false, "Attempting to emit an empty union");
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
if (schemaMembers.length === 1) {
|
|
450
|
-
// we can just return the single schema member after applying nullable
|
|
451
|
-
const schema = schemaMembers[0].schema;
|
|
452
|
-
const type = schemaMembers[0].type;
|
|
453
|
-
const additionalProps = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyConstraints).call(this, union, {});
|
|
454
|
-
if (nullable) {
|
|
455
|
-
additionalProps.nullable = true;
|
|
456
|
-
}
|
|
457
|
-
if (Object.keys(additionalProps).length === 0) {
|
|
458
|
-
return new ObjectBuilder(schema);
|
|
459
|
-
}
|
|
460
|
-
else {
|
|
461
|
-
if ((schema instanceof Placeholder || "$ref" in schema) &&
|
|
462
|
-
!(type && shouldInline(program, type))) {
|
|
463
|
-
if (type && type.kind === "Model") {
|
|
464
|
-
return new ObjectBuilder({
|
|
465
|
-
type: "object",
|
|
466
|
-
allOf: B.array([schema]),
|
|
467
|
-
...additionalProps,
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
else {
|
|
471
|
-
return new ObjectBuilder({ oneOf: B.array([schema]), ...additionalProps });
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
else {
|
|
475
|
-
const merged = new ObjectBuilder(schema);
|
|
476
|
-
for (const [key, value] of Object.entries(additionalProps)) {
|
|
477
|
-
merged.set(key, value);
|
|
478
|
-
}
|
|
479
|
-
return merged;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
const schema = {
|
|
484
|
-
[ofType]: schemaMembers.map((m) => m.schema),
|
|
485
|
-
};
|
|
486
|
-
if (nullable) {
|
|
487
|
-
schema.nullable = true;
|
|
488
|
-
}
|
|
489
|
-
if (discriminator) {
|
|
490
|
-
// the decorator validates that all the variants will be a model type
|
|
491
|
-
// with the discriminator field present.
|
|
492
|
-
schema.discriminator = { ...discriminator };
|
|
493
|
-
// Diagnostic already reported in compiler for unions
|
|
494
|
-
const discriminatedUnion = ignoreDiagnostics(getDiscriminatedUnion(union, discriminator));
|
|
495
|
-
if (discriminatedUnion.variants.size > 0) {
|
|
496
|
-
schema.discriminator.mapping = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getDiscriminatorMapping).call(this, discriminatedUnion);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
return __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyConstraints).call(this, union, schema);
|
|
500
|
-
}, _OpenAPI3SchemaEmitter_getDiscriminatorMapping = function _OpenAPI3SchemaEmitter_getDiscriminatorMapping(union) {
|
|
501
|
-
const mapping = {};
|
|
502
|
-
for (const [key, model] of union.variants.entries()) {
|
|
503
|
-
const ref = this.emitter.emitTypeReference(model);
|
|
504
|
-
compilerAssert(ref.kind === "code", "Unexpected ref schema. Should be kind: code");
|
|
505
|
-
mapping[key] = ref.value.$ref;
|
|
506
|
-
}
|
|
507
|
-
return mapping;
|
|
508
|
-
}, _OpenAPI3SchemaEmitter_attachExtensions = function _OpenAPI3SchemaEmitter_attachExtensions(program, type, emitObject) {
|
|
509
|
-
// Attach any OpenAPI extensions
|
|
510
|
-
const extensions = getExtensions(program, type);
|
|
511
|
-
if (extensions) {
|
|
512
|
-
for (const key of extensions.keys()) {
|
|
513
|
-
emitObject[key] = extensions.get(key);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}, _OpenAPI3SchemaEmitter_getSchemaForScalar = function _OpenAPI3SchemaEmitter_getSchemaForScalar(scalar) {
|
|
517
|
-
let result = {};
|
|
518
|
-
const isStd = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_isStdType).call(this, scalar);
|
|
519
|
-
if (isStd) {
|
|
520
|
-
result = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getSchemaForStdScalars).call(this, scalar);
|
|
521
|
-
}
|
|
522
|
-
else if (scalar.baseScalar) {
|
|
523
|
-
result = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getSchemaForScalar).call(this, scalar.baseScalar);
|
|
524
|
-
}
|
|
525
|
-
const withDecorators = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyEncoding).call(this, scalar, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyConstraints).call(this, scalar, result));
|
|
526
|
-
if (isStd) {
|
|
527
|
-
// Standard types are going to be inlined in the spec and we don't want the description of the scalar to show up
|
|
528
|
-
delete withDecorators.description;
|
|
529
|
-
}
|
|
530
|
-
return withDecorators;
|
|
531
|
-
}, _OpenAPI3SchemaEmitter_getSchemaForStdScalars = function _OpenAPI3SchemaEmitter_getSchemaForStdScalars(scalar) {
|
|
532
|
-
switch (scalar.name) {
|
|
533
|
-
case "bytes":
|
|
534
|
-
return { type: "string", format: "byte" };
|
|
535
|
-
case "numeric":
|
|
536
|
-
return { type: "number" };
|
|
537
|
-
case "integer":
|
|
538
|
-
return { type: "integer" };
|
|
539
|
-
case "int8":
|
|
540
|
-
return { type: "integer", format: "int8" };
|
|
541
|
-
case "int16":
|
|
542
|
-
return { type: "integer", format: "int16" };
|
|
543
|
-
case "int32":
|
|
544
|
-
return { type: "integer", format: "int32" };
|
|
545
|
-
case "int64":
|
|
546
|
-
return { type: "integer", format: "int64" };
|
|
547
|
-
case "safeint":
|
|
548
|
-
switch (__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_options, "f").safeintStrategy) {
|
|
549
|
-
case "double-int":
|
|
550
|
-
return { type: "integer", format: "double-int" };
|
|
551
|
-
case "int64":
|
|
552
|
-
default:
|
|
553
|
-
return { type: "integer", format: "int64" };
|
|
554
|
-
}
|
|
555
|
-
case "uint8":
|
|
556
|
-
return { type: "integer", format: "uint8" };
|
|
557
|
-
case "uint16":
|
|
558
|
-
return { type: "integer", format: "uint16" };
|
|
559
|
-
case "uint32":
|
|
560
|
-
return { type: "integer", format: "uint32" };
|
|
561
|
-
case "uint64":
|
|
562
|
-
return { type: "integer", format: "uint64" };
|
|
563
|
-
case "float":
|
|
564
|
-
return { type: "number" };
|
|
565
|
-
case "float64":
|
|
566
|
-
return { type: "number", format: "double" };
|
|
567
|
-
case "float32":
|
|
568
|
-
return { type: "number", format: "float" };
|
|
569
|
-
case "decimal":
|
|
570
|
-
return { type: "number", format: "decimal" };
|
|
571
|
-
case "decimal128":
|
|
572
|
-
return { type: "number", format: "decimal128" };
|
|
573
|
-
case "string":
|
|
574
|
-
return { type: "string" };
|
|
575
|
-
case "boolean":
|
|
576
|
-
return { type: "boolean" };
|
|
577
|
-
case "plainDate":
|
|
578
|
-
return { type: "string", format: "date" };
|
|
579
|
-
case "utcDateTime":
|
|
580
|
-
case "offsetDateTime":
|
|
581
|
-
return { type: "string", format: "date-time" };
|
|
582
|
-
case "plainTime":
|
|
583
|
-
return { type: "string", format: "time" };
|
|
584
|
-
case "duration":
|
|
585
|
-
return { type: "string", format: "duration" };
|
|
586
|
-
case "url":
|
|
587
|
-
return { type: "string", format: "uri" };
|
|
588
|
-
default:
|
|
589
|
-
const _assertNever = scalar.name;
|
|
590
|
-
return {};
|
|
591
|
-
}
|
|
592
|
-
}, _OpenAPI3SchemaEmitter_applyConstraints = function _OpenAPI3SchemaEmitter_applyConstraints(type, original) {
|
|
593
|
-
const schema = new ObjectBuilder(original);
|
|
594
|
-
const program = this.emitter.getProgram();
|
|
595
|
-
const applyConstraint = (fn, key) => {
|
|
596
|
-
const value = fn(program, type);
|
|
597
|
-
if (value !== undefined) {
|
|
598
|
-
schema[key] = value;
|
|
599
|
-
}
|
|
600
|
-
};
|
|
601
|
-
applyConstraint(getMinLength, "minLength");
|
|
602
|
-
applyConstraint(getMaxLength, "maxLength");
|
|
603
|
-
applyConstraint(getMinValue, "minimum");
|
|
604
|
-
applyConstraint(getMaxValue, "maximum");
|
|
605
|
-
applyConstraint(getPattern, "pattern");
|
|
606
|
-
applyConstraint(getMinItems, "minItems");
|
|
607
|
-
applyConstraint(getMaxItems, "maxItems");
|
|
608
|
-
const minValueExclusive = getMinValueExclusive(program, type);
|
|
609
|
-
if (minValueExclusive !== undefined) {
|
|
610
|
-
schema.minimum = minValueExclusive;
|
|
611
|
-
schema.exclusiveMinimum = true;
|
|
612
|
-
}
|
|
613
|
-
const maxValueExclusive = getMaxValueExclusive(program, type);
|
|
614
|
-
if (maxValueExclusive !== undefined) {
|
|
615
|
-
schema.maximum = maxValueExclusive;
|
|
616
|
-
schema.exclusiveMaximum = true;
|
|
617
|
-
}
|
|
618
|
-
if (isSecret(program, type)) {
|
|
619
|
-
schema.format = "password";
|
|
620
|
-
}
|
|
621
|
-
// the stdlib applies a format of "url" but json schema wants "uri",
|
|
622
|
-
// so ignore this format if it's the built-in type.
|
|
623
|
-
if (!__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_isStdType).call(this, type) || type.name !== "url") {
|
|
624
|
-
applyConstraint(getFormat, "format");
|
|
625
|
-
}
|
|
626
|
-
applyConstraint(getDoc, "description");
|
|
627
|
-
applyConstraint(getSummary, "title");
|
|
628
|
-
applyConstraint((p, t) => (getDeprecated(p, t) !== undefined ? true : undefined), "deprecated");
|
|
629
|
-
__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_attachExtensions).call(this, program, type, schema);
|
|
630
|
-
const values = getKnownValues(program, type);
|
|
631
|
-
if (values) {
|
|
632
|
-
return new ObjectBuilder({
|
|
633
|
-
oneOf: [schema, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_enumSchema).call(this, values)],
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
|
-
return new ObjectBuilder(schema);
|
|
637
|
-
}, _OpenAPI3SchemaEmitter_inlineType = function _OpenAPI3SchemaEmitter_inlineType(type, schema) {
|
|
638
|
-
if (__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_options, "f").includeXTypeSpecName !== "never") {
|
|
639
|
-
schema.set("x-typespec-name", getTypeName(type, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_typeNameOptions).call(this)));
|
|
640
|
-
}
|
|
641
|
-
return schema;
|
|
642
|
-
}, _OpenAPI3SchemaEmitter_createDeclaration = function _OpenAPI3SchemaEmitter_createDeclaration(type, name, schema) {
|
|
643
|
-
const refUrl = getRef(this.emitter.getProgram(), type);
|
|
644
|
-
if (refUrl) {
|
|
645
|
-
return {
|
|
646
|
-
$ref: refUrl,
|
|
647
|
-
};
|
|
648
|
-
}
|
|
649
|
-
if (shouldInline(this.emitter.getProgram(), type)) {
|
|
650
|
-
return __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_inlineType).call(this, type, schema);
|
|
651
|
-
}
|
|
652
|
-
const title = getSummary(this.emitter.getProgram(), type);
|
|
653
|
-
if (title) {
|
|
654
|
-
schema.set("title", title);
|
|
655
|
-
}
|
|
656
|
-
const usage = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_visibilityUsage, "f").getUsage(type);
|
|
657
|
-
const shouldAddSuffix = usage !== undefined && usage.size > 1;
|
|
658
|
-
const visibility = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getVisibilityContext).call(this);
|
|
659
|
-
const fullName = name + (shouldAddSuffix ? getVisibilitySuffix(visibility, Visibility.Read) : "");
|
|
660
|
-
const decl = this.emitter.result.declaration(fullName, schema);
|
|
661
|
-
checkDuplicateTypeName(this.emitter.getProgram(), type, fullName, Object.fromEntries(decl.scope.declarations.map((x) => [x.name, true])));
|
|
662
|
-
return decl;
|
|
663
|
-
}, _OpenAPI3SchemaEmitter_isStdType = function _OpenAPI3SchemaEmitter_isStdType(type) {
|
|
664
|
-
return this.emitter.getProgram().checker.isStdType(type);
|
|
665
|
-
}, _OpenAPI3SchemaEmitter_applyEncoding = function _OpenAPI3SchemaEmitter_applyEncoding(typespecType, target) {
|
|
666
|
-
const encodeData = getEncode(this.emitter.getProgram(), typespecType);
|
|
667
|
-
if (encodeData) {
|
|
668
|
-
const newTarget = new ObjectBuilder(target);
|
|
669
|
-
const newType = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getSchemaForStdScalars).call(this, encodeData.type);
|
|
670
|
-
newTarget.type = newType.type;
|
|
671
|
-
// If the target already has a format it takes priority. (e.g. int32)
|
|
672
|
-
newTarget.format = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_mergeFormatAndEncoding).call(this, newTarget.format, encodeData.encoding, newType.format);
|
|
673
|
-
return newTarget;
|
|
674
|
-
}
|
|
675
|
-
const result = new ObjectBuilder(target);
|
|
676
|
-
return result;
|
|
677
|
-
}, _OpenAPI3SchemaEmitter_mergeFormatAndEncoding = function _OpenAPI3SchemaEmitter_mergeFormatAndEncoding(format, encoding, encodeAsFormat) {
|
|
678
|
-
switch (format) {
|
|
679
|
-
case undefined:
|
|
680
|
-
return encodeAsFormat !== null && encodeAsFormat !== void 0 ? encodeAsFormat : encoding;
|
|
681
|
-
case "date-time":
|
|
682
|
-
switch (encoding) {
|
|
683
|
-
case "rfc3339":
|
|
684
|
-
return "date-time";
|
|
685
|
-
case "unixTimestamp":
|
|
686
|
-
return "unixtime";
|
|
687
|
-
case "rfc7231":
|
|
688
|
-
return "http-date";
|
|
689
|
-
default:
|
|
690
|
-
return encoding;
|
|
691
|
-
}
|
|
692
|
-
case "duration":
|
|
693
|
-
switch (encoding) {
|
|
694
|
-
case "ISO8601":
|
|
695
|
-
return "duration";
|
|
696
|
-
default:
|
|
697
|
-
return encodeAsFormat !== null && encodeAsFormat !== void 0 ? encodeAsFormat : encoding;
|
|
698
|
-
}
|
|
699
|
-
default:
|
|
700
|
-
return encodeAsFormat !== null && encodeAsFormat !== void 0 ? encodeAsFormat : encoding;
|
|
701
|
-
}
|
|
702
|
-
};
|
|
703
707
|
function isLiteralType(type) {
|
|
704
708
|
return type.kind === "Boolean" || type.kind === "String" || type.kind === "Number";
|
|
705
709
|
}
|
|
@@ -714,10 +718,9 @@ function literalType(type) {
|
|
|
714
718
|
}
|
|
715
719
|
}
|
|
716
720
|
function includeDerivedModel(model) {
|
|
717
|
-
var _a, _b;
|
|
718
721
|
return (!isTemplateDeclaration(model) &&
|
|
719
|
-
(
|
|
720
|
-
|
|
722
|
+
(model.templateMapper?.args === undefined ||
|
|
723
|
+
model.templateMapper.args?.length === 0 ||
|
|
721
724
|
model.derivedModels.length > 0));
|
|
722
725
|
}
|
|
723
726
|
const B = {
|
|
@@ -737,7 +740,6 @@ const B = {
|
|
|
737
740
|
},
|
|
738
741
|
};
|
|
739
742
|
export function getDefaultValue(program, type, defaultType) {
|
|
740
|
-
var _a;
|
|
741
743
|
switch (defaultType.kind) {
|
|
742
744
|
case "String":
|
|
743
745
|
return defaultType.value;
|
|
@@ -762,7 +764,7 @@ export function getDefaultValue(program, type, defaultType) {
|
|
|
762
764
|
target: defaultType,
|
|
763
765
|
});
|
|
764
766
|
case "EnumMember":
|
|
765
|
-
return
|
|
767
|
+
return defaultType.value ?? defaultType.name;
|
|
766
768
|
case "UnionVariant":
|
|
767
769
|
return getDefaultValue(program, type, defaultType.type);
|
|
768
770
|
default:
|