api 7.0.0-beta.0 → 7.0.0-beta.2
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/codegen/codegenerator.d.ts +9 -11
- package/dist/codegen/codegenerator.d.ts.map +1 -1
- package/dist/codegen/codegenerator.js +11 -0
- package/dist/codegen/codegenerator.js.map +1 -1
- package/dist/codegen/factory.d.ts +19 -3
- package/dist/codegen/factory.d.ts.map +1 -1
- package/dist/codegen/factory.js +12 -5
- package/dist/codegen/factory.js.map +1 -1
- package/dist/codegen/languages/typescript/index.d.ts +8 -2
- package/dist/codegen/languages/typescript/index.d.ts.map +1 -1
- package/dist/codegen/languages/typescript/index.js +63 -17
- package/dist/codegen/languages/typescript/index.js.map +1 -1
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +4 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +5 -5
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/list.d.ts +4 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +37 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/uninstall.d.ts +4 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +72 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/lockfileSchema.d.ts +125 -0
- package/dist/lockfileSchema.d.ts.map +1 -0
- package/dist/lockfileSchema.js +78 -0
- package/dist/lockfileSchema.js.map +1 -0
- package/dist/packageInfo.d.ts +1 -1
- package/dist/packageInfo.js +1 -1
- package/dist/storage.d.ts +75 -60
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +85 -25
- package/dist/storage.js.map +1 -1
- package/package.json +14 -7
- package/schema.json +69 -0
- package/src/bin.ts +0 -21
- package/src/codegen/codegenerator.ts +0 -75
- package/src/codegen/factory.ts +0 -23
- package/src/codegen/languages/typescript/index.ts +0 -984
- package/src/codegen/languages/typescript/util.ts +0 -174
- package/src/commands/index.ts +0 -5
- package/src/commands/install.ts +0 -196
- package/src/fetcher.ts +0 -145
- package/src/lib/prompt.ts +0 -29
- package/src/logger.ts +0 -10
- package/src/packageInfo.ts +0 -3
- package/src/storage.ts +0 -333
- package/tsconfig.json +0 -10
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import camelCase from 'lodash.camelcase';
|
|
2
|
-
import deburr from 'lodash.deburr';
|
|
3
|
-
import startCase from 'lodash.startcase';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* This is a mix of reserved JS words and keywords in TypeScript that might be reserved or
|
|
7
|
-
* allowable but functionally confusing (like `let any = 'buster';`)
|
|
8
|
-
*
|
|
9
|
-
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar}
|
|
10
|
-
*/
|
|
11
|
-
const RESERVED_WORDS = [
|
|
12
|
-
'abstract',
|
|
13
|
-
'any',
|
|
14
|
-
'arguments',
|
|
15
|
-
'as',
|
|
16
|
-
'async',
|
|
17
|
-
'await',
|
|
18
|
-
'boolean',
|
|
19
|
-
'break',
|
|
20
|
-
'byte',
|
|
21
|
-
'case',
|
|
22
|
-
'catch',
|
|
23
|
-
'char',
|
|
24
|
-
'class',
|
|
25
|
-
'const',
|
|
26
|
-
'continue',
|
|
27
|
-
'constructor',
|
|
28
|
-
'debugger',
|
|
29
|
-
'default',
|
|
30
|
-
'delete',
|
|
31
|
-
'do',
|
|
32
|
-
'double',
|
|
33
|
-
'else',
|
|
34
|
-
'enum',
|
|
35
|
-
'eval',
|
|
36
|
-
'export',
|
|
37
|
-
'extends',
|
|
38
|
-
'false',
|
|
39
|
-
'final',
|
|
40
|
-
'finally',
|
|
41
|
-
'float',
|
|
42
|
-
'for',
|
|
43
|
-
'from',
|
|
44
|
-
'function',
|
|
45
|
-
'get',
|
|
46
|
-
'goto',
|
|
47
|
-
'if',
|
|
48
|
-
'implements',
|
|
49
|
-
'import',
|
|
50
|
-
'interface',
|
|
51
|
-
'in',
|
|
52
|
-
'instanceof',
|
|
53
|
-
'int',
|
|
54
|
-
'let',
|
|
55
|
-
'long',
|
|
56
|
-
'native',
|
|
57
|
-
'new',
|
|
58
|
-
'null',
|
|
59
|
-
'number',
|
|
60
|
-
'of',
|
|
61
|
-
'package',
|
|
62
|
-
'private',
|
|
63
|
-
'protected',
|
|
64
|
-
'public',
|
|
65
|
-
'module',
|
|
66
|
-
'namespace',
|
|
67
|
-
'return',
|
|
68
|
-
'set',
|
|
69
|
-
'short',
|
|
70
|
-
'static',
|
|
71
|
-
'string',
|
|
72
|
-
'super',
|
|
73
|
-
'switch',
|
|
74
|
-
'synchronized',
|
|
75
|
-
'this',
|
|
76
|
-
'throw',
|
|
77
|
-
'throws',
|
|
78
|
-
'transient',
|
|
79
|
-
'true',
|
|
80
|
-
'try',
|
|
81
|
-
'type',
|
|
82
|
-
'typeof',
|
|
83
|
-
'var',
|
|
84
|
-
'void',
|
|
85
|
-
'while',
|
|
86
|
-
'with',
|
|
87
|
-
'volatile',
|
|
88
|
-
'yield',
|
|
89
|
-
|
|
90
|
-
// These aren't reserved keywords but because we maybe codegen'ing an SDK to be used in the
|
|
91
|
-
// browser it'd be very bad if we overwrote these. This obviously doesn't account for browser APIs
|
|
92
|
-
// you can access outside of the `Window` API (like `alert()`), but we can add checks for those
|
|
93
|
-
// later if we need to.
|
|
94
|
-
'frames',
|
|
95
|
-
'global',
|
|
96
|
-
'globalThis',
|
|
97
|
-
'navigator',
|
|
98
|
-
'self',
|
|
99
|
-
'window',
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* @see {@link https://www.30secondsofcode.org/js/s/word-wrap}
|
|
104
|
-
*/
|
|
105
|
-
export function wordWrap(str: string, max = 88) {
|
|
106
|
-
return str.replace(new RegExp(`(?![^\\n]{1,${max}}$)([^\\n]{1,${max}})\\s`, 'g'), '$1\n');
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Safely escape some string characters that may break a docblock.
|
|
111
|
-
*
|
|
112
|
-
*/
|
|
113
|
-
export function docblockEscape(str: string) {
|
|
114
|
-
return str.replace(/\/\*/g, '/\\*').replace(/\*\//g, '*\\/');
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Convert a string that might contain spaces or special characters to one that can safely be used
|
|
119
|
-
* as a TypeScript interface or enum name.
|
|
120
|
-
*
|
|
121
|
-
* This function has been adapted and slighty modified from `json-schema-to-typescript`.
|
|
122
|
-
*
|
|
123
|
-
* @license MIT
|
|
124
|
-
* @see {@link https://github.com/bcherny/json-schema-to-typescript}
|
|
125
|
-
*/
|
|
126
|
-
function toSafeString(str: string) {
|
|
127
|
-
// identifiers in javaScript/ts:
|
|
128
|
-
// First character: a-zA-Z | _ | $
|
|
129
|
-
// Rest: a-zA-Z | _ | $ | 0-9
|
|
130
|
-
|
|
131
|
-
// remove accents, umlauts, ... by their basic latin letters
|
|
132
|
-
return (
|
|
133
|
-
deburr(str)
|
|
134
|
-
// if the string starts with a number, prefix it with character that typescript can accept
|
|
135
|
-
// https://github.com/bcherny/json-schema-to-typescript/issues/489
|
|
136
|
-
.replace(/^(\d){1}/, '$$1')
|
|
137
|
-
// replace chars which are not valid for typescript identifiers with whitespace
|
|
138
|
-
.replace(/(^\s*[^a-zA-Z_$])|([^a-zA-Z_$\d])/g, ' ')
|
|
139
|
-
// uppercase leading underscores followed by lowercase
|
|
140
|
-
.replace(/^_[a-z]/g, (match: string) => match.toUpperCase())
|
|
141
|
-
// remove non-leading underscores followed by lowercase (convert snake_case)
|
|
142
|
-
.replace(/_[a-z]/g, (match: string) => match.substr(1, match.length).toUpperCase())
|
|
143
|
-
// uppercase letters after digits, dollars
|
|
144
|
-
.replace(/([\d$]+[a-zA-Z])/g, (match: string) => match.toUpperCase())
|
|
145
|
-
// uppercase first letter after whitespace
|
|
146
|
-
.replace(/\s+([a-zA-Z])/g, (match: string) => match.toUpperCase().trim())
|
|
147
|
-
// remove remaining whitespace
|
|
148
|
-
.replace(/\s/g, '')
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export function generateTypeName(...parts: string[]) {
|
|
153
|
-
let str;
|
|
154
|
-
|
|
155
|
-
// If the end of our string ends with something like `2XX`, the combination of `startCase` and
|
|
156
|
-
// `camelCase` will transform it into `2Xx`.
|
|
157
|
-
if (parts.length > 1) {
|
|
158
|
-
const last = parts[parts.length - 1];
|
|
159
|
-
if (last.match(/^(\d)XX$/)) {
|
|
160
|
-
str = startCase(camelCase(parts.slice(0, -1).join(' ')));
|
|
161
|
-
str += ` ${last}`;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (!str) {
|
|
166
|
-
str = startCase(camelCase(parts.join(' ')));
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (RESERVED_WORDS.includes(str.toLowerCase())) {
|
|
170
|
-
str = `$${str}`;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return toSafeString(str);
|
|
174
|
-
}
|
package/src/commands/index.ts
DELETED
package/src/commands/install.ts
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import { Command, Option } from 'commander';
|
|
2
|
-
import figures from 'figures';
|
|
3
|
-
import Oas from 'oas';
|
|
4
|
-
import ora from 'ora';
|
|
5
|
-
import uslug from 'uslug';
|
|
6
|
-
|
|
7
|
-
import codegenFactory, { SupportedLanguages } from '../codegen/factory.js';
|
|
8
|
-
import Fetcher from '../fetcher.js';
|
|
9
|
-
import promptTerminal from '../lib/prompt.js';
|
|
10
|
-
import logger from '../logger.js';
|
|
11
|
-
import Storage from '../storage.js';
|
|
12
|
-
|
|
13
|
-
// @todo log logs to `.api/.logs` and have `.logs` ignored
|
|
14
|
-
const cmd = new Command();
|
|
15
|
-
cmd
|
|
16
|
-
.name('install')
|
|
17
|
-
.description('install an API SDK into your codebase')
|
|
18
|
-
.argument('<uri>', 'an API to install')
|
|
19
|
-
.option('-i, --identifier <identifier>', 'API identifier (eg. `@api/petstore`)')
|
|
20
|
-
.addOption(
|
|
21
|
-
new Option('-l, --lang <language>', 'SDK language').default(SupportedLanguages.JS).choices([SupportedLanguages.JS]),
|
|
22
|
-
)
|
|
23
|
-
.addOption(new Option('-y, --yes', 'Automatically answer "yes" to any prompts printed'))
|
|
24
|
-
.action(async (uri: string, options: { identifier?: string; lang: string; yes?: boolean }) => {
|
|
25
|
-
let language: SupportedLanguages;
|
|
26
|
-
if (options.lang) {
|
|
27
|
-
language = options.lang as SupportedLanguages;
|
|
28
|
-
} else {
|
|
29
|
-
({ value: language } = await promptTerminal({
|
|
30
|
-
type: 'select',
|
|
31
|
-
name: 'value',
|
|
32
|
-
message: 'What language would you like to generate an SDK for?',
|
|
33
|
-
choices: [{ title: 'JavaScript', value: SupportedLanguages.JS }],
|
|
34
|
-
initial: 1,
|
|
35
|
-
}));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// @todo let them know that we're going to be creating a `.api/ directory
|
|
39
|
-
// @todo detect if they have a gitigore and .npmignore and if .api woudl be ignored by that
|
|
40
|
-
// @todo don't support swagger files without upconverting them
|
|
41
|
-
|
|
42
|
-
if (Storage.isInLockFile({ source: uri })) {
|
|
43
|
-
// @todo
|
|
44
|
-
// logger(`It looks like you already have this API installed. Would you like to update it?`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
let spinner = ora('Fetching your API definition').start();
|
|
48
|
-
const storage = new Storage(uri);
|
|
49
|
-
|
|
50
|
-
const oas = await storage
|
|
51
|
-
.load(false)
|
|
52
|
-
.then(res => {
|
|
53
|
-
spinner.succeed(spinner.text);
|
|
54
|
-
return res;
|
|
55
|
-
})
|
|
56
|
-
.then(Oas.init)
|
|
57
|
-
.then(async spec => {
|
|
58
|
-
await spec.dereference({ preserveRefAsJSONSchemaTitle: true });
|
|
59
|
-
return spec;
|
|
60
|
-
})
|
|
61
|
-
.catch(err => {
|
|
62
|
-
// @todo cleanup installed files
|
|
63
|
-
spinner.fail(spinner.text);
|
|
64
|
-
logger(err.message, true);
|
|
65
|
-
process.exit(1);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
let identifier;
|
|
69
|
-
if (options.identifier) {
|
|
70
|
-
// `Storage.isIdentifierValid` will throw an exception if an identifier is invalid.
|
|
71
|
-
if (Storage.isIdentifierValid(options.identifier)) {
|
|
72
|
-
identifier = options.identifier;
|
|
73
|
-
}
|
|
74
|
-
} else if (Fetcher.isAPIRegistryUUID(uri)) {
|
|
75
|
-
identifier = Fetcher.getProjectPrefixFromRegistryUUID(uri);
|
|
76
|
-
} else {
|
|
77
|
-
({ value: identifier } = await promptTerminal({
|
|
78
|
-
type: 'text',
|
|
79
|
-
name: 'value',
|
|
80
|
-
initial: oas.api?.info?.title ? uslug(oas.api.info.title, { lower: true }) : undefined,
|
|
81
|
-
message:
|
|
82
|
-
'What would you like to identify this API as? This will be how you use the SDK. (e.g. entering `petstore` would result in `@api/petstore`)',
|
|
83
|
-
validate: value => {
|
|
84
|
-
if (!value) {
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
return Storage.isIdentifierValid(value, true);
|
|
90
|
-
} catch (err) {
|
|
91
|
-
return err.message;
|
|
92
|
-
}
|
|
93
|
-
},
|
|
94
|
-
}));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (!identifier) {
|
|
98
|
-
logger('You must tell us what you would like to identify this API as in order to install it.', true);
|
|
99
|
-
process.exit(1);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Now that we've got an identifier we can save their spec and generate the directory structure
|
|
103
|
-
// for their SDK.
|
|
104
|
-
storage.setIdentifier(identifier);
|
|
105
|
-
await storage.save(oas.api);
|
|
106
|
-
|
|
107
|
-
// @todo look for a prettier config and if we find one ask them if we should use it
|
|
108
|
-
spinner = ora('Generating your SDK').start();
|
|
109
|
-
const generator = codegenFactory(language, oas, '../openapi.json', identifier);
|
|
110
|
-
const sdkSource = await generator
|
|
111
|
-
.generate()
|
|
112
|
-
.then(res => {
|
|
113
|
-
spinner.succeed(spinner.text);
|
|
114
|
-
return res;
|
|
115
|
-
})
|
|
116
|
-
.catch(err => {
|
|
117
|
-
// @todo cleanup installed files
|
|
118
|
-
spinner.fail(spinner.text);
|
|
119
|
-
logger(err.message, true);
|
|
120
|
-
process.exit(1);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
spinner = ora('Saving your SDK into your codebase').start();
|
|
124
|
-
await storage
|
|
125
|
-
.saveSourceFiles(sdkSource)
|
|
126
|
-
.then(() => {
|
|
127
|
-
spinner.succeed(spinner.text);
|
|
128
|
-
})
|
|
129
|
-
.catch(err => {
|
|
130
|
-
// @todo cleanup installed files
|
|
131
|
-
spinner.fail(spinner.text);
|
|
132
|
-
logger(err.message, true);
|
|
133
|
-
process.exit(1);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
if (generator.hasRequiredPackages()) {
|
|
137
|
-
logger(`${figures.warning} This generator requires some packages to be installed alongside it:`);
|
|
138
|
-
Object.entries(generator.requiredPackages).forEach(([pkg, pkgInfo]) => {
|
|
139
|
-
let msg = ` ${figures.pointerSmall} ${pkg}: ${pkgInfo.reason}`;
|
|
140
|
-
if (pkgInfo.url) {
|
|
141
|
-
msg += ` ${pkgInfo.url}`;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
logger(msg);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
if (!options.yes) {
|
|
148
|
-
await promptTerminal({
|
|
149
|
-
type: 'confirm',
|
|
150
|
-
name: 'value',
|
|
151
|
-
message: 'OK to proceed with package installation?',
|
|
152
|
-
initial: true,
|
|
153
|
-
}).then(({ value }) => {
|
|
154
|
-
if (!value) {
|
|
155
|
-
// @todo cleanup installed files
|
|
156
|
-
logger('Installation cancelled.', true);
|
|
157
|
-
process.exit(1);
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
spinner = ora('Installing required packages').start();
|
|
163
|
-
try {
|
|
164
|
-
await generator.install(storage);
|
|
165
|
-
spinner.succeed(spinner.text);
|
|
166
|
-
} catch (err) {
|
|
167
|
-
// @todo cleanup installed files
|
|
168
|
-
spinner.fail(spinner.text);
|
|
169
|
-
logger(err.message, true);
|
|
170
|
-
process.exit(1);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
spinner = ora('Compiling your SDK').start();
|
|
175
|
-
try {
|
|
176
|
-
await generator.compile(storage);
|
|
177
|
-
spinner.succeed(spinner.text);
|
|
178
|
-
} catch (err) {
|
|
179
|
-
// @todo cleanup installed files
|
|
180
|
-
spinner.fail(spinner.text);
|
|
181
|
-
logger(err.message, true);
|
|
182
|
-
process.exit(1);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
logger('🚀 All done!');
|
|
186
|
-
})
|
|
187
|
-
.addHelpText(
|
|
188
|
-
'after',
|
|
189
|
-
`
|
|
190
|
-
Examples:
|
|
191
|
-
$ api install @developers/v2.0#nysezql0wwo236
|
|
192
|
-
$ api install https://raw.githubusercontent.com/readmeio/oas-examples/main/3.0/json/petstore-simple.json
|
|
193
|
-
$ api install ./petstore.json`,
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
export default cmd;
|
package/src/fetcher.ts
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import type { OASDocument } from 'oas/rmoas.types';
|
|
2
|
-
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
|
|
6
|
-
import OpenAPIParser from '@readme/openapi-parser';
|
|
7
|
-
import yaml from 'js-yaml';
|
|
8
|
-
|
|
9
|
-
export default class Fetcher {
|
|
10
|
-
uri: string | OASDocument;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @note This regex also exists in `httpsnippet-client-api`.
|
|
14
|
-
*
|
|
15
|
-
* @example @petstore/v1.0#n6kvf10vakpemvplx
|
|
16
|
-
* @example @petstore#n6kvf10vakpemvplx
|
|
17
|
-
*/
|
|
18
|
-
static registryUUIDRegex = /^@(?<project>[a-zA-Z0-9-_]+)(\/?(?<version>.+))?#(?<uuid>[a-z0-9]+)$/;
|
|
19
|
-
|
|
20
|
-
constructor(uri: string | OASDocument) {
|
|
21
|
-
if (typeof uri === 'string') {
|
|
22
|
-
if (Fetcher.isAPIRegistryUUID(uri)) {
|
|
23
|
-
// Resolve OpenAPI definition shorthand accessors from within the ReadMe API Registry.
|
|
24
|
-
this.uri = uri.replace(Fetcher.registryUUIDRegex, 'https://dash.readme.com/api/v1/api-registry/$4');
|
|
25
|
-
} else if (Fetcher.isGitHubBlobURL(uri)) {
|
|
26
|
-
/**
|
|
27
|
-
* People may try to use a public repository URL to the source viewer on GitHub not knowing
|
|
28
|
-
* that this page actually serves HTML. In this case we want to rewrite these to the "raw"
|
|
29
|
-
* version of this page that'll allow us to access the API definition.
|
|
30
|
-
*
|
|
31
|
-
* @example https://github.com/readmeio/oas-examples/blob/main/3.1/json/petstore.json
|
|
32
|
-
*/
|
|
33
|
-
this.uri = uri.replace(/\/\/github.com/, '//raw.githubusercontent.com').replace(/\/blob\//, '/');
|
|
34
|
-
} else {
|
|
35
|
-
this.uri = uri;
|
|
36
|
-
}
|
|
37
|
-
} else {
|
|
38
|
-
this.uri = uri;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
static isAPIRegistryUUID(uri: string) {
|
|
43
|
-
return Fetcher.registryUUIDRegex.test(uri);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
static isGitHubBlobURL(uri: string) {
|
|
47
|
-
return /\/\/github.com\/[-_a-zA-Z0-9]+\/[-_a-zA-Z0-9]+\/blob\/(.*).(yaml|json|yml)/.test(uri);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* @note This function also exists in `httpsnippet-client-api`.
|
|
52
|
-
*/
|
|
53
|
-
static getProjectPrefixFromRegistryUUID(uri: string) {
|
|
54
|
-
const matches = uri.match(Fetcher.registryUUIDRegex);
|
|
55
|
-
if (!matches) {
|
|
56
|
-
return undefined;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return matches.groups?.project;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async load() {
|
|
63
|
-
if (typeof this.uri !== 'string') {
|
|
64
|
-
throw new TypeError(
|
|
65
|
-
"Something disastrous occurred and a non-string URI was supplied to the Fetcher library. This shouldn't have happened!",
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return Promise.resolve(this.uri)
|
|
70
|
-
.then(uri => {
|
|
71
|
-
let url;
|
|
72
|
-
try {
|
|
73
|
-
url = new URL(uri);
|
|
74
|
-
} catch (err) {
|
|
75
|
-
// If that try fails for whatever reason than the URI that we have isn't a real URL and
|
|
76
|
-
// we can safely attempt to look for it on the filesystem.
|
|
77
|
-
return Fetcher.getFile(uri);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return Fetcher.getURL(url.href);
|
|
81
|
-
})
|
|
82
|
-
.then(res => Fetcher.validate(res))
|
|
83
|
-
.then(res => res as OASDocument);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
static getURL(url: string) {
|
|
87
|
-
// @todo maybe include our user-agent here to identify our request
|
|
88
|
-
return fetch(url).then(res => {
|
|
89
|
-
if (!res.ok) {
|
|
90
|
-
throw new Error(`Unable to retrieve URL (${url}). Reason: ${res.statusText}`);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (res.headers.get('content-type') === 'application/yaml' || /\.(yaml|yml)/.test(url)) {
|
|
94
|
-
return res.text().then(text => {
|
|
95
|
-
return yaml.load(text);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return res.json();
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
static getFile(uri: string) {
|
|
104
|
-
// Support relative paths by resolving them against the cwd.
|
|
105
|
-
const file = path.resolve(process.cwd(), uri);
|
|
106
|
-
|
|
107
|
-
if (!fs.existsSync(file)) {
|
|
108
|
-
throw new Error(
|
|
109
|
-
`Sorry, we were unable to load an API definition from ${file}. Please either supply a URL or a path on your filesystem.`,
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return Promise.resolve(fs.readFileSync(file, 'utf8')).then((res: string) => {
|
|
114
|
-
if (/\.(yaml|yml)/.test(file)) {
|
|
115
|
-
return yaml.load(res);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return JSON.parse(res);
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
static validate(json: OASDocument) {
|
|
123
|
-
if (json.swagger) {
|
|
124
|
-
throw new Error('Sorry, this module only supports OpenAPI definitions.');
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// The `validate` method handles dereferencing for us.
|
|
128
|
-
return OpenAPIParser.validate(json, {
|
|
129
|
-
dereference: {
|
|
130
|
-
/**
|
|
131
|
-
* If circular `$refs` are ignored they'll remain in the API definition as `$ref: String`.
|
|
132
|
-
* This allows us to not only do easy circular reference detection but also stringify and
|
|
133
|
-
* save dereferenced API definitions back into the cache directory.
|
|
134
|
-
*/
|
|
135
|
-
circular: 'ignore',
|
|
136
|
-
},
|
|
137
|
-
}).catch(err => {
|
|
138
|
-
if (/is not a valid openapi definition/i.test(err.message)) {
|
|
139
|
-
throw new Error("Sorry, that doesn't look like a valid OpenAPI definition.");
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
throw err;
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
}
|
package/src/lib/prompt.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import prompts from 'prompts';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* The `prompts` library doesn't always interpret CTRL+C and release the terminal back to the user
|
|
5
|
-
* so we need handle this ourselves. This function is just a simple overload of the main `prompts`
|
|
6
|
-
* import that we use.
|
|
7
|
-
*
|
|
8
|
-
* @see {@link https://github.com/terkelg/prompts/issues/252}
|
|
9
|
-
*/
|
|
10
|
-
export default async function promptTerminal<T extends string = string>(
|
|
11
|
-
question: prompts.PromptObject<T>,
|
|
12
|
-
options?: prompts.Options,
|
|
13
|
-
) {
|
|
14
|
-
const enableTerminalCursor = () => {
|
|
15
|
-
process.stdout.write('\x1B[?25h');
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const onState = (state: { aborted: boolean }) => {
|
|
19
|
-
if (state.aborted) {
|
|
20
|
-
// If we don't re-enable the terminal cursor before exiting the program, the cursor will
|
|
21
|
-
// remain hidden.
|
|
22
|
-
enableTerminalCursor();
|
|
23
|
-
process.stdout.write('\n');
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
return prompts({ ...question, onState }, options);
|
|
29
|
-
}
|
package/src/logger.ts
DELETED
package/src/packageInfo.ts
DELETED