react-native-onyx
Version:
State management for React Native
146 lines (145 loc) • 7.52 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const react_native_nitro_sqlite_1 = require("react-native-nitro-sqlite");
const react_native_device_info_1 = require("react-native-device-info");
const utils_1 = __importDefault(require("../../utils"));
// By default, NitroSQLite does not accept nullish values due to current limitations in Nitro Modules.
// This flag enables a feature in NitroSQLite that allows for nullish values to be passed to operations, such as "execute" or "executeBatch".
// Simple null handling can potentially add a minor performance overhead,
// since parameters and results from SQLite queries need to be parsed from and to JavaScript nullish values.
// https://github.com/margelo/react-native-nitro-sqlite#sending-and-receiving-nullish-values
(0, react_native_nitro_sqlite_1.enableSimpleNullHandling)();
const DB_NAME = 'OnyxDB';
let db;
/**
* Prevents the stringifying of the object markers.
*/
function objectMarkRemover(key, value) {
if (key === utils_1.default.ONYX_INTERNALS__REPLACE_OBJECT_MARK)
return undefined;
return value;
}
/**
* Transforms the replace null patches into SQL queries to be passed to JSON_REPLACE.
*/
function generateJSONReplaceSQLQueries(key, patches) {
const queries = patches.map(([pathArray, value]) => {
const jsonPath = `$.${pathArray.join('.')}`;
return [jsonPath, JSON.stringify(value), key];
});
return queries;
}
const provider = {
/**
* The name of the provider that can be printed to the logs
*/
name: 'SQLiteProvider',
/**
* Initializes the storage provider
*/
init() {
db = (0, react_native_nitro_sqlite_1.open)({ name: DB_NAME });
db.execute('CREATE TABLE IF NOT EXISTS keyvaluepairs (record_key TEXT NOT NULL PRIMARY KEY , valueJSON JSON NOT NULL) WITHOUT ROWID;');
// All of the 3 pragmas below were suggested by SQLite team.
// You can find more info about them here: https://www.sqlite.org/pragma.html
db.execute('PRAGMA CACHE_SIZE=-20000;');
db.execute('PRAGMA synchronous=NORMAL;');
db.execute('PRAGMA journal_mode=WAL;');
},
getItem(key) {
return db.executeAsync('SELECT record_key, valueJSON FROM keyvaluepairs WHERE record_key = ?;', [key]).then(({ rows }) => {
if (!rows || (rows === null || rows === void 0 ? void 0 : rows.length) === 0) {
return null;
}
const result = rows === null || rows === void 0 ? void 0 : rows.item(0);
if (result == null) {
return null;
}
return JSON.parse(result.valueJSON);
});
},
multiGet(keys) {
const placeholders = keys.map(() => '?').join(',');
const command = `SELECT record_key, valueJSON FROM keyvaluepairs WHERE record_key IN (${placeholders});`;
return db.executeAsync(command, keys).then(({ rows }) => {
// eslint-disable-next-line no-underscore-dangle
const result = rows === null || rows === void 0 ? void 0 : rows._array.map((row) => [row.record_key, JSON.parse(row.valueJSON)]);
return (result !== null && result !== void 0 ? result : []);
});
},
setItem(key, value) {
return db.executeAsync('REPLACE INTO keyvaluepairs (record_key, valueJSON) VALUES (?, ?);', [key, JSON.stringify(value)]).then(() => undefined);
},
multiSet(pairs) {
const query = 'REPLACE INTO keyvaluepairs (record_key, valueJSON) VALUES (?, json(?));';
const params = pairs.map((pair) => [pair[0], JSON.stringify(pair[1] === undefined ? null : pair[1])]);
if (utils_1.default.isEmptyObject(params)) {
return Promise.resolve();
}
return db.executeBatchAsync([{ query, params }]).then(() => undefined);
},
multiMerge(pairs) {
const commands = [];
// Query to merge the change into the DB value.
const patchQuery = `INSERT INTO keyvaluepairs (record_key, valueJSON)
VALUES (:key, JSON(:value))
ON CONFLICT DO UPDATE
SET valueJSON = JSON_PATCH(valueJSON, JSON(:value));
`;
const patchQueryArguments = [];
// Query to fully replace the nested objects of the DB value.
const replaceQuery = `UPDATE keyvaluepairs
SET valueJSON = JSON_REPLACE(valueJSON, ?, JSON(?))
WHERE record_key = ?;
`;
const replaceQueryArguments = [];
const nonNullishPairs = pairs.filter((pair) => pair[1] !== undefined);
for (const [key, value, replaceNullPatches] of nonNullishPairs) {
const changeWithoutMarkers = JSON.stringify(value, objectMarkRemover);
patchQueryArguments.push([key, changeWithoutMarkers]);
const patches = replaceNullPatches !== null && replaceNullPatches !== void 0 ? replaceNullPatches : [];
if (patches.length > 0) {
const queries = generateJSONReplaceSQLQueries(key, patches);
if (queries.length > 0) {
replaceQueryArguments.push(...queries);
}
}
}
commands.push({ query: patchQuery, params: patchQueryArguments });
if (replaceQueryArguments.length > 0) {
commands.push({ query: replaceQuery, params: replaceQueryArguments });
}
return db.executeBatchAsync(commands).then(() => undefined);
},
mergeItem(key, change, replaceNullPatches) {
// Since Onyx already merged the existing value with the changes, we can just set the value directly.
return this.multiMerge([[key, change, replaceNullPatches]]);
},
getAllKeys: () => db.executeAsync('SELECT record_key FROM keyvaluepairs;').then(({ rows }) => {
// eslint-disable-next-line no-underscore-dangle
const result = rows === null || rows === void 0 ? void 0 : rows._array.map((row) => row.record_key);
return (result !== null && result !== void 0 ? result : []);
}),
removeItem: (key) => db.executeAsync('DELETE FROM keyvaluepairs WHERE record_key = ?;', [key]).then(() => undefined),
removeItems: (keys) => {
const placeholders = keys.map(() => '?').join(',');
const query = `DELETE FROM keyvaluepairs WHERE record_key IN (${placeholders});`;
return db.executeAsync(query, keys).then(() => undefined);
},
clear: () => db.executeAsync('DELETE FROM keyvaluepairs;', []).then(() => undefined),
getDatabaseSize() {
return Promise.all([db.executeAsync('PRAGMA page_size;'), db.executeAsync('PRAGMA page_count;'), (0, react_native_device_info_1.getFreeDiskStorage)()]).then(([pageSizeResult, pageCountResult, bytesRemaining]) => {
var _a, _b, _c, _d, _e, _f;
const pageSize = (_c = (_b = (_a = pageSizeResult.rows) === null || _a === void 0 ? void 0 : _a.item(0)) === null || _b === void 0 ? void 0 : _b.page_size) !== null && _c !== void 0 ? _c : 0;
const pageCount = (_f = (_e = (_d = pageCountResult.rows) === null || _d === void 0 ? void 0 : _d.item(0)) === null || _e === void 0 ? void 0 : _e.page_count) !== null && _f !== void 0 ? _f : 0;
return {
bytesUsed: pageSize * pageCount,
bytesRemaining,
};
});
},
};
exports.default = provider;