@gjsify/node-gi 0.44.0 → 0.46.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/README.md +31 -9
- package/gi.js +73 -2
- package/gtk-runtime.d.ts +4 -1
- package/gtk-runtime.js +81 -8
- package/index.d.ts +15 -1
- package/index.js +16 -0
- package/native-prebuilds.js +510 -0
- package/package.json +2 -1
- package/prebuilds/darwin-arm64/node_gi.node +0 -0
- package/prebuilds/darwin-x64/node_gi.node +0 -0
- package/prebuilds/linux-arm64/node_gi.node +0 -0
- package/prebuilds/linux-x64/node_gi.node +0 -0
- package/prebuilds/win32-x64/node_gi.node +0 -0
- package/src/addon.cc +1 -0
- package/src/calls.cc +102 -26
- package/src/class.cc +22 -9
- package/src/common.h +70 -20
- package/src/marshal.cc +561 -28
- package/src/object.cc +26 -0
- package/src/repo.cc +15 -4
- package/src/toggle.cc +208 -53
- package/system-gi.d.ts +4 -0
- package/system-gi.js +65 -11
package/README.md
CHANGED
|
@@ -35,9 +35,10 @@ vendored as-is — gjsify ships its own dual (GJS + Node) example/test infra.
|
|
|
35
35
|
|
|
36
36
|
- **Node.js ≥ 20, Bun ≥ 1.3, or Deno ≥ 2** — the engine is a Node-API addon, so
|
|
37
37
|
ONE binary serves all three (see [Runtimes](#runtimes-node--bun--deno)).
|
|
38
|
-
- **No C++ toolchain on
|
|
39
|
-
`prebuilds/<platform>-<arch>/node_gi.node` for
|
|
40
|
-
`
|
|
38
|
+
- **No C++ toolchain on a prebuilt target.** The published tarball ships
|
|
39
|
+
`prebuilds/<platform>-<arch>/node_gi.node` for every target
|
|
40
|
+
`package.json#gjsify.platforms` declares — `linux-x64`, `linux-arm64`,
|
|
41
|
+
`darwin-arm64`, `darwin-x64`, `win32-x64` — and the
|
|
41
42
|
`install` script (`scripts/install.mjs`) uses it instead of building: a source
|
|
42
43
|
build is the FALLBACK for a host with no prebuild, not the default. Force it
|
|
43
44
|
with `NODE_GI_BUILD_FROM_SOURCE=1` (or npm's `--build-from-source`); skip it
|
|
@@ -55,6 +56,14 @@ vendored as-is — gjsify ships its own dual (GJS + Node) example/test infra.
|
|
|
55
56
|
specific target, so each new bundle is found by the mechanism that found the
|
|
56
57
|
first. The loader contract and the per-platform measurements are
|
|
57
58
|
[docs/node-gi-platform-notes.md](../../../docs/node-gi-platform-notes.md).
|
|
59
|
+
- An application that carries **its own** GI library — a typelib plus the shared
|
|
60
|
+
library behind it, staged beside the app rather than installed — names those
|
|
61
|
+
directories in `GJSIFY_GI_LIBRARY_PATH` (`:`-separated; `;` on Windows;
|
|
62
|
+
absolute paths only). node-gi reads it in JS and hands the directories to
|
|
63
|
+
`gi_repository_prepend_library_path()`, so GI can open the bare leaf its own
|
|
64
|
+
typelib records. This is the only route that survives on macOS: dyld strips
|
|
65
|
+
every `DYLD_*` from a restricted process, so a signed `.app` cannot use one.
|
|
66
|
+
Entries take precedence over the GTK bundle / system GI libdirs.
|
|
58
67
|
|
|
59
68
|
## Install
|
|
60
69
|
|
|
@@ -76,12 +85,25 @@ npm install @gjsify/gtk-runtime-darwin-arm64 # macOS Apple silicon
|
|
|
76
85
|
npm install @gjsify/gtk-runtime-darwin-x64 # macOS Intel
|
|
77
86
|
```
|
|
78
87
|
|
|
79
|
-
All three declare `os`/`cpu`, so npm/yarn/pnpm skip them off-platform.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
88
|
+
All three declare `os`/`cpu`, so npm/yarn/pnpm skip them off-platform.
|
|
89
|
+
|
|
90
|
+
**This package will not declare one, and that is a decision, not a gap** —
|
|
91
|
+
[ADR 0023 § 1 + § 3](../../../docs/adr/0023-gtk-source-precedence.md). Whoever
|
|
92
|
+
SHIPS an application declares the bundle (for the showcases that is the `gjsify`
|
|
93
|
+
CLI, which adds `@gjsify/gtk-runtime-<target>` to the tree it assembles); node-gi
|
|
94
|
+
declares it in no dependency field at all. The reason is #910: a CI job compiled
|
|
95
|
+
the addon against Homebrew GTK and then ran it on a bundle it had never been
|
|
96
|
+
linked against, producing wrong method entries and a 29-minute timeout. #920
|
|
97
|
+
reverted the dependency and the prohibition was written down. Pairing a
|
|
98
|
+
from-source addon with a bundle is not a preference that came out wrong, it is
|
|
99
|
+
an ABI error, so the bundle cannot arrive by default.
|
|
100
|
+
|
|
101
|
+
What that costs is worth stating rather than discovering: on a Mac or a Windows
|
|
102
|
+
box with **no** system GTK and **no** bundle installed, `@gjsify/node-gi` does
|
|
103
|
+
not load at all — its addon's own link closure (`libgirepository-2.0`,
|
|
104
|
+
`libglib-2.0`, `libcairo`) is unresolvable, and the failure names only the
|
|
105
|
+
`.node` file (#1063). The addon's rpath lists exactly the two places it accepts:
|
|
106
|
+
a sibling `gtk-runtime-<target>/gtk/lib`, and the host's `/usr/local/lib`.
|
|
85
107
|
|
|
86
108
|
## Usage
|
|
87
109
|
|
package/gi.js
CHANGED
|
@@ -70,6 +70,26 @@ function unwrapArg(value) {
|
|
|
70
70
|
if (value !== null && typeof value === 'object' && value[HANDLE] !== undefined) {
|
|
71
71
|
return value[HANDLE];
|
|
72
72
|
}
|
|
73
|
+
// An ARRAY is a container whose ELEMENTS need the same unwrapping, and until this
|
|
74
|
+
// line they did not get it: only the top-level argument was unwrapped, so an array
|
|
75
|
+
// of GObjects reached the engine as an array of JS wrappers and
|
|
76
|
+
// `NodeGiToGIArgument`'s GI_TYPE_TAG_INTERFACE branch — which accepts nothing but
|
|
77
|
+
// an External carrying the handle — threw
|
|
78
|
+
// `expected a GObject handle as a container element`. That is EVERY GI method
|
|
79
|
+
// taking an array of objects, not one method: measured on
|
|
80
|
+
// `Gio.ListStore.splice(0, n, [a, b])`, where `append(a)` on the same store
|
|
81
|
+
// succeeded because one GObject as a top-level arg was always unwrapped.
|
|
82
|
+
//
|
|
83
|
+
// `Array.isArray`, not `typeof value === 'object'`: a Uint8Array is a
|
|
84
|
+
// GByteArray/guint8[] argument the engine reads directly, and mapping over it here
|
|
85
|
+
// would turn it into a plain Array and lose that.
|
|
86
|
+
//
|
|
87
|
+
// Recursive, so a nested array and a GObject inside one are handled by the same
|
|
88
|
+
// rule rather than by a second one that drifts — and this is now the same shape
|
|
89
|
+
// `wrapReturn` has carried all along for the OTHER direction. That asymmetry is
|
|
90
|
+
// why the gap stayed invisible: a container coming BACK had its elements wrapped,
|
|
91
|
+
// a container going IN did not, and nothing read both lines at once.
|
|
92
|
+
if (Array.isArray(value)) return value.map(unwrapArg);
|
|
73
93
|
if (typeof value === 'function') return wrapCallbackFn(value);
|
|
74
94
|
return value;
|
|
75
95
|
}
|
|
@@ -146,7 +166,12 @@ function buildGError(domainName, domainQuark, code, message) {
|
|
|
146
166
|
error.domainQuark = domainQuark;
|
|
147
167
|
return error;
|
|
148
168
|
}
|
|
149
|
-
|
|
169
|
+
// The CLASS is registered with the builder, because the engine needs it the other
|
|
170
|
+
// way round too: a `GError`-typed IN argument recognises a caught (or `new`-ed)
|
|
171
|
+
// GLib.Error by `instanceof` and rebuilds a real GError from its fields, so an
|
|
172
|
+
// application can hand back the error it is holding (`GLib.propagate_error`,
|
|
173
|
+
// `Gst.Message.new_error`). One call, so builder and class cannot drift apart.
|
|
174
|
+
native.setErrorBuilder(buildGError, GLibError);
|
|
150
175
|
|
|
151
176
|
// Stamps a makeClass prototype with its { namespace, typeName } so Gio._promisify can
|
|
152
177
|
// record WHICH introspected class a registration belongs to — matched against the
|
|
@@ -1388,6 +1413,28 @@ function defineLazyGType(ctor, namespace, typeName) {
|
|
|
1388
1413
|
});
|
|
1389
1414
|
}
|
|
1390
1415
|
|
|
1416
|
+
// The GType a static call's `this` names, or `null` when the receiver names none —
|
|
1417
|
+
// a primitive, a plain object, a raw (unregistered) subclass. `null` means "no
|
|
1418
|
+
// receiver", and the engine then runs the method on the type the name was read from,
|
|
1419
|
+
// which is the pre-#1438 behaviour for every non-class-struct call.
|
|
1420
|
+
//
|
|
1421
|
+
// A class answers its own `$gtype`. An INSTANCE has none — `$gtype` is a
|
|
1422
|
+
// constructor-level member here — but gjs answers the instance's RUNTIME class for
|
|
1423
|
+
// it, and the difference is observable: measured, `GObject.Object.list_properties`
|
|
1424
|
+
// borrowed onto an instance of a registerClass'd subclass reads the SUBCLASS's 6
|
|
1425
|
+
// pspecs under gjs and read GObject's 0 here. So resolve it through the type system,
|
|
1426
|
+
// the route `Symbol.hasInstance` above already takes.
|
|
1427
|
+
function gtypeOf(receiver) {
|
|
1428
|
+
if (receiver === null || receiver === undefined) return null;
|
|
1429
|
+
if (typeof receiver !== 'function' && typeof receiver !== 'object') return null;
|
|
1430
|
+
const gt = receiver.$gtype;
|
|
1431
|
+
if (gt !== undefined) return gt;
|
|
1432
|
+
const handle = receiver[HANDLE];
|
|
1433
|
+
if (handle === undefined || !native.isGObjectHandle(handle)) return null;
|
|
1434
|
+
const G = requireGi('GObject', '2.0');
|
|
1435
|
+
return G.type_from_name(native.getTypeName(handle));
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1391
1438
|
// The GObject behind a prototype method's `this`. A prototype function is
|
|
1392
1439
|
// reachable without an instance (`Cls.prototype.m()`, a detached reference), so
|
|
1393
1440
|
// the engine must never be handed `undefined` for the instance argument.
|
|
@@ -1626,7 +1673,20 @@ function makeClass(namespace, typeName) {
|
|
|
1626
1673
|
return objectStaticConstructor(prop);
|
|
1627
1674
|
}
|
|
1628
1675
|
const giName = camelToSnake(prop);
|
|
1629
|
-
|
|
1676
|
+
// NOT an arrow: a class-struct method (GObjectClass's `list_properties`,
|
|
1677
|
+
// `find_property`, …) runs on the class the call went THROUGH, so `this` is
|
|
1678
|
+
// load-bearing and an arrow would discard it (#1438). What the engine does
|
|
1679
|
+
// with it — and why a plain static and an incompatible receiver ignore it —
|
|
1680
|
+
// is `CallStaticMethod`'s header in `src/calls.cc`.
|
|
1681
|
+
return function (...args) {
|
|
1682
|
+
// Short-circuit the direct `Ns.Class.method()` call: the engine would
|
|
1683
|
+
// compare the receiver against the same GType and ignore it anyway, and
|
|
1684
|
+
// the read would arm the lazy `$gtype` getter for nothing.
|
|
1685
|
+
const receiverGType = this === proxy ? null : gtypeOf(this);
|
|
1686
|
+
return wrapReturn(
|
|
1687
|
+
native.callStaticMethod(namespace, typeName, giName, unwrapArgs(args), receiverGType),
|
|
1688
|
+
);
|
|
1689
|
+
};
|
|
1630
1690
|
},
|
|
1631
1691
|
});
|
|
1632
1692
|
// Replacing ctor.prototype dropped the automatic back-link; restore it pointing at
|
|
@@ -1708,6 +1768,17 @@ function makeEnum(namespace, typeName) {
|
|
|
1708
1768
|
if (domain !== null) {
|
|
1709
1769
|
Object.defineProperty(out, ERROR_DOMAIN, { value: domain, enumerable: false });
|
|
1710
1770
|
}
|
|
1771
|
+
// `Ns.Enum.$gtype` — gjs answers a registered G_TYPE_ENUM/G_TYPE_FLAGS here, so
|
|
1772
|
+
// `GObject.type_name(Gio.BusType.$gtype)` is `GBusType` there and was `null` over
|
|
1773
|
+
// the bridge: an enum object carried no `$gtype` at all. EAGER rather than
|
|
1774
|
+
// defineLazyGType, because the object is frozen on the way out and a
|
|
1775
|
+
// self-redefining getter could never install its cache. Non-enumerable, so
|
|
1776
|
+
// `Object.keys` stays the member list; omitted entirely for an UNREGISTERED enum,
|
|
1777
|
+
// which has no GType and where gjs answers `undefined` too.
|
|
1778
|
+
const gtype = native.getGType(namespace, typeName);
|
|
1779
|
+
if (gtype !== null) {
|
|
1780
|
+
Object.defineProperty(out, '$gtype', { value: gtype, enumerable: false });
|
|
1781
|
+
}
|
|
1711
1782
|
return Object.freeze(out);
|
|
1712
1783
|
}
|
|
1713
1784
|
|
package/gtk-runtime.d.ts
CHANGED
|
@@ -19,7 +19,10 @@ export function activateBundledGtkRuntime(native: {
|
|
|
19
19
|
prependLibraryPath?: (p: string) => void;
|
|
20
20
|
}): GtkRuntimeBundle | null;
|
|
21
21
|
|
|
22
|
-
/**
|
|
22
|
+
/** Absolute directories from `GJSIFY_GI_LIBRARY_PATH` — where an app carries its OWN GI libraries. */
|
|
23
|
+
export function appGiLibraryDirs(opts?: { platform?: NodeJS.Platform | string; env?: NodeJS.ProcessEnv }): string[];
|
|
24
|
+
|
|
25
|
+
/** Prepend GI's shared-library search path: the app's own dirs, then the GTK the policy chose. */
|
|
23
26
|
export function activateGiLibraryPath(native: { prependLibraryPath?: (p: string) => void }): string[];
|
|
24
27
|
/** TEST-ONLY: allow the activation to run again. */
|
|
25
28
|
export function resetGiLibraryPathForTests(): void;
|
package/gtk-runtime.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// uses the host's GTK exactly as before).
|
|
10
10
|
import { createRequire } from 'node:module';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
|
-
import { dirname, join } from 'node:path';
|
|
12
|
+
import { dirname, join, posix, win32 } from 'node:path';
|
|
13
13
|
import { existsSync } from 'node:fs';
|
|
14
14
|
import { composeDyldFallback, pathCovers, splitSearchPath, systemGiLibraryDirs } from './system-gi.js';
|
|
15
15
|
import { nativeCandidates, prebuildAddonPath } from './native-paths.js';
|
|
@@ -460,6 +460,20 @@ export function maybeWireGtkWindowingEnv() {
|
|
|
460
460
|
const scanner = join(bundle.dir, 'libexec', 'gstreamer-1.0', gstScannerLeaf());
|
|
461
461
|
if (existsSync(scanner)) setIfUnset('GST_PLUGIN_SCANNER', scanner);
|
|
462
462
|
}
|
|
463
|
+
|
|
464
|
+
// The GIO modules, and specifically the TLS backend. `GTlsConnection` has no
|
|
465
|
+
// implementation in GIO itself — glib-networking ships one as a module GIO
|
|
466
|
+
// g_module_opens out of ONE directory, whose compiled-in default is the BUILD
|
|
467
|
+
// machine's prefix. On a user's box that path is absent (or, worse, a foreign
|
|
468
|
+
// glib-networking against a different GLib), so a bundle-activated process fell back
|
|
469
|
+
// to the dummy backend and every https request failed: `souphttpsrc` reports
|
|
470
|
+
// "Internal data stream error", @gjsify/{tls,http2,ws} fail the same way one layer up.
|
|
471
|
+
//
|
|
472
|
+
// GIO_MODULE_DIR, not GIO_EXTRA_MODULES, for GST_PLUGIN_SYSTEM_PATH's reason: it
|
|
473
|
+
// REPLACES the compiled-in default rather than adding to it, and mixing a host's
|
|
474
|
+
// modules into the bundle's own GIO is the case we are avoiding, not one to preserve.
|
|
475
|
+
const gioModules = join(bundle.dir, 'lib', 'gio', 'modules');
|
|
476
|
+
if (existsSync(gioModules)) setIfUnset('GIO_MODULE_DIR', gioModules);
|
|
463
477
|
}
|
|
464
478
|
|
|
465
479
|
/** The plugin scanner's leaf name — the bundles ship the same tree on both OSes. */
|
|
@@ -469,9 +483,49 @@ function gstScannerLeaf() {
|
|
|
469
483
|
|
|
470
484
|
let libraryPathActivated = false;
|
|
471
485
|
|
|
486
|
+
/**
|
|
487
|
+
* Where an APPLICATION carries the shared libraries ITS OWN typelibs name by bare
|
|
488
|
+
* leaf — `GJSIFY_GI_LIBRARY_PATH`, `:`-separated (`;` on win32).
|
|
489
|
+
*
|
|
490
|
+
* THE GAP THIS CLOSES is a different one from the note below. An app shipping a GI
|
|
491
|
+
* library of its own (`gjsify.ship.bundledTypelibs`) stages the typelib and its
|
|
492
|
+
* backer side by side and points `GI_TYPELIB_PATH` there, so GI finds the typelib
|
|
493
|
+
* and then `g_module_open`s a bare leaf nothing has located. The launcher's
|
|
494
|
+
* `LD_LIBRARY_PATH` covers that on linux; on macOS no environment variable can,
|
|
495
|
+
* because dyld strips every `DYLD_*` from a restricted process and a signed,
|
|
496
|
+
* hardened `.app` IS restricted — so a launcher depending on one works unsigned and
|
|
497
|
+
* breaks at signing time. The dirs below do not cover it either: they answer "where
|
|
498
|
+
* is the GTK stack", never "where are this application's own libraries". Neither
|
|
499
|
+
* does `dirname(GI_TYPELIB_PATH entry)`: {@link systemGiLibraryDirs} derives a
|
|
500
|
+
* libdir that way because GI's INSTALL layout puts typelibs in
|
|
501
|
+
* `<libdir>/girepository-1.0/`, and an app stages the pair FLAT, so that derivation
|
|
502
|
+
* yields the directory's PARENT — a real path holding nothing.
|
|
503
|
+
*
|
|
504
|
+
* Only ABSOLUTE entries are kept: a relative one resolves against the working
|
|
505
|
+
* directory, which for a double-clicked `.app` is `/`. PURE in its inputs, so the
|
|
506
|
+
* win32 dialect is executable from any host.
|
|
507
|
+
* @param {object} [opts]
|
|
508
|
+
* @param {NodeJS.Platform | string} [opts.platform]
|
|
509
|
+
* @param {NodeJS.ProcessEnv} [opts.env]
|
|
510
|
+
* @returns {string[]} absolute directories, in the order the variable listed them
|
|
511
|
+
*/
|
|
512
|
+
export function appGiLibraryDirs({ platform = process.platform, env = process.env } = {}) {
|
|
513
|
+
const isAbsolute = platform === 'win32' ? win32.isAbsolute : posix.isAbsolute;
|
|
514
|
+
const out = [];
|
|
515
|
+
for (const dir of splitSearchPath(env.GJSIFY_GI_LIBRARY_PATH, platform === 'win32' ? ';' : ':')) {
|
|
516
|
+
if (isAbsolute(dir) && !out.includes(dir)) out.push(dir);
|
|
517
|
+
}
|
|
518
|
+
return out;
|
|
519
|
+
}
|
|
520
|
+
|
|
472
521
|
/**
|
|
473
522
|
* Tell GI where the shared libraries its typelibs name by BARE LEAF actually are
|
|
474
|
-
* — for whichever GTK the policy chose
|
|
523
|
+
* — for whichever GTK the policy chose plus whatever the app carries itself, and
|
|
524
|
+
* WITHOUT a loader environment variable.
|
|
525
|
+
*
|
|
526
|
+
* `GJSIFY_GI_LIBRARY_PATH` is not a counter-example: it is read by THIS process, in
|
|
527
|
+
* JS, and handed to GI through the binding. dyld never sees it — which is the whole
|
|
528
|
+
* point, because dyld is what refuses to see `DYLD_*` in a signed, hardened app.
|
|
475
529
|
*
|
|
476
530
|
* THE GAP THIS CLOSES. A typelib records its backer as `libgtk-4.1.dylib`, and on
|
|
477
531
|
* macOS neither a relocated bundle's `lib` dir nor Homebrew's `/usr/local/lib` is
|
|
@@ -488,10 +542,23 @@ let libraryPathActivated = false;
|
|
|
488
542
|
* still cover a dylib pulled in by ANOTHER dylib's link closure, which never
|
|
489
543
|
* passes through GI.
|
|
490
544
|
*
|
|
491
|
-
* The dirs come from `gtkSource()`, not a second opinion: the bundle's
|
|
492
|
-
* for `bundle`, `systemGiLibraryDirs()` for `system` — the pair the
|
|
493
|
-
* composes. Mixing them is the two-copies hazard #920 records.
|
|
494
|
-
*
|
|
545
|
+
* The GTK dirs come from `gtkSource()`, not a second opinion: the bundle's
|
|
546
|
+
* `libDir` for `bundle`, `systemGiLibraryDirs()` for `system` — the pair the
|
|
547
|
+
* re-exec composes. Mixing them is the two-copies hazard #920 records. The
|
|
548
|
+
* application's own dirs ({@link appGiLibraryDirs}) are added on TOP of whichever
|
|
549
|
+
* of those the policy picked, and independently of it: an app carrying its own GI
|
|
550
|
+
* library needs it found whether the GTK behind it is a bundle, the host's, or —
|
|
551
|
+
* `source === 'none'` — neither.
|
|
552
|
+
*
|
|
553
|
+
* ORDER: the app's entries come FIRST. That only decides an OVERLAP — a leaf both
|
|
554
|
+
* places hold — since a name only one of them has is found either way. An
|
|
555
|
+
* explicitly-set variable is consent and outranks an inferred location (the
|
|
556
|
+
* precedence `GJSIFY_GTK_RUNTIME` already has over the probed bundle candidates),
|
|
557
|
+
* and on an overlap the app's copy is the one its OWN typelib was compiled against:
|
|
558
|
+
* the pair ships as a unit, and splitting it is not a missing library but an ABI
|
|
559
|
+
* mismatch — #910's failure shape, the expensive one.
|
|
560
|
+
*
|
|
561
|
+
* Idempotent, and never fatal: an addon predating the binding changes nothing.
|
|
495
562
|
* @param {{ prependLibraryPath?: (p: string) => void }} native the loaded addon
|
|
496
563
|
* @returns {string[]} the directories handed to GI (empty when there was nothing to add)
|
|
497
564
|
*/
|
|
@@ -501,12 +568,18 @@ export function activateGiLibraryPath(native) {
|
|
|
501
568
|
if (typeof native?.prependLibraryPath !== 'function') return [];
|
|
502
569
|
|
|
503
570
|
const source = gtkSource();
|
|
504
|
-
const
|
|
571
|
+
const gtkDirs =
|
|
505
572
|
source === 'bundle' ? [resolveGtkRuntimeBundle()?.libDir] : source === 'system' ? systemGiLibraryDirs() : [];
|
|
573
|
+
const dirs = [];
|
|
574
|
+
// Deduplicated: an app that names a directory the policy already found would
|
|
575
|
+
// otherwise grow GI's search path by a second, identical entry.
|
|
576
|
+
for (const dir of [...appGiLibraryDirs(), ...gtkDirs.filter(Boolean)]) {
|
|
577
|
+
if (!dirs.includes(dir)) dirs.push(dir);
|
|
578
|
+
}
|
|
506
579
|
const applied = [];
|
|
507
580
|
// LAST wins with a prepend, so walking in reverse leaves the array's own
|
|
508
581
|
// order intact in GI's search path.
|
|
509
|
-
for (const dir of dirs.
|
|
582
|
+
for (const dir of dirs.reverse()) {
|
|
510
583
|
native.prependLibraryPath(dir);
|
|
511
584
|
applied.unshift(dir);
|
|
512
585
|
}
|
package/index.d.ts
CHANGED
|
@@ -57,10 +57,15 @@ export function getErrorDomain(namespace: string, name: string): { name: string;
|
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
59
|
* Register the L1 GLib.Error factory the engine calls when a GI invoke fails, so
|
|
60
|
-
* a failed sync call throws a real `GLib.Error` (instanceof, with `.matches()`)
|
|
60
|
+
* a failed sync call throws a real `GLib.Error` (instanceof, with `.matches()`),
|
|
61
|
+
* together with the CLASS it builds — which a `GError`-typed IN argument uses to
|
|
62
|
+
* recognise such an error coming back and rebuild a real `GError` from its
|
|
63
|
+
* fields. Without the class that direction accepts only a marshalled (boxed)
|
|
64
|
+
* error, so a caught one cannot be handed on.
|
|
61
65
|
*/
|
|
62
66
|
export function setErrorBuilder(
|
|
63
67
|
builder: (domainName: string, domainQuark: number, code: number, message: string) => Error,
|
|
68
|
+
errorClass?: new (domain: unknown, code: number, message: string) => Error,
|
|
64
69
|
): void;
|
|
65
70
|
|
|
66
71
|
/** Prepend a directory to the GIRepository typelib search path. */
|
|
@@ -387,6 +392,14 @@ export function setBoxedField(handle: BoxedHandle, name: string, value: unknown)
|
|
|
387
392
|
* registered GType. Lets L1 attach type-specific conveniences (GLib.Bytes.toArray).
|
|
388
393
|
*/
|
|
389
394
|
export function boxedTypeName(value: unknown): string | null;
|
|
395
|
+
/**
|
|
396
|
+
* TEST-ONLY: the address a boxed handle wraps, as a hex string (`null` for anything
|
|
397
|
+
* else). Lets a test assert an IN-transfer ownership rule — "a transfer-full boxed
|
|
398
|
+
* IN arg is handed a COPY" — directly, instead of running the call many times and
|
|
399
|
+
* waiting for a double free that only aborts when the allocator has not recycled
|
|
400
|
+
* the block. Nothing on the runtime path may branch on it.
|
|
401
|
+
*/
|
|
402
|
+
export function __boxedAddress(value: unknown): string | null;
|
|
390
403
|
|
|
391
404
|
/**
|
|
392
405
|
* Opaque handle to a GObject.ParamSpec (a GObject fundamental, ref-counted via
|
|
@@ -604,6 +617,7 @@ declare const native: {
|
|
|
604
617
|
getBoxedField: typeof getBoxedField;
|
|
605
618
|
setBoxedField: typeof setBoxedField;
|
|
606
619
|
boxedTypeName: typeof boxedTypeName;
|
|
620
|
+
__boxedAddress: typeof __boxedAddress;
|
|
607
621
|
isParamSpecHandle: typeof isParamSpecHandle;
|
|
608
622
|
paramSpecProp: typeof paramSpecProp;
|
|
609
623
|
variantNew: typeof variantNew;
|
package/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
maybeReexecForGtkRuntime,
|
|
17
17
|
maybeWireGtkWindowingEnv,
|
|
18
18
|
} from './gtk-runtime.js';
|
|
19
|
+
import { activateNativePrebuilds } from './native-prebuilds.js';
|
|
19
20
|
|
|
20
21
|
// macOS bundled GTK: dyld reads DYLD_FALLBACK_LIBRARY_PATH only at launch, and GI
|
|
21
22
|
// needs it to g_module_open typelib backers (get_type / Pango / Gdk / Graphene) by
|
|
@@ -124,6 +125,18 @@ try {
|
|
|
124
125
|
// Never fatal: a missing/partial bundle just leaves the host GTK in charge.
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
// Every OTHER staged prebuild typelib (@gjsify/webkit-native and its kin): same
|
|
129
|
+
// env-free treatment, so a namespace whose typelib is installed in node_modules
|
|
130
|
+
// resolves under a plain `node app.mjs` and not only under `gjsify run`. Deliberately
|
|
131
|
+
// AFTER the GTK activation — that one owns the bundle-vs-host policy (ADR 0023) and
|
|
132
|
+
// this one skips gtk-runtime-* for exactly that reason.
|
|
133
|
+
try {
|
|
134
|
+
activateNativePrebuilds(native);
|
|
135
|
+
} catch {
|
|
136
|
+
// Same contract as the calls around it: a failed discovery leaves the search
|
|
137
|
+
// path untouched, and the caller gets GI's own "typelib not found".
|
|
138
|
+
}
|
|
139
|
+
|
|
127
140
|
// And where the LIBRARIES those typelibs name actually live. Separate from the
|
|
128
141
|
// call above because it is not about a bundle: it applies to whichever GTK the
|
|
129
142
|
// policy chose, and it is the only loader repair bun and deno get on macOS —
|
|
@@ -231,6 +244,9 @@ export const boxedMemberKind = native.boxedMemberKind;
|
|
|
231
244
|
export const getBoxedField = native.getBoxedField;
|
|
232
245
|
export const setBoxedField = native.setBoxedField;
|
|
233
246
|
export const boxedTypeName = native.boxedTypeName;
|
|
247
|
+
/** TEST-ONLY: the address a boxed handle wraps, so an ownership rule about pointer
|
|
248
|
+
* IDENTITY can be asserted instead of waited on (see marshal.cc BoxedAddress). */
|
|
249
|
+
export const __boxedAddress = native.__boxedAddress;
|
|
234
250
|
export const isParamSpecHandle = native.isParamSpecHandle;
|
|
235
251
|
|
|
236
252
|
// True for a non-GObject GObject-fundamental handle (a GskRenderNode from
|