varstor 0.6.61 → 0.6.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
1
  /*! For license information please see varstor.min.js.LICENSE.txt */
2
- !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Varstor=t():e.Varstor=t()}(this,()=>(()=>{"use strict";var e={"./src/helpers.js"(e,t,n){n.r(t),n.d(t,{getKeys:()=>l,getParamNames:()=>u,isArray:()=>a,isFunction:()=>i,isObject:()=>o,isString:()=>c,toCamelCase:()=>f});const r=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm,s=/([^\s,]+)/g;function i(e){return"function"==typeof e}function o(e){return"[object Object]"===Object.prototype.toString.call(e)}function c(e){return"string"==typeof e}function a(e){return Array.isArray(e)}function u(e){const t=e.toString().replace(r,"").split("=>")[0],n=t.slice(t.indexOf("(")+1,t.indexOf(")")).match(s);return null===n?[]:n}function l(e,t){return Object.fromEntries(Object.entries(e).map(([e,n])=>[e,n[t]]))}function f(e,t){const n=e.split(t);return`${n[0]}${n.slice(1).reduce((e,t)=>`${e}${t.charAt(0).toUpperCase()}${t.slice(1)}`,"")}`}},"./src/index.js"(e,t,n){n.r(t),n.d(t,{ACCESSORS:()=>a,default:()=>V});var r=n("./src/storage.js"),s=n("./src/helpers.js"),i=n("./src/namespace.js"),o=n("./src/validation.js");const c={},a={},u={},l={},f={GET_TYPE:null,IS_AVAILABE:null,UPDATE_STATE:null,SET_VALUE:null};async function p(e,t,n){const r=f.GET_TYPE(n),s=Object.assign({},t),c=(0,i.namespacify)(e(),t);f.IS_AVAILABE(r)&&await f.UPDATE_STATE(c,r);const u={};for(const n in t)o.isValid.Defining(e(n)),u[e(n)]=d(e(n),c[e(n)],s[n],r);return Object.assign(a,u),w(e())}function d(e,t,n,r){const o=(0,s.isFunction)(t);return o&&function(e,t){const[,n]=(0,i.splitFullKey)(e),r=(0,s.getParamNames)(t).map(e=>(0,i.addNamespace)(n,e));l[e]=r,r.forEach(t=>{u[t]?u[t].push(e):u[t]=[e]})}(e,t),c[e]={value:o?t.apply(null,y(e)):t,computeFn:t,listeners:[],defaultValue:n},function(e,t){const n=()=>c[e].value;return Object.assign(n,{valueOf:()=>c[e].value,toString:()=>c[e].value,set:async n=>await g(e,n,t),onChange:t=>c[e].listeners.push(t),removeListener:t=>c[e].listeners=c[e].listeners.filter(e=>e!==t),reset:()=>g(e,c[e].defaultValue,t)}),new Proxy(n,{get:(t,r)=>Object.keys(n).includes(r)?t[r]:c[e].value[r]})}(e,r)}function y(e){const t=S(e);return l[e].map(e=>t[(0,i.splitFullKey)(e)[0]]).concat(t)}async function g(e,t,n){if(f.IS_AVAILABE(n)){if(await f.SET_VALUE(n,e,t))return}j({[e]:{newValue:t}})}function j(e){const t={};for(const n in e){const r=c[n].value,s=e[n].newValue;r!==s&&(c[n].value=s,t[n]={newValue:s,prevValue:r},m(n,t))}for(const e in t)c[e].listeners.forEach(async n=>await n(c[e].value,S(e),t[e]))}function m(e,t){const n=u[e];n&&n.forEach(e=>{const n=c[e].value,r=c[e].computeFn.apply(null,y(e));n!==r&&(c[e].value=r,t[e]={newValue:r,prevValue:n},m(e,t))})}function E(e,t){return(0,s.isString)(t)?w(t):(0,s.isFunction)(t)?function(e,t){const n=(0,i.getByNamespace)(e,a);return t.call(null,n,w(e()))}(e(""),t):S(e(""))}function S(e){return v((0,s.getKeys)((0,i.getByNamespace)(e,c),"value"))}async function O(e,t){if(!Object.keys(t).length)b();else for(const[n,r]of Object.entries(t))o.isValid.Setting(e(n)),await a[e(n)].set(r);return w(e())}function b(e){return Object.entries(a).filter(([t])=>t.startsWith(e())).forEach((e,{reset:t})=>t()),w(e())}function A(e,t,n){return t.forEach(t=>a[e(t)].onChange(n)),w(e())}function v(e){let t=e;if((0,s.isArray)(e))return t=[],e.forEach(e=>t.push(v(e))),t;if((0,s.isObject)(e)){t={};for(let n in e)t[n]=v(e[n]);return t}return t}function h(){const e=arguments[1];return!e||(0,s.isFunction)(e)||(0,s.isString)(e)?E.apply(null,arguments):Array.isArray(e)?A.apply(null,arguments):(0,s.isObject)(e)?O.apply(null,arguments):void 0}function T(e){Object.assign(f,e)}function w(e){const t=t=>(0,i.addNamespace)(e,t);return Object.assign(h.bind(null,t),{add:e=>p(t,e,!1),addPersistent:e=>p(t,e,!0),get:e=>E(t,e),set:async e=>await O(t,e),resetAll:()=>b(t),onChange:(e,n)=>A(t,e,n),removeListener:(e,n)=>function(e,t,n){return t.forEach(t=>a[e(t)].removeListener(n)),w(e())}(t,e,n),setStorageUtils:T,onStateChange:j})}T(r.default);const V=w("")},"./src/namespace.js"(e,t,n){n.r(t),n.d(t,{addNamespace:()=>i,getByNamespace:()=>c,namespacify:()=>a,splitFullKey:()=>o});var r=n("./src/helpers.js");const s="::";function i(e,t){return`${e}${(0,r.isString)(t)&&s||""}${t||""}`}function o(e){const t=e.split(s);return[t.slice(-1)[0],t.slice(0,-1).join(s)]}function c(e,t){const n=o(e)[1]+s;return Object.fromEntries(Object.entries(t).filter(([e,t])=>e.startsWith(n)).map(([e,t])=>[e.slice(n.length),t]))}function a(e,t){return Object.fromEntries(Object.entries(t).map(([t,n])=>[i(e,t),n]))}},"./src/storage.js"(e,t,n){n.r(t),n.d(t,{default:()=>r});const r={GET_TYPE:function(e){return e&&"localStorage"},IS_AVAILABE:function(e){return e},UPDATE_STATE:function(e){const t={};for(const n in e){const e=localStorage.getItem(n);null!==e&&(t[n]=e)}return Object.assign(e,t)},SET_VALUE:async function(e,t,n){return await window.localStorage.setItem(t,n),!1}}},"./src/validation.js"(e,t,n){n.r(t),n.d(t,{isValid:()=>i});var r=n("./src/index.js"),s=n("./src/namespace.js");const i={Setting:e=>{if(!r.ACCESSORS[e]){const[t,n]=(0,s.splitFullKey)(e);throw new Error(`Setting "${t}" key in "${n}" namespace. DOES NOT EXIST`)}return!0},Defining:e=>{if(r.ACCESSORS[e]){const[t,n]=(0,s.splitFullKey)(e);throw new Error(`Redefining "${t}" key in "${n}" namespace. ALREADY DEFINED`)}return!0}}}};const t={};function n(r){const s=t[r];if(void 0!==s)return s.exports;const i=t[r]={exports:{}};if(!(r in e)){delete t[r];const e=new Error("Cannot find module '"+r+"'");throw e.code="MODULE_NOT_FOUND",e}return e[r](i,i.exports,n),i.exports}n.d=(e,t)=>{if(Array.isArray(t))for(var r=0;r<t.length;){var s=t[r++],i=t[r++];n.o(e,s)?0===i&&r++:0===i?Object.defineProperty(e,s,{enumerable:!0,value:t[r++]}):Object.defineProperty(e,s,{enumerable:!0,get:i})}else for(var s in t)n.o(t,s)&&!n.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};let r=n("./src/index.js");return r=r.default,r})());
2
+ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Varstor=t():e.Varstor=t()}(this,()=>(()=>{"use strict";var e={"./src/actions.ts"(e,t,n){n.r(t),n.d(t,{addStateActions:()=>s});var r=n("./src/index.ts");function s(e,t){const n={get:r.getState.bind(null,e),set:r.setState.bind(null,e),reset:r.resetState.bind(null,e)};for(let[s,c]of Object.entries(t))r.STATE[e(s)]={key:s,namespace:e,value:c.bind(null,n),isAction:!0,listeners:[]}}},"./src/constants.ts"(e,t,n){n.r(t),n.d(t,{NAMESPACE_DELIMITER:()=>r});const r="::"},"./src/helpers.ts"(e,t,n){n.r(t),n.d(t,{filter:()=>p,forEach:()=>d,getParamNames:()=>l,isArray:()=>c,isFunction:()=>i,isObject:()=>a,isString:()=>o,map:()=>f,recreateStructure:()=>S});const r=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm,s=/([^\s,]+)/g;function c(e){return"[object Array]"===u(e)}function o(e){return"[object String]"===u(e)}function i(e){return"[object Function]"===u(e)}function a(e){return"[object Object]"===u(e)}function u(e){return Object.prototype.toString.call(e)}function l(e){const t=e.toString().replace(r,"").split("=>")[0],n=t.slice(t.indexOf("(")+1,t.indexOf(")")).match(s);return null===n?[]:n}function f(e,t){const n=Object.entries(e).map(([e,n])=>t(e,n));return 2===n[0]?.length?Object.fromEntries(n):n}function d(e,t){Object.entries(e||{}).forEach(([e,n])=>t(e,n))}function p(e,t){return Object.fromEntries(Object.entries(e||{}).filter(([e,n])=>!0===t(e,n)))}function S(e){let t=e;if(c(e))return t=[],e.forEach(e=>t.push(S(e))),t;if(a(e)){t={};for(let n in e)t[n]=S(e[n]);return t}return t}},"./src/index.ts"(e,t,n){n.r(t),n.d(t,{STATE:()=>u,createStore:()=>h,default:()=>v,getState:()=>A,getValues:()=>S,resetState:()=>m,setState:()=>T,setupValue:()=>d});var r=n("./src/storage.ts"),s=n("./src/helpers.ts"),c=n("./src/actions.ts"),o=n("./src/constants.ts"),i=n("./src/validation.ts"),a=n("./src/listeners.ts");const u={},l={};async function f(e,t,n){const r=l.GET_TYPE(n),c=Object.assign({},t),o=(0,s.map)(t,(t,n)=>[e(t),n]);l.IS_AVAILABE(r)&&await l.UPDATE_STATE(o,r),a.LISTENERS[e()]||(a.LISTENERS[e()]=[]);for(const n in t){const t=e(n);i.isValid.Defining(t),u[t]=d(n,e,o[t],c[n],r)}return h(e())}function d(e,t,n,r,c){const o=t(e),i=(0,s.isFunction)(n),a=i?(0,s.getParamNames)(n):[];return a.forEach(e=>{const n=t(e);u[n]||(u[n]={dependants:[],dependencies:[],namespace:t,storageType:c}),u[n].dependants?.push(o)}),{key:e,fullKey:o,value:i?n.apply(null,E(a,t)):n,computeFn:i&&n,dependencies:a,dependants:[],listeners:[],defaultValue:r,storageType:c,namespace:t}}function p(e){return(0,s.filter)(u,(t,{key:n})=>t===e(n))}function S(e){return(0,s.map)(p(e),(e,{key:t,value:n})=>[t,n])}function E(e,t){const n=S(t);return e.map(e=>n[e])}async function g(e){const t={};for(const n in e){const r=u[n].value,s=e[n].newValue;r!==s?(t[n]={newValue:s,prevValue:r},u[n].value=s):t[n]={isSame:!0},y(n,e,t)}await(0,a.runStateChangeListeners)((0,s.filter)(t,(e,t)=>!t.isSame))}function y(e,t,n){u[e].dependants?.forEach(e=>{const{dependencies:r,namespace:s}=u[e];if(!function(e,t,n,r){return e.every(e=>{const s=t(e);return n.includes(s)&&r.includes(s)||!n.includes(s)})}(r,s,Object.keys(t),Object.keys(n))||n[e])return;const c=u[e].value,o=u[e].computeFn?.apply(null,E(r,s));c!==o&&(n[e]={newValue:o,prevValue:c},u[e].value=o,y(e,t,n))})}function A(e,t){return(0,s.isString)(t)?h(t):(0,s.recreateStructure)(S(e))}async function T(e,t){const n={};(0,s.forEach)(t,(t,r)=>{const s=e(t),{storageType:c,computeFn:o,isAction:i}=u[s];o||i||(n[c]||(n[c]={}),n[c][s]=r)});for(let[e,t]of Object.entries(n))await b(e,t);return h(e())}async function b(e,t){if(l.IS_AVAILABE(e)){if(await l.SET_VALUES(e,t))return}g((0,s.map)(t,(e,t)=>[e,{newValue:t}]))}async function m(e,t){const n=p(e);return await T(e,(0,s.map)(t?(0,s.filter)(n,(e,{key:n})=>t.includes(n)):n,(e,{key:t,defaultValue:n})=>[t,n])),h(e())}function j(e){return!arguments[1]||(0,s.isString)(arguments[1])?A.apply(null,arguments):(0,s.isArray)(arguments[1])?a.addStateListener.apply(null,arguments):(0,s.isObject)(arguments[1])?T.apply(null,arguments):void 0}function O(e){Object.assign(l,e)}function h(e){const t=t=>function(e,t){return`${e}${(0,s.isString)(t)&&o.NAMESPACE_DELIMITER||""}${t||""}`}(e,t);return Object.assign(j.bind(null,t),{add:e=>f(t,e,!1),addPersistent:e=>f(t,e,!0),get:e=>A(t,e),set:async e=>await T(t,e),reset:e=>m(t,e),onChange:(e,n)=>(0,a.addStateListener)(t,e,n),removeListener:(e,n)=>(0,a.removeStateListener)(t,e,n),actions:e=>(0,c.addStateActions)(t,e),setStorageUtils:O,onStateChange:g})}O(r.default);const v=h("")},"./src/listeners.ts"(e,t,n){n.r(t),n.d(t,{LISTENERS:()=>o,addStateListener:()=>a,removeStateListener:()=>u,runStateChangeListeners:()=>i});var r=n("./src/index.ts"),s=n("./src/constants.ts"),c=n("./src/helpers.ts");const o={};async function i(e){if(Object.keys(e).length){const{namespace:t}=r.STATE[Object.keys(e)[0]],n=(0,r.getValues)(t);for(let[t,s]of Object.entries(e)){const{listeners:e,key:c}=r.STATE[t];for(const t of e)await t([c],n,s)}const i=(0,c.map)(e,e=>e.slice(s.NAMESPACE_DELIMITER.length));for(const r of o[t()])await r(i,n,(0,c.map)(e,(e,t)=>[e.slice(s.NAMESPACE_DELIMITER.length),t]))}}function a(e,t,n){return(0,c.isFunction)(t)?(o[e()].push(t),(0,r.createStore)(e())):(t.forEach(t=>r.STATE[e(t)].listeners?.push(n)),(0,r.createStore)(e()))}function u(e,t,n){if((0,c.isFunction)(t)){const n=o[e()].findIndex(e=>e===t);return o[e()].splice(n,1),(0,r.createStore)(e())}return t.forEach(t=>{const s=r.STATE[e(t)].listeners,c=s?.findIndex(e=>e===n);s?.splice(c,1)}),(0,r.createStore)(e())}},"./src/storage.ts"(e,t,n){n.r(t),n.d(t,{default:()=>r});const r={GET_TYPE:function(e){return e?"localStorage":""},IS_AVAILABE:function(e){return!!e},UPDATE_STATE:async function(e){const t={};for(const n in e){const e=localStorage.getItem(n);null!==e&&(t[n]=e)}return Object.assign(e,t)},SET_VALUES:async function(e,t){for(let[e,n]of Object.entries(t))localStorage.setItem(e,n);return!1}}},"./src/validation.ts"(e,t,n){n.r(t),n.d(t,{isValid:()=>o});var r=n("./src/index.ts"),s=n("./src/constants.ts");function c(e){const t=e.split(s.NAMESPACE_DELIMITER);return[t.slice(-1)[0],t.join(s.NAMESPACE_DELIMITER)]}const o={Setting:e=>{if(!r.STATE[e]){const[t,n]=c(e);throw new Error(`Setting "${t}" key in "${n}" namespace. DOES NOT EXIST`)}return!0},Defining:e=>{if(r.STATE[e]){const[t,n]=c(e);throw new Error(`Redefining "${t}" key in "${n}" namespace. ALREADY DEFINED`)}return!0}}}};const t={};function n(r){const s=t[r];if(void 0!==s)return s.exports;const c=t[r]={exports:{}};if(!(r in e)){delete t[r];const e=new Error("Cannot find module '"+r+"'");throw e.code="MODULE_NOT_FOUND",e}return e[r](c,c.exports,n),c.exports}n.d=(e,t)=>{if(Array.isArray(t))for(var r=0;r<t.length;){var s=t[r++],c=t[r++];n.o(e,s)?0===c&&r++:0===c?Object.defineProperty(e,s,{enumerable:!0,value:t[r++]}):Object.defineProperty(e,s,{enumerable:!0,get:c})}else for(var s in t)n.o(t,s)&&!n.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};let r=n("./src/index.ts");return r=r.default,r})());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "varstor",
3
- "version": "0.6.61",
3
+ "version": "0.6.63",
4
4
  "description": "State manager for web applications and webextensions, employing reactivity, wrapping, and uniting the usage of different storages' values and common variables into a simple universal interface",
5
5
  "license": "MIT",
6
6
  "author": "Igor Morozov <ismorozs@gmail.com>",
@@ -16,7 +16,11 @@
16
16
  "build": "webpack --env production"
17
17
  },
18
18
  "devDependencies": {
19
+ "@types/node": "26.6.3",
20
+ "@types/webpack-env": "1.18.8",
19
21
  "minimizer-webpack-plugin": "5.6.1",
22
+ "ts-loader": "9.6.2",
23
+ "typescript": "6.0.3",
20
24
  "webextension-polyfill": "0.12.0",
21
25
  "webpack": "5.108.4",
22
26
  "webpack-cli": "7.2.1"
package/src/actions.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { STATE, getState, setState, resetState } from ".";
2
+
3
+ export function addStateActions(namespace: INamespace, actions: IStateActions) {
4
+ const stateAcessor = {
5
+ get: getState.bind(null, namespace),
6
+ set: setState.bind(null, namespace),
7
+ reset: resetState.bind(null, namespace),
8
+ };
9
+
10
+ for (let [actionName, actionFn] of Object.entries(actions)) {
11
+ STATE[namespace(actionName)] = {
12
+ key: actionName,
13
+ namespace,
14
+ value: actionFn.bind(null, stateAcessor),
15
+ isAction: true,
16
+ listeners: [],
17
+ };
18
+ }
19
+ }
@@ -0,0 +1 @@
1
+ export const NAMESPACE_DELIMITER = "::";
package/src/helpers.ts ADDED
@@ -0,0 +1,88 @@
1
+ const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
2
+ const ARGUMENT_NAMES = /([^\s,]+)/g;
3
+
4
+ export function isArray(obj: unknown) {
5
+ return getObjectType(obj) === "[object Array]";
6
+ }
7
+
8
+ export function isString(obj: unknown) {
9
+ return getObjectType(obj) === "[object String]";
10
+ }
11
+
12
+ export function isFunction(obj: unknown) {
13
+ return getObjectType(obj) === "[object Function]";
14
+ }
15
+
16
+ export function isObject(obj: unknown) {
17
+ return getObjectType(obj) === "[object Object]";
18
+ }
19
+
20
+ function getObjectType(obj: unknown) {
21
+ return Object.prototype.toString.call(obj);
22
+ }
23
+
24
+ export function getParamNames(fn: () => {}) {
25
+ const fnStr = fn.toString().replace(STRIP_COMMENTS, "").split("=>")[0];
26
+
27
+ const names = fnStr
28
+ .slice(fnStr.indexOf("(") + 1, fnStr.indexOf(")"))
29
+ .match(ARGUMENT_NAMES);
30
+
31
+ if (names === null) {
32
+ return [];
33
+ }
34
+
35
+ return names;
36
+ }
37
+
38
+ export function map<T>(
39
+ obj: Record<string, T>,
40
+ cb: (key: string, value: T) => any,
41
+ ) {
42
+ const res = Object.entries(obj).map(([k, v]) => cb(k, v));
43
+ if (res[0]?.length === 2) {
44
+ return Object.fromEntries(res);
45
+ }
46
+
47
+ return res;
48
+ }
49
+
50
+ export function forEach<T>(
51
+ obj: Record<string, T>,
52
+ cb: (k: string, v: T) => void,
53
+ ) {
54
+ Object.entries(obj || {}).forEach(([k, v]) => cb(k, v));
55
+ }
56
+
57
+ export function filter<T>(
58
+ obj: Record<string, T>,
59
+ cb: (k: string, v: T) => boolean,
60
+ ) {
61
+ return Object.fromEntries(
62
+ Object.entries(obj || {}).filter(([k, v]) => cb(k, v) === true),
63
+ );
64
+ }
65
+
66
+ export function recreateStructure(value: unknown) {
67
+ let newValue = value;
68
+
69
+ if (isArray(value)) {
70
+ newValue = [];
71
+ (value as unknown[]).forEach((v) =>
72
+ (newValue as unknown[]).push(recreateStructure(v)),
73
+ );
74
+ return newValue;
75
+ }
76
+
77
+ if (isObject(value)) {
78
+ newValue = {};
79
+ for (let key in value as Record<string, unknown>) {
80
+ (newValue as Record<string, unknown>)[key] = recreateStructure(
81
+ (value as Record<string, unknown>)[key],
82
+ );
83
+ }
84
+ return newValue;
85
+ }
86
+
87
+ return newValue;
88
+ }
package/src/index.ts ADDED
@@ -0,0 +1,314 @@
1
+ import storageUtils from "./storage";
2
+
3
+ import {
4
+ isFunction,
5
+ isArray,
6
+ isObject,
7
+ isString,
8
+ getParamNames,
9
+ map,
10
+ filter,
11
+ recreateStructure,
12
+ forEach,
13
+ } from "./helpers";
14
+
15
+ import { addStateActions } from "./actions";
16
+
17
+ import { NAMESPACE_DELIMITER } from "./constants";
18
+
19
+ import { isValid } from "./validation";
20
+ import {
21
+ runStateChangeListeners,
22
+ addStateListener,
23
+ removeStateListener,
24
+ LISTENERS,
25
+ } from "./listeners";
26
+
27
+ export const STATE = {} as IState;
28
+ const STORAGE = {} as IStorageFunctions;
29
+
30
+ setStorageUtils(storageUtils);
31
+
32
+ async function addState(
33
+ namespace: INamespace,
34
+ initialState: IStateDefault,
35
+ isPersistent: boolean,
36
+ ) {
37
+ const storageType = STORAGE.GET_TYPE(isPersistent);
38
+ const defaultValues = Object.assign({}, initialState);
39
+ const values = map(initialState, (k, v) => [namespace(k), v]) as Record<
40
+ string,
41
+ unknown
42
+ >;
43
+
44
+ if (STORAGE.IS_AVAILABE(storageType as string)) {
45
+ await STORAGE.UPDATE_STATE(values, storageType as string);
46
+ }
47
+
48
+ if (!LISTENERS[namespace()]) {
49
+ LISTENERS[namespace()] = [];
50
+ }
51
+
52
+ for (const key in initialState) {
53
+ const fullKey = namespace(key);
54
+ isValid.Defining(fullKey);
55
+ STATE[fullKey] = setupValue(
56
+ key,
57
+ namespace,
58
+ values[fullKey],
59
+ defaultValues[key],
60
+ storageType as string,
61
+ );
62
+ }
63
+
64
+ return createStore(namespace());
65
+ }
66
+
67
+ export function setupValue(
68
+ key: string,
69
+ namespace: INamespace,
70
+ value: unknown,
71
+ defaultValue: unknown,
72
+ storageType: string,
73
+ ): IValue {
74
+ const fullKey = namespace(key);
75
+ const isComputedValue = isFunction(value);
76
+ const dependencies = isComputedValue
77
+ ? getParamNames(value as IComputeFunction)
78
+ : [];
79
+
80
+ dependencies.forEach((dependency) => {
81
+ const fullDependencyKey = namespace(dependency);
82
+ if (!STATE[fullDependencyKey]) {
83
+ STATE[fullDependencyKey] = {
84
+ dependants: [],
85
+ dependencies: [],
86
+ namespace,
87
+ storageType,
88
+ } as IValue;
89
+ }
90
+ STATE[fullDependencyKey as keyof typeof STATE].dependants?.push(fullKey);
91
+ });
92
+
93
+ return {
94
+ key,
95
+ fullKey,
96
+ value: isComputedValue
97
+ ? (value as IComputeFunction).apply(
98
+ null,
99
+ getArguments(dependencies, namespace),
100
+ )
101
+ : value,
102
+ computeFn: isComputedValue && (value as IComputeFunction),
103
+ dependencies,
104
+ dependants: [],
105
+ listeners: [],
106
+ defaultValue,
107
+ storageType,
108
+ namespace,
109
+ };
110
+ }
111
+
112
+ function getNamespaceState(namespace: INamespace) {
113
+ return filter(STATE, (k, { key }) => k === namespace(key));
114
+ }
115
+
116
+ export function getValues(namespace: INamespace) {
117
+ return map(getNamespaceState(namespace), (k, { key, value }) => [
118
+ key,
119
+ value,
120
+ ]) as Record<string, unknown>;
121
+ }
122
+
123
+ function getArguments(dependencies: string[], namespace: INamespace) {
124
+ const values = getValues(namespace);
125
+ return dependencies.map((name) => values[name as keyof typeof values]);
126
+ }
127
+
128
+ async function onStateChange(changes: IStateChanges) {
129
+ const realChanges = {} as IStateChanges;
130
+
131
+ for (const key in changes) {
132
+ const prevValue = STATE[key].value;
133
+ const newValue = changes[key].newValue;
134
+
135
+ if (prevValue !== newValue) {
136
+ realChanges[key] = { newValue, prevValue };
137
+ STATE[key].value = newValue;
138
+ } else {
139
+ realChanges[key] = { isSame: true };
140
+ }
141
+
142
+ updateDependencies(key, changes, realChanges);
143
+ }
144
+
145
+ await runStateChangeListeners(filter(realChanges, (k, v) => !v.isSame));
146
+ }
147
+
148
+ function updateDependencies(
149
+ key: string,
150
+ changes: IStateChanges,
151
+ realChanges: IStateChanges,
152
+ ) {
153
+ STATE[key].dependants?.forEach((name) => {
154
+ const { dependencies, namespace } = STATE[name];
155
+
156
+ if (
157
+ !isEveryDependencyReady(
158
+ dependencies!,
159
+ namespace,
160
+ Object.keys(changes),
161
+ Object.keys(realChanges),
162
+ ) ||
163
+ realChanges[name]
164
+ ) {
165
+ return;
166
+ }
167
+
168
+ const prevValue = STATE[name].value;
169
+ const newValue = (STATE[name].computeFn as IComputeFunction)?.apply(
170
+ null,
171
+ getArguments(dependencies!, namespace),
172
+ );
173
+
174
+ if (prevValue !== newValue) {
175
+ realChanges[name] = { newValue, prevValue };
176
+ STATE[name].value = newValue;
177
+ updateDependencies(name, changes, realChanges);
178
+ }
179
+ });
180
+ }
181
+
182
+ function isEveryDependencyReady(
183
+ dependencies: string[],
184
+ namespace: INamespace,
185
+ changesKeys: string[],
186
+ realChangesKeys: string[],
187
+ ) {
188
+ return dependencies.every((name) => {
189
+ const fullKey = namespace(name);
190
+ return (
191
+ (changesKeys.includes(fullKey) && realChangesKeys.includes(fullKey)) ||
192
+ !changesKeys.includes(fullKey)
193
+ );
194
+ });
195
+ }
196
+
197
+ export function getState(namespace: INamespace, arg: string) {
198
+ if (isString(arg)) {
199
+ return createStore(arg);
200
+ }
201
+
202
+ return recreateStructure(getValues(namespace));
203
+ }
204
+
205
+ export async function setState(
206
+ namespace: INamespace,
207
+ changes: Record<string, unknown>,
208
+ ): Promise<unknown> {
209
+ const storageChanges: Record<string, any> = {};
210
+
211
+ forEach(changes, (k, v) => {
212
+ const fullKey = namespace(k);
213
+ const { storageType, computeFn, isAction } = STATE[fullKey];
214
+
215
+ if (computeFn || isAction) {
216
+ return;
217
+ }
218
+
219
+ if (!storageChanges[storageType!]) {
220
+ storageChanges[storageType!] = {};
221
+ }
222
+
223
+ storageChanges[storageType!][fullKey] = v;
224
+ });
225
+
226
+ for (let [storageType, changes] of Object.entries(storageChanges)) {
227
+ await setValues(storageType, changes);
228
+ }
229
+
230
+ return createStore(namespace());
231
+ }
232
+
233
+ async function setValues(
234
+ storageType: string,
235
+ changes: Record<string, unknown>,
236
+ ) {
237
+ if (STORAGE.IS_AVAILABE(storageType)) {
238
+ const isAutoUpdate = await STORAGE.SET_VALUES(storageType, changes);
239
+ if (isAutoUpdate) {
240
+ return;
241
+ }
242
+ }
243
+
244
+ onStateChange(
245
+ map(changes, (k, newValue) => [k, { newValue }]) as Record<string, any>,
246
+ );
247
+ }
248
+
249
+ export async function resetState(namespace: INamespace, keys?: string[]) {
250
+ const namespaceState = getNamespaceState(namespace);
251
+
252
+ await setState(
253
+ namespace,
254
+ map(
255
+ keys
256
+ ? filter(namespaceState, (k, { key }) => keys.includes(key!))
257
+ : namespaceState,
258
+ (k, { key, defaultValue }) => [key, defaultValue],
259
+ ) as Record<string, any>,
260
+ );
261
+
262
+ return createStore(namespace());
263
+ }
264
+
265
+ function main(namespace: INamespace): any {
266
+ if (!arguments[1] || isString(arguments[1])) {
267
+ return getState.apply(null, arguments as unknown as [INamespace, string]);
268
+ }
269
+
270
+ if (isArray(arguments[1])) {
271
+ return addStateListener.apply(
272
+ null,
273
+ arguments as unknown as [INamespace, string[], () => {}],
274
+ );
275
+ }
276
+
277
+ if (isObject(arguments[1])) {
278
+ return setState.apply(
279
+ null,
280
+ arguments as unknown as [INamespace, Record<string, unknown>],
281
+ );
282
+ }
283
+ }
284
+
285
+ function setStorageUtils(storageUtils: IStorageFunctions) {
286
+ Object.assign(STORAGE, storageUtils);
287
+ }
288
+
289
+ function addNamespace(namespace: string, str: string) {
290
+ return `${namespace}${(isString(str) && NAMESPACE_DELIMITER) || ""}${str || ""}`;
291
+ }
292
+
293
+ export function createStore(_namespace: string) {
294
+ const namespace = ((key: string) =>
295
+ addNamespace(_namespace, key)) as INamespace;
296
+
297
+ return Object.assign(main.bind(null, namespace), {
298
+ add: (state: IStateDefault) => addState(namespace, state, false),
299
+ addPersistent: (state: IStateDefault) => addState(namespace, state, true),
300
+ get: (newNamespace: string) => getState(namespace, newNamespace),
301
+ set: async (changes: Record<string, unknown>) =>
302
+ await setState(namespace, changes),
303
+ reset: (keys: string[]) => resetState(namespace, keys),
304
+ onChange: (keys: string[], cb: () => {}) =>
305
+ addStateListener(namespace, keys, cb),
306
+ removeListener: (keys: string[], cb: () => {}) =>
307
+ removeStateListener(namespace, keys, cb),
308
+ actions: (actions: IStateActions) => addStateActions(namespace, actions),
309
+ setStorageUtils,
310
+ onStateChange,
311
+ });
312
+ }
313
+
314
+ export default createStore("");
@@ -0,0 +1,74 @@
1
+ import { STATE, getValues, createStore } from ".";
2
+ import { NAMESPACE_DELIMITER } from "./constants";
3
+ import { map, isFunction } from "./helpers";
4
+
5
+ export const LISTENERS = {} as IStateListeners;
6
+
7
+ export async function runStateChangeListeners(realChanges: IStateChanges) {
8
+ if (Object.keys(realChanges).length) {
9
+ const { namespace } =
10
+ STATE[Object.keys(realChanges)[0] as keyof typeof STATE];
11
+ const stateValues = getValues(namespace);
12
+
13
+ for (let [changeKey, change] of Object.entries(realChanges)) {
14
+ const { listeners, key } = STATE[changeKey];
15
+ for (const cb of listeners!) {
16
+ await cb([key], stateValues, change);
17
+ }
18
+ }
19
+
20
+ const readableKeys = map(realChanges, (k) =>
21
+ k.slice(NAMESPACE_DELIMITER.length),
22
+ ) as string[];
23
+
24
+ for (const cb of LISTENERS[namespace()]) {
25
+ await cb(
26
+ readableKeys,
27
+ stateValues,
28
+ map(realChanges, (k, v) => [
29
+ k.slice(NAMESPACE_DELIMITER.length),
30
+ v,
31
+ ]) as IStateChanges,
32
+ );
33
+ }
34
+ }
35
+ }
36
+
37
+ export function addStateListener(
38
+ namespace: INamespace,
39
+ observables: string[] | IStateListener,
40
+ cb: () => {},
41
+ ) {
42
+ if (isFunction(observables)) {
43
+ LISTENERS[namespace()].push(observables as IStateListener);
44
+ return createStore(namespace());
45
+ }
46
+
47
+ (observables as string[]).forEach((key) =>
48
+ STATE[namespace(key)].listeners?.push(cb),
49
+ );
50
+
51
+ return createStore(namespace());
52
+ }
53
+
54
+ export function removeStateListener(
55
+ namespace: INamespace,
56
+ observables: string[] | IStateListener,
57
+ removeCb: () => {},
58
+ ) {
59
+ if (isFunction(observables)) {
60
+ const removeIdx = LISTENERS[namespace()].findIndex(
61
+ (cb) => cb === observables,
62
+ );
63
+ LISTENERS[namespace()].splice(removeIdx, 1);
64
+ return createStore(namespace());
65
+ }
66
+
67
+ (observables as string[]).forEach((key) => {
68
+ const listeners = STATE[namespace(key)].listeners;
69
+ const removeIdx = listeners?.findIndex((cb) => cb === removeCb) as number;
70
+ listeners?.splice(removeIdx, 1);
71
+ });
72
+
73
+ return createStore(namespace());
74
+ }
package/src/storage.ts ADDED
@@ -0,0 +1,37 @@
1
+ async function updateFromLocalStorage(state: IStateDefault) {
2
+ const stored = {} as IStateDefault;
3
+ for (const key in state) {
4
+ const value = localStorage.getItem(key);
5
+ if (value !== null) {
6
+ stored[key] = value;
7
+ }
8
+ }
9
+
10
+ return Object.assign(state, stored);
11
+ }
12
+
13
+ function getStorageType(isPersistent: boolean) {
14
+ return isPersistent ? "localStorage" : "";
15
+ }
16
+
17
+ function isStorageAvailable(storageType: string) {
18
+ return !!storageType;
19
+ }
20
+
21
+ async function setStorageValue(
22
+ storageType: string,
23
+ changes: Record<string, string>,
24
+ ) {
25
+ for (let [k, v] of Object.entries(changes)) {
26
+ localStorage.setItem(k, v);
27
+ }
28
+
29
+ return false;
30
+ }
31
+
32
+ export default {
33
+ GET_TYPE: getStorageType,
34
+ IS_AVAILABE: isStorageAvailable,
35
+ UPDATE_STATE: updateFromLocalStorage,
36
+ SET_VALUES: setStorageValue,
37
+ };
package/src/types.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ type INamespace = (key?: string) => string;
2
+
3
+ type IComputeFunction = (...args: any[]) => any;
4
+
5
+ type IValue = {
6
+ key?: string;
7
+ fullKey?: string;
8
+ value?: any;
9
+ computeFn?: boolean | IComputeFunction;
10
+ isAction?: boolean;
11
+ dependencies?: string[];
12
+ dependants?: string[];
13
+ storageType?: string;
14
+ listeners?: any[];
15
+ defaultValue?: any;
16
+ namespace: INamespace;
17
+ };
18
+
19
+ type IState = Record<string, IValue>;
20
+
21
+ type IStateDefault = Record<string, any>;
22
+
23
+ type IStateChange = { newValue?: any; prevValue?: any; isSame?: boolean };
24
+
25
+ type IStateChanges = Record<string, IStateChange>;
26
+
27
+ type IStorageFunctions = {
28
+ GET_TYPE: (type: boolean) => boolean | string;
29
+ IS_AVAILABE: (type: string) => boolean;
30
+ UPDATE_STATE: (state: IStateDefault, type: string) => IStateDefault;
31
+ SET_VALUES: (type: string, changes: Record<string, any>) => Promise<boolean>;
32
+ };
33
+
34
+ type IStateListener = (
35
+ changes: string[],
36
+ values: Record<string, any>,
37
+ data: IStateChanges,
38
+ ) => {};
39
+
40
+ type IStateListeners = Record<string, IStateListener[]>;
41
+
42
+ type IStateAction = (mutator: IStateMutator, args: any[]) => void;
43
+
44
+ type IStateActions = Record<string, IStateAction>;
@@ -1,10 +1,14 @@
1
- import { ACCESSORS } from "."
2
- import { splitFullKey } from "./namespace";
1
+ import { STATE } from ".";
2
+ import { NAMESPACE_DELIMITER } from "./constants";
3
3
 
4
+ function splitFullKey(fullKey: string) {
5
+ const segments = fullKey.split(NAMESPACE_DELIMITER);
6
+ return [segments.slice(-1)[0], segments.join(NAMESPACE_DELIMITER)];
7
+ }
4
8
 
5
9
  export const isValid = {
6
- Setting: (fullKey) => {
7
- if (!ACCESSORS[fullKey]) {
10
+ Setting: (fullKey: string) => {
11
+ if (!STATE[fullKey]) {
8
12
  const [key, namespace] = splitFullKey(fullKey);
9
13
  throw new Error(
10
14
  `Setting "${key}" key in "${namespace}" namespace. DOES NOT EXIST`,
@@ -14,8 +18,8 @@ export const isValid = {
14
18
  return true;
15
19
  },
16
20
 
17
- Defining: (fullKey) => {
18
- if (ACCESSORS[fullKey]) {
21
+ Defining: (fullKey: string) => {
22
+ if (STATE[fullKey]) {
19
23
  const [key, namespace] = splitFullKey(fullKey);
20
24
  throw new Error(
21
25
  `Redefining "${key}" key in "${namespace}" namespace. ALREADY DEFINED`,
@@ -23,6 +27,5 @@ export const isValid = {
23
27
  }
24
28
 
25
29
  return true;
26
- }
27
-
30
+ },
28
31
  };