@maykonpaulo/maestro-server 0.1.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +115 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +188 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# @maykonpaulo/maestro-server
|
|
2
|
+
|
|
3
|
+
The **turnkey HTTP server** for [`@maykonpaulo/maestro-core`](https://www.npmjs.com/package/@maykonpaulo/maestro-core), per [ADR 0008 — Camada Turnkey](../../docs/adr/0008-turnkey-server-and-admin-ui.md).
|
|
4
|
+
|
|
5
|
+
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**.
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @maykonpaulo/maestro-server @maykonpaulo/maestro-core
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`@maykonpaulo/maestro-core` is a peer dependency. `express` is a direct dependency of this package.
|
|
16
|
+
|
|
17
|
+
## Point at a database, manipulate everything (introspection mode)
|
|
18
|
+
|
|
19
|
+
The fastest path: hand it a live provider (e.g. [`@maykonpaulo/maestro-provider-mongodb`](../provider-mongodb)) and get a full admin over every discovered collection — no declarations required.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { createMongoProvider } from '@maykonpaulo/maestro-provider-mongodb';
|
|
23
|
+
import { createMaestroServer, createIntrospectedEngine } from '@maykonpaulo/maestro-server';
|
|
24
|
+
|
|
25
|
+
const provider = createMongoProvider({ uri: process.env.MONGO_URL!, database: 'app' });
|
|
26
|
+
|
|
27
|
+
// Introspects the live DB and builds an engine over every collection, with write access enabled.
|
|
28
|
+
const engine = await createIntrospectedEngine({ provider, access: 'full' });
|
|
29
|
+
|
|
30
|
+
const server = createMaestroServer({ engine });
|
|
31
|
+
await server.listen(3000);
|
|
32
|
+
// → REST admin for every collection at http://localhost:3000/entities/:collection
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`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.
|
|
36
|
+
|
|
37
|
+
> 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.
|
|
38
|
+
|
|
39
|
+
## Curated mode (declarations)
|
|
40
|
+
|
|
41
|
+
When you want nice labels, fine-grained RBAC and governance on chosen entities, build the engine yourself with `createMaestro` and just serve it:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { createMaestro } from '@maykonpaulo/maestro-core';
|
|
45
|
+
import { createMaestroServer } from '@maykonpaulo/maestro-server';
|
|
46
|
+
|
|
47
|
+
const engine = createMaestro({ datasources: { main: provider }, declarations, policies, audit });
|
|
48
|
+
const server = createMaestroServer({ engine, actorResolver });
|
|
49
|
+
await server.listen(3000);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Both modes can be combined: pass an `overrides` (curated) config to `createIntrospectedEngine` and the core `HybridLoader` merges it over the introspected schema.
|
|
53
|
+
|
|
54
|
+
## Authentication is yours (the `actorResolver` hook)
|
|
55
|
+
|
|
56
|
+
Authentication stays the consumer's responsibility. `createMaestroServer` takes an `actorResolver` that derives the acting `Actor` from each request (a JWT, a cookie, a session):
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const server = createMaestroServer({
|
|
60
|
+
engine,
|
|
61
|
+
actorResolver: async (req) => {
|
|
62
|
+
const user = await verifyJwt(req.headers['authorization']);
|
|
63
|
+
return { actor: { id: user.id, type: 'user', roles: user.roles } };
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
When omitted, a **development-only** `devActorResolver` grants every request a single admin actor. Never use it in production.
|
|
69
|
+
|
|
70
|
+
## API
|
|
71
|
+
|
|
72
|
+
### `createMaestroServer(options): MaestroServer`
|
|
73
|
+
|
|
74
|
+
| Option | Description |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `engine` | The `MaestroEngine` to serve (from `createMaestro` or `createIntrospectedEngine`). |
|
|
77
|
+
| `actorResolver?` | Resolves the `Actor` per request. Defaults to `devActorResolver` (dev only). |
|
|
78
|
+
| `cors?` | `true`/omitted → permissive defaults; an object → configured (`origin`, `credentials`, `allowedHeaders`); `false` → no CORS middleware. |
|
|
79
|
+
| `basePath?` | Path prefix for every route (e.g. `/api/admin`). |
|
|
80
|
+
| `app?` | Bring your own Express app (to add auth middleware/other routes). When given, JSON/CORS are not auto-applied. |
|
|
81
|
+
|
|
82
|
+
Returns `{ app, handlers, listen(port, host?) }`. `listen` resolves with the Node `http.Server` once bound.
|
|
83
|
+
|
|
84
|
+
### `createIntrospectedEngine(options): Promise<MaestroEngine>`
|
|
85
|
+
|
|
86
|
+
| Option | Description |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `provider` | A provider implementing both `IntrospectionProvider` and `DatasourceProvider`. |
|
|
89
|
+
| `access?` | `'full'` (default) enables all capabilities; `'readonly'` enables list/detail/export only. |
|
|
90
|
+
| `datasourceId?` | Datasource id the entities bind to. Defaults to `main`. |
|
|
91
|
+
| `overrides?` | Optional curated overlay merged via the core `HybridLoader`. |
|
|
92
|
+
| `policies?` | RBAC policy. Defaults to an `admin` role with `['*']`. |
|
|
93
|
+
| `operations?`, `audit?`, `logger?` | Forwarded to the engine. |
|
|
94
|
+
|
|
95
|
+
## Routes
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
GET /health
|
|
99
|
+
GET /metadata
|
|
100
|
+
GET /metadata/:entity
|
|
101
|
+
GET /entities/:entity (list — filter/sort/paginate/search via query string)
|
|
102
|
+
POST /entities/:entity (create)
|
|
103
|
+
GET /entities/:entity/:id (detail)
|
|
104
|
+
PATCH /entities/:entity/:id (update)
|
|
105
|
+
DELETE /entities/:entity/:id (hard delete)
|
|
106
|
+
POST /entities/:entity/:id/clone
|
|
107
|
+
POST /entities/:entity/:id/soft-delete
|
|
108
|
+
POST /entities/:entity/:id/restore
|
|
109
|
+
GET /entities/:entity/export (?format=csv|json)
|
|
110
|
+
POST /operations/:operationId
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## What this package is not
|
|
114
|
+
|
|
115
|
+
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).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Server } from 'node:http';
|
|
2
|
+
import { Express } from 'express';
|
|
3
|
+
import { MaestroEngine, MaestroActorResolver, MaestroHttpHandlers, IntrospectionProvider, DatasourceProvider, LoadedConfig, RbacPolicy, OperationDef, AuditRepository, Logger } from '@maykonpaulo/maestro-core';
|
|
4
|
+
|
|
5
|
+
interface CorsOptions {
|
|
6
|
+
/** Allowed origin. Defaults to `*`. Set to a concrete origin when using credentials. */
|
|
7
|
+
origin?: string;
|
|
8
|
+
/** Emit `Access-Control-Allow-Credentials: true`. Defaults to false. */
|
|
9
|
+
credentials?: boolean;
|
|
10
|
+
/** Extra request headers to allow, appended to the defaults. */
|
|
11
|
+
allowedHeaders?: string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A dependency-free CORS middleware. Kept tiny on purpose — the turnkey server should not pull an extra
|
|
15
|
+
* package for a handful of headers, and a consumer with stricter needs can disable this and mount their own.
|
|
16
|
+
*/
|
|
17
|
+
declare function applyCors(app: Express, options?: CorsOptions): void;
|
|
18
|
+
|
|
19
|
+
interface CreateMaestroServerOptions {
|
|
20
|
+
/** The engine to serve. Build it from declarations (`createMaestro`) or from a live database (`createIntrospectedEngine`). */
|
|
21
|
+
engine: MaestroEngine;
|
|
22
|
+
/**
|
|
23
|
+
* Resolves the acting `Actor` for each request (authentication is the consumer's responsibility).
|
|
24
|
+
* Defaults to {@link devActorResolver} — a single admin actor, suitable only for local development.
|
|
25
|
+
*/
|
|
26
|
+
actorResolver?: MaestroActorResolver;
|
|
27
|
+
/** CORS handling. `true`/omitted → permissive defaults; an object → configured; `false` → no CORS middleware. */
|
|
28
|
+
cors?: boolean | CorsOptions;
|
|
29
|
+
/** Path prefix for every route (e.g. `/api/admin`). Defaults to none. */
|
|
30
|
+
basePath?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Bring your own Express app to mount the routes on (to add auth middleware, logging, other routes, etc.).
|
|
33
|
+
* When omitted, a fresh app is created with a JSON body parser and CORS.
|
|
34
|
+
*/
|
|
35
|
+
app?: Express;
|
|
36
|
+
}
|
|
37
|
+
interface MaestroServer {
|
|
38
|
+
/** The Express app with all Maestro routes mounted. Mount extra routes/middleware before calling `listen`. */
|
|
39
|
+
app: Express;
|
|
40
|
+
/** The underlying framework-agnostic handlers, in case you want to mount a subset yourself. */
|
|
41
|
+
handlers: MaestroHttpHandlers;
|
|
42
|
+
/** Starts listening and resolves with the Node `http.Server` once bound. */
|
|
43
|
+
listen(port: number, host?: string): Promise<Server>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Mounts the Maestro HTTP handlers on a real Express server — the turnkey layer the core deliberately
|
|
47
|
+
* omits (ADR 0008). It adds only what a server needs and the core must not carry: JSON body parsing,
|
|
48
|
+
* CORS, route wiring and a `listen()` lifecycle. All domain behaviour still comes from the engine.
|
|
49
|
+
*/
|
|
50
|
+
declare function createMaestroServer(options: CreateMaestroServerOptions): MaestroServer;
|
|
51
|
+
|
|
52
|
+
/** Access level applied to entities discovered by introspection. */
|
|
53
|
+
type IntrospectedAccess = 'full' | 'readonly';
|
|
54
|
+
interface CreateIntrospectedEngineOptions {
|
|
55
|
+
/**
|
|
56
|
+
* A live provider that can both describe the database (`introspect`) and operate it (`list/create/...`).
|
|
57
|
+
* The MongoDB, SQL, and other official providers implement both interfaces on a single instance.
|
|
58
|
+
*/
|
|
59
|
+
provider: IntrospectionProvider & DatasourceProvider;
|
|
60
|
+
/** Datasource id the discovered entities are bound to. Defaults to `main`. */
|
|
61
|
+
datasourceId?: string;
|
|
62
|
+
/**
|
|
63
|
+
* `full` (default) enables every capability on discovered entities, turning the engine into a complete
|
|
64
|
+
* database manipulator. `readonly` enables only list/detail/export — a safe way to browse a database
|
|
65
|
+
* without any risk of mutating it.
|
|
66
|
+
*/
|
|
67
|
+
access?: IntrospectedAccess;
|
|
68
|
+
/**
|
|
69
|
+
* Optional curated overlay (labels, extra fields, relations, RBAC) merged over the introspected schema
|
|
70
|
+
* via the core `HybridLoader`. Entities you curate here keep their own explicit `capabilities`.
|
|
71
|
+
*/
|
|
72
|
+
overrides?: LoadedConfig;
|
|
73
|
+
/**
|
|
74
|
+
* RBAC policy for the engine. When omitted, a default `admin` role with `['*']` is used — appropriate
|
|
75
|
+
* for a single platform operator and meant to be replaced with a real policy in multi-role deployments.
|
|
76
|
+
*/
|
|
77
|
+
policies?: RbacPolicy;
|
|
78
|
+
operations?: OperationDef[];
|
|
79
|
+
audit?: AuditRepository;
|
|
80
|
+
logger?: Logger;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Builds a `MaestroEngine` directly from a live database, with no per-entity declaration required
|
|
84
|
+
* (ADR 0008, DA-17). It introspects the provider, turns every discovered collection/table into a
|
|
85
|
+
* manipulable entity, and — unlike `createMaestroFromIntrospection`, whose entities are read-only by
|
|
86
|
+
* default — enables write capabilities according to `access`, so "point at the database and manipulate
|
|
87
|
+
* everything" works out of the box.
|
|
88
|
+
*
|
|
89
|
+
* Uses only public core exports (`introspect` → `mergeIntrospectionWithOverrides` → `createMaestro`).
|
|
90
|
+
*/
|
|
91
|
+
declare function createIntrospectedEngine(options: CreateIntrospectedEngineOptions): Promise<MaestroEngine>;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The default resolver used when `createMaestroServer` is called without one. It grants every request a
|
|
95
|
+
* single admin actor — convenient for local development and demos, and **never** appropriate for
|
|
96
|
+
* production. In production the consumer supplies its own resolver (deriving the actor from a JWT, cookie
|
|
97
|
+
* or session), keeping authentication the consumer's responsibility (ADR 0008, DA-18).
|
|
98
|
+
*/
|
|
99
|
+
declare const devActorResolver: MaestroActorResolver;
|
|
100
|
+
|
|
101
|
+
export { type CorsOptions, type CreateIntrospectedEngineOptions, type CreateMaestroServerOptions, type IntrospectedAccess, type MaestroServer, applyCors, createIntrospectedEngine, createMaestroServer, devActorResolver };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// src/createMaestroServer.ts
|
|
2
|
+
import { createServer } from "http";
|
|
3
|
+
import express from "express";
|
|
4
|
+
import { createMaestroHttpHandlers } from "@maykonpaulo/maestro-core";
|
|
5
|
+
|
|
6
|
+
// src/expressAdapter.ts
|
|
7
|
+
function toMaestroRequest(req) {
|
|
8
|
+
return {
|
|
9
|
+
params: req.params,
|
|
10
|
+
query: req.query,
|
|
11
|
+
body: req.body,
|
|
12
|
+
headers: req.headers,
|
|
13
|
+
method: req.method,
|
|
14
|
+
path: req.path
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function adapt(handler) {
|
|
18
|
+
return async (req, res) => {
|
|
19
|
+
const response = await handler(toMaestroRequest(req));
|
|
20
|
+
if (response.headers) {
|
|
21
|
+
for (const [key, value] of Object.entries(response.headers)) {
|
|
22
|
+
res.setHeader(key, value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
res.status(response.status);
|
|
26
|
+
if (response.body === null || response.body === void 0) {
|
|
27
|
+
res.end();
|
|
28
|
+
} else if (typeof response.body === "string") {
|
|
29
|
+
res.send(response.body);
|
|
30
|
+
} else {
|
|
31
|
+
res.json(response.body);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/cors.ts
|
|
37
|
+
var DEFAULT_HEADERS = ["Content-Type", "Authorization", "X-Role"];
|
|
38
|
+
var ALLOWED_METHODS = "GET,POST,PATCH,DELETE,OPTIONS";
|
|
39
|
+
function applyCors(app, options = {}) {
|
|
40
|
+
const headers = [...DEFAULT_HEADERS, ...options.allowedHeaders ?? []].join(", ");
|
|
41
|
+
app.use((req, res, next) => {
|
|
42
|
+
res.setHeader("Access-Control-Allow-Origin", options.origin ?? "*");
|
|
43
|
+
res.setHeader("Access-Control-Allow-Methods", ALLOWED_METHODS);
|
|
44
|
+
res.setHeader("Access-Control-Allow-Headers", headers);
|
|
45
|
+
if (options.credentials) res.setHeader("Access-Control-Allow-Credentials", "true");
|
|
46
|
+
if (req.method === "OPTIONS") {
|
|
47
|
+
res.status(204).end();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
next();
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/devActorResolver.ts
|
|
55
|
+
var devActorResolver = async () => ({
|
|
56
|
+
actor: {
|
|
57
|
+
id: "dev-admin",
|
|
58
|
+
type: "user",
|
|
59
|
+
name: "Development Admin",
|
|
60
|
+
roles: ["admin"]
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// src/createMaestroServer.ts
|
|
65
|
+
var ROUTES = {
|
|
66
|
+
health: "/health",
|
|
67
|
+
metadata: "/metadata",
|
|
68
|
+
entityMetadata: "/metadata/:entity",
|
|
69
|
+
operation: "/operations/:operationId",
|
|
70
|
+
export: "/entities/:entity/export",
|
|
71
|
+
list: "/entities/:entity",
|
|
72
|
+
create: "/entities/:entity",
|
|
73
|
+
findById: "/entities/:entity/:id",
|
|
74
|
+
update: "/entities/:entity/:id",
|
|
75
|
+
clone: "/entities/:entity/:id/clone",
|
|
76
|
+
softDelete: "/entities/:entity/:id/soft-delete",
|
|
77
|
+
restore: "/entities/:entity/:id/restore",
|
|
78
|
+
hardDelete: "/entities/:entity/:id"
|
|
79
|
+
};
|
|
80
|
+
function createMaestroServer(options) {
|
|
81
|
+
const actorResolver = options.actorResolver ?? devActorResolver;
|
|
82
|
+
const handlers = createMaestroHttpHandlers(options.engine, { actorResolver });
|
|
83
|
+
const app = options.app ?? express();
|
|
84
|
+
if (!options.app) {
|
|
85
|
+
if (options.cors !== false) {
|
|
86
|
+
applyCors(app, typeof options.cors === "object" ? options.cors : {});
|
|
87
|
+
}
|
|
88
|
+
app.use(express.json());
|
|
89
|
+
}
|
|
90
|
+
const base = options.basePath ?? "";
|
|
91
|
+
const at = (path) => `${base}${path}`;
|
|
92
|
+
app.get(at(ROUTES.health), adapt(handlers.health));
|
|
93
|
+
app.get(at(ROUTES.metadata), adapt(handlers.getMetadata));
|
|
94
|
+
app.get(at(ROUTES.entityMetadata), adapt(handlers.getEntityMetadata));
|
|
95
|
+
app.post(at(ROUTES.operation), adapt(handlers.executeOperation));
|
|
96
|
+
app.get(at(ROUTES.export), adapt(handlers.export));
|
|
97
|
+
app.get(at(ROUTES.list), adapt(handlers.list));
|
|
98
|
+
app.post(at(ROUTES.create), adapt(handlers.create));
|
|
99
|
+
app.get(at(ROUTES.findById), adapt(handlers.findById));
|
|
100
|
+
app.patch(at(ROUTES.update), adapt(handlers.update));
|
|
101
|
+
app.post(at(ROUTES.clone), adapt(handlers.clone));
|
|
102
|
+
app.post(at(ROUTES.softDelete), adapt(handlers.softDelete));
|
|
103
|
+
app.post(at(ROUTES.restore), adapt(handlers.restore));
|
|
104
|
+
app.delete(at(ROUTES.hardDelete), adapt(handlers.hardDelete));
|
|
105
|
+
return {
|
|
106
|
+
app,
|
|
107
|
+
handlers,
|
|
108
|
+
listen(port, host) {
|
|
109
|
+
const server = createServer(app);
|
|
110
|
+
return new Promise((resolve, reject) => {
|
|
111
|
+
server.once("error", reject);
|
|
112
|
+
const onReady = () => {
|
|
113
|
+
server.off("error", reject);
|
|
114
|
+
resolve(server);
|
|
115
|
+
};
|
|
116
|
+
if (host) server.listen(port, host, onReady);
|
|
117
|
+
else server.listen(port, onReady);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/createIntrospectedEngine.ts
|
|
124
|
+
import {
|
|
125
|
+
createMaestro,
|
|
126
|
+
mergeIntrospectionWithOverrides
|
|
127
|
+
} from "@maykonpaulo/maestro-core";
|
|
128
|
+
var FULL_CAPABILITIES = {
|
|
129
|
+
list: true,
|
|
130
|
+
detail: true,
|
|
131
|
+
create: true,
|
|
132
|
+
update: true,
|
|
133
|
+
clone: true,
|
|
134
|
+
delete: true,
|
|
135
|
+
softDelete: true,
|
|
136
|
+
export: true,
|
|
137
|
+
bulkActions: true
|
|
138
|
+
};
|
|
139
|
+
var READONLY_CAPABILITIES = {
|
|
140
|
+
list: true,
|
|
141
|
+
detail: true,
|
|
142
|
+
create: false,
|
|
143
|
+
update: false,
|
|
144
|
+
clone: false,
|
|
145
|
+
delete: false,
|
|
146
|
+
softDelete: false,
|
|
147
|
+
export: true,
|
|
148
|
+
bulkActions: false
|
|
149
|
+
};
|
|
150
|
+
var DEFAULT_ADMIN_POLICY = {
|
|
151
|
+
roles: {
|
|
152
|
+
admin: { id: "admin", name: "Administrator", permissions: ["*"] }
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
function capabilitiesFor(entity, access) {
|
|
156
|
+
if (entity.capabilities) return entity.capabilities;
|
|
157
|
+
const base = access === "full" ? FULL_CAPABILITIES : READONLY_CAPABILITIES;
|
|
158
|
+
return { ...base, softDelete: access === "full" ? Boolean(entity.softDelete) : false };
|
|
159
|
+
}
|
|
160
|
+
async function createIntrospectedEngine(options) {
|
|
161
|
+
const datasourceId = options.datasourceId ?? "main";
|
|
162
|
+
const access = options.access ?? "full";
|
|
163
|
+
const introspection = await options.provider.introspect();
|
|
164
|
+
const overrides = options.overrides ?? { entities: [], relations: [] };
|
|
165
|
+
const config = mergeIntrospectionWithOverrides({
|
|
166
|
+
introspection,
|
|
167
|
+
overrides,
|
|
168
|
+
datasources: { [datasourceId]: options.provider },
|
|
169
|
+
defaultDatasource: datasourceId,
|
|
170
|
+
strategy: "extend",
|
|
171
|
+
operations: options.operations,
|
|
172
|
+
audit: options.audit,
|
|
173
|
+
logger: options.logger
|
|
174
|
+
});
|
|
175
|
+
config.entities = (config.entities ?? []).map((entity) => ({
|
|
176
|
+
...entity,
|
|
177
|
+
capabilities: capabilitiesFor(entity, access)
|
|
178
|
+
}));
|
|
179
|
+
config.policies = options.policies ?? config.policies ?? DEFAULT_ADMIN_POLICY;
|
|
180
|
+
return createMaestro(config);
|
|
181
|
+
}
|
|
182
|
+
export {
|
|
183
|
+
applyCors,
|
|
184
|
+
createIntrospectedEngine,
|
|
185
|
+
createMaestroServer,
|
|
186
|
+
devActorResolver
|
|
187
|
+
};
|
|
188
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/createMaestroServer.ts","../src/expressAdapter.ts","../src/cors.ts","../src/devActorResolver.ts","../src/createIntrospectedEngine.ts"],"sourcesContent":["import { createServer, type Server } from 'node:http';\nimport express, { type Express } from 'express';\nimport { createMaestroHttpHandlers } from '@maykonpaulo/maestro-core';\nimport type {\n MaestroEngine,\n MaestroActorResolver,\n MaestroHttpHandlers,\n} from '@maykonpaulo/maestro-core';\nimport { adapt } from './expressAdapter.js';\nimport { applyCors, type CorsOptions } from './cors.js';\nimport { devActorResolver } from './devActorResolver.js';\n\nexport interface CreateMaestroServerOptions {\n /** The engine to serve. Build it from declarations (`createMaestro`) or from a live database (`createIntrospectedEngine`). */\n engine: MaestroEngine;\n /**\n * Resolves the acting `Actor` for each request (authentication is the consumer's responsibility).\n * Defaults to {@link devActorResolver} — a single admin actor, suitable only for local development.\n */\n actorResolver?: MaestroActorResolver;\n /** CORS handling. `true`/omitted → permissive defaults; an object → configured; `false` → no CORS middleware. */\n cors?: boolean | CorsOptions;\n /** Path prefix for every route (e.g. `/api/admin`). Defaults to none. */\n basePath?: string;\n /**\n * Bring your own Express app to mount the routes on (to add auth middleware, logging, other routes, etc.).\n * When omitted, a fresh app is created with a JSON body parser and CORS.\n */\n app?: Express;\n}\n\nexport interface MaestroServer {\n /** The Express app with all Maestro routes mounted. Mount extra routes/middleware before calling `listen`. */\n app: Express;\n /** The underlying framework-agnostic handlers, in case you want to mount a subset yourself. */\n handlers: MaestroHttpHandlers;\n /** Starts listening and resolves with the Node `http.Server` once bound. */\n listen(port: number, host?: string): Promise<Server>;\n}\n\nconst ROUTES = {\n health: '/health',\n metadata: '/metadata',\n entityMetadata: '/metadata/:entity',\n operation: '/operations/:operationId',\n export: '/entities/:entity/export',\n list: '/entities/:entity',\n create: '/entities/:entity',\n findById: '/entities/:entity/:id',\n update: '/entities/:entity/:id',\n clone: '/entities/:entity/:id/clone',\n softDelete: '/entities/:entity/:id/soft-delete',\n restore: '/entities/:entity/:id/restore',\n hardDelete: '/entities/:entity/:id',\n} as const;\n\n/**\n * Mounts the Maestro HTTP handlers on a real Express server — the turnkey layer the core deliberately\n * omits (ADR 0008). It adds only what a server needs and the core must not carry: JSON body parsing,\n * CORS, route wiring and a `listen()` lifecycle. All domain behaviour still comes from the engine.\n */\nexport function createMaestroServer(options: CreateMaestroServerOptions): MaestroServer {\n const actorResolver = options.actorResolver ?? devActorResolver;\n const handlers = createMaestroHttpHandlers(options.engine, { actorResolver });\n\n const app = options.app ?? express();\n\n if (!options.app) {\n if (options.cors !== false) {\n applyCors(app, typeof options.cors === 'object' ? options.cors : {});\n }\n app.use(express.json());\n }\n\n const base = options.basePath ?? '';\n const at = (path: string): string => `${base}${path}`;\n\n app.get(at(ROUTES.health), adapt(handlers.health));\n app.get(at(ROUTES.metadata), adapt(handlers.getMetadata));\n app.get(at(ROUTES.entityMetadata), adapt(handlers.getEntityMetadata));\n\n app.post(at(ROUTES.operation), adapt(handlers.executeOperation));\n\n // `/entities/:entity/export` must be registered before `/entities/:entity/:id` so \"export\" isn't\n // captured as an id.\n app.get(at(ROUTES.export), adapt(handlers.export));\n app.get(at(ROUTES.list), adapt(handlers.list));\n app.post(at(ROUTES.create), adapt(handlers.create));\n app.get(at(ROUTES.findById), adapt(handlers.findById));\n app.patch(at(ROUTES.update), adapt(handlers.update));\n app.post(at(ROUTES.clone), adapt(handlers.clone));\n app.post(at(ROUTES.softDelete), adapt(handlers.softDelete));\n app.post(at(ROUTES.restore), adapt(handlers.restore));\n app.delete(at(ROUTES.hardDelete), adapt(handlers.hardDelete));\n\n return {\n app,\n handlers,\n listen(port: number, host?: string): Promise<Server> {\n const server = createServer(app);\n return new Promise((resolve, reject) => {\n server.once('error', reject);\n const onReady = (): void => {\n server.off('error', reject);\n resolve(server);\n };\n if (host) server.listen(port, host, onReady);\n else server.listen(port, onReady);\n });\n },\n };\n}\n","import type { Request, Response } from 'express';\nimport type { MaestroHttpHandler, MaestroHttpRequest } from '@maykonpaulo/maestro-core';\n\nfunction toMaestroRequest(req: Request): MaestroHttpRequest {\n return {\n params: req.params as Record<string, string>,\n query: req.query as Record<string, string | string[]>,\n body: req.body as unknown,\n headers: req.headers as Record<string, string | string[] | undefined>,\n method: req.method,\n path: req.path,\n };\n}\n\n/**\n * Bridges a framework-agnostic `MaestroHttpHandler` (from the core) to an Express request handler.\n * The core owns the contract; this adapter is the only Express-aware surface in the package.\n */\nexport function adapt(handler: MaestroHttpHandler) {\n return async (req: Request, res: Response): Promise<void> => {\n const response = await handler(toMaestroRequest(req));\n\n if (response.headers) {\n for (const [key, value] of Object.entries(response.headers)) {\n res.setHeader(key, value);\n }\n }\n\n res.status(response.status);\n\n if (response.body === null || response.body === undefined) {\n res.end();\n } else if (typeof response.body === 'string') {\n res.send(response.body);\n } else {\n res.json(response.body);\n }\n };\n}\n","import type { Express, Request, Response, NextFunction } from 'express';\n\nexport interface CorsOptions {\n /** Allowed origin. Defaults to `*`. Set to a concrete origin when using credentials. */\n origin?: string;\n /** Emit `Access-Control-Allow-Credentials: true`. Defaults to false. */\n credentials?: boolean;\n /** Extra request headers to allow, appended to the defaults. */\n allowedHeaders?: string[];\n}\n\nconst DEFAULT_HEADERS = ['Content-Type', 'Authorization', 'X-Role'];\nconst ALLOWED_METHODS = 'GET,POST,PATCH,DELETE,OPTIONS';\n\n/**\n * A dependency-free CORS middleware. Kept tiny on purpose — the turnkey server should not pull an extra\n * package for a handful of headers, and a consumer with stricter needs can disable this and mount their own.\n */\nexport function applyCors(app: Express, options: CorsOptions = {}): void {\n const headers = [...DEFAULT_HEADERS, ...(options.allowedHeaders ?? [])].join(', ');\n app.use((req: Request, res: Response, next: NextFunction): void => {\n res.setHeader('Access-Control-Allow-Origin', options.origin ?? '*');\n res.setHeader('Access-Control-Allow-Methods', ALLOWED_METHODS);\n res.setHeader('Access-Control-Allow-Headers', headers);\n if (options.credentials) res.setHeader('Access-Control-Allow-Credentials', 'true');\n if (req.method === 'OPTIONS') {\n res.status(204).end();\n return;\n }\n next();\n });\n}\n","import type { MaestroActorResolver } from '@maykonpaulo/maestro-core';\n\n/**\n * The default resolver used when `createMaestroServer` is called without one. It grants every request a\n * single admin actor — convenient for local development and demos, and **never** appropriate for\n * production. In production the consumer supplies its own resolver (deriving the actor from a JWT, cookie\n * or session), keeping authentication the consumer's responsibility (ADR 0008, DA-18).\n */\nexport const devActorResolver: MaestroActorResolver = async () => ({\n actor: {\n id: 'dev-admin',\n type: 'user',\n name: 'Development Admin',\n roles: ['admin'],\n },\n});\n","import {\n createMaestro,\n mergeIntrospectionWithOverrides,\n} from '@maykonpaulo/maestro-core';\nimport type {\n MaestroEngine,\n IntrospectionProvider,\n DatasourceProvider,\n EntityCapabilities,\n EntitySchema,\n RbacPolicy,\n LoadedConfig,\n OperationDef,\n AuditRepository,\n Logger,\n} from '@maykonpaulo/maestro-core';\n\n/** Access level applied to entities discovered by introspection. */\nexport type IntrospectedAccess = 'full' | 'readonly';\n\nexport interface CreateIntrospectedEngineOptions {\n /**\n * A live provider that can both describe the database (`introspect`) and operate it (`list/create/...`).\n * The MongoDB, SQL, and other official providers implement both interfaces on a single instance.\n */\n provider: IntrospectionProvider & DatasourceProvider;\n /** Datasource id the discovered entities are bound to. Defaults to `main`. */\n datasourceId?: string;\n /**\n * `full` (default) enables every capability on discovered entities, turning the engine into a complete\n * database manipulator. `readonly` enables only list/detail/export — a safe way to browse a database\n * without any risk of mutating it.\n */\n access?: IntrospectedAccess;\n /**\n * Optional curated overlay (labels, extra fields, relations, RBAC) merged over the introspected schema\n * via the core `HybridLoader`. Entities you curate here keep their own explicit `capabilities`.\n */\n overrides?: LoadedConfig;\n /**\n * RBAC policy for the engine. When omitted, a default `admin` role with `['*']` is used — appropriate\n * for a single platform operator and meant to be replaced with a real policy in multi-role deployments.\n */\n policies?: RbacPolicy;\n operations?: OperationDef[];\n audit?: AuditRepository;\n logger?: Logger;\n}\n\nconst FULL_CAPABILITIES: EntityCapabilities = {\n list: true,\n detail: true,\n create: true,\n update: true,\n clone: true,\n delete: true,\n softDelete: true,\n export: true,\n bulkActions: true,\n};\n\nconst READONLY_CAPABILITIES: Partial<EntityCapabilities> = {\n list: true,\n detail: true,\n create: false,\n update: false,\n clone: false,\n delete: false,\n softDelete: false,\n export: true,\n bulkActions: false,\n};\n\nconst DEFAULT_ADMIN_POLICY: RbacPolicy = {\n roles: {\n admin: { id: 'admin', name: 'Administrator', permissions: ['*'] },\n },\n};\n\n/**\n * Resolves the capabilities for one entity. Curated entities (an override that set `capabilities`\n * explicitly) are respected as-is; entities discovered by introspection get the access-level defaults.\n * The `softDelete` capability is only ever enabled when the entity actually declares a soft-delete field,\n * since the engine throws a configuration error otherwise.\n */\nfunction capabilitiesFor(entity: EntitySchema, access: IntrospectedAccess): Partial<EntityCapabilities> {\n if (entity.capabilities) return entity.capabilities;\n const base = access === 'full' ? FULL_CAPABILITIES : READONLY_CAPABILITIES;\n return { ...base, softDelete: access === 'full' ? Boolean(entity.softDelete) : false };\n}\n\n/**\n * Builds a `MaestroEngine` directly from a live database, with no per-entity declaration required\n * (ADR 0008, DA-17). It introspects the provider, turns every discovered collection/table into a\n * manipulable entity, and — unlike `createMaestroFromIntrospection`, whose entities are read-only by\n * default — enables write capabilities according to `access`, so \"point at the database and manipulate\n * everything\" works out of the box.\n *\n * Uses only public core exports (`introspect` → `mergeIntrospectionWithOverrides` → `createMaestro`).\n */\nexport async function createIntrospectedEngine(\n options: CreateIntrospectedEngineOptions,\n): Promise<MaestroEngine> {\n const datasourceId = options.datasourceId ?? 'main';\n const access = options.access ?? 'full';\n\n const introspection = await options.provider.introspect();\n const overrides: LoadedConfig = options.overrides ?? { entities: [], relations: [] };\n\n const config = mergeIntrospectionWithOverrides({\n introspection,\n overrides,\n datasources: { [datasourceId]: options.provider },\n defaultDatasource: datasourceId,\n strategy: 'extend',\n operations: options.operations,\n audit: options.audit,\n logger: options.logger,\n });\n\n config.entities = (config.entities ?? []).map((entity) => ({\n ...entity,\n capabilities: capabilitiesFor(entity, access),\n }));\n\n config.policies = options.policies ?? config.policies ?? DEFAULT_ADMIN_POLICY;\n\n return createMaestro(config);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@maykonpaulo/maestro-server",
|
|
3
|
+
"version": "0.1.0-next.1",
|
|
4
|
+
"description": "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-free; this sibling package is the batteries-included layer.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@maykonpaulo/maestro-core": "0.7.0-next.2"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"express": "^4.19.2"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/express": "^4.17.21",
|
|
30
|
+
"@types/node": "^22.0.0",
|
|
31
|
+
"mongodb": "^7.4.0",
|
|
32
|
+
"mongodb-memory-server": "^11.2.0",
|
|
33
|
+
"rimraf": "^6.0.0",
|
|
34
|
+
"tsup": "^8.0.0",
|
|
35
|
+
"typescript": "^5.6.0",
|
|
36
|
+
"vitest": "^2.0.0",
|
|
37
|
+
"@maykonpaulo/maestro-core": "0.7.0-next.2",
|
|
38
|
+
"@maykonpaulo/maestro-provider-mongodb": "1.0.0-next.1"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"lint": "eslint src tests",
|
|
46
|
+
"clean": "rimraf dist"
|
|
47
|
+
}
|
|
48
|
+
}
|