@gtkx/gir 0.20.0 → 0.21.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.
Files changed (115) hide show
  1. package/dist/index.d.ts +25 -12
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +21 -10
  4. package/dist/index.js.map +1 -1
  5. package/dist/internal/loader.d.ts +30 -0
  6. package/dist/internal/loader.d.ts.map +1 -0
  7. package/dist/internal/loader.js +109 -0
  8. package/dist/internal/loader.js.map +1 -0
  9. package/dist/internal/normalizer.d.ts +98 -15
  10. package/dist/internal/normalizer.d.ts.map +1 -1
  11. package/dist/internal/normalizer.js +381 -412
  12. package/dist/internal/normalizer.js.map +1 -1
  13. package/dist/internal/parser.d.ts +23 -31
  14. package/dist/internal/parser.d.ts.map +1 -1
  15. package/dist/internal/parser.js +228 -244
  16. package/dist/internal/parser.js.map +1 -1
  17. package/dist/internal/raw-types.d.ts +58 -109
  18. package/dist/internal/raw-types.d.ts.map +1 -1
  19. package/dist/internal/raw-types.js +0 -8
  20. package/dist/internal/raw-types.js.map +1 -1
  21. package/dist/intrinsics.d.ts.map +1 -1
  22. package/dist/intrinsics.js +7 -0
  23. package/dist/intrinsics.js.map +1 -1
  24. package/dist/model/alias.d.ts +21 -0
  25. package/dist/model/alias.d.ts.map +1 -0
  26. package/dist/model/alias.js +22 -0
  27. package/dist/model/alias.js.map +1 -0
  28. package/dist/model/callables.d.ts +102 -0
  29. package/dist/model/callables.d.ts.map +1 -0
  30. package/dist/model/callables.js +123 -0
  31. package/dist/model/callables.js.map +1 -0
  32. package/dist/model/callback.d.ts +22 -0
  33. package/dist/model/callback.d.ts.map +1 -0
  34. package/dist/model/callback.js +20 -0
  35. package/dist/model/callback.js.map +1 -0
  36. package/dist/model/class.d.ts +92 -0
  37. package/dist/model/class.d.ts.map +1 -0
  38. package/dist/model/class.js +171 -0
  39. package/dist/model/class.js.map +1 -0
  40. package/dist/model/constant.d.ts +21 -0
  41. package/dist/model/constant.d.ts.map +1 -0
  42. package/dist/model/constant.js +20 -0
  43. package/dist/model/constant.js.map +1 -0
  44. package/dist/model/enumeration.d.ts +41 -0
  45. package/dist/model/enumeration.d.ts.map +1 -0
  46. package/dist/model/enumeration.js +47 -0
  47. package/dist/model/enumeration.js.map +1 -0
  48. package/dist/model/field.d.ts +21 -0
  49. package/dist/model/field.d.ts.map +1 -0
  50. package/dist/model/field.js +20 -0
  51. package/dist/model/field.js.map +1 -0
  52. package/dist/model/interface.d.ts +44 -0
  53. package/dist/model/interface.d.ts.map +1 -0
  54. package/dist/model/interface.js +67 -0
  55. package/dist/model/interface.js.map +1 -0
  56. package/dist/model/namespace.d.ts +44 -0
  57. package/dist/model/namespace.d.ts.map +1 -0
  58. package/dist/model/namespace.js +36 -0
  59. package/dist/model/namespace.js.map +1 -0
  60. package/dist/model/parameter.d.ts +43 -0
  61. package/dist/model/parameter.d.ts.map +1 -0
  62. package/dist/model/parameter.js +54 -0
  63. package/dist/model/parameter.js.map +1 -0
  64. package/dist/model/property.d.ts +62 -0
  65. package/dist/model/property.d.ts.map +1 -0
  66. package/dist/model/property.js +69 -0
  67. package/dist/model/property.js.map +1 -0
  68. package/dist/model/record.d.ts +56 -0
  69. package/dist/model/record.d.ts.map +1 -0
  70. package/dist/model/record.js +70 -0
  71. package/dist/model/record.js.map +1 -0
  72. package/dist/model/repository-like.d.ts +19 -0
  73. package/dist/model/repository-like.d.ts.map +1 -0
  74. package/dist/model/repository-like.js +2 -0
  75. package/dist/model/repository-like.js.map +1 -0
  76. package/dist/model/signal.d.ts +22 -0
  77. package/dist/model/signal.d.ts.map +1 -0
  78. package/dist/model/signal.js +22 -0
  79. package/dist/model/signal.js.map +1 -0
  80. package/dist/model/type.d.ts +71 -0
  81. package/dist/model/type.d.ts.map +1 -0
  82. package/dist/model/type.js +112 -0
  83. package/dist/model/type.js.map +1 -0
  84. package/dist/repository.d.ts +92 -138
  85. package/dist/repository.d.ts.map +1 -1
  86. package/dist/repository.js +155 -219
  87. package/dist/repository.js.map +1 -1
  88. package/package.json +2 -2
  89. package/src/index.ts +25 -39
  90. package/src/internal/loader.ts +127 -0
  91. package/src/internal/normalizer.ts +451 -475
  92. package/src/internal/parser.ts +242 -284
  93. package/src/internal/raw-types.ts +65 -116
  94. package/src/intrinsics.ts +7 -0
  95. package/src/model/alias.ts +31 -0
  96. package/src/model/callables.ts +172 -0
  97. package/src/model/callback.ts +30 -0
  98. package/src/model/class.ts +215 -0
  99. package/src/model/constant.ts +29 -0
  100. package/src/model/enumeration.ts +64 -0
  101. package/src/model/field.ts +29 -0
  102. package/src/model/interface.ts +89 -0
  103. package/src/model/namespace.ts +60 -0
  104. package/src/model/parameter.ts +74 -0
  105. package/src/model/property.ts +97 -0
  106. package/src/model/record.ts +97 -0
  107. package/src/model/repository-like.ts +20 -0
  108. package/src/model/signal.ts +32 -0
  109. package/src/model/type.ts +143 -0
  110. package/src/repository.ts +197 -283
  111. package/dist/types.d.ts +0 -655
  112. package/dist/types.d.ts.map +0 -1
  113. package/dist/types.js +0 -879
  114. package/dist/types.js.map +0 -1
  115. package/src/types.ts +0 -1192
package/src/repository.ts CHANGED
@@ -1,259 +1,234 @@
1
+ import { GirLoader } from "./internal/loader.js";
2
+ import type { GirNamespaceIntermediate } from "./internal/normalizer.js";
3
+ import { GirNormalizer } from "./internal/normalizer.js";
4
+ import { GirParser } from "./internal/parser.js";
5
+ import type { RawNamespace } from "./internal/raw-types.js";
6
+ import { isIntrinsicType } from "./intrinsics.js";
7
+ import type { GirAlias } from "./model/alias.js";
8
+ import type { GirFunction } from "./model/callables.js";
9
+ import type { GirCallback } from "./model/callback.js";
10
+ import { GirClass } from "./model/class.js";
11
+ import type { GirConstant } from "./model/constant.js";
12
+ import type { GirEnumeration } from "./model/enumeration.js";
13
+ import { GirInterface } from "./model/interface.js";
14
+ import { GirNamespace } from "./model/namespace.js";
15
+ import type { GirRecord } from "./model/record.js";
16
+ import type { RepositoryLike, TypeKind } from "./model/repository-like.js";
17
+ import type { GirType } from "./model/type.js";
18
+
1
19
  /**
2
- * GIR Repository - Central entry point for GIR data.
3
- *
4
- * Loads, normalizes, and provides query access to GIR namespaces.
5
- * All type references are normalized to fully qualified names.
20
+ * Options for loading a GIR repository.
6
21
  */
22
+ export type RepositoryOptions = {
23
+ girPath: string[];
24
+ };
7
25
 
8
- import { readdir, readFile } from "node:fs/promises";
9
- import { join } from "node:path";
10
- import { type NormalizerContext, normalizeNamespace } from "./internal/normalizer.js";
11
- import { type ParserOptions, RawGirParser } from "./internal/parser.js";
12
- import type { RawNamespace } from "./internal/raw-types.js";
13
- import { isIntrinsicType } from "./intrinsics.js";
14
- import {
15
- type GirAlias,
16
- type GirCallback,
17
- type GirClass,
18
- type GirConstant,
19
- type GirEnumeration,
20
- type GirFunction,
21
- type GirInterface,
22
- type GirNamespace,
23
- type GirRecord,
24
- type GirType,
25
- parseQualifiedName,
26
- type QualifiedName,
27
- type TypeKind,
28
- } from "./types.js";
26
+ /**
27
+ * A dependency graph entry mapping namespace keys to their file paths
28
+ * and direct dependencies.
29
+ */
30
+ export type DependencyGraph = Map<string, { filePath: string; dependencies: string[] }>;
29
31
 
30
32
  /**
31
- * Central repository for GIR data.
33
+ * Central registry for GIR data.
32
34
  *
33
- * Loads, normalizes, and provides query access to GIR namespaces.
34
- * All type references are normalized to fully qualified names.
35
+ * Loads, resolves, and provides query access to GIR namespaces.
36
+ * All type references use fully qualified names (`Namespace.TypeName`).
35
37
  *
36
38
  * @example
37
39
  * ```typescript
38
- * const repo = new GirRepository();
39
- * await repo.loadFromDirectory("./girs");
40
- * repo.resolve();
40
+ * const repo = await GirRepository.load(["Gtk-4.0", "Adw-1"], {
41
+ * girPath: ["/usr/share/gir-1.0"]
42
+ * });
41
43
  *
42
- * const gtkNs = repo.getNamespace("Gtk");
43
- * const buttonClass = repo.resolveClass("Gtk.Button");
44
- * const chain = buttonClass.getInheritanceChain();
44
+ * const button = repo.resolveClass("Gtk.Button");
45
+ * button.isSubclassOf("Gtk.Widget"); // true
46
+ * button.getInheritanceChain();
45
47
  * // ["Gtk.Button", "Gtk.Widget", "GObject.InitiallyUnowned", "GObject.Object"]
46
48
  * ```
47
49
  */
48
- export type RepositoryOptions = ParserOptions;
49
-
50
- export class GirRepository {
51
- private rawNamespaces = new Map<string, RawNamespace>();
52
- private normalizedNamespaces = new Map<string, GirNamespace>();
53
- private parser: RawGirParser;
54
- private resolved = false;
50
+ export class GirRepository implements RepositoryLike {
51
+ private readonly namespaces: Map<string, GirNamespace>;
55
52
 
56
- constructor(options: RepositoryOptions = {}) {
57
- this.parser = new RawGirParser(options);
53
+ private constructor(namespaces: Map<string, GirNamespace>) {
54
+ this.namespaces = namespaces;
58
55
  }
59
56
 
60
57
  /**
61
- * Loads a single GIR file from XML content.
58
+ * Discovers all transitive dependencies for the given roots without
59
+ * performing a full parse. Returns namespace keys and their file paths.
60
+ *
61
+ * @param roots - Namespace keys to start from (e.g., `["Gtk-4.0", "Adw-1"]`)
62
+ * @param options - Search paths for GIR files
62
63
  */
63
- loadFromXml(xml: string): void {
64
- const raw = this.parser.parse(xml);
65
- const existing = this.rawNamespaces.get(raw.name);
66
- if (existing && this.compareVersions(existing.version, raw.version) >= 0) {
67
- return;
68
- }
69
- this.rawNamespaces.set(raw.name, raw);
70
- this.resolved = false;
64
+ static async discoverDependencies(roots: string[], options: RepositoryOptions): Promise<DependencyGraph> {
65
+ const loader = new GirLoader(options.girPath);
66
+ return loader.discoverDependencies(roots);
71
67
  }
72
68
 
73
69
  /**
74
- * Loads a GIR file from disk.
70
+ * Loads GIR files for the given namespace roots and all their transitive
71
+ * dependencies, returning a fully resolved repository.
72
+ *
73
+ * @param roots - Namespace keys to load (e.g., `["Gtk-4.0", "Adw-1"]`)
74
+ * @param options - Search paths for GIR files
75
75
  */
76
- async loadFromFile(path: string): Promise<void> {
77
- const xml = await readFile(path, "utf-8");
78
- this.loadFromXml(xml);
79
- }
76
+ static async load(roots: string[], options: RepositoryOptions): Promise<GirRepository> {
77
+ const loader = new GirLoader(options.girPath);
78
+ const loaded = await loader.loadAll(roots);
80
79
 
81
- /**
82
- * Loads all .gir files from a directory.
83
- */
84
- async loadFromDirectory(dirPath: string): Promise<void> {
85
- const files = await readdir(dirPath);
86
- const girFiles = files.filter((f) => f.endsWith(".gir"));
80
+ const rawNamespaces = new Map<string, RawNamespace>();
81
+ for (const [name, { raw }] of loaded) {
82
+ rawNamespaces.set(name, raw);
83
+ }
87
84
 
88
- await Promise.all(girFiles.map((file) => this.loadFromFile(join(dirPath, file))));
85
+ return GirRepository.buildFromRaw(rawNamespaces);
89
86
  }
90
87
 
91
88
  /**
92
- * After loading all raw GIR files, normalize and resolve all references.
93
- * This must be called after loading and before querying.
89
+ * Creates a repository from inline GIR XML strings.
90
+ * Useful for testing and scenarios where GIR data is already in memory.
94
91
  */
95
- resolve(): void {
96
- if (this.resolved) return;
97
-
98
- const ctx: NormalizerContext = {
99
- rawNamespaces: this.rawNamespaces,
100
- };
92
+ static fromXml(xmlStrings: string[]): GirRepository {
93
+ const parser = new GirParser();
94
+ const rawNamespaces = new Map<string, RawNamespace>();
101
95
 
102
- for (const [name, raw] of this.rawNamespaces) {
103
- const normalized = normalizeNamespace(raw, ctx);
104
- this.normalizedNamespaces.set(name, normalized);
96
+ for (const xml of xmlStrings) {
97
+ const raw = parser.parseNamespace(xml);
98
+ rawNamespaces.set(raw.name, raw);
105
99
  }
106
100
 
107
- for (const ns of this.normalizedNamespaces.values()) {
108
- for (const cls of ns.classes.values()) {
109
- cls._setRepository(this);
101
+ return GirRepository.buildFromRaw(rawNamespaces);
102
+ }
103
+
104
+ private static buildFromRaw(rawNamespaces: Map<string, RawNamespace>): GirRepository {
105
+ const normalizer = new GirNormalizer();
106
+ const intermediates = normalizer.normalize(rawNamespaces);
107
+ return GirRepository.buildFromIntermediates(intermediates);
108
+ }
109
+
110
+ private static buildFromIntermediates(intermediates: Map<string, GirNamespaceIntermediate>): GirRepository {
111
+ const namespaceMap = new Map<string, GirNamespace>();
112
+ const repo = new GirRepository(namespaceMap);
113
+
114
+ for (const [nsName, data] of intermediates) {
115
+ const classes = new Map<string, GirClass>();
116
+ for (const [name, classData] of data.classes) {
117
+ classes.set(name, new GirClass(classData, repo));
110
118
  }
111
- for (const iface of ns.interfaces.values()) {
112
- iface._setRepository(this);
119
+
120
+ const interfaces = new Map<string, GirInterface>();
121
+ for (const [name, ifaceData] of data.interfaces) {
122
+ interfaces.set(name, new GirInterface(ifaceData, repo));
113
123
  }
124
+
125
+ namespaceMap.set(
126
+ nsName,
127
+ new GirNamespace({
128
+ name: data.name,
129
+ version: data.version,
130
+ sharedLibrary: data.sharedLibrary,
131
+ cPrefix: data.cPrefix,
132
+ classes,
133
+ interfaces,
134
+ records: data.records,
135
+ enumerations: data.enumerations,
136
+ bitfields: data.bitfields,
137
+ callbacks: data.callbacks,
138
+ functions: data.functions,
139
+ constants: data.constants,
140
+ aliases: data.aliases,
141
+ doc: data.doc,
142
+ }),
143
+ );
114
144
  }
115
145
 
116
- this.resolved = true;
146
+ return repo;
117
147
  }
118
148
 
119
- /**
120
- * Gets all loaded namespace names.
121
- */
149
+ /** Gets all loaded namespace names. */
122
150
  getNamespaceNames(): string[] {
123
- this.ensureResolved();
124
- return [...this.normalizedNamespaces.keys()];
151
+ return [...this.namespaces.keys()];
125
152
  }
126
153
 
127
- /**
128
- * Gets a normalized namespace by name.
129
- */
154
+ /** Gets a namespace by name, or null if not loaded. */
130
155
  getNamespace(name: string): GirNamespace | null {
131
- this.ensureResolved();
132
- return this.normalizedNamespaces.get(name) ?? null;
156
+ return this.namespaces.get(name) ?? null;
133
157
  }
134
158
 
135
- /**
136
- * Gets all normalized namespaces.
137
- */
159
+ /** Gets all loaded namespaces. */
138
160
  getAllNamespaces(): Map<string, GirNamespace> {
139
- this.ensureResolved();
140
- return this.normalizedNamespaces;
161
+ return this.namespaces;
141
162
  }
142
163
 
143
- /**
144
- * Resolves a class by qualified name.
145
- * @example repo.resolveClass("Gtk.Button" as QualifiedName)
146
- */
147
- resolveClass(qualifiedName: QualifiedName): GirClass | null {
148
- this.ensureResolved();
149
- const { namespace, name } = parseQualifiedName(qualifiedName);
150
- return this.normalizedNamespaces.get(namespace)?.classes.get(name) ?? null;
164
+ /** Resolves a class by qualified name (e.g., `"Gtk.Button"`). */
165
+ resolveClass(qualifiedName: string): GirClass | null {
166
+ const { namespace, name } = splitQualifiedName(qualifiedName);
167
+ return this.namespaces.get(namespace)?.classes.get(name) ?? null;
151
168
  }
152
169
 
153
- /**
154
- * Resolves an interface by qualified name.
155
- * @example repo.resolveInterface("Gio.ListModel" as QualifiedName)
156
- */
157
- resolveInterface(qualifiedName: QualifiedName): GirInterface | null {
158
- this.ensureResolved();
159
- const { namespace, name } = parseQualifiedName(qualifiedName);
160
- return this.normalizedNamespaces.get(namespace)?.interfaces.get(name) ?? null;
170
+ /** Resolves an interface by qualified name (e.g., `"Gio.ListModel"`). */
171
+ resolveInterface(qualifiedName: string): GirInterface | null {
172
+ const { namespace, name } = splitQualifiedName(qualifiedName);
173
+ return this.namespaces.get(namespace)?.interfaces.get(name) ?? null;
161
174
  }
162
175
 
163
- /**
164
- * Resolves a record (boxed type) by qualified name.
165
- * @example repo.resolveRecord("Gdk.Rectangle" as QualifiedName)
166
- */
167
- resolveRecord(qualifiedName: QualifiedName): GirRecord | null {
168
- this.ensureResolved();
169
- const { namespace, name } = parseQualifiedName(qualifiedName);
170
- return this.normalizedNamespaces.get(namespace)?.records.get(name) ?? null;
176
+ /** Resolves a record by qualified name (e.g., `"Gdk.Rectangle"`). */
177
+ resolveRecord(qualifiedName: string): GirRecord | null {
178
+ const { namespace, name } = splitQualifiedName(qualifiedName);
179
+ return this.namespaces.get(namespace)?.records.get(name) ?? null;
171
180
  }
172
181
 
173
- /**
174
- * Resolves an enumeration by qualified name.
175
- * @example repo.resolveEnum("Gtk.Orientation" as QualifiedName)
176
- */
177
- resolveEnum(qualifiedName: QualifiedName): GirEnumeration | null {
178
- this.ensureResolved();
179
- const { namespace, name } = parseQualifiedName(qualifiedName);
180
- return this.normalizedNamespaces.get(namespace)?.enumerations.get(name) ?? null;
182
+ /** Resolves an enumeration by qualified name (e.g., `"Gtk.Orientation"`). */
183
+ resolveEnum(qualifiedName: string): GirEnumeration | null {
184
+ const { namespace, name } = splitQualifiedName(qualifiedName);
185
+ return this.namespaces.get(namespace)?.enumerations.get(name) ?? null;
181
186
  }
182
187
 
183
- /**
184
- * Resolves a bitfield (flags) by qualified name.
185
- * @example repo.resolveFlags("Gdk.ModifierType" as QualifiedName)
186
- */
187
- resolveFlags(qualifiedName: QualifiedName): GirEnumeration | null {
188
- this.ensureResolved();
189
- const { namespace, name } = parseQualifiedName(qualifiedName);
190
- return this.normalizedNamespaces.get(namespace)?.bitfields.get(name) ?? null;
188
+ /** Resolves a bitfield by qualified name (e.g., `"Gdk.ModifierType"`). */
189
+ resolveFlags(qualifiedName: string): GirEnumeration | null {
190
+ const { namespace, name } = splitQualifiedName(qualifiedName);
191
+ return this.namespaces.get(namespace)?.bitfields.get(name) ?? null;
191
192
  }
192
193
 
193
- /**
194
- * Resolves a callback type by qualified name.
195
- * @example repo.resolveCallback("Gio.AsyncReadyCallback" as QualifiedName)
196
- */
197
- resolveCallback(qualifiedName: QualifiedName): GirCallback | null {
198
- this.ensureResolved();
199
- const { namespace, name } = parseQualifiedName(qualifiedName);
200
- return this.normalizedNamespaces.get(namespace)?.callbacks.get(name) ?? null;
194
+ /** Resolves a callback by qualified name (e.g., `"Gio.AsyncReadyCallback"`). */
195
+ resolveCallback(qualifiedName: string): GirCallback | null {
196
+ const { namespace, name } = splitQualifiedName(qualifiedName);
197
+ return this.namespaces.get(namespace)?.callbacks.get(name) ?? null;
201
198
  }
202
199
 
203
- /**
204
- * Resolves a constant by qualified name.
205
- * @example repo.resolveConstant("Gtk.MAJOR_VERSION" as QualifiedName)
206
- */
207
- resolveConstant(qualifiedName: QualifiedName): GirConstant | null {
208
- this.ensureResolved();
209
- const { namespace, name } = parseQualifiedName(qualifiedName);
210
- return this.normalizedNamespaces.get(namespace)?.constants.get(name) ?? null;
200
+ /** Resolves a constant by qualified name (e.g., `"Gtk.MAJOR_VERSION"`). */
201
+ resolveConstant(qualifiedName: string): GirConstant | null {
202
+ const { namespace, name } = splitQualifiedName(qualifiedName);
203
+ return this.namespaces.get(namespace)?.constants.get(name) ?? null;
211
204
  }
212
205
 
213
- /**
214
- * Resolves a standalone function by qualified name.
215
- * @example repo.resolveFunction("Gtk.init" as QualifiedName)
216
- */
217
- resolveFunction(qualifiedName: QualifiedName): GirFunction | null {
218
- this.ensureResolved();
219
- const { namespace, name } = parseQualifiedName(qualifiedName);
220
- return this.normalizedNamespaces.get(namespace)?.functions.get(name) ?? null;
206
+ /** Resolves a standalone function by qualified name (e.g., `"Gtk.init"`). */
207
+ resolveFunction(qualifiedName: string): GirFunction | null {
208
+ const { namespace, name } = splitQualifiedName(qualifiedName);
209
+ return this.namespaces.get(namespace)?.functions.get(name) ?? null;
221
210
  }
222
211
 
223
- /**
224
- * Resolves an alias by qualified name.
225
- * @example repo.resolveAlias("Pango.LayoutRun" as QualifiedName)
226
- */
227
- resolveAlias(qualifiedName: QualifiedName): GirAlias | null {
228
- this.ensureResolved();
229
- const { namespace, name } = parseQualifiedName(qualifiedName);
230
- return this.normalizedNamespaces.get(namespace)?.aliases.get(name) ?? null;
212
+ /** Resolves an alias by qualified name (e.g., `"Pango.LayoutRun"`). */
213
+ resolveAlias(qualifiedName: string): GirAlias | null {
214
+ const { namespace, name } = splitQualifiedName(qualifiedName);
215
+ return this.namespaces.get(namespace)?.aliases.get(name) ?? null;
231
216
  }
232
217
 
233
- /**
234
- * Resolves a type name through aliases to get the target type.
235
- * If the type is an alias, returns the target type.
236
- * If not an alias, returns null.
237
- * @example repo.resolveTypeAlias("Pango.LayoutRun" as QualifiedName) // returns GirType pointing to Pango.GlyphItem
238
- */
239
- resolveTypeAlias(qualifiedName: QualifiedName): GirType | null {
240
- const alias = this.resolveAlias(qualifiedName);
241
- return alias?.targetType ?? null;
218
+ /** Resolves a type alias to its target type, or null if not an alias. */
219
+ resolveTypeAlias(qualifiedName: string): GirType | null {
220
+ return this.resolveAlias(qualifiedName)?.targetType ?? null;
242
221
  }
243
222
 
244
223
  /**
245
224
  * Gets the kind of a type (class, interface, record, enum, flags, callback).
246
225
  * Returns null for intrinsic types or unknown types.
247
226
  */
248
- getTypeKind(qualifiedName: QualifiedName): TypeKind | null {
249
- this.ensureResolved();
250
-
251
- if (isIntrinsicType(qualifiedName)) {
252
- return null;
253
- }
227
+ getTypeKind(qualifiedName: string): TypeKind | null {
228
+ if (isIntrinsicType(qualifiedName)) return null;
254
229
 
255
- const { namespace, name } = parseQualifiedName(qualifiedName);
256
- const ns = this.normalizedNamespaces.get(namespace);
230
+ const { namespace, name } = splitQualifiedName(qualifiedName);
231
+ const ns = this.namespaces.get(namespace);
257
232
  if (!ns) return null;
258
233
 
259
234
  if (ns.classes.has(name)) return "class";
@@ -266,156 +241,95 @@ export class GirRepository {
266
241
  return null;
267
242
  }
268
243
 
269
- /**
270
- * Gets the full inheritance chain for a class.
271
- * Returns array from most derived to base.
272
- *
273
- * @example
274
- * getInheritanceChain("Gtk.Button" as QualifiedName)
275
- * // ["Gtk.Button", "Gtk.Widget", "GObject.InitiallyUnowned", "GObject.Object"]
276
- */
277
- getInheritanceChain(qualifiedName: QualifiedName): QualifiedName[] {
278
- const cls = this.resolveClass(qualifiedName);
279
- return cls?.getInheritanceChain() ?? [];
244
+ /** Gets the full inheritance chain for a class (most derived to base). */
245
+ getInheritanceChain(qualifiedName: string): string[] {
246
+ return this.resolveClass(qualifiedName)?.getInheritanceChain() ?? [];
280
247
  }
281
248
 
282
- /**
283
- * Gets all interfaces implemented by a class (including inherited).
284
- */
285
- getImplementedInterfaces(qualifiedName: QualifiedName): QualifiedName[] {
286
- const cls = this.resolveClass(qualifiedName);
287
- return cls?.getAllImplementedInterfaces() ?? [];
249
+ /** Gets all interfaces implemented by a class (including inherited). */
250
+ getImplementedInterfaces(qualifiedName: string): string[] {
251
+ return this.resolveClass(qualifiedName)?.getAllImplementedInterfaces() ?? [];
288
252
  }
289
253
 
290
- /**
291
- * Gets all classes that derive from a given class.
292
- */
293
- getDerivedClasses(qualifiedName: QualifiedName): QualifiedName[] {
294
- this.ensureResolved();
295
- const derived: QualifiedName[] = [];
296
-
297
- for (const ns of this.normalizedNamespaces.values()) {
254
+ /** Gets all classes that derive from a given class. */
255
+ getDerivedClasses(qualifiedName: string): string[] {
256
+ const derived: string[] = [];
257
+ for (const ns of this.namespaces.values()) {
298
258
  for (const cls of ns.classes.values()) {
299
259
  if (cls.qualifiedName !== qualifiedName && cls.isSubclassOf(qualifiedName)) {
300
260
  derived.push(cls.qualifiedName);
301
261
  }
302
262
  }
303
263
  }
304
-
305
264
  return derived;
306
265
  }
307
266
 
308
- /**
309
- * Gets all classes that implement a given interface.
310
- */
311
- getImplementors(interfaceName: QualifiedName): QualifiedName[] {
312
- this.ensureResolved();
313
- const implementors: QualifiedName[] = [];
314
-
315
- for (const ns of this.normalizedNamespaces.values()) {
267
+ /** Gets all classes that implement a given interface. */
268
+ getImplementors(interfaceName: string): string[] {
269
+ const implementors: string[] = [];
270
+ for (const ns of this.namespaces.values()) {
316
271
  for (const cls of ns.classes.values()) {
317
272
  if (cls.implementsInterface(interfaceName)) {
318
273
  implementors.push(cls.qualifiedName);
319
274
  }
320
275
  }
321
276
  }
322
-
323
277
  return implementors;
324
278
  }
325
279
 
326
- /**
327
- * Checks if a type is a GObject (class with GType).
328
- */
329
- isGObject(qualifiedName: QualifiedName): boolean {
330
- const cls = this.resolveClass(qualifiedName);
331
- return cls?.hasGType() ?? false;
280
+ /** Checks if a type is a GObject (class with GType). */
281
+ isGObject(qualifiedName: string): boolean {
282
+ return this.resolveClass(qualifiedName)?.hasGType() ?? false;
332
283
  }
333
284
 
334
- /**
335
- * Checks if a type is a boxed type (record with GType).
336
- */
337
- isBoxed(qualifiedName: QualifiedName): boolean {
338
- const record = this.resolveRecord(qualifiedName);
339
- return record?.isBoxed() ?? false;
285
+ /** Checks if a type is a boxed type (record with GType). */
286
+ isBoxed(qualifiedName: string): boolean {
287
+ return this.resolveRecord(qualifiedName)?.isBoxed() ?? false;
340
288
  }
341
289
 
342
- /**
343
- * Checks if a type is a primitive (intrinsic).
344
- */
290
+ /** Checks if a type is a primitive (intrinsic). */
345
291
  isPrimitive(typeName: string): boolean {
346
292
  return isIntrinsicType(typeName);
347
293
  }
348
294
 
349
- /**
350
- * Finds all classes matching a predicate across all namespaces.
351
- */
295
+ /** Finds all classes matching a predicate across all namespaces. */
352
296
  findClasses(predicate: (cls: GirClass) => boolean): GirClass[] {
353
- this.ensureResolved();
354
297
  const results: GirClass[] = [];
355
-
356
- for (const ns of this.normalizedNamespaces.values()) {
298
+ for (const ns of this.namespaces.values()) {
357
299
  for (const cls of ns.classes.values()) {
358
- if (predicate(cls)) {
359
- results.push(cls);
360
- }
300
+ if (predicate(cls)) results.push(cls);
361
301
  }
362
302
  }
363
-
364
303
  return results;
365
304
  }
366
305
 
367
- /**
368
- * Finds all interfaces matching a predicate across all namespaces.
369
- */
306
+ /** Finds all interfaces matching a predicate across all namespaces. */
370
307
  findInterfaces(predicate: (iface: GirInterface) => boolean): GirInterface[] {
371
- this.ensureResolved();
372
308
  const results: GirInterface[] = [];
373
-
374
- for (const ns of this.normalizedNamespaces.values()) {
309
+ for (const ns of this.namespaces.values()) {
375
310
  for (const iface of ns.interfaces.values()) {
376
- if (predicate(iface)) {
377
- results.push(iface);
378
- }
311
+ if (predicate(iface)) results.push(iface);
379
312
  }
380
313
  }
381
-
382
314
  return results;
383
315
  }
384
316
 
385
- /**
386
- * Finds all records matching a predicate across all namespaces.
387
- */
317
+ /** Finds all records matching a predicate across all namespaces. */
388
318
  findRecords(predicate: (record: GirRecord) => boolean): GirRecord[] {
389
- this.ensureResolved();
390
319
  const results: GirRecord[] = [];
391
-
392
- for (const ns of this.normalizedNamespaces.values()) {
320
+ for (const ns of this.namespaces.values()) {
393
321
  for (const record of ns.records.values()) {
394
- if (predicate(record)) {
395
- results.push(record);
396
- }
322
+ if (predicate(record)) results.push(record);
397
323
  }
398
324
  }
399
-
400
325
  return results;
401
326
  }
327
+ }
402
328
 
403
- private compareVersions(a: string, b: string): number {
404
- const partsA = a.split(".").map(Number);
405
- const partsB = b.split(".").map(Number);
406
- const len = Math.max(partsA.length, partsB.length);
407
- for (let i = 0; i < len; i++) {
408
- const diff = (partsA[i] ?? 0) - (partsB[i] ?? 0);
409
- if (diff !== 0) return diff;
410
- }
411
- return 0;
412
- }
413
-
414
- private ensureResolved(): void {
415
- if (!this.resolved) {
416
- throw new Error(
417
- "GirRepository.resolve() must be called before querying. Call resolve() after loading all GIR files.",
418
- );
419
- }
420
- }
329
+ function splitQualifiedName(qn: string): { namespace: string; name: string } {
330
+ const dot = qn.indexOf(".");
331
+ return {
332
+ namespace: qn.slice(0, dot),
333
+ name: qn.slice(dot + 1),
334
+ };
421
335
  }