@juangadm/pre-post 0.3.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 (153) hide show
  1. package/README.md +174 -233
  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 +243 -458
  7. package/dist/bin/cli.js.map +1 -1
  8. package/dist/browser.d.ts +68 -25
  9. package/dist/browser.d.ts.map +1 -1
  10. package/dist/browser.js +500 -105
  11. package/dist/browser.js.map +1 -1
  12. package/dist/capture.d.ts +1 -6
  13. package/dist/capture.d.ts.map +1 -1
  14. package/dist/capture.js +12 -36
  15. package/dist/capture.js.map +1 -1
  16. package/dist/commands/compare.d.ts +18 -0
  17. package/dist/commands/compare.d.ts.map +1 -0
  18. package/dist/commands/compare.js +82 -0
  19. package/dist/commands/compare.js.map +1 -0
  20. package/dist/commands/detect.d.ts +25 -0
  21. package/dist/commands/detect.d.ts.map +1 -0
  22. package/dist/commands/detect.js +22 -0
  23. package/dist/commands/detect.js.map +1 -0
  24. package/dist/commands/doctor.d.ts +42 -0
  25. package/dist/commands/doctor.d.ts.map +1 -0
  26. package/dist/commands/doctor.js +119 -0
  27. package/dist/commands/doctor.js.map +1 -0
  28. package/dist/commands/login.d.ts +6 -0
  29. package/dist/commands/login.d.ts.map +1 -0
  30. package/dist/commands/login.js +34 -0
  31. package/dist/commands/login.js.map +1 -0
  32. package/dist/commands/pr.d.ts +32 -0
  33. package/dist/commands/pr.d.ts.map +1 -0
  34. package/dist/commands/pr.js +259 -0
  35. package/dist/commands/pr.js.map +1 -0
  36. package/dist/commands/prune.d.ts +10 -0
  37. package/dist/commands/prune.d.ts.map +1 -0
  38. package/dist/commands/prune.js +13 -0
  39. package/dist/commands/prune.js.map +1 -0
  40. package/dist/comparison.d.ts +104 -0
  41. package/dist/comparison.d.ts.map +1 -0
  42. package/dist/comparison.js +236 -0
  43. package/dist/comparison.js.map +1 -0
  44. package/dist/config.d.ts +46 -0
  45. package/dist/config.d.ts.map +1 -0
  46. package/dist/config.js +82 -0
  47. package/dist/config.js.map +1 -0
  48. package/dist/deployments.d.ts +71 -0
  49. package/dist/deployments.d.ts.map +1 -0
  50. package/dist/deployments.js +204 -0
  51. package/dist/deployments.js.map +1 -0
  52. package/dist/diff-pool.d.ts +17 -0
  53. package/dist/diff-pool.d.ts.map +1 -0
  54. package/dist/diff-pool.js +92 -0
  55. package/dist/diff-pool.js.map +1 -0
  56. package/dist/diff-worker.d.ts +32 -0
  57. package/dist/diff-worker.d.ts.map +1 -0
  58. package/dist/diff-worker.js +39 -0
  59. package/dist/diff-worker.js.map +1 -0
  60. package/dist/diff.d.ts +47 -0
  61. package/dist/diff.d.ts.map +1 -0
  62. package/dist/diff.js +281 -0
  63. package/dist/diff.js.map +1 -0
  64. package/dist/doctor.d.ts +79 -0
  65. package/dist/doctor.d.ts.map +1 -0
  66. package/dist/doctor.js +138 -0
  67. package/dist/doctor.js.map +1 -0
  68. package/dist/errors.d.ts +38 -0
  69. package/dist/errors.d.ts.map +1 -0
  70. package/dist/errors.js +59 -0
  71. package/dist/errors.js.map +1 -0
  72. package/dist/git.d.ts +61 -0
  73. package/dist/git.d.ts.map +1 -0
  74. package/dist/git.js +182 -0
  75. package/dist/git.js.map +1 -0
  76. package/dist/github.d.ts +163 -0
  77. package/dist/github.d.ts.map +1 -0
  78. package/dist/github.js +411 -0
  79. package/dist/github.js.map +1 -0
  80. package/dist/index.d.ts +22 -23
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +17 -67
  83. package/dist/index.js.map +1 -1
  84. package/dist/landing.d.ts +48 -0
  85. package/dist/landing.d.ts.map +1 -0
  86. package/dist/landing.js +93 -0
  87. package/dist/landing.js.map +1 -0
  88. package/dist/pkg.d.ts +17 -0
  89. package/dist/pkg.d.ts.map +1 -0
  90. package/dist/pkg.js +49 -0
  91. package/dist/pkg.js.map +1 -0
  92. package/dist/report.d.ts +20 -0
  93. package/dist/report.d.ts.map +1 -0
  94. package/dist/report.js +130 -0
  95. package/dist/report.js.map +1 -0
  96. package/dist/routes/imports.d.ts +46 -0
  97. package/dist/routes/imports.d.ts.map +1 -0
  98. package/dist/routes/imports.js +208 -0
  99. package/dist/routes/imports.js.map +1 -0
  100. package/dist/routes/vite.d.ts +15 -0
  101. package/dist/routes/vite.d.ts.map +1 -0
  102. package/dist/routes/vite.js +130 -0
  103. package/dist/routes/vite.js.map +1 -0
  104. package/dist/routes.d.ts +65 -11
  105. package/dist/routes.d.ts.map +1 -1
  106. package/dist/routes.js +346 -141
  107. package/dist/routes.js.map +1 -1
  108. package/dist/run.d.ts +116 -0
  109. package/dist/run.d.ts.map +1 -0
  110. package/dist/run.js +316 -0
  111. package/dist/run.js.map +1 -0
  112. package/dist/sameness.d.ts +91 -0
  113. package/dist/sameness.d.ts.map +1 -0
  114. package/dist/sameness.js +157 -0
  115. package/dist/sameness.js.map +1 -0
  116. package/dist/sessions.d.ts +50 -0
  117. package/dist/sessions.d.ts.map +1 -0
  118. package/dist/sessions.js +80 -0
  119. package/dist/sessions.js.map +1 -0
  120. package/dist/shift.d.ts +67 -0
  121. package/dist/shift.d.ts.map +1 -0
  122. package/dist/shift.js +184 -0
  123. package/dist/shift.js.map +1 -0
  124. package/dist/types.d.ts +179 -68
  125. package/dist/types.d.ts.map +1 -1
  126. package/dist/types.js +1 -0
  127. package/dist/types.js.map +1 -1
  128. package/dist/url.d.ts +12 -0
  129. package/dist/url.d.ts.map +1 -0
  130. package/dist/url.js +30 -0
  131. package/dist/url.js.map +1 -0
  132. package/dist/viewport.d.ts +6 -1
  133. package/dist/viewport.d.ts.map +1 -1
  134. package/dist/viewport.js +13 -7
  135. package/dist/viewport.js.map +1 -1
  136. package/package.json +21 -14
  137. package/skill/SKILL.md +56 -221
  138. package/dist/clipboard.d.ts +0 -9
  139. package/dist/clipboard.d.ts.map +0 -1
  140. package/dist/clipboard.js +0 -26
  141. package/dist/clipboard.js.map +0 -1
  142. package/dist/upload.d.ts +0 -39
  143. package/dist/upload.d.ts.map +0 -1
  144. package/dist/upload.js +0 -159
  145. package/dist/upload.js.map +0 -1
  146. package/dist/video.d.ts +0 -19
  147. package/dist/video.d.ts.map +0 -1
  148. package/dist/video.js +0 -263
  149. package/dist/video.js.map +0 -1
  150. package/skill/scripts/adapters/0x0st.sh +0 -36
  151. package/skill/scripts/adapters/blob.sh +0 -55
  152. package/skill/scripts/adapters/git-native.sh +0 -51
  153. package/skill/scripts/upload-and-copy.sh +0 -230
package/dist/diff.js ADDED
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Pixel diff between two PNG screenshots.
3
+ *
4
+ * Pure JS (pixelmatch + pngjs): no native build step, works everywhere the
5
+ * CLI runs. Produces a change ratio, the bounding box of the change, a
6
+ * highlight image, and tight crops of before/after around the change.
7
+ */
8
+ import pixelmatch from 'pixelmatch';
9
+ import { PNG } from 'pngjs';
10
+ import { alignBefore, detectShift, insertedBand } from './shift.js';
11
+ const DIFF_COLOR = [255, 0, 0];
12
+ const AA_COLOR = [255, 200, 0];
13
+ /** Background used to pad images of different sizes. */
14
+ const PAD_COLOR = [255, 255, 255];
15
+ /**
16
+ * pixelmatch per-pixel colour distance (0..1).
17
+ *
18
+ * Calibrated, not chosen. At the old 0.1 a 600x320 card recoloured from #ffffff
19
+ * to #e6e6e6 — plainly visible, a sixth of the viewport — reported *zero*
20
+ * changed pixels, and `rung-6-section` measured 1649 CSS px² instead of 67539.
21
+ * The ladder never caught it because every rung varies painted *area* while the
22
+ * threshold governs *contrast*, so nothing in the suite moved along that axis.
23
+ * `contrast-*` is that missing axis.
24
+ *
25
+ * Measured across the fixtures, changed CSS px² by threshold:
26
+ *
27
+ * 0.1 0.05 0.03 0.02 0.01
28
+ * contrast-25 0 191754 191754 191754 191754
29
+ * contrast-15 0 191758 191758 191758 191758
30
+ * contrast-10 0 0 191758 191758 191758
31
+ * contrast-05 0 0 0 0 191762
32
+ * rung-6 1649 1649 67539 67539 67539
33
+ * noise-* 0 0 0 0 0
34
+ *
35
+ * Every no-op stays at zero the whole way down, and so does the strongest
36
+ * real-world check available: the same page captured from two different
37
+ * deployments, which reports 0 at every value above, desktop and mobile.
38
+ *
39
+ * 0.02 catches every delta a reader would call visible and keeps a margin above
40
+ * the floor for sites noisier than the ones measured. 0.01 was also clean here;
41
+ * the margin is deliberate, since the alternative to a marginal false positive
42
+ * is silently publishing "No visual changes" about a page that changed.
43
+ */
44
+ const PIXEL_THRESHOLD = 0.02;
45
+ /** No crop when the change covers more than this fraction of the canvas. */
46
+ const CROP_MAX_RATIO = 0.5;
47
+ /**
48
+ * A shift is only worth reporting when putting the two sides back in register
49
+ * accounts for most of the difference. This is the whole test: an offset that
50
+ * does not remove the pixels it claims to explain is not the story.
51
+ *
52
+ * Judged over the rows Pre and Post share. Content Post gained is excluded
53
+ * from both sides of the comparison, because an insertion is the reason for
54
+ * the move rather than evidence against it: a page pushed down by a banner
55
+ * would otherwise be refused for the size of the banner.
56
+ *
57
+ * Measured on the shift ladder, as a share of the changed pixels over those
58
+ * shared rows: every true shift leaves 0.23 or less, and the horizontal
59
+ * reflow — the one case no vertical offset explains — leaves 0.45. This sits
60
+ * in the empty band between, nearer the real changes so a genuine shift
61
+ * carrying a larger change still reads as one. Erring low is deliberate:
62
+ * rejecting a shift falls back to the old behaviour, while claiming one that
63
+ * isn't there tells a reviewer something untrue.
64
+ */
65
+ const MAX_ALIGNED_SHARE = 0.3;
66
+ function padTo(src, width, height, bg) {
67
+ if (src.width === width && src.height === height)
68
+ return src;
69
+ const out = new PNG({ width, height });
70
+ for (let i = 0; i < out.data.length; i += 4) {
71
+ out.data[i] = bg[0];
72
+ out.data[i + 1] = bg[1];
73
+ out.data[i + 2] = bg[2];
74
+ out.data[i + 3] = 255;
75
+ }
76
+ PNG.bitblt(src, out, 0, 0, src.width, src.height, 0, 0);
77
+ return out;
78
+ }
79
+ function crop(src, region) {
80
+ const out = new PNG({ width: region.width, height: region.height });
81
+ PNG.bitblt(src, out, region.x, region.y, region.width, region.height, 0, 0);
82
+ return encode(out);
83
+ }
84
+ function encode(png) {
85
+ return PNG.sync.write(png, { deflateLevel: 4, filterType: 4 });
86
+ }
87
+ /** Box-filter downscale by an integer factor. */
88
+ export function downscale(src, factor) {
89
+ if (factor <= 1)
90
+ return src;
91
+ const w = Math.max(1, Math.floor(src.width / factor));
92
+ const h = Math.max(1, Math.floor(src.height / factor));
93
+ const out = new PNG({ width: w, height: h });
94
+ const n = factor * factor;
95
+ for (let y = 0; y < h; y++) {
96
+ for (let x = 0; x < w; x++) {
97
+ let r = 0, g = 0, b = 0, a = 0;
98
+ for (let dy = 0; dy < factor; dy++) {
99
+ let i = ((y * factor + dy) * src.width + x * factor) * 4;
100
+ for (let dx = 0; dx < factor; dx++, i += 4) {
101
+ r += src.data[i];
102
+ g += src.data[i + 1];
103
+ b += src.data[i + 2];
104
+ a += src.data[i + 3];
105
+ }
106
+ }
107
+ const o = (y * w + x) * 4;
108
+ out.data[o] = r / n;
109
+ out.data[o + 1] = g / n;
110
+ out.data[o + 2] = b / n;
111
+ out.data[o + 3] = a / n;
112
+ }
113
+ }
114
+ return out;
115
+ }
116
+ export function decodePng(buffer) {
117
+ return PNG.sync.read(buffer);
118
+ }
119
+ /** Diff-coloured pixels within a row range, for weighing one band against another. */
120
+ function countChangedRows(diff, y0, y1) {
121
+ const { width, data } = diff;
122
+ let count = 0;
123
+ for (let y = Math.max(0, y0); y < Math.min(diff.height, y1); y++) {
124
+ const row = y * width * 4;
125
+ for (let x = 0; x < width; x++) {
126
+ const i = row + x * 4;
127
+ if (data[i] === DIFF_COLOR[0] && data[i + 1] === DIFF_COLOR[1] && data[i + 2] === DIFF_COLOR[2])
128
+ count++;
129
+ }
130
+ }
131
+ return count;
132
+ }
133
+ /** Bounding box of pixels painted with the diff color. */
134
+ function boundingBox(diff) {
135
+ const { width, height, data } = diff;
136
+ let minX = width, minY = height, maxX = -1, maxY = -1;
137
+ for (let y = 0; y < height; y++) {
138
+ const row = y * width * 4;
139
+ for (let x = 0; x < width; x++) {
140
+ const i = row + x * 4;
141
+ if (data[i] === DIFF_COLOR[0] && data[i + 1] === DIFF_COLOR[1] && data[i + 2] === DIFF_COLOR[2]) {
142
+ if (x < minX)
143
+ minX = x;
144
+ if (x > maxX)
145
+ maxX = x;
146
+ if (y < minY)
147
+ minY = y;
148
+ if (y > maxY)
149
+ maxY = y;
150
+ }
151
+ }
152
+ }
153
+ if (maxX < 0)
154
+ return null;
155
+ return { x: minX, y: minY, width: maxX - minX + 1, height: maxY - minY + 1 };
156
+ }
157
+ /** Expand a region by padding and to a minimum size, clamped to the canvas. */
158
+ export function expandRegion(region, canvas, padding, min) {
159
+ let x0 = region.x - padding;
160
+ let y0 = region.y - padding;
161
+ let x1 = region.x + region.width + padding;
162
+ let y1 = region.y + region.height + padding;
163
+ const grow = (lo, hi, target, limit) => {
164
+ const size = hi - lo;
165
+ if (size < target) {
166
+ const extra = target - size;
167
+ lo -= Math.floor(extra / 2);
168
+ hi += Math.ceil(extra / 2);
169
+ }
170
+ if (lo < 0) {
171
+ hi -= lo;
172
+ lo = 0;
173
+ }
174
+ if (hi > limit) {
175
+ lo -= hi - limit;
176
+ hi = limit;
177
+ }
178
+ return [Math.max(0, lo), Math.min(limit, hi)];
179
+ };
180
+ [x0, x1] = grow(x0, x1, Math.min(min.width, canvas.width), canvas.width);
181
+ [y0, y1] = grow(y0, y1, Math.min(min.height, canvas.height), canvas.height);
182
+ return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
183
+ }
184
+ /**
185
+ * Compare two PNG buffers. Images of different sizes are padded onto a shared
186
+ * canvas so a height change shows up as a change rather than an error.
187
+ */
188
+ export function diffImages(beforePng, afterPng, options = {}) {
189
+ const bg = PAD_COLOR;
190
+ const a = decodePng(beforePng);
191
+ const b = decodePng(afterPng);
192
+ const width = Math.max(a.width, b.width);
193
+ const height = Math.max(a.height, b.height);
194
+ const sizeChanged = a.width !== b.width || a.height !== b.height;
195
+ const before = padTo(a, width, height, bg);
196
+ const after = padTo(b, width, height, bg);
197
+ const diff = new PNG({ width, height });
198
+ const changedPixels = pixelmatch(before.data, after.data, diff.data, width, height, {
199
+ threshold: PIXEL_THRESHOLD,
200
+ includeAA: false,
201
+ alpha: 0.5,
202
+ diffColor: DIFF_COLOR,
203
+ aaColor: AA_COLOR,
204
+ diffMask: false,
205
+ });
206
+ // A vertical shift repaints everything below where it starts, so the raw
207
+ // bounding box covers most of the page and the crop guards below refuse to
208
+ // zoom in. Comparing Post put back in register with Pre isolates the change
209
+ // the branch actually made, which is small enough to crop and to describe.
210
+ let shift;
211
+ let alignedBefore;
212
+ let alignedDiff;
213
+ if (changedPixels > 0) {
214
+ const candidate = detectShift(before, after);
215
+ if (candidate) {
216
+ const aligned = alignBefore(before, candidate, bg);
217
+ const diffOfAligned = new PNG({ width, height });
218
+ const alignedChangedPixels = pixelmatch(aligned.data, after.data, diffOfAligned.data, width, height, {
219
+ threshold: PIXEL_THRESHOLD,
220
+ includeAA: false,
221
+ alpha: 0.5,
222
+ diffColor: DIFF_COLOR,
223
+ aaColor: AA_COLOR,
224
+ diffMask: false,
225
+ });
226
+ // Weigh the offset only on rows both sides have. Whatever Post put in
227
+ // the gap is new content, and it still counts as a change -- it just
228
+ // does not get a vote on whether the move happened.
229
+ const gap = insertedBand(candidate);
230
+ const insertedChanged = gap ? countChangedRows(diffOfAligned, gap.y, gap.y + gap.height) : 0;
231
+ const rawInGap = gap ? countChangedRows(diff, gap.y, gap.y + gap.height) : 0;
232
+ const mappedChanged = alignedChangedPixels - insertedChanged;
233
+ const rawMapped = changedPixels - rawInGap;
234
+ if (mappedChanged <= rawMapped * MAX_ALIGNED_SHARE) {
235
+ shift = {
236
+ dy: candidate.dy,
237
+ from: candidate.from,
238
+ alignedChangedPixels,
239
+ alignedChangedRatio: alignedChangedPixels / (width * height),
240
+ };
241
+ alignedBefore = aligned;
242
+ alignedDiff = diffOfAligned;
243
+ }
244
+ }
245
+ }
246
+ const region = changedPixels > 0 ? boundingBox(diff) : null;
247
+ const result = {
248
+ changedRatio: changedPixels / (width * height),
249
+ changedPixels,
250
+ width,
251
+ height,
252
+ region,
253
+ sizeChanged,
254
+ // Identical pages need no highlight; skip the downscale + deflate.
255
+ highlight: region && options.highlight !== false
256
+ ? encode(downscale(diff, options.highlightDownscale ?? 1))
257
+ : undefined,
258
+ shift,
259
+ };
260
+ // With a shift, crop what is left once the move is undone; without one, crop
261
+ // the change itself. A pure shift leaves nothing over and gets no crop: the
262
+ // sentence is the report, and the full pages are already published.
263
+ const cropFrom = alignedBefore ?? before;
264
+ const cropRegion = alignedDiff ? boundingBox(alignedDiff) : region;
265
+ if (cropRegion) {
266
+ const area = cropRegion.width * cropRegion.height;
267
+ if (area / (width * height) <= CROP_MAX_RATIO) {
268
+ const expanded = expandRegion(cropRegion, { width, height }, options.padding ?? 80, options.minCrop ?? { width: 800, height: 400 });
269
+ // Only crop when it meaningfully zooms in.
270
+ if (expanded.width * expanded.height < width * height * 0.8) {
271
+ result.crop = {
272
+ before: crop(cropFrom, expanded),
273
+ after: crop(after, expanded),
274
+ region: expanded,
275
+ };
276
+ }
277
+ }
278
+ }
279
+ return result;
280
+ }
281
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAE5B,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAqBpE,MAAM,UAAU,GAA6B,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AACzD,MAAM,QAAQ,GAA6B,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;AACzD,wDAAwD;AACxD,MAAM,SAAS,GAA6B,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAC5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,eAAe,GAAG,IAAI,CAAC;AAC7B,4EAA4E;AAC5E,MAAM,cAAc,GAAG,GAAG,CAAC;AAC3B;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B,SAAS,KAAK,CAAC,GAAQ,EAAE,KAAa,EAAE,MAAc,EAAE,EAA4B;IAClF,IAAI,GAAG,CAAC,KAAK,KAAK,KAAK,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,GAAG,CAAC;IAC7D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5C,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;QAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;QAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;QAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;IAC/F,CAAC;IACD,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACxD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,IAAI,CAAC,GAAQ,EAAE,MAAkB;IACxC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACpE,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5E,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,MAAM,CAAC,GAAQ;IACtB,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,SAAS,CAAC,GAAQ,EAAE,MAAc;IAChD,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAC5B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC;IACtD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IACvD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7C,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;YAC/B,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC;gBACnC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,EAAE,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;gBACzD,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC3C,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;oBAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;oBAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;oBAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBACrF,CAAC;YACH,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YAC1B,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjG,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,sFAAsF;AACtF,SAAS,gBAAgB,CAAC,IAAS,EAAE,EAAU,EAAE,EAAU;IACzD,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IAC7B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACjE,MAAM,GAAG,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;QAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC;gBAAE,KAAK,EAAE,CAAC;QAC3G,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,0DAA0D;AAC1D,SAAS,WAAW,CAAC,IAAS;IAC5B,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IACrC,IAAI,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;IACtD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;QAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChG,IAAI,CAAC,GAAG,IAAI;oBAAE,IAAI,GAAG,CAAC,CAAC;gBACvB,IAAI,CAAC,GAAG,IAAI;oBAAE,IAAI,GAAG,CAAC,CAAC;gBACvB,IAAI,CAAC,GAAG,IAAI;oBAAE,IAAI,GAAG,CAAC,CAAC;gBACvB,IAAI,CAAC,GAAG,IAAI;oBAAE,IAAI,GAAG,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,IAAI,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1B,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC;AAC/E,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,YAAY,CAC1B,MAAkB,EAClB,MAAyC,EACzC,OAAe,EACf,GAAsC;IAEtC,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC;IAC5B,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC;IAC5B,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC;IAC3C,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC;IAE5C,MAAM,IAAI,GAAG,CAAC,EAAU,EAAE,EAAU,EAAE,MAAc,EAAE,KAAa,EAAoB,EAAE;QACvF,MAAM,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC;QACrB,IAAI,IAAI,GAAG,MAAM,EAAE,CAAC;YAClB,MAAM,KAAK,GAAG,MAAM,GAAG,IAAI,CAAC;YAC5B,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;YAC5B,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;YAAC,EAAE,IAAI,EAAE,CAAC;YAAC,EAAE,GAAG,CAAC,CAAC;QAAC,CAAC;QACjC,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC;YAAC,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;YAAC,EAAE,GAAG,KAAK,CAAC;QAAC,CAAC;QACjD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;IAChD,CAAC,CAAC;IAEF,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACzE,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5E,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC;AAC3D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAE,QAAgB,EAAE,UAAuB,EAAE;IACvF,MAAM,EAAE,GAAG,SAAS,CAAC;IACrB,MAAM,CAAC,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,MAAM,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAAC;IAEjE,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAExC,MAAM,aAAa,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE;QAClF,SAAS,EAAE,eAAe;QAC1B,SAAS,EAAE,KAAK;QAChB,KAAK,EAAE,GAAG;QACV,SAAS,EAAE,UAAU;QACrB,OAAO,EAAE,QAAQ;QACjB,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC;IAEH,yEAAyE;IACzE,2EAA2E;IAC3E,4EAA4E;IAC5E,2EAA2E;IAC3E,IAAI,KAA+B,CAAC;IACpC,IAAI,aAA8B,CAAC;IACnC,IAAI,WAA4B,CAAC;IACjC,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAC7C,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;YACnD,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YACjD,MAAM,oBAAoB,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE;gBACnG,SAAS,EAAE,eAAe;gBAC1B,SAAS,EAAE,KAAK;gBAChB,KAAK,EAAE,GAAG;gBACV,SAAS,EAAE,UAAU;gBACrB,OAAO,EAAE,QAAQ;gBACjB,QAAQ,EAAE,KAAK;aAChB,CAAC,CAAC;YACH,sEAAsE;YACtE,qEAAqE;YACrE,oDAAoD;YACpD,MAAM,GAAG,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;YACpC,MAAM,eAAe,GAAG,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7F,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7E,MAAM,aAAa,GAAG,oBAAoB,GAAG,eAAe,CAAC;YAC7D,MAAM,SAAS,GAAG,aAAa,GAAG,QAAQ,CAAC;YAC3C,IAAI,aAAa,IAAI,SAAS,GAAG,iBAAiB,EAAE,CAAC;gBACnD,KAAK,GAAG;oBACN,EAAE,EAAE,SAAS,CAAC,EAAE;oBAChB,IAAI,EAAE,SAAS,CAAC,IAAI;oBACpB,oBAAoB;oBACpB,mBAAmB,EAAE,oBAAoB,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC;iBAC7D,CAAC;gBACF,aAAa,GAAG,OAAO,CAAC;gBACxB,WAAW,GAAG,aAAa,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5D,MAAM,MAAM,GAAe;QACzB,YAAY,EAAE,aAAa,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC;QAC9C,aAAa;QACb,KAAK;QACL,MAAM;QACN,MAAM;QACN,WAAW;QACX,mEAAmE;QACnE,SAAS,EAAE,MAAM,IAAI,OAAO,CAAC,SAAS,KAAK,KAAK;YAC9C,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,kBAAkB,IAAI,CAAC,CAAC,CAAC;YAC1D,CAAC,CAAC,SAAS;QACb,KAAK;KACN,CAAC;IAEF,6EAA6E;IAC7E,4EAA4E;IAC5E,oEAAoE;IACpE,MAAM,QAAQ,GAAG,aAAa,IAAI,MAAM,CAAC;IACzC,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACnE,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC;QAClD,IAAI,IAAI,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,cAAc,EAAE,CAAC;YAC9C,MAAM,QAAQ,GAAG,YAAY,CAC3B,UAAU,EACV,EAAE,KAAK,EAAE,MAAM,EAAE,EACjB,OAAO,CAAC,OAAO,IAAI,EAAE,EACrB,OAAO,CAAC,OAAO,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAC/C,CAAC;YACF,2CAA2C;YAC3C,IAAI,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,GAAG,EAAE,CAAC;gBAC5D,MAAM,CAAC,IAAI,GAAG;oBACZ,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC;oBAChC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC;oBAC5B,MAAM,EAAE,QAAQ;iBACjB,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Preflight: browser present, GitHub auth, dev server, target reachability.
3
+ * Fixes what it can (installs the headless shell) and otherwise stops with
4
+ * exactly one sentence telling the human what to do.
5
+ */
6
+ import { HttpStatusError } from './errors.js';
7
+ export { NeedsHumanError } from './errors.js';
8
+ /**
9
+ * Launch (and install if needed) the shared headless browser. The instance
10
+ * stays open for the captures that follow, so this is the launch, not a probe.
11
+ */
12
+ export declare function ensureBrowser(): Promise<void>;
13
+ /** Headed browser for `pre-post login`; caller owns and closes it. */
14
+ export declare function launchHeadedBrowser(): Promise<import("playwright-core").Browser>;
15
+ /**
16
+ * Is this a dev server, or just something listening?
17
+ *
18
+ * A reachable socket used to be enough, and it is not: on macOS, AirPlay
19
+ * Receiver holds port 5000 and answers every request 403 with no body. The
20
+ * probe adopted it, the 403 was read as access control, and the run advised
21
+ * signing in to a system service — in place of "no dev server is running",
22
+ * which is the one thing that was true. It bit precisely when nothing else was
23
+ * listening, the case the message exists to describe.
24
+ *
25
+ * So the bar is a plausible *app* response, not a reachable port:
26
+ *
27
+ * - 401/403 never qualifies. That is a wall or a stranger; either way there is
28
+ * no site behind it to capture, and inferring a dev server from one is how
29
+ * the above happened.
30
+ * - A redirect does. Locale prefixes and trailing slashes make it ordinary, and
31
+ * there is no body to inspect, so the status is all there is to go on. If it
32
+ * leads somewhere sign-in shaped, `landing.ts` catches that at capture time
33
+ * with a message about the wall rather than about ports.
34
+ * - Anything else has to have served an HTML document. This deliberately keeps
35
+ * a 404 or a 500 that renders a page: a dev server with no `/` route, or one
36
+ * with a compile error, is still the dev server, and refusing it would send
37
+ * someone hunting for a server they are already running.
38
+ */
39
+ export declare function looksLikeDevServer(result: ProbeResult): boolean;
40
+ export interface DevServerScan {
41
+ /** Base URL of the dev server, or null when none of the ports had one. */
42
+ url: string | null;
43
+ /** Ports that answered but are not serving a site — worth naming, not using. */
44
+ ignored: Array<{
45
+ port: number;
46
+ status: number;
47
+ }>;
48
+ }
49
+ /**
50
+ * Probe the usual ports and say what was found *and* what was passed over.
51
+ *
52
+ * `doctor` reports the ignored ports because "nothing on the usual ports" is
53
+ * confusing when something is plainly listening on one of them.
54
+ */
55
+ export declare function scanDevServers(ports?: number[]): Promise<DevServerScan>;
56
+ /** Find a running local dev server. Returns its base URL or null. */
57
+ export declare function detectDevServer(ports?: number[]): Promise<string | null>;
58
+ export interface ProbeResult {
59
+ /** HTTP status, or null when the host could not be reached at all */
60
+ status: number | null;
61
+ vercel: boolean;
62
+ /** Response Content-Type, lowercased and without parameters. */
63
+ contentType?: string;
64
+ }
65
+ /**
66
+ * Probe a base URL with the headers we will use for capture.
67
+ */
68
+ export declare function probeUrl(url: string, headers?: Record<string, string>, options?: {
69
+ timeoutMs?: number;
70
+ redirect?: RequestRedirect;
71
+ }): Promise<ProbeResult>;
72
+ /**
73
+ * Explain a 401/403 in one actionable sentence. Accepts a probe result or the typed capture error.
74
+ */
75
+ export declare function authHint(target: HttpStatusError | {
76
+ url: string;
77
+ vercel: boolean;
78
+ }): string;
79
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAwB,eAAe,EAAqC,MAAM,aAAa,CAAC;AACvG,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAe9C;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAMnD;AAED,sEAAsE;AACtE,wBAAsB,mBAAmB,+CAMxC;AAoBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAM/D;AAED,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,gFAAgF;IAChF,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,KAAK,GAAE,MAAM,EAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAWxF;AAED,qEAAqE;AACrE,wBAAsB,eAAe,CAAC,KAAK,GAAE,MAAM,EAAc,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEzF;AAED,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACpC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,eAAe,CAAA;CAAO,GAC/D,OAAO,CAAC,WAAW,CAAC,CAQtB;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAO3F"}
package/dist/doctor.js ADDED
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Preflight: browser present, GitHub auth, dev server, target reachability.
3
+ * Fixes what it can (installs the headless shell) and otherwise stops with
4
+ * exactly one sentence telling the human what to do.
5
+ */
6
+ import { getBrowser, launchBrowserOrInstall } from './browser.js';
7
+ import { hostOf } from './url.js';
8
+ import { BrowserNotFoundError, NeedsHumanError, isVercelResponse } from './errors.js';
9
+ export { NeedsHumanError } from './errors.js';
10
+ const INSTALL_HINT = 'Run: npx playwright-core install chromium-headless-shell (or set PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH), then re-run.';
11
+ function explainBrowserFailure(err) {
12
+ if (err instanceof BrowserNotFoundError) {
13
+ throw new NeedsHumanError(err.installed
14
+ ? `Chromium was installed but cannot launch. ${INSTALL_HINT}`
15
+ : `Chromium could not be installed automatically. ${INSTALL_HINT}`);
16
+ }
17
+ throw err;
18
+ }
19
+ /**
20
+ * Launch (and install if needed) the shared headless browser. The instance
21
+ * stays open for the captures that follow, so this is the launch, not a probe.
22
+ */
23
+ export async function ensureBrowser() {
24
+ try {
25
+ await getBrowser();
26
+ }
27
+ catch (err) {
28
+ explainBrowserFailure(err);
29
+ }
30
+ }
31
+ /** Headed browser for `pre-post login`; caller owns and closes it. */
32
+ export async function launchHeadedBrowser() {
33
+ try {
34
+ return await launchBrowserOrInstall({ headless: false });
35
+ }
36
+ catch (err) {
37
+ explainBrowserFailure(err);
38
+ }
39
+ }
40
+ /**
41
+ * Ports worth asking, most likely first: the winner is the earliest that
42
+ * answers, so order is only ever a tie-break between two live servers.
43
+ *
44
+ * Order is not what keeps AirPlay out — `looksLikeDevServer` is, on every
45
+ * platform and at any position. 5000 sits last because on macOS it usually
46
+ * belongs to AirPlay Receiver rather than to a dev server, so it is the
47
+ * weakest guess in the list, not because being last protects anything. 7000,
48
+ * its sibling, is left out entirely: no framework defaults to it, so it has
49
+ * nothing to weigh against the noise.
50
+ */
51
+ const DEV_PORTS = [3000, 3001, 3002, 5173, 5174, 4173, 4321, 8080, 8000, 4200, 5000];
52
+ /** An HTML document. `probeUrl` has already lowercased and dropped parameters. */
53
+ function isHtml(contentType) {
54
+ return contentType === 'text/html' || contentType === 'application/xhtml+xml';
55
+ }
56
+ /**
57
+ * Is this a dev server, or just something listening?
58
+ *
59
+ * A reachable socket used to be enough, and it is not: on macOS, AirPlay
60
+ * Receiver holds port 5000 and answers every request 403 with no body. The
61
+ * probe adopted it, the 403 was read as access control, and the run advised
62
+ * signing in to a system service — in place of "no dev server is running",
63
+ * which is the one thing that was true. It bit precisely when nothing else was
64
+ * listening, the case the message exists to describe.
65
+ *
66
+ * So the bar is a plausible *app* response, not a reachable port:
67
+ *
68
+ * - 401/403 never qualifies. That is a wall or a stranger; either way there is
69
+ * no site behind it to capture, and inferring a dev server from one is how
70
+ * the above happened.
71
+ * - A redirect does. Locale prefixes and trailing slashes make it ordinary, and
72
+ * there is no body to inspect, so the status is all there is to go on. If it
73
+ * leads somewhere sign-in shaped, `landing.ts` catches that at capture time
74
+ * with a message about the wall rather than about ports.
75
+ * - Anything else has to have served an HTML document. This deliberately keeps
76
+ * a 404 or a 500 that renders a page: a dev server with no `/` route, or one
77
+ * with a compile error, is still the dev server, and refusing it would send
78
+ * someone hunting for a server they are already running.
79
+ */
80
+ export function looksLikeDevServer(result) {
81
+ const { status, contentType } = result;
82
+ if (status === null)
83
+ return false;
84
+ if (status === 401 || status === 403)
85
+ return false;
86
+ if (status >= 300 && status < 400)
87
+ return true;
88
+ return isHtml(contentType);
89
+ }
90
+ /**
91
+ * Probe the usual ports and say what was found *and* what was passed over.
92
+ *
93
+ * `doctor` reports the ignored ports because "nothing on the usual ports" is
94
+ * confusing when something is plainly listening on one of them.
95
+ */
96
+ export async function scanDevServers(ports = DEV_PORTS) {
97
+ const results = await Promise.all(ports.map(async (port) => ({
98
+ port,
99
+ ...(await probeUrl(`http://localhost:${port}/`, {}, { timeoutMs: 1500, redirect: 'manual' })),
100
+ })));
101
+ const scan = { url: null, ignored: [] };
102
+ for (const result of results) {
103
+ if (looksLikeDevServer(result))
104
+ scan.url ??= `http://localhost:${result.port}`;
105
+ else if (result.status !== null)
106
+ scan.ignored.push({ port: result.port, status: result.status });
107
+ }
108
+ return scan;
109
+ }
110
+ /** Find a running local dev server. Returns its base URL or null. */
111
+ export async function detectDevServer(ports = DEV_PORTS) {
112
+ return (await scanDevServers(ports)).url;
113
+ }
114
+ /**
115
+ * Probe a base URL with the headers we will use for capture.
116
+ */
117
+ export async function probeUrl(url, headers = {}, options = {}) {
118
+ try {
119
+ const res = await fetch(url, { method: 'GET', headers, redirect: options.redirect ?? 'follow', signal: AbortSignal.timeout(options.timeoutMs ?? 10_000) });
120
+ const contentType = res.headers.get('content-type')?.split(';')[0].trim().toLowerCase();
121
+ return { status: res.status, vercel: isVercelResponse(res.headers), contentType: contentType || undefined };
122
+ }
123
+ catch {
124
+ return { status: null, vercel: false };
125
+ }
126
+ }
127
+ /**
128
+ * Explain a 401/403 in one actionable sentence. Accepts a probe result or the typed capture error.
129
+ */
130
+ export function authHint(target) {
131
+ const { url, vercel } = target;
132
+ const host = hostOf(url);
133
+ if (vercel || host.endsWith('.vercel.app')) {
134
+ return `${host} is protected by Vercel Deployment Protection. Either set VERCEL_AUTOMATION_BYPASS_SECRET (Project → Settings → Deployment Protection → Protection Bypass for Automation) or run: npx pre-post login ${url}`;
135
+ }
136
+ return `${host} requires a login. Run: npx pre-post login ${url} (opens a browser once, saves the session locally)`;
137
+ }
138
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Typed errors shared across modules. Thrown at the source, mapped once in the CLI.
3
+ */
4
+ /** A human must do exactly one thing; the message is that one sentence. Exit code 3. */
5
+ export declare class NeedsHumanError extends Error {
6
+ constructor(message: string);
7
+ }
8
+ /** The main document answered with an HTTP status that means "not for you" (401/403). */
9
+ export declare class HttpStatusError extends Error {
10
+ readonly status: number;
11
+ readonly url: string;
12
+ /** Response looked like it came from Vercel (deployment protection). */
13
+ readonly vercel: boolean;
14
+ constructor(status: number, url: string,
15
+ /** Response looked like it came from Vercel (deployment protection). */
16
+ vercel?: boolean);
17
+ }
18
+ export type NavigationFailure = 'refused' | 'timeout' | 'dns' | 'other';
19
+ /** page.goto failed before any document arrived. */
20
+ export declare class NavigationError extends Error {
21
+ readonly kind: NavigationFailure;
22
+ readonly url: string;
23
+ constructor(kind: NavigationFailure, url: string, cause: Error);
24
+ }
25
+ export declare class BrowserNotFoundError extends Error {
26
+ readonly cause?: (Error | null) | undefined;
27
+ readonly installed: boolean;
28
+ constructor(cause?: (Error | null) | undefined, /** true when an install just ran and launch still failed */ installed?: boolean);
29
+ }
30
+ export declare class GitHubError extends Error {
31
+ readonly status: number;
32
+ constructor(status: number, message: string);
33
+ }
34
+ export declare const GH_LOGIN_HINT = "Run: gh auth login (or set GH_TOKEN with repo access), then re-run.";
35
+ export declare function isVercelResponse(headers: {
36
+ get(name: string): string | null;
37
+ }): boolean;
38
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,wFAAwF;AACxF,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED,yFAAyF;AACzF,qBAAa,eAAgB,SAAQ,KAAK;aAEtB,MAAM,EAAE,MAAM;aACd,GAAG,EAAE,MAAM;IAC3B,wEAAwE;aACxD,MAAM;gBAHN,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM;IAC3B,wEAAwE;IACxD,MAAM,UAAQ;CAKjC;AAED,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,KAAK,GAAG,OAAO,CAAC;AAExE,oDAAoD;AACpD,qBAAa,eAAgB,SAAQ,KAAK;aACZ,IAAI,EAAE,iBAAiB;aAAkB,GAAG,EAAE,MAAM;gBAApD,IAAI,EAAE,iBAAiB,EAAkB,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;CAI/F;AAED,qBAAa,oBAAqB,SAAQ,KAAK;aACjB,KAAK,CAAC,GAAE,KAAK,GAAG,IAAI;aAA+E,SAAS;gBAA5G,KAAK,CAAC,GAAE,KAAK,GAAG,IAAI,aAAA,EAAE,4DAA4D,CAAiB,SAAS,UAAQ;CAIjJ;AAED,qBAAa,WAAY,SAAQ,KAAK;aACR,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAI5D;AAED,eAAO,MAAM,aAAa,0EAA0E,CAAC;AAErG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,OAAO,CAEvF"}
package/dist/errors.js ADDED
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Typed errors shared across modules. Thrown at the source, mapped once in the CLI.
3
+ */
4
+ /** A human must do exactly one thing; the message is that one sentence. Exit code 3. */
5
+ export class NeedsHumanError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = 'NeedsHumanError';
9
+ }
10
+ }
11
+ /** The main document answered with an HTTP status that means "not for you" (401/403). */
12
+ export class HttpStatusError extends Error {
13
+ status;
14
+ url;
15
+ vercel;
16
+ constructor(status, url,
17
+ /** Response looked like it came from Vercel (deployment protection). */
18
+ vercel = false) {
19
+ super(`${status} for ${url}`);
20
+ this.status = status;
21
+ this.url = url;
22
+ this.vercel = vercel;
23
+ this.name = 'HttpStatusError';
24
+ }
25
+ }
26
+ /** page.goto failed before any document arrived. */
27
+ export class NavigationError extends Error {
28
+ kind;
29
+ url;
30
+ constructor(kind, url, cause) {
31
+ super(cause.message.split('\n')[0]);
32
+ this.kind = kind;
33
+ this.url = url;
34
+ this.name = 'NavigationError';
35
+ }
36
+ }
37
+ export class BrowserNotFoundError extends Error {
38
+ cause;
39
+ installed;
40
+ constructor(cause, /** true when an install just ran and launch still failed */ installed = false) {
41
+ super('No usable Chromium found');
42
+ this.cause = cause;
43
+ this.installed = installed;
44
+ this.name = 'BrowserNotFoundError';
45
+ }
46
+ }
47
+ export class GitHubError extends Error {
48
+ status;
49
+ constructor(status, message) {
50
+ super(message);
51
+ this.status = status;
52
+ this.name = 'GitHubError';
53
+ }
54
+ }
55
+ export const GH_LOGIN_HINT = 'Run: gh auth login (or set GH_TOKEN with repo access), then re-run.';
56
+ export function isVercelResponse(headers) {
57
+ return Boolean(headers.get('x-vercel-id') || headers.get('server')?.toLowerCase().includes('vercel'));
58
+ }
59
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,wFAAwF;AACxF,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED,yFAAyF;AACzF,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAEtB;IACA;IAEA;IAJlB,YACkB,MAAc,EACd,GAAW;IAC3B,wEAAwE;IACxD,SAAS,KAAK;QAE9B,KAAK,CAAC,GAAG,MAAM,QAAQ,GAAG,EAAE,CAAC,CAAC;QALd,WAAM,GAAN,MAAM,CAAQ;QACd,QAAG,GAAH,GAAG,CAAQ;QAEX,WAAM,GAAN,MAAM,CAAQ;QAG9B,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAID,oDAAoD;AACpD,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACZ;IAAyC;IAArE,YAA4B,IAAuB,EAAkB,GAAW,EAAE,KAAY;QAC5F,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QADV,SAAI,GAAJ,IAAI,CAAmB;QAAkB,QAAG,GAAH,GAAG,CAAQ;QAE9E,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IACjB;IAAmG;IAA/H,YAA4B,KAAoB,EAAE,4DAA4D,CAAiB,YAAY,KAAK;QAC9I,KAAK,CAAC,0BAA0B,CAAC,CAAC;QADR,UAAK,GAAL,KAAK,CAAe;QAA+E,cAAS,GAAT,SAAS,CAAQ;QAE9I,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAED,MAAM,OAAO,WAAY,SAAQ,KAAK;IACR;IAA5B,YAA4B,MAAc,EAAE,OAAe;QACzD,KAAK,CAAC,OAAO,CAAC,CAAC;QADW,WAAM,GAAN,MAAM,CAAQ;QAExC,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,uEAAuE,CAAC;AAErG,MAAM,UAAU,gBAAgB,CAAC,OAA6C;IAC5E,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACxG,CAAC"}