craftdriver 0.0.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +104 -0
- package/README.md +186 -10
- package/bin/craftdriver.mjs +26 -0
- package/dist/cli/client.d.ts +17 -0
- package/dist/cli/client.d.ts.map +1 -0
- package/dist/cli/client.js +88 -0
- package/dist/cli/client.js.map +1 -0
- package/dist/cli/daemon.d.ts +15 -0
- package/dist/cli/daemon.d.ts.map +1 -0
- package/dist/cli/daemon.js +145 -0
- package/dist/cli/daemon.js.map +1 -0
- package/dist/cli/defaults.d.ts +10 -0
- package/dist/cli/defaults.d.ts.map +1 -0
- package/dist/cli/defaults.js +42 -0
- package/dist/cli/defaults.js.map +1 -0
- package/dist/cli/dispatcher.d.ts +28 -0
- package/dist/cli/dispatcher.d.ts.map +1 -0
- package/dist/cli/dispatcher.js +319 -0
- package/dist/cli/dispatcher.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +404 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +24 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +192 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/mcp/artifacts.d.ts +43 -0
- package/dist/cli/mcp/artifacts.d.ts.map +1 -0
- package/dist/cli/mcp/artifacts.js +104 -0
- package/dist/cli/mcp/artifacts.js.map +1 -0
- package/dist/cli/mcp/server.d.ts +34 -0
- package/dist/cli/mcp/server.d.ts.map +1 -0
- package/dist/cli/mcp/server.js +214 -0
- package/dist/cli/mcp/server.js.map +1 -0
- package/dist/cli/mcp/tools.d.ts +34 -0
- package/dist/cli/mcp/tools.d.ts.map +1 -0
- package/dist/cli/mcp/tools.js +236 -0
- package/dist/cli/mcp/tools.js.map +1 -0
- package/dist/cli/parseArgs.d.ts +37 -0
- package/dist/cli/parseArgs.d.ts.map +1 -0
- package/dist/cli/parseArgs.js +215 -0
- package/dist/cli/parseArgs.js.map +1 -0
- package/dist/cli/protocol.d.ts +34 -0
- package/dist/cli/protocol.d.ts.map +1 -0
- package/dist/cli/protocol.js +2 -0
- package/dist/cli/protocol.js.map +1 -0
- package/dist/cli/selector.d.ts +29 -0
- package/dist/cli/selector.d.ts.map +1 -0
- package/dist/cli/selector.js +100 -0
- package/dist/cli/selector.js.map +1 -0
- package/dist/cli/snapshot.d.ts +48 -0
- package/dist/cli/snapshot.d.ts.map +1 -0
- package/dist/cli/snapshot.js +179 -0
- package/dist/cli/snapshot.js.map +1 -0
- package/dist/index.d.ts +14 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/a11y.d.ts +81 -0
- package/dist/lib/a11y.d.ts.map +1 -0
- package/dist/lib/a11y.js +171 -0
- package/dist/lib/a11y.js.map +1 -0
- package/dist/lib/bidi/connection.d.ts +62 -0
- package/dist/lib/bidi/connection.d.ts.map +1 -0
- package/dist/lib/bidi/connection.js +227 -0
- package/dist/lib/bidi/connection.js.map +1 -0
- package/dist/lib/bidi/index.d.ts +101 -0
- package/dist/lib/bidi/index.d.ts.map +1 -0
- package/dist/lib/bidi/index.js +163 -0
- package/dist/lib/bidi/index.js.map +1 -0
- package/dist/lib/bidi/logs.d.ts +104 -0
- package/dist/lib/bidi/logs.d.ts.map +1 -0
- package/dist/lib/bidi/logs.js +273 -0
- package/dist/lib/bidi/logs.js.map +1 -0
- package/dist/lib/bidi/network.d.ts +165 -0
- package/dist/lib/bidi/network.d.ts.map +1 -0
- package/dist/lib/bidi/network.js +477 -0
- package/dist/lib/bidi/network.js.map +1 -0
- package/dist/lib/bidi/storage.d.ts +83 -0
- package/dist/lib/bidi/storage.d.ts.map +1 -0
- package/dist/lib/bidi/storage.js +299 -0
- package/dist/lib/bidi/storage.js.map +1 -0
- package/dist/lib/bidi/types.d.ts +323 -0
- package/dist/lib/bidi/types.d.ts.map +1 -0
- package/dist/lib/bidi/types.js +6 -0
- package/dist/lib/bidi/types.js.map +1 -0
- package/dist/lib/browser.d.ts +776 -13
- package/dist/lib/browser.d.ts.map +1 -1
- package/dist/lib/browser.js +1547 -70
- package/dist/lib/browser.js.map +1 -1
- package/dist/lib/browserContext.d.ts +486 -0
- package/dist/lib/browserContext.d.ts.map +1 -0
- package/dist/lib/browserContext.js +937 -0
- package/dist/lib/browserContext.js.map +1 -0
- package/dist/lib/builder.d.ts +3 -0
- package/dist/lib/builder.d.ts.map +1 -1
- package/dist/lib/builder.js +31 -4
- package/dist/lib/builder.js.map +1 -1
- package/dist/lib/by.d.ts +18 -2
- package/dist/lib/by.d.ts.map +1 -1
- package/dist/lib/by.js +20 -2
- package/dist/lib/by.js.map +1 -1
- package/dist/lib/chrome.d.ts +13 -1
- package/dist/lib/chrome.d.ts.map +1 -1
- package/dist/lib/chrome.js +13 -17
- package/dist/lib/chrome.js.map +1 -1
- package/dist/lib/clock.d.ts +115 -0
- package/dist/lib/clock.d.ts.map +1 -0
- package/dist/lib/clock.js +407 -0
- package/dist/lib/clock.js.map +1 -0
- package/dist/lib/driver.d.ts +45 -0
- package/dist/lib/driver.d.ts.map +1 -1
- package/dist/lib/driver.js +129 -1
- package/dist/lib/driver.js.map +1 -1
- package/dist/lib/driverManager.d.ts +25 -0
- package/dist/lib/driverManager.d.ts.map +1 -0
- package/dist/lib/driverManager.js +399 -0
- package/dist/lib/driverManager.js.map +1 -0
- package/dist/lib/elementHandle.d.ts +69 -2
- package/dist/lib/elementHandle.d.ts.map +1 -1
- package/dist/lib/elementHandle.js +215 -40
- package/dist/lib/elementHandle.js.map +1 -1
- package/dist/lib/errors.d.ts +71 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +73 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/expect.d.ts +6 -1
- package/dist/lib/expect.d.ts.map +1 -1
- package/dist/lib/expect.js +55 -37
- package/dist/lib/expect.js.map +1 -1
- package/dist/lib/firefox.d.ts +18 -0
- package/dist/lib/firefox.d.ts.map +1 -0
- package/dist/lib/firefox.js +33 -0
- package/dist/lib/firefox.js.map +1 -0
- package/dist/lib/frame.d.ts +57 -0
- package/dist/lib/frame.d.ts.map +1 -0
- package/dist/lib/frame.js +277 -0
- package/dist/lib/frame.js.map +1 -0
- package/dist/lib/keyboard.d.ts +1 -1
- package/dist/lib/keyboard.d.ts.map +1 -1
- package/dist/lib/keyboard.js +1 -1
- package/dist/lib/keyboard.js.map +1 -1
- package/dist/lib/locator.d.ts +71 -2
- package/dist/lib/locator.d.ts.map +1 -1
- package/dist/lib/locator.js +289 -31
- package/dist/lib/locator.js.map +1 -1
- package/dist/lib/mouse.d.ts +1 -1
- package/dist/lib/mouse.d.ts.map +1 -1
- package/dist/lib/mouse.js +1 -1
- package/dist/lib/mouse.js.map +1 -1
- package/dist/lib/page.d.ts +107 -0
- package/dist/lib/page.d.ts.map +1 -0
- package/dist/lib/page.js +388 -0
- package/dist/lib/page.js.map +1 -0
- package/dist/lib/tracing.d.ts +128 -0
- package/dist/lib/tracing.d.ts.map +1 -0
- package/dist/lib/tracing.js +272 -0
- package/dist/lib/tracing.js.map +1 -0
- package/dist/lib/wait.d.ts.map +1 -1
- package/dist/lib/wait.js +14 -1
- package/dist/lib/wait.js.map +1 -1
- package/dist/lib/webelement.d.ts +2 -0
- package/dist/lib/webelement.d.ts.map +1 -1
- package/dist/lib/webelement.js +18 -0
- package/dist/lib/webelement.js.map +1 -1
- package/docs/accessibility.md +177 -0
- package/docs/api-reference.md +71 -0
- package/docs/assertions.md +182 -0
- package/docs/bidi-features.md +462 -0
- package/docs/browser-api.md +673 -0
- package/docs/browser-context.md +522 -0
- package/docs/cli.md +233 -0
- package/docs/clock.md +245 -0
- package/docs/dialogs.md +143 -0
- package/docs/driver-configuration.md +64 -0
- package/docs/element-api.md +219 -0
- package/docs/emulation.md +155 -0
- package/docs/error-codes.md +67 -0
- package/docs/getting-started.md +168 -0
- package/docs/keyboard-mouse.md +250 -0
- package/docs/mcp.md +250 -0
- package/docs/mobile-emulation.md +194 -0
- package/docs/screenshots.md +146 -0
- package/docs/selectors.md +382 -0
- package/docs/session-management.md +229 -0
- package/docs/tracing.md +282 -0
- package/package.json +25 -5
- package/skills/craftdriver/SKILL.md +84 -0
- package/skills/craftdriver/cheatsheet.md +194 -0
- package/skills/craftdriver/cli.md +104 -0
- package/skills/craftdriver/patterns.md +139 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Virtual clock control — freeze, advance, or override the wall clock the
|
|
3
|
+
* page sees: Date.now, new Date(), performance.now, setTimeout, setInterval,
|
|
4
|
+
* requestAnimationFrame.
|
|
5
|
+
*/
|
|
6
|
+
import { CraftdriverError, ErrorCode } from './errors.js';
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Fake-timer shim — injected into the page via addInitScript
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
//
|
|
11
|
+
// The shim:
|
|
12
|
+
// • Captures real globals once (at controller-creation time).
|
|
13
|
+
// • Exposes window.__craftdriverClock__ with install / uninstall / tick /
|
|
14
|
+
// setFixedTime / setSystemTime / runFor.
|
|
15
|
+
// • The controller-creation block is guarded by an existence check so that
|
|
16
|
+
// re-evaluating the script on the same document doesn't re-capture
|
|
17
|
+
// already-faked globals.
|
|
18
|
+
// • Uses only ES5-compatible syntax so it works in every modern browser
|
|
19
|
+
// without transpilation.
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
const SHIM_SETUP = `(function() {
|
|
22
|
+
if (window.__craftdriverClock__) return;
|
|
23
|
+
|
|
24
|
+
var originals = {
|
|
25
|
+
Date: window.Date,
|
|
26
|
+
setTimeout: window.setTimeout,
|
|
27
|
+
clearTimeout: window.clearTimeout,
|
|
28
|
+
setInterval: window.setInterval,
|
|
29
|
+
clearInterval: window.clearInterval,
|
|
30
|
+
performanceNow: (window.performance && typeof window.performance.now === 'function')
|
|
31
|
+
? window.performance.now.bind(window.performance)
|
|
32
|
+
: null,
|
|
33
|
+
requestAnimationFrame: typeof window.requestAnimationFrame === 'function'
|
|
34
|
+
? window.requestAnimationFrame.bind(window)
|
|
35
|
+
: null,
|
|
36
|
+
cancelAnimationFrame: typeof window.cancelAnimationFrame === 'function'
|
|
37
|
+
? window.cancelAnimationFrame.bind(window)
|
|
38
|
+
: null,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
var state = {
|
|
42
|
+
installed: false,
|
|
43
|
+
fixedMode: false,
|
|
44
|
+
fixedTime: 0,
|
|
45
|
+
virtualNow: 0,
|
|
46
|
+
performanceOrigin: 0,
|
|
47
|
+
timers: [],
|
|
48
|
+
nextId: 1,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function fakeNow() {
|
|
52
|
+
if (state.fixedMode) return state.fixedTime;
|
|
53
|
+
return state.virtualNow;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function patchDate() {
|
|
57
|
+
var OrigDate = originals.Date;
|
|
58
|
+
function FakeDate() {
|
|
59
|
+
var t;
|
|
60
|
+
if (arguments.length === 0) {
|
|
61
|
+
t = new OrigDate(fakeNow());
|
|
62
|
+
} else if (arguments.length === 1) {
|
|
63
|
+
t = new OrigDate(arguments[0]);
|
|
64
|
+
} else if (arguments.length === 2) {
|
|
65
|
+
t = new OrigDate(arguments[0], arguments[1]);
|
|
66
|
+
} else if (arguments.length === 3) {
|
|
67
|
+
t = new OrigDate(arguments[0], arguments[1], arguments[2]);
|
|
68
|
+
} else if (arguments.length === 4) {
|
|
69
|
+
t = new OrigDate(arguments[0], arguments[1], arguments[2], arguments[3]);
|
|
70
|
+
} else if (arguments.length === 5) {
|
|
71
|
+
t = new OrigDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
|
|
72
|
+
} else if (arguments.length === 6) {
|
|
73
|
+
t = new OrigDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
|
|
74
|
+
} else {
|
|
75
|
+
t = new OrigDate(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]);
|
|
76
|
+
}
|
|
77
|
+
if (!(this instanceof FakeDate)) return t.toString();
|
|
78
|
+
return t;
|
|
79
|
+
}
|
|
80
|
+
FakeDate.now = function() { return fakeNow(); };
|
|
81
|
+
FakeDate.parse = OrigDate.parse.bind(OrigDate);
|
|
82
|
+
FakeDate.UTC = OrigDate.UTC.bind(OrigDate);
|
|
83
|
+
Object.setPrototypeOf(FakeDate, OrigDate);
|
|
84
|
+
FakeDate.prototype = OrigDate.prototype;
|
|
85
|
+
window.Date = FakeDate;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function runTimers(targetTime) {
|
|
89
|
+
while (true) {
|
|
90
|
+
var earliest = null;
|
|
91
|
+
for (var i = 0; i < state.timers.length; i++) {
|
|
92
|
+
var t = state.timers[i];
|
|
93
|
+
if (!t.cancelled && t.deadline <= targetTime) {
|
|
94
|
+
if (!earliest || t.deadline < earliest.deadline ||
|
|
95
|
+
(t.deadline === earliest.deadline && t.id < earliest.id)) {
|
|
96
|
+
earliest = t;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!earliest) break;
|
|
101
|
+
|
|
102
|
+
state.virtualNow = earliest.deadline;
|
|
103
|
+
|
|
104
|
+
if (earliest.interval !== null) {
|
|
105
|
+
earliest.deadline += earliest.interval;
|
|
106
|
+
} else {
|
|
107
|
+
var idx = state.timers.indexOf(earliest);
|
|
108
|
+
if (idx !== -1) state.timers.splice(idx, 1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
if (typeof earliest.fn === 'function') {
|
|
113
|
+
earliest.fn.apply(null, earliest.args || []);
|
|
114
|
+
} else if (typeof earliest.fn === 'string') {
|
|
115
|
+
// setTimeout(codeString, ...) form
|
|
116
|
+
// eslint-disable-next-line no-new-func
|
|
117
|
+
new Function(earliest.fn)();
|
|
118
|
+
}
|
|
119
|
+
} catch (_e) { /* swallow timer errors */ }
|
|
120
|
+
}
|
|
121
|
+
state.virtualNow = targetTime;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
var controller = {
|
|
125
|
+
install: function(opts) {
|
|
126
|
+
opts = opts || {};
|
|
127
|
+
var startTime = (opts.time != null) ? opts.time : originals.Date.now();
|
|
128
|
+
state.installed = true;
|
|
129
|
+
state.fixedMode = false;
|
|
130
|
+
state.virtualNow = startTime;
|
|
131
|
+
state.performanceOrigin = startTime;
|
|
132
|
+
state.timers = [];
|
|
133
|
+
state.nextId = 1;
|
|
134
|
+
|
|
135
|
+
patchDate();
|
|
136
|
+
|
|
137
|
+
if (window.performance && typeof window.performance.now === 'function') {
|
|
138
|
+
window.performance.now = function() {
|
|
139
|
+
return state.virtualNow - state.performanceOrigin;
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
window.setTimeout = function(fn, delay) {
|
|
144
|
+
var args = [];
|
|
145
|
+
for (var i = 2; i < arguments.length; i++) args.push(arguments[i]);
|
|
146
|
+
var id = state.nextId++;
|
|
147
|
+
var d = (delay == null || isNaN(+delay) || +delay < 0) ? 0 : +delay;
|
|
148
|
+
state.timers.push({ id: id, deadline: state.virtualNow + d, fn: fn, args: args, interval: null, cancelled: false });
|
|
149
|
+
return id;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
window.clearTimeout = function(id) {
|
|
153
|
+
for (var i = 0; i < state.timers.length; i++) {
|
|
154
|
+
if (state.timers[i].id === id) { state.timers[i].cancelled = true; return; }
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
window.setInterval = function(fn, delay) {
|
|
159
|
+
var args = [];
|
|
160
|
+
for (var i = 2; i < arguments.length; i++) args.push(arguments[i]);
|
|
161
|
+
var id = state.nextId++;
|
|
162
|
+
var d = (delay == null || isNaN(+delay) || +delay < 0) ? 0 : +delay;
|
|
163
|
+
state.timers.push({ id: id, deadline: state.virtualNow + d, fn: fn, args: args, interval: d, cancelled: false });
|
|
164
|
+
return id;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
window.clearInterval = function(id) {
|
|
168
|
+
for (var i = 0; i < state.timers.length; i++) {
|
|
169
|
+
if (state.timers[i].id === id) { state.timers[i].cancelled = true; return; }
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
if (originals.requestAnimationFrame) {
|
|
174
|
+
window.requestAnimationFrame = function(fn) { return window.setTimeout(fn, 16); };
|
|
175
|
+
window.cancelAnimationFrame = function(id) { window.clearTimeout(id); };
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
uninstall: function() {
|
|
180
|
+
if (!state.installed && !state.fixedMode) return;
|
|
181
|
+
window.Date = originals.Date;
|
|
182
|
+
if (window.performance && originals.performanceNow) {
|
|
183
|
+
window.performance.now = originals.performanceNow;
|
|
184
|
+
}
|
|
185
|
+
window.setTimeout = originals.setTimeout;
|
|
186
|
+
window.clearTimeout = originals.clearTimeout;
|
|
187
|
+
window.setInterval = originals.setInterval;
|
|
188
|
+
window.clearInterval = originals.clearInterval;
|
|
189
|
+
if (originals.requestAnimationFrame) {
|
|
190
|
+
window.requestAnimationFrame = originals.requestAnimationFrame;
|
|
191
|
+
window.cancelAnimationFrame = originals.cancelAnimationFrame;
|
|
192
|
+
}
|
|
193
|
+
state.installed = false;
|
|
194
|
+
state.fixedMode = false;
|
|
195
|
+
state.timers = [];
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
tick: function(ms) {
|
|
199
|
+
if (!state.installed) throw new Error('[craftdriver clock] tick() requires install() first');
|
|
200
|
+
runTimers(state.virtualNow + ms);
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
setFixedTime: function(time) {
|
|
204
|
+
state.fixedMode = true;
|
|
205
|
+
state.fixedTime = time;
|
|
206
|
+
patchDate();
|
|
207
|
+
if (window.performance && typeof window.performance.now === 'function') {
|
|
208
|
+
window.performance.now = function() { return 0; };
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
setSystemTime: function(time) {
|
|
213
|
+
if (!state.installed) throw new Error('[craftdriver clock] setSystemTime() requires install() first');
|
|
214
|
+
var delta = time - state.virtualNow;
|
|
215
|
+
state.virtualNow = time;
|
|
216
|
+
// Shift all pending timer deadlines by the same amount so they remain
|
|
217
|
+
// at the same relative distance from the new "now".
|
|
218
|
+
for (var i = 0; i < state.timers.length; i++) {
|
|
219
|
+
if (!state.timers[i].cancelled) {
|
|
220
|
+
state.timers[i].deadline += delta;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
runFor: function(ms) {
|
|
226
|
+
if (!state.installed) throw new Error('[craftdriver clock] runFor() requires install() first');
|
|
227
|
+
var target = state.virtualNow + ms;
|
|
228
|
+
while (state.virtualNow < target) {
|
|
229
|
+
var next = Math.min(state.virtualNow + 16, target);
|
|
230
|
+
runTimers(next);
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
window.__craftdriverClock__ = controller;
|
|
236
|
+
})();`;
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
// Node-side Clock class
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
/** Controls the virtual clock inside the browser page. */
|
|
241
|
+
export class Clock {
|
|
242
|
+
adapter;
|
|
243
|
+
_preloadHandle = null;
|
|
244
|
+
constructor(adapter) {
|
|
245
|
+
this.adapter = adapter;
|
|
246
|
+
}
|
|
247
|
+
// ── Time helpers ──────────────────────────────────────────────────────────
|
|
248
|
+
parseTime(time) {
|
|
249
|
+
if (typeof time === 'number')
|
|
250
|
+
return time;
|
|
251
|
+
if (time instanceof Date)
|
|
252
|
+
return time.getTime();
|
|
253
|
+
const ms = new Date(time).getTime();
|
|
254
|
+
if (isNaN(ms)) {
|
|
255
|
+
throw new CraftdriverError(ErrorCode.INVALID_ARGUMENT, `clock: cannot parse time "${String(time)}". ` +
|
|
256
|
+
'Pass a number (ms since epoch), a Date object, or an ISO date string ' +
|
|
257
|
+
'like "2026-01-01T00:00:00Z".', { detail: { time: String(time) } });
|
|
258
|
+
}
|
|
259
|
+
return ms;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Parse a duration string "MM:SS" or "HH:MM:SS" (numbers can exceed
|
|
263
|
+
* 59, e.g. "90:00" = 90 minutes). A plain number is returned as-is.
|
|
264
|
+
*/
|
|
265
|
+
parseDuration(d) {
|
|
266
|
+
if (typeof d === 'number')
|
|
267
|
+
return d;
|
|
268
|
+
const parts = d.split(':').map(Number);
|
|
269
|
+
if (parts.some(isNaN)) {
|
|
270
|
+
throw new CraftdriverError(ErrorCode.INVALID_ARGUMENT, `clock.fastForward: cannot parse duration "${d}". ` +
|
|
271
|
+
'Pass a number (ms), "MM:SS", or "HH:MM:SS".', { detail: { duration: d } });
|
|
272
|
+
}
|
|
273
|
+
if (parts.length === 2) {
|
|
274
|
+
const [mm, ss] = parts;
|
|
275
|
+
return (mm * 60 + ss) * 1000;
|
|
276
|
+
}
|
|
277
|
+
if (parts.length === 3) {
|
|
278
|
+
const [hh, mm, ss] = parts;
|
|
279
|
+
return (hh * 3600 + mm * 60 + ss) * 1000;
|
|
280
|
+
}
|
|
281
|
+
throw new CraftdriverError(ErrorCode.INVALID_ARGUMENT, `clock.fastForward: cannot parse duration "${d}". ` +
|
|
282
|
+
'Pass a number (ms), "MM:SS", or "HH:MM:SS".', { detail: { duration: d } });
|
|
283
|
+
}
|
|
284
|
+
// ── Preload script management ──────────────────────────────────────────────
|
|
285
|
+
async registerPreload(methodCall) {
|
|
286
|
+
if (this._preloadHandle) {
|
|
287
|
+
await this._preloadHandle.remove();
|
|
288
|
+
this._preloadHandle = null;
|
|
289
|
+
}
|
|
290
|
+
const script = `${SHIM_SETUP}\nwindow.__craftdriverClock__.${methodCall};`;
|
|
291
|
+
this._preloadHandle = await this.adapter.addInitScript(script);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Evaluate the shim + method call in the current document, ignoring errors
|
|
295
|
+
* that occur when no page is loaded yet (about:blank / no context).
|
|
296
|
+
*/
|
|
297
|
+
async applyNow(methodCall) {
|
|
298
|
+
const script = `${SHIM_SETUP}\nwindow.__craftdriverClock__.${methodCall}`;
|
|
299
|
+
try {
|
|
300
|
+
await this.adapter.evaluate(script);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
// No document loaded yet — the preload script will handle it on next navigate.
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// ── Public API ─────────────────────────────────────────────────────────────
|
|
307
|
+
/**
|
|
308
|
+
* Install fake timers in the browser page.
|
|
309
|
+
*
|
|
310
|
+
* Patches `Date`, `performance.now`, `setTimeout`, `setInterval`,
|
|
311
|
+
* `clearTimeout`, `clearInterval`, `requestAnimationFrame`, and
|
|
312
|
+
* `cancelAnimationFrame`. Timers only fire when you call `tick()`,
|
|
313
|
+
* `fastForward()`, or `runFor()`.
|
|
314
|
+
*
|
|
315
|
+
* Also registers a preload script so the fake clock is re-installed
|
|
316
|
+
* automatically on every subsequent navigation.
|
|
317
|
+
*
|
|
318
|
+
* @example
|
|
319
|
+
* await browser.clock.install({ time: '2026-01-01T09:00:00Z' });
|
|
320
|
+
* await browser.navigateTo('/dashboard');
|
|
321
|
+
* await browser.clock.fastForward('15:01');
|
|
322
|
+
*/
|
|
323
|
+
async install(options) {
|
|
324
|
+
const timeMs = options?.time !== undefined ? this.parseTime(options.time) : Date.now();
|
|
325
|
+
const methodCall = `install({time: ${timeMs}})`;
|
|
326
|
+
await this.registerPreload(methodCall);
|
|
327
|
+
await this.applyNow(methodCall);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Uninstall fake timers and restore the real `Date`, `setTimeout`, etc.
|
|
331
|
+
* Also removes the preload script so navigations use the real clock again.
|
|
332
|
+
*/
|
|
333
|
+
async uninstall() {
|
|
334
|
+
if (this._preloadHandle) {
|
|
335
|
+
await this._preloadHandle.remove();
|
|
336
|
+
this._preloadHandle = null;
|
|
337
|
+
}
|
|
338
|
+
await this.applyNow('uninstall()');
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Advance the fake clock by `ms` milliseconds and fire all timers that
|
|
342
|
+
* come due within that window.
|
|
343
|
+
*
|
|
344
|
+
* Requires `install()` to have been called first.
|
|
345
|
+
*/
|
|
346
|
+
async tick(ms) {
|
|
347
|
+
await this.adapter.evaluate(`window.__craftdriverClock__.tick(${ms})`);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Advance the fake clock and fire due timers.
|
|
351
|
+
* Accepts a number of milliseconds **or** a duration string:
|
|
352
|
+
* `"MM:SS"` (e.g. `"15:01"` = 15 min 1 s) or `"HH:MM:SS"`.
|
|
353
|
+
*
|
|
354
|
+
* Requires `install()` to have been called first.
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* await browser.clock.fastForward('15:01'); // advance 15 minutes 1 second
|
|
358
|
+
* await browser.clock.fastForward(60_000); // advance 60 seconds
|
|
359
|
+
*/
|
|
360
|
+
async fastForward(duration) {
|
|
361
|
+
const ms = this.parseDuration(duration);
|
|
362
|
+
await this.tick(ms);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Freeze `Date.now()` / `new Date()` at the given instant.
|
|
366
|
+
*
|
|
367
|
+
* Unlike `install()`, this does **not** fake `setTimeout` or `setInterval`;
|
|
368
|
+
* real timers continue to fire. Use this when you only need to control
|
|
369
|
+
* what "now" is for date-based rendering.
|
|
370
|
+
*
|
|
371
|
+
* Also registers a preload script so the fixed date persists across
|
|
372
|
+
* navigations.
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* await browser.clock.setFixedTime('2026-06-15T23:59:00Z');
|
|
376
|
+
* await browser.navigateTo('/billing');
|
|
377
|
+
* await browser.expect('#trial-banner').toContainText('expires today');
|
|
378
|
+
*/
|
|
379
|
+
async setFixedTime(time) {
|
|
380
|
+
const ms = this.parseTime(time);
|
|
381
|
+
const methodCall = `setFixedTime(${ms})`;
|
|
382
|
+
await this.registerPreload(methodCall);
|
|
383
|
+
await this.applyNow(methodCall);
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Move the fake clock to `time` without advancing the timer queue.
|
|
387
|
+
* Timers that were scheduled continue to fire at their original
|
|
388
|
+
* relative offsets from the new base time when you call `tick()`.
|
|
389
|
+
*
|
|
390
|
+
* Requires `install()` to have been called first.
|
|
391
|
+
*/
|
|
392
|
+
async setSystemTime(time) {
|
|
393
|
+
const ms = this.parseTime(time);
|
|
394
|
+
await this.adapter.evaluate(`window.__craftdriverClock__.setSystemTime(${ms})`);
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Advance the fake clock by `ms` milliseconds, yielding between each
|
|
398
|
+
* ~16 ms frame so that microtasks and promise callbacks resolve in the
|
|
399
|
+
* correct order.
|
|
400
|
+
*
|
|
401
|
+
* Requires `install()` to have been called first.
|
|
402
|
+
*/
|
|
403
|
+
async runFor(ms) {
|
|
404
|
+
await this.adapter.evaluate(`window.__craftdriverClock__.runFor(${ms})`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
//# sourceMappingURL=clock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clock.js","sourceRoot":"","sources":["../../src/lib/clock.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/driver.d.ts
CHANGED
|
@@ -5,15 +5,31 @@ import { type Condition, type WaitOptions } from './wait.js';
|
|
|
5
5
|
export declare class Driver {
|
|
6
6
|
private endpoint;
|
|
7
7
|
private sessionId;
|
|
8
|
+
/** BiDi WebSocket URL if available */
|
|
9
|
+
__wsUrl?: string;
|
|
8
10
|
constructor(endpoint: WebDriverEndpoint, sessionId: string);
|
|
9
11
|
static create(endpoint: WebDriverEndpoint, caps: Capabilities): Promise<Driver>;
|
|
10
12
|
navigateTo(url: string): Promise<void>;
|
|
13
|
+
/** W3C `POST /session/{id}/back` — navigate the current top-level browsing context back one step. */
|
|
14
|
+
back(): Promise<void>;
|
|
15
|
+
/** W3C `POST /session/{id}/forward` — navigate forward one step in the joint session history. */
|
|
16
|
+
forward(): Promise<void>;
|
|
17
|
+
/** W3C `POST /session/{id}/refresh` — reload the current top-level browsing context. */
|
|
18
|
+
refresh(): Promise<void>;
|
|
11
19
|
quit(): Promise<void>;
|
|
12
20
|
getCurrentUrl(): Promise<string>;
|
|
13
21
|
getTitle(): Promise<string>;
|
|
14
22
|
findElement(locator: By): Promise<WebElement>;
|
|
15
23
|
findElements(locator: By): Promise<WebElement[]>;
|
|
16
24
|
executeScript<T = unknown>(script: string, args?: unknown[]): Promise<T>;
|
|
25
|
+
/**
|
|
26
|
+
* Execute an asynchronous script. The script receives a callback as its
|
|
27
|
+
* last argument and must invoke it with the result (or with no argument
|
|
28
|
+
* for `undefined`). Resolves with the value passed to the callback.
|
|
29
|
+
*/
|
|
30
|
+
executeAsyncScript<T = unknown>(script: string, args?: unknown[]): Promise<T>;
|
|
31
|
+
/** Configure the script (and async-script) timeout in milliseconds. */
|
|
32
|
+
setScriptTimeout(ms: number): Promise<void>;
|
|
17
33
|
wait<T>(condition: Condition<T>, options?: WaitOptions & {
|
|
18
34
|
message?: string;
|
|
19
35
|
}): Promise<T>;
|
|
@@ -30,6 +46,35 @@ export declare class Driver {
|
|
|
30
46
|
performTouchSwipe(from: [number, number], to: [number, number], durationMs?: number): Promise<void>;
|
|
31
47
|
performTouchPinch(center: [number, number], scale?: number, distance?: number, durationMs?: number): Promise<void>;
|
|
32
48
|
screenshotBase64(): Promise<string>;
|
|
49
|
+
/**
|
|
50
|
+
* W3C `POST /session/{id}/window/rect` — resize / reposition the
|
|
51
|
+
* current top-level browsing context's window. Approximates a
|
|
52
|
+
* viewport resize when BiDi's `browsingContext.setViewport` is not
|
|
53
|
+
* available.
|
|
54
|
+
*/
|
|
55
|
+
setWindowRect(rect: {
|
|
56
|
+
width: number;
|
|
57
|
+
height: number;
|
|
58
|
+
x?: number;
|
|
59
|
+
y?: number;
|
|
60
|
+
}): Promise<void>;
|
|
61
|
+
getAlertText(): Promise<string>;
|
|
62
|
+
acceptAlert(): Promise<void>;
|
|
63
|
+
dismissAlert(): Promise<void>;
|
|
64
|
+
sendAlertText(text: string): Promise<void>;
|
|
33
65
|
__sendActions(actions: any[]): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Switch to an iframe by null (top-level), numeric index, or element reference.
|
|
68
|
+
* Pass `null` to switch back to the top-level browsing context.
|
|
69
|
+
*/
|
|
70
|
+
switchToFrame(id: null | number | {
|
|
71
|
+
elementId: string;
|
|
72
|
+
}): Promise<void>;
|
|
73
|
+
/** Return all window handles for the current session. */
|
|
74
|
+
getWindowHandles(): Promise<string[]>;
|
|
75
|
+
/** Switch focus to the window with the given handle. */
|
|
76
|
+
switchToWindow(handle: string): Promise<void>;
|
|
77
|
+
/** Return the handle for the currently focused window. */
|
|
78
|
+
getCurrentWindowHandle(): Promise<string>;
|
|
34
79
|
}
|
|
35
80
|
//# sourceMappingURL=driver.d.ts.map
|
package/dist/lib/driver.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/lib/driver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAmB,iBAAiB,EAAmB,MAAM,YAAY,CAAC;AACpG,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAuC,MAAM,iBAAiB,CAAC;AAClF,OAAO,EAAiB,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAE5E,qBAAa,MAAM;
|
|
1
|
+
{"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/lib/driver.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/driver.js
CHANGED
|
@@ -4,6 +4,8 @@ import { WebDriverWait } from './wait.js';
|
|
|
4
4
|
export class Driver {
|
|
5
5
|
endpoint;
|
|
6
6
|
sessionId;
|
|
7
|
+
/** BiDi WebSocket URL if available */
|
|
8
|
+
__wsUrl;
|
|
7
9
|
constructor(endpoint, sessionId) {
|
|
8
10
|
this.endpoint = endpoint;
|
|
9
11
|
this.sessionId = sessionId;
|
|
@@ -23,12 +25,33 @@ export class Driver {
|
|
|
23
25
|
const msg = v?.message || v?.data || JSON.stringify(res);
|
|
24
26
|
throw new Error(`Failed to create session${err ? ` [${err}]` : ''}: ${msg}`);
|
|
25
27
|
}
|
|
26
|
-
|
|
28
|
+
const driver = new Driver(endpoint, sessionId);
|
|
29
|
+
// Capture BiDi WebSocket URL from capabilities if available
|
|
30
|
+
const capabilities = value?.capabilities || res?.capabilities;
|
|
31
|
+
if (capabilities?.webSocketUrl) {
|
|
32
|
+
driver.__wsUrl = capabilities.webSocketUrl;
|
|
33
|
+
}
|
|
34
|
+
return driver;
|
|
27
35
|
}
|
|
28
36
|
async navigateTo(url) {
|
|
29
37
|
const client = new HttpClient(this.endpoint);
|
|
30
38
|
await client.send({ method: 'POST', path: `/session/${this.sessionId}/url`, body: { url } });
|
|
31
39
|
}
|
|
40
|
+
/** W3C `POST /session/{id}/back` — navigate the current top-level browsing context back one step. */
|
|
41
|
+
async back() {
|
|
42
|
+
const client = new HttpClient(this.endpoint);
|
|
43
|
+
await client.send({ method: 'POST', path: `/session/${this.sessionId}/back`, body: {} });
|
|
44
|
+
}
|
|
45
|
+
/** W3C `POST /session/{id}/forward` — navigate forward one step in the joint session history. */
|
|
46
|
+
async forward() {
|
|
47
|
+
const client = new HttpClient(this.endpoint);
|
|
48
|
+
await client.send({ method: 'POST', path: `/session/${this.sessionId}/forward`, body: {} });
|
|
49
|
+
}
|
|
50
|
+
/** W3C `POST /session/{id}/refresh` — reload the current top-level browsing context. */
|
|
51
|
+
async refresh() {
|
|
52
|
+
const client = new HttpClient(this.endpoint);
|
|
53
|
+
await client.send({ method: 'POST', path: `/session/${this.sessionId}/refresh`, body: {} });
|
|
54
|
+
}
|
|
32
55
|
async quit() {
|
|
33
56
|
const client = new HttpClient(this.endpoint);
|
|
34
57
|
await client.send({ method: 'DELETE', path: `/session/${this.sessionId}` });
|
|
@@ -92,6 +115,29 @@ export class Driver {
|
|
|
92
115
|
});
|
|
93
116
|
return (res?.value ?? res);
|
|
94
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Execute an asynchronous script. The script receives a callback as its
|
|
120
|
+
* last argument and must invoke it with the result (or with no argument
|
|
121
|
+
* for `undefined`). Resolves with the value passed to the callback.
|
|
122
|
+
*/
|
|
123
|
+
async executeAsyncScript(script, args = []) {
|
|
124
|
+
const client = new HttpClient(this.endpoint);
|
|
125
|
+
const res = await client.send({
|
|
126
|
+
method: 'POST',
|
|
127
|
+
path: `/session/${this.sessionId}/execute/async`,
|
|
128
|
+
body: { script, args },
|
|
129
|
+
});
|
|
130
|
+
return (res?.value ?? res);
|
|
131
|
+
}
|
|
132
|
+
/** Configure the script (and async-script) timeout in milliseconds. */
|
|
133
|
+
async setScriptTimeout(ms) {
|
|
134
|
+
const client = new HttpClient(this.endpoint);
|
|
135
|
+
await client.send({
|
|
136
|
+
method: 'POST',
|
|
137
|
+
path: `/session/${this.sessionId}/timeouts`,
|
|
138
|
+
body: { script: ms },
|
|
139
|
+
});
|
|
140
|
+
}
|
|
95
141
|
wait(condition, a, b, c) {
|
|
96
142
|
// Overload: prefer options object, but accept legacy positional args
|
|
97
143
|
if (typeof a === 'object') {
|
|
@@ -334,6 +380,50 @@ export class Driver {
|
|
|
334
380
|
});
|
|
335
381
|
return String(res?.value ?? res ?? '');
|
|
336
382
|
}
|
|
383
|
+
/**
|
|
384
|
+
* W3C `POST /session/{id}/window/rect` — resize / reposition the
|
|
385
|
+
* current top-level browsing context's window. Approximates a
|
|
386
|
+
* viewport resize when BiDi's `browsingContext.setViewport` is not
|
|
387
|
+
* available.
|
|
388
|
+
*/
|
|
389
|
+
async setWindowRect(rect) {
|
|
390
|
+
const client = new HttpClient(this.endpoint);
|
|
391
|
+
const body = { width: rect.width, height: rect.height };
|
|
392
|
+
if (rect.x !== undefined)
|
|
393
|
+
body.x = rect.x;
|
|
394
|
+
if (rect.y !== undefined)
|
|
395
|
+
body.y = rect.y;
|
|
396
|
+
await client.send({
|
|
397
|
+
method: 'POST',
|
|
398
|
+
path: `/session/${this.sessionId}/window/rect`,
|
|
399
|
+
body,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
// --- Alert / dialog endpoints (W3C Classic WebDriver) ---
|
|
403
|
+
async getAlertText() {
|
|
404
|
+
const client = new HttpClient(this.endpoint);
|
|
405
|
+
const res = await client.send({
|
|
406
|
+
method: 'GET',
|
|
407
|
+
path: `/session/${this.sessionId}/alert/text`,
|
|
408
|
+
});
|
|
409
|
+
return String(res?.value ?? res ?? '');
|
|
410
|
+
}
|
|
411
|
+
async acceptAlert() {
|
|
412
|
+
const client = new HttpClient(this.endpoint);
|
|
413
|
+
await client.send({ method: 'POST', path: `/session/${this.sessionId}/alert/accept`, body: {} });
|
|
414
|
+
}
|
|
415
|
+
async dismissAlert() {
|
|
416
|
+
const client = new HttpClient(this.endpoint);
|
|
417
|
+
await client.send({ method: 'POST', path: `/session/${this.sessionId}/alert/dismiss`, body: {} });
|
|
418
|
+
}
|
|
419
|
+
async sendAlertText(text) {
|
|
420
|
+
const client = new HttpClient(this.endpoint);
|
|
421
|
+
await client.send({
|
|
422
|
+
method: 'POST',
|
|
423
|
+
path: `/session/${this.sessionId}/alert/text`,
|
|
424
|
+
body: { text },
|
|
425
|
+
});
|
|
426
|
+
}
|
|
337
427
|
// Internal helper for ActionsBuilder to send combined payloads
|
|
338
428
|
async __sendActions(actions) {
|
|
339
429
|
const client = new HttpClient(this.endpoint);
|
|
@@ -343,5 +433,43 @@ export class Driver {
|
|
|
343
433
|
body: { actions },
|
|
344
434
|
});
|
|
345
435
|
}
|
|
436
|
+
// --- Frame / window switching (W3C Classic WebDriver) ---
|
|
437
|
+
/**
|
|
438
|
+
* Switch to an iframe by null (top-level), numeric index, or element reference.
|
|
439
|
+
* Pass `null` to switch back to the top-level browsing context.
|
|
440
|
+
*/
|
|
441
|
+
async switchToFrame(id) {
|
|
442
|
+
const client = new HttpClient(this.endpoint);
|
|
443
|
+
let bodyId;
|
|
444
|
+
if (id === null) {
|
|
445
|
+
bodyId = null;
|
|
446
|
+
}
|
|
447
|
+
else if (typeof id === 'number') {
|
|
448
|
+
bodyId = id;
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
bodyId = { [W3C_ELEMENT_KEY]: id.elementId, [LEGACY_ELEMENT_KEY]: id.elementId };
|
|
452
|
+
}
|
|
453
|
+
await client.send({ method: 'POST', path: `/session/${this.sessionId}/frame`, body: { id: bodyId } });
|
|
454
|
+
}
|
|
455
|
+
/** Return all window handles for the current session. */
|
|
456
|
+
async getWindowHandles() {
|
|
457
|
+
const client = new HttpClient(this.endpoint);
|
|
458
|
+
const res = await client.send({ method: 'GET', path: `/session/${this.sessionId}/window/handles` });
|
|
459
|
+
const v = res?.value ?? res;
|
|
460
|
+
return Array.isArray(v) ? v : [];
|
|
461
|
+
}
|
|
462
|
+
/** Switch focus to the window with the given handle. */
|
|
463
|
+
async switchToWindow(handle) {
|
|
464
|
+
const client = new HttpClient(this.endpoint);
|
|
465
|
+
await client.send({ method: 'POST', path: `/session/${this.sessionId}/window`, body: { handle } });
|
|
466
|
+
}
|
|
467
|
+
/** Return the handle for the currently focused window. */
|
|
468
|
+
async getCurrentWindowHandle() {
|
|
469
|
+
const client = new HttpClient(this.endpoint);
|
|
470
|
+
const res = await client.send({ method: 'GET', path: `/session/${this.sessionId}/window` });
|
|
471
|
+
const v = res?.value ?? res;
|
|
472
|
+
return String(v ?? '');
|
|
473
|
+
}
|
|
346
474
|
}
|
|
347
475
|
//# sourceMappingURL=driver.js.map
|