@dk/hipp 0.1.7 → 0.1.8
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 +10 -3
- package/hipp.js +89 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -151,10 +151,17 @@ PERFORMANCE OF THIS SOFTWARE.
|
|
|
151
151
|
```json
|
|
152
152
|
{
|
|
153
153
|
"origin": "git@github.com:dmytri/hipp.git",
|
|
154
|
-
"tag": "v0.1.
|
|
154
|
+
"tag": "v0.1.8"
|
|
155
155
|
}
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
-
```npx @dk/hipp @dk/hipp@0.1.
|
|
158
|
+
```npx @dk/hipp @dk/hipp@0.1.8```
|
|
159
159
|
|
|
160
|
-
<!-- HIPP-MANIFEST
|
|
160
|
+
<!-- HIPP-MANIFEST -->
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"hash": "57d56d76a120bbc755700c3aa1f91f756e2cda09076dc0df522beb6a22018dca",
|
|
164
|
+
"signature": "eclO/AvbMfQMGS7g/vwX1DbB3dbm4XxWvlMFjo06KpE+d3w7KagtIv9klyjTazls74yGy3qcHhoy3i6/zWmZAg=="
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
<!-- /HIPP-MANIFEST -->
|
package/hipp.js
CHANGED
|
@@ -108,12 +108,12 @@ function verifySignature(data, signature, publicKey) {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
function createManifest(hash, signature) {
|
|
111
|
-
return
|
|
111
|
+
return JSON.stringify({ hash, signature }, null, 2);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
function parseManifest(
|
|
114
|
+
function parseManifest(manifestStr) {
|
|
115
115
|
try {
|
|
116
|
-
return JSON.parse(
|
|
116
|
+
return JSON.parse(manifestStr);
|
|
117
117
|
} catch {
|
|
118
118
|
return null;
|
|
119
119
|
}
|
|
@@ -127,7 +127,7 @@ const MANIFEST_START = '<!-- HIPP-MANIFEST -->';
|
|
|
127
127
|
const MANIFEST_END = '<!-- /HIPP-MANIFEST -->';
|
|
128
128
|
|
|
129
129
|
function appendManifestToReadme(readmeContent, manifest) {
|
|
130
|
-
return `${readmeContent}${MANIFEST_START}
|
|
130
|
+
return `${readmeContent}${MANIFEST_START}\n\`\`\`json\n${manifest}\n\`\`\`\n${MANIFEST_END}\n`;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
function extractManifestFromReadme(readmeContent) {
|
|
@@ -136,7 +136,7 @@ function extractManifestFromReadme(readmeContent) {
|
|
|
136
136
|
const endIdx = readmeContent.indexOf(MANIFEST_END, startIdx);
|
|
137
137
|
if (endIdx === -1) return null;
|
|
138
138
|
const content = readmeContent.slice(startIdx + MANIFEST_START.length, endIdx).trim();
|
|
139
|
-
const match = content.match(/^```(.+)```$/s);
|
|
139
|
+
const match = content.match(/^```json\n(.+)\n```$/s);
|
|
140
140
|
if (!match) return null;
|
|
141
141
|
return match[1];
|
|
142
142
|
}
|
|
@@ -162,6 +162,42 @@ function computeReadmeHash(readmeContent) {
|
|
|
162
162
|
return sha256(stripped);
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
function extractJsonMetaFromReadme(readmeContent) {
|
|
166
|
+
const lines = readmeContent.split('\n');
|
|
167
|
+
let jsonStart = -1;
|
|
168
|
+
let braceCount = 0;
|
|
169
|
+
let inJson = false;
|
|
170
|
+
|
|
171
|
+
for (let i = 0; i < lines.length; i++) {
|
|
172
|
+
const line = lines[i];
|
|
173
|
+
if (line === '```json') {
|
|
174
|
+
jsonStart = i + 1;
|
|
175
|
+
inJson = true;
|
|
176
|
+
braceCount = 0;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (inJson) {
|
|
180
|
+
for (const char of line) {
|
|
181
|
+
if (char === '{') braceCount++;
|
|
182
|
+
if (char === '}') braceCount--;
|
|
183
|
+
}
|
|
184
|
+
if (braceCount === 0 && line.includes('}')) {
|
|
185
|
+
const jsonStr = lines.slice(jsonStart, i + 1).join('\n');
|
|
186
|
+
try {
|
|
187
|
+
const parsed = JSON.parse(jsonStr);
|
|
188
|
+
if (parsed.origin && parsed.tag) {
|
|
189
|
+
return parsed;
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
// continue searching
|
|
193
|
+
}
|
|
194
|
+
inJson = false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
165
201
|
function safeStageName(name) {
|
|
166
202
|
return name.replace(/[^a-zA-Z0-9._-]/g, '-');
|
|
167
203
|
}
|
|
@@ -358,40 +394,74 @@ function copyTrackedFiles(stageDir, files) {
|
|
|
358
394
|
}
|
|
359
395
|
|
|
360
396
|
async function runVerify(packageSpec) {
|
|
361
|
-
|
|
397
|
+
let pkgName, pkgVersion;
|
|
398
|
+
if (packageSpec.startsWith('@')) {
|
|
399
|
+
const atIndex = packageSpec.indexOf('@', 1);
|
|
400
|
+
if (atIndex === -1) {
|
|
401
|
+
pkgName = packageSpec;
|
|
402
|
+
pkgVersion = undefined;
|
|
403
|
+
} else {
|
|
404
|
+
pkgName = packageSpec.slice(0, atIndex);
|
|
405
|
+
pkgVersion = packageSpec.slice(atIndex + 1);
|
|
406
|
+
}
|
|
407
|
+
} else {
|
|
408
|
+
const atIndex = packageSpec.indexOf('@');
|
|
409
|
+
if (atIndex === -1) {
|
|
410
|
+
pkgName = packageSpec;
|
|
411
|
+
pkgVersion = undefined;
|
|
412
|
+
} else {
|
|
413
|
+
pkgName = packageSpec.slice(0, atIndex);
|
|
414
|
+
pkgVersion = packageSpec.slice(atIndex + 1);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
362
417
|
log.info(`🔍 HIPP Verify: ${pkgName}${pkgVersion ? '@' + pkgVersion : ''}`);
|
|
363
418
|
|
|
364
|
-
const registryUrl =
|
|
365
|
-
const fetchUrl = `${registryUrl}/${encodeURIComponent(pkgName)}/${pkgVersion ? pkgVersion : 'latest'}`;
|
|
419
|
+
const registryUrl = `https://registry.npmjs.org/${encodeURIComponent(pkgName)}/${pkgVersion || 'latest'}`;
|
|
366
420
|
|
|
367
421
|
log.info(`📦 Fetching from npm...`);
|
|
422
|
+
const registryJson = runCmd('curl', ['-s', '-L', registryUrl]);
|
|
368
423
|
let tarballUrl;
|
|
369
424
|
try {
|
|
370
|
-
const
|
|
371
|
-
tarballUrl =
|
|
425
|
+
const json = JSON.parse(registryJson.stdout.trim());
|
|
426
|
+
tarballUrl = json.dist.tarball;
|
|
372
427
|
} catch {
|
|
373
|
-
fail(`❌ Failed to
|
|
428
|
+
fail(`❌ Failed to parse npm registry response for ${pkgName}`);
|
|
374
429
|
}
|
|
375
430
|
|
|
376
431
|
const tarballPath = path.join(os.tmpdir(), `hipp-verify-${safeStageName(pkgName)}-tgz`);
|
|
432
|
+
const extractDir = path.join(os.tmpdir(), `hipp-verify-extract-${safeStageName(pkgName)}`);
|
|
433
|
+
|
|
377
434
|
try {
|
|
378
|
-
log.info(`📦 Downloading tarball...`);
|
|
435
|
+
log.info(`📦 Downloading tarball from ${tarballUrl}...`);
|
|
379
436
|
const curlResult = runCmd('curl', ['-s', '-L', '-o', tarballPath, tarballUrl]);
|
|
380
437
|
if (curlResult.status !== 0) {
|
|
381
438
|
fail(`❌ Failed to download tarball`);
|
|
382
439
|
}
|
|
383
440
|
|
|
441
|
+
if (fs.existsSync(extractDir)) {
|
|
442
|
+
fs.rmSync(extractDir, { recursive: true });
|
|
443
|
+
}
|
|
444
|
+
fs.mkdirSync(extractDir, { recursive: true });
|
|
445
|
+
|
|
384
446
|
log.info(`📦 Extracting tarball...`);
|
|
385
|
-
|
|
447
|
+
const tarResult = spawnSync('tar', ['-xzf', tarballPath, '-C', extractDir], { encoding: 'utf8', stdio: 'pipe' });
|
|
448
|
+
if (tarResult.status !== 0) {
|
|
449
|
+
fail(`❌ Failed to extract tarball: ${tarResult.stderr}`);
|
|
450
|
+
}
|
|
386
451
|
|
|
387
|
-
const packageDir = path.join(
|
|
452
|
+
const packageDir = path.join(extractDir, 'package');
|
|
388
453
|
const stagedReadmePath = path.join(packageDir, 'README.md');
|
|
389
454
|
|
|
390
455
|
if (!fs.existsSync(stagedReadmePath)) {
|
|
391
|
-
fail(`❌ README.md not found in package`);
|
|
456
|
+
fail(`❌ README.md not found in package at ${stagedReadmePath}`);
|
|
392
457
|
}
|
|
393
458
|
|
|
394
459
|
const stagedReadme = fs.readFileSync(stagedReadmePath, 'utf8');
|
|
460
|
+
const jsonMeta = extractJsonMetaFromReadme(stagedReadme);
|
|
461
|
+
if (!jsonMeta || !jsonMeta.origin || !jsonMeta.tag) {
|
|
462
|
+
fail(`❌ JSON meta (origin/tag) not found in README`);
|
|
463
|
+
}
|
|
464
|
+
|
|
395
465
|
const manifestBase64 = extractManifestFromReadme(stagedReadme);
|
|
396
466
|
if (!manifestBase64) {
|
|
397
467
|
fail(`❌ Manifest not found in README`);
|
|
@@ -402,15 +472,12 @@ async function runVerify(packageSpec) {
|
|
|
402
472
|
fail(`❌ Invalid manifest format`);
|
|
403
473
|
}
|
|
404
474
|
|
|
405
|
-
log.info(`📦 Extracting tarball to staging...`);
|
|
406
|
-
runCmd('tar', ['-xzf', tarballPath, '-C', os.tmpdir()], { stdio: 'pipe' });
|
|
407
|
-
|
|
408
475
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), `hipp-verify-git-`));
|
|
409
476
|
try {
|
|
410
477
|
log.info(`🌿 Fetching from git origin...`);
|
|
411
478
|
|
|
412
|
-
const originUrl =
|
|
413
|
-
const tag =
|
|
479
|
+
const originUrl = jsonMeta.origin;
|
|
480
|
+
const tag = jsonMeta.tag;
|
|
414
481
|
|
|
415
482
|
git(['clone', '--branch', tag, '--depth', '1', originUrl, tmpDir], { stdio: 'pipe' });
|
|
416
483
|
|
|
@@ -450,9 +517,8 @@ async function runVerify(packageSpec) {
|
|
|
450
517
|
}
|
|
451
518
|
} finally {
|
|
452
519
|
fs.rmSync(tarballPath, { recursive: true, force: true });
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
fs.rmSync(packageExtractDir, { recursive: true, force: true });
|
|
520
|
+
if (fs.existsSync(extractDir)) {
|
|
521
|
+
fs.rmSync(extractDir, { recursive: true, force: true });
|
|
456
522
|
}
|
|
457
523
|
}
|
|
458
524
|
}
|