@backendsystems/nibble 0.3.1 → 0.4.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 (2) hide show
  1. package/bin/install.js +23 -185
  2. package/package.json +4 -1
package/bin/install.js CHANGED
@@ -1,208 +1,46 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs');
4
- const os = require('os');
5
3
  const path = require('path');
6
- const https = require('https');
7
- const crypto = require('crypto');
8
- const { spawnSync } = require('child_process');
4
+ const BinWrapper = require('bin-wrapper');
9
5
 
10
6
  const PROJECT = 'nibble';
11
7
  const OWNER = 'backendsystems';
12
- const REPO = 'nibble';
13
8
  const ROOT = path.resolve(__dirname, '..');
14
9
  const VENDOR_DIR = path.join(ROOT, 'vendor');
15
10
  const PACKAGE_JSON = require(path.join(ROOT, 'package.json'));
16
11
 
17
- function mapPlatform() {
18
- const platformMap = {
19
- linux: 'linux',
20
- darwin: 'darwin',
21
- win32: 'windows',
22
- };
23
-
24
- const archMap = {
25
- x64: 'amd64',
26
- arm64: 'arm64',
27
- };
28
-
29
- const osName = platformMap[process.platform];
30
- const arch = archMap[process.arch];
31
- if (!osName || !arch) {
32
- throw new Error(`unsupported platform: ${process.platform}/${process.arch}`);
33
- }
34
-
35
- return { osName, arch };
36
- }
37
-
38
- function download(url, outFile) {
39
- return new Promise((resolve, reject) => {
40
- const req = https.get(url, (res) => {
41
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
42
- res.resume();
43
- return resolve(download(res.headers.location, outFile));
44
- }
45
-
46
- if (res.statusCode !== 200) {
47
- res.resume();
48
- return reject(new Error(`download failed (${res.statusCode}): ${url}`));
49
- }
50
-
51
- const file = fs.createWriteStream(outFile);
52
- res.pipe(file);
53
- file.on('finish', () => file.close(() => resolve()));
54
- file.on('error', reject);
55
- });
56
- req.on('error', reject);
57
- });
58
- }
59
-
60
- function downloadText(url) {
61
- return new Promise((resolve, reject) => {
62
- const req = https.get(url, (res) => {
63
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
64
- res.resume();
65
- return resolve(downloadText(res.headers.location));
66
- }
67
-
68
- if (res.statusCode !== 200) {
69
- res.resume();
70
- return reject(new Error(`download failed (${res.statusCode}): ${url}`));
71
- }
72
-
73
- let data = '';
74
- res.setEncoding('utf8');
75
- res.on('data', (chunk) => {
76
- data += chunk;
77
- });
78
- res.on('end', () => resolve(data));
79
- res.on('error', reject);
80
- });
81
- req.on('error', reject);
82
- });
83
- }
84
-
85
- function parseChecksums(text) {
86
- const checksums = new Map();
87
- for (const line of text.split(/\r?\n/)) {
88
- const trimmed = line.trim();
89
- if (!trimmed || trimmed.startsWith('#')) {
90
- continue;
12
+ function buildWrapper() {
13
+ const tag = `v${PACKAGE_JSON.version}`;
14
+ const base = `https://github.com/${OWNER}/${PROJECT}/releases/download/${tag}`;
15
+ const binName = process.platform === 'win32' ? `${PROJECT}.exe` : PROJECT;
16
+ const supportedOs = ['linux', 'darwin', 'win32'];
17
+ const supportedArch = ['x64', 'arm64'];
18
+
19
+ const wrapper = new BinWrapper({ skipCheck: true });
20
+ for (const os of supportedOs) {
21
+ let osTarget = os;
22
+ if (os === 'win32') {
23
+ osTarget = 'windows';
91
24
  }
92
25
 
93
- const match = trimmed.match(/^([a-fA-F0-9]{64})\s+\*?(.+)$/);
94
- if (!match) {
95
- continue;
96
- }
97
- checksums.set(match[2].trim(), match[1].toLowerCase());
98
- }
99
- return checksums;
100
- }
101
-
102
- function sha256File(filePath) {
103
- const hash = crypto.createHash('sha256');
104
- hash.update(fs.readFileSync(filePath));
105
- return hash.digest('hex');
106
- }
107
-
108
- async function verifyArchiveChecksum(urlBase, archivePath, archiveName, version) {
109
- const checksumCandidates = [
110
- 'checksums.txt',
111
- `${PROJECT}_${version}_checksums.txt`,
112
- ];
113
-
114
- let checksumFile = '';
115
- let checksumName = '';
116
- for (const candidate of checksumCandidates) {
117
- try {
118
- checksumFile = await downloadText(`${urlBase}/${candidate}`);
119
- checksumName = candidate;
120
- break;
121
- } catch (err) {
122
- if (!String(err.message).includes('download failed (404)')) {
123
- throw err;
26
+ for (const arch of supportedArch) {
27
+ let archTarget = arch;
28
+ if (arch === 'x64') {
29
+ archTarget = 'amd64';
124
30
  }
125
- }
126
- }
127
-
128
- if (!checksumFile) {
129
- throw new Error(`no checksum file found (tried: ${checksumCandidates.join(', ')})`);
130
- }
131
31
 
132
- const checksums = parseChecksums(checksumFile);
133
- const expected = checksums.get(archiveName);
134
- if (!expected) {
135
- throw new Error(`checksum for ${archiveName} not found in ${checksumName}`);
136
- }
137
-
138
- const actual = sha256File(archivePath);
139
- if (actual !== expected) {
140
- throw new Error(`checksum mismatch for ${archiveName}`);
141
- }
142
- }
143
-
144
- function run(cmd, args) {
145
- const result = spawnSync(cmd, args, { stdio: 'inherit' });
146
- if (result.status !== 0) {
147
- throw new Error(`command failed: ${cmd} ${args.join(' ')}`);
148
- }
149
- }
150
-
151
- function installFromArchive(archivePath, osName) {
152
- fs.mkdirSync(VENDOR_DIR, { recursive: true });
153
-
154
- const binName = osName === 'windows' ? `${PROJECT}.exe` : PROJECT;
155
- const destBinary = path.join(VENDOR_DIR, binName);
156
-
157
- if (archivePath.endsWith('.zip')) {
158
- if (process.platform === 'win32') {
159
- run('powershell', [
160
- '-NoProfile',
161
- '-Command',
162
- `Expand-Archive -Path \"${archivePath}\" -DestinationPath \"${VENDOR_DIR}\" -Force`,
163
- ]);
164
- } else {
165
- run('unzip', ['-o', archivePath, '-d', VENDOR_DIR]);
32
+ wrapper.src(`${base}/${PROJECT}_${osTarget}_${archTarget}.tar.gz`, os, arch);
166
33
  }
167
- } else {
168
- run('tar', ['-xzf', archivePath, '-C', VENDOR_DIR]);
169
- }
170
-
171
- if (!fs.existsSync(destBinary)) {
172
- throw new Error(`binary not found after extraction: ${destBinary}`);
173
34
  }
174
35
 
175
- if (process.platform !== 'win32') {
176
- fs.chmodSync(destBinary, 0o755);
177
- }
36
+ return wrapper.dest(VENDOR_DIR).use(binName);
178
37
  }
179
38
 
180
39
  async function main() {
181
- const { osName, arch } = mapPlatform();
182
- const pkgVersion = PACKAGE_JSON.version.replace(/^v/, '');
183
- const tag = `v${pkgVersion}`;
184
- const assetBase = `${PROJECT}_${osName}_${arch}`;
185
- const ext = 'tar.gz';
186
- const asset = `${assetBase}.${ext}`;
187
- const urlBase = `https://github.com/${OWNER}/${REPO}/releases/download/${tag}`;
188
- const url = `${urlBase}/${asset}`;
189
-
190
- const cacheDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nibble-npm-'));
191
- const archivePath = path.join(cacheDir, asset);
192
-
193
- try {
194
- console.log(`Downloading ${asset} from ${url}`);
195
- await download(url, archivePath);
196
- await verifyArchiveChecksum(urlBase, archivePath, asset, pkgVersion);
197
- installFromArchive(archivePath, osName);
198
- console.log('Installed nibble binary successfully');
199
- } finally {
200
- if (fs.rmSync) {
201
- fs.rmSync(cacheDir, { recursive: true, force: true });
202
- } else {
203
- fs.rmdirSync(cacheDir, { recursive: true });
204
- }
205
- }
40
+ console.log(`Installing ${PROJECT} ${PACKAGE_JSON.version}...`);
41
+ const bin = buildWrapper();
42
+ await bin.run();
43
+ console.log('Installed nibble binary successfully');
206
44
  }
207
45
 
208
46
  main().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backendsystems/nibble",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Fast local network scanner with hardware identification and a terminal UI",
5
5
  "bin": {
6
6
  "nibble": "bin/nibble.js"
@@ -8,6 +8,9 @@
8
8
  "scripts": {
9
9
  "postinstall": "node bin/install.js"
10
10
  },
11
+ "dependencies": {
12
+ "bin-wrapper": "^4.1.0"
13
+ },
11
14
  "repository": {
12
15
  "type": "git",
13
16
  "url": "https://github.com/backendsystems/nibble"