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
JavaScript
"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
});