@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/gtk-runtime.js CHANGED
@@ -11,7 +11,7 @@ import { createRequire } from 'node:module';
11
11
  import { fileURLToPath } from 'node:url';
12
12
  import { dirname, join } from 'node:path';
13
13
  import { existsSync } from 'node:fs';
14
- import { pathCovers, splitSearchPath, systemGiLibraryDirs } from './system-gi.js';
14
+ import { composeDyldFallback, pathCovers, splitSearchPath, systemGiLibraryDirs } from './system-gi.js';
15
15
  import { nativeCandidates, prebuildAddonPath } from './native-paths.js';
16
16
 
17
17
  // NB: node:child_process is intentionally NOT imported at module top level. It is
@@ -309,9 +309,11 @@ export function maybeReexecForGtkRuntime() {
309
309
  const env = {
310
310
  ...process.env,
311
311
  [REEXEC_SENTINEL]: '1',
312
- // Our dirs first, then whatever the host asked for — falling back to
313
- // dyld's own default tail so setting the variable never REMOVES /usr/lib.
314
- DYLD_FALLBACK_LIBRARY_PATH: [...libDirs, ...(curDyldDirs.length > 0 ? curDyldDirs : ['/usr/lib'])].join(':'),
312
+ // Our dirs first, then whatever the host asked for, then dyld's own
313
+ // default list — setting the variable REPLACES that list, so carrying
314
+ // it is what keeps the re-exec'd child from searching LESS than the
315
+ // process that spawned it.
316
+ DYLD_FALLBACK_LIBRARY_PATH: composeDyldFallback(libDirs),
315
317
  };
316
318
  // Only a bundle relocates typelibs; a host GI stack's are already on GI's own
317
319
  // search path, so the system branch deliberately leaves GI_TYPELIB_PATH alone.
@@ -457,6 +459,57 @@ function gstScannerLeaf() {
457
459
  return process.platform === 'win32' ? 'gst-plugin-scanner.exe' : 'gst-plugin-scanner';
458
460
  }
459
461
 
462
+ let libraryPathActivated = false;
463
+
464
+ /**
465
+ * Tell GI where the shared libraries its typelibs name by BARE LEAF actually are
466
+ * — for whichever GTK the policy chose, and WITHOUT an environment variable.
467
+ *
468
+ * THE GAP THIS CLOSES. A typelib records its backer as `libgtk-4.1.dylib`, and on
469
+ * macOS neither a relocated bundle's `lib` dir nor Homebrew's `/usr/local/lib` is
470
+ * on dyld's search path. Node papers over it in {@link maybeReexecForGtkRuntime}
471
+ * by re-execing with `DYLD_FALLBACK_LIBRARY_PATH` set — a Node-shaped re-exec, so
472
+ * it returns early on bun and deno, which got NO loader repair and died at the
473
+ * first widget behind three errors that all point elsewhere (`GtkWidget is not
474
+ * registered…`, `Adw.Application has no property 'application-id'`, `Gtk.GLArea is
475
+ * not a subclassable GObject type`). Measured on the macOS 15.7.9 x86_64 VM.
476
+ *
477
+ * `gi_repository_prepend_library_path()` is GI's own answer, so nothing is
478
+ * captured at process launch: identical on node, bun and deno, no re-exec, and the
479
+ * environment is left alone. The env paths elsewhere in this module stay — they
480
+ * still cover a dylib pulled in by ANOTHER dylib's link closure, which never
481
+ * passes through GI.
482
+ *
483
+ * The dirs come from `gtkSource()`, not a second opinion: the bundle's `libDir`
484
+ * for `bundle`, `systemGiLibraryDirs()` for `system` — the pair the re-exec
485
+ * composes. Mixing them is the two-copies hazard #920 records. Idempotent, and
486
+ * never fatal: an addon predating the binding changes nothing.
487
+ * @param {{ prependLibraryPath?: (p: string) => void }} native the loaded addon
488
+ * @returns {string[]} the directories handed to GI (empty when there was nothing to add)
489
+ */
490
+ export function activateGiLibraryPath(native) {
491
+ if (libraryPathActivated) return [];
492
+ libraryPathActivated = true;
493
+ if (typeof native?.prependLibraryPath !== 'function') return [];
494
+
495
+ const source = gtkSource();
496
+ const dirs =
497
+ source === 'bundle' ? [resolveGtkRuntimeBundle()?.libDir] : source === 'system' ? systemGiLibraryDirs() : [];
498
+ const applied = [];
499
+ // LAST wins with a prepend, so walking in reverse leaves the array's own
500
+ // order intact in GI's search path.
501
+ for (const dir of dirs.filter(Boolean).reverse()) {
502
+ native.prependLibraryPath(dir);
503
+ applied.unshift(dir);
504
+ }
505
+ return applied;
506
+ }
507
+
508
+ /** TEST-ONLY: allow a spec to run the activation again. */
509
+ export function resetGiLibraryPathForTests() {
510
+ libraryPathActivated = false;
511
+ }
512
+
460
513
  let activated = null; // memoize: the activation result (idempotent)
461
514
 
462
515
  /**
@@ -502,9 +555,10 @@ export function activateBundledGtkRuntime(native) {
502
555
  } else {
503
556
  const existing = process.env.DYLD_FALLBACK_LIBRARY_PATH;
504
557
  if (!existing || !existing.split(':').includes(bundle.libDir)) {
505
- process.env.DYLD_FALLBACK_LIBRARY_PATH = existing
506
- ? `${bundle.libDir}:${existing}`
507
- : `${bundle.libDir}:/usr/lib`;
558
+ // Same composition as the re-exec above: the bundle's libdir first,
559
+ // then the inherited value, then dyld's default list — which this
560
+ // variable REPLACES rather than extends.
561
+ process.env.DYLD_FALLBACK_LIBRARY_PATH = composeDyldFallback([bundle.libDir]);
508
562
  }
509
563
  }
510
564
 
package/index.d.ts CHANGED
@@ -65,6 +65,8 @@ export function setErrorBuilder(
65
65
 
66
66
  /** Prepend a directory to the GIRepository typelib search path. */
67
67
  export function prependSearchPath(path: string): void;
68
+ /** Prepend a directory GI searches for the SHARED LIBRARY a typelib names. */
69
+ export function prependLibraryPath(path: string): void;
68
70
 
69
71
  /**
70
72
  * Invoke a namespace-level GObject-Introspection function (not an instance
@@ -519,6 +521,7 @@ declare const native: {
519
521
  getErrorDomain: typeof getErrorDomain;
520
522
  setErrorBuilder: typeof setErrorBuilder;
521
523
  prependSearchPath: typeof prependSearchPath;
524
+ prependLibraryPath: typeof prependLibraryPath;
522
525
  callFunction: typeof callFunction;
523
526
  callMethod: typeof callMethod;
524
527
  hasMethod: typeof hasMethod;