├── .gitignore ├── .npmignore ├── .prettierrc ├── LICENSE ├── README.md ├── dist ├── default.d.ts ├── default.js ├── index.d.ts ├── index.js ├── interface.d.ts ├── interface.js ├── store.d.ts ├── store.js ├── utils.d.ts └── utils.js ├── docs ├── 0.advanced │ ├── README.EN.md │ ├── README.JP.md │ └── README.KR.md ├── README.JP.md ├── README.KR.md └── README.PL.md ├── package-lock.json ├── package.json ├── src ├── default.ts ├── index.ts ├── interface.ts ├── store.ts └── utils.ts ├── tsconfig.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # bundle 64 | export/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "semi": false, 5 | "singleQuote": true 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 AhaOfficial 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | # 📮 vue-state-store (vss) 8 | 9 | > Simple state management system that full supports for typescript. 10 | 11 | 📬 Distributed state management module system for Vue. [Pub & Sub model based] 12 | 13 |
14 | 15 | ## 🌎 Global 16 | 17 | > The following multilingual documents are provided. (need pull request help) 18 | 19 | - ([日本語 文書](https://github.com/AhaOfficial/vue-state-store/blob/master/docs/README.JP.md)) JP contributed by @yopinoji (thx! 😊) 20 | 21 | - ([한국어 문서](https://github.com/AhaOfficial/vue-state-store/blob/master/docs/README.KR.md)) 22 | 23 | - ([Dokumenty Polskie](https://github.com/AhaOfficial/vue-state-store/blob/master/docs/README.PL.md)) PL contributed by @Milesq (🎉 so thanks!) 24 | 25 |
26 | 27 | ## 📔 Table of Contents 28 | 29 | * [😊 Easy use! & Powerful application!](#-easy-use--powerful-application) 30 | * [💡 Advantages compared to vuex](#-advantages-compared-to-vuex) 31 | * [💬 Installation](#-installation) 32 | * [🔮 Devtools Apply](#-devtools-apply) 33 | * [📬 Pub & Sub Model Description](#-pub--sub-model-description) 34 | * [😎 Basic Usage](#-basic-usage) 35 | * [📮 Primitive Type Pub & Sub](#-primitive-type-pub--sub) 36 | * [📮 Object Type Pub & Sub](#-object-type-pub--sub) 37 | * [📮 Create state & embedded action](#-create-state--embedded-action) 38 | * [📮 Binding within the Vue template](#-binding-within-the-vue-template) 39 | * [🚀 Advanced Usage](#-advanced-usage) 40 | * [⏳ Asynchronous-tic Usage](#-asynchronous-tic-usage) 41 | * [💡 Vscode Intellisense Usage](#-vscode-intellisense-usage) 42 | * [📮 (Advanced) State Use Function Design Pattern](#-advanced-state-use-function-design-pattern) 43 | * [📮 (Advanced) Declare-Define-Inject-Use Design Pattern](#-advanced-declare-define-inject-use-design-pattern) 44 | * [🤔 Q&A](#-qa) 45 | * [🧲 Q. Doesn't have a $store with all the state stores like vuex?](#-q-doesnt-have-a-store-with-all-the-state-stores-like-vuex) 46 | * [👀 Q. Will the changed value be rendered again if the `.bind()` value is changed?](#-q-will-the-changed-value-be-rendered-again-if-the-bind-value-is-changed) 47 | * [📡 Q. Is the `.bind()` value work two way binding?](#-q-is-the-bind-value-work-two-way-binding) 48 | * [📔 License](#-license) 49 | 50 |
51 | 52 | ## 😊 Easy use! & Powerful application! 53 | 54 | `vue-state-store (vss)` is a module that is intended to completely replace the `vuex` modules that were popular with the `vue`. **The purpose of this module is to make state management very easy by using 200% efficiency of typescript.** 55 | 56 |
57 | 58 | ### 💡 Advantages compared to vuex 59 | 60 | - **Low running curve** - Use simple publishing & subscription model 61 | - **Supports Typescript Intellisense** - Status & Actions & Mutation & When using variables within Templates 62 | - **Auto-Bind function** - Easy vue template binding. 63 | - **Pure typescript class based definition** - no need to use mix-in 64 | - **A unified action structure** - Flexible use with no distinction between action and motion. 65 | - **Allow flexible state use** - If you omit Mutation, you can use it as Getters. 66 | 67 |
68 | 69 | ### 💬 Installation 70 | 71 | > (Vue2, Vue3, Nuxt is supported. Automatic binding function in Composition API : .bind(')' is supported.) 72 | 73 | ``` 74 | npm i vue-state-store 75 | ``` 76 | 77 |
78 | 79 | ### 🔮 Devtools Apply 80 | 81 | > `vue-state-store` supports `vue-devtools`. (You can see information about the state stores created through `vue-state-store` on the `vuex` tab.) 82 | 83 | [View Related Content](https://github.com/AhaOfficial/vue-state-store-devtools) 84 | 85 |
86 | 87 | ### 📬 Pub & Sub Model Description 88 | 89 | > vue-state-store uses publish & subscription design patterns. 90 | 91 | `vue-state-store` is a storage that exists in the memory where values are stored. The `.subscribe (callback)` function allows you to receive changed values in a callback when values in the storage change, and you can update the values in the storage through `.set (newValue)` and `.update((data) => data)`. 92 | 93 |
94 | 95 | ## 😎 Basic Usage 96 | 97 | > `vue-state-store` is an easy way to manage both state and actions and mutations through a `function` or `class`. 98 | 99 |
100 | 101 | ### 📮 Primitive Type Pub & Sub 102 | 103 | > Primitive type means five basic types (number, string, boolean, null, undefined). 104 | 105 | 106 | 107 | > The `.subscribe()` function, when the execution, gives a function as a return value, which can interrupt the storage subscription at any time. So I write that value name of "unsubscribe". 108 | 109 |
110 | 111 | ### 📮 Object Type Pub & Sub 112 | 113 | > If you look below, you can see that there is little difference between the top and the usage. 114 | > 115 | > In `store(value)`, the value can be a primary type or object. 116 | 117 | 118 | 119 |
120 | 121 | ### 📮 Create state & embedded action 122 | 123 | > `vue-state-store` can define embedded actions by inheriting classes. 124 | 125 | In `vue-state-store`, the distinction between action and motion is not required. 126 | 127 | - By creating any function in the class, you can configure **Embedded action**. 128 | - Any function that transforms a state without being embedded in the class is called **Outside Action**. 129 | 130 | 131 | 132 |
133 | 134 | ### 📮 Binding within the Vue template 135 | 136 | > `vue-state-store` can easily bind the repository to the vue template tag, and the bound store continues to support Typescript Intellisense within the template tag. Typescript Intellisense is also supported when using embedded actions into the storage within the script tag. 137 | 138 | - The embedded action can be called just by calling the state through `import`. 139 | - The '`bind()` function automatically binds the storage into the template. 140 | - It is recommended to put '$' in front of the existing storage name as a naming rule, as shown in `return { $vote: vote.bind() }` . 141 | - The `.bind()` function is recommended to be used within the `setup` function of **@vue/composition-api**. 142 | 143 | 144 | 145 |
146 | 147 | ## 🚀 Advanced Usage 148 | 149 | > Explain the advanced ways to use it. (This does not raise the learning curve.) 150 | 151 |
152 | 153 | ### ⏳ Asynchronous-tic Usage 154 | 155 | > The function `.update()` and '.set()' return Promise. 156 | 157 |
158 | 159 | - `await` allows certain logic to run after the update job completes when you update the storage values. 160 | 161 | 162 | 163 | 164 | 165 | - You can also define callbacks to async that are passed to the update function. 166 | 167 | 168 | 169 |
170 | 171 | ### 💡 Vscode Intellisense Usage 172 | 173 | > To use both **vscode** and **typescript** at the same time and need some Intellisense support, you can obtain the module below. 174 | 175 | [Vetur]: https://marketplace.visualstudio.com/items?itemName=octref.vetur 176 | 177 | In order to receive support for intellisense in the template after installing vetur, the following option should be added to the vscode setting. 178 | 179 | ```json 180 | "vetur.experimental.templateInterpolationService": true 181 | ``` 182 | 183 |
184 | 185 | ### 📮 (Advanced) State Use Function Design Pattern 186 | 187 | > `vue-state-store` provides examples of design patterns of functions that begin with `use~` similar to React Hooks to make the most of the composition API. 188 | 189 | State Use Function refers to the use of a function that is preceded by the word `use` (if there is a state called `useTodo` inside the component and receives the status store as a result). 190 | 191 | > This allows you to use the life cycle of the component in the state store. 192 | 193 | If you use the accessor(`.set() and .update()`) to modify the state, it can be very cumbersome to create complex logic, unlike when you modify the existing general variables. 194 | 195 | `vue-state-store` allows for convenient change of state by directly accessing bindings within the store without the use of such an accessor. This design pattern is only a simple example of configuring the status usage function when using the Composition API (not necessarily), **If you need to modify the state in a complex way**, or **If you need to create multiple `compute` objects**. 196 | 197 | Even if you modify a bound value, the changes are automatically distributed to the callbacks you are subscribing to each time the value changes. 198 | 199 | 200 | 201 | > If you refer to the value of a bound store in a callback wrapped in `computed` , the callback will occur again whenever the value of that store changes. This reduces the fatigue of re-calculating each function as it is called, thus improving performance when using a state. 202 | 203 | 204 | 205 | > To define Vue's lifecycle or `computed`, you must create one isolated function, such as `useTodo`, and must be call within the component. 206 | 207 | 208 | 209 | > As shown above, you can use it within the template tag immediately after using useToto(). (Of course Typescript Intellisense is still supported.) 210 | 211 |
212 | 213 | ### 📮 (Advanced) Declare-Define-Inject-Use Design Pattern 214 | 215 | > Please check the design pattern on a separate page. 216 | 217 | [View Description](https://github.com/AhaOfficial/vue-state-store/blob/master/docs/0.advanced/README.EN.md) 218 | 219 |
220 | 221 | ## 🤔 Q&A 222 | 223 | > Questions can also be asked through the Github Issue section. 224 | 225 |
226 | 227 | ### 🧲 Q. Doesn't have a $store with all the state stores like vuex? 228 | 229 | > A. No, it's not. It's a non-recommended design pattern. 230 | 231 | it's recommended that you import and use only a few stores after creating any index.ts file, , such as `import { vote } from '~/store`. `vue-state-store` has a distributed structure and can only refer to each other individually if each storage is required. 232 | 233 | `vue-state-store` consists of `vue` completely independent (until it is used within the vue template tag through the `.bind()` function). 234 | 235 |
236 | 237 | ### 👀 Q. Will the changed value be rendered again if the `.bind()` value is changed? 238 | 239 | > A. Yes, the storage values changed through the ref of vue are reflected in the DOM through template tags in real time. 240 | 241 |
242 | 243 | ### 📡 Q. Is the `.bind()` value work two way binding? 244 | 245 | > A. Yes, the binding value changes as the storage value changes, and the storage value changes as the binding value changes. 246 | 247 |
248 | 249 |
250 | 251 | ## 📔 License 252 | 253 | > Copyright (c) 2020 AhaOfficial 254 | 255 | **MIT Licensed** 256 | 257 | -------------------------------------------------------------------------------- /dist/default.d.ts: -------------------------------------------------------------------------------- 1 | import * as Default from './'; 2 | export default Default; 3 | -------------------------------------------------------------------------------- /dist/default.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importStar = (this && this.__importStar) || function (mod) { 3 | if (mod && mod.__esModule) return mod; 4 | var result = {}; 5 | if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; 6 | result["default"] = mod; 7 | return result; 8 | }; 9 | exports.__esModule = true; 10 | var Default = __importStar(require("./")); 11 | exports["default"] = Default; 12 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | import { StartStopNotifier, IStore } from './interface'; 2 | import { Store } from './store'; 3 | /** 4 | * Create a store that allows both updating and reading by subscription. 5 | * @param {*=}value initial value 6 | * @param {StartStopNotifier=}start start and stop notifications for subscriptions 7 | */ 8 | export declare const store: (value: T, start?: StartStopNotifier) => Store; 9 | /** 10 | * Generate SSR Store Data 11 | */ 12 | export declare const useSSR: () => { 13 | _vss: { 14 | [x: string]: any; 15 | }; 16 | }; 17 | export type { IStore }; 18 | export { Store }; 19 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importStar = (this && this.__importStar) || function (mod) { 3 | if (mod && mod.__esModule) return mod; 4 | var result = {}; 5 | if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; 6 | result["default"] = mod; 7 | return result; 8 | }; 9 | exports.__esModule = true; 10 | var Utils = __importStar(require("./utils")); 11 | var store_1 = require("./store"); 12 | exports.Store = store_1.Store; 13 | /** 14 | * Create a store that allows both updating and reading by subscription. 15 | * @param {*=}value initial value 16 | * @param {StartStopNotifier=}start start and stop notifications for subscriptions 17 | */ 18 | exports.store = function (value, start) { 19 | if (start === void 0) { start = Utils.noop; } 20 | return new store_1.Store(value, start); 21 | }; 22 | /** 23 | * Generate SSR Store Data 24 | */ 25 | exports.useSSR = function () { 26 | var renderedStates = {}; 27 | for (var _i = 0, _a = Object.keys(store_1.storeMap); _i < _a.length; _i++) { 28 | var storeName = _a[_i]; 29 | try { 30 | var storeValue = store_1.storeMap[storeName].get(); 31 | if (storeValue) 32 | renderedStates[storeName] = storeValue; 33 | } 34 | catch (e) { } 35 | } 36 | return { _vss: renderedStates }; 37 | }; 38 | -------------------------------------------------------------------------------- /dist/interface.d.ts: -------------------------------------------------------------------------------- 1 | export declare type Subscriber = (value: T) => void | Promise; 2 | export declare type Unsubscriber = () => void; 3 | export declare type Updater = (value: T) => T; 4 | export declare type AsyncUpdater = (value: T) => Promise; 5 | export declare type Invalidator = (value?: T) => void; 6 | export declare type SubscribeInvalidateTuple = [Subscriber, Invalidator]; 7 | export declare type StartStopNotifier = (set: Subscriber) => Unsubscriber | void; 8 | export interface IStore { 9 | /** 10 | * Get value and inform subscribers. 11 | */ 12 | get(): T; 13 | /** 14 | * Set value and inform subscribers. 15 | * @param value to set 16 | */ 17 | set(value?: T): void; 18 | /** 19 | * Subscribe on value changes. 20 | * @param run subscription callback 21 | * @param invalidate cleanup callback 22 | */ 23 | subscribe(run: Subscriber, invalidate?: Invalidator): Unsubscriber; 24 | /** 25 | * Update value using callback and inform subscribers. 26 | * @param updater callback 27 | */ 28 | update(updater: Updater): void; 29 | } 30 | -------------------------------------------------------------------------------- /dist/interface.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | -------------------------------------------------------------------------------- /dist/store.d.ts: -------------------------------------------------------------------------------- 1 | import * as Interface from './interface'; 2 | export declare const storeMap: { 3 | [storeName in string]: Interface.IStore; 4 | }; 5 | export declare class Store implements Interface.IStore { 6 | protected stop: Interface.Unsubscriber | null; 7 | protected subscribers: Array>; 8 | protected start: Interface.StartStopNotifier; 9 | private _value; 10 | constructor(value: T, start?: Interface.StartStopNotifier); 11 | get(): T; 12 | getItem(key: K): T[K]; 13 | set(newValue: T, force?: boolean): Promise; 14 | update(callback: Interface.Updater | Interface.AsyncUpdater): Promise; 15 | subscribe(run: Interface.Subscriber, invalidate?: Interface.Invalidator): Interface.Unsubscriber; 16 | bind(): any; 17 | watch(callback: Interface.Subscriber, option?: { 18 | immediate: boolean; 19 | }): void; 20 | patch(key: K, value: T[K]): Promise; 21 | } 22 | -------------------------------------------------------------------------------- /dist/store.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (_) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | var __importStar = (this && this.__importStar) || function (mod) { 39 | if (mod && mod.__esModule) return mod; 40 | var result = {}; 41 | if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; 42 | result["default"] = mod; 43 | return result; 44 | }; 45 | exports.__esModule = true; 46 | var Utils = __importStar(require("./utils")); 47 | var composition_api_1 = require("@vue/composition-api"); 48 | var subscriberQueue = []; 49 | exports.storeMap = {}; 50 | var Store = /** @class */ (function () { 51 | function Store(value, start) { 52 | if (start === void 0) { start = Utils.noop; } 53 | this.stop = null; 54 | this.subscribers = []; 55 | this._value = value; 56 | this.start = start; 57 | var storeName = this.constructor.name; 58 | if (typeof window !== 'undefined' && 59 | window.__NUXT__ && 60 | window.__NUXT__._vss && 61 | window.__NUXT__._vss[storeName]) { 62 | this._value = window.__NUXT__._vss[storeName]; 63 | } 64 | exports.storeMap[storeName] = this; 65 | devtoolsBind(this, storeName); 66 | } 67 | Store.prototype.get = function () { 68 | return Utils.getStoreValue(this); 69 | }; 70 | Store.prototype.getItem = function (key) { 71 | var data = Utils.getStoreValue(this); 72 | return data[key]; 73 | }; 74 | Store.prototype.set = function (newValue, force) { 75 | var _this = this; 76 | if (force === void 0) { force = false; } 77 | return new Promise(function (resolve) { 78 | if (Utils.notEqual(_this._value, newValue) || force) { 79 | _this._value = newValue; 80 | if (_this.stop) { 81 | var runQueue = !subscriberQueue.length; 82 | for (var i = 0; i < _this.subscribers.length; i += 1) { 83 | var s = _this.subscribers[i]; 84 | s[1](); 85 | subscriberQueue.push(s, _this._value); 86 | } 87 | if (runQueue) { 88 | for (var i = 0; i < subscriberQueue.length; i += 2) 89 | subscriberQueue[i][0](subscriberQueue[i + 1]); 90 | subscriberQueue.length = 0; 91 | } 92 | } 93 | resolve(); 94 | } 95 | }); 96 | }; 97 | Store.prototype.update = function (callback) { 98 | return __awaiter(this, void 0, void 0, function () { 99 | var _a; 100 | return __generator(this, function (_b) { 101 | switch (_b.label) { 102 | case 0: 103 | _a = this.set; 104 | return [4 /*yield*/, callback(this._value)]; 105 | case 1: return [4 /*yield*/, _a.apply(this, [_b.sent()])]; 106 | case 2: 107 | _b.sent(); 108 | return [2 /*return*/]; 109 | } 110 | }); 111 | }); 112 | }; 113 | Store.prototype.subscribe = function (run, invalidate) { 114 | var _this = this; 115 | if (invalidate === void 0) { invalidate = Utils.noop; } 116 | var subscriber = [run, invalidate]; 117 | this.subscribers.push(subscriber); 118 | if (this.subscribers.length === 1) 119 | this.stop = this.start(this.set) || Utils.noop; 120 | if (this._value) 121 | run(this._value); 122 | return function () { 123 | var index = _this.subscribers.indexOf(subscriber); 124 | if (index !== -1) 125 | _this.subscribers.splice(index, 1); 126 | if (_this.subscribers.length === 0) { 127 | if (_this.stop) 128 | _this.stop(); 129 | _this.stop = null; 130 | } 131 | }; 132 | }; 133 | Store.prototype.bind = function () { 134 | var _this = this; 135 | var bindedValue = composition_api_1.ref(Utils.clone(this._value)); 136 | var unsubscribeStore = this.subscribe(function (data) { 137 | bindedValue.value = Utils.clone(data); 138 | }); 139 | var unsubscribeWatch = composition_api_1.watch(bindedValue, function () { 140 | var data = bindedValue.value; 141 | var dataOfObserverRemoved = Utils.clone(data); 142 | var originOfOfObserverRemoved = Utils.clone(_this._value); 143 | if (!Utils.deepEqual(dataOfObserverRemoved, originOfOfObserverRemoved)) 144 | _this.set(dataOfObserverRemoved); 145 | }, { 146 | deep: true 147 | }); 148 | composition_api_1.onUnmounted(function () { 149 | if (unsubscribeWatch) 150 | unsubscribeWatch(); 151 | if (unsubscribeStore) 152 | unsubscribeStore(); 153 | }); 154 | return bindedValue; 155 | }; 156 | Store.prototype.watch = function (callback, option) { 157 | var _this = this; 158 | if (option === void 0) { option = { 159 | immediate: false 160 | }; } 161 | var unsubscribe; 162 | var isFirst = true; 163 | composition_api_1.onMounted(function () { 164 | unsubscribe = _this.subscribe(function (data) { 165 | if (isFirst && !option.immediate) { 166 | isFirst = false; 167 | return; 168 | } 169 | callback(data); 170 | }); 171 | }); 172 | composition_api_1.onUnmounted(function () { 173 | if (unsubscribe) 174 | unsubscribe(); 175 | }); 176 | }; 177 | Store.prototype.patch = function (key, value) { 178 | return this.update(function (data) { 179 | data[key] = value; 180 | return data; 181 | }); 182 | }; 183 | return Store; 184 | }()); 185 | exports.Store = Store; 186 | /** 187 | * Processing points for nuxt 188 | */ 189 | var target = typeof window !== 'undefined' 190 | ? window 191 | : typeof global !== 'undefined' 192 | ? global 193 | : {}; 194 | var devtoolsBind = function (store, storeName) { return __awaiter(void 0, void 0, void 0, function () { 195 | var devtoolsHook; 196 | return __generator(this, function (_a) { 197 | devtoolsHook = target.__VUE_DEVTOOLS_GLOBAL_HOOK__; 198 | if (!devtoolsHook) 199 | return [2 /*return*/]; 200 | try { 201 | // vue-state-store-devtools 202 | if (typeof target.VueStateStoreDevtools != 'undefined') 203 | target.VueStateStoreDevtools.devtoolsBind(store, storeName); 204 | } 205 | catch (e) { } 206 | return [2 /*return*/]; 207 | }); 208 | }); }; 209 | -------------------------------------------------------------------------------- /dist/utils.d.ts: -------------------------------------------------------------------------------- 1 | export declare const noop: () => void; 2 | export declare const isPromise: (value: any) => value is PromiseLike; 3 | export declare const run: (callback: any) => any; 4 | export declare const isFunction: (thing: any) => thing is Function; 5 | export declare const safeNotEqual: (a: any, b: any) => boolean; 6 | export declare const notEqual: (a: any, b: any) => boolean; 7 | export declare const validateStore: (store: any, name: any) => void; 8 | export declare const subscribe: (store: any, ...callbacks: any[]) => any; 9 | export declare const getStoreValue: (store: any) => any; 10 | export declare const deepEqual: (x: any, y: any) => boolean; 11 | export declare const clone: (object: any) => any; 12 | -------------------------------------------------------------------------------- /dist/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | exports.noop = function () { }; 4 | exports.isPromise = function (value) { 5 | return value && typeof value === 'object' && typeof value.then === 'function'; 6 | }; 7 | exports.run = function (callback) { 8 | return callback(); 9 | }; 10 | exports.isFunction = function (thing) { 11 | return typeof thing === 'function'; 12 | }; 13 | exports.safeNotEqual = function (a, b) { 14 | return a != a 15 | ? b == b 16 | : a !== b || (a && typeof a === 'object') || typeof a === 'function'; 17 | }; 18 | exports.notEqual = function (a, b) { 19 | return a != a ? b == b : a !== b; 20 | }; 21 | exports.validateStore = function (store, name) { 22 | if (store != null && typeof store.subscribe !== 'function') 23 | throw new Error("'" + name + "' is not a store with a 'subscribe' method"); 24 | }; 25 | exports.subscribe = function (store) { 26 | var callbacks = []; 27 | for (var _i = 1; _i < arguments.length; _i++) { 28 | callbacks[_i - 1] = arguments[_i]; 29 | } 30 | if (store == null) 31 | return exports.noop; 32 | var unsub = store.subscribe.apply(store, callbacks); 33 | return unsub.unsubscribe ? function () { return unsub.unsubscribe(); } : unsub; 34 | }; 35 | exports.getStoreValue = function (store) { 36 | var value; 37 | exports.subscribe(store, function (_) { return (value = _); })(); 38 | return value; 39 | }; 40 | exports.deepEqual = function (x, y) { 41 | if (x === y) { 42 | return true; 43 | } 44 | else if (typeof x == 'object' && 45 | x != null && 46 | typeof y == 'object' && 47 | y != null) { 48 | if (Object.keys(x).length != Object.keys(y).length) 49 | return false; 50 | for (var prop in x) { 51 | if (y.hasOwnProperty(prop)) { 52 | if (!exports.deepEqual(x[prop], y[prop])) 53 | return false; 54 | } 55 | else 56 | return false; 57 | } 58 | return true; 59 | } 60 | else 61 | return false; 62 | }; 63 | exports.clone = function (object) { return JSON.parse(JSON.stringify(object)); }; 64 | -------------------------------------------------------------------------------- /docs/0.advanced/README.EN.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | # 📮 vue-state-store 6 | 7 | > Simple state management system that full supports for typescript. 8 | 9 | 📬 Distributed state management module system for Vue. [Pub & Sub model based] 10 | 11 |
12 | 13 | ## ⚗️ Recommended Design Pattern 14 | 15 | > Describes the recommended design patterns for developing state storage with `vue-state-store`. 16 | 17 | When defining components, it is recommended that they be implemented in the following ways: 18 | 19 | Declare → Define → Inject → Use 20 | 21 |
22 | 23 | ### 📐 Declare 24 | 25 | > The Declare process actually refers to the process of defining a data type to be contained in a state store as a typescript interface in advance. 'export' all elements for reference in other files. 26 | 27 |
28 | 29 | ```typescript 30 | /** 31 | * Declaration of standards for to-do 32 | */ 33 | export interface ITodoItem { 34 | /** 35 | * Sequence number of work to be done 36 | */ 37 | id: number 38 | /** 39 | * Title of to-do 40 | */ 41 | title: string 42 | /** 43 | * Completion Status 44 | */ 45 | done: boolean 46 | } 47 | 48 | /** 49 | * To Do List Status Specification Declaration 50 | */ 51 | export interface ITodo { 52 | /** 53 | * Todo List (Array) 54 | */ 55 | todoList: ITodoItem[] 56 | /** 57 | * * Variable of typing in a new to do title 58 | */ 59 | newTodo: '' 60 | /** 61 | * * Whether to show the work to be completed 62 | */ 63 | showComplete: boolean 64 | } 65 | 66 | ``` 67 | 68 |
69 | 70 | ### 📦 Define 71 | 72 | > The Define process is the process of defining what typescript type values the state store will contain and what values will initially be contained. 'export' all elements for reference in other files. 73 | 74 |
75 | 76 | ```typescript 77 | import { Store } from 'vue-state-store' 78 | import { ITodo, ITodoItem } from './declare' 79 | 80 | /** 81 | * To Do List State Storage Specification Definition 82 | */ 83 | export class Todo extends Store {} 84 | 85 | /** 86 | * * To Do List State Storage Initialize 87 | */ 88 | export const todo = new Todo({ 89 | todoList: [ 90 | { 91 | id: 0, 92 | title: 'Giving peanuts to squirrels', 93 | done: false 94 | }, 95 | { 96 | id: 1, 97 | title: 'Giving Churu to a Cat', 98 | done: false 99 | }, 100 | { 101 | id: 4, 102 | title: 'Create Vue Example', 103 | done: true 104 | } 105 | ], 106 | newTodo: '', 107 | showComplete: false 108 | }) 109 | 110 | ``` 111 | 112 | 113 | 114 |
115 | 116 | ### 💉 Inject 117 | 118 | > The injection process is logic that can only exist within a component, which defines logic for later injection into the component.The reason of why need to be as a class is because is need to increase the reuse of component logic. (This is a method to resolve the Mix-in pattern of Vue as a typescript while reducing complexity.) 'export' all elements for reference in other files. 119 | 120 |
121 | 122 | ```typescript 123 | import { ITodoItem } from './declare' 124 | import { todo } from './define' 125 | import { VueAPI } from '~/core' 126 | 127 | /** 128 | * To Do List State Injection Class 129 | */ 130 | export class UseTodo { 131 | /** 132 | * To Do List State Store 133 | */ 134 | todo = todo 135 | /** 136 | * Binded To Do Status Store 137 | */ 138 | $todo = todo.bind() 139 | 140 | /** 141 | * Work in Progress List 142 | */ 143 | pending = VueAPI.computed(() => { 144 | return this.$todo.todoList.filter(item => { 145 | return !item.done 146 | }) 147 | }) 148 | 149 | /** 150 | * Completed Task List 151 | */ 152 | completed = VueAPI.computed(() => { 153 | return this.$todo.todoList.filter(item => { 154 | return item.done 155 | }) 156 | }) 157 | } 158 | ``` 159 | 160 |
161 | 162 | ### 💊 Use 163 | 164 | > The use process is to define a function that, when you actually try to use this state store within a component, injects that store and its associated component logic so that it can be used immediately. 'export' all elements for reference in other files. 165 | 166 |
167 | 168 | ```typescript 169 | import { todo } from './define' 170 | import { UseTodo } from './inject' 171 | 172 | /** 173 | * To Do List State Use Function 174 | */ 175 | export const useTodo = () => { 176 | // Create an instance of a class for using state. 177 | const todoInstance = new UseTodo() 178 | 179 | // Load previously stored local storage values. 180 | todoInstance.getTodos() 181 | 182 | // Store values on local storage as todolist changes. 183 | todo.subscribe(data => { 184 | try { 185 | if (window) { 186 | window.localStorage.setItem('todo_list', JSON.stringify(data.todoList)) 187 | } 188 | } catch (e) {} 189 | }) 190 | 191 | return todoInstance 192 | } 193 | 194 | ``` 195 | 196 |
197 | 198 | ## 🍱 Separation rule 199 | 200 | > Basically, 'declare' → 'define' → 'inject' → 'use' process is implemented in a single file format that matches each English name. However, if a specific process becomes very long, please define the name of the course as a folder and flexibly separate the code from one file for reference through the 'index.ts' file. 201 | 202 |
203 | 204 | 205 | 206 |
207 | 208 | 209 | 210 | 211 | 212 | ## 🤔 Single File Example 213 | 214 | > If the state store and its associated logic are simple, or if you need to just create it quickly, you can put all of the logic in one file, as shown below. However, please make sure to specify the 4th step as much as possible. 215 | 216 |
217 | 218 | ```typescript 219 | import { Store } from 'vue-state-store' 220 | 221 | /** 222 | * Counter State Declaration 223 | */ 224 | export interface ICounter { 225 | /** 226 | * A number that is counted one by one. 227 | */ 228 | count: number 229 | } 230 | 231 | /** 232 | * Define Counter State Storage Specifications 233 | */ 234 | export class Counter extends Store {} 235 | 236 | /** 237 | * Counter State Storage Definition 238 | */ 239 | export const counter = new Counter({ 240 | count: 0 241 | }) 242 | 243 | /** 244 | * Counter State Injection Class 245 | */ 246 | export class UseCount { 247 | /** 248 | * The counter state store. 249 | */ 250 | counter = counter 251 | /** 252 | * Binded counter state store. 253 | */ 254 | $counter = counter.bind() 255 | /** 256 | * The action that raises the counter by 1. 257 | */ 258 | up = () => this.$counter.count++ 259 | /** 260 | * The action that drops the counter by 1. 261 | */ 262 | down = () => this.$counter.count-- 263 | } 264 | 265 | /** 266 | * Counter state use function 267 | */ 268 | export const useCount = () => { 269 | const countInstance = new UseCount() 270 | return countInstance 271 | } 272 | 273 | ``` 274 | 275 | 276 | 277 | -------------------------------------------------------------------------------- /docs/0.advanced/README.JP.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | # 📮 vue-state-store 6 | 7 | > 完全にTypescriptをサポートする簡単なstate management体系です。 8 | 9 | 📬 Vue専用に分散された状態管理モジュール体系です。 [発行-購読(Pub & Sub)モデル基盤] 10 | 11 |
12 | 13 | ## 🌎 翻訳支援 14 | 15 | @yopinojiさん、ありがとうございます。😊😊 16 | 17 |
18 | 19 | ## ⚗️ 推奨デザインパターン 20 | 21 | > `vue-state-store` による状態リポジトリ開発時に推奨されるデザインパターンを説明します。 22 | 23 | コンポーネントを定義する際、以下の方式を通じて実装することをお勧めします。 24 | 25 | 宣言(declare) → 定義(decline) → 注入(inject) → 使用(use) 26 | 27 |
28 | 29 | ### 📐 宣言 (declare) 30 | 31 | > 宣言(declare)とは、実際にステータスリポジトリに入れられる資料型を予めタイプスクリプトインタフェースで定義する過程を意味します。 他のファイルで参照できるようにすべての要素を「エクスポート」します。 32 | 33 |
34 | 35 | ```typescript 36 | /** 37 | * すべきことの規格を宣言します. 38 | */ 39 | export interface ITodoItem { 40 | /** 41 | * 実行する作業のシーケンス番号です。 42 | */ 43 | id: number 44 | /** 45 | * ToDoのタイトルです。 46 | */ 47 | title: string 48 | /** 49 | * 完了ステータスです。 50 | */ 51 | done: boolean 52 | } 53 | 54 | /** 55 | * すべきことリストのステータスを宣言します。 56 | */ 57 | export interface ITodo { 58 | /** 59 | * Todo List (Array) 60 | */ 61 | todoList: ITodoItem[] 62 | /** 63 | * * 新しいToDoタイトルを入力する変数です。 64 | */ 65 | newTodo: '' 66 | /** 67 | * * 完成する作業を表示するかどうかを指定します。 68 | */ 69 | showComplete: boolean 70 | } 71 | ``` 72 | 73 |
74 | 75 | ### 📦 定義 (define) 76 | 77 | > 定義(define)プロセスは、ステータスリポジトリがどのようなタイプのスクリプトタイプの値を入れるのか、初期にどのような値を入れるのか、定義するプロセスです。 他のファイルで参照できるようにすべての要素を「エクスポート」します。 78 | 79 |
80 | 81 | ```typescript 82 | import { Store } from 'vue-state-store' 83 | import { ITodo, ITodoItem } from './declare' 84 | 85 | /** 86 | * やることリストの状態、リポジトリ規格の定義です。 87 | */ 88 | export class Todo extends Store {} 89 | 90 | /** 91 | * * することリスト状態のリポジトリの初期化過程です。 92 | */ 93 | export const todo = new Todo({ 94 | todoList: [ 95 | { 96 | id: 0, 97 | title: 'リスにピーナッツをあげなければなりません。', 98 | done: false 99 | }, 100 | { 101 | id: 1, 102 | title: '猫にChuruをあげなければなりません。', 103 | done: false 104 | }, 105 | { 106 | id: 4, 107 | title: 'Vue例を作成する必要があります。', 108 | done: true 109 | } 110 | ], 111 | newTodo: '', 112 | showComplete: false 113 | }) 114 | ``` 115 | 116 | 117 | 118 |
119 | 120 | ### 💉 注入 (inject) 121 | 122 | > 注入(inject)過程はコンポーネント内でのみ存在するロジックで、後でコンポーネントの中に注入されるためのロジックを定義する過程です。 クラスで構成する理由は,コンポーネントロジックのリユース性を高めるためです。 (VueのMix-inパターンをタイプスクリプト的に解決しながらも、複雑度を下げるための方法です。)他のファイルで参照されるように全ての要素を「エクスポート」します。 123 | 124 |
125 | 126 | ```typescript 127 | import { ITodoItem } from './declare' 128 | import { todo } from './define' 129 | import { VueAPI } from '~/core' 130 | 131 | /** 132 | * することリストステータス使用注入クラスです。 133 | */ 134 | export class UseTodo { 135 | /** 136 | * することリストステータスリポジトリ 137 | */ 138 | todo = todo 139 | /** 140 | * バインディングされたすることリストのステータスリポジトリ 141 | */ 142 | $todo = todo.bind() 143 | 144 | /** 145 | * 作業中のリスト 146 | */ 147 | pending = VueAPI.computed(() => { 148 | return this.$todo.todoList.filter(item => { 149 | return !item.done 150 | }) 151 | }) 152 | 153 | /** 154 | * 完了した作業リスト 155 | */ 156 | completed = VueAPI.computed(() => { 157 | return this.$todo.todoList.filter(item => { 158 | return item.done 159 | }) 160 | }) 161 | } 162 | ``` 163 | 164 | 165 | 166 |
167 | 168 | ### 💊 使用 (use) 169 | 170 | > 使用(use)プロセスとは、コンポーネント内で実際にこの状態のリポジトリを使用しようとするときに呼び出すだけで該当リポジトリとそれに関連するコンポーネントロジックを注入し、リポジトリに関連するコンポーネントロジックをすぐに使用できるようにしてくれる関数を定義するプロセスです。 他のファイルで参照できるようにすべての要素を「エクスポート」します。 171 | 172 |
173 | 174 | ```typescript 175 | import { todo } from './define' 176 | import { UseTodo } from './inject' 177 | 178 | /** 179 | * することリストステータス使用関数 180 | */ 181 | export const useTodo = () => { 182 | // 状態を使用するクラスのインスタンスを作成します。 183 | const todoInstance = new UseTodo() 184 | 185 | // 以前に保存されたローカルストレージの値をロードします。 186 | todoInstance.getTodos() 187 | 188 | // リストの変更に応じて、値をローカル ストレージに保存します。 189 | todo.subscribe(data => { 190 | try { 191 | if (window) { 192 | window.localStorage.setItem('todo_list', JSON.stringify(data.todoList)) 193 | } 194 | } catch (e) {} 195 | }) 196 | 197 | return todoInstance 198 | } 199 | ``` 200 | 201 | 202 | 203 |
204 | 205 | ## 🍱 分離規則 206 | 207 | > 基本的には「宣言(declare)」「定義(declare)」「→「注入(inject)」「使用(use)」プロセスがそれぞれ英語名で一致する単一ファイル形態で実現します。 ですが、特定のプロセスのコードが長くなる場合、そのプロセス名をフォルダで定義してから、「index.ts」ファイルで参照されるようにコードをひとつのファイルから柔軟に分離してください。 208 | 209 |
210 | 211 | 212 | 213 |
214 | 215 | 216 | 217 | 218 | 219 | ## 🤔 単一ファイルの例 220 | 221 | > もしステータス リポジトリとそれに関連するロジックが簡単な場合や、いったん素早い作成が必要な場合、以下のように一つのファイルにすべてのロジックを収めることができます。 ただし、4段階の過程を最大限必ず明示してください。 222 | 223 |
224 | 225 | ```typescript 226 | import { Store } from 'vue-state-store' 227 | 228 | /** 229 | * カウンターステート宣言です。 230 | */ 231 | export interface ICounter { 232 | /** 233 | * 1ずつカウンティングされる数字です。 234 | */ 235 | count: number 236 | } 237 | 238 | /** 239 | * カウンターステートストレージ仕様を定義します。 240 | */ 241 | export class Counter extends Store {} 242 | 243 | /** 244 | * カウンターステートストレージの定義です。 245 | */ 246 | export const counter = new Counter({ 247 | count: 0 248 | }) 249 | 250 | /** 251 | * Counter State Injection クラスです。 252 | */ 253 | export class UseCount { 254 | /** 255 | * カウンタ ステート ストアです。 256 | */ 257 | counter = counter 258 | /** 259 | * バインドされたカウンタ ステート ストアです。 260 | */ 261 | $counter = counter.bind() 262 | /** 263 | * カウンタを 1 だけ上げるアクションです。 264 | */ 265 | up = () => this.$counter.count++ 266 | /** 267 | * カウンタを 1 だけドロップするアクションです。 268 | */ 269 | down = () => this.$counter.count-- 270 | } 271 | 272 | /** 273 | * カウンタ状態を使用する関数です。 274 | */ 275 | export const useCount = () => { 276 | const countInstance = new UseCount() 277 | return countInstance 278 | } 279 | ``` 280 | 281 | -------------------------------------------------------------------------------- /docs/0.advanced/README.KR.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | # 📮 vue-state-store 6 | 7 | > 완전히 타입스크립트를 지원하는 간단한 상태관리 체계 8 | 9 | 📬 뷰 전용 분산된 상태관리 모듈 체계 [발행-구독(Pub & Sub) 모델 기반] 10 | 11 |
12 | 13 | ## ⚗️ 권장 디자인 패턴 14 | 15 | > `vue-state-store` 를 통한 상태 저장소 개발 시 권장되는 디자인 패턴을 설명합니다. 16 | 17 | 컴포넌트 정의 시 아래와 같은 방식을 통해서 구현하는 것을 권장합니다. 18 | 19 | 선언(declare) → 정의(define) → 주입(inject) → 사용(use) 20 | 21 |
22 | 23 | ### 📐 선언 (declare) 24 | 25 | > 선언 (declare) 과정은 실제로 상태 저장소에 담길 자료형을 미리 타입스크립트 인터페이스로 정의하는 과정을 의미합니다. 다른 파일에서 참조 될 수 있도록 모든 요소를 `export` 합니다. 26 | 27 |
28 | 29 | 30 | 31 |
32 | 33 | ### 📦 정의 (define) 34 | 35 | > 정의 (define) 과정은 상태 저장소가 어떠한 타입스크립트 타입인 값을 담을지, 초기에 어떠한 값을 담을 정의하는 과정입니다. 다른 파일에서 참조 될 수 있도록 모든 요소를 `export` 합니다. 36 | 37 |
38 | 39 | 40 | 41 |
42 | 43 | ### 💉 주입 (inject) 44 | 45 | > 주입 (inject) 과정은 컴포넌트 내에서만 존재할 수 있는 로직들로로, 나중에 컴포넌트 안으로 주입되기 위한 로직들을 정의하는 과정입니다. 클래스로 구성하는 이유는, 컴포넌트 로직의 재사용성을 높이기 위해서 입니다. (Vue 의 Mix-in 패턴을 타입스크립트 적으로 해결하면서도 복잡도를 낮추기 위한 방법입니다.) 다른 파일에서 참조 될 수 있도록 모든 요소를 `export` 합니다. 46 | 47 |
48 | 49 | 50 | 51 |
52 | 53 | ### 💊 사용 (use) 54 | 55 | > 사용 (use) 과정은 컴포넌트 내에서 실제로 이 상태 저장소를 사용하려 할 때 호출만 하면 해당 저장소와 그와 관련된 컴포넌트 로직들을 주입해주어서 저장소와 관련 컴포넌트 로직들을 바로 사용 할 수 있게 해주는 함수를 정의하는 과정입니다. 다른 파일에서 참조 될 수 있도록 모든 요소를 `export` 합니다. 56 | 57 |
58 | 59 | 60 | 61 |
62 | 63 | ## 🍱 분리 규칙 64 | 65 | > 기본적으로는 `선언(declare)` → `정의(define)` → `주입(inject)` → `사용(use)` 과정을 각각 영어명으로 일치하는 단일 파일 형태로 구현합니다. 하지만 특정 과정이 매우 코드가 길어지는 경우 해당 과정 명을 폴더로 정의해주신 후 `index.ts` 파일을 통해서 참조되도록 코드를 하나의 파일에서 유연하게 분리해주시기 바랍니다. 66 | 67 |
68 | 69 | 70 | 71 |
72 | 73 | 74 | 75 | 76 | 77 | ## 🤔 단일 파일 예시 78 | 79 | > 만약 상태 저장소와 그와 관련된 로직이 간단한 경우나, 일단 빠른 작성이 필요한 경우 아래와 같이 한 파일에 모든 로직을 담을 수 있습니다. 다만 4단계 과정을 최대한 꼭 명시 해주세요. 80 | 81 |
82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /docs/README.JP.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | # 📮 vue-state-store (vss) 6 | 7 | > 完全に Typescript をサポートし簡単に使うことができる状態管理ライブラリです。 8 | 9 | 📬 Vue 向けに作成された分散型の状態管理ライブラリです。 [出版-購読型モデル(Pub & Sub)による] 10 | 11 |
12 | 13 | ## 🌎 翻訳支援 14 | 15 | @yopinojiさん、ありがとうございます。😊😊 16 | 17 |
18 | 19 | ## 😊 使いやすく強力な状態管理ライブラリ 20 | 21 | `vue-state-store` は、従来の Vue でよく使われた Vuex モジュールを完全に代替するために開発されたモジュールです。 **このモジュールは Type Script の効率を 200% 使用して、状態管理(state management)をとても容易にすることを目標にしています。** 22 | 23 |
24 | 25 | ### 💡 vuex と比較したときの長所 26 | 27 | - **低い学習コスト** - 簡単に使える出版-購読型モデル(Pub & Sub)を使用 28 | - **Typescript Intellisense 対応** - ストアアクションミューテーションなどを参照する際にエディタによる補完が有効 29 | - **自動バインディング関数** - 簡単に Vue テンプレートにバインディング可能 30 | - **純粋な TypeScript によるクラス定義** - mix-in を使う必要はありません 31 | - **一元化されたアクション構造** - アクションとミューテーションを区別することなく柔軟に使用可能 32 | - **柔軟な状態の使用が可能** - ミューテーションを省きゲッターとしても使用可能 33 | 34 |
35 | 36 | ### 💬 インストール方法 37 | 38 | > (Vue2、Vue3、Nuxt に対応しており、Composition API のバインディング関数。`.bind()`に対応しています。) 39 | 40 | ```bash 41 | npm i vue-state-store 42 | ``` 43 | 44 |
45 | 46 | ### 🔮 Devtools 適用 47 | 48 | > `vue-state-store` は `vue-devtools` をサポートします。 (`vuex` タブで `vue-state-store` を通じて生成された状態を見ることができます。) 49 | 50 | [関連するリンク](https://github.com/AhaOfficial/vue-state-store-devtools) 51 | 52 |
53 | 54 | ### 📬 出版-購読型モデルの説明 55 | 56 | > `vue-state-store (vss)` は、出版-購読型モデルによるデザインパターンを使用します。 57 | 58 | `vue-state-store (vss)` は値を格納するメモリ内に存在するストレージです。`.subscribe(callback)` 関数を通じて、ストレージ内の値が変更されたときにコールバックに変更された値を受け取ることができ、`.set(newValue)`と`.update((data) => data)`を通じて、ストレージ内の値をアップデートすることができます。 59 | 60 |
61 | 62 | ## 😎 基本的な使い方 63 | 64 | > `vue-state-store (vss)`は、関数またはクラスを通じて、ストアアクションミューテーション の両方を簡単に管理することができます。 65 | 66 |
67 | 68 | ### 📮 基本的な型での出版-購読モデルの構築 69 | 70 | > 基本型として 5 つの基本**型(Types)**(number, string, boolean, null, undefined)があります。 71 | 72 | ```typescript 73 | import { store } from 'vue-state-store' 74 | 75 | // ステータスを保存するオブジェクトを作成します。 76 | const version = store(0) // 初期値を挿入します。 77 | 78 | // Get 79 | version.get() // 0 が返されます。 80 | 81 | // Set 82 | version.set(1) // 状態を 1 に更新します。 83 | 84 | // Update 85 | version.update((data) => { // 既存の状態値に 1 を追加します。 86 | data += 1 87 | return data 88 | }) 89 | 90 | // Subscribe 91 | version.subscribe((data)=> { 92 | console.log('次の値が変更されました。: ', data) 93 | }) 94 | 95 | // Unsubscribable 96 | const unsubscribe = 97 | version.subscribe((data) => { 98 | console.log('次の値が変更されました。:', data) 99 | }) 100 | // 次の機能を呼び出すと、いつでもストレージの 101 | // サブスクリプションを中断することができます。 102 | unsubscribe() 103 | ``` 104 | 105 |
106 | 107 | > `.subscribe()` 関数は実行されると返り値として関数を与えますが、この関数を実行させると好きなときに状態のサブスクリプションを中断させることができます。 そのため返り値として受け取った変数を「unsubscribe」と命名しています。 108 | 109 |
110 | 111 | ### 📮 オブジェクトでの出版-購読モデルの構築 112 | 113 | > 下を見ると、上(基本的な型)と使い方の違いがほとんどないことが分かります。 114 | > 115 | > `store(value)`へのvalueは基本的な型またはオブジェクトになります。 116 | 117 | ```typescript 118 | import { store } from 'vue-state-store' 119 | 120 | // 状態を保存するオブジェクトを作成します。 121 | const detail = store({ 122 | version: 0, 123 | author: 'AhaOfficial' 124 | }) // 初期値を挿入します。 125 | 126 | // Get 127 | detail.get() // オブジェクトを返します。 128 | 129 | // Set 130 | detail.set({ 131 | version: 1, 132 | author: 'AhaOfficial' 133 | }) // 状態をその値に更新します。 134 | 135 | // Update 136 | detail.update((data) => { // 既存の状態値に 1 を追加します。 137 | data.version += 1 138 | return data 139 | }) 140 | 141 | // Subscribe 142 | detail.subscribe((data)=> { 143 | console.log('次の値が変更されました。:', data) 144 | }) 145 | 146 | // Unsubscribable 147 | const unsubscribe = 148 | detail.subscribe((data) => { 149 | console.log('次の値が変更されました。:', data) 150 | }) 151 | // 次の機能を呼び出すと、いつでもストレージの 152 | // サブスクリプションを中断することができます。 153 | unsubscribe() 154 | ``` 155 | 156 | 157 | 158 |
159 | 160 | ### 📮 ステータスおよび組み込みアクション作成 161 | 162 | > 「vue-state-store」はクラスを継承することで組み込みアクションを定義することができます。 163 | 164 | `vue-state-store`ではアクションとミューテーションの区別は必要ありません。 165 | 166 | - クラスに任意の関数を生成することで、**組み込みアクション(Embedded Action)**の構成が可能となります。 167 | - クラスに組み込まずに状態を変化させるすべての関数は外部アクション(Outside Action)と名称します。 168 | 169 | ```typescript 170 | import { Store } from 'vue-state-store' 171 | 172 | // ストアのインターフェイスを事前に定義します。 173 | export interface IVote { 174 | upVoteCount: number 175 | downVoteCount: number 176 | } 177 | 178 | // ストアを継承してクラスを作成します。 179 | export class Vote extends Store { 180 | 181 | // Action または Mutation は、.update および 182 | // .set を使用して、スクリプト クラスの関数として自由に定義できます。 183 | async upVote() { 184 | await this.update((data) => { 185 | data.upVoteCount += 1 186 | return data 187 | }) 188 | } 189 | async downVote() { 190 | await this.update((data) => { 191 | data.upVoteCount -= 1 192 | return data 193 | }) 194 | } 195 | syncWithNetwork() { 196 | // API通信機能も自由にご利用いただけます。 197 | } 198 | } 199 | 200 | // ストアを作成し、初期値を追加します。 201 | export const vote = new Vote({ 202 | upVoteCount: 0, 203 | downVoteCount: 0, 204 | }) 205 | 206 | // アクションによる状態の更新を使用する例を示します。 207 | vote.upVote() 208 | vote.downVote() 209 | vote.syncWithNetwork() 210 | ``` 211 | 212 | 213 | 214 |
215 | 216 | ### 📮 Vueテンプレート内でのバインディング 217 | 218 | > `vue-state-store` は、保持している状態を vue テンプレートタグに簡単にバインドすることができ、バインドされた状態はテンプレートタグ内で Typescript Intellisense によるサポートを受けられます。Typescript Intellisense は、script タグ内のストアに埋め込まれたアクションを使用する場合にもサポートされます。 219 | 220 | - `import`を通じてストアを持ってくるだけで内蔵されたアクションの呼び出しが可能です。 221 | - `.bind()`関数を通じて状態をテンプレート内に自動的にバインディングすることができます。 222 | - `return { $vote: vote.bind() }` のように既存のストレージ名の前に「$」を付けることをネーミング規則として推奨します。 223 | - `.bind()`関数は**@vue/composition-api** の`setup`関数内で使用することが推奨されます。 224 | 225 | ```typescript 226 | 236 | 237 | 257 | ``` 258 | 259 |
260 | 261 | ## 🚀 応用した使い方を説明 262 | 263 | > 応用した使用方法について説明します(学習コストはあまり上がりません)。 264 | 265 |
266 | 267 | ### ⏳ Async の使用方法 268 | 269 | > `.update()` 関数と `.set()` 関数は Promise を返します。 270 | 271 |
272 | 273 | - await を通じてストレージの値を更新する際、その更新作業が完了した後、特定のロジックが実行されるようにすることができます。 274 | 275 | ```typescript 276 | // Promise Update 277 | await version.update((data) => { 278 | data += 1 279 | return data 280 | }) 281 | await version.set({}) 282 | ``` 283 | 284 |
285 | 286 | - update 関数で使われるコールバック関数に async キーワードを付けて定義することもできます。 287 | 288 | ```typescript 289 | // Promise Update 290 | await detail.update( 291 | async (data) => { 292 | const response = await fetch('https://~~~') 293 | data = response.data 294 | return data 295 | } 296 | ) 297 | ``` 298 | 299 |
300 | 301 | ### 💡 Vscode インテリセンス適用方法 302 | 303 | > Vscode で vue と Typescript を同時に使用する場合、intellisense のサポートを受けるためには以下のモジュールを受ける必要があります。 304 | 305 | [Vetur]: https://marketplace.visualstudio.com/items?itemName=octref.vetur 306 | 307 | Vetur インストール後、テンプレート内の intellisense に対応するためには以下のオプションを Vscode の設定に追加する必要があります。 308 | 309 | ```json 310 | "vetur.experimental.templateInterpolationService": true 311 | ``` 312 | 313 |
314 | 315 | ### 📮 (応用編) 状態管理関数デザインパターン 316 | 317 | > `vue-state-store` は、Composition API を極限に活用し、React Hooks と類似した「use~」で始まる状態管理関数のデザインパターンの例を提供します。 318 | 319 | 状態管理関数は、状態をコンポーネント内部で `useTodo` のように(Todoという状態があれば)前に `use` という単語が付く関数を実行し、その結果返ってきた値で状態を受け取るという使い方を意味します。 320 | 321 | > これにより、状態管理にコンポーネントのライフサイクルを使用することができます。 322 | 323 | アクセサ(`.set()や.update()`)を使って状態を変更する場合、既存の一般変数を変更する場合とは異なり、複雑なロジックを作成するのが非常に面倒になります。 324 | 325 | `vue-state-store` を使うと、このようなアクセサを使わなくても、ストア内のバインディングに直接アクセスすることで、便利に状態を変更することができます。この設計パターンは、コンポジションAPIを使用する際に状態利用機能を設定する際の単純な例に過ぎません(必ずしもそうではありません)、**複雑な方法で状態を変更する必要がある場合**や、**複数の`computed`オブジェクトを作成する必要がある場合**などが考えられます。 326 | 327 | バインドされた値を変更した場合でも、値が変更されるたびに、その変更は自動的に購読しているコールバック関数により更新されます。 328 | 329 | ```typescript 330 | import { Store } from 'vue-state-store' 331 | import * as VueAPI from '@vue/composition-api' 332 | 333 | // ステータス値の規格を定義します。 334 | export interface ITodo { 335 | todoList: string[] 336 | } 337 | 338 | // ステータスクラスを定義します。 339 | export class Todo extends Store { 340 | 341 | // 状態値をストア クラス内でバインドします。 342 | value = this.bind() 343 | 344 | // 計算された値を定義します。 345 | pending = VueAPI.computed(() => { 346 | return this.value.todoList.filter(item => { 347 | return item.indexOf('(DONE)') == -1 348 | }) 349 | }) 350 | 351 | // Todo を追加します。 352 | addItem(newTodo) { 353 | this.value.todoList.unshift(newTodo) 354 | } 355 | 356 | // Todo を消します。 357 | deleteItem(item: string) { 358 | this.value.todoList.splice(this.value.todoList.indexOf(item), 1) 359 | } 360 | 361 | // ローカル ストレージに保存されているスケジュールを読み込みます。 362 | getTodos() { 363 | try { 364 | if (window.localStorage.getItem('todo_list')) { 365 | this.value.todoList = 366 | JSON.parse( 367 | window.localStorage.getItem('todo_list') 368 | ) 369 | } 370 | } catch (e) { } 371 | } 372 | 373 | } 374 | ``` 375 | 376 | 377 | 378 | > コールバック関数でバインドされたストアの値を `computed` でラップしたコールバック関数で参照すると、ストアの値が変わるたびにコールバック関数が再実行されます。これにより、関数が呼び出されるたびに各関数を再計算する疲労が軽減され、ストアを使用する際のパフォーマンスが向上します。 379 | 380 | ```typescript 381 | // 状態管理関数を定義します。 382 | export const useTodo = () => { 383 | 384 | // state 使用関数を定義します。 385 | const todo = new Todo({ 386 | todoList: [ 387 | 'リスにピーナッツをあげる', 388 | '猫にチュールをあげる', 389 | 'Vueによる例を作成する必要がある', 390 | ] 391 | }) 392 | 393 | // 以前にローカル ストレージに 394 | // 保存されていた状態値をロードします。 395 | todo.getTodos() 396 | 397 | // ToDo リストが変更されたときに、 398 | // 値をローカル ストレージに保存します。 399 | todo.subscribe(data => { 400 | try { 401 | window.localStorage.setItem( 402 | 'todo_list', 403 | JSON.stringify(data.todoList)) 404 | } catch (e) { } 405 | }) 406 | 407 | return todo 408 | } 409 | ``` 410 | 411 | 412 | 413 | > Vue のライフサイクルや `computed` を定義するには、必ず `useTodo` のように隔離された関数を一つ作ってから、この関数をコンポーネントの中から呼び出して使う必要があります。 414 | 415 | ```typescript 416 | import * as VueAPI from '@vue/composition-api' 417 | import { useTodo } from '~/store' 418 | 419 | export default VueAPI.defineComponent({ 420 | setup(props, context) { 421 | return { 422 | // Todo状態をこのコンポーネントで使用します。 423 | todo: useTodo() 424 | } 425 | } 426 | }) 427 | ``` 428 | 429 | 430 | 431 | > 上記のように `useTodo()` を使用後、すぐに template タグの中で使うこともできます。 (勿論、この場合にも Typescript Intellisense はサポートされます。) 432 | 433 |
434 | 435 | ### 📮 (応用編) 宣言-定義-注入-使用デザインパターン 436 | 437 | > 該当デザインパターンは、別途のページでご確認ください。 438 | 439 | [説明リンク](https://github.com/AhaOfficial/vue-state-store/blob/master/docs/0.advanced/README.JP.md) 440 | 441 |
442 | 443 | ## 🤔 Q&A 444 | 445 | > Github Issues 欄での質問も可能です。 446 | 447 |
448 | 449 | ### 🧲 Q. vuexのように全てのストレージの状態を持っている$storeはないのですか? 450 | 451 | > A. はい、ありません。 非推奨パターンです。 452 | 453 | 任意の index.ts ファイルを作成した後、`import { vote } from '~/store` のように一部のストアだけを持ってきて使用することをお勧めします。 `vue-state-store` は分散した構造を持ち、各ストレージが必要な場合に限って個別に参照することができます。 454 | 455 | `.bind()`関数を介してvueテンプレートタグ内で使用されるまで `vue-state-store` は `vue` と完全に独立した状態で構成されます。 456 | 457 |
458 | 459 | ### 👀 Q. `.bind()`した値が変更されると、変更された値は再びレンダリングされますか? 460 | 461 | > A. はい、Vue の ref によって変更されたリポジトリの値がリアルタイムでテンプレートタグを通じて DOM に反映されます。 462 | 463 |
464 | 465 | ### 📡 Q. `.bind()` した値は双方向バインディングですか? 466 | 467 | > A. はい、双方向バインディングです。格納されている値が変更されればバインディングされている値も変化しますし、バインディングされている値が変化すれば格納されている値も変更されます。 468 | 469 |
470 | 471 |
472 | 473 | ## 📔 ライセンス 474 | 475 | > Copyright (c) 2020 AhaOfficial 476 | 477 | **MIT Licensed** 478 | 479 | -------------------------------------------------------------------------------- /docs/README.KR.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | # 📮 vue-state-store (vss) 6 | 7 | > 완전히 타입스크립트를 지원하는 간단한 상태관리 체계 8 | 9 | 📬 뷰 전용 분산된 상태관리 모듈 체계 [발행-구독(Pub & Sub) 모델 기반] 10 | 11 |
12 | 13 | ## 📔 목차 14 | 15 | * [😊 쉬운 사용! & 강력한 응용!](#-쉬운-사용--강력한-응용) 16 | * [💡 vuex 와 비교하였을때 장점](#-vuex-와-비교하였을때-장점) 17 | * [💬 설치 방법](#-설치-방법) 18 | * [🔮 데브툴 설치](#-데브툴-설치) 19 | * [📬 발행 & 구독 모델 설명](#-발행--구독-모델-설명) 20 | * [😎 기본적인 사용방법](#-기본적인-사용방법) 21 | * [📮 기본 타입 발행 & 구독](#-기본-타입-발행--구독) 22 | * [📮 객체 타입 발행 & 구독](#-객체-타입-발행--구독) 23 | * [📮 상태 및 내장된 액션 생성](#-상태-및-내장된-액션-생성) 24 | * [📮 Vue 템플릿 내에서의 바인딩](#-vue-템플릿-내에서의-바인딩) 25 | * [🚀 고급 사용법 설명](#-고급-사용법-설명) 26 | * [⏳ 비동기 사용 방법](#-비동기-사용-방법) 27 | * [💡 vscode 인텔리센스 적용방법](#-vscode-인텔리센스-적용방법) 28 | * [📮 (고급) 상태 사용함수 디자인 패턴](#-고급-상태-사용함수-디자인-패턴) 29 | * [📮 (고급) 선언-정의-주입-사용 디자인 패턴](#-고급-선언-정의-주입-사용-디자인-패턴) 30 | * [🤔 Q&A](#-qa) 31 | * [🧲 Q. vuex 처럼 모든 상태 저장소들을 다 가지고 있는 $store는 없나요?](#-q-vuex-처럼-모든-상태-저장소들을-다-가지고-있는-store는-없나요) 32 | * [👀 Q. `.bind()` 된 값이 변경되면 변경된 값이 다시 렌더링되나요?](#-q-bind-된-값이-변경되면-변경된-값이-다시-렌더링되나요) 33 | * [📡 Q. `.bind()` 된 값은 양방향 바인딩 인가요?](#-q-bind-된-값은-양방향-바인딩-인가요) 34 | * [📔 라이센스](#-라이센스) 35 | 36 |
37 | 38 | ## 😊 쉬운 사용! & 강력한 응용! 39 | 40 | `vue-state-store (vss)` 는 기존의 `vue` 에서 많이 사용되던 `vuex` 모듈을 완전히 대체하기 위해서 나온 모듈입니다. 이 모듈은 **타입스크립트의 효율을 200% 로 사용해서 상태관리를 매우 쉽게 만드는 것을 목표로 합니다.** 41 | 42 |
43 | 44 | ### 💡 vuex 와 비교하였을때 장점 45 | 46 | - **낮은 러닝커브** - 간단한 발행 & 구독 모델 사용 47 | - **Typescript Intellisense** 지원 - 상태 & 액션& 뮤테이션 & 템플릿 내 변수 사용 시 48 | - **자동 바인딩 함수** - 간편한 vue template 바인딩 가능 49 | - **순수 타입스크립트 클래스 기반 정의** - mix-in 사용 불필요 50 | - **일원화 된 액션 구조** - Action 과 Mutation 구별 없이 유연한 사용 가능 51 | - **유연한 상태 사용 허용** - Mutation 을 생략하면 Getters 로 사용 가능 52 | 53 |
54 | 55 | ### 💬 설치 방법 56 | 57 | > (Vue2, Vue3, Nuxt 를 지원하며, Composition API 에서 자동 바인딩 함수 `.bind()` 를 지원합니다.) 58 | 59 | ```bash 60 | npm i vue-state-store 61 | ``` 62 | 63 |
64 | 65 | ### 🔮 데브툴 적용 66 | 67 | > `vue-state-store` 는 `vue-devtools` 를 지원합니다. (`vuex` 탭에서 `vue-state-store` 를 통해 생성된 상태 저장소들의 정보들을 보실 수 있습니다.) 68 | 69 | [관련된 내용 보기](https://github.com/AhaOfficial/vue-state-store-devtools) 70 | 71 |
72 | 73 | ### 📬 발행 & 구독 모델 설명 74 | 75 | > vue-state-store 는 발행 & 구독 디자인 패턴을 사용합니다. 76 | 77 | `vue-state-store` 는 값을 저장한 메모리 상에 존재하는 저장소입니다. `.subscribe(callback)` 함수를 통해서 저장소 안의 값이 변경될 때 콜백으로 변경된 값을 전달 받을 수 있으며, `.set(newValue)` 과 `.update((data) => data)` 를 통해 저장소 안의 값을 업데이트 할 수 있습니다. 78 | 79 |
80 | 81 | ## 😎 기본적인 사용방법 82 | 83 | > `vue-state-store` 는 함수 또는 클래스를 통해 상태 & 액션 & 뮤테이션을 모두 쉽게 관리할 수 있습니다. 84 | 85 |
86 | 87 | ### 📮 기본 타입 발행 & 구독 88 | 89 | > 기본 타입은 다섯가지 기본 **타입** (number, string, boolean, null, undefined) 을 뜻합니다. 90 | 91 | 92 | 93 | > `.subscribe()` 함수는 실행되면 반환 값으로 함수를 주는데 이 함수를 실행시키면 원하는 때에 저장소 구독을 중단시킬 수 있습니다. 그래서 이름을 unsubscribe 와 같이 적습니다. 94 | 95 |
96 | 97 | ### 📮 객체 타입 발행 & 구독 98 | 99 | > 아래를 보시면 위와 사용법 차이가 거의 없다는 것을 확인하실 수 있습니다. 100 | > 101 | > `store(value)` 에서 value 는 기본 타입 또는 객체가 될 수 있습니다. 102 | 103 | 104 | 105 |
106 | 107 | ### 📮 상태 및 내장된 액션 생성 108 | 109 | > `vue-state-store` 는 클래스를 상속함으로써 내장된 액션을 정의가능합니다. 110 | 111 | `vue-state-store` 에서는 Action 과 Mutation 의 구별이 필요하지 않습니다. 112 | 113 | - 클래스에 임의의 함수를 생성함으로써 **내장된 액션(Embedded Action)** 의 구성이 가능합니다. 114 | - 클래스에 내장되지 않은채로 상태를 변형하는 모든 함수는 **외부 액션(Outside Action)** 으로 명칭합니다. 115 | 116 | 117 | 118 |
119 | 120 | ### 📮 Vue 템플릿 내에서의 바인딩 121 | 122 | > `vue-state-store`는 vue template 태그에 저장소를 쉽게 바인딩할 수 있으며, 바인딩 된 스토어는 템플릿 태그 내에서도 Typescript Intellisense 가 계속 지원됩니다. 또한 script 태그 내에서 저장소에 내장된 액션을 사용시에도 Typescript Intellisense 가 지원됩니다. 123 | 124 | - `import` 를 통해서 상태를 불러오는 것만으로 내장된 액션의 호출이 가능합니다. 125 | - `.bind()` 함수를 통해서 저장소를 템플릿 내로 자동으로 바인딩할 수 있습니다. 126 | - `return { $vote: vote.bind() }` 와 같이 기존 저장소 명 앞에 `$` 를 붙이는 것을 네이밍 규칙으로 권장합니다. 127 | - `.bind()` 함수는 **@vue/composition-api** 의 `setup` 함수 내에서 사용하는 것이 권장됩니다. 128 | 129 | 130 | 131 |
132 | 133 | ## 🚀 고급 사용법 설명 134 | 135 | > 고급적으로 사용할 수 있는 방법들을 설명합니다. (러닝커브를 높이지 않습니다.) 136 | 137 |
138 | 139 | ### ⏳ 비동기 사용 방법 140 | 141 | > `.update()` 함수와 `.set()` 함수는 Promise 를 반환합니다. 142 | 143 |
144 | 145 | - await 을 통해 저장소 값을 갱신할 때 해당 갱신 작업이 완료된 다음 특정 로직이 실행되도록 할 수 있습니다. 146 | 147 | 148 | 149 | 150 | 151 | - update 함수에 전달되는 콜백을 async 로 정의할 수도 있습니다. 152 | 153 | 154 | 155 |
156 | 157 | ### 💡 vscode 인텔리센스 적용방법 158 | 159 | > vscode 에서 vue 와 typescript 를 동시에 사용 시 인텔리센스를 지원받으려면 아래 모듈을 받으시면 됩니다. 160 | 161 | [Vetur]: https://marketplace.visualstudio.com/items?itemName=octref.vetur 162 | 163 | vetur 설치후 템플릿 내 인텔리센스를 지원받기 위해서 아래의 옵션을 vscode 설정에 추가해주어야 합니다. 164 | 165 | ```json 166 | "vetur.experimental.templateInterpolationService": true 167 | ``` 168 | 169 |
170 | 171 | ### 📮 (고급) 상태 사용함수 디자인 패턴 172 | 173 | > `vue-state-store`는 컴포지션 API를 극한으로 활용하되, React Hooks 와 유사하게 `use~` 로 시작하는 상태 사용함수 디자인 패턴 예시를 제공합니다. 174 | 175 | 상태 사용함수는 상태를 컴포넌트 내부에서 `useTodo` 와 같이 (Todo 라는 상태가 있다면,) 앞에 `use` 라는 단어가 붙는 함수를 실행시키고 그 결과 값으로 상태 저장소를 받는 사용 방식을 뜻합니다. 176 | 177 | > 이러한 과정을 통해서 상태 저장소에서 컴포넌트의 생명주기를 사용할 수 있습니다. 178 | 179 | 접근자(`.set() 과 .update()`) 를 통해서 상태를 변형을 진행하다보면, 기존의 일반 변수들을 수정할때와 달라서 복잡한 로직을 작성할 때 무척 번거로울 수 있습니다. 180 | 181 | `vue-state-store` 는 이러한 접근자의 사용 없이, 스토어 내에서 바인딩을 내장해서 직접 접근해서 편리하게 상태를 변형할 수 있습니다. 이러한 디자인 패턴은 컴포지션 API 를 사용 시 상태 사용함수를 구성하는 단순 예시일 뿐, 반드시 사용해야하는 것은 아니며, **복잡하게 상태를 변형할 경우**나, **`computed` 객체를 여러개 만들어야하는 경우**에 사용하면 됩니다. 182 | 183 | 바인딩된 값을 변형하더라도 값이 바뀔때마다 구독하고 있는 콜백들에게 바뀐 점이 자동으로 배포됩니다. 184 | 185 | 186 | 187 | > `computed` 로 감싸여진 콜백에서 바인딩 된 스토어 값을 참조하면, 해당 스토어 값이 다르게 바뀔 때 마다 해당 콜백이 다시 발생됩니다. 이는 매 함수가 불려질때마다 다시 연산하는 피로도를 줄여서 상태 사용 시 성능을 높일 수 있습니다. 188 | 189 | 190 | 191 | > Vue 의 라이프사이클이나 `computed` 를 정의하려면 반드시 `useTodo` 와 같이 격리된 함수를 하나 만든 후 이 함수를 컴포넌트 안에서 호출해서 사용하도록 정의해야합니다. 192 | 193 | 194 | 195 | > 위와 같이 useTodo() 를 사용한 후 바로 template 태그 안에서 사용할 수 있습니다. (물론 이경우에도 Typescript Intellisense 는 지원됩니다.) 196 | 197 |
198 | 199 | ### 📮 (고급) 선언-정의-주입-사용 디자인 패턴 200 | 201 | > 해당 디자인 패턴은 별도의 페이지를 통해서 확인해주세요. 202 | 203 | [설명 보기](https://github.com/AhaOfficial/vue-state-store/blob/master/docs/0.advanced/README.KR.md) 204 | 205 |
206 | 207 | ## 🤔 Q&A 208 | 209 | > Github Issues 란을 통한 질문 또한 가능합니다. 210 | 211 |
212 | 213 | ### 🧲 Q. vuex 처럼 모든 상태 저장소들을 다 가지고 있는 $store는 없나요? 214 | 215 | > A. 네 없습니다. 비 권장 패턴입니다. 216 | 217 | 임의의 index.ts 파일을 만든 후 `import { vote } from '~/store` 와 같이 일부 스토어만 가져와서 사용하는 것이 권장됩니다. `vue-state-store` 는 분산된 구조를 가지며, 각 저장소가 필요한 경우에만 개별적으로 서로를 참조할 수 있습니다. 218 | 219 | `.bind()` 함수를 통해서 vue 템플릿 태그 내에서 사용되기 전까지 `vue-state-store` 는 `vue` 와 완전히 독립된 상태로 구성됩니다. 220 | 221 |
222 | 223 | ### 👀 Q. `.bind()` 된 값이 변경되면 변경된 값이 다시 렌더링되나요? 224 | 225 | > A. 네 vue의 ref 를 통해서 변경된 저장소 값이 실시간으로 템플릿 태그를 통해 DOM에 반영됩니다. 226 | 227 |
228 | 229 | ### 📡 Q. `.bind()` 된 값은 양방향 바인딩 인가요? 230 | 231 | > A. 네 바인딩 값은 저장소 값이 변경되면 바인딩 값도 변화될 뿐만 아니라 바인딩 값이 변화되면 저장소 값도 변경됩니다. 232 | 233 |
234 | 235 |
236 | 237 | ## 📔 라이센스 238 | 239 | > Copyright (c) 2020 AhaOfficial 240 | 241 | **MIT Licensed** 242 | 243 | -------------------------------------------------------------------------------- /docs/README.PL.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 📮 vue-state-store (vss) 4 | 5 | > Prosty system zarządzania stanem aplikacji, w pełni wspierający typescript 6 | 7 | 📬 Rozproszony system zarządzania stanem dla Vue. [Pub & Sub model based] 8 | 9 |
10 | 11 | ## 🌎 Wsparcie w tłumaczeniach 12 | 13 | Dziękuję! @Milesq 😊😊 14 | 15 |
16 | 17 | ## 📔 Spis treści 18 | 19 | - [😊 Łatwy w użyciu! & Niesamowite możliwości!](#-łatwy-w-użyciu--niesamowite-możliwości) 20 | - [💡 Plusy w porównaniu do vuex](#-plusy-w-porównaniu-do-vuex) 21 | - [💬 Instalacja](#-instalacja) 22 | - [📬 Opis Wzorca Pub & Sub](#-opis-wzorca-pub--sub) 23 | - [😎 Podstawy](#-podstawy) 24 | - [📮 Primitive Type Pub & Sub](#-primitive-type-pub--sub) 25 | - [📮 Object Type Pub & Sub](#-object-type-pub--sub) 26 | - [📮 Akcje wewnętrzę i store'a jako klasa](#-akcje-wewnętrzę-i-storea-jako-klasa) 27 | - [📮 Używanie wraz z Vue component template](#-używanie-wraz-z-vue-component-template) 28 | - [🚀 Używanie zaawansowane](#-używanie-zaawansowane) 29 | - [⏳ Asynchroniczność](#-asynchroniczność) 30 | - [💡 Vscode Intellisense](#-vscode-intellisense) 31 | - [📮 (Zaawansowane) Wzorzec Funkcja Używająca Stan (and. State Use Function)](#-zaawansowane-wzorzec-funkcja-używająca-stan-and-state-use-function) 32 | - [🤔 Q&A](#-qa) 33 | - [🧲 Q. Czy istnieje globalna zmienna `$store` która zawiera wszystkie pomniejszy store'y jak w vuex?](#-q-czy-istnieje-globalna-zmienna-store-która-zawiera-wszystkie-pomniejszy-storey-jak-w-vuex) 34 | - [👀 Q. Czy jeśli wartość `.bind()` zostanie zmieniona, komponent zostanie przerenderowany?](#-q-czy-istnieje-globalna-zmienna-store-która-zawiera-wszystkie-pomniejszy-storey-jak-w-vuex) 35 | - [📡 Q. Czy metoda `.bind()` utrzymuje powiązanie dwu-kierunkowe? (ang. two-way binding)](#-q-czy-metoda-bind-utrzymuje-powiązanie-dwu-kierunkowe-ang-two-way-binding) 36 | - [📔 Licencja](#-license) 37 | 38 |
39 | 40 | ## 😊 Łatwy w użyciu! & Niesamowite możliwości! 41 | 42 | `vue-state-store (vss)` to paczka która ma całkowicie zastąpić moduły (ang. modules) `vuex` które były często używane wraz z `vue`. **Celem tej paczki jest sprawienie, żeby zarządzanie stanem było jak najprostsze, wykorzystując 200% możliwości typescript'a.** 43 | 44 |
45 | 46 | ### 💡 Plusy w porównaniu do vuex 47 | 48 | - **Szybki start, mało kodu boilerplate** - Use simple publishing & subscription model 49 | - **Podpowiedzi typów w TypeScript** - Status & Actions & Mutation & When using variables within Templates 50 | - **Auto-Bind function** - Easy vue template binding. 51 | - **Pure typescript class based definition** - no need to use mix-in 52 | - **A unified action structure** - Flexible use with no distinction between action and motion. 53 | - **Allow flexible state use** - If you omit Mutation, you can use it as Getters. 54 | 55 |
56 | 57 | ### 💬 Instalacja 58 | 59 | > (Zarówno Vue2, Vue3 jak i Nuxt są wspierane. Wspierane jest automatyczne bindowanie funkcji w Composition API : .bind(')'.) 60 | 61 | ``` 62 | npm i vue-state-store 63 | yarn add vue-state-store 64 | ``` 65 | 66 |
67 | 68 | ### 📬 Opis Wzorca Pub & Sub 69 | 70 | > `vue-state-store` używa wzorca projektowego publish & subscription 71 | 72 | `vue-state-store` to pewna przestrzeń wewnątrz pamięci w której przechowywane są dane. Metoda `.subscribe(callback)` pozwala na wychwytywanie zmian stanu poprzez funkcję callback. Możesz też zmienić stan store'a poprzez metody `.set(nowaWartość)` i `.update(staraWartość => nowaWartość)` 73 | 74 |
75 | 76 | ## 😎 Podstawy 77 | 78 | > `vue-state-store` to łatwy sposób na obsługę stanów (ang. atate), akcji i mutacji poprzez funckcje lub klasy 79 | 80 |
81 | 82 | ### 📮 Primitive Type Pub & Sub 83 | 84 | > Typy podstawowe (ang. primitives) oznaczają pięć podstawowych typów danych (liczba (number), napis (string), typ logiczny tak/nie (boolean), null, undefined). 85 | 86 | ```typescript 87 | import { store } from 'vue-state-store' 88 | 89 | // Tworzenie obiektu aby przechowywać stan 90 | const version = store(0) // Jako pierwszy argument przekaż wartość początkową 91 | 92 | version.get() // zwróci 0, czyli wartość początkową. 93 | 94 | version.set(1) // Ustawia stan na podaną wartość, czyli w tym przypadku 1. 95 | 96 | version.update((data) => { 97 | // jako argument callback'a otrzymujesz wartość stanu 98 | // zinkrementuj wartość i zwróć ją 99 | data += 1 100 | return data 101 | }) 102 | 103 | version.subscribe((data) => { 104 | console.log('Następujące wartości zostały zmienione: ', data) 105 | }) 106 | 107 | const unsubscribe = version.subscribe((data) => { 108 | console.log('Następujące wartości zostały zmienione:', data) 109 | }) 110 | // Możesz przerwać subkskrybowanie stanu w dowolnym momencie 111 | // poprzez wywołanie funkcji `unsubscribe()` 112 | unsubscribe() 113 | ``` 114 | 115 | > Funkcja `.subscribe()` zwraca funkcję która przerywa subskrybcję 116 | 117 |
118 | 119 | ### 📮 Object Type Pub & Sub 120 | 121 | > Jeśli zerkniesz niżej, możesz zobaczyć, że istnieją pewne rónice między przykładami wyżej 122 | > 123 | > `store` może przyjmować prymitywy lub obiekty. 124 | 125 | ```typescript 126 | import { store } from 'vue-state-store' 127 | 128 | // Stwórz obiekt do przechowywania stanu. 129 | const detail = store({ 130 | version: 0, 131 | author: 'AhaOfficial', 132 | }) // I jako argument podaj obiekt początkowy 133 | 134 | detail.get() // Zwraca obiekt 135 | 136 | detail.set({ 137 | version: 1, 138 | author: 'AhaOfficial', 139 | }) // Ustaw nową wartość stanu 140 | 141 | detail.update((data) => { 142 | // zinkrementuj wartość i zwróć ją 143 | data.version += 1 144 | return data 145 | }) 146 | 147 | detail.subscribe((data) => { 148 | console.log('Następujące wartości zostały zmienione:', data) 149 | }) 150 | 151 | const unsubscribe = detail.subscribe((data) => { 152 | console.log('Następujące wartości zostały zmienione:', data) 153 | }) 154 | // Podobnie jak wcześniej możesz przerwać subkskrybowanie 155 | // poprzez wywołanie funkcji `unsubscribe()` w dowolnym momencie 156 | unsubscribe() 157 | ``` 158 | 159 |
160 | 161 | ### 📮 Akcje wewnętrzę i store'a jako klasa 162 | 163 | > w `vue-state-store` możesz stworzyć nową klasę store'a poprzez dziedziczenie z klasy Store. 164 | 165 | `vue-state-store`, nie wymaga rozróżnienia między akcjami i mutacjami 166 | 167 | - Poprzez stworzenie funkcji wewnątrz klasy, tworzysz **Wewnętrzne (ang. Embedded) akcje**. 168 | - Każda funkcja która zmienia stan niebędąca wewnętrzną (ang. Embedded) akcją, jest nazywana **Akcją Zewnętrzną (ang. outside)**. 169 | 170 | ```typescript 171 | import { Store } from 'vue-state-store' 172 | 173 | // Określanie interface'u dla stanu 174 | export interface IVote { 175 | upVoteCount: number 176 | downVoteCount: number 177 | } 178 | 179 | // Następnie tworzymy klasę stanu która dziedziczy z klasy `Store` 180 | export class Vote extends Store { 181 | // Teraz możemy zdefiniować wewnętrzne akcje 182 | // jako metody używające metod .update i .set 183 | async upVote() { 184 | await this.update((data) => { 185 | data.upVoteCount += 1 186 | return data 187 | }) 188 | } 189 | 190 | async downVote() { 191 | await this.update((data) => { 192 | data.upVoteCount -= 1 193 | return data 194 | }) 195 | } 196 | 197 | syncWithNetwork() { 198 | // Inne metody również będą 199 | // dostępne z poziomu instancji. 200 | } 201 | } 202 | 203 | // tworzenie store'a z wartością początkową 204 | export const vote = new Vote({ 205 | upVoteCount: 0, 206 | downVoteCount: 0, 207 | }) 208 | 209 | // Przykłady wykorzystywania akcji/mutacji: 210 | vote.upVote() 211 | vote.downVote() 212 | vote.syncWithNetwork() 213 | ``` 214 | 215 |
216 | 217 | ### 📮 Używanie wraz z Vue component template 218 | 219 | > `vue-state-store` może być w łatwy sposób wykorzystywane wewnątrz tagu template we vue, nadal zachowując wszystkie informacje o typach. Informacje o typach TypeScript są też obsługiwane kiedy używasz _akcji wewnętrznych_ wewnątrz tagu ` 256 | ``` 257 | 258 |
259 | 260 | ## 🚀 Używanie zaawansowane 261 | 262 | > Objaśnienie nieco bardziej zaawansowanych sposobów używania. (Krzywa uczenia pozostaje taka sama :) 263 | 264 |
265 | 266 | ### ⏳ Asynchroniczność 267 | 268 | > Metody `.update()` i `.set()` zwracają obietnice 269 | 270 |
271 | 272 | - `await` umożliwia zatrzymanie bieżącej funkcji do zakończeniu zadania aktualizacji wartości store'a. 273 | 274 | ```typescript 275 | // Promise Update 276 | await version.update((data) => { 277 | data += 1 278 | return data 279 | }) 280 | await version.set({}) 281 | ``` 282 | 283 | - Callback przekazywany do metody `.update()` może zwracać konkretną wartość, ale również obietnicę 284 | 285 | ```typescript 286 | // Promise Update 287 | await detail.update(async (data) => { 288 | const response = await fetch('https://~~~') 289 | data = response.data 290 | return data 291 | }) 292 | ``` 293 | 294 |
295 | 296 | ### 💡 Vscode Intellisense 297 | 298 | > To use both **vscode** and **typescript** at the same time and need some Intellisense support, you can obtain the module below. 299 | > Żeby **typescript'u** w raz z **vscode'em** trzeba odpowiednio przygotować VsCode. Najpierw (jeśli jeszcze tego nie zrobiłeś ;) zainstaluj poniższe rozszerzenie 300 | 301 | [vetur]: https://marketplace.visualstudio.com/items?itemName=octref.vetur 302 | 303 | In order to receive support for intellisense in the template after installing vetur, the following option should be added to the vscode setting. 304 | 305 | Żeby włączyć wsparcie Intellisense dla typów TypeScript dodaj następującą linię do opcji VsCode (Ctrl+,) 306 | 307 | ```json 308 | "vetur.experimental.templateInterpolationService": true 309 | ``` 310 | 311 |
312 | 313 | ### 📮 (Zaawansowane) Wzorzec Funkcja Używająca Stan (and. State Use Function) 314 | 315 | > `vue-state-store` przykłady użycia wzorca funkcji używającej stan. Taka funkcja zaczyna się od "use~" jest to podobne do hooków React'a i umożliwia pełne wykorzystanie możliwości Composition API we Vue3. 316 | 317 | Funkcja Używająca Stan oznacza funkcję poprzedzoną słowem `use`, zwracającą stan 318 | 319 | > Ten wzorzec pozwala ci m.in. na używanie metod cyklu życia komponentu (ang. life cycle) w metodach store'a. 320 | 321 | Jeśli używasz metod dostępu(`.set()` i `.update()`) żeby modyfikować stan, tworzenie złożonej logiki może być bardzo kłopotliwe, w przeciwieństwie do modyfikowania istniejących zmiennych. 322 | 323 | `vue-state-store` pozwala na łatwą modyfikację stanu bezpośrednio przez zbindowany obiekt, nie ma potrzeby używać dodatkowych funkcji 324 | 325 | `vue-state-store` allows for convenient change of state by directly accessing bindings within the store without the use of such an accessor. Ten wzorzec projektowy to tylko prosty przykład konfigurowania funkcji użycia statusu podczas korzystania z interfejsu Composition API (wzorca można używać bez Composition API), **Jeśli musisz modyfikować stan w bardziej złożony sposób**, lub **If you need to create multiple `compute` objects**. 326 | 327 | Even if you modify a bound value, the changes are automatically distributed to the callbacks you are subscribing to each time the value changes. 328 | 329 | ```typescript 330 | import { Store } from 'vue-state-store' 331 | import * as VueAPI from '@vue/composition-api' 332 | 333 | // Defines the specification of the state value. 334 | export interface ITodo { 335 | todoList: string[] 336 | } 337 | 338 | // Defines the state class. 339 | export class Todo extends Store { 340 | // Bind the state value within the store class. 341 | value = this.bind() 342 | 343 | // Defines the computed value. 344 | pending = VueAPI.computed(() => { 345 | return this.value.todoList.filter((item) => { 346 | return item.indexOf('(DONE)') == -1 347 | }) 348 | }) 349 | 350 | // Add a Todo. 351 | addItem(newTodo) { 352 | this.value.todoList.unshift(newTodo) 353 | } 354 | 355 | // Delete A todo 356 | deleteItem(item: string) { 357 | this.value.todoList.splice(this.value.todoList.indexOf(item), 1) 358 | } 359 | 360 | // Load schedules stored on local storage. 361 | getTodos() { 362 | try { 363 | if (window.localStorage.getItem('todo_list')) { 364 | this.value.todoList = JSON.parse( 365 | window.localStorage.getItem('todo_list') 366 | ) 367 | } 368 | } catch (e) {} 369 | } 370 | } 371 | ``` 372 | 373 | > If you refer to the value of a bound store in a callback wrapped in `computed` , the callback will occur again whenever the value of that store changes. This reduces the fatigue of re-calculating each function as it is called, thus improving performance when using a state. 374 | 375 | ```typescript 376 | // Defines the state use function. 377 | export const useTodo = () => { 378 | // Defines the initial value of the store. 379 | const todo = new Todo({ 380 | todoList: [ 381 | 'Give the peanuts to squirrel.', 382 | 'Giving Churu to a Cat.', 383 | 'Create Vue Example.', 384 | ], 385 | }) 386 | 387 | // Load the state values that 388 | // were previously stored on local storage. 389 | todo.getTodos() 390 | 391 | // Store values on local storage 392 | // when the todolist changes. 393 | todo.subscribe((data) => { 394 | try { 395 | window.localStorage.setItem( 396 | 'todo_list', 397 | JSON.stringify(data.todoList) 398 | ) 399 | } catch (e) {} 400 | }) 401 | 402 | return todo 403 | } 404 | ``` 405 | 406 | > To define Vue's lifecycle or `computed`, you must create one isolated function, such as `useTodo`, and must be call within the component. 407 | 408 | ```typescript 409 | import * as VueAPI from '@vue/composition-api' 410 | import { useTodo } from '~/store' 411 | 412 | export default VueAPI.defineComponent({ 413 | setup(props, context) { 414 | return { 415 | // Use the Todo state in this component. 416 | todo: useTodo(), 417 | } 418 | }, 419 | }) 420 | ``` 421 | 422 | > As shown above, you can use it within the template tag immediately after using useToto(). (Of course Typescript Intellisense is still supported.) 423 | 424 |
425 | 426 |
427 | 428 | ## 🤔 Q&A 429 | 430 | > Pytania możesz zadawać w github issues 431 | 432 |
433 | 434 | ### 🧲 Q. Czy istnieje globalna zmienna `$store` która zawiera wszystkie pomniejszy store'y jak w vuex? 435 | 436 | > A. Nie, nie ma. Nie jest to rekomendowana praktyka 437 | 438 | Zaleca się importowanie i używanie tylko kilku store'ów w każdym pliku 439 | 440 | np. `import { vote } from '@/store` 441 | 442 | `vue-state-store` ma rozproszoną strukturę i do każdego store'a może się odnosić tylko bezpośrednio. 443 | 444 | `vue-state-store` jest całkowicie niezależny od `vue` dopóki nie zostanie użyty wewnątrz vue template poprzez metodę `.bind()` 445 | 446 |
447 | 448 | ### 👀 Q. Czy jeśli wartość `.bind()` zostanie zmieniona, komponent zostanie przerenderowany? 449 | 450 | > A. Taj, zmiany stanu są natychmiastowo odzwierciedlenie wartości w DOM 451 | 452 |
453 | 454 | ### 📡 Q. Czy metoda `.bind()` utrzymuje powiązanie dwu-kierunkowe? (ang. two-way binding) 455 | 456 | > A. Tak, wartość zbindowana (powiązana) ze store'em zmieni się w momencie zmiany wartości w store'rze i odwrotnie 457 | 458 |
459 | 460 |
461 | 462 | ## 📔 Licencja 463 | 464 | > Copyright (c) 2020 AhaOfficial 465 | 466 | **Licencja MIT** 467 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-state-store", 3 | "version": "3.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/eslint": { 8 | "version": "7.2.4", 9 | "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.4.tgz", 10 | "integrity": "sha512-YCY4kzHMsHoyKspQH+nwSe+70Kep7Vjt2X+dZe5Vs2vkRudqtoFoUIv1RlJmZB8Hbp7McneupoZij4PadxsK5Q==", 11 | "dev": true, 12 | "requires": { 13 | "@types/estree": "*", 14 | "@types/json-schema": "*" 15 | } 16 | }, 17 | "@types/eslint-scope": { 18 | "version": "3.7.0", 19 | "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz", 20 | "integrity": "sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==", 21 | "dev": true, 22 | "requires": { 23 | "@types/eslint": "*", 24 | "@types/estree": "*" 25 | } 26 | }, 27 | "@types/estree": { 28 | "version": "0.0.45", 29 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.45.tgz", 30 | "integrity": "sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==", 31 | "dev": true 32 | }, 33 | "@types/json-schema": { 34 | "version": "7.0.6", 35 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", 36 | "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", 37 | "dev": true 38 | }, 39 | "@types/node": { 40 | "version": "14.14.0", 41 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.0.tgz", 42 | "integrity": "sha512-BfbIHP9IapdupGhq/hc+jT5dyiBVZ2DdeC5WwJWQWDb0GijQlzUFAeIQn/2GtvZcd2HVUU7An8felIICFTC2qg==", 43 | "dev": true 44 | }, 45 | "@vue/composition-api": { 46 | "version": "1.0.0-beta.8", 47 | "resolved": "https://registry.npmjs.org/@vue/composition-api/-/composition-api-1.0.0-beta.8.tgz", 48 | "integrity": "sha512-qpYFyUXEjm/7LAXAEk2gCAp1a8Hzdt8KpMxVRo+XzO2tgM4tWJGxO+nh93lDdNah6k8XTS9ETepZX76dvEO1Uw==", 49 | "dev": true, 50 | "requires": { 51 | "tslib": "^2.0.0" 52 | } 53 | }, 54 | "@webassemblyjs/ast": { 55 | "version": "1.9.0", 56 | "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", 57 | "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", 58 | "dev": true, 59 | "requires": { 60 | "@webassemblyjs/helper-module-context": "1.9.0", 61 | "@webassemblyjs/helper-wasm-bytecode": "1.9.0", 62 | "@webassemblyjs/wast-parser": "1.9.0" 63 | } 64 | }, 65 | "@webassemblyjs/floating-point-hex-parser": { 66 | "version": "1.9.0", 67 | "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", 68 | "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", 69 | "dev": true 70 | }, 71 | "@webassemblyjs/helper-api-error": { 72 | "version": "1.9.0", 73 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", 74 | "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", 75 | "dev": true 76 | }, 77 | "@webassemblyjs/helper-buffer": { 78 | "version": "1.9.0", 79 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", 80 | "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", 81 | "dev": true 82 | }, 83 | "@webassemblyjs/helper-code-frame": { 84 | "version": "1.9.0", 85 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", 86 | "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", 87 | "dev": true, 88 | "requires": { 89 | "@webassemblyjs/wast-printer": "1.9.0" 90 | } 91 | }, 92 | "@webassemblyjs/helper-fsm": { 93 | "version": "1.9.0", 94 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", 95 | "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", 96 | "dev": true 97 | }, 98 | "@webassemblyjs/helper-module-context": { 99 | "version": "1.9.0", 100 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", 101 | "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", 102 | "dev": true, 103 | "requires": { 104 | "@webassemblyjs/ast": "1.9.0" 105 | } 106 | }, 107 | "@webassemblyjs/helper-wasm-bytecode": { 108 | "version": "1.9.0", 109 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", 110 | "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", 111 | "dev": true 112 | }, 113 | "@webassemblyjs/helper-wasm-section": { 114 | "version": "1.9.0", 115 | "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", 116 | "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", 117 | "dev": true, 118 | "requires": { 119 | "@webassemblyjs/ast": "1.9.0", 120 | "@webassemblyjs/helper-buffer": "1.9.0", 121 | "@webassemblyjs/helper-wasm-bytecode": "1.9.0", 122 | "@webassemblyjs/wasm-gen": "1.9.0" 123 | } 124 | }, 125 | "@webassemblyjs/ieee754": { 126 | "version": "1.9.0", 127 | "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", 128 | "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", 129 | "dev": true, 130 | "requires": { 131 | "@xtuc/ieee754": "^1.2.0" 132 | } 133 | }, 134 | "@webassemblyjs/leb128": { 135 | "version": "1.9.0", 136 | "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", 137 | "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", 138 | "dev": true, 139 | "requires": { 140 | "@xtuc/long": "4.2.2" 141 | } 142 | }, 143 | "@webassemblyjs/utf8": { 144 | "version": "1.9.0", 145 | "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", 146 | "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", 147 | "dev": true 148 | }, 149 | "@webassemblyjs/wasm-edit": { 150 | "version": "1.9.0", 151 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", 152 | "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", 153 | "dev": true, 154 | "requires": { 155 | "@webassemblyjs/ast": "1.9.0", 156 | "@webassemblyjs/helper-buffer": "1.9.0", 157 | "@webassemblyjs/helper-wasm-bytecode": "1.9.0", 158 | "@webassemblyjs/helper-wasm-section": "1.9.0", 159 | "@webassemblyjs/wasm-gen": "1.9.0", 160 | "@webassemblyjs/wasm-opt": "1.9.0", 161 | "@webassemblyjs/wasm-parser": "1.9.0", 162 | "@webassemblyjs/wast-printer": "1.9.0" 163 | } 164 | }, 165 | "@webassemblyjs/wasm-gen": { 166 | "version": "1.9.0", 167 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", 168 | "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", 169 | "dev": true, 170 | "requires": { 171 | "@webassemblyjs/ast": "1.9.0", 172 | "@webassemblyjs/helper-wasm-bytecode": "1.9.0", 173 | "@webassemblyjs/ieee754": "1.9.0", 174 | "@webassemblyjs/leb128": "1.9.0", 175 | "@webassemblyjs/utf8": "1.9.0" 176 | } 177 | }, 178 | "@webassemblyjs/wasm-opt": { 179 | "version": "1.9.0", 180 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", 181 | "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", 182 | "dev": true, 183 | "requires": { 184 | "@webassemblyjs/ast": "1.9.0", 185 | "@webassemblyjs/helper-buffer": "1.9.0", 186 | "@webassemblyjs/wasm-gen": "1.9.0", 187 | "@webassemblyjs/wasm-parser": "1.9.0" 188 | } 189 | }, 190 | "@webassemblyjs/wasm-parser": { 191 | "version": "1.9.0", 192 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", 193 | "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", 194 | "dev": true, 195 | "requires": { 196 | "@webassemblyjs/ast": "1.9.0", 197 | "@webassemblyjs/helper-api-error": "1.9.0", 198 | "@webassemblyjs/helper-wasm-bytecode": "1.9.0", 199 | "@webassemblyjs/ieee754": "1.9.0", 200 | "@webassemblyjs/leb128": "1.9.0", 201 | "@webassemblyjs/utf8": "1.9.0" 202 | } 203 | }, 204 | "@webassemblyjs/wast-parser": { 205 | "version": "1.9.0", 206 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", 207 | "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", 208 | "dev": true, 209 | "requires": { 210 | "@webassemblyjs/ast": "1.9.0", 211 | "@webassemblyjs/floating-point-hex-parser": "1.9.0", 212 | "@webassemblyjs/helper-api-error": "1.9.0", 213 | "@webassemblyjs/helper-code-frame": "1.9.0", 214 | "@webassemblyjs/helper-fsm": "1.9.0", 215 | "@xtuc/long": "4.2.2" 216 | } 217 | }, 218 | "@webassemblyjs/wast-printer": { 219 | "version": "1.9.0", 220 | "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", 221 | "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", 222 | "dev": true, 223 | "requires": { 224 | "@webassemblyjs/ast": "1.9.0", 225 | "@webassemblyjs/wast-parser": "1.9.0", 226 | "@xtuc/long": "4.2.2" 227 | } 228 | }, 229 | "@webpack-cli/info": { 230 | "version": "1.0.2", 231 | "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.0.2.tgz", 232 | "integrity": "sha512-FEfLQwmN4pXZSYSrtp+KC84rFanoCIxXFpS2wUvviDCE2fnajwxw2GXzbj83IlH4Dl8Wq8kJjavVwvxv3YJmnw==", 233 | "dev": true, 234 | "requires": { 235 | "envinfo": "^7.7.3" 236 | } 237 | }, 238 | "@webpack-cli/serve": { 239 | "version": "1.0.1", 240 | "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.0.1.tgz", 241 | "integrity": "sha512-WGMaTMTK6NOe29Hw1WBEok9vGLfKg5C6jWzNOS/6HH1YadR+RL+TRWRcSyc81Dzulljhk/Ree9mrDM4Np9GGOQ==", 242 | "dev": true 243 | }, 244 | "@xtuc/ieee754": { 245 | "version": "1.2.0", 246 | "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", 247 | "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", 248 | "dev": true 249 | }, 250 | "@xtuc/long": { 251 | "version": "4.2.2", 252 | "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", 253 | "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", 254 | "dev": true 255 | }, 256 | "acorn": { 257 | "version": "8.0.4", 258 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.4.tgz", 259 | "integrity": "sha512-XNP0PqF1XD19ZlLKvB7cMmnZswW4C/03pRHgirB30uSJTaS3A3V1/P4sS3HPvFmjoriPCJQs+JDSbm4bL1TxGQ==", 260 | "dev": true 261 | }, 262 | "ajv": { 263 | "version": "6.12.6", 264 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 265 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 266 | "dev": true, 267 | "requires": { 268 | "fast-deep-equal": "^3.1.1", 269 | "fast-json-stable-stringify": "^2.0.0", 270 | "json-schema-traverse": "^0.4.1", 271 | "uri-js": "^4.2.2" 272 | } 273 | }, 274 | "ajv-keywords": { 275 | "version": "3.5.2", 276 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", 277 | "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", 278 | "dev": true 279 | }, 280 | "ansi-colors": { 281 | "version": "4.1.1", 282 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 283 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 284 | "dev": true 285 | }, 286 | "ansi-escapes": { 287 | "version": "4.3.1", 288 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", 289 | "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", 290 | "dev": true, 291 | "requires": { 292 | "type-fest": "^0.11.0" 293 | } 294 | }, 295 | "ansi-styles": { 296 | "version": "3.2.1", 297 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 298 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 299 | "dev": true, 300 | "requires": { 301 | "color-convert": "^1.9.0" 302 | } 303 | }, 304 | "array-back": { 305 | "version": "4.0.1", 306 | "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.1.tgz", 307 | "integrity": "sha512-Z/JnaVEXv+A9xabHzN43FiiiWEE7gPCRXMrVmRm00tWbjZRul1iHm7ECzlyNq1p4a4ATXz+G9FJ3GqGOkOV3fg==", 308 | "dev": true 309 | }, 310 | "browserslist": { 311 | "version": "4.14.5", 312 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.5.tgz", 313 | "integrity": "sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==", 314 | "dev": true, 315 | "requires": { 316 | "caniuse-lite": "^1.0.30001135", 317 | "electron-to-chromium": "^1.3.571", 318 | "escalade": "^3.1.0", 319 | "node-releases": "^1.1.61" 320 | } 321 | }, 322 | "buffer-from": { 323 | "version": "1.1.1", 324 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 325 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 326 | "dev": true 327 | }, 328 | "caniuse-lite": { 329 | "version": "1.0.30001148", 330 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz", 331 | "integrity": "sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw==", 332 | "dev": true 333 | }, 334 | "chalk": { 335 | "version": "2.4.2", 336 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 337 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 338 | "dev": true, 339 | "requires": { 340 | "ansi-styles": "^3.2.1", 341 | "escape-string-regexp": "^1.0.5", 342 | "supports-color": "^5.3.0" 343 | }, 344 | "dependencies": { 345 | "has-flag": { 346 | "version": "3.0.0", 347 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 348 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 349 | "dev": true 350 | }, 351 | "supports-color": { 352 | "version": "5.5.0", 353 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 354 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 355 | "dev": true, 356 | "requires": { 357 | "has-flag": "^3.0.0" 358 | } 359 | } 360 | } 361 | }, 362 | "chrome-trace-event": { 363 | "version": "1.0.2", 364 | "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", 365 | "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", 366 | "dev": true, 367 | "requires": { 368 | "tslib": "^1.9.0" 369 | }, 370 | "dependencies": { 371 | "tslib": { 372 | "version": "1.14.1", 373 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 374 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 375 | "dev": true 376 | } 377 | } 378 | }, 379 | "color-convert": { 380 | "version": "1.9.3", 381 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 382 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 383 | "dev": true, 384 | "requires": { 385 | "color-name": "1.1.3" 386 | } 387 | }, 388 | "color-name": { 389 | "version": "1.1.3", 390 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 391 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 392 | "dev": true 393 | }, 394 | "colorette": { 395 | "version": "1.2.1", 396 | "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", 397 | "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", 398 | "dev": true 399 | }, 400 | "command-line-usage": { 401 | "version": "6.1.0", 402 | "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.0.tgz", 403 | "integrity": "sha512-Ew1clU4pkUeo6AFVDFxCbnN7GIZfXl48HIOQeFQnkO3oOqvpI7wdqtLRwv9iOCZ/7A+z4csVZeiDdEcj8g6Wiw==", 404 | "dev": true, 405 | "requires": { 406 | "array-back": "^4.0.0", 407 | "chalk": "^2.4.2", 408 | "table-layout": "^1.0.0", 409 | "typical": "^5.2.0" 410 | } 411 | }, 412 | "commander": { 413 | "version": "2.20.3", 414 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 415 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 416 | "dev": true 417 | }, 418 | "cross-spawn": { 419 | "version": "7.0.3", 420 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 421 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 422 | "dev": true, 423 | "requires": { 424 | "path-key": "^3.1.0", 425 | "shebang-command": "^2.0.0", 426 | "which": "^2.0.1" 427 | } 428 | }, 429 | "deep-extend": { 430 | "version": "0.6.0", 431 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 432 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", 433 | "dev": true 434 | }, 435 | "electron-to-chromium": { 436 | "version": "1.3.582", 437 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.582.tgz", 438 | "integrity": "sha512-0nCJ7cSqnkMC+kUuPs0YgklFHraWGl/xHqtZWWtOeVtyi+YqkoAOMGuZQad43DscXCQI/yizcTa3u6B5r+BLww==", 439 | "dev": true 440 | }, 441 | "end-of-stream": { 442 | "version": "1.4.4", 443 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 444 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 445 | "dev": true, 446 | "requires": { 447 | "once": "^1.4.0" 448 | } 449 | }, 450 | "enhanced-resolve": { 451 | "version": "5.3.0", 452 | "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.3.0.tgz", 453 | "integrity": "sha512-EENz3E701+77g0wfbOITeI8WLPNso2kQNMBIBEi/TH/BEa9YXtS01X7sIEk5XXsfFq1jNkhIpu08hBPH1TRLIQ==", 454 | "dev": true, 455 | "requires": { 456 | "graceful-fs": "^4.2.4", 457 | "tapable": "^2.0.0" 458 | } 459 | }, 460 | "enquirer": { 461 | "version": "2.3.6", 462 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", 463 | "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", 464 | "dev": true, 465 | "requires": { 466 | "ansi-colors": "^4.1.1" 467 | } 468 | }, 469 | "envinfo": { 470 | "version": "7.7.3", 471 | "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.7.3.tgz", 472 | "integrity": "sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA==", 473 | "dev": true 474 | }, 475 | "escalade": { 476 | "version": "3.1.1", 477 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 478 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 479 | "dev": true 480 | }, 481 | "escape-string-regexp": { 482 | "version": "1.0.5", 483 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 484 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 485 | "dev": true 486 | }, 487 | "eslint-scope": { 488 | "version": "5.1.1", 489 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", 490 | "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", 491 | "dev": true, 492 | "requires": { 493 | "esrecurse": "^4.3.0", 494 | "estraverse": "^4.1.1" 495 | } 496 | }, 497 | "esrecurse": { 498 | "version": "4.3.0", 499 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 500 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 501 | "dev": true, 502 | "requires": { 503 | "estraverse": "^5.2.0" 504 | }, 505 | "dependencies": { 506 | "estraverse": { 507 | "version": "5.2.0", 508 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 509 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", 510 | "dev": true 511 | } 512 | } 513 | }, 514 | "estraverse": { 515 | "version": "4.3.0", 516 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 517 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 518 | "dev": true 519 | }, 520 | "events": { 521 | "version": "3.2.0", 522 | "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", 523 | "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", 524 | "dev": true 525 | }, 526 | "execa": { 527 | "version": "4.0.3", 528 | "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", 529 | "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", 530 | "dev": true, 531 | "requires": { 532 | "cross-spawn": "^7.0.0", 533 | "get-stream": "^5.0.0", 534 | "human-signals": "^1.1.1", 535 | "is-stream": "^2.0.0", 536 | "merge-stream": "^2.0.0", 537 | "npm-run-path": "^4.0.0", 538 | "onetime": "^5.1.0", 539 | "signal-exit": "^3.0.2", 540 | "strip-final-newline": "^2.0.0" 541 | } 542 | }, 543 | "fast-deep-equal": { 544 | "version": "3.1.3", 545 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 546 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 547 | "dev": true 548 | }, 549 | "fast-json-stable-stringify": { 550 | "version": "2.1.0", 551 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 552 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 553 | "dev": true 554 | }, 555 | "find-up": { 556 | "version": "4.1.0", 557 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 558 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 559 | "dev": true, 560 | "requires": { 561 | "locate-path": "^5.0.0", 562 | "path-exists": "^4.0.0" 563 | } 564 | }, 565 | "function-bind": { 566 | "version": "1.1.1", 567 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 568 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 569 | "dev": true 570 | }, 571 | "get-stream": { 572 | "version": "5.2.0", 573 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", 574 | "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", 575 | "dev": true, 576 | "requires": { 577 | "pump": "^3.0.0" 578 | } 579 | }, 580 | "glob-to-regexp": { 581 | "version": "0.4.1", 582 | "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", 583 | "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", 584 | "dev": true 585 | }, 586 | "graceful-fs": { 587 | "version": "4.2.4", 588 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 589 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", 590 | "dev": true 591 | }, 592 | "has": { 593 | "version": "1.0.3", 594 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 595 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 596 | "dev": true, 597 | "requires": { 598 | "function-bind": "^1.1.1" 599 | } 600 | }, 601 | "has-flag": { 602 | "version": "4.0.0", 603 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 604 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 605 | "dev": true 606 | }, 607 | "human-signals": { 608 | "version": "1.1.1", 609 | "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", 610 | "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", 611 | "dev": true 612 | }, 613 | "import-local": { 614 | "version": "3.0.2", 615 | "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", 616 | "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", 617 | "dev": true, 618 | "requires": { 619 | "pkg-dir": "^4.2.0", 620 | "resolve-cwd": "^3.0.0" 621 | } 622 | }, 623 | "interpret": { 624 | "version": "2.2.0", 625 | "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", 626 | "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", 627 | "dev": true 628 | }, 629 | "is-core-module": { 630 | "version": "2.0.0", 631 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.0.0.tgz", 632 | "integrity": "sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==", 633 | "dev": true, 634 | "requires": { 635 | "has": "^1.0.3" 636 | } 637 | }, 638 | "is-stream": { 639 | "version": "2.0.0", 640 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", 641 | "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", 642 | "dev": true 643 | }, 644 | "isexe": { 645 | "version": "2.0.0", 646 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 647 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 648 | "dev": true 649 | }, 650 | "jest-worker": { 651 | "version": "26.5.0", 652 | "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.5.0.tgz", 653 | "integrity": "sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug==", 654 | "dev": true, 655 | "requires": { 656 | "@types/node": "*", 657 | "merge-stream": "^2.0.0", 658 | "supports-color": "^7.0.0" 659 | } 660 | }, 661 | "json-parse-better-errors": { 662 | "version": "1.0.2", 663 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", 664 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", 665 | "dev": true 666 | }, 667 | "json-schema-traverse": { 668 | "version": "0.4.1", 669 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 670 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 671 | "dev": true 672 | }, 673 | "loader-runner": { 674 | "version": "4.1.0", 675 | "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.1.0.tgz", 676 | "integrity": "sha512-oR4lB4WvwFoC70ocraKhn5nkKSs23t57h9udUgw8o0iH8hMXeEoRuUgfcvgUwAJ1ZpRqBvcou4N2SMvM1DwMrA==", 677 | "dev": true 678 | }, 679 | "locate-path": { 680 | "version": "5.0.0", 681 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 682 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 683 | "dev": true, 684 | "requires": { 685 | "p-locate": "^4.1.0" 686 | } 687 | }, 688 | "lodash": { 689 | "version": "4.17.20", 690 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 691 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", 692 | "dev": true 693 | }, 694 | "merge-stream": { 695 | "version": "2.0.0", 696 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 697 | "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", 698 | "dev": true 699 | }, 700 | "mime-db": { 701 | "version": "1.44.0", 702 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", 703 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", 704 | "dev": true 705 | }, 706 | "mime-types": { 707 | "version": "2.1.27", 708 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", 709 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", 710 | "dev": true, 711 | "requires": { 712 | "mime-db": "1.44.0" 713 | } 714 | }, 715 | "mimic-fn": { 716 | "version": "2.1.0", 717 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", 718 | "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", 719 | "dev": true 720 | }, 721 | "neo-async": { 722 | "version": "2.6.2", 723 | "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", 724 | "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", 725 | "dev": true 726 | }, 727 | "node-releases": { 728 | "version": "1.1.64", 729 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.64.tgz", 730 | "integrity": "sha512-Iec8O9166/x2HRMJyLLLWkd0sFFLrFNy+Xf+JQfSQsdBJzPcHpNl3JQ9gD4j+aJxmCa25jNsIbM4bmACtSbkSg==", 731 | "dev": true 732 | }, 733 | "npm-run-path": { 734 | "version": "4.0.1", 735 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", 736 | "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", 737 | "dev": true, 738 | "requires": { 739 | "path-key": "^3.0.0" 740 | } 741 | }, 742 | "once": { 743 | "version": "1.4.0", 744 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 745 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 746 | "dev": true, 747 | "requires": { 748 | "wrappy": "1" 749 | } 750 | }, 751 | "onetime": { 752 | "version": "5.1.2", 753 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", 754 | "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", 755 | "dev": true, 756 | "requires": { 757 | "mimic-fn": "^2.1.0" 758 | } 759 | }, 760 | "p-limit": { 761 | "version": "3.0.2", 762 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", 763 | "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", 764 | "dev": true, 765 | "requires": { 766 | "p-try": "^2.0.0" 767 | } 768 | }, 769 | "p-locate": { 770 | "version": "4.1.0", 771 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 772 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 773 | "dev": true, 774 | "requires": { 775 | "p-limit": "^2.2.0" 776 | }, 777 | "dependencies": { 778 | "p-limit": { 779 | "version": "2.3.0", 780 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 781 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 782 | "dev": true, 783 | "requires": { 784 | "p-try": "^2.0.0" 785 | } 786 | } 787 | } 788 | }, 789 | "p-try": { 790 | "version": "2.2.0", 791 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 792 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 793 | "dev": true 794 | }, 795 | "path-exists": { 796 | "version": "4.0.0", 797 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 798 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 799 | "dev": true 800 | }, 801 | "path-key": { 802 | "version": "3.1.1", 803 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 804 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 805 | "dev": true 806 | }, 807 | "path-parse": { 808 | "version": "1.0.6", 809 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 810 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 811 | "dev": true 812 | }, 813 | "pkg-dir": { 814 | "version": "4.2.0", 815 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", 816 | "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", 817 | "dev": true, 818 | "requires": { 819 | "find-up": "^4.0.0" 820 | } 821 | }, 822 | "prettier": { 823 | "version": "2.1.2", 824 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz", 825 | "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", 826 | "dev": true 827 | }, 828 | "pump": { 829 | "version": "3.0.0", 830 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 831 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 832 | "dev": true, 833 | "requires": { 834 | "end-of-stream": "^1.1.0", 835 | "once": "^1.3.1" 836 | } 837 | }, 838 | "punycode": { 839 | "version": "2.1.1", 840 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 841 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 842 | "dev": true 843 | }, 844 | "randombytes": { 845 | "version": "2.1.0", 846 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 847 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 848 | "dev": true, 849 | "requires": { 850 | "safe-buffer": "^5.1.0" 851 | } 852 | }, 853 | "rechoir": { 854 | "version": "0.7.0", 855 | "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", 856 | "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", 857 | "dev": true, 858 | "requires": { 859 | "resolve": "^1.9.0" 860 | } 861 | }, 862 | "reduce-flatten": { 863 | "version": "2.0.0", 864 | "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", 865 | "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", 866 | "dev": true 867 | }, 868 | "resolve": { 869 | "version": "1.18.1", 870 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", 871 | "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", 872 | "dev": true, 873 | "requires": { 874 | "is-core-module": "^2.0.0", 875 | "path-parse": "^1.0.6" 876 | } 877 | }, 878 | "resolve-cwd": { 879 | "version": "3.0.0", 880 | "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", 881 | "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", 882 | "dev": true, 883 | "requires": { 884 | "resolve-from": "^5.0.0" 885 | } 886 | }, 887 | "resolve-from": { 888 | "version": "5.0.0", 889 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", 890 | "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", 891 | "dev": true 892 | }, 893 | "safe-buffer": { 894 | "version": "5.2.1", 895 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 896 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 897 | "dev": true 898 | }, 899 | "schema-utils": { 900 | "version": "3.0.0", 901 | "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", 902 | "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", 903 | "dev": true, 904 | "requires": { 905 | "@types/json-schema": "^7.0.6", 906 | "ajv": "^6.12.5", 907 | "ajv-keywords": "^3.5.2" 908 | } 909 | }, 910 | "serialize-javascript": { 911 | "version": "5.0.1", 912 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", 913 | "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", 914 | "dev": true, 915 | "requires": { 916 | "randombytes": "^2.1.0" 917 | } 918 | }, 919 | "shebang-command": { 920 | "version": "2.0.0", 921 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 922 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 923 | "dev": true, 924 | "requires": { 925 | "shebang-regex": "^3.0.0" 926 | } 927 | }, 928 | "shebang-regex": { 929 | "version": "3.0.0", 930 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 931 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 932 | "dev": true 933 | }, 934 | "signal-exit": { 935 | "version": "3.0.3", 936 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", 937 | "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", 938 | "dev": true 939 | }, 940 | "source-list-map": { 941 | "version": "2.0.1", 942 | "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", 943 | "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", 944 | "dev": true 945 | }, 946 | "source-map": { 947 | "version": "0.6.1", 948 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 949 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 950 | "dev": true 951 | }, 952 | "source-map-support": { 953 | "version": "0.5.19", 954 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", 955 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", 956 | "dev": true, 957 | "requires": { 958 | "buffer-from": "^1.0.0", 959 | "source-map": "^0.6.0" 960 | } 961 | }, 962 | "strip-final-newline": { 963 | "version": "2.0.0", 964 | "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", 965 | "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", 966 | "dev": true 967 | }, 968 | "supports-color": { 969 | "version": "7.2.0", 970 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 971 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 972 | "dev": true, 973 | "requires": { 974 | "has-flag": "^4.0.0" 975 | } 976 | }, 977 | "table-layout": { 978 | "version": "1.0.1", 979 | "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.1.tgz", 980 | "integrity": "sha512-dEquqYNJiGwY7iPfZ3wbXDI944iqanTSchrACLL2nOB+1r+h1Nzu2eH+DuPPvWvm5Ry7iAPeFlgEtP5bIp5U7Q==", 981 | "dev": true, 982 | "requires": { 983 | "array-back": "^4.0.1", 984 | "deep-extend": "~0.6.0", 985 | "typical": "^5.2.0", 986 | "wordwrapjs": "^4.0.0" 987 | } 988 | }, 989 | "tapable": { 990 | "version": "2.0.0", 991 | "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.0.0.tgz", 992 | "integrity": "sha512-bjzn0C0RWoffnNdTzNi7rNDhs1Zlwk2tRXgk8EiHKAOX1Mag3d6T0Y5zNa7l9CJ+EoUne/0UHdwS8tMbkh9zDg==", 993 | "dev": true 994 | }, 995 | "terser": { 996 | "version": "5.3.7", 997 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.7.tgz", 998 | "integrity": "sha512-lJbKdfxWvjpV330U4PBZStCT9h3N9A4zZVA5Y4k9sCWXknrpdyxi1oMsRKLmQ/YDMDxSBKIh88v0SkdhdqX06w==", 999 | "dev": true, 1000 | "requires": { 1001 | "commander": "^2.20.0", 1002 | "source-map": "~0.7.2", 1003 | "source-map-support": "~0.5.19" 1004 | }, 1005 | "dependencies": { 1006 | "source-map": { 1007 | "version": "0.7.3", 1008 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", 1009 | "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", 1010 | "dev": true 1011 | } 1012 | } 1013 | }, 1014 | "terser-webpack-plugin": { 1015 | "version": "5.0.0", 1016 | "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.0.0.tgz", 1017 | "integrity": "sha512-rf7l5a9xamIVX3enQeTl0MY2MNeZClo5yPX/tVPy22oY0nzu0b45h7JqyFi/bygqKWtzXMnml0u12mArhQPsBQ==", 1018 | "dev": true, 1019 | "requires": { 1020 | "jest-worker": "^26.5.0", 1021 | "p-limit": "^3.0.2", 1022 | "schema-utils": "^3.0.0", 1023 | "serialize-javascript": "^5.0.1", 1024 | "source-map": "^0.6.1", 1025 | "terser": "^5.3.5" 1026 | } 1027 | }, 1028 | "tslib": { 1029 | "version": "2.0.0", 1030 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", 1031 | "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==", 1032 | "dev": true 1033 | }, 1034 | "type-fest": { 1035 | "version": "0.11.0", 1036 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", 1037 | "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", 1038 | "dev": true 1039 | }, 1040 | "typical": { 1041 | "version": "5.2.0", 1042 | "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", 1043 | "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", 1044 | "dev": true 1045 | }, 1046 | "uri-js": { 1047 | "version": "4.4.0", 1048 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", 1049 | "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", 1050 | "dev": true, 1051 | "requires": { 1052 | "punycode": "^2.1.0" 1053 | } 1054 | }, 1055 | "v8-compile-cache": { 1056 | "version": "2.1.1", 1057 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", 1058 | "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", 1059 | "dev": true 1060 | }, 1061 | "watchpack": { 1062 | "version": "2.0.0", 1063 | "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.0.0.tgz", 1064 | "integrity": "sha512-xSdCxxYZWNk3VK13bZRYhsQpfa8Vg63zXG+3pyU8ouqSLRCv4IGXIp9Kr226q6GBkGRlZrST2wwKtjfKz2m7Cg==", 1065 | "dev": true, 1066 | "requires": { 1067 | "glob-to-regexp": "^0.4.1", 1068 | "graceful-fs": "^4.1.2" 1069 | } 1070 | }, 1071 | "webpack": { 1072 | "version": "5.1.3", 1073 | "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.1.3.tgz", 1074 | "integrity": "sha512-bNBF5EOpt5a6NeCBFu0+8KJtG61cVmOb2b/a5tPNRLz3OWgDpHMbmnDkaSm3nf/UQ6ufw4PWYGVsVOAi8UfL2A==", 1075 | "dev": true, 1076 | "requires": { 1077 | "@types/eslint-scope": "^3.7.0", 1078 | "@types/estree": "^0.0.45", 1079 | "@webassemblyjs/ast": "1.9.0", 1080 | "@webassemblyjs/helper-module-context": "1.9.0", 1081 | "@webassemblyjs/wasm-edit": "1.9.0", 1082 | "@webassemblyjs/wasm-parser": "1.9.0", 1083 | "acorn": "^8.0.3", 1084 | "browserslist": "^4.14.3", 1085 | "chrome-trace-event": "^1.0.2", 1086 | "enhanced-resolve": "^5.2.0", 1087 | "eslint-scope": "^5.1.0", 1088 | "events": "^3.2.0", 1089 | "glob-to-regexp": "^0.4.1", 1090 | "graceful-fs": "^4.2.4", 1091 | "json-parse-better-errors": "^1.0.2", 1092 | "loader-runner": "^4.1.0", 1093 | "mime-types": "^2.1.27", 1094 | "neo-async": "^2.6.2", 1095 | "pkg-dir": "^4.2.0", 1096 | "schema-utils": "^3.0.0", 1097 | "tapable": "^2.0.0", 1098 | "terser-webpack-plugin": "^5.0.0", 1099 | "watchpack": "^2.0.0", 1100 | "webpack-sources": "^2.0.1" 1101 | } 1102 | }, 1103 | "webpack-cli": { 1104 | "version": "4.1.0", 1105 | "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.1.0.tgz", 1106 | "integrity": "sha512-NdhxXMZmoik62Y05t0h1y65LjBM7BwFPq311ihXuMM3RY6dlc4KkCTyHLzTuBEc+bqq6d3xh+CWmU0xRexNJBA==", 1107 | "dev": true, 1108 | "requires": { 1109 | "@webpack-cli/info": "^1.0.2", 1110 | "@webpack-cli/serve": "^1.0.1", 1111 | "ansi-escapes": "^4.3.1", 1112 | "colorette": "^1.2.1", 1113 | "command-line-usage": "^6.1.0", 1114 | "commander": "^6.0.0", 1115 | "enquirer": "^2.3.4", 1116 | "execa": "^4.0.0", 1117 | "import-local": "^3.0.2", 1118 | "interpret": "^2.0.0", 1119 | "rechoir": "^0.7.0", 1120 | "v8-compile-cache": "^2.1.0", 1121 | "webpack-merge": "^4.2.2" 1122 | }, 1123 | "dependencies": { 1124 | "commander": { 1125 | "version": "6.1.0", 1126 | "resolved": "https://registry.npmjs.org/commander/-/commander-6.1.0.tgz", 1127 | "integrity": "sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==", 1128 | "dev": true 1129 | } 1130 | } 1131 | }, 1132 | "webpack-merge": { 1133 | "version": "4.2.2", 1134 | "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", 1135 | "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", 1136 | "dev": true, 1137 | "requires": { 1138 | "lodash": "^4.17.15" 1139 | } 1140 | }, 1141 | "webpack-sources": { 1142 | "version": "2.0.1", 1143 | "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.0.1.tgz", 1144 | "integrity": "sha512-A9oYz7ANQBK5EN19rUXbvNgfdfZf5U2gP0769OXsj9CvYkCR6OHOsd6OKyEy4H38GGxpsQPKIL83NC64QY6Xmw==", 1145 | "dev": true, 1146 | "requires": { 1147 | "source-list-map": "^2.0.1", 1148 | "source-map": "^0.6.1" 1149 | } 1150 | }, 1151 | "which": { 1152 | "version": "2.0.2", 1153 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1154 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1155 | "dev": true, 1156 | "requires": { 1157 | "isexe": "^2.0.0" 1158 | } 1159 | }, 1160 | "wordwrapjs": { 1161 | "version": "4.0.0", 1162 | "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.0.tgz", 1163 | "integrity": "sha512-Svqw723a3R34KvsMgpjFBYCgNOSdcW3mQFK4wIfhGQhtaFVOJmdYoXgi63ne3dTlWgatVcUc7t4HtQ/+bUVIzQ==", 1164 | "dev": true, 1165 | "requires": { 1166 | "reduce-flatten": "^2.0.0", 1167 | "typical": "^5.0.0" 1168 | } 1169 | }, 1170 | "wrappy": { 1171 | "version": "1.0.2", 1172 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1173 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1174 | "dev": true 1175 | } 1176 | } 1177 | } 1178 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-state-store", 3 | "version": "3.0.1", 4 | "description": "📦 Vue State Management (with Publish & Subscribe pattern)", 5 | "scripts": { 6 | "build": "tsc", 7 | "start": "tsc && node dist/index.js", 8 | "clean": "rm -rf ./dist/*.*", 9 | "lint": "prettier --write \"./src/**/*.{js,ts}\"", 10 | "export": "tsc && webpack" 11 | }, 12 | "main": "./dist/index.js", 13 | "types": "./dist/index.d.ts", 14 | "keywords": [ 15 | "vue", 16 | "vuejs", 17 | "vue-module", 18 | "state", 19 | "state-management", 20 | "store", 21 | "state-store", 22 | "nuxt", 23 | "nuxtjs", 24 | "nuxt-module", 25 | "typescript" 26 | ], 27 | "homepage": "https://a-ha.io", 28 | "repository": { 29 | "type": "git", 30 | "url": "https://github.com/AhaOfficial/vue-state-store" 31 | }, 32 | "author": "AhaOfficial", 33 | "license": "MIT", 34 | "dependencies": {}, 35 | "devDependencies": { 36 | "@types/node": "^14.14.0", 37 | "@vue/composition-api": "*", 38 | "prettier": "^2.1.2", 39 | "terser-webpack-plugin": "^5.0.0", 40 | "webpack": "^5.1.3", 41 | "webpack-cli": "^4.1.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/default.ts: -------------------------------------------------------------------------------- 1 | import * as Default from './' 2 | export default Default 3 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { StartStopNotifier, IStore } from './interface' 2 | import * as Utils from './utils' 3 | import { Store, storeMap } from './store' 4 | /** 5 | * Create a store that allows both updating and reading by subscription. 6 | * @param {*=}value initial value 7 | * @param {StartStopNotifier=}start start and stop notifications for subscriptions 8 | */ 9 | export const store = ( 10 | value: T, 11 | start: StartStopNotifier = Utils.noop 12 | ) => { 13 | return new Store(value, start) 14 | } 15 | 16 | /** 17 | * Generate SSR Store Data 18 | */ 19 | export const useSSR = () => { 20 | const renderedStates: { 21 | [storeName in string]: any 22 | } = {} 23 | 24 | for (let storeName of Object.keys(storeMap)) { 25 | try { 26 | const storeValue = storeMap[storeName].get() 27 | if (storeValue) renderedStates[storeName] = storeValue 28 | } catch (e) {} 29 | } 30 | 31 | return { _vss: renderedStates } 32 | } 33 | 34 | export type { IStore } 35 | export { Store } 36 | -------------------------------------------------------------------------------- /src/interface.ts: -------------------------------------------------------------------------------- 1 | export type Subscriber = (value: T) => void | Promise 2 | export type Unsubscriber = () => void 3 | export type Updater = (value: T) => T 4 | export type AsyncUpdater = (value: T) => Promise 5 | export type Invalidator = (value?: T) => void 6 | 7 | export type SubscribeInvalidateTuple = [Subscriber, Invalidator] 8 | export type StartStopNotifier = (set: Subscriber) => Unsubscriber | void 9 | 10 | export interface IStore { 11 | /** 12 | * Get value and inform subscribers. 13 | */ 14 | get(): T 15 | 16 | /** 17 | * Set value and inform subscribers. 18 | * @param value to set 19 | */ 20 | set(value?: T): void 21 | 22 | /** 23 | * Subscribe on value changes. 24 | * @param run subscription callback 25 | * @param invalidate cleanup callback 26 | */ 27 | subscribe(run: Subscriber, invalidate?: Invalidator): Unsubscriber 28 | 29 | /** 30 | * Update value using callback and inform subscribers. 31 | * @param updater callback 32 | */ 33 | update(updater: Updater): void 34 | } 35 | -------------------------------------------------------------------------------- /src/store.ts: -------------------------------------------------------------------------------- 1 | import * as Interface from './interface' 2 | import * as Utils from './utils' 3 | import { 4 | ref, 5 | UnwrapRef, 6 | watch, 7 | onUnmounted, 8 | WatchSource, 9 | onMounted, 10 | } from '@vue/composition-api' 11 | declare const window: any 12 | 13 | const subscriberQueue: any[] = [] 14 | export const storeMap: { 15 | [storeName in string]: Interface.IStore 16 | } = {} 17 | 18 | export class Store implements Interface.IStore { 19 | protected stop: Interface.Unsubscriber | null = null 20 | protected subscribers: Array> = [] 21 | protected start: Interface.StartStopNotifier 22 | 23 | private _value: T 24 | 25 | constructor(value: T, start: Interface.StartStopNotifier = Utils.noop) { 26 | this._value = value 27 | this.start = start 28 | 29 | const storeName = this.constructor.name 30 | 31 | if ( 32 | typeof window !== 'undefined' && 33 | window.__NUXT__ && 34 | window.__NUXT__._vss && 35 | window.__NUXT__._vss[storeName] 36 | ) { 37 | this._value = window.__NUXT__._vss[storeName] 38 | } 39 | storeMap[storeName] = this 40 | 41 | devtoolsBind(this, storeName) 42 | } 43 | 44 | get(): T { 45 | return Utils.getStoreValue(this) 46 | } 47 | 48 | getItem(key: K): T[K] { 49 | const data = Utils.getStoreValue(this) 50 | return data[key] 51 | } 52 | 53 | set(newValue: T, force: boolean = false) { 54 | return new Promise((resolve) => { 55 | if (Utils.notEqual(this._value, newValue) || force) { 56 | this._value = newValue 57 | if (this.stop) { 58 | const runQueue = !subscriberQueue.length 59 | for (let i = 0; i < this.subscribers.length; i += 1) { 60 | const s = this.subscribers[i] 61 | s[1]() 62 | subscriberQueue.push(s, this._value) 63 | } 64 | if (runQueue) { 65 | for (let i = 0; i < subscriberQueue.length; i += 2) 66 | subscriberQueue[i][0](subscriberQueue[i + 1]) 67 | subscriberQueue.length = 0 68 | } 69 | } 70 | resolve() 71 | } 72 | }) 73 | } 74 | 75 | async update(callback: Interface.Updater | Interface.AsyncUpdater) { 76 | await this.set(await callback(this._value)) 77 | } 78 | 79 | subscribe( 80 | run: Interface.Subscriber, 81 | invalidate: Interface.Invalidator = Utils.noop 82 | ): Interface.Unsubscriber { 83 | const subscriber: Interface.SubscribeInvalidateTuple = [run, invalidate] 84 | this.subscribers.push(subscriber) 85 | if (this.subscribers.length === 1) 86 | this.stop = this.start(this.set) || Utils.noop 87 | if (this._value) run(this._value) 88 | 89 | return () => { 90 | const index = this.subscribers.indexOf(subscriber) 91 | if (index !== -1) this.subscribers.splice(index, 1) 92 | if (this.subscribers.length === 0) { 93 | if (this.stop) this.stop() 94 | this.stop = null 95 | } 96 | } 97 | } 98 | 99 | bind() { 100 | const bindedValue = ref(Utils.clone(this._value)) 101 | 102 | const unsubscribeStore = this.subscribe((data) => { 103 | bindedValue.value = Utils.clone(data) as UnwrapRef 104 | }) 105 | 106 | const unsubscribeWatch = watch( 107 | bindedValue as WatchSource, 108 | () => { 109 | const data = bindedValue.value as T 110 | const dataOfObserverRemoved = Utils.clone(data) 111 | const originOfOfObserverRemoved = Utils.clone(this._value) 112 | 113 | if (!Utils.deepEqual(dataOfObserverRemoved, originOfOfObserverRemoved)) 114 | this.set(dataOfObserverRemoved as T) 115 | }, 116 | { 117 | deep: true, 118 | } 119 | ) 120 | onUnmounted(() => { 121 | if (unsubscribeWatch) unsubscribeWatch() 122 | if (unsubscribeStore) unsubscribeStore() 123 | }) 124 | return bindedValue 125 | } 126 | 127 | watch( 128 | callback: Interface.Subscriber, 129 | option: { 130 | immediate: boolean 131 | } = { 132 | immediate: false, 133 | } 134 | ) { 135 | let unsubscribe: Interface.Unsubscriber 136 | let isFirst = true 137 | onMounted(() => { 138 | unsubscribe = this.subscribe((data) => { 139 | if (isFirst && !option.immediate) { 140 | isFirst = false 141 | return 142 | } 143 | callback(data) 144 | }) 145 | }) 146 | onUnmounted(() => { 147 | if (unsubscribe) unsubscribe() 148 | }) 149 | } 150 | 151 | patch(key: K, value: T[K]) { 152 | return this.update((data) => { 153 | data[key] = value 154 | return data 155 | }) 156 | } 157 | } 158 | 159 | /** 160 | * Processing points for nuxt 161 | */ 162 | const target: any = 163 | typeof window !== 'undefined' 164 | ? window 165 | : typeof global !== 'undefined' 166 | ? global 167 | : {} 168 | 169 | const devtoolsBind = async ( 170 | store: Interface.IStore, 171 | storeName: string 172 | ) => { 173 | const devtoolsHook = target.__VUE_DEVTOOLS_GLOBAL_HOOK__ 174 | if (!devtoolsHook) return 175 | 176 | try { 177 | // vue-state-store-devtools 178 | if (typeof target.VueStateStoreDevtools != 'undefined') 179 | target.VueStateStoreDevtools.devtoolsBind(store, storeName) 180 | } catch (e) {} 181 | } 182 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | export const noop = () => {} 2 | 3 | export const isPromise = (value: any): value is PromiseLike => { 4 | return value && typeof value === 'object' && typeof value.then === 'function' 5 | } 6 | 7 | export const run = (callback: any) => { 8 | return callback() 9 | } 10 | 11 | export const isFunction = (thing: any): thing is Function => { 12 | return typeof thing === 'function' 13 | } 14 | 15 | export const safeNotEqual = (a, b) => { 16 | return a != a 17 | ? b == b 18 | : a !== b || (a && typeof a === 'object') || typeof a === 'function' 19 | } 20 | 21 | export const notEqual = (a, b) => { 22 | return a != a ? b == b : a !== b 23 | } 24 | 25 | export const validateStore = (store, name) => { 26 | if (store != null && typeof store.subscribe !== 'function') 27 | throw new Error(`'${name}' is not a store with a 'subscribe' method`) 28 | } 29 | 30 | export const subscribe = (store, ...callbacks) => { 31 | if (store == null) return noop 32 | const unsub = store.subscribe(...callbacks) 33 | return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub 34 | } 35 | 36 | export const getStoreValue = (store) => { 37 | let value 38 | subscribe(store, (_) => (value = _))() 39 | return value 40 | } 41 | export const deepEqual = (x, y) => { 42 | if (x === y) { 43 | return true 44 | } else if ( 45 | typeof x == 'object' && 46 | x != null && 47 | typeof y == 'object' && 48 | y != null 49 | ) { 50 | if (Object.keys(x).length != Object.keys(y).length) return false 51 | 52 | for (var prop in x) { 53 | if (y.hasOwnProperty(prop)) { 54 | if (!deepEqual(x[prop], y[prop])) return false 55 | } else return false 56 | } 57 | 58 | return true 59 | } else return false 60 | } 61 | 62 | export const clone = (object) => JSON.parse(JSON.stringify(object)) 63 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist", 4 | "baseUrl": ".", 5 | "importHelpers": false, 6 | "sourceMap": false, 7 | "declaration": true, 8 | "inlineSourceMap": false, 9 | "noImplicitAny": false, 10 | "module": "commonjs", 11 | "target": "ES3", 12 | "types": [ 13 | "node" 14 | ], 15 | "lib": [ 16 | "es5", 17 | "es6", 18 | "dom" 19 | ], 20 | "moduleResolution": "node", 21 | "forceConsistentCasingInFileNames": true, 22 | "noImplicitReturns": true, 23 | "noImplicitThis": true, 24 | "strictNullChecks": true, 25 | "suppressImplicitAnyIndexErrors": true, 26 | "resolveJsonModule": true, 27 | "allowSyntheticDefaultImports": true, 28 | "esModuleInterop": true, 29 | "emitDecoratorMetadata": true, 30 | "experimentalDecorators": true, 31 | "skipLibCheck": true 32 | }, 33 | "typeRoots": [ 34 | "./node_modules/@types" 35 | ] 36 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const TerserPlugin = require('terser-webpack-plugin') 2 | const path = require('path') 3 | 4 | module.exports = { 5 | entry: './dist/default.js', 6 | output: { 7 | path: path.resolve(__dirname, 'export'), 8 | filename: 'vue-state-store.js', 9 | publicPath: './dist', 10 | library: 'VueStateStore', 11 | libraryTarget: 'this', 12 | libraryExport: 'default' 13 | }, 14 | mode: 'production', 15 | optimization: { 16 | minimize: true, 17 | minimizer: [new TerserPlugin()] 18 | }, 19 | 20 | externals: { 21 | "@vue/composition-api": "VueCompositionAPI", 22 | }, 23 | } 24 | --------------------------------------------------------------------------------