@typespec/openapi3
Advanced tools
Comparing version 0.56.0-dev.2 to 0.56.0-dev.3
@@ -61,6 +61,5 @@ import { compilerAssert, createDiagnosticCollector, emitFile, getAllTags, getAnyExtensionFromPath, getDoc, getEncode, getFormat, getKnownValues, getMaxItems, getMaxLength, getMaxValue, getMaxValueExclusive, getMinItems, getMinLength, getMinValue, getMinValueExclusive, getNamespaceFullName, getPattern, getService, getSummary, ignoreDiagnostics, interpolatePath, isDeprecated, isGlobalNamespace, isNeverType, isSecret, isVoidType, listServices, navigateTypesInNamespace, projectProgram, resolvePath, } from "@typespec/compiler"; | ||
export function resolveOptions(context) { | ||
var _a, _b; | ||
const resolvedOptions = { ...defaultOptions, ...context.options }; | ||
const fileType = (_a = resolvedOptions["file-type"]) !== null && _a !== void 0 ? _a : findFileTypeFromFilename(resolvedOptions["output-file"]); | ||
const outputFile = (_b = resolvedOptions["output-file"]) !== null && _b !== void 0 ? _b : `openapi.{service-name}.{version}.${fileType}`; | ||
const fileType = resolvedOptions["file-type"] ?? findFileTypeFromFilename(resolvedOptions["output-file"]); | ||
const outputFile = resolvedOptions["output-file"] ?? `openapi.{service-name}.{version}.${fileType}`; | ||
return { | ||
@@ -141,3 +140,2 @@ fileType, | ||
function initializeEmitter(service, allHttpAuthentications, defaultAuth, version) { | ||
var _a; | ||
currentService = service; | ||
@@ -162,3 +160,3 @@ metadataInfo = createMetadataInfo(program, { | ||
...info, | ||
version: (_a = version !== null && version !== void 0 ? version : info === null || info === void 0 ? void 0 : info.version) !== null && _a !== void 0 ? _a : "0.0.0", | ||
version: version ?? info?.version ?? "0.0.0", | ||
}, | ||
@@ -190,3 +188,2 @@ externalDocs: getExternalDocs(program, service.type), | ||
function isValidServerVariableType(program, type) { | ||
var _a; | ||
switch (type.kind) { | ||
@@ -196,3 +193,3 @@ case "String": | ||
case "Scalar": | ||
return ignoreDiagnostics(program.checker.isTypeAssignableTo((_a = type.projectionBase) !== null && _a !== void 0 ? _a : type, program.checker.getStdType("string"), type)); | ||
return ignoreDiagnostics(program.checker.isTypeAssignableTo(type.projectionBase ?? type, program.checker.getStdType("string"), type)); | ||
case "Enum": | ||
@@ -730,5 +727,4 @@ for (const member of type.members.values()) { | ||
function emitResponseObject(statusCode, response) { | ||
var _a, _b; | ||
const openApiResponse = (_a = currentEndpoint.responses[statusCode]) !== null && _a !== void 0 ? _a : { | ||
description: (_b = response.description) !== null && _b !== void 0 ? _b : getResponseDescriptionForStatusCode(statusCode), | ||
const openApiResponse = currentEndpoint.responses[statusCode] ?? { | ||
description: response.description ?? getResponseDescriptionForStatusCode(statusCode), | ||
}; | ||
@@ -740,6 +736,5 @@ emitResponseHeaders(openApiResponse, response.responses, response.type); | ||
function emitResponseHeaders(obj, responses, target) { | ||
var _a; | ||
for (const data of responses) { | ||
if (data.headers && Object.keys(data.headers).length > 0) { | ||
(_a = obj.headers) !== null && _a !== void 0 ? _a : (obj.headers = {}); | ||
obj.headers ??= {}; | ||
// OpenAPI can't represent different headers per content type. | ||
@@ -766,4 +761,3 @@ // So we merge headers here, and report any duplicates unless they are identical | ||
function emitResponseContent(obj, responses, schemaMap = undefined) { | ||
var _a; | ||
schemaMap !== null && schemaMap !== void 0 ? schemaMap : (schemaMap = new Map()); | ||
schemaMap ??= new Map(); | ||
for (const data of responses) { | ||
@@ -773,3 +767,3 @@ if (data.body === undefined) { | ||
} | ||
(_a = obj.content) !== null && _a !== void 0 ? _a : (obj.content = {}); | ||
obj.content ??= {}; | ||
for (const contentType of data.body.contentTypes) { | ||
@@ -800,7 +794,6 @@ const isBinary = isBinaryPayload(data.body.type, contentType); | ||
function getResponseDescriptionForStatusCode(statusCode) { | ||
var _a; | ||
if (statusCode === "default") { | ||
return "An unexpected error response."; | ||
} | ||
return (_a = getStatusCodeDescription(statusCode)) !== null && _a !== void 0 ? _a : "unknown"; | ||
return getStatusCodeDescription(statusCode) ?? "unknown"; | ||
} | ||
@@ -856,3 +849,3 @@ function getResponseHeader(prop) { | ||
const effectiveType = metadataInfo.getEffectivePayloadType(type, visibility); | ||
return callSchemaEmitter(effectiveType, visibility, multipart !== null && multipart !== void 0 ? multipart : "application/json"); | ||
return callSchemaEmitter(effectiveType, visibility, multipart ?? "application/json"); | ||
} | ||
@@ -1222,3 +1215,3 @@ function getParamPlaceholder(property) { | ||
case undefined: | ||
return encodeAsFormat !== null && encodeAsFormat !== void 0 ? encodeAsFormat : encoding; | ||
return encodeAsFormat ?? encoding; | ||
case "date-time": | ||
@@ -1240,6 +1233,6 @@ switch (encoding) { | ||
default: | ||
return encodeAsFormat !== null && encodeAsFormat !== void 0 ? encodeAsFormat : encoding; | ||
return encodeAsFormat ?? encoding; | ||
} | ||
default: | ||
return encodeAsFormat !== null && encodeAsFormat !== void 0 ? encodeAsFormat : encoding; | ||
return encodeAsFormat ?? encoding; | ||
} | ||
@@ -1306,3 +1299,3 @@ } | ||
refreshUrl: flow.refreshUrl, | ||
scopes: Object.fromEntries(flow.scopes.map((x) => { var _a; return [x.value, (_a = x.description) !== null && _a !== void 0 ? _a : ""]; })), | ||
scopes: Object.fromEntries(flow.scopes.map((x) => [x.value, x.description ?? ""])), | ||
}; | ||
@@ -1359,8 +1352,7 @@ } | ||
function sortOpenAPIDocument(doc) { | ||
var _a, _b; | ||
doc.paths = sortObjectByKeys(doc.paths); | ||
if ((_a = doc.components) === null || _a === void 0 ? void 0 : _a.schemas) { | ||
if (doc.components?.schemas) { | ||
doc.components.schemas = sortObjectByKeys(doc.components.schemas); | ||
} | ||
if ((_b = doc.components) === null || _b === void 0 ? void 0 : _b.parameters) { | ||
if (doc.components?.parameters) { | ||
doc.components.parameters = sortObjectByKeys(doc.components.parameters); | ||
@@ -1367,0 +1359,0 @@ } |
@@ -1,13 +0,1 @@ | ||
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { | ||
if (kind === "m") throw new TypeError("Private method is not writable"); | ||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); | ||
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"); | ||
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; | ||
}; | ||
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { | ||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); | ||
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"); | ||
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); | ||
}; | ||
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; | ||
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"; | ||
@@ -23,11 +11,10 @@ import { ArrayBuilder, ObjectBuilder, Placeholder, TypeEmitter, } from "@typespec/compiler/emitter-framework"; | ||
export class OpenAPI3SchemaEmitter extends TypeEmitter { | ||
#metadataInfo; | ||
#visibilityUsage; | ||
#options; | ||
constructor(emitter, metadataInfo, visibilityUsage, options) { | ||
super(emitter); | ||
_OpenAPI3SchemaEmitter_instances.add(this); | ||
_OpenAPI3SchemaEmitter_metadataInfo.set(this, void 0); | ||
_OpenAPI3SchemaEmitter_visibilityUsage.set(this, void 0); | ||
_OpenAPI3SchemaEmitter_options.set(this, void 0); | ||
__classPrivateFieldSet(this, _OpenAPI3SchemaEmitter_metadataInfo, metadataInfo, "f"); | ||
__classPrivateFieldSet(this, _OpenAPI3SchemaEmitter_visibilityUsage, visibilityUsage, "f"); | ||
__classPrivateFieldSet(this, _OpenAPI3SchemaEmitter_options, options, "f"); | ||
this.#metadataInfo = metadataInfo; | ||
this.#visibilityUsage = visibilityUsage; | ||
this.#options = options; | ||
} | ||
@@ -50,8 +37,8 @@ modelDeclarationReferenceContext(model, name) { | ||
reduceContext(type) { | ||
const visibility = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getVisibilityContext).call(this); | ||
const visibility = this.#getVisibilityContext(); | ||
const patch = {}; | ||
if (visibility !== Visibility.Read && !__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_metadataInfo, "f").isTransformed(type, visibility)) { | ||
if (visibility !== Visibility.Read && !this.#metadataInfo.isTransformed(type, visibility)) { | ||
patch.visibility = Visibility.Read; | ||
} | ||
const contentType = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getContentType).call(this); | ||
const contentType = this.#getContentType(); | ||
if (contentType === "application/json") { | ||
@@ -64,6 +51,6 @@ patch.contentType = undefined; | ||
const program = this.emitter.getProgram(); | ||
const visibility = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getVisibilityContext).call(this); | ||
const visibility = this.#getVisibilityContext(); | ||
const schema = new ObjectBuilder({ | ||
type: "object", | ||
required: __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_requiredModelProperties).call(this, model, visibility), | ||
required: this.#requiredModelProperties(model, visibility), | ||
properties: this.emitter.emitModelProperties(model), | ||
@@ -84,15 +71,37 @@ }); | ||
if (union.variants.size > 0) { | ||
openApiDiscriminator.mapping = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getDiscriminatorMapping).call(this, union); | ||
openApiDiscriminator.mapping = this.#getDiscriminatorMapping(union); | ||
} | ||
schema.discriminator = openApiDiscriminator; | ||
} | ||
__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyExternalDocs).call(this, model, schema); | ||
this.#applyExternalDocs(model, schema); | ||
if (model.baseModel) { | ||
schema.set("allOf", B.array([this.emitter.emitTypeReference(model.baseModel)])); | ||
} | ||
const baseName = getOpenAPITypeName(program, model, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_typeNameOptions).call(this)); | ||
const isMultipart = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getContentType).call(this).startsWith("multipart/"); | ||
const baseName = getOpenAPITypeName(program, model, this.#typeNameOptions()); | ||
const isMultipart = this.#getContentType().startsWith("multipart/"); | ||
const name = isMultipart ? baseName + "MultiPart" : baseName; | ||
return __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_createDeclaration).call(this, model, name, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyConstraints).call(this, model, schema)); | ||
return this.#createDeclaration(model, name, this.#applyConstraints(model, schema)); | ||
} | ||
#applyExternalDocs(typespecType, target) { | ||
const externalDocs = getExternalDocs(this.emitter.getProgram(), typespecType); | ||
if (externalDocs) { | ||
target.externalDocs = externalDocs; | ||
} | ||
} | ||
#typeNameOptions() { | ||
const serviceNamespaceName = this.emitter.getContext().serviceNamespaceName; | ||
return { | ||
// shorten type names by removing TypeSpec and service namespace | ||
namespaceFilter(ns) { | ||
const name = getNamespaceFullName(ns); | ||
return name !== serviceNamespaceName; | ||
}, | ||
}; | ||
} | ||
#getVisibilityContext() { | ||
return this.emitter.getContext().visibility ?? Visibility.Read; | ||
} | ||
#getContentType() { | ||
return this.emitter.getContext().contentType ?? "application/json"; | ||
} | ||
modelLiteral(model) { | ||
@@ -102,3 +111,3 @@ const schema = new ObjectBuilder({ | ||
properties: this.emitter.emitModelProperties(model), | ||
required: __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_requiredModelProperties).call(this, model, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getVisibilityContext).call(this)), | ||
required: this.#requiredModelProperties(model, this.#getVisibilityContext()), | ||
}); | ||
@@ -111,3 +120,3 @@ if (model.indexer) { | ||
modelInstantiation(model, name) { | ||
name = name !== null && name !== void 0 ? name : getOpenAPITypeName(this.emitter.getProgram(), model, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_typeNameOptions).call(this)); | ||
name = name ?? getOpenAPITypeName(this.emitter.getProgram(), model, this.#typeNameOptions()); | ||
if (!name) { | ||
@@ -123,11 +132,11 @@ return this.modelLiteral(model); | ||
}); | ||
return __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_createDeclaration).call(this, array, name, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyConstraints).call(this, array, schema)); | ||
return this.#createDeclaration(array, name, this.#applyConstraints(array, schema)); | ||
} | ||
arrayDeclarationReferenceContext(array, name, elementType) { | ||
return { | ||
visibility: __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getVisibilityContext).call(this) | Visibility.Item, | ||
visibility: this.#getVisibilityContext() | Visibility.Item, | ||
}; | ||
} | ||
arrayLiteral(array, elementType) { | ||
return __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_inlineType).call(this, array, new ObjectBuilder({ | ||
return this.#inlineType(array, new ObjectBuilder({ | ||
type: "array", | ||
@@ -139,5 +148,22 @@ items: this.emitter.emitTypeReference(elementType), | ||
return { | ||
visibility: __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getVisibilityContext).call(this) | Visibility.Item, | ||
visibility: this.#getVisibilityContext() | Visibility.Item, | ||
}; | ||
} | ||
#requiredModelProperties(model, visibility) { | ||
const requiredProps = []; | ||
for (const prop of model.properties.values()) { | ||
if (isNeverType(prop.type)) { | ||
// If the property has a type of 'never', don't include it in the schema | ||
continue; | ||
} | ||
if (!this.#metadataInfo.isPayloadProperty(prop, visibility)) { | ||
continue; | ||
} | ||
if (!this.#metadataInfo.isOptional(prop, visibility)) { | ||
const encodedName = resolveEncodedName(this.emitter.getProgram(), prop, this.#getContentType()); | ||
requiredProps.push(encodedName); | ||
} | ||
} | ||
return requiredProps.length > 0 ? requiredProps : undefined; | ||
} | ||
modelProperties(model) { | ||
@@ -147,3 +173,3 @@ const program = this.emitter.getProgram(); | ||
const visibility = this.emitter.getContext().visibility; | ||
const contentType = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getContentType).call(this); | ||
const contentType = this.#getContentType(); | ||
for (const prop of model.properties.values()) { | ||
@@ -154,3 +180,3 @@ if (isNeverType(prop.type)) { | ||
} | ||
if (!__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_metadataInfo, "f").isPayloadProperty(prop, visibility)) { | ||
if (!this.#metadataInfo.isPayloadProperty(prop, visibility)) { | ||
continue; | ||
@@ -174,7 +200,11 @@ } | ||
} | ||
#isBytesKeptRaw(type) { | ||
const program = this.emitter.getProgram(); | ||
return (type.kind === "Scalar" && type.name === "bytes" && getEncode(program, type) === undefined); | ||
} | ||
modelPropertyLiteral(prop) { | ||
const program = this.emitter.getProgram(); | ||
const isMultipart = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getContentType).call(this).startsWith("multipart/"); | ||
const isMultipart = this.#getContentType().startsWith("multipart/"); | ||
if (isMultipart) { | ||
if (__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_isBytesKeptRaw).call(this, prop.type) && getEncode(program, prop) === undefined) { | ||
if (this.#isBytesKeptRaw(prop.type) && getEncode(program, prop) === undefined) { | ||
return { type: "string", format: "binary" }; | ||
@@ -184,3 +214,3 @@ } | ||
isArrayModelType(program, prop.type) && | ||
__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_isBytesKeptRaw).call(this, prop.type.indexer.value)) { | ||
this.#isBytesKeptRaw(prop.type.indexer.value)) { | ||
return { type: "array", items: { type: "string", format: "binary" } }; | ||
@@ -192,3 +222,3 @@ } | ||
(prop.type.kind !== "Union" || | ||
![...prop.type.variants.values()].some((x) => __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_isBytesKeptRaw).call(this, x.type))) | ||
![...prop.type.variants.values()].some((x) => this.#isBytesKeptRaw(x.type))) | ||
? { contentType: "application/json" } | ||
@@ -201,5 +231,5 @@ : {}, | ||
const isRef = refSchema.value instanceof Placeholder || "$ref" in refSchema.value; | ||
const schema = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyEncoding).call(this, prop, refSchema.value); | ||
const schema = this.#applyEncoding(prop, refSchema.value); | ||
// Apply decorators on the property to the type's schema | ||
const additionalProps = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyConstraints).call(this, prop, {}); | ||
const additionalProps = this.#applyConstraints(prop, {}); | ||
if (prop.default) { | ||
@@ -212,3 +242,3 @@ additionalProps.default = getDefaultValue(program, prop.type, prop.default); | ||
// Attach any additional OpenAPI extensions | ||
__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_attachExtensions).call(this, program, prop, additionalProps); | ||
this.#attachExtensions(program, prop, additionalProps); | ||
if (schema && isRef && !(prop.type.kind === "Model" && isArrayModelType(program, prop.type))) { | ||
@@ -257,5 +287,23 @@ if (Object.keys(additionalProps).length === 0) { | ||
enumDeclaration(en, name) { | ||
const baseName = getOpenAPITypeName(this.emitter.getProgram(), en, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_typeNameOptions).call(this)); | ||
return __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_createDeclaration).call(this, en, baseName, new ObjectBuilder(__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_enumSchema).call(this, en))); | ||
const baseName = getOpenAPITypeName(this.emitter.getProgram(), en, this.#typeNameOptions()); | ||
return this.#createDeclaration(en, baseName, new ObjectBuilder(this.#enumSchema(en))); | ||
} | ||
#enumSchema(en) { | ||
const program = this.emitter.getProgram(); | ||
if (en.members.size === 0) { | ||
reportDiagnostic(program, { code: "empty-enum", target: en }); | ||
return {}; | ||
} | ||
const enumTypes = new Set(); | ||
const enumValues = new Set(); | ||
for (const member of en.members.values()) { | ||
enumTypes.add(typeof member.value === "number" ? "number" : "string"); | ||
enumValues.add(member.value ?? member.name); | ||
} | ||
if (enumTypes.size > 1) { | ||
reportDiagnostic(program, { code: "enum-unique-type", target: en }); | ||
} | ||
const schema = { type: enumTypes.values().next().value, enum: [...enumValues] }; | ||
return this.#applyConstraints(en, schema); | ||
} | ||
enumMember(member) { | ||
@@ -276,7 +324,123 @@ return this.enumMemberReference(member); | ||
unionDeclaration(union, name) { | ||
const schema = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_unionSchema).call(this, union); | ||
return __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_createDeclaration).call(this, union, name, schema); | ||
const schema = this.#unionSchema(union); | ||
return this.#createDeclaration(union, name, schema); | ||
} | ||
#unionSchema(union) { | ||
const program = this.emitter.getProgram(); | ||
if (union.variants.size === 0) { | ||
reportDiagnostic(program, { code: "empty-union", target: union }); | ||
return new ObjectBuilder({}); | ||
} | ||
const variants = Array.from(union.variants.values()); | ||
const literalVariantEnumByType = {}; | ||
const ofType = getOneOf(program, union) ? "oneOf" : "anyOf"; | ||
const schemaMembers = []; | ||
let nullable = false; | ||
const discriminator = getDiscriminator(program, union); | ||
const isMultipart = this.#getContentType().startsWith("multipart/"); | ||
for (const variant of variants) { | ||
if (isNullType(variant.type)) { | ||
nullable = true; | ||
continue; | ||
} | ||
if (isMultipart && this.#isBytesKeptRaw(variant.type)) { | ||
schemaMembers.push({ schema: { type: "string", format: "binary" }, type: variant.type }); | ||
continue; | ||
} | ||
if (isLiteralType(variant.type)) { | ||
if (!literalVariantEnumByType[variant.type.kind]) { | ||
const enumValue = [variant.type.value]; | ||
literalVariantEnumByType[variant.type.kind] = enumValue; | ||
schemaMembers.push({ | ||
schema: { type: literalType(variant.type), enum: enumValue }, | ||
type: null, | ||
}); | ||
} | ||
else { | ||
literalVariantEnumByType[variant.type.kind].push(variant.type.value); | ||
} | ||
} | ||
else { | ||
const enumSchema = this.emitter.emitTypeReference(variant.type, { | ||
referenceContext: isMultipart ? { contentType: "application/json" } : {}, | ||
}); | ||
compilerAssert(enumSchema.kind === "code", "Unexpected enum schema. Should be kind: code"); | ||
schemaMembers.push({ schema: enumSchema.value, type: variant.type }); | ||
} | ||
} | ||
if (schemaMembers.length === 0) { | ||
if (nullable) { | ||
// This union is equivalent to just `null` but OA3 has no way to specify | ||
// null as a value, so we throw an error. | ||
reportDiagnostic(program, { code: "union-null", target: union }); | ||
return new ObjectBuilder({}); | ||
} | ||
else { | ||
// completely empty union can maybe only happen with bugs? | ||
compilerAssert(false, "Attempting to emit an empty union"); | ||
} | ||
} | ||
if (schemaMembers.length === 1) { | ||
// we can just return the single schema member after applying nullable | ||
const schema = schemaMembers[0].schema; | ||
const type = schemaMembers[0].type; | ||
const additionalProps = this.#applyConstraints(union, {}); | ||
if (nullable) { | ||
additionalProps.nullable = true; | ||
} | ||
if (Object.keys(additionalProps).length === 0) { | ||
return new ObjectBuilder(schema); | ||
} | ||
else { | ||
if ((schema instanceof Placeholder || "$ref" in schema) && | ||
!(type && shouldInline(program, type))) { | ||
if (type && type.kind === "Model") { | ||
return new ObjectBuilder({ | ||
type: "object", | ||
allOf: B.array([schema]), | ||
...additionalProps, | ||
}); | ||
} | ||
else { | ||
return new ObjectBuilder({ oneOf: B.array([schema]), ...additionalProps }); | ||
} | ||
} | ||
else { | ||
const merged = new ObjectBuilder(schema); | ||
for (const [key, value] of Object.entries(additionalProps)) { | ||
merged.set(key, value); | ||
} | ||
return merged; | ||
} | ||
} | ||
} | ||
const schema = { | ||
[ofType]: schemaMembers.map((m) => m.schema), | ||
}; | ||
if (nullable) { | ||
schema.nullable = true; | ||
} | ||
if (discriminator) { | ||
// the decorator validates that all the variants will be a model type | ||
// with the discriminator field present. | ||
schema.discriminator = { ...discriminator }; | ||
// Diagnostic already reported in compiler for unions | ||
const discriminatedUnion = ignoreDiagnostics(getDiscriminatedUnion(union, discriminator)); | ||
if (discriminatedUnion.variants.size > 0) { | ||
schema.discriminator.mapping = this.#getDiscriminatorMapping(discriminatedUnion); | ||
} | ||
} | ||
return this.#applyConstraints(union, schema); | ||
} | ||
#getDiscriminatorMapping(union) { | ||
const mapping = {}; | ||
for (const [key, model] of union.variants.entries()) { | ||
const ref = this.emitter.emitTypeReference(model); | ||
compilerAssert(ref.kind === "code", "Unexpected ref schema. Should be kind: code"); | ||
mapping[key] = ref.value.$ref; | ||
} | ||
return mapping; | ||
} | ||
unionLiteral(union) { | ||
return __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_unionSchema).call(this, union); | ||
return this.#unionSchema(union); | ||
} | ||
@@ -296,2 +460,11 @@ unionVariants(union) { | ||
} | ||
#attachExtensions(program, type, emitObject) { | ||
// Attach any OpenAPI extensions | ||
const extensions = getExtensions(program, type); | ||
if (extensions) { | ||
for (const key of extensions.keys()) { | ||
emitObject[key] = extensions.get(key); | ||
} | ||
} | ||
} | ||
reference(targetDeclaration, pathUp, pathDown, commonScope) { | ||
@@ -324,10 +497,10 @@ if (targetDeclaration.value instanceof Placeholder) { | ||
scalarDeclaration(scalar, name) { | ||
const isStd = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_isStdType).call(this, scalar); | ||
const schema = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getSchemaForScalar).call(this, scalar); | ||
const baseName = getOpenAPITypeName(this.emitter.getProgram(), scalar, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_typeNameOptions).call(this)); | ||
const isStd = this.#isStdType(scalar); | ||
const schema = this.#getSchemaForScalar(scalar); | ||
const baseName = getOpenAPITypeName(this.emitter.getProgram(), scalar, this.#typeNameOptions()); | ||
// Don't create a declaration for std types | ||
return isStd ? schema : __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_createDeclaration).call(this, scalar, baseName, new ObjectBuilder(schema)); | ||
return isStd ? schema : this.#createDeclaration(scalar, baseName, new ObjectBuilder(schema)); | ||
} | ||
scalarInstantiation(scalar, name) { | ||
return __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getSchemaForScalar).call(this, scalar); | ||
return this.#getSchemaForScalar(scalar); | ||
} | ||
@@ -337,387 +510,215 @@ tupleLiteral(tuple) { | ||
} | ||
intrinsic(intrinsic, name) { | ||
switch (name) { | ||
case "unknown": | ||
return {}; | ||
case "null": | ||
return { nullable: true }; | ||
#getSchemaForScalar(scalar) { | ||
let result = {}; | ||
const isStd = this.#isStdType(scalar); | ||
if (isStd) { | ||
result = this.#getSchemaForStdScalars(scalar); | ||
} | ||
reportDiagnostic(this.emitter.getProgram(), { | ||
code: "invalid-schema", | ||
format: { type: name }, | ||
target: intrinsic, | ||
}); | ||
return {}; | ||
} | ||
programContext(program) { | ||
const sourceFile = this.emitter.createSourceFile("openapi"); | ||
return { scope: sourceFile.globalScope }; | ||
} | ||
} | ||
_OpenAPI3SchemaEmitter_metadataInfo = new WeakMap(), _OpenAPI3SchemaEmitter_visibilityUsage = new WeakMap(), _OpenAPI3SchemaEmitter_options = new WeakMap(), _OpenAPI3SchemaEmitter_instances = new WeakSet(), _OpenAPI3SchemaEmitter_applyExternalDocs = function _OpenAPI3SchemaEmitter_applyExternalDocs(typespecType, target) { | ||
const externalDocs = getExternalDocs(this.emitter.getProgram(), typespecType); | ||
if (externalDocs) { | ||
target.externalDocs = externalDocs; | ||
} | ||
}, _OpenAPI3SchemaEmitter_typeNameOptions = function _OpenAPI3SchemaEmitter_typeNameOptions() { | ||
const serviceNamespaceName = this.emitter.getContext().serviceNamespaceName; | ||
return { | ||
// shorten type names by removing TypeSpec and service namespace | ||
namespaceFilter(ns) { | ||
const name = getNamespaceFullName(ns); | ||
return name !== serviceNamespaceName; | ||
}, | ||
}; | ||
}, _OpenAPI3SchemaEmitter_getVisibilityContext = function _OpenAPI3SchemaEmitter_getVisibilityContext() { | ||
var _a; | ||
return (_a = this.emitter.getContext().visibility) !== null && _a !== void 0 ? _a : Visibility.Read; | ||
}, _OpenAPI3SchemaEmitter_getContentType = function _OpenAPI3SchemaEmitter_getContentType() { | ||
var _a; | ||
return (_a = this.emitter.getContext().contentType) !== null && _a !== void 0 ? _a : "application/json"; | ||
}, _OpenAPI3SchemaEmitter_requiredModelProperties = function _OpenAPI3SchemaEmitter_requiredModelProperties(model, visibility) { | ||
const requiredProps = []; | ||
for (const prop of model.properties.values()) { | ||
if (isNeverType(prop.type)) { | ||
// If the property has a type of 'never', don't include it in the schema | ||
continue; | ||
else if (scalar.baseScalar) { | ||
result = this.#getSchemaForScalar(scalar.baseScalar); | ||
} | ||
if (!__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_metadataInfo, "f").isPayloadProperty(prop, visibility)) { | ||
continue; | ||
const withDecorators = this.#applyEncoding(scalar, this.#applyConstraints(scalar, result)); | ||
if (isStd) { | ||
// Standard types are going to be inlined in the spec and we don't want the description of the scalar to show up | ||
delete withDecorators.description; | ||
} | ||
if (!__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_metadataInfo, "f").isOptional(prop, visibility)) { | ||
const encodedName = resolveEncodedName(this.emitter.getProgram(), prop, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getContentType).call(this)); | ||
requiredProps.push(encodedName); | ||
return withDecorators; | ||
} | ||
#getSchemaForStdScalars(scalar) { | ||
switch (scalar.name) { | ||
case "bytes": | ||
return { type: "string", format: "byte" }; | ||
case "numeric": | ||
return { type: "number" }; | ||
case "integer": | ||
return { type: "integer" }; | ||
case "int8": | ||
return { type: "integer", format: "int8" }; | ||
case "int16": | ||
return { type: "integer", format: "int16" }; | ||
case "int32": | ||
return { type: "integer", format: "int32" }; | ||
case "int64": | ||
return { type: "integer", format: "int64" }; | ||
case "safeint": | ||
switch (this.#options.safeintStrategy) { | ||
case "double-int": | ||
return { type: "integer", format: "double-int" }; | ||
case "int64": | ||
default: | ||
return { type: "integer", format: "int64" }; | ||
} | ||
case "uint8": | ||
return { type: "integer", format: "uint8" }; | ||
case "uint16": | ||
return { type: "integer", format: "uint16" }; | ||
case "uint32": | ||
return { type: "integer", format: "uint32" }; | ||
case "uint64": | ||
return { type: "integer", format: "uint64" }; | ||
case "float": | ||
return { type: "number" }; | ||
case "float64": | ||
return { type: "number", format: "double" }; | ||
case "float32": | ||
return { type: "number", format: "float" }; | ||
case "decimal": | ||
return { type: "number", format: "decimal" }; | ||
case "decimal128": | ||
return { type: "number", format: "decimal128" }; | ||
case "string": | ||
return { type: "string" }; | ||
case "boolean": | ||
return { type: "boolean" }; | ||
case "plainDate": | ||
return { type: "string", format: "date" }; | ||
case "utcDateTime": | ||
case "offsetDateTime": | ||
return { type: "string", format: "date-time" }; | ||
case "plainTime": | ||
return { type: "string", format: "time" }; | ||
case "duration": | ||
return { type: "string", format: "duration" }; | ||
case "url": | ||
return { type: "string", format: "uri" }; | ||
default: | ||
const _assertNever = scalar.name; | ||
return {}; | ||
} | ||
} | ||
return requiredProps.length > 0 ? requiredProps : undefined; | ||
}, _OpenAPI3SchemaEmitter_isBytesKeptRaw = function _OpenAPI3SchemaEmitter_isBytesKeptRaw(type) { | ||
const program = this.emitter.getProgram(); | ||
return (type.kind === "Scalar" && type.name === "bytes" && getEncode(program, type) === undefined); | ||
}, _OpenAPI3SchemaEmitter_enumSchema = function _OpenAPI3SchemaEmitter_enumSchema(en) { | ||
var _a; | ||
const program = this.emitter.getProgram(); | ||
if (en.members.size === 0) { | ||
reportDiagnostic(program, { code: "empty-enum", target: en }); | ||
return {}; | ||
} | ||
const enumTypes = new Set(); | ||
const enumValues = new Set(); | ||
for (const member of en.members.values()) { | ||
enumTypes.add(typeof member.value === "number" ? "number" : "string"); | ||
enumValues.add((_a = member.value) !== null && _a !== void 0 ? _a : member.name); | ||
} | ||
if (enumTypes.size > 1) { | ||
reportDiagnostic(program, { code: "enum-unique-type", target: en }); | ||
} | ||
const schema = { type: enumTypes.values().next().value, enum: [...enumValues] }; | ||
return __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyConstraints).call(this, en, schema); | ||
}, _OpenAPI3SchemaEmitter_unionSchema = function _OpenAPI3SchemaEmitter_unionSchema(union) { | ||
const program = this.emitter.getProgram(); | ||
if (union.variants.size === 0) { | ||
reportDiagnostic(program, { code: "empty-union", target: union }); | ||
return new ObjectBuilder({}); | ||
} | ||
const variants = Array.from(union.variants.values()); | ||
const literalVariantEnumByType = {}; | ||
const ofType = getOneOf(program, union) ? "oneOf" : "anyOf"; | ||
const schemaMembers = []; | ||
let nullable = false; | ||
const discriminator = getDiscriminator(program, union); | ||
const isMultipart = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getContentType).call(this).startsWith("multipart/"); | ||
for (const variant of variants) { | ||
if (isNullType(variant.type)) { | ||
nullable = true; | ||
continue; | ||
#applyConstraints(type, original) { | ||
const schema = new ObjectBuilder(original); | ||
const program = this.emitter.getProgram(); | ||
const applyConstraint = (fn, key) => { | ||
const value = fn(program, type); | ||
if (value !== undefined) { | ||
schema[key] = value; | ||
} | ||
}; | ||
applyConstraint(getMinLength, "minLength"); | ||
applyConstraint(getMaxLength, "maxLength"); | ||
applyConstraint(getMinValue, "minimum"); | ||
applyConstraint(getMaxValue, "maximum"); | ||
applyConstraint(getPattern, "pattern"); | ||
applyConstraint(getMinItems, "minItems"); | ||
applyConstraint(getMaxItems, "maxItems"); | ||
const minValueExclusive = getMinValueExclusive(program, type); | ||
if (minValueExclusive !== undefined) { | ||
schema.minimum = minValueExclusive; | ||
schema.exclusiveMinimum = true; | ||
} | ||
if (isMultipart && __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_isBytesKeptRaw).call(this, variant.type)) { | ||
schemaMembers.push({ schema: { type: "string", format: "binary" }, type: variant.type }); | ||
continue; | ||
const maxValueExclusive = getMaxValueExclusive(program, type); | ||
if (maxValueExclusive !== undefined) { | ||
schema.maximum = maxValueExclusive; | ||
schema.exclusiveMaximum = true; | ||
} | ||
if (isLiteralType(variant.type)) { | ||
if (!literalVariantEnumByType[variant.type.kind]) { | ||
const enumValue = [variant.type.value]; | ||
literalVariantEnumByType[variant.type.kind] = enumValue; | ||
schemaMembers.push({ | ||
schema: { type: literalType(variant.type), enum: enumValue }, | ||
type: null, | ||
}); | ||
} | ||
else { | ||
literalVariantEnumByType[variant.type.kind].push(variant.type.value); | ||
} | ||
if (isSecret(program, type)) { | ||
schema.format = "password"; | ||
} | ||
else { | ||
const enumSchema = this.emitter.emitTypeReference(variant.type, { | ||
referenceContext: isMultipart ? { contentType: "application/json" } : {}, | ||
// the stdlib applies a format of "url" but json schema wants "uri", | ||
// so ignore this format if it's the built-in type. | ||
if (!this.#isStdType(type) || type.name !== "url") { | ||
applyConstraint(getFormat, "format"); | ||
} | ||
applyConstraint(getDoc, "description"); | ||
applyConstraint(getSummary, "title"); | ||
applyConstraint((p, t) => (getDeprecated(p, t) !== undefined ? true : undefined), "deprecated"); | ||
this.#attachExtensions(program, type, schema); | ||
const values = getKnownValues(program, type); | ||
if (values) { | ||
return new ObjectBuilder({ | ||
oneOf: [schema, this.#enumSchema(values)], | ||
}); | ||
compilerAssert(enumSchema.kind === "code", "Unexpected enum schema. Should be kind: code"); | ||
schemaMembers.push({ schema: enumSchema.value, type: variant.type }); | ||
} | ||
return new ObjectBuilder(schema); | ||
} | ||
if (schemaMembers.length === 0) { | ||
if (nullable) { | ||
// This union is equivalent to just `null` but OA3 has no way to specify | ||
// null as a value, so we throw an error. | ||
reportDiagnostic(program, { code: "union-null", target: union }); | ||
return new ObjectBuilder({}); | ||
#inlineType(type, schema) { | ||
if (this.#options.includeXTypeSpecName !== "never") { | ||
schema.set("x-typespec-name", getTypeName(type, this.#typeNameOptions())); | ||
} | ||
else { | ||
// completely empty union can maybe only happen with bugs? | ||
compilerAssert(false, "Attempting to emit an empty union"); | ||
} | ||
return schema; | ||
} | ||
if (schemaMembers.length === 1) { | ||
// we can just return the single schema member after applying nullable | ||
const schema = schemaMembers[0].schema; | ||
const type = schemaMembers[0].type; | ||
const additionalProps = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyConstraints).call(this, union, {}); | ||
if (nullable) { | ||
additionalProps.nullable = true; | ||
#createDeclaration(type, name, schema) { | ||
const refUrl = getRef(this.emitter.getProgram(), type); | ||
if (refUrl) { | ||
return { | ||
$ref: refUrl, | ||
}; | ||
} | ||
if (Object.keys(additionalProps).length === 0) { | ||
return new ObjectBuilder(schema); | ||
if (shouldInline(this.emitter.getProgram(), type)) { | ||
return this.#inlineType(type, schema); | ||
} | ||
else { | ||
if ((schema instanceof Placeholder || "$ref" in schema) && | ||
!(type && shouldInline(program, type))) { | ||
if (type && type.kind === "Model") { | ||
return new ObjectBuilder({ | ||
type: "object", | ||
allOf: B.array([schema]), | ||
...additionalProps, | ||
}); | ||
} | ||
else { | ||
return new ObjectBuilder({ oneOf: B.array([schema]), ...additionalProps }); | ||
} | ||
} | ||
else { | ||
const merged = new ObjectBuilder(schema); | ||
for (const [key, value] of Object.entries(additionalProps)) { | ||
merged.set(key, value); | ||
} | ||
return merged; | ||
} | ||
const title = getSummary(this.emitter.getProgram(), type); | ||
if (title) { | ||
schema.set("title", title); | ||
} | ||
const usage = this.#visibilityUsage.getUsage(type); | ||
const shouldAddSuffix = usage !== undefined && usage.size > 1; | ||
const visibility = this.#getVisibilityContext(); | ||
const fullName = name + (shouldAddSuffix ? getVisibilitySuffix(visibility, Visibility.Read) : ""); | ||
const decl = this.emitter.result.declaration(fullName, schema); | ||
checkDuplicateTypeName(this.emitter.getProgram(), type, fullName, Object.fromEntries(decl.scope.declarations.map((x) => [x.name, true]))); | ||
return decl; | ||
} | ||
const schema = { | ||
[ofType]: schemaMembers.map((m) => m.schema), | ||
}; | ||
if (nullable) { | ||
schema.nullable = true; | ||
#isStdType(type) { | ||
return this.emitter.getProgram().checker.isStdType(type); | ||
} | ||
if (discriminator) { | ||
// the decorator validates that all the variants will be a model type | ||
// with the discriminator field present. | ||
schema.discriminator = { ...discriminator }; | ||
// Diagnostic already reported in compiler for unions | ||
const discriminatedUnion = ignoreDiagnostics(getDiscriminatedUnion(union, discriminator)); | ||
if (discriminatedUnion.variants.size > 0) { | ||
schema.discriminator.mapping = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getDiscriminatorMapping).call(this, discriminatedUnion); | ||
#applyEncoding(typespecType, target) { | ||
const encodeData = getEncode(this.emitter.getProgram(), typespecType); | ||
if (encodeData) { | ||
const newTarget = new ObjectBuilder(target); | ||
const newType = this.#getSchemaForStdScalars(encodeData.type); | ||
newTarget.type = newType.type; | ||
// If the target already has a format it takes priority. (e.g. int32) | ||
newTarget.format = this.#mergeFormatAndEncoding(newTarget.format, encodeData.encoding, newType.format); | ||
return newTarget; | ||
} | ||
const result = new ObjectBuilder(target); | ||
return result; | ||
} | ||
return __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyConstraints).call(this, union, schema); | ||
}, _OpenAPI3SchemaEmitter_getDiscriminatorMapping = function _OpenAPI3SchemaEmitter_getDiscriminatorMapping(union) { | ||
const mapping = {}; | ||
for (const [key, model] of union.variants.entries()) { | ||
const ref = this.emitter.emitTypeReference(model); | ||
compilerAssert(ref.kind === "code", "Unexpected ref schema. Should be kind: code"); | ||
mapping[key] = ref.value.$ref; | ||
} | ||
return mapping; | ||
}, _OpenAPI3SchemaEmitter_attachExtensions = function _OpenAPI3SchemaEmitter_attachExtensions(program, type, emitObject) { | ||
// Attach any OpenAPI extensions | ||
const extensions = getExtensions(program, type); | ||
if (extensions) { | ||
for (const key of extensions.keys()) { | ||
emitObject[key] = extensions.get(key); | ||
#mergeFormatAndEncoding(format, encoding, encodeAsFormat) { | ||
switch (format) { | ||
case undefined: | ||
return encodeAsFormat ?? encoding; | ||
case "date-time": | ||
switch (encoding) { | ||
case "rfc3339": | ||
return "date-time"; | ||
case "unixTimestamp": | ||
return "unixtime"; | ||
case "rfc7231": | ||
return "http-date"; | ||
default: | ||
return encoding; | ||
} | ||
case "duration": | ||
switch (encoding) { | ||
case "ISO8601": | ||
return "duration"; | ||
default: | ||
return encodeAsFormat ?? encoding; | ||
} | ||
default: | ||
return encodeAsFormat ?? encoding; | ||
} | ||
} | ||
}, _OpenAPI3SchemaEmitter_getSchemaForScalar = function _OpenAPI3SchemaEmitter_getSchemaForScalar(scalar) { | ||
let result = {}; | ||
const isStd = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_isStdType).call(this, scalar); | ||
if (isStd) { | ||
result = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getSchemaForStdScalars).call(this, scalar); | ||
} | ||
else if (scalar.baseScalar) { | ||
result = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getSchemaForScalar).call(this, scalar.baseScalar); | ||
} | ||
const withDecorators = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyEncoding).call(this, scalar, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_applyConstraints).call(this, scalar, result)); | ||
if (isStd) { | ||
// Standard types are going to be inlined in the spec and we don't want the description of the scalar to show up | ||
delete withDecorators.description; | ||
} | ||
return withDecorators; | ||
}, _OpenAPI3SchemaEmitter_getSchemaForStdScalars = function _OpenAPI3SchemaEmitter_getSchemaForStdScalars(scalar) { | ||
switch (scalar.name) { | ||
case "bytes": | ||
return { type: "string", format: "byte" }; | ||
case "numeric": | ||
return { type: "number" }; | ||
case "integer": | ||
return { type: "integer" }; | ||
case "int8": | ||
return { type: "integer", format: "int8" }; | ||
case "int16": | ||
return { type: "integer", format: "int16" }; | ||
case "int32": | ||
return { type: "integer", format: "int32" }; | ||
case "int64": | ||
return { type: "integer", format: "int64" }; | ||
case "safeint": | ||
switch (__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_options, "f").safeintStrategy) { | ||
case "double-int": | ||
return { type: "integer", format: "double-int" }; | ||
case "int64": | ||
default: | ||
return { type: "integer", format: "int64" }; | ||
} | ||
case "uint8": | ||
return { type: "integer", format: "uint8" }; | ||
case "uint16": | ||
return { type: "integer", format: "uint16" }; | ||
case "uint32": | ||
return { type: "integer", format: "uint32" }; | ||
case "uint64": | ||
return { type: "integer", format: "uint64" }; | ||
case "float": | ||
return { type: "number" }; | ||
case "float64": | ||
return { type: "number", format: "double" }; | ||
case "float32": | ||
return { type: "number", format: "float" }; | ||
case "decimal": | ||
return { type: "number", format: "decimal" }; | ||
case "decimal128": | ||
return { type: "number", format: "decimal128" }; | ||
case "string": | ||
return { type: "string" }; | ||
case "boolean": | ||
return { type: "boolean" }; | ||
case "plainDate": | ||
return { type: "string", format: "date" }; | ||
case "utcDateTime": | ||
case "offsetDateTime": | ||
return { type: "string", format: "date-time" }; | ||
case "plainTime": | ||
return { type: "string", format: "time" }; | ||
case "duration": | ||
return { type: "string", format: "duration" }; | ||
case "url": | ||
return { type: "string", format: "uri" }; | ||
default: | ||
const _assertNever = scalar.name; | ||
return {}; | ||
} | ||
}, _OpenAPI3SchemaEmitter_applyConstraints = function _OpenAPI3SchemaEmitter_applyConstraints(type, original) { | ||
const schema = new ObjectBuilder(original); | ||
const program = this.emitter.getProgram(); | ||
const applyConstraint = (fn, key) => { | ||
const value = fn(program, type); | ||
if (value !== undefined) { | ||
schema[key] = value; | ||
intrinsic(intrinsic, name) { | ||
switch (name) { | ||
case "unknown": | ||
return {}; | ||
case "null": | ||
return { nullable: true }; | ||
} | ||
}; | ||
applyConstraint(getMinLength, "minLength"); | ||
applyConstraint(getMaxLength, "maxLength"); | ||
applyConstraint(getMinValue, "minimum"); | ||
applyConstraint(getMaxValue, "maximum"); | ||
applyConstraint(getPattern, "pattern"); | ||
applyConstraint(getMinItems, "minItems"); | ||
applyConstraint(getMaxItems, "maxItems"); | ||
const minValueExclusive = getMinValueExclusive(program, type); | ||
if (minValueExclusive !== undefined) { | ||
schema.minimum = minValueExclusive; | ||
schema.exclusiveMinimum = true; | ||
} | ||
const maxValueExclusive = getMaxValueExclusive(program, type); | ||
if (maxValueExclusive !== undefined) { | ||
schema.maximum = maxValueExclusive; | ||
schema.exclusiveMaximum = true; | ||
} | ||
if (isSecret(program, type)) { | ||
schema.format = "password"; | ||
} | ||
// the stdlib applies a format of "url" but json schema wants "uri", | ||
// so ignore this format if it's the built-in type. | ||
if (!__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_isStdType).call(this, type) || type.name !== "url") { | ||
applyConstraint(getFormat, "format"); | ||
} | ||
applyConstraint(getDoc, "description"); | ||
applyConstraint(getSummary, "title"); | ||
applyConstraint((p, t) => (getDeprecated(p, t) !== undefined ? true : undefined), "deprecated"); | ||
__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_attachExtensions).call(this, program, type, schema); | ||
const values = getKnownValues(program, type); | ||
if (values) { | ||
return new ObjectBuilder({ | ||
oneOf: [schema, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_enumSchema).call(this, values)], | ||
reportDiagnostic(this.emitter.getProgram(), { | ||
code: "invalid-schema", | ||
format: { type: name }, | ||
target: intrinsic, | ||
}); | ||
return {}; | ||
} | ||
return new ObjectBuilder(schema); | ||
}, _OpenAPI3SchemaEmitter_inlineType = function _OpenAPI3SchemaEmitter_inlineType(type, schema) { | ||
if (__classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_options, "f").includeXTypeSpecName !== "never") { | ||
schema.set("x-typespec-name", getTypeName(type, __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_typeNameOptions).call(this))); | ||
programContext(program) { | ||
const sourceFile = this.emitter.createSourceFile("openapi"); | ||
return { scope: sourceFile.globalScope }; | ||
} | ||
return schema; | ||
}, _OpenAPI3SchemaEmitter_createDeclaration = function _OpenAPI3SchemaEmitter_createDeclaration(type, name, schema) { | ||
const refUrl = getRef(this.emitter.getProgram(), type); | ||
if (refUrl) { | ||
return { | ||
$ref: refUrl, | ||
}; | ||
} | ||
if (shouldInline(this.emitter.getProgram(), type)) { | ||
return __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_inlineType).call(this, type, schema); | ||
} | ||
const title = getSummary(this.emitter.getProgram(), type); | ||
if (title) { | ||
schema.set("title", title); | ||
} | ||
const usage = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_visibilityUsage, "f").getUsage(type); | ||
const shouldAddSuffix = usage !== undefined && usage.size > 1; | ||
const visibility = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getVisibilityContext).call(this); | ||
const fullName = name + (shouldAddSuffix ? getVisibilitySuffix(visibility, Visibility.Read) : ""); | ||
const decl = this.emitter.result.declaration(fullName, schema); | ||
checkDuplicateTypeName(this.emitter.getProgram(), type, fullName, Object.fromEntries(decl.scope.declarations.map((x) => [x.name, true]))); | ||
return decl; | ||
}, _OpenAPI3SchemaEmitter_isStdType = function _OpenAPI3SchemaEmitter_isStdType(type) { | ||
return this.emitter.getProgram().checker.isStdType(type); | ||
}, _OpenAPI3SchemaEmitter_applyEncoding = function _OpenAPI3SchemaEmitter_applyEncoding(typespecType, target) { | ||
const encodeData = getEncode(this.emitter.getProgram(), typespecType); | ||
if (encodeData) { | ||
const newTarget = new ObjectBuilder(target); | ||
const newType = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_getSchemaForStdScalars).call(this, encodeData.type); | ||
newTarget.type = newType.type; | ||
// If the target already has a format it takes priority. (e.g. int32) | ||
newTarget.format = __classPrivateFieldGet(this, _OpenAPI3SchemaEmitter_instances, "m", _OpenAPI3SchemaEmitter_mergeFormatAndEncoding).call(this, newTarget.format, encodeData.encoding, newType.format); | ||
return newTarget; | ||
} | ||
const result = new ObjectBuilder(target); | ||
return result; | ||
}, _OpenAPI3SchemaEmitter_mergeFormatAndEncoding = function _OpenAPI3SchemaEmitter_mergeFormatAndEncoding(format, encoding, encodeAsFormat) { | ||
switch (format) { | ||
case undefined: | ||
return encodeAsFormat !== null && encodeAsFormat !== void 0 ? encodeAsFormat : encoding; | ||
case "date-time": | ||
switch (encoding) { | ||
case "rfc3339": | ||
return "date-time"; | ||
case "unixTimestamp": | ||
return "unixtime"; | ||
case "rfc7231": | ||
return "http-date"; | ||
default: | ||
return encoding; | ||
} | ||
case "duration": | ||
switch (encoding) { | ||
case "ISO8601": | ||
return "duration"; | ||
default: | ||
return encodeAsFormat !== null && encodeAsFormat !== void 0 ? encodeAsFormat : encoding; | ||
} | ||
default: | ||
return encodeAsFormat !== null && encodeAsFormat !== void 0 ? encodeAsFormat : encoding; | ||
} | ||
}; | ||
} | ||
function isLiteralType(type) { | ||
@@ -737,6 +738,5 @@ return type.kind === "Boolean" || type.kind === "String" || type.kind === "Number"; | ||
function includeDerivedModel(model) { | ||
var _a, _b; | ||
return (!isTemplateDeclaration(model) && | ||
(((_a = model.templateMapper) === null || _a === void 0 ? void 0 : _a.args) === undefined || | ||
((_b = model.templateMapper.args) === null || _b === void 0 ? void 0 : _b.length) === 0 || | ||
(model.templateMapper?.args === undefined || | ||
model.templateMapper.args?.length === 0 || | ||
model.derivedModels.length > 0)); | ||
@@ -761,3 +761,2 @@ } | ||
export function getDefaultValue(program, type, defaultType) { | ||
var _a; | ||
switch (defaultType.kind) { | ||
@@ -787,3 +786,3 @@ case "String": | ||
case "EnumMember": | ||
return (_a = defaultType.value) !== null && _a !== void 0 ? _a : defaultType.name; | ||
return defaultType.value ?? defaultType.name; | ||
case "UnionVariant": | ||
@@ -790,0 +789,0 @@ return getDefaultValue(program, type, defaultType.type); |
@@ -56,4 +56,3 @@ import { ignoreDiagnostics, navigateTypesInNamespace, } from "@typespec/compiler"; | ||
function trackUsageExact(usages, type, usage) { | ||
var _a; | ||
const existingFlag = (_a = usages.get(type)) !== null && _a !== void 0 ? _a : new Set(); | ||
const existingFlag = usages.get(type) ?? new Set(); | ||
existingFlag.add(usage); | ||
@@ -90,4 +89,3 @@ usages.set(type, existingFlag); | ||
function navigateReferencedTypes(type, usage, callback, visited = new TwoLevelMap()) { | ||
var _a; | ||
if ((_a = visited.get(type)) === null || _a === void 0 ? void 0 : _a.get(usage)) { | ||
if (visited.get(type)?.get(usage)) { | ||
return; | ||
@@ -94,0 +92,0 @@ } |
{ | ||
"name": "@typespec/openapi3", | ||
"version": "0.56.0-dev.2", | ||
"version": "0.56.0-dev.3", | ||
"author": "Microsoft Corporation", | ||
@@ -57,8 +57,8 @@ "description": "TypeSpec library for emitting OpenAPI 3.0 from the TypeSpec REST protocol binding", | ||
"@typespec/versioning": "~0.55.0 || >=0.56.0-dev <0.56.0", | ||
"@vitest/coverage-v8": "^1.4.0", | ||
"@vitest/ui": "^1.4.0", | ||
"@vitest/coverage-v8": "^1.5.0", | ||
"@vitest/ui": "^1.5.0", | ||
"c8": "^9.1.0", | ||
"rimraf": "~5.0.5", | ||
"typescript": "~5.4.3", | ||
"vitest": "^1.4.0", | ||
"typescript": "~5.4.5", | ||
"vitest": "^1.5.0", | ||
"@typespec/tspd": "~0.46.0" | ||
@@ -65,0 +65,0 @@ }, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
271760
3671