adorn-api 1.0.9 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -137,6 +137,7 @@ function analyzeMethod(node, className, checker) {
137
137
  if (!signature) return null;
138
138
  let returnType = checker.getReturnTypeOfSignature(signature);
139
139
  returnType = unwrapPromise(returnType, checker);
140
+ const returnTypeNode = unwrapPromiseTypeNode(node.type);
140
141
  const parameters = [];
141
142
  for (let i = 0; i < node.parameters.length; i++) {
142
143
  const param = node.parameters[i];
@@ -153,7 +154,7 @@ function analyzeMethod(node, className, checker) {
153
154
  }
154
155
  const pathParamNames = extractPathParams(path4);
155
156
  const pathParamIndices = matchPathParamsToIndices(pathParamNames, parameters);
156
- const { bodyParamIndex, queryParamIndices, queryObjectParamIndex, headerObjectParamIndex, cookieObjectParamIndex, bodyContentType } = classifyParameters(parameters, httpMethod, pathParamIndices, checker);
157
+ const { bodyParamIndex, queryParamIndices, queryObjectParamIndex, headerObjectParamIndex, cookieObjectParamIndex, paginationParamIndex, bodyContentType } = classifyParameters(parameters, httpMethod, pathParamIndices, checker);
157
158
  return {
158
159
  methodName,
159
160
  httpMethod,
@@ -161,6 +162,7 @@ function analyzeMethod(node, className, checker) {
161
162
  operationId: defaultOperationId(className, methodName),
162
163
  methodDeclaration: node,
163
164
  returnType,
165
+ returnTypeNode,
164
166
  parameters,
165
167
  pathParamIndices,
166
168
  bodyParamIndex,
@@ -168,6 +170,7 @@ function analyzeMethod(node, className, checker) {
168
170
  queryObjectParamIndex,
169
171
  headerObjectParamIndex,
170
172
  cookieObjectParamIndex,
173
+ paginationParamIndex,
171
174
  bodyContentType
172
175
  };
173
176
  }
@@ -193,6 +196,7 @@ function classifyParameters(parameters, httpMethod, pathParamIndices, checker) {
193
196
  let queryObjectParamIndex = null;
194
197
  let headerObjectParamIndex = null;
195
198
  let cookieObjectParamIndex = null;
199
+ let paginationParamIndex = null;
196
200
  const isBodyMethod = ["POST", "PUT", "PATCH"].includes(httpMethod);
197
201
  for (let i = 0; i < parameters.length; i++) {
198
202
  const param = parameters[i];
@@ -219,6 +223,11 @@ function classifyParameters(parameters, httpMethod, pathParamIndices, checker) {
219
223
  usedIndices.add(i);
220
224
  continue;
221
225
  }
226
+ if (typeStr === "PaginationParams") {
227
+ paginationParamIndex = i;
228
+ usedIndices.add(i);
229
+ continue;
230
+ }
222
231
  if (isBodyMethod && bodyParamIndex === null) {
223
232
  bodyParamIndex = i;
224
233
  usedIndices.add(i);
@@ -239,6 +248,7 @@ function classifyParameters(parameters, httpMethod, pathParamIndices, checker) {
239
248
  queryObjectParamIndex,
240
249
  headerObjectParamIndex,
241
250
  cookieObjectParamIndex,
251
+ paginationParamIndex,
242
252
  bodyContentType: void 0
243
253
  };
244
254
  }
@@ -291,6 +301,15 @@ function unwrapPromise(type, checker) {
291
301
  }
292
302
  return type;
293
303
  }
304
+ function unwrapPromiseTypeNode(typeNode) {
305
+ if (!typeNode) return void 0;
306
+ if (ts2.isTypeReferenceNode(typeNode)) {
307
+ if (ts2.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise") {
308
+ return typeNode.typeArguments?.[0] ?? typeNode;
309
+ }
310
+ }
311
+ return typeNode;
312
+ }
294
313
 
295
314
  // src/compiler/schema/openapi.ts
296
315
  import ts6 from "typescript";
@@ -337,10 +356,10 @@ function typeToJsonSchema(type, ctx, typeNode) {
337
356
  return { type: "boolean", enum: [intrinsic === "true"] };
338
357
  }
339
358
  if (type.isUnion()) {
340
- return handleUnion(type.types, ctx, typeNode);
359
+ return handleUnion(type, ctx, typeNode);
341
360
  }
342
361
  if (type.isIntersection()) {
343
- return handleIntersection(type.types, ctx, typeNode);
362
+ return handleIntersection(type, ctx, typeNode);
344
363
  }
345
364
  if (checker.isArrayType(type)) {
346
365
  const typeArgs = type.typeArguments;
@@ -377,56 +396,96 @@ function isSetType(type, checker) {
377
396
  if (name === "Set") return true;
378
397
  return false;
379
398
  }
380
- function handleUnion(types, ctx, typeNode) {
381
- const nullType = types.find((t) => t.flags & ts3.TypeFlags.Null);
382
- const otherTypes = types.filter((t) => !(t.flags & ts3.TypeFlags.Null) && !(t.flags & ts3.TypeFlags.Undefined));
383
- const allStringLiterals = otherTypes.every((t) => t.flags & ts3.TypeFlags.StringLiteral);
384
- if (allStringLiterals && otherTypes.length > 0) {
385
- const enumValues = otherTypes.map((t) => t.value);
386
- const schema = { type: "string", enum: enumValues };
387
- if (nullType) {
388
- schema.type = ["string", "null"];
389
- }
390
- return schema;
399
+ function getSchemaName(type, typeNode) {
400
+ const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
401
+ const aliasName = aliasSymbol?.getName();
402
+ if (aliasName && aliasName !== "__type") {
403
+ return aliasName;
391
404
  }
392
- if (otherTypes.length === 1 && nullType) {
393
- const innerSchema = typeToJsonSchema(otherTypes[0], ctx);
394
- if (typeof innerSchema.type === "string") {
395
- innerSchema.type = [innerSchema.type, "null"];
396
- }
397
- return innerSchema;
398
- }
399
- if (otherTypes.length > 1) {
400
- const branches = otherTypes.map((t) => typeToJsonSchema(t, ctx));
401
- const hasNull = !!nullType;
402
- const result = {};
403
- if (hasNull) {
404
- result.anyOf = [...branches, { type: "null" }];
405
- } else {
406
- result.anyOf = branches;
407
- }
408
- const discriminatorResult = detectDiscriminatedUnion(otherTypes, ctx, branches);
409
- if (discriminatorResult) {
410
- result.oneOf = branches;
411
- result.discriminator = discriminatorResult;
412
- }
413
- return result;
405
+ const symbol = type.getSymbol();
406
+ const symbolName = symbol?.getName?.();
407
+ if (symbolName && symbolName !== "__type") {
408
+ return symbolName;
414
409
  }
415
- if (otherTypes.length === 1) {
416
- return typeToJsonSchema(otherTypes[0], ctx);
410
+ const nodeName = getExplicitTypeNameFromNode(typeNode);
411
+ if (nodeName && nodeName !== "__type") {
412
+ return nodeName;
417
413
  }
418
- return {};
414
+ return null;
419
415
  }
420
- function handleIntersection(types, ctx, typeNode) {
421
- const brandCollapsed = tryCollapseBrandedIntersection(types, ctx, typeNode);
422
- if (brandCollapsed) {
423
- return brandCollapsed;
416
+ function buildNamedSchema(type, ctx, typeNode, build) {
417
+ const name = getSchemaName(type, typeNode);
418
+ if (!name) {
419
+ return build();
424
420
  }
425
- const allOf = [];
426
- for (const t of types) {
427
- allOf.push(typeToJsonSchema(t, ctx));
421
+ const { components, typeStack } = ctx;
422
+ if (components.has(name) || typeStack.has(type)) {
423
+ return { $ref: `#/components/schemas/${name}` };
428
424
  }
429
- return { allOf };
425
+ typeStack.add(type);
426
+ const schema = build();
427
+ typeStack.delete(type);
428
+ if (!components.has(name)) {
429
+ components.set(name, schema);
430
+ }
431
+ return { $ref: `#/components/schemas/${name}` };
432
+ }
433
+ function handleUnion(type, ctx, typeNode) {
434
+ return buildNamedSchema(type, ctx, typeNode, () => {
435
+ const types = type.types;
436
+ const nullType = types.find((t) => t.flags & ts3.TypeFlags.Null);
437
+ const otherTypes = types.filter((t) => !(t.flags & ts3.TypeFlags.Null) && !(t.flags & ts3.TypeFlags.Undefined));
438
+ const allStringLiterals = otherTypes.every((t) => t.flags & ts3.TypeFlags.StringLiteral);
439
+ if (allStringLiterals && otherTypes.length > 0) {
440
+ const enumValues = otherTypes.map((t) => t.value);
441
+ const schema = { type: "string", enum: enumValues };
442
+ if (nullType) {
443
+ schema.type = ["string", "null"];
444
+ }
445
+ return schema;
446
+ }
447
+ if (otherTypes.length === 1 && nullType) {
448
+ const innerSchema = typeToJsonSchema(otherTypes[0], ctx);
449
+ if (typeof innerSchema.type === "string") {
450
+ innerSchema.type = [innerSchema.type, "null"];
451
+ }
452
+ return innerSchema;
453
+ }
454
+ if (otherTypes.length > 1) {
455
+ const branches = otherTypes.map((t) => typeToJsonSchema(t, ctx));
456
+ const hasNull = !!nullType;
457
+ const result = {};
458
+ if (hasNull) {
459
+ result.anyOf = [...branches, { type: "null" }];
460
+ } else {
461
+ result.anyOf = branches;
462
+ }
463
+ const discriminatorResult = detectDiscriminatedUnion(otherTypes, ctx, branches);
464
+ if (discriminatorResult) {
465
+ result.oneOf = branches;
466
+ result.discriminator = discriminatorResult;
467
+ }
468
+ return result;
469
+ }
470
+ if (otherTypes.length === 1) {
471
+ return typeToJsonSchema(otherTypes[0], ctx);
472
+ }
473
+ return {};
474
+ });
475
+ }
476
+ function handleIntersection(type, ctx, typeNode) {
477
+ return buildNamedSchema(type, ctx, typeNode, () => {
478
+ const types = type.types;
479
+ const brandCollapsed = tryCollapseBrandedIntersection(types, ctx, typeNode);
480
+ if (brandCollapsed) {
481
+ return brandCollapsed;
482
+ }
483
+ const allOf = [];
484
+ for (const t of types) {
485
+ allOf.push(typeToJsonSchema(t, ctx));
486
+ }
487
+ return { allOf };
488
+ });
430
489
  }
431
490
  function tryCollapseBrandedIntersection(types, ctx, typeNode) {
432
491
  const { checker } = ctx;
@@ -557,8 +616,8 @@ function handleObjectType(type, ctx, typeNode) {
557
616
  typeStack.delete(type);
558
617
  return schema;
559
618
  }
560
- function getTypeNameFromNode(typeNode, ctx) {
561
- if (!typeNode) return `Anonymous_${ctx.typeNameStack.length}`;
619
+ function getExplicitTypeNameFromNode(typeNode) {
620
+ if (!typeNode) return null;
562
621
  if (ts3.isTypeReferenceNode(typeNode)) {
563
622
  if (ts3.isIdentifier(typeNode.typeName)) {
564
623
  return typeNode.typeName.text;
@@ -569,6 +628,11 @@ function getTypeNameFromNode(typeNode, ctx) {
569
628
  return typeNode.parent.name.text;
570
629
  }
571
630
  }
631
+ return null;
632
+ }
633
+ function getTypeNameFromNode(typeNode, ctx) {
634
+ const explicitName = getExplicitTypeNameFromNode(typeNode);
635
+ if (explicitName) return explicitName;
572
636
  return `Anonymous_${ctx.typeNameStack.length}`;
573
637
  }
574
638
  function buildObjectSchema(type, ctx, typeNode) {
@@ -867,7 +931,7 @@ function buildOperation(operation, ctx, controllerConsumes) {
867
931
  if (parameters.length > 0) {
868
932
  op.parameters = parameters;
869
933
  }
870
- const responseSchema = typeToJsonSchema(operation.returnType, ctx);
934
+ const responseSchema = typeToJsonSchema(operation.returnType, ctx, operation.returnTypeNode);
871
935
  const status = operation.httpMethod === "POST" ? 201 : 200;
872
936
  op.responses[status] = {
873
937
  description: status === 201 ? "Created" : "OK",
@@ -1085,7 +1149,8 @@ function buildOperationEntry(op, ctx) {
1085
1149
  path: [],
1086
1150
  query: [],
1087
1151
  headers: [],
1088
- cookies: []
1152
+ cookies: [],
1153
+ paginationParamIndex: op.paginationParamIndex
1089
1154
  };
1090
1155
  buildPathArgs(op, ctx, args);
1091
1156
  buildQueryArgs(op, ctx, args);
@@ -1104,7 +1169,7 @@ function buildOperationEntry(op, ctx) {
1104
1169
  };
1105
1170
  }
1106
1171
  }
1107
- const responseSchema = typeToJsonSchema(op.returnType, ctx);
1172
+ const responseSchema = typeToJsonSchema(op.returnType, ctx, op.returnTypeNode);
1108
1173
  const status = op.httpMethod === "POST" ? 201 : 200;
1109
1174
  let schemaRef = responseSchema.$ref;
1110
1175
  let isArray = false;