@stablyai/playwright-base 0.2.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +118 -77
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +140 -33
- package/dist/index.d.ts +140 -33
- package/dist/index.mjs +115 -75
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,25 +1,89 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import {
|
|
1
|
+
import * as _playwright_test from '@playwright/test';
|
|
2
|
+
import { BrowserContext, Page, Locator, MatcherReturnType, Browser, BrowserType } from '@playwright/test';
|
|
3
3
|
import * as z4 from 'zod/v4/core';
|
|
4
4
|
|
|
5
|
+
type AnthropicModel = "anthropic/claude-sonnet-4-5-20250929";
|
|
6
|
+
type GeminiModel = "google/gemini-2.5-computer-use-preview-10-2025";
|
|
7
|
+
type Model = AnthropicModel | GeminiModel | (string & {});
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Options for configuring agent behavior during execution.
|
|
11
|
+
*/
|
|
12
|
+
type AgentActOptions = {
|
|
13
|
+
/**
|
|
14
|
+
* The page the agent will operate on.
|
|
15
|
+
*/
|
|
16
|
+
page: Page;
|
|
17
|
+
/**
|
|
18
|
+
* Maximum number of thinking cycles the agent can perform.
|
|
19
|
+
* Each cycle includes observing the page, planning, and executing one or more actions.
|
|
20
|
+
* @default 30
|
|
21
|
+
*/
|
|
22
|
+
maxCycles?: number;
|
|
23
|
+
/**
|
|
24
|
+
* AI model to use for agent reasoning.
|
|
25
|
+
* Different models may have different capabilities and performance characteristics.
|
|
26
|
+
*/
|
|
27
|
+
model?: Model;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* AI agent for automating browser interactions using natural language.
|
|
31
|
+
*
|
|
32
|
+
* The Agent can perform complex browser actions by interpreting natural language instructions.
|
|
33
|
+
* It observes the page state, makes decisions, and executes actions autonomously.
|
|
34
|
+
*
|
|
35
|
+
* Agents are created via {@link BrowserContext.newAgent} or {@link Browser.newAgent}.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const agent = context.newAgent();
|
|
40
|
+
* await agent.act('Fill out the login form and submit', { page });
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
declare class Agent {
|
|
44
|
+
readonly browserContext: BrowserContext;
|
|
45
|
+
constructor(browserContext: BrowserContext);
|
|
46
|
+
/**
|
|
47
|
+
* Instructs the agent to perform actions on the page using natural language.
|
|
48
|
+
*
|
|
49
|
+
* The agent will analyze the page, plan actions, and execute them autonomously.
|
|
50
|
+
* It can perform multiple actions such as clicking, typing, navigating, and verifying
|
|
51
|
+
* page state based on the provided instruction.
|
|
52
|
+
*
|
|
53
|
+
* @param prompt - Natural language instruction describing what the agent should do
|
|
54
|
+
* @param options - Configuration for the agent's behavior including the page to operate on
|
|
55
|
+
* @returns Promise that resolves with the result of the agent's actions
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* // Simple action
|
|
60
|
+
* await agent.act('Click the login button', { page });
|
|
61
|
+
*
|
|
62
|
+
* // Complex multi-step action
|
|
63
|
+
* await agent.act('Navigate to settings, enable notifications, and save changes', { page });
|
|
64
|
+
*
|
|
65
|
+
* // With custom options
|
|
66
|
+
* const result = await agent.act('Complete the checkout process', {
|
|
67
|
+
* page,
|
|
68
|
+
* maxCycles: 20,
|
|
69
|
+
* model: 'gpt-4'
|
|
70
|
+
* });
|
|
71
|
+
*
|
|
72
|
+
* if (result.success) {
|
|
73
|
+
* console.log('Agent completed the task successfully');
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
act(prompt: string, options: AgentActOptions): Promise<{
|
|
78
|
+
success: boolean;
|
|
79
|
+
}>;
|
|
80
|
+
}
|
|
81
|
+
|
|
5
82
|
type ExtractSchema = {
|
|
6
83
|
safeParseAsync(data: unknown, params?: z4.ParseContext<z4.$ZodIssue>): Promise<z4.util.SafeParseResult<z4.output<any>>>;
|
|
7
84
|
} & z4.$ZodType;
|
|
8
85
|
type SchemaOutput<T extends ExtractSchema> = z4.output<T>;
|
|
9
86
|
|
|
10
|
-
type LocatorDescribeOptions = {
|
|
11
|
-
autoHeal?: boolean;
|
|
12
|
-
};
|
|
13
|
-
declare function augmentLocator<T extends Locator>(locator: T): T;
|
|
14
|
-
declare function augmentPage<T extends Page>(page: T): T;
|
|
15
|
-
declare function augmentBrowserContext<T extends BrowserContext>(context: T): T;
|
|
16
|
-
declare function augmentBrowser<T extends Browser>(browser: T): T;
|
|
17
|
-
declare function augmentBrowserType<TBrowser extends Browser>(browserType: BrowserType<TBrowser>): BrowserType<TBrowser>;
|
|
18
|
-
|
|
19
|
-
type AnthropicModel = "anthropic/claude-sonnet-4-5-20250929";
|
|
20
|
-
type GeminiModel = "google/gemini-2.5-computer-use-preview-10-2025";
|
|
21
|
-
type Model = AnthropicModel | GeminiModel | string;
|
|
22
|
-
|
|
23
87
|
type MatcherContext = {
|
|
24
88
|
isNot: boolean;
|
|
25
89
|
message?: () => string;
|
|
@@ -28,15 +92,21 @@ declare const stablyPlaywrightMatchers: {
|
|
|
28
92
|
readonly toMatchScreenshotPrompt: (this: MatcherContext, received: Page | Locator, condition: string, options?: ScreenshotPromptOptions) => Promise<MatcherReturnType>;
|
|
29
93
|
};
|
|
30
94
|
|
|
95
|
+
declare function augmentLocator<T extends Locator>(locator: T): T;
|
|
96
|
+
declare function augmentPage<T extends Page>(page: T): T;
|
|
97
|
+
declare function augmentBrowserContext<T extends BrowserContext>(context: T): T;
|
|
98
|
+
declare function augmentBrowser<T extends Browser>(browser: T): T;
|
|
99
|
+
declare function augmentBrowserType<TBrowser extends Browser>(browserType: BrowserType<TBrowser>): BrowserType<TBrowser>;
|
|
100
|
+
|
|
31
101
|
declare function setApiKey(apiKey: string): void;
|
|
32
102
|
declare function requireApiKey(): string;
|
|
33
103
|
|
|
34
|
-
type ScreenshotPromptOptions =
|
|
104
|
+
type ScreenshotPromptOptions = _playwright_test.PageAssertionsToHaveScreenshotOptions;
|
|
35
105
|
|
|
36
106
|
type Expect<T = Page> = {
|
|
37
107
|
toMatchScreenshotPrompt(condition: string, options?: ScreenshotPromptOptions): Promise<void>;
|
|
38
108
|
};
|
|
39
|
-
declare module "@
|
|
109
|
+
declare module "@playwright/test" {
|
|
40
110
|
interface Locator {
|
|
41
111
|
/**
|
|
42
112
|
* Extracts information from this locator using Stably AI.
|
|
@@ -63,7 +133,6 @@ declare module "@stablyai/internal-playwright-test" {
|
|
|
63
133
|
extract<T extends ExtractSchema>(prompt: string, options: {
|
|
64
134
|
schema: T;
|
|
65
135
|
}): Promise<SchemaOutput<T>>;
|
|
66
|
-
describe(description: string, options?: LocatorDescribeOptions): Locator;
|
|
67
136
|
}
|
|
68
137
|
interface Page {
|
|
69
138
|
/**
|
|
@@ -93,23 +162,61 @@ declare module "@stablyai/internal-playwright-test" {
|
|
|
93
162
|
}): Promise<SchemaOutput<T>>;
|
|
94
163
|
}
|
|
95
164
|
interface BrowserContext {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
165
|
+
/**
|
|
166
|
+
* Creates a new AI agent instance for automating browser interactions.
|
|
167
|
+
*
|
|
168
|
+
* An agent can perform complex browser actions by interpreting natural language instructions.
|
|
169
|
+
* It observes the page, makes autonomous decisions, and executes actions like clicking,
|
|
170
|
+
* typing, navigating, and verifying page state.
|
|
171
|
+
*
|
|
172
|
+
* The agent operates within this browser context and can interact with all pages in the context.
|
|
173
|
+
* Use the returned agent's [agent.act(prompt, options)](#agent-act) method to give instructions.
|
|
174
|
+
* The page to operate on is specified when calling `act()`.
|
|
175
|
+
*
|
|
176
|
+
* **Usage**
|
|
177
|
+
*
|
|
178
|
+
* ```js
|
|
179
|
+
* const context = await browser.newContext();
|
|
180
|
+
* const page = await context.newPage();
|
|
181
|
+
* await page.goto('https://example.com');
|
|
182
|
+
*
|
|
183
|
+
* // Create an agent for this context
|
|
184
|
+
* const agent = context.newAgent();
|
|
185
|
+
*
|
|
186
|
+
* // Give the agent natural language instructions with the page to operate on
|
|
187
|
+
* await agent.act('Click the login button and enter credentials', { page });
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
newAgent(): Agent;
|
|
103
191
|
}
|
|
104
192
|
interface Browser {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
193
|
+
/**
|
|
194
|
+
* Creates a new AI agent instance for automating browser interactions.
|
|
195
|
+
*
|
|
196
|
+
* An agent can perform complex browser actions by interpreting natural language instructions.
|
|
197
|
+
* It observes the page, makes autonomous decisions, and executes actions like clicking,
|
|
198
|
+
* typing, navigating, and verifying page state.
|
|
199
|
+
*
|
|
200
|
+
* The agent operates within a browser context and can interact with all pages in that context.
|
|
201
|
+
* Use the returned agent's [agent.act(prompt, options)](#agent-act) method to give instructions.
|
|
202
|
+
* The page to operate on is specified when calling `act()`.
|
|
203
|
+
*
|
|
204
|
+
* **Usage**
|
|
205
|
+
*
|
|
206
|
+
* ```js
|
|
207
|
+
* const browser = await chromium.launch();
|
|
208
|
+
* const page = await browser.newPage();
|
|
209
|
+
* await page.goto('https://example.com');
|
|
210
|
+
*
|
|
211
|
+
* // Create an agent
|
|
212
|
+
* const agent = browser.newAgent();
|
|
213
|
+
*
|
|
214
|
+
* // Give the agent natural language instructions with the page to operate on
|
|
215
|
+
* await agent.act('Fill out the registration form and submit', { page });
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
newAgent(): Agent;
|
|
112
219
|
}
|
|
113
220
|
}
|
|
114
221
|
|
|
115
|
-
export { type Expect, type ExtractSchema, type
|
|
222
|
+
export { Agent, type Expect, type ExtractSchema, type SchemaOutput, type ScreenshotPromptOptions, augmentBrowser, augmentBrowserContext, augmentBrowserType, augmentLocator, augmentPage, requireApiKey, setApiKey, stablyPlaywrightMatchers };
|
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
6
6
|
});
|
|
7
7
|
|
|
8
8
|
// src/expect.ts
|
|
9
|
-
import { test } from "@
|
|
9
|
+
import { test } from "@playwright/test";
|
|
10
10
|
|
|
11
11
|
// src/runtime.ts
|
|
12
12
|
var configuredApiKey = process.env.STABLY_API_KEY;
|
|
@@ -34,11 +34,16 @@ var isObject = (value) => {
|
|
|
34
34
|
// src/ai/metadata.ts
|
|
35
35
|
var SDK_METADATA_HEADERS = {
|
|
36
36
|
"X-Client-Name": "stably-playwright-sdk-js",
|
|
37
|
-
"X-Client-Version": "0.
|
|
37
|
+
"X-Client-Version": "1.0.0-next.1"
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
// src/ai/verify-prompt.ts
|
|
41
|
-
var
|
|
41
|
+
var PROMPT_ASSERTION_PATH = "internal/v1/assert";
|
|
42
|
+
var STABLY_API_URL = process.env.STABLY_API_URL || "https://api.stably.ai";
|
|
43
|
+
var PROMPT_ASSERTION_ENDPOINT = new URL(
|
|
44
|
+
PROMPT_ASSERTION_PATH,
|
|
45
|
+
STABLY_API_URL
|
|
46
|
+
).toString();
|
|
42
47
|
var parseSuccessResponse = (value) => {
|
|
43
48
|
if (!isObject(value)) {
|
|
44
49
|
throw new Error("Verify prompt returned unexpected response shape");
|
|
@@ -413,7 +418,7 @@ var stablyPlaywrightMatchers = {
|
|
|
413
418
|
};
|
|
414
419
|
|
|
415
420
|
// src/playwright-augment/methods/agent.ts
|
|
416
|
-
import { test as test2 } from "@
|
|
421
|
+
import { test as test2 } from "@playwright/test";
|
|
417
422
|
|
|
418
423
|
// src/utils/truncate.ts
|
|
419
424
|
var truncate = (inp, length) => inp.length <= length || inp.length <= 3 ? inp : `${inp.slice(0, length - 3)}...`;
|
|
@@ -621,16 +626,48 @@ ${ariaSnapshot}` }
|
|
|
621
626
|
|
|
622
627
|
// src/playwright-augment/methods/agent.ts
|
|
623
628
|
var AGENT_PATH = "internal/v3/agent";
|
|
624
|
-
var
|
|
625
|
-
var AGENT_ENDPOINT = new URL(AGENT_PATH,
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
+
var STABLY_API_URL2 = process.env.STABLY_API_URL || "https://api.stably.ai";
|
|
630
|
+
var AGENT_ENDPOINT = new URL(AGENT_PATH, STABLY_API_URL2).toString();
|
|
631
|
+
var Agent = class {
|
|
632
|
+
constructor(browserContext) {
|
|
633
|
+
this.browserContext = browserContext;
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Instructs the agent to perform actions on the page using natural language.
|
|
637
|
+
*
|
|
638
|
+
* The agent will analyze the page, plan actions, and execute them autonomously.
|
|
639
|
+
* It can perform multiple actions such as clicking, typing, navigating, and verifying
|
|
640
|
+
* page state based on the provided instruction.
|
|
641
|
+
*
|
|
642
|
+
* @param prompt - Natural language instruction describing what the agent should do
|
|
643
|
+
* @param options - Configuration for the agent's behavior including the page to operate on
|
|
644
|
+
* @returns Promise that resolves with the result of the agent's actions
|
|
645
|
+
*
|
|
646
|
+
* @example
|
|
647
|
+
* ```typescript
|
|
648
|
+
* // Simple action
|
|
649
|
+
* await agent.act('Click the login button', { page });
|
|
650
|
+
*
|
|
651
|
+
* // Complex multi-step action
|
|
652
|
+
* await agent.act('Navigate to settings, enable notifications, and save changes', { page });
|
|
653
|
+
*
|
|
654
|
+
* // With custom options
|
|
655
|
+
* const result = await agent.act('Complete the checkout process', {
|
|
656
|
+
* page,
|
|
657
|
+
* maxCycles: 20,
|
|
658
|
+
* model: 'gpt-4'
|
|
659
|
+
* });
|
|
660
|
+
*
|
|
661
|
+
* if (result.success) {
|
|
662
|
+
* console.log('Agent completed the task successfully');
|
|
663
|
+
* }
|
|
664
|
+
* ```
|
|
665
|
+
*/
|
|
666
|
+
async act(prompt, options) {
|
|
629
667
|
const apiKey = requireApiKey();
|
|
630
668
|
const maxCycles = options.maxCycles ?? 30;
|
|
631
|
-
const browserContext = options.page.context();
|
|
632
669
|
const tabManager = /* @__PURE__ */ new Map();
|
|
633
|
-
browserContext.pages().forEach((page, index) => {
|
|
670
|
+
this.browserContext.pages().forEach((page, index) => {
|
|
634
671
|
tabManager.set(page, `page${index + 1}`);
|
|
635
672
|
});
|
|
636
673
|
let activePage = options.page;
|
|
@@ -653,14 +690,14 @@ function createAgentStub() {
|
|
|
653
690
|
const alias = tabManager.get(page);
|
|
654
691
|
newPageOpenedMsg = `opened new tab ${alias} (${page.url()})`;
|
|
655
692
|
};
|
|
656
|
-
browserContext.on("page", onNewPage);
|
|
693
|
+
this.browserContext.on("page", onNewPage);
|
|
657
694
|
return await test2.step(`[Agent] ${prompt}`, async () => {
|
|
658
695
|
try {
|
|
659
696
|
for (let i = 0; i < maxCycles; i++) {
|
|
660
697
|
if (agentMessage.shouldTerminate) {
|
|
661
698
|
break;
|
|
662
699
|
}
|
|
663
|
-
const agentResponses = await test2.step(`[Thinking ${
|
|
700
|
+
const agentResponses = await test2.step(`[Thinking ${i + 1}]`, async (stepInfo) => {
|
|
664
701
|
const screenshot = await takeStableScreenshot(activePage);
|
|
665
702
|
const response = await fetch(AGENT_ENDPOINT, {
|
|
666
703
|
body: constructAgentPayload({
|
|
@@ -690,13 +727,12 @@ function createAgentStub() {
|
|
|
690
727
|
const reasoningText = reasoningTexts.join("\n\n");
|
|
691
728
|
const truncatedReasoningText = truncate(reasoningText, 120);
|
|
692
729
|
await stepInfo.attach(
|
|
693
|
-
`[Thinking ${
|
|
730
|
+
`[Thinking ${i + 1}] ${truncatedReasoningText}`,
|
|
694
731
|
{
|
|
695
732
|
body: reasoningText,
|
|
696
733
|
contentType: "text/plain"
|
|
697
734
|
}
|
|
698
735
|
);
|
|
699
|
-
thoughtsIndex++;
|
|
700
736
|
return responseJson;
|
|
701
737
|
});
|
|
702
738
|
let combinedMessages = [];
|
|
@@ -710,7 +746,7 @@ function createAgentStub() {
|
|
|
710
746
|
} = await execResponse({
|
|
711
747
|
activePage,
|
|
712
748
|
agentResponse,
|
|
713
|
-
browserContext,
|
|
749
|
+
browserContext: this.browserContext,
|
|
714
750
|
tabManager
|
|
715
751
|
});
|
|
716
752
|
activePage = newActivePage;
|
|
@@ -731,17 +767,19 @@ function createAgentStub() {
|
|
|
731
767
|
};
|
|
732
768
|
}
|
|
733
769
|
} finally {
|
|
734
|
-
browserContext.off("page", onNewPage);
|
|
770
|
+
this.browserContext.off("page", onNewPage);
|
|
735
771
|
}
|
|
736
772
|
return { success: finalSuccess ?? false };
|
|
737
773
|
});
|
|
738
|
-
}
|
|
739
|
-
}
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
var createNewAgent = (browserContext) => new Agent(browserContext);
|
|
740
777
|
|
|
741
778
|
// src/ai/extract.ts
|
|
779
|
+
import { test as test3 } from "@playwright/test";
|
|
742
780
|
var EXTRACT_PATH = "internal/v2/extract";
|
|
743
|
-
var
|
|
744
|
-
var EXTRACT_ENDPOINT = new URL(EXTRACT_PATH,
|
|
781
|
+
var STABLY_API_URL3 = process.env.STABLY_API_URL || "https://api.stably.ai";
|
|
782
|
+
var EXTRACT_ENDPOINT = new URL(EXTRACT_PATH, STABLY_API_URL3).toString();
|
|
745
783
|
var zodV4 = (() => {
|
|
746
784
|
try {
|
|
747
785
|
return __require("zod/v4/core");
|
|
@@ -771,7 +809,10 @@ var ExtractValidationError = class extends Error {
|
|
|
771
809
|
async function validateWithSchema(schema, value) {
|
|
772
810
|
const result = await schema.safeParseAsync(value);
|
|
773
811
|
if (!result.success) {
|
|
774
|
-
throw new ExtractValidationError(
|
|
812
|
+
throw new ExtractValidationError(
|
|
813
|
+
"AI is unable to return the data in the desired format",
|
|
814
|
+
result.error.issues
|
|
815
|
+
);
|
|
775
816
|
}
|
|
776
817
|
return result.data;
|
|
777
818
|
}
|
|
@@ -789,36 +830,47 @@ async function extract({
|
|
|
789
830
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
790
831
|
schema
|
|
791
832
|
) : void 0;
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
const pngBuffer = await pageOrLocator.screenshot({ type: "png" });
|
|
799
|
-
const u8 = Uint8Array.from(pngBuffer);
|
|
800
|
-
const blob = new Blob([u8], { type: "image/png" });
|
|
801
|
-
form.append("image", blob, "screenshot.png");
|
|
802
|
-
const response = await fetch(EXTRACT_ENDPOINT, {
|
|
803
|
-
body: form,
|
|
804
|
-
headers: {
|
|
805
|
-
...SDK_METADATA_HEADERS,
|
|
806
|
-
Authorization: `Bearer ${apiKey}`
|
|
807
|
-
},
|
|
808
|
-
method: "POST"
|
|
809
|
-
});
|
|
810
|
-
const raw = await response.json().catch(() => void 0);
|
|
811
|
-
if (response.ok) {
|
|
812
|
-
if (!isExtractionResponse(raw)) {
|
|
813
|
-
throw new Error("Extract returned unexpected response shape");
|
|
833
|
+
return await test3.step(`[Extract] ${prompt}`, async (stepInfo) => {
|
|
834
|
+
const apiKey = requireApiKey();
|
|
835
|
+
const form = new FormData();
|
|
836
|
+
form.append("prompt", prompt);
|
|
837
|
+
if (jsonSchema) {
|
|
838
|
+
form.append("jsonSchema", JSON.stringify(jsonSchema));
|
|
814
839
|
}
|
|
815
|
-
|
|
816
|
-
|
|
840
|
+
const pngBuffer = await pageOrLocator.screenshot({ type: "png" });
|
|
841
|
+
const u8 = Uint8Array.from(pngBuffer);
|
|
842
|
+
const blob = new Blob([u8], { type: "image/png" });
|
|
843
|
+
form.append("image", blob, "screenshot.png");
|
|
844
|
+
const response = await fetch(EXTRACT_ENDPOINT, {
|
|
845
|
+
body: form,
|
|
846
|
+
headers: {
|
|
847
|
+
...SDK_METADATA_HEADERS,
|
|
848
|
+
Authorization: `Bearer ${apiKey}`
|
|
849
|
+
},
|
|
850
|
+
method: "POST"
|
|
851
|
+
});
|
|
852
|
+
const raw = await response.json().catch(() => void 0);
|
|
853
|
+
if (response.ok) {
|
|
854
|
+
if (!isExtractionResponse(raw)) {
|
|
855
|
+
throw new Error("Extract returned unexpected response shape");
|
|
856
|
+
}
|
|
857
|
+
if (!raw.success) {
|
|
858
|
+
await stepInfo.attach("[Extract] error", {
|
|
859
|
+
body: raw.error,
|
|
860
|
+
contentType: "text/plain"
|
|
861
|
+
});
|
|
862
|
+
throw new Error(`Extract failed: ${raw.error}`);
|
|
863
|
+
}
|
|
864
|
+
const { value } = raw;
|
|
865
|
+
const body = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
866
|
+
await stepInfo.attach("[Extract] result", {
|
|
867
|
+
body,
|
|
868
|
+
contentType: "text/plain"
|
|
869
|
+
});
|
|
870
|
+
return schema ? await validateWithSchema(schema, value) : typeof value === "string" ? value : JSON.stringify(value);
|
|
817
871
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
}
|
|
821
|
-
throw new Error(isErrorResponse(raw) ? raw.error : "Extract failed");
|
|
872
|
+
throw new Error(isErrorResponse(raw) ? raw.error : "Extract failed");
|
|
873
|
+
});
|
|
822
874
|
}
|
|
823
875
|
|
|
824
876
|
// src/playwright-augment/methods/extract.ts
|
|
@@ -840,9 +892,6 @@ var createPageExtract = (page) => createExtract(page);
|
|
|
840
892
|
|
|
841
893
|
// src/playwright-augment/augment.ts
|
|
842
894
|
var LOCATOR_PATCHED = Symbol.for("stably.playwright.locatorPatched");
|
|
843
|
-
var LOCATOR_DESCRIBE_WRAPPED = Symbol.for(
|
|
844
|
-
"stably.playwright.locatorDescribeWrapped"
|
|
845
|
-
);
|
|
846
895
|
var PAGE_PATCHED = Symbol.for("stably.playwright.pagePatched");
|
|
847
896
|
var CONTEXT_PATCHED = Symbol.for("stably.playwright.contextPatched");
|
|
848
897
|
var BROWSER_PATCHED = Symbol.for("stably.playwright.browserPatched");
|
|
@@ -860,15 +909,6 @@ function augmentLocator(locator) {
|
|
|
860
909
|
return locator;
|
|
861
910
|
}
|
|
862
911
|
defineHiddenProperty(locator, "extract", createLocatorExtract(locator));
|
|
863
|
-
const markerTarget = locator;
|
|
864
|
-
if (typeof locator.describe === "function" && !markerTarget[LOCATOR_DESCRIBE_WRAPPED]) {
|
|
865
|
-
const originalDescribe = locator.describe.bind(locator);
|
|
866
|
-
locator.describe = (description, options) => {
|
|
867
|
-
const result = originalDescribe(description, options);
|
|
868
|
-
return result ? augmentLocator(result) : result;
|
|
869
|
-
};
|
|
870
|
-
defineHiddenProperty(locator, LOCATOR_DESCRIBE_WRAPPED, true);
|
|
871
|
-
}
|
|
872
912
|
defineHiddenProperty(locator, LOCATOR_PATCHED, true);
|
|
873
913
|
return locator;
|
|
874
914
|
}
|
|
@@ -896,13 +936,12 @@ function augmentBrowserContext(context) {
|
|
|
896
936
|
};
|
|
897
937
|
const originalPages = context.pages?.bind(context);
|
|
898
938
|
if (originalPages) {
|
|
899
|
-
context.pages = () => originalPages().map(
|
|
900
|
-
(page) => augmentPage(page)
|
|
901
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
902
|
-
);
|
|
939
|
+
context.pages = () => originalPages().map((page) => augmentPage(page));
|
|
903
940
|
}
|
|
904
|
-
if (!context.
|
|
905
|
-
defineHiddenProperty(context, "
|
|
941
|
+
if (!context.newAgent) {
|
|
942
|
+
defineHiddenProperty(context, "newAgent", () => {
|
|
943
|
+
return createNewAgent(context);
|
|
944
|
+
});
|
|
906
945
|
}
|
|
907
946
|
defineHiddenProperty(context, CONTEXT_PATCHED, true);
|
|
908
947
|
return context;
|
|
@@ -924,10 +963,13 @@ function augmentBrowser(browser) {
|
|
|
924
963
|
const originalContexts = browser.contexts.bind(browser);
|
|
925
964
|
browser.contexts = () => originalContexts().map(
|
|
926
965
|
(context) => augmentBrowserContext(context)
|
|
927
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
928
966
|
);
|
|
929
|
-
if (!browser.
|
|
930
|
-
defineHiddenProperty(browser, "
|
|
967
|
+
if (!browser.newAgent) {
|
|
968
|
+
defineHiddenProperty(browser, "newAgent", () => {
|
|
969
|
+
const contexts = browser.contexts();
|
|
970
|
+
const context = contexts.length > 0 ? contexts[0] : browser.contexts()[0];
|
|
971
|
+
return createNewAgent(context);
|
|
972
|
+
});
|
|
931
973
|
}
|
|
932
974
|
defineHiddenProperty(browser, BROWSER_PATCHED, true);
|
|
933
975
|
return browser;
|
|
@@ -957,10 +999,7 @@ function augmentBrowserType(browserType) {
|
|
|
957
999
|
return augmentBrowser(browser);
|
|
958
1000
|
};
|
|
959
1001
|
}
|
|
960
|
-
const originalLaunchPersistentContext = (
|
|
961
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
962
|
-
browserType.launchPersistentContext?.bind(browserType)
|
|
963
|
-
);
|
|
1002
|
+
const originalLaunchPersistentContext = browserType.launchPersistentContext?.bind(browserType);
|
|
964
1003
|
if (originalLaunchPersistentContext) {
|
|
965
1004
|
browserType.launchPersistentContext = async (...args) => {
|
|
966
1005
|
const context = await originalLaunchPersistentContext(...args);
|
|
@@ -971,6 +1010,7 @@ function augmentBrowserType(browserType) {
|
|
|
971
1010
|
return browserType;
|
|
972
1011
|
}
|
|
973
1012
|
export {
|
|
1013
|
+
Agent,
|
|
974
1014
|
augmentBrowser,
|
|
975
1015
|
augmentBrowserContext,
|
|
976
1016
|
augmentBrowserType,
|