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