@amazon/vinyl-browserstack 3.0.1
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/README.md +46 -0
- package/dist/WebDriverWorker.d.ts +148 -0
- package/dist/WebDriverWorker.d.ts.map +1 -0
- package/dist/WebDriverWorker.js +332 -0
- package/dist/WebDriverWorkerController.d.ts +62 -0
- package/dist/WebDriverWorkerController.d.ts.map +1 -0
- package/dist/WebDriverWorkerController.js +165 -0
- package/dist/capabilities.d.ts +41 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +59 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/runSeleniumBrowserStack.d.ts +30 -0
- package/dist/runSeleniumBrowserStack.d.ts.map +1 -0
- package/dist/runSeleniumBrowserStack.js +145 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# @amazon/vinyl-browserstack
|
|
2
|
+
|
|
3
|
+
Runs an **in-browser Jasmine** suite across BrowserStack real browsers over
|
|
4
|
+
**Selenium/WebDriver** (`hub-cloud.browserstack.com/wd/hub`).
|
|
5
|
+
|
|
6
|
+
## How it works
|
|
7
|
+
|
|
8
|
+
The consuming package builds a browser test page (its Jasmine bootstrap + specs)
|
|
9
|
+
and serves it. This runner:
|
|
10
|
+
|
|
11
|
+
1. Starts a local Express server (serving the test page) plus a **reporter REST
|
|
12
|
+
API** the in-browser suite posts progress and logs to.
|
|
13
|
+
2. Starts the **BrowserStack Local** tunnel.
|
|
14
|
+
3. Builds a real **WebDriver** session per browser — parallelized to the
|
|
15
|
+
account's available session budget — and navigates each to the test URL with
|
|
16
|
+
a `?reportApi=` query param.
|
|
17
|
+
4. Keeps each session alive with a periodic no-op WebDriver command (the specs
|
|
18
|
+
run in-browser with no driver interaction, so the session's idle timer must
|
|
19
|
+
be reset explicitly — REST status polling does not reset it), and enforces a
|
|
20
|
+
local max-duration cap since WebDriver has no server-side worker timeout.
|
|
21
|
+
5. Aggregates per-spec pass/fail, writes `bStackLogs/<sessionId>.log` per
|
|
22
|
+
session, and stamps each session pass/fail via the Automate REST API.
|
|
23
|
+
|
|
24
|
+
This is the WebDriver successor to the legacy `/5/worker` (JS Testing) transport
|
|
25
|
+
in `@amazon/vinyl-build-utils`. It reuses that package's framework-agnostic
|
|
26
|
+
primitives (reporter API, Express server, Local tunnel, Automate client,
|
|
27
|
+
credentials, logging) and swaps only the transport.
|
|
28
|
+
|
|
29
|
+
Unlike the legacy transport, WebDriver sessions populate the BrowserStack
|
|
30
|
+
dashboard **Console Logs** tab (Chrome only; `bStackLogs/` remains the complete
|
|
31
|
+
cross-browser log source).
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { runSeleniumBrowserStackAndExit } from '@amazon/vinyl-browserstack'
|
|
37
|
+
import { vinylDefaultBrowserStackOptions } from '@amazon/vinyl-build-utils'
|
|
38
|
+
|
|
39
|
+
runSeleniumBrowserStackAndExit({
|
|
40
|
+
...vinylDefaultBrowserStackOptions,
|
|
41
|
+
workerCommon: { project: 'MyProject' },
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Credentials come from `options.credentials` or the `BROWSERSTACK_USERNAME` /
|
|
46
|
+
`BROWSERSTACK_ACCESS_KEY` environment variables (a `.env` file is honored).
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { type BrowserStackAutomateClient, type BrowserStackCredentials, type BrowserStackWorkerOptions, type LogEntry, type SessionDetails, type WorkerOptions } from '@amazon/vinyl-build-utils';
|
|
2
|
+
/**
|
|
3
|
+
* The default BrowserStack WebDriver hub.
|
|
4
|
+
*/
|
|
5
|
+
export declare const DEFAULT_HUB_URL = "https://hub-cloud.browserstack.com/wd/hub";
|
|
6
|
+
/**
|
|
7
|
+
* Local state for a single WebDriver session.
|
|
8
|
+
*/
|
|
9
|
+
export interface WebDriverWorkerState {
|
|
10
|
+
/**
|
|
11
|
+
* A local identifier, used to map reporter API calls back to this session.
|
|
12
|
+
*/
|
|
13
|
+
readonly localId: string;
|
|
14
|
+
/**
|
|
15
|
+
* The browser configuration provided during construction. Replaced with the
|
|
16
|
+
* fallback browser combination if the worker exhausts its capture retries
|
|
17
|
+
* without reporting any progress (see {@link WorkerOptions.fallbackBrowser}).
|
|
18
|
+
*/
|
|
19
|
+
workerOptions: WorkerOptions;
|
|
20
|
+
/**
|
|
21
|
+
* If non-null, indicates a fatal error.
|
|
22
|
+
*/
|
|
23
|
+
error: Error | null;
|
|
24
|
+
/**
|
|
25
|
+
* The session details, as retrieved from the BrowserStack Automate API.
|
|
26
|
+
*/
|
|
27
|
+
session: SessionDetails | null;
|
|
28
|
+
/**
|
|
29
|
+
* True once {@link WebDriverWorker.start} has been called. Does not become
|
|
30
|
+
* false after termination.
|
|
31
|
+
*/
|
|
32
|
+
started: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* True while the WebDriver session is active.
|
|
35
|
+
*/
|
|
36
|
+
active: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* The BrowserStack session hash (the WebDriver session id). Set once the
|
|
39
|
+
* session is established. There is no separate numeric worker id in the
|
|
40
|
+
* WebDriver transport.
|
|
41
|
+
*/
|
|
42
|
+
sessionId: string | null;
|
|
43
|
+
/**
|
|
44
|
+
* The total number of specs, as reported by the in-browser reporter.
|
|
45
|
+
* Null until received.
|
|
46
|
+
*/
|
|
47
|
+
totalSpecs: number | null;
|
|
48
|
+
/**
|
|
49
|
+
* The number of specs completed, as reported by the in-browser reporter.
|
|
50
|
+
*/
|
|
51
|
+
totalCompleted: number;
|
|
52
|
+
/**
|
|
53
|
+
* The number of capture retry attempts remaining. A retry happens on a
|
|
54
|
+
* capture timeout (no progress), not on a test failure.
|
|
55
|
+
*/
|
|
56
|
+
attemptsRemaining: number;
|
|
57
|
+
}
|
|
58
|
+
export type ReadonlyWebDriverWorkerState = Readonly<WebDriverWorkerState>;
|
|
59
|
+
export interface WebDriverWorkerDeps {
|
|
60
|
+
/**
|
|
61
|
+
* The BrowserStack Automate REST client, used for session status polling
|
|
62
|
+
* and pass/fail stamping (endpoints that apply to WebDriver sessions).
|
|
63
|
+
*/
|
|
64
|
+
readonly client: BrowserStackAutomateClient;
|
|
65
|
+
/**
|
|
66
|
+
* Credentials, placed in the session capabilities.
|
|
67
|
+
*/
|
|
68
|
+
readonly credentials: BrowserStackCredentials;
|
|
69
|
+
/**
|
|
70
|
+
* The WebDriver hub URL. Defaults to {@link DEFAULT_HUB_URL}.
|
|
71
|
+
*/
|
|
72
|
+
readonly hubUrl?: string;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Drives a single BrowserStack session over Selenium/WebDriver: it builds a
|
|
76
|
+
* real session, navigates it to the in-browser test URL, keeps it alive while
|
|
77
|
+
* the specs run, and quits it when done. The in-browser Jasmine suite reports
|
|
78
|
+
* progress/pass-fail out of band via the reporter REST API, exactly as with the
|
|
79
|
+
* legacy worker transport.
|
|
80
|
+
*/
|
|
81
|
+
export declare class WebDriverWorker {
|
|
82
|
+
private readonly deps;
|
|
83
|
+
/**
|
|
84
|
+
* Set to observe state updates.
|
|
85
|
+
*/
|
|
86
|
+
onUpdate: () => void;
|
|
87
|
+
private readonly _state;
|
|
88
|
+
private tasks;
|
|
89
|
+
private driver;
|
|
90
|
+
private pollIntervalId;
|
|
91
|
+
private captureTimeoutId;
|
|
92
|
+
private maxDurationTimeoutId;
|
|
93
|
+
private options;
|
|
94
|
+
/**
|
|
95
|
+
* The worker url with an appended reportApi query param.
|
|
96
|
+
*/
|
|
97
|
+
private readonly url;
|
|
98
|
+
constructor(deps: WebDriverWorkerDeps, workerOptions: WorkerOptions, options?: BrowserStackWorkerOptions);
|
|
99
|
+
/**
|
|
100
|
+
* True once the session status indicates completion or an error occurred.
|
|
101
|
+
* The reporter's `done` stamps the passed/failed status via the Automate API.
|
|
102
|
+
*/
|
|
103
|
+
get completed(): boolean;
|
|
104
|
+
get state(): ReadonlyWebDriverWorkerState;
|
|
105
|
+
get name(): string | undefined;
|
|
106
|
+
get passed(): boolean;
|
|
107
|
+
set totalCompleted(value: number);
|
|
108
|
+
set totalSpecs(value: number);
|
|
109
|
+
done(passed: boolean, reason: string): Promise<void>;
|
|
110
|
+
private refreshSession;
|
|
111
|
+
/**
|
|
112
|
+
* Sends a no-op WebDriver command to reset the session idle timer. A
|
|
113
|
+
* WebDriver session is killed by `idleTimeout` if no commands are sent, but
|
|
114
|
+
* the specs run in-browser with zero driver interaction. REST status polling
|
|
115
|
+
* does NOT reset the idle timer, so this keep-alive is required.
|
|
116
|
+
*/
|
|
117
|
+
private keepAlive;
|
|
118
|
+
appendLogs(logs: readonly LogEntry[]): void;
|
|
119
|
+
/**
|
|
120
|
+
* Builds the WebDriver session and navigates it to the test URL.
|
|
121
|
+
*/
|
|
122
|
+
start(): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Quits the WebDriver session. The session may be created again.
|
|
125
|
+
*/
|
|
126
|
+
terminate(): Promise<void>;
|
|
127
|
+
private clearTimers;
|
|
128
|
+
/**
|
|
129
|
+
* Terminates the session, then starts a fresh one.
|
|
130
|
+
*/
|
|
131
|
+
restart(): Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Invoked when the capture timeout is reached; retries (or falls back) if no
|
|
134
|
+
* test progress has been received yet.
|
|
135
|
+
*/
|
|
136
|
+
private captureTimeoutHandler;
|
|
137
|
+
/**
|
|
138
|
+
* If a fallback browser is configured, swaps to it (keeping the rest of the
|
|
139
|
+
* worker options), resets the retry budget, and restarts. Returns true if a
|
|
140
|
+
* fallback was available.
|
|
141
|
+
*/
|
|
142
|
+
private tryFallbackBrowser;
|
|
143
|
+
/**
|
|
144
|
+
* Sets the worker to a failed state and terminates.
|
|
145
|
+
*/
|
|
146
|
+
private setError;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=WebDriverWorker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebDriverWorker.d.ts","sourceRoot":"","sources":["../src/WebDriverWorker.ts"],"names":[],"mappings":"AAKA,OAAO,EACH,KAAK,0BAA0B,EAC/B,KAAK,uBAAuB,EAC5B,KAAK,yBAAyB,EAC9B,KAAK,QAAQ,EAEb,KAAK,cAAc,EAEnB,KAAK,aAAa,EACrB,MAAM,2BAA2B,CAAA;AAQlC;;GAEG;AACH,eAAO,MAAM,eAAe,8CAA8C,CAAA;AAE1E;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IAExB;;;;OAIG;IACH,aAAa,EAAE,aAAa,CAAA;IAE5B;;OAEG;IACH,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IAEnB;;OAEG;IACH,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;IAE9B;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAA;IAEhB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAA;IAEf;;;;OAIG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IAExB;;;OAGG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IAEzB;;OAEG;IACH,cAAc,EAAE,MAAM,CAAA;IAEtB;;;OAGG;IACH,iBAAiB,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC,oBAAoB,CAAC,CAAA;AAEzE,MAAM,WAAW,mBAAmB;IAChC;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAA;IAE3C;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,uBAAuB,CAAA;IAE7C;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;GAMG;AACH,qBAAa,eAAe;IAoBpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAnBzB;;OAEG;IACI,QAAQ,EAAE,MAAM,IAAI,CAAW;IAEtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,OAAO,CAAqC;IAEpD;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;gBAGP,IAAI,EAAE,mBAAmB,EAC1C,aAAa,EAAE,aAAa,EAC5B,OAAO,CAAC,EAAE,yBAAyB;IAoCvC;;;OAGG;IACH,IAAI,SAAS,IAAI,OAAO,CASvB;IAED,IAAI,KAAK,IAAI,4BAA4B,CAExC;IAED,IAAI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,cAAc,CAAC,KAAK,EAAE,MAAM,EAE/B;IAED,IAAI,UAAU,CAAC,KAAK,EAAE,MAAM,EAE3B;IAED,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAyBtC,cAAc;IAkB5B;;;;;OAKG;YACW,SAAS;IAQvB,UAAU,CAAC,IAAI,EAAE,SAAS,QAAQ,EAAE;IAgBpC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA4D5B;;OAEG;IACH,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB1B,OAAO,CAAC,WAAW;IAenB;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAO9B;;;OAGG;IACH,OAAO,CAAC,qBAAqB,CAkB5B;IAED;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAiC1B;;OAEG;IACH,OAAO,CAAC,QAAQ;CAyBnB"}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { logger, TaskQueue, } from '@amazon/vinyl-build-utils';
|
|
6
|
+
import { Builder } from 'selenium-webdriver';
|
|
7
|
+
import { buildCapabilities } from './capabilities';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import crypto from 'node:crypto';
|
|
11
|
+
/**
|
|
12
|
+
* The default BrowserStack WebDriver hub.
|
|
13
|
+
*/
|
|
14
|
+
export const DEFAULT_HUB_URL = 'https://hub-cloud.browserstack.com/wd/hub';
|
|
15
|
+
/**
|
|
16
|
+
* Drives a single BrowserStack session over Selenium/WebDriver: it builds a
|
|
17
|
+
* real session, navigates it to the in-browser test URL, keeps it alive while
|
|
18
|
+
* the specs run, and quits it when done. The in-browser Jasmine suite reports
|
|
19
|
+
* progress/pass-fail out of band via the reporter REST API, exactly as with the
|
|
20
|
+
* legacy worker transport.
|
|
21
|
+
*/
|
|
22
|
+
export class WebDriverWorker {
|
|
23
|
+
deps;
|
|
24
|
+
/**
|
|
25
|
+
* Set to observe state updates.
|
|
26
|
+
*/
|
|
27
|
+
onUpdate = () => { };
|
|
28
|
+
_state;
|
|
29
|
+
tasks = new TaskQueue();
|
|
30
|
+
driver = null;
|
|
31
|
+
pollIntervalId = null;
|
|
32
|
+
captureTimeoutId = null;
|
|
33
|
+
maxDurationTimeoutId = null;
|
|
34
|
+
options;
|
|
35
|
+
/**
|
|
36
|
+
* The worker url with an appended reportApi query param.
|
|
37
|
+
*/
|
|
38
|
+
url;
|
|
39
|
+
constructor(deps, workerOptions, options) {
|
|
40
|
+
this.deps = deps;
|
|
41
|
+
this.tasks.onError = (error) => this.setError(error);
|
|
42
|
+
this.options = {
|
|
43
|
+
retries: 2,
|
|
44
|
+
captureTimeout: 45,
|
|
45
|
+
pollInterval: 5,
|
|
46
|
+
logsDir: './bStackLogs',
|
|
47
|
+
queryParams: null,
|
|
48
|
+
...options,
|
|
49
|
+
};
|
|
50
|
+
this._state = {
|
|
51
|
+
localId: crypto.randomUUID(),
|
|
52
|
+
workerOptions,
|
|
53
|
+
error: null,
|
|
54
|
+
session: null,
|
|
55
|
+
started: false,
|
|
56
|
+
active: false,
|
|
57
|
+
sessionId: null,
|
|
58
|
+
totalSpecs: null,
|
|
59
|
+
totalCompleted: 0,
|
|
60
|
+
attemptsRemaining: this.options.retries,
|
|
61
|
+
};
|
|
62
|
+
const finalUrl = new URL(workerOptions.url);
|
|
63
|
+
finalUrl.searchParams.set('reportApi', `/report/${this._state.localId}`);
|
|
64
|
+
if (options?.queryParams) {
|
|
65
|
+
for (const [name, value] of Object.entries(options.queryParams)) {
|
|
66
|
+
finalUrl.searchParams.set(name, value);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
this.url = finalUrl.toString();
|
|
70
|
+
if (this.options.logsDir)
|
|
71
|
+
fs.mkdirSync(this.options.logsDir, { recursive: true });
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* True once the session status indicates completion or an error occurred.
|
|
75
|
+
* The reporter's `done` stamps the passed/failed status via the Automate API.
|
|
76
|
+
*/
|
|
77
|
+
get completed() {
|
|
78
|
+
const state = this._state;
|
|
79
|
+
const status = state.session?.status;
|
|
80
|
+
return (state.error != null ||
|
|
81
|
+
(state.started &&
|
|
82
|
+
status != null &&
|
|
83
|
+
['timeout', 'passed', 'failed'].includes(status)));
|
|
84
|
+
}
|
|
85
|
+
get state() {
|
|
86
|
+
return this._state;
|
|
87
|
+
}
|
|
88
|
+
get name() {
|
|
89
|
+
return this._state.workerOptions.name;
|
|
90
|
+
}
|
|
91
|
+
get passed() {
|
|
92
|
+
return this._state.session?.status === 'passed';
|
|
93
|
+
}
|
|
94
|
+
set totalCompleted(value) {
|
|
95
|
+
this._state.totalCompleted = value;
|
|
96
|
+
}
|
|
97
|
+
set totalSpecs(value) {
|
|
98
|
+
this._state.totalSpecs = value;
|
|
99
|
+
}
|
|
100
|
+
done(passed, reason) {
|
|
101
|
+
const name = this.name;
|
|
102
|
+
if (passed)
|
|
103
|
+
logger.info(`✅ ${name} passed`);
|
|
104
|
+
else
|
|
105
|
+
logger.info(`❌ ${name} failed\n${reason}\n`);
|
|
106
|
+
const client = this.deps.client;
|
|
107
|
+
return this.tasks
|
|
108
|
+
.enqueue(async () => {
|
|
109
|
+
const { sessionId } = this._state;
|
|
110
|
+
if (!sessionId) {
|
|
111
|
+
logger.debug('done aborting - sessionId is missing');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
logger.debug('done, passed:', passed);
|
|
115
|
+
this._state.session = await client.updateSession(sessionId, {
|
|
116
|
+
status: passed ? 'passed' : 'failed',
|
|
117
|
+
reason,
|
|
118
|
+
});
|
|
119
|
+
this.onUpdate();
|
|
120
|
+
})
|
|
121
|
+
.finally(() => {
|
|
122
|
+
this.terminate().catch(() => { });
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async refreshSession() {
|
|
126
|
+
const state = this._state;
|
|
127
|
+
if (!state.sessionId)
|
|
128
|
+
return;
|
|
129
|
+
const previousStatus = state.session?.status;
|
|
130
|
+
state.session = await this.deps.client.getSession(state.sessionId);
|
|
131
|
+
if (state.session.status === 'timeout' &&
|
|
132
|
+
previousStatus !== 'timeout') {
|
|
133
|
+
const secs = state.workerOptions.timeout;
|
|
134
|
+
logger.error(`⏱️ ${this.name} timed out${secs ? ` after ${secs}s` : ''} — ` +
|
|
135
|
+
`session ended before tests completed; counts as a failure`);
|
|
136
|
+
}
|
|
137
|
+
this.onUpdate();
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Sends a no-op WebDriver command to reset the session idle timer. A
|
|
141
|
+
* WebDriver session is killed by `idleTimeout` if no commands are sent, but
|
|
142
|
+
* the specs run in-browser with zero driver interaction. REST status polling
|
|
143
|
+
* does NOT reset the idle timer, so this keep-alive is required.
|
|
144
|
+
*/
|
|
145
|
+
async keepAlive() {
|
|
146
|
+
try {
|
|
147
|
+
await this.driver?.getTitle();
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
logger.debug(`keep-alive failed for '${this.name}':`, error);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
appendLogs(logs) {
|
|
154
|
+
if (this.options.logsDir == null || logs.length === 0)
|
|
155
|
+
return;
|
|
156
|
+
const logFilePath = path.join(this.options.logsDir, `${this._state.sessionId}.log`);
|
|
157
|
+
fs.appendFile(logFilePath, logs.map((entry) => entry.join(' ')).join('\n') + '\n', (error) => {
|
|
158
|
+
if (error)
|
|
159
|
+
logger.error('Could not write to console log file:', error);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Builds the WebDriver session and navigates it to the test URL.
|
|
164
|
+
*/
|
|
165
|
+
async start() {
|
|
166
|
+
logger.debug(`Starting session '${this.name}'...`);
|
|
167
|
+
const state = this._state;
|
|
168
|
+
state.started = true;
|
|
169
|
+
await this.tasks
|
|
170
|
+
.enqueue(async () => {
|
|
171
|
+
if (state.active)
|
|
172
|
+
return;
|
|
173
|
+
state.active = true;
|
|
174
|
+
this.onUpdate();
|
|
175
|
+
this.pollIntervalId = setInterval(() => {
|
|
176
|
+
void this.keepAlive();
|
|
177
|
+
void this.refreshSession();
|
|
178
|
+
}, this.options.pollInterval * 1000);
|
|
179
|
+
this.captureTimeoutId = setTimeout(this.captureTimeoutHandler, this.options.captureTimeout * 1000);
|
|
180
|
+
// WebDriver has no server-side total-worker timeout (unlike the
|
|
181
|
+
// legacy /5/worker `timeout`), so enforce it locally as a
|
|
182
|
+
// max-duration safety net for runaway sessions.
|
|
183
|
+
const timeout = state.workerOptions.timeout;
|
|
184
|
+
if (timeout) {
|
|
185
|
+
this.maxDurationTimeoutId = setTimeout(() => {
|
|
186
|
+
this.setError(new Error(`Session '${this.name}' exceeded max duration of ${timeout}s.`));
|
|
187
|
+
}, timeout * 1000);
|
|
188
|
+
}
|
|
189
|
+
// fallbackBrowser is a local concern, not a capability.
|
|
190
|
+
const { fallbackBrowser: _fallbackBrowser, ...workerOptions } = state.workerOptions;
|
|
191
|
+
const capabilities = buildCapabilities({ ...workerOptions, url: this.url }, this.deps.credentials);
|
|
192
|
+
const driver = await new Builder()
|
|
193
|
+
.usingServer(this.deps.hubUrl ?? DEFAULT_HUB_URL)
|
|
194
|
+
.withCapabilities(capabilities)
|
|
195
|
+
.build();
|
|
196
|
+
this.driver = driver;
|
|
197
|
+
state.sessionId = (await driver.getSession()).getId();
|
|
198
|
+
logger.info(`Session created for '${this.name}': ${state.sessionId}`);
|
|
199
|
+
await driver.get(this.url);
|
|
200
|
+
await this.refreshSession();
|
|
201
|
+
this.onUpdate();
|
|
202
|
+
})
|
|
203
|
+
.catch((error) => {
|
|
204
|
+
logger.error(`Session start failed '${this.name}'`, error);
|
|
205
|
+
state.started = false;
|
|
206
|
+
this.clearTimers();
|
|
207
|
+
this.onUpdate();
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Quits the WebDriver session. The session may be created again.
|
|
212
|
+
*/
|
|
213
|
+
terminate() {
|
|
214
|
+
logger.debug(`Terminate session '${this.name}'`);
|
|
215
|
+
return this.tasks.enqueue(async () => {
|
|
216
|
+
const state = this._state;
|
|
217
|
+
if (!state.active)
|
|
218
|
+
return;
|
|
219
|
+
state.active = false;
|
|
220
|
+
this.clearTimers();
|
|
221
|
+
const driver = this.driver;
|
|
222
|
+
if (driver) {
|
|
223
|
+
this.driver = null;
|
|
224
|
+
try {
|
|
225
|
+
await driver.quit();
|
|
226
|
+
logger.debug(`Quit session '${this.name}'`);
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
// The session may already be gone (e.g. BrowserStack ended
|
|
230
|
+
// it); a failed quit is not fatal.
|
|
231
|
+
logger.warn('Could not quit WebDriver session:', error);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
logger.debug(`Terminated session '${this.name}'...`);
|
|
235
|
+
this.onUpdate();
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
clearTimers() {
|
|
239
|
+
if (this.pollIntervalId) {
|
|
240
|
+
clearInterval(this.pollIntervalId);
|
|
241
|
+
this.pollIntervalId = null;
|
|
242
|
+
}
|
|
243
|
+
if (this.captureTimeoutId) {
|
|
244
|
+
clearTimeout(this.captureTimeoutId);
|
|
245
|
+
this.captureTimeoutId = null;
|
|
246
|
+
}
|
|
247
|
+
if (this.maxDurationTimeoutId) {
|
|
248
|
+
clearTimeout(this.maxDurationTimeoutId);
|
|
249
|
+
this.maxDurationTimeoutId = null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Terminates the session, then starts a fresh one.
|
|
254
|
+
*/
|
|
255
|
+
async restart() {
|
|
256
|
+
await this.terminate().catch(() => { });
|
|
257
|
+
this._state.sessionId = null;
|
|
258
|
+
this._state.session = null;
|
|
259
|
+
await this.start();
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Invoked when the capture timeout is reached; retries (or falls back) if no
|
|
263
|
+
* test progress has been received yet.
|
|
264
|
+
*/
|
|
265
|
+
captureTimeoutHandler = () => {
|
|
266
|
+
const state = this._state;
|
|
267
|
+
if (state.totalSpecs != null)
|
|
268
|
+
return;
|
|
269
|
+
if (state.attemptsRemaining > 0) {
|
|
270
|
+
state.attemptsRemaining--;
|
|
271
|
+
logger.warn(`No test results received for ${this.name}, retrying. Attempts remaining: ${state.attemptsRemaining}`);
|
|
272
|
+
this.restart().catch((error) => this.setError(error));
|
|
273
|
+
}
|
|
274
|
+
else if (!this.tryFallbackBrowser()) {
|
|
275
|
+
this.setError(new Error(`Did not capture test progress after ${this.options.retries + 1} attempts for '${this.name}'.`));
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
/**
|
|
279
|
+
* If a fallback browser is configured, swaps to it (keeping the rest of the
|
|
280
|
+
* worker options), resets the retry budget, and restarts. Returns true if a
|
|
281
|
+
* fallback was available.
|
|
282
|
+
*/
|
|
283
|
+
tryFallbackBrowser() {
|
|
284
|
+
const state = this._state;
|
|
285
|
+
const { fallbackBrowser } = state.workerOptions;
|
|
286
|
+
if (!fallbackBrowser)
|
|
287
|
+
return false;
|
|
288
|
+
const target = [
|
|
289
|
+
fallbackBrowser.device,
|
|
290
|
+
fallbackBrowser.os,
|
|
291
|
+
fallbackBrowser.os_version,
|
|
292
|
+
fallbackBrowser.browser,
|
|
293
|
+
fallbackBrowser.browser_version,
|
|
294
|
+
]
|
|
295
|
+
.filter(Boolean)
|
|
296
|
+
.join(' ');
|
|
297
|
+
logger.warn(`Did not capture test progress for '${this.name}'; falling back to '${target}'.`);
|
|
298
|
+
const { fallbackBrowser: _fallbackBrowser, browser: _browser, browser_version: _browserVersion, device: _device, ...rest } = state.workerOptions;
|
|
299
|
+
state.workerOptions = {
|
|
300
|
+
...rest,
|
|
301
|
+
...fallbackBrowser,
|
|
302
|
+
name: `${target} [fallback]`,
|
|
303
|
+
};
|
|
304
|
+
state.attemptsRemaining = this.options.retries;
|
|
305
|
+
this.restart().catch((error) => this.setError(error));
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Sets the worker to a failed state and terminates.
|
|
310
|
+
*/
|
|
311
|
+
setError(error) {
|
|
312
|
+
const state = this._state;
|
|
313
|
+
if (state.error)
|
|
314
|
+
return;
|
|
315
|
+
state.error = error;
|
|
316
|
+
logger.error(error == null
|
|
317
|
+
? 'An internal error has occurred.'
|
|
318
|
+
: (error.message ?? String(error)));
|
|
319
|
+
this.tasks
|
|
320
|
+
.enqueue(async () => {
|
|
321
|
+
if (state.sessionId) {
|
|
322
|
+
state.session = await this.deps.client.updateSession(state.sessionId, {
|
|
323
|
+
status: 'failed',
|
|
324
|
+
reason: error?.message ?? 'Internal error',
|
|
325
|
+
});
|
|
326
|
+
this.onUpdate();
|
|
327
|
+
}
|
|
328
|
+
})
|
|
329
|
+
.catch(() => { });
|
|
330
|
+
this.terminate().catch(() => { });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type BrowserStackAutomateClient, type BrowserStackCredentials, type BrowserStackWorkerControllerOptions, type DoneRequest, type ProgressRequest, type ReportingListener, type StartedRequest, type WorkerOptions } from '@amazon/vinyl-build-utils';
|
|
2
|
+
import { type ReadonlyWebDriverWorkerState } from './WebDriverWorker';
|
|
3
|
+
export interface WebDriverWorkerControllerDeps {
|
|
4
|
+
/**
|
|
5
|
+
* The BrowserStack Automate REST client.
|
|
6
|
+
*/
|
|
7
|
+
readonly client: BrowserStackAutomateClient;
|
|
8
|
+
/**
|
|
9
|
+
* Credentials, forwarded to each worker's session capabilities.
|
|
10
|
+
*/
|
|
11
|
+
readonly credentials: BrowserStackCredentials;
|
|
12
|
+
/**
|
|
13
|
+
* The WebDriver hub URL. Defaults per {@link WebDriverWorker}.
|
|
14
|
+
*/
|
|
15
|
+
readonly hubUrl?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface WebDriverWorkerControllerState {
|
|
18
|
+
readonly workers: readonly ReadonlyWebDriverWorkerState[];
|
|
19
|
+
pending: number;
|
|
20
|
+
running: number;
|
|
21
|
+
passed: number;
|
|
22
|
+
failed: number;
|
|
23
|
+
percentComplete: number;
|
|
24
|
+
remaining: number;
|
|
25
|
+
readonly total: number;
|
|
26
|
+
}
|
|
27
|
+
export type ReadonlyWebDriverWorkerControllerState = Readonly<WebDriverWorkerControllerState>;
|
|
28
|
+
/**
|
|
29
|
+
* Schedules and parallelizes {@link WebDriverWorker}s up to the account's
|
|
30
|
+
* available parallel-session budget, and fans the reporter REST callbacks out
|
|
31
|
+
* to the matching worker. Transport-agnostic scheduling logic mirrors the
|
|
32
|
+
* legacy controller; only the worker type differs.
|
|
33
|
+
*/
|
|
34
|
+
export declare class WebDriverWorkerController implements ReportingListener {
|
|
35
|
+
private readonly deps;
|
|
36
|
+
private readonly options?;
|
|
37
|
+
onUpdate: () => void;
|
|
38
|
+
private readonly _state;
|
|
39
|
+
private readonly workers;
|
|
40
|
+
private readonly localIdToWorkerMap;
|
|
41
|
+
private sessionPollIntervalId;
|
|
42
|
+
private startSessionsInFlight;
|
|
43
|
+
private startSessionsRequested;
|
|
44
|
+
constructor(deps: WebDriverWorkerControllerDeps, browsers: readonly WorkerOptions[], options?: BrowserStackWorkerControllerOptions | undefined);
|
|
45
|
+
get state(): ReadonlyWebDriverWorkerControllerState;
|
|
46
|
+
shouldHandleRequest(id: string): boolean;
|
|
47
|
+
onStarted(request: StartedRequest): void;
|
|
48
|
+
onProgress(request: ProgressRequest): void;
|
|
49
|
+
onDone(request: DoneRequest): void;
|
|
50
|
+
private refreshState;
|
|
51
|
+
private updateHandler;
|
|
52
|
+
/**
|
|
53
|
+
* Starts pending workers up to the available parallel-session budget.
|
|
54
|
+
* Reentrancy-safe: coalesces overlapping invocations into a single follow-up.
|
|
55
|
+
*/
|
|
56
|
+
private startSessions;
|
|
57
|
+
private clearOpenSessionPollTimeout;
|
|
58
|
+
get allPassed(): boolean;
|
|
59
|
+
start(): void;
|
|
60
|
+
terminate(): Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=WebDriverWorkerController.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebDriverWorkerController.d.ts","sourceRoot":"","sources":["../src/WebDriverWorkerController.ts"],"names":[],"mappings":"AAKA,OAAO,EACH,KAAK,0BAA0B,EAC/B,KAAK,uBAAuB,EAC5B,KAAK,mCAAmC,EACxC,KAAK,WAAW,EAEhB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,aAAa,EACrB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACH,KAAK,4BAA4B,EAEpC,MAAM,mBAAmB,CAAA;AAE1B,MAAM,WAAW,6BAA6B;IAC1C;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAA;IAE3C;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,uBAAuB,CAAA;IAE7C;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,8BAA8B;IAC3C,QAAQ,CAAC,OAAO,EAAE,SAAS,4BAA4B,EAAE,CAAA;IACzD,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,MAAM,sCAAsC,GAC9C,QAAQ,CAAC,8BAA8B,CAAC,CAAA;AAI5C;;;;;GAKG;AACH,qBAAa,yBAA0B,YAAW,iBAAiB;IAW3D,OAAO,CAAC,QAAQ,CAAC,IAAI;IAErB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IAZtB,QAAQ,EAAE,MAAM,IAAI,CAAW;IAEtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;IACvD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA8B;IACjE,OAAO,CAAC,qBAAqB,CAAuB;IACpD,OAAO,CAAC,qBAAqB,CAA6B;IAC1D,OAAO,CAAC,sBAAsB,CAAQ;gBAGjB,IAAI,EAAE,6BAA6B,EACpD,QAAQ,EAAE,SAAS,aAAa,EAAE,EACjB,OAAO,CAAC,EAAE,mCAAmC,YAAA;IAuBlE,IAAI,KAAK,IAAI,sCAAsC,CAElD;IAED,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxC,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAIxC,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAM1C,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAQlC,OAAO,CAAC,YAAY;IA2BpB,OAAO,CAAC,aAAa,CAMpB;IAED;;;OAGG;IACH,OAAO,CAAC,aAAa;IA2DrB,OAAO,CAAC,2BAA2B;IAOnC,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,KAAK,IAAI,IAAI;IAKP,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAanC"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { logger, } from '@amazon/vinyl-build-utils';
|
|
6
|
+
import { WebDriverWorker, } from './WebDriverWorker';
|
|
7
|
+
/**
|
|
8
|
+
* Schedules and parallelizes {@link WebDriverWorker}s up to the account's
|
|
9
|
+
* available parallel-session budget, and fans the reporter REST callbacks out
|
|
10
|
+
* to the matching worker. Transport-agnostic scheduling logic mirrors the
|
|
11
|
+
* legacy controller; only the worker type differs.
|
|
12
|
+
*/
|
|
13
|
+
export class WebDriverWorkerController {
|
|
14
|
+
deps;
|
|
15
|
+
options;
|
|
16
|
+
onUpdate = () => { };
|
|
17
|
+
_state;
|
|
18
|
+
workers;
|
|
19
|
+
localIdToWorkerMap;
|
|
20
|
+
sessionPollIntervalId = null;
|
|
21
|
+
startSessionsInFlight = null;
|
|
22
|
+
startSessionsRequested = false;
|
|
23
|
+
constructor(deps, browsers, options) {
|
|
24
|
+
this.deps = deps;
|
|
25
|
+
this.options = options;
|
|
26
|
+
this.workers = browsers.map((browser) => new WebDriverWorker(deps, browser, options?.worker));
|
|
27
|
+
this._state = {
|
|
28
|
+
workers: this.workers.map((worker) => worker.state),
|
|
29
|
+
pending: this.workers.length,
|
|
30
|
+
remaining: this.workers.length,
|
|
31
|
+
running: 0,
|
|
32
|
+
failed: 0,
|
|
33
|
+
passed: 0,
|
|
34
|
+
percentComplete: 0,
|
|
35
|
+
total: this.workers.length,
|
|
36
|
+
};
|
|
37
|
+
this.localIdToWorkerMap = new Map(this.workers.map((worker) => [worker.state.localId, worker]));
|
|
38
|
+
for (const worker of this.workers) {
|
|
39
|
+
worker.onUpdate = this.updateHandler;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
get state() {
|
|
43
|
+
return this._state;
|
|
44
|
+
}
|
|
45
|
+
shouldHandleRequest(id) {
|
|
46
|
+
return this.localIdToWorkerMap.has(id);
|
|
47
|
+
}
|
|
48
|
+
onStarted(request) {
|
|
49
|
+
this.localIdToWorkerMap.get(request.id).totalSpecs = request.totalSpecs;
|
|
50
|
+
}
|
|
51
|
+
onProgress(request) {
|
|
52
|
+
const worker = this.localIdToWorkerMap.get(request.id);
|
|
53
|
+
worker.totalCompleted = request.totalCompleted;
|
|
54
|
+
worker.appendLogs(request.logs);
|
|
55
|
+
}
|
|
56
|
+
onDone(request) {
|
|
57
|
+
const worker = this.localIdToWorkerMap.get(request.id);
|
|
58
|
+
void worker.done(request.overallStatus === 'passed', request.reason ?? '');
|
|
59
|
+
}
|
|
60
|
+
refreshState() {
|
|
61
|
+
const state = this._state;
|
|
62
|
+
let running = 0;
|
|
63
|
+
let pending = 0;
|
|
64
|
+
let failed = 0;
|
|
65
|
+
let passed = 0;
|
|
66
|
+
let percentSum = 0;
|
|
67
|
+
for (const worker of this.workers) {
|
|
68
|
+
if (worker.completed) {
|
|
69
|
+
if (worker.passed)
|
|
70
|
+
passed++;
|
|
71
|
+
else
|
|
72
|
+
failed++;
|
|
73
|
+
percentSum++;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
if (worker.state.started)
|
|
77
|
+
running++;
|
|
78
|
+
else
|
|
79
|
+
pending++;
|
|
80
|
+
percentSum +=
|
|
81
|
+
worker.state.totalCompleted / (worker.state.totalSpecs || 1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
state.running = running;
|
|
85
|
+
state.pending = pending;
|
|
86
|
+
state.failed = failed;
|
|
87
|
+
state.passed = passed;
|
|
88
|
+
state.remaining = running + pending;
|
|
89
|
+
state.percentComplete = percentSum / (state.total || 1);
|
|
90
|
+
}
|
|
91
|
+
updateHandler = () => {
|
|
92
|
+
const state = this._state;
|
|
93
|
+
const previousRunning = state.running;
|
|
94
|
+
this.refreshState();
|
|
95
|
+
if (previousRunning !== state.running)
|
|
96
|
+
this.startSessions();
|
|
97
|
+
this.onUpdate();
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Starts pending workers up to the available parallel-session budget.
|
|
101
|
+
* Reentrancy-safe: coalesces overlapping invocations into a single follow-up.
|
|
102
|
+
*/
|
|
103
|
+
startSessions() {
|
|
104
|
+
if (this.startSessionsInFlight) {
|
|
105
|
+
this.startSessionsRequested = true;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const run = (async () => {
|
|
109
|
+
const state = this._state;
|
|
110
|
+
if (state.pending === 0)
|
|
111
|
+
return;
|
|
112
|
+
const planDetails = await this.deps.client.getPlan();
|
|
113
|
+
const remainingOpenSessions = Math.min(this.options?.maxSessions ?? Number.MAX_SAFE_INTEGER, planDetails.parallel_sessions_max_allowed) -
|
|
114
|
+
Math.max(state.running, planDetails.parallel_sessions_running);
|
|
115
|
+
logger.debug('Plan parallel sessions:', `${planDetails.parallel_sessions_running} running, ${planDetails.parallel_sessions_max_allowed} allowed`);
|
|
116
|
+
const workersToStart = this.workers
|
|
117
|
+
.filter((worker) => !worker.state.started)
|
|
118
|
+
.slice(0, remainingOpenSessions);
|
|
119
|
+
if (workersToStart.length) {
|
|
120
|
+
logger.debug(`Starting ${workersToStart.length} sessions...`);
|
|
121
|
+
await Promise.all(workersToStart.map((worker) => worker.start().catch(() => { })));
|
|
122
|
+
}
|
|
123
|
+
this.refreshState();
|
|
124
|
+
if (this._state.pending > 0) {
|
|
125
|
+
logger.debug('No open sessions, checking again in 15s.');
|
|
126
|
+
this.clearOpenSessionPollTimeout();
|
|
127
|
+
this.sessionPollIntervalId = setTimeout(() => this.startSessions(), 15000);
|
|
128
|
+
}
|
|
129
|
+
})()
|
|
130
|
+
.catch((error) => {
|
|
131
|
+
logger.warn('startSessions failed', error);
|
|
132
|
+
this.clearOpenSessionPollTimeout();
|
|
133
|
+
this.sessionPollIntervalId = setTimeout(() => this.startSessions(), 15000);
|
|
134
|
+
})
|
|
135
|
+
.finally(() => {
|
|
136
|
+
this.startSessionsInFlight = null;
|
|
137
|
+
if (this.startSessionsRequested) {
|
|
138
|
+
this.startSessionsRequested = false;
|
|
139
|
+
this.clearOpenSessionPollTimeout();
|
|
140
|
+
this.startSessions();
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
this.startSessionsInFlight = run;
|
|
144
|
+
}
|
|
145
|
+
clearOpenSessionPollTimeout() {
|
|
146
|
+
if (this.sessionPollIntervalId != null) {
|
|
147
|
+
clearTimeout(this.sessionPollIntervalId);
|
|
148
|
+
this.sessionPollIntervalId = null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
get allPassed() {
|
|
152
|
+
return this.workers.every((worker) => worker.passed);
|
|
153
|
+
}
|
|
154
|
+
start() {
|
|
155
|
+
logger.debug('WebDriverWorkerController start');
|
|
156
|
+
this.startSessions();
|
|
157
|
+
}
|
|
158
|
+
async terminate() {
|
|
159
|
+
logger.debug('Terminating workers');
|
|
160
|
+
this.clearOpenSessionPollTimeout();
|
|
161
|
+
await Promise.all(this.workers.map((worker) => worker.terminate().catch(() => {
|
|
162
|
+
logger.warn(`Could not terminate worker: '${worker.state.workerOptions.name}'`);
|
|
163
|
+
}))).finally(() => logger.debug('terminate done'));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { BrowserStackCredentials, WorkerOptions } from '@amazon/vinyl-build-utils';
|
|
2
|
+
/**
|
|
3
|
+
* BrowserStack-specific WebDriver capabilities, nested under `bstack:options`
|
|
4
|
+
* per the W3C protocol.
|
|
5
|
+
* @see https://www.browserstack.com/docs/automate/capabilities
|
|
6
|
+
*/
|
|
7
|
+
export interface BStackOptions {
|
|
8
|
+
os?: string;
|
|
9
|
+
osVersion?: string;
|
|
10
|
+
deviceName?: string;
|
|
11
|
+
realMobile?: boolean;
|
|
12
|
+
sessionName?: string;
|
|
13
|
+
buildName?: string;
|
|
14
|
+
projectName?: string;
|
|
15
|
+
resolution?: string;
|
|
16
|
+
video?: boolean;
|
|
17
|
+
networkLogs?: boolean;
|
|
18
|
+
consoleLogs?: 'disable' | 'errors' | 'warnings' | 'info' | 'verbose';
|
|
19
|
+
local?: boolean;
|
|
20
|
+
userName?: string;
|
|
21
|
+
accessKey?: string;
|
|
22
|
+
seleniumVersion?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* W3C WebDriver capabilities for a BrowserStack session.
|
|
26
|
+
*/
|
|
27
|
+
export interface WebDriverCapabilities {
|
|
28
|
+
browserName: string;
|
|
29
|
+
browserVersion?: string;
|
|
30
|
+
'bstack:options': BStackOptions;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Builds W3C WebDriver capabilities from framework-agnostic {@link WorkerOptions}.
|
|
34
|
+
*
|
|
35
|
+
* Credentials are placed in `bstack:options` (not the hub URL) so they never
|
|
36
|
+
* leak into logged request URLs. `local: true` routes the session through the
|
|
37
|
+
* BrowserStack Local tunnel. `consoleLogs: 'verbose'` populates the dashboard
|
|
38
|
+
* Console Logs tab (Chrome only; harmless elsewhere).
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildCapabilities(workerOptions: WorkerOptions, credentials: BrowserStackCredentials): WebDriverCapabilities;
|
|
41
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACR,uBAAuB,EACvB,aAAa,EAChB,MAAM,2BAA2B,CAAA;AAElC;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAA;IACpE,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,gBAAgB,EAAE,aAAa,CAAA;CAClC;AAUD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC7B,aAAa,EAAE,aAAa,EAC5B,WAAW,EAAE,uBAAuB,GACrC,qBAAqB,CA2CvB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Infers the WebDriver `browserName` for a mobile device from its OS: Android
|
|
7
|
+
* devices run Chrome, iOS devices run Safari.
|
|
8
|
+
*/
|
|
9
|
+
function mobileBrowserName(os) {
|
|
10
|
+
return os.toLowerCase() === 'ios' ? 'safari' : 'chrome';
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Builds W3C WebDriver capabilities from framework-agnostic {@link WorkerOptions}.
|
|
14
|
+
*
|
|
15
|
+
* Credentials are placed in `bstack:options` (not the hub URL) so they never
|
|
16
|
+
* leak into logged request URLs. `local: true` routes the session through the
|
|
17
|
+
* BrowserStack Local tunnel. `consoleLogs: 'verbose'` populates the dashboard
|
|
18
|
+
* Console Logs tab (Chrome only; harmless elsewhere).
|
|
19
|
+
*/
|
|
20
|
+
export function buildCapabilities(workerOptions, credentials) {
|
|
21
|
+
const isMobile = workerOptions.device != null;
|
|
22
|
+
const bstackOptions = {
|
|
23
|
+
osVersion: workerOptions.os_version,
|
|
24
|
+
video: workerOptions.video ?? true,
|
|
25
|
+
networkLogs: workerOptions.networkLogs ?? false,
|
|
26
|
+
consoleLogs: 'verbose',
|
|
27
|
+
local: true,
|
|
28
|
+
userName: credentials.username,
|
|
29
|
+
accessKey: credentials.key,
|
|
30
|
+
// Only set when defined — `exactOptionalPropertyTypes` forbids
|
|
31
|
+
// assigning `undefined` to an optional property.
|
|
32
|
+
...(workerOptions.name != null && { sessionName: workerOptions.name }),
|
|
33
|
+
...(workerOptions.build != null && { buildName: workerOptions.build }),
|
|
34
|
+
...(workerOptions.project != null && {
|
|
35
|
+
projectName: workerOptions.project,
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
if (isMobile) {
|
|
39
|
+
bstackOptions.deviceName = workerOptions.device;
|
|
40
|
+
bstackOptions.realMobile = true;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
bstackOptions.os = workerOptions.os;
|
|
44
|
+
// `resolution` is a desktop-only capability.
|
|
45
|
+
if (workerOptions.resolution)
|
|
46
|
+
bstackOptions.resolution = workerOptions.resolution;
|
|
47
|
+
}
|
|
48
|
+
const browserName = isMobile
|
|
49
|
+
? mobileBrowserName(workerOptions.os)
|
|
50
|
+
: workerOptions.browser;
|
|
51
|
+
const capabilities = {
|
|
52
|
+
browserName,
|
|
53
|
+
'bstack:options': bstackOptions,
|
|
54
|
+
};
|
|
55
|
+
// browserVersion applies to desktop browsers only.
|
|
56
|
+
if (!isMobile && workerOptions.browser_version)
|
|
57
|
+
capabilities.browserVersion = workerOptions.browser_version;
|
|
58
|
+
return capabilities;
|
|
59
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,mBAAmB,CAAA;AACjC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,2BAA2B,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
export * from './capabilities';
|
|
6
|
+
export * from './WebDriverWorker';
|
|
7
|
+
export * from './WebDriverWorkerController';
|
|
8
|
+
export * from './runSeleniumBrowserStack';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type BrowserStackOptions } from '@amazon/vinyl-build-utils';
|
|
2
|
+
/**
|
|
3
|
+
* When the BrowserStack run has completed, provides the final results.
|
|
4
|
+
*/
|
|
5
|
+
export interface BrowserStackResults {
|
|
6
|
+
readonly passed: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Runs an in-browser Jasmine suite across BrowserStack real browsers over
|
|
10
|
+
* Selenium/WebDriver.
|
|
11
|
+
*
|
|
12
|
+
* This will:
|
|
13
|
+
* - Start a local server, including the reporter REST API the in-browser suite
|
|
14
|
+
* posts progress/logs to.
|
|
15
|
+
* - Start the BrowserStack Local tunnel.
|
|
16
|
+
* - Build a real WebDriver session per browser (parallelized to the account's
|
|
17
|
+
* available session budget) and navigate each to the test URL.
|
|
18
|
+
*
|
|
19
|
+
* The transport is WebDriver (`hub-cloud.browserstack.com/wd/hub`); the
|
|
20
|
+
* in-browser reporter contract (per-spec pass/fail, `bStackLogs/<sessionId>.log`)
|
|
21
|
+
* is unchanged.
|
|
22
|
+
*
|
|
23
|
+
* @return the aggregate test results.
|
|
24
|
+
*/
|
|
25
|
+
export declare function runSeleniumBrowserStack(options: BrowserStackOptions): Promise<BrowserStackResults>;
|
|
26
|
+
/**
|
|
27
|
+
* Calls {@link runSeleniumBrowserStack}, exiting the process when settled.
|
|
28
|
+
*/
|
|
29
|
+
export declare function runSeleniumBrowserStackAndExit(options: BrowserStackOptions): void;
|
|
30
|
+
//# sourceMappingURL=runSeleniumBrowserStack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runSeleniumBrowserStack.d.ts","sourceRoot":"","sources":["../src/runSeleniumBrowserStack.ts"],"names":[],"mappings":"AAMA,OAAO,EAGH,KAAK,mBAAmB,EAW3B,MAAM,2BAA2B,CAAA;AAKlC;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CAC3B;AAiBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,uBAAuB,CACzC,OAAO,EAAE,mBAAmB,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CAwG9B;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC1C,OAAO,EAAE,mBAAmB,GAC7B,IAAI,CAaN"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import express from 'express';
|
|
6
|
+
import { ansi, BrowserStackAutomateClient, configureExpress, configureReportingApi, getEnvBrowserStackCredentials, logger, LogLevel, progressBar, startBrowserStackLocal, startExpressServer, } from '@amazon/vinyl-build-utils';
|
|
7
|
+
import { WebDriverWorkerController } from './WebDriverWorkerController';
|
|
8
|
+
import crypto from 'node:crypto';
|
|
9
|
+
/**
|
|
10
|
+
* Creates a session name for the given browser options.
|
|
11
|
+
*/
|
|
12
|
+
function generateNameForBrowser(browser) {
|
|
13
|
+
return [
|
|
14
|
+
browser.device,
|
|
15
|
+
browser.os,
|
|
16
|
+
browser.os_version,
|
|
17
|
+
browser.browser,
|
|
18
|
+
browser.browser_version,
|
|
19
|
+
]
|
|
20
|
+
.filter((str) => str)
|
|
21
|
+
.join(' ');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Runs an in-browser Jasmine suite across BrowserStack real browsers over
|
|
25
|
+
* Selenium/WebDriver.
|
|
26
|
+
*
|
|
27
|
+
* This will:
|
|
28
|
+
* - Start a local server, including the reporter REST API the in-browser suite
|
|
29
|
+
* posts progress/logs to.
|
|
30
|
+
* - Start the BrowserStack Local tunnel.
|
|
31
|
+
* - Build a real WebDriver session per browser (parallelized to the account's
|
|
32
|
+
* available session budget) and navigate each to the test URL.
|
|
33
|
+
*
|
|
34
|
+
* The transport is WebDriver (`hub-cloud.browserstack.com/wd/hub`); the
|
|
35
|
+
* in-browser reporter contract (per-spec pass/fail, `bStackLogs/<sessionId>.log`)
|
|
36
|
+
* is unchanged.
|
|
37
|
+
*
|
|
38
|
+
* @return the aggregate test results.
|
|
39
|
+
*/
|
|
40
|
+
export async function runSeleniumBrowserStack(options) {
|
|
41
|
+
if (!options.browsers.length)
|
|
42
|
+
throw new Error('options.browsers is required');
|
|
43
|
+
let done;
|
|
44
|
+
const donePromise = new Promise((r) => (done = r));
|
|
45
|
+
const bsCredentials = (await options.credentials) ?? getEnvBrowserStackCredentials();
|
|
46
|
+
if (!bsCredentials.username || !bsCredentials.key)
|
|
47
|
+
throw new Error('Missing BrowserStack credentials. May be provided via options.credentials, or the environmental ' +
|
|
48
|
+
'variables BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY. ' +
|
|
49
|
+
'Environmental variables may be set via a .env file.');
|
|
50
|
+
const useSticky = options.stickyProgress ?? process.stdout.isTTY;
|
|
51
|
+
logger.level = options.logLevel ?? LogLevel.INFO;
|
|
52
|
+
const localOptions = {
|
|
53
|
+
key: bsCredentials.key,
|
|
54
|
+
logFile: './bStackLogs/local.log',
|
|
55
|
+
...options.local,
|
|
56
|
+
};
|
|
57
|
+
const shutdownLocalTunnel = await startBrowserStackLocal(localOptions);
|
|
58
|
+
const app = express();
|
|
59
|
+
configureExpress(app, options.server);
|
|
60
|
+
const serverHandle = await startExpressServer({ app }, options.server);
|
|
61
|
+
const client = new BrowserStackAutomateClient(bsCredentials);
|
|
62
|
+
const build = `build-${crypto.randomUUID()}`;
|
|
63
|
+
const workerOptions = options.browsers.map((browser) => {
|
|
64
|
+
const name = browser.name ?? generateNameForBrowser(browser);
|
|
65
|
+
let url = browser.url ??
|
|
66
|
+
options.workerCommon?.url ??
|
|
67
|
+
serverHandle.https?.url ??
|
|
68
|
+
serverHandle.http.url;
|
|
69
|
+
// Safari and iOS do not route `localhost` through the BrowserStack Local
|
|
70
|
+
// tunnel; they must reach the test server via `bs-local.com`.
|
|
71
|
+
if (browser.browser === 'safari' || browser.os === 'ios') {
|
|
72
|
+
url = url.replace('//localhost', '//bs-local.com');
|
|
73
|
+
}
|
|
74
|
+
return { build, name, ...options.workerCommon, ...browser, url };
|
|
75
|
+
});
|
|
76
|
+
const workerController = new WebDriverWorkerController({ client, credentials: bsCredentials }, workerOptions, options);
|
|
77
|
+
configureReportingApi(app, workerController);
|
|
78
|
+
const state = workerController.state;
|
|
79
|
+
function createProgressMessage() {
|
|
80
|
+
let failedStr = `failed: ${state.failed}`;
|
|
81
|
+
if (state.failed > 0)
|
|
82
|
+
failedStr = ansi.red + failedStr + ansi.resetColor;
|
|
83
|
+
return (`${ansi.green}passed: ${state.passed}${ansi.resetColor}, ${failedStr}, running: ${state.running}, pending: ${state.pending}\n` +
|
|
84
|
+
progressBar(state.percentComplete));
|
|
85
|
+
}
|
|
86
|
+
let previousDecile = 0;
|
|
87
|
+
workerController.onUpdate = () => {
|
|
88
|
+
if (disposed)
|
|
89
|
+
return;
|
|
90
|
+
const progressStr = createProgressMessage();
|
|
91
|
+
if (useSticky)
|
|
92
|
+
logger.sticky(progressStr);
|
|
93
|
+
else {
|
|
94
|
+
const decile = Math.trunc(state.percentComplete * 10);
|
|
95
|
+
if (decile !== previousDecile) {
|
|
96
|
+
logger.info(progressStr);
|
|
97
|
+
previousDecile = decile;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (options.stopOnFirstFailure && state.failed)
|
|
101
|
+
void shutdown();
|
|
102
|
+
if (!state.remaining)
|
|
103
|
+
void shutdown();
|
|
104
|
+
};
|
|
105
|
+
const sigIntHandler = () => {
|
|
106
|
+
logger.info('SIGINT signal received.');
|
|
107
|
+
void shutdown().then(() => process.exit(1));
|
|
108
|
+
};
|
|
109
|
+
process.once('SIGINT', sigIntHandler);
|
|
110
|
+
let disposed = false;
|
|
111
|
+
async function shutdown() {
|
|
112
|
+
if (disposed)
|
|
113
|
+
return;
|
|
114
|
+
disposed = true;
|
|
115
|
+
logger.clearSticky();
|
|
116
|
+
logger.info(createProgressMessage());
|
|
117
|
+
logger.debug('Shutting down');
|
|
118
|
+
process.removeListener('SIGINT', sigIntHandler);
|
|
119
|
+
await workerController.terminate();
|
|
120
|
+
await serverHandle.close();
|
|
121
|
+
await shutdownLocalTunnel();
|
|
122
|
+
const passed = workerController.allPassed;
|
|
123
|
+
if (passed)
|
|
124
|
+
logger.info('✅ All tests passed');
|
|
125
|
+
done({ passed });
|
|
126
|
+
}
|
|
127
|
+
workerController.start();
|
|
128
|
+
return await donePromise;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Calls {@link runSeleniumBrowserStack}, exiting the process when settled.
|
|
132
|
+
*/
|
|
133
|
+
export function runSeleniumBrowserStackAndExit(options) {
|
|
134
|
+
runSeleniumBrowserStack(options)
|
|
135
|
+
.then((result) => {
|
|
136
|
+
if (!result.passed)
|
|
137
|
+
process.exit(1);
|
|
138
|
+
})
|
|
139
|
+
.catch((error) => {
|
|
140
|
+
console.error(ansi.red +
|
|
141
|
+
String('message' in error ? error.message : error) +
|
|
142
|
+
ansi.resetColor);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"fileNames":["../../../node_modules/typescript/lib/lib.es5.d.ts","../../../node_modules/typescript/lib/lib.es2015.d.ts","../../../node_modules/typescript/lib/lib.es2016.d.ts","../../../node_modules/typescript/lib/lib.es2017.d.ts","../../../node_modules/typescript/lib/lib.es2018.d.ts","../../../node_modules/typescript/lib/lib.es2019.d.ts","../../../node_modules/typescript/lib/lib.es2020.d.ts","../../../node_modules/typescript/lib/lib.es2021.d.ts","../../../node_modules/typescript/lib/lib.es2022.d.ts","../../../node_modules/typescript/lib/lib.es2023.d.ts","../../../node_modules/typescript/lib/lib.es2024.d.ts","../../../node_modules/typescript/lib/lib.dom.d.ts","../../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../../node_modules/typescript/lib/lib.es2023.array.d.ts","../../../node_modules/typescript/lib/lib.es2023.collection.d.ts","../../../node_modules/typescript/lib/lib.es2023.intl.d.ts","../../../node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../../../node_modules/typescript/lib/lib.es2024.collection.d.ts","../../../node_modules/typescript/lib/lib.es2024.object.d.ts","../../../node_modules/typescript/lib/lib.es2024.promise.d.ts","../../../node_modules/typescript/lib/lib.es2024.regexp.d.ts","../../../node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2024.string.d.ts","../../../node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../../node_modules/typescript/lib/lib.esnext.float16.d.ts","../../../node_modules/typescript/lib/lib.decorators.d.ts","../../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../../node_modules/@babel/types/lib/index.d.ts","../../../node_modules/@types/babel__generator/index.d.ts","../../../node_modules/@babel/parser/typings/babel-parser.d.ts","../../../node_modules/@types/babel__template/index.d.ts","../../../node_modules/@types/babel__traverse/index.d.ts","../../../node_modules/@types/babel__core/index.d.ts","../../vinyl-build-utils/dist/babel/compattransform.d.ts","../../../node_modules/browserstack-local/index.d.ts","../../../node_modules/@types/node/compatibility/iterators.d.ts","../../../node_modules/@types/node/globals.typedarray.d.ts","../../../node_modules/@types/node/buffer.buffer.d.ts","../../../node_modules/@types/node/globals.d.ts","../../../node_modules/@types/node/web-globals/abortcontroller.d.ts","../../../node_modules/@types/node/web-globals/crypto.d.ts","../../../node_modules/@types/node/web-globals/domexception.d.ts","../../../node_modules/@types/node/web-globals/events.d.ts","../../../node_modules/buffer/index.d.ts","../../../node_modules/undici-types/utility.d.ts","../../../node_modules/undici-types/header.d.ts","../../../node_modules/undici-types/readable.d.ts","../../../node_modules/undici-types/fetch.d.ts","../../../node_modules/undici-types/formdata.d.ts","../../../node_modules/undici-types/connector.d.ts","../../../node_modules/undici-types/client-stats.d.ts","../../../node_modules/undici-types/client.d.ts","../../../node_modules/undici-types/errors.d.ts","../../../node_modules/undici-types/dispatcher.d.ts","../../../node_modules/undici-types/global-dispatcher.d.ts","../../../node_modules/undici-types/global-origin.d.ts","../../../node_modules/undici-types/pool-stats.d.ts","../../../node_modules/undici-types/pool.d.ts","../../../node_modules/undici-types/handlers.d.ts","../../../node_modules/undici-types/balanced-pool.d.ts","../../../node_modules/undici-types/h2c-client.d.ts","../../../node_modules/undici-types/agent.d.ts","../../../node_modules/undici-types/mock-interceptor.d.ts","../../../node_modules/undici-types/mock-call-history.d.ts","../../../node_modules/undici-types/mock-agent.d.ts","../../../node_modules/undici-types/mock-client.d.ts","../../../node_modules/undici-types/mock-pool.d.ts","../../../node_modules/undici-types/snapshot-agent.d.ts","../../../node_modules/undici-types/mock-errors.d.ts","../../../node_modules/undici-types/proxy-agent.d.ts","../../../node_modules/undici-types/env-http-proxy-agent.d.ts","../../../node_modules/undici-types/retry-handler.d.ts","../../../node_modules/undici-types/retry-agent.d.ts","../../../node_modules/undici-types/api.d.ts","../../../node_modules/undici-types/cache-interceptor.d.ts","../../../node_modules/undici-types/interceptors.d.ts","../../../node_modules/undici-types/util.d.ts","../../../node_modules/undici-types/cookies.d.ts","../../../node_modules/undici-types/patch.d.ts","../../../node_modules/undici-types/websocket.d.ts","../../../node_modules/undici-types/eventsource.d.ts","../../../node_modules/undici-types/diagnostics-channel.d.ts","../../../node_modules/undici-types/content-type.d.ts","../../../node_modules/undici-types/cache.d.ts","../../../node_modules/undici-types/index.d.ts","../../../node_modules/@types/node/web-globals/fetch.d.ts","../../../node_modules/@types/node/web-globals/navigator.d.ts","../../../node_modules/@types/node/web-globals/storage.d.ts","../../../node_modules/@types/node/web-globals/streams.d.ts","../../../node_modules/@types/node/assert.d.ts","../../../node_modules/@types/node/assert/strict.d.ts","../../../node_modules/@types/node/async_hooks.d.ts","../../../node_modules/@types/node/buffer.d.ts","../../../node_modules/@types/node/child_process.d.ts","../../../node_modules/@types/node/cluster.d.ts","../../../node_modules/@types/node/console.d.ts","../../../node_modules/@types/node/constants.d.ts","../../../node_modules/@types/node/crypto.d.ts","../../../node_modules/@types/node/dgram.d.ts","../../../node_modules/@types/node/diagnostics_channel.d.ts","../../../node_modules/@types/node/dns.d.ts","../../../node_modules/@types/node/dns/promises.d.ts","../../../node_modules/@types/node/domain.d.ts","../../../node_modules/@types/node/events.d.ts","../../../node_modules/@types/node/fs.d.ts","../../../node_modules/@types/node/fs/promises.d.ts","../../../node_modules/@types/node/http.d.ts","../../../node_modules/@types/node/http2.d.ts","../../../node_modules/@types/node/https.d.ts","../../../node_modules/@types/node/inspector.d.ts","../../../node_modules/@types/node/inspector.generated.d.ts","../../../node_modules/@types/node/module.d.ts","../../../node_modules/@types/node/net.d.ts","../../../node_modules/@types/node/os.d.ts","../../../node_modules/@types/node/path.d.ts","../../../node_modules/@types/node/perf_hooks.d.ts","../../../node_modules/@types/node/process.d.ts","../../../node_modules/@types/node/punycode.d.ts","../../../node_modules/@types/node/querystring.d.ts","../../../node_modules/@types/node/readline.d.ts","../../../node_modules/@types/node/readline/promises.d.ts","../../../node_modules/@types/node/repl.d.ts","../../../node_modules/@types/node/sea.d.ts","../../../node_modules/@types/node/sqlite.d.ts","../../../node_modules/@types/node/stream.d.ts","../../../node_modules/@types/node/stream/promises.d.ts","../../../node_modules/@types/node/stream/consumers.d.ts","../../../node_modules/@types/node/stream/web.d.ts","../../../node_modules/@types/node/string_decoder.d.ts","../../../node_modules/@types/node/test.d.ts","../../../node_modules/@types/node/timers.d.ts","../../../node_modules/@types/node/timers/promises.d.ts","../../../node_modules/@types/node/tls.d.ts","../../../node_modules/@types/node/trace_events.d.ts","../../../node_modules/@types/node/tty.d.ts","../../../node_modules/@types/node/url.d.ts","../../../node_modules/@types/node/util.d.ts","../../../node_modules/@types/node/v8.d.ts","../../../node_modules/@types/node/vm.d.ts","../../../node_modules/@types/node/wasi.d.ts","../../../node_modules/@types/node/worker_threads.d.ts","../../../node_modules/@types/node/zlib.d.ts","../../../node_modules/@types/node/index.d.ts","../../../node_modules/@types/send/index.d.ts","../../../node_modules/@types/qs/index.d.ts","../../../node_modules/@types/range-parser/index.d.ts","../../../node_modules/@types/express-serve-static-core/index.d.ts","../../../node_modules/@types/http-errors/index.d.ts","../../../node_modules/@types/serve-static/index.d.ts","../../../node_modules/@types/connect/index.d.ts","../../../node_modules/@types/body-parser/index.d.ts","../../../node_modules/@types/express/index.d.ts","../../../node_modules/@types/http-proxy/index.d.ts","../../../node_modules/http-proxy-middleware/dist/types.d.ts","../../../node_modules/http-proxy-middleware/dist/factory.d.ts","../../../node_modules/http-proxy-middleware/dist/handlers/response-interceptor.d.ts","../../../node_modules/http-proxy-middleware/dist/handlers/fix-request-body.d.ts","../../../node_modules/http-proxy-middleware/dist/handlers/public.d.ts","../../../node_modules/http-proxy-middleware/dist/handlers/index.d.ts","../../../node_modules/http-proxy-middleware/dist/plugins/default/debug-proxy-errors-plugin.d.ts","../../../node_modules/http-proxy-middleware/dist/plugins/default/error-response-plugin.d.ts","../../../node_modules/http-proxy-middleware/dist/plugins/default/logger-plugin.d.ts","../../../node_modules/http-proxy-middleware/dist/plugins/default/proxy-events.d.ts","../../../node_modules/http-proxy-middleware/dist/plugins/default/index.d.ts","../../../node_modules/http-proxy-middleware/dist/legacy/types.d.ts","../../../node_modules/http-proxy-middleware/dist/legacy/create-proxy-middleware.d.ts","../../../node_modules/http-proxy-middleware/dist/legacy/public.d.ts","../../../node_modules/http-proxy-middleware/dist/legacy/index.d.ts","../../../node_modules/http-proxy-middleware/dist/index.d.ts","../../vinyl-build-utils/dist/express/startexpressserver.d.ts","../../vinyl-build-utils/dist/express/serve.d.ts","../../vinyl-build-utils/dist/util/logger.d.ts","../../vinyl-build-utils/dist/browserstack/browserstackrestapi.d.ts","../../vinyl-build-utils/dist/browserstack/browserstackconfig.d.ts","../../vinyl-build-utils/dist/browserstack/browserstacklocal.d.ts","../../vinyl-build-utils/dist/browserstack/configurereporterapi.d.ts","../../vinyl-build-utils/dist/esbuild/analyzeexports.d.ts","../../../node_modules/esbuild/lib/main.d.ts","../../vinyl-build-utils/dist/esbuild/buildall.d.ts","../../vinyl-build-utils/dist/esbuild/compattransformplugin.d.ts","../../vinyl-build-utils/dist/esbuild/optionalentrypoints.d.ts","../../vinyl-build-utils/dist/esbuild/pureexportsplugin.d.ts","../../../node_modules/typescript/lib/typescript.d.ts","../../vinyl-build-utils/dist/typescript/tsc.d.ts","../../vinyl-build-utils/dist/typescript/buildtypedeclarations.d.ts","../../vinyl-build-utils/dist/esbuild/typedeclarationsplugin.d.ts","../../vinyl-build-utils/dist/typescript/parsetsconfig.d.ts","../../vinyl-build-utils/dist/typescript/replacepathaliases.d.ts","../../vinyl-build-utils/dist/typescript/resolveprojectreferences.d.ts","../../vinyl-build-utils/dist/util/console.d.ts","../../vinyl-build-utils/dist/util/copyfiles.d.ts","../../vinyl-build-utils/dist/util/extractargs.d.ts","../../vinyl-build-utils/dist/util/getrootprojectdir.d.ts","../../vinyl-build-utils/dist/util/prompt.d.ts","../../vinyl-build-utils/dist/util/sleep.d.ts","../../vinyl-build-utils/dist/util/string.d.ts","../../vinyl-build-utils/dist/util/taskqueue.d.ts","../../vinyl-build-utils/dist/util/writeindexfile.d.ts","../../vinyl-build-utils/dist/vinyl/buildvinylpackage.d.ts","../../vinyl-build-utils/dist/vinyl/vinyldefaultbrowserstackoptions.d.ts","../../vinyl-build-utils/dist/vinyl/vinyldefaulttscoptions.d.ts","../../vinyl-build-utils/dist/vinyl/vinylsupportedbrowsers.d.ts","../../vinyl-build-utils/dist/index.d.ts","../../../node_modules/@types/selenium-webdriver/bidi/filterby.d.ts","../../../node_modules/@types/selenium-webdriver/bidi/remotevalue.d.ts","../../../node_modules/@types/selenium-webdriver/bidi/scripttypes.d.ts","../../../node_modules/@types/selenium-webdriver/bidi/logentries.d.ts","../../../node_modules/@types/selenium-webdriver/bidi/loginspector.d.ts","../../../node_modules/@types/selenium-webdriver/bidi/browsingcontexttypes.d.ts","../../../node_modules/@types/selenium-webdriver/bidi/cliprectangle.d.ts","../../../node_modules/@types/selenium-webdriver/bidi/capturescreenshotparameters.d.ts","../../../node_modules/@types/selenium-webdriver/bidi/createcontextparameters.d.ts","../../../node_modules/@types/selenium-webdriver/bidi/protocolvalue.d.ts","../../../node_modules/@types/selenium-webdriver/bidi/browsingcontext.d.ts","../../../node_modules/@types/selenium-webdriver/http.d.ts","../../../node_modules/@types/selenium-webdriver/remote.d.ts","../../../node_modules/@types/selenium-webdriver/chrome.d.ts","../../../node_modules/@types/selenium-webdriver/edge.d.ts","../../../node_modules/@types/selenium-webdriver/lib/symbols.d.ts","../../../node_modules/@types/selenium-webdriver/firefox.d.ts","../../../node_modules/@types/selenium-webdriver/ie.d.ts","../../../node_modules/@types/selenium-webdriver/lib/by.d.ts","../../../node_modules/@types/selenium-webdriver/lib/capabilities.d.ts","../../../node_modules/@types/selenium-webdriver/lib/command.d.ts","../../../node_modules/@types/selenium-webdriver/lib/input.d.ts","../../../node_modules/@types/selenium-webdriver/lib/logging.d.ts","../../../node_modules/@types/selenium-webdriver/lib/until.d.ts","../../../node_modules/@types/ws/index.d.mts","../../../node_modules/@types/selenium-webdriver/bidi/index.d.ts","../../../node_modules/@types/selenium-webdriver/devtools/networkinterceptor.d.ts","../../../node_modules/@types/selenium-webdriver/lib/webdriver.d.ts","../../../node_modules/@types/selenium-webdriver/safari.d.ts","../../../node_modules/@types/selenium-webdriver/chromium.d.ts","../../../node_modules/@types/selenium-webdriver/lib/promise.d.ts","../../../node_modules/@types/selenium-webdriver/index.d.ts","../src/capabilities.ts","../src/webdriverworker.ts","../src/webdriverworkercontroller.ts","../src/runseleniumbrowserstack.ts","../src/index.ts"],"fileIdsList":[[74,84,139,156,157],[84,139,156,157],[74,75,76,77,78,84,139,156,157],[74,76,84,139,156,157],[84,139,153,156,157,189,196],[84,139,153,156,157,189],[84,139,150,153,156,157,189,190,191,192],[84,139,156,157,193,195,197],[84,139,150,153,155,156,157,159,171,182,189],[84,136,137,139,156,157],[84,138,139,156,157],[139,156,157],[84,139,144,156,157,174],[84,139,140,145,150,156,157,159,171,182],[84,139,140,141,150,156,157,159],[84,139,142,156,157,183],[84,139,143,144,151,156,157,160],[84,139,144,156,157,171,179],[84,139,145,147,150,156,157,159],[84,138,139,146,156,157],[84,139,147,148,156,157],[84,139,149,150,156,157],[84,138,139,150,156,157],[84,139,150,151,152,156,157,171,182],[84,139,150,151,152,156,157,166,171,174],[84,131,139,147,150,153,156,157,159,171,182],[84,139,150,151,153,154,156,157,159,171,179,182],[84,139,153,155,156,157,171,179,182],[82,83,84,85,86,87,88,89,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188],[84,139,150,156,157],[84,139,156,157,158,182],[84,139,147,150,156,157,159,171],[84,139,156,157,160],[84,139,156,157,161],[84,138,139,156,157,162],[84,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188],[84,139,156,157,164],[84,139,156,157,165],[84,139,150,156,157,166,167],[84,139,156,157,166,168,183,185],[84,139,151,156,157],[84,139,150,156,157,171,172,174],[84,139,156,157,173,174],[84,139,156,157,171,172],[84,139,156,157,174],[84,139,156,157,175],[84,136,139,156,157,171,176,182],[84,139,150,156,157,177,178],[84,139,156,157,177,178],[84,139,144,156,157,159,171,179],[84,139,156,157,180],[84,139,156,157,159,181],[84,139,153,156,157,165,182],[84,139,144,156,157,183],[84,139,156,157,171,184],[84,139,156,157,158,185],[84,139,156,157,186],[84,139,144,156,157],[84,131,139,156,157],[84,139,156,157,187],[84,131,139,150,152,156,157,162,171,174,182,184,185,187],[84,139,156,157,171,188],[84,139,156,157,255,257,258,259,281],[84,139,156,157,256],[84,139,150,156,157,189],[84,139,156,157,252],[84,139,156,157,250,253],[84,139,156,157,251],[84,139,156,157,261,262,281],[84,139,156,157,262,281],[84,139,156,157,261,262,265,281],[84,139,156,157,281],[84,139,156,157,254,260,261,263,264,266,267,268,269,271,272,273,277,278,279,280],[84,139,156,157,265,281],[84,139,156,157,270,281],[84,139,156,157,270,271,274,275,276,281],[84,139,151,156,157,171,189],[84,139,153,156,157,189,194],[84,139,150,153,155,156,157,159,171,179,182,188,189],[84,139,153,156,157,200],[84,139,153,156,157],[84,139,156,157,204],[84,139,156,157,202,203],[84,139,156,157,200,201,205,210,214],[84,139,153,156,157,200,211],[84,139,156,157,213],[84,139,156,157,211,212],[84,139,156,157,200],[84,139,156,157,206,207,208,209],[84,139,153,156,157,159,199],[84,97,100,103,104,139,156,157,182],[84,100,139,156,157,171,182],[84,100,104,139,156,157,182],[84,139,156,157,171],[84,94,139,156,157],[84,98,139,156,157],[84,96,97,100,139,156,157,182],[84,139,156,157,159,179],[84,139,156,157,189],[84,94,139,156,157,189],[84,96,100,139,156,157,159,182],[84,91,92,93,95,99,139,150,156,157,171,182],[84,100,108,116,139,156,157],[84,92,98,139,156,157],[84,100,125,126,139,156,157],[84,92,95,100,139,156,157,174,182,189],[84,100,139,156,157],[84,96,100,139,156,157,182],[84,91,139,156,157],[84,94,95,96,98,99,100,101,102,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,126,127,128,129,130,139,156,157],[84,100,118,121,139,147,156,157],[84,100,108,109,110,139,156,157],[84,98,100,109,111,139,156,157],[84,99,139,156,157],[84,92,94,100,139,156,157],[84,100,104,109,111,139,156,157],[84,104,139,156,157],[84,98,100,103,139,156,157,182],[84,92,96,100,108,139,156,157],[84,100,118,139,156,157],[84,111,139,156,157],[84,94,100,125,139,156,157,174,187,189],[84,139,156,157,249],[84,139,156,157,282,283,284,285],[81,84,139,144,156,157,198,249,284],[84,139,144,151,156,157,161,249,281,282],[84,139,156,157,249,283],[79,84,139,156,157],[81,84,139,156,157,217,218,219],[81,84,139,156,157],[84,139,156,157,198],[84,139,156,157,224],[84,139,156,157,224,231],[84,139,156,157,198,215,216],[84,139,156,157,179,198],[80,84,139,156,157,216,217,218,219,220,221,222,223,225,226,227,228,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248],[84,139,156,157,230],[84,139,156,157,229],[84,139,156,157,233],[84,139,156,157,217,224],[84,139,156,157,231],[84,139,156,157,220]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"556ccd493ec36c7d7cb130d51be66e147b91cc1415be383d71da0f1e49f742a9","impliedFormat":1},{"version":"b6d03c9cfe2cf0ba4c673c209fcd7c46c815b2619fd2aad59fc4229aaef2ed43","impliedFormat":1},{"version":"95aba78013d782537cc5e23868e736bec5d377b918990e28ed56110e3ae8b958","impliedFormat":1},{"version":"670a76db379b27c8ff42f1ba927828a22862e2ab0b0908e38b671f0e912cc5ed","impliedFormat":1},{"version":"13b77ab19ef7aadd86a1e54f2f08ea23a6d74e102909e3c00d31f231ed040f62","impliedFormat":1},{"version":"069bebfee29864e3955378107e243508b163e77ab10de6a5ee03ae06939f0bb9","impliedFormat":1},"4ac27de8e092863e77dd783e0e1221fd6037786a8729292526fb042cc4bbe7f5",{"version":"e83e467ebe549ca06658e4299b7024ee01ba52edf8faf5c70ed830a98a8ea3f3","impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"378281aa35786c27d5811af7e6bcaa492eebd0c7013d48137c35bbc69a2b9751","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"1b2dd1cbeb0cc6ae20795958ba5950395ebb2849b7c8326853dd15530c77ab0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"387a023d363f755eb63450a66c28b14cdd7bc30a104565e2dbf0a8988bb4a56c","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"487b694c3de27ddf4ad107d4007ad304d29effccf9800c8ae23c2093638d906a","impliedFormat":1},{"version":"3a80bc85f38526ca3b08007ee80712e7bb0601df178b23fbf0bf87036fce40ce","impliedFormat":1},{"version":"ccf4552357ce3c159ef75f0f0114e80401702228f1898bdc9402214c9499e8c0","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"2931540c47ee0ff8a62860e61782eb17b155615db61e36986e54645ec67f67c2","impliedFormat":1},{"version":"ccab02f3920fc75c01174c47fcf67882a11daf16baf9e81701d0a94636e94556","impliedFormat":1},{"version":"f6faf5f74e4c4cc309a6c6a6c4da02dbb840be5d3e92905a23dcd7b2b0bd1986","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"33e981bf6376e939f99bd7f89abec757c64897d33c005036b9a10d9587d80187","impliedFormat":1},{"version":"7fd1b31fd35876b0aa650811c25ec2c97a3c6387e5473eb18004bed86cdd76b6","impliedFormat":1},{"version":"b41767d372275c154c7ea6c9d5449d9a741b8ce080f640155cc88ba1763e35b3","impliedFormat":1},{"version":"3bacf516d686d08682751a3bd2519ea3b8041a164bfb4f1d35728993e70a2426","impliedFormat":1},{"version":"7fb266686238369442bd1719bc0d7edd0199da4fb8540354e1ff7f16669b4323","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"5b03a034c72146b61573aab280f295b015b9168470f2df05f6080a2122f9b4df","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"54c3e2371e3d016469ad959697fd257e5621e16296fa67082c2575d0bf8eced0","impliedFormat":1},{"version":"beb8233b2c220cfa0feea31fbe9218d89fa02faa81ef744be8dce5acb89bb1fd","impliedFormat":1},{"version":"c183b931b68ad184bc8e8372bf663f3d33304772fb482f29fb91b3c391031f3e","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"48cc3ec153b50985fb95153258a710782b25975b10dd4ac8a4f3920632d10790","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"e1528ca65ac90f6fa0e4a247eb656b4263c470bb22d9033e466463e13395e599","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"866078923a56d026e39243b4392e282c1c63159723996fa89243140e1388a98d","impliedFormat":1},{"version":"f724236417941ea77ec8d38c6b7021f5fb7f8521c7f8c1538e87661f2c6a0774","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d97fb21da858fb18b8ae72c314e9743fd52f73ebe2764e12af1db32fc03f853f","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ea15fd99b2e34cb25fe8346c955000bb70c8b423ae4377a972ef46bfb37f595","impliedFormat":1},{"version":"7cf69dd5502c41644c9e5106210b5da7144800670cbe861f66726fa209e231c4","impliedFormat":1},{"version":"72c1f5e0a28e473026074817561d1bc9647909cf253c8d56c41d1df8d95b85f7","impliedFormat":1},{"version":"f9b4137a0d285bd77dba2e6e895530112264310ae47e07bf311feae428fb8b61","affectsGlobalScope":true,"impliedFormat":1},{"version":"c06b2652ffeb89afd0f1c52c165ced77032f9cd09bc481153fbd6b5504c69494","impliedFormat":1},{"version":"51aecd2df90a3cffea1eb4696b33b2d78594ea2aa2138e6b9471ec4841c6c2ee","impliedFormat":1},{"version":"9d8f9e63e29a3396285620908e7f14d874d066caea747dc4b2c378f0599166b4","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"612422d5ba6b4a5c4537f423e9199645468ad80a689801da63ab7edb43f7b835","impliedFormat":1},{"version":"db9ada976f9e52e13f7ae8b9a320f4b67b87685938c5879187d8864b2fbe97f3","impliedFormat":1},{"version":"9f39e70a354d0fba29ac3cdf6eca00b7f9e96f64b2b2780c432e8ea27f133743","impliedFormat":1},{"version":"0dace96cc0f7bc6d0ee2044921bdf19fe42d16284dbcc8ae200800d1c9579335","impliedFormat":1},{"version":"a2e2bbde231b65c53c764c12313897ffdfb6c49183dd31823ee2405f2f7b5378","impliedFormat":1},{"version":"ad1cc0ed328f3f708771272021be61ab146b32ecf2b78f3224959ff1e2cd2a5c","impliedFormat":1},{"version":"c64e1888baaa3253ca4405b455e4bf44f76357868a1bd0a52998ade9a092ad78","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc8c6f5322961b56d9906601b20798725df60baeab45ec014fba9f795d5596fd","impliedFormat":1},{"version":"0904660ae854e6d41f6ff25356db1d654436c6305b0f0aa89d1532df0253486e","impliedFormat":1},{"version":"060d305fe4494d8cb2b99d620928d369d1ee55c1645f5e729a2aca07d0f108cb","impliedFormat":1},{"version":"a016b2a26863dd6acb1f18c4ff37363c2b51e27ca7b3374dc6342aff1ddead2d","impliedFormat":1},{"version":"0c50296ee73dae94efc3f0da4936b1146ca6ce2217acfabb44c19c9a33fa30e5","impliedFormat":1},{"version":"bbf42f98a5819f4f06e18c8b669a994afe9a17fe520ae3454a195e6eabf7700d","impliedFormat":1},{"version":"0e5974dfff7a97181c7c376545f126b20acf2f1341db7d3fccea4977bf3ce19c","impliedFormat":1},{"version":"c7f977ea78a1b060a30554c1c4ec0e2269c6e305a349ca2ada14931ac27ecc0b","affectsGlobalScope":true,"impliedFormat":1},{"version":"3806cdd6b48ba01a9198134e62a384ec217a98f316d4baef74dd46d62c947a63","impliedFormat":1},{"version":"ff65b8a8bd380c6d129becc35de02f7c29ad7ce03300331ca91311fb4044d1a9","impliedFormat":1},{"version":"04bf1aa481d1adfb16d93d76e44ce71c51c8ef68039d849926551199489637f6","impliedFormat":1},{"version":"2c9adcc85574b002c9a6311ff2141055769e0071856ec979d92ff989042b1f1b","affectsGlobalScope":true,"impliedFormat":1},{"version":"b8bf3fe89ec8baa335f6370b9fa36308e1bc7a72e2eb2dad1e94f31e27fa28b5","affectsGlobalScope":true,"impliedFormat":1},{"version":"a58a15da4c5ba3df60c910a043281256fa52d36a0fcdef9b9100c646282e88dd","impliedFormat":1},{"version":"b36beffbf8acdc3ebc58c8bb4b75574b31a2169869c70fc03f82895b93950a12","impliedFormat":1},{"version":"de263f0089aefbfd73c89562fb7254a7468b1f33b61839aafc3f035d60766cb4","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"8c81fd4a110490c43d7c578e8c6f69b3af01717189196899a6a44f93daa57a3a","impliedFormat":1},{"version":"5fb39858b2459864b139950a09adae4f38dad87c25bf572ce414f10e4bd7baab","impliedFormat":1},{"version":"35390d6fa94bdb432c5d0bcb6547bdd11406c2692a6b90b9e47be2105ea19bd6","impliedFormat":1},{"version":"3910dab597c40e173bf0e0d419d3ce9682c54ebf6ae84849f9b829b1451a17ec","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"486c074a5c0f2254345c0d1c9540380f5463999e42d7e1a159305ea823d3c4b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"c119835edf36415081dfd9ed15fc0cd37aaa28d232be029ad073f15f3d88c323","impliedFormat":1},{"version":"8e7c3bed5f19ade8f911677ddc83052e2283e25b0a8654cd89db9079d4b323c7","impliedFormat":1},{"version":"9705cd157ffbb91c5cab48bdd2de5a437a372e63f870f8a8472e72ff634d47c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"ae86f30d5d10e4f75ce8dcb6e1bd3a12ecec3d071a21e8f462c5c85c678efb41","impliedFormat":1},{"version":"ccf3afaeebbeee4ca9092101e99fd6abd681116b6e5ec23e381bbb1e1f32262c","impliedFormat":1},{"version":"e03460fe72b259f6d25ad029f085e4bedc3f90477da4401d8fbc1efa9793230e","impliedFormat":1},{"version":"4286a3a6619514fca656089aee160bb6f2e77f4dd53dc5a96b26a0b4fc778055","impliedFormat":1},{"version":"ab7818a9d57a9297b90e456fc68b77f84d74395a9210a3cfa9d87db33aff8b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb08062718a5470cd864c1fae0eb5b3a3adc5bcd05dcf87608d6f60b65eca3f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"3a815b7d1aebc0646b91548eab2fc19dada09ff255d04c71ced00bbd3058c8eb","impliedFormat":1},{"version":"255d948f87f24ffd57bcb2fdf95792fd418a2e1f712a98cf2cce88744d75085c","impliedFormat":1},{"version":"0d5b085f36e6dc55bc6332ecb9c733be3a534958c238fb8d8d18d4a2b6f2a15a","impliedFormat":1},{"version":"836b36913830645ac3b28fe33731aac3fdb3524ee8adbb4cdab9a5c189f41943","affectsGlobalScope":true,"impliedFormat":1},{"version":"bfd3b3c21a56104693183942e221c1896ee23bcb8f8d91ab0b941f7b32985411","impliedFormat":1},{"version":"d7e9ab1b0996639047c61c1e62f85c620e4382206b3abb430d9a21fb7bc23c77","impliedFormat":1},{"version":"d34aa8df2d0b18fb56b1d772ff9b3c7aea7256cf0d692f969be6e1d27b74d660","impliedFormat":1},{"version":"93a3b8e57c68e348fc4054b245bd7cf4893225f56c991028844b693c2fa8c03c","impliedFormat":1},{"version":"2f5747b1508ccf83fad0c251ba1e5da2f5a30b78b09ffa1cfaf633045160afed","impliedFormat":1},{"version":"6823ccc7b5b77bbf898d878dbcad18aa45e0fa96bdd0abd0de98d514845d9ed9","affectsGlobalScope":true,"impliedFormat":1},{"version":"b71c603a539078a5e3a039b20f2b0a0d1708967530cf97dec8850a9ca45baa2b","impliedFormat":1},{"version":"168d88e14e0d81fe170e0dadd38ae9d217476c11435ea640ddb9b7382bdb6c1f","impliedFormat":1},{"version":"104c67f0da1bdf0d94865419247e20eded83ce7f9911a1aa75fc675c077ca66e","impliedFormat":1},{"version":"cc0d0b339f31ce0ab3b7a5b714d8e578ce698f1e13d7f8c60bfb766baeb1d35c","impliedFormat":1},{"version":"8e04cf0688e0d921111659c2b55851957017148fa7b977b02727477d155b3c47","impliedFormat":1},{"version":"26b7d0cd4b41ab557ef9e3bfeec42dcf24252843633e3d29f38d2c0b13aaa528","impliedFormat":1},{"version":"47bd5449f6c874f45d737a54e6aa4d9f723fa97067f46a05d8f7d7b69c5e8ebe","impliedFormat":1},{"version":"7d60217ec2719e0f373e50c0a397f401233ea42a569f8232a2ec17c77769f99a","impliedFormat":1},{"version":"39d59b812869b4f3bee27a44a4f37d7f5bf96c8dabd71a2dbd03c769b36e22c7","impliedFormat":1},{"version":"ddafa5a34d6dc63a55afbc273ff884e64025a4127b68c54016b91cf8d73fa656","impliedFormat":1},{"version":"901becb8779e1089442378fda5623e607ee4588762a32e7692436f1ea81cf848","impliedFormat":1},{"version":"8286d84d2567b713fd6a1fdfbb1a0abc8cfa668ee1e0e83d7dd4ade5761f2750","impliedFormat":1},{"version":"e4a52c589745f7bca3346962af0b62270302f9e60b116037483b3dea345d5f9d","impliedFormat":1},{"version":"38f50c96cd804ec61e75c742cd8165651f31076307a2492e6afd595f574975e6","impliedFormat":1},{"version":"c0513cfcf00f4fc7a7fe41b4c66b8cd56e280f37233c2ed8b89c9ae6f5fc3a7f","impliedFormat":1},{"version":"85e65d83a8e65bc6ff28b67b8b4a4565b6754be8e74a48080fc83f00be07a16f","impliedFormat":1},{"version":"9678e67085896b6fdfdaf3be547822a5f86ca030284b653a08235df00914d70d","impliedFormat":1},{"version":"8c806b7b1e03a9de990cced43abb3541304ce46b31073d326879d5f243cec1f4","impliedFormat":1},{"version":"a76355c652099c5ecfae4f038b23235b50ab00bbfda8d9685014b0c53e38210a","impliedFormat":1},{"version":"41c851891f277afd9bf0b3af06fb3d35c11b32cb0120e85ae5b7e6dbbda037de","impliedFormat":1},{"version":"8286d84d2567b713fd6a1fdfbb1a0abc8cfa668ee1e0e83d7dd4ade5761f2750","impliedFormat":1},{"version":"a710a056936098886b0b5eb6734034589d15dbd7fa29329dc936f3f4e7bce93b","impliedFormat":1},"1676e8dbe08ff284a0f75938d97e6a4da2dc39fcecb03b18276f598bd30ce58a","9669ae16bbbb071a7720ddfa9a27fb17da2c8114505e309d446ea34d8406c918","4ae7784b4e00b43c1c66f700748a3ca59a23f2ca5794ae204ba8cb4f6ddbda1c","0352d7ffabfeceb77a18b7bd7419cada59eee667ef2a1d4258c50c82a0f8138b","460c8a20b1d42ef2b6b433fc86a14916d31b94f170b483414a5b204fc8cb2dd2","1c2547abfdde0923c93d564601ba55a7de6aacdb172eaecd73e990e1c5e0d862","9a910c69bbe500390412706a8dc369832bef51d8b869483dcbc7bdf301465f2b","e280f80a5c28f3cf46d992920189be89bf31743bf38160e9fe50fca859dc78b7",{"version":"161c8e0690c46021506e32fda85956d785b70f309ae97011fd27374c065cac9b","affectsGlobalScope":true,"impliedFormat":1},"5138d0a331b474840a2aa8e1cad03446851e8545868ca303afec8b21d4239abd","caa69bfb6afa0055855768c8da388ca05bd720a8dd6c98442c7403c14cbd7852","b31238cf420fa129974ae9ca9884f59471dd7c406f2dfd0375b668cf0612ef1a","f87e597de6a4ffb71dd3e5876521ce41fa7e97fdd0121ca00a7928757ec156fc",{"version":"e134052a6b1ded61693b4037f615dc72f14e2881e79c1ddbff6c514c8a516b05","impliedFormat":1},"4a60ec1824049d71b400dd64537a32bcceca9a6abc4765774b96a3d5420a0dc5","e825c2088ed3da685196d340f3892364f545d6560c38cc80f203edebd6603e9b","e5090b9aef9ebd8998217d6a31fc3539b1d110916520e55985942fef00d44020","c35f6831d1bc77fc87541fe8036a5902d76a22d0bbb47d2d6dec0e1045b15872","3034a050a425edfd70b60feb643f839b5da4212943c52034db6417f3358e7b78","a574acd60e681a10957b00f85a8dfeda9ee4bdf1c7c0b7a2536da66363445d34","2d54add319e6484f93ff621e871c332d9e0912ddcebbc1e821910916317a74c6","1f7eee5d9fa88d0277f1c352cd50a810d5e2d7e2056ef0353fcabba90ec782cd","d43468180c2eeb03e3d166a9c7ea263be91b46421fff92a48c63bb31f98ff3ed","d61a755219ba1293fd90c8b78f4f9993173767572dbe738177802454d729da1f","4e6be930eb663c841e95e4668362b536b17c71c4ce34814bd63594e627ac7c01","b84cfff8a3846ab492435cf340c953a0d21d7a12aa4310b6289afe75adb7dec3","cce04a28a25886d5e02c6691aefc840cad682dfd0ed9c965b3b86277b400462f","a6a0ac02b9be82beba788dd3685f97baa13eb28919c7beffd4000dea9f3561fd","e9be992849b419a503eac4d58799d7d3539ba64423b9ec28ed84cf940f596633","c4ebe96ec8dbbcbbe2d872e18c32b75a83139c8b2fb62635de76fcf9ebc659b5","6e7c7d739ff13d1159386b5f55f0e3b5dcf8f751faeab98682bc6807bde5c38e","90e2f8aa0f21c24c059ef654db2080f3f989ef10a4a54cb465386958e87f3c01","c4e94d055a424c0e465b09b354d0149477927c398906d19a71fb8a1ab0c97ccc","e9c107b44c0182b2a023924108751cbd0ad1cb466f155996ee93be626ad704c2",{"version":"37aaeb31561ca66a4264fa5a3a965b417793d66308b0452aa38cf0b0b9acf404","impliedFormat":1},{"version":"880e16c716211a52c099f35a54760a50f0963a4a99fe47fffacd80b8c76cfa55","impliedFormat":1},{"version":"c394b124f1dc199fdb4334db392908a3101593ed35ccb32cdf7a88dd08191b01","impliedFormat":1},{"version":"84efa072f3137628fb1b36348630e54f2558551fabd44e6797e437669e5be7de","impliedFormat":1},{"version":"42edae270fb55922752561301d8f4ed6f6238888415596a68e0d57e648ef4e78","impliedFormat":1},{"version":"e5cfe3bccf8885ecb7f06609f08ce005eac9f9db92b96981da880cc2a955d3af","impliedFormat":1},{"version":"bd97fa393719c616f9c99a73848c06a82e41774badce3915c427bde4d2bcd8a7","impliedFormat":1},{"version":"5e2e4472ad4ef7e3e33924d7f249de1a9f04d483c4eb77c2a039d151b5f6cada","impliedFormat":1},{"version":"d1726670bab0f583c8476433382e57d3acbce6ca9565cd485843314be2406d98","impliedFormat":1},{"version":"e4fab1a9e6a94557e59fff48d56d5fe94dd96bb92f8e6b31f45affd92fa40432","impliedFormat":1},{"version":"ba0f0e98feb906008e247531dc84ee68ce90fdef655e06a0c3c9ae2c0796a993","impliedFormat":1},{"version":"031306386855ceeb4f4c875dd4f625ef40724cd322bb67cacbd8fbce0285e2ab","impliedFormat":1},{"version":"cf6a174888a6c2a0b8c330645226f5282f1c371694a7a1937004ebd5694e2822","impliedFormat":1},{"version":"e085ea128f0119276ca5ce2ec6e2f47a05c330a75b2e716623888269a5fdcb5b","impliedFormat":1},{"version":"caa6c6a57261f968d545f23458b60d6a464673452b92af66b0c2745252bb7be1","impliedFormat":1},{"version":"3fa21de354079911eec4d38d95252c606ff31980e435a4abef88958564bb5621","impliedFormat":1},{"version":"95e590ff188fca6a972449ed73899b93285692f0fed890a08fefe89786fc1527","impliedFormat":1},{"version":"8a9fd98412afb1ff9d56e0b0a6b341d17adc0629830e534f8661434a862cde64","impliedFormat":1},{"version":"274331ed7fac5c85be5a325ed1f2bb88b846efc6e67ff1488de005a97c9c582f","impliedFormat":1},{"version":"c09cc5fb4c91696aab69654caef6331920199be4970ccea0e007336458cda97f","impliedFormat":1},{"version":"4b49f2ae6c20a3f6221b79ebf3f3f3b89d7e64257d4f9ad56b345c6a2607e635","impliedFormat":1},{"version":"a537be85e045f432f8ddb0a0d152635301a5a6022560305d53ee833064d79a0c","impliedFormat":1},{"version":"1489add5d9512bd619ce820bbdc0ce5c45625e6257e68bd994b5ef1d7f9c3343","impliedFormat":1},{"version":"d966ecf371d8ca9cfbd839b9a60fec0e9c4288870ea99b5936b1ccf644029ca2","impliedFormat":1},{"version":"316f1486e15cbf7896425f0a16dfe12d447dd57cfb3244b8b119c77df870858f","impliedFormat":99},{"version":"624c6c996b36eaa03ae6ba2a2a71976445ac6b49d35c3aab55617751e48165e5","impliedFormat":1},{"version":"d59f81a60bc6f5351ca3d8d9f0cf1d580b2c109537581fc648317418ff2d28eb","impliedFormat":1},{"version":"5a8e78257763161fb85fff3374d95c06f1ea004416fc7b71d76725dadb834534","impliedFormat":1},{"version":"704a9d2ddd3034598c8703b948617ce724b11e3123513126dc5b5de871c46dc3","impliedFormat":1},{"version":"c2f45eaa5c9d816ca5205ba349d8daebfc2e5022b0cf98b117e2ed5663a38480","impliedFormat":1},{"version":"f2c8c6fbbad111c5e0551b2af8eccb40fb8b98972582f491efe97b0286e64853","impliedFormat":1},{"version":"0592c6bcb292c0bf1646e18b371082c5a897845bf701c69d532a3bac9c83a587","impliedFormat":1},{"version":"89d14ee1607b01e178605aca8bf7644e50ccf2ba0df8134b624f491c68f32bf5","signature":"7c9f10b2b58b2cfec669150ca8c0b117b4b11f4ec96af412d0e5ab76ce7c38cb"},{"version":"e57000889962c7814e1383a71ae5f0c38ce69502312a1249880efd503113edb6","signature":"29b1f327bb1c11add65c101437ccd5d53e8ae23d9cc69c4c8c090f06360026e1"},{"version":"cb3277a3eadaa7ff4a4e8d45fda5c7e05d74c08e3a92d5d449ca80e38216f8f8","signature":"2ae67ff426bc525b2ccc2621d6a43bbb81c0e8c48306601ff3f7abe90b4a7ba9"},{"version":"dc31e832b03a1815094d5bfde9fe5a82eaef9b8681060f09acd8ed20bcb758c9","signature":"c75d17b45ffdbc39e9c673772e17b662df059d1356d2257e84b8a8f7d3a6b861"},{"version":"db5dbe14bb3cd58e20b037b7a7660ad1c16c35b572e4eea9f30712c43d25e88c","signature":"eb376149bdf6db6318faec365aba73d7750044df00492ac8ad3b7bdff2fd807c"}],"root":[[282,286]],"options":{"alwaysStrict":true,"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"exactOptionalPropertyTypes":true,"jsx":1,"module":99,"noImplicitAny":true,"noImplicitThis":true,"outDir":"./","skipLibCheck":true,"strict":true,"strictBindCallApply":true,"strictFunctionTypes":true,"strictNullChecks":true,"strictPropertyInitialization":true,"target":99,"useUnknownInCatchVariables":true},"referencedMap":[[76,1],[74,2],[79,3],[75,1],[77,4],[78,1],[197,5],[196,6],[193,7],[198,8],[194,2],[199,9],[136,10],[137,10],[138,11],[84,12],[139,13],[140,14],[141,15],[82,2],[142,16],[143,17],[144,18],[145,19],[146,20],[147,21],[148,21],[149,22],[150,23],[151,24],[152,25],[85,2],[83,2],[153,26],[154,27],[155,28],[189,29],[156,30],[157,2],[158,31],[159,32],[160,33],[161,34],[162,35],[163,36],[164,37],[165,38],[166,39],[167,39],[168,40],[169,2],[170,41],[171,42],[173,43],[172,44],[174,45],[175,46],[176,47],[177,48],[178,49],[179,50],[180,51],[181,52],[182,53],[183,54],[184,55],[185,56],[186,57],[86,2],[87,58],[88,2],[89,2],[132,59],[133,60],[134,2],[135,45],[187,61],[188,62],[191,2],[192,2],[260,63],[255,2],[257,64],[256,2],[258,2],[250,2],[275,65],[253,66],[254,67],[259,2],[251,2],[252,68],[263,69],[279,69],[276,2],[264,70],[266,71],[261,72],[267,70],[281,73],[268,72],[269,74],[270,2],[271,75],[272,2],[280,72],[265,2],[273,72],[277,76],[262,72],[278,70],[190,77],[195,78],[274,79],[81,2],[90,2],[224,2],[201,80],[203,81],[205,82],[204,83],[202,81],[215,84],[212,85],[214,86],[213,87],[211,80],[206,88],[207,88],[210,89],[208,88],[209,88],[200,90],[72,2],[73,2],[12,2],[13,2],[15,2],[14,2],[2,2],[16,2],[17,2],[18,2],[19,2],[20,2],[21,2],[22,2],[23,2],[3,2],[24,2],[25,2],[4,2],[26,2],[30,2],[27,2],[28,2],[29,2],[31,2],[32,2],[33,2],[5,2],[34,2],[35,2],[36,2],[37,2],[6,2],[41,2],[38,2],[39,2],[40,2],[42,2],[7,2],[43,2],[48,2],[49,2],[44,2],[45,2],[46,2],[47,2],[8,2],[53,2],[50,2],[51,2],[52,2],[54,2],[9,2],[55,2],[56,2],[57,2],[59,2],[58,2],[60,2],[61,2],[10,2],[62,2],[63,2],[64,2],[11,2],[65,2],[66,2],[67,2],[68,2],[69,2],[1,2],[70,2],[71,2],[229,2],[108,91],[120,92],[106,93],[121,94],[130,95],[97,96],[98,97],[96,98],[129,99],[124,100],[128,101],[100,102],[117,103],[99,104],[127,105],[94,106],[95,100],[101,107],[102,2],[107,108],[105,107],[92,109],[131,110],[122,111],[111,112],[110,107],[112,113],[115,114],[109,115],[113,116],[125,99],[103,117],[104,118],[116,119],[93,94],[119,120],[118,107],[114,121],[123,2],[91,2],[126,122],[282,123],[286,124],[285,125],[283,126],[284,127],[80,128],[220,129],[221,130],[219,2],[222,131],[223,2],[225,132],[226,132],[227,2],[228,132],[232,133],[217,134],[216,135],[249,136],[231,137],[233,138],[234,2],[235,139],[230,138],[236,2],[237,2],[238,2],[239,2],[218,2],[240,2],[241,2],[242,2],[243,2],[244,2],[245,140],[246,123],[247,141],[248,142]],"latestChangedDtsFile":"./index.d.ts","version":"5.9.3"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@amazon/vinyl-browserstack",
|
|
3
|
+
"version": "3.0.1",
|
|
4
|
+
"description": "Runs an in-browser Jasmine suite across BrowserStack real browsers over Selenium/WebDriver.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/amazonmusic/vinyl.git"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"development": "./src/index.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc -p ./src/tsconfig.json",
|
|
24
|
+
"build:release": "tsc -p ./src/tsconfig.json",
|
|
25
|
+
"build:watch": "tsc -p ./src/tsconfig.json -w",
|
|
26
|
+
"clean": "rimraf --glob dist \"**/*.tsbuildinfo\"",
|
|
27
|
+
"lint": "npm run oxlint:check && npm run prettier:check",
|
|
28
|
+
"lint:write": "npm run oxlint:write && npm run prettier:write",
|
|
29
|
+
"prettier:check": "prettier . --ignore-path ../../.prettierignore --cache --check",
|
|
30
|
+
"prettier:write": "prettier . --ignore-path ../../.prettierignore --cache --write",
|
|
31
|
+
"release": "npm run lint && npm run build:release",
|
|
32
|
+
"test": "echo \"No tests\"",
|
|
33
|
+
"test:coverage": "echo \"No tests\"",
|
|
34
|
+
"oxlint:check": "oxlint --type-aware --deny-warnings",
|
|
35
|
+
"oxlint:write": "oxlint --type-aware --fix"
|
|
36
|
+
},
|
|
37
|
+
"author": "",
|
|
38
|
+
"license": "Apache-2.0",
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/express": "^5.0.1",
|
|
41
|
+
"@types/selenium-webdriver": "^4.1.28"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@amazon/vinyl-build-utils": "^3.0.1",
|
|
45
|
+
"express": "^5.1.0",
|
|
46
|
+
"selenium-webdriver": "^4.28.1"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
}
|
|
51
|
+
}
|