@scenar/cli 0.9.0 → 0.9.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.
Files changed (52) hide show
  1. package/example-bundle/assets/index-DlmG1Uh7.js +388 -0
  2. package/example-bundle/assets/{style-YRMeJPPX.css → style-BjquDnTE.css} +1 -1
  3. package/example-bundle/index.html +2 -2
  4. package/example-bundle/pack-manifest.json +9 -9
  5. package/example-bundle/scenario.json +6 -2
  6. package/package.json +4 -4
  7. package/src/__tests__/collect-pack-shots.test.ts +97 -0
  8. package/src/__tests__/pack-manifest.test.ts +17 -0
  9. package/src/__tests__/read-shots.test.ts +57 -0
  10. package/src/__tests__/run-shoot.test.ts +51 -2
  11. package/src/bundle/read-shots.d.ts +28 -0
  12. package/src/bundle/read-shots.d.ts.map +1 -0
  13. package/src/bundle/read-shots.js +28 -0
  14. package/src/bundle/read-shots.js.map +1 -0
  15. package/src/bundle/read-shots.ts +38 -0
  16. package/src/pack/build.d.ts +33 -1
  17. package/src/pack/build.d.ts.map +1 -1
  18. package/src/pack/build.js +35 -10
  19. package/src/pack/build.js.map +1 -1
  20. package/src/pack/build.ts +63 -17
  21. package/src/pack/collect-pack-shots.d.ts +40 -0
  22. package/src/pack/collect-pack-shots.d.ts.map +1 -0
  23. package/src/pack/collect-pack-shots.js +59 -0
  24. package/src/pack/collect-pack-shots.js.map +1 -0
  25. package/src/pack/collect-pack-shots.ts +76 -0
  26. package/src/pack/generate-embed-entry.d.ts +7 -0
  27. package/src/pack/generate-embed-entry.d.ts.map +1 -1
  28. package/src/pack/generate-embed-entry.js +10 -2
  29. package/src/pack/generate-embed-entry.js.map +1 -1
  30. package/src/pack/generate-embed-entry.ts +11 -2
  31. package/src/pack/pack-manifest.d.ts +12 -3
  32. package/src/pack/pack-manifest.d.ts.map +1 -1
  33. package/src/pack/pack-manifest.js +13 -3
  34. package/src/pack/pack-manifest.js.map +1 -1
  35. package/src/pack/pack-manifest.ts +13 -2
  36. package/src/pack/run-pack.d.ts +6 -0
  37. package/src/pack/run-pack.d.ts.map +1 -1
  38. package/src/pack/run-pack.js +41 -14
  39. package/src/pack/run-pack.js.map +1 -1
  40. package/src/pack/run-pack.ts +55 -14
  41. package/src/shoot/run-shoot.d.ts +6 -0
  42. package/src/shoot/run-shoot.d.ts.map +1 -1
  43. package/src/shoot/run-shoot.js +18 -0
  44. package/src/shoot/run-shoot.js.map +1 -1
  45. package/src/shoot/run-shoot.ts +19 -0
  46. package/src/util/load-ts.d.ts +12 -2
  47. package/src/util/load-ts.d.ts.map +1 -1
  48. package/src/util/load-ts.js +25 -10
  49. package/src/util/load-ts.js.map +1 -1
  50. package/src/util/load-ts.ts +25 -11
  51. package/tsconfig.tsbuildinfo +1 -1
  52. package/example-bundle/assets/index-BoIR0_nN.js +0 -147
@@ -41,14 +41,24 @@ export const SCENARIO_JSON_FILE = "scenario.json";
41
41
  * `viewport` baked into the bundle: the player reads its steps from the bundled
42
42
  * JS, so the full serialized spec is still a follow-up, but `deploy` needs the
43
43
  * viewport to derive a correctly-proportioned embed snippet (DD-004). The
44
- * viewport mirrors `ViewportConfig { width, height }`. Validated against the
45
- * backend rules before writing (which accept the extra `viewport` key).
44
+ * viewport mirrors `ViewportConfig { width, height }`.
45
+ *
46
+ * `shots` records the scenario's declared shot names (in step order) when
47
+ * pack could discover them, so tooling — `scenar shoot` first of all — can
48
+ * learn a bundle's shots without booting a browser. The key is written only
49
+ * when the list is known: absent means "unknown", present (even empty) is
50
+ * authoritative. Names only, never times — shot times depend on narration
51
+ * and are derived at the point of use (DD-004's derive-don't-store rule).
52
+ *
53
+ * Validated against the backend rules before writing (which accept the
54
+ * extra `viewport`/`shots` keys).
46
55
  */
47
56
  export async function writeScenarioJson(
48
57
  outDir: string,
49
58
  scenarioId: string,
50
59
  generatorVersion: string,
51
60
  viewport: Viewport,
61
+ shots?: readonly string[],
52
62
  ): Promise<void> {
53
63
  const content = JSON.stringify(
54
64
  {
@@ -56,6 +66,7 @@ export async function writeScenarioJson(
56
66
  id: scenarioId,
57
67
  generator: `@scenar/cli pack ${generatorVersion}`,
58
68
  viewport: { width: viewport.width, height: viewport.height },
69
+ ...(shots !== undefined ? { shots } : {}),
59
70
  },
60
71
  null,
61
72
  2,
@@ -27,6 +27,12 @@ export interface PackResult {
27
27
  readonly renderFilePath: string;
28
28
  readonly providersPath: string | null;
29
29
  readonly hasNarration: boolean;
30
+ /**
31
+ * The declared shot names recorded in scenario.json (empty = the scenario
32
+ * authoritatively declares none), or undefined when the steps module could
33
+ * not be loaded under Node and the shots are unknown.
34
+ */
35
+ readonly shots?: readonly string[];
30
36
  readonly manifest: PackManifest;
31
37
  readonly totalBytes: number;
32
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"run-pack.d.ts","sourceRoot":"","sources":["../../../src/pack/run-pack.ts"],"names":[],"mappings":"AAOA,OAAO,EAKL,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;AAM5B,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,+EAA+E;IAC/E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C;AAED,wCAAwC;AACxC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CA2F1E"}
1
+ {"version":3,"file":"run-pack.d.ts","sourceRoot":"","sources":["../../../src/pack/run-pack.ts"],"names":[],"mappings":"AAQA,OAAO,EAKL,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;AAS5B,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,+EAA+E;IAC/E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C;AAED,wCAAwC;AACxC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CAiH1E"}
@@ -3,12 +3,16 @@ 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 } from "./collect-pack-shots.js";
6
7
  import { runViteBuild } from "./build.js";
7
8
  import { copyEmbedLoader } from "./embed-loader.js";
8
9
  import { buildPackManifest, writePackManifest, writeScenarioJson, verifyManifestFilesExist, } from "./pack-manifest.js";
9
10
  import { DEFAULT_VIEWPORT } from "./viewport.js";
10
- /** Generator version stamped into scenario.json (kept in sync with the CLI). */
11
- const PACK_GENERATOR_VERSION = "0.0.1";
11
+ /**
12
+ * Generator version stamped into scenario.json (kept in sync with the CLI).
13
+ * 0.0.2: scenario.json may carry `shots` (declared shot names, in step order).
14
+ */
15
+ const PACK_GENERATOR_VERSION = "0.0.2";
12
16
  /**
13
17
  * Bundle a scenario directory into a self-contained, hosted embed — the pure
14
18
  * orchestration behind `scenar pack`, with no process/exit coupling so both the
@@ -42,7 +46,14 @@ export async function runPack(options) {
42
46
  onLog(`Providers: ${providersPath ?? "none"}`);
43
47
  onLog(`Narration: ${hasNarration ? "yes (manifest found)" : "none"}`);
44
48
  onLog(`Output: ${outDir}`);
45
- // 1. Generate the browser entry + index.html into the temp dir.
49
+ // 1. Discover the declared shots by SSR-loading the steps module (same
50
+ // Vite resolution as the build below). Before the build on purpose:
51
+ // authoring errors — bad shot names, no steps array — fail fast here,
52
+ // without paying for a bundle. An import failure is tolerated: the
53
+ // shots are then unknown and scenario.json omits the key.
54
+ const collectedShots = await collectPackShots(scenarioDir);
55
+ onLog(`Shots: ${describeCollectedShots(collectedShots)}`);
56
+ // 2. Generate the browser entry + index.html into the temp dir.
46
57
  const stage = options.stage ?? false;
47
58
  const entrySource = generateEmbedEntry({
48
59
  scenarioDir,
@@ -59,34 +70,42 @@ export async function runPack(options) {
59
70
  const entryHtmlPath = join(tempDir, "index.html");
60
71
  await writeFile(join(tempDir, entryFileName), entrySource, "utf-8");
61
72
  await writeFile(entryHtmlPath, generateEmbedHtml(scenarioId, entryFileName, stage), "utf-8");
62
- // 2. Build the static bundle with Vite.
73
+ // 3. Build the static bundle with Vite.
63
74
  onLog("Bundling embed with Vite...");
64
75
  await runViteBuild({ root: tempDir, outDir, entryHtmlPath });
65
- // 3. Copy the narration manifest + audio. The embed fetches
76
+ // 4. Copy the narration manifest + audio. The embed fetches
66
77
  // ./narration/manifest.json at runtime and resolves each clip's
67
78
  // relative src against it, so both must ship in the bundle.
68
79
  if (hasNarration) {
69
80
  await copyNarration(scenarioDir, outDir);
70
81
  }
71
- // 4. Write the required scenario.json descriptor, recording the canonical
82
+ // 5. Write the required scenario.json descriptor, recording the canonical
72
83
  // viewport baked into the bundle so `deploy`/`serve`/`publish` can derive
73
- // a correctly-proportioned embed snippet (DD-004).
74
- await writeScenarioJson(outDir, scenarioId, PACK_GENERATOR_VERSION, {
75
- width,
76
- height: shellHeight,
77
- });
78
- // 4b. Copy the optional <scenar-embed> loader into the bundle as embed.js
84
+ // a correctly-proportioned embed snippet (DD-004), and — when known —
85
+ // the declared shot names so `shoot` and other tooling never boot a
86
+ // browser just to learn a bundle has nothing to capture.
87
+ await writeScenarioJson(outDir, scenarioId, PACK_GENERATOR_VERSION, { width, height: shellHeight }, collectedShots.recorded ? collectedShots.shots : undefined);
88
+ // 5b. Copy the optional <scenar-embed> loader into the bundle as embed.js
79
89
  // (sibling of index.html), so the enhanced snippet works on any static
80
90
  // host (GitHub Pages, `serve`, the edge) with no extra setup. It lands
81
91
  // before the manifest pass below so it ships as a first-class bundle file.
82
92
  await copyEmbedLoader(outDir);
83
- // 5. Compute + write the deploy-facing pack manifest (validates the bundle
93
+ // 6. Compute + write the deploy-facing pack manifest (validates the bundle
84
94
  // against the backend allowlist; throws on any violation).
85
95
  const manifest = await buildPackManifest(outDir, scenarioId);
86
96
  await verifyManifestFilesExist(outDir, manifest);
87
97
  await writePackManifest(outDir, manifest);
88
98
  const totalBytes = manifest.files.reduce((sum, f) => sum + f.sizeBytes, 0);
89
- return { scenarioId, outDir, renderFilePath, providersPath, hasNarration, manifest, totalBytes };
99
+ return {
100
+ scenarioId,
101
+ outDir,
102
+ renderFilePath,
103
+ providersPath,
104
+ hasNarration,
105
+ shots: collectedShots.recorded ? collectedShots.shots : undefined,
106
+ manifest,
107
+ totalBytes,
108
+ };
90
109
  }
91
110
  finally {
92
111
  if (!options.keepTemp) {
@@ -113,6 +132,14 @@ async function copyNarration(scenarioDir, outDir) {
113
132
  }
114
133
  }
115
134
  }
135
+ /** One log-friendly line for the shot discovery outcome. */
136
+ function describeCollectedShots(collected) {
137
+ if (!collected.recorded) {
138
+ // Vite errors can run many lines; the first carries the message.
139
+ return `unknown — steps module not loadable under Node (${collected.reason.split("\n")[0]})`;
140
+ }
141
+ return collected.shots.length === 0 ? "none declared" : collected.shots.join(", ");
142
+ }
116
143
  async function fileExists(path) {
117
144
  try {
118
145
  await access(path);
@@ -1 +1 @@
1
- {"version":3,"file":"run-pack.js","sourceRoot":"","sources":["../../../src/pack/run-pack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,wBAAwB,GAEzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,gFAAgF;AAChF,MAAM,sBAAsB,GAAG,OAAO,CAAC;AAmCvC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,OAAuB;IACnD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC1C,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAEjD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACvD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,CAAC,WAAW,kBAAkB,CAAC,CAAC;IAC5D,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,GAAG,OAAO,CAAC,WAAW,uBAAuB;YAC3C,iEAAiE,CACpE,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,UAAU,SAAS,CAAC,CAAC;IAC5F,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,gBAAgB,CAAC,KAAK,CAAC;IACtD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,gBAAgB,CAAC,MAAM,CAAC;IAEnE,qEAAqE;IACrE,8DAA8D;IAC9D,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IAElD,IAAI,CAAC;QACH,MAAM,cAAc,GAAG,MAAM,kBAAkB,CAAC,WAAW,CAAC,CAAC;QAC7D,MAAM,aAAa,GAAG,MAAM,oBAAoB,CAAC,WAAW,CAAC,CAAC;QAC9D,MAAM,YAAY,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,eAAe,CAAC,CAAC,CAAC;QAEvF,KAAK,CAAC,cAAc,UAAU,EAAE,CAAC,CAAC;QAClC,KAAK,CAAC,cAAc,cAAc,EAAE,CAAC,CAAC;QACtC,KAAK,CAAC,cAAc,aAAa,IAAI,MAAM,EAAE,CAAC,CAAC;QAC/C,KAAK,CAAC,cAAc,YAAY,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QACtE,KAAK,CAAC,cAAc,MAAM,EAAE,CAAC,CAAC;QAE9B,gEAAgE;QAChE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC;QACrC,MAAM,WAAW,GAAG,kBAAkB,CAAC;YACrC,WAAW;YACX,cAAc;YACd,UAAU;YACV,YAAY;YACZ,aAAa;YACb,cAAc,EAAE,KAAK;YACrB,WAAW;YACX,KAAK;SACN,CAAC,CAAC;QACH,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,MAAM,aAAa,GAAG,WAAW,CAAC;QAClC,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QAClD,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;QACpE,MAAM,SAAS,CAAC,aAAa,EAAE,iBAAiB,CAAC,UAAU,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;QAE7F,wCAAwC;QACxC,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACrC,MAAM,YAAY,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;QAE7D,4DAA4D;QAC5D,mEAAmE;QACnE,+DAA+D;QAC/D,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,aAAa,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;QAED,0EAA0E;QAC1E,6EAA6E;QAC7E,sDAAsD;QACtD,MAAM,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,sBAAsB,EAAE;YAClE,KAAK;YACL,MAAM,EAAE,WAAW;SACpB,CAAC,CAAC;QAEH,0EAA0E;QAC1E,2EAA2E;QAC3E,2EAA2E;QAC3E,+EAA+E;QAC/E,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;QAE9B,2EAA2E;QAC3E,8DAA8D;QAC9D,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAC7D,MAAM,wBAAwB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACjD,MAAM,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAE1C,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IACnG,CAAC;YAAS,CAAC;QACT,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YACtB,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,aAAa,CAAC,WAAmB,EAAE,MAAc;IAC9D,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC1C,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;YAAE,SAAS;QAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACtC,IAAI,IAAI,KAAK,eAAe,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACtD,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;AACH,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,IAAY;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"run-pack.js","sourceRoot":"","sources":["../../../src/pack/run-pack.ts"],"names":[],"mappings":"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"}
@@ -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 {
@@ -14,8 +15,11 @@ import {
14
15
  } from "./pack-manifest.js";
15
16
  import { DEFAULT_VIEWPORT } 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 {
@@ -46,6 +50,12 @@ export interface PackResult {
46
50
  readonly renderFilePath: string;
47
51
  readonly providersPath: string | null;
48
52
  readonly hasNarration: boolean;
53
+ /**
54
+ * The declared shot names recorded in scenario.json (empty = the scenario
55
+ * authoritatively declares none), or undefined when the steps module could
56
+ * not be loaded under Node and the shots are unknown.
57
+ */
58
+ readonly shots?: readonly string[];
49
59
  readonly manifest: PackManifest;
50
60
  readonly totalBytes: number;
51
61
  }
@@ -91,7 +101,15 @@ export async function runPack(options: RunPackOptions): Promise<PackResult> {
91
101
  onLog(`Narration: ${hasNarration ? "yes (manifest found)" : "none"}`);
92
102
  onLog(`Output: ${outDir}`);
93
103
 
94
- // 1. Generate the browser entry + index.html into the temp dir.
104
+ // 1. Discover the declared shots by SSR-loading the steps module (same
105
+ // Vite resolution as the build below). Before the build on purpose:
106
+ // authoring errors — bad shot names, no steps array — fail fast here,
107
+ // without paying for a bundle. An import failure is tolerated: the
108
+ // shots are then unknown and scenario.json omits the key.
109
+ const collectedShots = await collectPackShots(scenarioDir);
110
+ onLog(`Shots: ${describeCollectedShots(collectedShots)}`);
111
+
112
+ // 2. Generate the browser entry + index.html into the temp dir.
95
113
  const stage = options.stage ?? false;
96
114
  const entrySource = generateEmbedEntry({
97
115
  scenarioDir,
@@ -109,39 +127,53 @@ export async function runPack(options: RunPackOptions): Promise<PackResult> {
109
127
  await writeFile(join(tempDir, entryFileName), entrySource, "utf-8");
110
128
  await writeFile(entryHtmlPath, generateEmbedHtml(scenarioId, entryFileName, stage), "utf-8");
111
129
 
112
- // 2. Build the static bundle with Vite.
130
+ // 3. Build the static bundle with Vite.
113
131
  onLog("Bundling embed with Vite...");
114
132
  await runViteBuild({ root: tempDir, outDir, entryHtmlPath });
115
133
 
116
- // 3. Copy the narration manifest + audio. The embed fetches
134
+ // 4. Copy the narration manifest + audio. The embed fetches
117
135
  // ./narration/manifest.json at runtime and resolves each clip's
118
136
  // relative src against it, so both must ship in the bundle.
119
137
  if (hasNarration) {
120
138
  await copyNarration(scenarioDir, outDir);
121
139
  }
122
140
 
123
- // 4. Write the required scenario.json descriptor, recording the canonical
141
+ // 5. Write the required scenario.json descriptor, recording the canonical
124
142
  // 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
- });
143
+ // a correctly-proportioned embed snippet (DD-004), and — when known —
144
+ // the declared shot names so `shoot` and other tooling never boot a
145
+ // browser just to learn a bundle has nothing to capture.
146
+ await writeScenarioJson(
147
+ outDir,
148
+ scenarioId,
149
+ PACK_GENERATOR_VERSION,
150
+ { width, height: shellHeight },
151
+ collectedShots.recorded ? collectedShots.shots : undefined,
152
+ );
130
153
 
131
- // 4b. Copy the optional <scenar-embed> loader into the bundle as embed.js
154
+ // 5b. Copy the optional <scenar-embed> loader into the bundle as embed.js
132
155
  // (sibling of index.html), so the enhanced snippet works on any static
133
156
  // host (GitHub Pages, `serve`, the edge) with no extra setup. It lands
134
157
  // before the manifest pass below so it ships as a first-class bundle file.
135
158
  await copyEmbedLoader(outDir);
136
159
 
137
- // 5. Compute + write the deploy-facing pack manifest (validates the bundle
160
+ // 6. Compute + write the deploy-facing pack manifest (validates the bundle
138
161
  // against the backend allowlist; throws on any violation).
139
162
  const manifest = await buildPackManifest(outDir, scenarioId);
140
163
  await verifyManifestFilesExist(outDir, manifest);
141
164
  await writePackManifest(outDir, manifest);
142
165
 
143
166
  const totalBytes = manifest.files.reduce((sum, f) => sum + f.sizeBytes, 0);
144
- return { scenarioId, outDir, renderFilePath, providersPath, hasNarration, manifest, totalBytes };
167
+ return {
168
+ scenarioId,
169
+ outDir,
170
+ renderFilePath,
171
+ providersPath,
172
+ hasNarration,
173
+ shots: collectedShots.recorded ? collectedShots.shots : undefined,
174
+ manifest,
175
+ totalBytes,
176
+ };
145
177
  } finally {
146
178
  if (!options.keepTemp) {
147
179
  await rm(tempDir, { recursive: true, force: true }).catch(() => {});
@@ -168,6 +200,15 @@ async function copyNarration(scenarioDir: string, outDir: string): Promise<void>
168
200
  }
169
201
  }
170
202
 
203
+ /** One log-friendly line for the shot discovery outcome. */
204
+ function describeCollectedShots(collected: CollectedPackShots): string {
205
+ if (!collected.recorded) {
206
+ // Vite errors can run many lines; the first carries the message.
207
+ return `unknown — steps module not loadable under Node (${collected.reason.split("\n")[0]})`;
208
+ }
209
+ return collected.shots.length === 0 ? "none declared" : collected.shots.join(", ");
210
+ }
211
+
171
212
  async function fileExists(path: string): Promise<boolean> {
172
213
  try {
173
214
  await access(path);
@@ -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":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,wBAAwB,EACxB,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAGtE,uEAAuE;AACvE,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC;AAEnC,qEAAqE;AACrE,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC;;;GAGG;AACH,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAmC9B;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAwB;IACrD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAK,CAAC,OAAO,EAAE,MAAM,CAAW,CAAC;IAC9D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,kBAAkB,CAAC;IAE1D,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC;IACnD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAEzD,KAAK,CAAC,cAAc,UAAU,EAAE,CAAC,CAAC;IAClC,KAAK,CAAC,cAAc,SAAS,EAAE,CAAC,CAAC;IACjC,KAAK,CAAC,cAAc,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,MAAM,UAAU,CAAC,CAAC;IACjE,KAAK,CAAC,cAAc,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEzC,MAAM,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAExE,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IACxE,IAAI,OAAgC,CAAC;IACrC,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,cAAc,IAAI,2BAA2B,CAAC,EAAE,CAAC;QAE1E,MAAM,eAAe,GAAG;YACtB,KAAK,EAAE,QAAQ,CAAC,KAAK,GAAG,kBAAkB;YAC1C,MAAM,EAAE,QAAQ,CAAC,MAAM,GAAG,kBAAkB;SAC7C,CAAC;QAEF,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA0D,CAAC;QACnF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC,CAAC;QAClG,CAAC;QAED,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAE,CAAE,CAAC,IAAI,CAAC;QAC7C,4BAA4B,CAAC,QAAQ,CAAC,CAAC;QAEvC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,KAAK,CAAC,iDAAiD,CAAC,CAAC;YACzD,MAAM,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;YAC7C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QAClF,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,KAAK,CAAC,6DAA6D,CAAC,CAAC;YACrE,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;gBACzF,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAE,EAAE,KAAK,CAAC,CAAC;YAC1D,CAAC;YACD,KAAK,CAAC,mEAAmE,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9D,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC;YAC9C,KAAK,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;gBAC7C,MAAM,QAAQ,GAAG,GAAG,UAAU,IAAI,IAAI,CAAC,IAAI,IAAI,KAAK,MAAM,CAAC;gBAC3D,MAAM,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC;gBACrE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACrB,KAAK,CAAC,cAAc,QAAQ,EAAE,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;QAED,MAAM,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAC7C,OAAO;YACL,UAAU;YACV,SAAS;YACT,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,KAAK;YACL,MAAM;YACN,QAAQ,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;SAClC,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACvC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACvC,CAAC;AACH,CAAC;AAED,0EAA0E;AAC1E,KAAK,UAAU,YAAY,CACzB,OAAoB,EACpB,OAAe,EACf,KAAgB,EAChB,QAA2C,EAC3C,SAAiB;IAEjB,yEAAyE;IACzE,qEAAqE;IACrE,wDAAwD;IACxD,MAAM,GAAG,GAAG,GAAG,OAAO,QAAQ,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACtE,MAAM,OAAO,GAAgB,MAAM,OAAO,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3E,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAChD,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,oEAAoE;QACpE,0DAA0D;QAC1D,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,KAAK,UAAU,cAAc,CAAC,SAAiB;IAC7C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACrD,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,GAAG,SAAS,iEAAiE,CAAC,CAAC;IACjG,CAAC;IACD,KAAK,MAAM,QAAQ,IAAI,CAAC,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,CAAC,EAAE,CAAC;QAC9E,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACzE,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACb,MAAM,QAAQ,OAAO,SAAS,4DAA4D,CAC3F,CAAC;QACJ,CAAC;IACH,CAAC;IACD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAC7B,MAAM,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,EAAE,OAAO,CAAC,CACzC,CAAC;IACtB,IAAI,OAAO,YAAY,CAAC,EAAE,KAAK,QAAQ,IAAI,YAAY,CAAC,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,GAAG,kBAAkB,OAAO,SAAS,sBAAsB,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,YAAY,CAAC,EAAE,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,SAAS,4BAA4B,CACnC,QAAqE;IAErE,MAAM,KAAK,GAAG,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CACvC,CAAC,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAU,CACpE,CAAC;IACF,MAAM,CAAC,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IAChC,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QAClC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,uBAAuB,KAAK,gDAAgD,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,EAAE,CAC3F,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,SAAS,mBAAmB,CAC1B,KAAgB,EAChB,KAAkD,EAClD,MAAmD;IAEnD,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK;SAC/B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC;SAC7D,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,6BAA6B,KAAK,cAAc,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY;YAC9E,uEAAuE;YACvE,yEAAyE,CAC5E,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,eAAe,CAAC,SAAiB,EAAE,UAAkB;IAClE,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAChE,MAAM,wBAAwB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACpD,MAAM,iBAAiB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC/C,CAAC"}
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,20 @@ 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;
10
21
  /**
11
22
  * 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).
23
+ * steps array by duck-typing ({@link findStepsArray}).
14
24
  *
15
25
  * Requires the caller's Node process to have a TypeScript loader
16
26
  * 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;;;;;;;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,22 @@ 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
+ /**
25
+ * Dynamically import a TypeScript steps file and extract the
26
+ * steps array by duck-typing ({@link findStepsArray}).
27
+ *
28
+ * Requires the caller's Node process to have a TypeScript loader
29
+ * active (e.g. running via `tsx`). The CLI itself does not depend
30
+ * on any TS compilation tool.
31
+ */
32
+ export async function loadStepsFromTs(filePath) {
33
+ const mod = await import(pathToFileURL(filePath).href);
34
+ const steps = findStepsArray(mod.default ?? mod);
35
+ if (steps === null) {
36
+ throw new Error(`No steps array found in ${filePath}`);
37
+ }
38
+ return steps;
24
39
  }
25
40
  //# 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":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAYzC;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgC;IAC7D,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;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;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,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC;IACjD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,2BAA2B,QAAQ,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -11,18 +11,16 @@ export interface ImportedStep {
11
11
  }
12
12
 
13
13
  /**
14
- * Dynamically import a TypeScript steps file and extract the
15
- * steps array by duck-typing (looks for the first exported array
16
- * whose elements have a `delayMs` property).
14
+ * Find the steps array in a loaded module's exports by duck-typing: the
15
+ * first exported array whose first element has a `delayMs` property.
16
+ * Returns null when the module exports no such array.
17
17
  *
18
- * Requires the caller's Node process to have a TypeScript loader
19
- * active (e.g. running via `tsx`). The CLI itself does not depend
20
- * on any TS compilation tool.
18
+ * This is THE steps-discovery rule, shared by every Node-side loader and
19
+ * mirrored verbatim by the browser-side `_findSteps` in the generated
20
+ * pack entry (see generate-embed-entry.ts) — the mirrors must agree, or
21
+ * pack-time tooling would see different steps than the packed bundle.
21
22
  */
22
- export async function loadStepsFromTs(filePath: string): Promise<ImportedStep[]> {
23
- const mod = await import(pathToFileURL(filePath).href);
24
- const exports = mod.default ?? mod;
25
-
23
+ export function findStepsArray(exports: Record<string, unknown>): ImportedStep[] | null {
26
24
  for (const value of Object.values(exports)) {
27
25
  if (
28
26
  Array.isArray(value) &&
@@ -34,6 +32,22 @@ export async function loadStepsFromTs(filePath: string): Promise<ImportedStep[]>
34
32
  return value as ImportedStep[];
35
33
  }
36
34
  }
35
+ return null;
36
+ }
37
37
 
38
- throw new Error(`No steps array found in ${filePath}`);
38
+ /**
39
+ * Dynamically import a TypeScript steps file and extract the
40
+ * steps array by duck-typing ({@link findStepsArray}).
41
+ *
42
+ * Requires the caller's Node process to have a TypeScript loader
43
+ * active (e.g. running via `tsx`). The CLI itself does not depend
44
+ * on any TS compilation tool.
45
+ */
46
+ export async function loadStepsFromTs(filePath: string): Promise<ImportedStep[]> {
47
+ const mod = await import(pathToFileURL(filePath).href);
48
+ const steps = findStepsArray(mod.default ?? mod);
49
+ if (steps === null) {
50
+ throw new Error(`No steps array found in ${filePath}`);
51
+ }
52
+ return steps;
39
53
  }