@maykonpaulo/maestro-core
v0.7.3
Published
Este guia leva um programador do zero a um **painel administrativo governado** (listar, filtrar, criar, editar, excluir, exportar, com RBAC e auditoria) sobre **o banco de dados de qualquer sistema** — sem escrever CRUD por entidade.
Readme
@maykonpaulo/maestro-core
Este guia leva um programador do zero a um painel administrativo governado (listar, filtrar, criar, editar, excluir, exportar, com RBAC e auditoria) sobre o banco de dados de qualquer sistema — sem escrever CRUD por entidade.
O que é o Maestro? Uma engine metadata-driven em TypeScript que você conecta a um banco existente para operá-lo com segurança, sem acesso direto ao banco. Você aponta para o banco, o Maestro descobre o schema (ou você declara), e ele entrega uma API REST + uma UI de admin genérica que se auto-monta. É agnóstico de framework, de banco e de domínio.
Índice
- Pré-requisitos
- Escolha seu banco (providers)
- Instalação
- Caminho A — Turnkey (recomendado)
- Caminho B — Declarativo/curado
- Autenticação (
actorResolver) - Autorização (RBAC)
- Auditoria
- Operações customizadas (ações de negócio)
- Campos: tipos, sensíveis, soft delete, relações
- A UI de admin (
maestro-admin) - A API HTTP (para frontends próprios)
- Referência rápida
- Produção: checklist e ressalvas
- Solução de problemas
- Pacotes e versões
1. Pré-requisitos
- Node.js 18+ e um gerenciador de pacotes (
npm,pnpmouyarn). - Acesso ao banco de dados do sistema que você quer administrar (string de conexão).
- Um mecanismo de autenticação seu (JWT, cookie de sessão, etc.) — o Maestro não faz auth; ele consome o "ator" que você já autenticou.
2. Escolha seu banco (providers)
O Maestro fala com o banco através de um provider. Existem 9 pacotes oficiais publicados — cada um implementa leitura de schema (introspecção), CRUD real e criação de coleção/tabela:
| Banco | Pacote |
|---|---|
| PostgreSQL · MySQL · SQLite · SQL Server | @maykonpaulo/maestro-provider-sql |
| MongoDB | @maykonpaulo/maestro-provider-mongodb |
| Redis | @maykonpaulo/maestro-provider-redis |
| Elasticsearch | @maykonpaulo/maestro-provider-elasticsearch |
| DynamoDB | @maykonpaulo/maestro-provider-dynamodb |
| Cassandra | @maykonpaulo/maestro-provider-cassandra |
| Couchbase | @maykonpaulo/maestro-provider-couchbase |
| Firestore | @maykonpaulo/maestro-provider-firestore |
| Neo4j | @maykonpaulo/maestro-provider-neo4j |
Banco fora dessa lista? Você pode implementar a interface
DatasourceProvider(6 métodos) — ver seção Solução de problemas. Mas para qualquer um acima, é só instalar.
3. Instalação
Instale a engine, o provider do seu banco, o servidor turnkey e a UI:
# engine + servidor + UI
npm i @maykonpaulo/maestro-core @maykonpaulo/maestro-server @maykonpaulo/maestro-admin
# provider do seu banco (exemplo: MongoDB)
npm i @maykonpaulo/maestro-provider-mongodbTroque o provider pelo do seu banco (ex.: @maykonpaulo/maestro-provider-sql).
4. Caminho A — Turnkey (recomendado)
Aponte para o banco e tenha um admin completo, sem declarar entidade nenhuma. O Maestro introspecta o banco, transforma cada coleção/tabela em uma entidade administrável e sobe um servidor REST. É o caminho mais rápido.
4.1 Conectar o provider
Escolha a fábrica do seu banco (confira as opções exatas de conexão no README do provider):
// MongoDB
import { createMongoProvider } from '@maykonpaulo/maestro-provider-mongodb';
const provider = createMongoProvider({ uri: process.env.MONGO_URL!, database: 'app' });
// PostgreSQL (mesma família: mysql/sqlite/mssql via `dialect`)
import { createSqlProvider } from '@maykonpaulo/maestro-provider-sql';
const provider = createSqlProvider({ dialect: 'postgres', connectionString: process.env.PG_URL! });
// sqlite: createSqlProvider({ dialect: 'sqlite', databasePath: './app.db' })
// mysql: createSqlProvider({ dialect: 'mysql', uri: process.env.MYSQL_URL! })
// Outros
import { createRedisProvider } from '@maykonpaulo/maestro-provider-redis';
const provider = createRedisProvider({ uri: 'redis://localhost:6379' });
// createElasticsearchProvider({ node, auth })
// createDynamoDbProvider({ region })
// createCassandraProvider({ contactPoints, localDataCenter, keyspace })
// createCouchbaseProvider({ connectionString, username, password, bucket })
// createFirestoreProvider({ projectId })
// createNeo4jProvider({ uri, username, password })4.2 Construir o engine por introspecção
import { createIntrospectedEngine } from '@maykonpaulo/maestro-server';
const engine = await createIntrospectedEngine({
provider,
access: 'full', // 'full' = list/detalhe/criar/editar/excluir/exportar
// 'readonly' = só navegar (sem risco de alterar)
});createIntrospectedEngine descobre toda coleção/tabela e habilita as capacidades de escrita
(o padrão do core seria só-leitura — este helper é o que liga o CRUD).
Opções:
| Opção | Descrição |
|---|---|
| provider | O provider conectado (obrigatório). |
| access | 'full' (padrão) ou 'readonly'. |
| datasourceId | id do datasource (padrão 'main'). |
| policies | Política RBAC (ver §7). Padrão: papel admin com ['*']. |
| overrides | Sobreposição curada (rótulos, campos, RBAC) mesclada sobre o schema descoberto. |
| operations | Operações customizadas (ver §9). |
| audit | Repositório de auditoria (ver §8). |
4.3 Subir o servidor
import { createMaestroServer } from '@maykonpaulo/maestro-server';
const server = createMaestroServer({ engine /*, actorResolver, cors, basePath */ });
await server.listen(3000);
// → REST admin para cada coleção em http://localhost:3000/entities/:colecao4.4 Autenticação
Sem actorResolver, o servidor usa um resolver só-dev que concede um ator admin a toda
requisição. Em produção, sempre passe o seu — ver §6.
4.5 Subir a UI
A UI genérica lê GET /metadata e se auto-monta (navegação, tabela, detalhe, formulários)
para todas as entidades. Rodando como app:
VITE_MAESTRO_API_URL=http://localhost:3000 \
npm --prefix node_modules/@maykonpaulo/maestro-admin run dev
# abra http://localhost:5173Ou embutida no seu app React — ver §11.
4.6 Servidor completo (copy-paste)
// server.ts
import { createMongoProvider } from '@maykonpaulo/maestro-provider-mongodb';
import { createMaestroServer, createIntrospectedEngine } from '@maykonpaulo/maestro-server';
import { InMemoryAuditRepository, type RbacPolicy } from '@maykonpaulo/maestro-core';
const provider = createMongoProvider({ uri: process.env.MONGO_URL!, database: 'app' });
const policies: RbacPolicy = {
roles: {
admin: { id: 'admin', name: 'Admin', permissions: ['*'] },
viewer: { id: 'viewer', name: 'Viewer', permissions: ['entity.*.list', 'entity.*.detail'] },
},
};
const engine = await createIntrospectedEngine({
provider,
access: 'full',
policies,
audit: new InMemoryAuditRepository(), // troque por seu repositório em produção (§8)
});
const server = createMaestroServer({
engine,
actorResolver: async (req) => {
// derive o ator da SUA autenticação (JWT/cookie)
const token = String(req.headers['authorization'] ?? '').replace('Bearer ', '');
const user = await verifyJwt(token); // sua função
return { actor: { id: user.sub, type: 'user', roles: user.roles } };
},
});
await server.listen(3000);
console.log('Admin API em http://localhost:3000');5. Caminho B — Declarativo/curado
Quando quiser rótulos bonitos, RBAC fino, tipos de campo explícitos e formulários controlados, declare as entidades. Você tem controle total; o Maestro monta o resto.
5.1 Declarar entidades
import { type DeclarativeConfig } from '@maykonpaulo/maestro-core';
const declarations: DeclarativeConfig = {
entities: [
{
entity: 'order', // = nome da tabela/coleção
label: 'Pedido',
pluralLabel: 'Pedidos',
fields: {
id: { type: 'string', primary: true },
code: { type: 'string', label: 'Código', required: true, searchable: true, sortable: true },
status: { type: 'enum', label: 'Status',
enumOptions: [
{ value: 'open', label: 'Aberto' },
{ value: 'paid', label: 'Pago' },
{ value: 'canceled', label: 'Cancelado' },
] },
total: { type: 'currency', label: 'Total', sortable: true },
paid: { type: 'boolean', label: 'Pago' },
createdAt: { type: 'datetime', label: 'Criado em', sortable: true },
notes: { type: 'text', label: 'Observações' },
},
// capacidades: liste o que este admin pode fazer nesta entidade
capabilities: { list: true, detail: true, create: true, update: true, delete: true, export: true },
},
],
};5.2 createMaestro + servir
import { createMaestro } from '@maykonpaulo/maestro-core';
import { createMaestroServer } from '@maykonpaulo/maestro-server';
const engine = createMaestro({
datasources: { main: provider }, // o provider do seu banco (§4.1)
declarations,
policies, // RBAC (§7)
audit: myAuditRepository, // (§8)
operations: [cancelOrder], // ações customizadas (§9)
});
await createMaestroServer({ engine, actorResolver }).listen(3000);5.3 A vs B (e híbrido)
- A (introspecção): o mais rápido; toda coleção vira entidade; schema inferido.
- B (declarativo): controle total; ideal para as entidades que os operadores mais usam.
- Híbrido: passe
overrides(config declarativa) paracreateIntrospectedEngine— o Maestro descobre tudo e aplica seus rótulos/regras por cima nas entidades que você curou.
6. Autenticação (actorResolver)
Autenticação é responsabilidade sua. O único ponto onde ela entra no Maestro é o
actorResolver, que converte cada requisição em um Actor:
const server = createMaestroServer({
engine,
actorResolver: async (req) => {
const token = String(req.headers['authorization'] ?? '').replace('Bearer ', '');
const user = await verifyJwt(token); // SUA autenticação
return {
actor: {
id: user.sub,
type: 'user', // 'user' | 'system' | 'job' | 'integration' | 'ai-agent'
name: user.name,
email: user.email,
roles: user.roles, // dão as permissões (§7)
},
correlationId: String(req.headers['x-correlation-id'] ?? ''), // opcional (rastreio na auditoria)
};
},
});- Omitir
actorResolver= rodar sem autenticação (o defaultdevActorResolverconcede admin a todos). Nunca faça isso em produção. - O
Actoré passado a toda operação; o RBAC decide o que ele pode fazer.
7. Autorização (RBAC)
Você define papéis e as permissões de cada um. O ator recebe permissões pelos seus roles.
import { type RbacPolicy } from '@maykonpaulo/maestro-core';
const policies: RbacPolicy = {
roles: {
admin: { id: 'admin', name: 'Admin', permissions: ['*'] },
operator: { id: 'operator', name: 'Operador', permissions: ['entity.order.*', 'operation.order.cancel'] },
viewer: { id: 'viewer', name: 'Leitura', permissions: ['entity.*.list', 'entity.*.detail', 'entity.*.export'] },
},
};Formato das permissões:
| Permissão | Concede |
|---|---|
| entity.<entidade>.<capacidade> | Uma ação em uma entidade. Capacidades: list, detail, create, update, clone, delete, softDelete, restore, export. |
| operation.<opId> | Executar uma operação customizada (§9). |
| entity.order.* | Tudo na entidade order. |
| entity.*.list | list em todas as entidades. |
| * | Tudo. |
Curinga: * (tudo) e prefixo.* (tudo abaixo do prefixo). Um 403 é retornado quando negado — a
UI trata isso como erro na tela, sem quebrar.
Importante: as capacidades (o que a entidade permite) e o RBAC (o que o ator pode) são checados juntos. No modo introspecção
access: 'full', todas as capacidades ficam ligadas; o RBAC é quem restringe por papel.
8. Auditoria
Toda operação emite um evento de auditoria quando você fornece um AuditRepository. Para começar,
use o InMemoryAuditRepository; em produção, implemente o contrato gravando no seu store:
import { type AuditRepository, type AuditEvent } from '@maykonpaulo/maestro-core';
class MyAuditRepository implements AuditRepository {
async record(event: AuditEvent): Promise<void> {
await db.collection('audit').insertOne(event); // grave onde quiser
}
async list(filter) { /* consulte por ator/ação/entidade/período/correlationId */ return []; }
}Cada evento inclui ação, ator, recurso, nível, before/after (em update/delete) e correlationId.
9. Operações customizadas (ações de negócio)
Além do CRUD, exponha ações (ex.: "cancelar pedido", "reenviar e-mail"). Elas aparecem como botões (por linha / em massa / globais) e executam sua lógica com RBAC + auditoria:
import { type OperationDef } from '@maykonpaulo/maestro-core';
const cancelOrder: OperationDef = {
id: 'order.cancel',
label: 'Cancelar pedido',
entity: 'order',
scope: 'record', // 'record' | 'bulk' | 'global'
requiredPermission: 'operation.order.cancel',
execute: async ({ record, input, actor }) => {
// sua lógica de negócio aqui
await paymentGateway.refund(record.id);
return { success: true, message: 'Pedido cancelado.' };
},
};
// registre no engine:
createMaestro({ /* ... */ operations: [cancelOrder] });
// ou createIntrospectedEngine({ /* ... */ operations: [cancelOrder] });Chamada HTTP: POST /operations/order.cancel com { entityId, record, input }.
10. Campos: tipos, sensíveis, soft delete, relações
Tipos de campo (type): string, text, number, integer, decimal, currency,
boolean, date, datetime, time, email, phone, url, document, uuid, enum,
json, relation, array. A UI escolhe o controle certo por tipo (checkbox, número, select,
data, textarea/JSON…).
Flags úteis por campo (no modo declarativo): required, readonly, primary, sensitive,
searchable, sortable, filterable, exportable, enumOptions (para enum), relationEntity
(para relation).
sensitive: true— mascara o valor (***) na exportação CSV e na UI. (Logging/before/aftercontinuam responsabilidade sua.)- Soft delete — configure um campo que marca "inativo" (ex.:
active/deletedAt). As operaçõessoftDelete/restorepassam a existir em vez de exclusão física. - Relações — o Maestro não infere FK em bancos NoSQL; declare relações à mão quando quiser abas de relacionamento no detalhe.
- Campos cifrados (E2EE / zero-knowledge) — se o seu backend cifra campos, eles chegam opacos; a UI os exibe como estão e nunca descriptografa. Comece o admin pelas coleções não cifradas.
11. A UI de admin (maestro-admin)
A UI (React + Vite + Tailwind) lê GET /metadata e monta sozinha: navegação por entidade, tabela
(com busca/ordenação/paginação/filtros vindos do metadata), detalhe, e formulários de criar/editar.
Ela respeita as capacidades (esconde New/Edit/Delete quando desabilitados) e o RBAC (403 vira
erro na tela). Uma tela, todas as entidades.
App standalone
VITE_MAESTRO_API_URL=http://localhost:3000 \
npm --prefix node_modules/@maykonpaulo/maestro-admin run dev # dev server
# ou build estático:
VITE_MAESTRO_API_URL=http://localhost:3000 \
npm --prefix node_modules/@maykonpaulo/maestro-admin run build:appVITE_MAESTRO_ROLE=viewer (ou ?role=viewer na URL) define o header X-Role — útil para testar RBAC.
Embutida no seu app React
import { MaestroAdmin } from '@maykonpaulo/maestro-admin';
import '@maykonpaulo/maestro-admin/src/styles.css';
export function AdminPage() {
return (
<MaestroAdmin
apiUrl="http://localhost:3000"
headers={() => ({ Authorization: `Bearer ${getToken()}` })} // pode ser função (token fresco a cada request)
title="Admin"
/>
);
}Também exportamos os blocos (EntityList, EntityDetail, EntityForm, Sidebar, useMetadata,
useEntityList, MaestroClient, …) para montar um layout próprio.
12. A API HTTP (para frontends próprios)
Se você quiser construir sua própria UI (ou integrar outro sistema), o servidor expõe:
GET /health
GET /metadata # descreve todas as entidades (campos, tipos, capacidades)
GET /metadata/:entidade
GET /entities/:entidade # list — query: page, pageSize, search, searchFields, sort, filter
POST /entities/:entidade # criar
GET /entities/:entidade/:id # detalhe
PATCH /entities/:entidade/:id # editar
DELETE /entities/:entidade/:id # excluir (hard delete)
POST /entities/:entidade/:id/clone
POST /entities/:entidade/:id/soft-delete
POST /entities/:entidade/:id/restore
GET /entities/:entidade/export # ?format=csv|json
POST /operations/:operationId # { entityId, record, input }Query string do list:
| Param | Exemplo | Significado |
|---|---|---|
| page, pageSize | ?page=1&pageSize=20 | Paginação. |
| search, searchFields | ?search=ada&searchFields=name,email | Busca textual. |
| sort | ?sort=name:asc (repetível) | Ordenação. |
| filter | ?filter=status:equals:open (repetível) | Filtro campo:operador:valor. |
Todo endpoint checa RBAC e emite auditoria. O GET /metadata é o contrato para gerar qualquer UI.
13. Referência rápida
Operadores de filtro: equals, notEquals, contains, startsWith, endsWith, in,
notIn, gt, gte, lt, lte, between, isNull, isNotNull, isTrue, isFalse.
Capacidades de entidade: list, detail, create, update, clone, delete, softDelete,
export, bulkActions. Padrão do core: list/detail ligadas, o resto desligado (por segurança).
No modo createIntrospectedEngine({ access: 'full' }), todas ligadas.
Métodos da engine (se você usar o core direto, sem o servidor): list, findById, create,
update, clone, softDelete, restore, hardDelete, export, executeOperation, getMetadata
— todos recebem o actor e checam RBAC + auditoria.
Actor: { id, type: 'user'|'system'|'job'|'integration'|'ai-agent', name?, email?, roles?, permissions?, metadata? }.
14. Produção: checklist e ressalvas
- [ ] Sempre forneça um
actorResolverreal (JWT/cookie). Sem ele = sem auth. - [ ] Defina papéis RBAC de verdade (não deixe todo mundo
admin/['*']). - [ ] Comece com
access: 'readonly'para validar a leitura do banco de produção sem risco; suba para'full'quando confiar. - [ ] Implemente um
AuditRepositoryque persista (não o InMemory). - [ ] Configure CORS conforme seu frontend (
createMaestroServer({ cors: { origin, credentials } })). - [ ] Comece o admin pelas coleções operacionais não cifradas (usuários, configs, etc.). Campos E2EE aparecem opacos.
- [ ] Rode o servidor atrás do seu gateway/HTTPS; o Maestro é só a camada de operação.
15. Solução de problemas
- "Nome da coleção não bate" (MongoDB/Mongoose). No modo introspecção, cada entidade é a coleção
real (ex.:
users). No modo declarativo, oentityda declaração é o nome da coleção — o Mongoose costuma pluralizar (User→users); declare o nome real, ou use oEntitySchemade baixo nível (source.table) para mapear um id amigável a uma coleção diferente. _id(MongoDB). O provider trata_idcomoObjectIdnativo ou string de forma transparente — funciona sobre coleções existentes sem migração.- Campos
currency/decimalaparecem como texto? Use a versão mais recente domaestro-admin. - Banco não coberto pelos 9 providers. Implemente
DatasourceProvider(6 métodos:list,findById,create,update,delete,count) e passe emdatasources: { main: seuProvider }. - Publicou no
@latestpor engano? Auth e canais de release não fazem parte deste guia — o admin em si só precisa dos pacotes instalados e do servidor no ar.
16. Pacotes e versões
Instale pela tag latest (estável):
npm i @maykonpaulo/maestro-core \
@maykonpaulo/maestro-server \
@maykonpaulo/maestro-admin \
@maykonpaulo/maestro-provider-<seu-banco>| Pacote | O que é |
|---|---|
| @maykonpaulo/maestro-core | Engine, contratos, metadata, RBAC, auditoria, operações, export. |
| @maykonpaulo/maestro-server | Servidor HTTP turnkey (createMaestroServer, createIntrospectedEngine). |
| @maykonpaulo/maestro-admin | UI genérica metadata-driven (app + componentes React). |
| @maykonpaulo/maestro-provider-* | Provider do seu banco (9 opções). |
| @maykonpaulo/maestro-cli | CLI opcional (introspect/generate/validate/diff/snapshot). |
Resumo em 6 linhas
const provider = createMongoProvider({ uri, database }); // 1. conecte o banco
const engine = await createIntrospectedEngine({ provider, access: 'full', policies, audit }); // 2. engine
const server = createMaestroServer({ engine, actorResolver }); // 3. auth + servidor
await server.listen(3000); // 4. API no ar
// 5. rode @maykonpaulo/maestro-admin apontando VITE_MAESTRO_API_URL para :3000
// 6. pronto: admin de todo o sistema, com RBAC e auditoriaReferência do pacote @maykonpaulo/maestro-core
Central package of the Maestro engine. Provides the complete runtime for operating any system's data without direct database access.
Installation
pnpm add @maykonpaulo/maestro-coreQuick start
import {
createMaestro,
InMemoryDatasourceProvider,
InMemoryAuditRepository,
type EntitySchema,
type RbacPolicy,
type Actor,
} from '@maykonpaulo/maestro-core';
// 1. Implement or use a datasource provider
const datasource = new InMemoryDatasourceProvider();
datasource.seed('users', [
{ id: 'u1', name: 'Alice', email: '[email protected]', active: true },
]);
// 2. Define entities
const usersEntity: EntitySchema = {
id: 'users',
label: { singular: 'User', plural: 'Users' },
source: { datasource: 'main', table: 'users', primaryKey: 'id' },
capabilities: { list: true, detail: true, create: true, update: true, export: true },
fields: [
{ name: 'id', label: 'ID', type: 'uuid', readonly: true },
{ name: 'name', label: 'Name', type: 'string', required: true, searchable: true },
{ name: 'email', label: 'Email', type: 'email', required: true },
{ name: 'active', label: 'Active',type: 'boolean', filterable: true },
],
};
// 3. Configure RBAC
const policies: RbacPolicy = {
roles: {
admin: { id: 'admin', name: 'Admin', permissions: ['*'] },
viewer: { id: 'viewer', name: 'Viewer', permissions: ['entity.users.list', 'entity.users.detail'] },
},
};
// 4. Create the engine
const engine = createMaestro({
datasources: { main: datasource },
entities: [usersEntity],
policies,
audit: new InMemoryAuditRepository(),
});
// 5. Use the engine
const actor: Actor = { id: 'admin-1', type: 'user', roles: ['admin'] };
const result = await engine.list('users', {
filters: [{ field: 'active', operator: 'isTrue' }],
sort: [{ field: 'name', direction: 'asc' }],
pagination: { strategy: 'page', page: 1, pageSize: 20 },
}, actor);
const user = await engine.findById('users', 'u1', actor);
const created = await engine.create('users', { name: 'Bob', email: '[email protected]', active: true }, actor);
const exported = await engine.export('users', {}, actor, 'csv');Module reference
| Module | Exports |
|---|---|
| Engine | createMaestro, MaestroEngine, MetadataEngine, createMaestroFromIntrospection |
| Schema | EntitySchema, FieldSchema, RelationSchema, MaestroConfig, validateMaestroConfig |
| Config Loader | loadMaestroConfig, YamlParser, FileSystemReader, LoadedConfig |
| HTTP | createMaestroHttpHandlers, parseQueryInput, MaestroHttpHandlers, MaestroActorResolver, MaestroHttpOptions |
| Introspection | IntrospectionProvider, SchemaWriteProvider, runIntrospectionProvider, validateIntrospectionResult, IntrospectionResult |
| Declarative | EntityDeclaration, compileDeclarations, DeclarativeConfig, loadDeclarativeConfigFromFile, resolveConsumerProjections |
| Governance | ContextualAuthorizationEngine, PolicyEngine, ConfirmationEngine, DefaultGovernanceApi, GovernanceApi |
| Metadata | EntityMetadata, FieldMetadata, RelationMetadata, MaestroMetadata, OperationMetadata, EntityCapabilities, DEFAULT_CAPABILITIES |
| Query | QueryInput, FilterDescriptor, FilterOperator, SortDescriptor, PaginationInput, SearchInput, ListResult |
| Datasource | DatasourceProvider, DatasourceRegistry, InMemoryDatasourceProvider |
| Operation | OperationDef, OperationContext, OperationResult, OperationRegistry |
| Export | ExportProvider, CsvExportProvider, ExportFormat, ExportResult, ExportOptions |
| RBAC | RbacEngine, RbacPolicy, Role, Permission |
| Audit | AuditRecorder, AuditRepository, AuditEvent, InMemoryAuditRepository |
| Logging | Logger, ConsoleLogger, LogLevel |
| Events | DomainEvent, EventBus, InMemoryEventBus |
| Feature Flags | FeatureFlagProvider, InMemoryFeatureFlagProvider |
| Config | ConfigProvider, InMemoryConfigProvider |
| Errors | MaestroError, ErrorCode |
| Types | Actor, Metadata, ResourceRef |
Filter operators
equals, notEquals, contains, startsWith, endsWith, in, notIn, gt, gte, lt, lte, between, isNull, isNotNull, isTrue, isFalse
Permission patterns
| Pattern | Matches |
|---|---|
| * | Everything |
| entity.users.list | Exact permission |
| entity.users.* | All operations on users entity |
| entity.*.list | List on all entities |
Design constraints
The core has zero runtime dependencies on:
- Any web framework (Express, Nest, Fastify...)
- Any ORM (Prisma, TypeORM, Sequelize...)
- Any database (PostgreSQL, MySQL, MongoDB...)
- React or any UI library
You provide a DatasourceProvider implementation. Maestro handles everything else. In practice you
usually don't write one by hand — you install a published @maykonpaulo/maestro-provider-* package
(see Ecosystem below), which ships a real DatasourceProvider (and IntrospectionProvider)
for your database.
Ecosystem
@maykonpaulo/maestro-core is the framework-free engine. Published sibling packages build on top of
it so you don't have to wire everything yourself:
| Package | Purpose |
|---|---|
| @maykonpaulo/maestro-server | Turnkey HTTP server over the core's handlers — createMaestroServer, createIntrospectedEngine. |
| @maykonpaulo/maestro-admin | Generic React admin UI that reads GET /metadata and self-assembles. |
| @maykonpaulo/maestro-provider-sql | Real DatasourceProvider + IntrospectionProvider for SQLite, PostgreSQL, MySQL and SQL Server (dialect-selected). |
| @maykonpaulo/maestro-provider-mongodb, -redis, -elasticsearch, -dynamodb, -cassandra, -couchbase, -firestore, -neo4j | Real providers for each datastore. |
To just point at your database and get a governed admin with no per-entity code, take the turnkey
path (maestro-provider-* + maestro-server + maestro-admin) — see the
turnkey admin guide.
