@trojs/openapi-dereference 1.1.1 → 1.2.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/package.json +1 -1
- package/src/dereference.js +66 -35
package/package.json
CHANGED
package/src/dereference.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/* eslint-disable sonarjs/cognitive-complexity */
|
|
2
|
-
/* eslint-disable no-param-reassign */
|
|
3
|
-
/* eslint-disable no-restricted-syntax */
|
|
4
1
|
import { klona } from './klona.js'
|
|
5
2
|
import { resolveRefSync } from './resolveRef.js'
|
|
6
3
|
|
|
@@ -9,51 +6,85 @@ import { resolveRefSync } from './resolveRef.js'
|
|
|
9
6
|
* @typedef {import('./types').DereferencedJSONSchema} DereferencedJSONSchema
|
|
10
7
|
*/
|
|
11
8
|
|
|
9
|
+
const PROHIBITED_KEYS = new Set(['__proto__', 'constructor', 'prototype'])
|
|
12
10
|
const cache = new Map()
|
|
13
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Removes prohibited keys from an object (shallow).
|
|
14
|
+
* @param {object} obj
|
|
15
|
+
* @returns {object}
|
|
16
|
+
*/
|
|
17
|
+
function filterProhibitedKeys (obj) {
|
|
18
|
+
return Object.fromEntries(
|
|
19
|
+
Object.entries(obj).filter(([key]) => !PROHIBITED_KEYS.has(key))
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
14
23
|
/**
|
|
15
24
|
* Resolves all $ref pointers in a schema and returns a new schema without any $ref pointers.
|
|
16
|
-
*
|
|
17
|
-
* @
|
|
25
|
+
* Handles circular references and deeply nested $refs.
|
|
26
|
+
* @param {JSONSchema} schema - The JSON schema to dereference.
|
|
27
|
+
* @returns {DereferencedJSONSchema} The dereferenced schema.
|
|
18
28
|
*/
|
|
19
29
|
export const dereferenceSync = (schema) => {
|
|
20
|
-
if (cache.has(schema))
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
if (cache.has(schema)) return cache.get(schema)
|
|
31
|
+
|
|
32
|
+
// Filter prohibited keys at the root level
|
|
33
|
+
const filtered = typeof schema === 'object' && schema !== null && !Array.isArray(schema)
|
|
34
|
+
? filterProhibitedKeys(schema)
|
|
35
|
+
: schema
|
|
23
36
|
|
|
24
|
-
const
|
|
25
|
-
const
|
|
37
|
+
const cloned = klona(filtered)
|
|
38
|
+
const seen = new WeakMap()
|
|
26
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Recursively resolves a value (object, array, or primitive).
|
|
42
|
+
* @param {any} current - The current value to resolve.
|
|
43
|
+
* @param {string} path - The current JSON pointer path.
|
|
44
|
+
* @returns {any} The resolved value.
|
|
45
|
+
*/
|
|
27
46
|
const resolve = (current, path) => {
|
|
28
|
-
if (typeof current
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
47
|
+
if (typeof current !== 'object' || current === null) return current
|
|
48
|
+
|
|
49
|
+
// Handle circular references
|
|
50
|
+
if (seen.has(current)) return seen.get(current)
|
|
51
|
+
|
|
52
|
+
// Handle arrays
|
|
53
|
+
if (Array.isArray(current)) {
|
|
54
|
+
const arr = current.map((item, i) => resolve(item, `${path}/${i}`))
|
|
55
|
+
seen.set(current, arr)
|
|
56
|
+
return arr
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Handle $ref
|
|
60
|
+
if ('$ref' in current && typeof current.$ref === 'string') {
|
|
61
|
+
const ref = resolveRefSync(cloned, current.$ref)
|
|
62
|
+
if (!ref) {
|
|
63
|
+
return null
|
|
64
|
+
}
|
|
65
|
+
if (seen.has(ref)) {
|
|
66
|
+
return seen.get(ref)
|
|
32
67
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
for (let index = 0; index < current.length; index++) {
|
|
38
|
-
current[index] = resolve(current[index], `${path}/${index}`)
|
|
39
|
-
}
|
|
40
|
-
} else {
|
|
41
|
-
// object
|
|
42
|
-
if ('$ref' in current && typeof current.$ref === 'string') {
|
|
43
|
-
let ref = current
|
|
44
|
-
do {
|
|
45
|
-
ref = resolveRefSync(cloned, ref.$ref)
|
|
46
|
-
} while (ref?.$ref)
|
|
47
|
-
return ref
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
for (const key in current) {
|
|
51
|
-
current[key] = resolve(current[key], `${path}/${key}`)
|
|
52
|
-
}
|
|
68
|
+
if (Array.isArray(ref)) {
|
|
69
|
+
const resolvedArray = resolve(ref, current.$ref)
|
|
70
|
+
seen.set(ref, resolvedArray)
|
|
71
|
+
return resolvedArray
|
|
53
72
|
}
|
|
73
|
+
const placeholder = {}
|
|
74
|
+
seen.set(current, placeholder)
|
|
75
|
+
const resolved = resolve(ref, current.$ref)
|
|
76
|
+
Object.assign(placeholder, resolved)
|
|
77
|
+
return resolved
|
|
54
78
|
}
|
|
55
79
|
|
|
56
|
-
|
|
80
|
+
// Handle objects
|
|
81
|
+
const obj = Object.fromEntries(
|
|
82
|
+
Object.entries(current)
|
|
83
|
+
.filter(([key]) => !PROHIBITED_KEYS.has(key))
|
|
84
|
+
.map(([key, value]) => [key, resolve(value, `${path}/${key}`)])
|
|
85
|
+
)
|
|
86
|
+
seen.set(current, obj)
|
|
87
|
+
return obj
|
|
57
88
|
}
|
|
58
89
|
|
|
59
90
|
const result = resolve(cloned, '#')
|