Advertisement
Uno-Dan

DBMagic

Mar 4th, 2021
1,165
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.    
  2. const log = console.log;
  3.  
  4. const indexedDB = window.indexedDB || window.mozIndexedDB ||
  5.     window.webkitIndexedDB || window.msIndexedDB;
  6.    
  7. class Table {
  8.   constructor( db, name) {
  9.     this.db = db;
  10.     this.name = name;
  11.     this.debug = db.debug;
  12.     this.limitSize = 0;
  13.   }
  14.  
  15.   limit( limit ) {
  16.     this.limitSize = limit;
  17.     return this;
  18.   }
  19.  
  20.   order( mode = 'asc' ) {
  21.     this.mode = ( mode === 'asc' ? true : false );
  22.     return this;
  23.   }
  24.  
  25.   orderBy( ...keys ) {
  26.     this.orderby = keys;
  27.     return this;
  28.   }
  29.  
  30.   async bound( ...args ) {
  31.     return IDBKeyRange.lowerBound( ...args );
  32.   }
  33.  
  34.   async add( ...args ) {
  35.     const db = this.db.name;
  36.    
  37.     if ( ! Array.isArray( args ) ) args = [ args ];
  38.    
  39.     const stores = await this.db.stores();
  40.     const version = await this.db.version();
  41.     const create_store = ! stores.contains( this.name );
  42.    
  43.     let promise = false;
  44.    
  45.     if ( create_store ) {
  46.       promise = new Promise( ( resolve, reject ) => {
  47.         let req = indexedDB.open( db, version + 1 );
  48.        
  49.         req.onerror = e => { reject( e ); };
  50.        
  51.         req.onsuccess = () => { req.result.close(); resolve(); };
  52.        
  53.         req.onupgradeneeded = () => {
  54.           req.result.createObjectStore( this.name, { keyPath: 'id' } );
  55.         };
  56.       } );
  57.     }
  58.    
  59.     if ( promise ) await promise;
  60.    
  61.     let promises = [];
  62.    
  63.     const tx = await this.transaction( this.name, 'readwrite' );
  64.    
  65.     return new Promise( ( resolve, reject ) => {
  66.       args.forEach( item => {
  67.         promises.push( this.do_add( tx, item ) );
  68.       } );
  69.       Promise.all( promises )
  70.       .then( () => resolve( this ) )
  71.       .catch( err => reject( err ) );
  72.     } );
  73.   }
  74.  
  75.   async do_add( tx, args) {
  76.     const db = this.db.name;
  77.    
  78.     return new Promise(( resolve, reject ) => {
  79.       let addReq = tx.objectStore( this.name ).add( args );
  80.  
  81.       addReq.onerror = e => {
  82.         if ( this.debug ) {  
  83.           const err = this.db.haltErrors ? 'ERROR' : 'WARN';
  84.          
  85.           const msg = `[ ${ err }:add ], could not add key [${ args.id
  86.           }] in store [${  addReq.source.name }] in database [${ db }], ` +
  87.           e.target.error;
  88.           log( msg );
  89.         }
  90.         this.db.haltErrors ? reject( e ) : resolve( this );  
  91.       };
  92.  
  93.       addReq.onsuccess = () => {  
  94.         if ( this.debug )
  95.           log( `[ INFO:add ], Added key [${ args.id }] in store [${
  96.           addReq.source.name }] in database [${ db }]` );
  97.         resolve( this );  
  98.       };
  99.     } );
  100.   }
  101.  
  102.   async get( ...args ) {
  103.     const sortColumns = ( arr, props, mode = true ) => {
  104.       if ( ! Array.isArray( props ) ) props = [ props ];
  105.      
  106.       let sorted = arr.sort( ( a, b ) => {
  107.         let prop = props[ 0 ];
  108.         for ( let idx = 0; idx < props.length; idx++ ) {
  109.           const curProp = props[ idx ];
  110.           if ( a[ curProp ] !== b[ curProp ] ) {
  111.             prop = curProp;
  112.             break;
  113.           }  
  114.         }  
  115.         if ( a[ prop ] === b[ prop ] ) return 0;
  116.           return ( a[ prop ] > b[ prop ] ? -1 : 1 ) * ( mode ? -1 : 1 );
  117.       } );  
  118.       return sorted;
  119.     };
  120.    
  121.     switch ( typeof args) {
  122.       case 'string': args = parseInt( args );
  123.       case 'number': args = [ args ];
  124.       case 'object':
  125.         if ( Array.isArray( args ) ) {
  126.           args.forEach( ( item, idx ) => {
  127.             if ( typeof item === 'number' )
  128.               args[ idx ] = { id: item };
  129.             else if ( typeof item === 'string' )
  130.               args[ idx ] = { id: parseInt( item) };
  131.           } );
  132.         }
  133.       break;
  134.       case 'undefined': args = false; break;
  135.     }
  136.     if ( ! args.length ) args = false;
  137.    
  138.     const db = this.db.name;
  139.     const stores = await this.db.stores();
  140.     if ( ! stores.contains( this.name ) ) return false;
  141.     if ( args && ! Array.isArray( args ) ) args = [ args ];
  142.    
  143.     let promises = [];
  144.     const tx = await this.transaction( this.name, 'readonly' );
  145.    
  146.     return new Promise( ( resolve, reject ) => {
  147.       if ( args )
  148.         args.forEach( item => { promises.push( this.do_get( tx, item ) ); } );
  149.       else
  150.         promises.push( this.do_get( tx ) );
  151.            
  152.       Promise.all( promises )      
  153.       .then( results => {
  154.        
  155.         let results_copy = [];
  156.         if ( args ) {
  157.           results.forEach( item => results_copy.push( item[0] ) );
  158.           results = results_copy;
  159.          
  160.         } else results = results[ 0 ];
  161.        
  162.         if ( this.limitSize && results.length > 1 ) {
  163.           results = results.slice( 0, this.limitSize );
  164.           this.limitSize = 0;
  165.         }
  166.        
  167.         if ( this.orderby ) {
  168.           results = sortColumns( results, this.orderby, this.mode );
  169.         }
  170.         resolve( results );
  171.       } )
  172.       .catch( err => reject( err ) );
  173.     } );
  174.   }
  175.  
  176.   async do_get( tx, args ) {
  177.     const db = this.db.name;
  178.    
  179.     return new Promise( ( resolve, reject ) => {
  180.       let obj = tx.objectStore( this.name );
  181.      
  182.       let req = args ? obj.get( parseInt( args.id ) ) : obj.getAll();
  183.       req.onerror = e => {
  184.         if ( this.debug )
  185.           log( `[ ERROR:getAll ], could not retrieve keys from store [${
  186.           req.source.name }] in database [${ db }]` );
  187.         reject( e );  
  188.       };
  189.  
  190.       req.onsuccess = () => {
  191.         if ( this.debug )
  192.           log( `[ INFO:getAll ], Retrieved all keys from store [${
  193.           req.source.name }] in database [${ db }]` );
  194.  
  195.         const result = ! Array.isArray( req.result ) ?
  196.         [ req.result ] : req.result;
  197.         resolve( result );  
  198.       };
  199.     } );
  200.   }
  201.          
  202.   async update( ...args ) {
  203.     const db = this.db.name;
  204.     const stores = await this.db.stores();
  205.     if ( ! stores.contains( this.name ) ) return this;
  206.        
  207.     const values = await this.get( ...args );
  208.     args.forEach( ( item, idx ) => {
  209.       let id = item.id;
  210.       let defaults = values.find( x => x.id === id );
  211.       if ( defaults ) {
  212.         item = { ...defaults, ...item };
  213.         args[ idx ] = item;
  214.       }
  215.     } );
  216.    
  217.     let promises = [];
  218.     const tx = await this.transaction( this.name, 'readwrite' );
  219.     return new Promise( ( resolve, reject ) => {
  220.      
  221.       args.forEach( item => { promises.push( this.do_update( tx, item ) ); } );
  222.       Promise.all( promises )
  223.       .then( () => resolve( this ) )
  224.       .catch( err => reject( err ) );
  225.     } );
  226.   }
  227.  
  228.   async do_update( tx, args ) {
  229.     const db = this.db.name;
  230.     return new Promise(( resolve, reject ) => {
  231.       let addReq = tx.objectStore( this.name ).put( args );
  232.  
  233.       addReq.onerror = e => {
  234.         if ( this.debug )
  235.           log( `[ ERROR:add ], could not update key [${ args.id
  236.           }] in store [${ addReq.source.name }] in database [${ db }]` );
  237.         reject( e );  
  238.       };
  239.  
  240.       addReq.onsuccess = () => {  
  241.         if ( this.debug )
  242.           log( `[ INFO:update ], Updated key [${ args.id }] in store [${
  243.           addReq.source.name }] in database [${ db }]` );
  244.         resolve( this );  
  245.       };
  246.     } );
  247.   }
  248.  
  249.   async delete( ...args ) {
  250.     const db = this.db.name;
  251.     const stores = await this.db.stores();
  252.     const version = await this.db.version();
  253.    
  254.     switch ( typeof args) {
  255.       case 'string': args = parseInt( args );
  256.       case 'number': args = [ args ];
  257.       case 'object':
  258.         if ( Array.isArray( args ) ) {
  259.           args.forEach( ( item, idx ) => {
  260.             if ( typeof item === 'number' )
  261.               args[ idx ] = { id: item };
  262.             else if ( typeof item === 'string' )
  263.               args[ idx ] = { id: parseInt( item) };
  264.           } );
  265.         }
  266.       break;
  267.       case 'undefined': args = false; break;
  268.     }
  269.     if ( ! args.length ) args = false;
  270.    
  271.     let promises = [];
  272.    
  273.     if ( ! args )
  274.       return new Promise( ( resolve, reject ) => {
  275.         var req = indexedDB.open( this.db.name, version + 1  );
  276.  
  277.         req.onerror = e => reject( e );
  278.  
  279.         req.onsuccess = ( ) => { req.result.close(); resolve( this ); };
  280.  
  281.         req.onupgradeneeded = () => {
  282.           req.result.deleteObjectStore( this.name );
  283.         };
  284.       } );
  285.    
  286.     else return new Promise( ( resolve, reject ) => {
  287.       args.forEach( item => { promises.push( this.do_delete( item ) ); } );
  288.       Promise.all( promises )
  289.       .then( () => resolve( this ) )
  290.       .catch( err => reject( err ) );
  291.     } );
  292.    
  293.   }  
  294.  
  295.   async do_delete( args ) {
  296.     const db = this.db.name;
  297.     const tx = await this.transaction( this.name, 'readwrite' );
  298.    
  299.     return new Promise(( resolve, reject ) => {
  300.       let req = tx.objectStore( this.name ).delete( args.id );
  301.  
  302.       req.onerror = e => {
  303.         if ( this.debug ) {  
  304.           const err = this.db.haltErrors ? 'ERROR' : 'WARN';
  305.           const msg = `[ ${ err }:add ], could not add key [${ args.id
  306.           }] in store [${  req.source.name }] in database [${ db }], ` +
  307.           e.target.error;
  308.           log( msg );
  309.         }
  310.         this.db.haltErrors ? reject( e ) : resolve( this );
  311.       };
  312.  
  313.       req.onsuccess = () => {  
  314.         if ( this.debug )
  315.           log( `[ INFO:add ], Deleted key [${ args.id }] from store [${
  316.           req.source.name }] in database [${ db }]` );
  317.         resolve( this );  
  318.       };
  319.     } );
  320.   }
  321.  
  322.   async addIndex( ...keys ) {
  323.     const db = this.db.name;
  324.     let version = await this.db.version();
  325.        
  326.     return new Promise( ( resolve, reject ) => {
  327.       var req = indexedDB.open( this.db.name, version + 1  );
  328.  
  329.       req.onerror = e => reject( e );
  330.  
  331.       req.onsuccess = ( ) => { req.result.close(); resolve( this ); };
  332.  
  333.       req.onupgradeneeded = ( e ) => {
  334.         let db = e.target.result;
  335.         let tx = event.target.transaction;
  336.        
  337.         let objectStore = ( ! db.objectStoreNames.contains( this.name ) ) ?
  338.         db.createObjectStore( this.name ) : tx.objectStore( this.name );
  339.  
  340.         keys.forEach( key => {
  341.           if ( ! objectStore.indexNames.contains( `${ key }IDX` ) ) {
  342.             objectStore.createIndex( `${ key }IDX`, key, {  unique: false } );
  343.           }
  344.         } );
  345.       };
  346.     } );
  347.   }
  348.  
  349.   async getIndex( index ) {
  350.     let tx = await this.transaction( this.name );
  351.     let store = tx.objectStore( this.name );
  352.     return store.index( `${index}IDX` );
  353.   }
  354.  
  355.   async transaction( store, mode ) {    
  356.     const db = this.db.name;  
  357.     if ( ! Array.isArray( store ) ) store = [ store ];
  358.    
  359.     return new Promise(( resolve, reject ) => {
  360.       let openReq = indexedDB.open( db );
  361.  
  362.       openReq.onerror = e => reject( e );
  363.  
  364.       openReq.onsuccess = () => {
  365.         const tx = openReq.result.transaction ( store, mode );
  366.         resolve( tx );
  367.         openReq.result.close();
  368.       };
  369.     } );
  370.   }
  371. }
  372.  
  373. export default class DBMagic {
  374.   constructor( name ) {
  375.     this.name = name;
  376.     this.debug = false;
  377.     this.tables = {};
  378.     this.init();
  379.   }
  380.  
  381.   init() {
  382.     Object.values( this.stores() ).forEach( name => this.table( name ) );
  383.   }
  384.  
  385.   table( name ) {
  386.     let args = {}
  387.     if ( ! ( name in this.tables ) ) {
  388.       this.tables[ name ] = new Table( this, name, args );
  389.     }
  390.     return this.tables[ name ];
  391.   }
  392.  
  393.   config( args ) {
  394.     for (var key in args )
  395.       this[ key ] = args[ key ];
  396.     return this;
  397.   }
  398.  
  399.   delete( args ) {
  400.     if ( args ) {
  401.       if ( ! Array.isArray( args ) ) args = [ args ];
  402.    
  403.       args.forEach( store => {
  404.         if ( store in this.tables ) {
  405.           this.tables[ store ].delete();
  406.           delete this.tables[ store ];
  407.         }
  408.       } );
  409.     } else {
  410.       return new Promise( ( resolve, reject ) => {
  411.         var req = indexedDB.deleteDatabase( this.name );
  412.  
  413.         req.onerror = err => { reject( err ); };
  414.  
  415.         req.onsuccess = () => {
  416.           if( this.debug )
  417.             log( '[INFO:delete], Deleted database [' + this.name + ']');
  418.          
  419.           this.tables = {};
  420.           resolve( this );
  421.         };
  422.       } );
  423.     }
  424.   }
  425.  
  426.   stores() {
  427.     return new Promise( ( resolve, reject ) => {
  428.       var req = indexedDB.open( this.name );
  429.  
  430.       req.onerror = err => { reject( err ); };
  431.  
  432.       req.onsuccess = () => {
  433.         let names = req.result.objectStoreNames;
  434.         if( this.debug )
  435.           log( '[INFO:db_version], Database [' + this.name +
  436.           '], version []');
  437.  
  438.         resolve( names );
  439.         req.result.close();
  440.       };
  441.      
  442.       req.onupgradeneeded = () => { return true; };
  443.     } );
  444.   }
  445.  
  446.   version() {
  447.     const db = this.name;
  448.     return new Promise( ( resolve, reject ) => {
  449.       var req = indexedDB.open( db );
  450.  
  451.       req.onerror = err => { reject( err ); };
  452.  
  453.       req.onsuccess = () => {
  454.         let version = req.result.version;
  455.         if( this.debug )
  456.           log( '[INFO:db_version], Database [' + db +
  457.           '], version [' + version + ']');
  458.  
  459.         req.result.close();
  460.         resolve( version );
  461.       };
  462.       req.onupgradeneeded = () => { return true; };
  463.     } );
  464.   }
  465. }
  466.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement