UNPKG

chex-storage

Version:

A wrapper library for Chrome extension storage local - the standard storage in the chrome extension.

419 lines (411 loc) 11.8 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/extension/index.ts var extension_exports = {}; __export(extension_exports, { ChexTable: () => table_default, default: () => extension_default }); module.exports = __toCommonJS(extension_exports); // src/utils/result.ts function ChexStorageResult(items) { const newArray = new Array(...items); Object.setPrototypeOf(Array.prototype, ChexStorageResult.prototype); ChexStorageResult.prototype.sortBy = function(fieldName) { const field = fieldName.slice(0, -1); const self = this; if (fieldName.indexOf("+") > 0) { return self.sort( (a, b) => a[field] > b[field] ? 1 : -1 ); } return self.sort( (a, b) => a[field] > b[field] ? -1 : 1 ); }; return newArray; } var result_default = ChexStorageResult; // src/utils/function.ts function standardizeCharacter(str) { str = str.replace(/à|á|ạ|ả|ã|â|ầ|ấ|ậ|ẩ|ẫ|ă|ằ|ắ|ặ|ẳ|ẵ/g, "a"); str = str.replace(/è|é|ẹ|ẻ|ẽ|ê|ề|ế|ệ|ể|ễ/g, "e"); str = str.replace(/ì|í|ị|ỉ|ĩ/g, "i"); str = str.replace(/ò|ó|ọ|ỏ|õ|ô|ồ|ố|ộ|ổ|ỗ|ơ|ờ|ớ|ợ|ở|ỡ/g, "o"); str = str.replace(/ù|ú|ụ|ủ|ũ|ư|ừ|ứ|ự|ử|ữ/g, "u"); str = str.replace(/ỳ|ý|ỵ|ỷ|ỹ/g, "y"); str = str.replace(/đ/g, "d"); str = str.replace(/\u0300|\u0301|\u0303|\u0309|\u0323/g, ""); str = str.replace(/\u02C6|\u0306|\u031B/g, ""); str = str.replace(/A|Á|À|Ã|Ạ|Â|Ấ|Ầ|Ẫ|Ậ|Ă|Ắ|Ằ|Ẵ|Ặ/g, "A"); str = str.replace(/à|á|ạ|ả|ã|â|ầ|ấ|ậ|ẩ|ẫ|ă|ằ|ắ|ặ|ẳ|ẵ/g, "a"); str = str.replace(/E|É|È|Ẽ|Ẹ|Ê|Ế|Ề|Ễ|Ệ/, "E"); str = str.replace(/è|é|ẹ|ẻ|ẽ|ê|ề|ế|ệ|ể|ễ/g, "e"); str = str.replace(/I|Í|Ì|Ĩ|Ị/g, "I"); str = str.replace(/ì|í|ị|ỉ|ĩ/g, "i"); str = str.replace(/O|Ó|Ò|Õ|Ọ|Ô|Ố|Ồ|Ỗ|Ộ|Ơ|Ớ|Ờ|Ỡ|Ợ/g, "O"); str = str.replace(/ò|ó|ọ|ỏ|õ|ô|ồ|ố|ộ|ổ|ỗ|ơ|ờ|ớ|ợ|ở|ỡ/g, "o"); str = str.replace(/U|Ú|Ù|Ũ|Ụ|Ư|Ứ|Ừ|Ữ|Ự/g, "U"); str = str.replace(/ù|ú|ụ|ủ|ũ|ư|ừ|ứ|ự|ử|ữ/g, "u"); str = str.replace(/Y|Ý|Ỳ|Ỹ|Ỵ/g, "Y"); str = str.replace(/ỳ|ý|ỵ|ỷ|ỹ/g, "y"); str = str.replace(/Đ/g, "D"); str = str.replace(/đ/g, "d"); str = str.replace(/\u0300|\u0301|\u0303|\u0309|\u0323/g, ""); str = str.replace(/\u02C6|\u0306|\u031B/g, ""); return str; } // src/extension/main/bitap-search.ts function bitapFuzzyBitwiseSearch(text, pattern, k) { if (pattern.length === 0) return text; if (pattern.length > 31) return null; const m = pattern.length; const patternMask = Array.from({ length: 256 }, () => ~0n); const R = Array.from({ length: k + 1 }, () => ~0n); for (let i = 0; i < m; i++) { patternMask[pattern.charCodeAt(i)] &= ~(1n << BigInt(i)); } for (let i = 0; i < text.length; i++) { const charCode = text.charCodeAt(i); let oldRd1 = R[0]; R[0] |= patternMask[charCode]; R[0] <<= 1n; for (let d = 1; d <= k; d++) { const tmp = R[d]; R[d] = (oldRd1 & (R[d] | patternMask[charCode])) << 1n; oldRd1 = tmp; } if ((R[k] & 1n << BigInt(m)) === 0n) { return text.slice(i - m + 1, i + 1); } } return null; } function bitapSearch(text, pattern, approximed = 0.5) { return bitapFuzzyBitwiseSearch( " " + standardizeCharacter(text), standardizeCharacter(pattern), pattern.length - Math.round(pattern.length * approximed) ); } // src/extension/main/_where.ts var ChexStorageWhereMethods = class { #database; #tableName; #keyWhere; constructor(database, tableName, keyWhere) { this.#database = database; this.#tableName = tableName; this.#keyWhere = keyWhere; } /** * Query data where the specified key equals the given value * * @param val * @returns TData[] */ async equals(val) { const tableData = (await chrome.storage.local.get(this.#database))?.[this.#database]?.[this.#tableName] || []; return tableData.filter((row) => row[this.#keyWhere] === val); } /** * Search with fuzzy search algorithm * * @param pattern * @returns TData[] */ async search(pattern) { const tableData = (await chrome.storage.local.get(this.#database))?.[this.#database]?.[this.#tableName] || []; return result_default( tableData.filter( (row) => bitapSearch(row[this.#keyWhere], pattern) ) ); } }; var where_default = ChexStorageWhereMethods; // src/extension/main/table.ts var ChexTable = class { #tableName; #database; #keyName; #key; constructor(database, name, keyName) { this.#database = database; this.#tableName = name; this.#keyName = keyName.split("++").length === 2 ? keyName.split("++")[1] : keyName; this.#key = keyName; } /** * Get data database */ async getDatabase() { const data = await chrome.storage.local.get(this.#database); return data[this.#database]; } /** * Generate key * When add new data, it'll detect auto create or not key * @param data * @returns */ async generateKey(data) { if (this.#key.indexOf("++") === 0) { const db = await this.getDatabase(); const keyName = this.#key.split("++")[1]; const tableData = db[this.#tableName]?.sort( (a, b) => a[keyName] - b[keyName] ) || []; return (tableData?.[tableData.length - 1]?.[keyName] || 0) + 1; } if (!data?.[this.#keyName]) { throw new Error("Must have key in data"); } return data[this.#keyName]; } /** * Querying data with the specified key condition * * @param keyWhere * @returns ChexStorageWhereMethods */ where(keyWhere) { const whereMethod = new where_default( this.#database, this.#tableName, keyWhere ); return whereMethod; } /** * Adds new data to the table and returns the key of the added data. * * @param data * @returns Key value */ async add(data) { const db = await this.getDatabase(); const tableData = db[this.#tableName] || []; const key = await this.generateKey(data); await chrome.storage.local.set({ [this.#database]: { ...db, [this.#tableName]: [ ...tableData, { [this.#keyName]: key, ...data } ] } }); return key; } /** * Adds multiple data entries to the table * * @param data */ async bulkAdd(data) { const db = await this.getDatabase(); const tableData = db[this.#tableName] || []; const addedItems = []; for (const row of data) { const key = await this.generateKey(row); addedItems.push({ [this.#keyName]: key, ...row }); } await chrome.storage.local.set({ [this.#database]: { ...db, [this.#tableName]: [...tableData, ...addedItems] } }); } async get(key) { const db = await this.getDatabase(); const tableData = db?.[this.#tableName] || []; if (!key) { return tableData; } return tableData?.find((row) => row?.[this.#keyName] === key); } /** * Updates data in the table by key and returns the key of the updated data * * @param key * @param change */ async update(keyValue, change) { const db = await this.getDatabase(); const tableData = db[this.#tableName]; const rowNeedUpdate = tableData.find( (row) => row[this.#keyName] === keyValue ); if (!rowNeedUpdate) { throw new Error("Don't find item with key"); } await chrome.storage.local.set({ [this.#database]: { ...db, [this.#tableName]: [ ...tableData.filter((row) => row[this.#keyName] !== keyValue), { ...rowNeedUpdate, ...change } ] } }); return keyValue; } /** * Update data for create new record * * @param keyValue * @param change * @param defaultValue * @returns */ async updateOrCreate(keyValue, change, defaultValue) { const db = await this.getDatabase(); const tableData = db[this.#tableName]; const rowNeedUpdate = tableData?.find( (row) => row[this.#keyName] === keyValue ); if (!rowNeedUpdate) { return await this.add({ [this.#keyName]: keyValue, ...defaultValue }); } await chrome.storage.local.set({ [this.#database]: { ...db, [this.#tableName]: [ ...tableData.filter((row) => row[this.#keyName] !== keyValue), { ...rowNeedUpdate, ...change } ] } }); return keyValue; } /** * Updates all data in the table with the specified changes. * * @param change */ async updateAll(change) { const db = await this.getDatabase(); const tableData = db[this.#tableName]?.map((row) => { return { ...row, ...change }; }); await chrome.storage.local.set({ [this.#database]: { ...db, [this.#tableName]: tableData } }); } /** * Deletes multiple data entries from the table by their keys * * @param keyValue */ async delete(keyValue) { const db = await this.getDatabase(); const tableData = db[this.#tableName]; await chrome.storage.local.set({ [this.#database]: { ...db, [this.#tableName]: tableData.filter( (row) => row[this.#keyName] !== keyValue ) } }); } /** * Delete * * @param keyValues */ async bulkDelete(keyValues) { const db = await this.getDatabase(); const tableData = db[this.#tableName]; await chrome.storage.local.set({ [this.#database]: { ...db, [this.#tableName]: tableData.filter( (row) => !keyValues.includes(row[this.#keyName]) ) } }); } /** * Query data with the same of filter factory function * * @param callback * @returns TData[] */ async filter(callback) { const db = await this.getDatabase(); const tableData = db[this.#tableName]; return tableData.filter(callback); } }; var table_default = ChexTable; // src/extension/index.ts var ChexDatabase = class { constructor(databaseName, initData) { this.databaseName = databaseName; chrome.storage.local.get(databaseName).then((db) => { if (!db || !Object.keys(db).length) { chrome.storage.local.set({ [databaseName]: { ...initData || {} } }); } }); } tables(tables) { const self = this; Object.keys(tables).forEach((table) => { const tableClass = new table_default( self.databaseName, table, tables[table] ); self[table] = tableClass; }); } }; var extension_default = ChexDatabase; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { ChexTable });