├── .npmignore ├── hook.png ├── .travis.yml ├── .gitignore ├── example.js ├── LICENSE ├── package.json ├── CHANGELOG.md ├── index.js ├── README.md └── test └── hooks.js /.npmignore: -------------------------------------------------------------------------------- 1 | *.png 2 | -------------------------------------------------------------------------------- /hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcomnes/level-hookdown/HEAD/hook.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'node' 4 | sudo: false 5 | cache: 6 | directories: 7 | - node_modules 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | var hook = require('./') 2 | var MemDB = require('memdb') 3 | var sub = require('sublevel') 4 | var db = hook(sub(MemDB(), 'test', { valueEncoding: 'json' }), { protectHook: true }) 5 | 6 | function prehook (operation, cb) { 7 | console.log('this should run before the db operation') 8 | console.log(operation) 9 | cb() 10 | } 11 | 12 | function posthook (operation, cb) { 13 | console.log('this should run after the db operation') 14 | console.log(operation) 15 | cb() 16 | } 17 | 18 | db.prehooks.push(prehook) 19 | db.posthooks.push(posthook) 20 | 21 | db.put('beep', 'boop', function (err) { 22 | if (err) throw err 23 | db.del('beep', function (err) { 24 | if (err) throw err 25 | db.batch([ 26 | { type: 'put', key: 'gleep', value: 'gloop' }, 27 | { type: 'put', key: 'bleep', value: 'bloop' } 28 | ], function (err) { 29 | if (err) throw err 30 | db.get('bleep', function (err, value) { 31 | if (err) throw err 32 | console.log(value) 33 | console.log('done') 34 | }) 35 | }) 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 hypermodules 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "level-hookdown", 3 | "version": "3.1.2", 4 | "description": "Simple leveldb hooks", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "run-s test:*", 8 | "test:deps": "dependency-check ./package.json --no-dev", 9 | "test:lint": "standard | snazzy", 10 | "test:tape": "tape test/* | tap-format-spec" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/hypermodules/level-hookdown.git" 15 | }, 16 | "keywords": [ 17 | "leveldb", 18 | "levelup", 19 | "hooks", 20 | "pre", 21 | "post", 22 | "hook" 23 | ], 24 | "author": "Bret Comnes", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/hypermodules/level-hookdown/issues" 28 | }, 29 | "homepage": "https://github.com/hypermodules/level-hookdown#readme", 30 | "devDependencies": { 31 | "@tap-format/spec": "^0.2.0", 32 | "dependency-check": "^4.1.0", 33 | "level-mem": "^5.0.1", 34 | "npm-run-all": "^4.1.5", 35 | "snazzy": "^8.0.0", 36 | "standard": "^14.0.0", 37 | "sublevel": "^2.4.0", 38 | "subleveldown": "^4.1.0", 39 | "tape": "^4.11.0" 40 | }, 41 | "dependencies": { 42 | "run-parallel": "^1.1.9", 43 | "run-parallel-limit": "^1.0.5", 44 | "run-series": "^1.1.8", 45 | "xtend": "^4.0.2" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # level-hookdown change log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## 3.1.2 9 | 10 | * Fix a few issues with returned promises. (https://github.com/hypermodules/level-hookdown/pull/31) 11 | 12 | ## 3.1.1 13 | 14 | * Update outdated dependencies 15 | 16 | ## 3.1.0 17 | 18 | * Add promise support (https://github.com/hypermodules/level-hookdown/pull/28) 19 | 20 | ## 3.0.0 21 | 22 | * Update all deps 23 | * Levelup 4.1.0 24 | 25 | ## 2.0.1 26 | 27 | * Removed abstract leveldown. not needed 28 | 29 | ## 2.0.0 30 | 31 | #### Breaking 32 | 33 | - level-hookdown now mutates the wrapped level. It does this in a correct and conventional way however, using prototype method override on the instance object. The methods `get`, `del`, and `batch` are added to the instance, and internally call the instance prototype methods between pre and post hooks. 34 | - Some of the leveldown baggage has been removed. 35 | - Most of the API is the same. 36 | - A `protectHook` option is added to preserve the batch object if the wrapped `level` mutates that object during its operation. Levels like `subleveldown` do this. 37 | 38 | #### Added 39 | 40 | * added some new tests to cover concurrency options 41 | 42 | #### Changed 43 | 44 | * minor readme improvements 45 | 46 | ## 1.0.0 47 | 48 | * engage 49 | * Initial release 50 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var runTypes = { 2 | series: require('run-series'), 3 | parallel: require('run-parallel'), 4 | limit: require('run-parallel-limit') 5 | } 6 | var getPrototypeOf = Object.getPrototypeOf 7 | var extend = require('xtend') 8 | 9 | module.exports = hook 10 | 11 | function hook (db, opts) { 12 | if (!opts) opts = {} 13 | 14 | if (Object.prototype.hasOwnProperty.call(db, 'put')) throw (new Error('Can\'t hook: put method already set on instance')) 15 | if (Object.prototype.hasOwnProperty.call(db, 'del')) throw (new Error('Can\'t hook: del method already set on instance')) 16 | if (Object.prototype.hasOwnProperty.call(db, 'batch')) throw (new Error('Can\'t hook: batch method already set on instance')) 17 | db.prehooks = [] 18 | db.posthooks = [] 19 | db._hookRunner = makeRunner(opts.type || 'parallel', opts.limit || 5) 20 | db._protectHook = opts.protectHook || false 21 | db.put = put 22 | db.batch = batch 23 | db.del = del 24 | return db 25 | } 26 | 27 | function makeRunner (type, limit) { 28 | if (type === 'limit') { 29 | return function plimit (work, callback) { 30 | runTypes.limit(work, limit, callback) 31 | } 32 | } else { 33 | return runTypes[type] 34 | } 35 | } 36 | 37 | function put (key, value, opts, cb) { 38 | cb = getCallback(opts, cb) 39 | if (!cb) { 40 | cb = createPromiseCb() 41 | } 42 | opts = getOptions(opts) 43 | 44 | var self = this 45 | var protoPut = getPrototypeOf(self).put.bind(this) 46 | var preWork = this.prehooks.map(function (hook) { 47 | return hook.bind(hook, { type: 'put', key: key, value: value, opts: opts }) 48 | }) 49 | 50 | this._hookRunner(preWork, preCb) 51 | 52 | return cb.promise 53 | 54 | function preCb (err) { 55 | if (err) return cb(err) 56 | protoPut(key, value, opts, postCb) 57 | } 58 | 59 | function postCb (err) { 60 | if (err) return cb(err) 61 | var postWork = self.posthooks.map(function (hook) { 62 | return hook.bind(hook, { type: 'put', key: key, value: value, opts: opts }) 63 | }) 64 | self._hookRunner(postWork, function (err) { 65 | cb(err) 66 | }) 67 | } 68 | } 69 | 70 | module.exports.put = put 71 | 72 | function del (key, opts, cb) { 73 | cb = getCallback(opts, cb) 74 | if (!cb) { 75 | cb = createPromiseCb() 76 | } 77 | opts = getOptions(opts) 78 | 79 | var self = this 80 | var protoDel = getPrototypeOf(self).del.bind(this) 81 | var preWork = this.prehooks.map(function (hook) { 82 | return hook.bind(hook, { type: 'del', key: key, opts: opts }) 83 | }) 84 | this._hookRunner(preWork, preCb) 85 | 86 | return cb.promise 87 | 88 | function preCb (err) { 89 | if (err) return cb(err) 90 | protoDel(key, opts, postCb) 91 | } 92 | 93 | function postCb (err) { 94 | if (err) return cb(err) 95 | var postWork = self.posthooks.map(function (hook) { 96 | return hook.bind(hook, { type: 'del', key: key, opts: opts }) 97 | }) 98 | self._hookRunner(postWork, function (err) { 99 | cb(err) 100 | }) 101 | } 102 | } 103 | 104 | module.exports.del = del 105 | 106 | function batch (operations, opts, cb) { 107 | cb = getCallback(opts, cb) 108 | if (!cb) { 109 | cb = createPromiseCb() 110 | } 111 | opts = getOptions(opts) 112 | 113 | var self = this 114 | var protoBatch = getPrototypeOf(self).batch.bind(this) 115 | var oper = operations.slice() 116 | var hookOperations = this._protectHook ? operations.map(copyArrayObj) : operations 117 | var preWork = this.prehooks.map(function (hook) { 118 | return hook.bind(hook, { type: 'batch', array: hookOperations, opts: opts }) 119 | }) 120 | 121 | this._hookRunner(preWork, preCb) 122 | 123 | return cb.promise 124 | 125 | function preCb (err) { 126 | if (err) return cb(err) 127 | if (!Array.isArray(operations)) return this.leveldown.batch.apply(null, operations, opts, postCb) 128 | protoBatch(oper, opts, postCb) 129 | } 130 | 131 | function postCb (err) { 132 | if (err) return cb(err) 133 | var postWork = self.posthooks.map(function (hook) { 134 | return hook.bind(hook, { type: 'batch', array: hookOperations, opts: opts }) 135 | }) 136 | self._hookRunner(postWork, function (err) { 137 | cb(err) 138 | }) 139 | } 140 | } 141 | 142 | module.exports.batch = batch 143 | 144 | function copyArrayObj (obj) { 145 | return extend(obj) 146 | } 147 | 148 | // These are levelup utility functions 149 | 150 | function getCallback (options, callback) { 151 | return typeof options === 'function' ? options : callback 152 | } 153 | 154 | function getOptions (options) { 155 | if (typeof options === 'string') { 156 | options = { valueEncoding: options } 157 | } 158 | if (typeof options !== 'object') { 159 | options = {} 160 | } 161 | return options 162 | } 163 | 164 | function createPromiseCb () { 165 | let callback 166 | 167 | const promise = new Promise((resolve, reject) => { 168 | callback = (err, ...rest) => { 169 | if (err) reject(err) 170 | else resolve(...rest) 171 | } 172 | }) 173 | 174 | callback.promise = promise 175 | return callback 176 | } 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # level-hookdown 2 | 3 | Simple levelup hooks implemented using instance method override of arbitrary levelups. 4 | 5 | ``` 6 | npm install level-hookdown 7 | ``` 8 | 9 | [![level badge][level-badge]](https://github.com/level/awesome) 10 | [![npm][npm-image]][npm-url] 11 | [![Build Status](https://travis-ci.org/hypermodules/level-hookdown.svg?branch=master)](https://travis-ci.org/hypermodules/level-hookdown) 12 | [![dependencies Status](https://david-dm.org/hypermodules/level-hookdown/status.svg)](https://david-dm.org/hypermodules/level-hookdown) 13 | [![devDependencies Status](https://david-dm.org/hypermodules/level-hookdown/dev-status.svg)](https://david-dm.org/hypermodules/level-hookdown?type=dev) 14 | 15 | 16 | 17 | [level-badge]: https://camo.githubusercontent.com/1bd15320a5fad1db168bba8bcedb098735f82464/68747470733a2f2f6c6576656c6a732e6f72672f696d672f62616467652e737667 18 | [npm-image]: https://img.shields.io/npm/v/level-hookdown.svg 19 | [npm-url]: https://www.npmjs.com/package/level-hookdown 20 | 21 | ## Usage 22 | 23 | ```js 24 | var hook = require('level-hookdown') 25 | var mem = require('level-mem') // or 'level' or other levelup factory 26 | var mdb = mem() 27 | var db = hook(mdb) 28 | 29 | function prehook (operation, cb) { 30 | console.log('this should run before the db operation') 31 | console.log(operation) 32 | cb() 33 | } 34 | 35 | function posthook (operation, cb) { 36 | console.log('this should run after the db operation') 37 | console.log(operation) 38 | cb() 39 | } 40 | 41 | db.prehooks.push(prehook) 42 | db.posthooks.push(posthook) 43 | 44 | db.put('beep', 'boop', function (err) { 45 | if (err) throw err 46 | db.del('beep', function (err) { 47 | if (err) throw err 48 | db.batch([ 49 | { type: 'put', key: 'father', value: 'gloop' }, 50 | { type: 'put', key: 'another', value: 'heep' } 51 | ], function (err) { 52 | if (err) throw err 53 | console.log('done') 54 | }) 55 | }) 56 | }) 57 | 58 | ``` 59 | 60 | ## API 61 | 62 | #### `hookdb = hook(db, [options])` 63 | 64 | Returns a levelup instance that executes a series of pre and post hook functions before `put`, `del`, and `batch` method calls. Composes well with [mafintosh/subleveldown](https://github.com/mafintosh/subleveldown). Conflicts with [dominictarr/level-sublevel](https://github.com/dominictarr/level-sublevel). 65 | 66 | The following `options` can be set when wrapping a `level` with `hook`: 67 | 68 | ```js 69 | { 70 | type: 'parallel' | 'series' | 'limit', 71 | limit: 5, 72 | protectHook: false 73 | } 74 | ``` 75 | 76 | - The `type` determines if the hook functions are run in series, parallel or parallel-limit. Default is `parallel`. 77 | - The limit option is passed to `run-parallel-limit` when `type` is set to `limit`. The default is 5. 78 | - `protectHook` performs a deep copy on the operation array in batches to preserve values if the levelup mutates them (like `subleveldown` does). 79 | 80 | ### Hooks 81 | 82 | #### `hookdb.prehooks` 83 | 84 | An array of hook functions that are run before `put`, `del`, and `batch` method calls to the `hookdb` instance. If a hook function in the prehook array returns an `err` object to its callback, the originating `put`, `del` or `batch` operation will not be run on the contained `db` that `hookdb` is wrapping. A prehook veto convetion could be built on top of this behavior via error handling and retry. 85 | 86 | #### `hookdb.posthooks` 87 | 88 | An array of functions that are run after sucessful `put`, `del`, and `batch` method calls on the wrapped `db` instance inside a `hookdb` instance. 89 | 90 | #### `hookFn(operation, callback)` 91 | 92 | Hook functions receive an `operation` object that describes the level operation and a `callback` function. 93 | 94 | `hookdb.prehooks` and `hookdb.posthooks` are arrays that you can add, rearrange, and delete hook functions from using standard array methods like `hookdb.prehooks.push(fn)` and `hookdb.posthooks.pop(fn)`. 95 | 96 | ##### `operation` 97 | 98 | The `operation` argument can contain an object that looks like: 99 | 100 | - `{type:'put', key: 'key', value: 'value', opts}` 101 | - `{type: 'del', key: 'key', opts}` 102 | - `{type: 'batch', array: [operationArray], opts}` 103 | 104 | The `opts` object in the level operation object are the options that get passed through to the wrapped level. 105 | 106 | ## See Also 107 | 108 | - [hypermodules/level-auto-index](https://github.com/hypermodules/level-auto-index) - Automatic secondary indexing for leveldb and subleveldown leveraging `level-hookdown`. 109 | - [hypermodules/level-idx](https://github.com/hypermodules/level-idx) - Another high-level API for creating secondary leveldb indexes using `level-auto-index` and `level-hookdown`. 110 | 111 | This module is basically an alternative implementation of: 112 | 113 | - [dominictarr/level-hooks](https://github.com/dominictarr/level-hooks) 114 | 115 | but aimed at a subleveldown workflow. These were also influential: 116 | 117 | - [dominictarr/level-post](https://github.com/dominictarr/level-post) 118 | - [dominictarr/level-sublevel](https://github.com/dominictarr/level-sublevel) 119 | -------------------------------------------------------------------------------- /test/hooks.js: -------------------------------------------------------------------------------- 1 | var tape = require('tape') 2 | var hook = require('../') 3 | var mem = require('level-mem') 4 | var sub = require('subleveldown') 5 | 6 | tape('test level-hookdown', function (t) { 7 | var sublevel = sub(mem(), 'test', { valueEncoding: 'json' }) 8 | var db = hook(sublevel) 9 | 10 | t.plan(4) 11 | 12 | db.prehooks.push(prehook) 13 | db.posthooks.push(posthook) 14 | 15 | var setVal = { 16 | beep: 'boop' 17 | } 18 | 19 | db.put('main', setVal, function (err) { 20 | t.error(err, 'put on wrapped db is error free') 21 | }) 22 | 23 | function prehook (operation, cb) { 24 | db.get('main', function (err, value) { 25 | t.equal(err.type, 'NotFoundError', 'main operation hasnt been performed in prehook') 26 | cb(err.type !== 'NotFoundError' ? err : null) 27 | }) 28 | } 29 | 30 | function posthook (operation, cb) { 31 | db.get('main', function (err, value) { 32 | t.error(err) 33 | if (err) return cb(err) 34 | t.deepEqual(value, setVal, 'main put worked fine') 35 | cb() 36 | }) 37 | } 38 | }) 39 | 40 | tape('[promises] test level-hookdown', async function (t) { 41 | var sublevel = sub(mem(), 'test', { valueEncoding: 'json' }) 42 | var db = hook(sublevel) 43 | 44 | t.plan(4) 45 | 46 | db.prehooks.push(prehook) 47 | db.posthooks.push(posthook) 48 | 49 | var setVal = { 50 | beep: 'boop' 51 | } 52 | 53 | t.doesNotThrow(async function () { 54 | await db.put('main', setVal) 55 | }, 'put on wrapped db is error free') 56 | 57 | function prehook (operation, cb) { 58 | db.get('main', function (err, value) { 59 | t.equal(err.type, 'NotFoundError', 'main operation hasnt been performed in prehook') 60 | cb(err.type !== 'NotFoundError' ? err : null) 61 | }) 62 | } 63 | 64 | function posthook (operation, cb) { 65 | db.get('main', function (err, value) { 66 | t.error(err) 67 | if (err) return cb(err) 68 | t.deepEqual(value, setVal, 'main put worked fine') 69 | cb() 70 | }) 71 | } 72 | }) 73 | 74 | tape('test level-hookdown series', function (t) { 75 | var level = mem({ valueEncoding: 'json' }) 76 | var db = hook(level, { type: 'series' }) 77 | 78 | t.plan(4) 79 | 80 | db.prehooks.push(prehook) 81 | db.posthooks.push(posthook) 82 | 83 | var setVal = { 84 | beep: 'boop' 85 | } 86 | 87 | db.put('main', setVal, function (err) { 88 | t.error(err, 'put on wrapped db is error free') 89 | }) 90 | 91 | function prehook (operation, cb) { 92 | db.get('main', function (err, value) { 93 | t.equal(err.type, 'NotFoundError', 'main operation hasnt been performed in prehook') 94 | cb(err.type !== 'NotFoundError' ? err : null) 95 | }) 96 | } 97 | 98 | function posthook (operation, cb) { 99 | db.get('main', function (err, value) { 100 | t.error(err) 101 | if (err) return cb(err) 102 | t.deepEqual(value, setVal, 'main put worked fine') 103 | cb() 104 | }) 105 | } 106 | }) 107 | 108 | tape('[promises] test level-hookdown series', function (t) { 109 | var level = mem({ valueEncoding: 'json' }) 110 | var db = hook(level, { type: 'series' }) 111 | 112 | t.plan(4) 113 | 114 | db.prehooks.push(prehook) 115 | db.posthooks.push(posthook) 116 | 117 | var setVal = { 118 | beep: 'boop' 119 | } 120 | 121 | t.doesNotThrow(async function () { 122 | await db.put('main', setVal) 123 | }, 'put on wrapped db is error free') 124 | 125 | function prehook (operation, cb) { 126 | db.get('main', function (err, value) { 127 | t.equal(err.type, 'NotFoundError', 'main operation hasnt been performed in prehook') 128 | cb(err.type !== 'NotFoundError' ? err : null) 129 | }) 130 | } 131 | 132 | function posthook (operation, cb) { 133 | db.get('main', function (err, value) { 134 | t.error(err) 135 | if (err) return cb(err) 136 | t.deepEqual(value, setVal, 'main put worked fine') 137 | cb() 138 | }) 139 | } 140 | }) 141 | 142 | tape('test level-hookdown limit', function (t) { 143 | var sublevel = sub(mem(), 'test', { valueEncoding: 'json' }) 144 | var db = hook(sublevel, { type: 'limit', limit: 2 }) 145 | 146 | t.plan(4) 147 | 148 | db.prehooks.push(prehook) 149 | db.posthooks.push(posthook) 150 | 151 | var setVal = { 152 | beep: 'boop' 153 | } 154 | 155 | db.put('main', setVal, function (err) { 156 | t.error(err, 'put on wrapped db is error free') 157 | }) 158 | 159 | function prehook (operation, cb) { 160 | db.get('main', function (err, value) { 161 | t.equal(err.type, 'NotFoundError', 'main operation hasnt been performed in prehook') 162 | cb(err.type !== 'NotFoundError' ? err : null) 163 | }) 164 | } 165 | 166 | function posthook (operation, cb) { 167 | db.get('main', function (err, value) { 168 | t.error(err) 169 | if (err) return cb(err) 170 | t.deepEqual(value, setVal, 'main put worked fine') 171 | setTimeout(cb, 100) 172 | }) 173 | } 174 | }) 175 | 176 | tape('[promises] test level-hookdown limit', function (t) { 177 | var sublevel = sub(mem(), 'test', { valueEncoding: 'json' }) 178 | var db = hook(sublevel, { type: 'limit', limit: 2 }) 179 | 180 | t.plan(4) 181 | 182 | db.prehooks.push(prehook) 183 | db.posthooks.push(posthook) 184 | 185 | var setVal = { 186 | beep: 'boop' 187 | } 188 | 189 | t.doesNotThrow(async function () { 190 | await db.put('main', setVal) 191 | }, 'put on wrapped db is error free') 192 | 193 | function prehook (operation, cb) { 194 | db.get('main', function (err, value) { 195 | t.equal(err.type, 'NotFoundError', 'main operation hasnt been performed in prehook') 196 | cb(err.type !== 'NotFoundError' ? err : null) 197 | }) 198 | } 199 | 200 | function posthook (operation, cb) { 201 | db.get('main', function (err, value) { 202 | t.error(err) 203 | if (err) return cb(err) 204 | t.deepEqual(value, setVal, 'main put worked fine') 205 | setTimeout(cb, 100) 206 | }) 207 | } 208 | }) 209 | --------------------------------------------------------------------------------