@gfxlabs/third-eye-cli 3.24.0 → 3.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.mjs +6 -6
- package/dist/bin.mjs.map +1 -1
- package/package.json +1 -1
package/dist/bin.mjs
CHANGED
|
@@ -2094,15 +2094,15 @@ const generateShotName = (title, name) => [kebabCase(title), kebabCase(name)].fi
|
|
|
2094
2094
|
* using git diff. Returns an empty array if git is unavailable or fails.
|
|
2095
2095
|
*/
|
|
2096
2096
|
const getChangedFiles = (baseRef) => {
|
|
2097
|
-
|
|
2098
|
-
|
|
2097
|
+
const refsToTry = [baseRef, `origin/${baseRef}`];
|
|
2098
|
+
for (const ref of refsToTry) try {
|
|
2099
|
+
return execSync(`git diff --name-only ${ref}...HEAD`, {
|
|
2099
2100
|
encoding: "utf-8",
|
|
2100
2101
|
timeout: 3e4
|
|
2101
2102
|
}).trim().split("\n").filter((f) => f.length > 0);
|
|
2102
|
-
} catch
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
}
|
|
2103
|
+
} catch {}
|
|
2104
|
+
log.process("warn", "general", `Failed to get changed files for refs: ${refsToTry.join(", ")}`);
|
|
2105
|
+
return [];
|
|
2106
2106
|
};
|
|
2107
2107
|
const runner = async (config) => {
|
|
2108
2108
|
const executionStart = process.hrtime();
|
package/dist/bin.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.mjs","names":["z","z","odiffCompare","kebabCase","generateBrowserConfig","fse"],"sources":["../src/log.ts","../src/configHelper.ts","../src/schemas.ts","../src/config.ts","../src/constants.ts","../src/utils.ts","../src/compare/utils.ts","../src/compare/compare.ts","../src/checkDifferences.ts","../src/shots/utils.ts","../src/crawler/ladleScreenshots.ts","../src/crawler/storybook.ts","../src/crawler/pageScreenshots.ts","../src/shots/shots.ts","../src/crawler/utils.ts","../src/crawler/histoireScreenshots.ts","../src/createShots.ts","../src/git.ts","../../shared/dist/client.js","../src/api.ts","../src/upload.ts","../src/turbosnap.ts","../src/runner.ts","../src/docker-runner/utils.ts","../src/docker-runner/index.ts","../src/generatePagesFromSitemap.ts","../src/bin.ts"],"sourcesContent":["import type { ShotMode } from \"./types.js\";\n\ntype LogLevel = \"info\" | \"error\" | \"warn\" | \"debug\";\n\ntype LogEntry = {\n timestamp: Date;\n level: LogLevel;\n item?: {\n shotMode: ShotMode;\n uniqueItemId: string;\n itemIndex: number;\n totalItems: number;\n };\n source: \"process\" | \"browser\";\n context: \"general\" | \"api\" | \"console\" | \"network\" | \"timeout\" | \"config\";\n content: unknown[];\n};\n\nexport type LogMemory = LogEntry[];\n\nexport const logMemory: LogMemory = [];\n\nconst serializeError = (error: Error) => ({\n message: error.message,\n name: error.name,\n stack: error.stack,\n});\n\nconst serializeErrors = (content: unknown[]) =>\n content.map((item) => {\n if (item instanceof Error) {\n return serializeError(item);\n }\n\n return item;\n });\n\nconst renderLog = (entry: LogEntry) => {\n if (entry.source === \"browser\" && entry.context === \"console\") {\n return;\n }\n\n if (entry.source === \"browser\" && entry.context === \"network\") {\n return;\n }\n\n const { log } = console;\n const logPrefix = [];\n\n if (entry.item) {\n logPrefix.push(`[${entry.item.itemIndex + 1}/${entry.item.totalItems}]`);\n }\n\n if (![\"general\", \"api\", \"config\"].includes(entry.context)) {\n logPrefix.push(`[${entry.context}]`);\n }\n\n if (entry.level === \"error\") {\n logPrefix.push(`❌`);\n }\n\n if (entry.item?.uniqueItemId) {\n log(...logPrefix, ...entry.content, `(${entry.item?.uniqueItemId})`);\n } else {\n log(...logPrefix, ...entry.content);\n }\n};\n\nexport const log = {\n item: (item: LogEntry[\"item\"]) => ({\n process(level: LogEntry[\"level\"], context: LogEntry[\"context\"], ...content: unknown[]) {\n const entry: LogEntry = {\n timestamp: new Date(),\n level,\n item,\n source: \"process\",\n context,\n content,\n };\n\n renderLog(entry);\n logMemory.push({\n ...entry,\n content: serializeErrors(content),\n });\n },\n browser(level: LogEntry[\"level\"], context: LogEntry[\"context\"], ...content: unknown[]) {\n const entry: LogEntry = {\n timestamp: new Date(),\n level,\n item,\n source: \"browser\",\n context,\n content,\n };\n\n renderLog(entry);\n logMemory.push({\n ...entry,\n content: serializeErrors(content),\n });\n },\n }),\n process(level: LogEntry[\"level\"], context: LogEntry[\"context\"], ...content: unknown[]) {\n const entry: LogEntry = {\n timestamp: new Date(),\n level,\n source: \"process\",\n context,\n content,\n };\n\n renderLog(entry);\n logMemory.push({\n ...entry,\n content: serializeErrors(content),\n });\n },\n browser(level: LogEntry[\"level\"], context: LogEntry[\"context\"], ...content: unknown[]) {\n const entry: LogEntry = {\n timestamp: new Date(),\n level,\n source: \"browser\",\n context,\n content,\n };\n\n renderLog(entry);\n logMemory.push({\n ...entry,\n content: serializeErrors(content),\n });\n },\n};\n","import { bundleRequire } from \"bundle-require\";\nimport { log } from \"./log.js\";\n\nexport const loadProjectConfigFile = async (configFilepath: string): Promise<unknown> => {\n try {\n const { mod } = await bundleRequire<{\n default?: unknown;\n config?: unknown;\n }>({\n filepath: configFilepath,\n esbuildOptions: {\n // logLevel: 'silent',\n },\n });\n\n return mod?.default ?? mod?.config ?? mod;\n } catch (error: unknown) {\n log.process(\"error\", \"config\", error);\n throw error;\n }\n};\n\nexport const loadTSProjectConfigFile = async (configFilepath: string): Promise<unknown> => {\n try {\n const imported = (await import(configFilepath)) as Record<string, unknown>;\n\n return imported?.default ?? imported?.config;\n } catch (error: unknown) {\n // @ts-expect-error Error type definition is missing 'code'\n if ([\"ERR_MODULE_NOT_FOUND\", \"MODULE_NOT_FOUND\"].includes(error.code)) {\n log.process(\"error\", \"config\", \"Failed to load TypeScript configuration file\");\n process.exit(1);\n }\n\n log.process(\"error\", \"config\", error);\n process.exit(1);\n }\n};\n","import * as z from \"zod\";\nimport type { BrowserContextOptions } from \"playwright-core\";\n\nexport const BrowserSchema = z.enum([\"chromium\", \"firefox\", \"webkit\"]);\n\nexport const ShotModeSchema = z.enum([\"storybook\", \"ladle\", \"histoire\", \"page\", \"custom\"]);\n\nexport const MaskSchema = z.object({\n /**\n * CSS selector for the element to mask\n * Examples:\n * - `#my-id`: Selects the element with the id `my-id`\n * - `.my-class`: Selects all elements with the class `my-class`\n * - `div`: Selects all `div` elements\n * - `div.my-class`: Selects all `div` elements with the class `my-class`\n * - `li:nth-child(2n)`: Selects all even `li` elements\n * - `[data-testid=\"hero-banner\"]`: Selects all elements with the attribute `data-testid` set to `hero-banner`\n * - `div > p`: Selects all `p` elements that are direct children of a `div` element\n */\n selector: z.string(),\n});\n\nexport const ShotItemSchema = z.object({\n shotMode: ShotModeSchema,\n id: z.string(),\n shotName: z.string(),\n url: z.string(),\n filePathBaseline: z.string(),\n filePathCurrent: z.string(),\n filePathDifference: z.string(),\n browserConfig: z.custom<BrowserContextOptions>().optional(),\n threshold: z.number(),\n waitBeforeScreenshot: z.number().optional(),\n importPath: z.string().optional(),\n mask: z.array(MaskSchema).optional(),\n viewport: z\n .object({\n width: z.number(),\n height: z.number().optional(),\n })\n .optional(),\n breakpoint: z.number().optional(),\n breakpointGroup: z.string().optional(),\n elementLocator: z.string().optional(),\n waitForSelector: z.string().optional(),\n // Story metadata for platform integration\n componentPath: z.string().optional(),\n storyName: z.string().optional(),\n /** Original Storybook story ID (e.g. \"pages-projectsettings--default\") */\n storyId: z.string().optional(),\n storyArgs: z.record(z.string(), z.unknown()).optional(),\n tags: z.array(z.string()).optional(),\n});\n","import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { type LaunchOptions, type BrowserContextOptions, type Page } from \"playwright-core\";\nimport * as z from \"zod\";\nimport { loadProjectConfigFile, loadTSProjectConfigFile } from \"./configHelper.js\";\nimport { log } from \"./log.js\";\nimport { BrowserSchema, MaskSchema, ShotModeSchema } from \"./schemas.js\";\n\nexport const PageScreenshotParameterSchema = z.object({\n /**\n * Path to the page to take a screenshot of (e.g. /login)\n */\n path: z.string(),\n\n /**\n * Unique name for the page\n */\n name: z.string(),\n\n /**\n * Time to wait before taking a screenshot\n * @default 1_000\n */\n waitBeforeScreenshot: z.number().default(1000),\n\n /**\n * Threshold for the difference between the baseline and current image\n *\n * Values between 0 and 1 are interpreted as percentage of the image size\n *\n * Values greater or equal to 1 are interpreted as pixel count.\n * @default 0\n */\n threshold: z.number().default(0),\n\n /**\n * Define custom breakpoints for the page as width in pixels\n * @default []\n * @example\n * [ 320, 768, 1280 ]\n */\n breakpoints: z.array(z.number()).optional(),\n\n /**\n * Define a custom viewport for the page\n * @default { width: 1280, height: 720 }\n */\n viewport: z\n .object({\n width: z.number().optional(),\n height: z.number().optional(),\n })\n .optional(),\n\n /**\n * Define areas for the page where differences will be ignored\n */\n mask: z.array(MaskSchema).optional(),\n});\n\nexport type PageScreenshotParameter = z.infer<typeof PageScreenshotParameterSchema>;\n\nconst StorybookShotsSchema = z.object({\n /**\n * URL of the Storybook instance or local folder\n * @default 'storybook-static'\n */\n storybookUrl: z.string(),\n\n /**\n * Define areas for all stories where differences will be ignored\n */\n mask: z.array(MaskSchema).optional(),\n\n /**\n * Define custom breakpoints for all Storybook shots as width in pixels\n * @default []\n * @example\n * [ 320, 768, 1280 ]\n */\n breakpoints: z.array(z.number()).optional(),\n\n /**\n * Target specific element on page with a selector\n */\n elementLocator: z.string().optional(),\n\n /**\n * Wait for a specific selector before taking a screenshot\n * @example '[data-storyloaded]'\n */\n waitForSelector: z.string().optional(),\n});\n\nconst LadleShotsSchema = z.object({\n /**\n * URL of the Ladle served instance\n * @default 'http://localhost:61000'\n */\n ladleUrl: z.string(),\n\n /**\n * Define areas for all stories where differences will be ignored\n */\n mask: z.array(MaskSchema).optional(),\n\n /**\n * Define custom breakpoints for all Ladle shots as width in pixels\n * @default []\n * @example\n * [ 320, 768, 1280 ]\n */\n breakpoints: z.array(z.number()).optional(),\n\n /**\n * Wait for a specific selector before taking a screenshot\n * @example '[data-storyloaded]'\n */\n waitForSelector: z.string().optional(),\n});\n\nconst HistoireShotsSchema = z.object({\n /**\n * URL of the Histoire served instance\n * @default 'http://localhost:61000'\n */\n histoireUrl: z.string(),\n\n /**\n * Define areas for all stories where differences will be ignored\n */\n mask: z.array(MaskSchema).optional(),\n\n /**\n * Define custom breakpoints for all Histoire shots as width in pixels\n * @default []\n * @example\n * [ 320, 768, 1280 ]\n */\n breakpoints: z.array(z.number()).optional(),\n\n /**\n * Wait for a specific selector before taking a screenshot\n * @example '[data-storyloaded]'\n */\n waitForSelector: z.string().optional(),\n});\n\nconst PageShotsSchema = z.object({\n /**\n * Paths to take screenshots of\n */\n pages: z.array(PageScreenshotParameterSchema),\n\n /**\n * Url that must return a JSON compatible with `PageScreenshotParameter[]`. It is useful when you want to autogenerate the pages that you want to run third-eye on. Can be used together with `pages` as both are composed into a single run.\n */\n pagesJsonUrl: z.string().optional(),\n\n /**\n * Url that must return a JSON compatible with `PageScreenshotParameter[]`. It is useful when you want to autogenerate the pages that you want to run third-eye on. Can be used together with `pages` as both are composed into a single run.\n */\n pagesJsonRefiner: z\n .custom<(pages: PageScreenshotParameter[]) => PageScreenshotParameter[]>()\n .optional(),\n\n /**\n * Base URL of the running application (e.g. http://localhost:3000)\n */\n baseUrl: z.string(),\n\n /**\n * Define areas for all pages where differences will be ignored\n */\n mask: z.array(MaskSchema).optional(),\n\n /**\n * Define custom breakpoints for all page shots as width in pixels\n * @default []\n * @example\n * [ 320, 768, 1280 ]\n */\n breakpoints: z.array(z.number()).optional(),\n\n /**\n * Wait for a specific selector before taking a screenshot\n * @example '[data-storyloaded]'\n */\n waitForSelector: z.string().optional(),\n});\n\nconst CustomShotsSchema = z.object({\n /**\n * Path to current shots folder\n *\n * This path cannot be the same as the `imagePathCurrent` path\n */\n currentShotsPath: z.string(),\n});\n\nconst StoryLikeSchema = z.object({\n shotMode: ShotModeSchema,\n id: z.string().optional(),\n kind: z.string().optional(),\n story: z.string().optional(),\n shotName: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n filePathBaseline: z.string().optional(),\n filePathCurrent: z.string().optional(),\n filePathDifference: z.string().optional(),\n});\n\ntype StoryLike = z.infer<typeof StoryLikeSchema>;\n\nconst ShotItem = z.object({\n shotMode: ShotModeSchema,\n id: z.string(),\n shotName: z.string(),\n url: z.string(),\n filePathBaseline: z.string(),\n filePathCurrent: z.string(),\n filePathDifference: z.string(),\n browserConfig: z.custom<BrowserContextOptions>().optional(),\n threshold: z.number(),\n waitBeforeScreenshot: z.number().optional(),\n importPath: z.string().optional(),\n mask: z.array(MaskSchema).optional(),\n viewport: z\n .object({\n width: z.number(),\n height: z.number().optional(),\n })\n .optional(),\n breakpoint: z.number().optional(),\n breakpointGroup: z.string().optional(),\n elementLocator: z.string().optional(),\n waitForSelector: z.string().optional(),\n});\n\nconst TimeoutsSchema = z.object({\n /**\n * Timeout for fetching stories\n * @default 30_000\n */\n fetchStories: z.number().default(30_000),\n\n /**\n * Timeout for loading the state of the page\n * @default 30_000\n */\n loadState: z.number().default(30_000),\n\n /**\n * Timeout for waiting for network requests to finish\n * @default 30_000\n */\n networkRequests: z.number().default(30_000),\n});\n\nconst BaseConfigSchema = z.object({\n /**\n * Browser to use: chromium, firefox, or webkit\n * @default 'chromium'\n */\n browser: z\n .union([BrowserSchema, z.array(BrowserSchema).default([\"chromium\"])])\n .default(\"chromium\"),\n\n /**\n * Enable Storybook mode\n */\n storybookShots: StorybookShotsSchema.optional(),\n\n /**\n * Enable Ladle mode\n */\n ladleShots: LadleShotsSchema.optional(),\n\n /**\n * Enable Histoire mode\n */\n histoireShots: HistoireShotsSchema.optional(),\n\n /**\n * Enable Page mode\n */\n pageShots: PageShotsSchema.optional(),\n\n /**\n * Enable Custom mode\n */\n customShots: CustomShotsSchema.optional(),\n\n /**\n * Path to the current image folder\n * @default '.thirdeye/current/'\n */\n imagePathCurrent: z.string().default(\".thirdeye/current/\"),\n\n /**\n * Define custom breakpoints for all tests as width in pixels\n * @default []\n * @example\n * [ 320, 768, 1280 ]\n */\n breakpoints: z.array(z.number()).optional(),\n\n /**\n * Number of concurrent shots to take\n * @default 5\n */\n shotConcurrency: z.number().default(5),\n\n /**\n * Timeouts for various stages of the test\n */\n timeouts: TimeoutsSchema.default({\n fetchStories: 30_000,\n loadState: 30_000,\n networkRequests: 30_000,\n }),\n\n /**\n * Time to wait before taking a screenshot\n * @default 1_000\n */\n waitBeforeScreenshot: z.number().default(1000),\n\n /**\n * Time to wait for the first network request to start\n * @default 1_000\n */\n waitForFirstRequest: z.number().default(1000),\n\n /**\n * Time to wait for the last network request to start\n * @default 1_000\n */\n waitForLastRequest: z.number().default(1000),\n\n /**\n * Threshold for the difference between the baseline and current image\n *\n * Values between 0 and 1 are interpreted as percentage of the image size\n *\n * Values greater or equal to 1 are interpreted as pixel count.\n * @default 0\n */\n threshold: z.number().default(0),\n\n /**\n * Enable TurboSnap: only re-capture stories whose source files changed\n * since the last build on the base branch. Requires platform mode.\n * @default false\n */\n turboSnap: z.boolean().optional().default(false),\n\n /**\n * How often to retry a shot for a stable result\n * @default 0\n */\n flakynessRetries: z.number().default(0),\n\n /**\n * Time to wait between flakyness retries\n * @default 2_000\n */\n waitBetweenFlakynessRetries: z.number().default(2000),\n\n /**\n * Global shot filter\n */\n filterShot: z.custom<(story: StoryLike) => boolean>().optional(),\n\n /**\n * Shot and file name generator for images\n */\n shotNameGenerator: z.custom<(story: StoryLike) => string>().optional(),\n\n /**\n * Configure browser context options\n */\n configureBrowser: z.custom<(story: StoryLike) => BrowserContextOptions>().optional(),\n\n /**\n * Configure page before screenshot\n */\n beforeScreenshot: z.custom<(page: Page, story: StoryLike) => Promise<void>>().optional(),\n\n /**\n * Perform actions after screenshot was taken\n */\n afterScreenshot: z.custom<(page: Page, story: StoryLike) => Promise<void>>().optional(),\n\n /**\n * Launch options for the browser\n */\n browserLaunchOptions: z\n .object({\n chromium: z.custom<LaunchOptions>().optional(),\n firefox: z.custom<LaunchOptions>().optional(),\n webkit: z.custom<LaunchOptions>().optional(),\n })\n .optional(),\n});\n\nexport const PlatformModeConfigSchema = BaseConfigSchema.extend({\n /**\n * URL of the Third Eye server API endpoint\n * @default 'https://third-eye.gfxlabs.cloud'\n */\n thirdEyePlatform: z.string().default(\"https://third-eye.gfxlabs.cloud\"),\n\n /**\n * API key for the Third Eye platform\n */\n apiKey: z.string(),\n\n /**\n * Project ID\n */\n thirdEyeProjectId: z\n .string({\n error: \"Required\",\n })\n // @ts-expect-error If not set, it will be caught during config validation\n .default(process.env.THIRD_EYE_PROJECT_ID),\n\n /**\n * Organization ID for the Lost Pixel platform\n */\n thirdEyeOrgId: z\n .string({\n error: \"Required\",\n })\n // @ts-expect-error If not set, it will be caught during config validation\n .default(process.env.THIRD_EYE_ORG_ID),\n\n /**\n * CI build ID\n */\n ciBuildId: z\n .string({\n error: \"Required (can be set via `CI_BUILD_ID` env variable)\",\n })\n // @ts-expect-error If not set, it will be caught during config validation\n .default(process.env.CI_BUILD_ID),\n\n /**\n * CI build number\n */\n ciBuildNumber: z\n .string({\n error: \"Required (can be set via `CI_BUILD_NUMBER` env variable)\",\n })\n // @ts-expect-error If not set, it will be caught during config validation\n .default(process.env.CI_BUILD_NUMBER),\n\n /**\n * Git repository name (e.g. 'third-eye/third-eye-storybook')\n */\n repository: z\n .string({\n error: \"Required (can be set via `REPOSITORY` env variable)\",\n })\n // @ts-expect-error If not set, it will be caught during config validation\n .default(process.env.REPOSITORY ?? process.env.GITHUB_REPOSITORY),\n\n /**\n * Git branch name (e.g. 'main')\n */\n commitRefName: z\n .string({\n error: \"Required (can be set via `COMMIT_REF_NAME` env variable)\",\n })\n .default(\n // @ts-expect-error If not set, it will be caught during config validation\n process.env.COMMIT_REF_NAME ?? process.env.GITHUB_HEAD_REF ?? process.env.GITHUB_REF_NAME,\n ),\n\n /**\n * Git commit SHA (e.g. 'b9b8b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9')\n */\n commitHash: z\n .string({\n error: \"Required (can be set via `COMMIT_HASH` env variable)\",\n })\n // @ts-expect-error If not set, it will be caught during config validation\n .default(process.env.COMMIT_HASH ?? process.env.GITHUB_SHA),\n\n /**\n * Base branch (the target branch of a PR, e.g. 'main')\n * Auto-detected in GitHub Actions from GITHUB_BASE_REF\n */\n baseBranch: z\n .string()\n .optional()\n .default(process.env.GITHUB_BASE_REF ?? \"\"),\n\n /**\n * Pull request number\n * Auto-detected in GitHub Actions from the event payload\n */\n prNumber: z.coerce\n .number()\n .optional()\n .default(\n // @ts-expect-error If not set, it will be caught during config validation\n process.env.THIRD_EYE_PR_NUMBER ? Number(process.env.THIRD_EYE_PR_NUMBER) : undefined,\n ),\n\n /**\n * Whether to set the GitHub status check on process start or not\n *\n * Setting this option to `true` makes only sense if the repository settings have pending status checks disabled\n * @default false\n */\n setPendingStatusCheck: z.boolean().default(false),\n\n /**\n * Path to the storybook-static directory to upload for hosted storybook.\n * When set, the CLI will upload the built storybook to the platform.\n */\n storybookStaticDir: z.string().optional(),\n});\n\nexport const GenerateOnlyModeConfigSchema = BaseConfigSchema.extend({\n /**\n * Run in local mode\n * @deprecated Defaults to running in generateOnly mode\n */\n generateOnly: z.boolean().optional(),\n\n /**\n * Flag that decides if process should exit if a difference is found\n */\n failOnDifference: z.boolean().optional(),\n\n /**\n * Path to the baseline image folder\n * @default '.thirdeye/baseline/'\n */\n imagePathBaseline: z.string().default(\".thirdeye/baseline/\"),\n\n /**\n * Path to the difference image folder\n * @default '.thirdeye/difference/'\n */\n imagePathDifference: z.string().default(\".thirdeye/difference/\"),\n\n /**\n * Number of concurrent screenshots to compare\n * @default 10\n */\n compareConcurrency: z.number().default(10),\n\n /**\n * Which comparison engine to use for diffing images\n * @default 'pixelmatch'\n */\n compareEngine: z.enum([\"pixelmatch\", \"odiff\"]).default(\"pixelmatch\"),\n\n /**\n * Filter stories to take screenshots of and run comparisons on (existing shots remain untouched)\n */\n filterItemsToCheck: z.custom<(item: z.infer<typeof ShotItem>) => boolean>().optional(),\n});\n\nexport const ConfigSchema = z.union([PlatformModeConfigSchema, GenerateOnlyModeConfigSchema]);\n\n// use partial() specifically for the inferred type\nexport const FlexibleConfigSchema = z.union([\n PlatformModeConfigSchema.extend({\n timeouts: TimeoutsSchema.partial(),\n pageShots: PageShotsSchema.extend({\n pages: z.array(PageScreenshotParameterSchema.partial()),\n }),\n }).partial(),\n GenerateOnlyModeConfigSchema.extend({\n timeouts: TimeoutsSchema.partial(),\n pageShots: PageShotsSchema.extend({\n pages: z.array(PageScreenshotParameterSchema.partial()),\n }),\n }).partial(),\n]);\n\nexport type PlatformModeConfig = z.infer<typeof PlatformModeConfigSchema>;\nexport type GenerateOnlyModeConfig = z.infer<typeof GenerateOnlyModeConfigSchema>;\n\nexport type Config = z.infer<typeof ConfigSchema>;\nexport type CustomProjectConfig = z.infer<typeof FlexibleConfigSchema>;\n\nexport let config: Config;\n\nexport const isPlatformModeConfig = (\n userConfig: PlatformModeConfig | GenerateOnlyModeConfig,\n): userConfig is PlatformModeConfig =>\n (\"apiKey\" in userConfig && typeof userConfig.apiKey === \"string\") ||\n (\"thirdEyeProjectId\" in userConfig && typeof userConfig.thirdEyeProjectId === \"string\") ||\n (\"thirdEyeOrgId\" in userConfig && typeof userConfig.thirdEyeOrgId === \"string\");\n\nconst printConfigErrors = (error: z.ZodError) => {\n for (const issue of error.issues) {\n log.process(\n \"error\",\n \"config\",\n [\n \"Configuration error:\",\n ` - Path: ${issue.path.join(\".\")}`,\n ` - Message: ${issue.message}`,\n ].join(\"\\n\"),\n );\n }\n};\n\nexport const parseConfig = (userConfig: Config) => {\n if (isPlatformModeConfig(userConfig)) {\n const platformCheck = PlatformModeConfigSchema.safeParse(userConfig);\n\n if (platformCheck.success) {\n return platformCheck.data;\n }\n\n printConfigErrors(platformCheck.error);\n } else {\n const generateOnlyCheck = GenerateOnlyModeConfigSchema.safeParse(userConfig);\n\n if (generateOnlyCheck.success) {\n return generateOnlyCheck.data;\n }\n\n printConfigErrors(generateOnlyCheck.error);\n }\n\n throw new Error(\"Configuration error\");\n};\n\nconst configDirBase = process.env.THIRD_EYE_CONFIG_DIR ?? process.cwd();\n\nconst configFileNameBase = path.join(\n path.isAbsolute(configDirBase) ? \"\" : process.cwd(),\n configDirBase,\n \"thirdeye.config\",\n);\n\nconst loadProjectConfig = async (): Promise<Config> => {\n log.process(\"info\", \"config\", \"Loading project config ...\");\n log.process(\"info\", \"config\", \"Current working directory:\", process.cwd());\n\n if (process.env.THIRD_EYE_CONFIG_DIR) {\n log.process(\"info\", \"config\", \"Defined config directory:\", process.env.THIRD_EYE_CONFIG_DIR);\n }\n\n const configExtensions = [\"ts\", \"js\", \"cjs\", \"mjs\"];\n const configExtensionsString = configExtensions.join(\"|\");\n\n log.process(\n \"info\",\n \"config\",\n \"Looking for config file:\",\n `${configFileNameBase}.(${configExtensionsString})`,\n );\n\n const configFiles = configExtensions\n .map((ext) => `${configFileNameBase}.${ext}`)\n .filter((file) => existsSync(file));\n\n if (configFiles.length === 0) {\n log.process(\n \"error\",\n \"config\",\n `Couldn't find project config file 'thirdeye.config.(${configExtensionsString})'`,\n );\n process.exit(1);\n }\n\n if (configFiles.length > 1) {\n log.process(\"info\", \"config\", \"✅ Found multiple config files, taking:\", configFiles[0]);\n } else {\n log.process(\"info\", \"config\", \"✅ Found config file:\", configFiles[0]);\n }\n\n const configFile = configFiles[0];\n\n try {\n const imported = (await loadProjectConfigFile(configFile)) as Config;\n\n return imported;\n } catch {\n log.process(\"error\", \"config\", \"Loading config using ESBuild failed, using fallback option\");\n\n try {\n if (existsSync(`${configFileNameBase}.js`)) {\n const imported = (await import(`${configFileNameBase}.js`)) as Record<string, unknown>;\n const projectConfig = (imported?.default ?? imported?.config ?? imported) as Config;\n\n log.process(\n \"info\",\n \"config\",\n \"✅ Successfully loaded configuration from:\",\n `${configFileNameBase}.js`,\n );\n\n return projectConfig;\n }\n\n if (existsSync(`${configFileNameBase}.ts`)) {\n const imported = (await loadTSProjectConfigFile(configFile)) as Config;\n\n log.process(\n \"info\",\n \"config\",\n \"✅ Successfully loaded configuration from:\",\n `${configFileNameBase}.ts`,\n );\n\n return imported;\n }\n\n log.process(\"error\", \"config\", \"Couldn't find project config file 'thirdeye.config.js'\");\n process.exit(1);\n } catch (error) {\n log.process(\"error\", \"config\", `Failed to load config file: ${configFile}`);\n log.process(\"error\", \"config\", error);\n process.exit(1);\n }\n }\n};\n\nexport const configure = async ({\n customProjectConfig,\n localDebugMode,\n}: {\n customProjectConfig?: CustomProjectConfig;\n localDebugMode?: boolean;\n}) => {\n if (customProjectConfig) {\n config = parseConfig(customProjectConfig as Config);\n\n return;\n }\n\n let loadedProjectConfig = await loadProjectConfig();\n\n if (localDebugMode) {\n let localDebugConfig = loadedProjectConfig;\n\n if (isPlatformModeConfig(loadedProjectConfig)) {\n localDebugConfig = {\n ...loadedProjectConfig,\n generateOnly: true,\n // @ts-expect-error Force it into generateOnly mode by dropping the platform specific properties\n thirdEyeProjectId: undefined,\n // @ts-expect-error Force it into generateOnly mode by dropping the platform specific properties\n apiKey: undefined,\n };\n }\n\n const urlChunks = [\"http://\", \"https://\", \"127.0.0.1\"];\n\n if (\n localDebugConfig.pageShots?.baseUrl &&\n urlChunks.some((urlChunk) => localDebugConfig?.pageShots?.baseUrl.includes(urlChunk))\n ) {\n const url = new URL(localDebugConfig.pageShots.baseUrl);\n\n url.hostname = \"localhost\";\n localDebugConfig.pageShots.baseUrl = url.toString();\n }\n\n loadedProjectConfig = localDebugConfig;\n }\n\n // Default to Storybook mode if no mode is defined\n if (\n !loadedProjectConfig.storybookShots &&\n !loadedProjectConfig.pageShots &&\n !loadedProjectConfig.ladleShots &&\n !loadedProjectConfig.histoireShots &&\n !loadedProjectConfig.customShots\n ) {\n loadedProjectConfig.storybookShots = {\n storybookUrl: \"storybook-static\",\n };\n }\n\n config = parseConfig(loadedProjectConfig);\n};\n","export const notSupported = \"not supported\";\n\nexport const MEDIA_UPLOAD_CONCURRENCY = 10;\n\nexport const POST_HOG_API_KEY = \"phc_RDNnzvANh1mNm9JKogF9UunG3Ky02YCxWP9gXScKShk\";\n","import {\n existsSync,\n mkdirSync,\n readdirSync,\n unlinkSync,\n writeFileSync,\n readFileSync,\n} from \"node:fs\";\nimport * as crypto from \"node:crypto\";\nimport { normalize, join } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { PostHog } from \"posthog-node\";\nimport { type BrowserType, chromium, firefox, webkit } from \"playwright-core\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { config, isPlatformModeConfig } from \"./config.js\";\nimport { log } from \"./log.js\";\nimport type { BrowserName, ShotItem } from \"./types.js\";\nimport { POST_HOG_API_KEY, notSupported } from \"./constants.js\";\n\ntype ParsedYargs = {\n _: [\"update\", \"meta\", \"docker\", \"local\", \"page-sitemap-gen\"];\n m: \"update\";\n};\n\ntype CliMode = \"update\" | \"page-sitemap-gen\";\n\ntype FilenameWithPath = {\n name: string;\n path: string;\n};\n\ntype FilenameWithAllPaths = {\n name: string;\n path: string;\n pathCurrent?: string;\n};\n\ntype Files = {\n baseline: FilenameWithPath[];\n current: FilenameWithPath[];\n difference: FilenameWithPath[];\n};\n\nexport type Changes = {\n difference: FilenameWithAllPaths[];\n deletion: FilenameWithAllPaths[];\n addition: FilenameWithAllPaths[];\n};\n\nexport const isUpdateMode = (): boolean => {\n const args = yargs(hideBin(process.argv)).parseSync() as unknown as ParsedYargs;\n\n return (\n args._.includes(\"update\") ||\n args.m === \"update\" ||\n (process.env.THIRD_EYE_MODE as CliMode) === \"update\"\n );\n};\n\nexport const isSitemapPageGenMode = (): boolean => {\n const args = yargs(hideBin(process.argv)).parseSync() as unknown as ParsedYargs;\n\n return (\n args._.includes(\"page-sitemap-gen\") ||\n (process.env.THIRD_EYE_MODE as CliMode) === \"page-sitemap-gen\"\n );\n};\n\nexport const isDockerMode = (): boolean => {\n const args = yargs(hideBin(process.argv)).parseSync() as unknown as ParsedYargs;\n\n return args._.includes(\"docker\") || process.env.THIRD_EYE_DOCKER === \"true\";\n};\n\nexport const isLocalDebugMode = (): boolean => {\n const args = yargs(hideBin(process.argv)).parseSync() as unknown as ParsedYargs;\n\n return args._.includes(\"local\") || process.env.THIRD_EYE_LOCAL === \"true\";\n};\n\nexport const shallGenerateMeta = (): boolean => {\n const args = yargs(hideBin(process.argv)).parseSync() as unknown as ParsedYargs;\n\n return args._.includes(\"meta\") || process.env.THIRD_EYE_GENERATE_META === \"true\";\n};\n\nexport const getChanges = (files: Files): Changes => {\n return {\n difference: files.difference\n .map((file) => ({\n ...file,\n pathCurrent: files.current.find(({ name }) => name === file.name)?.path, // Keep track of custom shots path\n }))\n .sort((a, b) => a.name.localeCompare(b.name)),\n deletion: files.baseline\n .filter((file1) => !files.current.some((file2) => file1.name === file2.name))\n .sort((a, b) => a.name.localeCompare(b.name)),\n addition: files.current\n .filter((file1) => !files.baseline.some((file2) => file1.name === file2.name))\n .sort((a, b) => a.name.localeCompare(b.name)),\n };\n};\n\ntype ExtendFileName = {\n fileName: string;\n extension: \"after\" | \"before\" | \"difference\";\n};\n\nexport const extendFileName = ({ fileName, extension }: ExtendFileName) => {\n const parts = fileName.split(\".\").filter((part) => part !== \"\");\n const extensionIndex = parts.length - 1;\n\n if (parts.length === 1) {\n return `${extension}.${parts[0]}`;\n }\n\n if (parts.length === 0) {\n return extension;\n }\n\n parts[extensionIndex] = `${extension}.${parts[extensionIndex]}`;\n\n return parts.join(\".\");\n};\n\nexport const createShotsFolders = () => {\n const paths = isPlatformModeConfig(config)\n ? [config.imagePathCurrent]\n : [config.imagePathBaseline, config.imagePathCurrent, config.imagePathDifference];\n\n for (const path of paths) {\n if (!existsSync(path)) {\n mkdirSync(path, { recursive: true });\n }\n }\n\n const ignoreFile = normalize(join(config.imagePathCurrent, \"..\", \".gitignore\"));\n\n if (!existsSync(ignoreFile)) {\n writeFileSync(ignoreFile, \"current\\ndifference\\n\");\n }\n};\n\nexport const sleep = async (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\nexport const removeFilesInFolder = (path: string, excludePaths?: string[]) => {\n const files = readdirSync(path);\n\n const filesPathsIgnoringExclude = files\n .map((file) => join(path, file))\n .filter((filePath) => !excludePaths?.includes(filePath));\n\n log.process(\"info\", \"general\", `Removing ${filesPathsIgnoringExclude.length} files from ${path}`);\n\n for (const filePath of filesPathsIgnoringExclude) {\n unlinkSync(filePath);\n }\n};\n\nconst convertBrowser = (browserKey?: string) => {\n switch (browserKey) {\n case \"chromium\": {\n return chromium;\n }\n\n case \"firefox\": {\n return firefox;\n }\n\n case \"webkit\": {\n return webkit;\n }\n\n default: {\n return chromium;\n }\n }\n};\n\nexport const getBrowser = (): BrowserType => {\n if (Array.isArray(config.browser)) return convertBrowser(config.browser[0]);\n\n return convertBrowser(config.browser);\n};\n\nexport const getBrowsers = (): BrowserType[] => {\n if (!Array.isArray(config.browser) || config.browser.length === 0) return [getBrowser()];\n\n const browsers = config.browser.map((key) => convertBrowser(key));\n\n return [...new Set(browsers)];\n};\n\nexport const getVersion = (): string | void => {\n try {\n const packageJsonPath = new URL(\"../package.json\", import.meta.url);\n const packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as {\n version: string;\n };\n\n return packageJson.version;\n } catch {}\n};\n\nconst fileNameWithoutExtension = (fileName: string): string => {\n return fileName.split(\".\").slice(0, -1).join(\".\");\n};\n\nexport const readDirIntoShotItems = (path: string): ShotItem[] => {\n const files = readdirSync(path);\n\n return files\n .filter((name) => name.endsWith(\".png\"))\n .map((fileNameWithExt): ShotItem => {\n const fileName = fileNameWithoutExtension(fileNameWithExt);\n\n return {\n id: fileName,\n shotName: fileName,\n shotMode: \"custom\",\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: join(path, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : join(config.imagePathDifference, fileNameWithExt),\n url: fileName,\n // TODO: custom shots take thresholds only from config - not possible to source configs from individual story\n threshold: config.threshold,\n };\n });\n};\n\nconst sendTelemetryData = async (properties: {\n runDuration?: number;\n shotsNumber?: number;\n error?: unknown;\n}) => {\n const client = new PostHog(POST_HOG_API_KEY);\n const id: string = randomUUID();\n\n try {\n log.process(\"info\", \"general\", \"Sending anonymized telemetry data.\");\n\n const version = getVersion() as string;\n const modes = [];\n\n if (config.storybookShots) modes.push(\"storybook\");\n\n if (config.ladleShots) modes.push(\"ladle\");\n\n if (config.pageShots) modes.push(\"pages\");\n\n if (config.customShots) modes.push(\"custom\");\n\n if (properties.error) {\n client.capture({\n distinctId: id,\n event: \"third-eye-error\",\n properties: { ...properties },\n });\n } else {\n client.capture({\n distinctId: id,\n event: \"third-eye-run\",\n properties: { ...properties, version, modes },\n });\n }\n\n await client.shutdown();\n } catch (error: unknown) {\n log.process(\"error\", \"general\", \"Error when sending telemetry data\", error);\n }\n};\n\nexport const parseHrtimeToSeconds = (hrtime: [number, number]) => {\n const seconds = (hrtime[0] + hrtime[1] / 1e9).toFixed(3);\n\n return seconds;\n};\n\nexport const exitProcess = async (properties: {\n runDuration?: number;\n shotsNumber?: number;\n error?: unknown;\n exitCode?: 0 | 1;\n}) => {\n if (process.env.THIRD_EYE_DISABLE_TELEMETRY === \"1\") {\n process.exit(properties.exitCode ?? 1);\n } else {\n await sendTelemetryData(properties).finally(() => {\n process.exit(properties.exitCode ?? 1);\n });\n }\n};\n\nconst hashBuffer = (buffer: Uint8Array): string => {\n const hashSum = crypto.createHash(\"sha256\");\n\n hashSum.update(buffer);\n\n return hashSum.digest(\"hex\");\n};\n\nexport const hashFile = (filePath: string): string => {\n const file = readFileSync(filePath);\n\n return hashBuffer(file);\n};\n\nexport const featureNotSupported = (feature: string) => {\n log.process(\"error\", \"general\", `${feature} is not supported in this configuration mode`);\n\n process.exit(1);\n};\n\n// Chromium flags that make font/text rendering deterministic across machines.\n// Without these, subpixel hinting, LCD text, and platform-specific Skia opts\n// cause tiny pixel diffs in text between different environments (e.g. local vs CI).\nconst DETERMINISTIC_RENDERING_ARGS = [\n \"--font-render-hinting=none\",\n \"--disable-skia-runtime-opts\",\n \"--disable-font-subpixel-positioning\",\n \"--disable-lcd-text\",\n];\n\nexport const launchBrowser = async (_browser?: BrowserType) => {\n const browserType = _browser ?? getBrowser();\n const browserName = browserType.name() as BrowserName;\n const userOptions = config.browserLaunchOptions?.[browserName] ?? {};\n\n // Only inject deterministic rendering args for Chromium-based browsers\n if (browserName === \"chromium\") {\n const userArgs = userOptions.args ?? [];\n\n return browserType.launch({\n ...userOptions,\n args: [...DETERMINISTIC_RENDERING_ARGS, ...userArgs],\n });\n }\n\n return browserType.launch(userOptions);\n};\n","import { PNG } from \"pngjs\";\n\nexport const resizeImage = (originalImage: PNG, width: number, height: number) => {\n const newImage = new PNG({\n width,\n height,\n fill: true,\n inputHasAlpha: true,\n });\n\n for (let x = 0; x < width; x++) {\n for (let y = 0; y < height; y++) {\n const index = ((width * y + x) << 2) + 3;\n\n newImage.data[index] = 64;\n }\n }\n\n PNG.bitblt(originalImage, newImage, 0, 0, originalImage.width, originalImage.height, 0, 0);\n\n return newImage;\n};\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport pixelmatch from \"pixelmatch\";\nimport { compare as odiffCompare } from \"odiff-bin\";\nimport { PNG } from \"pngjs\";\nimport { config, isPlatformModeConfig } from \"../config.js\";\nimport { featureNotSupported } from \"../utils.js\";\nimport { resizeImage } from \"./utils.js\";\n\nexport const checkThreshold = (threshold: number, pixelsTotal: number, pixelDifference: number) => {\n // Treat theshold as percentage\n if (threshold < 1) {\n return pixelDifference <= pixelsTotal * threshold;\n }\n\n // Treat threshold as absolute value\n return pixelDifference <= threshold;\n};\n\nexport const compareImagesViaPixelmatch = async (\n threshold: number,\n baselineShotPath: string,\n currentShotPath: string,\n differenceShotPath?: string,\n): Promise<{\n pixelDifference: number;\n pixelDifferencePercentage: number;\n isWithinThreshold: boolean;\n}> => {\n const baselineImageBuffer = readFileSync(baselineShotPath);\n const currentImageBuffer = readFileSync(currentShotPath);\n\n if (baselineImageBuffer.equals(currentImageBuffer)) {\n return {\n pixelDifference: 0,\n pixelDifferencePercentage: 0,\n isWithinThreshold: true,\n };\n }\n\n let baselineImage: PNG = PNG.sync.read(baselineImageBuffer);\n let currentImage: PNG = PNG.sync.read(currentImageBuffer);\n\n const maxWidth = Math.max(baselineImage.width || 100, currentImage.width || 100);\n const maxHeight = Math.max(baselineImage.height || 100, currentImage.height || 100);\n\n if (baselineImage.width !== currentImage.width || baselineImage.height !== currentImage.height) {\n baselineImage = resizeImage(baselineImage, maxWidth, maxHeight);\n currentImage = resizeImage(currentImage, maxWidth, maxHeight);\n }\n\n const differenceImage = new PNG({ width: maxWidth, height: maxHeight });\n\n const pixelDifference = pixelmatch(\n baselineImage.data,\n currentImage.data,\n differenceImage.data,\n maxWidth,\n maxHeight,\n { threshold: 0 },\n );\n\n const pixelsTotal = baselineImage.width * baselineImage.height;\n\n if (pixelDifference > 0 && differenceShotPath) {\n const isWithinThreshold = checkThreshold(threshold, pixelsTotal, pixelDifference);\n\n if (!isWithinThreshold) {\n writeFileSync(differenceShotPath, PNG.sync.write(differenceImage));\n }\n\n return {\n pixelDifference,\n pixelDifferencePercentage: pixelDifference / pixelsTotal,\n isWithinThreshold,\n };\n }\n\n return {\n pixelDifference,\n pixelDifferencePercentage: pixelDifference / pixelsTotal,\n isWithinThreshold: true,\n };\n};\n\nexport const compareImagesViaOdiff = async (\n threshold: number,\n baselineShotPath: string,\n currentShotPath: string,\n differenceShotPath: string,\n): Promise<{\n pixelDifference: number;\n pixelDifferencePercentage: number;\n isWithinThreshold: boolean;\n}> => {\n const result = await odiffCompare(baselineShotPath, currentShotPath, differenceShotPath, {\n failOnLayoutDiff: false,\n });\n\n if (result.match) {\n return {\n pixelDifference: 0,\n pixelDifferencePercentage: 0,\n isWithinThreshold: true,\n };\n }\n\n if (result.reason === \"pixel-diff\") {\n let isWithinThreshold = true;\n\n // Treat theshold as percentage\n const pixelDifferencePercentage = Number(result.diffPercentage / 100);\n\n if (threshold < 1) {\n isWithinThreshold = pixelDifferencePercentage <= threshold;\n } else {\n // Treat threshold as absolute value\n isWithinThreshold = result.diffCount <= threshold;\n }\n\n return {\n pixelDifference: Number(result.diffCount),\n pixelDifferencePercentage,\n isWithinThreshold,\n };\n }\n\n throw new Error(\"Couldn't compare images\");\n};\n\nexport const compareImages = async (\n threshold: number,\n baselineShotPath: string,\n currentShotPath: string,\n differenceShotPath: string,\n): Promise<{\n pixelDifference: number;\n pixelDifferencePercentage: number;\n isWithinThreshold: boolean;\n}> => {\n if (isPlatformModeConfig(config)) {\n return featureNotSupported(\"compareImages()\");\n }\n\n if (config.compareEngine === \"pixelmatch\") {\n return compareImagesViaPixelmatch(\n threshold,\n baselineShotPath,\n currentShotPath,\n differenceShotPath,\n );\n }\n\n return compareImagesViaOdiff(threshold, baselineShotPath, currentShotPath, differenceShotPath);\n};\n","import { existsSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { mapLimit } from \"async\";\nimport { compareImages } from \"./compare/compare.js\";\nimport { log } from \"./log.js\";\nimport { config, isPlatformModeConfig } from \"./config.js\";\nimport type { ShotItem } from \"./types.js\";\nimport { featureNotSupported, shallGenerateMeta } from \"./utils.js\";\n\nexport const checkDifferences = async (shotItems: ShotItem[]) => {\n if (isPlatformModeConfig(config)) {\n return featureNotSupported(\"checkDifferences()\");\n }\n\n log.process(\n \"info\",\n \"general\",\n `Comparing ${shotItems.length} screenshots using '${config.compareEngine}' as compare engine`,\n );\n\n const total = shotItems.length;\n const noBaselinesItems: ShotItem[] = [];\n const aboveThresholdDifferenceItems: ShotItem[] = [];\n\n const comparisonResults: Record<\n string,\n {\n pixelDifference: number;\n pixelDifferencePercentage: number;\n isWithinThreshold: boolean;\n }\n > = {};\n\n await mapLimit<[number, ShotItem], void>(\n shotItems.entries(),\n config.compareConcurrency,\n async (item: [number, ShotItem]) => {\n const [index, shotItem] = item;\n const logger = (message: string) => {\n log\n .item({\n shotMode: shotItem.shotMode,\n uniqueItemId: shotItem.shotName,\n itemIndex: index,\n totalItems: total,\n })\n .process(\"info\", \"general\", message);\n };\n\n logger(`Comparing '${shotItem.id}'`);\n\n const baselineImageExists = existsSync(shotItem.filePathBaseline);\n\n if (!baselineImageExists) {\n logger(\"Baseline image missing. Will be treated as addition.\");\n noBaselinesItems.push(shotItem);\n\n return;\n }\n\n const currentImageExists = existsSync(shotItem.filePathCurrent);\n\n if (!currentImageExists) {\n throw new Error(`Error: Missing current image: ${shotItem.filePathCurrent}`);\n }\n\n const { pixelDifference, pixelDifferencePercentage, isWithinThreshold } = await compareImages(\n shotItem.threshold,\n shotItem.filePathBaseline,\n shotItem.filePathCurrent,\n shotItem.filePathDifference,\n );\n\n if (shallGenerateMeta()) {\n comparisonResults[shotItem.id] = {\n pixelDifference,\n pixelDifferencePercentage,\n isWithinThreshold,\n };\n }\n\n if (pixelDifference > 0) {\n const percentage = (pixelDifferencePercentage * 100).toFixed(2);\n\n if (isWithinThreshold) {\n logger(\n `Difference of ${pixelDifference} pixels (${percentage}%) found but within threshold.`,\n );\n } else {\n aboveThresholdDifferenceItems.push(shotItem);\n logger(\n `Difference of ${pixelDifference} pixels (${percentage}%) found. Difference image saved to: ${shotItem.filePathDifference}`,\n );\n }\n } else {\n logger(\"No difference found.\");\n }\n },\n );\n\n if (shallGenerateMeta()) {\n log.process(\n \"info\",\n \"general\",\n `Writing meta file with ${Object.entries(comparisonResults).length} items.`,\n );\n writeFileSync(\n `${path.join(config.imagePathCurrent, \"meta\")}.json`,\n JSON.stringify(comparisonResults, null, 2),\n );\n }\n\n log.process(\"info\", \"general\", \"Comparison done!\");\n\n return { aboveThresholdDifferenceItems, noBaselinesItems };\n};\n","import type { BrowserType, Page, Request } from \"playwright-core\";\nimport { config } from \"../config.js\";\nimport type { log } from \"../log.js\";\n\nconst checkIgnoreUrls = (url: string, ignoreUrls: string[]) => {\n for (const ignoreUrl of ignoreUrls) {\n if (url.includes(ignoreUrl)) {\n return true;\n }\n }\n\n return false;\n};\n\nexport const waitForNetworkRequests = async ({\n page,\n logger,\n timeout = config.timeouts.networkRequests,\n waitForFirstRequest = config.waitForFirstRequest,\n waitForLastRequest = config.waitForLastRequest,\n ignoreUrls = [],\n}: {\n page: Page;\n logger: ReturnType<typeof log.item>;\n timeout?: number;\n waitForFirstRequest?: number;\n waitForLastRequest?: number;\n ignoreUrls?: string[];\n}) =>\n new Promise((resolve, reject) => {\n let requestCounter = 0;\n const requests = new Set<Request>();\n let lastRequestTimeoutId: NodeJS.Timeout;\n\n const timeoutId = setTimeout(() => {\n const pendingUrls = [...requests].map((request) => request.url());\n\n logger.process(\"info\", \"network\", \"Pending requests:\", pendingUrls);\n\n cleanup();\n reject(new Error(\"Timeout\"));\n }, timeout);\n\n const firstRequestTimeoutId = setTimeout(() => {\n cleanup();\n resolve(true);\n }, waitForFirstRequest);\n\n const onRequest = (request: Request) => {\n if (!checkIgnoreUrls(request.url(), ignoreUrls)) {\n clearTimeout(firstRequestTimeoutId);\n clearTimeout(lastRequestTimeoutId);\n requestCounter++;\n requests.add(request);\n logger.browser(\"info\", \"network\", `+ ${request.url()}`);\n }\n };\n\n const onRequestFinished = async (request: Request) => {\n clearTimeout(lastRequestTimeoutId);\n\n if (!checkIgnoreUrls(request.url(), ignoreUrls)) {\n const failure = request.failure();\n const response = await request.response();\n\n requestCounter--;\n requests.delete(request);\n\n const statusText = failure\n ? failure.errorText\n : `${response?.status() ?? \"unknown\"} ${response?.statusText() ?? \"unknown\"}`;\n\n logger.browser(\"info\", \"network\", `- ${request.url()} [${statusText}]`);\n }\n\n lastRequestTimeoutId = setTimeout(() => {\n // `requestCounter` can be below 0 if requests have completed before they were being tracked\n if (requestCounter <= 0) {\n cleanup();\n resolve(true);\n }\n }, waitForLastRequest);\n };\n\n function cleanup() {\n clearTimeout(timeoutId);\n clearTimeout(firstRequestTimeoutId);\n clearTimeout(lastRequestTimeoutId);\n page.removeListener(\"request\", onRequest);\n page.removeListener(\"requestfinished\", onRequestFinished);\n page.removeListener(\"requestfailed\", onRequestFinished);\n }\n\n page.on(\"request\", onRequest);\n page.on(\"requestfinished\", onRequestFinished);\n page.on(\"requestfailed\", onRequestFinished);\n });\n\nexport const resizeViewportToFullscreen = async ({ page }: { page: Page }) => {\n const viewport = await page.evaluate(\n async () =>\n new Promise<{ height: number; width: number }>((resolve) => {\n const { body } = document;\n const html = document.documentElement;\n\n const height = Math.max(\n body.scrollHeight,\n body.offsetHeight,\n html.clientHeight,\n html.scrollHeight,\n html.offsetHeight,\n );\n\n const width = Math.max(\n body.scrollWidth,\n body.offsetWidth,\n html.clientWidth,\n html.scrollWidth,\n html.offsetWidth,\n );\n\n resolve({ height, width });\n }),\n );\n\n await page.setViewportSize({\n width: Math.max(page.viewportSize()?.width ?? 800, viewport.width),\n height: viewport.height,\n });\n};\n\nexport const selectBreakpoints = (\n topLevelBreakpoints?: number[],\n modeBreakpoints?: number[],\n shotBreakpoints?: number[],\n) => {\n if (shotBreakpoints && shotBreakpoints.length > 0) {\n return shotBreakpoints;\n }\n\n if (modeBreakpoints && modeBreakpoints.length > 0) {\n return modeBreakpoints;\n }\n\n return topLevelBreakpoints ?? [];\n};\n\nexport const generateLabel = ({\n breakpoint,\n browser,\n}: {\n breakpoint?: number;\n browser?: BrowserType;\n}): string => {\n const widthLabel = breakpoint && breakpoint > 0 ? `w${breakpoint}px` : \"\";\n const browserLabel = browser?.name() ?? \"\";\n\n const labels = [widthLabel, browserLabel].filter(Boolean);\n\n if (labels.length === 0) return \"\";\n\n return `__[${labels.join(\"|\")}]`;\n};\n","import path from \"node:path\";\nimport axios from \"axios\";\nimport type { BrowserType } from \"playwright-core\";\nimport { config, isPlatformModeConfig } from \"../config.js\";\nimport type { Mask, ShotItem } from \"../types.js\";\nimport { selectBreakpoints, generateLabel } from \"../shots/utils.js\";\nimport { notSupported } from \"../constants.js\";\nimport type { Story } from \"./storybook.js\";\n\ntype LadleStoryConfig = {\n name: string;\n filePath: string;\n meta: Record<string, unknown>;\n};\n\nexport const generateLadleShotItems = (\n baseUrl: string,\n isLocalServer: boolean,\n ladleStories: Story[],\n mask?: Mask[],\n modeBreakpoints?: number[],\n browser?: BrowserType,\n): ShotItem[] => {\n const ladleUrl = isLocalServer ? `${baseUrl}/index.html` : baseUrl;\n\n return ladleStories\n .filter((story) => story.parameters?.thirdeye?.disable !== true)\n .filter((story) =>\n config.filterShot ? config.filterShot({ ...story, shotMode: \"ladle\" }) : true,\n )\n .flatMap((ladleStory): ShotItem[] => {\n const shotName =\n config.shotNameGenerator?.({ ...ladleStory, shotMode: \"ladle\" }) ?? ladleStory.id;\n let label = generateLabel({ browser });\n let fileNameWithExt = `${shotName}${label}.png`;\n\n const shotItem: ShotItem = {\n shotMode: \"ladle\",\n id: `${ladleStory.story}${label}`,\n shotName: `${shotName}${label}`,\n importPath: ladleStory.importPath,\n url: `${ladleUrl}?story=${ladleStory.story}&mode=preview`,\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n threshold: ladleStory.parameters?.thirdeye?.threshold ?? config.threshold,\n waitBeforeScreenshot:\n ladleStory.parameters?.thirdeye?.waitBeforeScreenshot ?? config.waitBeforeScreenshot,\n mask: [...(mask ?? []), ...(ladleStory.parameters?.thirdeye?.mask ?? [])],\n elementLocator:\n ladleStory.parameters?.thirdeye?.elementLocator ??\n config?.storybookShots?.elementLocator ??\n \"\",\n waitForSelector: config?.ladleShots?.waitForSelector ?? \"[data-storyloaded]\",\n componentPath: ladleStory.kind,\n storyName: ladleStory.story,\n };\n\n const breakpoints = selectBreakpoints(\n config.breakpoints,\n modeBreakpoints,\n ladleStory.parameters?.thirdeye?.breakpoints,\n );\n\n if (breakpoints.length === 0) {\n return [shotItem];\n }\n\n return breakpoints.map((breakpoint) => {\n label = generateLabel({ breakpoint, browser });\n fileNameWithExt = `${shotName}${label}.png`;\n\n return {\n ...shotItem,\n id: `${ladleStory.story}${label}`,\n shotName: `${ladleStory.story}${label}`,\n breakpoint,\n breakpointGroup: ladleStory.story,\n url: `${ladleUrl}?story=${ladleStory.story}&mode=preview&width=${breakpoint}`,\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n viewport: { width: breakpoint },\n };\n });\n });\n};\n\nexport const collectLadleStories = async (ladleUrl: string) => {\n const {\n data,\n }: {\n data: {\n stories: LadleStoryConfig[];\n };\n } = await axios.get(`${ladleUrl}/meta.json`);\n\n const collection: Story[] = [];\n\n for (const [key, storyConfig] of Object.entries(data.stories)) {\n collection.push({\n id: key,\n story: key,\n kind: key,\n importPath: storyConfig.filePath,\n parameters: storyConfig.meta,\n });\n }\n\n return collection;\n};\n","import { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { BrowserContext, BrowserType } from \"playwright-core\";\nimport type { Mask, ShotItem } from \"../types.js\";\nimport { config, isPlatformModeConfig } from \"../config.js\";\nimport { launchBrowser } from \"../utils.js\";\nimport { log } from \"../log.js\";\nimport { generateLabel, selectBreakpoints } from \"../shots/utils.js\";\nimport { notSupported } from \"../constants.js\";\n\nconst kebabCase = (str?: string): string =>\n (str ?? \"\")\n .replace(/([a-z\\d])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_/]+/g, \"-\")\n .toLowerCase();\n\ntype ExtraShots = {\n name?: string;\n args?: Record<string, unknown>; // Additional args for the snapshot\n prefix?: string; // Prefix for the snapshot name\n suffix?: string; // Suffix for the snapshot name\n};\nexport type StoryParameters = {\n thirdeye?: {\n disable?: boolean;\n threshold?: number;\n waitBeforeScreenshot?: number;\n mask?: Mask[];\n breakpoints?: number[];\n args?: Record<string, unknown>; // Args for the story\n extraShots?: ExtraShots[]; // Additional snapshots for the story\n elementLocator?: string;\n };\n viewport?: {\n width?: number;\n height?: number;\n };\n fileName?: string;\n};\n\nexport type Story = {\n id: string;\n kind: string;\n story: string;\n name?: string;\n title?: string;\n importPath?: string;\n parameters?: StoryParameters & {\n storyshots?: {\n disable?: boolean;\n };\n };\n};\n\ntype StorybookPreviewApi = {\n ready: () => Promise<void>;\n extract?: () => Promise<Record<string, Story>>;\n};\n\ntype StorybookClientApi = {\n raw?: () => Story[];\n storyStore?: {\n cacheAllCSFFiles: () => Promise<void>;\n };\n};\n\ntype WindowObject = typeof window & {\n __STORYBOOK_PREVIEW__: StorybookPreviewApi;\n __STORYBOOK_CLIENT_API__: StorybookClientApi;\n};\n\ntype CrawlerResult = {\n stories: Story[] | undefined;\n};\n\nexport const getStoryBookUrl = (url: string) => {\n if (url.startsWith(\"http://\") || url.startsWith(\"https://\") || url.startsWith(\"file://\")) {\n return url;\n }\n\n if (url.startsWith(\"/\")) {\n return `file://${url}`;\n }\n\n return `file://${path.normalize(path.join(process.cwd(), url))}`;\n};\n\nexport const getIframeUrl = (url: string) =>\n url.endsWith(\"/\") ? `${url}iframe.html` : `${url}/iframe.html`;\n\nexport const collectStoriesViaWindowApi = async (\n context: BrowserContext,\n url: string,\n isIframeUrl?: boolean,\n) => {\n const page = await context.newPage();\n const iframeUrl = isIframeUrl ? getStoryBookUrl(url) : getIframeUrl(getStoryBookUrl(url));\n\n await page.goto(iframeUrl);\n\n await page.waitForFunction(() => (window as WindowObject).__STORYBOOK_PREVIEW__, null, {\n timeout: config.timeouts.fetchStories,\n });\n\n // Storybook >= 8 expose a new preview API that has a `ready` method to be awaited before proceeding\n const isV8OrAbove = await page.evaluate(async () => {\n const { __STORYBOOK_PREVIEW__: api } = window as WindowObject;\n\n return api.ready !== undefined;\n });\n\n if (isV8OrAbove) {\n // SB v8 and above\n await page.evaluate(async () => {\n const { __STORYBOOK_PREVIEW__: api } = window as WindowObject;\n\n if (api.ready) {\n await api.ready();\n }\n });\n } else {\n // SB v7 and below\n await page.waitForFunction(() => (window as WindowObject).__STORYBOOK_CLIENT_API__, null, {\n timeout: config.timeouts.fetchStories,\n });\n\n await page.evaluate(async () => {\n const { __STORYBOOK_CLIENT_API__: api } = window as WindowObject;\n\n if (api.storyStore) {\n await api.storyStore.cacheAllCSFFiles?.();\n }\n });\n }\n\n const result = await page.evaluate(async (): Promise<CrawlerResult> => {\n const parseParameters = <T>(\n parameters: T,\n level = 0,\n ): T | \"UNSUPPORTED_DEPTH\" | \"UNSUPPORTED_TYPE\" => {\n if (level > 10) {\n return \"UNSUPPORTED_DEPTH\";\n }\n\n if (Array.isArray(parameters)) {\n // @ts-expect-error FIXME\n return parameters.map((value) => parseParameters<unknown>(value, level + 1));\n }\n\n if (\n typeof parameters === \"string\" ||\n typeof parameters === \"number\" ||\n typeof parameters === \"boolean\" ||\n parameters === undefined ||\n typeof parameters === \"function\" ||\n parameters instanceof RegExp ||\n parameters instanceof Date ||\n parameters === null\n ) {\n return parameters;\n }\n\n if (typeof parameters === \"object\" && parameters !== null) {\n // @ts-expect-error FIXME\n return Object.keys(parameters).reduce<T>((acc, key: keyof T) => {\n // @ts-expect-error FIXME\n acc[key] = parseParameters(parameters[key], level + 1);\n\n return acc;\n }, {});\n }\n\n return \"UNSUPPORTED_TYPE\";\n };\n\n const mapStories = (stories: Story[]): Story[] =>\n stories.map((story) => {\n const parameters = parseParameters(\n story.parameters as Record<string, unknown>,\n ) as Story[\"parameters\"];\n\n return {\n id: story.id,\n kind: story.kind,\n story: story.story,\n importPath: parameters?.fileName,\n parameters,\n };\n });\n\n const { __STORYBOOK_PREVIEW__: previewApi, __STORYBOOK_CLIENT_API__: clientApi } =\n window as WindowObject;\n\n let stories: Story[] = [];\n\n if (previewApi.extract) {\n const items = await previewApi.extract();\n\n stories = mapStories(Object.values(items));\n } else if (clientApi.raw) {\n // Fallback for 6.4 and below\n stories = mapStories(clientApi.raw());\n }\n\n return { stories };\n });\n\n return result;\n};\n\nexport const collectStoriesViaStoriesJson = async (context: BrowserContext, url: string) => {\n const indexJsonUrl = url.endsWith(\"/\") ? `${url}index.json` : `${url}/index.json`;\n const storiesJsonUrl = url.endsWith(\"/\") ? `${url}stories.json` : `${url}/stories.json`;\n\n const tryLoadJson = async (jsonUrl: string): Promise<Record<string, unknown> | null> => {\n if (jsonUrl.startsWith(\"file://\")) {\n try {\n const file = readFileSync(jsonUrl.slice(7));\n return JSON.parse(file.toString()) as Record<string, unknown>;\n } catch {\n return null;\n }\n }\n\n try {\n const result = await context.request.get(jsonUrl);\n if (result.status() !== 200) return null;\n return (await result.json()) as Record<string, unknown>;\n } catch {\n return null;\n }\n };\n\n // Try index.json first (Storybook v7+), then fall back to stories.json\n for (const jsonUrl of [indexJsonUrl, storiesJsonUrl]) {\n const json = await tryLoadJson(jsonUrl);\n if (!json) continue;\n\n // Storybook v7+ uses \"entries\", older versions use \"stories\"\n const entries = json.entries ?? json.stories;\n if (typeof entries === \"object\" && entries !== null) {\n const raw = Object.values(entries) as (Story & { type?: string })[];\n const stories = raw\n // index.json includes \"docs\" entries; filter to stories only\n .filter((entry) => entry.type !== \"docs\")\n .map((entry) => ({\n ...entry,\n // index.json uses \"title\" and \"name\"; normalize to \"kind\" and \"story\"\n kind: entry.kind ?? entry.title ?? \"\",\n story: entry.story ?? entry.name ?? \"\",\n }));\n return { stories };\n }\n }\n\n throw new Error(`Cannot load stories from ${indexJsonUrl} or ${storiesJsonUrl}`);\n};\n\nexport const collectStories = async (url: string) => {\n const browser = await launchBrowser();\n const context = await browser.newContext();\n\n try {\n log.process(\"info\", \"general\", \"Trying to collect stories via window object\");\n const result = await collectStoriesViaWindowApi(context, url);\n\n await browser.close();\n\n return result;\n } catch (error: unknown) {\n log.process(\"info\", \"general\", \"Fallback to /stories.json\");\n log.process(\"error\", \"general\", error);\n }\n\n try {\n const result = await collectStoriesViaStoriesJson(context, url);\n\n await browser.close();\n\n return result;\n } catch (error: unknown) {\n await browser.close();\n throw error;\n }\n};\n\nconst generateBrowserConfig = (story: Story) => {\n const browserConfig = config.configureBrowser?.({\n ...story,\n shotMode: \"storybook\",\n });\n\n if (story.parameters?.viewport && browserConfig) {\n browserConfig.viewport ??= {\n width: 1280,\n height: 720,\n };\n browserConfig.viewport = {\n ...browserConfig.viewport,\n ...story.parameters.viewport,\n };\n }\n\n return browserConfig;\n};\n\nconst generateStoryUrl = (\n iframeUrl: string,\n storyId: string,\n args?: Record<string, unknown>,\n breakpoint?: number,\n): string => {\n let url = `${iframeUrl}?id=${storyId}&viewMode=story`;\n\n if (args) {\n const argsString = Object.entries(args)\n .map(([key, value]) => `${key}:${value as string}`)\n .join(\";\");\n\n url += `&args=${argsString}`;\n }\n\n if (breakpoint !== undefined) {\n url += `&width=${breakpoint}`;\n }\n\n return url;\n};\n\nconst generateFilename = (kind: string, story: string, prefix?: string, suffix?: string) => {\n return [prefix, kebabCase(kind), kebabCase(story), kebabCase(suffix)].filter(Boolean).join(\"--\");\n};\n\nexport const generateStorybookShotItems = (\n baseUrl: string,\n stories: Story[],\n mask?: Mask[],\n modeBreakpoints?: number[],\n browser?: BrowserType,\n): ShotItem[] => {\n const iframeUrl = getIframeUrl(getStoryBookUrl(baseUrl));\n\n return stories\n .filter((story) => story.parameters?.thirdeye?.disable !== true)\n .filter((story) => story.parameters?.storyshots?.disable !== true)\n .filter((story) =>\n config.filterShot ? config.filterShot({ ...story, shotMode: \"storybook\" }) : true,\n )\n .flatMap((story): ShotItem[] => {\n const shotName =\n config.shotNameGenerator?.({ ...story, shotMode: \"storybook\" }) ??\n generateFilename(story.kind, story.story);\n let label = generateLabel({ browser });\n let fileNameWithExt = `${shotName}${label}.png`;\n\n const baseShotItem: ShotItem = {\n shotMode: \"storybook\",\n id: `${story.id}${label}`,\n shotName: `${shotName}${label}`,\n importPath: story.importPath,\n url: generateStoryUrl(iframeUrl, story.id, story.parameters?.thirdeye?.args),\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n browserConfig: generateBrowserConfig(story),\n threshold: story.parameters?.thirdeye?.threshold ?? config.threshold,\n waitBeforeScreenshot:\n story.parameters?.thirdeye?.waitBeforeScreenshot ?? config.waitBeforeScreenshot,\n mask: [...(mask ?? []), ...(story.parameters?.thirdeye?.mask ?? [])],\n elementLocator:\n story.parameters?.thirdeye?.elementLocator ??\n config?.storybookShots?.elementLocator ??\n \"\",\n waitForSelector: config?.storybookShots?.waitForSelector,\n componentPath: story.kind,\n storyName: story.story,\n storyId: story.id,\n storyArgs: story.parameters?.thirdeye?.args,\n };\n\n const storyLevelBreakpoints = story.parameters?.thirdeye?.breakpoints ?? [];\n\n const breakpoints = selectBreakpoints(\n config.breakpoints,\n modeBreakpoints,\n storyLevelBreakpoints,\n );\n\n let shotItems = [];\n\n if (!breakpoints || breakpoints.length === 0) {\n shotItems = [baseShotItem];\n } else {\n shotItems = breakpoints.map((breakpoint) => {\n label = generateLabel({ breakpoint, browser });\n fileNameWithExt = `${shotName}${label}.png`;\n\n return {\n ...baseShotItem,\n id: `${story.id}${label}`,\n shotName: `${shotName}${label}`,\n breakpoint,\n breakpointGroup: story.id,\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n viewport: {\n width: breakpoint,\n height: undefined,\n },\n url: generateStoryUrl(\n iframeUrl,\n story.id,\n story.parameters?.thirdeye?.args,\n breakpoint,\n ),\n browserConfig: generateBrowserConfig({\n ...story,\n parameters: {\n ...story.parameters,\n viewport: {\n width: breakpoint,\n },\n },\n }),\n };\n });\n }\n\n const extraShots =\n story.parameters?.thirdeye?.extraShots?.flatMap((snapshot) => {\n const combinedArgs = {\n ...story.parameters?.thirdeye?.args,\n ...snapshot.args,\n };\n const snapshotShotName = generateFilename(\n story.kind,\n story.story,\n snapshot.prefix,\n snapshot.suffix,\n );\n\n return (breakpoints?.length === 0 ? [undefined] : breakpoints).map((breakpoint) => {\n label = generateLabel({ breakpoint, browser });\n fileNameWithExt = `${snapshotShotName}${label}.png`;\n\n return {\n ...baseShotItem,\n id: `${story.id}${label}-${snapshot.name ?? \"snapshot\"}`,\n shotName: `${snapshotShotName}${label}`,\n breakpoint,\n breakpointGroup: story.id,\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n url: generateStoryUrl(iframeUrl, story.id, combinedArgs, breakpoint),\n viewport: breakpoint\n ? {\n width: breakpoint,\n height: undefined,\n }\n : undefined,\n browserConfig: generateBrowserConfig({\n ...story,\n parameters: {\n ...story.parameters,\n viewport: {\n width: breakpoint,\n },\n },\n }),\n };\n });\n }) ?? [];\n\n return [...shotItems, ...extraShots];\n });\n};\n","import path from \"node:path\";\nimport axios, { isAxiosError } from \"axios\";\nimport { z } from \"zod\";\nimport type { BrowserType } from \"playwright-core\";\nimport fs from \"fs-extra\";\nimport { log } from \"../log.js\";\nimport { type PageScreenshotParameter, config, isPlatformModeConfig } from \"../config.js\";\nimport type { Mask, ShotItem } from \"../types.js\";\nimport { selectBreakpoints, generateLabel } from \"../shots/utils.js\";\nimport { notSupported } from \"../constants.js\";\n\nconst generateBrowserConfig = (page: PageScreenshotParameter) => {\n const browserConfig = config.configureBrowser?.({\n ...page,\n shotMode: \"page\",\n });\n\n if (page.viewport && browserConfig) {\n browserConfig.viewport ??= {\n width: 1280,\n height: 720,\n };\n browserConfig.viewport = {\n ...browserConfig.viewport,\n ...page.viewport,\n };\n }\n\n return browserConfig;\n};\n\nexport const generatePageShotItems = (\n pages: PageScreenshotParameter[],\n baseUrl: string,\n mask?: Mask[],\n modeBreakpoints?: number[],\n browser?: BrowserType,\n): ShotItem[] => {\n const names = pages.map((page) => page.name);\n const uniqueNames = new Set(names);\n\n if (names.length !== uniqueNames.size) {\n throw new Error(\"Error: Page names must be unique\");\n }\n\n return pages.flatMap((page): ShotItem[] => {\n const shotName = config.shotNameGenerator?.({ ...page, shotMode: \"page\" }) ?? page.name;\n let label = generateLabel({ browser });\n let fileNameWithExt = `${shotName}${label}.png`;\n\n const baseShotItem: ShotItem = {\n shotMode: \"page\",\n id: `${shotName}${label}`,\n shotName: `${shotName}${label}`,\n url: path.join(baseUrl, page.path),\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n browserConfig: generateBrowserConfig(page),\n threshold: page.threshold ?? config.threshold,\n waitBeforeScreenshot: page.waitBeforeScreenshot ?? config.waitBeforeScreenshot,\n mask: [...(mask ?? []), ...(page.mask ?? [])],\n waitForSelector: config?.pageShots?.waitForSelector,\n componentPath: undefined,\n storyName: undefined,\n };\n\n const breakpoints = selectBreakpoints(config.breakpoints, modeBreakpoints, page.breakpoints);\n\n if (breakpoints.length === 0) {\n return [baseShotItem];\n }\n\n return breakpoints.map((breakpoint) => {\n label = generateLabel({ breakpoint, browser });\n fileNameWithExt = `${shotName}${label}.png`;\n\n return {\n ...baseShotItem,\n id: `${shotName}${label}`,\n shotName: `${shotName}${label}`,\n breakpoint,\n breakpointGroup: page.name,\n url: path.join(baseUrl, page.path),\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n viewport: { width: breakpoint },\n browserConfig: generateBrowserConfig({\n ...page,\n viewport: { width: breakpoint },\n }),\n };\n });\n });\n};\n\n// Helper function to check if a string is a valid URL\nconst isValidHttpUrl = (string: string) => {\n let url;\n\n try {\n url = new URL(string);\n } catch {\n return false;\n }\n\n return url.protocol === \"http:\" || url.protocol === \"https:\";\n};\n\nexport const getPagesFromExternalLoader = async () => {\n try {\n if (!config.pageShots?.pagesJsonUrl) {\n return [];\n }\n\n log.process(\"info\", \"general\", `⏬ Loading pages from ${config.pageShots.pagesJsonUrl}`);\n\n let pages;\n\n // Check if the pagesJsonUrl is a valid URL or a local file path\n if (isValidHttpUrl(config.pageShots.pagesJsonUrl)) {\n log.process(\"info\", \"general\", `🕸️ Trying to fetch from URL`);\n const response = await axios.get<PageScreenshotParameter[]>(config.pageShots.pagesJsonUrl);\n\n pages = response.data;\n } else {\n // Read the file from the local filesystem\n log.process(\"info\", \"general\", `⏬ Trying to fetch from local file`);\n const fileContents = await fs.readFile(config.pageShots.pagesJsonUrl, \"utf8\");\n\n pages = JSON.parse(fileContents) as PageScreenshotParameter[];\n }\n\n // Validation logic remains the same\n const pagesArraySchema = z.array(\n z.object({\n path: z.string(),\n name: z.string(),\n waitBeforeScreenshot: z.number().optional(),\n threshold: z.number().optional(),\n mask: z\n .array(\n z.object({\n selector: z.string(),\n }),\n )\n .optional(),\n viewport: z\n .object({\n width: z.string(),\n height: z.string(),\n })\n .optional(),\n }),\n );\n\n const validatePages = pagesArraySchema.safeParse(pages);\n\n if (validatePages.success) {\n log.process(\n \"info\",\n \"general\",\n `✅ Successfully validated pages structure & loaded ${pages.length} pages from JSON file.`,\n );\n\n return pages;\n }\n\n log.process(\"error\", \"general\", \"❌ Error validating the loaded pages structure\");\n log.process(\"error\", \"general\", validatePages.error);\n\n return [];\n } catch (error: unknown) {\n if (isAxiosError(error) || error instanceof Error) {\n log.process(\"error\", \"network\", `❌ Error when fetching data: ${error.message}`);\n }\n\n return [];\n }\n};\n","import path from \"node:path\";\nimport { writeFileSync } from \"node:fs\";\nimport { mapLimit } from \"async\";\nimport type { Browser, BrowserType, PageScreenshotOptions } from \"playwright-core\";\nimport { log } from \"../log.js\";\nimport { hashFile, launchBrowser, sleep } from \"../utils.js\";\nimport { config } from \"../config.js\";\nimport type { ShotItem } from \"../types.js\";\nimport { resizeViewportToFullscreen, waitForNetworkRequests } from \"./utils.js\";\n\nconst takeScreenShot = async ({\n browser,\n shotItem,\n logger,\n}: {\n browser: Browser;\n shotItem: ShotItem;\n logger: ReturnType<typeof log.item>;\n}): Promise<boolean> => {\n const context = await browser.newContext(shotItem.browserConfig);\n const page = await context.newPage();\n let success = false;\n\n page.on(\"pageerror\", (exception) => {\n logger.browser(\"error\", \"general\", \"Uncaught exception:\", exception);\n });\n\n page.on(\"console\", async (message) => {\n const values: unknown[] = [];\n\n try {\n for (const arg of message.args()) {\n values.push(await arg.jsonValue());\n }\n } catch (error: unknown) {\n logger.browser(\"error\", \"console\", \"Error while collecting console output\", error);\n }\n\n logger.browser(\"info\", \"console\", String(values.shift()), ...values);\n });\n\n try {\n await page.goto(shotItem.url);\n } catch (error: unknown) {\n if (error instanceof Error && error.name === \"TimeoutError\") {\n logger.process(\"error\", \"timeout\", `Timeout while loading page: ${shotItem.url}`);\n } else {\n logger.process(\"error\", \"general\", \"Page loading failed\", error);\n }\n }\n\n try {\n await page.waitForLoadState(\"load\", {\n timeout: config.timeouts.loadState,\n });\n } catch (error: unknown) {\n logger.process(\n \"error\",\n \"timeout\",\n `Timeout while waiting for page load state: ${shotItem.url}`,\n error,\n );\n }\n\n if (shotItem.waitForSelector) {\n try {\n await page.waitForSelector(shotItem.waitForSelector, {\n state: \"attached\",\n timeout: config.timeouts.loadState,\n });\n } catch (error: unknown) {\n logger.process(\n \"error\",\n \"timeout\",\n `Timeout while waiting for Selector ('${shotItem.waitForSelector}') to appear: ${shotItem.url}`,\n error,\n );\n }\n }\n\n try {\n await waitForNetworkRequests({\n page,\n logger,\n ignoreUrls: [\"/__webpack_hmr\"],\n });\n } catch (error: unknown) {\n logger.process(\n \"error\",\n \"timeout\",\n `Timeout while waiting for all network requests: ${shotItem.url}`,\n error,\n );\n }\n\n if (config.beforeScreenshot) {\n await config.beforeScreenshot(page, {\n shotMode: shotItem.shotMode,\n id: shotItem.id,\n shotName: shotItem.shotName,\n });\n }\n\n let fullScreenMode = true;\n\n // Wait for all fonts to finish loading before taking the screenshot.\n // Wait for fonts and freeze animations for deterministic screenshots.\n try {\n await page.evaluate(() => document.fonts.ready);\n } catch {\n // Ignore errors (e.g. page already closed)\n }\n\n // Freeze all animations for deterministic screenshots.\n // CSS: reset all animations/transitions to their initial state.\n // JS: pause SVG SMIL animations and stop requestAnimationFrame loops.\n // Playwright's `animations: \"disabled\"` only pauses at *current* position,\n // which is non-deterministic for spinners/loaders.\n try {\n await page.addStyleTag({\n content: `\n *, *::before, *::after {\n animation-delay: -0.0001ms !important;\n animation-duration: 0s !important;\n animation-play-state: paused !important;\n transition-duration: 0s !important;\n transition-delay: 0s !important;\n }\n `,\n });\n\n await page.evaluate(() => {\n // Pause all SVG SMIL animations\n document.querySelectorAll(\"svg\").forEach((svg) => {\n try {\n svg.pauseAnimations?.();\n } catch {\n // Not all SVGs support this\n }\n });\n\n // Force all web animations to jump to their start\n document.getAnimations().forEach((anim) => {\n anim.pause();\n anim.currentTime = 0;\n });\n });\n } catch {\n // Ignore errors (e.g. page already closed)\n }\n\n await sleep(shotItem?.waitBeforeScreenshot ?? config.waitBeforeScreenshot);\n\n try {\n if (shotItem.viewport) {\n const currentViewport = page.viewportSize();\n\n await page.setViewportSize({\n width: shotItem.viewport.width,\n height: currentViewport?.height ?? 500,\n });\n\n fullScreenMode = true;\n } else {\n await resizeViewportToFullscreen({ page });\n fullScreenMode = false;\n }\n } catch (error: unknown) {\n logger.process(\n \"error\",\n \"general\",\n `Could not resize viewport to fullscreen: ${shotItem.shotName}`,\n error,\n );\n }\n\n let retryCount = 0;\n let lastShotHash;\n\n try {\n while (retryCount <= config.flakynessRetries) {\n const { elementLocator } = shotItem;\n\n let screenshotOptions: PageScreenshotOptions = {\n path: shotItem.filePathCurrent,\n animations: \"disabled\",\n mask: shotItem.mask ? shotItem.mask.map((mask) => page.locator(mask.selector)) : [],\n };\n\n if (elementLocator) {\n // Explicit locator — use it directly\n await page.locator(elementLocator).screenshot(screenshotOptions);\n } else if (shotItem.shotMode === \"storybook\") {\n // Smart Storybook capture: detect portals (dialogs, popovers, etc.)\n // and choose the right element to screenshot.\n const hasPortal = await page.evaluate(() => {\n const selectors = [\n \"[data-radix-portal]\",\n \"[data-radix-popper-content-wrapper]\",\n \"[role='dialog']\",\n \"[role='alertdialog']\",\n \".ReactModal__Overlay\",\n ];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el && (el as HTMLElement).offsetHeight > 0) return true;\n }\n return false;\n });\n\n if (hasPortal) {\n // Portal detected — capture the visible content using a clip\n // that covers the union of all visible elements' bounding rects.\n const clip = await page.evaluate(() => {\n const elements = document.querySelectorAll(\n \"#storybook-root, #storybook-root *, [data-radix-portal], [data-radix-portal] *, [role='dialog'], [role='dialog'] *\",\n );\n let minX = Infinity;\n let minY = Infinity;\n let maxX = 0;\n let maxY = 0;\n for (const el of elements) {\n const rect = (el as HTMLElement).getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) continue;\n minX = Math.min(minX, rect.left);\n minY = Math.min(minY, rect.top);\n maxX = Math.max(maxX, rect.right);\n maxY = Math.max(maxY, rect.bottom);\n }\n if (minX === Infinity) return null;\n return {\n x: Math.max(0, Math.floor(minX)),\n y: Math.max(0, Math.floor(minY)),\n width: Math.ceil(maxX - Math.max(0, minX)),\n height: Math.ceil(maxY - Math.max(0, minY)),\n };\n });\n\n if (clip && clip.width > 0 && clip.height > 0) {\n await page.screenshot({ ...screenshotOptions, clip });\n } else {\n await page.screenshot({ ...screenshotOptions, fullPage: fullScreenMode });\n }\n } else {\n // No portal — screenshot #storybook-root for tight crop\n const storybookRoot = page.locator(\"#storybook-root\");\n if ((await storybookRoot.count()) > 0) {\n await storybookRoot.screenshot(screenshotOptions);\n } else {\n await page.screenshot({ ...screenshotOptions, fullPage: fullScreenMode });\n }\n }\n } else {\n screenshotOptions = { ...screenshotOptions, fullPage: fullScreenMode };\n await page.screenshot(screenshotOptions);\n }\n\n const currentShotHash = hashFile(shotItem.filePathCurrent);\n\n if (lastShotHash) {\n logger.process(\n \"info\",\n \"general\",\n `Screenshot of '${shotItem.shotName}' taken (Retry ${retryCount}). Hash: ${currentShotHash} - Previous hash: ${lastShotHash}`,\n );\n\n if (lastShotHash === currentShotHash) {\n break;\n }\n }\n\n lastShotHash = currentShotHash;\n\n if (retryCount < config.flakynessRetries) {\n await sleep(config.waitBetweenFlakynessRetries);\n }\n\n retryCount++;\n }\n\n success = true;\n\n // Capture DOM snapshot alongside screenshot\n try {\n const domHtml = await page.content();\n const htmlPath = shotItem.filePathCurrent.replace(/\\.png$/, \".html\");\n\n writeFileSync(htmlPath, domHtml);\n } catch (domError: unknown) {\n logger.process(\"error\", \"general\", \"Failed to capture DOM snapshot\", domError);\n }\n } catch (error: unknown) {\n logger.process(\"error\", \"general\", \"Error when taking screenshot\", error);\n }\n\n if (config.afterScreenshot) {\n await config.afterScreenshot(page, shotItem);\n }\n\n await context.close();\n\n const videoPath = await page.video()?.path();\n\n if (videoPath) {\n const dirname = path.dirname(videoPath);\n const ext = videoPath.split(\".\").pop() ?? \"webm\";\n const newVideoPath = `${dirname}/${shotItem.shotName}.${ext}`;\n\n await page.video()?.saveAs(newVideoPath);\n await page.video()?.delete();\n\n logger.process(\n \"info\",\n \"general\",\n `Video of '${shotItem.shotName}' recorded and saved to '${newVideoPath}`,\n );\n }\n\n return success;\n};\n\nexport const takeScreenShots = async (shotItems: ShotItem[], _browser?: BrowserType) => {\n const browser = await launchBrowser(_browser);\n const total = shotItems.length;\n\n await mapLimit<[number, ShotItem], void>(\n shotItems.entries(),\n config.shotConcurrency,\n async (item: [number, ShotItem]) => {\n const [index, shotItem] = item;\n const logger = log.item({\n shotMode: shotItem.shotMode,\n uniqueItemId: shotItem.shotName,\n itemIndex: index,\n totalItems: total,\n });\n\n logger.process(\n \"info\",\n \"general\",\n\n `Taking screenshot of '${shotItem.shotName} ${\n shotItem.breakpoint ? `[${shotItem.breakpoint}]` : \"\"\n }'`,\n );\n\n const startTime = Date.now();\n\n const result = await takeScreenShot({ browser, shotItem, logger });\n const endTime = Date.now();\n const elapsedTime = Number((endTime - startTime) / 1000).toFixed(3);\n\n if (result) {\n logger.process(\n \"info\",\n \"general\",\n `Screenshot of '${shotItem.shotName}' taken and saved to '${shotItem.filePathCurrent}' in ${elapsedTime}s`,\n );\n } else {\n logger.process(\n \"info\",\n \"general\",\n `Screenshot of '${shotItem.shotName}' failed and took ${elapsedTime}s`,\n );\n }\n },\n );\n\n await browser.close();\n};\n","import http from \"node:http\";\nimport handler from \"serve-handler\";\nimport { getPort } from \"get-port-please\";\n\nexport const launchStaticWebServer = async (basePath: string) => {\n const port = await getPort({\n random: true,\n });\n\n const server = http.createServer(async (request, response) => {\n return handler(request, response, {\n public: basePath.startsWith(\"file://\") ? basePath.slice(7) : basePath,\n cleanUrls: false,\n });\n });\n\n server.listen(port);\n\n return {\n server,\n port,\n url: `http://localhost:${port}`,\n };\n};\n","import path from \"node:path\";\nimport axios from \"axios\";\nimport type { BrowserType } from \"playwright-core\";\nimport { log } from \"../log.js\";\nimport { config, isPlatformModeConfig } from \"../config.js\";\nimport { type ShotItem } from \"../types.js\";\nimport { notSupported } from \"../constants.js\";\nimport { generateLabel } from \"../shots/utils.js\";\n\ntype HistoireStory = {\n id: string;\n title: string;\n group: string | undefined;\n layout: {\n type: string;\n width: string;\n };\n variants?: HistoireStory[];\n};\n\ntype HistoireResponse = {\n stories: HistoireStory[];\n};\n\nconst generateShotItemsForStory = (\n story: HistoireStory,\n baseUrl: string,\n browser?: BrowserType,\n): ShotItem[] => {\n const shotItems: ShotItem[] = [];\n\n // Treat stories without variants as if they had a single variant\n const variants = story.variants ?? [story];\n\n for (const variant of variants) {\n const shotName =\n config.shotNameGenerator?.({ ...variant, shotMode: \"histoire\" }) ??\n `${story.id}_${variant.title}`;\n const label = generateLabel({ browser });\n const fileNameWithExt = `${shotName}${label}.png`;\n\n shotItems.push({\n shotMode: \"histoire\",\n id: `${story.id}_${variant.id}${label}`,\n shotName: `${shotName}${label}`,\n url: `${baseUrl}/__sandbox.html?storyId=${story.id}&variantId=${variant.id}`,\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n threshold: config.threshold,\n waitForSelector: config?.histoireShots?.waitForSelector,\n componentPath: story.id,\n storyName: variant.title,\n });\n }\n\n return shotItems.filter((story) => story.id !== \"full-config\");\n};\n\nexport const generateHistoireShotItems = (\n baseUrl: string,\n stories: HistoireStory[],\n browser?: BrowserType,\n): ShotItem[] => {\n return stories.flatMap((story) => generateShotItemsForStory(story, baseUrl, browser));\n};\n\nexport const collectHistoireStories = async (histoireUrl: string) => {\n const jsonUrl = `${histoireUrl}/histoire.json`;\n\n log.process(\"info\", \"general\", `\\n=== [Histoire Mode] ${jsonUrl} ===\\n`);\n const response = await axios.get<HistoireResponse>(jsonUrl);\n\n // Ignore the full-config story from Histoire as it is just JSON\n return response.data.stories;\n};\n","import type { Server } from \"node:http\";\nimport { mapLimit } from \"async\";\nimport type { BrowserType } from \"playwright-core\";\nimport { collectLadleStories, generateLadleShotItems } from \"./crawler/ladleScreenshots.js\";\nimport { type PageScreenshotParameter, config, isPlatformModeConfig } from \"./config.js\";\nimport { collectStories, generateStorybookShotItems } from \"./crawler/storybook.js\";\nimport { generatePageShotItems, getPagesFromExternalLoader } from \"./crawler/pageScreenshots.js\";\nimport { log } from \"./log.js\";\nimport { takeScreenShots } from \"./shots/shots.js\";\nimport { getBrowsers, readDirIntoShotItems, removeFilesInFolder } from \"./utils.js\";\nimport { launchStaticWebServer } from \"./crawler/utils.js\";\nimport type { ShotItem } from \"./types.js\";\nimport {\n collectHistoireStories,\n generateHistoireShotItems,\n} from \"./crawler/histoireScreenshots.js\";\n\n/**\n * @param turboSnapFilter - When provided, only stories whose shotName is in this set\n * will be screenshotted. Stories not in the set are skipped entirely (no Playwright\n * navigation), which is the main performance win of TurboSnap.\n */\nexport const createShots = async (turboSnapFilter?: Set<string>) => {\n const { ladleShots, histoireShots, storybookShots, pageShots, customShots, imagePathCurrent } =\n config;\n let storybookShotItems: ShotItem[] = [];\n let ladleShotItems: ShotItem[] = [];\n let histoireShotItems: ShotItem[] = [];\n let pageShotItems: ShotItem[] = [];\n let customShotItems: ShotItem[] = [];\n\n removeFilesInFolder(imagePathCurrent);\n\n if (!isPlatformModeConfig(config)) {\n removeFilesInFolder(config.imagePathDifference);\n }\n\n const browsers = getBrowsers();\n\n if (ladleShots) {\n const { ladleUrl, mask } = ladleShots;\n\n log.process(\"info\", \"general\", `\\n=== [Ladle Mode] ${ladleUrl} ===\\n`);\n\n let ladleWebUrl = ladleUrl;\n let localServer: undefined | Server;\n\n if (!ladleUrl.startsWith(\"http://\") && !ladleUrl.startsWith(\"https://\")) {\n const staticWebServer = await launchStaticWebServer(ladleUrl);\n\n ladleWebUrl = staticWebServer.url;\n localServer = staticWebServer.server;\n }\n\n try {\n const collection = await collectLadleStories(ladleWebUrl);\n\n if (!collection || collection.length === 0) {\n throw new Error(\"Error: Stories not found\");\n }\n\n log.process(\"info\", \"general\", `Found ${collection.length} ladle stories`);\n\n await mapLimit(browsers, 1, async (browser: BrowserType) => {\n const shotItems = generateLadleShotItems(\n ladleWebUrl,\n Boolean(localServer),\n collection,\n mask,\n ladleShots.breakpoints,\n browsers.length > 1 ? browser : undefined,\n );\n\n const filterItemsToCheck =\n \"filterItemsToCheck\" in config ? config.filterItemsToCheck : undefined;\n\n const filteredShotItems = filterItemsToCheck\n ? shotItems.filter((item) => filterItemsToCheck(item))\n : shotItems;\n\n ladleShotItems = shotItems;\n\n log.process(\n \"info\",\n \"general\",\n `Prepared ${filteredShotItems.length} ladle stories for screenshots on ${browser.name()}`,\n );\n\n await takeScreenShots(filteredShotItems, browser);\n });\n\n localServer?.close();\n } catch (error: unknown) {\n localServer?.close();\n throw error;\n }\n\n log.process(\"info\", \"general\", \"Screenshots done!\");\n }\n\n if (histoireShots) {\n const { histoireUrl } = histoireShots;\n\n let localServer;\n let histoireWebUrl: undefined | string;\n\n if (!histoireUrl.startsWith(\"http://\") && !histoireUrl.startsWith(\"https://\")) {\n const staticWebServer = await launchStaticWebServer(histoireUrl);\n\n histoireWebUrl = staticWebServer.url;\n localServer = staticWebServer.server;\n }\n\n if (!histoireWebUrl) {\n throw new Error(\"Error: Histoire web url not found\");\n }\n\n log.process(\"info\", \"general\", `\\n=== [Histoire Mode] ${histoireUrl} ===\\n`);\n\n try {\n const collection = await collectHistoireStories(histoireWebUrl);\n\n if (!collection || collection.length === 0) {\n throw new Error(\"Error: Stories not found\");\n }\n\n log.process(\"info\", \"general\", `Found ${collection.length} Histoire stories`);\n\n await mapLimit(browsers, 1, async (browser: BrowserType) => {\n const shotItems = generateHistoireShotItems(\n histoireWebUrl,\n collection,\n browsers.length > 1 ? browser : undefined,\n );\n\n histoireShotItems = shotItems;\n\n log.process(\n \"info\",\n \"general\",\n `Prepared ${shotItems.length} Histoire stories for screenshots on ${browser.name()}`,\n );\n\n await takeScreenShots(shotItems, browser);\n });\n\n localServer?.close();\n } catch (error: unknown) {\n localServer?.close();\n\n throw error;\n }\n\n log.process(\"info\", \"general\", \"Screenshots done!\");\n }\n\n if (storybookShots) {\n const { storybookUrl, mask } = storybookShots;\n\n log.process(\"info\", \"general\", `\\n=== [Storybook Mode] ${storybookUrl} ===\\n`);\n\n let storybookWebUrl = storybookUrl;\n let localServer;\n\n if (!storybookUrl.startsWith(\"http://\") && !storybookUrl.startsWith(\"https://\")) {\n const staticWebServer = await launchStaticWebServer(storybookUrl);\n\n storybookWebUrl = staticWebServer.url;\n localServer = staticWebServer.server;\n }\n\n try {\n const collection = await collectStories(storybookWebUrl);\n\n if (!collection?.stories || collection.stories.length === 0) {\n throw new Error(\"Error: Stories not found\");\n }\n\n log.process(\"info\", \"general\", `Found ${collection.stories.length} stories`);\n\n await mapLimit(browsers, 1, async (browser: BrowserType) => {\n const shotItems = generateStorybookShotItems(\n storybookWebUrl,\n collection.stories!,\n mask,\n storybookShots.breakpoints,\n browsers.length > 1 ? browser : undefined,\n );\n\n const filterItemsToCheck =\n \"filterItemsToCheck\" in config ? config.filterItemsToCheck : undefined;\n\n let filteredShotItems = filterItemsToCheck\n ? shotItems.filter((item) => filterItemsToCheck(item))\n : shotItems;\n\n // TurboSnap: skip stories that aren't affected by code changes\n if (turboSnapFilter) {\n filteredShotItems = filteredShotItems.filter(\n (item) =>\n turboSnapFilter.has(item.shotName) ||\n turboSnapFilter.has(`${item.shotMode}/${item.shotName}`),\n );\n }\n\n storybookShotItems = shotItems; // Keep full list for metadata upload\n const capturedItems = filteredShotItems;\n\n log.process(\n \"info\",\n \"general\",\n turboSnapFilter\n ? `Prepared ${capturedItems.length}/${shotItems.length} stories for screenshots on ${browser.name()} (TurboSnap: ${shotItems.length - capturedItems.length} skipped)`\n : `Prepared ${capturedItems.length} stories for screenshots on ${browser.name()}`,\n );\n\n await takeScreenShots(capturedItems, browser);\n });\n\n localServer?.close();\n } catch (error: unknown) {\n localServer?.close();\n throw error;\n }\n\n log.process(\"info\", \"general\", \"Screenshots done!\");\n }\n\n if (pageShots) {\n const { pages: pagesFromConfig, baseUrl, mask, breakpoints } = pageShots;\n\n const pagesFromLoader = await getPagesFromExternalLoader();\n\n let jsonPages: PageScreenshotParameter[] = pagesFromLoader || [];\n\n if (config.pageShots?.pagesJsonRefiner) {\n log.process(\n \"info\",\n \"general\",\n `🧬 Refining pages received in json with function provided in pagesJsonRefiner`,\n );\n\n jsonPages = config.pageShots.pagesJsonRefiner(pagesFromLoader || []);\n }\n\n if (jsonPages.length > 0) {\n log.process(\"info\", \"general\", `Found ${jsonPages.length} pages from external loader`);\n }\n\n const pages = [...(pagesFromConfig || []), ...(jsonPages || [])];\n\n log.process(\"info\", \"general\", `\\n=== [Page Mode] ${baseUrl} ===\\n`);\n\n await mapLimit(browsers, 1, async (browser: BrowserType) => {\n const shotItems = generatePageShotItems(\n pages,\n baseUrl,\n mask,\n breakpoints,\n browsers.length > 1 ? browser : undefined,\n );\n\n pageShotItems = shotItems;\n\n log.process(\n \"info\",\n \"general\",\n `Prepared ${shotItems.length} pages for screenshots on ${browser.name()}`,\n );\n\n await takeScreenShots(shotItems, browser);\n });\n\n log.process(\"info\", \"general\", \"Screenshots done!\");\n }\n\n if (customShots) {\n const { currentShotsPath } = customShots;\n\n log.process(\"info\", \"general\", `\\n=== [Custom Mode] ${currentShotsPath} ===\\n`);\n\n customShotItems = readDirIntoShotItems(currentShotsPath);\n log.process(\"info\", \"general\", `Found ${customShotItems.length} custom shots`);\n }\n\n return [\n ...storybookShotItems,\n ...pageShotItems,\n ...ladleShotItems,\n ...histoireShotItems,\n ...customShotItems,\n ];\n};\n","import { execSync } from \"node:child_process\";\nimport { log } from \"./log.js\";\n\nconst INITIAL_BATCH_SIZE = 20;\n\n/**\n * Execute a git command and return the trimmed output.\n */\nconst execGit = (command: string): string => {\n try {\n return execSync(command, { encoding: \"utf-8\", timeout: 10_000 }).trim();\n } catch {\n return \"\";\n }\n};\n\n/**\n * Get the timestamp (in seconds since epoch) of the earliest build in the\n * project so we don't walk the entire git history.\n */\ntype HasBuildsWithCommitsFn = (commits: string[]) => Promise<string[]>;\n\n/**\n * Find the \"covering\" set of ancestor commits that have builds on the server.\n *\n * This mirrors Chromatic's approach:\n * 1. Walk git history with `git rev-list`\n * 2. Ask the server which commits have builds\n * 3. Use `--not` to exclude ancestors of commits with builds\n * 4. Repeat with exponentially larger batches until no more uncovered commits\n *\n * The result is the minimal set of ancestor commits with builds such that\n * every ancestor of HEAD either has a build or is an ancestor of a commit\n * with a build.\n */\nexport const getParentCommits = async (\n hasBuildsWithCommits: HasBuildsWithCommitsFn,\n): Promise<string[]> => {\n // Check if we're in a git repo at all\n const headCommit = execGit(\"git rev-parse HEAD\");\n if (!headCommit) {\n log.process(\"info\", \"general\", \"Not a git repository, skipping ancestor detection\");\n return [];\n }\n\n // Check if there's at least one parent commit\n const hasParent = execGit('git --no-pager log -n 1 --skip=1 --format=\"%H\"');\n if (!hasParent) {\n log.process(\"info\", \"general\", \"Initial commit, no ancestors\");\n return [];\n }\n\n let commitsWithBuilds: string[] = [];\n let commitsWithoutBuilds: string[] = [];\n let limit = INITIAL_BATCH_SIZE;\n\n for (;;) {\n // Get the next batch of candidate commits\n // `--not commitsWithBuilds` excludes ancestors of commits that already have builds\n const notArgs = commitsWithBuilds.map((c) => c.trim()).join(\" \");\n const command = `git rev-list HEAD -n ${limit + commitsWithoutBuilds.length}${notArgs ? ` --not ${notArgs}` : \"\"}`;\n\n const output = execGit(command);\n const allCommits = output ? output.split(\"\\n\").filter(Boolean) : [];\n\n // Filter out commits we already know about\n const candidates = allCommits\n .filter((c) => !commitsWithBuilds.includes(c))\n .filter((c) => !commitsWithoutBuilds.includes(c))\n .slice(0, limit);\n\n if (candidates.length === 0) {\n // No more uncovered commits — we're done\n break;\n }\n\n log.process(\n \"info\",\n \"general\",\n `🔍 Checking ${candidates.length} commits for builds (batch size ${limit})`,\n );\n\n // Ask server which of these have builds\n const newCommitsWithBuilds = await hasBuildsWithCommits(candidates);\n const newCommitsWithoutBuilds = candidates.filter((c) => !newCommitsWithBuilds.includes(c));\n\n commitsWithBuilds = [...commitsWithBuilds, ...newCommitsWithBuilds];\n commitsWithoutBuilds = [...commitsWithoutBuilds, ...newCommitsWithoutBuilds];\n\n // Exponentially increase batch size\n limit *= 2;\n\n // Safety limit — don't walk more than 10000 commits\n if (commitsWithoutBuilds.length > 10000) {\n log.process(\"info\", \"general\", \"Reached max history depth (10000 commits)\");\n break;\n }\n }\n\n if (commitsWithBuilds.length === 0) {\n log.process(\"info\", \"general\", \"No ancestor builds found — this may be the first build\");\n return [];\n }\n\n // Find the maximally descendent commits — remove any that are ancestors of others\n // This gives us the minimal covering set\n if (commitsWithBuilds.length > 1) {\n const parentArgs = commitsWithBuilds.map((c) => `\"${c}^@\"`).join(\" \");\n const maxOutput = execGit(`git rev-list ${commitsWithBuilds.join(\" \")} --not ${parentArgs}`);\n const maxCommits = maxOutput ? maxOutput.split(\"\\n\").filter(Boolean) : [];\n if (maxCommits.length > 0) {\n commitsWithBuilds = maxCommits;\n }\n }\n\n log.process(\n \"info\",\n \"general\",\n `📌 Found ${commitsWithBuilds.length} ancestor build(s): ${commitsWithBuilds.map((c) => c.slice(0, 7)).join(\", \")}`,\n );\n\n return commitsWithBuilds;\n};\n","/**\n * Typed oRPC client factory.\n * Used by CLI and frontend to create a type-safe client.\n *\n * Uses RPCLink which speaks the oRPC RPC wire format.\n * The server mounts both OpenAPIHandler (for .route()-defined REST endpoints)\n * and RPCHandler (fallback for all procedures). The RPCLink client always\n * hits the RPCHandler path, ensuring compatibility during the OpenAPI migration.\n */\nimport { createORPCClient } from \"@orpc/client\";\nimport { RPCLink } from \"@orpc/client/fetch\";\nconst resolveBaseUrl = (url) => {\n if (url.startsWith(\"http\"))\n return url;\n if (typeof window !== \"undefined\")\n return `${window.location.origin}${url}`;\n return url;\n};\nconst isBrowser = typeof window !== \"undefined\";\nexport const createTypedClient = (options) => {\n const link = new RPCLink({\n url: () => `${resolveBaseUrl(options.url)}/rpc`,\n // Browser uses cookies; CLI/CI uses Authorization / x-api-key headers\n fetch: (input, init) => {\n const signal = options.fetchTimeout != null ? AbortSignal.timeout(options.fetchTimeout) : undefined;\n return fetch(input, {\n ...init,\n ...(isBrowser ? { credentials: \"include\" } : {}),\n ...(signal ? { signal } : {}),\n });\n },\n headers: () => {\n const headers = {};\n if (options.apiToken) {\n headers.authorization = `Bearer ${options.apiToken}`;\n }\n if (options.apiKey) {\n headers[\"x-api-key\"] = options.apiKey;\n }\n return headers;\n },\n });\n return createORPCClient(link);\n};\n","import { readFileSync } from \"node:fs\";\nimport { retry } from \"async\";\nimport { createTypedClient } from \"@gfxlabs/third-eye-shared/client\";\nimport type {\n GetApiTokenOutput,\n BuildInitOutput,\n CheckCacheOutput,\n PrepareUploadOutput,\n ProcessShotsOutput,\n GetAffectedStoriesOutput,\n} from \"@gfxlabs/third-eye-shared\";\nimport { log, logMemory } from \"./log.js\";\nimport type { PlatformModeConfig } from \"./config.js\";\n\nexport type ShotConfig = {\n name: string;\n threshold?: number;\n};\n\nconst createClient = (platformUrl: string, apiKey?: string, apiToken?: string) =>\n createTypedClient({ url: platformUrl, apiKey, apiToken });\n\n// Retry wrapper: 3 attempts with exponential backoff\nconst withRetry = async <T>(\n action: string,\n fn: () => Promise<T>,\n logger: typeof log.process = log.process,\n): Promise<T> => {\n logger(\"info\", \"api\", `Sending to API [${action}]`);\n\n const result = await retry(\n {\n times: 3,\n interval(retryCount) {\n const delay = Math.round(2 ** retryCount * 3000 * Math.random());\n\n logger(\"info\", \"api\", `Retry attempt ${retryCount} in ${delay}ms [${action}]`);\n\n return delay;\n },\n },\n async () => fn(),\n );\n\n logger(\"info\", \"api\", `Successfully sent to API [${action}]`);\n\n return result;\n};\n\nexport const getApiToken = async (config: PlatformModeConfig): Promise<GetApiTokenOutput> => {\n const client = createClient(config.thirdEyePlatform, config.apiKey);\n\n try {\n return await withRetry(\"getApiToken\", () =>\n client.auth.getApiToken({\n projectId: config.thirdEyeProjectId,\n orgId: config.thirdEyeOrgId,\n }),\n );\n } catch (error: unknown) {\n if (error instanceof Error) {\n log.process(\"error\", \"api\", error.message);\n } else {\n log.process(\"error\", \"api\", error);\n }\n\n process.exit(1);\n }\n};\n\nexport const sendInitToAPI = async (\n config: PlatformModeConfig,\n apiToken: string,\n parentCommits?: string[],\n): Promise<BuildInitOutput> => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n\n return withRetry(\"init\", () =>\n client.orgs.projects.builds.init({\n orgId: config.thirdEyeOrgId,\n projectId: config.thirdEyeProjectId,\n commit: config.commitHash,\n branchName: config.commitRefName,\n buildNumber: config.ciBuildNumber,\n baseBranch: config.baseBranch || undefined,\n prNumber: config.prNumber,\n parentCommits,\n }),\n );\n};\n\nexport const sendHasBuildsWithCommitsToAPI = async (\n config: PlatformModeConfig,\n apiToken: string,\n commits: string[],\n): Promise<string[]> => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n\n const result = await withRetry(\"hasBuildsWithCommits\", () =>\n client.orgs.projects.builds.hasBuildsWithCommits({\n orgId: config.thirdEyeOrgId,\n projectId: config.thirdEyeProjectId,\n commits,\n }),\n );\n\n return result.commits;\n};\n\nexport const sendFinalizeToAPI = async (\n config: PlatformModeConfig,\n apiToken: string,\n): Promise<unknown> => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n\n return withRetry(\"finalize\", () =>\n client.orgs.projects.builds.finalize({\n orgId: config.thirdEyeOrgId,\n projectId: config.thirdEyeProjectId,\n branchName: config.commitRefName,\n commit: config.commitHash,\n buildNumber: config.ciBuildNumber,\n }),\n );\n};\n\nexport const sendCheckCacheToAPI = async (\n config: PlatformModeConfig,\n apiToken: string,\n cacheKey: string,\n): Promise<CheckCacheOutput> => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n\n return withRetry(\"checkCache\", () =>\n client.orgs.projects.builds.checkCache({\n orgId: config.thirdEyeOrgId,\n projectId: config.thirdEyeProjectId,\n cacheKey,\n }),\n );\n};\n\nexport const prepareUpload = async (\n config: PlatformModeConfig,\n apiToken: string,\n shotNamesWithHashes: Array<{ name: string; hash: string }>,\n cacheKey?: string,\n): Promise<PrepareUploadOutput> => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n\n return withRetry(\"prepareUpload\", () =>\n client.orgs.projects.uploads.prepare({\n orgId: config.thirdEyeOrgId,\n projectId: config.thirdEyeProjectId,\n branchName: config.commitRefName,\n commit: config.commitHash,\n buildNumber: config.ciBuildNumber,\n currentShots: shotNamesWithHashes,\n cacheKey,\n }),\n );\n};\n\nexport const uploadShot = async ({\n config,\n apiToken,\n uploadToken,\n name,\n file,\n shotMode,\n componentPath,\n storyName,\n storyId,\n importPath,\n metadata,\n logger,\n}: {\n config: PlatformModeConfig;\n apiToken: string;\n uploadToken: string;\n name: string;\n file: string;\n shotMode?: string;\n componentPath?: string;\n storyName?: string;\n storyId?: string;\n importPath?: string;\n metadata?: Record<string, unknown>;\n logger?: ReturnType<typeof log.item>;\n}) => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n const logFn = logger?.process ?? log.process;\n\n const fileBuffer = readFileSync(file);\n const fileData = fileBuffer.toString(\"base64\");\n\n return withRetry(\n \"uploadShot\",\n () =>\n client.orgs.projects.uploads.uploadShot({\n orgId: config.thirdEyeOrgId,\n uploadToken,\n name,\n fileData,\n shotMode,\n componentPath,\n storyName,\n storyId,\n importPath,\n metadata,\n }),\n logFn,\n );\n};\n\nexport const processShots = async (\n config: PlatformModeConfig,\n apiToken: string,\n uploadToken: string,\n shotsConfig?: ShotConfig[],\n cacheKey?: string,\n): Promise<ProcessShotsOutput> => {\n // processShots uses a dedicated client with a 10-minute fetch timeout.\n // It is NOT retried because the server deletes existing comparisons before\n // creating new ones (idempotent). Retrying would discard server-side progress.\n const client = createTypedClient({\n url: config.thirdEyePlatform,\n apiToken,\n fetchTimeout: 600_000, // 10 minutes — server compares every image pair sequentially\n });\n\n log.process(\"info\", \"api\", \"Sending to API [processShots]\");\n\n const result = await client.orgs.projects.builds.processShots({\n orgId: config.thirdEyeOrgId,\n uploadToken,\n config: {\n shots: shotsConfig,\n threshold: config.threshold,\n },\n log: logMemory,\n cacheKey,\n });\n\n log.process(\"info\", \"api\", \"Successfully sent to API [processShots]\");\n\n return result;\n};\n\nexport const sendRecordLogsToAPI = async (_config: PlatformModeConfig, _apiToken: string) => {\n // Logs are sent as part of processShots\n log.process(\"info\", \"api\", \"Logs recorded with build\");\n};\n\nexport const getAffectedStories = async (\n config: PlatformModeConfig,\n apiToken: string,\n changedFiles: string[],\n): Promise<GetAffectedStoriesOutput> => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n\n return withRetry(\"getAffectedStories\", () =>\n client.orgs.projects.builds.getAffectedStories({\n orgId: config.thirdEyeOrgId,\n projectId: config.thirdEyeProjectId,\n changedFiles,\n }),\n );\n};\n\nexport const uploadStorybookArchive = async (\n config: PlatformModeConfig,\n apiToken: string,\n projectId: string,\n buildId: string,\n archive: string,\n) => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n\n return withRetry(\"uploadStorybookArchive\", () =>\n client.orgs.projects.storybook.uploadArchive({\n orgId: config.thirdEyeOrgId,\n projectId,\n buildId,\n archive,\n }),\n );\n};\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { mapLimit } from \"async\";\nimport { type PlatformModeConfig } from \"./config.js\";\nimport type { ExtendedShotItem } from \"./types.js\";\nimport { uploadShot } from \"./api.js\";\nimport { log } from \"./log.js\";\nimport { parseHrtimeToSeconds } from \"./utils.js\";\nimport { MEDIA_UPLOAD_CONCURRENCY } from \"./constants.js\";\n\nexport const uploadRequiredShots = async ({\n config,\n apiToken,\n uploadToken,\n requiredFileHashes,\n extendedShotItems,\n dependencyMap,\n}: {\n config: PlatformModeConfig;\n apiToken: string;\n uploadToken: string;\n requiredFileHashes: string[];\n extendedShotItems: ExtendedShotItem[];\n /** TurboSnap dependency map: story importPath → array of dependency file paths */\n dependencyMap?: Map<string, string[]>;\n}) => {\n if (requiredFileHashes.length > 0) {\n log.process(\"info\", \"api\", \"Uploading shots\");\n\n const uploadStart = process.hrtime();\n\n const requiredShotItems = extendedShotItems.filter((shotItem) =>\n requiredFileHashes.includes(shotItem.hash),\n );\n\n await mapLimit<[number, ExtendedShotItem], void>(\n requiredShotItems.entries(),\n MEDIA_UPLOAD_CONCURRENCY,\n async ([index, shotItem]: [number, ExtendedShotItem]) => {\n const logger = log.item({\n shotMode: shotItem.shotMode,\n uniqueItemId: shotItem.shotName,\n itemIndex: index,\n totalItems: requiredShotItems.length,\n });\n\n // Include DOM HTML as base64 in metadata if available\n const htmlPath = shotItem.filePathCurrent.replace(/\\.png$/, \".html\");\n const domHtml = existsSync(htmlPath)\n ? readFileSync(htmlPath).toString(\"base64\")\n : undefined;\n\n await uploadShot({\n config,\n apiToken,\n uploadToken,\n name: `${shotItem.shotMode}/${shotItem.shotName}`,\n file: shotItem.filePathCurrent,\n shotMode: shotItem.shotMode,\n componentPath: shotItem.componentPath,\n storyName: shotItem.storyName,\n storyId: shotItem.storyId,\n importPath: shotItem.importPath,\n metadata: {\n ...(shotItem.storyArgs ? { args: shotItem.storyArgs } : {}),\n ...(shotItem.tags ? { tags: shotItem.tags } : {}),\n ...(shotItem.viewport ? { viewport: shotItem.viewport } : {}),\n ...(shotItem.breakpoint !== undefined ? { breakpoint: shotItem.breakpoint } : {}),\n ...(domHtml ? { dom_html: domHtml } : {}),\n ...(dependencyMap && shotItem.importPath && dependencyMap.has(shotItem.importPath)\n ? { dependencies: dependencyMap.get(shotItem.importPath) }\n : {}),\n },\n logger,\n });\n },\n );\n\n const uploadStop = process.hrtime(uploadStart);\n\n log.process(\"info\", \"api\", `Uploading shots took ${parseHrtimeToSeconds(uploadStop)} seconds`);\n }\n\n return true;\n};\n","/**\n * TurboSnap: static import tracer for determining which stories are affected by code changes.\n *\n * Given a list of changed files (from git diff) and the storybook index (importPath per story),\n * this module traces each story file's transitive imports to determine which stories need\n * re-screenshotting. Stories whose dependency tree does not overlap with any changed file are\n * skipped entirely — they never get launched in Playwright, saving the majority of run time.\n *\n * The import tracer is intentionally simple: it parses `import`/`require` statements with a\n * regex, resolves relative and aliased paths, and follows them recursively with cycle detection.\n * It does not evaluate dynamic imports or re-exports — those are rare in component code and the\n * fallback is always \"capture the story\" (safe default).\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\n// Extensions to try when resolving bare imports (e.g. `./Button` → `./Button.tsx`)\nconst RESOLVE_EXTENSIONS = [\".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\"];\nconst INDEX_FILES = RESOLVE_EXTENSIONS.map((ext) => `index${ext}`);\n\n/**\n * Regex that captures the string literal from import/require statements.\n * Handles:\n * import ... from 'foo'\n * import ... from \"foo\"\n * import 'foo'\n * export ... from 'foo'\n * require('foo')\n */\nconst IMPORT_RE =\n /(?:import\\s+(?:[\\s\\S]*?\\s+from\\s+)?|export\\s+(?:[\\s\\S]*?\\s+from\\s+)?|require\\s*\\()[\"']([^\"']+)[\"']/g;\n\ntype PathAliases = Record<string, string>;\n\n/**\n * Parse tsconfig-style path aliases into a simple prefix → directory map.\n * E.g. `{ \"#/*\": [\"./src/app/*\"] }` → `{ \"#/\": \"/abs/path/src/app/\" }`\n */\nexport const parsePathAliases = (tsconfigPath: string, projectRoot: string): PathAliases => {\n const aliases: PathAliases = {};\n\n try {\n const raw = readFileSync(tsconfigPath, \"utf-8\");\n // Strip comments (// and /* */) for lenient JSON parsing\n const stripped = raw\n .replace(/\\/\\/[^\\n]*/g, \"\")\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n // Strip trailing commas before } or ]\n .replace(/,(\\s*[}\\]])/g, \"$1\");\n const tsconfig = JSON.parse(stripped) as {\n compilerOptions?: { paths?: Record<string, string[]> };\n };\n const paths = tsconfig.compilerOptions?.paths;\n\n if (paths) {\n for (const [pattern, targets] of Object.entries(paths)) {\n if (targets.length > 0) {\n // Convert `#/*` → `#/` and `./src/app/*` → `/abs/path/src/app/`\n const prefix = pattern.replace(/\\*$/, \"\");\n const target = targets[0].replace(/\\*$/, \"\");\n aliases[prefix] = path.resolve(projectRoot, target);\n }\n }\n }\n } catch {\n // No tsconfig or unparseable — aliases will be empty\n }\n\n return aliases;\n};\n\n/**\n * Resolve a single import specifier to an absolute file path.\n * Returns undefined if the import is external (node_modules) or unresolvable.\n */\nconst resolveImport = (\n specifier: string,\n fromFile: string,\n aliases: PathAliases,\n): string | undefined => {\n // Skip node_modules / bare specifiers (react, @storybook/*, etc.)\n if (\n !specifier.startsWith(\".\") &&\n !specifier.startsWith(\"/\") &&\n !Object.keys(aliases).some((prefix) => specifier.startsWith(prefix))\n ) {\n return undefined;\n }\n\n let resolved: string;\n\n // Check aliases\n const matchingAlias = Object.entries(aliases).find(([prefix]) => specifier.startsWith(prefix));\n\n if (matchingAlias) {\n const [prefix, target] = matchingAlias;\n resolved = path.join(target, specifier.slice(prefix.length));\n } else {\n // Relative import\n resolved = path.resolve(path.dirname(fromFile), specifier);\n }\n\n // Try exact path first\n if (existsSync(resolved) && !isDirectory(resolved)) {\n return resolved;\n }\n\n // Try with extensions\n for (const ext of RESOLVE_EXTENSIONS) {\n const withExt = resolved + ext;\n if (existsSync(withExt)) return withExt;\n }\n\n // Try as directory with index file\n for (const indexFile of INDEX_FILES) {\n const withIndex = path.join(resolved, indexFile);\n if (existsSync(withIndex)) return withIndex;\n }\n\n return undefined;\n};\n\nconst isDirectory = (p: string): boolean => {\n try {\n const { statSync } = require(\"node:fs\") as typeof import(\"node:fs\");\n return statSync(p).isDirectory();\n } catch {\n return false;\n }\n};\n\n/**\n * Extract all import specifiers from a source file.\n */\nconst extractImports = (filePath: string): string[] => {\n try {\n const content = readFileSync(filePath, \"utf-8\");\n const imports: string[] = [];\n let match: RegExpExecArray | null;\n\n // Reset regex state\n IMPORT_RE.lastIndex = 0;\n\n while ((match = IMPORT_RE.exec(content)) !== null) {\n if (match[1]) imports.push(match[1]);\n }\n\n return imports;\n } catch {\n return [];\n }\n};\n\n/**\n * Trace all transitive dependencies of a file recursively.\n * Returns a Set of absolute file paths that the file depends on (including itself).\n */\nexport const traceDependencies = (\n entryFile: string,\n projectRoot: string,\n aliases: PathAliases,\n _cache?: Map<string, Set<string>>,\n): Set<string> => {\n const cache = _cache ?? new Map<string, Set<string>>();\n\n // Normalize to absolute\n const absEntry = path.isAbsolute(entryFile) ? entryFile : path.resolve(projectRoot, entryFile);\n\n if (cache.has(absEntry)) return cache.get(absEntry)!;\n\n // Insert a placeholder to detect cycles\n const deps = new Set<string>([absEntry]);\n cache.set(absEntry, deps);\n\n if (!existsSync(absEntry)) return deps;\n\n const importSpecifiers = extractImports(absEntry);\n\n for (const spec of importSpecifiers) {\n const resolved = resolveImport(spec, absEntry, aliases);\n\n if (resolved && !deps.has(resolved)) {\n deps.add(resolved);\n\n // Recurse into transitive deps\n const transitiveDeps = traceDependencies(resolved, projectRoot, aliases, cache);\n\n for (const d of transitiveDeps) {\n deps.add(d);\n }\n }\n }\n\n return deps;\n};\n\nexport type TurboSnapResult = {\n /** Story IDs/names that should be re-captured */\n affected: string[];\n /** Story IDs/names that can be skipped (unchanged) */\n skipped: string[];\n /** Total number of stories */\n total: number;\n /** Dependency map: story import path → set of dependency file paths (relative to project root) */\n dependencyMap: Map<string, string[]>;\n};\n\n/**\n * Determine which stories are affected by a set of changed files.\n *\n * @param stories - Array of { shotName, importPath } from the storybook index\n * @param changedFiles - Relative file paths from `git diff --name-only`\n * @param projectRoot - Absolute path to the project root (for resolving imports)\n * @returns Which stories to capture and which to skip\n */\nexport const getAffectedStoriesLocal = (\n stories: Array<{ shotName: string; importPath?: string }>,\n changedFiles: string[],\n projectRoot: string,\n): TurboSnapResult => {\n const aliases = parsePathAliases(path.join(projectRoot, \"tsconfig.json\"), projectRoot);\n\n // Normalize changed files to absolute paths\n const changedAbsolute = new Set(changedFiles.map((f) => path.resolve(projectRoot, f)));\n\n const affected: string[] = [];\n const skipped: string[] = [];\n const dependencyMap = new Map<string, string[]>();\n const depCache = new Map<string, Set<string>>();\n\n for (const story of stories) {\n if (!story.importPath) {\n // No import path — can't trace dependencies, always capture\n affected.push(story.shotName);\n continue;\n }\n\n // Resolve story import path relative to project root\n // Storybook's importPath is like `./src/components/Button.stories.tsx`\n const storyFile = path.resolve(projectRoot, story.importPath);\n const deps = traceDependencies(storyFile, projectRoot, aliases, depCache);\n\n // Store relative deps for the server cache\n const relativeDeps = [...deps].map((d) => path.relative(projectRoot, d));\n dependencyMap.set(story.importPath, relativeDeps);\n\n // Check if any dependency overlaps with changed files\n let isAffected = false;\n\n for (const dep of deps) {\n if (changedAbsolute.has(dep)) {\n isAffected = true;\n break;\n }\n }\n\n if (isAffected) {\n affected.push(story.shotName);\n } else {\n skipped.push(story.shotName);\n }\n }\n\n return { affected, skipped, total: stories.length, dependencyMap };\n};\n","import { execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport fse from \"fs-extra\";\nimport { checkDifferences } from \"./checkDifferences.js\";\nimport { createShots } from \"./createShots.js\";\nimport {\n createShotsFolders,\n exitProcess,\n hashFile,\n isUpdateMode,\n parseHrtimeToSeconds,\n removeFilesInFolder,\n} from \"./utils.js\";\nimport type { GenerateOnlyModeConfig, PlatformModeConfig } from \"./config.js\";\nimport { getParentCommits } from \"./git.js\";\nimport {\n type ShotConfig,\n getApiToken,\n prepareUpload,\n processShots,\n sendInitToAPI,\n sendHasBuildsWithCommitsToAPI,\n sendRecordLogsToAPI,\n sendCheckCacheToAPI,\n getAffectedStories,\n uploadStorybookArchive,\n} from \"./api.js\";\nimport { log } from \"./log.js\";\nimport type { ExtendedShotItem } from \"./types.js\";\nimport { uploadRequiredShots } from \"./upload.js\";\nimport { getAffectedStoriesLocal } from \"./turbosnap.js\";\n\nconst kebabCase = (str?: string): string =>\n (str ?? \"\")\n .replace(/([a-z\\d])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_/]+/g, \"-\")\n .toLowerCase();\n\n/** Generate the shot name from a storybook title and story name, matching the crawler's logic. */\nconst generateShotName = (title: string, name: string): string =>\n [kebabCase(title), kebabCase(name)].filter(Boolean).join(\"--\");\n\n/**\n * Get the list of files changed between the current HEAD and a base ref\n * using git diff. Returns an empty array if git is unavailable or fails.\n */\nconst getChangedFiles = (baseRef: string): string[] => {\n try {\n const output = execSync(`git diff --name-only ${baseRef}...HEAD`, {\n encoding: \"utf-8\",\n timeout: 30_000,\n });\n\n return output\n .trim()\n .split(\"\\n\")\n .filter((f) => f.length > 0);\n } catch (error: unknown) {\n if (error instanceof Error) {\n log.process(\"warn\", \"general\", `Failed to get changed files: ${error.message}`);\n }\n\n return [];\n }\n};\n\nexport const runner = async (config: GenerateOnlyModeConfig) => {\n const executionStart = process.hrtime();\n\n try {\n if (isUpdateMode()) {\n log.process(\n \"info\",\n \"general\",\n \"Running third-eye in update mode. Baseline screenshots will be updated\",\n );\n }\n\n log.process(\"info\", \"general\", \"📂 Creating shot folders\");\n const createShotsStart = process.hrtime();\n\n createShotsFolders();\n\n log.process(\"info\", \"general\", \"📸 Creating shots\");\n const shotItems = await createShots();\n\n const createShotsStop = process.hrtime(createShotsStart);\n\n log.process(\n \"info\",\n \"general\",\n `Creating shots took ${parseHrtimeToSeconds(createShotsStop)} seconds`,\n );\n\n if (config.generateOnly && shotItems.length === 0) {\n log.process(\"info\", \"general\", `👋 Exiting process with nothing to compare.`);\n await exitProcess({ shotsNumber: shotItems.length });\n }\n\n log.process(\"info\", \"general\", \"🔍 Checking differences\");\n const checkDifferenceStart = process.hrtime();\n\n const { filterItemsToCheck } = config;\n const filteredShotItems = filterItemsToCheck\n ? shotItems.filter((item) => filterItemsToCheck(item))\n : shotItems;\n const { aboveThresholdDifferenceItems, noBaselinesItems } =\n await checkDifferences(filteredShotItems);\n\n if (isUpdateMode()) {\n // Remove only the files which are no longer present in our shot items\n removeFilesInFolder(\n config.imagePathBaseline,\n shotItems.map((shotItem) => shotItem.filePathBaseline),\n );\n\n // Synchronize differences from both lack of baseline and over threshold difference\n for (const noBaselineItem of noBaselinesItems) {\n fse.copySync(noBaselineItem.filePathCurrent, noBaselineItem.filePathBaseline);\n }\n\n for (const aboveThresholdDifferenceItem of aboveThresholdDifferenceItems) {\n fse.copySync(\n aboveThresholdDifferenceItem.filePathCurrent,\n aboveThresholdDifferenceItem.filePathBaseline,\n );\n }\n }\n\n if (\n (aboveThresholdDifferenceItems.length > 0 || noBaselinesItems.length > 0) &&\n config.failOnDifference\n ) {\n log.process(\n \"info\",\n \"general\",\n `👋 Exiting process with ${aboveThresholdDifferenceItems.length} found differences & ${noBaselinesItems.length} baselines to update`,\n );\n\n if (config.generateOnly) {\n await exitProcess({ shotsNumber: shotItems.length });\n }\n }\n\n const checkDifferenceStop = process.hrtime(checkDifferenceStart);\n\n log.process(\n \"info\",\n \"general\",\n `⏱ Checking differences took ${parseHrtimeToSeconds(checkDifferenceStop)} seconds`,\n );\n\n const executionStop = process.hrtime(executionStart);\n\n log.process(\n \"info\",\n \"general\",\n `⏱ Lost Pixel run took ${parseHrtimeToSeconds(executionStop)} seconds`,\n );\n\n await exitProcess({\n shotsNumber: shotItems.length,\n runDuration: Number(parseHrtimeToSeconds(executionStop)),\n exitCode: 0,\n });\n } catch (error: unknown) {\n const executionStop = process.hrtime(executionStart);\n\n if (error instanceof Error) {\n log.process(\"error\", \"general\", error.message);\n } else {\n log.process(\"error\", \"general\", error);\n }\n\n await exitProcess({\n runDuration: Number(parseHrtimeToSeconds(executionStop)),\n error,\n });\n }\n};\n\nexport const getPlatformApiToken = async (config: PlatformModeConfig) => {\n if (!config.apiKey) {\n log.process(\"error\", \"general\", `Running Lost Pixel in 'platform' mode requires an API key`);\n process.exit(1);\n }\n\n if (isUpdateMode()) {\n log.process(\n \"error\",\n \"general\",\n `Running Lost Pixel in 'update' mode requires the 'generateOnly' option to be set to true`,\n );\n process.exit(1);\n }\n\n try {\n const result = await getApiToken(config);\n\n return result.apiToken;\n } catch (error: unknown) {\n if (error instanceof Error) {\n log.process(\"error\", \"general\", error.message);\n } else {\n log.process(\"error\", \"general\", error);\n }\n\n process.exit(1);\n }\n};\n\nconst checkForCachedBuild = async (config: PlatformModeConfig, apiToken: string) => {\n if (process.env.THIRD_EYE_CACHE_KEY) {\n log.process(\"info\", \"general\", `♻️ Using cache key ${process.env.THIRD_EYE_CACHE_KEY}`);\n\n const { cacheExists } = await sendCheckCacheToAPI(\n config,\n apiToken,\n process.env.THIRD_EYE_CACHE_KEY,\n );\n\n if (cacheExists) {\n log.process(\n \"info\",\n \"general\",\n `♻️ Cache hit for key ${process.env.THIRD_EYE_CACHE_KEY} - Skipping shot creation`,\n );\n\n const { uploadToken } = await prepareUpload(\n config,\n apiToken,\n [],\n process.env.THIRD_EYE_CACHE_KEY,\n );\n\n await processShots(config, apiToken, uploadToken, [], process.env.THIRD_EYE_CACHE_KEY);\n\n return true;\n }\n\n log.process(\"info\", \"general\", `♻️ Cache miss for key ${process.env.THIRD_EYE_CACHE_KEY}`);\n }\n\n return false;\n};\n\nexport const platformRunner = async (config: PlatformModeConfig, apiToken: string) => {\n const executionStart = process.hrtime();\n\n try {\n log.process(\n \"info\",\n \"general\",\n [\n \"📀 Using details:\",\n `ciBuildId = ${config.ciBuildId}`,\n `ciBuildNumber = ${config.ciBuildNumber}`,\n `repository = ${config.repository}`,\n `commitRefName = ${config.commitRefName}`,\n `commitHash = ${config.commitHash}`,\n ].join(\"\\n - \"),\n );\n\n // Resolve ancestor builds using git history (Chromatic-style baseline resolution)\n log.process(\"info\", \"general\", \"🔍 Resolving ancestor builds from git history...\");\n const parentCommits = await getParentCommits((commits) =>\n sendHasBuildsWithCommitsToAPI(config, apiToken, commits),\n );\n\n await sendInitToAPI(config, apiToken, parentCommits);\n\n const foundCache = await checkForCachedBuild(config, apiToken);\n\n if (!foundCache) {\n log.process(\"info\", \"general\", \"📂 Creating shot folders\");\n const createShotsStart = process.hrtime();\n\n createShotsFolders();\n\n // TurboSnap: determine which stories need re-capturing BEFORE launching Playwright.\n // This reads the storybook index.json from disk, traces each story's import dependencies,\n // and only passes affected stories to createShots — skipping Playwright entirely for\n // unchanged stories.\n let turboSnapFilter: Set<string> | undefined;\n let turboSnapDependencyMap: Map<string, string[]> | undefined;\n\n if (config.turboSnap && config.baseBranch) {\n log.process(\n \"info\",\n \"general\",\n `⚡ TurboSnap enabled, checking changed files against ${config.baseBranch}`,\n );\n\n const changedFiles = getChangedFiles(config.baseBranch);\n\n if (changedFiles.length > 0) {\n log.process(\"info\", \"general\", `Found ${changedFiles.length} changed file(s)`);\n\n try {\n // Read storybook index.json directly from disk (no browser needed)\n const storybookPath = config.storybookShots?.storybookUrl ?? \"\";\n const indexJsonPath = path.join(\n storybookPath.startsWith(\"http\") ? \"\" : storybookPath,\n \"index.json\",\n );\n\n if (existsSync(indexJsonPath)) {\n const indexJson = JSON.parse(fse.readFileSync(indexJsonPath, \"utf-8\")) as {\n entries?: Record<\n string,\n { id: string; title: string; name: string; importPath?: string; type?: string }\n >;\n stories?: Record<\n string,\n { id: string; title: string; name: string; importPath?: string; type?: string }\n >;\n };\n const entries = indexJson.entries ?? indexJson.stories ?? {};\n const stories = Object.values(entries)\n .filter((e) => e.type !== \"docs\")\n .map((e) => ({\n shotName: generateShotName(e.title, e.name),\n importPath: e.importPath,\n }));\n\n const turboResult = getAffectedStoriesLocal(stories, changedFiles, process.cwd());\n\n log.process(\n \"info\",\n \"general\",\n `⚡ TurboSnap: ${turboResult.affected.length} affected, ${turboResult.skipped.length} skipped out of ${turboResult.total} stories`,\n );\n\n turboSnapFilter = new Set(turboResult.affected);\n turboSnapDependencyMap = turboResult.dependencyMap;\n } else {\n // Fall back to server-side TurboSnap if index.json is not on disk\n // (e.g. when storybookUrl is an HTTP URL)\n try {\n const serverResult = await getAffectedStories(config, apiToken, changedFiles);\n\n log.process(\n \"info\",\n \"general\",\n `⚡ TurboSnap (server): ${serverResult.affectedCount} affected, ${serverResult.skippedCount} skipped`,\n );\n\n if (serverResult.affected.length > 0) {\n turboSnapFilter = new Set(serverResult.affected);\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n log.process(\n \"warn\",\n \"general\",\n `TurboSnap server query failed, capturing all stories: ${error.message}`,\n );\n }\n }\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n log.process(\n \"warn\",\n \"general\",\n `TurboSnap filtering failed, capturing all stories: ${error.message}`,\n );\n }\n }\n } else {\n log.process(\n \"info\",\n \"general\",\n \"TurboSnap: no changed files detected, capturing all stories\",\n );\n }\n }\n\n log.process(\"info\", \"general\", \"📸 Creating shots\");\n let shotItems = await createShots(turboSnapFilter);\n\n const shotNames = shotItems.map((shotItem) => shotItem.shotName);\n const uniqueShotNames = new Set(shotNames);\n\n if (shotNames.length !== uniqueShotNames.size) {\n const duplicates: string[] = shotNames.filter(\n (shotName) => shotNames.filter((item) => item === shotName).length > 1,\n );\n\n throw new Error(\n `Error: Shot names must be unique (check for duplicate Story names: [ ${[\n ...new Set(duplicates),\n ].join(\", \")} ])`,\n );\n }\n\n const createShotsStop = process.hrtime(createShotsStart);\n\n log.process(\n \"info\",\n \"general\",\n `⏱ Creating shots took ${parseHrtimeToSeconds(createShotsStop)} seconds`,\n );\n\n // Filter out shots whose files don't exist (e.g. stories that render nothing)\n const validShotItems = shotItems.filter((shotItem) => {\n if (!existsSync(shotItem.filePathCurrent)) {\n log.process(\n \"warn\",\n \"general\",\n `⚠️ Skipping shot '${shotItem.shotName}' — file not found: ${shotItem.filePathCurrent}`,\n );\n return false;\n }\n return true;\n });\n\n const extendedShotItems: ExtendedShotItem[] = validShotItems.map((shotItem) => ({\n ...shotItem,\n uniqueName: `${shotItem.shotMode}/${shotItem.shotName}`,\n hash: hashFile(shotItem.filePathCurrent),\n }));\n\n const { buildId, requiredFileHashes, uploadToken } = await prepareUpload(\n config,\n apiToken,\n extendedShotItems.map((shotItem) => ({\n name: shotItem.uniqueName,\n hash: shotItem.hash,\n storyId: shotItem.storyId,\n })),\n );\n\n log.process(\n \"info\",\n \"general\",\n [\n `🏙 `,\n `${shotItems.length} shot(s) in total.`,\n `${shotItems.length - requiredFileHashes.length} shot(s) already exist on platform.`,\n `${requiredFileHashes.length} shot(s) will be uploaded.`,\n ].join(\" \"),\n );\n\n await uploadRequiredShots({\n config,\n apiToken,\n uploadToken,\n requiredFileHashes,\n extendedShotItems,\n dependencyMap: turboSnapDependencyMap,\n });\n\n const shotsConfig: ShotConfig[] = shotItems.map((shotItem) => ({\n name: `${shotItem.shotMode}/${shotItem.shotName}`,\n threshold: shotItem.threshold,\n }));\n\n await processShots(\n config,\n apiToken,\n uploadToken,\n shotsConfig,\n process.env.THIRD_EYE_CACHE_KEY,\n );\n\n // Upload storybook-static if configured (for hosted storybook feature)\n if (config.storybookStaticDir && existsSync(config.storybookStaticDir)) {\n log.process(\"info\", \"general\", \"Uploading Storybook archive...\");\n\n // Create tar.gz of the storybook-static directory\n const archive = execSync(`tar -czf - -C ${JSON.stringify(config.storybookStaticDir)} .`, {\n maxBuffer: 50 * 1024 * 1024,\n }).toString(\"base64\");\n\n const result = await uploadStorybookArchive(\n config,\n apiToken,\n config.thirdEyeProjectId,\n buildId,\n archive,\n );\n\n log.process(\n \"info\",\n \"general\",\n `Storybook uploaded (${result.fileCount} files, ${Math.round(result.totalSizeBytes / 1024)} KB)`,\n );\n log.process(\"info\", \"general\", `Storybook URL: ${result.url}`);\n }\n }\n\n const executionStop = process.hrtime(executionStart);\n\n log.process(\n \"info\",\n \"general\",\n `⏱ Lost Pixel run took ${parseHrtimeToSeconds(executionStop)} seconds`,\n );\n } catch (error: unknown) {\n if (error instanceof Error) {\n log.process(\"error\", \"general\", error.message);\n } else {\n log.process(\"error\", \"general\", error);\n }\n\n log.process(\"info\", \"general\", \"🪵 Sending logs to platform.\");\n\n await sendRecordLogsToAPI(config, apiToken);\n\n process.exit(1);\n }\n};\n","import { execa } from \"execa\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { isLocalDebugMode, isUpdateMode, shallGenerateMeta } from \"../utils.js\";\n\ntype ParsedYargs = {\n configDir: \"string\";\n};\n\nexport const executeDockerRun = async ({ version }: { version: string }) => {\n const isUpdateModeEnabled = isUpdateMode();\n const isGenerateMetaEnabled = shallGenerateMeta();\n const isLocalDebugModeEnabled = isLocalDebugMode();\n\n const argv = yargs(hideBin(process.argv)).parseSync() as unknown as ParsedYargs;\n\n const args = [\n \"run\",\n \"--rm\",\n // TODO: remove interactive mode for now, while it clashes with Tauri execution\n // '-it',\n `-v ${process.cwd()}:${process.cwd()}`,\n `-e WORKSPACE=${process.cwd()}`,\n \"-e DOCKER=1\",\n `-e THIRD_EYE_DISABLE_TELEMETRY=${process.env.THIRD_EYE_DISABLE_TELEMETRY}`,\n argv.configDir ? `-e THIRD_EYE_CONFIG_DIR=${argv.configDir}` : \"\",\n isUpdateModeEnabled ? \"-e THIRD_EYE_MODE=update\" : \"\",\n isGenerateMetaEnabled ? \"-e THIRD_EYE_GENERATE_META=true\" : \"\",\n isLocalDebugModeEnabled ? \"-e THIRD_EYE_LOCAL=true\" : \"\",\n `thirdeye/third-eye:v${version}`,\n ];\n\n return execa(\"docker\", args, { shell: true, stdio: \"inherit\" });\n};\n","import { log } from \"../log.js\";\nimport { getVersion } from \"../utils.js\";\nimport { executeDockerRun } from \"./utils.js\";\n\nexport const runInDocker = async () => {\n const version = getVersion();\n\n if (version) {\n log.process(\"info\", \"general\", `Running in docker: third-eye:${version}`);\n\n try {\n await executeDockerRun({ version });\n } catch (error: unknown) {\n log.process(\"error\", \"general\", error);\n }\n } else {\n log.process(\n \"error\",\n \"config\",\n \"Seems like third-eye is missing in your package.json. Running third-eye@latest\",\n );\n }\n};\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport axios from \"axios\";\nimport { XMLParser } from \"fast-xml-parser\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { log } from \"./log.js\";\nimport { type PageScreenshotParameter, PageScreenshotParameterSchema } from \"./config.js\";\n\ntype SitemapParserOptions = {\n outputPath: string;\n};\n\ntype SitemapUrlEntry = {\n loc: string[];\n lastmod: string[];\n};\n\ntype Sitemap = {\n urlset: {\n $: {\n xmlns: string;\n };\n url: SitemapUrlEntry[];\n };\n};\n\nasync function fetchSitemap(url: string): Promise<string> {\n if (url.startsWith(\"http\")) {\n const response = await axios.get(url);\n\n return response.data as string;\n }\n\n return readFileSync(url, \"utf8\");\n}\n\nasync function parseSitemap(sitemapContent: string): Promise<string[]> {\n const parser = new XMLParser({\n isArray: (_tagName, jPath) =>\n typeof jPath === \"string\" &&\n (jPath === \"urlset.url\" || jPath === \"urlset.url.loc\" || jPath === \"urlset.url.lastmod\"),\n });\n const result: Sitemap = parser.parse(sitemapContent) as Sitemap;\n\n if (!result.urlset || !Array.isArray(result.urlset.url)) {\n throw new Error(\"Invalid sitemap format\");\n }\n\n return result.urlset.url\n .filter((urlEntry: SitemapUrlEntry) => urlEntry.loc && urlEntry.loc.length > 0)\n .map((urlEntry: SitemapUrlEntry) => urlEntry.loc[0]);\n}\n\nasync function generatePagesFileFromSitemap(\n url: string,\n options: SitemapParserOptions,\n): Promise<void> {\n try {\n const sitemapContent = await fetchSitemap(url);\n const urls = await parseSitemap(sitemapContent);\n\n const pages: PageScreenshotParameter[] = urls.map((url) => {\n const page: PageScreenshotParameter = PageScreenshotParameterSchema.parse({\n path: new URL(url).pathname, // Extract the path from the URL\n name: url.replace(/^https?:\\/\\/(www\\.)?|^www\\./g, \"\").replace(/\\//g, \"_\"),\n });\n\n return page;\n });\n\n writeFileSync(options.outputPath, JSON.stringify(pages, null, 2));\n log.process(\"info\", \"general\", \"✅ Pages file generated successfully at\", options.outputPath);\n } catch (error) {\n log.process(\n \"error\",\n \"general\",\n \"❌ Pages file generation errored out. Please check the error message below\",\n error,\n );\n }\n}\n\nexport const generatePagesFromSitemap = async () => {\n const argv = await yargs(hideBin(process.argv))\n .usage(\"Usage: $0 <command> <sitemapUrl> <outputPath>\")\n .command(\"page-sitemap-gen <sitemapUrl> <outputPath>\", \"Generate pages file from sitemap\")\n .demandCommand(1).argv;\n\n const { sitemapUrl, outputPath } = argv;\n\n if (\n !sitemapUrl ||\n typeof sitemapUrl !== \"string\" ||\n !outputPath ||\n typeof outputPath !== \"string\"\n ) {\n log.process(\"error\", \"general\", \"❌ sitemapUrl and outputPath are required\");\n\n return;\n }\n\n log.process(\n \"info\",\n \"general\",\n `🧬 Running third-eye in sitemap-page-gen mode. Pages file will be generated from provided sitemap on ${sitemapUrl}`,\n );\n await generatePagesFileFromSitemap(sitemapUrl, {\n outputPath,\n });\n};\n","#!/usr/bin/env node\n\nimport path from \"node:path\";\nimport { readFileSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport fs from \"fs-extra\";\nimport { log } from \"./log.js\";\nimport { getPlatformApiToken, platformRunner, runner } from \"./runner.js\";\nimport { getVersion, isDockerMode, isSitemapPageGenMode, isLocalDebugMode } from \"./utils.js\";\nimport { sendFinalizeToAPI } from \"./api.js\";\nimport { config, configure, isPlatformModeConfig } from \"./config.js\";\nimport { runInDocker } from \"./docker-runner/index.js\";\nimport { generatePagesFromSitemap } from \"./generatePagesFromSitemap.js\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\ntype CommandArgs = [\"docker\", \"init-js\", \"init-ts\", \"finalize\"];\n\nconst args = yargs(hideBin(process.argv)).parseSync();\nconst commandArgs = args._ as CommandArgs;\n\nconst version = getVersion();\n\nif (version) {\n log.process(\"info\", \"general\", `Version: ${version}`);\n}\n\n(async () => {\n if (isSitemapPageGenMode()) {\n await generatePagesFromSitemap();\n\n return;\n }\n\n if (isDockerMode()) {\n await runInDocker();\n } else if (commandArgs.includes(\"init-js\")) {\n log.process(\"info\", \"general\", \"Initializing javascript third-eye config\");\n\n await fs.copy(\n path.join(__dirname, \"..\", \"config-templates\", \"example.thirdeye.config.js\"),\n path.join(process.cwd(), \"./thirdeye.config.js\"),\n );\n log.process(\"info\", \"general\", \"✅ Config successfully initialized\");\n } else if (commandArgs.includes(\"init-ts\")) {\n log.process(\"info\", \"general\", \"Initializing typescript third-eye config\");\n\n // Replace local type resolution with module resolution\n const file = fs.readFileSync(\n path.join(__dirname, \"..\", \"config-templates\", \"example.thirdeye.config.ts\"),\n );\n const modifiedFile = file.toString().replace(\"../src/config\", \"third-eye\");\n\n fs.writeFileSync(path.join(process.cwd(), \"./thirdeye.config.ts\"), modifiedFile);\n log.process(\"info\", \"general\", \"✅ Config successfully initialized\");\n } else {\n // Auto-detect PR number from GitHub Actions event payload\n if (process.env.GITHUB_EVENT_PATH && !process.env.THIRD_EYE_PR_NUMBER) {\n try {\n const event = JSON.parse(readFileSync(process.env.GITHUB_EVENT_PATH, \"utf-8\"));\n const prNumber = event.pull_request?.number ?? event.number;\n if (prNumber) {\n process.env.THIRD_EYE_PR_NUMBER = String(prNumber);\n }\n } catch {\n // ignore - event file might not exist or be malformed\n }\n }\n\n await configure({\n localDebugMode: isLocalDebugMode(),\n });\n\n if (isPlatformModeConfig(config)) {\n log.process(\"info\", \"general\", `🚀 Starting Lost Pixel in 'platform' mode`);\n\n const apiToken = await getPlatformApiToken(config);\n\n if (commandArgs.includes(\"finalize\")) {\n await sendFinalizeToAPI(config, apiToken);\n } else {\n await platformRunner(config, apiToken);\n }\n } else {\n log.process(\"info\", \"general\", `🚀 Starting Lost Pixel in 'generateOnly' mode`);\n\n await runner(config);\n }\n }\n})();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bin.mjs","names":["z","z","odiffCompare","kebabCase","generateBrowserConfig","fse"],"sources":["../src/log.ts","../src/configHelper.ts","../src/schemas.ts","../src/config.ts","../src/constants.ts","../src/utils.ts","../src/compare/utils.ts","../src/compare/compare.ts","../src/checkDifferences.ts","../src/shots/utils.ts","../src/crawler/ladleScreenshots.ts","../src/crawler/storybook.ts","../src/crawler/pageScreenshots.ts","../src/shots/shots.ts","../src/crawler/utils.ts","../src/crawler/histoireScreenshots.ts","../src/createShots.ts","../src/git.ts","../../shared/dist/client.js","../src/api.ts","../src/upload.ts","../src/turbosnap.ts","../src/runner.ts","../src/docker-runner/utils.ts","../src/docker-runner/index.ts","../src/generatePagesFromSitemap.ts","../src/bin.ts"],"sourcesContent":["import type { ShotMode } from \"./types.js\";\n\ntype LogLevel = \"info\" | \"error\" | \"warn\" | \"debug\";\n\ntype LogEntry = {\n timestamp: Date;\n level: LogLevel;\n item?: {\n shotMode: ShotMode;\n uniqueItemId: string;\n itemIndex: number;\n totalItems: number;\n };\n source: \"process\" | \"browser\";\n context: \"general\" | \"api\" | \"console\" | \"network\" | \"timeout\" | \"config\";\n content: unknown[];\n};\n\nexport type LogMemory = LogEntry[];\n\nexport const logMemory: LogMemory = [];\n\nconst serializeError = (error: Error) => ({\n message: error.message,\n name: error.name,\n stack: error.stack,\n});\n\nconst serializeErrors = (content: unknown[]) =>\n content.map((item) => {\n if (item instanceof Error) {\n return serializeError(item);\n }\n\n return item;\n });\n\nconst renderLog = (entry: LogEntry) => {\n if (entry.source === \"browser\" && entry.context === \"console\") {\n return;\n }\n\n if (entry.source === \"browser\" && entry.context === \"network\") {\n return;\n }\n\n const { log } = console;\n const logPrefix = [];\n\n if (entry.item) {\n logPrefix.push(`[${entry.item.itemIndex + 1}/${entry.item.totalItems}]`);\n }\n\n if (![\"general\", \"api\", \"config\"].includes(entry.context)) {\n logPrefix.push(`[${entry.context}]`);\n }\n\n if (entry.level === \"error\") {\n logPrefix.push(`❌`);\n }\n\n if (entry.item?.uniqueItemId) {\n log(...logPrefix, ...entry.content, `(${entry.item?.uniqueItemId})`);\n } else {\n log(...logPrefix, ...entry.content);\n }\n};\n\nexport const log = {\n item: (item: LogEntry[\"item\"]) => ({\n process(level: LogEntry[\"level\"], context: LogEntry[\"context\"], ...content: unknown[]) {\n const entry: LogEntry = {\n timestamp: new Date(),\n level,\n item,\n source: \"process\",\n context,\n content,\n };\n\n renderLog(entry);\n logMemory.push({\n ...entry,\n content: serializeErrors(content),\n });\n },\n browser(level: LogEntry[\"level\"], context: LogEntry[\"context\"], ...content: unknown[]) {\n const entry: LogEntry = {\n timestamp: new Date(),\n level,\n item,\n source: \"browser\",\n context,\n content,\n };\n\n renderLog(entry);\n logMemory.push({\n ...entry,\n content: serializeErrors(content),\n });\n },\n }),\n process(level: LogEntry[\"level\"], context: LogEntry[\"context\"], ...content: unknown[]) {\n const entry: LogEntry = {\n timestamp: new Date(),\n level,\n source: \"process\",\n context,\n content,\n };\n\n renderLog(entry);\n logMemory.push({\n ...entry,\n content: serializeErrors(content),\n });\n },\n browser(level: LogEntry[\"level\"], context: LogEntry[\"context\"], ...content: unknown[]) {\n const entry: LogEntry = {\n timestamp: new Date(),\n level,\n source: \"browser\",\n context,\n content,\n };\n\n renderLog(entry);\n logMemory.push({\n ...entry,\n content: serializeErrors(content),\n });\n },\n};\n","import { bundleRequire } from \"bundle-require\";\nimport { log } from \"./log.js\";\n\nexport const loadProjectConfigFile = async (configFilepath: string): Promise<unknown> => {\n try {\n const { mod } = await bundleRequire<{\n default?: unknown;\n config?: unknown;\n }>({\n filepath: configFilepath,\n esbuildOptions: {\n // logLevel: 'silent',\n },\n });\n\n return mod?.default ?? mod?.config ?? mod;\n } catch (error: unknown) {\n log.process(\"error\", \"config\", error);\n throw error;\n }\n};\n\nexport const loadTSProjectConfigFile = async (configFilepath: string): Promise<unknown> => {\n try {\n const imported = (await import(configFilepath)) as Record<string, unknown>;\n\n return imported?.default ?? imported?.config;\n } catch (error: unknown) {\n // @ts-expect-error Error type definition is missing 'code'\n if ([\"ERR_MODULE_NOT_FOUND\", \"MODULE_NOT_FOUND\"].includes(error.code)) {\n log.process(\"error\", \"config\", \"Failed to load TypeScript configuration file\");\n process.exit(1);\n }\n\n log.process(\"error\", \"config\", error);\n process.exit(1);\n }\n};\n","import * as z from \"zod\";\nimport type { BrowserContextOptions } from \"playwright-core\";\n\nexport const BrowserSchema = z.enum([\"chromium\", \"firefox\", \"webkit\"]);\n\nexport const ShotModeSchema = z.enum([\"storybook\", \"ladle\", \"histoire\", \"page\", \"custom\"]);\n\nexport const MaskSchema = z.object({\n /**\n * CSS selector for the element to mask\n * Examples:\n * - `#my-id`: Selects the element with the id `my-id`\n * - `.my-class`: Selects all elements with the class `my-class`\n * - `div`: Selects all `div` elements\n * - `div.my-class`: Selects all `div` elements with the class `my-class`\n * - `li:nth-child(2n)`: Selects all even `li` elements\n * - `[data-testid=\"hero-banner\"]`: Selects all elements with the attribute `data-testid` set to `hero-banner`\n * - `div > p`: Selects all `p` elements that are direct children of a `div` element\n */\n selector: z.string(),\n});\n\nexport const ShotItemSchema = z.object({\n shotMode: ShotModeSchema,\n id: z.string(),\n shotName: z.string(),\n url: z.string(),\n filePathBaseline: z.string(),\n filePathCurrent: z.string(),\n filePathDifference: z.string(),\n browserConfig: z.custom<BrowserContextOptions>().optional(),\n threshold: z.number(),\n waitBeforeScreenshot: z.number().optional(),\n importPath: z.string().optional(),\n mask: z.array(MaskSchema).optional(),\n viewport: z\n .object({\n width: z.number(),\n height: z.number().optional(),\n })\n .optional(),\n breakpoint: z.number().optional(),\n breakpointGroup: z.string().optional(),\n elementLocator: z.string().optional(),\n waitForSelector: z.string().optional(),\n // Story metadata for platform integration\n componentPath: z.string().optional(),\n storyName: z.string().optional(),\n /** Original Storybook story ID (e.g. \"pages-projectsettings--default\") */\n storyId: z.string().optional(),\n storyArgs: z.record(z.string(), z.unknown()).optional(),\n tags: z.array(z.string()).optional(),\n});\n","import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { type LaunchOptions, type BrowserContextOptions, type Page } from \"playwright-core\";\nimport * as z from \"zod\";\nimport { loadProjectConfigFile, loadTSProjectConfigFile } from \"./configHelper.js\";\nimport { log } from \"./log.js\";\nimport { BrowserSchema, MaskSchema, ShotModeSchema } from \"./schemas.js\";\n\nexport const PageScreenshotParameterSchema = z.object({\n /**\n * Path to the page to take a screenshot of (e.g. /login)\n */\n path: z.string(),\n\n /**\n * Unique name for the page\n */\n name: z.string(),\n\n /**\n * Time to wait before taking a screenshot\n * @default 1_000\n */\n waitBeforeScreenshot: z.number().default(1000),\n\n /**\n * Threshold for the difference between the baseline and current image\n *\n * Values between 0 and 1 are interpreted as percentage of the image size\n *\n * Values greater or equal to 1 are interpreted as pixel count.\n * @default 0\n */\n threshold: z.number().default(0),\n\n /**\n * Define custom breakpoints for the page as width in pixels\n * @default []\n * @example\n * [ 320, 768, 1280 ]\n */\n breakpoints: z.array(z.number()).optional(),\n\n /**\n * Define a custom viewport for the page\n * @default { width: 1280, height: 720 }\n */\n viewport: z\n .object({\n width: z.number().optional(),\n height: z.number().optional(),\n })\n .optional(),\n\n /**\n * Define areas for the page where differences will be ignored\n */\n mask: z.array(MaskSchema).optional(),\n});\n\nexport type PageScreenshotParameter = z.infer<typeof PageScreenshotParameterSchema>;\n\nconst StorybookShotsSchema = z.object({\n /**\n * URL of the Storybook instance or local folder\n * @default 'storybook-static'\n */\n storybookUrl: z.string(),\n\n /**\n * Define areas for all stories where differences will be ignored\n */\n mask: z.array(MaskSchema).optional(),\n\n /**\n * Define custom breakpoints for all Storybook shots as width in pixels\n * @default []\n * @example\n * [ 320, 768, 1280 ]\n */\n breakpoints: z.array(z.number()).optional(),\n\n /**\n * Target specific element on page with a selector\n */\n elementLocator: z.string().optional(),\n\n /**\n * Wait for a specific selector before taking a screenshot\n * @example '[data-storyloaded]'\n */\n waitForSelector: z.string().optional(),\n});\n\nconst LadleShotsSchema = z.object({\n /**\n * URL of the Ladle served instance\n * @default 'http://localhost:61000'\n */\n ladleUrl: z.string(),\n\n /**\n * Define areas for all stories where differences will be ignored\n */\n mask: z.array(MaskSchema).optional(),\n\n /**\n * Define custom breakpoints for all Ladle shots as width in pixels\n * @default []\n * @example\n * [ 320, 768, 1280 ]\n */\n breakpoints: z.array(z.number()).optional(),\n\n /**\n * Wait for a specific selector before taking a screenshot\n * @example '[data-storyloaded]'\n */\n waitForSelector: z.string().optional(),\n});\n\nconst HistoireShotsSchema = z.object({\n /**\n * URL of the Histoire served instance\n * @default 'http://localhost:61000'\n */\n histoireUrl: z.string(),\n\n /**\n * Define areas for all stories where differences will be ignored\n */\n mask: z.array(MaskSchema).optional(),\n\n /**\n * Define custom breakpoints for all Histoire shots as width in pixels\n * @default []\n * @example\n * [ 320, 768, 1280 ]\n */\n breakpoints: z.array(z.number()).optional(),\n\n /**\n * Wait for a specific selector before taking a screenshot\n * @example '[data-storyloaded]'\n */\n waitForSelector: z.string().optional(),\n});\n\nconst PageShotsSchema = z.object({\n /**\n * Paths to take screenshots of\n */\n pages: z.array(PageScreenshotParameterSchema),\n\n /**\n * Url that must return a JSON compatible with `PageScreenshotParameter[]`. It is useful when you want to autogenerate the pages that you want to run third-eye on. Can be used together with `pages` as both are composed into a single run.\n */\n pagesJsonUrl: z.string().optional(),\n\n /**\n * Url that must return a JSON compatible with `PageScreenshotParameter[]`. It is useful when you want to autogenerate the pages that you want to run third-eye on. Can be used together with `pages` as both are composed into a single run.\n */\n pagesJsonRefiner: z\n .custom<(pages: PageScreenshotParameter[]) => PageScreenshotParameter[]>()\n .optional(),\n\n /**\n * Base URL of the running application (e.g. http://localhost:3000)\n */\n baseUrl: z.string(),\n\n /**\n * Define areas for all pages where differences will be ignored\n */\n mask: z.array(MaskSchema).optional(),\n\n /**\n * Define custom breakpoints for all page shots as width in pixels\n * @default []\n * @example\n * [ 320, 768, 1280 ]\n */\n breakpoints: z.array(z.number()).optional(),\n\n /**\n * Wait for a specific selector before taking a screenshot\n * @example '[data-storyloaded]'\n */\n waitForSelector: z.string().optional(),\n});\n\nconst CustomShotsSchema = z.object({\n /**\n * Path to current shots folder\n *\n * This path cannot be the same as the `imagePathCurrent` path\n */\n currentShotsPath: z.string(),\n});\n\nconst StoryLikeSchema = z.object({\n shotMode: ShotModeSchema,\n id: z.string().optional(),\n kind: z.string().optional(),\n story: z.string().optional(),\n shotName: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n filePathBaseline: z.string().optional(),\n filePathCurrent: z.string().optional(),\n filePathDifference: z.string().optional(),\n});\n\ntype StoryLike = z.infer<typeof StoryLikeSchema>;\n\nconst ShotItem = z.object({\n shotMode: ShotModeSchema,\n id: z.string(),\n shotName: z.string(),\n url: z.string(),\n filePathBaseline: z.string(),\n filePathCurrent: z.string(),\n filePathDifference: z.string(),\n browserConfig: z.custom<BrowserContextOptions>().optional(),\n threshold: z.number(),\n waitBeforeScreenshot: z.number().optional(),\n importPath: z.string().optional(),\n mask: z.array(MaskSchema).optional(),\n viewport: z\n .object({\n width: z.number(),\n height: z.number().optional(),\n })\n .optional(),\n breakpoint: z.number().optional(),\n breakpointGroup: z.string().optional(),\n elementLocator: z.string().optional(),\n waitForSelector: z.string().optional(),\n});\n\nconst TimeoutsSchema = z.object({\n /**\n * Timeout for fetching stories\n * @default 30_000\n */\n fetchStories: z.number().default(30_000),\n\n /**\n * Timeout for loading the state of the page\n * @default 30_000\n */\n loadState: z.number().default(30_000),\n\n /**\n * Timeout for waiting for network requests to finish\n * @default 30_000\n */\n networkRequests: z.number().default(30_000),\n});\n\nconst BaseConfigSchema = z.object({\n /**\n * Browser to use: chromium, firefox, or webkit\n * @default 'chromium'\n */\n browser: z\n .union([BrowserSchema, z.array(BrowserSchema).default([\"chromium\"])])\n .default(\"chromium\"),\n\n /**\n * Enable Storybook mode\n */\n storybookShots: StorybookShotsSchema.optional(),\n\n /**\n * Enable Ladle mode\n */\n ladleShots: LadleShotsSchema.optional(),\n\n /**\n * Enable Histoire mode\n */\n histoireShots: HistoireShotsSchema.optional(),\n\n /**\n * Enable Page mode\n */\n pageShots: PageShotsSchema.optional(),\n\n /**\n * Enable Custom mode\n */\n customShots: CustomShotsSchema.optional(),\n\n /**\n * Path to the current image folder\n * @default '.thirdeye/current/'\n */\n imagePathCurrent: z.string().default(\".thirdeye/current/\"),\n\n /**\n * Define custom breakpoints for all tests as width in pixels\n * @default []\n * @example\n * [ 320, 768, 1280 ]\n */\n breakpoints: z.array(z.number()).optional(),\n\n /**\n * Number of concurrent shots to take\n * @default 5\n */\n shotConcurrency: z.number().default(5),\n\n /**\n * Timeouts for various stages of the test\n */\n timeouts: TimeoutsSchema.default({\n fetchStories: 30_000,\n loadState: 30_000,\n networkRequests: 30_000,\n }),\n\n /**\n * Time to wait before taking a screenshot\n * @default 1_000\n */\n waitBeforeScreenshot: z.number().default(1000),\n\n /**\n * Time to wait for the first network request to start\n * @default 1_000\n */\n waitForFirstRequest: z.number().default(1000),\n\n /**\n * Time to wait for the last network request to start\n * @default 1_000\n */\n waitForLastRequest: z.number().default(1000),\n\n /**\n * Threshold for the difference between the baseline and current image\n *\n * Values between 0 and 1 are interpreted as percentage of the image size\n *\n * Values greater or equal to 1 are interpreted as pixel count.\n * @default 0\n */\n threshold: z.number().default(0),\n\n /**\n * Enable TurboSnap: only re-capture stories whose source files changed\n * since the last build on the base branch. Requires platform mode.\n * @default false\n */\n turboSnap: z.boolean().optional().default(false),\n\n /**\n * How often to retry a shot for a stable result\n * @default 0\n */\n flakynessRetries: z.number().default(0),\n\n /**\n * Time to wait between flakyness retries\n * @default 2_000\n */\n waitBetweenFlakynessRetries: z.number().default(2000),\n\n /**\n * Global shot filter\n */\n filterShot: z.custom<(story: StoryLike) => boolean>().optional(),\n\n /**\n * Shot and file name generator for images\n */\n shotNameGenerator: z.custom<(story: StoryLike) => string>().optional(),\n\n /**\n * Configure browser context options\n */\n configureBrowser: z.custom<(story: StoryLike) => BrowserContextOptions>().optional(),\n\n /**\n * Configure page before screenshot\n */\n beforeScreenshot: z.custom<(page: Page, story: StoryLike) => Promise<void>>().optional(),\n\n /**\n * Perform actions after screenshot was taken\n */\n afterScreenshot: z.custom<(page: Page, story: StoryLike) => Promise<void>>().optional(),\n\n /**\n * Launch options for the browser\n */\n browserLaunchOptions: z\n .object({\n chromium: z.custom<LaunchOptions>().optional(),\n firefox: z.custom<LaunchOptions>().optional(),\n webkit: z.custom<LaunchOptions>().optional(),\n })\n .optional(),\n});\n\nexport const PlatformModeConfigSchema = BaseConfigSchema.extend({\n /**\n * URL of the Third Eye server API endpoint\n * @default 'https://third-eye.gfxlabs.cloud'\n */\n thirdEyePlatform: z.string().default(\"https://third-eye.gfxlabs.cloud\"),\n\n /**\n * API key for the Third Eye platform\n */\n apiKey: z.string(),\n\n /**\n * Project ID\n */\n thirdEyeProjectId: z\n .string({\n error: \"Required\",\n })\n // @ts-expect-error If not set, it will be caught during config validation\n .default(process.env.THIRD_EYE_PROJECT_ID),\n\n /**\n * Organization ID for the Lost Pixel platform\n */\n thirdEyeOrgId: z\n .string({\n error: \"Required\",\n })\n // @ts-expect-error If not set, it will be caught during config validation\n .default(process.env.THIRD_EYE_ORG_ID),\n\n /**\n * CI build ID\n */\n ciBuildId: z\n .string({\n error: \"Required (can be set via `CI_BUILD_ID` env variable)\",\n })\n // @ts-expect-error If not set, it will be caught during config validation\n .default(process.env.CI_BUILD_ID),\n\n /**\n * CI build number\n */\n ciBuildNumber: z\n .string({\n error: \"Required (can be set via `CI_BUILD_NUMBER` env variable)\",\n })\n // @ts-expect-error If not set, it will be caught during config validation\n .default(process.env.CI_BUILD_NUMBER),\n\n /**\n * Git repository name (e.g. 'third-eye/third-eye-storybook')\n */\n repository: z\n .string({\n error: \"Required (can be set via `REPOSITORY` env variable)\",\n })\n // @ts-expect-error If not set, it will be caught during config validation\n .default(process.env.REPOSITORY ?? process.env.GITHUB_REPOSITORY),\n\n /**\n * Git branch name (e.g. 'main')\n */\n commitRefName: z\n .string({\n error: \"Required (can be set via `COMMIT_REF_NAME` env variable)\",\n })\n .default(\n // @ts-expect-error If not set, it will be caught during config validation\n process.env.COMMIT_REF_NAME ?? process.env.GITHUB_HEAD_REF ?? process.env.GITHUB_REF_NAME,\n ),\n\n /**\n * Git commit SHA (e.g. 'b9b8b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9')\n */\n commitHash: z\n .string({\n error: \"Required (can be set via `COMMIT_HASH` env variable)\",\n })\n // @ts-expect-error If not set, it will be caught during config validation\n .default(process.env.COMMIT_HASH ?? process.env.GITHUB_SHA),\n\n /**\n * Base branch (the target branch of a PR, e.g. 'main')\n * Auto-detected in GitHub Actions from GITHUB_BASE_REF\n */\n baseBranch: z\n .string()\n .optional()\n .default(process.env.GITHUB_BASE_REF ?? \"\"),\n\n /**\n * Pull request number\n * Auto-detected in GitHub Actions from the event payload\n */\n prNumber: z.coerce\n .number()\n .optional()\n .default(\n // @ts-expect-error If not set, it will be caught during config validation\n process.env.THIRD_EYE_PR_NUMBER ? Number(process.env.THIRD_EYE_PR_NUMBER) : undefined,\n ),\n\n /**\n * Whether to set the GitHub status check on process start or not\n *\n * Setting this option to `true` makes only sense if the repository settings have pending status checks disabled\n * @default false\n */\n setPendingStatusCheck: z.boolean().default(false),\n\n /**\n * Path to the storybook-static directory to upload for hosted storybook.\n * When set, the CLI will upload the built storybook to the platform.\n */\n storybookStaticDir: z.string().optional(),\n});\n\nexport const GenerateOnlyModeConfigSchema = BaseConfigSchema.extend({\n /**\n * Run in local mode\n * @deprecated Defaults to running in generateOnly mode\n */\n generateOnly: z.boolean().optional(),\n\n /**\n * Flag that decides if process should exit if a difference is found\n */\n failOnDifference: z.boolean().optional(),\n\n /**\n * Path to the baseline image folder\n * @default '.thirdeye/baseline/'\n */\n imagePathBaseline: z.string().default(\".thirdeye/baseline/\"),\n\n /**\n * Path to the difference image folder\n * @default '.thirdeye/difference/'\n */\n imagePathDifference: z.string().default(\".thirdeye/difference/\"),\n\n /**\n * Number of concurrent screenshots to compare\n * @default 10\n */\n compareConcurrency: z.number().default(10),\n\n /**\n * Which comparison engine to use for diffing images\n * @default 'pixelmatch'\n */\n compareEngine: z.enum([\"pixelmatch\", \"odiff\"]).default(\"pixelmatch\"),\n\n /**\n * Filter stories to take screenshots of and run comparisons on (existing shots remain untouched)\n */\n filterItemsToCheck: z.custom<(item: z.infer<typeof ShotItem>) => boolean>().optional(),\n});\n\nexport const ConfigSchema = z.union([PlatformModeConfigSchema, GenerateOnlyModeConfigSchema]);\n\n// use partial() specifically for the inferred type\nexport const FlexibleConfigSchema = z.union([\n PlatformModeConfigSchema.extend({\n timeouts: TimeoutsSchema.partial(),\n pageShots: PageShotsSchema.extend({\n pages: z.array(PageScreenshotParameterSchema.partial()),\n }),\n }).partial(),\n GenerateOnlyModeConfigSchema.extend({\n timeouts: TimeoutsSchema.partial(),\n pageShots: PageShotsSchema.extend({\n pages: z.array(PageScreenshotParameterSchema.partial()),\n }),\n }).partial(),\n]);\n\nexport type PlatformModeConfig = z.infer<typeof PlatformModeConfigSchema>;\nexport type GenerateOnlyModeConfig = z.infer<typeof GenerateOnlyModeConfigSchema>;\n\nexport type Config = z.infer<typeof ConfigSchema>;\nexport type CustomProjectConfig = z.infer<typeof FlexibleConfigSchema>;\n\nexport let config: Config;\n\nexport const isPlatformModeConfig = (\n userConfig: PlatformModeConfig | GenerateOnlyModeConfig,\n): userConfig is PlatformModeConfig =>\n (\"apiKey\" in userConfig && typeof userConfig.apiKey === \"string\") ||\n (\"thirdEyeProjectId\" in userConfig && typeof userConfig.thirdEyeProjectId === \"string\") ||\n (\"thirdEyeOrgId\" in userConfig && typeof userConfig.thirdEyeOrgId === \"string\");\n\nconst printConfigErrors = (error: z.ZodError) => {\n for (const issue of error.issues) {\n log.process(\n \"error\",\n \"config\",\n [\n \"Configuration error:\",\n ` - Path: ${issue.path.join(\".\")}`,\n ` - Message: ${issue.message}`,\n ].join(\"\\n\"),\n );\n }\n};\n\nexport const parseConfig = (userConfig: Config) => {\n if (isPlatformModeConfig(userConfig)) {\n const platformCheck = PlatformModeConfigSchema.safeParse(userConfig);\n\n if (platformCheck.success) {\n return platformCheck.data;\n }\n\n printConfigErrors(platformCheck.error);\n } else {\n const generateOnlyCheck = GenerateOnlyModeConfigSchema.safeParse(userConfig);\n\n if (generateOnlyCheck.success) {\n return generateOnlyCheck.data;\n }\n\n printConfigErrors(generateOnlyCheck.error);\n }\n\n throw new Error(\"Configuration error\");\n};\n\nconst configDirBase = process.env.THIRD_EYE_CONFIG_DIR ?? process.cwd();\n\nconst configFileNameBase = path.join(\n path.isAbsolute(configDirBase) ? \"\" : process.cwd(),\n configDirBase,\n \"thirdeye.config\",\n);\n\nconst loadProjectConfig = async (): Promise<Config> => {\n log.process(\"info\", \"config\", \"Loading project config ...\");\n log.process(\"info\", \"config\", \"Current working directory:\", process.cwd());\n\n if (process.env.THIRD_EYE_CONFIG_DIR) {\n log.process(\"info\", \"config\", \"Defined config directory:\", process.env.THIRD_EYE_CONFIG_DIR);\n }\n\n const configExtensions = [\"ts\", \"js\", \"cjs\", \"mjs\"];\n const configExtensionsString = configExtensions.join(\"|\");\n\n log.process(\n \"info\",\n \"config\",\n \"Looking for config file:\",\n `${configFileNameBase}.(${configExtensionsString})`,\n );\n\n const configFiles = configExtensions\n .map((ext) => `${configFileNameBase}.${ext}`)\n .filter((file) => existsSync(file));\n\n if (configFiles.length === 0) {\n log.process(\n \"error\",\n \"config\",\n `Couldn't find project config file 'thirdeye.config.(${configExtensionsString})'`,\n );\n process.exit(1);\n }\n\n if (configFiles.length > 1) {\n log.process(\"info\", \"config\", \"✅ Found multiple config files, taking:\", configFiles[0]);\n } else {\n log.process(\"info\", \"config\", \"✅ Found config file:\", configFiles[0]);\n }\n\n const configFile = configFiles[0];\n\n try {\n const imported = (await loadProjectConfigFile(configFile)) as Config;\n\n return imported;\n } catch {\n log.process(\"error\", \"config\", \"Loading config using ESBuild failed, using fallback option\");\n\n try {\n if (existsSync(`${configFileNameBase}.js`)) {\n const imported = (await import(`${configFileNameBase}.js`)) as Record<string, unknown>;\n const projectConfig = (imported?.default ?? imported?.config ?? imported) as Config;\n\n log.process(\n \"info\",\n \"config\",\n \"✅ Successfully loaded configuration from:\",\n `${configFileNameBase}.js`,\n );\n\n return projectConfig;\n }\n\n if (existsSync(`${configFileNameBase}.ts`)) {\n const imported = (await loadTSProjectConfigFile(configFile)) as Config;\n\n log.process(\n \"info\",\n \"config\",\n \"✅ Successfully loaded configuration from:\",\n `${configFileNameBase}.ts`,\n );\n\n return imported;\n }\n\n log.process(\"error\", \"config\", \"Couldn't find project config file 'thirdeye.config.js'\");\n process.exit(1);\n } catch (error) {\n log.process(\"error\", \"config\", `Failed to load config file: ${configFile}`);\n log.process(\"error\", \"config\", error);\n process.exit(1);\n }\n }\n};\n\nexport const configure = async ({\n customProjectConfig,\n localDebugMode,\n}: {\n customProjectConfig?: CustomProjectConfig;\n localDebugMode?: boolean;\n}) => {\n if (customProjectConfig) {\n config = parseConfig(customProjectConfig as Config);\n\n return;\n }\n\n let loadedProjectConfig = await loadProjectConfig();\n\n if (localDebugMode) {\n let localDebugConfig = loadedProjectConfig;\n\n if (isPlatformModeConfig(loadedProjectConfig)) {\n localDebugConfig = {\n ...loadedProjectConfig,\n generateOnly: true,\n // @ts-expect-error Force it into generateOnly mode by dropping the platform specific properties\n thirdEyeProjectId: undefined,\n // @ts-expect-error Force it into generateOnly mode by dropping the platform specific properties\n apiKey: undefined,\n };\n }\n\n const urlChunks = [\"http://\", \"https://\", \"127.0.0.1\"];\n\n if (\n localDebugConfig.pageShots?.baseUrl &&\n urlChunks.some((urlChunk) => localDebugConfig?.pageShots?.baseUrl.includes(urlChunk))\n ) {\n const url = new URL(localDebugConfig.pageShots.baseUrl);\n\n url.hostname = \"localhost\";\n localDebugConfig.pageShots.baseUrl = url.toString();\n }\n\n loadedProjectConfig = localDebugConfig;\n }\n\n // Default to Storybook mode if no mode is defined\n if (\n !loadedProjectConfig.storybookShots &&\n !loadedProjectConfig.pageShots &&\n !loadedProjectConfig.ladleShots &&\n !loadedProjectConfig.histoireShots &&\n !loadedProjectConfig.customShots\n ) {\n loadedProjectConfig.storybookShots = {\n storybookUrl: \"storybook-static\",\n };\n }\n\n config = parseConfig(loadedProjectConfig);\n};\n","export const notSupported = \"not supported\";\n\nexport const MEDIA_UPLOAD_CONCURRENCY = 10;\n\nexport const POST_HOG_API_KEY = \"phc_RDNnzvANh1mNm9JKogF9UunG3Ky02YCxWP9gXScKShk\";\n","import {\n existsSync,\n mkdirSync,\n readdirSync,\n unlinkSync,\n writeFileSync,\n readFileSync,\n} from \"node:fs\";\nimport * as crypto from \"node:crypto\";\nimport { normalize, join } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { PostHog } from \"posthog-node\";\nimport { type BrowserType, chromium, firefox, webkit } from \"playwright-core\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { config, isPlatformModeConfig } from \"./config.js\";\nimport { log } from \"./log.js\";\nimport type { BrowserName, ShotItem } from \"./types.js\";\nimport { POST_HOG_API_KEY, notSupported } from \"./constants.js\";\n\ntype ParsedYargs = {\n _: [\"update\", \"meta\", \"docker\", \"local\", \"page-sitemap-gen\"];\n m: \"update\";\n};\n\ntype CliMode = \"update\" | \"page-sitemap-gen\";\n\ntype FilenameWithPath = {\n name: string;\n path: string;\n};\n\ntype FilenameWithAllPaths = {\n name: string;\n path: string;\n pathCurrent?: string;\n};\n\ntype Files = {\n baseline: FilenameWithPath[];\n current: FilenameWithPath[];\n difference: FilenameWithPath[];\n};\n\nexport type Changes = {\n difference: FilenameWithAllPaths[];\n deletion: FilenameWithAllPaths[];\n addition: FilenameWithAllPaths[];\n};\n\nexport const isUpdateMode = (): boolean => {\n const args = yargs(hideBin(process.argv)).parseSync() as unknown as ParsedYargs;\n\n return (\n args._.includes(\"update\") ||\n args.m === \"update\" ||\n (process.env.THIRD_EYE_MODE as CliMode) === \"update\"\n );\n};\n\nexport const isSitemapPageGenMode = (): boolean => {\n const args = yargs(hideBin(process.argv)).parseSync() as unknown as ParsedYargs;\n\n return (\n args._.includes(\"page-sitemap-gen\") ||\n (process.env.THIRD_EYE_MODE as CliMode) === \"page-sitemap-gen\"\n );\n};\n\nexport const isDockerMode = (): boolean => {\n const args = yargs(hideBin(process.argv)).parseSync() as unknown as ParsedYargs;\n\n return args._.includes(\"docker\") || process.env.THIRD_EYE_DOCKER === \"true\";\n};\n\nexport const isLocalDebugMode = (): boolean => {\n const args = yargs(hideBin(process.argv)).parseSync() as unknown as ParsedYargs;\n\n return args._.includes(\"local\") || process.env.THIRD_EYE_LOCAL === \"true\";\n};\n\nexport const shallGenerateMeta = (): boolean => {\n const args = yargs(hideBin(process.argv)).parseSync() as unknown as ParsedYargs;\n\n return args._.includes(\"meta\") || process.env.THIRD_EYE_GENERATE_META === \"true\";\n};\n\nexport const getChanges = (files: Files): Changes => {\n return {\n difference: files.difference\n .map((file) => ({\n ...file,\n pathCurrent: files.current.find(({ name }) => name === file.name)?.path, // Keep track of custom shots path\n }))\n .sort((a, b) => a.name.localeCompare(b.name)),\n deletion: files.baseline\n .filter((file1) => !files.current.some((file2) => file1.name === file2.name))\n .sort((a, b) => a.name.localeCompare(b.name)),\n addition: files.current\n .filter((file1) => !files.baseline.some((file2) => file1.name === file2.name))\n .sort((a, b) => a.name.localeCompare(b.name)),\n };\n};\n\ntype ExtendFileName = {\n fileName: string;\n extension: \"after\" | \"before\" | \"difference\";\n};\n\nexport const extendFileName = ({ fileName, extension }: ExtendFileName) => {\n const parts = fileName.split(\".\").filter((part) => part !== \"\");\n const extensionIndex = parts.length - 1;\n\n if (parts.length === 1) {\n return `${extension}.${parts[0]}`;\n }\n\n if (parts.length === 0) {\n return extension;\n }\n\n parts[extensionIndex] = `${extension}.${parts[extensionIndex]}`;\n\n return parts.join(\".\");\n};\n\nexport const createShotsFolders = () => {\n const paths = isPlatformModeConfig(config)\n ? [config.imagePathCurrent]\n : [config.imagePathBaseline, config.imagePathCurrent, config.imagePathDifference];\n\n for (const path of paths) {\n if (!existsSync(path)) {\n mkdirSync(path, { recursive: true });\n }\n }\n\n const ignoreFile = normalize(join(config.imagePathCurrent, \"..\", \".gitignore\"));\n\n if (!existsSync(ignoreFile)) {\n writeFileSync(ignoreFile, \"current\\ndifference\\n\");\n }\n};\n\nexport const sleep = async (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\nexport const removeFilesInFolder = (path: string, excludePaths?: string[]) => {\n const files = readdirSync(path);\n\n const filesPathsIgnoringExclude = files\n .map((file) => join(path, file))\n .filter((filePath) => !excludePaths?.includes(filePath));\n\n log.process(\"info\", \"general\", `Removing ${filesPathsIgnoringExclude.length} files from ${path}`);\n\n for (const filePath of filesPathsIgnoringExclude) {\n unlinkSync(filePath);\n }\n};\n\nconst convertBrowser = (browserKey?: string) => {\n switch (browserKey) {\n case \"chromium\": {\n return chromium;\n }\n\n case \"firefox\": {\n return firefox;\n }\n\n case \"webkit\": {\n return webkit;\n }\n\n default: {\n return chromium;\n }\n }\n};\n\nexport const getBrowser = (): BrowserType => {\n if (Array.isArray(config.browser)) return convertBrowser(config.browser[0]);\n\n return convertBrowser(config.browser);\n};\n\nexport const getBrowsers = (): BrowserType[] => {\n if (!Array.isArray(config.browser) || config.browser.length === 0) return [getBrowser()];\n\n const browsers = config.browser.map((key) => convertBrowser(key));\n\n return [...new Set(browsers)];\n};\n\nexport const getVersion = (): string | void => {\n try {\n const packageJsonPath = new URL(\"../package.json\", import.meta.url);\n const packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as {\n version: string;\n };\n\n return packageJson.version;\n } catch {}\n};\n\nconst fileNameWithoutExtension = (fileName: string): string => {\n return fileName.split(\".\").slice(0, -1).join(\".\");\n};\n\nexport const readDirIntoShotItems = (path: string): ShotItem[] => {\n const files = readdirSync(path);\n\n return files\n .filter((name) => name.endsWith(\".png\"))\n .map((fileNameWithExt): ShotItem => {\n const fileName = fileNameWithoutExtension(fileNameWithExt);\n\n return {\n id: fileName,\n shotName: fileName,\n shotMode: \"custom\",\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: join(path, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : join(config.imagePathDifference, fileNameWithExt),\n url: fileName,\n // TODO: custom shots take thresholds only from config - not possible to source configs from individual story\n threshold: config.threshold,\n };\n });\n};\n\nconst sendTelemetryData = async (properties: {\n runDuration?: number;\n shotsNumber?: number;\n error?: unknown;\n}) => {\n const client = new PostHog(POST_HOG_API_KEY);\n const id: string = randomUUID();\n\n try {\n log.process(\"info\", \"general\", \"Sending anonymized telemetry data.\");\n\n const version = getVersion() as string;\n const modes = [];\n\n if (config.storybookShots) modes.push(\"storybook\");\n\n if (config.ladleShots) modes.push(\"ladle\");\n\n if (config.pageShots) modes.push(\"pages\");\n\n if (config.customShots) modes.push(\"custom\");\n\n if (properties.error) {\n client.capture({\n distinctId: id,\n event: \"third-eye-error\",\n properties: { ...properties },\n });\n } else {\n client.capture({\n distinctId: id,\n event: \"third-eye-run\",\n properties: { ...properties, version, modes },\n });\n }\n\n await client.shutdown();\n } catch (error: unknown) {\n log.process(\"error\", \"general\", \"Error when sending telemetry data\", error);\n }\n};\n\nexport const parseHrtimeToSeconds = (hrtime: [number, number]) => {\n const seconds = (hrtime[0] + hrtime[1] / 1e9).toFixed(3);\n\n return seconds;\n};\n\nexport const exitProcess = async (properties: {\n runDuration?: number;\n shotsNumber?: number;\n error?: unknown;\n exitCode?: 0 | 1;\n}) => {\n if (process.env.THIRD_EYE_DISABLE_TELEMETRY === \"1\") {\n process.exit(properties.exitCode ?? 1);\n } else {\n await sendTelemetryData(properties).finally(() => {\n process.exit(properties.exitCode ?? 1);\n });\n }\n};\n\nconst hashBuffer = (buffer: Uint8Array): string => {\n const hashSum = crypto.createHash(\"sha256\");\n\n hashSum.update(buffer);\n\n return hashSum.digest(\"hex\");\n};\n\nexport const hashFile = (filePath: string): string => {\n const file = readFileSync(filePath);\n\n return hashBuffer(file);\n};\n\nexport const featureNotSupported = (feature: string) => {\n log.process(\"error\", \"general\", `${feature} is not supported in this configuration mode`);\n\n process.exit(1);\n};\n\n// Chromium flags that make font/text rendering deterministic across machines.\n// Without these, subpixel hinting, LCD text, and platform-specific Skia opts\n// cause tiny pixel diffs in text between different environments (e.g. local vs CI).\nconst DETERMINISTIC_RENDERING_ARGS = [\n \"--font-render-hinting=none\",\n \"--disable-skia-runtime-opts\",\n \"--disable-font-subpixel-positioning\",\n \"--disable-lcd-text\",\n];\n\nexport const launchBrowser = async (_browser?: BrowserType) => {\n const browserType = _browser ?? getBrowser();\n const browserName = browserType.name() as BrowserName;\n const userOptions = config.browserLaunchOptions?.[browserName] ?? {};\n\n // Only inject deterministic rendering args for Chromium-based browsers\n if (browserName === \"chromium\") {\n const userArgs = userOptions.args ?? [];\n\n return browserType.launch({\n ...userOptions,\n args: [...DETERMINISTIC_RENDERING_ARGS, ...userArgs],\n });\n }\n\n return browserType.launch(userOptions);\n};\n","import { PNG } from \"pngjs\";\n\nexport const resizeImage = (originalImage: PNG, width: number, height: number) => {\n const newImage = new PNG({\n width,\n height,\n fill: true,\n inputHasAlpha: true,\n });\n\n for (let x = 0; x < width; x++) {\n for (let y = 0; y < height; y++) {\n const index = ((width * y + x) << 2) + 3;\n\n newImage.data[index] = 64;\n }\n }\n\n PNG.bitblt(originalImage, newImage, 0, 0, originalImage.width, originalImage.height, 0, 0);\n\n return newImage;\n};\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport pixelmatch from \"pixelmatch\";\nimport { compare as odiffCompare } from \"odiff-bin\";\nimport { PNG } from \"pngjs\";\nimport { config, isPlatformModeConfig } from \"../config.js\";\nimport { featureNotSupported } from \"../utils.js\";\nimport { resizeImage } from \"./utils.js\";\n\nexport const checkThreshold = (threshold: number, pixelsTotal: number, pixelDifference: number) => {\n // Treat theshold as percentage\n if (threshold < 1) {\n return pixelDifference <= pixelsTotal * threshold;\n }\n\n // Treat threshold as absolute value\n return pixelDifference <= threshold;\n};\n\nexport const compareImagesViaPixelmatch = async (\n threshold: number,\n baselineShotPath: string,\n currentShotPath: string,\n differenceShotPath?: string,\n): Promise<{\n pixelDifference: number;\n pixelDifferencePercentage: number;\n isWithinThreshold: boolean;\n}> => {\n const baselineImageBuffer = readFileSync(baselineShotPath);\n const currentImageBuffer = readFileSync(currentShotPath);\n\n if (baselineImageBuffer.equals(currentImageBuffer)) {\n return {\n pixelDifference: 0,\n pixelDifferencePercentage: 0,\n isWithinThreshold: true,\n };\n }\n\n let baselineImage: PNG = PNG.sync.read(baselineImageBuffer);\n let currentImage: PNG = PNG.sync.read(currentImageBuffer);\n\n const maxWidth = Math.max(baselineImage.width || 100, currentImage.width || 100);\n const maxHeight = Math.max(baselineImage.height || 100, currentImage.height || 100);\n\n if (baselineImage.width !== currentImage.width || baselineImage.height !== currentImage.height) {\n baselineImage = resizeImage(baselineImage, maxWidth, maxHeight);\n currentImage = resizeImage(currentImage, maxWidth, maxHeight);\n }\n\n const differenceImage = new PNG({ width: maxWidth, height: maxHeight });\n\n const pixelDifference = pixelmatch(\n baselineImage.data,\n currentImage.data,\n differenceImage.data,\n maxWidth,\n maxHeight,\n { threshold: 0 },\n );\n\n const pixelsTotal = baselineImage.width * baselineImage.height;\n\n if (pixelDifference > 0 && differenceShotPath) {\n const isWithinThreshold = checkThreshold(threshold, pixelsTotal, pixelDifference);\n\n if (!isWithinThreshold) {\n writeFileSync(differenceShotPath, PNG.sync.write(differenceImage));\n }\n\n return {\n pixelDifference,\n pixelDifferencePercentage: pixelDifference / pixelsTotal,\n isWithinThreshold,\n };\n }\n\n return {\n pixelDifference,\n pixelDifferencePercentage: pixelDifference / pixelsTotal,\n isWithinThreshold: true,\n };\n};\n\nexport const compareImagesViaOdiff = async (\n threshold: number,\n baselineShotPath: string,\n currentShotPath: string,\n differenceShotPath: string,\n): Promise<{\n pixelDifference: number;\n pixelDifferencePercentage: number;\n isWithinThreshold: boolean;\n}> => {\n const result = await odiffCompare(baselineShotPath, currentShotPath, differenceShotPath, {\n failOnLayoutDiff: false,\n });\n\n if (result.match) {\n return {\n pixelDifference: 0,\n pixelDifferencePercentage: 0,\n isWithinThreshold: true,\n };\n }\n\n if (result.reason === \"pixel-diff\") {\n let isWithinThreshold = true;\n\n // Treat theshold as percentage\n const pixelDifferencePercentage = Number(result.diffPercentage / 100);\n\n if (threshold < 1) {\n isWithinThreshold = pixelDifferencePercentage <= threshold;\n } else {\n // Treat threshold as absolute value\n isWithinThreshold = result.diffCount <= threshold;\n }\n\n return {\n pixelDifference: Number(result.diffCount),\n pixelDifferencePercentage,\n isWithinThreshold,\n };\n }\n\n throw new Error(\"Couldn't compare images\");\n};\n\nexport const compareImages = async (\n threshold: number,\n baselineShotPath: string,\n currentShotPath: string,\n differenceShotPath: string,\n): Promise<{\n pixelDifference: number;\n pixelDifferencePercentage: number;\n isWithinThreshold: boolean;\n}> => {\n if (isPlatformModeConfig(config)) {\n return featureNotSupported(\"compareImages()\");\n }\n\n if (config.compareEngine === \"pixelmatch\") {\n return compareImagesViaPixelmatch(\n threshold,\n baselineShotPath,\n currentShotPath,\n differenceShotPath,\n );\n }\n\n return compareImagesViaOdiff(threshold, baselineShotPath, currentShotPath, differenceShotPath);\n};\n","import { existsSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { mapLimit } from \"async\";\nimport { compareImages } from \"./compare/compare.js\";\nimport { log } from \"./log.js\";\nimport { config, isPlatformModeConfig } from \"./config.js\";\nimport type { ShotItem } from \"./types.js\";\nimport { featureNotSupported, shallGenerateMeta } from \"./utils.js\";\n\nexport const checkDifferences = async (shotItems: ShotItem[]) => {\n if (isPlatformModeConfig(config)) {\n return featureNotSupported(\"checkDifferences()\");\n }\n\n log.process(\n \"info\",\n \"general\",\n `Comparing ${shotItems.length} screenshots using '${config.compareEngine}' as compare engine`,\n );\n\n const total = shotItems.length;\n const noBaselinesItems: ShotItem[] = [];\n const aboveThresholdDifferenceItems: ShotItem[] = [];\n\n const comparisonResults: Record<\n string,\n {\n pixelDifference: number;\n pixelDifferencePercentage: number;\n isWithinThreshold: boolean;\n }\n > = {};\n\n await mapLimit<[number, ShotItem], void>(\n shotItems.entries(),\n config.compareConcurrency,\n async (item: [number, ShotItem]) => {\n const [index, shotItem] = item;\n const logger = (message: string) => {\n log\n .item({\n shotMode: shotItem.shotMode,\n uniqueItemId: shotItem.shotName,\n itemIndex: index,\n totalItems: total,\n })\n .process(\"info\", \"general\", message);\n };\n\n logger(`Comparing '${shotItem.id}'`);\n\n const baselineImageExists = existsSync(shotItem.filePathBaseline);\n\n if (!baselineImageExists) {\n logger(\"Baseline image missing. Will be treated as addition.\");\n noBaselinesItems.push(shotItem);\n\n return;\n }\n\n const currentImageExists = existsSync(shotItem.filePathCurrent);\n\n if (!currentImageExists) {\n throw new Error(`Error: Missing current image: ${shotItem.filePathCurrent}`);\n }\n\n const { pixelDifference, pixelDifferencePercentage, isWithinThreshold } = await compareImages(\n shotItem.threshold,\n shotItem.filePathBaseline,\n shotItem.filePathCurrent,\n shotItem.filePathDifference,\n );\n\n if (shallGenerateMeta()) {\n comparisonResults[shotItem.id] = {\n pixelDifference,\n pixelDifferencePercentage,\n isWithinThreshold,\n };\n }\n\n if (pixelDifference > 0) {\n const percentage = (pixelDifferencePercentage * 100).toFixed(2);\n\n if (isWithinThreshold) {\n logger(\n `Difference of ${pixelDifference} pixels (${percentage}%) found but within threshold.`,\n );\n } else {\n aboveThresholdDifferenceItems.push(shotItem);\n logger(\n `Difference of ${pixelDifference} pixels (${percentage}%) found. Difference image saved to: ${shotItem.filePathDifference}`,\n );\n }\n } else {\n logger(\"No difference found.\");\n }\n },\n );\n\n if (shallGenerateMeta()) {\n log.process(\n \"info\",\n \"general\",\n `Writing meta file with ${Object.entries(comparisonResults).length} items.`,\n );\n writeFileSync(\n `${path.join(config.imagePathCurrent, \"meta\")}.json`,\n JSON.stringify(comparisonResults, null, 2),\n );\n }\n\n log.process(\"info\", \"general\", \"Comparison done!\");\n\n return { aboveThresholdDifferenceItems, noBaselinesItems };\n};\n","import type { BrowserType, Page, Request } from \"playwright-core\";\nimport { config } from \"../config.js\";\nimport type { log } from \"../log.js\";\n\nconst checkIgnoreUrls = (url: string, ignoreUrls: string[]) => {\n for (const ignoreUrl of ignoreUrls) {\n if (url.includes(ignoreUrl)) {\n return true;\n }\n }\n\n return false;\n};\n\nexport const waitForNetworkRequests = async ({\n page,\n logger,\n timeout = config.timeouts.networkRequests,\n waitForFirstRequest = config.waitForFirstRequest,\n waitForLastRequest = config.waitForLastRequest,\n ignoreUrls = [],\n}: {\n page: Page;\n logger: ReturnType<typeof log.item>;\n timeout?: number;\n waitForFirstRequest?: number;\n waitForLastRequest?: number;\n ignoreUrls?: string[];\n}) =>\n new Promise((resolve, reject) => {\n let requestCounter = 0;\n const requests = new Set<Request>();\n let lastRequestTimeoutId: NodeJS.Timeout;\n\n const timeoutId = setTimeout(() => {\n const pendingUrls = [...requests].map((request) => request.url());\n\n logger.process(\"info\", \"network\", \"Pending requests:\", pendingUrls);\n\n cleanup();\n reject(new Error(\"Timeout\"));\n }, timeout);\n\n const firstRequestTimeoutId = setTimeout(() => {\n cleanup();\n resolve(true);\n }, waitForFirstRequest);\n\n const onRequest = (request: Request) => {\n if (!checkIgnoreUrls(request.url(), ignoreUrls)) {\n clearTimeout(firstRequestTimeoutId);\n clearTimeout(lastRequestTimeoutId);\n requestCounter++;\n requests.add(request);\n logger.browser(\"info\", \"network\", `+ ${request.url()}`);\n }\n };\n\n const onRequestFinished = async (request: Request) => {\n clearTimeout(lastRequestTimeoutId);\n\n if (!checkIgnoreUrls(request.url(), ignoreUrls)) {\n const failure = request.failure();\n const response = await request.response();\n\n requestCounter--;\n requests.delete(request);\n\n const statusText = failure\n ? failure.errorText\n : `${response?.status() ?? \"unknown\"} ${response?.statusText() ?? \"unknown\"}`;\n\n logger.browser(\"info\", \"network\", `- ${request.url()} [${statusText}]`);\n }\n\n lastRequestTimeoutId = setTimeout(() => {\n // `requestCounter` can be below 0 if requests have completed before they were being tracked\n if (requestCounter <= 0) {\n cleanup();\n resolve(true);\n }\n }, waitForLastRequest);\n };\n\n function cleanup() {\n clearTimeout(timeoutId);\n clearTimeout(firstRequestTimeoutId);\n clearTimeout(lastRequestTimeoutId);\n page.removeListener(\"request\", onRequest);\n page.removeListener(\"requestfinished\", onRequestFinished);\n page.removeListener(\"requestfailed\", onRequestFinished);\n }\n\n page.on(\"request\", onRequest);\n page.on(\"requestfinished\", onRequestFinished);\n page.on(\"requestfailed\", onRequestFinished);\n });\n\nexport const resizeViewportToFullscreen = async ({ page }: { page: Page }) => {\n const viewport = await page.evaluate(\n async () =>\n new Promise<{ height: number; width: number }>((resolve) => {\n const { body } = document;\n const html = document.documentElement;\n\n const height = Math.max(\n body.scrollHeight,\n body.offsetHeight,\n html.clientHeight,\n html.scrollHeight,\n html.offsetHeight,\n );\n\n const width = Math.max(\n body.scrollWidth,\n body.offsetWidth,\n html.clientWidth,\n html.scrollWidth,\n html.offsetWidth,\n );\n\n resolve({ height, width });\n }),\n );\n\n await page.setViewportSize({\n width: Math.max(page.viewportSize()?.width ?? 800, viewport.width),\n height: viewport.height,\n });\n};\n\nexport const selectBreakpoints = (\n topLevelBreakpoints?: number[],\n modeBreakpoints?: number[],\n shotBreakpoints?: number[],\n) => {\n if (shotBreakpoints && shotBreakpoints.length > 0) {\n return shotBreakpoints;\n }\n\n if (modeBreakpoints && modeBreakpoints.length > 0) {\n return modeBreakpoints;\n }\n\n return topLevelBreakpoints ?? [];\n};\n\nexport const generateLabel = ({\n breakpoint,\n browser,\n}: {\n breakpoint?: number;\n browser?: BrowserType;\n}): string => {\n const widthLabel = breakpoint && breakpoint > 0 ? `w${breakpoint}px` : \"\";\n const browserLabel = browser?.name() ?? \"\";\n\n const labels = [widthLabel, browserLabel].filter(Boolean);\n\n if (labels.length === 0) return \"\";\n\n return `__[${labels.join(\"|\")}]`;\n};\n","import path from \"node:path\";\nimport axios from \"axios\";\nimport type { BrowserType } from \"playwright-core\";\nimport { config, isPlatformModeConfig } from \"../config.js\";\nimport type { Mask, ShotItem } from \"../types.js\";\nimport { selectBreakpoints, generateLabel } from \"../shots/utils.js\";\nimport { notSupported } from \"../constants.js\";\nimport type { Story } from \"./storybook.js\";\n\ntype LadleStoryConfig = {\n name: string;\n filePath: string;\n meta: Record<string, unknown>;\n};\n\nexport const generateLadleShotItems = (\n baseUrl: string,\n isLocalServer: boolean,\n ladleStories: Story[],\n mask?: Mask[],\n modeBreakpoints?: number[],\n browser?: BrowserType,\n): ShotItem[] => {\n const ladleUrl = isLocalServer ? `${baseUrl}/index.html` : baseUrl;\n\n return ladleStories\n .filter((story) => story.parameters?.thirdeye?.disable !== true)\n .filter((story) =>\n config.filterShot ? config.filterShot({ ...story, shotMode: \"ladle\" }) : true,\n )\n .flatMap((ladleStory): ShotItem[] => {\n const shotName =\n config.shotNameGenerator?.({ ...ladleStory, shotMode: \"ladle\" }) ?? ladleStory.id;\n let label = generateLabel({ browser });\n let fileNameWithExt = `${shotName}${label}.png`;\n\n const shotItem: ShotItem = {\n shotMode: \"ladle\",\n id: `${ladleStory.story}${label}`,\n shotName: `${shotName}${label}`,\n importPath: ladleStory.importPath,\n url: `${ladleUrl}?story=${ladleStory.story}&mode=preview`,\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n threshold: ladleStory.parameters?.thirdeye?.threshold ?? config.threshold,\n waitBeforeScreenshot:\n ladleStory.parameters?.thirdeye?.waitBeforeScreenshot ?? config.waitBeforeScreenshot,\n mask: [...(mask ?? []), ...(ladleStory.parameters?.thirdeye?.mask ?? [])],\n elementLocator:\n ladleStory.parameters?.thirdeye?.elementLocator ??\n config?.storybookShots?.elementLocator ??\n \"\",\n waitForSelector: config?.ladleShots?.waitForSelector ?? \"[data-storyloaded]\",\n componentPath: ladleStory.kind,\n storyName: ladleStory.story,\n };\n\n const breakpoints = selectBreakpoints(\n config.breakpoints,\n modeBreakpoints,\n ladleStory.parameters?.thirdeye?.breakpoints,\n );\n\n if (breakpoints.length === 0) {\n return [shotItem];\n }\n\n return breakpoints.map((breakpoint) => {\n label = generateLabel({ breakpoint, browser });\n fileNameWithExt = `${shotName}${label}.png`;\n\n return {\n ...shotItem,\n id: `${ladleStory.story}${label}`,\n shotName: `${ladleStory.story}${label}`,\n breakpoint,\n breakpointGroup: ladleStory.story,\n url: `${ladleUrl}?story=${ladleStory.story}&mode=preview&width=${breakpoint}`,\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n viewport: { width: breakpoint },\n };\n });\n });\n};\n\nexport const collectLadleStories = async (ladleUrl: string) => {\n const {\n data,\n }: {\n data: {\n stories: LadleStoryConfig[];\n };\n } = await axios.get(`${ladleUrl}/meta.json`);\n\n const collection: Story[] = [];\n\n for (const [key, storyConfig] of Object.entries(data.stories)) {\n collection.push({\n id: key,\n story: key,\n kind: key,\n importPath: storyConfig.filePath,\n parameters: storyConfig.meta,\n });\n }\n\n return collection;\n};\n","import { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { BrowserContext, BrowserType } from \"playwright-core\";\nimport type { Mask, ShotItem } from \"../types.js\";\nimport { config, isPlatformModeConfig } from \"../config.js\";\nimport { launchBrowser } from \"../utils.js\";\nimport { log } from \"../log.js\";\nimport { generateLabel, selectBreakpoints } from \"../shots/utils.js\";\nimport { notSupported } from \"../constants.js\";\n\nconst kebabCase = (str?: string): string =>\n (str ?? \"\")\n .replace(/([a-z\\d])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_/]+/g, \"-\")\n .toLowerCase();\n\ntype ExtraShots = {\n name?: string;\n args?: Record<string, unknown>; // Additional args for the snapshot\n prefix?: string; // Prefix for the snapshot name\n suffix?: string; // Suffix for the snapshot name\n};\nexport type StoryParameters = {\n thirdeye?: {\n disable?: boolean;\n threshold?: number;\n waitBeforeScreenshot?: number;\n mask?: Mask[];\n breakpoints?: number[];\n args?: Record<string, unknown>; // Args for the story\n extraShots?: ExtraShots[]; // Additional snapshots for the story\n elementLocator?: string;\n };\n viewport?: {\n width?: number;\n height?: number;\n };\n fileName?: string;\n};\n\nexport type Story = {\n id: string;\n kind: string;\n story: string;\n name?: string;\n title?: string;\n importPath?: string;\n parameters?: StoryParameters & {\n storyshots?: {\n disable?: boolean;\n };\n };\n};\n\ntype StorybookPreviewApi = {\n ready: () => Promise<void>;\n extract?: () => Promise<Record<string, Story>>;\n};\n\ntype StorybookClientApi = {\n raw?: () => Story[];\n storyStore?: {\n cacheAllCSFFiles: () => Promise<void>;\n };\n};\n\ntype WindowObject = typeof window & {\n __STORYBOOK_PREVIEW__: StorybookPreviewApi;\n __STORYBOOK_CLIENT_API__: StorybookClientApi;\n};\n\ntype CrawlerResult = {\n stories: Story[] | undefined;\n};\n\nexport const getStoryBookUrl = (url: string) => {\n if (url.startsWith(\"http://\") || url.startsWith(\"https://\") || url.startsWith(\"file://\")) {\n return url;\n }\n\n if (url.startsWith(\"/\")) {\n return `file://${url}`;\n }\n\n return `file://${path.normalize(path.join(process.cwd(), url))}`;\n};\n\nexport const getIframeUrl = (url: string) =>\n url.endsWith(\"/\") ? `${url}iframe.html` : `${url}/iframe.html`;\n\nexport const collectStoriesViaWindowApi = async (\n context: BrowserContext,\n url: string,\n isIframeUrl?: boolean,\n) => {\n const page = await context.newPage();\n const iframeUrl = isIframeUrl ? getStoryBookUrl(url) : getIframeUrl(getStoryBookUrl(url));\n\n await page.goto(iframeUrl);\n\n await page.waitForFunction(() => (window as WindowObject).__STORYBOOK_PREVIEW__, null, {\n timeout: config.timeouts.fetchStories,\n });\n\n // Storybook >= 8 expose a new preview API that has a `ready` method to be awaited before proceeding\n const isV8OrAbove = await page.evaluate(async () => {\n const { __STORYBOOK_PREVIEW__: api } = window as WindowObject;\n\n return api.ready !== undefined;\n });\n\n if (isV8OrAbove) {\n // SB v8 and above\n await page.evaluate(async () => {\n const { __STORYBOOK_PREVIEW__: api } = window as WindowObject;\n\n if (api.ready) {\n await api.ready();\n }\n });\n } else {\n // SB v7 and below\n await page.waitForFunction(() => (window as WindowObject).__STORYBOOK_CLIENT_API__, null, {\n timeout: config.timeouts.fetchStories,\n });\n\n await page.evaluate(async () => {\n const { __STORYBOOK_CLIENT_API__: api } = window as WindowObject;\n\n if (api.storyStore) {\n await api.storyStore.cacheAllCSFFiles?.();\n }\n });\n }\n\n const result = await page.evaluate(async (): Promise<CrawlerResult> => {\n const parseParameters = <T>(\n parameters: T,\n level = 0,\n ): T | \"UNSUPPORTED_DEPTH\" | \"UNSUPPORTED_TYPE\" => {\n if (level > 10) {\n return \"UNSUPPORTED_DEPTH\";\n }\n\n if (Array.isArray(parameters)) {\n // @ts-expect-error FIXME\n return parameters.map((value) => parseParameters<unknown>(value, level + 1));\n }\n\n if (\n typeof parameters === \"string\" ||\n typeof parameters === \"number\" ||\n typeof parameters === \"boolean\" ||\n parameters === undefined ||\n typeof parameters === \"function\" ||\n parameters instanceof RegExp ||\n parameters instanceof Date ||\n parameters === null\n ) {\n return parameters;\n }\n\n if (typeof parameters === \"object\" && parameters !== null) {\n // @ts-expect-error FIXME\n return Object.keys(parameters).reduce<T>((acc, key: keyof T) => {\n // @ts-expect-error FIXME\n acc[key] = parseParameters(parameters[key], level + 1);\n\n return acc;\n }, {});\n }\n\n return \"UNSUPPORTED_TYPE\";\n };\n\n const mapStories = (stories: Story[]): Story[] =>\n stories.map((story) => {\n const parameters = parseParameters(\n story.parameters as Record<string, unknown>,\n ) as Story[\"parameters\"];\n\n return {\n id: story.id,\n kind: story.kind,\n story: story.story,\n importPath: parameters?.fileName,\n parameters,\n };\n });\n\n const { __STORYBOOK_PREVIEW__: previewApi, __STORYBOOK_CLIENT_API__: clientApi } =\n window as WindowObject;\n\n let stories: Story[] = [];\n\n if (previewApi.extract) {\n const items = await previewApi.extract();\n\n stories = mapStories(Object.values(items));\n } else if (clientApi.raw) {\n // Fallback for 6.4 and below\n stories = mapStories(clientApi.raw());\n }\n\n return { stories };\n });\n\n return result;\n};\n\nexport const collectStoriesViaStoriesJson = async (context: BrowserContext, url: string) => {\n const indexJsonUrl = url.endsWith(\"/\") ? `${url}index.json` : `${url}/index.json`;\n const storiesJsonUrl = url.endsWith(\"/\") ? `${url}stories.json` : `${url}/stories.json`;\n\n const tryLoadJson = async (jsonUrl: string): Promise<Record<string, unknown> | null> => {\n if (jsonUrl.startsWith(\"file://\")) {\n try {\n const file = readFileSync(jsonUrl.slice(7));\n return JSON.parse(file.toString()) as Record<string, unknown>;\n } catch {\n return null;\n }\n }\n\n try {\n const result = await context.request.get(jsonUrl);\n if (result.status() !== 200) return null;\n return (await result.json()) as Record<string, unknown>;\n } catch {\n return null;\n }\n };\n\n // Try index.json first (Storybook v7+), then fall back to stories.json\n for (const jsonUrl of [indexJsonUrl, storiesJsonUrl]) {\n const json = await tryLoadJson(jsonUrl);\n if (!json) continue;\n\n // Storybook v7+ uses \"entries\", older versions use \"stories\"\n const entries = json.entries ?? json.stories;\n if (typeof entries === \"object\" && entries !== null) {\n const raw = Object.values(entries) as (Story & { type?: string })[];\n const stories = raw\n // index.json includes \"docs\" entries; filter to stories only\n .filter((entry) => entry.type !== \"docs\")\n .map((entry) => ({\n ...entry,\n // index.json uses \"title\" and \"name\"; normalize to \"kind\" and \"story\"\n kind: entry.kind ?? entry.title ?? \"\",\n story: entry.story ?? entry.name ?? \"\",\n }));\n return { stories };\n }\n }\n\n throw new Error(`Cannot load stories from ${indexJsonUrl} or ${storiesJsonUrl}`);\n};\n\nexport const collectStories = async (url: string) => {\n const browser = await launchBrowser();\n const context = await browser.newContext();\n\n try {\n log.process(\"info\", \"general\", \"Trying to collect stories via window object\");\n const result = await collectStoriesViaWindowApi(context, url);\n\n await browser.close();\n\n return result;\n } catch (error: unknown) {\n log.process(\"info\", \"general\", \"Fallback to /stories.json\");\n log.process(\"error\", \"general\", error);\n }\n\n try {\n const result = await collectStoriesViaStoriesJson(context, url);\n\n await browser.close();\n\n return result;\n } catch (error: unknown) {\n await browser.close();\n throw error;\n }\n};\n\nconst generateBrowserConfig = (story: Story) => {\n const browserConfig = config.configureBrowser?.({\n ...story,\n shotMode: \"storybook\",\n });\n\n if (story.parameters?.viewport && browserConfig) {\n browserConfig.viewport ??= {\n width: 1280,\n height: 720,\n };\n browserConfig.viewport = {\n ...browserConfig.viewport,\n ...story.parameters.viewport,\n };\n }\n\n return browserConfig;\n};\n\nconst generateStoryUrl = (\n iframeUrl: string,\n storyId: string,\n args?: Record<string, unknown>,\n breakpoint?: number,\n): string => {\n let url = `${iframeUrl}?id=${storyId}&viewMode=story`;\n\n if (args) {\n const argsString = Object.entries(args)\n .map(([key, value]) => `${key}:${value as string}`)\n .join(\";\");\n\n url += `&args=${argsString}`;\n }\n\n if (breakpoint !== undefined) {\n url += `&width=${breakpoint}`;\n }\n\n return url;\n};\n\nconst generateFilename = (kind: string, story: string, prefix?: string, suffix?: string) => {\n return [prefix, kebabCase(kind), kebabCase(story), kebabCase(suffix)].filter(Boolean).join(\"--\");\n};\n\nexport const generateStorybookShotItems = (\n baseUrl: string,\n stories: Story[],\n mask?: Mask[],\n modeBreakpoints?: number[],\n browser?: BrowserType,\n): ShotItem[] => {\n const iframeUrl = getIframeUrl(getStoryBookUrl(baseUrl));\n\n return stories\n .filter((story) => story.parameters?.thirdeye?.disable !== true)\n .filter((story) => story.parameters?.storyshots?.disable !== true)\n .filter((story) =>\n config.filterShot ? config.filterShot({ ...story, shotMode: \"storybook\" }) : true,\n )\n .flatMap((story): ShotItem[] => {\n const shotName =\n config.shotNameGenerator?.({ ...story, shotMode: \"storybook\" }) ??\n generateFilename(story.kind, story.story);\n let label = generateLabel({ browser });\n let fileNameWithExt = `${shotName}${label}.png`;\n\n const baseShotItem: ShotItem = {\n shotMode: \"storybook\",\n id: `${story.id}${label}`,\n shotName: `${shotName}${label}`,\n importPath: story.importPath,\n url: generateStoryUrl(iframeUrl, story.id, story.parameters?.thirdeye?.args),\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n browserConfig: generateBrowserConfig(story),\n threshold: story.parameters?.thirdeye?.threshold ?? config.threshold,\n waitBeforeScreenshot:\n story.parameters?.thirdeye?.waitBeforeScreenshot ?? config.waitBeforeScreenshot,\n mask: [...(mask ?? []), ...(story.parameters?.thirdeye?.mask ?? [])],\n elementLocator:\n story.parameters?.thirdeye?.elementLocator ??\n config?.storybookShots?.elementLocator ??\n \"\",\n waitForSelector: config?.storybookShots?.waitForSelector,\n componentPath: story.kind,\n storyName: story.story,\n storyId: story.id,\n storyArgs: story.parameters?.thirdeye?.args,\n };\n\n const storyLevelBreakpoints = story.parameters?.thirdeye?.breakpoints ?? [];\n\n const breakpoints = selectBreakpoints(\n config.breakpoints,\n modeBreakpoints,\n storyLevelBreakpoints,\n );\n\n let shotItems = [];\n\n if (!breakpoints || breakpoints.length === 0) {\n shotItems = [baseShotItem];\n } else {\n shotItems = breakpoints.map((breakpoint) => {\n label = generateLabel({ breakpoint, browser });\n fileNameWithExt = `${shotName}${label}.png`;\n\n return {\n ...baseShotItem,\n id: `${story.id}${label}`,\n shotName: `${shotName}${label}`,\n breakpoint,\n breakpointGroup: story.id,\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n viewport: {\n width: breakpoint,\n height: undefined,\n },\n url: generateStoryUrl(\n iframeUrl,\n story.id,\n story.parameters?.thirdeye?.args,\n breakpoint,\n ),\n browserConfig: generateBrowserConfig({\n ...story,\n parameters: {\n ...story.parameters,\n viewport: {\n width: breakpoint,\n },\n },\n }),\n };\n });\n }\n\n const extraShots =\n story.parameters?.thirdeye?.extraShots?.flatMap((snapshot) => {\n const combinedArgs = {\n ...story.parameters?.thirdeye?.args,\n ...snapshot.args,\n };\n const snapshotShotName = generateFilename(\n story.kind,\n story.story,\n snapshot.prefix,\n snapshot.suffix,\n );\n\n return (breakpoints?.length === 0 ? [undefined] : breakpoints).map((breakpoint) => {\n label = generateLabel({ breakpoint, browser });\n fileNameWithExt = `${snapshotShotName}${label}.png`;\n\n return {\n ...baseShotItem,\n id: `${story.id}${label}-${snapshot.name ?? \"snapshot\"}`,\n shotName: `${snapshotShotName}${label}`,\n breakpoint,\n breakpointGroup: story.id,\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n url: generateStoryUrl(iframeUrl, story.id, combinedArgs, breakpoint),\n viewport: breakpoint\n ? {\n width: breakpoint,\n height: undefined,\n }\n : undefined,\n browserConfig: generateBrowserConfig({\n ...story,\n parameters: {\n ...story.parameters,\n viewport: {\n width: breakpoint,\n },\n },\n }),\n };\n });\n }) ?? [];\n\n return [...shotItems, ...extraShots];\n });\n};\n","import path from \"node:path\";\nimport axios, { isAxiosError } from \"axios\";\nimport { z } from \"zod\";\nimport type { BrowserType } from \"playwright-core\";\nimport fs from \"fs-extra\";\nimport { log } from \"../log.js\";\nimport { type PageScreenshotParameter, config, isPlatformModeConfig } from \"../config.js\";\nimport type { Mask, ShotItem } from \"../types.js\";\nimport { selectBreakpoints, generateLabel } from \"../shots/utils.js\";\nimport { notSupported } from \"../constants.js\";\n\nconst generateBrowserConfig = (page: PageScreenshotParameter) => {\n const browserConfig = config.configureBrowser?.({\n ...page,\n shotMode: \"page\",\n });\n\n if (page.viewport && browserConfig) {\n browserConfig.viewport ??= {\n width: 1280,\n height: 720,\n };\n browserConfig.viewport = {\n ...browserConfig.viewport,\n ...page.viewport,\n };\n }\n\n return browserConfig;\n};\n\nexport const generatePageShotItems = (\n pages: PageScreenshotParameter[],\n baseUrl: string,\n mask?: Mask[],\n modeBreakpoints?: number[],\n browser?: BrowserType,\n): ShotItem[] => {\n const names = pages.map((page) => page.name);\n const uniqueNames = new Set(names);\n\n if (names.length !== uniqueNames.size) {\n throw new Error(\"Error: Page names must be unique\");\n }\n\n return pages.flatMap((page): ShotItem[] => {\n const shotName = config.shotNameGenerator?.({ ...page, shotMode: \"page\" }) ?? page.name;\n let label = generateLabel({ browser });\n let fileNameWithExt = `${shotName}${label}.png`;\n\n const baseShotItem: ShotItem = {\n shotMode: \"page\",\n id: `${shotName}${label}`,\n shotName: `${shotName}${label}`,\n url: path.join(baseUrl, page.path),\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n browserConfig: generateBrowserConfig(page),\n threshold: page.threshold ?? config.threshold,\n waitBeforeScreenshot: page.waitBeforeScreenshot ?? config.waitBeforeScreenshot,\n mask: [...(mask ?? []), ...(page.mask ?? [])],\n waitForSelector: config?.pageShots?.waitForSelector,\n componentPath: undefined,\n storyName: undefined,\n };\n\n const breakpoints = selectBreakpoints(config.breakpoints, modeBreakpoints, page.breakpoints);\n\n if (breakpoints.length === 0) {\n return [baseShotItem];\n }\n\n return breakpoints.map((breakpoint) => {\n label = generateLabel({ breakpoint, browser });\n fileNameWithExt = `${shotName}${label}.png`;\n\n return {\n ...baseShotItem,\n id: `${shotName}${label}`,\n shotName: `${shotName}${label}`,\n breakpoint,\n breakpointGroup: page.name,\n url: path.join(baseUrl, page.path),\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n viewport: { width: breakpoint },\n browserConfig: generateBrowserConfig({\n ...page,\n viewport: { width: breakpoint },\n }),\n };\n });\n });\n};\n\n// Helper function to check if a string is a valid URL\nconst isValidHttpUrl = (string: string) => {\n let url;\n\n try {\n url = new URL(string);\n } catch {\n return false;\n }\n\n return url.protocol === \"http:\" || url.protocol === \"https:\";\n};\n\nexport const getPagesFromExternalLoader = async () => {\n try {\n if (!config.pageShots?.pagesJsonUrl) {\n return [];\n }\n\n log.process(\"info\", \"general\", `⏬ Loading pages from ${config.pageShots.pagesJsonUrl}`);\n\n let pages;\n\n // Check if the pagesJsonUrl is a valid URL or a local file path\n if (isValidHttpUrl(config.pageShots.pagesJsonUrl)) {\n log.process(\"info\", \"general\", `🕸️ Trying to fetch from URL`);\n const response = await axios.get<PageScreenshotParameter[]>(config.pageShots.pagesJsonUrl);\n\n pages = response.data;\n } else {\n // Read the file from the local filesystem\n log.process(\"info\", \"general\", `⏬ Trying to fetch from local file`);\n const fileContents = await fs.readFile(config.pageShots.pagesJsonUrl, \"utf8\");\n\n pages = JSON.parse(fileContents) as PageScreenshotParameter[];\n }\n\n // Validation logic remains the same\n const pagesArraySchema = z.array(\n z.object({\n path: z.string(),\n name: z.string(),\n waitBeforeScreenshot: z.number().optional(),\n threshold: z.number().optional(),\n mask: z\n .array(\n z.object({\n selector: z.string(),\n }),\n )\n .optional(),\n viewport: z\n .object({\n width: z.string(),\n height: z.string(),\n })\n .optional(),\n }),\n );\n\n const validatePages = pagesArraySchema.safeParse(pages);\n\n if (validatePages.success) {\n log.process(\n \"info\",\n \"general\",\n `✅ Successfully validated pages structure & loaded ${pages.length} pages from JSON file.`,\n );\n\n return pages;\n }\n\n log.process(\"error\", \"general\", \"❌ Error validating the loaded pages structure\");\n log.process(\"error\", \"general\", validatePages.error);\n\n return [];\n } catch (error: unknown) {\n if (isAxiosError(error) || error instanceof Error) {\n log.process(\"error\", \"network\", `❌ Error when fetching data: ${error.message}`);\n }\n\n return [];\n }\n};\n","import path from \"node:path\";\nimport { writeFileSync } from \"node:fs\";\nimport { mapLimit } from \"async\";\nimport type { Browser, BrowserType, PageScreenshotOptions } from \"playwright-core\";\nimport { log } from \"../log.js\";\nimport { hashFile, launchBrowser, sleep } from \"../utils.js\";\nimport { config } from \"../config.js\";\nimport type { ShotItem } from \"../types.js\";\nimport { resizeViewportToFullscreen, waitForNetworkRequests } from \"./utils.js\";\n\nconst takeScreenShot = async ({\n browser,\n shotItem,\n logger,\n}: {\n browser: Browser;\n shotItem: ShotItem;\n logger: ReturnType<typeof log.item>;\n}): Promise<boolean> => {\n const context = await browser.newContext(shotItem.browserConfig);\n const page = await context.newPage();\n let success = false;\n\n page.on(\"pageerror\", (exception) => {\n logger.browser(\"error\", \"general\", \"Uncaught exception:\", exception);\n });\n\n page.on(\"console\", async (message) => {\n const values: unknown[] = [];\n\n try {\n for (const arg of message.args()) {\n values.push(await arg.jsonValue());\n }\n } catch (error: unknown) {\n logger.browser(\"error\", \"console\", \"Error while collecting console output\", error);\n }\n\n logger.browser(\"info\", \"console\", String(values.shift()), ...values);\n });\n\n try {\n await page.goto(shotItem.url);\n } catch (error: unknown) {\n if (error instanceof Error && error.name === \"TimeoutError\") {\n logger.process(\"error\", \"timeout\", `Timeout while loading page: ${shotItem.url}`);\n } else {\n logger.process(\"error\", \"general\", \"Page loading failed\", error);\n }\n }\n\n try {\n await page.waitForLoadState(\"load\", {\n timeout: config.timeouts.loadState,\n });\n } catch (error: unknown) {\n logger.process(\n \"error\",\n \"timeout\",\n `Timeout while waiting for page load state: ${shotItem.url}`,\n error,\n );\n }\n\n if (shotItem.waitForSelector) {\n try {\n await page.waitForSelector(shotItem.waitForSelector, {\n state: \"attached\",\n timeout: config.timeouts.loadState,\n });\n } catch (error: unknown) {\n logger.process(\n \"error\",\n \"timeout\",\n `Timeout while waiting for Selector ('${shotItem.waitForSelector}') to appear: ${shotItem.url}`,\n error,\n );\n }\n }\n\n try {\n await waitForNetworkRequests({\n page,\n logger,\n ignoreUrls: [\"/__webpack_hmr\"],\n });\n } catch (error: unknown) {\n logger.process(\n \"error\",\n \"timeout\",\n `Timeout while waiting for all network requests: ${shotItem.url}`,\n error,\n );\n }\n\n if (config.beforeScreenshot) {\n await config.beforeScreenshot(page, {\n shotMode: shotItem.shotMode,\n id: shotItem.id,\n shotName: shotItem.shotName,\n });\n }\n\n let fullScreenMode = true;\n\n // Wait for all fonts to finish loading before taking the screenshot.\n // Wait for fonts and freeze animations for deterministic screenshots.\n try {\n await page.evaluate(() => document.fonts.ready);\n } catch {\n // Ignore errors (e.g. page already closed)\n }\n\n // Freeze all animations for deterministic screenshots.\n // CSS: reset all animations/transitions to their initial state.\n // JS: pause SVG SMIL animations and stop requestAnimationFrame loops.\n // Playwright's `animations: \"disabled\"` only pauses at *current* position,\n // which is non-deterministic for spinners/loaders.\n try {\n await page.addStyleTag({\n content: `\n *, *::before, *::after {\n animation-delay: -0.0001ms !important;\n animation-duration: 0s !important;\n animation-play-state: paused !important;\n transition-duration: 0s !important;\n transition-delay: 0s !important;\n }\n `,\n });\n\n await page.evaluate(() => {\n // Pause all SVG SMIL animations\n document.querySelectorAll(\"svg\").forEach((svg) => {\n try {\n svg.pauseAnimations?.();\n } catch {\n // Not all SVGs support this\n }\n });\n\n // Force all web animations to jump to their start\n document.getAnimations().forEach((anim) => {\n anim.pause();\n anim.currentTime = 0;\n });\n });\n } catch {\n // Ignore errors (e.g. page already closed)\n }\n\n await sleep(shotItem?.waitBeforeScreenshot ?? config.waitBeforeScreenshot);\n\n try {\n if (shotItem.viewport) {\n const currentViewport = page.viewportSize();\n\n await page.setViewportSize({\n width: shotItem.viewport.width,\n height: currentViewport?.height ?? 500,\n });\n\n fullScreenMode = true;\n } else {\n await resizeViewportToFullscreen({ page });\n fullScreenMode = false;\n }\n } catch (error: unknown) {\n logger.process(\n \"error\",\n \"general\",\n `Could not resize viewport to fullscreen: ${shotItem.shotName}`,\n error,\n );\n }\n\n let retryCount = 0;\n let lastShotHash;\n\n try {\n while (retryCount <= config.flakynessRetries) {\n const { elementLocator } = shotItem;\n\n let screenshotOptions: PageScreenshotOptions = {\n path: shotItem.filePathCurrent,\n animations: \"disabled\",\n mask: shotItem.mask ? shotItem.mask.map((mask) => page.locator(mask.selector)) : [],\n };\n\n if (elementLocator) {\n // Explicit locator — use it directly\n await page.locator(elementLocator).screenshot(screenshotOptions);\n } else if (shotItem.shotMode === \"storybook\") {\n // Smart Storybook capture: detect portals (dialogs, popovers, etc.)\n // and choose the right element to screenshot.\n const hasPortal = await page.evaluate(() => {\n const selectors = [\n \"[data-radix-portal]\",\n \"[data-radix-popper-content-wrapper]\",\n \"[role='dialog']\",\n \"[role='alertdialog']\",\n \".ReactModal__Overlay\",\n ];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el && (el as HTMLElement).offsetHeight > 0) return true;\n }\n return false;\n });\n\n if (hasPortal) {\n // Portal detected — capture the visible content using a clip\n // that covers the union of all visible elements' bounding rects.\n const clip = await page.evaluate(() => {\n const elements = document.querySelectorAll(\n \"#storybook-root, #storybook-root *, [data-radix-portal], [data-radix-portal] *, [role='dialog'], [role='dialog'] *\",\n );\n let minX = Infinity;\n let minY = Infinity;\n let maxX = 0;\n let maxY = 0;\n for (const el of elements) {\n const rect = (el as HTMLElement).getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) continue;\n minX = Math.min(minX, rect.left);\n minY = Math.min(minY, rect.top);\n maxX = Math.max(maxX, rect.right);\n maxY = Math.max(maxY, rect.bottom);\n }\n if (minX === Infinity) return null;\n return {\n x: Math.max(0, Math.floor(minX)),\n y: Math.max(0, Math.floor(minY)),\n width: Math.ceil(maxX - Math.max(0, minX)),\n height: Math.ceil(maxY - Math.max(0, minY)),\n };\n });\n\n if (clip && clip.width > 0 && clip.height > 0) {\n await page.screenshot({ ...screenshotOptions, clip });\n } else {\n await page.screenshot({ ...screenshotOptions, fullPage: fullScreenMode });\n }\n } else {\n // No portal — screenshot #storybook-root for tight crop\n const storybookRoot = page.locator(\"#storybook-root\");\n if ((await storybookRoot.count()) > 0) {\n await storybookRoot.screenshot(screenshotOptions);\n } else {\n await page.screenshot({ ...screenshotOptions, fullPage: fullScreenMode });\n }\n }\n } else {\n screenshotOptions = { ...screenshotOptions, fullPage: fullScreenMode };\n await page.screenshot(screenshotOptions);\n }\n\n const currentShotHash = hashFile(shotItem.filePathCurrent);\n\n if (lastShotHash) {\n logger.process(\n \"info\",\n \"general\",\n `Screenshot of '${shotItem.shotName}' taken (Retry ${retryCount}). Hash: ${currentShotHash} - Previous hash: ${lastShotHash}`,\n );\n\n if (lastShotHash === currentShotHash) {\n break;\n }\n }\n\n lastShotHash = currentShotHash;\n\n if (retryCount < config.flakynessRetries) {\n await sleep(config.waitBetweenFlakynessRetries);\n }\n\n retryCount++;\n }\n\n success = true;\n\n // Capture DOM snapshot alongside screenshot\n try {\n const domHtml = await page.content();\n const htmlPath = shotItem.filePathCurrent.replace(/\\.png$/, \".html\");\n\n writeFileSync(htmlPath, domHtml);\n } catch (domError: unknown) {\n logger.process(\"error\", \"general\", \"Failed to capture DOM snapshot\", domError);\n }\n } catch (error: unknown) {\n logger.process(\"error\", \"general\", \"Error when taking screenshot\", error);\n }\n\n if (config.afterScreenshot) {\n await config.afterScreenshot(page, shotItem);\n }\n\n await context.close();\n\n const videoPath = await page.video()?.path();\n\n if (videoPath) {\n const dirname = path.dirname(videoPath);\n const ext = videoPath.split(\".\").pop() ?? \"webm\";\n const newVideoPath = `${dirname}/${shotItem.shotName}.${ext}`;\n\n await page.video()?.saveAs(newVideoPath);\n await page.video()?.delete();\n\n logger.process(\n \"info\",\n \"general\",\n `Video of '${shotItem.shotName}' recorded and saved to '${newVideoPath}`,\n );\n }\n\n return success;\n};\n\nexport const takeScreenShots = async (shotItems: ShotItem[], _browser?: BrowserType) => {\n const browser = await launchBrowser(_browser);\n const total = shotItems.length;\n\n await mapLimit<[number, ShotItem], void>(\n shotItems.entries(),\n config.shotConcurrency,\n async (item: [number, ShotItem]) => {\n const [index, shotItem] = item;\n const logger = log.item({\n shotMode: shotItem.shotMode,\n uniqueItemId: shotItem.shotName,\n itemIndex: index,\n totalItems: total,\n });\n\n logger.process(\n \"info\",\n \"general\",\n\n `Taking screenshot of '${shotItem.shotName} ${\n shotItem.breakpoint ? `[${shotItem.breakpoint}]` : \"\"\n }'`,\n );\n\n const startTime = Date.now();\n\n const result = await takeScreenShot({ browser, shotItem, logger });\n const endTime = Date.now();\n const elapsedTime = Number((endTime - startTime) / 1000).toFixed(3);\n\n if (result) {\n logger.process(\n \"info\",\n \"general\",\n `Screenshot of '${shotItem.shotName}' taken and saved to '${shotItem.filePathCurrent}' in ${elapsedTime}s`,\n );\n } else {\n logger.process(\n \"info\",\n \"general\",\n `Screenshot of '${shotItem.shotName}' failed and took ${elapsedTime}s`,\n );\n }\n },\n );\n\n await browser.close();\n};\n","import http from \"node:http\";\nimport handler from \"serve-handler\";\nimport { getPort } from \"get-port-please\";\n\nexport const launchStaticWebServer = async (basePath: string) => {\n const port = await getPort({\n random: true,\n });\n\n const server = http.createServer(async (request, response) => {\n return handler(request, response, {\n public: basePath.startsWith(\"file://\") ? basePath.slice(7) : basePath,\n cleanUrls: false,\n });\n });\n\n server.listen(port);\n\n return {\n server,\n port,\n url: `http://localhost:${port}`,\n };\n};\n","import path from \"node:path\";\nimport axios from \"axios\";\nimport type { BrowserType } from \"playwright-core\";\nimport { log } from \"../log.js\";\nimport { config, isPlatformModeConfig } from \"../config.js\";\nimport { type ShotItem } from \"../types.js\";\nimport { notSupported } from \"../constants.js\";\nimport { generateLabel } from \"../shots/utils.js\";\n\ntype HistoireStory = {\n id: string;\n title: string;\n group: string | undefined;\n layout: {\n type: string;\n width: string;\n };\n variants?: HistoireStory[];\n};\n\ntype HistoireResponse = {\n stories: HistoireStory[];\n};\n\nconst generateShotItemsForStory = (\n story: HistoireStory,\n baseUrl: string,\n browser?: BrowserType,\n): ShotItem[] => {\n const shotItems: ShotItem[] = [];\n\n // Treat stories without variants as if they had a single variant\n const variants = story.variants ?? [story];\n\n for (const variant of variants) {\n const shotName =\n config.shotNameGenerator?.({ ...variant, shotMode: \"histoire\" }) ??\n `${story.id}_${variant.title}`;\n const label = generateLabel({ browser });\n const fileNameWithExt = `${shotName}${label}.png`;\n\n shotItems.push({\n shotMode: \"histoire\",\n id: `${story.id}_${variant.id}${label}`,\n shotName: `${shotName}${label}`,\n url: `${baseUrl}/__sandbox.html?storyId=${story.id}&variantId=${variant.id}`,\n filePathBaseline: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathBaseline, fileNameWithExt),\n filePathCurrent: path.join(config.imagePathCurrent, fileNameWithExt),\n filePathDifference: isPlatformModeConfig(config)\n ? notSupported\n : path.join(config.imagePathDifference, fileNameWithExt),\n threshold: config.threshold,\n waitForSelector: config?.histoireShots?.waitForSelector,\n componentPath: story.id,\n storyName: variant.title,\n });\n }\n\n return shotItems.filter((story) => story.id !== \"full-config\");\n};\n\nexport const generateHistoireShotItems = (\n baseUrl: string,\n stories: HistoireStory[],\n browser?: BrowserType,\n): ShotItem[] => {\n return stories.flatMap((story) => generateShotItemsForStory(story, baseUrl, browser));\n};\n\nexport const collectHistoireStories = async (histoireUrl: string) => {\n const jsonUrl = `${histoireUrl}/histoire.json`;\n\n log.process(\"info\", \"general\", `\\n=== [Histoire Mode] ${jsonUrl} ===\\n`);\n const response = await axios.get<HistoireResponse>(jsonUrl);\n\n // Ignore the full-config story from Histoire as it is just JSON\n return response.data.stories;\n};\n","import type { Server } from \"node:http\";\nimport { mapLimit } from \"async\";\nimport type { BrowserType } from \"playwright-core\";\nimport { collectLadleStories, generateLadleShotItems } from \"./crawler/ladleScreenshots.js\";\nimport { type PageScreenshotParameter, config, isPlatformModeConfig } from \"./config.js\";\nimport { collectStories, generateStorybookShotItems } from \"./crawler/storybook.js\";\nimport { generatePageShotItems, getPagesFromExternalLoader } from \"./crawler/pageScreenshots.js\";\nimport { log } from \"./log.js\";\nimport { takeScreenShots } from \"./shots/shots.js\";\nimport { getBrowsers, readDirIntoShotItems, removeFilesInFolder } from \"./utils.js\";\nimport { launchStaticWebServer } from \"./crawler/utils.js\";\nimport type { ShotItem } from \"./types.js\";\nimport {\n collectHistoireStories,\n generateHistoireShotItems,\n} from \"./crawler/histoireScreenshots.js\";\n\n/**\n * @param turboSnapFilter - When provided, only stories whose shotName is in this set\n * will be screenshotted. Stories not in the set are skipped entirely (no Playwright\n * navigation), which is the main performance win of TurboSnap.\n */\nexport const createShots = async (turboSnapFilter?: Set<string>) => {\n const { ladleShots, histoireShots, storybookShots, pageShots, customShots, imagePathCurrent } =\n config;\n let storybookShotItems: ShotItem[] = [];\n let ladleShotItems: ShotItem[] = [];\n let histoireShotItems: ShotItem[] = [];\n let pageShotItems: ShotItem[] = [];\n let customShotItems: ShotItem[] = [];\n\n removeFilesInFolder(imagePathCurrent);\n\n if (!isPlatformModeConfig(config)) {\n removeFilesInFolder(config.imagePathDifference);\n }\n\n const browsers = getBrowsers();\n\n if (ladleShots) {\n const { ladleUrl, mask } = ladleShots;\n\n log.process(\"info\", \"general\", `\\n=== [Ladle Mode] ${ladleUrl} ===\\n`);\n\n let ladleWebUrl = ladleUrl;\n let localServer: undefined | Server;\n\n if (!ladleUrl.startsWith(\"http://\") && !ladleUrl.startsWith(\"https://\")) {\n const staticWebServer = await launchStaticWebServer(ladleUrl);\n\n ladleWebUrl = staticWebServer.url;\n localServer = staticWebServer.server;\n }\n\n try {\n const collection = await collectLadleStories(ladleWebUrl);\n\n if (!collection || collection.length === 0) {\n throw new Error(\"Error: Stories not found\");\n }\n\n log.process(\"info\", \"general\", `Found ${collection.length} ladle stories`);\n\n await mapLimit(browsers, 1, async (browser: BrowserType) => {\n const shotItems = generateLadleShotItems(\n ladleWebUrl,\n Boolean(localServer),\n collection,\n mask,\n ladleShots.breakpoints,\n browsers.length > 1 ? browser : undefined,\n );\n\n const filterItemsToCheck =\n \"filterItemsToCheck\" in config ? config.filterItemsToCheck : undefined;\n\n const filteredShotItems = filterItemsToCheck\n ? shotItems.filter((item) => filterItemsToCheck(item))\n : shotItems;\n\n ladleShotItems = shotItems;\n\n log.process(\n \"info\",\n \"general\",\n `Prepared ${filteredShotItems.length} ladle stories for screenshots on ${browser.name()}`,\n );\n\n await takeScreenShots(filteredShotItems, browser);\n });\n\n localServer?.close();\n } catch (error: unknown) {\n localServer?.close();\n throw error;\n }\n\n log.process(\"info\", \"general\", \"Screenshots done!\");\n }\n\n if (histoireShots) {\n const { histoireUrl } = histoireShots;\n\n let localServer;\n let histoireWebUrl: undefined | string;\n\n if (!histoireUrl.startsWith(\"http://\") && !histoireUrl.startsWith(\"https://\")) {\n const staticWebServer = await launchStaticWebServer(histoireUrl);\n\n histoireWebUrl = staticWebServer.url;\n localServer = staticWebServer.server;\n }\n\n if (!histoireWebUrl) {\n throw new Error(\"Error: Histoire web url not found\");\n }\n\n log.process(\"info\", \"general\", `\\n=== [Histoire Mode] ${histoireUrl} ===\\n`);\n\n try {\n const collection = await collectHistoireStories(histoireWebUrl);\n\n if (!collection || collection.length === 0) {\n throw new Error(\"Error: Stories not found\");\n }\n\n log.process(\"info\", \"general\", `Found ${collection.length} Histoire stories`);\n\n await mapLimit(browsers, 1, async (browser: BrowserType) => {\n const shotItems = generateHistoireShotItems(\n histoireWebUrl,\n collection,\n browsers.length > 1 ? browser : undefined,\n );\n\n histoireShotItems = shotItems;\n\n log.process(\n \"info\",\n \"general\",\n `Prepared ${shotItems.length} Histoire stories for screenshots on ${browser.name()}`,\n );\n\n await takeScreenShots(shotItems, browser);\n });\n\n localServer?.close();\n } catch (error: unknown) {\n localServer?.close();\n\n throw error;\n }\n\n log.process(\"info\", \"general\", \"Screenshots done!\");\n }\n\n if (storybookShots) {\n const { storybookUrl, mask } = storybookShots;\n\n log.process(\"info\", \"general\", `\\n=== [Storybook Mode] ${storybookUrl} ===\\n`);\n\n let storybookWebUrl = storybookUrl;\n let localServer;\n\n if (!storybookUrl.startsWith(\"http://\") && !storybookUrl.startsWith(\"https://\")) {\n const staticWebServer = await launchStaticWebServer(storybookUrl);\n\n storybookWebUrl = staticWebServer.url;\n localServer = staticWebServer.server;\n }\n\n try {\n const collection = await collectStories(storybookWebUrl);\n\n if (!collection?.stories || collection.stories.length === 0) {\n throw new Error(\"Error: Stories not found\");\n }\n\n log.process(\"info\", \"general\", `Found ${collection.stories.length} stories`);\n\n await mapLimit(browsers, 1, async (browser: BrowserType) => {\n const shotItems = generateStorybookShotItems(\n storybookWebUrl,\n collection.stories!,\n mask,\n storybookShots.breakpoints,\n browsers.length > 1 ? browser : undefined,\n );\n\n const filterItemsToCheck =\n \"filterItemsToCheck\" in config ? config.filterItemsToCheck : undefined;\n\n let filteredShotItems = filterItemsToCheck\n ? shotItems.filter((item) => filterItemsToCheck(item))\n : shotItems;\n\n // TurboSnap: skip stories that aren't affected by code changes\n if (turboSnapFilter) {\n filteredShotItems = filteredShotItems.filter(\n (item) =>\n turboSnapFilter.has(item.shotName) ||\n turboSnapFilter.has(`${item.shotMode}/${item.shotName}`),\n );\n }\n\n storybookShotItems = shotItems; // Keep full list for metadata upload\n const capturedItems = filteredShotItems;\n\n log.process(\n \"info\",\n \"general\",\n turboSnapFilter\n ? `Prepared ${capturedItems.length}/${shotItems.length} stories for screenshots on ${browser.name()} (TurboSnap: ${shotItems.length - capturedItems.length} skipped)`\n : `Prepared ${capturedItems.length} stories for screenshots on ${browser.name()}`,\n );\n\n await takeScreenShots(capturedItems, browser);\n });\n\n localServer?.close();\n } catch (error: unknown) {\n localServer?.close();\n throw error;\n }\n\n log.process(\"info\", \"general\", \"Screenshots done!\");\n }\n\n if (pageShots) {\n const { pages: pagesFromConfig, baseUrl, mask, breakpoints } = pageShots;\n\n const pagesFromLoader = await getPagesFromExternalLoader();\n\n let jsonPages: PageScreenshotParameter[] = pagesFromLoader || [];\n\n if (config.pageShots?.pagesJsonRefiner) {\n log.process(\n \"info\",\n \"general\",\n `🧬 Refining pages received in json with function provided in pagesJsonRefiner`,\n );\n\n jsonPages = config.pageShots.pagesJsonRefiner(pagesFromLoader || []);\n }\n\n if (jsonPages.length > 0) {\n log.process(\"info\", \"general\", `Found ${jsonPages.length} pages from external loader`);\n }\n\n const pages = [...(pagesFromConfig || []), ...(jsonPages || [])];\n\n log.process(\"info\", \"general\", `\\n=== [Page Mode] ${baseUrl} ===\\n`);\n\n await mapLimit(browsers, 1, async (browser: BrowserType) => {\n const shotItems = generatePageShotItems(\n pages,\n baseUrl,\n mask,\n breakpoints,\n browsers.length > 1 ? browser : undefined,\n );\n\n pageShotItems = shotItems;\n\n log.process(\n \"info\",\n \"general\",\n `Prepared ${shotItems.length} pages for screenshots on ${browser.name()}`,\n );\n\n await takeScreenShots(shotItems, browser);\n });\n\n log.process(\"info\", \"general\", \"Screenshots done!\");\n }\n\n if (customShots) {\n const { currentShotsPath } = customShots;\n\n log.process(\"info\", \"general\", `\\n=== [Custom Mode] ${currentShotsPath} ===\\n`);\n\n customShotItems = readDirIntoShotItems(currentShotsPath);\n log.process(\"info\", \"general\", `Found ${customShotItems.length} custom shots`);\n }\n\n return [\n ...storybookShotItems,\n ...pageShotItems,\n ...ladleShotItems,\n ...histoireShotItems,\n ...customShotItems,\n ];\n};\n","import { execSync } from \"node:child_process\";\nimport { log } from \"./log.js\";\n\nconst INITIAL_BATCH_SIZE = 20;\n\n/**\n * Execute a git command and return the trimmed output.\n */\nconst execGit = (command: string): string => {\n try {\n return execSync(command, { encoding: \"utf-8\", timeout: 10_000 }).trim();\n } catch {\n return \"\";\n }\n};\n\n/**\n * Get the timestamp (in seconds since epoch) of the earliest build in the\n * project so we don't walk the entire git history.\n */\ntype HasBuildsWithCommitsFn = (commits: string[]) => Promise<string[]>;\n\n/**\n * Find the \"covering\" set of ancestor commits that have builds on the server.\n *\n * This mirrors Chromatic's approach:\n * 1. Walk git history with `git rev-list`\n * 2. Ask the server which commits have builds\n * 3. Use `--not` to exclude ancestors of commits with builds\n * 4. Repeat with exponentially larger batches until no more uncovered commits\n *\n * The result is the minimal set of ancestor commits with builds such that\n * every ancestor of HEAD either has a build or is an ancestor of a commit\n * with a build.\n */\nexport const getParentCommits = async (\n hasBuildsWithCommits: HasBuildsWithCommitsFn,\n): Promise<string[]> => {\n // Check if we're in a git repo at all\n const headCommit = execGit(\"git rev-parse HEAD\");\n if (!headCommit) {\n log.process(\"info\", \"general\", \"Not a git repository, skipping ancestor detection\");\n return [];\n }\n\n // Check if there's at least one parent commit\n const hasParent = execGit('git --no-pager log -n 1 --skip=1 --format=\"%H\"');\n if (!hasParent) {\n log.process(\"info\", \"general\", \"Initial commit, no ancestors\");\n return [];\n }\n\n let commitsWithBuilds: string[] = [];\n let commitsWithoutBuilds: string[] = [];\n let limit = INITIAL_BATCH_SIZE;\n\n for (;;) {\n // Get the next batch of candidate commits\n // `--not commitsWithBuilds` excludes ancestors of commits that already have builds\n const notArgs = commitsWithBuilds.map((c) => c.trim()).join(\" \");\n const command = `git rev-list HEAD -n ${limit + commitsWithoutBuilds.length}${notArgs ? ` --not ${notArgs}` : \"\"}`;\n\n const output = execGit(command);\n const allCommits = output ? output.split(\"\\n\").filter(Boolean) : [];\n\n // Filter out commits we already know about\n const candidates = allCommits\n .filter((c) => !commitsWithBuilds.includes(c))\n .filter((c) => !commitsWithoutBuilds.includes(c))\n .slice(0, limit);\n\n if (candidates.length === 0) {\n // No more uncovered commits — we're done\n break;\n }\n\n log.process(\n \"info\",\n \"general\",\n `🔍 Checking ${candidates.length} commits for builds (batch size ${limit})`,\n );\n\n // Ask server which of these have builds\n const newCommitsWithBuilds = await hasBuildsWithCommits(candidates);\n const newCommitsWithoutBuilds = candidates.filter((c) => !newCommitsWithBuilds.includes(c));\n\n commitsWithBuilds = [...commitsWithBuilds, ...newCommitsWithBuilds];\n commitsWithoutBuilds = [...commitsWithoutBuilds, ...newCommitsWithoutBuilds];\n\n // Exponentially increase batch size\n limit *= 2;\n\n // Safety limit — don't walk more than 10000 commits\n if (commitsWithoutBuilds.length > 10000) {\n log.process(\"info\", \"general\", \"Reached max history depth (10000 commits)\");\n break;\n }\n }\n\n if (commitsWithBuilds.length === 0) {\n log.process(\"info\", \"general\", \"No ancestor builds found — this may be the first build\");\n return [];\n }\n\n // Find the maximally descendent commits — remove any that are ancestors of others\n // This gives us the minimal covering set\n if (commitsWithBuilds.length > 1) {\n const parentArgs = commitsWithBuilds.map((c) => `\"${c}^@\"`).join(\" \");\n const maxOutput = execGit(`git rev-list ${commitsWithBuilds.join(\" \")} --not ${parentArgs}`);\n const maxCommits = maxOutput ? maxOutput.split(\"\\n\").filter(Boolean) : [];\n if (maxCommits.length > 0) {\n commitsWithBuilds = maxCommits;\n }\n }\n\n log.process(\n \"info\",\n \"general\",\n `📌 Found ${commitsWithBuilds.length} ancestor build(s): ${commitsWithBuilds.map((c) => c.slice(0, 7)).join(\", \")}`,\n );\n\n return commitsWithBuilds;\n};\n","/**\n * Typed oRPC client factory.\n * Used by CLI and frontend to create a type-safe client.\n *\n * Uses RPCLink which speaks the oRPC RPC wire format.\n * The server mounts both OpenAPIHandler (for .route()-defined REST endpoints)\n * and RPCHandler (fallback for all procedures). The RPCLink client always\n * hits the RPCHandler path, ensuring compatibility during the OpenAPI migration.\n */\nimport { createORPCClient } from \"@orpc/client\";\nimport { RPCLink } from \"@orpc/client/fetch\";\nconst resolveBaseUrl = (url) => {\n if (url.startsWith(\"http\"))\n return url;\n if (typeof window !== \"undefined\")\n return `${window.location.origin}${url}`;\n return url;\n};\nconst isBrowser = typeof window !== \"undefined\";\nexport const createTypedClient = (options) => {\n const link = new RPCLink({\n url: () => `${resolveBaseUrl(options.url)}/rpc`,\n // Browser uses cookies; CLI/CI uses Authorization / x-api-key headers\n fetch: (input, init) => {\n const signal = options.fetchTimeout != null ? AbortSignal.timeout(options.fetchTimeout) : undefined;\n return fetch(input, {\n ...init,\n ...(isBrowser ? { credentials: \"include\" } : {}),\n ...(signal ? { signal } : {}),\n });\n },\n headers: () => {\n const headers = {};\n if (options.apiToken) {\n headers.authorization = `Bearer ${options.apiToken}`;\n }\n if (options.apiKey) {\n headers[\"x-api-key\"] = options.apiKey;\n }\n return headers;\n },\n });\n return createORPCClient(link);\n};\n","import { readFileSync } from \"node:fs\";\nimport { retry } from \"async\";\nimport { createTypedClient } from \"@gfxlabs/third-eye-shared/client\";\nimport type {\n GetApiTokenOutput,\n BuildInitOutput,\n CheckCacheOutput,\n PrepareUploadOutput,\n ProcessShotsOutput,\n GetAffectedStoriesOutput,\n} from \"@gfxlabs/third-eye-shared\";\nimport { log, logMemory } from \"./log.js\";\nimport type { PlatformModeConfig } from \"./config.js\";\n\nexport type ShotConfig = {\n name: string;\n threshold?: number;\n};\n\nconst createClient = (platformUrl: string, apiKey?: string, apiToken?: string) =>\n createTypedClient({ url: platformUrl, apiKey, apiToken });\n\n// Retry wrapper: 3 attempts with exponential backoff\nconst withRetry = async <T>(\n action: string,\n fn: () => Promise<T>,\n logger: typeof log.process = log.process,\n): Promise<T> => {\n logger(\"info\", \"api\", `Sending to API [${action}]`);\n\n const result = await retry(\n {\n times: 3,\n interval(retryCount) {\n const delay = Math.round(2 ** retryCount * 3000 * Math.random());\n\n logger(\"info\", \"api\", `Retry attempt ${retryCount} in ${delay}ms [${action}]`);\n\n return delay;\n },\n },\n async () => fn(),\n );\n\n logger(\"info\", \"api\", `Successfully sent to API [${action}]`);\n\n return result;\n};\n\nexport const getApiToken = async (config: PlatformModeConfig): Promise<GetApiTokenOutput> => {\n const client = createClient(config.thirdEyePlatform, config.apiKey);\n\n try {\n return await withRetry(\"getApiToken\", () =>\n client.auth.getApiToken({\n projectId: config.thirdEyeProjectId,\n orgId: config.thirdEyeOrgId,\n }),\n );\n } catch (error: unknown) {\n if (error instanceof Error) {\n log.process(\"error\", \"api\", error.message);\n } else {\n log.process(\"error\", \"api\", error);\n }\n\n process.exit(1);\n }\n};\n\nexport const sendInitToAPI = async (\n config: PlatformModeConfig,\n apiToken: string,\n parentCommits?: string[],\n): Promise<BuildInitOutput> => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n\n return withRetry(\"init\", () =>\n client.orgs.projects.builds.init({\n orgId: config.thirdEyeOrgId,\n projectId: config.thirdEyeProjectId,\n commit: config.commitHash,\n branchName: config.commitRefName,\n buildNumber: config.ciBuildNumber,\n baseBranch: config.baseBranch || undefined,\n prNumber: config.prNumber,\n parentCommits,\n }),\n );\n};\n\nexport const sendHasBuildsWithCommitsToAPI = async (\n config: PlatformModeConfig,\n apiToken: string,\n commits: string[],\n): Promise<string[]> => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n\n const result = await withRetry(\"hasBuildsWithCommits\", () =>\n client.orgs.projects.builds.hasBuildsWithCommits({\n orgId: config.thirdEyeOrgId,\n projectId: config.thirdEyeProjectId,\n commits,\n }),\n );\n\n return result.commits;\n};\n\nexport const sendFinalizeToAPI = async (\n config: PlatformModeConfig,\n apiToken: string,\n): Promise<unknown> => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n\n return withRetry(\"finalize\", () =>\n client.orgs.projects.builds.finalize({\n orgId: config.thirdEyeOrgId,\n projectId: config.thirdEyeProjectId,\n branchName: config.commitRefName,\n commit: config.commitHash,\n buildNumber: config.ciBuildNumber,\n }),\n );\n};\n\nexport const sendCheckCacheToAPI = async (\n config: PlatformModeConfig,\n apiToken: string,\n cacheKey: string,\n): Promise<CheckCacheOutput> => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n\n return withRetry(\"checkCache\", () =>\n client.orgs.projects.builds.checkCache({\n orgId: config.thirdEyeOrgId,\n projectId: config.thirdEyeProjectId,\n cacheKey,\n }),\n );\n};\n\nexport const prepareUpload = async (\n config: PlatformModeConfig,\n apiToken: string,\n shotNamesWithHashes: Array<{ name: string; hash: string }>,\n cacheKey?: string,\n): Promise<PrepareUploadOutput> => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n\n return withRetry(\"prepareUpload\", () =>\n client.orgs.projects.uploads.prepare({\n orgId: config.thirdEyeOrgId,\n projectId: config.thirdEyeProjectId,\n branchName: config.commitRefName,\n commit: config.commitHash,\n buildNumber: config.ciBuildNumber,\n currentShots: shotNamesWithHashes,\n cacheKey,\n }),\n );\n};\n\nexport const uploadShot = async ({\n config,\n apiToken,\n uploadToken,\n name,\n file,\n shotMode,\n componentPath,\n storyName,\n storyId,\n importPath,\n metadata,\n logger,\n}: {\n config: PlatformModeConfig;\n apiToken: string;\n uploadToken: string;\n name: string;\n file: string;\n shotMode?: string;\n componentPath?: string;\n storyName?: string;\n storyId?: string;\n importPath?: string;\n metadata?: Record<string, unknown>;\n logger?: ReturnType<typeof log.item>;\n}) => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n const logFn = logger?.process ?? log.process;\n\n const fileBuffer = readFileSync(file);\n const fileData = fileBuffer.toString(\"base64\");\n\n return withRetry(\n \"uploadShot\",\n () =>\n client.orgs.projects.uploads.uploadShot({\n orgId: config.thirdEyeOrgId,\n uploadToken,\n name,\n fileData,\n shotMode,\n componentPath,\n storyName,\n storyId,\n importPath,\n metadata,\n }),\n logFn,\n );\n};\n\nexport const processShots = async (\n config: PlatformModeConfig,\n apiToken: string,\n uploadToken: string,\n shotsConfig?: ShotConfig[],\n cacheKey?: string,\n): Promise<ProcessShotsOutput> => {\n // processShots uses a dedicated client with a 10-minute fetch timeout.\n // It is NOT retried because the server deletes existing comparisons before\n // creating new ones (idempotent). Retrying would discard server-side progress.\n const client = createTypedClient({\n url: config.thirdEyePlatform,\n apiToken,\n fetchTimeout: 600_000, // 10 minutes — server compares every image pair sequentially\n });\n\n log.process(\"info\", \"api\", \"Sending to API [processShots]\");\n\n const result = await client.orgs.projects.builds.processShots({\n orgId: config.thirdEyeOrgId,\n uploadToken,\n config: {\n shots: shotsConfig,\n threshold: config.threshold,\n },\n log: logMemory,\n cacheKey,\n });\n\n log.process(\"info\", \"api\", \"Successfully sent to API [processShots]\");\n\n return result;\n};\n\nexport const sendRecordLogsToAPI = async (_config: PlatformModeConfig, _apiToken: string) => {\n // Logs are sent as part of processShots\n log.process(\"info\", \"api\", \"Logs recorded with build\");\n};\n\nexport const getAffectedStories = async (\n config: PlatformModeConfig,\n apiToken: string,\n changedFiles: string[],\n): Promise<GetAffectedStoriesOutput> => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n\n return withRetry(\"getAffectedStories\", () =>\n client.orgs.projects.builds.getAffectedStories({\n orgId: config.thirdEyeOrgId,\n projectId: config.thirdEyeProjectId,\n changedFiles,\n }),\n );\n};\n\nexport const uploadStorybookArchive = async (\n config: PlatformModeConfig,\n apiToken: string,\n projectId: string,\n buildId: string,\n archive: string,\n) => {\n const client = createClient(config.thirdEyePlatform, undefined, apiToken);\n\n return withRetry(\"uploadStorybookArchive\", () =>\n client.orgs.projects.storybook.uploadArchive({\n orgId: config.thirdEyeOrgId,\n projectId,\n buildId,\n archive,\n }),\n );\n};\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { mapLimit } from \"async\";\nimport { type PlatformModeConfig } from \"./config.js\";\nimport type { ExtendedShotItem } from \"./types.js\";\nimport { uploadShot } from \"./api.js\";\nimport { log } from \"./log.js\";\nimport { parseHrtimeToSeconds } from \"./utils.js\";\nimport { MEDIA_UPLOAD_CONCURRENCY } from \"./constants.js\";\n\nexport const uploadRequiredShots = async ({\n config,\n apiToken,\n uploadToken,\n requiredFileHashes,\n extendedShotItems,\n dependencyMap,\n}: {\n config: PlatformModeConfig;\n apiToken: string;\n uploadToken: string;\n requiredFileHashes: string[];\n extendedShotItems: ExtendedShotItem[];\n /** TurboSnap dependency map: story importPath → array of dependency file paths */\n dependencyMap?: Map<string, string[]>;\n}) => {\n if (requiredFileHashes.length > 0) {\n log.process(\"info\", \"api\", \"Uploading shots\");\n\n const uploadStart = process.hrtime();\n\n const requiredShotItems = extendedShotItems.filter((shotItem) =>\n requiredFileHashes.includes(shotItem.hash),\n );\n\n await mapLimit<[number, ExtendedShotItem], void>(\n requiredShotItems.entries(),\n MEDIA_UPLOAD_CONCURRENCY,\n async ([index, shotItem]: [number, ExtendedShotItem]) => {\n const logger = log.item({\n shotMode: shotItem.shotMode,\n uniqueItemId: shotItem.shotName,\n itemIndex: index,\n totalItems: requiredShotItems.length,\n });\n\n // Include DOM HTML as base64 in metadata if available\n const htmlPath = shotItem.filePathCurrent.replace(/\\.png$/, \".html\");\n const domHtml = existsSync(htmlPath)\n ? readFileSync(htmlPath).toString(\"base64\")\n : undefined;\n\n await uploadShot({\n config,\n apiToken,\n uploadToken,\n name: `${shotItem.shotMode}/${shotItem.shotName}`,\n file: shotItem.filePathCurrent,\n shotMode: shotItem.shotMode,\n componentPath: shotItem.componentPath,\n storyName: shotItem.storyName,\n storyId: shotItem.storyId,\n importPath: shotItem.importPath,\n metadata: {\n ...(shotItem.storyArgs ? { args: shotItem.storyArgs } : {}),\n ...(shotItem.tags ? { tags: shotItem.tags } : {}),\n ...(shotItem.viewport ? { viewport: shotItem.viewport } : {}),\n ...(shotItem.breakpoint !== undefined ? { breakpoint: shotItem.breakpoint } : {}),\n ...(domHtml ? { dom_html: domHtml } : {}),\n ...(dependencyMap && shotItem.importPath && dependencyMap.has(shotItem.importPath)\n ? { dependencies: dependencyMap.get(shotItem.importPath) }\n : {}),\n },\n logger,\n });\n },\n );\n\n const uploadStop = process.hrtime(uploadStart);\n\n log.process(\"info\", \"api\", `Uploading shots took ${parseHrtimeToSeconds(uploadStop)} seconds`);\n }\n\n return true;\n};\n","/**\n * TurboSnap: static import tracer for determining which stories are affected by code changes.\n *\n * Given a list of changed files (from git diff) and the storybook index (importPath per story),\n * this module traces each story file's transitive imports to determine which stories need\n * re-screenshotting. Stories whose dependency tree does not overlap with any changed file are\n * skipped entirely — they never get launched in Playwright, saving the majority of run time.\n *\n * The import tracer is intentionally simple: it parses `import`/`require` statements with a\n * regex, resolves relative and aliased paths, and follows them recursively with cycle detection.\n * It does not evaluate dynamic imports or re-exports — those are rare in component code and the\n * fallback is always \"capture the story\" (safe default).\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\n// Extensions to try when resolving bare imports (e.g. `./Button` → `./Button.tsx`)\nconst RESOLVE_EXTENSIONS = [\".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\"];\nconst INDEX_FILES = RESOLVE_EXTENSIONS.map((ext) => `index${ext}`);\n\n/**\n * Regex that captures the string literal from import/require statements.\n * Handles:\n * import ... from 'foo'\n * import ... from \"foo\"\n * import 'foo'\n * export ... from 'foo'\n * require('foo')\n */\nconst IMPORT_RE =\n /(?:import\\s+(?:[\\s\\S]*?\\s+from\\s+)?|export\\s+(?:[\\s\\S]*?\\s+from\\s+)?|require\\s*\\()[\"']([^\"']+)[\"']/g;\n\ntype PathAliases = Record<string, string>;\n\n/**\n * Parse tsconfig-style path aliases into a simple prefix → directory map.\n * E.g. `{ \"#/*\": [\"./src/app/*\"] }` → `{ \"#/\": \"/abs/path/src/app/\" }`\n */\nexport const parsePathAliases = (tsconfigPath: string, projectRoot: string): PathAliases => {\n const aliases: PathAliases = {};\n\n try {\n const raw = readFileSync(tsconfigPath, \"utf-8\");\n // Strip comments (// and /* */) for lenient JSON parsing\n const stripped = raw\n .replace(/\\/\\/[^\\n]*/g, \"\")\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\")\n // Strip trailing commas before } or ]\n .replace(/,(\\s*[}\\]])/g, \"$1\");\n const tsconfig = JSON.parse(stripped) as {\n compilerOptions?: { paths?: Record<string, string[]> };\n };\n const paths = tsconfig.compilerOptions?.paths;\n\n if (paths) {\n for (const [pattern, targets] of Object.entries(paths)) {\n if (targets.length > 0) {\n // Convert `#/*` → `#/` and `./src/app/*` → `/abs/path/src/app/`\n const prefix = pattern.replace(/\\*$/, \"\");\n const target = targets[0].replace(/\\*$/, \"\");\n aliases[prefix] = path.resolve(projectRoot, target);\n }\n }\n }\n } catch {\n // No tsconfig or unparseable — aliases will be empty\n }\n\n return aliases;\n};\n\n/**\n * Resolve a single import specifier to an absolute file path.\n * Returns undefined if the import is external (node_modules) or unresolvable.\n */\nconst resolveImport = (\n specifier: string,\n fromFile: string,\n aliases: PathAliases,\n): string | undefined => {\n // Skip node_modules / bare specifiers (react, @storybook/*, etc.)\n if (\n !specifier.startsWith(\".\") &&\n !specifier.startsWith(\"/\") &&\n !Object.keys(aliases).some((prefix) => specifier.startsWith(prefix))\n ) {\n return undefined;\n }\n\n let resolved: string;\n\n // Check aliases\n const matchingAlias = Object.entries(aliases).find(([prefix]) => specifier.startsWith(prefix));\n\n if (matchingAlias) {\n const [prefix, target] = matchingAlias;\n resolved = path.join(target, specifier.slice(prefix.length));\n } else {\n // Relative import\n resolved = path.resolve(path.dirname(fromFile), specifier);\n }\n\n // Try exact path first\n if (existsSync(resolved) && !isDirectory(resolved)) {\n return resolved;\n }\n\n // Try with extensions\n for (const ext of RESOLVE_EXTENSIONS) {\n const withExt = resolved + ext;\n if (existsSync(withExt)) return withExt;\n }\n\n // Try as directory with index file\n for (const indexFile of INDEX_FILES) {\n const withIndex = path.join(resolved, indexFile);\n if (existsSync(withIndex)) return withIndex;\n }\n\n return undefined;\n};\n\nconst isDirectory = (p: string): boolean => {\n try {\n const { statSync } = require(\"node:fs\") as typeof import(\"node:fs\");\n return statSync(p).isDirectory();\n } catch {\n return false;\n }\n};\n\n/**\n * Extract all import specifiers from a source file.\n */\nconst extractImports = (filePath: string): string[] => {\n try {\n const content = readFileSync(filePath, \"utf-8\");\n const imports: string[] = [];\n let match: RegExpExecArray | null;\n\n // Reset regex state\n IMPORT_RE.lastIndex = 0;\n\n while ((match = IMPORT_RE.exec(content)) !== null) {\n if (match[1]) imports.push(match[1]);\n }\n\n return imports;\n } catch {\n return [];\n }\n};\n\n/**\n * Trace all transitive dependencies of a file recursively.\n * Returns a Set of absolute file paths that the file depends on (including itself).\n */\nexport const traceDependencies = (\n entryFile: string,\n projectRoot: string,\n aliases: PathAliases,\n _cache?: Map<string, Set<string>>,\n): Set<string> => {\n const cache = _cache ?? new Map<string, Set<string>>();\n\n // Normalize to absolute\n const absEntry = path.isAbsolute(entryFile) ? entryFile : path.resolve(projectRoot, entryFile);\n\n if (cache.has(absEntry)) return cache.get(absEntry)!;\n\n // Insert a placeholder to detect cycles\n const deps = new Set<string>([absEntry]);\n cache.set(absEntry, deps);\n\n if (!existsSync(absEntry)) return deps;\n\n const importSpecifiers = extractImports(absEntry);\n\n for (const spec of importSpecifiers) {\n const resolved = resolveImport(spec, absEntry, aliases);\n\n if (resolved && !deps.has(resolved)) {\n deps.add(resolved);\n\n // Recurse into transitive deps\n const transitiveDeps = traceDependencies(resolved, projectRoot, aliases, cache);\n\n for (const d of transitiveDeps) {\n deps.add(d);\n }\n }\n }\n\n return deps;\n};\n\nexport type TurboSnapResult = {\n /** Story IDs/names that should be re-captured */\n affected: string[];\n /** Story IDs/names that can be skipped (unchanged) */\n skipped: string[];\n /** Total number of stories */\n total: number;\n /** Dependency map: story import path → set of dependency file paths (relative to project root) */\n dependencyMap: Map<string, string[]>;\n};\n\n/**\n * Determine which stories are affected by a set of changed files.\n *\n * @param stories - Array of { shotName, importPath } from the storybook index\n * @param changedFiles - Relative file paths from `git diff --name-only`\n * @param projectRoot - Absolute path to the project root (for resolving imports)\n * @returns Which stories to capture and which to skip\n */\nexport const getAffectedStoriesLocal = (\n stories: Array<{ shotName: string; importPath?: string }>,\n changedFiles: string[],\n projectRoot: string,\n): TurboSnapResult => {\n const aliases = parsePathAliases(path.join(projectRoot, \"tsconfig.json\"), projectRoot);\n\n // Normalize changed files to absolute paths\n const changedAbsolute = new Set(changedFiles.map((f) => path.resolve(projectRoot, f)));\n\n const affected: string[] = [];\n const skipped: string[] = [];\n const dependencyMap = new Map<string, string[]>();\n const depCache = new Map<string, Set<string>>();\n\n for (const story of stories) {\n if (!story.importPath) {\n // No import path — can't trace dependencies, always capture\n affected.push(story.shotName);\n continue;\n }\n\n // Resolve story import path relative to project root\n // Storybook's importPath is like `./src/components/Button.stories.tsx`\n const storyFile = path.resolve(projectRoot, story.importPath);\n const deps = traceDependencies(storyFile, projectRoot, aliases, depCache);\n\n // Store relative deps for the server cache\n const relativeDeps = [...deps].map((d) => path.relative(projectRoot, d));\n dependencyMap.set(story.importPath, relativeDeps);\n\n // Check if any dependency overlaps with changed files\n let isAffected = false;\n\n for (const dep of deps) {\n if (changedAbsolute.has(dep)) {\n isAffected = true;\n break;\n }\n }\n\n if (isAffected) {\n affected.push(story.shotName);\n } else {\n skipped.push(story.shotName);\n }\n }\n\n return { affected, skipped, total: stories.length, dependencyMap };\n};\n","import { execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport fse from \"fs-extra\";\nimport { checkDifferences } from \"./checkDifferences.js\";\nimport { createShots } from \"./createShots.js\";\nimport {\n createShotsFolders,\n exitProcess,\n hashFile,\n isUpdateMode,\n parseHrtimeToSeconds,\n removeFilesInFolder,\n} from \"./utils.js\";\nimport type { GenerateOnlyModeConfig, PlatformModeConfig } from \"./config.js\";\nimport { getParentCommits } from \"./git.js\";\nimport {\n type ShotConfig,\n getApiToken,\n prepareUpload,\n processShots,\n sendInitToAPI,\n sendHasBuildsWithCommitsToAPI,\n sendRecordLogsToAPI,\n sendCheckCacheToAPI,\n getAffectedStories,\n uploadStorybookArchive,\n} from \"./api.js\";\nimport { log } from \"./log.js\";\nimport type { ExtendedShotItem } from \"./types.js\";\nimport { uploadRequiredShots } from \"./upload.js\";\nimport { getAffectedStoriesLocal } from \"./turbosnap.js\";\n\nconst kebabCase = (str?: string): string =>\n (str ?? \"\")\n .replace(/([a-z\\d])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_/]+/g, \"-\")\n .toLowerCase();\n\n/** Generate the shot name from a storybook title and story name, matching the crawler's logic. */\nconst generateShotName = (title: string, name: string): string =>\n [kebabCase(title), kebabCase(name)].filter(Boolean).join(\"--\");\n\n/**\n * Get the list of files changed between the current HEAD and a base ref\n * using git diff. Returns an empty array if git is unavailable or fails.\n */\nconst getChangedFiles = (baseRef: string): string[] => {\n // Try the ref directly, then with origin/ prefix (CI environments often\n // don't have local branch refs, only remote tracking refs).\n const refsToTry = [baseRef, `origin/${baseRef}`];\n\n for (const ref of refsToTry) {\n try {\n const output = execSync(`git diff --name-only ${ref}...HEAD`, {\n encoding: \"utf-8\",\n timeout: 30_000,\n });\n\n return output\n .trim()\n .split(\"\\n\")\n .filter((f) => f.length > 0);\n } catch {\n // Try next ref\n }\n }\n\n log.process(\"warn\", \"general\", `Failed to get changed files for refs: ${refsToTry.join(\", \")}`);\n\n return [];\n};\n\nexport const runner = async (config: GenerateOnlyModeConfig) => {\n const executionStart = process.hrtime();\n\n try {\n if (isUpdateMode()) {\n log.process(\n \"info\",\n \"general\",\n \"Running third-eye in update mode. Baseline screenshots will be updated\",\n );\n }\n\n log.process(\"info\", \"general\", \"📂 Creating shot folders\");\n const createShotsStart = process.hrtime();\n\n createShotsFolders();\n\n log.process(\"info\", \"general\", \"📸 Creating shots\");\n const shotItems = await createShots();\n\n const createShotsStop = process.hrtime(createShotsStart);\n\n log.process(\n \"info\",\n \"general\",\n `Creating shots took ${parseHrtimeToSeconds(createShotsStop)} seconds`,\n );\n\n if (config.generateOnly && shotItems.length === 0) {\n log.process(\"info\", \"general\", `👋 Exiting process with nothing to compare.`);\n await exitProcess({ shotsNumber: shotItems.length });\n }\n\n log.process(\"info\", \"general\", \"🔍 Checking differences\");\n const checkDifferenceStart = process.hrtime();\n\n const { filterItemsToCheck } = config;\n const filteredShotItems = filterItemsToCheck\n ? shotItems.filter((item) => filterItemsToCheck(item))\n : shotItems;\n const { aboveThresholdDifferenceItems, noBaselinesItems } =\n await checkDifferences(filteredShotItems);\n\n if (isUpdateMode()) {\n // Remove only the files which are no longer present in our shot items\n removeFilesInFolder(\n config.imagePathBaseline,\n shotItems.map((shotItem) => shotItem.filePathBaseline),\n );\n\n // Synchronize differences from both lack of baseline and over threshold difference\n for (const noBaselineItem of noBaselinesItems) {\n fse.copySync(noBaselineItem.filePathCurrent, noBaselineItem.filePathBaseline);\n }\n\n for (const aboveThresholdDifferenceItem of aboveThresholdDifferenceItems) {\n fse.copySync(\n aboveThresholdDifferenceItem.filePathCurrent,\n aboveThresholdDifferenceItem.filePathBaseline,\n );\n }\n }\n\n if (\n (aboveThresholdDifferenceItems.length > 0 || noBaselinesItems.length > 0) &&\n config.failOnDifference\n ) {\n log.process(\n \"info\",\n \"general\",\n `👋 Exiting process with ${aboveThresholdDifferenceItems.length} found differences & ${noBaselinesItems.length} baselines to update`,\n );\n\n if (config.generateOnly) {\n await exitProcess({ shotsNumber: shotItems.length });\n }\n }\n\n const checkDifferenceStop = process.hrtime(checkDifferenceStart);\n\n log.process(\n \"info\",\n \"general\",\n `⏱ Checking differences took ${parseHrtimeToSeconds(checkDifferenceStop)} seconds`,\n );\n\n const executionStop = process.hrtime(executionStart);\n\n log.process(\n \"info\",\n \"general\",\n `⏱ Lost Pixel run took ${parseHrtimeToSeconds(executionStop)} seconds`,\n );\n\n await exitProcess({\n shotsNumber: shotItems.length,\n runDuration: Number(parseHrtimeToSeconds(executionStop)),\n exitCode: 0,\n });\n } catch (error: unknown) {\n const executionStop = process.hrtime(executionStart);\n\n if (error instanceof Error) {\n log.process(\"error\", \"general\", error.message);\n } else {\n log.process(\"error\", \"general\", error);\n }\n\n await exitProcess({\n runDuration: Number(parseHrtimeToSeconds(executionStop)),\n error,\n });\n }\n};\n\nexport const getPlatformApiToken = async (config: PlatformModeConfig) => {\n if (!config.apiKey) {\n log.process(\"error\", \"general\", `Running Lost Pixel in 'platform' mode requires an API key`);\n process.exit(1);\n }\n\n if (isUpdateMode()) {\n log.process(\n \"error\",\n \"general\",\n `Running Lost Pixel in 'update' mode requires the 'generateOnly' option to be set to true`,\n );\n process.exit(1);\n }\n\n try {\n const result = await getApiToken(config);\n\n return result.apiToken;\n } catch (error: unknown) {\n if (error instanceof Error) {\n log.process(\"error\", \"general\", error.message);\n } else {\n log.process(\"error\", \"general\", error);\n }\n\n process.exit(1);\n }\n};\n\nconst checkForCachedBuild = async (config: PlatformModeConfig, apiToken: string) => {\n if (process.env.THIRD_EYE_CACHE_KEY) {\n log.process(\"info\", \"general\", `♻️ Using cache key ${process.env.THIRD_EYE_CACHE_KEY}`);\n\n const { cacheExists } = await sendCheckCacheToAPI(\n config,\n apiToken,\n process.env.THIRD_EYE_CACHE_KEY,\n );\n\n if (cacheExists) {\n log.process(\n \"info\",\n \"general\",\n `♻️ Cache hit for key ${process.env.THIRD_EYE_CACHE_KEY} - Skipping shot creation`,\n );\n\n const { uploadToken } = await prepareUpload(\n config,\n apiToken,\n [],\n process.env.THIRD_EYE_CACHE_KEY,\n );\n\n await processShots(config, apiToken, uploadToken, [], process.env.THIRD_EYE_CACHE_KEY);\n\n return true;\n }\n\n log.process(\"info\", \"general\", `♻️ Cache miss for key ${process.env.THIRD_EYE_CACHE_KEY}`);\n }\n\n return false;\n};\n\nexport const platformRunner = async (config: PlatformModeConfig, apiToken: string) => {\n const executionStart = process.hrtime();\n\n try {\n log.process(\n \"info\",\n \"general\",\n [\n \"📀 Using details:\",\n `ciBuildId = ${config.ciBuildId}`,\n `ciBuildNumber = ${config.ciBuildNumber}`,\n `repository = ${config.repository}`,\n `commitRefName = ${config.commitRefName}`,\n `commitHash = ${config.commitHash}`,\n ].join(\"\\n - \"),\n );\n\n // Resolve ancestor builds using git history (Chromatic-style baseline resolution)\n log.process(\"info\", \"general\", \"🔍 Resolving ancestor builds from git history...\");\n const parentCommits = await getParentCommits((commits) =>\n sendHasBuildsWithCommitsToAPI(config, apiToken, commits),\n );\n\n await sendInitToAPI(config, apiToken, parentCommits);\n\n const foundCache = await checkForCachedBuild(config, apiToken);\n\n if (!foundCache) {\n log.process(\"info\", \"general\", \"📂 Creating shot folders\");\n const createShotsStart = process.hrtime();\n\n createShotsFolders();\n\n // TurboSnap: determine which stories need re-capturing BEFORE launching Playwright.\n // This reads the storybook index.json from disk, traces each story's import dependencies,\n // and only passes affected stories to createShots — skipping Playwright entirely for\n // unchanged stories.\n let turboSnapFilter: Set<string> | undefined;\n let turboSnapDependencyMap: Map<string, string[]> | undefined;\n\n if (config.turboSnap && config.baseBranch) {\n log.process(\n \"info\",\n \"general\",\n `⚡ TurboSnap enabled, checking changed files against ${config.baseBranch}`,\n );\n\n const changedFiles = getChangedFiles(config.baseBranch);\n\n if (changedFiles.length > 0) {\n log.process(\"info\", \"general\", `Found ${changedFiles.length} changed file(s)`);\n\n try {\n // Read storybook index.json directly from disk (no browser needed)\n const storybookPath = config.storybookShots?.storybookUrl ?? \"\";\n const indexJsonPath = path.join(\n storybookPath.startsWith(\"http\") ? \"\" : storybookPath,\n \"index.json\",\n );\n\n if (existsSync(indexJsonPath)) {\n const indexJson = JSON.parse(fse.readFileSync(indexJsonPath, \"utf-8\")) as {\n entries?: Record<\n string,\n { id: string; title: string; name: string; importPath?: string; type?: string }\n >;\n stories?: Record<\n string,\n { id: string; title: string; name: string; importPath?: string; type?: string }\n >;\n };\n const entries = indexJson.entries ?? indexJson.stories ?? {};\n const stories = Object.values(entries)\n .filter((e) => e.type !== \"docs\")\n .map((e) => ({\n shotName: generateShotName(e.title, e.name),\n importPath: e.importPath,\n }));\n\n const turboResult = getAffectedStoriesLocal(stories, changedFiles, process.cwd());\n\n log.process(\n \"info\",\n \"general\",\n `⚡ TurboSnap: ${turboResult.affected.length} affected, ${turboResult.skipped.length} skipped out of ${turboResult.total} stories`,\n );\n\n turboSnapFilter = new Set(turboResult.affected);\n turboSnapDependencyMap = turboResult.dependencyMap;\n } else {\n // Fall back to server-side TurboSnap if index.json is not on disk\n // (e.g. when storybookUrl is an HTTP URL)\n try {\n const serverResult = await getAffectedStories(config, apiToken, changedFiles);\n\n log.process(\n \"info\",\n \"general\",\n `⚡ TurboSnap (server): ${serverResult.affectedCount} affected, ${serverResult.skippedCount} skipped`,\n );\n\n if (serverResult.affected.length > 0) {\n turboSnapFilter = new Set(serverResult.affected);\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n log.process(\n \"warn\",\n \"general\",\n `TurboSnap server query failed, capturing all stories: ${error.message}`,\n );\n }\n }\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n log.process(\n \"warn\",\n \"general\",\n `TurboSnap filtering failed, capturing all stories: ${error.message}`,\n );\n }\n }\n } else {\n log.process(\n \"info\",\n \"general\",\n \"TurboSnap: no changed files detected, capturing all stories\",\n );\n }\n }\n\n log.process(\"info\", \"general\", \"📸 Creating shots\");\n let shotItems = await createShots(turboSnapFilter);\n\n const shotNames = shotItems.map((shotItem) => shotItem.shotName);\n const uniqueShotNames = new Set(shotNames);\n\n if (shotNames.length !== uniqueShotNames.size) {\n const duplicates: string[] = shotNames.filter(\n (shotName) => shotNames.filter((item) => item === shotName).length > 1,\n );\n\n throw new Error(\n `Error: Shot names must be unique (check for duplicate Story names: [ ${[\n ...new Set(duplicates),\n ].join(\", \")} ])`,\n );\n }\n\n const createShotsStop = process.hrtime(createShotsStart);\n\n log.process(\n \"info\",\n \"general\",\n `⏱ Creating shots took ${parseHrtimeToSeconds(createShotsStop)} seconds`,\n );\n\n // Filter out shots whose files don't exist (e.g. stories that render nothing)\n const validShotItems = shotItems.filter((shotItem) => {\n if (!existsSync(shotItem.filePathCurrent)) {\n log.process(\n \"warn\",\n \"general\",\n `⚠️ Skipping shot '${shotItem.shotName}' — file not found: ${shotItem.filePathCurrent}`,\n );\n return false;\n }\n return true;\n });\n\n const extendedShotItems: ExtendedShotItem[] = validShotItems.map((shotItem) => ({\n ...shotItem,\n uniqueName: `${shotItem.shotMode}/${shotItem.shotName}`,\n hash: hashFile(shotItem.filePathCurrent),\n }));\n\n const { buildId, requiredFileHashes, uploadToken } = await prepareUpload(\n config,\n apiToken,\n extendedShotItems.map((shotItem) => ({\n name: shotItem.uniqueName,\n hash: shotItem.hash,\n storyId: shotItem.storyId,\n })),\n );\n\n log.process(\n \"info\",\n \"general\",\n [\n `🏙 `,\n `${shotItems.length} shot(s) in total.`,\n `${shotItems.length - requiredFileHashes.length} shot(s) already exist on platform.`,\n `${requiredFileHashes.length} shot(s) will be uploaded.`,\n ].join(\" \"),\n );\n\n await uploadRequiredShots({\n config,\n apiToken,\n uploadToken,\n requiredFileHashes,\n extendedShotItems,\n dependencyMap: turboSnapDependencyMap,\n });\n\n const shotsConfig: ShotConfig[] = shotItems.map((shotItem) => ({\n name: `${shotItem.shotMode}/${shotItem.shotName}`,\n threshold: shotItem.threshold,\n }));\n\n await processShots(\n config,\n apiToken,\n uploadToken,\n shotsConfig,\n process.env.THIRD_EYE_CACHE_KEY,\n );\n\n // Upload storybook-static if configured (for hosted storybook feature)\n if (config.storybookStaticDir && existsSync(config.storybookStaticDir)) {\n log.process(\"info\", \"general\", \"Uploading Storybook archive...\");\n\n // Create tar.gz of the storybook-static directory\n const archive = execSync(`tar -czf - -C ${JSON.stringify(config.storybookStaticDir)} .`, {\n maxBuffer: 50 * 1024 * 1024,\n }).toString(\"base64\");\n\n const result = await uploadStorybookArchive(\n config,\n apiToken,\n config.thirdEyeProjectId,\n buildId,\n archive,\n );\n\n log.process(\n \"info\",\n \"general\",\n `Storybook uploaded (${result.fileCount} files, ${Math.round(result.totalSizeBytes / 1024)} KB)`,\n );\n log.process(\"info\", \"general\", `Storybook URL: ${result.url}`);\n }\n }\n\n const executionStop = process.hrtime(executionStart);\n\n log.process(\n \"info\",\n \"general\",\n `⏱ Lost Pixel run took ${parseHrtimeToSeconds(executionStop)} seconds`,\n );\n } catch (error: unknown) {\n if (error instanceof Error) {\n log.process(\"error\", \"general\", error.message);\n } else {\n log.process(\"error\", \"general\", error);\n }\n\n log.process(\"info\", \"general\", \"🪵 Sending logs to platform.\");\n\n await sendRecordLogsToAPI(config, apiToken);\n\n process.exit(1);\n }\n};\n","import { execa } from \"execa\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { isLocalDebugMode, isUpdateMode, shallGenerateMeta } from \"../utils.js\";\n\ntype ParsedYargs = {\n configDir: \"string\";\n};\n\nexport const executeDockerRun = async ({ version }: { version: string }) => {\n const isUpdateModeEnabled = isUpdateMode();\n const isGenerateMetaEnabled = shallGenerateMeta();\n const isLocalDebugModeEnabled = isLocalDebugMode();\n\n const argv = yargs(hideBin(process.argv)).parseSync() as unknown as ParsedYargs;\n\n const args = [\n \"run\",\n \"--rm\",\n // TODO: remove interactive mode for now, while it clashes with Tauri execution\n // '-it',\n `-v ${process.cwd()}:${process.cwd()}`,\n `-e WORKSPACE=${process.cwd()}`,\n \"-e DOCKER=1\",\n `-e THIRD_EYE_DISABLE_TELEMETRY=${process.env.THIRD_EYE_DISABLE_TELEMETRY}`,\n argv.configDir ? `-e THIRD_EYE_CONFIG_DIR=${argv.configDir}` : \"\",\n isUpdateModeEnabled ? \"-e THIRD_EYE_MODE=update\" : \"\",\n isGenerateMetaEnabled ? \"-e THIRD_EYE_GENERATE_META=true\" : \"\",\n isLocalDebugModeEnabled ? \"-e THIRD_EYE_LOCAL=true\" : \"\",\n `thirdeye/third-eye:v${version}`,\n ];\n\n return execa(\"docker\", args, { shell: true, stdio: \"inherit\" });\n};\n","import { log } from \"../log.js\";\nimport { getVersion } from \"../utils.js\";\nimport { executeDockerRun } from \"./utils.js\";\n\nexport const runInDocker = async () => {\n const version = getVersion();\n\n if (version) {\n log.process(\"info\", \"general\", `Running in docker: third-eye:${version}`);\n\n try {\n await executeDockerRun({ version });\n } catch (error: unknown) {\n log.process(\"error\", \"general\", error);\n }\n } else {\n log.process(\n \"error\",\n \"config\",\n \"Seems like third-eye is missing in your package.json. Running third-eye@latest\",\n );\n }\n};\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport axios from \"axios\";\nimport { XMLParser } from \"fast-xml-parser\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { log } from \"./log.js\";\nimport { type PageScreenshotParameter, PageScreenshotParameterSchema } from \"./config.js\";\n\ntype SitemapParserOptions = {\n outputPath: string;\n};\n\ntype SitemapUrlEntry = {\n loc: string[];\n lastmod: string[];\n};\n\ntype Sitemap = {\n urlset: {\n $: {\n xmlns: string;\n };\n url: SitemapUrlEntry[];\n };\n};\n\nasync function fetchSitemap(url: string): Promise<string> {\n if (url.startsWith(\"http\")) {\n const response = await axios.get(url);\n\n return response.data as string;\n }\n\n return readFileSync(url, \"utf8\");\n}\n\nasync function parseSitemap(sitemapContent: string): Promise<string[]> {\n const parser = new XMLParser({\n isArray: (_tagName, jPath) =>\n typeof jPath === \"string\" &&\n (jPath === \"urlset.url\" || jPath === \"urlset.url.loc\" || jPath === \"urlset.url.lastmod\"),\n });\n const result: Sitemap = parser.parse(sitemapContent) as Sitemap;\n\n if (!result.urlset || !Array.isArray(result.urlset.url)) {\n throw new Error(\"Invalid sitemap format\");\n }\n\n return result.urlset.url\n .filter((urlEntry: SitemapUrlEntry) => urlEntry.loc && urlEntry.loc.length > 0)\n .map((urlEntry: SitemapUrlEntry) => urlEntry.loc[0]);\n}\n\nasync function generatePagesFileFromSitemap(\n url: string,\n options: SitemapParserOptions,\n): Promise<void> {\n try {\n const sitemapContent = await fetchSitemap(url);\n const urls = await parseSitemap(sitemapContent);\n\n const pages: PageScreenshotParameter[] = urls.map((url) => {\n const page: PageScreenshotParameter = PageScreenshotParameterSchema.parse({\n path: new URL(url).pathname, // Extract the path from the URL\n name: url.replace(/^https?:\\/\\/(www\\.)?|^www\\./g, \"\").replace(/\\//g, \"_\"),\n });\n\n return page;\n });\n\n writeFileSync(options.outputPath, JSON.stringify(pages, null, 2));\n log.process(\"info\", \"general\", \"✅ Pages file generated successfully at\", options.outputPath);\n } catch (error) {\n log.process(\n \"error\",\n \"general\",\n \"❌ Pages file generation errored out. Please check the error message below\",\n error,\n );\n }\n}\n\nexport const generatePagesFromSitemap = async () => {\n const argv = await yargs(hideBin(process.argv))\n .usage(\"Usage: $0 <command> <sitemapUrl> <outputPath>\")\n .command(\"page-sitemap-gen <sitemapUrl> <outputPath>\", \"Generate pages file from sitemap\")\n .demandCommand(1).argv;\n\n const { sitemapUrl, outputPath } = argv;\n\n if (\n !sitemapUrl ||\n typeof sitemapUrl !== \"string\" ||\n !outputPath ||\n typeof outputPath !== \"string\"\n ) {\n log.process(\"error\", \"general\", \"❌ sitemapUrl and outputPath are required\");\n\n return;\n }\n\n log.process(\n \"info\",\n \"general\",\n `🧬 Running third-eye in sitemap-page-gen mode. Pages file will be generated from provided sitemap on ${sitemapUrl}`,\n );\n await generatePagesFileFromSitemap(sitemapUrl, {\n outputPath,\n });\n};\n","#!/usr/bin/env node\n\nimport path from \"node:path\";\nimport { readFileSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport fs from \"fs-extra\";\nimport { log } from \"./log.js\";\nimport { getPlatformApiToken, platformRunner, runner } from \"./runner.js\";\nimport { getVersion, isDockerMode, isSitemapPageGenMode, isLocalDebugMode } from \"./utils.js\";\nimport { sendFinalizeToAPI } from \"./api.js\";\nimport { config, configure, isPlatformModeConfig } from \"./config.js\";\nimport { runInDocker } from \"./docker-runner/index.js\";\nimport { generatePagesFromSitemap } from \"./generatePagesFromSitemap.js\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\ntype CommandArgs = [\"docker\", \"init-js\", \"init-ts\", \"finalize\"];\n\nconst args = yargs(hideBin(process.argv)).parseSync();\nconst commandArgs = args._ as CommandArgs;\n\nconst version = getVersion();\n\nif (version) {\n log.process(\"info\", \"general\", `Version: ${version}`);\n}\n\n(async () => {\n if (isSitemapPageGenMode()) {\n await generatePagesFromSitemap();\n\n return;\n }\n\n if (isDockerMode()) {\n await runInDocker();\n } else if (commandArgs.includes(\"init-js\")) {\n log.process(\"info\", \"general\", \"Initializing javascript third-eye config\");\n\n await fs.copy(\n path.join(__dirname, \"..\", \"config-templates\", \"example.thirdeye.config.js\"),\n path.join(process.cwd(), \"./thirdeye.config.js\"),\n );\n log.process(\"info\", \"general\", \"✅ Config successfully initialized\");\n } else if (commandArgs.includes(\"init-ts\")) {\n log.process(\"info\", \"general\", \"Initializing typescript third-eye config\");\n\n // Replace local type resolution with module resolution\n const file = fs.readFileSync(\n path.join(__dirname, \"..\", \"config-templates\", \"example.thirdeye.config.ts\"),\n );\n const modifiedFile = file.toString().replace(\"../src/config\", \"third-eye\");\n\n fs.writeFileSync(path.join(process.cwd(), \"./thirdeye.config.ts\"), modifiedFile);\n log.process(\"info\", \"general\", \"✅ Config successfully initialized\");\n } else {\n // Auto-detect PR number from GitHub Actions event payload\n if (process.env.GITHUB_EVENT_PATH && !process.env.THIRD_EYE_PR_NUMBER) {\n try {\n const event = JSON.parse(readFileSync(process.env.GITHUB_EVENT_PATH, \"utf-8\"));\n const prNumber = event.pull_request?.number ?? event.number;\n if (prNumber) {\n process.env.THIRD_EYE_PR_NUMBER = String(prNumber);\n }\n } catch {\n // ignore - event file might not exist or be malformed\n }\n }\n\n await configure({\n localDebugMode: isLocalDebugMode(),\n });\n\n if (isPlatformModeConfig(config)) {\n log.process(\"info\", \"general\", `🚀 Starting Lost Pixel in 'platform' mode`);\n\n const apiToken = await getPlatformApiToken(config);\n\n if (commandArgs.includes(\"finalize\")) {\n await sendFinalizeToAPI(config, apiToken);\n } else {\n await platformRunner(config, apiToken);\n }\n } else {\n log.process(\"info\", \"general\", `🚀 Starting Lost Pixel in 'generateOnly' mode`);\n\n await runner(config);\n }\n }\n})();\n"],"mappings":"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"}
|