@gjsify/node-gi 0.21.0 → 0.23.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 +10 -0
- package/gi.js +5 -2
- package/globals.d.ts +13 -0
- package/globals.js +6 -0
- package/gtk-runtime.js +12 -4
- package/index.js +50 -12
- package/overrides/byte-array.js +197 -0
- package/package.json +8 -1
- package/prebuilds/linux-arm64/node_gi.node +0 -0
- package/prebuilds/linux-x64/node_gi.node +0 -0
package/README.md
CHANGED
|
@@ -678,6 +678,16 @@ imports.signals.addSignalMethods(emitter); // the pure-JS Signals mixin
|
|
|
678
678
|
emitter.connect('ready', () => imports.mainloop.quit());
|
|
679
679
|
imports.mainloop.timeout_add(50, () => { emitter.emit('ready'); return false; });
|
|
680
680
|
imports.mainloop.run(); // thin GLib.MainLoop wrapper
|
|
681
|
+
|
|
682
|
+
// imports.byteArray — the legacy byte-array module, with GJS semantics
|
|
683
|
+
// (fromString/toString are zero-terminated + fatal-decode; fromGBytes/toGBytes
|
|
684
|
+
// round-trip GLib.Bytes; fromArray wraps in the legacy ByteArray class). This
|
|
685
|
+
// is the seam @gjsify/utils' cli()/gbytesToUint8Array — and through them
|
|
686
|
+
// @gjsify/os + @gjsify/child_process — read GLib subprocess output with:
|
|
687
|
+
const [ok, out] = imports.gi.GLib.spawn_command_line_sync('echo hi');
|
|
688
|
+
console.log(imports.byteArray.toString(out)); // 'hi\n'
|
|
689
|
+
const bytes = imports.byteArray.toGBytes(Uint8Array.of(1, 2, 3));
|
|
690
|
+
imports.byteArray.fromGBytes(bytes); // Uint8Array [1, 2, 3] (a copy)
|
|
681
691
|
```
|
|
682
692
|
|
|
683
693
|
A follow-up `--app node` build step will inject this automatically for any
|
package/gi.js
CHANGED
|
@@ -985,7 +985,10 @@ let pumpRefCount = 0;
|
|
|
985
985
|
* @returns {() => void}
|
|
986
986
|
*/
|
|
987
987
|
export function startMainContextPump() {
|
|
988
|
-
|
|
988
|
+
// Node: the libuv↔GLib bridge already co-pumps. GJS: the host loop IS
|
|
989
|
+
// GLib's default main context — a JS-timer pump would only iterate the
|
|
990
|
+
// context recursively from within its own dispatch.
|
|
991
|
+
if (native.isNodeRuntime || native.RUNTIME === 'gjs') return () => {};
|
|
989
992
|
pumpRefCount++;
|
|
990
993
|
if (pumpTimer === null) {
|
|
991
994
|
// ~4 ms cadence: low latency without busy-spinning. Each tick drains every
|
|
@@ -1011,7 +1014,7 @@ export function startMainContextPump() {
|
|
|
1011
1014
|
|
|
1012
1015
|
/** Decrement the main-context pump reference count; clears the timer at zero. */
|
|
1013
1016
|
export function stopMainContextPump() {
|
|
1014
|
-
if (native.isNodeRuntime || pumpRefCount === 0) return;
|
|
1017
|
+
if (native.isNodeRuntime || native.RUNTIME === 'gjs' || pumpRefCount === 0) return;
|
|
1015
1018
|
pumpRefCount--;
|
|
1016
1019
|
if (pumpRefCount === 0 && pumpTimer !== null) {
|
|
1017
1020
|
clearInterval(pumpTimer);
|
package/globals.d.ts
CHANGED
|
@@ -15,6 +15,19 @@ export interface GjsImports {
|
|
|
15
15
|
system: SystemModule;
|
|
16
16
|
/** `imports.gettext` — the `@gjsify/node-gi/gettext` module (no-translation passthrough). */
|
|
17
17
|
gettext: GettextModule;
|
|
18
|
+
/** `imports.byteArray` — the legacy GJS byte-array module (GJS semantics: zero-terminated, fatal decode). */
|
|
19
|
+
byteArray: {
|
|
20
|
+
ByteArray: new (arg?: number | Uint8Array) => {
|
|
21
|
+
length: number;
|
|
22
|
+
toString(encoding?: string): string;
|
|
23
|
+
toGBytes(): unknown;
|
|
24
|
+
};
|
|
25
|
+
fromArray(array: Iterable<number>): unknown;
|
|
26
|
+
fromGBytes(bytes: unknown): Uint8Array;
|
|
27
|
+
fromString(string: string, encoding?: string): Uint8Array;
|
|
28
|
+
toGBytes(array: Uint8Array): unknown;
|
|
29
|
+
toString(byteArray: Uint8Array, encoding?: string): string;
|
|
30
|
+
};
|
|
18
31
|
versions: Record<string, unknown>;
|
|
19
32
|
}
|
|
20
33
|
|
package/globals.js
CHANGED
|
@@ -22,6 +22,7 @@ import cairo from './cairo.js';
|
|
|
22
22
|
// Legacy `imports.signals` (the pure-JS Signals mixin) + `imports.mainloop` (the
|
|
23
23
|
// GLib.MainLoop convenience layer) — the GJS script modules many older sources use.
|
|
24
24
|
import * as signalsModule from './overrides/_signals.js';
|
|
25
|
+
import { createByteArray } from './overrides/byte-array.js';
|
|
25
26
|
import { createMainloop } from './overrides/mainloop.js';
|
|
26
27
|
import { createPackage } from './overrides/package.js';
|
|
27
28
|
|
|
@@ -109,6 +110,11 @@ function makeImports() {
|
|
|
109
110
|
system,
|
|
110
111
|
gettext,
|
|
111
112
|
cairo,
|
|
113
|
+
// `imports.byteArray` — the legacy GJS byte-array module
|
|
114
|
+
// (fromGBytes/toGBytes/fromString/toString/fromArray/ByteArray), the seam
|
|
115
|
+
// `@gjsify/utils`' gbytesToUint8Array/cli() read subprocess output through.
|
|
116
|
+
// GLib is only pulled in lazily (toGBytes), so seeding it here is free.
|
|
117
|
+
byteArray: createByteArray(requireGi),
|
|
112
118
|
signals: { addSignalMethods, _connect, _connectAfter, _disconnect, _emit, _signalHandlerIsConnected, _disconnectAll },
|
|
113
119
|
// GJS's `imports.searchPath` — `imports.package.init` unshifts the app's
|
|
114
120
|
// moduledir onto it. Inert on node-gi (ESM + gi://), but present + safe so
|
package/gtk-runtime.js
CHANGED
|
@@ -158,7 +158,12 @@ export function maybePrependGtkRuntimeDllPath() {
|
|
|
158
158
|
* @returns {void}
|
|
159
159
|
*/
|
|
160
160
|
export function maybeWireGtkWindowingEnv() {
|
|
161
|
-
|
|
161
|
+
// win32 AND darwin ship a batteries-included --windowing bundle whose runtime
|
|
162
|
+
// DATA (gschemas / icons / loaders / gtksource / fonts) is located by these env
|
|
163
|
+
// vars — GLib/GTK read them at init, not at launch (unlike dyld's DYLD_FALLBACK),
|
|
164
|
+
// so setting them in-process works on both. The only platform difference is the
|
|
165
|
+
// XDG_DATA_DIRS separator (';' on win32, ':' on unix/darwin).
|
|
166
|
+
if (process.platform !== 'win32' && process.platform !== 'darwin') return;
|
|
162
167
|
const bundle = resolveGtkRuntimeBundle();
|
|
163
168
|
if (!bundle) return; // strict no-op when no bundle is present
|
|
164
169
|
|
|
@@ -166,6 +171,7 @@ export function maybeWireGtkWindowingEnv() {
|
|
|
166
171
|
// gschemas.compiled = the windowing-data marker; absent → display-free bundle.
|
|
167
172
|
if (!existsSync(join(schemaDir, 'gschemas.compiled'))) return;
|
|
168
173
|
|
|
174
|
+
const pathSep = process.platform === 'win32' ? ';' : ':';
|
|
169
175
|
const setIfUnset = (name, value) => {
|
|
170
176
|
if (!process.env[name]) process.env[name] = value;
|
|
171
177
|
};
|
|
@@ -177,11 +183,13 @@ export function maybeWireGtkWindowingEnv() {
|
|
|
177
183
|
setIfUnset('GDK_PIXBUF_MODULE_FILE', loaderCache);
|
|
178
184
|
}
|
|
179
185
|
|
|
186
|
+
// XDG_DATA_DIRS → <bundle>/share so the icon themes (share/icons) AND GtkSource's
|
|
187
|
+
// language-specs/styles (share/gtksourceview-5) resolve.
|
|
180
188
|
const shareDir = join(bundle.dir, 'share');
|
|
181
|
-
if (existsSync(
|
|
189
|
+
if (existsSync(shareDir)) {
|
|
182
190
|
const cur = process.env.XDG_DATA_DIRS ?? '';
|
|
183
|
-
if (!cur.split(
|
|
184
|
-
process.env.XDG_DATA_DIRS = cur ? `${shareDir}
|
|
191
|
+
if (!cur.split(pathSep).filter(Boolean).includes(shareDir)) {
|
|
192
|
+
process.env.XDG_DATA_DIRS = cur ? `${shareDir}${pathSep}${cur}` : shareDir;
|
|
185
193
|
}
|
|
186
194
|
}
|
|
187
195
|
|
package/index.js
CHANGED
|
@@ -49,19 +49,25 @@ const require = createRequire(import.meta.url);
|
|
|
49
49
|
const here = dirname(fileURLToPath(import.meta.url)); // package root
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
* Which JS runtime we are on. The Node-API addon loads on all
|
|
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
|
|
53
55
|
* libuv-backed main-loop bridge (startMainLoop) is Node-only — Deno exports no
|
|
54
56
|
* libuv symbols and Bun panics on uv_backend_fd — so Bun/Deno use the portable
|
|
55
|
-
* GLib-iteration pump instead (see gi.js)
|
|
56
|
-
*
|
|
57
|
-
*
|
|
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.
|
|
61
|
+
* @type {'bun' | 'deno' | 'gjs' | 'node'}
|
|
58
62
|
*/
|
|
59
63
|
export const RUNTIME =
|
|
60
|
-
typeof globalThis.
|
|
61
|
-
? '
|
|
62
|
-
: typeof globalThis.
|
|
63
|
-
? '
|
|
64
|
-
: '
|
|
64
|
+
typeof globalThis.imports !== 'undefined' && typeof globalThis.print === 'function'
|
|
65
|
+
? 'gjs'
|
|
66
|
+
: typeof globalThis.Bun !== 'undefined'
|
|
67
|
+
? 'bun'
|
|
68
|
+
: typeof globalThis.Deno !== 'undefined'
|
|
69
|
+
? 'deno'
|
|
70
|
+
: 'node';
|
|
65
71
|
|
|
66
72
|
/** Whether we are on Node.js (the only runtime with the libuv main-loop bridge). */
|
|
67
73
|
export const isNodeRuntime = RUNTIME === 'node';
|
|
@@ -84,9 +90,37 @@ function nativeCandidates() {
|
|
|
84
90
|
return [prebuild, release, debug];
|
|
85
91
|
}
|
|
86
92
|
|
|
93
|
+
// GJS host mode: the addon is loaded through the @gjsify/napi shim, not
|
|
94
|
+
// `require()`. The loader native is `globalThis.__gjsifyNapiLoadAddon`,
|
|
95
|
+
// installed by `GjsifyNapi.init()`; if another consumer (the @gjsify/napi L1)
|
|
96
|
+
// already captured + deleted it, init() re-defines it (idempotent). The
|
|
97
|
+
// GjsifyNapi typelib must be resolvable (GI_TYPELIB_PATH/LD_LIBRARY_PATH →
|
|
98
|
+
// the shim prebuild).
|
|
99
|
+
function gjsLoadAddon(path) {
|
|
100
|
+
let load = globalThis.__gjsifyNapiLoadAddon;
|
|
101
|
+
if (typeof load !== 'function') {
|
|
102
|
+
const GjsifyNapi = globalThis.imports?.gi?.GjsifyNapi;
|
|
103
|
+
if (!GjsifyNapi || !GjsifyNapi.init()) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
'@gjsify/node-gi: GJS host mode needs the @gjsify/napi shim — the GjsifyNapi ' +
|
|
106
|
+
'typelib is not loadable (point GI_TYPELIB_PATH and LD_LIBRARY_PATH at its prebuild).',
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
load = globalThis.__gjsifyNapiLoadAddon;
|
|
110
|
+
if (typeof load !== 'function') {
|
|
111
|
+
throw new Error('@gjsify/node-gi: GjsifyNapi.init() did not install the addon loader.');
|
|
112
|
+
}
|
|
113
|
+
// Capture-then-delete, the same no-side-channel discipline as the L1.
|
|
114
|
+
delete globalThis.__gjsifyNapiLoadAddon;
|
|
115
|
+
}
|
|
116
|
+
return load(path);
|
|
117
|
+
}
|
|
118
|
+
|
|
87
119
|
function loadNative() {
|
|
88
120
|
for (const candidate of nativeCandidates()) {
|
|
89
|
-
if (existsSync(candidate))
|
|
121
|
+
if (existsSync(candidate)) {
|
|
122
|
+
return RUNTIME === 'gjs' ? gjsLoadAddon(candidate) : require(candidate);
|
|
123
|
+
}
|
|
90
124
|
}
|
|
91
125
|
throw new Error(
|
|
92
126
|
`@gjsify/node-gi: native addon not found for ${RUNTIME} on ${process.platform}-${process.arch}. ` +
|
|
@@ -94,7 +128,8 @@ function loadNative() {
|
|
|
94
128
|
'Run `node-gyp rebuild` in ' +
|
|
95
129
|
here +
|
|
96
130
|
' (requires a C++ toolchain and the girepository-2.0 / glib-2.0 development headers), ' +
|
|
97
|
-
'or install a package build that ships a prebuild for your platform.'
|
|
131
|
+
'or install a package build that ships a prebuild for your platform. ' +
|
|
132
|
+
'Under a bundled GJS app (import.meta anchors at the bundle), set NODE_GI_NATIVE to the addon path.',
|
|
98
133
|
);
|
|
99
134
|
}
|
|
100
135
|
|
|
@@ -133,7 +168,10 @@ try {
|
|
|
133
168
|
// matching Node's checkpoint and GJS (SpiderMonkey drains the promise-job queue
|
|
134
169
|
// when the last JS frame exits). Node never registers — its checkpoint already
|
|
135
170
|
// runs natively, so the Node path is byte-identical to before.
|
|
136
|
-
|
|
171
|
+
// GJS never registers: SpiderMonkey/GJS drains the promise-job queue from its
|
|
172
|
+
// own main-loop source whenever the last JS frame exits — the exact semantic
|
|
173
|
+
// this checkpoint recreates for Bun/Deno.
|
|
174
|
+
if (!isNodeRuntime && RUNTIME !== 'gjs') {
|
|
137
175
|
try {
|
|
138
176
|
let drain = null;
|
|
139
177
|
if (RUNTIME === 'bun') {
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
|
|
2
|
+
// SPDX-FileCopyrightText: 2017 Philip Chimento <philip.chimento@gmail.com>
|
|
3
|
+
//
|
|
4
|
+
// Adapted from GJS (refs/gjs/modules/script/byteArray.js + the native
|
|
5
|
+
// refs/gjs/gjs/byteArray.cpp `imports._byteArrayNative` functions). Copyright
|
|
6
|
+
// (c) 2017 Philip Chimento. MIT OR LGPL-2.0-or-later.
|
|
7
|
+
// Modifications: the legacy `imports.byteArray` module ported to
|
|
8
|
+
// @gjsify/node-gi. The native `fromGBytes`/`fromString`/`toString` are
|
|
9
|
+
// reimplemented in JS over the engine's boxed-GBytes surface and
|
|
10
|
+
// TextEncoder/TextDecoder, preserving GJS semantics:
|
|
11
|
+
// - toString/fromString are ZERO-TERMINATED (data truncates at the first NUL
|
|
12
|
+
// byte — gjs/text-encoding.cpp `zero_terminated_length`), and decoding is
|
|
13
|
+
// FATAL (invalid sequences throw a TypeError, like gjs's fatal=true).
|
|
14
|
+
// - fromGBytes/fromString return a COPY as a plain Uint8Array carrying the
|
|
15
|
+
// legacy own `toString(encoding)` method gjs tacks on
|
|
16
|
+
// (`define_legacy_tostring`), minus gjs's once-per-callsite deprecation
|
|
17
|
+
// warning (stderr-only noise a byte-golden never sees).
|
|
18
|
+
// - toGBytes routes through `new GLib.Bytes(array)` exactly as the gjs
|
|
19
|
+
// script module does; GLib is loaded lazily so building `imports` never
|
|
20
|
+
// pulls the namespace in.
|
|
21
|
+
|
|
22
|
+
// Truncate a byte array at the first NUL, per gjs's ZERO_TERMINATED handling
|
|
23
|
+
// (gjs/text-encoding.cpp zero_terminated_length).
|
|
24
|
+
function zeroTerminated(u8) {
|
|
25
|
+
const nul = u8.indexOf(0);
|
|
26
|
+
return nul === -1 ? u8 : u8.subarray(0, nul);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// gjs routes non-UTF-8 encodings through iconv; Node's TextDecoder (decode
|
|
30
|
+
// side) accepts WHATWG labels which cover the practical iconv set, so decoding
|
|
31
|
+
// just passes the label through. The ENCODE side has no general converter on
|
|
32
|
+
// Node — map the labels gjs consumers actually use onto Buffer encodings and
|
|
33
|
+
// throw a clear error for the rest.
|
|
34
|
+
const ENCODE_LABELS = new Map([
|
|
35
|
+
['latin1', 'latin1'],
|
|
36
|
+
['iso-8859-1', 'latin1'],
|
|
37
|
+
['iso8859-1', 'latin1'],
|
|
38
|
+
['ascii', 'ascii'],
|
|
39
|
+
['us-ascii', 'ascii'],
|
|
40
|
+
['utf-16', 'utf16le'],
|
|
41
|
+
['utf-16le', 'utf16le'],
|
|
42
|
+
['utf16le', 'utf16le'],
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
function isUtf8Label(encoding) {
|
|
46
|
+
const label = String(encoding).toLowerCase();
|
|
47
|
+
return label === 'utf-8' || label === 'utf8';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// The legacy own `toString(encoding)` gjs defines on arrays returned by
|
|
51
|
+
// fromString/fromGBytes/fromArray (byteArray.cpp define_legacy_tostring).
|
|
52
|
+
function defineLegacyToString(u8, toString) {
|
|
53
|
+
Object.defineProperty(u8, 'toString', {
|
|
54
|
+
value(encoding = 'utf-8') {
|
|
55
|
+
return toString(u8, encoding);
|
|
56
|
+
},
|
|
57
|
+
writable: true,
|
|
58
|
+
enumerable: false,
|
|
59
|
+
configurable: true,
|
|
60
|
+
});
|
|
61
|
+
return u8;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Build the legacy `imports.byteArray` module bound to the L1 backend.
|
|
66
|
+
* @param {(ns: string, version?: string) => Record<string, any>} requireGi
|
|
67
|
+
* @returns {{ ByteArray: any, fromArray: Function, fromGBytes: Function, fromString: Function, toGBytes: Function, toString: Function }}
|
|
68
|
+
*/
|
|
69
|
+
export function createByteArray(requireGi) {
|
|
70
|
+
/**
|
|
71
|
+
* @param {Uint8Array} byteArray bytes to decode
|
|
72
|
+
* @param {string} [encoding] a WHATWG/iconv label, default UTF-8
|
|
73
|
+
* @returns {string} the decoded string, stopping at the first NUL byte
|
|
74
|
+
*/
|
|
75
|
+
function toString(byteArray, encoding = 'utf-8') {
|
|
76
|
+
// Plain Error, not TypeError — gjs raises these argument failures via
|
|
77
|
+
// gjs_throw (Error); only the GBytes typecheck below is a TypeError.
|
|
78
|
+
if (!(byteArray instanceof Uint8Array)) throw new Error('Argument to decode() must be a Uint8Array');
|
|
79
|
+
const data = zeroTerminated(byteArray);
|
|
80
|
+
if (data.length === 0) return '';
|
|
81
|
+
// fatal:true mirrors gjs's fatal decode (invalid sequences throw).
|
|
82
|
+
return new TextDecoder(encoding, { fatal: true }).decode(data);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {string} string the string to encode
|
|
87
|
+
* @param {string} [encoding] a WHATWG/iconv label, default UTF-8
|
|
88
|
+
* @returns {Uint8Array} the encoded bytes, truncated at the first NUL byte
|
|
89
|
+
*/
|
|
90
|
+
function fromString(string, encoding = 'utf-8') {
|
|
91
|
+
if (typeof string !== 'string') throw new Error('Argument to fromString() must be a string');
|
|
92
|
+
let encoded;
|
|
93
|
+
if (isUtf8Label(encoding)) {
|
|
94
|
+
encoded = new TextEncoder().encode(string);
|
|
95
|
+
} else {
|
|
96
|
+
const bufferEncoding = ENCODE_LABELS.get(String(encoding).toLowerCase());
|
|
97
|
+
if (!bufferEncoding) throw new Error(`Unsupported encoding for fromString(): ${encoding}`);
|
|
98
|
+
const buf = Buffer.from(string, bufferEncoding);
|
|
99
|
+
encoded = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
100
|
+
}
|
|
101
|
+
const data = zeroTerminated(encoded);
|
|
102
|
+
// Copy into a fresh plain Uint8Array (gjs returns a standalone buffer, and
|
|
103
|
+
// Buffer.from may hand out a view into Node's shared slab).
|
|
104
|
+
const out = new Uint8Array(data.length);
|
|
105
|
+
out.set(data);
|
|
106
|
+
return defineLegacyToString(out, toString);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @param {any} bytes a GLib.Bytes boxed handle
|
|
111
|
+
* @returns {Uint8Array} a copy of the bytes as a plain Uint8Array
|
|
112
|
+
*/
|
|
113
|
+
function fromGBytes(bytes) {
|
|
114
|
+
// The engine's boxed GBytes proxy carries `toArray()` (gi.js wrapBoxed) —
|
|
115
|
+
// the same convenience gjs adds to GLib.Bytes.prototype. Its absence means
|
|
116
|
+
// the value is not a GBytes, matching gjs's G_TYPE_BYTES typecheck throw.
|
|
117
|
+
if (!bytes || typeof bytes.toArray !== 'function') {
|
|
118
|
+
throw new TypeError('Argument to fromGBytes() must be a GLib.Bytes');
|
|
119
|
+
}
|
|
120
|
+
const data = bytes.toArray();
|
|
121
|
+
// toArray() already returns a copy of the GBytes data; re-view it as a
|
|
122
|
+
// PLAIN Uint8Array (gjs returns Uint8Array, not Node's Buffer subclass).
|
|
123
|
+
const out = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
124
|
+
return defineLegacyToString(out, toString);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @param {Uint8Array} array the Uint8Array to convert to GLib.Bytes
|
|
129
|
+
* @returns {any} a GLib.Bytes boxed handle
|
|
130
|
+
*/
|
|
131
|
+
function toGBytes(array) {
|
|
132
|
+
if (!(array instanceof Uint8Array)) throw new Error('Argument to ByteArray.toGBytes() must be a Uint8Array');
|
|
133
|
+
const GLib = requireGi('GLib', '2.0');
|
|
134
|
+
return new GLib.Bytes(array);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// The legacy ByteArray wrapper class, ported verbatim from
|
|
138
|
+
// refs/gjs/modules/script/byteArray.js (backwards compatibility only).
|
|
139
|
+
const ByteArray = class ByteArray {
|
|
140
|
+
constructor(arg = 0) {
|
|
141
|
+
if (arg instanceof Uint8Array) this._array = arg;
|
|
142
|
+
else this._array = new Uint8Array(arg);
|
|
143
|
+
return new Proxy(this, ByteArray);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static get(target, prop, receiver) {
|
|
147
|
+
if (!Number.isNaN(Number.parseInt(prop))) return Reflect.get(target._array, prop);
|
|
148
|
+
return Reflect.get(target, prop, receiver);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
static set(target, prop, val, receiver) {
|
|
152
|
+
const ix = Number.parseInt(prop);
|
|
153
|
+
if (!Number.isNaN(ix)) {
|
|
154
|
+
if (ix >= target._array.length) {
|
|
155
|
+
const newArray = new Uint8Array(ix + 1);
|
|
156
|
+
newArray.set(target._array);
|
|
157
|
+
target._array = newArray;
|
|
158
|
+
}
|
|
159
|
+
return Reflect.set(target._array, prop, val);
|
|
160
|
+
}
|
|
161
|
+
return Reflect.set(target, prop, val, receiver);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
get length() {
|
|
165
|
+
return this._array.length;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
set length(newLength) {
|
|
169
|
+
if (newLength === this._array.length) return;
|
|
170
|
+
if (newLength < this._array.length) {
|
|
171
|
+
this._array = new Uint8Array(this._array.buffer, 0, newLength);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const newArray = new Uint8Array(newLength);
|
|
175
|
+
newArray.set(this._array);
|
|
176
|
+
this._array = newArray;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
toString(encoding = 'UTF-8') {
|
|
180
|
+
return toString(this._array, encoding);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
toGBytes() {
|
|
184
|
+
return toGBytes(this._array);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @param {Iterable<number>} array an iterable to convert into a ByteArray wrapper
|
|
190
|
+
* @returns {InstanceType<ByteArray>}
|
|
191
|
+
*/
|
|
192
|
+
function fromArray(array) {
|
|
193
|
+
return new ByteArray(Uint8Array.from(array));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return { ByteArray, fromArray, fromGBytes, fromString, toGBytes, toString };
|
|
197
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/node-gi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "GObject-Introspection runtime for Node.js — load gi:// namespaces (GLib/GObject/Gio/…) with GJS-compatible semantics. Vendored + rewritten from node-gtk under MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -58,6 +58,12 @@
|
|
|
58
58
|
"prebuilds"
|
|
59
59
|
],
|
|
60
60
|
"gjsify": {
|
|
61
|
+
"platforms": [
|
|
62
|
+
"linux-x64",
|
|
63
|
+
"linux-arm64",
|
|
64
|
+
"darwin-arm64",
|
|
65
|
+
"win32-x64"
|
|
66
|
+
],
|
|
61
67
|
"runtimes": {
|
|
62
68
|
"gjs": "none",
|
|
63
69
|
"node": "polyfill",
|
|
@@ -79,6 +85,7 @@
|
|
|
79
85
|
"test:dbus": "dbus-run-session -- env NODE_GI_NATIVE=build node --test --expose-gc test/dbus.test.mjs test/dbus-async.test.mjs",
|
|
80
86
|
"test:bun": "node scripts/cross-runtime.mjs bun",
|
|
81
87
|
"test:conformance": "node scripts/conformance.mjs",
|
|
88
|
+
"test:conformance:gjs-napi": "node scripts/conformance.mjs --runtimes=gjs-napi",
|
|
82
89
|
"test:gimarshalling": "node scripts/gimarshalling.mjs",
|
|
83
90
|
"test:deno": "node scripts/cross-runtime.mjs deno",
|
|
84
91
|
"test:cairo": "NODE_GI_NATIVE=build node --test test/cairo.test.mjs",
|
|
Binary file
|
|
Binary file
|