fork of hey-api/openapi-ts because I need some additional things
1import { StructureNode } from './node';
2import type { StructureInsert } from './types';
3
4export class StructureModel {
5 /** Root nodes mapped by their names. */
6 private _roots: Map<string, StructureNode> = new Map();
7 /** Node for data without a specific root. */
8 private _virtualRoot?: StructureNode;
9
10 /**
11 * Get all root nodes.
12 */
13 get roots(): ReadonlyArray<StructureNode> {
14 const roots = Array.from(this._roots.values());
15 if (this._virtualRoot) roots.unshift(this._virtualRoot);
16 return roots;
17 }
18
19 /**
20 * Insert data into the structure.
21 */
22 insert(args: StructureInsert): void {
23 const { data, locations, source } = args;
24 for (const location of locations) {
25 const { path, shell } = location;
26 const fullPath = path.filter((s): s is string => Boolean(s));
27 const segments = fullPath.slice(0, -1);
28 const name = fullPath[fullPath.length - 1];
29
30 if (!name) {
31 throw new Error('Cannot insert data without path.');
32 }
33
34 let cursor: StructureNode | null = null;
35
36 for (const segment of segments) {
37 if (!cursor) {
38 cursor = this.root(segment);
39 } else {
40 cursor = cursor.child(segment);
41 }
42
43 if (shell && !cursor.shell) {
44 cursor.shell = shell;
45 cursor.shellSource = source;
46 }
47 }
48
49 if (!cursor) {
50 cursor = this.root(null);
51 }
52
53 cursor.items.push({ data, location: fullPath, source });
54 }
55 }
56
57 /**
58 * Gets or creates a root by name.
59 *
60 * If the root doesn't exist, it's created automatically.
61 *
62 * @param name - The name of the root
63 * @returns The root instance
64 */
65 root(name: string | null): StructureNode {
66 if (!name) {
67 return (this._virtualRoot ??= new StructureNode('', undefined, {
68 virtual: true,
69 }));
70 }
71 if (!this._roots.has(name)) {
72 this._roots.set(name, new StructureNode(name));
73 }
74 return this._roots.get(name)!;
75 }
76
77 /**
78 * Walk all nodes in the structure (depth-first, post-order).
79 *
80 * @returns Generator of all structure nodes
81 */
82 *walk(): Generator<StructureNode> {
83 if (this._virtualRoot) {
84 yield* this._virtualRoot.walk();
85 }
86 for (const root of this._roots.values()) {
87 yield* root.walk();
88 }
89 }
90}