blob: e26559c12d6d9e8cc1ff148efca5199d26df23f2 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
var assert = require('assert');
var debug = require('debug')('reader:pdf-store');
var EventEmitter = require('events').EventEmitter;
var extend = require('xtend');
var format = require('format');
var hash = require('./hash-blob');
var IteratorStream = require('level-iterator-stream');
var leveljs = require('level-js');
var series = require('run-series');
var through = require('through2');
var thunky = require('thunky');
var window = require('global/window');
// Create a singleton instance of a database for this application.
var db = leveljs('pdf-store');
// Use the thunky module to lazily evaluate the async db.open() and cache the
// result for subsequent calls. Any of the exported methods could be the first
// database call and need to start with a call to open in thier series chain of
// async function calls.
var open = thunky(function opening(callback) {
debug('initializing database');
db.open(callback);
});
// Create a singleton EventEmitter instance so that other parts of the
// application can react to emitted database updates (puts, deletes, etc).
// NOTE: There might be a better way to do this...
var ee = new EventEmitter();
var defaults = {
raw: true,
silent: false
};
module.exports = {
get: get,
put: put,
del: del,
has: has,
createReadStream: createReadStream,
on: ee.on.bind(ee)
};
function createReadStream() {
var stream = through.obj();
open(function onopen(err) {
if (err) {
stream.emit('error', err);
return;
}
var iterator = new IteratorStream(db.iterator());
iterator.pipe(stream);
});
return stream;
}
function get(key, options, callback) {
callback = typeof options === 'function' ? options : callback;
options = extend(defaults, (typeof options === 'object' ? options : {}));
var tasks = [
open,
db.get.bind(db, key, options)
];
series(tasks, function done(err, results) {
if (err) {
debug('get error: %s\ns', err.message, err.stack);
return callback(err);
}
// The value is the result of the last task.
var last = results.length - 1;
var value = results[last];
if (value.size === 0) {
// TODO(jasoncampbell): figure out why blobs from indexedDB will randomly
// have a size === 0
var message = format('PDF: %s has a zero size', key);
return callback(new Error(message));
}
callback(null, value);
});
}
function has(key, callback) {
var tasks = [
open,
get.bind(null, key)
];
series(tasks, function done(err, results) {
if (err && err.message !== 'NotFound') {
callback(err);
} else {
callback(null, !err);
}
});
}
function put(file, options, callback) {
callback = typeof options === 'function' ? options : callback;
options = extend(defaults, (typeof options === 'object' ? options : {}));
assert.ok(file instanceof window.Blob, 'Must use a Blob object.');
hash(file, onhash);
return;
function onhash(err, key) {
if (err) {
debug('hash error: %s\ns', err.message, err.stack);
return callback(err);
}
var tasks = [
open,
db.put.bind(db, key, file, options),
];
series(tasks, function done(err, results){
if (err) {
debug('put error: %s\ns', err.message, err.stack);
return callback(err);
}
callback(null, key, file);
if (!options.silent) {
ee.emit('put', key, file);
}
});
}
}
function del(key, options, callback) {
callback = typeof options === 'function' ? options : callback;
options = extend(defaults, (typeof options === 'object' ? options : {}));
var tasks = [
open,
db.del.bind(db, key, options, callback),
];
series(tasks, function done(err, results) {
if (err) {
debug('del error: %s\n%s', err.message, err.stack);
callback(err);
} else {
callback();
}
});
}