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