@juangadm/pre-post 1.0.0 → 1.2.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/README.md +99 -20
- package/dist/baseline.d.ts +208 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +413 -0
- package/dist/baseline.js.map +1 -0
- package/dist/bin/cli.js +87 -42
- package/dist/bin/cli.js.map +1 -1
- package/dist/browser.d.ts +31 -3
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +291 -97
- package/dist/browser.js.map +1 -1
- package/dist/commands/compare.d.ts.map +1 -1
- package/dist/commands/compare.js +21 -3
- package/dist/commands/compare.js.map +1 -1
- package/dist/commands/detect.d.ts +5 -0
- package/dist/commands/detect.d.ts.map +1 -1
- package/dist/commands/detect.js +4 -1
- package/dist/commands/detect.js.map +1 -1
- package/dist/commands/doctor.d.ts +32 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +90 -9
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/login.js +1 -1
- package/dist/commands/pr.d.ts +4 -0
- package/dist/commands/pr.d.ts.map +1 -1
- package/dist/commands/pr.js +157 -51
- package/dist/commands/pr.js.map +1 -1
- package/dist/commands/prune.js +1 -1
- package/dist/commands/prune.js.map +1 -1
- package/dist/comparison.d.ts +104 -0
- package/dist/comparison.d.ts.map +1 -0
- package/dist/comparison.js +241 -0
- package/dist/comparison.js.map +1 -0
- package/dist/config.d.ts +12 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +25 -3
- package/dist/config.js.map +1 -1
- package/dist/deployments.d.ts +71 -0
- package/dist/deployments.d.ts.map +1 -0
- package/dist/deployments.js +204 -0
- package/dist/deployments.js.map +1 -0
- package/dist/diff-pool.d.ts.map +1 -1
- package/dist/diff-pool.js +5 -2
- package/dist/diff-pool.js.map +1 -1
- package/dist/diff-worker.d.ts +3 -0
- package/dist/diff-worker.d.ts.map +1 -1
- package/dist/diff-worker.js +1 -0
- package/dist/diff-worker.js.map +1 -1
- package/dist/diff.d.ts +10 -0
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +117 -7
- package/dist/diff.js.map +1 -1
- package/dist/doctor.d.ts +42 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +73 -7
- package/dist/doctor.js.map +1 -1
- package/dist/git.d.ts +41 -3
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +103 -7
- package/dist/git.js.map +1 -1
- package/dist/github.d.ts +92 -2
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +191 -20
- package/dist/github.js.map +1 -1
- package/dist/landing.d.ts +48 -0
- package/dist/landing.d.ts.map +1 -0
- package/dist/landing.js +93 -0
- package/dist/landing.js.map +1 -0
- package/dist/pkg.d.ts +9 -0
- package/dist/pkg.d.ts.map +1 -1
- package/dist/pkg.js +14 -0
- package/dist/pkg.js.map +1 -1
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +59 -28
- package/dist/report.js.map +1 -1
- package/dist/routes.d.ts +27 -0
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +98 -23
- package/dist/routes.js.map +1 -1
- package/dist/run.d.ts +88 -8
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +224 -28
- package/dist/run.js.map +1 -1
- package/dist/sameness.d.ts +91 -0
- package/dist/sameness.d.ts.map +1 -0
- package/dist/sameness.js +157 -0
- package/dist/sameness.js.map +1 -0
- package/dist/sessions.d.ts.map +1 -1
- package/dist/sessions.js +7 -1
- package/dist/sessions.js.map +1 -1
- package/dist/shift.d.ts +67 -0
- package/dist/shift.d.ts.map +1 -0
- package/dist/shift.js +184 -0
- package/dist/shift.js.map +1 -0
- package/dist/types.d.ts +83 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -1
- package/dist/url.d.ts +12 -0
- package/dist/url.d.ts.map +1 -0
- package/dist/url.js +30 -0
- package/dist/url.js.map +1 -0
- package/package.json +1 -1
- package/skill/SKILL.md +40 -7
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Choosing what to compare against what.
|
|
3
|
+
*
|
|
4
|
+
* Pre and Post used to be resolved independently, which could pair a production
|
|
5
|
+
* deployment against a local dev server. Different fonts, different data,
|
|
6
|
+
* different analytics — a diff full of changes the PR did not make. Nobody
|
|
7
|
+
* reviews the raw images (the skill forbids it), so a noisy diff is believed.
|
|
8
|
+
*
|
|
9
|
+
* So a *pair* is resolved, not two URLs. Each strategy either produces both
|
|
10
|
+
* sides from the same kind of environment or yields to the next one. Mixing
|
|
11
|
+
* happens only when the caller asked for it by name, and is reported when it
|
|
12
|
+
* does.
|
|
13
|
+
*/
|
|
14
|
+
import { deploymentUrlForSha, findPreviewForCommit, latestProductionDeployment } from './deployments.js';
|
|
15
|
+
import { serveBaseCommit, serveWorkingTree } from './baseline.js';
|
|
16
|
+
import { NeedsHumanError } from './doctor.js';
|
|
17
|
+
import { isLocalUrl, normalizeUrl } from './url.js';
|
|
18
|
+
import { mergeBase } from './git.js';
|
|
19
|
+
const noop = async () => undefined;
|
|
20
|
+
const side = (url, detail, probe) => ({ url: normalizeUrl(url), detail, probe });
|
|
21
|
+
/**
|
|
22
|
+
* The one place a Comparison is built, so `mixed` is derived rather than
|
|
23
|
+
* recomputed — a construction site that forgot it would report a production
|
|
24
|
+
* page against a dev server as a clean comparison.
|
|
25
|
+
*/
|
|
26
|
+
const pair = (strategy, before, after, stop = noop) => ({ strategy, before, after, mixed: isLocalUrl(before.url) !== isLocalUrl(after.url), stop });
|
|
27
|
+
/** Reachable and not sitting behind a login wall. */
|
|
28
|
+
function isUsable(result) {
|
|
29
|
+
return result.status !== null && result.status !== 401 && result.status !== 403;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The preview deployment for the commit being reviewed, if there is a fresh one.
|
|
33
|
+
*
|
|
34
|
+
* Keyed on the commit rather than on the PR: a host builds a branch as soon as
|
|
35
|
+
* it is pushed, so the preview usually exists before anyone opens the PR. The
|
|
36
|
+
* PR number is passed when there is one only because the bot-comment fallback
|
|
37
|
+
* needs somewhere to look.
|
|
38
|
+
*/
|
|
39
|
+
async function previewForHead(ctx) {
|
|
40
|
+
const { gh, ownerRepo, pr } = ctx;
|
|
41
|
+
const sha = pr?.head.sha ?? ctx.headSha;
|
|
42
|
+
if (!gh || !sha)
|
|
43
|
+
return null;
|
|
44
|
+
const found = await findPreviewForCommit(gh, ownerRepo, { sha, prNumber: pr?.number, appPrefix: ctx.appPrefix });
|
|
45
|
+
return found ? side(found.url, `${found.environment} deployment for ${sha.slice(0, 7)}`) : null;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The deployed baseline, most honest answer first.
|
|
49
|
+
*
|
|
50
|
+
* Pinning Pre to the base commit is the right question and it is tried first.
|
|
51
|
+
* The second step exists because production is a branch, not a commit: a
|
|
52
|
+
* repository that does not deploy every push to its default branch has no
|
|
53
|
+
* deployment at the fork point, and requiring one left the deployed comparison
|
|
54
|
+
* with no Pre — a preview would be found, no baseline would be, and the run
|
|
55
|
+
* fell back to needing a dev server nobody in the target audience has.
|
|
56
|
+
*
|
|
57
|
+
* Widening stops at deployments the host actually recorded. Every step names
|
|
58
|
+
* the commit it answered with, so Pre is never mistaken for the base.
|
|
59
|
+
*/
|
|
60
|
+
async function deployedBaseline(ctx) {
|
|
61
|
+
if (ctx.before)
|
|
62
|
+
return side(ctx.before, 'passed with --before');
|
|
63
|
+
if (ctx.config.before)
|
|
64
|
+
return side(ctx.config.before, 'from .pre-post.json');
|
|
65
|
+
if (!ctx.gh)
|
|
66
|
+
return null;
|
|
67
|
+
// What this branch forked from, if that exact commit was deployed. Usually it
|
|
68
|
+
// was — a host that deploys every push to the default branch has one — but a
|
|
69
|
+
// repository that deploys on a tag, promotes by hand, or whose base commit is
|
|
70
|
+
// older than its retained deployments will find nothing here.
|
|
71
|
+
const baseSha = ctx.baseSha ?? ctx.pr?.base.sha;
|
|
72
|
+
if (baseSha) {
|
|
73
|
+
const pinned = await deploymentUrlForSha(ctx.gh, ctx.ownerRepo, baseSha, { production: true });
|
|
74
|
+
if (pinned)
|
|
75
|
+
return side(pinned.url, `${pinned.environment} deployment for ${baseSha.slice(0, 7)}`);
|
|
76
|
+
}
|
|
77
|
+
// Widening is for a fork point this tool worked out on its own. A base the
|
|
78
|
+
// caller named is a different thing: production is some other commit, so
|
|
79
|
+
// answering with it would compare against a commit they did not ask for while
|
|
80
|
+
// route detection still describes the ref they did -- the same Pre/route-list
|
|
81
|
+
// disagreement that made `--base` worth fixing in the first place. Yield
|
|
82
|
+
// instead, and let the local strategy build the named commit from source.
|
|
83
|
+
if (ctx.baseExplicit) {
|
|
84
|
+
ctx.log(`No deployment for the base you asked for (${baseSha?.slice(0, 7)}); building it locally rather than comparing against a different commit.`);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
// Otherwise what is on production now: the same site, possibly a few commits
|
|
88
|
+
// ahead, so name the commit it was built from instead of implying the base.
|
|
89
|
+
const latest = await latestProductionDeployment(ctx.gh, ctx.ownerRepo);
|
|
90
|
+
if (latest) {
|
|
91
|
+
return side(latest.url, `${latest.environment} deployment${latest.sha ? ` for ${latest.sha.slice(0, 7)}` : ''}`);
|
|
92
|
+
}
|
|
93
|
+
// Anything past here would be a guess at the site's address, and a baseline
|
|
94
|
+
// that is quietly the wrong site reads as 100% changed on every route — a
|
|
95
|
+
// result that looks exactly like a real one. Better to say so and stop.
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/** Both sides named by the caller: their call, mixed or not. */
|
|
99
|
+
function explicitPair(ctx) {
|
|
100
|
+
if (!ctx.before || !ctx.after)
|
|
101
|
+
return null;
|
|
102
|
+
return pair('explicit', side(ctx.before, 'passed with --before'), side(ctx.after, 'passed with --after'));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Both sides deployed — same build pipeline, same CDN, same data.
|
|
106
|
+
*
|
|
107
|
+
* The preview is looked up first and alone: it is the cheap question, and when
|
|
108
|
+
* there is no preview there is nothing for a baseline to pair with, so no
|
|
109
|
+
* baseline requests are spent on a run that will compare locally anyway.
|
|
110
|
+
*/
|
|
111
|
+
async function deployedPair(ctx) {
|
|
112
|
+
const preview = await previewForHead(ctx);
|
|
113
|
+
if (!preview)
|
|
114
|
+
return { comparison: null, preview: null };
|
|
115
|
+
const [previewProbe, baseline] = await Promise.all([ctx.probe(preview.url), deployedBaseline(ctx)]);
|
|
116
|
+
if (!isUsable(previewProbe)) {
|
|
117
|
+
ctx.log(`Preview deployment ${preview.url} is not reachable (it may be behind Deployment Protection); comparing locally instead.`);
|
|
118
|
+
return { comparison: null, preview: null };
|
|
119
|
+
}
|
|
120
|
+
const baselineProbe = baseline && !isLocalUrl(baseline.url) ? await ctx.probe(baseline.url) : null;
|
|
121
|
+
if (!baseline || !baselineProbe || !isUsable(baselineProbe)) {
|
|
122
|
+
ctx.log('Preview deployment found but no reachable deployed baseline; comparing locally instead.');
|
|
123
|
+
return { comparison: null, preview, rejectedBaseline: baseline ? { side: baseline, probe: baselineProbe } : undefined };
|
|
124
|
+
}
|
|
125
|
+
return { comparison: pair('deployed', { ...baseline, probe: baselineProbe }, { ...preview, probe: previewProbe }), preview };
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Both sides local — same browser, same machine, no network.
|
|
129
|
+
*
|
|
130
|
+
* This is the strategy that always has something to offer, so it owns the dev
|
|
131
|
+
* servers it starts: every exit below either hands their teardown to the
|
|
132
|
+
* Comparison or runs it before throwing.
|
|
133
|
+
*/
|
|
134
|
+
async function localPair(ctx, deployed) {
|
|
135
|
+
const running = await ctx.devServer;
|
|
136
|
+
let after = ctx.after
|
|
137
|
+
? side(ctx.after, 'passed with --after')
|
|
138
|
+
: running ? side(running, 'local dev server') : null;
|
|
139
|
+
// Nothing deployed and nothing running is not a reason to stop: this is the
|
|
140
|
+
// command you hit on the way out of a PR, so it starts the dev server itself
|
|
141
|
+
// rather than handing back a chore.
|
|
142
|
+
let postServer = null;
|
|
143
|
+
if (!after && ctx.allowLocalBaseline !== false) {
|
|
144
|
+
postServer = await (ctx.servePost ?? serveWorkingTree)({ repoRoot: ctx.repoRoot, appPrefix: ctx.appPrefix, log: ctx.log });
|
|
145
|
+
if (postServer)
|
|
146
|
+
after = side(postServer.url, 'working tree, served locally');
|
|
147
|
+
}
|
|
148
|
+
if (!after)
|
|
149
|
+
throw deployed.preview ? new NoDeployedBaselineError(deployed.preview.url, deployed.rejectedBaseline) : new NoPostError();
|
|
150
|
+
const stopPost = postServer ? postServer.stop : noop;
|
|
151
|
+
// A baseline the caller named wins even if it is remote; they asked for it.
|
|
152
|
+
if (ctx.before)
|
|
153
|
+
return pair('explicit', side(ctx.before, 'passed with --before'), after, stopPost);
|
|
154
|
+
// git knows what this branch forked from, so a baseline does not depend on a
|
|
155
|
+
// PR existing yet — this often runs before one is opened.
|
|
156
|
+
const baseSha = ctx.baseSha ?? ctx.pr?.base.sha ?? mergeBase(ctx.repoRoot) ?? undefined;
|
|
157
|
+
// Serving the base can now fail loudly rather than yielding — an install
|
|
158
|
+
// that died is not a "try the next option", it is the answer. The Post
|
|
159
|
+
// server is already up by then, so it has to come down before the throw
|
|
160
|
+
// escapes; nothing above this has a handle on it yet.
|
|
161
|
+
const baseline = ctx.allowLocalBaseline === false || !baseSha
|
|
162
|
+
? null
|
|
163
|
+
: await (ctx.serveBaseline ?? serveBaseCommit)({ repoRoot: ctx.repoRoot, sha: baseSha, appPrefix: ctx.appPrefix, log: ctx.log })
|
|
164
|
+
.catch(async (err) => { await stopPost(); throw err; });
|
|
165
|
+
if (baseline) {
|
|
166
|
+
const before = side(baseline.url, `base commit ${baseSha.slice(0, 7)}, served locally`);
|
|
167
|
+
return pair('local', before, after, async () => { await baseline.stop(); await stopPost(); });
|
|
168
|
+
}
|
|
169
|
+
// Nothing local could be built; a configured URL beats no comparison at all.
|
|
170
|
+
if (ctx.config.before)
|
|
171
|
+
return pair('explicit', side(ctx.config.before, 'from .pre-post.json'), after, stopPost);
|
|
172
|
+
await stopPost();
|
|
173
|
+
throw new NoBaselineError();
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Resolve both sides together, cheapest strategy first.
|
|
177
|
+
*
|
|
178
|
+
* `explicit` when the caller named both; then `deployed`, which needs a preview
|
|
179
|
+
* for this commit and a deployed baseline; then `local`, which needs nothing but
|
|
180
|
+
* the repository itself.
|
|
181
|
+
*/
|
|
182
|
+
export async function resolveComparison(ctx) {
|
|
183
|
+
const explicit = explicitPair(ctx);
|
|
184
|
+
if (explicit)
|
|
185
|
+
return explicit;
|
|
186
|
+
const deployed = await deployedPair(ctx);
|
|
187
|
+
return deployed.comparison ?? localPair(ctx, deployed);
|
|
188
|
+
}
|
|
189
|
+
export class NoPostError extends NeedsHumanError {
|
|
190
|
+
constructor() {
|
|
191
|
+
super('No preview deployment for this commit and no dev server on the usual ports (3000, 5173, ...). Start the dev server (e.g. npm run dev), then re-run — or pass --after http://localhost:PORT.');
|
|
192
|
+
this.name = 'NoPostError';
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* A preview exists but nothing could stand beside it.
|
|
197
|
+
*
|
|
198
|
+
* Worth its own error because the generic one says "no preview deployment for
|
|
199
|
+
* this commit", which in this case is the opposite of what happened — and the
|
|
200
|
+
* person hitting it is the one least able to act on advice about dev servers.
|
|
201
|
+
*
|
|
202
|
+
* The instruction depends on *why* there was no baseline. Telling someone to
|
|
203
|
+
* pass `--before` when the baseline was found and turned them away at a login
|
|
204
|
+
* wall sends them back to the same URL for the same failure; the fix there is
|
|
205
|
+
* access, not an argument.
|
|
206
|
+
*/
|
|
207
|
+
export class NoDeployedBaselineError extends NeedsHumanError {
|
|
208
|
+
constructor(previewUrl, rejected) {
|
|
209
|
+
super(`Found the preview deployment for this commit (${previewUrl}) but nothing to compare it against: ${reasonFor(rejected)} No dev server is running either.`);
|
|
210
|
+
this.name = 'NoDeployedBaselineError';
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const PIN_HINT = 'Re-run with --before https://your-production-url (it is saved to .pre-post.json for next time).';
|
|
214
|
+
function reasonFor(rejected) {
|
|
215
|
+
if (!rejected)
|
|
216
|
+
return `no production deployment is recorded for this repository. ${PIN_HINT}`;
|
|
217
|
+
const { side: baseline, probe } = rejected;
|
|
218
|
+
if (probe && (probe.status === 401 || probe.status === 403)) {
|
|
219
|
+
return `the baseline (${baseline.url} — ${baseline.detail}) returned ${probe.status}, so it is behind access control.${probe.vercel ? ' Set VERCEL_AUTOMATION_BYPASS_SECRET, or' : ''} run \`pre-post login ${baseline.url}\`, then re-run.`;
|
|
220
|
+
}
|
|
221
|
+
return `the baseline (${baseline.url} — ${baseline.detail}) could not be reached. Check it is up, or ${PIN_HINT[0].toLowerCase()}${PIN_HINT.slice(1)}`;
|
|
222
|
+
}
|
|
223
|
+
export class NoBaselineError extends NeedsHumanError {
|
|
224
|
+
constructor() {
|
|
225
|
+
super('No baseline to compare against: no deployed baseline, and the base commit could not be served locally. Re-run with --before https://your-production-url (it is saved to .pre-post.json for next time).');
|
|
226
|
+
this.name = 'NoBaselineError';
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/** The plan, for the log — an unattended tool has to say what it chose. */
|
|
230
|
+
export function describeComparison(c) {
|
|
231
|
+
const lines = [
|
|
232
|
+
`Comparing (${c.strategy}):`,
|
|
233
|
+
` Pre ${c.before.url} — ${c.before.detail}`,
|
|
234
|
+
` Post ${c.after.url} — ${c.after.detail}`,
|
|
235
|
+
];
|
|
236
|
+
if (c.mixed) {
|
|
237
|
+
lines.push(' Note: the two sides run in different environments, so some differences may not come from this branch.');
|
|
238
|
+
}
|
|
239
|
+
return lines;
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=comparison.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comparison.js","sourceRoot":"","sources":["../src/comparison.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AACzG,OAAO,EAAiB,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,EAAE,eAAe,EAAe,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAEpD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AA0DrC,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE,CAAC,SAAS,CAAC;AAElD,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,MAAc,EAAE,KAAmB,EAAQ,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AAErH;;;;GAIG;AACH,MAAM,IAAI,GAAG,CAAC,QAAsB,EAAE,MAAY,EAAE,KAAW,EAAE,OAA4B,IAAI,EAAc,EAAE,CAC/G,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AAE/F,qDAAqD;AACrD,SAAS,QAAQ,CAAC,MAAmB;IACnC,OAAO,MAAM,CAAC,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC;AAClF,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,cAAc,CAAC,GAAmB;IAC/C,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IAClC,MAAM,GAAG,GAAG,EAAE,EAAE,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC;IACxC,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;IACjH,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,WAAW,mBAAmB,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAClG,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,KAAK,UAAU,gBAAgB,CAAC,GAAmB;IACjD,IAAI,GAAG,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAChE,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IAC7E,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAEzB,8EAA8E;IAC9E,6EAA6E;IAC7E,8EAA8E;IAC9E,8DAA8D;IAC9D,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC;IAChD,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/F,IAAI,MAAM;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,WAAW,mBAAmB,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACrG,CAAC;IAED,2EAA2E;IAC3E,yEAAyE;IACzE,8EAA8E;IAC9E,8EAA8E;IAC9E,yEAAyE;IACzE,0EAA0E;IAC1E,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC;QACrB,GAAG,CAAC,GAAG,CAAC,6CAA6C,OAAO,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,0EAA0E,CAAC,CAAC;QACrJ,OAAO,IAAI,CAAC;IACd,CAAC;IAED,6EAA6E;IAC7E,4EAA4E;IAC5E,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;IACvE,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,WAAW,cAAc,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACnH,CAAC;IAED,4EAA4E;IAC5E,0EAA0E;IAC1E,wEAAwE;IACxE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gEAAgE;AAChE,SAAS,YAAY,CAAC,GAAmB;IACvC,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAC3C,OAAO,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC;AAC5G,CAAC;AAeD;;;;;;GAMG;AACH,KAAK,UAAU,YAAY,CAAC,GAAmB;IAC7C,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC;IAC1C,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAEzD,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACpG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QAC5B,GAAG,CAAC,GAAG,CAAC,sBAAsB,OAAO,CAAC,GAAG,wFAAwF,CAAC,CAAC;QACnI,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7C,CAAC;IAED,MAAM,aAAa,GAAG,QAAQ,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACnG,IAAI,CAAC,QAAQ,IAAI,CAAC,aAAa,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QAC5D,GAAG,CAAC,GAAG,CAAC,yFAAyF,CAAC,CAAC;QACnG,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;IAC1H,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,GAAG,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC;AAC/H,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,SAAS,CAAC,GAAmB,EAAE,QAAyB;IACrE,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC;IACpC,IAAI,KAAK,GAAgB,GAAG,CAAC,KAAK;QAChC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,qBAAqB,CAAC;QACxC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEvD,4EAA4E;IAC5E,6EAA6E;IAC7E,oCAAoC;IACpC,IAAI,UAAU,GAAyB,IAAI,CAAC;IAC5C,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,kBAAkB,KAAK,KAAK,EAAE,CAAC;QAC/C,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,IAAI,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QAC3H,IAAI,UAAU;YAAE,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,8BAA8B,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,CAAC,KAAK;QAAE,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,uBAAuB,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC;IACtI,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAErD,4EAA4E;IAC5E,IAAI,GAAG,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAEnG,6EAA6E;IAC7E,0DAA0D;IAC1D,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC;IACxF,yEAAyE;IACzE,uEAAuE;IACvE,wEAAwE;IACxE,sDAAsD;IACtD,MAAM,QAAQ,GAAG,GAAG,CAAC,kBAAkB,KAAK,KAAK,IAAI,CAAC,OAAO;QAC3D,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,IAAI,eAAe,CAAC,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;aAC3H,KAAK,CAAC,KAAK,EAAC,GAAG,EAAC,EAAE,GAAG,MAAM,QAAQ,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,eAAe,OAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,kBAAkB,CAAC,CAAC;QACzF,OAAO,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAChG,CAAC;IAED,6EAA6E;IAC7E,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAChH,MAAM,QAAQ,EAAE,CAAC;IACjB,MAAM,IAAI,eAAe,EAAE,CAAC;AAC9B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAmB;IACzD,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC;IACzC,OAAO,QAAQ,CAAC,UAAU,IAAI,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,OAAO,WAAY,SAAQ,eAAe;IAC9C;QACE,KAAK,CAAC,6LAA6L,CAAC,CAAC;QACrM,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,uBAAwB,SAAQ,eAAe;IAC1D,YAAY,UAAkB,EAAE,QAAoD;QAClF,KAAK,CAAC,iDAAiD,UAAU,wCAAwC,SAAS,CAAC,QAAQ,CAAC,mCAAmC,CAAC,CAAC;QACjK,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AAED,MAAM,QAAQ,GAAG,iGAAiG,CAAC;AAEnH,SAAS,SAAS,CAAC,QAAoD;IACrE,IAAI,CAAC,QAAQ;QAAE,OAAO,6DAA6D,QAAQ,EAAE,CAAC;IAC9F,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC;IAC3C,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,CAAC;QAC5D,OAAO,iBAAiB,QAAQ,CAAC,GAAG,MAAM,QAAQ,CAAC,MAAM,cAAc,KAAK,CAAC,MAAM,oCAAoC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,0CAA0C,CAAC,CAAC,CAAC,EAAE,yBAAyB,QAAQ,CAAC,GAAG,kBAAkB,CAAC;IAC/O,CAAC;IACD,OAAO,iBAAiB,QAAQ,CAAC,GAAG,MAAM,QAAQ,CAAC,MAAM,8CAA8C,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AACzJ,CAAC;AAED,MAAM,OAAO,eAAgB,SAAQ,eAAe;IAClD;QACE,KAAK,CAAC,wMAAwM,CAAC,CAAC;QAChN,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED,2EAA2E;AAC3E,MAAM,UAAU,kBAAkB,CAAC,CAAa;IAC9C,MAAM,KAAK,GAAG;QACZ,cAAc,CAAC,CAAC,QAAQ,IAAI;QAC5B,WAAW,CAAC,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE;QAC/C,WAAW,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE;KAC9C,CAAC;IACF,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QACZ,KAAK,CAAC,IAAI,CAAC,yGAAyG,CAAC,CAAC;IACxH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/config.d.ts
CHANGED
|
@@ -7,7 +7,17 @@ export declare const CONFIG_FILENAME = ".pre-post.json";
|
|
|
7
7
|
export declare const CONFIG_DEFAULTS: {
|
|
8
8
|
viewports: string[];
|
|
9
9
|
threshold: number;
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Smallest painted change that counts, in CSS pixels².
|
|
12
|
+
*
|
|
13
|
+
* Roughly a third of a 16px icon. Against the fixture ladder every real
|
|
14
|
+
* change measures 242 CSS px² or more while every no-op measures exactly 0,
|
|
15
|
+
* so this sits in the middle of a wide empty band — low enough that missing
|
|
16
|
+
* a real change (the worse error by far: the tool would report "no visual
|
|
17
|
+
* changes" on a PR that changed something) stays implausible, high enough to
|
|
18
|
+
* absorb antialiasing differences between two machines.
|
|
19
|
+
*/
|
|
20
|
+
minChangedArea: number;
|
|
11
21
|
maxRoutes: number;
|
|
12
22
|
fullPage: boolean;
|
|
13
23
|
maxHeight: number;
|
|
@@ -18,7 +28,7 @@ export declare const CONFIG_DEFAULTS: {
|
|
|
18
28
|
export interface Settings {
|
|
19
29
|
viewports: string[];
|
|
20
30
|
threshold: number;
|
|
21
|
-
|
|
31
|
+
minChangedArea: number;
|
|
22
32
|
maxRoutes: number;
|
|
23
33
|
fullPage: boolean;
|
|
24
34
|
maxHeight: number;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAEhD,eAAO,MAAM,eAAe;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAEhD,eAAO,MAAM,eAAe;eACA,MAAM,EAAE;;IAElC;;;;;;;;;OASG;;;;;;;;CAQJ,CAAC;AAEF,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,MAAM,GAAE,aAAkB,EAAE,SAAS,GAAE,OAAO,CAAC,QAAQ,CAAM,GAAG,QAAQ,CA0BvG;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAU1D;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAExE;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAI3F"}
|
package/dist/config.js
CHANGED
|
@@ -6,9 +6,19 @@ import fs from 'fs';
|
|
|
6
6
|
import path from 'path';
|
|
7
7
|
export const CONFIG_FILENAME = '.pre-post.json';
|
|
8
8
|
export const CONFIG_DEFAULTS = {
|
|
9
|
-
viewports: ['desktop'
|
|
9
|
+
viewports: ['desktop'],
|
|
10
10
|
threshold: 0.001,
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Smallest painted change that counts, in CSS pixels².
|
|
13
|
+
*
|
|
14
|
+
* Roughly a third of a 16px icon. Against the fixture ladder every real
|
|
15
|
+
* change measures 242 CSS px² or more while every no-op measures exactly 0,
|
|
16
|
+
* so this sits in the middle of a wide empty band — low enough that missing
|
|
17
|
+
* a real change (the worse error by far: the tool would report "no visual
|
|
18
|
+
* changes" on a PR that changed something) stays implausible, high enough to
|
|
19
|
+
* absorb antialiasing differences between two machines.
|
|
20
|
+
*/
|
|
21
|
+
minChangedArea: 100,
|
|
12
22
|
maxRoutes: 6,
|
|
13
23
|
fullPage: true,
|
|
14
24
|
maxHeight: 2400,
|
|
@@ -19,10 +29,22 @@ export const CONFIG_DEFAULTS = {
|
|
|
19
29
|
/** Defaults < .pre-post.json < explicit overrides (undefined overrides are ignored). */
|
|
20
30
|
export function resolveSettings(config = {}, overrides = {}) {
|
|
21
31
|
const pick = (key) => (overrides[key] ?? config[key] ?? CONFIG_DEFAULTS[key]);
|
|
32
|
+
// minChangedPixels was device pixels, and library callers passed it as an
|
|
33
|
+
// override as well as setting it in the config file, so both levels have to
|
|
34
|
+
// be honoured rather than silently falling back to the new default. The new
|
|
35
|
+
// key wins over the legacy one at the same level; overrides win over config.
|
|
36
|
+
if (overrides.minChangedArea === undefined) {
|
|
37
|
+
const legacy = overrides.minChangedPixels
|
|
38
|
+
?? (config.minChangedArea === undefined ? config.minChangedPixels : undefined);
|
|
39
|
+
if (legacy !== undefined) {
|
|
40
|
+
const scale = pick('scale');
|
|
41
|
+
config = { ...config, minChangedArea: legacy / (scale * scale) };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
22
44
|
return {
|
|
23
45
|
viewports: pick('viewports'),
|
|
24
46
|
threshold: pick('threshold'),
|
|
25
|
-
|
|
47
|
+
minChangedArea: pick('minChangedArea'),
|
|
26
48
|
maxRoutes: pick('maxRoutes'),
|
|
27
49
|
fullPage: pick('fullPage'),
|
|
28
50
|
maxHeight: pick('maxHeight'),
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,CAAC,MAAM,eAAe,GAAG,gBAAgB,CAAC;AAEhD,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,SAAS,EAAE,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment URLs from the GitHub Deployments API.
|
|
3
|
+
*
|
|
4
|
+
* Vercel, Cloudflare Pages, Netlify and Render all report their deploys to
|
|
5
|
+
* GitHub against a commit, so one lookup covers every provider: no per-host
|
|
6
|
+
* API tokens, and nothing to configure per repo.
|
|
7
|
+
*/
|
|
8
|
+
import { GitHub } from './github.js';
|
|
9
|
+
export interface DeploymentUrl {
|
|
10
|
+
url: string;
|
|
11
|
+
environment: string;
|
|
12
|
+
/** The commit the deployment was built from, when the API reported one. */
|
|
13
|
+
sha?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The newest successful deployment URL for a commit.
|
|
17
|
+
*
|
|
18
|
+
* `production: true` accepts only production environments (the baseline for
|
|
19
|
+
* "Pre"); `false` accepts only the others, which is where every provider puts
|
|
20
|
+
* per-PR previews. Neither falls back to the other kind — a preview standing in
|
|
21
|
+
* for production would silently compare against the wrong thing.
|
|
22
|
+
*
|
|
23
|
+
* Returns null whenever the answer is not unambiguous, so callers fall through
|
|
24
|
+
* to their next option. Never throws: a repo with no deployments, or a token
|
|
25
|
+
* without `deployments: read`, is a normal outcome, not a failure.
|
|
26
|
+
*/
|
|
27
|
+
export declare function deploymentUrlForSha(gh: GitHub, ownerRepo: string, sha: string, opts: {
|
|
28
|
+
production: boolean;
|
|
29
|
+
}): Promise<DeploymentUrl | null>;
|
|
30
|
+
/**
|
|
31
|
+
* What is deployed to production right now, whatever commit it was built from.
|
|
32
|
+
*
|
|
33
|
+
* `deploymentUrlForSha` pins Pre to the commit the branch forked from, which is
|
|
34
|
+
* the most honest baseline there is — but it only exists if production happened
|
|
35
|
+
* to be deployed at exactly that commit. Production is a moving branch, so on
|
|
36
|
+
* most repositories it was not, and pinning alone leaves the deployed
|
|
37
|
+
* comparison with no Pre at all.
|
|
38
|
+
*
|
|
39
|
+
* This asks the same question without the pin. It reports the commit it found,
|
|
40
|
+
* so the caller can name what Pre actually is rather than implying the base.
|
|
41
|
+
*/
|
|
42
|
+
export declare function latestProductionDeployment(gh: GitHub, ownerRepo: string): Promise<DeploymentUrl | null>;
|
|
43
|
+
/**
|
|
44
|
+
* The preview for a commit, from whichever mechanism the provider uses.
|
|
45
|
+
*
|
|
46
|
+
* The freshness rule lives here rather than in the caller: a bot comment is
|
|
47
|
+
* edited in place and carries no SHA, so mid-deploy it still advertises the
|
|
48
|
+
* previous commit's preview. Screenshotting that and labelling it as this
|
|
49
|
+
* branch is the worst failure this tool has, because it looks exactly like a
|
|
50
|
+
* correct result. Every caller gets that guarantee by construction.
|
|
51
|
+
*/
|
|
52
|
+
export declare function findPreviewForCommit(gh: GitHub, ownerRepo: string, opts: {
|
|
53
|
+
sha: string;
|
|
54
|
+
prNumber?: number;
|
|
55
|
+
appPrefix?: string;
|
|
56
|
+
}): Promise<DeploymentUrl | null>;
|
|
57
|
+
/**
|
|
58
|
+
* The preview URL a deployment bot posted on the PR.
|
|
59
|
+
*
|
|
60
|
+
* Not every provider records a GitHub Deployment — Vercel's GitHub app reports
|
|
61
|
+
* a commit status and a comment, and the commit status only links to its own
|
|
62
|
+
* dashboard. The comment is where the deployed URL actually appears, so this is
|
|
63
|
+
* the fallback when `deploymentUrlForSha` finds nothing.
|
|
64
|
+
*
|
|
65
|
+
* `appPrefix` disambiguates a monorepo comment listing several projects, by
|
|
66
|
+
* matching the project whose root directory is the app being captured.
|
|
67
|
+
*/
|
|
68
|
+
export declare function previewUrlFromComments(gh: GitHub, ownerRepo: string, prNumber: number, opts?: {
|
|
69
|
+
appPrefix?: string;
|
|
70
|
+
}): Promise<DeploymentUrl | null>;
|
|
71
|
+
//# sourceMappingURL=deployments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployments.d.ts","sourceRoot":"","sources":["../src/deployments.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AA2BrC,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA8DD;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,GAC5B,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAE/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,0BAA0B,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAa7G;AAYD;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3D,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAU/B;AAwBD;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAChC,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAmC/B"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment URLs from the GitHub Deployments API.
|
|
3
|
+
*
|
|
4
|
+
* Vercel, Cloudflare Pages, Netlify and Render all report their deploys to
|
|
5
|
+
* GitHub against a commit, so one lookup covers every provider: no per-host
|
|
6
|
+
* API tokens, and nothing to configure per repo.
|
|
7
|
+
*/
|
|
8
|
+
import { normalizeUrl } from './url.js';
|
|
9
|
+
/**
|
|
10
|
+
* Bots whose PR comments may be read for a preview URL. A comment is only
|
|
11
|
+
* trusted when GitHub reports its author as one of these apps: anyone can post
|
|
12
|
+
* a comment that looks like Vercel's, and following it would point a capture at
|
|
13
|
+
* a URL a stranger chose.
|
|
14
|
+
*/
|
|
15
|
+
const TRUSTED_BOTS = new Set(['vercel[bot]', 'netlify[bot]']);
|
|
16
|
+
/** Commit-status contexts these same providers post under. */
|
|
17
|
+
const PROVIDER_STATUS = /vercel|netlify|cloudflare|render/i;
|
|
18
|
+
const PRODUCTION = /^prod/i;
|
|
19
|
+
/** How many deployments to inspect per page; each costs one status request. */
|
|
20
|
+
const MAX_DEPLOYMENTS = 5;
|
|
21
|
+
/** Deployments per page when scanning for production. */
|
|
22
|
+
const SCAN_PAGE_SIZE = 100;
|
|
23
|
+
/** How far back to scan. A repo can bury production under a wall of previews. */
|
|
24
|
+
const SCAN_PAGES = 3;
|
|
25
|
+
/**
|
|
26
|
+
* The newest successful deployment URL matching a query.
|
|
27
|
+
*
|
|
28
|
+
* Shared by the two questions callers ask — "what was deployed for this
|
|
29
|
+
* commit?" and "what is deployed to production right now?" — so both apply the
|
|
30
|
+
* same production/preview split and the same "success with a URL" rule.
|
|
31
|
+
*/
|
|
32
|
+
async function listDeployments(gh, ownerRepo, query) {
|
|
33
|
+
try {
|
|
34
|
+
const deployments = await gh.request('GET', `/repos/${ownerRepo}/deployments?${query}`);
|
|
35
|
+
return Array.isArray(deployments) ? deployments : null;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** The first of these deployments that reported success with a live URL. */
|
|
42
|
+
async function firstReady(gh, ownerRepo, candidates) {
|
|
43
|
+
const ready = await Promise.all(candidates.map(async (deployment) => {
|
|
44
|
+
try {
|
|
45
|
+
const statuses = await gh.request('GET', `/repos/${ownerRepo}/deployments/${deployment.id}/statuses?per_page=20`);
|
|
46
|
+
if (!Array.isArray(statuses))
|
|
47
|
+
return null;
|
|
48
|
+
const ok = statuses.find((s) => s.state === 'success' && !!s.environment_url);
|
|
49
|
+
return ok ? { url: ok.environment_url, environment: deployment.environment, sha: deployment.sha } : null;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}));
|
|
55
|
+
// Candidates stay in newest-first order, so the first hit is the newest.
|
|
56
|
+
return ready.find(Boolean) ?? null;
|
|
57
|
+
}
|
|
58
|
+
/** One page of deployments, narrowed to production or preview. */
|
|
59
|
+
async function newestSuccessful(gh, ownerRepo, query, production) {
|
|
60
|
+
const deployments = await listDeployments(gh, ownerRepo, query);
|
|
61
|
+
if (!deployments)
|
|
62
|
+
return null;
|
|
63
|
+
return firstReady(gh, ownerRepo, matching(deployments, production));
|
|
64
|
+
}
|
|
65
|
+
/** Production or preview, newest first, capped at what is worth a status call. */
|
|
66
|
+
function matching(deployments, production) {
|
|
67
|
+
return deployments.filter(d => PRODUCTION.test(d.environment || '') === production).slice(0, MAX_DEPLOYMENTS);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The newest successful deployment URL for a commit.
|
|
71
|
+
*
|
|
72
|
+
* `production: true` accepts only production environments (the baseline for
|
|
73
|
+
* "Pre"); `false` accepts only the others, which is where every provider puts
|
|
74
|
+
* per-PR previews. Neither falls back to the other kind — a preview standing in
|
|
75
|
+
* for production would silently compare against the wrong thing.
|
|
76
|
+
*
|
|
77
|
+
* Returns null whenever the answer is not unambiguous, so callers fall through
|
|
78
|
+
* to their next option. Never throws: a repo with no deployments, or a token
|
|
79
|
+
* without `deployments: read`, is a normal outcome, not a failure.
|
|
80
|
+
*/
|
|
81
|
+
export async function deploymentUrlForSha(gh, ownerRepo, sha, opts) {
|
|
82
|
+
return newestSuccessful(gh, ownerRepo, `sha=${encodeURIComponent(sha)}&per_page=20`, opts.production);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* What is deployed to production right now, whatever commit it was built from.
|
|
86
|
+
*
|
|
87
|
+
* `deploymentUrlForSha` pins Pre to the commit the branch forked from, which is
|
|
88
|
+
* the most honest baseline there is — but it only exists if production happened
|
|
89
|
+
* to be deployed at exactly that commit. Production is a moving branch, so on
|
|
90
|
+
* most repositories it was not, and pinning alone leaves the deployed
|
|
91
|
+
* comparison with no Pre at all.
|
|
92
|
+
*
|
|
93
|
+
* This asks the same question without the pin. It reports the commit it found,
|
|
94
|
+
* so the caller can name what Pre actually is rather than implying the base.
|
|
95
|
+
*/
|
|
96
|
+
export async function latestProductionDeployment(gh, ownerRepo) {
|
|
97
|
+
// Deployments come back newest first, previews and production interleaved.
|
|
98
|
+
// A busy repository can push its live production deployment well past the
|
|
99
|
+
// first page, so scan a few pages rather than reading one and giving up —
|
|
100
|
+
// but stop the moment a page comes back short, which is the end of the list.
|
|
101
|
+
for (let page = 1; page <= SCAN_PAGES; page++) {
|
|
102
|
+
const deployments = await listDeployments(gh, ownerRepo, `per_page=${SCAN_PAGE_SIZE}&page=${page}`);
|
|
103
|
+
if (!deployments)
|
|
104
|
+
return null;
|
|
105
|
+
const found = await firstReady(gh, ownerRepo, matching(deployments, true));
|
|
106
|
+
if (found)
|
|
107
|
+
return { ...found, url: normalizeUrl(found.url) };
|
|
108
|
+
if (deployments.length < SCAN_PAGE_SIZE)
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
/** Has a deployment provider reported success against this exact commit? */
|
|
114
|
+
async function providerSucceededFor(gh, ownerRepo, sha) {
|
|
115
|
+
try {
|
|
116
|
+
const combined = await gh.request('GET', `/repos/${ownerRepo}/commits/${sha}/status`);
|
|
117
|
+
return (combined.statuses ?? []).some(s => s.state === 'success' && PROVIDER_STATUS.test(s.context));
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The preview for a commit, from whichever mechanism the provider uses.
|
|
125
|
+
*
|
|
126
|
+
* The freshness rule lives here rather than in the caller: a bot comment is
|
|
127
|
+
* edited in place and carries no SHA, so mid-deploy it still advertises the
|
|
128
|
+
* previous commit's preview. Screenshotting that and labelling it as this
|
|
129
|
+
* branch is the worst failure this tool has, because it looks exactly like a
|
|
130
|
+
* correct result. Every caller gets that guarantee by construction.
|
|
131
|
+
*/
|
|
132
|
+
export async function findPreviewForCommit(gh, ownerRepo, opts) {
|
|
133
|
+
const recorded = await deploymentUrlForSha(gh, ownerRepo, opts.sha, { production: false });
|
|
134
|
+
if (recorded)
|
|
135
|
+
return { ...recorded, url: normalizeUrl(recorded.url) };
|
|
136
|
+
if (opts.prNumber === undefined)
|
|
137
|
+
return null;
|
|
138
|
+
const [fresh, commented] = await Promise.all([
|
|
139
|
+
providerSucceededFor(gh, ownerRepo, opts.sha),
|
|
140
|
+
previewUrlFromComments(gh, ownerRepo, opts.prNumber, { appPrefix: opts.appPrefix }),
|
|
141
|
+
]);
|
|
142
|
+
return fresh && commented ? { ...commented, url: normalizeUrl(commented.url) } : null;
|
|
143
|
+
}
|
|
144
|
+
function decodeVercelPayload(body) {
|
|
145
|
+
const marker = /^\[vc\]: #[^:\n]+:(\S+)/m.exec(body);
|
|
146
|
+
if (!marker)
|
|
147
|
+
return null;
|
|
148
|
+
try {
|
|
149
|
+
const json = JSON.parse(Buffer.from(marker[1], 'base64').toString('utf8'));
|
|
150
|
+
return json && typeof json === 'object' ? json : null;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* The preview URL a deployment bot posted on the PR.
|
|
158
|
+
*
|
|
159
|
+
* Not every provider records a GitHub Deployment — Vercel's GitHub app reports
|
|
160
|
+
* a commit status and a comment, and the commit status only links to its own
|
|
161
|
+
* dashboard. The comment is where the deployed URL actually appears, so this is
|
|
162
|
+
* the fallback when `deploymentUrlForSha` finds nothing.
|
|
163
|
+
*
|
|
164
|
+
* `appPrefix` disambiguates a monorepo comment listing several projects, by
|
|
165
|
+
* matching the project whose root directory is the app being captured.
|
|
166
|
+
*/
|
|
167
|
+
export async function previewUrlFromComments(gh, ownerRepo, prNumber, opts = {}) {
|
|
168
|
+
let comments;
|
|
169
|
+
try {
|
|
170
|
+
comments = await gh.request('GET', `/repos/${ownerRepo}/issues/${prNumber}/comments?per_page=100`);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
if (!Array.isArray(comments))
|
|
176
|
+
return null;
|
|
177
|
+
// Newest first: a re-run posts an updated URL.
|
|
178
|
+
for (const comment of [...comments].reverse()) {
|
|
179
|
+
const body = comment.body ?? '';
|
|
180
|
+
if (!comment.user || !TRUSTED_BOTS.has(comment.user.login))
|
|
181
|
+
continue;
|
|
182
|
+
const payload = decodeVercelPayload(body);
|
|
183
|
+
const projects = (payload?.projects ?? []).filter(p => p.previewUrl);
|
|
184
|
+
if (projects.length) {
|
|
185
|
+
// Only projects that could be the app being captured. A project that
|
|
186
|
+
// declares a different root directory is a different app, so falling back
|
|
187
|
+
// to it would screenshot the wrong site.
|
|
188
|
+
const norm = (dir) => (dir ?? '').replace(/^\.?\//, '').replace(/\/$/, '');
|
|
189
|
+
const candidates = projects.filter(p => !opts.appPrefix || !p.rootDirectory || norm(p.rootDirectory) === opts.appPrefix);
|
|
190
|
+
const chosen = candidates.length === 1 ? candidates[0] : undefined;
|
|
191
|
+
// A build still running has a URL that does not serve the branch yet.
|
|
192
|
+
if (chosen && (chosen.nextCommitStatus ?? 'DEPLOYED') === 'DEPLOYED') {
|
|
193
|
+
return { url: normalizeUrl(chosen.previewUrl), environment: chosen.name ? `Preview (${chosen.name})` : 'Preview' };
|
|
194
|
+
}
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
// Providers without a structured payload still link the preview by name.
|
|
198
|
+
const link = /\[(?:Preview|Deploy Preview)\]\((https?:\/\/[^\s)]+)\)/i.exec(body);
|
|
199
|
+
if (link)
|
|
200
|
+
return { url: link[1], environment: 'Preview' };
|
|
201
|
+
}
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=deployments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployments.js","sourceRoot":"","sources":["../src/deployments.ts"],"names":[],"mappings":"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"}
|