UNPKG

stormflow

Version:

StormFlow is a versatile Node.js data management library designed for efficient data management.

2 lines (1 loc) 3.69 kB
"use strict";const e=require("fs/promises"),t=require("path"),o=require("util"),a=require("zlib"),i=e=>{if("object"!=typeof e||null===e)return e;if(Array.isArray(e)){const t=new Array(e.length);for(let o=0;o<e.length;o++)t[o]=i(e[o]);return t}{const t={},o=Object.keys(e);for(let a=0;a<o.length;a++)t[o[a]]=i(e[o[a]]);return t}},r=e=>null==e?null===e?"null":"undefined":Array.isArray(e)?"array":e instanceof Date?"date":typeof e,n=(e={})=>{if("object"!==r(e))throw new Error("defaultConfig must be an object");let t=i(e),o=i(e);return{getDefaultConfig:()=>i(t),resetConfig(){o=i(t)},getConfig:()=>i(o),setConfig(e){(e=>{const o=Object.keys(t);if("object"!==r(e))throw new Error('Invalid type of options. Expected type is "object"');Object.keys(e).forEach(a=>{if(!o.includes(a)){const e=o.join('", "');throw new Error(`Invalid "${a}" key in options.\r\nAllowed keys: "${e}"`)}const i=r(t[a]);if(r(e[a])!==i)throw new Error(`Invalid type of "${a}" key in options. Expected type is ${i}`)})})(e),Object.assign(o,t,e)},get:e=>o[e],set(e,t){o[e]=t}}},l=o.promisify(a.gzip),s=o.promisify(a.gunzip),c=async t=>{await e.mkdir(t,{recursive:!0})},f=async(t,o=!1)=>{try{await e.unlink(t),o&&console.log(`Deleted file: ${t}`)}catch(e){if("ENOENT"===e.code)return;o&&console.error(`Failed to delete file: ${t}:`,e)}};module.exports=(o={})=>{const a=n({dataFolder:"./data",throttle:100,verbose:!1,...o}),i={},g={},d=async()=>{await c(a.get("dataFolder")),await(async(o,a=!1)=>{await c(o);const i=await e.readdir(o);for(const r of i)if(r.endsWith(".json")){const i=t.join(o,r),n=t.join(o,r.replace(/\.json$/,".sfc"));try{a&&console.log("[FileStorage]",`Converting ${i} to sfc`);const t=await e.readFile(i,"utf8"),o=await l(t);await e.writeFile(n,o),a&&console.log("[FileStorage]",`Converted and saved as ${n}`),await f(i,a)}catch(e){console.error(`[FileStorage] Error converting ${i} to gzip:`,e)}}})(a.get("dataFolder"),a.get("verbose"));const o=(await e.readdir(a.get("dataFolder"))).filter(e=>e.endsWith(".sfc"));o.length>0&&console.log("[FileStorage]","Restoring data from files:");const i=await Promise.all(o.map(async o=>{console.log("[FileStorage]",` → ${o}`);const i=t.join(a.get("dataFolder"),o);return{fileName:o,fileContent:await e.readFile(i)}}));return await(async(e=[])=>{if("array"!==r(e))return;const t=[];for(const{fileName:o,fileContent:a}of e){const e=o.replace(/\.sfc$/,""),i=await s(a),r=JSON.parse(i.toString("utf8"));t.push({collectionName:e,collectionData:r})}return t})(i)},w=async(o,i)=>{try{await c(a.get("dataFolder")),a.get("verbose")&&console.log("[FileStorage]",`Compress data before writing: ${o}`);const{fileName:r,fileContent:n}=await(async(e,t)=>{if(!t)return{collectionName:e};const o=`${e}.sfc`,a=JSON.stringify(t);return{collectionName:e,fileName:o,fileContent:await l(a)}})(o,i),s=t.join(a.get("dataFolder"),`${r}.tmp`),g=t.join(a.get("dataFolder"),r);if(0===i.length)return a.get("verbose")&&console.log("[FileStorage]",`Removing empty file: ${g}`),void await f(g,a.get("verbose"));a.get("verbose")&&console.log("[FileStorage]",`Writing data to temp file: ${s}`),await e.writeFile(s,n),await new Promise(e=>setTimeout(e,50)),a.get("verbose")&&console.log("[FileStorage]",`Move data to final file: ${g}`),await e.rename(s,g),await f(s,a.get("verbose"))}catch(e){console.error(`Failed to write ${o}:`,e)}},y=(e,t)=>{g[e]=t,i[e]||(i[e]=!0,setTimeout(async()=>{const t=g[e];delete g[e],await w(e,t),i[e]=!1,g[e]&&y(e,g[e])},a.get("throttle")))};return{init:async()=>({collections:await d()}),insert:async({collectionName:e,collectionData:t})=>y(e,t),update:async({collectionName:e,collectionData:t})=>y(e,t),delete:async({collectionName:e,collectionData:t})=>y(e,t)}};