@storyshelf/runner-puppeteer 0.8.0 → 0.9.0-next-d2cc082
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/capture-runner.mjs +1 -1
- package/dist/pipeline.mjs +14 -5
- package/dist/pipeline.mjs.map +1 -1
- package/package.json +5 -14
package/dist/capture-runner.mjs
CHANGED
|
@@ -34,7 +34,7 @@ function createPuppeteerCaptureRunner(options = {}) {
|
|
|
34
34
|
return {
|
|
35
35
|
metadata: {
|
|
36
36
|
name: "Puppeteer",
|
|
37
|
-
version: "0.
|
|
37
|
+
version: "0.9.0-next-d2cc082",
|
|
38
38
|
description: "Puppeteer capture runner (chrome-headless-shell)",
|
|
39
39
|
kind: "puppeteer",
|
|
40
40
|
category: "capture-runner",
|
package/dist/pipeline.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { createRuntimeParamsState } from "./runtime-params.mjs";
|
|
|
8
8
|
import { captureScreenshot } from "./screenshot.mjs";
|
|
9
9
|
import { createBrowserSession } from "./session.mjs";
|
|
10
10
|
import { createStaticServer } from "./static-server.mjs";
|
|
11
|
-
import { TargetLaunchError, engineOf } from "@storyshelf/core/adapter/capture-runner";
|
|
11
|
+
import { TargetLaunchError, engineOf, logRemoteSizing } from "@storyshelf/core/adapter/capture-runner";
|
|
12
12
|
import { DEFAULT_RECYCLE_BROWSER_EVERY, chunk, createPageLimiter, describeSizing, detectCaptureEnvironment, isTimeoutError, sessionGateFor, storybookBase } from "@storyshelf/core/utils";
|
|
13
13
|
import { StorybookAdapter, resolveStoryViewports } from "@storyshelf/core/capture";
|
|
14
14
|
// src/pipeline.ts
|
|
@@ -25,10 +25,7 @@ async function renderAll(rawInput, active, driver, defaultTarget) {
|
|
|
25
25
|
let ctx;
|
|
26
26
|
return await finishing(async () => {
|
|
27
27
|
server = await serve(input, session);
|
|
28
|
-
input
|
|
29
|
-
...input,
|
|
30
|
-
browser: engineOf(name)
|
|
31
|
-
}), "capture sizing");
|
|
28
|
+
logHostSizing(input, name, remote);
|
|
32
29
|
const environment = await measureEnvironment(session, remote, input, active.abort?.signal);
|
|
33
30
|
ctx = buildContext(session, server.url, input);
|
|
34
31
|
const { captures, failures } = await runCaptures(input, active, ctx, relaunch);
|
|
@@ -68,6 +65,11 @@ function planRun(rawInput, driver, defaultTarget) {
|
|
|
68
65
|
value: String(value)
|
|
69
66
|
}, "sessions() returned an invalid value; keeping the last good one");
|
|
70
67
|
});
|
|
68
|
+
if (remote) logRemoteSizing(rawInput.logger, name, remote.remote, {
|
|
69
|
+
gate,
|
|
70
|
+
concurrency: rawInput.concurrency,
|
|
71
|
+
pagesPerConnection: false
|
|
72
|
+
});
|
|
71
73
|
return {
|
|
72
74
|
input: {
|
|
73
75
|
...rawInput,
|
|
@@ -236,6 +238,13 @@ function pushRenderFailure(failures, input, storyId, viewportName, error) {
|
|
|
236
238
|
err: error
|
|
237
239
|
}, "render failed for story");
|
|
238
240
|
}
|
|
241
|
+
/** The host's sizing of a capture; a remote target runs elsewhere, so nothing is measured here. */
|
|
242
|
+
function logHostSizing(input, name, remote) {
|
|
243
|
+
if (!remote) input.logger?.info(describeSizing({
|
|
244
|
+
...input,
|
|
245
|
+
browser: engineOf(name)
|
|
246
|
+
}), "capture sizing");
|
|
247
|
+
}
|
|
239
248
|
export { assertReachable, launchContextOf, planRun, renderAll, serve };
|
|
240
249
|
|
|
241
250
|
//# sourceMappingURL=pipeline.mjs.map
|
package/dist/pipeline.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.mjs","names":[],"sources":["../src/pipeline.ts"],"sourcesContent":["/** Render orchestration: static server, browser, and per-story captures. */\n\nimport {\n TargetLaunchError,\n engineOf,\n type CaptureEnvironment,\n type CaptureTarget,\n type RemoteTarget,\n type RenderResult,\n type RenderedSnapshot,\n type StoryEntry,\n type Viewport,\n} from \"@storyshelf/core/adapter/capture-runner\";\nimport { StorybookAdapter, resolveStoryViewports } from \"@storyshelf/core/capture\";\nimport {\n DEFAULT_RECYCLE_BROWSER_EVERY,\n chunk,\n describeSizing,\n createPageLimiter,\n detectCaptureEnvironment,\n isTimeoutError,\n sessionGateFor,\n storybookBase,\n type SessionGate,\n} from \"@storyshelf/core/utils\";\nimport type { Browser } from \"puppeteer-core\";\nimport { abortIfCancelled, finishing, messageOf, outcomeOf, safeCloseServer } from \"./browser.ts\";\nimport { PagePool } from \"./pages.ts\";\nimport { measureRemote } from \"./remote-environment.ts\";\nimport { createRuntimeParamsState } from \"./runtime-params.ts\";\nimport { captureScreenshot } from \"./screenshot.ts\";\nimport { createBrowserSession, type BrowserSession } from \"./session.ts\";\nimport { createStaticServer } from \"./static-server.ts\";\nimport type {\n ActiveRun,\n CaptureContext,\n LaunchContext,\n PuppeteerRenderInput,\n RunInput,\n TargetDriver,\n} from \"./types.ts\";\n\n/** Where the browser finds the Storybook: the loopback server, or the URL core handed over. */\ninterface Serving {\n url: string;\n close(): Promise<void>;\n}\n\n/** Render all story/viewport combinations for a build. */\nexport async function renderAll(\n rawInput: PuppeteerRenderInput,\n active: ActiveRun,\n driver: TargetDriver,\n defaultTarget: CaptureTarget,\n): Promise<RenderResult> {\n const { input, name, remote } = planRun(rawInput, driver, defaultTarget);\n assertReachable(name, remote, input.storybookUrl);\n const relaunch = async (): Promise<BrowserSession> =>\n createBrowserSession(\n await driver.launch(\n name,\n input.logger,\n launchContextOf(input, \"render\", active.abort?.signal),\n ),\n );\n // Launch first: a target that will not start must fail before a static server is bound.\n const session = await relaunch();\n active.session = session;\n await abortIfCancelled(active, session);\n let server: Serving | undefined;\n let ctx: CaptureContext | undefined;\n return await finishing(\n async () => {\n server = await serve(input, session);\n input.logger?.info(describeSizing({ ...input, browser: engineOf(name) }), \"capture sizing\");\n // Measured while the browser is open: Puppeteer cannot read the version of a closed one.\n const environment = await measureEnvironment(session, remote, input, active.abort?.signal);\n ctx = buildContext(session, server.url, input);\n const { captures, failures } = await runCaptures(input, active, ctx, relaunch);\n // Echo the target so persisted rows are attributable without the request.\n for (const row of [...captures, ...failures]) row.browser = name;\n return { captures, failures, environment };\n },\n async (failed, error) => {\n await ctx?.pagePool?.dispose();\n const current = active.session ?? session;\n active.session = null;\n await Promise.all([current.release(outcomeOf(active, failed), error), closeServer(server)]);\n },\n );\n}\n\nasync function closeServer(server: Serving | undefined): Promise<void> {\n if (server !== undefined) await safeCloseServer(server);\n}\n\n/** What a remote service's `connect` is told about the run a browser is for. */\nexport function launchContextOf(\n input: { buildId: string; runId?: string | undefined; shardIndex?: number | undefined },\n purpose: LaunchContext[\"purpose\"],\n signal?: AbortSignal,\n): LaunchContext {\n return {\n buildId: input.buildId,\n runId: input.runId ?? input.buildId,\n shardIndex: input.shardIndex,\n purpose,\n signal,\n };\n}\n\n/** The input with its target resolved, and the session gate when the target is remote. */\nexport function planRun<\n T extends { target?: CaptureTarget | undefined; logger?: PuppeteerRenderInput[\"logger\"] },\n>(\n rawInput: T,\n driver: TargetDriver,\n defaultTarget: CaptureTarget,\n): {\n input: T & { target: CaptureTarget; gate?: SessionGate | undefined };\n name: CaptureTarget;\n remote: RemoteTarget | undefined;\n} {\n const name = rawInput.target ?? defaultTarget;\n const remote = driver.remote(name);\n const gate =\n remote &&\n sessionGateFor(name, remote.remote.sessions, (value) => {\n rawInput.logger?.warn(\n { target: name, value: String(value) },\n \"sessions() returned an invalid value; keeping the last good one\",\n );\n });\n return { input: { ...rawInput, target: name, gate }, name, remote };\n}\n\n/** A target whose browser cannot route back to this host needs a URL; failing here names why. */\nexport function assertReachable(\n target: CaptureTarget,\n remote: RemoteTarget | undefined,\n storybookUrl: string | undefined,\n): void {\n if (remote?.reach === \"url\" && storybookUrl === undefined) {\n throw new TargetLaunchError(\n target,\n \"its browser cannot reach this host, so it needs a Storybook URL (a public origin and a secret to sign it) and none was given\",\n { transient: true },\n );\n }\n if (storybookUrl !== undefined) {\n assertStorybookRoot(target, storybookUrl);\n }\n}\n\n/** The URL must be the build's directory root; refused before any browser connects. */\nfunction assertStorybookRoot(target: CaptureTarget, storybookUrl: string): void {\n try {\n storybookBase(storybookUrl);\n } catch (error) {\n throw new TargetLaunchError(target, error instanceof Error ? error.message : String(error));\n }\n}\n\n/** The loopback server, or the URL given; a server that cannot start releases the browser it was for. */\nexport async function serve(\n input: Pick<RunInput, \"storybookDir\" | \"storybookUrl\">,\n session: BrowserSession,\n): Promise<Serving> {\n if (input.storybookUrl !== undefined) {\n return {\n url: storybookBase(input.storybookUrl),\n close: async () => {\n await Promise.resolve();\n },\n };\n }\n return await createStaticServer(input.storybookDir).catch(async (error: unknown) => {\n await session.release(\"failed\", error);\n throw error;\n });\n}\n\n/** This host's environment: a local browser renders on the machine the runner runs on. */\nasync function measureEnvironment(\n session: BrowserSession,\n remote: RemoteTarget | undefined,\n input: RunInput,\n signal: AbortSignal | undefined,\n): Promise<CaptureEnvironment> {\n if (remote) return await measureRemote(session, remote.remote, { logger: input.logger, signal });\n const browser: Browser = session.browser;\n const version = typeof browser.version === \"function\" ? await browser.version() : undefined;\n return await detectCaptureEnvironment(version);\n}\n\nasync function runCaptures(\n input: RunInput,\n active: ActiveRun,\n ctx: CaptureContext,\n relaunch: () => Promise<BrowserSession>,\n): Promise<Pick<RenderResult, \"captures\" | \"failures\">> {\n const captures: RenderedSnapshot[] = [];\n const failures: RenderResult[\"failures\"] = [];\n await runAllCaptures(input, active, ctx, captures, failures, relaunch);\n return { captures, failures };\n}\n\nfunction buildContext(\n session: BrowserSession,\n baseUrl: string,\n input: PuppeteerRenderInput,\n): CaptureContext {\n return {\n session,\n adapter: input.adapter ?? new StorybookAdapter(),\n baseUrl,\n navigationTimeoutMs: input.navigationTimeoutMs,\n executePlay: input.executePlay,\n playTimeoutMs: input.playTimeoutMs,\n runA11y: input.runA11y,\n ...(input.logger ? { logger: input.logger } : {}),\n runtimeParams: createRuntimeParamsState(),\n ...(input.reusePages ? { pagePool: new PagePool(session) } : {}),\n };\n}\n\nasync function runAllCaptures(\n input: RunInput,\n active: ActiveRun,\n ctx: CaptureContext,\n captures: RenderedSnapshot[],\n failures: RenderResult[\"failures\"],\n relaunch: () => Promise<BrowserSession>,\n): Promise<void> {\n // Bounded: one page per story x viewport all at once exhausts memory on large Storybooks.\n const jobs = input.stories.flatMap((story) =>\n resolveStoryViewports(story, input.viewports).map((viewport) => ({ story, viewport })),\n );\n // Batches drain in-flight pages, so the browser can be swapped between them.\n const batches = chunk(jobs, input.recycleBrowserEvery ?? DEFAULT_RECYCLE_BROWSER_EVERY);\n const pages = createPageLimiter({\n concurrency: input.concurrency,\n ...(input.gate === undefined ? {} : { gate: input.gate }),\n browser: engineOf(input.target ?? \"chromium\"),\n logger: input.logger,\n });\n try {\n await runBatches(batches, pages, input, active, ctx, { captures, failures, relaunch });\n } finally {\n pages.dispose();\n }\n}\n\nasync function runBatches(\n batches: { story: StoryEntry; viewport: Viewport }[][],\n pages: ReturnType<typeof createPageLimiter>,\n input: RunInput,\n active: ActiveRun,\n ctx: CaptureContext,\n out: {\n captures: RenderedSnapshot[];\n failures: RenderResult[\"failures\"];\n relaunch: () => Promise<BrowserSession>;\n },\n): Promise<void> {\n for (const [index, batch] of batches.entries()) {\n if (index > 0) {\n // oxlint-disable-next-line eslint/no-await-in-loop -- recycle only between drained batches\n await recycleBrowser(active, ctx, out.relaunch, input.logger);\n }\n // oxlint-disable-next-line eslint/no-await-in-loop -- batches run one after another\n await pages.each(\n batch,\n async (job) => await captureOne(input, active, ctx, job.story, job.viewport, out),\n );\n }\n}\n\n/** Swap in a fresh browser; the old one is released first so two never coexist. */\nasync function recycleBrowser(\n active: ActiveRun,\n ctx: CaptureContext,\n relaunch: () => Promise<BrowserSession>,\n logger: PuppeteerRenderInput[\"logger\"],\n): Promise<void> {\n assertNotCancelled(active);\n await ctx.pagePool?.dispose();\n const previous = ctx.session;\n await previous.release(active.cancelled ? \"cancelled\" : \"finished\");\n assertNotCancelled(active);\n await acquireReplacement(active, ctx, relaunch);\n logger?.info({}, \"browser recycled\");\n}\n\nfunction assertNotCancelled(active: ActiveRun): void {\n if (active.cancelled) throw new Error(\"Capture cancelled\");\n}\n\nasync function acquireReplacement(\n active: ActiveRun,\n ctx: CaptureContext,\n relaunch: () => Promise<BrowserSession>,\n): Promise<BrowserSession> {\n const next = await relaunch();\n if (active.cancelled) {\n await next.release(\"cancelled\");\n throw new Error(\"Capture cancelled\");\n }\n ctx.session = next;\n if (ctx.pagePool) ctx.pagePool = new PagePool(next);\n active.session = next;\n return next;\n}\n\n/** Capture one story x viewport; resolves to whether it timed out (failures are recorded, not thrown). */\nasync function captureOne(\n input: PuppeteerRenderInput,\n active: ActiveRun,\n ctx: CaptureContext,\n story: StoryEntry,\n viewport: Viewport,\n out: { captures: RenderedSnapshot[]; failures: RenderResult[\"failures\"] },\n): Promise<boolean> {\n if (active.cancelled) throw new Error(\"Capture cancelled\");\n try {\n await pushCapture(ctx, story, viewport, out.captures, out.failures, input);\n return false;\n } catch (error) {\n pushRenderFailure(out.failures, input, story.id, viewport.name, error);\n return isTimeoutError(error);\n }\n}\n\nasync function pushCapture(\n ctx: CaptureContext,\n story: StoryEntry,\n viewport: Viewport,\n captures: RenderedSnapshot[],\n failures: RenderResult[\"failures\"],\n input: PuppeteerRenderInput,\n): Promise<void> {\n const result = await captureScreenshot(ctx, story, viewport);\n const snapshot: RenderedSnapshot = {\n story: result.story,\n viewportName: viewport.name,\n viewport,\n screenshot: result.screenshot,\n ...(result.a11yViolations.length > 0 ? { a11y: result.a11yViolations } : {}),\n };\n // Streaming: hand it over and drop the buffer rather than holding every screenshot.\n if (input.onCapture) await input.onCapture(snapshot);\n else captures.push(snapshot);\n if (result.a11yViolations.length > 0)\n handleA11y(input, failures, story, viewport, result.a11yViolations);\n}\nfunction handleA11y(\n input: PuppeteerRenderInput,\n failures: RenderResult[\"failures\"],\n story: StoryEntry,\n viewport: Viewport,\n violations: string[],\n): void {\n failures.push({\n storyId: story.id,\n viewportName: viewport.name,\n error: `a11y: ${violations.join(\"; \")}`,\n });\n input.logger?.warn(\n { storyId: story.id, viewport: viewport.name, violations },\n \"a11y violations found\",\n );\n}\n\nfunction pushRenderFailure(\n failures: RenderResult[\"failures\"],\n input: PuppeteerRenderInput,\n storyId: string,\n viewportName: string,\n error: unknown,\n): void {\n failures.push({ storyId, viewportName, error: messageOf(error) });\n input.logger?.error({ storyId, viewport: viewportName, err: error }, \"render failed for story\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pipeline.mjs","names":[],"sources":["../src/pipeline.ts"],"sourcesContent":["/** Render orchestration: static server, browser, and per-story captures. */\n\nimport {\n TargetLaunchError,\n engineOf,\n logRemoteSizing,\n type CaptureEnvironment,\n type CaptureTarget,\n type RemoteTarget,\n type RenderResult,\n type RenderedSnapshot,\n type StoryEntry,\n type Viewport,\n} from \"@storyshelf/core/adapter/capture-runner\";\nimport { StorybookAdapter, resolveStoryViewports } from \"@storyshelf/core/capture\";\nimport {\n DEFAULT_RECYCLE_BROWSER_EVERY,\n chunk,\n describeSizing,\n createPageLimiter,\n detectCaptureEnvironment,\n isTimeoutError,\n sessionGateFor,\n storybookBase,\n type SessionGate,\n} from \"@storyshelf/core/utils\";\nimport type { Browser } from \"puppeteer-core\";\nimport { abortIfCancelled, finishing, messageOf, outcomeOf, safeCloseServer } from \"./browser.ts\";\nimport { PagePool } from \"./pages.ts\";\nimport { measureRemote } from \"./remote-environment.ts\";\nimport { createRuntimeParamsState } from \"./runtime-params.ts\";\nimport { captureScreenshot } from \"./screenshot.ts\";\nimport { createBrowserSession, type BrowserSession } from \"./session.ts\";\nimport { createStaticServer } from \"./static-server.ts\";\nimport type {\n ActiveRun,\n CaptureContext,\n LaunchContext,\n PuppeteerRenderInput,\n RunInput,\n TargetDriver,\n} from \"./types.ts\";\n\n/** Where the browser finds the Storybook: the loopback server, or the URL core handed over. */\ninterface Serving {\n url: string;\n close(): Promise<void>;\n}\n\n/** Render all story/viewport combinations for a build. */\nexport async function renderAll(\n rawInput: PuppeteerRenderInput,\n active: ActiveRun,\n driver: TargetDriver,\n defaultTarget: CaptureTarget,\n): Promise<RenderResult> {\n const { input, name, remote } = planRun(rawInput, driver, defaultTarget);\n assertReachable(name, remote, input.storybookUrl);\n const relaunch = async (): Promise<BrowserSession> =>\n createBrowserSession(\n await driver.launch(\n name,\n input.logger,\n launchContextOf(input, \"render\", active.abort?.signal),\n ),\n );\n // Launch first: a target that will not start must fail before a static server is bound.\n const session = await relaunch();\n active.session = session;\n await abortIfCancelled(active, session);\n let server: Serving | undefined;\n let ctx: CaptureContext | undefined;\n return await finishing(\n async () => {\n server = await serve(input, session);\n logHostSizing(input, name, remote);\n // Measured while the browser is open: Puppeteer cannot read the version of a closed one.\n const environment = await measureEnvironment(session, remote, input, active.abort?.signal);\n ctx = buildContext(session, server.url, input);\n const { captures, failures } = await runCaptures(input, active, ctx, relaunch);\n // Echo the target so persisted rows are attributable without the request.\n for (const row of [...captures, ...failures]) row.browser = name;\n return { captures, failures, environment };\n },\n async (failed, error) => {\n await ctx?.pagePool?.dispose();\n const current = active.session ?? session;\n active.session = null;\n await Promise.all([current.release(outcomeOf(active, failed), error), closeServer(server)]);\n },\n );\n}\n\nasync function closeServer(server: Serving | undefined): Promise<void> {\n if (server !== undefined) await safeCloseServer(server);\n}\n\n/** What a remote service's `connect` is told about the run a browser is for. */\nexport function launchContextOf(\n input: { buildId: string; runId?: string | undefined; shardIndex?: number | undefined },\n purpose: LaunchContext[\"purpose\"],\n signal?: AbortSignal,\n): LaunchContext {\n return {\n buildId: input.buildId,\n runId: input.runId ?? input.buildId,\n shardIndex: input.shardIndex,\n purpose,\n signal,\n };\n}\n\n/** The input with its target resolved, and the session gate when the target is remote. */\nexport function planRun<\n T extends {\n target?: CaptureTarget | undefined;\n concurrency?: number | undefined;\n logger?: PuppeteerRenderInput[\"logger\"];\n },\n>(\n rawInput: T,\n driver: TargetDriver,\n defaultTarget: CaptureTarget,\n): {\n input: T & { target: CaptureTarget; gate?: SessionGate | undefined };\n name: CaptureTarget;\n remote: RemoteTarget | undefined;\n} {\n const name = rawInput.target ?? defaultTarget;\n const remote = driver.remote(name);\n const gate =\n remote &&\n sessionGateFor(name, remote.remote.sessions, (value) => {\n rawInput.logger?.warn(\n { target: name, value: String(value) },\n \"sessions() returned an invalid value; keeping the last good one\",\n );\n });\n if (remote) {\n logRemoteSizing(rawInput.logger, name, remote.remote, {\n gate,\n concurrency: rawInput.concurrency,\n pagesPerConnection: false,\n });\n }\n return { input: { ...rawInput, target: name, gate }, name, remote };\n}\n\n/** A target whose browser cannot route back to this host needs a URL; failing here names why. */\nexport function assertReachable(\n target: CaptureTarget,\n remote: RemoteTarget | undefined,\n storybookUrl: string | undefined,\n): void {\n if (remote?.reach === \"url\" && storybookUrl === undefined) {\n throw new TargetLaunchError(\n target,\n \"its browser cannot reach this host, so it needs a Storybook URL (a public origin and a secret to sign it) and none was given\",\n { transient: true },\n );\n }\n if (storybookUrl !== undefined) {\n assertStorybookRoot(target, storybookUrl);\n }\n}\n\n/** The URL must be the build's directory root; refused before any browser connects. */\nfunction assertStorybookRoot(target: CaptureTarget, storybookUrl: string): void {\n try {\n storybookBase(storybookUrl);\n } catch (error) {\n throw new TargetLaunchError(target, error instanceof Error ? error.message : String(error));\n }\n}\n\n/** The loopback server, or the URL given; a server that cannot start releases the browser it was for. */\nexport async function serve(\n input: Pick<RunInput, \"storybookDir\" | \"storybookUrl\">,\n session: BrowserSession,\n): Promise<Serving> {\n if (input.storybookUrl !== undefined) {\n return {\n url: storybookBase(input.storybookUrl),\n close: async () => {\n await Promise.resolve();\n },\n };\n }\n return await createStaticServer(input.storybookDir).catch(async (error: unknown) => {\n await session.release(\"failed\", error);\n throw error;\n });\n}\n\n/** This host's environment: a local browser renders on the machine the runner runs on. */\nasync function measureEnvironment(\n session: BrowserSession,\n remote: RemoteTarget | undefined,\n input: RunInput,\n signal: AbortSignal | undefined,\n): Promise<CaptureEnvironment> {\n if (remote) return await measureRemote(session, remote.remote, { logger: input.logger, signal });\n const browser: Browser = session.browser;\n const version = typeof browser.version === \"function\" ? await browser.version() : undefined;\n return await detectCaptureEnvironment(version);\n}\n\nasync function runCaptures(\n input: RunInput,\n active: ActiveRun,\n ctx: CaptureContext,\n relaunch: () => Promise<BrowserSession>,\n): Promise<Pick<RenderResult, \"captures\" | \"failures\">> {\n const captures: RenderedSnapshot[] = [];\n const failures: RenderResult[\"failures\"] = [];\n await runAllCaptures(input, active, ctx, captures, failures, relaunch);\n return { captures, failures };\n}\n\nfunction buildContext(\n session: BrowserSession,\n baseUrl: string,\n input: PuppeteerRenderInput,\n): CaptureContext {\n return {\n session,\n adapter: input.adapter ?? new StorybookAdapter(),\n baseUrl,\n navigationTimeoutMs: input.navigationTimeoutMs,\n executePlay: input.executePlay,\n playTimeoutMs: input.playTimeoutMs,\n runA11y: input.runA11y,\n ...(input.logger ? { logger: input.logger } : {}),\n runtimeParams: createRuntimeParamsState(),\n ...(input.reusePages ? { pagePool: new PagePool(session) } : {}),\n };\n}\n\nasync function runAllCaptures(\n input: RunInput,\n active: ActiveRun,\n ctx: CaptureContext,\n captures: RenderedSnapshot[],\n failures: RenderResult[\"failures\"],\n relaunch: () => Promise<BrowserSession>,\n): Promise<void> {\n // Bounded: one page per story x viewport all at once exhausts memory on large Storybooks.\n const jobs = input.stories.flatMap((story) =>\n resolveStoryViewports(story, input.viewports).map((viewport) => ({ story, viewport })),\n );\n // Batches drain in-flight pages, so the browser can be swapped between them.\n const batches = chunk(jobs, input.recycleBrowserEvery ?? DEFAULT_RECYCLE_BROWSER_EVERY);\n const pages = createPageLimiter({\n concurrency: input.concurrency,\n ...(input.gate === undefined ? {} : { gate: input.gate }),\n browser: engineOf(input.target ?? \"chromium\"),\n logger: input.logger,\n });\n try {\n await runBatches(batches, pages, input, active, ctx, { captures, failures, relaunch });\n } finally {\n pages.dispose();\n }\n}\n\nasync function runBatches(\n batches: { story: StoryEntry; viewport: Viewport }[][],\n pages: ReturnType<typeof createPageLimiter>,\n input: RunInput,\n active: ActiveRun,\n ctx: CaptureContext,\n out: {\n captures: RenderedSnapshot[];\n failures: RenderResult[\"failures\"];\n relaunch: () => Promise<BrowserSession>;\n },\n): Promise<void> {\n for (const [index, batch] of batches.entries()) {\n if (index > 0) {\n // oxlint-disable-next-line eslint/no-await-in-loop -- recycle only between drained batches\n await recycleBrowser(active, ctx, out.relaunch, input.logger);\n }\n // oxlint-disable-next-line eslint/no-await-in-loop -- batches run one after another\n await pages.each(\n batch,\n async (job) => await captureOne(input, active, ctx, job.story, job.viewport, out),\n );\n }\n}\n\n/** Swap in a fresh browser; the old one is released first so two never coexist. */\nasync function recycleBrowser(\n active: ActiveRun,\n ctx: CaptureContext,\n relaunch: () => Promise<BrowserSession>,\n logger: PuppeteerRenderInput[\"logger\"],\n): Promise<void> {\n assertNotCancelled(active);\n await ctx.pagePool?.dispose();\n const previous = ctx.session;\n await previous.release(active.cancelled ? \"cancelled\" : \"finished\");\n assertNotCancelled(active);\n await acquireReplacement(active, ctx, relaunch);\n logger?.info({}, \"browser recycled\");\n}\n\nfunction assertNotCancelled(active: ActiveRun): void {\n if (active.cancelled) throw new Error(\"Capture cancelled\");\n}\n\nasync function acquireReplacement(\n active: ActiveRun,\n ctx: CaptureContext,\n relaunch: () => Promise<BrowserSession>,\n): Promise<BrowserSession> {\n const next = await relaunch();\n if (active.cancelled) {\n await next.release(\"cancelled\");\n throw new Error(\"Capture cancelled\");\n }\n ctx.session = next;\n if (ctx.pagePool) ctx.pagePool = new PagePool(next);\n active.session = next;\n return next;\n}\n\n/** Capture one story x viewport; resolves to whether it timed out (failures are recorded, not thrown). */\nasync function captureOne(\n input: PuppeteerRenderInput,\n active: ActiveRun,\n ctx: CaptureContext,\n story: StoryEntry,\n viewport: Viewport,\n out: { captures: RenderedSnapshot[]; failures: RenderResult[\"failures\"] },\n): Promise<boolean> {\n if (active.cancelled) throw new Error(\"Capture cancelled\");\n try {\n await pushCapture(ctx, story, viewport, out.captures, out.failures, input);\n return false;\n } catch (error) {\n pushRenderFailure(out.failures, input, story.id, viewport.name, error);\n return isTimeoutError(error);\n }\n}\n\nasync function pushCapture(\n ctx: CaptureContext,\n story: StoryEntry,\n viewport: Viewport,\n captures: RenderedSnapshot[],\n failures: RenderResult[\"failures\"],\n input: PuppeteerRenderInput,\n): Promise<void> {\n const result = await captureScreenshot(ctx, story, viewport);\n const snapshot: RenderedSnapshot = {\n story: result.story,\n viewportName: viewport.name,\n viewport,\n screenshot: result.screenshot,\n ...(result.a11yViolations.length > 0 ? { a11y: result.a11yViolations } : {}),\n };\n // Streaming: hand it over and drop the buffer rather than holding every screenshot.\n if (input.onCapture) await input.onCapture(snapshot);\n else captures.push(snapshot);\n if (result.a11yViolations.length > 0)\n handleA11y(input, failures, story, viewport, result.a11yViolations);\n}\nfunction handleA11y(\n input: PuppeteerRenderInput,\n failures: RenderResult[\"failures\"],\n story: StoryEntry,\n viewport: Viewport,\n violations: string[],\n): void {\n failures.push({\n storyId: story.id,\n viewportName: viewport.name,\n error: `a11y: ${violations.join(\"; \")}`,\n });\n input.logger?.warn(\n { storyId: story.id, viewport: viewport.name, violations },\n \"a11y violations found\",\n );\n}\n\nfunction pushRenderFailure(\n failures: RenderResult[\"failures\"],\n input: PuppeteerRenderInput,\n storyId: string,\n viewportName: string,\n error: unknown,\n): void {\n failures.push({ storyId, viewportName, error: messageOf(error) });\n input.logger?.error({ storyId, viewport: viewportName, err: error }, \"render failed for story\");\n}\n\n/** The host's sizing of a capture; a remote target runs elsewhere, so nothing is measured here. */\nfunction logHostSizing(\n input: { logger?: PuppeteerRenderInput[\"logger\"]; concurrency?: number | undefined },\n name: CaptureTarget,\n remote: RemoteTarget | undefined,\n): void {\n if (!remote) {\n input.logger?.info(describeSizing({ ...input, browser: engineOf(name) }), \"capture sizing\");\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storyshelf/runner-puppeteer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0-next-d2cc082",
|
|
4
4
|
"description": "Puppeteer CaptureRunner for StoryShelf (Chromium via chrome-headless-shell, smaller image).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"puppeteer",
|
|
@@ -59,13 +59,13 @@
|
|
|
59
59
|
"test:flake": "vitest run --repeats=20 --maxWorkers=1 --no-file-parallelism src/capture-runner.test.ts src/settle.test.ts src/screenshot.test.ts"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@storyshelf/core": "0.
|
|
62
|
+
"@storyshelf/core": "0.9.0-next-d2cc082",
|
|
63
63
|
"puppeteer-core": "^24.0.0"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@storyshelf/app": "0.
|
|
67
|
-
"@storyshelf/db-sqlite": "0.
|
|
68
|
-
"@storyshelf/storage-local": "0.
|
|
66
|
+
"@storyshelf/app": "0.9.0-next-d2cc082",
|
|
67
|
+
"@storyshelf/db-sqlite": "0.9.0-next-d2cc082",
|
|
68
|
+
"@storyshelf/storage-local": "0.9.0-next-d2cc082",
|
|
69
69
|
"@types/node": "^26.0.0",
|
|
70
70
|
"@vitest/coverage-v8": "^4.0.0",
|
|
71
71
|
"adm-zip": "0.6.1",
|
|
@@ -78,14 +78,5 @@
|
|
|
78
78
|
},
|
|
79
79
|
"engines": {
|
|
80
80
|
"node": ">=24"
|
|
81
|
-
},
|
|
82
|
-
"jsr": {
|
|
83
|
-
"runtimeCompat": {
|
|
84
|
-
"node": true,
|
|
85
|
-
"deno": true,
|
|
86
|
-
"browser": false,
|
|
87
|
-
"workerd": false,
|
|
88
|
-
"bun": true
|
|
89
|
-
}
|
|
90
81
|
}
|
|
91
82
|
}
|