@gjsify/utils 0.3.12 → 0.3.14
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/lib/esm/base64.js +63 -61
- package/lib/esm/byte-array.js +10 -4
- package/lib/esm/callable.js +23 -13
- package/lib/esm/cli.js +8 -6
- package/lib/esm/defer.js +9 -4
- package/lib/esm/encoding.js +40 -31
- package/lib/esm/error.js +30 -19
- package/lib/esm/file.js +11 -9
- package/lib/esm/fs.js +18 -16
- package/lib/esm/gio-errors.js +106 -125
- package/lib/esm/gio.js +44 -31
- package/lib/esm/globals.js +11 -6
- package/lib/esm/index.js +20 -18
- package/lib/esm/main-loop.js +40 -22
- package/lib/esm/message.js +9 -9
- package/lib/esm/microtask.js +5 -4
- package/lib/esm/next-tick.js +53 -48
- package/lib/esm/path.js +34 -36
- package/lib/esm/structured-clone.js +187 -157
- package/package.json +6 -6
package/lib/esm/gio-errors.js
CHANGED
|
@@ -1,135 +1,116 @@
|
|
|
1
|
+
//#region src/gio-errors.ts
|
|
2
|
+
/** Map from Gio.IOErrorEnum numeric values to Node.js error code strings. */
|
|
1
3
|
const GIO_ERROR_TO_NODE = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// TOO_MANY_OPEN_FILES
|
|
38
|
-
24: "EROFS",
|
|
39
|
-
// READ_ONLY
|
|
40
|
-
25: "ECANCELED",
|
|
41
|
-
// CANCELLED
|
|
42
|
-
26: "EBUSY",
|
|
43
|
-
// BUSY
|
|
44
|
-
27: "ETIMEDOUT",
|
|
45
|
-
// TIMED_OUT
|
|
46
|
-
28: "EHOSTUNREACH",
|
|
47
|
-
// HOST_NOT_FOUND (was WOULD_BLOCK)
|
|
48
|
-
30: "EHOSTUNREACH",
|
|
49
|
-
// HOST_NOT_FOUND
|
|
50
|
-
31: "ENETUNREACH",
|
|
51
|
-
// NETWORK_UNREACHABLE
|
|
52
|
-
32: "ECONNREFUSED",
|
|
53
|
-
// CONNECTION_REFUSED (legacy value)
|
|
54
|
-
33: "EADDRINUSE",
|
|
55
|
-
// ADDRESS_IN_USE
|
|
56
|
-
34: "ECONNRESET",
|
|
57
|
-
// CONNECTION_CLOSED (mapped to reset)
|
|
58
|
-
36: "EPIPE",
|
|
59
|
-
// BROKEN_PIPE
|
|
60
|
-
38: "ENETUNREACH",
|
|
61
|
-
// NETWORK_UNREACHABLE (actual GJS value)
|
|
62
|
-
39: "ECONNREFUSED",
|
|
63
|
-
// CONNECTION_REFUSED (actual GJS value)
|
|
64
|
-
40: "ECONNREFUSED",
|
|
65
|
-
// PROXY_FAILED
|
|
66
|
-
41: "EACCES",
|
|
67
|
-
// PROXY_AUTH_FAILED
|
|
68
|
-
44: "ECONNRESET",
|
|
69
|
-
// CONNECTION_CLOSED (actual GJS value)
|
|
70
|
-
46: "EMSGSIZE"
|
|
71
|
-
// MESSAGE_TOO_LARGE
|
|
4
|
+
0: "EIO",
|
|
5
|
+
1: "ENOENT",
|
|
6
|
+
2: "EEXIST",
|
|
7
|
+
3: "EISDIR",
|
|
8
|
+
4: "ENOTDIR",
|
|
9
|
+
5: "ENOTEMPTY",
|
|
10
|
+
6: "ENOENT",
|
|
11
|
+
7: "ENFILE",
|
|
12
|
+
9: "EACCES",
|
|
13
|
+
10: "ENFILE",
|
|
14
|
+
11: "EINVAL",
|
|
15
|
+
12: "ELOOP",
|
|
16
|
+
13: "ENOSPC",
|
|
17
|
+
14: "EACCES",
|
|
18
|
+
17: "ELOOP",
|
|
19
|
+
19: "ENOSPC",
|
|
20
|
+
20: "ENOTSUP",
|
|
21
|
+
22: "EMFILE",
|
|
22
|
+
24: "EROFS",
|
|
23
|
+
25: "ECANCELED",
|
|
24
|
+
26: "EBUSY",
|
|
25
|
+
27: "ETIMEDOUT",
|
|
26
|
+
28: "EHOSTUNREACH",
|
|
27
|
+
30: "EHOSTUNREACH",
|
|
28
|
+
31: "ENETUNREACH",
|
|
29
|
+
32: "ECONNREFUSED",
|
|
30
|
+
33: "EADDRINUSE",
|
|
31
|
+
34: "ECONNRESET",
|
|
32
|
+
36: "EPIPE",
|
|
33
|
+
38: "ENETUNREACH",
|
|
34
|
+
39: "ECONNREFUSED",
|
|
35
|
+
40: "ECONNREFUSED",
|
|
36
|
+
41: "EACCES",
|
|
37
|
+
44: "ECONNRESET",
|
|
38
|
+
46: "EMSGSIZE"
|
|
72
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* Create a Node.js-style ErrnoException from a Gio error.
|
|
42
|
+
* Works for fs, net, dns, child-process, and other modules.
|
|
43
|
+
*/
|
|
73
44
|
function createNodeError(err, syscall, details) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
45
|
+
const errObj = err;
|
|
46
|
+
const code = GIO_ERROR_TO_NODE[errObj?.code ?? -1] || "EIO";
|
|
47
|
+
let msg = `${code}: ${errObj?.message || "unknown error"}, ${syscall}`;
|
|
48
|
+
if (details?.path) msg += ` '${details.path}'`;
|
|
49
|
+
if (details?.dest) msg += ` -> '${details.dest}'`;
|
|
50
|
+
if (details?.address) msg += ` ${details.address}`;
|
|
51
|
+
if (details?.port != null) msg += `:${details.port}`;
|
|
52
|
+
const error = new Error(msg);
|
|
53
|
+
error.code = code;
|
|
54
|
+
error.syscall = syscall;
|
|
55
|
+
error.errno = -(errObj?.code || 0);
|
|
56
|
+
if (details?.path) error.path = details.path;
|
|
57
|
+
if (details?.address) error.address = details.address;
|
|
58
|
+
if (details?.port != null) error.port = details.port;
|
|
59
|
+
return error;
|
|
89
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if a Gio error is a "not found" error.
|
|
63
|
+
*/
|
|
90
64
|
function isNotFoundError(err) {
|
|
91
|
-
|
|
92
|
-
|
|
65
|
+
const errObj = err;
|
|
66
|
+
return errObj?.code === 1 || errObj?.code === "ENOENT";
|
|
93
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Map from GLib.FileError numeric values to Node.js error code strings.
|
|
70
|
+
* Distinct from Gio.IOErrorEnum — GLib.IOChannel.new_file() and some other
|
|
71
|
+
* low-level GLib APIs throw GLib.FileError (domain "g-file-error"), which
|
|
72
|
+
* has different numeric values than Gio.IOErrorEnum (domain "g-io-error-quark").
|
|
73
|
+
*/
|
|
94
74
|
const GLIB_FILE_ERROR_TO_NODE = {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
75
|
+
0: "EEXIST",
|
|
76
|
+
1: "EISDIR",
|
|
77
|
+
2: "EACCES",
|
|
78
|
+
3: "ENAMETOOLONG",
|
|
79
|
+
4: "ENOENT",
|
|
80
|
+
5: "ENOTDIR",
|
|
81
|
+
6: "ENXIO",
|
|
82
|
+
7: "ENODEV",
|
|
83
|
+
8: "EROFS",
|
|
84
|
+
11: "ELOOP",
|
|
85
|
+
12: "ENOSPC",
|
|
86
|
+
13: "ENOMEM",
|
|
87
|
+
14: "EMFILE",
|
|
88
|
+
15: "ENFILE",
|
|
89
|
+
16: "EBADF",
|
|
90
|
+
17: "EINVAL",
|
|
91
|
+
18: "EPIPE",
|
|
92
|
+
21: "EIO",
|
|
93
|
+
22: "EPERM",
|
|
94
|
+
24: "EIO"
|
|
115
95
|
};
|
|
96
|
+
/**
|
|
97
|
+
* Map a GLib.FileError to a Node.js-style ErrnoException. Counterpart to
|
|
98
|
+
* `createNodeError` for the Gio.IOErrorEnum case; kept separate because the
|
|
99
|
+
* enum domains differ.
|
|
100
|
+
*/
|
|
116
101
|
function createGLibFileError(err, syscall, details) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
102
|
+
const errObj = err;
|
|
103
|
+
const code = GLIB_FILE_ERROR_TO_NODE[errObj?.code ?? -1] ?? "EIO";
|
|
104
|
+
let msg = `${code}: ${errObj?.message || "unknown error"}, ${syscall}`;
|
|
105
|
+
if (details?.path) msg += ` '${details.path}'`;
|
|
106
|
+
if (details?.dest) msg += ` -> '${details.dest}'`;
|
|
107
|
+
const error = new Error(msg);
|
|
108
|
+
error.code = code;
|
|
109
|
+
error.syscall = syscall;
|
|
110
|
+
error.errno = -(errObj?.code || 0);
|
|
111
|
+
if (details?.path) error.path = details.path;
|
|
112
|
+
return error;
|
|
128
113
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
createGLibFileError,
|
|
133
|
-
createNodeError,
|
|
134
|
-
isNotFoundError
|
|
135
|
-
};
|
|
114
|
+
|
|
115
|
+
//#endregion
|
|
116
|
+
export { GIO_ERROR_TO_NODE, GLIB_FILE_ERROR_TO_NODE, createGLibFileError, createNodeError, isNotFoundError };
|
package/lib/esm/gio.js
CHANGED
|
@@ -1,39 +1,52 @@
|
|
|
1
1
|
import GLib from "@girs/glib-2.0";
|
|
2
|
+
|
|
3
|
+
//#region src/gio.ts
|
|
2
4
|
const byteArray = imports.byteArray;
|
|
5
|
+
/**
|
|
6
|
+
* Generic promise wrapper for Gio async/finish method pairs.
|
|
7
|
+
*
|
|
8
|
+
* Example:
|
|
9
|
+
* const stream = await gioAsync<Gio.InputStream>(session, 'send_async', 'send_finish', msg, priority, null);
|
|
10
|
+
*/
|
|
3
11
|
function gioAsync(obj, asyncMethod, finishMethod, ...args) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
obj[asyncMethod](...args, (_self, asyncRes) => {
|
|
14
|
+
try {
|
|
15
|
+
resolve(obj[finishMethod](asyncRes));
|
|
16
|
+
} catch (error) {
|
|
17
|
+
reject(error);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
});
|
|
13
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Promise wrapper around `Gio.InputStream.read_bytes_async` / `read_bytes_finish`.
|
|
24
|
+
* Returns a `Uint8Array` or `null` if the end of the stream is reached.
|
|
25
|
+
*/
|
|
14
26
|
async function readBytesAsync(inputStream, count = 4096, ioPriority = GLib.PRIORITY_DEFAULT, cancellable = null) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
inputStream.read_bytes_async(count, ioPriority, cancellable, (_self, asyncRes) => {
|
|
29
|
+
try {
|
|
30
|
+
const res = inputStream.read_bytes_finish(asyncRes);
|
|
31
|
+
if (res.get_size() === 0) {
|
|
32
|
+
return resolve(null);
|
|
33
|
+
}
|
|
34
|
+
return resolve(byteArray.fromGBytes(res));
|
|
35
|
+
} catch (error) {
|
|
36
|
+
reject(error);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
28
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Async generator that yields `Uint8Array` chunks from a `Gio.InputStream`.
|
|
43
|
+
*/
|
|
29
44
|
async function* inputStreamAsyncIterator(inputStream, count = 4096, ioPriority = GLib.PRIORITY_DEFAULT, cancellable = null) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
45
|
+
let chunk;
|
|
46
|
+
while ((chunk = await readBytesAsync(inputStream, count, ioPriority, cancellable)) !== null) {
|
|
47
|
+
yield chunk;
|
|
48
|
+
}
|
|
34
49
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
readBytesAsync
|
|
39
|
-
};
|
|
50
|
+
|
|
51
|
+
//#endregion
|
|
52
|
+
export { gioAsync, inputStreamAsyncIterator, readBytesAsync };
|
package/lib/esm/globals.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
//#region src/globals.ts
|
|
2
|
+
/**
|
|
3
|
+
* Register a value as a global property if it doesn't already exist.
|
|
4
|
+
* This is a no-op in environments where the global is already defined (e.g. Node.js).
|
|
5
|
+
*/
|
|
1
6
|
function registerGlobal(name, value) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
7
|
+
if (typeof globalThis[name] === "undefined") {
|
|
8
|
+
globalThis[name] = value;
|
|
9
|
+
}
|
|
5
10
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
};
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
export { registerGlobal };
|
package/lib/esm/index.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
import { atobPolyfill, base64Decode, base64Encode, btoaPolyfill } from "./base64.js";
|
|
2
|
+
import { gbytesToUint8Array } from "./byte-array.js";
|
|
3
|
+
import { makeCallable } from "./callable.js";
|
|
4
|
+
import { cli } from "./cli.js";
|
|
5
|
+
import { deferEmit } from "./defer.js";
|
|
6
|
+
import { checkEncoding, normalizeEncoding } from "./encoding.js";
|
|
7
|
+
import { initErrorV8Methods } from "./error.js";
|
|
8
|
+
import { readJSON } from "./file.js";
|
|
9
|
+
import { existsFD, existsSync } from "./fs.js";
|
|
10
|
+
import { GIO_ERROR_TO_NODE, GLIB_FILE_ERROR_TO_NODE, createGLibFileError, createNodeError, isNotFoundError } from "./gio-errors.js";
|
|
11
|
+
import { gioAsync, inputStreamAsyncIterator, readBytesAsync } from "./gio.js";
|
|
12
|
+
import { registerGlobal } from "./globals.js";
|
|
13
|
+
import { notImplemented, warnNotImplemented } from "./message.js";
|
|
14
|
+
import { queueMicrotask } from "./microtask.js";
|
|
15
|
+
import { __resetBurstStateForTests, nextTick } from "./next-tick.js";
|
|
16
|
+
import { getNodeModulesPath, getPathSeparator, getProgramDir, getProgramExe, resolve } from "./path.js";
|
|
17
|
+
import { structuredClone } from "./structured-clone.js";
|
|
18
|
+
import { ensureMainLoop, quitMainLoop } from "./main-loop.js";
|
|
19
|
+
|
|
20
|
+
export { GIO_ERROR_TO_NODE, GLIB_FILE_ERROR_TO_NODE, __resetBurstStateForTests, atobPolyfill, base64Decode, base64Encode, btoaPolyfill, checkEncoding, cli, createGLibFileError, createNodeError, deferEmit, ensureMainLoop, existsFD, existsSync, gbytesToUint8Array, getNodeModulesPath, getPathSeparator, getProgramDir, getProgramExe, gioAsync, initErrorV8Methods, inputStreamAsyncIterator, isNotFoundError, makeCallable, nextTick, normalizeEncoding, notImplemented, queueMicrotask, quitMainLoop, readBytesAsync, readJSON, registerGlobal, resolve, structuredClone, warnNotImplemented };
|
package/lib/esm/main-loop.js
CHANGED
|
@@ -1,28 +1,46 @@
|
|
|
1
|
+
//#region src/main-loop.ts
|
|
2
|
+
/** Sentinel to prevent double-start (setMainLoopHook throws if called twice). */
|
|
1
3
|
let _started = false;
|
|
4
|
+
/** The singleton MainLoop instance, if created. */
|
|
2
5
|
let _loop = null;
|
|
6
|
+
/**
|
|
7
|
+
* Ensure a GLib MainLoop is running for async I/O dispatch (Soup.Server,
|
|
8
|
+
* Gio.SocketService, etc.). No-op on Node.js. Idempotent.
|
|
9
|
+
*
|
|
10
|
+
* - Called automatically by `http.Server.listen()`, `net.Server.listen()`,
|
|
11
|
+
* `dgram.Socket.bind()` etc.
|
|
12
|
+
* - GTK apps should NOT call this — they use `Gtk.Application.runAsync()` instead.
|
|
13
|
+
*
|
|
14
|
+
* @returns The MainLoop instance on GJS, or `undefined` on Node.js.
|
|
15
|
+
*/
|
|
3
16
|
function ensureMainLoop() {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return _loop;
|
|
17
|
+
const gjsImports = globalThis.imports;
|
|
18
|
+
if (!gjsImports) return undefined;
|
|
19
|
+
if (_started) return _loop;
|
|
20
|
+
const GLibModule = gjsImports.gi.GLib;
|
|
21
|
+
_loop = new GLibModule.MainLoop(null, false);
|
|
22
|
+
_started = true;
|
|
23
|
+
if (GLibModule.main_depth() === 0) {
|
|
24
|
+
try {
|
|
25
|
+
_loop.runAsync();
|
|
26
|
+
} catch {}
|
|
27
|
+
}
|
|
28
|
+
return _loop;
|
|
17
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Quit the MainLoop created by `ensureMainLoop()`. Idempotent, no-op on Node.js.
|
|
32
|
+
*
|
|
33
|
+
* Calling `quit()` on a loop that hasn't started yet pre-quits it — when the
|
|
34
|
+
* `setMainLoopHook` later fires and calls `run()`, it returns immediately.
|
|
35
|
+
* This is used by `@gjsify/unit` to prevent the loop from blocking after tests.
|
|
36
|
+
*/
|
|
18
37
|
function quitMainLoop() {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
38
|
+
if (_loop) {
|
|
39
|
+
_loop.quit();
|
|
40
|
+
_started = false;
|
|
41
|
+
_loop = null;
|
|
42
|
+
}
|
|
24
43
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
};
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
export { ensureMainLoop, quitMainLoop };
|
package/lib/esm/message.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
//#region src/message.ts
|
|
1
2
|
const notImplemented = (msg) => {
|
|
2
|
-
|
|
3
|
-
|
|
3
|
+
const message = msg ? `Not implemented: ${msg}` : "Not implemented";
|
|
4
|
+
throw new Error(message);
|
|
4
5
|
};
|
|
5
6
|
const warnNotImplemented = (msg) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
10
|
-
export {
|
|
11
|
-
notImplemented,
|
|
12
|
-
warnNotImplemented
|
|
7
|
+
const message = msg ? `Not implemented: ${msg}` : "Not implemented";
|
|
8
|
+
console.warn(message);
|
|
9
|
+
return message;
|
|
13
10
|
};
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
export { notImplemented, warnNotImplemented };
|
package/lib/esm/microtask.js
CHANGED
package/lib/esm/next-tick.js
CHANGED
|
@@ -1,60 +1,65 @@
|
|
|
1
|
+
//#region src/next-tick.ts
|
|
1
2
|
const CHUNK_SIZE = 64;
|
|
2
3
|
const YIELD_DELAY_MS = 1;
|
|
3
4
|
const _queue = [];
|
|
4
5
|
let _drainerArmed = false;
|
|
5
6
|
function drainOnce(GLib) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
7
|
+
const end = Math.min(CHUNK_SIZE, _queue.length);
|
|
8
|
+
for (let i = 0; i < end; i++) {
|
|
9
|
+
const cb = _queue.shift();
|
|
10
|
+
try {
|
|
11
|
+
cb();
|
|
12
|
+
} catch (err) {
|
|
13
|
+
try {
|
|
14
|
+
GLib.log_default_handler("gjsify-nextTick", GLib.LogLevelFlags.LEVEL_WARNING, String(err?.stack || err), null);
|
|
15
|
+
} catch {}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (_queue.length > 0) {
|
|
19
|
+
GLib.timeout_add(GLib.PRIORITY_DEFAULT, YIELD_DELAY_MS, () => {
|
|
20
|
+
drainOnce(GLib);
|
|
21
|
+
return false;
|
|
22
|
+
});
|
|
23
|
+
} else {
|
|
24
|
+
_drainerArmed = false;
|
|
25
|
+
}
|
|
26
26
|
}
|
|
27
27
|
function tryGLibTimeout(cb) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
const GLib = globalThis.imports?.gi?.GLib;
|
|
29
|
+
if (!GLib?.timeout_add) return false;
|
|
30
|
+
_queue.push(cb);
|
|
31
|
+
if (!_drainerArmed) {
|
|
32
|
+
_drainerArmed = true;
|
|
33
|
+
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 0, () => {
|
|
34
|
+
drainOnce(GLib);
|
|
35
|
+
return false;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
39
|
}
|
|
40
|
+
/** @internal Test helper: reset burst state. */
|
|
40
41
|
function __resetBurstStateForTests() {
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
_queue.length = 0;
|
|
43
|
+
_drainerArmed = false;
|
|
43
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Schedule a function on the next turn of the event loop.
|
|
47
|
+
* On GJS: uses GLib.timeout_add(PRIORITY_DEFAULT, delay=0).
|
|
48
|
+
* On Node.js: uses process.nextTick → queueMicrotask → Promise.resolve().then().
|
|
49
|
+
*/
|
|
44
50
|
const nextTick = (fn, ...args) => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
};
|
|
57
|
-
export {
|
|
58
|
-
__resetBurstStateForTests,
|
|
59
|
-
nextTick
|
|
51
|
+
const cb = args.length > 0 ? () => fn(...args) : fn;
|
|
52
|
+
if (tryGLibTimeout(cb)) return;
|
|
53
|
+
if (typeof globalThis.process?.nextTick === "function") {
|
|
54
|
+
globalThis.process.nextTick(fn, ...args);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (typeof queueMicrotask === "function") {
|
|
58
|
+
queueMicrotask(cb);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
Promise.resolve().then(cb);
|
|
60
62
|
};
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
export { __resetBurstStateForTests, nextTick };
|