@mattfillipe/diffler 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # diffler
2
+
3
+ Terminal code review for AI coding agents. This package installs a launcher
4
+ that runs the native binary from the matching `@diffler/<platform>` package.
5
+
6
+ ```sh
7
+ npm install -g diffler
8
+ diffler
9
+ ```
10
+
11
+ Docs: https://github.com/matheusfillipe/diffler
package/bin/diffler.js ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // Launcher shim: resolve the platform binary (fetching it on first run if the
5
+ // postinstall step was skipped), then exec it with the caller's args/stdio.
6
+
7
+ const { spawnSync } = require("node:child_process");
8
+ const { ensureBinary } = require("../lib/resolve.js");
9
+
10
+ async function main() {
11
+ let binary;
12
+ try {
13
+ binary = await ensureBinary();
14
+ } catch (err) {
15
+ process.stderr.write(`diffler: ${err.message}\n`);
16
+ process.exit(1);
17
+ }
18
+ const result = spawnSync(binary, process.argv.slice(2), { stdio: "inherit" });
19
+ if (result.error) {
20
+ process.stderr.write(`diffler: ${result.error.message}\n`);
21
+ process.exit(1);
22
+ }
23
+ process.exit(result.status === null ? 1 : result.status);
24
+ }
25
+
26
+ main();
package/install.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+
3
+ // postinstall: fetch the platform binary up front. A failure here is not fatal
4
+ // — the launcher shim downloads it lazily on first run, so `npm install
5
+ // --ignore-scripts` and offline-then-online still work.
6
+
7
+ const { ensureBinary } = require("./lib/resolve.js");
8
+
9
+ ensureBinary().catch((err) => {
10
+ process.stderr.write(
11
+ `diffler: could not prefetch the binary (${err.message}); ` +
12
+ "it will be fetched on first run.\n",
13
+ );
14
+ });
package/lib/resolve.js ADDED
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+
3
+ // Resolve (and, if missing, fetch) the diffler binary for the current
4
+ // platform. The npm package ships only this JS; the actual binary is pulled
5
+ // from the matching GitHub release asset on install (and lazily on first run,
6
+ // so `--ignore-scripts` installs still work).
7
+
8
+ const fs = require("node:fs");
9
+ const os = require("node:os");
10
+ const path = require("node:path");
11
+ const http = require("node:http");
12
+ const https = require("node:https");
13
+ const { execFileSync } = require("node:child_process");
14
+
15
+ const REPO = "matheusfillipe/diffler";
16
+ const BASE =
17
+ process.env.DIFFLER_DOWNLOAD_BASE ||
18
+ `https://github.com/${REPO}/releases/download`;
19
+
20
+ // node platform-arch -> { rust target triple, archive extension }
21
+ const TARGETS = {
22
+ "darwin-arm64": { target: "aarch64-apple-darwin", ext: "tar.gz" },
23
+ "darwin-x64": { target: "x86_64-apple-darwin", ext: "tar.gz" },
24
+ "linux-x64": { target: "x86_64-unknown-linux-musl", ext: "tar.gz" },
25
+ "linux-arm64": { target: "aarch64-unknown-linux-musl", ext: "tar.gz" },
26
+ "win32-x64": { target: "x86_64-pc-windows-msvc", ext: "zip" },
27
+ "win32-arm64": { target: "aarch64-pc-windows-msvc", ext: "zip" },
28
+ };
29
+
30
+ function binaryName() {
31
+ return process.platform === "win32" ? "diffler.exe" : "diffler";
32
+ }
33
+
34
+ function binaryPath() {
35
+ return path.join(__dirname, "..", "bin", binaryName());
36
+ }
37
+
38
+ function platformInfo() {
39
+ const key = `${process.platform}-${process.arch}`;
40
+ const info = TARGETS[key];
41
+ if (!info) {
42
+ throw new Error(
43
+ `unsupported platform ${key} (supported: ${Object.keys(TARGETS).join(", ")})`,
44
+ );
45
+ }
46
+ return info;
47
+ }
48
+
49
+ function assetUrl() {
50
+ const { target, ext } = platformInfo();
51
+ const version = require("../package.json").version;
52
+ const asset = `diffler-v${version}-${target}.${ext}`;
53
+ return { url: `${BASE}/v${version}/${asset}`, asset, ext };
54
+ }
55
+
56
+ function download(url, dest) {
57
+ return new Promise((resolve, reject) => {
58
+ const get = (current, redirects) => {
59
+ if (redirects > 5) {
60
+ reject(new Error("too many redirects"));
61
+ return;
62
+ }
63
+ const client = current.startsWith("http:") ? http : https;
64
+ client
65
+ .get(current, { headers: { "user-agent": "diffler-npm" } }, (res) => {
66
+ const { statusCode, headers } = res;
67
+ if (statusCode >= 300 && statusCode < 400 && headers.location) {
68
+ res.resume();
69
+ get(new URL(headers.location, current).toString(), redirects + 1);
70
+ return;
71
+ }
72
+ if (statusCode !== 200) {
73
+ res.resume();
74
+ reject(new Error(`HTTP ${statusCode} for ${current}`));
75
+ return;
76
+ }
77
+ const file = fs.createWriteStream(dest);
78
+ res.pipe(file);
79
+ file.on("finish", () => file.close(() => resolve()));
80
+ file.on("error", reject);
81
+ })
82
+ .on("error", reject);
83
+ };
84
+ get(url, 0);
85
+ });
86
+ }
87
+
88
+ function extract(archive, ext, into) {
89
+ fs.mkdirSync(into, { recursive: true });
90
+ if (ext === "zip") {
91
+ // Expand-Archive is built into Windows PowerShell; the only platform that
92
+ // ships a .zip asset is win32
93
+ execFileSync("powershell", [
94
+ "-NoProfile",
95
+ "-Command",
96
+ `Expand-Archive -LiteralPath '${archive}' -DestinationPath '${into}' -Force`,
97
+ ]);
98
+ } else {
99
+ execFileSync("tar", ["-xzf", archive, "-C", into]);
100
+ }
101
+ }
102
+
103
+ function findBinary(dir) {
104
+ const want = binaryName();
105
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
106
+ const full = path.join(dir, entry.name);
107
+ if (entry.isDirectory()) {
108
+ const hit = findBinary(full);
109
+ if (hit) return hit;
110
+ } else if (entry.name === want) {
111
+ return full;
112
+ }
113
+ }
114
+ return null;
115
+ }
116
+
117
+ async function ensureBinary() {
118
+ const dest = binaryPath();
119
+ if (fs.existsSync(dest)) return dest;
120
+
121
+ const { url, asset, ext } = assetUrl();
122
+ const tmpArchive = path.join(fs.mkdtempSync(path.join(os.tmpdir(), "diffler-")), asset);
123
+ await download(url, tmpArchive);
124
+
125
+ const extractDir = fs.mkdtempSync(path.join(os.tmpdir(), "diffler-"));
126
+ extract(tmpArchive, ext, extractDir);
127
+
128
+ const found = findBinary(extractDir);
129
+ if (!found) {
130
+ throw new Error(`binary ${binaryName()} not found inside ${asset}`);
131
+ }
132
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
133
+ fs.copyFileSync(found, dest);
134
+ fs.chmodSync(dest, 0o755);
135
+ return dest;
136
+ }
137
+
138
+ module.exports = { ensureBinary, binaryPath, assetUrl, platformInfo, findBinary, extract };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@mattfillipe/diffler",
3
+ "version": "0.1.0",
4
+ "description": "Terminal code review for AI coding agents",
5
+ "license": "(MIT OR Apache-2.0)",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/matheusfillipe/diffler.git"
9
+ },
10
+ "bin": {
11
+ "diffler": "bin/diffler.js"
12
+ },
13
+ "files": [
14
+ "bin/diffler.js",
15
+ "install.js",
16
+ "lib"
17
+ ],
18
+ "scripts": {
19
+ "postinstall": "node install.js"
20
+ },
21
+ "engines": {
22
+ "node": ">=18"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ }
27
+ }