{"id":17398491,"url":"https://github.com/vweevers/level-vinyl","last_synced_at":"2025-08-10T08:05:34.082Z","repository":{"id":25317979,"uuid":"28744791","full_name":"vweevers/level-vinyl","owner":"vweevers","description":"leveldb vinyl adapter and blob store","archived":false,"fork":false,"pushed_at":"2015-02-22T23:11:04.000Z","size":360,"stargazers_count":10,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-09T09:51:26.979Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"whosonfirst-data/whosonfirst-data-venue-nz","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vweevers.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-01-03T13:44:49.000Z","updated_at":"2022-12-24T13:14:11.000Z","dependencies_parsed_at":"2022-08-24T04:40:15.005Z","dependency_job_id":null,"html_url":"https://github.com/vweevers/level-vinyl","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/vweevers/level-vinyl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vweevers%2Flevel-vinyl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vweevers%2Flevel-vinyl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vweevers%2Flevel-vinyl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vweevers%2Flevel-vinyl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vweevers","download_url":"https://codeload.github.com/vweevers/level-vinyl/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vweevers%2Flevel-vinyl/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269693593,"owners_count":24460248,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-16T14:57:29.352Z","updated_at":"2025-08-10T08:05:34.056Z","avatar_url":"https://github.com/vweevers.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# level-vinyl\r\n\r\n\u003e leveldb vinyl adapter and blob store. Saves file contents in a content\r\naddressable blob store, file metadata in leveldb. Supports globbing, most of the gulp 4.0 options and emits streaming [vinyl](https://www.npmjs.com/package/vinyl) files.\r\n\r\n[![npm status](http://img.shields.io/npm/v/level-vinyl.svg?style=flat-square)](https://www.npmjs.org/package/level-vinyl) [![Travis build status](https://img.shields.io/travis/vweevers/level-vinyl.svg?style=flat-square\u0026label=travis)](http://travis-ci.org/vweevers/level-vinyl) [![AppVeyor build status](https://img.shields.io/appveyor/ci/vweevers/level-vinyl.svg?style=flat-square\u0026label=appveyor)](https://ci.appveyor.com/project/vweevers/level-vinyl) [![Dependency status](https://img.shields.io/david/vweevers/level-vinyl.svg?style=flat-square)](https://david-dm.org/vweevers/level-vinyl)\r\n\r\nJump to: [example](#example) / [usage](#usage) / [vinyl adapter api](#vinyl-adapter-api) / [levelup api](#levelup-api) / [install](#install) / [license](#license)\r\n\r\n## why?\r\n\r\nBecause level-vinyl is a vinyl adapter, you can:\r\n\r\n- use [1000+ gulp plugins](http://gulpjs.com/plugins) to transform files\r\n- aggregate files using multiple globs and negation\r\n- do `gulp.src('src/*.png').pipe(db.dest('/assets'))` like a pro: `src/1.png` ends up\r\n  in your database at `/assets/1.png`\r\n- stream only modified files with `options.since`\r\n\r\nBecause level-vinyl saves metadata (stat, digest and custom properties) to leveldb by a virtual path, you can:\r\n\r\n- pipe to and from other databases, local and elsewhere (theoretically).\r\n- index metadata, do map-reduces\r\n- use sublevels to for example, save multiple versions of the same files\r\n- use triggers and hooks to process new files\r\n- or do [something else](https://github.com/rvagg/node-levelup/wiki/Modules) entirely\r\n\r\n## example\r\n\r\n```js\r\nvar levelvinyl = require('level-vinyl')\r\n  , level      = require('level-test')()\r\n  , sublevel   = require('level-sublevel')\r\n  , buffer     = require('vinyl-buffer')\r\n  , imagemin   = require('imagemin-jpegtran')()\r\n  , vfs        = require('vinyl-fs')\r\n\r\n// Create database\r\nvar db = level();\r\nvar sdb = sublevel(db, { valueEncoding: 'json'});\r\nvar vinylDb = levelvinyl(sdb, './example/blobs')\r\n\r\n// Create a sublevel for minified images\r\nvar min  = vinylDb.subvinyl('minified').dest('/')\r\nvar main = vinylDb.dest('/')\r\n\r\nvfs.src('*.jpg')     // file.contents is a buffer\r\n  .pipe(main)        // save to db\r\n  .pipe(imagemin())  // minify\r\n  .pipe(min)         // save minified to db\r\n```\r\n\r\nSame thing, other way around:\r\n\r\n```js\r\nvar min  = vfs.dest('./example/minified')\r\nvar main = vfs.dest('./example')\r\n\r\nvinylDb.src('*.jpg') // file.contents is a stream\r\n  .pipe(main)        // copy to fs\r\n  .pipe(buffer())    // imagemin wants buffers\r\n  .pipe(imagemin())  // minify\r\n  .pipe(min)         // copy minified to fs\r\n```\r\n\r\nNote though, it's a levelup database:\r\n\r\n```js\r\nvinylDb.get('/example.jpg', function(err, file){\r\n  file.contents.pipe(somewhere)\r\n})\r\n\r\nvinylDb.get('/example.jpg', { read: false }, function(err, file){\r\n  console.log( file.isNull() === true )\r\n})\r\n```\r\n\r\n## usage\r\n\r\n`levelVinyl(db, options || path)`\r\n\r\n- `db` a levelup database that has [sublevel](https://www.npmjs.com/package/level-sublevel) 6.x.x installed\r\n- `options.path || path` where to store the blobs (required)\r\n\r\nIf you provide an options object, it will be passed to [content-addressable-blob-store](https://www.npmjs.com/package/content-addressable-blob-store).\r\n\r\n## vinyl adapter api\r\n\r\nThe items below are described in terms of compatibility with [vinyl-fs](https://www.npmjs.com/package/vinyl-fs).\r\n\r\n### `db.src(pattern(s)[, options])`\r\n\r\n**Differences**\r\n\r\n- `file.contents` is a stream; `options.buffer` is not supported. You can use [vinyl-buffer](https://www.npmjs.com/package/vinyl-buffer) to convert streams to buffers (as the above example does).\r\n- Only streams regular files (`file.stat.isFile()` is always true).\r\n\r\n**Features**\r\n\r\n- aggregate multiple glob patterns, negation\r\n- results are ordered\r\n- `file.base` is set to the \"glob base\" or `options.base`\r\n- `file.isNull()` when `options.read == false`\r\n- should glob a directory\r\n- return dead stream if globs is empty array\r\n- throw on invalid glob (not a string or array)\r\n- support `options.since`\r\n\r\n**Missing  / partial support**\r\n\r\n- should pass through writes (*needs test*)\r\n- set glob options (`nobrace` etc).\r\n\r\n### `db.dest([path][, options])`\r\n\r\n**Differences**\r\n\r\n- The `path` argument is optional and defaults to `/`. Files are identified in leveldb by an absolute\r\nvirtual path, which is constructed from a file's `relative` property, optionally prefixed\r\nwith `path`. Note that `dest('/docs')` currently does the same as `dest('docs')`,\r\nbecause there is no concept of a \"current working directory\" within the tree.\r\n- Saves a small subset of `file.stat`: mtime, ctime, mode and size. Mode is 777\r\n  or `options.mode`; only permission flags are saved.\r\n\r\n**Features**\r\n\r\n- updates files after write (`cwd`, `base`, `path` and `stat`)\r\n- custom mode with `options.mode` (this is just metadata and has no effect on the blob store)\r\n- writes buffers and streams, skips null files\r\n- supports `path` as function (gets a vinyl file, should return a path).\r\n\r\n**Missing  / partial support**\r\n\r\n- should allow piping multiple dests and should reset streams (*needs specific test*)\r\n- support `options.cwd` (*irrelevant for save, but does set `file.cwd`*)\r\n\r\n### `db.watch([pattern(s)][, options][, cb])`\r\n\r\n**Differences**\r\n\r\n- does not emit a `ready` event and `add()` has no callback argument\r\n- the change types `renamed` and `added` are not supported\r\n- does not keep the process alive\r\n- both `add()` and `remove()` accept glob patterns.\r\n\r\n**Features**\r\n\r\n- adds `cb` as change listener\r\n- `options.debounceDelay`: debounce events for the same file, default delay is 500\r\n- `options.maxListeners` is passed to [emitter.setMaxListeners](http://nodejs.org/api/events.html#events_emitter_setmaxlisteners_n)\r\n- does nothing if `patterns` is empty.\r\n\r\nReturns an emitter with these events:\r\n\r\n- `change` with `{type, path}` data where type is `changed` or `deleted`.\r\n- `nomatch`, when a changed or deleted file doesn't match the patterns\r\n- `end` when stopped.\r\n\r\nAnd these methods:\r\n\r\n- `add(pattern(s))`: add patterns to be watched\r\n- `remove(pattern(s))`: exclude files from being matched\r\n- `end()`: stop watching.\r\n\r\n## levelup api\r\n\r\n### `db.get(path[, options], cb)`\r\n\r\nGet a single file. `path` will be made absolute and unixified.\r\n\r\n```js\r\ndb.get('/images/moon.png', cb) // i like this\r\ndb.get('images\\\\moon.png', cb) // but this is fine too\r\n```\r\n\r\n- `options.read` if false, the blob store isn't accessed and `file.contents` will be `null`\r\n- `options.valueEncoding || options.encoding` if set, the retrieved value won't be coerced to Vinyl\r\n\r\n### `db.subvinyl(prefix[, options])`\r\n\r\nCreate a sublevel and install level-vinyl - the sublevel will have its own blob store. Prefix and options are passed to sublevel.\r\n\r\n### `db.put(key, vinyl[, options][, cb])`\r\n### `db.put(vinyl[, options][, cb])`\r\n\r\nSave a single file.\r\n\r\n- `options.mode` set `file.stat.mode` before saving, default is 777\r\n- `options.base` set the file's `base`\r\n- `options.valueEncoding || options.encoding` if set, the regular `put()` is called.\r\n\r\n### `db.del(key[, cb])`\r\n\r\nNothing special, except this again:\r\n\r\n```js\r\ndb.del('/document') == db.del('\\\\document')\r\n```\r\n\r\n### `db.batch(ops[, options], cb)`\r\n\r\nFor `put` operations:\r\n\r\n- `options.mode` set `file.stat.mode` on all files\r\n- `op.mode` same, but per operation (overrides `options.mode`):\r\n\r\n```js\r\ndb.batch([{\r\n  type: 'put',\r\n  key: '/project/readme',\r\n  value: readmeFile,\r\n  mode: 0755\r\n}])\r\n```\r\n\r\n### `db.createReadStream([options])`\r\n\r\nThis is a plain stream, i.e. it does not decode values to Vinyl files.\r\n\r\n- `options.since` stream only files modified since a date or timestamp. Ignored for key streams (when `options.values === false`).\r\n\r\n### `db.getBlobStore()`\r\n\r\nGet the blob store instance.\r\n\r\n## install\r\n\r\nWith [npm](https://npmjs.org) do:\r\n\r\n```\r\nnpm install level-vinyl\r\n```\r\n\r\n## license\r\n\r\n[MIT](http://opensource.org/licenses/MIT) © [Vincent Weevers](http://vincentweevers.nl)\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvweevers%2Flevel-vinyl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvweevers%2Flevel-vinyl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvweevers%2Flevel-vinyl/lists"}