@allwright.dev/core 0.0.31 → 0.0.33

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.
@@ -0,0 +1,17 @@
1
+ import type { ClickResult, CommandOptions, CountResult, ElementResult, FillResult, HighlightOptions, HighlightResult, Locator, LocatorInfo, Page, PressOptions, PressResult, TextResult, WaitForSelectorOptions, WaitForSelectorResult } from "./types.js";
2
+ export declare class LocatorImpl implements Locator {
3
+ readonly page: Page;
4
+ readonly selector: string;
5
+ constructor(input: LocatorInfo);
6
+ click(options?: CommandOptions): Promise<ClickResult>;
7
+ count(options?: CommandOptions): Promise<CountResult>;
8
+ highlight(options?: HighlightOptions): Promise<HighlightResult>;
9
+ focus(options?: CommandOptions): Promise<ElementResult>;
10
+ fill(value: string, options?: CommandOptions): Promise<FillResult>;
11
+ hover(options?: CommandOptions): Promise<ElementResult>;
12
+ press(key: string, options?: PressOptions): Promise<PressResult>;
13
+ textContent(options?: CommandOptions): Promise<TextResult>;
14
+ innerText(options?: CommandOptions): Promise<TextResult>;
15
+ waitFor(options?: WaitForSelectorOptions): Promise<WaitForSelectorResult>;
16
+ locator(selector: string): Locator;
17
+ }
@@ -0,0 +1,45 @@
1
+ import { chainSelectorForTransport } from "./selectors.js";
2
+ export class LocatorImpl {
3
+ page;
4
+ selector;
5
+ constructor(input) {
6
+ this.page = input.page;
7
+ this.selector = input.selector;
8
+ }
9
+ async click(options = {}) {
10
+ return this.page.click(this.selector, options);
11
+ }
12
+ async count(options = {}) {
13
+ return this.page.count(this.selector, options);
14
+ }
15
+ async highlight(options = {}) {
16
+ return this.page.highlight(this.selector, options);
17
+ }
18
+ async focus(options = {}) {
19
+ return this.page.focus(this.selector, options);
20
+ }
21
+ async fill(value, options = {}) {
22
+ return this.page.fill(this.selector, value, options);
23
+ }
24
+ async hover(options = {}) {
25
+ return this.page.hover(this.selector, options);
26
+ }
27
+ async press(key, options = {}) {
28
+ return this.page.press(this.selector, key, options);
29
+ }
30
+ async textContent(options = {}) {
31
+ return this.page.textContent(this.selector, options);
32
+ }
33
+ async innerText(options = {}) {
34
+ return this.page.innerText(this.selector, options);
35
+ }
36
+ async waitFor(options = {}) {
37
+ return this.page.waitForSelector(this.selector, options);
38
+ }
39
+ locator(selector) {
40
+ return new LocatorImpl({
41
+ page: this.page,
42
+ selector: chainSelectorForTransport(this.selector, selector),
43
+ });
44
+ }
45
+ }
package/dist/page.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ import type { ClickResult, CommandOptions, CountResult, ElementResult, FillResult, HighlightOptions, HighlightResult, Locator, NavigateResult, Page, PageInfo, PressOptions, PressResult, RuntimeClient, TextResult, WaitForSelectorOptions, WaitForSelectorResult } from "./types.js";
2
+ export declare class PageImpl implements Page {
3
+ #private;
4
+ constructor(input: PageInfo & {
5
+ runtime: RuntimeClient;
6
+ });
7
+ readonly sessionId: string;
8
+ readonly browserSessionId: string;
9
+ locator(selector: string): Locator;
10
+ goto(url: string, options?: CommandOptions): Promise<NavigateResult>;
11
+ click(selector: string, options?: CommandOptions): Promise<ClickResult>;
12
+ count(selector: string, options?: CommandOptions): Promise<CountResult>;
13
+ highlight(selector: string, options?: HighlightOptions): Promise<HighlightResult>;
14
+ focus(selector: string, options?: CommandOptions): Promise<ElementResult>;
15
+ fill(selector: string, value: string, options?: CommandOptions): Promise<FillResult>;
16
+ hover(selector: string, options?: CommandOptions): Promise<ElementResult>;
17
+ press(selector: string, key: string, options?: PressOptions): Promise<PressResult>;
18
+ textContent(selector: string, options?: CommandOptions): Promise<TextResult>;
19
+ innerText(selector: string, options?: CommandOptions): Promise<TextResult>;
20
+ waitForSelector(selector: string, options?: WaitForSelectorOptions): Promise<WaitForSelectorResult>;
21
+ close(): Promise<void>;
22
+ ping(message?: string): Promise<string>;
23
+ pageInfo(): PageInfo;
24
+ navigate(url: string, options?: CommandOptions): Promise<NavigateResult>;
25
+ }
package/dist/page.js ADDED
@@ -0,0 +1,418 @@
1
+ import { LocatorImpl } from "./locator.js";
2
+ import { normalizeSelectorForTransport } from "./selectors.js";
3
+ import { createPageHandle } from "./runtime.js";
4
+ export class PageImpl {
5
+ #runtime;
6
+ #handlePromise = null;
7
+ constructor(input) {
8
+ this.#runtime = input.runtime;
9
+ this.sessionId = input.sessionId;
10
+ this.browserSessionId = input.browserSessionId;
11
+ }
12
+ sessionId;
13
+ browserSessionId;
14
+ locator(selector) {
15
+ return new LocatorImpl({ page: this, selector: normalizeSelectorForTransport(selector) });
16
+ }
17
+ async goto(url, options = {}) {
18
+ const handle = await this.#getHandle();
19
+ this.#ensureOpen(handle);
20
+ handle.stream.write({
21
+ browserSessionId: this.browserSessionId,
22
+ tabSessionId: this.sessionId,
23
+ navigate: {
24
+ url,
25
+ retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
26
+ },
27
+ });
28
+ let navigated = null;
29
+ let injection = null;
30
+ while (true) {
31
+ const event = await handle.queue.next();
32
+ if (event.navigated) {
33
+ navigated = event.navigated;
34
+ }
35
+ if (event.chromiumBidiInjection) {
36
+ injection = event.chromiumBidiInjection;
37
+ }
38
+ if (event.error?.message) {
39
+ throw new Error(`page session error while navigating: ${event.error.message}`);
40
+ }
41
+ if (event.closed) {
42
+ handle.closed = true;
43
+ throw new Error(`page session ${this.sessionId} closed while navigating`);
44
+ }
45
+ if (navigated && injection) {
46
+ return {
47
+ url: navigated.url ?? "",
48
+ note: navigated.note ?? "",
49
+ bidiSessionId: injection.bidiSessionId ?? "",
50
+ mapperTargetId: injection.mapperTargetId ?? "",
51
+ mapperSessionId: injection.mapperSessionId ?? "",
52
+ packageVersion: injection.packageVersion ?? "",
53
+ };
54
+ }
55
+ }
56
+ }
57
+ async click(selector, options = {}) {
58
+ const handle = await this.#getHandle();
59
+ this.#ensureOpen(handle);
60
+ const transportSelector = normalizeSelectorForTransport(selector);
61
+ handle.stream.write({
62
+ browserSessionId: this.browserSessionId,
63
+ tabSessionId: this.sessionId,
64
+ clickElement: {
65
+ cssSelector: transportSelector,
66
+ retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
67
+ },
68
+ });
69
+ while (true) {
70
+ const event = await handle.queue.next();
71
+ if (event.elementClicked) {
72
+ return {
73
+ selector: event.elementClicked.cssSelector ?? "",
74
+ note: event.elementClicked.note ?? "",
75
+ bidiSessionId: event.elementClicked.bidiSessionId ?? "",
76
+ };
77
+ }
78
+ if (event.error?.message) {
79
+ throw new Error(`page session error while clicking: ${event.error.message}`);
80
+ }
81
+ if (event.closed) {
82
+ handle.closed = true;
83
+ throw new Error(`page session ${this.sessionId} closed while waiting for click result`);
84
+ }
85
+ }
86
+ }
87
+ async count(selector, options = {}) {
88
+ const handle = await this.#getHandle();
89
+ this.#ensureOpen(handle);
90
+ const transportSelector = normalizeSelectorForTransport(selector);
91
+ handle.stream.write({
92
+ browserSessionId: this.browserSessionId,
93
+ tabSessionId: this.sessionId,
94
+ countElements: {
95
+ cssSelector: transportSelector,
96
+ retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
97
+ },
98
+ });
99
+ while (true) {
100
+ const event = await handle.queue.next();
101
+ if (event.elementCounted) {
102
+ return {
103
+ selector: event.elementCounted.cssSelector ?? "",
104
+ count: event.elementCounted.count ?? 0,
105
+ note: event.elementCounted.note ?? "",
106
+ };
107
+ }
108
+ if (event.error?.message) {
109
+ throw new Error(`page session error while counting elements: ${event.error.message}`);
110
+ }
111
+ if (event.closed) {
112
+ handle.closed = true;
113
+ throw new Error(`page session ${this.sessionId} closed while waiting for count result`);
114
+ }
115
+ }
116
+ }
117
+ async highlight(selector, options = {}) {
118
+ const handle = await this.#getHandle();
119
+ this.#ensureOpen(handle);
120
+ const transportSelector = normalizeSelectorForTransport(selector);
121
+ handle.stream.write({
122
+ browserSessionId: this.browserSessionId,
123
+ tabSessionId: this.sessionId,
124
+ highlightElements: {
125
+ cssSelector: transportSelector,
126
+ durationMs: options.durationMs,
127
+ retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
128
+ },
129
+ });
130
+ while (true) {
131
+ const event = await handle.queue.next();
132
+ if (event.elementsHighlighted) {
133
+ return {
134
+ selector: event.elementsHighlighted.cssSelector ?? "",
135
+ count: event.elementsHighlighted.count ?? 0,
136
+ note: event.elementsHighlighted.note ?? "",
137
+ };
138
+ }
139
+ if (event.error?.message) {
140
+ throw new Error(`page session error while highlighting elements: ${event.error.message}`);
141
+ }
142
+ if (event.closed) {
143
+ handle.closed = true;
144
+ throw new Error(`page session ${this.sessionId} closed while waiting for highlight result`);
145
+ }
146
+ }
147
+ }
148
+ async focus(selector, options = {}) {
149
+ const handle = await this.#getHandle();
150
+ this.#ensureOpen(handle);
151
+ const transportSelector = normalizeSelectorForTransport(selector);
152
+ handle.stream.write({
153
+ browserSessionId: this.browserSessionId,
154
+ tabSessionId: this.sessionId,
155
+ focusElement: {
156
+ cssSelector: transportSelector,
157
+ retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
158
+ },
159
+ });
160
+ while (true) {
161
+ const event = await handle.queue.next();
162
+ if (event.elementFocused) {
163
+ return {
164
+ selector: event.elementFocused.cssSelector ?? "",
165
+ note: event.elementFocused.note ?? "",
166
+ };
167
+ }
168
+ if (event.error?.message) {
169
+ throw new Error(`page session error while focusing: ${event.error.message}`);
170
+ }
171
+ if (event.closed) {
172
+ handle.closed = true;
173
+ throw new Error(`page session ${this.sessionId} closed while waiting for focus result`);
174
+ }
175
+ }
176
+ }
177
+ async fill(selector, value, options = {}) {
178
+ const handle = await this.#getHandle();
179
+ this.#ensureOpen(handle);
180
+ const transportSelector = normalizeSelectorForTransport(selector);
181
+ handle.stream.write({
182
+ browserSessionId: this.browserSessionId,
183
+ tabSessionId: this.sessionId,
184
+ fillElement: {
185
+ cssSelector: transportSelector,
186
+ value,
187
+ retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
188
+ },
189
+ });
190
+ while (true) {
191
+ const event = await handle.queue.next();
192
+ if (event.elementFilled) {
193
+ return {
194
+ selector: event.elementFilled.cssSelector ?? "",
195
+ value: event.elementFilled.value ?? "",
196
+ note: event.elementFilled.note ?? "",
197
+ };
198
+ }
199
+ if (event.error?.message) {
200
+ throw new Error(`page session error while filling: ${event.error.message}`);
201
+ }
202
+ if (event.closed) {
203
+ handle.closed = true;
204
+ throw new Error(`page session ${this.sessionId} closed while waiting for fill result`);
205
+ }
206
+ }
207
+ }
208
+ async hover(selector, options = {}) {
209
+ const handle = await this.#getHandle();
210
+ this.#ensureOpen(handle);
211
+ const transportSelector = normalizeSelectorForTransport(selector);
212
+ handle.stream.write({
213
+ browserSessionId: this.browserSessionId,
214
+ tabSessionId: this.sessionId,
215
+ hoverElement: {
216
+ cssSelector: transportSelector,
217
+ retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
218
+ },
219
+ });
220
+ while (true) {
221
+ const event = await handle.queue.next();
222
+ if (event.elementHovered) {
223
+ return {
224
+ selector: event.elementHovered.cssSelector ?? "",
225
+ note: event.elementHovered.note ?? "",
226
+ };
227
+ }
228
+ if (event.error?.message) {
229
+ throw new Error(`page session error while hovering: ${event.error.message}`);
230
+ }
231
+ if (event.closed) {
232
+ handle.closed = true;
233
+ throw new Error(`page session ${this.sessionId} closed while waiting for hover result`);
234
+ }
235
+ }
236
+ }
237
+ async press(selector, key, options = {}) {
238
+ const handle = await this.#getHandle();
239
+ this.#ensureOpen(handle);
240
+ const transportSelector = normalizeSelectorForTransport(selector);
241
+ handle.stream.write({
242
+ browserSessionId: this.browserSessionId,
243
+ tabSessionId: this.sessionId,
244
+ pressKey: {
245
+ cssSelector: transportSelector,
246
+ key,
247
+ text: options.text,
248
+ retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
249
+ },
250
+ });
251
+ while (true) {
252
+ const event = await handle.queue.next();
253
+ if (event.keyPressed) {
254
+ return {
255
+ selector: event.keyPressed.cssSelector ?? "",
256
+ key: event.keyPressed.key ?? "",
257
+ note: event.keyPressed.note ?? "",
258
+ };
259
+ }
260
+ if (event.error?.message) {
261
+ throw new Error(`page session error while pressing key: ${event.error.message}`);
262
+ }
263
+ if (event.closed) {
264
+ handle.closed = true;
265
+ throw new Error(`page session ${this.sessionId} closed while waiting for press result`);
266
+ }
267
+ }
268
+ }
269
+ async textContent(selector, options = {}) {
270
+ return this.#readText(selector, options, true);
271
+ }
272
+ async innerText(selector, options = {}) {
273
+ return this.#readText(selector, options, false);
274
+ }
275
+ async waitForSelector(selector, options = {}) {
276
+ const handle = await this.#getHandle();
277
+ this.#ensureOpen(handle);
278
+ const transportSelector = normalizeSelectorForTransport(selector);
279
+ handle.stream.write({
280
+ browserSessionId: this.browserSessionId,
281
+ tabSessionId: this.sessionId,
282
+ waitForSelector: {
283
+ cssSelector: transportSelector,
284
+ visible: options.visible,
285
+ retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
286
+ },
287
+ });
288
+ while (true) {
289
+ const event = await handle.queue.next();
290
+ if (event.selectorWaitSatisfied) {
291
+ return {
292
+ selector: event.selectorWaitSatisfied.cssSelector ?? "",
293
+ visible: event.selectorWaitSatisfied.visible ?? false,
294
+ note: event.selectorWaitSatisfied.note ?? "",
295
+ };
296
+ }
297
+ if (event.error?.message) {
298
+ throw new Error(`page session error while waiting for selector: ${event.error.message}`);
299
+ }
300
+ if (event.closed) {
301
+ handle.closed = true;
302
+ throw new Error(`page session ${this.sessionId} closed while waiting for selector result`);
303
+ }
304
+ }
305
+ }
306
+ async close() {
307
+ const handle = await this.#getHandle();
308
+ if (handle.closed) {
309
+ return;
310
+ }
311
+ handle.stream.write({
312
+ browserSessionId: this.browserSessionId,
313
+ tabSessionId: this.sessionId,
314
+ close: {},
315
+ });
316
+ while (true) {
317
+ const event = await handle.queue.next();
318
+ if (event.closed) {
319
+ handle.closed = true;
320
+ handle.stream.end();
321
+ return;
322
+ }
323
+ if (event.error?.message) {
324
+ throw new Error(`page session error while closing: ${event.error.message}`);
325
+ }
326
+ }
327
+ }
328
+ async ping(message = "ping") {
329
+ const handle = await this.#getHandle();
330
+ this.#ensureOpen(handle);
331
+ handle.stream.write({
332
+ browserSessionId: this.browserSessionId,
333
+ tabSessionId: this.sessionId,
334
+ ping: {
335
+ message,
336
+ },
337
+ });
338
+ while (true) {
339
+ const event = await handle.queue.next();
340
+ if (event.pong?.message) {
341
+ return event.pong.message;
342
+ }
343
+ if (event.error?.message) {
344
+ throw new Error(`page session error while pinging: ${event.error.message}`);
345
+ }
346
+ if (event.closed) {
347
+ handle.closed = true;
348
+ throw new Error(`page session ${this.sessionId} closed while waiting for pong`);
349
+ }
350
+ }
351
+ }
352
+ pageInfo() {
353
+ return {
354
+ sessionId: this.sessionId,
355
+ browserSessionId: this.browserSessionId,
356
+ };
357
+ }
358
+ async navigate(url, options = {}) {
359
+ return this.goto(url, options);
360
+ }
361
+ async #readText(selector, options, textContent) {
362
+ const handle = await this.#getHandle();
363
+ this.#ensureOpen(handle);
364
+ const transportSelector = normalizeSelectorForTransport(selector);
365
+ handle.stream.write(textContent
366
+ ? {
367
+ browserSessionId: this.browserSessionId,
368
+ tabSessionId: this.sessionId,
369
+ getTextContent: {
370
+ cssSelector: transportSelector,
371
+ retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
372
+ },
373
+ }
374
+ : {
375
+ browserSessionId: this.browserSessionId,
376
+ tabSessionId: this.sessionId,
377
+ getInnerText: {
378
+ cssSelector: transportSelector,
379
+ retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
380
+ },
381
+ });
382
+ while (true) {
383
+ const event = await handle.queue.next();
384
+ if (event.textContentResolved) {
385
+ return {
386
+ selector: event.textContentResolved.cssSelector ?? "",
387
+ text: event.textContentResolved.text ?? "",
388
+ note: event.textContentResolved.note ?? "",
389
+ };
390
+ }
391
+ if (event.innerTextResolved) {
392
+ return {
393
+ selector: event.innerTextResolved.cssSelector ?? "",
394
+ text: event.innerTextResolved.text ?? "",
395
+ note: event.innerTextResolved.note ?? "",
396
+ };
397
+ }
398
+ if (event.error?.message) {
399
+ throw new Error(`page session error while reading text: ${event.error.message}`);
400
+ }
401
+ if (event.closed) {
402
+ handle.closed = true;
403
+ throw new Error(`page session ${this.sessionId} closed while waiting for text result`);
404
+ }
405
+ }
406
+ }
407
+ #ensureOpen(handle) {
408
+ if (handle.closed) {
409
+ throw new Error(`page session ${this.sessionId} is closed`);
410
+ }
411
+ }
412
+ async #getHandle() {
413
+ if (!this.#handlePromise) {
414
+ this.#handlePromise = createPageHandle(this.#runtime);
415
+ }
416
+ return this.#handlePromise;
417
+ }
418
+ }
@@ -0,0 +1,14 @@
1
+ import { EventQueue } from "./types.js";
2
+ import type { BrowserSessionEvent, BrowserSessionStream, Browser, BrowserKind, LaunchOptions, PageHandle, ResolveConfigOptions, ResolvedAllwrightConfig, RuntimeClient } from "./types.js";
3
+ export declare function setServerAddr(serverAddr: string): void;
4
+ export declare function shutdown(): Promise<void>;
5
+ export declare function ping(): Promise<string>;
6
+ export declare function getRuntime(): Promise<RuntimeClient>;
7
+ export declare function createPageHandle(runtime: RuntimeClient): Promise<PageHandle>;
8
+ export declare function createBrowserSessionHandle(runtime: RuntimeClient): Promise<{
9
+ stream: BrowserSessionStream;
10
+ queue: EventQueue<BrowserSessionEvent>;
11
+ }>;
12
+ export declare function launchConfiguredBrowser(config: ResolvedAllwrightConfig, launchBrowserKind: (browserKind: BrowserKind, options?: LaunchOptions) => Promise<Browser>): Promise<Browser>;
13
+ export declare function normalizeServerAddr(raw: string): string;
14
+ export declare function resolveLaunchBrowserArgs(browserKindOrOptions: BrowserKind | ResolveConfigOptions | undefined): browserKindOrOptions is undefined | ResolveConfigOptions;
@@ -0,0 +1,107 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import grpc from "@grpc/grpc-js";
3
+ import protoLoader from "@grpc/proto-loader";
4
+ import { EventQueue } from "./types.js";
5
+ const DEFAULT_SERVER_ADDR = "127.0.0.1:50051";
6
+ const SERVER_ADDR_ENV_VAR = "ALLWRIGHT_SERVER_ADDR";
7
+ const PROTO_ROOT = fileURLToPath(new URL("../proto/", import.meta.url));
8
+ const ENGINE_PROTO_PATH = fileURLToPath(new URL("../proto/engine/v1/engine.proto", import.meta.url));
9
+ let runtimePromise = null;
10
+ let serverAddrOverride = null;
11
+ export function setServerAddr(serverAddr) {
12
+ serverAddrOverride = normalizeServerAddr(serverAddr);
13
+ runtimePromise = null;
14
+ }
15
+ export async function shutdown() {
16
+ if (!runtimePromise) {
17
+ return;
18
+ }
19
+ const runtime = await runtimePromise;
20
+ runtime.client.close();
21
+ runtimePromise = null;
22
+ }
23
+ export async function ping() {
24
+ const runtime = await getRuntime();
25
+ return new Promise((resolve, reject) => {
26
+ runtime.client.Ping({}, (error, response) => {
27
+ if (error) {
28
+ reject(new Error(`ping engine server: ${error.message}`));
29
+ return;
30
+ }
31
+ resolve(response.message ?? "");
32
+ });
33
+ });
34
+ }
35
+ export async function getRuntime() {
36
+ if (!runtimePromise) {
37
+ runtimePromise = Promise.resolve(createRuntime());
38
+ }
39
+ return runtimePromise;
40
+ }
41
+ export async function createPageHandle(runtime) {
42
+ const stream = runtime.client.TabSession();
43
+ const queue = bindStreamQueue(stream);
44
+ return {
45
+ stream,
46
+ queue,
47
+ closed: false,
48
+ };
49
+ }
50
+ export async function createBrowserSessionHandle(runtime) {
51
+ const stream = runtime.client.BrowserSession();
52
+ const queue = bindStreamQueue(stream);
53
+ return { stream, queue };
54
+ }
55
+ export async function launchConfiguredBrowser(config, launchBrowserKind) {
56
+ return launchBrowserKind(config.browserName, {
57
+ ...config.launchOptions,
58
+ browserBinary: config.browserBinary ?? config.launchOptions.browserBinary,
59
+ });
60
+ }
61
+ export function normalizeServerAddr(raw) {
62
+ const trimmed = raw.trim();
63
+ if (trimmed.startsWith("dns:") || trimmed.startsWith("unix:")) {
64
+ return trimmed;
65
+ }
66
+ if (trimmed.includes("://")) {
67
+ const parsed = new URL(trimmed);
68
+ return parsed.host;
69
+ }
70
+ return trimmed;
71
+ }
72
+ export function resolveLaunchBrowserArgs(browserKindOrOptions) {
73
+ return browserKindOrOptions === undefined || typeof browserKindOrOptions !== "string";
74
+ }
75
+ function createRuntime() {
76
+ const loaded = protoLoader.loadSync(ENGINE_PROTO_PATH, {
77
+ includeDirs: [PROTO_ROOT],
78
+ keepCase: false,
79
+ longs: String,
80
+ enums: String,
81
+ defaults: true,
82
+ oneofs: true,
83
+ });
84
+ const proto = grpc.loadPackageDefinition(loaded);
85
+ const ClientCtor = proto.allwright.engine.v1.EngineService;
86
+ const client = new ClientCtor(configuredServerAddr(), grpc.credentials.createInsecure());
87
+ return { client };
88
+ }
89
+ function configuredServerAddr() {
90
+ if (serverAddrOverride) {
91
+ return serverAddrOverride;
92
+ }
93
+ return normalizeServerAddr(process.env[SERVER_ADDR_ENV_VAR] ?? DEFAULT_SERVER_ADDR);
94
+ }
95
+ function bindStreamQueue(stream) {
96
+ const queue = new EventQueue();
97
+ stream.on("data", (event) => {
98
+ queue.push(event);
99
+ });
100
+ stream.on("error", (error) => {
101
+ queue.fail(new Error(`grpc stream error: ${error.message}`));
102
+ });
103
+ stream.on("end", () => {
104
+ queue.fail(new Error("grpc stream ended"));
105
+ });
106
+ return queue;
107
+ }
@@ -0,0 +1,7 @@
1
+ export type SelectorFlavor = "css" | "xpath";
2
+ export declare function parseSelectorForTransport(selector: string): {
3
+ flavor: SelectorFlavor;
4
+ body: string;
5
+ };
6
+ export declare function normalizeSelectorForTransport(selector: string): string;
7
+ export declare function chainSelectorForTransport(parent: string, child: string): string;
@@ -0,0 +1,33 @@
1
+ export function parseSelectorForTransport(selector) {
2
+ const trimmed = selector.trim();
3
+ const lower = trimmed.toLowerCase();
4
+ if (lower.startsWith("xpath=") || lower.startsWith("xpath:")) {
5
+ return { flavor: "xpath", body: trimmed.slice(6).trim() };
6
+ }
7
+ if (lower.startsWith("css=") || lower.startsWith("css:")) {
8
+ return { flavor: "css", body: trimmed.slice(4).trim() };
9
+ }
10
+ if (trimmed.startsWith("//") ||
11
+ trimmed.startsWith(".//") ||
12
+ trimmed.startsWith("../") ||
13
+ trimmed.startsWith("/") ||
14
+ trimmed.startsWith("(")) {
15
+ return { flavor: "xpath", body: trimmed };
16
+ }
17
+ return { flavor: "css", body: trimmed };
18
+ }
19
+ export function normalizeSelectorForTransport(selector) {
20
+ const parsed = parseSelectorForTransport(selector);
21
+ return `${parsed.flavor}=${JSON.stringify(parsed.body)}`;
22
+ }
23
+ export function chainSelectorForTransport(parent, child) {
24
+ const parentSelector = normalizeSelectorForTransport(parent);
25
+ const childSelector = normalizeSelectorForTransport(child);
26
+ if (!parentSelector) {
27
+ return childSelector;
28
+ }
29
+ if (!childSelector) {
30
+ return parentSelector;
31
+ }
32
+ return `${parentSelector} ${childSelector}`;
33
+ }