Warning: file_get_contents(https://raw.githubusercontent.com/Den1xxx/Filemanager/master/languages/ru.json): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 88

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 215

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 216

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 217

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 218

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 219

Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 220
PK!6::en.jsnu['use strict' module.exports = require('./locales/en.js') PK!>HXQQ README.es.mdnu[# 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/zkat/cacache.svg)](https://travis-ci.org/zkat/cacache) [![AppVeyor](https://ci.appveyor.com/api/projects/status/github/zkat/cacache?svg=true)](https://ci.appveyor.com/project/zkat/cacache) [![Coverage Status](https://coveralls.io/repos/github/zkat/cacache/badge.svg?branch=latest)](https://coveralls.io/github/zkat/cacache?branch=latest) [`cacache`](https://github.com/zkat/cacache) es una librería de Node.js para manejar caches locales en disco, con acceso tanto con claves únicas como direcciones de contenido (hashes/hacheos). Es súper rápida, excelente con el acceso concurrente, y jamás te dará datos incorrectos, aún si se corrompen o manipulan directamente los ficheros del cache. El propósito original era reemplazar el caché local de [npm](https://npm.im/npm), pero se puede usar por su propia cuenta. _Traducciones: [English](README.md)_ ## Instalación `$ npm install --save cacache` ## Índice * [Ejemplo](#ejemplo) * [Características](#características) * [Cómo Contribuir](#cómo-contribuir) * [API](#api) * [Usando el API en español](#localized-api) * Leer * [`ls`](#ls) * [`ls.flujo`](#ls-stream) * [`saca`](#get-data) * [`saca.flujo`](#get-stream) * [`saca.info`](#get-info) * [`saca.tieneDatos`](#get-hasContent) * Escribir * [`mete`](#put-data) * [`mete.flujo`](#put-stream) * [opciones para `mete*`](#put-options) * [`rm.todo`](#rm-all) * [`rm.entrada`](#rm-entry) * [`rm.datos`](#rm-content) * Utilidades * [`ponLenguaje`](#set-locale) * [`limpiaMemoizado`](#clear-memoized) * [`tmp.hazdir`](#tmp-mkdir) * [`tmp.conTmp`](#with-tmp) * Integridad * [Subresource Integrity](#integrity) * [`verifica`](#verify) * [`verifica.ultimaVez`](#verify-last-run) ### Ejemplo ```javascript const cacache = require('cacache/es') const fs = require('fs') const tarbol = '/ruta/a/mi-tar.tgz' const rutaCache = '/tmp/my-toy-cache' const clave = 'mi-clave-única-1234' // ¡Añádelo al caché! Usa `rutaCache` como raíz del caché. cacache.mete(rutaCache, clave, '10293801983029384').then(integrity => { console.log(`Saved content to ${rutaCache}.`) }) const destino = '/tmp/mytar.tgz' // Copia el contenido del caché a otro fichero, pero esta vez con flujos. cacache.saca.flujo( rutaCache, clave ).pipe( fs.createWriteStream(destino) ).on('finish', () => { console.log('extracción completada') }) // La misma cosa, pero accesando el contenido directamente, sin tocar el índice. cacache.saca.porHacheo(rutaCache, integridad).then(datos => { fs.writeFile(destino, datos, err => { console.log('datos del tarbol sacados basado en su sha512, y escrito a otro fichero') }) }) ``` ### Características * Extracción por clave o por dirección de contenido (shasum, etc) * Usa el estándard de web, [Subresource Integrity](#integrity) * Compatible con multiples algoritmos - usa sha1, sha512, etc, en el mismo caché sin problema * Entradas con contenido idéntico comparten ficheros * Tolerancia de fallas (inmune a corrupción, ficheros parciales, carreras de proceso, etc) * Verificación completa de datos cuando (escribiendo y leyendo) * Concurrencia rápida, segura y "lockless" * Compatible con `stream`s (flujos) * Compatible con `Promise`s (promesas) * Bastante rápida -- acceso, incluyendo verificación, en microsegundos * Almacenaje de metadatos arbitrarios * Colección de basura y verificación adicional fuera de banda * Cobertura rigurosa de pruebas * Probablente hay un "Bloom filter" por ahí en algún lado. Eso le mola a la gente, ¿Verdad? 🤔 ### Cómo Contribuir El equipo de cacache felizmente acepta contribuciones de código y otras maneras de participación. ¡Hay muchas formas diferentes de contribuir! La [Guía de Colaboradores](CONTRIBUTING.md) (en inglés) tiene toda la información que necesitas para cualquier tipo de contribución: todo desde cómo reportar errores hasta cómo someter parches con nuevas características. Con todo y eso, no se preocupe por si lo que haces está exáctamente correcto: no hay ningún problema en hacer preguntas si algo no está claro, o no lo encuentras. El equipo de cacache tiene miembros hispanohablantes: es completamente aceptable crear `issues` y `pull requests` en español/castellano. Todos los participantes en este proyecto deben obedecer el [Código de Conducta](CODE_OF_CONDUCT.md) (en inglés), y en general actuar de forma amable y respetuosa mientras participan en esta comunidad. Por favor refiérase al [Historial de Cambios](CHANGELOG.md) (en inglés) para detalles sobre cambios importantes incluídos en cada versión. Finalmente, cacache tiene un sistema de localización de lenguaje. Si te interesa añadir lenguajes o mejorar los que existen, mira en el directorio `./locales` para comenzar. Happy hacking! ### API #### Usando el API en español cacache incluye una traducción completa de su API al castellano, con las mismas características. Para usar el API como está documentado en este documento, usa `require('cacache/es')` cacache también tiene otros lenguajes: encuéntralos bajo `./locales`, y podrás usar el API en ese lenguaje con `require('cacache/')` #### `> cacache.ls(cache) -> Promise` Enumera todas las entradas en el caché, dentro de un solo objeto. Cada entrada en el objeto tendrá como clave la clave única usada para el índice, el valor siendo un objeto de [`saca.info`](#get-info). ##### Ejemplo ```javascript cacache.ls(rutaCache).then(console.log) // Salida { 'my-thing': { key: 'my-thing', integrity: 'sha512-BaSe64/EnCoDED+HAsh==' path: '.testcache/content/deadbeef', // unido con `rutaCache` time: 12345698490, size: 4023948, metadata: { name: 'blah', version: '1.2.3', description: 'this was once a package but now it is my-thing' } }, 'other-thing': { key: 'other-thing', integrity: 'sha1-ANothER+hasH=', path: '.testcache/content/bada55', time: 11992309289, size: 111112 } } ``` #### `> cacache.ls.flujo(cache) -> Readable` Enumera todas las entradas en el caché, emitiendo un objeto de [`saca.info`](#get-info) por cada evento de `data` en el flujo. ##### Ejemplo ```javascript cacache.ls.flujo(rutaCache).on('data', console.log) // Salida { key: 'my-thing', integrity: 'sha512-BaSe64HaSh', path: '.testcache/content/deadbeef', // unido con `rutaCache` time: 12345698490, size: 13423, metadata: { name: 'blah', version: '1.2.3', description: 'this was once a package but now it is my-thing' } } { key: 'other-thing', integrity: 'whirlpool-WoWSoMuchSupport', path: '.testcache/content/bada55', time: 11992309289, size: 498023984029 } { ... } ``` #### `> cacache.saca(cache, clave, [ops]) -> Promise({data, metadata, integrity})` Devuelve un objeto con los datos, hacheo de integridad y metadatos identificados por la `clave`. La propiedad `data` de este objeto será una instancia de `Buffer` con los datos almacenados en el caché. to do with it! cacache just won't care. `integrity` es un `string` de [Subresource Integrity](#integrity). Dígase, un `string` que puede ser usado para verificar a la `data`, que tiene como formato `-`. So no existe ninguna entrada identificada por `clave`, o se los datos almacenados localmente fallan verificación, el `Promise` fallará. Una sub-función, `saca.porHacheo`, tiene casi el mismo comportamiento, excepto que busca entradas usando el hacheo de integridad, sin tocar el índice general. Esta versión *sólo* devuelve `data`, sin ningún objeto conteniéndola. ##### Nota Esta función lee la entrada completa a la memoria antes de devolverla. Si estás almacenando datos Muy Grandes, es posible que [`saca.flujo`](#get-stream) sea una mejor solución. ##### Ejemplo ```javascript // Busca por clave cache.saca(rutaCache, 'my-thing').then(console.log) // Salida: { metadata: { thingName: 'my' }, integrity: 'sha512-BaSe64HaSh', data: Buffer#, size: 9320 } // Busca por hacheo cache.saca.porHacheo(rutaCache, 'sha512-BaSe64HaSh').then(console.log) // Salida: Buffer# ``` #### `> cacache.saca.flujo(cache, clave, [ops]) -> Readable` Devuelve un [Readable Stream](https://nodejs.org/api/stream.html#stream_readable_streams) de los datos almacenados bajo `clave`. So no existe ninguna entrada identificada por `clave`, o se los datos almacenados localmente fallan verificación, el `Promise` fallará. `metadata` y `integrity` serán emitidos como eventos antes de que el flujo cierre. Una sub-función, `saca.flujo.porHacheo`, tiene casi el mismo comportamiento, excepto que busca entradas usando el hacheo de integridad, sin tocar el índice general. Esta versión no emite eventos de `metadata` o `integrity`. ##### Ejemplo ```javascript // Busca por clave cache.saca.flujo( rutaCache, 'my-thing' ).on('metadata', metadata => { console.log('metadata:', metadata) }).on('integrity', integrity => { console.log('integrity:', integrity) }).pipe( fs.createWriteStream('./x.tgz') ) // Salidas: metadata: { ... } integrity: 'sha512-SoMeDIGest+64==' // Busca por hacheo cache.saca.flujo.porHacheo( rutaCache, 'sha512-SoMeDIGest+64==' ).pipe( fs.createWriteStream('./x.tgz') ) ``` #### `> cacache.saca.info(cache, clave) -> Promise` Busca la `clave` en el índice del caché, devolviendo información sobre la entrada si existe. ##### Campos * `key` - Clave de la entrada. Igual al argumento `clave`. * `integrity` - [hacheo de Subresource Integrity](#integrity) del contenido al que se refiere esta entrada. * `path` - Dirección del fichero de datos almacenados, unida al argumento `cache`. * `time` - Hora de creación de la entrada * `metadata` - Metadatos asignados a esta entrada por el usuario ##### Ejemplo ```javascript cacache.saca.info(rutaCache, 'my-thing').then(console.log) // Salida { key: 'my-thing', integrity: 'sha256-MUSTVERIFY+ALL/THINGS==' path: '.testcache/content/deadbeef', time: 12345698490, size: 849234, metadata: { name: 'blah', version: '1.2.3', description: 'this was once a package but now it is my-thing' } } ``` #### `> cacache.saca.tieneDatos(cache, integrity) -> Promise` Busca un [hacheo Subresource Integrity](#integrity) en el caché. Si existe el contenido asociado con `integrity`, devuelve un objeto con dos campos: el hacheo _específico_ que se usó para la búsqueda, `sri`, y el tamaño total del contenido, `size`. Si no existe ningún contenido asociado con `integrity`, devuelve `false`. ##### Ejemplo ```javascript cacache.saca.tieneDatos(rutaCache, 'sha256-MUSTVERIFY+ALL/THINGS==').then(console.log) // Salida { sri: { source: 'sha256-MUSTVERIFY+ALL/THINGS==', algorithm: 'sha256', digest: 'MUSTVERIFY+ALL/THINGS==', options: [] }, size: 9001 } cacache.saca.tieneDatos(rutaCache, 'sha521-NOT+IN/CACHE==').then(console.log) // Salida false ``` #### `> cacache.mete(cache, clave, datos, [ops]) -> Promise` Inserta `datos` en el caché. El `Promise` devuelto se resuelve con un hacheo (generado conforme a [`ops.algorithms`](#optsalgorithms)) después que la entrada haya sido escrita en completo. ##### Ejemplo ```javascript fetch( 'https://registry.npmjs.org/cacache/-/cacache-1.0.0.tgz' ).then(datos => { return cacache.mete(rutaCache, 'registry.npmjs.org|cacache@1.0.0', datos) }).then(integridad => { console.log('el hacheo de integridad es', integridad) }) ``` #### `> cacache.mete.flujo(cache, clave, [ops]) -> Writable` Devuelve un [Writable Stream](https://nodejs.org/api/stream.html#stream_writable_streams) que inserta al caché los datos escritos a él. Emite un evento `integrity` con el hacheo del contenido escrito, cuando completa. ##### Ejemplo ```javascript request.get( 'https://registry.npmjs.org/cacache/-/cacache-1.0.0.tgz' ).pipe( cacache.mete.flujo( rutaCache, 'registry.npmjs.org|cacache@1.0.0' ).on('integrity', d => console.log(`integrity digest is ${d}`)) ) ``` #### `> opciones para cacache.mete` La funciones `cacache.mete` tienen un número de opciones en común. ##### `ops.metadata` Metadatos del usuario que se almacenarán con la entrada. ##### `ops.size` El tamaño declarado de los datos que se van a insertar. Si es proveído, cacache verificará que los datos escritos sean de ese tamaño, o si no, fallará con un error con código `EBADSIZE`. ##### `ops.integrity` El hacheo de integridad de los datos siendo escritos. Si es proveído, y los datos escritos no le corresponden, la operación fallará con un error con código `EINTEGRITY`. `ops.algorithms` no tiene ningún efecto si esta opción está presente. ##### `ops.algorithms` Por Defecto: `['sha512']` Algoritmos que se deben usar cuando se calcule el hacheo de [subresource integrity](#integrity) para los datos insertados. Puede usar cualquier algoritmo enumerado en `crypto.getHashes()`. Por el momento, sólo se acepta un algoritmo (dígase, un array con exáctamente un valor). No tiene ningún efecto si `ops.integrity` también ha sido proveido. ##### `ops.uid`/`ops.gid` Si están presentes, cacache hará todo lo posible para asegurarse que todos los ficheros creados en el proceso de sus operaciones en el caché usen esta combinación en particular. ##### `ops.memoize` Por Defecto: `null` Si es verdad, cacache tratará de memoizar los datos de la entrada en memoria. La próxima vez que el proceso corriente trate de accesar los datos o entrada, cacache buscará en memoria antes de buscar en disco. Si `ops.memoize` es un objeto regular o un objeto como `Map` (es decir, un objeto con métodos `get()` y `set()`), este objeto en sí sera usado en vez del caché de memoria global. Esto permite tener lógica específica a tu aplicación encuanto al almacenaje en memoria de tus datos. Si quieres asegurarte que los datos se lean del disco en vez de memoria, usa `memoize: false` cuando uses funciones de `cacache.saca`. #### `> cacache.rm.todo(cache) -> Promise` Borra el caché completo, incluyendo ficheros temporeros, ficheros de datos, y el índice del caché. ##### Ejemplo ```javascript cacache.rm.todo(rutaCache).then(() => { console.log('THE APOCALYPSE IS UPON US 😱') }) ``` #### `> cacache.rm.entrada(cache, clave) -> Promise` Alias: `cacache.rm` Borra la entrada `clave` del índuce. El contenido asociado con esta entrada seguirá siendo accesible por hacheo usando [`saca.flujo.porHacheo`](#get-stream). Para borrar el contenido en sí, usa [`rm.datos`](#rm-content). Si quieres hacer esto de manera más segura (pues ficheros de contenido pueden ser usados por multiples entradas), usa [`verifica`](#verify) para borrar huérfanos. ##### Ejemplo ```javascript cacache.rm.entrada(rutaCache, 'my-thing').then(() => { console.log('I did not like it anyway') }) ``` #### `> cacache.rm.datos(cache, integrity) -> Promise` Borra el contenido identificado por `integrity`. Cualquier entrada que se refiera a este contenido quedarán huérfanas y se invalidarán si se tratan de accesar, al menos que contenido idéntico sea añadido bajo `integrity`. ##### Ejemplo ```javascript cacache.rm.datos(rutaCache, 'sha512-SoMeDIGest/IN+BaSE64==').then(() => { console.log('los datos para `mi-cosa` se borraron') }) ``` #### `> cacache.ponLenguaje(locale)` Configura el lenguaje usado para mensajes y errores de cacache. La lista de lenguajes disponibles está en el directorio `./locales` del proyecto. _Te interesa añadir más lenguajes? [Somete un PR](CONTRIBUTING.md)!_ #### `> cacache.limpiaMemoizado()` Completamente reinicializa el caché de memoria interno. Si estás usando tu propio objecto con `ops.memoize`, debes hacer esto de manera específica a él. #### `> tmp.hazdir(cache, ops) -> Promise` Alias: `tmp.mkdir` Devuelve un directorio único dentro del directorio `tmp` del caché. Una vez tengas el directorio, es responsabilidad tuya asegurarte que todos los ficheros escrito a él sean creados usando los permisos y `uid`/`gid` concordante con el caché. Si no, puedes pedirle a cacache que lo haga llamando a [`cacache.tmp.fix()`](#tmp-fix). Esta función arreglará todos los permisos en el directorio tmp. Si quieres que cacache limpie el directorio automáticamente cuando termines, usa [`cacache.tmp.conTmp()`](#with-tpm). ##### Ejemplo ```javascript cacache.tmp.mkdir(cache).then(dir => { fs.writeFile(path.join(dir, 'blablabla'), Buffer#<1234>, ...) }) ``` #### `> tmp.conTmp(cache, ops, cb) -> Promise` Crea un directorio temporero con [`tmp.mkdir()`](#tmp-mkdir) y ejecuta `cb` con él como primer argumento. El directorio creado será removido automáticamente cuando el valor devolvido por `cb()` se resuelva. Las mismas advertencias aplican en cuanto a manejando permisos para los ficheros dentro del directorio. ##### Ejemplo ```javascript cacache.tmp.conTmp(cache, dir => { return fs.writeFileAsync(path.join(dir, 'blablabla'), Buffer#<1234>, ...) }).then(() => { // `dir` no longer exists }) ``` #### Hacheos de Subresource Integrity cacache usa strings que siguen la especificación de [Subresource Integrity spec](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). Es decir, donde quiera cacache espera un argumento o opción `integrity`, ese string debería usar el formato `-`. Una variación importante sobre los hacheos que cacache acepta es que acepta el nombre de cualquier algoritmo aceptado por el proceso de Node.js donde se usa. Puedes usar `crypto.getHashes()` para ver cuales están disponibles. ##### Generando tus propios hacheos Si tienes un `shasum`, en general va a estar en formato de string hexadecimal (es decir, un `sha1` se vería como algo así: `5f5513f8822fdbe5145af33b64d8d970dcf95c6e`). Para ser compatible con cacache, necesitas convertir esto a su equivalente en subresource integrity. Por ejemplo, el hacheo correspondiente al ejemplo anterior sería: `sha1-X1UT+IIv2+UUWvM7ZNjZcNz5XG4=`. Puedes usar código así para generarlo por tu cuenta: ```javascript const crypto = require('crypto') const algoritmo = 'sha512' const datos = 'foobarbaz' const integrity = ( algorithm + '-' + crypto.createHash(algoritmo).update(datos).digest('base64') ) ``` También puedes usar [`ssri`](https://npm.im/ssri) para deferir el trabajo a otra librería que garantiza que todo esté correcto, pues maneja probablemente todas las operaciones que tendrías que hacer con SRIs, incluyendo convirtiendo entre hexadecimal y el formato SRI. #### `> cacache.verifica(cache, ops) -> Promise` Examina y arregla tu caché: * Limpia entradas inválidas, huérfanas y corrompidas * Te deja filtrar cuales entradas retener, con tu propio filtro * Reclama cualquier ficheros de contenido sin referencias en el índice * Verifica integridad de todos los ficheros de contenido y remueve los malos * Arregla permisos del caché * Remieve el directorio `tmp` en el caché, y todo su contenido. Cuando termine, devuelve un objeto con varias estadísticas sobre el proceso de verificación, por ejemplo la cantidad de espacio de disco reclamado, el número de entradas válidas, número de entradas removidas, etc. ##### Opciones * `ops.uid` - uid para asignarle al caché y su contenido * `ops.gid` - gid para asignarle al caché y su contenido * `ops.filter` - recibe una entrada como argumento. Devuelve falso para removerla. Nota: es posible que esta función sea invocada con la misma entrada más de una vez. ##### Example ```sh echo somegarbage >> $RUTACACHE/content/deadbeef ``` ```javascript cacache.verifica(rutaCache).then(stats => { // deadbeef collected, because of invalid checksum. console.log('cache is much nicer now! stats:', stats) }) ``` #### `> cacache.verifica.ultimaVez(cache) -> Promise` Alias: `últimaVez` Devuelve un `Date` que representa la última vez que `cacache.verifica` fue ejecutada en `cache`. ##### Example ```javascript cacache.verifica(rutaCache).then(() => { cacache.verifica.ultimaVez(rutaCache).then(última => { console.log('La última vez que se usó cacache.verifica() fue ' + última) }) }) ``` PK!Lyyls.jsnu['use strict' var index = require('./lib/entry-index') module.exports = index.ls module.exports.stream = index.lsStream PK!6::index.jsnu['use strict' module.exports = require('./locales/en.js') PK!gM|PP README.mdnu[# 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) [`cacache`](https://github.com/npm/cacache) is a Node.js library for managing local key and content address caches. It's really fast, really good at concurrency, and it will never give you corrupted data, even if cache files get corrupted or manipulated. On systems that support user and group settings on files, cacache will match the `uid` and `gid` values to the folder where the cache lives, even when running as `root`. It was written to be used as [npm](https://npm.im)'s local cache, but can just as easily be used on its own. _Translations: [español](README.es.md)_ ## Install `$ npm install --save cacache` ## Table of Contents * [Example](#example) * [Features](#features) * [Contributing](#contributing) * [API](#api) * [Using localized APIs](#localized-api) * Reading * [`ls`](#ls) * [`ls.stream`](#ls-stream) * [`get`](#get-data) * [`get.stream`](#get-stream) * [`get.info`](#get-info) * [`get.hasContent`](#get-hasContent) * Writing * [`put`](#put-data) * [`put.stream`](#put-stream) * [`put*` opts](#put-options) * [`rm.all`](#rm-all) * [`rm.entry`](#rm-entry) * [`rm.content`](#rm-content) * Utilities * [`setLocale`](#set-locale) * [`clearMemoized`](#clear-memoized) * [`tmp.mkdir`](#tmp-mkdir) * [`tmp.withTmp`](#with-tmp) * Integrity * [Subresource Integrity](#integrity) * [`verify`](#verify) * [`verify.lastRun`](#verify-last-run) ### Example ```javascript const cacache = require('cacache/en') const fs = require('fs') const tarball = '/path/to/mytar.tgz' const cachePath = '/tmp/my-toy-cache' const key = 'my-unique-key-1234' // Cache it! Use `cachePath` as the root of the content cache cacache.put(cachePath, key, '10293801983029384').then(integrity => { console.log(`Saved content to ${cachePath}.`) }) const destination = '/tmp/mytar.tgz' // Copy the contents out of the cache and into their destination! // But this time, use stream instead! cacache.get.stream( cachePath, key ).pipe( fs.createWriteStream(destination) ).on('finish', () => { console.log('done extracting!') }) // The same thing, but skip the key index. cacache.get.byDigest(cachePath, integrityHash).then(data => { fs.writeFile(destination, data, err => { console.log('tarball data fetched based on its sha512sum and written out!') }) }) ``` ### Features * Extraction by key or by content address (shasum, etc) * [Subresource Integrity](#integrity) web standard support * Multi-hash support - safely host sha1, sha512, etc, in a single cache * Automatic content deduplication * Fault tolerance (immune to corruption, partial writes, process races, etc) * Consistency guarantees on read and write (full data verification) * Lockless, high-concurrency cache access * Streaming support * Promise support * Pretty darn fast -- sub-millisecond reads and writes including verification * Arbitrary metadata storage * Garbage collection and additional offline verification * Thorough test coverage * There's probably a bloom filter in there somewhere. Those are cool, right? 🤔 ### Contributing 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. 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. Please refer to the [Changelog](CHANGELOG.md) for project history details, too. Happy hacking! ### API #### Using localized APIs cacache includes a complete API in English, with the same features as other translations. To use the English API as documented in this README, use `require('cacache/en')`. This is also currently the default if you do `require('cacache')`, but may change in the future. cacache also supports other languages! You can find the list of currently supported ones by looking in `./locales` in the source directory. You can use the API in that language with `require('cacache/')`. Want to add support for a new language? Please go ahead! You should be able to copy `./locales/en.js` and `./locales/en.json` and fill them in. Translating the `README.md` is a bit more work, but also appreciated if you get around to it. 👍🏼 #### `> cacache.ls(cache) -> Promise` Lists info for all entries currently in the cache as a single large object. Each entry in the object will be keyed by the unique index key, with corresponding [`get.info`](#get-info) objects as the values. ##### Example ```javascript cacache.ls(cachePath).then(console.log) // Output { 'my-thing': { key: 'my-thing', integrity: 'sha512-BaSe64/EnCoDED+HAsh==' path: '.testcache/content/deadbeef', // joined with `cachePath` time: 12345698490, size: 4023948, metadata: { name: 'blah', version: '1.2.3', description: 'this was once a package but now it is my-thing' } }, 'other-thing': { key: 'other-thing', integrity: 'sha1-ANothER+hasH=', path: '.testcache/content/bada55', time: 11992309289, size: 111112 } } ``` #### `> cacache.ls.stream(cache) -> Readable` Lists info for all entries currently in the cache as a single large object. This works just like [`ls`](#ls), except [`get.info`](#get-info) entries are returned as `'data'` events on the returned stream. ##### Example ```javascript cacache.ls.stream(cachePath).on('data', console.log) // Output { key: 'my-thing', integrity: 'sha512-BaSe64HaSh', path: '.testcache/content/deadbeef', // joined with `cachePath` time: 12345698490, size: 13423, metadata: { name: 'blah', version: '1.2.3', description: 'this was once a package but now it is my-thing' } } { key: 'other-thing', integrity: 'whirlpool-WoWSoMuchSupport', path: '.testcache/content/bada55', time: 11992309289, size: 498023984029 } { ... } ``` #### `> cacache.get(cache, key, [opts]) -> Promise({data, metadata, integrity})` Returns an object with the cached data, digest, and metadata identified by `key`. The `data` property of this object will be a `Buffer` instance that presumably holds some data that means something to you. I'm sure you know what to do with it! cacache just won't care. `integrity` is a [Subresource Integrity](#integrity) string. That is, a string that can be used to verify `data`, which looks like `-`. If there is no content identified by `key`, or if the locally-stored data does not pass the validity checksum, the promise will be rejected. A sub-function, `get.byDigest` may be used for identical behavior, except lookup will happen by integrity hash, bypassing the index entirely. This version of the function *only* returns `data` itself, without any wrapper. ##### Note This function loads the entire cache entry into memory before returning it. If you're dealing with Very Large data, consider using [`get.stream`](#get-stream) instead. ##### Example ```javascript // Look up by key cache.get(cachePath, 'my-thing').then(console.log) // Output: { metadata: { thingName: 'my' }, integrity: 'sha512-BaSe64HaSh', data: Buffer#, size: 9320 } // Look up by digest cache.get.byDigest(cachePath, 'sha512-BaSe64HaSh').then(console.log) // Output: Buffer# ``` #### `> cacache.get.stream(cache, key, [opts]) -> Readable` Returns a [Readable Stream](https://nodejs.org/api/stream.html#stream_readable_streams) of the cached data identified by `key`. If there is no content identified by `key`, or if the locally-stored data does not pass the validity checksum, an error will be emitted. `metadata` and `integrity` events will be emitted before the stream closes, if you need to collect that extra data about the cached entry. A sub-function, `get.stream.byDigest` may be used for identical behavior, except lookup will happen by integrity hash, bypassing the index entirely. This version does not emit the `metadata` and `integrity` events at all. ##### Example ```javascript // Look up by key cache.get.stream( cachePath, 'my-thing' ).on('metadata', metadata => { console.log('metadata:', metadata) }).on('integrity', integrity => { console.log('integrity:', integrity) }).pipe( fs.createWriteStream('./x.tgz') ) // Outputs: metadata: { ... } integrity: 'sha512-SoMeDIGest+64==' // Look up by digest cache.get.stream.byDigest( cachePath, 'sha512-SoMeDIGest+64==' ).pipe( fs.createWriteStream('./x.tgz') ) ``` #### `> cacache.get.info(cache, key) -> Promise` Looks up `key` in the cache index, returning information about the entry if one exists. ##### Fields * `key` - Key the entry was looked up under. Matches the `key` argument. * `integrity` - [Subresource Integrity hash](#integrity) for the content this entry refers to. * `path` - Filesystem path where content is stored, joined with `cache` argument. * `time` - Timestamp the entry was first added on. * `metadata` - User-assigned metadata associated with the entry/content. ##### Example ```javascript cacache.get.info(cachePath, 'my-thing').then(console.log) // Output { key: 'my-thing', integrity: 'sha256-MUSTVERIFY+ALL/THINGS==' path: '.testcache/content/deadbeef', time: 12345698490, size: 849234, metadata: { name: 'blah', version: '1.2.3', description: 'this was once a package but now it is my-thing' } } ``` #### `> cacache.get.hasContent(cache, integrity) -> Promise` Looks up a [Subresource Integrity hash](#integrity) in the cache. If content exists for this `integrity`, it will return an object, with the specific single integrity hash 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`. ##### Example ```javascript cacache.get.hasContent(cachePath, 'sha256-MUSTVERIFY+ALL/THINGS==').then(console.log) // Output { sri: { source: 'sha256-MUSTVERIFY+ALL/THINGS==', algorithm: 'sha256', digest: 'MUSTVERIFY+ALL/THINGS==', options: [] }, size: 9001 } cacache.get.hasContent(cachePath, 'sha521-NOT+IN/CACHE==').then(console.log) // Output false ``` #### `> cacache.put(cache, key, data, [opts]) -> Promise` Inserts data passed to it into the cache. The returned Promise resolves with a digest (generated according to [`opts.algorithms`](#optsalgorithms)) after the cache entry has been successfully written. ##### Example ```javascript fetch( 'https://registry.npmjs.org/cacache/-/cacache-1.0.0.tgz' ).then(data => { return cacache.put(cachePath, 'registry.npmjs.org|cacache@1.0.0', data) }).then(integrity => { console.log('integrity hash is', integrity) }) ``` #### `> cacache.put.stream(cache, key, [opts]) -> Writable` Returns a [Writable Stream](https://nodejs.org/api/stream.html#stream_writable_streams) that inserts data written to it into the cache. Emits an `integrity` event with the digest of written contents when it succeeds. ##### Example ```javascript request.get( 'https://registry.npmjs.org/cacache/-/cacache-1.0.0.tgz' ).pipe( cacache.put.stream( cachePath, 'registry.npmjs.org|cacache@1.0.0' ).on('integrity', d => console.log(`integrity digest is ${d}`)) ) ``` #### `> cacache.put options` `cacache.put` functions have a number of options in common. ##### `opts.metadata` Arbitrary metadata to be attached to the inserted key. ##### `opts.size` If provided, the data stream will be verified to check that enough data was passed through. If there's more or less data than expected, insertion will fail with an `EBADSIZE` error. ##### `opts.integrity` If present, the pre-calculated digest for the inserted content. If this option if provided and does not match the post-insertion digest, insertion will fail with an `EINTEGRITY` error. `algorithms` has no effect if this option is present. ##### `opts.algorithms` Default: ['sha512'] Hashing algorithms to use when calculating the [subresource integrity digest](#integrity) for inserted data. Can use any algorithm listed in `crypto.getHashes()` or `'omakase'`/`'お任せします'` to pick a random hash algorithm on each insertion. You may also use any anagram of `'modnar'` to use this feature. Currently only supports one algorithm at a time (i.e., an array length of exactly `1`). Has no effect if `opts.integrity` is present. ##### `opts.memoize` Default: null If provided, cacache will memoize the given cache insertion in memory, bypassing any filesystem checks for that key or digest in future cache fetches. Nothing will be written to the in-memory cache unless this option is explicitly truthy. If `opts.memoize` is an object or a `Map`-like (that is, an object with `get` and `set` methods), it will be written to instead of the global memoization cache. Reading from disk data can be forced by explicitly passing `memoize: false` to the reader functions, but their default will be to read from memory. #### `> cacache.rm.all(cache) -> Promise` Clears the entire cache. Mainly by blowing away the cache directory itself. ##### Example ```javascript cacache.rm.all(cachePath).then(() => { console.log('THE APOCALYPSE IS UPON US 😱') }) ``` #### `> cacache.rm.entry(cache, key) -> Promise` Alias: `cacache.rm` Removes the index entry for `key`. Content will still be accessible if requested directly by content address ([`get.stream.byDigest`](#get-stream)). To remove the content itself (which might still be used by other entries), use [`rm.content`](#rm-content). Or, to safely vacuum any unused content, use [`verify`](#verify). ##### Example ```javascript cacache.rm.entry(cachePath, 'my-thing').then(() => { console.log('I did not like it anyway') }) ``` #### `> cacache.rm.content(cache, integrity) -> Promise` Removes the content identified by `integrity`. Any index entries referring to it will not be usable again until the content is re-added to the cache with an identical digest. ##### Example ```javascript cacache.rm.content(cachePath, 'sha512-SoMeDIGest/IN+BaSE64==').then(() => { console.log('data for my-thing is gone!') }) ``` #### `> cacache.setLocale(locale)` Configure the language/locale used for messages and errors coming from cacache. The list of available locales is in the `./locales` directory in the project root. _Interested in contributing more languages! [Submit a PR](CONTRIBUTING.md)!_ #### `> cacache.clearMemoized()` Completely resets the in-memory entry cache. #### `> tmp.mkdir(cache, opts) -> Promise` Returns a unique temporary directory inside the cache's `tmp` dir. This directory will use the same safe user assignment that all the other stuff use. Once the directory is made, it's the user's responsibility that all files within are given the appropriate `gid`/`uid` ownership settings to match the rest of the cache. If not, you can ask cacache to do it for you by calling [`tmp.fix()`](#tmp-fix), which will fix all tmp directory permissions. If you want automatic cleanup of this directory, use [`tmp.withTmp()`](#with-tpm) ##### Example ```javascript cacache.tmp.mkdir(cache).then(dir => { fs.writeFile(path.join(dir, 'blablabla'), Buffer#<1234>, ...) }) ``` #### `> tmp.fix(cache) -> Promise` Sets the `uid` and `gid` properties on all files and folders within the tmp folder to match the rest of the cache. Use this after manually writing files into [`tmp.mkdir`](#tmp-mkdir) or [`tmp.withTmp`](#with-tmp). ##### Example ```javascript cacache.tmp.mkdir(cache).then(dir => { writeFile(path.join(dir, 'file'), someData).then(() => { // make sure we didn't just put a root-owned file in the cache cacache.tmp.fix().then(() => { // all uids and gids match now }) }) }) ``` #### `> tmp.withTmp(cache, opts, cb) -> Promise` Creates a temporary directory with [`tmp.mkdir()`](#tmp-mkdir) and calls `cb` with it. The created temporary directory will be removed when the return value of `cb()` resolves -- that is, if you return a Promise from `cb()`, the tmp directory will be automatically deleted once that promise completes. The same caveats apply when it comes to managing permissions for the tmp dir's contents. ##### Example ```javascript cacache.tmp.withTmp(cache, dir => { return fs.writeFileAsync(path.join(dir, 'blablabla'), Buffer#<1234>, ...) }).then(() => { // `dir` no longer exists }) ``` #### Subresource Integrity Digests For content verification and addressing, cacache uses strings following the [Subresource Integrity spec](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). That is, any time cacache expects an `integrity` argument or option, it should be in the format `-`. One deviation from the current spec is that cacache will support any hash algorithms supported by the underlying Node.js process. You can use `crypto.getHashes()` to see which ones you can use. ##### Generating Digests Yourself If you have an existing content shasum, they are generally formatted as a hexadecimal string (that is, a sha1 would look like: `5f5513f8822fdbe5145af33b64d8d970dcf95c6e`). In order to be compatible with cacache, you'll need to convert this to an equivalent subresource integrity string. For this example, the corresponding hash would be: `sha1-X1UT+IIv2+UUWvM7ZNjZcNz5XG4=`. If you want to generate an integrity string yourself for existing data, you can use something like this: ```javascript const crypto = require('crypto') const hashAlgorithm = 'sha512' const data = 'foobarbaz' const integrity = ( hashAlgorithm + '-' + crypto.createHash(hashAlgorithm).update(data).digest('base64') ) ``` You can also use [`ssri`](https://npm.im/ssri) to have a richer set of functionality around SRI strings, including generation, parsing, and translating from existing hex-formatted strings. #### `> cacache.verify(cache, opts) -> Promise` Checks out and fixes up your cache: * Cleans up corrupted or invalid index entries. * Custom entry filtering options. * Garbage collects any content entries not referenced by the index. * Checks integrity for all content entries and removes invalid content. * Fixes cache ownership. * Removes the `tmp` directory in the cache and all its contents. When it's done, it'll return an object with various stats about the verification process, including amount of storage reclaimed, number of valid entries, number of entries removed, etc. ##### Options * `opts.filter` - receives a formatted entry. Return false to remove it. Note: might be called more than once on the same entry. ##### Example ```sh echo somegarbage >> $CACHEPATH/content/deadbeef ``` ```javascript cacache.verify(cachePath).then(stats => { // deadbeef collected, because of invalid checksum. console.log('cache is much nicer now! stats:', stats) }) ``` #### `> cacache.verify.lastRun(cache) -> Promise` Returns a `Date` representing the last time `cacache.verify` was run on `cache`. ##### Example ```javascript cacache.verify(cachePath).then(() => { cacache.verify.lastRun(cachePath).then(lastTime => { console.log('cacache.verify was last called on' + lastTime) }) }) ``` PK!:Eƕrm.jsnu['use strict' const BB = require('bluebird') const index = require('./lib/entry-index') const memo = require('./lib/memoization') const path = require('path') const rimraf = BB.promisify(require('rimraf')) const rmContent = require('./lib/content/rm') module.exports = entry module.exports.entry = entry function entry (cache, key) { memo.clearMemoized() return index.delete(cache, key) } module.exports.content = content function content (cache, integrity) { memo.clearMemoized() return rmContent(cache, integrity) } module.exports.all = all function all (cache) { memo.clearMemoized() return rimraf(path.join(cache, '*(content-*|index-*)')) } PK!d?::es.jsnu['use strict' module.exports = require('./locales/es.js') PK!n## locales/en.jsnu['use strict' const ls = require('../ls.js') const get = require('../get.js') const put = require('../put.js') const rm = require('../rm.js') const verify = require('../verify.js') const setLocale = require('../lib/util/y.js').setLocale const clearMemoized = require('../lib/memoization.js').clearMemoized const tmp = require('../lib/util/tmp.js') setLocale('en') const x = module.exports x.ls = cache => ls(cache) x.ls.stream = cache => ls.stream(cache) x.get = (cache, key, opts) => get(cache, key, opts) x.get.byDigest = (cache, hash, opts) => get.byDigest(cache, hash, opts) x.get.sync = (cache, key, opts) => get.sync(cache, key, opts) x.get.sync.byDigest = (cache, key, opts) => get.sync.byDigest(cache, key, opts) x.get.stream = (cache, key, opts) => get.stream(cache, key, opts) x.get.stream.byDigest = (cache, hash, opts) => get.stream.byDigest(cache, hash, opts) x.get.copy = (cache, key, dest, opts) => get.copy(cache, key, dest, opts) x.get.copy.byDigest = (cache, hash, dest, opts) => get.copy.byDigest(cache, hash, dest, opts) x.get.info = (cache, key) => get.info(cache, key) x.get.hasContent = (cache, hash) => get.hasContent(cache, hash) x.get.hasContent.sync = (cache, hash) => get.hasContent.sync(cache, hash) x.put = (cache, key, data, opts) => put(cache, key, data, opts) x.put.stream = (cache, key, opts) => put.stream(cache, key, opts) x.rm = (cache, key) => rm.entry(cache, key) x.rm.all = cache => rm.all(cache) x.rm.entry = x.rm x.rm.content = (cache, hash) => rm.content(cache, hash) x.setLocale = lang => setLocale(lang) x.clearMemoized = () => clearMemoized() x.tmp = {} x.tmp.mkdir = (cache, opts) => tmp.mkdir(cache, opts) x.tmp.withTmp = (cache, opts, cb) => tmp.withTmp(cache, opts, cb) x.verify = (cache, opts) => verify(cache, opts) x.verify.lastRun = cache => verify.lastRun(cache) PK!$v.locales/en.jsonnu[{ "No cache entry for `%s` found in `%s`": "No cache entry for %s found in %s", "Integrity verification failed for %s (%s)": "Integrity verification failed for %s (%s)", "Bad data size: expected inserted data to be %s bytes, but got %s instead": "Bad data size: expected inserted data to be %s bytes, but got %s instead", "Cache input stream was empty": "Cache input stream was empty", "Integrity check failed:\n Wanted: %s\n Found: %s": "Integrity check failed:\n Wanted: %s\n Found: %s" }PK!w  locales/es.jsnu['use strict' const ls = require('../ls.js') const get = require('../get.js') const put = require('../put.js') const rm = require('../rm.js') const verify = require('../verify.js') const setLocale = require('../lib/util/y.js').setLocale const clearMemoized = require('../lib/memoization.js').clearMemoized const tmp = require('../lib/util/tmp.js') setLocale('es') const x = module.exports x.ls = cache => ls(cache) x.ls.flujo = cache => ls.stream(cache) x.saca = (cache, clave, ops) => get(cache, clave, ops) x.saca.porHacheo = (cache, hacheo, ops) => get.byDigest(cache, hacheo, ops) x.saca.sinc = (cache, clave, ops) => get.sync(cache, clave, ops) x.saca.sinc.porHacheo = (cache, hacheo, ops) => get.sync.byDigest(cache, hacheo, ops) x.saca.flujo = (cache, clave, ops) => get.stream(cache, clave, ops) x.saca.flujo.porHacheo = (cache, hacheo, ops) => get.stream.byDigest(cache, hacheo, ops) x.sava.copia = (cache, clave, destino, opts) => get.copy(cache, clave, destino, opts) x.sava.copia.porHacheo = (cache, hacheo, destino, opts) => get.copy.byDigest(cache, hacheo, destino, opts) x.saca.info = (cache, clave) => get.info(cache, clave) x.saca.tieneDatos = (cache, hacheo) => get.hasContent(cache, hacheo) x.saca.tieneDatos.sinc = (cache, hacheo) => get.hasContent.sync(cache, hacheo) x.mete = (cache, clave, datos, ops) => put(cache, clave, datos, ops) x.mete.flujo = (cache, clave, ops) => put.stream(cache, clave, ops) x.rm = (cache, clave) => rm.entry(cache, clave) x.rm.todo = cache => rm.all(cache) x.rm.entrada = x.rm x.rm.datos = (cache, hacheo) => rm.content(cache, hacheo) x.ponLenguaje = lang => setLocale(lang) x.limpiaMemoizado = () => clearMemoized() x.tmp = {} x.tmp.mkdir = (cache, ops) => tmp.mkdir(cache, ops) x.tmp.hazdir = x.tmp.mkdir x.tmp.conTmp = (cache, ops, cb) => tmp.withTmp(cache, ops, cb) x.verifica = (cache, ops) => verify(cache, ops) x.verifica.ultimaVez = cache => verify.lastRun(cache) x.verifica.últimaVez = x.verifica.ultimaVez PK!f9.locales/es.jsonnu[{ "No cache entry for `%s` found in `%s`": "No existe ninguna entrada para «%s» en «%s»", "Integrity verification failed for %s (%s)": "Verificación de integridad falló para «%s» (%s)", "Bad data size: expected inserted data to be %s bytes, but got %s instead": "Tamaño incorrecto de datos: los datos insertados debieron haber sido %s octetos, pero fueron %s", "Cache input stream was empty": "El stream de entrada al caché estaba vacío" } PK!(} ## [11.3.2](https://github.com/npm/cacache/compare/v11.3.1...v11.3.2) (2018-12-21) ### Bug Fixes * **get:** make sure to handle errors in the .then ([b10bcd0](https://github.com/npm/cacache/commit/b10bcd0)) ## [11.3.1](https://github.com/npm/cacache/compare/v11.3.0...v11.3.1) (2018-11-05) ### Bug Fixes * **get:** export hasContent.sync properly ([d76c920](https://github.com/npm/cacache/commit/d76c920)) # [11.3.0](https://github.com/npm/cacache/compare/v11.2.0...v11.3.0) (2018-11-05) ### Features * **get:** add sync API for reading ([db1e094](https://github.com/npm/cacache/commit/db1e094)) # [11.2.0](https://github.com/npm/cacache/compare/v11.1.0...v11.2.0) (2018-08-08) ### Features * **read:** add sync support to other internal read.js fns ([fe638b6](https://github.com/npm/cacache/commit/fe638b6)) # [11.1.0](https://github.com/npm/cacache/compare/v11.0.3...v11.1.0) (2018-08-01) ### Features * **read:** add sync support for low-level content read ([b43af83](https://github.com/npm/cacache/commit/b43af83)) ## [11.0.3](https://github.com/npm/cacache/compare/v11.0.2...v11.0.3) (2018-08-01) ### Bug Fixes * **config:** add ssri config options ([#136](https://github.com/npm/cacache/issues/136)) ([10d5d9a](https://github.com/npm/cacache/commit/10d5d9a)) * **perf:** refactor content.read to avoid lstats ([c5ac10e](https://github.com/npm/cacache/commit/c5ac10e)) * **test:** oops when removing safe-buffer ([1950490](https://github.com/npm/cacache/commit/1950490)) ## [11.0.2](https://github.com/npm/cacache/compare/v11.0.1...v11.0.2) (2018-05-07) ### Bug Fixes * **verify:** size param no longer lost in a verify ([#131](https://github.com/npm/cacache/issues/131)) ([c614a19](https://github.com/npm/cacache/commit/c614a19)), closes [#130](https://github.com/npm/cacache/issues/130) ## [11.0.1](https://github.com/npm/cacache/compare/v11.0.0...v11.0.1) (2018-04-10) # [11.0.0](https://github.com/npm/cacache/compare/v10.0.4...v11.0.0) (2018-04-09) ### Features * **opts:** use figgy-pudding for opts ([#128](https://github.com/npm/cacache/issues/128)) ([33d4eed](https://github.com/npm/cacache/commit/33d4eed)) ### meta * drop support for node@4 ([529f347](https://github.com/npm/cacache/commit/529f347)) ### BREAKING CHANGES * node@4 is no longer supported ## [10.0.4](https://github.com/npm/cacache/compare/v10.0.3...v10.0.4) (2018-02-16) ## [10.0.3](https://github.com/npm/cacache/compare/v10.0.2...v10.0.3) (2018-02-16) ### Bug Fixes * **content:** rethrow aggregate errors as ENOENT ([fa918f5](https://github.com/npm/cacache/commit/fa918f5)) ## [10.0.2](https://github.com/npm/cacache/compare/v10.0.1...v10.0.2) (2018-01-07) ### Bug Fixes * **ls:** deleted entries could cause a premature stream EOF ([347dc36](https://github.com/npm/cacache/commit/347dc36)) ## [10.0.1](https://github.com/npm/cacache/compare/v10.0.0...v10.0.1) (2017-11-15) ### Bug Fixes * **move-file:** actually use the fallback to `move-concurrently` (#110) ([073fbe1](https://github.com/npm/cacache/commit/073fbe1)) # [10.0.0](https://github.com/npm/cacache/compare/v9.3.0...v10.0.0) (2017-10-23) ### Features * **license:** relicense to ISC (#111) ([fdbb4e5](https://github.com/npm/cacache/commit/fdbb4e5)) ### Performance Improvements * more copyFile benchmarks ([63787bb](https://github.com/npm/cacache/commit/63787bb)) ### BREAKING CHANGES * **license:** the license has been changed from CC0-1.0 to ISC. # [9.3.0](https://github.com/npm/cacache/compare/v9.2.9...v9.3.0) (2017-10-07) ### Features * **copy:** added cacache.get.copy api for fast copies (#107) ([067b5f6](https://github.com/npm/cacache/commit/067b5f6)) ## [9.2.9](https://github.com/npm/cacache/compare/v9.2.8...v9.2.9) (2017-06-17) ## [9.2.8](https://github.com/npm/cacache/compare/v9.2.7...v9.2.8) (2017-06-05) ### Bug Fixes * **ssri:** bump ssri for bugfix ([c3232ea](https://github.com/npm/cacache/commit/c3232ea)) ## [9.2.7](https://github.com/npm/cacache/compare/v9.2.6...v9.2.7) (2017-06-05) ### Bug Fixes * **content:** make verified content completely read-only (#96) ([4131196](https://github.com/npm/cacache/commit/4131196)) ## [9.2.6](https://github.com/npm/cacache/compare/v9.2.5...v9.2.6) (2017-05-31) ### Bug Fixes * **node:** update ssri to prevent old node 4 crash ([5209ffe](https://github.com/npm/cacache/commit/5209ffe)) ## [9.2.5](https://github.com/npm/cacache/compare/v9.2.4...v9.2.5) (2017-05-25) ### Bug Fixes * **deps:** fix lockfile issues and bump ssri ([84e1d7e](https://github.com/npm/cacache/commit/84e1d7e)) ## [9.2.4](https://github.com/npm/cacache/compare/v9.2.3...v9.2.4) (2017-05-24) ### Bug Fixes * **deps:** bumping deps ([bbccb12](https://github.com/npm/cacache/commit/bbccb12)) ## [9.2.3](https://github.com/npm/cacache/compare/v9.2.2...v9.2.3) (2017-05-24) ### Bug Fixes * **rm:** stop crashing if content is missing on rm ([ac90bc0](https://github.com/npm/cacache/commit/ac90bc0)) ## [9.2.2](https://github.com/npm/cacache/compare/v9.2.1...v9.2.2) (2017-05-14) ### Bug Fixes * **i18n:** lets pretend this didn't happen ([519b4ee](https://github.com/npm/cacache/commit/519b4ee)) ## [9.2.1](https://github.com/npm/cacache/compare/v9.2.0...v9.2.1) (2017-05-14) ### Bug Fixes * **docs:** fixing translation messup ([bb9e4f9](https://github.com/npm/cacache/commit/bb9e4f9)) # [9.2.0](https://github.com/npm/cacache/compare/v9.1.0...v9.2.0) (2017-05-14) ### Features * **i18n:** add Spanish translation for API ([531f9a4](https://github.com/npm/cacache/commit/531f9a4)) # [9.1.0](https://github.com/npm/cacache/compare/v9.0.0...v9.1.0) (2017-05-14) ### Features * **i18n:** Add Spanish translation and i18n setup (#91) ([323b90c](https://github.com/npm/cacache/commit/323b90c)) # [9.0.0](https://github.com/npm/cacache/compare/v8.0.0...v9.0.0) (2017-04-28) ### Bug Fixes * **memoization:** actually use the LRU ([0e55dc9](https://github.com/npm/cacache/commit/0e55dc9)) ### Features * **memoization:** memoizers can be injected through opts.memoize (#90) ([e5614c7](https://github.com/npm/cacache/commit/e5614c7)) ### BREAKING CHANGES * **memoization:** If you were passing an object to opts.memoize, it will now be used as an injected memoization object. If you were only passing booleans and other non-objects through that option, no changes are needed. # [8.0.0](https://github.com/npm/cacache/compare/v7.1.0...v8.0.0) (2017-04-22) ### Features * **read:** change hasContent to return {sri, size} (#88) ([bad6c49](https://github.com/npm/cacache/commit/bad6c49)), closes [#87](https://github.com/npm/cacache/issues/87) ### BREAKING CHANGES * **read:** hasContent now returns an object with `{sri, size}` instead of `sri`. Use `result.sri` anywhere that needed the old return value. # [7.1.0](https://github.com/npm/cacache/compare/v7.0.5...v7.1.0) (2017-04-20) ### Features * **size:** handle content size info (#49) ([91230af](https://github.com/npm/cacache/commit/91230af)) ## [7.0.5](https://github.com/npm/cacache/compare/v7.0.4...v7.0.5) (2017-04-18) ### Bug Fixes * **integrity:** new ssri with fixed integrity stream ([6d13e8e](https://github.com/npm/cacache/commit/6d13e8e)) * **write:** wrap stuff in promises to improve errors ([3624fc5](https://github.com/npm/cacache/commit/3624fc5)) ## [7.0.4](https://github.com/npm/cacache/compare/v7.0.3...v7.0.4) (2017-04-15) ### Bug Fixes * **fix-owner:** throw away ENOENTs on chownr ([d49bbcd](https://github.com/npm/cacache/commit/d49bbcd)) ## [7.0.3](https://github.com/npm/cacache/compare/v7.0.2...v7.0.3) (2017-04-05) ### Bug Fixes * **read:** fixing error message for integrity verification failures ([9d4f0a5](https://github.com/npm/cacache/commit/9d4f0a5)) ## [7.0.2](https://github.com/npm/cacache/compare/v7.0.1...v7.0.2) (2017-04-03) ### Bug Fixes * **integrity:** use EINTEGRITY error code and update ssri ([8dc2e62](https://github.com/npm/cacache/commit/8dc2e62)) ## [7.0.1](https://github.com/npm/cacache/compare/v7.0.0...v7.0.1) (2017-04-03) ### Bug Fixes * **docs:** fix header name conflict in readme ([afcd456](https://github.com/npm/cacache/commit/afcd456)) # [7.0.0](https://github.com/npm/cacache/compare/v6.3.0...v7.0.0) (2017-04-03) ### Bug Fixes * **test:** fix content.write tests when running in docker ([d2e9b6a](https://github.com/npm/cacache/commit/d2e9b6a)) ### Features * **integrity:** subresource integrity support (#78) ([b1e731f](https://github.com/npm/cacache/commit/b1e731f)) ### BREAKING CHANGES * **integrity:** The entire API has been overhauled to use SRI hashes instead of digest/hashAlgorithm pairs. SRI hashes follow the Subresource Integrity standard and support strings and objects compatible with [`ssri`](https://npm.im/ssri). * This change bumps the index version, which will invalidate all previous index entries. Content entries will remain intact, and existing caches will automatically reuse any content from before this breaking change. * `cacache.get.info()`, `cacache.ls()`, and `cacache.ls.stream()` will now return objects that looks like this: ``` { key: String, integrity: '-', path: ContentPath, time: Date, metadata: Any } ``` * `opts.digest` and `opts.hashAlgorithm` are obsolete for any API calls that used them. * Anywhere `opts.digest` was accepted, `opts.integrity` is now an option. Any valid SRI hash is accepted here -- multiple hash entries will be resolved according to the standard: first, the "strongest" hash algorithm will be picked, and then each of the entries for that algorithm will be matched against the content. Content will be validated if *any* of the entries match (so, a single integrity string can be used for multiple "versions" of the same document/data). * `put.byDigest()`, `put.stream.byDigest`, `get.byDigest()` and `get.stream.byDigest()` now expect an SRI instead of a `digest` + `opts.hashAlgorithm` pairing. * `get.hasContent()` now expects an integrity hash instead of a digest. If content exists, it will return the specific single integrity hash that was found in the cache. * `verify()` has learned to handle integrity-based caches, and forgotten how to handle old-style cache indices due to the format change. * `cacache.rm.content()` now expects an integrity hash instead of a hex digest. # [6.3.0](https://github.com/npm/cacache/compare/v6.2.0...v6.3.0) (2017-04-01) ### Bug Fixes * **fixOwner:** ignore EEXIST race condition from mkdirp ([4670e9b](https://github.com/npm/cacache/commit/4670e9b)) * **index:** ignore index removal races when inserting ([b9d2fa2](https://github.com/npm/cacache/commit/b9d2fa2)) * **memo:** use lru-cache for better mem management (#75) ([d8ac5aa](https://github.com/npm/cacache/commit/d8ac5aa)) ### Features * **dependencies:** Switch to move-concurrently (#77) ([dc6482d](https://github.com/npm/cacache/commit/dc6482d)) # [6.2.0](https://github.com/npm/cacache/compare/v6.1.2...v6.2.0) (2017-03-15) ### Bug Fixes * **index:** additional bucket entry verification with checksum (#72) ([f8e0f25](https://github.com/npm/cacache/commit/f8e0f25)) * **verify:** return fixOwner.chownr promise ([6818521](https://github.com/npm/cacache/commit/6818521)) ### Features * **tmp:** safe tmp dir creation/management util (#73) ([c42da71](https://github.com/npm/cacache/commit/c42da71)) ## [6.1.2](https://github.com/npm/cacache/compare/v6.1.1...v6.1.2) (2017-03-13) ### Bug Fixes * **index:** set default hashAlgorithm ([d6eb2f0](https://github.com/npm/cacache/commit/d6eb2f0)) ## [6.1.1](https://github.com/npm/cacache/compare/v6.1.0...v6.1.1) (2017-03-13) ### Bug Fixes * **coverage:** bumping coverage for verify (#71) ([0b7faf6](https://github.com/npm/cacache/commit/0b7faf6)) * **deps:** glob should have been a regular dep :< ([0640bc4](https://github.com/npm/cacache/commit/0640bc4)) # [6.1.0](https://github.com/npm/cacache/compare/v6.0.2...v6.1.0) (2017-03-12) ### Bug Fixes * **coverage:** more coverage for content reads (#70) ([ef4f70a](https://github.com/npm/cacache/commit/ef4f70a)) * **tests:** use safe-buffer because omfg (#69) ([6ab8132](https://github.com/npm/cacache/commit/6ab8132)) ### Features * **rm:** limited rm.all and fixed bugs (#66) ([d5d25ba](https://github.com/npm/cacache/commit/d5d25ba)), closes [#66](https://github.com/npm/cacache/issues/66) * **verify:** tested, working cache verifier/gc (#68) ([45ad77a](https://github.com/npm/cacache/commit/45ad77a)) ## [6.0.2](https://github.com/npm/cacache/compare/v6.0.1...v6.0.2) (2017-03-11) ### Bug Fixes * **index:** segment cache items with another subbucket (#64) ([c3644e5](https://github.com/npm/cacache/commit/c3644e5)) ## [6.0.1](https://github.com/npm/cacache/compare/v6.0.0...v6.0.1) (2017-03-05) ### Bug Fixes * **docs:** Missed spots in README ([8ffb7fa](https://github.com/npm/cacache/commit/8ffb7fa)) # [6.0.0](https://github.com/npm/cacache/compare/v5.0.3...v6.0.0) (2017-03-05) ### Bug Fixes * **api:** keep memo cache mostly-internal ([2f72d0a](https://github.com/npm/cacache/commit/2f72d0a)) * **content:** use the rest of the string, not the whole string ([fa8f3c3](https://github.com/npm/cacache/commit/fa8f3c3)) * **deps:** removed `format-number@2.0.2` ([1187791](https://github.com/npm/cacache/commit/1187791)) * **deps:** removed inflight@1.0.6 ([0d1819c](https://github.com/npm/cacache/commit/0d1819c)) * **deps:** rimraf@2.6.1 ([9efab6b](https://github.com/npm/cacache/commit/9efab6b)) * **deps:** standard@9.0.0 ([4202cba](https://github.com/npm/cacache/commit/4202cba)) * **deps:** tap@10.3.0 ([aa03088](https://github.com/npm/cacache/commit/aa03088)) * **deps:** weallcontribute@1.0.8 ([ad4f4dc](https://github.com/npm/cacache/commit/ad4f4dc)) * **docs:** add security note to hashKey ([03f81ba](https://github.com/npm/cacache/commit/03f81ba)) * **hashes:** change default hashAlgorithm to sha512 ([ea00ba6](https://github.com/npm/cacache/commit/ea00ba6)) * **hashes:** missed a spot for hashAlgorithm defaults ([45997d8](https://github.com/npm/cacache/commit/45997d8)) * **index:** add length header before JSON for verification ([fb8cb4d](https://github.com/npm/cacache/commit/fb8cb4d)) * **index:** change index filenames to sha1s of keys ([bbc5fca](https://github.com/npm/cacache/commit/bbc5fca)) * **index:** who cares about race conditions anyway ([b1d3888](https://github.com/npm/cacache/commit/b1d3888)) * **perf:** bulk-read get+read for massive speed ([d26cdf9](https://github.com/npm/cacache/commit/d26cdf9)) * **perf:** use bulk file reads for index reads ([79a8891](https://github.com/npm/cacache/commit/79a8891)) * **put-stream:** remove tmp file on stream insert error ([65f6632](https://github.com/npm/cacache/commit/65f6632)) * **put-stream:** robustified and predictibilized ([daf9e08](https://github.com/npm/cacache/commit/daf9e08)) * **put-stream:** use new promise API for moves ([1d36013](https://github.com/npm/cacache/commit/1d36013)) * **readme:** updated to reflect new default hashAlgo ([c60a2fa](https://github.com/npm/cacache/commit/c60a2fa)) * **verify:** tiny typo fix ([db22d05](https://github.com/npm/cacache/commit/db22d05)) ### Features * **api:** converted external api ([7bf032f](https://github.com/npm/cacache/commit/7bf032f)) * **cacache:** exported clearMemoized() utility ([8d2c5b6](https://github.com/npm/cacache/commit/8d2c5b6)) * **cache:** add versioning to content and index ([31bc549](https://github.com/npm/cacache/commit/31bc549)) * **content:** collate content files into subdirs ([c094d9f](https://github.com/npm/cacache/commit/c094d9f)) * **deps:** `@npmcorp/move@1.0.0` ([bdd00bf](https://github.com/npm/cacache/commit/bdd00bf)) * **deps:** `bluebird@3.4.7` ([3a17aff](https://github.com/npm/cacache/commit/3a17aff)) * **deps:** `promise-inflight@1.0.1` ([a004fe6](https://github.com/npm/cacache/commit/a004fe6)) * **get:** added memoization support for get ([c77d794](https://github.com/npm/cacache/commit/c77d794)) * **get:** export hasContent ([2956ec3](https://github.com/npm/cacache/commit/2956ec3)) * **index:** add hashAlgorithm and format insert ret val ([b639746](https://github.com/npm/cacache/commit/b639746)) * **index:** collate index files into subdirs ([e8402a5](https://github.com/npm/cacache/commit/e8402a5)) * **index:** promisify entry index ([cda3335](https://github.com/npm/cacache/commit/cda3335)) * **memo:** added memoization lib ([da07b92](https://github.com/npm/cacache/commit/da07b92)) * **memo:** export memoization api ([954b1b3](https://github.com/npm/cacache/commit/954b1b3)) * **move-file:** add move fallback for weird errors ([5cf4616](https://github.com/npm/cacache/commit/5cf4616)) * **perf:** bulk content write api ([51b536e](https://github.com/npm/cacache/commit/51b536e)) * **put:** added memoization support to put ([b613a70](https://github.com/npm/cacache/commit/b613a70)) * **read:** switched to promises ([a869362](https://github.com/npm/cacache/commit/a869362)) * **rm:** added memoization support to rm ([4205cf0](https://github.com/npm/cacache/commit/4205cf0)) * **rm:** switched to promises ([a000d24](https://github.com/npm/cacache/commit/a000d24)) * **util:** promise-inflight ownership fix requests ([9517cd7](https://github.com/npm/cacache/commit/9517cd7)) * **util:** use promises for api ([ae204bb](https://github.com/npm/cacache/commit/ae204bb)) * **verify:** converted to Promises ([f0b3974](https://github.com/npm/cacache/commit/f0b3974)) ### BREAKING CHANGES * cache: index/content directories are now versioned. Previous caches are no longer compatible and cannot be migrated. * util: fix-owner now uses Promises instead of callbacks * index: Previously-generated index entries are no longer compatible and the index must be regenerated. * index: The index format has changed and previous caches are no longer compatible. Existing caches will need to be regenerated. * hashes: Default hashAlgorithm changed from sha1 to sha512. If you rely on the prior setting, pass `opts.hashAlgorithm` in explicitly. * content: Previously-generated content directories are no longer compatible and must be regenerated. * verify: API is now promise-based * read: Switches to a Promise-based API and removes callback stuff * rm: Switches to a Promise-based API and removes callback stuff * index: this changes the API to work off promises instead of callbacks * api: this means we are going all in on promises now PK! g LICENSE.mdnu[ISC License Copyright (c) npm, Inc. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE COPYRIGHT HOLDER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. PK!#"77 verify.jsnu['use strict' module.exports = require('./lib/verify') PK!Plib/memoization.jsnu['use strict' const { LRUCache } = require('lru-cache') const MEMOIZED = new LRUCache({ max: 500, maxSize: 50 * 1024 * 1024, // 50MB ttl: 3 * 60 * 1000, // 3 minutes sizeCalculation: (entry, key) => key.startsWith('key:') ? entry.data.length : entry.length, }) module.exports.clearMemoized = clearMemoized function clearMemoized () { const old = {} MEMOIZED.forEach((v, k) => { old[k] = v }) MEMOIZED.clear() return old } module.exports.put = put function put (cache, entry, data, opts) { pickMem(opts).set(`key:${cache}:${entry.key}`, { entry, data }) putDigest(cache, entry.integrity, data, opts) } module.exports.put.byDigest = putDigest function putDigest (cache, integrity, data, opts) { pickMem(opts).set(`digest:${cache}:${integrity}`, data) } module.exports.get = get function get (cache, key, opts) { return pickMem(opts).get(`key:${cache}:${key}`) } module.exports.get.byDigest = getDigest function getDigest (cache, integrity, opts) { return pickMem(opts).get(`digest:${cache}:${integrity}`) } class ObjProxy { constructor (obj) { this.obj = obj } get (key) { return this.obj[key] } set (key, val) { this.obj[key] = val } } function pickMem (opts) { if (!opts || !opts.memoize) { return MEMOIZED } else if (opts.memoize.get && opts.memoize.set) { return opts.memoize } else if (typeof opts.memoize === 'object') { return new ObjProxy(opts.memoize) } else { return MEMOIZED } } PK!q3$3$lib/entry-index.jsnu['use strict' const crypto = require('crypto') const { appendFile, mkdir, readFile, readdir, rm, writeFile, } = require('fs/promises') const { Minipass } = require('minipass') const path = require('path') const ssri = require('ssri') const { tmpName } = require('./util/tmp') const contentPath = require('./content/path') const hashToSegments = require('./util/hash-to-segments') const indexV = require('../package.json')['cache-version'].index const { moveFile } = require('@npmcli/fs') const lsStreamConcurrency = 5 module.exports.NotFoundError = class NotFoundError extends Error { constructor (cache, key) { super(`No cache entry for ${key} found in ${cache}`) this.code = 'ENOENT' this.cache = cache this.key = key } } module.exports.compact = compact async function compact (cache, key, matchFn, opts = {}) { const bucket = bucketPath(cache, key) const entries = await bucketEntries(bucket) const newEntries = [] // we loop backwards because the bottom-most result is the newest // since we add new entries with appendFile for (let i = entries.length - 1; i >= 0; --i) { const entry = entries[i] // a null integrity could mean either a delete was appended // or the user has simply stored an index that does not map // to any content. we determine if the user wants to keep the // null integrity based on the validateEntry function passed in options. // if the integrity is null and no validateEntry is provided, we break // as we consider the null integrity to be a deletion of everything // that came before it. if (entry.integrity === null && !opts.validateEntry) { break } // if this entry is valid, and it is either the first entry or // the newEntries array doesn't already include an entry that // matches this one based on the provided matchFn, then we add // it to the beginning of our list if ((!opts.validateEntry || opts.validateEntry(entry) === true) && (newEntries.length === 0 || !newEntries.find((oldEntry) => matchFn(oldEntry, entry)))) { newEntries.unshift(entry) } } const newIndex = '\n' + newEntries.map((entry) => { const stringified = JSON.stringify(entry) const hash = hashEntry(stringified) return `${hash}\t${stringified}` }).join('\n') const setup = async () => { const target = tmpName(cache, opts.tmpPrefix) await mkdir(path.dirname(target), { recursive: true }) return { target, moved: false, } } const teardown = async (tmp) => { if (!tmp.moved) { return rm(tmp.target, { recursive: true, force: true }) } } const write = async (tmp) => { await writeFile(tmp.target, newIndex, { flag: 'wx' }) await mkdir(path.dirname(bucket), { recursive: true }) // we use @npmcli/move-file directly here because we // want to overwrite the existing file await moveFile(tmp.target, bucket) tmp.moved = true } // write the file atomically const tmp = await setup() try { await write(tmp) } finally { await teardown(tmp) } // we reverse the list we generated such that the newest // entries come first in order to make looping through them easier // the true passed to formatEntry tells it to keep null // integrity values, if they made it this far it's because // validateEntry returned true, and as such we should return it return newEntries.reverse().map((entry) => formatEntry(cache, entry, true)) } module.exports.insert = insert async function insert (cache, key, integrity, opts = {}) { const { metadata, size, time } = opts const bucket = bucketPath(cache, key) const entry = { key, integrity: integrity && ssri.stringify(integrity), time: time || Date.now(), size, metadata, } try { await mkdir(path.dirname(bucket), { recursive: true }) const stringified = JSON.stringify(entry) // NOTE - Cleverness ahoy! // // This works because it's tremendously unlikely for an entry to corrupt // another while still preserving the string length of the JSON in // question. So, we just slap the length in there and verify it on read. // // Thanks to @isaacs for the whiteboarding session that ended up with // this. await appendFile(bucket, `\n${hashEntry(stringified)}\t${stringified}`) } catch (err) { if (err.code === 'ENOENT') { return undefined } throw err } return formatEntry(cache, entry) } module.exports.find = find async function find (cache, key) { const bucket = bucketPath(cache, key) try { const entries = await bucketEntries(bucket) return entries.reduce((latest, next) => { if (next && next.key === key) { return formatEntry(cache, next) } else { return latest } }, null) } catch (err) { if (err.code === 'ENOENT') { return null } else { throw err } } } module.exports.delete = del function del (cache, key, opts = {}) { if (!opts.removeFully) { return insert(cache, key, null, opts) } const bucket = bucketPath(cache, key) return rm(bucket, { recursive: true, force: true }) } module.exports.lsStream = lsStream function lsStream (cache) { const indexDir = bucketDir(cache) const stream = new Minipass({ objectMode: true }) // Set all this up to run on the stream and then just return the stream Promise.resolve().then(async () => { const { default: pMap } = await import('p-map') const buckets = await readdirOrEmpty(indexDir) await pMap(buckets, async (bucket) => { const bucketPath = path.join(indexDir, bucket) const subbuckets = await readdirOrEmpty(bucketPath) await pMap(subbuckets, async (subbucket) => { const subbucketPath = path.join(bucketPath, subbucket) // "/cachename//./*" const subbucketEntries = await readdirOrEmpty(subbucketPath) await pMap(subbucketEntries, async (entry) => { const entryPath = path.join(subbucketPath, entry) try { const entries = await bucketEntries(entryPath) // using a Map here prevents duplicate keys from showing up // twice, I guess? const reduced = entries.reduce((acc, entry) => { acc.set(entry.key, entry) return acc }, new Map()) // reduced is a map of key => entry for (const entry of reduced.values()) { const formatted = formatEntry(cache, entry) if (formatted) { stream.write(formatted) } } } catch (err) { if (err.code === 'ENOENT') { return undefined } throw err } }, { concurrency: lsStreamConcurrency }) }, { concurrency: lsStreamConcurrency }) }, { concurrency: lsStreamConcurrency }) stream.end() return stream }).catch(err => stream.emit('error', err)) return stream } module.exports.ls = ls async function ls (cache) { const entries = await lsStream(cache).collect() return entries.reduce((acc, xs) => { acc[xs.key] = xs return acc }, {}) } module.exports.bucketEntries = bucketEntries async function bucketEntries (bucket, filter) { const data = await readFile(bucket, 'utf8') return _bucketEntries(data, filter) } function _bucketEntries (data) { const entries = [] data.split('\n').forEach((entry) => { if (!entry) { return } const pieces = entry.split('\t') if (!pieces[1] || hashEntry(pieces[1]) !== pieces[0]) { // Hash is no good! Corruption or malice? Doesn't matter! // EJECT EJECT return } let obj try { obj = JSON.parse(pieces[1]) } catch (_) { // eslint-ignore-next-line no-empty-block } // coverage disabled here, no need to test with an entry that parses to something falsey // istanbul ignore else if (obj) { entries.push(obj) } }) return entries } module.exports.bucketDir = bucketDir function bucketDir (cache) { return path.join(cache, `index-v${indexV}`) } module.exports.bucketPath = bucketPath function bucketPath (cache, key) { const hashed = hashKey(key) return path.join.apply( path, [bucketDir(cache)].concat(hashToSegments(hashed)) ) } module.exports.hashKey = hashKey function hashKey (key) { return hash(key, 'sha256') } module.exports.hashEntry = hashEntry function hashEntry (str) { return hash(str, 'sha1') } function hash (str, digest) { return crypto .createHash(digest) .update(str) .digest('hex') } function formatEntry (cache, entry, keepAll) { // Treat null digests as deletions. They'll shadow any previous entries. if (!entry.integrity && !keepAll) { return null } return { key: entry.key, integrity: entry.integrity, path: entry.integrity ? contentPath(cache, entry.integrity) : undefined, size: entry.size, time: entry.time, metadata: entry.metadata, } } function readdirOrEmpty (dir) { return readdir(dir).catch((err) => { if (err.code === 'ENOENT' || err.code === 'ENOTDIR') { return [] } throw err }) } PK! |lib/content/read.jsnu['use strict' const fs = require('fs/promises') const fsm = require('fs-minipass') const ssri = require('ssri') const contentPath = require('./path') const Pipeline = require('minipass-pipeline') module.exports = read const MAX_SINGLE_READ_SIZE = 64 * 1024 * 1024 async function read (cache, integrity, opts = {}) { const { size } = opts const { stat, cpath, sri } = await withContentSri(cache, integrity, async (cpath, sri) => { // get size const stat = size ? { size } : await fs.stat(cpath) return { stat, cpath, sri } }) if (stat.size > MAX_SINGLE_READ_SIZE) { return readPipeline(cpath, stat.size, sri, new Pipeline()).concat() } const data = await fs.readFile(cpath, { encoding: null }) if (stat.size !== data.length) { throw sizeError(stat.size, data.length) } if (!ssri.checkData(data, sri)) { throw integrityError(sri, cpath) } return data } const readPipeline = (cpath, size, sri, stream) => { stream.push( new fsm.ReadStream(cpath, { size, readSize: MAX_SINGLE_READ_SIZE, }), ssri.integrityStream({ integrity: sri, size, }) ) return stream } module.exports.stream = readStream module.exports.readStream = readStream function readStream (cache, integrity, opts = {}) { const { size } = opts const stream = new Pipeline() // Set all this up to run on the stream and then just return the stream Promise.resolve().then(async () => { const { stat, cpath, sri } = await withContentSri(cache, integrity, async (cpath, sri) => { // get size const stat = size ? { size } : await fs.stat(cpath) return { stat, cpath, sri } }) return readPipeline(cpath, stat.size, sri, stream) }).catch(err => stream.emit('error', err)) return stream } module.exports.copy = copy function copy (cache, integrity, dest) { return withContentSri(cache, integrity, (cpath) => { return fs.copyFile(cpath, dest) }) } module.exports.hasContent = hasContent async function hasContent (cache, integrity) { if (!integrity) { return false } try { return await withContentSri(cache, integrity, async (cpath, sri) => { const stat = await fs.stat(cpath) return { size: stat.size, sri, stat } }) } catch (err) { if (err.code === 'ENOENT') { return false } if (err.code === 'EPERM') { /* istanbul ignore else */ if (process.platform !== 'win32') { throw err } else { return false } } } } async function withContentSri (cache, integrity, fn) { const sri = ssri.parse(integrity) // If `integrity` has multiple entries, pick the first digest // with available local data. const algo = sri.pickAlgorithm() const digests = sri[algo] if (digests.length <= 1) { const cpath = contentPath(cache, digests[0]) return fn(cpath, digests[0]) } else { // Can't use race here because a generic error can happen before // a ENOENT error, and can happen before a valid result const results = await Promise.all(digests.map(async (meta) => { try { return await withContentSri(cache, meta, fn) } catch (err) { if (err.code === 'ENOENT') { return Object.assign( new Error('No matching content found for ' + sri.toString()), { code: 'ENOENT' } ) } return err } })) // Return the first non error if it is found const result = results.find((r) => !(r instanceof Error)) if (result) { return result } // Throw the No matching content found error const enoentError = results.find((r) => r.code === 'ENOENT') if (enoentError) { throw enoentError } // Throw generic error throw results.find((r) => r instanceof Error) } } function sizeError (expected, found) { /* eslint-disable-next-line max-len */ const err = new Error(`Bad data size: expected inserted data to be ${expected} bytes, but got ${found} instead`) err.expected = expected err.found = found err.code = 'EBADSIZE' return err } function integrityError (sri, path) { const err = new Error(`Integrity verification failed for ${sri} (${path})`) err.code = 'EINTEGRITY' err.sri = sri err.path = path return err } PK!D?lib/content/rm.jsnu['use strict' const fs = require('fs/promises') const contentPath = require('./path') const { hasContent } = require('./read') module.exports = rm async function rm (cache, integrity) { const content = await hasContent(cache, integrity) // ~pretty~ sure we can't end up with a content lacking sri, but be safe if (content && content.sri) { await fs.rm(contentPath(cache, content.sri), { recursive: true, force: true }) return true } else { return false } } PK!*lib/content/path.jsnu['use strict' const contentVer = require('../../package.json')['cache-version'].content const hashToSegments = require('../util/hash-to-segments') const path = require('path') const ssri = require('ssri') // Current format of content file path: // // sha512-BaSE64Hex= -> // ~/.my-cache/content-v2/sha512/ba/da/55deadbeefc0ffee // module.exports = contentPath function contentPath (cache, integrity) { const sri = ssri.parse(integrity, { single: true }) // contentPath is the *strongest* algo given return path.join( contentDir(cache), sri.algorithm, ...hashToSegments(sri.hexDigest()) ) } module.exports.contentDir = contentDir function contentDir (cache) { return path.join(cache, `content-v${contentVer}`) } PK!UUlib/content/write.jsnu['use strict' const events = require('events') const contentPath = require('./path') const fs = require('fs/promises') const { moveFile } = require('@npmcli/fs') const { Minipass } = require('minipass') const Pipeline = require('minipass-pipeline') const Flush = require('minipass-flush') const path = require('path') const ssri = require('ssri') const { tmpName } = require('../util/tmp') const fsm = require('fs-minipass') module.exports = write // Cache of move operations in process so we don't duplicate const moveOperations = new Map() async function write (cache, data, opts = {}) { const { algorithms, size, integrity } = opts if (typeof size === 'number' && data.length !== size) { throw sizeError(size, data.length) } const sri = ssri.fromData(data, algorithms ? { algorithms } : {}) if (integrity && !ssri.checkData(data, integrity, opts)) { throw checksumError(integrity, sri) } for (const algo in sri) { const tmp = await makeTmp(cache, opts) const hash = sri[algo].toString() try { await fs.writeFile(tmp.target, data, { flag: 'wx' }) await moveToDestination(tmp, cache, hash, opts) } finally { if (!tmp.moved) { await fs.rm(tmp.target, { recursive: true, force: true }) } } } return { integrity: sri, size: data.length } } module.exports.stream = writeStream // writes proxied to the 'inputStream' that is passed to the Promise // 'end' is deferred until content is handled. class CacacheWriteStream extends Flush { constructor (cache, opts) { super() this.opts = opts this.cache = cache this.inputStream = new Minipass() this.inputStream.on('error', er => this.emit('error', er)) this.inputStream.on('drain', () => this.emit('drain')) this.handleContentP = null } write (chunk, encoding, cb) { if (!this.handleContentP) { this.handleContentP = handleContent( this.inputStream, this.cache, this.opts ) this.handleContentP.catch(error => this.emit('error', error)) } return this.inputStream.write(chunk, encoding, cb) } flush (cb) { this.inputStream.end(() => { if (!this.handleContentP) { const e = new Error('Cache input stream was empty') e.code = 'ENODATA' // empty streams are probably emitting end right away. // defer this one tick by rejecting a promise on it. return Promise.reject(e).catch(cb) } // eslint-disable-next-line promise/catch-or-return this.handleContentP.then( (res) => { res.integrity && this.emit('integrity', res.integrity) // eslint-disable-next-line promise/always-return res.size !== null && this.emit('size', res.size) cb() }, (er) => cb(er) ) }) } } function writeStream (cache, opts = {}) { return new CacacheWriteStream(cache, opts) } async function handleContent (inputStream, cache, opts) { const tmp = await makeTmp(cache, opts) try { const res = await pipeToTmp(inputStream, cache, tmp.target, opts) await moveToDestination( tmp, cache, res.integrity, opts ) return res } finally { if (!tmp.moved) { await fs.rm(tmp.target, { recursive: true, force: true }) } } } async function pipeToTmp (inputStream, cache, tmpTarget, opts) { const outStream = new fsm.WriteStream(tmpTarget, { flags: 'wx', }) if (opts.integrityEmitter) { // we need to create these all simultaneously since they can fire in any order const [integrity, size] = await Promise.all([ events.once(opts.integrityEmitter, 'integrity').then(res => res[0]), events.once(opts.integrityEmitter, 'size').then(res => res[0]), new Pipeline(inputStream, outStream).promise(), ]) return { integrity, size } } let integrity let size const hashStream = ssri.integrityStream({ integrity: opts.integrity, algorithms: opts.algorithms, size: opts.size, }) hashStream.on('integrity', i => { integrity = i }) hashStream.on('size', s => { size = s }) const pipeline = new Pipeline(inputStream, hashStream, outStream) await pipeline.promise() return { integrity, size } } async function makeTmp (cache, opts) { const tmpTarget = tmpName(cache, opts.tmpPrefix) await fs.mkdir(path.dirname(tmpTarget), { recursive: true }) return { target: tmpTarget, moved: false, } } async function moveToDestination (tmp, cache, sri) { const destination = contentPath(cache, sri) const destDir = path.dirname(destination) if (moveOperations.has(destination)) { return moveOperations.get(destination) } moveOperations.set( destination, fs.mkdir(destDir, { recursive: true }) .then(async () => { await moveFile(tmp.target, destination, { overwrite: false }) tmp.moved = true return tmp.moved }) .catch(err => { if (!err.message.startsWith('The destination file exists')) { throw Object.assign(err, { code: 'EEXIST' }) } }).finally(() => { moveOperations.delete(destination) }) ) return moveOperations.get(destination) } function sizeError (expected, found) { /* eslint-disable-next-line max-len */ const err = new Error(`Bad data size: expected inserted data to be ${expected} bytes, but got ${found} instead`) err.expected = expected err.found = found err.code = 'EBADSIZE' return err } function checksumError (expected, found) { const err = new Error(`Integrity check failed: Wanted: ${expected} Found: ${found}`) err.code = 'EINTEGRITY' err.expected = expected err.found = found return err } PK!{mlib/util/move-file.jsnu['use strict' const fs = require('graceful-fs') const BB = require('bluebird') const chmod = BB.promisify(fs.chmod) const unlink = BB.promisify(fs.unlink) let move let pinflight module.exports = moveFile function moveFile (src, dest) { // This isn't quite an fs.rename -- the assumption is that // if `dest` already exists, and we get certain errors while // trying to move it, we should just not bother. // // In the case of cache corruption, users will receive an // EINTEGRITY error elsewhere, and can remove the offending // content their own way. // // Note that, as the name suggests, this strictly only supports file moves. return BB.fromNode(cb => { fs.link(src, dest, err => { if (err) { if (err.code === 'EEXIST' || err.code === 'EBUSY') { // file already exists, so whatever } else if (err.code === 'EPERM' && process.platform === 'win32') { // file handle stayed open even past graceful-fs limits } else { return cb(err) } } return cb() }) }).then(() => { // content should never change for any reason, so make it read-only return BB.join(unlink(src), process.platform !== 'win32' && chmod(dest, '0444')) }).catch(() => { if (!pinflight) { pinflight = require('promise-inflight') } return pinflight('cacache-move-file:' + dest, () => { return BB.promisify(fs.stat)(dest).catch(err => { if (err.code !== 'ENOENT') { // Something else is wrong here. Bail bail bail throw err } // file doesn't already exist! let's try a rename -> copy fallback if (!move) { move = require('move-concurrently') } return move(src, dest, { BB, fs }) }) }) }) } PK!$5lib/util/tmp.jsnu['use strict' const crypto = require('crypto') const { withTempDir } = require('@npmcli/fs') const fs = require('fs/promises') const path = require('path') module.exports.mkdir = mktmpdir module.exports.tmpName = function tmpName (cache, tmpPrefix) { const id = crypto.randomUUID() return path.join(cache, 'tmp', tmpPrefix ? `${tmpPrefix}-${id}` : id) } async function mktmpdir (cache, opts = {}) { const { tmpPrefix } = opts const tmpDir = path.join(cache, 'tmp') await fs.mkdir(tmpDir, { recursive: true, owner: 'inherit' }) // do not use path.join(), it drops the trailing / if tmpPrefix is unset const target = `${tmpDir}${path.sep}${tmpPrefix || ''}` return fs.mkdtemp(target, { owner: 'inherit' }) } module.exports.withTmp = withTmp function withTmp (cache, opts, cb) { if (!cb) { cb = opts opts = {} } return withTempDir(path.join(cache, 'tmp'), cb, opts) } PK!4lib/util/hash-to-segments.jsnu['use strict' module.exports = hashToSegments function hashToSegments (hash) { return [hash.slice(0, 2), hash.slice(2, 4), hash.slice(4)] } PK!%ɐS lib/util/y.jsnu['use strict' const path = require('path') const y18n = require('y18n')({ directory: path.join(__dirname, '../../locales'), locale: 'en', updateFiles: process.env.CACACHE_UPDATE_LOCALE_FILES === 'true' }) module.exports = yTag function yTag (parts) { let str = '' parts.forEach((part, i) => { const arg = arguments[i + 1] str += part if (arg) { str += '%s' } }) return y18n.__.apply(null, [str].concat([].slice.call(arguments, 1))) } module.exports.setLocale = locale => { y18n.setLocale(locale) } PK! lib/util/fix-owner.jsnu['use strict' const BB = require('bluebird') const chownr = BB.promisify(require('chownr')) const mkdirp = BB.promisify(require('mkdirp')) const inflight = require('promise-inflight') const inferOwner = require('infer-owner') // Memoize getuid()/getgid() calls. // patch process.setuid/setgid to invalidate cached value on change const self = { uid: null, gid: null } const getSelf = () => { if (typeof self.uid !== 'number') { self.uid = process.getuid() const setuid = process.setuid process.setuid = (uid) => { self.uid = null process.setuid = setuid return process.setuid(uid) } } if (typeof self.gid !== 'number') { self.gid = process.getgid() const setgid = process.setgid process.setgid = (gid) => { self.gid = null process.setgid = setgid return process.setgid(gid) } } } module.exports.chownr = fixOwner function fixOwner (cache, filepath) { if (!process.getuid) { // This platform doesn't need ownership fixing return BB.resolve() } getSelf() if (self.uid !== 0) { // almost certainly can't chown anyway return BB.resolve() } return BB.resolve(inferOwner(cache)).then(owner => { const { uid, gid } = owner // No need to override if it's already what we used. if (self.uid === uid && self.gid === gid) { return } return inflight( 'fixOwner: fixing ownership on ' + filepath, () => chownr( filepath, typeof uid === 'number' ? uid : self.uid, typeof gid === 'number' ? gid : self.gid ).catch({ code: 'ENOENT' }, () => null) ) }) } module.exports.chownr.sync = fixOwnerSync function fixOwnerSync (cache, filepath) { if (!process.getuid) { // This platform doesn't need ownership fixing return } const { uid, gid } = inferOwner.sync(cache) getSelf() if (self.uid === uid && self.gid === gid) { // No need to override if it's already what we used. return } try { chownr.sync( filepath, typeof uid === 'number' ? uid : self.uid, typeof gid === 'number' ? gid : self.gid ) } catch (err) { // only catch ENOENT, any other error is a problem. if (err.code === 'ENOENT') { return null } throw err } } module.exports.mkdirfix = mkdirfix function mkdirfix (cache, p, cb) { // we have to infer the owner _before_ making the directory, even though // we aren't going to use the results, since the cache itself might not // exist yet. If we mkdirp it, then our current uid/gid will be assumed // to be correct if it creates the cache folder in the process. return BB.resolve(inferOwner(cache)).then(() => { return mkdirp(p).then(made => { if (made) { return fixOwner(cache, made).then(() => made) } }).catch({ code: 'EEXIST' }, () => { // There's a race in mkdirp! return fixOwner(cache, p).then(() => null) }) }) } module.exports.mkdirfix.sync = mkdirfixSync function mkdirfixSync (cache, p) { try { inferOwner.sync(cache) const made = mkdirp.sync(p) if (made) { fixOwnerSync(cache, made) return made } } catch (err) { if (err.code === 'EEXIST') { fixOwnerSync(cache, p) return null } else { throw err } } } PK!՘`` lib/verify.jsnu['use strict' const { mkdir, readFile, rm, stat, truncate, writeFile, } = require('fs/promises') const contentPath = require('./content/path') const fsm = require('fs-minipass') const glob = require('./util/glob.js') const index = require('./entry-index') const path = require('path') const ssri = require('ssri') const hasOwnProperty = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key) const verifyOpts = (opts) => ({ concurrency: 20, log: { silly () {} }, ...opts, }) module.exports = verify async function verify (cache, opts) { opts = verifyOpts(opts) opts.log.silly('verify', 'verifying cache at', cache) const steps = [ markStartTime, fixPerms, garbageCollect, rebuildIndex, cleanTmp, writeVerifile, markEndTime, ] const stats = {} for (const step of steps) { const label = step.name const start = new Date() const s = await step(cache, opts) if (s) { Object.keys(s).forEach((k) => { stats[k] = s[k] }) } const end = new Date() if (!stats.runTime) { stats.runTime = {} } stats.runTime[label] = end - start } stats.runTime.total = stats.endTime - stats.startTime opts.log.silly( 'verify', 'verification finished for', cache, 'in', `${stats.runTime.total}ms` ) return stats } async function markStartTime () { return { startTime: new Date() } } async function markEndTime () { return { endTime: new Date() } } async function fixPerms (cache, opts) { opts.log.silly('verify', 'fixing cache permissions') await mkdir(cache, { recursive: true }) return null } // Implements a naive mark-and-sweep tracing garbage collector. // // The algorithm is basically as follows: // 1. Read (and filter) all index entries ("pointers") // 2. Mark each integrity value as "live" // 3. Read entire filesystem tree in `content-vX/` dir // 4. If content is live, verify its checksum and delete it if it fails // 5. If content is not marked as live, rm it. // async function garbageCollect (cache, opts) { opts.log.silly('verify', 'garbage collecting content') const { default: pMap } = await import('p-map') const indexStream = index.lsStream(cache) const liveContent = new Set() indexStream.on('data', (entry) => { if (opts.filter && !opts.filter(entry)) { return } // integrity is stringified, re-parse it so we can get each hash const integrity = ssri.parse(entry.integrity) for (const algo in integrity) { liveContent.add(integrity[algo].toString()) } }) await new Promise((resolve, reject) => { indexStream.on('end', resolve).on('error', reject) }) const contentDir = contentPath.contentDir(cache) const files = await glob(path.join(contentDir, '**'), { follow: false, nodir: true, nosort: true, }) const stats = { verifiedContent: 0, reclaimedCount: 0, reclaimedSize: 0, badContentCount: 0, keptSize: 0, } await pMap( files, async (f) => { const split = f.split(/[/\\]/) const digest = split.slice(split.length - 3).join('') const algo = split[split.length - 4] const integrity = ssri.fromHex(digest, algo) if (liveContent.has(integrity.toString())) { const info = await verifyContent(f, integrity) if (!info.valid) { stats.reclaimedCount++ stats.badContentCount++ stats.reclaimedSize += info.size } else { stats.verifiedContent++ stats.keptSize += info.size } } else { // No entries refer to this content. We can delete. stats.reclaimedCount++ const s = await stat(f) await rm(f, { recursive: true, force: true }) stats.reclaimedSize += s.size } return stats }, { concurrency: opts.concurrency } ) return stats } async function verifyContent (filepath, sri) { const contentInfo = {} try { const { size } = await stat(filepath) contentInfo.size = size contentInfo.valid = true await ssri.checkStream(new fsm.ReadStream(filepath), sri) } catch (err) { if (err.code === 'ENOENT') { return { size: 0, valid: false } } if (err.code !== 'EINTEGRITY') { throw err } await rm(filepath, { recursive: true, force: true }) contentInfo.valid = false } return contentInfo } async function rebuildIndex (cache, opts) { opts.log.silly('verify', 'rebuilding index') const { default: pMap } = await import('p-map') const entries = await index.ls(cache) const stats = { missingContent: 0, rejectedEntries: 0, totalEntries: 0, } const buckets = {} for (const k in entries) { /* istanbul ignore else */ if (hasOwnProperty(entries, k)) { const hashed = index.hashKey(k) const entry = entries[k] const excluded = opts.filter && !opts.filter(entry) excluded && stats.rejectedEntries++ if (buckets[hashed] && !excluded) { buckets[hashed].push(entry) } else if (buckets[hashed] && excluded) { // skip } else if (excluded) { buckets[hashed] = [] buckets[hashed]._path = index.bucketPath(cache, k) } else { buckets[hashed] = [entry] buckets[hashed]._path = index.bucketPath(cache, k) } } } await pMap( Object.keys(buckets), (key) => { return rebuildBucket(cache, buckets[key], stats, opts) }, { concurrency: opts.concurrency } ) return stats } async function rebuildBucket (cache, bucket, stats) { await truncate(bucket._path) // This needs to be serialized because cacache explicitly // lets very racy bucket conflicts clobber each other. for (const entry of bucket) { const content = contentPath(cache, entry.integrity) try { await stat(content) await index.insert(cache, entry.key, entry.integrity, { metadata: entry.metadata, size: entry.size, time: entry.time, }) stats.totalEntries++ } catch (err) { if (err.code === 'ENOENT') { stats.rejectedEntries++ stats.missingContent++ } else { throw err } } } } function cleanTmp (cache, opts) { opts.log.silly('verify', 'cleaning tmp directory') return rm(path.join(cache, 'tmp'), { recursive: true, force: true }) } async function writeVerifile (cache, opts) { const verifile = path.join(cache, '_lastverified') opts.log.silly('verify', 'writing verifile to ' + verifile) return writeFile(verifile, `${Date.now()}`) } module.exports.lastRun = lastRun async function lastRun (cache) { const data = await readFile(path.join(cache, '_lastverified'), { encoding: 'utf8' }) return new Date(+data) } PK!tg4put.jsnu['use strict' const figgyPudding = require('figgy-pudding') const index = require('./lib/entry-index') const memo = require('./lib/memoization') const write = require('./lib/content/write') const to = require('mississippi').to const PutOpts = figgyPudding({ algorithms: { default: ['sha512'] }, integrity: {}, memoize: {}, metadata: {}, pickAlgorithm: {}, size: {}, tmpPrefix: {}, single: {}, sep: {}, error: {}, strict: {} }) module.exports = putData function putData (cache, key, data, opts) { opts = PutOpts(opts) return write(cache, data, opts).then(res => { return index.insert( cache, key, res.integrity, opts.concat({ size: res.size }) ).then(entry => { if (opts.memoize) { memo.put(cache, entry, data, opts) } return res.integrity }) }) } module.exports.stream = putStream function putStream (cache, key, opts) { opts = PutOpts(opts) let integrity let size const contentStream = write.stream( cache, opts ).on('integrity', int => { integrity = int }).on('size', s => { size = s }) let memoData let memoTotal = 0 const stream = to((chunk, enc, cb) => { contentStream.write(chunk, enc, () => { if (opts.memoize) { if (!memoData) { memoData = [] } memoData.push(chunk) memoTotal += chunk.length } cb() }) }, cb => { contentStream.end(() => { index.insert(cache, key, integrity, opts.concat({ size })).then(entry => { if (opts.memoize) { memo.put(cache, entry, Buffer.concat(memoData, memoTotal), opts) } stream.emit('integrity', integrity) cb() }) }) }) let erred = false stream.once('error', err => { if (erred) { return } erred = true contentStream.emit('error', err) }) contentStream.once('error', err => { if (erred) { return } erred = true stream.emit('error', err) }) return stream } PK!;vl package.jsonnu[{ "name": "cacache", "version": "20.0.4", "cache-version": { "content": "2", "index": "5" }, "description": "Fast, fault-tolerant, cross-platform, disk-based, data-agnostic, content-addressable cache.", "main": "lib/index.js", "files": [ "bin/", "lib/" ], "scripts": { "test": "tap", "snap": "tap", "coverage": "tap", "test-docker": "docker run -it --rm --name pacotest -v \"$PWD\":/tmp -w /tmp node:latest npm test", "lint": "npm run eslint", "npmclilint": "npmcli-lint", "lintfix": "npm run eslint -- --fix", "postsnap": "npm run lintfix --", "postlint": "template-oss-check", "posttest": "npm run lint", "template-oss-apply": "template-oss-apply --force", "eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"" }, "repository": { "type": "git", "url": "git+https://github.com/npm/cacache.git" }, "keywords": [ "cache", "caching", "content-addressable", "sri", "sri hash", "subresource integrity", "cache", "storage", "store", "file store", "filesystem", "disk cache", "disk storage" ], "license": "ISC", "dependencies": { "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", "glob": "^13.0.0", "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^13.0.0" }, "devDependencies": { "@npmcli/eslint-config": "^6.0.1", "@npmcli/template-oss": "4.29.0", "tap": "^16.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "windowsCI": false, "version": "4.29.0", "publish": "true" }, "author": "GitHub Inc.", "tap": { "nyc-arg": [ "--exclude", "tap-snapshots/**" ] } } PK!$9get.jsnu['use strict' const BB = require('bluebird') const figgyPudding = require('figgy-pudding') const fs = require('fs') const index = require('./lib/entry-index') const memo = require('./lib/memoization') const pipe = require('mississippi').pipe const pipeline = require('mississippi').pipeline const read = require('./lib/content/read') const through = require('mississippi').through const GetOpts = figgyPudding({ integrity: {}, memoize: {}, size: {} }) module.exports = function get (cache, key, opts) { return getData(false, cache, key, opts) } module.exports.byDigest = function getByDigest (cache, digest, opts) { return getData(true, cache, digest, opts) } function getData (byDigest, cache, key, opts) { opts = GetOpts(opts) const memoized = ( byDigest ? memo.get.byDigest(cache, key, opts) : memo.get(cache, key, opts) ) if (memoized && opts.memoize !== false) { return BB.resolve(byDigest ? memoized : { metadata: memoized.entry.metadata, data: memoized.data, integrity: memoized.entry.integrity, size: memoized.entry.size }) } return ( byDigest ? BB.resolve(null) : index.find(cache, key, opts) ).then(entry => { if (!entry && !byDigest) { throw new index.NotFoundError(cache, key) } return read(cache, byDigest ? key : entry.integrity, { integrity: opts.integrity, size: opts.size }).then(data => byDigest ? data : { metadata: entry.metadata, data: data, size: entry.size, integrity: entry.integrity }).then(res => { if (opts.memoize && byDigest) { memo.put.byDigest(cache, key, res, opts) } else if (opts.memoize) { memo.put(cache, entry, res.data, opts) } return res }) }) } module.exports.sync = function get (cache, key, opts) { return getDataSync(false, cache, key, opts) } module.exports.sync.byDigest = function getByDigest (cache, digest, opts) { return getDataSync(true, cache, digest, opts) } function getDataSync (byDigest, cache, key, opts) { opts = GetOpts(opts) const memoized = ( byDigest ? memo.get.byDigest(cache, key, opts) : memo.get(cache, key, opts) ) if (memoized && opts.memoize !== false) { return byDigest ? memoized : { metadata: memoized.entry.metadata, data: memoized.data, integrity: memoized.entry.integrity, size: memoized.entry.size } } const entry = !byDigest && index.find.sync(cache, key, opts) if (!entry && !byDigest) { throw new index.NotFoundError(cache, key) } const data = read.sync( cache, byDigest ? key : entry.integrity, { integrity: opts.integrity, size: opts.size } ) const res = byDigest ? data : { metadata: entry.metadata, data: data, size: entry.size, integrity: entry.integrity } if (opts.memoize && byDigest) { memo.put.byDigest(cache, key, res, opts) } else if (opts.memoize) { memo.put(cache, entry, res.data, opts) } return res } module.exports.stream = getStream function getStream (cache, key, opts) { opts = GetOpts(opts) let stream = through() const memoized = memo.get(cache, key, opts) if (memoized && opts.memoize !== false) { stream.on('newListener', function (ev, cb) { ev === 'metadata' && cb(memoized.entry.metadata) ev === 'integrity' && cb(memoized.entry.integrity) ev === 'size' && cb(memoized.entry.size) }) stream.write(memoized.data, () => stream.end()) return stream } index.find(cache, key).then(entry => { if (!entry) { return stream.emit( 'error', new index.NotFoundError(cache, key) ) } let memoStream if (opts.memoize) { let memoData = [] let memoLength = 0 memoStream = through((c, en, cb) => { memoData && memoData.push(c) memoLength += c.length cb(null, c, en) }, cb => { memoData && memo.put(cache, entry, Buffer.concat(memoData, memoLength), opts) cb() }) } else { memoStream = through() } stream.emit('metadata', entry.metadata) stream.emit('integrity', entry.integrity) stream.emit('size', entry.size) stream.on('newListener', function (ev, cb) { ev === 'metadata' && cb(entry.metadata) ev === 'integrity' && cb(entry.integrity) ev === 'size' && cb(entry.size) }) pipe( read.readStream(cache, entry.integrity, opts.concat({ size: opts.size == null ? entry.size : opts.size })), memoStream, stream ) }).catch(err => stream.emit('error', err)) return stream } module.exports.stream.byDigest = getStreamDigest function getStreamDigest (cache, integrity, opts) { opts = GetOpts(opts) const memoized = memo.get.byDigest(cache, integrity, opts) if (memoized && opts.memoize !== false) { const stream = through() stream.write(memoized, () => stream.end()) return stream } else { let stream = read.readStream(cache, integrity, opts) if (opts.memoize) { let memoData = [] let memoLength = 0 const memoStream = through((c, en, cb) => { memoData && memoData.push(c) memoLength += c.length cb(null, c, en) }, cb => { memoData && memo.put.byDigest( cache, integrity, Buffer.concat(memoData, memoLength), opts ) cb() }) stream = pipeline(stream, memoStream) } return stream } } module.exports.info = info function info (cache, key, opts) { opts = GetOpts(opts) const memoized = memo.get(cache, key, opts) if (memoized && opts.memoize !== false) { return BB.resolve(memoized.entry) } else { return index.find(cache, key) } } module.exports.hasContent = read.hasContent module.exports.copy = function cp (cache, key, dest, opts) { return copy(false, cache, key, dest, opts) } module.exports.copy.byDigest = function cpDigest (cache, digest, dest, opts) { return copy(true, cache, digest, dest, opts) } function copy (byDigest, cache, key, dest, opts) { opts = GetOpts(opts) if (read.copy) { return ( byDigest ? BB.resolve(null) : index.find(cache, key, opts) ).then(entry => { if (!entry && !byDigest) { throw new index.NotFoundError(cache, key) } return read.copy( cache, byDigest ? key : entry.integrity, dest, opts ).then(() => byDigest ? key : { metadata: entry.metadata, size: entry.size, integrity: entry.integrity }) }) } else { return getData(byDigest, cache, key, opts).then(res => { return fs.writeFileAsync(dest, byDigest ? res : res.data) .then(() => byDigest ? key : { metadata: res.metadata, size: res.size, integrity: res.integrity }) }) } } PK!Jnn lib/get.jsnu['use strict' const Collect = require('minipass-collect') const { Minipass } = require('minipass') const Pipeline = require('minipass-pipeline') const index = require('./entry-index') const memo = require('./memoization') const read = require('./content/read') async function getData (cache, key, opts = {}) { const { integrity, memoize, size } = opts const memoized = memo.get(cache, key, opts) if (memoized && memoize !== false) { return { metadata: memoized.entry.metadata, data: memoized.data, integrity: memoized.entry.integrity, size: memoized.entry.size, } } const entry = await index.find(cache, key, opts) if (!entry) { throw new index.NotFoundError(cache, key) } const data = await read(cache, entry.integrity, { integrity, size }) if (memoize) { memo.put(cache, entry, data, opts) } return { data, metadata: entry.metadata, size: entry.size, integrity: entry.integrity, } } module.exports = getData async function getDataByDigest (cache, key, opts = {}) { const { integrity, memoize, size } = opts const memoized = memo.get.byDigest(cache, key, opts) if (memoized && memoize !== false) { return memoized } const res = await read(cache, key, { integrity, size }) if (memoize) { memo.put.byDigest(cache, key, res, opts) } return res } module.exports.byDigest = getDataByDigest const getMemoizedStream = (memoized) => { const stream = new Minipass() stream.on('newListener', function (ev, cb) { ev === 'metadata' && cb(memoized.entry.metadata) ev === 'integrity' && cb(memoized.entry.integrity) ev === 'size' && cb(memoized.entry.size) }) stream.end(memoized.data) return stream } function getStream (cache, key, opts = {}) { const { memoize, size } = opts const memoized = memo.get(cache, key, opts) if (memoized && memoize !== false) { return getMemoizedStream(memoized) } const stream = new Pipeline() // Set all this up to run on the stream and then just return the stream Promise.resolve().then(async () => { const entry = await index.find(cache, key) if (!entry) { throw new index.NotFoundError(cache, key) } stream.emit('metadata', entry.metadata) stream.emit('integrity', entry.integrity) stream.emit('size', entry.size) stream.on('newListener', function (ev, cb) { ev === 'metadata' && cb(entry.metadata) ev === 'integrity' && cb(entry.integrity) ev === 'size' && cb(entry.size) }) const src = read.readStream( cache, entry.integrity, { ...opts, size: typeof size !== 'number' ? entry.size : size } ) if (memoize) { const memoStream = new Collect.PassThrough() memoStream.on('collect', data => memo.put(cache, entry, data, opts)) stream.unshift(memoStream) } stream.unshift(src) return stream }).catch((err) => stream.emit('error', err)) return stream } module.exports.stream = getStream function getStreamDigest (cache, integrity, opts = {}) { const { memoize } = opts const memoized = memo.get.byDigest(cache, integrity, opts) if (memoized && memoize !== false) { const stream = new Minipass() stream.end(memoized) return stream } else { const stream = read.readStream(cache, integrity, opts) if (!memoize) { return stream } const memoStream = new Collect.PassThrough() memoStream.on('collect', data => memo.put.byDigest( cache, integrity, data, opts )) return new Pipeline(stream, memoStream) } } module.exports.stream.byDigest = getStreamDigest function info (cache, key, opts = {}) { const { memoize } = opts const memoized = memo.get(cache, key, opts) if (memoized && memoize !== false) { return Promise.resolve(memoized.entry) } else { return index.find(cache, key) } } module.exports.info = info async function copy (cache, key, dest, opts = {}) { const entry = await index.find(cache, key, opts) if (!entry) { throw new index.NotFoundError(cache, key) } await read.copy(cache, entry.integrity, dest, opts) return { metadata: entry.metadata, size: entry.size, integrity: entry.integrity, } } module.exports.copy = copy async function copyByDigest (cache, key, dest, opts = {}) { await read.copy(cache, key, dest, opts) return key } module.exports.copy.byDigest = copyByDigest module.exports.hasContent = read.hasContent PK!_Qlib/util/glob.jsnu['use strict' const { glob } = require('glob') const path = require('path') const globify = (pattern) => pattern.split(path.win32.sep).join(path.posix.sep) module.exports = (path, options) => glob(globify(path), options) PK!zf lib/rm.jsnu['use strict' const { rm } = require('fs/promises') const glob = require('./util/glob.js') const index = require('./entry-index') const memo = require('./memoization') const path = require('path') const rmContent = require('./content/rm') module.exports = entry module.exports.entry = entry function entry (cache, key, opts) { memo.clearMemoized() return index.delete(cache, key, opts) } module.exports.content = content function content (cache, integrity) { memo.clearMemoized() return rmContent(cache, integrity) } module.exports.all = all async function all (cache) { memo.clearMemoized() const paths = await glob(path.join(cache, '*(content-*|index-*)'), { silent: true, nosort: true }) return Promise.all(paths.map((p) => rm(p, { recursive: true, force: true }))) } PK! lib/index.jsnu['use strict' const get = require('./get.js') const put = require('./put.js') const rm = require('./rm.js') const verify = require('./verify.js') const { clearMemoized } = require('./memoization.js') const tmp = require('./util/tmp.js') const index = require('./entry-index.js') module.exports.index = {} module.exports.index.compact = index.compact module.exports.index.insert = index.insert module.exports.ls = index.ls module.exports.ls.stream = index.lsStream module.exports.get = get module.exports.get.byDigest = get.byDigest module.exports.get.stream = get.stream module.exports.get.stream.byDigest = get.stream.byDigest module.exports.get.copy = get.copy module.exports.get.copy.byDigest = get.copy.byDigest module.exports.get.info = get.info module.exports.get.hasContent = get.hasContent module.exports.put = put module.exports.put.stream = put.stream module.exports.rm = rm.entry module.exports.rm.all = rm.all module.exports.rm.entry = module.exports.rm module.exports.rm.content = rm.content module.exports.clearMemoized = clearMemoized module.exports.tmp = {} module.exports.tmp.mkdir = tmp.mkdir module.exports.tmp.withTmp = tmp.withTmp module.exports.verify = verify module.exports.verify.lastRun = verify.lastRun PK!ٷ lib/put.jsnu['use strict' const index = require('./entry-index') const memo = require('./memoization') const write = require('./content/write') const Flush = require('minipass-flush') const { PassThrough } = require('minipass-collect') const Pipeline = require('minipass-pipeline') const putOpts = (opts) => ({ algorithms: ['sha512'], ...opts, }) module.exports = putData async function putData (cache, key, data, opts = {}) { const { memoize } = opts opts = putOpts(opts) const res = await write(cache, data, opts) const entry = await index.insert(cache, key, res.integrity, { ...opts, size: res.size }) if (memoize) { memo.put(cache, entry, data, opts) } return res.integrity } module.exports.stream = putStream function putStream (cache, key, opts = {}) { const { memoize } = opts opts = putOpts(opts) let integrity let size let error let memoData const pipeline = new Pipeline() // first item in the pipeline is the memoizer, because we need // that to end first and get the collected data. if (memoize) { const memoizer = new PassThrough().on('collect', data => { memoData = data }) pipeline.push(memoizer) } // contentStream is a write-only, not a passthrough // no data comes out of it. const contentStream = write.stream(cache, opts) .on('integrity', (int) => { integrity = int }) .on('size', (s) => { size = s }) .on('error', (err) => { error = err }) pipeline.push(contentStream) // last but not least, we write the index and emit hash and size, // and memoize if we're doing that pipeline.push(new Flush({ async flush () { if (!error) { const entry = await index.insert(cache, key, integrity, { ...opts, size }) if (memoize && memoData) { memo.put(cache, entry, memoData, opts) } pipeline.emit('integrity', integrity) pipeline.emit('size', size) } }, })) return pipeline } PK!6::en.jsnu[PK!>HXQQ oREADME.es.mdnu[PK!LyyRls.jsnu[PK!6::BSindex.jsnu[PK!gM|PP SREADME.mdnu[PK!:Eƕrm.jsnu[PK!d?::es.jsnu[PK!n## (locales/en.jsnu[PK!$v.locales/en.jsonnu[PK!w  locales/es.jsnu[PK!f9.locales/es.jsonnu[PK!(}