api 7.0.0-alpha.0 → 7.0.0-alpha.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.
@@ -0,0 +1,4 @@
1
+ declare const _default: {
2
+ install: import("commander").Command;
3
+ };
4
+ export default _default;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const install_1 = __importDefault(require("./install"));
7
+ exports.default = {
8
+ install: install_1.default,
9
+ };
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ declare const cmd: Command;
3
+ export default cmd;
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const commander_1 = require("commander");
7
+ const figures_1 = __importDefault(require("figures"));
8
+ const oas_1 = __importDefault(require("oas"));
9
+ const ora_1 = __importDefault(require("ora"));
10
+ const codegen_1 = __importDefault(require("../codegen"));
11
+ const fetcher_1 = __importDefault(require("../fetcher"));
12
+ const prompt_1 = __importDefault(require("../lib/prompt"));
13
+ const logger_1 = __importDefault(require("../logger"));
14
+ const storage_1 = __importDefault(require("../storage"));
15
+ // @todo log logs to `.api/.logs` and have `.logs` ignored
16
+ const cmd = new commander_1.Command();
17
+ cmd
18
+ .name('install')
19
+ .description('install an API SDK into your codebase')
20
+ .argument('<uri>', 'an API to install')
21
+ .option('-i, --identifier <identifier>', 'API identifier (eg. `@api/petstore`)')
22
+ .addOption(new commander_1.Option('-l, --lang <language>', 'SDK language').choices([
23
+ 'js',
24
+ 'js-cjs',
25
+ 'js-esm',
26
+ 'ts',
27
+ ]))
28
+ .addOption(new commander_1.Option('-y, --yes', 'Automatically answer "yes" to any prompts printed'))
29
+ .action(async (uri, options) => {
30
+ let language;
31
+ if (options.lang) {
32
+ language = options.lang;
33
+ }
34
+ else {
35
+ ({ value: language } = await (0, prompt_1.default)({
36
+ type: 'select',
37
+ name: 'value',
38
+ message: 'What language would you like to generate an SDK for?',
39
+ choices: [
40
+ { title: 'TypeScript', value: 'ts' },
41
+ { title: 'JavaScript', value: 'js' },
42
+ ],
43
+ initial: 1,
44
+ }));
45
+ }
46
+ // Because our TS generation outputs raw TS source files we don't need to worry about CJS/ESM.
47
+ if (language === 'js') {
48
+ ({ value: language } = await (0, prompt_1.default)({
49
+ type: 'select',
50
+ name: 'value',
51
+ message: 'How are your project imports and exports structured?',
52
+ choices: [
53
+ { title: 'CommonJS', description: 'require/exports', value: 'cjs' },
54
+ { title: 'ECMAScript Modules', description: 'import/export', value: 'esm' },
55
+ ],
56
+ initial: 0,
57
+ format: sel => (sel === 'cjs' ? 'js-cjs' : 'js-esm'),
58
+ }));
59
+ }
60
+ // @todo let them know that we're going to be creating a `.api/ directory
61
+ // @todo detect if they have a gitigore and .npmignore and if .api woudl be ignored by that
62
+ // @todo don't support swagger files without upconverting them
63
+ if (storage_1.default.isInLockFile({ source: uri })) {
64
+ // @todo
65
+ // logger(`It looks like you already have this API installed. Would you like to update it?`);
66
+ }
67
+ let identifier;
68
+ if (options.identifier) {
69
+ // `Storage.isIdentifierValid` will throw an exception if an identifier is invalid.
70
+ if (storage_1.default.isIdentifierValid(options.identifier)) {
71
+ identifier = options.identifier;
72
+ }
73
+ }
74
+ else if (fetcher_1.default.isAPIRegistryUUID(uri)) {
75
+ identifier = fetcher_1.default.getProjectPrefixFromRegistryUUID(uri);
76
+ }
77
+ else {
78
+ ({ value: identifier } = await (0, prompt_1.default)({
79
+ type: 'text',
80
+ name: 'value',
81
+ message: 'What would you like to identify this API as? This will be how you import the SDK. (e.g. entering `petstore` would result in `@api/petstore`)',
82
+ validate: value => {
83
+ if (!value) {
84
+ return false;
85
+ }
86
+ try {
87
+ return storage_1.default.isIdentifierValid(value, true);
88
+ }
89
+ catch (err) {
90
+ return err.message;
91
+ }
92
+ },
93
+ }));
94
+ }
95
+ if (!identifier) {
96
+ (0, logger_1.default)('You must tell us what you would like to identify this API as in order to install it.', true);
97
+ process.exit(1);
98
+ }
99
+ let spinner = (0, ora_1.default)('Fetching your API').start();
100
+ const storage = new storage_1.default(uri, identifier);
101
+ const oas = await storage
102
+ .load()
103
+ .then(res => {
104
+ spinner.succeed(spinner.text);
105
+ return res;
106
+ })
107
+ .then(oas_1.default.init)
108
+ .catch(err => {
109
+ // @todo cleanup installed files
110
+ spinner.fail(spinner.text);
111
+ (0, logger_1.default)(err.message, true);
112
+ process.exit(1);
113
+ });
114
+ // @todo look for a prettier config and if we find one ask them if we should use it
115
+ spinner = (0, ora_1.default)('Generating your SDK').start();
116
+ const generator = (0, codegen_1.default)(language, oas, './openapi.json', identifier);
117
+ const sdkSource = await generator
118
+ .generator()
119
+ .then(res => {
120
+ spinner.succeed(spinner.text);
121
+ return res;
122
+ })
123
+ .catch(err => {
124
+ // @todo cleanup installed files
125
+ spinner.fail(spinner.text);
126
+ (0, logger_1.default)(err.message, true);
127
+ process.exit(1);
128
+ });
129
+ spinner = (0, ora_1.default)('Saving your SDK into your codebase').start();
130
+ await storage
131
+ .saveSourceFiles(sdkSource)
132
+ .then(() => {
133
+ spinner.succeed(spinner.text);
134
+ })
135
+ .catch(err => {
136
+ // @todo cleanup installed files
137
+ spinner.fail(spinner.text);
138
+ (0, logger_1.default)(err.message, true);
139
+ process.exit(1);
140
+ });
141
+ if (generator.hasRequiredPackages()) {
142
+ (0, logger_1.default)(`${figures_1.default.warning} This generator requires some packages to be installed alongside it:`);
143
+ Object.entries(generator.requiredPackages).forEach(([pkg, pkgInfo]) => {
144
+ (0, logger_1.default)(` ${figures_1.default.pointerSmall} ${pkg}: ${pkgInfo.reason} ${pkgInfo.url}`);
145
+ });
146
+ if (!options.yes) {
147
+ await (0, prompt_1.default)({
148
+ type: 'confirm',
149
+ name: 'value',
150
+ message: 'OK to proceed with package installation?',
151
+ initial: true,
152
+ }).then(({ value }) => {
153
+ if (!value) {
154
+ // @todo cleanup installed files
155
+ (0, logger_1.default)('Installation cancelled.', true);
156
+ process.exit(1);
157
+ }
158
+ });
159
+ }
160
+ spinner = (0, ora_1.default)('Installing required packages').start();
161
+ try {
162
+ await generator.installer(storage);
163
+ spinner.succeed(spinner.text);
164
+ }
165
+ catch (err) {
166
+ // @todo cleanup installed files
167
+ spinner.fail(spinner.text);
168
+ (0, logger_1.default)(err.message, true);
169
+ process.exit(1);
170
+ }
171
+ }
172
+ (0, logger_1.default)('🚀 All done!');
173
+ })
174
+ .addHelpText('after', `
175
+ Examples:
176
+ $ api install @developers/v2.0#nysezql0wwo236
177
+ $ api install https://raw.githubusercontent.com/readmeio/oas-examples/main/3.0/json/petstore-simple.json
178
+ $ api install ./petstore.json`);
179
+ exports.default = cmd;
@@ -0,0 +1,53 @@
1
+ import type { OASDocument } from 'oas/rmoas.types';
2
+ export default class Fetcher {
3
+ uri: string | OASDocument;
4
+ /**
5
+ * @example @petstore/v1.0#n6kvf10vakpemvplx
6
+ * @example @petstore#n6kvf10vakpemvplx
7
+ */
8
+ static registryUUIDRegex: RegExp;
9
+ constructor(uri: string | OASDocument);
10
+ static isAPIRegistryUUID(uri: string): boolean;
11
+ static isGitHubBlobURL(uri: string): boolean;
12
+ static getProjectPrefixFromRegistryUUID(uri: string): string | undefined;
13
+ load(): Promise<(import("openapi-types").OpenAPIV3.Document<{}> & Record<string, unknown>) | (Omit<Omit<import("openapi-types").OpenAPIV3.Document<{}>, "paths" | "components">, "paths" | "components" | "info" | "servers" | "webhooks" | "jsonSchemaDialect"> & {
14
+ info: import("openapi-types").OpenAPIV3_1.InfoObject;
15
+ jsonSchemaDialect?: string | undefined;
16
+ servers?: import("openapi-types").OpenAPIV3_1.ServerObject[] | undefined;
17
+ } & Pick<{
18
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
19
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
20
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
21
+ }, "paths"> & Omit<Partial<{
22
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
23
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
24
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
25
+ }>, "paths"> & Record<string, unknown>) | (Omit<Omit<import("openapi-types").OpenAPIV3.Document<{}>, "paths" | "components">, "paths" | "components" | "info" | "servers" | "webhooks" | "jsonSchemaDialect"> & {
26
+ info: import("openapi-types").OpenAPIV3_1.InfoObject;
27
+ jsonSchemaDialect?: string | undefined;
28
+ servers?: import("openapi-types").OpenAPIV3_1.ServerObject[] | undefined;
29
+ } & Pick<{
30
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
31
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
32
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
33
+ }, "webhooks"> & Omit<Partial<{
34
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
35
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
36
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
37
+ }>, "webhooks"> & Record<string, unknown>) | (Omit<Omit<import("openapi-types").OpenAPIV3.Document<{}>, "paths" | "components">, "paths" | "components" | "info" | "servers" | "webhooks" | "jsonSchemaDialect"> & {
38
+ info: import("openapi-types").OpenAPIV3_1.InfoObject;
39
+ jsonSchemaDialect?: string | undefined;
40
+ servers?: import("openapi-types").OpenAPIV3_1.ServerObject[] | undefined;
41
+ } & Pick<{
42
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
43
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
44
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
45
+ }, "components"> & Omit<Partial<{
46
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
47
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
48
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
49
+ }>, "components"> & Record<string, unknown>)>;
50
+ static getURL(url: string): Promise<any>;
51
+ static getFile(uri: string): Promise<any>;
52
+ static validate(json: OASDocument): Promise<import("openapi-types").OpenAPI.Document<{}>>;
53
+ }
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_fs_1 = __importDefault(require("node:fs"));
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const openapi_parser_1 = __importDefault(require("@readme/openapi-parser"));
9
+ const js_yaml_1 = __importDefault(require("js-yaml"));
10
+ class Fetcher {
11
+ uri;
12
+ /**
13
+ * @example @petstore/v1.0#n6kvf10vakpemvplx
14
+ * @example @petstore#n6kvf10vakpemvplx
15
+ */
16
+ static registryUUIDRegex = /^@(?<project>[a-zA-Z0-9-_]+)(\/?(?<version>.+))?#(?<uuid>[a-z0-9]+)$/;
17
+ constructor(uri) {
18
+ if (typeof uri === 'string') {
19
+ if (Fetcher.isAPIRegistryUUID(uri)) {
20
+ // Resolve OpenAPI definition shorthand accessors from within the ReadMe API Registry.
21
+ this.uri = uri.replace(Fetcher.registryUUIDRegex, 'https://dash.readme.com/api/v1/api-registry/$4');
22
+ }
23
+ else if (Fetcher.isGitHubBlobURL(uri)) {
24
+ /**
25
+ * People may try to use a public repository URL to the source viewer on GitHub not knowing
26
+ * that this page actually serves HTML. In this case we want to rewrite these to the "raw"
27
+ * version of this page that'll allow us to access the API definition.
28
+ *
29
+ * @example https://github.com/readmeio/oas-examples/blob/main/3.1/json/petstore.json
30
+ */
31
+ this.uri = uri.replace(/\/\/github.com/, '//raw.githubusercontent.com').replace(/\/blob\//, '/');
32
+ }
33
+ else {
34
+ this.uri = uri;
35
+ }
36
+ }
37
+ else {
38
+ this.uri = uri;
39
+ }
40
+ }
41
+ static isAPIRegistryUUID(uri) {
42
+ return Fetcher.registryUUIDRegex.test(uri);
43
+ }
44
+ static isGitHubBlobURL(uri) {
45
+ return /\/\/github.com\/[-_a-zA-Z0-9]+\/[-_a-zA-Z0-9]+\/blob\/(.*).(yaml|json|yml)/.test(uri);
46
+ }
47
+ static getProjectPrefixFromRegistryUUID(uri) {
48
+ const matches = uri.match(Fetcher.registryUUIDRegex);
49
+ if (!matches) {
50
+ return undefined;
51
+ }
52
+ return matches.groups?.project;
53
+ }
54
+ async load() {
55
+ if (typeof this.uri !== 'string') {
56
+ throw new TypeError("Something disastrous occurred and a non-string URI was supplied to the Fetcher library. This shouldn't have happened!");
57
+ }
58
+ return Promise.resolve(this.uri)
59
+ .then(uri => {
60
+ let url;
61
+ try {
62
+ url = new URL(uri);
63
+ }
64
+ catch (err) {
65
+ // If that try fails for whatever reason than the URI that we have isn't a real URL and
66
+ // we can safely attempt to look for it on the filesystem.
67
+ return Fetcher.getFile(uri);
68
+ }
69
+ return Fetcher.getURL(url.href);
70
+ })
71
+ .then(res => Fetcher.validate(res))
72
+ .then(res => res);
73
+ }
74
+ static getURL(url) {
75
+ // @todo maybe include our user-agent here to identify our request
76
+ return fetch(url).then(res => {
77
+ if (!res.ok) {
78
+ throw new Error(`Unable to retrieve URL (${url}). Reason: ${res.statusText}`);
79
+ }
80
+ if (res.headers.get('content-type') === 'application/yaml' || /\.(yaml|yml)/.test(url)) {
81
+ return res.text().then(text => {
82
+ return js_yaml_1.default.load(text);
83
+ });
84
+ }
85
+ return res.json();
86
+ });
87
+ }
88
+ static getFile(uri) {
89
+ // Support relative paths by resolving them against the cwd.
90
+ const file = node_path_1.default.resolve(process.cwd(), uri);
91
+ if (!node_fs_1.default.existsSync(file)) {
92
+ throw new Error(`Sorry, we were unable to load an API definition from ${file}. Please either supply a URL or a path on your filesystem.`);
93
+ }
94
+ return Promise.resolve(node_fs_1.default.readFileSync(file, 'utf8')).then((res) => {
95
+ if (/\.(yaml|yml)/.test(file)) {
96
+ return js_yaml_1.default.load(res);
97
+ }
98
+ return JSON.parse(res);
99
+ });
100
+ }
101
+ static validate(json) {
102
+ if (json.swagger) {
103
+ throw new Error('Sorry, this module only supports OpenAPI definitions.');
104
+ }
105
+ // The `validate` method handles dereferencing for us.
106
+ return openapi_parser_1.default.validate(json, {
107
+ dereference: {
108
+ /**
109
+ * If circular `$refs` are ignored they'll remain in the API definition as `$ref: String`.
110
+ * This allows us to not only do easy circular reference detection but also stringify and
111
+ * save dereferenced API definitions back into the cache directory.
112
+ */
113
+ circular: 'ignore',
114
+ },
115
+ }).catch(err => {
116
+ if (/is not a valid openapi definition/i.test(err.message)) {
117
+ throw new Error("Sorry, that doesn't look like a valid OpenAPI definition.");
118
+ }
119
+ throw err;
120
+ });
121
+ }
122
+ }
123
+ exports.default = Fetcher;
@@ -0,0 +1,9 @@
1
+ import prompts from 'prompts';
2
+ /**
3
+ * The `prompts` library doesn't always interpret CTRL+C and release the terminal back to the user
4
+ * so we need handle this ourselves. This function is just a simple overload of the main `prompts`
5
+ * import that we use.
6
+ *
7
+ * @see {@link https://github.com/terkelg/prompts/issues/252}
8
+ */
9
+ export default function promptTerminal<T extends string = string>(question: prompts.PromptObject<T>, options?: prompts.Options): Promise<prompts.Answers<T>>;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const prompts_1 = __importDefault(require("prompts"));
7
+ /**
8
+ * The `prompts` library doesn't always interpret CTRL+C and release the terminal back to the user
9
+ * so we need handle this ourselves. This function is just a simple overload of the main `prompts`
10
+ * import that we use.
11
+ *
12
+ * @see {@link https://github.com/terkelg/prompts/issues/252}
13
+ */
14
+ async function promptTerminal(question, options) {
15
+ const enableTerminalCursor = () => {
16
+ process.stdout.write('\x1B[?25h');
17
+ };
18
+ const onState = (state) => {
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
+ return (0, prompts_1.default)({ ...question, onState }, options);
28
+ }
29
+ exports.default = promptTerminal;
@@ -0,0 +1 @@
1
+ export default function logger(log: string, error?: boolean): void;
package/dist/logger.js ADDED
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ /* eslint-disable no-console */
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ function logger(log, error) {
9
+ if (error) {
10
+ console.error(chalk_1.default.red(log));
11
+ }
12
+ else {
13
+ console.log(log);
14
+ }
15
+ }
16
+ exports.default = logger;
@@ -0,0 +1,2 @@
1
+ export declare const PACKAGE_NAME = "api";
2
+ export declare const PACKAGE_VERSION = "7.0.0-alpha.2";
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PACKAGE_VERSION = exports.PACKAGE_NAME = void 0;
4
+ // This file is automatically updated by the build script.
5
+ exports.PACKAGE_NAME = 'api';
6
+ exports.PACKAGE_VERSION = '7.0.0-alpha.2';
@@ -0,0 +1,106 @@
1
+ import type { OASDocument } from 'oas/rmoas.types';
2
+ import Fetcher from './fetcher';
3
+ export default class Storage {
4
+ static dir: string;
5
+ static lockfile: false | Lockfile;
6
+ /**
7
+ * This is the original source that the file came from (relative/absolute file path, URL, ReadMe
8
+ * registry UUID, etc.).
9
+ */
10
+ source: string;
11
+ identifier: string;
12
+ fetcher: Fetcher;
13
+ constructor(source: string, identifier?: string);
14
+ static getLockfilePath(): string;
15
+ static getAPIsDir(): string;
16
+ static setStorageDir(dir?: string): void;
17
+ /**
18
+ * Reset the state of the entire storage system.
19
+ *
20
+ * This will completely destroy the contents of the `.api/` directory!
21
+ */
22
+ static reset(): Promise<void>;
23
+ static getDefaultLockfile(): Lockfile;
24
+ static generateIntegrityHash(definition: OASDocument): string;
25
+ static getLockfile(): Lockfile;
26
+ static isIdentifierValid(identifier: string, prefixWithAPINamespace?: boolean): boolean;
27
+ static isInLockFile(search: {
28
+ identifier?: string;
29
+ source?: string;
30
+ }): false | LockfileAPI;
31
+ setIdentifier(identifier: string): void;
32
+ /**
33
+ * Determine if the current spec + identifier we're working with is already in the lockfile.
34
+ */
35
+ isInLockfile(): boolean;
36
+ /**
37
+ * Retrieve the lockfile record for the current spec + identifier if it exists in the lockfile.
38
+ */
39
+ getFromLockfile(): LockfileAPI | undefined;
40
+ getIdentifierStorageDir(): string;
41
+ getAPIDefinition(): any;
42
+ saveSourceFiles(files: Record<string, string>): Promise<unknown>;
43
+ load(): Promise<OASDocument>;
44
+ /**
45
+ * @example <caption>Storage directory structure</caption>
46
+ * .api/
47
+ * ├── api.json // The `package-lock.json` equivalent that records everything that's
48
+ * | // installed, when it was installed, what the original source was,
49
+ * | // and what version of `api` was used.
50
+ * └── apis/
51
+ * ├── readme/
52
+ * | ├── node_modules/
53
+ * │ ├── index.js // We may offer the option to export a raw TS file for folks who want
54
+ * | | // that, but for now it'll be a compiled JS file.
55
+ * │ ├── index.d.ts // All types for their SDK, ready to use in an IDE.
56
+ * │ |── openapi.json
57
+ * │ └── package.json
58
+ * └── petstore/
59
+ * ├── node_modules/
60
+ * ├── index.js
61
+ * ├── index.d.ts
62
+ * ├── openapi.json
63
+ * └── package.json
64
+ *
65
+ */
66
+ save(spec: OASDocument): OASDocument;
67
+ }
68
+ interface Lockfile {
69
+ apis: LockfileAPI[];
70
+ /**
71
+ * The `api.json` schema version. This will only ever change if we introduce breaking changes to
72
+ * this store.
73
+ */
74
+ version: '1.0';
75
+ }
76
+ interface LockfileAPI {
77
+ /**
78
+ * A unique identifier of the API. This'll be used to do requires on `@api/<identifier>` and also
79
+ * where the SDK code will be located in `.api/apis/<identifier>`.
80
+ *
81
+ * @example petstore
82
+ */
83
+ identifier: string;
84
+ /**
85
+ * The version of `api` that was used to install this SDK.
86
+ *
87
+ * @example 5.0.0
88
+ */
89
+ installerVersion: string;
90
+ /**
91
+ * An integrity hash that will be used to determine on `npx api update` calls if the API has
92
+ * changed since the SDK was last generated.
93
+ *
94
+ * @example sha512-ld+djZk8uRWmzXC+JYla1PTBScg0NjP/8x9vOOKRW+DuJ3NNMRjrpfbY7T77Jgnc87dZZsU49robbQfYe3ukug==
95
+ */
96
+ integrity: string;
97
+ /**
98
+ * The original source that was used to generate the SDK with.
99
+ *
100
+ * @example https://raw.githubusercontent.com/readmeio/oas-examples/main/3.0/json/petstore-simple.json
101
+ * @example ./petstore.json
102
+ * @example @developers/v2.0#nysezql0wwo236
103
+ */
104
+ source: string;
105
+ }
106
+ export {};