|
@@ -1,687 +0,0 @@
|
|
|
-/*! *****************************************************************************
|
|
|
-Copyright (c) Microsoft Corporation.
|
|
|
-
|
|
|
-Permission to use, copy, modify, and/or distribute this software for any
|
|
|
-purpose with or without fee is hereby granted.
|
|
|
-
|
|
|
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
|
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
|
-AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
|
-INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
|
-LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
-OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
-PERFORMANCE OF THIS SOFTWARE.
|
|
|
-***************************************************************************** */
|
|
|
-
|
|
|
-var __assign = function() {
|
|
|
- __assign = Object.assign || function __assign(t) {
|
|
|
- for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
|
- s = arguments[i];
|
|
|
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
|
- }
|
|
|
- return t;
|
|
|
- };
|
|
|
- return __assign.apply(this, arguments);
|
|
|
-};
|
|
|
-
|
|
|
-function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
|
- return new (P || (P = Promise))(function (resolve, reject) {
|
|
|
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
|
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
|
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
|
- step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
-function __generator(thisArg, body) {
|
|
|
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
|
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
|
- function verb(n) { return function (v) { return step([n, v]); }; }
|
|
|
- function step(op) {
|
|
|
- if (f) throw new TypeError("Generator is already executing.");
|
|
|
- while (_) try {
|
|
|
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
|
- if (y = 0, t) op = [op[0] & 2, t.value];
|
|
|
- switch (op[0]) {
|
|
|
- case 0: case 1: t = op; break;
|
|
|
- case 4: _.label++; return { value: op[1], done: false };
|
|
|
- case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
|
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
|
- default:
|
|
|
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
|
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
|
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
|
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
|
- if (t[2]) _.ops.pop();
|
|
|
- _.trys.pop(); continue;
|
|
|
- }
|
|
|
- op = body.call(thisArg, _);
|
|
|
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
|
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// TODO: GLOBAL ERROR HANDLER
|
|
|
-// TODO: optimizing for duplicated codes, make a global promise to handle error
|
|
|
-// TODO: make sure that schema is sync with objectStore
|
|
|
-var GoDBTable = /** @class */ (function () {
|
|
|
- function GoDBTable(godb, name, schema) {
|
|
|
- this.godb = godb;
|
|
|
- this.name = name;
|
|
|
- this.schema = schema || null;
|
|
|
- }
|
|
|
- // crud(operation: GoDBTableCRUD): Promise<void | GoDBData | GoDBData[]> {
|
|
|
- // return new Promise((resolve, reject) => {
|
|
|
- // this.godb.getDB((idb) => {
|
|
|
- // try {
|
|
|
- // } catch (err) {
|
|
|
- // reject(err);
|
|
|
- // }
|
|
|
- // });
|
|
|
- // });
|
|
|
- // }
|
|
|
- // TODO: check if criteria's key fits schema
|
|
|
- GoDBTable.prototype.get = function (criteria) {
|
|
|
- var _this = this;
|
|
|
- return new Promise(function (resolve, reject) {
|
|
|
- _this.godb.getDB(function (idb) {
|
|
|
- try {
|
|
|
- var store = idb
|
|
|
- .transaction(_this.name, 'readonly')
|
|
|
- .objectStore(_this.name);
|
|
|
- // if the key is not unique, return one object with least id
|
|
|
- // TODO: warning user if the key is not unique
|
|
|
- var onSuccess = function (e) {
|
|
|
- var result = e.target.result;
|
|
|
- resolve(result);
|
|
|
- };
|
|
|
- var onError = function (e) {
|
|
|
- var error = e.target.error;
|
|
|
- reject(error);
|
|
|
- };
|
|
|
- if (typeof criteria === 'object') {
|
|
|
- var key = Object.keys(criteria)[0];
|
|
|
- var value = criteria[key];
|
|
|
- var request = key === 'id'
|
|
|
- ? store.get(value)
|
|
|
- : store.index(key).get(value);
|
|
|
- request.onsuccess = onSuccess;
|
|
|
- request.onerror = onError;
|
|
|
- }
|
|
|
- else if (typeof criteria === 'number') {
|
|
|
- var request = store.get(criteria);
|
|
|
- request.onsuccess = onSuccess;
|
|
|
- request.onerror = onError;
|
|
|
- }
|
|
|
- else {
|
|
|
- reject(new Error('Table.get() failed: invalid criteria'));
|
|
|
- }
|
|
|
- }
|
|
|
- catch (err) {
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- };
|
|
|
- GoDBTable.prototype.getAll = function (limit) {
|
|
|
- var _this = this;
|
|
|
- return new Promise(function (resolve, reject) {
|
|
|
- _this.godb.getDB(function (idb) {
|
|
|
- try {
|
|
|
- var store = idb
|
|
|
- .transaction(_this.name, 'readonly')
|
|
|
- .objectStore(_this.name);
|
|
|
- var request = limit
|
|
|
- ? store.getAll(null, limit)
|
|
|
- : store.getAll();
|
|
|
- request.onsuccess = function (e) {
|
|
|
- var result = e.target.result;
|
|
|
- resolve(result);
|
|
|
- };
|
|
|
- request.onerror = function (e) {
|
|
|
- var error = e.target.error;
|
|
|
- reject(error);
|
|
|
- };
|
|
|
- }
|
|
|
- catch (err) {
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- };
|
|
|
- // TODO: check data's schema
|
|
|
- // resolve: id of added item
|
|
|
- GoDBTable.prototype.add = function (data) {
|
|
|
- var _this = this;
|
|
|
- return new Promise(function (resolve, reject) {
|
|
|
- _this.godb.getDB(function (idb) {
|
|
|
- try {
|
|
|
- var store = idb
|
|
|
- .transaction(_this.name, 'readwrite')
|
|
|
- .objectStore(_this.name);
|
|
|
- var request = store.add(data);
|
|
|
- // TODO: according to MDN, `onsuccess` does not exactly mean a successful adding
|
|
|
- request.onsuccess = function (e) {
|
|
|
- var result = e.target.result;
|
|
|
- resolve(__assign(__assign({}, data), { id: result }));
|
|
|
- };
|
|
|
- request.onerror = function (e) {
|
|
|
- var error = e.target.error;
|
|
|
- reject(error);
|
|
|
- };
|
|
|
- }
|
|
|
- catch (err) {
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- };
|
|
|
- // TODO FIX: the order might be unexpected when
|
|
|
- // `addMany` and `add` were executing at the same time
|
|
|
- GoDBTable.prototype.addMany = function (data) {
|
|
|
- var _this = this;
|
|
|
- return new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
|
|
|
- var arr, _i, data_1, item, _a, _b;
|
|
|
- return __generator(this, function (_c) {
|
|
|
- switch (_c.label) {
|
|
|
- case 0:
|
|
|
- if (!Array.isArray(data)) return [3 /*break*/, 5];
|
|
|
- arr = [];
|
|
|
- _i = 0, data_1 = data;
|
|
|
- _c.label = 1;
|
|
|
- case 1:
|
|
|
- if (!(_i < data_1.length)) return [3 /*break*/, 4];
|
|
|
- item = data_1[_i];
|
|
|
- _b = (_a = arr).push;
|
|
|
- return [4 /*yield*/, this.add(item)];
|
|
|
- case 2:
|
|
|
- _b.apply(_a, [_c.sent()]);
|
|
|
- _c.label = 3;
|
|
|
- case 3:
|
|
|
- _i++;
|
|
|
- return [3 /*break*/, 1];
|
|
|
- case 4:
|
|
|
- resolve(arr);
|
|
|
- return [3 /*break*/, 6];
|
|
|
- case 5:
|
|
|
- reject(new Error('Table.addMany() failed: input data should be an array'));
|
|
|
- _c.label = 6;
|
|
|
- case 6: return [2 /*return*/];
|
|
|
- }
|
|
|
- });
|
|
|
- }); });
|
|
|
- };
|
|
|
- // TODO: check data's schema
|
|
|
- // if data is not in table, `put` will add the data, otherwise update
|
|
|
- // TODO: check schema's unique key, which decides whether update or add data
|
|
|
- // resolve: id of updated item
|
|
|
- GoDBTable.prototype.put = function (data) {
|
|
|
- var _this = this;
|
|
|
- return new Promise(function (resolve, reject) {
|
|
|
- _this.godb.getDB(function (idb) {
|
|
|
- if (!(data && typeof data === 'object'))
|
|
|
- return reject(new Error('Table.put() failed: data should be an object'));
|
|
|
- if (!data.id)
|
|
|
- return reject(new Error('Table.put() failed: id is required in data'));
|
|
|
- try {
|
|
|
- var store = idb
|
|
|
- .transaction(_this.name, 'readwrite')
|
|
|
- .objectStore(_this.name);
|
|
|
- // equals to `add(data)` if `data` is not in table
|
|
|
- var request = store.put(data);
|
|
|
- // TODO: according to MDN, `onsuccess` does not exactly mean a successful adding
|
|
|
- request.onsuccess = function (e) {
|
|
|
- var result = e.target.result;
|
|
|
- resolve(__assign(__assign({}, data), { id: result }));
|
|
|
- };
|
|
|
- request.onerror = function (e) {
|
|
|
- var error = e.target.error;
|
|
|
- reject(error);
|
|
|
- };
|
|
|
- }
|
|
|
- catch (err) {
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- };
|
|
|
- // update only, be ware of the difference to `put`
|
|
|
- GoDBTable.prototype.update = function () {
|
|
|
- };
|
|
|
- GoDBTable.prototype["delete"] = function (criteria) {
|
|
|
- var _this = this;
|
|
|
- return new Promise(function (resolve, reject) {
|
|
|
- _this.godb.getDB(function (idb) {
|
|
|
- try {
|
|
|
- _this.get(criteria).then(function (doc) {
|
|
|
- var store = idb
|
|
|
- .transaction(_this.name, 'readwrite')
|
|
|
- .objectStore(_this.name);
|
|
|
- var request = store["delete"](doc.id);
|
|
|
- request.onsuccess = function () {
|
|
|
- resolve();
|
|
|
- };
|
|
|
- request.onerror = function (e) {
|
|
|
- var error = e.target.error;
|
|
|
- reject(error);
|
|
|
- };
|
|
|
- });
|
|
|
- }
|
|
|
- catch (err) {
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- };
|
|
|
- // find by a function
|
|
|
- GoDBTable.prototype.find = function (fn) {
|
|
|
- var _this = this;
|
|
|
- return new Promise(function (resolve, reject) {
|
|
|
- _this.godb.getDB(function (idb) {
|
|
|
- try {
|
|
|
- var store = idb
|
|
|
- .transaction(_this.name, 'readonly')
|
|
|
- .objectStore(_this.name);
|
|
|
- store.openCursor().onsuccess = function (e) {
|
|
|
- var cursor = e.target.result;
|
|
|
- if (cursor) {
|
|
|
- if (fn(cursor.value))
|
|
|
- return resolve(cursor.value);
|
|
|
- cursor["continue"]();
|
|
|
- }
|
|
|
- else {
|
|
|
- resolve(null);
|
|
|
- }
|
|
|
- };
|
|
|
- }
|
|
|
- catch (err) {
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- };
|
|
|
- // return all results by a find function
|
|
|
- GoDBTable.prototype.findAll = function (fn) {
|
|
|
- var _this = this;
|
|
|
- return new Promise(function (resolve, reject) {
|
|
|
- _this.godb.getDB(function (idb) {
|
|
|
- try {
|
|
|
- var store = idb
|
|
|
- .transaction(_this.name, 'readonly')
|
|
|
- .objectStore(_this.name);
|
|
|
- var data_2 = [];
|
|
|
- store.openCursor().onsuccess = function (e) {
|
|
|
- var cursor = e.target.result;
|
|
|
- if (cursor) {
|
|
|
- if (fn(cursor.value))
|
|
|
- data_2.push(cursor.value);
|
|
|
- cursor["continue"]();
|
|
|
- }
|
|
|
- else {
|
|
|
- resolve(data_2);
|
|
|
- }
|
|
|
- };
|
|
|
- }
|
|
|
- catch (err) {
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- };
|
|
|
- // TODO: people.where('age').below(22).toArray()
|
|
|
- GoDBTable.prototype.where = function () {
|
|
|
- };
|
|
|
- // showing 1000 items maximum in Chrome and Firefox
|
|
|
- GoDBTable.prototype.consoleTable = function (limit) {
|
|
|
- var _this = this;
|
|
|
- if (limit === void 0) { limit = 1000; }
|
|
|
- return new Promise(function (resolve, reject) {
|
|
|
- _this.godb.getDB(function (idb) {
|
|
|
- try {
|
|
|
- if (limit > 1000) {
|
|
|
- console.warn("Table.consoleTable() expects a limit no more than 1000");
|
|
|
- limit = 1000;
|
|
|
- }
|
|
|
- var count_1 = 0;
|
|
|
- var data_3 = {};
|
|
|
- var store = idb
|
|
|
- .transaction(_this.name, 'readonly')
|
|
|
- .objectStore(_this.name);
|
|
|
- store.openCursor().onsuccess = function (e) {
|
|
|
- var cursor = e.target.result;
|
|
|
- if (cursor && count_1 < limit) {
|
|
|
- count_1 += 1;
|
|
|
- data_3[cursor.key] = __assign({}, cursor.value);
|
|
|
- delete data_3[cursor.key].id;
|
|
|
- cursor["continue"]();
|
|
|
- }
|
|
|
- else {
|
|
|
- console.log("Data in Table['" + _this.name + "'] of Database['" + _this.godb.name + "'], limit " + limit + ":");
|
|
|
- console.table(data_3);
|
|
|
- resolve();
|
|
|
- }
|
|
|
- };
|
|
|
- }
|
|
|
- catch (err) {
|
|
|
- reject(err);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- };
|
|
|
- return GoDBTable;
|
|
|
-}());
|
|
|
-
|
|
|
-var global = window;
|
|
|
-var indexedDB = global.indexedDB ||
|
|
|
- global.webkitIndexedDB ||
|
|
|
- global.mozIndexedDB ||
|
|
|
- global.msIndexedDB;
|
|
|
-
|
|
|
-var GoDB = /** @class */ (function () {
|
|
|
- function GoDB(name, schema, config) {
|
|
|
- // init params
|
|
|
- this.version = 0;
|
|
|
- this.tables = {};
|
|
|
- this._callbackQueue = [];
|
|
|
- // save database's name
|
|
|
- this.name = name;
|
|
|
- // settings
|
|
|
- if (config) {
|
|
|
- var version = config.version;
|
|
|
- if (version)
|
|
|
- this.version = version;
|
|
|
- }
|
|
|
- // init tables, `this.idb` is null when init
|
|
|
- this.updateSchema(schema);
|
|
|
- // open connection to the IndexedDB
|
|
|
- this.getDB();
|
|
|
- }
|
|
|
- GoDB.prototype.table = function (table, tableSchema) {
|
|
|
- if (!this.tables[table]) {
|
|
|
- this.tables[table] = new GoDBTable(this, table, tableSchema);
|
|
|
- this.updateSchema();
|
|
|
- }
|
|
|
- else if (tableSchema) {
|
|
|
- this.tables[table] = new GoDBTable(this, table, tableSchema);
|
|
|
- if (this._shouldUpgrade())
|
|
|
- this.updateSchema();
|
|
|
- }
|
|
|
- return this.tables[table];
|
|
|
- };
|
|
|
- GoDB.prototype.updateSchema = function (schema) {
|
|
|
- if (schema) {
|
|
|
- this.tables = {};
|
|
|
- for (var table in schema)
|
|
|
- this.tables[table] = new GoDBTable(this, table, schema[table]);
|
|
|
- }
|
|
|
- if (this.idb) {
|
|
|
- // create new objectStores when database is already opened
|
|
|
- console.log("Updating Schema of Database['" + this.name + "']");
|
|
|
- this.idb.close();
|
|
|
- // activate callbackQueue in getDB()
|
|
|
- // and avoid repeating calling _openDB() before db's opening
|
|
|
- this.idb = null;
|
|
|
- // triger `onupgradeneeded` event in _openDB() to update objectStores
|
|
|
- this._openDB(++this.version, true);
|
|
|
- }
|
|
|
- };
|
|
|
- GoDB.prototype.close = function () {
|
|
|
- if (this.idb) {
|
|
|
- this.idb.close();
|
|
|
- this.idb = null;
|
|
|
- this._connection = null;
|
|
|
- this._closed = true;
|
|
|
- console.log("A connection to Database['" + this.name + "'] is closed");
|
|
|
- }
|
|
|
- else {
|
|
|
- console.warn("Unable to close Database['" + this.name + "']: it is not opened yet");
|
|
|
- }
|
|
|
- };
|
|
|
- // drop a database
|
|
|
- GoDB.prototype.drop = function () {
|
|
|
- var _this = this;
|
|
|
- return new Promise(function (resolve, reject) {
|
|
|
- var database = _this.name;
|
|
|
- _this.close();
|
|
|
- // no need to handle Exception according to MDN
|
|
|
- var deleteRequest = indexedDB.deleteDatabase(database);
|
|
|
- deleteRequest.onsuccess = function (ev) {
|
|
|
- _this.version = 0;
|
|
|
- console.log("Database['" + database + "'] is successfully dropped");
|
|
|
- if (_this.onClosed) {
|
|
|
- if (typeof _this.onClosed === 'function')
|
|
|
- _this.onClosed();
|
|
|
- else
|
|
|
- console.warn("'onClosed' should be a function, not " + typeof _this.onClosed);
|
|
|
- _this.onClosed = null;
|
|
|
- }
|
|
|
- resolve(ev);
|
|
|
- };
|
|
|
- deleteRequest.onerror = function (ev) {
|
|
|
- var error = ev.target.error;
|
|
|
- var name = error.name, message = error.message;
|
|
|
- console.warn("Unable to drop Database['" + database + "'] \n- " + name + ": " + message);
|
|
|
- reject(name + ": " + message);
|
|
|
- };
|
|
|
- });
|
|
|
- };
|
|
|
- GoDB.prototype.getDBState = function () {
|
|
|
- if (this._closed)
|
|
|
- return 'closed';
|
|
|
- if (this.idb)
|
|
|
- return 'opened';
|
|
|
- if (this._connection)
|
|
|
- return 'connecting';
|
|
|
- return 'init';
|
|
|
- };
|
|
|
- /**
|
|
|
- * It is necessary to get `IDBDatabase` instance (`this.idb`) before
|
|
|
- * table operations like table.add(), table.get(), etc.
|
|
|
- * that's why a `getDB` function was needed in this class
|
|
|
- *
|
|
|
- * There are 4 possible states when calling `getDB`:
|
|
|
- *
|
|
|
- * State `closed`: database is closed or dropped by user
|
|
|
- * Operation: warning user in console, not calling callback
|
|
|
- * Where: After invoking `this.close()` or `this.drop()`
|
|
|
- *
|
|
|
- * State `init`: no connection yet (`this.connection` is undefined)
|
|
|
- * Operation: open connection to the IndexedDB database
|
|
|
- * Where: Constructor's `this.getDB()`, only executed once
|
|
|
- *
|
|
|
- * State `connecting`: connection opened, but db is not opened yet (`this.idb` is undefined)
|
|
|
- * Operation: push the table operations to a queue, executing them when db is opened
|
|
|
- * Where: Table operations that are in the same macrotask as `new GoDB()`
|
|
|
- *
|
|
|
- * State `opened`: The database is opened
|
|
|
- * Operation: Invoking the callback synchronously with `this.idb`
|
|
|
- * Where: Table operations when the db is opened,
|
|
|
- * mention that the db's opening only takes a few milliseconds,
|
|
|
- * and normally most of user's table operations will happen after db's opening
|
|
|
- *
|
|
|
- * State sequence:
|
|
|
- * init -> connecting -> opened -> closed
|
|
|
- *
|
|
|
- * Attention: callback is async-called in state `init` and `connecting`,
|
|
|
- * but being sync-called in state `opened`
|
|
|
- */
|
|
|
- GoDB.prototype.getDB = function (callback) {
|
|
|
- // State `opened`: db is opened, calling callback synchronously with IDBDatabase instance
|
|
|
- if (this.idb) {
|
|
|
- if (callback && typeof callback === 'function')
|
|
|
- callback(this.idb);
|
|
|
- return;
|
|
|
- }
|
|
|
- // State `closed`: db is closed
|
|
|
- if (this._closed) {
|
|
|
- console.warn("Database['" + this.name + "'] is closed, operations will not be executed");
|
|
|
- return;
|
|
|
- }
|
|
|
- // State `connecting`: connection is opened, but db is not opened yet
|
|
|
- if (this._connection) {
|
|
|
- if (callback && typeof callback === 'function')
|
|
|
- this._callbackQueue.push(callback);
|
|
|
- return;
|
|
|
- }
|
|
|
- // State `init`: opening connection to IndexedDB
|
|
|
- // In case user has set a version in GoDBConfig
|
|
|
- if (this.version)
|
|
|
- this._openDB(this.version);
|
|
|
- else
|
|
|
- this._openDB();
|
|
|
- };
|
|
|
- GoDB.prototype._openDB = function (version, stopUpgrade) {
|
|
|
- var _this = this;
|
|
|
- var database = this.name;
|
|
|
- var tables = this.tables;
|
|
|
- this._connection = version
|
|
|
- ? indexedDB.open(database, version)
|
|
|
- : indexedDB.open(database);
|
|
|
- this._connection.onsuccess = function (ev) {
|
|
|
- var result = _this._connection.result
|
|
|
- || ev.target.result;
|
|
|
- _this.version = result.version;
|
|
|
- // triggered when 'onupgradeneeded' was not called
|
|
|
- // meaning that the database was already existed in browser
|
|
|
- if (!_this.idb) {
|
|
|
- _this.idb = result;
|
|
|
- console.log("Database['" + database + "'] with version (" + result.version + ") is exisiting");
|
|
|
- }
|
|
|
- // @ts-ignore
|
|
|
- for (var _i = 0, _a = _this.idb.objectStoreNames; _i < _a.length; _i++) {
|
|
|
- var name_1 = _a[_i];
|
|
|
- if (!_this.tables[name_1])
|
|
|
- _this.tables[name_1] = new GoDBTable(_this, name_1);
|
|
|
- }
|
|
|
- // `stopUpgrade` is used to avoid infinite recursion
|
|
|
- if (_this._shouldUpgrade() && !stopUpgrade) {
|
|
|
- // make sure the objectStores structure are matching with the Schema
|
|
|
- _this.updateSchema();
|
|
|
- }
|
|
|
- else {
|
|
|
- console.log("A connection to Database['" + database + "'] is opening");
|
|
|
- // executing Table operations invoked by user at State `connecting`
|
|
|
- if (_this._callbackQueue.length) {
|
|
|
- _this._callbackQueue.forEach(function (fn) { return fn(_this.idb); });
|
|
|
- _this._callbackQueue = [];
|
|
|
- }
|
|
|
- // call onOpened if it is defined by user
|
|
|
- if (_this.onOpened) {
|
|
|
- if (typeof _this.onOpened === 'function')
|
|
|
- _this.onOpened(_this.idb);
|
|
|
- else
|
|
|
- console.warn("'onOpened' should be a function, not " + typeof _this.onOpened);
|
|
|
- _this.onOpened = null;
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
- // called when db version is changing
|
|
|
- // it is called before `onsuccess`
|
|
|
- this._connection.onupgradeneeded = function (ev) {
|
|
|
- var oldVersion = ev.oldVersion, newVersion = ev.newVersion, target = ev.target;
|
|
|
- var transaction = target.transaction;
|
|
|
- // get IndexedDB database instance
|
|
|
- _this.idb = target.result;
|
|
|
- _this.version = newVersion;
|
|
|
- if (oldVersion === 0)
|
|
|
- console.log("Creating Database['" + database + "'] with version (" + newVersion + ")");
|
|
|
- // make sure the IDB objectStores structure are matching with GoDB Tables' Schema
|
|
|
- for (var table in tables)
|
|
|
- _this._updateObjectStore(table, tables[table].schema, transaction);
|
|
|
- if (oldVersion !== 0)
|
|
|
- console.log("Database['" + database + "'] version changed from (" + oldVersion + ") to (" + newVersion + ")");
|
|
|
- };
|
|
|
- this._connection.onerror = function (ev) {
|
|
|
- var error = ev.target.error;
|
|
|
- var name = error.name, message = error.message;
|
|
|
- throw Error("Failed to open Database['" + database + "']"
|
|
|
- + ("\n- " + name + ": " + message));
|
|
|
- };
|
|
|
- this._connection.onblocked = function (ev) {
|
|
|
- var _a = ev, newVersion = _a.newVersion, oldVersion = _a.oldVersion;
|
|
|
- console.warn("Database['" + database + "'] is opening somewhere with version (" + oldVersion + "),", "thus new opening request to version (" + newVersion + ") was blocked.");
|
|
|
- };
|
|
|
- };
|
|
|
- // check if it is needed to update the objectStores in a existing database
|
|
|
- // return true when objectStores structure are not matching with the schema
|
|
|
- // - objectStore (table) is not existing
|
|
|
- // - table schema has one or more index that objectStore do not have
|
|
|
- // - index properties are different from which defined in schema
|
|
|
- GoDB.prototype._shouldUpgrade = function () {
|
|
|
- var idb = this.idb;
|
|
|
- if (!idb)
|
|
|
- return false;
|
|
|
- var tables = this.tables;
|
|
|
- for (var table in tables) {
|
|
|
- // check if objectStores is existing
|
|
|
- if (idb.objectStoreNames.contains(table)) {
|
|
|
- var transaction = idb.transaction(table, 'readonly');
|
|
|
- var _a = transaction.objectStore(table), indexNames = _a.indexNames, keyPath = _a.keyPath, autoIncrement = _a.autoIncrement;
|
|
|
- // if the keyPath is not 'id', or it is not autoIncrement
|
|
|
- if (keyPath !== 'id' || !autoIncrement) {
|
|
|
- this.close();
|
|
|
- throw Error("The current existing objectStore '" + table + "' in IndexedDB '" + this.name + "'"
|
|
|
- + (" has a " + (autoIncrement ? '' : 'non-') + "autoIncrement keyPath `" + keyPath + "`,")
|
|
|
- + " while GoDB requires an autoIncrement keyPath `id`,"
|
|
|
- + (" thus GoDB can not open Database['" + this.name + "']"));
|
|
|
- }
|
|
|
- for (var index in tables[table].schema) {
|
|
|
- // check if the objectStore has the index
|
|
|
- if (indexNames.contains(index)) ;
|
|
|
- else {
|
|
|
- // closing the transaction to avoid db's `blocked` event when upgrading
|
|
|
- transaction.abort();
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- transaction.abort();
|
|
|
- }
|
|
|
- else {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- return false;
|
|
|
- };
|
|
|
- // applying the schema to corresponding IndexedDB objectStores to setup indexes
|
|
|
- // require IDBTransaction `versionchange`
|
|
|
- // it can only be called in `onupgradeneeded`
|
|
|
- GoDB.prototype._updateObjectStore = function (table, schema, transaction) {
|
|
|
- var idb = this.idb;
|
|
|
- if (!idb)
|
|
|
- return;
|
|
|
- var putIndex = function (index, store, update) {
|
|
|
- if (index === 'id')
|
|
|
- return;
|
|
|
- if (update)
|
|
|
- store.deleteIndex(index);
|
|
|
- store.createIndex(index, index, {
|
|
|
- unique: !!schema[index]['unique'],
|
|
|
- multiEntry: !!schema[index]['multiEntry']
|
|
|
- });
|
|
|
- console.log((update ? 'Updating' : 'Creating') + " Index['" + index + "']", "in Table['" + table + "'], Database['" + idb.name + "']");
|
|
|
- };
|
|
|
- if (idb.objectStoreNames.contains(table)) {
|
|
|
- // objectStore is existing, update its indexes
|
|
|
- var store = transaction.objectStore(table);
|
|
|
- var indexNames = store.indexNames;
|
|
|
- // if the objectStore contains an index, update the index
|
|
|
- // if not, create a new index in the objectStore
|
|
|
- for (var index in this.tables[table].schema)
|
|
|
- putIndex(index, store, indexNames.contains(index));
|
|
|
- }
|
|
|
- else {
|
|
|
- // create objectStore if not exist
|
|
|
- var store = idb.createObjectStore(table, {
|
|
|
- keyPath: 'id',
|
|
|
- autoIncrement: true
|
|
|
- });
|
|
|
- console.log("Creating Table['" + table + "'] in Database['" + idb.name + "']");
|
|
|
- for (var index in schema)
|
|
|
- putIndex(index, store);
|
|
|
- }
|
|
|
- };
|
|
|
- return GoDB;
|
|
|
-}());
|
|
|
-
|
|
|
-export default GoDB;
|