@baxriddin-dev/clientdb
Version:
An intuitive library enabling frontend developers to test their applications using a local database.
1 lines • 5.5 kB
JavaScript
class Storage{static set(key,value){window.localStorage.setItem(key,JSON.stringify(value))}static get(key){return JSON.parse(window.localStorage.getItem(key))}static remove(key){window.localStorage.removeItem(key)}}class ID{static unique(){const timestamp=Date.now().toString(36);const randomStrStart=Math.random().toString(36).substring(2,11);const randomStrEnd=Math.random().toString(36).substring(2,11);return`${randomStrStart}-${timestamp}-${randomStrEnd}`}}class ClientDBError extends Error{constructor(message){super(message);this.name="ClientDB Error"}}function isObject(value){return value!==null&&typeof value==="object"&&value.constructor===Object}function isEmptyObject(obj){return Object.keys(obj).length===0}class ClientDB{static createdDatabases={};static instances={};constructor(databaseName){const trimmedName=this._validateName(databaseName,"database");if(ClientDB.instances[trimmedName]){return ClientDB.instances[trimmedName]}ClientDB.createdDatabases[trimmedName]=true;ClientDB.instances[trimmedName]=this;this.database=trimmedName;this.collections=Storage.get(this.database)||{};this._listCollectionMethods()}_save(){Storage.set(this.database,this.collections)}_validateName(name,type,status=null){const trimmedName=name?.toString().trim();if(!trimmedName&&status==="drop"){throw new ClientDBError(`You did not specify the name of the ${type} to delete.`)}if(!trimmedName){throw new ClientDBError(`You did not name the ${type}!`)}return trimmedName}_listCollectionMethods(){Object.keys(this.collections).forEach(key=>{const currentCollection=this.collections[key];const collectionMethods={add:data=>{if(data===undefined)return;if(!isObject(data)){throw new ClientDBError("The data must be of type object")}if(isEmptyObject(data)){throw new ClientDBError("Data must not be empty")}const newData={...data,$id:ID.unique(),$createdAt:Date.now(),$updatedAt:Date.now()};currentCollection.push(newData);this._save();return{success:true,message:"Item added successfully",data:newData}},update:(id,data)=>{if(data===undefined)return;if(!isObject(data)){throw new ClientDBError("The data must be of type object")}const idx=currentCollection.findIndex(item=>item.$id===id);if(idx===-1){return{success:false,message:`No item found with id ${id}`}}const originalData=currentCollection[idx];const updatedData={...originalData,...data,$id:originalData.$id,$createdAt:originalData.$createdAt,$updatedAt:Date.now()};currentCollection[idx]=updatedData;this._save();return{success:true,message:"Item updated successfully",data:{...updatedData}}},delete:id=>{const idx=currentCollection.findIndex(item=>item.$id===id);if(idx!==-1){currentCollection.splice(idx,1);this._save();return{success:true,message:"Item deleted successfully."}}else{return{success:false,message:"No information available for this id."}}},list:(options={})=>{const{id,filter,sort,search,limit,page}=options;let result=currentCollection;if(id){const idx=result.findIndex(item=>item.$id===id);return result[idx]}if(filter){result=result.filter(item=>{return Object.keys(filter).every(key=>{if(typeof filter[key]==="function"){return filter[key](item[key])}else{return item[key]==filter[key]}})})}if(search){result=result.filter(item=>{return Object.keys(search).some(key=>{let itemValue=item[key]!==null&&item[key]!==undefined?String(item[key]).toLowerCase():"";let searchValue=search[key]!==null&&search[key]!==undefined?String(search[key]).toLowerCase():"";return itemValue.includes(searchValue)})})}if(sort){result=result.sort((a,b)=>{for(let key in sort){if(a[key]>b[key])return sort[key]==="asc"?1:-1;if(a[key]<b[key])return sort[key]==="asc"?-1:1}return 0})}if(limit!==undefined&&page!==undefined){const start=(page-1)*limit;const end=page*limit;result=result.slice(start,end)}return[...result]}};if(!Object.prototype.hasOwnProperty.call(this,key)){Object.defineProperty(this,key,{get:()=>collectionMethods,enumerable:false})}})}createCollection(collectionName){if(!this.collections){this.collections={}}const trimmedName=this._validateName(collectionName,"collection");if(!(trimmedName in this.collections)){this.collections[trimmedName]=[];this._save();this._listCollectionMethods()}}dropCollection(collectionName){const trimmedName=this._validateName(collectionName,"collection","drop");if(trimmedName){delete this.collections[trimmedName];this._save();this._listCollectionMethods()}}dropDatabase(){delete ClientDB.createdDatabases[this.database];Storage.remove(this.database)}importData(data){if(!isObject(data)){throw new ClientDBError("The data must be of type object")}Object.keys(data).forEach(collectionName=>{const trimmedName=this._validateName(collectionName,"collection");if(!this.collections[trimmedName]){this.collections[trimmedName]=[]}data[trimmedName].forEach(item=>{if(isObject(item)&&!isEmptyObject(item)){const newData={...item,$id:item.$id||ID.unique(),$createdAt:item.$createdAt||Date.now(),$updatedAt:item.$updatedAt||Date.now()};this.collections[trimmedName].push(newData)}})});this._save();this._listCollectionMethods()}static databaseExists(databaseName){const trimmedName=databaseName?.toString().trim();return!!ClientDB.createdDatabases[trimmedName]}static collectionExists(databaseName,collectionName){const trimmedDatabaseName=databaseName?.toString().trim();const trimmedCollectionName=collectionName?.toString().trim();if(!ClientDB.createdDatabases[trimmedDatabaseName]){return false}const collections=Storage.get(trimmedDatabaseName)||{};return trimmedCollectionName in collections}}