@scenar/cli 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/example-bundle/assets/index-DpJUOVBT.js +388 -0
  2. package/example-bundle/assets/style-ix_q0zsc.css +1 -0
  3. package/example-bundle/embed.js +1 -1
  4. package/example-bundle/index.html +2 -2
  5. package/example-bundle/pack-manifest.json +11 -11
  6. package/example-bundle/scenario.json +6 -2
  7. package/package.json +4 -4
  8. package/src/__tests__/collect-pack-shots.test.ts +115 -0
  9. package/src/__tests__/find-authored-viewport.test.ts +58 -0
  10. package/src/__tests__/pack-generate-embed-entry.test.ts +5 -1
  11. package/src/__tests__/pack-manifest.test.ts +17 -0
  12. package/src/__tests__/read-shots.test.ts +57 -0
  13. package/src/__tests__/resolve-pack-viewport.test.ts +46 -0
  14. package/src/__tests__/run-shoot.test.ts +51 -2
  15. package/src/bundle/read-shots.d.ts +28 -0
  16. package/src/bundle/read-shots.d.ts.map +1 -0
  17. package/src/bundle/read-shots.js +28 -0
  18. package/src/bundle/read-shots.js.map +1 -0
  19. package/src/bundle/read-shots.ts +38 -0
  20. package/src/commands/pack.d.ts.map +1 -1
  21. package/src/commands/pack.js +23 -4
  22. package/src/commands/pack.js.map +1 -1
  23. package/src/commands/pack.ts +26 -5
  24. package/src/pack/build.d.ts +33 -1
  25. package/src/pack/build.d.ts.map +1 -1
  26. package/src/pack/build.js +35 -10
  27. package/src/pack/build.js.map +1 -1
  28. package/src/pack/build.ts +63 -17
  29. package/src/pack/collect-pack-shots.d.ts +47 -0
  30. package/src/pack/collect-pack-shots.d.ts.map +1 -0
  31. package/src/pack/collect-pack-shots.js +66 -0
  32. package/src/pack/collect-pack-shots.js.map +1 -0
  33. package/src/pack/collect-pack-shots.ts +89 -0
  34. package/src/pack/generate-embed-entry.d.ts +7 -0
  35. package/src/pack/generate-embed-entry.d.ts.map +1 -1
  36. package/src/pack/generate-embed-entry.js +14 -3
  37. package/src/pack/generate-embed-entry.js.map +1 -1
  38. package/src/pack/generate-embed-entry.ts +15 -3
  39. package/src/pack/pack-manifest.d.ts +12 -3
  40. package/src/pack/pack-manifest.d.ts.map +1 -1
  41. package/src/pack/pack-manifest.js +13 -3
  42. package/src/pack/pack-manifest.js.map +1 -1
  43. package/src/pack/pack-manifest.ts +13 -2
  44. package/src/pack/run-pack.d.ts +13 -2
  45. package/src/pack/run-pack.d.ts.map +1 -1
  46. package/src/pack/run-pack.js +56 -17
  47. package/src/pack/run-pack.js.map +1 -1
  48. package/src/pack/run-pack.ts +81 -19
  49. package/src/pack/viewport.d.ts +17 -0
  50. package/src/pack/viewport.d.ts.map +1 -1
  51. package/src/pack/viewport.js +19 -0
  52. package/src/pack/viewport.js.map +1 -1
  53. package/src/pack/viewport.ts +29 -0
  54. package/src/shoot/run-shoot.d.ts +6 -0
  55. package/src/shoot/run-shoot.d.ts.map +1 -1
  56. package/src/shoot/run-shoot.js +18 -0
  57. package/src/shoot/run-shoot.js.map +1 -1
  58. package/src/shoot/run-shoot.ts +19 -0
  59. package/src/util/load-ts.d.ts +35 -2
  60. package/src/util/load-ts.d.ts.map +1 -1
  61. package/src/util/load-ts.js +76 -10
  62. package/src/util/load-ts.js.map +1 -1
  63. package/src/util/load-ts.ts +87 -11
  64. package/tsconfig.tsbuildinfo +1 -1
  65. package/example-bundle/assets/index-BoIR0_nN.js +0 -147
  66. package/example-bundle/assets/style-YRMeJPPX.css +0 -1
@@ -3,6 +3,7 @@ import { stat, mkdir, writeFile, rm, readdir, copyFile, access } from "node:fs/p
3
3
  import { detectRenderExport } from "../render/detect-render-export.js";
4
4
  import { resolveProvidersPath } from "../render/resolve-providers.js";
5
5
  import { generateEmbedEntry, generateEmbedHtml } from "./generate-embed-entry.js";
6
+ import { collectPackShots, type CollectedPackShots } from "./collect-pack-shots.js";
6
7
  import { runViteBuild } from "./build.js";
7
8
  import { copyEmbedLoader } from "./embed-loader.js";
8
9
  import {
@@ -12,10 +13,13 @@ import {
12
13
  verifyManifestFilesExist,
13
14
  type PackManifest,
14
15
  } from "./pack-manifest.js";
15
- import { DEFAULT_VIEWPORT } from "./viewport.js";
16
+ import { DEFAULT_VIEWPORT, resolvePackViewport } from "./viewport.js";
16
17
 
17
- /** Generator version stamped into scenario.json (kept in sync with the CLI). */
18
- const PACK_GENERATOR_VERSION = "0.0.1";
18
+ /**
19
+ * Generator version stamped into scenario.json (kept in sync with the CLI).
20
+ * 0.0.2: scenario.json may carry `shots` (declared shot names, in step order).
21
+ */
22
+ const PACK_GENERATOR_VERSION = "0.0.2";
19
23
 
20
24
  /** Options for {@link runPack}. Paths may be relative; they are resolved here. */
21
25
  export interface RunPackOptions {
@@ -23,9 +27,14 @@ export interface RunPackOptions {
23
27
  readonly scenarioDir: string;
24
28
  /** Output directory for the bundle (default: ./<scenario-id>-bundle). */
25
29
  readonly outDir?: string;
26
- /** Canonical viewport width in px (default: {@link DEFAULT_VIEWPORT}.width). */
30
+ /**
31
+ * Canonical viewport width in px. Omitted -> the scenario's own authored
32
+ * viewport (`createScenario({ viewport })` or an exported `viewport`
33
+ * constant) applies; a scenario that authors none falls back to
34
+ * {@link DEFAULT_VIEWPORT}. Explicit flags always win over both.
35
+ */
27
36
  readonly width?: number;
28
- /** Shell/container height in px (default: {@link DEFAULT_VIEWPORT}.height). */
37
+ /** Shell/container height in px. Same resolution chain as `width`. */
29
38
  readonly shellHeight?: number;
30
39
  /**
31
40
  * Float the scenario on a backdrop with a window shadow (screen-recording
@@ -46,6 +55,12 @@ export interface PackResult {
46
55
  readonly renderFilePath: string;
47
56
  readonly providersPath: string | null;
48
57
  readonly hasNarration: boolean;
58
+ /**
59
+ * The declared shot names recorded in scenario.json (empty = the scenario
60
+ * authoritatively declares none), or undefined when the steps module could
61
+ * not be loaded under Node and the shots are unknown.
62
+ */
63
+ readonly shots?: readonly string[];
49
64
  readonly manifest: PackManifest;
50
65
  readonly totalBytes: number;
51
66
  }
@@ -73,8 +88,6 @@ export async function runPack(options: RunPackOptions): Promise<PackResult> {
73
88
 
74
89
  const scenarioId = basename(scenarioDir);
75
90
  const outDir = options.outDir ? resolve(options.outDir) : resolve(`./${scenarioId}-bundle`);
76
- const width = options.width ?? DEFAULT_VIEWPORT.width;
77
- const shellHeight = options.shellHeight ?? DEFAULT_VIEWPORT.height;
78
91
 
79
92
  // The entry must live inside the scenario directory so the bundler's
80
93
  // node_modules resolution walks up into the consumer project.
@@ -91,7 +104,33 @@ export async function runPack(options: RunPackOptions): Promise<PackResult> {
91
104
  onLog(`Narration: ${hasNarration ? "yes (manifest found)" : "none"}`);
92
105
  onLog(`Output: ${outDir}`);
93
106
 
94
- // 1. Generate the browser entry + index.html into the temp dir.
107
+ // 1. Discover the declared shots and the authored viewport — by
108
+ // SSR-loading the steps module (same Vite resolution as the build
109
+ // below). Before the build on purpose: authoring errors — bad shot
110
+ // names, no steps array — fail fast here, without paying for a
111
+ // bundle. An import failure is tolerated: the shots are then unknown
112
+ // and scenario.json omits the key.
113
+ const collectedShots = await collectPackShots(scenarioDir);
114
+ onLog(`Shots: ${describeCollectedShots(collectedShots)}`);
115
+
116
+ // Canonical viewport: explicit options > the scenario's authored
117
+ // viewport > the packer default. Authored viewports used to be silently
118
+ // ignored (the authoring docs demonstrated `createScenario({ viewport })`
119
+ // while pack only ever read its own options); the source is logged so a
120
+ // surprising size is one log line from its explanation.
121
+ const resolvedViewport = resolvePackViewport(
122
+ options,
123
+ collectedShots.recorded ? collectedShots.authoredViewport : null,
124
+ );
125
+ const { width, height: shellHeight } = resolvedViewport;
126
+ const viewportSourceLabel = {
127
+ explicit: "explicit",
128
+ authored: "authored by the scenario",
129
+ default: "packer default",
130
+ }[resolvedViewport.source];
131
+ onLog(`Viewport: ${width}x${shellHeight} (${viewportSourceLabel})`);
132
+
133
+ // 2. Generate the browser entry + index.html into the temp dir.
95
134
  const stage = options.stage ?? false;
96
135
  const entrySource = generateEmbedEntry({
97
136
  scenarioDir,
@@ -109,39 +148,53 @@ export async function runPack(options: RunPackOptions): Promise<PackResult> {
109
148
  await writeFile(join(tempDir, entryFileName), entrySource, "utf-8");
110
149
  await writeFile(entryHtmlPath, generateEmbedHtml(scenarioId, entryFileName, stage), "utf-8");
111
150
 
112
- // 2. Build the static bundle with Vite.
151
+ // 3. Build the static bundle with Vite.
113
152
  onLog("Bundling embed with Vite...");
114
153
  await runViteBuild({ root: tempDir, outDir, entryHtmlPath });
115
154
 
116
- // 3. Copy the narration manifest + audio. The embed fetches
155
+ // 4. Copy the narration manifest + audio. The embed fetches
117
156
  // ./narration/manifest.json at runtime and resolves each clip's
118
157
  // relative src against it, so both must ship in the bundle.
119
158
  if (hasNarration) {
120
159
  await copyNarration(scenarioDir, outDir);
121
160
  }
122
161
 
123
- // 4. Write the required scenario.json descriptor, recording the canonical
162
+ // 5. Write the required scenario.json descriptor, recording the canonical
124
163
  // viewport baked into the bundle so `deploy`/`serve`/`publish` can derive
125
- // a correctly-proportioned embed snippet (DD-004).
126
- await writeScenarioJson(outDir, scenarioId, PACK_GENERATOR_VERSION, {
127
- width,
128
- height: shellHeight,
129
- });
164
+ // a correctly-proportioned embed snippet (DD-004), and — when known —
165
+ // the declared shot names so `shoot` and other tooling never boot a
166
+ // browser just to learn a bundle has nothing to capture.
167
+ await writeScenarioJson(
168
+ outDir,
169
+ scenarioId,
170
+ PACK_GENERATOR_VERSION,
171
+ { width, height: shellHeight },
172
+ collectedShots.recorded ? collectedShots.shots : undefined,
173
+ );
130
174
 
131
- // 4b. Copy the optional <scenar-embed> loader into the bundle as embed.js
175
+ // 5b. Copy the optional <scenar-embed> loader into the bundle as embed.js
132
176
  // (sibling of index.html), so the enhanced snippet works on any static
133
177
  // host (GitHub Pages, `serve`, the edge) with no extra setup. It lands
134
178
  // before the manifest pass below so it ships as a first-class bundle file.
135
179
  await copyEmbedLoader(outDir);
136
180
 
137
- // 5. Compute + write the deploy-facing pack manifest (validates the bundle
181
+ // 6. Compute + write the deploy-facing pack manifest (validates the bundle
138
182
  // against the backend allowlist; throws on any violation).
139
183
  const manifest = await buildPackManifest(outDir, scenarioId);
140
184
  await verifyManifestFilesExist(outDir, manifest);
141
185
  await writePackManifest(outDir, manifest);
142
186
 
143
187
  const totalBytes = manifest.files.reduce((sum, f) => sum + f.sizeBytes, 0);
144
- return { scenarioId, outDir, renderFilePath, providersPath, hasNarration, manifest, totalBytes };
188
+ return {
189
+ scenarioId,
190
+ outDir,
191
+ renderFilePath,
192
+ providersPath,
193
+ hasNarration,
194
+ shots: collectedShots.recorded ? collectedShots.shots : undefined,
195
+ manifest,
196
+ totalBytes,
197
+ };
145
198
  } finally {
146
199
  if (!options.keepTemp) {
147
200
  await rm(tempDir, { recursive: true, force: true }).catch(() => {});
@@ -168,6 +221,15 @@ async function copyNarration(scenarioDir: string, outDir: string): Promise<void>
168
221
  }
169
222
  }
170
223
 
224
+ /** One log-friendly line for the shot discovery outcome. */
225
+ function describeCollectedShots(collected: CollectedPackShots): string {
226
+ if (!collected.recorded) {
227
+ // Vite errors can run many lines; the first carries the message.
228
+ return `unknown — steps module not loadable under Node (${collected.reason.split("\n")[0]})`;
229
+ }
230
+ return collected.shots.length === 0 ? "none declared" : collected.shots.join(", ");
231
+ }
232
+
171
233
  async function fileExists(path: string): Promise<boolean> {
172
234
  try {
173
235
  await access(path);
@@ -33,4 +33,21 @@ export declare const DEFAULT_VIEWPORT: Viewport;
33
33
  * accept a recorded viewport while falling back cleanly for older bundles.
34
34
  */
35
35
  export declare function parseViewport(value: unknown): Viewport | null;
36
+ /** A resolved pack viewport plus where each number came from (for the log). */
37
+ export interface ResolvedPackViewport extends Viewport {
38
+ readonly source: "explicit" | "authored" | "default";
39
+ }
40
+ /**
41
+ * Resolve the canonical viewport a bundle is packed at: explicit options
42
+ * (CLI flags / MCP params) > the scenario's own authored viewport > the
43
+ * packer default. Width and height resolve as a pair — an explicit *partial*
44
+ * override (only `--width`) still counts as explicit, filling the other axis
45
+ * from the authored viewport when present, else the default — and the
46
+ * source names the strongest contributor so the pack log can explain a
47
+ * surprising size in one line.
48
+ */
49
+ export declare function resolvePackViewport(options: {
50
+ readonly width?: number;
51
+ readonly shellHeight?: number;
52
+ }, authored: Viewport | null): ResolvedPackViewport;
36
53
  //# sourceMappingURL=viewport.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"viewport.d.ts","sourceRoot":"","sources":["../../../src/pack/viewport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,QAAQ;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kFAAkF;IAClF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAsC,CAAC;AAEtE;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAK7D"}
1
+ {"version":3,"file":"viewport.d.ts","sourceRoot":"","sources":["../../../src/pack/viewport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,QAAQ;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kFAAkF;IAClF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAsC,CAAC;AAEtE;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAK7D;AAED,+EAA+E;AAC/E,MAAM,WAAW,oBAAqB,SAAQ,QAAQ;IACpD,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;CACtD;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EACnE,QAAQ,EAAE,QAAQ,GAAG,IAAI,GACxB,oBAAoB,CAUtB"}
@@ -19,6 +19,25 @@ export function parseViewport(value) {
19
19
  return null;
20
20
  return { width, height };
21
21
  }
22
+ /**
23
+ * Resolve the canonical viewport a bundle is packed at: explicit options
24
+ * (CLI flags / MCP params) > the scenario's own authored viewport > the
25
+ * packer default. Width and height resolve as a pair — an explicit *partial*
26
+ * override (only `--width`) still counts as explicit, filling the other axis
27
+ * from the authored viewport when present, else the default — and the
28
+ * source names the strongest contributor so the pack log can explain a
29
+ * surprising size in one line.
30
+ */
31
+ export function resolvePackViewport(options, authored) {
32
+ const width = options.width ?? authored?.width ?? DEFAULT_VIEWPORT.width;
33
+ const height = options.shellHeight ?? authored?.height ?? DEFAULT_VIEWPORT.height;
34
+ const source = options.width !== undefined || options.shellHeight !== undefined
35
+ ? "explicit"
36
+ : authored
37
+ ? "authored"
38
+ : "default";
39
+ return { width, height, source };
40
+ }
22
41
  function isPositiveInt(value) {
23
42
  return typeof value === "number" && Number.isInteger(value) && value > 0;
24
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"viewport.js","sourceRoot":"","sources":["../../../src/pack/viewport.ts"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAa,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAEtE;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC7D,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,KAAgC,CAAC;IAC3D,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACjE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC3B,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;AAC3E,CAAC"}
1
+ {"version":3,"file":"viewport.js","sourceRoot":"","sources":["../../../src/pack/viewport.ts"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAa,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAEtE;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC7D,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,KAAgC,CAAC;IAC3D,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACjE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC3B,CAAC;AAOD;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAmE,EACnE,QAAyB;IAEzB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,QAAQ,EAAE,KAAK,IAAI,gBAAgB,CAAC,KAAK,CAAC;IACzE,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,IAAI,QAAQ,EAAE,MAAM,IAAI,gBAAgB,CAAC,MAAM,CAAC;IAClF,MAAM,MAAM,GACV,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS;QAC9D,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,QAAQ;YACR,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,SAAS,CAAC;IAClB,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACnC,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;AAC3E,CAAC"}
@@ -41,6 +41,35 @@ export function parseViewport(value: unknown): Viewport | null {
41
41
  return { width, height };
42
42
  }
43
43
 
44
+ /** A resolved pack viewport plus where each number came from (for the log). */
45
+ export interface ResolvedPackViewport extends Viewport {
46
+ readonly source: "explicit" | "authored" | "default";
47
+ }
48
+
49
+ /**
50
+ * Resolve the canonical viewport a bundle is packed at: explicit options
51
+ * (CLI flags / MCP params) > the scenario's own authored viewport > the
52
+ * packer default. Width and height resolve as a pair — an explicit *partial*
53
+ * override (only `--width`) still counts as explicit, filling the other axis
54
+ * from the authored viewport when present, else the default — and the
55
+ * source names the strongest contributor so the pack log can explain a
56
+ * surprising size in one line.
57
+ */
58
+ export function resolvePackViewport(
59
+ options: { readonly width?: number; readonly shellHeight?: number },
60
+ authored: Viewport | null,
61
+ ): ResolvedPackViewport {
62
+ const width = options.width ?? authored?.width ?? DEFAULT_VIEWPORT.width;
63
+ const height = options.shellHeight ?? authored?.height ?? DEFAULT_VIEWPORT.height;
64
+ const source =
65
+ options.width !== undefined || options.shellHeight !== undefined
66
+ ? "explicit"
67
+ : authored
68
+ ? "authored"
69
+ : "default";
70
+ return { width, height, source };
71
+ }
72
+
44
73
  function isPositiveInt(value: unknown): value is number {
45
74
  return typeof value === "number" && Number.isInteger(value) && value > 0;
46
75
  }
@@ -47,6 +47,12 @@ export interface ShootResult {
47
47
  * or renamed shot must never linger in a deployed bundle, and a manifest
48
48
  * listing deleted files would fail its own existence check at the next
49
49
  * publish.
50
+ *
51
+ * When scenario.json records an authoritatively empty shot list (pack ≥
52
+ * generator 0.0.2), the whole server + browser boot is skipped — both
53
+ * invariants above still hold on that path. A recorded non-empty list does
54
+ * NOT bypass the capture page: the running bundle stays the runtime truth
55
+ * for what gets shot; the record only proves there is something to boot for.
50
56
  */
51
57
  export declare function runShoot(options: RunShootOptions): Promise<ShootResult>;
52
58
  //# sourceMappingURL=run-shoot.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run-shoot.d.ts","sourceRoot":"","sources":["../../../src/shoot/run-shoot.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAe,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvF,uEAAuE;AACvE,eAAO,MAAM,UAAU,WAAW,CAAC;AAWnC,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,oDAAoD;IACpD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;CACtD;AAED,yCAAyC;AACzC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;IACzC,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CA0E7E"}
1
+ {"version":3,"file":"run-shoot.d.ts","sourceRoot":"","sources":["../../../src/shoot/run-shoot.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAe,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvF,uEAAuE;AACvE,eAAO,MAAM,UAAU,WAAW,CAAC;AAWnC,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,oDAAoD;IACpD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;CACtD;AAED,yCAAyC;AACzC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;IACzC,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAsF7E"}
@@ -2,6 +2,7 @@ import { join, resolve } from "node:path";
2
2
  import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
3
3
  import { PACK_MANIFEST_FILE, SCENARIO_JSON_FILE, buildPackManifest, verifyManifestFilesExist, writePackManifest, } from "../pack/pack-manifest.js";
4
4
  import { readBundleViewport } from "../bundle/read-viewport.js";
5
+ import { readBundleShots } from "../bundle/read-shots.js";
5
6
  import { startBundleServer } from "../serve/static-server.js";
6
7
  import { createPlaywrightShotBrowser } from "./playwright-browser.js";
7
8
  /** Directory inside the bundle where stills land (and deploy from). */
@@ -28,6 +29,12 @@ const VIEWPORT_MARGIN_PX = 64;
28
29
  * or renamed shot must never linger in a deployed bundle, and a manifest
29
30
  * listing deleted files would fail its own existence check at the next
30
31
  * publish.
32
+ *
33
+ * When scenario.json records an authoritatively empty shot list (pack ≥
34
+ * generator 0.0.2), the whole server + browser boot is skipped — both
35
+ * invariants above still hold on that path. A recorded non-empty list does
36
+ * NOT bypass the capture page: the running bundle stays the runtime truth
37
+ * for what gets shot; the record only proves there is something to boot for.
31
38
  */
32
39
  export async function runShoot(options) {
33
40
  const onLog = options.onLog ?? (() => { });
@@ -41,6 +48,17 @@ export async function runShoot(options) {
41
48
  onLog(`Viewport: ${viewport.width}x${viewport.height} (DPR 2)`);
42
49
  onLog(`Themes: ${themes.join(", ")}`);
43
50
  await rm(join(bundleDir, STILLS_DIR), { recursive: true, force: true });
51
+ // Short-circuit on pack's authority: a recorded-and-empty shot list means
52
+ // there is provably nothing to capture, so don't pay for a static server
53
+ // and a Chromium launch just to learn that. Absent record = unknown — fall
54
+ // through and let the capture page answer, exactly as before the record
55
+ // existed.
56
+ const recordedShots = await readBundleShots(bundleDir);
57
+ if (recordedShots.recorded && recordedShots.shots.length === 0) {
58
+ onLog("No steps declare a `shot` — nothing to capture.");
59
+ await rebuildManifest(bundleDir, scenarioId);
60
+ return { scenarioId, bundleDir, shots: [], files: [], themes, verified: false };
61
+ }
44
62
  const server = await startBundleServer({ rootDir: bundleDir, port: 0 });
45
63
  let browser;
46
64
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"run-shoot.js","sourceRoot":"","sources":["../../../src/shoot/run-shoot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run-shoot.js","sourceRoot":"","sources":["../../../src/shoot/run-shoot.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ import {
8
8
  writePackManifest,
9
9
  } from "../pack/pack-manifest.js";
10
10
  import { readBundleViewport } from "../bundle/read-viewport.js";
11
+ import { readBundleShots } from "../bundle/read-shots.js";
11
12
  import { startBundleServer } from "../serve/static-server.js";
12
13
  import { createPlaywrightShotBrowser } from "./playwright-browser.js";
13
14
  import type { ShotBrowser, ShotCaptureInfo, ShotSession, ShotTheme } from "./types.js";
@@ -72,6 +73,12 @@ export interface ShootResult {
72
73
  * or renamed shot must never linger in a deployed bundle, and a manifest
73
74
  * listing deleted files would fail its own existence check at the next
74
75
  * publish.
76
+ *
77
+ * When scenario.json records an authoritatively empty shot list (pack ≥
78
+ * generator 0.0.2), the whole server + browser boot is skipped — both
79
+ * invariants above still hold on that path. A recorded non-empty list does
80
+ * NOT bypass the capture page: the running bundle stays the runtime truth
81
+ * for what gets shot; the record only proves there is something to boot for.
75
82
  */
76
83
  export async function runShoot(options: RunShootOptions): Promise<ShootResult> {
77
84
  const onLog = options.onLog ?? (() => {});
@@ -89,6 +96,18 @@ export async function runShoot(options: RunShootOptions): Promise<ShootResult> {
89
96
 
90
97
  await rm(join(bundleDir, STILLS_DIR), { recursive: true, force: true });
91
98
 
99
+ // Short-circuit on pack's authority: a recorded-and-empty shot list means
100
+ // there is provably nothing to capture, so don't pay for a static server
101
+ // and a Chromium launch just to learn that. Absent record = unknown — fall
102
+ // through and let the capture page answer, exactly as before the record
103
+ // existed.
104
+ const recordedShots = await readBundleShots(bundleDir);
105
+ if (recordedShots.recorded && recordedShots.shots.length === 0) {
106
+ onLog("No steps declare a `shot` — nothing to capture.");
107
+ await rebuildManifest(bundleDir, scenarioId);
108
+ return { scenarioId, bundleDir, shots: [], files: [], themes, verified: false };
109
+ }
110
+
92
111
  const server = await startBundleServer({ rootDir: bundleDir, port: 0 });
93
112
  let browser: ShotBrowser | undefined;
94
113
  try {
@@ -7,10 +7,43 @@ export interface ImportedStep {
7
7
  delayMs: number;
8
8
  narration?: string;
9
9
  }
10
+ /**
11
+ * Find the steps array in a loaded module's exports by duck-typing: the
12
+ * first exported array whose first element has a `delayMs` property.
13
+ * Returns null when the module exports no such array.
14
+ *
15
+ * This is THE steps-discovery rule, shared by every Node-side loader and
16
+ * mirrored verbatim by the browser-side `_findSteps` in the generated
17
+ * pack entry (see generate-embed-entry.ts) — the mirrors must agree, or
18
+ * pack-time tooling would see different steps than the packed bundle.
19
+ */
20
+ export declare function findStepsArray(exports: Record<string, unknown>): ImportedStep[] | null;
21
+ /** A scenario-authored canonical viewport, in CSS pixels. */
22
+ export interface AuthoredViewport {
23
+ width: number;
24
+ height: number;
25
+ }
26
+ /**
27
+ * Find the scenario's authored canonical viewport in a loaded module's
28
+ * exports, or null when the module authors none (the common case — the
29
+ * packer then falls back to CLI flags or its default).
30
+ *
31
+ * Two authored forms are honored, mirroring the two authoring surfaces:
32
+ *
33
+ * 1. A scenario-shaped export — an object carrying both a `viewport` and a
34
+ * delayMs-bearing `steps` array, i.e. what `createScenario()` returns.
35
+ * The viewport rides the scenario, so only a real scenario export
36
+ * qualifies; a stray `viewport` field on an unrelated object does not.
37
+ * 2. An export *named* `viewport` with `{ width, height }` — the
38
+ * directory-form counterpart, for authors of a raw steps array.
39
+ *
40
+ * Precedence between the two follows specificity: a scenario-shaped export
41
+ * wins, because its viewport is inseparable from the steps it sizes.
42
+ */
43
+ export declare function findAuthoredViewport(exports: Record<string, unknown>): AuthoredViewport | null;
10
44
  /**
11
45
  * Dynamically import a TypeScript steps file and extract the
12
- * steps array by duck-typing (looks for the first exported array
13
- * whose elements have a `delayMs` property).
46
+ * steps array by duck-typing ({@link findStepsArray}).
14
47
  *
15
48
  * Requires the caller's Node process to have a TypeScript loader
16
49
  * active (e.g. running via `tsx`). The CLI itself does not depend
@@ -1 +1 @@
1
- {"version":3,"file":"load-ts.d.ts","sourceRoot":"","sources":["../../../src/util/load-ts.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAiB/E"}
1
+ {"version":3,"file":"load-ts.d.ts","sourceRoot":"","sources":["../../../src/util/load-ts.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,YAAY,EAAE,GAAG,IAAI,CAatF;AAED,6DAA6D;AAC7D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAyBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,GAAG,IAAI,CAc9F;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAO/E"}
@@ -1,16 +1,15 @@
1
1
  import { pathToFileURL } from "node:url";
2
2
  /**
3
- * Dynamically import a TypeScript steps file and extract the
4
- * steps array by duck-typing (looks for the first exported array
5
- * whose elements have a `delayMs` property).
3
+ * Find the steps array in a loaded module's exports by duck-typing: the
4
+ * first exported array whose first element has a `delayMs` property.
5
+ * Returns null when the module exports no such array.
6
6
  *
7
- * Requires the caller's Node process to have a TypeScript loader
8
- * active (e.g. running via `tsx`). The CLI itself does not depend
9
- * on any TS compilation tool.
7
+ * This is THE steps-discovery rule, shared by every Node-side loader and
8
+ * mirrored verbatim by the browser-side `_findSteps` in the generated
9
+ * pack entry (see generate-embed-entry.ts) — the mirrors must agree, or
10
+ * pack-time tooling would see different steps than the packed bundle.
10
11
  */
11
- export async function loadStepsFromTs(filePath) {
12
- const mod = await import(pathToFileURL(filePath).href);
13
- const exports = mod.default ?? mod;
12
+ export function findStepsArray(exports) {
14
13
  for (const value of Object.values(exports)) {
15
14
  if (Array.isArray(value) &&
16
15
  value.length > 0 &&
@@ -20,6 +19,73 @@ export async function loadStepsFromTs(filePath) {
20
19
  return value;
21
20
  }
22
21
  }
23
- throw new Error(`No steps array found in ${filePath}`);
22
+ return null;
23
+ }
24
+ function isViewportShape(value) {
25
+ if (typeof value !== "object" || value === null || Array.isArray(value))
26
+ return false;
27
+ const record = value;
28
+ return (typeof record["width"] === "number" &&
29
+ Number.isFinite(record["width"]) &&
30
+ record["width"] > 0 &&
31
+ typeof record["height"] === "number" &&
32
+ Number.isFinite(record["height"]) &&
33
+ record["height"] > 0);
34
+ }
35
+ function isStepsShape(value) {
36
+ return (Array.isArray(value) &&
37
+ value.length > 0 &&
38
+ typeof value[0] === "object" &&
39
+ value[0] !== null &&
40
+ "delayMs" in value[0]);
41
+ }
42
+ /**
43
+ * Find the scenario's authored canonical viewport in a loaded module's
44
+ * exports, or null when the module authors none (the common case — the
45
+ * packer then falls back to CLI flags or its default).
46
+ *
47
+ * Two authored forms are honored, mirroring the two authoring surfaces:
48
+ *
49
+ * 1. A scenario-shaped export — an object carrying both a `viewport` and a
50
+ * delayMs-bearing `steps` array, i.e. what `createScenario()` returns.
51
+ * The viewport rides the scenario, so only a real scenario export
52
+ * qualifies; a stray `viewport` field on an unrelated object does not.
53
+ * 2. An export *named* `viewport` with `{ width, height }` — the
54
+ * directory-form counterpart, for authors of a raw steps array.
55
+ *
56
+ * Precedence between the two follows specificity: a scenario-shaped export
57
+ * wins, because its viewport is inseparable from the steps it sizes.
58
+ */
59
+ export function findAuthoredViewport(exports) {
60
+ for (const value of Object.values(exports)) {
61
+ if (typeof value !== "object" || value === null || Array.isArray(value))
62
+ continue;
63
+ const record = value;
64
+ if (isViewportShape(record["viewport"]) && isStepsShape(record["steps"])) {
65
+ const viewport = record["viewport"];
66
+ return { width: viewport.width, height: viewport.height };
67
+ }
68
+ }
69
+ const named = exports["viewport"];
70
+ if (isViewportShape(named)) {
71
+ return { width: named.width, height: named.height };
72
+ }
73
+ return null;
74
+ }
75
+ /**
76
+ * Dynamically import a TypeScript steps file and extract the
77
+ * steps array by duck-typing ({@link findStepsArray}).
78
+ *
79
+ * Requires the caller's Node process to have a TypeScript loader
80
+ * active (e.g. running via `tsx`). The CLI itself does not depend
81
+ * on any TS compilation tool.
82
+ */
83
+ export async function loadStepsFromTs(filePath) {
84
+ const mod = await import(pathToFileURL(filePath).href);
85
+ const steps = findStepsArray(mod.default ?? mod);
86
+ if (steps === null) {
87
+ throw new Error(`No steps array found in ${filePath}`);
88
+ }
89
+ return steps;
24
90
  }
25
91
  //# sourceMappingURL=load-ts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"load-ts.js","sourceRoot":"","sources":["../../../src/util/load-ts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAYzC;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB;IACpD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC;IAEnC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3C,IACE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YACpB,KAAK,CAAC,MAAM,GAAG,CAAC;YAChB,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ;YAC5B,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;YACjB,SAAS,IAAI,KAAK,CAAC,CAAC,CAAC,EACrB,CAAC;YACD,OAAO,KAAuB,CAAC;QACjC,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,2BAA2B,QAAQ,EAAE,CAAC,CAAC;AACzD,CAAC"}
1
+ {"version":3,"file":"load-ts.js","sourceRoot":"","sources":["../../../src/util/load-ts.ts"],"names":[],"mappings":"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"}