@gtkx/gir 0.19.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 +4 -3
  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
@@ -1,20 +1,12 @@
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
1
  import { XMLParser } from "fast-xml-parser";
2
+ import type { ContainerType } from "../model/type.js";
11
3
  import type {
12
- ContainerType,
13
4
  RawAlias,
14
5
  RawCallback,
15
6
  RawClass,
16
7
  RawConstant,
17
8
  RawConstructor,
9
+ RawDependency,
18
10
  RawEnumeration,
19
11
  RawEnumerationMember,
20
12
  RawField,
@@ -25,6 +17,7 @@ import type {
25
17
  RawParameter,
26
18
  RawProperty,
27
19
  RawRecord,
20
+ RawRepositoryHeader,
28
21
  RawSignal,
29
22
  RawType,
30
23
  } from "./raw-types.js";
@@ -40,7 +33,7 @@ const ARRAY_ELEMENT_PATHS = new Set<string>([
40
33
  "namespace.constant",
41
34
  "namespace.alias",
42
35
  "namespace.class.method",
43
- "namespace.class.constructor",
36
+ "namespace.class._constructor",
44
37
  "namespace.class.function",
45
38
  "namespace.class.property",
46
39
  "namespace.class.signal",
@@ -50,11 +43,11 @@ const ARRAY_ELEMENT_PATHS = new Set<string>([
50
43
  "namespace.interface.signal",
51
44
  "namespace.interface.glib:signal",
52
45
  "namespace.record.method",
53
- "namespace.record.constructor",
46
+ "namespace.record._constructor",
54
47
  "namespace.record.function",
55
48
  "namespace.record.field",
56
49
  "namespace.class.method.parameters.parameter",
57
- "namespace.class.constructor.parameters.parameter",
50
+ "namespace.class._constructor.parameters.parameter",
58
51
  "namespace.class.function.parameters.parameter",
59
52
  "namespace.function.parameters.parameter",
60
53
  "namespace.enumeration.member",
@@ -63,64 +56,86 @@ const ARRAY_ELEMENT_PATHS = new Set<string>([
63
56
  "namespace.class.glib:signal.parameters.parameter",
64
57
  "namespace.interface.glib:signal.parameters.parameter",
65
58
  "namespace.record.method.parameters.parameter",
66
- "namespace.record.constructor.parameters.parameter",
59
+ "namespace.record._constructor.parameters.parameter",
67
60
  "namespace.record.function.parameters.parameter",
68
61
  "namespace.callback.parameters.parameter",
69
62
  ]);
70
63
 
71
- const extractDoc = (node: Record<string, unknown>): string | undefined => {
64
+ const INCLUDE_RE = /<include\s+name="([^"]+)"\s+version="([^"]+)"/g;
65
+ const NS_NAME_RE = /<namespace\s[^>]*name="([^"]+)"/;
66
+ const NS_VERSION_RE = /<namespace\s[^>]*version="([^"]+)"/;
67
+
68
+ function extractDoc(node: Record<string, unknown>): string | undefined {
72
69
  const doc = node.doc as Record<string, unknown> | undefined;
73
70
  if (!doc) return undefined;
74
71
  const text = doc["#text"];
75
72
  if (typeof text !== "string") return undefined;
76
73
  return text.trim();
77
- };
78
-
79
- const ensureArray = (value: unknown): Record<string, unknown>[] =>
80
- Array.isArray(value) ? (value as Record<string, unknown>[]) : [];
74
+ }
81
75
 
82
- export type ParserOptions = {
83
- includeNonIntrospectableNamespaces?: Set<string>;
84
- };
76
+ function ensureArray(value: unknown): Record<string, unknown>[] {
77
+ return Array.isArray(value) ? (value as Record<string, unknown>[]) : [];
78
+ }
85
79
 
86
80
  /**
87
81
  * Parser for GObject Introspection XML (GIR) files.
88
82
  *
89
- * Converts GIR XML into raw TypeScript objects.
83
+ * Provides both a lightweight header parse (for dependency graph discovery)
84
+ * and a full parse (for complete namespace extraction).
90
85
  */
91
- export class RawGirParser {
92
- private parser: XMLParser;
93
- private includeNonIntrospectableNamespaces: Set<string>;
94
- private currentNamespace: string = "";
86
+ export class GirParser {
87
+ private readonly parser: XMLParser;
95
88
 
96
- constructor(options: ParserOptions = {}) {
97
- this.includeNonIntrospectableNamespaces = options.includeNonIntrospectableNamespaces ?? new Set();
89
+ constructor() {
98
90
  this.parser = new XMLParser({
99
91
  ignoreAttributes: false,
100
92
  attributeNamePrefix: "@_",
101
93
  textNodeName: "#text",
94
+ transformTagName: (tagName) => (tagName === "constructor" ? "_constructor" : tagName),
102
95
  isArray: (_name, jpath, _isLeafNode, _isAttribute) => {
96
+ if (typeof jpath !== "string") return false;
103
97
  const path = jpath.split(".").slice(1).join(".");
104
98
  return ARRAY_ELEMENT_PATHS.has(path);
105
99
  },
100
+ processEntities: { maxTotalExpansions: 100000 },
106
101
  });
107
102
  }
108
103
 
109
- private shouldIncludeNonIntrospectable(): boolean {
110
- return this.includeNonIntrospectableNamespaces.has(this.currentNamespace);
111
- }
104
+ /**
105
+ * Lightweight header parse using regex — extracts namespace name, version,
106
+ * and `<include>` dependencies without paying the cost of full XML parsing.
107
+ */
108
+ parseHeader(girXml: string): RawRepositoryHeader {
109
+ const nameMatch = NS_NAME_RE.exec(girXml);
110
+ const versionMatch = NS_VERSION_RE.exec(girXml);
112
111
 
113
- private isIntrospectable(node: Record<string, unknown>): boolean {
114
- if (node["@_introspectable"] === "0") {
115
- return this.shouldIncludeNonIntrospectable();
112
+ if (!nameMatch || !versionMatch) {
113
+ throw new Error("Failed to parse GIR header: missing namespace name or version");
114
+ }
115
+
116
+ const dependencies: RawDependency[] = [];
117
+ INCLUDE_RE.lastIndex = 0;
118
+ for (let m = INCLUDE_RE.exec(girXml); m !== null; m = INCLUDE_RE.exec(girXml)) {
119
+ const name = m[1];
120
+ const version = m[2];
121
+ if (name && version) {
122
+ dependencies.push({ name, version });
123
+ }
116
124
  }
117
- return true;
125
+
126
+ const namespaceName = nameMatch[1];
127
+ const namespaceVersion = versionMatch[1];
128
+ if (!namespaceName || !namespaceVersion) {
129
+ throw new Error("Failed to parse GIR header: missing namespace name or version");
130
+ }
131
+
132
+ return { namespaceName, namespaceVersion, dependencies };
118
133
  }
119
134
 
120
135
  /**
121
- * Parses a GIR XML string into a raw namespace object.
136
+ * Full parse of a GIR XML string into a raw namespace.
122
137
  */
123
- parse(girXml: string): RawNamespace {
138
+ parseNamespace(girXml: string): RawNamespace {
124
139
  const parsed = this.parser.parse(girXml);
125
140
  const repository = parsed.repository;
126
141
 
@@ -129,7 +144,6 @@ export class RawGirParser {
129
144
  }
130
145
 
131
146
  const namespace = repository.namespace;
132
- this.currentNamespace = namespace["@_name"];
133
147
 
134
148
  return {
135
149
  name: namespace["@_name"],
@@ -148,25 +162,6 @@ export class RawGirParser {
148
162
  };
149
163
  }
150
164
 
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
165
  private parseClasses(classes: Record<string, unknown>[]): RawClass[] {
171
166
  return classes.map((cls) => ({
172
167
  name: String(cls["@_name"] ?? ""),
@@ -183,7 +178,7 @@ export class RawGirParser {
183
178
  cls.implements as Record<string, unknown>[] | Record<string, unknown> | undefined,
184
179
  ),
185
180
  methods: this.parseMethods(ensureArray(cls.method)),
186
- constructors: this.parseConstructors(ensureArray(cls.constructor)),
181
+ constructors: this.parseConstructors(ensureArray(cls._constructor)),
187
182
  functions: this.parseFunctions(ensureArray(cls.function)),
188
183
  properties: this.parseProperties(ensureArray(cls.property)),
189
184
  signals: this.parseSignals(ensureArray(cls["glib:signal"])),
@@ -198,9 +193,7 @@ export class RawGirParser {
198
193
  }
199
194
 
200
195
  private parseInterfaces(interfaces: Record<string, unknown>[]): RawInterface[] {
201
- if (!interfaces || !Array.isArray(interfaces)) {
202
- return [];
203
- }
196
+ if (!interfaces || !Array.isArray(interfaces)) return [];
204
197
  return interfaces.map((iface) => ({
205
198
  name: String(iface["@_name"] ?? ""),
206
199
  cType: String(iface["@_c:type"] ?? iface["@_glib:type-name"] ?? ""),
@@ -224,20 +217,12 @@ export class RawGirParser {
224
217
  }
225
218
 
226
219
  private parseMethods(methods: Record<string, unknown>[]): RawMethod[] {
227
- if (!methods || !Array.isArray(methods)) {
228
- return [];
229
- }
220
+ if (!methods || !Array.isArray(methods)) return [];
230
221
  return methods
231
- .filter((method) => this.isIntrospectable(method))
222
+ .filter((m) => m["@_introspectable"] !== "0")
232
223
  .map((method) => {
233
224
  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
- : {};
225
+ const parametersNode = this.extractParametersNode(method);
241
226
  return {
242
227
  name: String(method["@_name"] ?? ""),
243
228
  cIdentifier: String(method["@_c:identifier"] ?? ""),
@@ -247,84 +232,212 @@ export class RawGirParser {
247
232
  throws: method["@_throws"] === "1",
248
233
  doc: extractDoc(method),
249
234
  returnDoc: returnValue ? extractDoc(returnValue) : undefined,
250
- finishFunc: finishFunc || undefined,
251
- shadows: shadows || undefined,
252
- shadowedBy: shadowedBy || undefined,
235
+ finishFunc: (method["@_glib:finish-func"] as string) || undefined,
236
+ shadows: (method["@_shadows"] as string) || undefined,
237
+ shadowedBy: (method["@_shadowed-by"] as string) || undefined,
253
238
  };
254
239
  });
255
240
  }
256
241
 
257
242
  private parseConstructors(constructors: Record<string, unknown>[]): RawConstructor[] {
258
- if (!constructors || !Array.isArray(constructors)) {
259
- return [];
260
- }
243
+ if (!constructors || !Array.isArray(constructors)) return [];
261
244
  return constructors
262
- .filter((ctor) => this.isIntrospectable(ctor))
245
+ .filter((c) => c["@_introspectable"] !== "0")
263
246
  .map((ctor) => {
264
247
  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
248
  return {
268
249
  name: String(ctor["@_name"] ?? ""),
269
250
  cIdentifier: String(ctor["@_c:identifier"] ?? ""),
270
251
  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
- ),
252
+ parameters: this.parseParameters(this.extractParametersNode(ctor)),
276
253
  throws: ctor["@_throws"] === "1",
277
254
  doc: extractDoc(ctor),
278
255
  returnDoc: returnValue ? extractDoc(returnValue) : undefined,
279
- shadows: shadows || undefined,
280
- shadowedBy: shadowedBy || undefined,
256
+ shadows: (ctor["@_shadows"] as string) || undefined,
257
+ shadowedBy: (ctor["@_shadowed-by"] as string) || undefined,
281
258
  };
282
259
  });
283
260
  }
284
261
 
285
262
  private parseFunctions(functions: Record<string, unknown>[]): RawFunction[] {
286
- if (!functions || !Array.isArray(functions)) {
287
- return [];
288
- }
263
+ if (!functions || !Array.isArray(functions)) return [];
289
264
  return functions
290
- .filter((func) => this.isIntrospectable(func))
265
+ .filter((f) => f["@_introspectable"] !== "0")
291
266
  .map((func) => {
292
267
  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
268
  return {
296
269
  name: String(func["@_name"] ?? ""),
297
270
  cIdentifier: String(func["@_c:identifier"] ?? ""),
298
271
  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
- ),
272
+ parameters: this.parseParameters(this.extractParametersNode(func)),
304
273
  throws: func["@_throws"] === "1",
305
274
  doc: extractDoc(func),
306
275
  returnDoc: returnValue ? extractDoc(returnValue) : undefined,
307
- shadows: shadows || undefined,
308
- shadowedBy: shadowedBy || undefined,
276
+ shadows: (func["@_shadows"] as string) || undefined,
277
+ shadowedBy: (func["@_shadowed-by"] as string) || undefined,
309
278
  };
310
279
  });
311
280
  }
312
281
 
313
- private parseParameters(parametersNode: Record<string, unknown>): RawParameter[] {
314
- if (!parametersNode?.parameter) {
315
- return [];
316
- }
282
+ private parseCallbacks(callbacks: Record<string, unknown>[]): RawCallback[] {
283
+ if (!callbacks || !Array.isArray(callbacks)) return [];
284
+ return callbacks
285
+ .filter((cb) => cb["@_introspectable"] !== "0")
286
+ .map((cb) => ({
287
+ name: String(cb["@_name"] ?? ""),
288
+ cType: String(cb["@_c:type"] ?? ""),
289
+ returnType: this.parseReturnType(cb["return-value"] as Record<string, unknown> | undefined),
290
+ parameters: this.parseParameters(this.extractParametersNode(cb)),
291
+ doc: extractDoc(cb),
292
+ }));
293
+ }
317
294
 
318
- const params = Array.isArray(parametersNode.parameter) ? parametersNode.parameter : [parametersNode.parameter];
295
+ private parseRecords(records: Record<string, unknown>[]): RawRecord[] {
296
+ if (!records || !Array.isArray(records)) return [];
297
+ return records.map((record) => ({
298
+ name: String(record["@_name"] ?? ""),
299
+ cType: String(record["@_c:type"] ?? record["@_glib:type-name"] ?? ""),
300
+ opaque: record["@_opaque"] === "1",
301
+ disguised: record["@_disguised"] === "1",
302
+ glibTypeName: record["@_glib:type-name"] ? String(record["@_glib:type-name"]) : undefined,
303
+ glibGetType: record["@_glib:get-type"] ? String(record["@_glib:get-type"]) : undefined,
304
+ isGtypeStructFor: record["@_glib:is-gtype-struct-for"]
305
+ ? String(record["@_glib:is-gtype-struct-for"])
306
+ : undefined,
307
+ copyFunction: record["@_copy-function"] ? String(record["@_copy-function"]) : undefined,
308
+ freeFunction: record["@_free-function"] ? String(record["@_free-function"]) : undefined,
309
+ fields: this.parseFields(ensureArray(record.field)),
310
+ methods: this.parseMethods(ensureArray(record.method)),
311
+ constructors: this.parseConstructors(ensureArray(record._constructor)),
312
+ functions: this.parseFunctions(ensureArray(record.function)),
313
+ doc: extractDoc(record),
314
+ }));
315
+ }
316
+
317
+ private parseEnumerations(enumerations: Record<string, unknown>[]): RawEnumeration[] {
318
+ if (!enumerations || !Array.isArray(enumerations)) return [];
319
+ return enumerations.map((enumeration) => ({
320
+ name: String(enumeration["@_name"] ?? ""),
321
+ cType: String(enumeration["@_c:type"] ?? ""),
322
+ members: this.parseEnumerationMembers(ensureArray(enumeration.member)),
323
+ glibGetType:
324
+ typeof enumeration["@_glib:get-type"] === "string" ? enumeration["@_glib:get-type"] : undefined,
325
+ doc: extractDoc(enumeration),
326
+ }));
327
+ }
328
+
329
+ private parseEnumerationMembers(members: Record<string, unknown>[]): RawEnumerationMember[] {
330
+ if (!members || !Array.isArray(members)) return [];
331
+ return members.map((member) => ({
332
+ name: String(member["@_name"] ?? ""),
333
+ value: String(member["@_value"] ?? ""),
334
+ cIdentifier: String(member["@_c:identifier"] ?? ""),
335
+ doc: extractDoc(member),
336
+ }));
337
+ }
319
338
 
339
+ private parseConstants(constants: Record<string, unknown>[]): RawConstant[] {
340
+ if (!constants || !Array.isArray(constants)) return [];
341
+ return constants.map((constant) => ({
342
+ name: String(constant["@_name"] ?? ""),
343
+ cType: String(constant["@_c:type"] ?? ""),
344
+ value: String(constant["@_value"] ?? ""),
345
+ type: this.parseType((constant.type ?? constant.array) as Record<string, unknown> | undefined),
346
+ doc: extractDoc(constant),
347
+ }));
348
+ }
349
+
350
+ private parseAliases(aliases: Record<string, unknown>[]): RawAlias[] {
351
+ if (!aliases || !Array.isArray(aliases)) return [];
352
+ return aliases.map((alias) => ({
353
+ name: String(alias["@_name"] ?? ""),
354
+ cType: String(alias["@_c:type"] ?? ""),
355
+ targetType: this.parseType(alias.type as Record<string, unknown> | undefined),
356
+ doc: extractDoc(alias),
357
+ }));
358
+ }
359
+
360
+ private parseProperties(properties: Record<string, unknown>[]): RawProperty[] {
361
+ if (!properties || !Array.isArray(properties)) return [];
362
+ return properties.map((prop) => {
363
+ let getter = prop["@_getter"] ? String(prop["@_getter"]) : undefined;
364
+ let setter = prop["@_setter"] ? String(prop["@_setter"]) : undefined;
365
+
366
+ const attributes = prop.attribute as Record<string, unknown>[] | Record<string, unknown> | undefined;
367
+ if (attributes) {
368
+ const attrList = Array.isArray(attributes) ? attributes : [attributes];
369
+ for (const attr of attrList) {
370
+ if (attr["@_name"] === "org.gtk.Property.get" && attr["@_value"]) {
371
+ getter = String(attr["@_value"]);
372
+ } else if (attr["@_name"] === "org.gtk.Property.set" && attr["@_value"]) {
373
+ setter = String(attr["@_value"]);
374
+ }
375
+ }
376
+ }
377
+
378
+ return {
379
+ name: String(prop["@_name"] ?? ""),
380
+ type: this.parseType((prop.type ?? prop.array) as Record<string, unknown> | undefined),
381
+ readable: prop["@_readable"] !== "0",
382
+ writable: prop["@_writable"] === "1",
383
+ constructOnly: prop["@_construct-only"] === "1",
384
+ defaultValueRaw: prop["@_default-value"] !== undefined ? String(prop["@_default-value"]) : undefined,
385
+ getter,
386
+ setter,
387
+ doc: extractDoc(prop),
388
+ };
389
+ });
390
+ }
391
+
392
+ private parseSignals(signals: Record<string, unknown>[]): RawSignal[] {
393
+ if (!signals || !Array.isArray(signals)) return [];
394
+ return signals.map((signal) => {
395
+ const whenValue = String(signal["@_when"] ?? "last");
396
+ const validWhen = whenValue === "first" || whenValue === "last" || whenValue === "cleanup";
397
+ return {
398
+ name: String(signal["@_name"] ?? ""),
399
+ when: validWhen ? (whenValue as "first" | "last" | "cleanup") : "last",
400
+ returnType: signal["return-value"]
401
+ ? this.parseReturnType(signal["return-value"] as Record<string, unknown>)
402
+ : undefined,
403
+ parameters:
404
+ signal.parameters && typeof signal.parameters === "object" && signal.parameters !== null
405
+ ? this.parseParameters(signal.parameters as Record<string, unknown>)
406
+ : [],
407
+ doc: extractDoc(signal),
408
+ };
409
+ });
410
+ }
411
+
412
+ private parseFields(fields: Record<string, unknown>[]): RawField[] {
413
+ if (!fields || !Array.isArray(fields)) return [];
414
+ return fields
415
+ .filter((field) => field.callback === undefined)
416
+ .map((field) => ({
417
+ name: String(field["@_name"] ?? ""),
418
+ type: this.parseType((field.type ?? field.array) as Record<string, unknown> | undefined),
419
+ writable: field["@_writable"] === "1",
420
+ readable: field["@_readable"] !== "0",
421
+ private: field["@_private"] === "1",
422
+ doc: extractDoc(field),
423
+ }));
424
+ }
425
+
426
+ private extractParametersNode(node: Record<string, unknown>): Record<string, unknown> {
427
+ return node.parameters && typeof node.parameters === "object" && node.parameters !== null
428
+ ? (node.parameters as Record<string, unknown>)
429
+ : {};
430
+ }
431
+
432
+ private parseParameters(parametersNode: Record<string, unknown>): RawParameter[] {
433
+ if (!parametersNode?.parameter) return [];
434
+ const params = Array.isArray(parametersNode.parameter) ? parametersNode.parameter : [parametersNode.parameter];
320
435
  return params.map((param: Record<string, unknown>) => this.parseSingleParameter(param));
321
436
  }
322
437
 
323
438
  private parseInstanceParameter(parametersNode: Record<string, unknown>): RawParameter | undefined {
324
439
  const instanceParam = parametersNode?.["instance-parameter"] as Record<string, unknown> | undefined;
325
- if (!instanceParam) {
326
- return undefined;
327
- }
440
+ if (!instanceParam) return undefined;
328
441
  return this.parseSingleParameter(instanceParam);
329
442
  }
330
443
 
@@ -341,7 +454,7 @@ export class RawGirParser {
341
454
  callerAllocates: callerAllocates === "1",
342
455
  nullable: param["@_nullable"] === "1",
343
456
  optional: param["@_allow-none"] === "1" || param["@_optional"] === "1",
344
- scope: scope as "async" | "call" | "notified" | undefined,
457
+ scope: scope as "async" | "call" | "notified" | "forever" | undefined,
345
458
  closure: closure !== undefined ? parseInt(closure, 10) : undefined,
346
459
  destroy: destroy !== undefined ? parseInt(destroy, 10) : undefined,
347
460
  transferOwnership:
@@ -353,9 +466,7 @@ export class RawGirParser {
353
466
  }
354
467
 
355
468
  private parseReturnType(returnValue: Record<string, unknown> | undefined): RawType {
356
- if (!returnValue) {
357
- return { name: "void" };
358
- }
469
+ if (!returnValue) return { name: "void" };
359
470
  const type = this.parseType((returnValue.type ?? returnValue.array) as Record<string, unknown> | undefined);
360
471
  const transferOwnership = returnValue["@_transfer-ownership"] as string | undefined;
361
472
  if (transferOwnership === "none" || transferOwnership === "full" || transferOwnership === "container") {
@@ -368,21 +479,15 @@ export class RawGirParser {
368
479
  }
369
480
 
370
481
  private parseType(typeNode: Record<string, unknown> | undefined): RawType {
371
- if (!typeNode) {
372
- return { name: "void" };
373
- }
482
+ if (!typeNode) return { name: "void" };
374
483
 
375
484
  const typeName = typeNode["@_name"] ? String(typeNode["@_name"]) : undefined;
376
485
  const cType = typeNode["@_c:type"] ? String(typeNode["@_c:type"]) : undefined;
377
486
 
378
487
  const containerResult = this.parseGLibContainerType(typeName, typeNode, cType);
379
- if (containerResult) {
380
- return containerResult;
381
- }
488
+ if (containerResult) return containerResult;
382
489
 
383
- if (typeName) {
384
- return { name: typeName, cType };
385
- }
490
+ if (typeName) return { name: typeName, cType };
386
491
 
387
492
  const isArrayNode =
388
493
  typeNode.type ||
@@ -391,17 +496,14 @@ export class RawGirParser {
391
496
  typeNode["@_length"] !== undefined;
392
497
 
393
498
  if (isArrayNode) {
394
- const lengthAttr = typeNode["@_length"];
395
- const zeroTerminatedAttr = typeNode["@_zero-terminated"];
396
- const fixedSizeAttr = typeNode["@_fixed-size"];
397
-
398
499
  return {
399
500
  name: "array",
400
501
  isArray: true,
401
502
  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,
503
+ sizeParamIndex: typeNode["@_length"] !== undefined ? Number(typeNode["@_length"]) : undefined,
504
+ zeroTerminated:
505
+ typeNode["@_zero-terminated"] !== undefined ? typeNode["@_zero-terminated"] !== "0" : undefined,
506
+ fixedSize: typeNode["@_fixed-size"] !== undefined ? Number(typeNode["@_fixed-size"]) : undefined,
405
507
  };
406
508
  }
407
509
 
@@ -461,6 +563,16 @@ export class RawGirParser {
461
563
  };
462
564
  }
463
565
 
566
+ if (typeName === "GLib.ByteArray") {
567
+ return {
568
+ name: typeName,
569
+ cType,
570
+ isArray: true,
571
+ containerType: "gbytearray" as ContainerType,
572
+ elementType: { name: "guint8", cType: "guint8" },
573
+ };
574
+ }
575
+
464
576
  if (typeName === "GLib.List" || typeName === "GLib.SList") {
465
577
  const innerType = (typeNode.type ?? typeNode.array) as Record<string, unknown> | undefined;
466
578
  const elementType = innerType ? this.parseType(innerType) : undefined;
@@ -476,158 +588,4 @@ export class RawGirParser {
476
588
 
477
589
  return null;
478
590
  }
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
591
  }