@gtkx/runtime 0.0.1 → 1.0.0-rc.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 (123) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +171 -3
  3. package/dist/arg.d.ts +13 -0
  4. package/dist/arg.d.ts.map +1 -0
  5. package/dist/arg.js +6 -0
  6. package/dist/arg.js.map +1 -0
  7. package/dist/bind.d.ts +5 -0
  8. package/dist/bind.d.ts.map +1 -0
  9. package/dist/bind.js +19 -0
  10. package/dist/bind.js.map +1 -0
  11. package/dist/callback.d.ts +13 -0
  12. package/dist/callback.d.ts.map +1 -0
  13. package/dist/callback.js +86 -0
  14. package/dist/callback.js.map +1 -0
  15. package/dist/descriptors.d.ts +158 -0
  16. package/dist/descriptors.d.ts.map +1 -0
  17. package/dist/descriptors.js +135 -0
  18. package/dist/descriptors.js.map +1 -0
  19. package/dist/error.d.ts +24 -0
  20. package/dist/error.d.ts.map +1 -0
  21. package/dist/error.js +33 -0
  22. package/dist/error.js.map +1 -0
  23. package/dist/exit-hook.d.ts +2 -0
  24. package/dist/exit-hook.d.ts.map +1 -0
  25. package/dist/exit-hook.js +3 -0
  26. package/dist/exit-hook.js.map +1 -0
  27. package/dist/fn.d.ts +10 -0
  28. package/dist/fn.d.ts.map +1 -0
  29. package/dist/fn.js +90 -0
  30. package/dist/fn.js.map +1 -0
  31. package/dist/index.d.ts +16 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +16 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/internal.d.ts +5 -0
  36. package/dist/internal.d.ts.map +1 -0
  37. package/dist/internal.js +5 -0
  38. package/dist/internal.js.map +1 -0
  39. package/dist/library.d.ts +8 -0
  40. package/dist/library.d.ts.map +1 -0
  41. package/dist/library.js +14 -0
  42. package/dist/library.js.map +1 -0
  43. package/dist/lifecycle.d.ts +42 -0
  44. package/dist/lifecycle.d.ts.map +1 -0
  45. package/dist/lifecycle.js +70 -0
  46. package/dist/lifecycle.js.map +1 -0
  47. package/dist/listeners.d.ts +36 -0
  48. package/dist/listeners.d.ts.map +1 -0
  49. package/dist/listeners.js +85 -0
  50. package/dist/listeners.js.map +1 -0
  51. package/dist/mixin.d.ts +13 -0
  52. package/dist/mixin.d.ts.map +1 -0
  53. package/dist/mixin.js +49 -0
  54. package/dist/mixin.js.map +1 -0
  55. package/dist/native-value.d.ts +13 -0
  56. package/dist/native-value.d.ts.map +1 -0
  57. package/dist/native-value.js +105 -0
  58. package/dist/native-value.js.map +1 -0
  59. package/dist/object.d.ts +32 -0
  60. package/dist/object.d.ts.map +1 -0
  61. package/dist/object.js +66 -0
  62. package/dist/object.js.map +1 -0
  63. package/dist/promisify.d.ts +21 -0
  64. package/dist/promisify.d.ts.map +1 -0
  65. package/dist/promisify.js +39 -0
  66. package/dist/promisify.js.map +1 -0
  67. package/dist/properties.d.ts +14 -0
  68. package/dist/properties.d.ts.map +1 -0
  69. package/dist/properties.js +79 -0
  70. package/dist/properties.js.map +1 -0
  71. package/dist/register-class.d.ts +25 -0
  72. package/dist/register-class.d.ts.map +1 -0
  73. package/dist/register-class.js +178 -0
  74. package/dist/register-class.js.map +1 -0
  75. package/dist/registry.d.ts +69 -0
  76. package/dist/registry.d.ts.map +1 -0
  77. package/dist/registry.js +186 -0
  78. package/dist/registry.js.map +1 -0
  79. package/dist/signal.d.ts +35 -0
  80. package/dist/signal.d.ts.map +1 -0
  81. package/dist/signal.js +95 -0
  82. package/dist/signal.js.map +1 -0
  83. package/dist/t.d.ts +50 -0
  84. package/dist/t.d.ts.map +1 -0
  85. package/dist/t.js +49 -0
  86. package/dist/t.js.map +1 -0
  87. package/dist/tuple.d.ts +7 -0
  88. package/dist/tuple.d.ts.map +1 -0
  89. package/dist/tuple.js +26 -0
  90. package/dist/tuple.js.map +1 -0
  91. package/dist/type.d.ts +88 -0
  92. package/dist/type.d.ts.map +1 -0
  93. package/dist/type.js +194 -0
  94. package/dist/type.js.map +1 -0
  95. package/dist/value.d.ts +29 -0
  96. package/dist/value.d.ts.map +1 -0
  97. package/dist/value.js +292 -0
  98. package/dist/value.js.map +1 -0
  99. package/package.json +54 -13
  100. package/src/arg.ts +15 -0
  101. package/src/bind.ts +31 -0
  102. package/src/callback.ts +149 -0
  103. package/src/descriptors.ts +350 -0
  104. package/src/error.ts +58 -0
  105. package/src/exit-hook.ts +3 -0
  106. package/src/fn.ts +139 -0
  107. package/src/index.ts +59 -0
  108. package/src/internal.ts +12 -0
  109. package/src/library.ts +17 -0
  110. package/src/lifecycle.ts +101 -0
  111. package/src/listeners.ts +112 -0
  112. package/src/mixin.ts +73 -0
  113. package/src/native-value.ts +135 -0
  114. package/src/object.ts +95 -0
  115. package/src/promisify.ts +67 -0
  116. package/src/properties.ts +115 -0
  117. package/src/register-class.ts +305 -0
  118. package/src/registry.ts +311 -0
  119. package/src/signal.ts +192 -0
  120. package/src/t.ts +90 -0
  121. package/src/tuple.ts +37 -0
  122. package/src/type.ts +290 -0
  123. package/src/value.ts +430 -0
package/src/signal.ts ADDED
@@ -0,0 +1,192 @@
1
+ import type { Descriptor, ExternalObject, Handle } from "@gtkx/native";
2
+ import type { TypedClass } from "./type.js";
3
+ import { type Arg, isCallerAllocatedArg, isInoutArg, isOutputArg } from "./arg.js";
4
+ import { bind, createBindCache } from "./bind.js";
5
+ import { wrapCallback } from "./callback.js";
6
+ import {
7
+ arrayT,
8
+ biguint64T,
9
+ booleanT,
10
+ type CallbackDescriptor,
11
+ objectT,
12
+ stringT,
13
+ uint32T,
14
+ uint64T,
15
+ voidT,
16
+ } from "./descriptors.js";
17
+ import { LIB, VALUE_SIZE, VALUE_T } from "./library.js";
18
+ import { getHandle } from "./registry.js";
19
+ import { packTupleResult } from "./tuple.js";
20
+ import {
21
+ fromValue,
22
+ getBoxedValue,
23
+ inoutValueForBoxedDescriptor,
24
+ newValueForDescriptor,
25
+ outValueForBoxedDescriptor,
26
+ outValueForDescriptor,
27
+ toValue,
28
+ } from "./value.js";
29
+
30
+ /** Function invoked when a connected GObject signal is emitted. */
31
+ type SignalHandler = (...args: unknown[]) => unknown;
32
+
33
+ type SignalConnectSpec = {
34
+ callback: CallbackDescriptor;
35
+ handler: SignalHandler;
36
+ after: boolean;
37
+ };
38
+
39
+ type EmitArg = Arg & { value?: unknown };
40
+
41
+ const connectCache = createBindCache();
42
+ const gQuarkFromString = bind(LIB, "g_quark_from_string", [stringT("borrowed")], uint32T);
43
+ const gSignalLookup = bind(LIB, "g_signal_lookup", [stringT("borrowed"), biguint64T], uint32T);
44
+
45
+ const gSignalEmitv = bind(
46
+ LIB,
47
+ "g_signal_emitv",
48
+ [arrayT(VALUE_T, "array", "borrowed", { elementSize: VALUE_SIZE }), uint32T, uint32T, VALUE_T],
49
+ voidT,
50
+ );
51
+
52
+ const gSignalHandlersBlockMatched = bind(
53
+ LIB,
54
+ "g_signal_handlers_block_matched",
55
+ [objectT("borrowed"), uint32T, uint32T, uint32T, objectT("borrowed"), objectT("borrowed"), objectT("borrowed")],
56
+ uint32T,
57
+ );
58
+
59
+ const gSignalHandlerIsConnected = bind(
60
+ LIB,
61
+ "g_signal_handler_is_connected",
62
+ [objectT("borrowed"), uint64T],
63
+ booleanT,
64
+ );
65
+
66
+ /** Returns the signal name without its detail suffix (the part after `::`). */
67
+ const getSignalBaseName = (signal: string): string => {
68
+ const detailIndex = signal.indexOf("::");
69
+
70
+ return detailIndex === -1 ? signal : signal.slice(0, detailIndex);
71
+ };
72
+
73
+ function getSignalDetailQuark(signal: string): number {
74
+ const detailIndex = signal.indexOf("::");
75
+
76
+ if (detailIndex === -1) {
77
+ return 0;
78
+ }
79
+
80
+ return gQuarkFromString(signal.slice(detailIndex + 2)) as number;
81
+ }
82
+
83
+ const isSignalHandlerConnected = (instance: object, handlerId: number): boolean =>
84
+ gSignalHandlerIsConnected(getHandle(instance), handlerId) as boolean;
85
+
86
+ const getSignalId = (instance: object, signal: string): number => {
87
+ const type: bigint = (instance as TypedClass).__type__;
88
+
89
+ return gSignalLookup(getSignalBaseName(signal), type) as number;
90
+ };
91
+
92
+ function connectBind(type: bigint, signal: string, callback: CallbackDescriptor): (...values: unknown[]) => unknown {
93
+ const key = `${String(type)}\0${getSignalBaseName(signal)}`;
94
+
95
+ return connectCache(
96
+ key,
97
+ LIB,
98
+ "g_signal_connect_data",
99
+ [objectT("borrowed"), stringT("borrowed"), callback, uint32T],
100
+ uint64T,
101
+ );
102
+ }
103
+
104
+ /**
105
+ * Connects a handler to a GObject signal on an instance and returns the handler id.
106
+ * @param instance Emitter to connect to.
107
+ * @param signal Signal name, optionally including a `::detail` suffix.
108
+ * @param spec Callback descriptor, handler function, and whether to run after the default handler.
109
+ */
110
+ function connectSignal(instance: object, signal: string, spec: SignalConnectSpec): number {
111
+ const { callback, handler, after } = spec;
112
+ const wrapped = wrapCallback(handler, callback, "emitter");
113
+ const type: bigint = (instance as TypedClass).__type__;
114
+ const connect = connectBind(type, signal, callback);
115
+
116
+ return connect(getHandle(instance), signal, wrapped, after ? 1 : 0) as number;
117
+ }
118
+
119
+ function blockMatchedSignalHandlers(instance: object, signal: string): void {
120
+ const signalId = getSignalId(instance, signal);
121
+ gSignalHandlersBlockMatched(getHandle(instance), 1, signalId, 0, undefined, undefined, undefined);
122
+ }
123
+
124
+ const createEmitValue = (arg: EmitArg): { value: ExternalObject<Handle>; read?: () => unknown } => {
125
+ if (!isOutputArg(arg)) {
126
+ return { value: toValue(arg.type, arg.value) };
127
+ }
128
+
129
+ if (isCallerAllocatedArg(arg)) {
130
+ if (isInoutArg(arg)) {
131
+ return { value: inoutValueForBoxedDescriptor(arg.type, arg.value as object) };
132
+ }
133
+
134
+ const value = outValueForBoxedDescriptor(arg.type, arg.value as object);
135
+
136
+ return { value, read: () => getBoxedValue(value) };
137
+ }
138
+
139
+ return isInoutArg(arg) ? outValueForDescriptor(arg.type, arg.value) : outValueForDescriptor(arg.type);
140
+ };
141
+
142
+ /**
143
+ * Emits a signal on an instance with the given arguments and returns its result
144
+ * combined with any output-argument values.
145
+ * @param instance Emitter to emit the signal on.
146
+ * @param signal Signal name, optionally including a `::detail` suffix.
147
+ * @param args Arguments to pass, including output and inout arguments.
148
+ * @param returnDescriptor Descriptor for the signal's return value, omitted when it returns void.
149
+ */
150
+ function emitSignal(instance: object, signal: string, args: EmitArg[], returnDescriptor?: Descriptor): unknown {
151
+ const signalId = getSignalId(instance, signal);
152
+ const detail = getSignalDetailQuark(signal);
153
+ const values: ExternalObject<Handle>[] = [toValue(objectT("full"), instance)];
154
+ const reads: (() => unknown)[] = [];
155
+
156
+ for (const arg of args) {
157
+ const { value, read } = createEmitValue(arg);
158
+ values.push(value);
159
+
160
+ if (read) {
161
+ reads.push(read);
162
+ }
163
+ }
164
+
165
+ if (returnDescriptor !== undefined) {
166
+ const returnValue = newValueForDescriptor(returnDescriptor);
167
+ gSignalEmitv(values, signalId, detail, returnValue);
168
+
169
+ return packTupleResult(
170
+ reads.map((read) => read()),
171
+ fromValue(returnValue),
172
+ true,
173
+ );
174
+ }
175
+
176
+ gSignalEmitv(values, signalId, detail, undefined);
177
+
178
+ return packTupleResult(
179
+ reads.map((read) => read()),
180
+ undefined,
181
+ false,
182
+ );
183
+ }
184
+
185
+ export {
186
+ getSignalBaseName,
187
+ connectSignal,
188
+ blockMatchedSignalHandlers,
189
+ emitSignal,
190
+ isSignalHandlerConnected,
191
+ type SignalHandler,
192
+ };
package/src/t.ts ADDED
@@ -0,0 +1,90 @@
1
+ import { bind } from "./bind.js";
2
+ import * as helpers from "./descriptors.js";
3
+ import { fn } from "./fn.js";
4
+
5
+ type T = {
6
+ bind: typeof bind;
7
+ int8: typeof helpers.int8T;
8
+ uint8: typeof helpers.uint8T;
9
+ int16: typeof helpers.int16T;
10
+ uint16: typeof helpers.uint16T;
11
+ int32: typeof helpers.int32T;
12
+ uint32: typeof helpers.uint32T;
13
+ int64: typeof helpers.int64T;
14
+ uint64: typeof helpers.uint64T;
15
+ bigint64: typeof helpers.bigint64T;
16
+ biguint64: typeof helpers.biguint64T;
17
+ gtype: typeof helpers.gtypeT;
18
+ float32: typeof helpers.float32T;
19
+ float64: typeof helpers.float64T;
20
+ boolean: typeof helpers.booleanT;
21
+ void: typeof helpers.voidT;
22
+ unichar: typeof helpers.unicharT;
23
+ buffer: typeof helpers.bufferT;
24
+ string: typeof helpers.stringT;
25
+ object: typeof helpers.objectT;
26
+ boxed: typeof helpers.boxedT;
27
+ struct: typeof helpers.structT;
28
+ fundamental: typeof helpers.fundamentalT;
29
+ ref: typeof helpers.refT;
30
+ hashTable: typeof helpers.hashTableT;
31
+ enum: typeof helpers.enumT;
32
+ flags: typeof helpers.flagsT;
33
+ array: typeof helpers.arrayT;
34
+ list: typeof helpers.listT;
35
+ slist: typeof helpers.slistT;
36
+ ptrArray: typeof helpers.ptrArrayT;
37
+ gArray: typeof helpers.gArrayT;
38
+ byteArray: typeof helpers.byteArrayT;
39
+ sizedArray: typeof helpers.sizedArrayT;
40
+ fixedArray: typeof helpers.fixedArrayT;
41
+ callback: typeof helpers.callbackT;
42
+ fn: typeof fn;
43
+ };
44
+
45
+ /**
46
+ * Type descriptor builder: a collection of factory helpers that describe C types for
47
+ * FFI marshalling, plus `bind` to bind a native function and `fn` to describe a
48
+ * function signature.
49
+ */
50
+ const t: T = {
51
+ bind,
52
+ int8: helpers.int8T,
53
+ uint8: helpers.uint8T,
54
+ int16: helpers.int16T,
55
+ uint16: helpers.uint16T,
56
+ int32: helpers.int32T,
57
+ uint32: helpers.uint32T,
58
+ int64: helpers.int64T,
59
+ uint64: helpers.uint64T,
60
+ bigint64: helpers.bigint64T,
61
+ biguint64: helpers.biguint64T,
62
+ gtype: helpers.gtypeT,
63
+ float32: helpers.float32T,
64
+ float64: helpers.float64T,
65
+ boolean: helpers.booleanT,
66
+ void: helpers.voidT,
67
+ unichar: helpers.unicharT,
68
+ buffer: helpers.bufferT,
69
+ string: helpers.stringT,
70
+ object: helpers.objectT,
71
+ boxed: helpers.boxedT,
72
+ struct: helpers.structT,
73
+ fundamental: helpers.fundamentalT,
74
+ ref: helpers.refT,
75
+ hashTable: helpers.hashTableT,
76
+ enum: helpers.enumT,
77
+ flags: helpers.flagsT,
78
+ array: helpers.arrayT,
79
+ list: helpers.listT,
80
+ slist: helpers.slistT,
81
+ ptrArray: helpers.ptrArrayT,
82
+ gArray: helpers.gArrayT,
83
+ byteArray: helpers.byteArrayT,
84
+ sizedArray: helpers.sizedArrayT,
85
+ fixedArray: helpers.fixedArrayT,
86
+ callback: helpers.callbackT,
87
+ fn,
88
+ };
89
+
90
+ export { t };
package/src/tuple.ts ADDED
@@ -0,0 +1,37 @@
1
+ const packTupleResult = (outs: unknown[], primary: unknown, hasPrimary: boolean): unknown => {
2
+ if (hasPrimary) {
3
+ return outs.length === 0 ? primary : [primary, ...outs];
4
+ }
5
+
6
+ if (outs.length === 0) {
7
+ return undefined;
8
+ }
9
+
10
+ if (outs.length === 1) {
11
+ return outs[0];
12
+ }
13
+
14
+ return outs;
15
+ };
16
+
17
+ const splitTupleResult = (
18
+ result: unknown,
19
+ hasPrimary: boolean,
20
+ outCount: number,
21
+ ): { primary: unknown; outValues: unknown[] } => {
22
+ if (hasPrimary) {
23
+ if (Array.isArray(result)) {
24
+ return { primary: result[0], outValues: result.slice(1) };
25
+ }
26
+
27
+ return { primary: result, outValues: [] };
28
+ }
29
+
30
+ if (outCount === 1) {
31
+ return { primary: undefined, outValues: [result] };
32
+ }
33
+
34
+ return { primary: undefined, outValues: Array.isArray(result) ? result : [] };
35
+ };
36
+
37
+ export { packTupleResult, splitTupleResult };
package/src/type.ts ADDED
@@ -0,0 +1,290 @@
1
+ import { type Descriptor, resolveType as nativeResolveType } from "@gtkx/native";
2
+ import { bind } from "./bind.js";
3
+ import {
4
+ type ArrayDescriptor,
5
+ biguint64T,
6
+ booleanT,
7
+ type BoxedDescriptor,
8
+ type FundamentalDescriptor,
9
+ refT,
10
+ sizedArrayT,
11
+ stringT,
12
+ uint32T,
13
+ } from "./descriptors.js";
14
+ import { LIB } from "./library.js";
15
+
16
+ /** Object tagged with its GLib type through a `__type__` GType field. */
17
+ type TypedClass = {
18
+ __type__: bigint;
19
+ };
20
+
21
+ type ResolvableKind = "enum" | "flags" | "boxed" | "fundamental" | "array";
22
+ /** Descriptor kinds whose GType is resolved from library metadata rather than a fixed fundamental type. */
23
+ type ResolvableDescriptor = Extract<Descriptor, { kind: ResolvableKind }>;
24
+
25
+ const resolvedTypeCache: Map<string, bigint> = new Map();
26
+ const gTypeFromName = bind(LIB, "g_type_from_name", [stringT("borrowed")], biguint64T);
27
+ const gTypeIsA = bind(LIB, "g_type_is_a", [biguint64T, biguint64T], booleanT);
28
+ const gTypeParent = bind(LIB, "g_type_parent", [biguint64T], biguint64T);
29
+ const gTypeFundamental = bind(LIB, "g_type_fundamental", [biguint64T], biguint64T);
30
+ const gTypeName = bind(LIB, "g_type_name", [biguint64T], stringT("borrowed"));
31
+ const gTypeInterfaces = bind(LIB, "g_type_interfaces", [biguint64T, refT(uint32T)], sizedArrayT(biguint64T, 1, "full"));
32
+ /** GType tag for an invalid or uninitialized type. */
33
+ const TYPE_INVALID = 0n;
34
+ /** GType tag for the absence of a value (`void`). */
35
+ const TYPE_NONE: bigint = typeFromName("void");
36
+ /** GType tag for the base GInterface type. */
37
+ const TYPE_INTERFACE: bigint = typeFromName("GInterface");
38
+ /** GType tag for `gchar` (signed 8-bit integer). */
39
+ const TYPE_CHAR: bigint = typeFromName("gchar");
40
+ /** GType tag for `guchar` (unsigned 8-bit integer). */
41
+ const TYPE_UCHAR: bigint = typeFromName("guchar");
42
+ /** GType tag for `gboolean`. */
43
+ const TYPE_BOOLEAN: bigint = typeFromName("gboolean");
44
+ /** GType tag for `gint`. */
45
+ const TYPE_INT: bigint = typeFromName("gint");
46
+ /** GType tag for `guint`. */
47
+ const TYPE_UINT: bigint = typeFromName("guint");
48
+ /** GType tag for `glong`. */
49
+ const TYPE_LONG: bigint = typeFromName("glong");
50
+ /** GType tag for `gulong`. */
51
+ const TYPE_ULONG: bigint = typeFromName("gulong");
52
+ /** GType tag for `gint64`. */
53
+ const TYPE_INT64: bigint = typeFromName("gint64");
54
+ /** GType tag for `guint64`. */
55
+ const TYPE_UINT64: bigint = typeFromName("guint64");
56
+ /** GType tag for the base GEnum type. */
57
+ const TYPE_ENUM: bigint = typeFromName("GEnum");
58
+ /** GType tag for the base GFlags type. */
59
+ const TYPE_FLAGS: bigint = typeFromName("GFlags");
60
+ /** GType tag for `gfloat`. */
61
+ const TYPE_FLOAT: bigint = typeFromName("gfloat");
62
+ /** GType tag for `gdouble`. */
63
+ const TYPE_DOUBLE: bigint = typeFromName("gdouble");
64
+ /** GType tag for `gchararray` (a nul-terminated C string). */
65
+ const TYPE_STRING: bigint = typeFromName("gchararray");
66
+ /** GType tag for `gpointer` (an opaque pointer). */
67
+ const TYPE_POINTER: bigint = typeFromName("gpointer");
68
+ /** GType tag for the base GBoxed type. */
69
+ const TYPE_BOXED: bigint = typeFromName("GBoxed");
70
+ /** GType tag for the base GParam type. */
71
+ const TYPE_PARAM: bigint = typeFromName("GParam");
72
+ /** GType tag for the base GObject type. */
73
+ const TYPE_OBJECT: bigint = typeFromName("GObject");
74
+ /** GType tag for a GType value itself. */
75
+ const TYPE_GTYPE: bigint = typeFromName("GType");
76
+ /** GType tag for GVariant. */
77
+ const TYPE_VARIANT: bigint = typeFromName("GVariant");
78
+ /** GType tag for a Unicode character stored as `guint`. */
79
+ const TYPE_UNICHAR: bigint = typeFromName("guint");
80
+ const getErrorType: () => bigint = lazyType("GError");
81
+ const getStrvType: () => bigint = lazyType("GStrv");
82
+
83
+ const PLAIN_DESCRIPTOR_TYPES: Partial<Record<Descriptor["kind"], bigint>> = {
84
+ boolean: TYPE_BOOLEAN,
85
+ string: TYPE_STRING,
86
+ int8: TYPE_INT,
87
+ int16: TYPE_INT,
88
+ int32: TYPE_INT,
89
+ uint8: TYPE_UINT,
90
+ uint16: TYPE_UINT,
91
+ uint32: TYPE_UINT,
92
+ int64: TYPE_INT64,
93
+ bigint64: TYPE_INT64,
94
+ uint64: TYPE_UINT64,
95
+ biguint64: TYPE_UINT64,
96
+ float32: TYPE_FLOAT,
97
+ float64: TYPE_DOUBLE,
98
+ object: TYPE_OBJECT,
99
+ };
100
+
101
+ const RESOLVABLE_DESCRIPTOR_KINDS: Set<Descriptor["kind"]> = new Set<ResolvableKind>([
102
+ "enum",
103
+ "flags",
104
+ "boxed",
105
+ "fundamental",
106
+ "array",
107
+ ]);
108
+
109
+ function lazyType(name: string): () => bigint {
110
+ let cached: bigint | undefined;
111
+
112
+ return () => {
113
+ cached ??= typeFromName(name);
114
+
115
+ return cached;
116
+ };
117
+ }
118
+
119
+ const isTypedClass = (value: unknown): value is TypedClass =>
120
+ typeof value === "object" && value !== null && "__type__" in value && typeof value.__type__ === "bigint";
121
+
122
+ /** Returns whether `type` is `ancestorType` or descends from it. */
123
+ function typeIsA(type: bigint, ancestorType: bigint): boolean {
124
+ return gTypeIsA(type, ancestorType) as boolean;
125
+ }
126
+
127
+ /** Returns the immediate parent GType of the given type. */
128
+ function typeParent(type: bigint): bigint {
129
+ return gTypeParent(type) as bigint;
130
+ }
131
+
132
+ /** Returns the GTypes of the interfaces implemented by the given type. */
133
+ function typeInterfaces(type: bigint): bigint[] {
134
+ const nInterfacesRef = { value: 0 };
135
+
136
+ return gTypeInterfaces(type, nInterfacesRef) as bigint[];
137
+ }
138
+
139
+ /** Returns the GType registered under the given name, or `TYPE_INVALID` if none exists. */
140
+ function typeFromName(name: string): bigint {
141
+ return gTypeFromName(name) as bigint;
142
+ }
143
+
144
+ function typeFundamental(type: bigint): bigint {
145
+ return gTypeFundamental(type) as bigint;
146
+ }
147
+
148
+ /** Returns the registered name of a GType, or null if it has none. */
149
+ function typeName(type: bigint): string | null {
150
+ return (gTypeName(type) ?? null) as string | null;
151
+ }
152
+
153
+ /** Returns whether `value` is a typed wrapper whose GType is or descends from `gtype`. */
154
+ function valueIsA(value: unknown, gtype: bigint): boolean {
155
+ return isTypedClass(value) && typeIsA(value.__type__, gtype);
156
+ }
157
+
158
+ /**
159
+ * Resolves the GType produced by a `get_type` function in a shared library, caching
160
+ * the result per library and function name.
161
+ * @param sharedLibrary Path or name of the shared library exporting the function.
162
+ * @param typeFnName Name of the `*_get_type` function to call.
163
+ */
164
+ const resolveType = (sharedLibrary: string, typeFnName: string): bigint => {
165
+ const key = `${sharedLibrary}:${typeFnName}`;
166
+ const cached = resolvedTypeCache.get(key);
167
+
168
+ if (cached !== undefined) {
169
+ return cached;
170
+ }
171
+
172
+ const gtype = nativeResolveType(sharedLibrary, typeFnName);
173
+ resolvedTypeCache.set(key, gtype);
174
+
175
+ return gtype;
176
+ };
177
+
178
+ const resolveBoxedType = (descriptor: BoxedDescriptor): bigint => {
179
+ if (descriptor.getTypeFnName && descriptor.sharedLibrary) {
180
+ return resolveType(descriptor.sharedLibrary, descriptor.getTypeFnName);
181
+ }
182
+
183
+ const gtype = typeFromName(descriptor.typeName);
184
+
185
+ if (gtype === TYPE_INVALID) {
186
+ throw new Error(`Cannot resolve gtype for boxed type '${descriptor.typeName}'`);
187
+ }
188
+
189
+ return gtype;
190
+ };
191
+
192
+ const resolveFundamentalType = (descriptor: FundamentalDescriptor): bigint => {
193
+ if (descriptor.typeName) {
194
+ const gtype = typeFromName(descriptor.typeName);
195
+
196
+ if (gtype !== TYPE_INVALID) {
197
+ return gtype;
198
+ }
199
+ }
200
+
201
+ throw new Error("Cannot resolve gtype for fundamental type without a typeName");
202
+ };
203
+
204
+ function resolveArrayType(descriptor: ArrayDescriptor): bigint {
205
+ if (descriptor.itemDescriptor.kind === "string" && descriptor.arrayKind === "array") {
206
+ return getStrvType();
207
+ }
208
+
209
+ throw new Error(`Unsupported array type ${descriptor.arrayKind} of ${descriptor.itemDescriptor.kind}`);
210
+ }
211
+
212
+ function isResolvableDescriptor(descriptor: Descriptor): descriptor is ResolvableDescriptor {
213
+ return RESOLVABLE_DESCRIPTOR_KINDS.has(descriptor.kind);
214
+ }
215
+
216
+ function resolveDescriptorType(descriptor: Descriptor): bigint {
217
+ if (descriptor.kind === "biguint64" && "type" in descriptor) {
218
+ return TYPE_GTYPE;
219
+ }
220
+
221
+ const plain = PLAIN_DESCRIPTOR_TYPES[descriptor.kind];
222
+
223
+ if (plain !== undefined) {
224
+ return plain;
225
+ }
226
+
227
+ if (!isResolvableDescriptor(descriptor)) {
228
+ throw new Error(`Unsupported type descriptor '${descriptor.kind}'`);
229
+ }
230
+
231
+ switch (descriptor.kind) {
232
+ case "enum":
233
+ case "flags": {
234
+ return resolveType(descriptor.sharedLibrary, descriptor.getTypeFnName);
235
+ }
236
+ case "boxed": {
237
+ return resolveBoxedType(descriptor);
238
+ }
239
+ case "fundamental": {
240
+ return resolveFundamentalType(descriptor);
241
+ }
242
+ case "array": {
243
+ return resolveArrayType(descriptor);
244
+ }
245
+ }
246
+ }
247
+
248
+ export {
249
+ TYPE_INVALID,
250
+ TYPE_NONE,
251
+ TYPE_INTERFACE,
252
+ TYPE_CHAR,
253
+ TYPE_UCHAR,
254
+ TYPE_BOOLEAN,
255
+ TYPE_INT,
256
+ TYPE_UINT,
257
+ TYPE_LONG,
258
+ TYPE_ULONG,
259
+ TYPE_INT64,
260
+ TYPE_UINT64,
261
+ TYPE_ENUM,
262
+ TYPE_FLAGS,
263
+ TYPE_FLOAT,
264
+ TYPE_DOUBLE,
265
+ TYPE_STRING,
266
+ TYPE_POINTER,
267
+ TYPE_BOXED,
268
+ TYPE_PARAM,
269
+ TYPE_OBJECT,
270
+ TYPE_GTYPE,
271
+ TYPE_VARIANT,
272
+ TYPE_UNICHAR,
273
+ getErrorType,
274
+ getStrvType,
275
+ isResolvableDescriptor,
276
+ isTypedClass,
277
+ typeIsA,
278
+ typeParent,
279
+ typeInterfaces,
280
+ typeFromName,
281
+ typeFundamental,
282
+ typeName,
283
+ valueIsA,
284
+ resolveType,
285
+ resolveBoxedType,
286
+ resolveFundamentalType,
287
+ resolveDescriptorType,
288
+ type ResolvableDescriptor,
289
+ type TypedClass,
290
+ };