kv-storage
Version:
💾 Create data storage that uses a simple key-value method for Node, Browser, Deno, Bun, Cloudflare Workers
356 lines (352 loc) • 14.6 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.KVStorage = factory());
})(this, (function () { 'use strict';
async function KVStorage({ runtime = 'node', databaseName = 'kvstorage', storageName = 'storage', databaseBinding }) {
function isAlphanumeric(str) {
return /^[a-zA-Z0-9]+$/.test(str);
}
function showError(msg = 'Error') {
throw new Error(msg);
}
if (!isAlphanumeric(runtime))
showError('Runtime must be Alphanumeric');
if (!isAlphanumeric(databaseName))
showError('storageName must be Alphanumeric');
if (!isAlphanumeric(storageName))
showError('storageName must be Alphanumeric');
switch (runtime.toLowerCase()) {
case 'node':
if (typeof caches === "undefined" && typeof global !== "undefined" && typeof window === "undefined") {
let runnode;
try {
const url = './kv-storage-module';
runnode = await import(url);
}
catch {
const url = './kv-storage-module.js';
runnode = await import(url);
}
const dbnode = await runnode.KVStorageModule.init({
dataDirName: databaseName,
storageName
});
return dbnode;
}
break;
case 'deno':
if ((typeof caches !== "undefined" && typeof global !== "undefined" && typeof window === "undefined") || (typeof caches !== "undefined" && typeof global === "undefined" && typeof window !== "undefined")) {
let rundeno;
try {
const url = './kv-storage-module.ts';
rundeno = await import(url);
}
catch {
const url = './kv-storage-module.js';
rundeno = await import(url);
}
const dbdeno = await rundeno.KVStorageModule.init({
dataDirName: databaseName,
storageName
});
return dbdeno;
}
break;
case 'bun':
if (typeof caches === "undefined" && typeof global !== "undefined" && typeof window === "undefined") {
let runbun;
try {
const url = './kv-storage-module';
runbun = await import(url);
}
catch {
const url = './kv-storage-module.js';
runbun = await import(url);
}
const dbbun = await runbun.KVStorageModule.init({
dataDirName: databaseName,
storageName
});
return dbbun;
}
break;
case 'browser':
if (typeof window !== "undefined" && typeof window.document !== "undefined") {
if (databaseName === 'kvstorage')
databaseName = storageName;
const dbbrowser = KVStorageBrowser.init({
databaseName,
storageName
});
return dbbrowser;
}
break;
case 'cloudflare':
if (typeof caches !== "undefined" && typeof global === "undefined" && typeof window === "undefined") {
const dbcloudflare = await KVStorageCloudflare.init({
databaseBinding,
storageName
});
return dbcloudflare;
}
break;
default:
showError('Runtime unknown');
}
}
class KVStorageCloudflare {
constructor({ databaseBinding, storageName }) {
this._databaseBinding = databaseBinding;
this._storageName = storageName;
}
isAlphanumeric(str) {
return /^[a-zA-Z0-9]+$/.test(str);
}
showError(msg = 'Error') {
throw new Error(msg);
}
static async init({ databaseBinding, storageName, }) {
function isAlphanumeric(str) {
return /^[a-zA-Z0-9]+$/.test(str);
}
function showError(msg = 'Error') {
throw new Error(msg);
}
if (!isAlphanumeric(storageName))
showError('storageName must be Alphanumeric');
const stmt = databaseBinding.prepare('CREATE TABLE IF NOT EXISTS ' + storageName + ' (key text NOT NULL PRIMARY KEY,value text NOT NULL)');
await stmt.run();
return new KVStorageCloudflare({ databaseBinding, storageName });
}
async put(key, value) {
if (!this.isAlphanumeric(key))
this.showError('Key must be Alphanumeric');
const stmt = this._databaseBinding.prepare('SELECT value FROM ' + this._storageName + ' WHERE key = ?1').bind(key);
const values = await stmt.first();
if (values == null) {
const stmt = this._databaseBinding.prepare('INSERT INTO ' + this._storageName + ' (key,value) VALUES (?1,?2)').bind(key, value);
const values = await stmt.run();
return values.success;
}
else {
const stmt = this._databaseBinding.prepare('UPDATE ' + this._storageName + ' SET value = ?2 WHERE key = ?1').bind(key, value);
const values = await stmt.run();
return values.success;
}
}
async get(key) {
if (!this.isAlphanumeric(key))
this.showError('Key must be Alphanumeric');
const stmt = this._databaseBinding.prepare('SELECT value FROM ' + this._storageName + ' WHERE key = ?1').bind(key);
const values = await stmt.first();
let output;
if (values == null) {
output = false;
}
else {
output = values.value;
}
return output;
}
async delete(key) {
if (!this.isAlphanumeric(key))
this.showError('Key must be Alphanumeric');
const stmt = this._databaseBinding.prepare('DELETE FROM ' + this._storageName + ' WHERE key = ?1').bind(key);
const values = await stmt.run();
return values.success;
}
async has(key) {
if (!this.isAlphanumeric(key))
this.showError('Key must be Alphanumeric');
const stmt = this._databaseBinding.prepare('SELECT value FROM ' + this._storageName + ' WHERE key = ?1').bind(key);
const values = await stmt.run();
let output;
if (values.results == 0) {
output = false;
}
else {
output = true;
}
return output;
}
async list() {
const stmt = this._databaseBinding.prepare('SELECT key FROM ' + this._storageName).bind();
const values = await stmt.all();
let output;
if (values.success) {
let keys = [];
values.results.forEach((obj) => {
keys.push(obj.key);
});
let result = {
keys: keys,
complete: true
};
output = result;
}
else {
output = false;
}
return output;
}
async clear() {
const stmt = this._databaseBinding.prepare('DELETE FROM ' + this._storageName);
const values = await stmt.run();
return values.success;
}
}
class KVStorageBrowser {
constructor({ databaseName, storageName, dbVersion, iDB }) {
this._databaseName = databaseName;
this._storageName = storageName;
this._dbVersion = 1;
this._iDB = iDB;
}
isAlphanumeric(str) {
return /^[a-zA-Z0-9]+$/.test(str);
}
showError(msg = 'Error') {
throw new Error(msg);
}
static async init({ databaseName = "data", storageName, }) {
function isAlphanumeric(str) {
return /^[a-zA-Z0-9]+$/.test(str);
}
function showError(msg = 'Error') {
throw new Error(msg);
}
function indexedDBStuff() {
// Check for IndexedDB support
if (!('indexedDB' in window)) {
// Can't use IndexedDB
showError("This browser doesn't support IndexedDB");
return;
}
}
// Run IndexedDB code
indexedDBStuff();
const dbVersion = 1;
if (!isAlphanumeric(databaseName))
showError('dataDirName must be Alphanumeric');
if (!isAlphanumeric(storageName))
showError('storageName must be Alphanumeric');
const createdb = new Promise((resolve) => {
let request = window.indexedDB.open(databaseName, dbVersion);
request.onerror = function () {
console.error("Error", request.error);
};
request.onsuccess = function () {
let db = request.result;
resolve(db);
};
request.onupgradeneeded = function (event) {
let db = request.result;
if (!db.objectStoreNames.contains(storageName)) {
db.createObjectStore(storageName, { keyPath: 'key' });
}
};
request.onblocked = function () {
console.error("Error, conflict");
};
});
const iDB = await createdb;
return new KVStorageBrowser({ databaseName, storageName, dbVersion, iDB });
}
async put(key, value) {
return new Promise((resolve) => {
if (!this.isAlphanumeric(key))
this.showError('Key must be Alphanumeric');
let transaction = this._iDB.transaction(this._storageName, "readwrite");
let request = transaction.objectStore(this._storageName).put({ key: key, value: value });
request.onsuccess = function () {
resolve(true);
};
request.onerror = function () {
resolve(false);
};
});
}
async get(key) {
return new Promise((resolve) => {
if (!this.isAlphanumeric(key))
this.showError('Key must be Alphanumeric');
let transaction = this._iDB.transaction(this._storageName, "readonly");
let request = transaction.objectStore(this._storageName).get(key);
request.onsuccess = function (event) {
if (request.result != undefined) {
resolve(request.result.value);
}
else {
resolve(false);
}
};
request.onerror = function () {
resolve(false);
};
});
}
async delete(key) {
return new Promise((resolve) => {
if (!this.isAlphanumeric(key))
this.showError('Key must be Alphanumeric');
let transaction = this._iDB.transaction(this._storageName, "readwrite");
let request = transaction.objectStore(this._storageName).delete(key);
request.onsuccess = function (event) {
resolve(true);
};
request.onerror = function () {
resolve(false);
};
});
}
async has(key) {
return new Promise((resolve) => {
if (!this.isAlphanumeric(key))
this.showError('Key must be Alphanumeric');
let transaction = this._iDB.transaction(this._storageName, "readonly");
let request = transaction.objectStore(this._storageName).get(key);
request.onsuccess = function (event) {
if (request.result != undefined) {
resolve(true);
}
else {
resolve(false);
}
};
request.onerror = function () {
resolve(false);
};
});
}
async list() {
return new Promise((resolve) => {
let transaction = this._iDB.transaction(this._storageName, "readonly");
let request = transaction.objectStore(this._storageName).getAllKeys();
request.onsuccess = function (event) {
let result = {
keys: request.result,
complete: true
};
resolve(result);
};
request.onerror = function () {
resolve(false);
};
});
}
async clear() {
return new Promise((resolve) => {
let transaction = this._iDB.transaction(this._storageName, "readwrite");
let request = transaction.objectStore(this._storageName).clear();
request.onsuccess = function (event) {
resolve(true);
};
request.onerror = function () {
resolve(false);
};
});
}
}
return KVStorage;
}));