@juangadm/pre-post 0.2.0 → 1.0.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 (125) hide show
  1. package/README.md +102 -222
  2. package/dist/bin/cli.js +199 -347
  3. package/dist/bin/cli.js.map +1 -1
  4. package/dist/browser.d.ts +42 -11
  5. package/dist/browser.d.ts.map +1 -1
  6. package/dist/browser.js +371 -94
  7. package/dist/browser.js.map +1 -1
  8. package/dist/capture.d.ts +1 -6
  9. package/dist/capture.d.ts.map +1 -1
  10. package/dist/capture.js +12 -35
  11. package/dist/capture.js.map +1 -1
  12. package/dist/commands/compare.d.ts +18 -0
  13. package/dist/commands/compare.d.ts.map +1 -0
  14. package/dist/commands/compare.js +64 -0
  15. package/dist/commands/compare.js.map +1 -0
  16. package/dist/commands/detect.d.ts +20 -0
  17. package/dist/commands/detect.d.ts.map +1 -0
  18. package/dist/commands/detect.js +19 -0
  19. package/dist/commands/detect.js.map +1 -0
  20. package/dist/commands/doctor.d.ts +10 -0
  21. package/dist/commands/doctor.d.ts.map +1 -0
  22. package/dist/commands/doctor.js +38 -0
  23. package/dist/commands/doctor.js.map +1 -0
  24. package/dist/commands/login.d.ts +6 -0
  25. package/dist/commands/login.d.ts.map +1 -0
  26. package/dist/commands/login.js +34 -0
  27. package/dist/commands/login.js.map +1 -0
  28. package/dist/commands/pr.d.ts +28 -0
  29. package/dist/commands/pr.d.ts.map +1 -0
  30. package/dist/commands/pr.js +159 -0
  31. package/dist/commands/pr.js.map +1 -0
  32. package/dist/commands/prune.d.ts +10 -0
  33. package/dist/commands/prune.d.ts.map +1 -0
  34. package/dist/commands/prune.js +13 -0
  35. package/dist/commands/prune.js.map +1 -0
  36. package/dist/config.d.ts +36 -0
  37. package/dist/config.d.ts.map +1 -0
  38. package/dist/config.js +60 -0
  39. package/dist/config.js.map +1 -0
  40. package/dist/diff-pool.d.ts +17 -0
  41. package/dist/diff-pool.d.ts.map +1 -0
  42. package/dist/diff-pool.js +89 -0
  43. package/dist/diff-pool.js.map +1 -0
  44. package/dist/diff-worker.d.ts +29 -0
  45. package/dist/diff-worker.d.ts.map +1 -0
  46. package/dist/diff-worker.js +38 -0
  47. package/dist/diff-worker.js.map +1 -0
  48. package/dist/diff.d.ts +37 -0
  49. package/dist/diff.d.ts.map +1 -0
  50. package/dist/diff.js +171 -0
  51. package/dist/diff.js.map +1 -0
  52. package/dist/doctor.d.ts +38 -0
  53. package/dist/doctor.d.ts.map +1 -0
  54. package/dist/doctor.js +72 -0
  55. package/dist/doctor.js.map +1 -0
  56. package/dist/errors.d.ts +38 -0
  57. package/dist/errors.d.ts.map +1 -0
  58. package/dist/errors.js +59 -0
  59. package/dist/errors.js.map +1 -0
  60. package/dist/filename.d.ts +2 -0
  61. package/dist/filename.d.ts.map +1 -1
  62. package/dist/filename.js +2 -1
  63. package/dist/filename.js.map +1 -1
  64. package/dist/git.d.ts +23 -0
  65. package/dist/git.d.ts.map +1 -0
  66. package/dist/git.js +86 -0
  67. package/dist/git.js.map +1 -0
  68. package/dist/github.d.ts +73 -0
  69. package/dist/github.d.ts.map +1 -0
  70. package/dist/github.js +240 -0
  71. package/dist/github.js.map +1 -0
  72. package/dist/index.d.ts +22 -22
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +17 -66
  75. package/dist/index.js.map +1 -1
  76. package/dist/pkg.d.ts +8 -0
  77. package/dist/pkg.d.ts.map +1 -0
  78. package/dist/pkg.js +35 -0
  79. package/dist/pkg.js.map +1 -0
  80. package/dist/report.d.ts +20 -0
  81. package/dist/report.d.ts.map +1 -0
  82. package/dist/report.js +107 -0
  83. package/dist/report.js.map +1 -0
  84. package/dist/routes/imports.d.ts +46 -0
  85. package/dist/routes/imports.d.ts.map +1 -0
  86. package/dist/routes/imports.js +208 -0
  87. package/dist/routes/imports.js.map +1 -0
  88. package/dist/routes/vite.d.ts +15 -0
  89. package/dist/routes/vite.d.ts.map +1 -0
  90. package/dist/routes/vite.js +130 -0
  91. package/dist/routes/vite.js.map +1 -0
  92. package/dist/routes.d.ts +41 -14
  93. package/dist/routes.d.ts.map +1 -1
  94. package/dist/routes.js +271 -141
  95. package/dist/routes.js.map +1 -1
  96. package/dist/run.d.ts +36 -0
  97. package/dist/run.d.ts.map +1 -0
  98. package/dist/run.js +128 -0
  99. package/dist/run.js.map +1 -0
  100. package/dist/sessions.d.ts +50 -0
  101. package/dist/sessions.d.ts.map +1 -0
  102. package/dist/sessions.js +74 -0
  103. package/dist/sessions.js.map +1 -0
  104. package/dist/types.d.ts +118 -41
  105. package/dist/types.d.ts.map +1 -1
  106. package/dist/types.js +1 -0
  107. package/dist/types.js.map +1 -1
  108. package/dist/viewport.d.ts +6 -1
  109. package/dist/viewport.d.ts.map +1 -1
  110. package/dist/viewport.js +13 -7
  111. package/dist/viewport.js.map +1 -1
  112. package/package.json +21 -13
  113. package/skill/SKILL.md +31 -229
  114. package/dist/clipboard.d.ts +0 -9
  115. package/dist/clipboard.d.ts.map +0 -1
  116. package/dist/clipboard.js +0 -26
  117. package/dist/clipboard.js.map +0 -1
  118. package/dist/upload.d.ts +0 -39
  119. package/dist/upload.d.ts.map +0 -1
  120. package/dist/upload.js +0 -149
  121. package/dist/upload.js.map +0 -1
  122. package/skill/scripts/adapters/0x0st.sh +0 -36
  123. package/skill/scripts/adapters/blob.sh +0 -55
  124. package/skill/scripts/adapters/git-native.sh +0 -51
  125. package/skill/scripts/upload-and-copy.sh +0 -230
@@ -0,0 +1,36 @@
1
+ /**
2
+ * .pre-post.json — per-repo configuration. Optional; every field has a default.
3
+ * `resolveSettings` is the one place defaults, config, and CLI overrides meet.
4
+ */
5
+ import { PrePostConfig } from './types.js';
6
+ export declare const CONFIG_FILENAME = ".pre-post.json";
7
+ export declare const CONFIG_DEFAULTS: {
8
+ viewports: string[];
9
+ threshold: number;
10
+ minChangedPixels: number;
11
+ maxRoutes: number;
12
+ fullPage: boolean;
13
+ maxHeight: number;
14
+ scale: number;
15
+ assetsBranch: string;
16
+ pruneDays: number;
17
+ };
18
+ export interface Settings {
19
+ viewports: string[];
20
+ threshold: number;
21
+ minChangedPixels: number;
22
+ maxRoutes: number;
23
+ fullPage: boolean;
24
+ maxHeight: number;
25
+ scale: number;
26
+ assetsBranch: string;
27
+ pruneDays: number;
28
+ }
29
+ /** Defaults < .pre-post.json < explicit overrides (undefined overrides are ignored). */
30
+ export declare function resolveSettings(config?: PrePostConfig, overrides?: Partial<Settings>): Settings;
31
+ export declare function configPath(repoRoot: string): string;
32
+ export declare function loadConfig(repoRoot: string): PrePostConfig;
33
+ export declare function saveConfig(repoRoot: string, config: PrePostConfig): void;
34
+ /** Merge a partial update into the on-disk config (creating it if needed). */
35
+ export declare function updateConfig(repoRoot: string, patch: Partial<PrePostConfig>): PrePostConfig;
36
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAEhD,eAAO,MAAM,eAAe;eACU,MAAM,EAAE;;;;;;;;;CAS7C,CAAC;AAEF,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,MAAM,GAAE,aAAkB,EAAE,SAAS,GAAE,OAAO,CAAC,QAAQ,CAAM,GAAG,QAAQ,CAcvG;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAU1D;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAExE;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,CAI3F"}
package/dist/config.js ADDED
@@ -0,0 +1,60 @@
1
+ /**
2
+ * .pre-post.json — per-repo configuration. Optional; every field has a default.
3
+ * `resolveSettings` is the one place defaults, config, and CLI overrides meet.
4
+ */
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ export const CONFIG_FILENAME = '.pre-post.json';
8
+ export const CONFIG_DEFAULTS = {
9
+ viewports: ['desktop', 'mobile'],
10
+ threshold: 0.001,
11
+ minChangedPixels: 40,
12
+ maxRoutes: 6,
13
+ fullPage: true,
14
+ maxHeight: 2400,
15
+ scale: 2,
16
+ assetsBranch: 'pre-post-assets',
17
+ pruneDays: 90,
18
+ };
19
+ /** Defaults < .pre-post.json < explicit overrides (undefined overrides are ignored). */
20
+ export function resolveSettings(config = {}, overrides = {}) {
21
+ const pick = (key) => (overrides[key] ?? config[key] ?? CONFIG_DEFAULTS[key]);
22
+ return {
23
+ viewports: pick('viewports'),
24
+ threshold: pick('threshold'),
25
+ minChangedPixels: pick('minChangedPixels'),
26
+ maxRoutes: pick('maxRoutes'),
27
+ fullPage: pick('fullPage'),
28
+ maxHeight: pick('maxHeight'),
29
+ scale: pick('scale'),
30
+ assetsBranch: pick('assetsBranch'),
31
+ pruneDays: pick('pruneDays'),
32
+ };
33
+ }
34
+ export function configPath(repoRoot) {
35
+ return path.join(repoRoot, CONFIG_FILENAME);
36
+ }
37
+ export function loadConfig(repoRoot) {
38
+ const file = configPath(repoRoot);
39
+ if (!fs.existsSync(file))
40
+ return {};
41
+ try {
42
+ const parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));
43
+ if (parsed && typeof parsed === 'object')
44
+ return parsed;
45
+ return {};
46
+ }
47
+ catch (err) {
48
+ throw new Error(`${CONFIG_FILENAME} is not valid JSON: ${err.message}`);
49
+ }
50
+ }
51
+ export function saveConfig(repoRoot, config) {
52
+ fs.writeFileSync(configPath(repoRoot), JSON.stringify(config, null, 2) + '\n');
53
+ }
54
+ /** Merge a partial update into the on-disk config (creating it if needed). */
55
+ export function updateConfig(repoRoot, patch) {
56
+ const next = { ...loadConfig(repoRoot), ...patch };
57
+ saveConfig(repoRoot, next);
58
+ return next;
59
+ }
60
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,CAAC,MAAM,eAAe,GAAG,gBAAgB,CAAC;AAEhD,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,SAAS,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAa;IAC5C,SAAS,EAAE,KAAK;IAChB,gBAAgB,EAAE,EAAE;IACpB,SAAS,EAAE,CAAC;IACZ,QAAQ,EAAE,IAAI;IACd,SAAS,EAAE,IAAI;IACf,KAAK,EAAE,CAAC;IACR,YAAY,EAAE,iBAAiB;IAC/B,SAAS,EAAE,EAAE;CACd,CAAC;AAcF,wFAAwF;AACxF,MAAM,UAAU,eAAe,CAAC,SAAwB,EAAE,EAAE,YAA+B,EAAE;IAC3F,MAAM,IAAI,GAAG,CAA2B,GAAM,EAAe,EAAE,CAC7D,CAAC,SAAS,CAAC,GAAG,CAAC,IAAK,MAA4B,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,CAAgB,CAAC;IAChG,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC;QAC5B,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC;QAC5B,gBAAgB,EAAE,IAAI,CAAC,kBAAkB,CAAC;QAC1C,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC;QAC5B,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC;QAC1B,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC;QAC5B,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC;QACpB,YAAY,EAAE,IAAI,CAAC,cAAc,CAAC;QAClC,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC;KAC7B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QAC1D,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,MAAuB,CAAC;QACzE,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,GAAG,eAAe,uBAAwB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IACrF,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,MAAqB;IAChE,EAAE,CAAC,aAAa,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACjF,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,KAA6B;IAC1E,MAAM,IAAI,GAAG,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC;IACnD,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC3B,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * A small worker pool for pixel diffs. Runs inline when worker threads are
3
+ * unavailable (e.g. executing from TypeScript sources under a test runner).
4
+ */
5
+ import { DiffJob, DiffJobResult } from './diff-worker.js';
6
+ export declare class DiffPool {
7
+ private readonly size;
8
+ private workers;
9
+ private queue;
10
+ private inline;
11
+ constructor(size?: number);
12
+ run(job: DiffJob): Promise<DiffJobResult>;
13
+ private spawn;
14
+ private pump;
15
+ close(): Promise<void>;
16
+ }
17
+ //# sourceMappingURL=diff-pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-pool.d.ts","sourceRoot":"","sources":["../src/diff-pool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,OAAO,EAAE,aAAa,EAAkB,MAAM,kBAAkB,CAAC;AAa1E,qBAAa,QAAQ;IAKP,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJjC,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,MAAM,CAAS;gBAEM,IAAI,SAAiD;IAElF,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC;IAOzC,OAAO,CAAC,KAAK;IA8Bb,OAAO,CAAC,IAAI;IAmBN,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * A small worker pool for pixel diffs. Runs inline when worker threads are
3
+ * unavailable (e.g. executing from TypeScript sources under a test runner).
4
+ */
5
+ import os from 'os';
6
+ import { Worker } from 'worker_threads';
7
+ import { executeDiffJob } from './diff-worker.js';
8
+ function runInline(p) {
9
+ try {
10
+ p.resolve(executeDiffJob(p.job));
11
+ }
12
+ catch (e) {
13
+ p.reject(e);
14
+ }
15
+ }
16
+ export class DiffPool {
17
+ size;
18
+ workers = [];
19
+ queue = [];
20
+ inline = false;
21
+ constructor(size = Math.max(1, Math.min(4, os.cpus().length - 1))) {
22
+ this.size = size;
23
+ }
24
+ run(job) {
25
+ return new Promise((resolve, reject) => {
26
+ this.queue.push({ job, resolve, reject });
27
+ this.pump();
28
+ });
29
+ }
30
+ spawn() {
31
+ try {
32
+ const worker = new Worker(new URL('./diff-worker.js', import.meta.url));
33
+ const slot = { worker, busy: null };
34
+ worker.on('message', (msg) => {
35
+ const pending = slot.busy;
36
+ slot.busy = null;
37
+ if (pending) {
38
+ if (msg.error)
39
+ pending.reject(new Error(msg.error));
40
+ else
41
+ pending.resolve(msg);
42
+ }
43
+ this.pump();
44
+ });
45
+ worker.on('error', () => {
46
+ // The worker died: finish its job inline and stop using workers if none are left.
47
+ const pending = slot.busy;
48
+ slot.busy = null;
49
+ this.workers = this.workers.filter(w => w !== slot);
50
+ if (pending)
51
+ runInline(pending);
52
+ if (this.workers.length === 0)
53
+ this.inline = true;
54
+ this.pump();
55
+ });
56
+ this.workers.push(slot);
57
+ return slot;
58
+ }
59
+ catch {
60
+ this.inline = true;
61
+ return null;
62
+ }
63
+ }
64
+ pump() {
65
+ while (this.queue.length) {
66
+ if (this.inline) {
67
+ runInline(this.queue.shift());
68
+ continue;
69
+ }
70
+ let slot = this.workers.find(w => !w.busy);
71
+ if (!slot && this.workers.length < this.size)
72
+ slot = this.spawn() ?? undefined;
73
+ if (!slot) {
74
+ if (this.inline)
75
+ continue; // spawn failed: drain inline
76
+ return; // all workers busy: wait for a message
77
+ }
78
+ const pending = this.queue.shift();
79
+ slot.busy = pending;
80
+ const { before, after } = pending.job;
81
+ slot.worker.postMessage(pending.job, [before.buffer, after.buffer]);
82
+ }
83
+ }
84
+ async close() {
85
+ await Promise.all(this.workers.map(w => w.worker.terminate()));
86
+ this.workers = [];
87
+ }
88
+ }
89
+ //# sourceMappingURL=diff-pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-pool.js","sourceRoot":"","sources":["../src/diff-pool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Worker-thread entry: diff two PNGs held in memory and write every output.
3
+ * Keeps pixel work and file I/O off the main thread so captures keep flowing.
4
+ */
5
+ import { DiffOptions } from './diff.js';
6
+ export interface DiffJob {
7
+ /** PNG bytes; transferred to the worker, so the caller must not reuse them. */
8
+ before: Uint8Array;
9
+ after: Uint8Array;
10
+ /** Where to write pre, post, diff, and (when produced) crops. */
11
+ outputs: {
12
+ before: string;
13
+ after: string;
14
+ diff: string;
15
+ cropBefore: string;
16
+ cropAfter: string;
17
+ };
18
+ options?: DiffOptions;
19
+ }
20
+ export interface DiffJobResult {
21
+ changedRatio: number;
22
+ changedPixels: number;
23
+ sizeChanged: boolean;
24
+ hasCrop: boolean;
25
+ hasHighlight: boolean;
26
+ error?: string;
27
+ }
28
+ export declare function executeDiffJob(job: DiffJob): DiffJobResult;
29
+ //# sourceMappingURL=diff-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-worker.d.ts","sourceRoot":"","sources":["../src/diff-worker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAc,WAAW,EAAE,MAAM,WAAW,CAAC;AAEpD,MAAM,WAAW,OAAO;IACtB,+EAA+E;IAC/E,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;IAClB,iEAAiE;IACjE,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAChG,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CAkB1D"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Worker-thread entry: diff two PNGs held in memory and write every output.
3
+ * Keeps pixel work and file I/O off the main thread so captures keep flowing.
4
+ */
5
+ import fs from 'fs';
6
+ import { parentPort } from 'worker_threads';
7
+ import { diffImages } from './diff.js';
8
+ export function executeDiffJob(job) {
9
+ const before = Buffer.from(job.before.buffer, job.before.byteOffset, job.before.byteLength);
10
+ const after = Buffer.from(job.after.buffer, job.after.byteOffset, job.after.byteLength);
11
+ fs.writeFileSync(job.outputs.before, before);
12
+ fs.writeFileSync(job.outputs.after, after);
13
+ const result = diffImages(before, after, job.options);
14
+ if (result.highlight)
15
+ fs.writeFileSync(job.outputs.diff, result.highlight);
16
+ if (result.crop) {
17
+ fs.writeFileSync(job.outputs.cropBefore, result.crop.before);
18
+ fs.writeFileSync(job.outputs.cropAfter, result.crop.after);
19
+ }
20
+ return {
21
+ changedRatio: result.changedRatio,
22
+ changedPixels: result.changedPixels,
23
+ sizeChanged: result.sizeChanged,
24
+ hasCrop: Boolean(result.crop),
25
+ hasHighlight: Boolean(result.highlight),
26
+ };
27
+ }
28
+ if (parentPort) {
29
+ parentPort.on('message', (job) => {
30
+ try {
31
+ parentPort.postMessage(executeDiffJob(job));
32
+ }
33
+ catch (err) {
34
+ parentPort.postMessage({ error: err.message });
35
+ }
36
+ });
37
+ }
38
+ //# sourceMappingURL=diff-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-worker.js","sourceRoot":"","sources":["../src/diff-worker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAe,MAAM,WAAW,CAAC;AAoBpD,MAAM,UAAU,cAAc,CAAC,GAAY;IACzC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC5F,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACxF,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACtD,IAAI,MAAM,CAAC,SAAS;QAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAC3E,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7D,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO;QACL,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;QAC7B,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;KACxC,CAAC;AACJ,CAAC;AAED,IAAI,UAAU,EAAE,CAAC;IACf,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAY,EAAE,EAAE;QACxC,IAAI,CAAC;YACH,UAAW,CAAC,WAAW,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,UAAW,CAAC,WAAW,CAAC,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAmB,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
package/dist/diff.d.ts ADDED
@@ -0,0 +1,37 @@
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 { PNG } from 'pngjs';
9
+ import { DiffRegion, DiffResult } from './types.js';
10
+ export interface DiffOptions {
11
+ /** Padding around the change region for crops, in device pixels. Default 80 */
12
+ padding?: number;
13
+ /** Minimum crop size in device pixels. Default 800x400 */
14
+ minCrop?: {
15
+ width: number;
16
+ height: number;
17
+ };
18
+ /** Integer factor to shrink the highlight image by (e.g. 2 for 2x captures). Default 1 */
19
+ highlightDownscale?: number;
20
+ }
21
+ /** Box-filter downscale by an integer factor. */
22
+ export declare function downscale(src: PNG, factor: number): PNG;
23
+ export declare function decodePng(buffer: Buffer): PNG;
24
+ /** Expand a region by padding and to a minimum size, clamped to the canvas. */
25
+ export declare function expandRegion(region: DiffRegion, canvas: {
26
+ width: number;
27
+ height: number;
28
+ }, padding: number, min: {
29
+ width: number;
30
+ height: number;
31
+ }): DiffRegion;
32
+ /**
33
+ * Compare two PNG buffers. Images of different sizes are padded onto a shared
34
+ * canvas so a height change shows up as a change rather than an error.
35
+ */
36
+ export declare function diffImages(beforePng: Buffer, afterPng: Buffer, options?: DiffOptions): DiffResult;
37
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,0FAA0F;IAC1F,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AA+BD,iDAAiD;AACjD,wBAAgB,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,CAoBvD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAE7C;AAsBD,+EAA+E;AAC/E,wBAAgB,YAAY,CAC1B,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACzC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrC,UAAU,CAqBZ;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,UAAU,CAsDrG"}
package/dist/diff.js ADDED
@@ -0,0 +1,171 @@
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
+ const DIFF_COLOR = [255, 0, 0];
11
+ const AA_COLOR = [255, 200, 0];
12
+ /** Background used to pad images of different sizes. */
13
+ const PAD_COLOR = [255, 255, 255];
14
+ /** pixelmatch per-pixel color distance (0..1). */
15
+ const PIXEL_THRESHOLD = 0.1;
16
+ /** No crop when the change covers more than this fraction of the canvas. */
17
+ const CROP_MAX_RATIO = 0.5;
18
+ function padTo(src, width, height, bg) {
19
+ if (src.width === width && src.height === height)
20
+ return src;
21
+ const out = new PNG({ width, height });
22
+ for (let i = 0; i < out.data.length; i += 4) {
23
+ out.data[i] = bg[0];
24
+ out.data[i + 1] = bg[1];
25
+ out.data[i + 2] = bg[2];
26
+ out.data[i + 3] = 255;
27
+ }
28
+ PNG.bitblt(src, out, 0, 0, src.width, src.height, 0, 0);
29
+ return out;
30
+ }
31
+ function crop(src, region) {
32
+ const out = new PNG({ width: region.width, height: region.height });
33
+ PNG.bitblt(src, out, region.x, region.y, region.width, region.height, 0, 0);
34
+ return encode(out);
35
+ }
36
+ function encode(png) {
37
+ return PNG.sync.write(png, { deflateLevel: 4, filterType: 4 });
38
+ }
39
+ /** Box-filter downscale by an integer factor. */
40
+ export function downscale(src, factor) {
41
+ if (factor <= 1)
42
+ return src;
43
+ const w = Math.max(1, Math.floor(src.width / factor));
44
+ const h = Math.max(1, Math.floor(src.height / factor));
45
+ const out = new PNG({ width: w, height: h });
46
+ const n = factor * factor;
47
+ for (let y = 0; y < h; y++) {
48
+ for (let x = 0; x < w; x++) {
49
+ let r = 0, g = 0, b = 0, a = 0;
50
+ for (let dy = 0; dy < factor; dy++) {
51
+ let i = ((y * factor + dy) * src.width + x * factor) * 4;
52
+ for (let dx = 0; dx < factor; dx++, i += 4) {
53
+ r += src.data[i];
54
+ g += src.data[i + 1];
55
+ b += src.data[i + 2];
56
+ a += src.data[i + 3];
57
+ }
58
+ }
59
+ const o = (y * w + x) * 4;
60
+ out.data[o] = r / n;
61
+ out.data[o + 1] = g / n;
62
+ out.data[o + 2] = b / n;
63
+ out.data[o + 3] = a / n;
64
+ }
65
+ }
66
+ return out;
67
+ }
68
+ export function decodePng(buffer) {
69
+ return PNG.sync.read(buffer);
70
+ }
71
+ /** Bounding box of pixels painted with the diff color. */
72
+ function boundingBox(diff) {
73
+ const { width, height, data } = diff;
74
+ let minX = width, minY = height, maxX = -1, maxY = -1;
75
+ for (let y = 0; y < height; y++) {
76
+ const row = y * width * 4;
77
+ for (let x = 0; x < width; x++) {
78
+ const i = row + x * 4;
79
+ if (data[i] === DIFF_COLOR[0] && data[i + 1] === DIFF_COLOR[1] && data[i + 2] === DIFF_COLOR[2]) {
80
+ if (x < minX)
81
+ minX = x;
82
+ if (x > maxX)
83
+ maxX = x;
84
+ if (y < minY)
85
+ minY = y;
86
+ if (y > maxY)
87
+ maxY = y;
88
+ }
89
+ }
90
+ }
91
+ if (maxX < 0)
92
+ return null;
93
+ return { x: minX, y: minY, width: maxX - minX + 1, height: maxY - minY + 1 };
94
+ }
95
+ /** Expand a region by padding and to a minimum size, clamped to the canvas. */
96
+ export function expandRegion(region, canvas, padding, min) {
97
+ let x0 = region.x - padding;
98
+ let y0 = region.y - padding;
99
+ let x1 = region.x + region.width + padding;
100
+ let y1 = region.y + region.height + padding;
101
+ const grow = (lo, hi, target, limit) => {
102
+ const size = hi - lo;
103
+ if (size < target) {
104
+ const extra = target - size;
105
+ lo -= Math.floor(extra / 2);
106
+ hi += Math.ceil(extra / 2);
107
+ }
108
+ if (lo < 0) {
109
+ hi -= lo;
110
+ lo = 0;
111
+ }
112
+ if (hi > limit) {
113
+ lo -= hi - limit;
114
+ hi = limit;
115
+ }
116
+ return [Math.max(0, lo), Math.min(limit, hi)];
117
+ };
118
+ [x0, x1] = grow(x0, x1, Math.min(min.width, canvas.width), canvas.width);
119
+ [y0, y1] = grow(y0, y1, Math.min(min.height, canvas.height), canvas.height);
120
+ return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
121
+ }
122
+ /**
123
+ * Compare two PNG buffers. Images of different sizes are padded onto a shared
124
+ * canvas so a height change shows up as a change rather than an error.
125
+ */
126
+ export function diffImages(beforePng, afterPng, options = {}) {
127
+ const bg = PAD_COLOR;
128
+ const a = decodePng(beforePng);
129
+ const b = decodePng(afterPng);
130
+ const width = Math.max(a.width, b.width);
131
+ const height = Math.max(a.height, b.height);
132
+ const sizeChanged = a.width !== b.width || a.height !== b.height;
133
+ const before = padTo(a, width, height, bg);
134
+ const after = padTo(b, width, height, bg);
135
+ const diff = new PNG({ width, height });
136
+ const changedPixels = pixelmatch(before.data, after.data, diff.data, width, height, {
137
+ threshold: PIXEL_THRESHOLD,
138
+ includeAA: false,
139
+ alpha: 0.5,
140
+ diffColor: DIFF_COLOR,
141
+ aaColor: AA_COLOR,
142
+ diffMask: false,
143
+ });
144
+ const region = changedPixels > 0 ? boundingBox(diff) : null;
145
+ const result = {
146
+ changedRatio: changedPixels / (width * height),
147
+ changedPixels,
148
+ width,
149
+ height,
150
+ region,
151
+ sizeChanged,
152
+ // Identical pages need no highlight; skip the downscale + deflate.
153
+ highlight: region ? encode(downscale(diff, options.highlightDownscale ?? 1)) : undefined,
154
+ };
155
+ if (region) {
156
+ const area = region.width * region.height;
157
+ if (area / (width * height) <= CROP_MAX_RATIO) {
158
+ const expanded = expandRegion(region, { width, height }, options.padding ?? 80, options.minCrop ?? { width: 800, height: 400 });
159
+ // Only crop when it meaningfully zooms in.
160
+ if (expanded.width * expanded.height < width * height * 0.8) {
161
+ result.crop = {
162
+ before: crop(before, expanded),
163
+ after: crop(after, expanded),
164
+ region: expanded,
165
+ };
166
+ }
167
+ }
168
+ }
169
+ return result;
170
+ }
171
+ //# 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;AAY5B,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,kDAAkD;AAClD,MAAM,eAAe,GAAG,GAAG,CAAC;AAC5B,4EAA4E;AAC5E,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,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,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,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,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,kBAAkB,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;KACzF,CAAC;IAEF,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;QAC1C,IAAI,IAAI,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,cAAc,EAAE,CAAC;YAC9C,MAAM,QAAQ,GAAG,YAAY,CAC3B,MAAM,EACN,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,MAAM,EAAE,QAAQ,CAAC;oBAC9B,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,38 @@
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
+ * Find a running local dev server. Returns its base URL or null.
17
+ */
18
+ export declare function detectDevServer(ports?: number[]): Promise<string | null>;
19
+ export interface ProbeResult {
20
+ /** HTTP status, or null when the host could not be reached at all */
21
+ status: number | null;
22
+ vercel: boolean;
23
+ }
24
+ /**
25
+ * Probe a base URL with the headers we will use for capture.
26
+ */
27
+ export declare function probeUrl(url: string, headers?: Record<string, string>, options?: {
28
+ timeoutMs?: number;
29
+ redirect?: RequestRedirect;
30
+ }): Promise<ProbeResult>;
31
+ /**
32
+ * Explain a 401/403 in one actionable sentence. Accepts a probe result or the typed capture error.
33
+ */
34
+ export declare function authHint(target: HttpStatusError | {
35
+ url: string;
36
+ vercel: boolean;
37
+ }): string;
38
+ //# 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;AAID;;GAEG;AACH,wBAAsB,eAAe,CAAC,KAAK,GAAE,MAAM,EAAc,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIzF;AAED,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;CACjB;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,CAOtB;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,72 @@
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 './run.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
+ const DEV_PORTS = [3000, 3001, 3002, 5173, 5174, 4173, 4321, 8080, 8000, 5000, 4200];
41
+ /**
42
+ * Find a running local dev server. Returns its base URL or null.
43
+ */
44
+ export async function detectDevServer(ports = DEV_PORTS) {
45
+ const results = await Promise.all(ports.map(async (p) => ({ p, ...(await probeUrl(`http://localhost:${p}/`, {}, { timeoutMs: 1500, redirect: 'manual' })) })));
46
+ const hit = results.find(r => r.status !== null);
47
+ return hit ? `http://localhost:${hit.p}` : null;
48
+ }
49
+ /**
50
+ * Probe a base URL with the headers we will use for capture.
51
+ */
52
+ export async function probeUrl(url, headers = {}, options = {}) {
53
+ try {
54
+ const res = await fetch(url, { method: 'GET', headers, redirect: options.redirect ?? 'follow', signal: AbortSignal.timeout(options.timeoutMs ?? 10_000) });
55
+ return { status: res.status, vercel: isVercelResponse(res.headers) };
56
+ }
57
+ catch {
58
+ return { status: null, vercel: false };
59
+ }
60
+ }
61
+ /**
62
+ * Explain a 401/403 in one actionable sentence. Accepts a probe result or the typed capture error.
63
+ */
64
+ export function authHint(target) {
65
+ const { url, vercel } = target;
66
+ const host = hostOf(url);
67
+ if (vercel || host.endsWith('.vercel.app')) {
68
+ 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}`;
69
+ }
70
+ return `${host} requires a login. Run: npx pre-post login ${url} (opens a browser once, saves the session locally)`;
71
+ }
72
+ //# sourceMappingURL=doctor.js.map