substate
Version:
Pub/Sub pattern with State Management
3 lines (2 loc) • 11.7 kB
JavaScript
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).substate={})}(this,function(t){"use strict";function e(t,e,s){const r=e.length;if(0===r)return void 0!==s?(t[""]=s,s):t;let n=!1;for(let t=0;t<r;t++)if("."!==e[t]){n=!0;break}if(!n)return void 0!==s?(t[""]=s,s):t[""];let i=t,a=0;const o=void 0!==s;for(let t=0;t<=r;t++){const n=t<r?e[t]:null,c=t===r;if("."===n||"["===n||c){if("["!==n||a<t){const f=e.slice(a,c?r:t);if("["===n)if(o)null==i[f]&&(i[f]=[]),i=i[f];else{if("object"!=typeof i||null==i)return;if(i=i[f],null==i)return}else{if(c)return o?(i[f]=s,s):"object"==typeof i&&null!=i?i[f]:void 0;if(o){if(null==i[f]){const s=t+1<r?e[t+1]:"";i[f]="["===s?[]:{}}i=i[f]}else{if("object"!=typeof i||null==i)return;if(i=i[f],null==i)return}}}if("["===n){for(t++,a=t;t<r&&"]"!==e[t];)t++;if(t>=r){const t=e.slice(a-1);return o?(i[t]=s,s):void 0}const n=e.slice(a,t),c=parseInt(n,10);if(isNaN(c)||n!==c.toString()){const t=`[${n}]`;return o?(i[t]=s,s):i&&"object"==typeof i?i[t]:void 0}if(t++,t>=r)return o?(Array.isArray(i)||(i=[]),i.length<=c&&(i.length=c+1),i[c]=s,s):Array.isArray(i)?i[c]:void 0;if(o){if(Array.isArray(i)||(i=[]),i.length<=c&&(i.length=c+1),null==i[c]){let s=!1;s=t<r&&"."===e[t]?t+1<r&&"["===e[t+1]:t<r&&"["===e[t],i[c]=s?[]:{}}i=i[c]}else{if(!Array.isArray(i))return;if(i=i[c],null==i)return}t<r&&"."===e[t]&&t++,a=t;continue}"."===n?(t++,a=t):c||(a=t)}}return i}function s(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var r,n;var i=s(function(){if(n)return r;function t(t){return t instanceof Buffer?Buffer.from(t):new t.constructor(t.buffer.slice(),t.byteOffset,t.length)}return n=1,r=function(e){if((e=e||{}).circles)return function(e){const s=[],r=[],n=new Map;if(n.set(Date,t=>new Date(t)),n.set(Map,(t,e)=>new Map(a(Array.from(t),e))),n.set(Set,(t,e)=>new Set(a(Array.from(t),e))),e.constructorHandlers)for(const t of e.constructorHandlers)n.set(t[0],t[1]);let i=null;return e.proto?c:o;function a(e,a){const o=Object.keys(e),c=new Array(o.length);for(let f=0;f<o.length;f++){const u=o[f],h=e[u];if("object"!=typeof h||null===h)c[u]=h;else if(h.constructor!==Object&&(i=n.get(h.constructor)))c[u]=i(h,a);else if(ArrayBuffer.isView(h))c[u]=t(h);else{const t=s.indexOf(h);c[u]=-1!==t?r[t]:a(h)}}return c}function o(e){if("object"!=typeof e||null===e)return e;if(Array.isArray(e))return a(e,o);if(e.constructor!==Object&&(i=n.get(e.constructor)))return i(e,o);const c={};s.push(e),r.push(c);for(const a in e){if(!1===Object.hasOwnProperty.call(e,a))continue;const f=e[a];if("object"!=typeof f||null===f)c[a]=f;else if(f.constructor!==Object&&(i=n.get(f.constructor)))c[a]=i(f,o);else if(ArrayBuffer.isView(f))c[a]=t(f);else{const t=s.indexOf(f);c[a]=-1!==t?r[t]:o(f)}}return s.pop(),r.pop(),c}function c(e){if("object"!=typeof e||null===e)return e;if(Array.isArray(e))return a(e,c);if(e.constructor!==Object&&(i=n.get(e.constructor)))return i(e,c);const o={};s.push(e),r.push(o);for(const a in e){const f=e[a];if("object"!=typeof f||null===f)o[a]=f;else if(f.constructor!==Object&&(i=n.get(f.constructor)))o[a]=i(f,c);else if(ArrayBuffer.isView(f))o[a]=t(f);else{const t=s.indexOf(f);o[a]=-1!==t?r[t]:c(f)}}return s.pop(),r.pop(),o}}(e);const s=new Map;if(s.set(Date,t=>new Date(t)),s.set(Map,(t,e)=>new Map(n(Array.from(t),e))),s.set(Set,(t,e)=>new Set(n(Array.from(t),e))),e.constructorHandlers)for(const t of e.constructorHandlers)s.set(t[0],t[1]);let r=null;return e.proto?function e(i){if("object"!=typeof i||null===i)return i;if(Array.isArray(i))return n(i,e);if(i.constructor!==Object&&(r=s.get(i.constructor)))return r(i,e);const a={};for(const n in i){const o=i[n];"object"!=typeof o||null===o?a[n]=o:o.constructor!==Object&&(r=s.get(o.constructor))?a[n]=r(o,e):ArrayBuffer.isView(o)?a[n]=t(o):a[n]=e(o)}return a}:function e(i){if("object"!=typeof i||null===i)return i;if(Array.isArray(i))return n(i,e);if(i.constructor!==Object&&(r=s.get(i.constructor)))return r(i,e);const a={};for(const n in i){if(!1===Object.hasOwnProperty.call(i,n))continue;const o=i[n];"object"!=typeof o||null===o?a[n]=o:o.constructor!==Object&&(r=s.get(o.constructor))?a[n]=r(o,e):ArrayBuffer.isView(o)?a[n]=t(o):a[n]=e(o)}return a};function n(e,n){const i=Object.keys(e),a=new Array(i.length);for(let o=0;o<i.length;o++){const c=i[o],f=e[c];"object"!=typeof f||null===f?a[c]=f:f.constructor!==Object&&(r=s.get(f.constructor))?a[c]=r(f,n):ArrayBuffer.isView(f)?a[c]=t(f):a[c]=n(f)}return a}}}());const a="STATE_UPDATED",o="STATE_RESET",c="UPDATE_STATE",f="TAG_REMOVED",u="TAG_JUMPED",h="HISTORY_LIMIT_CHANGED",l="HISTORY_CLEARED";class d{events;constructor(){this.events=Object.create(null)}on(t,e){this.events[t]||(this.events[t]=[]),this.events[t].push(e)}off(t,e){const s=this.events[t];if(s){const r=s.indexOf(e);r>-1&&(s.splice(r,1),0===s.length&&delete this.events[t])}}removeAll(){this.events=Object.create(null)}removeAllOf(t){this.events[t]=[]}emit(t,e={}){const s=this.events[t];if(s&&s.length>0)for(const t of s)t(e)}}const g=i();class p extends d{name;afterUpdate;beforeUpdate;currentState;stateStorage;defaultDeep;maxHistorySize;taggedStates;_hasMiddleware;_hasTaggedStates;constructor(t={}){super(),this.name=t.name||"SubStateInstance",this.afterUpdate=t.afterUpdate||[],this.beforeUpdate=t.beforeUpdate||[],this.currentState=t.currentState||0,this.stateStorage=t.stateStorage||[],this.defaultDeep=t.defaultDeep||!1,this.maxHistorySize=t.maxHistorySize||50,this.taggedStates=new Map,this._hasMiddleware=this.beforeUpdate.length>0||this.afterUpdate.length>0,this._hasTaggedStates=!1,t.state&&this.stateStorage.push(t.state),this.on(c,t=>this.updateState(t))}getState(t){return this.stateStorage[t]}getCurrentState(){return this.stateStorage[this.currentState]}getProp(t){const s=this.getCurrentState();return function(t){if(void 0===t)throw new Error("String is undefined");return t.includes(".")||t.includes("[")}(t)?e(s,t):s[t]}resetState(){this.currentState=0,this.stateStorage=[this.stateStorage[0]],this.emit(o)}updateState(t){const e=Object.keys(t),s=this.getCurrentState();if(this.canUseFastPathOptimized(t,e))return void this.fastUpdateStateOptimized(t,s,e);this._hasMiddleware&&this.fireBeforeMiddleware(t);const r=function(t,e){return void 0!==t.$deep?t.$deep:e}(t,this.defaultDeep);let n=this.cloneStateOptimized(r,s);n=this.tempUpdateOptimized(n,t,e,this.defaultDeep),this.pushState(n),this.updateTaggedStates(t,n),this._hasMiddleware&&this.fireAfterMiddleware(t),this.emit(t.$type||a,n)}batchUpdateState(t){if(0!==t.length){if(!this._hasMiddleware&&!this._hasTaggedStates){let e=!0;for(let s=0;s<t.length;s++){const r=t[s];if(r.$deep||void 0!==r.$tag){e=!1;break}const n=Object.keys(r);for(let t=0;t<n.length;t++){const s=n[t];if(s.includes(".")||"$deep"===s||"$type"===s||"$tag"===s){e=!1;break}}if(!e)break}if(e)return void this.fastBatchUpdate(t)}for(let e=0;e<t.length;e++)this.updateState(t[e])}}sync(t){const{readerObj:s,stateField:r,readField:n=r,beforeUpdate:i=[],afterUpdate:o=[]}=t;this.validateSyncFields(r);const c=t=>{const e={source:"substate",field:r,readField:n};let s=t;return i.forEach(t=>{s=t(s,e,this)}),s},f=t=>{const e={source:"substate",field:r,readField:n};o.forEach(s=>{s(t,e,this)})},u=t=>{const i=e(t,r);if(void 0!==i){const t=c(i);e(s,n,t),f(t)}},h=this.getProp(r);if(void 0!==h){const t=c(h);e(s,n,t),f(t)}return this.on(a,u),{unsync:()=>{this.off(a,u)}}}clearHistory(){const t=this.stateStorage.length,e=this.getCurrentState();this.stateStorage=[e],this.currentState=0,this.taggedStates.clear(),this.emit(l,{previousLength:t})}limitHistory(t){if(t<1)throw new Error("History size must be at least 1");const e=this.maxHistorySize;if(this.maxHistorySize=t,this.stateStorage.length>t){const e=this.stateStorage.length-t;this.stateStorage.splice(0,e);for(const[t,s]of this.taggedStates.entries())s.stateIndex<e?this.taggedStates.delete(t):s.stateIndex-=e;this.currentState=this.stateStorage.length-1}this.emit(h,{previousSize:e,newSize:t,currentHistoryLength:this.stateStorage.length})}getMemoryUsage(){const t=this.stateStorage.length;if(0===t)return{stateCount:0,taggedCount:0,estimatedSizeKB:0};let e=0;try{const s=Math.min(3,t);let r=0;for(let e=0;e<s;e++){const n=Math.floor(e*(t-1)/Math.max(1,s-1));r+=2*JSON.stringify(this.stateStorage[n]).length}e=(s>0?r/s:0)*t}catch(t){return{stateCount:this.stateStorage.length,taggedCount:this.taggedStates.size,estimatedSizeKB:null}}const s=Math.max(1,Math.round(e/1024));return{stateCount:t,taggedCount:this.taggedStates.size,estimatedSizeKB:s}}getTaggedState(t){const e=this.taggedStates.get(t);return e?g(e.state):void 0}getAvailableTags(){return Array.from(this.taggedStates.keys())}jumpToTag(t){const e=this.taggedStates.get(t);if(!e)throw new Error(`Tag "${t}" not found`);const s=g(e.state);delete s.$tag,this.pushState(s),this.emit(u,{tag:t,state:this.getCurrentState()}),this.emit(a,this.getCurrentState())}removeTag(t){const e=this.taggedStates.has(t);return this.taggedStates.delete(t),e&&this.emit(f,{tag:t}),e}clearTags(){const t=this.taggedStates.size;this.taggedStates.clear(),this.emit("TAGS_CLEARED",{clearedCount:t})}get hasMiddleware(){return this._hasMiddleware}get hasTaggedStates(){return this._hasTaggedStates}updateTaggedStates(t,e){t.$tag&&(this._hasTaggedStates=!0,this.taggedStates.set(t.$tag,{stateIndex:this.currentState,state:g(e)}))}pushState(t){this.stateStorage.push(t),this.stateStorage.length>this.maxHistorySize&&this.performHistoryTrim(),this.currentState=this.stateStorage.length-1}performHistoryTrim(){if(this.stateStorage.length<=this.maxHistorySize)return;const t=this.stateStorage.length-this.maxHistorySize;this.stateStorage.splice(0,t);for(const[e,s]of this.taggedStates.entries())s.stateIndex<t?this.taggedStates.delete(e):s.stateIndex-=t;this.currentState=this.stateStorage.length-1}fireBeforeMiddleware(t){this.beforeUpdate.length>0&&this.beforeUpdate.forEach(e=>{e(this,t)})}fireAfterMiddleware(t){this.afterUpdate.length>0&&this.afterUpdate.forEach(e=>{e(this,t)})}canUseFastPathOptimized(t,e){if(this._hasMiddleware||this._hasTaggedStates||t.$deep||t.$tag)return!1;for(let t=0;t<e.length;t++){const s=e[t];if(s.includes(".")||s.includes("[")||"$type"===s)return!1}return!0}cloneStateOptimized(t,e){return t?g(e):{...e}}tempUpdateOptimized(t,s,r,n){let i=!1;for(let t=0;t<r.length;t++){const e=r[t];if(e.includes(".")||e.includes("[")){i=!0;break}}if(i){const n=[],i=[];for(let t=0;t<r.length;t++){const e=r[t];e.includes(".")||e.includes("[")?i.push(e):n.push(e)}for(let e=0;e<n.length;e++){const r=n[e];t[r]=s[r]}for(let r=0;r<i.length;r++){const n=i[r];e(t,n,s[n])}}else for(let e=0;e<r.length;e++){const n=r[e];t[n]=s[n]}return n||(t.$deep=!1),t.$type=s.$type||c,t}fastUpdateStateOptimized(t,e,s){const r={...e};for(let e=0;e<s.length;e++){const n=s[e];"$deep"!==n&&"$type"!==n&&"$tag"!==n&&(r[n]=t[n])}r.$type=c,this.pushState(r),this.emit(a,r)}fastBatchUpdate(t){const e={...this.getCurrentState()};for(let s=0;s<t.length;s++){const r=t[s],n=Object.keys(r);for(let t=0;t<n.length;t++){const s=n[t];"$deep"!==s&&"$type"!==s&&"$tag"!==s&&(e[s]=r[s])}}e.$type=c,this.pushState(e),this.emit(a,e)}validateSyncFields(t){if(void 0===this.getProp(t))throw new Error(`State field '${t}' not found in current state. Available state properties: ${Object.keys(this.getCurrentState()).join(", ")}`)}}t.Substate=p,t.createStore=function(t={}){return new p({name:t.name,state:t.state,defaultDeep:t.defaultDeep??!1,beforeUpdate:t.beforeUpdate||[],afterUpdate:t.afterUpdate||[],maxHistorySize:t.maxHistorySize??50})}});
//# sourceMappingURL=index.umd.js.map