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-server

v0.3.0

Published

Turnkey HTTP server for @maykonpaulo/maestro-core — mounts the framework-agnostic Maestro HTTP handlers on a real Express server and turns any live datasource provider into a full, governed admin over the whole database (ADR 0008). The core stays server-f

Readme

@maykonpaulo/maestro-server

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-server

The turnkey HTTP server for @maykonpaulo/maestro-core, per ADR 0008 — Camada Turnkey.

The core is deliberately server-free and framework-agnostic: it exposes createMaestroHttpHandlers (handlers you mount on your Express/Fastify/Lambda), never an actual server. This sibling package is the batteries-included layer that boots a real HTTP server, and — combined with a live datasource provider — turns any database into a complete, governed admin over every collection, without writing a line of per-entity code.

It adds only what a server needs and the core must not carry: JSON body parsing, CORS, route wiring and a listen()/close() lifecycle. All domain behaviour (RBAC, audit, governance, CRUD) still comes from the engine.

Installation

npm install @maykonpaulo/maestro-server @maykonpaulo/maestro-core

@maykonpaulo/maestro-core is a peer dependency. express is a direct dependency of this package.

Point at a database, manipulate everything (introspection mode)

The fastest path: hand it a live provider (e.g. @maykonpaulo/maestro-provider-mongodb) and get a full admin over every discovered collection — no declarations required.

import { createMongoProvider } from '@maykonpaulo/maestro-provider-mongodb';
import { createMaestroServer, createIntrospectedEngine } from '@maykonpaulo/maestro-server';

const provider = createMongoProvider({ uri: process.env.MONGO_URL!, database: 'app' });

// Introspects the live DB and builds an engine over every collection, with write access enabled.
const engine = await createIntrospectedEngine({ provider, access: 'full' });

const server = createMaestroServer({ engine });
await server.listen(3000);
// → REST admin for every collection at http://localhost:3000/entities/:collection

access: 'readonly' enables only list/detail/export — a safe way to browse a production database without any risk of mutating it. access: 'full' (default) enables the complete CRUD surface.

Entities discovered by introspection are read-only under the core's createMaestroFromIntrospection; createIntrospectedEngine is the piece that enables write capabilities according to access, so "manipulate everything" works out of the box.

Curated mode (declarations)

When you want nice labels, fine-grained RBAC and governance on chosen entities, build the engine yourself with createMaestro and just serve it:

import { createMaestro } from '@maykonpaulo/maestro-core';
import { createMaestroServer } from '@maykonpaulo/maestro-server';

const engine = createMaestro({ datasources: { main: provider }, declarations, policies, audit });
const server = createMaestroServer({ engine, actorResolver });
await server.listen(3000);

Both modes can be combined: pass an overrides (curated) config to createIntrospectedEngine and the core HybridLoader merges it over the introspected schema.

Authentication is yours (the actorResolver hook)

Authentication stays the consumer's responsibility. createMaestroServer takes an actorResolver that derives the acting Actor from each request (a JWT, a cookie, a session):

const server = createMaestroServer({
  engine,
  actorResolver: async (req) => {
    const user = await verifyJwt(req.headers['authorization']);
    return { actor: { id: user.id, type: 'user', roles: user.roles } };
  },
});

When omitted, a development-only devActorResolver grants every request a single admin actor. Never use it in production.

API

createMaestroServer(options): MaestroServer

| Option | Description | |---|---| | engine | The MaestroEngine to serve (from createMaestro or createIntrospectedEngine). | | actorResolver? | Resolves the Actor per request. Defaults to devActorResolver (dev only). | | cors? | true/omitted → permissive defaults; an object → configured (origin, credentials, allowedHeaders); false → no CORS middleware. | | basePath? | Path prefix for every route (e.g. /api/admin). | | app? | Bring your own Express app (to add auth middleware/other routes). When given, JSON/CORS are not auto-applied. |

Returns { app, handlers, listen(port, host?) }. listen resolves with the Node http.Server once bound.

createIntrospectedEngine(options): Promise<MaestroEngine>

| Option | Description | |---|---| | provider | A provider implementing both IntrospectionProvider and DatasourceProvider. | | access? | 'full' (default) enables all capabilities; 'readonly' enables list/detail/export only. | | datasourceId? | Datasource id the entities bind to. Defaults to main. | | overrides? | Optional curated overlay merged via the core HybridLoader. | | policies? | RBAC policy. Defaults to an admin role with ['*']. | | operations?, audit?, logger? | Forwarded to the engine. |

Routes

GET    /health
GET    /metadata
GET    /metadata/:entity
GET    /entities/:entity            (list — filter/sort/paginate/search via query string)
POST   /entities/:entity            (create)
GET    /entities/:entity/:id        (detail)
PATCH  /entities/:entity/:id        (update)
DELETE /entities/:entity/:id        (hard delete)
POST   /entities/:entity/:id/clone
POST   /entities/:entity/:id/soft-delete
POST   /entities/:entity/:id/restore
GET    /entities/:entity/export     (?format=csv|json)
POST   /operations/:operationId

What this package is not

It bundles no database driver — it depends on a DatasourceProvider you instantiate (the driver comes with the provider package). It does not handle authentication or identity — that is the actorResolver's job. Those boundaries are intentional and preserve the core's purity (ADR 0008).