@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.
- package/example-bundle/assets/index-DpJUOVBT.js +388 -0
- package/example-bundle/assets/style-ix_q0zsc.css +1 -0
- package/example-bundle/embed.js +1 -1
- package/example-bundle/index.html +2 -2
- package/example-bundle/pack-manifest.json +11 -11
- package/example-bundle/scenario.json +6 -2
- package/package.json +4 -4
- package/src/__tests__/collect-pack-shots.test.ts +115 -0
- package/src/__tests__/find-authored-viewport.test.ts +58 -0
- package/src/__tests__/pack-generate-embed-entry.test.ts +5 -1
- package/src/__tests__/pack-manifest.test.ts +17 -0
- package/src/__tests__/read-shots.test.ts +57 -0
- package/src/__tests__/resolve-pack-viewport.test.ts +46 -0
- package/src/__tests__/run-shoot.test.ts +51 -2
- package/src/bundle/read-shots.d.ts +28 -0
- package/src/bundle/read-shots.d.ts.map +1 -0
- package/src/bundle/read-shots.js +28 -0
- package/src/bundle/read-shots.js.map +1 -0
- package/src/bundle/read-shots.ts +38 -0
- package/src/commands/pack.d.ts.map +1 -1
- package/src/commands/pack.js +23 -4
- package/src/commands/pack.js.map +1 -1
- package/src/commands/pack.ts +26 -5
- package/src/pack/build.d.ts +33 -1
- package/src/pack/build.d.ts.map +1 -1
- package/src/pack/build.js +35 -10
- package/src/pack/build.js.map +1 -1
- package/src/pack/build.ts +63 -17
- package/src/pack/collect-pack-shots.d.ts +47 -0
- package/src/pack/collect-pack-shots.d.ts.map +1 -0
- package/src/pack/collect-pack-shots.js +66 -0
- package/src/pack/collect-pack-shots.js.map +1 -0
- package/src/pack/collect-pack-shots.ts +89 -0
- package/src/pack/generate-embed-entry.d.ts +7 -0
- package/src/pack/generate-embed-entry.d.ts.map +1 -1
- package/src/pack/generate-embed-entry.js +14 -3
- package/src/pack/generate-embed-entry.js.map +1 -1
- package/src/pack/generate-embed-entry.ts +15 -3
- package/src/pack/pack-manifest.d.ts +12 -3
- package/src/pack/pack-manifest.d.ts.map +1 -1
- package/src/pack/pack-manifest.js +13 -3
- package/src/pack/pack-manifest.js.map +1 -1
- package/src/pack/pack-manifest.ts +13 -2
- package/src/pack/run-pack.d.ts +13 -2
- package/src/pack/run-pack.d.ts.map +1 -1
- package/src/pack/run-pack.js +56 -17
- package/src/pack/run-pack.js.map +1 -1
- package/src/pack/run-pack.ts +81 -19
- package/src/pack/viewport.d.ts +17 -0
- package/src/pack/viewport.d.ts.map +1 -1
- package/src/pack/viewport.js +19 -0
- package/src/pack/viewport.js.map +1 -1
- package/src/pack/viewport.ts +29 -0
- package/src/shoot/run-shoot.d.ts +6 -0
- package/src/shoot/run-shoot.d.ts.map +1 -1
- package/src/shoot/run-shoot.js +18 -0
- package/src/shoot/run-shoot.js.map +1 -1
- package/src/shoot/run-shoot.ts +19 -0
- package/src/util/load-ts.d.ts +35 -2
- package/src/util/load-ts.d.ts.map +1 -1
- package/src/util/load-ts.js +76 -10
- package/src/util/load-ts.js.map +1 -1
- package/src/util/load-ts.ts +87 -11
- package/tsconfig.tsbuildinfo +1 -1
- package/example-bundle/assets/index-BoIR0_nN.js +0 -147
- package/example-bundle/assets/style-YRMeJPPX.css +0 -1
package/src/pack/run-pack.ts
CHANGED
|
@@ -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
|
-
/**
|
|
18
|
-
|
|
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
|
-
/**
|
|
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
|
|
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.
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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 {
|
|
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);
|
package/src/pack/viewport.d.ts
CHANGED
|
@@ -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"}
|
package/src/pack/viewport.js
CHANGED
|
@@ -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
|
}
|
package/src/pack/viewport.js.map
CHANGED
|
@@ -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"}
|
package/src/pack/viewport.ts
CHANGED
|
@@ -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
|
}
|
package/src/shoot/run-shoot.d.ts
CHANGED
|
@@ -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":"
|
|
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"}
|
package/src/shoot/run-shoot.js
CHANGED
|
@@ -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
|
|
1
|
+
{"version":3,"file":"run-shoot.js","sourceRoot":"","sources":["../../../src/shoot/run-shoot.ts"],"names":[],"mappings":"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"}
|
package/src/shoot/run-shoot.ts
CHANGED
|
@@ -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 {
|
package/src/util/load-ts.d.ts
CHANGED
|
@@ -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 (
|
|
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
|
|
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"}
|
package/src/util/load-ts.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { pathToFileURL } from "node:url";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
package/src/util/load-ts.js.map
CHANGED
|
@@ -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
|
|
1
|
+
{"version":3,"file":"load-ts.js","sourceRoot":"","sources":["../../../src/util/load-ts.ts"],"names":[],"mappings":"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"}
|