@gjsify/node-gi 0.36.0 → 0.38.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.
package/index.js CHANGED
@@ -1,63 +1,50 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  // @gjsify/node-gi — thin ESM loader for the native GObject-Introspection addon.
3
3
  //
4
- // Reference: refs/node-gtk (romgrk, MIT). Hand-authored loader shim — a native
5
- // package's JS entry is a loader, not a tsc artifact, and the repo ignores
6
- // `lib/`, so it lives at the package root. The native binary is built by
7
- // node-gyp into build/{Release,Debug}; a published package instead ships a
8
- // prebuild under prebuilds/<platform>-<arch>/.
9
- //
10
- // The addon is Node-API (ABI-stable), so it loads unchanged on Node, Bun and
11
- // Deno — the three runtimes that implement Node-API. Runtime-specific behaviour
12
- // (the libuv main-loop bridge is Node-only) is gated in gi.js off RUNTIME below.
4
+ // Reference: refs/node-gtk (romgrk, MIT). Hand-authored and at the package root:
5
+ // a native package's JS entry is a loader, not a tsc artifact, and the repo
6
+ // ignores `lib/`. The addon is Node-API (ABI-stable), so the same binary loads on
7
+ // Node, Bun and Deno; runtime-specific behaviour is gated off RUNTIME below.
13
8
  import { createRequire } from 'node:module';
14
9
  import { existsSync } from 'node:fs';
15
10
  import { hostTarget, nativeCandidates, packageRoot } from './native-paths.js';
16
11
  import { describeAddonLoadFailure } from './load-diagnostics.js';
17
12
  import {
18
13
  activateBundledGtkRuntime,
14
+ activateGiLibraryPath,
19
15
  maybePrependGtkRuntimeDllPath,
20
16
  maybeReexecForGtkRuntime,
21
17
  maybeWireGtkWindowingEnv,
22
18
  } from './gtk-runtime.js';
23
19
 
24
- // macOS batteries-included GTK: if a relocated bundle ships for this platform,
25
- // re-exec ONCE with DYLD_FALLBACK_LIBRARY_PATH pointed at it BEFORE the addon (and
26
- // its GTK dependency closure) is dlopen'd — dyld only reads that var at launch, and
27
- // GObject-Introspection needs it to g_module_open type backers (get_type / Pango /
28
- // Gdk / Graphene) by leaf soname. No-op off darwin, without a bundle, or once the
29
- // fallback already covers the bundle (the re-exec'd child). Never returns on re-exec.
20
+ // macOS bundled GTK: dyld reads DYLD_FALLBACK_LIBRARY_PATH only at launch, and GI
21
+ // needs it to g_module_open typelib backers (get_type / Pango / Gdk / Graphene) by
22
+ // leaf soname — so the re-exec must happen BEFORE the addon's GTK closure is
23
+ // dlopen'd. Never returns when it re-execs.
30
24
  maybeReexecForGtkRuntime();
31
25
 
32
- // Windows batteries-included GTK: if a bundle ships for this platform, prepend its
33
- // gtk/bin DLL dir to process.env.PATH BEFORE the addon is require()'d below. Windows
34
- // re-reads the DLL search path at each LoadLibrary (unlike dyld's launch-time
35
- // capture), so this in-process PATH mutation is enough for the addon's static DLL
36
- // imports AND the runtime g_module_open of typelib backers — no re-exec needed.
37
- // No-op off win32 / without a bundle.
26
+ // Windows bundled GTK: Windows re-reads the DLL search path at every LoadLibrary
27
+ // (unlike dyld's launch-time capture), so mutating process.env.PATH in-process
28
+ // before the require() below covers both the static DLL imports and the runtime
29
+ // g_module_open of typelib backers — no re-exec needed.
38
30
  maybePrependGtkRuntimeDllPath();
39
31
 
40
- // Windows FULL-WINDOWING GTK: when the bundle carries the runtime data a real GTK
41
- // WINDOW needs (compiled GSettings schemas, gdk-pixbuf loaders, icon themes,
42
- // fontconfig — the `--windowing` superset), wire the env vars that locate it
43
- // (GSETTINGS_SCHEMA_DIR / GDK_PIXBUF_MODULE_FILE / XDG_DATA_DIRS / FONTCONFIG_*).
44
- // Strict no-op off win32, without a bundle, or for the display-free bundle (no
45
- // windowing data) — so a display-free bundle load is byte-unchanged.
32
+ // Windows full-windowing GTK: point GSETTINGS_SCHEMA_DIR / GDK_PIXBUF_MODULE_FILE /
33
+ // XDG_DATA_DIRS / FONTCONFIG_* at the data a real GTK window needs. Strict no-op for
34
+ // the display-free bundle, which therefore loads byte-unchanged.
46
35
  maybeWireGtkWindowingEnv();
47
36
 
48
37
  const require = createRequire(import.meta.url);
49
- const here = packageRoot; // the package root, from the shared native-paths module
38
+ const here = packageRoot;
50
39
 
51
40
  /**
52
- * Which JS runtime we are on. The Node-API addon loads on all four: Node, Bun
53
- * and Deno implement Node-API natively; under GJS the `@gjsify/napi` shim is
54
- * the N-API host and the addon is loaded through it (`loadNative` below). The
55
- * libuv-backed main-loop bridge (startMainLoop) is Node-only — Deno exports no
56
- * libuv symbols and Bun panics on uv_backend_fd — so Bun/Deno use the portable
57
- * GLib-iteration pump instead (see gi.js), and GJS needs NEITHER: the host
58
- * loop already IS GLib's default main context. Detection order matters: GJS is
59
- * probed first via its own globals (`imports` + `print` — no other runtime
60
- * defines both), then Bun/Deno (both expose a `process` shim), then Node.
41
+ * Which JS runtime we are on. The addon loads on all four: Node/Bun/Deno implement
42
+ * Node-API natively, GJS hosts it through the `@gjsify/napi` shim. The libuv main-loop
43
+ * bridge (startMainLoop) is Node-only — Deno exports no libuv symbols and Bun panics on
44
+ * uv_backend_fd — so Bun/Deno use gi.js's portable GLib-iteration pump, and GJS needs
45
+ * neither (its host loop already IS GLib's default main context). Detection order is
46
+ * load-bearing: GJS first via `imports` + `print` (no other runtime defines both), then
47
+ * Bun/Deno, which both expose a `process` shim.
61
48
  * @type {'bun' | 'deno' | 'gjs' | 'node'}
62
49
  */
63
50
  export const RUNTIME =
@@ -72,12 +59,10 @@ export const RUNTIME =
72
59
  /** Whether we are on Node.js (the only runtime with the libuv main-loop bridge). */
73
60
  export const isNodeRuntime = RUNTIME === 'node';
74
61
 
75
- // GJS host mode: the addon is loaded through the @gjsify/napi shim, not
76
- // `require()`. The loader native is `globalThis.__gjsifyNapiLoadAddon`,
77
- // installed by `GjsifyNapi.init()`; if another consumer (the @gjsify/napi L1)
78
- // already captured + deleted it, init() re-defines it (idempotent). The
79
- // GjsifyNapi typelib must be resolvable (GI_TYPELIB_PATH/LD_LIBRARY_PATH →
80
- // the shim prebuild).
62
+ // GJS host mode: `GjsifyNapi.init()` installs the loader as
63
+ // `globalThis.__gjsifyNapiLoadAddon` and re-installs it if another consumer (the
64
+ // @gjsify/napi L1) already captured + deleted it. Needs the GjsifyNapi typelib
65
+ // resolvable via GI_TYPELIB_PATH/LD_LIBRARY_PATH.
81
66
  function gjsLoadAddon(path) {
82
67
  let load = globalThis.__gjsifyNapiLoadAddon;
83
68
  if (typeof load !== 'function') {
@@ -104,15 +89,13 @@ function loadNative() {
104
89
  try {
105
90
  return RUNTIME === 'gjs' ? gjsLoadAddon(candidate) : require(candidate);
106
91
  } catch (err) {
107
- // The addon FILE is here and still would not load. On win32 and
108
- // darwin that means its GTK dependency closure was not found, and
109
- // the raw error names only the .node — in the OS's language, about
110
- // a file that plainly exists. Reported from a clean Windows host as
111
- // `Error: Das angegebene Modul wurde nicht gefunden.` pointing at a
112
- // present node_gi.node (#1063), which names nothing a user can act
113
- // on. Same class as PR #994, where a missing load-time library
114
- // surfaced as "Unsupported type void, deriving from fundamental
115
- // void". The cause is knowable here, so say it.
92
+ // The addon FILE is here and still would not load — on win32/darwin
93
+ // that means a missing GTK dependency closure, but the raw error
94
+ // names only the .node, in the OS's language, about a file that
95
+ // plainly exists (#1063: `Das angegebene Modul wurde nicht
96
+ // gefunden.` on a present node_gi.node; #994: a missing load-time
97
+ // library surfacing as "Unsupported type void, deriving from
98
+ // fundamental void"). The cause is knowable here, so say it.
116
99
  throw new Error(describeAddonLoadFailure(candidate, err), { cause: err });
117
100
  }
118
101
  }
@@ -130,43 +113,39 @@ function loadNative() {
130
113
 
131
114
  const native = loadNative();
132
115
 
133
- // ---- batteries-included GTK runtime (macOS) ---------------------------------
134
- //
135
- // Before any namespace is required, activate a relocated GTK/GI runtime bundle
136
- // if one ships for this platform (@gjsify/gtk-runtime-<platform>-<arch>, or a
137
- // staged prebuilds/<platform>-<arch>/gtk/). This prepends the bundle's typelib
138
- // dir to the GIRepository search path (env-free) so gi:// namespaces load with
139
- // no Homebrew/system GTK. No-op on platforms without a bundle, and harmless when
140
- // none is present (the addon then uses the host's GTK as before). Scoped to
141
- // darwin + win32 — see gtk-runtime.js + @gjsify/gtk-runtime-{darwin-arm64,
142
- // darwin-x64,win32-x64}.
116
+ // Activate a relocated GTK/GI runtime bundle (@gjsify/gtk-runtime-<os>-<arch> or a
117
+ // staged prebuilds/<os>-<arch>/gtk/) BEFORE any namespace is required: it prepends the
118
+ // bundle's typelib dir to the GIRepository search path env-free, so gi:// namespaces
119
+ // load with no Homebrew/system GTK. Darwin + win32 only; without a bundle the addon
120
+ // uses the host GTK as before.
143
121
  try {
144
122
  activateBundledGtkRuntime(native);
145
123
  } catch {
146
124
  // Never fatal: a missing/partial bundle just leaves the host GTK in charge.
147
125
  }
148
126
 
149
- // ---- cross-runtime microtask checkpoint (Bun/Deno) --------------------------
150
- //
151
- // Node's napi_make_callback runs the nextTick + microtask checkpoint when its
152
- // callback scope closes, so promise continuations queued by a loop-dispatched
153
- // GLib→JS callback (a DBus method-call closure, a GLib timeout, a GTK signal)
154
- // drain at the callback boundary even while a blocking
155
- // GLib.MainLoop.run()/Gio.Application.run() owns the thread. Bun's and Deno's
156
- // N-API implementations perform NO checkpoint there (Deno's napi_make_callback
157
- // is a plain Function::Call; Deno's own FFI trampoline drains explicitly for
158
- // exactly this reason), and with the runtime's event loop paused for the
159
- // blocking run's lifetime the queue never drained: an async (Promise-returning)
160
- // DBus method handler never sent its reply — the client timed out — while sync
161
- // handlers worked. Register the runtime's own drain primitive with the engine;
162
- // the loop-dispatched trampolines (src/signals.cc / calls.cc / class.cc) invoke
163
- // it after each OUTERMOST callback returns (NodeGiMaybeDrainMicrotasks),
164
- // matching Node's checkpoint and GJS (SpiderMonkey drains the promise-job queue
165
- // when the last JS frame exits). Node never registers — its checkpoint already
166
- // runs natively, so the Node path is byte-identical to before.
167
- // GJS never registers: SpiderMonkey/GJS drains the promise-job queue from its
168
- // own main-loop source whenever the last JS frame exits — the exact semantic
169
- // this checkpoint recreates for Bun/Deno.
127
+ // And where the LIBRARIES those typelibs name actually live. Separate from the
128
+ // call above because it is not about a bundle: it applies to whichever GTK the
129
+ // policy chose, and it is the only loader repair bun and deno get on macOS —
130
+ // the re-exec above is Node-shaped and skips them.
131
+ try {
132
+ activateGiLibraryPath(native);
133
+ } catch {
134
+ // Same contract as above: never fatal.
135
+ }
136
+
137
+ // Cross-runtime microtask checkpoint (Bun/Deno only). Node's napi_make_callback runs
138
+ // the nextTick + microtask checkpoint when its callback scope closes, so promise
139
+ // continuations queued by a loop-dispatched GLib→JS callback drain at the callback
140
+ // boundary even while a blocking GLib.MainLoop.run()/Gio.Application.run() owns the
141
+ // thread; GJS gets the same from SpiderMonkey draining the promise-job queue when the
142
+ // last JS frame exits. Bun and Deno checkpoint NOWHERE there (Deno's
143
+ // napi_make_callback is a plain Function::Call), and with their event loop paused for
144
+ // the blocking run's lifetime the queue never drained: an async DBus method handler
145
+ // never sent its reply and the client timed out, while sync handlers worked. So
146
+ // register the runtime's own drain primitive; the loop-dispatched trampolines
147
+ // (src/signals.cc / calls.cc / class.cc) invoke it after each OUTERMOST callback
148
+ // returns (NodeGiMaybeDrainMicrotasks).
170
149
  if (!isNodeRuntime && RUNTIME !== 'gjs') {
171
150
  try {
172
151
  let drain = null;
@@ -176,613 +155,110 @@ if (!isNodeRuntime && RUNTIME !== 'gjs') {
176
155
  ({ drainMicrotasks: drain } = require('bun:jsc'));
177
156
  } else if (typeof globalThis.Deno?.internal === 'symbol') {
178
157
  // V8: the checkpoint needs BOTH queues. core.runMicrotasks is only
179
- // Isolate::PerformMicrotaskCheckpoint; Deno's node-compat
180
- // process.nextTick queue is a SEPARATE deno_core queue
181
- // (ext:deno_node/_next_tick.ts → core.queueNextTick) that only the
182
- // runtime's own event loop drains — and that loop is paused for the
183
- // whole lifetime of a blocking run(). node:stream delivers stream
184
- // 'end' via process.nextTick (endReadableNT), so with a
185
- // microtask-only drain any body consumption over a Readable
186
- // (@gjsify/fetch consumeBody → XHR arrayBuffer/text) got its chunks
187
- // (microtask-delivered) but never the end: every asset load hung at
188
- // readyState 3 forever on Deno while the SAME bundle settled on Bun,
189
- // whose nextTick rides JSC's microtask queue (bun:jsc
190
- // drainMicrotasks covers it). core.runNextTicks mirrors node's
191
- // task_queues.js runNextTicks: microtask checkpoint when no ticks
192
- // are scheduled, full processTicksAndRejections (ticks + microtasks
193
- // interleaved) when there are — one call, both queues. Regression:
194
- // test/blocking-run-checkpoint.test.mjs.
158
+ // Isolate::PerformMicrotaskCheckpoint, while Deno's node-compat
159
+ // process.nextTick queue is a SEPARATE deno_core queue that only the
160
+ // runtime's own event loop drains — and that loop is paused for the whole
161
+ // lifetime of a blocking run(). node:stream delivers 'end' via nextTick
162
+ // (endReadableNT), so a microtask-only drain got body chunks but never the
163
+ // end: every asset load hung at readyState 3 forever on Deno while the SAME
164
+ // bundle settled on Bun, whose nextTick rides JSC's microtask queue.
165
+ // core.runNextTicks covers both queues in one call, like node's
166
+ // task_queues.js. Regression: test/blocking-run-checkpoint.test.mjs.
195
167
  const core = globalThis.Deno[globalThis.Deno.internal]?.core;
196
168
  if (typeof core?.runNextTicks === 'function') {
197
169
  drain = () => core.runNextTicks();
198
170
  } else if (typeof core?.runMicrotasks === 'function') {
199
- // Older deno_core without runNextTicks: microtask-only drain
200
- // (pre-fix behaviour — promise continuations settle, nextTick
201
- // consumers like node:stream 'end' still starve).
171
+ // Older deno_core without runNextTicks: promise continuations
172
+ // settle, nextTick consumers like node:stream 'end' still starve.
202
173
  drain = () => core.runMicrotasks();
203
174
  }
204
175
  }
205
176
  if (typeof drain === 'function') native.setMicrotaskDrain(drain);
206
177
  } catch {
207
- // Unregistered ⇒ pre-fix behaviour on this runtime build: promise
208
- // continuations drain only when the runtime's own loop runs
209
- // (startMainContextPump); async DBus replies inside a BLOCKING run stay
178
+ // Unregistered ⇒ promise continuations drain only when the runtime's own loop
179
+ // runs (startMainContextPump), so async DBus replies inside a BLOCKING run stay
210
180
  // unavailable. Never fatal — the sync surface is unaffected.
211
181
  }
212
182
  }
213
183
 
214
- /**
215
- * Require a GObject-Introspection namespace and report its resolved version
216
- * and top-level info count. The Node twin of GJS's gi:// / imports.gi load step.
217
- * @param {string} namespace e.g. "GLib"
218
- * @param {string} [version] e.g. "2.0" (omit to let GIRepository resolve)
219
- * @returns {{ namespace: string, version: string, infoCount: number }}
220
- */
184
+ // Straight re-exports of the native engine surface. Each contract — arguments,
185
+ // ownership, the OUT/INOUT return-tuple convention, the keep-alive rules of the
186
+ // main-loop group — is documented once in index.d.ts, the published type surface;
187
+ // only what index.d.ts does not carry is noted here.
221
188
  export const requireNamespace = native.requireNamespace;
222
-
223
- /**
224
- * Enumerate the top-level introspection-info names of an already-required namespace.
225
- * @param {string} namespace
226
- * @returns {string[]}
227
- */
228
189
  export const listInfoNames = native.listInfoNames;
229
-
230
- /**
231
- * Classify a top-level namespace member (so the L1 wrapper knows whether it is a
232
- * constructible class, a callable function, an enum, a constant, …). Returns
233
- * `null` when the name is not found.
234
- * @param {string} namespace
235
- * @param {string} name
236
- * @returns {{ kind: 'function'|'object'|'interface'|'struct'|'union'|'enum'|'flags'|'constant'|'callback'|'other' } | null}
237
- */
238
190
  export const findInfo = native.findInfo;
239
-
240
- /**
241
- * Read a namespace-level GI constant (e.g. `GLib.PRIORITY_DEFAULT`) and marshal
242
- * it to a JS value.
243
- * @param {string} namespace
244
- * @param {string} name
245
- * @returns {unknown}
246
- */
247
191
  export const getConstantValue = native.getConstantValue;
248
-
249
- /**
250
- * Enumerate an enum/flags type's members as `{ rawGiName: number }` (the L1
251
- * wrapper re-keys them GJS-style: UPPER_CASE with `-` → `_`).
252
- * @param {string} namespace
253
- * @param {string} name
254
- * @returns {Record<string, number>}
255
- */
256
192
  export const getEnumValues = native.getEnumValues;
257
-
258
- /**
259
- * For an enum type registered as a GError domain (e.g. `Gio.IOErrorEnum`), report
260
- * its domain quark name + numeric quark; `null` for a plain enum. The L1 wrapper
261
- * attaches this to the enum object so `error.matches(Gio.IOErrorEnum, code)` can
262
- * resolve the enum to its error domain.
263
- * @param {string} namespace
264
- * @param {string} name
265
- * @returns {{ name: string, quark: number } | null}
266
- */
267
193
  export const getErrorDomain = native.getErrorDomain;
268
-
269
- /**
270
- * Register the L1 GLib.Error factory the engine calls when a GI invoke fails, so
271
- * a failed sync call throws a real `GLib.Error` (instanceof, with `.matches()`).
272
- * The builder receives `(domainName, domainQuark, code, message)` and returns the
273
- * Error to throw. Called once by the L1 layer at load.
274
- * @param {(domainName: string, domainQuark: number, code: number, message: string) => Error} builder
275
- * @returns {void}
276
- */
277
194
  export const setErrorBuilder = native.setErrorBuilder;
278
-
279
- /**
280
- * Prepend a directory to the GIRepository typelib search path (call before
281
- * requireNamespace for non-system typelibs).
282
- * @param {string} path
283
- * @returns {void}
284
- */
285
195
  export const prependSearchPath = native.prependSearchPath;
286
-
287
- /**
288
- * Invoke a namespace-level GObject-Introspection function (not an instance
289
- * method) with IN-only primitive/string arguments. The first marshalling slice;
290
- * instance methods, OUT/INOUT params and compound types follow.
291
- * @param {string} namespace e.g. "GLib"
292
- * @param {string} functionName e.g. "get_host_name"
293
- * @param {unknown[]} [args]
294
- * @returns {unknown}
295
- */
196
+ export const prependLibraryPath = native.prependLibraryPath;
296
197
  export const callFunction = native.callFunction;
297
-
298
- /**
299
- * Invoke an instance method on a GObject handle with IN-only
300
- * primitive/string/object/enum args. The method is resolved against the
301
- * instance's introspection type (own + implemented-interface methods, then up
302
- * the parent chain) — the Node twin of `obj.method(...)`.
303
- * @param {unknown} handle a handle from {@link newObject}
304
- * @param {string} methodName GI method name, e.g. "get_name"
305
- * @param {unknown[]} [args]
306
- * @returns {unknown}
307
- */
308
198
  export const callMethod = native.callMethod;
309
-
310
- /**
311
- * `true` iff {@link callMethod} would resolve an invocable instance method for
312
- * `methodName` (literal introspected name or snake_case alias, walking the same
313
- * own/interface/parent chain). Feature detection — never throws for a merely
314
- * unknown name; a non-introspectable instance reports `false`. Backs the L1
315
- * wrapper's GJS-parity property access (`obj.nonexistentMethod === undefined`).
316
- * @param {unknown} handle a handle from {@link newObject}
317
- * @param {string} methodName GI method name, e.g. "get_name"
318
- * @returns {boolean}
319
- */
320
199
  export const hasMethod = native.hasMethod;
321
-
322
- /**
323
- * Invoke a type-level constructor/static function (e.g. `Gio.File.new_for_path`,
324
- * `Gtk.Label.new`) — a function found on a type but taking no instance. The Node
325
- * twin of `Ns.Class.method(...)`.
326
- * @param {string} namespace e.g. "Gio"
327
- * @param {string} typeName e.g. "File"
328
- * @param {string} methodName e.g. "new_for_path"
329
- * @param {unknown[]} [args]
330
- * @returns {unknown}
331
- */
332
200
  export const callStaticMethod = native.callStaticMethod;
333
-
334
- /**
335
- * Construct a boxed/plain struct instance — the `new <Struct>()` path (GJS
336
- * gi/boxed.cpp parity). A struct WITH a 'new' constructor routes to it with
337
- * `args` (e.g. `GLib.MainLoop`); a struct WITHOUT one and ZERO args is
338
- * zero-allocated (`Graphene.Rect`, `Gdk.RGBA`); args without a 'new' throw.
339
- * @param {string} namespace e.g. "Graphene"
340
- * @param {string} typeName e.g. "Rect"
341
- * @param {unknown[]} [args]
342
- * @returns {unknown} boxed handle
343
- */
344
201
  export const constructStruct = native.constructStruct;
345
-
346
- /**
347
- * Construct a GObject of `namespace.typeName` with optional construct/settable
348
- * properties, returning an opaque handle owned by node-gi (released on GC).
349
- * @param {string} namespace e.g. "Gio"
350
- * @param {string} typeName e.g. "SimpleAction"
351
- * @param {Record<string, unknown>} [props]
352
- * @returns {unknown} opaque GObject handle
353
- */
354
202
  export const newObject = native.newObject;
355
-
356
- /**
357
- * Register a new GObject subclass of `parentNamespace.parentTypeName` named
358
- * `name`, inheriting the parent's class/instance layout, and return an opaque
359
- * type handle. Construct instances of it with {@link constructType}. The optional
360
- * `options` installs custom properties (backed by a per-instance value store),
361
- * signals, and vfunc overrides in the new type's `class_init` — the Node twin of
362
- * (the engine half of) GJS's `GObject.registerClass`. A `vfuncs` entry maps a
363
- * parent vfunc name (e.g. `constructed`) to a JS function that overrides it; the
364
- * override runs as a method on the instance (`this` is the GObject handle) and can
365
- * chain up to the parent implementation via {@link callParentVfunc}.
366
- * @param {string} name unique GType name, e.g. "MyAction"
367
- * @param {string} parentNamespace e.g. "Gio"
368
- * @param {string} parentTypeName e.g. "SimpleAction"
369
- * @param {{ properties?: Array<{name:string,type:string,flags?:number,default?:unknown,minimum?:number,maximum?:number}>, signals?: Array<{name:string,paramTypes?:string[],returnType?:string,flags?:number}>, vfuncs?: Record<string, (...args: unknown[]) => unknown> }} [options]
370
- * @returns {unknown} opaque type handle
371
- */
372
203
  export const registerClass = native.registerClass;
373
204
 
374
- /**
375
- * Register a new GObject subclass of an ALREADY-REGISTERED parent type, given the
376
- * parent's GType handle (from a previous {@link registerClass} / its `$gtype`)
377
- * rather than a `namespace.typeName`. This is the multi-level registered-of-
378
- * registered path: a registered (dynamic) parent has no introspection entry, so it
379
- * cannot be resolved by name. Inherited custom properties, signals and vfunc slots
380
- * of registered ancestors compose for free through normal GObject inheritance. The
381
- * L1 `GObject.registerClass` picks this variant (over {@link registerClass}) when
382
- * the nearest base is itself a registered class.
383
- * @param {string} name unique GType name, e.g. "AdwStorybookClamp"
384
- * @param {unknown} parentGType a GType handle from {@link registerClass} (the parent's `$gtype`)
385
- * @param {{ properties?, signals?, vfuncs?, template?, cssName?, children?, internalChildren? }} [options]
386
- * @returns {unknown} opaque type handle
387
- */
205
+ // Subclass an ALREADY-REGISTERED parent by its GType handle instead of a
206
+ // `namespace.typeName`: a registered (dynamic) parent has no introspection entry, so it
207
+ // cannot be resolved by name. Custom properties, signals and vfunc slots of registered
208
+ // ancestors compose through ordinary GObject inheritance. The L1 `GObject.registerClass`
209
+ // picks this variant when the nearest base is itself a registered class.
388
210
  export const registerClassFromGType = native.registerClassFromGType;
389
211
 
390
- /**
391
- * Construct a GObject of a registered type handle (from {@link registerClass})
392
- * with optional construct/settable properties, returning an owned handle. For a
393
- * templated type the engine runs `gtk_widget_init_template` before returning.
394
- * @param {unknown} typeHandle a handle from {@link registerClass}
395
- * @param {Record<string, unknown>} [props]
396
- * @returns {unknown} opaque GObject handle
397
- */
398
212
  export const constructType = native.constructType;
399
-
400
- /**
401
- * Chain up to the parent implementation of an overridden vfunc — the engine half
402
- * of `super.vfunc_<name>(...)`. Invokes the function that was in the instance
403
- * type's vtable slot BEFORE the registerClass override was installed (the C
404
- * default, or a JS override further up the chain), with `handle` as the instance
405
- * and `args` as the vfunc's declared arguments; returns the marshalled vfunc
406
- * return (or undefined for a void vfunc). The L1 layer wires it to
407
- * `super.vfunc_<name>` via the introspected base class's prototype chain. Throws
408
- * if no overridden vfunc by that name owns a slot on the instance's type.
409
- * @param {unknown} handle a GObject handle (the instance / `this` in the override)
410
- * @param {string} vfuncName the vfunc name without the `vfunc_` prefix, e.g. "constructed"
411
- * @param {unknown[]} [args] the vfunc's declared IN arguments
412
- * @returns {unknown}
413
- */
414
213
  export const callParentVfunc = native.callParentVfunc;
415
-
416
- /**
417
- * Resolve a Gtk.Widget composite-template child (bound via the registerClass
418
- * Children/InternalChildren options) by id on a templated instance —
419
- * `gtk_widget_get_template_child(widget, G_OBJECT_TYPE(widget), name)`. Returns the
420
- * child as a wrapped GObject handle (borrowed; owned by the parent via the
421
- * template) or `null`. The L1 layer assigns it onto the instance (`this.<name>` /
422
- * `this._<name>`).
423
- * @param {unknown} handle a templated GObject handle from {@link constructType}
424
- * @param {string} name the template child id
425
- * @returns {unknown} opaque child GObject handle, or null
426
- */
427
214
  export const getTemplateChild = native.getTemplateChild;
428
-
429
- /**
430
- * Read a GObject property.
431
- * @param {unknown} handle a handle from {@link newObject}
432
- * @param {string} name property name (kebab- or snake-case as GObject expects)
433
- * @returns {unknown}
434
- */
435
215
  export const getProperty = native.getProperty;
436
-
437
- /**
438
- * Write a GObject property.
439
- * @param {unknown} handle a handle from {@link newObject}
440
- * @param {string} name property name
441
- * @param {unknown} value
442
- * @returns {void}
443
- */
444
216
  export const setProperty = native.setProperty;
445
-
446
- /**
447
- * Whether the instance's type has a GObject property by this name (kebab- or
448
- * snake-case). The L1 wrapper uses it to route `obj.foo` to a property read vs
449
- * an `obj.foo()` method call.
450
- * @param {unknown} handle
451
- * @param {string} name
452
- * @returns {boolean}
453
- */
454
217
  export const hasProperty = native.hasProperty;
455
-
456
- /**
457
- * The runtime GType name of a GObject handle (e.g. "GSimpleAction").
458
- * @param {unknown} handle
459
- * @returns {string}
460
- */
461
218
  export const getTypeName = native.getTypeName;
462
-
463
- /**
464
- * The runtime GType of an introspected registered type, as an opaque node-gi
465
- * GType handle (a tag-distinct External carrying the GType — NOT a number/pointer).
466
- * The L1 layer surfaces it as a lazy `Ns.Type.$gtype` getter and feeds it to
467
- * GType-typed GI arguments (`GObject.type_ensure`, `g_param_spec_object`'s value
468
- * type, …). Returns `null` for an unknown/unregistered name.
469
- * @param {string} namespace e.g. "Adw"
470
- * @param {string} name e.g. "Clamp"
471
- * @returns {unknown} opaque GType handle, or null
472
- */
473
219
  export const getGType = native.getGType;
474
-
475
- /**
476
- * Whether a GObject handle's GType is-a `namespace.typeName` (g_type_is_a — also
477
- * true when the type implements an interface). Used by the L1 wrapper to pick the
478
- * right `Gio._promisify` registration when two classes promisify a method of the
479
- * same name.
480
- * @param {unknown} handle
481
- * @param {string} namespace
482
- * @param {string} typeName
483
- * @returns {boolean}
484
- */
485
220
  export const isInstanceOf = native.isInstanceOf;
486
-
487
- /**
488
- * Whether `value` is one of node-gi's GObject-instance handles (tag-checked, no
489
- * dereference). Lets the L1 wrapper wrap object-typed return values for chaining
490
- * without misclassifying a registerClass type handle.
491
- * @param {unknown} value
492
- * @returns {boolean}
493
- */
494
221
  export const isGObjectHandle = native.isGObjectHandle;
495
-
496
- /**
497
- * Allocate a fresh, zero-initialised GValue and return it as a node-gi boxed
498
- * handle (GType G_TYPE_VALUE, owned → freed on GC). GObject.Value has no
499
- * `g_value_new()`, so the L1 layer (gi.js `makeValueClass`) uses this to build a
500
- * `new GObject.Value()` and then drives `.init(gtype)` / `.set_*` / `.get_*` /
501
- * `.copy` / `.unset` through the boxed-method path.
502
- * @returns {unknown} opaque GObject.Value boxed handle
503
- */
504
222
  export const newGValue = native.newGValue;
505
-
506
- /**
507
- * Invoke an instance method on a boxed/struct handle (e.g. `mainLoop.run()` /
508
- * `mainLoop.quit()`). The method is resolved against the boxed GType's
509
- * introspection info and invoked with the boxed pointer as the instance.
510
- * @param {unknown} handle a boxed handle (e.g. from `GLib.MainLoop.new(...)`)
511
- * @param {string} methodName GI method name, e.g. "run"
512
- * @param {unknown[]} [args]
513
- * @returns {unknown}
514
- */
515
223
  export const callBoxedMethod = native.callBoxedMethod;
516
-
517
- /**
518
- * Whether `value` is one of node-gi's boxed/struct handles (tag-checked, no
519
- * dereference). The L1 wrapper uses it to wrap boxed return values (GMainLoop,
520
- * …) with a method-routing proxy.
521
- * @param {unknown} value
522
- * @returns {boolean}
523
- */
524
224
  export const isBoxedHandle = native.isBoxedHandle;
525
-
526
- /**
527
- * Classify a name on a boxed/struct/union handle: 0 (neither) | 1 (method) |
528
- * 2 (field). Methods take priority (gjs renames a colliding field to `_name`), so
529
- * the L1 wrapBoxed uses this to route method-dispatch vs field get/set.
530
- * @param {unknown} handle a node-gi boxed handle
531
- * @param {string} name GI member name (snake_case)
532
- * @returns {number}
533
- */
534
225
  export const boxedMemberKind = native.boxedMemberKind;
535
-
536
- /**
537
- * Read a named field of a boxed/struct/union handle (`simpleStruct.long_`).
538
- * Throws if `name` is not a field, or the field is unreadable/unsupported.
539
- * @param {unknown} handle a node-gi boxed handle
540
- * @param {string} name GI field name (snake_case)
541
- * @returns {unknown}
542
- */
543
226
  export const getBoxedField = native.getBoxedField;
544
-
545
- /**
546
- * Write a simple-typed field of a boxed/struct/union handle (`union.long_ = 5`).
547
- * Throws if `name` is not a field, or the field is unwritable/unsupported.
548
- * @param {unknown} handle a node-gi boxed handle
549
- * @param {string} name GI field name (snake_case)
550
- * @param {unknown} value
551
- * @returns {void}
552
- */
553
227
  export const setBoxedField = native.setBoxedField;
554
-
555
- /**
556
- * The boxed handle's GType name (e.g. "GBytes"), or null when it carries no
557
- * registered GType. Lets the L1 layer attach type-specific conveniences (the
558
- * GLib.Bytes `toArray` shim) without a per-type wrapper.
559
- * @param {unknown} value
560
- * @returns {string | null}
561
- */
562
228
  export const boxedTypeName = native.boxedTypeName;
563
-
564
- /**
565
- * Whether `value` is a node-gi GParamSpec handle (tag-checked, no dereference).
566
- * The L1 wrapper uses it to give a notify handler's pspec / a GParamSpec-typed
567
- * value the GObject.ParamSpec ergonomics.
568
- * @param {unknown} value
569
- * @returns {boolean}
570
- */
571
229
  export const isParamSpecHandle = native.isParamSpecHandle;
572
230
 
573
- /**
574
- * True when `value` is a node-gi non-GObject GObject-fundamental handle (e.g. a
575
- * GskRenderNode from Gtk.Snapshot.to_node, a GdkEvent) — introspected as object
576
- * info but ref-counted via its own ref/unref funcs (NOT g_object_ref). The L1
577
- * wrapper surfaces it as an opaque, round-trippable pass-through handle.
578
- * @param {unknown} value
579
- * @returns {boolean}
580
- */
231
+ // True for a non-GObject GObject-fundamental handle (a GskRenderNode from
232
+ // Gtk.Snapshot.to_node, a GdkEvent): introspected as object info, but ref-counted
233
+ // through its own ref/unref funcs, NOT g_object_ref. L1 surfaces it as an opaque,
234
+ // round-trippable pass-through handle.
581
235
  export const isFundamentalHandle = native.isFundamentalHandle;
582
236
 
583
- /**
584
- * Read a GParamSpec accessor by name: name | nick | blurb | flags | valueType |
585
- * ownerType | defaultValue. Backs the L1 GObject.ParamSpec getters + methods.
586
- * @param {unknown} handle a node-gi GParamSpec handle
587
- * @param {string} which the accessor name
588
- * @returns {unknown}
589
- */
590
237
  export const paramSpecProp = native.paramSpecProp;
591
-
592
- /**
593
- * Build a GVariant from a GVariant type signature + JS value, returning an owned
594
- * boxed GLib.Variant handle (the floating ref is sunk; released on GC). The
595
- * recursive native packer behind `new GLib.Variant(signature, value)`. Supports
596
- * the basics `b y n q i u x t h d s o g`, `v`, `m*` maybe, `a*` arrays (incl.
597
- * `as`, `ay`, `a{..}` dicts), `(...)` tuples and `{kv}` dict-entries.
598
- * @param {string} signature a GVariant type string, e.g. "a{sv}", "(si)", "s"
599
- * @param {unknown} [value]
600
- * @returns {unknown} boxed GLib.Variant handle
601
- */
602
238
  export const variantNew = native.variantNew;
603
-
604
- /**
605
- * Unpack a GLib.Variant handle to a JS value. `deep` unpacks container children
606
- * (one level for nested `v` variants, which stay Variants unless `recursive`);
607
- * `recursive` additionally unwraps nested `v` variants to plain JS. Drives the
608
- * L1 `.unpack()` (deep=false), `.deepUnpack()` (deep=true) and
609
- * `.recursiveUnpack()` (deep=true, recursive=true).
610
- * @param {unknown} handle a handle from {@link variantNew} (or a returned Variant)
611
- * @param {boolean} [deep]
612
- * @param {boolean} [recursive]
613
- * @returns {unknown}
614
- */
615
239
  export const variantUnpack = native.variantUnpack;
616
-
617
- /**
618
- * The GVariant type string of a GLib.Variant handle (e.g. "a{sv}").
619
- * @param {unknown} handle
620
- * @returns {string}
621
- */
622
240
  export const variantGetTypeString = native.variantGetTypeString;
623
-
624
- /**
625
- * Whether `value` is one of node-gi's GLib.Variant handles (a boxed handle
626
- * tagged with the GVariant GType; tag-checked, no dereference). The L1 wrapper
627
- * uses it to give returned/unpacked variants the GLib.Variant ergonomics.
628
- * @param {unknown} value
629
- * @returns {boolean}
630
- */
631
241
  export const isVariantHandle = native.isVariantHandle;
632
-
633
- /**
634
- * Attach the libuv-backed GSource to the default GLib main context so a blocking
635
- * GLib main loop (`GLib.MainLoop.run()`, `Gio.Application.run()`) keeps Node's
636
- * timers, promises and I/O alive — the Node twin of GJS running the GLib loop as
637
- * the process loop. Also arms the uv-driven GLib AUTO-PUMP for the non-blocking
638
- * case: pending GLib sources (Gio async completions, GLib timeouts/idles, DBus)
639
- * dispatch from Node's own libuv loop, so a plain `node bundle.mjs` that awaits a
640
- * Gio async op needs NO explicit mainloop (matching GJS, where the GLib loop is
641
- * the process loop). An in-flight scope=async callback (GAsyncReadyCallback) and
642
- * an armed GLib timeout keep the process alive like Node's own pending I/O and
643
- * timers; a purely-sync program still exits immediately. Idempotent. The L1 layer
644
- * calls it once when a namespace is first required.
645
- * @returns {void}
646
- */
647
242
  export const startMainLoop = native.startMainLoop;
648
-
649
- /**
650
- * Iterate the default GLib main context once, dispatching any ready sources (GIO
651
- * async callbacks, GLib timeouts/idles, DBus). Pure GLib — no libuv — so it is the
652
- * portable main-loop primitive on Bun/Deno, where {@link startMainLoop} can't run.
653
- * The L1 layer drives it from a JS timer to co-pump GLib while the runtime's own
654
- * event loop stays in control. Returns true if a source was dispatched.
655
- * @param {boolean} [mayBlock] block until a source is ready (default false)
656
- * @returns {boolean}
657
- */
658
243
  export const iterateMainContext = native.iterateMainContext;
659
-
660
- /**
661
- * Whether the default GLib main context holds work a *pumping* runtime must stay
662
- * alive for: a scheduled source (an armed timeout/idle — the prepare/query
663
- * timeout hint is >= 0) or an in-flight scope=async GI callback. Pure GLib —
664
- * the companion of {@link iterateMainContext} and the query half of the
665
- * keep-alive contract the L1 `startMainContextPump` applies on Bun/Deno (Node
666
- * reads the same facts inline in the uv auto-pump). A `false` answer is what
667
- * lets a sync-only program exit immediately.
668
- * @returns {boolean}
669
- */
670
244
  export const mainContextHasPending = native.mainContextHasPending;
671
-
672
- /**
673
- * Create an `Int32Array(1)` view over the SAME JS-armed-work counter
674
- * {@link mainContextHasPending} reads — `view[0] > 0` ⇔
675
- * `mainContextHasPending()`. The zero-N-API spelling: reading the view is a
676
- * plain V8 memory access, no call into the addon. Load-bearing for the Bun/Deno
677
- * pump's IDLE beat — on Deno, merely ENTERING the addon from a timer tick
678
- * during the between-test-files GC window reproduces the #47 boxed-handle
679
- * teardown SIGSEGV (measured: a query-only tick crashed 3/3 where a tick that
680
- * never touches the addon exited 0), so the beat must be able to answer "is
681
- * there work?" without a single native call. A lazy FACTORY rather than an
682
- * Init-time export: the @gjsify/napi shim (the gjs-napi conformance oracle)
683
- * loud-stubs `napi_create_external_arraybuffer`, and the gjs host never arms
684
- * the portable pump — called only on the first Bun/Deno pump arm.
685
- * @returns {Int32Array}
686
- */
687
245
  export const makePumpPendingCount = native.makePumpPendingCount;
688
-
689
- /**
690
- * Drain ready GLib sources + re-arm the auto-pump's libuv wake-ups NOW (no-op
691
- * unless {@link startMainLoop} armed the pump, i.e. Node's main env). The L1
692
- * layer wires this to `process.on('beforeExit')`: with only unref'd handles
693
- * active, `uv_run` exits without ever running the pump's prepare/check phase, so
694
- * an otherwise-empty loop would never arm GLib's timer wake-up — beforeExit
695
- * fires exactly then, and the kick revives the loop while GLib still has
696
- * scheduled work. Rarely needed directly.
697
- * @returns {void}
698
- */
699
246
  export const pumpKick = native.pumpKick;
700
-
701
- /**
702
- * Register the runtime-native microtask drain the engine runs after each
703
- * OUTERMOST loop-dispatched GLib→JS callback returns (the cross-runtime
704
- * microtask checkpoint — see the registration block above). Registered
705
- * automatically at addon load on Bun/Deno; never call it on Node (its
706
- * napi_make_callback already checkpoints natively). Exposed for completeness.
707
- * @param {() => void} drain
708
- * @returns {void}
709
- */
710
247
  export const setMicrotaskDrain = native.setMicrotaskDrain;
711
-
712
- /**
713
- * Connect a JS callback to a GObject signal. Returns a handler id for
714
- * {@link disconnectSignal}. The callback receives the signal arguments
715
- * (the emitter instance is not passed in this milestone).
716
- * @param {unknown} handle a handle from {@link newObject}
717
- * @param {string} signalName
718
- * @param {(...args: unknown[]) => unknown} callback
719
- * @param {boolean} [after] connect in the "after" phase
720
- * @returns {number} handler id
721
- */
722
248
  export const connectSignal = native.connectSignal;
723
-
724
- /**
725
- * Emit a signal on a GObject with optional arguments; returns the signal's
726
- * return value (or undefined for void signals).
727
- * @param {unknown} handle
728
- * @param {string} signalName
729
- * @param {unknown[]} [args]
730
- * @returns {unknown}
731
- */
732
249
  export const emitSignal = native.emitSignal;
733
-
734
- /**
735
- * Disconnect a previously connected signal handler.
736
- * @param {unknown} handle
737
- * @param {number} handlerId from {@link connectSignal}
738
- * @returns {void}
739
- */
740
250
  export const disconnectSignal = native.disconnectSignal;
741
-
742
- /**
743
- * Register the L1 resolver that maps a Gtk.Template `<signal handler="…">` handler
744
- * name to the instance's bound JS method, for the engine's template-callback scope.
745
- * @param {(handle: unknown, handlerName: string) => ((...args: unknown[]) => unknown) | undefined} resolver
746
- * @returns {void}
747
- */
748
251
  export const setTemplateCallbackResolver = native.setTemplateCallbackResolver;
749
252
 
750
- /**
751
- * Register the L1 callback the engine's overridden `constructor` vfunc invokes to
752
- * run a registered class's JS constructor for a GObject it instantiated from C (a
753
- * GtkBuilder composite-template InternalChild). Given (instanceHandle, gtypeName)
754
- * it Reflect.constructs the class in adopt mode (see gi.js runCtorForCObject).
755
- * @param {(handle: unknown, gtypeName: string) => void} cb
756
- * @returns {void}
757
- */
253
+ // Registers the L1 callback the engine's overridden `constructor` vfunc invokes to run a
254
+ // registered class's JS constructor for a GObject that C instantiated (a GtkBuilder
255
+ // composite-template InternalChild): given (instanceHandle, gtypeName) it
256
+ // Reflect.constructs the class in adopt mode — see gi.js runCtorForCObject.
758
257
  export const setConstructCallback = native.setConstructCallback;
759
258
 
760
- /**
761
- * Install (or clear, with `null`) the structured-log writer function — the
762
- * engine's twin of GjsPrivate.log_set_writer_func backing GLib.log_set_writer_func.
763
- * The writer receives (logLevel: number, fields: Record<string, Uint8Array|null>)
764
- * and returns a GLib.LogWriterOutput. NOTE: GLib allows at most ONE
765
- * g_log_set_writer_func install per process (a second install aborts in GLib).
766
- */
767
259
  export const logSetWriterFunc = native.logSetWriterFunc;
768
-
769
- /**
770
- * Route structured logs back to the default writer (the engine's twin of
771
- * GjsPrivate.log_set_writer_default backing GLib.log_set_writer_default).
772
- */
773
260
  export const logSetWriterDefault = native.logSetWriterDefault;
774
-
775
- /**
776
- * g_object_bind_property_full with JS transform functions — the engine's twin of
777
- * GjsPrivate.g_object_bind_property_full backing
778
- * GObject.Object.prototype.bind_property_full.
779
- */
780
261
  export const bindPropertyFull = native.bindPropertyFull;
781
-
782
- /**
783
- * g_binding_group_bind_full with JS transform functions — the engine's twin of
784
- * GjsPrivate.g_binding_group_bind_full backing GObject.BindingGroup.bind_full.
785
- */
786
262
  export const bindingGroupBindFull = native.bindingGroupBindFull;
787
263
 
788
264
  export default native;