├── .gitignore ├── .npmignore ├── .vscode └── settings.json ├── dist ├── state-radio.d.ts ├── state-radio.js └── state-radio.umd.cjs ├── docs ├── fonts │ ├── OpenSans-Bold-webfont.eot │ ├── OpenSans-Bold-webfont.svg │ ├── OpenSans-Bold-webfont.woff │ ├── OpenSans-BoldItalic-webfont.eot │ ├── OpenSans-BoldItalic-webfont.svg │ ├── OpenSans-BoldItalic-webfont.woff │ ├── OpenSans-Italic-webfont.eot │ ├── OpenSans-Italic-webfont.svg │ ├── OpenSans-Italic-webfont.woff │ ├── OpenSans-Light-webfont.eot │ ├── OpenSans-Light-webfont.svg │ ├── OpenSans-Light-webfont.woff │ ├── OpenSans-LightItalic-webfont.eot │ ├── OpenSans-LightItalic-webfont.svg │ ├── OpenSans-LightItalic-webfont.woff │ ├── OpenSans-Regular-webfont.eot │ ├── OpenSans-Regular-webfont.svg │ └── OpenSans-Regular-webfont.woff ├── index.html ├── scripts │ ├── linenumber.js │ └── prettify │ │ ├── Apache-License-2.0.txt │ │ ├── lang-css.js │ │ └── prettify.js └── styles │ ├── jsdoc-default.css │ ├── prettify-jsdoc.css │ └── prettify-tomorrow.css ├── example ├── index.html ├── index.js ├── package.json ├── pnpm-lock.yaml ├── todo.html └── todo.js ├── index.js ├── lib └── state-radio.js ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── readme.md └── vite.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | state-radio-react/node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | example 3 | .vscode 4 | docs 5 | package-lock.json 6 | pnpm-lock.yaml 7 | vite.config.js 8 | .gitignore 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "statemanagement" 4 | ] 5 | } -------------------------------------------------------------------------------- /dist/state-radio.d.ts: -------------------------------------------------------------------------------- 1 | export interface Plugin { 2 | name?: string; 3 | getter?: { 4 | method: (state: any, options?: any) => any; 5 | options?: any; 6 | }; 7 | setter?: { 8 | method: (state: any, options?: any) => any; 9 | options?: any; 10 | }; 11 | exposes?: Array<{ 12 | name: string; 13 | method: (state: any, ...args: any[]) => any; 14 | }>; 15 | } 16 | 17 | export interface StateRadioOptions { 18 | plugins?: Plugin[]; 19 | } 20 | 21 | export interface Channel { 22 | name: string; 23 | activePlugins: Plugin[]; 24 | subscribers: Set<(state: any) => void>; 25 | state: any; 26 | middleWares: Array<(state: any, ...args: any[]) => Promise>; 27 | history: any[]; 28 | setState: (newState: any | ((state: any) => any)) => any; 29 | setStateAsync: (newState: any | ((state: any) => any)) => Promise; 30 | getState: (options?: { auto?: boolean }) => any; 31 | getHistory: () => any[]; 32 | addMiddleWares: ( 33 | ...callbackFns: Array<(state: any, ...args: any[]) => Promise> 34 | ) => Array<(state: any, ...args: any[]) => Promise>; 35 | subscribe: (callbackFn: (state: any) => void) => void; 36 | unSubscribe: (callbackFn: (state: any) => void) => void; 37 | notifySubscribers: () => void; 38 | usePlugin: (pluginName: string) => void; 39 | } 40 | 41 | export interface StateRadioInstance { 42 | channels: { 43 | getChannels: () => { [key: string]: Channel }; 44 | getChannel: (channelName: string) => Channel | null; 45 | addChannel: (channelName: string, initialState?: any) => Channel; 46 | removeChannel: ( 47 | channelName: string 48 | ) => { [key: string]: Channel } | undefined; 49 | }; 50 | } 51 | 52 | export function StateRadio(options?: StateRadioOptions): StateRadioInstance; 53 | -------------------------------------------------------------------------------- /dist/state-radio.js: -------------------------------------------------------------------------------- 1 | function StateRadio$1(options = {}) { 2 | let channels = []; 3 | let _plugins = options.plugins || []; 4 | let plugins = {}; 5 | if (_plugins.length > 0) { 6 | _plugins.forEach((plugin) => { 7 | const name = plugin.name || "UnNamedPlugin"; 8 | plugins[name] = plugin; 9 | }); 10 | } 11 | let maxHistoryLimit = 10; 12 | const usePlugin = (channelName, pluginName) => { 13 | const plugin = plugins[pluginName] || null; 14 | if (plugin) { 15 | channels[channelName].activePlugins.push(plugin); 16 | const exposedMethods = plugin.exposes || []; 17 | exposedMethods.forEach((item) => { 18 | channels[channelName][item.name] = (...args) => item.method(channels[channelName].state, ...args); 19 | }); 20 | console.log(`Plugin '${pluginName}' added to channel '${channelName}'`); 21 | } else { 22 | console.error( 23 | `Plugin '${pluginName}' not found, make sure your using the correct plugin name!` 24 | ); 25 | } 26 | }; 27 | const getState = (channelName) => { 28 | return channels[channelName].state; 29 | }; 30 | const getHistory = (channelName) => { 31 | return channels[channelName].history; 32 | }; 33 | const getStateAuto = (channelName, options2) => { 34 | if ((options2 == null ? void 0 : options2.auto) ?? true) { 35 | let stateGetterCallback = () => channels[channelName].state; 36 | subscribe(channelName, stateGetterCallback); 37 | } 38 | return channels[channelName].state; 39 | }; 40 | const getStateWithPlugins = (channelName, options2) => { 41 | const channel = channels[channelName]; 42 | let state = channel.state; 43 | for (const plugin of channel.activePlugins) { 44 | if (plugin.getter) { 45 | const pluginState = plugin.getter.method(state, plugin.getter.options); 46 | state = { ...state, ...pluginState }; 47 | } 48 | } 49 | return getStateAuto(channelName, options2); 50 | }; 51 | const setStateWithPlugins = (channelName, newState) => { 52 | const channel = channels[channelName]; 53 | let currentState = getState(channelName); 54 | let updatedState = newState; 55 | if (typeof newState === "function") { 56 | updatedState = newState(currentState); 57 | } 58 | for (const plugin of channel.activePlugins) { 59 | if (plugin.setter) { 60 | updatedState = plugin.setter.method( 61 | updatedState, 62 | plugin.setter.options 63 | ); 64 | } 65 | } 66 | return stateSetter(channelName, updatedState); 67 | }; 68 | const subscribe = (channelName, fn) => { 69 | channels[channelName].subscribers.add(fn); 70 | }; 71 | const addMiddleWares = (channelName, ...asyncFns) => { 72 | let oldMiddleWares = channels[channelName].middleWares; 73 | channels[channelName].middleWares = [...oldMiddleWares, ...asyncFns]; 74 | return channels[channelName].middleWares; 75 | }; 76 | const notifySubscribers = (channelName) => { 77 | channels[channelName].subscribers.forEach( 78 | (subscriber) => subscriber(channels[channelName].state) 79 | ); 80 | }; 81 | const unSubscribe = (channelName, fn) => { 82 | channels[channelName].subscribers.delete(fn); 83 | }; 84 | const addChannel = (channelName, initialState = {}) => { 85 | channels[channelName] = { 86 | name: channelName, 87 | activePlugins: [], 88 | subscribers: /* @__PURE__ */ new Set(), 89 | state: initialState, 90 | middleWares: [], 91 | history: [], 92 | setState: (newState) => setStateWithPlugins(channelName, newState), 93 | setStateAsync: (newState) => setStateAsync(channelName, newState), 94 | getState: (options2) => getStateWithPlugins(channelName, options2), 95 | getHistory: () => getHistory(channelName), 96 | addMiddleWares: (...callbackFns) => addMiddleWares(channelName, ...callbackFns), 97 | subscribe: (callbackFn) => subscribe(channelName, callbackFn), 98 | unSubscribe: (callbackFn) => unSubscribe(channelName, callbackFn), 99 | notifySubscribers: (channelName2) => notifySubscribers(channelName2), 100 | usePlugin: (pluginName) => usePlugin(channelName, pluginName) 101 | }; 102 | return channels[channelName]; 103 | }; 104 | const getChannel = (channelName) => { 105 | if (!channels[channelName]) { 106 | console.error(`State Radio: ${channelName} channel not found!`); 107 | return null; 108 | } 109 | return channels[channelName]; 110 | }; 111 | const removeChannel = (channelName) => { 112 | if (channels[channelName]) { 113 | const updatedChannels = Object.keys(channels).filter((key) => key !== channelName).reduce((obj, key) => { 114 | obj[key] = channels[key]; 115 | return obj; 116 | }, {}); 117 | channels = updatedChannels; 118 | } else { 119 | console.error(`State Radio: ${channelName} channel does not exist!`); 120 | } 121 | return channels; 122 | }; 123 | const stateSetter = (channelName, newState) => { 124 | let currentState = getState(channelName); 125 | let _newState = newState; 126 | if (typeof newState === "function") { 127 | _newState = newState(currentState); 128 | } 129 | return setState(channelName, _newState); 130 | }; 131 | const setState = (channelName, newState) => { 132 | let previousState = channels[channelName].state; 133 | channels[channelName].state = newState; 134 | if (channels[channelName].history.length <= maxHistoryLimit) { 135 | channels[channelName].history.push(previousState); 136 | } else { 137 | channels[channelName].history = []; 138 | channels[channelName].history.push(previousState); 139 | } 140 | notifySubscribers(channelName); 141 | return channels[channelName].state; 142 | }; 143 | const getChannels = () => channels; 144 | const setStateAsync = async (channelName, newState) => { 145 | const channel = channels[channelName]; 146 | const composedAsyncChain = composeAsync(channel.middleWares, channelName); 147 | let currentState = getState(channelName); 148 | let _newState = newState; 149 | if (typeof newState === "function") { 150 | _newState = newState(currentState); 151 | } 152 | try { 153 | const newStateAfterMiddlewares = await composedAsyncChain(_newState); 154 | return setState(channelName, newStateAfterMiddlewares); 155 | } catch (error) { 156 | console.error("State update error, something happened:", error); 157 | } 158 | }; 159 | const composeAsync = (functionsArray, channelName) => functionsArray.reduce( 160 | (currentFn, nextFn, index) => async (state, ...args) => { 161 | try { 162 | const result = await currentFn(state, ...args); 163 | return await nextFn(result, ...args); 164 | } catch (error) { 165 | console.error( 166 | `Error in middleware ${index} for channel ${channelName}:`, 167 | error 168 | ); 169 | throw error; 170 | } 171 | } 172 | ); 173 | return { 174 | channels: { 175 | getChannels, 176 | getChannel, 177 | addChannel, 178 | removeChannel 179 | } 180 | }; 181 | } 182 | const StateRadio = StateRadio$1; 183 | export { 184 | StateRadio 185 | }; 186 | -------------------------------------------------------------------------------- /dist/state-radio.umd.cjs: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self)["state-radio"]={})}(this,(function(e){"use strict";const t=function(e={}){let t=[],s=e.plugins||[],n={};s.length>0&&s.forEach((e=>{const t=e.name||"UnNamedPlugin";n[t]=e}));const r=e=>t[e].state,o=(e,s)=>{const n=t[e];let r=n.state;for(const t of n.activePlugins)if(t.getter){const e=t.getter.method(r,t.getter.options);r={...r,...e}}return((e,s)=>{((null==s?void 0:s.auto)??1)&&i(e,(()=>t[e].state));return t[e].state})(e,s)},i=(e,s)=>{t[e].subscribers.add(s)},a=e=>{t[e].subscribers.forEach((s=>s(t[e].state)))},c=(e,t)=>{let s=r(e),n=t;return"function"==typeof t&&(n=t(s)),u(e,n)},u=(e,s)=>{let n=t[e].state;return t[e].state=s,t[e].history.length<=10||(t[e].history=[]),t[e].history.push(n),a(e),t[e].state},l=async(e,s)=>{const n=t[e],o=d(n.middleWares,e);let i=r(e),a=s;"function"==typeof s&&(a=s(i));try{const t=await o(a);return u(e,t)}catch(c){}},d=(e,t)=>e.reduce(((e,t,s)=>async(s,...n)=>{try{const r=await e(s,...n);return await t(r,...n)}catch(r){throw r}}));return{channels:{getChannels:()=>t,getChannel:e=>t[e]?t[e]:null,addChannel:(e,s={})=>(t[e]={name:e,activePlugins:[],subscribers:/* @__PURE__ */new Set,state:s,middleWares:[],history:[],setState:s=>((e,s)=>{const n=t[e];let o=r(e),i=s;"function"==typeof s&&(i=s(o));for(const t of n.activePlugins)t.setter&&(i=t.setter.method(i,t.setter.options));return c(e,i)})(e,s),setStateAsync:t=>l(e,t),getState:t=>o(e,t),getHistory:()=>(e=>t[e].history)(e),addMiddleWares:(...s)=>((e,...s)=>{let n=t[e].middleWares;return t[e].middleWares=[...n,...s],t[e].middleWares})(e,...s),subscribe:t=>i(e,t),unSubscribe:s=>((e,s)=>{t[e].subscribers.delete(s)})(e,s),notifySubscribers:e=>a(e),usePlugin:s=>((e,s)=>{const r=n[s]||null;r&&(t[e].activePlugins.push(r),(r.exposes||[]).forEach((s=>{t[e][s.name]=(...n)=>s.method(t[e].state,...n)})))})(e,s)},t[e]),removeChannel:e=>{if(t[e]){const s=Object.keys(t).filter((t=>t!==e)).reduce(((e,s)=>(e[s]=t[s],e)),{});t=s}return t}}}};e.StateRadio=t,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})})); 2 | -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hussseinkizz/state-radio/a7f335b9e05b22c47bdf29a226c4f363abea0725/docs/fonts/OpenSans-Bold-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hussseinkizz/state-radio/a7f335b9e05b22c47bdf29a226c4f363abea0725/docs/fonts/OpenSans-Bold-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-BoldItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hussseinkizz/state-radio/a7f335b9e05b22c47bdf29a226c4f363abea0725/docs/fonts/OpenSans-BoldItalic-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-BoldItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hussseinkizz/state-radio/a7f335b9e05b22c47bdf29a226c4f363abea0725/docs/fonts/OpenSans-BoldItalic-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hussseinkizz/state-radio/a7f335b9e05b22c47bdf29a226c4f363abea0725/docs/fonts/OpenSans-Italic-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hussseinkizz/state-radio/a7f335b9e05b22c47bdf29a226c4f363abea0725/docs/fonts/OpenSans-Italic-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hussseinkizz/state-radio/a7f335b9e05b22c47bdf29a226c4f363abea0725/docs/fonts/OpenSans-Light-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hussseinkizz/state-radio/a7f335b9e05b22c47bdf29a226c4f363abea0725/docs/fonts/OpenSans-Light-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-LightItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hussseinkizz/state-radio/a7f335b9e05b22c47bdf29a226c4f363abea0725/docs/fonts/OpenSans-LightItalic-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-LightItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hussseinkizz/state-radio/a7f335b9e05b22c47bdf29a226c4f363abea0725/docs/fonts/OpenSans-LightItalic-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hussseinkizz/state-radio/a7f335b9e05b22c47bdf29a226c4f363abea0725/docs/fonts/OpenSans-Regular-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hussseinkizz/state-radio/a7f335b9e05b22c47bdf29a226c4f363abea0725/docs/fonts/OpenSans-Regular-webfont.woff -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Home 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Home

21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |

30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 55 | 56 |
57 | 58 |
59 | Documentation generated by JSDoc 4.0.2 on Tue Mar 26 2024 13:41:35 GMT+0300 (East Africa Time) 60 |
61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /docs/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | /*global document */ 2 | (() => { 3 | const source = document.getElementsByClassName('prettyprint source linenums'); 4 | let i = 0; 5 | let lineNumber = 0; 6 | let lineId; 7 | let lines; 8 | let totalLines; 9 | let anchorHash; 10 | 11 | if (source && source[0]) { 12 | anchorHash = document.location.hash.substring(1); 13 | lines = source[0].getElementsByTagName('li'); 14 | totalLines = lines.length; 15 | 16 | for (; i < totalLines; i++) { 17 | lineNumber++; 18 | lineId = `line${lineNumber}`; 19 | lines[i].id = lineId; 20 | if (lineId === anchorHash) { 21 | lines[i].className += ' selected'; 22 | } 23 | } 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /docs/scripts/prettify/Apache-License-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /docs/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /docs/scripts/prettify/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p th:last-child { border-right: 1px solid #ddd; } 224 | 225 | .ancestors, .attribs { color: #999; } 226 | .ancestors a, .attribs a 227 | { 228 | color: #999 !important; 229 | text-decoration: none; 230 | } 231 | 232 | .clear 233 | { 234 | clear: both; 235 | } 236 | 237 | .important 238 | { 239 | font-weight: bold; 240 | color: #950B02; 241 | } 242 | 243 | .yes-def { 244 | text-indent: -1000px; 245 | } 246 | 247 | .type-signature { 248 | color: #aaa; 249 | } 250 | 251 | .name, .signature { 252 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 253 | } 254 | 255 | .details { margin-top: 14px; border-left: 2px solid #DDD; } 256 | .details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; } 257 | .details dd { margin-left: 70px; } 258 | .details ul { margin: 0; } 259 | .details ul { list-style-type: none; } 260 | .details li { margin-left: 30px; padding-top: 6px; } 261 | .details pre.prettyprint { margin: 0 } 262 | .details .object-value { padding-top: 0; } 263 | 264 | .description { 265 | margin-bottom: 1em; 266 | margin-top: 1em; 267 | } 268 | 269 | .code-caption 270 | { 271 | font-style: italic; 272 | font-size: 107%; 273 | margin: 0; 274 | } 275 | 276 | .source 277 | { 278 | border: 1px solid #ddd; 279 | width: 80%; 280 | overflow: auto; 281 | } 282 | 283 | .prettyprint.source { 284 | width: inherit; 285 | } 286 | 287 | .source code 288 | { 289 | font-size: 100%; 290 | line-height: 18px; 291 | display: block; 292 | padding: 4px 12px; 293 | margin: 0; 294 | background-color: #fff; 295 | color: #4D4E53; 296 | } 297 | 298 | .prettyprint code span.line 299 | { 300 | display: inline-block; 301 | } 302 | 303 | .prettyprint.linenums 304 | { 305 | padding-left: 70px; 306 | -webkit-user-select: none; 307 | -moz-user-select: none; 308 | -ms-user-select: none; 309 | user-select: none; 310 | } 311 | 312 | .prettyprint.linenums ol 313 | { 314 | padding-left: 0; 315 | } 316 | 317 | .prettyprint.linenums li 318 | { 319 | border-left: 3px #ddd solid; 320 | } 321 | 322 | .prettyprint.linenums li.selected, 323 | .prettyprint.linenums li.selected * 324 | { 325 | background-color: lightyellow; 326 | } 327 | 328 | .prettyprint.linenums li * 329 | { 330 | -webkit-user-select: text; 331 | -moz-user-select: text; 332 | -ms-user-select: text; 333 | user-select: text; 334 | } 335 | 336 | .params .name, .props .name, .name code { 337 | color: #4D4E53; 338 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 339 | font-size: 100%; 340 | } 341 | 342 | .params td.description > p:first-child, 343 | .props td.description > p:first-child 344 | { 345 | margin-top: 0; 346 | padding-top: 0; 347 | } 348 | 349 | .params td.description > p:last-child, 350 | .props td.description > p:last-child 351 | { 352 | margin-bottom: 0; 353 | padding-bottom: 0; 354 | } 355 | 356 | .disabled { 357 | color: #454545; 358 | } 359 | -------------------------------------------------------------------------------- /docs/styles/prettify-jsdoc.css: -------------------------------------------------------------------------------- 1 | /* JSDoc prettify.js theme */ 2 | 3 | /* plain text */ 4 | .pln { 5 | color: #000000; 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | /* string content */ 11 | .str { 12 | color: #006400; 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | /* a keyword */ 18 | .kwd { 19 | color: #000000; 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | /* a comment */ 25 | .com { 26 | font-weight: normal; 27 | font-style: italic; 28 | } 29 | 30 | /* a type name */ 31 | .typ { 32 | color: #000000; 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* a literal value */ 38 | .lit { 39 | color: #006400; 40 | font-weight: normal; 41 | font-style: normal; 42 | } 43 | 44 | /* punctuation */ 45 | .pun { 46 | color: #000000; 47 | font-weight: bold; 48 | font-style: normal; 49 | } 50 | 51 | /* lisp open bracket */ 52 | .opn { 53 | color: #000000; 54 | font-weight: bold; 55 | font-style: normal; 56 | } 57 | 58 | /* lisp close bracket */ 59 | .clo { 60 | color: #000000; 61 | font-weight: bold; 62 | font-style: normal; 63 | } 64 | 65 | /* a markup tag name */ 66 | .tag { 67 | color: #006400; 68 | font-weight: normal; 69 | font-style: normal; 70 | } 71 | 72 | /* a markup attribute name */ 73 | .atn { 74 | color: #006400; 75 | font-weight: normal; 76 | font-style: normal; 77 | } 78 | 79 | /* a markup attribute value */ 80 | .atv { 81 | color: #006400; 82 | font-weight: normal; 83 | font-style: normal; 84 | } 85 | 86 | /* a declaration */ 87 | .dec { 88 | color: #000000; 89 | font-weight: bold; 90 | font-style: normal; 91 | } 92 | 93 | /* a variable name */ 94 | .var { 95 | color: #000000; 96 | font-weight: normal; 97 | font-style: normal; 98 | } 99 | 100 | /* a function name */ 101 | .fun { 102 | color: #000000; 103 | font-weight: bold; 104 | font-style: normal; 105 | } 106 | 107 | /* Specify class=linenums on a pre to get line numbering */ 108 | ol.linenums { 109 | margin-top: 0; 110 | margin-bottom: 0; 111 | } 112 | -------------------------------------------------------------------------------- /docs/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | State Radio 8 | 9 | 10 |

Hello Listeners!

11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { StateRadio } from '../lib/state-radio.js'; 4 | import _useStorage from '@kizz-js/use-local-storage'; 5 | 6 | // define states 7 | let nums = [1, 2, 3]; 8 | let user = { 9 | name: 'js', 10 | age: 20, 11 | }; 12 | 13 | // configure plugins 14 | let localStorage = { 15 | name: 'localStorage', 16 | plugin: _useStorage, 17 | options: { encrypt: false }, 18 | setter: { 19 | method: _useStorage.setState, 20 | options: { cache: true }, 21 | }, 22 | getter: { 23 | method: _useStorage.getState, 24 | options: {}, 25 | }, 26 | exposes: [{ name: 'onLocalChange', method: _useStorage.onChange }], 27 | }; 28 | 29 | // Create a logging plugin 30 | const logging = { 31 | name: 'loggingPlugin', 32 | setter: { 33 | method: (state, options) => { 34 | // Add a 'logEntry' key with a timestamp to the state 35 | console.log('do something with state via plugin'); 36 | console.log('log:', state, 'options:', options); 37 | return [...state, 100]; 38 | }, 39 | options: { cry: 'meow meow!!' }, 40 | }, 41 | getter: { 42 | method: (state, options) => { 43 | // No special behavior for getting in this example 44 | return state; 45 | }, 46 | options: {}, 47 | }, 48 | // No exposes in this example, as it's a simple logging plugin 49 | }; 50 | 51 | // add plugins when initializing a new state radio 52 | const { channels } = new StateRadio({ 53 | // plugins: [localStorage], 54 | plugins: [logging], 55 | }); 56 | 57 | // add channels to radio station 58 | let numberChannel = channels.addChannel('numberChannel', nums); 59 | let userChannel = channels.addChannel('userChannel', user); 60 | 61 | // use a plugin on a channel and the channel usage remains the same 62 | // numberChannel.usePlugin('localStorage'); 63 | numberChannel.usePlugin('loggingPlugin'); 64 | 65 | // execute methods exposed by plugin 66 | // numberChannel.onLocalChange((e) => 67 | // console.log('Local storage state has changed via plugin!') 68 | // ); 69 | 70 | // update number channel 71 | numberChannel.setState((oldState) => [...oldState, 4]); 72 | console.log('Number Channel State:', numberChannel.getState()); 73 | 74 | // update user channel 75 | userChannel.setState((user) => ({ ...user, name: 'Kizz' })); 76 | console.log('User Channel State:', userChannel.getState()); 77 | console.log('channels log 1:', channels.getChannels()); 78 | 79 | // add channel, get channel and remove channel 80 | channels.addChannel('uselessChannel', 'foobar!'); 81 | let uselessChannel = channels.getChannel('uselessChannel'); 82 | console.log('Useless Channel State:', uselessChannel.getState()); 83 | let newChannels = channels.removeChannel('uselessChannel'); 84 | 85 | console.log('channels log 2:', newChannels); 86 | 87 | // subscriptions 88 | let callback = (newState) => 89 | console.log('Number channel changed to:', newState); 90 | numberChannel.subscribe(callback); 91 | numberChannel.setState((oldState) => [...oldState, 5]); 92 | console.log('Number Channel State 2:', numberChannel.getState({ auto: false })); 93 | // numberChannel.unSubscribe(callback); 94 | numberChannel.setState((oldState) => [...oldState, 6]); 95 | console.log('Number Channel State 3:', numberChannel.getState({ auto: false })); 96 | 97 | // middlewares 98 | const addOneToEach = async (state) => { 99 | console.log('Async Middleware 1 Operating..'); 100 | // add 1 to each num in state 101 | let newNums = state.map((n) => n + 1); 102 | // Simulate an asynchronous operation 103 | await new Promise((resolve) => setTimeout(resolve, 1000)); 104 | return newNums; 105 | }; 106 | 107 | const removeOddNums = async (state) => { 108 | console.log('Async Middleware 2 Operating..'); 109 | // add 1 to each num in state 110 | let newNums = state.filter((n) => n % 2 === 0); 111 | // Simulate an asynchronous operation 112 | await new Promise((resolve) => setTimeout(resolve, 500)); 113 | return newNums; 114 | }; 115 | 116 | numberChannel.addMiddleWares(addOneToEach, removeOddNums); 117 | 118 | numberChannel 119 | .setStateAsync((oldState) => [...oldState, 7]) 120 | .then((updatedState) => { 121 | console.log('Async state updated:', updatedState); 122 | }) 123 | .catch((error) => { 124 | console.error('Error updating state:', error); 125 | }); 126 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "state-radio": "^1.0.3" 15 | } 16 | } -------------------------------------------------------------------------------- /example/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | state-radio: 9 | specifier: ^1.0.3 10 | version: 1.0.3 11 | 12 | packages: 13 | 14 | /@kizz-js/use-local-storage@1.0.3: 15 | resolution: {integrity: sha512-2nHsugDfqIAMhNVO/eITwhTBH+6c4S1hKdSq2lLJizBhsbWKxbjGNpa1RupJuvu+xTkZ5dQ5llA44mcZp/naSg==} 16 | dev: false 17 | 18 | /state-radio@1.0.3: 19 | resolution: {integrity: sha512-FyX6AmIaVCz01gWMBNRoxVyVKNq7rWUSndoJv+Ul4Jy9u6f7ACKjxyFtMIo2iqwYBu94KelaerBgmr06yJMkgw==} 20 | dependencies: 21 | '@kizz-js/use-local-storage': 1.0.3 22 | dev: false 23 | -------------------------------------------------------------------------------- /example/todo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Todo App 8 | 9 | 21 | 22 | 23 | 24 |

Count

25 |
26 | 27 | 28 | 31 | 32 | 37 |
38 | 39 | 40 |
41 | 42 | 43 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /example/todo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import { StateRadio } from './node_modules/state-radio/dist/state-radio.js'; 3 | const taskInput = document.querySelector('#taskInput'); 4 | const addTaskButton = document.querySelector('#addTaskButton'); 5 | const filterSelect = document.querySelector('#filterSelect'); 6 | const counterElement = document.querySelector('#counter'); 7 | 8 | addTaskButton.addEventListener('click', () => addTask(taskInput.value)); 9 | filterSelect.addEventListener('change', () => setFilter(filterSelect.value)); 10 | 11 | const { channels } = new StateRadio(); 12 | 13 | // Add a channel for tasks 14 | const tasksChannel = channels.addChannel('tasks', []); 15 | const counterChannel = channels.addChannel('counter', 0); 16 | 17 | // Add a channel for visibility filter 18 | const filterChannel = channels.addChannel('filter', 'all'); 19 | 20 | // Action to add a task 21 | const addTask = (text) => { 22 | tasksChannel.setState((tasks) => [...tasks, { text, completed: false }]); 23 | }; 24 | 25 | // Action to toggle task completion 26 | const toggleTask = (index) => { 27 | tasksChannel.setState((tasks) => 28 | tasks.map((task, i) => 29 | i === index ? { ...task, completed: !task.completed } : task 30 | ) 31 | ); 32 | }; 33 | 34 | // Action to set visibility filter 35 | const setFilter = (filter) => { 36 | filterChannel.setState(filter); 37 | }; 38 | 39 | let count = counterChannel.getState(); 40 | 41 | // Subscribe to tasks changes 42 | tasksChannel.subscribe((tasks) => { 43 | // Update UI or trigger re-render 44 | console.log('Tasks Updated:', tasks); 45 | counterChannel.setState((count) => count + 1); 46 | counterElement.innerHTML = `Count: ${counterChannel.getState()}`; 47 | }); 48 | 49 | // Subscribe to filter changes 50 | filterChannel.subscribe((filter) => { 51 | // Update UI or trigger re-render based on filter 52 | console.log('Filter Updated:', filter); 53 | // Update UI based on filter 54 | let currentTasks = tasksChannel.getState(); 55 | let filteredTasks = []; 56 | 57 | if (filter === 'completed') { 58 | filteredTasks = currentTasks.filter((task) => task.completed === true); 59 | updateTasksUI(filteredTasks); 60 | } else if (filter === 'active') { 61 | updateTasksUI(currentTasks); 62 | } else { 63 | updateTasksUI(currentTasks); 64 | } 65 | }); 66 | 67 | // Update UI based on state changes 68 | function updateTasksUI(tasks) { 69 | const tasksContainer = document.getElementById('tasksContainer'); 70 | tasksContainer.innerHTML = ''; 71 | 72 | tasks.forEach((task, index) => { 73 | const taskElement = document.createElement('div'); 74 | taskElement.innerHTML = ` 75 | 76 | ${task.text} 77 | `; 78 | tasksContainer.appendChild(taskElement); 79 | // Add onChange event listener to the checkbox 80 | const checkbox = taskElement.querySelector('input[type="checkbox"]'); 81 | checkbox.addEventListener('change', () => toggleTask(index)); 82 | }); 83 | } 84 | 85 | // Subscribe to tasks changes 86 | tasksChannel.subscribe(updateTasksUI); 87 | 88 | // Initial rendering 89 | updateTasksUI(tasksChannel.getState()); 90 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { StateRadio as stateRadio } from './lib/state-radio.js'; 2 | 3 | export const StateRadio = stateRadio; 4 | -------------------------------------------------------------------------------- /lib/state-radio.js: -------------------------------------------------------------------------------- 1 | // State Radio - State Manager Library v1.0.0 2 | // By Hussein Kizz, Last Modified 29-01-2024 3 | 4 | export function StateRadio(options = {}) { 5 | let channels = []; 6 | let _plugins = options.plugins || []; 7 | let plugins = {}; 8 | 9 | // Modify and store plugins 10 | if (_plugins.length > 0) { 11 | _plugins.forEach((plugin) => { 12 | const name = plugin.name || 'UnNamedPlugin'; 13 | plugins[name] = plugin; 14 | }); 15 | } 16 | 17 | // console.log('Plugins:', plugins); 18 | 19 | let maxHistoryLimit = 10; // 10 default 20 | 21 | // Todo - finish plugins implementation 22 | // also modify entire implementation to actually consider plugins, how? 23 | // a plugin provides a setter and getter function, so on channel when getState is called or setState, we instead call the ones of the plugin and what they return we merge it into the channel state and then return, channels can also call other exposed plugin method as channel.exposedPluginMethod(...args) etc! 24 | 25 | // add plugin to channel active plugins and expose the plugin exposed methods on channel object 26 | 27 | // Todo - implement remove middleware 28 | 29 | const usePlugin = (channelName, pluginName) => { 30 | const plugin = plugins[pluginName] || null; 31 | 32 | if (plugin) { 33 | // Add the plugin to the channel's activePlugins 34 | channels[channelName].activePlugins.push(plugin); 35 | 36 | // Expose plugin methods on the channel object 37 | const exposedMethods = plugin.exposes || []; 38 | exposedMethods.forEach((item) => { 39 | channels[channelName][item.name] = (...args) => 40 | item.method(channels[channelName].state, ...args); 41 | }); 42 | 43 | console.log(`Plugin '${pluginName}' added to channel '${channelName}'`); 44 | } else { 45 | console.error( 46 | `Plugin '${pluginName}' not found, make sure your using the correct plugin name!` 47 | ); 48 | } 49 | }; 50 | 51 | const getState = (channelName) => { 52 | return channels[channelName].state; 53 | }; 54 | 55 | const getHistory = (channelName) => { 56 | return channels[channelName].history; 57 | }; 58 | 59 | const getStateAuto = (channelName, options) => { 60 | if (options?.auto ?? true) { 61 | let stateGetterCallback = () => channels[channelName].state; 62 | // auto subscribe the state getter 63 | subscribe(channelName, stateGetterCallback); 64 | } 65 | return channels[channelName].state; 66 | }; 67 | 68 | const getStateWithPlugins = (channelName, options) => { 69 | const channel = channels[channelName]; 70 | let state = channel.state; 71 | 72 | for (const plugin of channel.activePlugins) { 73 | if (plugin.getter) { 74 | const pluginState = plugin.getter.method(state, plugin.getter.options); 75 | state = { ...state, ...pluginState }; 76 | } 77 | } 78 | 79 | return getStateAuto(channelName, options); 80 | }; 81 | 82 | const setStateWithPlugins = (channelName, newState) => { 83 | const channel = channels[channelName]; 84 | let currentState = getState(channelName); 85 | let updatedState = newState; 86 | if (typeof newState === 'function') { 87 | updatedState = newState(currentState); 88 | } 89 | 90 | for (const plugin of channel.activePlugins) { 91 | if (plugin.setter) { 92 | updatedState = plugin.setter.method( 93 | updatedState, 94 | plugin.setter.options 95 | ); 96 | } 97 | } 98 | 99 | return stateSetter(channelName, updatedState); 100 | }; 101 | 102 | const subscribe = (channelName, fn) => { 103 | channels[channelName].subscribers.add(fn); 104 | }; 105 | 106 | const addMiddleWares = (channelName, ...asyncFns) => { 107 | let oldMiddleWares = channels[channelName].middleWares; 108 | channels[channelName].middleWares = [...oldMiddleWares, ...asyncFns]; 109 | 110 | return channels[channelName].middleWares; 111 | }; 112 | 113 | const notifySubscribers = (channelName) => { 114 | channels[channelName].subscribers.forEach((subscriber) => 115 | subscriber(channels[channelName].state) 116 | ); 117 | }; 118 | 119 | const unSubscribe = (channelName, fn) => { 120 | channels[channelName].subscribers.delete(fn); 121 | }; 122 | 123 | const addChannel = (channelName, initialState = {}) => { 124 | channels[channelName] = { 125 | name: channelName, 126 | activePlugins: [], 127 | subscribers: new Set(), 128 | state: initialState, 129 | middleWares: [], 130 | history: [], 131 | setState: (newState) => setStateWithPlugins(channelName, newState), 132 | setStateAsync: (newState) => setStateAsync(channelName, newState), 133 | getState: (options) => getStateWithPlugins(channelName, options), 134 | getHistory: () => getHistory(channelName), 135 | addMiddleWares: (...callbackFns) => 136 | addMiddleWares(channelName, ...callbackFns), 137 | subscribe: (callbackFn) => subscribe(channelName, callbackFn), 138 | unSubscribe: (callbackFn) => unSubscribe(channelName, callbackFn), 139 | notifySubscribers: (channelName) => notifySubscribers(channelName), 140 | usePlugin: (pluginName) => usePlugin(channelName, pluginName), 141 | }; 142 | 143 | return channels[channelName]; 144 | }; 145 | 146 | const getChannel = (channelName) => { 147 | if (!channels[channelName]) { 148 | console.error(`State Radio: ${channelName} channel not found!`); 149 | return null; 150 | } 151 | return channels[channelName]; 152 | }; 153 | 154 | const removeChannel = (channelName) => { 155 | if (channels[channelName]) { 156 | const updatedChannels = Object.keys(channels) 157 | .filter((key) => key !== channelName) 158 | .reduce((obj, key) => { 159 | obj[key] = channels[key]; 160 | return obj; 161 | }, {}); 162 | 163 | channels = updatedChannels; 164 | } else { 165 | console.error(`State Radio: ${channelName} channel does not exist!`); 166 | } 167 | return channels; 168 | }; 169 | 170 | const stateSetter = (channelName, newState) => { 171 | let currentState = getState(channelName); 172 | let _newState = newState; 173 | if (typeof newState === 'function') { 174 | _newState = newState(currentState); 175 | } 176 | return setState(channelName, _newState); 177 | }; 178 | 179 | const setState = (channelName, newState) => { 180 | let previousState = channels[channelName].state; 181 | 182 | channels[channelName].state = newState; 183 | 184 | // log previous state to history, first flash it out if limit reached 185 | if (channels[channelName].history.length <= maxHistoryLimit) { 186 | channels[channelName].history.push(previousState); 187 | } else { 188 | channels[channelName].history = []; 189 | channels[channelName].history.push(previousState); 190 | } 191 | 192 | // notify all listeners of the new changes 193 | notifySubscribers(channelName); 194 | 195 | return channels[channelName].state; 196 | }; 197 | 198 | const getChannels = () => channels; 199 | 200 | const setStateAsync = async (channelName, newState) => { 201 | const channel = channels[channelName]; 202 | 203 | // Compose async middleware functions chain for this channel 204 | const composedAsyncChain = composeAsync(channel.middleWares, channelName); 205 | 206 | // get actual state in case it's a function 207 | let currentState = getState(channelName); 208 | let _newState = newState; 209 | if (typeof newState === 'function') { 210 | _newState = newState(currentState); 211 | } 212 | 213 | try { 214 | // Apply the composed async middleware chain to update state 215 | const newStateAfterMiddlewares = await composedAsyncChain(_newState); 216 | 217 | // Finally commit the updated state 218 | return setState(channelName, newStateAfterMiddlewares); 219 | } catch (error) { 220 | console.error('State update error, something happened:', error); 221 | } 222 | }; 223 | 224 | const composeAsync = (functionsArray, channelName) => 225 | functionsArray.reduce( 226 | (currentFn, nextFn, index) => 227 | async (state, ...args) => { 228 | try { 229 | const result = await currentFn(state, ...args); 230 | return await nextFn(result, ...args); 231 | } catch (error) { 232 | console.error( 233 | `Error in middleware ${index} for channel ${channelName}:`, 234 | error 235 | ); 236 | throw error; 237 | } 238 | } 239 | ); 240 | 241 | return { 242 | channels: { 243 | getChannels, 244 | getChannel, 245 | addChannel, 246 | removeChannel, 247 | }, 248 | }; 249 | } 250 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-build", 3 | "version": "0.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "vite-build", 9 | "version": "0.0.0", 10 | "devDependencies": { 11 | "jsdoc": "^4.0.2", 12 | "vite": "^5.0.0" 13 | } 14 | }, 15 | "node_modules/@babel/parser": { 16 | "version": "7.23.4", 17 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.4.tgz", 18 | "integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==", 19 | "dev": true, 20 | "bin": { 21 | "parser": "bin/babel-parser.js" 22 | }, 23 | "engines": { 24 | "node": ">=6.0.0" 25 | } 26 | }, 27 | "node_modules/@esbuild/android-arm": { 28 | "version": "0.19.7", 29 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.7.tgz", 30 | "integrity": "sha512-YGSPnndkcLo4PmVl2tKatEn+0mlVMr3yEpOOT0BeMria87PhvoJb5dg5f5Ft9fbCVgtAz4pWMzZVgSEGpDAlww==", 31 | "cpu": [ 32 | "arm" 33 | ], 34 | "dev": true, 35 | "optional": true, 36 | "os": [ 37 | "android" 38 | ], 39 | "engines": { 40 | "node": ">=12" 41 | } 42 | }, 43 | "node_modules/@esbuild/android-arm64": { 44 | "version": "0.19.7", 45 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.7.tgz", 46 | "integrity": "sha512-YEDcw5IT7hW3sFKZBkCAQaOCJQLONVcD4bOyTXMZz5fr66pTHnAet46XAtbXAkJRfIn2YVhdC6R9g4xa27jQ1w==", 47 | "cpu": [ 48 | "arm64" 49 | ], 50 | "dev": true, 51 | "optional": true, 52 | "os": [ 53 | "android" 54 | ], 55 | "engines": { 56 | "node": ">=12" 57 | } 58 | }, 59 | "node_modules/@esbuild/android-x64": { 60 | "version": "0.19.7", 61 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.7.tgz", 62 | "integrity": "sha512-jhINx8DEjz68cChFvM72YzrqfwJuFbfvSxZAk4bebpngGfNNRm+zRl4rtT9oAX6N9b6gBcFaJHFew5Blf6CvUw==", 63 | "cpu": [ 64 | "x64" 65 | ], 66 | "dev": true, 67 | "optional": true, 68 | "os": [ 69 | "android" 70 | ], 71 | "engines": { 72 | "node": ">=12" 73 | } 74 | }, 75 | "node_modules/@esbuild/darwin-arm64": { 76 | "version": "0.19.7", 77 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.7.tgz", 78 | "integrity": "sha512-dr81gbmWN//3ZnBIm6YNCl4p3pjnabg1/ZVOgz2fJoUO1a3mq9WQ/1iuEluMs7mCL+Zwv7AY5e3g1hjXqQZ9Iw==", 79 | "cpu": [ 80 | "arm64" 81 | ], 82 | "dev": true, 83 | "optional": true, 84 | "os": [ 85 | "darwin" 86 | ], 87 | "engines": { 88 | "node": ">=12" 89 | } 90 | }, 91 | "node_modules/@esbuild/darwin-x64": { 92 | "version": "0.19.7", 93 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.7.tgz", 94 | "integrity": "sha512-Lc0q5HouGlzQEwLkgEKnWcSazqr9l9OdV2HhVasWJzLKeOt0PLhHaUHuzb8s/UIya38DJDoUm74GToZ6Wc7NGQ==", 95 | "cpu": [ 96 | "x64" 97 | ], 98 | "dev": true, 99 | "optional": true, 100 | "os": [ 101 | "darwin" 102 | ], 103 | "engines": { 104 | "node": ">=12" 105 | } 106 | }, 107 | "node_modules/@esbuild/freebsd-arm64": { 108 | "version": "0.19.7", 109 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.7.tgz", 110 | "integrity": "sha512-+y2YsUr0CxDFF7GWiegWjGtTUF6gac2zFasfFkRJPkMAuMy9O7+2EH550VlqVdpEEchWMynkdhC9ZjtnMiHImQ==", 111 | "cpu": [ 112 | "arm64" 113 | ], 114 | "dev": true, 115 | "optional": true, 116 | "os": [ 117 | "freebsd" 118 | ], 119 | "engines": { 120 | "node": ">=12" 121 | } 122 | }, 123 | "node_modules/@esbuild/freebsd-x64": { 124 | "version": "0.19.7", 125 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.7.tgz", 126 | "integrity": "sha512-CdXOxIbIzPJmJhrpmJTLx+o35NoiKBIgOvmvT+jeSadYiWJn0vFKsl+0bSG/5lwjNHoIDEyMYc/GAPR9jxusTA==", 127 | "cpu": [ 128 | "x64" 129 | ], 130 | "dev": true, 131 | "optional": true, 132 | "os": [ 133 | "freebsd" 134 | ], 135 | "engines": { 136 | "node": ">=12" 137 | } 138 | }, 139 | "node_modules/@esbuild/linux-arm": { 140 | "version": "0.19.7", 141 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.7.tgz", 142 | "integrity": "sha512-Y+SCmWxsJOdQtjcBxoacn/pGW9HDZpwsoof0ttL+2vGcHokFlfqV666JpfLCSP2xLxFpF1lj7T3Ox3sr95YXww==", 143 | "cpu": [ 144 | "arm" 145 | ], 146 | "dev": true, 147 | "optional": true, 148 | "os": [ 149 | "linux" 150 | ], 151 | "engines": { 152 | "node": ">=12" 153 | } 154 | }, 155 | "node_modules/@esbuild/linux-arm64": { 156 | "version": "0.19.7", 157 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.7.tgz", 158 | "integrity": "sha512-inHqdOVCkUhHNvuQPT1oCB7cWz9qQ/Cz46xmVe0b7UXcuIJU3166aqSunsqkgSGMtUCWOZw3+KMwI6otINuC9g==", 159 | "cpu": [ 160 | "arm64" 161 | ], 162 | "dev": true, 163 | "optional": true, 164 | "os": [ 165 | "linux" 166 | ], 167 | "engines": { 168 | "node": ">=12" 169 | } 170 | }, 171 | "node_modules/@esbuild/linux-ia32": { 172 | "version": "0.19.7", 173 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.7.tgz", 174 | "integrity": "sha512-2BbiL7nLS5ZO96bxTQkdO0euGZIUQEUXMTrqLxKUmk/Y5pmrWU84f+CMJpM8+EHaBPfFSPnomEaQiG/+Gmh61g==", 175 | "cpu": [ 176 | "ia32" 177 | ], 178 | "dev": true, 179 | "optional": true, 180 | "os": [ 181 | "linux" 182 | ], 183 | "engines": { 184 | "node": ">=12" 185 | } 186 | }, 187 | "node_modules/@esbuild/linux-loong64": { 188 | "version": "0.19.7", 189 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.7.tgz", 190 | "integrity": "sha512-BVFQla72KXv3yyTFCQXF7MORvpTo4uTA8FVFgmwVrqbB/4DsBFWilUm1i2Oq6zN36DOZKSVUTb16jbjedhfSHw==", 191 | "cpu": [ 192 | "loong64" 193 | ], 194 | "dev": true, 195 | "optional": true, 196 | "os": [ 197 | "linux" 198 | ], 199 | "engines": { 200 | "node": ">=12" 201 | } 202 | }, 203 | "node_modules/@esbuild/linux-mips64el": { 204 | "version": "0.19.7", 205 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.7.tgz", 206 | "integrity": "sha512-DzAYckIaK+pS31Q/rGpvUKu7M+5/t+jI+cdleDgUwbU7KdG2eC3SUbZHlo6Q4P1CfVKZ1lUERRFP8+q0ob9i2w==", 207 | "cpu": [ 208 | "mips64el" 209 | ], 210 | "dev": true, 211 | "optional": true, 212 | "os": [ 213 | "linux" 214 | ], 215 | "engines": { 216 | "node": ">=12" 217 | } 218 | }, 219 | "node_modules/@esbuild/linux-ppc64": { 220 | "version": "0.19.7", 221 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.7.tgz", 222 | "integrity": "sha512-JQ1p0SmUteNdUaaiRtyS59GkkfTW0Edo+e0O2sihnY4FoZLz5glpWUQEKMSzMhA430ctkylkS7+vn8ziuhUugQ==", 223 | "cpu": [ 224 | "ppc64" 225 | ], 226 | "dev": true, 227 | "optional": true, 228 | "os": [ 229 | "linux" 230 | ], 231 | "engines": { 232 | "node": ">=12" 233 | } 234 | }, 235 | "node_modules/@esbuild/linux-riscv64": { 236 | "version": "0.19.7", 237 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.7.tgz", 238 | "integrity": "sha512-xGwVJ7eGhkprY/nB7L7MXysHduqjpzUl40+XoYDGC4UPLbnG+gsyS1wQPJ9lFPcxYAaDXbdRXd1ACs9AE9lxuw==", 239 | "cpu": [ 240 | "riscv64" 241 | ], 242 | "dev": true, 243 | "optional": true, 244 | "os": [ 245 | "linux" 246 | ], 247 | "engines": { 248 | "node": ">=12" 249 | } 250 | }, 251 | "node_modules/@esbuild/linux-s390x": { 252 | "version": "0.19.7", 253 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.7.tgz", 254 | "integrity": "sha512-U8Rhki5PVU0L0nvk+E8FjkV8r4Lh4hVEb9duR6Zl21eIEYEwXz8RScj4LZWA2i3V70V4UHVgiqMpszXvG0Yqhg==", 255 | "cpu": [ 256 | "s390x" 257 | ], 258 | "dev": true, 259 | "optional": true, 260 | "os": [ 261 | "linux" 262 | ], 263 | "engines": { 264 | "node": ">=12" 265 | } 266 | }, 267 | "node_modules/@esbuild/linux-x64": { 268 | "version": "0.19.7", 269 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.7.tgz", 270 | "integrity": "sha512-ZYZopyLhm4mcoZXjFt25itRlocKlcazDVkB4AhioiL9hOWhDldU9n38g62fhOI4Pth6vp+Mrd5rFKxD0/S+7aQ==", 271 | "cpu": [ 272 | "x64" 273 | ], 274 | "dev": true, 275 | "optional": true, 276 | "os": [ 277 | "linux" 278 | ], 279 | "engines": { 280 | "node": ">=12" 281 | } 282 | }, 283 | "node_modules/@esbuild/netbsd-x64": { 284 | "version": "0.19.7", 285 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.7.tgz", 286 | "integrity": "sha512-/yfjlsYmT1O3cum3J6cmGG16Fd5tqKMcg5D+sBYLaOQExheAJhqr8xOAEIuLo8JYkevmjM5zFD9rVs3VBcsjtQ==", 287 | "cpu": [ 288 | "x64" 289 | ], 290 | "dev": true, 291 | "optional": true, 292 | "os": [ 293 | "netbsd" 294 | ], 295 | "engines": { 296 | "node": ">=12" 297 | } 298 | }, 299 | "node_modules/@esbuild/openbsd-x64": { 300 | "version": "0.19.7", 301 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.7.tgz", 302 | "integrity": "sha512-MYDFyV0EW1cTP46IgUJ38OnEY5TaXxjoDmwiTXPjezahQgZd+j3T55Ht8/Q9YXBM0+T9HJygrSRGV5QNF/YVDQ==", 303 | "cpu": [ 304 | "x64" 305 | ], 306 | "dev": true, 307 | "optional": true, 308 | "os": [ 309 | "openbsd" 310 | ], 311 | "engines": { 312 | "node": ">=12" 313 | } 314 | }, 315 | "node_modules/@esbuild/sunos-x64": { 316 | "version": "0.19.7", 317 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.7.tgz", 318 | "integrity": "sha512-JcPvgzf2NN/y6X3UUSqP6jSS06V0DZAV/8q0PjsZyGSXsIGcG110XsdmuWiHM+pno7/mJF6fjH5/vhUz/vA9fw==", 319 | "cpu": [ 320 | "x64" 321 | ], 322 | "dev": true, 323 | "optional": true, 324 | "os": [ 325 | "sunos" 326 | ], 327 | "engines": { 328 | "node": ">=12" 329 | } 330 | }, 331 | "node_modules/@esbuild/win32-arm64": { 332 | "version": "0.19.7", 333 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.7.tgz", 334 | "integrity": "sha512-ZA0KSYti5w5toax5FpmfcAgu3ZNJxYSRm0AW/Dao5up0YV1hDVof1NvwLomjEN+3/GMtaWDI+CIyJOMTRSTdMw==", 335 | "cpu": [ 336 | "arm64" 337 | ], 338 | "dev": true, 339 | "optional": true, 340 | "os": [ 341 | "win32" 342 | ], 343 | "engines": { 344 | "node": ">=12" 345 | } 346 | }, 347 | "node_modules/@esbuild/win32-ia32": { 348 | "version": "0.19.7", 349 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.7.tgz", 350 | "integrity": "sha512-CTOnijBKc5Jpk6/W9hQMMvJnsSYRYgveN6O75DTACCY18RA2nqka8dTZR+x/JqXCRiKk84+5+bRKXUSbbwsS0A==", 351 | "cpu": [ 352 | "ia32" 353 | ], 354 | "dev": true, 355 | "optional": true, 356 | "os": [ 357 | "win32" 358 | ], 359 | "engines": { 360 | "node": ">=12" 361 | } 362 | }, 363 | "node_modules/@esbuild/win32-x64": { 364 | "version": "0.19.7", 365 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.7.tgz", 366 | "integrity": "sha512-gRaP2sk6hc98N734luX4VpF318l3w+ofrtTu9j5L8EQXF+FzQKV6alCOHMVoJJHvVK/mGbwBXfOL1HETQu9IGQ==", 367 | "cpu": [ 368 | "x64" 369 | ], 370 | "dev": true, 371 | "optional": true, 372 | "os": [ 373 | "win32" 374 | ], 375 | "engines": { 376 | "node": ">=12" 377 | } 378 | }, 379 | "node_modules/@jsdoc/salty": { 380 | "version": "0.2.6", 381 | "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.6.tgz", 382 | "integrity": "sha512-aA+awb5yoml8TQ3CzI5Ue7sM3VMRC4l1zJJW4fgZ8OCL1wshJZhNzaf0PL85DSnOUw6QuFgeHGD/eq/xwwAF2g==", 383 | "dev": true, 384 | "dependencies": { 385 | "lodash": "^4.17.21" 386 | }, 387 | "engines": { 388 | "node": ">=v12.0.0" 389 | } 390 | }, 391 | "node_modules/@rollup/rollup-android-arm-eabi": { 392 | "version": "4.5.2", 393 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.5.2.tgz", 394 | "integrity": "sha512-ee7BudTwwrglFYSc3UnqInDDjCLWHKrFmGNi4aK7jlEyg4CyPa1DCMrZfsN1O13YT76UFEqXz2CoN7BCGpUlJw==", 395 | "cpu": [ 396 | "arm" 397 | ], 398 | "dev": true, 399 | "optional": true, 400 | "os": [ 401 | "android" 402 | ] 403 | }, 404 | "node_modules/@rollup/rollup-android-arm64": { 405 | "version": "4.5.2", 406 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.5.2.tgz", 407 | "integrity": "sha512-xOuhj9HHtn8128ir8veoQsBbAUBasDbHIBniYTEx02pAmu9EXL+ZjJqngnNEy6ZgZ4h1JwL33GMNu3yJL5Mzow==", 408 | "cpu": [ 409 | "arm64" 410 | ], 411 | "dev": true, 412 | "optional": true, 413 | "os": [ 414 | "android" 415 | ] 416 | }, 417 | "node_modules/@rollup/rollup-darwin-arm64": { 418 | "version": "4.5.2", 419 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.5.2.tgz", 420 | "integrity": "sha512-NTGJWoL8bKyqyWFn9/RzSv4hQ4wTbaAv0lHHRwf4OnpiiP4P8W0jiXbm8Nc5BCXKmWAwuvJY82mcIU2TayC20g==", 421 | "cpu": [ 422 | "arm64" 423 | ], 424 | "dev": true, 425 | "optional": true, 426 | "os": [ 427 | "darwin" 428 | ] 429 | }, 430 | "node_modules/@rollup/rollup-darwin-x64": { 431 | "version": "4.5.2", 432 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.5.2.tgz", 433 | "integrity": "sha512-hlKqj7bpPvU15sZo4za14u185lpMzdwWLMc9raMqPK4wywt0wR23y1CaVQ4oAFXat3b5/gmRntyfpwWTKl+vvA==", 434 | "cpu": [ 435 | "x64" 436 | ], 437 | "dev": true, 438 | "optional": true, 439 | "os": [ 440 | "darwin" 441 | ] 442 | }, 443 | "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 444 | "version": "4.5.2", 445 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.5.2.tgz", 446 | "integrity": "sha512-7ZIZx8c3u+pfI0ohQsft/GywrXez0uR6dUP0JhBuCK3sFO5TfdLn/YApnVkvPxuTv3+YKPIZend9Mt7Cz6sS3Q==", 447 | "cpu": [ 448 | "arm" 449 | ], 450 | "dev": true, 451 | "optional": true, 452 | "os": [ 453 | "linux" 454 | ] 455 | }, 456 | "node_modules/@rollup/rollup-linux-arm64-gnu": { 457 | "version": "4.5.2", 458 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.5.2.tgz", 459 | "integrity": "sha512-7Pk/5mO11JW/cH+a8lL/i0ZxmRGrbpYqN0VwO2DHhU+SJWWOH2zE1RAcPaj8KqiwC8DCDIJOSxjV9+9lLb6aeA==", 460 | "cpu": [ 461 | "arm64" 462 | ], 463 | "dev": true, 464 | "optional": true, 465 | "os": [ 466 | "linux" 467 | ] 468 | }, 469 | "node_modules/@rollup/rollup-linux-arm64-musl": { 470 | "version": "4.5.2", 471 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.5.2.tgz", 472 | "integrity": "sha512-KrRnuG5phJx756e62wxvWH2e+TK84MP2IVuPwfge+GBvWqIUfVzFRn09TKruuQBXzZp52Vyma7FjMDkwlA9xpg==", 473 | "cpu": [ 474 | "arm64" 475 | ], 476 | "dev": true, 477 | "optional": true, 478 | "os": [ 479 | "linux" 480 | ] 481 | }, 482 | "node_modules/@rollup/rollup-linux-x64-gnu": { 483 | "version": "4.5.2", 484 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.5.2.tgz", 485 | "integrity": "sha512-My+53GasPa2D2tU5dXiyHYwrELAUouSfkNlZ3bUKpI7btaztO5vpALEs3mvFjM7aKTvEbc7GQckuXeXIDKQ0fg==", 486 | "cpu": [ 487 | "x64" 488 | ], 489 | "dev": true, 490 | "optional": true, 491 | "os": [ 492 | "linux" 493 | ] 494 | }, 495 | "node_modules/@rollup/rollup-linux-x64-musl": { 496 | "version": "4.5.2", 497 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.5.2.tgz", 498 | "integrity": "sha512-/f0Q6Sc+Vw54Ws6N8fxaEe4R7at3b8pFyv+O/F2VaQ4hODUJcRUcCBJh6zuqtgQQt7w845VTkGLFgWZkP3tUoQ==", 499 | "cpu": [ 500 | "x64" 501 | ], 502 | "dev": true, 503 | "optional": true, 504 | "os": [ 505 | "linux" 506 | ] 507 | }, 508 | "node_modules/@rollup/rollup-win32-arm64-msvc": { 509 | "version": "4.5.2", 510 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.5.2.tgz", 511 | "integrity": "sha512-NCKuuZWLht6zj7s6EIFef4BxCRX1GMr83S2W4HPCA0RnJ4iHE4FS1695q6Ewoa6A9nFjJe1//yUu0kgBU07Edw==", 512 | "cpu": [ 513 | "arm64" 514 | ], 515 | "dev": true, 516 | "optional": true, 517 | "os": [ 518 | "win32" 519 | ] 520 | }, 521 | "node_modules/@rollup/rollup-win32-ia32-msvc": { 522 | "version": "4.5.2", 523 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.5.2.tgz", 524 | "integrity": "sha512-J5zL3riR4AOyU/J3M/i4k/zZ8eP1yT+nTmAKztCXJtnI36jYH0eepvob22mAQ/kLwfsK2TB6dbyVY1F8c/0H5A==", 525 | "cpu": [ 526 | "ia32" 527 | ], 528 | "dev": true, 529 | "optional": true, 530 | "os": [ 531 | "win32" 532 | ] 533 | }, 534 | "node_modules/@rollup/rollup-win32-x64-msvc": { 535 | "version": "4.5.2", 536 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.5.2.tgz", 537 | "integrity": "sha512-pL0RXRHuuGLhvs7ayX/SAHph1hrDPXOM5anyYUQXWJEENxw3nfHkzv8FfVlEVcLyKPAEgDRkd6RKZq2SMqS/yg==", 538 | "cpu": [ 539 | "x64" 540 | ], 541 | "dev": true, 542 | "optional": true, 543 | "os": [ 544 | "win32" 545 | ] 546 | }, 547 | "node_modules/@types/linkify-it": { 548 | "version": "3.0.5", 549 | "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", 550 | "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", 551 | "dev": true 552 | }, 553 | "node_modules/@types/markdown-it": { 554 | "version": "12.2.3", 555 | "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", 556 | "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", 557 | "dev": true, 558 | "dependencies": { 559 | "@types/linkify-it": "*", 560 | "@types/mdurl": "*" 561 | } 562 | }, 563 | "node_modules/@types/mdurl": { 564 | "version": "1.0.5", 565 | "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", 566 | "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", 567 | "dev": true 568 | }, 569 | "node_modules/argparse": { 570 | "version": "2.0.1", 571 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 572 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 573 | "dev": true 574 | }, 575 | "node_modules/bluebird": { 576 | "version": "3.7.2", 577 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", 578 | "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", 579 | "dev": true 580 | }, 581 | "node_modules/catharsis": { 582 | "version": "0.9.0", 583 | "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", 584 | "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", 585 | "dev": true, 586 | "dependencies": { 587 | "lodash": "^4.17.15" 588 | }, 589 | "engines": { 590 | "node": ">= 10" 591 | } 592 | }, 593 | "node_modules/entities": { 594 | "version": "2.1.0", 595 | "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", 596 | "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", 597 | "dev": true, 598 | "funding": { 599 | "url": "https://github.com/fb55/entities?sponsor=1" 600 | } 601 | }, 602 | "node_modules/esbuild": { 603 | "version": "0.19.7", 604 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.7.tgz", 605 | "integrity": "sha512-6brbTZVqxhqgbpqBR5MzErImcpA0SQdoKOkcWK/U30HtQxnokIpG3TX2r0IJqbFUzqLjhU/zC1S5ndgakObVCQ==", 606 | "dev": true, 607 | "hasInstallScript": true, 608 | "bin": { 609 | "esbuild": "bin/esbuild" 610 | }, 611 | "engines": { 612 | "node": ">=12" 613 | }, 614 | "optionalDependencies": { 615 | "@esbuild/android-arm": "0.19.7", 616 | "@esbuild/android-arm64": "0.19.7", 617 | "@esbuild/android-x64": "0.19.7", 618 | "@esbuild/darwin-arm64": "0.19.7", 619 | "@esbuild/darwin-x64": "0.19.7", 620 | "@esbuild/freebsd-arm64": "0.19.7", 621 | "@esbuild/freebsd-x64": "0.19.7", 622 | "@esbuild/linux-arm": "0.19.7", 623 | "@esbuild/linux-arm64": "0.19.7", 624 | "@esbuild/linux-ia32": "0.19.7", 625 | "@esbuild/linux-loong64": "0.19.7", 626 | "@esbuild/linux-mips64el": "0.19.7", 627 | "@esbuild/linux-ppc64": "0.19.7", 628 | "@esbuild/linux-riscv64": "0.19.7", 629 | "@esbuild/linux-s390x": "0.19.7", 630 | "@esbuild/linux-x64": "0.19.7", 631 | "@esbuild/netbsd-x64": "0.19.7", 632 | "@esbuild/openbsd-x64": "0.19.7", 633 | "@esbuild/sunos-x64": "0.19.7", 634 | "@esbuild/win32-arm64": "0.19.7", 635 | "@esbuild/win32-ia32": "0.19.7", 636 | "@esbuild/win32-x64": "0.19.7" 637 | } 638 | }, 639 | "node_modules/escape-string-regexp": { 640 | "version": "2.0.0", 641 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", 642 | "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", 643 | "dev": true, 644 | "engines": { 645 | "node": ">=8" 646 | } 647 | }, 648 | "node_modules/fsevents": { 649 | "version": "2.3.3", 650 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 651 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 652 | "dev": true, 653 | "hasInstallScript": true, 654 | "optional": true, 655 | "os": [ 656 | "darwin" 657 | ], 658 | "engines": { 659 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 660 | } 661 | }, 662 | "node_modules/graceful-fs": { 663 | "version": "4.2.11", 664 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 665 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 666 | "dev": true 667 | }, 668 | "node_modules/js2xmlparser": { 669 | "version": "4.0.2", 670 | "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", 671 | "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", 672 | "dev": true, 673 | "dependencies": { 674 | "xmlcreate": "^2.0.4" 675 | } 676 | }, 677 | "node_modules/jsdoc": { 678 | "version": "4.0.2", 679 | "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", 680 | "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", 681 | "dev": true, 682 | "dependencies": { 683 | "@babel/parser": "^7.20.15", 684 | "@jsdoc/salty": "^0.2.1", 685 | "@types/markdown-it": "^12.2.3", 686 | "bluebird": "^3.7.2", 687 | "catharsis": "^0.9.0", 688 | "escape-string-regexp": "^2.0.0", 689 | "js2xmlparser": "^4.0.2", 690 | "klaw": "^3.0.0", 691 | "markdown-it": "^12.3.2", 692 | "markdown-it-anchor": "^8.4.1", 693 | "marked": "^4.0.10", 694 | "mkdirp": "^1.0.4", 695 | "requizzle": "^0.2.3", 696 | "strip-json-comments": "^3.1.0", 697 | "underscore": "~1.13.2" 698 | }, 699 | "bin": { 700 | "jsdoc": "jsdoc.js" 701 | }, 702 | "engines": { 703 | "node": ">=12.0.0" 704 | } 705 | }, 706 | "node_modules/klaw": { 707 | "version": "3.0.0", 708 | "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", 709 | "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", 710 | "dev": true, 711 | "dependencies": { 712 | "graceful-fs": "^4.1.9" 713 | } 714 | }, 715 | "node_modules/linkify-it": { 716 | "version": "3.0.3", 717 | "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", 718 | "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", 719 | "dev": true, 720 | "dependencies": { 721 | "uc.micro": "^1.0.1" 722 | } 723 | }, 724 | "node_modules/lodash": { 725 | "version": "4.17.21", 726 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 727 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 728 | "dev": true 729 | }, 730 | "node_modules/markdown-it": { 731 | "version": "12.3.2", 732 | "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", 733 | "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", 734 | "dev": true, 735 | "dependencies": { 736 | "argparse": "^2.0.1", 737 | "entities": "~2.1.0", 738 | "linkify-it": "^3.0.1", 739 | "mdurl": "^1.0.1", 740 | "uc.micro": "^1.0.5" 741 | }, 742 | "bin": { 743 | "markdown-it": "bin/markdown-it.js" 744 | } 745 | }, 746 | "node_modules/markdown-it-anchor": { 747 | "version": "8.6.7", 748 | "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", 749 | "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", 750 | "dev": true, 751 | "peerDependencies": { 752 | "@types/markdown-it": "*", 753 | "markdown-it": "*" 754 | } 755 | }, 756 | "node_modules/marked": { 757 | "version": "4.3.0", 758 | "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", 759 | "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", 760 | "dev": true, 761 | "bin": { 762 | "marked": "bin/marked.js" 763 | }, 764 | "engines": { 765 | "node": ">= 12" 766 | } 767 | }, 768 | "node_modules/mdurl": { 769 | "version": "1.0.1", 770 | "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", 771 | "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", 772 | "dev": true 773 | }, 774 | "node_modules/mkdirp": { 775 | "version": "1.0.4", 776 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 777 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", 778 | "dev": true, 779 | "bin": { 780 | "mkdirp": "bin/cmd.js" 781 | }, 782 | "engines": { 783 | "node": ">=10" 784 | } 785 | }, 786 | "node_modules/nanoid": { 787 | "version": "3.3.7", 788 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", 789 | "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", 790 | "dev": true, 791 | "funding": [ 792 | { 793 | "type": "github", 794 | "url": "https://github.com/sponsors/ai" 795 | } 796 | ], 797 | "bin": { 798 | "nanoid": "bin/nanoid.cjs" 799 | }, 800 | "engines": { 801 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 802 | } 803 | }, 804 | "node_modules/picocolors": { 805 | "version": "1.0.0", 806 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 807 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 808 | "dev": true 809 | }, 810 | "node_modules/postcss": { 811 | "version": "8.4.31", 812 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", 813 | "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", 814 | "dev": true, 815 | "funding": [ 816 | { 817 | "type": "opencollective", 818 | "url": "https://opencollective.com/postcss/" 819 | }, 820 | { 821 | "type": "tidelift", 822 | "url": "https://tidelift.com/funding/github/npm/postcss" 823 | }, 824 | { 825 | "type": "github", 826 | "url": "https://github.com/sponsors/ai" 827 | } 828 | ], 829 | "dependencies": { 830 | "nanoid": "^3.3.6", 831 | "picocolors": "^1.0.0", 832 | "source-map-js": "^1.0.2" 833 | }, 834 | "engines": { 835 | "node": "^10 || ^12 || >=14" 836 | } 837 | }, 838 | "node_modules/requizzle": { 839 | "version": "0.2.4", 840 | "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", 841 | "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", 842 | "dev": true, 843 | "dependencies": { 844 | "lodash": "^4.17.21" 845 | } 846 | }, 847 | "node_modules/rollup": { 848 | "version": "4.5.2", 849 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.5.2.tgz", 850 | "integrity": "sha512-CRK1uoROBfkcqrZKyaFcqCcZWNsvJ6yVYZkqTlRocZhO2s5yER6Z3f/QaYtO8RGyloPnmhwgzuPQpNGeK210xQ==", 851 | "dev": true, 852 | "bin": { 853 | "rollup": "dist/bin/rollup" 854 | }, 855 | "engines": { 856 | "node": ">=18.0.0", 857 | "npm": ">=8.0.0" 858 | }, 859 | "optionalDependencies": { 860 | "@rollup/rollup-android-arm-eabi": "4.5.2", 861 | "@rollup/rollup-android-arm64": "4.5.2", 862 | "@rollup/rollup-darwin-arm64": "4.5.2", 863 | "@rollup/rollup-darwin-x64": "4.5.2", 864 | "@rollup/rollup-linux-arm-gnueabihf": "4.5.2", 865 | "@rollup/rollup-linux-arm64-gnu": "4.5.2", 866 | "@rollup/rollup-linux-arm64-musl": "4.5.2", 867 | "@rollup/rollup-linux-x64-gnu": "4.5.2", 868 | "@rollup/rollup-linux-x64-musl": "4.5.2", 869 | "@rollup/rollup-win32-arm64-msvc": "4.5.2", 870 | "@rollup/rollup-win32-ia32-msvc": "4.5.2", 871 | "@rollup/rollup-win32-x64-msvc": "4.5.2", 872 | "fsevents": "~2.3.2" 873 | } 874 | }, 875 | "node_modules/source-map-js": { 876 | "version": "1.0.2", 877 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 878 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 879 | "dev": true, 880 | "engines": { 881 | "node": ">=0.10.0" 882 | } 883 | }, 884 | "node_modules/strip-json-comments": { 885 | "version": "3.1.1", 886 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 887 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 888 | "dev": true, 889 | "engines": { 890 | "node": ">=8" 891 | }, 892 | "funding": { 893 | "url": "https://github.com/sponsors/sindresorhus" 894 | } 895 | }, 896 | "node_modules/uc.micro": { 897 | "version": "1.0.6", 898 | "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", 899 | "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", 900 | "dev": true 901 | }, 902 | "node_modules/underscore": { 903 | "version": "1.13.6", 904 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", 905 | "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", 906 | "dev": true 907 | }, 908 | "node_modules/vite": { 909 | "version": "5.0.2", 910 | "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.2.tgz", 911 | "integrity": "sha512-6CCq1CAJCNM1ya2ZZA7+jS2KgnhbzvxakmlIjN24cF/PXhRMzpM/z8QgsVJA/Dm5fWUWnVEsmtBoMhmerPxT0g==", 912 | "dev": true, 913 | "dependencies": { 914 | "esbuild": "^0.19.3", 915 | "postcss": "^8.4.31", 916 | "rollup": "^4.2.0" 917 | }, 918 | "bin": { 919 | "vite": "bin/vite.js" 920 | }, 921 | "engines": { 922 | "node": "^18.0.0 || >=20.0.0" 923 | }, 924 | "funding": { 925 | "url": "https://github.com/vitejs/vite?sponsor=1" 926 | }, 927 | "optionalDependencies": { 928 | "fsevents": "~2.3.3" 929 | }, 930 | "peerDependencies": { 931 | "@types/node": "^18.0.0 || >=20.0.0", 932 | "less": "*", 933 | "lightningcss": "^1.21.0", 934 | "sass": "*", 935 | "stylus": "*", 936 | "sugarss": "*", 937 | "terser": "^5.4.0" 938 | }, 939 | "peerDependenciesMeta": { 940 | "@types/node": { 941 | "optional": true 942 | }, 943 | "less": { 944 | "optional": true 945 | }, 946 | "lightningcss": { 947 | "optional": true 948 | }, 949 | "sass": { 950 | "optional": true 951 | }, 952 | "stylus": { 953 | "optional": true 954 | }, 955 | "sugarss": { 956 | "optional": true 957 | }, 958 | "terser": { 959 | "optional": true 960 | } 961 | } 962 | }, 963 | "node_modules/xmlcreate": { 964 | "version": "2.0.4", 965 | "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", 966 | "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", 967 | "dev": true 968 | } 969 | } 970 | } 971 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "state-radio", 3 | "version": "1.0.5", 4 | "type": "module", 5 | "description": "A state management library that let's you tune to your application state like a radio set", 6 | "main": "dist/state-radio.umd.cjs", 7 | "module": "index.js", 8 | "types": "dist/state-radio.d.ts", 9 | "files": [ 10 | "dist", 11 | "lib", 12 | "index.js" 13 | ], 14 | "directories": { 15 | "doc": "docs", 16 | "example": "example" 17 | }, 18 | "scripts": { 19 | "build": "vite build", 20 | "docs": "jsdoc -d ./docs index.js" 21 | }, 22 | "devDependencies": { 23 | "jsdoc": "^4.0.2", 24 | "terser": "^5.26.0", 25 | "vite": "^5.0.0" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "git+https://github.com/Hussseinkizz/state-radio.git" 30 | }, 31 | "keywords": [ 32 | "local", 33 | "storage", 34 | "react", 35 | "kizz", 36 | "useLocalStorage", 37 | "state", 38 | "state management", 39 | "signals", 40 | "pubsub", 41 | "storage", 42 | "js" 43 | ], 44 | "author": "Hussein Kizz", 45 | "license": "MIT", 46 | "bugs": { 47 | "url": "https://github.com/Hussseinkizz/state-radio/issues" 48 | }, 49 | "homepage": "https://github.com/Hussseinkizz/state-radio#readme", 50 | "dependencies": { 51 | "@kizz-js/use-local-storage": "^1.0.1" 52 | } 53 | } -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | '@kizz-js/use-local-storage': 9 | specifier: ^1.0.1 10 | version: 1.0.1 11 | 12 | devDependencies: 13 | jsdoc: 14 | specifier: ^4.0.2 15 | version: 4.0.2 16 | terser: 17 | specifier: ^5.26.0 18 | version: 5.26.0 19 | vite: 20 | specifier: ^5.0.0 21 | version: 5.0.11(terser@5.26.0) 22 | 23 | packages: 24 | 25 | /@babel/helper-string-parser@7.23.4: 26 | resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} 27 | engines: {node: '>=6.9.0'} 28 | dev: true 29 | 30 | /@babel/helper-validator-identifier@7.22.20: 31 | resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} 32 | engines: {node: '>=6.9.0'} 33 | dev: true 34 | 35 | /@babel/parser@7.23.6: 36 | resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==} 37 | engines: {node: '>=6.0.0'} 38 | hasBin: true 39 | dependencies: 40 | '@babel/types': 7.23.6 41 | dev: true 42 | 43 | /@babel/types@7.23.6: 44 | resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==} 45 | engines: {node: '>=6.9.0'} 46 | dependencies: 47 | '@babel/helper-string-parser': 7.23.4 48 | '@babel/helper-validator-identifier': 7.22.20 49 | to-fast-properties: 2.0.0 50 | dev: true 51 | 52 | /@esbuild/aix-ppc64@0.19.11: 53 | resolution: {integrity: sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==} 54 | engines: {node: '>=12'} 55 | cpu: [ppc64] 56 | os: [aix] 57 | requiresBuild: true 58 | dev: true 59 | optional: true 60 | 61 | /@esbuild/android-arm64@0.19.11: 62 | resolution: {integrity: sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==} 63 | engines: {node: '>=12'} 64 | cpu: [arm64] 65 | os: [android] 66 | requiresBuild: true 67 | dev: true 68 | optional: true 69 | 70 | /@esbuild/android-arm@0.19.11: 71 | resolution: {integrity: sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==} 72 | engines: {node: '>=12'} 73 | cpu: [arm] 74 | os: [android] 75 | requiresBuild: true 76 | dev: true 77 | optional: true 78 | 79 | /@esbuild/android-x64@0.19.11: 80 | resolution: {integrity: sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==} 81 | engines: {node: '>=12'} 82 | cpu: [x64] 83 | os: [android] 84 | requiresBuild: true 85 | dev: true 86 | optional: true 87 | 88 | /@esbuild/darwin-arm64@0.19.11: 89 | resolution: {integrity: sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==} 90 | engines: {node: '>=12'} 91 | cpu: [arm64] 92 | os: [darwin] 93 | requiresBuild: true 94 | dev: true 95 | optional: true 96 | 97 | /@esbuild/darwin-x64@0.19.11: 98 | resolution: {integrity: sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==} 99 | engines: {node: '>=12'} 100 | cpu: [x64] 101 | os: [darwin] 102 | requiresBuild: true 103 | dev: true 104 | optional: true 105 | 106 | /@esbuild/freebsd-arm64@0.19.11: 107 | resolution: {integrity: sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==} 108 | engines: {node: '>=12'} 109 | cpu: [arm64] 110 | os: [freebsd] 111 | requiresBuild: true 112 | dev: true 113 | optional: true 114 | 115 | /@esbuild/freebsd-x64@0.19.11: 116 | resolution: {integrity: sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==} 117 | engines: {node: '>=12'} 118 | cpu: [x64] 119 | os: [freebsd] 120 | requiresBuild: true 121 | dev: true 122 | optional: true 123 | 124 | /@esbuild/linux-arm64@0.19.11: 125 | resolution: {integrity: sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==} 126 | engines: {node: '>=12'} 127 | cpu: [arm64] 128 | os: [linux] 129 | requiresBuild: true 130 | dev: true 131 | optional: true 132 | 133 | /@esbuild/linux-arm@0.19.11: 134 | resolution: {integrity: sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==} 135 | engines: {node: '>=12'} 136 | cpu: [arm] 137 | os: [linux] 138 | requiresBuild: true 139 | dev: true 140 | optional: true 141 | 142 | /@esbuild/linux-ia32@0.19.11: 143 | resolution: {integrity: sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==} 144 | engines: {node: '>=12'} 145 | cpu: [ia32] 146 | os: [linux] 147 | requiresBuild: true 148 | dev: true 149 | optional: true 150 | 151 | /@esbuild/linux-loong64@0.19.11: 152 | resolution: {integrity: sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==} 153 | engines: {node: '>=12'} 154 | cpu: [loong64] 155 | os: [linux] 156 | requiresBuild: true 157 | dev: true 158 | optional: true 159 | 160 | /@esbuild/linux-mips64el@0.19.11: 161 | resolution: {integrity: sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==} 162 | engines: {node: '>=12'} 163 | cpu: [mips64el] 164 | os: [linux] 165 | requiresBuild: true 166 | dev: true 167 | optional: true 168 | 169 | /@esbuild/linux-ppc64@0.19.11: 170 | resolution: {integrity: sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==} 171 | engines: {node: '>=12'} 172 | cpu: [ppc64] 173 | os: [linux] 174 | requiresBuild: true 175 | dev: true 176 | optional: true 177 | 178 | /@esbuild/linux-riscv64@0.19.11: 179 | resolution: {integrity: sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==} 180 | engines: {node: '>=12'} 181 | cpu: [riscv64] 182 | os: [linux] 183 | requiresBuild: true 184 | dev: true 185 | optional: true 186 | 187 | /@esbuild/linux-s390x@0.19.11: 188 | resolution: {integrity: sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==} 189 | engines: {node: '>=12'} 190 | cpu: [s390x] 191 | os: [linux] 192 | requiresBuild: true 193 | dev: true 194 | optional: true 195 | 196 | /@esbuild/linux-x64@0.19.11: 197 | resolution: {integrity: sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==} 198 | engines: {node: '>=12'} 199 | cpu: [x64] 200 | os: [linux] 201 | requiresBuild: true 202 | dev: true 203 | optional: true 204 | 205 | /@esbuild/netbsd-x64@0.19.11: 206 | resolution: {integrity: sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==} 207 | engines: {node: '>=12'} 208 | cpu: [x64] 209 | os: [netbsd] 210 | requiresBuild: true 211 | dev: true 212 | optional: true 213 | 214 | /@esbuild/openbsd-x64@0.19.11: 215 | resolution: {integrity: sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==} 216 | engines: {node: '>=12'} 217 | cpu: [x64] 218 | os: [openbsd] 219 | requiresBuild: true 220 | dev: true 221 | optional: true 222 | 223 | /@esbuild/sunos-x64@0.19.11: 224 | resolution: {integrity: sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==} 225 | engines: {node: '>=12'} 226 | cpu: [x64] 227 | os: [sunos] 228 | requiresBuild: true 229 | dev: true 230 | optional: true 231 | 232 | /@esbuild/win32-arm64@0.19.11: 233 | resolution: {integrity: sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==} 234 | engines: {node: '>=12'} 235 | cpu: [arm64] 236 | os: [win32] 237 | requiresBuild: true 238 | dev: true 239 | optional: true 240 | 241 | /@esbuild/win32-ia32@0.19.11: 242 | resolution: {integrity: sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==} 243 | engines: {node: '>=12'} 244 | cpu: [ia32] 245 | os: [win32] 246 | requiresBuild: true 247 | dev: true 248 | optional: true 249 | 250 | /@esbuild/win32-x64@0.19.11: 251 | resolution: {integrity: sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==} 252 | engines: {node: '>=12'} 253 | cpu: [x64] 254 | os: [win32] 255 | requiresBuild: true 256 | dev: true 257 | optional: true 258 | 259 | /@jridgewell/gen-mapping@0.3.3: 260 | resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} 261 | engines: {node: '>=6.0.0'} 262 | dependencies: 263 | '@jridgewell/set-array': 1.1.2 264 | '@jridgewell/sourcemap-codec': 1.4.15 265 | '@jridgewell/trace-mapping': 0.3.20 266 | dev: true 267 | 268 | /@jridgewell/resolve-uri@3.1.1: 269 | resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} 270 | engines: {node: '>=6.0.0'} 271 | dev: true 272 | 273 | /@jridgewell/set-array@1.1.2: 274 | resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} 275 | engines: {node: '>=6.0.0'} 276 | dev: true 277 | 278 | /@jridgewell/source-map@0.3.5: 279 | resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} 280 | dependencies: 281 | '@jridgewell/gen-mapping': 0.3.3 282 | '@jridgewell/trace-mapping': 0.3.20 283 | dev: true 284 | 285 | /@jridgewell/sourcemap-codec@1.4.15: 286 | resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} 287 | dev: true 288 | 289 | /@jridgewell/trace-mapping@0.3.20: 290 | resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} 291 | dependencies: 292 | '@jridgewell/resolve-uri': 3.1.1 293 | '@jridgewell/sourcemap-codec': 1.4.15 294 | dev: true 295 | 296 | /@jsdoc/salty@0.2.7: 297 | resolution: {integrity: sha512-mh8LbS9d4Jq84KLw8pzho7XC2q2/IJGiJss3xwRoLD1A+EE16SjN4PfaG4jRCzKegTFLlN0Zd8SdUPE6XdoPFg==} 298 | engines: {node: '>=v12.0.0'} 299 | dependencies: 300 | lodash: 4.17.21 301 | dev: true 302 | 303 | /@kizz-js/use-local-storage@1.0.1: 304 | resolution: {integrity: sha512-mEJPIVEY/VUk/mIu9dj7AhOjmGgg4INfPEeum3pkivu/BAXR3xKBEVN1877Cp2a0Jj9qgjjI+9OeWEvQ4wAaJA==} 305 | dev: false 306 | 307 | /@rollup/rollup-android-arm-eabi@4.9.4: 308 | resolution: {integrity: sha512-ub/SN3yWqIv5CWiAZPHVS1DloyZsJbtXmX4HxUTIpS0BHm9pW5iYBo2mIZi+hE3AeiTzHz33blwSnhdUo+9NpA==} 309 | cpu: [arm] 310 | os: [android] 311 | requiresBuild: true 312 | dev: true 313 | optional: true 314 | 315 | /@rollup/rollup-android-arm64@4.9.4: 316 | resolution: {integrity: sha512-ehcBrOR5XTl0W0t2WxfTyHCR/3Cq2jfb+I4W+Ch8Y9b5G+vbAecVv0Fx/J1QKktOrgUYsIKxWAKgIpvw56IFNA==} 317 | cpu: [arm64] 318 | os: [android] 319 | requiresBuild: true 320 | dev: true 321 | optional: true 322 | 323 | /@rollup/rollup-darwin-arm64@4.9.4: 324 | resolution: {integrity: sha512-1fzh1lWExwSTWy8vJPnNbNM02WZDS8AW3McEOb7wW+nPChLKf3WG2aG7fhaUmfX5FKw9zhsF5+MBwArGyNM7NA==} 325 | cpu: [arm64] 326 | os: [darwin] 327 | requiresBuild: true 328 | dev: true 329 | optional: true 330 | 331 | /@rollup/rollup-darwin-x64@4.9.4: 332 | resolution: {integrity: sha512-Gc6cukkF38RcYQ6uPdiXi70JB0f29CwcQ7+r4QpfNpQFVHXRd0DfWFidoGxjSx1DwOETM97JPz1RXL5ISSB0pA==} 333 | cpu: [x64] 334 | os: [darwin] 335 | requiresBuild: true 336 | dev: true 337 | optional: true 338 | 339 | /@rollup/rollup-linux-arm-gnueabihf@4.9.4: 340 | resolution: {integrity: sha512-g21RTeFzoTl8GxosHbnQZ0/JkuFIB13C3T7Y0HtKzOXmoHhewLbVTFBQZu+z5m9STH6FZ7L/oPgU4Nm5ErN2fw==} 341 | cpu: [arm] 342 | os: [linux] 343 | requiresBuild: true 344 | dev: true 345 | optional: true 346 | 347 | /@rollup/rollup-linux-arm64-gnu@4.9.4: 348 | resolution: {integrity: sha512-TVYVWD/SYwWzGGnbfTkrNpdE4HON46orgMNHCivlXmlsSGQOx/OHHYiQcMIOx38/GWgwr/po2LBn7wypkWw/Mg==} 349 | cpu: [arm64] 350 | os: [linux] 351 | requiresBuild: true 352 | dev: true 353 | optional: true 354 | 355 | /@rollup/rollup-linux-arm64-musl@4.9.4: 356 | resolution: {integrity: sha512-XcKvuendwizYYhFxpvQ3xVpzje2HHImzg33wL9zvxtj77HvPStbSGI9czrdbfrf8DGMcNNReH9pVZv8qejAQ5A==} 357 | cpu: [arm64] 358 | os: [linux] 359 | requiresBuild: true 360 | dev: true 361 | optional: true 362 | 363 | /@rollup/rollup-linux-riscv64-gnu@4.9.4: 364 | resolution: {integrity: sha512-LFHS/8Q+I9YA0yVETyjonMJ3UA+DczeBd/MqNEzsGSTdNvSJa1OJZcSH8GiXLvcizgp9AlHs2walqRcqzjOi3A==} 365 | cpu: [riscv64] 366 | os: [linux] 367 | requiresBuild: true 368 | dev: true 369 | optional: true 370 | 371 | /@rollup/rollup-linux-x64-gnu@4.9.4: 372 | resolution: {integrity: sha512-dIYgo+j1+yfy81i0YVU5KnQrIJZE8ERomx17ReU4GREjGtDW4X+nvkBak2xAUpyqLs4eleDSj3RrV72fQos7zw==} 373 | cpu: [x64] 374 | os: [linux] 375 | requiresBuild: true 376 | dev: true 377 | optional: true 378 | 379 | /@rollup/rollup-linux-x64-musl@4.9.4: 380 | resolution: {integrity: sha512-RoaYxjdHQ5TPjaPrLsfKqR3pakMr3JGqZ+jZM0zP2IkDtsGa4CqYaWSfQmZVgFUCgLrTnzX+cnHS3nfl+kB6ZQ==} 381 | cpu: [x64] 382 | os: [linux] 383 | requiresBuild: true 384 | dev: true 385 | optional: true 386 | 387 | /@rollup/rollup-win32-arm64-msvc@4.9.4: 388 | resolution: {integrity: sha512-T8Q3XHV+Jjf5e49B4EAaLKV74BbX7/qYBRQ8Wop/+TyyU0k+vSjiLVSHNWdVd1goMjZcbhDmYZUYW5RFqkBNHQ==} 389 | cpu: [arm64] 390 | os: [win32] 391 | requiresBuild: true 392 | dev: true 393 | optional: true 394 | 395 | /@rollup/rollup-win32-ia32-msvc@4.9.4: 396 | resolution: {integrity: sha512-z+JQ7JirDUHAsMecVydnBPWLwJjbppU+7LZjffGf+Jvrxq+dVjIE7By163Sc9DKc3ADSU50qPVw0KonBS+a+HQ==} 397 | cpu: [ia32] 398 | os: [win32] 399 | requiresBuild: true 400 | dev: true 401 | optional: true 402 | 403 | /@rollup/rollup-win32-x64-msvc@4.9.4: 404 | resolution: {integrity: sha512-LfdGXCV9rdEify1oxlN9eamvDSjv9md9ZVMAbNHA87xqIfFCxImxan9qZ8+Un54iK2nnqPlbnSi4R54ONtbWBw==} 405 | cpu: [x64] 406 | os: [win32] 407 | requiresBuild: true 408 | dev: true 409 | optional: true 410 | 411 | /@types/estree@1.0.5: 412 | resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} 413 | dev: true 414 | 415 | /@types/linkify-it@3.0.5: 416 | resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==} 417 | dev: true 418 | 419 | /@types/markdown-it@12.2.3: 420 | resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==} 421 | dependencies: 422 | '@types/linkify-it': 3.0.5 423 | '@types/mdurl': 1.0.5 424 | dev: true 425 | 426 | /@types/mdurl@1.0.5: 427 | resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==} 428 | dev: true 429 | 430 | /acorn@8.11.3: 431 | resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} 432 | engines: {node: '>=0.4.0'} 433 | hasBin: true 434 | dev: true 435 | 436 | /argparse@2.0.1: 437 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 438 | dev: true 439 | 440 | /bluebird@3.7.2: 441 | resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} 442 | dev: true 443 | 444 | /buffer-from@1.1.2: 445 | resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} 446 | dev: true 447 | 448 | /catharsis@0.9.0: 449 | resolution: {integrity: sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==} 450 | engines: {node: '>= 10'} 451 | dependencies: 452 | lodash: 4.17.21 453 | dev: true 454 | 455 | /commander@2.20.3: 456 | resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} 457 | dev: true 458 | 459 | /entities@2.1.0: 460 | resolution: {integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==} 461 | dev: true 462 | 463 | /esbuild@0.19.11: 464 | resolution: {integrity: sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==} 465 | engines: {node: '>=12'} 466 | hasBin: true 467 | requiresBuild: true 468 | optionalDependencies: 469 | '@esbuild/aix-ppc64': 0.19.11 470 | '@esbuild/android-arm': 0.19.11 471 | '@esbuild/android-arm64': 0.19.11 472 | '@esbuild/android-x64': 0.19.11 473 | '@esbuild/darwin-arm64': 0.19.11 474 | '@esbuild/darwin-x64': 0.19.11 475 | '@esbuild/freebsd-arm64': 0.19.11 476 | '@esbuild/freebsd-x64': 0.19.11 477 | '@esbuild/linux-arm': 0.19.11 478 | '@esbuild/linux-arm64': 0.19.11 479 | '@esbuild/linux-ia32': 0.19.11 480 | '@esbuild/linux-loong64': 0.19.11 481 | '@esbuild/linux-mips64el': 0.19.11 482 | '@esbuild/linux-ppc64': 0.19.11 483 | '@esbuild/linux-riscv64': 0.19.11 484 | '@esbuild/linux-s390x': 0.19.11 485 | '@esbuild/linux-x64': 0.19.11 486 | '@esbuild/netbsd-x64': 0.19.11 487 | '@esbuild/openbsd-x64': 0.19.11 488 | '@esbuild/sunos-x64': 0.19.11 489 | '@esbuild/win32-arm64': 0.19.11 490 | '@esbuild/win32-ia32': 0.19.11 491 | '@esbuild/win32-x64': 0.19.11 492 | dev: true 493 | 494 | /escape-string-regexp@2.0.0: 495 | resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} 496 | engines: {node: '>=8'} 497 | dev: true 498 | 499 | /fsevents@2.3.3: 500 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 501 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 502 | os: [darwin] 503 | requiresBuild: true 504 | dev: true 505 | optional: true 506 | 507 | /graceful-fs@4.2.11: 508 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 509 | dev: true 510 | 511 | /js2xmlparser@4.0.2: 512 | resolution: {integrity: sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==} 513 | dependencies: 514 | xmlcreate: 2.0.4 515 | dev: true 516 | 517 | /jsdoc@4.0.2: 518 | resolution: {integrity: sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==} 519 | engines: {node: '>=12.0.0'} 520 | hasBin: true 521 | dependencies: 522 | '@babel/parser': 7.23.6 523 | '@jsdoc/salty': 0.2.7 524 | '@types/markdown-it': 12.2.3 525 | bluebird: 3.7.2 526 | catharsis: 0.9.0 527 | escape-string-regexp: 2.0.0 528 | js2xmlparser: 4.0.2 529 | klaw: 3.0.0 530 | markdown-it: 12.3.2 531 | markdown-it-anchor: 8.6.7(@types/markdown-it@12.2.3)(markdown-it@12.3.2) 532 | marked: 4.3.0 533 | mkdirp: 1.0.4 534 | requizzle: 0.2.4 535 | strip-json-comments: 3.1.1 536 | underscore: 1.13.6 537 | dev: true 538 | 539 | /klaw@3.0.0: 540 | resolution: {integrity: sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==} 541 | dependencies: 542 | graceful-fs: 4.2.11 543 | dev: true 544 | 545 | /linkify-it@3.0.3: 546 | resolution: {integrity: sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==} 547 | dependencies: 548 | uc.micro: 1.0.6 549 | dev: true 550 | 551 | /lodash@4.17.21: 552 | resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} 553 | dev: true 554 | 555 | /markdown-it-anchor@8.6.7(@types/markdown-it@12.2.3)(markdown-it@12.3.2): 556 | resolution: {integrity: sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==} 557 | peerDependencies: 558 | '@types/markdown-it': '*' 559 | markdown-it: '*' 560 | dependencies: 561 | '@types/markdown-it': 12.2.3 562 | markdown-it: 12.3.2 563 | dev: true 564 | 565 | /markdown-it@12.3.2: 566 | resolution: {integrity: sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==} 567 | hasBin: true 568 | dependencies: 569 | argparse: 2.0.1 570 | entities: 2.1.0 571 | linkify-it: 3.0.3 572 | mdurl: 1.0.1 573 | uc.micro: 1.0.6 574 | dev: true 575 | 576 | /marked@4.3.0: 577 | resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} 578 | engines: {node: '>= 12'} 579 | hasBin: true 580 | dev: true 581 | 582 | /mdurl@1.0.1: 583 | resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} 584 | dev: true 585 | 586 | /mkdirp@1.0.4: 587 | resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} 588 | engines: {node: '>=10'} 589 | hasBin: true 590 | dev: true 591 | 592 | /nanoid@3.3.7: 593 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 594 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 595 | hasBin: true 596 | dev: true 597 | 598 | /picocolors@1.0.0: 599 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 600 | dev: true 601 | 602 | /postcss@8.4.33: 603 | resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==} 604 | engines: {node: ^10 || ^12 || >=14} 605 | dependencies: 606 | nanoid: 3.3.7 607 | picocolors: 1.0.0 608 | source-map-js: 1.0.2 609 | dev: true 610 | 611 | /requizzle@0.2.4: 612 | resolution: {integrity: sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==} 613 | dependencies: 614 | lodash: 4.17.21 615 | dev: true 616 | 617 | /rollup@4.9.4: 618 | resolution: {integrity: sha512-2ztU7pY/lrQyXSCnnoU4ICjT/tCG9cdH3/G25ERqE3Lst6vl2BCM5hL2Nw+sslAvAf+ccKsAq1SkKQALyqhR7g==} 619 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 620 | hasBin: true 621 | dependencies: 622 | '@types/estree': 1.0.5 623 | optionalDependencies: 624 | '@rollup/rollup-android-arm-eabi': 4.9.4 625 | '@rollup/rollup-android-arm64': 4.9.4 626 | '@rollup/rollup-darwin-arm64': 4.9.4 627 | '@rollup/rollup-darwin-x64': 4.9.4 628 | '@rollup/rollup-linux-arm-gnueabihf': 4.9.4 629 | '@rollup/rollup-linux-arm64-gnu': 4.9.4 630 | '@rollup/rollup-linux-arm64-musl': 4.9.4 631 | '@rollup/rollup-linux-riscv64-gnu': 4.9.4 632 | '@rollup/rollup-linux-x64-gnu': 4.9.4 633 | '@rollup/rollup-linux-x64-musl': 4.9.4 634 | '@rollup/rollup-win32-arm64-msvc': 4.9.4 635 | '@rollup/rollup-win32-ia32-msvc': 4.9.4 636 | '@rollup/rollup-win32-x64-msvc': 4.9.4 637 | fsevents: 2.3.3 638 | dev: true 639 | 640 | /source-map-js@1.0.2: 641 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 642 | engines: {node: '>=0.10.0'} 643 | dev: true 644 | 645 | /source-map-support@0.5.21: 646 | resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} 647 | dependencies: 648 | buffer-from: 1.1.2 649 | source-map: 0.6.1 650 | dev: true 651 | 652 | /source-map@0.6.1: 653 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 654 | engines: {node: '>=0.10.0'} 655 | dev: true 656 | 657 | /strip-json-comments@3.1.1: 658 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 659 | engines: {node: '>=8'} 660 | dev: true 661 | 662 | /terser@5.26.0: 663 | resolution: {integrity: sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==} 664 | engines: {node: '>=10'} 665 | hasBin: true 666 | dependencies: 667 | '@jridgewell/source-map': 0.3.5 668 | acorn: 8.11.3 669 | commander: 2.20.3 670 | source-map-support: 0.5.21 671 | dev: true 672 | 673 | /to-fast-properties@2.0.0: 674 | resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} 675 | engines: {node: '>=4'} 676 | dev: true 677 | 678 | /uc.micro@1.0.6: 679 | resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} 680 | dev: true 681 | 682 | /underscore@1.13.6: 683 | resolution: {integrity: sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==} 684 | dev: true 685 | 686 | /vite@5.0.11(terser@5.26.0): 687 | resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} 688 | engines: {node: ^18.0.0 || >=20.0.0} 689 | hasBin: true 690 | peerDependencies: 691 | '@types/node': ^18.0.0 || >=20.0.0 692 | less: '*' 693 | lightningcss: ^1.21.0 694 | sass: '*' 695 | stylus: '*' 696 | sugarss: '*' 697 | terser: ^5.4.0 698 | peerDependenciesMeta: 699 | '@types/node': 700 | optional: true 701 | less: 702 | optional: true 703 | lightningcss: 704 | optional: true 705 | sass: 706 | optional: true 707 | stylus: 708 | optional: true 709 | sugarss: 710 | optional: true 711 | terser: 712 | optional: true 713 | dependencies: 714 | esbuild: 0.19.11 715 | postcss: 8.4.33 716 | rollup: 4.9.4 717 | terser: 5.26.0 718 | optionalDependencies: 719 | fsevents: 2.3.3 720 | dev: true 721 | 722 | /xmlcreate@2.0.4: 723 | resolution: {integrity: sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==} 724 | dev: true 725 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # State Radio 📻 2 | 3 | A state management library that let's you tune to your state channels via subscriptions and listen to state updates with fine tuned simplicity. 4 | 5 | Originally made to scale state management in [Z-js framework](https://github.com/javaScriptKampala/z-js) but anyone can use, as a seprate simple package, it's implemented in purely vanilla js to allow flexibility of adapting this is any other frameworks apart from Z-js, for exmple with react, see [state-radio-react](https://github.com/Hussseinkizz/state-radio-react) it's relatively simple to integrate in any js environment. 6 | 7 | In fact, if you know how a home radio set works, you surely know how this works already, but don't take my word for it, let's show you around... 8 | 9 | if you just want to test out and follow along with what we are about to go through, you can try this little live playground: [demo](https://stackblitz.com/edit/state-radio-example?file=main.js) 10 | 11 | otherwise, enough talks, let's dive in... 12 | 13 | ## 🔥 Features 14 | 15 | - **🔄 Framework-Agnostic:** Use with any frontend framework or library of your choice, vanilla or react or what, it all works. 16 | - **🚀 Simple API:** A straightforward API for quick integration and ease of use, create a channel, subscribe if you want, update state, do anything, enjoy the flexibility! 17 | - **🤯 Automatic Subscriptions:** Get State automatically subscribes to the state channel which saves you from explicitly subscribing to state un-necessarily, you can of course unsubscribe if you want to! 18 | - **⏳ Asynchronous State Updates:** Supports asynchronous state updates and middlewares for complex state transformations or asynchronous workflows or data fetching that triggers state updates. 19 | - **📜 State History:** Keeps track of state changes with a history feature, very useful for debugging and back tracing state changes. 20 | - **⚙️ Middleware:** Can integrate middleware functions to customize state updates. 21 | - **🔌 Plugin Support:** Easily extendable to enhance functionality with community plugins. 22 | - **📯 State Of The Art:** Has no dependencies, scales with your application and remains performant, only updates channels which changed, the rest remains intact, it just works! 23 | 24 | ## ▶️ Installation 25 | 26 | For vanilla js projects a [cdn import](https://cdn.jsdelivr.net/npm/state-radio@1.0.4/dist/state-radio.js) is recommended, otherwise you have to refrence the file exactly after installation for example: 27 | 28 | ```js 29 | 30 | import { StateRadio } from './node_modules/state-radio/dist/state-radio.js'; 31 | 32 | ``` 33 | 34 | while for others say in react or any build tooling based setup, say using vite, it's the usual stuff, just import from `state-radio` and the rest will be just fine and in that regards the steps is as follow: 35 | 36 | ```bash 37 | npm install state-radio 38 | ``` 39 | 40 | ### Usage And Initialization 41 | 42 | ``` javascript 43 | 44 | import { StateRadio } from 'state-radio'; 45 | 46 | const { channels } = new StateRadio(); 47 | ``` 48 | 49 | ### Creating Channels 50 | 51 | To create a channel, give it a channel name and initial state, it's recommended you put all your state management in a dedicated separate file, say store.js and export from there, but for simple ones, anywhere in your codebase can be just fine. 52 | 53 | ```javascript 54 | // define some initial states 55 | let nums = [1, 2, 3]; 56 | let user = { 57 | name: 'js', 58 | age: 20, 59 | }; 60 | 61 | // add new state channels to radio 62 | const numberChannel = channels.addChannel('numberChannel', nums); 63 | const userChannel = channels.addChannel('userChannel', user); 64 | ``` 65 | 66 | ### Using the channels and working with state 67 | 68 | ```javascript 69 | // update number channel 70 | numberChannel.setState((oldState) => [...oldState, 4]); 71 | 72 | console.log('Number Channel State:', numberChannel.getState()); 73 | 74 | // update user channel 75 | userChannel.setState((user) => ({ ...user, name: 'Kizz' })); 76 | 77 | console.log('User Channel State:', userChannel.getState()); 78 | 79 | // and definitely you can, add a channel 80 | channels.addChannel('uselessChannel', 'foobar!'); 81 | 82 | // get a channel by name 83 | let uselessChannel = channels.getChannel('uselessChannel'); 84 | 85 | console.log('Useless Channel State:', uselessChannel.getState()); 86 | 87 | // remove a channel if you want to. 88 | let newChannels = channels.removeChannel('uselessChannel'); 89 | 90 | console.log('updated channels:', newChannels); 91 | 92 | // or get all channels 93 | let ourChannels = channels.getChannels() 94 | console.log('our channels:', ourChannels); 95 | 96 | ``` 97 | 98 | pretty basic right? ok let's get a bit more serious... 99 | 100 | ### Asynchronous State 101 | 102 | This one is not good to use for everything, but if you have asynchronous operations that affect state, it can be a good feature to use, for example 103 | 104 | ``` javascript 105 | numberChannel.setStateAsync(async (oldState) => { 106 | let newNumber = await fetch('https://num-api/number/new'); 107 | return [...oldState, newNumber] 108 | }) 109 | .then((updatedState) => { 110 | console.log('Async state updated:', updatedState); 111 | }) 112 | .catch((error) => { 113 | console.error('Error updating state:', error); 114 | }); 115 | 116 | ``` 117 | 118 | Note that the api `https://num-api/number/new` is just for demo purposes and doesn't actually exist but just shows how it would work out with an api call that returns a number which in return we use in state. 119 | 120 | ### State History 121 | 122 | Get a list of previous state objects in the order they were applied in. 123 | 124 | ``` javascript 125 | 126 | const previousStates = numberChannel.getHistory(); 127 | console.log(previousStates); 128 | 129 | ``` 130 | 131 | ### Subscribing to state channels 132 | 133 | To subscribe to a state channel, you define a callback which will be invoked or called whenever state changes and it will recieve the new state as a paremeter from from state radio channel, for example below, we will console log whenever this channel's state changes, this can be useful for running side effects, say updating the UI whenever the state it depends on changes. 134 | 135 | ```javascript 136 | 137 | const callback = (newState) => console.log('Number channel changed to:', newState); 138 | 139 | // Subscribe to state changes 140 | numberChannel.subscribe(callback); 141 | 142 | // unSubscribe from state changes if you have to. 143 | numberChannel.unSubscribe(callback); 144 | 145 | // explictly notify subscribers, 146 | // this is done automatically but you can trigger it if you want 147 | numberChannel.notifySubscribers() 148 | ``` 149 | 150 | ### Using Middlewares 151 | 152 | Middlewares intercept the state update process and mutate state before it get's commited to subscribers, this can be useful where you have post effects you want to run before actually changing state, say logging current state or doing something before you log out a user, etc. For example... 153 | 154 | ``` javascript 155 | 156 | const addOneToEach = async (state) => { 157 | console.log('Async Middleware 1 Operating..'); 158 | // add 1 to each num in state 159 | let newNums = state.map((n) => n + 1); 160 | // Simulate an asynchronous operation 161 | await new Promise((resolve) => setTimeout(resolve, 1000)); 162 | return newNums; 163 | }; 164 | 165 | const removeOddNums = async (state) => { 166 | console.log('Async Middleware 2 Operating..'); 167 | // add 1 to each num in state 168 | let newNums = state.filter((n) => n % 2 === 0); 169 | // Simulate an asynchronous operation 170 | await new Promise((resolve) => setTimeout(resolve, 500)); 171 | return newNums; 172 | }; 173 | 174 | // attach middlewares to channel 175 | numberChannel.addMiddleWares(addOneToEach, removeOddNums); 176 | 177 | // remove middlewares from channel 178 | numberChannel.removeMiddleWares(removeOddNums); 179 | ``` 180 | 181 | Note, middlewares are run in the order they're given to `addMiddleWares` method, from left to right! 182 | 183 | ### Using Plugins, yes, extending the library functionality 184 | 185 | As you guessed, sometimes one size doesn't fit all, and there we had to provide an extensible system, anyone in state radio community can write their own plugins and integrate them into the library, here is what the convention looks like: 186 | 187 | ``` javascript 188 | 189 | // Create a logging plugin 190 | const logging = { 191 | name: 'loggingPlugin', 192 | setter: { 193 | method: (state, options) => { 194 | // Add a 'logEntry' key with a timestamp to the state 195 | console.log('do something with state via plugin'); 196 | console.log('log:', state, 'options:', options); 197 | return [...state, 100]; 198 | }, 199 | options: { cry: 'meow meow!!' }, 200 | }, 201 | getter: { 202 | method: (state, options) => { 203 | // No special behavior for getting in this example 204 | return state; 205 | }, 206 | options: {}, 207 | }, 208 | // No exposes in this example, as it's a simple logging plugin 209 | }; 210 | 211 | ``` 212 | 213 | or you can do some stuff, for example... 214 | 215 | ``` javascript 216 | 217 | import _useStorage from '@kizz-js/use-local-storage'; 218 | 219 | // local storage plugin 220 | let localStorage = { 221 | name: 'localStorage', 222 | plugin: _useStorage, 223 | options: { storageType: 'session' }, 224 | setter: { 225 | method: _useStorage.setState, 226 | options: { cache: true }, 227 | }, 228 | getter: { 229 | method: _useStorage.getState, 230 | options: {}, 231 | }, 232 | exposes: [{ name: 'onLocalChange', method: _useStorage.onChange }], 233 | }; 234 | ``` 235 | 236 | you can find the package `@kizz-js/use-local-storage` used in example as plugin here [_useLocalStorage 🪣](https://www.npmjs.com/package/@kizz-js/use-local-storage) 237 | 238 | Note: every plugin should provide a setter and getter method and each of those should return new state, setter takes in current channel state and returns it after setting it and manipulating it as it wants, and getter returns one it retrieves from it's own mechanisms. And then each plugin can be used as below: 239 | 240 | ``` javascript 241 | // add plugins when initializing a new state radio 242 | const { channels } = new StateRadio({ 243 | // plugins: [localStorage], 244 | plugins: [logging, localStorage], 245 | }); 246 | 247 | ``` 248 | 249 | plugins are applied in order their provided, ordering matters therefore. 250 | 251 | ## Example -- uses vanilla js and html 252 | 253 | That was a roller coaster, now let's see a working example combining some of those concepts convered. 254 | 255 | -- step 1, install state radio 256 | 257 | ```bash 258 | npm install state-radio 259 | ``` 260 | 261 | -- step 2, create a file in your root directory, call it `todo.js` and put the following... 262 | 263 | ``` javascript 264 | 265 | import { StateRadio } from 'state-radio.js'; 266 | const taskInput = document.querySelector('#taskInput'); 267 | const addTaskButton = document.querySelector('#addTaskButton'); 268 | const filterSelect = document.querySelector('#filterSelect'); 269 | const counterElement = document.querySelector('#counter'); 270 | 271 | addTaskButton.addEventListener('click', () => addTask(taskInput.value)); 272 | filterSelect.addEventListener('change', () => setFilter(filterSelect.value)); 273 | 274 | const { channels } = new StateRadio(); 275 | 276 | // Add a channel for tasks 277 | const tasksChannel = channels.addChannel('tasks', []); 278 | const counterChannel = channels.addChannel('counter', 0); 279 | 280 | // Add a channel for visibility filter 281 | const filterChannel = channels.addChannel('filter', 'all'); 282 | 283 | // Action to add a task 284 | const addTask = (text) => { 285 | tasksChannel.setState((tasks) => [...tasks, { text, completed: false }]); 286 | }; 287 | 288 | // Action to toggle task completion 289 | const toggleTask = (index) => { 290 | tasksChannel.setState((tasks) => 291 | tasks.map((task, i) => 292 | i === index ? { ...task, completed: !task.completed } : task 293 | ) 294 | ); 295 | }; 296 | 297 | // Action to set visibility filter 298 | const setFilter = (filter) => { 299 | filterChannel.setState(filter); 300 | }; 301 | 302 | let count = counterChannel.getState(); 303 | 304 | // Subscribe to tasks changes 305 | tasksChannel.subscribe((tasks) => { 306 | // Update UI or trigger re-render 307 | console.log('Tasks Updated:', tasks); 308 | counterChannel.setState((count) => count + 1); 309 | counterElement.innerHTML = `Count: ${counterChannel.getState()}`; 310 | }); 311 | 312 | // Subscribe to filter changes 313 | filterChannel.subscribe((filter) => { 314 | // Update UI or trigger re-render based on filter 315 | console.log('Filter Updated:', filter); 316 | // Update UI based on filter 317 | let currentTasks = tasksChannel.getState(); 318 | let filteredTasks = []; 319 | 320 | if (filter === 'completed') { 321 | filteredTasks = currentTasks.filter((task) => task.completed === true); 322 | updateTasksUI(filteredTasks); 323 | } else if (filter === 'active') { 324 | updateTasksUI(currentTasks); 325 | } else { 326 | updateTasksUI(currentTasks); 327 | } 328 | }); 329 | 330 | // Update UI based on state changes 331 | function updateTasksUI(tasks) { 332 | const tasksContainer = document.getElementById('tasksContainer'); 333 | tasksContainer.innerHTML = ''; 334 | 335 | tasks.forEach((task, index) => { 336 | const taskElement = document.createElement('div'); 337 | taskElement.innerHTML = ` 338 | 339 | ${task.text} 340 | `; 341 | tasksContainer.appendChild(taskElement); 342 | // Add onChange event listener to the checkbox 343 | const checkbox = taskElement.querySelector('input[type="checkbox"]'); 344 | checkbox.addEventListener('change', () => toggleTask(index)); 345 | }); 346 | } 347 | 348 | // Subscribe to tasks changes 349 | tasksChannel.subscribe(updateTasksUI); 350 | 351 | // Initial rendering 352 | updateTasksUI(tasksChannel.getState()); 353 | 354 | ``` 355 | 356 | and then create `todo.html` file and put 357 | 358 | ``` html 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | Todo App 367 | 368 | 380 | 381 | 382 | 383 |

Count

384 |
385 | 386 | 387 | 390 | 391 | 396 |
397 | 398 | 399 |
400 | 401 | 402 | 405 | 406 | 407 | 408 | 409 | ``` 410 | 411 | That's it, play around, enjoy the radio show!!! 412 | 413 | But wait, what about using this in react? well see our guide on that here [state-radio-react](https://github.com/Hussseinkizz/state-radio-react) or if you just want to test out what we gone through, try this live playground: [demo](https://stackblitz.com/edit/state-radio-example?file=main.js) 414 | 415 | 416 | ## Contributing 417 | 418 | If you find issues or have ideas for improvements, please open an issue or submit a pull request or contact author at [hssnkizz@gmail.com]('hssnkizz@gmail.com') 419 | 420 | This project is licensed under the MIT License. 421 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | export default defineConfig({ 4 | build: { 5 | lib: { 6 | entry: 'index.js', 7 | name: 'state-radio', 8 | fileName: 'state-radio', 9 | }, 10 | rollupOptions: { 11 | output: { 12 | manualChunks: undefined, 13 | }, 14 | }, 15 | minify: 'terser', 16 | terserOptions: { 17 | compress: { 18 | drop_console: true, 19 | drop_debugger: false, 20 | pure_funcs: ['console.log'], 21 | }, 22 | mangle: { 23 | safari10: true, 24 | }, 25 | output: { 26 | beautify: false, 27 | comments: 'some', 28 | preserve_annotations: true, 29 | semicolons: true, 30 | }, 31 | }, 32 | }, 33 | }); 34 | --------------------------------------------------------------------------------