incur 0.3.21 → 0.3.23

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.
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Dereferences all local `$ref` pointers in a JSON object (e.g. `{"$ref": "#/components/schemas/User"}`),
3
+ * replacing them inline with the resolved values. Only handles local (`#/...`) references.
4
+ *
5
+ * Handles circular references by caching a mutable placeholder before recursing.
6
+ *
7
+ * Minimal reimplementation of the dereferencing behavior from `@apidevtools/json-schema-ref-parser`
8
+ * (https://github.com/APIDevTools/json-schema-ref-parser). Only supports in-memory, local-pointer
9
+ * resolution — no file/URL resolution, no `$id` scoping.
10
+ */
11
+ export function dereference<value>(root: value): value {
12
+ const cache = new Map<string, unknown>()
13
+ return walk(root, root, cache) as value
14
+ }
15
+
16
+ function walk(node: unknown, root: unknown, cache: Map<string, unknown>): unknown {
17
+ if (Array.isArray(node)) return node.map((item) => walk(item, root, cache))
18
+
19
+ if (typeof node !== 'object' || node === null) return node
20
+
21
+ const obj = node as Record<string, unknown>
22
+
23
+ // Resolve $ref pointer
24
+ if (typeof obj.$ref === 'string' && obj.$ref.startsWith('#')) {
25
+ const ref = obj.$ref
26
+ if (cache.has(ref)) return cache.get(ref)
27
+
28
+ const resolved = resolvePointer(root, ref)
29
+
30
+ // Non-object targets (primitives, arrays) can't be circular — resolve directly
31
+ if (typeof resolved !== 'object' || resolved === null || Array.isArray(resolved)) {
32
+ const dereferenced = walk(resolved, root, cache)
33
+ cache.set(ref, dereferenced)
34
+ return dereferenced
35
+ }
36
+
37
+ // Use a mutable placeholder so circular refs resolve to the same object.
38
+ // If the walked result is not a plain object (e.g. chained ref to primitive/array),
39
+ // skip the placeholder and cache directly.
40
+ const placeholder: Record<string, unknown> = {}
41
+ cache.set(ref, placeholder)
42
+ const dereferenced = walk(resolved, root, cache)
43
+ if (typeof dereferenced !== 'object' || dereferenced === null || Array.isArray(dereferenced)) {
44
+ cache.set(ref, dereferenced)
45
+ return dereferenced
46
+ }
47
+ Object.assign(placeholder, dereferenced)
48
+ return placeholder
49
+ }
50
+
51
+ const result: Record<string, unknown> = {}
52
+ for (const key of Object.keys(obj)) result[key] = walk(obj[key], root, cache)
53
+ return result
54
+ }
55
+
56
+ /** Resolves a JSON Pointer (e.g. `#/components/schemas/User`) against a root object. */
57
+ function resolvePointer(root: unknown, pointer: string): unknown {
58
+ // "#" or "#/" → root
59
+ const fragment = pointer.slice(1)
60
+ if (fragment === '' || fragment === '/') return root
61
+
62
+ const parts = fragment
63
+ .slice(1)
64
+ .split('/')
65
+ .map((p) => p.replace(/~1/g, '/').replace(/~0/g, '~'))
66
+
67
+ let current: unknown = root
68
+ for (const part of parts) {
69
+ if (typeof current !== 'object' || current === null)
70
+ throw new Error(`Cannot resolve $ref "${pointer}": path segment "${part}" not found`)
71
+ current = (current as Record<string, unknown>)[part]
72
+ if (current === undefined) throw new Error(`Cannot resolve $ref "${pointer}": "${part}" not found`)
73
+ }
74
+ return current
75
+ }