@atproto/oauth-client-browser
Version:
ATPROTO OAuth client for the browser (relies on WebCrypto & Indexed DB)
94 lines • 3.25 kB
JavaScript
import 'core-js/es/symbol/dispose.js';
import { DBTransaction } from './db-transaction.js';
export class DB {
static async open(dbName, migrations, txOptions) {
const db = await new Promise((resolve, reject) => {
const request = indexedDB.open(dbName, migrations.length);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
request.onupgradeneeded = ({ oldVersion, newVersion }) => {
const db = request.result;
try {
for (let version = oldVersion; version < (newVersion ?? migrations.length); ++version) {
const migration = migrations[version];
if (migration)
migration(db);
else
throw new Error(`Missing migration for version ${version}`);
}
}
catch (err) {
db.close();
reject(err);
}
};
});
return new DB(db, txOptions);
}
#db;
constructor(db, txOptions) {
this.txOptions = txOptions;
this.#db = db;
const cleanup = () => {
this.#db = null;
db.removeEventListener('versionchange', cleanup);
db.removeEventListener('close', cleanup);
db.close(); // Can we call close on a "closed" database?
};
db.addEventListener('versionchange', cleanup);
db.addEventListener('close', cleanup);
}
get db() {
if (!this.#db)
throw new Error('Database closed');
return this.#db;
}
get name() {
return this.db.name;
}
get objectStoreNames() {
return this.db.objectStoreNames;
}
get version() {
return this.db.version;
}
async transaction(storeNames, mode, run) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
try {
const tx = this.db.transaction(storeNames, mode, this.txOptions);
let result = { done: false };
tx.oncomplete = () => {
if (result.done)
resolve(result.value);
else
reject(new Error('Transaction completed without result'));
};
tx.onerror = () => reject(tx.error);
tx.onabort = () => reject(tx.error || new Error('Transaction aborted'));
try {
const value = await run(new DBTransaction(tx));
result = { done: true, value };
tx.commit();
}
catch (err) {
tx.abort();
throw err;
}
}
catch (err) {
reject(err);
}
});
}
close() {
const { db } = this;
this.#db = null;
db.close();
}
async [Symbol.asyncDispose]() {
if (this.#db)
await this.close();
}
}
//# sourceMappingURL=db.js.map