@forestadmin/agent 1.99.2 → 1.100.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 +73 -0
- package/dist/agent.d.ts +38 -1
- package/dist/agent.js +101 -14
- package/dist/bff-routes.d.ts +25 -0
- package/dist/bff-routes.js +49 -0
- package/dist/embedded-bff.d.ts +61 -0
- package/dist/embedded-bff.js +220 -0
- package/dist/framework-mounter.d.ts +8 -4
- package/dist/framework-mounter.js +21 -14
- package/dist/index.d.ts +1 -1
- package/dist/root-middleware.d.ts +33 -0
- package/dist/root-middleware.js +70 -0
- package/dist/types.d.ts +47 -1
- package/dist/types.js +1 -1
- package/package.json +12 -5
- package/dist/mcp-middleware.d.ts +0 -27
- package/dist/mcp-middleware.js +0 -67
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const bff_routes_1 = require("./bff-routes");
|
|
37
|
+
/**
|
|
38
|
+
* Serialize the BFF's structured log context onto the message: the agent's logger only accepts an
|
|
39
|
+
* Error as its third argument, so the context would be dropped otherwise. Errors are unfolded by
|
|
40
|
+
* hand — their `message` and `stack` are not enumerable, so `JSON.stringify` alone turns the one
|
|
41
|
+
* value worth logging into `{}`. Never throws — logging must not break a request.
|
|
42
|
+
*/
|
|
43
|
+
function formatLog(message, context) {
|
|
44
|
+
if (!context || Object.keys(context).length === 0)
|
|
45
|
+
return `[BFF] ${message}`;
|
|
46
|
+
try {
|
|
47
|
+
const serialized = JSON.stringify(context, (_key, value) => value instanceof Error
|
|
48
|
+
? { name: value.name, message: value.message, stack: value.stack }
|
|
49
|
+
: value);
|
|
50
|
+
return `[BFF] ${message} ${serialized}`;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return `[BFF] ${message} [unserializable context]`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Owns the lifecycle of a BFF embedded in the agent process: configuration, dynamic loading of the
|
|
58
|
+
* optional package, build, and the request handler the agent mounts at `/bff`. The agent only wires
|
|
59
|
+
* the callback and delegates start/stop.
|
|
60
|
+
*/
|
|
61
|
+
class EmbeddedBff {
|
|
62
|
+
constructor(options, embedOptions) {
|
|
63
|
+
this.options = options;
|
|
64
|
+
this.embedOptions = embedOptions;
|
|
65
|
+
this.config = null;
|
|
66
|
+
this.bff = null;
|
|
67
|
+
this.stopped = false;
|
|
68
|
+
/**
|
|
69
|
+
* The callback the agent registers at `/bff`. Answers 503 rather than falling through while the
|
|
70
|
+
* BFF is not serving: a 404 from the host would read as "wrong url" instead of "not available".
|
|
71
|
+
* Boot and shutdown are told apart, because a probe should wait on the first and drain on the
|
|
72
|
+
* second.
|
|
73
|
+
*/
|
|
74
|
+
this.handle = (req, res) => {
|
|
75
|
+
if (!this.bff) {
|
|
76
|
+
const type = this.stopped ? 'bff_stopped' : 'bff_not_started';
|
|
77
|
+
const message = this.stopped
|
|
78
|
+
? 'The embedded BFF was stopped with the agent.'
|
|
79
|
+
: 'The embedded BFF is not started yet.';
|
|
80
|
+
res.statusCode = 503;
|
|
81
|
+
res.setHeader('Content-Type', 'application/json');
|
|
82
|
+
res.end(JSON.stringify({ error: { type, status: 503, message } }));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// The BFF knows nothing of the prefix it is served under. `originalUrl` is claimed before the
|
|
86
|
+
// rewrite so a host logger reading it still reports the url the client asked for.
|
|
87
|
+
const bffReq = req;
|
|
88
|
+
bffReq.originalUrl ?? (bffReq.originalUrl = bffReq.url);
|
|
89
|
+
req.url = (0, bff_routes_1.stripBffPrefix)(req.url ?? bff_routes_1.BFF_PREFIX);
|
|
90
|
+
this.bff.callback(req, res);
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Load the package and validate the options the caller handed to `addBff()`. Called before the
|
|
95
|
+
* agent mounts anything: everything checked here is caller input, and `parseConfig` throws on a
|
|
96
|
+
* mistyped `tokenEncryptionKey`, timezone, timeout or url. Failing here leaves nothing serving,
|
|
97
|
+
* where failing after mount() would leave the host with a live `/forest` and a bricked `/bff`.
|
|
98
|
+
*/
|
|
99
|
+
async prepare() {
|
|
100
|
+
const { parseConfig, IN_PROCESS_AGENT_URL } = await this.importPackage();
|
|
101
|
+
const { embedOptions } = this;
|
|
102
|
+
this.config = parseConfig({
|
|
103
|
+
FOREST_AUTH_SECRET: this.options.authSecret,
|
|
104
|
+
FOREST_ENV_SECRET: this.options.envSecret,
|
|
105
|
+
FOREST_SERVER_URL: this.options.forestServerUrl,
|
|
106
|
+
FOREST_APP_URL: this.options.forestAppUrl,
|
|
107
|
+
// Names where the agent answers, never how it is reached: the dispatcher is the transport.
|
|
108
|
+
AGENT_URL: IN_PROCESS_AGENT_URL,
|
|
109
|
+
BFF_TOKEN_ENCRYPTION_KEY: embedOptions.tokenEncryptionKey,
|
|
110
|
+
BFF_ALLOWED_ORIGINS: embedOptions.allowedOrigins?.join(','),
|
|
111
|
+
BFF_DEFAULT_TIMEZONE: embedOptions.defaultTimezone,
|
|
112
|
+
BFF_AGENT_TIMEOUT_MS: embedOptions.agentTimeoutMs?.toString(),
|
|
113
|
+
BFF_AI_TIMEOUT_MS: embedOptions.aiTimeoutMs?.toString(),
|
|
114
|
+
// Off unless asked for: the document is not filtered per caller, so mounting a BFF must not
|
|
115
|
+
// publish the name of every exposed collection and field on an already-open port.
|
|
116
|
+
BFF_OPENAPI_ENABLED: String(embedOptions.openapiEnabled ?? false),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Build the BFF. Called from agent.start() after mount(), because the dispatcher only reaches the
|
|
121
|
+
* agent's own stack once that stack is mounted.
|
|
122
|
+
*/
|
|
123
|
+
async start(dispatcher) {
|
|
124
|
+
if (!this.config)
|
|
125
|
+
await this.prepare();
|
|
126
|
+
// Cleared before the await, not after it: a start() following a stop() has to drop the previous
|
|
127
|
+
// shutdown's flag, while a stop() landing DURING the await must still be seen by the guard below.
|
|
128
|
+
this.stopped = false;
|
|
129
|
+
const { buildBff } = await this.importPackage();
|
|
130
|
+
const bff = await buildBff({
|
|
131
|
+
config: this.config,
|
|
132
|
+
dispatcher,
|
|
133
|
+
basePath: bff_routes_1.BFF_PREFIX,
|
|
134
|
+
// Counters are the schema cache's and the action-endpoint resolver's only channel — they take
|
|
135
|
+
// no logger — and every one of them reports a failure, so they go to the host's logs. Gauges
|
|
136
|
+
// do not: they are periodic cache sizes, and the default console sink reports them at Info,
|
|
137
|
+
// which would flood a host that only asked for a BFF.
|
|
138
|
+
metrics: {
|
|
139
|
+
// Tags carried through: `action_endpoint_error` and `action_endpoint_miss` name the
|
|
140
|
+
// rendering, collection and action that failed, which is the whole of what makes the line
|
|
141
|
+
// actionable — the metric name alone says only that something, somewhere, did not resolve.
|
|
142
|
+
increment: (name, tags) => this.options.logger('Warn', formatLog(`metric ${name}`, tags)),
|
|
143
|
+
gauge: () => undefined,
|
|
144
|
+
},
|
|
145
|
+
logger: (level, message, context) => this.options.logger(level, formatLog(message, context)),
|
|
146
|
+
});
|
|
147
|
+
// stop() may have landed while buildBff() was in flight. Assigning anyway would resurrect a
|
|
148
|
+
// stopped agent's /bff — dispatching into the stack it just tore down instead of answering 503.
|
|
149
|
+
if (this.stopped)
|
|
150
|
+
return;
|
|
151
|
+
this.bff = bff;
|
|
152
|
+
this.options.logger('Info', formatLog(`Embedded BFF mounted on ${bff_routes_1.BFF_PREFIX}`));
|
|
153
|
+
await this.warnIfExemptFromIpWhitelist();
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* The whitelist exempts a caller arriving over a loopback socket with no proxy hop, which is
|
|
157
|
+
* exactly what the in-process transport looks like — so every BFF request escapes it. That is the
|
|
158
|
+
* decision, not an oversight: propagating the caller's ip would make the embedded mode stricter
|
|
159
|
+
* than the standalone one, where the whitelist only ever sees the BFF's own host, and would refuse
|
|
160
|
+
* the browsers a third-party UI is made of. What it must not be is silent — an operator who turned
|
|
161
|
+
* the whitelist on to close a door has no other way to learn this door is not part of it.
|
|
162
|
+
*
|
|
163
|
+
* Reads the configuration itself rather than borrowing the one `IpWhitelist` already fetched: that
|
|
164
|
+
* route keeps it private, and reaching into it would put BFF concerns in an unrelated part of the
|
|
165
|
+
* agent. Costs one round-trip at boot. Never fails the boot: a warning is not worth refusing to
|
|
166
|
+
* serve over, and this is the same posture as the rest of the BFF's startup.
|
|
167
|
+
*/
|
|
168
|
+
async warnIfExemptFromIpWhitelist() {
|
|
169
|
+
try {
|
|
170
|
+
const { isFeatureEnabled } = await this.options.forestAdminClient.getIpWhitelistConfiguration();
|
|
171
|
+
if (!isFeatureEnabled)
|
|
172
|
+
return;
|
|
173
|
+
this.options.logger('Warn', formatLog(`The IP whitelist is enabled for this environment, but requests served under ` +
|
|
174
|
+
`${bff_routes_1.BFF_PREFIX} are not subject to it: they reach the agent in-process, which the ` +
|
|
175
|
+
`whitelist exempts as a trusted loopback caller. A resolved API key or a valid OAuth ` +
|
|
176
|
+
`session is still required.`));
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
this.options.logger('Debug', formatLog('Could not read the IP whitelist configuration', {
|
|
180
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/** Drop what the BFF read from the SaaS. Called on a restart, which means the schema moved. */
|
|
185
|
+
invalidate() {
|
|
186
|
+
this.bff?.invalidate();
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Stop answering. The host application keeps whatever middleware it registered, so without this
|
|
190
|
+
* a stopped agent would go on serving BFF data through a dispatcher pointing at a dead stack.
|
|
191
|
+
*/
|
|
192
|
+
stop() {
|
|
193
|
+
this.bff = null;
|
|
194
|
+
this.stopped = true;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Dynamically load the optional @forestadmin/agent-bff package. Deferred so agents that embed no
|
|
198
|
+
* BFF never load its code at startup.
|
|
199
|
+
*/
|
|
200
|
+
async importPackage() {
|
|
201
|
+
try {
|
|
202
|
+
return await Promise.resolve().then(() => __importStar(require('@forestadmin/agent-bff')));
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
// The original reason is kept: the package resolves from the host's own node_modules, so this
|
|
206
|
+
// also fires on a broken transitive dependency, a SyntaxError from a partial install, or a
|
|
207
|
+
// throw during the package's own evaluation — none of which "install it" would fix.
|
|
208
|
+
const { message } = error;
|
|
209
|
+
const wrapped = new Error(`The embedded BFF requires the \`@forestadmin/agent-bff\` package, which failed to ` +
|
|
210
|
+
`load: ${message}. Install it with \`npm install @forestadmin/agent-bff\`.`);
|
|
211
|
+
// Assigned rather than passed to the constructor: the repo targets ES2020, whose lib types no
|
|
212
|
+
// options bag on Error. A plain assignment carries the cause on every runtime, and the reason
|
|
213
|
+
// is in the message regardless of what reads it.
|
|
214
|
+
wrapped.cause = error;
|
|
215
|
+
throw wrapped;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
exports.default = EmbeddedBff;
|
|
220
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW1iZWRkZWQtYmZmLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2VtYmVkZGVkLWJmZi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUdBLDZDQUEwRDtBQUUxRDs7Ozs7R0FLRztBQUNILFNBQVMsU0FBUyxDQUFDLE9BQWUsRUFBRSxPQUFpQztJQUNuRSxJQUFJLENBQUMsT0FBTyxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsTUFBTSxLQUFLLENBQUM7UUFBRSxPQUFPLFNBQVMsT0FBTyxFQUFFLENBQUM7SUFFN0UsSUFBSSxDQUFDO1FBQ0gsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLEVBQUUsQ0FDekQsS0FBSyxZQUFZLEtBQUs7WUFDcEIsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPLEVBQUUsS0FBSyxFQUFFLEtBQUssQ0FBQyxLQUFLLEVBQUU7WUFDbEUsQ0FBQyxDQUFDLEtBQUssQ0FDVixDQUFDO1FBRUYsT0FBTyxTQUFTLE9BQU8sSUFBSSxVQUFVLEVBQUUsQ0FBQztJQUMxQyxDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AsT0FBTyxTQUFTLE9BQU8sMkJBQTJCLENBQUM7SUFDckQsQ0FBQztBQUNILENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBcUIsV0FBVztJQUs5QixZQUNtQixPQUFpQyxFQUNqQyxZQUE2QjtRQUQ3QixZQUFPLEdBQVAsT0FBTyxDQUEwQjtRQUNqQyxpQkFBWSxHQUFaLFlBQVksQ0FBaUI7UUFOeEMsV0FBTSxHQUFxQixJQUFJLENBQUM7UUFDaEMsUUFBRyxHQUFlLElBQUksQ0FBQztRQUN2QixZQUFPLEdBQUcsS0FBSyxDQUFDO1FBa0l4Qjs7Ozs7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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HttpCallback,
|
|
1
|
+
import type { HttpCallback, RootHandler } from './types';
|
|
2
2
|
import type { Logger } from '@forestadmin/datasource-toolkit';
|
|
3
3
|
import Router from '@koa/router';
|
|
4
4
|
import InProcessDispatcher from './mcp-in-process-dispatcher';
|
|
@@ -11,16 +11,20 @@ export default class FrameworkMounter {
|
|
|
11
11
|
protected readonly prefix: string;
|
|
12
12
|
private readonly logger;
|
|
13
13
|
private readonly fastifyAdapter;
|
|
14
|
-
private readonly
|
|
14
|
+
private readonly rootMiddleware;
|
|
15
15
|
private readonly inProcessDispatcher;
|
|
16
16
|
private inProcessHookRegistered;
|
|
17
17
|
/** Compute the prefix that the main router should be mounted at in the client's application */
|
|
18
18
|
private get completeMountPrefix();
|
|
19
19
|
constructor(prefix: string, logger: Logger);
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* Register the MCP server at the root of the host application, or `null` to unregister it. The
|
|
22
|
+
* matcher comes with the callback: there is no default pattern set, so nothing can claim host
|
|
23
|
+
* urls by accident. Call this before mount() or remount().
|
|
22
24
|
*/
|
|
23
|
-
protected setMcpCallback(
|
|
25
|
+
protected setMcpCallback(handler: RootHandler | null): void;
|
|
26
|
+
/** Serve an embedded BFF at `/bff`. Pass null to stop answering there. */
|
|
27
|
+
protected setBffCallback(callback: HttpCallback | null): void;
|
|
24
28
|
/**
|
|
25
29
|
* Dispatcher that runs agent-client requests against the agent's own `/forest` stack in-memory.
|
|
26
30
|
* The handler is rebuilt on every (re)mount so a captured reference never goes stale.
|
|
@@ -7,9 +7,10 @@ const router_1 = __importDefault(require("@koa/router"));
|
|
|
7
7
|
const http_1 = require("http");
|
|
8
8
|
const koa_1 = __importDefault(require("koa"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const bff_routes_1 = require("./bff-routes");
|
|
10
11
|
const fastify_adapter_1 = __importDefault(require("./fastify-adapter"));
|
|
11
12
|
const mcp_in_process_dispatcher_1 = __importDefault(require("./mcp-in-process-dispatcher"));
|
|
12
|
-
const
|
|
13
|
+
const root_middleware_1 = __importDefault(require("./root-middleware"));
|
|
13
14
|
class FrameworkMounter {
|
|
14
15
|
/** Compute the prefix that the main router should be mounted at in the client's application */
|
|
15
16
|
get completeMountPrefix() {
|
|
@@ -23,14 +24,20 @@ class FrameworkMounter {
|
|
|
23
24
|
this.prefix = prefix;
|
|
24
25
|
this.logger = logger;
|
|
25
26
|
this.fastifyAdapter = new fastify_adapter_1.default(logger);
|
|
26
|
-
this.
|
|
27
|
+
this.rootMiddleware = new root_middleware_1.default(logger);
|
|
27
28
|
this.inProcessDispatcher = new mcp_in_process_dispatcher_1.default(logger);
|
|
28
29
|
}
|
|
29
30
|
/**
|
|
30
|
-
*
|
|
31
|
+
* Register the MCP server at the root of the host application, or `null` to unregister it. The
|
|
32
|
+
* matcher comes with the callback: there is no default pattern set, so nothing can claim host
|
|
33
|
+
* urls by accident. Call this before mount() or remount().
|
|
31
34
|
*/
|
|
32
|
-
setMcpCallback(
|
|
33
|
-
this.
|
|
35
|
+
setMcpCallback(handler) {
|
|
36
|
+
this.rootMiddleware.set('mcp', handler);
|
|
37
|
+
}
|
|
38
|
+
/** Serve an embedded BFF at `/bff`. Pass null to stop answering there. */
|
|
39
|
+
setBffCallback(callback) {
|
|
40
|
+
this.rootMiddleware.set('bff', callback && { callback, matches: bff_routes_1.isBffRoute });
|
|
34
41
|
}
|
|
35
42
|
/**
|
|
36
43
|
* Dispatcher that runs agent-client requests against the agent's own `/forest` stack in-memory.
|
|
@@ -101,7 +108,7 @@ class FrameworkMounter {
|
|
|
101
108
|
*/
|
|
102
109
|
mountOnExpress(express) {
|
|
103
110
|
// MCP middleware - the callback handles its own path filtering and calls next() for non-MCP routes
|
|
104
|
-
express.use(this.
|
|
111
|
+
express.use(this.rootMiddleware.getExpressMiddleware());
|
|
105
112
|
// Mount main forest routes at /{prefix}/forest
|
|
106
113
|
express.use(this.completeMountPrefix, this.getConnectCallback(false));
|
|
107
114
|
this.logger('Info', `Successfully mounted on Express.js`);
|
|
@@ -113,7 +120,7 @@ class FrameworkMounter {
|
|
|
113
120
|
*/
|
|
114
121
|
mountOnFastify(fastify) {
|
|
115
122
|
// MCP middleware at root - the callback handles its own path filtering
|
|
116
|
-
this.fastifyAdapter.useCallback(fastify, this.
|
|
123
|
+
this.fastifyAdapter.useCallback(fastify, this.rootMiddleware.getExpressMiddleware(), '/');
|
|
117
124
|
// Mount main forest routes
|
|
118
125
|
const callback = this.getConnectCallback(false);
|
|
119
126
|
this.fastifyAdapter.useCallback(fastify, callback, this.completeMountPrefix);
|
|
@@ -138,7 +145,7 @@ class FrameworkMounter {
|
|
|
138
145
|
parentRouter.use(driverRouter.routes());
|
|
139
146
|
});
|
|
140
147
|
// MCP middleware - intercepts MCP routes before they reach Koa's body parser
|
|
141
|
-
koa.use(this.
|
|
148
|
+
koa.use(this.rootMiddleware.getKoaMiddleware());
|
|
142
149
|
koa.use(parentRouter.routes());
|
|
143
150
|
this.logger('Info', `Successfully mounted on Koa`);
|
|
144
151
|
return this;
|
|
@@ -152,13 +159,13 @@ class FrameworkMounter {
|
|
|
152
159
|
const callback = this.getConnectCallback(false);
|
|
153
160
|
if (adapter.constructor.name === 'ExpressAdapter') {
|
|
154
161
|
// MCP middleware at root - the callback handles its own path filtering
|
|
155
|
-
nestJs.use(this.
|
|
162
|
+
nestJs.use(this.rootMiddleware.getExpressMiddleware());
|
|
156
163
|
// Mount main forest routes
|
|
157
164
|
nestJs.use(this.completeMountPrefix, callback);
|
|
158
165
|
}
|
|
159
166
|
else {
|
|
160
167
|
// Fastify adapter - MCP middleware at root
|
|
161
|
-
this.fastifyAdapter.useCallback(nestJs, this.
|
|
168
|
+
this.fastifyAdapter.useCallback(nestJs, this.rootMiddleware.getExpressMiddleware(), '/');
|
|
162
169
|
this.fastifyAdapter.useCallback(nestJs, callback, this.completeMountPrefix);
|
|
163
170
|
}
|
|
164
171
|
this.logger('Info', `Successfully mounted on NestJS`);
|
|
@@ -176,9 +183,9 @@ class FrameworkMounter {
|
|
|
176
183
|
return (req, res) => {
|
|
177
184
|
// For standalone server (nested), check MCP callback first
|
|
178
185
|
// The MCP callback handles its own path filtering
|
|
179
|
-
const
|
|
180
|
-
if (nested &&
|
|
181
|
-
|
|
186
|
+
const rootCallback = this.rootMiddleware.getCallback();
|
|
187
|
+
if (nested && rootCallback) {
|
|
188
|
+
rootCallback(req, res, () => {
|
|
182
189
|
// next() called means not an MCP route - forward to main handler
|
|
183
190
|
if (handler) {
|
|
184
191
|
handler(req, res);
|
|
@@ -201,4 +208,4 @@ class FrameworkMounter {
|
|
|
201
208
|
}
|
|
202
209
|
}
|
|
203
210
|
exports.default = FrameworkMounter;
|
|
204
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
211
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnJhbWV3b3JrLW1vdW50ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZnJhbWV3b3JrLW1vdW50ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFLQSx5REFBaUM7QUFDakMsK0JBQW9DO0FBQ3BDLDhDQUFzQjtBQUN0QixnREFBd0I7QUFFeEIsNkNBQTBDO0FBQzFDLHdFQUErQztBQUMvQyw0RkFBOEQ7QUFDOUQsd0VBQStDO0FBRS9DLE1BQXFCLGdCQUFnQjtJQWdCbkMsK0ZBQStGO0lBQy9GLElBQVksbUJBQW1CO1FBQzdCLE9BQU8sY0FBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDckQsQ0FBQztJQUVELFlBQVksTUFBYyxFQUFFLE1BQWM7UUFqQnpCLGlCQUFZLEdBQTRCLEVBQUUsQ0FBQztRQUMzQyxnQkFBVyxHQUEwQyxFQUFFLENBQUM7UUFFeEQsV0FBTSxHQUE0QixFQUFFLENBQUM7UUFPOUMsNEJBQXVCLEdBQUcsS0FBSyxDQUFDO1FBUXRDLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxjQUFjLEdBQUcsSUFBSSx5QkFBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2pELElBQUksQ0FBQyxjQUFjLEdBQUcsSUFBSSx5QkFBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2pELElBQUksQ0FBQyxtQkFBbUIsR0FBRyxJQUFJLG1DQUFtQixDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQzdELENBQUM7SUFFRDs7OztPQUlHO0lBQ08sY0FBYyxDQUFDLE9BQTJCO1FBQ2xELElBQUksQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsQ0FBQztJQUMxQyxDQUFDO0lBRUQsMEVBQTBFO0lBQ2hFLGNBQWMsQ0FBQyxRQUE2QjtRQUNwRCxJQUFJLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsUUFBUSxJQUFJLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSx1QkFBVSxFQUFFLENBQUMsQ0FBQztJQUNoRixDQUFDO0lBRUQ7OztPQUdHO0lBQ08sc0JBQXNCO1FBQzlCLElBQUksQ0FBQyxJQUFJLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztZQUNsQyxJQUFJLENBQUMsdUJBQXVCLEdBQUcsSUFBSSxDQUFDO1lBQ3BDLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBQyxZQUFZLEVBQUMsRUFBRTtnQkFDekMsTUFBTSxNQUFNLEdBQUcsSUFBSSxnQkFBTSxDQUFDLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLFlBQVksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO2dCQUM1RSxJQUFJLENBQUMsbUJBQW1CLENBQUMsVUFBVSxDQUFDLElBQUksYUFBRyxFQUFFLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUM7WUFDakYsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUMsbUJBQW1CLENBQUM7SUFDbEMsQ0FBQztJQUVTLEtBQUssQ0FBQyxLQUFLLENBQUMsTUFBYztRQUNsQyxLQUFLLE1BQU0sSUFBSSxJQUFJLElBQUksQ0FBQyxZQUFZO1lBQUUsTUFBTSxJQUFJLEVBQUUsQ0FBQyxDQUFDLHVDQUF1QztRQUUzRixNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDN0IsQ0FBQztJQUVTLEtBQUssQ0FBQyxPQUFPLENBQUMsTUFBYztRQUNwQyxLQUFLLE1BQU0sSUFBSSxJQUFJLElBQUksQ0FBQyxXQUFXO1lBQUUsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyx1Q0FBdUM7SUFDbEcsQ0FBQztJQUVNLEtBQUssQ0FBQyxJQUFJO1FBQ2YsS0FBSyxNQUFNLElBQUksSUFBSSxJQUFJLENBQUMsTUFBTTtZQUFFLE1BQU0sSUFBSSxFQUFFLENBQUMsQ0FBQyx1Q0FBdUM7SUFDdkYsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNILHVCQUF1QixDQUFDLElBQWEsRUFBRSxJQUFhO1FBQ2xELE1BQU0sV0FBVyxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLFNBQVMsQ0FBQztRQUMxRCxNQUFNLFVBQVUsR0FBRyxJQUFJLElBQUksV0FBVyxJQUFJLElBQUksQ0FBQztRQUMvQyxJQUFJLENBQUMsb0JBQW9CLEdBQUcsSUFBSSxDQUFDO1FBQ2pDLE1BQU0sTUFBTSxHQUFHLElBQUEsbUJBQVksRUFBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUUzRCxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDMUIsT0FBTyxJQUFJLE9BQU8sQ0FBTyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtnQkFDM0MsTUFBTSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRTtvQkFDbkMsSUFBSSxDQUFDLG9CQUFvQixHQUFJLE1BQU0sQ0FBQyxPQUFPLEVBQXNCLENBQUMsSUFBSSxDQUFDO29CQUN2RSxJQUFJLENBQUMsTUFBTSxDQUNULE1BQU0sRUFDTixxREFBcUQsSUFBSSxJQUFJLFNBQVMsSUFDcEUsSUFBSSxDQUFDLG9CQUNQLEdBQUcsQ0FDSixDQUFDO29CQUVGLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssSUFBSSxFQUFFO3dCQUMxQixNQUFNLElBQUksT0FBTyxDQUFPLENBQUMsV0FBVyxFQUFFLFVBQVUsRUFBRSxFQUFFOzRCQUNsRCxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBUSxFQUFFLEVBQUU7Z0NBQ3hCLElBQUksR0FBRyxFQUFFLENBQUM7b0NBQ1IsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dDQUNsQixDQUFDO3FDQUFNLENBQUM7b0NBQ04sV0FBVyxFQUFFLENBQUM7Z0NBQ2hCLENBQUM7NEJBQ0gsQ0FBQyxDQUFDLENBQUM7d0JBQ0wsQ0FBQyxDQUFDLENBQUM7b0JBQ0wsQ0FBQyxDQUFDLENBQUM7b0JBRUgsT0FBTyxFQUFFLENBQUM7Z0JBQ1osQ0FBQyxDQUFDLENBQUM7Z0JBQ0gsTUFBTSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUM7WUFDN0IsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztRQUVILE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVEOzs7T0FHRztJQUNILGNBQWMsQ0FBQyxPQUFZO1FBQ3pCLG1HQUFtRztRQUNuRyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsb0JBQW9CLEVBQUUsQ0FBQyxDQUFDO1FBRXhELCtDQUErQztRQUMvQyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxJQUFJLENBQUMsa0JBQWtCLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUV0RSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxvQ0FBb0MsQ0FBQyxDQUFDO1FBRTFELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVEOzs7T0FHRztJQUNILGNBQWMsQ0FBQyxPQUFZO1FBQ3pCLHVFQUF1RTtRQUN2RSxJQUFJLENBQUMsY0FBYyxDQUFDLFdBQVcsQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLGNBQWMsQ0FBQyxvQkFBb0IsRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBRTFGLDJCQUEyQjtRQUMzQixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsa0JBQWtCLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDaEQsSUFBSSxDQUFDLGNBQWMsQ0FBQyxXQUFXLENBQUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxJQUFJLENBQUMsbUJBQW1CLENBQUMsQ0FBQztRQUU3RSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxpQ0FBaUMsQ0FBQyxDQUFDO1FBRXZELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVEOzs7T0FHRztJQUNILFVBQVUsQ0FBQyxHQUFRO1FBQ2pCLE1BQU0sWUFBWSxHQUFHLElBQUksZ0JBQU0sQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsbUJBQW1CLEVBQUUsQ0FBQyxDQUFDO1FBRXRFLHdFQUF3RTtRQUN4RSxZQUFZLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsRUFBRTtZQUMxQixHQUFHLENBQUMsUUFBUSxDQUFDLE1BQU0sR0FBRyxHQUFHLENBQUM7WUFDMUIsR0FBRyxDQUFDLFFBQVEsQ0FBQyxJQUFJLEdBQUcsRUFBRSxLQUFLLEVBQUUsc0JBQXNCLEVBQUUsQ0FBQztRQUN4RCxDQUFDLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBQyxZQUFZLEVBQUMsRUFBRTtZQUN6QywyQkFBMkI7WUFDM0IsWUFBWSxDQUFDLEtBQUssR0FBRyxFQUFFLENBQUM7WUFDeEIsa0JBQWtCO1lBQ2xCLFlBQVksQ0FBQyxHQUFHLENBQUMsWUFBWSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFDMUMsQ0FBQyxDQUFDLENBQUM7UUFFSCw2RUFBNkU7UUFDN0UsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLGdCQUFnQixFQUFFLENBQUMsQ0FBQztRQUNoRCxHQUFHLENBQUMsR0FBRyxDQUFDLFlBQVksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQy9CLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLDZCQUE2QixDQUFDLENBQUM7UUFFbkQsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsYUFBYSxDQUFDLE1BQVc7UUFDdkIsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ3hDLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUVoRCxJQUFJLE9BQU8sQ0FBQyxXQUFXLENBQUMsSUFBSSxLQUFLLGdCQUFnQixFQUFFLENBQUM7WUFDbEQsdUVBQXVFO1lBQ3ZFLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxvQkFBb0IsRUFBRSxDQUFDLENBQUM7WUFDdkQsMkJBQTJCO1lBQzNCLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLG1CQUFtQixFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ2pELENBQUM7YUFBTSxDQUFDO1lBQ04sMkNBQTJDO1lBQzNDLElBQUksQ0FBQyxjQUFjLENBQUMsV0FBVyxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsY0FBYyxDQUFDLG9CQUFvQixFQUFFLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDekYsSUFBSSxDQUFDLGNBQWMsQ0FBQyxXQUFXLENBQUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxJQUFJLENBQUMsbUJBQW1CLENBQUMsQ0FBQztRQUM5RSxDQUFDO1FBRUQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsZ0NBQWdDLENBQUMsQ0FBQztRQUV0RCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFTyxrQkFBa0IsQ0FBQyxNQUFlO1FBQ3hDLElBQUksT0FBTyxHQUEwQyxJQUFJLENBQUM7UUFFMUQsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFDLFlBQVksRUFBQyxFQUFFO1lBQ3pDLElBQUksTUFBTSxHQUFHLFlBQVksQ0FBQztZQUUxQixJQUFJLE1BQU0sRUFBRSxDQUFDO2dCQUNYLE1BQU0sR0FBRyxJQUFJLGdCQUFNLENBQUMsRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLG1CQUFtQixFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7WUFDakYsQ0FBQztZQUVELE9BQU8sR0FBRyxJQUFJLGFBQUcsRUFBRSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUN0RCxDQUFDLENBQUMsQ0FBQztRQUVILE9BQU8sQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEVBQUU7WUFDbEIsMkRBQTJEO1lBQzNELGtEQUFrRDtZQUNsRCxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBRXZELElBQUksTUFBTSxJQUFJLFlBQVksRUFBRSxDQUFDO2dCQUMzQixZQUFZLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUU7b0JBQzFCLGlFQUFpRTtvQkFDakUsSUFBSSxPQUFPLEVBQUUsQ0FBQzt3QkFDWixPQUFPLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO29CQUNwQixDQUFDO3lCQUFNLENBQUM7d0JBQ04sR0FBRyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxjQUFjLEVBQUUsa0JBQWtCLEVBQUUsQ0FBQyxDQUFDO3dCQUMzRCxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxLQUFLLEVBQUUsc0JBQXNCLEVBQUUsQ0FBQyxDQUFDLENBQUM7b0JBQzdELENBQUM7Z0JBQ0gsQ0FBQyxDQUFDLENBQUM7Z0JBRUgsT0FBTztZQUNULENBQUM7WUFFRCxJQUFJLE9BQU8sRUFBRSxDQUFDO2dCQUNaLE9BQU8sQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDcEIsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLEdBQUcsQ0FBQyxTQUFTLENBQUMsR0FBRyxFQUFFLEVBQUUsY0FBYyxFQUFFLGtCQUFrQixFQUFFLENBQUMsQ0FBQztnQkFDM0QsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsS0FBSyxFQUFFLHNCQUFzQixFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQzdELENBQUM7UUFDSCxDQUFDLENBQUM7SUFDSixDQUFDO0NBQ0Y7QUFuUEQsbUNBbVBDIn0=
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { TSchema } from '@forestadmin/datasource-customizer';
|
|
|
3
3
|
import Agent from './agent';
|
|
4
4
|
export declare function createAgent<S extends TSchema = TSchema>(options: AgentOptions): Agent<S>;
|
|
5
5
|
export { Agent };
|
|
6
|
-
export { AgentOptions, WorkflowExecutorEmbedOptions } from './types';
|
|
6
|
+
export { AgentOptions, BffEmbedOptions, WorkflowExecutorEmbedOptions } from './types';
|
|
7
7
|
export * from '@forestadmin/datasource-customizer';
|
|
8
8
|
export { default as SchemaGenerator } from './utils/forest-schema/generator';
|
|
9
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { HttpCallback, RootHandler } from './types';
|
|
2
|
+
import type { Logger } from '@forestadmin/datasource-toolkit';
|
|
3
|
+
import type Koa from 'koa';
|
|
4
|
+
/**
|
|
5
|
+
* Handlers the agent serves at the root of the host application rather than under its own router
|
|
6
|
+
* prefix — the MCP server, the embedded BFF. Each one claims a set of paths through its matcher;
|
|
7
|
+
* anything else falls through to the host, untouched.
|
|
8
|
+
*
|
|
9
|
+
* Registered by name so a handler can be replaced or removed on a restart without the mount points
|
|
10
|
+
* having to know what is registered.
|
|
11
|
+
*/
|
|
12
|
+
export default class RootMiddleware {
|
|
13
|
+
private readonly handlers;
|
|
14
|
+
private readonly logger;
|
|
15
|
+
constructor(logger: Logger);
|
|
16
|
+
set(name: string, handler: RootHandler | null): void;
|
|
17
|
+
private entryFor;
|
|
18
|
+
/**
|
|
19
|
+
* First registered matcher wins. Overlapping matchers cannot be caught at registration — the
|
|
20
|
+
* predicates are opaque — so the winner is named here instead: a request answered by the wrong
|
|
21
|
+
* handler is otherwise indistinguishable from one whose route was never registered.
|
|
22
|
+
*/
|
|
23
|
+
private handlerFor;
|
|
24
|
+
/**
|
|
25
|
+
* Connect-style middleware. A handler that decides not to answer calls `next()`, which passes the
|
|
26
|
+
* request on to the host exactly as if nothing had been registered.
|
|
27
|
+
*/
|
|
28
|
+
getExpressMiddleware(): HttpCallback;
|
|
29
|
+
getKoaMiddleware(): Koa.Middleware;
|
|
30
|
+
/** The combined callback, or null when nothing is registered and the host needs no detour. */
|
|
31
|
+
getCallback(): HttpCallback | null;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=root-middleware.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const express_to_koa_1 = __importDefault(require("./utils/express-to-koa"));
|
|
7
|
+
/**
|
|
8
|
+
* Handlers the agent serves at the root of the host application rather than under its own router
|
|
9
|
+
* prefix — the MCP server, the embedded BFF. Each one claims a set of paths through its matcher;
|
|
10
|
+
* anything else falls through to the host, untouched.
|
|
11
|
+
*
|
|
12
|
+
* Registered by name so a handler can be replaced or removed on a restart without the mount points
|
|
13
|
+
* having to know what is registered.
|
|
14
|
+
*/
|
|
15
|
+
class RootMiddleware {
|
|
16
|
+
constructor(logger) {
|
|
17
|
+
this.handlers = new Map();
|
|
18
|
+
this.logger = logger;
|
|
19
|
+
}
|
|
20
|
+
set(name, handler) {
|
|
21
|
+
if (handler)
|
|
22
|
+
this.handlers.set(name, handler);
|
|
23
|
+
else
|
|
24
|
+
this.handlers.delete(name);
|
|
25
|
+
}
|
|
26
|
+
entryFor(url) {
|
|
27
|
+
for (const entry of this.handlers) {
|
|
28
|
+
if (entry[1].matches(url))
|
|
29
|
+
return entry;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* First registered matcher wins. Overlapping matchers cannot be caught at registration — the
|
|
35
|
+
* predicates are opaque — so the winner is named here instead: a request answered by the wrong
|
|
36
|
+
* handler is otherwise indistinguishable from one whose route was never registered.
|
|
37
|
+
*/
|
|
38
|
+
handlerFor(url) {
|
|
39
|
+
const entry = this.entryFor(url);
|
|
40
|
+
if (!entry)
|
|
41
|
+
return null;
|
|
42
|
+
this.logger('Debug', `Root middleware: '${entry[0]}' claims ${url}`);
|
|
43
|
+
return entry[1].callback;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Connect-style middleware. A handler that decides not to answer calls `next()`, which passes the
|
|
47
|
+
* request on to the host exactly as if nothing had been registered.
|
|
48
|
+
*/
|
|
49
|
+
getExpressMiddleware() {
|
|
50
|
+
return (req, res, next) => {
|
|
51
|
+
const handler = this.handlerFor(req.url ?? '/');
|
|
52
|
+
if (handler) {
|
|
53
|
+
handler(req, res, next);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
next?.();
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
getKoaMiddleware() {
|
|
60
|
+
return (0, express_to_koa_1.default)(this.getExpressMiddleware(), url => this.entryFor(url) !== null);
|
|
61
|
+
}
|
|
62
|
+
/** The combined callback, or null when nothing is registered and the host needs no detour. */
|
|
63
|
+
getCallback() {
|
|
64
|
+
if (this.handlers.size === 0)
|
|
65
|
+
return null;
|
|
66
|
+
return this.getExpressMiddleware();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.default = RootMiddleware;
|
|
70
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm9vdC1taWRkbGV3YXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3Jvb3QtbWlkZGxld2FyZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUlBLDRFQUFrRDtBQUVsRDs7Ozs7OztHQU9HO0FBQ0gsTUFBcUIsY0FBYztJQUlqQyxZQUFZLE1BQWM7UUFIVCxhQUFRLEdBQUcsSUFBSSxHQUFHLEVBQXVCLENBQUM7UUFJekQsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7SUFDdkIsQ0FBQztJQUVELEdBQUcsQ0FBQyxJQUFZLEVBQUUsT0FBMkI7UUFDM0MsSUFBSSxPQUFPO1lBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDOztZQUN6QyxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNsQyxDQUFDO0lBRU8sUUFBUSxDQUFDLEdBQVc7UUFDMUIsS0FBSyxNQUFNLEtBQUssSUFBSSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDbEMsSUFBSSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQztnQkFBRSxPQUFPLEtBQUssQ0FBQztRQUMxQyxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNLLFVBQVUsQ0FBQyxHQUFXO1FBQzVCLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUM7UUFFakMsSUFBSSxDQUFDLEtBQUs7WUFBRSxPQUFPLElBQUksQ0FBQztRQUV4QixJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxxQkFBcUIsS0FBSyxDQUFDLENBQUMsQ0FBQyxZQUFZLEdBQUcsRUFBRSxDQUFDLENBQUM7UUFFckUsT0FBTyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDO0lBQzNCLENBQUM7SUFFRDs7O09BR0c7SUFDSCxvQkFBb0I7UUFDbEIsT0FBTyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLEVBQUU7WUFDeEIsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDO1lBRWhELElBQUksT0FBTyxFQUFFLENBQUM7Z0JBQ1osT0FBTyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUM7Z0JBRXhCLE9BQU87WUFDVCxDQUFDO1lBRUQsSUFBSSxFQUFFLEVBQUUsQ0FBQztRQUNYLENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRCxnQkFBZ0I7UUFDZCxPQUFPLElBQUEsd0JBQVksRUFBQyxJQUFJLENBQUMsb0JBQW9CLEVBQUUsRUFBRSxHQUFHLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEtBQUssSUFBSSxDQUFDLENBQUM7SUFDdkYsQ0FBQztJQUVELDhGQUE4RjtJQUM5RixXQUFXO1FBQ1QsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksS0FBSyxDQUFDO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFFMUMsT0FBTyxJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztJQUNyQyxDQUFDO0NBQ0Y7QUFoRUQsaUNBZ0VDIn0=
|
package/dist/types.d.ts
CHANGED
|
@@ -151,6 +151,47 @@ export type WorkflowExecutorEmbedOptions = Omit<WorkflowExecutorTuningOptions, '
|
|
|
151
151
|
*/
|
|
152
152
|
encryptionKey?: string;
|
|
153
153
|
};
|
|
154
|
+
/**
|
|
155
|
+
* Options of an embedded BFF. Everything the BFF shares with the agent — the secrets, the Forest
|
|
156
|
+
* urls, the logger — is inherited rather than repeated: a divergent `authSecret` would make the
|
|
157
|
+
* agent reject the very tokens the BFF mints, as an opaque 401.
|
|
158
|
+
*/
|
|
159
|
+
export type BffEmbedOptions = {
|
|
160
|
+
/**
|
|
161
|
+
* Base64-encoded 32-byte AES-256 key encrypting stored refresh tokens. Gates the OAuth
|
|
162
|
+
* login/refresh flow (mode 1) and, with it, the AI relay.
|
|
163
|
+
*
|
|
164
|
+
* It is not an access control. The data routes accept a `bff_access` bearer whether or not this
|
|
165
|
+
* is set, because that token is verified against the agent's `authSecret` alone. Omitting it
|
|
166
|
+
* closes the login flow, not the session-bearer path — what protects that path is `authSecret`.
|
|
167
|
+
*/
|
|
168
|
+
tokenEncryptionKey?: string;
|
|
169
|
+
/**
|
|
170
|
+
* Exact origins (scheme + host + port) allowed to call the BFF from a browser. No wildcard.
|
|
171
|
+
* Empty means no cross-origin browser access at all, which for a backend-for-frontend is
|
|
172
|
+
* almost always a mistake — the BFF warns about it at startup.
|
|
173
|
+
*/
|
|
174
|
+
allowedOrigins?: string[];
|
|
175
|
+
/** Fallback IANA timezone for a request that carries none. */
|
|
176
|
+
defaultTimezone?: string;
|
|
177
|
+
/**
|
|
178
|
+
* How long a call to the agent may take. Defaults to 10s.
|
|
179
|
+
*
|
|
180
|
+
* It bounds how long the BFF waits, and cancels nothing: an action or a write cut off at the
|
|
181
|
+
* deadline keeps running inside the agent and still commits, so a client retrying on the timeout
|
|
182
|
+
* can double the mutation. Raise it rather than relying on a retry for operations that are not
|
|
183
|
+
* idempotent.
|
|
184
|
+
*/
|
|
185
|
+
agentTimeoutMs?: number;
|
|
186
|
+
/** How long the AI relay waits for the Forest server. Defaults to 120s. */
|
|
187
|
+
aiTimeoutMs?: number;
|
|
188
|
+
/**
|
|
189
|
+
* Serve `/bff/docs` and `/bff/agent/openapi.json`. Defaults to `false` when embedded: the
|
|
190
|
+
* document is not filtered per caller, so any authenticated caller reads the name of every
|
|
191
|
+
* exposed collection, relation and field.
|
|
192
|
+
*/
|
|
193
|
+
openapiEnabled?: boolean;
|
|
194
|
+
};
|
|
154
195
|
export type AuditTrailRuntime = AuditTrailConfig & {
|
|
155
196
|
store: AuditStore;
|
|
156
197
|
close: () => Promise<void>;
|
|
@@ -159,7 +200,12 @@ export type AgentOptionsWithDefaults = Readonly<Required<Omit<AgentOptions, 'aud
|
|
|
159
200
|
auditTrail: AuditTrailRuntime | null;
|
|
160
201
|
}>;
|
|
161
202
|
export type HttpCallback = (req: IncomingMessage, res: ServerResponse, next?: () => void) => void;
|
|
162
|
-
export type
|
|
203
|
+
export type RouteMatcher = (url: string) => boolean;
|
|
204
|
+
/** A handler served at the root of the host application, with the urls it claims. */
|
|
205
|
+
export type RootHandler = {
|
|
206
|
+
callback: HttpCallback;
|
|
207
|
+
matches: RouteMatcher;
|
|
208
|
+
};
|
|
163
209
|
export declare enum HttpCode {
|
|
164
210
|
BadRequest = 400,
|
|
165
211
|
Unauthorized = 401,
|
package/dist/types.js
CHANGED
|
@@ -22,4 +22,4 @@ var RouteType;
|
|
|
22
22
|
RouteType[RouteType["Authentication"] = 3] = "Authentication";
|
|
23
23
|
RouteType[RouteType["PrivateRoute"] = 4] = "PrivateRoute";
|
|
24
24
|
})(RouteType || (exports.RouteType = RouteType = {}));
|
|
25
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
25
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBbU5BLElBQVksUUFVWDtBQVZELFdBQVksUUFBUTtJQUNsQixxREFBZ0IsQ0FBQTtJQUNoQix5REFBa0IsQ0FBQTtJQUNsQixtREFBZSxDQUFBO0lBQ2YsaURBQWMsQ0FBQTtJQUNkLDJEQUFtQixDQUFBO0lBQ25CLCtEQUFxQixDQUFBO0lBQ3JCLHVFQUF5QixDQUFBO0lBQ3pCLG1EQUFlLENBQUE7SUFDZixxQ0FBUSxDQUFBO0FBQ1YsQ0FBQyxFQVZXLFFBQVEsd0JBQVIsUUFBUSxRQVVuQjtBQUVELElBQVksU0FPWDtBQVBELFdBQVksU0FBUztJQUNuQixpR0FBaUc7SUFDakcsMkRBQWlCLENBQUE7SUFDakIseURBQWdCLENBQUE7SUFDaEIsdURBQWUsQ0FBQTtJQUNmLDZEQUFrQixDQUFBO0lBQ2xCLHlEQUFnQixDQUFBO0FBQ2xCLENBQUMsRUFQVyxTQUFTLHlCQUFULFNBQVMsUUFPcEIifQ==
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forestadmin/agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.100.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
6
|
"publishConfig": {
|
|
@@ -49,35 +49,42 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@fastify/express": "^1.1.0",
|
|
52
|
+
"@forestadmin/agent-bff": "1.29.0",
|
|
52
53
|
"@forestadmin/datasource-sql": "1.17.14",
|
|
53
|
-
"@forestadmin/workflow-executor": "1.28.
|
|
54
|
+
"@forestadmin/workflow-executor": "1.28.1",
|
|
54
55
|
"@nestjs/common": "^11.1.24",
|
|
55
56
|
"@nestjs/core": "^11.1.24",
|
|
56
57
|
"@nestjs/platform-express": "^11.1.24",
|
|
57
58
|
"@nestjs/platform-fastify": "^11.1.24",
|
|
58
59
|
"@shopify/jest-koa-mocks": "^3.1.0",
|
|
59
|
-
"sqlite3": "^5.1.7",
|
|
60
60
|
"@types/json-api-serializer": "^2.6.3",
|
|
61
61
|
"@types/jsonwebtoken": "^9.0.1",
|
|
62
62
|
"@types/koa": "^2.13.5",
|
|
63
63
|
"@types/koa__cors": "^3.3.0",
|
|
64
64
|
"@types/superagent": "^8.1.9",
|
|
65
|
+
"@types/supertest": "^6.0.2",
|
|
65
66
|
"express": "^4.18.2",
|
|
66
67
|
"fastify": "^3.29.0",
|
|
67
68
|
"fastify2": "npm:fastify@^2.15.3",
|
|
68
69
|
"fastify4": "npm:fastify@^4.28.0",
|
|
69
70
|
"openid-client": "^5.7.1",
|
|
70
71
|
"reflect-metadata": "^0.1.13",
|
|
71
|
-
"rxjs": "^7.8.0"
|
|
72
|
+
"rxjs": "^7.8.0",
|
|
73
|
+
"sqlite3": "^5.1.7",
|
|
74
|
+
"supertest": "^7.1.3"
|
|
72
75
|
},
|
|
73
76
|
"peerDependencies": {
|
|
74
77
|
"@fastify/express": "^1.1.0 || ^2.0.0 || ^3.0.0 || ^4.0.0",
|
|
75
|
-
"@forestadmin/
|
|
78
|
+
"@forestadmin/agent-bff": "1.29.0",
|
|
79
|
+
"@forestadmin/workflow-executor": "1.28.1"
|
|
76
80
|
},
|
|
77
81
|
"peerDependenciesMeta": {
|
|
78
82
|
"@fastify/express": {
|
|
79
83
|
"optional": true
|
|
80
84
|
},
|
|
85
|
+
"@forestadmin/agent-bff": {
|
|
86
|
+
"optional": true
|
|
87
|
+
},
|
|
81
88
|
"@forestadmin/workflow-executor": {
|
|
82
89
|
"optional": true
|
|
83
90
|
}
|