Skip to content

Commit

Permalink
Properly commit transactions and close connections
Browse files Browse the repository at this point in the history
  • Loading branch information
ljans committed Jul 12, 2023
1 parent 87bc8eb commit dd2e29b
Showing 1 changed file with 43 additions and 26 deletions.
69 changes: 43 additions & 26 deletions idb.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/*!
* idb.js IndexedDB wrapper v3.1
* idb.js IndexedDB wrapper v3.2
* Licensed under the MIT license
* Copyright (c) 2023 Lukas Jans
* https://github.com/ljans/idb
*/
class IDB {

// Wrap IDBRequest im promise
// Wrap IDBRequest in promise
promise(request) {
return new Promise((resolve, reject) => {
request.onerror = () => reject(request.error);
Expand All @@ -16,29 +16,44 @@ class IDB {

// Constructor
constructor(tables, config={}) {
this.tables = tables;
this.config = config;

// Connect to DB
const request = indexedDB.open(config.name || 'IDB', config.version || 1);
this.connection = this.promise(request);

// Perform upgrade
request.onupgradeneeded = e => {
// Bind table controllers
for(const name of Object.keys(tables)) this[name] = new this.Table(name, this);

// Establish an initial connection to catch upgrade events
//this.connection.then(db => db.close());
}

// Establish a db connection
get connection() {
const request = indexedDB.open(this.config.name || 'IDB', this.config.version || 1);

/**
* Always check for an upgade event, because querying a table when the database does not exists
* (either because it was deleted or initial creation is commented out in the constructor)
* causes indexedDB.open to create the database without any tables until another upgrade happens.
*/
request.onupgradeneeded = async e => {
const db = request.result;

// Clear old tables
for(const name of db.objectStoreNames) if(!tables[name]) db.deleteObjectStore(name);
for(const name of db.objectStoreNames) if(!this.tables[name]) db.deleteObjectStore(name);

// Create new tables
for(const [name, options] of Object.entries(tables)) if(!db.objectStoreNames.contains(name)) db.createObjectStore(name, options);
for(const [name, options] of Object.entries(this.tables)) {
if(!db.objectStoreNames.contains(name)) db.createObjectStore(name, options);
}

// Bubble upgrade event
if(config.upgrade) config.upgrade(e);
if(this.config.upgrade) await this.config.upgrade(e);
}

// Bind table controllers
for(const name of Object.keys(tables)) this[name] = new this.Table(name, this);
// Return a promise that resolves to the established connection
return this.promise(request);
}

// Table controller
get Table() {
return class {
Expand All @@ -49,18 +64,20 @@ class IDB {
this.instance = instance;
}

// Perform operation in transaction
transaction(operation) {
return this.instance.connection.then(db => {

// Retrieve operation result
const transaction = db.transaction(this.name, 'readwrite');
const table = transaction.objectStore(this.name);
const result = operation(table);

// Return promise or wrap IDBRequest in promise
return result instanceof Promise ? result : this.instance.promise(result);
});
// Perform operation in transaction (commit transaction and close connection so it's always possible to delete the db)
async transaction(operation) {
const db = await this.instance.connection;

// Perform the operation
const transaction = db.transaction(this.name, 'readwrite');
const table = transaction.objectStore(this.name);
var result = await operation(table);
transaction.commit();

// Wait for result and close connection
if(result instanceof IDBRequest) result = await this.instance.promise(result);
db.close();
return result;
}

// Read data by index
Expand Down

0 comments on commit dd2e29b

Please sign in to comment.