@scalar/openapi-parser 0.28.16 → 0.29.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/configuration/index.d.ts +6 -5307
  3. package/dist/configuration/index.d.ts.map +1 -1
  4. package/dist/configuration/index.js +2 -15
  5. package/dist/types/index.d.ts +3 -13
  6. package/dist/types/index.d.ts.map +1 -1
  7. package/dist/utils/details.d.ts.map +1 -1
  8. package/dist/utils/details.js +8 -1
  9. package/dist/utils/openapi/openapi.d.ts +172 -172
  10. package/dist/utils/validate.d.ts +6 -1
  11. package/dist/utils/validate.d.ts.map +1 -1
  12. package/dist/utils/validate.js +75 -29
  13. package/package.json +7 -11
  14. package/dist/lib/Validator/Validator.d.ts +0 -22
  15. package/dist/lib/Validator/Validator.d.ts.map +0 -1
  16. package/dist/lib/Validator/Validator.js +0 -146
  17. package/dist/schemas/v2.0/schema.d.ts +0 -1407
  18. package/dist/schemas/v2.0/schema.d.ts.map +0 -1
  19. package/dist/schemas/v2.0/schema.js +0 -1466
  20. package/dist/schemas/v3.0/schema.d.ts +0 -1269
  21. package/dist/schemas/v3.0/schema.d.ts.map +0 -1
  22. package/dist/schemas/v3.0/schema.js +0 -1499
  23. package/dist/schemas/v3.1/schema.d.ts +0 -1225
  24. package/dist/schemas/v3.1/schema.d.ts.map +0 -1
  25. package/dist/schemas/v3.1/schema.js +0 -1292
  26. package/dist/schemas/v3.2/schema.d.ts +0 -1409
  27. package/dist/schemas/v3.2/schema.d.ts.map +0 -1
  28. package/dist/schemas/v3.2/schema.js +0 -1509
  29. package/dist/utils/betterAjvErrors/helpers.d.ts +0 -3
  30. package/dist/utils/betterAjvErrors/helpers.d.ts.map +0 -1
  31. package/dist/utils/betterAjvErrors/helpers.js +0 -159
  32. package/dist/utils/betterAjvErrors/index.d.ts +0 -17
  33. package/dist/utils/betterAjvErrors/index.d.ts.map +0 -1
  34. package/dist/utils/betterAjvErrors/index.js +0 -12
  35. package/dist/utils/betterAjvErrors/utils.d.ts +0 -13
  36. package/dist/utils/betterAjvErrors/utils.d.ts.map +0 -1
  37. package/dist/utils/betterAjvErrors/utils.js +0 -21
  38. package/dist/utils/betterAjvErrors/validation-errors/additional-prop.d.ts +0 -10
  39. package/dist/utils/betterAjvErrors/validation-errors/additional-prop.d.ts.map +0 -1
  40. package/dist/utils/betterAjvErrors/validation-errors/additional-prop.js +0 -15
  41. package/dist/utils/betterAjvErrors/validation-errors/base.d.ts +0 -23
  42. package/dist/utils/betterAjvErrors/validation-errors/base.d.ts.map +0 -1
  43. package/dist/utils/betterAjvErrors/validation-errors/base.js +0 -21
  44. package/dist/utils/betterAjvErrors/validation-errors/default.d.ts +0 -10
  45. package/dist/utils/betterAjvErrors/validation-errors/default.d.ts.map +0 -1
  46. package/dist/utils/betterAjvErrors/validation-errors/default.js +0 -15
  47. package/dist/utils/betterAjvErrors/validation-errors/enum.d.ts +0 -11
  48. package/dist/utils/betterAjvErrors/validation-errors/enum.d.ts.map +0 -1
  49. package/dist/utils/betterAjvErrors/validation-errors/enum.js +0 -36
  50. package/dist/utils/betterAjvErrors/validation-errors/format.d.ts +0 -27
  51. package/dist/utils/betterAjvErrors/validation-errors/format.d.ts.map +0 -1
  52. package/dist/utils/betterAjvErrors/validation-errors/format.js +0 -73
  53. package/dist/utils/betterAjvErrors/validation-errors/index.d.ts +0 -8
  54. package/dist/utils/betterAjvErrors/validation-errors/index.d.ts.map +0 -1
  55. package/dist/utils/betterAjvErrors/validation-errors/index.js +0 -7
  56. package/dist/utils/betterAjvErrors/validation-errors/pattern.d.ts +0 -10
  57. package/dist/utils/betterAjvErrors/validation-errors/pattern.d.ts.map +0 -1
  58. package/dist/utils/betterAjvErrors/validation-errors/pattern.js +0 -15
  59. package/dist/utils/betterAjvErrors/validation-errors/required.d.ts +0 -10
  60. package/dist/utils/betterAjvErrors/validation-errors/required.d.ts.map +0 -1
  61. package/dist/utils/betterAjvErrors/validation-errors/required.js +0 -14
  62. package/dist/utils/betterAjvErrors/validation-errors/unevaluated-prop.d.ts +0 -10
  63. package/dist/utils/betterAjvErrors/validation-errors/unevaluated-prop.d.ts.map +0 -1
  64. package/dist/utils/betterAjvErrors/validation-errors/unevaluated-prop.js +0 -15
  65. package/dist/utils/transform-errors.d.ts +0 -6
  66. package/dist/utils/transform-errors.d.ts.map +0 -1
  67. package/dist/utils/transform-errors.js +0 -65
  68. package/dist/utils/validate-path-parameters.d.ts +0 -6
  69. package/dist/utils/validate-path-parameters.d.ts.map +0 -1
  70. package/dist/utils/validate-path-parameters.js +0 -94
@@ -1,7 +1,12 @@
1
1
  import type { Filesystem, ThrowOnErrorOption, UnknownObject, ValidateResult } from '../types/index.js';
2
2
  export type ValidateOptions = ThrowOnErrorOption;
3
3
  /**
4
- * Validates an OpenAPI document
4
+ * Validates an OpenAPI document.
5
+ *
6
+ * Schema and semantic validation are delegated to `@scalar/openapi-validator`.
7
+ * Reference resolution stays in the parser: references are resolved here and any
8
+ * resolution errors are merged into the result, so the behaviour matches the
9
+ * previous, self-contained validator.
5
10
  */
6
11
  export declare function validate(value: string | UnknownObject | Filesystem, options?: ValidateOptions): Promise<ValidateResult>;
7
12
  //# sourceMappingURL=validate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EAEV,kBAAkB,EAClB,aAAa,EACb,cAAc,EACf,MAAM,eAAe,CAAA;AAItB,MAAM,MAAM,eAAe,GAAG,kBAAkB,CAAA;AAqBhD;;GAEG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,UAAU,EAC1C,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,cAAc,CAAC,CA6CzB"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,UAAU,EAEV,kBAAkB,EAClB,aAAa,EACb,cAAc,EACf,MAAM,eAAe,CAAA;AAMtB,MAAM,MAAM,eAAe,GAAG,kBAAkB,CAAA;AAqBhD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,UAAU,EAC1C,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,cAAc,CAAC,CAyFzB"}
@@ -1,5 +1,9 @@
1
- import { Validator } from '../lib/Validator/Validator.js';
1
+ import { isObject } from '@scalar/helpers/object/is-object';
2
+ import { validate as validateDocument, validatePathParameters } from '@scalar/openapi-validator';
3
+ import { ERRORS } from '../configuration/index.js';
4
+ import { getEntrypoint } from './get-entrypoint.js';
2
5
  import { makeFilesystem } from './make-filesystem.js';
6
+ import { resolveReferences } from './resolve-references.js';
3
7
  const withStrictSpecification = (specification, version) => {
4
8
  if (!specification || typeof specification !== 'object') {
5
9
  return undefined;
@@ -13,44 +17,86 @@ const withStrictSpecification = (specification, version) => {
13
17
  return undefined;
14
18
  };
15
19
  /**
16
- * Validates an OpenAPI document
20
+ * Validates an OpenAPI document.
21
+ *
22
+ * Schema and semantic validation are delegated to `@scalar/openapi-validator`.
23
+ * Reference resolution stays in the parser: references are resolved here and any
24
+ * resolution errors are merged into the result, so the behaviour matches the
25
+ * previous, self-contained validator.
17
26
  */
18
27
  export function validate(value, options) {
19
28
  try {
20
29
  const filesystem = makeFilesystem(value);
21
- const validator = new Validator();
22
- const result = validator.validate(filesystem, options);
23
- /**
24
- * Currently contains no asynchronous logic, but returns a Promise
25
- * to preserve API compatibility and allow async logic in the future.
26
- */
27
- if (result.valid) {
28
- const specification = withStrictSpecification(validator.specification, validator.version);
29
- if (!specification) {
30
- return Promise.resolve({
31
- valid: false,
32
- errors: [
33
- {
34
- message: `Validated OpenAPI ${validator.version} document is missing required top-level version field.`,
35
- },
36
- ],
37
- schema: result.schema,
38
- specification: validator.specification,
39
- version: validator.version,
40
- });
30
+ const entrypoint = getEntrypoint(filesystem);
31
+ // A filesystem without an entrypoint (for example a top-level array) has no
32
+ // document to validate.
33
+ if (!entrypoint || entrypoint.specification === undefined || entrypoint.specification === null) {
34
+ if (options?.throwOnError) {
35
+ throw new Error(ERRORS.EMPTY_OR_INVALID);
41
36
  }
37
+ return Promise.resolve({ valid: false, errors: [{ message: ERRORS.EMPTY_OR_INVALID }] });
38
+ }
39
+ const specification = entrypoint.specification;
40
+ // Be lenient about a missing `info.version`: default it before validation so
41
+ // documents that omit this required field still validate. The standalone
42
+ // `@scalar/openapi-validator` is strict and would otherwise reject them.
43
+ //
44
+ // Semver recommends `0.1.0` as the starting version for initial development,
45
+ // which makes it the right stand-in for a document that never declared one.
46
+ // (This used to be `0.0.1`, which semver does not suggest anywhere.)
47
+ if (isObject(specification) && isObject(specification.info) && typeof specification.info.version !== 'string') {
48
+ specification.info.version = '0.1.0';
49
+ }
50
+ // Schema and version validation only (no reference resolution). Runs first so
51
+ // empty/invalid input reports the same error, in the same order, including
52
+ // when `throwOnError` is set. Path-parameter semantics are left off here (the
53
+ // validator's default) and run below on the resolved document, so parameters
54
+ // declared via `$ref` are seen (validating the unresolved document would
55
+ // report them as missing).
56
+ const outcome = validateDocument(specification, options);
57
+ // Resolve references whenever the document passed schema validation.
58
+ // `outcome.schema` is only set once schema and version validation succeeded.
59
+ const passedSchemaValidation = outcome.schema !== undefined;
60
+ const resolved = passedSchemaValidation ? resolveReferences(filesystem, options) : undefined;
61
+ const referenceErrors = resolved?.errors ?? [];
62
+ const schema = (resolved?.schema ?? outcome.schema);
63
+ // Path-template semantics run on the resolved document (schema validation
64
+ // stays on the unresolved one to avoid following circular references). This
65
+ // matches the previous validator, which merged reference and semantic errors.
66
+ const semanticErrors = passedSchemaValidation
67
+ ? validatePathParameters((schema ?? specification))
68
+ : [];
69
+ const errors = [...(outcome.errors ?? []), ...referenceErrors, ...semanticErrors];
70
+ const valid = outcome.valid && referenceErrors.length === 0 && semanticErrors.length === 0;
71
+ if (!valid) {
72
+ return Promise.resolve({
73
+ valid: false,
74
+ errors,
75
+ schema,
76
+ specification,
77
+ version: outcome.version,
78
+ });
79
+ }
80
+ const strictSpecification = withStrictSpecification(specification, outcome.version);
81
+ if (!strictSpecification) {
42
82
  return Promise.resolve({
43
- ...result,
83
+ valid: false,
84
+ errors: [
85
+ {
86
+ message: `Validated OpenAPI ${outcome.version} document is missing required top-level version field.`,
87
+ },
88
+ ],
89
+ schema,
44
90
  specification,
45
- version: validator.version,
91
+ version: outcome.version,
46
92
  });
47
93
  }
48
94
  return Promise.resolve({
49
- valid: false,
50
- errors: result.errors,
51
- schema: result.schema,
52
- specification: validator.specification,
53
- version: validator.version,
95
+ valid: true,
96
+ errors,
97
+ schema: schema,
98
+ specification: strictSpecification,
99
+ version: outcome.version,
54
100
  });
55
101
  }
56
102
  catch (err) {
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "parser",
18
18
  "typescript"
19
19
  ],
20
- "version": "0.28.16",
20
+ "version": "0.29.0",
21
21
  "engines": {
22
22
  "node": ">=22"
23
23
  },
@@ -48,23 +48,19 @@
48
48
  ],
49
49
  "sideEffects": false,
50
50
  "dependencies": {
51
- "ajv": "^8.17.1",
52
- "ajv-draft-04": "^1.0.0",
53
- "ajv-formats": "^3.0.1",
54
- "jsonpointer": "^5.0.1",
55
- "leven": "^4.0.0",
56
51
  "yaml": "^2.9.0",
57
- "@scalar/helpers": "0.11.1",
52
+ "@scalar/helpers": "0.11.2",
53
+ "@scalar/json-magic": "0.13.3",
54
+ "@scalar/openapi-upgrader": "0.2.15",
55
+ "@scalar/openapi-validator": "0.1.0",
58
56
  "@scalar/openapi-types": "0.9.5",
59
- "@scalar/json-magic": "0.13.2",
60
- "@scalar/openapi-upgrader": "0.2.15"
57
+ "@scalar/types": "0.18.3"
61
58
  },
62
59
  "devDependencies": {
63
60
  "@apidevtools/swagger-parser": "10.1.0",
64
61
  "@types/node": "^24.1.0",
65
62
  "just-diff": "^6.0.2",
66
- "vite": "8.1.5",
67
- "@scalar/types": "0.18.2"
63
+ "vite": "8.1.5"
68
64
  },
69
65
  "scripts": {
70
66
  "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
@@ -1,22 +0,0 @@
1
- import { type ValidateFunction } from 'ajv';
2
- import { type OpenApiVersion } from '../../configuration/index.js';
3
- import type { Filesystem, ThrowOnErrorOption, UnknownObject, ValidationOutcome } from '../../types/index.js';
4
- export declare class Validator {
5
- version: OpenApiVersion;
6
- static supportedVersions: ("2.0" | "3.0" | "3.1" | "3.2")[];
7
- protected ajvValidators: Record<string, ValidateFunction>;
8
- protected errors: string;
9
- protected specificationVersion: string;
10
- protected specificationType: string;
11
- specification: UnknownObject;
12
- private isMutableRecord;
13
- /**
14
- * Checks whether a specification is valid and all references can be resolved.
15
- */
16
- validate(filesystem: Filesystem, options?: ThrowOnErrorOption): ValidationOutcome;
17
- /**
18
- * Ajv JSON schema validator
19
- */
20
- getAjvValidator(version: OpenApiVersion): ValidateFunction;
21
- }
22
- //# sourceMappingURL=Validator.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Validator.d.ts","sourceRoot":"","sources":["../../../src/lib/Validator/Validator.ts"],"names":[],"mappings":"AAAA,OAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,KAAK,CAAA;AAKhD,OAAO,EAAiC,KAAK,cAAc,EAAmB,MAAM,iBAAiB,CAAA;AACrG,OAAO,KAAK,EAAE,UAAU,EAAmB,kBAAkB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAetH,qBAAa,SAAS;IACb,OAAO,EAAE,cAAc,CAAA;IAE9B,OAAc,iBAAiB,oCAAkB;IAGjD,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAK;IAE9D,SAAS,CAAC,MAAM,EAAE,MAAM,CAAA;IAExB,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAA;IAEtC,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAA;IAE5B,aAAa,EAAE,aAAa,CAAA;IAEnC,OAAO,CAAC,eAAe;IAIvB;;OAEG;IACH,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,iBAAiB;IAmGjF;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,gBAAgB;CAkC3D"}
@@ -1,146 +0,0 @@
1
- import Ajv, {} from 'ajv';
2
- import Ajv2020 from 'ajv/dist/2020.js';
3
- import Ajv04 from 'ajv-draft-04';
4
- import addFormats from 'ajv-formats';
5
- import { ERRORS, OpenApiSpecifications, OpenApiVersions } from '../../configuration/index.js';
6
- import { details as getOpenApiVersion } from '../../utils/details.js';
7
- import { resolveReferences } from '../../utils/resolve-references.js';
8
- import { transformErrors } from '../../utils/transform-errors.js';
9
- import { validatePathParameters } from '../../utils/validate-path-parameters.js';
10
- /**
11
- * Configure available JSON Schema versions
12
- */
13
- const jsonSchemaVersions = {
14
- 'http://json-schema.org/draft-04/schema#': Ajv04,
15
- 'http://json-schema.org/draft-07/schema#': Ajv,
16
- 'https://json-schema.org/draft/2020-12/schema': Ajv2020,
17
- };
18
- export class Validator {
19
- version;
20
- static supportedVersions = OpenApiVersions;
21
- // Object with function *or* object { errors: string }
22
- ajvValidators = {};
23
- errors;
24
- specificationVersion;
25
- specificationType;
26
- specification;
27
- isMutableRecord(value) {
28
- return typeof value === 'object' && value !== null && !Array.isArray(value);
29
- }
30
- /**
31
- * Checks whether a specification is valid and all references can be resolved.
32
- */
33
- validate(filesystem, options) {
34
- const entrypoint = filesystem.find((file) => file.isEntrypoint);
35
- const specification = entrypoint?.specification;
36
- // TODO: How does this work with a filesystem?
37
- this.specification = specification;
38
- // TODO: defaulting info.version to keep parser compatible with the previous one
39
- // we should bubble this error up and not throw on it
40
- if (this.isMutableRecord(this.specification) &&
41
- this.isMutableRecord(this.specification.info) &&
42
- typeof this.specification.info.version !== 'string') {
43
- this.specification.info.version = '0.0.1';
44
- }
45
- try {
46
- // AnyObject is empty or invalid
47
- if (specification === undefined || specification === null) {
48
- if (options?.throwOnError) {
49
- throw new Error(ERRORS.EMPTY_OR_INVALID);
50
- }
51
- return {
52
- valid: false,
53
- errors: transformErrors(specification, ERRORS.EMPTY_OR_INVALID),
54
- };
55
- }
56
- // Meta data about the specification
57
- const { version, specificationType, specificationVersion } = getOpenApiVersion(specification);
58
- this.version = version;
59
- this.specificationVersion = specificationVersion;
60
- this.specificationType = specificationType;
61
- // AnyObject is not supported
62
- if (!version) {
63
- if (options?.throwOnError) {
64
- throw new Error(ERRORS.OPENAPI_VERSION_NOT_SUPPORTED);
65
- }
66
- return {
67
- valid: false,
68
- errors: transformErrors(specification, ERRORS.OPENAPI_VERSION_NOT_SUPPORTED),
69
- };
70
- }
71
- // Get the correct OpenAPI validator
72
- const validateSchema = this.getAjvValidator(version);
73
- const schemaResult = validateSchema(specification);
74
- // Error handling
75
- if (validateSchema.errors) {
76
- if (validateSchema.errors.length > 0) {
77
- if (options?.throwOnError) {
78
- throw new Error(validateSchema.errors[0].message);
79
- }
80
- return {
81
- valid: false,
82
- errors: transformErrors(specification, validateSchema.errors),
83
- };
84
- }
85
- }
86
- // Check if the references are valid
87
- const resolvedReferences = resolveReferences(filesystem, options);
88
- const semanticErrors = validatePathParameters(resolvedReferences.schema);
89
- const errors = [...resolvedReferences.errors, ...semanticErrors];
90
- const valid = schemaResult && resolvedReferences.valid && semanticErrors.length === 0;
91
- if (!valid) {
92
- return {
93
- valid: false,
94
- errors,
95
- schema: resolvedReferences.schema,
96
- };
97
- }
98
- return {
99
- valid: true,
100
- errors,
101
- schema: resolvedReferences.schema,
102
- };
103
- }
104
- catch (error) {
105
- // Something went horribly wrong!
106
- if (options?.throwOnError) {
107
- throw error;
108
- }
109
- return {
110
- valid: false,
111
- errors: transformErrors(specification, error.message ?? error),
112
- };
113
- }
114
- }
115
- /**
116
- * Ajv JSON schema validator
117
- */
118
- getAjvValidator(version) {
119
- // Schema loaded already
120
- if (this.ajvValidators[version]) {
121
- return this.ajvValidators[version];
122
- }
123
- // Load OpenAPI Schema
124
- const schema = OpenApiSpecifications[version];
125
- // Load JSON Schema
126
- const AjvClass = jsonSchemaVersions[schema.$schema];
127
- // Get the correct Ajv validator
128
- const ajv = new AjvClass({
129
- // Ajv is a bit too strict in its strict validation of OpenAPI schemas.
130
- // Switch strict mode off.
131
- strict: false,
132
- // Enable discriminator support for better oneOf error messages
133
- discriminator: true,
134
- // Show all errors, not just the first one
135
- allErrors: true,
136
- });
137
- // Register formats
138
- // https://ajv.js.org/packages/ajv-formats.html#formats
139
- addFormats(ajv);
140
- // OpenAPI 3.1 and 3.2 uses media-range format
141
- if (version === '3.1' || version === '3.2') {
142
- ajv.addFormat('media-range', true);
143
- }
144
- return (this.ajvValidators[version] = ajv.compile(schema));
145
- }
146
- }