@typespec/openapi3 0.51.0-dev.1 → 0.51.0-dev.3

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.
@@ -1,11 +1,13 @@
1
- import { compilerAssert, emitFile, getAllTags, getAnyExtensionFromPath, getDiscriminatedUnion, getDiscriminator, getDoc, getEncode, getFormat, getKnownValues, getMaxItems, getMaxLength, getMaxValue, getMaxValueExclusive, getMinItems, getMinLength, getMinValue, getMinValueExclusive, getNamespaceFullName, getPattern, getService, getSummary, ignoreDiagnostics, interpolatePath, isArrayModelType, isDeprecated, isErrorType, isGlobalNamespace, isNeverType, isNullType, isRecordModelType, isSecret, isTemplateDeclaration, isVoidType, listServices, navigateTypesInNamespace, projectProgram, resolvePath, TwoLevelMap, } from "@typespec/compiler";
2
- import { createMetadataInfo, getAuthentication, getHttpService, getServers, getStatusCodeDescription, getVisibilitySuffix, isContentTypeHeader, isOverloadSameEndpoint, reportIfNoRoutes, resolveRequestVisibility, Visibility, } from "@typespec/http";
3
- import { checkDuplicateTypeName, getExtensions, getExternalDocs, getInfo, getOpenAPITypeName, getParameterKey, isDefaultResponse, isReadonlyProperty, resolveOperationId, shouldInline, } from "@typespec/openapi";
1
+ import { compilerAssert, emitFile, getAllTags, getAnyExtensionFromPath, getDoc, getEncode, getFormat, getKnownValues, getMaxItems, getMaxLength, getMaxValue, getMaxValueExclusive, getMinItems, getMinLength, getMinValue, getMinValueExclusive, getNamespaceFullName, getPattern, getService, getSummary, ignoreDiagnostics, interpolatePath, isArrayModelType, isDeprecated, isErrorType, isGlobalNamespace, isNeverType, isNullType, isSecret, isVoidType, listServices, navigateTypesInNamespace, projectProgram, resolvePath, } from "@typespec/compiler";
2
+ import { createMetadataInfo, getAuthentication, getHttpService, getServers, getStatusCodeDescription, isContentTypeHeader, isOverloadSameEndpoint, reportIfNoRoutes, resolveRequestVisibility, Visibility, } from "@typespec/http";
3
+ import { getExtensions, getExternalDocs, getInfo, getOpenAPITypeName, getParameterKey, isDefaultResponse, isReadonlyProperty, resolveOperationId, shouldInline, } from "@typespec/openapi";
4
4
  import { buildVersionProjections } from "@typespec/versioning";
5
5
  import { stringify } from "yaml";
6
- import { getOneOf, getRef } from "./decorators.js";
6
+ import { getRef } from "./decorators.js";
7
7
  import { reportDiagnostic } from "./lib.js";
8
+ import { OpenAPI3SchemaEmitter } from "./schema-emitter.js";
8
9
  import { deepEquals } from "./util.js";
10
+ import { resolveVisibilityUsage } from "./visibility-usage.js";
9
11
  const defaultFileType = "yaml";
10
12
  const defaultOptions = {
11
13
  "new-line": "lf",
@@ -14,7 +16,7 @@ const defaultOptions = {
14
16
  };
15
17
  export async function $onEmit(context) {
16
18
  const options = resolveOptions(context);
17
- const emitter = createOAPIEmitter(context.program, options);
19
+ const emitter = createOAPIEmitter(context, options);
18
20
  await emitter.emitOpenAPI();
19
21
  }
20
22
  function findFileTypeFromFilename(filename) {
@@ -44,29 +46,16 @@ export function resolveOptions(context) {
44
46
  outputFile: resolvePath(context.emitterOutputDir, outputFile),
45
47
  };
46
48
  }
47
- /**
48
- * Represents a node that will hold a JSON reference. The value is computed
49
- * at the end so that we can defer decisions about the name that is
50
- * referenced.
51
- */
52
- class Ref {
53
- toJSON() {
54
- compilerAssert(this.value, "Reference value never set.");
55
- return this.value;
56
- }
57
- }
58
- function createOAPIEmitter(program, options) {
49
+ function createOAPIEmitter(context, options) {
50
+ let program = context.program;
51
+ let schemaEmitter;
59
52
  let root;
60
53
  // Get the service namespace string for use in name shortening
61
54
  let serviceNamespace;
62
55
  let currentPath;
63
56
  let currentEndpoint;
64
57
  let metadataInfo;
65
- // Keep a map of all Types+Visibility combinations that were encountered
66
- // that need schema definitions.
67
- let pendingSchemas = new TwoLevelMap();
68
- // Reuse a single ref object per Type+Visibility combination.
69
- let refs = new TwoLevelMap();
58
+ let visibilityUsage;
70
59
  // Keep track of inline types still in the process of having their schema computed
71
60
  // This is used to detect cycles in inline types, which is an
72
61
  let inProgressInlineTypes = new Set();
@@ -89,6 +78,16 @@ function createOAPIEmitter(program, options) {
89
78
  return { emitOpenAPI };
90
79
  function initializeEmitter(service, version) {
91
80
  var _a, _b, _c;
81
+ metadataInfo = createMetadataInfo(program, {
82
+ canonicalVisibility: Visibility.Read,
83
+ canShareProperty: (p) => isReadonlyProperty(program, p),
84
+ });
85
+ visibilityUsage = resolveVisibilityUsage(program, metadataInfo, service.type);
86
+ schemaEmitter = context.getAssetEmitter(class extends OpenAPI3SchemaEmitter {
87
+ constructor(emitter) {
88
+ super(emitter, metadataInfo, visibilityUsage, options);
89
+ }
90
+ });
92
91
  const auth = processAuth(service.type);
93
92
  root = {
94
93
  openapi: "3.0.0",
@@ -117,12 +116,6 @@ function createOAPIEmitter(program, options) {
117
116
  }
118
117
  serviceNamespace = getNamespaceFullName(service.type);
119
118
  currentPath = root.paths;
120
- pendingSchemas = new TwoLevelMap();
121
- refs = new TwoLevelMap();
122
- metadataInfo = createMetadataInfo(program, {
123
- canonicalVisibility: Visibility.Read,
124
- canShareProperty: (p) => isReadonlyProperty(program, p),
125
- });
126
119
  inProgressInlineTypes = new Set();
127
120
  params = new Map();
128
121
  paramModels = new Set();
@@ -169,10 +162,10 @@ function createOAPIEmitter(program, options) {
169
162
  description: getDoc(program, prop),
170
163
  };
171
164
  if (prop.type.kind === "Enum") {
172
- variable.enum = getSchemaForEnum(prop.type).enum;
165
+ variable.enum = getSchemaValue(prop.type, Visibility.Read).enum;
173
166
  }
174
167
  else if (prop.type.kind === "Union") {
175
- variable.enum = getSchemaForUnion(prop.type, Visibility.Read).enum;
168
+ variable.enum = getSchemaValue(prop.type, Visibility.Read).enum;
176
169
  }
177
170
  else if (prop.type.kind === "String") {
178
171
  variable.enum = [prop.type.value];
@@ -698,35 +691,56 @@ function createOAPIEmitter(program, options) {
698
691
  function getResponseHeader(prop) {
699
692
  return getOpenAPIParameterBase(prop, Visibility.Read);
700
693
  }
701
- function getSchemaOrRef(type, visibility) {
702
- var _a;
703
- const refUrl = getRef(program, type);
704
- if (refUrl) {
705
- return {
706
- $ref: refUrl,
707
- };
708
- }
709
- if (type.kind === "Scalar" && program.checker.isStdType(type)) {
710
- return getSchemaForScalar(type);
711
- }
712
- if (type.kind === "String" || type.kind === "Number" || type.kind === "Boolean") {
713
- // For literal types, we just want to emit them directly as well.
714
- return getSchemaForLiterals(type);
715
- }
716
- if (type.kind === "Intrinsic" && type.name === "unknown") {
717
- return getSchemaForIntrinsicType(type);
694
+ function callSchemaEmitter(type, visibility) {
695
+ const result = emitTypeWithSchemaEmitter(type, visibility);
696
+ switch (result.kind) {
697
+ case "code":
698
+ return result.value;
699
+ case "declaration":
700
+ return { $ref: `#/components/schemas/${result.name}` };
701
+ case "circular":
702
+ reportDiagnostic(program, {
703
+ code: "inline-cycle",
704
+ format: { type: getOpenAPITypeName(program, type, typeNameOptions) },
705
+ target: type,
706
+ });
707
+ return {};
708
+ case "none":
709
+ return {};
718
710
  }
719
- if (type.kind === "EnumMember") {
720
- // Enum members are just the OA representation of their values.
721
- if (typeof type.value === "number") {
722
- return { type: "number", enum: [type.value] };
723
- }
724
- else {
725
- return { type: "string", enum: [(_a = type.value) !== null && _a !== void 0 ? _a : type.name] };
726
- }
711
+ }
712
+ function getSchemaValue(type, visibility) {
713
+ const result = emitTypeWithSchemaEmitter(type, visibility);
714
+ switch (result.kind) {
715
+ case "code":
716
+ case "declaration":
717
+ return result.value;
718
+ case "circular":
719
+ reportDiagnostic(program, {
720
+ code: "inline-cycle",
721
+ format: { type: getOpenAPITypeName(program, type, typeNameOptions) },
722
+ target: type,
723
+ });
724
+ return {};
725
+ case "none":
726
+ return {};
727
727
  }
728
- if (type.kind === "ModelProperty") {
729
- return resolveProperty(type, visibility);
728
+ }
729
+ function emitTypeWithSchemaEmitter(type, visibility) {
730
+ return schemaEmitter.emitType(type, {
731
+ referenceContext: { visibility, serviceNamespaceName: serviceNamespace },
732
+ });
733
+ }
734
+ function getSchemaOrRef(type, visibility) {
735
+ if ((type.kind === "Scalar" && program.checker.isStdType(type)) ||
736
+ type.kind === "String" ||
737
+ type.kind === "Number" ||
738
+ type.kind === "Boolean" ||
739
+ (type.kind === "Intrinsic" && type.name === "unknown") ||
740
+ type.kind === "EnumMember" ||
741
+ type.kind === "ModelProperty") {
742
+ // Those types should just be inlined.
743
+ return callSchemaEmitter(type, visibility);
730
744
  }
731
745
  type = metadataInfo.getEffectivePayloadType(type, visibility);
732
746
  const name = getOpenAPITypeName(program, type, typeNameOptions);
@@ -737,10 +751,6 @@ function createOAPIEmitter(program, options) {
737
751
  // be caught and handled in emitOpenAPI.
738
752
  throw new ErrorTypeFoundError();
739
753
  }
740
- // helps to read output and correlate to TypeSpec
741
- if (schema && options.includeXTypeSpecName !== "never") {
742
- schema["x-typespec-name"] = name;
743
- }
744
754
  return schema;
745
755
  }
746
756
  else {
@@ -748,14 +758,7 @@ function createOAPIEmitter(program, options) {
748
758
  if (!metadataInfo.isTransformed(type, visibility)) {
749
759
  visibility = Visibility.Read;
750
760
  }
751
- const pending = pendingSchemas.getOrAdd(type, visibility, () => ({
752
- type,
753
- visibility,
754
- ref: refs.getOrAdd(type, visibility, () => new Ref()),
755
- }));
756
- return {
757
- $ref: pending.ref,
758
- };
761
+ return callSchemaEmitter(type, visibility);
759
762
  }
760
763
  }
761
764
  function getSchemaForInlineType(type, visibility, name) {
@@ -1010,46 +1013,23 @@ function createOAPIEmitter(program, options) {
1010
1013
  }
1011
1014
  }
1012
1015
  function emitSchemas(serviceNamespace) {
1013
- const processedSchemas = new TwoLevelMap();
1014
- processSchemas();
1015
1016
  if (!options.omitUnreachableTypes) {
1016
1017
  processUnreferencedSchemas();
1017
1018
  }
1018
- // Emit the processed schemas. Only now can we compute the names as it
1019
- // depends on whether we have produced multiple schemas for a single
1020
- // TYPESPEC type.
1021
- for (const group of processedSchemas.values()) {
1022
- for (const [visibility, processed] of group) {
1023
- let name = getOpenAPITypeName(program, processed.type, typeNameOptions);
1024
- if (group.size > 1) {
1025
- name += getVisibilitySuffix(visibility, Visibility.Read);
1026
- }
1027
- checkDuplicateTypeName(program, processed.type, name, root.components.schemas);
1028
- processed.ref.value = "#/components/schemas/" + encodeURIComponent(name);
1029
- if (processed.schema) {
1030
- root.components.schemas[name] = processed.schema;
1031
- }
1032
- }
1033
- }
1034
- function processSchemas() {
1035
- // Process pending schemas. Note that getSchemaForType may pull in new
1036
- // pending schemas so we iterate until there are no pending schemas
1037
- // remaining.
1038
- while (pendingSchemas.size > 0) {
1039
- for (const [type, group] of pendingSchemas) {
1040
- for (const [visibility, pending] of group) {
1041
- processedSchemas.getOrAdd(type, visibility, () => ({
1042
- ...pending,
1043
- schema: getSchemaForType(type, visibility),
1044
- }));
1045
- }
1046
- pendingSchemas.delete(type);
1047
- }
1019
+ const files = schemaEmitter.getSourceFiles();
1020
+ if (files.length > 0) {
1021
+ compilerAssert(files.length === 1, `Should only have a single file for now but got ${files.length}`);
1022
+ const schemas = root.components.schemas;
1023
+ const declarations = files[0].globalScope.declarations;
1024
+ for (const declaration of declarations) {
1025
+ schemas[declaration.name] = declaration.value;
1048
1026
  }
1049
1027
  }
1050
1028
  function processUnreferencedSchemas() {
1051
1029
  const addSchema = (type) => {
1052
- if (!processedSchemas.has(type) && !paramModels.has(type) && !shouldInline(program, type)) {
1030
+ if (visibilityUsage.getUsage(type) === undefined &&
1031
+ !paramModels.has(type) &&
1032
+ !shouldInline(program, type)) {
1053
1033
  getSchemaOrRef(type, Visibility.Read);
1054
1034
  }
1055
1035
  };
@@ -1060,7 +1040,6 @@ function createOAPIEmitter(program, options) {
1060
1040
  enum: addSchema,
1061
1041
  union: addSchema,
1062
1042
  }, { skipSubNamespaces });
1063
- processSchemas();
1064
1043
  }
1065
1044
  }
1066
1045
  function emitTags() {
@@ -1069,187 +1048,7 @@ function createOAPIEmitter(program, options) {
1069
1048
  }
1070
1049
  }
1071
1050
  function getSchemaForType(type, visibility) {
1072
- const builtinType = getSchemaForLiterals(type);
1073
- if (builtinType !== undefined)
1074
- return builtinType;
1075
- switch (type.kind) {
1076
- case "Intrinsic":
1077
- return getSchemaForIntrinsicType(type);
1078
- case "Model":
1079
- return getSchemaForModel(type, visibility);
1080
- case "ModelProperty":
1081
- return getSchemaForType(type.type, visibility);
1082
- case "Scalar":
1083
- return getSchemaForScalar(type);
1084
- case "Union":
1085
- return getSchemaForUnion(type, visibility);
1086
- case "UnionVariant":
1087
- return getSchemaForUnionVariant(type, visibility);
1088
- case "Enum":
1089
- return getSchemaForEnum(type);
1090
- case "Tuple":
1091
- return { type: "array", items: {} };
1092
- case "TemplateParameter":
1093
- // Note: This should never happen if it does there is a bug in the compiler.
1094
- reportDiagnostic(program, {
1095
- code: "invalid-schema",
1096
- format: { type: `${type.node.id.sv} (template parameter)` },
1097
- target: type,
1098
- });
1099
- return undefined;
1100
- }
1101
- reportDiagnostic(program, {
1102
- code: "invalid-schema",
1103
- format: { type: type.kind },
1104
- target: type,
1105
- });
1106
- return undefined;
1107
- }
1108
- function getSchemaForIntrinsicType(type) {
1109
- switch (type.name) {
1110
- case "unknown":
1111
- return {};
1112
- }
1113
- reportDiagnostic(program, {
1114
- code: "invalid-schema",
1115
- format: { type: type.name },
1116
- target: type,
1117
- });
1118
- return {};
1119
- }
1120
- function getSchemaForEnum(e) {
1121
- var _a;
1122
- const values = [];
1123
- if (e.members.size === 0) {
1124
- reportDiagnostic(program, { code: "empty-enum", target: e });
1125
- return {};
1126
- }
1127
- const type = enumMemberType(e.members.values().next().value);
1128
- for (const option of e.members.values()) {
1129
- if (type !== enumMemberType(option)) {
1130
- reportDiagnostic(program, { code: "enum-unique-type", target: e });
1131
- continue;
1132
- }
1133
- values.push((_a = option.value) !== null && _a !== void 0 ? _a : option.name);
1134
- }
1135
- const schema = { type, description: getDoc(program, e) };
1136
- if (values.length > 0) {
1137
- schema.enum = values;
1138
- }
1139
- const title = getSummary(program, e);
1140
- if (title) {
1141
- schema.title = title;
1142
- }
1143
- return schema;
1144
- function enumMemberType(member) {
1145
- if (typeof member.value === "number") {
1146
- return "number";
1147
- }
1148
- return "string";
1149
- }
1150
- }
1151
- /**
1152
- * A TypeSpec union maps to a variety of OA3 structures according to the following rules:
1153
- *
1154
- * * A union containing `null` makes a `nullable` schema comprised of the remaining
1155
- * union variants.
1156
- * * A union containing literal types are converted to OA3 enums. All literals of the
1157
- * same type are combined into single enums.
1158
- * * A union that contains multiple items (after removing null and combining like-typed
1159
- * literals into enums) is an `anyOf` union unless `oneOf` is applied to the union
1160
- * declaration.
1161
- */
1162
- function getSchemaForUnion(union, visibility) {
1163
- if (union.variants.size === 0) {
1164
- reportDiagnostic(program, { code: "empty-union", target: union });
1165
- return {};
1166
- }
1167
- const variants = Array.from(union.variants.values());
1168
- const literalVariantEnumByType = {};
1169
- const ofType = getOneOf(program, union) ? "oneOf" : "anyOf";
1170
- const schemaMembers = [];
1171
- let nullable = false;
1172
- const discriminator = getDiscriminator(program, union);
1173
- for (const variant of variants) {
1174
- if (isNullType(variant.type)) {
1175
- nullable = true;
1176
- continue;
1177
- }
1178
- if (isLiteralType(variant.type)) {
1179
- if (!literalVariantEnumByType[variant.type.kind]) {
1180
- const enumSchema = getSchemaForLiterals(variant.type);
1181
- literalVariantEnumByType[variant.type.kind] = enumSchema;
1182
- schemaMembers.push({ schema: enumSchema, type: null });
1183
- }
1184
- else {
1185
- literalVariantEnumByType[variant.type.kind].enum.push(variant.type.value);
1186
- }
1187
- continue;
1188
- }
1189
- schemaMembers.push({ schema: getSchemaOrRef(variant.type, visibility), type: variant.type });
1190
- }
1191
- if (schemaMembers.length === 0) {
1192
- if (nullable) {
1193
- // This union is equivalent to just `null` but OA3 has no way to specify
1194
- // null as a value, so we throw an error.
1195
- reportDiagnostic(program, { code: "union-null", target: union });
1196
- return {};
1197
- }
1198
- else {
1199
- // completely empty union can maybe only happen with bugs?
1200
- compilerAssert(false, "Attempting to emit an empty union");
1201
- }
1202
- }
1203
- if (schemaMembers.length === 1) {
1204
- // we can just return the single schema member after applying nullable
1205
- const schema = schemaMembers[0].schema;
1206
- applyIntrinsicDecorators(union, schema);
1207
- const title = getSummary(program, union);
1208
- if (title) {
1209
- schema.title = title;
1210
- }
1211
- const type = schemaMembers[0].type;
1212
- if (nullable) {
1213
- if (schema.$ref) {
1214
- // but we can't make a ref "nullable", so wrap in an allOf (for models)
1215
- // or oneOf (for all other types)
1216
- if (type && type.kind === "Model") {
1217
- return { type: "object", allOf: [schema], nullable: true };
1218
- }
1219
- else {
1220
- return { oneOf: [schema], nullable: true };
1221
- }
1222
- }
1223
- else {
1224
- schema.nullable = true;
1225
- }
1226
- }
1227
- return schema;
1228
- }
1229
- const schema = {
1230
- [ofType]: schemaMembers.map((m) => m.schema),
1231
- };
1232
- if (nullable) {
1233
- schema.nullable = true;
1234
- }
1235
- if (discriminator) {
1236
- // the decorator validates that all the variants will be a model type
1237
- // with the discriminator field present.
1238
- schema.discriminator = { ...discriminator };
1239
- // Diagnostic already reported in compiler for unions
1240
- const discriminatedUnion = ignoreDiagnostics(getDiscriminatedUnion(union, discriminator));
1241
- if (discriminatedUnion.variants.size > 0) {
1242
- schema.discriminator.mapping = getDiscriminatorMapping(discriminatedUnion, visibility);
1243
- }
1244
- }
1245
- return applyIntrinsicDecorators(union, schema);
1246
- }
1247
- function getSchemaForUnionVariant(variant, visibility) {
1248
- const schema = getSchemaForType(variant.type, visibility);
1249
- return schema;
1250
- }
1251
- function isLiteralType(type) {
1252
- return type.kind === "Boolean" || type.kind === "String" || type.kind === "Number";
1051
+ return callSchemaEmitter(type, visibility);
1253
1052
  }
1254
1053
  function getDefaultValue(type, defaultType) {
1255
1054
  var _a;
@@ -1286,111 +1085,6 @@ function createOAPIEmitter(program, options) {
1286
1085
  });
1287
1086
  }
1288
1087
  }
1289
- function includeDerivedModel(model) {
1290
- var _a, _b;
1291
- return (!isTemplateDeclaration(model) &&
1292
- (((_a = model.templateMapper) === null || _a === void 0 ? void 0 : _a.args) === undefined ||
1293
- ((_b = model.templateMapper.args) === null || _b === void 0 ? void 0 : _b.length) === 0 ||
1294
- model.derivedModels.length > 0));
1295
- }
1296
- function getSchemaForModel(model, visibility) {
1297
- const array = getArrayType(model, visibility);
1298
- if (array) {
1299
- return array;
1300
- }
1301
- const modelSchema = {
1302
- type: "object",
1303
- description: getDoc(program, model),
1304
- };
1305
- const properties = {};
1306
- if (isRecordModelType(program, model)) {
1307
- modelSchema.additionalProperties = getSchemaOrRef(model.indexer.value, visibility);
1308
- }
1309
- const derivedModels = model.derivedModels.filter(includeDerivedModel);
1310
- // getSchemaOrRef on all children to push them into components.schemas
1311
- for (const child of derivedModels) {
1312
- getSchemaOrRef(child, visibility);
1313
- }
1314
- const discriminator = getDiscriminator(program, model);
1315
- if (discriminator) {
1316
- const [union] = getDiscriminatedUnion(model, discriminator);
1317
- const openApiDiscriminator = { ...discriminator };
1318
- if (union.variants.size > 0) {
1319
- openApiDiscriminator.mapping = getDiscriminatorMapping(union, visibility);
1320
- }
1321
- modelSchema.discriminator = openApiDiscriminator;
1322
- properties[discriminator.propertyName] = {
1323
- type: "string",
1324
- description: `Discriminator property for ${model.name}.`,
1325
- };
1326
- }
1327
- applyExternalDocs(model, modelSchema);
1328
- for (const [name, prop] of model.properties) {
1329
- if (!metadataInfo.isPayloadProperty(prop, visibility)) {
1330
- continue;
1331
- }
1332
- if (isNeverType(prop.type)) {
1333
- // If the property has a type of 'never', don't include it in the schema
1334
- continue;
1335
- }
1336
- if (!metadataInfo.isOptional(prop, visibility)) {
1337
- if (!modelSchema.required) {
1338
- modelSchema.required = [];
1339
- }
1340
- modelSchema.required.push(name);
1341
- }
1342
- properties[name] = resolveProperty(prop, visibility);
1343
- }
1344
- if (model.baseModel) {
1345
- const baseSchema = getSchemaOrRef(model.baseModel, visibility);
1346
- modelSchema.allOf = [baseSchema];
1347
- }
1348
- if (Object.keys(properties).length > 0) {
1349
- modelSchema.properties = properties;
1350
- }
1351
- const title = getSummary(program, model);
1352
- if (title) {
1353
- modelSchema.title = title;
1354
- }
1355
- // Attach any OpenAPI extensions
1356
- attachExtensions(program, model, modelSchema);
1357
- return modelSchema;
1358
- }
1359
- function resolveProperty(prop, visibility) {
1360
- const description = getDoc(program, prop);
1361
- const schema = applyEncoding(prop, getSchemaOrRef(prop.type, visibility));
1362
- // Apply decorators on the property to the type's schema
1363
- const additionalProps = applyIntrinsicDecorators(prop, {});
1364
- if (description) {
1365
- additionalProps.description = description;
1366
- }
1367
- if (prop.default) {
1368
- additionalProps.default = getDefaultValue(prop.type, prop.default);
1369
- }
1370
- if (isReadonlyProperty(program, prop)) {
1371
- additionalProps.readOnly = true;
1372
- }
1373
- // Attach any additional OpenAPI extensions
1374
- attachExtensions(program, prop, additionalProps);
1375
- if (schema && "$ref" in schema) {
1376
- if (Object.keys(additionalProps).length === 0) {
1377
- return schema;
1378
- }
1379
- else {
1380
- return {
1381
- allOf: [schema],
1382
- ...additionalProps,
1383
- };
1384
- }
1385
- }
1386
- else {
1387
- if (getOneOf(program, prop) && schema.anyOf) {
1388
- schema.oneOf = schema.anyOf;
1389
- delete schema.anyOf;
1390
- }
1391
- return { ...schema, ...additionalProps };
1392
- }
1393
- }
1394
1088
  function attachExtensions(program, type, emitObject) {
1395
1089
  // Attach any OpenAPI extensions
1396
1090
  const extensions = getExtensions(program, type);
@@ -1400,13 +1094,6 @@ function createOAPIEmitter(program, options) {
1400
1094
  }
1401
1095
  }
1402
1096
  }
1403
- function getDiscriminatorMapping(union, visibility) {
1404
- const mapping = {};
1405
- for (const [key, model] of union.variants.entries()) {
1406
- mapping[key] = getSchemaOrRef(model, visibility).$ref;
1407
- }
1408
- return mapping;
1409
- }
1410
1097
  function applyIntrinsicDecorators(typespecType, target) {
1411
1098
  const newTarget = { ...target };
1412
1099
  const docStr = getDoc(program, typespecType);
@@ -1465,7 +1152,7 @@ function createOAPIEmitter(program, options) {
1465
1152
  const values = getKnownValues(program, typespecType);
1466
1153
  if (values) {
1467
1154
  return {
1468
- oneOf: [newTarget, getSchemaForEnum(values)],
1155
+ oneOf: [newTarget, callSchemaEmitter(values, Visibility.Read)],
1469
1156
  };
1470
1157
  }
1471
1158
  attachExtensions(program, typespecType, newTarget);
@@ -1475,7 +1162,7 @@ function createOAPIEmitter(program, options) {
1475
1162
  const encodeData = getEncode(program, typespecType);
1476
1163
  if (encodeData) {
1477
1164
  const newTarget = { ...target };
1478
- const newType = getSchemaForScalar(encodeData.type);
1165
+ const newType = callSchemaEmitter(encodeData.type, Visibility.Read);
1479
1166
  newTarget.type = newType.type;
1480
1167
  // If the target already has a format it takes priority. (e.g. int32)
1481
1168
  newTarget.format = mergeFormatAndEncoding(newTarget.format, encodeData.encoding, newType.format);
@@ -1515,103 +1202,6 @@ function createOAPIEmitter(program, options) {
1515
1202
  target.externalDocs = externalDocs;
1516
1203
  }
1517
1204
  }
1518
- function getSchemaForLiterals(typespecType) {
1519
- switch (typespecType.kind) {
1520
- case "Number":
1521
- return { type: "number", enum: [typespecType.value] };
1522
- case "String":
1523
- return { type: "string", enum: [typespecType.value] };
1524
- case "Boolean":
1525
- return { type: "boolean", enum: [typespecType.value] };
1526
- default:
1527
- return undefined;
1528
- }
1529
- }
1530
- /**
1531
- * Map TypeSpec intrinsic models to open api definitions
1532
- */
1533
- function getArrayType(typespecType, visibility) {
1534
- if (isArrayModelType(program, typespecType)) {
1535
- const array = {
1536
- type: "array",
1537
- items: getSchemaOrRef(typespecType.indexer.value, visibility | Visibility.Item),
1538
- };
1539
- return applyIntrinsicDecorators(typespecType, array);
1540
- }
1541
- return undefined;
1542
- }
1543
- function getSchemaForScalar(scalar) {
1544
- let result = {};
1545
- const isStd = program.checker.isStdType(scalar);
1546
- if (isStd) {
1547
- result = getSchemaForStdScalars(scalar);
1548
- }
1549
- else if (scalar.baseScalar) {
1550
- result = getSchemaForScalar(scalar.baseScalar);
1551
- }
1552
- const withDecorators = applyEncoding(scalar, applyIntrinsicDecorators(scalar, result));
1553
- if (isStd) {
1554
- // Standard types are going to be inlined in the spec and we don't want the description of the scalar to show up
1555
- delete withDecorators.description;
1556
- }
1557
- return withDecorators;
1558
- }
1559
- function getSchemaForStdScalars(scalar) {
1560
- switch (scalar.name) {
1561
- case "bytes":
1562
- return { type: "string", format: "byte" };
1563
- case "numeric":
1564
- return { type: "number" };
1565
- case "integer":
1566
- return { type: "integer" };
1567
- case "int8":
1568
- return { type: "integer", format: "int8" };
1569
- case "int16":
1570
- return { type: "integer", format: "int16" };
1571
- case "int32":
1572
- return { type: "integer", format: "int32" };
1573
- case "int64":
1574
- return { type: "integer", format: "int64" };
1575
- case "safeint":
1576
- return { type: "integer", format: "int64" };
1577
- case "uint8":
1578
- return { type: "integer", format: "uint8" };
1579
- case "uint16":
1580
- return { type: "integer", format: "uint16" };
1581
- case "uint32":
1582
- return { type: "integer", format: "uint32" };
1583
- case "uint64":
1584
- return { type: "integer", format: "uint64" };
1585
- case "float":
1586
- return { type: "number" };
1587
- case "float64":
1588
- return { type: "number", format: "double" };
1589
- case "float32":
1590
- return { type: "number", format: "float" };
1591
- case "decimal":
1592
- return { type: "number", format: "decimal" };
1593
- case "decimal128":
1594
- return { type: "number", format: "decimal128" };
1595
- case "string":
1596
- return { type: "string" };
1597
- case "boolean":
1598
- return { type: "boolean" };
1599
- case "plainDate":
1600
- return { type: "string", format: "date" };
1601
- case "utcDateTime":
1602
- case "offsetDateTime":
1603
- return { type: "string", format: "date-time" };
1604
- case "plainTime":
1605
- return { type: "string", format: "time" };
1606
- case "duration":
1607
- return { type: "string", format: "duration" };
1608
- case "url":
1609
- return { type: "string", format: "uri" };
1610
- default:
1611
- const _assertNever = scalar.name;
1612
- return {};
1613
- }
1614
- }
1615
1205
  function processAuth(serviceNamespace) {
1616
1206
  const authentication = getAuthentication(program, serviceNamespace);
1617
1207
  if (authentication) {
@@ -1667,9 +1257,7 @@ function serializeDocument(root, fileType) {
1667
1257
  case "json":
1668
1258
  return prettierOutput(JSON.stringify(root, null, 2));
1669
1259
  case "yaml":
1670
- return stringify(root, (key, value) => {
1671
- return value instanceof Ref ? value.toJSON() : value;
1672
- }, {
1260
+ return stringify(root, {
1673
1261
  singleQuote: true,
1674
1262
  aliasDuplicateObjects: false,
1675
1263
  lineWidth: 0,