@nyssance/tauri-hasgard 0.6.0-alpha.2 → 0.6.0-alpha.4

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/dist/index.js CHANGED
@@ -1,29 +1,27 @@
1
- import {
2
- stopProcessGroup
3
- } from "./chunk-N7YDYAKI.js";
4
-
5
1
  // src/endpoint.ts
6
- import { tmpdir } from "os";
7
- import { join } from "path";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
8
4
  function endpointPathForPlatform(name, platform, temporaryDirectory) {
9
- if (platform === "win32") return `\\\\.\\pipe\\${name}`;
5
+ if (platform === "win32")
6
+ return `\\\\.\\pipe\\${name}`;
10
7
  return join(temporaryDirectory, `${name}.sock`);
11
8
  }
12
9
  function hasgardEndpointPath(name) {
13
10
  return endpointPathForPlatform(name, process.platform, tmpdir());
14
11
  }
15
-
16
12
  // src/expect.ts
17
13
  async function poll(read, matches, timeoutMs) {
18
14
  if (!Number.isFinite(timeoutMs) || timeoutMs < 0)
19
15
  throw new RangeError("Assertion timeout must be a finite non-negative number");
20
16
  const deadline = Date.now() + timeoutMs;
21
- for (; ; ) {
17
+ for (;; ) {
22
18
  try {
23
19
  const value = await read();
24
- if (matches(value) || Date.now() >= deadline) return value;
20
+ if (matches(value) || Date.now() >= deadline)
21
+ return value;
25
22
  } catch (error) {
26
- if (Date.now() >= deadline) throw error;
23
+ if (Date.now() >= deadline)
24
+ throw error;
27
25
  }
28
26
  await new Promise((resolve) => setTimeout(resolve, Math.min(50, Math.max(0, deadline - Date.now()))));
29
27
  }
@@ -31,29 +29,22 @@ async function poll(read, matches, timeoutMs) {
31
29
  function createHasgardExpect(playwrightExpect) {
32
30
  return playwrightExpect.extend({
33
31
  async toBeVisible(locator, options = {}) {
34
- const timeout = options.timeout === void 0 ? 5e3 : options.timeout;
35
- const result = await poll(
36
- () => locator.isVisible(),
37
- (visible) => this.isNot ? !visible : visible,
38
- timeout
39
- );
32
+ const { timeout = 5000 } = options;
33
+ const result = await poll(() => locator.isVisible(), (visible) => this.isNot ? !visible : visible, timeout);
40
34
  return {
41
- pass: result === true,
35
+ pass: result,
42
36
  message: () => this.isNot ? "expected Hasgard locator not to be visible" : `expected Hasgard locator to be visible within ${timeout}ms`
43
37
  };
44
38
  },
45
39
  async toHaveText(locator, expected, options = {}) {
46
- const timeout = options.timeout === void 0 ? 5e3 : options.timeout;
40
+ const { timeout = 5000 } = options;
47
41
  const matches = (text) => {
48
- if (typeof expected === "string") return text === expected;
42
+ if (typeof expected === "string")
43
+ return text === expected;
49
44
  expected.lastIndex = 0;
50
45
  return expected.test(text);
51
46
  };
52
- const result = await poll(
53
- () => locator.textContent(),
54
- (text) => this.isNot ? !matches(text) : matches(text),
55
- timeout
56
- );
47
+ const result = await poll(() => locator.textContent(), (text) => this.isNot ? !matches(text) : matches(text), timeout);
57
48
  return {
58
49
  pass: matches(result),
59
50
  message: () => this.isNot ? `expected Hasgard locator text not to match ${String(expected)}, received ${String(result)}` : `expected Hasgard locator text to match ${String(expected)}, received ${String(result)}`
@@ -61,13 +52,46 @@ function createHasgardExpect(playwrightExpect) {
61
52
  }
62
53
  });
63
54
  }
55
+ // src/process-manager.ts
56
+ import { fork, spawn } from "node:child_process";
57
+ import { lstat } from "node:fs/promises";
58
+ import { createConnection } from "node:net";
59
+ import { setTimeout as delay2 } from "node:timers/promises";
60
+
61
+ // src/process-group.js
62
+ import { setTimeout as delay } from "node:timers/promises";
63
+ function signalGroup(pid, signal) {
64
+ try {
65
+ process.kill(-pid, signal);
66
+ return true;
67
+ } catch (error) {
68
+ if (error.code === "ESRCH")
69
+ return false;
70
+ if (signal === 0 && error.code === "EPERM")
71
+ return true;
72
+ throw error;
73
+ }
74
+ }
75
+ async function waitForExit(pid, timeoutMs) {
76
+ const deadline = Date.now() + timeoutMs;
77
+ while (signalGroup(pid, 0)) {
78
+ if (Date.now() >= deadline)
79
+ return false;
80
+ await delay(25);
81
+ }
82
+ return true;
83
+ }
84
+ async function stopProcessGroup(pid) {
85
+ signalGroup(pid, "SIGTERM");
86
+ if (!await waitForExit(pid, 5000)) {
87
+ signalGroup(pid, "SIGKILL");
88
+ if (!await waitForExit(pid, 5000))
89
+ throw new Error(`Hasgard process group ${pid} survived SIGKILL`);
90
+ }
91
+ }
64
92
 
65
93
  // src/process-manager.ts
66
- import { fork, spawn } from "child_process";
67
- import { lstat } from "fs/promises";
68
- import { createConnection } from "net";
69
- import { setTimeout as delay } from "timers/promises";
70
- var HasgardProcess = class {
94
+ class HasgardProcess {
71
95
  child;
72
96
  stdout = "";
73
97
  stderr = "";
@@ -75,28 +99,29 @@ var HasgardProcess = class {
75
99
  stopping;
76
100
  applicationPid;
77
101
  applicationExit;
78
- /** Bounded log tails and the supervisor's actual child-close result. */
79
102
  async diagnostics(waitForExitMs = 0) {
80
- if (!Number.isFinite(waitForExitMs) || waitForExitMs < 0) throw new RangeError("Invalid diagnostics wait");
103
+ if (!Number.isFinite(waitForExitMs) || waitForExitMs < 0)
104
+ throw new RangeError("Invalid diagnostics wait");
81
105
  const deadline = Date.now() + waitForExitMs;
82
106
  while (this.child && !this.applicationExit && Date.now() < deadline)
83
- await delay(Math.min(25, deadline - Date.now()));
107
+ await delay2(Math.min(25, deadline - Date.now()));
84
108
  return { pid: this.applicationPid, exit: this.applicationExit, stdout: this.stdout, stderr: this.stderr };
85
109
  }
86
110
  async start(config, socketPath) {
87
- if (this.child || this.stopping || this.starting) throw new Error("Hasgard app process is already running");
111
+ if (this.child || this.stopping || this.starting)
112
+ throw new Error("Hasgard app process is already running");
88
113
  this.starting = this.launch(config, socketPath);
89
114
  try {
90
115
  await this.starting;
91
116
  } finally {
92
- this.starting = void 0;
117
+ this.starting = undefined;
93
118
  }
94
119
  }
95
120
  async launch(config, socketPath) {
96
121
  this.stdout = "";
97
122
  this.stderr = "";
98
- this.applicationPid = void 0;
99
- this.applicationExit = void 0;
123
+ this.applicationPid = undefined;
124
+ this.applicationExit = undefined;
100
125
  if (process.platform !== "win32" && await endpointAcceptsConnections(socketPath)) {
101
126
  throw new Error(`Hasgard endpoint is already in use: ${socketPath}`);
102
127
  }
@@ -151,26 +176,28 @@ var HasgardProcess = class {
151
176
  };
152
177
  child.once("error", failed);
153
178
  child.once("exit", () => failed(new Error("Hasgard process supervisor exited")));
154
- child.on(
155
- "message",
156
- (message) => {
157
- if (message.type === "stdout") this.stdout = appendOutput(this.stdout, message.data);
158
- else if (message.type === "stderr") this.stderr = appendOutput(this.stderr, message.data);
159
- else if (message.type === "exit") this.applicationExit = { code: message.code, signal: message.signal };
160
- else if (message.type === "error") failed(Object.assign(new Error(message.message), { code: message.code }));
161
- else if (message.type === "started") {
162
- if (!Number.isSafeInteger(message.pid) || message.pid <= 1) {
163
- failed(new Error("Invalid supervised application PID"));
164
- return;
165
- }
166
- this.applicationPid = message.pid;
167
- clearTimeout(timer);
168
- resolve();
179
+ child.on("message", (message) => {
180
+ if (message.type === "stdout")
181
+ this.stdout = appendOutput(this.stdout, message.data);
182
+ else if (message.type === "stderr")
183
+ this.stderr = appendOutput(this.stderr, message.data);
184
+ else if (message.type === "exit")
185
+ this.applicationExit = { code: message.code, signal: message.signal };
186
+ else if (message.type === "error")
187
+ failed(Object.assign(new Error(message.message), { code: message.code }));
188
+ else if (message.type === "started") {
189
+ if (!Number.isSafeInteger(message.pid) || message.pid <= 1) {
190
+ failed(new Error("Invalid supervised application PID"));
191
+ return;
169
192
  }
193
+ this.applicationPid = message.pid;
194
+ clearTimeout(timer);
195
+ resolve();
170
196
  }
171
- );
197
+ });
172
198
  child.send({ ...config, env: { ...process.env, ...config.env, TAURI_HASGARD_SOCKET: socketPath } }, (error) => {
173
- if (error) failed(error);
199
+ if (error)
200
+ failed(error);
174
201
  });
175
202
  });
176
203
  await this.waitForSocket(socketPath, config.timeoutMs);
@@ -190,114 +217,120 @@ var HasgardProcess = class {
190
217
  const child = this.child;
191
218
  if (this.applicationExit) {
192
219
  const { code, signal } = this.applicationExit;
193
- throw new Error(
194
- `Tauri process exited with ${signal ? `signal ${signal}` : `code ${code}`}
220
+ throw new Error(`Tauri process exited with ${signal ? `signal ${signal}` : `code ${code}`}
195
221
  stdout:
196
222
  ${this.stdout}
197
223
  stderr:
198
- ${this.stderr}`
199
- );
224
+ ${this.stderr}`);
200
225
  }
201
226
  if (child && (child.exitCode !== null || child.signalCode !== null)) {
202
- throw new Error(
203
- `Tauri process exited with ${child.signalCode ? `signal ${child.signalCode}` : `code ${child.exitCode}`}
227
+ throw new Error(`Tauri process exited with ${child.signalCode ? `signal ${child.signalCode}` : `code ${child.exitCode}`}
204
228
  stdout:
205
229
  ${this.stdout}
206
230
  stderr:
207
- ${this.stderr}`
208
- );
231
+ ${this.stderr}`);
209
232
  }
210
- if (await endpointAcceptsConnections(socketPath)) return;
211
- await delay(50);
233
+ if (await endpointAcceptsConnections(socketPath))
234
+ return;
235
+ await delay2(50);
212
236
  }
213
237
  throw new Error(`Hasgard socket did not appear within ${timeoutMs}ms: ${socketPath}`);
214
238
  }
215
239
  async confirmEndpointOwnership(child, path, timeoutMs) {
216
240
  const stat = await lstat(path);
217
- if (!stat.isSocket()) throw new Error(`Hasgard endpoint is not a socket: ${path}`);
241
+ if (!stat.isSocket())
242
+ throw new Error(`Hasgard endpoint is not a socket: ${path}`);
218
243
  await new Promise((resolve, reject) => {
219
244
  const done = (error) => {
220
245
  clearTimeout(timer);
221
246
  child.off("message", message);
222
247
  child.off("exit", exited);
223
- if (error) reject(error);
224
- else resolve();
248
+ if (error)
249
+ reject(error);
250
+ else
251
+ resolve();
225
252
  };
226
253
  const message = (value) => {
227
- if (value.type === "endpoint-owned") done();
228
- else if (value.type === "error") done(new Error(value.message));
254
+ if (value.type === "endpoint-owned")
255
+ done();
256
+ else if (value.type === "error")
257
+ done(new Error(value.message));
229
258
  };
230
259
  const exited = () => done(new Error("Hasgard supervisor exited before acknowledging endpoint ownership"));
231
- const timer = setTimeout(
232
- () => done(new Error("Hasgard supervisor did not acknowledge endpoint ownership")),
233
- timeoutMs
234
- );
260
+ const timer = setTimeout(() => done(new Error("Hasgard supervisor did not acknowledge endpoint ownership")), timeoutMs);
235
261
  child.on("message", message);
236
262
  child.once("exit", exited);
237
263
  child.send({ type: "endpoint", identity: { dev: stat.dev, ino: stat.ino } }, (error) => {
238
- if (error) done(error);
264
+ if (error)
265
+ done(error);
239
266
  });
240
267
  });
241
268
  }
242
269
  async stop() {
243
- if (this.starting) await this.starting;
244
- if (this.stopping) return this.stopping;
270
+ if (this.starting)
271
+ await this.starting;
272
+ if (this.stopping)
273
+ return this.stopping;
245
274
  this.stopping = this.stopChild();
246
275
  try {
247
276
  await this.stopping;
248
277
  } finally {
249
- this.stopping = void 0;
278
+ this.stopping = undefined;
250
279
  }
251
280
  }
252
281
  async stopChild() {
253
282
  const child = this.child;
254
- if (!child) return;
283
+ if (!child)
284
+ return;
255
285
  if (process.platform !== "win32") {
256
- if (this.applicationPid !== void 0) {
286
+ if (this.applicationPid !== undefined) {
257
287
  await stopProcessGroup(this.applicationPid);
258
288
  }
259
- const exited2 = child.exitCode !== null || child.signalCode !== null ? Promise.resolve() : new Promise((resolve) => child.once("exit", () => resolve()));
260
- if (child.connected) child.disconnect();
261
- const abort2 = new AbortController();
289
+ const exited = child.exitCode !== null || child.signalCode !== null ? Promise.resolve() : new Promise((resolve) => child.once("exit", () => resolve()));
290
+ if (child.connected)
291
+ child.disconnect();
292
+ const abort = new AbortController;
262
293
  try {
263
- if (await Promise.race([exited2, delay(11e3, "timeout", { signal: abort2.signal })]) === "timeout") {
294
+ if (await Promise.race([exited, delay2(11000, "timeout", { signal: abort.signal })]) === "timeout") {
264
295
  throw new Error(`Hasgard process supervisor ${child.pid} did not finish cleanup`);
265
296
  }
266
297
  } finally {
267
- abort2.abort();
298
+ abort.abort();
268
299
  }
269
- this.child = void 0;
270
- this.applicationPid = void 0;
300
+ this.child = undefined;
301
+ this.applicationPid = undefined;
271
302
  if (child.exitCode !== 0 || child.signalCode !== null) {
272
303
  throw new Error(`Hasgard supervisor cleanup failed (${child.signalCode ?? child.exitCode}):
273
304
  ${this.stderr}`);
274
305
  }
275
306
  return;
276
307
  }
277
- if (child.pid === void 0 || child.exitCode !== null || child.signalCode !== null) {
278
- this.child = void 0;
308
+ if (child.pid === undefined || child.exitCode !== null || child.signalCode !== null) {
309
+ this.child = undefined;
279
310
  return;
280
311
  }
281
312
  const exited = new Promise((resolve) => child.once("exit", () => resolve()));
282
- if (process.platform === "win32" && child.pid !== void 0) {
313
+ if (process.platform === "win32" && child.pid !== undefined) {
283
314
  await terminateWindowsProcessTree(child.pid);
284
- } else if (child.pid !== void 0) {
315
+ } else if (child.pid !== undefined) {
285
316
  process.kill(-child.pid, "SIGTERM");
286
317
  }
287
- const abort = new AbortController();
288
- const timeout = delay(5e3, "timeout", { signal: abort.signal });
318
+ const abort = new AbortController;
319
+ const timeout = delay2(5000, "timeout", { signal: abort.signal });
289
320
  try {
290
321
  if (await Promise.race([exited, timeout]) === "timeout") {
291
- if (process.platform === "win32" && child.pid !== void 0) await terminateWindowsProcessTree(child.pid);
292
- else if (child.pid !== void 0) process.kill(-child.pid, "SIGKILL");
322
+ if (process.platform === "win32" && child.pid !== undefined)
323
+ await terminateWindowsProcessTree(child.pid);
324
+ else if (child.pid !== undefined)
325
+ process.kill(-child.pid, "SIGKILL");
293
326
  await exited;
294
327
  }
295
328
  } finally {
296
329
  abort.abort();
297
330
  }
298
- this.child = void 0;
331
+ this.child = undefined;
299
332
  }
300
- };
333
+ }
301
334
  function appendOutput(previous, chunk) {
302
335
  const output = previous + chunk;
303
336
  return output.length > 65536 ? `[earlier output truncated]
@@ -306,9 +339,11 @@ ${output.slice(-65536)}` : output;
306
339
  async function endpointAcceptsConnections(path) {
307
340
  if (process.platform !== "win32") {
308
341
  try {
309
- if (!(await lstat(path)).isSocket()) throw new Error(`Hasgard endpoint exists but is not a socket: ${path}`);
342
+ if (!(await lstat(path)).isSocket())
343
+ throw new Error(`Hasgard endpoint exists but is not a socket: ${path}`);
310
344
  } catch (error) {
311
- if (error.code === "ENOENT") return false;
345
+ if (error.code === "ENOENT")
346
+ return false;
312
347
  throw error;
313
348
  }
314
349
  }
@@ -337,8 +372,11 @@ async function terminateWindowsProcessTree(pid) {
337
372
  }
338
373
 
339
374
  // src/rpc-client.ts
340
- import { createConnection as createConnection2 } from "net";
341
- import { createInterface } from "readline";
375
+ import { createConnection as createConnection2 } from "node:net";
376
+ import { createInterface } from "node:readline";
377
+
378
+ // src/protocol-limits.ts
379
+ var MAX_REQUEST_BYTES = 1048576;
342
380
 
343
381
  // src/protocol.ts
344
382
  function isObject(value) {
@@ -347,12 +385,16 @@ function isObject(value) {
347
385
  function parseRpcResponse(line) {
348
386
  const value = JSON.parse(line);
349
387
  const invalid = () => new Error(`Invalid Hasgard JSON-RPC envelope: ${line}`);
350
- if (!isObject(value) || value.jsonrpc !== "2.0" || !Object.hasOwn(value, "id")) throw invalid();
351
- if (value.id !== null && (!Number.isSafeInteger(value.id) || value.id < 0)) throw invalid();
388
+ if (!isObject(value) || value.jsonrpc !== "2.0" || !Object.hasOwn(value, "id"))
389
+ throw invalid();
390
+ if (value.id !== null && (!Number.isSafeInteger(value.id) || value.id < 0))
391
+ throw invalid();
352
392
  const hasResult = Object.hasOwn(value, "result");
353
393
  const hasError = Object.hasOwn(value, "error");
354
- if (hasResult === hasError) throw invalid();
355
- if (hasResult && value.id === null) throw invalid();
394
+ if (hasResult === hasError)
395
+ throw invalid();
396
+ if (hasResult && value.id === null)
397
+ throw invalid();
356
398
  if (hasError) {
357
399
  const error = value.error;
358
400
  if (!isObject(error) || !Number.isInteger(error.code) || error.code < -2147483648 || error.code > 2147483647 || typeof error.message !== "string")
@@ -361,11 +403,8 @@ function parseRpcResponse(line) {
361
403
  return value;
362
404
  }
363
405
 
364
- // src/protocol-limits.ts
365
- var MAX_REQUEST_BYTES = 1048576;
366
-
367
406
  // src/rpc-client.ts
368
- var HasgardRpcError = class extends Error {
407
+ class HasgardRpcError extends Error {
369
408
  code;
370
409
  data;
371
410
  constructor(code, message, data) {
@@ -374,18 +413,20 @@ var HasgardRpcError = class extends Error {
374
413
  this.code = code;
375
414
  this.data = data;
376
415
  }
377
- };
378
- var HasgardRpcClient = class {
416
+ }
417
+
418
+ class HasgardRpcClient {
379
419
  socketPath;
380
420
  socket;
381
421
  lines;
382
422
  nextId = 1;
383
- pending = /* @__PURE__ */ new Map();
423
+ pending = new Map;
384
424
  constructor(socketPath) {
385
425
  this.socketPath = socketPath;
386
426
  }
387
427
  async connect(timeoutMs) {
388
- if (this.socket) throw new Error("Hasgard RPC client is already connected");
428
+ if (this.socket)
429
+ throw new Error("Hasgard RPC client is already connected");
389
430
  const socket = createConnection2({ path: this.socketPath });
390
431
  this.socket = socket;
391
432
  try {
@@ -414,24 +455,28 @@ var HasgardRpcClient = class {
414
455
  socket.once("close", closed);
415
456
  });
416
457
  } catch (error) {
417
- if (this.socket === socket) this.socket = void 0;
458
+ if (this.socket === socket)
459
+ this.socket = undefined;
418
460
  socket.destroy();
419
461
  throw error;
420
462
  }
421
463
  this.lines = createInterface({ input: socket });
422
464
  this.lines.on("line", (line) => this.handleLine(line));
423
465
  socket.on("close", () => {
424
- if (this.socket !== socket) return;
466
+ if (this.socket !== socket)
467
+ return;
425
468
  this.rejectPending(new Error("Hasgard socket closed"));
426
469
  this.disconnect();
427
470
  });
428
471
  socket.on("error", (error) => {
429
- if (this.socket === socket) this.rejectPending(error);
472
+ if (this.socket === socket)
473
+ this.rejectPending(error);
430
474
  });
431
475
  }
432
476
  async call(method, params) {
433
477
  const socket = this.socket;
434
- if (!socket || socket.destroyed || !socket.writable) throw new Error("Hasgard RPC client is not connected");
478
+ if (!socket || socket.destroyed || !socket.writable)
479
+ throw new Error("Hasgard RPC client is not connected");
435
480
  const id = this.nextId;
436
481
  this.nextId += 1;
437
482
  const request = params ? { jsonrpc: "2.0", id, method, params } : { jsonrpc: "2.0", id, method };
@@ -448,9 +493,11 @@ var HasgardRpcClient = class {
448
493
  });
449
494
  });
450
495
  socket.write(line, (error) => {
451
- if (!error) return;
496
+ if (!error)
497
+ return;
452
498
  const pending = this.pending.get(id);
453
- if (!pending) return;
499
+ if (!pending)
500
+ return;
454
501
  this.pending.delete(id);
455
502
  pending.reject(error);
456
503
  });
@@ -458,9 +505,9 @@ var HasgardRpcClient = class {
458
505
  }
459
506
  disconnect() {
460
507
  this.lines?.close();
461
- this.lines = void 0;
508
+ this.lines = undefined;
462
509
  this.socket?.destroy();
463
- this.socket = void 0;
510
+ this.socket = undefined;
464
511
  this.rejectPending(new Error("Hasgard RPC client disconnected"));
465
512
  }
466
513
  handleLine(line) {
@@ -475,7 +522,7 @@ var HasgardRpcClient = class {
475
522
  this.rejectPending(new HasgardRpcError(response.error.code, response.error.message, response.error.data));
476
523
  return;
477
524
  }
478
- const pending = response.id === null ? void 0 : this.pending.get(response.id);
525
+ const pending = response.id === null ? undefined : this.pending.get(response.id);
479
526
  if (!pending) {
480
527
  this.rejectPending(new Error(`Unexpected Hasgard JSON-RPC response id: ${response.id}`));
481
528
  return;
@@ -488,19 +535,20 @@ var HasgardRpcClient = class {
488
535
  pending.resolve(response.result);
489
536
  }
490
537
  rejectPending(error) {
491
- for (const pending of this.pending.values()) pending.reject(error);
538
+ for (const pending of this.pending.values())
539
+ pending.reject(error);
492
540
  this.pending.clear();
493
541
  }
494
- };
542
+ }
495
543
 
496
544
  // src/window.ts
497
- import { readFile } from "fs/promises";
498
- import { basename } from "path";
499
- var locks = /* @__PURE__ */ new WeakMap();
545
+ import { readFile } from "node:fs/promises";
546
+ import { basename } from "node:path";
547
+ var locks = new WeakMap;
500
548
  function lockFor(rpc, label) {
501
549
  let clientLocks = locks.get(rpc);
502
550
  if (!clientLocks) {
503
- clientLocks = /* @__PURE__ */ new Map();
551
+ clientLocks = new Map;
504
552
  locks.set(rpc, clientLocks);
505
553
  }
506
554
  let lock = clientLocks.get(label);
@@ -518,36 +566,44 @@ function withWindow(window, params) {
518
566
  }
519
567
  async function readFileInputs(files) {
520
568
  const list = Array.isArray(files) ? files : [files];
521
- return Promise.all(
522
- list.map(async (entry) => {
523
- if (typeof entry !== "string") return entry;
524
- const contents = await readFile(entry);
525
- return { name: basename(entry), data: contents.toString("base64") };
526
- })
527
- );
569
+ return Promise.all(list.map(async (entry) => {
570
+ if (typeof entry !== "string")
571
+ return entry;
572
+ const contents = await readFile(entry);
573
+ return { name: basename(entry), data: contents.toString("base64") };
574
+ }));
528
575
  }
529
576
  function targetParams(target) {
530
- if ("ref" in target) return { ref: target.ref };
577
+ if ("ref" in target)
578
+ return { ref: target.ref };
531
579
  if ("selector" in target) {
532
- return target.index === void 0 ? { selector: target.selector } : { selector: target.selector, index: target.index };
580
+ return target.index === undefined ? { selector: target.selector } : { selector: target.selector, index: target.index };
533
581
  }
534
582
  return { x: target.x, y: target.y };
535
583
  }
536
584
  function clickParams(options) {
537
585
  const params = {};
538
- if (options.modifiers !== void 0) params.modifiers = options.modifiers;
539
- if (options.button !== void 0) params.button = options.button;
540
- if (options.clickCount !== void 0) params.clickCount = options.clickCount;
541
- if (options.position !== void 0) params.position = { ...options.position };
586
+ if (options.modifiers !== undefined)
587
+ params.modifiers = options.modifiers;
588
+ if (options.button !== undefined)
589
+ params.button = options.button;
590
+ if (options.clickCount !== undefined)
591
+ params.clickCount = options.clickCount;
592
+ if (options.position !== undefined)
593
+ params.position = { ...options.position };
542
594
  return params;
543
595
  }
544
596
  function routeParams(options) {
545
597
  const params = { pattern: options.pattern };
546
- if (options.method !== void 0) params.method = options.method;
547
- if (options.times !== void 0) params.times = options.times;
548
- if ("status" in options && options.status !== void 0) params.status = options.status;
549
- if ("body" in options && options.body !== void 0) params.body = options.body;
550
- if ("contentType" in options && options.contentType !== void 0) {
598
+ if (options.method !== undefined)
599
+ params.method = options.method;
600
+ if (options.times !== undefined)
601
+ params.times = options.times;
602
+ if ("status" in options && options.status !== undefined)
603
+ params.status = options.status;
604
+ if ("body" in options && options.body !== undefined)
605
+ params.body = options.body;
606
+ if ("contentType" in options && options.contentType !== undefined) {
551
607
  params.contentType = options.contentType;
552
608
  }
553
609
  return params;
@@ -595,27 +651,32 @@ function expectRecord(value, context) {
595
651
  return value;
596
652
  }
597
653
  function expectString(value, context) {
598
- if (typeof value !== "string") throw new Error(`${context} must be a string`);
654
+ if (typeof value !== "string")
655
+ throw new Error(`${context} must be a string`);
599
656
  return value;
600
657
  }
601
658
  function expectBoolean(value, context) {
602
- if (typeof value !== "boolean") throw new Error(`${context} must be a boolean`);
659
+ if (typeof value !== "boolean")
660
+ throw new Error(`${context} must be a boolean`);
603
661
  return value;
604
662
  }
605
663
  function expectNumber(value, context) {
606
- if (typeof value !== "number") throw new Error(`${context} must be a number`);
664
+ if (typeof value !== "number")
665
+ throw new Error(`${context} must be a number`);
607
666
  return value;
608
667
  }
609
668
  function expectArray(value, context) {
610
- if (!Array.isArray(value)) throw new Error(`${context} must be an array`);
669
+ if (!Array.isArray(value))
670
+ throw new Error(`${context} must be an array`);
611
671
  return value;
612
672
  }
613
673
  function expectNullableString(value, context) {
614
- if (value === null) return null;
674
+ if (value === null)
675
+ return null;
615
676
  return expectString(value, context);
616
677
  }
617
678
  function optionalString(value, context) {
618
- return value === void 0 ? void 0 : expectString(value, context);
679
+ return value === undefined ? undefined : expectString(value, context);
619
680
  }
620
681
  function parseConsoleLogEntry(value, index) {
621
682
  const entry = expectRecord(value, `console.getLogs[${index}]`);
@@ -650,7 +711,8 @@ function parseFormField(value, context) {
650
711
  name: expectString(field.name, `${context}.name`),
651
712
  value: Array.isArray(raw) ? raw.map((option, index) => expectString(option, `${context}.value[${index}]`)) : expectString(raw, `${context}.value`)
652
713
  };
653
- if (field.checked !== void 0) parsed.checked = expectBoolean(field.checked, `${context}.checked`);
714
+ if (field.checked !== undefined)
715
+ parsed.checked = expectBoolean(field.checked, `${context}.checked`);
654
716
  return parsed;
655
717
  }
656
718
  function parseFormEntry(value, index) {
@@ -661,11 +723,9 @@ function parseFormEntry(value, index) {
661
723
  name: expectString(form.name, `${context}.name`),
662
724
  action: expectString(form.action, `${context}.action`),
663
725
  method: expectString(form.method, `${context}.method`),
664
- fields: expectArray(form.fields, `${context}.fields`).map(
665
- (field, fieldIndex) => parseFormField(field, `${context}.fields[${fieldIndex}]`)
666
- )
726
+ fields: expectArray(form.fields, `${context}.fields`).map((field, fieldIndex) => parseFormField(field, `${context}.fields[${fieldIndex}]`))
667
727
  };
668
- if (form.fieldsTruncated !== void 0) {
728
+ if (form.fieldsTruncated !== undefined) {
669
729
  parsed.fieldsTruncated = expectBoolean(form.fieldsTruncated, `${context}.fieldsTruncated`);
670
730
  }
671
731
  return parsed;
@@ -674,15 +734,20 @@ function parseWatchNode(value, context) {
674
734
  const node = expectRecord(value, context);
675
735
  const parsed = { tag: expectString(node.tag, `${context}.tag`) };
676
736
  const id = optionalString(node.id, `${context}.id`);
677
- if (id !== void 0) parsed.id = id;
737
+ if (id !== undefined)
738
+ parsed.id = id;
678
739
  const className = optionalString(node.class, `${context}.class`);
679
- if (className !== void 0) parsed.class = className;
740
+ if (className !== undefined)
741
+ parsed.class = className;
680
742
  const text = optionalString(node.text, `${context}.text`);
681
- if (text !== void 0) parsed.text = text;
743
+ if (text !== undefined)
744
+ parsed.text = text;
682
745
  const attr = optionalString(node.attr, `${context}.attr`);
683
- if (attr !== void 0) parsed.attr = attr;
746
+ if (attr !== undefined)
747
+ parsed.attr = attr;
684
748
  const attrValue = optionalString(node.value, `${context}.value`);
685
- if (attrValue !== void 0) parsed.value = attrValue;
749
+ if (attrValue !== undefined)
750
+ parsed.value = attrValue;
686
751
  return parsed;
687
752
  }
688
753
  function parseWatchNodes(value, context) {
@@ -693,9 +758,7 @@ function parseDiffChange(value, index) {
693
758
  return {
694
759
  old: parseSnapshotElement(change.old, index),
695
760
  new: parseSnapshotElement(change.new, index),
696
- changes: expectArray(change.changes, `diff.changed[${index}].changes`).map(
697
- (entry, entryIndex) => expectString(entry, `diff.changed[${index}].changes[${entryIndex}]`)
698
- )
761
+ changes: expectArray(change.changes, `diff.changed[${index}].changes`).map((entry, entryIndex) => expectString(entry, `diff.changed[${index}].changes[${entryIndex}]`))
699
762
  };
700
763
  }
701
764
  function parseRecorderEntry(value, index) {
@@ -706,7 +769,8 @@ function parseRecorderEntry(value, index) {
706
769
  timestamp: expectNumber(entry.timestamp, `record.stop.entries[${index}].timestamp`)
707
770
  };
708
771
  }
709
- var HasgardApplication = class {
772
+
773
+ class HasgardApplication {
710
774
  rpc;
711
775
  constructor(rpc) {
712
776
  this.rpc = rpc;
@@ -720,7 +784,8 @@ var HasgardApplication = class {
720
784
  }
721
785
  async windows() {
722
786
  const value = expectRecord(await this.rpc.call("windows.list"), "windows.list result");
723
- if (!Array.isArray(value.windows)) throw new Error("windows.list.windows must be an array");
787
+ if (!Array.isArray(value.windows))
788
+ throw new Error("windows.list.windows must be an array");
724
789
  return value.windows.map((entry, index) => {
725
790
  const window = expectRecord(entry, `windows[${index}]`);
726
791
  const info = {
@@ -728,7 +793,7 @@ var HasgardApplication = class {
728
793
  url: expectString(window.url, `windows[${index}].url`),
729
794
  title: expectString(window.title, `windows[${index}].title`)
730
795
  };
731
- if (window.native_id !== void 0) {
796
+ if (window.native_id !== undefined) {
732
797
  info.nativeId = expectNumber(window.native_id, `windows[${index}].native_id`);
733
798
  }
734
799
  return info;
@@ -741,9 +806,11 @@ var HasgardApplication = class {
741
806
  const window = this.window(label);
742
807
  try {
743
808
  const state = await window.state();
744
- if (state.url !== "about:blank" && state.readyState !== "loading") return window;
809
+ if (state.url !== "about:blank" && state.readyState !== "loading")
810
+ return window;
745
811
  } catch (error) {
746
- if (!(error instanceof HasgardRpcError) || error.code !== -32002) throw error;
812
+ if (!(error instanceof HasgardRpcError) || error.code !== -32002)
813
+ throw error;
747
814
  }
748
815
  }
749
816
  await new Promise((resolve) => setTimeout(resolve, 50));
@@ -754,7 +821,8 @@ var HasgardApplication = class {
754
821
  const window = await this.waitForWindow(label, timeoutMs);
755
822
  const deadline = Date.now() + timeoutMs;
756
823
  while (Date.now() <= deadline) {
757
- if (await window.locator(readySelector).count() === 1) return window;
824
+ if (await window.locator(readySelector).count() === 1)
825
+ return window;
758
826
  await new Promise((resolve) => setTimeout(resolve, 50));
759
827
  }
760
828
  throw new Error(`Window '${label}' did not match ready selector '${readySelector}' within ${timeoutMs}ms`);
@@ -762,10 +830,6 @@ var HasgardApplication = class {
762
830
  window(label) {
763
831
  return new HasgardWindow(this.rpc, label);
764
832
  }
765
- /**
766
- * Action recorder. Captures the RPC calls this connection makes into a
767
- * replayable scenario — this is not video capture.
768
- */
769
833
  recorder = {
770
834
  start: async () => {
771
835
  await this.rpc.call("record.start");
@@ -789,8 +853,9 @@ var HasgardApplication = class {
789
853
  await this.rpc.call("record.add", entry);
790
854
  }
791
855
  };
792
- };
793
- var HasgardWindow = class {
856
+ }
857
+
858
+ class HasgardWindow {
794
859
  label;
795
860
  rpc;
796
861
  constructor(rpc, label) {
@@ -803,12 +868,6 @@ var HasgardWindow = class {
803
868
  getByRole(role, query = {}) {
804
869
  return new HasgardLocator(this, { kind: "role", role, query });
805
870
  }
806
- /**
807
- * Locate by visible text. Matching runs in the page against the live DOM, not
808
- * against a snapshot's `name`: that field collapses aria-label, alt, `<label>`,
809
- * placeholder, and text into one 50-character string, so a button with an
810
- * aria-label would never be findable by the text it actually shows.
811
- */
812
871
  getByText(text, query = {}) {
813
872
  return new HasgardLocator(this, { kind: "query", by: "text", value: text, query });
814
873
  }
@@ -824,27 +883,16 @@ var HasgardWindow = class {
824
883
  getByTitle(text, query = {}) {
825
884
  return new HasgardLocator(this, { kind: "query", by: "title", value: text, query });
826
885
  }
827
- /** Test ids always match exactly, so `save` cannot select `save-draft`. */
828
886
  getByTestId(testId) {
829
887
  return new HasgardLocator(this, { kind: "query", by: "testid", value: testId, query: {} });
830
888
  }
831
- /**
832
- * Scope locators to a same-origin `<iframe>`.
833
- *
834
- * Cross-origin frames are out of reach: the same-origin policy binds injected
835
- * script exactly as it binds the page's own, so no bridge can see into a
836
- * third-party payment or OAuth frame. Those raise an error naming the origin
837
- * problem rather than reporting a missing element.
838
- */
839
889
  frameLocator(selector) {
840
890
  return new HasgardFrameLocator(this, [selector]);
841
891
  }
842
892
  async snapshot(options = {}) {
843
- const raw = expectRecord(
844
- await this.rpc.call("snapshot", withWindow(this.label, options)),
845
- "snapshot result"
846
- );
847
- if (!Array.isArray(raw.elements)) throw new Error("snapshot.elements must be an array");
893
+ const raw = expectRecord(await this.rpc.call("snapshot", withWindow(this.label, options)), "snapshot result");
894
+ if (!Array.isArray(raw.elements))
895
+ throw new Error("snapshot.elements must be an array");
848
896
  return {
849
897
  elements: raw.elements.map((entry, index) => parseSnapshotElement(entry, index))
850
898
  };
@@ -874,31 +922,29 @@ var HasgardWindow = class {
874
922
  async title() {
875
923
  return expectString(await this.rpc.call("title", withWindow(this.label)), "title result");
876
924
  }
877
- /**
878
- * Navigate this webview by assigning `location.href`. Unlike Playwright's
879
- * `goto`, this does not wait for the load to finish and returns no response —
880
- * follow it with `waitFor` or `waitForWindowReady`.
881
- */
882
925
  async navigate(url) {
883
926
  await this.rpc.call("navigate", withWindow(this.label, { url }));
884
927
  }
885
- /** Scroll the document by a relative amount, or to `top`/`bottom`. */
886
928
  async scrollBy(options) {
887
929
  await this.rpc.call("scroll", withWindow(this.label, options));
888
930
  }
889
931
  async press(key, options) {
890
932
  const params = { key };
891
933
  if (options) {
892
- if (options.completion !== void 0) params.completion = options.completion;
893
- if (options.waitFor !== void 0) params.waitFor = options.waitFor;
894
- if (options.timeoutMs !== void 0) params.timeout = options.timeoutMs;
934
+ if (options.completion !== undefined)
935
+ params.completion = options.completion;
936
+ if (options.waitFor !== undefined)
937
+ params.waitFor = options.waitFor;
938
+ if (options.timeoutMs !== undefined)
939
+ params.timeout = options.timeoutMs;
895
940
  }
896
941
  await this.rpc.call("press", withWindow(this.label, params));
897
942
  }
898
943
  async diff(options = {}) {
899
944
  const { reference, ...snapshotOptions } = options;
900
945
  const params = { ...snapshotOptions };
901
- if (reference) params.reference = reference;
946
+ if (reference)
947
+ params.reference = reference;
902
948
  const value = expectRecord(await this.rpc.call("diff", withWindow(this.label, params)), "diff result");
903
949
  return {
904
950
  added: expectArray(value.added, "diff.added").map(parseSnapshotElement),
@@ -906,26 +952,25 @@ var HasgardWindow = class {
906
952
  changed: expectArray(value.changed, "diff.changed").map(parseDiffChange)
907
953
  };
908
954
  }
909
- /**
910
- * Poll a JavaScript expression in the webview until it returns a truthy
911
- * value, and return that value. The expression is polled rather than driven
912
- * by DOM mutations, so predicates that flip on a timer or a resolved fetch
913
- * are caught too. A predicate that throws rejects instead of being retried.
914
- */
915
955
  async waitForFunction(expression, options = {}) {
916
956
  const params = { expression };
917
- if (options.timeoutMs !== void 0) params.timeout = options.timeoutMs;
918
- if (options.pollMs !== void 0) params.poll = options.pollMs;
957
+ if (options.timeoutMs !== undefined)
958
+ params.timeout = options.timeoutMs;
959
+ if (options.pollMs !== undefined)
960
+ params.poll = options.pollMs;
919
961
  const value = expectRecord(await this.rpc.call("wait", withWindow(this.label, params)), "wait result");
920
962
  return value.value;
921
963
  }
922
- /** Observe DOM mutations until they settle, or reject when nothing changes. */
923
964
  async watch(options = {}) {
924
965
  const params = {};
925
- if (options.selector !== void 0) params.selector = options.selector;
926
- if (options.timeoutMs !== void 0) params.timeout = options.timeoutMs;
927
- if (options.stableMs !== void 0) params.stable = options.stableMs;
928
- if (options.requireMutation !== void 0) params.requireMutation = options.requireMutation;
966
+ if (options.selector !== undefined)
967
+ params.selector = options.selector;
968
+ if (options.timeoutMs !== undefined)
969
+ params.timeout = options.timeoutMs;
970
+ if (options.stableMs !== undefined)
971
+ params.stable = options.stableMs;
972
+ if (options.requireMutation !== undefined)
973
+ params.requireMutation = options.requireMutation;
929
974
  const value = expectRecord(await this.rpc.call("watch", withWindow(this.label, params)), "watch result");
930
975
  return {
931
976
  added: parseWatchNodes(value.added, "watch.added"),
@@ -935,43 +980,31 @@ var HasgardWindow = class {
935
980
  };
936
981
  }
937
982
  async consoleLogs(options = {}) {
938
- const value = await this.rpc.call(
939
- "console.getLogs",
940
- withWindow(this.label, options)
941
- );
983
+ const value = await this.rpc.call("console.getLogs", withWindow(this.label, options));
942
984
  return expectArray(value, "console.getLogs result").map(parseConsoleLogEntry);
943
985
  }
944
986
  async clearConsole() {
945
987
  await this.rpc.call("console.clear", withWindow(this.label));
946
988
  }
947
989
  async networkRequests(options = {}) {
948
- const value = await this.rpc.call(
949
- "network.getRequests",
950
- withWindow(this.label, options)
951
- );
990
+ const value = await this.rpc.call("network.getRequests", withWindow(this.label, options));
952
991
  return expectArray(value, "network.getRequests result").map(parseNetworkRequestEntry);
953
992
  }
954
993
  async clearNetwork() {
955
994
  await this.rpc.call("network.clear", withWindow(this.label));
956
995
  }
957
- /** `localStorage` by default; pass `{ session: true }` for `sessionStorage`. */
958
996
  storage = {
959
997
  get: async (key, options = {}) => {
960
- const value = expectRecord(
961
- await this.rpc.call("storage.get", withWindow(this.label, { key, ...options })),
962
- "storage.get result"
963
- );
964
- if (!expectBoolean(value.found, "storage.get.found")) return null;
998
+ const value = expectRecord(await this.rpc.call("storage.get", withWindow(this.label, { key, ...options })), "storage.get result");
999
+ if (!expectBoolean(value.found, "storage.get.found"))
1000
+ return null;
965
1001
  return expectString(value.value, "storage.get.value");
966
1002
  },
967
1003
  set: async (key, value, options = {}) => {
968
1004
  await this.rpc.call("storage.set", withWindow(this.label, { key, value, ...options }));
969
1005
  },
970
1006
  list: async (options = {}) => {
971
- const value = expectRecord(
972
- await this.rpc.call("storage.list", withWindow(this.label, { ...options })),
973
- "storage.list result"
974
- );
1007
+ const value = expectRecord(await this.rpc.call("storage.list", withWindow(this.label, { ...options })), "storage.list result");
975
1008
  return {
976
1009
  entries: expectArray(value.entries, "storage.list.entries").map((entry, index) => {
977
1010
  const parsed = expectRecord(entry, `storage.list.entries[${index}]`);
@@ -988,7 +1021,7 @@ var HasgardWindow = class {
988
1021
  }
989
1022
  };
990
1023
  async forms(selector) {
991
- const params = selector ? { selector } : void 0;
1024
+ const params = selector ? { selector } : undefined;
992
1025
  const value = expectRecord(await this.rpc.call("forms.dump", withWindow(this.label, params)), "forms.dump result");
993
1026
  return {
994
1027
  forms: expectArray(value.forms, "forms.dump.forms").map(parseFormEntry),
@@ -1002,22 +1035,21 @@ var HasgardWindow = class {
1002
1035
  return this.rpc.call("ipc", withWindow(this.label, { command, args }));
1003
1036
  }
1004
1037
  async screenshot(selector) {
1005
- const params = selector ? { selector } : void 0;
1038
+ const params = selector ? { selector } : undefined;
1006
1039
  const dataUrl = await this.rpc.call("screenshot", withWindow(this.label, params));
1007
1040
  const prefix = "data:image/png;base64,";
1008
- if (!dataUrl.startsWith(prefix)) throw new Error("screenshot result is not a PNG data URL");
1041
+ if (!dataUrl.startsWith(prefix))
1042
+ throw new Error("screenshot result is not a PNG data URL");
1009
1043
  return Buffer.from(dataUrl.slice(prefix.length), "base64");
1010
1044
  }
1011
- /** Start a bounded recording of this native window. macOS only; requires ffmpeg. */
1012
1045
  async startVideo(options) {
1013
1046
  await this.rpc.call("video.start", {
1014
1047
  window: this.label,
1015
1048
  output_path: options.outputPath,
1016
- ...options.fps === void 0 ? {} : { fps: options.fps },
1017
- ...options.maxDurationMs === void 0 ? {} : { max_duration_ms: options.maxDurationMs }
1049
+ ...options.fps === undefined ? {} : { fps: options.fps },
1050
+ ...options.maxDurationMs === undefined ? {} : { max_duration_ms: options.maxDurationMs }
1018
1051
  });
1019
1052
  }
1020
- /** Stop recording; capture and encoder failures reject rather than returning an empty artifact. */
1021
1053
  async stopVideo() {
1022
1054
  const value = expectRecord(await this.rpc.call("video.stop", { window: this.label }), "video.stop result");
1023
1055
  return {
@@ -1034,26 +1066,12 @@ var HasgardWindow = class {
1034
1066
  pendingResult: expectBoolean(value.pending_result, "video.pending_result")
1035
1067
  };
1036
1068
  }
1037
- /**
1038
- * Capture this window through the native compositor, writing a PNG to
1039
- * `outputPath`. macOS only.
1040
- *
1041
- * Unlike `screenshot`, which renders the DOM inside the page, this records
1042
- * what the window server actually shows — so it sees native chrome, layered
1043
- * content, and anything the DOM renderer cannot reproduce.
1044
- *
1045
- * The operating-system window id is resolved from this window's label, so
1046
- * callers do not have to discover it themselves.
1047
- */
1048
1069
  async screenshotNative(options) {
1049
1070
  const windowId = options.windowId ?? await this.nativeId();
1050
- const value = expectRecord(
1051
- await this.rpc.call("screenshot_native", {
1052
- window_id: windowId,
1053
- output_path: options.outputPath
1054
- }),
1055
- "screenshot_native result"
1056
- );
1071
+ const value = expectRecord(await this.rpc.call("screenshot_native", {
1072
+ window_id: windowId,
1073
+ output_path: options.outputPath
1074
+ }), "screenshot_native result");
1057
1075
  return {
1058
1076
  output_path: expectString(value.output_path, "screenshot_native.output_path"),
1059
1077
  window_id: expectNumber(value.window_id, "screenshot_native.window_id"),
@@ -1065,102 +1083,58 @@ var HasgardWindow = class {
1065
1083
  tcc_denied: expectBoolean(value.tcc_denied, "screenshot_native.tcc_denied")
1066
1084
  };
1067
1085
  }
1068
- /**
1069
- * Scroll the page by wheel.
1070
- *
1071
- * The document sees a real `wheel` event first and may cancel it; the scroll
1072
- * only follows if it does not. Resolves to whether the page cancelled — which
1073
- * is how you tell "the app intercepted my wheel" from "nothing was
1074
- * listening". Use the locator method to scroll a nested scrollport.
1075
- */
1076
1086
  async wheel(deltaX, deltaY) {
1077
1087
  const value = expectRecord(await this.call("wheel", { deltaX, deltaY }), "wheel result");
1078
1088
  return expectBoolean(value.defaultPrevented, "wheel.defaultPrevented");
1079
1089
  }
1080
- /**
1081
- * The page's modal dialogs (`alert`, `confirm`, `prompt`).
1082
- *
1083
- * These block the webview until answered, so the bridge always answers them —
1084
- * dismissing by default, as Playwright does. A test that never touches this
1085
- * namespace still cannot hang on a `confirm()`, and can read back what the
1086
- * app tried to ask.
1087
- */
1088
1090
  dialogs = {
1089
- /** Answer every subsequent dialog with OK. `promptText` fills a `prompt`. */
1090
1091
  accept: async (promptText) => {
1091
1092
  const params = { action: "accept" };
1092
- if (promptText !== void 0) params.promptText = promptText;
1093
+ if (promptText !== undefined)
1094
+ params.promptText = promptText;
1093
1095
  return parseDialogPolicy(await this.call("dialog.handle", params));
1094
1096
  },
1095
- /** Answer every subsequent dialog with Cancel. This is the default. */
1096
1097
  dismiss: async () => parseDialogPolicy(await this.call("dialog.handle", { action: "dismiss" })),
1097
- /** Every dialog the page has opened so far, oldest first. */
1098
1098
  list: async () => {
1099
1099
  const value = expectRecord(await this.call("dialog.list"), "dialog.list result");
1100
- if (!Array.isArray(value.dialogs)) throw new Error("dialog.list.dialogs must be an array");
1100
+ if (!Array.isArray(value.dialogs))
1101
+ throw new Error("dialog.list.dialogs must be an array");
1101
1102
  return {
1102
1103
  dialogs: value.dialogs.map((entry, index) => parseDialogRecord(entry, index)),
1103
1104
  policy: parseDialogPolicy(value.policy)
1104
1105
  };
1105
1106
  },
1106
- /** Forget the recorded dialogs. Leaves the accept/dismiss policy alone. */
1107
1107
  clear: async () => {
1108
1108
  await this.call("dialog.clear");
1109
1109
  }
1110
1110
  };
1111
- /**
1112
- * Shape what the page's own requests receive, without a real backend.
1113
- *
1114
- * Rules are declarative rather than Playwright's per-request callback: the
1115
- * bridge only ever answers requests, so it cannot call back into this process
1116
- * and await a handler while the page sits inside `fetch`. The same constraint
1117
- * shaped `dialogs`.
1118
- *
1119
- * Rules are checked in registration order and the first match wins, so
1120
- * register the narrow ones before a catch-all. Both `fetch` and
1121
- * `XMLHttpRequest` are covered; resources the webview loads itself -- the
1122
- * document, `<img src>`, stylesheets -- are not, since no page script sees
1123
- * them.
1124
- */
1125
1111
  routes = {
1126
- /** Answer matching requests with this response instead of the network. */
1127
1112
  fulfill: async (options) => {
1128
- const value = expectRecord(
1129
- await this.call("route.add", { ...routeParams(options), action: "fulfill" }),
1130
- "route.add result"
1131
- );
1113
+ const value = expectRecord(await this.call("route.add", { ...routeParams(options), action: "fulfill" }), "route.add result");
1132
1114
  return { id: expectNumber(value.id, "route.add.id") };
1133
1115
  },
1134
- /** Fail matching requests the way a dropped connection does. */
1135
1116
  abort: async (options) => {
1136
- const value = expectRecord(
1137
- await this.call("route.add", { ...routeParams(options), action: "abort" }),
1138
- "route.add result"
1139
- );
1117
+ const value = expectRecord(await this.call("route.add", { ...routeParams(options), action: "abort" }), "route.add result");
1140
1118
  return { id: expectNumber(value.id, "route.add.id") };
1141
1119
  },
1142
- /** The active rules, their use counts, and every request they answered. */
1143
1120
  list: async () => {
1144
1121
  const value = expectRecord(await this.call("route.list"), "route.list result");
1145
1122
  return {
1146
1123
  routes: expectArray(value.routes, "route.list.routes").map((entry, index) => parseRouteRule(entry, index)),
1147
- intercepted: expectArray(value.intercepted, "route.list.intercepted").map(
1148
- (entry, index) => parseInterceptedRequest(entry, index)
1149
- )
1124
+ intercepted: expectArray(value.intercepted, "route.list.intercepted").map((entry, index) => parseInterceptedRequest(entry, index))
1150
1125
  };
1151
1126
  },
1152
- /** Remove every rule and its interception log. */
1153
1127
  clear: async () => {
1154
1128
  const value = expectRecord(await this.call("route.clear"), "route.clear result");
1155
1129
  return { removed: expectNumber(value.removed, "route.clear.removed") };
1156
1130
  }
1157
1131
  };
1158
- /** This window's operating-system window id, where the platform exposes one. */
1159
1132
  async nativeId() {
1160
1133
  const listing = await new HasgardApplication(this.rpc).windows();
1161
1134
  const self = listing.find((entry) => entry.label === this.label);
1162
- if (!self) throw new Error(`Window '${this.label}' is no longer open`);
1163
- if (self.nativeId === void 0) {
1135
+ if (!self)
1136
+ throw new Error(`Window '${this.label}' is no longer open`);
1137
+ if (self.nativeId === undefined) {
1164
1138
  throw new Error(`Window '${this.label}' has no native window id on this platform`);
1165
1139
  }
1166
1140
  return self.nativeId;
@@ -1183,17 +1157,17 @@ var HasgardWindow = class {
1183
1157
  release();
1184
1158
  }
1185
1159
  }
1186
- };
1187
- var HasgardFrameLocator = class _HasgardFrameLocator {
1160
+ }
1161
+
1162
+ class HasgardFrameLocator {
1188
1163
  windowRef;
1189
1164
  frame;
1190
1165
  constructor(window, frame) {
1191
1166
  this.windowRef = window;
1192
1167
  this.frame = frame;
1193
1168
  }
1194
- /** Descend into a frame nested inside this one. */
1195
1169
  frameLocator(selector) {
1196
- return new _HasgardFrameLocator(this.windowRef, [...this.frame, selector]);
1170
+ return new HasgardFrameLocator(this.windowRef, [...this.frame, selector]);
1197
1171
  }
1198
1172
  locator(selector) {
1199
1173
  return new HasgardLocator(this.windowRef, { kind: "selector", selector, frame: [...this.frame] });
@@ -1219,29 +1193,21 @@ var HasgardFrameLocator = class _HasgardFrameLocator {
1219
1193
  getByTestId(testId) {
1220
1194
  return this.byDimension("testid", testId, {});
1221
1195
  }
1222
- /** Snapshot the frame's own document rather than the top one. */
1223
1196
  async snapshot(options = {}) {
1224
1197
  return this.windowRef.snapshot({ ...options, frame: [...this.frame] });
1225
1198
  }
1226
1199
  byDimension(by, value, query) {
1227
1200
  return new HasgardLocator(this.windowRef, { kind: "query", by, value, query, frame: [...this.frame] });
1228
1201
  }
1229
- };
1230
- var HasgardLocator = class _HasgardLocator {
1202
+ }
1203
+
1204
+ class HasgardLocator {
1231
1205
  window;
1232
1206
  query;
1233
1207
  constructor(window, query) {
1234
1208
  this.window = window;
1235
1209
  this.query = query;
1236
1210
  }
1237
- /**
1238
- * Click this element.
1239
- *
1240
- * A right or middle click raises `contextmenu`/`auxclick` and no `click`, and
1241
- * `clickCount: 2` escalates `detail` across the presses before a single
1242
- * `dblclick` -- both matching what the platform does, so a listener bound to
1243
- * the wrong event fails here rather than in front of a user.
1244
- */
1245
1211
  async click(options = {}) {
1246
1212
  await this.withTarget((target) => this.window.call("click", { ...this.scoped(target), ...clickParams(options) }));
1247
1213
  }
@@ -1251,61 +1217,37 @@ var HasgardLocator = class _HasgardLocator {
1251
1217
  async type(text) {
1252
1218
  await this.withTarget((target) => this.window.call("type", { ...this.scoped(target), text }));
1253
1219
  }
1254
- /** Empty the control, firing `input` and `change` as `fill` does. */
1255
1220
  async clear() {
1256
1221
  await this.fill("");
1257
1222
  }
1258
- /**
1259
- * Select files on an `<input type="file">` without a native file chooser,
1260
- * which an embedded webview gives no way to drive.
1261
- *
1262
- * Strings are read from the test machine's disk; `DropFile` payloads let you
1263
- * supply content that never existed as a file. An empty array deselects.
1264
- */
1265
1223
  async setInputFiles(files) {
1266
1224
  const payloads = await readFileInputs(files);
1267
- await this.withTarget(
1268
- (target) => this.window.call("setInputFiles", {
1269
- ...this.scoped(target),
1270
- files: payloads
1271
- })
1272
- );
1273
- }
1274
- /**
1275
- * Scroll this element by wheel.
1276
- *
1277
- * The page sees a real `wheel` event first and may cancel it; the scroll only
1278
- * follows if it does not — the same two steps a physical wheel produces.
1279
- * Resolves to whether the page cancelled.
1280
- */
1225
+ await this.withTarget((target) => this.window.call("setInputFiles", {
1226
+ ...this.scoped(target),
1227
+ files: payloads
1228
+ }));
1229
+ }
1281
1230
  async wheel(deltaX, deltaY) {
1282
1231
  return this.withTarget(async (target) => {
1283
- const value = expectRecord(
1284
- await this.window.call("wheel", { ...this.scoped(target), deltaX, deltaY }),
1285
- "wheel result"
1286
- );
1232
+ const value = expectRecord(await this.window.call("wheel", { ...this.scoped(target), deltaX, deltaY }), "wheel result");
1287
1233
  return expectBoolean(value.defaultPrevented, "wheel.defaultPrevented");
1288
1234
  });
1289
1235
  }
1290
1236
  async selectOption(value) {
1291
1237
  await this.withTarget((target) => this.window.call("select", { ...this.scoped(target), value }));
1292
1238
  }
1293
- /** Drive the checkbox to checked. A no-op when it is already checked. */
1294
1239
  async check() {
1295
1240
  await this.withTarget((target) => this.window.call("check", { ...this.scoped(target), checked: true }));
1296
1241
  }
1297
- /** Drive the checkbox to unchecked. A no-op when it is already unchecked. */
1298
1242
  async uncheck() {
1299
1243
  await this.withTarget((target) => this.window.call("check", { ...this.scoped(target), checked: false }));
1300
1244
  }
1301
- /** Flip the checkbox, whatever its current state. */
1302
1245
  async toggle() {
1303
1246
  await this.withTarget((target) => this.window.call("check", this.scoped(target)));
1304
1247
  }
1305
1248
  async hover() {
1306
1249
  await this.withTarget((target) => this.window.call("hover", this.scoped(target)));
1307
1250
  }
1308
- /** Shorthand for `click({ clickCount: 2 })`; other options still apply. */
1309
1251
  async dblclick(options = {}) {
1310
1252
  await this.click({ ...options, clickCount: 2 });
1311
1253
  }
@@ -1315,11 +1257,8 @@ var HasgardLocator = class _HasgardLocator {
1315
1257
  async blur() {
1316
1258
  await this.withTarget((target) => this.window.call("blur", this.scoped(target)));
1317
1259
  }
1318
- /** Scroll this element's own scrollport. */
1319
1260
  async scrollBy(options) {
1320
- await this.withTarget(
1321
- (target) => this.window.call("scroll", { ...this.scoped(target), ...options })
1322
- );
1261
+ await this.withTarget((target) => this.window.call("scroll", { ...this.scoped(target), ...options }));
1323
1262
  }
1324
1263
  async boundingBox() {
1325
1264
  return this.withTarget(async (target) => {
@@ -1342,25 +1281,18 @@ var HasgardLocator = class _HasgardLocator {
1342
1281
  return !await this.isDisabled();
1343
1282
  }
1344
1283
  async textContent() {
1345
- return this.withTarget(
1346
- async (target) => expectString(await this.window.call("text", this.scoped(target)), "text result")
1347
- );
1284
+ return this.withTarget(async (target) => expectString(await this.window.call("text", this.scoped(target)), "text result"));
1348
1285
  }
1349
1286
  async inputValue() {
1350
- return this.withTarget(
1351
- async (target) => expectString(await this.window.call("value", this.scoped(target)), "value result")
1352
- );
1287
+ return this.withTarget(async (target) => expectString(await this.window.call("value", this.scoped(target)), "value result"));
1353
1288
  }
1354
1289
  async innerHTML() {
1355
- return this.withTarget(
1356
- async (target) => expectString(await this.window.call("html", this.scoped(target)), "html result")
1357
- );
1290
+ return this.withTarget(async (target) => expectString(await this.window.call("html", this.scoped(target)), "html result"));
1358
1291
  }
1359
1292
  async getAttribute(name) {
1360
1293
  const attributes = await this.attributes();
1361
- return Object.prototype.hasOwnProperty.call(attributes, name) ? attributes[name] : null;
1294
+ return Object.hasOwn(attributes, name) ? attributes[name] : null;
1362
1295
  }
1363
- /** Drag this element onto another one in the same window. */
1364
1296
  async dragTo(target) {
1365
1297
  if (target.window !== this.window) {
1366
1298
  throw new Error("dragTo requires both locators to belong to the same window");
@@ -1374,33 +1306,26 @@ var HasgardLocator = class _HasgardLocator {
1374
1306
  });
1375
1307
  });
1376
1308
  }
1377
- /** Drag this element by a pixel offset from its centre. */
1378
1309
  async dragBy(offset) {
1379
- await this.withTarget(
1380
- (target) => this.window.call("drag", { source: this.scoped(target), offset: { x: offset.x, y: offset.y } })
1381
- );
1310
+ await this.withTarget((target) => this.window.call("drag", { source: this.scoped(target), offset: { x: offset.x, y: offset.y } }));
1382
1311
  }
1383
- /** Dispatch a file drop onto this element. `data` is base64-encoded. */
1384
1312
  async dropFiles(files) {
1385
- await this.withTarget(
1386
- (target) => this.window.call("drop", {
1387
- ...this.scoped(target),
1388
- files
1389
- })
1390
- );
1313
+ await this.withTarget((target) => this.window.call("drop", {
1314
+ ...this.scoped(target),
1315
+ files
1316
+ }));
1391
1317
  }
1392
1318
  async attributes() {
1393
1319
  return this.withTarget(async (target) => {
1394
1320
  const value = expectRecord(await this.window.call("attrs", this.scoped(target)), "attrs result");
1395
- return Object.fromEntries(
1396
- Object.entries(value).map(([name, attr]) => [name, expectString(attr, `attribute ${name}`)])
1397
- );
1321
+ return Object.fromEntries(Object.entries(value).map(([name, attr]) => [name, expectString(attr, `attribute ${name}`)]));
1398
1322
  });
1399
1323
  }
1400
1324
  async isVisible() {
1401
1325
  return this.window.runExclusive(async () => {
1402
1326
  const target = await this.resolveUnique(true);
1403
- if (target === null) return false;
1327
+ if (target === null)
1328
+ return false;
1404
1329
  const value = expectRecord(await this.window.call("visible", this.scoped(target)), "visible result");
1405
1330
  return expectBoolean(value.visible, "visible.visible");
1406
1331
  });
@@ -1412,19 +1337,14 @@ var HasgardLocator = class _HasgardLocator {
1412
1337
  });
1413
1338
  }
1414
1339
  async count() {
1415
- const total = this.query.kind === "selector" && !this.query.filters?.length ? expectNumber(
1416
- expectRecord(
1417
- await this.window.call("count", { selector: this.query.selector, ...this.frameParams() }),
1418
- "count result"
1419
- ).count,
1420
- "count.count"
1421
- ) : (await this.resolveAll()).length;
1422
- if (this.query.index === void 0) return total;
1340
+ const total = this.query.kind === "selector" && !this.query.filters?.length ? expectNumber(expectRecord(await this.window.call("count", { selector: this.query.selector, ...this.frameParams() }), "count result").count, "count.count") : (await this.resolveAll()).length;
1341
+ if (this.query.index === undefined)
1342
+ return total;
1423
1343
  const position = absoluteIndex(this.query.index, total);
1424
1344
  return position >= 0 && position < total ? 1 : 0;
1425
1345
  }
1426
1346
  async waitFor(options) {
1427
- if (this.query.kind === "selector" && this.query.index === void 0 && !this.query.filters?.length) {
1347
+ if (this.query.kind === "selector" && this.query.index === undefined && !this.query.filters?.length) {
1428
1348
  await this.window.call("wait", {
1429
1349
  selector: this.query.selector,
1430
1350
  gone: options.state === "detached",
@@ -1436,32 +1356,22 @@ var HasgardLocator = class _HasgardLocator {
1436
1356
  const deadline = Date.now() + options.timeoutMs;
1437
1357
  while (Date.now() <= deadline) {
1438
1358
  const found = await this.count() > 0;
1439
- if (found === (options.state === "attached")) return;
1359
+ if (found === (options.state === "attached"))
1360
+ return;
1440
1361
  await new Promise((resolve) => setTimeout(resolve, 50));
1441
1362
  }
1442
1363
  throw new Error(`Locator did not become ${options.state} within ${options.timeoutMs}ms`);
1443
1364
  }
1444
- /**
1445
- * Narrow this locator to a single match by position. A negative index counts
1446
- * back from the end, so `last()` stays one round trip.
1447
- */
1448
1365
  nth(index) {
1449
- if (!Number.isInteger(index)) throw new Error("nth requires an integer index");
1450
- return new _HasgardLocator(this.window, { ...this.query, index });
1451
- }
1452
- /**
1453
- * Narrow this locator to the elements whose text matches.
1454
- *
1455
- * Filters accumulate, so `filter({hasText}).filter({hasNotText})` applies
1456
- * both. Ordering matters against `nth`: filtering happens first, so
1457
- * `filter({hasText: "x"}).first()` is the first *matching* element, which is
1458
- * the reading that makes `first()` mean what it says.
1459
- */
1366
+ if (!Number.isInteger(index))
1367
+ throw new Error("nth requires an integer index");
1368
+ return new HasgardLocator(this.window, { ...this.query, index });
1369
+ }
1460
1370
  filter(options) {
1461
- if (options.hasText === void 0 && options.hasNotText === void 0) {
1371
+ if (options.hasText === undefined && options.hasNotText === undefined) {
1462
1372
  throw new Error("filter requires hasText or hasNotText");
1463
1373
  }
1464
- return new _HasgardLocator(this.window, {
1374
+ return new HasgardLocator(this.window, {
1465
1375
  ...this.query,
1466
1376
  filters: [...this.query.filters ?? [], options]
1467
1377
  });
@@ -1474,30 +1384,25 @@ var HasgardLocator = class _HasgardLocator {
1474
1384
  }
1475
1385
  async resolveUnique(allowMissing = false) {
1476
1386
  if (this.query.kind === "selector" && !this.query.filters?.length) {
1477
- return this.query.index === void 0 ? bySelector(this.query.selector) : { selector: this.query.selector, index: this.query.index };
1387
+ return this.query.index === undefined ? bySelector(this.query.selector) : { selector: this.query.selector, index: this.query.index };
1478
1388
  }
1479
1389
  const elements = await this.resolveAll();
1480
- if (this.query.index !== void 0) {
1390
+ if (this.query.index !== undefined) {
1481
1391
  const element = elements[absoluteIndex(this.query.index, elements.length)];
1482
1392
  if (!element) {
1483
- if (allowMissing) return null;
1393
+ if (allowMissing)
1394
+ return null;
1484
1395
  throw new Error(`Locator has no element at index ${this.query.index} (${elements.length} matched)`);
1485
1396
  }
1486
1397
  return byRef(element.ref);
1487
1398
  }
1488
- if (elements.length === 0 && allowMissing) return null;
1399
+ if (elements.length === 0 && allowMissing)
1400
+ return null;
1489
1401
  if (elements.length !== 1) {
1490
1402
  throw new Error(`Locator resolved to ${elements.length} elements; expected exactly one`);
1491
1403
  }
1492
1404
  return byRef(elements[0].ref);
1493
1405
  }
1494
- /**
1495
- * Serialize a target together with the frame chain this locator lives in.
1496
- *
1497
- * A ref carries its own document, so the bridge ignores `frame` for one; the
1498
- * chain still travels so that a selector or coordinate target -- which the
1499
- * bridge must resolve itself -- lands in the right document.
1500
- */
1501
1406
  scoped(target) {
1502
1407
  return { ...targetParams(target), ...this.frameParams() };
1503
1408
  }
@@ -1516,36 +1421,38 @@ var HasgardLocator = class _HasgardLocator {
1516
1421
  }
1517
1422
  if (this.query.kind === "query") {
1518
1423
  const params = { by: this.query.by, value: this.query.value };
1519
- if (this.query.query.exact !== void 0) params.exact = this.query.query.exact;
1424
+ if (this.query.query.exact !== undefined)
1425
+ params.exact = this.query.query.exact;
1520
1426
  return this.runQuery(params);
1521
1427
  }
1522
1428
  const query = this.query;
1523
1429
  const snapshot = await this.window.snapshot(query.frame ? { frame: [...query.frame] } : {});
1524
1430
  return snapshot.elements.filter((element) => {
1525
- if (element.role !== query.role) return false;
1431
+ if (element.role !== query.role)
1432
+ return false;
1526
1433
  const expected = query.query.name;
1527
- if (expected === void 0) return true;
1528
- if (element.name === void 0) return false;
1529
- if (expected instanceof RegExp) return expected.test(element.name);
1434
+ if (expected === undefined)
1435
+ return true;
1436
+ if (element.name === undefined)
1437
+ return false;
1438
+ if (expected instanceof RegExp)
1439
+ return expected.test(element.name);
1530
1440
  return query.query.exact ? element.name === expected : element.name.includes(expected);
1531
1441
  });
1532
1442
  }
1533
- /**
1534
- * Apply each accumulated filter, one round trip per filter.
1535
- *
1536
- * Filters run server-side rather than against a snapshot because the text a
1537
- * filter matches on is not carried in snapshot entries — deliberately, so
1538
- * that every unrelated `snapshot` call stays small.
1539
- */
1540
1443
  async applyFilters(elements) {
1541
1444
  const filters = this.query.filters ?? [];
1542
1445
  let current = elements;
1543
1446
  for (const filter of filters) {
1544
- if (current.length === 0) return current;
1447
+ if (current.length === 0)
1448
+ return current;
1545
1449
  const params = { refs: current.map((element) => element.ref) };
1546
- if (filter.hasText !== void 0) params.hasText = filter.hasText;
1547
- if (filter.hasNotText !== void 0) params.hasNotText = filter.hasNotText;
1548
- if (filter.exact !== void 0) params.exact = filter.exact;
1450
+ if (filter.hasText !== undefined)
1451
+ params.hasText = filter.hasText;
1452
+ if (filter.hasNotText !== undefined)
1453
+ params.hasNotText = filter.hasNotText;
1454
+ if (filter.exact !== undefined)
1455
+ params.exact = filter.exact;
1549
1456
  current = this.parseElements(await this.window.call("filter", params), "filter");
1550
1457
  }
1551
1458
  return current;
@@ -1555,10 +1462,11 @@ var HasgardLocator = class _HasgardLocator {
1555
1462
  }
1556
1463
  parseElements(raw, source) {
1557
1464
  const value = expectRecord(raw, `${source} result`);
1558
- if (!Array.isArray(value.elements)) throw new Error(`${source}.elements must be an array`);
1465
+ if (!Array.isArray(value.elements))
1466
+ throw new Error(`${source}.elements must be an array`);
1559
1467
  return value.elements.map((entry, index) => parseSnapshotElement(entry, index));
1560
1468
  }
1561
- };
1469
+ }
1562
1470
  function parseDialogPolicy(value) {
1563
1471
  const record = expectRecord(value, "dialog policy");
1564
1472
  const action = expectString(record.action, "dialog policy.action");
@@ -1583,10 +1491,11 @@ function parseDialogRecord(value, index) {
1583
1491
  message: expectString(entry.message, `dialogs[${index}].message`),
1584
1492
  accepted: expectBoolean(entry.accepted, `dialogs[${index}].accepted`)
1585
1493
  };
1586
- if (entry.defaultValue !== void 0) {
1494
+ if (entry.defaultValue !== undefined) {
1587
1495
  record.defaultValue = expectString(entry.defaultValue, `dialogs[${index}].defaultValue`);
1588
1496
  }
1589
- if (entry.returned !== void 0) record.returned = expectString(entry.returned, `dialogs[${index}].returned`);
1497
+ if (entry.returned !== undefined)
1498
+ record.returned = expectString(entry.returned, `dialogs[${index}].returned`);
1590
1499
  return record;
1591
1500
  }
1592
1501
  function parseSnapshotElement(value, index) {
@@ -1596,10 +1505,13 @@ function parseSnapshotElement(value, index) {
1596
1505
  role: expectString(entry.role, `snapshot.elements[${index}].role`),
1597
1506
  depth: expectNumber(entry.depth, `snapshot.elements[${index}].depth`)
1598
1507
  };
1599
- if (entry.name !== void 0) element.name = expectString(entry.name, `snapshot.elements[${index}].name`);
1600
- if (entry.value !== void 0) element.value = expectString(entry.value, `snapshot.elements[${index}].value`);
1601
- if (entry.checked !== void 0) element.checked = expectBoolean(entry.checked, `snapshot.elements[${index}].checked`);
1602
- if (entry.disabled !== void 0)
1508
+ if (entry.name !== undefined)
1509
+ element.name = expectString(entry.name, `snapshot.elements[${index}].name`);
1510
+ if (entry.value !== undefined)
1511
+ element.value = expectString(entry.value, `snapshot.elements[${index}].value`);
1512
+ if (entry.checked !== undefined)
1513
+ element.checked = expectBoolean(entry.checked, `snapshot.elements[${index}].checked`);
1514
+ if (entry.disabled !== undefined)
1603
1515
  element.disabled = expectBoolean(entry.disabled, `snapshot.elements[${index}].disabled`);
1604
1516
  return element;
1605
1517
  }
@@ -1608,23 +1520,26 @@ function parseSnapshotElement(value, index) {
1608
1520
  function createHasgardTest(config) {
1609
1521
  const base = config.test;
1610
1522
  const expect = createHasgardExpect(config.expect);
1611
- const readinessTimeoutMs = config.launch ? config.launch.timeoutMs : 3e4;
1612
- const fixtureTimeoutMs = config.launch ? config.launch.timeoutMs + 3e4 : 3e4;
1613
- const processes = /* @__PURE__ */ new WeakMap();
1523
+ const readinessTimeoutMs = config.launch ? config.launch.timeoutMs : 30000;
1524
+ const fixtureTimeoutMs = config.launch ? config.launch.timeoutMs + 30000 : 30000;
1525
+ const processes = new WeakMap;
1614
1526
  const test = base.extend({
1615
1527
  hasgard: [
1616
1528
  async ({}, use, workerInfo) => {
1617
1529
  const socketPath = typeof config.socketPath === "function" ? config.socketPath(workerInfo.workerIndex) : config.socketPath;
1618
- const process2 = new HasgardProcess();
1530
+ const process2 = new HasgardProcess;
1619
1531
  const rpc = new HasgardRpcClient(socketPath);
1620
1532
  try {
1621
- if (config.launch) await process2.start(config.launch, socketPath);
1622
- else await process2.waitForSocket(socketPath, workerInfo.project.timeout);
1533
+ if (config.launch)
1534
+ await process2.start(config.launch, socketPath);
1535
+ else
1536
+ await process2.waitForSocket(socketPath, workerInfo.project.timeout);
1623
1537
  await rpc.connect(workerInfo.project.timeout);
1624
1538
  const hasgard = new HasgardApplication(rpc);
1625
1539
  processes.set(hasgard, process2);
1626
1540
  const ping = await hasgard.ping();
1627
- if (ping.status !== "ok") throw new Error(`Unexpected Hasgard ping status: ${ping.status}`);
1541
+ if (ping.status !== "ok")
1542
+ throw new Error(`Unexpected Hasgard ping status: ${ping.status}`);
1628
1543
  await hasgard.waitForWindowReady(config.windowLabel, config.readySelector, readinessTimeoutMs);
1629
1544
  await use(hasgard);
1630
1545
  } catch (error) {
@@ -1653,7 +1568,8 @@ function createHasgardTest(config) {
1653
1568
  body: JSON.stringify(diagnostics, null, 2),
1654
1569
  contentType: "application/json"
1655
1570
  });
1656
- if (diagnostics.exit) console.error(`Hasgard application exited: ${JSON.stringify(diagnostics)}`);
1571
+ if (diagnostics.exit)
1572
+ console.error(`Hasgard application exited: ${JSON.stringify(diagnostics)}`);
1657
1573
  }
1658
1574
  try {
1659
1575
  const screenshot = await hasgard.window(config.windowLabel).screenshot();