@stablyai/playwright-base 0.1.7-next.9 → 0.1.7

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 CHANGED
@@ -67,7 +67,7 @@ var isObject = (value) => {
67
67
  // src/ai/metadata.ts
68
68
  var SDK_METADATA_HEADERS = {
69
69
  "X-Client-Name": "stably-playwright-sdk-js",
70
- "X-Client-Version": "0.1.7-next.9"
70
+ "X-Client-Version": "0.1.7"
71
71
  };
72
72
 
73
73
  // src/ai/extract.ts
@@ -369,8 +369,163 @@ async function verifyPrompt({
369
369
  );
370
370
  }
371
371
 
372
+ // ../../node_modules/.pnpm/pixelmatch@7.1.0/node_modules/pixelmatch/index.js
373
+ function pixelmatch(img1, img2, output, width, height, options = {}) {
374
+ const {
375
+ threshold = 0.1,
376
+ alpha = 0.1,
377
+ aaColor = [255, 255, 0],
378
+ diffColor = [255, 0, 0],
379
+ includeAA,
380
+ diffColorAlt,
381
+ diffMask
382
+ } = options;
383
+ if (!isPixelData(img1) || !isPixelData(img2) || output && !isPixelData(output))
384
+ throw new Error("Image data: Uint8Array, Uint8ClampedArray or Buffer expected.");
385
+ if (img1.length !== img2.length || output && output.length !== img1.length)
386
+ throw new Error("Image sizes do not match.");
387
+ if (img1.length !== width * height * 4) throw new Error("Image data size does not match width/height.");
388
+ const len = width * height;
389
+ const a32 = new Uint32Array(img1.buffer, img1.byteOffset, len);
390
+ const b32 = new Uint32Array(img2.buffer, img2.byteOffset, len);
391
+ let identical = true;
392
+ for (let i = 0; i < len; i++) {
393
+ if (a32[i] !== b32[i]) {
394
+ identical = false;
395
+ break;
396
+ }
397
+ }
398
+ if (identical) {
399
+ if (output && !diffMask) {
400
+ for (let i = 0; i < len; i++) drawGrayPixel(img1, 4 * i, alpha, output);
401
+ }
402
+ return 0;
403
+ }
404
+ const maxDelta = 35215 * threshold * threshold;
405
+ const [aaR, aaG, aaB] = aaColor;
406
+ const [diffR, diffG, diffB] = diffColor;
407
+ const [altR, altG, altB] = diffColorAlt || diffColor;
408
+ let diff = 0;
409
+ for (let y = 0; y < height; y++) {
410
+ for (let x = 0; x < width; x++) {
411
+ const i = y * width + x;
412
+ const pos = i * 4;
413
+ const delta = a32[i] === b32[i] ? 0 : colorDelta(img1, img2, pos, pos, false);
414
+ if (Math.abs(delta) > maxDelta) {
415
+ const isAA = antialiased(img1, x, y, width, height, a32, b32) || antialiased(img2, x, y, width, height, b32, a32);
416
+ if (!includeAA && isAA) {
417
+ if (output && !diffMask) drawPixel(output, pos, aaR, aaG, aaB);
418
+ } else {
419
+ if (output) {
420
+ if (delta < 0) {
421
+ drawPixel(output, pos, altR, altG, altB);
422
+ } else {
423
+ drawPixel(output, pos, diffR, diffG, diffB);
424
+ }
425
+ }
426
+ diff++;
427
+ }
428
+ } else if (output && !diffMask) {
429
+ drawGrayPixel(img1, pos, alpha, output);
430
+ }
431
+ }
432
+ }
433
+ return diff;
434
+ }
435
+ function isPixelData(arr) {
436
+ return ArrayBuffer.isView(arr) && arr.BYTES_PER_ELEMENT === 1;
437
+ }
438
+ function antialiased(img, x1, y1, width, height, a32, b32) {
439
+ const x0 = Math.max(x1 - 1, 0);
440
+ const y0 = Math.max(y1 - 1, 0);
441
+ const x2 = Math.min(x1 + 1, width - 1);
442
+ const y2 = Math.min(y1 + 1, height - 1);
443
+ const pos = y1 * width + x1;
444
+ let zeroes = x1 === x0 || x1 === x2 || y1 === y0 || y1 === y2 ? 1 : 0;
445
+ let min = 0;
446
+ let max = 0;
447
+ let minX = 0;
448
+ let minY = 0;
449
+ let maxX = 0;
450
+ let maxY = 0;
451
+ for (let x = x0; x <= x2; x++) {
452
+ for (let y = y0; y <= y2; y++) {
453
+ if (x === x1 && y === y1) continue;
454
+ const delta = colorDelta(img, img, pos * 4, (y * width + x) * 4, true);
455
+ if (delta === 0) {
456
+ zeroes++;
457
+ if (zeroes > 2) return false;
458
+ } else if (delta < min) {
459
+ min = delta;
460
+ minX = x;
461
+ minY = y;
462
+ } else if (delta > max) {
463
+ max = delta;
464
+ maxX = x;
465
+ maxY = y;
466
+ }
467
+ }
468
+ }
469
+ if (min === 0 || max === 0) return false;
470
+ return hasManySiblings(a32, minX, minY, width, height) && hasManySiblings(b32, minX, minY, width, height) || hasManySiblings(a32, maxX, maxY, width, height) && hasManySiblings(b32, maxX, maxY, width, height);
471
+ }
472
+ function hasManySiblings(img, x1, y1, width, height) {
473
+ const x0 = Math.max(x1 - 1, 0);
474
+ const y0 = Math.max(y1 - 1, 0);
475
+ const x2 = Math.min(x1 + 1, width - 1);
476
+ const y2 = Math.min(y1 + 1, height - 1);
477
+ const val = img[y1 * width + x1];
478
+ let zeroes = x1 === x0 || x1 === x2 || y1 === y0 || y1 === y2 ? 1 : 0;
479
+ for (let x = x0; x <= x2; x++) {
480
+ for (let y = y0; y <= y2; y++) {
481
+ if (x === x1 && y === y1) continue;
482
+ zeroes += +(val === img[y * width + x]);
483
+ if (zeroes > 2) return true;
484
+ }
485
+ }
486
+ return false;
487
+ }
488
+ function colorDelta(img1, img2, k, m, yOnly) {
489
+ const r1 = img1[k];
490
+ const g1 = img1[k + 1];
491
+ const b1 = img1[k + 2];
492
+ const a1 = img1[k + 3];
493
+ const r2 = img2[m];
494
+ const g2 = img2[m + 1];
495
+ const b2 = img2[m + 2];
496
+ const a2 = img2[m + 3];
497
+ let dr = r1 - r2;
498
+ let dg = g1 - g2;
499
+ let db = b1 - b2;
500
+ const da = a1 - a2;
501
+ if (!dr && !dg && !db && !da) return 0;
502
+ if (a1 < 255 || a2 < 255) {
503
+ const rb = 48 + 159 * (k % 2);
504
+ const gb = 48 + 159 * ((k / 1.618033988749895 | 0) % 2);
505
+ const bb = 48 + 159 * ((k / 2.618033988749895 | 0) % 2);
506
+ dr = (r1 * a1 - r2 * a2 - rb * da) / 255;
507
+ dg = (g1 * a1 - g2 * a2 - gb * da) / 255;
508
+ db = (b1 * a1 - b2 * a2 - bb * da) / 255;
509
+ }
510
+ const y = dr * 0.29889531 + dg * 0.58662247 + db * 0.11448223;
511
+ if (yOnly) return y;
512
+ const i = dr * 0.59597799 - dg * 0.2741761 - db * 0.32180189;
513
+ const q = dr * 0.21147017 - dg * 0.52261711 + db * 0.31114694;
514
+ const delta = 0.5053 * y * y + 0.299 * i * i + 0.1957 * q * q;
515
+ return y > 0 ? -delta : delta;
516
+ }
517
+ function drawPixel(output, pos, r, g, b) {
518
+ output[pos + 0] = r;
519
+ output[pos + 1] = g;
520
+ output[pos + 2] = b;
521
+ output[pos + 3] = 255;
522
+ }
523
+ function drawGrayPixel(img, i, alpha, output) {
524
+ const val = 255 + (img[i] * 0.29889531 + img[i + 1] * 0.58662247 + img[i + 2] * 0.11448223 - 255) * alpha * img[i + 3] / 255;
525
+ drawPixel(output, i, val, val, val);
526
+ }
527
+
372
528
  // src/image-compare.ts
373
- var import_pixelmatch = __toESM(require("pixelmatch"));
374
529
  var import_pngjs = require("pngjs");
375
530
  var jpeg = __toESM(require("jpeg-js"));
376
531
  var isPng = (buffer) => {
@@ -404,7 +559,7 @@ var imagesAreSimilar = ({
404
559
  const diffRgbaData = new Uint8Array(
405
560
  decodedImage1.width * decodedImage1.height * 4
406
561
  );
407
- const numDiffPixels = (0, import_pixelmatch.default)(
562
+ const numDiffPixels = pixelmatch(
408
563
  decodedImage1.data,
409
564
  decodedImage2.data,
410
565
  diffRgbaData,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/runtime.ts","../src/type-predicate/is-object.ts","../src/ai/metadata.ts","../src/ai/extract.ts","../src/playwright-augment/methods/extract.ts","../src/playwright-augment/methods/agent.ts","../src/playwright-augment/augment.ts","../src/playwright-type-predicates.ts","../src/ai/verify-prompt.ts","../src/image-compare.ts","../src/screenshot.ts","../src/expect.ts"],"sourcesContent":["import type { Page } from \"@stablyai/internal-playwright-test\";\nimport type { LocatorDescribeOptions } from \"./playwright-augment/augment\";\nimport type { ExtractSchema, SchemaOutput } from \"./ai/extract\";\n\nimport {\n augmentBrowser,\n augmentBrowserContext,\n augmentBrowserType,\n augmentLocator,\n augmentPage,\n} from \"./playwright-augment/augment\";\nimport { stablyPlaywrightMatchers } from \"./expect\";\nimport { requireApiKey } from \"./runtime\";\n\nexport { setApiKey } from \"./runtime\";\n\nexport type { LocatorDescribeOptions } from \"./playwright-augment/augment\";\nexport type { ExtractSchema, SchemaOutput } from \"./ai/extract\";\nexport type ScreenshotPromptOptions =\n import(\"@stablyai/internal-playwright-test\").PageAssertionsToHaveScreenshotOptions;\nexport {\n augmentBrowser,\n augmentBrowserContext,\n augmentBrowserType,\n augmentLocator,\n augmentPage,\n stablyPlaywrightMatchers,\n requireApiKey,\n};\n\nexport interface Expect<T = Page> {\n toMatchScreenshotPrompt(\n condition: string,\n options?: ScreenshotPromptOptions,\n ): Promise<void>;\n}\n\ndeclare module \"@stablyai/internal-playwright-test\" {\n interface Locator {\n /**\n * Extracts information from this locator using Stably AI.\n *\n * Takes a screenshot of the locator and uses AI to extract information based on the\n * provided prompt. When a schema is provided, the extracted data is validated and\n * typed according to the schema.\n *\n * @param prompt - A natural language description of what information to extract\n * @returns A string containing the extracted information\n */\n extract(prompt: string): Promise<string>;\n /**\n * Extracts information from this locator using Stably AI.\n *\n * Takes a screenshot of the locator and uses AI to extract information based on the\n * provided prompt. The extracted data is validated and typed according to the schema.\n *\n * @param prompt - A natural language description of what information to extract\n * @param options - Configuration object containing the Zod schema for validation\n * @param options.schema - Zod schema to validate and type the extracted data\n * @returns Typed data matching the provided schema\n */\n extract<T extends ExtractSchema>(\n prompt: string,\n options: { schema: T },\n ): Promise<SchemaOutput<T>>;\n describe(description: string, options?: LocatorDescribeOptions): Locator;\n }\n\n interface Page {\n /**\n * Extracts information from this page using Stably AI.\n *\n * Takes a screenshot of the page and uses AI to extract information based on the\n * provided prompt. When a schema is provided, the extracted data is validated and\n * typed according to the schema.\n *\n * @param prompt - A natural language description of what information to extract\n * @returns A string containing the extracted information\n */\n extract(prompt: string): Promise<string>;\n /**\n * Extracts information from this page using Stably AI.\n *\n * Takes a screenshot of the page and uses AI to extract information based on the\n * provided prompt. The extracted data is validated and typed according to the schema.\n *\n * @param prompt - A natural language description of what information to extract\n * @param options - Configuration object containing the Zod schema for validation\n * @param options.schema - Zod schema to validate and type the extracted data\n * @returns Typed data matching the provided schema\n */\n extract<T extends ExtractSchema>(\n prompt: string,\n options: { schema: T },\n ): Promise<SchemaOutput<T>>;\n }\n\n interface BrowserContext {\n agent(\n prompt: string,\n options: { page: Page; maxCycles?: number },\n ): Promise<{ success: boolean }>;\n }\n\n interface Browser {\n agent(\n prompt: string,\n options: { page: Page; maxCycles?: number },\n ): Promise<{ success: boolean }>;\n }\n}\n","let configuredApiKey: string | undefined = process.env.STABLY_API_KEY;\n\nexport function setApiKey(apiKey: string): void {\n configuredApiKey = apiKey;\n}\n\nexport function getApiKey(): string | undefined {\n return configuredApiKey;\n}\n\nexport function requireApiKey(): string {\n const apiKey = getApiKey();\n if (!apiKey) {\n throw new Error(\n \"Missing Stably API key. Call setApiKey(apiKey) or set the STABLY_API_KEY environment variable.\",\n );\n }\n return apiKey;\n}\n","export const isObject = (value: unknown): value is Record<string, unknown> => {\n return typeof value === \"object\" && value !== null;\n};\n","declare const __PACKAGE_VERSION__: string;\n\nexport const SDK_METADATA_HEADERS = {\n \"X-Client-Name\": \"stably-playwright-sdk-js\",\n \"X-Client-Version\": __PACKAGE_VERSION__,\n};\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport type * as z4 from \"zod/v4/core\";\nimport { requireApiKey } from \"../runtime\";\nimport { isObject } from \"../type-predicate/is-object\";\nimport { SDK_METADATA_HEADERS } from \"./metadata\";\n\ntype ZodV4 = typeof import(\"zod/v4/core\");\n\nexport interface ExtractSchema extends z4.$ZodType {\n safeParseAsync(\n data: unknown,\n params?: z4.ParseContext<z4.$ZodIssue>,\n ): Promise<z4.util.SafeParseResult<z4.output<this>>>;\n}\n\nexport type SchemaOutput<T extends ExtractSchema> = z4.output<T>;\n\ntype ExtractIssue = z4.$ZodIssue;\n\nconst EXTRACT_ENDPOINT = \"https://api.stably.ai/internal/v2/extract\";\n\nconst zodV4: ZodV4 | undefined = (() => {\n try {\n return require(\"zod/v4/core\") as ZodV4;\n } catch {\n return undefined;\n }\n})();\n\ntype ExtractionSuccess = { success: true; value: unknown };\ntype ExtractionFailure = { success: false; error: string };\ntype ExtractionResponse = ExtractionSuccess | ExtractionFailure;\n\ntype ErrorResponse = { error: string };\n\nconst isExtractionResponse = (value: unknown): value is ExtractionResponse => {\n if (!isObject(value)) {\n return false;\n }\n\n if (value.success === true) {\n return \"value\" in value;\n }\n\n return value.success === false && typeof value.error === \"string\";\n};\n\nconst isErrorResponse = (value: unknown): value is ErrorResponse => {\n return isObject(value) && typeof value.error === \"string\";\n};\n\nclass ExtractValidationError extends Error {\n constructor(\n message: string,\n readonly issues: ReadonlyArray<ExtractIssue>,\n ) {\n super(message);\n this.name = \"ExtractValidationError\";\n }\n}\n\nasync function validateWithSchema<T extends ExtractSchema>(\n schema: T,\n value: unknown,\n): Promise<SchemaOutput<T>> {\n const result = await schema.safeParseAsync(value);\n if (!result.success) {\n throw new ExtractValidationError(\"Validation failed\", result.error.issues);\n }\n\n return result.data;\n}\n\ntype BaseExtractArgs = {\n prompt: string;\n pageOrLocator: Page | Locator;\n};\n\ntype ExtractArgsWithSchema<T extends ExtractSchema> = BaseExtractArgs & {\n schema: T;\n};\n\nexport async function extract(args: BaseExtractArgs): Promise<string>;\nexport async function extract<T extends ExtractSchema>(\n args: ExtractArgsWithSchema<T>,\n): Promise<SchemaOutput<T>>;\nexport async function extract<T extends ExtractSchema>({\n prompt,\n pageOrLocator,\n schema,\n}: BaseExtractArgs & { schema?: T }): Promise<string | SchemaOutput<T>> {\n if (schema && !zodV4) {\n throw new Error(\n \"Schema support requires installing zod@4. Please add it to enable schemas.\",\n );\n }\n\n const jsonSchema =\n schema && zodV4\n ? zodV4?.toJSONSchema(\n schema as unknown as Parameters<ZodV4[\"toJSONSchema\"]>[0],\n )\n : undefined;\n\n const apiKey = requireApiKey();\n\n const form = new FormData();\n form.append(\"prompt\", prompt);\n if (jsonSchema) {\n form.append(\"jsonSchema\", JSON.stringify(jsonSchema));\n }\n\n const pngBuffer = await pageOrLocator.screenshot({ type: \"png\" }); // Buffer\n const u8 = Uint8Array.from(pngBuffer); // strips Buffer type → plain Uint8Array\n const blob = new Blob([u8], { type: \"image/png\" });\n form.append(\"image\", blob, \"screenshot.png\");\n\n const response = await fetch(EXTRACT_ENDPOINT, {\n method: \"POST\",\n headers: {\n ...SDK_METADATA_HEADERS,\n Authorization: `Bearer ${apiKey}`,\n },\n body: form,\n });\n\n const raw = await response.json().catch(() => undefined as unknown);\n\n if (response.ok) {\n if (!isExtractionResponse(raw)) {\n throw new Error(\"Extract returned unexpected response shape\");\n }\n\n if (raw.success === false) {\n return raw.error;\n }\n\n const { value } = raw;\n return schema\n ? await validateWithSchema(schema, value)\n : typeof value === \"string\"\n ? value\n : JSON.stringify(value);\n }\n\n throw new Error(isErrorResponse(raw) ? raw.error : \"Extract failed\");\n}\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport {\n type ExtractSchema,\n type SchemaOutput,\n extract,\n} from \"../../ai/extract\";\n\ntype ExtractOptions<T extends ExtractSchema> = {\n schema: T;\n};\n\ntype ExtractMethod = {\n (prompt: string): Promise<string>;\n <T extends ExtractSchema>(\n prompt: string,\n options: ExtractOptions<T>,\n ): Promise<SchemaOutput<T>>;\n};\n\ntype LocatorExtract = ExtractMethod;\ntype PageExtract = ExtractMethod;\n\ntype ExtractSubject = Locator | Page;\n\nfunction createExtract(pageOrLocator: ExtractSubject): ExtractMethod {\n const impl = (async (\n prompt: string,\n options?: ExtractOptions<ExtractSchema>,\n ) => {\n if (options?.schema) {\n return extract({\n prompt,\n schema: options.schema,\n pageOrLocator,\n });\n }\n\n return extract({ prompt, pageOrLocator });\n }) as ExtractMethod;\n\n return impl;\n}\n\nexport const createLocatorExtract = (locator: Locator): LocatorExtract =>\n createExtract(locator);\n\nexport const createPageExtract = (page: Page): PageExtract =>\n createExtract(page);\n","import type { Page } from \"@stablyai/internal-playwright-test\";\nimport { requireApiKey } from \"../../runtime\";\n\ntype AgentOptions = {\n page: Page;\n maxCycles?: number;\n};\n\nexport function createAgentStub(): (\n prompt: string,\n options: AgentOptions,\n) => Promise<{ success: boolean }> {\n return async (prompt: string, options: AgentOptions) => {\n requireApiKey();\n void prompt;\n void options;\n return { success: true };\n };\n}\n","import type {\n Browser,\n BrowserContext,\n BrowserType,\n Locator,\n Page,\n} from \"@stablyai/internal-playwright-test\";\n\nimport { createLocatorExtract, createPageExtract } from \"./methods/extract\";\nimport { createAgentStub } from \"./methods/agent\";\n\nexport interface LocatorDescribeOptions {\n autoHeal?: boolean;\n}\n\nconst LOCATOR_PATCHED = Symbol.for(\"stably.playwright.locatorPatched\");\nconst LOCATOR_DESCRIBE_WRAPPED = Symbol.for(\n \"stably.playwright.locatorDescribeWrapped\",\n);\nconst PAGE_PATCHED = Symbol.for(\"stably.playwright.pagePatched\");\nconst CONTEXT_PATCHED = Symbol.for(\"stably.playwright.contextPatched\");\nconst BROWSER_PATCHED = Symbol.for(\"stably.playwright.browserPatched\");\nconst BROWSER_TYPE_PATCHED = Symbol.for(\"stably.playwright.browserTypePatched\");\n\nfunction defineHiddenProperty<T, K extends PropertyKey>(\n target: T,\n key: K,\n value: unknown,\n): void {\n Object.defineProperty(target as unknown as object, key, {\n value,\n enumerable: false,\n configurable: true,\n writable: true,\n });\n}\n\nexport function augmentLocator<T extends Locator>(locator: T): T {\n if (\n (locator as unknown as { [LOCATOR_PATCHED]?: boolean })[LOCATOR_PATCHED]\n ) {\n return locator;\n }\n\n defineHiddenProperty(locator, \"extract\", createLocatorExtract(locator));\n\n const markerTarget = locator as unknown as Record<PropertyKey, unknown>;\n\n if (\n typeof locator.describe === \"function\" &&\n !markerTarget[LOCATOR_DESCRIBE_WRAPPED]\n ) {\n const originalDescribe = locator.describe.bind(locator);\n locator.describe = ((\n description: string,\n options?: LocatorDescribeOptions,\n ) => {\n void options;\n const result = originalDescribe(description);\n return result ? augmentLocator(result as Locator) : result;\n }) as Locator[\"describe\"];\n\n defineHiddenProperty(locator, LOCATOR_DESCRIBE_WRAPPED, true);\n }\n\n defineHiddenProperty(locator, LOCATOR_PATCHED, true);\n\n return locator;\n}\n\nexport function augmentPage<T extends Page>(page: T): T {\n if ((page as unknown as { [PAGE_PATCHED]?: boolean })[PAGE_PATCHED]) {\n return page;\n }\n\n const originalLocator = page.locator.bind(page);\n page.locator = ((...args: Parameters<Page[\"locator\"]>) => {\n const locator = originalLocator(...args);\n return augmentLocator(locator);\n }) as Page[\"locator\"];\n\n defineHiddenProperty(page, \"extract\", createPageExtract(page));\n defineHiddenProperty(page, PAGE_PATCHED, true);\n\n return page;\n}\n\nexport function augmentBrowserContext<T extends BrowserContext>(context: T): T {\n if (\n (context as unknown as { [CONTEXT_PATCHED]?: boolean })[CONTEXT_PATCHED]\n ) {\n return context;\n }\n\n const originalNewPage = context.newPage.bind(context);\n context.newPage = (async (...args: Parameters<BrowserContext[\"newPage\"]>) => {\n const page = await originalNewPage(...args);\n return augmentPage(page);\n }) as BrowserContext[\"newPage\"];\n\n const originalPages = context.pages?.bind(context);\n if (originalPages) {\n context.pages = (() =>\n originalPages().map((page) =>\n augmentPage(page),\n )) as BrowserContext[\"pages\"];\n }\n\n if (!(context as unknown as { agent?: unknown }).agent) {\n defineHiddenProperty(context, \"agent\", createAgentStub());\n }\n\n defineHiddenProperty(context, CONTEXT_PATCHED, true);\n\n return context;\n}\n\nexport function augmentBrowser<T extends Browser>(browser: T): T {\n if (\n (browser as unknown as { [BROWSER_PATCHED]?: boolean })[BROWSER_PATCHED]\n ) {\n return browser;\n }\n\n const originalNewContext = browser.newContext.bind(browser);\n browser.newContext = (async (...args: Parameters<Browser[\"newContext\"]>) => {\n const context = await originalNewContext(...args);\n return augmentBrowserContext(context);\n }) as Browser[\"newContext\"];\n\n const originalNewPage = browser.newPage.bind(browser);\n browser.newPage = (async (...args: Parameters<Browser[\"newPage\"]>) => {\n const page = await originalNewPage(...args);\n return augmentPage(page);\n }) as Browser[\"newPage\"];\n\n const originalContexts = browser.contexts.bind(browser);\n browser.contexts = (() =>\n originalContexts().map((context) =>\n augmentBrowserContext(context),\n )) as Browser[\"contexts\"];\n\n if (!(browser as unknown as { agent?: unknown }).agent) {\n defineHiddenProperty(browser, \"agent\", createAgentStub());\n }\n\n defineHiddenProperty(browser, BROWSER_PATCHED, true);\n\n return browser;\n}\n\nexport function augmentBrowserType<TBrowser extends Browser>(\n browserType: BrowserType<TBrowser>,\n): BrowserType<TBrowser> {\n if (\n (browserType as unknown as { [BROWSER_TYPE_PATCHED]?: boolean })[\n BROWSER_TYPE_PATCHED\n ]\n ) {\n return browserType;\n }\n\n const originalLaunch = browserType.launch.bind(browserType);\n browserType.launch = (async (\n ...args: Parameters<BrowserType<TBrowser>[\"launch\"]>\n ) => {\n const browser = await originalLaunch(...args);\n return augmentBrowser(browser);\n }) as BrowserType<TBrowser>[\"launch\"];\n\n const originalConnect = browserType.connect?.bind(browserType);\n if (originalConnect) {\n browserType.connect = (async (\n ...args: Parameters<NonNullable<BrowserType<TBrowser>[\"connect\"]>>\n ) => {\n const browser = await originalConnect(...args);\n return augmentBrowser(browser);\n }) as NonNullable<BrowserType<TBrowser>[\"connect\"]>;\n }\n\n const originalConnectOverCDP = browserType.connectOverCDP?.bind(browserType);\n if (originalConnectOverCDP) {\n browserType.connectOverCDP = (async (\n ...args: Parameters<NonNullable<BrowserType<TBrowser>[\"connectOverCDP\"]>>\n ) => {\n const browser = await originalConnectOverCDP(...args);\n return augmentBrowser(browser);\n }) as NonNullable<BrowserType<TBrowser>[\"connectOverCDP\"]>;\n }\n\n const originalLaunchPersistentContext =\n browserType.launchPersistentContext?.bind(browserType);\n if (originalLaunchPersistentContext) {\n browserType.launchPersistentContext = (async (\n ...args: Parameters<\n NonNullable<BrowserType<TBrowser>[\"launchPersistentContext\"]>\n >\n ) => {\n const context = await originalLaunchPersistentContext(...args);\n return augmentBrowserContext(context);\n }) as NonNullable<BrowserType<TBrowser>[\"launchPersistentContext\"]>;\n }\n\n defineHiddenProperty(browserType, BROWSER_TYPE_PATCHED, true);\n\n return browserType;\n}\n","import { Page, Locator } from \"@stablyai/internal-playwright-test\";\n\nexport function isPage(candidate: unknown): candidate is Page {\n return (\n typeof candidate === \"object\" &&\n candidate !== null &&\n typeof (candidate as Page).screenshot === \"function\" &&\n typeof (candidate as Page).goto === \"function\"\n );\n}\n\nexport function isLocator(candidate: unknown): candidate is Locator {\n return (\n typeof candidate === \"object\" &&\n candidate !== null &&\n typeof (candidate as Locator).screenshot === \"function\" &&\n typeof (candidate as Locator).nth === \"function\"\n );\n}\n","import { isObject } from \"../type-predicate/is-object\";\nimport { requireApiKey } from \"../runtime\";\nimport { SDK_METADATA_HEADERS } from \"./metadata\";\n\nconst PROMPT_ASSERTION_ENDPOINT = \"https://api.stably.ai/internal/v1/assert\";\n\ntype ParsedSuccessResponse = { success: boolean; reason?: string };\n\nconst parseSuccessResponse = (value: unknown): ParsedSuccessResponse => {\n if (!isObject(value)) {\n throw new Error(\"Verify prompt returned unexpected response shape\");\n }\n\n const { success, reason } = value;\n if (typeof success !== \"boolean\") {\n throw new Error(\"Verify prompt returned unexpected response shape\");\n }\n\n if (reason !== undefined && typeof reason !== \"string\") {\n throw new Error(\"Verify prompt returned unexpected response shape\");\n }\n\n return {\n success,\n reason,\n };\n};\n\ntype ParsedErrorResponse = { error: string };\n\nconst parseErrorResponse = (\n value: unknown,\n): ParsedErrorResponse | undefined => {\n if (!isObject(value)) {\n return undefined;\n }\n\n const { error } = value;\n return typeof error !== \"string\" ? undefined : { error };\n};\n\nexport async function verifyPrompt({\n prompt,\n screenshot,\n}: {\n prompt: string;\n screenshot: Uint8Array;\n}): Promise<{\n pass: boolean;\n reason?: string;\n}> {\n const apiKey = requireApiKey();\n\n const form = new FormData();\n form.append(\"prompt\", prompt);\n const u8 = Uint8Array.from(screenshot);\n const blob = new Blob([u8], { type: \"image/png\" });\n form.append(\"image\", blob, \"screenshot.png\");\n\n const response = await fetch(PROMPT_ASSERTION_ENDPOINT, {\n method: \"POST\",\n headers: {\n ...SDK_METADATA_HEADERS,\n Authorization: `Bearer ${apiKey}`,\n },\n body: form,\n });\n\n const parsed = await response.json().catch(() => undefined as unknown);\n\n if (response.ok) {\n const { success, reason } = parseSuccessResponse(parsed);\n return {\n pass: success,\n reason,\n };\n }\n\n const err = parseErrorResponse(parsed);\n throw new Error(\n `Verify prompt failed (${response.status})${err ? `: ${err.error}` : \"\"}`,\n );\n}\n","import pixelmatch from \"pixelmatch\";\nimport { PNG } from \"pngjs\";\nimport * as jpeg from \"jpeg-js\";\n\nconst isPng = (buffer: Buffer): boolean => {\n return (\n buffer.length >= 8 &&\n buffer[0] === 0x89 &&\n buffer[1] === 0x50 &&\n buffer[2] === 0x4e &&\n buffer[3] === 0x47 &&\n buffer[4] === 0x0d &&\n buffer[5] === 0x0a &&\n buffer[6] === 0x1a &&\n buffer[7] === 0x0a\n );\n};\n\nconst isJpeg = (buffer: Buffer): boolean => {\n return buffer.length >= 2 && buffer[0] === 0xff && buffer[1] === 0xd8;\n};\n\nconst decodeImage = (\n buffer: Buffer,\n): { data: Uint8Array; width: number; height: number } => {\n if (isPng(buffer)) {\n const png = PNG.sync.read(buffer);\n return { data: png.data, width: png.width, height: png.height };\n }\n if (isJpeg(buffer)) {\n const img = jpeg.decode(buffer, { maxMemoryUsageInMB: 1024 });\n return { data: img.data, width: img.width, height: img.height };\n }\n // Default to PNG decode; if it fails upstream, treat as different sizes\n const png = PNG.sync.read(buffer);\n return { data: png.data, width: png.width, height: png.height };\n};\n\nexport const imagesAreSimilar = ({\n image1,\n image2,\n threshold,\n}: {\n image1: Buffer;\n image2: Buffer;\n threshold: number;\n}): boolean => {\n const decodedImage1 = decodeImage(image1);\n const decodedImage2 = decodeImage(image2);\n if (\n decodedImage1.width !== decodedImage2.width ||\n decodedImage1.height !== decodedImage2.height\n ) {\n return false;\n }\n const diffRgbaData = new Uint8Array(\n decodedImage1.width * decodedImage1.height * 4,\n );\n const numDiffPixels = pixelmatch(\n decodedImage1.data,\n decodedImage2.data,\n diffRgbaData,\n decodedImage1.width,\n decodedImage1.height,\n { threshold },\n );\n\n return numDiffPixels === 0;\n};\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport type { ScreenshotPromptOptions } from \"./index\";\nimport { isPage } from \"./playwright-type-predicates\";\nimport { imagesAreSimilar } from \"./image-compare\";\n\nexport async function takeStableScreenshot(\n target: Page | Locator,\n options?: ScreenshotPromptOptions,\n): Promise<Buffer> {\n const page = isPage(target) ? target : target.page();\n\n // Use a small budget for stabilization within the overall assertion timeout.\n // We allocate up to 25% of the total timeout (bounded between 300ms and 2000ms).\n const totalTimeout =\n (options as { timeout?: number } | undefined)?.timeout ?? 5000;\n // Budget is 25% of the total timeout\n const stabilizationBudgetMs = Math.floor(totalTimeout * 0.25);\n const stabilityBudgetMs = Math.min(\n 2000,\n Math.max(300, stabilizationBudgetMs),\n );\n const deadline = Date.now() + stabilityBudgetMs;\n\n let actual: Buffer | undefined;\n let previous: Buffer | undefined;\n const pollIntervals = [0, 100, 250, 500];\n let isFirstIteration = true;\n\n while (true) {\n if (Date.now() >= deadline) break;\n const delay = pollIntervals.length ? pollIntervals.shift()! : 1000;\n if (delay) {\n await page.waitForTimeout(delay);\n }\n previous = actual;\n actual = await target.screenshot(options);\n if (\n !isFirstIteration &&\n actual &&\n previous &&\n imagesAreSimilar({\n image1: previous,\n image2: actual,\n threshold: options?.threshold ?? 0.02,\n })\n ) {\n return actual;\n }\n isFirstIteration = false;\n }\n return actual ?? (await target.screenshot(options));\n}\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport type { ScreenshotPromptOptions } from \"./index\";\n\nimport { isLocator, isPage } from \"./playwright-type-predicates\";\n\nimport { verifyPrompt } from \"./ai/verify-prompt\";\nimport { takeStableScreenshot } from \"./screenshot\";\n\ntype VerifyTargetType = \"page\" | \"locator\";\n\ntype MatcherContext = {\n isNot: boolean;\n message?: () => string;\n};\n\nfunction createFailureMessage({\n targetType,\n condition,\n didPass,\n isNot,\n reason,\n}: {\n targetType: VerifyTargetType;\n condition: string;\n didPass: boolean;\n isNot: boolean;\n reason?: string;\n}): string {\n const expectation = isNot ? \"not to satisfy\" : \"to satisfy\";\n const result = didPass ? \"it did\" : \"it did not\";\n\n let message = `Expected ${targetType} ${expectation} ${JSON.stringify(condition)}, but ${result}.`;\n if (reason) {\n message += `\\n\\nReason: ${reason}`;\n }\n\n return message;\n}\n\nexport const stablyPlaywrightMatchers = {\n async toMatchScreenshotPrompt(\n this: MatcherContext,\n received: Page | Locator,\n condition: string,\n options?: ScreenshotPromptOptions,\n ) {\n const target = isPage(received)\n ? received\n : isLocator(received)\n ? received\n : undefined;\n if (!target) {\n // Should never happen\n throw new Error(\n \"toMatchScreenshotPrompt only supports Playwright Page and Locator instances.\",\n );\n }\n const targetType: VerifyTargetType = isPage(target) ? \"page\" : \"locator\";\n\n // Wait for two consecutive identical screenshots before sending to AI\n const screenshot = await takeStableScreenshot(target, options);\n\n const verifyResult = await verifyPrompt({ prompt: condition, screenshot });\n\n return {\n pass: verifyResult.pass,\n message: () =>\n createFailureMessage({\n targetType,\n condition,\n didPass: verifyResult.pass,\n reason: verifyResult.reason,\n isNot: this.isNot,\n }),\n };\n },\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAI,mBAAuC,QAAQ,IAAI;AAEhD,SAAS,UAAU,QAAsB;AAC9C,qBAAmB;AACrB;AAEO,SAAS,YAAgC;AAC9C,SAAO;AACT;AAEO,SAAS,gBAAwB;AACtC,QAAM,SAAS,UAAU;AACzB,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AClBO,IAAM,WAAW,CAAC,UAAqD;AAC5E,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;;;ACAO,IAAM,uBAAuB;AAAA,EAClC,iBAAiB;AAAA,EACjB,oBAAoB;AACtB;;;ACcA,IAAM,mBAAmB;AAEzB,IAAM,SAA4B,MAAM;AACtC,MAAI;AACF,WAAO,QAAQ,aAAa;AAAA,EAC9B,QAAQ;AACN,WAAO;AAAA,EACT;AACF,GAAG;AAQH,IAAM,uBAAuB,CAAC,UAAgD;AAC5E,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,YAAY,MAAM;AAC1B,WAAO,WAAW;AAAA,EACpB;AAEA,SAAO,MAAM,YAAY,SAAS,OAAO,MAAM,UAAU;AAC3D;AAEA,IAAM,kBAAkB,CAAC,UAA2C;AAClE,SAAO,SAAS,KAAK,KAAK,OAAO,MAAM,UAAU;AACnD;AAEA,IAAM,yBAAN,cAAqC,MAAM;AAAA,EACzC,YACE,SACS,QACT;AACA,UAAM,OAAO;AAFJ;AAGT,SAAK,OAAO;AAAA,EACd;AACF;AAEA,eAAe,mBACb,QACA,OAC0B;AAC1B,QAAM,SAAS,MAAM,OAAO,eAAe,KAAK;AAChD,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,IAAI,uBAAuB,qBAAqB,OAAO,MAAM,MAAM;AAAA,EAC3E;AAEA,SAAO,OAAO;AAChB;AAeA,eAAsB,QAAiC;AAAA,EACrD;AAAA,EACA;AAAA,EACA;AACF,GAAwE;AACtE,MAAI,UAAU,CAAC,OAAO;AACpB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aACJ,UAAU,QACN,OAAO;AAAA,IACL;AAAA,EACF,IACA;AAEN,QAAM,SAAS,cAAc;AAE7B,QAAM,OAAO,IAAI,SAAS;AAC1B,OAAK,OAAO,UAAU,MAAM;AAC5B,MAAI,YAAY;AACd,SAAK,OAAO,cAAc,KAAK,UAAU,UAAU,CAAC;AAAA,EACtD;AAEA,QAAM,YAAY,MAAM,cAAc,WAAW,EAAE,MAAM,MAAM,CAAC;AAChE,QAAM,KAAK,WAAW,KAAK,SAAS;AACpC,QAAM,OAAO,IAAI,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACjD,OAAK,OAAO,SAAS,MAAM,gBAAgB;AAE3C,QAAM,WAAW,MAAM,MAAM,kBAAkB;AAAA,IAC7C,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,GAAG;AAAA,MACH,eAAe,UAAU,MAAM;AAAA,IACjC;AAAA,IACA,MAAM;AAAA,EACR,CAAC;AAED,QAAM,MAAM,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,MAAoB;AAElE,MAAI,SAAS,IAAI;AACf,QAAI,CAAC,qBAAqB,GAAG,GAAG;AAC9B,YAAM,IAAI,MAAM,4CAA4C;AAAA,IAC9D;AAEA,QAAI,IAAI,YAAY,OAAO;AACzB,aAAO,IAAI;AAAA,IACb;AAEA,UAAM,EAAE,MAAM,IAAI;AAClB,WAAO,SACH,MAAM,mBAAmB,QAAQ,KAAK,IACtC,OAAO,UAAU,WACf,QACA,KAAK,UAAU,KAAK;AAAA,EAC5B;AAEA,QAAM,IAAI,MAAM,gBAAgB,GAAG,IAAI,IAAI,QAAQ,gBAAgB;AACrE;;;AC1HA,SAAS,cAAc,eAA8C;AACnE,QAAM,OAAQ,OACZ,QACA,YACG;AACH,QAAI,SAAS,QAAQ;AACnB,aAAO,QAAQ;AAAA,QACb;AAAA,QACA,QAAQ,QAAQ;AAAA,QAChB;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO,QAAQ,EAAE,QAAQ,cAAc,CAAC;AAAA,EAC1C;AAEA,SAAO;AACT;AAEO,IAAM,uBAAuB,CAAC,YACnC,cAAc,OAAO;AAEhB,IAAM,oBAAoB,CAAC,SAChC,cAAc,IAAI;;;ACvCb,SAAS,kBAGmB;AACjC,SAAO,OAAO,QAAgB,YAA0B;AACtD,kBAAc;AACd,SAAK;AACL,SAAK;AACL,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AACF;;;ACHA,IAAM,kBAAkB,OAAO,IAAI,kCAAkC;AACrE,IAAM,2BAA2B,OAAO;AAAA,EACtC;AACF;AACA,IAAM,eAAe,OAAO,IAAI,+BAA+B;AAC/D,IAAM,kBAAkB,OAAO,IAAI,kCAAkC;AACrE,IAAM,kBAAkB,OAAO,IAAI,kCAAkC;AACrE,IAAM,uBAAuB,OAAO,IAAI,sCAAsC;AAE9E,SAAS,qBACP,QACA,KACA,OACM;AACN,SAAO,eAAe,QAA6B,KAAK;AAAA,IACtD;AAAA,IACA,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,UAAU;AAAA,EACZ,CAAC;AACH;AAEO,SAAS,eAAkC,SAAe;AAC/D,MACG,QAAuD,eAAe,GACvE;AACA,WAAO;AAAA,EACT;AAEA,uBAAqB,SAAS,WAAW,qBAAqB,OAAO,CAAC;AAEtE,QAAM,eAAe;AAErB,MACE,OAAO,QAAQ,aAAa,cAC5B,CAAC,aAAa,wBAAwB,GACtC;AACA,UAAM,mBAAmB,QAAQ,SAAS,KAAK,OAAO;AACtD,YAAQ,WAAY,CAClB,aACA,YACG;AACH,WAAK;AACL,YAAM,SAAS,iBAAiB,WAAW;AAC3C,aAAO,SAAS,eAAe,MAAiB,IAAI;AAAA,IACtD;AAEA,yBAAqB,SAAS,0BAA0B,IAAI;AAAA,EAC9D;AAEA,uBAAqB,SAAS,iBAAiB,IAAI;AAEnD,SAAO;AACT;AAEO,SAAS,YAA4B,MAAY;AACtD,MAAK,KAAiD,YAAY,GAAG;AACnE,WAAO;AAAA,EACT;AAEA,QAAM,kBAAkB,KAAK,QAAQ,KAAK,IAAI;AAC9C,OAAK,UAAW,IAAI,SAAsC;AACxD,UAAM,UAAU,gBAAgB,GAAG,IAAI;AACvC,WAAO,eAAe,OAAO;AAAA,EAC/B;AAEA,uBAAqB,MAAM,WAAW,kBAAkB,IAAI,CAAC;AAC7D,uBAAqB,MAAM,cAAc,IAAI;AAE7C,SAAO;AACT;AAEO,SAAS,sBAAgD,SAAe;AAC7E,MACG,QAAuD,eAAe,GACvE;AACA,WAAO;AAAA,EACT;AAEA,QAAM,kBAAkB,QAAQ,QAAQ,KAAK,OAAO;AACpD,UAAQ,UAAW,UAAU,SAAgD;AAC3E,UAAM,OAAO,MAAM,gBAAgB,GAAG,IAAI;AAC1C,WAAO,YAAY,IAAI;AAAA,EACzB;AAEA,QAAM,gBAAgB,QAAQ,OAAO,KAAK,OAAO;AACjD,MAAI,eAAe;AACjB,YAAQ,QAAS,MACf,cAAc,EAAE;AAAA,MAAI,CAAC,SACnB,YAAY,IAAI;AAAA,IAClB;AAAA,EACJ;AAEA,MAAI,CAAE,QAA2C,OAAO;AACtD,yBAAqB,SAAS,SAAS,gBAAgB,CAAC;AAAA,EAC1D;AAEA,uBAAqB,SAAS,iBAAiB,IAAI;AAEnD,SAAO;AACT;AAEO,SAAS,eAAkC,SAAe;AAC/D,MACG,QAAuD,eAAe,GACvE;AACA,WAAO;AAAA,EACT;AAEA,QAAM,qBAAqB,QAAQ,WAAW,KAAK,OAAO;AAC1D,UAAQ,aAAc,UAAU,SAA4C;AAC1E,UAAM,UAAU,MAAM,mBAAmB,GAAG,IAAI;AAChD,WAAO,sBAAsB,OAAO;AAAA,EACtC;AAEA,QAAM,kBAAkB,QAAQ,QAAQ,KAAK,OAAO;AACpD,UAAQ,UAAW,UAAU,SAAyC;AACpE,UAAM,OAAO,MAAM,gBAAgB,GAAG,IAAI;AAC1C,WAAO,YAAY,IAAI;AAAA,EACzB;AAEA,QAAM,mBAAmB,QAAQ,SAAS,KAAK,OAAO;AACtD,UAAQ,WAAY,MAClB,iBAAiB,EAAE;AAAA,IAAI,CAAC,YACtB,sBAAsB,OAAO;AAAA,EAC/B;AAEF,MAAI,CAAE,QAA2C,OAAO;AACtD,yBAAqB,SAAS,SAAS,gBAAgB,CAAC;AAAA,EAC1D;AAEA,uBAAqB,SAAS,iBAAiB,IAAI;AAEnD,SAAO;AACT;AAEO,SAAS,mBACd,aACuB;AACvB,MACG,YACC,oBACF,GACA;AACA,WAAO;AAAA,EACT;AAEA,QAAM,iBAAiB,YAAY,OAAO,KAAK,WAAW;AAC1D,cAAY,SAAU,UACjB,SACA;AACH,UAAM,UAAU,MAAM,eAAe,GAAG,IAAI;AAC5C,WAAO,eAAe,OAAO;AAAA,EAC/B;AAEA,QAAM,kBAAkB,YAAY,SAAS,KAAK,WAAW;AAC7D,MAAI,iBAAiB;AACnB,gBAAY,UAAW,UAClB,SACA;AACH,YAAM,UAAU,MAAM,gBAAgB,GAAG,IAAI;AAC7C,aAAO,eAAe,OAAO;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,yBAAyB,YAAY,gBAAgB,KAAK,WAAW;AAC3E,MAAI,wBAAwB;AAC1B,gBAAY,iBAAkB,UACzB,SACA;AACH,YAAM,UAAU,MAAM,uBAAuB,GAAG,IAAI;AACpD,aAAO,eAAe,OAAO;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,kCACJ,YAAY,yBAAyB,KAAK,WAAW;AACvD,MAAI,iCAAiC;AACnC,gBAAY,0BAA2B,UAClC,SAGA;AACH,YAAM,UAAU,MAAM,gCAAgC,GAAG,IAAI;AAC7D,aAAO,sBAAsB,OAAO;AAAA,IACtC;AAAA,EACF;AAEA,uBAAqB,aAAa,sBAAsB,IAAI;AAE5D,SAAO;AACT;;;AC5MO,SAAS,OAAO,WAAuC;AAC5D,SACE,OAAO,cAAc,YACrB,cAAc,QACd,OAAQ,UAAmB,eAAe,cAC1C,OAAQ,UAAmB,SAAS;AAExC;AAEO,SAAS,UAAU,WAA0C;AAClE,SACE,OAAO,cAAc,YACrB,cAAc,QACd,OAAQ,UAAsB,eAAe,cAC7C,OAAQ,UAAsB,QAAQ;AAE1C;;;ACdA,IAAM,4BAA4B;AAIlC,IAAM,uBAAuB,CAAC,UAA0C;AACtE,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AAEA,QAAM,EAAE,SAAS,OAAO,IAAI;AAC5B,MAAI,OAAO,YAAY,WAAW;AAChC,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AAEA,MAAI,WAAW,UAAa,OAAO,WAAW,UAAU;AACtD,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAIA,IAAM,qBAAqB,CACzB,UACoC;AACpC,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,WAAO;AAAA,EACT;AAEA,QAAM,EAAE,MAAM,IAAI;AAClB,SAAO,OAAO,UAAU,WAAW,SAAY,EAAE,MAAM;AACzD;AAEA,eAAsB,aAAa;AAAA,EACjC;AAAA,EACA;AACF,GAMG;AACD,QAAM,SAAS,cAAc;AAE7B,QAAM,OAAO,IAAI,SAAS;AAC1B,OAAK,OAAO,UAAU,MAAM;AAC5B,QAAM,KAAK,WAAW,KAAK,UAAU;AACrC,QAAM,OAAO,IAAI,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACjD,OAAK,OAAO,SAAS,MAAM,gBAAgB;AAE3C,QAAM,WAAW,MAAM,MAAM,2BAA2B;AAAA,IACtD,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,GAAG;AAAA,MACH,eAAe,UAAU,MAAM;AAAA,IACjC;AAAA,IACA,MAAM;AAAA,EACR,CAAC;AAED,QAAM,SAAS,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,MAAoB;AAErE,MAAI,SAAS,IAAI;AACf,UAAM,EAAE,SAAS,OAAO,IAAI,qBAAqB,MAAM;AACvD,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,mBAAmB,MAAM;AACrC,QAAM,IAAI;AAAA,IACR,yBAAyB,SAAS,MAAM,IAAI,MAAM,KAAK,IAAI,KAAK,KAAK,EAAE;AAAA,EACzE;AACF;;;AClFA,wBAAuB;AACvB,mBAAoB;AACpB,WAAsB;AAEtB,IAAM,QAAQ,CAAC,WAA4B;AACzC,SACE,OAAO,UAAU,KACjB,OAAO,CAAC,MAAM,OACd,OAAO,CAAC,MAAM,MACd,OAAO,CAAC,MAAM,MACd,OAAO,CAAC,MAAM,MACd,OAAO,CAAC,MAAM,MACd,OAAO,CAAC,MAAM,MACd,OAAO,CAAC,MAAM,MACd,OAAO,CAAC,MAAM;AAElB;AAEA,IAAM,SAAS,CAAC,WAA4B;AAC1C,SAAO,OAAO,UAAU,KAAK,OAAO,CAAC,MAAM,OAAQ,OAAO,CAAC,MAAM;AACnE;AAEA,IAAM,cAAc,CAClB,WACwD;AACxD,MAAI,MAAM,MAAM,GAAG;AACjB,UAAMA,OAAM,iBAAI,KAAK,KAAK,MAAM;AAChC,WAAO,EAAE,MAAMA,KAAI,MAAM,OAAOA,KAAI,OAAO,QAAQA,KAAI,OAAO;AAAA,EAChE;AACA,MAAI,OAAO,MAAM,GAAG;AAClB,UAAM,MAAW,YAAO,QAAQ,EAAE,oBAAoB,KAAK,CAAC;AAC5D,WAAO,EAAE,MAAM,IAAI,MAAM,OAAO,IAAI,OAAO,QAAQ,IAAI,OAAO;AAAA,EAChE;AAEA,QAAM,MAAM,iBAAI,KAAK,KAAK,MAAM;AAChC,SAAO,EAAE,MAAM,IAAI,MAAM,OAAO,IAAI,OAAO,QAAQ,IAAI,OAAO;AAChE;AAEO,IAAM,mBAAmB,CAAC;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF,MAIe;AACb,QAAM,gBAAgB,YAAY,MAAM;AACxC,QAAM,gBAAgB,YAAY,MAAM;AACxC,MACE,cAAc,UAAU,cAAc,SACtC,cAAc,WAAW,cAAc,QACvC;AACA,WAAO;AAAA,EACT;AACA,QAAM,eAAe,IAAI;AAAA,IACvB,cAAc,QAAQ,cAAc,SAAS;AAAA,EAC/C;AACA,QAAM,oBAAgB,kBAAAC;AAAA,IACpB,cAAc;AAAA,IACd,cAAc;AAAA,IACd;AAAA,IACA,cAAc;AAAA,IACd,cAAc;AAAA,IACd,EAAE,UAAU;AAAA,EACd;AAEA,SAAO,kBAAkB;AAC3B;;;AC/DA,eAAsB,qBACpB,QACA,SACiB;AACjB,QAAM,OAAO,OAAO,MAAM,IAAI,SAAS,OAAO,KAAK;AAInD,QAAM,eACH,SAA8C,WAAW;AAE5D,QAAM,wBAAwB,KAAK,MAAM,eAAe,IAAI;AAC5D,QAAM,oBAAoB,KAAK;AAAA,IAC7B;AAAA,IACA,KAAK,IAAI,KAAK,qBAAqB;AAAA,EACrC;AACA,QAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,MAAI;AACJ,MAAI;AACJ,QAAM,gBAAgB,CAAC,GAAG,KAAK,KAAK,GAAG;AACvC,MAAI,mBAAmB;AAEvB,SAAO,MAAM;AACX,QAAI,KAAK,IAAI,KAAK,SAAU;AAC5B,UAAM,QAAQ,cAAc,SAAS,cAAc,MAAM,IAAK;AAC9D,QAAI,OAAO;AACT,YAAM,KAAK,eAAe,KAAK;AAAA,IACjC;AACA,eAAW;AACX,aAAS,MAAM,OAAO,WAAW,OAAO;AACxC,QACE,CAAC,oBACD,UACA,YACA,iBAAiB;AAAA,MACf,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,WAAW,SAAS,aAAa;AAAA,IACnC,CAAC,GACD;AACA,aAAO;AAAA,IACT;AACA,uBAAmB;AAAA,EACrB;AACA,SAAO,UAAW,MAAM,OAAO,WAAW,OAAO;AACnD;;;ACpCA,SAAS,qBAAqB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMW;AACT,QAAM,cAAc,QAAQ,mBAAmB;AAC/C,QAAM,SAAS,UAAU,WAAW;AAEpC,MAAI,UAAU,YAAY,UAAU,IAAI,WAAW,IAAI,KAAK,UAAU,SAAS,CAAC,SAAS,MAAM;AAC/F,MAAI,QAAQ;AACV,eAAW;AAAA;AAAA,UAAe,MAAM;AAAA,EAClC;AAEA,SAAO;AACT;AAEO,IAAM,2BAA2B;AAAA,EACtC,MAAM,wBAEJ,UACA,WACA,SACA;AACA,UAAM,SAAS,OAAO,QAAQ,IAC1B,WACA,UAAU,QAAQ,IAChB,WACA;AACN,QAAI,CAAC,QAAQ;AAEX,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,aAA+B,OAAO,MAAM,IAAI,SAAS;AAG/D,UAAM,aAAa,MAAM,qBAAqB,QAAQ,OAAO;AAE7D,UAAM,eAAe,MAAM,aAAa,EAAE,QAAQ,WAAW,WAAW,CAAC;AAEzE,WAAO;AAAA,MACL,MAAM,aAAa;AAAA,MACnB,SAAS,MACP,qBAAqB;AAAA,QACnB;AAAA,QACA;AAAA,QACA,SAAS,aAAa;AAAA,QACtB,QAAQ,aAAa;AAAA,QACrB,OAAO,KAAK;AAAA,MACd,CAAC;AAAA,IACL;AAAA,EACF;AACF;","names":["png","pixelmatch"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/runtime.ts","../src/type-predicate/is-object.ts","../src/ai/metadata.ts","../src/ai/extract.ts","../src/playwright-augment/methods/extract.ts","../src/playwright-augment/methods/agent.ts","../src/playwright-augment/augment.ts","../src/playwright-type-predicates.ts","../src/ai/verify-prompt.ts","../../../node_modules/.pnpm/pixelmatch@7.1.0/node_modules/pixelmatch/index.js","../src/image-compare.ts","../src/screenshot.ts","../src/expect.ts"],"sourcesContent":["import type { Page } from \"@stablyai/internal-playwright-test\";\nimport type { LocatorDescribeOptions } from \"./playwright-augment/augment\";\nimport type { ExtractSchema, SchemaOutput } from \"./ai/extract\";\n\nimport {\n augmentBrowser,\n augmentBrowserContext,\n augmentBrowserType,\n augmentLocator,\n augmentPage,\n} from \"./playwright-augment/augment\";\nimport { stablyPlaywrightMatchers } from \"./expect\";\nimport { requireApiKey } from \"./runtime\";\n\nexport { setApiKey } from \"./runtime\";\n\nexport type { LocatorDescribeOptions } from \"./playwright-augment/augment\";\nexport type { ExtractSchema, SchemaOutput } from \"./ai/extract\";\nexport type ScreenshotPromptOptions =\n import(\"@stablyai/internal-playwright-test\").PageAssertionsToHaveScreenshotOptions;\nexport {\n augmentBrowser,\n augmentBrowserContext,\n augmentBrowserType,\n augmentLocator,\n augmentPage,\n stablyPlaywrightMatchers,\n requireApiKey,\n};\n\nexport interface Expect<T = Page> {\n toMatchScreenshotPrompt(\n condition: string,\n options?: ScreenshotPromptOptions,\n ): Promise<void>;\n}\n\ndeclare module \"@stablyai/internal-playwright-test\" {\n interface Locator {\n /**\n * Extracts information from this locator using Stably AI.\n *\n * Takes a screenshot of the locator and uses AI to extract information based on the\n * provided prompt. When a schema is provided, the extracted data is validated and\n * typed according to the schema.\n *\n * @param prompt - A natural language description of what information to extract\n * @returns A string containing the extracted information\n */\n extract(prompt: string): Promise<string>;\n /**\n * Extracts information from this locator using Stably AI.\n *\n * Takes a screenshot of the locator and uses AI to extract information based on the\n * provided prompt. The extracted data is validated and typed according to the schema.\n *\n * @param prompt - A natural language description of what information to extract\n * @param options - Configuration object containing the Zod schema for validation\n * @param options.schema - Zod schema to validate and type the extracted data\n * @returns Typed data matching the provided schema\n */\n extract<T extends ExtractSchema>(\n prompt: string,\n options: { schema: T },\n ): Promise<SchemaOutput<T>>;\n describe(description: string, options?: LocatorDescribeOptions): Locator;\n }\n\n interface Page {\n /**\n * Extracts information from this page using Stably AI.\n *\n * Takes a screenshot of the page and uses AI to extract information based on the\n * provided prompt. When a schema is provided, the extracted data is validated and\n * typed according to the schema.\n *\n * @param prompt - A natural language description of what information to extract\n * @returns A string containing the extracted information\n */\n extract(prompt: string): Promise<string>;\n /**\n * Extracts information from this page using Stably AI.\n *\n * Takes a screenshot of the page and uses AI to extract information based on the\n * provided prompt. The extracted data is validated and typed according to the schema.\n *\n * @param prompt - A natural language description of what information to extract\n * @param options - Configuration object containing the Zod schema for validation\n * @param options.schema - Zod schema to validate and type the extracted data\n * @returns Typed data matching the provided schema\n */\n extract<T extends ExtractSchema>(\n prompt: string,\n options: { schema: T },\n ): Promise<SchemaOutput<T>>;\n }\n\n interface BrowserContext {\n agent(\n prompt: string,\n options: { page: Page; maxCycles?: number },\n ): Promise<{ success: boolean }>;\n }\n\n interface Browser {\n agent(\n prompt: string,\n options: { page: Page; maxCycles?: number },\n ): Promise<{ success: boolean }>;\n }\n}\n","let configuredApiKey: string | undefined = process.env.STABLY_API_KEY;\n\nexport function setApiKey(apiKey: string): void {\n configuredApiKey = apiKey;\n}\n\nexport function getApiKey(): string | undefined {\n return configuredApiKey;\n}\n\nexport function requireApiKey(): string {\n const apiKey = getApiKey();\n if (!apiKey) {\n throw new Error(\n \"Missing Stably API key. Call setApiKey(apiKey) or set the STABLY_API_KEY environment variable.\",\n );\n }\n return apiKey;\n}\n","export const isObject = (value: unknown): value is Record<string, unknown> => {\n return typeof value === \"object\" && value !== null;\n};\n","declare const __PACKAGE_VERSION__: string;\n\nexport const SDK_METADATA_HEADERS = {\n \"X-Client-Name\": \"stably-playwright-sdk-js\",\n \"X-Client-Version\": __PACKAGE_VERSION__,\n};\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport type * as z4 from \"zod/v4/core\";\nimport { requireApiKey } from \"../runtime\";\nimport { isObject } from \"../type-predicate/is-object\";\nimport { SDK_METADATA_HEADERS } from \"./metadata\";\n\ntype ZodV4 = typeof import(\"zod/v4/core\");\n\nexport interface ExtractSchema extends z4.$ZodType {\n safeParseAsync(\n data: unknown,\n params?: z4.ParseContext<z4.$ZodIssue>,\n ): Promise<z4.util.SafeParseResult<z4.output<this>>>;\n}\n\nexport type SchemaOutput<T extends ExtractSchema> = z4.output<T>;\n\ntype ExtractIssue = z4.$ZodIssue;\n\nconst EXTRACT_ENDPOINT = \"https://api.stably.ai/internal/v2/extract\";\n\nconst zodV4: ZodV4 | undefined = (() => {\n try {\n return require(\"zod/v4/core\") as ZodV4;\n } catch {\n return undefined;\n }\n})();\n\ntype ExtractionSuccess = { success: true; value: unknown };\ntype ExtractionFailure = { success: false; error: string };\ntype ExtractionResponse = ExtractionSuccess | ExtractionFailure;\n\ntype ErrorResponse = { error: string };\n\nconst isExtractionResponse = (value: unknown): value is ExtractionResponse => {\n if (!isObject(value)) {\n return false;\n }\n\n if (value.success === true) {\n return \"value\" in value;\n }\n\n return value.success === false && typeof value.error === \"string\";\n};\n\nconst isErrorResponse = (value: unknown): value is ErrorResponse => {\n return isObject(value) && typeof value.error === \"string\";\n};\n\nclass ExtractValidationError extends Error {\n constructor(\n message: string,\n readonly issues: ReadonlyArray<ExtractIssue>,\n ) {\n super(message);\n this.name = \"ExtractValidationError\";\n }\n}\n\nasync function validateWithSchema<T extends ExtractSchema>(\n schema: T,\n value: unknown,\n): Promise<SchemaOutput<T>> {\n const result = await schema.safeParseAsync(value);\n if (!result.success) {\n throw new ExtractValidationError(\"Validation failed\", result.error.issues);\n }\n\n return result.data;\n}\n\ntype BaseExtractArgs = {\n prompt: string;\n pageOrLocator: Page | Locator;\n};\n\ntype ExtractArgsWithSchema<T extends ExtractSchema> = BaseExtractArgs & {\n schema: T;\n};\n\nexport async function extract(args: BaseExtractArgs): Promise<string>;\nexport async function extract<T extends ExtractSchema>(\n args: ExtractArgsWithSchema<T>,\n): Promise<SchemaOutput<T>>;\nexport async function extract<T extends ExtractSchema>({\n prompt,\n pageOrLocator,\n schema,\n}: BaseExtractArgs & { schema?: T }): Promise<string | SchemaOutput<T>> {\n if (schema && !zodV4) {\n throw new Error(\n \"Schema support requires installing zod@4. Please add it to enable schemas.\",\n );\n }\n\n const jsonSchema =\n schema && zodV4\n ? zodV4?.toJSONSchema(\n schema as unknown as Parameters<ZodV4[\"toJSONSchema\"]>[0],\n )\n : undefined;\n\n const apiKey = requireApiKey();\n\n const form = new FormData();\n form.append(\"prompt\", prompt);\n if (jsonSchema) {\n form.append(\"jsonSchema\", JSON.stringify(jsonSchema));\n }\n\n const pngBuffer = await pageOrLocator.screenshot({ type: \"png\" }); // Buffer\n const u8 = Uint8Array.from(pngBuffer); // strips Buffer type → plain Uint8Array\n const blob = new Blob([u8], { type: \"image/png\" });\n form.append(\"image\", blob, \"screenshot.png\");\n\n const response = await fetch(EXTRACT_ENDPOINT, {\n method: \"POST\",\n headers: {\n ...SDK_METADATA_HEADERS,\n Authorization: `Bearer ${apiKey}`,\n },\n body: form,\n });\n\n const raw = await response.json().catch(() => undefined as unknown);\n\n if (response.ok) {\n if (!isExtractionResponse(raw)) {\n throw new Error(\"Extract returned unexpected response shape\");\n }\n\n if (raw.success === false) {\n return raw.error;\n }\n\n const { value } = raw;\n return schema\n ? await validateWithSchema(schema, value)\n : typeof value === \"string\"\n ? value\n : JSON.stringify(value);\n }\n\n throw new Error(isErrorResponse(raw) ? raw.error : \"Extract failed\");\n}\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport {\n type ExtractSchema,\n type SchemaOutput,\n extract,\n} from \"../../ai/extract\";\n\ntype ExtractOptions<T extends ExtractSchema> = {\n schema: T;\n};\n\ntype ExtractMethod = {\n (prompt: string): Promise<string>;\n <T extends ExtractSchema>(\n prompt: string,\n options: ExtractOptions<T>,\n ): Promise<SchemaOutput<T>>;\n};\n\ntype LocatorExtract = ExtractMethod;\ntype PageExtract = ExtractMethod;\n\ntype ExtractSubject = Locator | Page;\n\nfunction createExtract(pageOrLocator: ExtractSubject): ExtractMethod {\n const impl = (async (\n prompt: string,\n options?: ExtractOptions<ExtractSchema>,\n ) => {\n if (options?.schema) {\n return extract({\n prompt,\n schema: options.schema,\n pageOrLocator,\n });\n }\n\n return extract({ prompt, pageOrLocator });\n }) as ExtractMethod;\n\n return impl;\n}\n\nexport const createLocatorExtract = (locator: Locator): LocatorExtract =>\n createExtract(locator);\n\nexport const createPageExtract = (page: Page): PageExtract =>\n createExtract(page);\n","import type { Page } from \"@stablyai/internal-playwright-test\";\nimport { requireApiKey } from \"../../runtime\";\n\ntype AgentOptions = {\n page: Page;\n maxCycles?: number;\n};\n\nexport function createAgentStub(): (\n prompt: string,\n options: AgentOptions,\n) => Promise<{ success: boolean }> {\n return async (prompt: string, options: AgentOptions) => {\n requireApiKey();\n void prompt;\n void options;\n return { success: true };\n };\n}\n","import type {\n Browser,\n BrowserContext,\n BrowserType,\n Locator,\n Page,\n} from \"@stablyai/internal-playwright-test\";\n\nimport { createLocatorExtract, createPageExtract } from \"./methods/extract\";\nimport { createAgentStub } from \"./methods/agent\";\n\nexport interface LocatorDescribeOptions {\n autoHeal?: boolean;\n}\n\nconst LOCATOR_PATCHED = Symbol.for(\"stably.playwright.locatorPatched\");\nconst LOCATOR_DESCRIBE_WRAPPED = Symbol.for(\n \"stably.playwright.locatorDescribeWrapped\",\n);\nconst PAGE_PATCHED = Symbol.for(\"stably.playwright.pagePatched\");\nconst CONTEXT_PATCHED = Symbol.for(\"stably.playwright.contextPatched\");\nconst BROWSER_PATCHED = Symbol.for(\"stably.playwright.browserPatched\");\nconst BROWSER_TYPE_PATCHED = Symbol.for(\"stably.playwright.browserTypePatched\");\n\nfunction defineHiddenProperty<T, K extends PropertyKey>(\n target: T,\n key: K,\n value: unknown,\n): void {\n Object.defineProperty(target as unknown as object, key, {\n value,\n enumerable: false,\n configurable: true,\n writable: true,\n });\n}\n\nexport function augmentLocator<T extends Locator>(locator: T): T {\n if (\n (locator as unknown as { [LOCATOR_PATCHED]?: boolean })[LOCATOR_PATCHED]\n ) {\n return locator;\n }\n\n defineHiddenProperty(locator, \"extract\", createLocatorExtract(locator));\n\n const markerTarget = locator as unknown as Record<PropertyKey, unknown>;\n\n if (\n typeof locator.describe === \"function\" &&\n !markerTarget[LOCATOR_DESCRIBE_WRAPPED]\n ) {\n const originalDescribe = locator.describe.bind(locator);\n locator.describe = ((\n description: string,\n options?: LocatorDescribeOptions,\n ) => {\n void options;\n const result = originalDescribe(description);\n return result ? augmentLocator(result as Locator) : result;\n }) as Locator[\"describe\"];\n\n defineHiddenProperty(locator, LOCATOR_DESCRIBE_WRAPPED, true);\n }\n\n defineHiddenProperty(locator, LOCATOR_PATCHED, true);\n\n return locator;\n}\n\nexport function augmentPage<T extends Page>(page: T): T {\n if ((page as unknown as { [PAGE_PATCHED]?: boolean })[PAGE_PATCHED]) {\n return page;\n }\n\n const originalLocator = page.locator.bind(page);\n page.locator = ((...args: Parameters<Page[\"locator\"]>) => {\n const locator = originalLocator(...args);\n return augmentLocator(locator);\n }) as Page[\"locator\"];\n\n defineHiddenProperty(page, \"extract\", createPageExtract(page));\n defineHiddenProperty(page, PAGE_PATCHED, true);\n\n return page;\n}\n\nexport function augmentBrowserContext<T extends BrowserContext>(context: T): T {\n if (\n (context as unknown as { [CONTEXT_PATCHED]?: boolean })[CONTEXT_PATCHED]\n ) {\n return context;\n }\n\n const originalNewPage = context.newPage.bind(context);\n context.newPage = (async (...args: Parameters<BrowserContext[\"newPage\"]>) => {\n const page = await originalNewPage(...args);\n return augmentPage(page);\n }) as BrowserContext[\"newPage\"];\n\n const originalPages = context.pages?.bind(context);\n if (originalPages) {\n context.pages = (() =>\n originalPages().map((page) =>\n augmentPage(page),\n )) as BrowserContext[\"pages\"];\n }\n\n if (!(context as unknown as { agent?: unknown }).agent) {\n defineHiddenProperty(context, \"agent\", createAgentStub());\n }\n\n defineHiddenProperty(context, CONTEXT_PATCHED, true);\n\n return context;\n}\n\nexport function augmentBrowser<T extends Browser>(browser: T): T {\n if (\n (browser as unknown as { [BROWSER_PATCHED]?: boolean })[BROWSER_PATCHED]\n ) {\n return browser;\n }\n\n const originalNewContext = browser.newContext.bind(browser);\n browser.newContext = (async (...args: Parameters<Browser[\"newContext\"]>) => {\n const context = await originalNewContext(...args);\n return augmentBrowserContext(context);\n }) as Browser[\"newContext\"];\n\n const originalNewPage = browser.newPage.bind(browser);\n browser.newPage = (async (...args: Parameters<Browser[\"newPage\"]>) => {\n const page = await originalNewPage(...args);\n return augmentPage(page);\n }) as Browser[\"newPage\"];\n\n const originalContexts = browser.contexts.bind(browser);\n browser.contexts = (() =>\n originalContexts().map((context) =>\n augmentBrowserContext(context),\n )) as Browser[\"contexts\"];\n\n if (!(browser as unknown as { agent?: unknown }).agent) {\n defineHiddenProperty(browser, \"agent\", createAgentStub());\n }\n\n defineHiddenProperty(browser, BROWSER_PATCHED, true);\n\n return browser;\n}\n\nexport function augmentBrowserType<TBrowser extends Browser>(\n browserType: BrowserType<TBrowser>,\n): BrowserType<TBrowser> {\n if (\n (browserType as unknown as { [BROWSER_TYPE_PATCHED]?: boolean })[\n BROWSER_TYPE_PATCHED\n ]\n ) {\n return browserType;\n }\n\n const originalLaunch = browserType.launch.bind(browserType);\n browserType.launch = (async (\n ...args: Parameters<BrowserType<TBrowser>[\"launch\"]>\n ) => {\n const browser = await originalLaunch(...args);\n return augmentBrowser(browser);\n }) as BrowserType<TBrowser>[\"launch\"];\n\n const originalConnect = browserType.connect?.bind(browserType);\n if (originalConnect) {\n browserType.connect = (async (\n ...args: Parameters<NonNullable<BrowserType<TBrowser>[\"connect\"]>>\n ) => {\n const browser = await originalConnect(...args);\n return augmentBrowser(browser);\n }) as NonNullable<BrowserType<TBrowser>[\"connect\"]>;\n }\n\n const originalConnectOverCDP = browserType.connectOverCDP?.bind(browserType);\n if (originalConnectOverCDP) {\n browserType.connectOverCDP = (async (\n ...args: Parameters<NonNullable<BrowserType<TBrowser>[\"connectOverCDP\"]>>\n ) => {\n const browser = await originalConnectOverCDP(...args);\n return augmentBrowser(browser);\n }) as NonNullable<BrowserType<TBrowser>[\"connectOverCDP\"]>;\n }\n\n const originalLaunchPersistentContext =\n browserType.launchPersistentContext?.bind(browserType);\n if (originalLaunchPersistentContext) {\n browserType.launchPersistentContext = (async (\n ...args: Parameters<\n NonNullable<BrowserType<TBrowser>[\"launchPersistentContext\"]>\n >\n ) => {\n const context = await originalLaunchPersistentContext(...args);\n return augmentBrowserContext(context);\n }) as NonNullable<BrowserType<TBrowser>[\"launchPersistentContext\"]>;\n }\n\n defineHiddenProperty(browserType, BROWSER_TYPE_PATCHED, true);\n\n return browserType;\n}\n","import { Page, Locator } from \"@stablyai/internal-playwright-test\";\n\nexport function isPage(candidate: unknown): candidate is Page {\n return (\n typeof candidate === \"object\" &&\n candidate !== null &&\n typeof (candidate as Page).screenshot === \"function\" &&\n typeof (candidate as Page).goto === \"function\"\n );\n}\n\nexport function isLocator(candidate: unknown): candidate is Locator {\n return (\n typeof candidate === \"object\" &&\n candidate !== null &&\n typeof (candidate as Locator).screenshot === \"function\" &&\n typeof (candidate as Locator).nth === \"function\"\n );\n}\n","import { isObject } from \"../type-predicate/is-object\";\nimport { requireApiKey } from \"../runtime\";\nimport { SDK_METADATA_HEADERS } from \"./metadata\";\n\nconst PROMPT_ASSERTION_ENDPOINT = \"https://api.stably.ai/internal/v1/assert\";\n\ntype ParsedSuccessResponse = { success: boolean; reason?: string };\n\nconst parseSuccessResponse = (value: unknown): ParsedSuccessResponse => {\n if (!isObject(value)) {\n throw new Error(\"Verify prompt returned unexpected response shape\");\n }\n\n const { success, reason } = value;\n if (typeof success !== \"boolean\") {\n throw new Error(\"Verify prompt returned unexpected response shape\");\n }\n\n if (reason !== undefined && typeof reason !== \"string\") {\n throw new Error(\"Verify prompt returned unexpected response shape\");\n }\n\n return {\n success,\n reason,\n };\n};\n\ntype ParsedErrorResponse = { error: string };\n\nconst parseErrorResponse = (\n value: unknown,\n): ParsedErrorResponse | undefined => {\n if (!isObject(value)) {\n return undefined;\n }\n\n const { error } = value;\n return typeof error !== \"string\" ? undefined : { error };\n};\n\nexport async function verifyPrompt({\n prompt,\n screenshot,\n}: {\n prompt: string;\n screenshot: Uint8Array;\n}): Promise<{\n pass: boolean;\n reason?: string;\n}> {\n const apiKey = requireApiKey();\n\n const form = new FormData();\n form.append(\"prompt\", prompt);\n const u8 = Uint8Array.from(screenshot);\n const blob = new Blob([u8], { type: \"image/png\" });\n form.append(\"image\", blob, \"screenshot.png\");\n\n const response = await fetch(PROMPT_ASSERTION_ENDPOINT, {\n method: \"POST\",\n headers: {\n ...SDK_METADATA_HEADERS,\n Authorization: `Bearer ${apiKey}`,\n },\n body: form,\n });\n\n const parsed = await response.json().catch(() => undefined as unknown);\n\n if (response.ok) {\n const { success, reason } = parseSuccessResponse(parsed);\n return {\n pass: success,\n reason,\n };\n }\n\n const err = parseErrorResponse(parsed);\n throw new Error(\n `Verify prompt failed (${response.status})${err ? `: ${err.error}` : \"\"}`,\n );\n}\n","/**\n * Compare two equally sized images, pixel by pixel.\n *\n * @param {Uint8Array | Uint8ClampedArray} img1 First image data.\n * @param {Uint8Array | Uint8ClampedArray} img2 Second image data.\n * @param {Uint8Array | Uint8ClampedArray | void} output Image data to write the diff to, if provided.\n * @param {number} width Input images width.\n * @param {number} height Input images height.\n *\n * @param {Object} [options]\n * @param {number} [options.threshold=0.1] Matching threshold (0 to 1); smaller is more sensitive.\n * @param {boolean} [options.includeAA=false] Whether to skip anti-aliasing detection.\n * @param {number} [options.alpha=0.1] Opacity of original image in diff output.\n * @param {[number, number, number]} [options.aaColor=[255, 255, 0]] Color of anti-aliased pixels in diff output.\n * @param {[number, number, number]} [options.diffColor=[255, 0, 0]] Color of different pixels in diff output.\n * @param {[number, number, number]} [options.diffColorAlt=options.diffColor] Whether to detect dark on light differences between img1 and img2 and set an alternative color to differentiate between the two.\n * @param {boolean} [options.diffMask=false] Draw the diff over a transparent background (a mask).\n *\n * @return {number} The number of mismatched pixels.\n */\nexport default function pixelmatch(img1, img2, output, width, height, options = {}) {\n const {\n threshold = 0.1,\n alpha = 0.1,\n aaColor = [255, 255, 0],\n diffColor = [255, 0, 0],\n includeAA, diffColorAlt, diffMask\n } = options;\n\n if (!isPixelData(img1) || !isPixelData(img2) || (output && !isPixelData(output)))\n throw new Error('Image data: Uint8Array, Uint8ClampedArray or Buffer expected.');\n\n if (img1.length !== img2.length || (output && output.length !== img1.length))\n throw new Error('Image sizes do not match.');\n\n if (img1.length !== width * height * 4) throw new Error('Image data size does not match width/height.');\n\n // check if images are identical\n const len = width * height;\n const a32 = new Uint32Array(img1.buffer, img1.byteOffset, len);\n const b32 = new Uint32Array(img2.buffer, img2.byteOffset, len);\n let identical = true;\n\n for (let i = 0; i < len; i++) {\n if (a32[i] !== b32[i]) { identical = false; break; }\n }\n if (identical) { // fast path if identical\n if (output && !diffMask) {\n for (let i = 0; i < len; i++) drawGrayPixel(img1, 4 * i, alpha, output);\n }\n return 0;\n }\n\n // maximum acceptable square distance between two colors;\n // 35215 is the maximum possible value for the YIQ difference metric\n const maxDelta = 35215 * threshold * threshold;\n const [aaR, aaG, aaB] = aaColor;\n const [diffR, diffG, diffB] = diffColor;\n const [altR, altG, altB] = diffColorAlt || diffColor;\n let diff = 0;\n\n // compare each pixel of one image against the other one\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n\n const i = y * width + x;\n const pos = i * 4;\n\n // squared YUV distance between colors at this pixel position, negative if the img2 pixel is darker\n const delta = a32[i] === b32[i] ? 0 : colorDelta(img1, img2, pos, pos, false);\n\n // the color difference is above the threshold\n if (Math.abs(delta) > maxDelta) {\n // check it's a real rendering difference or just anti-aliasing\n const isAA = antialiased(img1, x, y, width, height, a32, b32) || antialiased(img2, x, y, width, height, b32, a32);\n if (!includeAA && isAA) {\n // one of the pixels is anti-aliasing; draw as yellow and do not count as difference\n // note that we do not include such pixels in a mask\n if (output && !diffMask) drawPixel(output, pos, aaR, aaG, aaB);\n\n } else {\n // found substantial difference not caused by anti-aliasing; draw it as such\n if (output) {\n if (delta < 0) {\n drawPixel(output, pos, altR, altG, altB);\n } else {\n drawPixel(output, pos, diffR, diffG, diffB);\n }\n }\n diff++;\n }\n\n } else if (output && !diffMask) {\n // pixels are similar; draw background as grayscale image blended with white\n drawGrayPixel(img1, pos, alpha, output);\n }\n }\n }\n\n // return the number of different pixels\n return diff;\n}\n\n/** @param {Uint8Array | Uint8ClampedArray} arr */\nfunction isPixelData(arr) {\n // work around instanceof Uint8Array not working properly in some Jest environments\n return ArrayBuffer.isView(arr) && arr.BYTES_PER_ELEMENT === 1;\n}\n\n/**\n * Check if a pixel is likely a part of anti-aliasing;\n * based on \"Anti-aliased Pixel and Intensity Slope Detector\" paper by V. Vysniauskas, 2009\n * @param {Uint8Array | Uint8ClampedArray} img\n * @param {number} x1\n * @param {number} y1\n * @param {number} width\n * @param {number} height\n * @param {Uint32Array} a32\n * @param {Uint32Array} b32\n */\nfunction antialiased(img, x1, y1, width, height, a32, b32) {\n const x0 = Math.max(x1 - 1, 0);\n const y0 = Math.max(y1 - 1, 0);\n const x2 = Math.min(x1 + 1, width - 1);\n const y2 = Math.min(y1 + 1, height - 1);\n const pos = y1 * width + x1;\n let zeroes = x1 === x0 || x1 === x2 || y1 === y0 || y1 === y2 ? 1 : 0;\n let min = 0;\n let max = 0;\n let minX = 0;\n let minY = 0;\n let maxX = 0;\n let maxY = 0;\n\n // go through 8 adjacent pixels\n for (let x = x0; x <= x2; x++) {\n for (let y = y0; y <= y2; y++) {\n if (x === x1 && y === y1) continue;\n\n // brightness delta between the center pixel and adjacent one\n const delta = colorDelta(img, img, pos * 4, (y * width + x) * 4, true);\n\n // count the number of equal, darker and brighter adjacent pixels\n if (delta === 0) {\n zeroes++;\n // if found more than 2 equal siblings, it's definitely not anti-aliasing\n if (zeroes > 2) return false;\n\n // remember the darkest pixel\n } else if (delta < min) {\n min = delta;\n minX = x;\n minY = y;\n\n // remember the brightest pixel\n } else if (delta > max) {\n max = delta;\n maxX = x;\n maxY = y;\n }\n }\n }\n\n // if there are no both darker and brighter pixels among siblings, it's not anti-aliasing\n if (min === 0 || max === 0) return false;\n\n // if either the darkest or the brightest pixel has 3+ equal siblings in both images\n // (definitely not anti-aliased), this pixel is anti-aliased\n return (hasManySiblings(a32, minX, minY, width, height) && hasManySiblings(b32, minX, minY, width, height)) ||\n (hasManySiblings(a32, maxX, maxY, width, height) && hasManySiblings(b32, maxX, maxY, width, height));\n}\n\n/**\n * Check if a pixel has 3+ adjacent pixels of the same color.\n * @param {Uint32Array} img\n * @param {number} x1\n * @param {number} y1\n * @param {number} width\n * @param {number} height\n */\nfunction hasManySiblings(img, x1, y1, width, height) {\n const x0 = Math.max(x1 - 1, 0);\n const y0 = Math.max(y1 - 1, 0);\n const x2 = Math.min(x1 + 1, width - 1);\n const y2 = Math.min(y1 + 1, height - 1);\n const val = img[y1 * width + x1];\n let zeroes = x1 === x0 || x1 === x2 || y1 === y0 || y1 === y2 ? 1 : 0;\n\n // go through 8 adjacent pixels\n for (let x = x0; x <= x2; x++) {\n for (let y = y0; y <= y2; y++) {\n if (x === x1 && y === y1) continue;\n zeroes += +(val === img[y * width + x]);\n if (zeroes > 2) return true;\n }\n }\n return false;\n}\n\n/**\n * Calculate color difference according to the paper \"Measuring perceived color difference\n * using YIQ NTSC transmission color space in mobile applications\" by Y. Kotsarenko and F. Ramos\n * @param {Uint8Array | Uint8ClampedArray} img1\n * @param {Uint8Array | Uint8ClampedArray} img2\n * @param {number} k\n * @param {number} m\n * @param {boolean} yOnly\n */\nfunction colorDelta(img1, img2, k, m, yOnly) {\n const r1 = img1[k];\n const g1 = img1[k + 1];\n const b1 = img1[k + 2];\n const a1 = img1[k + 3];\n const r2 = img2[m];\n const g2 = img2[m + 1];\n const b2 = img2[m + 2];\n const a2 = img2[m + 3];\n\n let dr = r1 - r2;\n let dg = g1 - g2;\n let db = b1 - b2;\n const da = a1 - a2;\n\n if (!dr && !dg && !db && !da) return 0;\n\n if (a1 < 255 || a2 < 255) { // blend pixels with background\n const rb = 48 + 159 * (k % 2);\n const gb = 48 + 159 * ((k / 1.618033988749895 | 0) % 2);\n const bb = 48 + 159 * ((k / 2.618033988749895 | 0) % 2);\n dr = (r1 * a1 - r2 * a2 - rb * da) / 255;\n dg = (g1 * a1 - g2 * a2 - gb * da) / 255;\n db = (b1 * a1 - b2 * a2 - bb * da) / 255;\n }\n\n const y = dr * 0.29889531 + dg * 0.58662247 + db * 0.11448223;\n\n if (yOnly) return y; // brightness difference only\n\n const i = dr * 0.59597799 - dg * 0.27417610 - db * 0.32180189;\n const q = dr * 0.21147017 - dg * 0.52261711 + db * 0.31114694;\n\n const delta = 0.5053 * y * y + 0.299 * i * i + 0.1957 * q * q;\n\n // encode whether the pixel lightens or darkens in the sign\n return y > 0 ? -delta : delta;\n}\n\n/**\n * @param {Uint8Array | Uint8ClampedArray} output\n * @param {number} pos\n * @param {number} r\n * @param {number} g\n * @param {number} b\n */\nfunction drawPixel(output, pos, r, g, b) {\n output[pos + 0] = r;\n output[pos + 1] = g;\n output[pos + 2] = b;\n output[pos + 3] = 255;\n}\n\n/**\n * @param {Uint8Array | Uint8ClampedArray} img\n * @param {number} i\n * @param {number} alpha\n * @param {Uint8Array | Uint8ClampedArray} output\n */\nfunction drawGrayPixel(img, i, alpha, output) {\n const val = 255 + (img[i] * 0.29889531 + img[i + 1] * 0.58662247 + img[i + 2] * 0.11448223 - 255) * alpha * img[i + 3] / 255;\n drawPixel(output, i, val, val, val);\n}\n","// Note: pixelmatch seems to be pure ESM so mark it as noExternal in tsup config\nimport pixelmatch from \"pixelmatch\";\nimport { PNG } from \"pngjs\";\nimport * as jpeg from \"jpeg-js\";\n\nconst isPng = (buffer: Buffer): boolean => {\n return (\n buffer.length >= 8 &&\n buffer[0] === 0x89 &&\n buffer[1] === 0x50 &&\n buffer[2] === 0x4e &&\n buffer[3] === 0x47 &&\n buffer[4] === 0x0d &&\n buffer[5] === 0x0a &&\n buffer[6] === 0x1a &&\n buffer[7] === 0x0a\n );\n};\n\nconst isJpeg = (buffer: Buffer): boolean => {\n return buffer.length >= 2 && buffer[0] === 0xff && buffer[1] === 0xd8;\n};\n\nconst decodeImage = (\n buffer: Buffer,\n): { data: Uint8Array; width: number; height: number } => {\n if (isPng(buffer)) {\n const png = PNG.sync.read(buffer);\n return { data: png.data, width: png.width, height: png.height };\n }\n if (isJpeg(buffer)) {\n const img = jpeg.decode(buffer, { maxMemoryUsageInMB: 1024 });\n return { data: img.data, width: img.width, height: img.height };\n }\n // Default to PNG decode; if it fails upstream, treat as different sizes\n const png = PNG.sync.read(buffer);\n return { data: png.data, width: png.width, height: png.height };\n};\n\nexport const imagesAreSimilar = ({\n image1,\n image2,\n threshold,\n}: {\n image1: Buffer;\n image2: Buffer;\n threshold: number;\n}): boolean => {\n const decodedImage1 = decodeImage(image1);\n const decodedImage2 = decodeImage(image2);\n if (\n decodedImage1.width !== decodedImage2.width ||\n decodedImage1.height !== decodedImage2.height\n ) {\n return false;\n }\n const diffRgbaData = new Uint8Array(\n decodedImage1.width * decodedImage1.height * 4,\n );\n const numDiffPixels = pixelmatch(\n decodedImage1.data,\n decodedImage2.data,\n diffRgbaData,\n decodedImage1.width,\n decodedImage1.height,\n { threshold },\n );\n\n return numDiffPixels === 0;\n};\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport type { ScreenshotPromptOptions } from \"./index\";\nimport { isPage } from \"./playwright-type-predicates\";\nimport { imagesAreSimilar } from \"./image-compare\";\n\nexport async function takeStableScreenshot(\n target: Page | Locator,\n options?: ScreenshotPromptOptions,\n): Promise<Buffer> {\n const page = isPage(target) ? target : target.page();\n\n // Use a small budget for stabilization within the overall assertion timeout.\n // We allocate up to 25% of the total timeout (bounded between 300ms and 2000ms).\n const totalTimeout =\n (options as { timeout?: number } | undefined)?.timeout ?? 5000;\n // Budget is 25% of the total timeout\n const stabilizationBudgetMs = Math.floor(totalTimeout * 0.25);\n const stabilityBudgetMs = Math.min(\n 2000,\n Math.max(300, stabilizationBudgetMs),\n );\n const deadline = Date.now() + stabilityBudgetMs;\n\n let actual: Buffer | undefined;\n let previous: Buffer | undefined;\n const pollIntervals = [0, 100, 250, 500];\n let isFirstIteration = true;\n\n while (true) {\n if (Date.now() >= deadline) break;\n const delay = pollIntervals.length ? pollIntervals.shift()! : 1000;\n if (delay) {\n await page.waitForTimeout(delay);\n }\n previous = actual;\n actual = await target.screenshot(options);\n if (\n !isFirstIteration &&\n actual &&\n previous &&\n imagesAreSimilar({\n image1: previous,\n image2: actual,\n threshold: options?.threshold ?? 0.02,\n })\n ) {\n return actual;\n }\n isFirstIteration = false;\n }\n return actual ?? (await target.screenshot(options));\n}\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport type { ScreenshotPromptOptions } from \"./index\";\n\nimport { isLocator, isPage } from \"./playwright-type-predicates\";\n\nimport { verifyPrompt } from \"./ai/verify-prompt\";\nimport { takeStableScreenshot } from \"./screenshot\";\n\ntype VerifyTargetType = \"page\" | \"locator\";\n\ntype MatcherContext = {\n isNot: boolean;\n message?: () => string;\n};\n\nfunction createFailureMessage({\n targetType,\n condition,\n didPass,\n isNot,\n reason,\n}: {\n targetType: VerifyTargetType;\n condition: string;\n didPass: boolean;\n isNot: boolean;\n reason?: string;\n}): string {\n const expectation = isNot ? \"not to satisfy\" : \"to satisfy\";\n const result = didPass ? \"it did\" : \"it did not\";\n\n let message = `Expected ${targetType} ${expectation} ${JSON.stringify(condition)}, but ${result}.`;\n if (reason) {\n message += `\\n\\nReason: ${reason}`;\n }\n\n return message;\n}\n\nexport const stablyPlaywrightMatchers = {\n async toMatchScreenshotPrompt(\n this: MatcherContext,\n received: Page | Locator,\n condition: string,\n options?: ScreenshotPromptOptions,\n ) {\n const target = isPage(received)\n ? received\n : isLocator(received)\n ? received\n : undefined;\n if (!target) {\n // Should never happen\n throw new Error(\n \"toMatchScreenshotPrompt only supports Playwright Page and Locator instances.\",\n );\n }\n const targetType: VerifyTargetType = isPage(target) ? \"page\" : \"locator\";\n\n // Wait for two consecutive identical screenshots before sending to AI\n const screenshot = await takeStableScreenshot(target, options);\n\n const verifyResult = await verifyPrompt({ prompt: condition, screenshot });\n\n return {\n pass: verifyResult.pass,\n message: () =>\n createFailureMessage({\n targetType,\n condition,\n didPass: verifyResult.pass,\n reason: verifyResult.reason,\n isNot: this.isNot,\n }),\n };\n },\n} as const;\n"],"mappings":"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","names":["png"]}
package/dist/index.mjs CHANGED
@@ -31,7 +31,7 @@ var isObject = (value) => {
31
31
  // src/ai/metadata.ts
32
32
  var SDK_METADATA_HEADERS = {
33
33
  "X-Client-Name": "stably-playwright-sdk-js",
34
- "X-Client-Version": "0.1.7-next.9"
34
+ "X-Client-Version": "0.1.7"
35
35
  };
36
36
 
37
37
  // src/ai/extract.ts
@@ -333,8 +333,163 @@ async function verifyPrompt({
333
333
  );
334
334
  }
335
335
 
336
+ // ../../node_modules/.pnpm/pixelmatch@7.1.0/node_modules/pixelmatch/index.js
337
+ function pixelmatch(img1, img2, output, width, height, options = {}) {
338
+ const {
339
+ threshold = 0.1,
340
+ alpha = 0.1,
341
+ aaColor = [255, 255, 0],
342
+ diffColor = [255, 0, 0],
343
+ includeAA,
344
+ diffColorAlt,
345
+ diffMask
346
+ } = options;
347
+ if (!isPixelData(img1) || !isPixelData(img2) || output && !isPixelData(output))
348
+ throw new Error("Image data: Uint8Array, Uint8ClampedArray or Buffer expected.");
349
+ if (img1.length !== img2.length || output && output.length !== img1.length)
350
+ throw new Error("Image sizes do not match.");
351
+ if (img1.length !== width * height * 4) throw new Error("Image data size does not match width/height.");
352
+ const len = width * height;
353
+ const a32 = new Uint32Array(img1.buffer, img1.byteOffset, len);
354
+ const b32 = new Uint32Array(img2.buffer, img2.byteOffset, len);
355
+ let identical = true;
356
+ for (let i = 0; i < len; i++) {
357
+ if (a32[i] !== b32[i]) {
358
+ identical = false;
359
+ break;
360
+ }
361
+ }
362
+ if (identical) {
363
+ if (output && !diffMask) {
364
+ for (let i = 0; i < len; i++) drawGrayPixel(img1, 4 * i, alpha, output);
365
+ }
366
+ return 0;
367
+ }
368
+ const maxDelta = 35215 * threshold * threshold;
369
+ const [aaR, aaG, aaB] = aaColor;
370
+ const [diffR, diffG, diffB] = diffColor;
371
+ const [altR, altG, altB] = diffColorAlt || diffColor;
372
+ let diff = 0;
373
+ for (let y = 0; y < height; y++) {
374
+ for (let x = 0; x < width; x++) {
375
+ const i = y * width + x;
376
+ const pos = i * 4;
377
+ const delta = a32[i] === b32[i] ? 0 : colorDelta(img1, img2, pos, pos, false);
378
+ if (Math.abs(delta) > maxDelta) {
379
+ const isAA = antialiased(img1, x, y, width, height, a32, b32) || antialiased(img2, x, y, width, height, b32, a32);
380
+ if (!includeAA && isAA) {
381
+ if (output && !diffMask) drawPixel(output, pos, aaR, aaG, aaB);
382
+ } else {
383
+ if (output) {
384
+ if (delta < 0) {
385
+ drawPixel(output, pos, altR, altG, altB);
386
+ } else {
387
+ drawPixel(output, pos, diffR, diffG, diffB);
388
+ }
389
+ }
390
+ diff++;
391
+ }
392
+ } else if (output && !diffMask) {
393
+ drawGrayPixel(img1, pos, alpha, output);
394
+ }
395
+ }
396
+ }
397
+ return diff;
398
+ }
399
+ function isPixelData(arr) {
400
+ return ArrayBuffer.isView(arr) && arr.BYTES_PER_ELEMENT === 1;
401
+ }
402
+ function antialiased(img, x1, y1, width, height, a32, b32) {
403
+ const x0 = Math.max(x1 - 1, 0);
404
+ const y0 = Math.max(y1 - 1, 0);
405
+ const x2 = Math.min(x1 + 1, width - 1);
406
+ const y2 = Math.min(y1 + 1, height - 1);
407
+ const pos = y1 * width + x1;
408
+ let zeroes = x1 === x0 || x1 === x2 || y1 === y0 || y1 === y2 ? 1 : 0;
409
+ let min = 0;
410
+ let max = 0;
411
+ let minX = 0;
412
+ let minY = 0;
413
+ let maxX = 0;
414
+ let maxY = 0;
415
+ for (let x = x0; x <= x2; x++) {
416
+ for (let y = y0; y <= y2; y++) {
417
+ if (x === x1 && y === y1) continue;
418
+ const delta = colorDelta(img, img, pos * 4, (y * width + x) * 4, true);
419
+ if (delta === 0) {
420
+ zeroes++;
421
+ if (zeroes > 2) return false;
422
+ } else if (delta < min) {
423
+ min = delta;
424
+ minX = x;
425
+ minY = y;
426
+ } else if (delta > max) {
427
+ max = delta;
428
+ maxX = x;
429
+ maxY = y;
430
+ }
431
+ }
432
+ }
433
+ if (min === 0 || max === 0) return false;
434
+ return hasManySiblings(a32, minX, minY, width, height) && hasManySiblings(b32, minX, minY, width, height) || hasManySiblings(a32, maxX, maxY, width, height) && hasManySiblings(b32, maxX, maxY, width, height);
435
+ }
436
+ function hasManySiblings(img, x1, y1, width, height) {
437
+ const x0 = Math.max(x1 - 1, 0);
438
+ const y0 = Math.max(y1 - 1, 0);
439
+ const x2 = Math.min(x1 + 1, width - 1);
440
+ const y2 = Math.min(y1 + 1, height - 1);
441
+ const val = img[y1 * width + x1];
442
+ let zeroes = x1 === x0 || x1 === x2 || y1 === y0 || y1 === y2 ? 1 : 0;
443
+ for (let x = x0; x <= x2; x++) {
444
+ for (let y = y0; y <= y2; y++) {
445
+ if (x === x1 && y === y1) continue;
446
+ zeroes += +(val === img[y * width + x]);
447
+ if (zeroes > 2) return true;
448
+ }
449
+ }
450
+ return false;
451
+ }
452
+ function colorDelta(img1, img2, k, m, yOnly) {
453
+ const r1 = img1[k];
454
+ const g1 = img1[k + 1];
455
+ const b1 = img1[k + 2];
456
+ const a1 = img1[k + 3];
457
+ const r2 = img2[m];
458
+ const g2 = img2[m + 1];
459
+ const b2 = img2[m + 2];
460
+ const a2 = img2[m + 3];
461
+ let dr = r1 - r2;
462
+ let dg = g1 - g2;
463
+ let db = b1 - b2;
464
+ const da = a1 - a2;
465
+ if (!dr && !dg && !db && !da) return 0;
466
+ if (a1 < 255 || a2 < 255) {
467
+ const rb = 48 + 159 * (k % 2);
468
+ const gb = 48 + 159 * ((k / 1.618033988749895 | 0) % 2);
469
+ const bb = 48 + 159 * ((k / 2.618033988749895 | 0) % 2);
470
+ dr = (r1 * a1 - r2 * a2 - rb * da) / 255;
471
+ dg = (g1 * a1 - g2 * a2 - gb * da) / 255;
472
+ db = (b1 * a1 - b2 * a2 - bb * da) / 255;
473
+ }
474
+ const y = dr * 0.29889531 + dg * 0.58662247 + db * 0.11448223;
475
+ if (yOnly) return y;
476
+ const i = dr * 0.59597799 - dg * 0.2741761 - db * 0.32180189;
477
+ const q = dr * 0.21147017 - dg * 0.52261711 + db * 0.31114694;
478
+ const delta = 0.5053 * y * y + 0.299 * i * i + 0.1957 * q * q;
479
+ return y > 0 ? -delta : delta;
480
+ }
481
+ function drawPixel(output, pos, r, g, b) {
482
+ output[pos + 0] = r;
483
+ output[pos + 1] = g;
484
+ output[pos + 2] = b;
485
+ output[pos + 3] = 255;
486
+ }
487
+ function drawGrayPixel(img, i, alpha, output) {
488
+ const val = 255 + (img[i] * 0.29889531 + img[i + 1] * 0.58662247 + img[i + 2] * 0.11448223 - 255) * alpha * img[i + 3] / 255;
489
+ drawPixel(output, i, val, val, val);
490
+ }
491
+
336
492
  // src/image-compare.ts
337
- import pixelmatch from "pixelmatch";
338
493
  import { PNG } from "pngjs";
339
494
  import * as jpeg from "jpeg-js";
340
495
  var isPng = (buffer) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime.ts","../src/type-predicate/is-object.ts","../src/ai/metadata.ts","../src/ai/extract.ts","../src/playwright-augment/methods/extract.ts","../src/playwright-augment/methods/agent.ts","../src/playwright-augment/augment.ts","../src/playwright-type-predicates.ts","../src/ai/verify-prompt.ts","../src/image-compare.ts","../src/screenshot.ts","../src/expect.ts"],"sourcesContent":["let configuredApiKey: string | undefined = process.env.STABLY_API_KEY;\n\nexport function setApiKey(apiKey: string): void {\n configuredApiKey = apiKey;\n}\n\nexport function getApiKey(): string | undefined {\n return configuredApiKey;\n}\n\nexport function requireApiKey(): string {\n const apiKey = getApiKey();\n if (!apiKey) {\n throw new Error(\n \"Missing Stably API key. Call setApiKey(apiKey) or set the STABLY_API_KEY environment variable.\",\n );\n }\n return apiKey;\n}\n","export const isObject = (value: unknown): value is Record<string, unknown> => {\n return typeof value === \"object\" && value !== null;\n};\n","declare const __PACKAGE_VERSION__: string;\n\nexport const SDK_METADATA_HEADERS = {\n \"X-Client-Name\": \"stably-playwright-sdk-js\",\n \"X-Client-Version\": __PACKAGE_VERSION__,\n};\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport type * as z4 from \"zod/v4/core\";\nimport { requireApiKey } from \"../runtime\";\nimport { isObject } from \"../type-predicate/is-object\";\nimport { SDK_METADATA_HEADERS } from \"./metadata\";\n\ntype ZodV4 = typeof import(\"zod/v4/core\");\n\nexport interface ExtractSchema extends z4.$ZodType {\n safeParseAsync(\n data: unknown,\n params?: z4.ParseContext<z4.$ZodIssue>,\n ): Promise<z4.util.SafeParseResult<z4.output<this>>>;\n}\n\nexport type SchemaOutput<T extends ExtractSchema> = z4.output<T>;\n\ntype ExtractIssue = z4.$ZodIssue;\n\nconst EXTRACT_ENDPOINT = \"https://api.stably.ai/internal/v2/extract\";\n\nconst zodV4: ZodV4 | undefined = (() => {\n try {\n return require(\"zod/v4/core\") as ZodV4;\n } catch {\n return undefined;\n }\n})();\n\ntype ExtractionSuccess = { success: true; value: unknown };\ntype ExtractionFailure = { success: false; error: string };\ntype ExtractionResponse = ExtractionSuccess | ExtractionFailure;\n\ntype ErrorResponse = { error: string };\n\nconst isExtractionResponse = (value: unknown): value is ExtractionResponse => {\n if (!isObject(value)) {\n return false;\n }\n\n if (value.success === true) {\n return \"value\" in value;\n }\n\n return value.success === false && typeof value.error === \"string\";\n};\n\nconst isErrorResponse = (value: unknown): value is ErrorResponse => {\n return isObject(value) && typeof value.error === \"string\";\n};\n\nclass ExtractValidationError extends Error {\n constructor(\n message: string,\n readonly issues: ReadonlyArray<ExtractIssue>,\n ) {\n super(message);\n this.name = \"ExtractValidationError\";\n }\n}\n\nasync function validateWithSchema<T extends ExtractSchema>(\n schema: T,\n value: unknown,\n): Promise<SchemaOutput<T>> {\n const result = await schema.safeParseAsync(value);\n if (!result.success) {\n throw new ExtractValidationError(\"Validation failed\", result.error.issues);\n }\n\n return result.data;\n}\n\ntype BaseExtractArgs = {\n prompt: string;\n pageOrLocator: Page | Locator;\n};\n\ntype ExtractArgsWithSchema<T extends ExtractSchema> = BaseExtractArgs & {\n schema: T;\n};\n\nexport async function extract(args: BaseExtractArgs): Promise<string>;\nexport async function extract<T extends ExtractSchema>(\n args: ExtractArgsWithSchema<T>,\n): Promise<SchemaOutput<T>>;\nexport async function extract<T extends ExtractSchema>({\n prompt,\n pageOrLocator,\n schema,\n}: BaseExtractArgs & { schema?: T }): Promise<string | SchemaOutput<T>> {\n if (schema && !zodV4) {\n throw new Error(\n \"Schema support requires installing zod@4. Please add it to enable schemas.\",\n );\n }\n\n const jsonSchema =\n schema && zodV4\n ? zodV4?.toJSONSchema(\n schema as unknown as Parameters<ZodV4[\"toJSONSchema\"]>[0],\n )\n : undefined;\n\n const apiKey = requireApiKey();\n\n const form = new FormData();\n form.append(\"prompt\", prompt);\n if (jsonSchema) {\n form.append(\"jsonSchema\", JSON.stringify(jsonSchema));\n }\n\n const pngBuffer = await pageOrLocator.screenshot({ type: \"png\" }); // Buffer\n const u8 = Uint8Array.from(pngBuffer); // strips Buffer type → plain Uint8Array\n const blob = new Blob([u8], { type: \"image/png\" });\n form.append(\"image\", blob, \"screenshot.png\");\n\n const response = await fetch(EXTRACT_ENDPOINT, {\n method: \"POST\",\n headers: {\n ...SDK_METADATA_HEADERS,\n Authorization: `Bearer ${apiKey}`,\n },\n body: form,\n });\n\n const raw = await response.json().catch(() => undefined as unknown);\n\n if (response.ok) {\n if (!isExtractionResponse(raw)) {\n throw new Error(\"Extract returned unexpected response shape\");\n }\n\n if (raw.success === false) {\n return raw.error;\n }\n\n const { value } = raw;\n return schema\n ? await validateWithSchema(schema, value)\n : typeof value === \"string\"\n ? value\n : JSON.stringify(value);\n }\n\n throw new Error(isErrorResponse(raw) ? raw.error : \"Extract failed\");\n}\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport {\n type ExtractSchema,\n type SchemaOutput,\n extract,\n} from \"../../ai/extract\";\n\ntype ExtractOptions<T extends ExtractSchema> = {\n schema: T;\n};\n\ntype ExtractMethod = {\n (prompt: string): Promise<string>;\n <T extends ExtractSchema>(\n prompt: string,\n options: ExtractOptions<T>,\n ): Promise<SchemaOutput<T>>;\n};\n\ntype LocatorExtract = ExtractMethod;\ntype PageExtract = ExtractMethod;\n\ntype ExtractSubject = Locator | Page;\n\nfunction createExtract(pageOrLocator: ExtractSubject): ExtractMethod {\n const impl = (async (\n prompt: string,\n options?: ExtractOptions<ExtractSchema>,\n ) => {\n if (options?.schema) {\n return extract({\n prompt,\n schema: options.schema,\n pageOrLocator,\n });\n }\n\n return extract({ prompt, pageOrLocator });\n }) as ExtractMethod;\n\n return impl;\n}\n\nexport const createLocatorExtract = (locator: Locator): LocatorExtract =>\n createExtract(locator);\n\nexport const createPageExtract = (page: Page): PageExtract =>\n createExtract(page);\n","import type { Page } from \"@stablyai/internal-playwright-test\";\nimport { requireApiKey } from \"../../runtime\";\n\ntype AgentOptions = {\n page: Page;\n maxCycles?: number;\n};\n\nexport function createAgentStub(): (\n prompt: string,\n options: AgentOptions,\n) => Promise<{ success: boolean }> {\n return async (prompt: string, options: AgentOptions) => {\n requireApiKey();\n void prompt;\n void options;\n return { success: true };\n };\n}\n","import type {\n Browser,\n BrowserContext,\n BrowserType,\n Locator,\n Page,\n} from \"@stablyai/internal-playwright-test\";\n\nimport { createLocatorExtract, createPageExtract } from \"./methods/extract\";\nimport { createAgentStub } from \"./methods/agent\";\n\nexport interface LocatorDescribeOptions {\n autoHeal?: boolean;\n}\n\nconst LOCATOR_PATCHED = Symbol.for(\"stably.playwright.locatorPatched\");\nconst LOCATOR_DESCRIBE_WRAPPED = Symbol.for(\n \"stably.playwright.locatorDescribeWrapped\",\n);\nconst PAGE_PATCHED = Symbol.for(\"stably.playwright.pagePatched\");\nconst CONTEXT_PATCHED = Symbol.for(\"stably.playwright.contextPatched\");\nconst BROWSER_PATCHED = Symbol.for(\"stably.playwright.browserPatched\");\nconst BROWSER_TYPE_PATCHED = Symbol.for(\"stably.playwright.browserTypePatched\");\n\nfunction defineHiddenProperty<T, K extends PropertyKey>(\n target: T,\n key: K,\n value: unknown,\n): void {\n Object.defineProperty(target as unknown as object, key, {\n value,\n enumerable: false,\n configurable: true,\n writable: true,\n });\n}\n\nexport function augmentLocator<T extends Locator>(locator: T): T {\n if (\n (locator as unknown as { [LOCATOR_PATCHED]?: boolean })[LOCATOR_PATCHED]\n ) {\n return locator;\n }\n\n defineHiddenProperty(locator, \"extract\", createLocatorExtract(locator));\n\n const markerTarget = locator as unknown as Record<PropertyKey, unknown>;\n\n if (\n typeof locator.describe === \"function\" &&\n !markerTarget[LOCATOR_DESCRIBE_WRAPPED]\n ) {\n const originalDescribe = locator.describe.bind(locator);\n locator.describe = ((\n description: string,\n options?: LocatorDescribeOptions,\n ) => {\n void options;\n const result = originalDescribe(description);\n return result ? augmentLocator(result as Locator) : result;\n }) as Locator[\"describe\"];\n\n defineHiddenProperty(locator, LOCATOR_DESCRIBE_WRAPPED, true);\n }\n\n defineHiddenProperty(locator, LOCATOR_PATCHED, true);\n\n return locator;\n}\n\nexport function augmentPage<T extends Page>(page: T): T {\n if ((page as unknown as { [PAGE_PATCHED]?: boolean })[PAGE_PATCHED]) {\n return page;\n }\n\n const originalLocator = page.locator.bind(page);\n page.locator = ((...args: Parameters<Page[\"locator\"]>) => {\n const locator = originalLocator(...args);\n return augmentLocator(locator);\n }) as Page[\"locator\"];\n\n defineHiddenProperty(page, \"extract\", createPageExtract(page));\n defineHiddenProperty(page, PAGE_PATCHED, true);\n\n return page;\n}\n\nexport function augmentBrowserContext<T extends BrowserContext>(context: T): T {\n if (\n (context as unknown as { [CONTEXT_PATCHED]?: boolean })[CONTEXT_PATCHED]\n ) {\n return context;\n }\n\n const originalNewPage = context.newPage.bind(context);\n context.newPage = (async (...args: Parameters<BrowserContext[\"newPage\"]>) => {\n const page = await originalNewPage(...args);\n return augmentPage(page);\n }) as BrowserContext[\"newPage\"];\n\n const originalPages = context.pages?.bind(context);\n if (originalPages) {\n context.pages = (() =>\n originalPages().map((page) =>\n augmentPage(page),\n )) as BrowserContext[\"pages\"];\n }\n\n if (!(context as unknown as { agent?: unknown }).agent) {\n defineHiddenProperty(context, \"agent\", createAgentStub());\n }\n\n defineHiddenProperty(context, CONTEXT_PATCHED, true);\n\n return context;\n}\n\nexport function augmentBrowser<T extends Browser>(browser: T): T {\n if (\n (browser as unknown as { [BROWSER_PATCHED]?: boolean })[BROWSER_PATCHED]\n ) {\n return browser;\n }\n\n const originalNewContext = browser.newContext.bind(browser);\n browser.newContext = (async (...args: Parameters<Browser[\"newContext\"]>) => {\n const context = await originalNewContext(...args);\n return augmentBrowserContext(context);\n }) as Browser[\"newContext\"];\n\n const originalNewPage = browser.newPage.bind(browser);\n browser.newPage = (async (...args: Parameters<Browser[\"newPage\"]>) => {\n const page = await originalNewPage(...args);\n return augmentPage(page);\n }) as Browser[\"newPage\"];\n\n const originalContexts = browser.contexts.bind(browser);\n browser.contexts = (() =>\n originalContexts().map((context) =>\n augmentBrowserContext(context),\n )) as Browser[\"contexts\"];\n\n if (!(browser as unknown as { agent?: unknown }).agent) {\n defineHiddenProperty(browser, \"agent\", createAgentStub());\n }\n\n defineHiddenProperty(browser, BROWSER_PATCHED, true);\n\n return browser;\n}\n\nexport function augmentBrowserType<TBrowser extends Browser>(\n browserType: BrowserType<TBrowser>,\n): BrowserType<TBrowser> {\n if (\n (browserType as unknown as { [BROWSER_TYPE_PATCHED]?: boolean })[\n BROWSER_TYPE_PATCHED\n ]\n ) {\n return browserType;\n }\n\n const originalLaunch = browserType.launch.bind(browserType);\n browserType.launch = (async (\n ...args: Parameters<BrowserType<TBrowser>[\"launch\"]>\n ) => {\n const browser = await originalLaunch(...args);\n return augmentBrowser(browser);\n }) as BrowserType<TBrowser>[\"launch\"];\n\n const originalConnect = browserType.connect?.bind(browserType);\n if (originalConnect) {\n browserType.connect = (async (\n ...args: Parameters<NonNullable<BrowserType<TBrowser>[\"connect\"]>>\n ) => {\n const browser = await originalConnect(...args);\n return augmentBrowser(browser);\n }) as NonNullable<BrowserType<TBrowser>[\"connect\"]>;\n }\n\n const originalConnectOverCDP = browserType.connectOverCDP?.bind(browserType);\n if (originalConnectOverCDP) {\n browserType.connectOverCDP = (async (\n ...args: Parameters<NonNullable<BrowserType<TBrowser>[\"connectOverCDP\"]>>\n ) => {\n const browser = await originalConnectOverCDP(...args);\n return augmentBrowser(browser);\n }) as NonNullable<BrowserType<TBrowser>[\"connectOverCDP\"]>;\n }\n\n const originalLaunchPersistentContext =\n browserType.launchPersistentContext?.bind(browserType);\n if (originalLaunchPersistentContext) {\n browserType.launchPersistentContext = (async (\n ...args: Parameters<\n NonNullable<BrowserType<TBrowser>[\"launchPersistentContext\"]>\n >\n ) => {\n const context = await originalLaunchPersistentContext(...args);\n return augmentBrowserContext(context);\n }) as NonNullable<BrowserType<TBrowser>[\"launchPersistentContext\"]>;\n }\n\n defineHiddenProperty(browserType, BROWSER_TYPE_PATCHED, true);\n\n return browserType;\n}\n","import { Page, Locator } from \"@stablyai/internal-playwright-test\";\n\nexport function isPage(candidate: unknown): candidate is Page {\n return (\n typeof candidate === \"object\" &&\n candidate !== null &&\n typeof (candidate as Page).screenshot === \"function\" &&\n typeof (candidate as Page).goto === \"function\"\n );\n}\n\nexport function isLocator(candidate: unknown): candidate is Locator {\n return (\n typeof candidate === \"object\" &&\n candidate !== null &&\n typeof (candidate as Locator).screenshot === \"function\" &&\n typeof (candidate as Locator).nth === \"function\"\n );\n}\n","import { isObject } from \"../type-predicate/is-object\";\nimport { requireApiKey } from \"../runtime\";\nimport { SDK_METADATA_HEADERS } from \"./metadata\";\n\nconst PROMPT_ASSERTION_ENDPOINT = \"https://api.stably.ai/internal/v1/assert\";\n\ntype ParsedSuccessResponse = { success: boolean; reason?: string };\n\nconst parseSuccessResponse = (value: unknown): ParsedSuccessResponse => {\n if (!isObject(value)) {\n throw new Error(\"Verify prompt returned unexpected response shape\");\n }\n\n const { success, reason } = value;\n if (typeof success !== \"boolean\") {\n throw new Error(\"Verify prompt returned unexpected response shape\");\n }\n\n if (reason !== undefined && typeof reason !== \"string\") {\n throw new Error(\"Verify prompt returned unexpected response shape\");\n }\n\n return {\n success,\n reason,\n };\n};\n\ntype ParsedErrorResponse = { error: string };\n\nconst parseErrorResponse = (\n value: unknown,\n): ParsedErrorResponse | undefined => {\n if (!isObject(value)) {\n return undefined;\n }\n\n const { error } = value;\n return typeof error !== \"string\" ? undefined : { error };\n};\n\nexport async function verifyPrompt({\n prompt,\n screenshot,\n}: {\n prompt: string;\n screenshot: Uint8Array;\n}): Promise<{\n pass: boolean;\n reason?: string;\n}> {\n const apiKey = requireApiKey();\n\n const form = new FormData();\n form.append(\"prompt\", prompt);\n const u8 = Uint8Array.from(screenshot);\n const blob = new Blob([u8], { type: \"image/png\" });\n form.append(\"image\", blob, \"screenshot.png\");\n\n const response = await fetch(PROMPT_ASSERTION_ENDPOINT, {\n method: \"POST\",\n headers: {\n ...SDK_METADATA_HEADERS,\n Authorization: `Bearer ${apiKey}`,\n },\n body: form,\n });\n\n const parsed = await response.json().catch(() => undefined as unknown);\n\n if (response.ok) {\n const { success, reason } = parseSuccessResponse(parsed);\n return {\n pass: success,\n reason,\n };\n }\n\n const err = parseErrorResponse(parsed);\n throw new Error(\n `Verify prompt failed (${response.status})${err ? `: ${err.error}` : \"\"}`,\n );\n}\n","import pixelmatch from \"pixelmatch\";\nimport { PNG } from \"pngjs\";\nimport * as jpeg from \"jpeg-js\";\n\nconst isPng = (buffer: Buffer): boolean => {\n return (\n buffer.length >= 8 &&\n buffer[0] === 0x89 &&\n buffer[1] === 0x50 &&\n buffer[2] === 0x4e &&\n buffer[3] === 0x47 &&\n buffer[4] === 0x0d &&\n buffer[5] === 0x0a &&\n buffer[6] === 0x1a &&\n buffer[7] === 0x0a\n );\n};\n\nconst isJpeg = (buffer: Buffer): boolean => {\n return buffer.length >= 2 && buffer[0] === 0xff && buffer[1] === 0xd8;\n};\n\nconst decodeImage = (\n buffer: Buffer,\n): { data: Uint8Array; width: number; height: number } => {\n if (isPng(buffer)) {\n const png = PNG.sync.read(buffer);\n return { data: png.data, width: png.width, height: png.height };\n }\n if (isJpeg(buffer)) {\n const img = jpeg.decode(buffer, { maxMemoryUsageInMB: 1024 });\n return { data: img.data, width: img.width, height: img.height };\n }\n // Default to PNG decode; if it fails upstream, treat as different sizes\n const png = PNG.sync.read(buffer);\n return { data: png.data, width: png.width, height: png.height };\n};\n\nexport const imagesAreSimilar = ({\n image1,\n image2,\n threshold,\n}: {\n image1: Buffer;\n image2: Buffer;\n threshold: number;\n}): boolean => {\n const decodedImage1 = decodeImage(image1);\n const decodedImage2 = decodeImage(image2);\n if (\n decodedImage1.width !== decodedImage2.width ||\n decodedImage1.height !== decodedImage2.height\n ) {\n return false;\n }\n const diffRgbaData = new Uint8Array(\n decodedImage1.width * decodedImage1.height * 4,\n );\n const numDiffPixels = pixelmatch(\n decodedImage1.data,\n decodedImage2.data,\n diffRgbaData,\n decodedImage1.width,\n decodedImage1.height,\n { threshold },\n );\n\n return numDiffPixels === 0;\n};\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport type { ScreenshotPromptOptions } from \"./index\";\nimport { isPage } from \"./playwright-type-predicates\";\nimport { imagesAreSimilar } from \"./image-compare\";\n\nexport async function takeStableScreenshot(\n target: Page | Locator,\n options?: ScreenshotPromptOptions,\n): Promise<Buffer> {\n const page = isPage(target) ? target : target.page();\n\n // Use a small budget for stabilization within the overall assertion timeout.\n // We allocate up to 25% of the total timeout (bounded between 300ms and 2000ms).\n const totalTimeout =\n (options as { timeout?: number } | undefined)?.timeout ?? 5000;\n // Budget is 25% of the total timeout\n const stabilizationBudgetMs = Math.floor(totalTimeout * 0.25);\n const stabilityBudgetMs = Math.min(\n 2000,\n Math.max(300, stabilizationBudgetMs),\n );\n const deadline = Date.now() + stabilityBudgetMs;\n\n let actual: Buffer | undefined;\n let previous: Buffer | undefined;\n const pollIntervals = [0, 100, 250, 500];\n let isFirstIteration = true;\n\n while (true) {\n if (Date.now() >= deadline) break;\n const delay = pollIntervals.length ? pollIntervals.shift()! : 1000;\n if (delay) {\n await page.waitForTimeout(delay);\n }\n previous = actual;\n actual = await target.screenshot(options);\n if (\n !isFirstIteration &&\n actual &&\n previous &&\n imagesAreSimilar({\n image1: previous,\n image2: actual,\n threshold: options?.threshold ?? 0.02,\n })\n ) {\n return actual;\n }\n isFirstIteration = false;\n }\n return actual ?? (await target.screenshot(options));\n}\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport type { ScreenshotPromptOptions } from \"./index\";\n\nimport { isLocator, isPage } from \"./playwright-type-predicates\";\n\nimport { verifyPrompt } from \"./ai/verify-prompt\";\nimport { takeStableScreenshot } from \"./screenshot\";\n\ntype VerifyTargetType = \"page\" | \"locator\";\n\ntype MatcherContext = {\n isNot: boolean;\n message?: () => string;\n};\n\nfunction createFailureMessage({\n targetType,\n condition,\n didPass,\n isNot,\n reason,\n}: {\n targetType: VerifyTargetType;\n condition: string;\n didPass: boolean;\n isNot: boolean;\n reason?: string;\n}): string {\n const expectation = isNot ? \"not to satisfy\" : \"to satisfy\";\n const result = didPass ? \"it did\" : \"it did not\";\n\n let message = `Expected ${targetType} ${expectation} ${JSON.stringify(condition)}, but ${result}.`;\n if (reason) {\n message += `\\n\\nReason: ${reason}`;\n }\n\n return message;\n}\n\nexport const stablyPlaywrightMatchers = {\n async toMatchScreenshotPrompt(\n this: MatcherContext,\n received: Page | Locator,\n condition: string,\n options?: ScreenshotPromptOptions,\n ) {\n const target = isPage(received)\n ? received\n : isLocator(received)\n ? received\n : undefined;\n if (!target) {\n // Should never happen\n throw new Error(\n \"toMatchScreenshotPrompt only supports Playwright Page and Locator instances.\",\n );\n }\n const targetType: VerifyTargetType = isPage(target) ? \"page\" : \"locator\";\n\n // Wait for two consecutive identical screenshots before sending to AI\n const screenshot = await takeStableScreenshot(target, options);\n\n const verifyResult = await verifyPrompt({ prompt: condition, screenshot });\n\n return {\n pass: verifyResult.pass,\n message: () =>\n createFailureMessage({\n targetType,\n condition,\n didPass: verifyResult.pass,\n reason: verifyResult.reason,\n isNot: this.isNot,\n }),\n };\n },\n} as const;\n"],"mappings":"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","names":["png"]}
1
+ {"version":3,"sources":["../src/runtime.ts","../src/type-predicate/is-object.ts","../src/ai/metadata.ts","../src/ai/extract.ts","../src/playwright-augment/methods/extract.ts","../src/playwright-augment/methods/agent.ts","../src/playwright-augment/augment.ts","../src/playwright-type-predicates.ts","../src/ai/verify-prompt.ts","../../../node_modules/.pnpm/pixelmatch@7.1.0/node_modules/pixelmatch/index.js","../src/image-compare.ts","../src/screenshot.ts","../src/expect.ts"],"sourcesContent":["let configuredApiKey: string | undefined = process.env.STABLY_API_KEY;\n\nexport function setApiKey(apiKey: string): void {\n configuredApiKey = apiKey;\n}\n\nexport function getApiKey(): string | undefined {\n return configuredApiKey;\n}\n\nexport function requireApiKey(): string {\n const apiKey = getApiKey();\n if (!apiKey) {\n throw new Error(\n \"Missing Stably API key. Call setApiKey(apiKey) or set the STABLY_API_KEY environment variable.\",\n );\n }\n return apiKey;\n}\n","export const isObject = (value: unknown): value is Record<string, unknown> => {\n return typeof value === \"object\" && value !== null;\n};\n","declare const __PACKAGE_VERSION__: string;\n\nexport const SDK_METADATA_HEADERS = {\n \"X-Client-Name\": \"stably-playwright-sdk-js\",\n \"X-Client-Version\": __PACKAGE_VERSION__,\n};\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport type * as z4 from \"zod/v4/core\";\nimport { requireApiKey } from \"../runtime\";\nimport { isObject } from \"../type-predicate/is-object\";\nimport { SDK_METADATA_HEADERS } from \"./metadata\";\n\ntype ZodV4 = typeof import(\"zod/v4/core\");\n\nexport interface ExtractSchema extends z4.$ZodType {\n safeParseAsync(\n data: unknown,\n params?: z4.ParseContext<z4.$ZodIssue>,\n ): Promise<z4.util.SafeParseResult<z4.output<this>>>;\n}\n\nexport type SchemaOutput<T extends ExtractSchema> = z4.output<T>;\n\ntype ExtractIssue = z4.$ZodIssue;\n\nconst EXTRACT_ENDPOINT = \"https://api.stably.ai/internal/v2/extract\";\n\nconst zodV4: ZodV4 | undefined = (() => {\n try {\n return require(\"zod/v4/core\") as ZodV4;\n } catch {\n return undefined;\n }\n})();\n\ntype ExtractionSuccess = { success: true; value: unknown };\ntype ExtractionFailure = { success: false; error: string };\ntype ExtractionResponse = ExtractionSuccess | ExtractionFailure;\n\ntype ErrorResponse = { error: string };\n\nconst isExtractionResponse = (value: unknown): value is ExtractionResponse => {\n if (!isObject(value)) {\n return false;\n }\n\n if (value.success === true) {\n return \"value\" in value;\n }\n\n return value.success === false && typeof value.error === \"string\";\n};\n\nconst isErrorResponse = (value: unknown): value is ErrorResponse => {\n return isObject(value) && typeof value.error === \"string\";\n};\n\nclass ExtractValidationError extends Error {\n constructor(\n message: string,\n readonly issues: ReadonlyArray<ExtractIssue>,\n ) {\n super(message);\n this.name = \"ExtractValidationError\";\n }\n}\n\nasync function validateWithSchema<T extends ExtractSchema>(\n schema: T,\n value: unknown,\n): Promise<SchemaOutput<T>> {\n const result = await schema.safeParseAsync(value);\n if (!result.success) {\n throw new ExtractValidationError(\"Validation failed\", result.error.issues);\n }\n\n return result.data;\n}\n\ntype BaseExtractArgs = {\n prompt: string;\n pageOrLocator: Page | Locator;\n};\n\ntype ExtractArgsWithSchema<T extends ExtractSchema> = BaseExtractArgs & {\n schema: T;\n};\n\nexport async function extract(args: BaseExtractArgs): Promise<string>;\nexport async function extract<T extends ExtractSchema>(\n args: ExtractArgsWithSchema<T>,\n): Promise<SchemaOutput<T>>;\nexport async function extract<T extends ExtractSchema>({\n prompt,\n pageOrLocator,\n schema,\n}: BaseExtractArgs & { schema?: T }): Promise<string | SchemaOutput<T>> {\n if (schema && !zodV4) {\n throw new Error(\n \"Schema support requires installing zod@4. Please add it to enable schemas.\",\n );\n }\n\n const jsonSchema =\n schema && zodV4\n ? zodV4?.toJSONSchema(\n schema as unknown as Parameters<ZodV4[\"toJSONSchema\"]>[0],\n )\n : undefined;\n\n const apiKey = requireApiKey();\n\n const form = new FormData();\n form.append(\"prompt\", prompt);\n if (jsonSchema) {\n form.append(\"jsonSchema\", JSON.stringify(jsonSchema));\n }\n\n const pngBuffer = await pageOrLocator.screenshot({ type: \"png\" }); // Buffer\n const u8 = Uint8Array.from(pngBuffer); // strips Buffer type → plain Uint8Array\n const blob = new Blob([u8], { type: \"image/png\" });\n form.append(\"image\", blob, \"screenshot.png\");\n\n const response = await fetch(EXTRACT_ENDPOINT, {\n method: \"POST\",\n headers: {\n ...SDK_METADATA_HEADERS,\n Authorization: `Bearer ${apiKey}`,\n },\n body: form,\n });\n\n const raw = await response.json().catch(() => undefined as unknown);\n\n if (response.ok) {\n if (!isExtractionResponse(raw)) {\n throw new Error(\"Extract returned unexpected response shape\");\n }\n\n if (raw.success === false) {\n return raw.error;\n }\n\n const { value } = raw;\n return schema\n ? await validateWithSchema(schema, value)\n : typeof value === \"string\"\n ? value\n : JSON.stringify(value);\n }\n\n throw new Error(isErrorResponse(raw) ? raw.error : \"Extract failed\");\n}\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport {\n type ExtractSchema,\n type SchemaOutput,\n extract,\n} from \"../../ai/extract\";\n\ntype ExtractOptions<T extends ExtractSchema> = {\n schema: T;\n};\n\ntype ExtractMethod = {\n (prompt: string): Promise<string>;\n <T extends ExtractSchema>(\n prompt: string,\n options: ExtractOptions<T>,\n ): Promise<SchemaOutput<T>>;\n};\n\ntype LocatorExtract = ExtractMethod;\ntype PageExtract = ExtractMethod;\n\ntype ExtractSubject = Locator | Page;\n\nfunction createExtract(pageOrLocator: ExtractSubject): ExtractMethod {\n const impl = (async (\n prompt: string,\n options?: ExtractOptions<ExtractSchema>,\n ) => {\n if (options?.schema) {\n return extract({\n prompt,\n schema: options.schema,\n pageOrLocator,\n });\n }\n\n return extract({ prompt, pageOrLocator });\n }) as ExtractMethod;\n\n return impl;\n}\n\nexport const createLocatorExtract = (locator: Locator): LocatorExtract =>\n createExtract(locator);\n\nexport const createPageExtract = (page: Page): PageExtract =>\n createExtract(page);\n","import type { Page } from \"@stablyai/internal-playwright-test\";\nimport { requireApiKey } from \"../../runtime\";\n\ntype AgentOptions = {\n page: Page;\n maxCycles?: number;\n};\n\nexport function createAgentStub(): (\n prompt: string,\n options: AgentOptions,\n) => Promise<{ success: boolean }> {\n return async (prompt: string, options: AgentOptions) => {\n requireApiKey();\n void prompt;\n void options;\n return { success: true };\n };\n}\n","import type {\n Browser,\n BrowserContext,\n BrowserType,\n Locator,\n Page,\n} from \"@stablyai/internal-playwright-test\";\n\nimport { createLocatorExtract, createPageExtract } from \"./methods/extract\";\nimport { createAgentStub } from \"./methods/agent\";\n\nexport interface LocatorDescribeOptions {\n autoHeal?: boolean;\n}\n\nconst LOCATOR_PATCHED = Symbol.for(\"stably.playwright.locatorPatched\");\nconst LOCATOR_DESCRIBE_WRAPPED = Symbol.for(\n \"stably.playwright.locatorDescribeWrapped\",\n);\nconst PAGE_PATCHED = Symbol.for(\"stably.playwright.pagePatched\");\nconst CONTEXT_PATCHED = Symbol.for(\"stably.playwright.contextPatched\");\nconst BROWSER_PATCHED = Symbol.for(\"stably.playwright.browserPatched\");\nconst BROWSER_TYPE_PATCHED = Symbol.for(\"stably.playwright.browserTypePatched\");\n\nfunction defineHiddenProperty<T, K extends PropertyKey>(\n target: T,\n key: K,\n value: unknown,\n): void {\n Object.defineProperty(target as unknown as object, key, {\n value,\n enumerable: false,\n configurable: true,\n writable: true,\n });\n}\n\nexport function augmentLocator<T extends Locator>(locator: T): T {\n if (\n (locator as unknown as { [LOCATOR_PATCHED]?: boolean })[LOCATOR_PATCHED]\n ) {\n return locator;\n }\n\n defineHiddenProperty(locator, \"extract\", createLocatorExtract(locator));\n\n const markerTarget = locator as unknown as Record<PropertyKey, unknown>;\n\n if (\n typeof locator.describe === \"function\" &&\n !markerTarget[LOCATOR_DESCRIBE_WRAPPED]\n ) {\n const originalDescribe = locator.describe.bind(locator);\n locator.describe = ((\n description: string,\n options?: LocatorDescribeOptions,\n ) => {\n void options;\n const result = originalDescribe(description);\n return result ? augmentLocator(result as Locator) : result;\n }) as Locator[\"describe\"];\n\n defineHiddenProperty(locator, LOCATOR_DESCRIBE_WRAPPED, true);\n }\n\n defineHiddenProperty(locator, LOCATOR_PATCHED, true);\n\n return locator;\n}\n\nexport function augmentPage<T extends Page>(page: T): T {\n if ((page as unknown as { [PAGE_PATCHED]?: boolean })[PAGE_PATCHED]) {\n return page;\n }\n\n const originalLocator = page.locator.bind(page);\n page.locator = ((...args: Parameters<Page[\"locator\"]>) => {\n const locator = originalLocator(...args);\n return augmentLocator(locator);\n }) as Page[\"locator\"];\n\n defineHiddenProperty(page, \"extract\", createPageExtract(page));\n defineHiddenProperty(page, PAGE_PATCHED, true);\n\n return page;\n}\n\nexport function augmentBrowserContext<T extends BrowserContext>(context: T): T {\n if (\n (context as unknown as { [CONTEXT_PATCHED]?: boolean })[CONTEXT_PATCHED]\n ) {\n return context;\n }\n\n const originalNewPage = context.newPage.bind(context);\n context.newPage = (async (...args: Parameters<BrowserContext[\"newPage\"]>) => {\n const page = await originalNewPage(...args);\n return augmentPage(page);\n }) as BrowserContext[\"newPage\"];\n\n const originalPages = context.pages?.bind(context);\n if (originalPages) {\n context.pages = (() =>\n originalPages().map((page) =>\n augmentPage(page),\n )) as BrowserContext[\"pages\"];\n }\n\n if (!(context as unknown as { agent?: unknown }).agent) {\n defineHiddenProperty(context, \"agent\", createAgentStub());\n }\n\n defineHiddenProperty(context, CONTEXT_PATCHED, true);\n\n return context;\n}\n\nexport function augmentBrowser<T extends Browser>(browser: T): T {\n if (\n (browser as unknown as { [BROWSER_PATCHED]?: boolean })[BROWSER_PATCHED]\n ) {\n return browser;\n }\n\n const originalNewContext = browser.newContext.bind(browser);\n browser.newContext = (async (...args: Parameters<Browser[\"newContext\"]>) => {\n const context = await originalNewContext(...args);\n return augmentBrowserContext(context);\n }) as Browser[\"newContext\"];\n\n const originalNewPage = browser.newPage.bind(browser);\n browser.newPage = (async (...args: Parameters<Browser[\"newPage\"]>) => {\n const page = await originalNewPage(...args);\n return augmentPage(page);\n }) as Browser[\"newPage\"];\n\n const originalContexts = browser.contexts.bind(browser);\n browser.contexts = (() =>\n originalContexts().map((context) =>\n augmentBrowserContext(context),\n )) as Browser[\"contexts\"];\n\n if (!(browser as unknown as { agent?: unknown }).agent) {\n defineHiddenProperty(browser, \"agent\", createAgentStub());\n }\n\n defineHiddenProperty(browser, BROWSER_PATCHED, true);\n\n return browser;\n}\n\nexport function augmentBrowserType<TBrowser extends Browser>(\n browserType: BrowserType<TBrowser>,\n): BrowserType<TBrowser> {\n if (\n (browserType as unknown as { [BROWSER_TYPE_PATCHED]?: boolean })[\n BROWSER_TYPE_PATCHED\n ]\n ) {\n return browserType;\n }\n\n const originalLaunch = browserType.launch.bind(browserType);\n browserType.launch = (async (\n ...args: Parameters<BrowserType<TBrowser>[\"launch\"]>\n ) => {\n const browser = await originalLaunch(...args);\n return augmentBrowser(browser);\n }) as BrowserType<TBrowser>[\"launch\"];\n\n const originalConnect = browserType.connect?.bind(browserType);\n if (originalConnect) {\n browserType.connect = (async (\n ...args: Parameters<NonNullable<BrowserType<TBrowser>[\"connect\"]>>\n ) => {\n const browser = await originalConnect(...args);\n return augmentBrowser(browser);\n }) as NonNullable<BrowserType<TBrowser>[\"connect\"]>;\n }\n\n const originalConnectOverCDP = browserType.connectOverCDP?.bind(browserType);\n if (originalConnectOverCDP) {\n browserType.connectOverCDP = (async (\n ...args: Parameters<NonNullable<BrowserType<TBrowser>[\"connectOverCDP\"]>>\n ) => {\n const browser = await originalConnectOverCDP(...args);\n return augmentBrowser(browser);\n }) as NonNullable<BrowserType<TBrowser>[\"connectOverCDP\"]>;\n }\n\n const originalLaunchPersistentContext =\n browserType.launchPersistentContext?.bind(browserType);\n if (originalLaunchPersistentContext) {\n browserType.launchPersistentContext = (async (\n ...args: Parameters<\n NonNullable<BrowserType<TBrowser>[\"launchPersistentContext\"]>\n >\n ) => {\n const context = await originalLaunchPersistentContext(...args);\n return augmentBrowserContext(context);\n }) as NonNullable<BrowserType<TBrowser>[\"launchPersistentContext\"]>;\n }\n\n defineHiddenProperty(browserType, BROWSER_TYPE_PATCHED, true);\n\n return browserType;\n}\n","import { Page, Locator } from \"@stablyai/internal-playwright-test\";\n\nexport function isPage(candidate: unknown): candidate is Page {\n return (\n typeof candidate === \"object\" &&\n candidate !== null &&\n typeof (candidate as Page).screenshot === \"function\" &&\n typeof (candidate as Page).goto === \"function\"\n );\n}\n\nexport function isLocator(candidate: unknown): candidate is Locator {\n return (\n typeof candidate === \"object\" &&\n candidate !== null &&\n typeof (candidate as Locator).screenshot === \"function\" &&\n typeof (candidate as Locator).nth === \"function\"\n );\n}\n","import { isObject } from \"../type-predicate/is-object\";\nimport { requireApiKey } from \"../runtime\";\nimport { SDK_METADATA_HEADERS } from \"./metadata\";\n\nconst PROMPT_ASSERTION_ENDPOINT = \"https://api.stably.ai/internal/v1/assert\";\n\ntype ParsedSuccessResponse = { success: boolean; reason?: string };\n\nconst parseSuccessResponse = (value: unknown): ParsedSuccessResponse => {\n if (!isObject(value)) {\n throw new Error(\"Verify prompt returned unexpected response shape\");\n }\n\n const { success, reason } = value;\n if (typeof success !== \"boolean\") {\n throw new Error(\"Verify prompt returned unexpected response shape\");\n }\n\n if (reason !== undefined && typeof reason !== \"string\") {\n throw new Error(\"Verify prompt returned unexpected response shape\");\n }\n\n return {\n success,\n reason,\n };\n};\n\ntype ParsedErrorResponse = { error: string };\n\nconst parseErrorResponse = (\n value: unknown,\n): ParsedErrorResponse | undefined => {\n if (!isObject(value)) {\n return undefined;\n }\n\n const { error } = value;\n return typeof error !== \"string\" ? undefined : { error };\n};\n\nexport async function verifyPrompt({\n prompt,\n screenshot,\n}: {\n prompt: string;\n screenshot: Uint8Array;\n}): Promise<{\n pass: boolean;\n reason?: string;\n}> {\n const apiKey = requireApiKey();\n\n const form = new FormData();\n form.append(\"prompt\", prompt);\n const u8 = Uint8Array.from(screenshot);\n const blob = new Blob([u8], { type: \"image/png\" });\n form.append(\"image\", blob, \"screenshot.png\");\n\n const response = await fetch(PROMPT_ASSERTION_ENDPOINT, {\n method: \"POST\",\n headers: {\n ...SDK_METADATA_HEADERS,\n Authorization: `Bearer ${apiKey}`,\n },\n body: form,\n });\n\n const parsed = await response.json().catch(() => undefined as unknown);\n\n if (response.ok) {\n const { success, reason } = parseSuccessResponse(parsed);\n return {\n pass: success,\n reason,\n };\n }\n\n const err = parseErrorResponse(parsed);\n throw new Error(\n `Verify prompt failed (${response.status})${err ? `: ${err.error}` : \"\"}`,\n );\n}\n","/**\n * Compare two equally sized images, pixel by pixel.\n *\n * @param {Uint8Array | Uint8ClampedArray} img1 First image data.\n * @param {Uint8Array | Uint8ClampedArray} img2 Second image data.\n * @param {Uint8Array | Uint8ClampedArray | void} output Image data to write the diff to, if provided.\n * @param {number} width Input images width.\n * @param {number} height Input images height.\n *\n * @param {Object} [options]\n * @param {number} [options.threshold=0.1] Matching threshold (0 to 1); smaller is more sensitive.\n * @param {boolean} [options.includeAA=false] Whether to skip anti-aliasing detection.\n * @param {number} [options.alpha=0.1] Opacity of original image in diff output.\n * @param {[number, number, number]} [options.aaColor=[255, 255, 0]] Color of anti-aliased pixels in diff output.\n * @param {[number, number, number]} [options.diffColor=[255, 0, 0]] Color of different pixels in diff output.\n * @param {[number, number, number]} [options.diffColorAlt=options.diffColor] Whether to detect dark on light differences between img1 and img2 and set an alternative color to differentiate between the two.\n * @param {boolean} [options.diffMask=false] Draw the diff over a transparent background (a mask).\n *\n * @return {number} The number of mismatched pixels.\n */\nexport default function pixelmatch(img1, img2, output, width, height, options = {}) {\n const {\n threshold = 0.1,\n alpha = 0.1,\n aaColor = [255, 255, 0],\n diffColor = [255, 0, 0],\n includeAA, diffColorAlt, diffMask\n } = options;\n\n if (!isPixelData(img1) || !isPixelData(img2) || (output && !isPixelData(output)))\n throw new Error('Image data: Uint8Array, Uint8ClampedArray or Buffer expected.');\n\n if (img1.length !== img2.length || (output && output.length !== img1.length))\n throw new Error('Image sizes do not match.');\n\n if (img1.length !== width * height * 4) throw new Error('Image data size does not match width/height.');\n\n // check if images are identical\n const len = width * height;\n const a32 = new Uint32Array(img1.buffer, img1.byteOffset, len);\n const b32 = new Uint32Array(img2.buffer, img2.byteOffset, len);\n let identical = true;\n\n for (let i = 0; i < len; i++) {\n if (a32[i] !== b32[i]) { identical = false; break; }\n }\n if (identical) { // fast path if identical\n if (output && !diffMask) {\n for (let i = 0; i < len; i++) drawGrayPixel(img1, 4 * i, alpha, output);\n }\n return 0;\n }\n\n // maximum acceptable square distance between two colors;\n // 35215 is the maximum possible value for the YIQ difference metric\n const maxDelta = 35215 * threshold * threshold;\n const [aaR, aaG, aaB] = aaColor;\n const [diffR, diffG, diffB] = diffColor;\n const [altR, altG, altB] = diffColorAlt || diffColor;\n let diff = 0;\n\n // compare each pixel of one image against the other one\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n\n const i = y * width + x;\n const pos = i * 4;\n\n // squared YUV distance between colors at this pixel position, negative if the img2 pixel is darker\n const delta = a32[i] === b32[i] ? 0 : colorDelta(img1, img2, pos, pos, false);\n\n // the color difference is above the threshold\n if (Math.abs(delta) > maxDelta) {\n // check it's a real rendering difference or just anti-aliasing\n const isAA = antialiased(img1, x, y, width, height, a32, b32) || antialiased(img2, x, y, width, height, b32, a32);\n if (!includeAA && isAA) {\n // one of the pixels is anti-aliasing; draw as yellow and do not count as difference\n // note that we do not include such pixels in a mask\n if (output && !diffMask) drawPixel(output, pos, aaR, aaG, aaB);\n\n } else {\n // found substantial difference not caused by anti-aliasing; draw it as such\n if (output) {\n if (delta < 0) {\n drawPixel(output, pos, altR, altG, altB);\n } else {\n drawPixel(output, pos, diffR, diffG, diffB);\n }\n }\n diff++;\n }\n\n } else if (output && !diffMask) {\n // pixels are similar; draw background as grayscale image blended with white\n drawGrayPixel(img1, pos, alpha, output);\n }\n }\n }\n\n // return the number of different pixels\n return diff;\n}\n\n/** @param {Uint8Array | Uint8ClampedArray} arr */\nfunction isPixelData(arr) {\n // work around instanceof Uint8Array not working properly in some Jest environments\n return ArrayBuffer.isView(arr) && arr.BYTES_PER_ELEMENT === 1;\n}\n\n/**\n * Check if a pixel is likely a part of anti-aliasing;\n * based on \"Anti-aliased Pixel and Intensity Slope Detector\" paper by V. Vysniauskas, 2009\n * @param {Uint8Array | Uint8ClampedArray} img\n * @param {number} x1\n * @param {number} y1\n * @param {number} width\n * @param {number} height\n * @param {Uint32Array} a32\n * @param {Uint32Array} b32\n */\nfunction antialiased(img, x1, y1, width, height, a32, b32) {\n const x0 = Math.max(x1 - 1, 0);\n const y0 = Math.max(y1 - 1, 0);\n const x2 = Math.min(x1 + 1, width - 1);\n const y2 = Math.min(y1 + 1, height - 1);\n const pos = y1 * width + x1;\n let zeroes = x1 === x0 || x1 === x2 || y1 === y0 || y1 === y2 ? 1 : 0;\n let min = 0;\n let max = 0;\n let minX = 0;\n let minY = 0;\n let maxX = 0;\n let maxY = 0;\n\n // go through 8 adjacent pixels\n for (let x = x0; x <= x2; x++) {\n for (let y = y0; y <= y2; y++) {\n if (x === x1 && y === y1) continue;\n\n // brightness delta between the center pixel and adjacent one\n const delta = colorDelta(img, img, pos * 4, (y * width + x) * 4, true);\n\n // count the number of equal, darker and brighter adjacent pixels\n if (delta === 0) {\n zeroes++;\n // if found more than 2 equal siblings, it's definitely not anti-aliasing\n if (zeroes > 2) return false;\n\n // remember the darkest pixel\n } else if (delta < min) {\n min = delta;\n minX = x;\n minY = y;\n\n // remember the brightest pixel\n } else if (delta > max) {\n max = delta;\n maxX = x;\n maxY = y;\n }\n }\n }\n\n // if there are no both darker and brighter pixels among siblings, it's not anti-aliasing\n if (min === 0 || max === 0) return false;\n\n // if either the darkest or the brightest pixel has 3+ equal siblings in both images\n // (definitely not anti-aliased), this pixel is anti-aliased\n return (hasManySiblings(a32, minX, minY, width, height) && hasManySiblings(b32, minX, minY, width, height)) ||\n (hasManySiblings(a32, maxX, maxY, width, height) && hasManySiblings(b32, maxX, maxY, width, height));\n}\n\n/**\n * Check if a pixel has 3+ adjacent pixels of the same color.\n * @param {Uint32Array} img\n * @param {number} x1\n * @param {number} y1\n * @param {number} width\n * @param {number} height\n */\nfunction hasManySiblings(img, x1, y1, width, height) {\n const x0 = Math.max(x1 - 1, 0);\n const y0 = Math.max(y1 - 1, 0);\n const x2 = Math.min(x1 + 1, width - 1);\n const y2 = Math.min(y1 + 1, height - 1);\n const val = img[y1 * width + x1];\n let zeroes = x1 === x0 || x1 === x2 || y1 === y0 || y1 === y2 ? 1 : 0;\n\n // go through 8 adjacent pixels\n for (let x = x0; x <= x2; x++) {\n for (let y = y0; y <= y2; y++) {\n if (x === x1 && y === y1) continue;\n zeroes += +(val === img[y * width + x]);\n if (zeroes > 2) return true;\n }\n }\n return false;\n}\n\n/**\n * Calculate color difference according to the paper \"Measuring perceived color difference\n * using YIQ NTSC transmission color space in mobile applications\" by Y. Kotsarenko and F. Ramos\n * @param {Uint8Array | Uint8ClampedArray} img1\n * @param {Uint8Array | Uint8ClampedArray} img2\n * @param {number} k\n * @param {number} m\n * @param {boolean} yOnly\n */\nfunction colorDelta(img1, img2, k, m, yOnly) {\n const r1 = img1[k];\n const g1 = img1[k + 1];\n const b1 = img1[k + 2];\n const a1 = img1[k + 3];\n const r2 = img2[m];\n const g2 = img2[m + 1];\n const b2 = img2[m + 2];\n const a2 = img2[m + 3];\n\n let dr = r1 - r2;\n let dg = g1 - g2;\n let db = b1 - b2;\n const da = a1 - a2;\n\n if (!dr && !dg && !db && !da) return 0;\n\n if (a1 < 255 || a2 < 255) { // blend pixels with background\n const rb = 48 + 159 * (k % 2);\n const gb = 48 + 159 * ((k / 1.618033988749895 | 0) % 2);\n const bb = 48 + 159 * ((k / 2.618033988749895 | 0) % 2);\n dr = (r1 * a1 - r2 * a2 - rb * da) / 255;\n dg = (g1 * a1 - g2 * a2 - gb * da) / 255;\n db = (b1 * a1 - b2 * a2 - bb * da) / 255;\n }\n\n const y = dr * 0.29889531 + dg * 0.58662247 + db * 0.11448223;\n\n if (yOnly) return y; // brightness difference only\n\n const i = dr * 0.59597799 - dg * 0.27417610 - db * 0.32180189;\n const q = dr * 0.21147017 - dg * 0.52261711 + db * 0.31114694;\n\n const delta = 0.5053 * y * y + 0.299 * i * i + 0.1957 * q * q;\n\n // encode whether the pixel lightens or darkens in the sign\n return y > 0 ? -delta : delta;\n}\n\n/**\n * @param {Uint8Array | Uint8ClampedArray} output\n * @param {number} pos\n * @param {number} r\n * @param {number} g\n * @param {number} b\n */\nfunction drawPixel(output, pos, r, g, b) {\n output[pos + 0] = r;\n output[pos + 1] = g;\n output[pos + 2] = b;\n output[pos + 3] = 255;\n}\n\n/**\n * @param {Uint8Array | Uint8ClampedArray} img\n * @param {number} i\n * @param {number} alpha\n * @param {Uint8Array | Uint8ClampedArray} output\n */\nfunction drawGrayPixel(img, i, alpha, output) {\n const val = 255 + (img[i] * 0.29889531 + img[i + 1] * 0.58662247 + img[i + 2] * 0.11448223 - 255) * alpha * img[i + 3] / 255;\n drawPixel(output, i, val, val, val);\n}\n","// Note: pixelmatch seems to be pure ESM so mark it as noExternal in tsup config\nimport pixelmatch from \"pixelmatch\";\nimport { PNG } from \"pngjs\";\nimport * as jpeg from \"jpeg-js\";\n\nconst isPng = (buffer: Buffer): boolean => {\n return (\n buffer.length >= 8 &&\n buffer[0] === 0x89 &&\n buffer[1] === 0x50 &&\n buffer[2] === 0x4e &&\n buffer[3] === 0x47 &&\n buffer[4] === 0x0d &&\n buffer[5] === 0x0a &&\n buffer[6] === 0x1a &&\n buffer[7] === 0x0a\n );\n};\n\nconst isJpeg = (buffer: Buffer): boolean => {\n return buffer.length >= 2 && buffer[0] === 0xff && buffer[1] === 0xd8;\n};\n\nconst decodeImage = (\n buffer: Buffer,\n): { data: Uint8Array; width: number; height: number } => {\n if (isPng(buffer)) {\n const png = PNG.sync.read(buffer);\n return { data: png.data, width: png.width, height: png.height };\n }\n if (isJpeg(buffer)) {\n const img = jpeg.decode(buffer, { maxMemoryUsageInMB: 1024 });\n return { data: img.data, width: img.width, height: img.height };\n }\n // Default to PNG decode; if it fails upstream, treat as different sizes\n const png = PNG.sync.read(buffer);\n return { data: png.data, width: png.width, height: png.height };\n};\n\nexport const imagesAreSimilar = ({\n image1,\n image2,\n threshold,\n}: {\n image1: Buffer;\n image2: Buffer;\n threshold: number;\n}): boolean => {\n const decodedImage1 = decodeImage(image1);\n const decodedImage2 = decodeImage(image2);\n if (\n decodedImage1.width !== decodedImage2.width ||\n decodedImage1.height !== decodedImage2.height\n ) {\n return false;\n }\n const diffRgbaData = new Uint8Array(\n decodedImage1.width * decodedImage1.height * 4,\n );\n const numDiffPixels = pixelmatch(\n decodedImage1.data,\n decodedImage2.data,\n diffRgbaData,\n decodedImage1.width,\n decodedImage1.height,\n { threshold },\n );\n\n return numDiffPixels === 0;\n};\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport type { ScreenshotPromptOptions } from \"./index\";\nimport { isPage } from \"./playwright-type-predicates\";\nimport { imagesAreSimilar } from \"./image-compare\";\n\nexport async function takeStableScreenshot(\n target: Page | Locator,\n options?: ScreenshotPromptOptions,\n): Promise<Buffer> {\n const page = isPage(target) ? target : target.page();\n\n // Use a small budget for stabilization within the overall assertion timeout.\n // We allocate up to 25% of the total timeout (bounded between 300ms and 2000ms).\n const totalTimeout =\n (options as { timeout?: number } | undefined)?.timeout ?? 5000;\n // Budget is 25% of the total timeout\n const stabilizationBudgetMs = Math.floor(totalTimeout * 0.25);\n const stabilityBudgetMs = Math.min(\n 2000,\n Math.max(300, stabilizationBudgetMs),\n );\n const deadline = Date.now() + stabilityBudgetMs;\n\n let actual: Buffer | undefined;\n let previous: Buffer | undefined;\n const pollIntervals = [0, 100, 250, 500];\n let isFirstIteration = true;\n\n while (true) {\n if (Date.now() >= deadline) break;\n const delay = pollIntervals.length ? pollIntervals.shift()! : 1000;\n if (delay) {\n await page.waitForTimeout(delay);\n }\n previous = actual;\n actual = await target.screenshot(options);\n if (\n !isFirstIteration &&\n actual &&\n previous &&\n imagesAreSimilar({\n image1: previous,\n image2: actual,\n threshold: options?.threshold ?? 0.02,\n })\n ) {\n return actual;\n }\n isFirstIteration = false;\n }\n return actual ?? (await target.screenshot(options));\n}\n","import type { Locator, Page } from \"@stablyai/internal-playwright-test\";\nimport type { ScreenshotPromptOptions } from \"./index\";\n\nimport { isLocator, isPage } from \"./playwright-type-predicates\";\n\nimport { verifyPrompt } from \"./ai/verify-prompt\";\nimport { takeStableScreenshot } from \"./screenshot\";\n\ntype VerifyTargetType = \"page\" | \"locator\";\n\ntype MatcherContext = {\n isNot: boolean;\n message?: () => string;\n};\n\nfunction createFailureMessage({\n targetType,\n condition,\n didPass,\n isNot,\n reason,\n}: {\n targetType: VerifyTargetType;\n condition: string;\n didPass: boolean;\n isNot: boolean;\n reason?: string;\n}): string {\n const expectation = isNot ? \"not to satisfy\" : \"to satisfy\";\n const result = didPass ? \"it did\" : \"it did not\";\n\n let message = `Expected ${targetType} ${expectation} ${JSON.stringify(condition)}, but ${result}.`;\n if (reason) {\n message += `\\n\\nReason: ${reason}`;\n }\n\n return message;\n}\n\nexport const stablyPlaywrightMatchers = {\n async toMatchScreenshotPrompt(\n this: MatcherContext,\n received: Page | Locator,\n condition: string,\n options?: ScreenshotPromptOptions,\n ) {\n const target = isPage(received)\n ? received\n : isLocator(received)\n ? received\n : undefined;\n if (!target) {\n // Should never happen\n throw new Error(\n \"toMatchScreenshotPrompt only supports Playwright Page and Locator instances.\",\n );\n }\n const targetType: VerifyTargetType = isPage(target) ? \"page\" : \"locator\";\n\n // Wait for two consecutive identical screenshots before sending to AI\n const screenshot = await takeStableScreenshot(target, options);\n\n const verifyResult = await verifyPrompt({ prompt: condition, screenshot });\n\n return {\n pass: verifyResult.pass,\n message: () =>\n createFailureMessage({\n targetType,\n condition,\n didPass: verifyResult.pass,\n reason: verifyResult.reason,\n isNot: this.isNot,\n }),\n };\n },\n} as const;\n"],"mappings":"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","names":["png"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stablyai/playwright-base",
3
- "version": "0.1.7-next.9",
3
+ "version": "0.1.7",
4
4
  "description": "Shared augmentation runtime for Stably Playwright wrappers",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -18,7 +18,7 @@
18
18
  "node": ">=18"
19
19
  },
20
20
  "dependencies": {
21
- "@stablyai/internal-playwright-test": "0.1.8-next.2",
21
+ "@stablyai/internal-playwright-test": "0.1.15",
22
22
  "jpeg-js": "^0.4.4",
23
23
  "pixelmatch": "^7.1.0",
24
24
  "pngjs": "^7.0.0"