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

@maykonpaulo/maestro-admin

v0.3.0

Published

Generic, metadata-driven admin UI for @maykonpaulo/maestro-core (via @maykonpaulo/maestro-server). Reads GET /metadata and auto-builds navigation, tables, detail views and create/update forms for every entity/collection — no per-entity code. Ships as a ru

Readme

@maykonpaulo/maestro-admin

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

  1. Pré-requisitos
  2. Escolha seu banco (providers)
  3. Instalação
  4. Caminho A — Turnkey (recomendado)
  5. Caminho B — Declarativo/curado
  6. Autenticação (actorResolver)
  7. Autorização (RBAC)
  8. Auditoria
  9. Operações customizadas (ações de negócio)
  10. Campos: tipos, sensíveis, soft delete, relações
  11. A UI de admin (maestro-admin)
  12. A API HTTP (para frontends próprios)
  13. Referência rápida
  14. Produção: checklist e ressalvas
  15. Solução de problemas
  16. Pacotes e versões

1. Pré-requisitos

  • Node.js 18+ e um gerenciador de pacotes (npm, pnpm ou yarn).
  • 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-mongodb

Troque 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/:colecao

4.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:5173

Ou 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) para createIntrospectedEngine — 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 default devActorResolver concede 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/after continuam responsabilidade sua.)
  • Soft delete — configure um campo que marca "inativo" (ex.: active/deletedAt). As operações softDelete/restore passam 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:app

VITE_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 actorResolver real (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 AuditRepository que 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, o entity da declaração é o nome da coleção — o Mongoose costuma pluralizar (Userusers); declare o nome real, ou use o EntitySchema de baixo nível (source.table) para mapear um id amigável a uma coleção diferente.
  • _id (MongoDB). O provider trata _id como ObjectId nativo ou string de forma transparente — funciona sobre coleções existentes sem migração.
  • Campos currency/decimal aparecem como texto? Use a versão mais recente do maestro-admin.
  • Banco não coberto pelos 9 providers. Implemente DatasourceProvider (6 métodos: list, findById, create, update, delete, count) e passe em datasources: { main: seuProvider }.
  • Publicou no @latest por 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 auditoria

Referência do pacote @maykonpaulo/maestro-admin

The generic, metadata-driven admin UI for Maestro, per ADR 0008 — Camada Turnkey.

Point it at a running @maykonpaulo/maestro-server and it reads GET /metadata and auto-builds the whole admin: a navigation sidebar, a list table, a detail view and create/update forms for every entity/collection — no per-entity code. It respects each entity's capabilities (a read-only collection shows no New/Edit/Delete) and the server's RBAC (a 403 surfaces as an error, never a broken screen).

Built with React 19 + Vite + Tailwind. It ships two ways:

  • a runnable static app (vite build) you configure with an env var, and
  • exported React components (import { MaestroAdmin } from '@maykonpaulo/maestro-admin') to embed in your own app.

It depends only on the Maestro HTTP contract — never on @maykonpaulo/maestro-core — so it stays fully decoupled from the engine.

Run it as a standalone admin

# point it at your maestro-server and start it
VITE_MAESTRO_API_URL=http://localhost:3000 pnpm --filter @maykonpaulo/maestro-admin dev
# or build the static bundle
VITE_MAESTRO_API_URL=http://localhost:3000 pnpm --filter @maykonpaulo/maestro-admin build:app

VITE_MAESTRO_API_URL is the base URL of the server (defaults to same-origin). VITE_MAESTRO_ROLE optionally sets an X-Role header — handy against a dev server's actorResolver.

Embed the component

import { MaestroAdmin } from '@maykonpaulo/maestro-admin';
import '@maykonpaulo/maestro-admin/src/styles.css'; // or include the package in your Tailwind content

export function AdminPage() {
  return (
    <MaestroAdmin
      apiUrl="http://localhost:3000"
      headers={() => ({ Authorization: `Bearer ${getToken()}` })}
      title="Admin"
    />
  );
}

headers may be a function so a fresh auth token is read on every request. Pass a pre-built client (a MaestroClient) instead of apiUrl/headers for full control (custom fetch, SSR).

Styling

Tailwind v4. Either import the package's source stylesheet (@maykonpaulo/maestro-admin/src/styles.css) or, if you already run Tailwind, add the package to your content so its class names are scanned:

/* your app.css */
@import 'tailwindcss';
@source '../node_modules/@maykonpaulo/maestro-admin/dist';

Compose your own layout

Beyond the all-in-one <MaestroAdmin>, the building blocks are exported so you can assemble a custom shell:

  • Components: EntityList, EntityDetail, EntityForm, Sidebar, FieldValue, FieldInput
  • Hooks: useMetadata, useEntityList, useEntityRecord, useAsync, and listFields/detailFields/formFields
  • Client: MaestroClient, MaestroApiError, AdminClientProvider, useClient

All of them talk to the server through a single MaestroClient provided via AdminClientProvider.

What it renders from metadata

| Metadata | Drives | |---|---| | entities[] | the sidebar navigation | | field.list.visible / order | table columns | | field.detail.visible / order | the detail view | | field.form.creatable / editable / order | the create/update form fields | | field.type | the right table cell and form control (checkbox, number, select, date, json, …) | | field.enumOptions | select options | | field.sensitive | masked values | | capabilities | which of New / Edit / Delete appear |

Fields the server marks as encrypted (as in a zero-knowledge / E2EE system) come across as opaque values — the admin shows them as-is; it never decrypts.