@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.
Files changed (3) hide show
  1. package/README.md +10 -3
  2. package/hipp.js +89 -23
  3. 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.7"
154
+ "tag": "v0.1.8"
155
155
  }
156
156
  ```
157
157
 
158
- ```npx @dk/hipp @dk/hipp@0.1.7```
158
+ ```npx @dk/hipp @dk/hipp@0.1.8```
159
159
 
160
- <!-- HIPP-MANIFEST -->```eyJoYXNoIjoiODYxNDgxZmM3Yzk0ZTM4YTJhZjdjNDc3ZmJkMDcyMTA2YTMyYTI2MjdkYmM1ZTg4MTI1OTk1ODMyYjdlMzhhMiIsInNpZ25hdHVyZSI6IkVnUTBSZUMzU0hKanovdFZoWlY0V3U1SVJOMDJTSlFPWUtxOFBrdHFpcUhvWnRTbE5pQWxrN25nWDcySldxL0ZRa2JTVE10TldlZUNrSG9hczAwR0N3PT0ifQ==```<!-- /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 Buffer.from(JSON.stringify({ hash, signature })).toString('base64');
111
+ return JSON.stringify({ hash, signature }, null, 2);
112
112
  }
113
113
 
114
- function parseManifest(manifestBase64) {
114
+ function parseManifest(manifestStr) {
115
115
  try {
116
- return JSON.parse(Buffer.from(manifestBase64, 'base64').toString('utf8'));
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}\`\`\`${manifest}\`\`\`${MANIFEST_END}\n`;
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
- const [pkgName, pkgVersion] = packageSpec.split('@');
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 = 'https://registry.npmjs.org';
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 fetchResult = runCmd('curl', ['-s', '-L', '-w', '%{url_effective}', '-o', '/dev/null', fetchUrl]);
371
- tarballUrl = fetchResult.stdout.trim();
425
+ const json = JSON.parse(registryJson.stdout.trim());
426
+ tarballUrl = json.dist.tarball;
372
427
  } catch {
373
- fail(`❌ Failed to fetch package info for ${pkgName}`);
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
- runCmd('tar', ['-xzf', tarballPath, '-C', os.tmpdir()], { stdio: 'pipe' });
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(os.tmpdir(), 'package');
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 = manifest.origin;
413
- const tag = manifest.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
- const packageExtractDir = path.join(os.tmpdir(), 'package');
454
- if (fs.existsSync(packageExtractDir)) {
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dk/hipp",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "High Integrity Package Publisher",
5
5
  "main": "hipp.js",
6
6
  "bin": {