You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

641 lines
20 KiB

4 years ago
  1. # cacache [![npm version](https://img.shields.io/npm/v/cacache.svg)](https://npm.im/cacache) [![license](https://img.shields.io/npm/l/cacache.svg)](https://npm.im/cacache) [![Travis](https://img.shields.io/travis/npm/cacache.svg)](https://travis-ci.org/npm/cacache) [![AppVeyor](https://ci.appveyor.com/api/projects/status/github/npm/cacache?svg=true)](https://ci.appveyor.com/project/npm/cacache) [![Coverage Status](https://coveralls.io/repos/github/npm/cacache/badge.svg?branch=latest)](https://coveralls.io/github/npm/cacache?branch=latest)
  2. [`cacache`](https://github.com/npm/cacache) is a Node.js library for managing
  3. local key and content address caches. It's really fast, really good at
  4. concurrency, and it will never give you corrupted data, even if cache files
  5. get corrupted or manipulated.
  6. On systems that support user and group settings on files, cacache will
  7. match the `uid` and `gid` values to the folder where the cache lives, even
  8. when running as `root`.
  9. It was written to be used as [npm](https://npm.im)'s local cache, but can
  10. just as easily be used on its own.
  11. _Translations: [español](README.es.md)_
  12. ## Install
  13. `$ npm install --save cacache`
  14. ## Table of Contents
  15. * [Example](#example)
  16. * [Features](#features)
  17. * [Contributing](#contributing)
  18. * [API](#api)
  19. * [Using localized APIs](#localized-api)
  20. * Reading
  21. * [`ls`](#ls)
  22. * [`ls.stream`](#ls-stream)
  23. * [`get`](#get-data)
  24. * [`get.stream`](#get-stream)
  25. * [`get.info`](#get-info)
  26. * [`get.hasContent`](#get-hasContent)
  27. * Writing
  28. * [`put`](#put-data)
  29. * [`put.stream`](#put-stream)
  30. * [`put*` opts](#put-options)
  31. * [`rm.all`](#rm-all)
  32. * [`rm.entry`](#rm-entry)
  33. * [`rm.content`](#rm-content)
  34. * Utilities
  35. * [`setLocale`](#set-locale)
  36. * [`clearMemoized`](#clear-memoized)
  37. * [`tmp.mkdir`](#tmp-mkdir)
  38. * [`tmp.withTmp`](#with-tmp)
  39. * Integrity
  40. * [Subresource Integrity](#integrity)
  41. * [`verify`](#verify)
  42. * [`verify.lastRun`](#verify-last-run)
  43. ### Example
  44. ```javascript
  45. const cacache = require('cacache/en')
  46. const fs = require('fs')
  47. const tarball = '/path/to/mytar.tgz'
  48. const cachePath = '/tmp/my-toy-cache'
  49. const key = 'my-unique-key-1234'
  50. // Cache it! Use `cachePath` as the root of the content cache
  51. cacache.put(cachePath, key, '10293801983029384').then(integrity => {
  52. console.log(`Saved content to ${cachePath}.`)
  53. })
  54. const destination = '/tmp/mytar.tgz'
  55. // Copy the contents out of the cache and into their destination!
  56. // But this time, use stream instead!
  57. cacache.get.stream(
  58. cachePath, key
  59. ).pipe(
  60. fs.createWriteStream(destination)
  61. ).on('finish', () => {
  62. console.log('done extracting!')
  63. })
  64. // The same thing, but skip the key index.
  65. cacache.get.byDigest(cachePath, integrityHash).then(data => {
  66. fs.writeFile(destination, data, err => {
  67. console.log('tarball data fetched based on its sha512sum and written out!')
  68. })
  69. })
  70. ```
  71. ### Features
  72. * Extraction by key or by content address (shasum, etc)
  73. * [Subresource Integrity](#integrity) web standard support
  74. * Multi-hash support - safely host sha1, sha512, etc, in a single cache
  75. * Automatic content deduplication
  76. * Fault tolerance (immune to corruption, partial writes, process races, etc)
  77. * Consistency guarantees on read and write (full data verification)
  78. * Lockless, high-concurrency cache access
  79. * Streaming support
  80. * Promise support
  81. * Pretty darn fast -- sub-millisecond reads and writes including verification
  82. * Arbitrary metadata storage
  83. * Garbage collection and additional offline verification
  84. * Thorough test coverage
  85. * There's probably a bloom filter in there somewhere. Those are cool, right? 🤔
  86. ### Contributing
  87. The cacache team enthusiastically welcomes contributions and project participation! There's a bunch of things you can do if you want to contribute! The [Contributor Guide](CONTRIBUTING.md) has all the information you need for everything from reporting bugs to contributing entire new features. Please don't hesitate to jump in if you'd like to, or even ask us questions if something isn't clear.
  88. All participants and maintainers in this project are expected to follow [Code of Conduct](CODE_OF_CONDUCT.md), and just generally be excellent to each other.
  89. Please refer to the [Changelog](CHANGELOG.md) for project history details, too.
  90. Happy hacking!
  91. ### API
  92. #### <a name="localized-api"></a> Using localized APIs
  93. cacache includes a complete API in English, with the same features as other
  94. translations. To use the English API as documented in this README, use
  95. `require('cacache/en')`. This is also currently the default if you do
  96. `require('cacache')`, but may change in the future.
  97. cacache also supports other languages! You can find the list of currently
  98. supported ones by looking in `./locales` in the source directory. You can use
  99. the API in that language with `require('cacache/<lang>')`.
  100. Want to add support for a new language? Please go ahead! You should be able to
  101. copy `./locales/en.js` and `./locales/en.json` and fill them in. Translating the
  102. `README.md` is a bit more work, but also appreciated if you get around to it. 👍🏼
  103. #### <a name="ls"></a> `> cacache.ls(cache) -> Promise<Object>`
  104. Lists info for all entries currently in the cache as a single large object. Each
  105. entry in the object will be keyed by the unique index key, with corresponding
  106. [`get.info`](#get-info) objects as the values.
  107. ##### Example
  108. ```javascript
  109. cacache.ls(cachePath).then(console.log)
  110. // Output
  111. {
  112. 'my-thing': {
  113. key: 'my-thing',
  114. integrity: 'sha512-BaSe64/EnCoDED+HAsh=='
  115. path: '.testcache/content/deadbeef', // joined with `cachePath`
  116. time: 12345698490,
  117. size: 4023948,
  118. metadata: {
  119. name: 'blah',
  120. version: '1.2.3',
  121. description: 'this was once a package but now it is my-thing'
  122. }
  123. },
  124. 'other-thing': {
  125. key: 'other-thing',
  126. integrity: 'sha1-ANothER+hasH=',
  127. path: '.testcache/content/bada55',
  128. time: 11992309289,
  129. size: 111112
  130. }
  131. }
  132. ```
  133. #### <a name="ls-stream"></a> `> cacache.ls.stream(cache) -> Readable`
  134. Lists info for all entries currently in the cache as a single large object.
  135. This works just like [`ls`](#ls), except [`get.info`](#get-info) entries are
  136. returned as `'data'` events on the returned stream.
  137. ##### Example
  138. ```javascript
  139. cacache.ls.stream(cachePath).on('data', console.log)
  140. // Output
  141. {
  142. key: 'my-thing',
  143. integrity: 'sha512-BaSe64HaSh',
  144. path: '.testcache/content/deadbeef', // joined with `cachePath`
  145. time: 12345698490,
  146. size: 13423,
  147. metadata: {
  148. name: 'blah',
  149. version: '1.2.3',
  150. description: 'this was once a package but now it is my-thing'
  151. }
  152. }
  153. {
  154. key: 'other-thing',
  155. integrity: 'whirlpool-WoWSoMuchSupport',
  156. path: '.testcache/content/bada55',
  157. time: 11992309289,
  158. size: 498023984029
  159. }
  160. {
  161. ...
  162. }
  163. ```
  164. #### <a name="get-data"></a> `> cacache.get(cache, key, [opts]) -> Promise({data, metadata, integrity})`
  165. Returns an object with the cached data, digest, and metadata identified by
  166. `key`. The `data` property of this object will be a `Buffer` instance that
  167. presumably holds some data that means something to you. I'm sure you know what
  168. to do with it! cacache just won't care.
  169. `integrity` is a [Subresource
  170. Integrity](#integrity)
  171. string. That is, a string that can be used to verify `data`, which looks like
  172. `<hash-algorithm>-<base64-integrity-hash>`.
  173. If there is no content identified by `key`, or if the locally-stored data does
  174. not pass the validity checksum, the promise will be rejected.
  175. A sub-function, `get.byDigest` may be used for identical behavior, except lookup
  176. will happen by integrity hash, bypassing the index entirely. This version of the
  177. function *only* returns `data` itself, without any wrapper.
  178. ##### Note
  179. This function loads the entire cache entry into memory before returning it. If
  180. you're dealing with Very Large data, consider using [`get.stream`](#get-stream)
  181. instead.
  182. ##### Example
  183. ```javascript
  184. // Look up by key
  185. cache.get(cachePath, 'my-thing').then(console.log)
  186. // Output:
  187. {
  188. metadata: {
  189. thingName: 'my'
  190. },
  191. integrity: 'sha512-BaSe64HaSh',
  192. data: Buffer#<deadbeef>,
  193. size: 9320
  194. }
  195. // Look up by digest
  196. cache.get.byDigest(cachePath, 'sha512-BaSe64HaSh').then(console.log)
  197. // Output:
  198. Buffer#<deadbeef>
  199. ```
  200. #### <a name="get-stream"></a> `> cacache.get.stream(cache, key, [opts]) -> Readable`
  201. Returns a [Readable Stream](https://nodejs.org/api/stream.html#stream_readable_streams) of the cached data identified by `key`.
  202. If there is no content identified by `key`, or if the locally-stored data does
  203. not pass the validity checksum, an error will be emitted.
  204. `metadata` and `integrity` events will be emitted before the stream closes, if
  205. you need to collect that extra data about the cached entry.
  206. A sub-function, `get.stream.byDigest` may be used for identical behavior,
  207. except lookup will happen by integrity hash, bypassing the index entirely. This
  208. version does not emit the `metadata` and `integrity` events at all.
  209. ##### Example
  210. ```javascript
  211. // Look up by key
  212. cache.get.stream(
  213. cachePath, 'my-thing'
  214. ).on('metadata', metadata => {
  215. console.log('metadata:', metadata)
  216. }).on('integrity', integrity => {
  217. console.log('integrity:', integrity)
  218. }).pipe(
  219. fs.createWriteStream('./x.tgz')
  220. )
  221. // Outputs:
  222. metadata: { ... }
  223. integrity: 'sha512-SoMeDIGest+64=='
  224. // Look up by digest
  225. cache.get.stream.byDigest(
  226. cachePath, 'sha512-SoMeDIGest+64=='
  227. ).pipe(
  228. fs.createWriteStream('./x.tgz')
  229. )
  230. ```
  231. #### <a name="get-info"></a> `> cacache.get.info(cache, key) -> Promise`
  232. Looks up `key` in the cache index, returning information about the entry if
  233. one exists.
  234. ##### Fields
  235. * `key` - Key the entry was looked up under. Matches the `key` argument.
  236. * `integrity` - [Subresource Integrity hash](#integrity) for the content this entry refers to.
  237. * `path` - Filesystem path where content is stored, joined with `cache` argument.
  238. * `time` - Timestamp the entry was first added on.
  239. * `metadata` - User-assigned metadata associated with the entry/content.
  240. ##### Example
  241. ```javascript
  242. cacache.get.info(cachePath, 'my-thing').then(console.log)
  243. // Output
  244. {
  245. key: 'my-thing',
  246. integrity: 'sha256-MUSTVERIFY+ALL/THINGS=='
  247. path: '.testcache/content/deadbeef',
  248. time: 12345698490,
  249. size: 849234,
  250. metadata: {
  251. name: 'blah',
  252. version: '1.2.3',
  253. description: 'this was once a package but now it is my-thing'
  254. }
  255. }
  256. ```
  257. #### <a name="get-hasContent"></a> `> cacache.get.hasContent(cache, integrity) -> Promise`
  258. Looks up a [Subresource Integrity hash](#integrity) in the cache. If content
  259. exists for this `integrity`, it will return an object, with the specific single integrity hash
  260. that was found in `sri` key, and the size of the found content as `size`. If no content exists for this integrity, it will return `false`.
  261. ##### Example
  262. ```javascript
  263. cacache.get.hasContent(cachePath, 'sha256-MUSTVERIFY+ALL/THINGS==').then(console.log)
  264. // Output
  265. {
  266. sri: {
  267. source: 'sha256-MUSTVERIFY+ALL/THINGS==',
  268. algorithm: 'sha256',
  269. digest: 'MUSTVERIFY+ALL/THINGS==',
  270. options: []
  271. },
  272. size: 9001
  273. }
  274. cacache.get.hasContent(cachePath, 'sha521-NOT+IN/CACHE==').then(console.log)
  275. // Output
  276. false
  277. ```
  278. #### <a name="put-data"></a> `> cacache.put(cache, key, data, [opts]) -> Promise`
  279. Inserts data passed to it into the cache. The returned Promise resolves with a
  280. digest (generated according to [`opts.algorithms`](#optsalgorithms)) after the
  281. cache entry has been successfully written.
  282. ##### Example
  283. ```javascript
  284. fetch(
  285. 'https://registry.npmjs.org/cacache/-/cacache-1.0.0.tgz'
  286. ).then(data => {
  287. return cacache.put(cachePath, 'registry.npmjs.org|cacache@1.0.0', data)
  288. }).then(integrity => {
  289. console.log('integrity hash is', integrity)
  290. })
  291. ```
  292. #### <a name="put-stream"></a> `> cacache.put.stream(cache, key, [opts]) -> Writable`
  293. Returns a [Writable
  294. Stream](https://nodejs.org/api/stream.html#stream_writable_streams) that inserts
  295. data written to it into the cache. Emits an `integrity` event with the digest of
  296. written contents when it succeeds.
  297. ##### Example
  298. ```javascript
  299. request.get(
  300. 'https://registry.npmjs.org/cacache/-/cacache-1.0.0.tgz'
  301. ).pipe(
  302. cacache.put.stream(
  303. cachePath, 'registry.npmjs.org|cacache@1.0.0'
  304. ).on('integrity', d => console.log(`integrity digest is ${d}`))
  305. )
  306. ```
  307. #### <a name="put-options"></a> `> cacache.put options`
  308. `cacache.put` functions have a number of options in common.
  309. ##### `opts.metadata`
  310. Arbitrary metadata to be attached to the inserted key.
  311. ##### `opts.size`
  312. If provided, the data stream will be verified to check that enough data was
  313. passed through. If there's more or less data than expected, insertion will fail
  314. with an `EBADSIZE` error.
  315. ##### `opts.integrity`
  316. If present, the pre-calculated digest for the inserted content. If this option
  317. if provided and does not match the post-insertion digest, insertion will fail
  318. with an `EINTEGRITY` error.
  319. `algorithms` has no effect if this option is present.
  320. ##### `opts.algorithms`
  321. Default: ['sha512']
  322. Hashing algorithms to use when calculating the [subresource integrity
  323. digest](#integrity)
  324. for inserted data. Can use any algorithm listed in `crypto.getHashes()` or
  325. `'omakase'`/`'お任せします'` to pick a random hash algorithm on each insertion. You
  326. may also use any anagram of `'modnar'` to use this feature.
  327. Currently only supports one algorithm at a time (i.e., an array length of
  328. exactly `1`). Has no effect if `opts.integrity` is present.
  329. ##### `opts.memoize`
  330. Default: null
  331. If provided, cacache will memoize the given cache insertion in memory, bypassing
  332. any filesystem checks for that key or digest in future cache fetches. Nothing
  333. will be written to the in-memory cache unless this option is explicitly truthy.
  334. If `opts.memoize` is an object or a `Map`-like (that is, an object with `get`
  335. and `set` methods), it will be written to instead of the global memoization
  336. cache.
  337. Reading from disk data can be forced by explicitly passing `memoize: false` to
  338. the reader functions, but their default will be to read from memory.
  339. #### <a name="rm-all"></a> `> cacache.rm.all(cache) -> Promise`
  340. Clears the entire cache. Mainly by blowing away the cache directory itself.
  341. ##### Example
  342. ```javascript
  343. cacache.rm.all(cachePath).then(() => {
  344. console.log('THE APOCALYPSE IS UPON US 😱')
  345. })
  346. ```
  347. #### <a name="rm-entry"></a> `> cacache.rm.entry(cache, key) -> Promise`
  348. Alias: `cacache.rm`
  349. Removes the index entry for `key`. Content will still be accessible if
  350. requested directly by content address ([`get.stream.byDigest`](#get-stream)).
  351. To remove the content itself (which might still be used by other entries), use
  352. [`rm.content`](#rm-content). Or, to safely vacuum any unused content, use
  353. [`verify`](#verify).
  354. ##### Example
  355. ```javascript
  356. cacache.rm.entry(cachePath, 'my-thing').then(() => {
  357. console.log('I did not like it anyway')
  358. })
  359. ```
  360. #### <a name="rm-content"></a> `> cacache.rm.content(cache, integrity) -> Promise`
  361. Removes the content identified by `integrity`. Any index entries referring to it
  362. will not be usable again until the content is re-added to the cache with an
  363. identical digest.
  364. ##### Example
  365. ```javascript
  366. cacache.rm.content(cachePath, 'sha512-SoMeDIGest/IN+BaSE64==').then(() => {
  367. console.log('data for my-thing is gone!')
  368. })
  369. ```
  370. #### <a name="set-locale"></a> `> cacache.setLocale(locale)`
  371. Configure the language/locale used for messages and errors coming from cacache.
  372. The list of available locales is in the `./locales` directory in the project
  373. root.
  374. _Interested in contributing more languages! [Submit a PR](CONTRIBUTING.md)!_
  375. #### <a name="clear-memoized"></a> `> cacache.clearMemoized()`
  376. Completely resets the in-memory entry cache.
  377. #### <a name="tmp-mkdir"></a> `> tmp.mkdir(cache, opts) -> Promise<Path>`
  378. Returns a unique temporary directory inside the cache's `tmp` dir. This
  379. directory will use the same safe user assignment that all the other stuff use.
  380. Once the directory is made, it's the user's responsibility that all files
  381. within are given the appropriate `gid`/`uid` ownership settings to match
  382. the rest of the cache. If not, you can ask cacache to do it for you by
  383. calling [`tmp.fix()`](#tmp-fix), which will fix all tmp directory
  384. permissions.
  385. If you want automatic cleanup of this directory, use
  386. [`tmp.withTmp()`](#with-tpm)
  387. ##### Example
  388. ```javascript
  389. cacache.tmp.mkdir(cache).then(dir => {
  390. fs.writeFile(path.join(dir, 'blablabla'), Buffer#<1234>, ...)
  391. })
  392. ```
  393. #### <a name="tmp-fix"></a> `> tmp.fix(cache) -> Promise`
  394. Sets the `uid` and `gid` properties on all files and folders within the tmp
  395. folder to match the rest of the cache.
  396. Use this after manually writing files into [`tmp.mkdir`](#tmp-mkdir) or
  397. [`tmp.withTmp`](#with-tmp).
  398. ##### Example
  399. ```javascript
  400. cacache.tmp.mkdir(cache).then(dir => {
  401. writeFile(path.join(dir, 'file'), someData).then(() => {
  402. // make sure we didn't just put a root-owned file in the cache
  403. cacache.tmp.fix().then(() => {
  404. // all uids and gids match now
  405. })
  406. })
  407. })
  408. ```
  409. #### <a name="with-tmp"></a> `> tmp.withTmp(cache, opts, cb) -> Promise`
  410. Creates a temporary directory with [`tmp.mkdir()`](#tmp-mkdir) and calls `cb`
  411. with it. The created temporary directory will be removed when the return value
  412. of `cb()` resolves -- that is, if you return a Promise from `cb()`, the tmp
  413. directory will be automatically deleted once that promise completes.
  414. The same caveats apply when it comes to managing permissions for the tmp dir's
  415. contents.
  416. ##### Example
  417. ```javascript
  418. cacache.tmp.withTmp(cache, dir => {
  419. return fs.writeFileAsync(path.join(dir, 'blablabla'), Buffer#<1234>, ...)
  420. }).then(() => {
  421. // `dir` no longer exists
  422. })
  423. ```
  424. #### <a name="integrity"></a> Subresource Integrity Digests
  425. For content verification and addressing, cacache uses strings following the
  426. [Subresource
  427. Integrity spec](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity).
  428. That is, any time cacache expects an `integrity` argument or option, it
  429. should be in the format `<hashAlgorithm>-<base64-hash>`.
  430. One deviation from the current spec is that cacache will support any hash
  431. algorithms supported by the underlying Node.js process. You can use
  432. `crypto.getHashes()` to see which ones you can use.
  433. ##### Generating Digests Yourself
  434. If you have an existing content shasum, they are generally formatted as a
  435. hexadecimal string (that is, a sha1 would look like:
  436. `5f5513f8822fdbe5145af33b64d8d970dcf95c6e`). In order to be compatible with
  437. cacache, you'll need to convert this to an equivalent subresource integrity
  438. string. For this example, the corresponding hash would be:
  439. `sha1-X1UT+IIv2+UUWvM7ZNjZcNz5XG4=`.
  440. If you want to generate an integrity string yourself for existing data, you can
  441. use something like this:
  442. ```javascript
  443. const crypto = require('crypto')
  444. const hashAlgorithm = 'sha512'
  445. const data = 'foobarbaz'
  446. const integrity = (
  447. hashAlgorithm +
  448. '-' +
  449. crypto.createHash(hashAlgorithm).update(data).digest('base64')
  450. )
  451. ```
  452. You can also use [`ssri`](https://npm.im/ssri) to have a richer set of functionality
  453. around SRI strings, including generation, parsing, and translating from existing
  454. hex-formatted strings.
  455. #### <a name="verify"></a> `> cacache.verify(cache, opts) -> Promise`
  456. Checks out and fixes up your cache:
  457. * Cleans up corrupted or invalid index entries.
  458. * Custom entry filtering options.
  459. * Garbage collects any content entries not referenced by the index.
  460. * Checks integrity for all content entries and removes invalid content.
  461. * Fixes cache ownership.
  462. * Removes the `tmp` directory in the cache and all its contents.
  463. When it's done, it'll return an object with various stats about the verification
  464. process, including amount of storage reclaimed, number of valid entries, number
  465. of entries removed, etc.
  466. ##### Options
  467. * `opts.filter` - receives a formatted entry. Return false to remove it.
  468. Note: might be called more than once on the same entry.
  469. ##### Example
  470. ```sh
  471. echo somegarbage >> $CACHEPATH/content/deadbeef
  472. ```
  473. ```javascript
  474. cacache.verify(cachePath).then(stats => {
  475. // deadbeef collected, because of invalid checksum.
  476. console.log('cache is much nicer now! stats:', stats)
  477. })
  478. ```
  479. #### <a name="verify-last-run"></a> `> cacache.verify.lastRun(cache) -> Promise`
  480. Returns a `Date` representing the last time `cacache.verify` was run on `cache`.
  481. ##### Example
  482. ```javascript
  483. cacache.verify(cachePath).then(() => {
  484. cacache.verify.lastRun(cachePath).then(lastTime => {
  485. console.log('cacache.verify was last called on' + lastTime)
  486. })
  487. })
  488. ```