adorn-api 1.0.6 → 1.0.8

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
@@ -197,7 +197,8 @@ function classifyParameters(parameters, httpMethod, pathParamIndices, checker) {
197
197
  for (let i = 0; i < parameters.length; i++) {
198
198
  const param = parameters[i];
199
199
  if (usedIndices.has(i)) continue;
200
- const typeStr = param.type.getSymbol()?.getName() ?? "";
200
+ const nonNullableType = checker.getNonNullableType(param.type);
201
+ const typeStr = getTypeName(param.type) || getTypeName(nonNullableType);
201
202
  if (typeStr === "Body") {
202
203
  bodyParamIndex = i;
203
204
  usedIndices.add(i);
@@ -223,7 +224,7 @@ function classifyParameters(parameters, httpMethod, pathParamIndices, checker) {
223
224
  usedIndices.add(i);
224
225
  continue;
225
226
  }
226
- const isObj = isObjectType(param.type, checker);
227
+ const isObj = isObjectType(nonNullableType, checker);
227
228
  if (isObj && queryObjectParamIndex === null && !isBodyMethod) {
228
229
  queryObjectParamIndex = i;
229
230
  usedIndices.add(i);
@@ -252,6 +253,12 @@ function isObjectType(type, checker) {
252
253
  if (callSignatures && callSignatures.length > 0) return false;
253
254
  return true;
254
255
  }
256
+ function getTypeName(type) {
257
+ const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
258
+ if (aliasSymbol) return aliasSymbol.getName();
259
+ const symbol = type.getSymbol();
260
+ return symbol?.getName() ?? "";
261
+ }
255
262
  function findDecorator(node, name) {
256
263
  const decorators = ts2.getDecorators(node);
257
264
  if (!decorators) return null;
@@ -286,7 +293,7 @@ function unwrapPromise(type, checker) {
286
293
  }
287
294
 
288
295
  // src/compiler/schema/openapi.ts
289
- import ts5 from "typescript";
296
+ import ts6 from "typescript";
290
297
 
291
298
  // src/compiler/schema/typeToJsonSchema.ts
292
299
  import ts3 from "typescript";
@@ -298,6 +305,9 @@ function typeToJsonSchema(type, ctx, typeNode) {
298
305
  if (type.flags & ts3.TypeFlags.Null) {
299
306
  return { type: "null" };
300
307
  }
308
+ if (isDateType(type, checker)) {
309
+ return { type: "string", format: "date-time" };
310
+ }
301
311
  if (type.flags & ts3.TypeFlags.String) {
302
312
  return { type: "string" };
303
313
  }
@@ -347,6 +357,19 @@ function typeToJsonSchema(type, ctx, typeNode) {
347
357
  }
348
358
  return {};
349
359
  }
360
+ function isDateType(type, checker) {
361
+ const symbol = type.getSymbol();
362
+ const aliasSymbol = type.aliasSymbol;
363
+ if (aliasSymbol && aliasSymbol.flags & ts3.SymbolFlags.Alias) {
364
+ const aliased = checker.getAliasedSymbol(aliasSymbol);
365
+ return aliased?.getName() === "Date";
366
+ }
367
+ if (symbol && symbol.flags & ts3.SymbolFlags.Alias) {
368
+ const aliased = checker.getAliasedSymbol(symbol);
369
+ return aliased?.getName() === "Date";
370
+ }
371
+ return symbol?.getName() === "Date";
372
+ }
350
373
  function isSetType(type, checker) {
351
374
  const symbol = type.getSymbol();
352
375
  if (!symbol) return false;
@@ -744,6 +767,56 @@ function mergeFragments(base, ...frags) {
744
767
  return result;
745
768
  }
746
769
 
770
+ // src/compiler/analyze/extractQueryStyle.ts
771
+ import ts5 from "typescript";
772
+ function extractQueryStyleOptions(checker, method) {
773
+ if (!ts5.canHaveDecorators(method)) return null;
774
+ const decorators = ts5.getDecorators(method);
775
+ if (!decorators || decorators.length === 0) return null;
776
+ for (const decorator of decorators) {
777
+ const expr = decorator.expression;
778
+ const isCall = ts5.isCallExpression(expr);
779
+ const callee = isCall ? expr.expression : expr;
780
+ const args = isCall ? expr.arguments : ts5.factory.createNodeArray([]);
781
+ const sym = checker.getSymbolAtLocation(callee);
782
+ if (!sym) continue;
783
+ const resolved = sym.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym;
784
+ const name = resolved.getName();
785
+ if (name !== "QueryStyle") continue;
786
+ const optsNode = args[0];
787
+ if (!optsNode || !ts5.isObjectLiteralExpression(optsNode)) {
788
+ return {};
789
+ }
790
+ return parseQueryStyleOptions(optsNode);
791
+ }
792
+ return null;
793
+ }
794
+ function parseQueryStyleOptions(node) {
795
+ const opts = {};
796
+ for (const prop of node.properties) {
797
+ if (!ts5.isPropertyAssignment(prop)) continue;
798
+ const name = getPropName(prop.name);
799
+ if (!name) continue;
800
+ if (name === "style" && ts5.isStringLiteral(prop.initializer)) {
801
+ const style = prop.initializer.text;
802
+ opts.style = style;
803
+ } else if (name === "explode" && isBooleanLiteral(prop.initializer)) {
804
+ opts.explode = prop.initializer.kind === ts5.SyntaxKind.TrueKeyword;
805
+ } else if (name === "allowReserved" && isBooleanLiteral(prop.initializer)) {
806
+ opts.allowReserved = prop.initializer.kind === ts5.SyntaxKind.TrueKeyword;
807
+ }
808
+ }
809
+ return opts;
810
+ }
811
+ function getPropName(name) {
812
+ if (ts5.isIdentifier(name)) return name.text;
813
+ if (ts5.isStringLiteral(name)) return name.text;
814
+ return null;
815
+ }
816
+ function isBooleanLiteral(node) {
817
+ return node.kind === ts5.SyntaxKind.TrueKeyword || node.kind === ts5.SyntaxKind.FalseKeyword;
818
+ }
819
+
747
820
  // src/compiler/schema/openapi.ts
748
821
  function generateOpenAPI(controllers, checker, options = {}) {
749
822
  const components = /* @__PURE__ */ new Map();
@@ -835,12 +908,12 @@ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
835
908
  const declarations = typeSymbol.getDeclarations();
836
909
  if (!declarations || declarations.length === 0) return schema;
837
910
  const classDecl = declarations[0];
838
- if (!ts5.isClassDeclaration(classDecl)) return schema;
911
+ if (!ts6.isClassDeclaration(classDecl)) return schema;
839
912
  const result = { ...schema };
840
913
  const props = { ...result.properties };
841
914
  for (const member of classDecl.members) {
842
- if (!ts5.isPropertyDeclaration(member) || !member.name) continue;
843
- const propName = ts5.isIdentifier(member.name) ? member.name.text : null;
915
+ if (!ts6.isPropertyDeclaration(member) || !member.name) continue;
916
+ const propName = ts6.isIdentifier(member.name) ? member.name.text : null;
844
917
  if (!propName) continue;
845
918
  if (!props[propName]) continue;
846
919
  const frags = extractPropertySchemaFragments(ctx.checker, member);
@@ -875,19 +948,36 @@ function buildQueryParameters(operation, ctx, parameters) {
875
948
  if (operation.queryObjectParamIndex !== null) {
876
949
  const queryParam = operation.parameters[operation.queryObjectParamIndex];
877
950
  if (!queryParam) return;
951
+ const queryStyle = extractQueryStyleOptions(ctx.checker, operation.methodDeclaration);
878
952
  const querySchema = typeToJsonSchema(queryParam.type, ctx);
879
- if (!querySchema.properties) return;
880
- const queryObjProps = querySchema.properties;
881
- for (const [propName, propSchema] of Object.entries(queryObjProps)) {
882
- const isRequired = querySchema.required?.includes(propName) ?? false;
883
- const serialization = determineQuerySerialization(propSchema.type);
884
- parameters.push({
885
- name: propName,
953
+ if (queryStyle?.style === "deepObject") {
954
+ const explode = queryStyle.explode ?? true;
955
+ const deepParam = {
956
+ name: queryParam.name,
886
957
  in: "query",
887
- required: isRequired,
888
- schema: propSchema,
889
- ...Object.keys(serialization).length > 0 ? serialization : {}
890
- });
958
+ required: !queryParam.isOptional,
959
+ schema: querySchema.$ref ? { $ref: querySchema.$ref } : querySchema,
960
+ style: "deepObject",
961
+ explode
962
+ };
963
+ if (queryStyle.allowReserved !== void 0) {
964
+ deepParam.allowReserved = queryStyle.allowReserved;
965
+ }
966
+ parameters.push(deepParam);
967
+ } else {
968
+ if (!querySchema.properties) return;
969
+ const queryObjProps = querySchema.properties;
970
+ for (const [propName, propSchema] of Object.entries(queryObjProps)) {
971
+ const isRequired = querySchema.required?.includes(propName) ?? false;
972
+ const serialization = determineQuerySerialization(propSchema.type);
973
+ parameters.push({
974
+ name: propName,
975
+ in: "query",
976
+ required: isRequired,
977
+ schema: propSchema,
978
+ ...Object.keys(serialization).length > 0 ? serialization : {}
979
+ });
980
+ }
891
981
  }
892
982
  }
893
983
  for (const paramIndex of operation.queryParamIndices) {
@@ -957,7 +1047,7 @@ function buildCookieParameters(operation, ctx, parameters) {
957
1047
  }
958
1048
 
959
1049
  // src/compiler/manifest/emit.ts
960
- import ts6 from "typescript";
1050
+ import ts7 from "typescript";
961
1051
  function generateManifest(controllers, checker, version, validationMode = "ajv-runtime") {
962
1052
  const components = /* @__PURE__ */ new Map();
963
1053
  const ctx = {
@@ -978,7 +1068,7 @@ function generateManifest(controllers, checker, version, validationMode = "ajv-r
978
1068
  generator: {
979
1069
  name: "adorn-api",
980
1070
  version,
981
- typescript: ts6.version
1071
+ typescript: ts7.version
982
1072
  },
983
1073
  schemas: {
984
1074
  kind: "openapi-3.1",
@@ -1063,21 +1153,38 @@ function buildQueryArgs(op, ctx, args) {
1063
1153
  if (op.queryObjectParamIndex !== null) {
1064
1154
  const queryParam = op.parameters[op.queryObjectParamIndex];
1065
1155
  if (queryParam) {
1156
+ const queryStyle = extractQueryStyleOptions(ctx.checker, op.methodDeclaration);
1066
1157
  const querySchema = typeToJsonSchema(queryParam.type, ctx);
1067
- if (!querySchema.properties) return;
1068
- for (const [propName, propSchema] of Object.entries(querySchema.properties)) {
1069
- const isRequired = querySchema.required?.includes(propName) ?? false;
1070
- let schemaRef = propSchema.$ref;
1071
- if (!schemaRef) {
1072
- schemaRef = "#/components/schemas/InlineQueryParam";
1073
- }
1158
+ if (queryStyle?.style === "deepObject") {
1159
+ const schemaRef = querySchema.$ref ?? "#/components/schemas/InlineQueryParam";
1074
1160
  args.query.push({
1075
- name: propName,
1161
+ name: queryParam.name,
1076
1162
  index: queryParam.index,
1077
- required: !isRequired,
1163
+ required: !queryParam.isOptional,
1078
1164
  schemaRef,
1079
- schemaType: propSchema.type
1165
+ schemaType: querySchema.type,
1166
+ serialization: {
1167
+ style: "deepObject",
1168
+ explode: queryStyle.explode ?? true,
1169
+ allowReserved: queryStyle.allowReserved
1170
+ }
1080
1171
  });
1172
+ } else {
1173
+ if (!querySchema.properties) return;
1174
+ for (const [propName, propSchema] of Object.entries(querySchema.properties)) {
1175
+ const isRequired = querySchema.required?.includes(propName) ?? false;
1176
+ let schemaRef = propSchema.$ref;
1177
+ if (!schemaRef) {
1178
+ schemaRef = "#/components/schemas/InlineQueryParam";
1179
+ }
1180
+ args.query.push({
1181
+ name: propName,
1182
+ index: queryParam.index,
1183
+ required: !isRequired,
1184
+ schemaRef,
1185
+ schemaType: propSchema.type
1186
+ });
1187
+ }
1081
1188
  }
1082
1189
  }
1083
1190
  }
@@ -1386,7 +1493,7 @@ async function isStale(params) {
1386
1493
  // src/compiler/cache/writeCache.ts
1387
1494
  import fs3 from "fs";
1388
1495
  import path3 from "path";
1389
- import ts8 from "typescript";
1496
+ import ts9 from "typescript";
1390
1497
  function statMtimeMs2(p) {
1391
1498
  return fs3.statSync(p).mtimeMs;
1392
1499
  }
@@ -1419,7 +1526,7 @@ function writeCache(params) {
1419
1526
  generator: {
1420
1527
  name: "adorn-api",
1421
1528
  version: params.adornVersion,
1422
- typescript: ts8.version
1529
+ typescript: ts9.version
1423
1530
  },
1424
1531
  project: {
1425
1532
  tsconfigPath: params.tsconfigAbs,
@@ -1432,7 +1539,7 @@ function writeCache(params) {
1432
1539
  }
1433
1540
 
1434
1541
  // src/cli.ts
1435
- import ts9 from "typescript";
1542
+ import ts10 from "typescript";
1436
1543
  import process from "process";
1437
1544
  var ADORN_VERSION = "0.1.0";
1438
1545
  function log(msg) {
@@ -1460,7 +1567,7 @@ async function buildCommand(args) {
1460
1567
  outDir: outputDir,
1461
1568
  project: projectPath,
1462
1569
  adornVersion: ADORN_VERSION,
1463
- typescriptVersion: ts9.version
1570
+ typescriptVersion: ts10.version
1464
1571
  });
1465
1572
  if (!stale.stale) {
1466
1573
  log("adorn-api: artifacts up-to-date");