@gtkx/gir 0.18.0 → 0.18.2

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 (41) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/internal/normalizer.d.ts +1 -0
  6. package/dist/internal/normalizer.d.ts.map +1 -0
  7. package/dist/internal/normalizer.js +1 -0
  8. package/dist/internal/normalizer.js.map +1 -0
  9. package/dist/internal/parser.d.ts +1 -0
  10. package/dist/internal/parser.d.ts.map +1 -0
  11. package/dist/internal/parser.js +1 -0
  12. package/dist/internal/parser.js.map +1 -0
  13. package/dist/internal/raw-types.d.ts +1 -0
  14. package/dist/internal/raw-types.d.ts.map +1 -0
  15. package/dist/internal/raw-types.js +1 -0
  16. package/dist/internal/raw-types.js.map +1 -0
  17. package/dist/intrinsics.d.ts +1 -0
  18. package/dist/intrinsics.d.ts.map +1 -0
  19. package/dist/intrinsics.js +1 -0
  20. package/dist/intrinsics.js.map +1 -0
  21. package/dist/repository.d.ts +1 -0
  22. package/dist/repository.d.ts.map +1 -0
  23. package/dist/repository.js +1 -0
  24. package/dist/repository.js.map +1 -0
  25. package/dist/types.d.ts +1 -0
  26. package/dist/types.d.ts.map +1 -0
  27. package/dist/types.js +1 -0
  28. package/dist/types.js.map +1 -0
  29. package/dist/utils.d.ts +1 -0
  30. package/dist/utils.d.ts.map +1 -0
  31. package/dist/utils.js +1 -0
  32. package/dist/utils.js.map +1 -0
  33. package/package.json +4 -2
  34. package/src/index.ts +64 -0
  35. package/src/internal/normalizer.ts +551 -0
  36. package/src/internal/parser.ts +633 -0
  37. package/src/internal/raw-types.ts +268 -0
  38. package/src/intrinsics.ts +129 -0
  39. package/src/repository.ts +406 -0
  40. package/src/types.ts +1192 -0
  41. package/src/utils.ts +12 -0
@@ -0,0 +1,633 @@
1
+ /**
2
+ * GObject Introspection XML (GIR) parser.
3
+ *
4
+ * Parses GIR files to extract type information for GTK/GLib libraries.
5
+ * Outputs raw GIR types that are then normalized by the normalizer.
6
+ *
7
+ * @internal
8
+ */
9
+
10
+ import { XMLParser } from "fast-xml-parser";
11
+ import type {
12
+ ContainerType,
13
+ RawAlias,
14
+ RawCallback,
15
+ RawClass,
16
+ RawConstant,
17
+ RawConstructor,
18
+ RawEnumeration,
19
+ RawEnumerationMember,
20
+ RawField,
21
+ RawFunction,
22
+ RawInterface,
23
+ RawMethod,
24
+ RawNamespace,
25
+ RawParameter,
26
+ RawProperty,
27
+ RawRecord,
28
+ RawSignal,
29
+ RawType,
30
+ } from "./raw-types.js";
31
+
32
+ const ARRAY_ELEMENT_PATHS = new Set<string>([
33
+ "namespace.class",
34
+ "namespace.interface",
35
+ "namespace.function",
36
+ "namespace.enumeration",
37
+ "namespace.bitfield",
38
+ "namespace.record",
39
+ "namespace.callback",
40
+ "namespace.constant",
41
+ "namespace.alias",
42
+ "namespace.class.method",
43
+ "namespace.class.constructor",
44
+ "namespace.class.function",
45
+ "namespace.class.property",
46
+ "namespace.class.signal",
47
+ "namespace.class.glib:signal",
48
+ "namespace.interface.method",
49
+ "namespace.interface.property",
50
+ "namespace.interface.signal",
51
+ "namespace.interface.glib:signal",
52
+ "namespace.record.method",
53
+ "namespace.record.constructor",
54
+ "namespace.record.function",
55
+ "namespace.record.field",
56
+ "namespace.class.method.parameters.parameter",
57
+ "namespace.class.constructor.parameters.parameter",
58
+ "namespace.class.function.parameters.parameter",
59
+ "namespace.function.parameters.parameter",
60
+ "namespace.enumeration.member",
61
+ "namespace.bitfield.member",
62
+ "namespace.interface.method.parameters.parameter",
63
+ "namespace.class.glib:signal.parameters.parameter",
64
+ "namespace.interface.glib:signal.parameters.parameter",
65
+ "namespace.record.method.parameters.parameter",
66
+ "namespace.record.constructor.parameters.parameter",
67
+ "namespace.record.function.parameters.parameter",
68
+ "namespace.callback.parameters.parameter",
69
+ ]);
70
+
71
+ const extractDoc = (node: Record<string, unknown>): string | undefined => {
72
+ const doc = node.doc as Record<string, unknown> | undefined;
73
+ if (!doc) return undefined;
74
+ const text = doc["#text"];
75
+ if (typeof text !== "string") return undefined;
76
+ return text.trim();
77
+ };
78
+
79
+ const ensureArray = (value: unknown): Record<string, unknown>[] =>
80
+ Array.isArray(value) ? (value as Record<string, unknown>[]) : [];
81
+
82
+ export type ParserOptions = {
83
+ includeNonIntrospectableNamespaces?: Set<string>;
84
+ };
85
+
86
+ /**
87
+ * Parser for GObject Introspection XML (GIR) files.
88
+ *
89
+ * Converts GIR XML into raw TypeScript objects.
90
+ */
91
+ export class RawGirParser {
92
+ private parser: XMLParser;
93
+ private includeNonIntrospectableNamespaces: Set<string>;
94
+ private currentNamespace: string = "";
95
+
96
+ constructor(options: ParserOptions = {}) {
97
+ this.includeNonIntrospectableNamespaces = options.includeNonIntrospectableNamespaces ?? new Set();
98
+ this.parser = new XMLParser({
99
+ ignoreAttributes: false,
100
+ attributeNamePrefix: "@_",
101
+ textNodeName: "#text",
102
+ isArray: (_name, jpath, _isLeafNode, _isAttribute) => {
103
+ const path = jpath.split(".").slice(1).join(".");
104
+ return ARRAY_ELEMENT_PATHS.has(path);
105
+ },
106
+ });
107
+ }
108
+
109
+ private shouldIncludeNonIntrospectable(): boolean {
110
+ return this.includeNonIntrospectableNamespaces.has(this.currentNamespace);
111
+ }
112
+
113
+ private isIntrospectable(node: Record<string, unknown>): boolean {
114
+ if (node["@_introspectable"] === "0") {
115
+ return this.shouldIncludeNonIntrospectable();
116
+ }
117
+ return true;
118
+ }
119
+
120
+ /**
121
+ * Parses a GIR XML string into a raw namespace object.
122
+ */
123
+ parse(girXml: string): RawNamespace {
124
+ const parsed = this.parser.parse(girXml);
125
+ const repository = parsed.repository;
126
+
127
+ if (!repository?.namespace) {
128
+ throw new Error("Failed to parse GIR file: missing repository or namespace element");
129
+ }
130
+
131
+ const namespace = repository.namespace;
132
+ this.currentNamespace = namespace["@_name"];
133
+
134
+ return {
135
+ name: namespace["@_name"],
136
+ version: namespace["@_version"],
137
+ sharedLibrary: namespace["@_shared-library"] ?? "",
138
+ cPrefix: namespace["@_c:identifier-prefixes"] ?? namespace["@_c:prefix"] ?? "",
139
+ classes: this.parseClasses(namespace.class ?? []),
140
+ interfaces: this.parseInterfaces(namespace.interface ?? []),
141
+ functions: this.parseFunctions(namespace.function ?? []),
142
+ enumerations: this.parseEnumerations(namespace.enumeration ?? []),
143
+ bitfields: this.parseEnumerations(namespace.bitfield ?? []),
144
+ records: this.parseRecords(namespace.record ?? []),
145
+ callbacks: this.parseCallbacks(namespace.callback ?? []),
146
+ constants: this.parseConstants(namespace.constant ?? []),
147
+ aliases: this.parseAliases(namespace.alias ?? []),
148
+ };
149
+ }
150
+
151
+ private parseCallbacks(callbacks: Record<string, unknown>[]): RawCallback[] {
152
+ if (!callbacks || !Array.isArray(callbacks)) {
153
+ return [];
154
+ }
155
+ return callbacks
156
+ .filter((cb) => this.isIntrospectable(cb))
157
+ .map((cb) => ({
158
+ name: String(cb["@_name"] ?? ""),
159
+ cType: String(cb["@_c:type"] ?? ""),
160
+ returnType: this.parseReturnType(cb["return-value"] as Record<string, unknown> | undefined),
161
+ parameters: this.parseParameters(
162
+ (cb.parameters && typeof cb.parameters === "object" && cb.parameters !== null
163
+ ? cb.parameters
164
+ : {}) as Record<string, unknown>,
165
+ ),
166
+ doc: extractDoc(cb),
167
+ }));
168
+ }
169
+
170
+ private parseClasses(classes: Record<string, unknown>[]): RawClass[] {
171
+ return classes.map((cls) => ({
172
+ name: String(cls["@_name"] ?? ""),
173
+ cType: String(cls["@_c:type"] ?? cls["@_glib:type-name"] ?? ""),
174
+ parent: cls["@_parent"] ? String(cls["@_parent"]) : undefined,
175
+ abstract: cls["@_abstract"] === "1",
176
+ glibTypeName: cls["@_glib:type-name"] ? String(cls["@_glib:type-name"]) : undefined,
177
+ glibGetType: cls["@_glib:get-type"] ? String(cls["@_glib:get-type"]) : undefined,
178
+ cSymbolPrefix: cls["@_c:symbol-prefix"] ? String(cls["@_c:symbol-prefix"]) : undefined,
179
+ fundamental: cls["@_glib:fundamental"] === "1",
180
+ refFunc: cls["@_glib:ref-func"] ? String(cls["@_glib:ref-func"]) : undefined,
181
+ unrefFunc: cls["@_glib:unref-func"] ? String(cls["@_glib:unref-func"]) : undefined,
182
+ implements: this.parseImplements(
183
+ cls.implements as Record<string, unknown>[] | Record<string, unknown> | undefined,
184
+ ),
185
+ methods: this.parseMethods(ensureArray(cls.method)),
186
+ constructors: this.parseConstructors(ensureArray(cls.constructor)),
187
+ functions: this.parseFunctions(ensureArray(cls.function)),
188
+ properties: this.parseProperties(ensureArray(cls.property)),
189
+ signals: this.parseSignals(ensureArray(cls["glib:signal"])),
190
+ doc: extractDoc(cls),
191
+ }));
192
+ }
193
+
194
+ private parseImplements(implements_: Record<string, unknown>[] | Record<string, unknown> | undefined): string[] {
195
+ if (!implements_) return [];
196
+ const arr = Array.isArray(implements_) ? implements_ : [implements_];
197
+ return arr.map((impl) => String(impl["@_name"] ?? "")).filter(Boolean);
198
+ }
199
+
200
+ private parseInterfaces(interfaces: Record<string, unknown>[]): RawInterface[] {
201
+ if (!interfaces || !Array.isArray(interfaces)) {
202
+ return [];
203
+ }
204
+ return interfaces.map((iface) => ({
205
+ name: String(iface["@_name"] ?? ""),
206
+ cType: String(iface["@_c:type"] ?? iface["@_glib:type-name"] ?? ""),
207
+ glibTypeName: iface["@_glib:type-name"] ? String(iface["@_glib:type-name"]) : undefined,
208
+ prerequisites: this.parsePrerequisites(
209
+ iface.prerequisite as Record<string, unknown>[] | Record<string, unknown> | undefined,
210
+ ),
211
+ methods: this.parseMethods(ensureArray(iface.method)),
212
+ properties: this.parseProperties(ensureArray(iface.property)),
213
+ signals: this.parseSignals(ensureArray(iface["glib:signal"])),
214
+ doc: extractDoc(iface),
215
+ }));
216
+ }
217
+
218
+ private parsePrerequisites(
219
+ prerequisites: Record<string, unknown>[] | Record<string, unknown> | undefined,
220
+ ): string[] {
221
+ if (!prerequisites) return [];
222
+ const arr = Array.isArray(prerequisites) ? prerequisites : [prerequisites];
223
+ return arr.map((prereq) => String(prereq["@_name"] ?? "")).filter(Boolean);
224
+ }
225
+
226
+ private parseMethods(methods: Record<string, unknown>[]): RawMethod[] {
227
+ if (!methods || !Array.isArray(methods)) {
228
+ return [];
229
+ }
230
+ return methods
231
+ .filter((method) => this.isIntrospectable(method))
232
+ .map((method) => {
233
+ const returnValue = method["return-value"] as Record<string, unknown> | undefined;
234
+ const finishFunc = method["@_glib:finish-func"] as string | undefined;
235
+ const shadows = method["@_shadows"] as string | undefined;
236
+ const shadowedBy = method["@_shadowed-by"] as string | undefined;
237
+ const parametersNode =
238
+ method.parameters && typeof method.parameters === "object" && method.parameters !== null
239
+ ? (method.parameters as Record<string, unknown>)
240
+ : {};
241
+ return {
242
+ name: String(method["@_name"] ?? ""),
243
+ cIdentifier: String(method["@_c:identifier"] ?? ""),
244
+ returnType: this.parseReturnType(returnValue),
245
+ parameters: this.parseParameters(parametersNode),
246
+ instanceParameter: this.parseInstanceParameter(parametersNode),
247
+ throws: method["@_throws"] === "1",
248
+ doc: extractDoc(method),
249
+ returnDoc: returnValue ? extractDoc(returnValue) : undefined,
250
+ finishFunc: finishFunc || undefined,
251
+ shadows: shadows || undefined,
252
+ shadowedBy: shadowedBy || undefined,
253
+ };
254
+ });
255
+ }
256
+
257
+ private parseConstructors(constructors: Record<string, unknown>[]): RawConstructor[] {
258
+ if (!constructors || !Array.isArray(constructors)) {
259
+ return [];
260
+ }
261
+ return constructors
262
+ .filter((ctor) => this.isIntrospectable(ctor))
263
+ .map((ctor) => {
264
+ const returnValue = ctor["return-value"] as Record<string, unknown> | undefined;
265
+ const shadows = ctor["@_shadows"] as string | undefined;
266
+ const shadowedBy = ctor["@_shadowed-by"] as string | undefined;
267
+ return {
268
+ name: String(ctor["@_name"] ?? ""),
269
+ cIdentifier: String(ctor["@_c:identifier"] ?? ""),
270
+ returnType: this.parseReturnType(returnValue),
271
+ parameters: this.parseParameters(
272
+ (ctor.parameters && typeof ctor.parameters === "object" && ctor.parameters !== null
273
+ ? ctor.parameters
274
+ : {}) as Record<string, unknown>,
275
+ ),
276
+ throws: ctor["@_throws"] === "1",
277
+ doc: extractDoc(ctor),
278
+ returnDoc: returnValue ? extractDoc(returnValue) : undefined,
279
+ shadows: shadows || undefined,
280
+ shadowedBy: shadowedBy || undefined,
281
+ };
282
+ });
283
+ }
284
+
285
+ private parseFunctions(functions: Record<string, unknown>[]): RawFunction[] {
286
+ if (!functions || !Array.isArray(functions)) {
287
+ return [];
288
+ }
289
+ return functions
290
+ .filter((func) => this.isIntrospectable(func))
291
+ .map((func) => {
292
+ const returnValue = func["return-value"] as Record<string, unknown> | undefined;
293
+ const shadows = func["@_shadows"] as string | undefined;
294
+ const shadowedBy = func["@_shadowed-by"] as string | undefined;
295
+ return {
296
+ name: String(func["@_name"] ?? ""),
297
+ cIdentifier: String(func["@_c:identifier"] ?? ""),
298
+ returnType: this.parseReturnType(returnValue),
299
+ parameters: this.parseParameters(
300
+ (func.parameters && typeof func.parameters === "object" && func.parameters !== null
301
+ ? func.parameters
302
+ : {}) as Record<string, unknown>,
303
+ ),
304
+ throws: func["@_throws"] === "1",
305
+ doc: extractDoc(func),
306
+ returnDoc: returnValue ? extractDoc(returnValue) : undefined,
307
+ shadows: shadows || undefined,
308
+ shadowedBy: shadowedBy || undefined,
309
+ };
310
+ });
311
+ }
312
+
313
+ private parseParameters(parametersNode: Record<string, unknown>): RawParameter[] {
314
+ if (!parametersNode?.parameter) {
315
+ return [];
316
+ }
317
+
318
+ const params = Array.isArray(parametersNode.parameter) ? parametersNode.parameter : [parametersNode.parameter];
319
+
320
+ return params.map((param: Record<string, unknown>) => this.parseSingleParameter(param));
321
+ }
322
+
323
+ private parseInstanceParameter(parametersNode: Record<string, unknown>): RawParameter | undefined {
324
+ const instanceParam = parametersNode?.["instance-parameter"] as Record<string, unknown> | undefined;
325
+ if (!instanceParam) {
326
+ return undefined;
327
+ }
328
+ return this.parseSingleParameter(instanceParam);
329
+ }
330
+
331
+ private parseSingleParameter(param: Record<string, unknown>): RawParameter {
332
+ const scope = param["@_scope"] as string | undefined;
333
+ const closure = param["@_closure"] as string | undefined;
334
+ const destroy = param["@_destroy"] as string | undefined;
335
+ const transferOwnership = param["@_transfer-ownership"] as string | undefined;
336
+ const callerAllocates = param["@_caller-allocates"] as string | undefined;
337
+ return {
338
+ name: String(param["@_name"] ?? ""),
339
+ type: this.parseType((param.type ?? param.array) as Record<string, unknown> | undefined),
340
+ direction: (String(param["@_direction"] ?? "in") as "in" | "out" | "inout") || "in",
341
+ callerAllocates: callerAllocates === "1",
342
+ nullable: param["@_nullable"] === "1",
343
+ optional: param["@_allow-none"] === "1" || param["@_optional"] === "1",
344
+ scope: scope as "async" | "call" | "notified" | undefined,
345
+ closure: closure !== undefined ? parseInt(closure, 10) : undefined,
346
+ destroy: destroy !== undefined ? parseInt(destroy, 10) : undefined,
347
+ transferOwnership:
348
+ transferOwnership === "none" || transferOwnership === "full" || transferOwnership === "container"
349
+ ? transferOwnership
350
+ : undefined,
351
+ doc: extractDoc(param),
352
+ };
353
+ }
354
+
355
+ private parseReturnType(returnValue: Record<string, unknown> | undefined): RawType {
356
+ if (!returnValue) {
357
+ return { name: "void" };
358
+ }
359
+ const type = this.parseType((returnValue.type ?? returnValue.array) as Record<string, unknown> | undefined);
360
+ const transferOwnership = returnValue["@_transfer-ownership"] as string | undefined;
361
+ if (transferOwnership === "none" || transferOwnership === "full" || transferOwnership === "container") {
362
+ type.transferOwnership = transferOwnership;
363
+ }
364
+ if (returnValue["@_nullable"] === "1") {
365
+ type.nullable = true;
366
+ }
367
+ return type;
368
+ }
369
+
370
+ private parseType(typeNode: Record<string, unknown> | undefined): RawType {
371
+ if (!typeNode) {
372
+ return { name: "void" };
373
+ }
374
+
375
+ const typeName = typeNode["@_name"] ? String(typeNode["@_name"]) : undefined;
376
+ const cType = typeNode["@_c:type"] ? String(typeNode["@_c:type"]) : undefined;
377
+
378
+ const containerResult = this.parseGLibContainerType(typeName, typeNode, cType);
379
+ if (containerResult) {
380
+ return containerResult;
381
+ }
382
+
383
+ if (typeName) {
384
+ return { name: typeName, cType };
385
+ }
386
+
387
+ const isArrayNode =
388
+ typeNode.type ||
389
+ typeNode["@_zero-terminated"] !== undefined ||
390
+ typeNode["@_fixed-size"] !== undefined ||
391
+ typeNode["@_length"] !== undefined;
392
+
393
+ if (isArrayNode) {
394
+ const lengthAttr = typeNode["@_length"];
395
+ const zeroTerminatedAttr = typeNode["@_zero-terminated"];
396
+ const fixedSizeAttr = typeNode["@_fixed-size"];
397
+
398
+ return {
399
+ name: "array",
400
+ isArray: true,
401
+ elementType: typeNode.type ? this.parseType(typeNode.type as Record<string, unknown>) : undefined,
402
+ sizeParamIndex: lengthAttr !== undefined ? Number(lengthAttr) : undefined,
403
+ zeroTerminated: zeroTerminatedAttr !== undefined ? zeroTerminatedAttr !== "0" : undefined,
404
+ fixedSize: fixedSizeAttr !== undefined ? Number(fixedSizeAttr) : undefined,
405
+ };
406
+ }
407
+
408
+ return { name: "void" };
409
+ }
410
+
411
+ private extractTypeParameters(typeNode: Record<string, unknown>): RawType[] {
412
+ const types: RawType[] = [];
413
+ const typeChildren = typeNode.type;
414
+ const arrayChildren = typeNode.array;
415
+
416
+ if (Array.isArray(typeChildren)) {
417
+ for (const child of typeChildren) {
418
+ types.push(this.parseType(child as Record<string, unknown>));
419
+ }
420
+ } else if (typeChildren) {
421
+ types.push(this.parseType(typeChildren as Record<string, unknown>));
422
+ }
423
+
424
+ if (Array.isArray(arrayChildren)) {
425
+ for (const child of arrayChildren) {
426
+ types.push(this.parseType(child as Record<string, unknown>));
427
+ }
428
+ } else if (arrayChildren) {
429
+ types.push(this.parseType(arrayChildren as Record<string, unknown>));
430
+ }
431
+
432
+ return types;
433
+ }
434
+
435
+ private parseGLibContainerType(
436
+ typeName: string | undefined,
437
+ typeNode: Record<string, unknown>,
438
+ cType: string | undefined,
439
+ ): RawType | null {
440
+ if (typeName === "GLib.HashTable") {
441
+ const typeParams = this.extractTypeParameters(typeNode);
442
+ return {
443
+ name: "GLib.HashTable",
444
+ cType,
445
+ isArray: false,
446
+ containerType: "ghashtable" as ContainerType,
447
+ typeParameters: typeParams.length >= 2 ? typeParams : undefined,
448
+ elementType: typeParams[1],
449
+ };
450
+ }
451
+
452
+ if (typeName === "GLib.PtrArray" || typeName === "GLib.Array") {
453
+ const typeParams = this.extractTypeParameters(typeNode);
454
+ return {
455
+ name: typeName,
456
+ cType,
457
+ isArray: true,
458
+ containerType: (typeName === "GLib.PtrArray" ? "gptrarray" : "garray") as ContainerType,
459
+ typeParameters: typeParams.length > 0 ? typeParams : undefined,
460
+ elementType: typeParams[0],
461
+ };
462
+ }
463
+
464
+ if (typeName === "GLib.List" || typeName === "GLib.SList") {
465
+ const innerType = (typeNode.type ?? typeNode.array) as Record<string, unknown> | undefined;
466
+ const elementType = innerType ? this.parseType(innerType) : undefined;
467
+ return {
468
+ name: "array",
469
+ cType,
470
+ isArray: true,
471
+ containerType: (typeName === "GLib.List" ? "glist" : "gslist") as ContainerType,
472
+ typeParameters: elementType ? [elementType] : undefined,
473
+ elementType,
474
+ };
475
+ }
476
+
477
+ return null;
478
+ }
479
+
480
+ private parseProperties(properties: Record<string, unknown>[]): RawProperty[] {
481
+ if (!properties || !Array.isArray(properties)) {
482
+ return [];
483
+ }
484
+ return properties.map((prop) => {
485
+ let getter = prop["@_getter"] ? String(prop["@_getter"]) : undefined;
486
+ let setter = prop["@_setter"] ? String(prop["@_setter"]) : undefined;
487
+
488
+ const attributes = prop.attribute as Record<string, unknown>[] | Record<string, unknown> | undefined;
489
+ if (attributes) {
490
+ const attrList = Array.isArray(attributes) ? attributes : [attributes];
491
+ for (const attr of attrList) {
492
+ const attrName = attr["@_name"];
493
+ const attrValue = attr["@_value"];
494
+ if (attrName === "org.gtk.Property.get" && attrValue) {
495
+ getter = String(attrValue);
496
+ } else if (attrName === "org.gtk.Property.set" && attrValue) {
497
+ setter = String(attrValue);
498
+ }
499
+ }
500
+ }
501
+
502
+ return {
503
+ name: String(prop["@_name"] ?? ""),
504
+ type: this.parseType((prop.type ?? prop.array) as Record<string, unknown> | undefined),
505
+ readable: prop["@_readable"] !== "0",
506
+ writable: prop["@_writable"] === "1",
507
+ constructOnly: prop["@_construct-only"] === "1",
508
+ defaultValueRaw: prop["@_default-value"] !== undefined ? String(prop["@_default-value"]) : undefined,
509
+ getter,
510
+ setter,
511
+ doc: extractDoc(prop),
512
+ };
513
+ });
514
+ }
515
+
516
+ private parseSignals(signals: Record<string, unknown>[]): RawSignal[] {
517
+ if (!signals || !Array.isArray(signals)) {
518
+ return [];
519
+ }
520
+ return signals.map((signal) => {
521
+ const whenValue = String(signal["@_when"] ?? "last");
522
+ const validWhen = whenValue === "first" || whenValue === "last" || whenValue === "cleanup";
523
+ return {
524
+ name: String(signal["@_name"] ?? ""),
525
+ when: validWhen ? (whenValue as "first" | "last" | "cleanup") : "last",
526
+ returnType: signal["return-value"]
527
+ ? this.parseReturnType(signal["return-value"] as Record<string, unknown>)
528
+ : undefined,
529
+ parameters:
530
+ signal.parameters && typeof signal.parameters === "object" && signal.parameters !== null
531
+ ? this.parseParameters(signal.parameters as Record<string, unknown>)
532
+ : [],
533
+ doc: extractDoc(signal),
534
+ };
535
+ });
536
+ }
537
+
538
+ private parseRecords(records: Record<string, unknown>[]): RawRecord[] {
539
+ if (!records || !Array.isArray(records)) {
540
+ return [];
541
+ }
542
+ return records.map((record) => ({
543
+ name: String(record["@_name"] ?? ""),
544
+ cType: String(record["@_c:type"] ?? record["@_glib:type-name"] ?? ""),
545
+ opaque: record["@_opaque"] === "1",
546
+ disguised: record["@_disguised"] === "1",
547
+ glibTypeName: record["@_glib:type-name"] ? String(record["@_glib:type-name"]) : undefined,
548
+ glibGetType: record["@_glib:get-type"] ? String(record["@_glib:get-type"]) : undefined,
549
+ isGtypeStructFor: record["@_glib:is-gtype-struct-for"]
550
+ ? String(record["@_glib:is-gtype-struct-for"])
551
+ : undefined,
552
+ copyFunction: record["@_copy-function"] ? String(record["@_copy-function"]) : undefined,
553
+ freeFunction: record["@_free-function"] ? String(record["@_free-function"]) : undefined,
554
+ fields: this.parseFields(ensureArray(record.field)),
555
+ methods: this.parseMethods(ensureArray(record.method)),
556
+ constructors: this.parseConstructors(ensureArray(record.constructor)),
557
+ functions: this.parseFunctions(ensureArray(record.function)),
558
+ doc: extractDoc(record),
559
+ }));
560
+ }
561
+
562
+ private parseFields(fields: Record<string, unknown>[]): RawField[] {
563
+ if (!fields || !Array.isArray(fields)) {
564
+ return [];
565
+ }
566
+ return fields
567
+ .filter((field) => {
568
+ const hasCallback = field.callback !== undefined;
569
+ return !hasCallback;
570
+ })
571
+ .map((field) => ({
572
+ name: String(field["@_name"] ?? ""),
573
+ type: this.parseType((field.type ?? field.array) as Record<string, unknown> | undefined),
574
+ writable: field["@_writable"] === "1",
575
+ readable: field["@_readable"] !== "0",
576
+ private: field["@_private"] === "1",
577
+ doc: extractDoc(field),
578
+ }));
579
+ }
580
+
581
+ private parseEnumerations(enumerations: Record<string, unknown>[]): RawEnumeration[] {
582
+ if (!enumerations || !Array.isArray(enumerations)) {
583
+ return [];
584
+ }
585
+ return enumerations.map((enumeration) => {
586
+ const glibGetType = enumeration["@_glib:get-type"];
587
+ return {
588
+ name: String(enumeration["@_name"] ?? ""),
589
+ cType: String(enumeration["@_c:type"] ?? ""),
590
+ members: this.parseEnumerationMembers(ensureArray(enumeration.member)),
591
+ glibGetType: typeof glibGetType === "string" ? glibGetType : undefined,
592
+ doc: extractDoc(enumeration),
593
+ };
594
+ });
595
+ }
596
+
597
+ private parseEnumerationMembers(members: Record<string, unknown>[]): RawEnumerationMember[] {
598
+ if (!members || !Array.isArray(members)) {
599
+ return [];
600
+ }
601
+ return members.map((member) => ({
602
+ name: String(member["@_name"] ?? ""),
603
+ value: String(member["@_value"] ?? ""),
604
+ cIdentifier: String(member["@_c:identifier"] ?? ""),
605
+ doc: extractDoc(member),
606
+ }));
607
+ }
608
+
609
+ private parseConstants(constants: Record<string, unknown>[]): RawConstant[] {
610
+ if (!constants || !Array.isArray(constants)) {
611
+ return [];
612
+ }
613
+ return constants.map((constant) => ({
614
+ name: String(constant["@_name"] ?? ""),
615
+ cType: String(constant["@_c:type"] ?? ""),
616
+ value: String(constant["@_value"] ?? ""),
617
+ type: this.parseType((constant.type ?? constant.array) as Record<string, unknown> | undefined),
618
+ doc: extractDoc(constant),
619
+ }));
620
+ }
621
+
622
+ private parseAliases(aliases: Record<string, unknown>[]): RawAlias[] {
623
+ if (!aliases || !Array.isArray(aliases)) {
624
+ return [];
625
+ }
626
+ return aliases.map((alias) => ({
627
+ name: String(alias["@_name"] ?? ""),
628
+ cType: String(alias["@_c:type"] ?? ""),
629
+ targetType: this.parseType(alias.type as Record<string, unknown> | undefined),
630
+ doc: extractDoc(alias),
631
+ }));
632
+ }
633
+ }