craftdriver 0.2.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/lib/bidi/connection.d.ts +1 -1
  3. package/dist/lib/bidi/connection.d.ts.map +1 -1
  4. package/dist/lib/bidi/connection.js +3 -2
  5. package/dist/lib/bidi/connection.js.map +1 -1
  6. package/dist/lib/bidi/index.d.ts +40 -3
  7. package/dist/lib/bidi/index.d.ts.map +1 -1
  8. package/dist/lib/bidi/index.js +55 -18
  9. package/dist/lib/bidi/index.js.map +1 -1
  10. package/dist/lib/bidi/logs.d.ts.map +1 -1
  11. package/dist/lib/bidi/logs.js +7 -2
  12. package/dist/lib/bidi/logs.js.map +1 -1
  13. package/dist/lib/bidi/network.d.ts +9 -0
  14. package/dist/lib/bidi/network.d.ts.map +1 -1
  15. package/dist/lib/bidi/network.js +26 -11
  16. package/dist/lib/bidi/network.js.map +1 -1
  17. package/dist/lib/browser.d.ts +42 -4
  18. package/dist/lib/browser.d.ts.map +1 -1
  19. package/dist/lib/browser.js +137 -60
  20. package/dist/lib/browser.js.map +1 -1
  21. package/dist/lib/browserContext.d.ts +7 -2
  22. package/dist/lib/browserContext.d.ts.map +1 -1
  23. package/dist/lib/browserContext.js +15 -6
  24. package/dist/lib/browserContext.js.map +1 -1
  25. package/dist/lib/builder.d.ts +2 -1
  26. package/dist/lib/builder.d.ts.map +1 -1
  27. package/dist/lib/builder.js +35 -5
  28. package/dist/lib/builder.js.map +1 -1
  29. package/dist/lib/driver.d.ts.map +1 -1
  30. package/dist/lib/driver.js +21 -6
  31. package/dist/lib/driver.js.map +1 -1
  32. package/dist/lib/driverManager.d.ts +6 -0
  33. package/dist/lib/driverManager.d.ts.map +1 -1
  34. package/dist/lib/driverManager.js +97 -22
  35. package/dist/lib/driverManager.js.map +1 -1
  36. package/dist/lib/expect.d.ts.map +1 -1
  37. package/dist/lib/expect.js +91 -229
  38. package/dist/lib/expect.js.map +1 -1
  39. package/dist/lib/firefox.d.ts.map +1 -1
  40. package/dist/lib/firefox.js +2 -1
  41. package/dist/lib/firefox.js.map +1 -1
  42. package/dist/lib/frame.d.ts.map +1 -1
  43. package/dist/lib/frame.js +4 -3
  44. package/dist/lib/frame.js.map +1 -1
  45. package/dist/lib/http.d.ts +7 -0
  46. package/dist/lib/http.d.ts.map +1 -1
  47. package/dist/lib/http.js +37 -0
  48. package/dist/lib/http.js.map +1 -1
  49. package/dist/lib/loadState.d.ts +23 -0
  50. package/dist/lib/loadState.d.ts.map +1 -0
  51. package/dist/lib/loadState.js +25 -0
  52. package/dist/lib/loadState.js.map +1 -0
  53. package/dist/lib/locator.d.ts.map +1 -1
  54. package/dist/lib/locator.js +11 -4
  55. package/dist/lib/locator.js.map +1 -1
  56. package/dist/lib/mouse.d.ts.map +1 -1
  57. package/dist/lib/mouse.js +2 -1
  58. package/dist/lib/mouse.js.map +1 -1
  59. package/dist/lib/page.d.ts +14 -1
  60. package/dist/lib/page.d.ts.map +1 -1
  61. package/dist/lib/page.js +41 -9
  62. package/dist/lib/page.js.map +1 -1
  63. package/dist/lib/service.d.ts +1 -0
  64. package/dist/lib/service.d.ts.map +1 -1
  65. package/dist/lib/service.js +21 -3
  66. package/dist/lib/service.js.map +1 -1
  67. package/dist/lib/timing.d.ts +104 -0
  68. package/dist/lib/timing.d.ts.map +1 -0
  69. package/dist/lib/timing.js +108 -0
  70. package/dist/lib/timing.js.map +1 -0
  71. package/dist/lib/wait.d.ts.map +1 -1
  72. package/dist/lib/wait.js +5 -4
  73. package/dist/lib/wait.js.map +1 -1
  74. package/docs/bidi-features.md +10 -2
  75. package/docs/browser-api.md +2 -1
  76. package/docs/browser-context.md +11 -14
  77. package/docs/dialogs.md +14 -3
  78. package/docs/driver-configuration.md +126 -8
  79. package/docs/emulation.md +8 -4
  80. package/docs/error-codes.md +8 -3
  81. package/docs/keyboard-mouse.md +20 -7
  82. package/docs/session-management.md +1 -1
  83. package/package.json +4 -1
  84. package/skills/craftdriver/SKILL.md +3 -2
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Centralized timing constants: poll intervals, timeouts, fixed delays, and
3
+ * retry policy. Kept in one place so they are easy to find and tune rather than
4
+ * scattered as magic numbers across the codebase. All values are milliseconds.
5
+ *
6
+ * This module intentionally has no imports so any file can depend on it without
7
+ * risking an import cycle.
8
+ */
9
+ /**
10
+ * Base cadence for all auto-wait retry loops. Kept short so a condition that
11
+ * flips true mid-interval is noticed quickly rather than up to a full interval
12
+ * late. The first check of every loop runs immediately, so conditions that are
13
+ * already satisfied never pay it — it only affects genuine waits. Round trips
14
+ * to a local driver are cheap (~a few ms), so polling this often is fine.
15
+ *
16
+ * Used for element visibility/existence waits and `Locator` resolution.
17
+ */
18
+ export declare const DEFAULT_POLL_INTERVAL_MS = 25;
19
+ /**
20
+ * How often `expect(...)` assertion retries re-check their matcher (text,
21
+ * value, class, …). Same responsive cadence as {@link DEFAULT_POLL_INTERVAL_MS}
22
+ * so an assertion that becomes true shortly after a failed check passes almost
23
+ * immediately instead of waiting out a long interval.
24
+ */
25
+ export declare const ASSERTION_POLL_INTERVAL_MS = 25;
26
+ /** Poll interval for the driver service's `/status` readiness check. */
27
+ export declare const DRIVER_READINESS_POLL_INTERVAL_MS = 25;
28
+ /**
29
+ * Poll interval for state that changes at process/OS speed rather than DOM
30
+ * speed: Classic `document.readyState`, the Classic `waitForPage`
31
+ * window-handle fallback, and download-file polling. Kept at the same 25ms
32
+ * cadence for responsiveness — these checks (a script eval, a window-handle
33
+ * list, an `fs.stat`) are cheap enough to run this often.
34
+ */
35
+ export declare const STATE_POLL_INTERVAL_MS = 25;
36
+ /**
37
+ * Per-attempt cap on how long one `expect(...)` retry waits for the element to
38
+ * *exist* before looping back to re-run the whole matcher. This does NOT govern
39
+ * how fast an assertion re-checks — the matcher is re-evaluated every
40
+ * {@link ASSERTION_POLL_INTERVAL_MS}, and an element that already exists returns
41
+ * from the existence wait immediately. Existence itself is polled at
42
+ * {@link DEFAULT_POLL_INTERVAL_MS} inside this window, so a slow-to-appear
43
+ * element is still caught within ~25ms; the cap only bounds a single attempt so
44
+ * the outer loop keeps its cadence and honours the overall timeout.
45
+ */
46
+ export declare const ASSERTION_INNER_WAIT_CAP_MS = 250;
47
+ /** Default timeout for element interactions and waits (click, fill, find, …). */
48
+ export declare const DEFAULT_ELEMENT_TIMEOUT_MS = 5000;
49
+ /**
50
+ * Default timeout for navigation and network/event waits (navigateTo,
51
+ * waitForRequest/Response, log waits, download waits).
52
+ */
53
+ export declare const DEFAULT_NAVIGATION_TIMEOUT_MS = 30000;
54
+ /** Default timeout for the BiDi WebSocket connect and for each BiDi command. */
55
+ export declare const BIDI_TIMEOUT_MS = 30000;
56
+ /** How long to wait for the driver service to report ready after spawning. */
57
+ export declare const DRIVER_READINESS_TIMEOUT_MS = 5000;
58
+ /**
59
+ * Readiness deadline for geckodriver specifically — it takes longer than
60
+ * chromedriver to spin up Marionette and start listening.
61
+ */
62
+ export declare const FIREFOX_READINESS_TIMEOUT_MS = 15000;
63
+ /** Timeout for the `<driver|browser> --version` capability probe spawn. */
64
+ export declare const VERSION_PROBE_TIMEOUT_MS = 5000;
65
+ /** Timeout for the `which` / `where` PATH-probe spawn. */
66
+ export declare const PATH_PROBE_TIMEOUT_MS = 2000;
67
+ /** Best-effort settle used as the Classic fallback for `networkidle`. */
68
+ export declare const NETWORK_IDLE_SETTLE_MS = 500;
69
+ /**
70
+ * Pause between `driver.quit()` and stopping the driver service, giving the
71
+ * browser time to release its ports (notably Firefox's BiDi WebSocket) before a
72
+ * subsequent launch reuses them; without it a fresh launch can hit a 404.
73
+ */
74
+ export declare const PORT_RELEASE_DELAY_MS = 500;
75
+ /** Delay before a BiDi connection auto-reconnect attempt. */
76
+ export declare const BIDI_RECONNECT_DELAY_MS = 1000;
77
+ /**
78
+ * BiDi connect retries. Firefox may not have finished binding its BiDi
79
+ * WebSocket right after the session is created; back off linearly between
80
+ * attempts (delay = {@link BIDI_CONNECT_BACKOFF_STEP_MS} × attempt).
81
+ */
82
+ export declare const BIDI_CONNECT_MAX_ATTEMPTS = 8;
83
+ export declare const BIDI_CONNECT_BACKOFF_STEP_MS = 300;
84
+ /**
85
+ * WebDriver session-creation retries. Firefox's Marionette interface can lag
86
+ * geckodriver reporting healthy, so retry with linear backoff (delay =
87
+ * {@link SESSION_CREATE_BACKOFF_STEP_MS} × attempt); Chrome is reliable enough
88
+ * to need a single attempt.
89
+ */
90
+ export declare const FIREFOX_SESSION_MAX_ATTEMPTS = 4;
91
+ export declare const CHROME_SESSION_MAX_ATTEMPTS = 1;
92
+ export declare const SESSION_CREATE_BACKOFF_STEP_MS = 500;
93
+ /**
94
+ * `evaluate()` retries on a transient "execution contexts cleared" BiDi error.
95
+ * A Classic-first navigate returns at `readyState === 'complete'`, which is not
96
+ * a barrier the BiDi side respects — an immediately following `{ context }`
97
+ * script call can land while BiDi is still swapping the old realm for the new
98
+ * one. The error is pre-execution (the script never ran, no side effects), so a
99
+ * short retry that re-resolves the context is safe. See
100
+ * plans/TODO-bidi-first-navigation.md (Option A).
101
+ */
102
+ export declare const EVAL_REALM_RETRY_ATTEMPTS = 3;
103
+ export declare const EVAL_REALM_RETRY_DELAY_MS = 25;
104
+ //# sourceMappingURL=timing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../../src/lib/timing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C,wEAAwE;AACxE,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAI/C,iFAAiF;AACjF,eAAO,MAAM,0BAA0B,OAAQ,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,6BAA6B,QAAS,CAAC;AAEpD,gFAAgF;AAChF,eAAO,MAAM,eAAe,QAAS,CAAC;AAEtC,8EAA8E;AAC9E,eAAO,MAAM,2BAA2B,OAAQ,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAE9C,0DAA0D;AAC1D,eAAO,MAAM,qBAAqB,OAAQ,CAAC;AAI3C,yEAAyE;AACzE,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,6DAA6D;AAC7D,eAAO,MAAM,uBAAuB,OAAQ,CAAC;AAI7C;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAC3C,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAC9C,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAC7C,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAC3C,eAAO,MAAM,yBAAyB,KAAK,CAAC"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Centralized timing constants: poll intervals, timeouts, fixed delays, and
3
+ * retry policy. Kept in one place so they are easy to find and tune rather than
4
+ * scattered as magic numbers across the codebase. All values are milliseconds.
5
+ *
6
+ * This module intentionally has no imports so any file can depend on it without
7
+ * risking an import cycle.
8
+ */
9
+ // ── Auto-wait / retry poll intervals ─────────────────────────────────────────
10
+ /**
11
+ * Base cadence for all auto-wait retry loops. Kept short so a condition that
12
+ * flips true mid-interval is noticed quickly rather than up to a full interval
13
+ * late. The first check of every loop runs immediately, so conditions that are
14
+ * already satisfied never pay it — it only affects genuine waits. Round trips
15
+ * to a local driver are cheap (~a few ms), so polling this often is fine.
16
+ *
17
+ * Used for element visibility/existence waits and `Locator` resolution.
18
+ */
19
+ export const DEFAULT_POLL_INTERVAL_MS = 25;
20
+ /**
21
+ * How often `expect(...)` assertion retries re-check their matcher (text,
22
+ * value, class, …). Same responsive cadence as {@link DEFAULT_POLL_INTERVAL_MS}
23
+ * so an assertion that becomes true shortly after a failed check passes almost
24
+ * immediately instead of waiting out a long interval.
25
+ */
26
+ export const ASSERTION_POLL_INTERVAL_MS = 25;
27
+ /** Poll interval for the driver service's `/status` readiness check. */
28
+ export const DRIVER_READINESS_POLL_INTERVAL_MS = 25;
29
+ /**
30
+ * Poll interval for state that changes at process/OS speed rather than DOM
31
+ * speed: Classic `document.readyState`, the Classic `waitForPage`
32
+ * window-handle fallback, and download-file polling. Kept at the same 25ms
33
+ * cadence for responsiveness — these checks (a script eval, a window-handle
34
+ * list, an `fs.stat`) are cheap enough to run this often.
35
+ */
36
+ export const STATE_POLL_INTERVAL_MS = 25;
37
+ /**
38
+ * Per-attempt cap on how long one `expect(...)` retry waits for the element to
39
+ * *exist* before looping back to re-run the whole matcher. This does NOT govern
40
+ * how fast an assertion re-checks — the matcher is re-evaluated every
41
+ * {@link ASSERTION_POLL_INTERVAL_MS}, and an element that already exists returns
42
+ * from the existence wait immediately. Existence itself is polled at
43
+ * {@link DEFAULT_POLL_INTERVAL_MS} inside this window, so a slow-to-appear
44
+ * element is still caught within ~25ms; the cap only bounds a single attempt so
45
+ * the outer loop keeps its cadence and honours the overall timeout.
46
+ */
47
+ export const ASSERTION_INNER_WAIT_CAP_MS = 250;
48
+ // ── Default timeouts ─────────────────────────────────────────────────────────
49
+ /** Default timeout for element interactions and waits (click, fill, find, …). */
50
+ export const DEFAULT_ELEMENT_TIMEOUT_MS = 5_000;
51
+ /**
52
+ * Default timeout for navigation and network/event waits (navigateTo,
53
+ * waitForRequest/Response, log waits, download waits).
54
+ */
55
+ export const DEFAULT_NAVIGATION_TIMEOUT_MS = 30_000;
56
+ /** Default timeout for the BiDi WebSocket connect and for each BiDi command. */
57
+ export const BIDI_TIMEOUT_MS = 30_000;
58
+ /** How long to wait for the driver service to report ready after spawning. */
59
+ export const DRIVER_READINESS_TIMEOUT_MS = 5_000;
60
+ /**
61
+ * Readiness deadline for geckodriver specifically — it takes longer than
62
+ * chromedriver to spin up Marionette and start listening.
63
+ */
64
+ export const FIREFOX_READINESS_TIMEOUT_MS = 15_000;
65
+ /** Timeout for the `<driver|browser> --version` capability probe spawn. */
66
+ export const VERSION_PROBE_TIMEOUT_MS = 5_000;
67
+ /** Timeout for the `which` / `where` PATH-probe spawn. */
68
+ export const PATH_PROBE_TIMEOUT_MS = 2_000;
69
+ // ── Fixed delays ─────────────────────────────────────────────────────────────
70
+ /** Best-effort settle used as the Classic fallback for `networkidle`. */
71
+ export const NETWORK_IDLE_SETTLE_MS = 500;
72
+ /**
73
+ * Pause between `driver.quit()` and stopping the driver service, giving the
74
+ * browser time to release its ports (notably Firefox's BiDi WebSocket) before a
75
+ * subsequent launch reuses them; without it a fresh launch can hit a 404.
76
+ */
77
+ export const PORT_RELEASE_DELAY_MS = 500;
78
+ /** Delay before a BiDi connection auto-reconnect attempt. */
79
+ export const BIDI_RECONNECT_DELAY_MS = 1_000;
80
+ // ── Retry policy ─────────────────────────────────────────────────────────────
81
+ /**
82
+ * BiDi connect retries. Firefox may not have finished binding its BiDi
83
+ * WebSocket right after the session is created; back off linearly between
84
+ * attempts (delay = {@link BIDI_CONNECT_BACKOFF_STEP_MS} × attempt).
85
+ */
86
+ export const BIDI_CONNECT_MAX_ATTEMPTS = 8;
87
+ export const BIDI_CONNECT_BACKOFF_STEP_MS = 300;
88
+ /**
89
+ * WebDriver session-creation retries. Firefox's Marionette interface can lag
90
+ * geckodriver reporting healthy, so retry with linear backoff (delay =
91
+ * {@link SESSION_CREATE_BACKOFF_STEP_MS} × attempt); Chrome is reliable enough
92
+ * to need a single attempt.
93
+ */
94
+ export const FIREFOX_SESSION_MAX_ATTEMPTS = 4;
95
+ export const CHROME_SESSION_MAX_ATTEMPTS = 1;
96
+ export const SESSION_CREATE_BACKOFF_STEP_MS = 500;
97
+ /**
98
+ * `evaluate()` retries on a transient "execution contexts cleared" BiDi error.
99
+ * A Classic-first navigate returns at `readyState === 'complete'`, which is not
100
+ * a barrier the BiDi side respects — an immediately following `{ context }`
101
+ * script call can land while BiDi is still swapping the old realm for the new
102
+ * one. The error is pre-execution (the script never ran, no side effects), so a
103
+ * short retry that re-resolves the context is safe. See
104
+ * plans/TODO-bidi-first-navigation.md (Option A).
105
+ */
106
+ export const EVAL_REALM_RETRY_ATTEMPTS = 3;
107
+ export const EVAL_REALM_RETRY_DELAY_MS = 25;
108
+ //# sourceMappingURL=timing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timing.js","sourceRoot":"","sources":["../../src/lib/timing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,gFAAgF;AAEhF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,CAAC;AAE7C,wEAAwE;AACxE,MAAM,CAAC,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAEzC;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,gFAAgF;AAEhF,iFAAiF;AACjF,MAAM,CAAC,MAAM,0BAA0B,GAAG,KAAK,CAAC;AAEhD;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,MAAM,CAAC;AAEpD,gFAAgF;AAChF,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC;AAEtC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAC;AAEjD;;;GAGG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,MAAM,CAAC;AAEnD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAE9C,0DAA0D;AAC1D,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,CAAC;AAE3C,gFAAgF;AAEhF,yEAAyE;AACzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAE1C;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAEzC,6DAA6D;AAC7D,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,CAAC;AAE7C,gFAAgF;AAEhF;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAC3C,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAG,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC;AAC9C,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAC7C,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAElD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAC3C,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"wait.d.ts","sourceRoot":"","sources":["../../src/lib/wait.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,MAAM,MAAM,SAAS,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAEhE,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,aAAa;IAMtB,OAAO,CAAC,MAAM;IALhB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAC,CAAS;gBAGlB,MAAM,EAAE,MAAM,EACtB,gBAAgB,CAAC,EAAE,MAAM,GAAG,WAAW,EACvC,UAAU,CAAC,EAAE,MAAM;IAYf,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CA8BtE;AAOD,eAAO,MAAM,KAAK;8BACU,EAAE,MAAY,QAAQ,MAAM;6BAI7B,EAAE,MAAY,QAAQ,MAAM;gCASzB,EAAE,MAAY,QAAQ,MAAM;+BAS7B,EAAE,GAAG,UAAU,MAAY,QAAQ,MAAM;kCAUtC,EAAE,GAAG,UAAU,MAAY,QAAQ,MAAM;CAaxE,CAAC"}
1
+ {"version":3,"file":"wait.d.ts","sourceRoot":"","sources":["../../src/lib/wait.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI7C,MAAM,MAAM,SAAS,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAEhE,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,aAAa;IAMtB,OAAO,CAAC,MAAM;IALhB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAC,CAAS;gBAGlB,MAAM,EAAE,MAAM,EACtB,gBAAgB,CAAC,EAAE,MAAM,GAAG,WAAW,EACvC,UAAU,CAAC,EAAE,MAAM;IAYf,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CA8BtE;AAOD,eAAO,MAAM,KAAK;8BACU,EAAE,MAAY,QAAQ,MAAM;6BAI7B,EAAE,MAAY,QAAQ,MAAM;gCASzB,EAAE,MAAY,QAAQ,MAAM;+BAS7B,EAAE,GAAG,UAAU,MAAY,QAAQ,MAAM;kCAUtC,EAAE,GAAG,UAAU,MAAY,QAAQ,MAAM;CAaxE,CAAC"}
package/dist/lib/wait.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { WebElement } from './webelement.js';
2
2
  import { CraftdriverError, ErrorCode } from './errors.js';
3
+ import { DEFAULT_POLL_INTERVAL_MS, DEFAULT_ELEMENT_TIMEOUT_MS } from './timing.js';
3
4
  export class WebDriverWait {
4
5
  driver;
5
6
  timeoutMs;
@@ -8,13 +9,13 @@ export class WebDriverWait {
8
9
  constructor(driver, timeoutOrOptions, intervalMs) {
9
10
  this.driver = driver;
10
11
  if (typeof timeoutOrOptions === 'object') {
11
- this.timeoutMs = timeoutOrOptions?.timeout ?? 5000;
12
- this.intervalMs = timeoutOrOptions?.interval ?? 100;
12
+ this.timeoutMs = timeoutOrOptions?.timeout ?? DEFAULT_ELEMENT_TIMEOUT_MS;
13
+ this.intervalMs = timeoutOrOptions?.interval ?? DEFAULT_POLL_INTERVAL_MS;
13
14
  this.timeoutMsg = timeoutOrOptions?.timeoutMsg;
14
15
  }
15
16
  else {
16
- this.timeoutMs = timeoutOrOptions ?? 5000;
17
- this.intervalMs = intervalMs ?? 100;
17
+ this.timeoutMs = timeoutOrOptions ?? DEFAULT_ELEMENT_TIMEOUT_MS;
18
+ this.intervalMs = intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
18
19
  }
19
20
  }
20
21
  async until(condition, message) {
@@ -1 +1 @@
1
- {"version":3,"file":"wait.js","sourceRoot":"","sources":["../../src/lib/wait.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wait.js","sourceRoot":"","sources":["../../src/lib/wait.ts"],"names":[],"mappings":"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"}
@@ -19,8 +19,8 @@ in Classic mode.
19
19
  |---|---|---|
20
20
  | Network mocking / interception | [`browser.network.*`](#network-mocking) | yes |
21
21
  | Console & error log capture | [`browser.logs.*`](#console--error-logs) | yes |
22
- | `waitForLoadState('load' \| 'domcontentloaded' \| 'networkidle')` | `browser.waitForLoadState()` | yes |
23
- | `navigateTo(..., { waitUntil })` real load events | `browser.navigateTo()` | yes |
22
+ | `waitForLoadState('load' \| 'domcontentloaded' \| 'networkidle')` | `browser.waitForLoadState()` | no — event-driven over BiDi, polls `document.readyState` in Classic |
23
+ | `navigateTo(..., { waitUntil })` real load events | `browser.navigateTo()` | no — event-driven over BiDi, best-effort settle timer in Classic |
24
24
  | `waitForRequest()` / `waitForResponse()` | `browser.waitForRequest()` / `waitForResponse()` | yes |
25
25
  | Init scripts (run before any page script) | `browser.addInitScript()` | yes |
26
26
  | Open new tab / popup | `browser.openPage()` | yes |
@@ -229,6 +229,14 @@ waitForResponse("**/api/users") timed out after 30000ms
229
229
 
230
230
  Access browser console output and JavaScript errors via `browser.logs`.
231
231
 
232
+ > **Capture is lazy by default.** Craftdriver only subscribes to log events
233
+ > the first time you touch `browser.logs.onLog()` / `.onConsole()` /
234
+ > `.onError()` / `.on()` / `.waitForConsole()` / `.waitForError()` — messages
235
+ > emitted before that first touch are not captured, so a bare
236
+ > `browser.logs.getMessages()` right after `navigateTo()` can return `[]`.
237
+ > Either arm a listener before the action that logs, or launch with
238
+ > `Browser.launch({ captureLogs: true })` to start capture immediately.
239
+
232
240
  ### getMessages()
233
241
 
234
242
  Get all console messages.
@@ -28,7 +28,8 @@ const browser = await Browser.launch({
28
28
  | Option | Type | Default | Description |
29
29
  |--------|------|---------|-------------|
30
30
  | `browserName` | `'chrome' \| 'chromium' \| 'firefox'` | `'chrome'` | Browser to launch |
31
- | `enableBiDi` | `boolean` | `true` | Use WebDriver BiDi. Set `false` only when running against a browser that does not support it. |
31
+ | `enableBiDi` | `boolean` | `true` | Use WebDriver BiDi. Set `false` only when running against a browser that does not support it — every BiDi-only feature then throws a `requires BiDi` error if called. |
32
+ | `captureLogs` | `boolean` | `false` | Start console/error log capture immediately at launch instead of lazily on first `browser.logs`/`onConsole`/`onError`/`waitForConsole` touch. See [BiDi Features — Console & Error Logs](./bidi-features.md#console--error-logs). |
32
33
  | `storageState` | `string` | — | Path to a session-state JSON file to load on startup |
33
34
  | `mobileEmulation` | `MobileEmulation \| DeviceName` | — | Mobile device emulation settings (Chrome/Chromium only) |
34
35
  | `downloadsDir` | `string` | temp dir | Directory where downloaded files are saved |
@@ -333,13 +333,12 @@ Requests from other contexts are not seen.
333
333
 
334
334
  - `pattern`: a URL substring, a `RegExp`, or a glob (`*` matches any
335
335
  run of non-`/` characters; `**` matches any run of characters).
336
- - `handler({ request, fulfill, continue: cont, abort })`: called for
337
- every matched request. Must call exactly one of:
338
- - `fulfill({ status?, headers?, contentType?, body? })` — reply
339
- immediately without hitting the network.
340
- - `continue({ url?, method?, headers?, postData? })` let the
341
- request go through, optionally rewritten.
342
- - `abort(reason?)` — fail the request.
336
+ - `handler(request)`: called for every matched request, receiving an
337
+ `InterceptedRequest` (`url`, `method`, `headers`, `postData`, …).
338
+ Return a `MockResponse` (`{ status?, statusText?, headers?, body? }`)
339
+ to fulfill the request immediately without hitting the network, or
340
+ return/resolve `void` to let it continue to the network unmodified.
341
+ The handler may be async.
343
342
 
344
343
  ```typescript
345
344
  // Real-world: serve a fixture to one tenant, let the real API answer
@@ -347,13 +346,11 @@ Requests from other contexts are not seen.
347
346
  const admin = await browser.newContext();
348
347
  const guest = await browser.newContext();
349
348
 
350
- await admin.route('**/api/users', async ({ fulfill }) => {
351
- await fulfill({
352
- status: 200,
353
- contentType: 'application/json',
354
- body: JSON.stringify([{ id: 1, role: 'admin', name: 'Alice' }]),
355
- });
356
- });
349
+ await admin.route('**/api/users', (request) => ({
350
+ status: 200,
351
+ headers: { 'content-type': 'application/json' },
352
+ body: [{ id: 1, role: 'admin', name: 'Alice' }],
353
+ }));
357
354
 
358
355
  const aPage = await admin.newPage({ url: '/users' });
359
356
  const gPage = await guest.newPage({ url: '/users' });
package/docs/dialogs.md CHANGED
@@ -11,6 +11,13 @@ or the automation handles them.
11
11
  > failures so unexpected dialogs surface as broken tests rather than
12
12
  > passing tests against a stuck browser.
13
13
 
14
+ > **`waitForDialog()` and `onDialog()` require BiDi.** Both are built on
15
+ > BiDi's dialog-open event; under Classic WebDriver (`enableBiDi: false`)
16
+ > `onDialog()` silently registers a no-op and `waitForDialog()` will simply
17
+ > time out rather than error, since Classic has no push notification for
18
+ > "a dialog opened." Use the imperative `acceptDialog()` / `dismissDialog()`
19
+ > / `getDialogMessage()` API instead if you're running Classic-only.
20
+
14
21
  You handle a dialog one of three ways, depending on the shape of the
15
22
  test:
16
23
 
@@ -89,9 +96,13 @@ await browser.dismissDialog(); // click Cancel / close
89
96
  const text = await browser.getDialogMessage();
90
97
  ```
91
98
 
92
- These map directly onto W3C Classic alert endpoints
93
- (`/alert/accept`, `/alert/dismiss`, `/alert/text`) and throw if no
94
- dialog is currently open.
99
+ `acceptDialog` / `dismissDialog` use BiDi's `handleUserPrompt` when the
100
+ browser was launched with BiDi (the default), falling back to the W3C
101
+ Classic alert endpoints (`/alert/accept`, `/alert/dismiss`) only when
102
+ `enableBiDi: false`. `getDialogMessage` always uses the Classic
103
+ `/alert/text` endpoint — BiDi only surfaces the dialog message via the
104
+ `userPromptOpened` event, not a query, so there's nothing to query outside
105
+ `onDialog`/`waitForDialog`. All four throw if no dialog is currently open.
95
106
 
96
107
  ## `Dialog` interface
97
108
 
@@ -7,13 +7,25 @@ Craftdriver resolves the WebDriver binary through a chain — first match wins:
7
7
  | 1 | `driverPath` option in `Browser.launch()` |
8
8
  | 2 | `CRAFTDRIVER_CHROMEDRIVER_PATH` / `CRAFTDRIVER_GECKODRIVER_PATH` env var |
9
9
  | 3 | `CRAFTDRIVER_DRIVER_PATH` env var (generic fallback for either browser) |
10
- | 4 | `chromedriver` / `geckodriver` in `node_modules/.bin` |
11
- | 5 | `chromedriver` / `geckodriver` on `PATH` |
12
- | 6 | **Auto-download from Chrome for Testing / GitHub** the zero-config default |
10
+ | 4 | Legacy/Selenium-compatible env vars: `CHROMEDRIVER_PATH`, `SE_CHROMEDRIVER` (chromedriver) or `GECKODRIVER_PATH`, `GECKODRIVER_FILEPATH`, `SE_GECKODRIVER` (geckodriver) |
11
+ | 5 | **Cached auto-resolution** the path a previous auto-resolve settled on, reused within the `CRAFTDRIVER_DRIVER_TTL` window |
12
+ | 6 | `chromedriver` / `geckodriver` in `node_modules/.bin` |
13
+ | 7 | `chromedriver` / `geckodriver` on `PATH` |
14
+ | 8 | **Auto-download from Chrome for Testing / GitHub** ← the zero-config default |
13
15
 
14
- Downloaded drivers are cached in `~/.cache/craftdriver` and reused as long as
15
- your browser version doesn't change. Only the driver binary is downloaded,
16
- never the browser itself.
16
+ Downloaded drivers are cached in `~/.cache/craftdriver`, and so is the
17
+ *resolution itself* which driver path to use. Within the
18
+ `CRAFTDRIVER_DRIVER_TTL` window (default 24 h), craftdriver reuses the
19
+ resolved path directly and skips the system-browser probes it would
20
+ otherwise run on **every** launch: launching the browser binary just to read
21
+ its version string, and a `PATH` lookup. Both are blocking calls, so caching
22
+ the resolution measurably speeds up launch — most noticeably when several
23
+ browsers start in parallel (see [Performance](#performance)). After the TTL
24
+ expires, or if Chrome reports that the cached chromedriver is for the wrong
25
+ major version after a browser upgrade, the driver is re-resolved and
26
+ re-downloaded if needed. Only the driver binary is ever downloaded, never the
27
+ browser itself. Explicit configuration (steps 1–4) always takes precedence over
28
+ the cache.
17
29
 
18
30
  ## Environment variables
19
31
 
@@ -24,7 +36,7 @@ never the browser itself.
24
36
  | `CRAFTDRIVER_DRIVER_PATH` | Generic fallback path (either browser) | — |
25
37
  | `CRAFTDRIVER_CACHE_DIR` | Directory for cached driver downloads | `~/.cache/craftdriver` |
26
38
  | `CRAFTDRIVER_OFFLINE` | Set to `1` to disable all network calls | — |
27
- | `CRAFTDRIVER_DRIVER_TTL` | Geckodriver freshness check interval (seconds) | `86400` (24 h) |
39
+ | `CRAFTDRIVER_DRIVER_TTL` | Driver-resolution cache lifetime, in seconds (both browsers). `0` disables the cache | `86400` (24 h) |
28
40
 
29
41
  ## Examples
30
42
 
@@ -35,7 +47,7 @@ CRAFTDRIVER_CHROMEDRIVER_PATH=/usr/bin/chromedriver npm test
35
47
  # Pin a specific geckodriver
36
48
  CRAFTDRIVER_GECKODRIVER_PATH=/usr/local/bin/geckodriver npm test
37
49
 
38
- # Never make a network call (requires a local driver to exist in steps 1–5)
50
+ # Never make a network call (requires a local driver to exist in steps 1–7)
39
51
  CRAFTDRIVER_OFFLINE=1 npm test
40
52
 
41
53
  # Change the cache location
@@ -45,6 +57,112 @@ CRAFTDRIVER_CACHE_DIR=/tmp/my-driver-cache npm test
45
57
  CRAFTDRIVER_DRIVER_TTL=3600 npm test
46
58
  ```
47
59
 
60
+ ## Performance
61
+
62
+ Most of the time `Browser.launch()` spends is the browser process starting up
63
+ (that's the same for WebDriver Classic and BiDi and there's little a client
64
+ library can do about it). The part craftdriver *does* control is resolving and
65
+ starting the driver, and it's tuned to stay out of the way:
66
+
67
+ - **Driver resolution is cached** (see above). Without a cache, resolving a
68
+ chromedriver means launching your Chrome binary just to read its version
69
+ string — a blocking call of a few hundred milliseconds on *every* launch.
70
+ The TTL cache skips that after the first launch.
71
+ - **Point at a driver explicitly to skip resolution entirely.** If you set
72
+ `CRAFTDRIVER_CHROMEDRIVER_PATH` / `CRAFTDRIVER_GECKODRIVER_PATH` (or pass
73
+ `chromeService: new ChromeService({ binaryPath })`), craftdriver uses it
74
+ directly — no version detection, no `PATH` lookup, no cache read. This is
75
+ the fastest and most deterministic option and is recommended for CI:
76
+
77
+ ```bash
78
+ CRAFTDRIVER_CHROMEDRIVER_PATH=/opt/chromedriver/chromedriver npm test
79
+ ```
80
+
81
+ - **Parallel runs benefit the most.** The resolution work that the cache (or an
82
+ explicit path) removes was synchronous and blocked the event loop, so it
83
+ serialized when several browsers were launched at once. Removing it lets
84
+ concurrent `Browser.launch()` calls overlap their startup (measured ~17–20%
85
+ faster wall time for a batch of concurrent launches).
86
+
87
+ ### Concurrency and oversubscription
88
+
89
+ Once resolution is out of the way, the rest of launch time is the browser
90
+ process starting — and that is **CPU-bound**. Starting many browsers at once
91
+ on a machine with fewer CPU cores oversubscribes the CPU, and each browser's
92
+ startup slows down roughly in proportion. On an 8-core machine, for example, a
93
+ single launch is ~2s but 20 simultaneous launches take ~25s *each* (they still
94
+ finish sooner in aggregate than launching serially, just with diminishing
95
+ returns). This is not a craftdriver limitation — it's the browser competing for
96
+ CPU — and no client-side change removes it.
97
+
98
+ Practical guidance for parallel test suites (e.g. Vitest / Jest / Playwright
99
+ Test): **cap worker concurrency at roughly the number of CPU cores.** More
100
+ workers than cores mostly adds launch latency, and a launch that is merely slow
101
+ under heavy load can trip a short per-test/hook timeout and look like a hang.
102
+ With Vitest, set `maxWorkers` (or `poolOptions`) accordingly; a generous
103
+ `hookTimeout` for the `beforeAll` that launches the browser also helps on
104
+ loaded CI runners.
105
+
106
+ Indicative numbers from the `tests/perf/launch-critical-path.perf.ts`
107
+ benchmark (macOS, Chrome, headless — absolute values are machine-dependent,
108
+ but the direction holds):
109
+
110
+ | Scenario | Before | After |
111
+ |---|---|---|
112
+ | `Browser.launch()` (BiDi) | ~2760ms | ~2240ms |
113
+ | `Browser.launch()` (Classic) | ~2400ms | ~1860ms |
114
+ | 4 browsers launched concurrently | ~6450ms | ~5130ms |
115
+
116
+ Run it yourself with `npm run bench -- launch-critical-path`.
117
+
118
+ ### Browser startup flags (advanced, opt-in)
119
+
120
+ craftdriver launches the browser with **no performance flags of its own** — it
121
+ stays unopinionated so it never silently changes browser behavior underneath
122
+ you. If you want to experiment, you can pass extra browser command-line flags
123
+ via the `args` launch option (appended to `goog:chromeOptions.args` for
124
+ Chrome/Chromium, `moz:firefoxOptions.args` for Firefox):
125
+
126
+ ```typescript
127
+ const browser = await Browser.launch({
128
+ browserName: 'chrome',
129
+ args: [
130
+ '--no-first-run',
131
+ '--no-default-browser-check',
132
+ '--disable-background-networking',
133
+ '--disable-component-update',
134
+ '--disable-default-apps',
135
+ '--disable-extensions',
136
+ '--disable-sync',
137
+ '--metrics-recording-only',
138
+ '--disable-background-timer-throttling',
139
+ '--disable-backgrounding-occluded-windows',
140
+ '--disable-renderer-backgrounding',
141
+ '--mute-audio',
142
+ '--no-service-autorun',
143
+ '--password-store=basic',
144
+ '--use-mock-keychain', // macOS: skip keychain access
145
+ '--disable-features=Translate,BackForwardCache,AcceptCHFrame,MediaRouter,OptimizationHints',
146
+ ],
147
+ });
148
+ ```
149
+
150
+ > `args` are **browser** flags. They are distinct from the **driver**
151
+ > (chromedriver/geckodriver) args you'd pass via `chromeService` /
152
+ > `firefoxService` below.
153
+
154
+ **Set expectations honestly:** on a normal local machine this set moved
155
+ `Browser.launch()` wall time by **~0.4% (≈8ms — noise)** in our measurements.
156
+ Cold browser startup is dominated by unavoidable process/engine init; these
157
+ flags mostly suppress *background* work (auto-updates, telemetry, background
158
+ networking, sync) that happens after startup rather than on the launch critical
159
+ path. So their real value is **determinism and avoiding intermittent stalls in
160
+ CI / constrained environments** (no update popups, no background network
161
+ calls), not raw local launch speed. If you adopt them, **measure on your own
162
+ environment** — the payoff is environment-dependent, and some flags can change
163
+ behavior (e.g. `--no-sandbox`, or `--disable-features=...` entries a page or
164
+ fixture relies on).
165
+
48
166
  ## Pinning via code
49
167
 
50
168
  For tighter control (custom port, extra driver flags), pass a `ChromeService`
package/docs/emulation.md CHANGED
@@ -25,12 +25,16 @@ await browser.emulate({
25
25
 
26
26
  | Option | What the page sees | Transport | Cross-browser |
27
27
  |---|---|---|---|
28
- | `colorScheme` | `prefers-color-scheme` media query | BiDi + CDP `Emulation.setEmulatedMedia` | Chromium only |
29
- | `reducedMotion` | `prefers-reduced-motion` media query | BiDi + CDP `Emulation.setEmulatedMedia` | Chromium only |
30
- | `forcedColors` | `forced-colors` media query | BiDi + CDP `Emulation.setEmulatedMedia` | Chromium only |
28
+ | `colorScheme` | `prefers-color-scheme` media query | CDP `Emulation.setEmulatedMedia` (over the BiDi+CDP bridge) | Chromium only |
29
+ | `reducedMotion` | `prefers-reduced-motion` media query | CDP `Emulation.setEmulatedMedia` (over the BiDi+CDP bridge) | Chromium only |
30
+ | `forcedColors` | `forced-colors` media query | CDP `Emulation.setEmulatedMedia` (over the BiDi+CDP bridge) | Chromium only |
31
31
  | `locale` | `navigator.language`, `Intl.*` formatting | BiDi `emulation.setLocaleOverride` | yes |
32
32
  | `timezoneId` | `Intl.DateTimeFormat`, `Date` offsets | BiDi `emulation.setTimezoneOverride` | yes |
33
- | `offline` | `navigator.onLine`, network stack | BiDi + CDP `Network.emulateNetworkConditions` | Chromium only |
33
+ | `offline` | `navigator.onLine`, network stack | CDP `Network.emulateNetworkConditions` (over the BiDi+CDP bridge) | Chromium only |
34
+
35
+ All options still require an active BiDi session (`enableBiDi: true`, the
36
+ default) — the four CDP-backed options ride over a CDP bridge that BiDi
37
+ opens, they don't work with BiDi disabled.
34
38
 
35
39
  - Pass `null` for any field to clear that override.
36
40
  - Calling `emulate({})` is a no-op.
@@ -1,6 +1,6 @@
1
1
  # Error codes
2
2
 
3
- Every error thrown from the public craftdriver API is a
3
+ Nearly every error thrown from the public craftdriver API is a
4
4
  [`CraftdriverError`](../src/lib/errors.ts), carrying:
5
5
 
6
6
  - `code` — a stable, machine-readable identifier from the table below.
@@ -12,6 +12,11 @@ Every error thrown from the public craftdriver API is a
12
12
  `CraftdriverError extends Error`, so `instanceof Error` keeps working
13
13
  and stack traces are preserved.
14
14
 
15
+ > A few edge cases still throw a plain `Error` instead — e.g. calling
16
+ > `startTrace()` again while a trace is already running, or without
17
+ > `outDir`. Guard against these with normal usage (don't call `startTrace()`
18
+ > twice without `stopTrace()`) rather than a `code` check.
19
+
15
20
  ```ts
16
21
  import { CraftdriverError, ErrorCode } from 'craftdriver';
17
22
 
@@ -38,11 +43,11 @@ try {
38
43
  | `TIMEOUT` | Generic `WebDriverWait.until(...)` timeout with no more specific code. | Same as above — check the condition. |
39
44
  | `EXPECT_MISMATCH` | An `expect(locator).to…()` assertion failed after auto-waiting. | Inspect `error.detail` for the selector and observed value. |
40
45
  | `A11Y_VIOLATIONS` | `browser.a11y.check()` (or scoped variants) found axe-core violations. | Iterate over `error.violations` — each has an `id`, `impact`, and `helpUrl`. |
41
- | `EVAL_THREW` | The function passed to `evaluate()` threw inside the page. | The page-side exception text is in `error.detail.exception`. |
46
+ | `EVAL_THREW` | The function passed to `evaluate()` threw inside the page. Also fires for `browser.clock` methods (`tick()`, `setSystemTime()`, `runFor()`) called before `install()` — they run as in-page scripts under the hood. | The page-side exception text is in `error.detail.exception`. |
42
47
  | `EVAL_BAD_ARG` | `evaluate()` / `addInitScript()` received a non-JSON-serializable argument (function, Symbol, DOM node…). | Pass plain JSON values. |
43
48
  | `INVALID_ARGUMENT` | Caller passed an invalid value (bad enum, wrong shape, unparseable duration…). | Read the message; it lists the accepted forms. |
44
49
  | `UNSUPPORTED` | Feature exists but is unavailable on this browser/transport (e.g. Chromium-only over Firefox, or a BiDi-only feature with BiDi disabled). | Enable BiDi (`enableBiDi: true`) or switch browser. |
45
- | `STATE_INVALID` | Method called in the wrong state (e.g. `stopTrace()` without `startTrace()`, `clock.tick()` before `install()`). | Call the prerequisite first. |
50
+ | `STATE_INVALID` | Method called in the wrong state (e.g. `stopTrace()` without `startTrace()`). | Call the prerequisite first. |
46
51
  | `DRIVER_ERROR` | Driver-level / transport-level failure surfaced to the caller. | Inspect `error.cause`. |
47
52
 
48
53
  ## Stability