@scalar/helpers 0.2.16 → 0.2.18
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 +12 -0
- package/dist/general/create-limiter.d.ts +21 -0
- package/dist/general/create-limiter.d.ts.map +1 -0
- package/dist/general/create-limiter.js +32 -0
- package/dist/general/create-limiter.js.map +7 -0
- package/dist/json/escape-json-pointer.d.ts +7 -0
- package/dist/json/escape-json-pointer.d.ts.map +1 -0
- package/dist/json/escape-json-pointer.js +5 -0
- package/dist/json/escape-json-pointer.js.map +7 -0
- package/dist/json/parse-json-pointer-segments.d.ts +5 -0
- package/dist/json/parse-json-pointer-segments.d.ts.map +1 -0
- package/dist/json/parse-json-pointer-segments.js +6 -0
- package/dist/json/parse-json-pointer-segments.js.map +7 -0
- package/dist/json/unescape-json-pointer.d.ts +8 -0
- package/dist/json/unescape-json-pointer.d.ts.map +1 -0
- package/dist/json/unescape-json-pointer.js +5 -0
- package/dist/json/unescape-json-pointer.js.map +7 -0
- package/dist/object/get-value-at-path.d.ts +12 -0
- package/dist/object/get-value-at-path.d.ts.map +1 -0
- package/dist/object/get-value-at-path.js +12 -0
- package/dist/object/get-value-at-path.js.map +7 -0
- package/dist/object/to-json-compatible.d.ts +19 -0
- package/dist/object/to-json-compatible.d.ts.map +1 -0
- package/dist/object/to-json-compatible.js +68 -0
- package/dist/object/to-json-compatible.js.map +7 -0
- package/dist/queue/queue.d.ts +65 -0
- package/dist/queue/queue.d.ts.map +1 -0
- package/dist/queue/queue.js +91 -0
- package/dist/queue/queue.js.map +7 -0
- package/package.json +6 -1
- package/dist/object/circular-to-refs.d.ts +0 -24
- package/dist/object/circular-to-refs.d.ts.map +0 -1
- package/dist/object/circular-to-refs.js +0 -290
- package/dist/object/circular-to-refs.js.map +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @scalar/helpers
|
|
2
2
|
|
|
3
|
+
## 0.2.18
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#8314](https://github.com/scalar/scalar/pull/8314): chore: limit concurrent operations while migrating workspaces
|
|
8
|
+
|
|
9
|
+
## 0.2.17
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#8310](https://github.com/scalar/scalar/pull/8310): refactor: move helpers to the helpers package to share primitive helpers
|
|
14
|
+
|
|
3
15
|
## 0.2.16
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a function that limits the number of concurrent executions of async functions.
|
|
3
|
+
*
|
|
4
|
+
* @param maxConcurrent - Maximum number of concurrent executions allowed
|
|
5
|
+
* @returns A function that wraps async functions to limit their concurrent execution
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const limiter = createLimiter(2) // Allow max 2 concurrent executions
|
|
10
|
+
*
|
|
11
|
+
* // These will run with max 2 at a time
|
|
12
|
+
* const results = await Promise.all([
|
|
13
|
+
* limiter(() => fetch('/api/1')),
|
|
14
|
+
* limiter(() => fetch('/api/2')),
|
|
15
|
+
* limiter(() => fetch('/api/3')),
|
|
16
|
+
* limiter(() => fetch('/api/4'))
|
|
17
|
+
* ])
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function createLimiter(maxConcurrent: number): <T>(fn: () => Promise<T>) => Promise<T>;
|
|
21
|
+
//# sourceMappingURL=create-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-limiter.d.ts","sourceRoot":"","sources":["../../src/general/create-limiter.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,IAgB9B,CAAC,MAAM,MAAM,OAAO,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAAC,CAgBxD"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Queue } from "../queue/queue.js";
|
|
2
|
+
function createLimiter(maxConcurrent) {
|
|
3
|
+
let activeCount = 0;
|
|
4
|
+
const queue = new Queue();
|
|
5
|
+
const next = () => {
|
|
6
|
+
if (queue.isEmpty() || activeCount >= maxConcurrent) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const resolve = queue.dequeue();
|
|
10
|
+
if (resolve) {
|
|
11
|
+
resolve();
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const run = async (fn) => {
|
|
15
|
+
if (activeCount >= maxConcurrent) {
|
|
16
|
+
await new Promise((resolve) => queue.enqueue(resolve));
|
|
17
|
+
}
|
|
18
|
+
activeCount++;
|
|
19
|
+
try {
|
|
20
|
+
const result = await fn();
|
|
21
|
+
return result;
|
|
22
|
+
} finally {
|
|
23
|
+
activeCount--;
|
|
24
|
+
next();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
return run;
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
createLimiter
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=create-limiter.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/general/create-limiter.ts"],
|
|
4
|
+
"sourcesContent": ["import { Queue } from '../queue/queue'\n\n/**\n * Creates a function that limits the number of concurrent executions of async functions.\n *\n * @param maxConcurrent - Maximum number of concurrent executions allowed\n * @returns A function that wraps async functions to limit their concurrent execution\n *\n * @example\n * ```ts\n * const limiter = createLimiter(2) // Allow max 2 concurrent executions\n *\n * // These will run with max 2 at a time\n * const results = await Promise.all([\n * limiter(() => fetch('/api/1')),\n * limiter(() => fetch('/api/2')),\n * limiter(() => fetch('/api/3')),\n * limiter(() => fetch('/api/4'))\n * ])\n * ```\n */\nexport function createLimiter(maxConcurrent: number) {\n let activeCount = 0\n const queue = new Queue<() => void>()\n\n const next = () => {\n if (queue.isEmpty() || activeCount >= maxConcurrent) {\n return\n }\n\n const resolve = queue.dequeue()\n\n if (resolve) {\n resolve()\n }\n }\n\n const run = async <T>(fn: () => Promise<T>): Promise<T> => {\n if (activeCount >= maxConcurrent) {\n await new Promise<void>((resolve) => queue.enqueue(resolve))\n }\n\n activeCount++\n try {\n const result = await fn()\n return result\n } finally {\n activeCount--\n next()\n }\n }\n\n return run\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,aAAa;AAqBf,SAAS,cAAc,eAAuB;AACnD,MAAI,cAAc;AAClB,QAAM,QAAQ,IAAI,MAAkB;AAEpC,QAAM,OAAO,MAAM;AACjB,QAAI,MAAM,QAAQ,KAAK,eAAe,eAAe;AACnD;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,QAAQ;AAE9B,QAAI,SAAS;AACX,cAAQ;AAAA,IACV;AAAA,EACF;AAEA,QAAM,MAAM,OAAU,OAAqC;AACzD,QAAI,eAAe,eAAe;AAChC,YAAM,IAAI,QAAc,CAAC,YAAY,MAAM,QAAQ,OAAO,CAAC;AAAA,IAC7D;AAEA;AACA,QAAI;AACF,YAAM,SAAS,MAAM,GAAG;AACxB,aAAO;AAAA,IACT,UAAE;AACA;AACA,WAAK;AAAA,IACP;AAAA,EACF;AAEA,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escape-json-pointer.d.ts","sourceRoot":"","sources":["../../src/json/escape-json-pointer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,MAAsD,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/json/escape-json-pointer.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Escapes a JSON pointer string.\n *\n * Example: `/foo/bar~baz` -> `~1foo~1bar~0baz`\n */\nexport const escapeJsonPointer = (str: string): string => str.replace(/~/g, '~0').replace(/\\//g, '~1')\n"],
|
|
5
|
+
"mappings": "AAKO,MAAM,oBAAoB,CAAC,QAAwB,IAAI,QAAQ,MAAM,IAAI,EAAE,QAAQ,OAAO,IAAI;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-json-pointer-segments.d.ts","sourceRoot":"","sources":["../../src/json/parse-json-pointer-segments.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,eAAO,MAAM,wBAAwB,GAAI,MAAM,MAAM,KAAG,MAAM,EAOjC,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/json/parse-json-pointer-segments.ts"],
|
|
4
|
+
"sourcesContent": ["import { unescapeJsonPointer } from './unescape-json-pointer'\n\n/**\n * Translate `/paths/~1test` to `['paths', '/test']`\n */\nexport const parseJsonPointerSegments = (path: string): string[] =>\n path\n // ['', 'paths', '~1test']\n .split('/')\n // ['paths', '~1test']\n .slice(1)\n // ['paths', '/test']\n .map(unescapeJsonPointer)\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,2BAA2B;AAK7B,MAAM,2BAA2B,CAAC,SACvC,KAEG,MAAM,GAAG,EAET,MAAM,CAAC,EAEP,IAAI,mBAAmB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unescape-json-pointer.d.ts","sourceRoot":"","sources":["../../src/json/unescape-json-pointer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,MAAM,KAAG,MAAgE,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/json/unescape-json-pointer.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Unescape JSON pointer\n *\n * Examples:\n * /foo~1bar~0baz -> /foo/bar~baz\n */\nexport const unescapeJsonPointer = (uri: string): string => decodeURI(uri.replace(/~1/g, '/').replace(/~0/g, '~'))\n"],
|
|
5
|
+
"mappings": "AAMO,MAAM,sBAAsB,CAAC,QAAwB,UAAU,IAAI,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieves a nested value from the source document using a path array
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* getValueByPath(document, ['components', 'schemas', 'User'])
|
|
7
|
+
*
|
|
8
|
+
* { id: '123', name: 'John Doe' }
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export declare function getValueAtPath<R = unknown>(obj: any, pointer: string[]): R;
|
|
12
|
+
//# sourceMappingURL=get-value-at-path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-value-at-path.d.ts","sourceRoot":"","sources":["../../src/object/get-value-at-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,CAO1E"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
function getValueAtPath(obj, pointer) {
|
|
2
|
+
return pointer.reduce((acc, part) => {
|
|
3
|
+
if (acc === void 0 || acc === null) {
|
|
4
|
+
return void 0;
|
|
5
|
+
}
|
|
6
|
+
return acc[part];
|
|
7
|
+
}, obj);
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
getValueAtPath
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=get-value-at-path.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/object/get-value-at-path.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Retrieves a nested value from the source document using a path array\n *\n * @example\n * ```ts\n * getValueByPath(document, ['components', 'schemas', 'User'])\n *\n * { id: '123', name: 'John Doe' }\n * ```\n */\nexport function getValueAtPath<R = unknown>(obj: any, pointer: string[]): R {\n return pointer.reduce((acc, part) => {\n if (acc === undefined || acc === null) {\n return undefined\n }\n return acc[part]\n }, obj)\n}\n"],
|
|
5
|
+
"mappings": "AAUO,SAAS,eAA4B,KAAU,SAAsB;AAC1E,SAAO,QAAQ,OAAO,CAAC,KAAK,SAAS;AACnC,QAAI,QAAQ,UAAa,QAAQ,MAAM;AACrC,aAAO;AAAA,IACT;AACA,WAAO,IAAI,IAAI;AAAA,EACjB,GAAG,GAAG;AACR;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
type RemoveCircularOptions = {
|
|
2
|
+
/** Prefix to add before the path in $ref values */
|
|
3
|
+
prefix?: string;
|
|
4
|
+
/** Cache of already processed objects */
|
|
5
|
+
cache?: WeakMap<object, string>;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Traverses an object or array, returning a deep copy in which circular references are replaced
|
|
9
|
+
* by JSON Reference objects of the form: `{ $ref: "#/path/to/original" }`.
|
|
10
|
+
* This allows safe serialization of objects with cycles, following the JSON Reference convention (RFC 6901).
|
|
11
|
+
* An optional `prefix` for the `$ref` path can be provided via options.
|
|
12
|
+
*
|
|
13
|
+
* @param obj - The input object or array to process
|
|
14
|
+
* @param options - Optional configuration; you can set a prefix for $ref pointers
|
|
15
|
+
* @returns A new object or array, with all circular references replaced by $ref pointers
|
|
16
|
+
*/
|
|
17
|
+
export declare const toJsonCompatible: <T>(obj: T, options?: RemoveCircularOptions) => T;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=to-json-compatible.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"to-json-compatible.d.ts","sourceRoot":"","sources":["../../src/object/to-json-compatible.ts"],"names":[],"mappings":"AAGA,KAAK,qBAAqB,GAAG;IAC3B,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,yCAAyC;IACzC,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAChC,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,EAAE,KAAK,CAAC,EAAE,UAAS,qBAA0B,KAAG,CAqFjF,CAAA"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { escapeJsonPointer } from "../json/escape-json-pointer.js";
|
|
2
|
+
import { Queue } from "../queue/queue.js";
|
|
3
|
+
const toJsonCompatible = (obj, options = {}) => {
|
|
4
|
+
const { prefix = "", cache = /* @__PURE__ */ new WeakMap() } = options;
|
|
5
|
+
const toRef = (path) => ({ $ref: `#${path ?? ""}` });
|
|
6
|
+
if (typeof obj !== "object" || obj === null) {
|
|
7
|
+
return obj;
|
|
8
|
+
}
|
|
9
|
+
const rootPath = prefix;
|
|
10
|
+
cache.set(obj, rootPath);
|
|
11
|
+
const rootResult = Array.isArray(obj) ? new Array(obj.length) : {};
|
|
12
|
+
const queue = new Queue();
|
|
13
|
+
queue.enqueue({ node: obj, result: rootResult, path: rootPath });
|
|
14
|
+
while (!queue.isEmpty()) {
|
|
15
|
+
const frame = queue.dequeue();
|
|
16
|
+
if (!frame) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const { node, result, path } = frame;
|
|
20
|
+
if (Array.isArray(node)) {
|
|
21
|
+
const input = node;
|
|
22
|
+
const out2 = result;
|
|
23
|
+
for (let index = 0; index < input.length; index++) {
|
|
24
|
+
if (!(index in input)) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const item = input[index];
|
|
28
|
+
const itemPath = `${path}/${index}`;
|
|
29
|
+
if (typeof item !== "object" || item === null) {
|
|
30
|
+
out2[index] = item;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const existingPath = cache.get(item);
|
|
34
|
+
if (existingPath !== void 0) {
|
|
35
|
+
out2[index] = toRef(existingPath);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
cache.set(item, itemPath);
|
|
39
|
+
const childResult = Array.isArray(item) ? new Array(item.length) : {};
|
|
40
|
+
out2[index] = childResult;
|
|
41
|
+
queue.enqueue({ node: item, result: childResult, path: itemPath });
|
|
42
|
+
}
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const out = result;
|
|
46
|
+
for (const [key, value] of Object.entries(node)) {
|
|
47
|
+
const valuePath = `${path}/${escapeJsonPointer(key)}`;
|
|
48
|
+
if (typeof value !== "object" || value === null) {
|
|
49
|
+
out[key] = value;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const existingPath = cache.get(value);
|
|
53
|
+
if (existingPath !== void 0) {
|
|
54
|
+
out[key] = toRef(existingPath);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
cache.set(value, valuePath);
|
|
58
|
+
const childResult = Array.isArray(value) ? new Array(value.length) : {};
|
|
59
|
+
out[key] = childResult;
|
|
60
|
+
queue.enqueue({ node: value, result: childResult, path: valuePath });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return rootResult;
|
|
64
|
+
};
|
|
65
|
+
export {
|
|
66
|
+
toJsonCompatible
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=to-json-compatible.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/object/to-json-compatible.ts"],
|
|
4
|
+
"sourcesContent": ["import { escapeJsonPointer } from '@/json/escape-json-pointer'\nimport { Queue } from '@/queue/queue'\n\ntype RemoveCircularOptions = {\n /** Prefix to add before the path in $ref values */\n prefix?: string\n /** Cache of already processed objects */\n cache?: WeakMap<object, string>\n}\n\n/**\n * Traverses an object or array, returning a deep copy in which circular references are replaced\n * by JSON Reference objects of the form: `{ $ref: \"#/path/to/original\" }`.\n * This allows safe serialization of objects with cycles, following the JSON Reference convention (RFC 6901).\n * An optional `prefix` for the `$ref` path can be provided via options.\n *\n * @param obj - The input object or array to process\n * @param options - Optional configuration; you can set a prefix for $ref pointers\n * @returns A new object or array, with all circular references replaced by $ref pointers\n */\nexport const toJsonCompatible = <T>(obj: T, options: RemoveCircularOptions = {}): T => {\n const { prefix = '', cache = new WeakMap<object, string>() } = options\n\n const toRef = (path: string | undefined) => ({ $ref: `#${path ?? ''}` })\n\n // Primitives and null are returned as-is\n if (typeof obj !== 'object' || obj === null) {\n return obj\n }\n\n const rootPath = prefix\n cache.set(obj as object, rootPath)\n\n const rootResult: unknown = Array.isArray(obj) ? new Array((obj as unknown[]).length) : {}\n\n const queue = new Queue<{ node: object; result: unknown; path: string }>()\n queue.enqueue({ node: obj as object, result: rootResult, path: rootPath })\n\n while (!queue.isEmpty()) {\n const frame = queue.dequeue()\n if (!frame) {\n continue\n }\n\n const { node, result, path } = frame\n\n // Handle arrays (preserve sparse arrays like Array#map does)\n if (Array.isArray(node)) {\n const input = node as unknown[]\n const out = result as unknown[]\n\n for (let index = 0; index < input.length; index++) {\n if (!(index in input)) {\n continue\n }\n\n const item = input[index]\n const itemPath = `${path}/${index}`\n\n if (typeof item !== 'object' || item === null) {\n out[index] = item\n continue\n }\n\n const existingPath = cache.get(item as object)\n if (existingPath !== undefined) {\n out[index] = toRef(existingPath)\n continue\n }\n\n cache.set(item as object, itemPath)\n\n const childResult: unknown = Array.isArray(item) ? new Array((item as unknown[]).length) : {}\n out[index] = childResult\n queue.enqueue({ node: item as object, result: childResult, path: itemPath })\n }\n\n continue\n }\n\n // Handle objects - create a new object with processed values\n const out = result as Record<string, unknown>\n for (const [key, value] of Object.entries(node)) {\n const valuePath = `${path}/${escapeJsonPointer(key)}`\n\n if (typeof value !== 'object' || value === null) {\n out[key] = value\n continue\n }\n\n const existingPath = cache.get(value as object)\n if (existingPath !== undefined) {\n out[key] = toRef(existingPath)\n continue\n }\n\n cache.set(value as object, valuePath)\n\n const childResult: unknown = Array.isArray(value) ? new Array((value as unknown[]).length) : {}\n out[key] = childResult\n queue.enqueue({ node: value as object, result: childResult, path: valuePath })\n }\n }\n\n return rootResult as T\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,yBAAyB;AAClC,SAAS,aAAa;AAmBf,MAAM,mBAAmB,CAAI,KAAQ,UAAiC,CAAC,MAAS;AACrF,QAAM,EAAE,SAAS,IAAI,QAAQ,oBAAI,QAAwB,EAAE,IAAI;AAE/D,QAAM,QAAQ,CAAC,UAA8B,EAAE,MAAM,IAAI,QAAQ,EAAE,GAAG;AAGtE,MAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;AAC3C,WAAO;AAAA,EACT;AAEA,QAAM,WAAW;AACjB,QAAM,IAAI,KAAe,QAAQ;AAEjC,QAAM,aAAsB,MAAM,QAAQ,GAAG,IAAI,IAAI,MAAO,IAAkB,MAAM,IAAI,CAAC;AAEzF,QAAM,QAAQ,IAAI,MAAuD;AACzE,QAAM,QAAQ,EAAE,MAAM,KAAe,QAAQ,YAAY,MAAM,SAAS,CAAC;AAEzE,SAAO,CAAC,MAAM,QAAQ,GAAG;AACvB,UAAM,QAAQ,MAAM,QAAQ;AAC5B,QAAI,CAAC,OAAO;AACV;AAAA,IACF;AAEA,UAAM,EAAE,MAAM,QAAQ,KAAK,IAAI;AAG/B,QAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,YAAM,QAAQ;AACd,YAAMA,OAAM;AAEZ,eAAS,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS;AACjD,YAAI,EAAE,SAAS,QAAQ;AACrB;AAAA,QACF;AAEA,cAAM,OAAO,MAAM,KAAK;AACxB,cAAM,WAAW,GAAG,IAAI,IAAI,KAAK;AAEjC,YAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC7C,UAAAA,KAAI,KAAK,IAAI;AACb;AAAA,QACF;AAEA,cAAM,eAAe,MAAM,IAAI,IAAc;AAC7C,YAAI,iBAAiB,QAAW;AAC9B,UAAAA,KAAI,KAAK,IAAI,MAAM,YAAY;AAC/B;AAAA,QACF;AAEA,cAAM,IAAI,MAAgB,QAAQ;AAElC,cAAM,cAAuB,MAAM,QAAQ,IAAI,IAAI,IAAI,MAAO,KAAmB,MAAM,IAAI,CAAC;AAC5F,QAAAA,KAAI,KAAK,IAAI;AACb,cAAM,QAAQ,EAAE,MAAM,MAAgB,QAAQ,aAAa,MAAM,SAAS,CAAC;AAAA,MAC7E;AAEA;AAAA,IACF;AAGA,UAAM,MAAM;AACZ,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,YAAM,YAAY,GAAG,IAAI,IAAI,kBAAkB,GAAG,CAAC;AAEnD,UAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,YAAI,GAAG,IAAI;AACX;AAAA,MACF;AAEA,YAAM,eAAe,MAAM,IAAI,KAAe;AAC9C,UAAI,iBAAiB,QAAW;AAC9B,YAAI,GAAG,IAAI,MAAM,YAAY;AAC7B;AAAA,MACF;AAEA,YAAM,IAAI,OAAiB,SAAS;AAEpC,YAAM,cAAuB,MAAM,QAAQ,KAAK,IAAI,IAAI,MAAO,MAAoB,MAAM,IAAI,CAAC;AAC9F,UAAI,GAAG,IAAI;AACX,YAAM,QAAQ,EAAE,MAAM,OAAiB,QAAQ,aAAa,MAAM,UAAU,CAAC;AAAA,IAC/E;AAAA,EACF;AAEA,SAAO;AACT;",
|
|
6
|
+
"names": ["out"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a node in a singly linked list structure, used internally by the Queue.
|
|
3
|
+
*
|
|
4
|
+
* Example:
|
|
5
|
+
* const node = new Node<number>(42);
|
|
6
|
+
* console.log(node.data); // 42
|
|
7
|
+
* console.log(node.next); // null
|
|
8
|
+
*/
|
|
9
|
+
export declare class Node<T> {
|
|
10
|
+
data: T;
|
|
11
|
+
next: Node<T> | null;
|
|
12
|
+
constructor(data: T);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A generic queue implementation using a singly linked list.
|
|
16
|
+
*
|
|
17
|
+
* Example usage:
|
|
18
|
+
*
|
|
19
|
+
* const q = new Queue<number>();
|
|
20
|
+
* q.enqueue(1);
|
|
21
|
+
* q.enqueue(2);
|
|
22
|
+
* q.enqueue(3);
|
|
23
|
+
* console.log(q.dequeue()); // 1
|
|
24
|
+
* console.log(q.peek()); // 2
|
|
25
|
+
* console.log(q.getSize()); // 2
|
|
26
|
+
* console.log(q.toString()); // "2 -> 3"
|
|
27
|
+
* console.log(q.isEmpty()); // false
|
|
28
|
+
*/
|
|
29
|
+
export declare class Queue<T> {
|
|
30
|
+
front: Node<T> | null;
|
|
31
|
+
rear: Node<T> | null;
|
|
32
|
+
size: number;
|
|
33
|
+
constructor();
|
|
34
|
+
/**
|
|
35
|
+
* Adds an element to the end of the queue.
|
|
36
|
+
* @param data - The data to add to the queue.
|
|
37
|
+
*/
|
|
38
|
+
enqueue(data: T): void;
|
|
39
|
+
/**
|
|
40
|
+
* Removes and returns the front element of the queue.
|
|
41
|
+
* @returns The data from the removed node, or null if the queue is empty.
|
|
42
|
+
*/
|
|
43
|
+
dequeue(): T | null;
|
|
44
|
+
/**
|
|
45
|
+
* Returns the front element of the queue without removing it.
|
|
46
|
+
* @returns The front data, or null if the queue is empty.
|
|
47
|
+
*/
|
|
48
|
+
peek(): T | null;
|
|
49
|
+
/**
|
|
50
|
+
* Checks whether the queue is empty.
|
|
51
|
+
* @returns True if the queue has no elements, false otherwise.
|
|
52
|
+
*/
|
|
53
|
+
isEmpty(): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Returns the number of elements in the queue.
|
|
56
|
+
* @returns The size of the queue.
|
|
57
|
+
*/
|
|
58
|
+
getSize(): number;
|
|
59
|
+
/**
|
|
60
|
+
* Returns a string representation of the queue.
|
|
61
|
+
* @returns Elements of the queue separated by ' -> '.
|
|
62
|
+
*/
|
|
63
|
+
toString(): string;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/queue/queue.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,qBAAa,IAAI,CAAC,CAAC;IACjB,IAAI,EAAE,CAAC,CAAA;IACP,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;gBACR,IAAI,EAAE,CAAC;CAIpB;AAED;;;;;;;;;;;;;;GAcG;AAEH,qBAAa,KAAK,CAAC,CAAC;IAClB,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;IACrB,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;;IAQZ;;;OAGG;IACH,OAAO,CAAC,IAAI,EAAE,CAAC;IAYf;;;OAGG;IACH,OAAO;IAaP;;;OAGG;IACH,IAAI;IAOJ;;;OAGG;IACH,OAAO;IAIP;;;OAGG;IACH,OAAO;IAIP;;;OAGG;IACH,QAAQ;CAST"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
class Node {
|
|
2
|
+
data;
|
|
3
|
+
next;
|
|
4
|
+
constructor(data) {
|
|
5
|
+
this.data = data;
|
|
6
|
+
this.next = null;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
class Queue {
|
|
10
|
+
front;
|
|
11
|
+
rear;
|
|
12
|
+
size;
|
|
13
|
+
constructor() {
|
|
14
|
+
this.front = null;
|
|
15
|
+
this.rear = null;
|
|
16
|
+
this.size = 0;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Adds an element to the end of the queue.
|
|
20
|
+
* @param data - The data to add to the queue.
|
|
21
|
+
*/
|
|
22
|
+
enqueue(data) {
|
|
23
|
+
const newNode = new Node(data);
|
|
24
|
+
if (this.isEmpty() || !this.rear) {
|
|
25
|
+
this.front = newNode;
|
|
26
|
+
this.rear = newNode;
|
|
27
|
+
} else {
|
|
28
|
+
this.rear.next = newNode;
|
|
29
|
+
this.rear = newNode;
|
|
30
|
+
}
|
|
31
|
+
this.size++;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Removes and returns the front element of the queue.
|
|
35
|
+
* @returns The data from the removed node, or null if the queue is empty.
|
|
36
|
+
*/
|
|
37
|
+
dequeue() {
|
|
38
|
+
if (this.isEmpty() || !this.front) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const removedNode = this.front;
|
|
42
|
+
this.front = this.front.next;
|
|
43
|
+
if (this.front === null) {
|
|
44
|
+
this.rear = null;
|
|
45
|
+
}
|
|
46
|
+
this.size--;
|
|
47
|
+
return removedNode.data;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Returns the front element of the queue without removing it.
|
|
51
|
+
* @returns The front data, or null if the queue is empty.
|
|
52
|
+
*/
|
|
53
|
+
peek() {
|
|
54
|
+
if (this.isEmpty() || !this.front) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return this.front.data;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Checks whether the queue is empty.
|
|
61
|
+
* @returns True if the queue has no elements, false otherwise.
|
|
62
|
+
*/
|
|
63
|
+
isEmpty() {
|
|
64
|
+
return this.size === 0;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Returns the number of elements in the queue.
|
|
68
|
+
* @returns The size of the queue.
|
|
69
|
+
*/
|
|
70
|
+
getSize() {
|
|
71
|
+
return this.size;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Returns a string representation of the queue.
|
|
75
|
+
* @returns Elements of the queue separated by ' -> '.
|
|
76
|
+
*/
|
|
77
|
+
toString() {
|
|
78
|
+
let current = this.front;
|
|
79
|
+
const elements = [];
|
|
80
|
+
while (current) {
|
|
81
|
+
elements.push(current.data);
|
|
82
|
+
current = current.next;
|
|
83
|
+
}
|
|
84
|
+
return elements.join(" -> ");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export {
|
|
88
|
+
Node,
|
|
89
|
+
Queue
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=queue.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/queue/queue.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Represents a node in a singly linked list structure, used internally by the Queue.\n *\n * Example:\n * const node = new Node<number>(42);\n * console.log(node.data); // 42\n * console.log(node.next); // null\n */\nexport class Node<T> {\n data: T\n next: Node<T> | null\n constructor(data: T) {\n this.data = data\n this.next = null\n }\n}\n\n/**\n * A generic queue implementation using a singly linked list.\n *\n * Example usage:\n *\n * const q = new Queue<number>();\n * q.enqueue(1);\n * q.enqueue(2);\n * q.enqueue(3);\n * console.log(q.dequeue()); // 1\n * console.log(q.peek()); // 2\n * console.log(q.getSize()); // 2\n * console.log(q.toString()); // \"2 -> 3\"\n * console.log(q.isEmpty()); // false\n */\n\nexport class Queue<T> {\n front: Node<T> | null\n rear: Node<T> | null\n size: number\n\n constructor() {\n this.front = null\n this.rear = null\n this.size = 0\n }\n\n /**\n * Adds an element to the end of the queue.\n * @param data - The data to add to the queue.\n */\n enqueue(data: T) {\n const newNode = new Node(data)\n if (this.isEmpty() || !this.rear) {\n this.front = newNode\n this.rear = newNode\n } else {\n this.rear.next = newNode\n this.rear = newNode\n }\n this.size++\n }\n\n /**\n * Removes and returns the front element of the queue.\n * @returns The data from the removed node, or null if the queue is empty.\n */\n dequeue() {\n if (this.isEmpty() || !this.front) {\n return null\n }\n const removedNode = this.front\n this.front = this.front.next\n if (this.front === null) {\n this.rear = null\n }\n this.size--\n return removedNode.data\n }\n\n /**\n * Returns the front element of the queue without removing it.\n * @returns The front data, or null if the queue is empty.\n */\n peek() {\n if (this.isEmpty() || !this.front) {\n return null\n }\n return this.front.data\n }\n\n /**\n * Checks whether the queue is empty.\n * @returns True if the queue has no elements, false otherwise.\n */\n isEmpty() {\n return this.size === 0\n }\n\n /**\n * Returns the number of elements in the queue.\n * @returns The size of the queue.\n */\n getSize() {\n return this.size\n }\n\n /**\n * Returns a string representation of the queue.\n * @returns Elements of the queue separated by ' -> '.\n */\n toString() {\n let current = this.front\n const elements = []\n while (current) {\n elements.push(current.data)\n current = current.next\n }\n return elements.join(' -> ')\n }\n}\n"],
|
|
5
|
+
"mappings": "AAQO,MAAM,KAAQ;AAAA,EACnB;AAAA,EACA;AAAA,EACA,YAAY,MAAS;AACnB,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AACF;AAkBO,MAAM,MAAS;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EAEA,cAAc;AACZ,SAAK,QAAQ;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQ,MAAS;AACf,UAAM,UAAU,IAAI,KAAK,IAAI;AAC7B,QAAI,KAAK,QAAQ,KAAK,CAAC,KAAK,MAAM;AAChC,WAAK,QAAQ;AACb,WAAK,OAAO;AAAA,IACd,OAAO;AACL,WAAK,KAAK,OAAO;AACjB,WAAK,OAAO;AAAA,IACd;AACA,SAAK;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU;AACR,QAAI,KAAK,QAAQ,KAAK,CAAC,KAAK,OAAO;AACjC,aAAO;AAAA,IACT;AACA,UAAM,cAAc,KAAK;AACzB,SAAK,QAAQ,KAAK,MAAM;AACxB,QAAI,KAAK,UAAU,MAAM;AACvB,WAAK,OAAO;AAAA,IACd;AACA,SAAK;AACL,WAAO,YAAY;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO;AACL,QAAI,KAAK,QAAQ,KAAK,CAAC,KAAK,OAAO;AACjC,aAAO;AAAA,IACT;AACA,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU;AACR,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU;AACR,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,WAAW;AACT,QAAI,UAAU,KAAK;AACnB,UAAM,WAAW,CAAC;AAClB,WAAO,SAAS;AACd,eAAS,KAAK,QAAQ,IAAI;AAC1B,gBAAU,QAAQ;AAAA,IACpB;AACA,WAAO,SAAS,KAAK,MAAM;AAAA,EAC7B;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"helpers",
|
|
15
15
|
"js"
|
|
16
16
|
],
|
|
17
|
-
"version": "0.2.
|
|
17
|
+
"version": "0.2.18",
|
|
18
18
|
"engines": {
|
|
19
19
|
"node": ">=20"
|
|
20
20
|
},
|
|
@@ -57,6 +57,11 @@
|
|
|
57
57
|
"types": "./dist/http/*.d.ts",
|
|
58
58
|
"default": "./dist/http/*.js"
|
|
59
59
|
},
|
|
60
|
+
"./json/*": {
|
|
61
|
+
"import": "./dist/json/*.js",
|
|
62
|
+
"types": "./dist/json/*.d.ts",
|
|
63
|
+
"default": "./dist/json/*.js"
|
|
64
|
+
},
|
|
60
65
|
"./node/*": {
|
|
61
66
|
"import": "./dist/node/*.js",
|
|
62
67
|
"types": "./dist/node/*.d.ts",
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Detects and breaks circular JavaScript object references in an OpenAPI document tree.
|
|
3
|
-
*
|
|
4
|
-
* The legacy API client (via openapi-parser's dereference) resolved all $refs inline:
|
|
5
|
-
* it deleted the $ref key and copied every property from the resolved target directly
|
|
6
|
-
* onto the object. For self-referencing or mutually-referencing schemas this created
|
|
7
|
-
* circular JS object graphs that cannot be serialized to JSON or validated by TypeBox.
|
|
8
|
-
*
|
|
9
|
-
* This function walks the object tree depth-first, detects cycles via reference identity,
|
|
10
|
-
* extracts each circular component into the appropriate components section (schemas,
|
|
11
|
-
* responses, parameters, etc.) with a generated name, and replaces ALL occurrences
|
|
12
|
-
* (both first and back-references) with **only** `$ref` and any extra properties
|
|
13
|
-
* (no extra schema properties) so that TypeBox's Value.Cast picks the reference branch
|
|
14
|
-
* of the schemaOrReference union.
|
|
15
|
-
*
|
|
16
|
-
* Returns a new (deep-cloned) document with all cycles replaced. When no circular
|
|
17
|
-
* references exist, the output is structurally identical to the input.
|
|
18
|
-
*
|
|
19
|
-
* @param document - The OpenAPI document to process
|
|
20
|
-
* @param extraProps - Extra properties to add as siblings to the $ref (e.g., '$ref-value')
|
|
21
|
-
* @returns A new document with circular references replaced by $ref pointers
|
|
22
|
-
*/
|
|
23
|
-
export declare const circularToRefs: (document: Record<string, unknown>, extraProps?: Record<string, unknown>) => Record<string, unknown>;
|
|
24
|
-
//# sourceMappingURL=circular-to-refs.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"circular-to-refs.d.ts","sourceRoot":"","sources":["../../src/object/circular-to-refs.ts"],"names":[],"mappings":"AAyFA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,aAAY,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,KACvC,MAAM,CAAC,MAAM,EAAE,OAAO,CAwWxB,CAAA"}
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
const COMPONENT_PREFIXES = {
|
|
2
|
-
schemas: "CircularSchema",
|
|
3
|
-
responses: "CircularResponse",
|
|
4
|
-
parameters: "CircularParameter",
|
|
5
|
-
examples: "CircularExample",
|
|
6
|
-
requestBodies: "CircularRequestBody",
|
|
7
|
-
headers: "CircularHeader",
|
|
8
|
-
securitySchemes: "CircularSecurityScheme",
|
|
9
|
-
links: "CircularLink",
|
|
10
|
-
callbacks: "CircularCallback",
|
|
11
|
-
pathItems: "CircularPathItem"
|
|
12
|
-
};
|
|
13
|
-
const COMPONENT_TYPES = Object.keys(COMPONENT_PREFIXES);
|
|
14
|
-
const KEY_TO_CONTEXT = {
|
|
15
|
-
responses: "responses",
|
|
16
|
-
parameters: "parameters",
|
|
17
|
-
requestBody: "requestBodies",
|
|
18
|
-
headers: "headers",
|
|
19
|
-
examples: "examples",
|
|
20
|
-
links: "links",
|
|
21
|
-
callbacks: "callbacks",
|
|
22
|
-
securitySchemes: "securitySchemes",
|
|
23
|
-
schema: "schemas",
|
|
24
|
-
items: "schemas",
|
|
25
|
-
additionalProperties: "schemas",
|
|
26
|
-
allOf: "schemas",
|
|
27
|
-
oneOf: "schemas",
|
|
28
|
-
anyOf: "schemas",
|
|
29
|
-
not: "schemas",
|
|
30
|
-
properties: "schemas",
|
|
31
|
-
patternProperties: "schemas",
|
|
32
|
-
get: "pathItems",
|
|
33
|
-
put: "pathItems",
|
|
34
|
-
post: "pathItems",
|
|
35
|
-
delete: "pathItems",
|
|
36
|
-
options: "pathItems",
|
|
37
|
-
head: "pathItems",
|
|
38
|
-
patch: "pathItems",
|
|
39
|
-
trace: "pathItems"
|
|
40
|
-
};
|
|
41
|
-
const NON_REFERENCE_CONTAINER_KEYS = /* @__PURE__ */ new Set(["properties", "patternProperties", "responses"]);
|
|
42
|
-
const getContextForKey = (key) => {
|
|
43
|
-
const context = KEY_TO_CONTEXT[key];
|
|
44
|
-
if (context !== void 0) {
|
|
45
|
-
return context;
|
|
46
|
-
}
|
|
47
|
-
if (key.includes("{$") || key.charCodeAt(0) === 47) {
|
|
48
|
-
return "pathItems";
|
|
49
|
-
}
|
|
50
|
-
return null;
|
|
51
|
-
};
|
|
52
|
-
const circularToRefs = (document, extraProps = {}) => {
|
|
53
|
-
const circularMeta = /* @__PURE__ */ new Map();
|
|
54
|
-
const objectContext = /* @__PURE__ */ new Map();
|
|
55
|
-
const existingComponentNames = /* @__PURE__ */ new Map();
|
|
56
|
-
const nonReferenceContainers = /* @__PURE__ */ new WeakSet();
|
|
57
|
-
const nonReferenceContainerInfo = /* @__PURE__ */ new WeakMap();
|
|
58
|
-
const counters = {
|
|
59
|
-
schemas: 0,
|
|
60
|
-
responses: 0,
|
|
61
|
-
parameters: 0,
|
|
62
|
-
examples: 0,
|
|
63
|
-
requestBodies: 0,
|
|
64
|
-
headers: 0,
|
|
65
|
-
securitySchemes: 0,
|
|
66
|
-
links: 0,
|
|
67
|
-
callbacks: 0,
|
|
68
|
-
pathItems: 0
|
|
69
|
-
};
|
|
70
|
-
const scanExistingComponents = (doc) => {
|
|
71
|
-
const components = doc.components;
|
|
72
|
-
if (!components) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
for (const componentType of COMPONENT_TYPES) {
|
|
76
|
-
const section = components[componentType];
|
|
77
|
-
if (!section) {
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
for (const [name, value] of Object.entries(section)) {
|
|
81
|
-
if (value !== null && typeof value === "object") {
|
|
82
|
-
existingComponentNames.set(value, { type: componentType, name });
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
const identifyCircularObjects = (value, ancestors, context) => {
|
|
88
|
-
if (value === null || typeof value !== "object") {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const obj = value;
|
|
92
|
-
if (ancestors.has(obj)) {
|
|
93
|
-
if (nonReferenceContainers.has(obj)) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
if (!circularMeta.has(obj)) {
|
|
97
|
-
const existing = existingComponentNames.get(obj);
|
|
98
|
-
if (existing) {
|
|
99
|
-
circularMeta.set(obj, existing);
|
|
100
|
-
} else {
|
|
101
|
-
const componentType = objectContext.get(obj) ?? context;
|
|
102
|
-
const count = ++counters[componentType];
|
|
103
|
-
circularMeta.set(obj, {
|
|
104
|
-
type: componentType,
|
|
105
|
-
name: `${COMPONENT_PREFIXES[componentType]}${count}`
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
if (!objectContext.has(obj)) {
|
|
112
|
-
objectContext.set(obj, context);
|
|
113
|
-
}
|
|
114
|
-
ancestors.add(obj);
|
|
115
|
-
if (Array.isArray(obj)) {
|
|
116
|
-
for (const item of obj) {
|
|
117
|
-
identifyCircularObjects(item, ancestors, context);
|
|
118
|
-
}
|
|
119
|
-
} else {
|
|
120
|
-
const record = obj;
|
|
121
|
-
for (const key of Object.keys(record)) {
|
|
122
|
-
const child = record[key];
|
|
123
|
-
if (NON_REFERENCE_CONTAINER_KEYS.has(key) && child !== null && typeof child === "object" && !Array.isArray(child)) {
|
|
124
|
-
nonReferenceContainers.add(child);
|
|
125
|
-
nonReferenceContainerInfo.set(child, {
|
|
126
|
-
key,
|
|
127
|
-
owner: obj
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
const newContext = getContextForKey(key) ?? context;
|
|
131
|
-
identifyCircularObjects(child, ancestors, newContext);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
ancestors.delete(obj);
|
|
135
|
-
};
|
|
136
|
-
const extractedComponents = /* @__PURE__ */ new Map();
|
|
137
|
-
const hasExtraProps = Object.keys(extraProps).length > 0;
|
|
138
|
-
const createRefObject = (meta) => {
|
|
139
|
-
const ref = { $ref: `#/components/${meta.type}/${meta.name}` };
|
|
140
|
-
return hasExtraProps ? { ...ref, ...extraProps } : ref;
|
|
141
|
-
};
|
|
142
|
-
const getOrCreateExtractedSection = (type) => {
|
|
143
|
-
const existing = extractedComponents.get(type);
|
|
144
|
-
if (existing !== void 0) {
|
|
145
|
-
return existing;
|
|
146
|
-
}
|
|
147
|
-
const section = /* @__PURE__ */ new Map();
|
|
148
|
-
extractedComponents.set(type, section);
|
|
149
|
-
return section;
|
|
150
|
-
};
|
|
151
|
-
const ensureMeta = (obj, fallbackType) => {
|
|
152
|
-
const existingMeta = circularMeta.get(obj);
|
|
153
|
-
if (existingMeta !== void 0) {
|
|
154
|
-
return existingMeta;
|
|
155
|
-
}
|
|
156
|
-
const existingName = existingComponentNames.get(obj);
|
|
157
|
-
if (existingName !== void 0) {
|
|
158
|
-
circularMeta.set(obj, existingName);
|
|
159
|
-
return existingName;
|
|
160
|
-
}
|
|
161
|
-
const count = ++counters[fallbackType];
|
|
162
|
-
const meta = {
|
|
163
|
-
type: fallbackType,
|
|
164
|
-
name: `${COMPONENT_PREFIXES[fallbackType]}${count}`
|
|
165
|
-
};
|
|
166
|
-
circularMeta.set(obj, meta);
|
|
167
|
-
return meta;
|
|
168
|
-
};
|
|
169
|
-
const createLiftedRefForContainer = (container, clonedContainer) => {
|
|
170
|
-
const info = nonReferenceContainerInfo.get(container);
|
|
171
|
-
if (info === void 0) {
|
|
172
|
-
return void 0;
|
|
173
|
-
}
|
|
174
|
-
if (info.key === "properties" || info.key === "patternProperties") {
|
|
175
|
-
const meta = ensureMeta(info.owner, "schemas");
|
|
176
|
-
return createRefObject(meta);
|
|
177
|
-
}
|
|
178
|
-
const responses = container;
|
|
179
|
-
for (const [key, value] of Object.entries(responses)) {
|
|
180
|
-
if (value === null || typeof value !== "object" || value === container) {
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
const responseObject = value;
|
|
184
|
-
const meta = ensureMeta(responseObject, "responses");
|
|
185
|
-
if (!existingComponentNames.has(responseObject)) {
|
|
186
|
-
const section = getOrCreateExtractedSection(meta.type);
|
|
187
|
-
if (!section.has(meta.name)) {
|
|
188
|
-
const clonedValue = clonedContainer?.[key];
|
|
189
|
-
if (clonedValue !== void 0) {
|
|
190
|
-
section.set(meta.name, clonedValue);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return createRefObject(meta);
|
|
195
|
-
}
|
|
196
|
-
return void 0;
|
|
197
|
-
};
|
|
198
|
-
const cloneNode = (obj, visited, context) => {
|
|
199
|
-
return Array.isArray(obj) ? cloneArray(obj, visited, context) : cloneObject(obj, visited, context);
|
|
200
|
-
};
|
|
201
|
-
const cloneNodeWithVisitTracking = (obj, visited, context) => {
|
|
202
|
-
visited.add(obj);
|
|
203
|
-
const cloned = cloneNode(obj, visited, context);
|
|
204
|
-
visited.delete(obj);
|
|
205
|
-
return cloned;
|
|
206
|
-
};
|
|
207
|
-
const cloneWithRefs = (value, visited, context) => {
|
|
208
|
-
if (value === null || typeof value !== "object") {
|
|
209
|
-
return value;
|
|
210
|
-
}
|
|
211
|
-
const obj = value;
|
|
212
|
-
const meta = circularMeta.get(obj);
|
|
213
|
-
if (meta !== void 0) {
|
|
214
|
-
if (visited.has(obj)) {
|
|
215
|
-
return createRefObject(meta);
|
|
216
|
-
}
|
|
217
|
-
const cloned2 = cloneNodeWithVisitTracking(obj, visited, context);
|
|
218
|
-
if (existingComponentNames.has(obj)) {
|
|
219
|
-
return cloned2;
|
|
220
|
-
}
|
|
221
|
-
const section2 = getOrCreateExtractedSection(meta.type);
|
|
222
|
-
if (!section2.has(meta.name)) {
|
|
223
|
-
section2.set(meta.name, cloned2);
|
|
224
|
-
}
|
|
225
|
-
return createRefObject(meta);
|
|
226
|
-
}
|
|
227
|
-
if (visited.has(obj)) {
|
|
228
|
-
return void 0;
|
|
229
|
-
}
|
|
230
|
-
const cloned = cloneNodeWithVisitTracking(obj, visited, context);
|
|
231
|
-
const lateMeta = circularMeta.get(obj);
|
|
232
|
-
if (lateMeta === void 0 || existingComponentNames.has(obj)) {
|
|
233
|
-
return cloned;
|
|
234
|
-
}
|
|
235
|
-
const section = getOrCreateExtractedSection(lateMeta.type);
|
|
236
|
-
if (!section.has(lateMeta.name)) {
|
|
237
|
-
section.set(lateMeta.name, cloned);
|
|
238
|
-
}
|
|
239
|
-
return createRefObject(lateMeta);
|
|
240
|
-
};
|
|
241
|
-
const cloneArray = (arr, visited, context) => {
|
|
242
|
-
const result2 = [];
|
|
243
|
-
for (const item of arr) {
|
|
244
|
-
const clonedItem = cloneWithRefs(item, visited, context);
|
|
245
|
-
if (clonedItem !== void 0) {
|
|
246
|
-
result2.push(clonedItem);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
return result2;
|
|
250
|
-
};
|
|
251
|
-
const cloneObject = (obj, visited, context) => {
|
|
252
|
-
const record = obj;
|
|
253
|
-
const result2 = {};
|
|
254
|
-
for (const key of Object.keys(record)) {
|
|
255
|
-
const newContext = getContextForKey(key) ?? context;
|
|
256
|
-
const value = record[key];
|
|
257
|
-
const clonedValue = cloneWithRefs(value, visited, newContext);
|
|
258
|
-
if (clonedValue !== void 0) {
|
|
259
|
-
result2[key] = clonedValue;
|
|
260
|
-
continue;
|
|
261
|
-
}
|
|
262
|
-
if (value === obj && nonReferenceContainers.has(obj)) {
|
|
263
|
-
const liftedRef = createLiftedRefForContainer(obj, result2);
|
|
264
|
-
if (liftedRef !== void 0) {
|
|
265
|
-
result2[key] = liftedRef;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
return result2;
|
|
270
|
-
};
|
|
271
|
-
scanExistingComponents(document);
|
|
272
|
-
identifyCircularObjects(document, /* @__PURE__ */ new Set(), "schemas");
|
|
273
|
-
const result = cloneWithRefs(document, /* @__PURE__ */ new Set(), "schemas");
|
|
274
|
-
if (extractedComponents.size > 0) {
|
|
275
|
-
const components = result.components ?? {};
|
|
276
|
-
for (const [componentType, items] of extractedComponents) {
|
|
277
|
-
const section = components[componentType] ?? {};
|
|
278
|
-
for (const [name, component] of items) {
|
|
279
|
-
section[name] = component;
|
|
280
|
-
}
|
|
281
|
-
components[componentType] = section;
|
|
282
|
-
}
|
|
283
|
-
result.components = components;
|
|
284
|
-
}
|
|
285
|
-
return result;
|
|
286
|
-
};
|
|
287
|
-
export {
|
|
288
|
-
circularToRefs
|
|
289
|
-
};
|
|
290
|
-
//# sourceMappingURL=circular-to-refs.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/object/circular-to-refs.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * OpenAPI component types that can contain circular references.\n * Each type maps to a section in `#/components/{type}`.\n */\nconst COMPONENT_PREFIXES = {\n schemas: 'CircularSchema',\n responses: 'CircularResponse',\n parameters: 'CircularParameter',\n examples: 'CircularExample',\n requestBodies: 'CircularRequestBody',\n headers: 'CircularHeader',\n securitySchemes: 'CircularSecurityScheme',\n links: 'CircularLink',\n callbacks: 'CircularCallback',\n pathItems: 'CircularPathItem',\n} as const\n\ntype ComponentType = keyof typeof COMPONENT_PREFIXES\nconst COMPONENT_TYPES = Object.keys(COMPONENT_PREFIXES) as ComponentType[]\n\n/**\n * Lookup table for OpenAPI keys that change the component context.\n * Using an object lookup is faster than a switch statement for many keys.\n */\nconst KEY_TO_CONTEXT: Readonly<Record<string, ComponentType>> = {\n responses: 'responses',\n parameters: 'parameters',\n requestBody: 'requestBodies',\n headers: 'headers',\n examples: 'examples',\n links: 'links',\n callbacks: 'callbacks',\n securitySchemes: 'securitySchemes',\n schema: 'schemas',\n items: 'schemas',\n additionalProperties: 'schemas',\n allOf: 'schemas',\n oneOf: 'schemas',\n anyOf: 'schemas',\n not: 'schemas',\n properties: 'schemas',\n patternProperties: 'schemas',\n get: 'pathItems',\n put: 'pathItems',\n post: 'pathItems',\n delete: 'pathItems',\n options: 'pathItems',\n head: 'pathItems',\n patch: 'pathItems',\n trace: 'pathItems',\n}\n\n/**\n * These container objects must remain maps in OpenAPI and cannot be replaced by a Reference Object.\n * When a cycle points to one of these containers, we lift the ref to a legal parent object.\n */\nconst NON_REFERENCE_CONTAINER_KEYS = new Set(['properties', 'patternProperties', 'responses'])\n\n/**\n * Determines the component context for a given OpenAPI key.\n * Returns the appropriate ComponentType or null to inherit parent context.\n */\nconst getContextForKey = (key: string): ComponentType | null => {\n // Fast path: direct lookup for known keys\n const context = KEY_TO_CONTEXT[key]\n if (context !== undefined) {\n return context\n }\n\n // Callback path expressions (keys containing '{$') or path templates have pathItem values\n if (key.includes('{$') || key.charCodeAt(0) === 47 /* '/' */) {\n return 'pathItems'\n }\n\n // No context change \u2014 inherit from parent\n return null\n}\n\n/** Metadata about a circular object: its component type and generated name */\ntype CircularMeta = {\n readonly type: ComponentType\n readonly name: string\n}\n\ntype NonReferenceContainerInfo = {\n readonly key: 'properties' | 'patternProperties' | 'responses'\n readonly owner: object\n}\n\n/**\n * Detects and breaks circular JavaScript object references in an OpenAPI document tree.\n *\n * The legacy API client (via openapi-parser's dereference) resolved all $refs inline:\n * it deleted the $ref key and copied every property from the resolved target directly\n * onto the object. For self-referencing or mutually-referencing schemas this created\n * circular JS object graphs that cannot be serialized to JSON or validated by TypeBox.\n *\n * This function walks the object tree depth-first, detects cycles via reference identity,\n * extracts each circular component into the appropriate components section (schemas,\n * responses, parameters, etc.) with a generated name, and replaces ALL occurrences\n * (both first and back-references) with **only** `$ref` and any extra properties\n * (no extra schema properties) so that TypeBox's Value.Cast picks the reference branch\n * of the schemaOrReference union.\n *\n * Returns a new (deep-cloned) document with all cycles replaced. When no circular\n * references exist, the output is structurally identical to the input.\n *\n * @param document - The OpenAPI document to process\n * @param extraProps - Extra properties to add as siblings to the $ref (e.g., '$ref-value')\n * @returns A new document with circular references replaced by $ref pointers\n */\nexport const circularToRefs = (\n document: Record<string, unknown>,\n extraProps: Record<string, unknown> = {},\n): Record<string, unknown> => {\n /**\n * Phase 1 structures:\n * - circularMeta: Maps circular objects to their component type and generated name\n * - objectContext: Records the context when an object is first encountered\n * - existingComponentNames: Maps objects to their existing component names (if in components section)\n * - counters: Tracks unique naming counters per component type\n */\n const circularMeta = new Map<object, CircularMeta>()\n const objectContext = new Map<object, ComponentType>()\n const existingComponentNames = new Map<object, { type: ComponentType; name: string }>()\n const nonReferenceContainers = new WeakSet<object>()\n const nonReferenceContainerInfo = new WeakMap<object, NonReferenceContainerInfo>()\n const counters: Record<ComponentType, number> = {\n schemas: 0,\n responses: 0,\n parameters: 0,\n examples: 0,\n requestBodies: 0,\n headers: 0,\n securitySchemes: 0,\n links: 0,\n callbacks: 0,\n pathItems: 0,\n }\n\n /**\n * Pre-scan: Map existing components to their names.\n * This allows us to reference existing schemas instead of creating duplicates.\n */\n const scanExistingComponents = (doc: Record<string, unknown>): void => {\n const components = doc.components as Record<string, unknown> | undefined\n if (!components) {\n return\n }\n\n for (const componentType of COMPONENT_TYPES) {\n const section = components[componentType] as Record<string, unknown> | undefined\n if (!section) {\n continue\n }\n\n for (const [name, value] of Object.entries(section)) {\n if (value !== null && typeof value === 'object') {\n existingComponentNames.set(value as object, { type: componentType, name })\n }\n }\n }\n }\n\n /**\n * Phase 1: Identify all circular objects in the tree.\n *\n * We traverse depth-first, tracking ancestors in the current path. When we\n * encounter an object already in the ancestor set, we've found a cycle.\n * We record the circular object with its context and a generated name.\n *\n * @param value - Current value being traversed\n * @param ancestors - Set of objects in the current traversal path\n * @param context - Current OpenAPI component context\n */\n const identifyCircularObjects = (value: unknown, ancestors: Set<object>, context: ComponentType): void => {\n // Primitives and null cannot be circular\n if (value === null || typeof value !== 'object') {\n return\n }\n\n const obj = value as object\n\n // Cycle detected: this object is already in our ancestor chain\n if (ancestors.has(obj)) {\n // Keep container maps as maps; we will lift refs on their entries during cloning.\n if (nonReferenceContainers.has(obj)) {\n return\n }\n\n // Only register once \u2014 use existing name if available, otherwise generate new one\n if (!circularMeta.has(obj)) {\n const existing = existingComponentNames.get(obj)\n if (existing) {\n // Use the existing component name\n circularMeta.set(obj, existing)\n } else {\n // Generate a new name\n const componentType = objectContext.get(obj) ?? context\n const count = ++counters[componentType]\n circularMeta.set(obj, {\n type: componentType,\n name: `${COMPONENT_PREFIXES[componentType]}${count}`,\n })\n }\n }\n return\n }\n\n // Record context on first encounter for accurate categorization\n if (!objectContext.has(obj)) {\n objectContext.set(obj, context)\n }\n\n // Add to ancestor chain and recurse\n ancestors.add(obj)\n\n if (Array.isArray(obj)) {\n for (const item of obj) {\n identifyCircularObjects(item, ancestors, context)\n }\n } else {\n // Objects: check each property, updating context as needed\n const record = obj as Record<string, unknown>\n for (const key of Object.keys(record)) {\n const child = record[key]\n if (\n NON_REFERENCE_CONTAINER_KEYS.has(key) &&\n child !== null &&\n typeof child === 'object' &&\n !Array.isArray(child)\n ) {\n nonReferenceContainers.add(child as object)\n nonReferenceContainerInfo.set(child as object, {\n key: key as NonReferenceContainerInfo['key'],\n owner: obj,\n })\n }\n\n const newContext = getContextForKey(key) ?? context\n identifyCircularObjects(child, ancestors, newContext)\n }\n }\n\n // Remove from ancestor chain when backtracking\n ancestors.delete(obj)\n }\n\n /** Stores processed (cycle-free) definitions by type and name */\n const extractedComponents = new Map<ComponentType, Map<string, unknown>>()\n\n /** Precompute whether we have extra props to avoid repeated Object.keys calls */\n const hasExtraProps = Object.keys(extraProps).length > 0\n\n /** Creates a $ref object pointing to the extracted component */\n const createRefObject = (meta: CircularMeta): Record<string, unknown> => {\n const ref = { $ref: `#/components/${meta.type}/${meta.name}` }\n return hasExtraProps ? { ...ref, ...extraProps } : ref\n }\n\n /** Gets or creates the target section for extracted components. */\n const getOrCreateExtractedSection = (type: ComponentType): Map<string, unknown> => {\n const existing = extractedComponents.get(type)\n if (existing !== undefined) {\n return existing\n }\n\n const section = new Map<string, unknown>()\n extractedComponents.set(type, section)\n return section\n }\n\n /** Ensures an object has component metadata so it can be referenced legally. */\n const ensureMeta = (obj: object, fallbackType: ComponentType): CircularMeta => {\n const existingMeta = circularMeta.get(obj)\n if (existingMeta !== undefined) {\n return existingMeta\n }\n\n const existingName = existingComponentNames.get(obj)\n if (existingName !== undefined) {\n circularMeta.set(obj, existingName)\n return existingName\n }\n\n const count = ++counters[fallbackType]\n const meta: CircularMeta = {\n type: fallbackType,\n name: `${COMPONENT_PREFIXES[fallbackType]}${count}`,\n }\n circularMeta.set(obj, meta)\n return meta\n }\n\n /** Lifts illegal container self-cycles to a legal reference target. */\n const createLiftedRefForContainer = (\n container: object,\n clonedContainer?: Record<string, unknown>,\n ): Record<string, unknown> | undefined => {\n const info = nonReferenceContainerInfo.get(container)\n if (info === undefined) {\n return undefined\n }\n\n if (info.key === 'properties' || info.key === 'patternProperties') {\n const meta = ensureMeta(info.owner, 'schemas')\n return createRefObject(meta)\n }\n\n // responses map values must be Response Object or Reference Object.\n // Lift self-cycle to the first concrete response entry.\n const responses = container as Record<string, unknown>\n for (const [key, value] of Object.entries(responses)) {\n if (value === null || typeof value !== 'object' || value === container) {\n continue\n }\n\n const responseObject = value as object\n const meta = ensureMeta(responseObject, 'responses')\n if (!existingComponentNames.has(responseObject)) {\n const section = getOrCreateExtractedSection(meta.type)\n if (!section.has(meta.name)) {\n const clonedValue = clonedContainer?.[key]\n if (clonedValue !== undefined) {\n section.set(meta.name, clonedValue)\n }\n }\n }\n\n return createRefObject(meta)\n }\n\n return undefined\n }\n\n const cloneNode = (obj: object, visited: Set<object>, context: ComponentType): unknown => {\n return Array.isArray(obj) ? cloneArray(obj, visited, context) : cloneObject(obj, visited, context)\n }\n\n const cloneNodeWithVisitTracking = (obj: object, visited: Set<object>, context: ComponentType): unknown => {\n // Keep visit bookkeeping in one place so all clone paths handle cycles consistently.\n visited.add(obj)\n const cloned = cloneNode(obj, visited, context)\n visited.delete(obj)\n return cloned\n }\n\n /**\n * Phase 2: Create a deep clone with circular references replaced by $refs.\n * For circular objects, we extract to components and return $ref.\n * For non-circular objects, we simply deep clone.\n */\n const cloneWithRefs = (value: unknown, visited: Set<object>, context: ComponentType): unknown => {\n if (value === null || typeof value !== 'object') {\n return value\n }\n\n const obj = value as object\n const meta = circularMeta.get(obj)\n\n // If this is a circular object and we're already visiting it, return a $ref to break the cycle\n if (meta !== undefined) {\n if (visited.has(obj)) {\n return createRefObject(meta)\n }\n\n const cloned = cloneNodeWithVisitTracking(obj, visited, context)\n if (existingComponentNames.has(obj)) {\n // Do not extract \u2014 it already exists in components.\n // Just clone it in place and replace circular back-references with $ref.\n return cloned\n }\n\n const section = getOrCreateExtractedSection(meta.type)\n if (!section.has(meta.name)) {\n // Store the extracted clone once; later occurrences can directly return a $ref.\n section.set(meta.name, cloned)\n }\n\n return createRefObject(meta)\n }\n\n // Non-circular object: track traversal path to handle cycles that are intentionally\n // not extracted (for container maps where we lift refs to legal targets).\n if (visited.has(obj)) {\n return undefined\n }\n\n const cloned = cloneNodeWithVisitTracking(obj, visited, context)\n\n // This object may become referenceable while cloning children (e.g. lifting\n // a container self-cycle to the parent object). If so, extract it now.\n const lateMeta = circularMeta.get(obj)\n if (lateMeta === undefined || existingComponentNames.has(obj)) {\n // Existing components keep their inline structure, but still have back-refs normalized.\n return cloned\n }\n\n const section = getOrCreateExtractedSection(lateMeta.type)\n if (!section.has(lateMeta.name)) {\n section.set(lateMeta.name, cloned)\n }\n\n return createRefObject(lateMeta)\n }\n\n /**\n * Clones an array, recursively processing each element.\n */\n const cloneArray = (arr: unknown[], visited: Set<object>, context: ComponentType): unknown[] => {\n const result: unknown[] = []\n for (const item of arr) {\n const clonedItem = cloneWithRefs(item, visited, context)\n if (clonedItem !== undefined) {\n result.push(clonedItem)\n }\n }\n return result\n }\n\n /**\n * Clones an object, recursively processing each property.\n * Updates context based on property keys.\n */\n const cloneObject = (obj: object, visited: Set<object>, context: ComponentType): Record<string, unknown> => {\n const record = obj as Record<string, unknown>\n const result: Record<string, unknown> = {}\n\n for (const key of Object.keys(record)) {\n const newContext = getContextForKey(key) ?? context\n const value = record[key]\n const clonedValue = cloneWithRefs(value, visited, newContext)\n if (clonedValue !== undefined) {\n result[key] = clonedValue\n continue\n }\n\n // If a value loops back to its parent container map, lift to a legal reference target.\n if (value === obj && nonReferenceContainers.has(obj)) {\n const liftedRef = createLiftedRefForContainer(obj, result)\n if (liftedRef !== undefined) {\n result[key] = liftedRef\n }\n }\n }\n\n return result\n }\n\n // Execute Pre-scan: map existing components\n scanExistingComponents(document)\n\n // Execute Phase 1: identify all circular objects\n identifyCircularObjects(document, new Set(), 'schemas')\n\n // Execute Phase 2: clone with refs\n const result = cloneWithRefs(document, new Set(), 'schemas') as Record<string, unknown>\n\n // Inject extracted components into the result document\n if (extractedComponents.size > 0) {\n const components = (result.components ?? {}) as Record<string, unknown>\n\n for (const [componentType, items] of extractedComponents) {\n const section = (components[componentType] ?? {}) as Record<string, unknown>\n for (const [name, component] of items) {\n section[name] = component\n }\n components[componentType] = section\n }\n\n result.components = components\n }\n\n return result\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["cloned", "section", "result"]
|
|
7
|
-
}
|