pursr 0.10.0 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -19
- package/SKILL.md +169 -0
- package/bin/pursr.mjs +111 -86
- package/package.json +5 -1
- package/src/browser-discovery.js +196 -0
- package/src/cli-args.js +33 -0
- package/src/diff.js +3 -2
- package/src/doctor.js +88 -0
- package/src/eval.js +16 -10
- package/src/hover.js +8 -3
- package/src/interact.js +20 -14
- package/src/runway.js +40 -42
- package/src/shoot.js +7 -4
- package/src/shot.js +14 -10
- package/src/update-notifier.js +74 -0
package/src/shot.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
// Simple screenshot (no flags / overlays).
|
|
2
2
|
|
|
3
3
|
import { launch, newPage } from "./runway.js";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveViewport } from "./viewport.js";
|
|
5
5
|
import { gotoOrThrow, settle } from "./overlays.js";
|
|
6
|
-
import { requireArg } from "./util.js";
|
|
6
|
+
import { requireArg } from "./util.js";
|
|
7
|
+
import { mkdirSync } from "node:fs";
|
|
8
|
+
import { dirname } from "node:path";
|
|
7
9
|
|
|
8
|
-
export async function runShot(url, out, opts = {}) {
|
|
9
|
-
requireArg("url", url, "string");
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
export async function runShot(url, out, opts = {}) {
|
|
11
|
+
requireArg("url", url, "string");
|
|
12
|
+
const viewport = resolveViewport(opts);
|
|
13
|
+
const browser = await launch();
|
|
14
|
+
try {
|
|
15
|
+
const page = await newPage(browser, viewport);
|
|
13
16
|
const r = await gotoOrThrow(page, url);
|
|
14
17
|
await settle(page);
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
if (out) mkdirSync(dirname(out), { recursive: true });
|
|
19
|
+
await page.screenshot({ path: out, fullPage: !!opts.fullPage });
|
|
20
|
+
return { ...r, url, out, viewport, fullPage: !!opts.fullPage };
|
|
17
21
|
} finally { try { await browser.close(); } catch {} }
|
|
18
|
-
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { request as httpsRequest } from "node:https";
|
|
5
|
+
|
|
6
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
7
|
+
|
|
8
|
+
export function compareVersions(a, b) {
|
|
9
|
+
const pa = String(a || "0").split(".").map((x) => Number.parseInt(x, 10) || 0);
|
|
10
|
+
const pb = String(b || "0").split(".").map((x) => Number.parseInt(x, 10) || 0);
|
|
11
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
12
|
+
const da = pa[i] || 0;
|
|
13
|
+
const db = pb[i] || 0;
|
|
14
|
+
if (da > db) return 1;
|
|
15
|
+
if (da < db) return -1;
|
|
16
|
+
}
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function updateCachePath(env = process.env) {
|
|
21
|
+
const root = env.PURSR_HOME || join(homedir(), ".pursr");
|
|
22
|
+
return join(root, "update-check.json");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function shouldCheckForUpdate({ env = process.env, stdout = process.stdout, stderr = process.stderr, now = Date.now(), cachePath = updateCachePath(env) } = {}) {
|
|
26
|
+
if (env.PURSR_NO_UPDATE_NOTIFIER || env.NO_UPDATE_NOTIFIER || env.CI) return false;
|
|
27
|
+
if (!stderr?.isTTY || !stdout?.isTTY) return false;
|
|
28
|
+
try {
|
|
29
|
+
if (!existsSync(cachePath)) return true;
|
|
30
|
+
const cached = JSON.parse(readFileSync(cachePath, "utf8"));
|
|
31
|
+
return !cached.checkedAt || now - Date.parse(cached.checkedAt) > DAY_MS;
|
|
32
|
+
} catch {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function fetchLatestVersion({ registryUrl = "https://registry.npmjs.org/pursr/latest", timeoutMs = 800 } = {}) {
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
const req = httpsRequest(registryUrl, { method: "GET", timeout: timeoutMs, headers: { accept: "application/json" } }, (res) => {
|
|
40
|
+
let body = "";
|
|
41
|
+
res.setEncoding("utf8");
|
|
42
|
+
res.on("data", (chunk) => { body += chunk; });
|
|
43
|
+
res.on("end", () => {
|
|
44
|
+
try {
|
|
45
|
+
const json = JSON.parse(body);
|
|
46
|
+
resolve(json.version || null);
|
|
47
|
+
} catch {
|
|
48
|
+
resolve(null);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
req.on("timeout", () => { req.destroy(); resolve(null); });
|
|
53
|
+
req.on("error", () => resolve(null));
|
|
54
|
+
req.end();
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function checkForUpdate({ currentVersion, env = process.env, stdout = process.stdout, stderr = process.stderr, now = Date.now(), cachePath = updateCachePath(env), fetchLatest = fetchLatestVersion } = {}) {
|
|
59
|
+
if (!shouldCheckForUpdate({ env, stdout, stderr, now, cachePath })) return null;
|
|
60
|
+
const latest = await fetchLatest();
|
|
61
|
+
try {
|
|
62
|
+
mkdirSync(dirname(cachePath), { recursive: true });
|
|
63
|
+
writeFileSync(cachePath, JSON.stringify({ checkedAt: new Date(now).toISOString(), latest }, null, 2), "utf8");
|
|
64
|
+
} catch {}
|
|
65
|
+
if (!latest || compareVersions(latest, currentVersion) <= 0) return null;
|
|
66
|
+
return { current: currentVersion, latest, command: "npm i -g pursr@latest" };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function notifyUpdate(options = {}) {
|
|
70
|
+
const result = await checkForUpdate(options);
|
|
71
|
+
if (!result) return null;
|
|
72
|
+
options.stderr?.write?.(`\npursr update available: ${result.current} -> ${result.latest}\nRun: ${result.command}\n\n`);
|
|
73
|
+
return result;
|
|
74
|
+
}
|