@typespec/http-server-csharp 0.58.0-alpha.2 → 0.58.0-alpha.20-dev.0

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.
Files changed (78) hide show
  1. package/README.md +69 -6
  2. package/cmd/hscs.js +2 -0
  3. package/dist/src/cli/cli.d.ts +2 -0
  4. package/dist/src/cli/cli.d.ts.map +1 -0
  5. package/dist/src/cli/cli.js +173 -0
  6. package/dist/src/cli/cli.js.map +1 -0
  7. package/dist/src/{attributes.d.ts → lib/attributes.d.ts} +1 -1
  8. package/dist/src/lib/attributes.d.ts.map +1 -0
  9. package/dist/src/{attributes.js → lib/attributes.js} +113 -35
  10. package/dist/src/lib/attributes.js.map +1 -0
  11. package/dist/src/lib/boilerplate.d.ts +6 -0
  12. package/dist/src/lib/boilerplate.d.ts.map +1 -0
  13. package/dist/src/{boilerplate.js → lib/boilerplate.js} +253 -66
  14. package/dist/src/lib/boilerplate.js.map +1 -0
  15. package/dist/src/lib/doc.d.ts +5 -0
  16. package/dist/src/lib/doc.d.ts.map +1 -0
  17. package/dist/src/lib/doc.js +237 -0
  18. package/dist/src/lib/doc.js.map +1 -0
  19. package/dist/src/lib/index.d.ts.map +1 -0
  20. package/dist/src/lib/index.js.map +1 -0
  21. package/dist/src/{interfaces.d.ts → lib/interfaces.d.ts} +58 -4
  22. package/dist/src/lib/interfaces.d.ts.map +1 -0
  23. package/dist/src/{interfaces.js → lib/interfaces.js} +100 -27
  24. package/dist/src/lib/interfaces.js.map +1 -0
  25. package/dist/src/{lib.d.ts → lib/lib.d.ts} +54 -1
  26. package/dist/src/lib/lib.d.ts.map +1 -0
  27. package/dist/src/lib/lib.js +146 -0
  28. package/dist/src/lib/lib.js.map +1 -0
  29. package/dist/src/lib/project.d.ts +5 -0
  30. package/dist/src/lib/project.d.ts.map +1 -0
  31. package/dist/src/lib/project.js +101 -0
  32. package/dist/src/lib/project.js.map +1 -0
  33. package/dist/src/lib/scaffolding.d.ts +22 -0
  34. package/dist/src/lib/scaffolding.d.ts.map +1 -0
  35. package/dist/src/lib/scaffolding.js +461 -0
  36. package/dist/src/lib/scaffolding.js.map +1 -0
  37. package/dist/src/lib/service.d.ts.map +1 -0
  38. package/dist/src/lib/service.js +1188 -0
  39. package/dist/src/lib/service.js.map +1 -0
  40. package/dist/src/lib/testing/index.d.ts.map +1 -0
  41. package/dist/src/{testing → lib/testing}/index.js +1 -0
  42. package/dist/src/lib/testing/index.js.map +1 -0
  43. package/dist/src/{type-helpers.d.ts → lib/type-helpers.d.ts} +5 -1
  44. package/dist/src/lib/type-helpers.d.ts.map +1 -0
  45. package/dist/src/{type-helpers.js → lib/type-helpers.js} +31 -0
  46. package/dist/src/lib/type-helpers.js.map +1 -0
  47. package/dist/src/lib/utils.d.ts +114 -0
  48. package/dist/src/lib/utils.d.ts.map +1 -0
  49. package/dist/src/lib/utils.js +1557 -0
  50. package/dist/src/lib/utils.js.map +1 -0
  51. package/package.json +49 -27
  52. package/dist/src/attributes.d.ts.map +0 -1
  53. package/dist/src/attributes.js.map +0 -1
  54. package/dist/src/boilerplate.d.ts +0 -4
  55. package/dist/src/boilerplate.d.ts.map +0 -1
  56. package/dist/src/boilerplate.js.map +0 -1
  57. package/dist/src/index.d.ts.map +0 -1
  58. package/dist/src/index.js.map +0 -1
  59. package/dist/src/interfaces.d.ts.map +0 -1
  60. package/dist/src/interfaces.js.map +0 -1
  61. package/dist/src/lib.d.ts.map +0 -1
  62. package/dist/src/lib.js +0 -60
  63. package/dist/src/lib.js.map +0 -1
  64. package/dist/src/service.d.ts.map +0 -1
  65. package/dist/src/service.js +0 -829
  66. package/dist/src/service.js.map +0 -1
  67. package/dist/src/testing/index.d.ts.map +0 -1
  68. package/dist/src/testing/index.js.map +0 -1
  69. package/dist/src/type-helpers.d.ts.map +0 -1
  70. package/dist/src/type-helpers.js.map +0 -1
  71. package/dist/src/utils.d.ts +0 -48
  72. package/dist/src/utils.d.ts.map +0 -1
  73. package/dist/src/utils.js +0 -628
  74. package/dist/src/utils.js.map +0 -1
  75. /package/dist/src/{index.d.ts → lib/index.d.ts} +0 -0
  76. /package/dist/src/{index.js → lib/index.js} +0 -0
  77. /package/dist/src/{service.d.ts → lib/service.d.ts} +0 -0
  78. /package/dist/src/{testing → lib/testing}/index.d.ts +0 -0
@@ -0,0 +1,1557 @@
1
+ import { StringBuilder, code, } from "@typespec/asset-emitter";
2
+ import { NoTarget, getFriendlyName, getMinValue, isArrayModelType, isErrorModel, isNullType, isNumericType, isTemplateInstance, isUnknownType, isVoidType, resolveCompilerOptions, resolvePath, serializeValueAsJson, } from "@typespec/compiler";
3
+ import { $ } from "@typespec/compiler/typekit";
4
+ import { Visibility, createMetadataInfo, getHeaderFieldName, isBody, isBodyRoot, isHeader, isMetadata, isPathParam, isQueryParam, isStatusCode, } from "@typespec/http";
5
+ import { getUniqueItems } from "@typespec/json-schema";
6
+ import { camelCase, pascalCase } from "change-case";
7
+ import { createServer } from "net";
8
+ import { getAttributes } from "./attributes.js";
9
+ import { BooleanValue, CSharpCollectionType, CSharpType, CollectionType, NameCasingType, NullValue, NumericValue, StringValue, checkOrAddNamespaceToScope, } from "./interfaces.js";
10
+ import { CSharpServiceOptions, reportDiagnostic } from "./lib.js";
11
+ import { getDoubleType, getEnumType } from "./type-helpers.js";
12
+ const _scalars = new Map();
13
+ export function getCSharpTypeForScalar(program, scalar) {
14
+ if (_scalars.has(scalar))
15
+ return _scalars.get(scalar);
16
+ if (program.checker.isStdType(scalar)) {
17
+ return getCSharpTypeForStdScalars(program, scalar);
18
+ }
19
+ if (scalar.baseScalar) {
20
+ return getCSharpTypeForScalar(program, scalar.baseScalar);
21
+ }
22
+ reportDiagnostic(program, {
23
+ code: "unrecognized-scalar",
24
+ format: { typeName: scalar.name },
25
+ target: scalar,
26
+ });
27
+ const result = new CSharpType({
28
+ name: "object",
29
+ namespace: "System",
30
+ isBuiltIn: true,
31
+ isValueType: false,
32
+ });
33
+ _scalars.set(scalar, result);
34
+ return result;
35
+ }
36
+ export const UnknownType = new CSharpType({
37
+ name: "JsonNode",
38
+ namespace: "System.Text.Json.Nodes",
39
+ isValueType: false,
40
+ isBuiltIn: false,
41
+ isClass: true,
42
+ });
43
+ export const RecordType = new CSharpType({
44
+ name: "JsonObject",
45
+ namespace: "System.Text.Json.Nodes",
46
+ isBuiltIn: false,
47
+ isValueType: false,
48
+ isClass: true,
49
+ });
50
+ export function getCSharpType(program, type, namespace) {
51
+ const known = getKnownType(program, type);
52
+ if (known !== undefined)
53
+ return { type: known };
54
+ switch (type.kind) {
55
+ case "Boolean":
56
+ return { type: standardScalars.get("boolean"), value: new BooleanValue(type.value) };
57
+ case "Number":
58
+ return { type: standardScalars.get("numeric"), value: new NumericValue(type.value) };
59
+ case "String":
60
+ return { type: standardScalars.get("string"), value: new StringValue(type.value) };
61
+ case "EnumMember":
62
+ const enumValue = type.value === undefined ? type.name : type.value;
63
+ if (typeof enumValue === "string")
64
+ return { type: standardScalars.get("string"), value: new StringValue(enumValue) };
65
+ else
66
+ return { type: standardScalars.get("numeric"), value: new NumericValue(enumValue) };
67
+ case "Intrinsic":
68
+ return getCSharpTypeForIntrinsic(program, type);
69
+ case "ModelProperty":
70
+ return getCSharpType(program, type.type, namespace);
71
+ case "Scalar":
72
+ return { type: getCSharpTypeForScalar(program, type) };
73
+ case "Tuple":
74
+ const resolvedItem = coalesceTypes(program, type.values, namespace);
75
+ const itemType = resolvedItem.type;
76
+ return {
77
+ type: new CSharpType({
78
+ name: `${itemType.name}[]`,
79
+ namespace: itemType.namespace,
80
+ isBuiltIn: itemType.isBuiltIn,
81
+ isValueType: false,
82
+ }),
83
+ };
84
+ case "UnionVariant":
85
+ return getCSharpType(program, type.type, namespace);
86
+ case "Union":
87
+ return coalesceTypes(program, [...type.variants.values()].map((v) => v.type), namespace);
88
+ case "Interface":
89
+ return {
90
+ type: new CSharpType({
91
+ name: ensureCSharpIdentifier(program, type, type.name, NameCasingType.Class),
92
+ namespace: `${namespace}`,
93
+ isBuiltIn: false,
94
+ isValueType: false,
95
+ isClass: true,
96
+ }),
97
+ };
98
+ case "Enum":
99
+ if (getEnumType(type) === "double")
100
+ return { type: getDoubleType() };
101
+ return {
102
+ type: new CSharpType({
103
+ name: ensureCSharpIdentifier(program, type, type.name, NameCasingType.Class),
104
+ namespace: `${namespace}`,
105
+ isBuiltIn: false,
106
+ isValueType: true,
107
+ }),
108
+ };
109
+ case "Model":
110
+ if (type.indexer !== undefined && isNumericType(program, type.indexer?.key)) {
111
+ const resolvedItem = getCSharpType(program, type.indexer.value, namespace);
112
+ if (resolvedItem === undefined)
113
+ return undefined;
114
+ const { type: itemType, value: _ } = resolvedItem;
115
+ const uniqueItems = getUniqueItems(program, type);
116
+ const isByte = ["byte", "SByte"].includes(itemType.name);
117
+ const collectionType = CSharpServiceOptions.getInstance().collectionType;
118
+ const returnTypeCollection = uniqueItems
119
+ ? CollectionType.ISet
120
+ : isByte
121
+ ? CollectionType.Array
122
+ : collectionType;
123
+ const returnType = returnTypeCollection === CollectionType.Array
124
+ ? `${itemType.name}[]`
125
+ : `${returnTypeCollection}<${itemType.name}>`;
126
+ return {
127
+ type: new CSharpCollectionType({
128
+ name: returnType,
129
+ namespace: itemType.namespace,
130
+ isBuiltIn: itemType.isBuiltIn,
131
+ isValueType: false,
132
+ isClass: itemType.isClass,
133
+ isCollection: true,
134
+ }, returnTypeCollection, itemType.name),
135
+ };
136
+ }
137
+ if (isRecord(type))
138
+ return {
139
+ type: RecordType,
140
+ };
141
+ let name = type.name;
142
+ if (isTemplateInstance(type)) {
143
+ name = getModelInstantiationName(program, type, name);
144
+ }
145
+ return {
146
+ type: new CSharpType({
147
+ name: ensureCSharpIdentifier(program, type, name, NameCasingType.Class),
148
+ namespace: `${namespace}`,
149
+ isBuiltIn: false,
150
+ isValueType: false,
151
+ isClass: true,
152
+ }),
153
+ };
154
+ default:
155
+ return undefined;
156
+ }
157
+ }
158
+ export function resolveReferenceFromScopes(targetDeclaration, declarationScopes, referenceScopes) {
159
+ function getSourceFile(scopes) {
160
+ for (const scope of scopes) {
161
+ if (scope.kind === "sourceFile") {
162
+ return { scope: scope, file: scope.sourceFile };
163
+ }
164
+ }
165
+ return undefined;
166
+ }
167
+ const decl = getSourceFile(declarationScopes);
168
+ const ref = getSourceFile(referenceScopes);
169
+ if (targetDeclaration.name && decl) {
170
+ const declNs = decl.file.meta["ResolvedNamespace"];
171
+ if (!ref)
172
+ return declNs ? `${declNs}.${targetDeclaration.name} ` : undefined;
173
+ if (checkOrAddNamespaceToScope(declNs, ref.scope)) {
174
+ return targetDeclaration.name;
175
+ }
176
+ return declNs ? `${declNs}.${targetDeclaration.name} ` : undefined;
177
+ }
178
+ return undefined;
179
+ }
180
+ export function coalesceTypes(program, types, namespace) {
181
+ const visited = new Map();
182
+ let candidateType = undefined;
183
+ let candidateValue = undefined;
184
+ for (const type of types) {
185
+ if (!isNullType(type)) {
186
+ if (!visited.has(type)) {
187
+ const resolvedType = getCSharpType(program, type, namespace);
188
+ if (resolvedType === undefined)
189
+ return { type: UnknownType };
190
+ if (resolvedType.type === UnknownType)
191
+ return resolvedType;
192
+ if (candidateType === undefined) {
193
+ candidateType = resolvedType.type;
194
+ candidateValue = resolvedType.value;
195
+ }
196
+ else {
197
+ if (candidateValue !== resolvedType.value)
198
+ candidateValue = undefined;
199
+ if (candidateType !== resolvedType.type)
200
+ return { type: UnknownType };
201
+ }
202
+ visited.set(type, resolvedType);
203
+ }
204
+ }
205
+ }
206
+ return { type: candidateType !== undefined ? candidateType : UnknownType, value: candidateValue };
207
+ }
208
+ export function getKnownType(program, type) {
209
+ return undefined;
210
+ }
211
+ export function getCSharpTypeForIntrinsic(program, type) {
212
+ if (isUnknownType(type)) {
213
+ return { type: UnknownType };
214
+ }
215
+ if (isVoidType(type)) {
216
+ return {
217
+ type: new CSharpType({
218
+ name: "void",
219
+ namespace: "System",
220
+ isBuiltIn: true,
221
+ isValueType: false,
222
+ }),
223
+ };
224
+ }
225
+ if (isNullType(type)) {
226
+ return {
227
+ type: new CSharpType({
228
+ name: "object",
229
+ namespace: "System",
230
+ isBuiltIn: true,
231
+ isValueType: false,
232
+ }),
233
+ value: new NullValue(),
234
+ };
235
+ }
236
+ return undefined;
237
+ }
238
+ const standardScalars = new Map([
239
+ [
240
+ "bytes",
241
+ new CSharpType({ name: "byte[]", namespace: "System", isBuiltIn: true, isValueType: false }),
242
+ ],
243
+ [
244
+ "int8",
245
+ new CSharpType({ name: "SByte", namespace: "System", isBuiltIn: true, isValueType: true }),
246
+ ],
247
+ [
248
+ "uint8",
249
+ new CSharpType({ name: "Byte", namespace: "System", isBuiltIn: true, isValueType: true }),
250
+ ],
251
+ [
252
+ "int16",
253
+ new CSharpType({ name: "Int16", namespace: "System", isBuiltIn: true, isValueType: true }),
254
+ ],
255
+ [
256
+ "uint16",
257
+ new CSharpType({ name: "UInt16", namespace: "System", isBuiltIn: true, isValueType: true }),
258
+ ],
259
+ [
260
+ "int16",
261
+ new CSharpType({ name: "Int16", namespace: "System", isBuiltIn: true, isValueType: true }),
262
+ ],
263
+ [
264
+ "uint16",
265
+ new CSharpType({ name: "UInt16", namespace: "System", isBuiltIn: true, isValueType: true }),
266
+ ],
267
+ [
268
+ "int32",
269
+ new CSharpType({ name: "int", namespace: "System", isBuiltIn: true, isValueType: true }),
270
+ ],
271
+ [
272
+ "uint32",
273
+ new CSharpType({ name: "UInt32", namespace: "System", isBuiltIn: true, isValueType: true }),
274
+ ],
275
+ [
276
+ "integer",
277
+ new CSharpType({ name: "long", namespace: "System", isBuiltIn: true, isValueType: true }),
278
+ ],
279
+ [
280
+ "int64",
281
+ new CSharpType({ name: "long", namespace: "System", isBuiltIn: true, isValueType: true }),
282
+ ],
283
+ [
284
+ "uint64",
285
+ new CSharpType({ name: "UInt64", namespace: "System", isBuiltIn: true, isValueType: true }),
286
+ ],
287
+ [
288
+ "safeint",
289
+ new CSharpType({ name: "long", namespace: "System", isBuiltIn: true, isValueType: true }),
290
+ ],
291
+ [
292
+ "float",
293
+ new CSharpType({ name: "double", namespace: "System", isBuiltIn: true, isValueType: true }),
294
+ ],
295
+ [
296
+ "float64",
297
+ new CSharpType({ name: "double", namespace: "System", isBuiltIn: true, isValueType: true }),
298
+ ],
299
+ [
300
+ "float32",
301
+ new CSharpType({ name: "float", namespace: "System", isBuiltIn: true, isValueType: true }),
302
+ ],
303
+ [
304
+ "string",
305
+ new CSharpType({ name: "string", namespace: "System", isBuiltIn: true, isValueType: false }),
306
+ ],
307
+ [
308
+ "boolean",
309
+ new CSharpType({ name: "bool", namespace: "System", isBuiltIn: true, isValueType: true }),
310
+ ],
311
+ [
312
+ "plainDate",
313
+ new CSharpType({ name: "DateTime", namespace: "System", isBuiltIn: true, isValueType: true }),
314
+ ],
315
+ [
316
+ "utcDateTime",
317
+ new CSharpType({
318
+ name: "DateTimeOffset",
319
+ namespace: "System",
320
+ isBuiltIn: true,
321
+ isValueType: true,
322
+ }),
323
+ ],
324
+ [
325
+ "offsetDateTime",
326
+ new CSharpType({
327
+ name: "DateTimeOffset",
328
+ namespace: "System",
329
+ isBuiltIn: true,
330
+ isValueType: true,
331
+ }),
332
+ ],
333
+ [
334
+ "unixTimestamp32",
335
+ new CSharpType({
336
+ name: "DateTimeOffset",
337
+ namespace: "System",
338
+ isBuiltIn: true,
339
+ isValueType: true,
340
+ }),
341
+ ],
342
+ [
343
+ "plainTime",
344
+ new CSharpType({ name: "DateTime", namespace: "System", isBuiltIn: true, isValueType: true }),
345
+ ],
346
+ [
347
+ "duration",
348
+ new CSharpType({ name: "TimeSpan", namespace: "System", isBuiltIn: true, isValueType: true }),
349
+ ],
350
+ [
351
+ "numeric",
352
+ new CSharpType({ name: "object", namespace: "System", isBuiltIn: true, isValueType: false }),
353
+ ],
354
+ [
355
+ "url",
356
+ new CSharpType({ name: "string", namespace: "System", isBuiltIn: true, isValueType: false }),
357
+ ],
358
+ [
359
+ "decimal",
360
+ new CSharpType({ name: "decimal", namespace: "System", isBuiltIn: true, isValueType: true }),
361
+ ],
362
+ [
363
+ "decimal128",
364
+ new CSharpType({ name: "decimal", namespace: "System", isBuiltIn: true, isValueType: true }),
365
+ ],
366
+ ]);
367
+ export function getCSharpTypeForStdScalars(program, scalar) {
368
+ const cached = _scalars.get(scalar);
369
+ if (cached !== undefined)
370
+ return cached;
371
+ const builtIn = standardScalars.get(scalar.name);
372
+ if (builtIn !== undefined) {
373
+ _scalars.set(scalar, builtIn);
374
+ if (scalar.name === "numeric" || scalar.name === "integer" || scalar.name === "float") {
375
+ reportDiagnostic(program, {
376
+ code: "no-numeric",
377
+ format: { sourceType: scalar.name, targetType: builtIn?.getTypeReference() },
378
+ target: scalar,
379
+ });
380
+ }
381
+ return builtIn;
382
+ }
383
+ reportDiagnostic(program, {
384
+ code: "unrecognized-scalar",
385
+ format: { typeName: scalar.name },
386
+ target: scalar,
387
+ });
388
+ return new CSharpType({
389
+ name: "Object",
390
+ namespace: "System",
391
+ isBuiltIn: true,
392
+ isValueType: false,
393
+ });
394
+ }
395
+ export function isValueType(program, type) {
396
+ if (type.kind === "Boolean" ||
397
+ type.kind === "Number" ||
398
+ type.kind === "Enum" ||
399
+ type.kind === "EnumMember")
400
+ return true;
401
+ if (type.kind === "Scalar")
402
+ return getCSharpTypeForScalar(program, type).isValueType;
403
+ if (type.kind !== "Union")
404
+ return false;
405
+ return [...type.variants.values()]
406
+ .flatMap((v) => v.type)
407
+ .every((t) => isNullType(t) || isValueType(program, t));
408
+ }
409
+ export function formatComment(text, lineLength = 76, lineEnd = "\n") {
410
+ function getNextLine(target) {
411
+ for (let i = lineLength - 1; i > 0; i--) {
412
+ if ([" ", ";"].includes(target.charAt(i))) {
413
+ return `${target.substring(0, i)}`;
414
+ }
415
+ }
416
+ for (let i = lineLength - 1; i < target.length; i++) {
417
+ if ([" ", ";"].includes(target.charAt(i))) {
418
+ return `${target.substring(0, i)}`;
419
+ }
420
+ }
421
+ return `${target.substring(0, lineLength)}`;
422
+ }
423
+ let remaining = text.replaceAll("\n", " ");
424
+ const lines = [];
425
+ while (remaining.length > lineLength) {
426
+ const currentLine = getNextLine(remaining);
427
+ remaining =
428
+ remaining.length > currentLine.length ? remaining.substring(currentLine.length + 1) : "";
429
+ lines.push(`/// ${currentLine}`);
430
+ }
431
+ if (remaining.length > 0)
432
+ lines.push(`/// ${remaining}`);
433
+ return `///<summary>${lineEnd}${lines.join(lineEnd)}${lineEnd}///</summary>`;
434
+ }
435
+ export function getCSharpIdentifier(name, context = NameCasingType.Class, checkReserved = true) {
436
+ if (name === undefined)
437
+ return "Placeholder";
438
+ name = replaceCSharpReservedWord(name, context);
439
+ switch (context) {
440
+ case NameCasingType.Namespace:
441
+ const parts = [];
442
+ for (const part of name.split(".")) {
443
+ parts.push(getCSharpIdentifier(part, NameCasingType.Class));
444
+ }
445
+ return parts.join(".");
446
+ case NameCasingType.Parameter:
447
+ case NameCasingType.Variable:
448
+ return `${camelCase(name)}`;
449
+ default:
450
+ return `${pascalCase(name)}`;
451
+ }
452
+ }
453
+ export function ensureCSharpIdentifier(program, target, name, context = NameCasingType.Class) {
454
+ let location = "";
455
+ let includeDot = false;
456
+ switch (target.kind) {
457
+ case "Enum":
458
+ location = `enum ${target.name}`;
459
+ break;
460
+ case "EnumMember":
461
+ location = `enum ${target.enum.name}`;
462
+ break;
463
+ case "Interface":
464
+ location = `interface ${target.name}`;
465
+ break;
466
+ case "Model":
467
+ location = `model ${target.name}`;
468
+ break;
469
+ case "ModelProperty": {
470
+ const model = target.model;
471
+ if (!model) {
472
+ reportDiagnostic(program, {
473
+ code: "missing-type-parent",
474
+ format: { type: "ModelProperty", name: target.name },
475
+ target: target,
476
+ });
477
+ }
478
+ else {
479
+ location = `property '${target.name}' in model ${model?.name}`;
480
+ if (!model.name) {
481
+ location = `parameter '${target.name}' in operation`;
482
+ }
483
+ }
484
+ break;
485
+ }
486
+ case "Namespace":
487
+ location = `namespace ${target.name}`;
488
+ let invalid = false;
489
+ for (const part of name.split(".")) {
490
+ if (!isValidCSharpIdentifier(part)) {
491
+ invalid = true;
492
+ }
493
+ }
494
+ if (invalid) {
495
+ reportDiagnostic(program, {
496
+ code: "invalid-identifier",
497
+ format: { identifier: name, location: location },
498
+ target: target.node ?? NoTarget,
499
+ });
500
+ }
501
+ includeDot = true;
502
+ break;
503
+ case "Operation": {
504
+ const parent = target.interface
505
+ ? `interface ${target.interface.name}`
506
+ : `namespace ${target.namespace?.name}`;
507
+ location = `operation ${target.name} in ${parent}`;
508
+ break;
509
+ }
510
+ case "Union":
511
+ location = `union ${target.name}`;
512
+ break;
513
+ case "UnionVariant": {
514
+ location = `variant ${String(target.name)} in union ${target.union.name}`;
515
+ break;
516
+ }
517
+ }
518
+ if (!isValidCSharpIdentifier(name, includeDot)) {
519
+ reportDiagnostic(program, {
520
+ code: "invalid-identifier",
521
+ format: { identifier: name, location: location },
522
+ target: target.node ?? NoTarget,
523
+ });
524
+ return getCSharpIdentifier(transformInvalidIdentifier(name), context);
525
+ }
526
+ return getCSharpIdentifier(name, context);
527
+ }
528
+ export function getModelAttributes(program, entity, cSharpName) {
529
+ return getAttributes(program, entity, cSharpName);
530
+ }
531
+ export function getModelDeclarationName(program, model, defaultSuffix) {
532
+ if (model.name !== null && model.name.length > 0) {
533
+ return ensureCSharpIdentifier(program, model, model.name, NameCasingType.Class);
534
+ }
535
+ if (model.sourceModel && model.sourceModel.name && model.sourceModel.name.length > 0) {
536
+ return ensureCSharpIdentifier(program, model, `${model.sourceModel.name}${defaultSuffix}`, NameCasingType.Class);
537
+ }
538
+ if (model.sourceModels.length > 0) {
539
+ const sourceNames = model.sourceModels
540
+ .filter((m) => m.model.name !== undefined && m.model.name.length > 0)
541
+ .flatMap((m) => ensureCSharpIdentifier(program, model, m.model.name, NameCasingType.Class));
542
+ if (sourceNames.length > 0) {
543
+ return `${sourceNames.join()}${defaultSuffix}`;
544
+ }
545
+ }
546
+ return `Model${defaultSuffix}`;
547
+ }
548
+ export function getModelInstantiationName(program, model, name) {
549
+ const friendlyName = getFriendlyName(program, model);
550
+ if (friendlyName && friendlyName.length > 0)
551
+ return friendlyName;
552
+ if (name === undefined || name.length < 1)
553
+ name = ensureCSharpIdentifier(program, model, "", NameCasingType.Class);
554
+ const names = [name];
555
+ if (model.templateMapper !== undefined) {
556
+ for (const paramType of model.templateMapper.args) {
557
+ if (paramType.entityKind === "Type") {
558
+ switch (paramType.kind) {
559
+ case "Enum":
560
+ case "EnumMember":
561
+ case "Model":
562
+ case "ModelProperty":
563
+ case "Namespace":
564
+ case "Scalar":
565
+ case "Union":
566
+ names.push(getCSharpIdentifier(paramType?.name ?? paramType.kind, NameCasingType.Class));
567
+ break;
568
+ default:
569
+ names.push(getCSharpIdentifier(paramType.kind, NameCasingType.Class));
570
+ break;
571
+ }
572
+ }
573
+ }
574
+ }
575
+ return ensureCSharpIdentifier(program, model, names.join(""), NameCasingType.Class);
576
+ }
577
+ export class ModelInfo {
578
+ visited = [];
579
+ getAllProperties(program, model) {
580
+ if (this.visited.includes(model))
581
+ return undefined;
582
+ this.visited.push(model);
583
+ const props = [];
584
+ for (const prop of model.properties.values())
585
+ props.push(prop);
586
+ if (model.baseModel) {
587
+ const additional = this.getAllProperties(program, model.baseModel);
588
+ if (additional !== undefined)
589
+ props.concat(additional);
590
+ }
591
+ return props;
592
+ }
593
+ filterAllProperties(program, model, filter) {
594
+ if (this.visited.includes(model))
595
+ return undefined;
596
+ this.visited.push(model);
597
+ for (const prop of model.properties.values()) {
598
+ if (filter(prop))
599
+ return prop;
600
+ }
601
+ if (model.baseModel !== undefined) {
602
+ return this.filterAllProperties(program, model.baseModel, filter);
603
+ }
604
+ return undefined;
605
+ }
606
+ }
607
+ export function getPropertySource(program, property) {
608
+ let result = property.model;
609
+ while (property.sourceProperty !== undefined) {
610
+ const current = property.sourceProperty;
611
+ result = current.model;
612
+ property = property.sourceProperty;
613
+ }
614
+ return result;
615
+ }
616
+ export function getSourceModel(program, model) {
617
+ const modelTracker = new Set();
618
+ for (const prop of model.properties.values()) {
619
+ const source = getPropertySource(program, prop);
620
+ if (source === undefined)
621
+ return undefined;
622
+ modelTracker.add(source);
623
+ }
624
+ if (modelTracker.size === 1)
625
+ return [...modelTracker.values()][0];
626
+ return undefined;
627
+ }
628
+ export class HttpMetadata {
629
+ resolveLogicalResponseType(program, response) {
630
+ const responseType = response.type;
631
+ const metaInfo = createMetadataInfo(program, {
632
+ canonicalVisibility: Visibility.Read,
633
+ canShareProperty: (p) => true,
634
+ });
635
+ switch (responseType.kind) {
636
+ case "Model":
637
+ if (responseType.indexer && responseType.indexer.key.name !== "string")
638
+ return responseType;
639
+ if (isRecord(responseType))
640
+ return responseType;
641
+ const bodyProp = new ModelInfo().filterAllProperties(program, responseType, (p) => isBody(program, p) || isBodyRoot(program, p));
642
+ if (bodyProp !== undefined)
643
+ return metaInfo.getEffectivePayloadType(bodyProp.type, Visibility.Read);
644
+ const anyProp = new ModelInfo().filterAllProperties(program, responseType, (p) => !isMetadata(program, p) && !isStatusCode(program, p));
645
+ if (anyProp === undefined)
646
+ return $(program).intrinsic.void;
647
+ if (responseType.name === "") {
648
+ return metaInfo.getEffectivePayloadType(responseType, Visibility.Read);
649
+ }
650
+ break;
651
+ }
652
+ return responseType;
653
+ }
654
+ }
655
+ export function getOperationAttributes(program, entity) {
656
+ return getAttributes(program, entity);
657
+ }
658
+ export function transformInvalidIdentifier(name) {
659
+ const result = new StringBuilder();
660
+ for (let i = 0; i < name.length; ++i) {
661
+ result.pushLiteralSegment(getValidChar(name.charAt(i), i));
662
+ }
663
+ return result.segments.join("");
664
+ }
665
+ export function getOperationVerbDecorator(operation) {
666
+ switch (operation.verb) {
667
+ case "delete":
668
+ return "HttpDelete";
669
+ case "get":
670
+ return "HttpGet";
671
+ case "patch":
672
+ return "HttpPatch";
673
+ case "post":
674
+ return "HttpPost";
675
+ case "put":
676
+ return "HttpPut";
677
+ default:
678
+ return "HttpGet";
679
+ }
680
+ }
681
+ export function hasNonMetadataProperties(program, model) {
682
+ const props = [...model.properties.values()].filter((p) => !isMetadata(program, p));
683
+ return props.length > 0;
684
+ }
685
+ export async function ensureCleanDirectory(program, targetPath) {
686
+ try {
687
+ await program.host.stat(targetPath);
688
+ await program.host.rm(targetPath, { recursive: true });
689
+ }
690
+ catch { }
691
+ await program.host.mkdirp(targetPath);
692
+ }
693
+ export function isValidCSharpIdentifier(identifier, isNamespace = false) {
694
+ if (!isNamespace)
695
+ return identifier?.match(/^[A-Za-z_][\w]*$/) !== null;
696
+ return identifier?.match(/^[A-Za-z_][\w.]*$/) !== null;
697
+ }
698
+ export function replaceCSharpReservedWord(identifier, context) {
699
+ function generateReplacement(input) {
700
+ return [input, `${pascalCase(input)}Name`];
701
+ }
702
+ const contextualWords = [
703
+ "add",
704
+ "allows",
705
+ "alias",
706
+ "and",
707
+ "ascending",
708
+ "args",
709
+ "async",
710
+ "await",
711
+ "by",
712
+ "descending",
713
+ "dynamic",
714
+ "equals",
715
+ "field",
716
+ "file",
717
+ "from",
718
+ "get",
719
+ "global",
720
+ "group",
721
+ "init",
722
+ "into",
723
+ "join",
724
+ "let",
725
+ "managed",
726
+ "nameof",
727
+ "nint",
728
+ "not",
729
+ "notnull",
730
+ "nuint",
731
+ "on",
732
+ "or",
733
+ "orderby",
734
+ "partial",
735
+ "record",
736
+ "remove",
737
+ "required",
738
+ "scoped",
739
+ "select",
740
+ "set",
741
+ "unmanaged",
742
+ "value",
743
+ "var",
744
+ "when",
745
+ "where",
746
+ "with",
747
+ "yield",
748
+ ];
749
+ const reservedWords = [
750
+ "abstract",
751
+ "as",
752
+ "base",
753
+ "bool",
754
+ "boolean",
755
+ "break",
756
+ "byte",
757
+ "case",
758
+ "catch",
759
+ "char",
760
+ "checked",
761
+ "class",
762
+ "const",
763
+ "continue",
764
+ "decimal",
765
+ "default",
766
+ "do",
767
+ "double",
768
+ "else",
769
+ "enum",
770
+ "event",
771
+ "explicit",
772
+ "extern",
773
+ "false",
774
+ "finally",
775
+ "fixed",
776
+ "float",
777
+ "for",
778
+ "foreach",
779
+ "goto",
780
+ "if",
781
+ "implicit",
782
+ "in",
783
+ "int",
784
+ "interface",
785
+ "internal",
786
+ "is",
787
+ "lock",
788
+ "long",
789
+ "namespace",
790
+ "new",
791
+ "null",
792
+ "object",
793
+ "operator",
794
+ "out",
795
+ "override",
796
+ "params",
797
+ "private",
798
+ "protected",
799
+ "public",
800
+ "readonly",
801
+ "ref",
802
+ "return",
803
+ "sbyte",
804
+ "sealed",
805
+ "short",
806
+ "sizeof",
807
+ "stackalloc",
808
+ "static",
809
+ "string",
810
+ "struct",
811
+ "switch",
812
+ "this",
813
+ "throw",
814
+ "true",
815
+ "try",
816
+ "type",
817
+ "typeof",
818
+ "uint",
819
+ "ulong",
820
+ "unchecked",
821
+ "unsafe",
822
+ "ushort",
823
+ "using",
824
+ "virtual",
825
+ "void",
826
+ "volatile",
827
+ "while",
828
+ ];
829
+ const reserved = new Map(reservedWords.concat(contextualWords).map((w) => generateReplacement(w)));
830
+ const check = reserved.get(identifier.toLowerCase());
831
+ if (check !== undefined) {
832
+ return getCSharpIdentifier(check, context, false);
833
+ }
834
+ return identifier;
835
+ }
836
+ export function getValidChar(target, position) {
837
+ if (position === 0) {
838
+ if (target.match(/[A-Za-z_]/))
839
+ return target;
840
+ return `Generated_${target.match(/\w/) ? target : ""}`;
841
+ }
842
+ if (!target.match(/[\w]/))
843
+ return "_";
844
+ return target;
845
+ }
846
+ export function getCSharpStatusCode(entry) {
847
+ switch (entry) {
848
+ case 200:
849
+ return "HttpStatusCode.OK";
850
+ case 201:
851
+ return "HttpStatusCode.Created";
852
+ case 202:
853
+ return "HttpStatusCode.Accepted";
854
+ case 204:
855
+ return "HttpStatusCode.NoContent";
856
+ default:
857
+ return undefined;
858
+ }
859
+ }
860
+ export function isEmptyResponseModel(program, model) {
861
+ if (model.kind !== "Model")
862
+ return false;
863
+ if (model.properties.size === 0)
864
+ return true;
865
+ return (model.properties.size === 1 &&
866
+ isStatusCode(program, [...model.properties.values()][0]) &&
867
+ !isErrorModel(program, model));
868
+ }
869
+ export function isContentTypeHeader(program, parameter) {
870
+ return (isHeader(program, parameter) &&
871
+ (parameter.name === "contentType" || getHeaderFieldName(program, parameter) === "Content-type"));
872
+ }
873
+ export function isValidParameter(program, parameter) {
874
+ return (!isContentTypeHeader(program, parameter) &&
875
+ (parameter.type.kind !== "Intrinsic" || parameter.type.name !== "never") &&
876
+ parameter.model?.name === "");
877
+ }
878
+ /** Determine whether the given parameter is http metadata */
879
+ export function isHttpMetadata(program, property) {
880
+ return (isPathParam(program, property) || isHeader(program, property) || isQueryParam(program, property));
881
+ }
882
+ export function getBusinessLogicCallParameters(parameters) {
883
+ const builder = new StringBuilder();
884
+ const blParameters = parameters.filter((p) => p.operationKind === "BusinessLogic" || p.operationKind === "All");
885
+ let i = 0;
886
+ for (const param of blParameters) {
887
+ builder.push(code `${getBusinessLogicCallParameter(param)}${++i < blParameters.length ? ", " : ""}`);
888
+ }
889
+ return builder.reduce();
890
+ }
891
+ export function getBusinessLogicDeclParameters(parameters) {
892
+ const builder = new StringBuilder();
893
+ const blParameters = parameters.filter((p) => p.operationKind === "BusinessLogic" || p.operationKind === "All");
894
+ let i = 0;
895
+ for (const param of blParameters) {
896
+ builder.push(code `${getBusinessLogicSignatureParameter(param)}${++i < blParameters.length ? ", " : ""}`);
897
+ }
898
+ return builder.reduce();
899
+ }
900
+ export function getHttpDeclParameters(parameters) {
901
+ const builder = new StringBuilder();
902
+ const blParameters = parameters.filter((p) => p.operationKind === "Http" || p.operationKind === "All");
903
+ let i = 0;
904
+ for (const param of blParameters) {
905
+ builder.push(code `${getHttpSignatureParameter(param)}${++i < blParameters.length ? ", " : ""}`);
906
+ }
907
+ return builder.reduce();
908
+ }
909
+ export function getBusinessLogicCallParameter(param) {
910
+ const builder = new StringBuilder();
911
+ builder.push(code `${param.callName}`);
912
+ return builder.reduce();
913
+ }
914
+ export function getBusinessLogicSignatureParameter(param) {
915
+ const builder = new StringBuilder();
916
+ builder.push(code `${param.typeName}${param.optional || param.nullable ? "? " : " "}${param.name}`);
917
+ return builder.reduce();
918
+ }
919
+ export function getHttpSignatureParameter(param) {
920
+ const builder = new StringBuilder();
921
+ builder.push(code `${getHttpParameterDecorator(param)}${getBusinessLogicSignatureParameter(param)}${param.defaultValue === undefined ? "" : code ` = ${typeof param.defaultValue === "boolean" ? code `${param.defaultValue.toString()}` : code `${param.defaultValue}`}`}`);
922
+ return builder.reduce();
923
+ }
924
+ export function getHttpParameterDecorator(parameter) {
925
+ switch (parameter.httpParameterKind) {
926
+ case "query":
927
+ return code `[FromQuery${parameter.httpParameterName ? code `(Name="${parameter.httpParameterName}")` : ""}] `;
928
+ case "header":
929
+ return code `[FromHeader${parameter.httpParameterName ? code `(Name="${parameter.httpParameterName}")` : ""}] `;
930
+ default:
931
+ return "";
932
+ }
933
+ }
934
+ export function getParameterKind(parameter) {
935
+ switch (parameter.type) {
936
+ case "path":
937
+ return "path";
938
+ case "cookie":
939
+ case "header":
940
+ return "header";
941
+ case "query":
942
+ return "query";
943
+ }
944
+ }
945
+ export function canHaveDefault(program, type) {
946
+ switch (type.kind) {
947
+ case "Boolean":
948
+ case "EnumMember":
949
+ case "Enum":
950
+ case "Number":
951
+ case "String":
952
+ case "Scalar":
953
+ case "StringTemplate":
954
+ return true;
955
+ case "ModelProperty":
956
+ return canHaveDefault(program, type.type);
957
+ default:
958
+ return false;
959
+ }
960
+ }
961
+ export class CSharpOperationHelpers {
962
+ constructor(inEmitter) {
963
+ this.emitter = inEmitter;
964
+ this.#anonymousModels = new Map();
965
+ this.#opCache = new Map();
966
+ }
967
+ emitter;
968
+ #anonymousModels;
969
+ #opCache;
970
+ getResponse(program, operation) {
971
+ return new CSharpType({
972
+ name: "void",
973
+ namespace: "System",
974
+ isBuiltIn: true,
975
+ isValueType: true,
976
+ });
977
+ }
978
+ getParameters(program, operation) {
979
+ function safeConcat(...names) {
980
+ return names
981
+ .filter((n) => n !== undefined && n !== null && n.length > 0)
982
+ .flatMap((s) => getCSharpIdentifier(s, NameCasingType.Class))
983
+ .join();
984
+ }
985
+ const cached = this.#opCache.get(operation.operation);
986
+ if (cached)
987
+ return cached;
988
+ const bodyParam = operation.parameters.body;
989
+ const isExplicitBodyParam = bodyParam?.property !== undefined;
990
+ const result = [];
991
+ if (!cached && operation.verb === "get" && operation.parameters.body !== undefined) {
992
+ reportDiagnostic(program, {
993
+ code: "get-request-body",
994
+ target: operation.operation,
995
+ format: {},
996
+ });
997
+ this.#opCache.set(operation.operation, result);
998
+ return result;
999
+ }
1000
+ const validParams = operation.parameters.parameters.filter((p) => isValidParameter(program, p.param));
1001
+ const requiredParams = validParams.filter((p) => p.type === "path" || (!p.param.optional && p.param.defaultValue === undefined));
1002
+ const optionalParams = validParams.filter((p) => p.type !== "path" && (p.param.optional || p.param.defaultValue !== undefined));
1003
+ for (const parameter of requiredParams) {
1004
+ let { typeReference: paramType, defaultValue: paramValue } = this.getTypeInfo(program, parameter.param.type);
1005
+ // cSharp does not allow array defaults in operation parameters
1006
+ if (!canHaveDefault(program, parameter.param)) {
1007
+ paramValue = undefined;
1008
+ }
1009
+ const paramName = ensureCSharpIdentifier(program, parameter.param, parameter.param.name, NameCasingType.Parameter);
1010
+ result.push({
1011
+ isExplicitBody: false,
1012
+ name: paramName,
1013
+ callName: paramName,
1014
+ optional: false,
1015
+ typeName: paramType,
1016
+ defaultValue: paramValue,
1017
+ httpParameterKind: getParameterKind(parameter),
1018
+ httpParameterName: parameter.name,
1019
+ nullable: false,
1020
+ operationKind: "All",
1021
+ });
1022
+ }
1023
+ const overrideParameters = getExplicitBodyParameters(program, operation);
1024
+ if (overrideParameters !== undefined) {
1025
+ for (const overrideParam of overrideParameters) {
1026
+ result.push(overrideParam);
1027
+ }
1028
+ }
1029
+ else if (bodyParam !== undefined && isExplicitBodyParam) {
1030
+ let { typeReference: bodyType, defaultValue: bodyValue, nullableType: isNullable, } = this.getTypeInfo(program, bodyParam.type);
1031
+ if (!canHaveDefault(program, bodyParam.type)) {
1032
+ bodyValue = undefined;
1033
+ }
1034
+ result.push({
1035
+ isExplicitBody: true,
1036
+ httpParameterKind: "body",
1037
+ name: "body",
1038
+ callName: "body",
1039
+ typeName: bodyType,
1040
+ nullable: isNullable,
1041
+ defaultValue: bodyValue,
1042
+ optional: bodyParam.property?.optional ?? false,
1043
+ operationKind: "All",
1044
+ });
1045
+ }
1046
+ else if (bodyParam !== undefined) {
1047
+ switch (bodyParam.type.kind) {
1048
+ case "Model":
1049
+ let tsBody = bodyParam.type;
1050
+ if (!bodyParam.type.name) {
1051
+ tsBody = program.checker.cloneType(bodyParam.type, {
1052
+ name: safeConcat(operation.operation.interface?.name, operation.operation.name, "Request"),
1053
+ });
1054
+ }
1055
+ const { typeReference: bodyType } = this.getTypeInfo(program, tsBody);
1056
+ const bodyName = ensureCSharpIdentifier(program, bodyParam.type, "body", NameCasingType.Parameter);
1057
+ result.push({
1058
+ isExplicitBody: false,
1059
+ httpParameterKind: "body",
1060
+ name: bodyName,
1061
+ callName: bodyName,
1062
+ typeName: bodyType,
1063
+ nullable: false,
1064
+ defaultValue: undefined,
1065
+ optional: false,
1066
+ operationKind: "Http",
1067
+ });
1068
+ for (const [propName, propDef] of bodyParam.type.properties) {
1069
+ let { typeReference: csType, defaultValue: csValue, nullableType: isNullable, } = this.getTypeInfo(program, propDef.type, propDef);
1070
+ // cSharp does not allow array defaults in operation parameters
1071
+ if (!canHaveDefault(program, propDef)) {
1072
+ csValue = undefined;
1073
+ }
1074
+ const paramName = ensureCSharpIdentifier(program, propDef, propName, NameCasingType.Parameter);
1075
+ const refName = ensureCSharpIdentifier(program, propDef, propName, NameCasingType.Property);
1076
+ result.push({
1077
+ isExplicitBody: false,
1078
+ httpParameterKind: "body",
1079
+ name: paramName,
1080
+ callName: `body.${refName}`,
1081
+ typeName: csType,
1082
+ nullable: isNullable,
1083
+ defaultValue: csValue,
1084
+ optional: propDef.optional,
1085
+ operationKind: "BusinessLogic",
1086
+ });
1087
+ }
1088
+ break;
1089
+ case "ModelProperty":
1090
+ {
1091
+ let { typeReference: csType, defaultValue: csValue, nullableType: isNullable, } = this.getTypeInfo(program, bodyParam.type.type, bodyParam.type);
1092
+ if (!canHaveDefault(program, bodyParam.type)) {
1093
+ csValue = undefined;
1094
+ }
1095
+ const optName = ensureCSharpIdentifier(program, bodyParam.type.type, bodyParam.type.name, NameCasingType.Parameter);
1096
+ result.push({
1097
+ isExplicitBody: true,
1098
+ httpParameterKind: "body",
1099
+ name: optName,
1100
+ callName: optName,
1101
+ typeName: csType,
1102
+ nullable: isNullable,
1103
+ defaultValue: csValue,
1104
+ optional: bodyParam.type.optional,
1105
+ operationKind: "All",
1106
+ });
1107
+ }
1108
+ break;
1109
+ default: {
1110
+ let { typeReference: csType, defaultValue: csValue, nullableType: isNullable, } = this.getTypeInfo(program, bodyParam.type);
1111
+ if (!canHaveDefault(program, bodyParam.type)) {
1112
+ csValue = undefined;
1113
+ }
1114
+ result.push({
1115
+ isExplicitBody: true,
1116
+ httpParameterKind: "body",
1117
+ name: "body",
1118
+ callName: "body",
1119
+ typeName: csType,
1120
+ nullable: isNullable,
1121
+ defaultValue: csValue,
1122
+ optional: false,
1123
+ operationKind: "All",
1124
+ });
1125
+ }
1126
+ }
1127
+ }
1128
+ for (const parameter of optionalParams) {
1129
+ const { typeReference: paramType, defaultValue: paramValue, nullableType: isNullable, } = this.getTypeInfo(program, parameter.param.type, parameter.param);
1130
+ const optName = ensureCSharpIdentifier(program, parameter.param, parameter.param.name, NameCasingType.Parameter);
1131
+ result.push({
1132
+ isExplicitBody: false,
1133
+ name: optName,
1134
+ callName: optName,
1135
+ optional: true,
1136
+ typeName: paramType,
1137
+ defaultValue: paramValue,
1138
+ httpParameterKind: getParameterKind(parameter),
1139
+ httpParameterName: parameter.name,
1140
+ nullable: isNullable,
1141
+ operationKind: "All",
1142
+ });
1143
+ }
1144
+ return result;
1145
+ }
1146
+ getDefaultValue(program, tsType, defaultValue) {
1147
+ if (defaultValue === undefined)
1148
+ return undefined;
1149
+ switch (tsType.kind) {
1150
+ case "Enum":
1151
+ if (defaultValue.valueKind === "EnumValue") {
1152
+ const retVal = this.getTypeInfo(program, tsType);
1153
+ return `${retVal.typeReference}.${ensureCSharpIdentifier(program, defaultValue.value, defaultValue.value.name, NameCasingType.Property)}`;
1154
+ }
1155
+ return JSON.stringify(serializeValueAsJson(this.emitter.getProgram(), defaultValue, tsType));
1156
+ case "Union":
1157
+ const { typeReference: typeRef } = this.getUnionInfo(program, tsType);
1158
+ if (defaultValue.valueKind === "StringValue" && isStringEnumType(program, tsType)) {
1159
+ const matches = [...tsType.variants].filter((v) => typeof v[0] === "string" &&
1160
+ v[1].type.kind === "String" &&
1161
+ v[1].type.value === defaultValue.value);
1162
+ if (matches.length === 1) {
1163
+ return `${typeRef}.${ensureCSharpIdentifier(program, matches[0][1], matches[0][0], NameCasingType.Property)}`;
1164
+ }
1165
+ return undefined;
1166
+ }
1167
+ if (defaultValue.valueKind === "StringValue") {
1168
+ return JSON.stringify(serializeValueAsJson(this.emitter.getProgram(), defaultValue, tsType));
1169
+ }
1170
+ return undefined;
1171
+ default:
1172
+ return JSON.stringify(serializeValueAsJson(this.emitter.getProgram(), defaultValue, tsType));
1173
+ }
1174
+ }
1175
+ getTypeInfo(program, tsType, modelProperty) {
1176
+ const myEmitter = this.emitter;
1177
+ function extractStringValue(type, span) {
1178
+ switch (type.kind) {
1179
+ case "String":
1180
+ return type.value;
1181
+ case "Boolean":
1182
+ return `${type.value}`;
1183
+ case "Number":
1184
+ return type.valueAsString;
1185
+ case "StringTemplateSpan":
1186
+ if (type.isInterpolated) {
1187
+ return extractStringValue(type.type, span);
1188
+ }
1189
+ else {
1190
+ return type.type.value;
1191
+ }
1192
+ case "ModelProperty":
1193
+ return extractStringValue(type.type, span);
1194
+ case "EnumMember":
1195
+ if (type.value === undefined)
1196
+ return type.name;
1197
+ if (typeof type.value === "string")
1198
+ return type.value;
1199
+ if (typeof type.value === "number")
1200
+ return `${type.value}`;
1201
+ }
1202
+ reportDiagnostic(myEmitter.getProgram(), {
1203
+ code: "invalid-interpolation",
1204
+ target: span,
1205
+ format: {},
1206
+ });
1207
+ return "";
1208
+ }
1209
+ switch (tsType.kind) {
1210
+ case "String":
1211
+ return {
1212
+ typeReference: code `string`,
1213
+ defaultValue: `"${tsType.value}"`,
1214
+ nullableType: false,
1215
+ };
1216
+ case "StringTemplate":
1217
+ const template = tsType;
1218
+ if (template.stringValue !== undefined)
1219
+ return {
1220
+ typeReference: code `string`,
1221
+ defaultValue: `"${template.stringValue}"`,
1222
+ nullableType: false,
1223
+ };
1224
+ const spanResults = [];
1225
+ for (const span of template.spans) {
1226
+ spanResults.push(extractStringValue(span, span));
1227
+ }
1228
+ return {
1229
+ typeReference: code `string`,
1230
+ defaultValue: `"${spanResults.join("")}"`,
1231
+ nullableType: false,
1232
+ };
1233
+ case "Boolean":
1234
+ return {
1235
+ typeReference: code `bool`,
1236
+ defaultValue: `${tsType.value === true ? true : false}`,
1237
+ nullableType: false,
1238
+ };
1239
+ case "Number":
1240
+ const [type, value] = findNumericType(tsType);
1241
+ return { typeReference: code `${type}`, defaultValue: `${value}`, nullableType: false };
1242
+ case "Tuple":
1243
+ const defaults = [];
1244
+ const [csharpType, isObject] = coalesceTsTypes(program, tsType.values);
1245
+ if (isObject)
1246
+ return { typeReference: "object[]", defaultValue: undefined, nullableType: false };
1247
+ for (const value of tsType.values) {
1248
+ const { defaultValue: itemDefault } = this.getTypeInfo(program, value);
1249
+ defaults.push(itemDefault);
1250
+ }
1251
+ const collectionType = CSharpServiceOptions.getInstance().collectionType;
1252
+ switch (collectionType) {
1253
+ case CollectionType.IEnumerable:
1254
+ return {
1255
+ typeReference: code `IEnumerable<${csharpType.getTypeReference(myEmitter.getContext()?.scope)}>`,
1256
+ defaultValue: `new List<${csharpType.getTypeReference(myEmitter.getContext()?.scope)}> {${defaults.join(", ")}}`,
1257
+ nullableType: csharpType.isNullable,
1258
+ };
1259
+ case CollectionType.Array:
1260
+ default:
1261
+ return {
1262
+ typeReference: code `${csharpType.getTypeReference(myEmitter.getContext()?.scope)}[]`,
1263
+ defaultValue: `[${defaults.join(", ")}]`,
1264
+ nullableType: csharpType.isNullable,
1265
+ };
1266
+ }
1267
+ case "Model":
1268
+ let modelResult;
1269
+ const hasUniqueItems = modelProperty
1270
+ ? getUniqueItems(program, modelProperty) !== undefined
1271
+ : false;
1272
+ const cachedResult = this.#anonymousModels.get(tsType);
1273
+ if (cachedResult && cachedResult.hasUniqueItems === hasUniqueItems) {
1274
+ return cachedResult;
1275
+ }
1276
+ if (isRecord(tsType)) {
1277
+ modelResult = {
1278
+ typeReference: code `${RecordType.getTypeReference(myEmitter.getContext().scope)}`,
1279
+ nullableType: false,
1280
+ hasUniqueItems: hasUniqueItems,
1281
+ };
1282
+ }
1283
+ else if (isArrayModelType(program, tsType)) {
1284
+ const typeReference = code `${this.emitter.emitTypeReference(tsType.indexer.value)}`;
1285
+ modelResult = isByteType(tsType.indexer.value)
1286
+ ? {
1287
+ typeReference: code `${typeReference}[]`,
1288
+ nullableType: false,
1289
+ hasUniqueItems: hasUniqueItems,
1290
+ }
1291
+ : {
1292
+ typeReference: hasUniqueItems
1293
+ ? code `ISet<${typeReference}>`
1294
+ : code `${this.emitter.emitTypeReference(tsType)}`,
1295
+ nullableType: false,
1296
+ hasUniqueItems: hasUniqueItems,
1297
+ };
1298
+ }
1299
+ else {
1300
+ modelResult = {
1301
+ typeReference: code `${this.emitter.emitTypeReference(tsType, this.emitter.getContext())}`,
1302
+ nullableType: false,
1303
+ hasUniqueItems: hasUniqueItems,
1304
+ };
1305
+ }
1306
+ return modelResult;
1307
+ case "ModelProperty":
1308
+ return this.getTypeInfo(program, tsType.type, tsType);
1309
+ case "Enum":
1310
+ if (getEnumType(tsType) === "double")
1311
+ return { typeReference: getDoubleType().getTypeReference(), nullableType: false };
1312
+ return {
1313
+ typeReference: code `${this.emitter.emitTypeReference(tsType)}`,
1314
+ nullableType: false,
1315
+ };
1316
+ case "EnumMember":
1317
+ if (typeof tsType.value === "number") {
1318
+ const stringValue = tsType.value.toString();
1319
+ if (stringValue.includes(".") || stringValue.includes("e"))
1320
+ return { typeReference: "double", defaultValue: stringValue, nullableType: false };
1321
+ return { typeReference: "int", defaultValue: stringValue, nullableType: false };
1322
+ }
1323
+ if (typeof tsType.value === "string") {
1324
+ const retVal = this.getTypeInfo(program, tsType.enum);
1325
+ retVal.defaultValue = `${retVal.typeReference}.${ensureCSharpIdentifier(program, tsType, tsType.name, NameCasingType.Property)}`;
1326
+ return retVal;
1327
+ }
1328
+ return { typeReference: code `object`, nullableType: false };
1329
+ case "Union":
1330
+ return this.getUnionInfo(program, tsType);
1331
+ case "UnionVariant":
1332
+ if (isStringEnumType(program, tsType.union) &&
1333
+ tsType.type.kind === "String" &&
1334
+ typeof tsType.name === "string") {
1335
+ const retVal = this.getUnionInfo(program, tsType.union);
1336
+ retVal.defaultValue = `${retVal.typeReference}.${ensureCSharpIdentifier(program, tsType, tsType.name, NameCasingType.Property)}`;
1337
+ return retVal;
1338
+ }
1339
+ return this.getTypeInfo(program, tsType.type);
1340
+ default:
1341
+ return {
1342
+ typeReference: code `${this.emitter.emitTypeReference(tsType)}`,
1343
+ nullableType: false,
1344
+ };
1345
+ }
1346
+ }
1347
+ getUnionInfo(program, union) {
1348
+ const propResult = getNonNullableTsType(union);
1349
+ if (propResult === undefined || isStringEnumType(program, union)) {
1350
+ return {
1351
+ typeReference: code `${this.emitter.emitTypeReference(union)}`,
1352
+ nullableType: [...union.variants.values()].some((v) => isNullType(v.type)),
1353
+ };
1354
+ }
1355
+ const candidate = this.getTypeInfo(program, propResult.type);
1356
+ candidate.nullableType = propResult.nullable;
1357
+ return candidate;
1358
+ }
1359
+ }
1360
+ export function getExplicitBodyParameters(program, httpOperation) {
1361
+ if (httpOperation.parameters.body && httpOperation.parameters.body.bodyKind === "multipart") {
1362
+ return [
1363
+ {
1364
+ name: "reader",
1365
+ callName: "reader",
1366
+ nullable: false,
1367
+ optional: false,
1368
+ typeName: "MultipartReader",
1369
+ isExplicitBody: false,
1370
+ httpParameterKind: "body",
1371
+ operationKind: "BusinessLogic",
1372
+ },
1373
+ ];
1374
+ }
1375
+ return undefined;
1376
+ }
1377
+ export function findNumericType(type) {
1378
+ const stringValue = type.valueAsString;
1379
+ if (stringValue.includes(".") || stringValue.includes("e"))
1380
+ return ["double", stringValue];
1381
+ return ["int", stringValue];
1382
+ }
1383
+ export function isStringEnumType(program, union) {
1384
+ const baseType = coalesceUnionTypes(program, union);
1385
+ if (!baseType.isBuiltIn || baseType.name !== "string")
1386
+ return false;
1387
+ return ![...union.variants.values()].some((v) => (v.type.kind === "String" ||
1388
+ v.type.kind === "StringTemplate" ||
1389
+ v.type.kind === "StringTemplateSpan") &&
1390
+ typeof v.name !== "string");
1391
+ }
1392
+ export function coalesceUnionTypes(program, union) {
1393
+ const [result, _] = coalesceTsTypes(program, [...union.variants.values()].flatMap((v) => v.type));
1394
+ return result;
1395
+ }
1396
+ export function getNonNullableTsType(union) {
1397
+ const types = [...union.variants.values()];
1398
+ const nulls = types.flatMap((v) => v.type).filter((t) => isNullType(t));
1399
+ const nonNulls = types.flatMap((v) => v.type).filter((t) => !isNullType(t));
1400
+ if (nonNulls.length === 1)
1401
+ return { type: nonNulls[0], nullable: nulls.length > 0 };
1402
+ return undefined;
1403
+ }
1404
+ export function coalesceTsTypes(program, types) {
1405
+ const defaultValue = [
1406
+ new CSharpType({
1407
+ name: "object",
1408
+ namespace: "System",
1409
+ isBuiltIn: true,
1410
+ isValueType: false,
1411
+ }),
1412
+ true,
1413
+ ];
1414
+ let current = undefined;
1415
+ let nullable = false;
1416
+ for (const type of types) {
1417
+ let candidate = undefined;
1418
+ switch (type.kind) {
1419
+ case "Boolean":
1420
+ candidate = new CSharpType({ name: "bool", namespace: "System", isValueType: true });
1421
+ break;
1422
+ case "StringTemplate":
1423
+ case "String":
1424
+ candidate = new CSharpType({ name: "string", namespace: "System", isValueType: false });
1425
+ break;
1426
+ case "Number":
1427
+ const stringValue = type.valueAsString;
1428
+ if (stringValue.includes(".") || stringValue.includes("e")) {
1429
+ candidate = new CSharpType({
1430
+ name: "double",
1431
+ namespace: "System",
1432
+ isValueType: true,
1433
+ });
1434
+ }
1435
+ else {
1436
+ candidate = new CSharpType({ name: "int", namespace: "System", isValueType: true });
1437
+ }
1438
+ break;
1439
+ case "Union":
1440
+ candidate = coalesceUnionTypes(program, type);
1441
+ break;
1442
+ case "Scalar":
1443
+ candidate = getCSharpTypeForScalar(program, type);
1444
+ break;
1445
+ case "Intrinsic":
1446
+ if (isNullType(type)) {
1447
+ nullable = true;
1448
+ candidate = current;
1449
+ }
1450
+ else {
1451
+ return defaultValue;
1452
+ }
1453
+ break;
1454
+ default:
1455
+ return defaultValue;
1456
+ }
1457
+ current = current ?? candidate;
1458
+ if (current === undefined || (candidate !== undefined && !candidate.equals(current)))
1459
+ return defaultValue;
1460
+ }
1461
+ if (current !== undefined && nullable === true)
1462
+ current.isNullable = true;
1463
+ return current === undefined ? defaultValue : [current, false];
1464
+ }
1465
+ export function isRecord(type) {
1466
+ return type.kind === "Model" && type.name === "Record" && type.indexer !== undefined;
1467
+ }
1468
+ export async function getFreePort(minPort, maxPort, tries = 100) {
1469
+ const min = Math.floor(minPort);
1470
+ const max = Math.floor(maxPort);
1471
+ if (tries === 0)
1472
+ return min;
1473
+ const diff = Math.abs(max - min);
1474
+ const port = min + Math.floor(Math.random() * diff);
1475
+ const server = createServer();
1476
+ const free = await checkPort(port);
1477
+ if (free) {
1478
+ return port;
1479
+ }
1480
+ return await getFreePort(min, max, tries--);
1481
+ async function checkPort(port, timeout = 100) {
1482
+ return new Promise((resolve, _) => {
1483
+ server.on("error", (_) => {
1484
+ server.close();
1485
+ resolve(false);
1486
+ });
1487
+ server.listen(port, async () => {
1488
+ try {
1489
+ setTimeout(() => resolve(true), timeout);
1490
+ }
1491
+ catch (e) {
1492
+ resolve(false);
1493
+ }
1494
+ finally {
1495
+ server.close();
1496
+ }
1497
+ });
1498
+ });
1499
+ }
1500
+ }
1501
+ export async function getOpenApiConfig(program) {
1502
+ const root = program.projectRoot;
1503
+ const [options, _] = await resolveCompilerOptions(program.host, {
1504
+ cwd: root,
1505
+ entrypoint: resolvePath(root, "main.tsp"),
1506
+ });
1507
+ const oaiOptions = options.options !== undefined && Object.keys(options.options).includes("@typespec/openapi3")
1508
+ ? options.options["@typespec/openapi3"]
1509
+ : undefined;
1510
+ return {
1511
+ emitted: options.emit !== undefined && options.emit.includes("@typespec/openapi3"),
1512
+ outputDir: oaiOptions?.["emitter-output-dir"],
1513
+ fileName: oaiOptions?.["output-file"],
1514
+ options: oaiOptions,
1515
+ };
1516
+ }
1517
+ export function getStatusCode(program, model) {
1518
+ const statusCodeProperty = new ModelInfo().filterAllProperties(program, model, (p) => isStatusCode(program, p));
1519
+ if (!statusCodeProperty)
1520
+ return undefined;
1521
+ const { type } = statusCodeProperty;
1522
+ switch (type.kind) {
1523
+ case "Union":
1524
+ return {
1525
+ name: statusCodeProperty.name,
1526
+ value: statusCodeProperty.name,
1527
+ requiresConstructorArgument: true,
1528
+ };
1529
+ case "Number":
1530
+ return {
1531
+ value: type.value,
1532
+ };
1533
+ default:
1534
+ return { value: getMinValue(program, statusCodeProperty) ?? `default` };
1535
+ }
1536
+ }
1537
+ export function isByteType(type) {
1538
+ return type.kind === "Scalar" && ["int8", "uint8"].includes(type.name);
1539
+ }
1540
+ export function getImports(scope, visited) {
1541
+ if (scope === undefined)
1542
+ return [];
1543
+ if (!visited)
1544
+ visited = new Set();
1545
+ if (visited.has(scope))
1546
+ return [];
1547
+ visited.add(scope);
1548
+ switch (scope.kind) {
1549
+ case "namespace":
1550
+ return getImports(scope.parentScope, visited);
1551
+ case "sourceFile":
1552
+ return [...scope.sourceFile.imports.keys()];
1553
+ default:
1554
+ return [];
1555
+ }
1556
+ }
1557
+ //# sourceMappingURL=utils.js.map