@cnpmjs/packument

@cnpmjs/packument is a package metadata helper for Node.js.
It provides a way to parse package metadata from buffer and diff versions between local and remote.
Based on sonic-rs to parse JSON efficiently.
Features
Install
yarn add @cnpmjs/packument
Usage
Basic Usage
import { Package } from '@cnpmjs/packument'
import { readFileSync } from 'fs'
const buffer = readFileSync('path/to/packument.json')
const pkg = new Package(buffer)
console.log(pkg.name)
console.log(pkg.description)
console.log(pkg.readme)
console.log(pkg.distTags)
console.log(pkg.maintainers)
console.log(pkg.repository)
console.log(pkg.time)
console.log(pkg.isUnpublished)
const latestVersion = pkg.getLatestVersion()
console.log(latestVersion)
const versions = pkg.versions
console.log(versions)
Diff Versions Between Local and Remote
The diff method helps you find the difference between local package versions and remote package versions. This is useful for package synchronization scenarios.
import { Package } from '@cnpmjs/packument'
import { readFileSync } from 'fs'
const localVersions = ['1.0.0', '1.0.1', '1.0.2']
const remoteBuffer = readFileSync('path/to/remote-packument.json')
const remotePkg = new Package(remoteBuffer)
const diff = remotePkg.diff(localVersions)
console.log(diff.addedVersions)
console.log(diff.removedVersions)
The addedVersions array includes position information (startPos, endPos) which allows you to extract the raw JSON metadata for each version directly from the buffer without parsing the entire package:
const diff = remotePkg.diff(localVersions)
for (const [version, [start, end]] of diff.addedVersions) {
const versionMetadata = remoteBuffer.subarray(start, end)
const versionData = JSON.parse(versionMetadata)
console.log(`Version ${version}:`, versionData)
}
This approach is much more efficient than parsing the entire package JSON when you only need specific version metadata.
Modify JSON Without Full Parsing (JSONBuilder)
The JSONBuilder class allows you to set or delete properties in a JSON buffer without parsing the entire document. This is useful for modifying large packument files efficiently.
import { JSONBuilder } from '@cnpmjs/packument'
import { readFileSync, writeFileSync } from 'fs'
const buffer = readFileSync('path/to/packument.json')
const builder = new JSONBuilder(buffer)
builder.setIn(['dist-tags', 'latest'], '2.0.0')
builder.setIn(['versions', '2.0.0'], {
name: 'my-package',
version: '2.0.0',
dist: {
shasum: 'abc123',
tarball: 'https://registry.npmjs.org/my-package/-/my-package-2.0.0.tgz',
},
})
builder.deleteIn(['versions', '1.0.0-deprecated'])
builder.deleteIn(['versions', '0.0.1'], { autoDeleteParentIfEmpty: true })
const modifiedBuffer = builder.build()
writeFileSync('path/to/packument.json', modifiedBuffer)
Supported value types for setIn:
string
number
boolean
Date (serialized to ISO string)
object (serialized to JSON)
Benchmark
npm run bench
Node.js v24.11.1 Result
βββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββ¬ββββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬ββββββββββ
β (index) β Task name β Latency avg (ns) β Latency med (ns) β Throughput avg (ops/s) β Throughput med (ops/s) β Samples β
βββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββΌββββββββββββββββββββββββΌβββββββββββββββββββββββββΌβββββββββββββββββββββββββΌββββββββββ€
β 0 β 'JSONParse small data readme string (117KB)' β '266169 Β± 1.79%' β '256167 Β± 6458.0' β '3880 Β± 0.22%' β '3904 Β± 98' β 3758 β
β 1 β 'sonic-rs small data readme string (117KB)' β '96116 Β± 0.18%' β '93959 Β± 3334.0' β '10449 Β± 0.11%' β '10643 Β± 392' β 10405 β
β 2 β 'sonic-rs small data readme string with position (117KB)' β '104260 Β± 0.18%' β '101875 Β± 3541.0' β '9636 Β± 0.12%' β '9816 Β± 349' β 9592 β
β 3 β 'sonic-rs small data readme JSON buffer with position (117KB)' β '80798 Β± 0.10%' β '78125 Β± 1250.0' β '12415 Β± 0.09%' β '12800 Β± 202' β 12377 β
β 4 β 'JSONParse large data readme string (22MB)' β '61213649 Β± 0.46%' β '60913854 Β± 457520' β '16 Β± 0.46%' β '16 Β± 0' β 64 β
β 5 β 'sonic-rs large data readme string (22MB)' β '14408984 Β± 0.77%' β '14446271 Β± 314188' β '69 Β± 0.77%' β '69 Β± 2' β 70 β
β 6 β 'sonic-rs large data readme string with position (22MB)' β '14422542 Β± 0.82%' β '14286833 Β± 445437' β '69 Β± 0.81%' β '70 Β± 2' β 70 β
β 7 β 'sonic-rs large data readme JSON buffer with position (22MB)' β '14601161 Β± 0.90%' β '14690709 Β± 292041' β '69 Β± 0.90%' β '68 Β± 1' β 69 β
β 8 β 'JSONParse super large data readme string (89M)' β '156435868 Β± 1.25%' β '152015667 Β± 2226000' β '6 Β± 1.21%' β '7 Β± 0' β 64 β
β 9 β 'sonic-rs super large data readme string (89M)' β '52196231 Β± 0.78%' β '51696729 Β± 991125' β '19 Β± 0.77%' β '19 Β± 0' β 64 β
β 10 β 'sonic-rs super large data readme string with position (89M)' β '52017035 Β± 0.87%' β '51689855 Β± 1427041' β '19 Β± 0.86%' β '19 Β± 1' β 64 β
β 11 β 'sonic-rs super large data readme JSON buffer with position (89M)' β '52177505 Β± 0.85%' β '51624876 Β± 1226542' β '19 Β± 0.83%' β '19 Β± 0' β 64 β
β 12 β 'JSONParse big readme string (229KB, 64KB readme)' β '335244 Β± 1.66%' β '323958 Β± 6624.0' β '3071 Β± 0.26%' β '3087 Β± 63' β 2983 β
β 13 β 'sonic-rs big readme string (229KB, 64KB readme)' β '146211 Β± 0.21%' β '147375 Β± 4500.0' β '6867 Β± 0.13%' β '6785 Β± 209' β 6840 β
β 14 β 'sonic-rs big readme string with position (229KB, 64KB readme)' β '173274 Β± 0.19%' β '173729 Β± 5771.0' β '5791 Β± 0.14%' β '5756 Β± 192' β 5772 β
β 15 β 'sonic-rs big readme JSON buffer with position (229KB, 64KB readme)' β '126633 Β± 0.13%' β '127208 Β± 5292.0' β '7922 Β± 0.12%' β '7861 Β± 331' β 7897 β
βββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ΄ββββββββββββββββββββββ΄ββββββββββββββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββββββββββββββββ΄ββββββββββ
Memory Usage:
JSONParse description (22M): 478.1 MB (min: 477.9 MB, max: 478.5 MB)
JSONParse description (89M): 792.1 MB (min: 791.5 MB, max: 792.9 MB)
SonicJSONParse description (22M): 91.4 MB (min: 91.2 MB, max: 91.6 MB)
SonicJSONParse description (89M): 158.5 MB (min: 158.3 MB, max: 158.6 MB)
Node.js v22.21.1 Result
βββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬ββββββββββ
β (index) β Task name β Latency avg (ns) β Latency med (ns) β Throughput avg (ops/s) β Throughput med (ops/s) β Samples β
βββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββΌβββββββββββββββββββββββββΌβββββββββββββββββββββββββΌβββββββββββββββββββββββββΌββββββββββ€
β 0 β 'JSONParse small data readme string (117KB)' β '259742 Β± 0.52%' β '247230 Β± 8917.0' β '3913 Β± 0.34%' β '4045 Β± 149' β 3850 β
β 1 β 'sonic-rs small data readme string (117KB)' β '98949 Β± 0.29%' β '98417 Β± 4999.0' β '10189 Β± 0.14%' β '10161 Β± 515' β 10107 β
β 2 β 'sonic-rs small data readme string with position (117KB)' β '107065 Β± 0.22%' β '105917 Β± 5249.0' β '9404 Β± 0.14%' β '9441 Β± 456' β 9341 β
β 3 β 'sonic-rs small data readme JSON buffer with position (117KB)' β '86159 Β± 0.49%' β '87666 Β± 1084.0' β '11706 Β± 0.12%' β '11407 Β± 143' β 11607 β
β 4 β 'JSONParse large data readme string (22MB)' β '76834607 Β± 5.25%' β '66358500 Β± 2121250' β '14 Β± 4.62%' β '15 Β± 0' β 64 β
β 5 β 'sonic-rs large data readme string (22MB)' β '14920809 Β± 0.71%' β '14854833 Β± 258396' β '67 Β± 0.69%' β '67 Β± 1' β 68 β
β 6 β 'sonic-rs large data readme string with position (22MB)' β '15473730 Β± 0.91%' β '15392875 Β± 346958' β '65 Β± 0.83%' β '65 Β± 1' β 65 β
β 7 β 'sonic-rs large data readme JSON buffer with position (22MB)' β '15280011 Β± 0.88%' β '15226563 Β± 385272' β '66 Β± 0.88%' β '66 Β± 2' β 66 β
β 8 β 'JSONParse super large data readme string (89M)' β '166472681 Β± 2.56%' β '159452958 Β± 12719146' β '6 Β± 2.52%' β '6 Β± 1' β 64 β
β 9 β 'sonic-rs super large data readme string (89M)' β '55477295 Β± 0.58%' β '55223313 Β± 914063' β '18 Β± 0.57%' β '18 Β± 0' β 64 β
β 10 β 'sonic-rs super large data readme string with position (89M)' β '54809802 Β± 0.53%' β '54409938 Β± 633917' β '18 Β± 0.52%' β '18 Β± 0' β 64 β
β 11 β 'sonic-rs super large data readme JSON buffer with position (89M)' β '54798735 Β± 0.65%' β '54398208 Β± 554979' β '18 Β± 0.61%' β '18 Β± 0' β 64 β
β 12 β 'JSONParse big readme string (229KB, 64KB readme)' β '332583 Β± 1.01%' β '318792 Β± 6709.0' β '3091 Β± 0.36%' β '3137 Β± 67' β 3007 β
β 13 β 'sonic-rs big readme string (229KB, 64KB readme)' β '154914 Β± 0.66%' β '152417 Β± 2875.0' β '6549 Β± 0.20%' β '6561 Β± 123' β 6456 β
β 14 β 'sonic-rs big readme string with position (229KB, 64KB readme)' β '185652 Β± 0.74%' β '180625 Β± 5834.0' β '5499 Β± 0.27%' β '5536 Β± 179' β 5387 β
β 15 β 'sonic-rs big readme JSON buffer with position (229KB, 64KB readme)' β '134273 Β± 0.60%' β '133083 Β± 1666.0' β '7521 Β± 0.16%' β '7514 Β± 93' β 7448 β
β 16 β 'JSONParse large data add version (22MB)' β '141596438 Β± 3.18%' β '130096250 Β± 3036583' β '7 Β± 2.94%' β '8 Β± 0' β 64 β
β 17 β 'sonic-rs large data add version (22MB)' β '47422600 Β± 0.46%' β '47358021 Β± 578896' β '21 Β± 0.46%' β '21 Β± 0' β 64 β
β 18 β 'JSONParse super large data add version (89M)' β '395517850 Β± 0.63%' β '393929666 Β± 6207959' β '3 Β± 0.61%' β '3 Β± 0' β 64 β
β 19 β 'sonic-rs super large data add version (89M)' β '170208911 Β± 0.46%' β '170423042 Β± 2301708' β '6 Β± 0.46%' β '6 Β± 0' β 64 β
βββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ΄ββββββββββββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββββββββββββββββ΄ββββββββββ
Memory Usage:
JSONParse description (22M): 343.4 MB (min: 343.2 MB, max: 343.8 MB)
JSONParse description (89M): 577.1 MB (min: 576.5 MB, max: 577.7 MB)
SonicJSONParse description (22M): 81.3 MB (min: 81.1 MB, max: 81.8 MB)
SonicJSONParse description (89M): 148.8 MB (min: 148.5 MB, max: 149.2 MB)
Memory Usage:
JSONParse add version (22M): 463.9 MB (min: 462.3 MB, max: 466.6 MB)
JSONParse add version (89M): 726.5 MB (min: 723.6 MB, max: 730.4 MB)
SonicJSONParse add version (22M): 133.1 MB (min: 128.5 MB, max: 151.0 MB)
SonicJSONParse add version (89M): 417.8 MB (min: 417.8 MB, max: 417.9 MB)
Node.js 20.19.5 Result
$ npx tsx benchmark/bench.ts
βββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬ββββββββββ
β (index) β Task name β Latency avg (ns) β Latency med (ns) β Throughput avg (ops/s) β Throughput med (ops/s) β Samples β
βββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββΌβββββββββββββββββββββββββΌβββββββββββββββββββββββββΌβββββββββββββββββββββββββΌββββββββββ€
β 0 β 'JSONParse small data readme string (117KB)' β '282642 Β± 0.87%' β '272875 Β± 8667.0' β '3626 Β± 0.33%' β '3665 Β± 115' β 3539 β
β 1 β 'sonic-rs small data readme string (117KB)' β '97086 Β± 0.17%' β '96416 Β± 5042.0' β '10353 Β± 0.12%' β '10372 Β± 537' β 10301 β
β 2 β 'sonic-rs small data readme string with position (117KB)' β '106532 Β± 0.49%' β '105209 Β± 5584.0' β '9486 Β± 0.15%' β '9505 Β± 509' β 9387 β
β 3 β 'sonic-rs small data readme JSON buffer with position (117KB)' β '81501 Β± 0.11%' β '79625 Β± 2708.0' β '12312 Β± 0.10%' β '12559 Β± 442' β 12270 β
β 4 β 'JSONParse large data readme string (22MB)' β '81700227 Β± 3.67%' β '74035333 Β± 1724355' β '12 Β± 3.35%' β '14 Β± 0' β 64 β
β 5 β 'sonic-rs large data readme string (22MB)' β '14406041 Β± 0.58%' β '14339813 Β± 245083' β '69 Β± 0.57%' β '70 Β± 1' β 70 β
β 6 β 'sonic-rs large data readme string with position (22MB)' β '14373543 Β± 0.47%' β '14350042 Β± 98937' β '70 Β± 0.46%' β '70 Β± 0' β 70 β
β 7 β 'sonic-rs large data readme JSON buffer with position (22MB)' β '14372198 Β± 0.52%' β '14351229 Β± 96354' β '70 Β± 0.52%' β '70 Β± 0' β 70 β
β 8 β 'JSONParse super large data readme string (89M)' β '192673340 Β± 1.78%' β '201564354 Β± 19275396' β '5 Β± 1.75%' β '5 Β± 1' β 64 β
β 9 β 'sonic-rs super large data readme string (89M)' β '51303309 Β± 0.60%' β '51097167 Β± 809375' β '20 Β± 0.59%' β '20 Β± 0' β 64 β
β 10 β 'sonic-rs super large data readme string with position (89M)' β '51120106 Β± 0.44%' β '50995396 Β± 506500' β '20 Β± 0.43%' β '20 Β± 0' β 64 β
β 11 β 'sonic-rs super large data readme JSON buffer with position (89M)' β '51319434 Β± 0.44%' β '51296937 Β± 428146' β '19 Β± 0.43%' β '19 Β± 0' β 64 β
β 12 β 'JSONParse big readme string (229KB, 64KB readme)' β '339916 Β± 0.59%' β '331250 Β± 6375.0' β '2985 Β± 0.33%' β '3019 Β± 58' β 2942 β
β 13 β 'sonic-rs big readme string (229KB, 64KB readme)' β '145951 Β± 0.21%' β '147000 Β± 4167.0' β '6881 Β± 0.14%' β '6803 Β± 193' β 6852 β
β 14 β 'sonic-rs big readme string with position (229KB, 64KB readme)' β '171549 Β± 0.22%' β '170541 Β± 7417.0' β '5858 Β± 0.16%' β '5864 Β± 248' β 5830 β
β 15 β 'sonic-rs big readme JSON buffer with position (229KB, 64KB readme)' β '125563 Β± 0.19%' β '125875 Β± 5500.0' β '7994 Β± 0.12%' β '7944 Β± 345' β 7965 β
βββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ΄ββββββββββββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββββββββββββββββ΄ββββββββββ
Memory Usage:
JSONParse description (22M): 406.0 MB (min: 392.1 MB, max: 498.3 MB)
JSONParse description (89M): 659.6 MB (min: 594.5 MB, max: 987.7 MB)
SonicJSONParse description (22M): 179.7 MB (min: 177.0 MB, max: 181.3 MB)
SonicJSONParse description (89M): 179.3 MB (min: 175.2 MB, max: 181.8 MB)
Ability
Build
After npm run build command, you can see packument.[darwin|win32|linux].node file in project root. This is the native addon built from lib.rs.
Test
With vitest, run npm test to testing native addon.
CI
With GitHub Actions, each commit and pull request will be built and tested automatically in [node@20, @node22] x [macOS, Linux, Windows] matrix. You will never be afraid of the native addon broken in these platforms.
Release
Release native package is very difficult in old days. Native packages may ask developers who use it to install build toolchain like gcc/llvm, node-gyp or something more.
With GitHub actions, we can easily prebuild a binary for major platforms. And with N-API, we should never be afraid of ABI Compatible.
The other problem is how to deliver prebuild binary to users. Downloading it in postinstall script is a common way that most packages do it right now. The problem with this solution is it introduced many other packages to download binary that has not been used by runtime codes. The other problem is some users may not easily download the binary from GitHub/CDN if they are behind a private network (But in most cases, they have a private NPM mirror).
In this package, we choose a better way to solve this problem. We release different npm packages for different platforms. And add it to optionalDependencies before releasing the Major package to npm.
NPM will choose which native package should download from registry automatically. You can see npm dir for details. And you can also run yarn add @napi-rs/package-template to see how it works.
Develop requirements
- Install the latest
Rust
- Install
Node.js@10+ which fully supported Node-API
- Install
yarn@1.x
Test in local
- yarn
- yarn build
- yarn test
Release package
When you want to release the package:
npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease [--preid=<prerelease-id>] | from-git]
git push origin master --tags
GitHub actions will do the rest job for you.
WARN: Don't run npm publish manually.
License
MIT
Contributors

Made with contributors-img.