@ministerjs/store
v2.0.4
Published
Gerenciamento de estado opinado para Vue 3 + Pinia com foco em simplicidade, tipagem forte e reutilização. Fornece factories para **ItemStore** (estado de um único objeto) e **TableStore** (coleções) além de um gerenciador com **cache inteligente** (`useS
Readme
@ministerjs/store
Gerenciamento de estado opinado para Vue 3 + Pinia com foco em simplicidade, tipagem forte e reutilização. Fornece factories para ItemStore (estado de um único objeto) e TableStore (coleções) além de um gerenciador com cache inteligente (useStores).
📦 Instalação
npm install @ministerjs/store
# ou
pnpm add @ministerjs/store
# ou
yarn add @ministerjs/store🚀 Funcionalidades
- ItemStore: Estado de um item único (perfil, configurações, sessão...)
- TableStore: Coleções (listas, tabelas, catálogos) com utilidades frequentes
- Cache inteligente via
useStorespara evitar recriações - Tipagem 100% (inferência de chaves, primary key, partial updates)
- Pinia under the hood: integra DevTools sem esforço
- Debug logging opcional (
debug: true) - APIs mínimas e coesas: apenas o que é realmente usado no dia a dia
📖 Uso Básico
ItemStore – Gerenciando um item único
import { createItemStore } from "@ministerjs/store";
interface UserProfile {
id: number;
name: string;
email: string;
avatar?: string;
}
// Criando o store
const useUserProfile = createItemStore<UserProfile>({
displayName: "User Profile",
initialState: { name: "", email: "" },
debug: true,
});
// Usando no componente Vue
const { get, set, update, clear } = useUserProfile();
// Estado reativo
const profile = get();
console.log(profile.value.name); // Acesso reativo
// Definindo dados completos
set({ id: 1, name: "João", email: "[email protected]" });
// Atualizando parcialmente
update({ name: "João Silva" });
// Limpando o estado
clear();TableStore – Gerenciando coleções
import { createTableStore } from "@ministerjs/store";
interface User {
id: number;
name: string;
email: string;
active: boolean;
}
// Criando o store
const useUsersStore = createTableStore<User, "id">({
primaryKey: "id",
displayName: "Users Store",
initialState: [],
debug: true,
});
// Usando no componente Vue
const store = useUsersStore();
// Adicionando itens
store.add({ id: 1, name: "João", email: "[email protected]", active: true });
store.add({ id: 2, name: "Maria", email: "[email protected]", active: false });
// Buscando por ID
const user = store.get(1);
// Listando todos (reativo)
const allUsers = store.list();
// Busca por propriedade personalizada
const activeUser = store.getByProperty("email", "[email protected]");
// Atualizando
store.update(1, { name: "João Silva" });
// Removendo
store.remove(1);
// Substituindo toda a coleção
store.replaceAll([
{ id: 3, name: "Pedro", email: "[email protected]", active: true },
]);
// Lista computada com transformação
const activeUsers = store.computedList((users) =>
users.value.filter((u) => u.active),
);
// Ordenação
const sortedUsers = store.sort((a, b) => a.name.localeCompare(b.name));🏗️ Gerenciamento Avançado com useStores
O composable useStores centraliza criação e cache de múltiplos stores. Ele exige que você informe as factories padrão (createItemStore, createTableStore) uma única vez.
1. Uso Dinâmico (on-demand)
import {
useStores,
createItemStore,
createTableStore,
} from "@ministerjs/store";
// Passamos `undefined` para o primeiro argumento (não há pré-configuração)
const { generateItem, generateTable, getCacheSize } = useStores(undefined, {
createItemStore,
createTableStore,
});
console.log(getCacheSize()); // { items: 0, tables: 0 }
// Cria e cacheia um ItemStore
const userProfile = generateItem<UserProfile>("profile", {
debug: true,
initialState: { name: "", email: "" },
});
// Cria e cacheia um TableStore (primaryKey padrão = "id")
const usersTable = generateTable<User>("users", {
primaryKey: "id", // opcional; se omitido usa 'id'
debug: true,
});
// Reutiliza do cache
const profileAgain = generateItem("profile"); // Missão cumprida: cache hit2. Stores Pré-configurados
import {
useStores,
createItemStore,
createTableStore,
} from "@ministerjs/store";
// Definições lazy (cada função só roda uma vez)
const stores = useStores(
{
users: () =>
createTableStore<User>({ primaryKey: "id", displayName: "Users" }),
profile: () =>
createItemStore<UserProfile>({ displayName: "Profile", debug: true }),
settings: () =>
createItemStore<AppSettings>({
displayName: "App Settings",
initialState: { theme: "light", language: "pt-BR" },
}),
},
{
createItemStore,
createTableStore,
},
);
const userStore = stores.get("users"); // primeira chamada instancia
const userStoreAgain = stores.get("users"); // cache hit
console.log(stores.getCacheSize()); // { items: 2, tables: 1 } (exemplo)3. Integração Rápida em uma App Vue
// main.ts
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
const app = createApp(App);
app.use(createPinia());
app.mount("#app");
// Em qualquer composable
import {
useStores,
createItemStore,
createTableStore,
} from "@ministerjs/store";
export const stores = useStores(undefined, {
createItemStore,
createTableStore,
});🔧 API Reference
ItemStore Interface
interface ItemStore<T> {
get(): Ref<Partial<T>>; // Estado reativo
set(item: Partial<T>): void; // Define dados completos
update(data: Partial<T>): void; // Atualização parcial
clear(): void; // Limpa o estado
}TableStore Interface
interface TableStore<T, PK> {
add(data: T): void; // Adiciona item
get(primaryKey: T[PK]): T | undefined; // Busca por ID
list(): T[]; // Lista todos os itens
computedList<R>(callback: (items: Ref<T[]>) => R): Ref<R>; // Lista computada
getByProperty<K>(key: K, value: T[K]): T | undefined; // Busca por propriedade
update(primaryKey: T[PK], data: Partial<T>): void; // Atualiza item
remove(primaryKey: T[PK]): boolean; // Remove item
replaceAll(data: T[]): void; // Substitui todos
sort(callback: (a: T, b: T) => number): T[]; // Ordena itens
}Opções de Configuração
interface CreateItemStoreOptions<T> {
displayName?: string; // Nome para DevTools
initialState?: T; // Estado inicial
debug?: boolean; // Logs de debug
}
interface CreateTableStoreOptions<T, PK> {
primaryKey?: PK; // Chave primária (default: 'id')
displayName?: string; // Nome para DevTools
initialState?: T[]; // Estado inicial
debug?: boolean; // Logs de debug
}
### useStores
```typescript
// Sem stores pré-configurados
useStores(undefined, {
createItemStore,
createTableStore,
});
// Com stores pré-configurados
useStores({
users: () => createTableStore<User>({ primaryKey: 'id' }),
profile: () => createItemStore<UserProfile>({}),
}, {
createItemStore,
createTableStore,
});Métodos retornados:
generateItem(key, options?) // Cria ou retorna ItemStore cacheado
generateTable(key, options?) // Cria ou retorna TableStore cacheado
get(key) // Quando configurado com objeto de setups
getCacheSize() // { items, tables }
## 🏆 Boas Práticas
### 1. Organização de Stores
```typescript
// stores/user.ts
export const useUserProfile = createItemStore<UserProfile>({
displayName: "User Profile",
});
export const useUsers = createTableStore<User, "id">({
primaryKey: "id",
displayName: "Users",
});
// stores/index.ts
export const stores = useStores({
userProfile: () => useUserProfile(),
users: () => useUsers(),
});2. Composables Especializados
// composables/useAuth.ts
export const useAuth = () => {
const profile = stores.get("userProfile");
const login = async (credentials: LoginData) => {
const user = await authService.login(credentials);
profile.set(user);
};
const logout = () => {
profile.clear();
};
return { login, logout, profile: profile.get() };
};3. Integração com APIs
// composables/useUsersApi.ts
export const useUsersApi = () => {
const store = stores.get("users");
const fetchUsers = async () => {
const users = await api.get<User[]>("/users");
store.replaceAll(users);
};
const createUser = async (userData: Omit<User, "id">) => {
const user = await api.post<User>("/users", userData);
store.add(user);
return user;
};
return { fetchUsers, createUser, store };
};🔗 Dependências & Peer Deps
Certifique-se de ter instalado (peer dependencies):
pnpm add vue piniaVersões sugeridas:
- Vue >= 3.5
- Pinia >= 2.0
- TypeScript >= 5
O pacote não inicializa o Pinia automaticamente; você ainda precisa chamar app.use(createPinia()) em sua aplicação.
🧪 Testes
Dentro do monorepo:
pnpm test --filter @ministerjs/store⚠️ Notas & Gotchas
primaryKeyemcreateTableStoreé opcional (default"id").generateItemegenerateTablenão recebem a factory como argumento (a factory é injetada uma vez emuseStores).computedListrecebe um callback que recebe umRef<T[]>e retorna um valor derivado reativo.- Evite mutar diretamente objetos retornados por
get()/ itens da lista fora de ações controladas se quiser padronizar logs. - O cache vive no escopo onde
useStoresfoi chamado; para um cache global, exporte a instância.
📄 Licença
UNLICENSED – Uso interno do MinisterJS Framework.
🤝 Contribuindo
Este pacote faz parte do monorepo MinisterJS. Para contribuir:
- Clone o repositório principal
- Instale as dependências:
pnpm install - Execute os testes:
pnpm test - Faça suas alterações no pacote
packages/store/ - Execute o build:
pnpm build
Para mais informações, consulte o README principal do MinisterJS.
