├── .gitignore ├── .travis.yml ├── README.md ├── index.js ├── lib ├── next-tick.js └── stats.js ├── package.json └── test ├── chmod.js ├── chown.js ├── exists.js ├── get-level.js ├── mkir.js ├── read-file.js ├── read-stream.js ├── readdir.js ├── stat.js ├── unlink.js ├── write-file.js └── write-stream.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.sw* 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # level-fs 3 | 4 | node's [fs module](http://nodejs.org/api/fs.html) with 5 | [leveldb](https://github.com/rvagg/node-levelup) as backend. 6 | 7 | [![build status](https://secure.travis-ci.org/juliangruber/level-fs.svg)](http://travis-ci.org/juliangruber/level-fs) 8 | [![downloads](https://img.shields.io/npm/dm/level-fs.svg)](https://www.npmjs.org/package/level-fs) 9 | 10 | [![testling badge](https://ci.testling.com/juliangruber/level-fs.png)](https://ci.testling.com/juliangruber/level-fs) 11 | 12 | ## Usage 13 | 14 | ```js 15 | var level = require('level'); 16 | var db = level(__dirname + '/db'); 17 | var fs = require('level-fs')(db); 18 | 19 | fs.readFile('/etc/passwd', function (err, data) { 20 | if (err) throw err; 21 | console.log(data); 22 | }); 23 | ``` 24 | 25 | ## How it works 26 | 27 | Paths are sublevels, so `/a/b/c` is basically `db.sublevel('a').sublevel('b').get('c')`. 28 | Streaming access to stored data is provided through 29 | [level-store](https://github.com/juliangruber/level-store). 30 | 31 | ## Implemented 32 | 33 | * `readFile(filename, [options], callback)` 34 | * `writeFile(filename, data, [options], callback)` 35 | * `unlink(path, callback)` 36 | * `chown(path, uid, gid, callback)` 37 | * `chmod(path, mode, callback)` 38 | * `mkdir(path, [mode], callback)` 39 | * `readdir(path, callback)` 40 | * `exists(path, callback)` 41 | * `createReadStream(path, [options])` 42 | * `createWriteStream(path, [options])` 43 | * `Stats#isBlockDevice()` 44 | * `Stats#isCharacterDevice()` 45 | * `Stats#isSymbolicLink()` 46 | * `Stats#isFIFO()` 47 | * `Stats#isSocket()` 48 | * `Stats#isFile()` 49 | * `Stats#isDirectory()` 50 | * error codes: `ENOENT` 51 | 52 | ## ToDo 53 | 54 | * error codes 55 | * empty but existing files 56 | * modes 57 | * users and groups 58 | * byte indexes for offsets 59 | * file descriptors 60 | * directory support 61 | * `rename(oldPath, newPath, callback)` 62 | * `renameSync(oldPath, newPath)` 63 | * `ftruncate(fd, len, callback)` 64 | * `ftruncateSync(fd, len)` 65 | * `truncate(path, len, callback)` 66 | * `truncateSync(path, len)` 67 | * `chownSync(path, uid, gid)` 68 | * `fchown(fd, uid, gid, callback)` 69 | * `fchownSync(fd, uid, gid)` 70 | * `lchown(path, uid, gid, callback)` 71 | * `lchownSync(path, uid, gid)` 72 | * `chmodSync(path, mode)` 73 | * `fchmod(fd, mode, callback)` 74 | * `fchmodSync(fd, mode)` 75 | * `lchmod(path, mode, callback)` 76 | * `lchmodSync(path, mode)` 77 | * `stat(path, callback)` 78 | * `lstat(path, callback)` 79 | * `fstat(fd, callback)` 80 | * `statSync(path)` 81 | * `lstatSync(path)` 82 | * `fstatSync(fd)` 83 | * `Stats.{dev,ino,mode,nlink,uid,gid,rdev,size,blksize,blocks,atime,mtime,ctime}` 84 | * `link(srcpath, dstpath, callback)` 85 | * `linkSync(srcpath, dstpath)` 86 | * `symlink(srcpath, dstpath, [type], callback)` 87 | * `symlinkSync(srcpath, dstpath, [type])` 88 | * `readlink(path, callback)` 89 | * `readlinkSync(path)` 90 | * `realpath(path, [cache], callback)` 91 | * `realpathSync(path, [cache])` 92 | * `unlinkSync(path)` 93 | * `rmdir(path, callback)` 94 | * `rmdirSync(path)` 95 | * `mkdirSync(path, [mode])` 96 | * `readdirSync(path)` 97 | * `close(fd, callback)` 98 | * `closeSync(fd)` 99 | * `open(path, flags, [mode], callback)` 100 | * `openSync(path, flags, [mode])` 101 | * `utimes(path, atime, mtime, callback)` 102 | * `utimesSync(path, atime, mtime)` 103 | * `futimes(fd, atime, mtime, callback)` 104 | * `futimesSync(fd, atime, mtime)` 105 | * `fsync(fd, callback)` 106 | * `fsyncSync(fd)` 107 | * `write(fd, buffer, offset, length, position, callback)` 108 | * `writeSync(fd, buffer, offset, length, position)` 109 | * `read(fd, buffer, offset, length, position, callback)` 110 | * `readSync(fd, buffer, offset, length, position)` 111 | * `readFileSync(filename, [options])` 112 | * `writeFileSync(filename, data, [options])` 113 | * `appendFile(filename, data, [options], callback)` 114 | * `appendFileSync(filename, data, [options])` 115 | * `watchFile(filename, [options], listener)` 116 | * `unwatchFile(filename, [listener])` 117 | * `watch(filename, [options], [listener])` 118 | * `existsSync(path)` 119 | 120 | ## Installation 121 | 122 | With [npm](http://npmjs.org) do 123 | 124 | ```bash 125 | $ npm install level-fs 126 | ``` 127 | 128 | ## Tests 129 | 130 | Run the tests in node: 131 | 132 | ```bash 133 | $ npm test 134 | ``` 135 | 136 | Run the tests headlessly in [phantomjs](http://phantomjs.org): 137 | 138 | ```bash 139 | $ npm run test-phantom 140 | ``` 141 | 142 | Run the tests in any browser, open `http://localhost:3001` to start: 143 | 144 | ```bash 145 | $ npm run test-browser 146 | ``` 147 | 148 | ## License 149 | 150 | Copyright (c) 2013 Julian Gruber <julian@juliangruber.com> 151 | 152 | Permission is hereby granted, free of charge, to any person obtaining a copy 153 | of this software and associated documentation files (the "Software"), to deal 154 | in the Software without restriction, including without limitation the rights 155 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 156 | copies of the Software, and to permit persons to whom the Software is 157 | furnished to do so, subject to the following conditions: 158 | 159 | The above copyright notice and this permission notice shall be included in 160 | all copies or substantial portions of the Software. 161 | 162 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 163 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 164 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 165 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 166 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 167 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 168 | THE SOFTWARE. 169 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var SubLevel = require('level-sublevel'); 2 | var Store = require('level-store'); 3 | var Stats = require('./lib/stats'); 4 | var nextTick = require('./lib/next-tick'); 5 | 6 | module.exports = fs; 7 | 8 | function fs (db) { 9 | if (!(this instanceof fs)) return new fs(db); 10 | this.db = SubLevel(db); 11 | } 12 | 13 | fs.prototype.readFile = function (filename, opts, cb) { 14 | if (typeof opts == 'function') { 15 | cb = opts; 16 | opts = {}; 17 | } else if (typeof opts == 'string') { 18 | opts = { encoding: opts }; 19 | } 20 | 21 | var encoding = opts.encoding || 'binary'; 22 | var flag = opts.flag || 'r'; 23 | var m = this._getLevel(filename); 24 | var data = []; 25 | 26 | Store(m.level).get(m.file, { 27 | valueEncoding: encoding 28 | }, function (err, data) { 29 | if (err) { 30 | if (err.message == 'Stream not found.') { 31 | if (flag[0] != 'r') { 32 | err = null; 33 | } else { 34 | return cb(enoent(err)); 35 | } 36 | } else { 37 | return cb(err); 38 | } 39 | } 40 | if (!err && !data) { 41 | data = encoding == 'binary' 42 | ? new Buffer('') 43 | : ''; 44 | } 45 | return cb(err, data); 46 | }); 47 | }; 48 | 49 | fs.prototype.writeFile = function (filename, data, opts, cb) { 50 | if (typeof opts == 'function') { 51 | cb = opts; 52 | opts = {}; 53 | } 54 | if (typeof opts == 'string') opts = { encoding: opts }; 55 | if (typeof opts == 'undefined') opts = {}; 56 | 57 | var encoding = opts.encoding || 'utf8'; 58 | var flag = opts.flag || 'w'; 59 | 60 | var m = this._getLevel(filename); 61 | var method = flag[0] == 'w' 62 | ? 'set' 63 | : 'append'; 64 | Store(m.level)[method](m.file, data, { encoding: encoding }, cb); 65 | }; 66 | 67 | fs.prototype.stat = function (path, cb) { 68 | var stat = new Stats(); 69 | var m = this._getLevel(path); 70 | if (m.level.sublevels[m.file] || path === '/') { 71 | stat._isFile = false; 72 | return cb(null, stat); 73 | } 74 | Store(m.level).head(m.file, { index: true }, function (err) { 75 | if (err) { 76 | if (err.message == 'range not found') enoent(err); 77 | return cb(err); 78 | } 79 | cb(null, stat); 80 | }); 81 | }; 82 | 83 | fs.prototype.exists = function (path, cb) { 84 | this.stat(path, function (err, stat) { 85 | cb(!!stat); 86 | }); 87 | }; 88 | 89 | fs.prototype.unlink = function (path, cb) { 90 | var m = this._getLevel(path); 91 | Store(m.level).delete(m.file, function (err) { 92 | if (err && err.message == 'Stream not found.') enoent(err); 93 | cb(err); 94 | }); 95 | }; 96 | 97 | fs.prototype.chown = function (path, uid, gid, cb) { 98 | this.stat(path, cb); 99 | }; 100 | 101 | fs.prototype.chmod = function (path, mode, cb) { 102 | this.stat(path, cb); 103 | }; 104 | 105 | fs.prototype.mkdir = function (path, mode, cb) { 106 | if (typeof mode == 'function') { 107 | cb = mode; 108 | mode = 0777; 109 | } 110 | var m = this._getLevel(path); 111 | m.level.sublevel(m.file); 112 | if (cb) nextTick(cb); 113 | }; 114 | 115 | fs.prototype.readdir = function (path, cb) { 116 | var self = this; 117 | this.stat(path, function (err) { 118 | if (err) return cb(err); 119 | var m = self._getLevel(path.replace(/\/+$/, '') + '/xxx'); 120 | var files = {}; 121 | Object.keys(m.level.sublevels).forEach(function (dir) { 122 | files[dir] = true; 123 | }); 124 | 125 | var ks = Store(m.level).createKeyStream(); 126 | ks.on('data', function (file) { 127 | files[file] = true; 128 | }); 129 | ks.on('end', function () { 130 | if (cb) cb(null, Object.keys(files)); 131 | }); 132 | }); 133 | }; 134 | 135 | fs.prototype.createReadStream = function (path, opts) { 136 | if (!opts) opts = {}; 137 | 138 | var encoding = opts.encoding || 'binary'; 139 | var flags = opts.flags || 'r'; 140 | 141 | var m = this._getLevel(path); 142 | var rs = Store(m.level).createReadStream(m.file, { encoding: 'encoding' }); 143 | 144 | var read = false; 145 | rs.once('data', function () { 146 | read = true; 147 | }); 148 | rs.on('end', function () { 149 | if (!read && flags[0] == 'r') { 150 | var err = new Error('File not found'); 151 | rs.emit('error', enoent(err)); 152 | } 153 | }); 154 | 155 | return rs; 156 | }; 157 | 158 | fs.prototype.createWriteStream = function (path, opts) { 159 | if (!opts) opts = {}; 160 | var flags = opts.flags || 'w'; 161 | var encoding = opts.encoding || 'binary'; 162 | var mode = opts.mode || 0666; 163 | var m = this._getLevel(path); 164 | return Store(m.level).createWriteStream(m.file, { 165 | append: flags[0] == 'a', 166 | valueEncoding: encoding 167 | }); 168 | }; 169 | 170 | fs.prototype._getLevel = function (path) { 171 | var segs = path.split('/').filter(Boolean); 172 | var file = segs.pop() || ''; 173 | var level = segs.reduce(function (level, sub) { 174 | return level.sublevel(sub); 175 | }, this.db); 176 | return { 177 | level: level, 178 | file: file 179 | }; 180 | }; 181 | 182 | function enoent (err) { 183 | err.code = 'ENOENT'; 184 | return err; 185 | }; 186 | -------------------------------------------------------------------------------- /lib/next-tick.js: -------------------------------------------------------------------------------- 1 | module.exports = typeof setImmediate === 'function' 2 | ? setImmediate 3 | : process.nextTick; 4 | -------------------------------------------------------------------------------- /lib/stats.js: -------------------------------------------------------------------------------- 1 | module.exports = Stats; 2 | 3 | function Stats () { 4 | this._isFile = true; 5 | }; 6 | 7 | Stats.prototype.isFile = function () { 8 | return this._isFile; 9 | }; 10 | 11 | Stats.prototype.isDirectory = function () { 12 | return !this._isFile; 13 | }; 14 | 15 | // not supported 16 | Stats.prototype.isBlockDevice = 17 | Stats.prototype.isCharacterDevice = 18 | Stats.prototype.isSymbolicLink = 19 | Stats.prototype.isFIFO = 20 | Stats.prototype.isSocket = function () { 21 | return false; 22 | }; 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "level-fs", 3 | "description": "node's fs module with leveldb as backend", 4 | "version": "0.11.2", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/juliangruber/level-fs.git" 8 | }, 9 | "homepage": "https://github.com/juliangruber/level-fs", 10 | "main": "index.js", 11 | "scripts": { 12 | "test": "tape test/*.js", 13 | "test-phantom": "browserify test/*.js | tape-run", 14 | "test-browser": "browserify test/*.js | tape-run 3001" 15 | }, 16 | "dependencies": { 17 | "level-sublevel": "~4.7.1", 18 | "level-store": "~3.10.0" 19 | }, 20 | "devDependencies": { 21 | "tape": "~1.0.4", 22 | "browserify": "~2.23.1", 23 | "tape-run": "~0.1.0", 24 | "memdb": "0.0.1" 25 | }, 26 | "keywords": [ 27 | "leveldb", 28 | "level", 29 | "levelup", 30 | "fs", 31 | "filesystem", 32 | "browser", 33 | "native" 34 | ], 35 | "author": { 36 | "name": "Julian Gruber", 37 | "email": "mail@juliangruber.com", 38 | "url": "http://juliangruber.com" 39 | }, 40 | "license": "MIT", 41 | "testling": { 42 | "files": "test/*.js", 43 | "browsers": [ 44 | "ie/6..latest", 45 | "firefox/14..latest", 46 | "chrome/20..latest", 47 | "safari/4.0..latest", 48 | "opera/11.0..latest", 49 | "iphone/6.0", 50 | "ipad/6.0", 51 | "android-browser/4.2" 52 | ] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/chmod.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var level = require('memdb'); 3 | var levelFS = require('..'); 4 | 5 | test('ENOENT', function (t) { 6 | t.plan(2); 7 | var fs = levelFS(level()); 8 | 9 | fs.chmod('foo', 0666, function (err) { 10 | t.assert(err); 11 | t.equal(err.code, 'ENOENT'); 12 | }); 13 | }); 14 | 15 | test('file', function (t) { 16 | t.plan(2); 17 | var db = level(); 18 | var fs = levelFS(db); 19 | 20 | db.put('foo', 'bar', function (err) { 21 | t.error(err); 22 | fs.chmod('foo', 0666, function (err) { 23 | t.notOk(err); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/chown.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var level = require('memdb'); 3 | var levelFS = require('..'); 4 | 5 | test('ENOENT', function (t) { 6 | t.plan(2); 7 | var fs = levelFS(level()); 8 | 9 | fs.chown('foo', 'uid', 'gid', function (err) { 10 | t.assert(err); 11 | t.equal(err.code, 'ENOENT'); 12 | }); 13 | }); 14 | 15 | test('file', function (t) { 16 | t.plan(2); 17 | var db = level(); 18 | var fs = levelFS(db); 19 | 20 | db.put('foo', 'bar', function (err) { 21 | t.error(err); 22 | fs.chown('foo', 'uid', 'gid', function (err) { 23 | t.notOk(err); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/exists.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var level = require('memdb'); 3 | var levelFS = require('..'); 4 | 5 | test('exists', function (t) { 6 | t.plan(2); 7 | var fs = levelFS(level()); 8 | 9 | fs.writeFile('foo', 'bar', function (err) { 10 | t.error(err); 11 | fs.exists('foo', function (exists) { 12 | t.assert(exists); 13 | }); 14 | }); 15 | }); 16 | 17 | test('exist not', function (t) { 18 | t.plan(1); 19 | var fs = levelFS(level()); 20 | 21 | fs.exists('foo', function (exists) { 22 | t.notOk(exists); 23 | }); 24 | }); 25 | 26 | test('directory', function (t) { 27 | t.plan(2); 28 | var fs = levelFS(level()); 29 | 30 | fs.writeFile('a/foo', 'bar', function (err) { 31 | t.error(err); 32 | fs.exists('a', function (exists) { 33 | t.assert(exists); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/get-level.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var level = require('memdb'); 3 | var db = level(); 4 | var fs = require('..')(db); 5 | 6 | test('with sublevel', function (t) { 7 | var a = db.sublevel('a'); 8 | var b = a.sublevel('b'); 9 | var m = fs._getLevel('/a/b/c'); 10 | t.equal(m.level, b); 11 | t.equal(m.file, 'c'); 12 | t.end(); 13 | }); 14 | 15 | test('without sublevel', function (t) { 16 | var m = fs._getLevel('c'); 17 | t.equal(m.level, db); 18 | t.equal(m.file, 'c'); 19 | t.end(); 20 | }); 21 | 22 | test('leading slash', function (t) { 23 | var m = fs._getLevel('/file.txt'); 24 | t.equal(m.level, db); 25 | t.equal(m.file, 'file.txt'); 26 | t.end(); 27 | }); 28 | -------------------------------------------------------------------------------- /test/mkir.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var level = require('memdb'); 3 | var levelFS = require('..'); 4 | 5 | test('without mode', function (t) { 6 | t.plan(3); 7 | var fs = levelFS(level()); 8 | 9 | fs.mkdir('a', function (err) { 10 | t.error(err); 11 | fs.stat('a', function (err, stat) { 12 | t.error(err); 13 | t.ok(stat.isDirectory()); 14 | }); 15 | }); 16 | }); 17 | 18 | test('with mode', function (t) { 19 | t.plan(3); 20 | var fs = levelFS(level()); 21 | 22 | fs.mkdir('a', '0666', function (err) { 23 | t.error(err); 24 | fs.stat('a', function (err, stat) { 25 | t.error(err); 26 | t.ok(stat.isDirectory()); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/read-file.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var level = require('memdb'); 3 | var levelFS = require('..'); 4 | 5 | test('simple', function (t) { 6 | t.plan(4); 7 | var fs = levelFS(level()); 8 | 9 | fs.writeFile('foo', new Buffer('bar'), function (err) { 10 | t.error(err); 11 | fs.readFile('foo', function (err, data) { 12 | t.error(err, 'no error'); 13 | t.assert(Buffer.isBuffer(data), 'is buffer'); 14 | t.equal(data.toString(), 'bar', 'correct value'); 15 | }); 16 | }); 17 | }); 18 | 19 | test('non existant, bad flag', function (t) { 20 | t.plan(3); 21 | var fs = levelFS(level()); 22 | fs.readFile('foo', function (err, data) { 23 | t.ok(err, 'has error'); 24 | t.equal(err.code, 'ENOENT', 'error code'); 25 | t.notOk(data, 'no data'); 26 | }); 27 | }); 28 | 29 | test('non existant, good flag', function (t) { 30 | t.plan(2); 31 | var fs = levelFS(level()); 32 | fs.readFile('foo', { flag: 'w' }, function (err, data) { 33 | t.notOk(err); 34 | t.equal(data.toString(), ''); 35 | }); 36 | }); 37 | 38 | test('options string', function (t) { 39 | t.plan(4); 40 | var fs = levelFS(level()); 41 | 42 | fs.writeFile('foo', 'bar', function (err) { 43 | t.error(err); 44 | fs.readFile('foo', 'utf8', function (err, data) { 45 | t.error(err); 46 | t.assert(!Buffer.isBuffer(data)); 47 | t.equal(data, 'bar'); 48 | }); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /test/read-stream.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var level = require('memdb'); 3 | var levelFS = require('..'); 4 | var Store = require('level-store'); 5 | 6 | test('simple', function (t) { 7 | t.plan(2); 8 | var db = level(); 9 | var fs = levelFS(db); 10 | 11 | var ws = Store(db).createWriteStream('foo'); 12 | ws.on('close', function () { 13 | var first = true; 14 | fs.createReadStream('foo').on('data', function (chunk) { 15 | if (first) { 16 | t.equal(chunk, 'first'); 17 | first = false; 18 | } else { 19 | t.equal(chunk, 'second'); 20 | } 21 | }); 22 | }); 23 | ws.write('first'); 24 | ws.write('second'); 25 | ws.end(); 26 | }); 27 | 28 | test('not found, bad flag', function (t) { 29 | t.plan(2); 30 | var db = level(); 31 | var fs = levelFS(db); 32 | 33 | fs.createReadStream('foo', { flags: 'r' }) 34 | .on('data', function () { 35 | t.fail(); 36 | }) 37 | .on('error', function (err) { 38 | t.ok(err); 39 | t.equal(err.code, 'ENOENT'); 40 | }); 41 | }); 42 | 43 | test('not found, good flag', function (t) { 44 | t.plan(1); 45 | var db = level(); 46 | var fs = levelFS(db); 47 | 48 | fs.createReadStream('foo', { flags: 'w' }) 49 | .on('data', function () { 50 | t.fail(); 51 | }) 52 | .on('end', function (err) { 53 | t.ok(true); 54 | }); 55 | }); 56 | 57 | test('close event', function (t) { 58 | t.plan(1); 59 | var db = level(); 60 | var fs = levelFS(db); 61 | 62 | fs.createReadStream('foo', { flags: 'w' }).on('close', function (err) { 63 | t.ok(true); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /test/readdir.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var level = require('memdb'); 3 | var levelFS = require('..'); 4 | 5 | test('list of files', function (t) { 6 | t.plan(4); 7 | var fs = levelFS(level()); 8 | 9 | var files = [ 10 | [ '/home/substack/robots.txt', 'beep boop' ], 11 | [ '/home/substack/a.txt', 'aaaa\nbbbb\ncccc' ], 12 | [ '/xyz.txt', 'x\ny\nz\n' ] 13 | ]; 14 | 15 | (function next (err) { 16 | if (err) return t.fail(err); 17 | if (files.length > 0) { 18 | var file = files.shift(); 19 | fs.writeFile(file[0], file[1], next); 20 | } 21 | else { 22 | fs.readdir('/home/substack', function (err, files) { 23 | t.deepEqual(files.sort(), [ 'a.txt', 'robots.txt' ], 'files'); 24 | }); 25 | fs.readdir('/home', function (err, files) { 26 | t.deepEqual(files.sort(), [ 'substack' ], 'dir'); 27 | }); 28 | fs.readdir('/', function (err, files) { 29 | t.deepEqual(files.sort(), [ 'home', 'xyz.txt' ], 'dir and files'); 30 | }); 31 | fs.readdir('/whatever', function (err, files) { 32 | t.ok(err, 'not found'); 33 | }); 34 | } 35 | })(); 36 | }); 37 | -------------------------------------------------------------------------------- /test/stat.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var level = require('memdb'); 3 | var levelFS = require('..'); 4 | 5 | test('ENOENT', function (t) { 6 | t.plan(2); 7 | var fs = levelFS(level()); 8 | 9 | fs.stat('foo', function (err, stat) { 10 | t.assert(err); 11 | t.equal(err.code, 'ENOENT'); 12 | }); 13 | }); 14 | 15 | test('file', function (t) { 16 | t.plan(3); 17 | var fs = levelFS(level()); 18 | 19 | fs.writeFile('foo', 'bar', function (err) { 20 | t.error(err); 21 | fs.stat('foo', function (err, stat) { 22 | t.error(err); 23 | t.assert(stat.isFile()); 24 | }); 25 | }); 26 | }); 27 | 28 | test('directory', function (t) { 29 | t.plan(3); 30 | var fs = levelFS(level()); 31 | 32 | fs.writeFile('a/foo', 'bar', function (err) { 33 | t.error(err); 34 | fs.stat('a', function (err, stat) { 35 | t.error(err); 36 | t.assert(stat.isDirectory()); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/unlink.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var level = require('memdb'); 3 | var levelFS = require('..'); 4 | 5 | test('unlink existing file', function (t) { 6 | t.plan(3); 7 | var fs = levelFS(level()); 8 | 9 | fs.writeFile('foo', 'bar', function (err) { 10 | t.error(err); 11 | fs.unlink('foo', function (err) { 12 | t.error(err); 13 | fs.stat('foo', function (err) { 14 | t.ok(err); 15 | }); 16 | }); 17 | }); 18 | }); 19 | 20 | test('unlink not existing file', function (t) { 21 | t.plan(2); 22 | var fs = levelFS(level()); 23 | 24 | fs.unlink('foo', function (err) { 25 | t.ok(err); 26 | t.equal(err.code, 'ENOENT'); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/write-file.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var level = require('memdb'); 3 | var levelFS = require('..'); 4 | 5 | test('simple', function (t) { 6 | t.plan(3); 7 | var fs = levelFS(level()); 8 | 9 | fs.writeFile('foo', 'bar', function (err) { 10 | t.error(err); 11 | fs.readFile('foo', function (err, data) { 12 | t.error(err, 'no error'); 13 | t.equal(data.toString(), 'bar', 'correct value'); 14 | }); 15 | }); 16 | }); 17 | 18 | test('replace', function (t) { 19 | t.plan(4); 20 | var fs = levelFS(level()); 21 | 22 | fs.writeFile('foo', 'bar', function (err) { 23 | t.error(err); 24 | fs.writeFile('foo', 'baz', function (err) { 25 | t.error(err); 26 | fs.readFile('foo', function (err, data) { 27 | t.error(err, 'no error'); 28 | t.equal(data.toString(), 'baz', 'replaced'); 29 | }); 30 | }); 31 | }); 32 | }); 33 | 34 | test('append', function (t) { 35 | t.plan(4); 36 | var fs = levelFS(level()); 37 | 38 | fs.writeFile('foo', 'bar', function (err) { 39 | t.error(err); 40 | fs.writeFile('foo', 'baz', { flag: 'a' }, function (err) { 41 | t.error(err); 42 | fs.readFile('foo', function (err, data) { 43 | t.error(err, 'no error'); 44 | t.equal(data.toString(), 'barbaz', 'appended'); 45 | }); 46 | }); 47 | }); 48 | 49 | }); 50 | 51 | //test('non existant, bad flag'); 52 | //test('options string'); 53 | -------------------------------------------------------------------------------- /test/write-stream.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var level = require('memdb'); 3 | var levelFS = require('..'); 4 | 5 | test('simple', function (t) { 6 | t.plan(2); 7 | var fs = levelFS(level()); 8 | 9 | var ws = fs.createWriteStream('foo'); 10 | ws.write('bar'); 11 | ws.write('baz'); 12 | ws.end(); 13 | ws.on('close', function () { 14 | fs.readFile('foo', function (err, data) { 15 | t.error(err, 'no error'); 16 | t.equal(data.toString(), 'barbaz', 'correct value'); 17 | }); 18 | }); 19 | }); 20 | 21 | test('replace', function (t) { 22 | t.plan(2); 23 | var fs = levelFS(level()); 24 | 25 | var ws = fs.createWriteStream('foo'); 26 | ws.write('bar'); 27 | ws.end(); 28 | ws.on('close', function () { 29 | var ws2 = fs.createWriteStream('foo'); 30 | ws2.write('baz'); 31 | ws2.end(); 32 | ws2.on('close', function () { 33 | fs.readFile('foo', function (err, data) { 34 | t.error(err, 'no error'); 35 | t.equal(data.toString(), 'baz', 'replaced'); 36 | }); 37 | }); 38 | }); 39 | }); 40 | 41 | test('append', function (t) { 42 | t.plan(2); 43 | var fs = levelFS(level()); 44 | 45 | var ws = fs.createWriteStream('foo'); 46 | ws.write('bar'); 47 | ws.end(); 48 | ws.on('close', function () { 49 | var ws2 = fs.createWriteStream('foo', { flags: 'a' }); 50 | ws2.write('baz'); 51 | ws2.end(); 52 | ws2.on('close', function () { 53 | fs.readFile('foo', function (err, data) { 54 | t.error(err, 'no error'); 55 | t.equal(data.toString(), 'barbaz', 'appended'); 56 | }); 57 | }); 58 | }); 59 | }); 60 | --------------------------------------------------------------------------------