@gtkx/gir 0.10.5 → 0.11.1

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/dist/types.js CHANGED
@@ -1,1123 +1,781 @@
1
1
  /**
2
- * GIR type definitions and utilities.
2
+ * Normalized GIR types with helper methods.
3
3
  *
4
- * Provides TypeScript representations of GObject Introspection data
5
- * and utilities for type resolution and mapping.
6
- *
7
- * @packageDocumentation
4
+ * All type references use fully qualified names (Namespace.TypeName)
5
+ * except for intrinsic types which remain unqualified.
8
6
  */
9
- import { normalizeClassName } from "./class-names.js";
10
- import { toCamelCase, toPascalCase } from "./naming.js";
11
- export { toCamelCase, toPascalCase };
7
+ import { isIntrinsicType, isNumericType, isStringType, isVoidType } from "./intrinsics.js";
12
8
  /**
13
- * Builds a lookup map from class name to class definition.
14
- *
15
- * @param classes - Array of parsed GIR classes
16
- * @returns Map keyed by class name for O(1) lookup
9
+ * Creates a QualifiedName from namespace and type name.
17
10
  */
18
- export const buildClassMap = (classes) => {
19
- const classMap = new Map();
20
- for (const cls of classes) {
21
- classMap.set(cls.name, cls);
22
- }
23
- return classMap;
11
+ export const qualifiedName = (namespace, name) => {
12
+ return `${namespace}.${name}`;
24
13
  };
25
14
  /**
26
- * Registers all enumerations and bitfields from a namespace into a type mapper.
27
- *
28
- * @param typeMapper - The type mapper to register enums with
29
- * @param namespace - The parsed GIR namespace containing enums
15
+ * Parses a QualifiedName into its parts.
30
16
  */
31
- export const registerEnumsFromNamespace = (typeMapper, namespace) => {
32
- for (const enumeration of namespace.enumerations) {
33
- typeMapper.registerEnum(enumeration.name, toPascalCase(enumeration.name));
34
- }
35
- for (const bitfield of namespace.bitfields) {
36
- typeMapper.registerFlags(bitfield.name, toPascalCase(bitfield.name));
37
- }
17
+ export const parseQualifiedName = (qn) => {
18
+ const dot = qn.indexOf(".");
19
+ return {
20
+ namespace: qn.slice(0, dot),
21
+ name: qn.slice(dot + 1),
22
+ };
38
23
  };
39
24
  /**
40
- * Registry for resolving types across multiple GIR namespaces.
41
- *
42
- * Enables cross-namespace type resolution by maintaining a map of
43
- * all known types from all loaded namespaces.
44
- *
45
- * @example
46
- * ```tsx
47
- * const registry = TypeRegistry.fromNamespaces([gtkNamespace, gioNamespace]);
48
- * const buttonType = registry.resolve("Gtk.Button");
49
- * ```
25
+ * Normalized namespace containing all resolved types.
50
26
  */
51
- export class TypeRegistry {
52
- types = new Map();
53
- /**
54
- * Registers a native class type.
55
- *
56
- * @param namespace - The GIR namespace (e.g., "Gtk")
57
- * @param name - The class name (e.g., "Button")
58
- */
59
- registerNativeClass(namespace, name) {
60
- const transformedName = normalizeClassName(name, namespace);
61
- this.types.set(`${namespace}.${name}`, {
62
- kind: "class",
63
- name,
64
- namespace,
65
- transformedName,
66
- });
67
- }
68
- /**
69
- * Registers an interface type.
70
- *
71
- * @param namespace - The GIR namespace
72
- * @param name - The interface name
73
- */
74
- registerInterface(namespace, name) {
75
- const transformedName = normalizeClassName(name, namespace);
76
- this.types.set(`${namespace}.${name}`, {
77
- kind: "interface",
78
- name,
79
- namespace,
80
- transformedName,
81
- });
82
- }
83
- /**
84
- * Registers an enumeration type.
85
- *
86
- * @param namespace - The GIR namespace
87
- * @param name - The enum name
88
- */
89
- registerEnum(namespace, name) {
90
- const transformedName = toPascalCase(name);
91
- this.types.set(`${namespace}.${name}`, {
92
- kind: "enum",
93
- name,
94
- namespace,
95
- transformedName,
96
- });
97
- }
98
- /**
99
- * Registers a flags (bitfield) type.
100
- *
101
- * @param namespace - The GIR namespace
102
- * @param name - The flags name
103
- */
104
- registerFlags(namespace, name) {
105
- const transformedName = toPascalCase(name);
106
- this.types.set(`${namespace}.${name}`, {
107
- kind: "flags",
108
- name,
109
- namespace,
110
- transformedName,
111
- });
112
- }
113
- /**
114
- * Registers a record (boxed) type.
115
- *
116
- * @param namespace - The GIR namespace
117
- * @param name - The record name
118
- * @param glibTypeName - Optional GLib type name for runtime type info
119
- * @param sharedLibrary - Optional shared library containing the type
120
- * @param glibGetType - Optional get_type function name
121
- * @param isPlainStruct - If true, this is a plain C struct without GType
122
- * @param structSize - Size in bytes for plain structs
123
- * @param structFields - Field definitions for plain structs
124
- */
125
- registerRecord(namespace, name, glibTypeName, sharedLibrary, glibGetType, isPlainStruct, structSize, structFields) {
126
- const transformedName = normalizeClassName(name, namespace);
127
- this.types.set(`${namespace}.${name}`, {
128
- kind: "record",
129
- name,
130
- namespace,
131
- transformedName,
132
- glibTypeName,
133
- sharedLibrary,
134
- glibGetType,
135
- isPlainStruct,
136
- structSize,
137
- structFields,
138
- });
139
- }
140
- /**
141
- * Registers a callback type.
142
- *
143
- * @param namespace - The GIR namespace
144
- * @param name - The callback name
145
- */
146
- registerCallback(namespace, name) {
147
- const transformedName = toPascalCase(name);
148
- this.types.set(`${namespace}.${name}`, {
149
- kind: "callback",
150
- name,
151
- namespace,
152
- transformedName,
153
- });
154
- }
155
- /**
156
- * Resolves a fully qualified type name.
157
- *
158
- * @param qualifiedName - Type name in "Namespace.TypeName" format
159
- * @returns The registered type, or undefined if not found
160
- */
161
- resolve(qualifiedName) {
162
- return this.types.get(qualifiedName);
163
- }
164
- /**
165
- * Resolves a type name within a namespace context.
166
- *
167
- * First checks the current namespace, then falls back to
168
- * searching all registered types.
169
- *
170
- * @param name - Type name (may be unqualified)
171
- * @param currentNamespace - The namespace to search first
172
- * @returns The registered type, or undefined if not found
173
- */
174
- resolveInNamespace(name, currentNamespace) {
175
- if (name.includes(".")) {
176
- return this.resolve(name);
177
- }
178
- const inCurrent = this.resolve(`${currentNamespace}.${name}`);
179
- if (inCurrent) {
180
- return inCurrent;
27
+ export class GirNamespace {
28
+ name;
29
+ version;
30
+ sharedLibrary;
31
+ cPrefix;
32
+ classes;
33
+ interfaces;
34
+ records;
35
+ enumerations;
36
+ bitfields;
37
+ callbacks;
38
+ functions;
39
+ constants;
40
+ doc;
41
+ constructor(data) {
42
+ this.name = data.name;
43
+ this.version = data.version;
44
+ this.sharedLibrary = data.sharedLibrary;
45
+ this.cPrefix = data.cPrefix;
46
+ this.classes = data.classes;
47
+ this.interfaces = data.interfaces;
48
+ this.records = data.records;
49
+ this.enumerations = data.enumerations;
50
+ this.bitfields = data.bitfields;
51
+ this.callbacks = data.callbacks;
52
+ this.functions = data.functions;
53
+ this.constants = data.constants;
54
+ this.doc = data.doc;
55
+ }
56
+ }
57
+ /**
58
+ * Normalized class with helper methods.
59
+ */
60
+ export class GirClass {
61
+ name;
62
+ qualifiedName;
63
+ cType;
64
+ parent;
65
+ abstract;
66
+ glibTypeName;
67
+ glibGetType;
68
+ cSymbolPrefix;
69
+ implements;
70
+ methods;
71
+ constructors;
72
+ staticFunctions;
73
+ properties;
74
+ signals;
75
+ doc;
76
+ /** @internal */
77
+ _repo;
78
+ constructor(data) {
79
+ this.name = data.name;
80
+ this.qualifiedName = data.qualifiedName;
81
+ this.cType = data.cType;
82
+ this.parent = data.parent;
83
+ this.abstract = data.abstract;
84
+ this.glibTypeName = data.glibTypeName;
85
+ this.glibGetType = data.glibGetType;
86
+ this.cSymbolPrefix = data.cSymbolPrefix;
87
+ this.implements = data.implements;
88
+ this.methods = data.methods;
89
+ this.constructors = data.constructors;
90
+ this.staticFunctions = data.staticFunctions;
91
+ this.properties = data.properties;
92
+ this.signals = data.signals;
93
+ this.doc = data.doc;
94
+ }
95
+ /** @internal */
96
+ _setRepository(repo) {
97
+ this._repo = repo;
98
+ }
99
+ /** Checks if this class is a subclass of another (direct or transitive). */
100
+ isSubclassOf(qualifiedName) {
101
+ if (this.qualifiedName === qualifiedName)
102
+ return true;
103
+ if (!this.parent || !this._repo)
104
+ return false;
105
+ if (this.parent === qualifiedName)
106
+ return true;
107
+ const parentClass = this._repo.resolveClass(this.parent);
108
+ return parentClass?.isSubclassOf(qualifiedName) ?? false;
109
+ }
110
+ /** Gets the full inheritance chain from this class to the root. */
111
+ getInheritanceChain() {
112
+ const chain = [this.qualifiedName];
113
+ let current = this;
114
+ while (current?.parent && this._repo) {
115
+ chain.push(current.parent);
116
+ current = this._repo.resolveClass(current.parent);
181
117
  }
182
- for (const type of this.types.values()) {
183
- if (type.name === name || type.transformedName === name) {
184
- return type;
118
+ return chain;
119
+ }
120
+ /** Gets the parent class object, or null if this is a root class. */
121
+ getParent() {
122
+ return this.parent && this._repo ? this._repo.resolveClass(this.parent) : null;
123
+ }
124
+ /** Checks if this class directly or transitively implements an interface. */
125
+ implementsInterface(qualifiedName) {
126
+ if (this.implements.includes(qualifiedName))
127
+ return true;
128
+ const parent = this.getParent();
129
+ return parent?.implementsInterface(qualifiedName) ?? false;
130
+ }
131
+ /** Gets all implemented interfaces including inherited ones. */
132
+ getAllImplementedInterfaces() {
133
+ const interfaces = new Set(this.implements);
134
+ let current = this.getParent();
135
+ while (current) {
136
+ for (const iface of current.implements) {
137
+ interfaces.add(iface);
185
138
  }
139
+ current = current.getParent();
140
+ }
141
+ return [...interfaces];
142
+ }
143
+ /** Finds a method defined on this class by name. */
144
+ getMethod(name) {
145
+ return this.methods.find((m) => m.name === name) ?? null;
146
+ }
147
+ /** Finds a property defined on this class by name. */
148
+ getProperty(name) {
149
+ return this.properties.find((p) => p.name === name) ?? null;
150
+ }
151
+ /** Finds a signal defined on this class by name. */
152
+ getSignal(name) {
153
+ return this.signals.find((s) => s.name === name) ?? null;
154
+ }
155
+ /** Finds a constructor by name. */
156
+ getConstructor(name) {
157
+ return this.constructors.find((c) => c.name === name) ?? null;
158
+ }
159
+ /** Gets all methods including inherited ones. */
160
+ getAllMethods() {
161
+ const methods = [...this.methods];
162
+ let current = this.getParent();
163
+ while (current) {
164
+ methods.push(...current.methods);
165
+ current = current.getParent();
186
166
  }
167
+ return methods;
168
+ }
169
+ /** Gets all properties including inherited ones. */
170
+ getAllProperties() {
171
+ const properties = [...this.properties];
172
+ let current = this.getParent();
173
+ while (current) {
174
+ properties.push(...current.properties);
175
+ current = current.getParent();
176
+ }
177
+ return properties;
178
+ }
179
+ /** Gets all signals including inherited ones. */
180
+ getAllSignals() {
181
+ const signals = [...this.signals];
182
+ let current = this.getParent();
183
+ while (current) {
184
+ signals.push(...current.signals);
185
+ current = current.getParent();
186
+ }
187
+ return signals;
188
+ }
189
+ /** Finds a method by name, searching up the inheritance chain. */
190
+ findMethod(name) {
191
+ const own = this.getMethod(name);
192
+ if (own)
193
+ return own;
194
+ return this.getParent()?.findMethod(name) ?? null;
195
+ }
196
+ /** Finds a property by name, searching up the inheritance chain. */
197
+ findProperty(name) {
198
+ const own = this.getProperty(name);
199
+ if (own)
200
+ return own;
201
+ return this.getParent()?.findProperty(name) ?? null;
202
+ }
203
+ /** Finds a signal by name, searching up the inheritance chain. */
204
+ findSignal(name) {
205
+ const own = this.getSignal(name);
206
+ if (own)
207
+ return own;
208
+ return this.getParent()?.findSignal(name) ?? null;
209
+ }
210
+ /** True if this is an abstract class. */
211
+ isAbstract() {
212
+ return this.abstract;
213
+ }
214
+ /** True if this has a GType (most GObject classes do). */
215
+ hasGType() {
216
+ return this.glibTypeName !== undefined;
217
+ }
218
+ /** Gets direct subclasses of this class. */
219
+ getDirectSubclasses() {
220
+ if (!this._repo)
221
+ return [];
222
+ return this._repo.findClasses((cls) => cls.parent === this.qualifiedName);
187
223
  }
188
- /**
189
- * Creates a type registry from multiple parsed namespaces.
190
- *
191
- * @param namespaces - Array of parsed GIR namespaces
192
- * @returns A populated type registry
193
- *
194
- * @example
195
- * ```tsx
196
- * const parser = new GirParser();
197
- * const gtk = parser.parse(gtkGir);
198
- * const gio = parser.parse(gioGir);
199
- * const registry = TypeRegistry.fromNamespaces([gtk, gio]);
200
- * ```
201
- */
202
- static fromNamespaces(namespaces) {
203
- const registry = new TypeRegistry();
204
- for (const ns of namespaces) {
205
- for (const cls of ns.classes) {
206
- registry.registerNativeClass(ns.name, cls.name);
207
- }
208
- for (const iface of ns.interfaces) {
209
- registry.registerInterface(ns.name, iface.name);
210
- }
211
- for (const enumeration of ns.enumerations) {
212
- registry.registerEnum(ns.name, enumeration.name);
213
- }
214
- for (const bitfield of ns.bitfields) {
215
- registry.registerFlags(ns.name, bitfield.name);
216
- }
217
- for (const record of ns.records) {
218
- if (record.disguised)
219
- continue;
220
- if (record.glibTypeName) {
221
- registry.registerRecord(ns.name, record.name, record.glibTypeName, ns.sharedLibrary, record.glibGetType);
222
- }
223
- else if (record.fields.length > 0 && !record.opaque && hasOnlyPrimitiveFields(record.fields)) {
224
- const { size, fields } = calculateStructLayout(record.fields);
225
- registry.registerRecord(ns.name, record.name, undefined, ns.sharedLibrary, undefined, true, size, fields);
224
+ }
225
+ /**
226
+ * Normalized interface with helper methods.
227
+ */
228
+ export class GirInterface {
229
+ name;
230
+ qualifiedName;
231
+ cType;
232
+ glibTypeName;
233
+ prerequisites;
234
+ methods;
235
+ properties;
236
+ signals;
237
+ doc;
238
+ /** @internal */
239
+ _repo;
240
+ constructor(data) {
241
+ this.name = data.name;
242
+ this.qualifiedName = data.qualifiedName;
243
+ this.cType = data.cType;
244
+ this.glibTypeName = data.glibTypeName;
245
+ this.prerequisites = data.prerequisites;
246
+ this.methods = data.methods;
247
+ this.properties = data.properties;
248
+ this.signals = data.signals;
249
+ this.doc = data.doc;
250
+ }
251
+ /** @internal */
252
+ _setRepository(repo) {
253
+ this._repo = repo;
254
+ }
255
+ /** Checks if this interface has a prerequisite (direct or transitive). */
256
+ hasPrerequisite(qualifiedName) {
257
+ if (this.prerequisites.includes(qualifiedName))
258
+ return true;
259
+ if (!this._repo)
260
+ return false;
261
+ for (const prereq of this.prerequisites) {
262
+ const prereqIface = this._repo.resolveInterface(prereq);
263
+ if (prereqIface?.hasPrerequisite(qualifiedName))
264
+ return true;
265
+ }
266
+ return false;
267
+ }
268
+ /** Gets all prerequisites including transitive ones. */
269
+ getAllPrerequisites() {
270
+ const all = new Set(this.prerequisites);
271
+ if (this._repo) {
272
+ for (const prereq of this.prerequisites) {
273
+ const prereqIface = this._repo.resolveInterface(prereq);
274
+ if (prereqIface) {
275
+ for (const p of prereqIface.getAllPrerequisites()) {
276
+ all.add(p);
277
+ }
226
278
  }
227
279
  }
228
- for (const callback of ns.callbacks) {
229
- registry.registerCallback(ns.name, callback.name);
230
- }
231
280
  }
232
- return registry;
281
+ return [...all];
282
+ }
283
+ /** Finds a method by name. */
284
+ getMethod(name) {
285
+ return this.methods.find((m) => m.name === name) ?? null;
286
+ }
287
+ /** Finds a property by name. */
288
+ getProperty(name) {
289
+ return this.properties.find((p) => p.name === name) ?? null;
290
+ }
291
+ /** Finds a signal by name. */
292
+ getSignal(name) {
293
+ return this.signals.find((s) => s.name === name) ?? null;
233
294
  }
234
295
  }
235
- const STRING_TYPES = new Set(["utf8", "filename"]);
236
- const PRIMITIVE_TYPES = new Set([
237
- "gint",
238
- "guint",
239
- "gint8",
240
- "guint8",
241
- "gint16",
242
- "guint16",
243
- "gint32",
244
- "guint32",
245
- "gint64",
246
- "guint64",
247
- "gfloat",
248
- "gdouble",
249
- "gboolean",
250
- "gchar",
251
- "guchar",
252
- "gsize",
253
- "gssize",
254
- "glong",
255
- "gulong",
256
- ]);
257
296
  /**
258
- * Checks if a record has at least one public primitive field and all public fields are primitive.
259
- * Used to determine if a plain struct can be safely generated.
260
- * Records with only private fields are considered opaque and should not be plain structs.
297
+ * Normalized record (boxed type or plain struct) with helper methods.
261
298
  */
262
- const hasOnlyPrimitiveFields = (fields) => {
263
- const publicFields = fields.filter((f) => !f.private);
264
- if (publicFields.length === 0)
265
- return false;
266
- return publicFields.every((field) => PRIMITIVE_TYPES.has(field.type.name));
267
- };
299
+ export class GirRecord {
300
+ name;
301
+ qualifiedName;
302
+ cType;
303
+ opaque;
304
+ disguised;
305
+ glibTypeName;
306
+ glibGetType;
307
+ isGtypeStructFor;
308
+ fields;
309
+ methods;
310
+ constructors;
311
+ staticFunctions;
312
+ doc;
313
+ constructor(data) {
314
+ this.name = data.name;
315
+ this.qualifiedName = data.qualifiedName;
316
+ this.cType = data.cType;
317
+ this.opaque = data.opaque;
318
+ this.disguised = data.disguised;
319
+ this.glibTypeName = data.glibTypeName;
320
+ this.glibGetType = data.glibGetType;
321
+ this.isGtypeStructFor = data.isGtypeStructFor;
322
+ this.fields = data.fields;
323
+ this.methods = data.methods;
324
+ this.constructors = data.constructors;
325
+ this.staticFunctions = data.staticFunctions;
326
+ this.doc = data.doc;
327
+ }
328
+ /** True if this is a GLib boxed type (has glibTypeName). */
329
+ isBoxed() {
330
+ return this.glibTypeName !== undefined;
331
+ }
332
+ /** True if this is a GType struct (vtable for a class/interface). */
333
+ isGtypeStruct() {
334
+ return this.isGtypeStructFor !== undefined;
335
+ }
336
+ /** True if this is a plain C struct (no GType, has public fields). */
337
+ isPlainStruct() {
338
+ return !this.glibTypeName && !this.opaque && this.getPublicFields().length > 0;
339
+ }
340
+ /** Gets public (non-private) fields only. */
341
+ getPublicFields() {
342
+ return this.fields.filter((f) => !f.private);
343
+ }
344
+ /** Finds a method by name. */
345
+ getMethod(name) {
346
+ return this.methods.find((m) => m.name === name) ?? null;
347
+ }
348
+ /** Finds a field by name. */
349
+ getField(name) {
350
+ return this.fields.find((f) => f.name === name) ?? null;
351
+ }
352
+ /** Finds a constructor by name. */
353
+ getConstructor(name) {
354
+ return this.constructors.find((c) => c.name === name) ?? null;
355
+ }
356
+ }
268
357
  /**
269
- * Gets the size and alignment of a field type in bytes.
358
+ * Normalized enumeration with helper methods.
270
359
  */
271
- const getFieldSizeAndAlignment = (type) => {
272
- const typeName = type.name;
273
- if (["gboolean", "guint8", "gint8", "guchar", "gchar"].includes(typeName)) {
274
- return { size: 1, alignment: 1 };
275
- }
276
- if (["gint16", "guint16", "gshort", "gushort"].includes(typeName)) {
277
- return { size: 2, alignment: 2 };
360
+ export class GirEnumeration {
361
+ name;
362
+ qualifiedName;
363
+ cType;
364
+ members;
365
+ doc;
366
+ constructor(data) {
367
+ this.name = data.name;
368
+ this.qualifiedName = data.qualifiedName;
369
+ this.cType = data.cType;
370
+ this.members = data.members;
371
+ this.doc = data.doc;
372
+ }
373
+ /** Finds a member by name. */
374
+ getMember(name) {
375
+ return this.members.find((m) => m.name === name) ?? null;
376
+ }
377
+ /** Finds a member by value. */
378
+ getMemberByValue(value) {
379
+ return this.members.find((m) => m.value === value) ?? null;
380
+ }
381
+ /** Gets all member names. */
382
+ getMemberNames() {
383
+ return this.members.map((m) => m.name);
278
384
  }
279
- if (["gint", "guint", "gint32", "guint32", "gfloat", "float", "Quark", "GQuark"].includes(typeName)) {
280
- return { size: 4, alignment: 4 };
385
+ }
386
+ /**
387
+ * Normalized enumeration member.
388
+ */
389
+ export class GirEnumerationMember {
390
+ name;
391
+ value;
392
+ cIdentifier;
393
+ doc;
394
+ constructor(data) {
395
+ this.name = data.name;
396
+ this.value = data.value;
397
+ this.cIdentifier = data.cIdentifier;
398
+ this.doc = data.doc;
281
399
  }
282
- if (["gint64", "guint64", "gdouble", "double", "glong", "gulong", "gsize", "gssize", "GType", "gpointer"].includes(typeName)) {
283
- return { size: 8, alignment: 8 };
400
+ }
401
+ /**
402
+ * Normalized callback type.
403
+ */
404
+ export class GirCallback {
405
+ name;
406
+ qualifiedName;
407
+ cType;
408
+ returnType;
409
+ parameters;
410
+ doc;
411
+ constructor(data) {
412
+ this.name = data.name;
413
+ this.qualifiedName = data.qualifiedName;
414
+ this.cType = data.cType;
415
+ this.returnType = data.returnType;
416
+ this.parameters = data.parameters;
417
+ this.doc = data.doc;
284
418
  }
285
- return { size: 8, alignment: 8 };
286
- };
419
+ }
287
420
  /**
288
- * Calculates the memory layout of a struct (size and field offsets).
421
+ * Normalized constant.
289
422
  */
290
- const calculateStructLayout = (fields) => {
291
- const result = [];
292
- let currentOffset = 0;
293
- let maxAlignment = 1;
294
- for (const field of fields) {
295
- if (field.private)
296
- continue;
297
- const { size, alignment } = getFieldSizeAndAlignment(field.type);
298
- maxAlignment = Math.max(maxAlignment, alignment);
299
- currentOffset = Math.ceil(currentOffset / alignment) * alignment;
300
- result.push({
301
- name: field.name,
302
- offset: currentOffset,
303
- type: field.type.name,
304
- });
305
- currentOffset += size;
306
- }
307
- const finalSize = Math.ceil(currentOffset / maxAlignment) * maxAlignment;
308
- return { size: finalSize, fields: result };
309
- };
310
- const POINTER_TYPE = { ts: "number", ffi: { type: "int", size: 64, unsigned: true } };
311
- const C_TYPE_MAP = new Map([
312
- ["void", { ts: "void", ffi: { type: "undefined" } }],
313
- ["gboolean", { ts: "boolean", ffi: { type: "boolean" } }],
314
- ["gchar", { ts: "number", ffi: { type: "int", size: 8, unsigned: false } }],
315
- ["guchar", { ts: "number", ffi: { type: "int", size: 8, unsigned: true } }],
316
- ["gint", { ts: "number", ffi: { type: "int", size: 32, unsigned: false } }],
317
- ["guint", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
318
- ["gshort", { ts: "number", ffi: { type: "int", size: 16, unsigned: false } }],
319
- ["gushort", { ts: "number", ffi: { type: "int", size: 16, unsigned: true } }],
320
- ["glong", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
321
- ["gulong", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
322
- ["gint8", { ts: "number", ffi: { type: "int", size: 8, unsigned: false } }],
323
- ["guint8", { ts: "number", ffi: { type: "int", size: 8, unsigned: true } }],
324
- ["gint16", { ts: "number", ffi: { type: "int", size: 16, unsigned: false } }],
325
- ["guint16", { ts: "number", ffi: { type: "int", size: 16, unsigned: true } }],
326
- ["gint32", { ts: "number", ffi: { type: "int", size: 32, unsigned: false } }],
327
- ["guint32", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
328
- ["gint64", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
329
- ["guint64", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
330
- ["gfloat", { ts: "number", ffi: { type: "float", size: 32 } }],
331
- ["gdouble", { ts: "number", ffi: { type: "float", size: 64 } }],
332
- ["gsize", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
333
- ["gssize", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
334
- ["goffset", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
335
- ["int", { ts: "number", ffi: { type: "int", size: 32, unsigned: false } }],
336
- ["unsigned int", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
337
- ["long", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
338
- ["unsigned long", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
339
- ["double", { ts: "number", ffi: { type: "float", size: 64 } }],
340
- ["float", { ts: "number", ffi: { type: "float", size: 32 } }],
341
- ["size_t", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
342
- ["ssize_t", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
343
- ["GType", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
344
- ["GQuark", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
345
- ]);
346
- const mapCType = (cType) => {
347
- if (!cType) {
348
- return POINTER_TYPE;
349
- }
350
- if (cType.endsWith("*")) {
351
- return POINTER_TYPE;
352
- }
353
- const mapped = C_TYPE_MAP.get(cType);
354
- if (mapped) {
355
- return mapped;
356
- }
357
- if (cType.startsWith("const ")) {
358
- return mapCType(cType.slice(6));
359
- }
360
- return POINTER_TYPE;
361
- };
362
- const BASIC_TYPE_MAP = new Map([
363
- ["gboolean", { ts: "boolean", ffi: { type: "boolean" } }],
364
- ["gchar", { ts: "number", ffi: { type: "int", size: 8, unsigned: false } }],
365
- ["guchar", { ts: "number", ffi: { type: "int", size: 8, unsigned: true } }],
366
- ["gint", { ts: "number", ffi: { type: "int", size: 32, unsigned: false } }],
367
- ["guint", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
368
- ["gshort", { ts: "number", ffi: { type: "int", size: 16, unsigned: false } }],
369
- ["gushort", { ts: "number", ffi: { type: "int", size: 16, unsigned: true } }],
370
- ["glong", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
371
- ["gulong", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
372
- ["GType", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
373
- ["gint8", { ts: "number", ffi: { type: "int", size: 8, unsigned: false } }],
374
- ["guint8", { ts: "number", ffi: { type: "int", size: 8, unsigned: true } }],
375
- ["gint16", { ts: "number", ffi: { type: "int", size: 16, unsigned: false } }],
376
- ["guint16", { ts: "number", ffi: { type: "int", size: 16, unsigned: true } }],
377
- ["gint32", { ts: "number", ffi: { type: "int", size: 32, unsigned: false } }],
378
- ["guint32", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
379
- ["gint64", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
380
- ["guint64", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
381
- ["gfloat", { ts: "number", ffi: { type: "float", size: 32 } }],
382
- ["gdouble", { ts: "number", ffi: { type: "float", size: 64 } }],
383
- ["gpointer", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
384
- ["gconstpointer", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
385
- ["gsize", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
386
- ["gssize", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
387
- ["goffset", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
388
- ["guintptr", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
389
- ["gintptr", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
390
- ["pid_t", { ts: "number", ffi: { type: "int", size: 32, unsigned: false } }],
391
- ["uid_t", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
392
- ["gid_t", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
393
- ["time_t", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
394
- ["Quark", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
395
- ["GLib.Quark", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
396
- ["TimeSpan", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
397
- ["GLib.TimeSpan", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
398
- ["DateDay", { ts: "number", ffi: { type: "int", size: 8, unsigned: true } }],
399
- ["GLib.DateDay", { ts: "number", ffi: { type: "int", size: 8, unsigned: true } }],
400
- ["DateYear", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
401
- ["GLib.DateYear", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
402
- ["DateMonth", { ts: "number", ffi: { type: "int", size: 32, unsigned: false } }],
403
- ["GLib.DateMonth", { ts: "number", ffi: { type: "int", size: 32, unsigned: false } }],
404
- ["Pid", { ts: "number", ffi: { type: "int", size: 32, unsigned: false } }],
405
- ["GLib.Pid", { ts: "number", ffi: { type: "int", size: 32, unsigned: false } }],
406
- ["void", { ts: "void", ffi: { type: "undefined" } }],
407
- ["none", { ts: "void", ffi: { type: "undefined" } }],
408
- ["int", { ts: "number", ffi: { type: "int", size: 32, unsigned: false } }],
409
- ["uint", { ts: "number", ffi: { type: "int", size: 32, unsigned: true } }],
410
- ["long", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
411
- ["ulong", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
412
- ["size_t", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
413
- ["ssize_t", { ts: "number", ffi: { type: "int", size: 64, unsigned: false } }],
414
- ["double", { ts: "number", ffi: { type: "float", size: 64 } }],
415
- ["float", { ts: "number", ffi: { type: "float", size: 32 } }],
416
- ["GLib.DestroyNotify", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
417
- ["DestroyNotify", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
418
- ["GLib.FreeFunc", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
419
- ["FreeFunc", { ts: "number", ffi: { type: "int", size: 64, unsigned: true } }],
420
- ]);
423
+ export class GirConstant {
424
+ name;
425
+ qualifiedName;
426
+ cType;
427
+ value;
428
+ type;
429
+ doc;
430
+ constructor(data) {
431
+ this.name = data.name;
432
+ this.qualifiedName = data.qualifiedName;
433
+ this.cType = data.cType;
434
+ this.value = data.value;
435
+ this.type = data.type;
436
+ this.doc = data.doc;
437
+ }
438
+ }
421
439
  /**
422
- * Maps GIR types to TypeScript and FFI type representations.
423
- *
424
- * Handles conversion of GIR type information into:
425
- * - TypeScript type strings for generated declarations
426
- * - FFI type descriptors for runtime marshalling
427
- *
428
- * Supports callbacks for tracking type usage dependencies.
429
- *
430
- * @example
431
- * ```tsx
432
- * const mapper = new TypeMapper();
433
- * mapper.registerEnum("Orientation", "Orientation");
434
- * mapper.setTypeRegistry(registry, "Gtk");
435
- *
436
- * const result = mapper.mapType({ name: "utf8" });
437
- * // { ts: "string", ffi: { type: "string", borrowed: false } }
438
- * ```
440
+ * Normalized method with helper methods.
439
441
  */
440
- export class TypeMapper {
441
- enumNames = new Set();
442
- enumTransforms = new Map();
443
- flagsNames = new Set();
444
- flagsTransforms = new Map();
445
- recordNames = new Set();
446
- recordTransforms = new Map();
447
- recordGlibTypes = new Map();
448
- skippedClasses = new Set();
449
- onEnumUsed;
450
- onRecordUsed;
451
- onExternalTypeUsed;
452
- onSameNamespaceClassUsed;
453
- typeRegistry;
454
- currentNamespace;
455
- forceExternalNamespace;
456
- /**
457
- * Registers an enumeration for type mapping.
458
- *
459
- * @param originalName - The GIR enum name
460
- * @param transformedName - Optional transformed TypeScript name
461
- */
462
- registerEnum(originalName, transformedName) {
463
- this.enumNames.add(originalName);
464
- if (transformedName) {
465
- this.enumTransforms.set(originalName, transformedName);
442
+ export class GirMethod {
443
+ name;
444
+ cIdentifier;
445
+ returnType;
446
+ parameters;
447
+ throws;
448
+ doc;
449
+ returnDoc;
450
+ /** For async methods, the name of the corresponding finish function */
451
+ finishFunc;
452
+ constructor(data) {
453
+ this.name = data.name;
454
+ this.cIdentifier = data.cIdentifier;
455
+ this.returnType = data.returnType;
456
+ this.parameters = data.parameters;
457
+ this.throws = data.throws;
458
+ this.doc = data.doc;
459
+ this.returnDoc = data.returnDoc;
460
+ this.finishFunc = data.finishFunc;
461
+ }
462
+ /** True if this follows the async/finish pattern. */
463
+ isAsync() {
464
+ return this.name.endsWith("_async") || this.parameters.some((p) => p.scope === "async");
465
+ }
466
+ /** True if this is a _finish method for an async operation. */
467
+ isAsyncFinish() {
468
+ return this.name.endsWith("_finish");
469
+ }
470
+ /** Gets the corresponding _finish method name if this is async. */
471
+ getFinishMethodName() {
472
+ if (this.name.endsWith("_async")) {
473
+ return this.name.replace(/_async$/, "_finish");
466
474
  }
475
+ return null;
467
476
  }
468
- /**
469
- * Registers a flags (bitfield) type for type mapping.
470
- *
471
- * @param originalName - The GIR flags name
472
- * @param transformedName - Optional transformed TypeScript name
473
- */
474
- registerFlags(originalName, transformedName) {
475
- this.flagsNames.add(originalName);
476
- if (transformedName) {
477
- this.flagsTransforms.set(originalName, transformedName);
478
- }
477
+ /** Gets required (non-optional, non-nullable) parameters. */
478
+ getRequiredParameters() {
479
+ return this.parameters.filter((p) => !p.optional && !p.nullable && p.direction === "in");
479
480
  }
480
- /**
481
- * Registers a record for type mapping.
482
- *
483
- * @param originalName - The GIR record name
484
- * @param transformedName - Optional transformed TypeScript name
485
- * @param glibTypeName - Optional GLib type name for boxed type marshalling
486
- */
487
- registerRecord(originalName, transformedName, glibTypeName) {
488
- this.recordNames.add(originalName);
489
- if (transformedName) {
490
- this.recordTransforms.set(originalName, transformedName);
491
- }
492
- if (glibTypeName) {
493
- this.recordGlibTypes.set(originalName, glibTypeName);
494
- }
481
+ /** Gets optional parameters. */
482
+ getOptionalParameters() {
483
+ return this.parameters.filter((p) => p.optional || p.nullable);
495
484
  }
496
- setEnumUsageCallback(callback) {
497
- this.onEnumUsed = callback ?? undefined;
498
- }
499
- getEnumUsageCallback() {
500
- return this.onEnumUsed ?? null;
501
- }
502
- setRecordUsageCallback(callback) {
503
- this.onRecordUsed = callback ?? undefined;
504
- }
505
- getRecordUsageCallback() {
506
- return this.onRecordUsed ?? null;
507
- }
508
- setExternalTypeUsageCallback(callback) {
509
- this.onExternalTypeUsed = callback ?? undefined;
510
- }
511
- getExternalTypeUsageCallback() {
512
- return this.onExternalTypeUsed ?? null;
513
- }
514
- setSameNamespaceClassUsageCallback(callback) {
515
- this.onSameNamespaceClassUsed = callback ?? undefined;
516
- }
517
- getSameNamespaceClassUsageCallback() {
518
- return this.onSameNamespaceClassUsed ?? null;
519
- }
520
- /**
521
- * Sets the type registry for cross-namespace type resolution.
522
- *
523
- * @param registry - The type registry to use
524
- * @param currentNamespace - The current namespace being processed
525
- */
526
- setTypeRegistry(registry, currentNamespace) {
527
- this.typeRegistry = registry;
528
- this.currentNamespace = currentNamespace;
529
- }
530
- setForceExternalNamespace(namespace) {
531
- this.forceExternalNamespace = namespace ?? undefined;
532
- }
533
- registerSkippedClass(name) {
534
- this.skippedClasses.add(name);
535
- }
536
- clearSkippedClasses() {
537
- this.skippedClasses.clear();
538
- }
539
- /**
540
- * Maps a GIR type to TypeScript and FFI representations.
541
- *
542
- * @param girType - The GIR type to map
543
- * @param isReturn - Whether this is a return type (affects ownership)
544
- * @param parentTransferOwnership - Transfer ownership from parent (used for array elements)
545
- * @returns The mapped type with TypeScript and FFI descriptors
546
- */
547
- mapType(girType, isReturn = false, parentTransferOwnership) {
548
- if (girType.isArray || girType.name === "array") {
549
- const listType = girType.cType?.includes("GSList")
550
- ? "gslist"
551
- : girType.cType?.includes("GList")
552
- ? "glist"
553
- : undefined;
554
- if (girType.elementType) {
555
- const elementTransferOwnership = girType.transferOwnership ?? parentTransferOwnership;
556
- const elementType = this.mapType(girType.elementType, isReturn, elementTransferOwnership);
557
- return {
558
- ts: `${elementType.ts}[]`,
559
- ffi: listType
560
- ? { type: "array", itemType: elementType.ffi, listType, borrowed: isReturn }
561
- : { type: "array", itemType: elementType.ffi },
562
- };
563
- }
564
- return {
565
- ts: `unknown[]`,
566
- ffi: listType
567
- ? { type: "array", itemType: { type: "undefined" }, listType, borrowed: isReturn }
568
- : { type: "array", itemType: { type: "undefined" } },
569
- };
570
- }
571
- if (STRING_TYPES.has(girType.name)) {
572
- const effectiveTransferOwnership = girType.transferOwnership ?? parentTransferOwnership;
573
- const borrowed = effectiveTransferOwnership === "none";
574
- return {
575
- ts: "string",
576
- ffi: { type: "string", borrowed },
577
- };
578
- }
579
- const basicType = BASIC_TYPE_MAP.get(girType.name);
580
- if (basicType) {
581
- return basicType;
582
- }
583
- if (this.typeRegistry && this.currentNamespace && !girType.name.includes(".")) {
584
- const registered = this.typeRegistry.resolveInNamespace(girType.name, this.currentNamespace);
585
- if (registered) {
586
- const isExternal = registered.namespace !== this.currentNamespace ||
587
- registered.namespace === this.forceExternalNamespace;
588
- const qualifiedName = isExternal
589
- ? `${registered.namespace}.${registered.transformedName}`
590
- : registered.transformedName;
591
- const externalType = isExternal
592
- ? {
593
- namespace: registered.namespace,
594
- name: registered.name,
595
- transformedName: registered.transformedName,
596
- kind: registered.kind,
597
- }
598
- : undefined;
599
- if (isExternal) {
600
- this.onExternalTypeUsed?.(externalType);
601
- }
602
- else if (registered.kind === "class" || registered.kind === "interface") {
603
- if (this.skippedClasses.has(registered.name)) {
604
- return {
605
- ts: "unknown",
606
- ffi: { type: "gobject", borrowed: isReturn },
607
- };
608
- }
609
- this.onSameNamespaceClassUsed?.(registered.transformedName, registered.name);
610
- }
611
- else if (registered.kind === "enum" || registered.kind === "flags") {
612
- this.onEnumUsed?.(registered.transformedName);
613
- }
614
- else if (registered.kind === "record") {
615
- this.onRecordUsed?.(registered.transformedName);
616
- }
617
- if (registered.kind === "enum") {
618
- return {
619
- ts: qualifiedName,
620
- ffi: { type: "int", size: 32, unsigned: false },
621
- externalType,
622
- };
623
- }
624
- if (registered.kind === "flags") {
625
- return {
626
- ts: qualifiedName,
627
- ffi: { type: "int", size: 32, unsigned: true },
628
- externalType,
629
- };
630
- }
631
- if (registered.kind === "record") {
632
- if (registered.name === "Variant" && registered.namespace === "GLib") {
633
- return {
634
- ts: qualifiedName,
635
- ffi: { type: "gvariant", borrowed: isReturn },
636
- externalType,
637
- kind: registered.kind,
638
- };
639
- }
640
- if (registered.isPlainStruct) {
641
- return {
642
- ts: qualifiedName,
643
- ffi: {
644
- type: "struct",
645
- borrowed: isReturn,
646
- innerType: registered.transformedName,
647
- size: registered.structSize,
648
- },
649
- externalType,
650
- kind: registered.kind,
651
- };
652
- }
653
- return {
654
- ts: qualifiedName,
655
- ffi: {
656
- type: "boxed",
657
- borrowed: isReturn,
658
- innerType: registered.glibTypeName ?? registered.transformedName,
659
- lib: registered.sharedLibrary,
660
- getTypeFn: registered.glibGetType,
661
- },
662
- externalType,
663
- kind: registered.kind,
664
- };
665
- }
666
- if (registered.kind === "callback") {
667
- return POINTER_TYPE;
668
- }
669
- if (registered.name === "ParamSpec" && registered.namespace === "GObject") {
670
- return {
671
- ts: qualifiedName,
672
- ffi: { type: "gparam", borrowed: isReturn },
673
- externalType,
674
- kind: registered.kind,
675
- };
676
- }
677
- return {
678
- ts: qualifiedName,
679
- ffi: { type: "gobject", borrowed: isReturn },
680
- externalType,
681
- kind: registered.kind,
682
- };
683
- }
684
- }
685
- if (this.enumNames.has(girType.name)) {
686
- const transformedName = this.enumTransforms.get(girType.name) ?? girType.name;
687
- this.onEnumUsed?.(transformedName);
688
- return {
689
- ts: transformedName,
690
- ffi: { type: "int", size: 32, unsigned: false },
691
- };
692
- }
693
- if (this.flagsNames.has(girType.name)) {
694
- const transformedName = this.flagsTransforms.get(girType.name) ?? girType.name;
695
- this.onEnumUsed?.(transformedName);
696
- return {
697
- ts: transformedName,
698
- ffi: { type: "int", size: 32, unsigned: true },
699
- };
700
- }
701
- if (this.recordNames.has(girType.name)) {
702
- const transformedName = this.recordTransforms.get(girType.name) ?? girType.name;
703
- const glibTypeName = this.recordGlibTypes.get(girType.name) ?? transformedName;
704
- this.onRecordUsed?.(transformedName);
705
- return {
706
- ts: transformedName,
707
- ffi: { type: "boxed", borrowed: isReturn, innerType: glibTypeName },
708
- };
709
- }
710
- if (girType.name.includes(".")) {
711
- const [ns, typeName] = girType.name.split(".", 2);
712
- if (typeName && ns === this.currentNamespace) {
713
- if (this.enumNames.has(typeName)) {
714
- const transformedName = this.enumTransforms.get(typeName) ?? typeName;
715
- this.onEnumUsed?.(transformedName);
716
- return {
717
- ts: transformedName,
718
- ffi: { type: "int", size: 32, unsigned: false },
719
- };
720
- }
721
- if (this.flagsNames.has(typeName)) {
722
- const transformedName = this.flagsTransforms.get(typeName) ?? typeName;
723
- this.onEnumUsed?.(transformedName);
724
- return {
725
- ts: transformedName,
726
- ffi: { type: "int", size: 32, unsigned: true },
727
- };
728
- }
729
- if (this.recordNames.has(typeName)) {
730
- const transformedName = this.recordTransforms.get(typeName) ?? typeName;
731
- const glibTypeName = this.recordGlibTypes.get(typeName) ?? transformedName;
732
- this.onRecordUsed?.(transformedName);
733
- return {
734
- ts: transformedName,
735
- ffi: { type: "boxed", borrowed: isReturn, innerType: glibTypeName },
736
- };
737
- }
738
- }
739
- if (this.typeRegistry && ns && typeName) {
740
- const registered = this.typeRegistry.resolve(girType.name);
741
- if (registered) {
742
- const isExternal = registered.namespace !== this.currentNamespace ||
743
- registered.namespace === this.forceExternalNamespace;
744
- const qualifiedName = isExternal
745
- ? `${registered.namespace}.${registered.transformedName}`
746
- : registered.transformedName;
747
- const externalType = {
748
- namespace: registered.namespace,
749
- name: registered.name,
750
- transformedName: registered.transformedName,
751
- kind: registered.kind,
752
- };
753
- if (isExternal) {
754
- this.onExternalTypeUsed?.(externalType);
755
- }
756
- if (registered.kind === "enum") {
757
- return {
758
- ts: qualifiedName,
759
- ffi: { type: "int", size: 32, unsigned: false },
760
- externalType: isExternal ? externalType : undefined,
761
- };
762
- }
763
- if (registered.kind === "flags") {
764
- return {
765
- ts: qualifiedName,
766
- ffi: { type: "int", size: 32, unsigned: true },
767
- externalType: isExternal ? externalType : undefined,
768
- };
769
- }
770
- if (registered.kind === "record") {
771
- if (registered.name === "Variant" && registered.namespace === "GLib") {
772
- return {
773
- ts: qualifiedName,
774
- ffi: { type: "gvariant", borrowed: isReturn },
775
- externalType: isExternal ? externalType : undefined,
776
- };
777
- }
778
- if (registered.isPlainStruct) {
779
- return {
780
- ts: qualifiedName,
781
- ffi: {
782
- type: "struct",
783
- borrowed: isReturn,
784
- innerType: registered.transformedName,
785
- size: registered.structSize,
786
- },
787
- externalType: isExternal ? externalType : undefined,
788
- kind: registered.kind,
789
- };
790
- }
791
- return {
792
- ts: qualifiedName,
793
- ffi: {
794
- type: "boxed",
795
- borrowed: isReturn,
796
- innerType: registered.glibTypeName ?? registered.transformedName,
797
- lib: registered.sharedLibrary,
798
- getTypeFn: registered.glibGetType,
799
- },
800
- externalType: isExternal ? externalType : undefined,
801
- };
802
- }
803
- if (registered.kind === "callback") {
804
- return POINTER_TYPE;
805
- }
806
- if (registered.name === "ParamSpec" && registered.namespace === "GObject") {
807
- return {
808
- ts: qualifiedName,
809
- ffi: { type: "gparam", borrowed: isReturn },
810
- externalType: isExternal ? externalType : undefined,
811
- kind: registered.kind,
812
- };
813
- }
814
- return {
815
- ts: qualifiedName,
816
- ffi: { type: "gobject", borrowed: isReturn },
817
- externalType: isExternal ? externalType : undefined,
818
- kind: registered.kind,
819
- };
820
- }
821
- }
822
- return mapCType(girType.cType);
823
- }
824
- if (this.typeRegistry && this.currentNamespace) {
825
- const registered = this.typeRegistry.resolveInNamespace(girType.name, this.currentNamespace);
826
- if (registered) {
827
- const isExternal = registered.namespace !== this.currentNamespace ||
828
- registered.namespace === this.forceExternalNamespace;
829
- const qualifiedName = isExternal
830
- ? `${registered.namespace}.${registered.transformedName}`
831
- : registered.transformedName;
832
- const externalType = isExternal
833
- ? {
834
- namespace: registered.namespace,
835
- name: registered.name,
836
- transformedName: registered.transformedName,
837
- kind: registered.kind,
838
- }
839
- : undefined;
840
- if (isExternal) {
841
- this.onExternalTypeUsed?.(externalType);
842
- }
843
- else if (registered.kind === "class" || registered.kind === "interface") {
844
- if (this.skippedClasses.has(registered.name)) {
845
- return {
846
- ts: "unknown",
847
- ffi: { type: "gobject", borrowed: isReturn },
848
- };
849
- }
850
- this.onSameNamespaceClassUsed?.(registered.transformedName, registered.name);
851
- }
852
- if (registered.kind === "enum") {
853
- return {
854
- ts: qualifiedName,
855
- ffi: { type: "int", size: 32, unsigned: false },
856
- externalType,
857
- };
858
- }
859
- if (registered.kind === "flags") {
860
- return {
861
- ts: qualifiedName,
862
- ffi: { type: "int", size: 32, unsigned: true },
863
- externalType,
864
- };
865
- }
866
- if (registered.kind === "record") {
867
- if (registered.isPlainStruct) {
868
- return {
869
- ts: qualifiedName,
870
- ffi: {
871
- type: "struct",
872
- borrowed: isReturn,
873
- innerType: registered.transformedName,
874
- size: registered.structSize,
875
- },
876
- externalType,
877
- kind: registered.kind,
878
- };
879
- }
880
- return {
881
- ts: qualifiedName,
882
- ffi: {
883
- type: "boxed",
884
- borrowed: isReturn,
885
- innerType: registered.glibTypeName ?? registered.transformedName,
886
- lib: registered.sharedLibrary,
887
- getTypeFn: registered.glibGetType,
888
- },
889
- externalType,
890
- kind: registered.kind,
891
- };
892
- }
893
- if (registered.kind === "callback") {
894
- return POINTER_TYPE;
895
- }
896
- if (registered.name === "ParamSpec" && registered.namespace === "GObject") {
897
- return {
898
- ts: qualifiedName,
899
- ffi: { type: "gparam", borrowed: isReturn },
900
- externalType,
901
- kind: registered.kind,
902
- };
903
- }
904
- return {
905
- ts: qualifiedName,
906
- ffi: { type: "gobject", borrowed: isReturn },
907
- externalType,
908
- kind: registered.kind,
909
- };
910
- }
911
- }
912
- return mapCType(girType.cType);
913
- }
914
- isCallback(typeName) {
915
- if (this.typeRegistry) {
916
- const resolved = this.currentNamespace
917
- ? this.typeRegistry.resolveInNamespace(typeName, this.currentNamespace)
918
- : this.typeRegistry.resolve(typeName);
919
- return resolved?.kind === "callback";
920
- }
921
- return false;
485
+ /** True if any parameter is an out parameter. */
486
+ hasOutParameters() {
487
+ return this.parameters.some((p) => p.direction === "out" || p.direction === "inout");
922
488
  }
923
- /**
924
- * Maps a function parameter to TypeScript and FFI representations.
925
- *
926
- * Handles special cases like out parameters, callbacks, and
927
- * ownership transfer.
928
- *
929
- * @param param - The GIR parameter to map
930
- * @returns The mapped type with TypeScript and FFI descriptors
931
- */
932
- mapParameter(param) {
933
- if (param.direction === "out" || param.direction === "inout") {
934
- const innerType = this.mapType(param.type);
935
- const isBoxedOrGObjectOrStruct = innerType.ffi.type === "boxed" || innerType.ffi.type === "gobject" || innerType.ffi.type === "struct";
936
- if (param.callerAllocates && isBoxedOrGObjectOrStruct) {
937
- return {
938
- ...innerType,
939
- ffi: {
940
- ...innerType.ffi,
941
- borrowed: true,
942
- },
943
- };
944
- }
945
- if (!isBoxedOrGObjectOrStruct) {
946
- return {
947
- ts: `Ref<${innerType.ts}>`,
948
- ffi: {
949
- type: "ref",
950
- innerType: innerType.ffi,
951
- },
952
- };
953
- }
954
- return {
955
- ts: `Ref<${innerType.ts}>`,
956
- ffi: {
957
- type: "ref",
958
- innerType: innerType.ffi,
959
- },
960
- externalType: innerType.externalType,
961
- kind: innerType.kind,
962
- };
963
- }
964
- if (param.type.name === "Gio.AsyncReadyCallback") {
965
- return {
966
- ts: "(source: unknown, result: unknown) => void",
967
- ffi: {
968
- type: "callback",
969
- trampoline: "asyncReady",
970
- sourceType: { type: "gobject", borrowed: true },
971
- resultType: { type: "gobject", borrowed: true },
972
- },
973
- };
974
- }
975
- if (param.type.name === "GLib.DestroyNotify" || param.type.name === "DestroyNotify") {
976
- return {
977
- ts: "() => void",
978
- ffi: {
979
- type: "callback",
980
- trampoline: "destroy",
981
- },
982
- };
983
- }
984
- if (param.type.name === "Gtk.DrawingAreaDrawFunc" || param.type.name === "DrawingAreaDrawFunc") {
985
- this.onExternalTypeUsed?.({
986
- namespace: "cairo",
987
- name: "Context",
988
- transformedName: "Context",
989
- kind: "record",
990
- });
991
- this.onSameNamespaceClassUsed?.("DrawingArea", "DrawingArea");
992
- return {
993
- ts: "(self: DrawingArea, cr: cairo.Context, width: number, height: number) => void",
994
- ffi: {
995
- type: "callback",
996
- trampoline: "drawFunc",
997
- argTypes: [
998
- { type: "gobject", borrowed: true },
999
- {
1000
- type: "boxed",
1001
- borrowed: true,
1002
- innerType: "CairoContext",
1003
- lib: "libcairo-gobject.so.2",
1004
- getTypeFn: "cairo_gobject_context_get_type",
1005
- },
1006
- { type: "int", size: 32, unsigned: false },
1007
- { type: "int", size: 32, unsigned: false },
1008
- ],
1009
- },
1010
- };
1011
- }
1012
- if (param.type.name === "Gtk.ShortcutFunc" || param.type.name === "ShortcutFunc") {
1013
- this.onSameNamespaceClassUsed?.("Widget", "Widget");
1014
- this.onExternalTypeUsed?.({
1015
- namespace: "GLib",
1016
- name: "Variant",
1017
- transformedName: "Variant",
1018
- kind: "record",
1019
- });
1020
- return {
1021
- ts: "(widget: Widget, args: GLib.Variant | null) => boolean",
1022
- ffi: {
1023
- type: "callback",
1024
- trampoline: "shortcutFunc",
1025
- argTypes: [
1026
- { type: "gobject", borrowed: true },
1027
- { type: "gvariant", borrowed: true },
1028
- ],
1029
- returnType: { type: "boolean" },
1030
- },
1031
- };
1032
- }
1033
- if (param.type.name === "Gtk.TreeListModelCreateModelFunc" ||
1034
- param.type.name === "TreeListModelCreateModelFunc") {
1035
- this.onExternalTypeUsed?.({
1036
- namespace: "GObject",
1037
- name: "Object",
1038
- transformedName: "GObject",
1039
- kind: "class",
1040
- });
1041
- this.onExternalTypeUsed?.({
1042
- namespace: "Gio",
1043
- name: "ListModel",
1044
- transformedName: "ListModel",
1045
- kind: "interface",
1046
- });
1047
- return {
1048
- ts: "(item: GObject.GObject) => Gio.ListModel | null",
1049
- ffi: {
1050
- type: "callback",
1051
- trampoline: "treeListModelCreateFunc",
1052
- argTypes: [{ type: "gobject", borrowed: true }],
1053
- returnType: { type: "gobject", borrowed: true },
1054
- },
1055
- };
1056
- }
1057
- if (param.type.name === "GLib.Closure" || this.isCallback(param.type.name)) {
1058
- return {
1059
- ts: "(...args: unknown[]) => unknown",
1060
- ffi: { type: "callback" },
1061
- };
1062
- }
1063
- const mapped = this.mapType(param.type, false, param.transferOwnership);
1064
- const isObjectType = mapped.ffi.type === "gobject" || mapped.ffi.type === "boxed";
1065
- const isTransferFull = param.transferOwnership === "full";
1066
- const isTransferNone = param.transferOwnership === "none" || param.transferOwnership === undefined;
1067
- if (isObjectType && isTransferFull) {
1068
- return {
1069
- ts: mapped.ts,
1070
- ffi: { ...mapped.ffi, borrowed: false },
1071
- externalType: mapped.externalType,
1072
- kind: mapped.kind,
1073
- };
1074
- }
1075
- if (isObjectType && isTransferNone) {
1076
- return {
1077
- ts: mapped.ts,
1078
- ffi: { ...mapped.ffi, borrowed: true },
1079
- externalType: mapped.externalType,
1080
- kind: mapped.kind,
1081
- };
1082
- }
1083
- return mapped;
1084
- }
1085
- isClosureTarget(paramIndex, allParams) {
1086
- const trampolineCallbacks = [
1087
- "Gio.AsyncReadyCallback",
1088
- "Gtk.DrawingAreaDrawFunc",
1089
- "DrawingAreaDrawFunc",
1090
- "GLib.CompareDataFunc",
1091
- "CompareDataFunc",
1092
- "GLib.SourceFunc",
1093
- "SourceFunc",
1094
- "Gtk.TickCallback",
1095
- "TickCallback",
1096
- "Gtk.ShortcutFunc",
1097
- "ShortcutFunc",
1098
- "Gtk.TreeListModelCreateModelFunc",
1099
- "TreeListModelCreateModelFunc",
1100
- ];
1101
- return allParams.some((p) => trampolineCallbacks.includes(p.type.name) && (p.closure === paramIndex || p.destroy === paramIndex));
1102
- }
1103
- isNullable(param) {
1104
- return param.nullable === true || param.optional === true;
1105
- }
1106
- hasUnsupportedCallback(param) {
1107
- const supportedCallbacks = [
1108
- "Gio.AsyncReadyCallback",
1109
- "GLib.DestroyNotify",
1110
- "DestroyNotify",
1111
- "Gtk.DrawingAreaDrawFunc",
1112
- "DrawingAreaDrawFunc",
1113
- "Gtk.ShortcutFunc",
1114
- "ShortcutFunc",
1115
- "Gtk.TreeListModelCreateModelFunc",
1116
- "TreeListModelCreateModelFunc",
1117
- ];
1118
- if (supportedCallbacks.includes(param.type.name)) {
1119
- return false;
1120
- }
1121
- return param.type.name === "GLib.Closure" || this.isCallback(param.type.name);
489
+ /** Gets out parameters only. */
490
+ getOutParameters() {
491
+ return this.parameters.filter((p) => p.direction === "out" || p.direction === "inout");
492
+ }
493
+ }
494
+ /**
495
+ * Normalized constructor.
496
+ */
497
+ export class GirConstructor {
498
+ name;
499
+ cIdentifier;
500
+ returnType;
501
+ parameters;
502
+ throws;
503
+ doc;
504
+ returnDoc;
505
+ constructor(data) {
506
+ this.name = data.name;
507
+ this.cIdentifier = data.cIdentifier;
508
+ this.returnType = data.returnType;
509
+ this.parameters = data.parameters;
510
+ this.throws = data.throws;
511
+ this.doc = data.doc;
512
+ this.returnDoc = data.returnDoc;
513
+ }
514
+ /** Gets required (non-optional, non-nullable) parameters. */
515
+ getRequiredParameters() {
516
+ return this.parameters.filter((p) => !p.optional && !p.nullable && p.direction === "in");
517
+ }
518
+ }
519
+ /**
520
+ * Normalized standalone function.
521
+ */
522
+ export class GirFunction {
523
+ name;
524
+ cIdentifier;
525
+ returnType;
526
+ parameters;
527
+ throws;
528
+ doc;
529
+ returnDoc;
530
+ constructor(data) {
531
+ this.name = data.name;
532
+ this.cIdentifier = data.cIdentifier;
533
+ this.returnType = data.returnType;
534
+ this.parameters = data.parameters;
535
+ this.throws = data.throws;
536
+ this.doc = data.doc;
537
+ this.returnDoc = data.returnDoc;
538
+ }
539
+ /** True if this follows the async/finish pattern. */
540
+ isAsync() {
541
+ return this.name.endsWith("_async") || this.parameters.some((p) => p.scope === "async");
542
+ }
543
+ /** Gets required parameters. */
544
+ getRequiredParameters() {
545
+ return this.parameters.filter((p) => !p.optional && !p.nullable && p.direction === "in");
546
+ }
547
+ }
548
+ /**
549
+ * Normalized parameter with helper methods.
550
+ */
551
+ export class GirParameter {
552
+ name;
553
+ type;
554
+ direction;
555
+ callerAllocates;
556
+ nullable;
557
+ optional;
558
+ scope;
559
+ closure;
560
+ destroy;
561
+ transferOwnership;
562
+ doc;
563
+ constructor(data) {
564
+ this.name = data.name;
565
+ this.type = data.type;
566
+ this.direction = data.direction;
567
+ this.callerAllocates = data.callerAllocates;
568
+ this.nullable = data.nullable;
569
+ this.optional = data.optional;
570
+ this.scope = data.scope;
571
+ this.closure = data.closure;
572
+ this.destroy = data.destroy;
573
+ this.transferOwnership = data.transferOwnership;
574
+ this.doc = data.doc;
575
+ }
576
+ /** True if this is an input parameter. */
577
+ isIn() {
578
+ return this.direction === "in";
579
+ }
580
+ /** True if this is an output parameter. */
581
+ isOut() {
582
+ return this.direction === "out" || this.direction === "inout";
583
+ }
584
+ /** True if this is a callback parameter (has scope). */
585
+ isCallback() {
586
+ return this.scope !== undefined;
587
+ }
588
+ /** True if this is the user_data for a callback. */
589
+ isClosureData() {
590
+ return this.closure !== undefined;
591
+ }
592
+ /** True if this is a destroy notify for a callback. */
593
+ isDestroyNotify() {
594
+ return this.destroy !== undefined;
595
+ }
596
+ /** True if caller must allocate memory for this out param. */
597
+ requiresCallerAllocation() {
598
+ return this.callerAllocates && this.isOut();
599
+ }
600
+ }
601
+ /**
602
+ * Normalized property with helper methods.
603
+ */
604
+ export class GirProperty {
605
+ name;
606
+ type;
607
+ readable;
608
+ writable;
609
+ constructOnly;
610
+ hasDefault;
611
+ getter;
612
+ setter;
613
+ doc;
614
+ constructor(data) {
615
+ this.name = data.name;
616
+ this.type = data.type;
617
+ this.readable = data.readable;
618
+ this.writable = data.writable;
619
+ this.constructOnly = data.constructOnly;
620
+ this.hasDefault = data.hasDefault;
621
+ this.getter = data.getter;
622
+ this.setter = data.setter;
623
+ this.doc = data.doc;
624
+ }
625
+ /** True if readable but not writable. */
626
+ isReadOnly() {
627
+ return this.readable && !this.writable;
628
+ }
629
+ /** True if writable but not readable. */
630
+ isWriteOnly() {
631
+ return this.writable && !this.readable;
632
+ }
633
+ /** True if can only be set during construction. */
634
+ isConstructOnly() {
635
+ return this.constructOnly;
636
+ }
637
+ /** True if has both getter and setter methods. */
638
+ hasAccessors() {
639
+ return this.getter !== undefined && this.setter !== undefined;
640
+ }
641
+ }
642
+ /**
643
+ * Normalized signal with helper methods.
644
+ */
645
+ export class GirSignal {
646
+ name;
647
+ when;
648
+ returnType;
649
+ parameters;
650
+ doc;
651
+ constructor(data) {
652
+ this.name = data.name;
653
+ this.when = data.when;
654
+ this.returnType = data.returnType;
655
+ this.parameters = data.parameters;
656
+ this.doc = data.doc;
657
+ }
658
+ /** True if the signal returns a value. */
659
+ hasReturnValue() {
660
+ return this.returnType !== null && !this.returnType.isVoid();
661
+ }
662
+ }
663
+ /**
664
+ * Normalized field.
665
+ */
666
+ export class GirField {
667
+ name;
668
+ type;
669
+ writable;
670
+ readable;
671
+ private;
672
+ doc;
673
+ constructor(data) {
674
+ this.name = data.name;
675
+ this.type = data.type;
676
+ this.writable = data.writable;
677
+ this.readable = data.readable;
678
+ this.private = data.private;
679
+ this.doc = data.doc;
680
+ }
681
+ }
682
+ /**
683
+ * Normalized type reference with helper methods.
684
+ */
685
+ export class GirType {
686
+ /** Type name - either a QualifiedName or an intrinsic type string */
687
+ name;
688
+ cType;
689
+ isArray;
690
+ elementType;
691
+ typeParameters;
692
+ containerType;
693
+ transferOwnership;
694
+ nullable;
695
+ constructor(data) {
696
+ this.name = data.name;
697
+ this.cType = data.cType;
698
+ this.isArray = data.isArray;
699
+ this.elementType = data.elementType;
700
+ this.typeParameters = data.typeParameters ?? [];
701
+ this.containerType = data.containerType;
702
+ this.transferOwnership = data.transferOwnership;
703
+ this.nullable = data.nullable;
704
+ }
705
+ /** True if this is an intrinsic/primitive type. */
706
+ isIntrinsic() {
707
+ return typeof this.name === "string" && isIntrinsicType(this.name);
708
+ }
709
+ /** True if this is a string type (utf8 or filename). */
710
+ isString() {
711
+ return typeof this.name === "string" && isStringType(this.name);
712
+ }
713
+ /** True if this is a numeric type. */
714
+ isNumeric() {
715
+ return typeof this.name === "string" && isNumericType(this.name);
716
+ }
717
+ /** True if this is a boolean type. */
718
+ isBoolean() {
719
+ return this.name === "gboolean";
720
+ }
721
+ /** True if this is void. */
722
+ isVoid() {
723
+ return typeof this.name === "string" && isVoidType(this.name);
724
+ }
725
+ /** True if this is GVariant. */
726
+ isVariant() {
727
+ return this.name === "GVariant";
728
+ }
729
+ /** True if this is GParamSpec. */
730
+ isParamSpec() {
731
+ return this.name === "GParamSpec";
732
+ }
733
+ /** True if this is a GHashTable container. */
734
+ isHashTable() {
735
+ return this.containerType === "ghashtable";
736
+ }
737
+ /** True if this is a GPtrArray container. */
738
+ isPtrArray() {
739
+ return this.containerType === "gptrarray";
740
+ }
741
+ /** True if this is a GArray container. */
742
+ isGArray() {
743
+ return this.containerType === "garray";
744
+ }
745
+ /** True if this is a GList or GSList container. */
746
+ isList() {
747
+ return this.containerType === "glist" || this.containerType === "gslist";
748
+ }
749
+ /** True if this is any generic container type. */
750
+ isGenericContainer() {
751
+ return this.containerType !== undefined;
752
+ }
753
+ /** Gets the key type for GHashTable, or null for other types. */
754
+ getKeyType() {
755
+ if (!this.isHashTable() || this.typeParameters.length < 1)
756
+ return null;
757
+ return this.typeParameters[0] ?? null;
758
+ }
759
+ /** Gets the value type for GHashTable, or null for other types. */
760
+ getValueType() {
761
+ if (!this.isHashTable() || this.typeParameters.length < 2)
762
+ return null;
763
+ return this.typeParameters[1] ?? null;
764
+ }
765
+ /** Gets the namespace part of a qualified name, or null for intrinsics. */
766
+ getNamespace() {
767
+ if (this.isIntrinsic())
768
+ return null;
769
+ const qn = this.name;
770
+ const dot = qn.indexOf(".");
771
+ return dot >= 0 ? qn.slice(0, dot) : null;
772
+ }
773
+ /** Gets the simple name part (without namespace). */
774
+ getSimpleName() {
775
+ if (this.isIntrinsic())
776
+ return this.name;
777
+ const qn = this.name;
778
+ const dot = qn.indexOf(".");
779
+ return dot >= 0 ? qn.slice(dot + 1) : qn;
1122
780
  }
1123
781
  }