@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.
Files changed (103) hide show
  1. package/README.md +99 -20
  2. package/dist/baseline.d.ts +119 -0
  3. package/dist/baseline.d.ts.map +1 -0
  4. package/dist/baseline.js +316 -0
  5. package/dist/baseline.js.map +1 -0
  6. package/dist/bin/cli.js +80 -41
  7. package/dist/bin/cli.js.map +1 -1
  8. package/dist/browser.d.ts +31 -3
  9. package/dist/browser.d.ts.map +1 -1
  10. package/dist/browser.js +239 -97
  11. package/dist/browser.js.map +1 -1
  12. package/dist/commands/compare.d.ts.map +1 -1
  13. package/dist/commands/compare.js +21 -3
  14. package/dist/commands/compare.js.map +1 -1
  15. package/dist/commands/detect.d.ts +5 -0
  16. package/dist/commands/detect.d.ts.map +1 -1
  17. package/dist/commands/detect.js +4 -1
  18. package/dist/commands/detect.js.map +1 -1
  19. package/dist/commands/doctor.d.ts +32 -0
  20. package/dist/commands/doctor.d.ts.map +1 -1
  21. package/dist/commands/doctor.js +90 -9
  22. package/dist/commands/doctor.js.map +1 -1
  23. package/dist/commands/login.js +1 -1
  24. package/dist/commands/pr.d.ts +4 -0
  25. package/dist/commands/pr.d.ts.map +1 -1
  26. package/dist/commands/pr.js +150 -50
  27. package/dist/commands/pr.js.map +1 -1
  28. package/dist/commands/prune.js +1 -1
  29. package/dist/commands/prune.js.map +1 -1
  30. package/dist/comparison.d.ts +104 -0
  31. package/dist/comparison.d.ts.map +1 -0
  32. package/dist/comparison.js +236 -0
  33. package/dist/comparison.js.map +1 -0
  34. package/dist/config.d.ts +12 -2
  35. package/dist/config.d.ts.map +1 -1
  36. package/dist/config.js +25 -3
  37. package/dist/config.js.map +1 -1
  38. package/dist/deployments.d.ts +71 -0
  39. package/dist/deployments.d.ts.map +1 -0
  40. package/dist/deployments.js +204 -0
  41. package/dist/deployments.js.map +1 -0
  42. package/dist/diff-pool.d.ts.map +1 -1
  43. package/dist/diff-pool.js +5 -2
  44. package/dist/diff-pool.js.map +1 -1
  45. package/dist/diff-worker.d.ts +3 -0
  46. package/dist/diff-worker.d.ts.map +1 -1
  47. package/dist/diff-worker.js +1 -0
  48. package/dist/diff-worker.js.map +1 -1
  49. package/dist/diff.d.ts +10 -0
  50. package/dist/diff.d.ts.map +1 -1
  51. package/dist/diff.js +117 -7
  52. package/dist/diff.js.map +1 -1
  53. package/dist/doctor.d.ts +42 -1
  54. package/dist/doctor.d.ts.map +1 -1
  55. package/dist/doctor.js +73 -7
  56. package/dist/doctor.js.map +1 -1
  57. package/dist/git.d.ts +41 -3
  58. package/dist/git.d.ts.map +1 -1
  59. package/dist/git.js +103 -7
  60. package/dist/git.js.map +1 -1
  61. package/dist/github.d.ts +92 -2
  62. package/dist/github.d.ts.map +1 -1
  63. package/dist/github.js +191 -20
  64. package/dist/github.js.map +1 -1
  65. package/dist/landing.d.ts +48 -0
  66. package/dist/landing.d.ts.map +1 -0
  67. package/dist/landing.js +93 -0
  68. package/dist/landing.js.map +1 -0
  69. package/dist/pkg.d.ts +9 -0
  70. package/dist/pkg.d.ts.map +1 -1
  71. package/dist/pkg.js +14 -0
  72. package/dist/pkg.js.map +1 -1
  73. package/dist/report.d.ts.map +1 -1
  74. package/dist/report.js +49 -26
  75. package/dist/report.js.map +1 -1
  76. package/dist/routes.d.ts +27 -0
  77. package/dist/routes.d.ts.map +1 -1
  78. package/dist/routes.js +98 -23
  79. package/dist/routes.js.map +1 -1
  80. package/dist/run.d.ts +88 -8
  81. package/dist/run.d.ts.map +1 -1
  82. package/dist/run.js +214 -26
  83. package/dist/run.js.map +1 -1
  84. package/dist/sameness.d.ts +91 -0
  85. package/dist/sameness.d.ts.map +1 -0
  86. package/dist/sameness.js +157 -0
  87. package/dist/sameness.js.map +1 -0
  88. package/dist/sessions.d.ts.map +1 -1
  89. package/dist/sessions.js +7 -1
  90. package/dist/sessions.js.map +1 -1
  91. package/dist/shift.d.ts +67 -0
  92. package/dist/shift.d.ts.map +1 -0
  93. package/dist/shift.js +184 -0
  94. package/dist/shift.js.map +1 -0
  95. package/dist/types.d.ts +67 -5
  96. package/dist/types.d.ts.map +1 -1
  97. package/dist/types.js.map +1 -1
  98. package/dist/url.d.ts +12 -0
  99. package/dist/url.d.ts.map +1 -0
  100. package/dist/url.js +30 -0
  101. package/dist/url.js.map +1 -0
  102. package/package.json +1 -1
  103. package/skill/SKILL.md +40 -7
@@ -1 +1 @@
1
- {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Vertical displacement between two captures.
3
+ *
4
+ * A padding change near the top of a page moves everything below it. Pixel
5
+ * diffing counts every moved pixel as changed, so a change a designer would
6
+ * call "slightly roomier" reads as 60-90% of the page repainted — and above
7
+ * CROP_MAX_RATIO no crop is produced at all, so the most dramatic-looking
8
+ * result gives a reviewer the least to look at.
9
+ *
10
+ * A layout shift always has a start: content above the changed element stays
11
+ * put and content below it moves by a constant amount. Recovering that pair
12
+ * (how far, from where) turns the repaint back into a sentence — "content
13
+ * shifted down 48px" — and lets the diff run on aligned images, which is the
14
+ * change the branch actually made.
15
+ *
16
+ * Rows are matched by hash rather than correlated numerically: a shifted page
17
+ * re-renders its rows identically, so exact matches carry the signal and cost
18
+ * one pass over the pixels.
19
+ */
20
+ import { PNG } from 'pngjs';
21
+ export interface ShiftCandidate {
22
+ /** Device pixels the Post content moved down by; negative means up. */
23
+ dy: number;
24
+ /** First row (in Pre coordinates) that moved. Rows above it stayed put. */
25
+ from: number;
26
+ /** Rows the offset explains, as a fraction of all distinctive rows. */
27
+ coverage: number;
28
+ }
29
+ /**
30
+ * FNV-1a over each row's bytes. Distinct content gives distinct hashes; the
31
+ * collision rate over a few thousand rows is negligible, and a collision can
32
+ * only add a stray vote to an offset that other rows have to agree on anyway.
33
+ */
34
+ export declare function rowHashes(png: PNG): Uint32Array;
35
+ /**
36
+ * Find a single vertical displacement that explains the difference between two
37
+ * equally sized images, or null when none does.
38
+ *
39
+ * Rows that repeat (blank space, flat backgrounds, rules) are dropped first:
40
+ * they match every offset equally and would otherwise outvote the content.
41
+ * What remains votes for the offset that maps it from Pre to Post, and the
42
+ * winner has to be supported by enough rows to mean something.
43
+ */
44
+ export declare function detectShift(before: PNG, after: PNG): ShiftCandidate | null;
45
+ /**
46
+ * Rows Post has that Pre never did. A downward shift means something new
47
+ * pushed the page down, and those rows are that something.
48
+ */
49
+ export declare function insertedBand(shift: ShiftCandidate): {
50
+ y: number;
51
+ height: number;
52
+ } | null;
53
+ /**
54
+ * Rebuild Pre in Post's coordinates: content above the shift stays where it
55
+ * is, content below it moves down by `dy`, and an insertion leaves a gap.
56
+ *
57
+ * Aligning this way round rather than pulling Post back is what makes an
58
+ * insertion visible. Mapping every Pre row onto a Post row steps straight over
59
+ * the rows Post gained, so a banner nobody had seen before would contribute
60
+ * nothing and the report would call it "nothing else changed". Left as
61
+ * background, whatever Post put there reads as what it is: new content.
62
+ *
63
+ * It also means the Post image published beside it is the real capture rather
64
+ * than a synthesis, and the re-spaced side is the one being compared against.
65
+ */
66
+ export declare function alignBefore(before: PNG, shift: ShiftCandidate, pad: [number, number, number]): PNG;
67
+ //# sourceMappingURL=shift.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shift.d.ts","sourceRoot":"","sources":["../src/shift.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAS5B,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,EAAE,EAAE,MAAM,CAAC;IACX,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,WAAW,CAc/C;AAQD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,cAAc,GAAG,IAAI,CAiE1E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAExF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,GAAG,CAoBlG"}
package/dist/shift.js ADDED
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Vertical displacement between two captures.
3
+ *
4
+ * A padding change near the top of a page moves everything below it. Pixel
5
+ * diffing counts every moved pixel as changed, so a change a designer would
6
+ * call "slightly roomier" reads as 60-90% of the page repainted — and above
7
+ * CROP_MAX_RATIO no crop is produced at all, so the most dramatic-looking
8
+ * result gives a reviewer the least to look at.
9
+ *
10
+ * A layout shift always has a start: content above the changed element stays
11
+ * put and content below it moves by a constant amount. Recovering that pair
12
+ * (how far, from where) turns the repaint back into a sentence — "content
13
+ * shifted down 48px" — and lets the diff run on aligned images, which is the
14
+ * change the branch actually made.
15
+ *
16
+ * Rows are matched by hash rather than correlated numerically: a shifted page
17
+ * re-renders its rows identically, so exact matches carry the signal and cost
18
+ * one pass over the pixels.
19
+ */
20
+ import { PNG } from 'pngjs';
21
+ /** A row hash seen more often than this in either image carries no position information. */
22
+ const MAX_ROW_REPEATS = 4;
23
+ /** Below this many distinctive rows there is not enough signal to trust an offset. */
24
+ const MIN_DISTINCTIVE_ROWS = 16;
25
+ /** An offset supported by fewer rows than this is noise. */
26
+ const MIN_MOVED_ROWS = 8;
27
+ /**
28
+ * FNV-1a over each row's bytes. Distinct content gives distinct hashes; the
29
+ * collision rate over a few thousand rows is negligible, and a collision can
30
+ * only add a stray vote to an offset that other rows have to agree on anyway.
31
+ */
32
+ export function rowHashes(png) {
33
+ const { width, height, data } = png;
34
+ const stride = width * 4;
35
+ const out = new Uint32Array(height);
36
+ for (let y = 0; y < height; y++) {
37
+ let h = 0x811c9dc5;
38
+ const start = y * stride;
39
+ for (let i = start; i < start + stride; i++) {
40
+ h ^= data[i];
41
+ h = Math.imul(h, 0x01000193);
42
+ }
43
+ out[y] = h >>> 0;
44
+ }
45
+ return out;
46
+ }
47
+ function countHashes(hashes) {
48
+ const counts = new Map();
49
+ for (const h of hashes)
50
+ counts.set(h, (counts.get(h) ?? 0) + 1);
51
+ return counts;
52
+ }
53
+ /**
54
+ * Find a single vertical displacement that explains the difference between two
55
+ * equally sized images, or null when none does.
56
+ *
57
+ * Rows that repeat (blank space, flat backgrounds, rules) are dropped first:
58
+ * they match every offset equally and would otherwise outvote the content.
59
+ * What remains votes for the offset that maps it from Pre to Post, and the
60
+ * winner has to be supported by enough rows to mean something.
61
+ */
62
+ export function detectShift(before, after) {
63
+ if (before.width !== after.width || before.height !== after.height)
64
+ return null;
65
+ const height = before.height;
66
+ const beforeHashes = rowHashes(before);
67
+ const afterHashes = rowHashes(after);
68
+ const beforeCounts = countHashes(beforeHashes);
69
+ const afterCounts = countHashes(afterHashes);
70
+ const distinctive = (h) => (beforeCounts.get(h) ?? 0) > 0 &&
71
+ (beforeCounts.get(h) ?? 0) <= MAX_ROW_REPEATS &&
72
+ (afterCounts.get(h) ?? 0) <= MAX_ROW_REPEATS;
73
+ // Where each distinctive row sits in Pre, so Post rows can look it up.
74
+ const beforeRows = new Map();
75
+ for (let y = 0; y < height; y++) {
76
+ const h = beforeHashes[y];
77
+ if (!distinctive(h))
78
+ continue;
79
+ const list = beforeRows.get(h);
80
+ if (list)
81
+ list.push(y);
82
+ else
83
+ beforeRows.set(h, [y]);
84
+ }
85
+ const votes = new Map();
86
+ let distinctiveRows = 0;
87
+ for (let y = 0; y < height; y++) {
88
+ const h = afterHashes[y];
89
+ if (!distinctive(h))
90
+ continue;
91
+ distinctiveRows++;
92
+ const origins = beforeRows.get(h);
93
+ if (!origins)
94
+ continue;
95
+ for (const origin of origins) {
96
+ const d = y - origin;
97
+ votes.set(d, (votes.get(d) ?? 0) + 1);
98
+ }
99
+ }
100
+ if (distinctiveRows < MIN_DISTINCTIVE_ROWS)
101
+ return null;
102
+ let dy = 0;
103
+ let moved = 0;
104
+ for (const [d, count] of votes) {
105
+ if (d === 0)
106
+ continue;
107
+ if (count > moved) {
108
+ dy = d;
109
+ moved = count;
110
+ }
111
+ }
112
+ if (dy === 0 || moved < MIN_MOVED_ROWS)
113
+ return null;
114
+ // Where the movement starts: the first Pre row that maps to Post at the
115
+ // winning offset and never goes back to sitting still. Everything above it
116
+ // is content the change did not move.
117
+ const stayed = new Set();
118
+ const shifted = new Set();
119
+ for (let y = 0; y < height; y++) {
120
+ const h = beforeHashes[y];
121
+ if (!distinctive(h))
122
+ continue;
123
+ if (afterHashes[y] === h)
124
+ stayed.add(y);
125
+ if (y + dy >= 0 && y + dy < height && afterHashes[y + dy] === h)
126
+ shifted.add(y);
127
+ }
128
+ let from = height;
129
+ for (let y = height - 1; y >= 0; y--) {
130
+ if (shifted.has(y))
131
+ from = y;
132
+ else if (stayed.has(y))
133
+ break;
134
+ }
135
+ if (from >= height)
136
+ return null;
137
+ return { dy, from, coverage: moved / distinctiveRows };
138
+ }
139
+ /**
140
+ * Rows Post has that Pre never did. A downward shift means something new
141
+ * pushed the page down, and those rows are that something.
142
+ */
143
+ export function insertedBand(shift) {
144
+ return shift.dy > 0 ? { y: shift.from, height: shift.dy } : null;
145
+ }
146
+ /**
147
+ * Rebuild Pre in Post's coordinates: content above the shift stays where it
148
+ * is, content below it moves down by `dy`, and an insertion leaves a gap.
149
+ *
150
+ * Aligning this way round rather than pulling Post back is what makes an
151
+ * insertion visible. Mapping every Pre row onto a Post row steps straight over
152
+ * the rows Post gained, so a banner nobody had seen before would contribute
153
+ * nothing and the report would call it "nothing else changed". Left as
154
+ * background, whatever Post put there reads as what it is: new content.
155
+ *
156
+ * It also means the Post image published beside it is the real capture rather
157
+ * than a synthesis, and the re-spaced side is the one being compared against.
158
+ */
159
+ export function alignBefore(before, shift, pad) {
160
+ const { width, height } = before;
161
+ const out = new PNG({ width, height });
162
+ const stride = width * 4;
163
+ const gap = insertedBand(shift);
164
+ for (let y = 0; y < height; y++) {
165
+ // Above the shift the two sides agree on position; below it Pre sits `dy`
166
+ // higher than Post does.
167
+ const source = y < shift.from ? y : y - shift.dy;
168
+ const inGap = gap !== null && y >= gap.y && y < gap.y + gap.height;
169
+ const target = y * stride;
170
+ if (!inGap && source >= 0 && source < height) {
171
+ before.data.copy(out.data, target, source * stride, source * stride + stride);
172
+ }
173
+ else {
174
+ for (let i = target; i < target + stride; i += 4) {
175
+ out.data[i] = pad[0];
176
+ out.data[i + 1] = pad[1];
177
+ out.data[i + 2] = pad[2];
178
+ out.data[i + 3] = 255;
179
+ }
180
+ }
181
+ }
182
+ return out;
183
+ }
184
+ //# sourceMappingURL=shift.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shift.js","sourceRoot":"","sources":["../src/shift.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -48,6 +48,14 @@ export interface CaptureResult {
48
48
  selector?: string;
49
49
  /** HTTP status of the main document */
50
50
  status?: number;
51
+ /** URL the browser ended on, which is not the requested one after a redirect */
52
+ finalUrl?: string;
53
+ /** Document title, used to recognise a sign-in wall that answered with 200 */
54
+ title?: string;
55
+ /** Visible text, used to tell "the same site, changed" from "a different site" */
56
+ text?: string;
57
+ /** Response looked like it came from Vercel (deployment protection) */
58
+ vercel?: boolean;
51
59
  /** Wall-clock milliseconds for navigation + settle + screenshot */
52
60
  durationMs: number;
53
61
  }
@@ -87,6 +95,23 @@ export interface DiffResult {
87
95
  after: Buffer;
88
96
  region: DiffRegion;
89
97
  };
98
+ /** Vertical displacement explaining most of the difference, when one does */
99
+ shift?: ShiftSummary;
100
+ }
101
+ /**
102
+ * A layout shift: Post content moved down (or up) by a constant amount from a
103
+ * given row, and what remains different once the two sides are put back in
104
+ * register. Plain numbers, so it survives the worker-thread boundary.
105
+ */
106
+ export interface ShiftSummary {
107
+ /** Device pixels Post content moved down by; negative means up. */
108
+ dy: number;
109
+ /** First row, in Pre coordinates, that moved. Rows above it stayed put. */
110
+ from: number;
111
+ /** Changed pixels once Post is aligned with Pre. */
112
+ alignedChangedPixels: number;
113
+ /** Aligned changed pixels as a fraction of the canvas. */
114
+ alignedChangedRatio: number;
90
115
  }
91
116
  export type Framework = 'nextjs-app' | 'nextjs-pages' | 'vite' | 'generic';
92
117
  export interface DetectedRoute {
@@ -104,6 +129,8 @@ export interface RouteDetectionOptions {
104
129
  framework?: Framework;
105
130
  /** Maximum number of routes to return (default: 6) */
106
131
  maxRoutes?: number;
132
+ /** Where warnings go. Left unset, they are silent. */
133
+ log?: (msg: string) => void;
107
134
  }
108
135
  export interface PrePostConfig {
109
136
  /** Production base URL — the "before" state */
@@ -114,13 +141,16 @@ export interface PrePostConfig {
114
141
  routes?: string[];
115
142
  /** Sample URLs for dynamic routes, e.g. { "/blog/[slug]": "/blog/hello" } */
116
143
  samples?: Record<string, string>;
117
- /** Viewports to capture. Default: ["desktop", "mobile"] */
144
+ /** Viewports to capture. Default: ["desktop"] */
118
145
  viewports?: Array<ViewportPreset | string>;
119
146
  /** Extra headers for every request */
120
147
  headers?: Record<string, string>;
121
148
  /** Change ratio (fraction 0..1) at or above which a route counts as changed. Default 0.001 */
122
149
  threshold?: number;
123
150
  /** Absolute changed-pixel floor at or above which a route counts as changed, so small edits on tall pages register. Default 40 */
151
+ /** Smallest painted change that counts, in CSS pixels². */
152
+ minChangedArea?: number;
153
+ /** @deprecated device pixels; use minChangedArea. Converted at the capture scale. */
124
154
  minChangedPixels?: number;
125
155
  /** Max detected routes. Default 6 */
126
156
  maxRoutes?: number;
@@ -137,8 +167,10 @@ export interface PrePostConfig {
137
167
  }
138
168
  /** Pre, post, and (when there was a change) diff and crop images, as local paths or URLs. */
139
169
  export interface ArtifactSet {
140
- before: string;
141
- after: string;
170
+ /** Absent when the route has no baseline (a page this branch adds). */
171
+ before?: string;
172
+ /** Absent when the branch removed the route. */
173
+ after?: string;
142
174
  diff?: string;
143
175
  cropBefore?: string;
144
176
  cropAfter?: string;
@@ -149,15 +181,45 @@ export interface RouteCaptureOutcome {
149
181
  /** Route actually requested (after sample substitution) */
150
182
  resolvedRoute: string;
151
183
  viewport: string;
152
- status: 'changed' | 'unchanged' | 'error';
184
+ status: 'changed' | 'unchanged' | 'added' | 'removed' | 'error';
153
185
  changedRatio?: number;
154
186
  sizeChanged?: boolean;
155
187
  error?: string;
156
188
  files?: ArtifactSet;
157
189
  urls?: ArtifactSet;
158
190
  durationMs?: number;
159
- /** Extra context, e.g. "new page (404 on production)" */
191
+ /** sha1 of the baseline capture, for spotting a host that serves one page for every route */
192
+ baselineHash?: string;
193
+ /** Extra context, e.g. "new page — no baseline (example.com returned 404)" */
160
194
  note?: string;
195
+ /** Vertical displacement of Post against Pre, when one explains the change */
196
+ shift?: RouteShift;
197
+ /** A side answered with a sign-in wall, so nothing was really compared */
198
+ blocked?: BlockedSide;
199
+ /**
200
+ * How much of the two sides' wording is shared, or null when neither page
201
+ * had enough text to judge. Near zero on every route means the two sides are
202
+ * different sites, which a pixel ratio cannot tell you — see `sameness.ts`.
203
+ */
204
+ textOverlap?: number | null;
205
+ /**
206
+ * The same measure over the two titles. A title is where a site names
207
+ * itself, so it corroborates a run that only has one route to judge.
208
+ */
209
+ titleOverlap?: number | null;
210
+ }
211
+ export interface BlockedSide {
212
+ side: 'before' | 'after';
213
+ /** The sign-in page the capture landed on */
214
+ finalUrl: string;
215
+ /** Deployment protection, so the bypass secret is the fix */
216
+ vercel: boolean;
217
+ }
218
+ export interface RouteShift {
219
+ /** CSS pixels the content moved down by; negative means up. */
220
+ px: number;
221
+ /** Whether anything changed beyond the move, judged by the same rule as any other capture. */
222
+ otherChange: boolean;
161
223
  }
162
224
  export interface PrRunResult {
163
225
  repo: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,WAAW;AACX,+DAA+D;AAW/D,MAAM,CAAC,MAAM,gBAAgB,GAAyC;IACpE,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;IACrC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IACpC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE;CACpC,CAAC;AAkKF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,CAAU,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,WAAW;AACX,+DAA+D;AAW/D,MAAM,CAAC,MAAM,gBAAgB,GAAyC;IACpE,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;IACrC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IACpC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE;CACpC,CAAC;AAmMF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,CAAU,CAAC"}
package/dist/url.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * URL helpers shared by the capture layer and the command pipeline.
3
+ *
4
+ * Kept out of run.ts so browser.ts can ask "is this loopback?" without
5
+ * importing the module that imports it.
6
+ */
7
+ /** Is this URL served from the machine running the command? */
8
+ export declare function isLocalUrl(url: string): boolean;
9
+ export declare function normalizeUrl(url: string): string;
10
+ export declare function hostOf(url: string): string;
11
+ export declare function joinUrl(base: string, route: string): string;
12
+ //# sourceMappingURL=url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM1C;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3D"}
package/dist/url.js ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * URL helpers shared by the capture layer and the command pipeline.
3
+ *
4
+ * Kept out of run.ts so browser.ts can ask "is this loopback?" without
5
+ * importing the module that imports it.
6
+ */
7
+ /** Add a scheme to a bare URL (http for loopback hosts, https otherwise) and drop trailing slashes. */
8
+ const LOOPBACK = /^(?:(?:https?|file):\/\/)?(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])(:|\/|$)/i;
9
+ /** Is this URL served from the machine running the command? */
10
+ export function isLocalUrl(url) {
11
+ return LOOPBACK.test(url);
12
+ }
13
+ export function normalizeUrl(url) {
14
+ const withScheme = /^(https?|file):\/\//i.test(url)
15
+ ? url
16
+ : isLocalUrl(url) ? `http://${url}` : `https://${url}`;
17
+ return withScheme.replace(/\/+$/, '');
18
+ }
19
+ export function hostOf(url) {
20
+ try {
21
+ return new URL(url).host;
22
+ }
23
+ catch {
24
+ return url;
25
+ }
26
+ }
27
+ export function joinUrl(base, route) {
28
+ return base.replace(/\/+$/, '') + (route.startsWith('/') ? route : `/${route}`);
29
+ }
30
+ //# sourceMappingURL=url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.js","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,uGAAuG;AACvG,MAAM,QAAQ,GAAG,gFAAgF,CAAC;AAElG,+DAA+D;AAC/D,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,MAAM,UAAU,GAAG,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC;QACjD,CAAC,CAAC,GAAG;QACL,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,EAAE,CAAC,CAAC,CAAC,WAAW,GAAG,EAAE,CAAC;IACzD,OAAO,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,GAAW;IAChC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,KAAa;IACjD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;AAClF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juangadm/pre-post",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Before/after screenshots for pull requests: detect changed routes, capture, pixel-diff, and post a sticky PR comment",
5
5
  "author": "Juan Gabriel Delgado (forked from James Clements)",
6
6
  "license": "MIT",
package/skill/SKILL.md CHANGED
@@ -10,9 +10,31 @@ allowed-tools:
10
10
  # pre-post
11
11
 
12
12
  One command does everything: detects the routes this branch changed, screenshots them on
13
- production ("Pre") and on the local dev server ("Post") at desktop and mobile, pixel-diffs
14
- them, uploads the images to a `pre-post-assets` branch, and posts or updates a single
15
- comment on the open PR. The human reviews on GitHub.
13
+ desktop, pixel-diffs them, uploads the images to a `pre-post-assets` branch, and
14
+ posts or updates a single comment on the open PR. The human reviews on GitHub.
15
+
16
+ It picks both sides itself:
17
+
18
+ - **Post** (this branch) — the PR's preview deployment when one exists, read from the GitHub
19
+ Deployments API or, for providers that only comment (Vercel's GitHub app does), from the
20
+ deployment bot's PR comment. Otherwise a dev server: one already running, or one it starts
21
+ and stops itself.
22
+ - **Pre** (the baseline) — `before` from `.pre-post.json`, otherwise the production
23
+ deployment for the commit this branch forked from, otherwise that base commit itself,
24
+ checked out into a throwaway worktree and served locally.
25
+
26
+ Both sides always come from the same kind of environment. Pairing a deployment against a
27
+ dev server would compare different builds — a dev server renders Next.js's dev badge that
28
+ production never shows — so a diff would carry changes the branch never made.
29
+
30
+ So on a PR with a preview deployment, no dev server and no checkout are needed — anyone on
31
+ the team can run it against anyone's PR. With nothing deployed and nothing running, it
32
+ serves both sides itself.
33
+
34
+ The last baseline needs no network at all, so the tool still works inside a sandbox, a CI
35
+ container, or behind an egress allowlist where no deployment URL is reachable. It also
36
+ compares against exactly what the branch forked from, rendered in the same environment as
37
+ the Post side. Pass `--no-local-baseline` to turn it off.
16
38
 
17
39
  ## Run
18
40
 
@@ -20,14 +42,25 @@ comment on the open PR. The human reviews on GitHub.
20
42
  npx -y @juangadm/pre-post@latest pr
21
43
  ```
22
44
 
23
- Add `--before https://production-url` the first time in a repo (it is saved to
24
- `.pre-post.json`). Add `--routes /a,/b` when the user names pages explicitly.
45
+ Add `--routes /a,/b` when the user names pages explicitly. Add
46
+ `--before https://production-url` only if the run reports it cannot work out the baseline
47
+ (it is then saved to `.pre-post.json` for next time).
48
+
49
+ ## What it posts
50
+
51
+ The visual changes go at the **top of the PR description**, in a delimited block that
52
+ re-runs replace in place — the author's own text is never touched. If the PR cannot be
53
+ edited (a fork, a read-only token) it falls back to a single sticky comment.
54
+
55
+ Each changed route shows **Pre beside Post**, per viewport, full pages in a `<details>`. No
56
+ overlay, no percentage. A page that moved says `Content shifted down 48px`; relay as written.
25
57
 
26
58
  ## Rules
27
59
 
28
60
  - Run the command once. Do not open, read, or describe the screenshot files; the PR
29
61
  comment is the deliverable. Report the summary the command prints, plus the comment link.
30
- - Do not start dev servers, switch branches, or use a browser tool yourself.
62
+ - Do not switch branches, start dev servers, or use a browser tool yourself. The command
63
+ handles all three.
31
64
  - Exit code 3 means a human must do one thing (log in, start the dev server, pass
32
65
  `--before`). Relay that one sentence verbatim and stop.
33
66
  - Do not use `--dry-run` unless the user asks to preview without posting.
@@ -39,7 +72,7 @@ Add `--before https://production-url` the first time in a repo (it is saved to
39
72
  | Flag | Use when |
40
73
  |------|----------|
41
74
  | `--routes /a,/b` | The user names the pages |
42
- | `--viewports desktop` | Desktop only (default is desktop + mobile) |
75
+ | `-r`, `--responsive` | Desktop and mobile (default is desktop only) |
43
76
  | `--viewport-only` | First screen only instead of full page |
44
77
  | `--pr <n>` | The branch has several PRs or the lookup fails |
45
78
  | `--dry-run` | Preview locally, post nothing |