api 5.0.0-beta.3 → 5.0.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/README.md +7 -7
- package/dist/bin.js +1 -1
- package/dist/cache.d.ts +37 -2
- package/dist/cache.js +7 -26
- package/dist/cli/codegen/index.d.ts +1 -1
- package/dist/cli/codegen/language.d.ts +1 -1
- package/dist/cli/codegen/language.js +13 -0
- package/dist/cli/codegen/languages/typescript/util.d.ts +21 -0
- package/dist/cli/codegen/languages/typescript/util.js +185 -0
- package/dist/cli/codegen/languages/typescript.d.ts +31 -38
- package/dist/cli/codegen/languages/typescript.js +390 -478
- package/dist/cli/commands/install.js +6 -6
- package/dist/cli/storage.js +2 -2
- package/dist/core/errors/fetchError.d.ts +12 -0
- package/dist/core/errors/fetchError.js +36 -0
- package/dist/core/index.d.ts +11 -3
- package/dist/core/index.js +36 -11
- package/dist/core/parseResponse.d.ts +6 -1
- package/dist/core/parseResponse.js +9 -3
- package/dist/core/prepareAuth.js +47 -18
- package/dist/core/prepareParams.d.ts +0 -3
- package/dist/core/prepareParams.js +81 -57
- package/dist/fetcher.js +3 -3
- package/dist/index.js +24 -40
- package/dist/packageInfo.d.ts +1 -1
- package/dist/packageInfo.js +1 -1
- package/package.json +28 -17
- package/src/bin.ts +2 -1
- package/src/cache.ts +8 -30
- package/src/cli/codegen/index.ts +1 -1
- package/src/cli/codegen/language.ts +18 -1
- package/src/cli/codegen/languages/typescript/util.ts +183 -0
- package/src/cli/codegen/languages/typescript.ts +340 -402
- package/src/cli/commands/install.ts +6 -8
- package/src/cli/storage.ts +3 -3
- package/src/core/errors/fetchError.ts +31 -0
- package/src/core/getJSONSchemaDefaults.ts +2 -1
- package/src/core/index.ts +52 -17
- package/src/core/parseResponse.ts +8 -2
- package/src/core/prepareAuth.ts +55 -31
- package/src/core/prepareParams.ts +88 -55
- package/src/fetcher.ts +4 -3
- package/src/index.ts +24 -32
- package/src/packageInfo.ts +1 -1
- package/src/typings.d.ts +0 -1
- package/tsconfig.json +1 -1
|
@@ -76,18 +76,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
76
76
|
exports.__esModule = true;
|
|
77
77
|
var fs_1 = __importDefault(require("fs"));
|
|
78
78
|
var path_1 = __importDefault(require("path"));
|
|
79
|
-
var language_1 = __importDefault(require("../language"));
|
|
80
|
-
var logger_1 = __importDefault(require("../../logger"));
|
|
81
|
-
var object_hash_1 = __importDefault(require("object-hash"));
|
|
82
|
-
var ts_morph_1 = require("ts-morph");
|
|
83
|
-
var json_schema_to_typescript_1 = require("json-schema-to-typescript");
|
|
84
|
-
var formatter_1 = require("json-schema-to-typescript/dist/src/formatter");
|
|
85
79
|
var execa_1 = __importDefault(require("execa"));
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
80
|
+
var lodash_setwith_1 = __importDefault(require("lodash.setwith"));
|
|
81
|
+
var ts_morph_1 = require("ts-morph");
|
|
82
|
+
var logger_1 = __importDefault(require("../../logger"));
|
|
83
|
+
var language_1 = __importDefault(require("../language"));
|
|
84
|
+
var util_1 = require("./typescript/util");
|
|
91
85
|
var TSGenerator = /** @class */ (function (_super) {
|
|
92
86
|
__extends(TSGenerator, _super);
|
|
93
87
|
function TSGenerator(spec, specPath, identifier, opts) {
|
|
@@ -99,11 +93,16 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
99
93
|
options.compilerTarget = 'esm';
|
|
100
94
|
}
|
|
101
95
|
_this = _super.call(this, spec, specPath, identifier) || this;
|
|
96
|
+
_this.usesHTTPMethodRangeInterface = false;
|
|
102
97
|
_this.requiredPackages = {
|
|
103
|
-
|
|
98
|
+
api: {
|
|
104
99
|
reason: "Required for the `api/dist/core` library that the codegen'd SDK uses for making requests.",
|
|
105
100
|
url: 'https://npm.im/api'
|
|
106
101
|
},
|
|
102
|
+
'json-schema-to-ts': {
|
|
103
|
+
reason: 'Required for TypeScript type handling.',
|
|
104
|
+
url: 'https://npm.im/json-schema-to-ts'
|
|
105
|
+
},
|
|
107
106
|
oas: {
|
|
108
107
|
reason: 'Used within `api/dist/core` and is also loaded for TypeScript types.',
|
|
109
108
|
url: 'https://npm.im/oas'
|
|
@@ -114,24 +113,17 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
114
113
|
indentationText: ts_morph_1.IndentationText.TwoSpaces,
|
|
115
114
|
quoteKind: ts_morph_1.QuoteKind.Single
|
|
116
115
|
},
|
|
117
|
-
compilerOptions: __assign({
|
|
116
|
+
compilerOptions: __assign({
|
|
117
|
+
// If we're exporting a TypeScript SDK then we don't need to pollute the codegen directory
|
|
118
|
+
// with unnecessary declaration `.d.ts` files.
|
|
119
|
+
declaration: options.outputJS, outDir: 'dist', resolveJsonModule: true, target: options.compilerTarget === 'cjs' ? ts_morph_1.ScriptTarget.ES5 : ts_morph_1.ScriptTarget.ES2020 }, (options.compilerTarget === 'cjs' ? { esModuleInterop: true } : {}))
|
|
118
120
|
});
|
|
119
121
|
_this.compilerTarget = options.compilerTarget;
|
|
120
122
|
_this.outputJS = options.outputJS;
|
|
121
123
|
_this.types = new Map();
|
|
122
|
-
_this.
|
|
123
|
-
_this.schemas = new Map();
|
|
124
|
+
_this.schemas = {};
|
|
124
125
|
return _this;
|
|
125
126
|
}
|
|
126
|
-
TSGenerator.formatter = function (content) {
|
|
127
|
-
return (0, formatter_1.format)(content, {
|
|
128
|
-
format: true,
|
|
129
|
-
style: {
|
|
130
|
-
printWidth: 120,
|
|
131
|
-
singleQuote: true
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
};
|
|
135
127
|
TSGenerator.prototype.installer = function (storage, opts) {
|
|
136
128
|
if (opts === void 0) { opts = {}; }
|
|
137
129
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -162,7 +154,7 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
162
154
|
// This will install the installed SDK as a dependency within the current working directory,
|
|
163
155
|
// adding `@api/<sdk identifier>` as a dependency there so you can load it with
|
|
164
156
|
// `require('@api/<sdk identifier>)`.
|
|
165
|
-
return [2 /*return*/, (0, execa_1["default"])('npm', __spreadArray(
|
|
157
|
+
return [2 /*return*/, (0, execa_1["default"])('npm', __spreadArray([], npmInstall, true).filter(Boolean), { cwd: storage.getIdentifierStorageDir() }).then(function (res) {
|
|
166
158
|
if (opts.dryRun) {
|
|
167
159
|
(opts.logger ? opts.logger : logger_1["default"])(res.command);
|
|
168
160
|
(opts.logger ? opts.logger : logger_1["default"])(res.stdout);
|
|
@@ -178,300 +170,278 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
178
170
|
*/
|
|
179
171
|
TSGenerator.prototype.generator = function () {
|
|
180
172
|
return __awaiter(this, void 0, void 0, function () {
|
|
181
|
-
var
|
|
173
|
+
var sdkSource, types;
|
|
182
174
|
var _this = this;
|
|
183
|
-
return __generator(this, function (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (this.compilerTarget === 'cjs') {
|
|
225
|
-
sdkSource.addExportAssignment({
|
|
226
|
-
expression: 'createSDK'
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
/**
|
|
231
|
-
* Because `createSDK` above is an IFEE constant we can't use `setIsDefaultExport` on it due
|
|
232
|
-
* to `ts-morph` not having great handling for IFEE's.
|
|
233
|
-
*
|
|
234
|
-
* If we were to call `setIsDefaultExport` on our IFEE to attempt to compile it as
|
|
235
|
-
* `export default createSDK` then `ts-morph` hard crashes with a "Error replacing tree: The
|
|
236
|
-
* children of the old and new trees were expected to have the same count" exception due to
|
|
237
|
-
* it not being able properly handle IFEE's. It's for that reason that we need to manually
|
|
238
|
-
* write a statement expression to set `createSDK` as the default export.
|
|
239
|
-
*
|
|
240
|
-
* Another quirk that this work avoids is there being an empty `export {};` at the very end
|
|
241
|
-
* of our compiled `d.ts` declaration file. I'm not sure why it was being added, and it
|
|
242
|
-
* didn't appear to be harming anything, but us manually creating this export statement
|
|
243
|
-
* causes it to go away.
|
|
244
|
-
*
|
|
245
|
-
* Thankfully, fortunately, and curiously, these are all only problems in non-CJS compiled
|
|
246
|
-
* targets. ¯\_(ツ)_/¯
|
|
247
|
-
*/
|
|
248
|
-
sdkSource.addStatements('export default createSDK');
|
|
249
|
-
}
|
|
250
|
-
this.sdk.addProperties([
|
|
251
|
-
{ name: 'spec', type: 'Oas' },
|
|
252
|
-
{ name: 'core', type: 'APICore' },
|
|
253
|
-
{ name: 'authKeys', type: '(number | string)[][]', initializer: '[]' },
|
|
254
|
-
]);
|
|
255
|
-
this.sdk.addConstructor({
|
|
256
|
-
statements: function (writer) {
|
|
257
|
-
writer.writeLine('this.spec = Oas.init(definition);');
|
|
258
|
-
writer.write('this.core = new APICore(this.spec, ').quote(_this.userAgent).write(');');
|
|
259
|
-
return writer;
|
|
175
|
+
return __generator(this, function (_a) {
|
|
176
|
+
sdkSource = this.createSourceFile();
|
|
177
|
+
if (Object.keys(this.schemas).length) {
|
|
178
|
+
this.createSchemasFile();
|
|
179
|
+
this.createTypesFile();
|
|
180
|
+
types = Array.from(this.types.keys());
|
|
181
|
+
types.sort();
|
|
182
|
+
sdkSource.addExportDeclarations([
|
|
183
|
+
{
|
|
184
|
+
isTypeOnly: true,
|
|
185
|
+
namedExports: types,
|
|
186
|
+
moduleSpecifier: './types'
|
|
187
|
+
},
|
|
188
|
+
]);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// If we don't have any schemas then we shouldn't import a `types` file that doesn't exist.
|
|
192
|
+
sdkSource
|
|
193
|
+
.getImportDeclarations()
|
|
194
|
+
.find(function (id) { return id.getText() === "import type * as types from './types';"; })
|
|
195
|
+
.remove();
|
|
196
|
+
}
|
|
197
|
+
// If this SDK doesn't use the `HTTPMethodRange` interface for handling `2XX` response status
|
|
198
|
+
// codes then we should remove it from being imported.
|
|
199
|
+
if (!this.usesHTTPMethodRangeInterface) {
|
|
200
|
+
sdkSource
|
|
201
|
+
.getImportDeclarations()
|
|
202
|
+
.find(function (id) { return id.getText().includes('HTTPMethodRange'); })
|
|
203
|
+
.replaceWithText("import type { ConfigOptions, FetchResponse } from 'api/dist/core'");
|
|
204
|
+
}
|
|
205
|
+
if (this.outputJS) {
|
|
206
|
+
return [2 /*return*/, this.project
|
|
207
|
+
.emitToMemory()
|
|
208
|
+
.getFiles()
|
|
209
|
+
.map(function (sourceFile) {
|
|
210
|
+
var _a;
|
|
211
|
+
var file = path_1["default"].basename(sourceFile.filePath);
|
|
212
|
+
if (file === 'schemas.js' || file === 'types.js') {
|
|
213
|
+
// If we're generating a JS SDK then we don't need to generate these two files as the
|
|
214
|
+
// user will have `.d.ts` files for them instead.
|
|
215
|
+
return {};
|
|
260
216
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
217
|
+
var code = (0, util_1.formatter)(sourceFile.text);
|
|
218
|
+
if (file === 'index.js' && _this.compilerTarget === 'cjs') {
|
|
219
|
+
/**
|
|
220
|
+
* There's an annoying quirk with `ts-morph` where if we're exporting a default export
|
|
221
|
+
* to a CJS environment, it'll export it as `exports.default`. Because we don't want
|
|
222
|
+
* folks in these environments to have to load their SDKs with
|
|
223
|
+
* `require('@api/sdk').default` we're overriding that here to change it to being the
|
|
224
|
+
* module exports.
|
|
225
|
+
*
|
|
226
|
+
* `ts-morph` unfortunately doesn't give us any options for programatically doing this
|
|
227
|
+
* so we need to resort to modifying the emitted JS code.
|
|
228
|
+
*/
|
|
229
|
+
code = code
|
|
230
|
+
.replace(/Object\.defineProperty\(exports, '__esModule', { value: true }\);\n/, '')
|
|
231
|
+
.replace('exports.default = createSDK;', 'module.exports = createSDK;');
|
|
232
|
+
}
|
|
233
|
+
return _a = {},
|
|
234
|
+
_a[file] = code,
|
|
235
|
+
_a;
|
|
236
|
+
})
|
|
237
|
+
.reduce(function (prev, next) { return Object.assign(prev, next); })];
|
|
238
|
+
}
|
|
239
|
+
return [2 /*return*/, __spreadArray(__spreadArray([], this.project.getSourceFiles().map(function (sourceFile) {
|
|
240
|
+
var _a;
|
|
241
|
+
return (_a = {},
|
|
242
|
+
_a[sourceFile.getBaseName()] = (0, util_1.formatter)(sourceFile.getFullText()),
|
|
243
|
+
_a);
|
|
244
|
+
}), true), this.project
|
|
245
|
+
.emitToMemory({ emitOnlyDtsFiles: true })
|
|
246
|
+
.getFiles()
|
|
247
|
+
.map(function (sourceFile) {
|
|
248
|
+
var _a;
|
|
249
|
+
return (_a = {},
|
|
250
|
+
_a[path_1["default"].basename(sourceFile.filePath)] = (0, util_1.formatter)(sourceFile.text),
|
|
251
|
+
_a);
|
|
252
|
+
}), true).reduce(function (prev, next) { return Object.assign(prev, next); })];
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* Create our main SDK source file.
|
|
258
|
+
*
|
|
259
|
+
*/
|
|
260
|
+
TSGenerator.prototype.createSourceFile = function () {
|
|
261
|
+
var _this = this;
|
|
262
|
+
var operations = this.loadOperationsAndMethods().operations;
|
|
263
|
+
var sourceFile = this.project.createSourceFile('index.ts', '');
|
|
264
|
+
sourceFile.addImportDeclarations([
|
|
265
|
+
// This import will be automatically removed later if the SDK ends up not having any types.
|
|
266
|
+
{ defaultImport: 'type * as types', moduleSpecifier: './types' },
|
|
267
|
+
{
|
|
268
|
+
// `HTTPMethodRange` will be conditionally removed later if it ends up not being used.
|
|
269
|
+
defaultImport: 'type { ConfigOptions, FetchResponse, HTTPMethodRange }',
|
|
270
|
+
moduleSpecifier: 'api/dist/core'
|
|
271
|
+
},
|
|
272
|
+
{ defaultImport: 'Oas', moduleSpecifier: 'oas' },
|
|
273
|
+
{ defaultImport: 'APICore', moduleSpecifier: 'api/dist/core' },
|
|
274
|
+
{ defaultImport: 'definition', moduleSpecifier: this.specPath },
|
|
275
|
+
]);
|
|
276
|
+
// @todo add TOS, License, info.* to a docblock at the top of the SDK.
|
|
277
|
+
this.sdk = sourceFile.addClass({
|
|
278
|
+
name: 'SDK',
|
|
279
|
+
properties: [
|
|
280
|
+
{ name: 'spec', type: 'Oas' },
|
|
281
|
+
{ name: 'core', type: 'APICore' },
|
|
282
|
+
]
|
|
283
|
+
});
|
|
284
|
+
this.sdk.addConstructor({
|
|
285
|
+
statements: function (writer) {
|
|
286
|
+
writer.writeLine('this.spec = Oas.init(definition);');
|
|
287
|
+
writer.write('this.core = new APICore(this.spec, ').quote(_this.userAgent).write(');');
|
|
288
|
+
return writer;
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
// Add our core API methods for controlling auth, servers, and various configurable abilities.
|
|
292
|
+
this.sdk.addMethods([
|
|
293
|
+
{
|
|
294
|
+
name: 'config',
|
|
295
|
+
parameters: [{ name: 'config', type: 'ConfigOptions' }],
|
|
296
|
+
statements: function (writer) { return writer.writeLine('this.core.setConfig(config);'); },
|
|
297
|
+
docs: [
|
|
298
|
+
{
|
|
299
|
+
description: function (writer) {
|
|
300
|
+
return writer.writeLine((0, util_1.wordWrap)('Optionally configure various options that the SDK allows.'));
|
|
301
|
+
},
|
|
302
|
+
tags: [
|
|
303
|
+
{ tagName: 'param', text: 'config Object of supported SDK options and toggles.' },
|
|
295
304
|
{
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
statements: function (writer) {
|
|
299
|
-
writer.writeLine('this.core.setAuth(...values);');
|
|
300
|
-
writer.writeLine('return this;');
|
|
301
|
-
return writer;
|
|
302
|
-
},
|
|
303
|
-
docs: [
|
|
304
|
-
{
|
|
305
|
-
description: function (writer) {
|
|
306
|
-
return writer.writeLine(wordWrap("If the API you're using requires authentication you can supply the required credentials through this method and the library will magically determine how they should be used within your API request.\n\nWith the exception of OpenID and MutualTLS, it supports all forms of authentication supported by the OpenAPI specification.\n\n@example <caption>HTTP Basic auth</caption>\nsdk.auth('username', 'password');\n\n@example <caption>Bearer tokens (HTTP or OAuth 2)</caption>\nsdk.auth('myBearerToken');\n\n@example <caption>API Keys</caption>\nsdk.auth('myApiKey');"));
|
|
307
|
-
},
|
|
308
|
-
tags: [
|
|
309
|
-
{ tagName: 'see', text: '{@link https://spec.openapis.org/oas/v3.0.3#fixed-fields-22}' },
|
|
310
|
-
{ tagName: 'see', text: '{@link https://spec.openapis.org/oas/v3.1.0#fixed-fields-22}' },
|
|
311
|
-
{
|
|
312
|
-
tagName: 'param',
|
|
313
|
-
text: 'values Your auth credentials for the API; can specify up to two strings or numbers.'
|
|
314
|
-
},
|
|
315
|
-
]
|
|
316
|
-
},
|
|
317
|
-
]
|
|
305
|
+
tagName: 'param',
|
|
306
|
+
text: (0, util_1.wordWrap)('config.timeout Override the default `fetch` request timeout of 30 seconds. This number should be represented in milliseconds.')
|
|
318
307
|
},
|
|
308
|
+
]
|
|
309
|
+
},
|
|
310
|
+
]
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: 'auth',
|
|
314
|
+
parameters: [{ name: '...values', type: 'string[] | number[]' }],
|
|
315
|
+
statements: function (writer) {
|
|
316
|
+
writer.writeLine('this.core.setAuth(...values);');
|
|
317
|
+
writer.writeLine('return this;');
|
|
318
|
+
return writer;
|
|
319
|
+
},
|
|
320
|
+
docs: [
|
|
321
|
+
{
|
|
322
|
+
description: function (writer) {
|
|
323
|
+
return writer.writeLine((0, util_1.wordWrap)("If the API you're using requires authentication you can supply the required credentials through this method and the library will magically determine how they should be used within your API request.\n\nWith the exception of OpenID and MutualTLS, it supports all forms of authentication supported by the OpenAPI specification.\n\n@example <caption>HTTP Basic auth</caption>\nsdk.auth('username', 'password');\n\n@example <caption>Bearer tokens (HTTP or OAuth 2)</caption>\nsdk.auth('myBearerToken');\n\n@example <caption>API Keys</caption>\nsdk.auth('myApiKey');"));
|
|
324
|
+
},
|
|
325
|
+
tags: [
|
|
326
|
+
{ tagName: 'see', text: '{@link https://spec.openapis.org/oas/v3.0.3#fixed-fields-22}' },
|
|
327
|
+
{ tagName: 'see', text: '{@link https://spec.openapis.org/oas/v3.1.0#fixed-fields-22}' },
|
|
319
328
|
{
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
{ name: 'url', type: 'string' },
|
|
323
|
-
{ name: 'variables', initializer: '{}' },
|
|
324
|
-
],
|
|
325
|
-
statements: function (writer) { return writer.writeLine('this.core.setServer(url, variables);'); },
|
|
326
|
-
docs: [
|
|
327
|
-
{
|
|
328
|
-
description: function (writer) {
|
|
329
|
-
return writer.writeLine(wordWrap("If the API you're using offers alternate server URLs, and server variables, you can tell the SDK which one to use with this method. To use it you can supply either one of the server URLs that are contained within the OpenAPI definition (along with any server variables), or you can pass it a fully qualified URL to use (that may or may not exist within the OpenAPI definition).\n\n@example <caption>Server URL with server variables</caption>\nsdk.server('https://{region}.api.example.com/{basePath}', {\n name: 'eu',\n basePath: 'v14',\n});\n\n@example <caption>Fully qualified server URL</caption>\nsdk.server('https://eu.api.example.com/v14');"));
|
|
330
|
-
},
|
|
331
|
-
tags: [
|
|
332
|
-
{ tagName: 'param', text: 'url Server URL' },
|
|
333
|
-
{ tagName: 'param', text: 'variables An object of variables to replace into the server URL.' },
|
|
334
|
-
]
|
|
335
|
-
},
|
|
336
|
-
]
|
|
329
|
+
tagName: 'param',
|
|
330
|
+
text: 'values Your auth credentials for the API; can specify up to two strings or numbers.'
|
|
337
331
|
},
|
|
338
|
-
]
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
332
|
+
]
|
|
333
|
+
},
|
|
334
|
+
]
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
name: 'server',
|
|
338
|
+
parameters: [
|
|
339
|
+
{ name: 'url', type: 'string' },
|
|
340
|
+
{ name: 'variables', initializer: '{}' },
|
|
341
|
+
],
|
|
342
|
+
statements: function (writer) { return writer.writeLine('this.core.setServer(url, variables);'); },
|
|
343
|
+
docs: [
|
|
344
|
+
{
|
|
345
|
+
description: function (writer) {
|
|
346
|
+
return writer.writeLine((0, util_1.wordWrap)("If the API you're using offers alternate server URLs, and server variables, you can tell the SDK which one to use with this method. To use it you can supply either one of the server URLs that are contained within the OpenAPI definition (along with any server variables), or you can pass it a fully qualified URL to use (that may or may not exist within the OpenAPI definition).\n\n@example <caption>Server URL with server variables</caption>\nsdk.server('https://{region}.api.example.com/{basePath}', {\n name: 'eu',\n basePath: 'v14',\n});\n\n@example <caption>Fully qualified server URL</caption>\nsdk.server('https://eu.api.example.com/v14');"));
|
|
347
|
+
},
|
|
348
|
+
tags: [
|
|
349
|
+
{ tagName: 'param', text: 'url Server URL' },
|
|
350
|
+
{ tagName: 'param', text: 'variables An object of variables to replace into the server URL.' },
|
|
351
|
+
]
|
|
352
|
+
},
|
|
353
|
+
]
|
|
354
|
+
},
|
|
355
|
+
]);
|
|
356
|
+
// Add all available operation ID accessors into the SDK.
|
|
357
|
+
Object.entries(operations).forEach(function (_a) {
|
|
358
|
+
var operationId = _a[0], data = _a[1];
|
|
359
|
+
_this.createOperationAccessor(data.operation, operationId, data.types.params, data.types.responses);
|
|
360
|
+
});
|
|
361
|
+
// Export our SDK into the source file.
|
|
362
|
+
sourceFile.addVariableStatement({
|
|
363
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
364
|
+
declarations: [
|
|
365
|
+
{
|
|
366
|
+
name: 'createSDK',
|
|
367
|
+
initializer: function (writer) {
|
|
368
|
+
// `ts-morph` doesn't have any way to cleanly create an IFEE.
|
|
369
|
+
writer.writeLine('(() => { return new SDK(); })()');
|
|
370
|
+
return writer;
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
]
|
|
374
|
+
});
|
|
375
|
+
sourceFile.addExportAssignment({ isExportEquals: false, expression: 'createSDK' });
|
|
376
|
+
return sourceFile;
|
|
377
|
+
};
|
|
378
|
+
/**
|
|
379
|
+
* Create our main schemas file. This is where all of the JSON Schema that our TypeScript typing
|
|
380
|
+
* infrastructure sources its data from. Without this there are no types.
|
|
381
|
+
*
|
|
382
|
+
*/
|
|
383
|
+
TSGenerator.prototype.createSchemasFile = function () {
|
|
384
|
+
var sourceFile = this.project.createSourceFile('schemas.ts', '');
|
|
385
|
+
var sortedSchemas = new Map(Array.from(Object.entries(this.schemas)).sort());
|
|
386
|
+
Array.from(sortedSchemas).forEach(function (_a) {
|
|
387
|
+
var schemaName = _a[0], schema = _a[1];
|
|
388
|
+
sourceFile.addVariableStatement({
|
|
389
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
390
|
+
declarations: [
|
|
391
|
+
{
|
|
392
|
+
name: schemaName,
|
|
393
|
+
initializer: function (writer) {
|
|
349
394
|
/**
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
* causes TS to throw a TS2309 error for "An export assignment cannot be used in a module
|
|
353
|
-
* with other exported elements" because our types and interfaces are also being exported and
|
|
354
|
-
* the `export =` overrides those.
|
|
355
|
-
*
|
|
356
|
-
* Fixing this is, to be frank, a fucking HARD problem for a couple reasons:
|
|
395
|
+
* This is the conversion prefix that we add to all `$ref` pointers we find in
|
|
396
|
+
* generated JSON Schema.
|
|
357
397
|
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
* source file here.
|
|
364
|
-
* 2. Though `ts-morph` has APIs for adding type aliases and interfaces to a source file what
|
|
365
|
-
* it doesn't have is the ability to pass in a string, or a `Writer` class that exposes,
|
|
366
|
-
* to write raw strings to a type or an interface. If it did we'd be able to replace this
|
|
367
|
-
* `addStatements` call with an `addTypeAlias` and `addInterface` call for each of our
|
|
368
|
-
* JSON Schema schemas that we've got along with an `isExported` flag for `ts-morph` to
|
|
369
|
-
* export it.
|
|
398
|
+
* Because the pointer name is a string we want to have it reference the schema
|
|
399
|
+
* constant we're adding into the codegen'd schema file. As there's no way, not even
|
|
400
|
+
* using `eval()` in this case, to convert a string to a constant we're prefixing
|
|
401
|
+
* them with this so we can later remove it and rewrite the value to a literal.
|
|
402
|
+
* eg. `'Pet'` becomes `Pet`.
|
|
370
403
|
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
* /slightly/ reduces the usability of the TS codegen functionality but in order for the TS
|
|
374
|
-
* declaration files that we generate to be valid this is the only option that we've got.
|
|
375
|
-
*
|
|
376
|
-
* However, that said, if somebody needs an interface or type exported they can export it
|
|
377
|
-
* themselves in the SDK code that we compile for them.
|
|
378
|
-
*
|
|
379
|
-
* @fixme
|
|
404
|
+
* And because our TypeScript type name generator properly ignores `:`, this is safe
|
|
405
|
+
* to prepend to all generated type names.
|
|
380
406
|
*/
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
});
|
|
386
|
-
if (this.outputJS) {
|
|
387
|
-
return [2 /*return*/, this.project
|
|
388
|
-
.emitToMemory()
|
|
389
|
-
.getFiles()
|
|
390
|
-
.map(function (sourceFile) {
|
|
391
|
-
var _a;
|
|
392
|
-
return (_a = {},
|
|
393
|
-
_a[path_1["default"].basename(sourceFile.filePath)] = TSGenerator.formatter(sourceFile.text),
|
|
394
|
-
_a);
|
|
395
|
-
})
|
|
396
|
-
.reduce(function (prev, next) { return Object.assign(prev, next); })];
|
|
407
|
+
var str = JSON.stringify(schema);
|
|
408
|
+
str = str.replace(/"::convert::([a-zA-Z_$\\d]*)"/g, '$1');
|
|
409
|
+
writer.writeLine("".concat(str, " as const"));
|
|
410
|
+
return writer;
|
|
397
411
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
return (_a = {},
|
|
401
|
-
_a[sourceFile.getBaseName()] = TSGenerator.formatter(sourceFile.getFullText()),
|
|
402
|
-
_a);
|
|
403
|
-
}), true), this.project
|
|
404
|
-
.emitToMemory({ emitOnlyDtsFiles: true })
|
|
405
|
-
.getFiles()
|
|
406
|
-
.map(function (sourceFile) {
|
|
407
|
-
var _a;
|
|
408
|
-
return (_a = {},
|
|
409
|
-
_a[path_1["default"].basename(sourceFile.filePath)] = TSGenerator.formatter(sourceFile.text),
|
|
410
|
-
_a);
|
|
411
|
-
}), true).reduce(function (prev, next) { return Object.assign(prev, next); })];
|
|
412
|
-
}
|
|
412
|
+
},
|
|
413
|
+
]
|
|
413
414
|
});
|
|
414
415
|
});
|
|
416
|
+
sourceFile.addStatements("export { ".concat(Array.from(sortedSchemas.keys()).join(', '), " }"));
|
|
417
|
+
return sourceFile;
|
|
415
418
|
};
|
|
416
419
|
/**
|
|
417
|
-
* Create
|
|
420
|
+
* Create our main types file. This sources its data from the JSON Schema `schemas.ts` file and
|
|
421
|
+
* will re-export types to be used in TypeScript implementations and IDE intellisense. This
|
|
422
|
+
* typing work is functional with the `json-schema-to-ts` library.
|
|
418
423
|
*
|
|
419
|
-
* @
|
|
424
|
+
* @see {@link https://npm.im/json-schema-to-ts}
|
|
420
425
|
*/
|
|
421
|
-
TSGenerator.prototype.
|
|
422
|
-
var
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
// Method generic body + metadata parameters are always optional.
|
|
431
|
-
if (method !== 'get') {
|
|
432
|
-
parameters.push({ name: 'body', type: 'unknown', hasQuestionToken: true });
|
|
433
|
-
docblock.tags.push({ tagName: 'param', text: 'body Request body payload data.' });
|
|
434
|
-
}
|
|
435
|
-
parameters.push({ name: 'metadata', type: 'Record<string, unknown>', hasQuestionToken: true });
|
|
436
|
-
docblock.tags.push({
|
|
437
|
-
tagName: 'param',
|
|
438
|
-
text: 'metadata Object containing all path, query, header, and cookie parameters to supply.'
|
|
426
|
+
TSGenerator.prototype.createTypesFile = function () {
|
|
427
|
+
var sourceFile = this.project.createSourceFile('types.ts', '');
|
|
428
|
+
sourceFile.addImportDeclarations([
|
|
429
|
+
{ defaultImport: 'type { FromSchema }', moduleSpecifier: 'json-schema-to-ts' },
|
|
430
|
+
{ defaultImport: '* as schemas', moduleSpecifier: './schemas' },
|
|
431
|
+
]);
|
|
432
|
+
Array.from(new Map(Array.from(this.types.entries()).sort())).forEach(function (_a) {
|
|
433
|
+
var typeName = _a[0], typeExpression = _a[1];
|
|
434
|
+
sourceFile.addTypeAlias({ isExported: true, name: typeName, type: typeExpression });
|
|
439
435
|
});
|
|
440
|
-
|
|
441
|
-
name: method,
|
|
442
|
-
returnType: 'Promise<T>',
|
|
443
|
-
parameters: parameters,
|
|
444
|
-
typeParameters: ['T = unknown'],
|
|
445
|
-
docs: [docblock],
|
|
446
|
-
statements: function (writer) {
|
|
447
|
-
/**
|
|
448
|
-
* @example return this.core.fetch(path, 'get', body, metadata);
|
|
449
|
-
* @example return this.core.fetch(path, 'get', metadata);
|
|
450
|
-
*/
|
|
451
|
-
var fetchStmt = writer.write('return this.core.fetch(path, ').quote(method).write(', ');
|
|
452
|
-
var fetchArgs = parameters.slice(1).map(function (p) { return p.name; });
|
|
453
|
-
fetchArgs.forEach(function (arg, i) {
|
|
454
|
-
fetchStmt.write(arg);
|
|
455
|
-
if (fetchArgs.length > 1 && i !== fetchArgs.length) {
|
|
456
|
-
fetchStmt.write(', ');
|
|
457
|
-
}
|
|
458
|
-
});
|
|
459
|
-
fetchStmt.write(');');
|
|
460
|
-
return fetchStmt;
|
|
461
|
-
}
|
|
462
|
-
}));
|
|
436
|
+
return sourceFile;
|
|
463
437
|
};
|
|
464
438
|
/**
|
|
465
439
|
* Create operation accessors on the SDK.
|
|
466
440
|
*
|
|
467
|
-
* @param operation
|
|
468
|
-
* @param operationId
|
|
469
|
-
* @param paramTypes
|
|
470
|
-
* @param responseTypes
|
|
471
441
|
*/
|
|
472
442
|
TSGenerator.prototype.createOperationAccessor = function (operation, operationId, paramTypes, responseTypes) {
|
|
473
443
|
var _this = this;
|
|
474
|
-
var docblock = {
|
|
444
|
+
var docblock = {};
|
|
475
445
|
var summary = operation.getSummary();
|
|
476
446
|
var description = operation.getDescription();
|
|
477
447
|
if (summary || description) {
|
|
@@ -480,16 +450,16 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
480
450
|
// what we surface the main docblock description.
|
|
481
451
|
docblock.description = function (writer) {
|
|
482
452
|
if (description) {
|
|
483
|
-
writer.writeLine(description);
|
|
453
|
+
writer.writeLine((0, util_1.docblockEscape)((0, util_1.wordWrap)(description)));
|
|
484
454
|
}
|
|
485
455
|
else if (summary) {
|
|
486
|
-
writer.writeLine(summary);
|
|
456
|
+
writer.writeLine((0, util_1.docblockEscape)((0, util_1.wordWrap)(summary)));
|
|
487
457
|
}
|
|
488
458
|
writer.newLineIfLastNot();
|
|
489
459
|
return writer;
|
|
490
460
|
};
|
|
491
461
|
if (summary && description) {
|
|
492
|
-
docblock.tags
|
|
462
|
+
docblock.tags = [{ tagName: 'summary', text: (0, util_1.docblockEscape)((0, util_1.wordWrap)(summary)) }];
|
|
493
463
|
}
|
|
494
464
|
}
|
|
495
465
|
var hasOptionalBody = false;
|
|
@@ -502,9 +472,7 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
502
472
|
hasOptionalBody = !operation.hasRequiredRequestBody();
|
|
503
473
|
parameters.body = {
|
|
504
474
|
name: 'body',
|
|
505
|
-
type: paramTypes.body
|
|
506
|
-
? this.schemas.get(paramTypes.body).tsType
|
|
507
|
-
: this.schemas.get(paramTypes.formData).tsType,
|
|
475
|
+
type: paramTypes.body ? paramTypes.body : paramTypes.formData,
|
|
508
476
|
hasQuestionToken: hasOptionalBody
|
|
509
477
|
};
|
|
510
478
|
}
|
|
@@ -512,27 +480,37 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
512
480
|
hasOptionalMetadata = !operation.hasRequiredParameters();
|
|
513
481
|
parameters.metadata = {
|
|
514
482
|
name: 'metadata',
|
|
515
|
-
type:
|
|
483
|
+
type: paramTypes.metadata,
|
|
516
484
|
hasQuestionToken: hasOptionalMetadata
|
|
517
485
|
};
|
|
518
486
|
}
|
|
519
487
|
}
|
|
520
|
-
var returnType = 'Promise<
|
|
521
|
-
var typeParameters = null;
|
|
488
|
+
var returnType = 'Promise<FetchResponse<number, unknown>>';
|
|
522
489
|
if (responseTypes) {
|
|
523
|
-
returnType = "Promise<".concat(Object.
|
|
524
|
-
.map(function (
|
|
490
|
+
returnType = "Promise<".concat(Object.entries(responseTypes)
|
|
491
|
+
.map(function (_a) {
|
|
492
|
+
var status = _a[0], responseType = _a[1];
|
|
493
|
+
if (status.toLowerCase() === 'default') {
|
|
494
|
+
return "FetchResponse<number, ".concat(responseType, ">");
|
|
495
|
+
}
|
|
496
|
+
else if (status.length === 3 && status.toUpperCase().endsWith('XX')) {
|
|
497
|
+
var statusPrefix = status.slice(0, 1);
|
|
498
|
+
if (!Number.isInteger(Number(statusPrefix))) {
|
|
499
|
+
// If this matches the `_XX` format, but it isn't `{number}XX` then we can't handle
|
|
500
|
+
// it and should instead fall back to treating it as an unknown number.
|
|
501
|
+
return "FetchResponse<number, ".concat(responseType, ">");
|
|
502
|
+
}
|
|
503
|
+
_this.usesHTTPMethodRangeInterface = true;
|
|
504
|
+
return "FetchResponse<HTTPMethodRange<".concat(statusPrefix, "00, ").concat(statusPrefix, "99>, ").concat(responseType, ">");
|
|
505
|
+
}
|
|
506
|
+
return "FetchResponse<".concat(status, ", ").concat(responseType, ">");
|
|
507
|
+
})
|
|
525
508
|
.join(' | '), ">");
|
|
526
509
|
}
|
|
527
|
-
else {
|
|
528
|
-
// We should only add the `<T>` method typing if we don't have any response types present.
|
|
529
|
-
typeParameters = ['T = unknown'];
|
|
530
|
-
}
|
|
531
510
|
var operationIdAccessor = this.sdk.addMethod({
|
|
532
511
|
name: operationId,
|
|
533
|
-
typeParameters: typeParameters,
|
|
534
512
|
returnType: returnType,
|
|
535
|
-
docs: docblock ? [docblock] : null,
|
|
513
|
+
docs: Object.keys(docblock).length ? [docblock] : null,
|
|
536
514
|
statements: function (writer) {
|
|
537
515
|
/**
|
|
538
516
|
* @example return this.core.fetch('/pet/findByStatus', 'get', body, metadata);
|
|
@@ -569,20 +547,18 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
569
547
|
if (shouldAddAltTypedOverloads) {
|
|
570
548
|
// Create an overload that has both `body` and `metadata` parameters as required.
|
|
571
549
|
operationIdAccessor.addOverload({
|
|
572
|
-
typeParameters: typeParameters,
|
|
573
550
|
parameters: [
|
|
574
551
|
__assign(__assign({}, parameters.body), { hasQuestionToken: false }),
|
|
575
552
|
__assign(__assign({}, parameters.metadata), { hasQuestionToken: false }),
|
|
576
553
|
],
|
|
577
554
|
returnType: returnType,
|
|
578
|
-
docs: docblock ? [docblock] : null
|
|
555
|
+
docs: Object.keys(docblock).length ? [docblock] : null
|
|
579
556
|
});
|
|
580
557
|
// Create an overload that just has a single `metadata` parameter.
|
|
581
558
|
operationIdAccessor.addOverload({
|
|
582
|
-
typeParameters: typeParameters,
|
|
583
559
|
parameters: [__assign({}, parameters.metadata)],
|
|
584
560
|
returnType: returnType,
|
|
585
|
-
docs: docblock ? [docblock] : null
|
|
561
|
+
docs: Object.keys(docblock).length ? [docblock] : null
|
|
586
562
|
});
|
|
587
563
|
// Create an overload that has both `body` and `metadata` parameters as optional. Even though
|
|
588
564
|
// our `metadata` parameter is actually required for this operation this is the only way we're
|
|
@@ -592,88 +568,18 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
592
568
|
// see if what the user is supplying is `metadata` or `body` content when they supply one or
|
|
593
569
|
// both.
|
|
594
570
|
operationIdAccessor.addParameters([
|
|
595
|
-
__assign(__assign({}, parameters.body), {
|
|
571
|
+
__assign(__assign({}, parameters.body), {
|
|
572
|
+
// Overloads have to be the most distilled version of the method so that's why we need to
|
|
573
|
+
// type `body` as either `body` or `metadata`. If we didn't do this, if `body` was a JSON
|
|
574
|
+
// Schema type that didn't allow `additionalProperties` then the implementation overload
|
|
575
|
+
// would throw type errors.
|
|
576
|
+
type: "".concat(parameters.body.type, " | ").concat(parameters.metadata.type), hasQuestionToken: true }),
|
|
596
577
|
__assign(__assign({}, parameters.metadata), { hasQuestionToken: true }),
|
|
597
578
|
]);
|
|
598
579
|
}
|
|
599
580
|
else {
|
|
600
581
|
operationIdAccessor.addParameters(Object.values(parameters));
|
|
601
582
|
}
|
|
602
|
-
// Add a typed generic HTTP method overload for this operation.
|
|
603
|
-
if (this.methodGenerics.has(operation.method)) {
|
|
604
|
-
// If we created alternate overloads for the operation accessor then we need to do the same
|
|
605
|
-
// for its generic HTTP counterpart.
|
|
606
|
-
if (shouldAddAltTypedOverloads) {
|
|
607
|
-
// Create an overload that has both `body` and `metadata` parameters as required.
|
|
608
|
-
this.methodGenerics.get(operation.method).addOverload({
|
|
609
|
-
typeParameters: typeParameters,
|
|
610
|
-
parameters: [
|
|
611
|
-
{ name: 'path', type: "'".concat(operation.path, "'") },
|
|
612
|
-
__assign(__assign({}, parameters.body), { hasQuestionToken: false }),
|
|
613
|
-
__assign(__assign({}, parameters.metadata), { hasQuestionToken: false }),
|
|
614
|
-
],
|
|
615
|
-
returnType: returnType,
|
|
616
|
-
docs: docblock ? [docblock] : null
|
|
617
|
-
});
|
|
618
|
-
// Create an overload that just has a single `metadata` parameter.
|
|
619
|
-
this.methodGenerics.get(operation.method).addOverload({
|
|
620
|
-
typeParameters: typeParameters,
|
|
621
|
-
parameters: [{ name: 'path', type: "'".concat(operation.path, "'") }, parameters.metadata],
|
|
622
|
-
returnType: returnType,
|
|
623
|
-
docs: docblock ? [docblock] : null
|
|
624
|
-
});
|
|
625
|
-
}
|
|
626
|
-
else {
|
|
627
|
-
this.methodGenerics.get(operation.method).addOverload({
|
|
628
|
-
typeParameters: responseTypes ? null : ['T = unknown'],
|
|
629
|
-
parameters: __spreadArray([{ name: 'path', type: "'".concat(operation.path, "'") }], Object.values(parameters), true),
|
|
630
|
-
returnType: returnType,
|
|
631
|
-
docs: docblock ? [docblock] : null
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
};
|
|
636
|
-
/**
|
|
637
|
-
* Convert a JSON Schema object into a readily available TypeScript type or interface along with
|
|
638
|
-
* any `$ref` pointers that are in use and turn those into TS types too.
|
|
639
|
-
*
|
|
640
|
-
* Under the hood this uses https://npm.im/json-schema-to-typescript for all composition and
|
|
641
|
-
* conversion.
|
|
642
|
-
*
|
|
643
|
-
* @param schema
|
|
644
|
-
* @param name
|
|
645
|
-
*/
|
|
646
|
-
TSGenerator.prototype.convertJSONSchemaToTypescript = function (schema, name) {
|
|
647
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
648
|
-
var ts, primaryType, tempProject, declarations;
|
|
649
|
-
var _this = this;
|
|
650
|
-
return __generator(this, function (_a) {
|
|
651
|
-
switch (_a.label) {
|
|
652
|
-
case 0: return [4 /*yield*/, (0, json_schema_to_typescript_1.compile)(schema, name, {
|
|
653
|
-
bannerComment: '',
|
|
654
|
-
// Running Prettier here for every JSON Schema object we're generating is way too slow so
|
|
655
|
-
// we're instead running it at the very end after we've constructed the SDK.
|
|
656
|
-
format: false
|
|
657
|
-
})];
|
|
658
|
-
case 1:
|
|
659
|
-
ts = _a.sent();
|
|
660
|
-
tempProject = this.project.createSourceFile("".concat(name, ".types.tmp.ts"), ts);
|
|
661
|
-
declarations = tempProject.getExportedDeclarations();
|
|
662
|
-
Array.from(declarations.keys()).forEach(function (declarationName) {
|
|
663
|
-
if (!primaryType) {
|
|
664
|
-
primaryType = declarationName;
|
|
665
|
-
}
|
|
666
|
-
declarations.get(declarationName).forEach(function (declaration) {
|
|
667
|
-
_this.types.set(declarationName, declaration.getText());
|
|
668
|
-
});
|
|
669
|
-
});
|
|
670
|
-
this.project.removeSourceFile(tempProject);
|
|
671
|
-
return [2 /*return*/, {
|
|
672
|
-
primaryType: primaryType
|
|
673
|
-
}];
|
|
674
|
-
}
|
|
675
|
-
});
|
|
676
|
-
});
|
|
677
583
|
};
|
|
678
584
|
/**
|
|
679
585
|
* Scour through the current OpenAPI definition and compile a store of every operation, along
|
|
@@ -682,78 +588,59 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
682
588
|
*
|
|
683
589
|
*/
|
|
684
590
|
TSGenerator.prototype.loadOperationsAndMethods = function () {
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
if (operation.hasOperationId()) {
|
|
708
|
-
operations[operationId] = {
|
|
709
|
-
types: {
|
|
710
|
-
params: params,
|
|
711
|
-
responses: responses
|
|
712
|
-
},
|
|
713
|
-
operation: operation
|
|
714
|
-
};
|
|
715
|
-
}
|
|
716
|
-
});
|
|
717
|
-
});
|
|
718
|
-
// Run through and convert every schema we need to use into TS types.
|
|
719
|
-
return [4 /*yield*/, Promise.all(Array.from(this.schemas.entries()).map(function (_a) {
|
|
720
|
-
var hash = _a[0], _b = _a[1], schema = _b.schema, schemaName = _b.name;
|
|
721
|
-
return __awaiter(_this, void 0, void 0, function () {
|
|
722
|
-
var ts;
|
|
723
|
-
return __generator(this, function (_c) {
|
|
724
|
-
switch (_c.label) {
|
|
725
|
-
case 0: return [4 /*yield*/, this.convertJSONSchemaToTypescript(schema, schemaName)];
|
|
726
|
-
case 1:
|
|
727
|
-
ts = _c.sent();
|
|
728
|
-
this.schemas.set(hash, __assign(__assign({}, this.schemas.get(hash)), { tsType: ts.primaryType }));
|
|
729
|
-
return [2 /*return*/];
|
|
730
|
-
}
|
|
731
|
-
});
|
|
732
|
-
});
|
|
733
|
-
}))];
|
|
734
|
-
case 1:
|
|
735
|
-
// Run through and convert every schema we need to use into TS types.
|
|
736
|
-
_a.sent();
|
|
737
|
-
return [2 /*return*/, {
|
|
738
|
-
operations: operations,
|
|
739
|
-
methods: methods
|
|
740
|
-
}];
|
|
741
|
-
}
|
|
591
|
+
var _this = this;
|
|
592
|
+
var operations = {};
|
|
593
|
+
var methods = new Set();
|
|
594
|
+
// Prepare all of the schemas that we need to process for every operation within this API
|
|
595
|
+
// definition.
|
|
596
|
+
Object.entries(this.spec.getPaths()).forEach(function (_a) {
|
|
597
|
+
var ops = _a[1];
|
|
598
|
+
Object.entries(ops).forEach(function (_a) {
|
|
599
|
+
var method = _a[0], operation = _a[1];
|
|
600
|
+
methods.add(method);
|
|
601
|
+
var operationId = operation.getOperationId({
|
|
602
|
+
// This `camelCase` option will clean up any weird characters that might be present in
|
|
603
|
+
// the `operationId` so as we don't break TS compilation with an invalid method accessor.
|
|
604
|
+
camelCase: true
|
|
605
|
+
});
|
|
606
|
+
operations[operationId] = {
|
|
607
|
+
types: {
|
|
608
|
+
params: _this.prepareParameterTypesForOperation(operation, operationId),
|
|
609
|
+
responses: _this.prepareResponseTypesForOperation(operation, operationId)
|
|
610
|
+
},
|
|
611
|
+
operation: operation
|
|
612
|
+
};
|
|
742
613
|
});
|
|
743
614
|
});
|
|
615
|
+
if (!Object.keys(operations).length) {
|
|
616
|
+
throw new Error('Sorry, this OpenAPI definition does not have any operation paths to generate an SDK for.');
|
|
617
|
+
}
|
|
618
|
+
return {
|
|
619
|
+
operations: operations,
|
|
620
|
+
methods: methods
|
|
621
|
+
};
|
|
744
622
|
};
|
|
745
623
|
/**
|
|
746
624
|
* Compile the parameter (path, query, cookie, and header) schemas for an API operation into
|
|
747
625
|
* usable TypeScript types.
|
|
748
626
|
*
|
|
749
|
-
* @param operation
|
|
750
|
-
* @param operationId
|
|
751
627
|
*/
|
|
752
628
|
TSGenerator.prototype.prepareParameterTypesForOperation = function (operation, operationId) {
|
|
753
629
|
var _this = this;
|
|
754
|
-
var schemas = operation.
|
|
630
|
+
var schemas = operation.getParametersAsJSONSchema({
|
|
631
|
+
includeDiscriminatorMappingRefs: false,
|
|
755
632
|
mergeIntoBodyAndMetadata: true,
|
|
756
|
-
retainDeprecatedProperties: true
|
|
633
|
+
retainDeprecatedProperties: true,
|
|
634
|
+
transformer: function (s) {
|
|
635
|
+
// As our schemas are dereferenced in the `oas` library we don't want to pollute our
|
|
636
|
+
// codegen'd schemas file with duplicate schemas.
|
|
637
|
+
if ('x-readme-ref-name' in s) {
|
|
638
|
+
var typeName = (0, util_1.generateTypeName)(s['x-readme-ref-name']);
|
|
639
|
+
_this.addSchemaToExport(s, typeName, typeName);
|
|
640
|
+
return "::convert::".concat(typeName);
|
|
641
|
+
}
|
|
642
|
+
return s;
|
|
643
|
+
}
|
|
757
644
|
});
|
|
758
645
|
if (!schemas || !schemas.length) {
|
|
759
646
|
return false;
|
|
@@ -768,19 +655,20 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
768
655
|
.map(function (_a) {
|
|
769
656
|
var _b;
|
|
770
657
|
var paramType = _a[0], schema = _a[1];
|
|
771
|
-
var
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
});
|
|
658
|
+
var typeName;
|
|
659
|
+
if (typeof schema === 'string' && schema.startsWith('::convert::')) {
|
|
660
|
+
// If this schema is a string and has our conversion prefix then we've already created
|
|
661
|
+
// a type for it.
|
|
662
|
+
typeName = schema.replace('::convert::', '');
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
typeName = (0, util_1.generateTypeName)(operationId, paramType, 'param');
|
|
666
|
+
_this.addSchemaToExport(schema, typeName, "".concat((0, util_1.generateTypeName)(operationId), ".").concat(paramType));
|
|
781
667
|
}
|
|
782
668
|
return _b = {},
|
|
783
|
-
|
|
669
|
+
// Types are prefixed with `types.` because that's how we're importing them from
|
|
670
|
+
// `types.d.ts`.
|
|
671
|
+
_b[paramType] = "types.".concat(typeName),
|
|
784
672
|
_b;
|
|
785
673
|
})
|
|
786
674
|
.reduce(function (prev, next) { return Object.assign(prev, next); }, {});
|
|
@@ -788,9 +676,6 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
788
676
|
/**
|
|
789
677
|
* Compile the response schemas for an API operation into usable TypeScript types.
|
|
790
678
|
*
|
|
791
|
-
* @todo what does this do for a spec that has no responses?
|
|
792
|
-
* @param operation
|
|
793
|
-
* @param operationId
|
|
794
679
|
*/
|
|
795
680
|
TSGenerator.prototype.prepareResponseTypesForOperation = function (operation, operationId) {
|
|
796
681
|
var _this = this;
|
|
@@ -801,7 +686,19 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
801
686
|
var schemas = responseStatusCodes
|
|
802
687
|
.map(function (status) {
|
|
803
688
|
var _a;
|
|
804
|
-
var schema = operation.
|
|
689
|
+
var schema = operation.getResponseAsJSONSchema(status, {
|
|
690
|
+
includeDiscriminatorMappingRefs: false,
|
|
691
|
+
transformer: function (s) {
|
|
692
|
+
// As our schemas are dereferenced in the `oas` library we don't want to pollute our
|
|
693
|
+
// codegen'd schemas file with duplicate schemas.
|
|
694
|
+
if ('x-readme-ref-name' in s) {
|
|
695
|
+
var typeName = (0, util_1.generateTypeName)(s['x-readme-ref-name']);
|
|
696
|
+
_this.addSchemaToExport(s, typeName, "".concat(typeName));
|
|
697
|
+
return "::convert::".concat(typeName);
|
|
698
|
+
}
|
|
699
|
+
return s;
|
|
700
|
+
}
|
|
701
|
+
});
|
|
805
702
|
if (!schema) {
|
|
806
703
|
return false;
|
|
807
704
|
}
|
|
@@ -814,24 +711,39 @@ var TSGenerator = /** @class */ (function (_super) {
|
|
|
814
711
|
.map(function (_a) {
|
|
815
712
|
var _b;
|
|
816
713
|
var status = _a[0], schema = _a[1].schema;
|
|
817
|
-
var
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
714
|
+
var typeName;
|
|
715
|
+
if (typeof schema === 'string' && schema.startsWith('::convert::')) {
|
|
716
|
+
// If this schema is a string and has our conversion prefix then we've already created
|
|
717
|
+
// a type for it.
|
|
718
|
+
typeName = schema.replace('::convert::', '');
|
|
719
|
+
}
|
|
720
|
+
else {
|
|
721
|
+
typeName = (0, util_1.generateTypeName)(operationId, 'response', status);
|
|
722
|
+
// Because `status` will usually be a number here we need to set the pointer for it
|
|
723
|
+
// within an `[]` as if we do `FromSchema<typeof schemas.operation.response.200>`,
|
|
724
|
+
// TypeScript will throw a compilation error.
|
|
725
|
+
_this.addSchemaToExport(schema, typeName, "".concat((0, util_1.generateTypeName)(operationId), ".response['").concat(status, "']"));
|
|
827
726
|
}
|
|
828
727
|
return _b = {},
|
|
829
|
-
|
|
728
|
+
// Types are prefixed with `types.` because that's how we're importing them from
|
|
729
|
+
// `types.d.ts`.
|
|
730
|
+
_b[status] = "types.".concat(typeName),
|
|
830
731
|
_b;
|
|
831
732
|
})
|
|
832
733
|
.reduce(function (prev, next) { return Object.assign(prev, next); }, {});
|
|
833
734
|
return Object.keys(res).length ? res : undefined;
|
|
834
735
|
};
|
|
736
|
+
/**
|
|
737
|
+
* Add a given schema into our schema dataset that we'll be be exporting as types.
|
|
738
|
+
*
|
|
739
|
+
*/
|
|
740
|
+
TSGenerator.prototype.addSchemaToExport = function (schema, typeName, pointer) {
|
|
741
|
+
if (this.types.has(typeName)) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
(0, lodash_setwith_1["default"])(this.schemas, pointer, schema, Object);
|
|
745
|
+
this.types.set(typeName, "FromSchema<typeof schemas.".concat(pointer, ">"));
|
|
746
|
+
};
|
|
835
747
|
return TSGenerator;
|
|
836
748
|
}(language_1["default"]));
|
|
837
749
|
exports["default"] = TSGenerator;
|