@allwright.dev/core 0.0.32 → 0.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ import type { Browser, BrowserInfo, BrowserKind, BrowserLaunchState, BrowserType, CommandOptions, LaunchOptions, Page } from "./types.js";
2
+ export declare class BrowserTypeImpl implements BrowserType {
3
+ #private;
4
+ constructor(browserKind?: BrowserKind);
5
+ launch(options?: LaunchOptions): Promise<Browser>;
6
+ }
7
+ export declare class BrowserImpl implements Browser {
8
+ #private;
9
+ constructor(state: BrowserLaunchState);
10
+ readonly sessionId: string;
11
+ readonly browserName: string;
12
+ readonly launchNote: string;
13
+ readonly cdpWebSocketURL: string;
14
+ readonly userDataDir: string;
15
+ page(): Page;
16
+ initialPage(): Page;
17
+ pages(): Page[];
18
+ newPage(options?: CommandOptions): Promise<Page>;
19
+ close(): Promise<void>;
20
+ ping(message?: string): Promise<string>;
21
+ browserInfo(): BrowserInfo;
22
+ initialTab(): Page;
23
+ newTab(options?: CommandOptions): Promise<Page>;
24
+ }
@@ -0,0 +1,137 @@
1
+ import { PageImpl } from "./page.js";
2
+ export class BrowserTypeImpl {
3
+ #browserKind;
4
+ constructor(browserKind = "chromium") {
5
+ this.#browserKind = browserKind;
6
+ }
7
+ async launch(options = {}) {
8
+ const { launchBrowser } = await import("./index.js");
9
+ return launchBrowser(this.#browserKind, options);
10
+ }
11
+ }
12
+ export class BrowserImpl {
13
+ #closed = false;
14
+ #runtime;
15
+ #stream;
16
+ #queue;
17
+ #pages = new Map();
18
+ #initialPage;
19
+ constructor(state) {
20
+ const browserInfo = {
21
+ sessionId: state.sessionId,
22
+ browserName: state.launched.browser ?? "",
23
+ launchNote: state.launched.note ?? "",
24
+ cdpWebSocketURL: "",
25
+ userDataDir: state.launched.userDataDir ?? "",
26
+ };
27
+ this.#runtime = state.runtime;
28
+ this.#stream = state.stream;
29
+ this.#queue = state.queue;
30
+ this.sessionId = browserInfo.sessionId;
31
+ this.browserName = browserInfo.browserName;
32
+ this.launchNote = browserInfo.launchNote;
33
+ this.cdpWebSocketURL = browserInfo.cdpWebSocketURL;
34
+ this.userDataDir = browserInfo.userDataDir;
35
+ this.#initialPage = this.#createPage(state.launched.initialTabSessionId ?? "");
36
+ }
37
+ sessionId;
38
+ browserName;
39
+ launchNote;
40
+ cdpWebSocketURL;
41
+ userDataDir;
42
+ page() {
43
+ return this.#initialPage;
44
+ }
45
+ initialPage() {
46
+ return this.#initialPage;
47
+ }
48
+ pages() {
49
+ return [...this.#pages.values()];
50
+ }
51
+ async newPage(options = {}) {
52
+ this.#ensureOpen();
53
+ this.#stream.write({
54
+ openTab: {
55
+ retryOptions: options.timeoutMs ? { timeoutMs: options.timeoutMs } : undefined,
56
+ },
57
+ });
58
+ while (true) {
59
+ const event = await this.#queue.next();
60
+ if (event.tabOpened?.tabSessionId) {
61
+ return this.#createPage(event.tabOpened.tabSessionId);
62
+ }
63
+ if (event.error?.message) {
64
+ throw new Error(`browser session error while opening tab: ${event.error.message}`);
65
+ }
66
+ }
67
+ }
68
+ async close() {
69
+ if (this.#closed) {
70
+ return;
71
+ }
72
+ this.#stream.write({
73
+ close: {},
74
+ });
75
+ while (true) {
76
+ const event = await this.#queue.next();
77
+ if (event.closed) {
78
+ this.#closed = true;
79
+ this.#stream.end();
80
+ return;
81
+ }
82
+ if (event.error?.message) {
83
+ throw new Error(`browser session error while closing: ${event.error.message}`);
84
+ }
85
+ }
86
+ }
87
+ async ping(message = "ping") {
88
+ this.#ensureOpen();
89
+ this.#stream.write({
90
+ ping: {
91
+ message,
92
+ },
93
+ });
94
+ while (true) {
95
+ const event = await this.#queue.next();
96
+ if (event.pong?.message) {
97
+ return event.pong.message;
98
+ }
99
+ if (event.error?.message) {
100
+ throw new Error(`browser session error while pinging: ${event.error.message}`);
101
+ }
102
+ }
103
+ }
104
+ browserInfo() {
105
+ return {
106
+ sessionId: this.sessionId,
107
+ browserName: this.browserName,
108
+ launchNote: this.launchNote,
109
+ cdpWebSocketURL: this.cdpWebSocketURL,
110
+ userDataDir: this.userDataDir,
111
+ };
112
+ }
113
+ initialTab() {
114
+ return this.initialPage();
115
+ }
116
+ async newTab(options = {}) {
117
+ return this.newPage(options);
118
+ }
119
+ #createPage(sessionId) {
120
+ const existing = this.#pages.get(sessionId);
121
+ if (existing) {
122
+ return existing;
123
+ }
124
+ const page = new PageImpl({
125
+ runtime: this.#runtime,
126
+ browserSessionId: this.sessionId,
127
+ sessionId,
128
+ });
129
+ this.#pages.set(sessionId, page);
130
+ return page;
131
+ }
132
+ #ensureOpen() {
133
+ if (this.#closed) {
134
+ throw new Error(`browser session ${this.sessionId} is closed`);
135
+ }
136
+ }
137
+ }
@@ -0,0 +1,4 @@
1
+ import type { AllwrightConfig, ResolveConfigOptions, ResolvedAllwrightConfig } from "./types.js";
2
+ export declare function findConfigFile(startDir?: string): string | null;
3
+ export declare function loadConfigFile(configFile: string): AllwrightConfig;
4
+ export declare function resolveConfig(options?: ResolveConfigOptions): ResolvedAllwrightConfig;
package/dist/config.js ADDED
@@ -0,0 +1,177 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ const CONFIG_FILENAMES = [
4
+ "allwright.config.yaml",
5
+ "allwright.config.yml",
6
+ "allwright.config.json",
7
+ ".allwright/config.yaml",
8
+ ".allwright/config.yml",
9
+ ".allwright/config.json",
10
+ ];
11
+ export function findConfigFile(startDir = process.cwd()) {
12
+ let currentDir = path.resolve(startDir);
13
+ while (true) {
14
+ for (const filename of CONFIG_FILENAMES) {
15
+ const candidate = path.join(currentDir, filename);
16
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
17
+ return candidate;
18
+ }
19
+ }
20
+ const parentDir = path.dirname(currentDir);
21
+ if (parentDir === currentDir) {
22
+ return null;
23
+ }
24
+ currentDir = parentDir;
25
+ }
26
+ }
27
+ export function loadConfigFile(configFile) {
28
+ const resolved = path.resolve(configFile);
29
+ const raw = fs.readFileSync(resolved, "utf8");
30
+ const parsed = parseConfigContents(raw, resolved);
31
+ validateConfigShape(parsed, resolved);
32
+ return parsed;
33
+ }
34
+ export function resolveConfig(options = {}) {
35
+ const configFilePath = options.configFile ? path.resolve(options.configFile) : findConfigFile(options.cwd);
36
+ const fileConfig = configFilePath ? loadConfigFile(configFilePath) : {};
37
+ const suiteName = options.suite?.trim() || null;
38
+ const suiteConfig = suiteName ? fileConfig.suites?.[suiteName] : undefined;
39
+ if (suiteName && !suiteConfig) {
40
+ throw new Error(`allwright config suite "${suiteName}" was not found in ${configFilePath ?? "the resolved config file"}`);
41
+ }
42
+ const serverAddr = suiteConfig?.server?.addr ?? fileConfig.server?.addr;
43
+ const browserName = suiteConfig?.browser?.name ?? fileConfig.browser?.name ?? "chromium";
44
+ const browserBinary = suiteConfig?.browser?.binary ?? fileConfig.browser?.binary;
45
+ const launchOptions = mergeLaunchOptions(fileConfig.browser?.launchOptions, suiteConfig?.browser?.launchOptions);
46
+ const expect = {
47
+ ...(fileConfig.expect ?? {}),
48
+ ...(suiteConfig?.expect ?? {}),
49
+ };
50
+ return {
51
+ configFilePath,
52
+ suiteName,
53
+ serverAddr,
54
+ browserName,
55
+ browserBinary,
56
+ launchOptions: browserBinary ? { ...launchOptions, browserBinary } : launchOptions,
57
+ expect,
58
+ };
59
+ }
60
+ function mergeLaunchOptions(base, override) {
61
+ return {
62
+ ...(base ?? {}),
63
+ ...(override ?? {}),
64
+ };
65
+ }
66
+ function validateConfigShape(value, source) {
67
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
68
+ throw new Error(`allwright config ${source} must contain a top-level object`);
69
+ }
70
+ const config = value;
71
+ if (config.schemaVersion !== undefined && config.schemaVersion !== 1) {
72
+ throw new Error(`allwright config ${source} has unsupported schemaVersion ${String(config.schemaVersion)}; expected 1`);
73
+ }
74
+ const browserName = config.browser?.name;
75
+ if (browserName !== undefined && browserName !== "chromium" && browserName !== "firefox") {
76
+ throw new Error(`allwright config ${source} has unsupported browser.name ${String(browserName)}; use "chromium" or "firefox"`);
77
+ }
78
+ }
79
+ function parseConfigContents(raw, source) {
80
+ const extension = path.extname(source).toLowerCase();
81
+ if (extension === ".json") {
82
+ return JSON.parse(raw);
83
+ }
84
+ if (extension === ".yaml" || extension === ".yml") {
85
+ return parseSimpleYaml(raw, source);
86
+ }
87
+ throw new Error(`unsupported allwright config file extension ${extension || "<none>"} for ${source}`);
88
+ }
89
+ function parseSimpleYaml(raw, source) {
90
+ const root = {};
91
+ const stack = [
92
+ { indent: -1, value: root },
93
+ ];
94
+ for (const [index, originalLine] of raw.split(/\r?\n/).entries()) {
95
+ const lineNumber = index + 1;
96
+ const line = stripYamlComment(originalLine);
97
+ if (!line.trim()) {
98
+ continue;
99
+ }
100
+ const indent = countLeadingSpaces(line);
101
+ if (indent % 2 !== 0) {
102
+ throw new Error(`invalid YAML indentation in ${source}:${lineNumber}; use multiples of 2 spaces`);
103
+ }
104
+ while (stack.length > 1 && indent <= stack[stack.length - 1].indent) {
105
+ stack.pop();
106
+ }
107
+ const current = stack[stack.length - 1];
108
+ const trimmed = line.trim();
109
+ const separatorIndex = trimmed.indexOf(":");
110
+ if (separatorIndex <= 0) {
111
+ throw new Error(`invalid YAML mapping in ${source}:${lineNumber}`);
112
+ }
113
+ const key = trimmed.slice(0, separatorIndex).trim();
114
+ const rawValue = trimmed.slice(separatorIndex + 1).trim();
115
+ if (!key) {
116
+ throw new Error(`empty YAML key in ${source}:${lineNumber}`);
117
+ }
118
+ if (!rawValue) {
119
+ const child = {};
120
+ current.value[key] = child;
121
+ stack.push({ indent, value: child });
122
+ continue;
123
+ }
124
+ current.value[key] = parseYamlScalar(rawValue, source, lineNumber);
125
+ }
126
+ return root;
127
+ }
128
+ function stripYamlComment(line) {
129
+ let inSingleQuote = false;
130
+ let inDoubleQuote = false;
131
+ for (let index = 0; index < line.length; index += 1) {
132
+ const char = line[index];
133
+ if (char === "'" && !inDoubleQuote) {
134
+ inSingleQuote = !inSingleQuote;
135
+ continue;
136
+ }
137
+ if (char === "\"" && !inSingleQuote) {
138
+ inDoubleQuote = !inDoubleQuote;
139
+ continue;
140
+ }
141
+ if (char === "#" && !inSingleQuote && !inDoubleQuote) {
142
+ return line.slice(0, index);
143
+ }
144
+ }
145
+ return line;
146
+ }
147
+ function countLeadingSpaces(line) {
148
+ let count = 0;
149
+ while (count < line.length && line[count] === " ") {
150
+ count += 1;
151
+ }
152
+ return count;
153
+ }
154
+ function parseYamlScalar(value, source, lineNumber) {
155
+ if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) {
156
+ return value.slice(1, -1);
157
+ }
158
+ if (value === "true") {
159
+ return true;
160
+ }
161
+ if (value === "false") {
162
+ return false;
163
+ }
164
+ if (value === "null") {
165
+ return null;
166
+ }
167
+ if (/^-?\d+$/.test(value)) {
168
+ return Number.parseInt(value, 10);
169
+ }
170
+ if (/^-?\d+\.\d+$/.test(value)) {
171
+ return Number.parseFloat(value);
172
+ }
173
+ if (value.startsWith("[") || value.startsWith("{")) {
174
+ throw new Error(`unsupported YAML collection syntax in ${source}:${lineNumber}; use nested mappings instead`);
175
+ }
176
+ return value;
177
+ }
package/dist/index.d.ts CHANGED
@@ -1,183 +1,18 @@
1
- export interface LaunchOptions {
2
- browserBinary?: string;
3
- timeoutMs?: number;
4
- }
5
- export type BrowserKind = "chromium" | "firefox";
6
- export interface CommandOptions {
7
- timeoutMs?: number;
8
- }
9
- export interface NavigateResult {
10
- url: string;
11
- note: string;
12
- bidiSessionId: string;
13
- mapperTargetId: string;
14
- mapperSessionId: string;
15
- packageVersion: string;
16
- }
17
- export interface ClickResult {
18
- selector: string;
19
- note: string;
20
- bidiSessionId: string;
21
- }
22
- export interface CountResult {
23
- selector: string;
24
- count: number;
25
- note: string;
26
- }
27
- export interface HighlightOptions {
28
- timeoutMs?: number;
29
- durationMs?: number;
30
- }
31
- export interface HighlightResult {
32
- selector: string;
33
- count: number;
34
- note: string;
35
- }
36
- export interface ElementResult {
37
- selector: string;
38
- note: string;
39
- }
40
- export interface FillResult {
41
- selector: string;
42
- value: string;
43
- note: string;
44
- }
45
- export interface PressOptions {
46
- timeoutMs?: number;
47
- text?: string;
48
- }
49
- export interface PressResult {
50
- selector: string;
51
- key: string;
52
- note: string;
53
- }
54
- export interface TextResult {
55
- selector: string;
56
- text: string;
57
- note: string;
58
- }
59
- export interface WaitForSelectorOptions {
60
- timeoutMs?: number;
61
- visible?: boolean;
62
- }
63
- export interface WaitForSelectorResult {
64
- selector: string;
65
- visible: boolean;
66
- note: string;
67
- }
68
- export interface BrowserInfo {
69
- sessionId: string;
70
- browserName: string;
71
- launchNote: string;
72
- cdpWebSocketURL: string;
73
- userDataDir: string;
74
- }
75
- export interface PageInfo {
76
- sessionId: string;
77
- browserSessionId: string;
78
- }
79
- export interface LocatorInfo {
80
- page: Page;
81
- selector: string;
82
- }
83
- export interface BrowserType {
84
- launch(options?: LaunchOptions): Promise<Browser>;
85
- }
86
- export interface Browser extends BrowserInfo {
87
- page(): Page;
88
- initialPage(): Page;
89
- initialTab(): Page;
90
- pages(): Page[];
91
- newPage(options?: CommandOptions): Promise<Page>;
92
- newTab(options?: CommandOptions): Promise<Page>;
93
- close(): Promise<void>;
94
- ping(message?: string): Promise<string>;
95
- browserInfo(): BrowserInfo;
96
- }
97
- export interface Page extends PageInfo {
98
- locator(selector: string): Locator;
99
- goto(url: string, options?: CommandOptions): Promise<NavigateResult>;
100
- navigate(url: string, options?: CommandOptions): Promise<NavigateResult>;
101
- click(selector: string, options?: CommandOptions): Promise<ClickResult>;
102
- count(selector: string, options?: CommandOptions): Promise<CountResult>;
103
- highlight(selector: string, options?: HighlightOptions): Promise<HighlightResult>;
104
- focus(selector: string, options?: CommandOptions): Promise<ElementResult>;
105
- fill(selector: string, value: string, options?: CommandOptions): Promise<FillResult>;
106
- hover(selector: string, options?: CommandOptions): Promise<ElementResult>;
107
- press(selector: string, key: string, options?: PressOptions): Promise<PressResult>;
108
- textContent(selector: string, options?: CommandOptions): Promise<TextResult>;
109
- innerText(selector: string, options?: CommandOptions): Promise<TextResult>;
110
- waitForSelector(selector: string, options?: WaitForSelectorOptions): Promise<WaitForSelectorResult>;
111
- close(): Promise<void>;
112
- ping(message?: string): Promise<string>;
113
- pageInfo(): PageInfo;
114
- }
115
- export interface Locator {
116
- readonly page: Page;
117
- readonly selector: string;
118
- click(options?: CommandOptions): Promise<ClickResult>;
119
- count(options?: CommandOptions): Promise<CountResult>;
120
- highlight(options?: HighlightOptions): Promise<HighlightResult>;
121
- focus(options?: CommandOptions): Promise<ElementResult>;
122
- fill(value: string, options?: CommandOptions): Promise<FillResult>;
123
- hover(options?: CommandOptions): Promise<ElementResult>;
124
- press(key: string, options?: PressOptions): Promise<PressResult>;
125
- textContent(options?: CommandOptions): Promise<TextResult>;
126
- innerText(options?: CommandOptions): Promise<TextResult>;
127
- waitFor(options?: WaitForSelectorOptions): Promise<WaitForSelectorResult>;
128
- locator(selector: string): Locator;
129
- }
130
- export interface AllwrightConfig {
131
- schemaVersion?: 1;
132
- server?: {
133
- addr?: string;
134
- };
135
- browser?: {
136
- name?: BrowserKind;
137
- binary?: string;
138
- launchOptions?: LaunchOptions;
139
- };
140
- expect?: RetryConfig;
141
- suites?: Record<string, AllwrightSuiteConfig>;
142
- }
143
- export interface AllwrightSuiteConfig {
144
- server?: {
145
- addr?: string;
146
- };
147
- browser?: {
148
- name?: BrowserKind;
149
- binary?: string;
150
- launchOptions?: LaunchOptions;
151
- };
152
- expect?: RetryConfig;
153
- }
154
- export interface RetryConfig {
155
- timeoutMs?: number;
156
- intervalMs?: number;
157
- }
158
- export interface ResolvedAllwrightConfig {
159
- configFilePath: string | null;
160
- suiteName: string | null;
161
- serverAddr?: string;
162
- browserName: BrowserKind;
163
- browserBinary?: string;
164
- launchOptions: LaunchOptions;
165
- expect: RetryConfig;
166
- }
167
- export interface ResolveConfigOptions {
168
- cwd?: string;
169
- configFile?: string;
170
- suite?: string;
171
- }
1
+ import { BrowserImpl, BrowserTypeImpl } from "./browser.js";
2
+ import { findConfigFile, loadConfigFile, resolveConfig } from "./config.js";
3
+ import { PageImpl } from "./page.js";
4
+ import { setServerAddr, shutdown } from "./runtime.js";
5
+ import type { Browser, BrowserKind, BrowserType, LaunchOptions, Page, ResolveConfigOptions, ResolvedAllwrightConfig } from "./types.js";
6
+ export { findConfigFile, loadConfigFile, resolveConfig, setServerAddr, shutdown };
7
+ export type { AllwrightConfig, Browser, BrowserInfo, BrowserKind, BrowserType, ClickResult, CommandOptions, CountResult, ElementResult, FillResult, HighlightOptions, HighlightResult, LaunchOptions, Locator, LocatorInfo, NavigateResult, Page, PageInfo, PressOptions, PressResult, ResolveConfigOptions, ResolvedAllwrightConfig, RetryConfig, TextResult, WaitForSelectorOptions, WaitForSelectorResult, } from "./types.js";
172
8
  export declare const chromium: BrowserType;
173
9
  export declare const firefox: BrowserType;
174
10
  export type Tab = Page;
175
11
  export declare function ping(): Promise<string>;
176
12
  export declare function launchChrome(options?: LaunchOptions): Promise<Browser>;
13
+ export declare function launchFirefox(options?: LaunchOptions): Promise<Browser>;
177
14
  export declare function launchConfiguredBrowser(config: ResolvedAllwrightConfig): Promise<Browser>;
15
+ export declare function launchBrowser(): Promise<Browser>;
16
+ export declare function launchBrowser(options: ResolveConfigOptions): Promise<Browser>;
178
17
  export declare function launchBrowser(browserKind: BrowserKind, options?: LaunchOptions): Promise<Browser>;
179
- export declare function setServerAddr(serverAddr: string): void;
180
- export declare function shutdown(): Promise<void>;
181
- export declare function findConfigFile(startDir?: string): string | null;
182
- export declare function loadConfigFile(configFile: string): AllwrightConfig;
183
- export declare function resolveConfig(options?: ResolveConfigOptions): ResolvedAllwrightConfig;
18
+ export { BrowserImpl, BrowserTypeImpl, PageImpl };