UNPKG

7.72 kBJavaScriptView Raw
1var EventEmitter = require('events').EventEmitter
2var inherits = require('util').inherits
3var extend = require('xtend')
4var DeferredLevelDOWN = require('deferred-leveldown')
5var IteratorStream = require('level-iterator-stream')
6var Batch = require('./batch')
7var errors = require('level-errors')
8var assert = require('assert')
9var promisify = require('./promisify')
10var getCallback = require('./common').getCallback
11var getOptions = require('./common').getOptions
12
13var WriteError = errors.WriteError
14var ReadError = errors.ReadError
15var NotFoundError = errors.NotFoundError
16var OpenError = errors.OpenError
17var InitializationError = errors.InitializationError
18
19// Possible AbstractLevelDOWN#status values:
20// - 'new' - newly created, not opened or closed
21// - 'opening' - waiting for the database to be opened, post open()
22// - 'open' - successfully opened the database, available for use
23// - 'closing' - waiting for the database to be closed, post close()
24// - 'closed' - database has been successfully closed, should not be
25// used except for another open() operation
26
27function LevelUP (db, options, callback) {
28 if (!(this instanceof LevelUP)) {
29 return new LevelUP(db, options, callback)
30 }
31
32 var error
33
34 EventEmitter.call(this)
35 this.setMaxListeners(Infinity)
36
37 if (typeof options === 'function') {
38 callback = options
39 options = {}
40 }
41
42 options = options || {}
43
44 if (!db || typeof db !== 'object') {
45 error = new InitializationError('First argument must be an abstract-leveldown compliant store')
46 if (typeof callback === 'function') {
47 return process.nextTick(callback, error)
48 }
49 throw error
50 }
51
52 assert.strictEqual(typeof db.status, 'string', '.status required, old abstract-leveldown')
53
54 this.options = getOptions(options)
55 this._db = db
56 this.db = new DeferredLevelDOWN(db)
57 this.open(callback)
58}
59
60LevelUP.prototype.emit = EventEmitter.prototype.emit
61LevelUP.prototype.once = EventEmitter.prototype.once
62inherits(LevelUP, EventEmitter)
63
64LevelUP.prototype.open = function (opts, callback) {
65 var self = this
66 var promise
67
68 if (typeof opts === 'function') {
69 callback = opts
70 opts = null
71 }
72
73 if (!callback) {
74 callback = promisify()
75 promise = callback.promise
76 }
77
78 if (!opts) {
79 opts = this.options
80 }
81
82 if (this.isOpen()) {
83 process.nextTick(callback, null, self)
84 return promise
85 }
86
87 if (this._isOpening()) {
88 this.once('open', function () { callback(null, self) })
89 return promise
90 }
91
92 this.emit('opening')
93
94 this.db.open(opts, function (err) {
95 if (err) {
96 return callback(new OpenError(err))
97 }
98 self.db = self._db
99 callback(null, self)
100 self.emit('open')
101 self.emit('ready')
102 })
103
104 return promise
105}
106
107LevelUP.prototype.close = function (callback) {
108 var self = this
109 var promise
110
111 if (!callback) {
112 callback = promisify()
113 promise = callback.promise
114 }
115
116 if (this.isOpen()) {
117 this.db.close(function () {
118 self.emit('closed')
119 callback.apply(null, arguments)
120 })
121 this.emit('closing')
122 this.db = new DeferredLevelDOWN(this._db)
123 } else if (this.isClosed()) {
124 process.nextTick(callback)
125 } else if (this.db.status === 'closing') {
126 this.once('closed', callback)
127 } else if (this._isOpening()) {
128 this.once('open', function () {
129 self.close(callback)
130 })
131 }
132
133 return promise
134}
135
136LevelUP.prototype.isOpen = function () {
137 return this.db.status === 'open'
138}
139
140LevelUP.prototype._isOpening = function () {
141 return this.db.status === 'opening'
142}
143
144LevelUP.prototype.isClosed = function () {
145 return (/^clos|new/).test(this.db.status)
146}
147
148LevelUP.prototype.get = function (key, options, callback) {
149 if (key === null || key === undefined) {
150 throw new ReadError('get() requires a key argument')
151 }
152
153 var promise
154
155 callback = getCallback(options, callback)
156
157 if (!callback) {
158 callback = promisify()
159 promise = callback.promise
160 }
161
162 if (maybeError(this, callback)) { return promise }
163
164 options = getOptions(options)
165
166 this.db.get(key, options, function (err, value) {
167 if (err) {
168 if ((/notfound/i).test(err) || err.notFound) {
169 err = new NotFoundError('Key not found in database [' + key + ']', err)
170 } else {
171 err = new ReadError(err)
172 }
173 return callback(err)
174 }
175 callback(null, value)
176 })
177
178 return promise
179}
180
181LevelUP.prototype.put = function (key, value, options, callback) {
182 if (key === null || key === undefined) {
183 throw new WriteError('put() requires a key argument')
184 }
185
186 var self = this
187 var promise
188
189 callback = getCallback(options, callback)
190
191 if (!callback) {
192 callback = promisify()
193 promise = callback.promise
194 }
195
196 if (maybeError(this, callback)) { return promise }
197
198 options = getOptions(options)
199
200 this.db.put(key, value, options, function (err) {
201 if (err) {
202 return callback(new WriteError(err))
203 }
204 self.emit('put', key, value)
205 callback()
206 })
207
208 return promise
209}
210
211LevelUP.prototype.del = function (key, options, callback) {
212 if (key === null || key === undefined) {
213 throw new WriteError('del() requires a key argument')
214 }
215
216 var self = this
217 var promise
218
219 callback = getCallback(options, callback)
220
221 if (!callback) {
222 callback = promisify()
223 promise = callback.promise
224 }
225
226 if (maybeError(this, callback)) { return promise }
227
228 options = getOptions(options)
229
230 this.db.del(key, options, function (err) {
231 if (err) {
232 return callback(new WriteError(err))
233 }
234 self.emit('del', key)
235 callback()
236 })
237
238 return promise
239}
240
241LevelUP.prototype.batch = function (arr, options, callback) {
242 if (!arguments.length) {
243 return new Batch(this)
244 }
245
246 if (!Array.isArray(arr)) {
247 throw new WriteError('batch() requires an array argument')
248 }
249
250 var self = this
251 var promise
252
253 callback = getCallback(options, callback)
254
255 if (!callback) {
256 callback = promisify()
257 promise = callback.promise
258 }
259
260 if (maybeError(this, callback)) { return promise }
261
262 options = getOptions(options)
263
264 this.db.batch(arr, options, function (err) {
265 if (err) {
266 return callback(new WriteError(err))
267 }
268 self.emit('batch', arr)
269 callback()
270 })
271
272 return promise
273}
274
275LevelUP.prototype.iterator = function (options) {
276 return this.db.iterator(options)
277}
278
279LevelUP.prototype.clear = function (options, callback) {
280 var self = this
281 var promise
282
283 callback = getCallback(options, callback)
284 options = getOptions(options)
285
286 if (!callback) {
287 callback = promisify()
288 promise = callback.promise
289 }
290
291 if (maybeError(this, callback)) {
292 return promise
293 }
294
295 this.db.clear(options, function (err) {
296 if (err) {
297 return callback(new WriteError(err))
298 }
299 self.emit('clear', options)
300 callback()
301 })
302
303 return promise
304}
305
306LevelUP.prototype.readStream =
307LevelUP.prototype.createReadStream = function (options) {
308 options = extend({ keys: true, values: true }, options)
309 if (typeof options.limit !== 'number') { options.limit = -1 }
310 return new IteratorStream(this.db.iterator(options), options)
311}
312
313LevelUP.prototype.keyStream =
314LevelUP.prototype.createKeyStream = function (options) {
315 return this.createReadStream(extend(options, { keys: true, values: false }))
316}
317
318LevelUP.prototype.valueStream =
319LevelUP.prototype.createValueStream = function (options) {
320 return this.createReadStream(extend(options, { keys: false, values: true }))
321}
322
323LevelUP.prototype.toString = function () {
324 return 'LevelUP'
325}
326
327function maybeError (db, callback) {
328 if (!db._isOpening() && !db.isOpen()) {
329 process.nextTick(callback, new ReadError('Database is not open'))
330 return true
331 }
332}
333
334LevelUP.errors = errors
335module.exports = LevelUP.default = LevelUP