Initial commit
This commit is contained in:
parent
5e90b7669f
commit
f4bc8eda96
4
.zuul.yml
Normal file
4
.zuul.yml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
ui: tape
|
||||||
|
browsers:
|
||||||
|
- name: chrome
|
||||||
|
version: -1..latest
|
21
package.json
Normal file
21
package.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "chunk-store",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"zuul-test": "zuul --local -- test/test.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"abstract-chunk-store": "^1.4.0",
|
||||||
|
"tape": "^4.9.1",
|
||||||
|
"zuul": "^3.12.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^12.0.0"
|
||||||
|
}
|
||||||
|
}
|
13
src/EventEmitter.ts
Normal file
13
src/EventEmitter.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export default class EventEmitter {
|
||||||
|
private subscribers: Array<Function> = []
|
||||||
|
|
||||||
|
subscribe (subscriber) {
|
||||||
|
this.subscribers.push(subscriber)
|
||||||
|
}
|
||||||
|
|
||||||
|
notify () {
|
||||||
|
this.subscribers.forEach((subscriber) => {
|
||||||
|
subscriber()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
169
src/Store.ts
Normal file
169
src/Store.ts
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
import EventEmitter from './EventEmitter'
|
||||||
|
|
||||||
|
export default class Store {
|
||||||
|
public chunkLength: number;
|
||||||
|
|
||||||
|
private idb: any;
|
||||||
|
private db: IDBDatabase;
|
||||||
|
|
||||||
|
// Closed flag
|
||||||
|
private closed:boolean = false;
|
||||||
|
|
||||||
|
private eventEmitter: EventEmitter;
|
||||||
|
|
||||||
|
// Use to count number of store objects created. Determine database nome
|
||||||
|
private static storeCount: number = 0
|
||||||
|
private readonly currentStoreCount: number = 0
|
||||||
|
|
||||||
|
constructor (chunkLength: number) {
|
||||||
|
this.currentStoreCount = Store.storeCount
|
||||||
|
this.chunkLength = chunkLength;
|
||||||
|
|
||||||
|
this.eventEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
// Open indexedDB (torrent_chunks) and
|
||||||
|
// create objectStore (torrent_chunk_store)
|
||||||
|
this.idb = window.indexedDB || {}; // || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
|
||||||
|
|
||||||
|
let request = this.idb.open(this.getDatabaseName(), 1);
|
||||||
|
|
||||||
|
Store.storeCount++
|
||||||
|
|
||||||
|
request.onerror = (event) => {
|
||||||
|
// Don't forget to handle errors!
|
||||||
|
}
|
||||||
|
|
||||||
|
request.onsuccess = (event: any) => {
|
||||||
|
this.db = event.target.result;
|
||||||
|
|
||||||
|
// EventEmmitter.emit('dbOpened')
|
||||||
|
this.eventEmitter.notify()
|
||||||
|
}
|
||||||
|
|
||||||
|
request.onupgradeneeded = (event: any) => {
|
||||||
|
// Save the IDBDatabase interface
|
||||||
|
this.db = event.target.result;
|
||||||
|
|
||||||
|
// Create an objectStore for this database
|
||||||
|
this.db.createObjectStore('torrent_chunk_store');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get (index: number, options, cb) {
|
||||||
|
let self = this
|
||||||
|
if (typeof options === 'function') {
|
||||||
|
return this.get(index, null, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cb) {
|
||||||
|
cb = () => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.db) {
|
||||||
|
// Execute listener
|
||||||
|
this.eventEmitter.subscribe(_get.bind(this, index))
|
||||||
|
} else {
|
||||||
|
return _get(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
function _get (index) {
|
||||||
|
// return new Promise(function (resolve, reject) {
|
||||||
|
let transaction = self.db.transaction('torrent_chunk_store', 'readonly');
|
||||||
|
let request = transaction.objectStore('torrent_chunk_store').get(index);
|
||||||
|
|
||||||
|
transaction.onerror = (event) => {
|
||||||
|
// Don't forget to handle errors!
|
||||||
|
}
|
||||||
|
|
||||||
|
request.onsuccess = (event) => {
|
||||||
|
// resolve(event.target)
|
||||||
|
let target = <IDBRequest> event.target
|
||||||
|
if (target.result === undefined) {
|
||||||
|
// cb(null, new Buffer(0))
|
||||||
|
this.nextTick(cb, new Error('Try to access non existen index'))
|
||||||
|
} else {
|
||||||
|
const buffer = new Buffer(target.result);
|
||||||
|
if (!options) {
|
||||||
|
return self.nextTick(cb, null, buffer)
|
||||||
|
}
|
||||||
|
const offset = options.offset || 0
|
||||||
|
const length = options.length || (target.result.length - offset)
|
||||||
|
cb(null, buffer.slice(offset, offset + length))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
put (index: number, chunkBuffer: Buffer, cb: Function) {
|
||||||
|
let self = this
|
||||||
|
|
||||||
|
if (!cb) {
|
||||||
|
cb = () => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunkBuffer.length !== this.chunkLength) {
|
||||||
|
return this.nextTick(cb, new Error('Chunk length must be: ' + this.chunkLength))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.db) {
|
||||||
|
// Execute listener
|
||||||
|
this.eventEmitter.subscribe(_put.bind(this, index, chunkBuffer))
|
||||||
|
} else {
|
||||||
|
_put()
|
||||||
|
}
|
||||||
|
|
||||||
|
function _put () {
|
||||||
|
let transaction = self.db.transaction('torrent_chunk_store', 'readwrite')
|
||||||
|
|
||||||
|
transaction.onerror = (event) => {
|
||||||
|
// Don't forget to handle errors!
|
||||||
|
}
|
||||||
|
|
||||||
|
let request = transaction.objectStore('torrent_chunk_store').put(chunkBuffer, index)
|
||||||
|
|
||||||
|
request.onsuccess = (event) => {
|
||||||
|
cb(null, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
request.onerror = (err) => {
|
||||||
|
cb(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close (cb) {
|
||||||
|
if (this.closed) return this.nextTick(cb, new Error('Storage is closed'));
|
||||||
|
if (!this.db) return this.nextTick(cb, undefined)
|
||||||
|
this.closed = true;
|
||||||
|
this.nextTick(cb, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy (cb) {
|
||||||
|
// Currently same implementation as close
|
||||||
|
// For indexeddb would be different:
|
||||||
|
// -- Close would empty database
|
||||||
|
// -- Destroy should delete database
|
||||||
|
this.close(cb)
|
||||||
|
this.idb.deleteDatabase(this.getDatabaseName())
|
||||||
|
}
|
||||||
|
|
||||||
|
nextTick (cb, err, val?) {
|
||||||
|
setTimeout(function () {
|
||||||
|
if (cb) cb(err, val)
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get database name based on storeCount
|
||||||
|
*/
|
||||||
|
private getDatabaseName () {
|
||||||
|
const databaseName = 'torrent_chunks'
|
||||||
|
|
||||||
|
if (this.currentStoreCount === 0) {
|
||||||
|
return databaseName
|
||||||
|
}
|
||||||
|
|
||||||
|
return databaseName + '_' + this.currentStoreCount
|
||||||
|
}
|
||||||
|
}
|
8
test/test.js
Normal file
8
test/test.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
var tests = require('abstract-chunk-store/tests')
|
||||||
|
var tape = require('tape')
|
||||||
|
|
||||||
|
var StoreLib = require('../dist/Store.js')
|
||||||
|
|
||||||
|
var Store = StoreLib.default
|
||||||
|
|
||||||
|
tests(tape, Store)
|
20
tsconfig.json
Normal file
20
tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// this aligns with Vue's browser support
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es2015", "dom"],
|
||||||
|
// this enables stricter inference for data properties on `this`
|
||||||
|
"strict": false,
|
||||||
|
// if using webpack 2+ or rollup, to leverage tree shaking:
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"**/*.spec.ts"
|
||||||
|
]
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user