oas-normalize 5.2.0 → 7.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/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2019 ReadMe
3
+ Copyright (c) 2022 ReadMe
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -14,22 +14,24 @@ npm install oas-normalize --save
14
14
 
15
15
  # Usage
16
16
 
17
- It's pretty simple:
18
-
19
17
  ```javascript
20
- const OASNormalize = require('oas-normalize');
18
+ import OASNormalize from 'oas-normalize';
19
+ // const { default: OASNormalize } = require('oas-normalize'); // If you're using CJS.
21
20
 
22
21
  const oas = new OASNormalize(
23
22
  // Or a string, pathname, JSON blob, whatever
24
23
  'https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore-expanded.yaml'
25
24
  );
26
25
 
27
- oas.validate().then(definition => {
28
- // Definition will always be JSON, and valid.
29
- console.log(definition);
30
- }).catch(err => {
31
- console.log(err);
32
- });
26
+ oas
27
+ .validate()
28
+ .then(definition => {
29
+ // Definition will always be JSON, and valid.
30
+ console.log(definition);
31
+ })
32
+ .catch(err => {
33
+ console.log(err);
34
+ });
33
35
  ```
34
36
 
35
37
  # Errors
@@ -59,18 +61,23 @@ For validation errors, when available, you'll get back an object:
59
61
 
60
62
  If you want some more functionality, you can do anything here:
61
63
 
64
+ <!--
65
+ Prettier's table formatting sucks, hence the ignore block below.
66
+ -->
67
+ <!-- prettier-ignore-start -->
62
68
  | Function | What it does |
63
69
  | :--- | :--- |
64
70
  | `.load()` | Just load the file, valid or not, as JSON |
65
71
  | `.bundle()` | Bring together all files into one JSON blob (but retain `$ref` pointers) |
66
72
  | `.deref()` | Resolve `$ref` pointers |
67
73
  | `.validate([convertToLatest?])` | Validate the whole thing! |
74
+ <!-- prettier-ignore-end -->
68
75
 
69
76
  # Other Little Features
70
77
 
71
78
  ### Always Return OpenAPI 3.x
72
79
 
73
- If you want `.validate` to always return an OpenAPI 3.x definition, supply `true` as its argument:
80
+ If you want `.validate()` to always return an OpenAPI 3.x definition, supply `true` as its argument:
74
81
 
75
82
  ```js
76
83
  OASNormalize.validate(true).then(...);
@@ -81,7 +88,7 @@ OASNormalize.validate(true).then(...);
81
88
  For security reasons, you need to opt into allowing fetching by a local path. To enable it supply the `enablePaths` option to the class instance:
82
89
 
83
90
  ```js
84
- const oas = new OASNormalize('./petstore.json', { enablePaths: true })
91
+ const oas = new OASNormalize('./petstore.json', { enablePaths: true });
85
92
  ```
86
93
 
87
94
  ### Colorized errors
@@ -91,7 +98,7 @@ If you wish errors from `.validate()` to be styled and colorized, supply `colori
91
98
  ```js
92
99
  const oas = new OASNormalize('https://example.com/petstore.json', {
93
100
  colorizeErrors: true,
94
- })
101
+ });
95
102
  ```
96
103
 
97
104
  Error messages will look like such:
@@ -0,0 +1,111 @@
1
+ import type { OpenAPI } from 'openapi-types';
2
+ export declare type Options = {
3
+ colorizeErrors?: boolean;
4
+ enablePaths?: boolean;
5
+ };
6
+ export default class OASNormalize {
7
+ cache: {
8
+ bundle?: false | OpenAPI.Document;
9
+ deref?: false | OpenAPI.Document;
10
+ load?: false | Record<string, unknown>;
11
+ };
12
+ file: any;
13
+ opts: Options;
14
+ type: boolean | string;
15
+ constructor(file: any, opts?: Options);
16
+ load(): Promise<any>;
17
+ /**
18
+ * Bundle up the given OpenAPI or Swagger definition, resolving any external `$ref` pointers in
19
+ * the process.
20
+ *
21
+ */
22
+ bundle(): Promise<import("openapi-types").OpenAPIV2.Document<{}> | import("openapi-types").OpenAPIV3.Document<{}> | (Omit<Omit<import("openapi-types").OpenAPIV3.Document<{}>, "paths" | "components">, "paths" | "components" | "info" | "servers" | "webhooks" | "jsonSchemaDialect"> & {
23
+ info: import("openapi-types").OpenAPIV3_1.InfoObject;
24
+ jsonSchemaDialect?: string;
25
+ servers?: import("openapi-types").OpenAPIV3_1.ServerObject[];
26
+ } & Pick<{
27
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
28
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
29
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
30
+ }, "paths"> & Omit<Partial<{
31
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
32
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
33
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
34
+ }>, "paths">) | (Omit<Omit<import("openapi-types").OpenAPIV3.Document<{}>, "paths" | "components">, "paths" | "components" | "info" | "servers" | "webhooks" | "jsonSchemaDialect"> & {
35
+ info: import("openapi-types").OpenAPIV3_1.InfoObject;
36
+ jsonSchemaDialect?: string;
37
+ servers?: import("openapi-types").OpenAPIV3_1.ServerObject[];
38
+ } & Pick<{
39
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
40
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
41
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
42
+ }, "webhooks"> & Omit<Partial<{
43
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
44
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
45
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
46
+ }>, "webhooks">) | (Omit<Omit<import("openapi-types").OpenAPIV3.Document<{}>, "paths" | "components">, "paths" | "components" | "info" | "servers" | "webhooks" | "jsonSchemaDialect"> & {
47
+ info: import("openapi-types").OpenAPIV3_1.InfoObject;
48
+ jsonSchemaDialect?: string;
49
+ servers?: import("openapi-types").OpenAPIV3_1.ServerObject[];
50
+ } & Pick<{
51
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
52
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
53
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
54
+ }, "components"> & Omit<Partial<{
55
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
56
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
57
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
58
+ }>, "components">)>;
59
+ /**
60
+ * Dereference the given OpenAPI or Swagger.
61
+ *
62
+ */
63
+ deref(): Promise<import("openapi-types").OpenAPIV2.Document<{}> | import("openapi-types").OpenAPIV3.Document<{}> | (Omit<Omit<import("openapi-types").OpenAPIV3.Document<{}>, "paths" | "components">, "paths" | "components" | "info" | "servers" | "webhooks" | "jsonSchemaDialect"> & {
64
+ info: import("openapi-types").OpenAPIV3_1.InfoObject;
65
+ jsonSchemaDialect?: string;
66
+ servers?: import("openapi-types").OpenAPIV3_1.ServerObject[];
67
+ } & Pick<{
68
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
69
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
70
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
71
+ }, "paths"> & Omit<Partial<{
72
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
73
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
74
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
75
+ }>, "paths">) | (Omit<Omit<import("openapi-types").OpenAPIV3.Document<{}>, "paths" | "components">, "paths" | "components" | "info" | "servers" | "webhooks" | "jsonSchemaDialect"> & {
76
+ info: import("openapi-types").OpenAPIV3_1.InfoObject;
77
+ jsonSchemaDialect?: string;
78
+ servers?: import("openapi-types").OpenAPIV3_1.ServerObject[];
79
+ } & Pick<{
80
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
81
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
82
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
83
+ }, "webhooks"> & Omit<Partial<{
84
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
85
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
86
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
87
+ }>, "webhooks">) | (Omit<Omit<import("openapi-types").OpenAPIV3.Document<{}>, "paths" | "components">, "paths" | "components" | "info" | "servers" | "webhooks" | "jsonSchemaDialect"> & {
88
+ info: import("openapi-types").OpenAPIV3_1.InfoObject;
89
+ jsonSchemaDialect?: string;
90
+ servers?: import("openapi-types").OpenAPIV3_1.ServerObject[];
91
+ } & Pick<{
92
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
93
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
94
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
95
+ }, "components"> & Omit<Partial<{
96
+ paths: import("openapi-types").OpenAPIV3_1.PathsObject<{}, {}>;
97
+ webhooks: Record<string, import("openapi-types").OpenAPIV3_1.ReferenceObject | import("openapi-types").OpenAPIV3_1.PathItemObject<{}>>;
98
+ components: import("openapi-types").OpenAPIV3_1.ComponentsObject;
99
+ }>, "components">)>;
100
+ /**
101
+ * Validate a given OpenAPI or Swagger definition, potentially upconverting it from Swagger to
102
+ * OpenAPI in the process if you wish.
103
+ *
104
+ */
105
+ validate(convertToLatest?: boolean): Promise<any>;
106
+ /**
107
+ * Retrieve the OpenAPI or Swagger version of the current API definition.
108
+ *
109
+ */
110
+ version(): Promise<string>;
111
+ }
package/dist/index.js ADDED
@@ -0,0 +1,225 @@
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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
36
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
37
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
38
+ return new (P || (P = Promise))(function (resolve, reject) {
39
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
40
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
41
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
42
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
43
+ });
44
+ };
45
+ var __generator = (this && this.__generator) || function (thisArg, body) {
46
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
47
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
48
+ function verb(n) { return function (v) { return step([n, v]); }; }
49
+ function step(op) {
50
+ if (f) throw new TypeError("Generator is already executing.");
51
+ while (_) try {
52
+ 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;
53
+ if (y = 0, t) op = [op[0] & 2, t.value];
54
+ switch (op[0]) {
55
+ case 0: case 1: t = op; break;
56
+ case 4: _.label++; return { value: op[1], done: false };
57
+ case 5: _.label++; y = op[1]; op = [0]; continue;
58
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
59
+ default:
60
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
61
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
62
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
63
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
64
+ if (t[2]) _.ops.pop();
65
+ _.trys.pop(); continue;
66
+ }
67
+ op = body.call(thisArg, _);
68
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
69
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
70
+ }
71
+ };
72
+ var __importDefault = (this && this.__importDefault) || function (mod) {
73
+ return (mod && mod.__esModule) ? mod : { "default": mod };
74
+ };
75
+ exports.__esModule = true;
76
+ var fs_1 = __importDefault(require("fs"));
77
+ var openapi_parser_1 = __importDefault(require("@readme/openapi-parser"));
78
+ var node_fetch_1 = __importDefault(require("node-fetch"));
79
+ var swagger2openapi_1 = __importDefault(require("swagger2openapi"));
80
+ var utils = __importStar(require("./lib/utils"));
81
+ var OASNormalize = /** @class */ (function () {
82
+ function OASNormalize(file, opts) {
83
+ this.file = file;
84
+ this.opts = __assign({ colorizeErrors: false, enablePaths: false }, opts);
85
+ this.type = utils.getType(this.file);
86
+ this.cache = {
87
+ load: false,
88
+ bundle: false,
89
+ deref: false
90
+ };
91
+ }
92
+ // Internal API for the most part
93
+ OASNormalize.prototype.load = function () {
94
+ return __awaiter(this, void 0, void 0, function () {
95
+ var resolve, _a, resp, contents;
96
+ var _this = this;
97
+ return __generator(this, function (_b) {
98
+ switch (_b.label) {
99
+ case 0:
100
+ if (this.cache.load)
101
+ return [2 /*return*/, Promise.resolve(this.cache.load)];
102
+ resolve = function (obj) {
103
+ var ret = utils.stringToJSON(obj);
104
+ _this.cache.load = ret;
105
+ return Promise.resolve(ret);
106
+ };
107
+ _a = this.type;
108
+ switch (_a) {
109
+ case 'json': return [3 /*break*/, 1];
110
+ case 'string-json': return [3 /*break*/, 1];
111
+ case 'string-yaml': return [3 /*break*/, 1];
112
+ case 'buffer': return [3 /*break*/, 2];
113
+ case 'url': return [3 /*break*/, 3];
114
+ case 'path': return [3 /*break*/, 5];
115
+ }
116
+ return [3 /*break*/, 6];
117
+ case 1: return [2 /*return*/, resolve(this.file)];
118
+ case 2: return [2 /*return*/, resolve(this.file.toString())];
119
+ case 3: return [4 /*yield*/, (0, node_fetch_1["default"])(this.file).then(function (res) { return res.text(); })];
120
+ case 4:
121
+ resp = _b.sent();
122
+ return [2 /*return*/, resolve(resp)];
123
+ case 5:
124
+ // Load a local file
125
+ if (!this.opts.enablePaths) {
126
+ return [2 /*return*/, Promise.reject(new Error('Use `opts.enablePaths` to enable accessing local files.'))];
127
+ }
128
+ contents = fs_1["default"].readFileSync(this.file).toString();
129
+ return [2 /*return*/, resolve(contents)];
130
+ case 6: return [2 /*return*/, Promise.reject(new Error('Could not load this file.'))];
131
+ }
132
+ });
133
+ });
134
+ };
135
+ /**
136
+ * Bundle up the given OpenAPI or Swagger definition, resolving any external `$ref` pointers in
137
+ * the process.
138
+ *
139
+ */
140
+ OASNormalize.prototype.bundle = function () {
141
+ return __awaiter(this, void 0, void 0, function () {
142
+ var _this = this;
143
+ return __generator(this, function (_a) {
144
+ if (this.cache.bundle)
145
+ return [2 /*return*/, Promise.resolve(this.cache.bundle)];
146
+ return [2 /*return*/, this.load()
147
+ .then(function (schema) { return openapi_parser_1["default"].bundle(schema); })
148
+ .then(function (bundle) {
149
+ _this.cache.bundle = bundle;
150
+ return bundle;
151
+ })];
152
+ });
153
+ });
154
+ };
155
+ /**
156
+ * Dereference the given OpenAPI or Swagger.
157
+ *
158
+ */
159
+ OASNormalize.prototype.deref = function () {
160
+ return __awaiter(this, void 0, void 0, function () {
161
+ var _this = this;
162
+ return __generator(this, function (_a) {
163
+ if (this.cache.deref)
164
+ return [2 /*return*/, Promise.resolve(this.cache.deref)];
165
+ return [2 /*return*/, this.load()
166
+ .then(function (schema) { return openapi_parser_1["default"].dereference(schema); })
167
+ .then(function (dereferenced) {
168
+ _this.cache.deref = dereferenced;
169
+ return dereferenced;
170
+ })];
171
+ });
172
+ });
173
+ };
174
+ /**
175
+ * Validate a given OpenAPI or Swagger definition, potentially upconverting it from Swagger to
176
+ * OpenAPI in the process if you wish.
177
+ *
178
+ */
179
+ OASNormalize.prototype.validate = function (convertToLatest) {
180
+ if (convertToLatest === void 0) { convertToLatest = false; }
181
+ return __awaiter(this, void 0, void 0, function () {
182
+ var colorizeErrors;
183
+ var _this = this;
184
+ return __generator(this, function (_a) {
185
+ colorizeErrors = this.opts.colorizeErrors;
186
+ return [2 /*return*/, this.load().then(function (schema) { return __awaiter(_this, void 0, void 0, function () {
187
+ var baseVersion, clonedSchema;
188
+ return __generator(this, function (_a) {
189
+ baseVersion = parseInt(utils.version(schema), 10);
190
+ if (baseVersion === 1) {
191
+ return [2 /*return*/, Promise.reject(new Error('Swagger v1.2 is unsupported.'))];
192
+ }
193
+ else if (baseVersion === 2 || baseVersion === 3) {
194
+ clonedSchema = JSON.parse(JSON.stringify(schema));
195
+ return [2 /*return*/, openapi_parser_1["default"]
196
+ .validate(clonedSchema, {
197
+ validate: {
198
+ colorizeErrors: colorizeErrors
199
+ }
200
+ })
201
+ .then(function () {
202
+ if (!convertToLatest) {
203
+ return schema;
204
+ }
205
+ return swagger2openapi_1["default"].convertObj(schema, { anchors: true }).then(function (options) {
206
+ return options.openapi;
207
+ });
208
+ })["catch"](function (err) { return Promise.reject(err); })];
209
+ }
210
+ return [2 /*return*/, Promise.reject(new Error('The supplied API definition is unsupported.'))];
211
+ });
212
+ }); })];
213
+ });
214
+ });
215
+ };
216
+ /**
217
+ * Retrieve the OpenAPI or Swagger version of the current API definition.
218
+ *
219
+ */
220
+ OASNormalize.prototype.version = function () {
221
+ return this.load().then(function (schema) { return utils.version(schema); });
222
+ };
223
+ return OASNormalize;
224
+ }());
225
+ exports["default"] = OASNormalize;
@@ -0,0 +1,21 @@
1
+ import type { OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
2
+ /**
3
+ * Retrieve the Swagger or OpenAPI version that a given Swagger or OpenAPI definition are targeting.
4
+ *
5
+ */
6
+ export declare function version(schema: OpenAPIV2.Document & OpenAPIV3.Document & OpenAPIV3_1.Document): string;
7
+ /**
8
+ * Determine if a given variable is a `Buffer`.
9
+ *
10
+ */
11
+ export declare function isBuffer(obj: any): any;
12
+ /**
13
+ * Convert a YAML blob or stringified JSON object into a JSON object.
14
+ *
15
+ */
16
+ export declare function stringToJSON(string: string | Record<string, unknown>): any;
17
+ /**
18
+ * Determine the type of a given variable. Returns `false` if unrecognized.
19
+ *
20
+ */
21
+ export declare function getType(obj: any): false | "path" | "json" | "buffer" | "string-json" | "string-yaml" | "url";
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ exports.__esModule = true;
6
+ exports.getType = exports.stringToJSON = exports.isBuffer = exports.version = void 0;
7
+ var js_yaml_1 = __importDefault(require("js-yaml"));
8
+ /**
9
+ * Retrieve the Swagger or OpenAPI version that a given Swagger or OpenAPI definition are targeting.
10
+ *
11
+ */
12
+ function version(schema) {
13
+ return schema.swagger || schema.openapi;
14
+ }
15
+ exports.version = version;
16
+ /**
17
+ * Determine if a given variable is a `Buffer`.
18
+ *
19
+ */
20
+ function isBuffer(obj) {
21
+ return (obj != null &&
22
+ obj.constructor != null &&
23
+ typeof obj.constructor.isBuffer === 'function' &&
24
+ obj.constructor.isBuffer(obj));
25
+ }
26
+ exports.isBuffer = isBuffer;
27
+ /**
28
+ * Convert a YAML blob or stringified JSON object into a JSON object.
29
+ *
30
+ */
31
+ function stringToJSON(string) {
32
+ if (typeof string === 'object') {
33
+ return string;
34
+ }
35
+ else if (string.match(/^\s*{/)) {
36
+ return JSON.parse(string);
37
+ }
38
+ return js_yaml_1["default"].load(string);
39
+ }
40
+ exports.stringToJSON = stringToJSON;
41
+ /**
42
+ * Determine the type of a given variable. Returns `false` if unrecognized.
43
+ *
44
+ */
45
+ function getType(obj) {
46
+ if (isBuffer(obj)) {
47
+ return 'buffer';
48
+ }
49
+ else if (typeof obj === 'object') {
50
+ return 'json';
51
+ }
52
+ else if (typeof obj === 'string') {
53
+ if (obj.match(/\s*{/)) {
54
+ return 'string-json';
55
+ }
56
+ else if (obj.match(/\n/)) {
57
+ // Not sure about this...
58
+ return 'string-yaml';
59
+ }
60
+ else if (obj.substring(0, 4) === 'http') {
61
+ return 'url';
62
+ }
63
+ return 'path';
64
+ }
65
+ return false;
66
+ }
67
+ exports.getType = getType;
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "oas-normalize",
3
- "version": "5.2.0",
3
+ "version": "7.0.0",
4
4
  "description": "OpenAPI 3.x or Swagger 2.0? YAML or JSON? URL, path, string or object? Who cares! It just works.",
5
- "main": "index.js",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
6
7
  "engines": {
7
- "node": "^12 || ^14 || ^16"
8
+ "node": ">=14"
8
9
  },
9
10
  "tags": [
10
11
  "api",
@@ -29,25 +30,36 @@
29
30
  "url": "https://github.com/readmeio/oas-normalize/issues"
30
31
  },
31
32
  "scripts": {
32
- "lint": "eslint .",
33
+ "build": "tsc",
34
+ "lint": "eslint . --ext .js,.ts && npm run prettier",
35
+ "prebuild": "rm -rf dist/",
36
+ "prepack": "npm run build",
33
37
  "pretest": "npm run lint",
34
- "prettier": "prettier --list-different --write \"./**/**.{js,jsx}\"",
38
+ "prettier": "prettier --list-different \"./**/**.{md,js,ts}\"",
39
+ "prettier:write": "prettier --list-different --write \"./**/**.{md,js,ts}\"",
35
40
  "test": "jest --coverage"
36
41
  },
37
42
  "license": "MIT",
38
43
  "dependencies": {
39
- "@readme/openapi-parser": "^2.1.0",
44
+ "@readme/openapi-parser": "^2.2.0",
40
45
  "js-yaml": "^4.1.0",
41
46
  "node-fetch": "^2.6.1",
47
+ "openapi-types": "^12.0.0",
42
48
  "swagger2openapi": "^7.0.8"
43
49
  },
44
50
  "devDependencies": {
45
- "@readme/eslint-config": "^8.5.1",
46
- "@readme/oas-examples": "^5.0.0",
47
- "eslint": "^8.12.0",
48
- "jest": "^27.5.1",
51
+ "@readme/eslint-config": "^10.0.0",
52
+ "@readme/oas-examples": "^5.1.1",
53
+ "@types/jest": "^28.1.6",
54
+ "@types/js-yaml": "^4.0.5",
55
+ "@types/node-fetch": "^2.6.2",
56
+ "eslint": "^8.21.0",
57
+ "jest": "^28.0.3",
58
+ "jet": "^0.8.1",
49
59
  "nock": "^13.2.4",
50
- "prettier": "^2.6.1"
60
+ "prettier": "^2.7.1",
61
+ "ts-jest": "^28.0.7",
62
+ "typescript": "^4.7.4"
51
63
  },
52
64
  "prettier": "@readme/eslint-config/prettier"
53
65
  }
package/src/.sink.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ // These packages don't have any TS types so we need to declare a module in order to use them.
2
+ declare module 'swagger2openapi';
package/src/index.ts ADDED
@@ -0,0 +1,167 @@
1
+ import type { OpenAPI } from 'openapi-types';
2
+
3
+ import fs from 'fs';
4
+
5
+ import openapiParser from '@readme/openapi-parser';
6
+ import fetch from 'node-fetch';
7
+
8
+ import converter from 'swagger2openapi';
9
+
10
+ import * as utils from './lib/utils';
11
+
12
+ export type Options = {
13
+ colorizeErrors?: boolean;
14
+ enablePaths?: boolean;
15
+ };
16
+
17
+ export default class OASNormalize {
18
+ cache: {
19
+ bundle?: false | OpenAPI.Document;
20
+ deref?: false | OpenAPI.Document;
21
+ load?: false | Record<string, unknown>;
22
+ };
23
+
24
+ file: any;
25
+
26
+ opts: Options;
27
+
28
+ type: boolean | string;
29
+
30
+ constructor(file: any, opts?: Options) {
31
+ this.file = file;
32
+ this.opts = {
33
+ colorizeErrors: false,
34
+ enablePaths: false,
35
+ ...opts,
36
+ };
37
+
38
+ this.type = utils.getType(this.file);
39
+
40
+ this.cache = {
41
+ load: false,
42
+ bundle: false,
43
+ deref: false,
44
+ };
45
+ }
46
+
47
+ // Internal API for the most part
48
+ async load() {
49
+ if (this.cache.load) return Promise.resolve(this.cache.load);
50
+
51
+ const resolve = (obj: Parameters<typeof utils.stringToJSON>[0]) => {
52
+ const ret = utils.stringToJSON(obj);
53
+ this.cache.load = ret;
54
+ return Promise.resolve(ret);
55
+ };
56
+
57
+ switch (this.type) {
58
+ case 'json':
59
+ case 'string-json':
60
+ case 'string-yaml':
61
+ return resolve(this.file);
62
+
63
+ case 'buffer':
64
+ return resolve(this.file.toString());
65
+
66
+ case 'url':
67
+ const resp = await fetch(this.file).then(res => res.text());
68
+ return resolve(resp);
69
+
70
+ case 'path':
71
+ // Load a local file
72
+ if (!this.opts.enablePaths) {
73
+ return Promise.reject(new Error('Use `opts.enablePaths` to enable accessing local files.'));
74
+ }
75
+
76
+ const contents = fs.readFileSync(this.file).toString();
77
+ return resolve(contents);
78
+
79
+ default:
80
+ return Promise.reject(new Error('Could not load this file.'));
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Bundle up the given OpenAPI or Swagger definition, resolving any external `$ref` pointers in
86
+ * the process.
87
+ *
88
+ */
89
+ async bundle() {
90
+ if (this.cache.bundle) return Promise.resolve(this.cache.bundle);
91
+
92
+ return this.load()
93
+ .then(schema => openapiParser.bundle(schema))
94
+ .then(bundle => {
95
+ this.cache.bundle = bundle;
96
+ return bundle;
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Dereference the given OpenAPI or Swagger.
102
+ *
103
+ */
104
+ async deref() {
105
+ if (this.cache.deref) return Promise.resolve(this.cache.deref);
106
+
107
+ return this.load()
108
+ .then(schema => openapiParser.dereference(schema))
109
+ .then(dereferenced => {
110
+ this.cache.deref = dereferenced;
111
+ return dereferenced;
112
+ });
113
+ }
114
+
115
+ /**
116
+ * Validate a given OpenAPI or Swagger definition, potentially upconverting it from Swagger to
117
+ * OpenAPI in the process if you wish.
118
+ *
119
+ */
120
+ async validate(convertToLatest = false) {
121
+ const colorizeErrors = this.opts.colorizeErrors;
122
+
123
+ return this.load().then(async schema => {
124
+ const baseVersion = parseInt(utils.version(schema), 10);
125
+
126
+ if (baseVersion === 1) {
127
+ return Promise.reject(new Error('Swagger v1.2 is unsupported.'));
128
+ } else if (baseVersion === 2 || baseVersion === 3) {
129
+ /**
130
+ * `openapiParser.validate()` dereferences schemas at the same time as validation and does
131
+ * not give us an option to disable this. Since all we already have a dereferencing method
132
+ * on this library and our `validate()` method here just needs to tell us if the definition
133
+ * is valid or not we need to clone it before passing it over to `openapi-parser` so as to
134
+ * not run into pass-by-reference problems.
135
+ */
136
+ const clonedSchema = JSON.parse(JSON.stringify(schema));
137
+
138
+ return openapiParser
139
+ .validate(clonedSchema, {
140
+ validate: {
141
+ colorizeErrors,
142
+ },
143
+ })
144
+ .then(() => {
145
+ if (!convertToLatest) {
146
+ return schema;
147
+ }
148
+
149
+ return converter.convertObj(schema, { anchors: true }).then((options: { openapi: OpenAPI.Document }) => {
150
+ return options.openapi;
151
+ });
152
+ })
153
+ .catch(err => Promise.reject(err));
154
+ }
155
+
156
+ return Promise.reject(new Error('The supplied API definition is unsupported.'));
157
+ });
158
+ }
159
+
160
+ /**
161
+ * Retrieve the OpenAPI or Swagger version of the current API definition.
162
+ *
163
+ */
164
+ version() {
165
+ return this.load().then(schema => utils.version(schema));
166
+ }
167
+ }
@@ -0,0 +1,63 @@
1
+ import type { OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
2
+
3
+ import YAML from 'js-yaml';
4
+
5
+ /**
6
+ * Retrieve the Swagger or OpenAPI version that a given Swagger or OpenAPI definition are targeting.
7
+ *
8
+ */
9
+ export function version(schema: OpenAPIV2.Document & OpenAPIV3.Document & OpenAPIV3_1.Document) {
10
+ return schema.swagger || schema.openapi;
11
+ }
12
+
13
+ /**
14
+ * Determine if a given variable is a `Buffer`.
15
+ *
16
+ */
17
+ export function isBuffer(obj: any) {
18
+ return (
19
+ obj != null &&
20
+ obj.constructor != null &&
21
+ typeof obj.constructor.isBuffer === 'function' &&
22
+ obj.constructor.isBuffer(obj)
23
+ );
24
+ }
25
+
26
+ /**
27
+ * Convert a YAML blob or stringified JSON object into a JSON object.
28
+ *
29
+ */
30
+ export function stringToJSON(string: string | Record<string, unknown>) {
31
+ if (typeof string === 'object') {
32
+ return string;
33
+ } else if (string.match(/^\s*{/)) {
34
+ return JSON.parse(string);
35
+ }
36
+
37
+ return YAML.load(string);
38
+ }
39
+
40
+ /**
41
+ * Determine the type of a given variable. Returns `false` if unrecognized.
42
+ *
43
+ */
44
+ export function getType(obj: any) {
45
+ if (isBuffer(obj)) {
46
+ return 'buffer';
47
+ } else if (typeof obj === 'object') {
48
+ return 'json';
49
+ } else if (typeof obj === 'string') {
50
+ if (obj.match(/\s*{/)) {
51
+ return 'string-json';
52
+ } else if (obj.match(/\n/)) {
53
+ // Not sure about this...
54
+ return 'string-yaml';
55
+ } else if (obj.substring(0, 4) === 'http') {
56
+ return 'url';
57
+ }
58
+
59
+ return 'path';
60
+ }
61
+
62
+ return false;
63
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowJs": true,
4
+ "baseUrl": "./src",
5
+ "declaration": true,
6
+ "esModuleInterop": true,
7
+ "lib": ["dom", "es2020"],
8
+ "outDir": "dist/",
9
+ "paths": {
10
+ "swagger2openapi": [".sink.d.ts"]
11
+ },
12
+ },
13
+ "include": ["./src/**/*"]
14
+ }
package/index.js DELETED
@@ -1,120 +0,0 @@
1
- const fetch = require('node-fetch');
2
- const fs = require('fs');
3
- const converter = require('swagger2openapi');
4
- const openapiParser = require('@readme/openapi-parser');
5
- const utils = require('./lib/utils');
6
-
7
- class oasNormalize {
8
- constructor(file, opts) {
9
- this.file = file;
10
- this.opts = {
11
- colorizeErrors: false,
12
- enablePaths: false,
13
- ...opts,
14
- };
15
-
16
- this.type = utils.type(this.file);
17
-
18
- this.cache = {
19
- load: false,
20
- bundle: false,
21
- deref: false,
22
- };
23
- }
24
-
25
- // Internal API for the most part
26
- async load() {
27
- if (this.cache.load) return Promise.resolve(this.cache.load);
28
-
29
- const resolve = obj => {
30
- const ret = utils.stringToJSON(obj);
31
- this.cache.load = ret;
32
- return Promise.resolve(ret);
33
- };
34
-
35
- if (this.type === 'json' || this.type === 'string-json' || this.type === 'string-yaml') {
36
- return resolve(this.file);
37
- } else if (this.type === 'buffer') {
38
- return resolve(this.file.toString());
39
- } else if (this.type === 'url') {
40
- const resp = await fetch(this.file).then(res => res.text());
41
- return resolve(resp);
42
- } else if (this.type === 'path') {
43
- // Load a local file
44
- if (!this.opts.enablePaths) {
45
- return Promise.reject(new Error('Use `opts.enablePaths` to enable accessing local files.'));
46
- }
47
-
48
- const contents = fs.readFileSync(this.file).toString();
49
- return resolve(contents);
50
- }
51
-
52
- return Promise.reject(new Error('Could not load this file.'));
53
- }
54
-
55
- async bundle() {
56
- if (this.cache.bundle) return Promise.resolve(this.cache.bundle);
57
-
58
- return this.load()
59
- .then(schema => openapiParser.bundle(schema))
60
- .then(bundle => {
61
- this.cache.bundle = bundle;
62
- return bundle;
63
- });
64
- }
65
-
66
- async deref() {
67
- if (this.cache.deref) return Promise.resolve(this.cache.deref);
68
-
69
- return this.load()
70
- .then(schema => openapiParser.dereference(schema))
71
- .then(dereferenced => {
72
- this.cache.deref = dereferenced;
73
- return dereferenced;
74
- });
75
- }
76
-
77
- async validate(convertToLatest = false) {
78
- const colorizeErrors = this.opts.colorizeErrors;
79
-
80
- return this.load().then(async schema => {
81
- const baseVersion = parseInt(utils.version(schema), 10);
82
-
83
- if (baseVersion === 1) {
84
- return Promise.reject(new Error('Swagger v1.2 is unsupported.'));
85
- } else if (baseVersion === 2 || baseVersion === 3) {
86
- // `openapiParser.validate()` dereferences schemas at the same time as validation and does
87
- // not give us an option to disable this. Since all we already have a dereferencing method
88
- // on this library and our `validate()` method here just needs to tell us if the definition
89
- // is valid or not we need to clone it before passing it over to `openapi-parser` so as to
90
- // not run into pass-by-reference problems.
91
- const clonedSchema = JSON.parse(JSON.stringify(schema));
92
-
93
- return openapiParser
94
- .validate(clonedSchema, {
95
- validate: {
96
- colorizeErrors,
97
- },
98
- })
99
- .then(() => {
100
- if (!convertToLatest) {
101
- return schema;
102
- }
103
-
104
- return converter.convertObj(schema, { anchors: true }).then(options => {
105
- return options.openapi;
106
- });
107
- })
108
- .catch(err => Promise.reject(err));
109
- }
110
-
111
- return Promise.reject(new Error('The supplied API definition is unsupported.'));
112
- });
113
- }
114
-
115
- version() {
116
- return this.load().then(schema => utils.version(schema));
117
- }
118
- }
119
-
120
- module.exports = oasNormalize;
package/lib/utils.js DELETED
@@ -1,44 +0,0 @@
1
- const YAML = require('js-yaml');
2
-
3
- module.exports = {
4
- // YAML or JSON string to JSON Object
5
- stringToJSON: string => {
6
- if (typeof string === 'object') {
7
- return string;
8
- } else if (string.match(/^\s*{/)) {
9
- return JSON.parse(string);
10
- }
11
-
12
- return YAML.load(string);
13
- },
14
-
15
- type: obj => {
16
- if (module.exports.isBuffer(obj)) {
17
- return 'buffer';
18
- } else if (typeof obj === 'object') {
19
- return 'json';
20
- } else if (typeof obj === 'string') {
21
- if (obj.match(/\s*{/)) {
22
- return 'string-json';
23
- }
24
- if (obj.match(/\n/)) {
25
- // Not sure about this...
26
- return 'string-yaml';
27
- }
28
- if (obj.substr(0, 4) === 'http') {
29
- return 'url';
30
- }
31
- return 'path';
32
- }
33
-
34
- return false;
35
- },
36
-
37
- version: schema => schema.swagger || schema.openapi,
38
-
39
- isBuffer: obj =>
40
- obj != null &&
41
- obj.constructor != null &&
42
- typeof obj.constructor.isBuffer === 'function' &&
43
- obj.constructor.isBuffer(obj),
44
- };