UNPKG

react-native-sqlcipher-storage

Version:
427 lines (383 loc) 15.9 kB
var SQLite3JS = (function () { "use strict"; var SQLite3JS, Database, ItemDataSource, GroupDataSource; SQLite3JS = { /// Set this to true to get some more logging output debug: false, logger: { trace: console.log.bind(console), warn: console.warn.bind(console), error: console.error.bind(console), info: console.info.bind(console) } }; function PromiseQueue() { this._items = []; this._busy = false; } PromiseQueue.prototype.append = function (createPromise) { var wrappingPromise, queueItem = { createPromise: createPromise }, _this = this; wrappingPromise = new WinJS.Promise(function (complete, error) { queueItem.complete = complete; queueItem.error = error; }, function () { if (queueItem.promise) { queueItem.promise.cancel(); } else { queueItem.cancelled = true; } _this._handleNext(); }); this._items.push(queueItem); if (!this._busy) { this._handleNext(); } return wrappingPromise; }; PromiseQueue.prototype._handleNext = function () { var _this = this; /* shorten call stack */ this._busy = true; setImmediate(function () { if (_this._items.length > 0) { var nextItem = _this._items[0]; _this._items = _this._items.slice(1); _this._handleItem(nextItem); } else { _this._busy = false; } }); }; PromiseQueue.prototype._handleItem = function (queueItem) { var _this = this; if (!queueItem.cancelled) { queueItem.promise = queueItem.createPromise(); queueItem.promise.done(function (result) { _this._handleNext(); queueItem.complete(result); }, function (error) { _this._handleNext(); queueItem.error(error); }); } else { this._handleNext(); } }; function toPropertySet(object) { var key, propertySet = new Windows.Foundation.Collections.PropertySet(); for (key in object) { if (object.hasOwnProperty(key)) { propertySet.insert(key, object[key]); } } return propertySet; } function prepareArgs(args) { args = args || []; return (args instanceof Array) ? args : toPropertySet(args); } function formatStatementAndArgs(sql, args) { var argString = args ? ' ' + args.toString() : ''; return '"' + sql + '", ' + argString; } function wrapException(exception, detailedMessage, functionName, sql, args) { var error, message, resultCode, number; if (exception.hasOwnProperty('number')) { // Convert the COM error to an unsigned hex value that we can check in JS like E_FAIL == 0x80004005 number = 0xffffffff + exception.number + 1; resultCode = number & 0x20000000 ? exception.number & 0xffff : 0; message = (resultCode > 0 ? resultCode : "0x" + number.toString(16)) + ": "; if (functionName) { message += functionName; if (sql) { message += '(' + formatStatementAndArgs(sql, args) + ') '; } else { message += " "; } } if (detailedMessage) { message += detailedMessage; } error = new WinJS.ErrorFromName("SQLiteError", message); error.resultCode = resultCode; error.number = number; error.sql = sql; error.args = args; error.functionName = functionName; } else { error = exception; } return WinJS.Promise.wrapError(error); } function wrapDatabase(connection) { var that, queue = new PromiseQueue(); function callNativeAsync(funcName, sql, args, callback) { var argString, preparedArgs, fullFuncName; return queue.append(function () { if (SQLite3JS.debug) { SQLite3JS.logger.trace(funcName + ': ' + formatStatementAndArgs(sql, args)); } preparedArgs = prepareArgs(args); fullFuncName = preparedArgs instanceof Windows.Foundation.Collections.PropertySet ? funcName + "Map" : funcName + "Vector"; return connection[fullFuncName](sql, preparedArgs, callback).then(null, function (error) { return wrapException(error, that.lastError, funcName, sql, args); }); }); } that = { runAsync: function (sql, args) { return callNativeAsync('runAsync', sql, args).then(function (affectedRowCount) { return affectedRowCount; }); }, oneAsync: function (sql, args) { return callNativeAsync('oneAsync', sql, args).then(function (row) { return row ? JSON.parse(row) : null; }); }, allAsync: function (sql, args) { return callNativeAsync('allAsync', sql, args).then(function (rows) { return rows ? JSON.parse(rows) : null; }); }, eachAsync: function (sql, args, callback) { if (!callback && typeof args === 'function') { callback = args; args = undefined; } return callNativeAsync('eachAsync', sql, args, function (row) { callback(JSON.parse(row)); }).then(function () { return that; }); }, mapAsync: function (sql, args, callback) { if (!callback && typeof args === 'function') { callback = args; args = undefined; } var results = []; return that.eachAsync(sql, args, function (row) { results.push(callback(row)); }).then(function () { return results; }); }, itemDataSource: function (sql, args, keyColumnName, groupKeyColumnName) { if (typeof args === 'string') { groupKeyColumnName = keyColumnName; keyColumnName = args; args = undefined; } return new ItemDataSource(that, sql, args, keyColumnName, groupKeyColumnName); }, groupDataSource: function (sql, args, keyColumnName, sizeColumnName) { if (typeof args === 'string') { sizeColumnName = keyColumnName; keyColumnName = args; args = undefined; } return new GroupDataSource(that, sql, args, keyColumnName, sizeColumnName); }, close: function () { connection.close(); }, vacuumAsync: function () { return new WinJS.Promise(function (complete) { connection.vacuumAsync(); complete(); }); }, totalChanges: function () { return connection.totalChanges; }, addEventListener: connection.addEventListener.bind(connection), removeEventListener: connection.removeEventListener.bind(connection) }; Object.defineProperties( that, WinJS.Utilities.createEventProperties('update', 'delete', 'insert') ); Object.defineProperties(that, { "collationLanguage": { set: function (value) { connection.collationLanguage = value; }, get: function () { return connection.collationLanguage; }, enumerable: true }, "fireEvents": { set: function (value) { connection.fireEvents = value; }, get: function () { return connection.fireEvents; }, enumerable: true }, "lastError": { get: function () { return connection.lastError; }, enumerable: true }, "autoCommit": { get: function () { return connection.autoCommit; }, enumerable: true }, "lastInsertRowId": { get: function () { return connection.lastInsertRowId; }, enumerable: true } }); return that; } ItemDataSource = WinJS.Class.derive(WinJS.UI.VirtualizedDataSource, function (db, sql, args, keyColumnName, groupKeyColumnName) { this._dataAdapter = { setQuery: function (sql, args) { this._sql = sql; this._args = args; }, getCount: function () { return db.oneAsync('SELECT COUNT(*) AS cnt FROM (' + this._sql + ')', this._args) .then(function (row) { return row.cnt; }); }, itemsFromIndex: function (requestIndex, countBefore, countAfter) { var items, limit = countBefore + 1 + countAfter, offset = requestIndex - countBefore, that = this; return this.getCount().then(function (totalCount) { return db.mapAsync( 'SELECT * FROM (' + that._sql + ') LIMIT ' + limit + ' OFFSET ' + offset, that._args, function (row) { var item = { key: row[keyColumnName].toString(), data: row }; if (groupKeyColumnName) { if (!row.hasOwnProperty(groupKeyColumnName) || row[groupKeyColumnName] === null) { throw "Group key property not found: " + groupKeyColumnName; } item.groupKey = row[groupKeyColumnName].toString(); } return item; }).then(function (items) { return { items: items, offset: countBefore, totalCount: totalCount }; }); }); }, setNotificationHandler: function (notificationHandler) { this._notificationHandler = notificationHandler; }, getNotificationHandler: function () { return this._notificationHandler; } }; this._dataAdapter.setQuery(sql, args); this._baseDataSourceConstructor(this._dataAdapter); }, { setQuery: function (sql, args) { this._dataAdapter.setQuery(sql, args); this.invalidateAll(); }, getNotificationHandler: function () { return this._dataAdapter.getNotificationHandler(); } } ); GroupDataSource = WinJS.Class.derive(WinJS.UI.VirtualizedDataSource, function (db, sql, args, keyColumnName, sizeColumnName) { var dataAdapter = { _keyIndexMap: {}, _ensureGroupsAsync: function () { if (dataAdapter._groups) { return WinJS.Promise.wrap(); } var groupIndex = 0, firstItemIndex = 0; return db.mapAsync(sql, args, function (row) { var item = { key: row[keyColumnName].toString(), groupSize: row[sizeColumnName], firstItemIndexHint: firstItemIndex, data: row }; dataAdapter._keyIndexMap[item.key] = groupIndex; groupIndex += 1; firstItemIndex += item.groupSize; return item; }).then(function (groups) { dataAdapter._groups = groups.filter(function (group) { return group.groupSize > 0; }); }); }, getCount: function () { return dataAdapter._ensureGroupsAsync().then(function () { return dataAdapter._groups.length; }); }, itemsFromIndex: function (requestIndex, countBefore, countAfter) { return dataAdapter._ensureGroupsAsync().then(function () { return { items: dataAdapter._groups.slice(), offset: requestIndex, absoluteIndex: requestIndex, totalCount: dataAdapter._groups.length, atStart: true, atEnd: true }; }); }, itemsFromKey: function (key, countBefore, countAfter) { return dataAdapter._ensureGroupsAsync().then(function () { return dataAdapter.itemsFromIndex(dataAdapter._keyIndexMap[key], countBefore, countAfter); }); }, setNotificationHandler: function (notificationHandler) { this._notificationHandler = notificationHandler; }, getNotificationHandler: function () { return this._notificationHandler; } }; this._dataAdapter = dataAdapter; this._baseDataSourceConstructor(dataAdapter); }, { getNotificationHandler: function () { return this._dataAdapter.getNotificationHandler(); } } ); SQLite3JS.openAsync = function (dbPath, options) { /// <summary> /// Opens a database from disk or in memory. /// </summary> /// <param name="dbPath" type="String"> /// Path to a file that is located in your apps local/temp/roaming storage or the string ":memory:" /// to create a database in memory /// </param> /// <returns>Database object upon completion of the promise</returns> var openWithKey = !!options.key && options.key.length !== 0; var promise = openWithKey ? SQLite3.Database.openAsyncWithKey(dbPath, options.key) : SQLite3.Database.openAsync(dbpath); return promise.then( connection => { var db = wrapDatabase(connection); if (SQLite3JS.version) { return db; } return db.oneAsync("SELECT sqlite_version() || ' (' || sqlite_source_id() || ')' as version") .then( result => { SQLite3JS.logger.info("SQLite3 version: " + (SQLite3JS.version = result.version)); return db; } ); }, error => { return wrapException(error, 'Could not open database "' + dbPath + '"', "openAsync"); }); }; return SQLite3JS; }());