@scalar/openapi-parser 0.10.1 → 0.10.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @scalar/openapi-parser
2
2
 
3
+ ## 0.10.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 8dce84f: Call `onDereference` after dereference process
8
+
9
+ ## 0.10.2
10
+
11
+ ### Patch Changes
12
+
13
+ - 3791738: fix(openapi-parser): correct schema upgrade for "format: binary"
14
+
3
15
  ## 0.10.1
4
16
 
5
17
  ### Patch Changes
@@ -23,8 +23,7 @@ const getSiblings =
23
23
  const concatAll =
24
24
  /* ::<T> */
25
25
 
26
-
27
- (xs /*: $ReadOnlyArray<T> */) =>
26
+ (xs /*: $ReadOnlyArray<T> */) =>
28
27
  (ys /* : $ReadOnlyArray<T> */) /* : $ReadOnlyArray<T> */ =>
29
28
  ys.reduce((zs, z) => zs.concat(z), xs);
30
29
 
@@ -6,6 +6,11 @@ export type ResolveReferencesResult = {
6
6
  schema: OpenAPI.Document;
7
7
  };
8
8
  export type ResolveReferencesOptions = ThrowOnErrorOption & {
9
+ /**
10
+ * Fired when dereferenced a schema.
11
+ *
12
+ * Note that for object schemas, its properties may not be dereferenced when the hook is called.
13
+ */
9
14
  onDereference?: (data: {
10
15
  schema: AnyObject;
11
16
  ref: string;
@@ -1 +1 @@
1
- {"version":3,"file":"resolveReferences.d.ts","sourceRoot":"","sources":["../../src/utils/resolveReferences.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAGpD,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,EACf,kBAAkB,EACnB,MAAM,UAAU,CAAA;AAejB,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG,kBAAkB,GAAG;IAC1D,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,SAAS,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CACnE,CAAA;AAOD;;GAEG;AACH,wBAAgB,iBAAiB,CAE/B,KAAK,EAAE,SAAS,GAAG,UAAU,EAE7B,OAAO,CAAC,EAAE,wBAAwB,EAElC,IAAI,CAAC,EAAE,eAAe,EAEtB,MAAM,CAAC,EAAE,WAAW,EAAE,GACrB,uBAAuB,CAqGzB"}
1
+ {"version":3,"file":"resolveReferences.d.ts","sourceRoot":"","sources":["../../src/utils/resolveReferences.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAGpD,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,EACf,kBAAkB,EACnB,MAAM,UAAU,CAAA;AAejB,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG,kBAAkB,GAAG;IAC1D;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,SAAS,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CACnE,CAAA;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAE/B,KAAK,EAAE,SAAS,GAAG,UAAU,EAE7B,OAAO,CAAC,EAAE,wBAAwB,EAElC,IAAI,CAAC,EAAE,eAAe,EAEtB,MAAM,GAAE,WAAW,EAAO,GACzB,uBAAuB,CAqCzB"}
@@ -14,66 +14,64 @@ options,
14
14
  // Fallback to the entrypoint
15
15
  file,
16
16
  // Errors that occurred during the process
17
- errors) {
18
- // Initialize errors
19
- if (errors === undefined) {
20
- errors = [];
21
- }
17
+ errors = []) {
22
18
  // Detach from input
23
19
  const clonedInput = structuredClone(input);
24
20
  // Make it a filesystem, even if it’s just one file
25
21
  const filesystem = makeFilesystem(clonedInput);
26
22
  // Get the main file
27
23
  const entrypoint = getEntrypoint(filesystem);
24
+ const finalInput = file?.specification ?? entrypoint.specification;
28
25
  // Recursively resolve all references
29
- resolve(file?.specification ?? entrypoint.specification, filesystem, file ?? entrypoint);
26
+ dereference(finalInput, filesystem, file ?? entrypoint, new WeakSet(), errors, options);
30
27
  // Remove duplicats (according to message) from errors
31
28
  errors = errors.filter((error, index, self) => index ===
32
29
  self.findIndex((t) => t.message === error.message && t.code === error.code));
33
30
  // Return the resolved specification
34
31
  return {
35
32
  valid: errors.length === 0,
36
- errors: errors,
37
- schema: (file ?? getEntrypoint(filesystem))
38
- .specification,
33
+ errors,
34
+ schema: finalInput,
39
35
  };
40
- /**
41
- * Resolves the circular reference to an object and deletes the $ref properties.
42
- */
43
- function resolve(schema, resolveFilesystem, resolveFile,
44
- // references to resolved object
45
- resolved = new WeakSet()) {
46
- let result = { errors: [] };
47
- if (schema === null || resolved.has(schema))
48
- return result;
49
- resolved.add(schema);
50
- function resolveExternal(externalFile) {
51
- resolve(externalFile.specification, resolveFilesystem, externalFile, resolved);
52
- return externalFile;
53
- }
54
- // Ignore parts without a reference
55
- while (schema.$ref !== undefined) {
56
- // Find the referenced content
57
- const target = resolveUri(schema.$ref, options, resolveFile, resolveFilesystem, resolveExternal, errors);
58
- // invalid
59
- if (typeof target !== 'object' || target === null)
60
- break;
61
- options?.onDereference?.({ schema, ref: schema.$ref });
62
- // Get rid of the reference
63
- delete schema.$ref;
64
- for (const key of Object.keys(target)) {
65
- if (schema[key] === undefined) {
66
- schema[key] = target[key];
67
- }
36
+ }
37
+ /**
38
+ * Resolves the circular reference to an object and deletes the $ref properties (in-place).
39
+ */
40
+ function dereference(schema, filesystem, entrypoint,
41
+ // references to resolved object
42
+ resolvedSchemas,
43
+ // error output
44
+ errors, options) {
45
+ if (schema === null || resolvedSchemas.has(schema))
46
+ return;
47
+ resolvedSchemas.add(schema);
48
+ function resolveExternal(externalFile) {
49
+ dereference(externalFile.specification, filesystem, externalFile, resolvedSchemas, errors, options);
50
+ return externalFile;
51
+ }
52
+ while (schema.$ref !== undefined) {
53
+ // Find the referenced content
54
+ const resolved = resolveUri(schema.$ref, options, entrypoint, filesystem, resolveExternal, errors);
55
+ // invalid
56
+ if (typeof resolved !== 'object' || resolved === null)
57
+ break;
58
+ const dereferencedRef = schema.$ref;
59
+ // Get rid of the reference
60
+ delete schema.$ref;
61
+ for (const key of Object.keys(resolved)) {
62
+ if (schema[key] === undefined) {
63
+ schema[key] = resolved[key];
68
64
  }
69
65
  }
70
- // Iterate over the whole object
71
- for (const value of Object.values(schema)) {
72
- if (typeof value === 'object' && value !== null) {
73
- result = resolve(value, resolveFilesystem, resolveFile, resolved);
74
- }
66
+ if (dereferencedRef) {
67
+ options?.onDereference?.({ schema, ref: dereferencedRef });
68
+ }
69
+ }
70
+ // Iterate over the whole object
71
+ for (const value of Object.values(schema)) {
72
+ if (typeof value === 'object' && value !== null) {
73
+ dereference(value, filesystem, entrypoint, resolvedSchemas, errors, options);
75
74
  }
76
- return result;
77
75
  }
78
76
  }
79
77
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"upgradeFromThreeToThreeOne.d.ts","sourceRoot":"","sources":["../../src/utils/upgradeFromThreeToThreeOne.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAIxD;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,qBAAqB,EAAE,aAAa;;;;;;;;;;;;;;;;;;;;mBAkHrC;AAED,uDAAuD;AACvD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAsBpD"}
1
+ {"version":3,"file":"upgradeFromThreeToThreeOne.d.ts","sourceRoot":"","sources":["../../src/utils/upgradeFromThreeToThreeOne.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAIxD;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,qBAAqB,EAAE,aAAa;;;;;;;;;;;;;;;;;;;;mBAmIrC;AAED,uDAAuD;AACvD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAsBpD"}
@@ -63,25 +63,38 @@ function upgradeFromThreeToThreeOne(originalSpecification) {
63
63
  return schema;
64
64
  });
65
65
  // Multipart file uploads with a binary file
66
- specification = traverse(specification, (schema) => {
66
+ specification = traverse(specification, (schema, path) => {
67
67
  if (schema.type === 'object' && schema.properties !== undefined) {
68
- // Types
69
- const entries = Object.entries(schema.properties);
70
- for (const [_, value] of entries) {
71
- if (typeof value === 'object' &&
72
- value.type === 'string' &&
73
- value.format === 'binary') {
74
- value.contentEncoding = 'application/octet-stream';
75
- delete value.format;
68
+ // Check if this is a multipart request body schema
69
+ const parentPath = path.slice(0, -1);
70
+ const isMultipart = parentPath.some((segment, index) => {
71
+ return (segment === 'content' && path[index + 1] === 'multipart/form-data');
72
+ });
73
+ if (isMultipart) {
74
+ // Types
75
+ const entries = Object.entries(schema.properties);
76
+ for (const [_, value] of entries) {
77
+ if (typeof value === 'object' &&
78
+ value.type === 'string' &&
79
+ value.format === 'binary') {
80
+ value.contentMediaType = 'application/octet-stream';
81
+ delete value.format;
82
+ }
76
83
  }
77
84
  }
78
85
  }
79
86
  return schema;
80
87
  });
81
88
  // Uploading a binary file in a POST request
82
- specification = traverse(specification, (schema) => {
89
+ specification = traverse(specification, (schema, path) => {
90
+ if (path.includes('content') && path.includes('application/octet-stream')) {
91
+ return {};
92
+ }
83
93
  if (schema.type === 'string' && schema.format === 'binary') {
84
- return undefined;
94
+ return {
95
+ type: 'string',
96
+ contentMediaType: 'application/octet-stream',
97
+ };
85
98
  }
86
99
  return schema;
87
100
  });
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "parser",
18
18
  "typescript"
19
19
  ],
20
- "version": "0.10.1",
20
+ "version": "0.10.3",
21
21
  "engines": {
22
22
  "node": ">=18"
23
23
  },
@@ -59,14 +59,14 @@
59
59
  "@apidevtools/swagger-parser": "^10.1.0",
60
60
  "@babel/code-frame": "^7.24.7",
61
61
  "@google-cloud/storage": "^7.12.1",
62
- "@types/node": "^20.14.10",
62
+ "@types/node": "^20.17.10",
63
63
  "glob": "^10.3.10",
64
64
  "json-to-ast": "^2.1.0",
65
65
  "just-diff": "^6.0.2",
66
66
  "tinybench": "^2.8.0",
67
+ "@scalar/types": "0.0.25",
67
68
  "@scalar/build-tooling": "0.1.12",
68
- "@scalar/openapi-types": "0.1.5",
69
- "@scalar/types": "0.0.25"
69
+ "@scalar/openapi-types": "0.1.5"
70
70
  },
71
71
  "scripts": {
72
72
  "build": "scalar-build-rollup",