UNPKG

kura

Version:

The FileSystem API abstraction library.

398 lines 15.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IdbAccessor = void 0; const AbstractAccessor_1 = require("../AbstractAccessor"); const BinaryConverter_1 = require("../BinaryConverter"); const FileError_1 = require("../FileError"); const FileSystemConstants_1 = require("../FileSystemConstants"); const FileSystemUtil_1 = require("../FileSystemUtil"); const ObjectUtil_1 = require("../ObjectUtil"); const TextConverter_1 = require("../TextConverter"); const IdbFileSystem_1 = require("./IdbFileSystem"); const IdbUtil_1 = require("./IdbUtil"); const ENTRY_STORE = "entries"; const CONTENT_STORE = "contents"; const indexedDB = window.indexedDB || window.mozIndexedDB || window.msIndexedDB; class IdbAccessor extends AbstractAccessor_1.AbstractAccessor { constructor(dbName, options) { super(options); this.dbName = dbName; this.filesystem = new IdbFileSystem_1.IdbFileSystem(this); } get name() { return this.dbName; } async doDelete(fullPath, _isFile) { try { await this.doGetObject(fullPath); } catch { return; } await this.doDeleteWithStore(ENTRY_STORE, fullPath); await this.doDeleteWithStore(CONTENT_STORE, fullPath); } doGetObject(fullPath) { return new Promise((resolve, reject) => { void (async () => { const db = await this.open(this.dbName); const tx = db.transaction([ENTRY_STORE], "readonly"); const range = IDBKeyRange.only(fullPath); const onError = (ev) => { const req = ev.target; db.close(); reject(new FileError_1.NotFoundError(this.name, fullPath, req.error || ev)); }; tx.onabort = onError; tx.onerror = onError; const request = tx.objectStore(ENTRY_STORE).get(range); const onSuccess = () => { db.close(); if (request.result != null) { resolve(request.result); } else { reject(new FileError_1.NotFoundError(this.name, fullPath)); } }; tx.oncomplete = onSuccess; request.onerror = onError; })(); }); } doGetObjects(fullPath) { return new Promise((resolve, reject) => { void (async () => { const db = await this.open(this.dbName); const tx = db.transaction([ENTRY_STORE], "readonly"); const onError = (ev) => { const req = ev.target; db.close(); reject(new FileError_1.NotFoundError(this.name, fullPath, req.error || ev)); }; tx.onabort = onError; tx.onerror = onError; const objects = []; tx.oncomplete = () => { db.close(); resolve(objects); }; let slashCount; if (fullPath === FileSystemConstants_1.DIR_SEPARATOR) { slashCount = 1; } else { slashCount = (0, IdbUtil_1.countSlash)(fullPath) + 1; } const range = (0, IdbUtil_1.getRange)(fullPath); const request = tx.objectStore(ENTRY_STORE).openCursor(range); request.onsuccess = (ev) => { const cursor = ev.target.result; if (cursor) { const obj = cursor.value; if (slashCount === (0, IdbUtil_1.countSlash)(obj.fullPath)) { objects.push(obj); } cursor.continue(); } }; request.onerror = onError; })(); }); } doMakeDirectory(fullPath) { return this.doPutObjectIDB({ fullPath, name: (0, FileSystemUtil_1.getName)(fullPath) }); } doPutObjectIDB(obj) { return new Promise((resolve, reject) => { void (async () => { const db = await this.open(this.dbName); const entryTx = db.transaction([ENTRY_STORE], "readwrite"); const onError = (ev) => { db.close(); reject(new FileError_1.InvalidModificationError(this.name, obj.fullPath, ev)); }; entryTx.onabort = onError; entryTx.onerror = onError; entryTx.oncomplete = () => { db.close(); resolve(); }; const entryReq = entryTx .objectStore(ENTRY_STORE) .put(obj, obj.fullPath); entryReq.onerror = onError; })(); }); } doReadContent(fullPath) { return new Promise((resolve, reject) => { void (async () => { const db = await this.open(this.dbName); const onError = (ev) => { const req = ev.target; db.close(); reject(new FileError_1.NotFoundError(this.name, fullPath, req.error || ev)); }; const tx = db.transaction([CONTENT_STORE], "readonly"); const range = IDBKeyRange.only(fullPath); tx.onabort = onError; tx.onerror = onError; const request = tx.objectStore(CONTENT_STORE).get(range); request.onerror = onError; const name = this.name; tx.oncomplete = () => { db.close(); if (request.result != null) { resolve(request.result); } else { reject(new FileError_1.NotFoundError(name, fullPath)); } }; })(); }); } async doWriteContent(fullPath, content) { try { const obj = { fullPath: fullPath, name: (0, FileSystemUtil_1.getName)(fullPath), lastModified: Date.now(), size: (0, FileSystemUtil_1.getSize)(content), }; await this.doPutObjectIDB(obj); if (typeof content === "string") { await this.doWriteBase64(fullPath, content); } else if ((0, BinaryConverter_1.isBlob)(content)) { await this.doWriteBlob(fullPath, content); } else if ((0, BinaryConverter_1.isBuffer)(content)) { await this.doWriteBuffer(fullPath, content); } else if (ArrayBuffer.isView(content)) { await this.doWriteUint8Array(fullPath, content); } else { await this.doWriteArrayBuffer(fullPath, content); } } catch (e) { if (e instanceof FileError_1.AbstractFileError) { throw e; } throw new FileError_1.InvalidModificationError(this.name, fullPath, e); } } async open(dbName) { if (IdbAccessor.SUPPORTS_BLOB == null || IdbAccessor.SUPPORTS_ARRAY_BUFFER == null) { await this.initializeDB(); } return new Promise((resolve, reject) => { const onError = (ev) => { const req = ev.target; const db = req.result; db?.close(); reject(`open failure (${req.error || ev}): ${dbName}`); }; const request = indexedDB.open(dbName.replace(":", "_")); request.onerror = onError; request.onblocked = onError; request.onupgradeneeded = (ev) => { const request = ev.target; const db = request.result; if (!db.objectStoreNames.contains(ENTRY_STORE)) { db.createObjectStore(ENTRY_STORE); } if (!db.objectStoreNames.contains(CONTENT_STORE)) { db.createObjectStore(CONTENT_STORE); } }; request.onsuccess = (e) => { const db = e.target.result; resolve(db); }; }); } async purge() { await this.drop(); } async doSaveRecord(indexPath, record) { const text = (0, ObjectUtil_1.objectToText)(record); const u8 = (0, TextConverter_1.textToUint8Array)(text); await this.doWriteContent(indexPath, u8); await this.doPutObjectIDB({ fullPath: indexPath, name: (0, FileSystemUtil_1.getName)(indexPath), lastModified: record.modified, size: (0, FileSystemUtil_1.getSize)(u8), }); } async doWriteArrayBuffer(fullPath, ab) { let content; if (IdbAccessor.SUPPORTS_ARRAY_BUFFER) { content = ab; } else if (IdbAccessor.SUPPORTS_BLOB) { content = (0, BinaryConverter_1.toBlob)(ab); } else { content = await (0, BinaryConverter_1.toBase64)(ab); } await this.doWriteContentToIdb(fullPath, content); } async doWriteBase64(fullPath, base64) { await this.doWriteContentToIdb(fullPath, base64); } async doWriteBlob(fullPath, blob) { let content; if (IdbAccessor.SUPPORTS_BLOB) { content = blob; } else if (IdbAccessor.SUPPORTS_ARRAY_BUFFER) { content = await (0, BinaryConverter_1.toArrayBuffer)(blob); } else { content = await (0, BinaryConverter_1.toBase64)(blob); } await this.doWriteContentToIdb(fullPath, content); } async doWriteBuffer(fullPath, buffer) { const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); await this.doWriteArrayBuffer(fullPath, arrayBuffer); } async doDeleteWithStore(storeName, fullPath) { await new Promise((resolve, reject) => { void (async () => { const db = await this.open(this.dbName); const entryTx = db.transaction([storeName], "readwrite"); const onError = (ev) => { const req = ev.target; db.close(); reject(`doDeleteWithStore failure (${req.error || ev}): ${fullPath} of ${storeName}`); }; entryTx.onabort = onError; entryTx.onerror = onError; entryTx.oncomplete = () => { db.close(); resolve(); }; let range = IDBKeyRange.only(fullPath); const request = entryTx.objectStore(storeName).delete(range); request.onerror = onError; })(); }); } doWriteContentToIdb(fullPath, content) { return new Promise((resolve, reject) => { void (async () => { const db = await this.open(this.dbName); const contentTx = db.transaction([CONTENT_STORE], "readwrite"); const onError = (ev) => { db.close(); reject(new FileError_1.InvalidModificationError(this.name, fullPath, ev)); }; contentTx.onabort = onError; contentTx.onerror = onError; contentTx.oncomplete = () => { db.close(); resolve(); }; const contentReq = contentTx .objectStore(CONTENT_STORE) .put(content, fullPath); contentReq.onerror = onError; })(); }); } drop() { return new Promise((resolve) => { void (async () => { const dbName = this.dbName; const db = await this.open(dbName); const onError = (ev) => { db.close(); console.debug(ev); resolve(); }; const request = indexedDB.deleteDatabase(dbName); request.onblocked = onError; request.onerror = onError; request.onsuccess = () => { db.close(); resolve(); }; })(); }); } async initializeDB() { await new Promise((resolve, reject) => { const dbName = "blob-support"; indexedDB.deleteDatabase(dbName).onsuccess = function () { const request = indexedDB.open(dbName, 1); const onError = (ev) => { const req = ev.target; const db = req.result; db?.close(); reject(ev); }; request.onupgradeneeded = () => request.result.createObjectStore("store"); request.onsuccess = () => { const db = request.result; try { const blob = new Blob(["test"]); const transaction = db.transaction("store", "readwrite"); transaction.objectStore("store").put(blob, "key"); IdbAccessor.SUPPORTS_BLOB = true; } catch (err) { IdbAccessor.SUPPORTS_BLOB = false; } finally { db.close(); indexedDB.deleteDatabase(dbName); } resolve(); }; request.onerror = onError; request.onblocked = onError; }; }); await new Promise((resolve, reject) => { const dbName = "arraybuffer-support"; indexedDB.deleteDatabase(dbName).onsuccess = function () { const request = indexedDB.open(dbName, 1); const onError = (ev) => { const req = ev.target; const db = req.result; db?.close(); reject(ev); }; request.onupgradeneeded = () => request.result.createObjectStore("store"); request.onsuccess = () => { const db = request.result; try { const buffer = new ArrayBuffer(10); const transaction = db.transaction("store", "readwrite"); transaction.objectStore("store").put(buffer, "key"); IdbAccessor.SUPPORTS_ARRAY_BUFFER = true; } catch (err) { IdbAccessor.SUPPORTS_ARRAY_BUFFER = false; } finally { db.close(); indexedDB.deleteDatabase(dbName); } resolve(); }; request.onerror = onError; request.onblocked = onError; }; }); } } exports.IdbAccessor = IdbAccessor; //# sourceMappingURL=IdbAccessor.js.map