mirror of
https://github.com/zero-peak/ZeroOmega.git
synced 2025-01-22 15:08:12 -05:00
213 lines
6.3 KiB
JavaScript
213 lines
6.3 KiB
JavaScript
import FDBObjectStore from "./FDBObjectStore.js";
|
|
import FDBRequest from "./FDBRequest.js";
|
|
import { AbortError, InvalidStateError, NotFoundError, TransactionInactiveError } from "./lib/errors.js";
|
|
import FakeDOMStringList from "./lib/FakeDOMStringList.js";
|
|
import FakeEvent from "./lib/FakeEvent.js";
|
|
import FakeEventTarget from "./lib/FakeEventTarget.js";
|
|
import { queueTask } from "./lib/scheduling.js";
|
|
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#transaction
|
|
class FDBTransaction extends FakeEventTarget {
|
|
_state = "active";
|
|
_started = false;
|
|
_rollbackLog = [];
|
|
_objectStoresCache = new Map();
|
|
error = null;
|
|
onabort = null;
|
|
oncomplete = null;
|
|
onerror = null;
|
|
_requests = [];
|
|
constructor(storeNames, mode, db) {
|
|
super();
|
|
this._scope = new Set(storeNames);
|
|
this.mode = mode;
|
|
this.db = db;
|
|
this.objectStoreNames = new FakeDOMStringList(...Array.from(this._scope).sort());
|
|
}
|
|
|
|
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-aborting-a-transaction
|
|
_abort(errName) {
|
|
for (const f of this._rollbackLog.reverse()) {
|
|
f();
|
|
}
|
|
if (errName !== null) {
|
|
const e = new DOMException(undefined, errName);
|
|
this.error = e;
|
|
}
|
|
|
|
// Should this directly remove from _requests?
|
|
for (const {
|
|
request
|
|
} of this._requests) {
|
|
if (request.readyState !== "done") {
|
|
request.readyState = "done"; // This will cancel execution of this request's operation
|
|
if (request.source) {
|
|
request.result = undefined;
|
|
request.error = new AbortError();
|
|
const event = new FakeEvent("error", {
|
|
bubbles: true,
|
|
cancelable: true
|
|
});
|
|
event.eventPath = [this.db, this];
|
|
request.dispatchEvent(event);
|
|
}
|
|
}
|
|
}
|
|
queueTask(() => {
|
|
const event = new FakeEvent("abort", {
|
|
bubbles: true,
|
|
cancelable: false
|
|
});
|
|
event.eventPath = [this.db];
|
|
this.dispatchEvent(event);
|
|
});
|
|
this._state = "finished";
|
|
}
|
|
abort() {
|
|
if (this._state === "committing" || this._state === "finished") {
|
|
throw new InvalidStateError();
|
|
}
|
|
this._state = "active";
|
|
this._abort(null);
|
|
}
|
|
|
|
// http://w3c.github.io/IndexedDB/#dom-idbtransaction-objectstore
|
|
objectStore(name) {
|
|
if (this._state !== "active") {
|
|
throw new InvalidStateError();
|
|
}
|
|
const objectStore = this._objectStoresCache.get(name);
|
|
if (objectStore !== undefined) {
|
|
return objectStore;
|
|
}
|
|
const rawObjectStore = this.db._rawDatabase.rawObjectStores.get(name);
|
|
if (!this._scope.has(name) || rawObjectStore === undefined) {
|
|
throw new NotFoundError();
|
|
}
|
|
const objectStore2 = new FDBObjectStore(this, rawObjectStore);
|
|
this._objectStoresCache.set(name, objectStore2);
|
|
return objectStore2;
|
|
}
|
|
|
|
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-asynchronously-executing-a-request
|
|
_execRequestAsync(obj) {
|
|
const source = obj.source;
|
|
const operation = obj.operation;
|
|
let request = Object.hasOwn(obj, "request") ? obj.request : null;
|
|
if (this._state !== "active") {
|
|
throw new TransactionInactiveError();
|
|
}
|
|
|
|
// Request should only be passed for cursors
|
|
if (!request) {
|
|
if (!source) {
|
|
// Special requests like indexes that just need to run some code
|
|
request = new FDBRequest();
|
|
} else {
|
|
request = new FDBRequest();
|
|
request.source = source;
|
|
request.transaction = source.transaction;
|
|
}
|
|
}
|
|
this._requests.push({
|
|
operation,
|
|
request
|
|
});
|
|
return request;
|
|
}
|
|
_start() {
|
|
this._started = true;
|
|
|
|
// Remove from request queue - cursor ones will be added back if necessary by cursor.continue and such
|
|
let operation;
|
|
let request;
|
|
while (this._requests.length > 0) {
|
|
const r = this._requests.shift();
|
|
|
|
// This should only be false if transaction was aborted
|
|
if (r && r.request.readyState !== "done") {
|
|
request = r.request;
|
|
operation = r.operation;
|
|
break;
|
|
}
|
|
}
|
|
if (request && operation) {
|
|
if (!request.source) {
|
|
// Special requests like indexes that just need to run some code, with error handling already built into
|
|
// operation
|
|
operation();
|
|
} else {
|
|
let defaultAction;
|
|
let event;
|
|
try {
|
|
const result = operation();
|
|
request.readyState = "done";
|
|
request.result = result;
|
|
request.error = undefined;
|
|
|
|
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-fire-a-success-event
|
|
if (this._state === "inactive") {
|
|
this._state = "active";
|
|
}
|
|
event = new FakeEvent("success", {
|
|
bubbles: false,
|
|
cancelable: false
|
|
});
|
|
} catch (err) {
|
|
request.readyState = "done";
|
|
request.result = undefined;
|
|
request.error = err;
|
|
|
|
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-fire-an-error-event
|
|
if (this._state === "inactive") {
|
|
this._state = "active";
|
|
}
|
|
event = new FakeEvent("error", {
|
|
bubbles: true,
|
|
cancelable: true
|
|
});
|
|
defaultAction = this._abort.bind(this, err.name);
|
|
}
|
|
try {
|
|
event.eventPath = [this.db, this];
|
|
request.dispatchEvent(event);
|
|
} catch (err) {
|
|
if (this._state !== "committing") {
|
|
this._abort("AbortError");
|
|
}
|
|
throw err;
|
|
}
|
|
|
|
// Default action of event
|
|
if (!event.canceled) {
|
|
if (defaultAction) {
|
|
defaultAction();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Give it another chance for new handlers to be set before finishing
|
|
queueTask(this._start.bind(this));
|
|
return;
|
|
}
|
|
|
|
// Check if transaction complete event needs to be fired
|
|
if (this._state !== "finished") {
|
|
// Either aborted or committed already
|
|
this._state = "finished";
|
|
if (!this.error) {
|
|
const event = new FakeEvent("complete");
|
|
this.dispatchEvent(event);
|
|
}
|
|
}
|
|
}
|
|
commit() {
|
|
if (this._state !== "active") {
|
|
throw new InvalidStateError();
|
|
}
|
|
this._state = "committing";
|
|
}
|
|
toString() {
|
|
return "[object IDBRequest]";
|
|
}
|
|
}
|
|
export default FDBTransaction; |