@mirta/store 0.3.5 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +295 -1
- package/README.ru.md +286 -1
- package/dist/index.d.mts +416 -11
- package/dist/index.mjs +508 -40
- package/package.json +13 -4
package/README.md
CHANGED
|
@@ -3,5 +3,299 @@
|
|
|
3
3
|
[](https://github.com/wb-mirta/core/blob/latest/packages/mirta-store/README.md)
|
|
4
4
|
[](https://github.com/wb-mirta/core/blob/latest/packages/mirta-store/README.ru.md)
|
|
5
5
|
[](https://npmjs.com/package/@mirta/store)
|
|
6
|
+
[](https://npmjs.com/package/@mirta/store)
|
|
6
7
|
|
|
7
|
-
Type-safe storage solution for
|
|
8
|
+
> Type-safe storage solution for automation scenarios, inspired by the Pinia architecture.
|
|
9
|
+
|
|
10
|
+
Each script in the `wb-rules` folder runs in an isolated context — with a separate namespace. This means that functions and variables from one script are inaccessible to others.
|
|
11
|
+
|
|
12
|
+
`@mirta/store` enables moving data into **centralized states**, available across any scripts and modules. Provides a convenient API for:
|
|
13
|
+
- defining state structure,
|
|
14
|
+
- type-safe access,
|
|
15
|
+
- reactive updates,
|
|
16
|
+
- instance isolation.
|
|
17
|
+
|
|
18
|
+
Works on Wiren Board controllers, supports TypeScript, and is compatible with `wb-rules`.
|
|
19
|
+
|
|
20
|
+
## 📦 Installation
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
pnpm add @mirta/store
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
✅ The package is processed by the configuration from `@mirta/rollup` and automatically embedded as a `wb-rules-modules` module when used in code.
|
|
27
|
+
|
|
28
|
+
## 🚀 Quick Start
|
|
29
|
+
|
|
30
|
+
### 1. Define the store structure
|
|
31
|
+
|
|
32
|
+
Use `defineStore` to describe the structure. Do this **once**, preferably in a module.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// src/wb-rules-modules/counter.ts
|
|
36
|
+
import { defineStore } from '@mirta/store'
|
|
37
|
+
|
|
38
|
+
export const useCounter = defineStore('counter', {
|
|
39
|
+
state: () => ({
|
|
40
|
+
count: 0,
|
|
41
|
+
}),
|
|
42
|
+
getters: {
|
|
43
|
+
double: (state) => state.count * 2,
|
|
44
|
+
},
|
|
45
|
+
actions: {
|
|
46
|
+
increment() {
|
|
47
|
+
this.count++
|
|
48
|
+
},
|
|
49
|
+
setCount(value: number) {
|
|
50
|
+
this.count = value
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 2. Use in scripts and modules
|
|
57
|
+
|
|
58
|
+
Import the store in any `wb-rules` script or `wb-rules-modules` module — the state will be shared.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// src/wb-rules/01-init.ts
|
|
62
|
+
import { useCounter } from '#wbm/counter'
|
|
63
|
+
|
|
64
|
+
const store = useCounter()
|
|
65
|
+
log(`Counter: ${store.count}`) // 0
|
|
66
|
+
|
|
67
|
+
store.increment()
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Changes in one script are instantly available in another.
|
|
71
|
+
|
|
72
|
+
## 📚 API
|
|
73
|
+
|
|
74
|
+
### `defineStore(typeId, options)`
|
|
75
|
+
|
|
76
|
+
Creates a store definition.
|
|
77
|
+
- **`typeId: string`** — store type identifier (must be unique),
|
|
78
|
+
- **`options: DefineStoreOptions`** — configuration: `state`, `getters`, `actions`.
|
|
79
|
+
|
|
80
|
+
> ❗ Repeated calls with the same `typeId` throw a `StoreError` — definitions are unique.
|
|
81
|
+
|
|
82
|
+
Returns the `useStore()` function.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
### `useStore(scope?)`
|
|
87
|
+
|
|
88
|
+
Returns a store instance.
|
|
89
|
+
|
|
90
|
+
- **`scope?: string`** — optional context identifier (e.g., `'kitchen'`).
|
|
91
|
+
|
|
92
|
+
If `scope` is provided, `storeId = "${typeId}/${scope}"`.
|
|
93
|
+
Otherwise, the general `storeId = typeId` is used.
|
|
94
|
+
|
|
95
|
+
#### Instance properties
|
|
96
|
+
|
|
97
|
+
| Property | Type | Description |
|
|
98
|
+
|--------|-----|----------|
|
|
99
|
+
| `$id` | `string` | Unique instance identifier |
|
|
100
|
+
| `$state` | `TState` | Reference to the state |
|
|
101
|
+
| `$patch` | `(patch: Partial<TState>) => void`<br/>`(mutator: (state: TState) => void) => void` | Updates the state |
|
|
102
|
+
| `$reset` | `() => void` | Resets the state to initial |
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
### `StoreError`
|
|
107
|
+
|
|
108
|
+
A specialized error class with the following codes:
|
|
109
|
+
|
|
110
|
+
- `'alreadyDefined'` — repeated definition,
|
|
111
|
+
- `'alreadyDefinedOutside'` — repeated definition in another file,
|
|
112
|
+
- `'readonlyProperty'` — modify a readonly property,
|
|
113
|
+
- `'unknownProperty'` — access to unknown property.
|
|
114
|
+
|
|
115
|
+
## 🔧 Features
|
|
116
|
+
|
|
117
|
+
### ✅ Full type safety
|
|
118
|
+
Autocompletion, type checking, and support for `this` in getters and actions.
|
|
119
|
+
|
|
120
|
+
<details>
|
|
121
|
+
<summary>Details</summary>
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
getters: {
|
|
125
|
+
double: (state) => state.count * 2,
|
|
126
|
+
doublePlusOne(): number { // ← explicit return type required
|
|
127
|
+
return this.double + 1
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
actions: {
|
|
131
|
+
increment() {
|
|
132
|
+
this.count++
|
|
133
|
+
this.$patch({ count: 5 })
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
> ⚠️ Getters using `this` **must have an explicit return type**.
|
|
139
|
+
</details>
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
### 📦 Deep updates with `$patch`
|
|
144
|
+
|
|
145
|
+
Safely update nested objects — `@mirta/store` performs **deep merging**.
|
|
146
|
+
|
|
147
|
+
<details>
|
|
148
|
+
<summary>Details</summary>
|
|
149
|
+
|
|
150
|
+
Two update methods are supported:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
store.$patch({ count: 10, config: { debug: true } })
|
|
154
|
+
store.$patch((state) => {
|
|
155
|
+
state.tags.push('new')
|
|
156
|
+
})
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
> Uses `deepMerge`: objects are merged, arrays are overwritten.
|
|
160
|
+
</details>
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
### 🔁 Reset state with `$reset`
|
|
165
|
+
|
|
166
|
+
Restore the store to its initial state — as when first created.
|
|
167
|
+
|
|
168
|
+
<details>
|
|
169
|
+
<summary>Details</summary>
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
store.$reset()
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
- Calls `state()`
|
|
176
|
+
- Preserves reactivity
|
|
177
|
+
- Resets state values for testing, logic restart, or configuration reset
|
|
178
|
+
</details>
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
### 🛑 Protection against duplication
|
|
183
|
+
|
|
184
|
+
A store cannot be defined twice with the same `typeId` — prevents conflicts.
|
|
185
|
+
|
|
186
|
+
<details>
|
|
187
|
+
<summary>Details</summary>
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
defineStore('sensor', { ... }) // ✅
|
|
191
|
+
defineStore('sensor', { ... }) // ❌ StoreError: alreadyDefined
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
> Check runs in both `development` and `production`.
|
|
195
|
+
> Ensures only one module controls a store type.
|
|
196
|
+
</details>
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
### 🧩 Scoped States — isolated instances
|
|
201
|
+
|
|
202
|
+
Create separate store instances for different contexts: rooms, devices, sessions.
|
|
203
|
+
|
|
204
|
+
<details>
|
|
205
|
+
<summary>Details</summary>
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
const useSensor = defineStore('sensor', { ... })
|
|
209
|
+
|
|
210
|
+
const kitchen = useSensor('kitchen')
|
|
211
|
+
const bathroom = useSensor('bathroom')
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Each instance has `storeId = "sensor/kitchen"` — state is isolated.
|
|
215
|
+
|
|
216
|
+
> Useful when managing multiple similar entities.
|
|
217
|
+
</details>
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
### 🔐 Internal Store — state encapsulation
|
|
222
|
+
|
|
223
|
+
Make a store inaccessible from outside by not exporting `useStore()`.
|
|
224
|
+
|
|
225
|
+
<details>
|
|
226
|
+
<summary>Details</summary>
|
|
227
|
+
|
|
228
|
+
If `useStore()` is not exported:
|
|
229
|
+
- external modules cannot access the state,
|
|
230
|
+
- redefining with the same `typeId` is prohibited,
|
|
231
|
+
- the state becomes an **internal implementation detail**.
|
|
232
|
+
|
|
233
|
+
> Useful in NPM packages where implementation must be hidden.
|
|
234
|
+
>
|
|
235
|
+
> ❗ Not meaningful in local modules where `defineStore()` and `useStore()` are in the same file.
|
|
236
|
+
</details>
|
|
237
|
+
|
|
238
|
+
### 💾 State Serialization
|
|
239
|
+
|
|
240
|
+
To save or transfer state, use $state.
|
|
241
|
+
|
|
242
|
+
<details>
|
|
243
|
+
<summary>Details</summary>
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
const store = useCounter()
|
|
247
|
+
|
|
248
|
+
// ✅ Correct — serializes only the state
|
|
249
|
+
const json = JSON.stringify(store.$state)
|
|
250
|
+
|
|
251
|
+
// ❌ Incorrect — includes functions and internal properties
|
|
252
|
+
const json = JSON.stringify(store)
|
|
253
|
+
```
|
|
254
|
+
The `$state` property contains a plain state object without methods or proxy-related data.
|
|
255
|
+
|
|
256
|
+
</details>
|
|
257
|
+
|
|
258
|
+
## 🔄 When to use
|
|
259
|
+
|
|
260
|
+
### 1. Temporary state: `@mirta/store` vs `global.__proto__`
|
|
261
|
+
|
|
262
|
+
| Feature | `@mirta/store` | `global.__proto__` |
|
|
263
|
+
|--------|----------------|--------------------|
|
|
264
|
+
| Encapsulation | ✔️ | ❌ |
|
|
265
|
+
| Type safety | ✔️ | ❌ |
|
|
266
|
+
| API (`$patch`, `$reset`) | ✔️ | ❌ |
|
|
267
|
+
| Instance isolation | ✔️ `useStore('kitchen')` | ❌ |
|
|
268
|
+
| Readability | ✔️ | ❌ |
|
|
269
|
+
| Performance | ✔️ Minimal overhead | ✔️ Direct access |
|
|
270
|
+
|
|
271
|
+
> ❌ `global.__proto__` — **an anti-pattern**. Do not use.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
### 2. Temporary vs persistent storage
|
|
276
|
+
|
|
277
|
+
| Feature | `@mirta/store` | `PersistentStorage` |
|
|
278
|
+
|--------|----------------|---------------------|
|
|
279
|
+
| Storage | RAM | Flash / FS |
|
|
280
|
+
| Survives reboot | ❌ | ✔️ |
|
|
281
|
+
| Type safety | ✔️ | ❌ |
|
|
282
|
+
| Speed | High | Slower (IO) |
|
|
283
|
+
| API | `$patch`, `$reset` | `get`, `set`, `remove` |
|
|
284
|
+
|
|
285
|
+
> ✅ Use together:
|
|
286
|
+
> - `@mirta/store` — for current runtime state,
|
|
287
|
+
> - `PersistentStorage` — for saving and restoring.
|
|
288
|
+
|
|
289
|
+
## 🧪 Testing
|
|
290
|
+
|
|
291
|
+
The package is fully covered with unit tests using `@mirta/testing` and `vitest`.
|
|
292
|
+
Tests verify:
|
|
293
|
+
- Store creation and usage
|
|
294
|
+
- `$patch`, `$reset` functionality
|
|
295
|
+
- Support for isolated instances
|
|
296
|
+
- Protection against duplication
|
|
297
|
+
|
|
298
|
+
## ⚠️ Limitations
|
|
299
|
+
|
|
300
|
+
- State is **lost** upon restarting the `wb-rules.service` or the controller.
|
|
301
|
+
- Store instances are cached globally and **not automatically cleared**. Dynamically creating many instances with unique `scope` values may lead to memory leaks. Use predictable, bounded `scope` values.
|
package/README.ru.md
CHANGED
|
@@ -3,5 +3,290 @@
|
|
|
3
3
|
[](https://github.com/wb-mirta/core/blob/latest/packages/mirta-store/README.md)
|
|
4
4
|
[](https://github.com/wb-mirta/core/blob/latest/packages/mirta-store/README.ru.md)
|
|
5
5
|
[](https://npmjs.com/package/@mirta/store)
|
|
6
|
+
[](https://npmjs.com/package/@mirta/store)
|
|
6
7
|
|
|
7
|
-
Типизированное хранилище для
|
|
8
|
+
> Типизированное хранилище состояний для сценариев автоматизации, вдохновлённое архитектурой Pinia.
|
|
9
|
+
|
|
10
|
+
Каждый скрипт в папке `wb-rules` выполняется в изолированном контексте — с отдельным пространством имён. Это означает, что функции и переменные одного скрипта недоступны другим.
|
|
11
|
+
|
|
12
|
+
`@mirta/store` позволяет выносить данные в **централизованные состояния**, доступные из любых скриптов и модулей. Предоставляет удобный API для:
|
|
13
|
+
- определения структуры состояния,
|
|
14
|
+
- типизированного доступа,
|
|
15
|
+
- реактивного обновления,
|
|
16
|
+
- изоляции экземпляров.
|
|
17
|
+
|
|
18
|
+
Работает на контроллерах Wiren Board, поддерживает TypeScript и совместим с `wb-rules`.
|
|
19
|
+
|
|
20
|
+
## 📦 Установка
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
pnpm add @mirta/store
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
✅ Пакет проходит сборку конфигурацией из пакета `@mirta/rollup` и при вызове в коде автоматически встраивается как модуль `wb-rules-modules`.
|
|
27
|
+
|
|
28
|
+
## 🚀 Быстрый старт
|
|
29
|
+
|
|
30
|
+
### 1. Задайте структуру хранилища
|
|
31
|
+
|
|
32
|
+
Используйте `defineStore` для описания структуры. Делайте это **один раз**, лучше в модуле.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// src/wb-rules-modules/counter.ts
|
|
36
|
+
import { defineStore } from '@mirta/store'
|
|
37
|
+
|
|
38
|
+
export const useCounter = defineStore('counter', {
|
|
39
|
+
state: () => ({
|
|
40
|
+
count: 0,
|
|
41
|
+
}),
|
|
42
|
+
getters: {
|
|
43
|
+
double: (state) => state.count * 2,
|
|
44
|
+
},
|
|
45
|
+
actions: {
|
|
46
|
+
increment() {
|
|
47
|
+
this.count++
|
|
48
|
+
},
|
|
49
|
+
setCount(value: number) {
|
|
50
|
+
this.count = value
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 2. Используйте в скриптах и модулях
|
|
57
|
+
|
|
58
|
+
Подключите хранилище в любом скрипте `wb-rules` или модуле `wb-rules-modules` — состояние будет общим.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// src/wb-rules/01-init.ts
|
|
62
|
+
import { useCounter } from '#wbm/counter'
|
|
63
|
+
|
|
64
|
+
const store = useCounter()
|
|
65
|
+
log(`Счётчик: ${store.count}`) // 0
|
|
66
|
+
|
|
67
|
+
store.increment()
|
|
68
|
+
```
|
|
69
|
+
Изменения в одном скрипте мгновенно доступны в другом.
|
|
70
|
+
|
|
71
|
+
## 📚 API
|
|
72
|
+
|
|
73
|
+
### `defineStore(typeId, options)`
|
|
74
|
+
|
|
75
|
+
Создаёт определение хранилища.
|
|
76
|
+
- **`typeId: string`** — идентификатор типа хранилища (должен быть уникальным),
|
|
77
|
+
- **`options: DefineStoreOptions`** — конфигурация: `state`, `getters`, `actions`.
|
|
78
|
+
|
|
79
|
+
> ❗ Повторный вызов с тем же `typeId` вызывает ошибку `StoreError` — определение уникально.
|
|
80
|
+
|
|
81
|
+
Возвращает функцию `useStore()`.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
### `useStore(scope?)`
|
|
86
|
+
|
|
87
|
+
Возвращает экземпляр хранилища.
|
|
88
|
+
|
|
89
|
+
- **`scope?: string`** — опциональный идентификатор контекста (например, `'kitchen'`).
|
|
90
|
+
|
|
91
|
+
Если `scope` указан, `storeId = "${typeId}/${scope}"`.
|
|
92
|
+
Если нет — используется общий `storeId = typeId`.
|
|
93
|
+
|
|
94
|
+
#### Свойства экземпляра
|
|
95
|
+
|
|
96
|
+
| Свойство | Тип | Описание |
|
|
97
|
+
|--------|-----|----------|
|
|
98
|
+
| `$id` | `string` | Уникальный идентификатор экземпляра |
|
|
99
|
+
| `$state` | `TState` | Ссылка на состояние |
|
|
100
|
+
| `$patch` | `(patch: Partial<TState>) => void`<br/>`(mutator: (state: TState) => void) => void` | Обновляет состояние |
|
|
101
|
+
| `$reset` | `() => void` | Сбрасывает состояние к начальному |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### `StoreError`
|
|
106
|
+
|
|
107
|
+
Специализированный класс ошибок с кодами:
|
|
108
|
+
|
|
109
|
+
- `'alreadyDefined'` — повторное определение,
|
|
110
|
+
- `'alreadyDefinedOutside'` — повторное определение в другом файле,
|
|
111
|
+
- `'readonlyProperty'` — изменение служебного поля,
|
|
112
|
+
- `'unknownProperty'` — обращение к неизвестному полю.
|
|
113
|
+
|
|
114
|
+
## 🔧 Особенности
|
|
115
|
+
|
|
116
|
+
### ✅ Полная типобезопасность
|
|
117
|
+
|
|
118
|
+
Автодополнение, проверка типов, поддержка `this` в геттерах и действиях.
|
|
119
|
+
|
|
120
|
+
<details>
|
|
121
|
+
<summary>Подробнее</summary>
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
getters: {
|
|
125
|
+
double: (state) => state.count * 2,
|
|
126
|
+
doublePlusOne(): number { // ← явный тип обязателен
|
|
127
|
+
return this.double + 1
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
actions: {
|
|
131
|
+
increment() {
|
|
132
|
+
this.count++
|
|
133
|
+
this.$patch({ count: 5 })
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
> ⚠️ Геттеры, использующие `this`, **должны иметь явный возвращаемый тип**.
|
|
139
|
+
</details>
|
|
140
|
+
|
|
141
|
+
### 📦 Глубокое обновление через `$patch`
|
|
142
|
+
|
|
143
|
+
Обновляйте вложенные объекты безопасно — `@mirta/store` выполняет **глубокое слияние**.
|
|
144
|
+
|
|
145
|
+
<details>
|
|
146
|
+
<summary>Подробнее</summary>
|
|
147
|
+
|
|
148
|
+
Поддерживается два способа:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
store.$patch({ count: 10, config: { debug: true } })
|
|
152
|
+
store.$patch((state) => {
|
|
153
|
+
state.tags.push('new')
|
|
154
|
+
})
|
|
155
|
+
```
|
|
156
|
+
> Использует `deepMerge`: объекты сливаются, массивы перезаписываются.
|
|
157
|
+
</details>
|
|
158
|
+
|
|
159
|
+
### 🔁 Сброс состояния через `$reset`
|
|
160
|
+
|
|
161
|
+
Верните хранилище к начальному состоянию — как при первом создании.
|
|
162
|
+
|
|
163
|
+
<details>
|
|
164
|
+
<summary>Подробнее</summary>
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
store.$reset()
|
|
168
|
+
```
|
|
169
|
+
- Вызывает `state()`
|
|
170
|
+
- Сохраняет реактивность
|
|
171
|
+
- Удаляет значения состояния для тестов, перезапуска логики, сброса настроек
|
|
172
|
+
|
|
173
|
+
</details>
|
|
174
|
+
|
|
175
|
+
### 🛑 Защита от дублирования
|
|
176
|
+
|
|
177
|
+
Хранилище нельзя определить дважды с одним `typeId` — это предотвращает конфликты.
|
|
178
|
+
|
|
179
|
+
<details>
|
|
180
|
+
<summary>Подробнее</summary>
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
defineStore('sensor', { ... }) // ✅
|
|
184
|
+
defineStore('sensor', { ... }) // ❌ StoreError: alreadyDefined
|
|
185
|
+
```
|
|
186
|
+
> Проверка работает всегда — в `development` и `production`.
|
|
187
|
+
> Гарантирует, что только один модуль может контролировать тип хранилища.
|
|
188
|
+
|
|
189
|
+
</details>
|
|
190
|
+
|
|
191
|
+
### 🧩 Scoped States — изолированные экземпляры
|
|
192
|
+
|
|
193
|
+
Создавайте отдельные экземпляры хранилища для разных контекстов: комнат, устройств, сессий.
|
|
194
|
+
|
|
195
|
+
<details>
|
|
196
|
+
<summary>Подробнее</summary>
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
const useSensor = defineStore('sensor', { ... })
|
|
200
|
+
|
|
201
|
+
const kitchen = useSensor('kitchen')
|
|
202
|
+
const bathroom = useSensor('bathroom')
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Каждый экземпляр имеет `storeId = "sensor/kitchen"` — состояние изолировано.
|
|
206
|
+
|
|
207
|
+
> Полезно при управлении множеством однотипных сущностей.
|
|
208
|
+
|
|
209
|
+
</details>
|
|
210
|
+
|
|
211
|
+
### 🔐 Internal Store — инкапсуляция состояния
|
|
212
|
+
|
|
213
|
+
Сделайте хранилище недоступным извне, не экспортируя `useStore()`.
|
|
214
|
+
|
|
215
|
+
<details>
|
|
216
|
+
<summary>Подробнее</summary>
|
|
217
|
+
|
|
218
|
+
Если `useStore()` не экспортирован:
|
|
219
|
+
- внешние модули не могут получить доступ к состоянию,
|
|
220
|
+
- повторное определение с тем же `typeId` запрещено,
|
|
221
|
+
- состояние становится **внутренней деталью реализации**.
|
|
222
|
+
|
|
223
|
+
> Полезно в NPM-пакетах, где нужно скрыть реализацию.
|
|
224
|
+
>
|
|
225
|
+
> ❗ Не имеет смысла в локальных модулях, где `defineStore()` и `useStore()` в одном файле.
|
|
226
|
+
|
|
227
|
+
</details>
|
|
228
|
+
|
|
229
|
+
### 💾 Сериализация состояния
|
|
230
|
+
Для сохранения или передачи состояния используйте **`$state`**.
|
|
231
|
+
|
|
232
|
+
<details>
|
|
233
|
+
<summary>Подробнее</summary>
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
const store = useCounter()
|
|
237
|
+
|
|
238
|
+
// ✅ Правильно — сериализует только состояние
|
|
239
|
+
const json = JSON.stringify(store.$state)
|
|
240
|
+
|
|
241
|
+
// ❌ Неправильно — содержит функции и служебные поля
|
|
242
|
+
const json = JSON.stringify(store)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Свойство `$state` содержит чистый объект состояния без методов и прокси-данных.
|
|
246
|
+
|
|
247
|
+
</details>
|
|
248
|
+
|
|
249
|
+
## 🔄 Когда что использовать
|
|
250
|
+
|
|
251
|
+
### 1. Временное состояние: `@mirta/store` vs `global.__proto__`
|
|
252
|
+
|
|
253
|
+
| Характеристика | `@mirta/store` | `global.__proto__` |
|
|
254
|
+
|----------------|----------------|--------------------|
|
|
255
|
+
| Инкапсуляция | ✔️ | ❌ |
|
|
256
|
+
| Типизация | ✔️ | ❌ |
|
|
257
|
+
| API (`$patch`, `$reset`) | ✔️ | ❌ |
|
|
258
|
+
| Изоляция экземпляров | ✔️ `useStore('kitchen')` | ❌ |
|
|
259
|
+
| Читаемость | ✔️ | ❌ |
|
|
260
|
+
| Производительность | ✔️ Минимальный оверхед | ✔️ Прямой доступ |
|
|
261
|
+
|
|
262
|
+
> ❌ `global.__proto__` — **антипаттерн**. Не используйте.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
### 2. Временное vs постоянное хранение
|
|
267
|
+
|
|
268
|
+
| Характеристика | `@mirta/store` | `PersistentStorage` |
|
|
269
|
+
|----------------|----------------|---------------------|
|
|
270
|
+
| Хранение | RAM | Flash / FS |
|
|
271
|
+
| После перезагрузки | ❌ | ✔️ |
|
|
272
|
+
| Типизация | ✔️ | ❌ |
|
|
273
|
+
| Скорость | Высокая | Ниже (IO) |
|
|
274
|
+
| API | `$patch`, `$reset` | `get`, `set`, `remove` |
|
|
275
|
+
|
|
276
|
+
> ✅ Используйте совместно:
|
|
277
|
+
> - `@mirta/store` — для текущего состояния,
|
|
278
|
+
> - `PersistentStorage` — для сохранения и восстановления.
|
|
279
|
+
|
|
280
|
+
## 🧪 Тестирование
|
|
281
|
+
|
|
282
|
+
Пакет полностью покрыт модульными тестами с использованием `@mirta/testing` и `vitest`.
|
|
283
|
+
Тесты проверяют:
|
|
284
|
+
- Создание и использование хранилищ
|
|
285
|
+
- Работу `$patch`, `$reset`
|
|
286
|
+
- Поддержку изолированных экземпляров
|
|
287
|
+
- Защиту от дублирования
|
|
288
|
+
|
|
289
|
+
## ⚠️ Ограничения
|
|
290
|
+
|
|
291
|
+
- Состояние **теряется** при перезагрузке сервиса `wb-rules.service` или контроллера.
|
|
292
|
+
- Экземпляры хранилищ кэшируются глобально и **не удаляются автоматически**. При динамическом создании большого количества экземпляров с уникальными `scope` может возникнуть утечка памяти. Рекомендуется использовать предсказуемые, ограниченные значения `scope`.
|