@juangadm/pre-post 1.0.0 → 1.1.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 +119 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +316 -0
- package/dist/baseline.js.map +1 -0
- package/dist/bin/cli.js +80 -41
- 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 +239 -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 +150 -50
- 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 +236 -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 +49 -26
- 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 +214 -26
- 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 +67 -5
- package/dist/types.d.ts.map +1 -1
- 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
package/dist/run.js
CHANGED
|
@@ -3,36 +3,50 @@
|
|
|
3
3
|
* Every route/viewport pair runs concurrently; the browser's page pool bounds it.
|
|
4
4
|
*/
|
|
5
5
|
import fs from 'fs';
|
|
6
|
+
import { createHash } from 'crypto';
|
|
6
7
|
import path from 'path';
|
|
7
8
|
import { captureScreenshot } from './browser.js';
|
|
9
|
+
import { checkLanding, signInHint } from './landing.js';
|
|
10
|
+
import { differentSitesHint, looksLikeDifferentSites, textOverlap, titleOverlap } from './sameness.js';
|
|
8
11
|
import { HttpStatusError, NavigationError } from './errors.js';
|
|
9
12
|
import { DiffPool } from './diff-pool.js';
|
|
10
13
|
import { authHint } from './doctor.js';
|
|
14
|
+
import { hostOf } from './url.js';
|
|
11
15
|
export function routeSlug(route) {
|
|
12
16
|
const slug = route.replace(/^\/+|\/+$/g, '').replace(/[^\w.-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
13
17
|
return slug || 'home';
|
|
14
18
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Did this capture change?
|
|
21
|
+
*
|
|
22
|
+
* Two arms, because they cover different regimes and neither covers both:
|
|
23
|
+
*
|
|
24
|
+
* - Painted area, in CSS pixels² — the rule for pages. Measured against the
|
|
25
|
+
* fixture ladder, every deliberate change covers 242 CSS px² or more while
|
|
26
|
+
* every no-op covers exactly 0, so the floor sits in a wide empty band. CSS
|
|
27
|
+
* pixels rather than device pixels so it means the same at --scale 1 as at
|
|
28
|
+
* --scale 2; the old device-pixel constant was silently four times stricter
|
|
29
|
+
* at scale 1, and at ~10 CSS px² amounted to a third of one letter.
|
|
30
|
+
* - Share of the canvas — the rule for small images. One pixel of a 20x20 icon
|
|
31
|
+
* is a quarter of a percent of it and obviously a change, but it will never
|
|
32
|
+
* reach an area floor sized for pages.
|
|
33
|
+
*
|
|
34
|
+
* On a full page the ratio arm effectively never fires on its own (a recoloured
|
|
35
|
+
* 16px icon is 0.024% of one), and on a small image the area arm never does.
|
|
36
|
+
* Dropping either one loses real changes, which is the worse error: reporting
|
|
37
|
+
* "no visual changes" on a PR that changed something is the one answer that
|
|
38
|
+
* makes a reviewer stop looking.
|
|
39
|
+
*/
|
|
40
|
+
export function isChanged(diff, opts) {
|
|
41
|
+
if (diff.changedPixels === 0)
|
|
42
|
+
return false;
|
|
43
|
+
return diff.changedPixels / (opts.scale * opts.scale) >= opts.minChangedArea
|
|
44
|
+
|| diff.changedRatio >= opts.threshold;
|
|
32
45
|
}
|
|
33
|
-
/**
|
|
34
|
-
export function
|
|
35
|
-
|
|
46
|
+
/** "shifted down 48px" — the direction spelled out, because the sign is not. */
|
|
47
|
+
export function describeShift(px) {
|
|
48
|
+
const rounded = Math.round(Math.abs(px));
|
|
49
|
+
return `shifted ${px < 0 ? 'up' : 'down'} ${rounded}px`;
|
|
36
50
|
}
|
|
37
51
|
function describeError(err, side, url) {
|
|
38
52
|
if (err instanceof HttpStatusError)
|
|
@@ -48,6 +62,11 @@ function describeError(err, side, url) {
|
|
|
48
62
|
const msg = err?.message || String(err);
|
|
49
63
|
return `${side}: ${msg.split('\n')[0].slice(0, 160)}`;
|
|
50
64
|
}
|
|
65
|
+
/** Did this side reach the site, or a wall standing in front of it? */
|
|
66
|
+
function blockedSide(requested, result, side) {
|
|
67
|
+
const landing = checkLanding(requested, result.finalUrl ?? requested, result.title ?? '');
|
|
68
|
+
return landing.blocked ? { side, finalUrl: landing.finalUrl, vercel: Boolean(result.vercel) } : null;
|
|
69
|
+
}
|
|
51
70
|
async function runTask(task, opts, pool) {
|
|
52
71
|
const started = Date.now();
|
|
53
72
|
const base = { route: task.route, resolvedRoute: task.resolvedRoute, viewport: task.viewport, status: 'error' };
|
|
@@ -73,12 +92,60 @@ async function runTask(task, opts, pool) {
|
|
|
73
92
|
}
|
|
74
93
|
const before = beforeRes.value;
|
|
75
94
|
const after = afterRes.value;
|
|
95
|
+
// A sign-in wall answers with 200, so both captures "succeeded" — of a login
|
|
96
|
+
// page. Diffing those and calling the result a visual change is the failure
|
|
97
|
+
// this check exists to prevent, so nothing is compared when either side is
|
|
98
|
+
// walled.
|
|
99
|
+
const blocked = blockedSide(task.beforeUrl, before, 'before') ?? blockedSide(task.afterUrl, after, 'after');
|
|
100
|
+
if (blocked) {
|
|
101
|
+
const label = blocked.side === 'before' ? 'Pre' : 'Post';
|
|
102
|
+
return {
|
|
103
|
+
...base,
|
|
104
|
+
blocked,
|
|
105
|
+
error: `${label} landed on a sign-in page (${blocked.finalUrl}), so the site was never captured`,
|
|
106
|
+
durationMs: Date.now() - started,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
76
109
|
if (before.status === 404 && after.status === 404) {
|
|
77
110
|
return { ...base, error: `404 on both ${task.beforeUrl} and ${task.afterUrl} (wrong route or missing sample?)`, durationMs: Date.now() - started };
|
|
78
111
|
}
|
|
79
112
|
const prefix = `${routeSlug(task.route)}-${task.viewport}`;
|
|
80
113
|
const file = (kind) => path.join(opts.outputDir, `${prefix}-${kind}.png`);
|
|
81
114
|
const outputs = { before: file('pre'), after: file('post'), diff: file('diff'), cropBefore: file('pre-crop'), cropAfter: file('post-crop') };
|
|
115
|
+
// A route that exists on only one side is not a comparison, and the moment to
|
|
116
|
+
// notice is here — before the diff, not after it in a footnote.
|
|
117
|
+
//
|
|
118
|
+
// The old path diffed anyway. For a page this branch adds, "Pre" was a
|
|
119
|
+
// screenshot of the baseline's 404 and the percentage measured the distance
|
|
120
|
+
// between an error page and a new one: 10.81% for a coloured box on white,
|
|
121
|
+
// 0.05% for a light-grey one, neither a fact about the change. Worse, both
|
|
122
|
+
// numbers were published as a Pre/Post pair, so a reviewer was shown a
|
|
123
|
+
// comparison that never existed and four images where one was true.
|
|
124
|
+
//
|
|
125
|
+
// Nothing can be recovered by trying harder — a page the baseline does not
|
|
126
|
+
// serve has no "before" to find — so the honest move is to keep the side that
|
|
127
|
+
// rendered, name why the other is missing, and skip the diff entirely.
|
|
128
|
+
const absent = absentSide(before, after);
|
|
129
|
+
if (absent) {
|
|
130
|
+
const present = absent === 'before' ? after : before;
|
|
131
|
+
const target = absent === 'before' ? outputs.after : outputs.before;
|
|
132
|
+
fs.writeFileSync(target, Buffer.from(present.image));
|
|
133
|
+
const status = absent === 'before' ? 'added' : 'removed';
|
|
134
|
+
const missingHost = hostOf(absent === 'before' ? task.beforeUrl : task.afterUrl);
|
|
135
|
+
opts.log?.(` ${status === 'added' ? 'new ' : 'removed '} ${task.route} @ ${task.viewport} (no ${absent === 'before' ? 'Pre' : 'Post'}, ${Date.now() - started}ms)`);
|
|
136
|
+
return {
|
|
137
|
+
...base,
|
|
138
|
+
status,
|
|
139
|
+
files: absent === 'before' ? { after: outputs.after } : { before: outputs.before },
|
|
140
|
+
// Deliberately no changedRatio: there is nothing to take a ratio of.
|
|
141
|
+
// Deliberately no textOverlap either — a 404's wording is evidence about
|
|
142
|
+
// an error page, not about whether the two sides are the same site.
|
|
143
|
+
note: absent === 'before'
|
|
144
|
+
? `new page — no baseline (${missingHost} returned 404)`
|
|
145
|
+
: `page removed — ${missingHost} returned 404`,
|
|
146
|
+
durationMs: Date.now() - started,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
82
149
|
// Copy into standalone buffers so they can be transferred to the worker.
|
|
83
150
|
const diff = await pool.run({
|
|
84
151
|
before: new Uint8Array(before.image),
|
|
@@ -88,22 +155,44 @@ async function runTask(task, opts, pool) {
|
|
|
88
155
|
padding: 40 * opts.scale,
|
|
89
156
|
minCrop: { width: 400 * opts.scale, height: 200 * opts.scale },
|
|
90
157
|
highlightDownscale: opts.scale >= 2 ? 2 : 1,
|
|
158
|
+
highlight: false,
|
|
91
159
|
},
|
|
92
160
|
});
|
|
93
161
|
const notes = [];
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
162
|
+
// The 404-on-one-side cases returned above; what is left is a page both sides
|
|
163
|
+
// served with an unhappy status, which is still worth diffing and flagging.
|
|
164
|
+
if (before.status && before.status >= 400)
|
|
97
165
|
notes.push(`production returned ${before.status}`);
|
|
98
166
|
if (after.status && after.status >= 400)
|
|
99
167
|
notes.push(`local returned ${after.status}`);
|
|
100
|
-
const changed = isChanged(diff, opts
|
|
101
|
-
|
|
168
|
+
const changed = isChanged(diff, opts);
|
|
169
|
+
// Cheap fingerprint of the baseline, kept so the run can notice a host that
|
|
170
|
+
// answers every route with the same page. See softNotFoundWarning.
|
|
171
|
+
const baselineHash = createHash('sha1').update(Buffer.from(before.image)).digest('hex');
|
|
172
|
+
// Recorded on every outcome, judged only across the whole run: one route
|
|
173
|
+
// sharing no words is a rewrite, every route sharing none is the wrong site.
|
|
174
|
+
const overlap = textOverlap(before.text ?? '', after.text ?? '');
|
|
175
|
+
const titles = titleOverlap(before.title ?? '', after.title ?? '');
|
|
176
|
+
// A shift is measured in device pixels; everything a reader sees is in CSS
|
|
177
|
+
// pixels. Whether anything changed besides the move is judged by the same
|
|
178
|
+
// rule as any other capture, against the aligned numbers.
|
|
179
|
+
const shift = diff.shift
|
|
180
|
+
? {
|
|
181
|
+
px: diff.shift.dy / opts.scale,
|
|
182
|
+
otherChange: isChanged({ changedPixels: diff.shift.alignedChangedPixels, changedRatio: diff.shift.alignedChangedRatio }, opts),
|
|
183
|
+
}
|
|
184
|
+
: undefined;
|
|
185
|
+
const shiftNote = shift ? `${describeShift(shift.px)}, ` : '';
|
|
186
|
+
opts.log?.(` ${changed ? 'changed ' : 'same '} ${task.route} @ ${task.viewport} (${shiftNote}${(diff.changedRatio * 100).toFixed(2)}%, ${Date.now() - started}ms)`);
|
|
102
187
|
return {
|
|
103
188
|
...base,
|
|
104
189
|
status: changed ? 'changed' : 'unchanged',
|
|
190
|
+
baselineHash,
|
|
105
191
|
changedRatio: diff.changedRatio,
|
|
106
192
|
sizeChanged: diff.sizeChanged,
|
|
193
|
+
textOverlap: overlap,
|
|
194
|
+
titleOverlap: titles,
|
|
195
|
+
shift,
|
|
107
196
|
files: {
|
|
108
197
|
before: outputs.before,
|
|
109
198
|
after: outputs.after,
|
|
@@ -115,14 +204,113 @@ async function runTask(task, opts, pool) {
|
|
|
115
204
|
durationMs: Date.now() - started,
|
|
116
205
|
};
|
|
117
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Did this run compare the two sites, or something standing in front of them?
|
|
209
|
+
*
|
|
210
|
+
* This lives beside the code that produces the evidence, not in the command
|
|
211
|
+
* that happens to want it. Both checks used to be written out in `pr`, and
|
|
212
|
+
* `compare` — the other caller of this pipeline — therefore had neither, and
|
|
213
|
+
* would print a confident percentage for two unrelated sites. Returning the
|
|
214
|
+
* verdict with the outcomes makes that omission impossible to repeat: a caller
|
|
215
|
+
* cannot get the numbers without also being handed the reason not to trust
|
|
216
|
+
* them.
|
|
217
|
+
*
|
|
218
|
+
* Order matters. A wall is the more specific diagnosis and has the more
|
|
219
|
+
* actionable fix, and a walled run also looks like two different sites — the
|
|
220
|
+
* sign-in page shares no words with the site — so it is answered first.
|
|
221
|
+
*/
|
|
222
|
+
/**
|
|
223
|
+
* Which side, if either, does not have this page at all.
|
|
224
|
+
*
|
|
225
|
+
* The signal is the HTTP status, not the page's wording. A 404 status is a fact
|
|
226
|
+
* the server states about the route; the string "404" in the body is just text,
|
|
227
|
+
* and it is present on pages that are merely *about* error codes and absent
|
|
228
|
+
* from every custom or localised not-found page ever shipped. Sniffing for it
|
|
229
|
+
* would mean this tool's own marketing page — which shows a 404 in a mockup —
|
|
230
|
+
* could be mistaken for a missing route.
|
|
231
|
+
*
|
|
232
|
+
* Soft 404s (a not-found view served with status 200, the usual SPA shape) are
|
|
233
|
+
* therefore invisible here by design; they are caught at the run level instead,
|
|
234
|
+
* where identical baselines across unrelated routes give them away.
|
|
235
|
+
*/
|
|
236
|
+
function absentSide(before, after) {
|
|
237
|
+
const gone = (c) => c.status === 404;
|
|
238
|
+
const served = (c) => !c.status || c.status < 400;
|
|
239
|
+
if (gone(before) && served(after))
|
|
240
|
+
return 'before';
|
|
241
|
+
if (gone(after) && served(before))
|
|
242
|
+
return 'after';
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Did the baseline answer unrelated routes with the same page?
|
|
247
|
+
*
|
|
248
|
+
* This is the soft 404 — a not-found view served with status 200, which no
|
|
249
|
+
* status check can see and which a keyword hunt for "404" would catch only in
|
|
250
|
+
* English, only when the page says it, and only when no real page happens to
|
|
251
|
+
* mention it. The durable signal is not the wording but the sameness: a host
|
|
252
|
+
* that returns pixel-identical bytes for several different routes is serving a
|
|
253
|
+
* catch-all, and every comparison against it is measuring the distance from
|
|
254
|
+
* that catch-all rather than from the page.
|
|
255
|
+
*
|
|
256
|
+
* It warns rather than reclassifies. Two routes can legitimately render the
|
|
257
|
+
* same page, and silently turning a real diff into "new page" on a heuristic
|
|
258
|
+
* would hide exactly the change this tool exists to show. The authoritative
|
|
259
|
+
* signal (a 404 status) acts; this one only tells the reader what it sees.
|
|
260
|
+
*/
|
|
261
|
+
export function softNotFoundWarning(outcomes, beforeUrl) {
|
|
262
|
+
const routesByHash = new Map();
|
|
263
|
+
for (const o of outcomes) {
|
|
264
|
+
if (!o.baselineHash)
|
|
265
|
+
continue;
|
|
266
|
+
const seen = routesByHash.get(o.baselineHash) ?? new Set();
|
|
267
|
+
seen.add(o.route);
|
|
268
|
+
routesByHash.set(o.baselineHash, seen);
|
|
269
|
+
}
|
|
270
|
+
let worst = null;
|
|
271
|
+
for (const routes of routesByHash.values()) {
|
|
272
|
+
if (routes.size > 1 && (!worst || routes.size > worst.size))
|
|
273
|
+
worst = routes;
|
|
274
|
+
}
|
|
275
|
+
if (!worst)
|
|
276
|
+
return null;
|
|
277
|
+
const shown = [...worst].sort().slice(0, 4).join(', ');
|
|
278
|
+
const more = worst.size > 4 ? `, and ${worst.size - 4} more` : '';
|
|
279
|
+
return `Note: ${hostOf(beforeUrl)} served an identical page for ${worst.size} routes (${shown}${more}). `
|
|
280
|
+
+ 'If that is a catch-all or a not-found page, those comparisons are against it, not against the real baseline.';
|
|
281
|
+
}
|
|
282
|
+
export function verdictFor(outcomes, sides) {
|
|
283
|
+
// Every route walled means the run never saw the site. Publishing anything
|
|
284
|
+
// from that, "no visual changes" included, would be a confident lie.
|
|
285
|
+
const walled = outcomes.filter(o => o.blocked);
|
|
286
|
+
if (walled.length > 0 && walled.length === outcomes.length) {
|
|
287
|
+
const { side, vercel } = walled[0].blocked;
|
|
288
|
+
return { kind: 'walled', hint: signInHint(side === 'before' ? sides.before.url : sides.after.url, vercel) };
|
|
289
|
+
}
|
|
290
|
+
if (looksLikeDifferentSites(outcomes)) {
|
|
291
|
+
return { kind: 'different-sites', hint: differentSitesHint(sides.before.url, sides.before.detail, sides.after.url, sides.fix) };
|
|
292
|
+
}
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
118
295
|
export async function runTasks(tasks, opts) {
|
|
119
296
|
fs.mkdirSync(opts.outputDir, { recursive: true });
|
|
120
297
|
const pool = new DiffPool();
|
|
298
|
+
let outcomes;
|
|
121
299
|
try {
|
|
122
|
-
|
|
300
|
+
outcomes = await Promise.all(tasks.map(t => runTask(t, opts, pool)));
|
|
123
301
|
}
|
|
124
302
|
finally {
|
|
125
303
|
await pool.close();
|
|
126
304
|
}
|
|
305
|
+
// Falling back to the first task's URLs keeps the verdict available to a
|
|
306
|
+
// caller that never resolved a pair — `compare`, given two URLs by hand.
|
|
307
|
+
const sides = opts.sides ?? {
|
|
308
|
+
before: { url: tasks[0]?.beforeUrl ?? '' },
|
|
309
|
+
after: { url: tasks[0]?.afterUrl ?? '' },
|
|
310
|
+
};
|
|
311
|
+
const softNotFound = softNotFoundWarning(outcomes, sides.before.url);
|
|
312
|
+
if (softNotFound)
|
|
313
|
+
opts.log?.(softNotFound);
|
|
314
|
+
return { outcomes, verdict: verdictFor(outcomes, sides) };
|
|
127
315
|
}
|
|
128
316
|
//# sourceMappingURL=run.js.map
|
package/dist/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Are the two sides the same site at all?
|
|
3
|
+
*
|
|
4
|
+
* The backstop this file exists to be was first specified as "a diff near 100%
|
|
5
|
+
* on every route means the two sides are different sites". Measured, that rule
|
|
6
|
+
* is wrong in both directions, and it is worth writing down why so it is not
|
|
7
|
+
* proposed again:
|
|
8
|
+
*
|
|
9
|
+
* | pair | changed |
|
|
10
|
+
* |-------------------------------------------------|---------|
|
|
11
|
+
* | the marketing site vs an unrelated real site | 2.66% desktop / 5.02% mobile |
|
|
12
|
+
* | fixture pricing page vs an unrelated fixture | 0.72% – 7.99% |
|
|
13
|
+
* | the same site with a full theme change (fixture) | 99.14% |
|
|
14
|
+
* | the same site redesigned (fixture rung-7) | 99.60% |
|
|
15
|
+
*
|
|
16
|
+
* Web pages are mostly background, and a pixel diff counts pixels that differ,
|
|
17
|
+
* so two *unrelated* pages agree on most of the canvas and read as a small
|
|
18
|
+
* change — the same 1-8% band as the login-page run that started all of this
|
|
19
|
+
* (4.53% / 1.99%). Meanwhile a legitimate redesign, the PR most worth
|
|
20
|
+
* screenshotting, reads ~100%. A threshold at 100% would have missed every
|
|
21
|
+
* real occurrence and fired on the best ones.
|
|
22
|
+
*
|
|
23
|
+
* What does separate them is the words on the page. A redesign changes how a
|
|
24
|
+
* page looks, not what it says; a different site says something else entirely.
|
|
25
|
+
* Measured over the same pairs, with common English words removed:
|
|
26
|
+
*
|
|
27
|
+
* different sites 0.000, 0.000, 0.000, 0.071
|
|
28
|
+
* same site 0.200 (the weakest fixture), 0.417+, 1.000 for real pages
|
|
29
|
+
*
|
|
30
|
+
* The floor below sits in that empty band.
|
|
31
|
+
*/
|
|
32
|
+
/** The distinctive words a page shows a reader. */
|
|
33
|
+
export declare function pageWords(text: string): Set<string>;
|
|
34
|
+
/**
|
|
35
|
+
* A page with almost nothing to say cannot answer this question: an image or a
|
|
36
|
+
* canvas with six words on it scores near zero against anything, including
|
|
37
|
+
* itself one version later. Below this many distinct words, the answer is "do
|
|
38
|
+
* not know" rather than "different".
|
|
39
|
+
*/
|
|
40
|
+
export declare const MIN_WORDS = 10;
|
|
41
|
+
/** The floor, from the measurements above: 0.071 was different, 0.200 was not. */
|
|
42
|
+
export declare const SAME_SITE_FLOOR = 0.1;
|
|
43
|
+
/** Overlap of two pages' body text, or null when either is too text-poor to judge. */
|
|
44
|
+
export declare function textOverlap(before: string, after: string): number | null;
|
|
45
|
+
/**
|
|
46
|
+
* Overlap of two pages' titles, or null when either says nothing distinctive.
|
|
47
|
+
*
|
|
48
|
+
* No `MIN_WORDS` here: a title is a handful of words by nature, and it is the
|
|
49
|
+
* one place a site almost always names itself. That makes it the corroborating
|
|
50
|
+
* signal for a run with only one route to go on — a branch that rewrites a
|
|
51
|
+
* page's copy keeps the site's name in the tab, a different site does not.
|
|
52
|
+
*/
|
|
53
|
+
export declare function titleOverlap(before: string, after: string): number | null;
|
|
54
|
+
/** What this needs from each capture: which page it was, and how the two sides compared. */
|
|
55
|
+
export interface RouteEvidence {
|
|
56
|
+
route?: string;
|
|
57
|
+
textOverlap?: number | null;
|
|
58
|
+
titleOverlap?: number | null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Do the captures, taken together, say these are two different sites?
|
|
62
|
+
*
|
|
63
|
+
* Every judgeable page has to disagree, and the evidence has to be about
|
|
64
|
+
* *pages*, not captures. One route shot at three viewports is one page's word
|
|
65
|
+
* list three times over; counting it as three agreeing witnesses is how a run
|
|
66
|
+
* that detected a single route — an explicit `--routes /`, or the `/` fallback
|
|
67
|
+
* when the diff names none — could reject the one thing this function's own
|
|
68
|
+
* rule permits: a branch that rewrote a page.
|
|
69
|
+
*
|
|
70
|
+
* So captures are folded per route, keeping each route's *strongest* showing of
|
|
71
|
+
* shared words, and a run with only one page to go on asks the title as well. A
|
|
72
|
+
* title is where a site names itself, so a rewritten page keeps it and a
|
|
73
|
+
* different site does not. Without a usable title there is no second witness
|
|
74
|
+
* and the run is published, which is the right way to be wrong here: publishing
|
|
75
|
+
* a diff someone can look at beats refusing to on one page's say-so.
|
|
76
|
+
*/
|
|
77
|
+
export declare function looksLikeDifferentSites(routes: RouteEvidence[]): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* The one sentence a human needs when the two sides are not the same site.
|
|
80
|
+
*
|
|
81
|
+
* `beforeDetail` is how Pre was chosen ("production deployment for 26aa6cd"),
|
|
82
|
+
* absent when a caller was handed both URLs by hand and has no provenance to
|
|
83
|
+
* report. `fix` is the remedy, which is the caller's to name: telling someone
|
|
84
|
+
* to pass `--before` is right when the tool picked the baseline and wrong in
|
|
85
|
+
* the two-URL mode, where both sides came in as positional arguments and that
|
|
86
|
+
* flag is not how the command is invoked. Advice that does not apply to the
|
|
87
|
+
* mode you are in is the failure this project keeps finding — it is what sent
|
|
88
|
+
* someone to sign in to a macOS system service.
|
|
89
|
+
*/
|
|
90
|
+
export declare function differentSitesHint(beforeUrl: string, beforeDetail: string | undefined, afterUrl: string, fix?: string): string;
|
|
91
|
+
//# sourceMappingURL=sameness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sameness.d.ts","sourceRoot":"","sources":["../src/sameness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAeH,mDAAmD;AACnD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAGnD;AAED;;;;;GAKG;AACH,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B,kFAAkF;AAClF,eAAO,MAAM,eAAe,MAAM,CAAC;AAgBnC,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKxE;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKzE;AAED,4FAA4F;AAC5F,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,CAkBxE;AAYD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,MAAoC,GACxC,MAAM,CAGR"}
|
package/dist/sameness.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Are the two sides the same site at all?
|
|
3
|
+
*
|
|
4
|
+
* The backstop this file exists to be was first specified as "a diff near 100%
|
|
5
|
+
* on every route means the two sides are different sites". Measured, that rule
|
|
6
|
+
* is wrong in both directions, and it is worth writing down why so it is not
|
|
7
|
+
* proposed again:
|
|
8
|
+
*
|
|
9
|
+
* | pair | changed |
|
|
10
|
+
* |-------------------------------------------------|---------|
|
|
11
|
+
* | the marketing site vs an unrelated real site | 2.66% desktop / 5.02% mobile |
|
|
12
|
+
* | fixture pricing page vs an unrelated fixture | 0.72% – 7.99% |
|
|
13
|
+
* | the same site with a full theme change (fixture) | 99.14% |
|
|
14
|
+
* | the same site redesigned (fixture rung-7) | 99.60% |
|
|
15
|
+
*
|
|
16
|
+
* Web pages are mostly background, and a pixel diff counts pixels that differ,
|
|
17
|
+
* so two *unrelated* pages agree on most of the canvas and read as a small
|
|
18
|
+
* change — the same 1-8% band as the login-page run that started all of this
|
|
19
|
+
* (4.53% / 1.99%). Meanwhile a legitimate redesign, the PR most worth
|
|
20
|
+
* screenshotting, reads ~100%. A threshold at 100% would have missed every
|
|
21
|
+
* real occurrence and fired on the best ones.
|
|
22
|
+
*
|
|
23
|
+
* What does separate them is the words on the page. A redesign changes how a
|
|
24
|
+
* page looks, not what it says; a different site says something else entirely.
|
|
25
|
+
* Measured over the same pairs, with common English words removed:
|
|
26
|
+
*
|
|
27
|
+
* different sites 0.000, 0.000, 0.000, 0.071
|
|
28
|
+
* same site 0.200 (the weakest fixture), 0.417+, 1.000 for real pages
|
|
29
|
+
*
|
|
30
|
+
* The floor below sits in that empty band.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Words too common to say anything about which site you are on. Without this,
|
|
34
|
+
* two unrelated English pages score 0.18 on shared articles and prepositions
|
|
35
|
+
* alone, which is close enough to the weakest same-site pair (0.20) to make the
|
|
36
|
+
* test unusable.
|
|
37
|
+
*/
|
|
38
|
+
const STOP_WORDS = new Set(('the and for you your are with that this from all not our but can has have was were will more than then ' +
|
|
39
|
+
'when who how why into out off over under new get set use using its about here there they them their ' +
|
|
40
|
+
'his her she him one two any some each own too very just also only same such per via yes non')
|
|
41
|
+
.split(' '));
|
|
42
|
+
/** The distinctive words a page shows a reader. */
|
|
43
|
+
export function pageWords(text) {
|
|
44
|
+
const found = text.toLowerCase().match(/[a-z0-9']{3,}/g) ?? [];
|
|
45
|
+
return new Set(found.filter(w => !STOP_WORDS.has(w)));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A page with almost nothing to say cannot answer this question: an image or a
|
|
49
|
+
* canvas with six words on it scores near zero against anything, including
|
|
50
|
+
* itself one version later. Below this many distinct words, the answer is "do
|
|
51
|
+
* not know" rather than "different".
|
|
52
|
+
*/
|
|
53
|
+
export const MIN_WORDS = 10;
|
|
54
|
+
/** The floor, from the measurements above: 0.071 was different, 0.200 was not. */
|
|
55
|
+
export const SAME_SITE_FLOOR = 0.1;
|
|
56
|
+
/**
|
|
57
|
+
* How much of the smaller vocabulary the larger one also has.
|
|
58
|
+
*
|
|
59
|
+
* Containment rather than Jaccard because the two sides legitimately differ in
|
|
60
|
+
* length — a branch that adds a section should not read as a different site for
|
|
61
|
+
* having more words than the baseline.
|
|
62
|
+
*/
|
|
63
|
+
function containment(a, b) {
|
|
64
|
+
const [small, large] = a.size <= b.size ? [a, b] : [b, a];
|
|
65
|
+
let shared = 0;
|
|
66
|
+
for (const word of small)
|
|
67
|
+
if (large.has(word))
|
|
68
|
+
shared++;
|
|
69
|
+
return shared / small.size;
|
|
70
|
+
}
|
|
71
|
+
/** Overlap of two pages' body text, or null when either is too text-poor to judge. */
|
|
72
|
+
export function textOverlap(before, after) {
|
|
73
|
+
const a = pageWords(before);
|
|
74
|
+
const b = pageWords(after);
|
|
75
|
+
if (a.size < MIN_WORDS || b.size < MIN_WORDS)
|
|
76
|
+
return null;
|
|
77
|
+
return containment(a, b);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Overlap of two pages' titles, or null when either says nothing distinctive.
|
|
81
|
+
*
|
|
82
|
+
* No `MIN_WORDS` here: a title is a handful of words by nature, and it is the
|
|
83
|
+
* one place a site almost always names itself. That makes it the corroborating
|
|
84
|
+
* signal for a run with only one route to go on — a branch that rewrites a
|
|
85
|
+
* page's copy keeps the site's name in the tab, a different site does not.
|
|
86
|
+
*/
|
|
87
|
+
export function titleOverlap(before, after) {
|
|
88
|
+
const a = pageWords(before);
|
|
89
|
+
const b = pageWords(after);
|
|
90
|
+
if (!a.size || !b.size)
|
|
91
|
+
return null;
|
|
92
|
+
return containment(a, b);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Do the captures, taken together, say these are two different sites?
|
|
96
|
+
*
|
|
97
|
+
* Every judgeable page has to disagree, and the evidence has to be about
|
|
98
|
+
* *pages*, not captures. One route shot at three viewports is one page's word
|
|
99
|
+
* list three times over; counting it as three agreeing witnesses is how a run
|
|
100
|
+
* that detected a single route — an explicit `--routes /`, or the `/` fallback
|
|
101
|
+
* when the diff names none — could reject the one thing this function's own
|
|
102
|
+
* rule permits: a branch that rewrote a page.
|
|
103
|
+
*
|
|
104
|
+
* So captures are folded per route, keeping each route's *strongest* showing of
|
|
105
|
+
* shared words, and a run with only one page to go on asks the title as well. A
|
|
106
|
+
* title is where a site names itself, so a rewritten page keeps it and a
|
|
107
|
+
* different site does not. Without a usable title there is no second witness
|
|
108
|
+
* and the run is published, which is the right way to be wrong here: publishing
|
|
109
|
+
* a diff someone can look at beats refusing to on one page's say-so.
|
|
110
|
+
*/
|
|
111
|
+
export function looksLikeDifferentSites(routes) {
|
|
112
|
+
const pages = new Map();
|
|
113
|
+
for (const [i, r] of routes.entries()) {
|
|
114
|
+
const key = r.route ?? `#${i}`;
|
|
115
|
+
const seen = pages.get(key);
|
|
116
|
+
// The best showing across viewports: any viewport that found shared words
|
|
117
|
+
// clears the page, so a single narrow layout cannot condemn it alone.
|
|
118
|
+
pages.set(key, {
|
|
119
|
+
text: strongest(seen?.text, r.textOverlap),
|
|
120
|
+
title: strongest(seen?.title, r.titleOverlap),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
const judged = [...pages.values()].filter(p => p.text !== null);
|
|
124
|
+
if (judged.length === 0 || !judged.every(p => p.text < SAME_SITE_FLOOR))
|
|
125
|
+
return false;
|
|
126
|
+
if (judged.length > 1)
|
|
127
|
+
return true;
|
|
128
|
+
const { title } = judged[0];
|
|
129
|
+
return title !== null && title < SAME_SITE_FLOOR;
|
|
130
|
+
}
|
|
131
|
+
/** The higher of two overlaps, treating "could not judge" as no evidence. */
|
|
132
|
+
function strongest(a, b) {
|
|
133
|
+
if (typeof a !== 'number')
|
|
134
|
+
return typeof b === 'number' ? b : null;
|
|
135
|
+
if (typeof b !== 'number')
|
|
136
|
+
return a;
|
|
137
|
+
return Math.max(a, b);
|
|
138
|
+
}
|
|
139
|
+
/** What to do about it, when the caller says nothing: the `pr` path, which chose Pre itself. */
|
|
140
|
+
const DEFAULT_DIFFERENT_SITES_FIX = "re-run with --before pointing at this site's own deployment";
|
|
141
|
+
/**
|
|
142
|
+
* The one sentence a human needs when the two sides are not the same site.
|
|
143
|
+
*
|
|
144
|
+
* `beforeDetail` is how Pre was chosen ("production deployment for 26aa6cd"),
|
|
145
|
+
* absent when a caller was handed both URLs by hand and has no provenance to
|
|
146
|
+
* report. `fix` is the remedy, which is the caller's to name: telling someone
|
|
147
|
+
* to pass `--before` is right when the tool picked the baseline and wrong in
|
|
148
|
+
* the two-URL mode, where both sides came in as positional arguments and that
|
|
149
|
+
* flag is not how the command is invoked. Advice that does not apply to the
|
|
150
|
+
* mode you are in is the failure this project keeps finding — it is what sent
|
|
151
|
+
* someone to sign in to a macOS system service.
|
|
152
|
+
*/
|
|
153
|
+
export function differentSitesHint(beforeUrl, beforeDetail, afterUrl, fix = DEFAULT_DIFFERENT_SITES_FIX) {
|
|
154
|
+
const pre = beforeDetail ? `${beforeUrl} — ${beforeDetail}` : beforeUrl;
|
|
155
|
+
return `Pre (${pre}) and Post (${afterUrl}) share no wording on any route, so they are not the same site: ${fix}.`;
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=sameness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sameness.js","sourceRoot":"","sources":["../src/sameness.ts"],"names":[],"mappings":"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"}
|
package/dist/sessions.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAC1D,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;KAC1F,CAAC,CAAC;IACH,OAAO,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CAC1F;AAED,wBAAgB,WAAW,IAAI,MAAM,CAKpC;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM,CAKrE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAQ7D;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,SAAS,CAAC,CAkBtE;AAED,MAAM,WAAW,gBAAgB;IAC/B,kCAAkC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,4DAA4D;IAC5D,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,WAAW,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAC1D,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;KAC1F,CAAC,CAAC;IACH,OAAO,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CAC1F;AAED,wBAAgB,WAAW,IAAI,MAAM,CAKpC;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM,CAKrE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAQ7D;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,SAAS,CAAC,CAkBtE;AAED,MAAM,WAAW,gBAAgB;IAC/B,kCAAkC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,4DAA4D;IAC5D,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,WAAW,GAAG,SAAS,CAkB5E"}
|
package/dist/sessions.js
CHANGED
|
@@ -61,9 +61,15 @@ export function cookiesForUrls(urls) {
|
|
|
61
61
|
export function resolveAuth(input) {
|
|
62
62
|
const headers = { ...(input.configHeaders || {}), ...(input.headers || {}) };
|
|
63
63
|
const bypass = process.env.VERCEL_AUTOMATION_BYPASS_SECRET;
|
|
64
|
+
// The bypass header alone is enough, and it is all we send. Asking for the
|
|
65
|
+
// cookie as well (`x-vercel-set-bypass-cookie: true`) makes the host set it
|
|
66
|
+
// through a redirect, which a client that keeps no cookie jar can never
|
|
67
|
+
// satisfy: the reachability probe uses plain `fetch`, so it looped until it
|
|
68
|
+
// threw and every protected preview was reported as unreachable. Measured
|
|
69
|
+
// against a live protected deployment on 2026-09-05 — with the one header,
|
|
70
|
+
// 200 and no redirect; with both, 307 until curl gave up at 50.
|
|
64
71
|
if (bypass && !headers['x-vercel-protection-bypass']) {
|
|
65
72
|
headers['x-vercel-protection-bypass'] = bypass;
|
|
66
|
-
headers['x-vercel-set-bypass-cookie'] = 'true';
|
|
67
73
|
}
|
|
68
74
|
const cookies = [
|
|
69
75
|
...cookiesForUrls(input.urls),
|