UNPKG

@itwin/core-backend

Version:
209 lines • 8.99 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module NativeApp */ Object.defineProperty(exports, "__esModule", { value: true }); exports.NativeAppStorage = void 0; const node_path_1 = require("node:path"); const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); const ECDb_1 = require("./ECDb"); const IModelHost_1 = require("./IModelHost"); const IModelJsFs_1 = require("./IModelJsFs"); const NativeHost_1 = require("./NativeHost"); // cspell:ignore ecdb /** * A local file stored in the [[NativeHost.appSettingsCacheDir]] for storing key/value pairs. * @public */ class NativeAppStorage { _ecdb; id; static _ext = ".settings-db"; static _storages = new Map(); static _init = false; constructor(_ecdb, id) { this._ecdb = _ecdb; this.id = id; } /** Set the value for a key */ setData(key, value) { const rc = this._ecdb.withPreparedSqliteStatement("INSERT INTO app_setting(key,type,val)VALUES(?,?,?) ON CONFLICT(key) DO UPDATE SET type=excluded.type,val=excluded.val", (stmt) => { let valType = (value === undefined || value === null) ? "null" : typeof value; if (valType === "object" && (value instanceof Uint8Array)) valType = "Uint8Array"; switch (valType) { case "null": case "number": case "string": case "boolean": case "Uint8Array": break; default: throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, `Unsupported type ${valType} for value for key='${key}`); } stmt.bindValue(1, key); stmt.bindValue(2, valType); stmt.bindValue(3, value); return stmt.step(); }); if (rc !== core_bentley_1.DbResult.BE_SQLITE_DONE) throw new core_common_1.IModelError(rc, "SQLite error"); this._ecdb.saveChanges(); } /** Get the value for a key from this Storage. If key is not present or is null, return undefined. */ getData(key) { return this._ecdb.withPreparedSqliteStatement("SELECT type,val FROM app_setting WHERE key=?", (stmt) => { stmt.bindValue(1, key); if (core_bentley_1.DbResult.BE_SQLITE_ROW !== stmt.step()) return undefined; const valType = stmt.getValueString(0); switch (valType) { case "number": return stmt.getValueDouble(1); case "string": return stmt.getValueString(1); case "boolean": return Boolean(stmt.getValueInteger(1)); case "Uint8Array": return stmt.getValueBlob(1); case "null": return undefined; } throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, `Unsupported type in cache ${valType}`); }); } /** return the type of the value for a key, or undefined if not present. */ getValueType(key) { return this._ecdb.withSqliteStatement("SELECT type FROM app_setting WHERE key=?", (stmt) => { stmt.bindValue(1, key); return stmt.step() === core_bentley_1.DbResult.BE_SQLITE_ROW ? stmt.getValueString(0) : undefined; }); } /** return `true` if the key is present, but has a null value. */ hasNullValue(key) { return this.getValueType(key) === "null"; } /** Get the value for a key as a string. If it is not present, or not of type string, return undefined */ getString(key) { const val = this.getData(key); return typeof val === "string" ? val : undefined; } /** Get the value for a key as a number. If it is not present, or not of type number, return undefined */ getNumber(key) { const val = this.getData(key); return typeof val === "number" ? val : undefined; } /** Get the value for a key as a boolean. If it is not present, or not of type boolean, return undefined */ getBoolean(key) { const val = this.getData(key); return typeof val === "boolean" ? val : undefined; } /** Get the value for a key as a Uint8Array. If it is not present, or not of type Uint8Array, return undefined */ getUint8Array(key) { const val = this.getData(key); return val instanceof Uint8Array ? val : undefined; } /** Get all key names in this Storage */ getKeys() { const keys = new Array(); this._ecdb.withPreparedSqliteStatement("SELECT key FROM app_setting", (stmt) => { while (core_bentley_1.DbResult.BE_SQLITE_ROW === stmt.step()) { keys.push(stmt.getValueString(0)); } }); return keys; } /** Remove a key/value pair from this Storage */ removeData(key) { const rc = this._ecdb.withPreparedSqliteStatement("DELETE FROM app_setting WHERE key=?", (stmt) => { stmt.bindValue(1, key); return stmt.step(); }); if (rc !== core_bentley_1.DbResult.BE_SQLITE_DONE) { throw new core_common_1.IModelError(rc, "SQLite error"); } } /** Remove all key/value pairs */ removeAll() { const rc = this._ecdb.withPreparedSqliteStatement("DELETE FROM app_setting", (stmt) => { return stmt.step(); }); if (rc !== core_bentley_1.DbResult.BE_SQLITE_DONE) { throw new core_common_1.IModelError(rc, "SQLite error"); } } /** Close this Storage. */ close(deleteFile = false) { const storageFile = (0, node_path_1.join)(NativeHost_1.NativeHost.appSettingsCacheDir, this.id); this._ecdb.saveChanges(); this._ecdb.closeDb(); this._ecdb = undefined; if (deleteFile) IModelJsFs_1.IModelJsFs.removeSync(storageFile); NativeAppStorage._storages.delete(this.id); } static init(ecdb) { return ecdb.withPreparedSqliteStatement("CREATE TABLE app_setting(key PRIMARY KEY,type,val);", (stmt) => { return stmt.step(); }); } /** find and open storage by its name. */ static find(name) { const storage = this._storages.get(name); if (undefined === storage) throw new core_common_1.IModelError(core_bentley_1.IModelStatus.FileNotFound, `Storage ${name} not open`); return storage; } /** Close all opened Storages. * @internal */ static closeAll() { this._storages.forEach((value) => value.close()); this._storages.clear(); } /** @internal */ static getStorageNames() { return IModelJsFs_1.IModelJsFs.readdirSync(NativeHost_1.NativeHost.appSettingsCacheDir).filter((name) => name.endsWith(this._ext)); } /** Open or find a Storage by name. */ static open(name) { if (!this._init) { IModelHost_1.IModelHost.onBeforeShutdown.addOnce(() => this.closeAll()); this._init = true; } if (name.includes("\x00")) throw new Error("Storage name contains illegal characters"); const fileName = name + this._ext; if (!IModelJsFs_1.IModelJsFs.existsSync(NativeHost_1.NativeHost.appSettingsCacheDir)) IModelJsFs_1.IModelJsFs.recursiveMkDirSync(NativeHost_1.NativeHost.appSettingsCacheDir); const storageFile = (0, node_path_1.join)(NativeHost_1.NativeHost.appSettingsCacheDir, fileName); if ((0, node_path_1.relative)(NativeHost_1.NativeHost.appSettingsCacheDir, storageFile).startsWith("..")) throw new Error("Storage name should not be a path"); try { return this.find(fileName); // see if it's already open } catch { const ecdb = new ECDb_1.ECDb(); if (IModelJsFs_1.IModelJsFs.existsSync(storageFile)) { ecdb.openDb(storageFile, ECDb_1.ECDbOpenMode.ReadWrite); } else { ecdb.createDb(storageFile); const rc = this.init(ecdb); if (rc !== core_bentley_1.DbResult.BE_SQLITE_DONE) throw new core_common_1.IModelError(rc, "SQLite error"); ecdb.saveChanges(); } const storage = new NativeAppStorage(ecdb, fileName); this._storages.set(fileName, storage); return storage; } } } exports.NativeAppStorage = NativeAppStorage; //# sourceMappingURL=NativeAppStorage.js.map