npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mirta/store

v0.5.1

Published

Type-safe store for wb-rules

Readme

@mirta/store

en ru NPM Version NPM Downloads

Типизированное хранилище состояний для сценариев автоматизации, вдохновлённое архитектурой Pinia.

Каждый скрипт в папке wb-rules выполняется в изолированном контексте — с отдельным пространством имён. Это означает, что функции и переменные одного скрипта недоступны другим.

@mirta/store позволяет выносить данные в централизованные состояния, доступные из любых скриптов и модулей. Предоставляет удобный API для:

  • определения структуры состояния,
  • типизированного доступа,
  • реактивного обновления,
  • изоляции экземпляров.

Работает на контроллерах Wiren Board, поддерживает TypeScript и совместим с wb-rules.

📦 Установка

pnpm add @mirta/store

✅ Пакет проходит сборку конфигурацией из пакета @mirta/rollup и при вызове в коде автоматически встраивается как модуль wb-rules-modules.

🚀 Быстрый старт

1. Задайте структуру хранилища

Используйте defineStore для описания структуры. Делайте это один раз, лучше в модуле.

// src/wb-rules-modules/counter.ts
import { defineStore } from '@mirta/store'

export const useCounter = defineStore('counter', {
  state: () => ({
    count: 0,
  }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
    setCount(value: number) {
      this.count = value
    },
  },
})

2. Используйте в скриптах и модулях

Подключите хранилище в любом скрипте wb-rules или модуле wb-rules-modules — состояние будет общим.

// src/wb-rules/01-init.ts
import { useCounter } from '#wbm/counter'

const store = useCounter()
log(`Счётчик: ${store.count}`) // 0

store.increment()

Изменения в одном скрипте мгновенно доступны в другом.

📚 API

defineStore(typeId, options)

Создаёт определение хранилища.

  • typeId: string — идентификатор типа хранилища (должен быть уникальным),
  • options: DefineStoreOptions — конфигурация: state, getters, actions.

❗ Повторный вызов с тем же typeId вызывает ошибку StoreError — определение уникально.

Возвращает функцию useStore().


useStore(scope?)

Возвращает экземпляр хранилища.

  • scope?: string — опциональный идентификатор контекста (например, 'kitchen').

Если scope указан, storeId = "${typeId}/${scope}".
Если нет — используется общий storeId = typeId.

Свойства экземпляра

| Свойство | Тип | Описание | |--------|-----|----------| | $id | string | Уникальный идентификатор экземпляра | | $state | TState | Ссылка на состояние | | $patch | (patch: Partial<TState>) => void(mutator: (state: TState) => void) => void | Обновляет состояние | | $reset | () => void | Сбрасывает состояние к начальному |


StoreError

Специализированный класс ошибок с кодами:

  • 'alreadyDefined' — повторное определение,
  • 'alreadyDefinedOutside' — повторное определение в другом файле,
  • 'readonlyProperty' — изменение служебного поля,
  • 'unknownProperty' — обращение к неизвестному полю.

🔧 Особенности

✅ Полная типобезопасность

Автодополнение, проверка типов, поддержка this в геттерах и действиях.

getters: {
  double: (state) => state.count * 2,
  doublePlusOne(): number { // ← явный тип обязателен
    return this.double + 1
  }
},
actions: {
  increment() {
    this.count++
    this.$patch({ count: 5 })
  }
}

⚠️ Геттеры, использующие this, должны иметь явный возвращаемый тип.

📦 Глубокое обновление через $patch

Обновляйте вложенные объекты безопасно — @mirta/store выполняет глубокое слияние.

Поддерживается два способа:

store.$patch({ count: 10, config: { debug: true } })
store.$patch((state) => {
  state.tags.push('new')
})

Использует deepMerge: объекты сливаются, массивы перезаписываются.

🔁 Сброс состояния через $reset

Верните хранилище к начальному состоянию — как при первом создании.

store.$reset()
  • Вызывает state()
  • Сохраняет реактивность
  • Удаляет значения состояния для тестов, перезапуска логики, сброса настроек

🛑 Защита от дублирования

Хранилище нельзя определить дважды с одним typeId — это предотвращает конфликты.

defineStore('sensor', { ... }) // ✅
defineStore('sensor', { ... }) // ❌ StoreError: alreadyDefined

Проверка работает всегда — в development и production.
Гарантирует, что только один модуль может контролировать тип хранилища.

🧩 Scoped States — изолированные экземпляры

Создавайте отдельные экземпляры хранилища для разных контекстов: комнат, устройств, сессий.

const useSensor = defineStore('sensor', { ... })

const kitchen = useSensor('kitchen')
const bathroom = useSensor('bathroom')

Каждый экземпляр имеет storeId = "sensor/kitchen" — состояние изолировано.

Полезно при управлении множеством однотипных сущностей.

🔐 Internal Store — инкапсуляция состояния

Сделайте хранилище недоступным извне, не экспортируя useStore().

Если useStore() не экспортирован:

  • внешние модули не могут получить доступ к состоянию,
  • повторное определение с тем же typeId запрещено,
  • состояние становится внутренней деталью реализации.

Полезно в NPM-пакетах, где нужно скрыть реализацию.

❗ Не имеет смысла в локальных модулях, где defineStore() и useStore() в одном файле.

💾 Сериализация состояния

Для сохранения или передачи состояния используйте $state.

const store = useCounter()

// ✅ Правильно — сериализует только состояние
const json = JSON.stringify(store.$state)

// ❌ Неправильно — содержит функции и служебные поля
const json = JSON.stringify(store)

Свойство $state содержит чистый объект состояния без методов и прокси-данных.

🔄 Когда что использовать

1. Временное состояние: @mirta/store vs global.__proto__

| Характеристика | @mirta/store | global.__proto__ | |----------------|----------------|--------------------| | Инкапсуляция | ✔️ | ❌ | | Типизация | ✔️ | ❌ | | API ($patch, $reset) | ✔️ | ❌ | | Изоляция экземпляров | ✔️ useStore('kitchen') | ❌ | | Читаемость | ✔️ | ❌ | | Производительность | ✔️ Минимальный оверхед | ✔️ Прямой доступ |

global.__proto__антипаттерн. Не используйте.


2. Временное vs постоянное хранение

| Характеристика | @mirta/store | PersistentStorage | |----------------|----------------|---------------------| | Хранение | RAM | Flash / FS | | После перезагрузки | ❌ | ✔️ | | Типизация | ✔️ | ❌ | | Скорость | Высокая | Ниже (IO) | | API | $patch, $reset | get, set, remove |

✅ Используйте совместно:

  • @mirta/store — для текущего состояния,
  • PersistentStorage — для сохранения и восстановления.

🧪 Тестирование

Пакет полностью покрыт модульными тестами с использованием @mirta/testing и vitest.
Тесты проверяют:

  • Создание и использование хранилищ
  • Работу $patch, $reset
  • Поддержку изолированных экземпляров
  • Защиту от дублирования

⚠️ Ограничения

  • Состояние теряется при перезагрузке сервиса wb-rules.service или контроллера.
  • Экземпляры хранилищ кэшируются глобально и не удаляются автоматически. При динамическом создании большого количества экземпляров с уникальными scope может возникнуть утечка памяти. Рекомендуется использовать предсказуемые, ограниченные значения scope.