kura
Version:
The FileSystem API abstraction library.
398 lines • 15.6 kB
JavaScript
;
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