gestament 0.4.0 → 0.6.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 +20 -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 +103 -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 +103 -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.d.ts +2 -2
- 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-Bst1BFbD.js → launchGtkApp-B3FMzhrc.js} +741 -74
- package/dist/launchGtkApp-B3FMzhrc.js.map +1 -0
- package/dist/{launchGtkApp-BfELuV-H.cjs → launchGtkApp-OggUtTTC.cjs} +743 -76
- package/dist/launchGtkApp-OggUtTTC.cjs.map +1 -0
- package/dist/launchGtkApp.d.ts +2 -2
- package/dist/launchGtkApp.d.ts.map +1 -1
- package/dist/launcherDriverProtocol.d.ts +37 -4
- package/dist/launcherDriverProtocol.d.ts.map +1 -1
- package/dist/{native-C6MsIBNF.js → native-C2tzSvVW.js} +25 -11
- package/dist/native-C2tzSvVW.js.map +1 -0
- package/dist/{native-BUWDWMBB.cjs → native-Xz0gjKti.cjs} +20 -6
- package/dist/native-Xz0gjKti.cjs.map +1 -0
- package/dist/native.d.ts +8 -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-BQE2KNaa.js +5 -0
- package/dist/packageMetadata-BQE2KNaa.js.map +1 -0
- package/dist/packageMetadata-BqAHZ5WO.cjs +4 -0
- package/dist/packageMetadata-BqAHZ5WO.cjs.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 +206 -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-BfELuV-H.cjs.map +0 -1
- package/dist/launchGtkApp-Bst1BFbD.js.map +0 -1
- package/dist/native-BUWDWMBB.cjs.map +0 -1
- package/dist/native-C6MsIBNF.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 nativeX11Info, G as nativeResizeHints, H 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 nativeSetWindowBounds, I as nativeResizeWindow, J as nativeMoveWindow, K as nativeBounds, L as nativeCapture, M as nativeTableCellAt, N as nativeFindAnyById, O as nativeTrayItems, P as nativeWindowCount, Q as nativeWindowAt, a as nativeCaptureScreen, R as nativeFindById, S as nativeProcessAtspiReadiness } from "./native-C2tzSvVW.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,6 +245,9 @@ 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;
|
|
25
253
|
const sessionOwnedEnvironmentKeys = [
|
|
@@ -33,6 +261,7 @@ const sessionOwnedEnvironmentKeys = [
|
|
|
33
261
|
"GESTAMENT_XVFB_ACTIVE",
|
|
34
262
|
"XDG_SESSION_TYPE"
|
|
35
263
|
];
|
|
264
|
+
let outputScopeCounter = 0;
|
|
36
265
|
let socketCounter = 0;
|
|
37
266
|
const leasedDisplayNumbers = /* @__PURE__ */ new Set();
|
|
38
267
|
const idleXvfbByKey = /* @__PURE__ */ new Map();
|
|
@@ -49,6 +278,12 @@ const appendOutput$1 = (lines, chunk) => {
|
|
|
49
278
|
lines.splice(0, lines.length - 40);
|
|
50
279
|
}
|
|
51
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
|
+
};
|
|
52
287
|
const unrefHandle = (handle) => {
|
|
53
288
|
const maybeRefHandle = handle;
|
|
54
289
|
if (typeof maybeRefHandle.unref === "function") {
|
|
@@ -67,6 +302,41 @@ ${stdoutText}
|
|
|
67
302
|
stderr:
|
|
68
303
|
${stderrText}`;
|
|
69
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;
|
|
70
340
|
const getHostDisplayState = () => ({
|
|
71
341
|
display: process.env.DISPLAY,
|
|
72
342
|
waylandDisplay: process.env.WAYLAND_DISPLAY
|
|
@@ -92,6 +362,63 @@ const resolveDisplay = (display) => {
|
|
|
92
362
|
);
|
|
93
363
|
};
|
|
94
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
|
+
};
|
|
95
422
|
const resolvePoolLimit = (name, value, defaultValue) => {
|
|
96
423
|
if (value === void 0) {
|
|
97
424
|
return defaultValue;
|
|
@@ -239,6 +566,7 @@ const retainIdleXvfb = async (xvfb, limits) => {
|
|
|
239
566
|
await terminateXvfb(xvfb);
|
|
240
567
|
return;
|
|
241
568
|
}
|
|
569
|
+
xvfb.systemOutputSink = void 0;
|
|
242
570
|
xvfb.lastUsedAt = Date.now();
|
|
243
571
|
pushArrayEntry(idleXvfbByKey, xvfb.key, xvfb);
|
|
244
572
|
await trimIdleXvfbKey(xvfb.key, limits.maxIdlePerKey);
|
|
@@ -249,6 +577,8 @@ const retainIdleAllSession = async (session, limits) => {
|
|
|
249
577
|
await session.session.terminate();
|
|
250
578
|
return;
|
|
251
579
|
}
|
|
580
|
+
session.session.setSystemOutputSink(void 0);
|
|
581
|
+
session.xvfb.systemOutputSink = void 0;
|
|
252
582
|
session.lastUsedAt = Date.now();
|
|
253
583
|
session.xvfb.lastUsedAt = session.lastUsedAt;
|
|
254
584
|
pushArrayEntry(idleAllByKey, session.key, session);
|
|
@@ -461,7 +791,7 @@ const installPoolCleanup = () => {
|
|
|
461
791
|
}
|
|
462
792
|
});
|
|
463
793
|
};
|
|
464
|
-
const spawnDirectXvfb = async (screen) => {
|
|
794
|
+
const spawnDirectXvfb = async (screen, systemOutputSink) => {
|
|
465
795
|
installPoolCleanup();
|
|
466
796
|
for (let displayNumber = firstPooledDisplayNumber; displayNumber <= lastPooledDisplayNumber; displayNumber += 1) {
|
|
467
797
|
if (!isDisplayNumberAvailable(displayNumber)) {
|
|
@@ -478,11 +808,30 @@ const spawnDirectXvfb = async (screen) => {
|
|
|
478
808
|
stdio: ["ignore", "pipe", "pipe"]
|
|
479
809
|
}
|
|
480
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
|
+
};
|
|
481
822
|
child.stdout.on("data", (chunk) => {
|
|
482
823
|
appendOutput$1(stdout, chunk);
|
|
824
|
+
appendSystemOutput(xvfb.systemOutputSink, "xvfb", "stdout", chunk);
|
|
483
825
|
});
|
|
484
826
|
child.stderr.on("data", (chunk) => {
|
|
485
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");
|
|
486
835
|
});
|
|
487
836
|
child.unref();
|
|
488
837
|
unrefHandle(child.stdout);
|
|
@@ -505,22 +854,10 @@ const spawnDirectXvfb = async (screen) => {
|
|
|
505
854
|
});
|
|
506
855
|
})
|
|
507
856
|
]);
|
|
508
|
-
const xvfb = {
|
|
509
|
-
child,
|
|
510
|
-
display: `:${displayNumber}`,
|
|
511
|
-
displayNumber,
|
|
512
|
-
key: screen,
|
|
513
|
-
lastUsedAt: Date.now(),
|
|
514
|
-
screen,
|
|
515
|
-
stderr,
|
|
516
|
-
stdout
|
|
517
|
-
};
|
|
518
857
|
directXvfbs.add(xvfb);
|
|
519
858
|
return xvfb;
|
|
520
859
|
} catch (error) {
|
|
521
|
-
killXvfbNow(
|
|
522
|
-
child
|
|
523
|
-
});
|
|
860
|
+
killXvfbNow(xvfb);
|
|
524
861
|
leasedDisplayNumbers.delete(displayNumber);
|
|
525
862
|
const message = error instanceof Error ? error.message : String(error);
|
|
526
863
|
if (message.includes("ENOENT")) {
|
|
@@ -548,22 +885,24 @@ const terminateXvfb = async (xvfb) => {
|
|
|
548
885
|
await delay(25);
|
|
549
886
|
}
|
|
550
887
|
}
|
|
888
|
+
xvfb.systemOutputSink = void 0;
|
|
551
889
|
leasedDisplayNumbers.delete(xvfb.displayNumber);
|
|
552
890
|
};
|
|
553
|
-
const leaseXvfb = async (screen) => {
|
|
891
|
+
const leaseXvfb = async (screen, systemOutputSink) => {
|
|
554
892
|
for (; ; ) {
|
|
555
893
|
const idle = popArrayEntry(idleXvfbByKey, screen);
|
|
556
894
|
if (idle === void 0) {
|
|
557
|
-
return spawnDirectXvfb(screen);
|
|
895
|
+
return spawnDirectXvfb(screen, systemOutputSink);
|
|
558
896
|
}
|
|
559
897
|
if (idle.child.exitCode === null && idle.child.signalCode === null) {
|
|
560
898
|
idle.lastUsedAt = Date.now();
|
|
899
|
+
idle.systemOutputSink = systemOutputSink;
|
|
561
900
|
return idle;
|
|
562
901
|
}
|
|
563
902
|
await terminateXvfb(idle);
|
|
564
903
|
}
|
|
565
904
|
};
|
|
566
|
-
const
|
|
905
|
+
const runXvfbProbeOnce = (xvfb, timeoutMs) => new Promise((resolveProbe, rejectProbe) => {
|
|
567
906
|
const probePath = resolveXvfbPoolProbePath();
|
|
568
907
|
const stdout = [];
|
|
569
908
|
const stderr = [];
|
|
@@ -583,12 +922,32 @@ const runXvfbProbe = (xvfb) => new Promise((resolveProbe, rejectProbe) => {
|
|
|
583
922
|
env,
|
|
584
923
|
stdio: ["ignore", "pipe", "pipe"]
|
|
585
924
|
});
|
|
586
|
-
|
|
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(() => {
|
|
587
948
|
child.kill("SIGKILL");
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
);
|
|
591
|
-
}, xvfbPoolProbeTimeoutMs);
|
|
949
|
+
rejectOnce(createXvfbProbeError("Timed out probing Xvfb pool.", false));
|
|
950
|
+
}, timeoutMs);
|
|
592
951
|
child.stdout.on("data", (chunk) => {
|
|
593
952
|
appendOutput$1(stdout, chunk);
|
|
594
953
|
});
|
|
@@ -596,42 +955,71 @@ const runXvfbProbe = (xvfb) => new Promise((resolveProbe, rejectProbe) => {
|
|
|
596
955
|
appendOutput$1(stderr, chunk);
|
|
597
956
|
});
|
|
598
957
|
child.once("error", (error) => {
|
|
599
|
-
|
|
600
|
-
rejectProbe(error);
|
|
958
|
+
rejectOnce(error);
|
|
601
959
|
});
|
|
602
960
|
child.once("exit", (code, signal) => {
|
|
603
|
-
clearTimeout(timeout);
|
|
604
961
|
if (code !== 0) {
|
|
605
|
-
|
|
606
|
-
|
|
962
|
+
const stderrText = stderr.join("");
|
|
963
|
+
rejectOnce(
|
|
964
|
+
createXvfbProbeError(
|
|
607
965
|
`Xvfb pool probe failed: code=${String(code)}, signal=${String(
|
|
608
966
|
signal
|
|
609
|
-
)}` + formatOutputTail(stdout, stderr)
|
|
967
|
+
)}` + formatOutputTail(stdout, stderr),
|
|
968
|
+
isRetryableXvfbProbeExit(stderrText)
|
|
610
969
|
)
|
|
611
970
|
);
|
|
612
971
|
return;
|
|
613
972
|
}
|
|
614
973
|
const output = stdout.join("").trim().split("\n").at(-1);
|
|
615
974
|
if (output === void 0 || output.length === 0) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
"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
|
|
619
979
|
)
|
|
620
980
|
);
|
|
621
981
|
return;
|
|
622
982
|
}
|
|
623
983
|
try {
|
|
624
|
-
|
|
984
|
+
resolveOnce(JSON.parse(output));
|
|
625
985
|
} catch (error) {
|
|
626
986
|
const message = error instanceof Error ? error.message : String(error);
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
`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
|
|
630
991
|
)
|
|
631
992
|
);
|
|
632
993
|
}
|
|
633
994
|
});
|
|
634
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
|
+
};
|
|
635
1023
|
const cleanCheckXvfb = async (xvfb, allowedMappedWindowCount) => {
|
|
636
1024
|
try {
|
|
637
1025
|
const probe = await runXvfbProbe(xvfb);
|
|
@@ -650,7 +1038,7 @@ const returnXvfbToPool = async (xvfb, limits) => {
|
|
|
650
1038
|
};
|
|
651
1039
|
const allPoolKey = (xvfb) => `${xvfb.screen}
|
|
652
1040
|
${xvfb.trayHost ? "tray" : "no-tray"}`;
|
|
653
|
-
const spawnDriverProcess = (driverPath, socketPath, effective, xvfb) => {
|
|
1041
|
+
const spawnDriverProcess = (driverPath, socketPath, effective, xvfb, systemOutputSink) => {
|
|
654
1042
|
const driverArgs = [
|
|
655
1043
|
"--socket",
|
|
656
1044
|
socketPath,
|
|
@@ -683,14 +1071,46 @@ const spawnDriverProcess = (driverPath, socketPath, effective, xvfb) => {
|
|
|
683
1071
|
env,
|
|
684
1072
|
stdio: ["ignore", "pipe", "pipe"]
|
|
685
1073
|
});
|
|
1074
|
+
const processState = {
|
|
1075
|
+
child,
|
|
1076
|
+
commandLine: [command.bin, ...command.args].join(" "),
|
|
1077
|
+
stderr,
|
|
1078
|
+
stdout,
|
|
1079
|
+
systemOutputSink
|
|
1080
|
+
};
|
|
686
1081
|
child.stdout.on("data", (chunk) => {
|
|
687
1082
|
appendOutput$1(stdout, chunk);
|
|
1083
|
+
appendSystemOutput(
|
|
1084
|
+
processState.systemOutputSink,
|
|
1085
|
+
"launcher-driver",
|
|
1086
|
+
"stdout",
|
|
1087
|
+
chunk
|
|
1088
|
+
);
|
|
688
1089
|
});
|
|
689
1090
|
child.stderr.on("data", (chunk) => {
|
|
690
1091
|
appendOutput$1(stderr, chunk);
|
|
1092
|
+
appendSystemOutput(
|
|
1093
|
+
processState.systemOutputSink,
|
|
1094
|
+
"launcher-driver",
|
|
1095
|
+
"stderr",
|
|
1096
|
+
chunk
|
|
1097
|
+
);
|
|
1098
|
+
});
|
|
1099
|
+
child.stdout.once("end", () => {
|
|
1100
|
+
flushSystemOutput(
|
|
1101
|
+
processState.systemOutputSink,
|
|
1102
|
+
"launcher-driver",
|
|
1103
|
+
"stdout"
|
|
1104
|
+
);
|
|
691
1105
|
});
|
|
692
|
-
|
|
693
|
-
|
|
1106
|
+
child.stderr.once("end", () => {
|
|
1107
|
+
flushSystemOutput(
|
|
1108
|
+
processState.systemOutputSink,
|
|
1109
|
+
"launcher-driver",
|
|
1110
|
+
"stderr"
|
|
1111
|
+
);
|
|
1112
|
+
});
|
|
1113
|
+
return processState;
|
|
694
1114
|
};
|
|
695
1115
|
const listenOnSocket = (server, socketPath) => new Promise((resolveListen, rejectListen) => {
|
|
696
1116
|
const rejectFromError = (error) => {
|
|
@@ -769,7 +1189,13 @@ const waitForDriverReady = (server, processState) => new Promise((resolveReady,
|
|
|
769
1189
|
const line = input.slice(0, newlineIndex);
|
|
770
1190
|
input = input.slice(newlineIndex + 1);
|
|
771
1191
|
try {
|
|
772
|
-
|
|
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) {
|
|
773
1199
|
if (settled) {
|
|
774
1200
|
return;
|
|
775
1201
|
}
|
|
@@ -847,6 +1273,7 @@ const decodeCapture = (capture) => ({
|
|
|
847
1273
|
visibleBounds: capture.visibleBounds
|
|
848
1274
|
});
|
|
849
1275
|
const createDriverSession = (socket, bufferedInput, processState, tempDirectory, poolOptions) => {
|
|
1276
|
+
const eventHandlers = /* @__PURE__ */ new Map();
|
|
850
1277
|
const pending = /* @__PURE__ */ new Map();
|
|
851
1278
|
let input = bufferedInput;
|
|
852
1279
|
let nextRequestId = 1;
|
|
@@ -866,8 +1293,34 @@ const createDriverSession = (socket, bufferedInput, processState, tempDirectory,
|
|
|
866
1293
|
closed = true;
|
|
867
1294
|
rejectPending(createGtkAppExitedError("Launcher driver has exited."));
|
|
868
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
|
+
};
|
|
869
1317
|
const handleResponse = (line) => {
|
|
870
|
-
const
|
|
1318
|
+
const message = JSON.parse(line);
|
|
1319
|
+
if (isDriverEventMessage(message)) {
|
|
1320
|
+
handleEvent(message);
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
const response = message;
|
|
871
1324
|
const entry = pending.get(response.id);
|
|
872
1325
|
if (entry === void 0) {
|
|
873
1326
|
return;
|
|
@@ -942,6 +1395,28 @@ const createDriverSession = (socket, bufferedInput, processState, tempDirectory,
|
|
|
942
1395
|
});
|
|
943
1396
|
});
|
|
944
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
|
+
};
|
|
945
1420
|
const waitForExit = async () => {
|
|
946
1421
|
const startedAt = Date.now();
|
|
947
1422
|
while (processState.child.exitCode === null && processState.child.signalCode === null) {
|
|
@@ -1026,10 +1501,10 @@ const createDriverSession = (socket, bufferedInput, processState, tempDirectory,
|
|
|
1026
1501
|
poolOptions.limits
|
|
1027
1502
|
);
|
|
1028
1503
|
};
|
|
1029
|
-
session = { release, request, terminate };
|
|
1504
|
+
session = { release, request, setSystemOutputSink, subscribe, terminate };
|
|
1030
1505
|
return session;
|
|
1031
1506
|
};
|
|
1032
|
-
const startFreshDriverSession = async (effective, xvfb, poolOptions) => {
|
|
1507
|
+
const startFreshDriverSession = async (effective, xvfb, poolOptions, systemOutputSink) => {
|
|
1033
1508
|
const driverPath = resolveDriverPath();
|
|
1034
1509
|
const tempDirectory = mkdtempSync(
|
|
1035
1510
|
join(tmpdir(), `gestament-launcher-${process.pid}-${socketCounter}-`)
|
|
@@ -1041,7 +1516,13 @@ const startFreshDriverSession = async (effective, xvfb, poolOptions) => {
|
|
|
1041
1516
|
try {
|
|
1042
1517
|
await listenOnSocket(server, socketPath);
|
|
1043
1518
|
server.unref();
|
|
1044
|
-
processState = spawnDriverProcess(
|
|
1519
|
+
processState = spawnDriverProcess(
|
|
1520
|
+
driverPath,
|
|
1521
|
+
socketPath,
|
|
1522
|
+
effective,
|
|
1523
|
+
xvfb,
|
|
1524
|
+
systemOutputSink
|
|
1525
|
+
);
|
|
1045
1526
|
const connection = await waitForDriverReady(server, processState);
|
|
1046
1527
|
server.close();
|
|
1047
1528
|
const resolvedPoolOptions = poolOptions.mode === "all" && xvfb !== void 0 ? {
|
|
@@ -1067,7 +1548,7 @@ const startFreshDriverSession = async (effective, xvfb, poolOptions) => {
|
|
|
1067
1548
|
throw error;
|
|
1068
1549
|
}
|
|
1069
1550
|
};
|
|
1070
|
-
const startDriverSession = async (options) => {
|
|
1551
|
+
const startDriverSession = async (options, systemOutputSink) => {
|
|
1071
1552
|
const display = resolveDisplay(options.display);
|
|
1072
1553
|
const xvfbOptions = resolveXvfbOptions(options);
|
|
1073
1554
|
const effective = resolveEffectiveDisplay(display, xvfbOptions);
|
|
@@ -1075,7 +1556,8 @@ const startDriverSession = async (options) => {
|
|
|
1075
1556
|
return startFreshDriverSession(
|
|
1076
1557
|
effective,
|
|
1077
1558
|
void 0,
|
|
1078
|
-
emptyDriverSessionPoolOptions()
|
|
1559
|
+
emptyDriverSessionPoolOptions(),
|
|
1560
|
+
systemOutputSink
|
|
1079
1561
|
);
|
|
1080
1562
|
}
|
|
1081
1563
|
const pool = effective.xvfb.pool;
|
|
@@ -1083,18 +1565,24 @@ const startDriverSession = async (options) => {
|
|
|
1083
1565
|
return startFreshDriverSession(
|
|
1084
1566
|
effective,
|
|
1085
1567
|
void 0,
|
|
1086
|
-
emptyDriverSessionPoolOptions()
|
|
1568
|
+
emptyDriverSessionPoolOptions(),
|
|
1569
|
+
systemOutputSink
|
|
1087
1570
|
);
|
|
1088
1571
|
}
|
|
1089
1572
|
if (pool.type === "xvfb") {
|
|
1090
|
-
const xvfb2 = await leaseXvfb(effective.xvfb.screen);
|
|
1091
|
-
return startFreshDriverSession(
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
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
|
+
);
|
|
1098
1586
|
}
|
|
1099
1587
|
const key = allPoolKey(effective.xvfb);
|
|
1100
1588
|
for (; ; ) {
|
|
@@ -1105,20 +1593,27 @@ const startDriverSession = async (options) => {
|
|
|
1105
1593
|
if (idle.xvfb.child.exitCode === null && idle.xvfb.child.signalCode === null) {
|
|
1106
1594
|
idle.lastUsedAt = Date.now();
|
|
1107
1595
|
idle.xvfb.lastUsedAt = idle.lastUsedAt;
|
|
1596
|
+
idle.xvfb.systemOutputSink = systemOutputSink;
|
|
1597
|
+
idle.session.setSystemOutputSink(systemOutputSink);
|
|
1108
1598
|
return idle.session;
|
|
1109
1599
|
}
|
|
1110
1600
|
await idle.session.terminate().catch(() => void 0);
|
|
1111
1601
|
}
|
|
1112
|
-
const xvfb = await leaseXvfb(effective.xvfb.screen);
|
|
1113
|
-
return startFreshDriverSession(
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
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
|
+
);
|
|
1120
1615
|
};
|
|
1121
|
-
const createLaunchPayload = (options, args) => {
|
|
1616
|
+
const createLaunchPayload = (options, args, launchOptions, outputScopeId) => {
|
|
1122
1617
|
const display = resolveDisplay(options.display);
|
|
1123
1618
|
const xvfb = resolveXvfbOptions(options);
|
|
1124
1619
|
const effective = resolveEffectiveDisplay(display, xvfb);
|
|
@@ -1126,6 +1621,11 @@ const createLaunchPayload = (options, args) => {
|
|
|
1126
1621
|
appPath: options.appPath,
|
|
1127
1622
|
args: [...options.args ?? [], ...args],
|
|
1128
1623
|
env: resolveLauncherEnvironment(options, effective),
|
|
1624
|
+
outputBufferBytes: resolveOutputBufferBytes(
|
|
1625
|
+
options.outputBufferBytes,
|
|
1626
|
+
launchOptions?.outputBufferBytes
|
|
1627
|
+
),
|
|
1628
|
+
outputScopeId,
|
|
1129
1629
|
timeoutMs: options.timeoutMs ?? null
|
|
1130
1630
|
};
|
|
1131
1631
|
};
|
|
@@ -1139,7 +1639,7 @@ const createEnvironmentPayload = (options) => {
|
|
|
1139
1639
|
};
|
|
1140
1640
|
const elementRefToProxy = (session, ref) => ref === null ? void 0 : createProxyGtkElement(session, ref);
|
|
1141
1641
|
const trayRefToProxy = (session, ref) => ref === null ? void 0 : createProxyGtkTrayItem(session, ref);
|
|
1142
|
-
const createProxyGtkApp = (session, ref) => {
|
|
1642
|
+
const createProxyGtkApp = (session, ref, outputSubscription) => {
|
|
1143
1643
|
let released = false;
|
|
1144
1644
|
const assertNotReleased = () => {
|
|
1145
1645
|
if (released) {
|
|
@@ -1158,6 +1658,7 @@ const createProxyGtkApp = (session, ref) => {
|
|
|
1158
1658
|
return;
|
|
1159
1659
|
}
|
|
1160
1660
|
released = true;
|
|
1661
|
+
outputSubscription?.dispose();
|
|
1161
1662
|
await session.request("app.release", { appId: ref.appId });
|
|
1162
1663
|
};
|
|
1163
1664
|
const app = {
|
|
@@ -1165,6 +1666,7 @@ const createProxyGtkApp = (session, ref) => {
|
|
|
1165
1666
|
environment: async () => wireEnvironmentToGtkAppEnvironment(
|
|
1166
1667
|
await appRequest("app.environment")
|
|
1167
1668
|
),
|
|
1669
|
+
output: () => appRequest("app.output"),
|
|
1168
1670
|
findById: async (id) => elementRefToProxy(
|
|
1169
1671
|
session,
|
|
1170
1672
|
await appRequest("app.findById", { id })
|
|
@@ -1278,9 +1780,23 @@ const createProxyGtkElement = (session, ref) => {
|
|
|
1278
1780
|
switch (ref.kind) {
|
|
1279
1781
|
case "window":
|
|
1280
1782
|
target.bounds = () => session.request("element.bounds", { elementId });
|
|
1783
|
+
target.moveTo = (x, y) => session.request("window.moveTo", {
|
|
1784
|
+
elementId,
|
|
1785
|
+
x,
|
|
1786
|
+
y
|
|
1787
|
+
});
|
|
1281
1788
|
target.resizeHints = () => session.request("window.resizeHints", {
|
|
1282
1789
|
elementId
|
|
1283
1790
|
});
|
|
1791
|
+
target.resizeTo = (width, height) => session.request("window.resizeTo", {
|
|
1792
|
+
elementId,
|
|
1793
|
+
height,
|
|
1794
|
+
width
|
|
1795
|
+
});
|
|
1796
|
+
target.setBounds = (bounds) => session.request("window.setBounds", {
|
|
1797
|
+
bounds,
|
|
1798
|
+
elementId
|
|
1799
|
+
});
|
|
1284
1800
|
target.x11Info = () => session.request("window.x11Info", { elementId });
|
|
1285
1801
|
addChildContainerProxyOperations(session, elementId, target);
|
|
1286
1802
|
break;
|
|
@@ -1392,19 +1908,72 @@ const createProxyGtkTrayItem = (session, ref) => {
|
|
|
1392
1908
|
};
|
|
1393
1909
|
};
|
|
1394
1910
|
const createDriverBackedGtkAppLauncher = (options) => {
|
|
1911
|
+
const outputSubscriptions = /* @__PURE__ */ new Set();
|
|
1912
|
+
let systemOutputRecorder = createGtkSystemOutputRecorder(
|
|
1913
|
+
normalizeOutputBufferBytes(
|
|
1914
|
+
options.systemOutputBufferBytes,
|
|
1915
|
+
"systemOutputBufferBytes"
|
|
1916
|
+
)
|
|
1917
|
+
);
|
|
1395
1918
|
let sessionPromise;
|
|
1396
1919
|
const ensureSession = () => {
|
|
1397
|
-
sessionPromise
|
|
1920
|
+
if (sessionPromise === void 0) {
|
|
1921
|
+
systemOutputRecorder = createGtkSystemOutputRecorder(
|
|
1922
|
+
normalizeOutputBufferBytes(
|
|
1923
|
+
options.systemOutputBufferBytes,
|
|
1924
|
+
"systemOutputBufferBytes"
|
|
1925
|
+
)
|
|
1926
|
+
);
|
|
1927
|
+
sessionPromise = startDriverSession(
|
|
1928
|
+
options,
|
|
1929
|
+
createSystemOutputSink(systemOutputRecorder, options.onSystemOutput)
|
|
1930
|
+
);
|
|
1931
|
+
}
|
|
1398
1932
|
return sessionPromise;
|
|
1399
1933
|
};
|
|
1400
|
-
const
|
|
1401
|
-
|
|
1934
|
+
const trackOutputSubscription = (subscription) => {
|
|
1935
|
+
let tracked;
|
|
1936
|
+
tracked = {
|
|
1937
|
+
dispose: () => {
|
|
1938
|
+
if (!outputSubscriptions.delete(tracked)) {
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1941
|
+
subscription.dispose();
|
|
1942
|
+
}
|
|
1943
|
+
};
|
|
1944
|
+
outputSubscriptions.add(tracked);
|
|
1945
|
+
return tracked;
|
|
1946
|
+
};
|
|
1947
|
+
const disposeOutputSubscriptions = () => {
|
|
1948
|
+
for (const subscription of [...outputSubscriptions]) {
|
|
1949
|
+
subscription.dispose();
|
|
1950
|
+
}
|
|
1951
|
+
};
|
|
1952
|
+
const launch = async (args, launchOptions) => {
|
|
1402
1953
|
const session = await ensureSession();
|
|
1403
|
-
const
|
|
1404
|
-
|
|
1405
|
-
|
|
1954
|
+
const onOutput = launchOptions?.onOutput;
|
|
1955
|
+
const outputScopeId = onOutput === void 0 ? null : createOutputScopeId();
|
|
1956
|
+
const outputSubscription = outputScopeId === null ? void 0 : trackOutputSubscription(
|
|
1957
|
+
session.subscribe("app.output", outputScopeId, (value) => {
|
|
1958
|
+
onOutput?.(value);
|
|
1959
|
+
})
|
|
1406
1960
|
);
|
|
1407
|
-
|
|
1961
|
+
const payload = createLaunchPayload(
|
|
1962
|
+
options,
|
|
1963
|
+
args ?? [],
|
|
1964
|
+
launchOptions,
|
|
1965
|
+
outputScopeId
|
|
1966
|
+
);
|
|
1967
|
+
try {
|
|
1968
|
+
const appRef = await session.request(
|
|
1969
|
+
"launcher.launch",
|
|
1970
|
+
payload
|
|
1971
|
+
);
|
|
1972
|
+
return createProxyGtkApp(session, appRef, outputSubscription);
|
|
1973
|
+
} catch (error) {
|
|
1974
|
+
outputSubscription?.dispose();
|
|
1975
|
+
throw error;
|
|
1976
|
+
}
|
|
1408
1977
|
};
|
|
1409
1978
|
const environment = async () => {
|
|
1410
1979
|
const payload = createEnvironmentPayload(options);
|
|
@@ -1416,6 +1985,7 @@ const createDriverBackedGtkAppLauncher = (options) => {
|
|
|
1416
1985
|
)
|
|
1417
1986
|
);
|
|
1418
1987
|
};
|
|
1988
|
+
const systemOutput = () => Promise.resolve(systemOutputRecorder.snapshot());
|
|
1419
1989
|
const release = async () => {
|
|
1420
1990
|
const releasingSession = sessionPromise;
|
|
1421
1991
|
sessionPromise = void 0;
|
|
@@ -1423,12 +1993,17 @@ const createDriverBackedGtkAppLauncher = (options) => {
|
|
|
1423
1993
|
return;
|
|
1424
1994
|
}
|
|
1425
1995
|
const session = await releasingSession;
|
|
1426
|
-
|
|
1996
|
+
try {
|
|
1997
|
+
await session.release();
|
|
1998
|
+
} finally {
|
|
1999
|
+
disposeOutputSubscriptions();
|
|
2000
|
+
}
|
|
1427
2001
|
};
|
|
1428
2002
|
return {
|
|
1429
2003
|
environment,
|
|
1430
2004
|
launch,
|
|
1431
2005
|
release,
|
|
2006
|
+
systemOutput,
|
|
1432
2007
|
[Symbol.asyncDispose]: release
|
|
1433
2008
|
};
|
|
1434
2009
|
};
|
|
@@ -1444,6 +2019,19 @@ const assertFiniteNumber = (name, value) => {
|
|
|
1444
2019
|
throw createGtkInvalidArgumentError(`${name} must be a finite number.`);
|
|
1445
2020
|
}
|
|
1446
2021
|
};
|
|
2022
|
+
const int32Min = -2147483648;
|
|
2023
|
+
const int32Max = 2147483647;
|
|
2024
|
+
const assertInt32 = (name, value) => {
|
|
2025
|
+
if (!Number.isInteger(value) || value < int32Min || value > int32Max) {
|
|
2026
|
+
throw createGtkInvalidArgumentError(`${name} must be a 32-bit integer.`);
|
|
2027
|
+
}
|
|
2028
|
+
};
|
|
2029
|
+
const assertPositiveInt32 = (name, value) => {
|
|
2030
|
+
assertInt32(name, value);
|
|
2031
|
+
if (value <= 0) {
|
|
2032
|
+
throw createGtkInvalidArgumentError(`${name} must be greater than zero.`);
|
|
2033
|
+
}
|
|
2034
|
+
};
|
|
1447
2035
|
const normalizeRoleName = (roleName) => roleName.trim().toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " ");
|
|
1448
2036
|
const hasInterface = (info, name) => info.interfaces.some(
|
|
1449
2037
|
(interfaceName) => interfaceName.toLowerCase() === name.toLowerCase()
|
|
@@ -1836,6 +2424,26 @@ const createImageInfoOperation = (handle) => async () => {
|
|
|
1836
2424
|
};
|
|
1837
2425
|
};
|
|
1838
2426
|
const createBoundsOperation = (handle) => async () => nativeBounds(handle);
|
|
2427
|
+
const createMoveToOperation = (handle) => async (x, y) => {
|
|
2428
|
+
assertInt32("x", x);
|
|
2429
|
+
assertInt32("y", y);
|
|
2430
|
+
return nativeMoveWindow(handle, x, y);
|
|
2431
|
+
};
|
|
2432
|
+
const createResizeToOperation = (handle) => async (width, height) => {
|
|
2433
|
+
assertPositiveInt32("width", width);
|
|
2434
|
+
assertPositiveInt32("height", height);
|
|
2435
|
+
return nativeResizeWindow(handle, width, height);
|
|
2436
|
+
};
|
|
2437
|
+
const createSetBoundsOperation = (handle) => async (bounds) => {
|
|
2438
|
+
if (typeof bounds !== "object" || bounds === null) {
|
|
2439
|
+
throw createGtkInvalidArgumentError("bounds must be an object.");
|
|
2440
|
+
}
|
|
2441
|
+
assertInt32("bounds.x", bounds.x);
|
|
2442
|
+
assertInt32("bounds.y", bounds.y);
|
|
2443
|
+
assertPositiveInt32("bounds.width", bounds.width);
|
|
2444
|
+
assertPositiveInt32("bounds.height", bounds.height);
|
|
2445
|
+
return nativeSetWindowBounds(handle, bounds);
|
|
2446
|
+
};
|
|
1839
2447
|
const createResizeHintsOperation = (handle) => async () => nativeResizeHints(handle);
|
|
1840
2448
|
const createX11InfoOperation = (handle) => async () => nativeX11Info(handle);
|
|
1841
2449
|
const createValueOperation = (handle) => async () => nativeValueInfo(handle).value;
|
|
@@ -2010,7 +2618,10 @@ const createGtkElement = (handle) => {
|
|
|
2010
2618
|
...common,
|
|
2011
2619
|
kind: "window",
|
|
2012
2620
|
bounds: createBoundsOperation(handle),
|
|
2621
|
+
moveTo: createMoveToOperation(handle),
|
|
2013
2622
|
...createChildContainerOperations(handle, void 0),
|
|
2623
|
+
resizeTo: createResizeToOperation(handle),
|
|
2624
|
+
setBounds: createSetBoundsOperation(handle),
|
|
2014
2625
|
resizeHints: createResizeHintsOperation(handle),
|
|
2015
2626
|
x11Info: createX11InfoOperation(handle)
|
|
2016
2627
|
};
|
|
@@ -2340,31 +2951,70 @@ const createGtkAppEnvironment = (baseEnv, overrides) => {
|
|
|
2340
2951
|
const launchGtkApp = (appPath, args, options) => {
|
|
2341
2952
|
const _args = args ?? [];
|
|
2342
2953
|
const _timeoutMs = options?.timeoutMs ?? 1e4;
|
|
2954
|
+
const outputBufferBytes = normalizeOutputBufferBytes(
|
|
2955
|
+
options?.outputBufferBytes
|
|
2956
|
+
);
|
|
2957
|
+
const outputRecorder = createGtkAppOutputRecorder(outputBufferBytes);
|
|
2343
2958
|
const appEnvironment = createGtkAppEnvironment(process.env, options?.env);
|
|
2344
2959
|
const child = spawn(appPath, [..._args], {
|
|
2345
2960
|
env: appEnvironment,
|
|
2346
2961
|
stdio: "pipe"
|
|
2347
2962
|
});
|
|
2963
|
+
let resolveClosed;
|
|
2964
|
+
const closedPromise = new Promise((resolve2) => {
|
|
2965
|
+
resolveClosed = resolve2;
|
|
2966
|
+
});
|
|
2348
2967
|
const state = {
|
|
2349
2968
|
atspiReadiness: "missing-bus-name",
|
|
2350
2969
|
atspiReady: false,
|
|
2970
|
+
closed: false,
|
|
2971
|
+
closedPromise,
|
|
2351
2972
|
exitCode: null,
|
|
2352
2973
|
exitSignal: null,
|
|
2353
2974
|
process: child,
|
|
2975
|
+
released: false,
|
|
2354
2976
|
stderr: [],
|
|
2355
2977
|
stdout: []
|
|
2356
2978
|
};
|
|
2979
|
+
const notifyOutput = (stream, chunk) => {
|
|
2980
|
+
notifyGtkAppOutput(options?.onOutput, outputRecorder.append(stream, chunk));
|
|
2981
|
+
};
|
|
2982
|
+
const flushOutput = (stream) => {
|
|
2983
|
+
notifyGtkAppOutput(options?.onOutput, outputRecorder.flush(stream));
|
|
2984
|
+
};
|
|
2357
2985
|
child.stdout.on("data", (chunk) => {
|
|
2358
2986
|
appendOutput(state.stdout, chunk);
|
|
2987
|
+
notifyOutput("stdout", chunk);
|
|
2359
2988
|
});
|
|
2360
2989
|
child.stderr.on("data", (chunk) => {
|
|
2361
2990
|
appendOutput(state.stderr, chunk);
|
|
2991
|
+
notifyOutput("stderr", chunk);
|
|
2992
|
+
});
|
|
2993
|
+
child.stdout.on("end", () => {
|
|
2994
|
+
flushOutput("stdout");
|
|
2995
|
+
});
|
|
2996
|
+
child.stderr.on("end", () => {
|
|
2997
|
+
flushOutput("stderr");
|
|
2362
2998
|
});
|
|
2363
2999
|
child.on("exit", (code, signal) => {
|
|
2364
3000
|
state.exitCode = code;
|
|
2365
3001
|
state.exitSignal = signal;
|
|
2366
3002
|
});
|
|
3003
|
+
child.on("close", (code, signal) => {
|
|
3004
|
+
if (state.exitCode === null && state.exitSignal === null) {
|
|
3005
|
+
state.exitCode = code;
|
|
3006
|
+
state.exitSignal = signal;
|
|
3007
|
+
}
|
|
3008
|
+
flushOutput("stdout");
|
|
3009
|
+
flushOutput("stderr");
|
|
3010
|
+
state.closed = true;
|
|
3011
|
+
resolveClosed?.();
|
|
3012
|
+
});
|
|
2367
3013
|
const release = async () => {
|
|
3014
|
+
if (state.released) {
|
|
3015
|
+
return;
|
|
3016
|
+
}
|
|
3017
|
+
state.released = true;
|
|
2368
3018
|
if (state.exitCode !== null || state.exitSignal !== null) {
|
|
2369
3019
|
return;
|
|
2370
3020
|
}
|
|
@@ -2378,6 +3028,11 @@ const launchGtkApp = (appPath, args, options) => {
|
|
|
2378
3028
|
await delay$1(25);
|
|
2379
3029
|
}
|
|
2380
3030
|
};
|
|
3031
|
+
const assertNotReleased = () => {
|
|
3032
|
+
if (state.released) {
|
|
3033
|
+
throw createGtkAppExitedError("GTK application has been released.");
|
|
3034
|
+
}
|
|
3035
|
+
};
|
|
2381
3036
|
const findById = async (id) => {
|
|
2382
3037
|
const startedAt = Date.now();
|
|
2383
3038
|
const timeoutMs = effectiveWaitTimeoutMs(_timeoutMs);
|
|
@@ -2485,6 +3140,7 @@ const launchGtkApp = (appPath, args, options) => {
|
|
|
2485
3140
|
};
|
|
2486
3141
|
const app = {
|
|
2487
3142
|
capture: async () => {
|
|
3143
|
+
assertNotReleased();
|
|
2488
3144
|
assertProcessRunning(state, appPath);
|
|
2489
3145
|
try {
|
|
2490
3146
|
return nativeCaptureScreen();
|
|
@@ -2493,9 +3149,20 @@ const launchGtkApp = (appPath, args, options) => {
|
|
|
2493
3149
|
}
|
|
2494
3150
|
},
|
|
2495
3151
|
environment: async () => {
|
|
3152
|
+
assertNotReleased();
|
|
2496
3153
|
assertProcessRunning(state, appPath);
|
|
2497
3154
|
return { ...appEnvironment };
|
|
2498
3155
|
},
|
|
3156
|
+
output: async () => {
|
|
3157
|
+
assertNotReleased();
|
|
3158
|
+
if ((state.exitCode !== null || state.exitSignal !== null) && !state.closed) {
|
|
3159
|
+
await state.closedPromise;
|
|
3160
|
+
}
|
|
3161
|
+
return outputRecorder.snapshot(
|
|
3162
|
+
state.exitCode,
|
|
3163
|
+
state.exitSignal === null ? null : state.exitSignal
|
|
3164
|
+
);
|
|
3165
|
+
},
|
|
2499
3166
|
findById,
|
|
2500
3167
|
findByPath,
|
|
2501
3168
|
getById,
|
|
@@ -2564,4 +3231,4 @@ export {
|
|
|
2564
3231
|
createGtkAppEnvironment as c,
|
|
2565
3232
|
launchGtkApp as l
|
|
2566
3233
|
};
|
|
2567
|
-
//# sourceMappingURL=launchGtkApp-
|
|
3234
|
+
//# sourceMappingURL=launchGtkApp-B3FMzhrc.js.map
|