@stablyai/internal-playwright-core 0.1.8 → 0.1.9

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,38 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var constants_exports = {};
20
+ __export(constants_exports, {
21
+ LIB_METADATA_HEADERS: () => LIB_METADATA_HEADERS,
22
+ STABLY_API_KEY: () => STABLY_API_KEY,
23
+ STABLY_API_URL: () => STABLY_API_URL
24
+ });
25
+ module.exports = __toCommonJS(constants_exports);
26
+ var import_utils = require("playwright-core/lib/utils");
27
+ const LIB_METADATA_HEADERS = {
28
+ "X-Client-Name": "playwright-js",
29
+ "X-Client-Version": (0, import_utils.getPlaywrightVersion)()
30
+ };
31
+ const STABLY_API_URL = process.env.STABLY_API_URL || "https://api.stably.ai";
32
+ const STABLY_API_KEY = process.env.STABLY_API_KEY;
33
+ // Annotate the CommonJS export names for ESM import in node:
34
+ 0 && (module.exports = {
35
+ LIB_METADATA_HEADERS,
36
+ STABLY_API_KEY,
37
+ STABLY_API_URL
38
+ });
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var createFormData_exports = {};
20
+ __export(createFormData_exports, {
21
+ createMultipartFormData: () => createMultipartFormData
22
+ });
23
+ module.exports = __toCommonJS(createFormData_exports);
24
+ function createMultipartFormData({
25
+ fields,
26
+ files
27
+ }) {
28
+ const form = new FormData();
29
+ for (const [k, v] of Object.entries(fields ?? {})) {
30
+ form.append(k, v);
31
+ }
32
+ for (const f of files ?? []) {
33
+ form.append(f.fieldName, new Blob([f.data], { type: f.contentType }), f.filename);
34
+ }
35
+ return form;
36
+ }
37
+ // Annotate the CommonJS export names for ESM import in node:
38
+ 0 && (module.exports = {
39
+ createMultipartFormData
40
+ });
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var compareScreenshots_exports = {};
30
+ __export(compareScreenshots_exports, {
31
+ aiCompareScreenshots: () => aiCompareScreenshots
32
+ });
33
+ module.exports = __toCommonJS(compareScreenshots_exports);
34
+ var import_constants = require("./common/constants");
35
+ var import_createFormData = require("./common/createFormData");
36
+ var import_zod = __toESM(require("zod"));
37
+ const SCREENSHOT_AUTOHEAL_ENDPOINT = "internal/v1/compare-screenshots";
38
+ const zAutohealScreenshotResponse = import_zod.default.object({
39
+ result: import_zod.default.boolean(),
40
+ reason: import_zod.default.string()
41
+ });
42
+ async function aiCompareScreenshots({
43
+ actualScreenshot,
44
+ expectedScreenshot,
45
+ model = "claude-sonnet-4-5"
46
+ }) {
47
+ if (!import_constants.STABLY_API_KEY)
48
+ throw new Error(
49
+ "STABLY_API_KEY environment variable is required for auto-healing"
50
+ );
51
+ const formData = (0, import_createFormData.createMultipartFormData)({
52
+ files: [
53
+ {
54
+ fieldName: "actualScreenshot",
55
+ filename: "actual.png",
56
+ data: actualScreenshot,
57
+ contentType: "image/png"
58
+ },
59
+ {
60
+ fieldName: "expectedScreenshot",
61
+ filename: "expected.png",
62
+ data: expectedScreenshot,
63
+ contentType: "image/png"
64
+ }
65
+ ]
66
+ });
67
+ const response = await fetch(
68
+ `${import_constants.STABLY_API_URL}/${SCREENSHOT_AUTOHEAL_ENDPOINT}?model=${encodeURIComponent(
69
+ model
70
+ )}`,
71
+ {
72
+ method: "POST",
73
+ headers: {
74
+ ...import_constants.LIB_METADATA_HEADERS,
75
+ Authorization: `Bearer ${import_constants.STABLY_API_KEY}`
76
+ },
77
+ body: formData
78
+ }
79
+ );
80
+ return zAutohealScreenshotResponse.parse(await response.json());
81
+ }
82
+ // Annotate the CommonJS export names for ESM import in node:
83
+ 0 && (module.exports = {
84
+ aiCompareScreenshots
85
+ });
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var healLocator_exports = {};
30
+ __export(healLocator_exports, {
31
+ aiHealLocator: () => aiHealLocator
32
+ });
33
+ module.exports = __toCommonJS(healLocator_exports);
34
+ var import_constants = require("./common/constants");
35
+ var import_createFormData = require("./common/createFormData");
36
+ var import_zod = __toESM(require("zod"));
37
+ const LOCATOR_AUTOHEAL_ENDPOINT = "internal/v1/heal-locator";
38
+ const zFastAutohealLocatorResponse = import_zod.default.discriminatedUnion("success", [
39
+ import_zod.default.object({
40
+ reasoning: import_zod.default.string(),
41
+ success: import_zod.default.literal(true),
42
+ coordinates: import_zod.default.tuple([import_zod.default.number(), import_zod.default.number()])
43
+ }),
44
+ import_zod.default.object({
45
+ reasoning: import_zod.default.string(),
46
+ success: import_zod.default.literal(false)
47
+ })
48
+ ]);
49
+ async function aiHealLocator({
50
+ description,
51
+ screenshot,
52
+ originalSelector,
53
+ actionName,
54
+ model = "claude-sonnet-4-5"
55
+ }) {
56
+ if (!import_constants.STABLY_API_KEY)
57
+ throw new Error(
58
+ "STABLY_API_KEY environment variable is required for auto-healing"
59
+ );
60
+ const formData = (0, import_createFormData.createMultipartFormData)({
61
+ fields: {
62
+ originalSelector,
63
+ description,
64
+ actionName
65
+ },
66
+ files: [
67
+ {
68
+ fieldName: "file",
69
+ filename: "screenshot.png",
70
+ data: screenshot,
71
+ contentType: "image/png"
72
+ }
73
+ ]
74
+ });
75
+ const response = await fetch(
76
+ `${import_constants.STABLY_API_URL}/${LOCATOR_AUTOHEAL_ENDPOINT}?model=${encodeURIComponent(
77
+ model
78
+ )}`,
79
+ {
80
+ method: "POST",
81
+ headers: {
82
+ ...import_constants.LIB_METADATA_HEADERS,
83
+ Authorization: `Bearer ${import_constants.STABLY_API_KEY}`
84
+ },
85
+ body: formData
86
+ }
87
+ );
88
+ return zFastAutohealLocatorResponse.parse(await response.json());
89
+ }
90
+ // Annotate the CommonJS export names for ESM import in node:
91
+ 0 && (module.exports = {
92
+ aiHealLocator
93
+ });
@@ -39,7 +39,7 @@ async function getElementHandleFromCoordinates(frame, coordinates) {
39
39
  coordinates[0] - box.x - style.left,
40
40
  coordinates[1] - box.y - style.top
41
41
  ];
42
- await handle.dispose();
42
+ handle.dispose();
43
43
  return await getElementHandleFromCoordinates(contentFrame, frameCoordinates);
44
44
  }
45
45
  }
@@ -50,7 +50,7 @@ async function getElementHandleFromCoordinates(frame, coordinates) {
50
50
  shadowRoot: element.shadowRoot
51
51
  }));
52
52
  if (!boundingBox || shadowRoot) {
53
- await handle.dispose();
53
+ handle.dispose();
54
54
  throw new Error(shadowRoot ? "Element is a shadow root" : "Element has no bounding box");
55
55
  }
56
56
  return { handle, frame };
@@ -29,13 +29,13 @@ __export(healingService_exports, {
29
29
  setHealingConfig: () => setHealingConfig
30
30
  });
31
31
  module.exports = __toCommonJS(healingService_exports);
32
- var import_stepContext = require("playwright/lib/worker/stepContext");
33
32
  var import_globals = require("playwright/lib/common/globals");
34
- var import_stablyApiCaller = require("../ai-tools/stablyApiCaller");
33
+ var import_stepContext = require("playwright/lib/worker/stepContext");
34
+ var import_locatorGenerators = require("../../../utils/isomorphic/locatorGenerators");
35
+ var import_compareScreenshots = require("../api/compareScreenshots");
35
36
  var import_elementHandleFromCoordinates = require("./elementHandleFromCoordinates");
36
37
  var import_screenshotFrame = require("./screenshotFrame");
37
- var import_locatorGenerators = require("../../../utils/isomorphic/locatorGenerators");
38
- var import_stablyApiCaller2 = require("../ai-tools/stablyApiCaller");
38
+ var import_healLocator = require("../api/healLocator");
39
39
  const defaultConfig = {
40
40
  enabled: false,
41
41
  timeout: 3e4
@@ -88,7 +88,7 @@ async function healLocator({
88
88
  const config = getLocatorHealingConfig();
89
89
  try {
90
90
  const screenshot = await (0, import_screenshotFrame.captureScreenshot)(frame);
91
- const aiResponse = await (0, import_stablyApiCaller.callAiToAutohealLocator)({
91
+ const aiResponse = await (0, import_healLocator.aiHealLocator)({
92
92
  description,
93
93
  screenshot,
94
94
  originalSelector: selector,
@@ -104,15 +104,21 @@ async function healLocator({
104
104
  reasoning
105
105
  };
106
106
  }
107
- const { handle, frame: elementFrame } = await (0, import_elementHandleFromCoordinates.getElementHandleFromCoordinates)(frame, [coordinate[0], coordinate[1]]);
107
+ const { handle, frame: elementFrame } = await (0, import_elementHandleFromCoordinates.getElementHandleFromCoordinates)(frame, [
108
+ coordinate[0],
109
+ coordinate[1]
110
+ ]);
108
111
  const context = await elementFrame._mainContext();
109
112
  const injectedScript = await context.injectedScript();
110
- const elementSelector = await injectedScript.evaluate((injected, element) => {
111
- const result = injected.generateSelector(element, {
112
- testIdAttributeName: ""
113
- });
114
- return result.selector;
115
- }, handle);
113
+ const elementSelector = await injectedScript.evaluate(
114
+ (injected, element) => {
115
+ const result = injected.generateSelector(element, {
116
+ testIdAttributeName: ""
117
+ });
118
+ return result.selector;
119
+ },
120
+ handle
121
+ );
116
122
  let newSelector = elementSelector;
117
123
  let locatorString = (0, import_locatorGenerators.asLocator)("javascript", elementSelector);
118
124
  if (elementFrame !== frame) {
@@ -121,14 +127,17 @@ async function healLocator({
121
127
  if (frameElement) {
122
128
  const parentContext = await frame._mainContext();
123
129
  const parentInjectedScript = await parentContext.injectedScript();
124
- const frameSelector = await parentInjectedScript.evaluate((injected, element) => {
125
- const result = injected.generateSelector(element, {
126
- testIdAttributeName: "",
127
- omitInternalEngines: true
128
- // omit internal engines for frame selector
129
- });
130
- return result.selector;
131
- }, frameElement);
130
+ const frameSelector = await parentInjectedScript.evaluate(
131
+ (injected, element) => {
132
+ const result = injected.generateSelector(element, {
133
+ testIdAttributeName: "",
134
+ omitInternalEngines: true
135
+ // omit internal engines for frame selector
136
+ });
137
+ return result.selector;
138
+ },
139
+ frameElement
140
+ );
132
141
  newSelector = `${frameSelector} >> internal:control=enter-frame >> ${elementSelector}`;
133
142
  locatorString = `locator('${frameSelector}').contentFrame().locator('${elementSelector}')`;
134
143
  await frameElement.dispose();
@@ -137,7 +146,7 @@ async function healLocator({
137
146
  throw new Error("Failed to get iframe selector");
138
147
  }
139
148
  }
140
- await handle.dispose();
149
+ handle.dispose();
141
150
  return {
142
151
  success: true,
143
152
  message: "AI found a suitable element for autohealing.",
@@ -160,8 +169,7 @@ async function attemptAutohealLocator({
160
169
  error
161
170
  }) {
162
171
  const description = (0, import_locatorGenerators.asLocatorDescription)("javascript", selector);
163
- if (!description)
164
- return void 0;
172
+ if (!description) return void 0;
165
173
  const result = await healLocator({
166
174
  frame,
167
175
  selector,
@@ -175,8 +183,7 @@ async function attemptAutohealLocator({
175
183
  actionName,
176
184
  error
177
185
  });
178
- if (result.success && result.newSelector)
179
- return result.newSelector;
186
+ if (result.success && result.newSelector) return result.newSelector;
180
187
  return void 0;
181
188
  }
182
189
  async function saveLocatorAutohealResult({
@@ -186,14 +193,18 @@ async function saveLocatorAutohealResult({
186
193
  actionName,
187
194
  error
188
195
  }) {
189
- const healingDataBody = JSON.stringify({
190
- timestampIso8601: (/* @__PURE__ */ new Date()).toISOString(),
191
- originalSelector: selector,
192
- description,
193
- actionName,
194
- originalError: error.message,
195
- result
196
- }, null, 2);
196
+ const healingDataBody = JSON.stringify(
197
+ {
198
+ timestampIso8601: (/* @__PURE__ */ new Date()).toISOString(),
199
+ originalSelector: selector,
200
+ description,
201
+ actionName,
202
+ originalError: error.message,
203
+ result
204
+ },
205
+ null,
206
+ 2
207
+ );
197
208
  const stepInfoOrTestInfo = (0, import_stepContext.getCurrentStepInfo)() ?? (0, import_globals.currentTestInfo)();
198
209
  stepInfoOrTestInfo?.attach("locator-autoheal-results", {
199
210
  body: healingDataBody,
@@ -206,7 +217,7 @@ async function healToHaveScreenshot({
206
217
  }) {
207
218
  const config = getScreenshotHealingConfig();
208
219
  try {
209
- return await (0, import_stablyApiCaller2.callAiToAutohealScreenshot)({
220
+ return await (0, import_compareScreenshots.aiCompareScreenshots)({
210
221
  actualScreenshot,
211
222
  expectedScreenshot,
212
223
  model: config.model
@@ -3,10 +3,6 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
6
  var __copyProps = (to, from, except, desc) => {
11
7
  if (from && typeof from === "object" || typeof from === "function") {
12
8
  for (let key of __getOwnPropNames(from))
@@ -17,15 +13,4 @@ var __copyProps = (to, from, except, desc) => {
17
13
  };
18
14
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
15
  var constants_exports = {};
20
- __export(constants_exports, {
21
- STABLY_API_HOSTNAME: () => STABLY_API_HOSTNAME,
22
- STABLY_API_PORT: () => STABLY_API_PORT
23
- });
24
16
  module.exports = __toCommonJS(constants_exports);
25
- const STABLY_API_HOSTNAME = process.env.STABLY_API_HOSTNAME || "api.stably.ai";
26
- const STABLY_API_PORT = process.env.STABLY_API_PORT || 8080;
27
- // Annotate the CommonJS export names for ESM import in node:
28
- 0 && (module.exports = {
29
- STABLY_API_HOSTNAME,
30
- STABLY_API_PORT
31
- });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stablyai/internal-playwright-core",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "A high-level API to automate web browsers",
5
5
  "repository": {
6
6
  "type": "git",