api 5.0.8 → 6.1.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.
package/dist/cache.d.ts CHANGED
@@ -3,8 +3,11 @@ import 'isomorphic-fetch';
3
3
  import Fetcher from './fetcher';
4
4
  type CacheStore = Record<string, {
5
5
  hash: string;
6
- path?: string;
7
6
  original: string | OASDocument;
7
+ /**
8
+ * @deprecated Deprecated in v4.5.0 in favor of `hash`.
9
+ */
10
+ path?: string;
8
11
  title?: string;
9
12
  version?: string;
10
13
  }>;
@@ -2,19 +2,22 @@ import type Storage from '../../storage';
2
2
  import type { InstallerOptions } from '../language';
3
3
  import type Oas from 'oas';
4
4
  import type { Operation } from 'oas';
5
- import type { ClassDeclaration } from 'ts-morph';
5
+ import type { ClassDeclaration, JSDocStructure, JSDocTagStructure, OptionalKind } from 'ts-morph';
6
6
  import { Project } from 'ts-morph';
7
7
  import CodeGeneratorLanguage from '../language';
8
8
  export interface TSGeneratorOptions {
9
- outputJS?: boolean;
10
9
  compilerTarget?: 'cjs' | 'esm';
10
+ outputJS?: boolean;
11
11
  }
12
12
  interface OperationTypeHousing {
13
+ operation: Operation;
13
14
  types: {
14
15
  params?: false | Record<'body' | 'formData' | 'metadata', string>;
15
- responses?: Record<string, string>;
16
+ responses?: Record<string | number, {
17
+ description?: string;
18
+ type: string;
19
+ }>;
16
20
  };
17
- operation: Operation;
18
21
  }
19
22
  export default class TSGenerator extends CodeGeneratorLanguage {
20
23
  project: Project;
@@ -57,6 +60,17 @@ export default class TSGenerator extends CodeGeneratorLanguage {
57
60
  * @see {@link https://npm.im/json-schema-to-ts}
58
61
  */
59
62
  createTypesFile(): import("ts-morph").SourceFile;
63
+ /**
64
+ * Add a new JSDoc `@tag` to an existing docblock.
65
+ *
66
+ */
67
+ static addTagToDocblock(docblock: OptionalKind<JSDocStructure>, tag: OptionalKind<JSDocTagStructure>): {
68
+ tags: OptionalKind<JSDocTagStructure>[];
69
+ description?: string | import("ts-morph").WriterFunction;
70
+ leadingTrivia?: string | import("ts-morph").WriterFunction | (string | import("ts-morph").WriterFunction)[];
71
+ trailingTrivia?: string | import("ts-morph").WriterFunction | (string | import("ts-morph").WriterFunction)[];
72
+ kind?: import("ts-morph").StructureKind.JSDoc;
73
+ };
60
74
  /**
61
75
  * Create operation accessors on the SDK.
62
76
  *
@@ -83,7 +97,10 @@ export default class TSGenerator extends CodeGeneratorLanguage {
83
97
  *
84
98
  */
85
99
  prepareResponseTypesForOperation(operation: Operation, operationId: string): {
86
- [x: string]: string;
100
+ [x: string]: {
101
+ type: string;
102
+ description: any;
103
+ };
87
104
  };
88
105
  /**
89
106
  * Add a given schema into our schema dataset that we'll be be exporting as types.
@@ -163,11 +163,18 @@ var TSGenerator = /** @class */ (function (_super) {
163
163
  // This will install the installed SDK as a dependency within the current working directory,
164
164
  // adding `@api/<sdk identifier>` as a dependency there so you can load it with
165
165
  // `require('@api/<sdk identifier>)`.
166
- return [2 /*return*/, (0, execa_1["default"])('npm', __spreadArray(__spreadArray([], npmInstall, true), [installDir], false).filter(Boolean)).then(function (res) {
166
+ return [2 /*return*/, (0, execa_1["default"])('npm', __spreadArray(__spreadArray([], npmInstall, true), [installDir], false).filter(Boolean))
167
+ .then(function (res) {
167
168
  if (opts.dryRun) {
168
169
  (opts.logger ? opts.logger : logger_1["default"])(res.command);
169
170
  (opts.logger ? opts.logger : logger_1["default"])(res.stdout);
170
171
  }
172
+ })["catch"](function (err) {
173
+ if (opts.dryRun) {
174
+ (opts.logger ? opts.logger : logger_1["default"])(err.message);
175
+ return;
176
+ }
177
+ throw err;
171
178
  })];
172
179
  }
173
180
  });
@@ -464,6 +471,16 @@ var TSGenerator = /** @class */ (function (_super) {
464
471
  });
465
472
  return sourceFile;
466
473
  };
474
+ /**
475
+ * Add a new JSDoc `@tag` to an existing docblock.
476
+ *
477
+ */
478
+ TSGenerator.addTagToDocblock = function (docblock, tag) {
479
+ var _a;
480
+ var tags = (_a = docblock.tags) !== null && _a !== void 0 ? _a : [];
481
+ tags.push(tag);
482
+ return __assign(__assign({}, docblock), { tags: tags });
483
+ };
467
484
  /**
468
485
  * Create operation accessors on the SDK.
469
486
  *
@@ -488,7 +505,10 @@ var TSGenerator = /** @class */ (function (_super) {
488
505
  return writer;
489
506
  };
490
507
  if (summary && description) {
491
- docblock.tags = [{ tagName: 'summary', text: (0, util_1.docblockEscape)((0, util_1.wordWrap)(summary)) }];
508
+ docblock = TSGenerator.addTagToDocblock(docblock, {
509
+ tagName: 'summary',
510
+ text: (0, util_1.docblockEscape)((0, util_1.wordWrap)(summary))
511
+ });
492
512
  }
493
513
  }
494
514
  var hasOptionalBody = false;
@@ -516,9 +536,9 @@ var TSGenerator = /** @class */ (function (_super) {
516
536
  }
517
537
  var returnType = 'Promise<FetchResponse<number, unknown>>';
518
538
  if (responseTypes) {
519
- returnType = "Promise<".concat(Object.entries(responseTypes)
539
+ var returnTypes = Object.entries(responseTypes)
520
540
  .map(function (_a) {
521
- var status = _a[0], responseType = _a[1];
541
+ var status = _a[0], _b = _a[1], responseDescription = _b.description, responseType = _b.type;
522
542
  if (status.toLowerCase() === 'default') {
523
543
  return "FetchResponse<number, ".concat(responseType, ">");
524
544
  }
@@ -529,17 +549,42 @@ var TSGenerator = /** @class */ (function (_super) {
529
549
  // it and should instead fall back to treating it as an unknown number.
530
550
  return "FetchResponse<number, ".concat(responseType, ">");
531
551
  }
552
+ if (Number(statusPrefix) >= 4) {
553
+ docblock = TSGenerator.addTagToDocblock(docblock, {
554
+ tagName: 'throws',
555
+ text: "FetchError<".concat(status, ", ").concat(responseType, ">").concat(responseDescription ? (0, util_1.docblockEscape)((0, util_1.wordWrap)(" ".concat(responseDescription))) : '')
556
+ });
557
+ return false;
558
+ }
532
559
  _this.usesHTTPMethodRangeInterface = true;
533
560
  return "FetchResponse<HTTPMethodRange<".concat(statusPrefix, "00, ").concat(statusPrefix, "99>, ").concat(responseType, ">");
534
561
  }
562
+ // 400 and 500 status code families are thrown as exceptions so adding them as a possible
563
+ // return type isn't valid.
564
+ if (Number(status) >= 400) {
565
+ docblock = TSGenerator.addTagToDocblock(docblock, {
566
+ tagName: 'throws',
567
+ text: "FetchError<".concat(status, ", ").concat(responseType, ">").concat(responseDescription ? (0, util_1.docblockEscape)((0, util_1.wordWrap)(" ".concat(responseDescription))) : '')
568
+ });
569
+ return false;
570
+ }
535
571
  return "FetchResponse<".concat(status, ", ").concat(responseType, ">");
536
572
  })
537
- .join(' | '), ">");
573
+ .filter(Boolean)
574
+ .join(' | ');
575
+ // If all of our documented responses are for error status codes then all we can document for
576
+ // anything else that might happen is `unknown`.
577
+ returnType = "Promise<".concat(returnTypes.length ? returnTypes : 'FetchResponse<number, unknown>', ">");
538
578
  }
579
+ var shouldAddAltTypedOverloads = Object.keys(parameters).length === 2 && hasOptionalBody && !hasOptionalMetadata;
539
580
  var operationIdAccessor = this.sdk.addMethod({
540
581
  name: operationId,
541
582
  returnType: returnType,
542
- docs: Object.keys(docblock).length ? [docblock] : null,
583
+ // If we're going to be creating typed method overloads for optional body an metadata handling
584
+ // we should only add a docblock to the first overload we create because IDE Intellisense will
585
+ // always use that and adding a docblock to all three will bloat the SDK with unused and
586
+ // unsurfaced method documentation.
587
+ docs: shouldAddAltTypedOverloads ? null : Object.keys(docblock).length ? [docblock] : null,
543
588
  statements: function (writer) {
544
589
  /**
545
590
  * @example return this.core.fetch('/pet/findByStatus', 'get', body, metadata);
@@ -569,10 +614,6 @@ var TSGenerator = /** @class */ (function (_super) {
569
614
  // If we have both body and metadata parameters but only body is optional we need to create
570
615
  // a couple function overloads as Typescript doesn't let us have an optional method parameter
571
616
  // come before one that's required.
572
- //
573
- // None of these accessor overloads will receive a docblock because the original will have
574
- // that covered.
575
- var shouldAddAltTypedOverloads = Object.keys(parameters).length === 2 && hasOptionalBody && !hasOptionalMetadata;
576
617
  if (shouldAddAltTypedOverloads) {
577
618
  // Create an overload that has both `body` and `metadata` parameters as required.
578
619
  operationIdAccessor.addOverload({
@@ -586,8 +627,7 @@ var TSGenerator = /** @class */ (function (_super) {
586
627
  // Create an overload that just has a single `metadata` parameter.
587
628
  operationIdAccessor.addOverload({
588
629
  parameters: [__assign({}, parameters.metadata)],
589
- returnType: returnType,
590
- docs: Object.keys(docblock).length ? [docblock] : null
630
+ returnType: returnType
591
631
  });
592
632
  // Create an overload that has both `body` and `metadata` parameters as optional. Even though
593
633
  // our `metadata` parameter is actually required for this operation this is the only way we're
@@ -739,7 +779,7 @@ var TSGenerator = /** @class */ (function (_super) {
739
779
  var res = Object.entries(schemas)
740
780
  .map(function (_a) {
741
781
  var _b;
742
- var status = _a[0], schema = _a[1].schema;
782
+ var status = _a[0], _c = _a[1], description = _c.description, schema = _c.schema;
743
783
  var typeName;
744
784
  if (typeof schema === 'string' && schema.startsWith('::convert::')) {
745
785
  // If this schema is a string and has our conversion prefix then we've already created
@@ -756,7 +796,10 @@ var TSGenerator = /** @class */ (function (_super) {
756
796
  return _b = {},
757
797
  // Types are prefixed with `types.` because that's how we're importing them from
758
798
  // `types.d.ts`.
759
- _b[status] = "types.".concat(typeName),
799
+ _b[status] = {
800
+ type: "types.".concat(typeName),
801
+ description: description
802
+ },
760
803
  _b;
761
804
  })
762
805
  .reduce(function (prev, next) { return Object.assign(prev, next); }, {});
@@ -43,7 +43,6 @@ var commander_1 = require("commander");
43
43
  var figures_1 = __importDefault(require("figures"));
44
44
  var oas_1 = __importDefault(require("oas"));
45
45
  var ora_1 = __importDefault(require("ora"));
46
- var validate_npm_package_name_1 = __importDefault(require("validate-npm-package-name"));
47
46
  var fetcher_1 = __importDefault(require("../../fetcher"));
48
47
  var codegen_1 = __importDefault(require("../codegen"));
49
48
  var prompt_1 = __importDefault(require("../lib/prompt"));
@@ -55,6 +54,7 @@ cmd
55
54
  .name('install')
56
55
  .description('install an API SDK into your codebase')
57
56
  .argument('<uri>', 'an API to install')
57
+ .option('-i, --identifier <identifier>', 'API identifier (eg. `@api/petstore`)')
58
58
  .addOption(new commander_1.Option('-l, --lang <language>', 'SDK language').choices([
59
59
  'js',
60
60
  'js-cjs',
@@ -107,10 +107,17 @@ cmd
107
107
  // @todo
108
108
  // logger(`It looks like you already have this API installed. Would you like to update it?`);
109
109
  }
110
- if (!fetcher_1["default"].isAPIRegistryUUID(uri)) return [3 /*break*/, 6];
110
+ if (!options.identifier) return [3 /*break*/, 6];
111
+ // `Storage.isIdentifierValid` will throw an exception if an identifier is invalid.
112
+ if (storage_1["default"].isIdentifierValid(options.identifier)) {
113
+ identifier = options.identifier;
114
+ }
115
+ return [3 /*break*/, 9];
116
+ case 6:
117
+ if (!fetcher_1["default"].isAPIRegistryUUID(uri)) return [3 /*break*/, 7];
111
118
  identifier = fetcher_1["default"].getProjectPrefixFromRegistryUUID(uri);
112
- return [3 /*break*/, 8];
113
- case 6: return [4 /*yield*/, (0, prompt_1["default"])({
119
+ return [3 /*break*/, 9];
120
+ case 7: return [4 /*yield*/, (0, prompt_1["default"])({
114
121
  type: 'text',
115
122
  name: 'value',
116
123
  message: 'What would you like to identify this API as? This will be how you import the SDK. (e.g. entering `petstore` would result in `@api/petstore`)',
@@ -118,23 +125,18 @@ cmd
118
125
  if (!value) {
119
126
  return false;
120
127
  }
121
- // Is this identifier already in storage?
122
- if (storage_1["default"].isInLockFile({ identifier: value })) {
123
- return "\"".concat(value, "\" is already taken in your `.api/` directory. Please enter another identifier.");
128
+ try {
129
+ return storage_1["default"].isIdentifierValid(value, true);
124
130
  }
125
- var isValidForNPM = (0, validate_npm_package_name_1["default"])("@api/".concat(value));
126
- if (!isValidForNPM.validForNewPackages) {
127
- // `prompts` doesn't support surfacing multiple errors in a `validate` call so we can
128
- // only surface the first to the user.
129
- return isValidForNPM.errors[0];
131
+ catch (err) {
132
+ return err.message;
130
133
  }
131
- return true;
132
134
  }
133
135
  })];
134
- case 7:
135
- (identifier = (_a.sent()).value);
136
- _a.label = 8;
137
136
  case 8:
137
+ (identifier = (_a.sent()).value);
138
+ _a.label = 9;
139
+ case 9:
138
140
  if (!identifier) {
139
141
  (0, logger_1["default"])('You must tell us what you would like to identify this API as in order to install it.', true);
140
142
  process.exit(1);
@@ -153,7 +155,7 @@ cmd
153
155
  (0, logger_1["default"])(err.message, true);
154
156
  process.exit(1);
155
157
  })];
156
- case 9:
158
+ case 10:
157
159
  oas = _a.sent();
158
160
  // @todo look for a prettier config and if we find one ask them if we should use it
159
161
  spinner = (0, ora_1["default"])('Generating your SDK').start();
@@ -169,7 +171,7 @@ cmd
169
171
  (0, logger_1["default"])(err.message, true);
170
172
  process.exit(1);
171
173
  })];
172
- case 10:
174
+ case 11:
173
175
  sdkSource = _a.sent();
174
176
  spinner = (0, ora_1["default"])('Saving your SDK into your codebase').start();
175
177
  return [4 /*yield*/, storage
@@ -182,15 +184,15 @@ cmd
182
184
  (0, logger_1["default"])(err.message, true);
183
185
  process.exit(1);
184
186
  })];
185
- case 11:
187
+ case 12:
186
188
  _a.sent();
187
- if (!generator.hasRequiredPackages()) return [3 /*break*/, 17];
189
+ if (!generator.hasRequiredPackages()) return [3 /*break*/, 18];
188
190
  (0, logger_1["default"])("".concat(figures_1["default"].warning, " This generator requires some packages to be installed alongside it:"));
189
191
  Object.entries(generator.requiredPackages).forEach(function (_a) {
190
192
  var pkg = _a[0], pkgInfo = _a[1];
191
193
  (0, logger_1["default"])(" ".concat(figures_1["default"].pointerSmall, " ").concat(pkg, ": ").concat(pkgInfo.reason, " ").concat(pkgInfo.url));
192
194
  });
193
- if (!!options.yes) return [3 /*break*/, 13];
195
+ if (!!options.yes) return [3 /*break*/, 14];
194
196
  return [4 /*yield*/, (0, prompt_1["default"])({
195
197
  type: 'confirm',
196
198
  name: 'value',
@@ -204,27 +206,27 @@ cmd
204
206
  process.exit(1);
205
207
  }
206
208
  })];
207
- case 12:
208
- _a.sent();
209
- _a.label = 13;
210
209
  case 13:
211
- spinner = (0, ora_1["default"])('Installing required packages').start();
210
+ _a.sent();
212
211
  _a.label = 14;
213
212
  case 14:
214
- _a.trys.push([14, 16, , 17]);
215
- return [4 /*yield*/, generator.installer(storage)];
213
+ spinner = (0, ora_1["default"])('Installing required packages').start();
214
+ _a.label = 15;
216
215
  case 15:
216
+ _a.trys.push([15, 17, , 18]);
217
+ return [4 /*yield*/, generator.installer(storage)];
218
+ case 16:
217
219
  _a.sent();
218
220
  spinner.succeed(spinner.text);
219
- return [3 /*break*/, 17];
220
- case 16:
221
+ return [3 /*break*/, 18];
222
+ case 17:
221
223
  err_1 = _a.sent();
222
224
  // @todo cleanup installed files
223
225
  spinner.fail(spinner.text);
224
226
  (0, logger_1["default"])(err_1.message, true);
225
227
  process.exit(1);
226
- return [3 /*break*/, 17];
227
- case 17:
228
+ return [3 /*break*/, 18];
229
+ case 18:
228
230
  (0, logger_1["default"])('🚀 All done!');
229
231
  return [2 /*return*/];
230
232
  }
@@ -23,6 +23,7 @@ export default class Storage {
23
23
  static getDefaultLockfile(): Lockfile;
24
24
  static generateIntegrityHash(definition: OASDocument): string;
25
25
  static getLockfile(): Lockfile;
26
+ static isIdentifierValid(identifier: string, prefixWithAPINamespace?: boolean): boolean;
26
27
  static isInLockFile(search: {
27
28
  identifier?: string;
28
29
  source?: string;
@@ -61,17 +62,16 @@ export default class Storage {
61
62
  * ├── openapi.json
62
63
  * └── package.json
63
64
  *
64
- * @param spec
65
65
  */
66
66
  save(spec: OASDocument): OASDocument;
67
67
  }
68
68
  export interface Lockfile {
69
+ apis: LockfileAPI[];
69
70
  /**
70
71
  * The `api.json` schema version. This will only ever change if we introduce breaking changes to
71
72
  * this store.
72
73
  */
73
74
  version: '1.0';
74
- apis: LockfileAPI[];
75
75
  }
76
76
  export interface LockfileAPI {
77
77
  /**
@@ -82,13 +82,11 @@ export interface LockfileAPI {
82
82
  */
83
83
  identifier: string;
84
84
  /**
85
- * The original source that was used to generate the SDK with.
85
+ * The version of `api` that was used to install this SDK.
86
86
  *
87
- * @example https://raw.githubusercontent.com/readmeio/oas-examples/main/3.0/json/petstore-simple.json
88
- * @example ./petstore.json
89
- * @example @developers/v2.0#nysezql0wwo236
87
+ * @example 5.0.0
90
88
  */
91
- source: string;
89
+ installerVersion: string;
92
90
  /**
93
91
  * An integrity hash that will be used to determine on `npx api update` calls if the API has
94
92
  * changed since the SDK was last generated.
@@ -97,9 +95,11 @@ export interface LockfileAPI {
97
95
  */
98
96
  integrity: string;
99
97
  /**
100
- * The version of `api` that was used to install this SDK.
98
+ * The original source that was used to generate the SDK with.
101
99
  *
102
- * @example 5.0.0
100
+ * @example https://raw.githubusercontent.com/readmeio/oas-examples/main/3.0/json/petstore-simple.json
101
+ * @example ./petstore.json
102
+ * @example @developers/v2.0#nysezql0wwo236
103
103
  */
104
- installerVersion: string;
104
+ source: string;
105
105
  }
@@ -43,6 +43,7 @@ var fs_1 = __importDefault(require("fs"));
43
43
  var path_1 = __importDefault(require("path"));
44
44
  var make_dir_1 = __importDefault(require("make-dir"));
45
45
  var ssri_1 = __importDefault(require("ssri"));
46
+ var validate_npm_package_name_1 = __importDefault(require("validate-npm-package-name"));
46
47
  var fetcher_1 = __importDefault(require("../fetcher"));
47
48
  var packageInfo_1 = require("../packageInfo");
48
49
  var Storage = /** @class */ (function () {
@@ -128,6 +129,19 @@ var Storage = /** @class */ (function () {
128
129
  }
129
130
  return Storage.lockfile;
130
131
  };
132
+ Storage.isIdentifierValid = function (identifier, prefixWithAPINamespace) {
133
+ // Is this identifier already in storage?
134
+ if (Storage.isInLockFile({ identifier: identifier })) {
135
+ throw new Error("\"".concat(identifier, "\" is already taken in your `.api/` directory. Please try another identifier."));
136
+ }
137
+ var isValidForNPM = (0, validate_npm_package_name_1["default"])(prefixWithAPINamespace ? "@api/".concat(identifier) : identifier);
138
+ if (!isValidForNPM.validForNewPackages) {
139
+ // `prompts` doesn't support surfacing multiple errors in a `validate` call so we can only
140
+ // surface the first to the user.
141
+ throw new Error("Identifier cannot be used for an NPM package: ".concat(isValidForNPM.errors[0]));
142
+ }
143
+ return true;
144
+ };
131
145
  Storage.isInLockFile = function (search) {
132
146
  // Because this method may run before we initialize a new storage object we should make sure
133
147
  // that we have a storage directory present.
@@ -221,7 +235,6 @@ var Storage = /** @class */ (function () {
221
235
  * ├── openapi.json
222
236
  * └── package.json
223
237
  *
224
- * @param spec
225
238
  */
226
239
  Storage.prototype.save = function (spec) {
227
240
  if (!this.identifier) {
@@ -1,12 +1,12 @@
1
- declare class FetchError extends Error {
1
+ declare class FetchError<Status = number, Data = unknown> extends Error {
2
2
  /** HTTP Status */
3
- status: number;
3
+ status: Status;
4
4
  /** The content of the response. */
5
- data: unknown;
5
+ data: Data;
6
6
  /** The Headers of the response. */
7
7
  headers: Headers;
8
8
  /** The raw `Response` object. */
9
9
  res: Response;
10
- constructor(status: number, data: unknown, headers: Headers, res: Response);
10
+ constructor(status: Status, data: Data, headers: Headers, res: Response);
11
11
  }
12
12
  export default FetchError;
@@ -8,7 +8,6 @@ import type { SchemaWrapper } from 'oas/dist/operation/get-parameters-as-json-sc
8
8
  *
9
9
  * @todo This is a good candidate to be moved into a core `oas` library method.
10
10
  * @see {@link https://github.com/mdornseif/json-schema-default}
11
- * @param jsonSchemas
12
11
  */
13
12
  export default function getJSONSchemaDefaults(jsonSchemas: SchemaWrapper[]): {
14
13
  [x: string]: Record<string, unknown>;
@@ -13,7 +13,6 @@ var json_schema_traverse_1 = __importDefault(require("json-schema-traverse"));
13
13
  *
14
14
  * @todo This is a good candidate to be moved into a core `oas` library method.
15
15
  * @see {@link https://github.com/mdornseif/json-schema-default}
16
- * @param jsonSchemas
17
16
  */
18
17
  function getJSONSchemaDefaults(jsonSchemas) {
19
18
  return jsonSchemas
@@ -16,9 +16,9 @@ export interface ConfigOptions {
16
16
  }
17
17
  export interface FetchResponse<status, data> {
18
18
  data: data;
19
- status: status;
20
19
  headers: Headers;
21
20
  res: Response;
21
+ status: status;
22
22
  }
23
23
  type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] extends N ? Acc[number] : Enumerate<N, [...Acc, Acc['length']]>;
24
24
  export type HTTPMethodRange<F extends number, T extends number> = Exclude<Enumerate<T>, Enumerate<F>>;
@@ -1,5 +1,5 @@
1
1
  import type { Operation } from 'oas';
2
2
  export default function prepareAuth(authKey: (number | string)[], operation: Operation): Record<string, string | number | {
3
- user: string | number;
4
3
  pass: string | number;
4
+ user: string | number;
5
5
  }>;
@@ -3,9 +3,6 @@ import type Oas from 'oas';
3
3
  * With an SDK server config and an instance of OAS we should extract and prepare the server and
4
4
  * any server variables to be supplied to `@readme/oas-to-har`.
5
5
  *
6
- * @param spec
7
- * @param url
8
- * @param variables
9
6
  */
10
7
  export default function prepareServer(spec: Oas, url: string, variables?: Record<string, string | number>): false | {
11
8
  selected: number;
@@ -10,9 +10,6 @@ function stripTrailingSlash(url) {
10
10
  * With an SDK server config and an instance of OAS we should extract and prepare the server and
11
11
  * any server variables to be supplied to `@readme/oas-to-har`.
12
12
  *
13
- * @param spec
14
- * @param url
15
- * @param variables
16
13
  */
17
14
  function prepareServer(spec, url, variables) {
18
15
  if (variables === void 0) { variables = {}; }
package/dist/index.js CHANGED
@@ -70,7 +70,6 @@ var Sdk = /** @class */ (function () {
70
70
  * Create dynamic accessors for every operation with a defined operation ID. If an operation
71
71
  * does not have an operation ID it can be accessed by its `.method('/path')` accessor instead.
72
72
  *
73
- * @param spec
74
73
  */
75
74
  function loadOperations(spec) {
76
75
  return Object.entries(spec.getPaths())
@@ -1,2 +1,2 @@
1
1
  export declare const PACKAGE_NAME = "api";
2
- export declare const PACKAGE_VERSION = "5.0.8";
2
+ export declare const PACKAGE_VERSION = "6.1.0";
@@ -3,4 +3,4 @@ exports.__esModule = true;
3
3
  exports.PACKAGE_VERSION = exports.PACKAGE_NAME = void 0;
4
4
  // This file is automatically updated by the build script.
5
5
  exports.PACKAGE_NAME = 'api';
6
- exports.PACKAGE_VERSION = '5.0.8';
6
+ exports.PACKAGE_VERSION = '6.1.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api",
3
- "version": "5.0.8",
3
+ "version": "6.1.0",
4
4
  "description": "Magical SDK generation from an OpenAPI definition 🪄",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -12,8 +12,8 @@
12
12
  "debug:bin": "node -r ts-node/register src/bin.ts",
13
13
  "prebuild": "rm -rf dist/; npm run version",
14
14
  "prepack": "npm run build",
15
- "test": "nyc mocha $(find test -name '*.test.ts' -not -path '*/smoketest.test.ts')",
16
- "test:smoke": "npx mocha test/cli/codegen/languages/typescript/smoketest.test.ts",
15
+ "test": "jest --coverage $(find test -name '*.test.ts' -not -path '*/smoketest.test.ts')",
16
+ "test:smoke": "npx jest test/cli/codegen/languages/typescript/smoketest.test.ts",
17
17
  "version": "node -p \"'// This file is automatically updated by the build script.\\nexport const PACKAGE_NAME = \\'' + require('./package.json').name + '\\';\\nexport const PACKAGE_VERSION = \\'' + require('./package.json').version + '\\';'\" > src/packageInfo.ts; git add src/packageInfo.ts"
18
18
  },
19
19
  "repository": {
@@ -28,7 +28,7 @@
28
28
  "author": "Jon Ursenbach <jon@readme.io>",
29
29
  "license": "MIT",
30
30
  "engines": {
31
- "node": ">=14"
31
+ "node": ">=16"
32
32
  },
33
33
  "keywords": [
34
34
  "api",
@@ -74,39 +74,28 @@
74
74
  "devDependencies": {
75
75
  "@readme/oas-examples": "^5.9.0",
76
76
  "@types/caseless": "^0.12.2",
77
- "@types/chai": "^4.3.4",
78
77
  "@types/find-cache-dir": "^3.2.1",
78
+ "@types/jest": "^29.5.2",
79
79
  "@types/js-yaml": "^4.0.5",
80
80
  "@types/lodash.camelcase": "^4.3.7",
81
81
  "@types/lodash.deburr": "^4.1.7",
82
82
  "@types/lodash.merge": "^4.6.7",
83
83
  "@types/lodash.setwith": "^4.3.7",
84
84
  "@types/lodash.startcase": "^4.4.7",
85
- "@types/mocha": "^10.0.1",
86
85
  "@types/prettier": "^2.7.2",
87
86
  "@types/prompts": "^2.4.2",
88
87
  "@types/semver": "^7.3.13",
89
- "@types/sinon-chai": "^3.2.9",
90
88
  "@types/ssri": "^7.1.1",
91
89
  "@types/validate-npm-package-name": "^4.0.0",
92
- "chai": "^4.3.7",
93
90
  "fetch-mock": "^9.11.0",
94
- "mocha": "^10.1.0",
95
- "mock-require": "^3.0.3",
96
- "nyc": "^15.1.0",
91
+ "jest": "^29.6.1",
92
+ "jest-extended": "^4.0.0",
97
93
  "oas-normalize": "^8.3.2",
98
- "sinon": "^15.0.0",
99
- "sinon-chai": "^3.7.0",
94
+ "ts-jest": "^29.1.1",
100
95
  "type-fest": "^3.5.4",
101
96
  "typescript": "^4.9.5",
102
97
  "unique-temp-dir": "^1.0.0"
103
98
  },
104
99
  "prettier": "@readme/eslint-config/prettier",
105
- "nyc": {
106
- "exclude": [
107
- "dist/",
108
- "test/"
109
- ]
110
- },
111
- "gitHead": "bd15ecccb3aadea9ee821056c41d7897638bbe9d"
100
+ "gitHead": "244093f2ad00e5ea527420f49bdb541bc2095806"
112
101
  }
package/src/cache.ts CHANGED
@@ -16,8 +16,11 @@ type CacheStore = Record<
16
16
  string,
17
17
  {
18
18
  hash: string;
19
- path?: string; // Deprecated in v4.5.0 in favor of `hash`.
20
19
  original: string | OASDocument;
20
+ /**
21
+ * @deprecated Deprecated in v4.5.0 in favor of `hash`.
22
+ */
23
+ path?: string;
21
24
  title?: string;
22
25
  version?: string;
23
26
  }
@@ -3,7 +3,13 @@ import type { InstallerOptions } from '../language';
3
3
  import type Oas from 'oas';
4
4
  import type { Operation } from 'oas';
5
5
  import type { HttpMethods, SchemaObject } from 'oas/dist/rmoas.types';
6
- import type { ClassDeclaration, JSDocStructure, OptionalKind, ParameterDeclarationStructure } from 'ts-morph';
6
+ import type {
7
+ ClassDeclaration,
8
+ JSDocStructure,
9
+ JSDocTagStructure,
10
+ OptionalKind,
11
+ ParameterDeclarationStructure,
12
+ } from 'ts-morph';
7
13
  import type { PackageJson } from 'type-fest';
8
14
 
9
15
  import fs from 'fs';
@@ -20,16 +26,22 @@ import CodeGeneratorLanguage from '../language';
20
26
  import { docblockEscape, formatter, generateTypeName, wordWrap } from './typescript/util';
21
27
 
22
28
  export interface TSGeneratorOptions {
23
- outputJS?: boolean;
24
29
  compilerTarget?: 'cjs' | 'esm';
30
+ outputJS?: boolean;
25
31
  }
26
32
 
27
33
  interface OperationTypeHousing {
34
+ operation: Operation;
28
35
  types: {
29
36
  params?: false | Record<'body' | 'formData' | 'metadata', string>;
30
- responses?: Record<string, string>;
37
+ responses?: Record<
38
+ string | number,
39
+ {
40
+ description?: string;
41
+ type: string;
42
+ }
43
+ >;
31
44
  };
32
- operation: Operation;
33
45
  }
34
46
 
35
47
  export default class TSGenerator extends CodeGeneratorLanguage {
@@ -60,7 +72,7 @@ export default class TSGenerator extends CodeGeneratorLanguage {
60
72
  usesHTTPMethodRangeInterface = false;
61
73
 
62
74
  constructor(spec: Oas, specPath: string, identifier: string, opts: TSGeneratorOptions = {}) {
63
- const options: { outputJS: boolean; compilerTarget: 'cjs' | 'esm' } = {
75
+ const options: { compilerTarget: 'cjs' | 'esm'; outputJS: boolean } = {
64
76
  outputJS: false,
65
77
  compilerTarget: 'cjs',
66
78
  ...opts,
@@ -153,12 +165,21 @@ export default class TSGenerator extends CodeGeneratorLanguage {
153
165
  // This will install the installed SDK as a dependency within the current working directory,
154
166
  // adding `@api/<sdk identifier>` as a dependency there so you can load it with
155
167
  // `require('@api/<sdk identifier>)`.
156
- return execa('npm', [...npmInstall, installDir].filter(Boolean)).then(res => {
157
- if (opts.dryRun) {
158
- (opts.logger ? opts.logger : logger)(res.command);
159
- (opts.logger ? opts.logger : logger)(res.stdout);
160
- }
161
- });
168
+ return execa('npm', [...npmInstall, installDir].filter(Boolean))
169
+ .then(res => {
170
+ if (opts.dryRun) {
171
+ (opts.logger ? opts.logger : logger)(res.command);
172
+ (opts.logger ? opts.logger : logger)(res.stdout);
173
+ }
174
+ })
175
+ .catch(err => {
176
+ if (opts.dryRun) {
177
+ (opts.logger ? opts.logger : logger)(err.message);
178
+ return;
179
+ }
180
+
181
+ throw err;
182
+ });
162
183
  }
163
184
 
164
185
  /**
@@ -493,6 +514,20 @@ sdk.server('https://eu.api.example.com/v14');`)
493
514
  return sourceFile;
494
515
  }
495
516
 
517
+ /**
518
+ * Add a new JSDoc `@tag` to an existing docblock.
519
+ *
520
+ */
521
+ static addTagToDocblock(docblock: OptionalKind<JSDocStructure>, tag: OptionalKind<JSDocTagStructure>) {
522
+ const tags = docblock.tags ?? [];
523
+ tags.push(tag);
524
+
525
+ return {
526
+ ...docblock,
527
+ tags,
528
+ };
529
+ }
530
+
496
531
  /**
497
532
  * Create operation accessors on the SDK.
498
533
  *
@@ -503,7 +538,7 @@ sdk.server('https://eu.api.example.com/v14');`)
503
538
  paramTypes?: OperationTypeHousing['types']['params'],
504
539
  responseTypes?: OperationTypeHousing['types']['responses']
505
540
  ) {
506
- const docblock: OptionalKind<JSDocStructure> = {};
541
+ let docblock: OptionalKind<JSDocStructure> = {};
507
542
  const summary = operation.getSummary();
508
543
  const description = operation.getDescription();
509
544
  if (summary || description) {
@@ -522,7 +557,10 @@ sdk.server('https://eu.api.example.com/v14');`)
522
557
  };
523
558
 
524
559
  if (summary && description) {
525
- docblock.tags = [{ tagName: 'summary', text: docblockEscape(wordWrap(summary)) }];
560
+ docblock = TSGenerator.addTagToDocblock(docblock, {
561
+ tagName: 'summary',
562
+ text: docblockEscape(wordWrap(summary)),
563
+ });
526
564
  }
527
565
  }
528
566
 
@@ -559,8 +597,8 @@ sdk.server('https://eu.api.example.com/v14');`)
559
597
 
560
598
  let returnType = 'Promise<FetchResponse<number, unknown>>';
561
599
  if (responseTypes) {
562
- returnType = `Promise<${Object.entries(responseTypes)
563
- .map(([status, responseType]) => {
600
+ const returnTypes = Object.entries(responseTypes)
601
+ .map(([status, { description: responseDescription, type: responseType }]) => {
564
602
  if (status.toLowerCase() === 'default') {
565
603
  return `FetchResponse<number, ${responseType}>`;
566
604
  } else if (status.length === 3 && status.toUpperCase().endsWith('XX')) {
@@ -571,19 +609,54 @@ sdk.server('https://eu.api.example.com/v14');`)
571
609
  return `FetchResponse<number, ${responseType}>`;
572
610
  }
573
611
 
612
+ if (Number(statusPrefix) >= 4) {
613
+ docblock = TSGenerator.addTagToDocblock(docblock, {
614
+ tagName: 'throws',
615
+ text: `FetchError<${status}, ${responseType}>${
616
+ responseDescription ? docblockEscape(wordWrap(` ${responseDescription}`)) : ''
617
+ }`,
618
+ });
619
+
620
+ return false;
621
+ }
622
+
574
623
  this.usesHTTPMethodRangeInterface = true;
575
624
  return `FetchResponse<HTTPMethodRange<${statusPrefix}00, ${statusPrefix}99>, ${responseType}>`;
576
625
  }
577
626
 
627
+ // 400 and 500 status code families are thrown as exceptions so adding them as a possible
628
+ // return type isn't valid.
629
+ if (Number(status) >= 400) {
630
+ docblock = TSGenerator.addTagToDocblock(docblock, {
631
+ tagName: 'throws',
632
+ text: `FetchError<${status}, ${responseType}>${
633
+ responseDescription ? docblockEscape(wordWrap(` ${responseDescription}`)) : ''
634
+ }`,
635
+ });
636
+
637
+ return false;
638
+ }
639
+
578
640
  return `FetchResponse<${status}, ${responseType}>`;
579
641
  })
580
- .join(' | ')}>`;
642
+ .filter(Boolean)
643
+ .join(' | ');
644
+
645
+ // If all of our documented responses are for error status codes then all we can document for
646
+ // anything else that might happen is `unknown`.
647
+ returnType = `Promise<${returnTypes.length ? returnTypes : 'FetchResponse<number, unknown>'}>`;
581
648
  }
582
649
 
650
+ const shouldAddAltTypedOverloads = Object.keys(parameters).length === 2 && hasOptionalBody && !hasOptionalMetadata;
583
651
  const operationIdAccessor = this.sdk.addMethod({
584
652
  name: operationId,
585
653
  returnType,
586
- docs: Object.keys(docblock).length ? [docblock] : null,
654
+
655
+ // If we're going to be creating typed method overloads for optional body an metadata handling
656
+ // we should only add a docblock to the first overload we create because IDE Intellisense will
657
+ // always use that and adding a docblock to all three will bloat the SDK with unused and
658
+ // unsurfaced method documentation.
659
+ docs: shouldAddAltTypedOverloads ? null : Object.keys(docblock).length ? [docblock] : null,
587
660
  statements: writer => {
588
661
  /**
589
662
  * @example return this.core.fetch('/pet/findByStatus', 'get', body, metadata);
@@ -617,10 +690,6 @@ sdk.server('https://eu.api.example.com/v14');`)
617
690
  // If we have both body and metadata parameters but only body is optional we need to create
618
691
  // a couple function overloads as Typescript doesn't let us have an optional method parameter
619
692
  // come before one that's required.
620
- //
621
- // None of these accessor overloads will receive a docblock because the original will have
622
- // that covered.
623
- const shouldAddAltTypedOverloads = Object.keys(parameters).length === 2 && hasOptionalBody && !hasOptionalMetadata;
624
693
  if (shouldAddAltTypedOverloads) {
625
694
  // Create an overload that has both `body` and `metadata` parameters as required.
626
695
  operationIdAccessor.addOverload({
@@ -636,7 +705,6 @@ sdk.server('https://eu.api.example.com/v14');`)
636
705
  operationIdAccessor.addOverload({
637
706
  parameters: [{ ...parameters.metadata }],
638
707
  returnType,
639
- docs: Object.keys(docblock).length ? [docblock] : null,
640
708
  });
641
709
 
642
710
  // Create an overload that has both `body` and `metadata` parameters as optional. Even though
@@ -798,7 +866,7 @@ sdk.server('https://eu.api.example.com/v14');`)
798
866
  .reduce((prev, next) => Object.assign(prev, next));
799
867
 
800
868
  const res = Object.entries(schemas)
801
- .map(([status, { schema }]) => {
869
+ .map(([status, { description, schema }]) => {
802
870
  let typeName;
803
871
 
804
872
  if (typeof schema === 'string' && schema.startsWith('::convert::')) {
@@ -817,7 +885,10 @@ sdk.server('https://eu.api.example.com/v14');`)
817
885
  return {
818
886
  // Types are prefixed with `types.` because that's how we're importing them from
819
887
  // `types.d.ts`.
820
- [status]: `types.${typeName}`,
888
+ [status]: {
889
+ type: `types.${typeName}`,
890
+ description,
891
+ },
821
892
  };
822
893
  })
823
894
  .reduce((prev, next) => Object.assign(prev, next), {});
@@ -4,7 +4,6 @@ import { Command, Option } from 'commander';
4
4
  import figures from 'figures';
5
5
  import Oas from 'oas';
6
6
  import ora from 'ora';
7
- import validateNPMPackageName from 'validate-npm-package-name';
8
7
 
9
8
  import Fetcher from '../../fetcher';
10
9
  import codegen from '../codegen';
@@ -18,6 +17,7 @@ cmd
18
17
  .name('install')
19
18
  .description('install an API SDK into your codebase')
20
19
  .argument('<uri>', 'an API to install')
20
+ .option('-i, --identifier <identifier>', 'API identifier (eg. `@api/petstore`)')
21
21
  .addOption(
22
22
  new Option('-l, --lang <language>', 'SDK language').choices([
23
23
  'js', // User generally wants JS, we'll prompt if they want CJS or ESM files.
@@ -27,7 +27,7 @@ cmd
27
27
  ])
28
28
  )
29
29
  .addOption(new Option('-y, --yes', 'Automatically answer "yes" to any prompts printed'))
30
- .action(async (uri: string, options: { lang: string; yes?: boolean }) => {
30
+ .action(async (uri: string, options: { identifier?: string; lang: string; yes?: boolean }) => {
31
31
  let language: SupportedLanguages;
32
32
  if (options.lang) {
33
33
  language = options.lang as SupportedLanguages;
@@ -69,7 +69,12 @@ cmd
69
69
  }
70
70
 
71
71
  let identifier;
72
- if (Fetcher.isAPIRegistryUUID(uri)) {
72
+ if (options.identifier) {
73
+ // `Storage.isIdentifierValid` will throw an exception if an identifier is invalid.
74
+ if (Storage.isIdentifierValid(options.identifier)) {
75
+ identifier = options.identifier;
76
+ }
77
+ } else if (Fetcher.isAPIRegistryUUID(uri)) {
73
78
  identifier = Fetcher.getProjectPrefixFromRegistryUUID(uri);
74
79
  } else {
75
80
  ({ value: identifier } = await promptTerminal({
@@ -82,19 +87,11 @@ cmd
82
87
  return false;
83
88
  }
84
89
 
85
- // Is this identifier already in storage?
86
- if (Storage.isInLockFile({ identifier: value })) {
87
- return `"${value}" is already taken in your \`.api/\` directory. Please enter another identifier.`;
88
- }
89
-
90
- const isValidForNPM = validateNPMPackageName(`@api/${value}`);
91
- if (!isValidForNPM.validForNewPackages) {
92
- // `prompts` doesn't support surfacing multiple errors in a `validate` call so we can
93
- // only surface the first to the user.
94
- return isValidForNPM.errors[0];
90
+ try {
91
+ return Storage.isIdentifierValid(value, true);
92
+ } catch (err) {
93
+ return err.message;
95
94
  }
96
-
97
- return true;
98
95
  },
99
96
  }));
100
97
  }
@@ -5,6 +5,7 @@ import path from 'path';
5
5
 
6
6
  import makeDir from 'make-dir';
7
7
  import ssri from 'ssri';
8
+ import validateNPMPackageName from 'validate-npm-package-name';
8
9
 
9
10
  import Fetcher from '../fetcher';
10
11
  import { PACKAGE_VERSION } from '../packageInfo';
@@ -105,6 +106,22 @@ export default class Storage {
105
106
  return Storage.lockfile;
106
107
  }
107
108
 
109
+ static isIdentifierValid(identifier: string, prefixWithAPINamespace?: boolean) {
110
+ // Is this identifier already in storage?
111
+ if (Storage.isInLockFile({ identifier })) {
112
+ throw new Error(`"${identifier}" is already taken in your \`.api/\` directory. Please try another identifier.`);
113
+ }
114
+
115
+ const isValidForNPM = validateNPMPackageName(prefixWithAPINamespace ? `@api/${identifier}` : identifier);
116
+ if (!isValidForNPM.validForNewPackages) {
117
+ // `prompts` doesn't support surfacing multiple errors in a `validate` call so we can only
118
+ // surface the first to the user.
119
+ throw new Error(`Identifier cannot be used for an NPM package: ${isValidForNPM.errors[0]}`);
120
+ }
121
+
122
+ return true;
123
+ }
124
+
108
125
  static isInLockFile(search: { identifier?: string; source?: string }) {
109
126
  // Because this method may run before we initialize a new storage object we should make sure
110
127
  // that we have a storage directory present.
@@ -207,7 +224,6 @@ export default class Storage {
207
224
  * ├── openapi.json
208
225
  * └── package.json
209
226
  *
210
- * @param spec
211
227
  */
212
228
  save(spec: OASDocument) {
213
229
  if (!this.identifier) {
@@ -254,12 +270,13 @@ export default class Storage {
254
270
  }
255
271
 
256
272
  export interface Lockfile {
273
+ apis: LockfileAPI[];
274
+
257
275
  /**
258
276
  * The `api.json` schema version. This will only ever change if we introduce breaking changes to
259
277
  * this store.
260
278
  */
261
279
  version: '1.0';
262
- apis: LockfileAPI[];
263
280
  }
264
281
 
265
282
  export interface LockfileAPI {
@@ -272,13 +289,11 @@ export interface LockfileAPI {
272
289
  identifier: string;
273
290
 
274
291
  /**
275
- * The original source that was used to generate the SDK with.
292
+ * The version of `api` that was used to install this SDK.
276
293
  *
277
- * @example https://raw.githubusercontent.com/readmeio/oas-examples/main/3.0/json/petstore-simple.json
278
- * @example ./petstore.json
279
- * @example @developers/v2.0#nysezql0wwo236
294
+ * @example 5.0.0
280
295
  */
281
- source: string;
296
+ installerVersion: string;
282
297
 
283
298
  /**
284
299
  * An integrity hash that will be used to determine on `npx api update` calls if the API has
@@ -289,9 +304,11 @@ export interface LockfileAPI {
289
304
  integrity: string;
290
305
 
291
306
  /**
292
- * The version of `api` that was used to install this SDK.
307
+ * The original source that was used to generate the SDK with.
293
308
  *
294
- * @example 5.0.0
309
+ * @example https://raw.githubusercontent.com/readmeio/oas-examples/main/3.0/json/petstore-simple.json
310
+ * @example ./petstore.json
311
+ * @example @developers/v2.0#nysezql0wwo236
295
312
  */
296
- installerVersion: string;
313
+ source: string;
297
314
  }
@@ -1,9 +1,9 @@
1
- class FetchError extends Error {
1
+ class FetchError<Status = number, Data = unknown> extends Error {
2
2
  /** HTTP Status */
3
- status: number;
3
+ status: Status;
4
4
 
5
5
  /** The content of the response. */
6
- data: unknown;
6
+ data: Data;
7
7
 
8
8
  /** The Headers of the response. */
9
9
  headers: Headers;
@@ -11,7 +11,7 @@ class FetchError extends Error {
11
11
  /** The raw `Response` object. */
12
12
  res: Response;
13
13
 
14
- constructor(status: number, data: unknown, headers: Headers, res: Response) {
14
+ constructor(status: Status, data: Data, headers: Headers, res: Response) {
15
15
  super(res.statusText);
16
16
 
17
17
  this.name = 'FetchError';
@@ -12,7 +12,6 @@ import traverse from 'json-schema-traverse';
12
12
  *
13
13
  * @todo This is a good candidate to be moved into a core `oas` library method.
14
14
  * @see {@link https://github.com/mdornseif/json-schema-default}
15
- * @param jsonSchemas
16
15
  */
17
16
  export default function getJSONSchemaDefaults(jsonSchemas: SchemaWrapper[]) {
18
17
  return jsonSchemas
package/src/core/index.ts CHANGED
@@ -26,9 +26,9 @@ export interface ConfigOptions {
26
26
 
27
27
  export interface FetchResponse<status, data> {
28
28
  data: data;
29
- status: status;
30
29
  headers: Headers;
31
30
  res: Response;
31
+ status: status;
32
32
  }
33
33
 
34
34
  // https://stackoverflow.com/a/39495173
@@ -128,7 +128,12 @@ export default class APICore {
128
128
  const parsed = await parseResponse(res);
129
129
 
130
130
  if (res.status >= 400 && res.status <= 599) {
131
- throw new FetchError(parsed.status, parsed.data, parsed.headers, parsed.res);
131
+ throw new FetchError<typeof parsed.status, typeof parsed.data>(
132
+ parsed.status,
133
+ parsed.data,
134
+ parsed.headers,
135
+ parsed.res
136
+ );
132
137
  }
133
138
 
134
139
  return parsed;
@@ -11,8 +11,8 @@ export default function prepareAuth(authKey: (number | string)[], operation: Ope
11
11
  | string
12
12
  | number
13
13
  | {
14
- user: string | number;
15
14
  pass: string | number;
15
+ user: string | number;
16
16
  }
17
17
  > = {};
18
18
 
@@ -76,7 +76,7 @@ function merge(src: any, target: any) {
76
76
  function processFile(
77
77
  paramName: string,
78
78
  file: string | ReadStream
79
- ): Promise<{ paramName: string; base64: string; filename: string; buffer: Buffer }> {
79
+ ): Promise<{ base64: string; buffer: Buffer; filename: string; paramName: string }> {
80
80
  if (typeof file === 'string') {
81
81
  // In order to support relative pathed files, we need to attempt to resolve them.
82
82
  const resolvedFile = path.resolve(file);
@@ -12,9 +12,6 @@ function stripTrailingSlash(url: string) {
12
12
  * With an SDK server config and an instance of OAS we should extract and prepare the server and
13
13
  * any server variables to be supplied to `@readme/oas-to-har`.
14
14
  *
15
- * @param spec
16
- * @param url
17
- * @param variables
18
15
  */
19
16
  export default function prepareServer(spec: Oas, url: string, variables: Record<string, string | number> = {}) {
20
17
  let serverIdx;
package/src/index.ts CHANGED
@@ -41,7 +41,6 @@ class Sdk {
41
41
  * Create dynamic accessors for every operation with a defined operation ID. If an operation
42
42
  * does not have an operation ID it can be accessed by its `.method('/path')` accessor instead.
43
43
  *
44
- * @param spec
45
44
  */
46
45
  function loadOperations(spec: Oas) {
47
46
  return Object.entries(spec.getPaths())
@@ -1,3 +1,3 @@
1
1
  // This file is automatically updated by the build script.
2
2
  export const PACKAGE_NAME = 'api';
3
- export const PACKAGE_VERSION = '5.0.8';
3
+ export const PACKAGE_VERSION = '6.1.0';