gestament 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -5
- package/dist/displaySession.d.ts +2 -2
- package/dist/displaySession.d.ts.map +1 -1
- package/dist/element.d.ts +2 -2
- package/dist/element.d.ts.map +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/generated/packageMetadata.d.ts +4 -4
- package/dist/gestament-config.d.ts +2 -2
- package/dist/gestament-launcher-driver.cjs +118 -5
- package/dist/gestament-launcher-driver.cjs.map +1 -1
- package/dist/gestament-launcher-driver.d.ts +2 -2
- package/dist/gestament-launcher-driver.mjs +118 -5
- package/dist/gestament-launcher-driver.mjs.map +1 -1
- package/dist/gestament-tray-host.cjs +9 -1
- package/dist/gestament-tray-host.cjs.map +1 -1
- package/dist/gestament-tray-host.d.ts +2 -2
- package/dist/gestament-tray-host.mjs +9 -1
- package/dist/gestament-tray-host.mjs.map +1 -1
- package/dist/gestament-xvfb-pool-probe.cjs +1 -1
- package/dist/gestament-xvfb-pool-probe.d.ts +2 -2
- package/dist/gestament-xvfb-pool-probe.mjs +1 -1
- package/dist/gestament-xvfb-worker.d.ts +2 -2
- package/dist/gestament-xvfb.cjs +7 -2
- package/dist/gestament-xvfb.cjs.map +1 -1
- package/dist/gestament-xvfb.d.ts +2 -2
- package/dist/gestament-xvfb.mjs +7 -2
- package/dist/gestament-xvfb.mjs.map +1 -1
- package/dist/gestament.cjs +279 -0
- package/dist/gestament.cjs.map +1 -0
- package/dist/gestament.d.ts +29 -0
- package/dist/gestament.d.ts.map +1 -0
- package/dist/gestament.mjs +278 -0
- package/dist/gestament.mjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{launchGtkApp-BIO_5Xjn.cjs → launchGtkApp-CzYcrc9f.cjs} +794 -89
- package/dist/launchGtkApp-CzYcrc9f.cjs.map +1 -0
- package/dist/{launchGtkApp-qi1qm5G4.js → launchGtkApp-EI6OIpPI.js} +792 -87
- package/dist/launchGtkApp-EI6OIpPI.js.map +1 -0
- package/dist/launchGtkApp.d.ts +2 -2
- package/dist/launchGtkApp.d.ts.map +1 -1
- package/dist/launcherDriverProtocol.d.ts +29 -4
- package/dist/launcherDriverProtocol.d.ts.map +1 -1
- package/dist/{native-CWdUmdty.js → native-DlCBBWlV.js} +16 -10
- package/dist/native-DlCBBWlV.js.map +1 -0
- package/dist/{native-CBXaFWP-.cjs → native-Kfm95Uxw.cjs} +12 -6
- package/dist/native-Kfm95Uxw.cjs.map +1 -0
- package/dist/native.d.ts +23 -2
- package/dist/native.d.ts.map +1 -1
- package/dist/output.d.ts +27 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/packageMetadata-CewXH0iF.cjs +4 -0
- package/dist/packageMetadata-CewXH0iF.cjs.map +1 -0
- package/dist/packageMetadata-DjxyJCJm.js +5 -0
- package/dist/packageMetadata-DjxyJCJm.js.map +1 -0
- package/dist/prerequisites.d.ts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/tray.d.ts +2 -2
- package/dist/types.d.ts +277 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/wait.d.ts +2 -2
- package/package.json +8 -7
- package/prebuilds/linux-arm/gtk3/node.napi.armv7.glibc.node +0 -0
- package/prebuilds/linux-arm/gtk4/node.napi.armv7.glibc.node +0 -0
- package/prebuilds/linux-arm64/gtk3/node.napi.glibc.node +0 -0
- package/prebuilds/linux-arm64/gtk4/node.napi.glibc.node +0 -0
- package/prebuilds/linux-ia32/gtk3/node.napi.glibc.node +0 -0
- package/prebuilds/linux-ia32/gtk4/node.napi.glibc.node +0 -0
- package/prebuilds/linux-riscv64/gtk3/node.napi.glibc.node +0 -0
- package/prebuilds/linux-riscv64/gtk4/node.napi.glibc.node +0 -0
- package/prebuilds/linux-x64/gtk3/node.napi.glibc.node +0 -0
- package/prebuilds/linux-x64/gtk4/node.napi.glibc.node +0 -0
- package/dist/launchGtkApp-BIO_5Xjn.cjs.map +0 -1
- package/dist/launchGtkApp-qi1qm5G4.js.map +0 -1
- package/dist/native-CBXaFWP-.cjs.map +0 -1
- package/dist/native-CWdUmdty.js.map +0 -1
|
@@ -7,7 +7,232 @@ import { tmpdir } from "node:os";
|
|
|
7
7
|
import { join, resolve, dirname } from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
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
|
|
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 nativeX11Info, G as nativeResizeHints, H as nativeBounds, I as nativeCapture, J as nativeTableCellAt, K as nativeFindAnyById, L as nativeTrayItems, M as nativeWindowCount, N as nativeWindowAt, a as nativeCaptureScreen, O as nativeFindById, P as nativeProcessAtspiReadiness } from "./native-DlCBBWlV.js";
|
|
11
|
+
const normalizeOutputBufferBytes = (value, optionName = "outputBufferBytes") => {
|
|
12
|
+
if (value === void 0) {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
16
|
+
throw createGtkInvalidArgumentError(
|
|
17
|
+
`${optionName} must be a non-negative safe integer.`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
};
|
|
22
|
+
const createStreamState = () => ({
|
|
23
|
+
byteLength: 0,
|
|
24
|
+
chunks: [],
|
|
25
|
+
decoder: new TextDecoder(),
|
|
26
|
+
flushed: false,
|
|
27
|
+
truncated: false
|
|
28
|
+
});
|
|
29
|
+
const appendBoundedChunk = (state, chunk, maxBytes) => {
|
|
30
|
+
if (chunk.length === 0) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (maxBytes === void 0) {
|
|
34
|
+
state.chunks.push(Buffer.from(chunk));
|
|
35
|
+
state.byteLength += chunk.length;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (maxBytes === 0) {
|
|
39
|
+
state.truncated = true;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (chunk.length >= maxBytes) {
|
|
43
|
+
state.chunks.splice(
|
|
44
|
+
0,
|
|
45
|
+
state.chunks.length,
|
|
46
|
+
Buffer.from(chunk.subarray(chunk.length - maxBytes))
|
|
47
|
+
);
|
|
48
|
+
state.byteLength = maxBytes;
|
|
49
|
+
state.truncated = true;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
state.chunks.push(Buffer.from(chunk));
|
|
53
|
+
state.byteLength += chunk.length;
|
|
54
|
+
while (state.byteLength > maxBytes) {
|
|
55
|
+
const first = state.chunks[0];
|
|
56
|
+
if (first === void 0) {
|
|
57
|
+
state.byteLength = 0;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const excess = state.byteLength - maxBytes;
|
|
61
|
+
if (first.length <= excess) {
|
|
62
|
+
state.chunks.shift();
|
|
63
|
+
state.byteLength -= first.length;
|
|
64
|
+
state.truncated = true;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
state.chunks[0] = Buffer.from(first.subarray(excess));
|
|
68
|
+
state.byteLength -= excess;
|
|
69
|
+
state.truncated = true;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const streamText = (state) => Buffer.concat(state.chunks, state.byteLength).toString("utf8");
|
|
73
|
+
const createOutputEvent = (nextSequence, create, text) => {
|
|
74
|
+
if (text.length === 0) {
|
|
75
|
+
return void 0;
|
|
76
|
+
}
|
|
77
|
+
return create(nextSequence());
|
|
78
|
+
};
|
|
79
|
+
const createGtkAppOutputRecorder = (outputBufferBytes) => {
|
|
80
|
+
const maxBytes = normalizeOutputBufferBytes(outputBufferBytes);
|
|
81
|
+
const stdout = createStreamState();
|
|
82
|
+
const stderr = createStreamState();
|
|
83
|
+
let nextSequence = 0;
|
|
84
|
+
const streamState = (stream) => stream === "stdout" ? stdout : stderr;
|
|
85
|
+
const takeSequence = () => {
|
|
86
|
+
const sequence = nextSequence;
|
|
87
|
+
nextSequence += 1;
|
|
88
|
+
return sequence;
|
|
89
|
+
};
|
|
90
|
+
const createEvent = (stream, text) => createOutputEvent(
|
|
91
|
+
takeSequence,
|
|
92
|
+
(sequence) => ({
|
|
93
|
+
sequence,
|
|
94
|
+
stream,
|
|
95
|
+
text,
|
|
96
|
+
timestampMs: Date.now()
|
|
97
|
+
}),
|
|
98
|
+
text
|
|
99
|
+
);
|
|
100
|
+
return {
|
|
101
|
+
append: (stream, chunk) => {
|
|
102
|
+
const state = streamState(stream);
|
|
103
|
+
appendBoundedChunk(state, chunk, maxBytes);
|
|
104
|
+
return createEvent(stream, state.decoder.decode(chunk, { stream: true }));
|
|
105
|
+
},
|
|
106
|
+
flush: (stream) => {
|
|
107
|
+
const state = streamState(stream);
|
|
108
|
+
if (state.flushed) {
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
state.flushed = true;
|
|
112
|
+
return createEvent(stream, state.decoder.decode());
|
|
113
|
+
},
|
|
114
|
+
snapshot: (exitCode, exitSignal) => ({
|
|
115
|
+
exitCode,
|
|
116
|
+
exitSignal,
|
|
117
|
+
stderr: streamText(stderr),
|
|
118
|
+
stderrTruncated: stderr.truncated,
|
|
119
|
+
stdout: streamText(stdout),
|
|
120
|
+
stdoutTruncated: stdout.truncated
|
|
121
|
+
})
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
const systemOutputSources = [
|
|
125
|
+
"xvfb",
|
|
126
|
+
"launcher-driver",
|
|
127
|
+
"tray-host"
|
|
128
|
+
];
|
|
129
|
+
const createSourceState = () => ({
|
|
130
|
+
stderr: createStreamState(),
|
|
131
|
+
stdout: createStreamState()
|
|
132
|
+
});
|
|
133
|
+
const createGtkSystemOutputRecorder = (systemOutputBufferBytes) => {
|
|
134
|
+
const maxBytes = normalizeOutputBufferBytes(
|
|
135
|
+
systemOutputBufferBytes,
|
|
136
|
+
"systemOutputBufferBytes"
|
|
137
|
+
);
|
|
138
|
+
const sources = /* @__PURE__ */ new Map();
|
|
139
|
+
let nextSequence = 0;
|
|
140
|
+
const takeSequence = () => {
|
|
141
|
+
const sequence = nextSequence;
|
|
142
|
+
nextSequence += 1;
|
|
143
|
+
return sequence;
|
|
144
|
+
};
|
|
145
|
+
const sourceState = (source) => {
|
|
146
|
+
const existing = sources.get(source);
|
|
147
|
+
if (existing !== void 0) {
|
|
148
|
+
return existing;
|
|
149
|
+
}
|
|
150
|
+
const state = createSourceState();
|
|
151
|
+
sources.set(source, state);
|
|
152
|
+
return state;
|
|
153
|
+
};
|
|
154
|
+
const streamState = (source, stream) => {
|
|
155
|
+
const state = sourceState(source);
|
|
156
|
+
return stream === "stdout" ? state.stdout : state.stderr;
|
|
157
|
+
};
|
|
158
|
+
const existingStreamState = (source, stream) => {
|
|
159
|
+
const state = sources.get(source);
|
|
160
|
+
if (state === void 0) {
|
|
161
|
+
return void 0;
|
|
162
|
+
}
|
|
163
|
+
return stream === "stdout" ? state.stdout : state.stderr;
|
|
164
|
+
};
|
|
165
|
+
const createEvent = (source, stream, text) => createOutputEvent(
|
|
166
|
+
takeSequence,
|
|
167
|
+
(sequence) => ({
|
|
168
|
+
sequence,
|
|
169
|
+
source,
|
|
170
|
+
stream,
|
|
171
|
+
text,
|
|
172
|
+
timestampMs: Date.now()
|
|
173
|
+
}),
|
|
174
|
+
text
|
|
175
|
+
);
|
|
176
|
+
const sourceSnapshot = (source, state) => ({
|
|
177
|
+
source,
|
|
178
|
+
stderr: streamText(state.stderr),
|
|
179
|
+
stderrTruncated: state.stderr.truncated,
|
|
180
|
+
stdout: streamText(state.stdout),
|
|
181
|
+
stdoutTruncated: state.stdout.truncated
|
|
182
|
+
});
|
|
183
|
+
return {
|
|
184
|
+
append: (source, stream, chunk) => {
|
|
185
|
+
const state = streamState(source, stream);
|
|
186
|
+
appendBoundedChunk(state, chunk, maxBytes);
|
|
187
|
+
return createEvent(
|
|
188
|
+
source,
|
|
189
|
+
stream,
|
|
190
|
+
state.decoder.decode(chunk, { stream: true })
|
|
191
|
+
);
|
|
192
|
+
},
|
|
193
|
+
flush: (source, stream) => {
|
|
194
|
+
const state = existingStreamState(source, stream);
|
|
195
|
+
if (state === void 0) {
|
|
196
|
+
return void 0;
|
|
197
|
+
}
|
|
198
|
+
if (state.flushed) {
|
|
199
|
+
return void 0;
|
|
200
|
+
}
|
|
201
|
+
state.flushed = true;
|
|
202
|
+
return createEvent(source, stream, state.decoder.decode());
|
|
203
|
+
},
|
|
204
|
+
snapshot: () => ({
|
|
205
|
+
sources: systemOutputSources.flatMap((source) => {
|
|
206
|
+
const state = sources.get(source);
|
|
207
|
+
return state === void 0 ? [] : [sourceSnapshot(source, state)];
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
const notifyGtkAppOutput = (callback, event) => {
|
|
213
|
+
if (callback === void 0 || event === void 0) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
callback(event);
|
|
218
|
+
} catch (error) {
|
|
219
|
+
queueMicrotask(() => {
|
|
220
|
+
throw error;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
const notifyGtkSystemOutput = (callback, event) => {
|
|
225
|
+
if (callback === void 0 || event === void 0) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
callback(event);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
queueMicrotask(() => {
|
|
232
|
+
throw error;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
};
|
|
11
236
|
const defaultDisplay = "xvfb";
|
|
12
237
|
const defaultGSettings = "memory";
|
|
13
238
|
const defaultTheme = "Adwaita";
|
|
@@ -20,8 +245,23 @@ const sessionStartupTimeoutMs = 3e4;
|
|
|
20
245
|
const sessionReleaseTimeoutMs = 5e3;
|
|
21
246
|
const xvfbStartupTimeoutMs = 1e4;
|
|
22
247
|
const xvfbPoolProbeTimeoutMs = 5e3;
|
|
248
|
+
const xvfbPoolProbeRetryIntervalMs = 50;
|
|
249
|
+
const xvfbPoolProbePrefix = "gestament-xvfb-pool-probe: ";
|
|
250
|
+
const x11DisplayOpenFailureMessage = "Failed to open the X11 display. Ensure DISPLAY points to an X11 display.";
|
|
23
251
|
const firstPooledDisplayNumber = 90;
|
|
24
252
|
const lastPooledDisplayNumber = 590;
|
|
253
|
+
const sessionOwnedEnvironmentKeys = [
|
|
254
|
+
"DISPLAY",
|
|
255
|
+
"WAYLAND_DISPLAY",
|
|
256
|
+
"GDK_BACKEND",
|
|
257
|
+
"DBUS_SESSION_BUS_ADDRESS",
|
|
258
|
+
"AT_SPI_BUS_ADDRESS",
|
|
259
|
+
"NO_AT_BRIDGE",
|
|
260
|
+
"XAUTHORITY",
|
|
261
|
+
"GESTAMENT_XVFB_ACTIVE",
|
|
262
|
+
"XDG_SESSION_TYPE"
|
|
263
|
+
];
|
|
264
|
+
let outputScopeCounter = 0;
|
|
25
265
|
let socketCounter = 0;
|
|
26
266
|
const leasedDisplayNumbers = /* @__PURE__ */ new Set();
|
|
27
267
|
const idleXvfbByKey = /* @__PURE__ */ new Map();
|
|
@@ -38,6 +278,12 @@ const appendOutput$1 = (lines, chunk) => {
|
|
|
38
278
|
lines.splice(0, lines.length - 40);
|
|
39
279
|
}
|
|
40
280
|
};
|
|
281
|
+
const appendSystemOutput = (sink, source, stream, chunk) => {
|
|
282
|
+
sink?.append(source, stream, chunk);
|
|
283
|
+
};
|
|
284
|
+
const flushSystemOutput = (sink, source, stream) => {
|
|
285
|
+
sink?.flush(source, stream);
|
|
286
|
+
};
|
|
41
287
|
const unrefHandle = (handle) => {
|
|
42
288
|
const maybeRefHandle = handle;
|
|
43
289
|
if (typeof maybeRefHandle.unref === "function") {
|
|
@@ -56,6 +302,41 @@ ${stdoutText}
|
|
|
56
302
|
stderr:
|
|
57
303
|
${stderrText}`;
|
|
58
304
|
};
|
|
305
|
+
const createXvfbProbeError = (message, retryable) => {
|
|
306
|
+
const error = createGtkOperationFailedError(message);
|
|
307
|
+
Object.defineProperty(error, "retryable", {
|
|
308
|
+
value: retryable
|
|
309
|
+
});
|
|
310
|
+
return error;
|
|
311
|
+
};
|
|
312
|
+
const parseXvfbProbeErrorPayload = (stderrText) => {
|
|
313
|
+
const lines = stderrText.trim().split("\n").reverse();
|
|
314
|
+
for (const line of lines) {
|
|
315
|
+
if (!line.startsWith(xvfbPoolProbePrefix)) {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
const value = JSON.parse(
|
|
320
|
+
line.slice(xvfbPoolProbePrefix.length)
|
|
321
|
+
);
|
|
322
|
+
if (!isRecord(value)) {
|
|
323
|
+
return void 0;
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
code: typeof value.code === "string" ? value.code : void 0,
|
|
327
|
+
message: typeof value.message === "string" ? value.message : void 0
|
|
328
|
+
};
|
|
329
|
+
} catch {
|
|
330
|
+
return void 0;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return void 0;
|
|
334
|
+
};
|
|
335
|
+
const isRetryableXvfbProbeExit = (stderrText) => {
|
|
336
|
+
const payload = parseXvfbProbeErrorPayload(stderrText);
|
|
337
|
+
return payload?.code === "OPERATION_FAILED" && payload.message === x11DisplayOpenFailureMessage;
|
|
338
|
+
};
|
|
339
|
+
const isRetryableXvfbProbeError = (error) => isRecord(error) && typeof error.retryable === "boolean" && error.retryable;
|
|
59
340
|
const getHostDisplayState = () => ({
|
|
60
341
|
display: process.env.DISPLAY,
|
|
61
342
|
waylandDisplay: process.env.WAYLAND_DISPLAY
|
|
@@ -81,6 +362,63 @@ const resolveDisplay = (display) => {
|
|
|
81
362
|
);
|
|
82
363
|
};
|
|
83
364
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
365
|
+
const createDriverEventKey = (channel, scopeId) => `${channel}
|
|
366
|
+
${scopeId}`;
|
|
367
|
+
const createOutputScopeId = () => {
|
|
368
|
+
const scopeId = `output-${process.pid}-${outputScopeCounter}`;
|
|
369
|
+
outputScopeCounter += 1;
|
|
370
|
+
return scopeId;
|
|
371
|
+
};
|
|
372
|
+
const isDriverEventMessage = (message) => isRecord(message) && message.type === "event" && typeof message.channel === "string" && typeof message.scopeId === "string";
|
|
373
|
+
const resolveOutputBufferBytes = (launcherValue, launchValue) => {
|
|
374
|
+
const value = launchValue ?? launcherValue;
|
|
375
|
+
if (value === void 0) {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
379
|
+
throw createGtkInvalidArgumentError(
|
|
380
|
+
"outputBufferBytes must be a non-negative safe integer."
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
return value;
|
|
384
|
+
};
|
|
385
|
+
const createSystemOutputSink = (recorder, callback) => ({
|
|
386
|
+
append: (source, stream, chunk) => {
|
|
387
|
+
notifyGtkSystemOutput(callback, recorder.append(source, stream, chunk));
|
|
388
|
+
},
|
|
389
|
+
flush: (source, stream) => {
|
|
390
|
+
notifyGtkSystemOutput(callback, recorder.flush(source, stream));
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
const isGtkSystemOutputSource = (value) => value === "xvfb" || value === "launcher-driver" || value === "tray-host";
|
|
394
|
+
const isGtkAppOutputStream = (value) => value === "stdout" || value === "stderr";
|
|
395
|
+
const isWireGtkSystemOutput = (value) => {
|
|
396
|
+
if (!isRecord(value)) {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
if (!isGtkSystemOutputSource(value.source) || !isGtkAppOutputStream(value.stream)) {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
if (value.type === "flush") {
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
return value.type === "chunk" && typeof value.chunkBase64 === "string";
|
|
406
|
+
};
|
|
407
|
+
const routeWireSystemOutput = (sink, value) => {
|
|
408
|
+
if (!isWireGtkSystemOutput(value)) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (value.type === "flush") {
|
|
412
|
+
flushSystemOutput(sink, value.source, value.stream);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
appendSystemOutput(
|
|
416
|
+
sink,
|
|
417
|
+
value.source,
|
|
418
|
+
value.stream,
|
|
419
|
+
Buffer.from(value.chunkBase64, "base64")
|
|
420
|
+
);
|
|
421
|
+
};
|
|
84
422
|
const resolvePoolLimit = (name, value, defaultValue) => {
|
|
85
423
|
if (value === void 0) {
|
|
86
424
|
return defaultValue;
|
|
@@ -228,6 +566,7 @@ const retainIdleXvfb = async (xvfb, limits) => {
|
|
|
228
566
|
await terminateXvfb(xvfb);
|
|
229
567
|
return;
|
|
230
568
|
}
|
|
569
|
+
xvfb.systemOutputSink = void 0;
|
|
231
570
|
xvfb.lastUsedAt = Date.now();
|
|
232
571
|
pushArrayEntry(idleXvfbByKey, xvfb.key, xvfb);
|
|
233
572
|
await trimIdleXvfbKey(xvfb.key, limits.maxIdlePerKey);
|
|
@@ -238,6 +577,8 @@ const retainIdleAllSession = async (session, limits) => {
|
|
|
238
577
|
await session.session.terminate();
|
|
239
578
|
return;
|
|
240
579
|
}
|
|
580
|
+
session.session.setSystemOutputSink(void 0);
|
|
581
|
+
session.xvfb.systemOutputSink = void 0;
|
|
241
582
|
session.lastUsedAt = Date.now();
|
|
242
583
|
session.xvfb.lastUsedAt = session.lastUsedAt;
|
|
243
584
|
pushArrayEntry(idleAllByKey, session.key, session);
|
|
@@ -311,12 +652,34 @@ const toWireEnvironment = (env) => {
|
|
|
311
652
|
}
|
|
312
653
|
return wireEnv;
|
|
313
654
|
};
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
655
|
+
const wireEnvironmentToGtkAppEnvironment = (env) => {
|
|
656
|
+
const appEnv = {};
|
|
657
|
+
for (const [key, value] of Object.entries(env)) {
|
|
658
|
+
appEnv[key] = value === null ? void 0 : value;
|
|
659
|
+
}
|
|
660
|
+
return appEnv;
|
|
661
|
+
};
|
|
662
|
+
const assertNoSessionOwnedEnvironmentOverrides = (options, effective) => {
|
|
663
|
+
if (effective.kind !== "xvfb" || options.env === void 0) {
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
for (const key of sessionOwnedEnvironmentKeys) {
|
|
667
|
+
if (Object.hasOwn(options.env, key)) {
|
|
668
|
+
throw createGtkInvalidArgumentError(
|
|
669
|
+
`options.env must not override ${key} when using internal Xvfb.`
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
const resolveLauncherEnvironment = (options, effective) => {
|
|
675
|
+
assertNoSessionOwnedEnvironmentOverrides(options, effective);
|
|
676
|
+
return toWireEnvironment({
|
|
677
|
+
GDK_BACKEND: resolveGdkBackend(effective),
|
|
678
|
+
GSETTINGS_BACKEND: options.gsettings === null ? void 0 : options.gsettings ?? defaultGSettings,
|
|
679
|
+
GTK_THEME: options.theme === null ? void 0 : options.theme ?? defaultTheme,
|
|
680
|
+
...options.env
|
|
681
|
+
});
|
|
682
|
+
};
|
|
320
683
|
const resolveDriverPath = () => {
|
|
321
684
|
const driverPath = resolve(
|
|
322
685
|
dirname(fileURLToPath(import.meta.url)),
|
|
@@ -350,11 +713,17 @@ const createDriverEnvironment = (effective, xvfb) => {
|
|
|
350
713
|
delete env.AT_SPI_BUS_ADDRESS;
|
|
351
714
|
delete env.NO_AT_BRIDGE;
|
|
352
715
|
if (effective.kind === "xvfb") {
|
|
716
|
+
delete env.DBUS_SESSION_BUS_ADDRESS;
|
|
717
|
+
delete env.DISPLAY;
|
|
718
|
+
delete env.WAYLAND_DISPLAY;
|
|
719
|
+
delete env.AT_SPI_BUS_ADDRESS;
|
|
720
|
+
delete env.NO_AT_BRIDGE;
|
|
721
|
+
delete env.XAUTHORITY;
|
|
353
722
|
env.GDK_BACKEND = "x11";
|
|
354
723
|
env.GESTAMENT_XVFB_ACTIVE = "1";
|
|
724
|
+
env.XDG_SESSION_TYPE = "x11";
|
|
355
725
|
if (xvfb !== void 0) {
|
|
356
726
|
env.DISPLAY = xvfb.display;
|
|
357
|
-
delete env.XAUTHORITY;
|
|
358
727
|
}
|
|
359
728
|
}
|
|
360
729
|
return env;
|
|
@@ -422,7 +791,7 @@ const installPoolCleanup = () => {
|
|
|
422
791
|
}
|
|
423
792
|
});
|
|
424
793
|
};
|
|
425
|
-
const spawnDirectXvfb = async (screen) => {
|
|
794
|
+
const spawnDirectXvfb = async (screen, systemOutputSink) => {
|
|
426
795
|
installPoolCleanup();
|
|
427
796
|
for (let displayNumber = firstPooledDisplayNumber; displayNumber <= lastPooledDisplayNumber; displayNumber += 1) {
|
|
428
797
|
if (!isDisplayNumberAvailable(displayNumber)) {
|
|
@@ -439,11 +808,30 @@ const spawnDirectXvfb = async (screen) => {
|
|
|
439
808
|
stdio: ["ignore", "pipe", "pipe"]
|
|
440
809
|
}
|
|
441
810
|
);
|
|
811
|
+
const xvfb = {
|
|
812
|
+
child,
|
|
813
|
+
display: `:${displayNumber}`,
|
|
814
|
+
displayNumber,
|
|
815
|
+
key: screen,
|
|
816
|
+
lastUsedAt: Date.now(),
|
|
817
|
+
screen,
|
|
818
|
+
stderr,
|
|
819
|
+
stdout,
|
|
820
|
+
systemOutputSink
|
|
821
|
+
};
|
|
442
822
|
child.stdout.on("data", (chunk) => {
|
|
443
823
|
appendOutput$1(stdout, chunk);
|
|
824
|
+
appendSystemOutput(xvfb.systemOutputSink, "xvfb", "stdout", chunk);
|
|
444
825
|
});
|
|
445
826
|
child.stderr.on("data", (chunk) => {
|
|
446
827
|
appendOutput$1(stderr, chunk);
|
|
828
|
+
appendSystemOutput(xvfb.systemOutputSink, "xvfb", "stderr", chunk);
|
|
829
|
+
});
|
|
830
|
+
child.stdout.once("end", () => {
|
|
831
|
+
flushSystemOutput(xvfb.systemOutputSink, "xvfb", "stdout");
|
|
832
|
+
});
|
|
833
|
+
child.stderr.once("end", () => {
|
|
834
|
+
flushSystemOutput(xvfb.systemOutputSink, "xvfb", "stderr");
|
|
447
835
|
});
|
|
448
836
|
child.unref();
|
|
449
837
|
unrefHandle(child.stdout);
|
|
@@ -466,22 +854,10 @@ const spawnDirectXvfb = async (screen) => {
|
|
|
466
854
|
});
|
|
467
855
|
})
|
|
468
856
|
]);
|
|
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
857
|
directXvfbs.add(xvfb);
|
|
480
858
|
return xvfb;
|
|
481
859
|
} catch (error) {
|
|
482
|
-
killXvfbNow(
|
|
483
|
-
child
|
|
484
|
-
});
|
|
860
|
+
killXvfbNow(xvfb);
|
|
485
861
|
leasedDisplayNumbers.delete(displayNumber);
|
|
486
862
|
const message = error instanceof Error ? error.message : String(error);
|
|
487
863
|
if (message.includes("ENOENT")) {
|
|
@@ -509,39 +885,69 @@ const terminateXvfb = async (xvfb) => {
|
|
|
509
885
|
await delay(25);
|
|
510
886
|
}
|
|
511
887
|
}
|
|
888
|
+
xvfb.systemOutputSink = void 0;
|
|
512
889
|
leasedDisplayNumbers.delete(xvfb.displayNumber);
|
|
513
890
|
};
|
|
514
|
-
const leaseXvfb = async (screen) => {
|
|
891
|
+
const leaseXvfb = async (screen, systemOutputSink) => {
|
|
515
892
|
for (; ; ) {
|
|
516
893
|
const idle = popArrayEntry(idleXvfbByKey, screen);
|
|
517
894
|
if (idle === void 0) {
|
|
518
|
-
return spawnDirectXvfb(screen);
|
|
895
|
+
return spawnDirectXvfb(screen, systemOutputSink);
|
|
519
896
|
}
|
|
520
897
|
if (idle.child.exitCode === null && idle.child.signalCode === null) {
|
|
521
898
|
idle.lastUsedAt = Date.now();
|
|
899
|
+
idle.systemOutputSink = systemOutputSink;
|
|
522
900
|
return idle;
|
|
523
901
|
}
|
|
524
902
|
await terminateXvfb(idle);
|
|
525
903
|
}
|
|
526
904
|
};
|
|
527
|
-
const
|
|
905
|
+
const runXvfbProbeOnce = (xvfb, timeoutMs) => new Promise((resolveProbe, rejectProbe) => {
|
|
528
906
|
const probePath = resolveXvfbPoolProbePath();
|
|
529
907
|
const stdout = [];
|
|
530
908
|
const stderr = [];
|
|
909
|
+
const env = {
|
|
910
|
+
...process.env,
|
|
911
|
+
DISPLAY: xvfb.display,
|
|
912
|
+
GDK_BACKEND: "x11",
|
|
913
|
+
GESTAMENT_XVFB_ACTIVE: "1",
|
|
914
|
+
XDG_SESSION_TYPE: "x11"
|
|
915
|
+
};
|
|
916
|
+
delete env.AT_SPI_BUS_ADDRESS;
|
|
917
|
+
delete env.DBUS_SESSION_BUS_ADDRESS;
|
|
918
|
+
delete env.NO_AT_BRIDGE;
|
|
919
|
+
delete env.WAYLAND_DISPLAY;
|
|
920
|
+
delete env.XAUTHORITY;
|
|
531
921
|
const child = spawn(process.execPath, [probePath], {
|
|
532
|
-
env
|
|
533
|
-
...process.env,
|
|
534
|
-
DISPLAY: xvfb.display,
|
|
535
|
-
GDK_BACKEND: "x11"
|
|
536
|
-
},
|
|
922
|
+
env,
|
|
537
923
|
stdio: ["ignore", "pipe", "pipe"]
|
|
538
924
|
});
|
|
539
|
-
|
|
925
|
+
let settled = false;
|
|
926
|
+
let timeout;
|
|
927
|
+
const rejectOnce = (error) => {
|
|
928
|
+
if (settled) {
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
settled = true;
|
|
932
|
+
if (timeout !== void 0) {
|
|
933
|
+
clearTimeout(timeout);
|
|
934
|
+
}
|
|
935
|
+
rejectProbe(error);
|
|
936
|
+
};
|
|
937
|
+
const resolveOnce = (result) => {
|
|
938
|
+
if (settled) {
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
settled = true;
|
|
942
|
+
if (timeout !== void 0) {
|
|
943
|
+
clearTimeout(timeout);
|
|
944
|
+
}
|
|
945
|
+
resolveProbe(result);
|
|
946
|
+
};
|
|
947
|
+
timeout = setTimeout(() => {
|
|
540
948
|
child.kill("SIGKILL");
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
);
|
|
544
|
-
}, xvfbPoolProbeTimeoutMs);
|
|
949
|
+
rejectOnce(createXvfbProbeError("Timed out probing Xvfb pool.", false));
|
|
950
|
+
}, timeoutMs);
|
|
545
951
|
child.stdout.on("data", (chunk) => {
|
|
546
952
|
appendOutput$1(stdout, chunk);
|
|
547
953
|
});
|
|
@@ -549,42 +955,71 @@ const runXvfbProbe = (xvfb) => new Promise((resolveProbe, rejectProbe) => {
|
|
|
549
955
|
appendOutput$1(stderr, chunk);
|
|
550
956
|
});
|
|
551
957
|
child.once("error", (error) => {
|
|
552
|
-
|
|
553
|
-
rejectProbe(error);
|
|
958
|
+
rejectOnce(error);
|
|
554
959
|
});
|
|
555
960
|
child.once("exit", (code, signal) => {
|
|
556
|
-
clearTimeout(timeout);
|
|
557
961
|
if (code !== 0) {
|
|
558
|
-
|
|
559
|
-
|
|
962
|
+
const stderrText = stderr.join("");
|
|
963
|
+
rejectOnce(
|
|
964
|
+
createXvfbProbeError(
|
|
560
965
|
`Xvfb pool probe failed: code=${String(code)}, signal=${String(
|
|
561
966
|
signal
|
|
562
|
-
)}` + formatOutputTail(stdout, stderr)
|
|
967
|
+
)}` + formatOutputTail(stdout, stderr),
|
|
968
|
+
isRetryableXvfbProbeExit(stderrText)
|
|
563
969
|
)
|
|
564
970
|
);
|
|
565
971
|
return;
|
|
566
972
|
}
|
|
567
973
|
const output = stdout.join("").trim().split("\n").at(-1);
|
|
568
974
|
if (output === void 0 || output.length === 0) {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
"Xvfb pool probe did not return a result." + formatOutputTail(stdout, stderr)
|
|
975
|
+
rejectOnce(
|
|
976
|
+
createXvfbProbeError(
|
|
977
|
+
"Xvfb pool probe did not return a result." + formatOutputTail(stdout, stderr),
|
|
978
|
+
false
|
|
572
979
|
)
|
|
573
980
|
);
|
|
574
981
|
return;
|
|
575
982
|
}
|
|
576
983
|
try {
|
|
577
|
-
|
|
984
|
+
resolveOnce(JSON.parse(output));
|
|
578
985
|
} catch (error) {
|
|
579
986
|
const message = error instanceof Error ? error.message : String(error);
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
`Xvfb pool probe returned invalid JSON: ${message}` + formatOutputTail(stdout, stderr)
|
|
987
|
+
rejectOnce(
|
|
988
|
+
createXvfbProbeError(
|
|
989
|
+
`Xvfb pool probe returned invalid JSON: ${message}` + formatOutputTail(stdout, stderr),
|
|
990
|
+
false
|
|
583
991
|
)
|
|
584
992
|
);
|
|
585
993
|
}
|
|
586
994
|
});
|
|
587
995
|
});
|
|
996
|
+
const runXvfbProbe = async (xvfb) => {
|
|
997
|
+
const startedAt = Date.now();
|
|
998
|
+
let lastError;
|
|
999
|
+
while (Date.now() - startedAt < xvfbPoolProbeTimeoutMs) {
|
|
1000
|
+
const remainingTimeoutMs = Math.max(
|
|
1001
|
+
1,
|
|
1002
|
+
xvfbPoolProbeTimeoutMs - (Date.now() - startedAt)
|
|
1003
|
+
);
|
|
1004
|
+
try {
|
|
1005
|
+
return await runXvfbProbeOnce(xvfb, remainingTimeoutMs);
|
|
1006
|
+
} catch (error) {
|
|
1007
|
+
lastError = error;
|
|
1008
|
+
if (!isRetryableXvfbProbeError(error)) {
|
|
1009
|
+
throw error;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
const remainingDelayMs = xvfbPoolProbeTimeoutMs - (Date.now() - startedAt);
|
|
1013
|
+
if (remainingDelayMs <= 0) {
|
|
1014
|
+
break;
|
|
1015
|
+
}
|
|
1016
|
+
await delay(Math.min(xvfbPoolProbeRetryIntervalMs, remainingDelayMs));
|
|
1017
|
+
}
|
|
1018
|
+
if (lastError instanceof Error) {
|
|
1019
|
+
throw lastError;
|
|
1020
|
+
}
|
|
1021
|
+
throw createGtkOperationFailedError("Timed out probing Xvfb pool.");
|
|
1022
|
+
};
|
|
588
1023
|
const cleanCheckXvfb = async (xvfb, allowedMappedWindowCount) => {
|
|
589
1024
|
try {
|
|
590
1025
|
const probe = await runXvfbProbe(xvfb);
|
|
@@ -603,7 +1038,7 @@ const returnXvfbToPool = async (xvfb, limits) => {
|
|
|
603
1038
|
};
|
|
604
1039
|
const allPoolKey = (xvfb) => `${xvfb.screen}
|
|
605
1040
|
${xvfb.trayHost ? "tray" : "no-tray"}`;
|
|
606
|
-
const spawnDriverProcess = (driverPath, socketPath, effective, xvfb) => {
|
|
1041
|
+
const spawnDriverProcess = (driverPath, socketPath, effective, xvfb, systemOutputSink) => {
|
|
607
1042
|
const driverArgs = [
|
|
608
1043
|
"--socket",
|
|
609
1044
|
socketPath,
|
|
@@ -636,14 +1071,46 @@ const spawnDriverProcess = (driverPath, socketPath, effective, xvfb) => {
|
|
|
636
1071
|
env,
|
|
637
1072
|
stdio: ["ignore", "pipe", "pipe"]
|
|
638
1073
|
});
|
|
1074
|
+
const processState = {
|
|
1075
|
+
child,
|
|
1076
|
+
commandLine: [command.bin, ...command.args].join(" "),
|
|
1077
|
+
stderr,
|
|
1078
|
+
stdout,
|
|
1079
|
+
systemOutputSink
|
|
1080
|
+
};
|
|
639
1081
|
child.stdout.on("data", (chunk) => {
|
|
640
1082
|
appendOutput$1(stdout, chunk);
|
|
1083
|
+
appendSystemOutput(
|
|
1084
|
+
processState.systemOutputSink,
|
|
1085
|
+
"launcher-driver",
|
|
1086
|
+
"stdout",
|
|
1087
|
+
chunk
|
|
1088
|
+
);
|
|
641
1089
|
});
|
|
642
1090
|
child.stderr.on("data", (chunk) => {
|
|
643
1091
|
appendOutput$1(stderr, chunk);
|
|
1092
|
+
appendSystemOutput(
|
|
1093
|
+
processState.systemOutputSink,
|
|
1094
|
+
"launcher-driver",
|
|
1095
|
+
"stderr",
|
|
1096
|
+
chunk
|
|
1097
|
+
);
|
|
644
1098
|
});
|
|
645
|
-
|
|
646
|
-
|
|
1099
|
+
child.stdout.once("end", () => {
|
|
1100
|
+
flushSystemOutput(
|
|
1101
|
+
processState.systemOutputSink,
|
|
1102
|
+
"launcher-driver",
|
|
1103
|
+
"stdout"
|
|
1104
|
+
);
|
|
1105
|
+
});
|
|
1106
|
+
child.stderr.once("end", () => {
|
|
1107
|
+
flushSystemOutput(
|
|
1108
|
+
processState.systemOutputSink,
|
|
1109
|
+
"launcher-driver",
|
|
1110
|
+
"stderr"
|
|
1111
|
+
);
|
|
1112
|
+
});
|
|
1113
|
+
return processState;
|
|
647
1114
|
};
|
|
648
1115
|
const listenOnSocket = (server, socketPath) => new Promise((resolveListen, rejectListen) => {
|
|
649
1116
|
const rejectFromError = (error) => {
|
|
@@ -722,7 +1189,13 @@ const waitForDriverReady = (server, processState) => new Promise((resolveReady,
|
|
|
722
1189
|
const line = input.slice(0, newlineIndex);
|
|
723
1190
|
input = input.slice(newlineIndex + 1);
|
|
724
1191
|
try {
|
|
725
|
-
|
|
1192
|
+
const message = JSON.parse(line);
|
|
1193
|
+
if (isDriverEventMessage(message)) {
|
|
1194
|
+
const event = message;
|
|
1195
|
+
if (event.channel === "system.output") {
|
|
1196
|
+
routeWireSystemOutput(processState.systemOutputSink, event.value);
|
|
1197
|
+
}
|
|
1198
|
+
} else if (isRecord(message) && message.type === "ready" && parseReadyMessage(line) !== void 0) {
|
|
726
1199
|
if (settled) {
|
|
727
1200
|
return;
|
|
728
1201
|
}
|
|
@@ -800,6 +1273,7 @@ const decodeCapture = (capture) => ({
|
|
|
800
1273
|
visibleBounds: capture.visibleBounds
|
|
801
1274
|
});
|
|
802
1275
|
const createDriverSession = (socket, bufferedInput, processState, tempDirectory, poolOptions) => {
|
|
1276
|
+
const eventHandlers = /* @__PURE__ */ new Map();
|
|
803
1277
|
const pending = /* @__PURE__ */ new Map();
|
|
804
1278
|
let input = bufferedInput;
|
|
805
1279
|
let nextRequestId = 1;
|
|
@@ -819,8 +1293,34 @@ const createDriverSession = (socket, bufferedInput, processState, tempDirectory,
|
|
|
819
1293
|
closed = true;
|
|
820
1294
|
rejectPending(createGtkAppExitedError("Launcher driver has exited."));
|
|
821
1295
|
};
|
|
1296
|
+
const handleEvent = (event) => {
|
|
1297
|
+
if (event.channel === "system.output") {
|
|
1298
|
+
routeWireSystemOutput(processState.systemOutputSink, event.value);
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
const handlers = eventHandlers.get(
|
|
1302
|
+
createDriverEventKey(event.channel, event.scopeId)
|
|
1303
|
+
);
|
|
1304
|
+
if (handlers === void 0) {
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
for (const handler of handlers) {
|
|
1308
|
+
try {
|
|
1309
|
+
handler(event.value);
|
|
1310
|
+
} catch (error) {
|
|
1311
|
+
queueMicrotask(() => {
|
|
1312
|
+
throw error;
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
822
1317
|
const handleResponse = (line) => {
|
|
823
|
-
const
|
|
1318
|
+
const message = JSON.parse(line);
|
|
1319
|
+
if (isDriverEventMessage(message)) {
|
|
1320
|
+
handleEvent(message);
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
const response = message;
|
|
824
1324
|
const entry = pending.get(response.id);
|
|
825
1325
|
if (entry === void 0) {
|
|
826
1326
|
return;
|
|
@@ -895,6 +1395,28 @@ const createDriverSession = (socket, bufferedInput, processState, tempDirectory,
|
|
|
895
1395
|
});
|
|
896
1396
|
});
|
|
897
1397
|
};
|
|
1398
|
+
const subscribe = (channel, scopeId, handler) => {
|
|
1399
|
+
const key = createDriverEventKey(channel, scopeId);
|
|
1400
|
+
const handlers = eventHandlers.get(key) ?? /* @__PURE__ */ new Set();
|
|
1401
|
+
handlers.add(handler);
|
|
1402
|
+
eventHandlers.set(key, handlers);
|
|
1403
|
+
let disposed = false;
|
|
1404
|
+
return {
|
|
1405
|
+
dispose: () => {
|
|
1406
|
+
if (disposed) {
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
disposed = true;
|
|
1410
|
+
handlers.delete(handler);
|
|
1411
|
+
if (handlers.size === 0) {
|
|
1412
|
+
eventHandlers.delete(key);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
};
|
|
1416
|
+
};
|
|
1417
|
+
const setSystemOutputSink = (sink) => {
|
|
1418
|
+
processState.systemOutputSink = sink;
|
|
1419
|
+
};
|
|
898
1420
|
const waitForExit = async () => {
|
|
899
1421
|
const startedAt = Date.now();
|
|
900
1422
|
while (processState.child.exitCode === null && processState.child.signalCode === null) {
|
|
@@ -979,10 +1501,10 @@ const createDriverSession = (socket, bufferedInput, processState, tempDirectory,
|
|
|
979
1501
|
poolOptions.limits
|
|
980
1502
|
);
|
|
981
1503
|
};
|
|
982
|
-
session = { release, request, terminate };
|
|
1504
|
+
session = { release, request, setSystemOutputSink, subscribe, terminate };
|
|
983
1505
|
return session;
|
|
984
1506
|
};
|
|
985
|
-
const startFreshDriverSession = async (effective, xvfb, poolOptions) => {
|
|
1507
|
+
const startFreshDriverSession = async (effective, xvfb, poolOptions, systemOutputSink) => {
|
|
986
1508
|
const driverPath = resolveDriverPath();
|
|
987
1509
|
const tempDirectory = mkdtempSync(
|
|
988
1510
|
join(tmpdir(), `gestament-launcher-${process.pid}-${socketCounter}-`)
|
|
@@ -994,7 +1516,13 @@ const startFreshDriverSession = async (effective, xvfb, poolOptions) => {
|
|
|
994
1516
|
try {
|
|
995
1517
|
await listenOnSocket(server, socketPath);
|
|
996
1518
|
server.unref();
|
|
997
|
-
processState = spawnDriverProcess(
|
|
1519
|
+
processState = spawnDriverProcess(
|
|
1520
|
+
driverPath,
|
|
1521
|
+
socketPath,
|
|
1522
|
+
effective,
|
|
1523
|
+
xvfb,
|
|
1524
|
+
systemOutputSink
|
|
1525
|
+
);
|
|
998
1526
|
const connection = await waitForDriverReady(server, processState);
|
|
999
1527
|
server.close();
|
|
1000
1528
|
const resolvedPoolOptions = poolOptions.mode === "all" && xvfb !== void 0 ? {
|
|
@@ -1020,7 +1548,7 @@ const startFreshDriverSession = async (effective, xvfb, poolOptions) => {
|
|
|
1020
1548
|
throw error;
|
|
1021
1549
|
}
|
|
1022
1550
|
};
|
|
1023
|
-
const startDriverSession = async (options) => {
|
|
1551
|
+
const startDriverSession = async (options, systemOutputSink) => {
|
|
1024
1552
|
const display = resolveDisplay(options.display);
|
|
1025
1553
|
const xvfbOptions = resolveXvfbOptions(options);
|
|
1026
1554
|
const effective = resolveEffectiveDisplay(display, xvfbOptions);
|
|
@@ -1028,7 +1556,8 @@ const startDriverSession = async (options) => {
|
|
|
1028
1556
|
return startFreshDriverSession(
|
|
1029
1557
|
effective,
|
|
1030
1558
|
void 0,
|
|
1031
|
-
emptyDriverSessionPoolOptions()
|
|
1559
|
+
emptyDriverSessionPoolOptions(),
|
|
1560
|
+
systemOutputSink
|
|
1032
1561
|
);
|
|
1033
1562
|
}
|
|
1034
1563
|
const pool = effective.xvfb.pool;
|
|
@@ -1036,18 +1565,24 @@ const startDriverSession = async (options) => {
|
|
|
1036
1565
|
return startFreshDriverSession(
|
|
1037
1566
|
effective,
|
|
1038
1567
|
void 0,
|
|
1039
|
-
emptyDriverSessionPoolOptions()
|
|
1568
|
+
emptyDriverSessionPoolOptions(),
|
|
1569
|
+
systemOutputSink
|
|
1040
1570
|
);
|
|
1041
1571
|
}
|
|
1042
1572
|
if (pool.type === "xvfb") {
|
|
1043
|
-
const xvfb2 = await leaseXvfb(effective.xvfb.screen);
|
|
1044
|
-
return startFreshDriverSession(
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1573
|
+
const xvfb2 = await leaseXvfb(effective.xvfb.screen, systemOutputSink);
|
|
1574
|
+
return startFreshDriverSession(
|
|
1575
|
+
effective,
|
|
1576
|
+
xvfb2,
|
|
1577
|
+
{
|
|
1578
|
+
allKey: void 0,
|
|
1579
|
+
allowedMappedWindowCount: 0,
|
|
1580
|
+
limits: poolLimits(pool),
|
|
1581
|
+
mode: "xvfb",
|
|
1582
|
+
xvfb: xvfb2
|
|
1583
|
+
},
|
|
1584
|
+
systemOutputSink
|
|
1585
|
+
);
|
|
1051
1586
|
}
|
|
1052
1587
|
const key = allPoolKey(effective.xvfb);
|
|
1053
1588
|
for (; ; ) {
|
|
@@ -1058,20 +1593,27 @@ const startDriverSession = async (options) => {
|
|
|
1058
1593
|
if (idle.xvfb.child.exitCode === null && idle.xvfb.child.signalCode === null) {
|
|
1059
1594
|
idle.lastUsedAt = Date.now();
|
|
1060
1595
|
idle.xvfb.lastUsedAt = idle.lastUsedAt;
|
|
1596
|
+
idle.xvfb.systemOutputSink = systemOutputSink;
|
|
1597
|
+
idle.session.setSystemOutputSink(systemOutputSink);
|
|
1061
1598
|
return idle.session;
|
|
1062
1599
|
}
|
|
1063
1600
|
await idle.session.terminate().catch(() => void 0);
|
|
1064
1601
|
}
|
|
1065
|
-
const xvfb = await leaseXvfb(effective.xvfb.screen);
|
|
1066
|
-
return startFreshDriverSession(
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1602
|
+
const xvfb = await leaseXvfb(effective.xvfb.screen, systemOutputSink);
|
|
1603
|
+
return startFreshDriverSession(
|
|
1604
|
+
effective,
|
|
1605
|
+
xvfb,
|
|
1606
|
+
{
|
|
1607
|
+
allKey: key,
|
|
1608
|
+
allowedMappedWindowCount: 0,
|
|
1609
|
+
limits: poolLimits(pool),
|
|
1610
|
+
mode: "all",
|
|
1611
|
+
xvfb
|
|
1612
|
+
},
|
|
1613
|
+
systemOutputSink
|
|
1614
|
+
);
|
|
1073
1615
|
};
|
|
1074
|
-
const createLaunchPayload = (options, args) => {
|
|
1616
|
+
const createLaunchPayload = (options, args, launchOptions, outputScopeId) => {
|
|
1075
1617
|
const display = resolveDisplay(options.display);
|
|
1076
1618
|
const xvfb = resolveXvfbOptions(options);
|
|
1077
1619
|
const effective = resolveEffectiveDisplay(display, xvfb);
|
|
@@ -1079,12 +1621,25 @@ const createLaunchPayload = (options, args) => {
|
|
|
1079
1621
|
appPath: options.appPath,
|
|
1080
1622
|
args: [...options.args ?? [], ...args],
|
|
1081
1623
|
env: resolveLauncherEnvironment(options, effective),
|
|
1624
|
+
outputBufferBytes: resolveOutputBufferBytes(
|
|
1625
|
+
options.outputBufferBytes,
|
|
1626
|
+
launchOptions?.outputBufferBytes
|
|
1627
|
+
),
|
|
1628
|
+
outputScopeId,
|
|
1082
1629
|
timeoutMs: options.timeoutMs ?? null
|
|
1083
1630
|
};
|
|
1084
1631
|
};
|
|
1632
|
+
const createEnvironmentPayload = (options) => {
|
|
1633
|
+
const display = resolveDisplay(options.display);
|
|
1634
|
+
const xvfb = resolveXvfbOptions(options);
|
|
1635
|
+
const effective = resolveEffectiveDisplay(display, xvfb);
|
|
1636
|
+
return {
|
|
1637
|
+
env: resolveLauncherEnvironment(options, effective)
|
|
1638
|
+
};
|
|
1639
|
+
};
|
|
1085
1640
|
const elementRefToProxy = (session, ref) => ref === null ? void 0 : createProxyGtkElement(session, ref);
|
|
1086
1641
|
const trayRefToProxy = (session, ref) => ref === null ? void 0 : createProxyGtkTrayItem(session, ref);
|
|
1087
|
-
const createProxyGtkApp = (session, ref) => {
|
|
1642
|
+
const createProxyGtkApp = (session, ref, outputSubscription) => {
|
|
1088
1643
|
let released = false;
|
|
1089
1644
|
const assertNotReleased = () => {
|
|
1090
1645
|
if (released) {
|
|
@@ -1103,10 +1658,15 @@ const createProxyGtkApp = (session, ref) => {
|
|
|
1103
1658
|
return;
|
|
1104
1659
|
}
|
|
1105
1660
|
released = true;
|
|
1661
|
+
outputSubscription?.dispose();
|
|
1106
1662
|
await session.request("app.release", { appId: ref.appId });
|
|
1107
1663
|
};
|
|
1108
1664
|
const app = {
|
|
1109
1665
|
capture: async () => decodeCapture(await appRequest("app.capture")),
|
|
1666
|
+
environment: async () => wireEnvironmentToGtkAppEnvironment(
|
|
1667
|
+
await appRequest("app.environment")
|
|
1668
|
+
),
|
|
1669
|
+
output: () => appRequest("app.output"),
|
|
1110
1670
|
findById: async (id) => elementRefToProxy(
|
|
1111
1671
|
session,
|
|
1112
1672
|
await appRequest("app.findById", { id })
|
|
@@ -1219,6 +1779,13 @@ const createProxyGtkElement = (session, ref) => {
|
|
|
1219
1779
|
};
|
|
1220
1780
|
switch (ref.kind) {
|
|
1221
1781
|
case "window":
|
|
1782
|
+
target.bounds = () => session.request("element.bounds", { elementId });
|
|
1783
|
+
target.resizeHints = () => session.request("window.resizeHints", {
|
|
1784
|
+
elementId
|
|
1785
|
+
});
|
|
1786
|
+
target.x11Info = () => session.request("window.x11Info", { elementId });
|
|
1787
|
+
addChildContainerProxyOperations(session, elementId, target);
|
|
1788
|
+
break;
|
|
1222
1789
|
case "container":
|
|
1223
1790
|
case "menu":
|
|
1224
1791
|
addChildContainerProxyOperations(session, elementId, target);
|
|
@@ -1327,19 +1894,84 @@ const createProxyGtkTrayItem = (session, ref) => {
|
|
|
1327
1894
|
};
|
|
1328
1895
|
};
|
|
1329
1896
|
const createDriverBackedGtkAppLauncher = (options) => {
|
|
1897
|
+
const outputSubscriptions = /* @__PURE__ */ new Set();
|
|
1898
|
+
let systemOutputRecorder = createGtkSystemOutputRecorder(
|
|
1899
|
+
normalizeOutputBufferBytes(
|
|
1900
|
+
options.systemOutputBufferBytes,
|
|
1901
|
+
"systemOutputBufferBytes"
|
|
1902
|
+
)
|
|
1903
|
+
);
|
|
1330
1904
|
let sessionPromise;
|
|
1331
1905
|
const ensureSession = () => {
|
|
1332
|
-
sessionPromise
|
|
1906
|
+
if (sessionPromise === void 0) {
|
|
1907
|
+
systemOutputRecorder = createGtkSystemOutputRecorder(
|
|
1908
|
+
normalizeOutputBufferBytes(
|
|
1909
|
+
options.systemOutputBufferBytes,
|
|
1910
|
+
"systemOutputBufferBytes"
|
|
1911
|
+
)
|
|
1912
|
+
);
|
|
1913
|
+
sessionPromise = startDriverSession(
|
|
1914
|
+
options,
|
|
1915
|
+
createSystemOutputSink(systemOutputRecorder, options.onSystemOutput)
|
|
1916
|
+
);
|
|
1917
|
+
}
|
|
1333
1918
|
return sessionPromise;
|
|
1334
1919
|
};
|
|
1335
|
-
const
|
|
1920
|
+
const trackOutputSubscription = (subscription) => {
|
|
1921
|
+
let tracked;
|
|
1922
|
+
tracked = {
|
|
1923
|
+
dispose: () => {
|
|
1924
|
+
if (!outputSubscriptions.delete(tracked)) {
|
|
1925
|
+
return;
|
|
1926
|
+
}
|
|
1927
|
+
subscription.dispose();
|
|
1928
|
+
}
|
|
1929
|
+
};
|
|
1930
|
+
outputSubscriptions.add(tracked);
|
|
1931
|
+
return tracked;
|
|
1932
|
+
};
|
|
1933
|
+
const disposeOutputSubscriptions = () => {
|
|
1934
|
+
for (const subscription of [...outputSubscriptions]) {
|
|
1935
|
+
subscription.dispose();
|
|
1936
|
+
}
|
|
1937
|
+
};
|
|
1938
|
+
const launch = async (args, launchOptions) => {
|
|
1336
1939
|
const session = await ensureSession();
|
|
1337
|
-
const
|
|
1338
|
-
|
|
1339
|
-
|
|
1940
|
+
const onOutput = launchOptions?.onOutput;
|
|
1941
|
+
const outputScopeId = onOutput === void 0 ? null : createOutputScopeId();
|
|
1942
|
+
const outputSubscription = outputScopeId === null ? void 0 : trackOutputSubscription(
|
|
1943
|
+
session.subscribe("app.output", outputScopeId, (value) => {
|
|
1944
|
+
onOutput?.(value);
|
|
1945
|
+
})
|
|
1946
|
+
);
|
|
1947
|
+
const payload = createLaunchPayload(
|
|
1948
|
+
options,
|
|
1949
|
+
args ?? [],
|
|
1950
|
+
launchOptions,
|
|
1951
|
+
outputScopeId
|
|
1340
1952
|
);
|
|
1341
|
-
|
|
1953
|
+
try {
|
|
1954
|
+
const appRef = await session.request(
|
|
1955
|
+
"launcher.launch",
|
|
1956
|
+
payload
|
|
1957
|
+
);
|
|
1958
|
+
return createProxyGtkApp(session, appRef, outputSubscription);
|
|
1959
|
+
} catch (error) {
|
|
1960
|
+
outputSubscription?.dispose();
|
|
1961
|
+
throw error;
|
|
1962
|
+
}
|
|
1342
1963
|
};
|
|
1964
|
+
const environment = async () => {
|
|
1965
|
+
const payload = createEnvironmentPayload(options);
|
|
1966
|
+
const session = await ensureSession();
|
|
1967
|
+
return wireEnvironmentToGtkAppEnvironment(
|
|
1968
|
+
await session.request(
|
|
1969
|
+
"launcher.environment",
|
|
1970
|
+
payload
|
|
1971
|
+
)
|
|
1972
|
+
);
|
|
1973
|
+
};
|
|
1974
|
+
const systemOutput = () => Promise.resolve(systemOutputRecorder.snapshot());
|
|
1343
1975
|
const release = async () => {
|
|
1344
1976
|
const releasingSession = sessionPromise;
|
|
1345
1977
|
sessionPromise = void 0;
|
|
@@ -1347,11 +1979,17 @@ const createDriverBackedGtkAppLauncher = (options) => {
|
|
|
1347
1979
|
return;
|
|
1348
1980
|
}
|
|
1349
1981
|
const session = await releasingSession;
|
|
1350
|
-
|
|
1982
|
+
try {
|
|
1983
|
+
await session.release();
|
|
1984
|
+
} finally {
|
|
1985
|
+
disposeOutputSubscriptions();
|
|
1986
|
+
}
|
|
1351
1987
|
};
|
|
1352
1988
|
return {
|
|
1989
|
+
environment,
|
|
1353
1990
|
launch,
|
|
1354
1991
|
release,
|
|
1992
|
+
systemOutput,
|
|
1355
1993
|
[Symbol.asyncDispose]: release
|
|
1356
1994
|
};
|
|
1357
1995
|
};
|
|
@@ -1758,6 +2396,9 @@ const createImageInfoOperation = (handle) => async () => {
|
|
|
1758
2396
|
capture: async () => nativeCaptureBounds(info.bounds)
|
|
1759
2397
|
};
|
|
1760
2398
|
};
|
|
2399
|
+
const createBoundsOperation = (handle) => async () => nativeBounds(handle);
|
|
2400
|
+
const createResizeHintsOperation = (handle) => async () => nativeResizeHints(handle);
|
|
2401
|
+
const createX11InfoOperation = (handle) => async () => nativeX11Info(handle);
|
|
1761
2402
|
const createValueOperation = (handle) => async () => nativeValueInfo(handle).value;
|
|
1762
2403
|
const createSetValueOperation = (handle) => async (value) => {
|
|
1763
2404
|
assertFiniteNumber("value", value);
|
|
@@ -1929,7 +2570,10 @@ const createGtkElement = (handle) => {
|
|
|
1929
2570
|
return {
|
|
1930
2571
|
...common,
|
|
1931
2572
|
kind: "window",
|
|
1932
|
-
|
|
2573
|
+
bounds: createBoundsOperation(handle),
|
|
2574
|
+
...createChildContainerOperations(handle, void 0),
|
|
2575
|
+
resizeHints: createResizeHintsOperation(handle),
|
|
2576
|
+
x11Info: createX11InfoOperation(handle)
|
|
1933
2577
|
};
|
|
1934
2578
|
case "button":
|
|
1935
2579
|
return { ...common, kind: "button", click: createClickOperation(handle) };
|
|
@@ -2257,30 +2901,70 @@ const createGtkAppEnvironment = (baseEnv, overrides) => {
|
|
|
2257
2901
|
const launchGtkApp = (appPath, args, options) => {
|
|
2258
2902
|
const _args = args ?? [];
|
|
2259
2903
|
const _timeoutMs = options?.timeoutMs ?? 1e4;
|
|
2904
|
+
const outputBufferBytes = normalizeOutputBufferBytes(
|
|
2905
|
+
options?.outputBufferBytes
|
|
2906
|
+
);
|
|
2907
|
+
const outputRecorder = createGtkAppOutputRecorder(outputBufferBytes);
|
|
2908
|
+
const appEnvironment = createGtkAppEnvironment(process.env, options?.env);
|
|
2260
2909
|
const child = spawn(appPath, [..._args], {
|
|
2261
|
-
env:
|
|
2910
|
+
env: appEnvironment,
|
|
2262
2911
|
stdio: "pipe"
|
|
2263
2912
|
});
|
|
2913
|
+
let resolveClosed;
|
|
2914
|
+
const closedPromise = new Promise((resolve2) => {
|
|
2915
|
+
resolveClosed = resolve2;
|
|
2916
|
+
});
|
|
2264
2917
|
const state = {
|
|
2265
2918
|
atspiReadiness: "missing-bus-name",
|
|
2266
2919
|
atspiReady: false,
|
|
2920
|
+
closed: false,
|
|
2921
|
+
closedPromise,
|
|
2267
2922
|
exitCode: null,
|
|
2268
2923
|
exitSignal: null,
|
|
2269
2924
|
process: child,
|
|
2925
|
+
released: false,
|
|
2270
2926
|
stderr: [],
|
|
2271
2927
|
stdout: []
|
|
2272
2928
|
};
|
|
2929
|
+
const notifyOutput = (stream, chunk) => {
|
|
2930
|
+
notifyGtkAppOutput(options?.onOutput, outputRecorder.append(stream, chunk));
|
|
2931
|
+
};
|
|
2932
|
+
const flushOutput = (stream) => {
|
|
2933
|
+
notifyGtkAppOutput(options?.onOutput, outputRecorder.flush(stream));
|
|
2934
|
+
};
|
|
2273
2935
|
child.stdout.on("data", (chunk) => {
|
|
2274
2936
|
appendOutput(state.stdout, chunk);
|
|
2937
|
+
notifyOutput("stdout", chunk);
|
|
2275
2938
|
});
|
|
2276
2939
|
child.stderr.on("data", (chunk) => {
|
|
2277
2940
|
appendOutput(state.stderr, chunk);
|
|
2941
|
+
notifyOutput("stderr", chunk);
|
|
2942
|
+
});
|
|
2943
|
+
child.stdout.on("end", () => {
|
|
2944
|
+
flushOutput("stdout");
|
|
2945
|
+
});
|
|
2946
|
+
child.stderr.on("end", () => {
|
|
2947
|
+
flushOutput("stderr");
|
|
2278
2948
|
});
|
|
2279
2949
|
child.on("exit", (code, signal) => {
|
|
2280
2950
|
state.exitCode = code;
|
|
2281
2951
|
state.exitSignal = signal;
|
|
2282
2952
|
});
|
|
2953
|
+
child.on("close", (code, signal) => {
|
|
2954
|
+
if (state.exitCode === null && state.exitSignal === null) {
|
|
2955
|
+
state.exitCode = code;
|
|
2956
|
+
state.exitSignal = signal;
|
|
2957
|
+
}
|
|
2958
|
+
flushOutput("stdout");
|
|
2959
|
+
flushOutput("stderr");
|
|
2960
|
+
state.closed = true;
|
|
2961
|
+
resolveClosed?.();
|
|
2962
|
+
});
|
|
2283
2963
|
const release = async () => {
|
|
2964
|
+
if (state.released) {
|
|
2965
|
+
return;
|
|
2966
|
+
}
|
|
2967
|
+
state.released = true;
|
|
2284
2968
|
if (state.exitCode !== null || state.exitSignal !== null) {
|
|
2285
2969
|
return;
|
|
2286
2970
|
}
|
|
@@ -2294,6 +2978,11 @@ const launchGtkApp = (appPath, args, options) => {
|
|
|
2294
2978
|
await delay$1(25);
|
|
2295
2979
|
}
|
|
2296
2980
|
};
|
|
2981
|
+
const assertNotReleased = () => {
|
|
2982
|
+
if (state.released) {
|
|
2983
|
+
throw createGtkAppExitedError("GTK application has been released.");
|
|
2984
|
+
}
|
|
2985
|
+
};
|
|
2297
2986
|
const findById = async (id) => {
|
|
2298
2987
|
const startedAt = Date.now();
|
|
2299
2988
|
const timeoutMs = effectiveWaitTimeoutMs(_timeoutMs);
|
|
@@ -2401,6 +3090,7 @@ const launchGtkApp = (appPath, args, options) => {
|
|
|
2401
3090
|
};
|
|
2402
3091
|
const app = {
|
|
2403
3092
|
capture: async () => {
|
|
3093
|
+
assertNotReleased();
|
|
2404
3094
|
assertProcessRunning(state, appPath);
|
|
2405
3095
|
try {
|
|
2406
3096
|
return nativeCaptureScreen();
|
|
@@ -2408,6 +3098,21 @@ const launchGtkApp = (appPath, args, options) => {
|
|
|
2408
3098
|
throw normalizeNativeError(error);
|
|
2409
3099
|
}
|
|
2410
3100
|
},
|
|
3101
|
+
environment: async () => {
|
|
3102
|
+
assertNotReleased();
|
|
3103
|
+
assertProcessRunning(state, appPath);
|
|
3104
|
+
return { ...appEnvironment };
|
|
3105
|
+
},
|
|
3106
|
+
output: async () => {
|
|
3107
|
+
assertNotReleased();
|
|
3108
|
+
if ((state.exitCode !== null || state.exitSignal !== null) && !state.closed) {
|
|
3109
|
+
await state.closedPromise;
|
|
3110
|
+
}
|
|
3111
|
+
return outputRecorder.snapshot(
|
|
3112
|
+
state.exitCode,
|
|
3113
|
+
state.exitSignal === null ? null : state.exitSignal
|
|
3114
|
+
);
|
|
3115
|
+
},
|
|
2411
3116
|
findById,
|
|
2412
3117
|
findByPath,
|
|
2413
3118
|
getById,
|
|
@@ -2476,4 +3181,4 @@ export {
|
|
|
2476
3181
|
createGtkAppEnvironment as c,
|
|
2477
3182
|
launchGtkApp as l
|
|
2478
3183
|
};
|
|
2479
|
-
//# sourceMappingURL=launchGtkApp-
|
|
3184
|
+
//# sourceMappingURL=launchGtkApp-EI6OIpPI.js.map
|