gestament 0.2.0 → 0.3.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.
Files changed (86) hide show
  1. package/README.md +4 -6
  2. package/dist/displaySession.d.ts +18 -0
  3. package/dist/displaySession.d.ts.map +1 -0
  4. package/dist/element.d.ts +2 -2
  5. package/dist/errors-6gj5YuLw.cjs +36 -0
  6. package/dist/errors-6gj5YuLw.cjs.map +1 -0
  7. package/dist/errors-CCW4ATME.js +37 -0
  8. package/dist/errors-CCW4ATME.js.map +1 -0
  9. package/dist/errors.d.ts +2 -2
  10. package/dist/errors.d.ts.map +1 -1
  11. package/dist/generated/packageMetadata.d.ts +4 -4
  12. package/dist/gestament-config.d.ts +2 -2
  13. package/dist/gestament-launcher-driver.cjs +656 -0
  14. package/dist/gestament-launcher-driver.cjs.map +1 -0
  15. package/dist/gestament-launcher-driver.d.ts +13 -0
  16. package/dist/gestament-launcher-driver.d.ts.map +1 -0
  17. package/dist/gestament-launcher-driver.mjs +655 -0
  18. package/dist/gestament-launcher-driver.mjs.map +1 -0
  19. package/dist/gestament-tray-host.cjs +1 -1
  20. package/dist/gestament-tray-host.d.ts +2 -2
  21. package/dist/gestament-tray-host.mjs +1 -1
  22. package/dist/gestament-xvfb-pool-probe.cjs +29 -0
  23. package/dist/gestament-xvfb-pool-probe.cjs.map +1 -0
  24. package/dist/gestament-xvfb-pool-probe.d.ts +13 -0
  25. package/dist/gestament-xvfb-pool-probe.d.ts.map +1 -0
  26. package/dist/gestament-xvfb-pool-probe.mjs +28 -0
  27. package/dist/gestament-xvfb-pool-probe.mjs.map +1 -0
  28. package/dist/gestament-xvfb-worker.d.ts +2 -2
  29. package/dist/gestament-xvfb.cjs +8 -2
  30. package/dist/gestament-xvfb.cjs.map +1 -1
  31. package/dist/gestament-xvfb.d.ts +2 -2
  32. package/dist/gestament-xvfb.mjs +8 -2
  33. package/dist/gestament-xvfb.mjs.map +1 -1
  34. package/dist/index.cjs +4 -1147
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.ts +2 -2
  37. package/dist/index.mjs +4 -1147
  38. package/dist/index.mjs.map +1 -1
  39. package/dist/launchGtkApp-BIO_5Xjn.cjs +2479 -0
  40. package/dist/launchGtkApp-BIO_5Xjn.cjs.map +1 -0
  41. package/dist/launchGtkApp-qi1qm5G4.js +2479 -0
  42. package/dist/launchGtkApp-qi1qm5G4.js.map +1 -0
  43. package/dist/launchGtkApp.d.ts +2 -2
  44. package/dist/launchGtkApp.d.ts.map +1 -1
  45. package/dist/launcherDriverProtocol.d.ts +107 -0
  46. package/dist/launcherDriverProtocol.d.ts.map +1 -0
  47. package/dist/{native-ie_XIt1J.cjs → native-CBXaFWP-.cjs} +14 -40
  48. package/dist/native-CBXaFWP-.cjs.map +1 -0
  49. package/dist/{native-DUeVYIBs.js → native-CWdUmdty.js} +48 -74
  50. package/dist/native-CWdUmdty.js.map +1 -0
  51. package/dist/native.d.ts +4 -2
  52. package/dist/native.d.ts.map +1 -1
  53. package/dist/prerequisites-BuZST2Dy.cjs +15 -0
  54. package/dist/prerequisites-BuZST2Dy.cjs.map +1 -0
  55. package/dist/prerequisites-JB0SKPVd.js +16 -0
  56. package/dist/prerequisites-JB0SKPVd.js.map +1 -0
  57. package/dist/prerequisites.d.ts +23 -0
  58. package/dist/prerequisites.d.ts.map +1 -0
  59. package/dist/testing.cjs +3 -0
  60. package/dist/testing.cjs.map +1 -1
  61. package/dist/testing.d.ts +4 -2
  62. package/dist/testing.d.ts.map +1 -1
  63. package/dist/testing.mjs +4 -1
  64. package/dist/testing.mjs.map +1 -1
  65. package/dist/tray.d.ts +2 -2
  66. package/dist/types.d.ts +64 -2
  67. package/dist/types.d.ts.map +1 -1
  68. package/dist/wait-DV5gkXs8.js +113 -0
  69. package/dist/wait-DV5gkXs8.js.map +1 -0
  70. package/dist/wait-eOIz4nZm.cjs +112 -0
  71. package/dist/wait-eOIz4nZm.cjs.map +1 -0
  72. package/dist/wait.d.ts +67 -0
  73. package/dist/wait.d.ts.map +1 -0
  74. package/package.json +7 -7
  75. package/prebuilds/linux-arm/gtk3/node.napi.armv7.glibc.node +0 -0
  76. package/prebuilds/linux-arm/gtk4/node.napi.armv7.glibc.node +0 -0
  77. package/prebuilds/linux-arm64/gtk3/node.napi.glibc.node +0 -0
  78. package/prebuilds/linux-arm64/gtk4/node.napi.glibc.node +0 -0
  79. package/prebuilds/linux-ia32/gtk3/node.napi.glibc.node +0 -0
  80. package/prebuilds/linux-ia32/gtk4/node.napi.glibc.node +0 -0
  81. package/prebuilds/linux-riscv64/gtk3/node.napi.glibc.node +0 -0
  82. package/prebuilds/linux-riscv64/gtk4/node.napi.glibc.node +0 -0
  83. package/prebuilds/linux-x64/gtk3/node.napi.glibc.node +0 -0
  84. package/prebuilds/linux-x64/gtk4/node.napi.glibc.node +0 -0
  85. package/dist/native-DUeVYIBs.js.map +0 -1
  86. package/dist/native-ie_XIt1J.cjs.map +0 -1
@@ -0,0 +1,2479 @@
1
+ "use strict";
2
+ const node_child_process = require("node:child_process");
3
+ const wait = require("./wait-eOIz4nZm.cjs");
4
+ const errors = require("./errors-6gj5YuLw.cjs");
5
+ const node_fs = require("node:fs");
6
+ const node_net = require("node:net");
7
+ const node_os = require("node:os");
8
+ const node_path = require("node:path");
9
+ const node_url = require("node:url");
10
+ const prerequisites = require("./prerequisites-BuZST2Dy.cjs");
11
+ const native = require("./native-CBXaFWP-.cjs");
12
+ var _documentCurrentScript = typeof document !== "undefined" ? document.currentScript : null;
13
+ const defaultDisplay = "xvfb";
14
+ const defaultGSettings = "memory";
15
+ const defaultTheme = "Adwaita";
16
+ const defaultXvfbScreen = "1280x720x24";
17
+ const defaultXvfbTrayHost = true;
18
+ const defaultXvfbPoolMaxIdlePerKey = 1;
19
+ const defaultXvfbPoolMaxIdleTotal = 4;
20
+ const screenPattern = /^[1-9][0-9]*x[1-9][0-9]*x[1-9][0-9]*$/;
21
+ const sessionStartupTimeoutMs = 3e4;
22
+ const sessionReleaseTimeoutMs = 5e3;
23
+ const xvfbStartupTimeoutMs = 1e4;
24
+ const xvfbPoolProbeTimeoutMs = 5e3;
25
+ const firstPooledDisplayNumber = 90;
26
+ const lastPooledDisplayNumber = 590;
27
+ let socketCounter = 0;
28
+ const leasedDisplayNumbers = /* @__PURE__ */ new Set();
29
+ const idleXvfbByKey = /* @__PURE__ */ new Map();
30
+ const idleAllByKey = /* @__PURE__ */ new Map();
31
+ const directXvfbs = /* @__PURE__ */ new Set();
32
+ let poolCleanupInstalled = false;
33
+ const hasValue = (value) => value !== void 0 && value.length > 0;
34
+ const delay = (timeoutMs) => new Promise((resolveDelay) => {
35
+ setTimeout(resolveDelay, timeoutMs);
36
+ });
37
+ const appendOutput$1 = (lines, chunk) => {
38
+ lines.push(chunk.toString("utf8"));
39
+ if (lines.length > 40) {
40
+ lines.splice(0, lines.length - 40);
41
+ }
42
+ };
43
+ const unrefHandle = (handle) => {
44
+ const maybeRefHandle = handle;
45
+ if (typeof maybeRefHandle.unref === "function") {
46
+ maybeRefHandle.unref();
47
+ }
48
+ };
49
+ const formatOutputTail = (stdout, stderr) => {
50
+ const stdoutText = stdout.join("").trim();
51
+ const stderrText = stderr.join("").trim();
52
+ if (stdoutText.length === 0 && stderrText.length === 0) {
53
+ return "";
54
+ }
55
+ return `
56
+ stdout:
57
+ ${stdoutText}
58
+ stderr:
59
+ ${stderrText}`;
60
+ };
61
+ const getHostDisplayState = () => ({
62
+ display: process.env.DISPLAY,
63
+ waylandDisplay: process.env.WAYLAND_DISPLAY
64
+ });
65
+ const resolveHostDisplayKind = (state) => {
66
+ if (hasValue(state.waylandDisplay)) {
67
+ return "wayland";
68
+ }
69
+ if (hasValue(state.display)) {
70
+ return "x11";
71
+ }
72
+ return void 0;
73
+ };
74
+ const resolveDisplay = (display) => {
75
+ if (display === void 0) {
76
+ return defaultDisplay;
77
+ }
78
+ if (display === "xvfb" || display === "host") {
79
+ return display;
80
+ }
81
+ throw errors.createGtkInvalidArgumentError(
82
+ `display must be "xvfb" or "host": ${String(display)}`
83
+ );
84
+ };
85
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
86
+ const resolvePoolLimit = (name, value, defaultValue) => {
87
+ if (value === void 0) {
88
+ return defaultValue;
89
+ }
90
+ if (!Number.isInteger(value) || value < 0) {
91
+ throw errors.createGtkInvalidArgumentError(`${name} must be an integer >= 0.`);
92
+ }
93
+ return value;
94
+ };
95
+ const resolveXvfbPool = (pool) => {
96
+ if (pool === void 0) {
97
+ return void 0;
98
+ }
99
+ if (!isRecord(pool)) {
100
+ throw errors.createGtkInvalidArgumentError(
101
+ 'xvfbPool must be an object with type "xvfb" or "all".'
102
+ );
103
+ }
104
+ const { type } = pool;
105
+ if (type !== "xvfb" && type !== "all") {
106
+ throw errors.createGtkInvalidArgumentError(
107
+ `xvfbPool.type must be "xvfb" or "all": ${String(type)}`
108
+ );
109
+ }
110
+ return {
111
+ maxIdlePerKey: resolvePoolLimit(
112
+ "xvfbPool.maxIdlePerKey",
113
+ pool.maxIdlePerKey,
114
+ defaultXvfbPoolMaxIdlePerKey
115
+ ),
116
+ maxIdleTotal: resolvePoolLimit(
117
+ "xvfbPool.maxIdleTotal",
118
+ pool.maxIdleTotal,
119
+ defaultXvfbPoolMaxIdleTotal
120
+ ),
121
+ type
122
+ };
123
+ };
124
+ const nonePoolLimits = () => ({
125
+ maxIdlePerKey: 0,
126
+ maxIdleTotal: 0
127
+ });
128
+ const poolLimits = (pool) => ({
129
+ maxIdlePerKey: pool.maxIdlePerKey,
130
+ maxIdleTotal: pool.maxIdleTotal
131
+ });
132
+ const shouldRetainIdlePool = (limits) => limits.maxIdlePerKey > 0 && limits.maxIdleTotal > 0;
133
+ const removeArrayEntry = (map, key, entry) => {
134
+ const entries = map.get(key);
135
+ if (entries === void 0) {
136
+ return;
137
+ }
138
+ const index = entries.indexOf(entry);
139
+ if (index >= 0) {
140
+ entries.splice(index, 1);
141
+ }
142
+ if (entries.length === 0) {
143
+ map.delete(key);
144
+ }
145
+ };
146
+ const pushArrayEntry = (map, key, entry) => {
147
+ const entries = map.get(key);
148
+ if (entries === void 0) {
149
+ map.set(key, [entry]);
150
+ return;
151
+ }
152
+ entries.push(entry);
153
+ };
154
+ const popArrayEntry = (map, key) => {
155
+ const entries = map.get(key);
156
+ if (entries === void 0) {
157
+ return void 0;
158
+ }
159
+ const entry = entries.pop();
160
+ if (entries.length === 0) {
161
+ map.delete(key);
162
+ }
163
+ return entry;
164
+ };
165
+ const totalArrayEntryCount = (map) => [...map.values()].reduce((total, entries) => total + entries.length, 0);
166
+ const oldestEntry = (entries) => [...entries].sort((first, second) => first.lastUsedAt - second.lastUsedAt)[0];
167
+ const allIdleXvfbs = () => [...idleXvfbByKey.values()].flat();
168
+ const allIdleAllSessions = () => [...idleAllByKey.values()].flat();
169
+ const totalIdlePoolSize = () => totalArrayEntryCount(idleXvfbByKey) + totalArrayEntryCount(idleAllByKey);
170
+ const candidateOldestIdlePool = () => {
171
+ const candidates = [
172
+ ...allIdleXvfbs().map((xvfb) => ({
173
+ kind: "xvfb",
174
+ lastUsedAt: xvfb.lastUsedAt,
175
+ xvfb
176
+ })),
177
+ ...allIdleAllSessions().map((session) => ({
178
+ kind: "all",
179
+ lastUsedAt: session.lastUsedAt,
180
+ session
181
+ }))
182
+ ].sort((first, second) => first.lastUsedAt - second.lastUsedAt);
183
+ const candidate = candidates[0];
184
+ if (candidate === void 0) {
185
+ return void 0;
186
+ }
187
+ return candidate.kind === "xvfb" ? { kind: "xvfb", xvfb: candidate.xvfb } : { kind: "all", session: candidate.session };
188
+ };
189
+ const evictIdleXvfb = async (xvfb) => {
190
+ removeArrayEntry(idleXvfbByKey, xvfb.key, xvfb);
191
+ await terminateXvfb(xvfb);
192
+ };
193
+ const evictIdleAllSession = async (session) => {
194
+ removeArrayEntry(idleAllByKey, session.key, session);
195
+ await session.session.terminate();
196
+ };
197
+ const evictIdlePools = async (maxIdleTotal) => {
198
+ while (totalIdlePoolSize() > maxIdleTotal) {
199
+ const candidate = candidateOldestIdlePool();
200
+ if (candidate === void 0) {
201
+ return;
202
+ }
203
+ if (candidate.kind === "xvfb") {
204
+ await evictIdleXvfb(candidate.xvfb);
205
+ } else {
206
+ await evictIdleAllSession(candidate.session);
207
+ }
208
+ }
209
+ };
210
+ const trimIdleXvfbKey = async (key, maxIdlePerKey) => {
211
+ while ((idleXvfbByKey.get(key)?.length ?? 0) > maxIdlePerKey) {
212
+ const entry = oldestEntry(idleXvfbByKey.get(key) ?? []);
213
+ if (entry === void 0) {
214
+ return;
215
+ }
216
+ await evictIdleXvfb(entry);
217
+ }
218
+ };
219
+ const trimIdleAllKey = async (key, maxIdlePerKey) => {
220
+ while ((idleAllByKey.get(key)?.length ?? 0) > maxIdlePerKey) {
221
+ const entry = oldestEntry(idleAllByKey.get(key) ?? []);
222
+ if (entry === void 0) {
223
+ return;
224
+ }
225
+ await evictIdleAllSession(entry);
226
+ }
227
+ };
228
+ const retainIdleXvfb = async (xvfb, limits) => {
229
+ if (!shouldRetainIdlePool(limits)) {
230
+ await terminateXvfb(xvfb);
231
+ return;
232
+ }
233
+ xvfb.lastUsedAt = Date.now();
234
+ pushArrayEntry(idleXvfbByKey, xvfb.key, xvfb);
235
+ await trimIdleXvfbKey(xvfb.key, limits.maxIdlePerKey);
236
+ await evictIdlePools(limits.maxIdleTotal);
237
+ };
238
+ const retainIdleAllSession = async (session, limits) => {
239
+ if (!shouldRetainIdlePool(limits)) {
240
+ await session.session.terminate();
241
+ return;
242
+ }
243
+ session.lastUsedAt = Date.now();
244
+ session.xvfb.lastUsedAt = session.lastUsedAt;
245
+ pushArrayEntry(idleAllByKey, session.key, session);
246
+ await trimIdleAllKey(session.key, limits.maxIdlePerKey);
247
+ await evictIdlePools(limits.maxIdleTotal);
248
+ };
249
+ const emptyDriverSessionPoolOptions = () => ({
250
+ allKey: void 0,
251
+ allowedMappedWindowCount: 0,
252
+ limits: nonePoolLimits(),
253
+ mode: "none",
254
+ xvfb: void 0
255
+ });
256
+ const resolveXvfbOptions = (options) => {
257
+ const screen = options.xvfbScreen ?? defaultXvfbScreen;
258
+ if (!screenPattern.test(screen)) {
259
+ throw errors.createGtkInvalidArgumentError(
260
+ `xvfbScreen must be WIDTHxHEIGHTxDEPTH: ${screen}`
261
+ );
262
+ }
263
+ return {
264
+ pool: resolveXvfbPool(options.xvfbPool),
265
+ screen,
266
+ trayHost: options.xvfbTrayHost ?? defaultXvfbTrayHost
267
+ };
268
+ };
269
+ const resolveEffectiveDisplay = (display, xvfb) => {
270
+ if (display === "xvfb") {
271
+ return {
272
+ hostDisplay: void 0,
273
+ hostWaylandDisplay: void 0,
274
+ kind: "xvfb",
275
+ xvfb
276
+ };
277
+ }
278
+ const hostDisplay = getHostDisplayState();
279
+ if (resolveHostDisplayKind(hostDisplay) !== void 0) {
280
+ return {
281
+ hostDisplay: hostDisplay.display,
282
+ hostWaylandDisplay: hostDisplay.waylandDisplay,
283
+ kind: "host",
284
+ xvfb: void 0
285
+ };
286
+ }
287
+ return {
288
+ hostDisplay: void 0,
289
+ hostWaylandDisplay: void 0,
290
+ kind: "xvfb",
291
+ xvfb
292
+ };
293
+ };
294
+ const resolveGdkBackend = (effective) => {
295
+ if (effective.kind === "xvfb") {
296
+ return "x11";
297
+ }
298
+ if (process.env.GDK_BACKEND !== void 0) {
299
+ return void 0;
300
+ }
301
+ if (hasValue(effective.hostDisplay)) {
302
+ return "x11";
303
+ }
304
+ if (hasValue(effective.hostWaylandDisplay)) {
305
+ return "wayland";
306
+ }
307
+ return void 0;
308
+ };
309
+ const toWireEnvironment = (env) => {
310
+ const wireEnv = {};
311
+ for (const [key, value] of Object.entries(env)) {
312
+ wireEnv[key] = value ?? null;
313
+ }
314
+ return wireEnv;
315
+ };
316
+ const resolveLauncherEnvironment = (options, effective) => toWireEnvironment({
317
+ GDK_BACKEND: resolveGdkBackend(effective),
318
+ GSETTINGS_BACKEND: options.gsettings === null ? void 0 : options.gsettings ?? defaultGSettings,
319
+ GTK_THEME: options.theme === null ? void 0 : options.theme ?? defaultTheme,
320
+ ...options.env
321
+ });
322
+ const resolveDriverPath = () => {
323
+ const driverPath = node_path.resolve(
324
+ node_path.dirname(node_url.fileURLToPath(typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("launchGtkApp-BIO_5Xjn.cjs", document.baseURI).href)),
325
+ "..",
326
+ "dist",
327
+ "gestament-launcher-driver.cjs"
328
+ );
329
+ if (!node_fs.existsSync(driverPath)) {
330
+ throw errors.createGtkOperationFailedError(
331
+ `Internal launcher driver was not found: ${driverPath}`
332
+ );
333
+ }
334
+ return driverPath;
335
+ };
336
+ const resolveXvfbPoolProbePath = () => {
337
+ const probePath = node_path.resolve(
338
+ node_path.dirname(node_url.fileURLToPath(typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("launchGtkApp-BIO_5Xjn.cjs", document.baseURI).href)),
339
+ "..",
340
+ "dist",
341
+ "gestament-xvfb-pool-probe.cjs"
342
+ );
343
+ if (!node_fs.existsSync(probePath)) {
344
+ throw errors.createGtkOperationFailedError(
345
+ `Internal Xvfb pool probe was not found: ${probePath}`
346
+ );
347
+ }
348
+ return probePath;
349
+ };
350
+ const createDriverEnvironment = (effective, xvfb) => {
351
+ const env = { ...process.env };
352
+ delete env.AT_SPI_BUS_ADDRESS;
353
+ delete env.NO_AT_BRIDGE;
354
+ if (effective.kind === "xvfb") {
355
+ env.GDK_BACKEND = "x11";
356
+ env.GESTAMENT_XVFB_ACTIVE = "1";
357
+ if (xvfb !== void 0) {
358
+ env.DISPLAY = xvfb.display;
359
+ delete env.XAUTHORITY;
360
+ }
361
+ }
362
+ return env;
363
+ };
364
+ const xvfbSocketPath = (displayNumber) => `/tmp/.X11-unix/X${displayNumber}`;
365
+ const isDisplayNumberAvailable = (displayNumber) => !leasedDisplayNumbers.has(displayNumber) && !node_fs.existsSync(xvfbSocketPath(displayNumber));
366
+ const connectUnixSocket = (path, timeoutMs) => new Promise((resolveConnect, rejectConnect) => {
367
+ const socket = node_net.createConnection(path);
368
+ let settled = false;
369
+ const settle = (callback) => {
370
+ if (settled) {
371
+ return;
372
+ }
373
+ settled = true;
374
+ clearTimeout(timeout);
375
+ socket.destroy();
376
+ callback();
377
+ };
378
+ const timeout = setTimeout(() => {
379
+ settle(() => {
380
+ rejectConnect(
381
+ errors.createGtkOperationFailedError(`Timed out connecting to ${path}.`)
382
+ );
383
+ });
384
+ }, timeoutMs);
385
+ socket.once("connect", () => {
386
+ settle(resolveConnect);
387
+ });
388
+ socket.once("error", (error) => {
389
+ settle(() => {
390
+ rejectConnect(error);
391
+ });
392
+ });
393
+ });
394
+ const waitForXvfbReady = async (displayNumber) => {
395
+ const startedAt = Date.now();
396
+ const path = xvfbSocketPath(displayNumber);
397
+ while (Date.now() - startedAt <= xvfbStartupTimeoutMs) {
398
+ if (node_fs.existsSync(path)) {
399
+ try {
400
+ await connectUnixSocket(path, 250);
401
+ return;
402
+ } catch {
403
+ }
404
+ }
405
+ await delay(25);
406
+ }
407
+ throw errors.createGtkOperationFailedError(
408
+ `Timed out waiting for Xvfb display :${displayNumber}.`
409
+ );
410
+ };
411
+ const killXvfbNow = (xvfb) => {
412
+ if (xvfb.child.exitCode === null && xvfb.child.signalCode === null) {
413
+ xvfb.child.kill("SIGTERM");
414
+ }
415
+ };
416
+ const installPoolCleanup = () => {
417
+ if (poolCleanupInstalled) {
418
+ return;
419
+ }
420
+ poolCleanupInstalled = true;
421
+ process.once("exit", () => {
422
+ for (const xvfb of directXvfbs) {
423
+ killXvfbNow(xvfb);
424
+ }
425
+ });
426
+ };
427
+ const spawnDirectXvfb = async (screen) => {
428
+ installPoolCleanup();
429
+ for (let displayNumber = firstPooledDisplayNumber; displayNumber <= lastPooledDisplayNumber; displayNumber += 1) {
430
+ if (!isDisplayNumberAvailable(displayNumber)) {
431
+ continue;
432
+ }
433
+ leasedDisplayNumbers.add(displayNumber);
434
+ const stdout = [];
435
+ const stderr = [];
436
+ const child = node_child_process.spawn(
437
+ "Xvfb",
438
+ [`:${displayNumber}`, "-screen", "0", screen, "-nolisten", "tcp"],
439
+ {
440
+ env: process.env,
441
+ stdio: ["ignore", "pipe", "pipe"]
442
+ }
443
+ );
444
+ child.stdout.on("data", (chunk) => {
445
+ appendOutput$1(stdout, chunk);
446
+ });
447
+ child.stderr.on("data", (chunk) => {
448
+ appendOutput$1(stderr, chunk);
449
+ });
450
+ child.unref();
451
+ unrefHandle(child.stdout);
452
+ unrefHandle(child.stderr);
453
+ try {
454
+ await Promise.race([
455
+ waitForXvfbReady(displayNumber),
456
+ new Promise((_resolve, reject) => {
457
+ child.once("error", reject);
458
+ }),
459
+ new Promise((_resolve, reject) => {
460
+ child.once("exit", (code, signal) => {
461
+ reject(
462
+ errors.createGtkOperationFailedError(
463
+ `Xvfb exited before ready: code=${String(
464
+ code
465
+ )}, signal=${String(signal)}` + formatOutputTail(stdout, stderr)
466
+ )
467
+ );
468
+ });
469
+ })
470
+ ]);
471
+ const xvfb = {
472
+ child,
473
+ display: `:${displayNumber}`,
474
+ displayNumber,
475
+ key: screen,
476
+ lastUsedAt: Date.now(),
477
+ screen,
478
+ stderr,
479
+ stdout
480
+ };
481
+ directXvfbs.add(xvfb);
482
+ return xvfb;
483
+ } catch (error) {
484
+ killXvfbNow({
485
+ child
486
+ });
487
+ leasedDisplayNumbers.delete(displayNumber);
488
+ const message = error instanceof Error ? error.message : String(error);
489
+ if (message.includes("ENOENT")) {
490
+ throw errors.createGtkOperationFailedError(
491
+ prerequisites.appendPrerequisiteInstallHint(`Failed to start Xvfb: ${message}`)
492
+ );
493
+ }
494
+ }
495
+ }
496
+ throw errors.createGtkOperationFailedError(
497
+ `Failed to allocate a pooled Xvfb display for screen ${screen}.`
498
+ );
499
+ };
500
+ const terminateXvfb = async (xvfb) => {
501
+ removeArrayEntry(idleXvfbByKey, xvfb.key, xvfb);
502
+ directXvfbs.delete(xvfb);
503
+ if (xvfb.child.exitCode === null && xvfb.child.signalCode === null) {
504
+ xvfb.child.kill("SIGTERM");
505
+ const startedAt = Date.now();
506
+ while (xvfb.child.exitCode === null && xvfb.child.signalCode === null) {
507
+ if (Date.now() - startedAt > sessionReleaseTimeoutMs) {
508
+ xvfb.child.kill("SIGKILL");
509
+ break;
510
+ }
511
+ await delay(25);
512
+ }
513
+ }
514
+ leasedDisplayNumbers.delete(xvfb.displayNumber);
515
+ };
516
+ const leaseXvfb = async (screen) => {
517
+ for (; ; ) {
518
+ const idle = popArrayEntry(idleXvfbByKey, screen);
519
+ if (idle === void 0) {
520
+ return spawnDirectXvfb(screen);
521
+ }
522
+ if (idle.child.exitCode === null && idle.child.signalCode === null) {
523
+ idle.lastUsedAt = Date.now();
524
+ return idle;
525
+ }
526
+ await terminateXvfb(idle);
527
+ }
528
+ };
529
+ const runXvfbProbe = (xvfb) => new Promise((resolveProbe, rejectProbe) => {
530
+ const probePath = resolveXvfbPoolProbePath();
531
+ const stdout = [];
532
+ const stderr = [];
533
+ const child = node_child_process.spawn(process.execPath, [probePath], {
534
+ env: {
535
+ ...process.env,
536
+ DISPLAY: xvfb.display,
537
+ GDK_BACKEND: "x11"
538
+ },
539
+ stdio: ["ignore", "pipe", "pipe"]
540
+ });
541
+ const timeout = setTimeout(() => {
542
+ child.kill("SIGKILL");
543
+ rejectProbe(
544
+ errors.createGtkOperationFailedError("Timed out probing Xvfb pool.")
545
+ );
546
+ }, xvfbPoolProbeTimeoutMs);
547
+ child.stdout.on("data", (chunk) => {
548
+ appendOutput$1(stdout, chunk);
549
+ });
550
+ child.stderr.on("data", (chunk) => {
551
+ appendOutput$1(stderr, chunk);
552
+ });
553
+ child.once("error", (error) => {
554
+ clearTimeout(timeout);
555
+ rejectProbe(error);
556
+ });
557
+ child.once("exit", (code, signal) => {
558
+ clearTimeout(timeout);
559
+ if (code !== 0) {
560
+ rejectProbe(
561
+ errors.createGtkOperationFailedError(
562
+ `Xvfb pool probe failed: code=${String(code)}, signal=${String(
563
+ signal
564
+ )}` + formatOutputTail(stdout, stderr)
565
+ )
566
+ );
567
+ return;
568
+ }
569
+ const output = stdout.join("").trim().split("\n").at(-1);
570
+ if (output === void 0 || output.length === 0) {
571
+ rejectProbe(
572
+ errors.createGtkOperationFailedError(
573
+ "Xvfb pool probe did not return a result." + formatOutputTail(stdout, stderr)
574
+ )
575
+ );
576
+ return;
577
+ }
578
+ try {
579
+ resolveProbe(JSON.parse(output));
580
+ } catch (error) {
581
+ const message = error instanceof Error ? error.message : String(error);
582
+ rejectProbe(
583
+ errors.createGtkOperationFailedError(
584
+ `Xvfb pool probe returned invalid JSON: ${message}` + formatOutputTail(stdout, stderr)
585
+ )
586
+ );
587
+ }
588
+ });
589
+ });
590
+ const cleanCheckXvfb = async (xvfb, allowedMappedWindowCount) => {
591
+ try {
592
+ const probe = await runXvfbProbe(xvfb);
593
+ return probe.mappedWindowCount <= allowedMappedWindowCount;
594
+ } catch {
595
+ return false;
596
+ }
597
+ };
598
+ const returnXvfbToPool = async (xvfb, limits) => {
599
+ const clean = await cleanCheckXvfb(xvfb, 0);
600
+ if (!clean) {
601
+ await terminateXvfb(xvfb);
602
+ return;
603
+ }
604
+ await retainIdleXvfb(xvfb, limits);
605
+ };
606
+ const allPoolKey = (xvfb) => `${xvfb.screen}
607
+ ${xvfb.trayHost ? "tray" : "no-tray"}`;
608
+ const spawnDriverProcess = (driverPath, socketPath, effective, xvfb) => {
609
+ const driverArgs = [
610
+ "--socket",
611
+ socketPath,
612
+ ...effective.kind === "xvfb" && effective.xvfb?.trayHost === true ? ["--with-tray-host"] : []
613
+ ];
614
+ const env = createDriverEnvironment(effective, xvfb);
615
+ const stdout = [];
616
+ const stderr = [];
617
+ const command = effective.kind === "xvfb" && xvfb === void 0 ? {
618
+ args: [
619
+ "-a",
620
+ "-s",
621
+ `-screen 0 ${effective.xvfb?.screen ?? defaultXvfbScreen}`,
622
+ "--",
623
+ "dbus-run-session",
624
+ "--",
625
+ process.execPath,
626
+ driverPath,
627
+ ...driverArgs
628
+ ],
629
+ bin: "xvfb-run"
630
+ } : effective.kind === "xvfb" ? {
631
+ args: ["--", process.execPath, driverPath, ...driverArgs],
632
+ bin: "dbus-run-session"
633
+ } : {
634
+ args: [driverPath, ...driverArgs],
635
+ bin: process.execPath
636
+ };
637
+ const child = node_child_process.spawn(command.bin, command.args, {
638
+ env,
639
+ stdio: ["ignore", "pipe", "pipe"]
640
+ });
641
+ child.stdout.on("data", (chunk) => {
642
+ appendOutput$1(stdout, chunk);
643
+ });
644
+ child.stderr.on("data", (chunk) => {
645
+ appendOutput$1(stderr, chunk);
646
+ });
647
+ const commandLine = [command.bin, ...command.args].join(" ");
648
+ return { child, commandLine, stderr, stdout };
649
+ };
650
+ const listenOnSocket = (server, socketPath) => new Promise((resolveListen, rejectListen) => {
651
+ const rejectFromError = (error) => {
652
+ server.removeListener("listening", resolveFromListening);
653
+ rejectListen(error);
654
+ };
655
+ const resolveFromListening = () => {
656
+ server.removeListener("error", rejectFromError);
657
+ resolveListen();
658
+ };
659
+ server.once("error", rejectFromError);
660
+ server.once("listening", resolveFromListening);
661
+ server.listen(socketPath);
662
+ });
663
+ const parseReadyMessage = (line) => {
664
+ const value = JSON.parse(line);
665
+ if (typeof value === "object" && value !== null && !Array.isArray(value) && "type" in value && value.type === "ready") {
666
+ return value;
667
+ }
668
+ return void 0;
669
+ };
670
+ const waitForDriverReady = (server, processState) => new Promise((resolveReady, rejectReady) => {
671
+ let socket;
672
+ let input = "";
673
+ let settled = false;
674
+ const cleanup = () => {
675
+ clearTimeout(timeout);
676
+ server.removeListener("connection", acceptConnection);
677
+ server.removeListener("error", rejectFromError);
678
+ processState.child.removeListener("error", rejectFromError);
679
+ processState.child.removeListener("exit", rejectFromExit);
680
+ socket?.removeListener("data", readReadyData);
681
+ socket?.removeListener("error", rejectFromError);
682
+ socket?.removeListener("close", rejectFromSocketClose);
683
+ };
684
+ const rejectOnce = (error) => {
685
+ if (settled) {
686
+ return;
687
+ }
688
+ settled = true;
689
+ cleanup();
690
+ socket?.destroy();
691
+ rejectReady(error);
692
+ };
693
+ const rejectFromError = (error) => {
694
+ rejectOnce(
695
+ errors.createGtkOperationFailedError(
696
+ prerequisites.appendPrerequisiteInstallHint(
697
+ `Launcher driver failed to start: ${error.message}` + formatOutputTail(processState.stdout, processState.stderr)
698
+ )
699
+ )
700
+ );
701
+ };
702
+ const rejectFromExit = (code, signal) => {
703
+ rejectOnce(
704
+ errors.createGtkOperationFailedError(
705
+ prerequisites.appendPrerequisiteInstallHint(
706
+ `Launcher driver exited before ready: code=${String(
707
+ code
708
+ )}, signal=${String(signal)}` + formatOutputTail(processState.stdout, processState.stderr)
709
+ )
710
+ )
711
+ );
712
+ };
713
+ const rejectFromSocketClose = () => {
714
+ rejectOnce(
715
+ errors.createGtkOperationFailedError(
716
+ "Launcher driver socket closed before ready." + formatOutputTail(processState.stdout, processState.stderr)
717
+ )
718
+ );
719
+ };
720
+ const readReadyData = (chunk) => {
721
+ input += chunk.toString("utf8");
722
+ let newlineIndex = input.indexOf("\n");
723
+ while (newlineIndex >= 0) {
724
+ const line = input.slice(0, newlineIndex);
725
+ input = input.slice(newlineIndex + 1);
726
+ try {
727
+ if (parseReadyMessage(line) !== void 0) {
728
+ if (settled) {
729
+ return;
730
+ }
731
+ settled = true;
732
+ cleanup();
733
+ if (socket === void 0) {
734
+ rejectReady(
735
+ errors.createGtkOperationFailedError(
736
+ "Launcher driver reported ready without a socket."
737
+ )
738
+ );
739
+ return;
740
+ }
741
+ resolveReady({ bufferedInput: input, socket });
742
+ return;
743
+ }
744
+ } catch (error) {
745
+ const message = error instanceof Error ? error.message : String(error);
746
+ rejectOnce(
747
+ errors.createGtkOperationFailedError(
748
+ `Launcher driver sent invalid ready message: ${message}` + formatOutputTail(processState.stdout, processState.stderr)
749
+ )
750
+ );
751
+ return;
752
+ }
753
+ newlineIndex = input.indexOf("\n");
754
+ }
755
+ };
756
+ const acceptConnection = (acceptedSocket) => {
757
+ if (socket !== void 0) {
758
+ acceptedSocket.destroy();
759
+ return;
760
+ }
761
+ socket = acceptedSocket;
762
+ acceptedSocket.on("data", readReadyData);
763
+ acceptedSocket.once("error", rejectFromError);
764
+ acceptedSocket.once("close", rejectFromSocketClose);
765
+ };
766
+ const timeout = setTimeout(() => {
767
+ rejectOnce(
768
+ errors.createGtkOperationFailedError(
769
+ `Timed out waiting for launcher driver: ${processState.commandLine}` + formatOutputTail(processState.stdout, processState.stderr)
770
+ )
771
+ );
772
+ }, sessionStartupTimeoutMs);
773
+ server.on("connection", acceptConnection);
774
+ server.once("error", rejectFromError);
775
+ processState.child.once("error", rejectFromError);
776
+ processState.child.once("exit", rejectFromExit);
777
+ });
778
+ const serializeRequest = (id, command, payload) => {
779
+ const deadlineMs = wait.currentWaitDeadlineMs();
780
+ const request = deadlineMs === void 0 ? { command, id, payload } : { command, deadlineMs, id, payload };
781
+ return `${JSON.stringify(request)}
782
+ `;
783
+ };
784
+ const reconstructDriverError = (error) => {
785
+ const reconstructed = new Error(error.message);
786
+ reconstructed.name = error.name;
787
+ if (error.stack !== void 0) {
788
+ reconstructed.stack = error.stack;
789
+ }
790
+ if (error.code !== void 0) {
791
+ Object.defineProperty(reconstructed, "code", {
792
+ enumerable: true,
793
+ value: error.code
794
+ });
795
+ }
796
+ return reconstructed;
797
+ };
798
+ const decodeCapture = (capture) => ({
799
+ bounds: capture.bounds,
800
+ clipped: capture.clipped,
801
+ image: Buffer.from(capture.imageBase64, "base64"),
802
+ visibleBounds: capture.visibleBounds
803
+ });
804
+ const createDriverSession = (socket, bufferedInput, processState, tempDirectory, poolOptions) => {
805
+ const pending = /* @__PURE__ */ new Map();
806
+ let input = bufferedInput;
807
+ let nextRequestId = 1;
808
+ let closed = false;
809
+ const rejectPending = (error) => {
810
+ const entries = [...pending.values()];
811
+ pending.clear();
812
+ for (const entry of entries) {
813
+ entry.reject(error);
814
+ }
815
+ socket.unref();
816
+ };
817
+ const markClosed = () => {
818
+ if (closed) {
819
+ return;
820
+ }
821
+ closed = true;
822
+ rejectPending(errors.createGtkAppExitedError("Launcher driver has exited."));
823
+ };
824
+ const handleResponse = (line) => {
825
+ const response = JSON.parse(line);
826
+ const entry = pending.get(response.id);
827
+ if (entry === void 0) {
828
+ return;
829
+ }
830
+ pending.delete(response.id);
831
+ if (pending.size === 0) {
832
+ socket.unref();
833
+ }
834
+ if (response.ok) {
835
+ entry.resolve(response.value);
836
+ } else {
837
+ entry.reject(
838
+ reconstructDriverError(response.error)
839
+ );
840
+ }
841
+ };
842
+ const readData = (chunk) => {
843
+ input += chunk.toString("utf8");
844
+ let newlineIndex = input.indexOf("\n");
845
+ while (newlineIndex >= 0) {
846
+ const line = input.slice(0, newlineIndex);
847
+ input = input.slice(newlineIndex + 1);
848
+ try {
849
+ handleResponse(line);
850
+ } catch (error) {
851
+ const message = error instanceof Error ? error.message : String(error);
852
+ rejectPending(
853
+ errors.createGtkOperationFailedError(
854
+ `Launcher driver sent an invalid response: ${message}`
855
+ )
856
+ );
857
+ }
858
+ newlineIndex = input.indexOf("\n");
859
+ }
860
+ };
861
+ socket.on("data", readData);
862
+ socket.once("close", markClosed);
863
+ socket.once("error", markClosed);
864
+ processState.child.once("exit", markClosed);
865
+ processState.child.unref();
866
+ unrefHandle(processState.child.stdout);
867
+ unrefHandle(processState.child.stderr);
868
+ socket.unref();
869
+ const request = (command, payload) => {
870
+ if (closed) {
871
+ return Promise.reject(
872
+ errors.createGtkAppExitedError("Launcher driver has exited.")
873
+ );
874
+ }
875
+ const id = nextRequestId;
876
+ nextRequestId += 1;
877
+ socket.ref();
878
+ return new Promise((resolveRequest, rejectRequest) => {
879
+ pending.set(id, {
880
+ reject: rejectRequest,
881
+ resolve: (value) => {
882
+ resolveRequest(value);
883
+ }
884
+ });
885
+ socket.write(serializeRequest(id, command, payload), (error) => {
886
+ if (error != null) {
887
+ pending.delete(id);
888
+ if (pending.size === 0) {
889
+ socket.unref();
890
+ }
891
+ rejectRequest(
892
+ errors.createGtkOperationFailedError(
893
+ `Failed to write launcher driver request: ${error.message}`
894
+ )
895
+ );
896
+ }
897
+ });
898
+ });
899
+ };
900
+ const waitForExit = async () => {
901
+ const startedAt = Date.now();
902
+ while (processState.child.exitCode === null && processState.child.signalCode === null) {
903
+ if (Date.now() - startedAt > sessionReleaseTimeoutMs) {
904
+ processState.child.kill("SIGKILL");
905
+ break;
906
+ }
907
+ await delay(25);
908
+ }
909
+ };
910
+ const closeDriver = async () => {
911
+ if (!closed) {
912
+ try {
913
+ await request("launcher.release", null);
914
+ } catch (error) {
915
+ if (!closed) {
916
+ throw error;
917
+ }
918
+ }
919
+ }
920
+ closed = true;
921
+ socket.destroy();
922
+ await waitForExit();
923
+ node_fs.rmSync(tempDirectory, { force: true, recursive: true });
924
+ };
925
+ const terminate = async () => {
926
+ try {
927
+ await closeDriver();
928
+ } finally {
929
+ if (poolOptions.xvfb !== void 0) {
930
+ await terminateXvfb(poolOptions.xvfb);
931
+ }
932
+ }
933
+ };
934
+ let session;
935
+ const release = async () => {
936
+ if (poolOptions.mode === "none") {
937
+ await closeDriver();
938
+ return;
939
+ }
940
+ if (poolOptions.mode === "xvfb") {
941
+ try {
942
+ await closeDriver();
943
+ } catch (error) {
944
+ if (poolOptions.xvfb !== void 0) {
945
+ await terminateXvfb(poolOptions.xvfb);
946
+ }
947
+ throw error;
948
+ }
949
+ if (poolOptions.xvfb !== void 0) {
950
+ await returnXvfbToPool(poolOptions.xvfb, poolOptions.limits);
951
+ }
952
+ return;
953
+ }
954
+ if (poolOptions.xvfb === void 0 || poolOptions.allKey === void 0) {
955
+ await terminate();
956
+ return;
957
+ }
958
+ let resetResult;
959
+ try {
960
+ resetResult = await request("launcher.reset", null);
961
+ } catch (error) {
962
+ await terminate().catch(() => void 0);
963
+ throw error;
964
+ }
965
+ const tablesAreEmpty = resetResult.appCount === 0 && resetResult.elementCount === 0 && resetResult.imageInfoCount === 0 && resetResult.trayItemCount === 0;
966
+ const clean = tablesAreEmpty && await cleanCheckXvfb(
967
+ poolOptions.xvfb,
968
+ poolOptions.allowedMappedWindowCount
969
+ );
970
+ if (!clean) {
971
+ await terminate();
972
+ return;
973
+ }
974
+ await retainIdleAllSession(
975
+ {
976
+ key: poolOptions.allKey,
977
+ lastUsedAt: Date.now(),
978
+ session,
979
+ xvfb: poolOptions.xvfb
980
+ },
981
+ poolOptions.limits
982
+ );
983
+ };
984
+ session = { release, request, terminate };
985
+ return session;
986
+ };
987
+ const startFreshDriverSession = async (effective, xvfb, poolOptions) => {
988
+ const driverPath = resolveDriverPath();
989
+ const tempDirectory = node_fs.mkdtempSync(
990
+ node_path.join(node_os.tmpdir(), `gestament-launcher-${process.pid}-${socketCounter}-`)
991
+ );
992
+ socketCounter += 1;
993
+ const socketPath = node_path.join(tempDirectory, "driver.sock");
994
+ const server = node_net.createServer();
995
+ let processState;
996
+ try {
997
+ await listenOnSocket(server, socketPath);
998
+ server.unref();
999
+ processState = spawnDriverProcess(driverPath, socketPath, effective, xvfb);
1000
+ const connection = await waitForDriverReady(server, processState);
1001
+ server.close();
1002
+ const resolvedPoolOptions = poolOptions.mode === "all" && xvfb !== void 0 ? {
1003
+ ...poolOptions,
1004
+ allowedMappedWindowCount: (await runXvfbProbe(xvfb)).mappedWindowCount
1005
+ } : poolOptions;
1006
+ return createDriverSession(
1007
+ connection.socket,
1008
+ connection.bufferedInput,
1009
+ processState,
1010
+ tempDirectory,
1011
+ resolvedPoolOptions
1012
+ );
1013
+ } catch (error) {
1014
+ server.close();
1015
+ if (processState !== void 0 && processState.child.exitCode === null && processState.child.signalCode === null) {
1016
+ processState.child.kill("SIGTERM");
1017
+ }
1018
+ if (xvfb !== void 0) {
1019
+ await terminateXvfb(xvfb);
1020
+ }
1021
+ node_fs.rmSync(tempDirectory, { force: true, recursive: true });
1022
+ throw error;
1023
+ }
1024
+ };
1025
+ const startDriverSession = async (options) => {
1026
+ const display = resolveDisplay(options.display);
1027
+ const xvfbOptions = resolveXvfbOptions(options);
1028
+ const effective = resolveEffectiveDisplay(display, xvfbOptions);
1029
+ if (effective.kind !== "xvfb" || effective.xvfb === void 0) {
1030
+ return startFreshDriverSession(
1031
+ effective,
1032
+ void 0,
1033
+ emptyDriverSessionPoolOptions()
1034
+ );
1035
+ }
1036
+ const pool = effective.xvfb.pool;
1037
+ if (pool === void 0) {
1038
+ return startFreshDriverSession(
1039
+ effective,
1040
+ void 0,
1041
+ emptyDriverSessionPoolOptions()
1042
+ );
1043
+ }
1044
+ if (pool.type === "xvfb") {
1045
+ const xvfb2 = await leaseXvfb(effective.xvfb.screen);
1046
+ return startFreshDriverSession(effective, xvfb2, {
1047
+ allKey: void 0,
1048
+ allowedMappedWindowCount: 0,
1049
+ limits: poolLimits(pool),
1050
+ mode: "xvfb",
1051
+ xvfb: xvfb2
1052
+ });
1053
+ }
1054
+ const key = allPoolKey(effective.xvfb);
1055
+ for (; ; ) {
1056
+ const idle = popArrayEntry(idleAllByKey, key);
1057
+ if (idle === void 0) {
1058
+ break;
1059
+ }
1060
+ if (idle.xvfb.child.exitCode === null && idle.xvfb.child.signalCode === null) {
1061
+ idle.lastUsedAt = Date.now();
1062
+ idle.xvfb.lastUsedAt = idle.lastUsedAt;
1063
+ return idle.session;
1064
+ }
1065
+ await idle.session.terminate().catch(() => void 0);
1066
+ }
1067
+ const xvfb = await leaseXvfb(effective.xvfb.screen);
1068
+ return startFreshDriverSession(effective, xvfb, {
1069
+ allKey: key,
1070
+ allowedMappedWindowCount: 0,
1071
+ limits: poolLimits(pool),
1072
+ mode: "all",
1073
+ xvfb
1074
+ });
1075
+ };
1076
+ const createLaunchPayload = (options, args) => {
1077
+ const display = resolveDisplay(options.display);
1078
+ const xvfb = resolveXvfbOptions(options);
1079
+ const effective = resolveEffectiveDisplay(display, xvfb);
1080
+ return {
1081
+ appPath: options.appPath,
1082
+ args: [...options.args ?? [], ...args],
1083
+ env: resolveLauncherEnvironment(options, effective),
1084
+ timeoutMs: options.timeoutMs ?? null
1085
+ };
1086
+ };
1087
+ const elementRefToProxy = (session, ref) => ref === null ? void 0 : createProxyGtkElement(session, ref);
1088
+ const trayRefToProxy = (session, ref) => ref === null ? void 0 : createProxyGtkTrayItem(session, ref);
1089
+ const createProxyGtkApp = (session, ref) => {
1090
+ let released = false;
1091
+ const assertNotReleased = () => {
1092
+ if (released) {
1093
+ throw errors.createGtkAppExitedError("GTK application has been released.");
1094
+ }
1095
+ };
1096
+ const appRequest = (command, payload = {}) => {
1097
+ assertNotReleased();
1098
+ return session.request(command, {
1099
+ appId: ref.appId,
1100
+ ...payload
1101
+ });
1102
+ };
1103
+ const release = async () => {
1104
+ if (released) {
1105
+ return;
1106
+ }
1107
+ released = true;
1108
+ await session.request("app.release", { appId: ref.appId });
1109
+ };
1110
+ const app = {
1111
+ capture: async () => decodeCapture(await appRequest("app.capture")),
1112
+ findById: async (id) => elementRefToProxy(
1113
+ session,
1114
+ await appRequest("app.findById", { id })
1115
+ ),
1116
+ getById: async (id) => createProxyGtkElement(
1117
+ session,
1118
+ await appRequest("app.getById", { id })
1119
+ ),
1120
+ findByPath: async (path) => elementRefToProxy(
1121
+ session,
1122
+ await appRequest("app.findByPath", { path })
1123
+ ),
1124
+ getByPath: async (path) => createProxyGtkElement(
1125
+ session,
1126
+ await appRequest("app.getByPath", { path })
1127
+ ),
1128
+ windowAt: async (index) => elementRefToProxy(
1129
+ session,
1130
+ await appRequest("app.windowAt", { index })
1131
+ ),
1132
+ getWindowCount: () => appRequest("app.getWindowCount"),
1133
+ findTrayItem: async (selector) => trayRefToProxy(
1134
+ session,
1135
+ await appRequest("app.findTrayItem", {
1136
+ selector
1137
+ })
1138
+ ),
1139
+ getTrayItem: async (selector) => createProxyGtkTrayItem(
1140
+ session,
1141
+ await appRequest("app.getTrayItem", { selector })
1142
+ ),
1143
+ trayItemAt: async (index) => trayRefToProxy(
1144
+ session,
1145
+ await appRequest("app.trayItemAt", {
1146
+ index
1147
+ })
1148
+ ),
1149
+ getTrayItemCount: () => appRequest("app.getTrayItemCount"),
1150
+ release,
1151
+ [Symbol.asyncDispose]: release
1152
+ };
1153
+ return app;
1154
+ };
1155
+ const addChildContainerProxyOperations = (session, elementId, target) => {
1156
+ target.childAt = async (index) => elementRefToProxy(
1157
+ session,
1158
+ await session.request("element.childAt", {
1159
+ elementId,
1160
+ index
1161
+ })
1162
+ );
1163
+ target.getChildCount = () => session.request("element.getChildCount", { elementId });
1164
+ };
1165
+ const addSelectableChildContainerProxyOperations = (session, elementId, target) => {
1166
+ addChildContainerProxyOperations(session, elementId, target);
1167
+ target.getSelectedChildCount = () => session.request("element.getSelectedChildCount", { elementId });
1168
+ target.selectedChildAt = async (selectedIndex) => elementRefToProxy(
1169
+ session,
1170
+ await session.request(
1171
+ "element.selectedChildAt",
1172
+ {
1173
+ elementId,
1174
+ selectedIndex
1175
+ }
1176
+ )
1177
+ );
1178
+ target.isChildSelected = (index) => session.request("element.isChildSelected", { elementId, index });
1179
+ target.selectChildAt = (index) => session.request("element.selectChildAt", { elementId, index }).then(() => void 0);
1180
+ target.deselectChildAt = (index) => session.request("element.deselectChildAt", { elementId, index }).then(() => void 0);
1181
+ target.selectAllChildren = () => session.request("element.selectAllChildren", { elementId }).then(() => void 0);
1182
+ target.clearSelection = () => session.request("element.clearSelection", { elementId }).then(() => void 0);
1183
+ };
1184
+ const addClickableProxyOperation = (session, elementId, target) => {
1185
+ target.click = () => session.request("element.click", { elementId }).then(() => void 0);
1186
+ };
1187
+ const addCheckableProxyOperations = (session, elementId, target) => {
1188
+ target.isChecked = () => session.request("element.isChecked", { elementId });
1189
+ target.toggle = () => session.request("element.toggle", { elementId }).then(() => void 0);
1190
+ };
1191
+ const addTextProxyOperation = (session, elementId, target) => {
1192
+ target.text = () => session.request("element.text", { elementId });
1193
+ };
1194
+ const addValueProxyOperations = (session, elementId, target, writable) => {
1195
+ target.value = () => session.request("element.value", { elementId });
1196
+ target.valueInfo = () => session.request("element.valueInfo", { elementId });
1197
+ if (writable) {
1198
+ target.setValue = (value) => session.request("element.setValue", { elementId, value }).then(() => void 0);
1199
+ }
1200
+ };
1201
+ const createProxyImageInfo = (session, info) => ({
1202
+ bounds: info.bounds,
1203
+ capture: async () => decodeCapture(
1204
+ await session.request("imageInfo.capture", {
1205
+ imageInfoId: info.imageInfoId
1206
+ })
1207
+ ),
1208
+ description: info.description,
1209
+ locale: info.locale,
1210
+ position: info.position,
1211
+ size: info.size
1212
+ });
1213
+ const createProxyGtkElement = (session, ref) => {
1214
+ const elementId = ref.elementId;
1215
+ const target = {
1216
+ capture: async () => decodeCapture(
1217
+ await session.request("element.capture", { elementId })
1218
+ ),
1219
+ info: () => session.request("element.info", { elementId }),
1220
+ kind: ref.kind
1221
+ };
1222
+ switch (ref.kind) {
1223
+ case "window":
1224
+ case "container":
1225
+ case "menu":
1226
+ addChildContainerProxyOperations(session, elementId, target);
1227
+ break;
1228
+ case "button":
1229
+ case "listItem":
1230
+ case "menuItem":
1231
+ addClickableProxyOperation(session, elementId, target);
1232
+ break;
1233
+ case "label":
1234
+ case "text":
1235
+ addTextProxyOperation(session, elementId, target);
1236
+ break;
1237
+ case "entry":
1238
+ addTextProxyOperation(session, elementId, target);
1239
+ target.setText = (text) => session.request("element.setText", { elementId, text }).then(() => void 0);
1240
+ break;
1241
+ case "checkbox":
1242
+ case "switch":
1243
+ case "radio":
1244
+ case "toggleButton":
1245
+ addClickableProxyOperation(session, elementId, target);
1246
+ addCheckableProxyOperations(session, elementId, target);
1247
+ break;
1248
+ case "slider":
1249
+ addValueProxyOperations(session, elementId, target, true);
1250
+ break;
1251
+ case "spinButton":
1252
+ addValueProxyOperations(session, elementId, target, true);
1253
+ target.increment = () => session.request("element.increment", { elementId }).then(() => void 0);
1254
+ target.decrement = () => session.request("element.decrement", { elementId }).then(() => void 0);
1255
+ break;
1256
+ case "progressBar":
1257
+ addValueProxyOperations(session, elementId, target, false);
1258
+ break;
1259
+ case "comboBox":
1260
+ addClickableProxyOperation(session, elementId, target);
1261
+ addSelectableChildContainerProxyOperations(session, elementId, target);
1262
+ break;
1263
+ case "list":
1264
+ addSelectableChildContainerProxyOperations(session, elementId, target);
1265
+ break;
1266
+ case "table":
1267
+ target.getRowCount = () => session.request("element.getRowCount", { elementId });
1268
+ target.getColumnCount = () => session.request("element.getColumnCount", { elementId });
1269
+ target.cellAt = async (row, column) => elementRefToProxy(
1270
+ session,
1271
+ await session.request("element.cellAt", {
1272
+ column,
1273
+ elementId,
1274
+ row
1275
+ })
1276
+ );
1277
+ target.selectedRows = () => session.request("element.selectedRows", {
1278
+ elementId
1279
+ });
1280
+ target.selectedColumns = () => session.request("element.selectedColumns", {
1281
+ elementId
1282
+ });
1283
+ target.isRowSelected = (row) => session.request("element.isRowSelected", { elementId, row });
1284
+ target.isColumnSelected = (column) => session.request("element.isColumnSelected", {
1285
+ column,
1286
+ elementId
1287
+ });
1288
+ target.isCellSelected = (row, column) => session.request("element.isCellSelected", {
1289
+ column,
1290
+ elementId,
1291
+ row
1292
+ });
1293
+ target.selectRow = (row) => session.request("element.selectRow", { elementId, row }).then(() => void 0);
1294
+ target.deselectRow = (row) => session.request("element.deselectRow", { elementId, row }).then(() => void 0);
1295
+ target.selectColumn = (column) => session.request("element.selectColumn", { column, elementId }).then(() => void 0);
1296
+ target.deselectColumn = (column) => session.request("element.deselectColumn", { column, elementId }).then(() => void 0);
1297
+ break;
1298
+ case "image":
1299
+ target.imageInfo = async () => createProxyImageInfo(
1300
+ session,
1301
+ await session.request("element.imageInfo", {
1302
+ elementId
1303
+ })
1304
+ );
1305
+ break;
1306
+ }
1307
+ return target;
1308
+ };
1309
+ const createProxyGtkTrayItem = (session, ref) => {
1310
+ const trayItemId = ref.trayItemId;
1311
+ return {
1312
+ capture: async () => decodeCapture(
1313
+ await session.request("tray.capture", { trayItemId })
1314
+ ),
1315
+ click: () => session.request("tray.click", { trayItemId }).then(() => void 0),
1316
+ element: async () => elementRefToProxy(
1317
+ session,
1318
+ await session.request("tray.element", {
1319
+ trayItemId
1320
+ })
1321
+ ),
1322
+ metadata: () => session.request("tray.metadata", { trayItemId }),
1323
+ openMenu: async () => elementRefToProxy(
1324
+ session,
1325
+ await session.request("tray.openMenu", {
1326
+ trayItemId
1327
+ })
1328
+ )
1329
+ };
1330
+ };
1331
+ const createDriverBackedGtkAppLauncher = (options) => {
1332
+ let sessionPromise;
1333
+ const ensureSession = () => {
1334
+ sessionPromise ??= startDriverSession(options);
1335
+ return sessionPromise;
1336
+ };
1337
+ const launch = async (args) => {
1338
+ const session = await ensureSession();
1339
+ const appRef = await session.request(
1340
+ "launcher.launch",
1341
+ createLaunchPayload(options, args ?? [])
1342
+ );
1343
+ return createProxyGtkApp(session, appRef);
1344
+ };
1345
+ const release = async () => {
1346
+ const releasingSession = sessionPromise;
1347
+ sessionPromise = void 0;
1348
+ if (releasingSession === void 0) {
1349
+ return;
1350
+ }
1351
+ const session = await releasingSession;
1352
+ await session.release();
1353
+ };
1354
+ return {
1355
+ launch,
1356
+ release,
1357
+ [Symbol.asyncDispose]: release
1358
+ };
1359
+ };
1360
+ const assertNonNegativeIndex$1 = (name, index) => {
1361
+ if (!Number.isInteger(index) || index < 0) {
1362
+ throw errors.createGtkInvalidArgumentError(
1363
+ `${name} must be a non-negative integer.`
1364
+ );
1365
+ }
1366
+ };
1367
+ const assertFiniteNumber = (name, value) => {
1368
+ if (!Number.isFinite(value)) {
1369
+ throw errors.createGtkInvalidArgumentError(`${name} must be a finite number.`);
1370
+ }
1371
+ };
1372
+ const normalizeRoleName = (roleName) => roleName.trim().toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " ");
1373
+ const hasInterface = (info, name) => info.interfaces.some(
1374
+ (interfaceName) => interfaceName.toLowerCase() === name.toLowerCase()
1375
+ );
1376
+ const hasState = (info, name) => info.states.some((state) => state.toLowerCase() === name.toLowerCase());
1377
+ const elementInfoOverrides = /* @__PURE__ */ new WeakMap();
1378
+ const overrideElementInfo = (handle, override) => {
1379
+ elementInfoOverrides.set(handle, override);
1380
+ return handle;
1381
+ };
1382
+ const nativeInfoKind = (handle) => elementInfoOverrides.get(handle)?.kind ?? widgetKindFromInfo(native.nativeElementInfo(handle));
1383
+ const widgetKindFromInfo = (info) => {
1384
+ const roleName = normalizeRoleName(info.roleName);
1385
+ if (hasInterface(info, "TableCell")) {
1386
+ return "tableCell";
1387
+ }
1388
+ switch (roleName) {
1389
+ case "frame":
1390
+ case "dialog":
1391
+ case "window":
1392
+ return "window";
1393
+ case "filler":
1394
+ case "panel":
1395
+ case "scroll pane":
1396
+ case "layered pane":
1397
+ case "split pane":
1398
+ case "viewport":
1399
+ return "container";
1400
+ case "button":
1401
+ case "push button":
1402
+ case "push button menu":
1403
+ return "button";
1404
+ case "label":
1405
+ case "static":
1406
+ return "label";
1407
+ case "entry":
1408
+ case "password text":
1409
+ case "search box":
1410
+ return "entry";
1411
+ case "text":
1412
+ return hasInterface(info, "EditableText") || hasState(info, "editable") || hasState(info, "singleLine") ? "entry" : "text";
1413
+ case "paragraph":
1414
+ return "text";
1415
+ case "check box":
1416
+ return "checkbox";
1417
+ case "switch":
1418
+ return "switch";
1419
+ case "radio button":
1420
+ return "radio";
1421
+ case "toggle button":
1422
+ return hasInterface(info, "Image") || info.name.length > 0 ? "toggleButton" : "switch";
1423
+ case "slider":
1424
+ return "slider";
1425
+ case "spin button":
1426
+ return "spinButton";
1427
+ case "progress bar":
1428
+ case "level bar":
1429
+ return "progressBar";
1430
+ case "combo box":
1431
+ return "comboBox";
1432
+ case "list":
1433
+ case "list box":
1434
+ return "list";
1435
+ case "tree":
1436
+ return hasInterface(info, "Table") ? "table" : "list";
1437
+ case "list item":
1438
+ case "tree item":
1439
+ return "listItem";
1440
+ case "table":
1441
+ case "tree table":
1442
+ return "table";
1443
+ case "table cell":
1444
+ return "tableCell";
1445
+ case "image":
1446
+ case "icon":
1447
+ return "image";
1448
+ case "menu":
1449
+ case "menu bar":
1450
+ case "popup menu":
1451
+ return "menu";
1452
+ case "menu item":
1453
+ case "check menu item":
1454
+ case "radio menu item":
1455
+ case "tearoff menu item":
1456
+ return "menuItem";
1457
+ default:
1458
+ return "unknown";
1459
+ }
1460
+ };
1461
+ const toGtkElementInfo = (info) => ({
1462
+ ...info,
1463
+ kind: widgetKindFromInfo(info)
1464
+ });
1465
+ const toGtkElementInfoForHandle = (handle) => {
1466
+ const info = toGtkElementInfo(native.nativeElementInfo(handle));
1467
+ const override = elementInfoOverrides.get(handle);
1468
+ return override === void 0 ? info : { ...info, ...override };
1469
+ };
1470
+ const createCommonElement = (handle) => ({
1471
+ info: async () => toGtkElementInfoForHandle(handle),
1472
+ capture: async () => native.nativeCapture(handle)
1473
+ });
1474
+ const expectedKindLabel = (expectedKinds) => expectedKinds.join(", ");
1475
+ const assertExpectedKind = (element, expectedKinds, operation) => {
1476
+ if (!expectedKinds.includes(element.kind)) {
1477
+ throw errors.createGtkUnsupportedInterfaceError(
1478
+ `${operation} returned ${element.kind}, expected ${expectedKindLabel(
1479
+ expectedKinds
1480
+ )}.`
1481
+ );
1482
+ }
1483
+ return element;
1484
+ };
1485
+ const createGetChildCountOperation = (handle) => async () => native.nativeChildCount(handle);
1486
+ const createChildAtOperation = (handle, expectedKinds) => async (index) => {
1487
+ assertNonNegativeIndex$1("index", index);
1488
+ const childHandle = native.nativeChildAt(handle, index);
1489
+ if (childHandle === void 0) {
1490
+ return void 0;
1491
+ }
1492
+ const child = createGtkElement(childHandle);
1493
+ return expectedKinds === void 0 ? child : assertExpectedKind(child, expectedKinds, "childAt()");
1494
+ };
1495
+ const createChildContainerOperations = (handle, expectedKinds) => ({
1496
+ childAt: createChildAtOperation(handle, expectedKinds),
1497
+ getChildCount: createGetChildCountOperation(handle)
1498
+ });
1499
+ const createSelectableChildContainerOperations = (handle, expectedKinds) => ({
1500
+ ...createChildContainerOperations(handle, expectedKinds),
1501
+ getSelectedChildCount: async () => native.nativeSelectedChildCount(handle),
1502
+ selectedChildAt: async (selectedIndex) => {
1503
+ assertNonNegativeIndex$1("selectedIndex", selectedIndex);
1504
+ const childHandle = native.nativeSelectedChildAt(handle, selectedIndex);
1505
+ if (childHandle === void 0) {
1506
+ return void 0;
1507
+ }
1508
+ const child = createGtkElement(childHandle);
1509
+ return assertExpectedKind(child, expectedKinds, "selectedChildAt()");
1510
+ },
1511
+ isChildSelected: async (index) => {
1512
+ assertNonNegativeIndex$1("index", index);
1513
+ return native.nativeIsChildSelected(handle, index);
1514
+ },
1515
+ selectChildAt: async (index) => {
1516
+ assertNonNegativeIndex$1("index", index);
1517
+ native.nativeSelectChildAt(handle, index);
1518
+ },
1519
+ deselectChildAt: async (index) => {
1520
+ assertNonNegativeIndex$1("index", index);
1521
+ native.nativeDeselectChildAt(handle, index);
1522
+ },
1523
+ selectAllChildren: async () => {
1524
+ native.nativeSelectAllChildren(handle);
1525
+ },
1526
+ clearSelection: async () => {
1527
+ native.nativeClearSelection(handle);
1528
+ }
1529
+ });
1530
+ const createOperationFailedForOutOfRangeIndex = (index) => errors.createGtkOperationFailedError(`Child index is out of range: ${index}`);
1531
+ const isUnsupportedInterfaceError = (error) => error.code === "UNSUPPORTED_INTERFACE";
1532
+ const visitNativeDescendants = (handle, visitor, maxNodes) => {
1533
+ const queue = [];
1534
+ const childCount = native.nativeChildCount(handle);
1535
+ for (let index = 0; index < childCount; index += 1) {
1536
+ const childHandle = native.nativeChildAt(handle, index);
1537
+ if (childHandle !== void 0) {
1538
+ queue.push(childHandle);
1539
+ }
1540
+ }
1541
+ let visitedNodes = 0;
1542
+ while (queue.length > 0 && visitedNodes < maxNodes) {
1543
+ const currentHandle = queue.shift();
1544
+ visitedNodes += 1;
1545
+ if (visitor(currentHandle)) {
1546
+ return;
1547
+ }
1548
+ const currentChildCount = native.nativeChildCount(currentHandle);
1549
+ for (let index = 0; index < currentChildCount; index += 1) {
1550
+ const childHandle = native.nativeChildAt(currentHandle, index);
1551
+ if (childHandle !== void 0) {
1552
+ queue.push(childHandle);
1553
+ }
1554
+ }
1555
+ }
1556
+ };
1557
+ const findFirstDescendantName = (handle) => {
1558
+ let foundName;
1559
+ visitNativeDescendants(
1560
+ handle,
1561
+ (descendantHandle) => {
1562
+ const name = native.nativeElementInfo(descendantHandle).name;
1563
+ if (name.length === 0) {
1564
+ return false;
1565
+ }
1566
+ foundName = name;
1567
+ return true;
1568
+ },
1569
+ 64
1570
+ );
1571
+ return foundName;
1572
+ };
1573
+ const directChildKindAt = (handle, index) => {
1574
+ const childHandle = native.nativeChildAt(handle, index);
1575
+ return childHandle === void 0 ? void 0 : nativeInfoKind(childHandle);
1576
+ };
1577
+ const handleContainsComboItems = (handle) => {
1578
+ const childCount = native.nativeChildCount(handle);
1579
+ if (childCount === 0) {
1580
+ return false;
1581
+ }
1582
+ const firstChildKind = directChildKindAt(handle, 0);
1583
+ return firstChildKind === "listItem" || firstChildKind === "menuItem";
1584
+ };
1585
+ const findComboBoxItemContainerHandle = (handle) => {
1586
+ if (handleContainsComboItems(handle)) {
1587
+ return handle;
1588
+ }
1589
+ let foundHandle;
1590
+ visitNativeDescendants(
1591
+ handle,
1592
+ (descendantHandle) => {
1593
+ const kind = nativeInfoKind(descendantHandle);
1594
+ if ((kind === "list" || kind === "menu") && handleContainsComboItems(descendantHandle)) {
1595
+ foundHandle = descendantHandle;
1596
+ return true;
1597
+ }
1598
+ return false;
1599
+ },
1600
+ 128
1601
+ );
1602
+ return foundHandle;
1603
+ };
1604
+ const resolveComboBoxItemContainerHandle = (handle) => {
1605
+ const descendantItemContainer = findComboBoxItemContainerHandle(handle);
1606
+ if (descendantItemContainer !== void 0) {
1607
+ return descendantItemContainer;
1608
+ }
1609
+ const childCount = native.nativeChildCount(handle);
1610
+ if (childCount !== 1) {
1611
+ return handle;
1612
+ }
1613
+ const childHandle = native.nativeChildAt(handle, 0);
1614
+ if (childHandle === void 0) {
1615
+ return handle;
1616
+ }
1617
+ const childKind = nativeInfoKind(childHandle);
1618
+ return childKind === "menu" || childKind === "list" ? childHandle : handle;
1619
+ };
1620
+ const comboBoxClickTargetHandle = (handle) => {
1621
+ const childHandle = native.nativeChildAt(handle, 0);
1622
+ if (childHandle === void 0) {
1623
+ return handle;
1624
+ }
1625
+ const childKind = nativeInfoKind(childHandle);
1626
+ return childKind === "button" || childKind === "toggleButton" ? childHandle : handle;
1627
+ };
1628
+ const overrideComboBoxItemInfo = (handle) => {
1629
+ const info = native.nativeElementInfo(handle);
1630
+ if (info.name.length > 0) {
1631
+ return handle;
1632
+ }
1633
+ const descendantName = findFirstDescendantName(handle);
1634
+ return descendantName === void 0 ? handle : overrideElementInfo(handle, { name: descendantName });
1635
+ };
1636
+ const itemIdentityMatches = (candidate, selected) => {
1637
+ if (candidate.accessibleId.length > 0 && selected.accessibleId.length > 0) {
1638
+ return candidate.accessibleId === selected.accessibleId;
1639
+ }
1640
+ return candidate.kind === selected.kind && candidate.roleName === selected.roleName && candidate.name === selected.name && candidate.description === selected.description;
1641
+ };
1642
+ const comboBoxItemLookupAt = (handle, index) => {
1643
+ const itemContainerHandle = resolveComboBoxItemContainerHandle(handle);
1644
+ const itemHandle = native.nativeChildAt(itemContainerHandle, index);
1645
+ if (itemHandle === void 0) {
1646
+ throw createOperationFailedForOutOfRangeIndex(index);
1647
+ }
1648
+ const itemKind = nativeInfoKind(itemHandle);
1649
+ if (itemKind !== "listItem" && itemKind !== "menuItem") {
1650
+ throw errors.createGtkUnsupportedInterfaceError(
1651
+ `ComboBox childAt() returned ${itemKind}, expected listItem, menuItem.`
1652
+ );
1653
+ }
1654
+ return {
1655
+ itemContainerHandle,
1656
+ itemHandle: overrideComboBoxItemInfo(itemHandle)
1657
+ };
1658
+ };
1659
+ const comboBoxItemHandleAt = (handle, index) => comboBoxItemLookupAt(handle, index).itemHandle;
1660
+ const createComboBoxOperations = (handle) => ({
1661
+ childAt: async (index) => {
1662
+ assertNonNegativeIndex$1("index", index);
1663
+ const itemContainerHandle = resolveComboBoxItemContainerHandle(handle);
1664
+ const itemHandle = native.nativeChildAt(itemContainerHandle, index);
1665
+ if (itemHandle === void 0) {
1666
+ return void 0;
1667
+ }
1668
+ overrideComboBoxItemInfo(itemHandle);
1669
+ return assertExpectedKind(
1670
+ createGtkElement(itemHandle),
1671
+ ["listItem", "menuItem"],
1672
+ "childAt()"
1673
+ );
1674
+ },
1675
+ getChildCount: async () => native.nativeChildCount(resolveComboBoxItemContainerHandle(handle)),
1676
+ getSelectedChildCount: async () => native.nativeSelectedChildCount(handle),
1677
+ selectedChildAt: async (selectedIndex) => {
1678
+ assertNonNegativeIndex$1("selectedIndex", selectedIndex);
1679
+ const childHandle = native.nativeSelectedChildAt(handle, selectedIndex);
1680
+ if (childHandle === void 0) {
1681
+ return void 0;
1682
+ }
1683
+ return assertExpectedKind(
1684
+ createGtkElement(childHandle),
1685
+ ["listItem", "menuItem"],
1686
+ "selectedChildAt()"
1687
+ );
1688
+ },
1689
+ isChildSelected: async (index) => {
1690
+ assertNonNegativeIndex$1("index", index);
1691
+ const itemHandle = comboBoxItemHandleAt(handle, index);
1692
+ const itemInfo = toGtkElementInfoForHandle(itemHandle);
1693
+ const selectedCount = native.nativeSelectedChildCount(handle);
1694
+ for (let selectedIndex = 0; selectedIndex < selectedCount; selectedIndex += 1) {
1695
+ const selectedHandle = native.nativeSelectedChildAt(handle, selectedIndex);
1696
+ if (selectedHandle === void 0) {
1697
+ continue;
1698
+ }
1699
+ const selectedInfo = toGtkElementInfo(native.nativeElementInfo(selectedHandle));
1700
+ if (itemIdentityMatches(itemInfo, selectedInfo)) {
1701
+ return true;
1702
+ }
1703
+ }
1704
+ return false;
1705
+ },
1706
+ selectChildAt: async (index) => {
1707
+ assertNonNegativeIndex$1("index", index);
1708
+ const { itemContainerHandle, itemHandle } = comboBoxItemLookupAt(
1709
+ handle,
1710
+ index
1711
+ );
1712
+ const expectedName = toGtkElementInfoForHandle(itemHandle).name;
1713
+ native.nativeClick(itemHandle);
1714
+ if (itemContainerHandle !== handle && expectedName.length > 0 && native.nativeElementInfo(handle).name !== expectedName) {
1715
+ throw errors.createGtkOperationFailedError(
1716
+ "ComboBox child selection did not change the selected item."
1717
+ );
1718
+ }
1719
+ },
1720
+ deselectChildAt: async (index) => {
1721
+ assertNonNegativeIndex$1("index", index);
1722
+ comboBoxItemHandleAt(handle, index);
1723
+ native.nativeDeselectChildAt(handle, 0);
1724
+ },
1725
+ selectAllChildren: async () => {
1726
+ native.nativeSelectAllChildren(handle);
1727
+ },
1728
+ clearSelection: async () => {
1729
+ const selectedBefore = native.nativeSelectedChildCount(handle);
1730
+ native.nativeClearSelection(handle);
1731
+ if (selectedBefore > 0 && native.nativeSelectedChildCount(handle) > 0) {
1732
+ throw errors.createGtkOperationFailedError(
1733
+ "ComboBox selection could not be cleared."
1734
+ );
1735
+ }
1736
+ }
1737
+ });
1738
+ const createSetTextOperation = (handle) => async (text) => {
1739
+ native.nativeSetText(handle, text);
1740
+ };
1741
+ const createClickOperation = (handle) => async () => {
1742
+ native.nativeClick(handle);
1743
+ };
1744
+ const createComboBoxClickOperation = (handle) => async () => {
1745
+ native.nativeClick(comboBoxClickTargetHandle(handle));
1746
+ };
1747
+ const createTextOperation = (handle) => async () => native.nativeText(handle);
1748
+ const createIsCheckedOperation = (handle) => async () => {
1749
+ const info = native.nativeElementInfo(handle);
1750
+ return hasState(info, "checked") || hasState(info, "pressed");
1751
+ };
1752
+ const createToggleOperation = (handle) => async () => {
1753
+ native.nativeClick(handle);
1754
+ };
1755
+ const createValueInfoOperation = (handle) => async () => native.nativeValueInfo(handle);
1756
+ const createImageInfoOperation = (handle) => async () => {
1757
+ const info = native.nativeImageInfo(handle);
1758
+ return {
1759
+ ...info,
1760
+ capture: async () => native.nativeCaptureBounds(info.bounds)
1761
+ };
1762
+ };
1763
+ const createValueOperation = (handle) => async () => native.nativeValueInfo(handle).value;
1764
+ const createSetValueOperation = (handle) => async (value) => {
1765
+ assertFiniteNumber("value", value);
1766
+ native.nativeSetValue(handle, value);
1767
+ };
1768
+ const assertUsableValueMetadata = (info) => {
1769
+ if (!Number.isFinite(info.value) || !Number.isFinite(info.minimum) || !Number.isFinite(info.maximum) || !Number.isFinite(info.minimumIncrement)) {
1770
+ throw errors.createGtkOperationFailedError(
1771
+ "Accessible value metadata does not contain usable numeric values."
1772
+ );
1773
+ }
1774
+ };
1775
+ const valueStep = (info) => {
1776
+ assertUsableValueMetadata(info);
1777
+ if (info.minimumIncrement > 0) {
1778
+ return info.minimumIncrement;
1779
+ }
1780
+ const range = info.maximum - info.minimum;
1781
+ if (!Number.isFinite(range) || range <= 0) {
1782
+ throw errors.createGtkOperationFailedError(
1783
+ "Accessible value metadata does not contain a usable increment."
1784
+ );
1785
+ }
1786
+ return Math.min(1, range);
1787
+ };
1788
+ const clampedValue = (value, minimum, maximum) => Math.min(Math.max(value, minimum), maximum);
1789
+ const createIncrementOperation = (handle) => async () => {
1790
+ const info = native.nativeValueInfo(handle);
1791
+ const step = valueStep(info);
1792
+ native.nativeSetValue(
1793
+ handle,
1794
+ clampedValue(info.value + step, info.minimum, info.maximum)
1795
+ );
1796
+ };
1797
+ const createDecrementOperation = (handle) => async () => {
1798
+ const info = native.nativeValueInfo(handle);
1799
+ const step = valueStep(info);
1800
+ native.nativeSetValue(
1801
+ handle,
1802
+ clampedValue(info.value - step, info.minimum, info.maximum)
1803
+ );
1804
+ };
1805
+ const collectDirectTableCellChildren = (rowHandle) => {
1806
+ const cells = [];
1807
+ const childCount = native.nativeChildCount(rowHandle);
1808
+ for (let index = 0; index < childCount; index += 1) {
1809
+ const childHandle = native.nativeChildAt(rowHandle, index);
1810
+ if (childHandle !== void 0 && nativeInfoKind(childHandle) === "tableCell") {
1811
+ cells.push(childHandle);
1812
+ }
1813
+ }
1814
+ return cells;
1815
+ };
1816
+ const collectFallbackTableRows = (handle) => {
1817
+ const rows = [];
1818
+ visitNativeDescendants(
1819
+ handle,
1820
+ (descendantHandle) => {
1821
+ const roleName = normalizeRoleName(
1822
+ native.nativeElementInfo(descendantHandle).roleName
1823
+ );
1824
+ if (roleName !== "table row") {
1825
+ return false;
1826
+ }
1827
+ const cells = collectDirectTableCellChildren(descendantHandle);
1828
+ if (cells.length > 0) {
1829
+ rows.push(
1830
+ cells.map(
1831
+ (cellHandle) => overrideElementInfo(cellHandle, { kind: "tableCell" })
1832
+ )
1833
+ );
1834
+ }
1835
+ return false;
1836
+ },
1837
+ 512
1838
+ );
1839
+ return rows;
1840
+ };
1841
+ const fallbackTableRowCount = (handle) => collectFallbackTableRows(handle).length;
1842
+ const fallbackTableColumnCount = (handle) => {
1843
+ const rows = collectFallbackTableRows(handle);
1844
+ return rows.reduce((max, row) => Math.max(max, row.length), 0);
1845
+ };
1846
+ const fallbackTableCellAt = (handle, row, column) => {
1847
+ const rows = collectFallbackTableRows(handle);
1848
+ return rows[row]?.[column];
1849
+ };
1850
+ const createTableCellAtOperation = (handle) => async (row, column) => {
1851
+ assertNonNegativeIndex$1("row", row);
1852
+ assertNonNegativeIndex$1("column", column);
1853
+ let cellHandle;
1854
+ try {
1855
+ cellHandle = native.nativeTableCellAt(handle, row, column);
1856
+ } catch (error) {
1857
+ if (!isUnsupportedInterfaceError(error)) {
1858
+ throw error;
1859
+ }
1860
+ cellHandle = fallbackTableCellAt(handle, row, column);
1861
+ }
1862
+ if (cellHandle === void 0) {
1863
+ return void 0;
1864
+ }
1865
+ overrideElementInfo(cellHandle, { kind: "tableCell" });
1866
+ return assertExpectedKind(
1867
+ createGtkElement(cellHandle),
1868
+ ["tableCell"],
1869
+ "cellAt()"
1870
+ );
1871
+ };
1872
+ const createTableOperations = (handle) => ({
1873
+ getRowCount: async () => {
1874
+ try {
1875
+ return native.nativeTableRowCount(handle);
1876
+ } catch (error) {
1877
+ if (!isUnsupportedInterfaceError(error)) {
1878
+ throw error;
1879
+ }
1880
+ return fallbackTableRowCount(handle);
1881
+ }
1882
+ },
1883
+ getColumnCount: async () => {
1884
+ try {
1885
+ return native.nativeTableColumnCount(handle);
1886
+ } catch (error) {
1887
+ if (!isUnsupportedInterfaceError(error)) {
1888
+ throw error;
1889
+ }
1890
+ return fallbackTableColumnCount(handle);
1891
+ }
1892
+ },
1893
+ cellAt: createTableCellAtOperation(handle),
1894
+ selectedRows: async () => native.nativeTableSelectedRows(handle),
1895
+ selectedColumns: async () => native.nativeTableSelectedColumns(handle),
1896
+ isRowSelected: async (row) => {
1897
+ assertNonNegativeIndex$1("row", row);
1898
+ return native.nativeTableIsRowSelected(handle, row);
1899
+ },
1900
+ isColumnSelected: async (column) => {
1901
+ assertNonNegativeIndex$1("column", column);
1902
+ return native.nativeTableIsColumnSelected(handle, column);
1903
+ },
1904
+ isCellSelected: async (row, column) => {
1905
+ assertNonNegativeIndex$1("row", row);
1906
+ assertNonNegativeIndex$1("column", column);
1907
+ return native.nativeTableIsCellSelected(handle, row, column);
1908
+ },
1909
+ selectRow: async (row) => {
1910
+ assertNonNegativeIndex$1("row", row);
1911
+ native.nativeTableSelectRow(handle, row);
1912
+ },
1913
+ deselectRow: async (row) => {
1914
+ assertNonNegativeIndex$1("row", row);
1915
+ native.nativeTableDeselectRow(handle, row);
1916
+ },
1917
+ selectColumn: async (column) => {
1918
+ assertNonNegativeIndex$1("column", column);
1919
+ native.nativeTableSelectColumn(handle, column);
1920
+ },
1921
+ deselectColumn: async (column) => {
1922
+ assertNonNegativeIndex$1("column", column);
1923
+ native.nativeTableDeselectColumn(handle, column);
1924
+ }
1925
+ });
1926
+ const createGtkElement = (handle) => {
1927
+ const initialInfo = toGtkElementInfoForHandle(handle);
1928
+ const common = createCommonElement(handle);
1929
+ switch (initialInfo.kind) {
1930
+ case "window":
1931
+ return {
1932
+ ...common,
1933
+ kind: "window",
1934
+ ...createChildContainerOperations(handle, void 0)
1935
+ };
1936
+ case "button":
1937
+ return { ...common, kind: "button", click: createClickOperation(handle) };
1938
+ case "container":
1939
+ return {
1940
+ ...common,
1941
+ kind: "container",
1942
+ ...createChildContainerOperations(handle, void 0)
1943
+ };
1944
+ case "label":
1945
+ return { ...common, kind: "label", text: createTextOperation(handle) };
1946
+ case "entry":
1947
+ return {
1948
+ ...common,
1949
+ kind: "entry",
1950
+ setText: createSetTextOperation(handle),
1951
+ text: createTextOperation(handle)
1952
+ };
1953
+ case "text":
1954
+ return { ...common, kind: "text", text: createTextOperation(handle) };
1955
+ case "checkbox":
1956
+ return {
1957
+ ...common,
1958
+ kind: "checkbox",
1959
+ click: createClickOperation(handle),
1960
+ isChecked: createIsCheckedOperation(handle),
1961
+ toggle: createToggleOperation(handle)
1962
+ };
1963
+ case "switch":
1964
+ return {
1965
+ ...common,
1966
+ kind: "switch",
1967
+ click: createClickOperation(handle),
1968
+ isChecked: createIsCheckedOperation(handle),
1969
+ toggle: createToggleOperation(handle)
1970
+ };
1971
+ case "radio":
1972
+ return {
1973
+ ...common,
1974
+ kind: "radio",
1975
+ click: createClickOperation(handle),
1976
+ isChecked: createIsCheckedOperation(handle),
1977
+ toggle: createToggleOperation(handle)
1978
+ };
1979
+ case "toggleButton":
1980
+ return {
1981
+ ...common,
1982
+ kind: "toggleButton",
1983
+ click: createClickOperation(handle),
1984
+ isChecked: createIsCheckedOperation(handle),
1985
+ toggle: createToggleOperation(handle)
1986
+ };
1987
+ case "slider":
1988
+ return {
1989
+ ...common,
1990
+ kind: "slider",
1991
+ value: createValueOperation(handle),
1992
+ valueInfo: createValueInfoOperation(handle),
1993
+ setValue: createSetValueOperation(handle)
1994
+ };
1995
+ case "spinButton":
1996
+ return {
1997
+ ...common,
1998
+ kind: "spinButton",
1999
+ value: createValueOperation(handle),
2000
+ valueInfo: createValueInfoOperation(handle),
2001
+ setValue: createSetValueOperation(handle),
2002
+ increment: createIncrementOperation(handle),
2003
+ decrement: createDecrementOperation(handle)
2004
+ };
2005
+ case "progressBar":
2006
+ return {
2007
+ ...common,
2008
+ kind: "progressBar",
2009
+ value: createValueOperation(handle),
2010
+ valueInfo: createValueInfoOperation(handle)
2011
+ };
2012
+ case "comboBox":
2013
+ return {
2014
+ ...common,
2015
+ kind: "comboBox",
2016
+ click: createComboBoxClickOperation(handle),
2017
+ ...createComboBoxOperations(handle)
2018
+ };
2019
+ case "list":
2020
+ return {
2021
+ ...common,
2022
+ kind: "list",
2023
+ ...createSelectableChildContainerOperations(
2024
+ handle,
2025
+ ["listItem"]
2026
+ )
2027
+ };
2028
+ case "listItem":
2029
+ return {
2030
+ ...common,
2031
+ kind: "listItem",
2032
+ click: createClickOperation(handle)
2033
+ };
2034
+ case "table":
2035
+ return { ...common, kind: "table", ...createTableOperations(handle) };
2036
+ case "tableCell":
2037
+ return { ...common, kind: "tableCell" };
2038
+ case "image":
2039
+ return {
2040
+ ...common,
2041
+ kind: "image",
2042
+ imageInfo: createImageInfoOperation(handle)
2043
+ };
2044
+ case "menu":
2045
+ return {
2046
+ ...common,
2047
+ kind: "menu",
2048
+ ...createChildContainerOperations(handle, [
2049
+ "menuItem"
2050
+ ])
2051
+ };
2052
+ case "menuItem":
2053
+ return {
2054
+ ...common,
2055
+ kind: "menuItem",
2056
+ click: createClickOperation(handle)
2057
+ };
2058
+ case "unknown":
2059
+ return { ...common, kind: "unknown" };
2060
+ }
2061
+ };
2062
+ const optionalString = (value) => value.length === 0 ? void 0 : value;
2063
+ const toMetadata = (item) => {
2064
+ const iconName = optionalString(item.iconName);
2065
+ const id = optionalString(item.id);
2066
+ const status = optionalString(item.status);
2067
+ const title = optionalString(item.title);
2068
+ return {
2069
+ backend: "status-notifier",
2070
+ ...iconName === void 0 ? {} : { iconName },
2071
+ ...id === void 0 ? {} : { id },
2072
+ ...status === void 0 ? {} : { status },
2073
+ ...title === void 0 ? {} : { title }
2074
+ };
2075
+ };
2076
+ const sameTrayItem = (first, second) => first.busName === second.busName && first.objectPath === second.objectPath;
2077
+ const staleTrayItemError = () => errors.createGtkStaleElementError("Tray item is no longer registered.");
2078
+ const resolveCurrentInfo = (handle) => {
2079
+ const item = native.nativeTrayItems(handle.processId).find(
2080
+ (candidate) => sameTrayItem(handle, candidate)
2081
+ );
2082
+ if (item === void 0) {
2083
+ throw staleTrayItemError();
2084
+ }
2085
+ return item;
2086
+ };
2087
+ const isClickableElement = (element) => {
2088
+ switch (element.kind) {
2089
+ case "button":
2090
+ case "checkbox":
2091
+ case "switch":
2092
+ case "radio":
2093
+ case "toggleButton":
2094
+ case "comboBox":
2095
+ case "listItem":
2096
+ case "menuItem":
2097
+ return true;
2098
+ default:
2099
+ return false;
2100
+ }
2101
+ };
2102
+ const nativeTrayItemMatchesSelector = (item, selector) => {
2103
+ if ("id" in selector) {
2104
+ return item.id === selector.id;
2105
+ }
2106
+ if ("title" in selector) {
2107
+ return item.title === selector.title;
2108
+ }
2109
+ if ("busName" in selector) {
2110
+ return item.busName === selector.busName && (selector.objectPath === void 0 || item.objectPath === selector.objectPath);
2111
+ }
2112
+ return false;
2113
+ };
2114
+ const createGtkTrayItem = (processId, item) => {
2115
+ const handle = {
2116
+ busName: item.busName,
2117
+ objectPath: item.objectPath,
2118
+ processId
2119
+ };
2120
+ const trayItem = {
2121
+ metadata: async () => toMetadata(resolveCurrentInfo(handle)),
2122
+ element: async () => {
2123
+ const current = resolveCurrentInfo(handle);
2124
+ const elementHandle = native.nativeFindAnyById(current.accessibleId);
2125
+ return elementHandle === void 0 ? void 0 : createGtkElement(elementHandle);
2126
+ },
2127
+ capture: async () => {
2128
+ const element = await trayItem.element();
2129
+ if (element === void 0) {
2130
+ throw errors.createGtkElementNotFoundError(
2131
+ "Tray item is registered but not visible."
2132
+ );
2133
+ }
2134
+ return element.capture();
2135
+ },
2136
+ click: async () => {
2137
+ const element = await trayItem.element();
2138
+ if (element === void 0) {
2139
+ throw errors.createGtkElementNotFoundError(
2140
+ "Tray item is registered but not visible."
2141
+ );
2142
+ }
2143
+ if (!isClickableElement(element)) {
2144
+ throw errors.createGtkUnsupportedInterfaceError(
2145
+ `Tray item element does not support click: ${element.kind}.`
2146
+ );
2147
+ }
2148
+ await element.click();
2149
+ },
2150
+ openMenu: async () => {
2151
+ resolveCurrentInfo(handle);
2152
+ return void 0;
2153
+ }
2154
+ };
2155
+ return trayItem;
2156
+ };
2157
+ const appendOutput = (lines, chunk) => {
2158
+ lines.push(chunk.toString("utf8"));
2159
+ if (lines.length > 40) {
2160
+ lines.splice(0, lines.length - 40);
2161
+ }
2162
+ };
2163
+ const formatProcessOutput = (state) => {
2164
+ const stdout = state.stdout.join("").trim();
2165
+ const stderr = state.stderr.join("").trim();
2166
+ if (stdout.length === 0 && stderr.length === 0) {
2167
+ return "";
2168
+ }
2169
+ return `
2170
+ stdout:
2171
+ ${stdout}
2172
+ stderr:
2173
+ ${stderr}`;
2174
+ };
2175
+ const assertProcessRunning = (state, command) => {
2176
+ if (state.exitCode !== null || state.exitSignal !== null) {
2177
+ throw errors.createGtkAppExitedError(
2178
+ `GTK application exited before the operation completed: ${command} (code=${String(state.exitCode)}, signal=${String(state.exitSignal)})` + formatProcessOutput(state)
2179
+ );
2180
+ }
2181
+ const processId = state.process.pid;
2182
+ if (processId === void 0) {
2183
+ throw errors.createGtkAppExitedError(
2184
+ `GTK application did not expose a process id: ${command}`
2185
+ );
2186
+ }
2187
+ return processId;
2188
+ };
2189
+ const assertNonNegativeIndex = (name, index) => {
2190
+ if (!Number.isInteger(index) || index < 0) {
2191
+ throw errors.createGtkInvalidArgumentError(
2192
+ `${name} must be a non-negative integer.`
2193
+ );
2194
+ }
2195
+ };
2196
+ const parseElementPath = (path) => {
2197
+ const segments = path.split(/[.:;,]/u);
2198
+ const id = segments[0];
2199
+ if (id === void 0 || id.length === 0) {
2200
+ throw errors.createGtkInvalidArgumentError(
2201
+ "path must start with an accessible id."
2202
+ );
2203
+ }
2204
+ const childIndexes = [];
2205
+ for (let index = 1; index < segments.length; index += 1) {
2206
+ const segment = segments[index];
2207
+ if (segment === void 0 || !/^\d+$/u.test(segment)) {
2208
+ throw errors.createGtkInvalidArgumentError(
2209
+ "path child indexes must be non-negative decimal integers."
2210
+ );
2211
+ }
2212
+ const childIndex = Number(segment);
2213
+ if (!Number.isSafeInteger(childIndex)) {
2214
+ throw errors.createGtkInvalidArgumentError(
2215
+ "path child indexes must be safe non-negative integers."
2216
+ );
2217
+ }
2218
+ childIndexes.push(childIndex);
2219
+ }
2220
+ return { id, childIndexes };
2221
+ };
2222
+ const isPathChildContainer = (element) => "childAt" in element;
2223
+ const waitForAtspiReady = async (state, command, timeoutMs, startedAt) => {
2224
+ if (state.atspiReady) {
2225
+ return assertProcessRunning(state, command);
2226
+ }
2227
+ while (Date.now() - startedAt <= timeoutMs) {
2228
+ const processId = assertProcessRunning(state, command);
2229
+ state.atspiReadiness = native.nativeProcessAtspiReadiness(processId);
2230
+ if (state.atspiReadiness === "ready") {
2231
+ state.atspiReady = true;
2232
+ return processId;
2233
+ }
2234
+ await wait.delay(50);
2235
+ }
2236
+ assertProcessRunning(state, command);
2237
+ throw errors.createGtkOperationFailedError(
2238
+ prerequisites.appendPrerequisiteInstallHint(
2239
+ `AT-SPI did not become ready for GTK application: ${command} (last readiness: ${state.atspiReadiness})` + formatProcessOutput(state)
2240
+ )
2241
+ );
2242
+ };
2243
+ const createGtkAppEnvironment = (baseEnv, overrides) => {
2244
+ const env = {
2245
+ ...baseEnv,
2246
+ GDK_BACKEND: baseEnv.GDK_BACKEND ?? "x11",
2247
+ GSETTINGS_BACKEND: baseEnv.GSETTINGS_BACKEND ?? "memory",
2248
+ GTK_THEME: baseEnv.GTK_THEME ?? "Adwaita",
2249
+ ...overrides
2250
+ };
2251
+ delete env.NO_AT_BRIDGE;
2252
+ for (const key of Object.keys(env)) {
2253
+ if (env[key] === void 0) {
2254
+ delete env[key];
2255
+ }
2256
+ }
2257
+ return env;
2258
+ };
2259
+ const launchGtkApp = (appPath, args, options) => {
2260
+ const _args = args ?? [];
2261
+ const _timeoutMs = options?.timeoutMs ?? 1e4;
2262
+ const child = node_child_process.spawn(appPath, [..._args], {
2263
+ env: createGtkAppEnvironment(process.env, options?.env),
2264
+ stdio: "pipe"
2265
+ });
2266
+ const state = {
2267
+ atspiReadiness: "missing-bus-name",
2268
+ atspiReady: false,
2269
+ exitCode: null,
2270
+ exitSignal: null,
2271
+ process: child,
2272
+ stderr: [],
2273
+ stdout: []
2274
+ };
2275
+ child.stdout.on("data", (chunk) => {
2276
+ appendOutput(state.stdout, chunk);
2277
+ });
2278
+ child.stderr.on("data", (chunk) => {
2279
+ appendOutput(state.stderr, chunk);
2280
+ });
2281
+ child.on("exit", (code, signal) => {
2282
+ state.exitCode = code;
2283
+ state.exitSignal = signal;
2284
+ });
2285
+ const release = async () => {
2286
+ if (state.exitCode !== null || state.exitSignal !== null) {
2287
+ return;
2288
+ }
2289
+ child.kill("SIGTERM");
2290
+ const startedAt = Date.now();
2291
+ while (state.exitCode === null && state.exitSignal === null) {
2292
+ if (Date.now() - startedAt > 2e3) {
2293
+ child.kill("SIGKILL");
2294
+ break;
2295
+ }
2296
+ await wait.delay(25);
2297
+ }
2298
+ };
2299
+ const findById = async (id) => {
2300
+ const startedAt = Date.now();
2301
+ const timeoutMs = wait.effectiveWaitTimeoutMs(_timeoutMs);
2302
+ await waitForAtspiReady(state, appPath, timeoutMs, startedAt);
2303
+ while (Date.now() - startedAt <= timeoutMs) {
2304
+ const processId = assertProcessRunning(state, appPath);
2305
+ try {
2306
+ const handle = native.nativeFindById(processId, id);
2307
+ if (handle !== void 0) {
2308
+ return createGtkElement(handle);
2309
+ }
2310
+ } catch (error) {
2311
+ throw errors.normalizeNativeError(error);
2312
+ }
2313
+ await wait.delay(50);
2314
+ }
2315
+ assertProcessRunning(state, appPath);
2316
+ return void 0;
2317
+ };
2318
+ const getById = async (id) => {
2319
+ const element = await findById(id);
2320
+ if (element === void 0) {
2321
+ throw errors.createGtkElementNotFoundError(`Accessible id was not found: ${id}`);
2322
+ }
2323
+ return element;
2324
+ };
2325
+ const findByPath = async (path) => {
2326
+ const parsedPath = parseElementPath(path);
2327
+ const startedAt = Date.now();
2328
+ const timeoutMs = wait.effectiveWaitTimeoutMs(_timeoutMs);
2329
+ await waitForAtspiReady(state, appPath, timeoutMs, startedAt);
2330
+ while (Date.now() - startedAt <= timeoutMs) {
2331
+ const processId = assertProcessRunning(state, appPath);
2332
+ try {
2333
+ const handle = native.nativeFindById(processId, parsedPath.id);
2334
+ if (handle !== void 0) {
2335
+ let element = createGtkElement(handle);
2336
+ let resolved = true;
2337
+ for (const childIndex of parsedPath.childIndexes) {
2338
+ if (!isPathChildContainer(element)) {
2339
+ resolved = false;
2340
+ break;
2341
+ }
2342
+ const child2 = await element.childAt(childIndex);
2343
+ if (child2 === void 0) {
2344
+ resolved = false;
2345
+ break;
2346
+ }
2347
+ element = child2;
2348
+ }
2349
+ if (resolved) {
2350
+ return element;
2351
+ }
2352
+ }
2353
+ } catch (error) {
2354
+ throw errors.normalizeNativeError(error);
2355
+ }
2356
+ await wait.delay(50);
2357
+ }
2358
+ assertProcessRunning(state, appPath);
2359
+ return void 0;
2360
+ };
2361
+ const getByPath = async (path) => {
2362
+ const element = await findByPath(path);
2363
+ if (element === void 0) {
2364
+ throw errors.createGtkElementNotFoundError(
2365
+ `Element path was not found: ${path}`
2366
+ );
2367
+ }
2368
+ return element;
2369
+ };
2370
+ const findTrayItem = async (selector) => {
2371
+ const startedAt = Date.now();
2372
+ const timeoutMs = wait.effectiveWaitTimeoutMs(_timeoutMs);
2373
+ while (Date.now() - startedAt <= timeoutMs) {
2374
+ const processId = assertProcessRunning(state, appPath);
2375
+ try {
2376
+ const item = native.nativeTrayItems(processId).find(
2377
+ (candidate) => nativeTrayItemMatchesSelector(candidate, selector)
2378
+ );
2379
+ if (item !== void 0) {
2380
+ return createGtkTrayItem(processId, item);
2381
+ }
2382
+ } catch (error) {
2383
+ const normalizedError = errors.normalizeNativeError(error);
2384
+ if (normalizedError.code === "OPERATION_FAILED" && normalizedError.message.includes("Timeout was reached")) {
2385
+ await wait.delay(50);
2386
+ continue;
2387
+ }
2388
+ throw normalizedError;
2389
+ }
2390
+ await wait.delay(50);
2391
+ }
2392
+ assertProcessRunning(state, appPath);
2393
+ return void 0;
2394
+ };
2395
+ const getTrayItem = async (selector) => {
2396
+ const trayItem = await findTrayItem(selector);
2397
+ if (trayItem === void 0) {
2398
+ throw errors.createGtkElementNotFoundError(
2399
+ `StatusNotifier tray item was not found: ${JSON.stringify(selector)}`
2400
+ );
2401
+ }
2402
+ return trayItem;
2403
+ };
2404
+ const app = {
2405
+ capture: async () => {
2406
+ assertProcessRunning(state, appPath);
2407
+ try {
2408
+ return native.nativeCaptureScreen();
2409
+ } catch (error) {
2410
+ throw errors.normalizeNativeError(error);
2411
+ }
2412
+ },
2413
+ findById,
2414
+ findByPath,
2415
+ getById,
2416
+ getByPath,
2417
+ windowAt: async (index) => {
2418
+ assertNonNegativeIndex("index", index);
2419
+ const startedAt = Date.now();
2420
+ const processId = await waitForAtspiReady(
2421
+ state,
2422
+ appPath,
2423
+ wait.effectiveWaitTimeoutMs(_timeoutMs),
2424
+ startedAt
2425
+ );
2426
+ try {
2427
+ const handle = native.nativeWindowAt(processId, index);
2428
+ return handle === void 0 ? void 0 : createGtkElement(handle);
2429
+ } catch (error) {
2430
+ throw errors.normalizeNativeError(error);
2431
+ }
2432
+ },
2433
+ getWindowCount: async () => {
2434
+ const startedAt = Date.now();
2435
+ const processId = await waitForAtspiReady(
2436
+ state,
2437
+ appPath,
2438
+ wait.effectiveWaitTimeoutMs(_timeoutMs),
2439
+ startedAt
2440
+ );
2441
+ try {
2442
+ return native.nativeWindowCount(processId);
2443
+ } catch (error) {
2444
+ throw errors.normalizeNativeError(error);
2445
+ }
2446
+ },
2447
+ findTrayItem,
2448
+ getTrayItem,
2449
+ trayItemAt: async (index) => {
2450
+ assertNonNegativeIndex("index", index);
2451
+ const processId = assertProcessRunning(state, appPath);
2452
+ try {
2453
+ const item = native.nativeTrayItems(processId)[index];
2454
+ return item === void 0 ? void 0 : createGtkTrayItem(processId, item);
2455
+ } catch (error) {
2456
+ throw errors.normalizeNativeError(error);
2457
+ }
2458
+ },
2459
+ getTrayItemCount: async () => {
2460
+ const processId = assertProcessRunning(state, appPath);
2461
+ try {
2462
+ return native.nativeTrayItems(processId).length;
2463
+ } catch (error) {
2464
+ throw errors.normalizeNativeError(error);
2465
+ }
2466
+ },
2467
+ release,
2468
+ [Symbol.asyncDispose]: release
2469
+ };
2470
+ child.on("error", (error) => {
2471
+ appendOutput(state.stderr, Buffer.from(error.message));
2472
+ });
2473
+ return Promise.resolve(app);
2474
+ };
2475
+ const createGtkAppLauncher = (options) => createDriverBackedGtkAppLauncher(options);
2476
+ exports.createGtkAppEnvironment = createGtkAppEnvironment;
2477
+ exports.createGtkAppLauncher = createGtkAppLauncher;
2478
+ exports.launchGtkApp = launchGtkApp;
2479
+ //# sourceMappingURL=launchGtkApp-BIO_5Xjn.cjs.map