npm-needs-publish 0.1.0 → 0.1.1

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.
@@ -27,3 +27,10 @@ export declare function getFileChangeSummary(changes: FileChange[]): string;
27
27
  * Check if changes are only in package.json
28
28
  */
29
29
  export declare function isOnlyPackageJsonChange(changes: FileChange[]): boolean;
30
+ /**
31
+ * Extract package.json from a tarball
32
+ *
33
+ * @param tarball - Tarball as Buffer (gzipped)
34
+ * @returns Parsed package.json object
35
+ */
36
+ export declare function extractPackageJson(tarball: Buffer): Promise<unknown>;
@@ -27,3 +27,10 @@ export declare function getFileChangeSummary(changes: FileChange[]): string;
27
27
  * Check if changes are only in package.json
28
28
  */
29
29
  export declare function isOnlyPackageJsonChange(changes: FileChange[]): boolean;
30
+ /**
31
+ * Extract package.json from a tarball
32
+ *
33
+ * @param tarball - Tarball as Buffer (gzipped)
34
+ * @returns Parsed package.json object
35
+ */
36
+ export declare function extractPackageJson(tarball: Buffer): Promise<unknown>;
@@ -19,6 +19,9 @@ _export(exports, {
19
19
  get comparePackageFiles () {
20
20
  return comparePackageFiles;
21
21
  },
22
+ get extractPackageJson () {
23
+ return extractPackageJson;
24
+ },
22
25
  get getFileChangeSummary () {
23
26
  return getFileChangeSummary;
24
27
  },
@@ -341,4 +344,31 @@ function isOnlyPackageJsonChange(changes) {
341
344
  }
342
345
  return true;
343
346
  }
347
+ function extractPackageJson(tarball) {
348
+ return _async_to_generator(function() {
349
+ var files, pkgJsonPath;
350
+ return _ts_generator(this, function(_state) {
351
+ switch(_state.label){
352
+ case 0:
353
+ return [
354
+ 4,
355
+ extractTarball(tarball)
356
+ ];
357
+ case 1:
358
+ files = _state.sent();
359
+ // Look for package.json in the tarball
360
+ pkgJsonPath = Object.keys(files).find(function(p) {
361
+ return p === 'package/package.json' || p.indexOf('/package.json') === p.length - 13;
362
+ });
363
+ if (!pkgJsonPath || !files[pkgJsonPath]) {
364
+ throw new Error('package.json not found in tarball');
365
+ }
366
+ return [
367
+ 2,
368
+ JSON.parse(files[pkgJsonPath].toString('utf8'))
369
+ ];
370
+ }
371
+ });
372
+ })();
373
+ }
344
374
  /* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/file-content.ts"],"sourcesContent":["/**\n * File content comparison for package tarballs\n *\n * Compares files between local and registry tarballs:\n * 1. Fast path: Compare tarball hashes\n * 2. If different: Extract and compare file-by-file\n * 3. Special handling for package.json (semantic comparison elsewhere)\n */\n\nimport crypto from 'crypto';\nimport Module from 'module';\nimport { Readable } from 'stream';\nimport { pipeline } from 'stream/promises';\nimport zlib from 'zlib';\nimport type { FileChange, FileComparison } from '../types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n// Lazy load tar\nlet _tar: typeof import('tar') | null = null;\n\nfunction getTar(): typeof import('tar') {\n if (!_tar) {\n _tar = _require('tar');\n }\n return _tar;\n}\n\n/**\n * Compare package files from two tarballs\n *\n * @param localTarball - Local package tarball as Buffer\n * @param registryTarball - Registry package tarball as Buffer\n * @returns File comparison result\n */\nexport async function comparePackageFiles(localTarball: Buffer, registryTarball: Buffer): Promise<FileComparison> {\n // Fast path: identical tarballs\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n return {\n identical: true,\n fileChanges: [],\n packageJsonOnly: false,\n };\n }\n\n // Extract both tarballs to memory\n const localFiles = await extractTarball(localTarball);\n const registryFiles = await extractTarball(registryTarball);\n\n const changes: FileChange[] = [];\n\n // Build combined set of all paths\n const allPaths: Record<string, boolean> = {};\n const localKeys = Object.keys(localFiles);\n for (let i = 0; i < localKeys.length; i++) {\n allPaths[localKeys[i]] = true;\n }\n const registryKeys = Object.keys(registryFiles);\n for (let i = 0; i < registryKeys.length; i++) {\n allPaths[registryKeys[i]] = true;\n }\n\n let onlyPackageJsonDiffers = true;\n\n const pathKeys = Object.keys(allPaths);\n for (let i = 0; i < pathKeys.length; i++) {\n const filePath = pathKeys[i];\n const isPackageJson = filePath === 'package/package.json' || filePath.indexOf('/package.json') === filePath.length - 13;\n const localContent = localFiles[filePath];\n const registryContent = registryFiles[filePath];\n\n if (!registryContent) {\n // File added\n changes.push({ path: filePath, action: 'added' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n } else if (!localContent) {\n // File removed\n changes.push({ path: filePath, action: 'removed' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n } else if (!buffersEqual(localContent, registryContent)) {\n // File modified\n changes.push({ path: filePath, action: 'modified' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n }\n }\n\n return {\n identical: changes.length === 0,\n fileChanges: changes,\n packageJsonOnly: onlyPackageJsonDiffers && changes.length > 0,\n };\n}\n\n/**\n * Extract tarball contents to memory\n *\n * @param tarball - Tarball as Buffer (gzipped)\n * @returns Map of file paths to content buffers\n */\nasync function extractTarball(tarball: Buffer): Promise<Record<string, Buffer>> {\n const files: Record<string, Buffer> = {};\n const tar = getTar();\n\n // Create a parser that collects file contents\n const parser = new tar.Parser({\n onReadEntry: (entry) => {\n if (entry.type === 'File') {\n const chunks: Buffer[] = [];\n\n entry.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n entry.on('end', () => {\n files[entry.path] = Buffer.concat(chunks);\n });\n } else {\n // Drain non-file entries (directories, etc.)\n entry.resume();\n }\n },\n });\n\n // Process the tarball\n await pipeline(Readable.from(tarball), zlib.createGunzip(), parser);\n\n return files;\n}\n\n/**\n * Hash a buffer using SHA-512\n */\nexport function hashBuffer(buffer: Buffer): string {\n return crypto.createHash('sha512').update(buffer).digest('base64');\n}\n\n/**\n * Compare two buffers for equality\n */\nfunction buffersEqual(a: Buffer, b: Buffer): boolean {\n if (a.length !== b.length) return false;\n return crypto.timingSafeEqual(a, b);\n}\n\n/**\n * Get a summary of file changes\n */\nexport function getFileChangeSummary(changes: FileChange[]): string {\n if (changes.length === 0) {\n return 'No file changes';\n }\n\n const added = changes.filter((c) => c.action === 'added');\n const removed = changes.filter((c) => c.action === 'removed');\n const modified = changes.filter((c) => c.action === 'modified');\n\n const parts: string[] = [];\n\n if (added.length > 0) {\n parts.push(`${added.length} added`);\n }\n if (removed.length > 0) {\n parts.push(`${removed.length} removed`);\n }\n if (modified.length > 0) {\n parts.push(`${modified.length} modified`);\n }\n\n return `Files: ${parts.join(', ')} (${changes.length} total)`;\n}\n\n/**\n * Check if changes are only in package.json\n */\nexport function isOnlyPackageJsonChange(changes: FileChange[]): boolean {\n if (changes.length === 0) return false;\n\n for (let i = 0; i < changes.length; i++) {\n const path = changes[i].path;\n if (path !== 'package/package.json' && path.indexOf('/package.json') !== path.length - 13) {\n return false;\n }\n }\n return true;\n}\n"],"names":["comparePackageFiles","getFileChangeSummary","hashBuffer","isOnlyPackageJsonChange","_require","require","Module","createRequire","_tar","getTar","localTarball","registryTarball","localHash","registryHash","localFiles","registryFiles","changes","allPaths","localKeys","i","registryKeys","onlyPackageJsonDiffers","pathKeys","filePath","isPackageJson","localContent","registryContent","identical","fileChanges","packageJsonOnly","extractTarball","Object","keys","length","indexOf","push","path","action","buffersEqual","tarball","files","tar","parser","Parser","onReadEntry","entry","type","chunks","on","chunk","Buffer","concat","resume","pipeline","Readable","from","zlib","createGunzip","buffer","crypto","createHash","update","digest","a","b","timingSafeEqual","added","filter","c","removed","modified","parts","join"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/file-content.ts"],"sourcesContent":["/**\n * File content comparison for package tarballs\n *\n * Compares files between local and registry tarballs:\n * 1. Fast path: Compare tarball hashes\n * 2. If different: Extract and compare file-by-file\n * 3. Special handling for package.json (semantic comparison elsewhere)\n */\n\nimport crypto from 'crypto';\nimport Module from 'module';\nimport { Readable } from 'stream';\nimport { pipeline } from 'stream/promises';\nimport zlib from 'zlib';\nimport type { FileChange, FileComparison } from '../types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n// Lazy load tar\nlet _tar: typeof import('tar') | null = null;\n\nfunction getTar(): typeof import('tar') {\n if (!_tar) {\n _tar = _require('tar');\n }\n return _tar;\n}\n\n/**\n * Compare package files from two tarballs\n *\n * @param localTarball - Local package tarball as Buffer\n * @param registryTarball - Registry package tarball as Buffer\n * @returns File comparison result\n */\nexport async function comparePackageFiles(localTarball: Buffer, registryTarball: Buffer): Promise<FileComparison> {\n // Fast path: identical tarballs\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n return {\n identical: true,\n fileChanges: [],\n packageJsonOnly: false,\n };\n }\n\n // Extract both tarballs to memory\n const localFiles = await extractTarball(localTarball);\n const registryFiles = await extractTarball(registryTarball);\n\n const changes: FileChange[] = [];\n\n // Build combined set of all paths\n const allPaths: Record<string, boolean> = {};\n const localKeys = Object.keys(localFiles);\n for (let i = 0; i < localKeys.length; i++) {\n allPaths[localKeys[i]] = true;\n }\n const registryKeys = Object.keys(registryFiles);\n for (let i = 0; i < registryKeys.length; i++) {\n allPaths[registryKeys[i]] = true;\n }\n\n let onlyPackageJsonDiffers = true;\n\n const pathKeys = Object.keys(allPaths);\n for (let i = 0; i < pathKeys.length; i++) {\n const filePath = pathKeys[i];\n const isPackageJson = filePath === 'package/package.json' || filePath.indexOf('/package.json') === filePath.length - 13;\n const localContent = localFiles[filePath];\n const registryContent = registryFiles[filePath];\n\n if (!registryContent) {\n // File added\n changes.push({ path: filePath, action: 'added' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n } else if (!localContent) {\n // File removed\n changes.push({ path: filePath, action: 'removed' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n } else if (!buffersEqual(localContent, registryContent)) {\n // File modified\n changes.push({ path: filePath, action: 'modified' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n }\n }\n\n return {\n identical: changes.length === 0,\n fileChanges: changes,\n packageJsonOnly: onlyPackageJsonDiffers && changes.length > 0,\n };\n}\n\n/**\n * Extract tarball contents to memory\n *\n * @param tarball - Tarball as Buffer (gzipped)\n * @returns Map of file paths to content buffers\n */\nasync function extractTarball(tarball: Buffer): Promise<Record<string, Buffer>> {\n const files: Record<string, Buffer> = {};\n const tar = getTar();\n\n // Create a parser that collects file contents\n const parser = new tar.Parser({\n onReadEntry: (entry) => {\n if (entry.type === 'File') {\n const chunks: Buffer[] = [];\n\n entry.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n entry.on('end', () => {\n files[entry.path] = Buffer.concat(chunks);\n });\n } else {\n // Drain non-file entries (directories, etc.)\n entry.resume();\n }\n },\n });\n\n // Process the tarball\n await pipeline(Readable.from(tarball), zlib.createGunzip(), parser);\n\n return files;\n}\n\n/**\n * Hash a buffer using SHA-512\n */\nexport function hashBuffer(buffer: Buffer): string {\n return crypto.createHash('sha512').update(buffer).digest('base64');\n}\n\n/**\n * Compare two buffers for equality\n */\nfunction buffersEqual(a: Buffer, b: Buffer): boolean {\n if (a.length !== b.length) return false;\n return crypto.timingSafeEqual(a, b);\n}\n\n/**\n * Get a summary of file changes\n */\nexport function getFileChangeSummary(changes: FileChange[]): string {\n if (changes.length === 0) {\n return 'No file changes';\n }\n\n const added = changes.filter((c) => c.action === 'added');\n const removed = changes.filter((c) => c.action === 'removed');\n const modified = changes.filter((c) => c.action === 'modified');\n\n const parts: string[] = [];\n\n if (added.length > 0) {\n parts.push(`${added.length} added`);\n }\n if (removed.length > 0) {\n parts.push(`${removed.length} removed`);\n }\n if (modified.length > 0) {\n parts.push(`${modified.length} modified`);\n }\n\n return `Files: ${parts.join(', ')} (${changes.length} total)`;\n}\n\n/**\n * Check if changes are only in package.json\n */\nexport function isOnlyPackageJsonChange(changes: FileChange[]): boolean {\n if (changes.length === 0) return false;\n\n for (let i = 0; i < changes.length; i++) {\n const path = changes[i].path;\n if (path !== 'package/package.json' && path.indexOf('/package.json') !== path.length - 13) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Extract package.json from a tarball\n *\n * @param tarball - Tarball as Buffer (gzipped)\n * @returns Parsed package.json object\n */\nexport async function extractPackageJson(tarball: Buffer): Promise<unknown> {\n const files = await extractTarball(tarball);\n\n // Look for package.json in the tarball\n const pkgJsonPath = Object.keys(files).find((p) => p === 'package/package.json' || p.indexOf('/package.json') === p.length - 13);\n\n if (!pkgJsonPath || !files[pkgJsonPath]) {\n throw new Error('package.json not found in tarball');\n }\n\n return JSON.parse(files[pkgJsonPath].toString('utf8'));\n}\n"],"names":["comparePackageFiles","extractPackageJson","getFileChangeSummary","hashBuffer","isOnlyPackageJsonChange","_require","require","Module","createRequire","_tar","getTar","localTarball","registryTarball","localHash","registryHash","localFiles","registryFiles","changes","allPaths","localKeys","i","registryKeys","onlyPackageJsonDiffers","pathKeys","filePath","isPackageJson","localContent","registryContent","identical","fileChanges","packageJsonOnly","extractTarball","Object","keys","length","indexOf","push","path","action","buffersEqual","tarball","files","tar","parser","Parser","onReadEntry","entry","type","chunks","on","chunk","Buffer","concat","resume","pipeline","Readable","from","zlib","createGunzip","buffer","crypto","createHash","update","digest","a","b","timingSafeEqual","added","filter","c","removed","modified","parts","join","pkgJsonPath","find","p","Error","JSON","parse","toString"],"mappings":"AAAA;;;;;;;CAOC;;;;;;;;;;;QA4BqBA;eAAAA;;QAgKAC;eAAAA;;QA7CNC;eAAAA;;QAfAC;eAAAA;;QA0CAC;eAAAA;;;6DAxKG;6DACA;sBACM;wBACA;2DACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGjB,IAAMC,WAAW,OAAOC,YAAY,cAAcC,eAAM,CAACC,aAAa,CAAC,uDAAmBF;AAE1F,gBAAgB;AAChB,IAAIG,OAAoC;AAExC,SAASC;IACP,IAAI,CAACD,MAAM;QACTA,OAAOJ,SAAS;IAClB;IACA,OAAOI;AACT;AASO,SAAeT,oBAAoBW,YAAoB,EAAEC,eAAuB;;YAE/EC,WACAC,cAWAC,YACAC,eAEAC,SAGAC,UACAC,WACGC,GAGHC,cACGD,IAILE,wBAEEC,UACGH,IACDI,UACAC,eACAC,cACAC;;;;oBApCR,gCAAgC;oBAC1Bd,YAAYV,WAAWQ;oBACvBG,eAAeX,WAAWS;oBAEhC,IAAIC,cAAcC,cAAc;wBAC9B;;4BAAO;gCACLc,WAAW;gCACXC,WAAW;gCACXC,iBAAiB;4BACnB;;oBACF;oBAGmB;;wBAAMC,eAAepB;;;oBAAlCI,aAAa;oBACG;;wBAAMgB,eAAenB;;;oBAArCI,gBAAgB;oBAEhBC;oBAEN,kCAAkC;oBAC5BC,WAAoC,CAAC;oBACrCC,YAAYa,OAAOC,IAAI,CAAClB;oBAC9B,IAASK,IAAI,GAAGA,IAAID,UAAUe,MAAM,EAAEd,IAAK;wBACzCF,QAAQ,CAACC,SAAS,CAACC,EAAE,CAAC,GAAG;oBAC3B;oBACMC,eAAeW,OAAOC,IAAI,CAACjB;oBACjC,IAASI,KAAI,GAAGA,KAAIC,aAAaa,MAAM,EAAEd,KAAK;wBAC5CF,QAAQ,CAACG,YAAY,CAACD,GAAE,CAAC,GAAG;oBAC9B;oBAEIE,yBAAyB;oBAEvBC,WAAWS,OAAOC,IAAI,CAACf;oBAC7B,IAASE,KAAI,GAAGA,KAAIG,SAASW,MAAM,EAAEd,KAAK;wBAClCI,WAAWD,QAAQ,CAACH,GAAE;wBACtBK,gBAAgBD,aAAa,0BAA0BA,SAASW,OAAO,CAAC,qBAAqBX,SAASU,MAAM,GAAG;wBAC/GR,eAAeX,UAAU,CAACS,SAAS;wBACnCG,kBAAkBX,aAAa,CAACQ,SAAS;wBAE/C,IAAI,CAACG,iBAAiB;4BACpB,aAAa;4BACbV,QAAQmB,IAAI,CAAC;gCAAEC,MAAMb;gCAAUc,QAAQ;4BAAQ;4BAC/C,IAAI,CAACb,eAAeH,yBAAyB;wBAC/C,OAAO,IAAI,CAACI,cAAc;4BACxB,eAAe;4BACfT,QAAQmB,IAAI,CAAC;gCAAEC,MAAMb;gCAAUc,QAAQ;4BAAU;4BACjD,IAAI,CAACb,eAAeH,yBAAyB;wBAC/C,OAAO,IAAI,CAACiB,aAAab,cAAcC,kBAAkB;4BACvD,gBAAgB;4BAChBV,QAAQmB,IAAI,CAAC;gCAAEC,MAAMb;gCAAUc,QAAQ;4BAAW;4BAClD,IAAI,CAACb,eAAeH,yBAAyB;wBAC/C;oBACF;oBAEA;;wBAAO;4BACLM,WAAWX,QAAQiB,MAAM,KAAK;4BAC9BL,aAAaZ;4BACba,iBAAiBR,0BAA0BL,QAAQiB,MAAM,GAAG;wBAC9D;;;;IACF;;AAEA;;;;;CAKC,GACD,SAAeH,eAAeS,OAAe;;YACrCC,OACAC,KAGAC;;;;oBAJAF,QAAgC,CAAC;oBACjCC,MAAMhC;oBAEZ,8CAA8C;oBACxCiC,SAAS,IAAID,IAAIE,MAAM,CAAC;wBAC5BC,aAAa,SAACC;4BACZ,IAAIA,MAAMC,IAAI,KAAK,QAAQ;gCACzB,IAAMC,SAAmB,EAAE;gCAE3BF,MAAMG,EAAE,CAAC,QAAQ,SAACC;oCAChBF,OAAOZ,IAAI,CAACc;gCACd;gCAEAJ,MAAMG,EAAE,CAAC,OAAO;oCACdR,KAAK,CAACK,MAAMT,IAAI,CAAC,GAAGc,OAAOC,MAAM,CAACJ;gCACpC;4BACF,OAAO;gCACL,6CAA6C;gCAC7CF,MAAMO,MAAM;4BACd;wBACF;oBACF;oBAEA,sBAAsB;oBACtB;;wBAAMC,IAAAA,kBAAQ,EAACC,gBAAQ,CAACC,IAAI,CAAChB,UAAUiB,aAAI,CAACC,YAAY,IAAIf;;;oBAA5D;oBAEA;;wBAAOF;;;;IACT;;AAKO,SAAStC,WAAWwD,MAAc;IACvC,OAAOC,eAAM,CAACC,UAAU,CAAC,UAAUC,MAAM,CAACH,QAAQI,MAAM,CAAC;AAC3D;AAEA;;CAEC,GACD,SAASxB,aAAayB,CAAS,EAAEC,CAAS;IACxC,IAAID,EAAE9B,MAAM,KAAK+B,EAAE/B,MAAM,EAAE,OAAO;IAClC,OAAO0B,eAAM,CAACM,eAAe,CAACF,GAAGC;AACnC;AAKO,SAAS/D,qBAAqBe,OAAqB;IACxD,IAAIA,QAAQiB,MAAM,KAAK,GAAG;QACxB,OAAO;IACT;IAEA,IAAMiC,QAAQlD,QAAQmD,MAAM,CAAC,SAACC;eAAMA,EAAE/B,MAAM,KAAK;;IACjD,IAAMgC,UAAUrD,QAAQmD,MAAM,CAAC,SAACC;eAAMA,EAAE/B,MAAM,KAAK;;IACnD,IAAMiC,WAAWtD,QAAQmD,MAAM,CAAC,SAACC;eAAMA,EAAE/B,MAAM,KAAK;;IAEpD,IAAMkC,QAAkB,EAAE;IAE1B,IAAIL,MAAMjC,MAAM,GAAG,GAAG;QACpBsC,MAAMpC,IAAI,CAAC,AAAC,GAAe,OAAb+B,MAAMjC,MAAM,EAAC;IAC7B;IACA,IAAIoC,QAAQpC,MAAM,GAAG,GAAG;QACtBsC,MAAMpC,IAAI,CAAC,AAAC,GAAiB,OAAfkC,QAAQpC,MAAM,EAAC;IAC/B;IACA,IAAIqC,SAASrC,MAAM,GAAG,GAAG;QACvBsC,MAAMpC,IAAI,CAAC,AAAC,GAAkB,OAAhBmC,SAASrC,MAAM,EAAC;IAChC;IAEA,OAAO,AAAC,UAA8BjB,OAArBuD,MAAMC,IAAI,CAAC,OAAM,MAAmB,OAAfxD,QAAQiB,MAAM,EAAC;AACvD;AAKO,SAAS9B,wBAAwBa,OAAqB;IAC3D,IAAIA,QAAQiB,MAAM,KAAK,GAAG,OAAO;IAEjC,IAAK,IAAId,IAAI,GAAGA,IAAIH,QAAQiB,MAAM,EAAEd,IAAK;QACvC,IAAMiB,OAAOpB,OAAO,CAACG,EAAE,CAACiB,IAAI;QAC5B,IAAIA,SAAS,0BAA0BA,KAAKF,OAAO,CAAC,qBAAqBE,KAAKH,MAAM,GAAG,IAAI;YACzF,OAAO;QACT;IACF;IACA,OAAO;AACT;AAQO,SAAejC,mBAAmBuC,OAAe;;YAChDC,OAGAiC;;;;oBAHQ;;wBAAM3C,eAAeS;;;oBAA7BC,QAAQ;oBAEd,uCAAuC;oBACjCiC,cAAc1C,OAAOC,IAAI,CAACQ,OAAOkC,IAAI,CAAC,SAACC;+BAAMA,MAAM,0BAA0BA,EAAEzC,OAAO,CAAC,qBAAqByC,EAAE1C,MAAM,GAAG;;oBAE7H,IAAI,CAACwC,eAAe,CAACjC,KAAK,CAACiC,YAAY,EAAE;wBACvC,MAAM,IAAIG,MAAM;oBAClB;oBAEA;;wBAAOC,KAAKC,KAAK,CAACtC,KAAK,CAACiC,YAAY,CAACM,QAAQ,CAAC;;;;IAChD"}
@@ -2,6 +2,6 @@
2
2
  * Comparators - Core comparison logic for npm-needs-publish
3
3
  */
4
4
  export { compareDependencies, getDependencyChangeSummary, hasSignificantDependencyChanges, } from './dependency.js';
5
- export { comparePackageFiles, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange, } from './file-content.js';
5
+ export { comparePackageFiles, extractPackageJson, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange, } from './file-content.js';
6
6
  export { comparePackageJson, isSignificantField, } from './package-json.js';
7
7
  export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions, } from './version-specifier.js';
@@ -2,6 +2,6 @@
2
2
  * Comparators - Core comparison logic for npm-needs-publish
3
3
  */
4
4
  export { compareDependencies, getDependencyChangeSummary, hasSignificantDependencyChanges, } from './dependency.js';
5
- export { comparePackageFiles, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange, } from './file-content.js';
5
+ export { comparePackageFiles, extractPackageJson, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange, } from './file-content.js';
6
6
  export { comparePackageJson, isSignificantField, } from './package-json.js';
7
7
  export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions, } from './version-specifier.js';
@@ -26,6 +26,9 @@ _export(exports, {
26
26
  get comparisonToSemanticChange () {
27
27
  return _versionspecifierts.comparisonToSemanticChange;
28
28
  },
29
+ get extractPackageJson () {
30
+ return _filecontentts.extractPackageJson;
31
+ },
29
32
  get getDependencyChangeSummary () {
30
33
  return _dependencyts.getDependencyChangeSummary;
31
34
  },
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/index.ts"],"sourcesContent":["/**\n * Comparators - Core comparison logic for npm-needs-publish\n */\n\nexport {\n compareDependencies,\n getDependencyChangeSummary,\n hasSignificantDependencyChanges,\n} from './dependency.ts';\nexport {\n comparePackageFiles,\n getFileChangeSummary,\n hashBuffer,\n isOnlyPackageJsonChange,\n} from './file-content.ts';\n\nexport {\n comparePackageJson,\n isSignificantField,\n} from './package-json.ts';\nexport {\n compareVersionSpecifiers,\n comparisonToSemanticChange,\n parseVersionSpecifier,\n type SemanticChangeOptions,\n} from './version-specifier.ts';\n"],"names":["compareDependencies","comparePackageFiles","comparePackageJson","compareVersionSpecifiers","comparisonToSemanticChange","getDependencyChangeSummary","getFileChangeSummary","hasSignificantDependencyChanges","hashBuffer","isOnlyPackageJsonChange","isSignificantField","parseVersionSpecifier"],"mappings":"AAAA;;CAEC;;;;;;;;;;;QAGCA;eAAAA,iCAAmB;;QAKnBC;eAAAA,kCAAmB;;QAOnBC;eAAAA,iCAAkB;;QAIlBC;eAAAA,4CAAwB;;QACxBC;eAAAA,8CAA0B;;QAhB1BC;eAAAA,wCAA0B;;QAK1BC;eAAAA,mCAAoB;;QAJpBC;eAAAA,6CAA+B;;QAK/BC;eAAAA,yBAAU;;QACVC;eAAAA,sCAAuB;;QAKvBC;eAAAA,iCAAkB;;QAKlBC;eAAAA,yCAAqB;;;4BAfhB;6BAMA;6BAKA;kCAMA"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/index.ts"],"sourcesContent":["/**\n * Comparators - Core comparison logic for npm-needs-publish\n */\n\nexport {\n compareDependencies,\n getDependencyChangeSummary,\n hasSignificantDependencyChanges,\n} from './dependency.ts';\nexport {\n comparePackageFiles,\n extractPackageJson,\n getFileChangeSummary,\n hashBuffer,\n isOnlyPackageJsonChange,\n} from './file-content.ts';\n\nexport {\n comparePackageJson,\n isSignificantField,\n} from './package-json.ts';\nexport {\n compareVersionSpecifiers,\n comparisonToSemanticChange,\n parseVersionSpecifier,\n type SemanticChangeOptions,\n} from './version-specifier.ts';\n"],"names":["compareDependencies","comparePackageFiles","comparePackageJson","compareVersionSpecifiers","comparisonToSemanticChange","extractPackageJson","getDependencyChangeSummary","getFileChangeSummary","hasSignificantDependencyChanges","hashBuffer","isOnlyPackageJsonChange","isSignificantField","parseVersionSpecifier"],"mappings":"AAAA;;CAEC;;;;;;;;;;;QAGCA;eAAAA,iCAAmB;;QAKnBC;eAAAA,kCAAmB;;QAQnBC;eAAAA,iCAAkB;;QAIlBC;eAAAA,4CAAwB;;QACxBC;eAAAA,8CAA0B;;QAZ1BC;eAAAA,iCAAkB;;QALlBC;eAAAA,wCAA0B;;QAM1BC;eAAAA,mCAAoB;;QALpBC;eAAAA,6CAA+B;;QAM/BC;eAAAA,yBAAU;;QACVC;eAAAA,sCAAuB;;QAKvBC;eAAAA,iCAAkB;;QAKlBC;eAAAA,yCAAqB;;;4BAhBhB;6BAOA;6BAKA;kCAMA"}
@@ -18,6 +18,7 @@
18
18
  * ```
19
19
  */
20
20
  export { compareDependencies } from './comparators/dependency.js';
21
+ export { extractPackageJson } from './comparators/file-content.js';
21
22
  export { comparePackageJson } from './comparators/package-json.js';
22
23
  export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions } from './comparators/version-specifier.js';
23
24
  export { type NeedsPublishCallback, needsPublish, needsPublishCb } from './needs-publish.js';
@@ -18,6 +18,7 @@
18
18
  * ```
19
19
  */
20
20
  export { compareDependencies } from './comparators/dependency.js';
21
+ export { extractPackageJson } from './comparators/file-content.js';
21
22
  export { comparePackageJson } from './comparators/package-json.js';
22
23
  export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions } from './comparators/version-specifier.js';
23
24
  export { type NeedsPublishCallback, needsPublish, needsPublishCb } from './needs-publish.js';
package/dist/cjs/index.js CHANGED
@@ -40,6 +40,9 @@ _export(exports, {
40
40
  get comparisonToSemanticChange () {
41
41
  return _versionspecifierts.comparisonToSemanticChange;
42
42
  },
43
+ get extractPackageJson () {
44
+ return _filecontentts.extractPackageJson;
45
+ },
43
46
  get needsPublish () {
44
47
  return _needspublishts.needsPublish;
45
48
  },
@@ -51,6 +54,7 @@ _export(exports, {
51
54
  }
52
55
  });
53
56
  var _dependencyts = require("./comparators/dependency.js");
57
+ var _filecontentts = require("./comparators/file-content.js");
54
58
  var _packagejsonts = require("./comparators/package-json.js");
55
59
  var _versionspecifierts = require("./comparators/version-specifier.js");
56
60
  var _needspublishts = require("./needs-publish.js");
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/index.ts"],"sourcesContent":["/**\n * npm-needs-publish\n *\n * Smart publish detection for npm packages - semantic package.json comparison\n * with semver-aware dependency analysis\n *\n * @example\n * ```typescript\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * // Proceed with npm publish\n * } else {\n * console.log('No publish needed:', result.reason);\n * }\n * ```\n */\n\n// Comparators\nexport { compareDependencies } from './comparators/dependency.ts';\nexport { comparePackageJson } from './comparators/package-json.ts';\nexport { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions } from './comparators/version-specifier.ts';\n// Main API\nexport { type NeedsPublishCallback, needsPublish, needsPublishCb } from './needs-publish.ts';\n\n// Types\nexport type {\n ChangeDetail,\n CompareOptions,\n CompareSpecifierOptions,\n DependencyChange,\n DependencyCompareOptions,\n DependencyComparison,\n FieldChange,\n FileChange,\n FileComparison,\n NeedsPublishOptions,\n NeedsPublishResult,\n PackageJson,\n PackageJsonComparison,\n ParsedVersionSpecifier,\n SemanticChange,\n SpecifierComparison,\n VersionSpecifierType,\n} from './types.ts';\n"],"names":["compareDependencies","comparePackageJson","compareVersionSpecifiers","comparisonToSemanticChange","needsPublish","needsPublishCb","parseVersionSpecifier"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;CAkBC,GAED,cAAc;;;;;;;;;;;;QACLA;eAAAA,iCAAmB;;QACnBC;eAAAA,iCAAkB;;QAClBC;eAAAA,4CAAwB;;QAAEC;eAAAA,8CAA0B;;QAEzBC;eAAAA,4BAAY;;QAAEC;eAAAA,8BAAc;;QAFDC;eAAAA,yCAAqB;;;4BAFhD;6BACD;kCACqF;8BAEhD"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/index.ts"],"sourcesContent":["/**\n * npm-needs-publish\n *\n * Smart publish detection for npm packages - semantic package.json comparison\n * with semver-aware dependency analysis\n *\n * @example\n * ```typescript\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * // Proceed with npm publish\n * } else {\n * console.log('No publish needed:', result.reason);\n * }\n * ```\n */\n\n// Comparators\nexport { compareDependencies } from './comparators/dependency.ts';\nexport { extractPackageJson } from './comparators/file-content.ts';\nexport { comparePackageJson } from './comparators/package-json.ts';\nexport { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions } from './comparators/version-specifier.ts';\n// Main API\nexport { type NeedsPublishCallback, needsPublish, needsPublishCb } from './needs-publish.ts';\n\n// Types\nexport type {\n ChangeDetail,\n CompareOptions,\n CompareSpecifierOptions,\n DependencyChange,\n DependencyCompareOptions,\n DependencyComparison,\n FieldChange,\n FileChange,\n FileComparison,\n NeedsPublishOptions,\n NeedsPublishResult,\n PackageJson,\n PackageJsonComparison,\n ParsedVersionSpecifier,\n SemanticChange,\n SpecifierComparison,\n VersionSpecifierType,\n} from './types.ts';\n"],"names":["compareDependencies","comparePackageJson","compareVersionSpecifiers","comparisonToSemanticChange","extractPackageJson","needsPublish","needsPublishCb","parseVersionSpecifier"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;CAkBC,GAED,cAAc;;;;;;;;;;;;QACLA;eAAAA,iCAAmB;;QAEnBC;eAAAA,iCAAkB;;QAClBC;eAAAA,4CAAwB;;QAAEC;eAAAA,8CAA0B;;QAFpDC;eAAAA,iCAAkB;;QAISC;eAAAA,4BAAY;;QAAEC;eAAAA,8BAAc;;QAFDC;eAAAA,yCAAqB;;;4BAHhD;6BACD;6BACA;kCACqF;8BAEhD"}
@@ -274,7 +274,7 @@ var workerWrapper = (0, _tsdslib.wrapWorker)(_path.default.join(dist, 'cjs', 'ne
274
274
  }
275
275
  (function() {
276
276
  return _async_to_generator(function() {
277
- var pacote, Arborist, npa, execFile, promisify, execFileAsync, _ref, comparePackageFiles, comparePackageJson, hashBuffer, registry, scope, stdout, e, registryPkg, registryTarball, _packument_disttags, _registryPkg_dist, _registryPkg_dist1, packument, latestVersion, tarballUrl, err, error, localTarball, spec, manifest, err1, error1, localHash, registryHash, fileComparison, pkgJsonComparison, changes;
277
+ var pacote, Arborist, npa, execFile, promisify, execFileAsync, _ref, comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer, registry, scope, stdout, e, registryPkg, registryTarball, _packument_disttags, _registryPkg_dist, _registryPkg_dist1, packument, latestVersion, tarballUrl, err, error, localTarball, spec, manifest, err1, error1, localHash, registryHash, fileComparison, localTarballPkg, registryTarballPkg, pkgJsonComparison, changes;
278
278
  return _ts_generator(this, function(_state) {
279
279
  switch(_state.label){
280
280
  case 0:
@@ -291,7 +291,7 @@ var workerWrapper = (0, _tsdslib.wrapWorker)(_path.default.join(dist, 'cjs', 'ne
291
291
  })
292
292
  ];
293
293
  case 1:
294
- _ref = _state.sent(), comparePackageFiles = _ref.comparePackageFiles, comparePackageJson = _ref.comparePackageJson, hashBuffer = _ref.hashBuffer;
294
+ _ref = _state.sent(), comparePackageFiles = _ref.comparePackageFiles, comparePackageJson = _ref.comparePackageJson, extractPackageJson = _ref.extractPackageJson, hashBuffer = _ref.hashBuffer;
295
295
  // Get registry URL for scoped packages
296
296
  registry = options.registry;
297
297
  if (!!registry) return [
@@ -511,61 +511,75 @@ var workerWrapper = (0, _tsdslib.wrapWorker)(_path.default.join(dist, 'cjs', 'ne
511
511
  2
512
512
  ];
513
513
  }
514
- // Step 6: If only package.json differs, do semantic comparison
515
- if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {
516
- pkgJsonComparison = comparePackageJson(localPkg, registryPkg, {
517
- includeOptionalDeps: options.includeOptionalDeps,
518
- additionalSignificantFields: options.additionalSignificantFields,
519
- ignoreFields: options.ignoreFields,
520
- treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent
521
- });
522
- if (!pkgJsonComparison.hasSignificantChanges) {
523
- callback(null, {
524
- needsPublish: false,
525
- reason: 'Package.json changes are not significant for consumers',
526
- changes: pkgJsonComparison.fieldChanges.map(function(fc) {
527
- return {
528
- type: 'field',
529
- field: fc.field,
530
- oldValue: fc.oldValue,
531
- newValue: fc.newValue,
532
- significance: 'informational'
533
- };
534
- })
535
- });
536
- return [
537
- 2
538
- ];
539
- }
540
- // Build changes list
541
- changes = _to_consumable_array(pkgJsonComparison.fieldChanges.map(function(fc) {
542
- return {
543
- type: 'field',
544
- field: fc.field,
545
- oldValue: fc.oldValue,
546
- newValue: fc.newValue,
547
- significance: fc.significance
548
- };
549
- })).concat(_to_consumable_array(pkgJsonComparison.dependencyChanges.filter(function(dc) {
550
- return dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none';
551
- }).map(function(dc) {
552
- return {
553
- type: 'dependency',
554
- field: "".concat(dc.type, ".").concat(dc.name),
555
- oldValue: dc.oldSpec,
556
- newValue: dc.newSpec,
557
- significance: 'significant'
558
- };
559
- })));
514
+ if (!(fileComparison.packageJsonOnly && !options.packageJsonOnly)) return [
515
+ 3,
516
+ 17
517
+ ];
518
+ return [
519
+ 4,
520
+ extractPackageJson(localTarball)
521
+ ];
522
+ case 15:
523
+ localTarballPkg = _state.sent();
524
+ return [
525
+ 4,
526
+ extractPackageJson(registryTarball)
527
+ ];
528
+ case 16:
529
+ registryTarballPkg = _state.sent();
530
+ pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {
531
+ includeOptionalDeps: options.includeOptionalDeps,
532
+ additionalSignificantFields: options.additionalSignificantFields,
533
+ ignoreFields: options.ignoreFields,
534
+ treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent
535
+ });
536
+ if (!pkgJsonComparison.hasSignificantChanges) {
560
537
  callback(null, {
561
- needsPublish: true,
562
- reason: pkgJsonComparison.summary,
563
- changes: changes
538
+ needsPublish: false,
539
+ reason: 'Package.json changes are not significant for consumers',
540
+ changes: pkgJsonComparison.fieldChanges.map(function(fc) {
541
+ return {
542
+ type: 'field',
543
+ field: fc.field,
544
+ oldValue: fc.oldValue,
545
+ newValue: fc.newValue,
546
+ significance: 'informational'
547
+ };
548
+ })
564
549
  });
565
550
  return [
566
551
  2
567
552
  ];
568
553
  }
554
+ // Build changes list
555
+ changes = _to_consumable_array(pkgJsonComparison.fieldChanges.map(function(fc) {
556
+ return {
557
+ type: 'field',
558
+ field: fc.field,
559
+ oldValue: fc.oldValue,
560
+ newValue: fc.newValue,
561
+ significance: fc.significance
562
+ };
563
+ })).concat(_to_consumable_array(pkgJsonComparison.dependencyChanges.filter(function(dc) {
564
+ return dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none';
565
+ }).map(function(dc) {
566
+ return {
567
+ type: 'dependency',
568
+ field: "".concat(dc.type, ".").concat(dc.name),
569
+ oldValue: dc.oldSpec,
570
+ newValue: dc.newSpec,
571
+ significance: 'significant'
572
+ };
573
+ })));
574
+ callback(null, {
575
+ needsPublish: true,
576
+ reason: pkgJsonComparison.summary,
577
+ changes: changes
578
+ });
579
+ return [
580
+ 2
581
+ ];
582
+ case 17:
569
583
  // Step 7: Other files changed
570
584
  callback(null, {
571
585
  needsPublish: true,
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/needs-publish.ts"],"sourcesContent":["/**\n * Main orchestration logic for npm-needs-publish\n *\n * Algorithm:\n * 1. Fetch registry packument → if E404, return needsPublish=true (first publish)\n * 2. Version check → if different, return needsPublish=true (intentional bump)\n * 3. Fast hash check → if identical, return needsPublish=false (no changes)\n * 4. Extract both tarballs, compare file-by-file (excluding package.json)\n * 5. If non-package.json files differ → return needsPublish=true\n * 6. If only package.json differs → do semantic comparison\n */\n\nimport fs from 'fs';\nimport Module from 'module';\nimport path from 'path';\nimport { wrapWorker } from 'tsds-lib';\nimport url from 'url';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\n// Version check for worker pattern - use worker for Node < 14\nconst major = +process.versions.node.split('.')[0];\nconst version = major > 14 ? 'local' : 'stable';\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\nconst __dirname = path.dirname(typeof __filename === 'undefined' ? url.fileURLToPath(import.meta.url) : __filename);\nconst dist = path.join(__dirname, '..');\nconst workerWrapper = wrapWorker(path.join(dist, 'cjs', 'needs-publish.js'));\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\n/**\n * Worker function that runs on Node 14+\n */\nfunction worker(options: NeedsPublishOptions, callback: NeedsPublishCallback): undefined {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private) {\n callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n return;\n }\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl) {\n callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n const pkgJsonComparison = comparePackageJson(localPkg, registryPkg, {\n includeOptionalDeps: options.includeOptionalDeps,\n additionalSignificantFields: options.additionalSignificantFields,\n ignoreFields: options.ignoreFields,\n treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent,\n });\n\n if (!pkgJsonComparison.hasSignificantChanges) {\n callback(null, {\n needsPublish: false,\n reason: 'Package.json changes are not significant for consumers',\n changes: pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: 'informational' as const,\n })),\n });\n return;\n }\n\n // Build changes list\n const changes: ChangeDetail[] = [\n ...pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: fc.significance,\n })),\n ...pkgJsonComparison.dependencyChanges\n .filter((dc) => dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none')\n .map((dc) => ({\n type: 'dependency' as const,\n field: `${dc.type}.${dc.name}`,\n oldValue: dc.oldSpec,\n newValue: dc.newSpec,\n significance: 'significant' as const,\n })),\n ];\n\n callback(null, {\n needsPublish: true,\n reason: pkgJsonComparison.summary,\n changes,\n });\n return;\n }\n\n // Step 7: Other files changed\n callback(null, {\n needsPublish: true,\n reason: `Code changes detected (${fileComparison.fileChanges.length} files changed)`,\n changes: fileComparison.fileChanges.map((fc) => ({\n type: 'file' as const,\n field: fc.path,\n significance: 'significant' as const,\n })),\n });\n })().catch(callback);\n}\n\n/**\n * Callback-based needsPublish - works on Node 0.8+\n * Uses worker for older Node versions, runs locally on Node 14+\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback): void {\n version !== 'local' ? workerWrapper(version, options, callback) : worker(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => {\n needsPublishCb(options, (error, result) => {\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error('No result returned'));\n });\n });\n}\n"],"names":["needsPublish","needsPublishCb","major","process","versions","node","split","version","_require","require","Module","createRequire","__dirname","path","dirname","__filename","url","fileURLToPath","dist","join","workerWrapper","wrapWorker","worker","options","callback","cwd","localPkg","package","JSON","parse","fs","readFileSync","private","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","hashBuffer","registry","scope","stdout","registryPkg","registryTarball","packument","latestVersion","tarballUrl","err","error","localTarball","spec","manifest","localHash","registryHash","fileComparison","pkgJsonComparison","changes","stringStartsWith","name","undefined","trim","latest","type","significance","field","oldValue","newValue","tarball","integrity","code","message","_resolved","_integrity","substring","identical","packageJsonOnly","includeOptionalDeps","additionalSignificantFields","ignoreFields","treatNarrowingAsEquivalent","hasSignificantChanges","fieldChanges","map","fc","dependencyChanges","filter","dc","semanticChange","oldSpec","newSpec","summary","fileChanges","length","catch","Promise","resolve","reject","result","Error"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/needs-publish.ts"],"sourcesContent":["/**\n * Main orchestration logic for npm-needs-publish\n *\n * Algorithm:\n * 1. Fetch registry packument → if E404, return needsPublish=true (first publish)\n * 2. Version check → if different, return needsPublish=true (intentional bump)\n * 3. Fast hash check → if identical, return needsPublish=false (no changes)\n * 4. Extract both tarballs, compare file-by-file (excluding package.json)\n * 5. If non-package.json files differ → return needsPublish=true\n * 6. If only package.json differs → do semantic comparison\n */\n\nimport fs from 'fs';\nimport Module from 'module';\nimport path from 'path';\nimport { wrapWorker } from 'tsds-lib';\nimport url from 'url';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\n// Version check for worker pattern - use worker for Node < 14\nconst major = +process.versions.node.split('.')[0];\nconst version = major > 14 ? 'local' : 'stable';\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\nconst __dirname = path.dirname(typeof __filename === 'undefined' ? url.fileURLToPath(import.meta.url) : __filename);\nconst dist = path.join(__dirname, '..');\nconst workerWrapper = wrapWorker(path.join(dist, 'cjs', 'needs-publish.js'));\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\n/**\n * Worker function that runs on Node 14+\n */\nfunction worker(options: NeedsPublishOptions, callback: NeedsPublishCallback): undefined {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private) {\n callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n return;\n }\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl) {\n callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n // Extract package.json from both tarballs for accurate comparison\n // (packument metadata is missing fields like 'files')\n const localTarballPkg = (await extractPackageJson(localTarball)) as PackageJson;\n const registryTarballPkg = (await extractPackageJson(registryTarball)) as PackageJson;\n\n const pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {\n includeOptionalDeps: options.includeOptionalDeps,\n additionalSignificantFields: options.additionalSignificantFields,\n ignoreFields: options.ignoreFields,\n treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent,\n });\n\n if (!pkgJsonComparison.hasSignificantChanges) {\n callback(null, {\n needsPublish: false,\n reason: 'Package.json changes are not significant for consumers',\n changes: pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: 'informational' as const,\n })),\n });\n return;\n }\n\n // Build changes list\n const changes: ChangeDetail[] = [\n ...pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: fc.significance,\n })),\n ...pkgJsonComparison.dependencyChanges\n .filter((dc) => dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none')\n .map((dc) => ({\n type: 'dependency' as const,\n field: `${dc.type}.${dc.name}`,\n oldValue: dc.oldSpec,\n newValue: dc.newSpec,\n significance: 'significant' as const,\n })),\n ];\n\n callback(null, {\n needsPublish: true,\n reason: pkgJsonComparison.summary,\n changes,\n });\n return;\n }\n\n // Step 7: Other files changed\n callback(null, {\n needsPublish: true,\n reason: `Code changes detected (${fileComparison.fileChanges.length} files changed)`,\n changes: fileComparison.fileChanges.map((fc) => ({\n type: 'file' as const,\n field: fc.path,\n significance: 'significant' as const,\n })),\n });\n })().catch(callback);\n}\n\n/**\n * Callback-based needsPublish - works on Node 0.8+\n * Uses worker for older Node versions, runs locally on Node 14+\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback): void {\n version !== 'local' ? workerWrapper(version, options, callback) : worker(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => {\n needsPublishCb(options, (error, result) => {\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error('No result returned'));\n });\n });\n}\n"],"names":["needsPublish","needsPublishCb","major","process","versions","node","split","version","_require","require","Module","createRequire","__dirname","path","dirname","__filename","url","fileURLToPath","dist","join","workerWrapper","wrapWorker","worker","options","callback","cwd","localPkg","package","JSON","parse","fs","readFileSync","private","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","extractPackageJson","hashBuffer","registry","scope","stdout","registryPkg","registryTarball","packument","latestVersion","tarballUrl","err","error","localTarball","spec","manifest","localHash","registryHash","fileComparison","localTarballPkg","registryTarballPkg","pkgJsonComparison","changes","stringStartsWith","name","undefined","trim","latest","type","significance","field","oldValue","newValue","tarball","integrity","code","message","_resolved","_integrity","substring","identical","packageJsonOnly","includeOptionalDeps","additionalSignificantFields","ignoreFields","treatNarrowingAsEquivalent","hasSignificantChanges","fieldChanges","map","fc","dependencyChanges","filter","dc","semanticChange","oldSpec","newSpec","summary","fileChanges","length","catch","Promise","resolve","reject","result","Error"],"mappings":"AAAA;;;;;;;;;;CAUC;;;;;;;;;;;QAkReA;eAAAA;;QAjBAC;eAAAA;;;yDA/PD;6DACI;2DACF;uBACU;0DACX;wBACiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGjC,8DAA8D;AAC9D,IAAMC,QAAQ,CAACC,QAAQC,QAAQ,CAACC,IAAI,CAACC,KAAK,CAAC,IAAI,CAAC,EAAE;AAClD,IAAMC,UAAUL,QAAQ,KAAK,UAAU;AACvC,IAAMM,WAAW,OAAOC,YAAY,cAAcC,eAAM,CAACC,aAAa,CAAC,uDAAmBF;AAC1F,IAAMG,YAAYC,aAAI,CAACC,OAAO,CAAC,OAAOC,eAAe,cAAcC,YAAG,CAACC,aAAa,CAAC,uDAAmBF;AACxG,IAAMG,OAAOL,aAAI,CAACM,IAAI,CAACP,WAAW;AAClC,IAAMQ,gBAAgBC,IAAAA,mBAAU,EAACR,aAAI,CAACM,IAAI,CAACD,MAAM,OAAO;AAOxD;;CAEC,GACD,SAASI,OAAOC,OAA4B,EAAEC,QAA8B;IAC1E,IAAMC,MAAMF,QAAQE,GAAG,IAAItB,QAAQsB,GAAG;IAEtC,0BAA0B;IAC1B,IAAMC,WAAwBH,QAAQI,OAAO,IAAIC,KAAKC,KAAK,CAACC,WAAE,CAACC,YAAY,CAAClB,aAAI,CAACM,IAAI,CAACM,KAAK,iBAAiB;IAE5G,wBAAwB;IACxB,IAAIC,SAASM,OAAO,EAAE;QACpBR,SAAS,MAAM;YACbxB,cAAc;YACdiC,QAAQ;QACV;QACA;IACF;IAEC,CAAA;;gBACOC,QACAC,UACAC,KACEC,UACAC,WACFC,eAG8E,MAA5EC,qBAAqBC,oBAAoBC,oBAAoBC,YAGjEC,UAEIC,OAGMC,WAUVC,aACAC,iBAQoBC,qBA+BHF,mBAYNA,oBAhDPE,WAKAC,eA+BAC,YAcCC,KACDC,OAmBJC,cAEIC,MACAC,UAKCJ,MACDC,QASFI,WACAC,cAWAC,gBAeEC,iBACAC,oBAEAC,mBAuBAC;;;;wBA3KF7B,SAAS1B,SAAS;wBAClB2B,WAAW3B,SAAS;wBACpB4B,MAAM5B,SAAS;wBACb6B,WAAa7B,SAAS,iBAAtB6B;wBACAC,YAAc9B,SAAS,QAAvB8B;wBACFC,gBAAgBD,UAAUD;wBAGoD;;4BAAM;+EAAA,QAAO;;;;wBAAb,OAAA,eAA5EG,sBAA4E,KAA5EA,qBAAqBC,qBAAuD,KAAvDA,oBAAoBC,qBAAmC,KAAnCA,oBAAoBC,aAAe,KAAfA;wBAErE,uCAAuC;wBACnCC,WAA+BrB,QAAQqB,QAAQ;6BAC/C,CAACA,UAAD;;;;wBACIC,QAAQmB,IAAAA,0BAAgB,EAACtC,SAASuC,IAAI,EAAE,OAAOvC,SAASuC,IAAI,CAAC3D,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG4D;6BAC/ErB,OAAAA;;;;;;;;;;;;wBAEmB;;4BAAMN,cAAc;gCAAQ;gCAAU;gCAAQ,GAAQ,OAANM,OAAM;;;;wBAAjEC,SAAW,cAAXA;wBACRF,WAAWE,OAAOqB,IAAI;wBACtB,IAAIvB,aAAa,aAAaA,WAAWsB;;;;;;;;;;;;;;;;;;wBAY3B;;4BAAMhC,OAAOe,SAAS,CAACvB,SAASuC,IAAI,EAAE;gCACtD9B,UAAAA;+BACIS,YAAY;gCAAEA,UAAAA;4BAAS;;;wBAFvBK,YAAY;wBAKZC,iBAAgBD,sBAAAA,SAAS,CAAC,YAAY,cAAtBA,0CAAAA,oBAAwBmB,MAAM;wBACpD,IAAI,CAAClB,eAAe;4BAClB1B,SAAS,MAAM;gCACbxB,cAAc;gCACdiC,QAAQ;gCACR8B,OAAO;oCAAG;wCAAEM,MAAM;wCAAiBC,cAAc;oCAAW;;4BAC9D;4BACA;;;wBACF;wBAEAvB,cAAcE,UAAU7C,QAAQ,CAAC8C,cAAc;wBAE/C,yCAAyC;wBACzC,IAAIxB,SAASnB,OAAO,KAAK2C,eAAe;4BACtC1B,SAAS,MAAM;gCACbxB,cAAc;gCACdiC,QAAQ,AAAC,2BAAyDiB,OAA/BxB,SAASnB,OAAO,EAAC,gBAA4B,OAAd2C,eAAc;gCAChFa,OAAO;oCACL;wCACEM,MAAM;wCACNE,OAAO;wCACPC,UAAUtB;wCACVuB,UAAU/C,SAASnB,OAAO;wCAC1B+D,cAAc;oCAChB;;4BAEJ;4BACA;;;wBACF;wBAEA,wCAAwC;wBAClCnB,cAAaJ,oBAAAA,YAAY7B,IAAI,cAAhB6B,wCAAAA,kBAAkB2B,OAAO;wBAC5C,IAAI,CAACvB,YAAY;4BACf3B,SAAS,MAAM;gCACbxB,cAAc;gCACdiC,QAAQ;gCACR8B,OAAO;oCAAG;wCAAEM,MAAM;wCAAiBC,cAAc;oCAAW;;4BAC9D;4BACA;;;wBACF;wBAEkB;;4BAAMpC,OAAOwC,OAAO,CAACvB,YAAY;gCACjDhB,UAAAA;gCACAwC,SAAS,GAAE5B,qBAAAA,YAAY7B,IAAI,cAAhB6B,yCAAAA,mBAAkB4B,SAAS;4BACxC;;;wBAHA3B,kBAAkB;;;;;;wBAIXI;wBACDC,QAAQD;wBACd,gDAAgD;wBAChD,IAAIC,MAAMuB,IAAI,KAAK,QAAQ;4BACzBpD,SAAS,MAAM;gCACbxB,cAAc;gCACdiC,QAAQ;gCACR8B,OAAO;oCAAG;wCAAEM,MAAM;wCAAiBC,cAAc;oCAAW;;4BAC9D;4BACA;;;wBACF;wBACA,4CAA4C;wBAC5C9C,SAAS,MAAM;4BACbxB,cAAc;4BACdiC,QAAQ,AAAC,4BAA4D,OAAjCoB,MAAMwB,OAAO,IAAI;wBACvD;wBACA;;;;;;;;;;wBAMMtB,OAAOnB,IAAIX;wBACA;;4BAAMS,OAAOsB,QAAQ,CAACD,MAAM;gCAAEpB,UAAAA;4BAAS;;;wBAAlDqB,WAAW;wBACF;;4BAAMtB,OAAOwC,OAAO,CAAClB,SAASsB,SAAS,EAAE;gCACtD3C,UAAAA;gCACAwC,WAAWnB,SAASuB,UAAU;4BAChC;;;wBAHAzB,eAAe;;;;;;wBAIRF;wBACDC,SAAQD;wBACd5B,SAAS,MAAM;4BACbxB,cAAc;4BACdiC,QAAQ,AAAC,gCAAgE,OAAjCoB,OAAMwB,OAAO,IAAI;wBAC3D;wBACA;;;;wBAGF,+BAA+B;wBACzBpB,YAAYd,WAAWW;wBACvBI,eAAef,WAAWK;wBAEhC,IAAIS,cAAcC,cAAc;4BAC9BlC,SAAS,MAAM;gCACbxB,cAAc;gCACdiC,QAAQ,AAAC,8BAAwD,OAA3BwB,UAAUuB,SAAS,CAAC,GAAG,KAAI;4BACnE;4BACA;;;wBACF;wBAGuB;;4BAAMxC,oBAAoBc,cAAcN;;;wBAAzDW,iBAAiB;wBAEvB,IAAIA,eAAesB,SAAS,EAAE;4BAC5B,yEAAyE;4BACzEzD,SAAS,MAAM;gCACbxB,cAAc;gCACdiC,QAAQ;4BACV;4BACA;;;wBACF;6BAGI0B,CAAAA,eAAeuB,eAAe,IAAI,CAAC3D,QAAQ2D,eAAe,AAAD,GAAzDvB;;;;wBAGuB;;4BAAMjB,mBAAmBY;;;wBAA5CM,kBAAmB;wBACG;;4BAAMlB,mBAAmBM;;;wBAA/Ca,qBAAsB;wBAEtBC,oBAAoBrB,mBAAmBmB,iBAAiBC,oBAAoB;4BAChFsB,qBAAqB5D,QAAQ4D,mBAAmB;4BAChDC,6BAA6B7D,QAAQ6D,2BAA2B;4BAChEC,cAAc9D,QAAQ8D,YAAY;4BAClCC,4BAA4B/D,QAAQ+D,0BAA0B;wBAChE;wBAEA,IAAI,CAACxB,kBAAkByB,qBAAqB,EAAE;4BAC5C/D,SAAS,MAAM;gCACbxB,cAAc;gCACdiC,QAAQ;gCACR8B,SAASD,kBAAkB0B,YAAY,CAACC,GAAG,CAAC,SAACC;2CAAQ;wCACnDrB,MAAM;wCACNE,OAAOmB,GAAGnB,KAAK;wCACfC,UAAUkB,GAAGlB,QAAQ;wCACrBC,UAAUiB,GAAGjB,QAAQ;wCACrBH,cAAc;oCAChB;;4BACF;4BACA;;;wBACF;wBAEA,qBAAqB;wBACfP,UAA0B,AAC9B,qBAAGD,kBAAkB0B,YAAY,CAACC,GAAG,CAAC,SAACC;mCAAQ;gCAC7CrB,MAAM;gCACNE,OAAOmB,GAAGnB,KAAK;gCACfC,UAAUkB,GAAGlB,QAAQ;gCACrBC,UAAUiB,GAAGjB,QAAQ;gCACrBH,cAAcoB,GAAGpB,YAAY;4BAC/B;mCACA,qBAAGR,kBAAkB6B,iBAAiB,CACnCC,MAAM,CAAC,SAACC;mCAAOA,GAAGC,cAAc,KAAK,gBAAgBD,GAAGC,cAAc,KAAK;2BAC3EL,GAAG,CAAC,SAACI;mCAAQ;gCACZxB,MAAM;gCACNE,OAAO,AAAC,GAAasB,OAAXA,GAAGxB,IAAI,EAAC,KAAW,OAARwB,GAAG5B,IAAI;gCAC5BO,UAAUqB,GAAGE,OAAO;gCACpBtB,UAAUoB,GAAGG,OAAO;gCACpB1B,cAAc;4BAChB;;wBAGJ9C,SAAS,MAAM;4BACbxB,cAAc;4BACdiC,QAAQ6B,kBAAkBmC,OAAO;4BACjClC,SAAAA;wBACF;wBACA;;;;wBAGF,8BAA8B;wBAC9BvC,SAAS,MAAM;4BACbxB,cAAc;4BACdiC,QAAQ,AAAC,0BAA2D,OAAlC0B,eAAeuC,WAAW,CAACC,MAAM,EAAC;4BACpEpC,SAASJ,eAAeuC,WAAW,CAACT,GAAG,CAAC,SAACC;uCAAQ;oCAC/CrB,MAAM;oCACNE,OAAOmB,GAAG7E,IAAI;oCACdyD,cAAc;gCAChB;;wBACF;;;;;;QACF;KAAA,IAAK8B,KAAK,CAAC5E;AACb;AAMO,SAASvB,eAAesB,OAA4B,EAAEC,QAA8B;IACzFjB,YAAY,UAAUa,cAAcb,SAASgB,SAASC,YAAYF,OAAOC,SAASC;AACpF;AAeO,SAASxB;QAAauB,UAAAA,iEAA+B,CAAC;IAC3D,OAAO,IAAI8E,QAAQ,SAACC,SAASC;QAC3BtG,eAAesB,SAAS,SAAC8B,OAAOmD;YAC9B,IAAInD,OAAOkD,OAAOlD;iBACb,IAAImD,QAAQF,QAAQE;iBACpBD,OAAO,IAAIE,MAAM;QACxB;IACF;AACF"}
@@ -27,3 +27,10 @@ export declare function getFileChangeSummary(changes: FileChange[]): string;
27
27
  * Check if changes are only in package.json
28
28
  */
29
29
  export declare function isOnlyPackageJsonChange(changes: FileChange[]): boolean;
30
+ /**
31
+ * Extract package.json from a tarball
32
+ *
33
+ * @param tarball - Tarball as Buffer (gzipped)
34
+ * @returns Parsed package.json object
35
+ */
36
+ export declare function extractPackageJson(tarball: Buffer): Promise<unknown>;
@@ -159,3 +159,17 @@ function getTar() {
159
159
  }
160
160
  return true;
161
161
  }
162
+ /**
163
+ * Extract package.json from a tarball
164
+ *
165
+ * @param tarball - Tarball as Buffer (gzipped)
166
+ * @returns Parsed package.json object
167
+ */ export async function extractPackageJson(tarball) {
168
+ const files = await extractTarball(tarball);
169
+ // Look for package.json in the tarball
170
+ const pkgJsonPath = Object.keys(files).find((p)=>p === 'package/package.json' || p.indexOf('/package.json') === p.length - 13);
171
+ if (!pkgJsonPath || !files[pkgJsonPath]) {
172
+ throw new Error('package.json not found in tarball');
173
+ }
174
+ return JSON.parse(files[pkgJsonPath].toString('utf8'));
175
+ }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/file-content.ts"],"sourcesContent":["/**\n * File content comparison for package tarballs\n *\n * Compares files between local and registry tarballs:\n * 1. Fast path: Compare tarball hashes\n * 2. If different: Extract and compare file-by-file\n * 3. Special handling for package.json (semantic comparison elsewhere)\n */\n\nimport crypto from 'crypto';\nimport Module from 'module';\nimport { Readable } from 'stream';\nimport { pipeline } from 'stream/promises';\nimport zlib from 'zlib';\nimport type { FileChange, FileComparison } from '../types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n// Lazy load tar\nlet _tar: typeof import('tar') | null = null;\n\nfunction getTar(): typeof import('tar') {\n if (!_tar) {\n _tar = _require('tar');\n }\n return _tar;\n}\n\n/**\n * Compare package files from two tarballs\n *\n * @param localTarball - Local package tarball as Buffer\n * @param registryTarball - Registry package tarball as Buffer\n * @returns File comparison result\n */\nexport async function comparePackageFiles(localTarball: Buffer, registryTarball: Buffer): Promise<FileComparison> {\n // Fast path: identical tarballs\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n return {\n identical: true,\n fileChanges: [],\n packageJsonOnly: false,\n };\n }\n\n // Extract both tarballs to memory\n const localFiles = await extractTarball(localTarball);\n const registryFiles = await extractTarball(registryTarball);\n\n const changes: FileChange[] = [];\n\n // Build combined set of all paths\n const allPaths: Record<string, boolean> = {};\n const localKeys = Object.keys(localFiles);\n for (let i = 0; i < localKeys.length; i++) {\n allPaths[localKeys[i]] = true;\n }\n const registryKeys = Object.keys(registryFiles);\n for (let i = 0; i < registryKeys.length; i++) {\n allPaths[registryKeys[i]] = true;\n }\n\n let onlyPackageJsonDiffers = true;\n\n const pathKeys = Object.keys(allPaths);\n for (let i = 0; i < pathKeys.length; i++) {\n const filePath = pathKeys[i];\n const isPackageJson = filePath === 'package/package.json' || filePath.indexOf('/package.json') === filePath.length - 13;\n const localContent = localFiles[filePath];\n const registryContent = registryFiles[filePath];\n\n if (!registryContent) {\n // File added\n changes.push({ path: filePath, action: 'added' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n } else if (!localContent) {\n // File removed\n changes.push({ path: filePath, action: 'removed' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n } else if (!buffersEqual(localContent, registryContent)) {\n // File modified\n changes.push({ path: filePath, action: 'modified' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n }\n }\n\n return {\n identical: changes.length === 0,\n fileChanges: changes,\n packageJsonOnly: onlyPackageJsonDiffers && changes.length > 0,\n };\n}\n\n/**\n * Extract tarball contents to memory\n *\n * @param tarball - Tarball as Buffer (gzipped)\n * @returns Map of file paths to content buffers\n */\nasync function extractTarball(tarball: Buffer): Promise<Record<string, Buffer>> {\n const files: Record<string, Buffer> = {};\n const tar = getTar();\n\n // Create a parser that collects file contents\n const parser = new tar.Parser({\n onReadEntry: (entry) => {\n if (entry.type === 'File') {\n const chunks: Buffer[] = [];\n\n entry.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n entry.on('end', () => {\n files[entry.path] = Buffer.concat(chunks);\n });\n } else {\n // Drain non-file entries (directories, etc.)\n entry.resume();\n }\n },\n });\n\n // Process the tarball\n await pipeline(Readable.from(tarball), zlib.createGunzip(), parser);\n\n return files;\n}\n\n/**\n * Hash a buffer using SHA-512\n */\nexport function hashBuffer(buffer: Buffer): string {\n return crypto.createHash('sha512').update(buffer).digest('base64');\n}\n\n/**\n * Compare two buffers for equality\n */\nfunction buffersEqual(a: Buffer, b: Buffer): boolean {\n if (a.length !== b.length) return false;\n return crypto.timingSafeEqual(a, b);\n}\n\n/**\n * Get a summary of file changes\n */\nexport function getFileChangeSummary(changes: FileChange[]): string {\n if (changes.length === 0) {\n return 'No file changes';\n }\n\n const added = changes.filter((c) => c.action === 'added');\n const removed = changes.filter((c) => c.action === 'removed');\n const modified = changes.filter((c) => c.action === 'modified');\n\n const parts: string[] = [];\n\n if (added.length > 0) {\n parts.push(`${added.length} added`);\n }\n if (removed.length > 0) {\n parts.push(`${removed.length} removed`);\n }\n if (modified.length > 0) {\n parts.push(`${modified.length} modified`);\n }\n\n return `Files: ${parts.join(', ')} (${changes.length} total)`;\n}\n\n/**\n * Check if changes are only in package.json\n */\nexport function isOnlyPackageJsonChange(changes: FileChange[]): boolean {\n if (changes.length === 0) return false;\n\n for (let i = 0; i < changes.length; i++) {\n const path = changes[i].path;\n if (path !== 'package/package.json' && path.indexOf('/package.json') !== path.length - 13) {\n return false;\n }\n }\n return true;\n}\n"],"names":["crypto","Module","Readable","pipeline","zlib","_require","require","createRequire","url","_tar","getTar","comparePackageFiles","localTarball","registryTarball","localHash","hashBuffer","registryHash","identical","fileChanges","packageJsonOnly","localFiles","extractTarball","registryFiles","changes","allPaths","localKeys","Object","keys","i","length","registryKeys","onlyPackageJsonDiffers","pathKeys","filePath","isPackageJson","indexOf","localContent","registryContent","push","path","action","buffersEqual","tarball","files","tar","parser","Parser","onReadEntry","entry","type","chunks","on","chunk","Buffer","concat","resume","from","createGunzip","buffer","createHash","update","digest","a","b","timingSafeEqual","getFileChangeSummary","added","filter","c","removed","modified","parts","join","isOnlyPackageJsonChange"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/file-content.ts"],"sourcesContent":["/**\n * File content comparison for package tarballs\n *\n * Compares files between local and registry tarballs:\n * 1. Fast path: Compare tarball hashes\n * 2. If different: Extract and compare file-by-file\n * 3. Special handling for package.json (semantic comparison elsewhere)\n */\n\nimport crypto from 'crypto';\nimport Module from 'module';\nimport { Readable } from 'stream';\nimport { pipeline } from 'stream/promises';\nimport zlib from 'zlib';\nimport type { FileChange, FileComparison } from '../types.ts';\n\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\n// Lazy load tar\nlet _tar: typeof import('tar') | null = null;\n\nfunction getTar(): typeof import('tar') {\n if (!_tar) {\n _tar = _require('tar');\n }\n return _tar;\n}\n\n/**\n * Compare package files from two tarballs\n *\n * @param localTarball - Local package tarball as Buffer\n * @param registryTarball - Registry package tarball as Buffer\n * @returns File comparison result\n */\nexport async function comparePackageFiles(localTarball: Buffer, registryTarball: Buffer): Promise<FileComparison> {\n // Fast path: identical tarballs\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n return {\n identical: true,\n fileChanges: [],\n packageJsonOnly: false,\n };\n }\n\n // Extract both tarballs to memory\n const localFiles = await extractTarball(localTarball);\n const registryFiles = await extractTarball(registryTarball);\n\n const changes: FileChange[] = [];\n\n // Build combined set of all paths\n const allPaths: Record<string, boolean> = {};\n const localKeys = Object.keys(localFiles);\n for (let i = 0; i < localKeys.length; i++) {\n allPaths[localKeys[i]] = true;\n }\n const registryKeys = Object.keys(registryFiles);\n for (let i = 0; i < registryKeys.length; i++) {\n allPaths[registryKeys[i]] = true;\n }\n\n let onlyPackageJsonDiffers = true;\n\n const pathKeys = Object.keys(allPaths);\n for (let i = 0; i < pathKeys.length; i++) {\n const filePath = pathKeys[i];\n const isPackageJson = filePath === 'package/package.json' || filePath.indexOf('/package.json') === filePath.length - 13;\n const localContent = localFiles[filePath];\n const registryContent = registryFiles[filePath];\n\n if (!registryContent) {\n // File added\n changes.push({ path: filePath, action: 'added' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n } else if (!localContent) {\n // File removed\n changes.push({ path: filePath, action: 'removed' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n } else if (!buffersEqual(localContent, registryContent)) {\n // File modified\n changes.push({ path: filePath, action: 'modified' });\n if (!isPackageJson) onlyPackageJsonDiffers = false;\n }\n }\n\n return {\n identical: changes.length === 0,\n fileChanges: changes,\n packageJsonOnly: onlyPackageJsonDiffers && changes.length > 0,\n };\n}\n\n/**\n * Extract tarball contents to memory\n *\n * @param tarball - Tarball as Buffer (gzipped)\n * @returns Map of file paths to content buffers\n */\nasync function extractTarball(tarball: Buffer): Promise<Record<string, Buffer>> {\n const files: Record<string, Buffer> = {};\n const tar = getTar();\n\n // Create a parser that collects file contents\n const parser = new tar.Parser({\n onReadEntry: (entry) => {\n if (entry.type === 'File') {\n const chunks: Buffer[] = [];\n\n entry.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n entry.on('end', () => {\n files[entry.path] = Buffer.concat(chunks);\n });\n } else {\n // Drain non-file entries (directories, etc.)\n entry.resume();\n }\n },\n });\n\n // Process the tarball\n await pipeline(Readable.from(tarball), zlib.createGunzip(), parser);\n\n return files;\n}\n\n/**\n * Hash a buffer using SHA-512\n */\nexport function hashBuffer(buffer: Buffer): string {\n return crypto.createHash('sha512').update(buffer).digest('base64');\n}\n\n/**\n * Compare two buffers for equality\n */\nfunction buffersEqual(a: Buffer, b: Buffer): boolean {\n if (a.length !== b.length) return false;\n return crypto.timingSafeEqual(a, b);\n}\n\n/**\n * Get a summary of file changes\n */\nexport function getFileChangeSummary(changes: FileChange[]): string {\n if (changes.length === 0) {\n return 'No file changes';\n }\n\n const added = changes.filter((c) => c.action === 'added');\n const removed = changes.filter((c) => c.action === 'removed');\n const modified = changes.filter((c) => c.action === 'modified');\n\n const parts: string[] = [];\n\n if (added.length > 0) {\n parts.push(`${added.length} added`);\n }\n if (removed.length > 0) {\n parts.push(`${removed.length} removed`);\n }\n if (modified.length > 0) {\n parts.push(`${modified.length} modified`);\n }\n\n return `Files: ${parts.join(', ')} (${changes.length} total)`;\n}\n\n/**\n * Check if changes are only in package.json\n */\nexport function isOnlyPackageJsonChange(changes: FileChange[]): boolean {\n if (changes.length === 0) return false;\n\n for (let i = 0; i < changes.length; i++) {\n const path = changes[i].path;\n if (path !== 'package/package.json' && path.indexOf('/package.json') !== path.length - 13) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Extract package.json from a tarball\n *\n * @param tarball - Tarball as Buffer (gzipped)\n * @returns Parsed package.json object\n */\nexport async function extractPackageJson(tarball: Buffer): Promise<unknown> {\n const files = await extractTarball(tarball);\n\n // Look for package.json in the tarball\n const pkgJsonPath = Object.keys(files).find((p) => p === 'package/package.json' || p.indexOf('/package.json') === p.length - 13);\n\n if (!pkgJsonPath || !files[pkgJsonPath]) {\n throw new Error('package.json not found in tarball');\n }\n\n return JSON.parse(files[pkgJsonPath].toString('utf8'));\n}\n"],"names":["crypto","Module","Readable","pipeline","zlib","_require","require","createRequire","url","_tar","getTar","comparePackageFiles","localTarball","registryTarball","localHash","hashBuffer","registryHash","identical","fileChanges","packageJsonOnly","localFiles","extractTarball","registryFiles","changes","allPaths","localKeys","Object","keys","i","length","registryKeys","onlyPackageJsonDiffers","pathKeys","filePath","isPackageJson","indexOf","localContent","registryContent","push","path","action","buffersEqual","tarball","files","tar","parser","Parser","onReadEntry","entry","type","chunks","on","chunk","Buffer","concat","resume","from","createGunzip","buffer","createHash","update","digest","a","b","timingSafeEqual","getFileChangeSummary","added","filter","c","removed","modified","parts","join","isOnlyPackageJsonChange","extractPackageJson","pkgJsonPath","find","p","Error","JSON","parse","toString"],"mappings":"AAAA;;;;;;;CAOC,GAED,OAAOA,YAAY,SAAS;AAC5B,OAAOC,YAAY,SAAS;AAC5B,SAASC,QAAQ,QAAQ,SAAS;AAClC,SAASC,QAAQ,QAAQ,kBAAkB;AAC3C,OAAOC,UAAU,OAAO;AAGxB,MAAMC,WAAW,OAAOC,YAAY,cAAcL,OAAOM,aAAa,CAAC,YAAYC,GAAG,IAAIF;AAE1F,gBAAgB;AAChB,IAAIG,OAAoC;AAExC,SAASC;IACP,IAAI,CAACD,MAAM;QACTA,OAAOJ,SAAS;IAClB;IACA,OAAOI;AACT;AAEA;;;;;;CAMC,GACD,OAAO,eAAeE,oBAAoBC,YAAoB,EAAEC,eAAuB;IACrF,gCAAgC;IAChC,MAAMC,YAAYC,WAAWH;IAC7B,MAAMI,eAAeD,WAAWF;IAEhC,IAAIC,cAAcE,cAAc;QAC9B,OAAO;YACLC,WAAW;YACXC,aAAa,EAAE;YACfC,iBAAiB;QACnB;IACF;IAEA,kCAAkC;IAClC,MAAMC,aAAa,MAAMC,eAAeT;IACxC,MAAMU,gBAAgB,MAAMD,eAAeR;IAE3C,MAAMU,UAAwB,EAAE;IAEhC,kCAAkC;IAClC,MAAMC,WAAoC,CAAC;IAC3C,MAAMC,YAAYC,OAAOC,IAAI,CAACP;IAC9B,IAAK,IAAIQ,IAAI,GAAGA,IAAIH,UAAUI,MAAM,EAAED,IAAK;QACzCJ,QAAQ,CAACC,SAAS,CAACG,EAAE,CAAC,GAAG;IAC3B;IACA,MAAME,eAAeJ,OAAOC,IAAI,CAACL;IACjC,IAAK,IAAIM,IAAI,GAAGA,IAAIE,aAAaD,MAAM,EAAED,IAAK;QAC5CJ,QAAQ,CAACM,YAAY,CAACF,EAAE,CAAC,GAAG;IAC9B;IAEA,IAAIG,yBAAyB;IAE7B,MAAMC,WAAWN,OAAOC,IAAI,CAACH;IAC7B,IAAK,IAAII,IAAI,GAAGA,IAAII,SAASH,MAAM,EAAED,IAAK;QACxC,MAAMK,WAAWD,QAAQ,CAACJ,EAAE;QAC5B,MAAMM,gBAAgBD,aAAa,0BAA0BA,SAASE,OAAO,CAAC,qBAAqBF,SAASJ,MAAM,GAAG;QACrH,MAAMO,eAAehB,UAAU,CAACa,SAAS;QACzC,MAAMI,kBAAkBf,aAAa,CAACW,SAAS;QAE/C,IAAI,CAACI,iBAAiB;YACpB,aAAa;YACbd,QAAQe,IAAI,CAAC;gBAAEC,MAAMN;gBAAUO,QAAQ;YAAQ;YAC/C,IAAI,CAACN,eAAeH,yBAAyB;QAC/C,OAAO,IAAI,CAACK,cAAc;YACxB,eAAe;YACfb,QAAQe,IAAI,CAAC;gBAAEC,MAAMN;gBAAUO,QAAQ;YAAU;YACjD,IAAI,CAACN,eAAeH,yBAAyB;QAC/C,OAAO,IAAI,CAACU,aAAaL,cAAcC,kBAAkB;YACvD,gBAAgB;YAChBd,QAAQe,IAAI,CAAC;gBAAEC,MAAMN;gBAAUO,QAAQ;YAAW;YAClD,IAAI,CAACN,eAAeH,yBAAyB;QAC/C;IACF;IAEA,OAAO;QACLd,WAAWM,QAAQM,MAAM,KAAK;QAC9BX,aAAaK;QACbJ,iBAAiBY,0BAA0BR,QAAQM,MAAM,GAAG;IAC9D;AACF;AAEA;;;;;CAKC,GACD,eAAeR,eAAeqB,OAAe;IAC3C,MAAMC,QAAgC,CAAC;IACvC,MAAMC,MAAMlC;IAEZ,8CAA8C;IAC9C,MAAMmC,SAAS,IAAID,IAAIE,MAAM,CAAC;QAC5BC,aAAa,CAACC;YACZ,IAAIA,MAAMC,IAAI,KAAK,QAAQ;gBACzB,MAAMC,SAAmB,EAAE;gBAE3BF,MAAMG,EAAE,CAAC,QAAQ,CAACC;oBAChBF,OAAOZ,IAAI,CAACc;gBACd;gBAEAJ,MAAMG,EAAE,CAAC,OAAO;oBACdR,KAAK,CAACK,MAAMT,IAAI,CAAC,GAAGc,OAAOC,MAAM,CAACJ;gBACpC;YACF,OAAO;gBACL,6CAA6C;gBAC7CF,MAAMO,MAAM;YACd;QACF;IACF;IAEA,sBAAsB;IACtB,MAAMpD,SAASD,SAASsD,IAAI,CAACd,UAAUtC,KAAKqD,YAAY,IAAIZ;IAE5D,OAAOF;AACT;AAEA;;CAEC,GACD,OAAO,SAAS5B,WAAW2C,MAAc;IACvC,OAAO1D,OAAO2D,UAAU,CAAC,UAAUC,MAAM,CAACF,QAAQG,MAAM,CAAC;AAC3D;AAEA;;CAEC,GACD,SAASpB,aAAaqB,CAAS,EAAEC,CAAS;IACxC,IAAID,EAAEjC,MAAM,KAAKkC,EAAElC,MAAM,EAAE,OAAO;IAClC,OAAO7B,OAAOgE,eAAe,CAACF,GAAGC;AACnC;AAEA;;CAEC,GACD,OAAO,SAASE,qBAAqB1C,OAAqB;IACxD,IAAIA,QAAQM,MAAM,KAAK,GAAG;QACxB,OAAO;IACT;IAEA,MAAMqC,QAAQ3C,QAAQ4C,MAAM,CAAC,CAACC,IAAMA,EAAE5B,MAAM,KAAK;IACjD,MAAM6B,UAAU9C,QAAQ4C,MAAM,CAAC,CAACC,IAAMA,EAAE5B,MAAM,KAAK;IACnD,MAAM8B,WAAW/C,QAAQ4C,MAAM,CAAC,CAACC,IAAMA,EAAE5B,MAAM,KAAK;IAEpD,MAAM+B,QAAkB,EAAE;IAE1B,IAAIL,MAAMrC,MAAM,GAAG,GAAG;QACpB0C,MAAMjC,IAAI,CAAC,GAAG4B,MAAMrC,MAAM,CAAC,MAAM,CAAC;IACpC;IACA,IAAIwC,QAAQxC,MAAM,GAAG,GAAG;QACtB0C,MAAMjC,IAAI,CAAC,GAAG+B,QAAQxC,MAAM,CAAC,QAAQ,CAAC;IACxC;IACA,IAAIyC,SAASzC,MAAM,GAAG,GAAG;QACvB0C,MAAMjC,IAAI,CAAC,GAAGgC,SAASzC,MAAM,CAAC,SAAS,CAAC;IAC1C;IAEA,OAAO,CAAC,OAAO,EAAE0C,MAAMC,IAAI,CAAC,MAAM,EAAE,EAAEjD,QAAQM,MAAM,CAAC,OAAO,CAAC;AAC/D;AAEA;;CAEC,GACD,OAAO,SAAS4C,wBAAwBlD,OAAqB;IAC3D,IAAIA,QAAQM,MAAM,KAAK,GAAG,OAAO;IAEjC,IAAK,IAAID,IAAI,GAAGA,IAAIL,QAAQM,MAAM,EAAED,IAAK;QACvC,MAAMW,OAAOhB,OAAO,CAACK,EAAE,CAACW,IAAI;QAC5B,IAAIA,SAAS,0BAA0BA,KAAKJ,OAAO,CAAC,qBAAqBI,KAAKV,MAAM,GAAG,IAAI;YACzF,OAAO;QACT;IACF;IACA,OAAO;AACT;AAEA;;;;;CAKC,GACD,OAAO,eAAe6C,mBAAmBhC,OAAe;IACtD,MAAMC,QAAQ,MAAMtB,eAAeqB;IAEnC,uCAAuC;IACvC,MAAMiC,cAAcjD,OAAOC,IAAI,CAACgB,OAAOiC,IAAI,CAAC,CAACC,IAAMA,MAAM,0BAA0BA,EAAE1C,OAAO,CAAC,qBAAqB0C,EAAEhD,MAAM,GAAG;IAE7H,IAAI,CAAC8C,eAAe,CAAChC,KAAK,CAACgC,YAAY,EAAE;QACvC,MAAM,IAAIG,MAAM;IAClB;IAEA,OAAOC,KAAKC,KAAK,CAACrC,KAAK,CAACgC,YAAY,CAACM,QAAQ,CAAC;AAChD"}
@@ -2,6 +2,6 @@
2
2
  * Comparators - Core comparison logic for npm-needs-publish
3
3
  */
4
4
  export { compareDependencies, getDependencyChangeSummary, hasSignificantDependencyChanges, } from './dependency.js';
5
- export { comparePackageFiles, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange, } from './file-content.js';
5
+ export { comparePackageFiles, extractPackageJson, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange, } from './file-content.js';
6
6
  export { comparePackageJson, isSignificantField, } from './package-json.js';
7
7
  export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions, } from './version-specifier.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Comparators - Core comparison logic for npm-needs-publish
3
3
  */ export { compareDependencies, getDependencyChangeSummary, hasSignificantDependencyChanges } from './dependency.js';
4
- export { comparePackageFiles, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange } from './file-content.js';
4
+ export { comparePackageFiles, extractPackageJson, getFileChangeSummary, hashBuffer, isOnlyPackageJsonChange } from './file-content.js';
5
5
  export { comparePackageJson, isSignificantField } from './package-json.js';
6
6
  export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier } from './version-specifier.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/index.ts"],"sourcesContent":["/**\n * Comparators - Core comparison logic for npm-needs-publish\n */\n\nexport {\n compareDependencies,\n getDependencyChangeSummary,\n hasSignificantDependencyChanges,\n} from './dependency.ts';\nexport {\n comparePackageFiles,\n getFileChangeSummary,\n hashBuffer,\n isOnlyPackageJsonChange,\n} from './file-content.ts';\n\nexport {\n comparePackageJson,\n isSignificantField,\n} from './package-json.ts';\nexport {\n compareVersionSpecifiers,\n comparisonToSemanticChange,\n parseVersionSpecifier,\n type SemanticChangeOptions,\n} from './version-specifier.ts';\n"],"names":["compareDependencies","getDependencyChangeSummary","hasSignificantDependencyChanges","comparePackageFiles","getFileChangeSummary","hashBuffer","isOnlyPackageJsonChange","comparePackageJson","isSignificantField","compareVersionSpecifiers","comparisonToSemanticChange","parseVersionSpecifier"],"mappings":"AAAA;;CAEC,GAED,SACEA,mBAAmB,EACnBC,0BAA0B,EAC1BC,+BAA+B,QAC1B,kBAAkB;AACzB,SACEC,mBAAmB,EACnBC,oBAAoB,EACpBC,UAAU,EACVC,uBAAuB,QAClB,oBAAoB;AAE3B,SACEC,kBAAkB,EAClBC,kBAAkB,QACb,oBAAoB;AAC3B,SACEC,wBAAwB,EACxBC,0BAA0B,EAC1BC,qBAAqB,QAEhB,yBAAyB"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/comparators/index.ts"],"sourcesContent":["/**\n * Comparators - Core comparison logic for npm-needs-publish\n */\n\nexport {\n compareDependencies,\n getDependencyChangeSummary,\n hasSignificantDependencyChanges,\n} from './dependency.ts';\nexport {\n comparePackageFiles,\n extractPackageJson,\n getFileChangeSummary,\n hashBuffer,\n isOnlyPackageJsonChange,\n} from './file-content.ts';\n\nexport {\n comparePackageJson,\n isSignificantField,\n} from './package-json.ts';\nexport {\n compareVersionSpecifiers,\n comparisonToSemanticChange,\n parseVersionSpecifier,\n type SemanticChangeOptions,\n} from './version-specifier.ts';\n"],"names":["compareDependencies","getDependencyChangeSummary","hasSignificantDependencyChanges","comparePackageFiles","extractPackageJson","getFileChangeSummary","hashBuffer","isOnlyPackageJsonChange","comparePackageJson","isSignificantField","compareVersionSpecifiers","comparisonToSemanticChange","parseVersionSpecifier"],"mappings":"AAAA;;CAEC,GAED,SACEA,mBAAmB,EACnBC,0BAA0B,EAC1BC,+BAA+B,QAC1B,kBAAkB;AACzB,SACEC,mBAAmB,EACnBC,kBAAkB,EAClBC,oBAAoB,EACpBC,UAAU,EACVC,uBAAuB,QAClB,oBAAoB;AAE3B,SACEC,kBAAkB,EAClBC,kBAAkB,QACb,oBAAoB;AAC3B,SACEC,wBAAwB,EACxBC,0BAA0B,EAC1BC,qBAAqB,QAEhB,yBAAyB"}
@@ -18,6 +18,7 @@
18
18
  * ```
19
19
  */
20
20
  export { compareDependencies } from './comparators/dependency.js';
21
+ export { extractPackageJson } from './comparators/file-content.js';
21
22
  export { comparePackageJson } from './comparators/package-json.js';
22
23
  export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions } from './comparators/version-specifier.js';
23
24
  export { type NeedsPublishCallback, needsPublish, needsPublishCb } from './needs-publish.js';
package/dist/esm/index.js CHANGED
@@ -18,6 +18,7 @@
18
18
  * ```
19
19
  */ // Comparators
20
20
  export { compareDependencies } from './comparators/dependency.js';
21
+ export { extractPackageJson } from './comparators/file-content.js';
21
22
  export { comparePackageJson } from './comparators/package-json.js';
22
23
  export { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier } from './comparators/version-specifier.js';
23
24
  // Main API
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/index.ts"],"sourcesContent":["/**\n * npm-needs-publish\n *\n * Smart publish detection for npm packages - semantic package.json comparison\n * with semver-aware dependency analysis\n *\n * @example\n * ```typescript\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * // Proceed with npm publish\n * } else {\n * console.log('No publish needed:', result.reason);\n * }\n * ```\n */\n\n// Comparators\nexport { compareDependencies } from './comparators/dependency.ts';\nexport { comparePackageJson } from './comparators/package-json.ts';\nexport { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions } from './comparators/version-specifier.ts';\n// Main API\nexport { type NeedsPublishCallback, needsPublish, needsPublishCb } from './needs-publish.ts';\n\n// Types\nexport type {\n ChangeDetail,\n CompareOptions,\n CompareSpecifierOptions,\n DependencyChange,\n DependencyCompareOptions,\n DependencyComparison,\n FieldChange,\n FileChange,\n FileComparison,\n NeedsPublishOptions,\n NeedsPublishResult,\n PackageJson,\n PackageJsonComparison,\n ParsedVersionSpecifier,\n SemanticChange,\n SpecifierComparison,\n VersionSpecifierType,\n} from './types.ts';\n"],"names":["compareDependencies","comparePackageJson","compareVersionSpecifiers","comparisonToSemanticChange","parseVersionSpecifier","needsPublish","needsPublishCb"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;CAkBC,GAED,cAAc;AACd,SAASA,mBAAmB,QAAQ,8BAA8B;AAClE,SAASC,kBAAkB,QAAQ,gCAAgC;AACnE,SAASC,wBAAwB,EAAEC,0BAA0B,EAAEC,qBAAqB,QAAoC,qCAAqC;AAC7J,WAAW;AACX,SAAoCC,YAAY,EAAEC,cAAc,QAAQ,qBAAqB"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/index.ts"],"sourcesContent":["/**\n * npm-needs-publish\n *\n * Smart publish detection for npm packages - semantic package.json comparison\n * with semver-aware dependency analysis\n *\n * @example\n * ```typescript\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * // Proceed with npm publish\n * } else {\n * console.log('No publish needed:', result.reason);\n * }\n * ```\n */\n\n// Comparators\nexport { compareDependencies } from './comparators/dependency.ts';\nexport { extractPackageJson } from './comparators/file-content.ts';\nexport { comparePackageJson } from './comparators/package-json.ts';\nexport { compareVersionSpecifiers, comparisonToSemanticChange, parseVersionSpecifier, type SemanticChangeOptions } from './comparators/version-specifier.ts';\n// Main API\nexport { type NeedsPublishCallback, needsPublish, needsPublishCb } from './needs-publish.ts';\n\n// Types\nexport type {\n ChangeDetail,\n CompareOptions,\n CompareSpecifierOptions,\n DependencyChange,\n DependencyCompareOptions,\n DependencyComparison,\n FieldChange,\n FileChange,\n FileComparison,\n NeedsPublishOptions,\n NeedsPublishResult,\n PackageJson,\n PackageJsonComparison,\n ParsedVersionSpecifier,\n SemanticChange,\n SpecifierComparison,\n VersionSpecifierType,\n} from './types.ts';\n"],"names":["compareDependencies","extractPackageJson","comparePackageJson","compareVersionSpecifiers","comparisonToSemanticChange","parseVersionSpecifier","needsPublish","needsPublishCb"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;CAkBC,GAED,cAAc;AACd,SAASA,mBAAmB,QAAQ,8BAA8B;AAClE,SAASC,kBAAkB,QAAQ,gCAAgC;AACnE,SAASC,kBAAkB,QAAQ,gCAAgC;AACnE,SAASC,wBAAwB,EAAEC,0BAA0B,EAAEC,qBAAqB,QAAoC,qCAAqC;AAC7J,WAAW;AACX,SAAoCC,YAAY,EAAEC,cAAc,QAAQ,qBAAqB"}
@@ -43,7 +43,7 @@ const workerWrapper = wrapWorker(path.join(dist, 'cjs', 'needs-publish.js'));
43
43
  const { promisify } = _require('util');
44
44
  const execFileAsync = promisify(execFile);
45
45
  // Dynamic import for comparators (they use modern features)
46
- const { comparePackageFiles, comparePackageJson, hashBuffer } = await import('./comparators/index.js');
46
+ const { comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer } = await import('./comparators/index.js');
47
47
  // Get registry URL for scoped packages
48
48
  let registry = options.registry;
49
49
  if (!registry) {
@@ -188,7 +188,11 @@ const workerWrapper = wrapWorker(path.join(dist, 'cjs', 'needs-publish.js'));
188
188
  }
189
189
  // Step 6: If only package.json differs, do semantic comparison
190
190
  if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {
191
- const pkgJsonComparison = comparePackageJson(localPkg, registryPkg, {
191
+ // Extract package.json from both tarballs for accurate comparison
192
+ // (packument metadata is missing fields like 'files')
193
+ const localTarballPkg = await extractPackageJson(localTarball);
194
+ const registryTarballPkg = await extractPackageJson(registryTarball);
195
+ const pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {
192
196
  includeOptionalDeps: options.includeOptionalDeps,
193
197
  additionalSignificantFields: options.additionalSignificantFields,
194
198
  ignoreFields: options.ignoreFields,
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/needs-publish.ts"],"sourcesContent":["/**\n * Main orchestration logic for npm-needs-publish\n *\n * Algorithm:\n * 1. Fetch registry packument → if E404, return needsPublish=true (first publish)\n * 2. Version check → if different, return needsPublish=true (intentional bump)\n * 3. Fast hash check → if identical, return needsPublish=false (no changes)\n * 4. Extract both tarballs, compare file-by-file (excluding package.json)\n * 5. If non-package.json files differ → return needsPublish=true\n * 6. If only package.json differs → do semantic comparison\n */\n\nimport fs from 'fs';\nimport Module from 'module';\nimport path from 'path';\nimport { wrapWorker } from 'tsds-lib';\nimport url from 'url';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\n// Version check for worker pattern - use worker for Node < 14\nconst major = +process.versions.node.split('.')[0];\nconst version = major > 14 ? 'local' : 'stable';\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\nconst __dirname = path.dirname(typeof __filename === 'undefined' ? url.fileURLToPath(import.meta.url) : __filename);\nconst dist = path.join(__dirname, '..');\nconst workerWrapper = wrapWorker(path.join(dist, 'cjs', 'needs-publish.js'));\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\n/**\n * Worker function that runs on Node 14+\n */\nfunction worker(options: NeedsPublishOptions, callback: NeedsPublishCallback): undefined {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private) {\n callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n return;\n }\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl) {\n callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n const pkgJsonComparison = comparePackageJson(localPkg, registryPkg, {\n includeOptionalDeps: options.includeOptionalDeps,\n additionalSignificantFields: options.additionalSignificantFields,\n ignoreFields: options.ignoreFields,\n treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent,\n });\n\n if (!pkgJsonComparison.hasSignificantChanges) {\n callback(null, {\n needsPublish: false,\n reason: 'Package.json changes are not significant for consumers',\n changes: pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: 'informational' as const,\n })),\n });\n return;\n }\n\n // Build changes list\n const changes: ChangeDetail[] = [\n ...pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: fc.significance,\n })),\n ...pkgJsonComparison.dependencyChanges\n .filter((dc) => dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none')\n .map((dc) => ({\n type: 'dependency' as const,\n field: `${dc.type}.${dc.name}`,\n oldValue: dc.oldSpec,\n newValue: dc.newSpec,\n significance: 'significant' as const,\n })),\n ];\n\n callback(null, {\n needsPublish: true,\n reason: pkgJsonComparison.summary,\n changes,\n });\n return;\n }\n\n // Step 7: Other files changed\n callback(null, {\n needsPublish: true,\n reason: `Code changes detected (${fileComparison.fileChanges.length} files changed)`,\n changes: fileComparison.fileChanges.map((fc) => ({\n type: 'file' as const,\n field: fc.path,\n significance: 'significant' as const,\n })),\n });\n })().catch(callback);\n}\n\n/**\n * Callback-based needsPublish - works on Node 0.8+\n * Uses worker for older Node versions, runs locally on Node 14+\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback): void {\n version !== 'local' ? workerWrapper(version, options, callback) : worker(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => {\n needsPublishCb(options, (error, result) => {\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error('No result returned'));\n });\n });\n}\n"],"names":["fs","Module","path","wrapWorker","url","stringStartsWith","major","process","versions","node","split","version","_require","require","createRequire","__dirname","dirname","__filename","fileURLToPath","dist","join","workerWrapper","worker","options","callback","cwd","localPkg","package","JSON","parse","readFileSync","private","needsPublish","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","hashBuffer","registry","scope","name","undefined","stdout","trim","registryPkg","registryTarball","packument","latestVersion","latest","changes","type","significance","field","oldValue","newValue","tarballUrl","tarball","integrity","err","error","code","message","localTarball","spec","manifest","_resolved","_integrity","localHash","registryHash","substring","fileComparison","identical","packageJsonOnly","pkgJsonComparison","includeOptionalDeps","additionalSignificantFields","ignoreFields","treatNarrowingAsEquivalent","hasSignificantChanges","fieldChanges","map","fc","dependencyChanges","filter","dc","semanticChange","oldSpec","newSpec","summary","fileChanges","length","catch","needsPublishCb","Promise","resolve","reject","result","Error"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/npm/npm-needs-publish/src/needs-publish.ts"],"sourcesContent":["/**\n * Main orchestration logic for npm-needs-publish\n *\n * Algorithm:\n * 1. Fetch registry packument → if E404, return needsPublish=true (first publish)\n * 2. Version check → if different, return needsPublish=true (intentional bump)\n * 3. Fast hash check → if identical, return needsPublish=false (no changes)\n * 4. Extract both tarballs, compare file-by-file (excluding package.json)\n * 5. If non-package.json files differ → return needsPublish=true\n * 6. If only package.json differs → do semantic comparison\n */\n\nimport fs from 'fs';\nimport Module from 'module';\nimport path from 'path';\nimport { wrapWorker } from 'tsds-lib';\nimport url from 'url';\nimport { stringStartsWith } from './compat.ts';\nimport type { ChangeDetail, NeedsPublishOptions, NeedsPublishResult, PackageJson } from './types.ts';\n\n// Version check for worker pattern - use worker for Node < 14\nconst major = +process.versions.node.split('.')[0];\nconst version = major > 14 ? 'local' : 'stable';\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\nconst __dirname = path.dirname(typeof __filename === 'undefined' ? url.fileURLToPath(import.meta.url) : __filename);\nconst dist = path.join(__dirname, '..');\nconst workerWrapper = wrapWorker(path.join(dist, 'cjs', 'needs-publish.js'));\n\n/**\n * Callback type for needsPublish\n */\nexport type NeedsPublishCallback = (error: Error | null, result?: NeedsPublishResult) => void;\n\n/**\n * Worker function that runs on Node 14+\n */\nfunction worker(options: NeedsPublishOptions, callback: NeedsPublishCallback): undefined {\n const cwd = options.cwd || process.cwd();\n\n // Load local package.json\n const localPkg: PackageJson = options.package || JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));\n\n // Skip private packages\n if (localPkg.private) {\n callback(null, {\n needsPublish: false,\n reason: 'Package is private',\n });\n return;\n }\n\n (async () => {\n const pacote = _require('pacote');\n const Arborist = _require('@npmcli/arborist');\n const npa = _require('npm-package-arg');\n const { execFile } = _require('child_process');\n const { promisify } = _require('util');\n const execFileAsync = promisify(execFile);\n\n // Dynamic import for comparators (they use modern features)\n const { comparePackageFiles, comparePackageJson, extractPackageJson, hashBuffer } = await import('./comparators/index.ts');\n\n // Get registry URL for scoped packages\n let registry: string | undefined = options.registry;\n if (!registry) {\n const scope = stringStartsWith(localPkg.name, '@') ? localPkg.name.split('/')[0] : undefined;\n if (scope) {\n try {\n const { stdout } = await execFileAsync('npm', ['config', 'get', `${scope}:registry`]);\n registry = stdout.trim();\n if (registry === 'undefined') registry = undefined;\n } catch {\n // Fallback to default registry\n }\n }\n }\n\n // Step 1: Try to fetch registry packument\n let registryPkg: PackageJson;\n let registryTarball: Buffer;\n\n try {\n const packument = await pacote.packument(localPkg.name, {\n Arborist,\n ...(registry && { registry }),\n });\n\n const latestVersion = packument['dist-tags']?.latest;\n if (!latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: 'No latest version found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryPkg = packument.versions[latestVersion] as unknown as PackageJson;\n\n // Step 2: Version comparison (fast path)\n if (localPkg.version !== latestVersion) {\n callback(null, {\n needsPublish: true,\n reason: `Version differs (local: ${localPkg.version}, registry: ${latestVersion})`,\n changes: [\n {\n type: 'version',\n field: 'version',\n oldValue: latestVersion,\n newValue: localPkg.version,\n significance: 'critical',\n },\n ],\n });\n return;\n }\n\n // Fetch registry tarball for comparison\n const tarballUrl = registryPkg.dist?.tarball;\n if (!tarballUrl) {\n callback(null, {\n needsPublish: true,\n reason: 'Registry package has no tarball URL',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n\n registryTarball = await pacote.tarball(tarballUrl, {\n Arborist,\n integrity: registryPkg.dist?.integrity,\n });\n } catch (err: unknown) {\n const error = err as { code?: string; message?: string };\n // Package not found in registry (first publish)\n if (error.code === 'E404') {\n callback(null, {\n needsPublish: true,\n reason: 'Package not found in registry (first publish)',\n changes: [{ type: 'first-publish', significance: 'critical' }],\n });\n return;\n }\n // Unknown error - assume changed to be safe\n callback(null, {\n needsPublish: true,\n reason: `Error checking registry: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 3: Pack local package\n let localTarball: Buffer;\n try {\n const spec = npa(cwd);\n const manifest = await pacote.manifest(spec, { Arborist });\n localTarball = await pacote.tarball(manifest._resolved, {\n Arborist,\n integrity: manifest._integrity,\n });\n } catch (err: unknown) {\n const error = err as { message?: string };\n callback(null, {\n needsPublish: true,\n reason: `Error packing local package: ${error.message || 'Unknown error'}`,\n });\n return;\n }\n\n // Step 4: Fast hash comparison\n const localHash = hashBuffer(localTarball);\n const registryHash = hashBuffer(registryTarball);\n\n if (localHash === registryHash) {\n callback(null, {\n needsPublish: false,\n reason: `No changes detected (hash: ${localHash.substring(0, 16)}...)`,\n });\n return;\n }\n\n // Step 5: File-by-file comparison\n const fileComparison = await comparePackageFiles(localTarball, registryTarball);\n\n if (fileComparison.identical) {\n // Hash mismatch but files identical - likely tarball metadata difference\n callback(null, {\n needsPublish: false,\n reason: 'Files identical (tarball metadata differs)',\n });\n return;\n }\n\n // Step 6: If only package.json differs, do semantic comparison\n if (fileComparison.packageJsonOnly && !options.packageJsonOnly) {\n // Extract package.json from both tarballs for accurate comparison\n // (packument metadata is missing fields like 'files')\n const localTarballPkg = (await extractPackageJson(localTarball)) as PackageJson;\n const registryTarballPkg = (await extractPackageJson(registryTarball)) as PackageJson;\n\n const pkgJsonComparison = comparePackageJson(localTarballPkg, registryTarballPkg, {\n includeOptionalDeps: options.includeOptionalDeps,\n additionalSignificantFields: options.additionalSignificantFields,\n ignoreFields: options.ignoreFields,\n treatNarrowingAsEquivalent: options.treatNarrowingAsEquivalent,\n });\n\n if (!pkgJsonComparison.hasSignificantChanges) {\n callback(null, {\n needsPublish: false,\n reason: 'Package.json changes are not significant for consumers',\n changes: pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: 'informational' as const,\n })),\n });\n return;\n }\n\n // Build changes list\n const changes: ChangeDetail[] = [\n ...pkgJsonComparison.fieldChanges.map((fc) => ({\n type: 'field' as const,\n field: fc.field,\n oldValue: fc.oldValue,\n newValue: fc.newValue,\n significance: fc.significance,\n })),\n ...pkgJsonComparison.dependencyChanges\n .filter((dc) => dc.semanticChange !== 'equivalent' && dc.semanticChange !== 'none')\n .map((dc) => ({\n type: 'dependency' as const,\n field: `${dc.type}.${dc.name}`,\n oldValue: dc.oldSpec,\n newValue: dc.newSpec,\n significance: 'significant' as const,\n })),\n ];\n\n callback(null, {\n needsPublish: true,\n reason: pkgJsonComparison.summary,\n changes,\n });\n return;\n }\n\n // Step 7: Other files changed\n callback(null, {\n needsPublish: true,\n reason: `Code changes detected (${fileComparison.fileChanges.length} files changed)`,\n changes: fileComparison.fileChanges.map((fc) => ({\n type: 'file' as const,\n field: fc.path,\n significance: 'significant' as const,\n })),\n });\n })().catch(callback);\n}\n\n/**\n * Callback-based needsPublish - works on Node 0.8+\n * Uses worker for older Node versions, runs locally on Node 14+\n */\nexport function needsPublishCb(options: NeedsPublishOptions, callback: NeedsPublishCallback): void {\n version !== 'local' ? workerWrapper(version, options, callback) : worker(options, callback);\n}\n\n/**\n * Determine if a package needs to be published to npm.\n *\n * @example\n * ```ts\n * import { needsPublish } from 'npm-needs-publish';\n *\n * const result = await needsPublish({ cwd: process.cwd() });\n * if (result.needsPublish) {\n * console.log('Publish needed:', result.reason);\n * }\n * ```\n */\nexport function needsPublish(options: NeedsPublishOptions = {}): Promise<NeedsPublishResult> {\n return new Promise((resolve, reject) => {\n needsPublishCb(options, (error, result) => {\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error('No result returned'));\n });\n });\n}\n"],"names":["fs","Module","path","wrapWorker","url","stringStartsWith","major","process","versions","node","split","version","_require","require","createRequire","__dirname","dirname","__filename","fileURLToPath","dist","join","workerWrapper","worker","options","callback","cwd","localPkg","package","JSON","parse","readFileSync","private","needsPublish","reason","pacote","Arborist","npa","execFile","promisify","execFileAsync","comparePackageFiles","comparePackageJson","extractPackageJson","hashBuffer","registry","scope","name","undefined","stdout","trim","registryPkg","registryTarball","packument","latestVersion","latest","changes","type","significance","field","oldValue","newValue","tarballUrl","tarball","integrity","err","error","code","message","localTarball","spec","manifest","_resolved","_integrity","localHash","registryHash","substring","fileComparison","identical","packageJsonOnly","localTarballPkg","registryTarballPkg","pkgJsonComparison","includeOptionalDeps","additionalSignificantFields","ignoreFields","treatNarrowingAsEquivalent","hasSignificantChanges","fieldChanges","map","fc","dependencyChanges","filter","dc","semanticChange","oldSpec","newSpec","summary","fileChanges","length","catch","needsPublishCb","Promise","resolve","reject","result","Error"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npm-needs-publish",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Smart publish detection for npm packages - semantic package.json comparison with semver-aware dependency analysis",
5
5
  "keywords": [
6
6
  "npm",