@allwright.dev/core 0.0.32 → 0.0.34
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/browser.d.ts +24 -0
- package/dist/browser.js +137 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +177 -0
- package/dist/index.d.ts +11 -176
- package/dist/index.js +17 -938
- package/dist/locator.d.ts +17 -0
- package/dist/locator.js +45 -0
- package/dist/page.d.ts +25 -0
- package/dist/page.js +418 -0
- package/dist/runtime.d.ts +14 -0
- package/dist/runtime.js +107 -0
- package/dist/selectors.d.ts +7 -0
- package/dist/selectors.js +33 -0
- package/dist/types.d.ts +482 -0
- package/dist/types.js +33 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,715 +1,31 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
const DEFAULT_SERVER_ADDR = "127.0.0.1:50051";
|
|
7
|
-
const SERVER_ADDR_ENV_VAR = "ALLWRIGHT_SERVER_ADDR";
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
-
const PACKAGE_ROOT = path.resolve(__dirname, "..");
|
|
11
|
-
const PROTO_ROOT = path.join(PACKAGE_ROOT, "proto");
|
|
12
|
-
const ENGINE_PROTO_PATH = path.join(PROTO_ROOT, "engine", "v1", "engine.proto");
|
|
13
|
-
let runtimePromise = null;
|
|
14
|
-
let serverAddrOverride = null;
|
|
15
|
-
function parseSelectorForTransport(selector) {
|
|
16
|
-
const trimmed = selector.trim();
|
|
17
|
-
const lower = trimmed.toLowerCase();
|
|
18
|
-
if (lower.startsWith("xpath=") || lower.startsWith("xpath:")) {
|
|
19
|
-
return { flavor: "xpath", body: trimmed.slice(6).trim() };
|
|
20
|
-
}
|
|
21
|
-
if (lower.startsWith("css=") || lower.startsWith("css:")) {
|
|
22
|
-
return { flavor: "css", body: trimmed.slice(4).trim() };
|
|
23
|
-
}
|
|
24
|
-
if (trimmed.startsWith("//") ||
|
|
25
|
-
trimmed.startsWith(".//") ||
|
|
26
|
-
trimmed.startsWith("../") ||
|
|
27
|
-
trimmed.startsWith("/") ||
|
|
28
|
-
trimmed.startsWith("(")) {
|
|
29
|
-
return { flavor: "xpath", body: trimmed };
|
|
30
|
-
}
|
|
31
|
-
return { flavor: "css", body: trimmed };
|
|
32
|
-
}
|
|
33
|
-
function normalizeSelectorForTransport(selector) {
|
|
34
|
-
const parsed = parseSelectorForTransport(selector);
|
|
35
|
-
return `${parsed.flavor}=${JSON.stringify(parsed.body)}`;
|
|
36
|
-
}
|
|
37
|
-
function chainSelectorForTransport(parent, child) {
|
|
38
|
-
const parentSelector = normalizeSelectorForTransport(parent);
|
|
39
|
-
const childSelector = normalizeSelectorForTransport(child);
|
|
40
|
-
if (!parentSelector) {
|
|
41
|
-
return childSelector;
|
|
42
|
-
}
|
|
43
|
-
if (!childSelector) {
|
|
44
|
-
return parentSelector;
|
|
45
|
-
}
|
|
46
|
-
return `${parentSelector} ${childSelector}`;
|
|
47
|
-
}
|
|
48
|
-
const CONFIG_FILENAMES = [
|
|
49
|
-
"allwright.config.yaml",
|
|
50
|
-
"allwright.config.yml",
|
|
51
|
-
"allwright.config.json",
|
|
52
|
-
".allwright/config.yaml",
|
|
53
|
-
".allwright/config.yml",
|
|
54
|
-
".allwright/config.json",
|
|
55
|
-
];
|
|
56
|
-
class EventQueue {
|
|
57
|
-
#items = [];
|
|
58
|
-
#waiters = [];
|
|
59
|
-
#endedError = null;
|
|
60
|
-
push(item) {
|
|
61
|
-
const waiter = this.#waiters.shift();
|
|
62
|
-
if (waiter) {
|
|
63
|
-
waiter.resolve(item);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
this.#items.push(item);
|
|
67
|
-
}
|
|
68
|
-
fail(error) {
|
|
69
|
-
if (this.#endedError) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
this.#endedError = error;
|
|
73
|
-
while (this.#waiters.length > 0) {
|
|
74
|
-
this.#waiters.shift().reject(error);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
async next() {
|
|
78
|
-
if (this.#items.length > 0) {
|
|
79
|
-
return this.#items.shift();
|
|
80
|
-
}
|
|
81
|
-
if (this.#endedError) {
|
|
82
|
-
throw this.#endedError;
|
|
83
|
-
}
|
|
84
|
-
return new Promise((resolve, reject) => {
|
|
85
|
-
this.#waiters.push({ resolve, reject });
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
class BrowserTypeImpl {
|
|
90
|
-
#browserKind;
|
|
91
|
-
constructor(browserKind = "chromium") {
|
|
92
|
-
this.#browserKind = browserKind;
|
|
93
|
-
}
|
|
94
|
-
async launch(options = {}) {
|
|
95
|
-
return launchBrowser(this.#browserKind, options);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
class BrowserImpl {
|
|
99
|
-
#closed = false;
|
|
100
|
-
#runtime;
|
|
101
|
-
#stream;
|
|
102
|
-
#queue;
|
|
103
|
-
#pages = new Map();
|
|
104
|
-
#initialPage;
|
|
105
|
-
constructor(state) {
|
|
106
|
-
const browserInfo = {
|
|
107
|
-
sessionId: state.sessionId,
|
|
108
|
-
browserName: state.launched.browser ?? "",
|
|
109
|
-
launchNote: state.launched.note ?? "",
|
|
110
|
-
cdpWebSocketURL: "",
|
|
111
|
-
userDataDir: state.launched.userDataDir ?? "",
|
|
112
|
-
};
|
|
113
|
-
this.#runtime = state.runtime;
|
|
114
|
-
this.#stream = state.stream;
|
|
115
|
-
this.#queue = state.queue;
|
|
116
|
-
this.sessionId = browserInfo.sessionId;
|
|
117
|
-
this.browserName = browserInfo.browserName;
|
|
118
|
-
this.launchNote = browserInfo.launchNote;
|
|
119
|
-
this.cdpWebSocketURL = browserInfo.cdpWebSocketURL;
|
|
120
|
-
this.userDataDir = browserInfo.userDataDir;
|
|
121
|
-
this.#initialPage = this.#createPage(state.launched.initialTabSessionId ?? "");
|
|
122
|
-
}
|
|
123
|
-
sessionId;
|
|
124
|
-
browserName;
|
|
125
|
-
launchNote;
|
|
126
|
-
cdpWebSocketURL;
|
|
127
|
-
userDataDir;
|
|
128
|
-
page() {
|
|
129
|
-
return this.#initialPage;
|
|
130
|
-
}
|
|
131
|
-
initialPage() {
|
|
132
|
-
return this.#initialPage;
|
|
133
|
-
}
|
|
134
|
-
pages() {
|
|
135
|
-
return [...this.#pages.values()];
|
|
136
|
-
}
|
|
137
|
-
async newPage(options = {}) {
|
|
138
|
-
this.#ensureOpen();
|
|
139
|
-
this.#stream.write({
|
|
140
|
-
openTab: {
|
|
141
|
-
retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
while (true) {
|
|
145
|
-
const event = await this.#queue.next();
|
|
146
|
-
if (event.tabOpened?.tabSessionId) {
|
|
147
|
-
return this.#createPage(event.tabOpened.tabSessionId);
|
|
148
|
-
}
|
|
149
|
-
if (event.error?.message) {
|
|
150
|
-
throw new Error(`browser session error while opening tab: ${event.error.message}`);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
async close() {
|
|
155
|
-
if (this.#closed) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
this.#stream.write({
|
|
159
|
-
close: {},
|
|
160
|
-
});
|
|
161
|
-
while (true) {
|
|
162
|
-
const event = await this.#queue.next();
|
|
163
|
-
if (event.closed) {
|
|
164
|
-
this.#closed = true;
|
|
165
|
-
this.#stream.end();
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
if (event.error?.message) {
|
|
169
|
-
throw new Error(`browser session error while closing: ${event.error.message}`);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
async ping(message = "ping") {
|
|
174
|
-
this.#ensureOpen();
|
|
175
|
-
this.#stream.write({
|
|
176
|
-
ping: {
|
|
177
|
-
message,
|
|
178
|
-
},
|
|
179
|
-
});
|
|
180
|
-
while (true) {
|
|
181
|
-
const event = await this.#queue.next();
|
|
182
|
-
if (event.pong?.message) {
|
|
183
|
-
return event.pong.message;
|
|
184
|
-
}
|
|
185
|
-
if (event.error?.message) {
|
|
186
|
-
throw new Error(`browser session error while pinging: ${event.error.message}`);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
browserInfo() {
|
|
191
|
-
return {
|
|
192
|
-
sessionId: this.sessionId,
|
|
193
|
-
browserName: this.browserName,
|
|
194
|
-
launchNote: this.launchNote,
|
|
195
|
-
cdpWebSocketURL: this.cdpWebSocketURL,
|
|
196
|
-
userDataDir: this.userDataDir,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
initialTab() {
|
|
200
|
-
return this.initialPage();
|
|
201
|
-
}
|
|
202
|
-
async newTab(options = {}) {
|
|
203
|
-
return this.newPage(options);
|
|
204
|
-
}
|
|
205
|
-
#createPage(sessionId) {
|
|
206
|
-
const existing = this.#pages.get(sessionId);
|
|
207
|
-
if (existing) {
|
|
208
|
-
return existing;
|
|
209
|
-
}
|
|
210
|
-
const page = new PageImpl({
|
|
211
|
-
runtime: this.#runtime,
|
|
212
|
-
browserSessionId: this.sessionId,
|
|
213
|
-
sessionId,
|
|
214
|
-
});
|
|
215
|
-
this.#pages.set(sessionId, page);
|
|
216
|
-
return page;
|
|
217
|
-
}
|
|
218
|
-
#ensureOpen() {
|
|
219
|
-
if (this.#closed) {
|
|
220
|
-
throw new Error(`browser session ${this.sessionId} is closed`);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
class PageImpl {
|
|
225
|
-
#runtime;
|
|
226
|
-
#handlePromise = null;
|
|
227
|
-
constructor(input) {
|
|
228
|
-
this.#runtime = input.runtime;
|
|
229
|
-
this.sessionId = input.sessionId;
|
|
230
|
-
this.browserSessionId = input.browserSessionId;
|
|
231
|
-
}
|
|
232
|
-
sessionId;
|
|
233
|
-
browserSessionId;
|
|
234
|
-
locator(selector) {
|
|
235
|
-
return new LocatorImpl({ page: this, selector: normalizeSelectorForTransport(selector) });
|
|
236
|
-
}
|
|
237
|
-
async goto(url, options = {}) {
|
|
238
|
-
const handle = await this.#getHandle();
|
|
239
|
-
this.#ensureOpen(handle);
|
|
240
|
-
handle.stream.write({
|
|
241
|
-
browserSessionId: this.browserSessionId,
|
|
242
|
-
tabSessionId: this.sessionId,
|
|
243
|
-
navigate: {
|
|
244
|
-
url,
|
|
245
|
-
retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
|
|
246
|
-
},
|
|
247
|
-
});
|
|
248
|
-
let navigated = null;
|
|
249
|
-
let injection = null;
|
|
250
|
-
while (true) {
|
|
251
|
-
const event = await handle.queue.next();
|
|
252
|
-
if (event.navigated) {
|
|
253
|
-
navigated = event.navigated;
|
|
254
|
-
}
|
|
255
|
-
if (event.chromiumBidiInjection) {
|
|
256
|
-
injection = event.chromiumBidiInjection;
|
|
257
|
-
}
|
|
258
|
-
if (event.error?.message) {
|
|
259
|
-
throw new Error(`page session error while navigating: ${event.error.message}`);
|
|
260
|
-
}
|
|
261
|
-
if (event.closed) {
|
|
262
|
-
handle.closed = true;
|
|
263
|
-
throw new Error(`page session ${this.sessionId} closed while navigating`);
|
|
264
|
-
}
|
|
265
|
-
if (navigated && injection) {
|
|
266
|
-
return {
|
|
267
|
-
url: navigated.url ?? "",
|
|
268
|
-
note: navigated.note ?? "",
|
|
269
|
-
bidiSessionId: injection.bidiSessionId ?? "",
|
|
270
|
-
mapperTargetId: injection.mapperTargetId ?? "",
|
|
271
|
-
mapperSessionId: injection.mapperSessionId ?? "",
|
|
272
|
-
packageVersion: injection.packageVersion ?? "",
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
async click(selector, options = {}) {
|
|
278
|
-
const handle = await this.#getHandle();
|
|
279
|
-
this.#ensureOpen(handle);
|
|
280
|
-
const transportSelector = normalizeSelectorForTransport(selector);
|
|
281
|
-
handle.stream.write({
|
|
282
|
-
browserSessionId: this.browserSessionId,
|
|
283
|
-
tabSessionId: this.sessionId,
|
|
284
|
-
clickElement: {
|
|
285
|
-
cssSelector: transportSelector,
|
|
286
|
-
retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
|
|
287
|
-
},
|
|
288
|
-
});
|
|
289
|
-
while (true) {
|
|
290
|
-
const event = await handle.queue.next();
|
|
291
|
-
if (event.elementClicked) {
|
|
292
|
-
return {
|
|
293
|
-
selector: event.elementClicked.cssSelector ?? "",
|
|
294
|
-
note: event.elementClicked.note ?? "",
|
|
295
|
-
bidiSessionId: event.elementClicked.bidiSessionId ?? "",
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
if (event.error?.message) {
|
|
299
|
-
throw new Error(`page session error while clicking: ${event.error.message}`);
|
|
300
|
-
}
|
|
301
|
-
if (event.closed) {
|
|
302
|
-
handle.closed = true;
|
|
303
|
-
throw new Error(`page session ${this.sessionId} closed while waiting for click result`);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
async count(selector, options = {}) {
|
|
308
|
-
const handle = await this.#getHandle();
|
|
309
|
-
this.#ensureOpen(handle);
|
|
310
|
-
const transportSelector = normalizeSelectorForTransport(selector);
|
|
311
|
-
handle.stream.write({
|
|
312
|
-
browserSessionId: this.browserSessionId,
|
|
313
|
-
tabSessionId: this.sessionId,
|
|
314
|
-
countElements: {
|
|
315
|
-
cssSelector: transportSelector,
|
|
316
|
-
retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
|
|
317
|
-
},
|
|
318
|
-
});
|
|
319
|
-
while (true) {
|
|
320
|
-
const event = await handle.queue.next();
|
|
321
|
-
if (event.elementCounted) {
|
|
322
|
-
return {
|
|
323
|
-
selector: event.elementCounted.cssSelector ?? "",
|
|
324
|
-
count: event.elementCounted.count ?? 0,
|
|
325
|
-
note: event.elementCounted.note ?? "",
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
if (event.error?.message) {
|
|
329
|
-
throw new Error(`page session error while counting elements: ${event.error.message}`);
|
|
330
|
-
}
|
|
331
|
-
if (event.closed) {
|
|
332
|
-
handle.closed = true;
|
|
333
|
-
throw new Error(`page session ${this.sessionId} closed while waiting for count result`);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
async highlight(selector, options = {}) {
|
|
338
|
-
const handle = await this.#getHandle();
|
|
339
|
-
this.#ensureOpen(handle);
|
|
340
|
-
const transportSelector = normalizeSelectorForTransport(selector);
|
|
341
|
-
handle.stream.write({
|
|
342
|
-
browserSessionId: this.browserSessionId,
|
|
343
|
-
tabSessionId: this.sessionId,
|
|
344
|
-
highlightElements: {
|
|
345
|
-
cssSelector: transportSelector,
|
|
346
|
-
durationMs: options.durationMs,
|
|
347
|
-
retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
|
|
348
|
-
},
|
|
349
|
-
});
|
|
350
|
-
while (true) {
|
|
351
|
-
const event = await handle.queue.next();
|
|
352
|
-
if (event.elementsHighlighted) {
|
|
353
|
-
return {
|
|
354
|
-
selector: event.elementsHighlighted.cssSelector ?? "",
|
|
355
|
-
count: event.elementsHighlighted.count ?? 0,
|
|
356
|
-
note: event.elementsHighlighted.note ?? "",
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
if (event.error?.message) {
|
|
360
|
-
throw new Error(`page session error while highlighting elements: ${event.error.message}`);
|
|
361
|
-
}
|
|
362
|
-
if (event.closed) {
|
|
363
|
-
handle.closed = true;
|
|
364
|
-
throw new Error(`page session ${this.sessionId} closed while waiting for highlight result`);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
async focus(selector, options = {}) {
|
|
369
|
-
const handle = await this.#getHandle();
|
|
370
|
-
this.#ensureOpen(handle);
|
|
371
|
-
const transportSelector = normalizeSelectorForTransport(selector);
|
|
372
|
-
handle.stream.write({
|
|
373
|
-
browserSessionId: this.browserSessionId,
|
|
374
|
-
tabSessionId: this.sessionId,
|
|
375
|
-
focusElement: {
|
|
376
|
-
cssSelector: transportSelector,
|
|
377
|
-
retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
|
|
378
|
-
},
|
|
379
|
-
});
|
|
380
|
-
while (true) {
|
|
381
|
-
const event = await handle.queue.next();
|
|
382
|
-
if (event.elementFocused) {
|
|
383
|
-
return {
|
|
384
|
-
selector: event.elementFocused.cssSelector ?? "",
|
|
385
|
-
note: event.elementFocused.note ?? "",
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
if (event.error?.message) {
|
|
389
|
-
throw new Error(`page session error while focusing: ${event.error.message}`);
|
|
390
|
-
}
|
|
391
|
-
if (event.closed) {
|
|
392
|
-
handle.closed = true;
|
|
393
|
-
throw new Error(`page session ${this.sessionId} closed while waiting for focus result`);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
async fill(selector, value, options = {}) {
|
|
398
|
-
const handle = await this.#getHandle();
|
|
399
|
-
this.#ensureOpen(handle);
|
|
400
|
-
const transportSelector = normalizeSelectorForTransport(selector);
|
|
401
|
-
handle.stream.write({
|
|
402
|
-
browserSessionId: this.browserSessionId,
|
|
403
|
-
tabSessionId: this.sessionId,
|
|
404
|
-
fillElement: {
|
|
405
|
-
cssSelector: transportSelector,
|
|
406
|
-
value,
|
|
407
|
-
retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
|
|
408
|
-
},
|
|
409
|
-
});
|
|
410
|
-
while (true) {
|
|
411
|
-
const event = await handle.queue.next();
|
|
412
|
-
if (event.elementFilled) {
|
|
413
|
-
return {
|
|
414
|
-
selector: event.elementFilled.cssSelector ?? "",
|
|
415
|
-
value: event.elementFilled.value ?? "",
|
|
416
|
-
note: event.elementFilled.note ?? "",
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
if (event.error?.message) {
|
|
420
|
-
throw new Error(`page session error while filling: ${event.error.message}`);
|
|
421
|
-
}
|
|
422
|
-
if (event.closed) {
|
|
423
|
-
handle.closed = true;
|
|
424
|
-
throw new Error(`page session ${this.sessionId} closed while waiting for fill result`);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
async hover(selector, options = {}) {
|
|
429
|
-
const handle = await this.#getHandle();
|
|
430
|
-
this.#ensureOpen(handle);
|
|
431
|
-
const transportSelector = normalizeSelectorForTransport(selector);
|
|
432
|
-
handle.stream.write({
|
|
433
|
-
browserSessionId: this.browserSessionId,
|
|
434
|
-
tabSessionId: this.sessionId,
|
|
435
|
-
hoverElement: {
|
|
436
|
-
cssSelector: transportSelector,
|
|
437
|
-
retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
|
|
438
|
-
},
|
|
439
|
-
});
|
|
440
|
-
while (true) {
|
|
441
|
-
const event = await handle.queue.next();
|
|
442
|
-
if (event.elementHovered) {
|
|
443
|
-
return {
|
|
444
|
-
selector: event.elementHovered.cssSelector ?? "",
|
|
445
|
-
note: event.elementHovered.note ?? "",
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
if (event.error?.message) {
|
|
449
|
-
throw new Error(`page session error while hovering: ${event.error.message}`);
|
|
450
|
-
}
|
|
451
|
-
if (event.closed) {
|
|
452
|
-
handle.closed = true;
|
|
453
|
-
throw new Error(`page session ${this.sessionId} closed while waiting for hover result`);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
async press(selector, key, options = {}) {
|
|
458
|
-
const handle = await this.#getHandle();
|
|
459
|
-
this.#ensureOpen(handle);
|
|
460
|
-
const transportSelector = normalizeSelectorForTransport(selector);
|
|
461
|
-
handle.stream.write({
|
|
462
|
-
browserSessionId: this.browserSessionId,
|
|
463
|
-
tabSessionId: this.sessionId,
|
|
464
|
-
pressKey: {
|
|
465
|
-
cssSelector: transportSelector,
|
|
466
|
-
key,
|
|
467
|
-
text: options.text,
|
|
468
|
-
retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
|
|
469
|
-
},
|
|
470
|
-
});
|
|
471
|
-
while (true) {
|
|
472
|
-
const event = await handle.queue.next();
|
|
473
|
-
if (event.keyPressed) {
|
|
474
|
-
return {
|
|
475
|
-
selector: event.keyPressed.cssSelector ?? "",
|
|
476
|
-
key: event.keyPressed.key ?? "",
|
|
477
|
-
note: event.keyPressed.note ?? "",
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
if (event.error?.message) {
|
|
481
|
-
throw new Error(`page session error while pressing key: ${event.error.message}`);
|
|
482
|
-
}
|
|
483
|
-
if (event.closed) {
|
|
484
|
-
handle.closed = true;
|
|
485
|
-
throw new Error(`page session ${this.sessionId} closed while waiting for press result`);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
async textContent(selector, options = {}) {
|
|
490
|
-
return this.#readText(selector, options, true);
|
|
491
|
-
}
|
|
492
|
-
async innerText(selector, options = {}) {
|
|
493
|
-
return this.#readText(selector, options, false);
|
|
494
|
-
}
|
|
495
|
-
async waitForSelector(selector, options = {}) {
|
|
496
|
-
const handle = await this.#getHandle();
|
|
497
|
-
this.#ensureOpen(handle);
|
|
498
|
-
const transportSelector = normalizeSelectorForTransport(selector);
|
|
499
|
-
handle.stream.write({
|
|
500
|
-
browserSessionId: this.browserSessionId,
|
|
501
|
-
tabSessionId: this.sessionId,
|
|
502
|
-
waitForSelector: {
|
|
503
|
-
cssSelector: transportSelector,
|
|
504
|
-
visible: options.visible,
|
|
505
|
-
retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
|
|
506
|
-
},
|
|
507
|
-
});
|
|
508
|
-
while (true) {
|
|
509
|
-
const event = await handle.queue.next();
|
|
510
|
-
if (event.selectorWaitSatisfied) {
|
|
511
|
-
return {
|
|
512
|
-
selector: event.selectorWaitSatisfied.cssSelector ?? "",
|
|
513
|
-
visible: event.selectorWaitSatisfied.visible ?? false,
|
|
514
|
-
note: event.selectorWaitSatisfied.note ?? "",
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
if (event.error?.message) {
|
|
518
|
-
throw new Error(`page session error while waiting for selector: ${event.error.message}`);
|
|
519
|
-
}
|
|
520
|
-
if (event.closed) {
|
|
521
|
-
handle.closed = true;
|
|
522
|
-
throw new Error(`page session ${this.sessionId} closed while waiting for selector result`);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
async close() {
|
|
527
|
-
const handle = await this.#getHandle();
|
|
528
|
-
if (handle.closed) {
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
handle.stream.write({
|
|
532
|
-
browserSessionId: this.browserSessionId,
|
|
533
|
-
tabSessionId: this.sessionId,
|
|
534
|
-
close: {},
|
|
535
|
-
});
|
|
536
|
-
while (true) {
|
|
537
|
-
const event = await handle.queue.next();
|
|
538
|
-
if (event.closed) {
|
|
539
|
-
handle.closed = true;
|
|
540
|
-
handle.stream.end();
|
|
541
|
-
return;
|
|
542
|
-
}
|
|
543
|
-
if (event.error?.message) {
|
|
544
|
-
throw new Error(`page session error while closing: ${event.error.message}`);
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
async ping(message = "ping") {
|
|
549
|
-
const handle = await this.#getHandle();
|
|
550
|
-
this.#ensureOpen(handle);
|
|
551
|
-
handle.stream.write({
|
|
552
|
-
browserSessionId: this.browserSessionId,
|
|
553
|
-
tabSessionId: this.sessionId,
|
|
554
|
-
ping: {
|
|
555
|
-
message,
|
|
556
|
-
},
|
|
557
|
-
});
|
|
558
|
-
while (true) {
|
|
559
|
-
const event = await handle.queue.next();
|
|
560
|
-
if (event.pong?.message) {
|
|
561
|
-
return event.pong.message;
|
|
562
|
-
}
|
|
563
|
-
if (event.error?.message) {
|
|
564
|
-
throw new Error(`page session error while pinging: ${event.error.message}`);
|
|
565
|
-
}
|
|
566
|
-
if (event.closed) {
|
|
567
|
-
handle.closed = true;
|
|
568
|
-
throw new Error(`page session ${this.sessionId} closed while waiting for pong`);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
pageInfo() {
|
|
573
|
-
return {
|
|
574
|
-
sessionId: this.sessionId,
|
|
575
|
-
browserSessionId: this.browserSessionId,
|
|
576
|
-
};
|
|
577
|
-
}
|
|
578
|
-
async navigate(url, options = {}) {
|
|
579
|
-
return this.goto(url, options);
|
|
580
|
-
}
|
|
581
|
-
async #readText(selector, options, textContent) {
|
|
582
|
-
const handle = await this.#getHandle();
|
|
583
|
-
this.#ensureOpen(handle);
|
|
584
|
-
const transportSelector = normalizeSelectorForTransport(selector);
|
|
585
|
-
handle.stream.write(textContent
|
|
586
|
-
? {
|
|
587
|
-
browserSessionId: this.browserSessionId,
|
|
588
|
-
tabSessionId: this.sessionId,
|
|
589
|
-
getTextContent: {
|
|
590
|
-
cssSelector: transportSelector,
|
|
591
|
-
retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
|
|
592
|
-
},
|
|
593
|
-
}
|
|
594
|
-
: {
|
|
595
|
-
browserSessionId: this.browserSessionId,
|
|
596
|
-
tabSessionId: this.sessionId,
|
|
597
|
-
getInnerText: {
|
|
598
|
-
cssSelector: transportSelector,
|
|
599
|
-
retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
|
|
600
|
-
},
|
|
601
|
-
});
|
|
602
|
-
while (true) {
|
|
603
|
-
const event = await handle.queue.next();
|
|
604
|
-
if (event.textContentResolved) {
|
|
605
|
-
return {
|
|
606
|
-
selector: event.textContentResolved.cssSelector ?? "",
|
|
607
|
-
text: event.textContentResolved.text ?? "",
|
|
608
|
-
note: event.textContentResolved.note ?? "",
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
|
-
if (event.innerTextResolved) {
|
|
612
|
-
return {
|
|
613
|
-
selector: event.innerTextResolved.cssSelector ?? "",
|
|
614
|
-
text: event.innerTextResolved.text ?? "",
|
|
615
|
-
note: event.innerTextResolved.note ?? "",
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
if (event.error?.message) {
|
|
619
|
-
throw new Error(`page session error while reading text: ${event.error.message}`);
|
|
620
|
-
}
|
|
621
|
-
if (event.closed) {
|
|
622
|
-
handle.closed = true;
|
|
623
|
-
throw new Error(`page session ${this.sessionId} closed while waiting for text result`);
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
#ensureOpen(handle) {
|
|
628
|
-
if (handle.closed) {
|
|
629
|
-
throw new Error(`page session ${this.sessionId} is closed`);
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
async #getHandle() {
|
|
633
|
-
if (!this.#handlePromise) {
|
|
634
|
-
this.#handlePromise = createPageHandle(this.#runtime);
|
|
635
|
-
}
|
|
636
|
-
return this.#handlePromise;
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
class LocatorImpl {
|
|
640
|
-
page;
|
|
641
|
-
selector;
|
|
642
|
-
constructor(input) {
|
|
643
|
-
this.page = input.page;
|
|
644
|
-
this.selector = input.selector;
|
|
645
|
-
}
|
|
646
|
-
async click(options = {}) {
|
|
647
|
-
return this.page.click(this.selector, options);
|
|
648
|
-
}
|
|
649
|
-
async count(options = {}) {
|
|
650
|
-
return this.page.count(this.selector, options);
|
|
651
|
-
}
|
|
652
|
-
async highlight(options = {}) {
|
|
653
|
-
return this.page.highlight(this.selector, options);
|
|
654
|
-
}
|
|
655
|
-
async focus(options = {}) {
|
|
656
|
-
return this.page.focus(this.selector, options);
|
|
657
|
-
}
|
|
658
|
-
async fill(value, options = {}) {
|
|
659
|
-
return this.page.fill(this.selector, value, options);
|
|
660
|
-
}
|
|
661
|
-
async hover(options = {}) {
|
|
662
|
-
return this.page.hover(this.selector, options);
|
|
663
|
-
}
|
|
664
|
-
async press(key, options = {}) {
|
|
665
|
-
return this.page.press(this.selector, key, options);
|
|
666
|
-
}
|
|
667
|
-
async textContent(options = {}) {
|
|
668
|
-
return this.page.textContent(this.selector, options);
|
|
669
|
-
}
|
|
670
|
-
async innerText(options = {}) {
|
|
671
|
-
return this.page.innerText(this.selector, options);
|
|
672
|
-
}
|
|
673
|
-
async waitFor(options = {}) {
|
|
674
|
-
return this.page.waitForSelector(this.selector, options);
|
|
675
|
-
}
|
|
676
|
-
locator(selector) {
|
|
677
|
-
return new LocatorImpl({
|
|
678
|
-
page: this.page,
|
|
679
|
-
selector: chainSelectorForTransport(this.selector, selector),
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
}
|
|
1
|
+
import { BrowserImpl, BrowserTypeImpl } from "./browser.js";
|
|
2
|
+
import { findConfigFile, loadConfigFile, resolveConfig } from "./config.js";
|
|
3
|
+
import { PageImpl } from "./page.js";
|
|
4
|
+
import { createBrowserSessionHandle, getRuntime, launchConfiguredBrowser as launchConfiguredBrowserWithResolver, ping as runtimePing, resolveLaunchBrowserArgs, setServerAddr, shutdown, } from "./runtime.js";
|
|
5
|
+
export { findConfigFile, loadConfigFile, resolveConfig, setServerAddr, shutdown };
|
|
683
6
|
export const chromium = new BrowserTypeImpl("chromium");
|
|
684
7
|
export const firefox = new BrowserTypeImpl("firefox");
|
|
685
8
|
export async function ping() {
|
|
686
|
-
|
|
687
|
-
return new Promise((resolve, reject) => {
|
|
688
|
-
runtime.client.Ping({}, (error, response) => {
|
|
689
|
-
if (error) {
|
|
690
|
-
reject(new Error(`ping engine server: ${error.message}`));
|
|
691
|
-
return;
|
|
692
|
-
}
|
|
693
|
-
resolve(response.message ?? "");
|
|
694
|
-
});
|
|
695
|
-
});
|
|
9
|
+
return runtimePing();
|
|
696
10
|
}
|
|
697
11
|
export async function launchChrome(options = {}) {
|
|
698
12
|
return launchBrowser("chromium", options);
|
|
699
13
|
}
|
|
14
|
+
export async function launchFirefox(options = {}) {
|
|
15
|
+
return launchBrowser("firefox", options);
|
|
16
|
+
}
|
|
700
17
|
export async function launchConfiguredBrowser(config) {
|
|
701
|
-
return
|
|
702
|
-
...config.launchOptions,
|
|
703
|
-
browserBinary: config.browserBinary ?? config.launchOptions.browserBinary,
|
|
704
|
-
});
|
|
18
|
+
return launchConfiguredBrowserWithResolver(config, (browserKind, launchOptions) => launchBrowser(browserKind, launchOptions));
|
|
705
19
|
}
|
|
706
|
-
export async function launchBrowser(
|
|
20
|
+
export async function launchBrowser(browserKindOrOptions, options = {}) {
|
|
21
|
+
if (resolveLaunchBrowserArgs(browserKindOrOptions)) {
|
|
22
|
+
return launchConfiguredBrowser(resolveConfig(browserKindOrOptions ?? {}));
|
|
23
|
+
}
|
|
707
24
|
const runtime = await getRuntime();
|
|
708
|
-
const stream = runtime
|
|
709
|
-
const queue = bindStreamQueue(stream);
|
|
25
|
+
const { stream, queue } = await createBrowserSessionHandle(runtime);
|
|
710
26
|
stream.write({
|
|
711
27
|
launchBrowser: {
|
|
712
|
-
browserKind:
|
|
28
|
+
browserKind: browserKindOrOptions === "firefox" ? 2 : 1,
|
|
713
29
|
browserBinary: options.browserBinary,
|
|
714
30
|
retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
|
|
715
31
|
},
|
|
@@ -730,241 +46,4 @@ export async function launchBrowser(browserKind, options = {}) {
|
|
|
730
46
|
}
|
|
731
47
|
}
|
|
732
48
|
}
|
|
733
|
-
export
|
|
734
|
-
serverAddrOverride = normalizeServerAddr(serverAddr);
|
|
735
|
-
runtimePromise = null;
|
|
736
|
-
}
|
|
737
|
-
export async function shutdown() {
|
|
738
|
-
if (!runtimePromise) {
|
|
739
|
-
return;
|
|
740
|
-
}
|
|
741
|
-
const runtime = await runtimePromise;
|
|
742
|
-
runtime.client.close();
|
|
743
|
-
runtimePromise = null;
|
|
744
|
-
}
|
|
745
|
-
export function findConfigFile(startDir = process.cwd()) {
|
|
746
|
-
let currentDir = path.resolve(startDir);
|
|
747
|
-
while (true) {
|
|
748
|
-
for (const filename of CONFIG_FILENAMES) {
|
|
749
|
-
const candidate = path.join(currentDir, filename);
|
|
750
|
-
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
751
|
-
return candidate;
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
const parentDir = path.dirname(currentDir);
|
|
755
|
-
if (parentDir === currentDir) {
|
|
756
|
-
return null;
|
|
757
|
-
}
|
|
758
|
-
currentDir = parentDir;
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
export function loadConfigFile(configFile) {
|
|
762
|
-
const resolved = path.resolve(configFile);
|
|
763
|
-
const raw = fs.readFileSync(resolved, "utf8");
|
|
764
|
-
const parsed = parseConfigContents(raw, resolved);
|
|
765
|
-
validateConfigShape(parsed, resolved);
|
|
766
|
-
return parsed;
|
|
767
|
-
}
|
|
768
|
-
export function resolveConfig(options = {}) {
|
|
769
|
-
const configFilePath = options.configFile ? path.resolve(options.configFile) : findConfigFile(options.cwd);
|
|
770
|
-
const fileConfig = configFilePath ? loadConfigFile(configFilePath) : {};
|
|
771
|
-
const suiteName = options.suite?.trim() || null;
|
|
772
|
-
const suiteConfig = suiteName ? fileConfig.suites?.[suiteName] : undefined;
|
|
773
|
-
if (suiteName && !suiteConfig) {
|
|
774
|
-
throw new Error(`allwright config suite "${suiteName}" was not found in ${configFilePath ?? "the resolved config file"}`);
|
|
775
|
-
}
|
|
776
|
-
const serverAddr = suiteConfig?.server?.addr ?? fileConfig.server?.addr;
|
|
777
|
-
const browserName = suiteConfig?.browser?.name ?? fileConfig.browser?.name ?? "chromium";
|
|
778
|
-
const browserBinary = suiteConfig?.browser?.binary ?? fileConfig.browser?.binary;
|
|
779
|
-
const launchOptions = mergeLaunchOptions(fileConfig.browser?.launchOptions, suiteConfig?.browser?.launchOptions);
|
|
780
|
-
const expect = {
|
|
781
|
-
...(fileConfig.expect ?? {}),
|
|
782
|
-
...(suiteConfig?.expect ?? {}),
|
|
783
|
-
};
|
|
784
|
-
return {
|
|
785
|
-
configFilePath,
|
|
786
|
-
suiteName,
|
|
787
|
-
serverAddr,
|
|
788
|
-
browserName,
|
|
789
|
-
browserBinary,
|
|
790
|
-
launchOptions: browserBinary ? { ...launchOptions, browserBinary } : launchOptions,
|
|
791
|
-
expect,
|
|
792
|
-
};
|
|
793
|
-
}
|
|
794
|
-
async function getRuntime() {
|
|
795
|
-
if (!runtimePromise) {
|
|
796
|
-
runtimePromise = Promise.resolve(createRuntime());
|
|
797
|
-
}
|
|
798
|
-
return runtimePromise;
|
|
799
|
-
}
|
|
800
|
-
function createRuntime() {
|
|
801
|
-
const loaded = protoLoader.loadSync(ENGINE_PROTO_PATH, {
|
|
802
|
-
includeDirs: [PROTO_ROOT],
|
|
803
|
-
keepCase: false,
|
|
804
|
-
longs: String,
|
|
805
|
-
enums: String,
|
|
806
|
-
defaults: true,
|
|
807
|
-
oneofs: true,
|
|
808
|
-
});
|
|
809
|
-
const proto = grpc.loadPackageDefinition(loaded);
|
|
810
|
-
const ClientCtor = proto.allwright.engine.v1.EngineService;
|
|
811
|
-
const client = new ClientCtor(configuredServerAddr(), grpc.credentials.createInsecure());
|
|
812
|
-
return { client };
|
|
813
|
-
}
|
|
814
|
-
function configuredServerAddr() {
|
|
815
|
-
if (serverAddrOverride) {
|
|
816
|
-
return serverAddrOverride;
|
|
817
|
-
}
|
|
818
|
-
return normalizeServerAddr(process.env[SERVER_ADDR_ENV_VAR] ?? DEFAULT_SERVER_ADDR);
|
|
819
|
-
}
|
|
820
|
-
function mergeLaunchOptions(base, override) {
|
|
821
|
-
return {
|
|
822
|
-
...(base ?? {}),
|
|
823
|
-
...(override ?? {}),
|
|
824
|
-
};
|
|
825
|
-
}
|
|
826
|
-
function validateConfigShape(value, source) {
|
|
827
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
828
|
-
throw new Error(`allwright config ${source} must contain a top-level object`);
|
|
829
|
-
}
|
|
830
|
-
const config = value;
|
|
831
|
-
if (config.schemaVersion !== undefined && config.schemaVersion !== 1) {
|
|
832
|
-
throw new Error(`allwright config ${source} has unsupported schemaVersion ${String(config.schemaVersion)}; expected 1`);
|
|
833
|
-
}
|
|
834
|
-
const browserName = config.browser?.name;
|
|
835
|
-
if (browserName !== undefined && browserName !== "chromium" && browserName !== "firefox") {
|
|
836
|
-
throw new Error(`allwright config ${source} has unsupported browser.name ${String(browserName)}; use "chromium" or "firefox"`);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
function parseConfigContents(raw, source) {
|
|
840
|
-
const extension = path.extname(source).toLowerCase();
|
|
841
|
-
if (extension === ".json") {
|
|
842
|
-
return JSON.parse(raw);
|
|
843
|
-
}
|
|
844
|
-
if (extension === ".yaml" || extension === ".yml") {
|
|
845
|
-
return parseSimpleYaml(raw, source);
|
|
846
|
-
}
|
|
847
|
-
throw new Error(`unsupported allwright config file extension ${extension || "<none>"} for ${source}`);
|
|
848
|
-
}
|
|
849
|
-
function parseSimpleYaml(raw, source) {
|
|
850
|
-
const root = {};
|
|
851
|
-
const stack = [
|
|
852
|
-
{ indent: -1, value: root },
|
|
853
|
-
];
|
|
854
|
-
for (const [index, originalLine] of raw.split(/\r?\n/).entries()) {
|
|
855
|
-
const lineNumber = index + 1;
|
|
856
|
-
const line = stripYamlComment(originalLine);
|
|
857
|
-
if (!line.trim()) {
|
|
858
|
-
continue;
|
|
859
|
-
}
|
|
860
|
-
const indent = countLeadingSpaces(line);
|
|
861
|
-
if (indent % 2 !== 0) {
|
|
862
|
-
throw new Error(`invalid YAML indentation in ${source}:${lineNumber}; use multiples of 2 spaces`);
|
|
863
|
-
}
|
|
864
|
-
while (stack.length > 1 && indent <= stack[stack.length - 1].indent) {
|
|
865
|
-
stack.pop();
|
|
866
|
-
}
|
|
867
|
-
const current = stack[stack.length - 1];
|
|
868
|
-
const trimmed = line.trim();
|
|
869
|
-
const separatorIndex = trimmed.indexOf(":");
|
|
870
|
-
if (separatorIndex <= 0) {
|
|
871
|
-
throw new Error(`invalid YAML mapping in ${source}:${lineNumber}`);
|
|
872
|
-
}
|
|
873
|
-
const key = trimmed.slice(0, separatorIndex).trim();
|
|
874
|
-
const rawValue = trimmed.slice(separatorIndex + 1).trim();
|
|
875
|
-
if (!key) {
|
|
876
|
-
throw new Error(`empty YAML key in ${source}:${lineNumber}`);
|
|
877
|
-
}
|
|
878
|
-
if (!rawValue) {
|
|
879
|
-
const child = {};
|
|
880
|
-
current.value[key] = child;
|
|
881
|
-
stack.push({ indent, value: child });
|
|
882
|
-
continue;
|
|
883
|
-
}
|
|
884
|
-
current.value[key] = parseYamlScalar(rawValue, source, lineNumber);
|
|
885
|
-
}
|
|
886
|
-
return root;
|
|
887
|
-
}
|
|
888
|
-
function stripYamlComment(line) {
|
|
889
|
-
let inSingleQuote = false;
|
|
890
|
-
let inDoubleQuote = false;
|
|
891
|
-
for (let index = 0; index < line.length; index += 1) {
|
|
892
|
-
const char = line[index];
|
|
893
|
-
if (char === "'" && !inDoubleQuote) {
|
|
894
|
-
inSingleQuote = !inSingleQuote;
|
|
895
|
-
continue;
|
|
896
|
-
}
|
|
897
|
-
if (char === "\"" && !inSingleQuote) {
|
|
898
|
-
inDoubleQuote = !inDoubleQuote;
|
|
899
|
-
continue;
|
|
900
|
-
}
|
|
901
|
-
if (char === "#" && !inSingleQuote && !inDoubleQuote) {
|
|
902
|
-
return line.slice(0, index);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
return line;
|
|
906
|
-
}
|
|
907
|
-
function countLeadingSpaces(line) {
|
|
908
|
-
let count = 0;
|
|
909
|
-
while (count < line.length && line[count] === " ") {
|
|
910
|
-
count += 1;
|
|
911
|
-
}
|
|
912
|
-
return count;
|
|
913
|
-
}
|
|
914
|
-
function parseYamlScalar(value, source, lineNumber) {
|
|
915
|
-
if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
916
|
-
return value.slice(1, -1);
|
|
917
|
-
}
|
|
918
|
-
if (value === "true") {
|
|
919
|
-
return true;
|
|
920
|
-
}
|
|
921
|
-
if (value === "false") {
|
|
922
|
-
return false;
|
|
923
|
-
}
|
|
924
|
-
if (value === "null") {
|
|
925
|
-
return null;
|
|
926
|
-
}
|
|
927
|
-
if (/^-?\d+$/.test(value)) {
|
|
928
|
-
return Number.parseInt(value, 10);
|
|
929
|
-
}
|
|
930
|
-
if (/^-?\d+\.\d+$/.test(value)) {
|
|
931
|
-
return Number.parseFloat(value);
|
|
932
|
-
}
|
|
933
|
-
if (value.startsWith("[") || value.startsWith("{")) {
|
|
934
|
-
throw new Error(`unsupported YAML collection syntax in ${source}:${lineNumber}; use nested mappings instead`);
|
|
935
|
-
}
|
|
936
|
-
return value;
|
|
937
|
-
}
|
|
938
|
-
function normalizeServerAddr(raw) {
|
|
939
|
-
const trimmed = raw.trim();
|
|
940
|
-
if (trimmed.startsWith("dns:") || trimmed.startsWith("unix:")) {
|
|
941
|
-
return trimmed;
|
|
942
|
-
}
|
|
943
|
-
if (trimmed.includes("://")) {
|
|
944
|
-
const parsed = new URL(trimmed);
|
|
945
|
-
return parsed.host;
|
|
946
|
-
}
|
|
947
|
-
return trimmed;
|
|
948
|
-
}
|
|
949
|
-
function bindStreamQueue(stream) {
|
|
950
|
-
const queue = new EventQueue();
|
|
951
|
-
stream.on("data", (event) => {
|
|
952
|
-
queue.push(event);
|
|
953
|
-
});
|
|
954
|
-
stream.on("error", (error) => {
|
|
955
|
-
queue.fail(new Error(`grpc stream error: ${error.message}`));
|
|
956
|
-
});
|
|
957
|
-
stream.on("end", () => {
|
|
958
|
-
queue.fail(new Error("grpc stream ended"));
|
|
959
|
-
});
|
|
960
|
-
return queue;
|
|
961
|
-
}
|
|
962
|
-
async function createPageHandle(runtime) {
|
|
963
|
-
const stream = runtime.client.TabSession();
|
|
964
|
-
const queue = bindStreamQueue(stream);
|
|
965
|
-
return {
|
|
966
|
-
stream,
|
|
967
|
-
queue,
|
|
968
|
-
closed: false,
|
|
969
|
-
};
|
|
970
|
-
}
|
|
49
|
+
export { BrowserImpl, BrowserTypeImpl, PageImpl };
|