api 4.5.1 → 5.0.0-beta.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/LICENSE +1 -1
- package/README.md +4 -5
- package/bin/api +2 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +91 -0
- package/dist/cache.d.ts +30 -0
- package/dist/cache.js +217 -0
- package/dist/cli/codegen/index.d.ts +4 -0
- package/dist/cli/codegen/index.js +23 -0
- package/dist/cli/codegen/language.d.ts +27 -0
- package/dist/cli/codegen/language.js +19 -0
- package/dist/cli/codegen/languages/typescript.d.ts +99 -0
- package/dist/cli/codegen/languages/typescript.js +762 -0
- package/dist/cli/commands/index.d.ts +4 -0
- package/dist/cli/commands/index.js +9 -0
- package/dist/cli/commands/install.d.ts +3 -0
- package/dist/cli/commands/install.js +230 -0
- package/dist/cli/lib/prompt.d.ts +9 -0
- package/dist/cli/lib/prompt.js +81 -0
- package/dist/cli/logger.d.ts +1 -0
- package/dist/cli/logger.js +16 -0
- package/dist/cli/storage.d.ts +105 -0
- package/dist/cli/storage.js +264 -0
- package/dist/core/getJSONSchemaDefaults.d.ts +15 -0
- package/dist/core/getJSONSchemaDefaults.js +62 -0
- package/dist/core/index.d.ts +32 -0
- package/dist/core/index.js +143 -0
- package/dist/core/parseResponse.d.ts +1 -0
- package/dist/core/parseResponse.js +65 -0
- package/dist/core/prepareAuth.d.ts +5 -0
- package/dist/core/prepareAuth.js +55 -0
- package/dist/core/prepareParams.d.ts +24 -0
- package/dist/core/prepareParams.js +351 -0
- package/dist/core/prepareServer.d.ts +13 -0
- package/dist/core/prepareServer.js +50 -0
- package/dist/fetcher.d.ts +53 -0
- package/dist/fetcher.js +149 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +276 -0
- package/dist/packageInfo.d.ts +2 -0
- package/dist/packageInfo.js +6 -0
- package/package.json +65 -26
- package/src/.sink.d.ts +1 -0
- package/src/bin.ts +20 -0
- package/src/cache.ts +212 -0
- package/src/cli/codegen/index.ts +31 -0
- package/src/cli/codegen/language.ts +47 -0
- package/src/cli/codegen/languages/typescript.ts +798 -0
- package/src/cli/commands/index.ts +5 -0
- package/src/cli/commands/install.ts +196 -0
- package/src/cli/lib/prompt.ts +29 -0
- package/src/cli/logger.ts +10 -0
- package/src/cli/storage.ts +297 -0
- package/src/core/getJSONSchemaDefaults.ts +74 -0
- package/src/core/index.ts +108 -0
- package/src/{lib/parseResponse.js → core/parseResponse.ts} +5 -7
- package/src/core/prepareAuth.ts +85 -0
- package/src/core/prepareParams.ts +338 -0
- package/src/{lib/prepareServer.js → core/prepareServer.ts} +13 -12
- package/src/fetcher.ts +126 -0
- package/src/index.ts +212 -0
- package/src/packageInfo.ts +3 -0
- package/src/typings.d.ts +3 -0
- package/tsconfig.json +24 -0
- package/src/cache.js +0 -225
- package/src/index.js +0 -177
- package/src/lib/getSchema.js +0 -34
- package/src/lib/index.js +0 -11
- package/src/lib/prepareAuth.js +0 -69
- package/src/lib/prepareParams.js +0 -198
package/dist/index.js
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
24
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (_) try {
|
|
29
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
50
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
51
|
+
if (ar || !(i in from)) {
|
|
52
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
53
|
+
ar[i] = from[i];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
57
|
+
};
|
|
58
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
59
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
60
|
+
};
|
|
61
|
+
var oas_1 = __importDefault(require("oas"));
|
|
62
|
+
var core_1 = __importDefault(require("./core"));
|
|
63
|
+
var cache_1 = __importDefault(require("./cache"));
|
|
64
|
+
var packageInfo_1 = require("./packageInfo");
|
|
65
|
+
var Sdk = /** @class */ (function () {
|
|
66
|
+
function Sdk(uri, opts) {
|
|
67
|
+
if (opts === void 0) { opts = {}; }
|
|
68
|
+
this.uri = uri;
|
|
69
|
+
this.userAgent = "".concat(packageInfo_1.PACKAGE_NAME, " (node)/").concat(packageInfo_1.PACKAGE_VERSION);
|
|
70
|
+
this.cacheDir = opts.cacheDir ? opts.cacheDir : false;
|
|
71
|
+
}
|
|
72
|
+
Sdk.prototype.load = function () {
|
|
73
|
+
var cache = new cache_1["default"](this.uri, this.cacheDir);
|
|
74
|
+
var userAgent = this.userAgent;
|
|
75
|
+
var core = new core_1["default"]();
|
|
76
|
+
core.setUserAgent(userAgent);
|
|
77
|
+
var isLoaded = false;
|
|
78
|
+
var isCached = cache.isCached();
|
|
79
|
+
var sdk = {};
|
|
80
|
+
/**
|
|
81
|
+
* Create dynamic accessors for every HTTP method that the OpenAPI specification supports.
|
|
82
|
+
*
|
|
83
|
+
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#fixed-fields-7}
|
|
84
|
+
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#fixed-fields-7}
|
|
85
|
+
*/
|
|
86
|
+
function loadMethods() {
|
|
87
|
+
return ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace']
|
|
88
|
+
.map(function (httpVerb) {
|
|
89
|
+
var _a;
|
|
90
|
+
return _a = {},
|
|
91
|
+
_a[httpVerb] = (function (method, path) {
|
|
92
|
+
var args = [];
|
|
93
|
+
for (var _i = 2; _i < arguments.length; _i++) {
|
|
94
|
+
args[_i - 2] = arguments[_i];
|
|
95
|
+
}
|
|
96
|
+
return core.fetch.apply(core, __spreadArray([path, method], args, false));
|
|
97
|
+
}).bind(null, httpVerb),
|
|
98
|
+
_a;
|
|
99
|
+
})
|
|
100
|
+
.reduce(function (prev, next) { return Object.assign(prev, next); });
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Create dynamic accessors for every operation with a defined operation ID. If an operation
|
|
104
|
+
* does not have an operation ID it can be accessed by its `.method('/path')` accessor instead.
|
|
105
|
+
*
|
|
106
|
+
* @param spec
|
|
107
|
+
*/
|
|
108
|
+
function loadOperations(spec) {
|
|
109
|
+
return Object.entries(spec.getPaths())
|
|
110
|
+
.map(function (_a) {
|
|
111
|
+
var operations = _a[1];
|
|
112
|
+
return Object.values(operations);
|
|
113
|
+
})
|
|
114
|
+
.reduce(function (prev, next) { return prev.concat(next); }, [])
|
|
115
|
+
.filter(function (operation) { return operation.hasOperationId(); })
|
|
116
|
+
.reduce(function (prev, next) {
|
|
117
|
+
var _a;
|
|
118
|
+
// `getOperationId()` creates dynamic operation IDs when one isn't available but we need
|
|
119
|
+
// to know here if we actually have one present or not. The `camelCase` option here also
|
|
120
|
+
// cleans up any `operationId` that we might have into something that can be used as a
|
|
121
|
+
// valid JS method.
|
|
122
|
+
var operationId = next.getOperationId({ camelCase: true });
|
|
123
|
+
return Object.assign(prev, (_a = {},
|
|
124
|
+
_a[operationId] = (function (operation) {
|
|
125
|
+
var args = [];
|
|
126
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
127
|
+
args[_i - 1] = arguments[_i];
|
|
128
|
+
}
|
|
129
|
+
return core.fetchOperation.apply(core, __spreadArray([operation], args, false));
|
|
130
|
+
}).bind(null, next),
|
|
131
|
+
_a));
|
|
132
|
+
}, {});
|
|
133
|
+
}
|
|
134
|
+
function loadFromCache() {
|
|
135
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
136
|
+
var cachedSpec, spec;
|
|
137
|
+
return __generator(this, function (_a) {
|
|
138
|
+
switch (_a.label) {
|
|
139
|
+
case 0:
|
|
140
|
+
if (!isCached) return [3 /*break*/, 2];
|
|
141
|
+
return [4 /*yield*/, cache.get()];
|
|
142
|
+
case 1:
|
|
143
|
+
cachedSpec = _a.sent();
|
|
144
|
+
return [3 /*break*/, 4];
|
|
145
|
+
case 2: return [4 /*yield*/, cache.load()];
|
|
146
|
+
case 3:
|
|
147
|
+
cachedSpec = _a.sent();
|
|
148
|
+
isCached = true;
|
|
149
|
+
_a.label = 4;
|
|
150
|
+
case 4:
|
|
151
|
+
spec = new oas_1["default"](cachedSpec);
|
|
152
|
+
core.setSpec(spec);
|
|
153
|
+
sdk = Object.assign(sdk, __assign(__assign({}, loadMethods()), loadOperations(spec)));
|
|
154
|
+
isLoaded = true;
|
|
155
|
+
return [2 /*return*/];
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
var sdkProxy = {
|
|
161
|
+
// @give this a better type than any
|
|
162
|
+
get: function (target, method) {
|
|
163
|
+
// Since auth returns a self-proxy, we **do not** want it to fall through into the async
|
|
164
|
+
// function below as when that'll happen, instead of returning a self-proxy, it'll end up
|
|
165
|
+
// returning a Promise. When that happens, chaining `sdk.auth().operationId()` will fail.
|
|
166
|
+
if (['auth', 'config'].includes(method)) {
|
|
167
|
+
// @todo split this up so we have better types for `auth` and `config`
|
|
168
|
+
return function () {
|
|
169
|
+
var args = [];
|
|
170
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
171
|
+
args[_i] = arguments[_i];
|
|
172
|
+
}
|
|
173
|
+
return target[method].apply(this, args);
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return function () {
|
|
177
|
+
var args = [];
|
|
178
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
179
|
+
args[_i] = arguments[_i];
|
|
180
|
+
}
|
|
181
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
182
|
+
return __generator(this, function (_a) {
|
|
183
|
+
switch (_a.label) {
|
|
184
|
+
case 0:
|
|
185
|
+
if (!!(method in target)) return [3 /*break*/, 2];
|
|
186
|
+
// If this method doesn't exist on the proxy, have we loaded the SDK? If we have, then
|
|
187
|
+
// this method isn't valid.
|
|
188
|
+
if (isLoaded) {
|
|
189
|
+
throw new Error("Sorry, `".concat(method, "` does not appear to be a valid operation on this API."));
|
|
190
|
+
}
|
|
191
|
+
return [4 /*yield*/, loadFromCache()];
|
|
192
|
+
case 1:
|
|
193
|
+
_a.sent();
|
|
194
|
+
// If after loading the SDK and this method still doesn't exist, then it's not real!
|
|
195
|
+
if (!(method in sdk)) {
|
|
196
|
+
throw new Error("Sorry, `".concat(method, "` does not appear to be a valid operation on this API."));
|
|
197
|
+
}
|
|
198
|
+
// @todo give sdk a better type
|
|
199
|
+
return [2 /*return*/, sdk[method].apply(this, args)];
|
|
200
|
+
case 2: return [2 /*return*/, target[method].apply(this, args)];
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
sdk = {
|
|
208
|
+
/**
|
|
209
|
+
* If the API you're using requires authentication you can supply the required credentials
|
|
210
|
+
* through this method and the library will magically determine how they should be used
|
|
211
|
+
* within your API request.
|
|
212
|
+
*
|
|
213
|
+
* With the exception of OpenID and MutualTLS, it supports all forms of authentication
|
|
214
|
+
* supported by the OpenAPI specification.
|
|
215
|
+
*
|
|
216
|
+
* @example <caption>HTTP Basic auth</caption>
|
|
217
|
+
* sdk.auth('username', 'password');
|
|
218
|
+
*
|
|
219
|
+
* @example <caption>Bearer tokens (HTTP or OAuth 2)</caption>
|
|
220
|
+
* sdk.auth('myBearerToken');
|
|
221
|
+
*
|
|
222
|
+
* @example <caption>API Keys</caption>
|
|
223
|
+
* sdk.auth('myApiKey');
|
|
224
|
+
*
|
|
225
|
+
* @see {@link https://spec.openapis.org/oas/v3.0.3#fixed-fields-22}
|
|
226
|
+
* @see {@link https://spec.openapis.org/oas/v3.1.0#fixed-fields-22}
|
|
227
|
+
* @param values Your auth credentials for the API. Can specify up to two strings or numbers.
|
|
228
|
+
*/
|
|
229
|
+
auth: function () {
|
|
230
|
+
var values = [];
|
|
231
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
232
|
+
values[_i] = arguments[_i];
|
|
233
|
+
}
|
|
234
|
+
core.setAuth.apply(core, values);
|
|
235
|
+
},
|
|
236
|
+
/**
|
|
237
|
+
* Optionally configure various options, such as response parsing, that the SDK allows.
|
|
238
|
+
*
|
|
239
|
+
* @param config Object of supported SDK options and toggles.
|
|
240
|
+
* @param config.parseResponse If responses are parsed according to its `Content-Type` header.
|
|
241
|
+
*/
|
|
242
|
+
config: function (config) {
|
|
243
|
+
core.setConfig(config);
|
|
244
|
+
},
|
|
245
|
+
/**
|
|
246
|
+
* If the API you're using offers alternate server URLs, and server variables, you can tell
|
|
247
|
+
* the SDK which one to use with this method. To use it you can supply either one of the
|
|
248
|
+
* server URLs that are contained within the OpenAPI definition (along with any server
|
|
249
|
+
* variables), or you can pass it a fully qualified URL to use (that may or may not exist
|
|
250
|
+
* within the OpenAPI definition).
|
|
251
|
+
*
|
|
252
|
+
* @example <caption>Server URL with server variables</caption>
|
|
253
|
+
* sdk.server('https://{region}.api.example.com/{basePath}', {
|
|
254
|
+
* name: 'eu',
|
|
255
|
+
* basePath: 'v14',
|
|
256
|
+
* });
|
|
257
|
+
*
|
|
258
|
+
* @example <caption>Fully qualified server URL</caption>
|
|
259
|
+
* sdk.server('https://eu.api.example.com/v14');
|
|
260
|
+
*
|
|
261
|
+
* @param url Server URL
|
|
262
|
+
* @param variables An object of variables to replace into the server URL.
|
|
263
|
+
*/
|
|
264
|
+
server: function (url, variables) {
|
|
265
|
+
if (variables === void 0) { variables = {}; }
|
|
266
|
+
core.setServer(url, variables);
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
return new Proxy(sdk, sdkProxy);
|
|
270
|
+
};
|
|
271
|
+
return Sdk;
|
|
272
|
+
}());
|
|
273
|
+
module.exports = function (uri, opts) {
|
|
274
|
+
if (opts === void 0) { opts = {}; }
|
|
275
|
+
return new Sdk(uri, opts).load();
|
|
276
|
+
};
|
package/package.json
CHANGED
|
@@ -1,56 +1,95 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0-beta.0",
|
|
4
4
|
"description": "Generate an SDK from an OpenAPI definition",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"api": "./bin/api"
|
|
9
|
+
},
|
|
6
10
|
"scripts": {
|
|
7
|
-
"
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"debug:bin": "node -r ts-node/register src/bin.ts",
|
|
13
|
+
"lint": "eslint . --ext .js,.ts",
|
|
14
|
+
"prebuild": "rm -rf dist/; npm run prebuild.packageConfig",
|
|
15
|
+
"prebuild.packageConfig": "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",
|
|
16
|
+
"prepack": "npm run build",
|
|
8
17
|
"pretest": "npm run lint",
|
|
9
|
-
"prettier": "prettier --list-different --write \"./**/**.js\"",
|
|
10
|
-
"test": "
|
|
18
|
+
"prettier": "prettier --list-different --write \"./**/**.{js,ts}\"",
|
|
19
|
+
"test": "nyc mocha \"test/**/*.test.ts\""
|
|
11
20
|
},
|
|
12
21
|
"repository": {
|
|
13
22
|
"type": "git",
|
|
14
23
|
"url": "https://github.com/readmeio/api.git",
|
|
15
24
|
"directory": "packages/api"
|
|
16
25
|
},
|
|
26
|
+
"homepage": "https://github.com/readmeio/api",
|
|
17
27
|
"bugs": {
|
|
18
28
|
"url": "https://github.com/readmeio/api/issues"
|
|
19
29
|
},
|
|
20
30
|
"author": "Jon Ursenbach <jon@readme.io>",
|
|
21
31
|
"license": "MIT",
|
|
22
32
|
"engines": {
|
|
23
|
-
"node": "
|
|
33
|
+
"node": ">=14"
|
|
24
34
|
},
|
|
25
35
|
"dependencies": {
|
|
26
|
-
"@readme/oas-to-har": "^
|
|
27
|
-
"@readme/openapi-parser": "^2.
|
|
36
|
+
"@readme/oas-to-har": "^17.0.8",
|
|
37
|
+
"@readme/openapi-parser": "^2.2.0",
|
|
38
|
+
"chalk": "^4.1.2",
|
|
39
|
+
"commander": "^9.2.0",
|
|
28
40
|
"datauri": "^4.1.0",
|
|
29
|
-
"
|
|
41
|
+
"execa": "^5.1.1",
|
|
42
|
+
"fetch-har": "^8.0.3",
|
|
43
|
+
"figures": "^3.2.0",
|
|
30
44
|
"find-cache-dir": "^3.3.1",
|
|
31
|
-
"form-data": "^
|
|
32
|
-
"
|
|
45
|
+
"form-data-encoder": "^1.7.2",
|
|
46
|
+
"formdata-node": "^4.3.2",
|
|
47
|
+
"get-stream": "^6.0.1",
|
|
48
|
+
"isomorphic-fetch": "^3.0.0",
|
|
33
49
|
"js-yaml": "^4.1.0",
|
|
50
|
+
"json-schema-to-typescript": "^11.0.1",
|
|
51
|
+
"json-schema-traverse": "^1.0.0",
|
|
52
|
+
"lodash.merge": "^4.6.2",
|
|
34
53
|
"make-dir": "^3.1.0",
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
54
|
+
"oas": "^18.3.3",
|
|
55
|
+
"object-hash": "^3.0.0",
|
|
56
|
+
"ora": "^5.4.1",
|
|
57
|
+
"prompts": "^2.4.2",
|
|
58
|
+
"ssri": "^9.0.0",
|
|
59
|
+
"ts-morph": "^15.1.0",
|
|
60
|
+
"validate-npm-package-name": "^4.0.0"
|
|
38
61
|
},
|
|
39
62
|
"devDependencies": {
|
|
40
|
-
"@readme/eslint-config": "^8.
|
|
41
|
-
"@readme/oas-examples": "^4.
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
63
|
+
"@readme/eslint-config": "^8.7.3",
|
|
64
|
+
"@readme/oas-examples": "^5.4.1",
|
|
65
|
+
"@types/chai": "^4.3.1",
|
|
66
|
+
"@types/find-cache-dir": "^3.2.1",
|
|
67
|
+
"@types/js-yaml": "^4.0.5",
|
|
68
|
+
"@types/lodash.merge": "^4.6.7",
|
|
69
|
+
"@types/mocha": "^9.1.1",
|
|
70
|
+
"@types/object-hash": "^2.2.1",
|
|
71
|
+
"@types/prompts": "^2.0.14",
|
|
72
|
+
"@types/sinon-chai": "^3.2.8",
|
|
73
|
+
"@types/ssri": "^7.1.1",
|
|
74
|
+
"@types/validate-npm-package-name": "^4.0.0",
|
|
75
|
+
"chai": "^4.3.6",
|
|
76
|
+
"eslint": "^8.14.0",
|
|
77
|
+
"fetch-mock": "^9.11.0",
|
|
78
|
+
"mocha": "^10.0.0",
|
|
79
|
+
"mock-require": "^3.0.3",
|
|
80
|
+
"nyc": "^15.1.0",
|
|
81
|
+
"prettier": "^2.6.2",
|
|
82
|
+
"sinon": "^14.0.0",
|
|
83
|
+
"sinon-chai": "^3.7.0",
|
|
84
|
+
"typescript": "^4.6.4",
|
|
85
|
+
"unique-temp-dir": "^1.0.0"
|
|
47
86
|
},
|
|
48
87
|
"prettier": "@readme/eslint-config/prettier",
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
88
|
+
"nyc": {
|
|
89
|
+
"exclude": [
|
|
90
|
+
"dist/",
|
|
91
|
+
"test/"
|
|
53
92
|
]
|
|
54
93
|
},
|
|
55
|
-
"gitHead": "
|
|
94
|
+
"gitHead": "d18f7e3a1c20edaf84d0c5c2e1a64714549a4ee6"
|
|
56
95
|
}
|
package/src/.sink.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module 'form-data-encoder';
|
package/src/bin.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import * as pkg from './packageInfo';
|
|
3
|
+
import commands from './cli/commands';
|
|
4
|
+
|
|
5
|
+
(async () => {
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program.name(pkg.PACKAGE_NAME);
|
|
8
|
+
program.version(pkg.PACKAGE_VERSION);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Instead of using Commander's `executableDir` API for loading in external command files we're
|
|
12
|
+
* programatically doing it like this because it's cleaner for us to let Commander manage option
|
|
13
|
+
* and argument parsing within this file than having each command manage that itself.
|
|
14
|
+
*/
|
|
15
|
+
Object.entries(commands).forEach(([, cmd]: [string, Command]) => {
|
|
16
|
+
program.addCommand(cmd);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
await program.parseAsync(process.argv);
|
|
20
|
+
})();
|
package/src/cache.ts
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import type { OASDocument } from 'oas/@types/rmoas.types';
|
|
2
|
+
|
|
3
|
+
import 'isomorphic-fetch';
|
|
4
|
+
import OpenAPIParser from '@readme/openapi-parser';
|
|
5
|
+
import crypto from 'crypto';
|
|
6
|
+
import findCacheDir from 'find-cache-dir';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import makeDir from 'make-dir';
|
|
11
|
+
|
|
12
|
+
import { PACKAGE_NAME } from './packageInfo';
|
|
13
|
+
|
|
14
|
+
import Fetcher from './fetcher';
|
|
15
|
+
|
|
16
|
+
type CacheStore = Record<
|
|
17
|
+
string,
|
|
18
|
+
{
|
|
19
|
+
hash: string;
|
|
20
|
+
path?: string; // Deprecated in v4.5.0 in favor of `hash`.
|
|
21
|
+
original: string | OASDocument;
|
|
22
|
+
title?: string;
|
|
23
|
+
version?: string;
|
|
24
|
+
}
|
|
25
|
+
>;
|
|
26
|
+
|
|
27
|
+
export default class Cache {
|
|
28
|
+
static dir: string;
|
|
29
|
+
|
|
30
|
+
static cacheStore: string;
|
|
31
|
+
|
|
32
|
+
static specsCache: string;
|
|
33
|
+
|
|
34
|
+
uri: string | OASDocument;
|
|
35
|
+
|
|
36
|
+
uriHash: string;
|
|
37
|
+
|
|
38
|
+
cached: false | CacheStore;
|
|
39
|
+
|
|
40
|
+
fetcher: Fetcher;
|
|
41
|
+
|
|
42
|
+
constructor(uri: string | OASDocument, cacheDir: string | false = false) {
|
|
43
|
+
Cache.setCacheDir(cacheDir);
|
|
44
|
+
Cache.cacheStore = path.join(Cache.dir, 'cache.json');
|
|
45
|
+
Cache.specsCache = path.join(Cache.dir, 'specs');
|
|
46
|
+
|
|
47
|
+
this.fetcher = new Fetcher(uri);
|
|
48
|
+
|
|
49
|
+
this.uri = this.fetcher.uri;
|
|
50
|
+
this.uriHash = Cache.getCacheHash(this.uri);
|
|
51
|
+
|
|
52
|
+
// This should default to false so we have awareness if we've looked at the cache yet.
|
|
53
|
+
this.cached = false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static getCacheHash(file: string | OASDocument) {
|
|
57
|
+
let data: string;
|
|
58
|
+
if (typeof file === 'object') {
|
|
59
|
+
// Under certain unit testing circumstances, we might be supplying the class with a raw JSON
|
|
60
|
+
// object so we'll need to convert it to a string in order to hand it off to the crypto
|
|
61
|
+
// module.
|
|
62
|
+
data = JSON.stringify(file);
|
|
63
|
+
} else {
|
|
64
|
+
data = file;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return crypto.createHash('md5').update(data).digest('hex');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static setCacheDir(dir?: string | false) {
|
|
71
|
+
if (dir) {
|
|
72
|
+
Cache.dir = dir;
|
|
73
|
+
return;
|
|
74
|
+
} else if (Cache.dir) {
|
|
75
|
+
// If we already have a cache dir set and aren't explicitly it to something new then we
|
|
76
|
+
// shouldn't overwrite what we've already got.
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Cache.dir = findCacheDir({ name: PACKAGE_NAME });
|
|
81
|
+
if (typeof Cache.dir === 'undefined') {
|
|
82
|
+
// The `find-cache-dir` module returns `undefined` if the `node_modules/` directory isn't
|
|
83
|
+
// writable, or there's no `package.json` in the root-most directory. If this happens, we can
|
|
84
|
+
// instead adhoc create a cache directory in the users OS temp directory and store our data
|
|
85
|
+
// there.
|
|
86
|
+
//
|
|
87
|
+
// @link https://github.com/avajs/find-cache-dir/issues/29
|
|
88
|
+
Cache.dir = makeDir.sync(path.join(os.tmpdir(), PACKAGE_NAME));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static async reset() {
|
|
93
|
+
if (Cache.cacheStore) {
|
|
94
|
+
await fs.promises.rm(Cache.cacheStore).catch(() => {
|
|
95
|
+
// no-op
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (Cache.specsCache) {
|
|
100
|
+
await fs.promises.rm(Cache.specsCache, { recursive: true }).catch(() => {
|
|
101
|
+
// no-op
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static validate(json: any) {
|
|
107
|
+
if (json.swagger) {
|
|
108
|
+
throw new Error('Sorry, this module only supports OpenAPI definitions.');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// The `validate` method handles dereferencing for us.
|
|
112
|
+
return OpenAPIParser.validate(json, {
|
|
113
|
+
dereference: {
|
|
114
|
+
// If circular `$refs` are ignored they'll remain in the API definition as `$ref: String`.
|
|
115
|
+
// This allows us to not only do easy circular reference detection but also stringify and
|
|
116
|
+
// save dereferenced API definitions back into the cache directory.
|
|
117
|
+
circular: 'ignore',
|
|
118
|
+
},
|
|
119
|
+
}).catch(err => {
|
|
120
|
+
if (/is not a valid openapi definition/i.test(err.message)) {
|
|
121
|
+
throw new Error("Sorry, that doesn't look like a valid OpenAPI definition.");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
throw err;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
isCached() {
|
|
129
|
+
const cache = this.getCache();
|
|
130
|
+
return cache && this.uriHash in cache;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
getCache() {
|
|
134
|
+
if (typeof this.cached === 'object') {
|
|
135
|
+
return this.cached;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.cached = {};
|
|
139
|
+
|
|
140
|
+
if (fs.existsSync(Cache.cacheStore)) {
|
|
141
|
+
this.cached = JSON.parse(fs.readFileSync(Cache.cacheStore, 'utf8')) as CacheStore;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return this.cached;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
get() {
|
|
148
|
+
// If the class was supplied a raw object, just go ahead and bypass the caching system and
|
|
149
|
+
// return that.
|
|
150
|
+
if (typeof this.uri === 'object') {
|
|
151
|
+
return this.uri;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!this.isCached()) {
|
|
155
|
+
throw new Error(`${this.uri} has not been cached yet and must do so before being retrieved.`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const cache = this.getCache();
|
|
159
|
+
|
|
160
|
+
// Prior to v4.5.0 we were putting a fully resolved path to the API definition in the cache
|
|
161
|
+
// store but if you had specified a custom caching directory and would generate the cache on
|
|
162
|
+
// your system, that filepath would obviously not be the same in other environments. For this
|
|
163
|
+
// reason the `path` was removed from the cache store in favor of storing the `hash` instead.
|
|
164
|
+
//
|
|
165
|
+
// If we still have `path` in the config cache for backwards compatibility we should use it.
|
|
166
|
+
if ('path' in cache[this.uriHash]) {
|
|
167
|
+
return JSON.parse(fs.readFileSync(cache[this.uriHash].path, 'utf8'));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return JSON.parse(fs.readFileSync(path.join(Cache.specsCache, `${cache[this.uriHash].hash}.json`), 'utf8'));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async load() {
|
|
174
|
+
// If the class was supplied a raw object, just go ahead and bypass the caching system and
|
|
175
|
+
// return that.
|
|
176
|
+
if (typeof this.uri === 'object') {
|
|
177
|
+
return this.uri;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return this.fetcher.load().then(async spec => this.save(spec));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
save(spec: OASDocument) {
|
|
184
|
+
if (!fs.existsSync(Cache.dir)) {
|
|
185
|
+
fs.mkdirSync(Cache.dir, { recursive: true });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (!fs.existsSync(Cache.specsCache)) {
|
|
189
|
+
fs.mkdirSync(Cache.specsCache, { recursive: true });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const cache = this.getCache();
|
|
193
|
+
if (!(this.uriHash in cache)) {
|
|
194
|
+
const saved = JSON.stringify(spec, null, 2);
|
|
195
|
+
const fileHash = crypto.createHash('md5').update(saved).digest('hex');
|
|
196
|
+
|
|
197
|
+
cache[this.uriHash] = {
|
|
198
|
+
hash: fileHash,
|
|
199
|
+
original: this.uri,
|
|
200
|
+
title: 'title' in spec.info ? spec.info.title : undefined,
|
|
201
|
+
version: 'version' in spec.info ? spec.info.version : undefined,
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
fs.writeFileSync(path.join(Cache.specsCache, `${fileHash}.json`), saved);
|
|
205
|
+
fs.writeFileSync(Cache.cacheStore, JSON.stringify(cache, null, 2));
|
|
206
|
+
|
|
207
|
+
this.cached = cache;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return spec;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type Oas from 'oas';
|
|
2
|
+
import type CodeGeneratorLanguage from './language';
|
|
3
|
+
|
|
4
|
+
import TSGenerator from './languages/typescript';
|
|
5
|
+
|
|
6
|
+
export type SupportedLanguages = 'js' | 'js-cjs' | 'js-esm' | 'ts';
|
|
7
|
+
|
|
8
|
+
export default function codegen(
|
|
9
|
+
language: SupportedLanguages,
|
|
10
|
+
spec: Oas,
|
|
11
|
+
specPath: string,
|
|
12
|
+
identifier: string
|
|
13
|
+
): CodeGeneratorLanguage {
|
|
14
|
+
switch (language) {
|
|
15
|
+
case 'js':
|
|
16
|
+
throw new TypeError('An export format of CommonJS or ECMAScript is required for JavaScript compilation.');
|
|
17
|
+
|
|
18
|
+
case 'js-cjs':
|
|
19
|
+
case 'js-esm':
|
|
20
|
+
case 'ts':
|
|
21
|
+
return new TSGenerator(spec, specPath, identifier, {
|
|
22
|
+
outputJS: ['js-cjs', 'js-esm'].includes(language),
|
|
23
|
+
|
|
24
|
+
// TS will always generate with ESM-like exports.
|
|
25
|
+
compilerTarget: language === 'js-cjs' ? 'cjs' : 'esm',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
default:
|
|
29
|
+
throw new TypeError(`Unsupported language supplied: ${language}`);
|
|
30
|
+
}
|
|
31
|
+
}
|