@juxtapose/core 0.8.0 → 0.9.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/dist/agent/handoff.d.ts +16 -0
- package/dist/agent/handoff.js +37 -2
- package/dist/agent/handoff.js.map +1 -1
- package/dist/analysis/affected.d.ts +17 -0
- package/dist/analysis/affected.js +34 -0
- package/dist/analysis/affected.js.map +1 -1
- package/dist/browser/flags.d.ts +14 -0
- package/dist/browser/flags.js +15 -0
- package/dist/browser/flags.js.map +1 -1
- package/dist/browser/page.d.ts +10 -0
- package/dist/browser/page.js +9 -3
- package/dist/browser/page.js.map +1 -1
- package/dist/capture/capture.d.ts +3 -1
- package/dist/capture/capture.js +81 -8
- package/dist/capture/capture.js.map +1 -1
- package/dist/capture/discover.d.ts +28 -0
- package/dist/capture/discover.js +109 -0
- package/dist/capture/discover.js.map +1 -0
- package/dist/capture/pool.d.ts +2 -0
- package/dist/capture/pool.js +1 -0
- package/dist/capture/pool.js.map +1 -1
- package/dist/capture/variants.d.ts +47 -0
- package/dist/capture/variants.js +192 -0
- package/dist/capture/variants.js.map +1 -0
- package/dist/diff/structural/deltas.d.ts +2 -3
- package/dist/diff/structural/deltas.js +3 -3
- package/dist/diff/structural/deltas.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/platform.d.ts +20 -0
- package/dist/platforms/urls.js +5 -1
- package/dist/platforms/urls.js.map +1 -1
- package/dist/report/comment.js +19 -8
- package/dist/report/comment.js.map +1 -1
- package/dist/report/format.d.ts +17 -0
- package/dist/report/format.js +16 -0
- package/dist/report/format.js.map +1 -1
- package/dist/report/html.js +40 -6
- package/dist/report/html.js.map +1 -1
- package/dist/run/run.d.ts +2 -0
- package/dist/run/run.js +15 -0
- package/dist/run/run.js.map +1 -1
- package/dist/types.d.ts +47 -1
- package/package.json +1 -1
package/dist/capture/capture.js
CHANGED
|
@@ -2,13 +2,18 @@
|
|
|
2
2
|
// each step exists. Because pass/fail is a byte-exact hash, every source of
|
|
3
3
|
// nondeterminism left in this path becomes a false failure somewhere.
|
|
4
4
|
import sharp from 'sharp';
|
|
5
|
+
import { emulatedMedia } from '../browser/page.js';
|
|
5
6
|
import { assertCssSelector } from '../browser/selectors.js';
|
|
6
7
|
import { armSettle, drain, FREEZE_ANIMATIONS_CSS, waitForPaint, waitForSelector, } from './settle.js';
|
|
7
8
|
import { captureStructure } from './structure.js';
|
|
9
|
+
import { deepEqual, mergeLiveParams, platformStoryId } from './variants.js';
|
|
8
10
|
const DEFAULT_CAPTURE_TIMEOUT_MS = 2000;
|
|
9
11
|
/** Per-page memo: skip work that only needs doing once per page. */
|
|
10
12
|
const styleInjected = new WeakSet();
|
|
11
13
|
const pageViewport = new WeakMap();
|
|
14
|
+
const pageColorScheme = new WeakMap();
|
|
15
|
+
/** The capture last shown on a page: its key and its platform story id. */
|
|
16
|
+
const pageCapture = new WeakMap();
|
|
12
17
|
const warmPages = new WeakMap();
|
|
13
18
|
export async function captureStory(page, story, deps, viewport) {
|
|
14
19
|
const started = Date.now();
|
|
@@ -17,30 +22,67 @@ export async function captureStory(page, story, deps, viewport) {
|
|
|
17
22
|
const params = { ...story.params };
|
|
18
23
|
const effectiveViewport = params.viewport ?? viewport;
|
|
19
24
|
const timeoutMs = params.captureTimeout ?? DEFAULT_CAPTURE_TIMEOUT_MS;
|
|
20
|
-
|
|
25
|
+
// Every platform call gets the platform's own id; the variant capture key
|
|
26
|
+
// means nothing to Storybook or Ladle.
|
|
27
|
+
const id = platformStoryId(story);
|
|
28
|
+
const ctx = {
|
|
29
|
+
story: id === story.storyId ? story : { ...story, storyId: id },
|
|
30
|
+
params,
|
|
31
|
+
timeoutMs,
|
|
32
|
+
};
|
|
21
33
|
// Selector syntax is validated before the page is touched: a Playwright-only
|
|
22
34
|
// selector (`text=`, `>>`) would otherwise match nothing and the story would
|
|
23
35
|
// be captured unmasked, or wait its whole timeout, and the baseline would
|
|
24
36
|
// record the wrong thing. Live params are re-checked below.
|
|
25
37
|
assertSelectors(params);
|
|
26
38
|
await ensureViewport(page, effectiveViewport);
|
|
39
|
+
// Only a variant sets a scheme: a default capture resets to light, which is
|
|
40
|
+
// what stops a pooled page carrying a dark variant's scheme into the next
|
|
41
|
+
// story.
|
|
42
|
+
const scheme = story.variant !== undefined ? (params.colorScheme ?? 'light') : 'light';
|
|
27
43
|
// Arm before triggering the render, or the observer misses the mutations it
|
|
28
44
|
// exists to watch.
|
|
29
45
|
//
|
|
30
46
|
// A story that wants animations running can't reuse a page whose stylesheet
|
|
31
|
-
// already froze them, so force the cold path for those.
|
|
47
|
+
// already froze them, so force the cold path for those. Likewise a scheme
|
|
48
|
+
// change: an app that reads matchMedia once at boot keeps the old scheme
|
|
49
|
+
// across a warm switch (measured: card then card__dark captured both light,
|
|
50
|
+
// the reverse both dark). Decided before the scheme is applied, against
|
|
51
|
+
// what the page booted under.
|
|
52
|
+
//
|
|
53
|
+
// And another capture of the same story (default -> mobile): the platform
|
|
54
|
+
// sees its story already rendered and "switches" without re-rendering, so
|
|
55
|
+
// the variant would be captured from the previous capture's layout.
|
|
56
|
+
const previous = pageCapture.get(page);
|
|
57
|
+
const sameStoryOtherCapture = previous !== undefined && previous.base === id && previous.key !== story.storyId;
|
|
58
|
+
const schemeChanged = pageColorScheme.has(page) && pageColorScheme.get(page) !== scheme;
|
|
32
59
|
const warm = deps.warmSwitch === true &&
|
|
33
60
|
warmPages.get(page) === deps.baseUrl &&
|
|
61
|
+
!schemeChanged &&
|
|
62
|
+
!sameStoryOtherCapture &&
|
|
34
63
|
!(params.animations === 'play' && styleInjected.has(page));
|
|
64
|
+
// Before showStory, on every capture, so the story renders in its scheme
|
|
65
|
+
// from the first frame. Memoized, so it costs nothing when unchanged.
|
|
66
|
+
await ensureColorScheme(page, scheme);
|
|
67
|
+
// A variant shares its story's URL, and a URL differing only in its
|
|
68
|
+
// fragment is a same-document navigation: the cold path below would not
|
|
69
|
+
// reload, and an app that read the scheme at boot would keep the old one,
|
|
70
|
+
// or keep the previous capture's render. Blank the page so the story boots
|
|
71
|
+
// fresh.
|
|
72
|
+
if ((schemeChanged || sameStoryOtherCapture) && warmPages.has(page)) {
|
|
73
|
+
await page.goto('about:blank');
|
|
74
|
+
warmPages.delete(page);
|
|
75
|
+
}
|
|
76
|
+
pageCapture.set(page, { key: story.storyId, base: id });
|
|
35
77
|
if (warm)
|
|
36
78
|
await armSettle(page);
|
|
37
79
|
const switched = warm && deps.platform.switchStory
|
|
38
|
-
? await deps.platform.switchStory(page,
|
|
80
|
+
? await deps.platform.switchStory(page, id, ctx)
|
|
39
81
|
: false;
|
|
40
82
|
if (!switched) {
|
|
41
83
|
// Delegated rather than built here: not every platform addresses a story
|
|
42
84
|
// by URL. Playwright galleries render via window.mount() on a fixed page.
|
|
43
|
-
await deps.platform.showStory(page,
|
|
85
|
+
await deps.platform.showStory(page, id, deps.baseUrl);
|
|
44
86
|
// A navigation discards injected style tags, so the freeze must be
|
|
45
87
|
// re-applied for the next story that wants it.
|
|
46
88
|
styleInjected.delete(page);
|
|
@@ -70,16 +112,25 @@ export async function captureStory(page, story, deps, viewport) {
|
|
|
70
112
|
// waitForSelector and skip on that platform.
|
|
71
113
|
//
|
|
72
114
|
// Live params override manifest params — the live preview is the story's own
|
|
73
|
-
// declaration, and the index is a lossy projection of it
|
|
74
|
-
|
|
115
|
+
// declaration, and the index is a lossy projection of it — but merge under
|
|
116
|
+
// a variant's own fields, so the mobile variant stays mobile.
|
|
117
|
+
const live = await deps.platform.readLiveParams?.(page, id);
|
|
75
118
|
if (live) {
|
|
76
|
-
Object.assign(params, live);
|
|
119
|
+
Object.assign(params, mergeLiveParams(story, live));
|
|
77
120
|
assertSelectors(params);
|
|
121
|
+
// A story can opt out of a project variant in params only the live
|
|
122
|
+
// preview carries (Storybook). Honour it as a skip.
|
|
123
|
+
if (story.variant !== undefined && live.variants?.[story.variant] === false) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
if (story.variant !== undefined && liveOverrideDiffers(story, live)) {
|
|
127
|
+
deps.onWarning?.(`jux: ${story.storyId}: the story's live parameters declare variant '${story.variant}' differently from the settings it was captured with. Parameters read after render can't change which variants exist or what they set; declare variants in the story's parameters, where discovery reads them before capture.`);
|
|
128
|
+
}
|
|
78
129
|
// Viewport is the one param that cannot be applied retroactively — the
|
|
79
130
|
// story has already laid out. Re-set it and let the platform settle
|
|
80
131
|
// again, so a story declaring its own viewport is captured at that size
|
|
81
132
|
// rather than the project default.
|
|
82
|
-
const liveViewport =
|
|
133
|
+
const liveViewport = params.viewport;
|
|
83
134
|
if (liveViewport && !sameViewport(liveViewport, effectiveViewport)) {
|
|
84
135
|
await ensureViewport(page, liveViewport);
|
|
85
136
|
await deps.platform.settle(page, { ...ctx, params });
|
|
@@ -132,6 +183,8 @@ export async function captureStory(page, story, deps, viewport) {
|
|
|
132
183
|
.toBuffer();
|
|
133
184
|
return {
|
|
134
185
|
storyId: story.storyId,
|
|
186
|
+
...(story.baseStoryId !== undefined ? { baseStoryId: story.baseStoryId } : {}),
|
|
187
|
+
...(story.variant !== undefined ? { variant: story.variant } : {}),
|
|
135
188
|
image,
|
|
136
189
|
// The live params may have widened or narrowed this after first render,
|
|
137
190
|
// so report what we actually captured at — the agent handoff computes
|
|
@@ -142,6 +195,16 @@ export async function captureStory(page, story, deps, viewport) {
|
|
|
142
195
|
...(structure ? { structure } : {}),
|
|
143
196
|
};
|
|
144
197
|
}
|
|
198
|
+
/** Whether the live preview declares this variant with settings other than
|
|
199
|
+
* the ones it was expanded with: Storybook keeps story-level variants out of
|
|
200
|
+
* index.json, so they only surface here, after the capture set is fixed. */
|
|
201
|
+
function liveOverrideDiffers(story, live) {
|
|
202
|
+
const spec = story.variant !== undefined ? live.variants?.[story.variant] : undefined;
|
|
203
|
+
if (spec === undefined || spec === false || typeof spec !== 'object')
|
|
204
|
+
return false;
|
|
205
|
+
const expanded = story.variantParams ?? {};
|
|
206
|
+
return Object.entries(spec).some(([k, v]) => k !== 'extends' && !deepEqual(v, expanded[k]));
|
|
207
|
+
}
|
|
145
208
|
const sameViewport = (a, b) => a.width === b.width && a.height === b.height;
|
|
146
209
|
/** Setting the viewport is a CDP round-trip; skip it when nothing changed. */
|
|
147
210
|
async function ensureViewport(page, viewport) {
|
|
@@ -151,6 +214,16 @@ async function ensureViewport(page, viewport) {
|
|
|
151
214
|
await page.setViewportSize(viewport);
|
|
152
215
|
pageViewport.set(page, key);
|
|
153
216
|
}
|
|
217
|
+
/** Media emulation is a CDP round-trip too; skip it when nothing changed. */
|
|
218
|
+
async function ensureColorScheme(page, scheme) {
|
|
219
|
+
if (pageColorScheme.get(page) === scheme)
|
|
220
|
+
return;
|
|
221
|
+
if (page.setColorScheme)
|
|
222
|
+
await page.setColorScheme(scheme);
|
|
223
|
+
else
|
|
224
|
+
await page.send('Emulation.setEmulatedMedia', emulatedMedia(scheme));
|
|
225
|
+
pageColorScheme.set(page, scheme);
|
|
226
|
+
}
|
|
154
227
|
/**
|
|
155
228
|
* Inject the freeze stylesheet, once per page.
|
|
156
229
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/capture/capture.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,4EAA4E;AAC5E,sEAAsE;AAEtE,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/capture/capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Browser } from '../browser/browser.js';
|
|
2
|
+
import type { EnumerateTarget, Platform } from '../platform.js';
|
|
3
|
+
import type { StoryManifest, VariantSpec, Viewport } from '../types.js';
|
|
4
|
+
export interface PrepareOptions {
|
|
5
|
+
platform: Platform;
|
|
6
|
+
browser: Browser;
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
viewport: Viewport;
|
|
9
|
+
/** `ProjectConfig.variants`. */
|
|
10
|
+
projectVariants?: Record<string, VariantSpec | false>;
|
|
11
|
+
onWarning?: (message: string) => void;
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
/** Keep discovered params on disk, keyed by the platform's
|
|
14
|
+
* `discoveryKey`, so an unchanged build skips the preview load. */
|
|
15
|
+
cache?: {
|
|
16
|
+
dir: string;
|
|
17
|
+
target: EnumerateTarget;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Merge the platform's discovered params over the enumerated ones. Never
|
|
22
|
+
* throws for discovery itself: a preview that can't be read costs the
|
|
23
|
+
* story-level settings the index lacks, which is today's behavior, and must
|
|
24
|
+
* not cost the run. Throws VariantConfigError from expansion.
|
|
25
|
+
*/
|
|
26
|
+
export declare function discoverParams(stories: readonly StoryManifest[], opts: Omit<PrepareOptions, 'projectVariants'>): Promise<StoryManifest[]>;
|
|
27
|
+
/** Discover, then expand: one StoryManifest per capture. */
|
|
28
|
+
export declare function prepareCaptures(stories: readonly StoryManifest[], opts: PrepareOptions): Promise<StoryManifest[]>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Enumeration-time parameter discovery, then variant expansion: the step
|
|
2
|
+
// between "what stories exist" and "what captures to take".
|
|
3
|
+
//
|
|
4
|
+
// Lives in core so every caller that captures (jux run, jux init, the review
|
|
5
|
+
// server) builds the same capture set from the same inputs.
|
|
6
|
+
import { createHash } from 'node:crypto';
|
|
7
|
+
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { expandVariants } from './variants.js';
|
|
10
|
+
/** A preview that hasn't produced its parameters in this long is broken, not
|
|
11
|
+
* slow: the example's 17 stories take ~60 ms. */
|
|
12
|
+
const DISCOVER_TIMEOUT_MS = 30_000;
|
|
13
|
+
/** Bumped when the cached shape or what discovery reads changes. */
|
|
14
|
+
const CACHE_SCHEMA = 1;
|
|
15
|
+
const CACHE_FILE = 'discovered-params.json';
|
|
16
|
+
const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
17
|
+
/**
|
|
18
|
+
* Merge the platform's discovered params over the enumerated ones. Never
|
|
19
|
+
* throws for discovery itself: a preview that can't be read costs the
|
|
20
|
+
* story-level settings the index lacks, which is today's behavior, and must
|
|
21
|
+
* not cost the run. Throws VariantConfigError from expansion.
|
|
22
|
+
*/
|
|
23
|
+
export async function discoverParams(stories, opts) {
|
|
24
|
+
const discover = opts.platform.discoverParams?.bind(opts.platform);
|
|
25
|
+
if (!discover || stories.length === 0)
|
|
26
|
+
return [...stories];
|
|
27
|
+
const key = await cacheKey(opts);
|
|
28
|
+
const cached = key ? await readCache(opts.cache.dir, key) : undefined;
|
|
29
|
+
const found = cached ?? (await discoverLive(discover, opts));
|
|
30
|
+
if (!found)
|
|
31
|
+
return [...stories];
|
|
32
|
+
if (key && !cached)
|
|
33
|
+
await writeCache(opts.cache.dir, key, found);
|
|
34
|
+
return stories.map((s) => {
|
|
35
|
+
const params = found.get(s.storyId);
|
|
36
|
+
return isRecord(params) ? { ...s, params: { ...s.params, ...params } } : s;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/** One preview load; undefined (after a warning) when it can't be read. */
|
|
40
|
+
async function discoverLive(discover, opts) {
|
|
41
|
+
let found;
|
|
42
|
+
const context = await opts.browser.newContext({ viewport: opts.viewport });
|
|
43
|
+
let timer;
|
|
44
|
+
try {
|
|
45
|
+
const page = await context.newPage();
|
|
46
|
+
const timeoutMs = opts.timeoutMs ?? DISCOVER_TIMEOUT_MS;
|
|
47
|
+
found = await Promise.race([
|
|
48
|
+
discover(page, opts.baseUrl),
|
|
49
|
+
new Promise((_, reject) => {
|
|
50
|
+
timer = setTimeout(() => reject(new Error(`no answer within ${timeoutMs} ms`)), timeoutMs);
|
|
51
|
+
}),
|
|
52
|
+
]);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
opts.onWarning?.(`jux: could not read story parameters from the ${opts.platform.name} preview (${err instanceof Error ? err.message : String(err)}); story-level settings and variants come from the index only`);
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
clearTimeout(timer);
|
|
60
|
+
await context.close().catch(() => { });
|
|
61
|
+
}
|
|
62
|
+
return found;
|
|
63
|
+
}
|
|
64
|
+
/** The platform's fingerprint of what it would discover from, salted with
|
|
65
|
+
* the platform and cache schema; undefined disables the cache. */
|
|
66
|
+
async function cacheKey(opts) {
|
|
67
|
+
if (!opts.cache || !opts.platform.discoveryKey)
|
|
68
|
+
return undefined;
|
|
69
|
+
try {
|
|
70
|
+
const key = await opts.platform.discoveryKey(opts.cache.target);
|
|
71
|
+
if (key === undefined)
|
|
72
|
+
return undefined;
|
|
73
|
+
return createHash('sha256')
|
|
74
|
+
.update(`${CACHE_SCHEMA}\0${opts.platform.name}\0${key}`)
|
|
75
|
+
.digest('hex');
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** A missing, unreadable or mismatched cache is a miss, never an error. */
|
|
82
|
+
async function readCache(dir, key) {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(await readFile(path.join(dir, CACHE_FILE), 'utf8'));
|
|
85
|
+
if (!isRecord(parsed) || parsed['key'] !== key || !isRecord(parsed['params']))
|
|
86
|
+
return undefined;
|
|
87
|
+
return new Map(Object.entries(parsed['params']));
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Written atomically; a failed write costs the next run a discovery. */
|
|
94
|
+
async function writeCache(dir, key, found) {
|
|
95
|
+
try {
|
|
96
|
+
await mkdir(dir, { recursive: true });
|
|
97
|
+
const tmp = path.join(dir, `${CACHE_FILE}.${process.pid}.tmp`);
|
|
98
|
+
await writeFile(tmp, JSON.stringify({ key, params: Object.fromEntries(found) }) + '\n');
|
|
99
|
+
await rename(tmp, path.join(dir, CACHE_FILE));
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// best effort
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Discover, then expand: one StoryManifest per capture. */
|
|
106
|
+
export async function prepareCaptures(stories, opts) {
|
|
107
|
+
return expandVariants(await discoverParams(stories, opts), opts.projectVariants);
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=discover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/capture/discover.ts"],"names":[],"mappings":"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"}
|
package/dist/capture/pool.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export interface PoolOptions {
|
|
|
23
23
|
/** Reuse warm pages and switch stories in place. Faster, but does not
|
|
24
24
|
* currently reproduce cold-load pixels exactly — see CaptureDeps. */
|
|
25
25
|
warmSwitch?: boolean;
|
|
26
|
+
/** Non-fatal notices from capture, e.g. an ignored live variant override. */
|
|
27
|
+
onWarning?: (message: string) => void;
|
|
26
28
|
}
|
|
27
29
|
export interface CaptureOutcome {
|
|
28
30
|
story: StoryManifest;
|
package/dist/capture/pool.js
CHANGED
package/dist/capture/pool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pool.js","sourceRoot":"","sources":["../../src/capture/pool.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,6EAA6E;AAC7E,6CAA6C;AAE7C,OAAO,EACL,gBAAgB,GAGjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAa,MAAM,oBAAoB,CAAC;AAGjE,OAAO,EAAE,YAAY,EAAoB,MAAM,cAAc,CAAC;AAE9D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"pool.js","sourceRoot":"","sources":["../../src/capture/pool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { StoryManifest, StoryParams, VariantSpec } from '../types.js';
|
|
2
|
+
/** Underscores are excluded so the `__` key separator can't occur in a name. */
|
|
3
|
+
export declare const VARIANT_NAME: RegExp;
|
|
4
|
+
export declare const VARIANT_SEPARATOR = "__";
|
|
5
|
+
/** A bad variant declaration. The CLI reports it and exits 2. */
|
|
6
|
+
export declare class VariantConfigError extends Error {
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
|
9
|
+
export declare const captureKey: (baseStoryId: string, variant: string) => string;
|
|
10
|
+
/** The id every platform call receives: the platform's own, never the key. */
|
|
11
|
+
export declare const platformStoryId: (story: {
|
|
12
|
+
storyId: string;
|
|
13
|
+
baseStoryId?: string;
|
|
14
|
+
}) => string;
|
|
15
|
+
/**
|
|
16
|
+
* Expand each story into its default capture plus one entry per variant,
|
|
17
|
+
* sorted by capture key in code-unit order.
|
|
18
|
+
*
|
|
19
|
+
* Throws VariantConfigError on a bad name, a malformed spec, an unknown or
|
|
20
|
+
* cyclic `extends`, or two captures that would share a key.
|
|
21
|
+
*/
|
|
22
|
+
export declare function expandVariants(stories: readonly StoryManifest[], projectVariants?: Record<string, VariantSpec | false>): StoryManifest[];
|
|
23
|
+
/**
|
|
24
|
+
* Merge live params (read from the rendered preview) for one capture.
|
|
25
|
+
*
|
|
26
|
+
* Live params override the manifest — the preview is the story's own
|
|
27
|
+
* declaration, the index a lossy projection of it — but they merge UNDER a
|
|
28
|
+
* variant's layer: a story's own live `viewport` must not undo the mobile
|
|
29
|
+
* variant's. `variants` and `colorScheme` in live params are declarations,
|
|
30
|
+
* not settings for this capture, so they are dropped.
|
|
31
|
+
*/
|
|
32
|
+
export declare function mergeLiveParams(story: Pick<StoryManifest, 'params' | 'variantParams'>, live: StoryParams | undefined): StoryParams;
|
|
33
|
+
/**
|
|
34
|
+
* Pick items by story id: a base id selects the story and every variant of
|
|
35
|
+
* it, a capture key selects exactly that capture. Ids that match nothing are
|
|
36
|
+
* returned so the caller can report them.
|
|
37
|
+
*/
|
|
38
|
+
export declare function selectByStoryId<T extends {
|
|
39
|
+
storyId: string;
|
|
40
|
+
baseStoryId?: string;
|
|
41
|
+
}>(items: readonly T[], ids: readonly string[]): {
|
|
42
|
+
selected: T[];
|
|
43
|
+
unknown: string[];
|
|
44
|
+
};
|
|
45
|
+
/** Structural equality for JSON-shaped values: key order doesn't matter,
|
|
46
|
+
* array order does. */
|
|
47
|
+
export declare function deepEqual(a: unknown, b: unknown): boolean;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// Per-story capture variants (spec 2026-10-13-variants-design.md).
|
|
2
|
+
//
|
|
3
|
+
// A variant is just another capture job: expansion happens once, after the
|
|
4
|
+
// platform enumerates, and everything downstream (pool, shuffle, warm browser,
|
|
5
|
+
// baselines, report) sees one StoryManifest per capture key. Pure on purpose:
|
|
6
|
+
// it does not care where a story's `variants` came from (index, meta.json, a
|
|
7
|
+
// url entry, or a preview extract), so a platform can change how it discovers
|
|
8
|
+
// them without touching this.
|
|
9
|
+
import { compareCodeUnits } from '../diff/structural/deltas.js';
|
|
10
|
+
/** Underscores are excluded so the `__` key separator can't occur in a name. */
|
|
11
|
+
export const VARIANT_NAME = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
12
|
+
export const VARIANT_SEPARATOR = '__';
|
|
13
|
+
/** A bad variant declaration. The CLI reports it and exits 2. */
|
|
14
|
+
export class VariantConfigError extends Error {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(`jux: ${message}`);
|
|
17
|
+
this.name = 'VariantConfigError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export const captureKey = (baseStoryId, variant) => `${baseStoryId}${VARIANT_SEPARATOR}${variant}`;
|
|
21
|
+
/** The id every platform call receives: the platform's own, never the key. */
|
|
22
|
+
export const platformStoryId = (story) => story.baseStoryId ?? story.storyId;
|
|
23
|
+
const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
24
|
+
/**
|
|
25
|
+
* Expand each story into its default capture plus one entry per variant,
|
|
26
|
+
* sorted by capture key in code-unit order.
|
|
27
|
+
*
|
|
28
|
+
* Throws VariantConfigError on a bad name, a malformed spec, an unknown or
|
|
29
|
+
* cyclic `extends`, or two captures that would share a key.
|
|
30
|
+
*/
|
|
31
|
+
export function expandVariants(stories, projectVariants) {
|
|
32
|
+
const project = checkDeclared(projectVariants, 'project config');
|
|
33
|
+
const out = [];
|
|
34
|
+
for (const story of stories) {
|
|
35
|
+
// `variants` is a declaration, not a capture setting, and `colorScheme`
|
|
36
|
+
// is variant-only: the default capture is always light.
|
|
37
|
+
const { variants: ownRaw, colorScheme: _ignored, ...baseParams } = story.params;
|
|
38
|
+
out.push({ ...story, params: baseParams });
|
|
39
|
+
// A skipped story skips every variant: there is nothing to vary.
|
|
40
|
+
if (baseParams.skip === true)
|
|
41
|
+
continue;
|
|
42
|
+
const own = checkDeclared(ownRaw, `story '${story.storyId}'`);
|
|
43
|
+
// Story wins by name; `false` drops the name entirely.
|
|
44
|
+
const merged = new Map();
|
|
45
|
+
for (const [name, spec] of Object.entries(project)) {
|
|
46
|
+
if (spec !== false)
|
|
47
|
+
merged.set(name, spec);
|
|
48
|
+
}
|
|
49
|
+
for (const [name, spec] of Object.entries(own)) {
|
|
50
|
+
if (spec === false)
|
|
51
|
+
merged.delete(name);
|
|
52
|
+
else
|
|
53
|
+
merged.set(name, spec);
|
|
54
|
+
}
|
|
55
|
+
for (const name of [...merged.keys()].sort(compareCodeUnits)) {
|
|
56
|
+
const layer = resolveChain(story.storyId, name, merged);
|
|
57
|
+
out.push({
|
|
58
|
+
...story,
|
|
59
|
+
storyId: captureKey(story.storyId, name),
|
|
60
|
+
baseStoryId: story.storyId,
|
|
61
|
+
variant: name,
|
|
62
|
+
variantParams: layer,
|
|
63
|
+
// Base params, then the extends chain, then the variant itself.
|
|
64
|
+
params: { ...baseParams, ...layer },
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
out.sort((a, b) => compareCodeUnits(a.storyId, b.storyId));
|
|
69
|
+
for (let i = 1; i < out.length; i++) {
|
|
70
|
+
const prev = out[i - 1];
|
|
71
|
+
const cur = out[i];
|
|
72
|
+
if (prev.storyId === cur.storyId) {
|
|
73
|
+
throw new VariantConfigError(`capture key '${cur.storyId}' is produced by both ${describe(prev)} and ${describe(cur)}; rename the variant or the story`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
const describe = (s) => s.variant === undefined
|
|
79
|
+
? `story '${s.storyId}'`
|
|
80
|
+
: `story '${s.baseStoryId}' variant '${s.variant}'`;
|
|
81
|
+
/** Validate names and shapes where they are declared, before any merge, so
|
|
82
|
+
* the error names the place the user has to edit. */
|
|
83
|
+
function checkDeclared(raw, where) {
|
|
84
|
+
if (raw === undefined)
|
|
85
|
+
return {};
|
|
86
|
+
if (!isRecord(raw)) {
|
|
87
|
+
throw new VariantConfigError(`${where}: 'variants' must be an object of named variants`);
|
|
88
|
+
}
|
|
89
|
+
for (const [name, spec] of Object.entries(raw)) {
|
|
90
|
+
if (!VARIANT_NAME.test(name)) {
|
|
91
|
+
throw new VariantConfigError(`${where}: variant name '${name}' must match ${VARIANT_NAME.source} (lowercase letters, digits and dashes; no underscores)`);
|
|
92
|
+
}
|
|
93
|
+
if (spec === false)
|
|
94
|
+
continue;
|
|
95
|
+
if (!isRecord(spec)) {
|
|
96
|
+
throw new VariantConfigError(`${where}: variant '${name}' must be an object of capture settings, or false to opt out`);
|
|
97
|
+
}
|
|
98
|
+
if ('skip' in spec) {
|
|
99
|
+
throw new VariantConfigError(`${where}: variant '${name}' sets 'skip'; set the variant to false instead`);
|
|
100
|
+
}
|
|
101
|
+
const scheme = spec['colorScheme'];
|
|
102
|
+
if (scheme !== undefined && scheme !== 'light' && scheme !== 'dark') {
|
|
103
|
+
throw new VariantConfigError(`${where}: variant '${name}' colorScheme must be 'light' or 'dark'`);
|
|
104
|
+
}
|
|
105
|
+
const ext = spec['extends'];
|
|
106
|
+
if (ext !== undefined && typeof ext !== 'string') {
|
|
107
|
+
throw new VariantConfigError(`${where}: variant '${name}' extends must be a variant name`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return raw;
|
|
111
|
+
}
|
|
112
|
+
/** The variant's own layer: its extends chain (farthest ancestor first),
|
|
113
|
+
* then the variant itself, with `extends` removed. */
|
|
114
|
+
function resolveChain(storyId, name, merged) {
|
|
115
|
+
const chain = [];
|
|
116
|
+
const seen = [];
|
|
117
|
+
let cur = name;
|
|
118
|
+
while (cur !== undefined) {
|
|
119
|
+
if (seen.includes(cur)) {
|
|
120
|
+
throw new VariantConfigError(`story '${storyId}' variant '${name}': extends cycle ${[...seen, cur].join(' -> ')}`);
|
|
121
|
+
}
|
|
122
|
+
seen.push(cur);
|
|
123
|
+
const spec = merged.get(cur);
|
|
124
|
+
if (!spec) {
|
|
125
|
+
// Only reachable for an ancestor: `name` itself came from `merged`.
|
|
126
|
+
throw new VariantConfigError(`story '${storyId}' variant '${seen[seen.length - 2]}' extends unknown variant '${cur}'`);
|
|
127
|
+
}
|
|
128
|
+
chain.unshift(spec);
|
|
129
|
+
cur = spec.extends;
|
|
130
|
+
}
|
|
131
|
+
const layer = {};
|
|
132
|
+
for (const spec of chain)
|
|
133
|
+
Object.assign(layer, spec);
|
|
134
|
+
delete layer.extends;
|
|
135
|
+
return layer;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Merge live params (read from the rendered preview) for one capture.
|
|
139
|
+
*
|
|
140
|
+
* Live params override the manifest — the preview is the story's own
|
|
141
|
+
* declaration, the index a lossy projection of it — but they merge UNDER a
|
|
142
|
+
* variant's layer: a story's own live `viewport` must not undo the mobile
|
|
143
|
+
* variant's. `variants` and `colorScheme` in live params are declarations,
|
|
144
|
+
* not settings for this capture, so they are dropped.
|
|
145
|
+
*/
|
|
146
|
+
export function mergeLiveParams(story, live) {
|
|
147
|
+
const params = { ...story.params };
|
|
148
|
+
if (!live)
|
|
149
|
+
return params;
|
|
150
|
+
const { variants: _v, colorScheme: _c, ...settings } = live;
|
|
151
|
+
Object.assign(params, settings, story.variantParams ?? {});
|
|
152
|
+
return params;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Pick items by story id: a base id selects the story and every variant of
|
|
156
|
+
* it, a capture key selects exactly that capture. Ids that match nothing are
|
|
157
|
+
* returned so the caller can report them.
|
|
158
|
+
*/
|
|
159
|
+
export function selectByStoryId(items, ids) {
|
|
160
|
+
const wanted = new Set(ids);
|
|
161
|
+
const hit = new Set();
|
|
162
|
+
const selected = items.filter((s) => {
|
|
163
|
+
if (wanted.has(s.storyId)) {
|
|
164
|
+
hit.add(s.storyId);
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
if (s.baseStoryId !== undefined && wanted.has(s.baseStoryId)) {
|
|
168
|
+
hit.add(s.baseStoryId);
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
return false;
|
|
172
|
+
});
|
|
173
|
+
return { selected, unknown: ids.filter((id) => !hit.has(id)) };
|
|
174
|
+
}
|
|
175
|
+
/** Structural equality for JSON-shaped values: key order doesn't matter,
|
|
176
|
+
* array order does. */
|
|
177
|
+
export function deepEqual(a, b) {
|
|
178
|
+
if (a === b)
|
|
179
|
+
return true;
|
|
180
|
+
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null)
|
|
181
|
+
return false;
|
|
182
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
183
|
+
return false;
|
|
184
|
+
if (Array.isArray(a) && Array.isArray(b))
|
|
185
|
+
return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
|
|
186
|
+
const ra = a;
|
|
187
|
+
const rb = b;
|
|
188
|
+
const ka = Object.keys(ra);
|
|
189
|
+
return (ka.length === Object.keys(rb).length &&
|
|
190
|
+
ka.every((k) => Object.hasOwn(rb, k) && deepEqual(ra[k], rb[k])));
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=variants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variants.js","sourceRoot":"","sources":["../../src/capture/variants.ts"],"names":[],"mappings":"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"}
|