@scalar/openapi-parser 0.8.6 → 0.8.8

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.
Files changed (74) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/dist/configuration/index.js +27 -1
  4. package/dist/index.js +26 -1
  5. package/dist/lib/Validator/Validator.js +127 -1
  6. package/dist/{utils/load/plugins → plugins/fetch-urls}/fetchUrls.d.ts +1 -1
  7. package/dist/plugins/fetch-urls/fetchUrls.d.ts.map +1 -0
  8. package/dist/plugins/fetch-urls/fetchUrls.js +45 -0
  9. package/dist/plugins/fetch-urls/index.d.ts +2 -0
  10. package/dist/plugins/fetch-urls/index.d.ts.map +1 -0
  11. package/dist/plugins/fetch-urls/index.js +1 -0
  12. package/dist/plugins/read-files/index.d.ts +2 -0
  13. package/dist/plugins/read-files/index.d.ts.map +1 -0
  14. package/dist/plugins/read-files/index.js +1 -0
  15. package/dist/plugins/read-files/readFiles.d.ts +3 -0
  16. package/dist/plugins/read-files/readFiles.d.ts.map +1 -0
  17. package/dist/plugins/read-files/readFiles.js +56 -0
  18. package/dist/polyfills/path.js +120 -1
  19. package/dist/schemas/v2.0/schema.json.js +1623 -1
  20. package/dist/schemas/v3.0/schema.json.js +1673 -1
  21. package/dist/schemas/v3.1/schema.json.js +1451 -1
  22. package/dist/utils/betterAjvErrors/helpers.js +108 -1
  23. package/dist/utils/betterAjvErrors/index.js +15 -1
  24. package/dist/utils/betterAjvErrors/utils.js +31 -1
  25. package/dist/utils/betterAjvErrors/validation-errors/additional-prop.js +20 -1
  26. package/dist/utils/betterAjvErrors/validation-errors/base.js +34 -1
  27. package/dist/utils/betterAjvErrors/validation-errors/default.js +20 -1
  28. package/dist/utils/betterAjvErrors/validation-errors/enum.js +58 -1
  29. package/dist/utils/betterAjvErrors/validation-errors/pattern.js +20 -1
  30. package/dist/utils/betterAjvErrors/validation-errors/required.js +19 -1
  31. package/dist/utils/betterAjvErrors/validation-errors/unevaluated-prop.js +20 -1
  32. package/dist/utils/dereference.js +21 -1
  33. package/dist/utils/details.js +25 -1
  34. package/dist/utils/escapeJsonPointer.js +10 -1
  35. package/dist/utils/filter.js +17 -1
  36. package/dist/utils/getEntrypoint.js +8 -1
  37. package/dist/utils/getListOfReferences.js +27 -1
  38. package/dist/utils/getSegmentsFromPath.js +18 -1
  39. package/dist/utils/isFilesystem.js +11 -1
  40. package/dist/utils/isJson.js +11 -1
  41. package/dist/utils/isObject.js +6 -1
  42. package/dist/utils/isYaml.js +19 -1
  43. package/dist/utils/load/load.js +121 -1
  44. package/dist/utils/makeFilesystem.js +25 -1
  45. package/dist/utils/normalize.js +25 -1
  46. package/dist/utils/openapi/actions/details.js +13 -1
  47. package/dist/utils/openapi/actions/files.js +11 -1
  48. package/dist/utils/openapi/actions/get.js +13 -1
  49. package/dist/utils/openapi/actions/toJson.js +13 -1
  50. package/dist/utils/openapi/actions/toYaml.js +13 -1
  51. package/dist/utils/openapi/commands/dereferenceCommand.js +29 -1
  52. package/dist/utils/openapi/commands/filterCommand.js +28 -1
  53. package/dist/utils/openapi/commands/loadCommand.js +44 -1
  54. package/dist/utils/openapi/commands/upgradeCommand.js +31 -1
  55. package/dist/utils/openapi/commands/validateCommand.js +35 -1
  56. package/dist/utils/openapi/openapi.js +18 -1
  57. package/dist/utils/openapi/utils/queueTask.js +11 -1
  58. package/dist/utils/openapi/utils/workThroughQueue.js +67 -1
  59. package/dist/utils/resolveReferences.js +153 -1
  60. package/dist/utils/toJson.js +3 -1
  61. package/dist/utils/toYaml.js +5 -1
  62. package/dist/utils/transformErrors.js +23 -1
  63. package/dist/utils/traverse.js +26 -1
  64. package/dist/utils/unescapeJsonPointer.js +11 -1
  65. package/dist/utils/upgrade.js +20 -1
  66. package/dist/utils/upgradeFromThreeToThreeOne.js +122 -1
  67. package/dist/utils/upgradeFromTwoToThree.js +174 -1
  68. package/dist/utils/validate.js +18 -1
  69. package/package.json +15 -13
  70. package/dist/utils/load/plugins/fetchUrls.d.ts.map +0 -1
  71. package/dist/utils/load/plugins/fetchUrls.js +0 -1
  72. package/dist/utils/load/plugins/readFiles.d.ts +0 -3
  73. package/dist/utils/load/plugins/readFiles.d.ts.map +0 -1
  74. package/dist/utils/load/plugins/readFiles.js +0 -1
@@ -1 +1,153 @@
1
- import{ERRORS as e}from"../configuration/index.js";import{getEntrypoint as r}from"./getEntrypoint.js";import{getSegmentsFromPath as t}from"./getSegmentsFromPath.js";import{makeFilesystem as o}from"./makeFilesystem.js";function i(e,t,i,E){void 0===E&&(E=[]);const s=structuredClone(e),c=o(s),f=r(c);return a(i?.specification??f.specification,c,i??f),a(i?.specification??f.specification,c,i??f),{valid:0===(E=E.filter(((e,r,t)=>r===t.findIndex((r=>r.message===e.message&&r.code===e.code))))).length,errors:E,schema:(i??r(c)).specification};function a(e,r,o){let i;return Object.entries(e??{}).forEach((([s,c])=>{if(void 0!==e.$ref){const i=n(e.$ref,t,o,r,E);if(void 0===i)return;delete e.$ref,"object"==typeof i&&Object.keys(i).forEach((r=>{void 0===e[r]&&(e[r]=i[r])}))}"object"!=typeof c||function(e){try{return JSON.stringify(e),!1}catch(e){return!0}}(c)||(i=a(c,r,o))})),{errors:i?.errors??[]}}}function n(r,o,E,s,c){if("string"!=typeof r){if(o?.throwOnError)throw new Error(e.INVALID_REFERENCE.replace("%s",r));return void c.push({code:"INVALID_REFERENCE",message:e.INVALID_REFERENCE.replace("%s",r)})}const[f,a]=r.split("#",2),p=f!==E.filename;if(f&&p){const r=s.find((e=>e.filename===f));if(!r){if(o?.throwOnError)throw new Error(e.EXTERNAL_REFERENCE_NOT_FOUND.replace("%s",f));return void c.push({code:"EXTERNAL_REFERENCE_NOT_FOUND",message:e.EXTERNAL_REFERENCE_NOT_FOUND.replace("%s",f)})}const t=i(s,o,r,c);return void 0===a?t.schema:n(`#${a}`,o,r,s,c)}const N=t(a);try{return N.reduce(((e,r)=>e[r]),E.specification)}catch(t){if(o?.throwOnError)throw new Error(e.INVALID_REFERENCE.replace("%s",r));c.push({code:"INVALID_REFERENCE",message:e.INVALID_REFERENCE.replace("%s",r)})}}export{i as resolveReferences};
1
+ import { ERRORS } from '../configuration/index.js';
2
+ import { getEntrypoint } from './getEntrypoint.js';
3
+ import { getSegmentsFromPath } from './getSegmentsFromPath.js';
4
+ import { makeFilesystem } from './makeFilesystem.js';
5
+
6
+ /**
7
+ * Takes a specification and resolves all references.
8
+ */
9
+ function resolveReferences(
10
+ // Just a specification, or a set of files.
11
+ input,
12
+ // Additional options to control the behaviour
13
+ options,
14
+ // Fallback to the entrypoint
15
+ file,
16
+ // Errors that occurred during the process
17
+ errors) {
18
+ // Initialize errors
19
+ if (errors === undefined) {
20
+ errors = [];
21
+ }
22
+ // Detach from input
23
+ const clonedInput = structuredClone(input);
24
+ // Make it a filesystem, even if it’s just one file
25
+ const filesystem = makeFilesystem(clonedInput);
26
+ // Get the main file
27
+ const entrypoint = getEntrypoint(filesystem);
28
+ // Recursively resolve all references
29
+ resolve(file?.specification ?? entrypoint.specification, filesystem, file ?? entrypoint);
30
+ // If we replace references with content, that includes a reference, we can’t deal with that right-away.
31
+ // That’s why we need a second run.
32
+ resolve(file?.specification ?? entrypoint.specification, filesystem, file ?? entrypoint);
33
+ // Remove duplicats (according to message) from errors
34
+ errors = errors.filter((error, index, self) => index ===
35
+ self.findIndex((t) => t.message === error.message && t.code === error.code));
36
+ // Return the resolved specification
37
+ return {
38
+ valid: errors.length === 0,
39
+ errors: errors,
40
+ schema: (file ?? getEntrypoint(filesystem))
41
+ .specification,
42
+ };
43
+ /**
44
+ * Resolves the circular reference to an object and deletes the $ref properties.
45
+ */
46
+ function resolve(schema, resolveFilesystem, resolveFile) {
47
+ let result;
48
+ // Iterate over the whole objecct
49
+ Object.entries(schema ?? {}).forEach(([_, value]) => {
50
+ // Ignore parts without a reference
51
+ if (schema.$ref !== undefined) {
52
+ // Find the referenced content
53
+ const target = resolveUri(schema.$ref, options, resolveFile, resolveFilesystem, errors);
54
+ if (target === undefined) {
55
+ return undefined;
56
+ }
57
+ // Get rid of the reference
58
+ delete schema.$ref;
59
+ if (typeof target === 'object') {
60
+ Object.keys(target).forEach((key) => {
61
+ if (schema[key] === undefined) {
62
+ schema[key] = target[key];
63
+ }
64
+ });
65
+ }
66
+ }
67
+ if (typeof value === 'object' && !isCircular(value)) {
68
+ result = resolve(value, resolveFilesystem, resolveFile);
69
+ }
70
+ });
71
+ return {
72
+ errors: result?.errors ?? [],
73
+ };
74
+ }
75
+ }
76
+ // TODO: Is there a better way? :D
77
+ function isCircular(schema) {
78
+ try {
79
+ JSON.stringify(schema);
80
+ return false;
81
+ }
82
+ catch (error) {
83
+ return true;
84
+ }
85
+ }
86
+ /**
87
+ * Resolves a URI to a part of the specification
88
+ */
89
+ function resolveUri(
90
+ // 'foobar.json#/foo/bar'
91
+ uri, options,
92
+ // { filename: './foobar.json '}
93
+ file,
94
+ // [ { filename: './foobar.json '} ]
95
+ filesystem, errors) {
96
+ // Ignore invalid URIs
97
+ if (typeof uri !== 'string') {
98
+ if (options?.throwOnError) {
99
+ throw new Error(ERRORS.INVALID_REFERENCE.replace('%s', uri));
100
+ }
101
+ errors.push({
102
+ code: 'INVALID_REFERENCE',
103
+ message: ERRORS.INVALID_REFERENCE.replace('%s', uri),
104
+ });
105
+ return;
106
+ }
107
+ // Understand the URI
108
+ const [prefix, path] = uri.split('#', 2);
109
+ /** Check whether the file is pointing to itself */
110
+ const isDifferentFile = prefix !== file.filename;
111
+ // External references
112
+ if (prefix && isDifferentFile) {
113
+ const externalReference = filesystem.find((entry) => {
114
+ return entry.filename === prefix;
115
+ });
116
+ if (!externalReference) {
117
+ if (options?.throwOnError) {
118
+ throw new Error(ERRORS.EXTERNAL_REFERENCE_NOT_FOUND.replace('%s', prefix));
119
+ }
120
+ errors.push({
121
+ code: 'EXTERNAL_REFERENCE_NOT_FOUND',
122
+ message: ERRORS.EXTERNAL_REFERENCE_NOT_FOUND.replace('%s', prefix),
123
+ });
124
+ return;
125
+ }
126
+ const result = resolveReferences(filesystem, options, externalReference, errors);
127
+ // $ref: 'other-file.yaml'
128
+ if (path === undefined) {
129
+ return result.schema;
130
+ }
131
+ // $ref: 'other-file.yaml#/foo/bar'
132
+ return resolveUri(`#${path}`, options, externalReference, filesystem, errors);
133
+ }
134
+ // Pointers
135
+ const segments = getSegmentsFromPath(path);
136
+ // Try to find the URI
137
+ try {
138
+ return segments.reduce((acc, key) => {
139
+ return acc[key];
140
+ }, file.specification);
141
+ }
142
+ catch (error) {
143
+ if (options?.throwOnError) {
144
+ throw new Error(ERRORS.INVALID_REFERENCE.replace('%s', uri));
145
+ }
146
+ errors.push({
147
+ code: 'INVALID_REFERENCE',
148
+ message: ERRORS.INVALID_REFERENCE.replace('%s', uri),
149
+ });
150
+ }
151
+ }
152
+
153
+ export { resolveReferences };
@@ -1 +1,3 @@
1
- const n=n=>JSON.stringify(n,null,2);export{n as toJson};
1
+ const toJson = (value) => JSON.stringify(value, null, 2);
2
+
3
+ export { toJson };
@@ -1 +1,5 @@
1
- import{stringify as o}from"yaml";const m=m=>o(m);export{m as toYaml};
1
+ import { stringify } from 'yaml';
2
+
3
+ const toYaml = (value) => stringify(value);
4
+
5
+ export { toYaml };
@@ -1 +1,23 @@
1
- import{betterAjvErrors as e}from"./betterAjvErrors/index.js";function r(r,t){return"string"==typeof t?[{message:t}]:e(r,null,t,{indent:2}).map((e=>(e.message=e.message.trim(),e)))}export{r as transformErrors};
1
+ import { betterAjvErrors } from './betterAjvErrors/index.js';
2
+
3
+ /**
4
+ * Transforms ajv errors, finds the positions in the schema and returns an enriched format.
5
+ */
6
+ function transformErrors(specification, errors) {
7
+ // TODO: This should work with multiple files
8
+ if (typeof errors === 'string') {
9
+ return [
10
+ {
11
+ message: errors,
12
+ },
13
+ ];
14
+ }
15
+ return betterAjvErrors(specification, null, errors, {
16
+ indent: 2,
17
+ }).map((error) => {
18
+ error.message = error.message.trim();
19
+ return error;
20
+ });
21
+ }
22
+
23
+ export { transformErrors };
@@ -1 +1,26 @@
1
- function t(o,n,r=[]){const e={};for(const[c,f]of Object.entries(o)){const o=[...r,c];Array.isArray(f)?e[c]=f.map(((r,e)=>"object"==typeof r&&null!==r?t(r,n,[...o,e.toString()]):r)):e[c]="object"==typeof f&&null!==f?t(f,n,o):f}return n(e,r)}export{t as traverse};
1
+ /**
2
+ * Recursively traverses the specification and applies the transform function to each node.
3
+ */
4
+ function traverse(specification, transform, path = []) {
5
+ const result = {};
6
+ for (const [key, value] of Object.entries(specification)) {
7
+ const currentPath = [...path, key];
8
+ if (Array.isArray(value)) {
9
+ result[key] = value.map((item, index) => {
10
+ if (typeof item === 'object' && item !== null) {
11
+ return traverse(item, transform, [...currentPath, index.toString()]);
12
+ }
13
+ return item;
14
+ });
15
+ }
16
+ else if (typeof value === 'object' && value !== null) {
17
+ result[key] = traverse(value, transform, currentPath);
18
+ }
19
+ else {
20
+ result[key] = value;
21
+ }
22
+ }
23
+ return transform(result, path);
24
+ }
25
+
26
+ export { traverse };
@@ -1 +1,11 @@
1
- function e(e){return decodeURI(e.replace(/~1/g,"/").replace(/~0/g,"~"))}export{e as unescapeJsonPointer};
1
+ /**
2
+ * Unescape JSON pointer
3
+ *
4
+ * Examples:
5
+ * /foo~1bar~0baz -> /foo/bar~baz
6
+ */
7
+ function unescapeJsonPointer(uri) {
8
+ return decodeURI(uri.replace(/~1/g, '/').replace(/~0/g, '~'));
9
+ }
10
+
11
+ export { unescapeJsonPointer };
@@ -1 +1,20 @@
1
- import{getEntrypoint as r}from"./getEntrypoint.js";import{makeFilesystem as e}from"./makeFilesystem.js";import{upgradeFromThreeToThreeOne as o}from"./upgradeFromThreeToThreeOne.js";import{upgradeFromTwoToThree as i}from"./upgradeFromTwoToThree.js";function t(t){return{specification:[i,o].reduce(((r,e)=>e(r)),r(e(t)).specification),version:"3.1"}}export{t as upgrade};
1
+ import { getEntrypoint } from './getEntrypoint.js';
2
+ import { makeFilesystem } from './makeFilesystem.js';
3
+ import { upgradeFromThreeToThreeOne } from './upgradeFromThreeToThreeOne.js';
4
+ import { upgradeFromTwoToThree } from './upgradeFromTwoToThree.js';
5
+
6
+ /**
7
+ * Upgrade specification to OpenAPI 3.1.0
8
+ */
9
+ function upgrade(value) {
10
+ const upgraders = [upgradeFromTwoToThree, upgradeFromThreeToThreeOne];
11
+ // TODO: Run upgrade over the whole filesystem
12
+ const result = upgraders.reduce((currentSpecification, upgrader) => upgrader(currentSpecification), getEntrypoint(makeFilesystem(value)).specification);
13
+ return {
14
+ specification: result,
15
+ // TODO: Make dynamic
16
+ version: '3.1',
17
+ };
18
+ }
19
+
20
+ export { upgrade };
@@ -1 +1,122 @@
1
- import{traverse as e}from"./traverse.js";function t(t){let m=t;return m.openapi?.startsWith("3.0")?(m.openapi="3.1.0",m=e(m,(e=>("undefined"!==e.type&&!0===e.nullable&&(e.type=["null",e.type],delete e.nullable),e))),m=e(m,(e=>(!0===e.exclusiveMinimum?(e.exclusiveMinimum=e.minimum,delete e.minimum):!1===e.exclusiveMinimum&&delete e.exclusiveMinimum,!0===e.exclusiveMaximum?(e.exclusiveMaximum=e.maximum,delete e.maximum):!1===e.exclusiveMaximum&&delete e.exclusiveMaximum,e))),m=e(m,((e,t)=>(void 0!==e.example&&(i(t)?e.examples=[e.example]:e.examples={default:e.example},delete e.example),e))),m=e(m,(e=>{if("object"===e.type&&void 0!==e.properties){const t=Object.entries(e.properties);for(const[e,i]of t)"object"==typeof i&&"string"===i.type&&"binary"===i.format&&(i.contentEncoding="application/octet-stream",delete i.format)}return e})),m=e(m,(e=>{if("string"!==e.type||"binary"!==e.format)return e})),m=e(m,(e=>"string"===e.type&&"base64"===e.format?{type:"string",contentEncoding:"base64"}:e)),m):m}function i(e){return[["components","schemas"],"properties","items","allOf","anyOf","oneOf","not","additionalProperties"].some((t=>Array.isArray(t)?t.every(((t,i)=>e[i]===t)):e.includes(t)))||e.includes("schema")||e.some((e=>e.endsWith("Schema")))}export{i as isSchemaPath,t as upgradeFromThreeToThreeOne};
1
+ import { traverse } from './traverse.js';
2
+
3
+ /**
4
+ * Upgrade from OpenAPI 3.0.x to 3.1.0
5
+ *
6
+ * https://www.openapis.org/blog/2021/02/16/migrating-from-openapi-3-0-to-3-1-0
7
+ */
8
+ function upgradeFromThreeToThreeOne(originalSpecification) {
9
+ let specification = originalSpecification;
10
+ // Version
11
+ if (specification.openapi?.startsWith('3.0')) {
12
+ specification.openapi = '3.1.0';
13
+ }
14
+ else {
15
+ // Skip if it’s something else than 3.0.x
16
+ return specification;
17
+ }
18
+ // Nullable types
19
+ specification = traverse(specification, (schema) => {
20
+ if (schema.type !== 'undefined' && schema.nullable === true) {
21
+ schema.type = ['null', schema.type];
22
+ delete schema.nullable;
23
+ }
24
+ return schema;
25
+ });
26
+ // exclusiveMinimum and exclusiveMaximum
27
+ specification = traverse(specification, (schema) => {
28
+ if (schema.exclusiveMinimum === true) {
29
+ schema.exclusiveMinimum = schema.minimum;
30
+ delete schema.minimum;
31
+ }
32
+ else if (schema.exclusiveMinimum === false) {
33
+ delete schema.exclusiveMinimum;
34
+ }
35
+ if (schema.exclusiveMaximum === true) {
36
+ schema.exclusiveMaximum = schema.maximum;
37
+ delete schema.maximum;
38
+ }
39
+ else if (schema.exclusiveMaximum === false) {
40
+ delete schema.exclusiveMaximum;
41
+ }
42
+ return schema;
43
+ });
44
+ // Use examples not example
45
+ specification = traverse(specification, (schema, path) => {
46
+ if (schema.example !== undefined) {
47
+ // Arrays in schemas
48
+ if (isSchemaPath(path)) {
49
+ schema.examples = [schema.example];
50
+ }
51
+ // Objects everywhere else
52
+ else {
53
+ schema.examples = {
54
+ default: schema.example,
55
+ };
56
+ }
57
+ delete schema.example;
58
+ }
59
+ return schema;
60
+ });
61
+ // Multipart file uploads with a binary file
62
+ specification = traverse(specification, (schema) => {
63
+ if (schema.type === 'object' && schema.properties !== undefined) {
64
+ // Types
65
+ const entries = Object.entries(schema.properties);
66
+ for (const [_, value] of entries) {
67
+ if (typeof value === 'object' &&
68
+ value.type === 'string' &&
69
+ value.format === 'binary') {
70
+ value.contentEncoding = 'application/octet-stream';
71
+ delete value.format;
72
+ }
73
+ }
74
+ }
75
+ return schema;
76
+ });
77
+ // Uploading a binary file in a POST request
78
+ specification = traverse(specification, (schema) => {
79
+ if (schema.type === 'string' && schema.format === 'binary') {
80
+ return undefined;
81
+ }
82
+ return schema;
83
+ });
84
+ // Uploading an image with base64 encoding
85
+ specification = traverse(specification, (schema) => {
86
+ if (schema.type === 'string' && schema.format === 'base64') {
87
+ return {
88
+ type: 'string',
89
+ contentEncoding: 'base64',
90
+ };
91
+ }
92
+ return schema;
93
+ });
94
+ // Declaring $schema Dialects to protect against change
95
+ // if (typeof specification.$schema === 'undefined') {
96
+ // specification.$schema = 'http://json-schema.org/draft-07/schema#'
97
+ // }
98
+ return specification;
99
+ }
100
+ /** Determine if the current path is within a schema */
101
+ function isSchemaPath(path) {
102
+ const schemaLocations = [
103
+ ['components', 'schemas'],
104
+ 'properties',
105
+ 'items',
106
+ 'allOf',
107
+ 'anyOf',
108
+ 'oneOf',
109
+ 'not',
110
+ 'additionalProperties',
111
+ ];
112
+ return (schemaLocations.some((location) => {
113
+ if (Array.isArray(location)) {
114
+ return location.every((segment, index) => path[index] === segment);
115
+ }
116
+ return path.includes(location);
117
+ }) ||
118
+ path.includes('schema') ||
119
+ path.some((segment) => segment.endsWith('Schema')));
120
+ }
121
+
122
+ export { isSchemaPath, upgradeFromThreeToThreeOne };
@@ -1 +1,174 @@
1
- import{traverse as e}from"./traverse.js";function t(t){if(!t.swagger?.startsWith("2.0"))return t;if(t.openapi="3.0.3",delete t.swagger,console.warn("[upgradeFromTwoToThree] The upgrade from Swagger 2.0 to OpenAPI 3.0 documents is experimental and lacks features."),t.host){const e=t.schemes?.length?t.schemes:["http"];t.servers=e.map((e=>({url:`${e}://${t.host}${t.basePath??""}`}))),delete t.basePath,delete t.schemes,delete t.host}if(t.definitions&&("object"!=typeof t.components&&(t.components={}),t.components.schemas=t.definitions,delete t.definitions,t=e(t,(e=>(e.$ref?.startsWith("#/definitions/")&&(e.$ref=e.$ref.replace(/^#\/definitions\//,"#/components/schemas/")),e)))),t.paths)for(const e in t.paths)if(Object.hasOwn(t.paths,e)){const o=t.paths[e];for(const e in o)if(Object.hasOwn(o,e)){const s=o[e];if(s.parameters){const e=structuredClone(s.parameters.find((e=>"body"===e.in))??{});if(e&&Object.keys(e).length){delete e.name,delete e.in;const o=t.consumes??s.consumes??["application/json"];"object"!=typeof s.requestBody&&(s.requestBody={}),"object"!=typeof s.requestBody.content&&(s.requestBody.content={});const{schema:n,...r}=e;s.requestBody={...s.requestBody,...r};for(const e of o)s.requestBody.content[e]={schema:n}}s.parameters=s.parameters.filter((e=>"body"!==e.in)),delete s.consumes;const o=s.parameters.filter((e=>"formData"===e.in));if(o.length>0){"object"!=typeof s.requestBody&&(s.requestBody={}),"object"!=typeof s.requestBody.content&&(s.requestBody.content={}),s.requestBody.content["application/x-www-form-urlencoded"]={schema:{type:"object",properties:{},required:[]}};for(const e of o)s.requestBody.content["application/x-www-form-urlencoded"].schema.properties[e.name]={type:e.type,description:e.description},e.required&&s.requestBody.content["application/x-www-form-urlencoded"].schema.required.push(e.name);s.parameters=s.parameters.filter((e=>"formData"!==e.in))}}if(s.responses)for(const e in s.responses)if(Object.hasOwn(s.responses,e)){const o=s.responses[e];if(o.schema){const e=t.produces??s.produces??["application/json"];"object"!=typeof o.content&&(o.content={});for(const t of e)o.content[t]={schema:o.schema};delete o.schema}}delete s.produces,0===s.parameters?.length&&delete s.parameters}}if(t.securityDefinitions){"object"!=typeof t.components&&(t.components={}),t.components.securitySchemes={};for(const[e,o]of Object.entries(t.securityDefinitions))if("object"==typeof o)if("type"in o&&"oauth2"===o.type){const{flow:s,authorizationUrl:n,tokenUrl:r,scopes:c}=o;t.components.securitySchemes[e]={type:"oauth2",flows:{[s]:{...n&&{authorizationUrl:n},...r&&{tokenUrl:r},...c&&{scopes:c}}}}}else t.components.securitySchemes[e]=o;delete t.securityDefinitions}return t}export{t as upgradeFromTwoToThree};
1
+ import { traverse } from './traverse.js';
2
+
3
+ /**
4
+ * Upgrade Swagger 2.0 to OpenAPI 3.0
5
+ *
6
+ * https://swagger.io/blog/news/whats-new-in-openapi-3-0/
7
+ */
8
+ function upgradeFromTwoToThree(specification) {
9
+ // Version
10
+ if (specification.swagger?.startsWith('2.0')) {
11
+ specification.openapi = '3.0.3';
12
+ delete specification.swagger;
13
+ }
14
+ else {
15
+ // Skip if it’s something else than 3.0.x
16
+ return specification;
17
+ }
18
+ console.warn(`[upgradeFromTwoToThree] The upgrade from Swagger 2.0 to OpenAPI 3.0 documents is experimental and lacks features.`);
19
+ // Servers
20
+ if (specification.host) {
21
+ const schemes = specification.schemes?.length
22
+ ? specification.schemes
23
+ : ['http'];
24
+ specification.servers = schemes.map((scheme) => ({
25
+ url: `${scheme}://${specification.host}${specification.basePath ?? ''}`,
26
+ }));
27
+ delete specification.basePath;
28
+ delete specification.schemes;
29
+ delete specification.host;
30
+ }
31
+ // Schemas
32
+ if (specification.definitions) {
33
+ if (typeof specification.components !== 'object') {
34
+ specification.components = {};
35
+ }
36
+ specification.components.schemas = specification.definitions;
37
+ delete specification.definitions;
38
+ // Rewrite $refs to definitions
39
+ specification = traverse(specification, (schema) => {
40
+ if (schema.$ref?.startsWith('#/definitions/')) {
41
+ schema.$ref = schema.$ref.replace(/^#\/definitions\//, '#/components/schemas/');
42
+ }
43
+ return schema;
44
+ });
45
+ }
46
+ // Paths
47
+ if (specification.paths) {
48
+ for (const path in specification.paths) {
49
+ if (Object.hasOwn(specification.paths, path)) {
50
+ const pathItem = specification.paths[path];
51
+ for (const method in pathItem) {
52
+ if (Object.hasOwn(pathItem, method)) {
53
+ const operationItem = pathItem[method];
54
+ // Request bodies
55
+ if (operationItem.parameters) {
56
+ const bodyParameter = structuredClone(operationItem.parameters.find((parameter) => parameter.in === 'body') ?? {});
57
+ if (bodyParameter && Object.keys(bodyParameter).length) {
58
+ delete bodyParameter.name;
59
+ delete bodyParameter.in;
60
+ const consumes = specification.consumes ??
61
+ operationItem.consumes ?? ['application/json'];
62
+ if (typeof operationItem.requestBody !== 'object') {
63
+ operationItem.requestBody = {};
64
+ }
65
+ if (typeof operationItem.requestBody.content !== 'object') {
66
+ operationItem.requestBody.content = {};
67
+ }
68
+ const { schema, ...requestBody } = bodyParameter;
69
+ operationItem.requestBody = {
70
+ ...operationItem.requestBody,
71
+ ...requestBody,
72
+ };
73
+ for (const type of consumes) {
74
+ operationItem.requestBody.content[type] = {
75
+ schema: schema,
76
+ };
77
+ }
78
+ }
79
+ // Delete body parameter
80
+ operationItem.parameters = operationItem.parameters.filter((parameter) => parameter.in !== 'body');
81
+ delete operationItem.consumes;
82
+ // formData parameters
83
+ const formDataParameters = operationItem.parameters.filter((parameter) => parameter.in === 'formData');
84
+ if (formDataParameters.length > 0) {
85
+ if (typeof operationItem.requestBody !== 'object') {
86
+ operationItem.requestBody = {};
87
+ }
88
+ if (typeof operationItem.requestBody.content !== 'object') {
89
+ operationItem.requestBody.content = {};
90
+ }
91
+ operationItem.requestBody.content['application/x-www-form-urlencoded'] = {
92
+ schema: {
93
+ type: 'object',
94
+ properties: {},
95
+ required: [], // Initialize required array
96
+ },
97
+ };
98
+ for (const param of formDataParameters) {
99
+ operationItem.requestBody.content['application/x-www-form-urlencoded'].schema.properties[param.name] = {
100
+ type: param.type,
101
+ description: param.description,
102
+ };
103
+ // Add to required array if param is required
104
+ if (param.required) {
105
+ operationItem.requestBody.content['application/x-www-form-urlencoded'].schema.required.push(param.name);
106
+ }
107
+ }
108
+ // Remove formData parameters from the parameters array
109
+ operationItem.parameters = operationItem.parameters.filter((parameter) => parameter.in !== 'formData');
110
+ }
111
+ }
112
+ // Responses
113
+ if (operationItem.responses) {
114
+ for (const response in operationItem.responses) {
115
+ if (Object.hasOwn(operationItem.responses, response)) {
116
+ const responseItem = operationItem.responses[response];
117
+ if (responseItem.schema) {
118
+ const produces = specification.produces ??
119
+ operationItem.produces ?? ['application/json'];
120
+ if (typeof responseItem.content !== 'object') {
121
+ responseItem.content = {};
122
+ }
123
+ for (const type of produces) {
124
+ responseItem.content[type] = {
125
+ schema: responseItem.schema,
126
+ };
127
+ }
128
+ delete responseItem.schema;
129
+ }
130
+ }
131
+ }
132
+ }
133
+ delete operationItem.produces;
134
+ // Delete empty parameters
135
+ if (operationItem.parameters?.length === 0) {
136
+ delete operationItem.parameters;
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
142
+ }
143
+ // Upgrade securityDefinitions
144
+ if (specification.securityDefinitions) {
145
+ if (typeof specification.components !== 'object') {
146
+ specification.components = {};
147
+ }
148
+ specification.components.securitySchemes = {};
149
+ for (const [key, securityScheme] of Object.entries(specification.securityDefinitions)) {
150
+ if (typeof securityScheme === 'object') {
151
+ if ('type' in securityScheme && securityScheme.type === 'oauth2') {
152
+ const { flow, authorizationUrl, tokenUrl, scopes } = securityScheme;
153
+ specification.components.securitySchemes[key] = {
154
+ type: 'oauth2',
155
+ flows: {
156
+ [flow]: {
157
+ ...(authorizationUrl && { authorizationUrl }),
158
+ ...(tokenUrl && { tokenUrl }),
159
+ ...(scopes && { scopes }),
160
+ },
161
+ },
162
+ };
163
+ }
164
+ else {
165
+ specification.components.securitySchemes[key] = securityScheme;
166
+ }
167
+ }
168
+ }
169
+ delete specification.securityDefinitions;
170
+ }
171
+ return specification;
172
+ }
173
+
174
+ export { upgradeFromTwoToThree };
@@ -1 +1,18 @@
1
- import{Validator as i}from"../lib/Validator/Validator.js";import{makeFilesystem as o}from"./makeFilesystem.js";async function t(t,a){const e=o(t),r=new i;return{...await r.validate(e,a),specification:r.specification,version:r.version}}export{t as validate};
1
+ import { makeFilesystem } from './makeFilesystem.js';
2
+ import { Validator } from '../lib/Validator/Validator.js';
3
+
4
+ /**
5
+ * Validates an OpenAPI schema.
6
+ */
7
+ async function validate(value, options) {
8
+ const filesystem = makeFilesystem(value);
9
+ const validator = new Validator();
10
+ const result = await validator.validate(filesystem, options);
11
+ return {
12
+ ...result,
13
+ specification: validator.specification,
14
+ version: validator.version,
15
+ };
16
+ }
17
+
18
+ export { validate };
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "parser",
18
18
  "typescript"
19
19
  ],
20
- "version": "0.8.6",
20
+ "version": "0.8.8",
21
21
  "engines": {
22
22
  "node": ">=18"
23
23
  },
@@ -26,13 +26,19 @@
26
26
  "types": "./dist/index.d.ts",
27
27
  "exports": {
28
28
  ".": {
29
- "import": "./dist/index.js"
29
+ "import": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js"
30
32
  },
31
33
  "./plugins/fetch-urls": {
32
- "import": "./dist/utils/load/plugins/fetchUrls.js"
34
+ "import": "./dist/plugins/fetch-urls/index.js",
35
+ "types": "./dist/plugins/fetch-urls/index.d.ts",
36
+ "default": "./dist/plugins/fetch-urls/index.js"
33
37
  },
34
38
  "./plugins/read-files": {
35
- "import": "./dist/utils/load/plugins/readFiles.js"
39
+ "import": "./dist/plugins/read-files/index.js",
40
+ "types": "./dist/plugins/read-files/index.d.ts",
41
+ "default": "./dist/plugins/read-files/index.js"
36
42
  }
37
43
  },
38
44
  "files": [
@@ -53,27 +59,23 @@
53
59
  "@apidevtools/swagger-parser": "^10.1.0",
54
60
  "@babel/code-frame": "^7.24.7",
55
61
  "@google-cloud/storage": "^7.12.1",
56
- "@rollup/plugin-json": "^6.1.0",
57
- "@rollup/plugin-terser": "^0.4.4",
58
- "@rollup/plugin-typescript": "^11.1.6",
59
62
  "@types/node": "^20.14.10",
60
63
  "glob": "^10.3.10",
61
64
  "json-to-ast": "^2.1.0",
62
65
  "just-diff": "^6.0.2",
63
- "rollup": "^4.16.4",
64
- "rollup-plugin-output-size": "^1.4.1",
65
66
  "tinybench": "^2.8.0",
66
- "@scalar/openapi-types": "0.1.2"
67
+ "@scalar/build-tooling": "0.1.11",
68
+ "@scalar/openapi-types": "0.1.4"
67
69
  },
68
70
  "scripts": {
69
- "build": "pnpm types:check && rollup -c --configPlugin typescript && pnpm types:build",
71
+ "build": "scalar-build-rollup",
70
72
  "format": "prettier --write .",
71
73
  "lint:check": "eslint .",
72
74
  "lint:fix": "eslint . --fix",
73
75
  "test": "vitest",
74
76
  "test:prepare": "vite-node scripts/load-files.ts",
75
77
  "test:unit": "vite-node scripts/load-files.ts && vitest",
76
- "types:build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
77
- "types:check": "tsc --noEmit --skipLibCheck"
78
+ "types:build": "scalar-types-build",
79
+ "types:check": "scalar-types-check"
78
80
  }
79
81
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"fetchUrls.d.ts","sourceRoot":"","sources":["../../../../src/utils/load/plugins/fetchUrls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC,eAAO,MAAM,6BAA6B;;CAEzC,CAAA;AAED,eAAO,MAAM,SAAS,EAAE,CAAC,mBAAmB,CAAC,EAAE;IAC7C;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;IACtB;;OAEG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CAC3C,KAAK,UAiDL,CAAA"}
@@ -1 +0,0 @@
1
- const t={limit:20},e=e=>{let s=0;const r={...t,...e};return{check:t=>"string"==typeof t&&!(!t.startsWith("http://")&&!t.startsWith("https://")),async get(t){if(!1!==r?.limit&&s>=r?.limit)console.warn(`[fetchUrls] Maximum number of requests reeached (${r?.limit}), skipping request`);else try{s++;const e=await(r?.fetch?r.fetch(t):fetch(t));return await e.text()}catch(e){console.error("[fetchUrls]",e.message,`(${t})`)}}}};export{e as fetchUrls,t as fetchUrlsDefaultConfiguration};
@@ -1,3 +0,0 @@
1
- import type { LoadPlugin } from '../load.js';
2
- export declare const readFiles: () => LoadPlugin;
3
- //# sourceMappingURL=readFiles.d.ts.map