UNPKG

stormflow

Version:

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

2 lines (1 loc) 13.9 kB
"use strict";const e=require("crypto"),t=e=>{if("object"!=typeof e||null===e)return e;if(Array.isArray(e)){const r=new Array(e.length);for(let n=0;n<e.length;n++)r[n]=t(e[n]);return r}{const r={},n=Object.keys(e);for(let o=0;o<n.length;o++)r[n[o]]=t(e[n[o]]);return r}},r=(e,t)=>n(e,"",t),n=(e,t,r)=>{if("object"!=typeof e||null===e)return;const o=Object.keys(e),a=!t;for(let i=0,s=o.length;i<s;i++){const s=o[i],c=e[s],l=a?s:t+"."+s,d=c&&"object"==typeof c;r({key:s,value:c,parent:e,path:l,parentPath:a?null:t,isNode:d}),d&&n(c,l,r)}},o=(e,t)=>{if(!t)return e;e=e||{};const r=t.split(".");let n=e;for(let e=0;e<r.length;e++){if(null==n)return;n=n[r[e]]}return n},a=(e,t,r)=>{const n=t.split(".");let o=e;for(let e=0;e<n.length;e++){const t=n[e],a=t===""+ +t?+t:t;if(["__proto__","constructor"].includes(String(a)))return;if(e===n.length-1)void 0===r?Array.isArray(o)?o.splice(Number(a),1):delete o[a]:o[a]=r;else{const t=n[e+1],r=t===""+ +t;a in o&&null!==o[a]||(o[a]=r?[]:{}),o=o[a]}}},i=Object.freeze(Object.defineProperty({__proto__:null,objClone:t,objPathResolve:o,objPathSet:a,objTraverse:r},Symbol.toStringTag,{value:"Module"})),s=e=>null==e?null===e?"null":"undefined":Array.isArray(e)?"array":e instanceof Date?"date":typeof e,c=Object.freeze(Object.defineProperty({__proto__:null,getType:s},Symbol.toStringTag,{value:"Module"})),l=((e={})=>{if("object"!==s(e))throw new Error("defaultConfig must be an object");let r=t(e),n=t(e);return{getDefaultConfig:()=>t(r),resetConfig(){n=t(r)},getConfig:()=>t(n),setConfig(e){(e=>{const t=Object.keys(r);if("object"!==s(e))throw new Error('Invalid type of options. Expected type is "object"');Object.keys(e).forEach(n=>{if(!t.includes(n)){const e=t.join('", "');throw new Error(`Invalid "${n}" key in options.\r\nAllowed keys: "${e}"`)}const o=s(r[n]);if(s(e[n])!==o)throw new Error(`Invalid type of "${n}" key in options. Expected type is ${o}`)})})(e),Object.assign(n,r,e)},get:e=>n[e],set(e,t){n[e]=t}}})({strict:!0,defaultFields:!0,verbose:!1}),d={},u={storage:null,backup:null,backupIntervalId:null},f=()=>{const e={};for(const r in d)e[r]=Array.from(d[r].values()).map(t);return e},y=async(e,r,n)=>{if(!u.storage||!d[r])return;await new Promise(e=>setTimeout(e,0));const o=Array.from(d[r].values()).map(t);await u.storage[e]({collectionName:r,collectionData:o,changes:n})},p={onInsert:async(e,t=[])=>y("insert",e,t),onUpdate:async(e,t=[])=>y("update",e,t),onDelete:async(e,t=[])=>y("delete",e,t),initCollections:async()=>{if(!u.storage)return;const e=await u.storage.init();if("array"===s(e?.collections))for(const{collectionName:t,collectionData:r}of e.collections)d[t]=new Map(r.map(e=>[e._id,e]))},onBackup:async()=>{u.backup&&await u.backup.backup(f())},stopBackup:()=>{u.backupIntervalId&&(clearInterval(u.backupIntervalId),u.backupIntervalId=null)}},w=()=>e.randomBytes(6).toString("hex"),g=Object.freeze(Object.defineProperty({__proto__:null,uniqueId:w},Symbol.toStringTag,{value:"Module"})),h=()=>Math.floor((new Date).getTime()/1e3),b=e=>Math.floor(new Date(e).getTime()/1e3),m=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],v=["January","February","March","April","May","June","July","August","September","October","November","December"],$=["Su","Mo","Tu","We","Th","Fr","Sa"],_=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],j=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],M=Object.freeze(Object.defineProperty({__proto__:null,timeFromStr:b,timeNow:h,timeToDateObj:e=>new Date(1e3*e),timeToDateStr:(e,t="YYYY-MM-DD HH:mm:ss")=>{const r=(e,t=2)=>String(e).padStart(t,"0"),n=new Date(1e3*e),o=n.getTimezoneOffset(),a=o>0?"-":"+",i=Math.abs(o),s=r(Math.floor(i/60)),c=r(i%60),l=`${a}${s}:${c}`,d=`${a}${s}${c}`,u={YYYY:n.getFullYear(),YY:String(n.getFullYear()).slice(-2),M:n.getMonth()+1,MM:r(n.getMonth()+1),MMM:m[n.getMonth()],MMMM:v[n.getMonth()],D:n.getDate(),DD:r(n.getDate()),d:n.getDay(),dd:$[n.getDay()],ddd:_[n.getDay()],dddd:j[n.getDay()],H:n.getHours(),HH:r(n.getHours()),h:n.getHours()%12||12,hh:r(n.getHours()%12||12),m:n.getMinutes(),mm:r(n.getMinutes()),s:n.getSeconds(),ss:r(n.getSeconds()),SSS:r(n.getMilliseconds(),3),A:n.getHours()<12?"AM":"PM",a:n.getHours()<12?"am":"pm"};return t.replace(/ZZ/g,d).replace(/Z/g,l).replace(/YYYY|YY|MMMM|MMM|MM|M|DD|D|dddd|ddd|dd|d|HH|H|hh|h|mm|m|ss|s|SSS|A|a/g,e=>u[e]??e)}},Symbol.toStringTag,{value:"Module"})),k=new Map,E=["pre","post"],I=["create","read","update","replace","delete"],S=(e,t,r,n)=>{if("array"===s(r))return r.map(r=>S(e,t,r,n));if(o=e,!E.includes(o))throw new Error(`Invalid middleware type: ${e}`);var o;if("string"!==s(t)||"*"===t)throw new Error(`Invalid middleware collection: ${t}`);if(!(e=>"*"===e||I.includes(e))(r))throw new Error(`Invalid middleware method: ${r}`);if("function"!==s(n))throw new Error(`Invalid middleware function: ${n}`);const a=w();return k.set(a,{type:e,collection:t,method:r,fn:n}),a},D=async(e,t,r,n)=>{const o="array"===s(n)?n:[n];await Promise.all(o.map(n=>(async(e,t,r,n)=>{const o=[];for(const n of k.values())n.type!==e||n.collection!==t||"*"!==n.method&&n.method!==r||o.push(n);for(const e of o)await e.fn(n)})(e,t,r,n)))},A=(e,t)=>{const r=e instanceof Map?Array.from(e.values()):e;if("object"!==s(t))return r;const n=Object.keys(t);if(1===n.length&&void 0!==t._id)return[e.get(t._id)];if("array"===s(t.$and))return r.filter(e=>t.$and.every(t=>A([e],t).length));if("array"===s(t.$or)){const e=new Set;for(const n of t.$or)for(const t of A(r,n))e.add(t);return Array.from(e)}if("object"===s(t.$not)){const e=new Set(A(r,t.$not));return r.filter(t=>!e.has(t))}if("array"===s(t.$nor)){const e=new Set;for(const n of t.$nor)for(const t of A(r,n))e.add(t);return r.filter(t=>!e.has(t))}return r.filter(e=>n.every(r=>{const n=t[r],a=((e,t)=>t.includes(".")?o(e,t):e[t])(e,r);if(!["object","array"].includes(s(n)))return a===n;if(void 0!==n.$eq)return a===n.$eq;if(void 0!==n.$ne)return a!==n.$ne;if(void 0!==n.$in)return n.$in.includes(a);if(void 0!==n.$nin)return!n.$nin.includes(a);if(void 0!==n.$lt)return a<n.$lt;if(void 0!==n.$lte)return a<=n.$lte;if(void 0!==n.$gt)return a>n.$gt;if(void 0!==n.$gte)return a>=n.$gte;if(void 0!==n.$regex){return new RegExp(n.$regex,n?.$options).test(a)}}))},O=e=>{if(["object","array"].includes(s(e))){const n=t(e);return r(n,({key:e,value:t,parent:r,path:o})=>{if("object"===s(t)&&t._ref){const{collection:i,id:s}=t._ref,c=d[i];if(delete r[e],c){const e=c.get(s);if(e){const{_ref:t,...r}=O(e);a(n,o,r)}}}}),n}return e},q=["string","number","boolean","date"],C=["type","required","default","unique","$ref"],T=(e,t)=>{const n={};if(void 0!==e){if("object"===s(t))return r(t,({value:t,path:r,parent:i})=>{const c="function"===s(t.default)?t.default():t.default,d=o(e,r),u=void 0===d&&void 0!==c?c:d,f=t.type||s(t),y=s(i),p=s(u),w="date"===f?"number"===p:f===p;if("array"===f&&a(n,r,[]),void 0!==u&&"array"!==y){if(l.get("strict")&&!w){throw new Error(`Invalid type of "${r}" value. Expected type is "${f}".`)}if(t.$ref){const e={_ref:{collection:t?.$ref,id:u?._id}};return void a(n,r.replace(/\.\$ref$/g,""),e)}switch("object"===f&&"object"===p&&a(n,r,{}),"array"===f&&"array"===p&&u.forEach((e,o)=>{const i=[r,o].join("."),{itemValue:s}=T({itemValue:e},{itemValue:t[0]});a(n,i,s)}),t.type){case"string":a(n,r,String(u));break;case"number":a(n,r,Number(u)||0);break;case"boolean":a(n,r,Boolean(u));break;case"date":a(n,r,b(u)||0)}}}),n;if(l.get("strict")){throw new Error('Invalid "object" type of schema definition.')}}else if(l.get("strict")){throw new Error('Invalid "undefined" source object.')}},Y={...g,...i,...c,...M};let H=!1;const x={start:async(e=l.getDefaultConfig(),t=null,r=null)=>{if(H){if(l.get("strict")){throw new Error("Stormflow is already initialized.")}}else H=!0,l.setConfig(e),await(async({storageAdapter:e,backupAdapter:t})=>{if(e&&(u.storage=e),t&&(u.backup=t),await p.initCollections(),t?.init){const e=await t.init();if(e?.backupInterval){const t=60*e.backupInterval*1e3;u.backupIntervalId=setInterval(()=>{u.backup.backup(f())},t)}}})({storageAdapter:t,backupAdapter:r})},getConfig:l.getConfig,setConfig:l.setConfig,Schema:(e={})=>{if("object"!==s(e)||0===Object.keys(e).length){throw new Error('Invalid "object" type of schema definition.')}const n=t(e);return r(n,({key:e,value:t,parent:r,isNode:n})=>{const o=s(t),a=!n&&!r.hasOwnProperty("type");if("function"===o){const n=t.name.toLowerCase();a&&(r[e]={type:n}),"type"===e&&(r[e]=n)}}),r(n,({key:e,value:t,parent:r,isNode:n})=>{if("array"===s(t)&&1!==t.length){throw new Error('Invalid "array" elements in schema definition.')}if(n){if(!Object.keys(t).length){throw new Error("Invalid empty object in schema definition.")}if(t.type&&!q.includes(t.type)){const e=`Invalid "type" key in schema definition: "${t.type}".\r\nAllowed types: "${q.join('", "')}".`;throw new Error(e)}if(void 0!==t.default){const e=s(t.default),r="function"===e?s(t.default()):e;if(void 0!==t.required){throw new Error('Invalid combination of "default" and "required" keys in schema definition.')}if("date"===t.type&&"number"!==r||"date"!==t.type&&t.type!==r){const e=`Invalid type of "default" key in schema definition. Expexted type is "${t.type}".`;throw new Error(e)}}}else{if("$ref"!==e&&!r.type){throw new Error('Missing "type" key in schema definition.')}if(!C.includes(e)){const t=`Invalid key in schema definition: "${e}".\r\nAllowed keys: "${C.join('", "')}".`;throw new Error(t)}}}),n},model:(e="",n={})=>{const i=Object.keys(n).length>0;if(0==/^[a-z0-9_-]+$/.test(e)){throw new Error(`Collection name (${e}) must contain only small caps, numbers, hypens or underscores.`)}if(["__proto__","prototype","constructor"].includes(e)){throw new Error(`Collection name (${e}) must not be "__proto__", "prototype" or "constructor".`)}if(!1===e.endsWith("s")){throw new Error(`Collection name (${e}) must be plural. For example: users, categories, products.`)}d[e]||(d[e]=new Map);const c=async(t,r=!1,n=!1)=>{if("object"!==s(t)){throw new Error("Invalid object type of query.")}n&&await D("pre",e,"find",t);const o=A(d[e],t),a=r?O(o):o;return n&&await D("post",e,"find",a),a},u=async a=>{a="array"===s(a)?a:[a],await D("pre",e,"create",a);const c=a.map(e=>i?T(e,n):e);r(n,({key:e,value:t})=>{if(t.required)for(const t of c){const r=o(t,e)||o(t,e);if(null==r)throw new Error(`"${e}" field is required.`)}});const u=[];r(n,({key:e,value:t})=>{t.unique&&u.push(e)});for(const t of u){const r=c.map(e=>o(e,t)).filter(e=>null!=e);for(const n of r){if(A(d[e],{[t]:n}).length>0)throw new Error(`"${t}" field must be unique.`)}const n=new Set;for(const e of r){if(n.has(e))throw new Error(`"${t}" field must be unique among new items.`);n.add(e)}}const f=c.filter(e=>Object.keys(e).length>0).map(e=>{const t=h();return e._id=w(),e._version=1,l.get("defaultFields")&&(e._created=t,e._updated=t),e});if(f.length>0){for(const r of f)d[e].set(r._id,t(r));p.onInsert(e,f)}return await D("post",e,"create",f),f},f=async(c,u,f)=>{if(c="array"===s(c)?c:[c],"object"!==s(u))throw new Error("Invalid object type of updates.");let y=0;const w=h(),g=f?"replace":"update";await D("pre",e,g,c);const b=i?T(u,n):u;r(n,({key:t,value:r})=>{if(r.unique){const r=[];for(const n of c){const a=o(b,t);if(null==a)continue;if(A(d[e],{[t]:a,_id:{$ne:n._id}}).length>0)throw new Error(`"${t}" field must be unique.`);r.push(a)}const n=new Set;for(const e of r){if(n.has(e))throw new Error(`"${t}" field must be unique.`);n.add(e)}}if(r.required)for(const e of c){let r;if(f?r=o(b,t):(r=o(b,t),void 0===r&&(r=o(e,t))),"object"===s(u.$unset)&&u.$unset[t])throw new Error(`"${t}" field is required and cannot be unset.`);if(null==r)throw new Error(`"${t}" field is required.`)}});const m=c.map(e=>{let t=0;if(f){const r={...{_id:e._id,_created:e._created},...b};e=r,t=1}else r(b,({value:r,path:n,isNode:i})=>{i||n.startsWith("$")||o(e,n)!==r&&(t++,a(e,n,r))});if("object"===s(u.$unset)){if(f)throw new Error("Cannot unset fields in replace operation.");for(const r in u.$unset)void 0!==o(e,r)&&(t++,a(e,r,void 0))}return t&&(e._version=(e._version||1)+1,l.get("defaultFields")&&(e._created??=w,e._updated=w),y++),e});if(y>0){for(const r of m)d[e].has(r._id)&&d[e].set(r._id,t(r));p.onUpdate(e,m)}return await D("post",e,g,m),m},y=async t=>{if(t&&t.length>0){await D("pre",e,"delete",t);for(const r of t)r&&r._id&&d[e].delete(r._id);return p.onDelete(e,t),await D("post",e,"delete",t),t}},g=async(e,t,r=!1)=>{const n=await v({_id:e}),o=await f(n,t,r);return O(o[0])},b=async(e,t,r=!1)=>{const n=await v(e),o=await f(n,t,r);return O(o[0])},m=async(e,t,r=!1)=>{const n=await c(e),o=await f(n,t,r);return O(o)},v=async(e,t=!1,r=!1)=>{const n=await c(e,!1,r);return t?O(n[0]):n[0]},$=async(e,t=!1,r=!1)=>{if(!e||"string"!==s(e)){throw new Error("Invalid string type of id.")}const n=await c({_id:e},!1,r);return t?O(n[0]):n[0]},_=async e=>(await c(e)).length,j=async(t,r)=>{await D("pre",e,"read",t);const n=await r();return await D("post",e,"read",n),n};return{pre:(t,r)=>S("pre",e,t,r),post:(t,r)=>S("post",e,t,r),find:async e=>j(e,()=>c(e,!0,!0)),findOne:async e=>j(e,()=>v(e,!0,!0)),findById:async e=>j({_id:e},()=>$(e,!0,!0)),findByIdAndReplace:async(e,t)=>await g(e,t,!0),findByIdAndUpdate:async(e,t)=>await g(e,t),findByIdAndDelete:async e=>{const t=await $(e)||null,r=await y([t]);return O(r[0])},insertOne:async e=>{if("object"!==s(e)){throw new Error("Invalid object type of item.")}const t=await u(e);return O(t[0])},insertMany:async e=>{if("array"!==s(e)){throw new Error("Invalid array type of items.")}const t=await u(e);return O(t)},updateOne:async(e,t)=>await b(e,t),updateMany:async(e,t)=>await m(e,t),replaceOne:async(e,t)=>await b(e,t,!0),replaceMany:async(e,t)=>await m(e,t,!0),deleteOne:async e=>{const t=await v(e)||null,r=await y([t]);return O(r[0])},deleteMany:async e=>{const t=await c(e);if(t&&t.length>0){const e=await y(t);return O(e)}return[]},count:_,exists:async e=>await _(e)>0}},utils:Y};module.exports=x;