@forestadmin/agent-bff 1.28.1 → 1.30.0

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 CHANGED
@@ -33,6 +33,9 @@ dropped collection reachable.
33
33
 
34
34
  ## Usage
35
35
 
36
+ Two ways to run it: embedded in a Forest agent (`agent.addBff()`, see
37
+ [Embedded in an agent](#embedded-in-an-agent)) or standalone, described here.
38
+
36
39
  Packaged / production — run the bin:
37
40
 
38
41
  ```bash
@@ -95,7 +98,7 @@ yarn start:dev # node --env-file=.env dist/cli.js
95
98
  | `FOREST_SERVER_URL` | yes | Forest SaaS API base URL. |
96
99
  | `FOREST_APP_URL` | yes | Forest front base URL, used to build the OAuth front-channel redirect (`src/oauth/oauth-routes.ts`). |
97
100
  | `AGENT_URL` | yes | The customer agent base URL the BFF calls via agent-client. |
98
- | `BFF_TOKEN_ENCRYPTION_KEY` | for OAuth | Base64-encoded 32-byte AES-256 key encrypting stored refresh tokens. Until it is set, the `/oauth/*` token-issuance routes are disabled and `/health` reports `degraded`; already-issued `bff_access` tokens still authenticate on `/agent/*` whenever `FOREST_AUTH_SECRET` is present. |
101
+ | `BFF_TOKEN_ENCRYPTION_KEY` | for OAuth | Base64-encoded 32-byte AES-256 key encrypting stored refresh tokens. Until it is set, the `/oauth/*` token-issuance routes are disabled and `/health` reports `configured.oauth: false` — but it stays `ok`, since the key gates OAuth and not boot; already-issued `bff_access` tokens still authenticate on `/agent/*` whenever `FOREST_AUTH_SECRET` is present. |
99
102
  | `HTTP_PORT` | no | Server port, integer 0–65535. Defaults to `3450`. `0` binds an OS-assigned ephemeral port. |
100
103
  | `BFF_ALLOWED_ORIGINS` | no | Comma-separated CORS allow-list of exact origins (scheme + host + port). No wildcard. Empty ⇒ no cross-origin browser access. |
101
104
  | `BFF_DEFAULT_TIMEZONE` | no | Fallback IANA timezone used when a request carries neither an `X-Forest-Timezone` header nor a body `timezone`. |
@@ -159,9 +162,12 @@ Two layers, both driven by exact-origin matching (case-insensitive scheme/host,
159
162
  normalized away, no trailing slash, no wildcard, no subdomain matching):
160
163
 
161
164
  - **Layer 1 (transport)** — the only layer that sets `Access-Control-Allow-Origin`. An origin in
162
- `BFF_ALLOWED_ORIGINS` is echoed back exactly; anything else gets no CORS headers (the browser
163
- blocks). Applies to `POST /oauth/token` too. Preflight `OPTIONS` from an allow-listed origin gets
164
- the allowed methods + headers; credentials are never enabled.
165
+ `BFF_ALLOWED_ORIGINS` is echoed back exactly; any other `Origin` is refused outright with
166
+ `403 origin_not_allowed`, rather than served without the header and left for the browser to
167
+ discard — omitting the header only stops the caller from *reading* the answer, and the request
168
+ would already have run. A request with no `Origin` at all is untouched, which is every
169
+ server-to-server api-key call. Applies to `POST /oauth/token` too. Preflight `OPTIONS` from an
170
+ allow-listed origin gets the allowed methods + headers; credentials are never enabled.
165
171
  - **Layer 2 (per-key authorization, Mode 2 only)** — when the resolved key has a non-empty
166
172
  `allowedOrigins`, an `Origin` sent by the client must be in that list, else
167
173
  `403 origin_not_allowed`. A request with no `Origin` at all — a server-side client such as curl,
@@ -230,11 +236,112 @@ with `session_expired`. Horizontal scaling requires both a shared session store
230
236
 
231
237
  ```jsonc
232
238
  // 200 — all required config present
233
- { "status": "ok", "version": "<package version>" }
239
+ {
240
+ "status": "ok",
241
+ "version": "<package version>",
242
+ "configured": { "oauth": true, "ai": true, "cors": true, "openapi": true }
243
+ }
234
244
  // 503 — one or more required keys missing
235
- { "status": "degraded", "version": "<package version>" }
245
+ { "status": "degraded", "version": "<package version>", "configured": { /* … */ } }
246
+ ```
247
+
248
+ `configured` says which optional surfaces this deployment was set up to serve — deliberately not
249
+ that they work: `oauth` is `true` as soon as a key is set, whether or not the Forest server ever
250
+ answers. They form a chain, not four independent switches:
251
+
252
+ | Surface | Switched on by |
253
+ | --- | --- |
254
+ | `oauth` | `BFF_TOKEN_ENCRYPTION_KEY` **and** `FOREST_SERVER_URL`, `FOREST_ENV_SECRET`, `FOREST_APP_URL`, `FOREST_AUTH_SECRET` — the routes need a Forest server to talk to as much as a key. Embedded, only `tokenEncryptionKey` is yours to set: the other four are inherited |
255
+ | `ai` | `oauth` — the relay needs a session, and only the OAuth flow creates one |
256
+ | `cors` | a non-empty `BFF_ALLOWED_ORIGINS` (`allowedOrigins`) |
257
+ | `openapi` | `BFF_OPENAPI_ENABLED` (`openapiEnabled`), and a mounted agent edge |
258
+
259
+ The body still never discloses which config *keys* are present or missing — that would leak the
260
+ internal config surface to an unauthenticated probe. It names the surfaces, never the values behind
261
+ them. Missing keys are logged once at startup (`Warn`) for operators. Every response the BFF
262
+ itself produces carries the `X-Forest-Bff-Version` header, read from `package.json`. The one
263
+ exception is the embedded `503 bff_not_started` / `bff_stopped` below: the agent writes those
264
+ itself, with no BFF to read a version from.
265
+
266
+ ## Embedded in an agent
267
+
268
+ The same BFF runs inside a Forest agent, with no second deployment and no second port:
269
+
270
+ ```ts
271
+ await createAgent(options)
272
+ .addDataSource(/* … */)
273
+ .addBff({ allowedOrigins: ['https://my-app.com'] })
274
+ .mountOnStandaloneServer(3351)
275
+ .start();
276
+ ```
277
+
278
+ A mount is what opens a socket — `start()` only builds the agent's router, so without a
279
+ `mountOn*` call nothing is served, BFF included.
280
+
281
+ It answers under `/bff` on whatever port the agent is mounted on — `/bff/agent/v1/{collection}/list`,
282
+ `/bff/health`, `/bff/oauth/*` — so the REST contract is the one documented above and only the base
283
+ url changes.
284
+
285
+ A host serving the agent under a sub-path of its own is supported: `app.use('/api', mounted)` puts
286
+ the BFF at `/api/bff`, and the emitted prefix is derived per request from `originalUrl`
287
+ (`base-path.ts`), so the `servers` entry and the docs page carry `/api/bff` rather than the
288
+ configured `/bff`.
289
+
290
+ The *served* OpenAPI document carries the prefix in its `servers` entry, so a client generated from
291
+ `GET /bff/agent/openapi.json` is correct as-is. The *exported* one does not: `forest-bff openapi`
292
+ has no way to be told about a prefix and always emits `servers: [{ "url": "/" }]`. Generating a
293
+ client from the export against an embedded BFF means pointing its base url at `/bff` yourself.
294
+
295
+ What differs from the standalone deployment:
296
+
297
+ | | Standalone | Embedded |
298
+ | --- | --- | --- |
299
+ | Configuration | environment variables | `addBff()` options; the secrets, the Forest urls and the logger are inherited from the agent and cannot be overridden |
300
+ | `AGENT_URL` | required, an http(s) url | gone — the BFF reaches the agent in the same process, without a socket |
301
+ | `HTTP_PORT` | its own listener | gone — the agent's port serves it |
302
+ | `openapiEnabled` | `true` | `false`. The document is not filtered per caller, so adding a BFF must not silently publish every collection and field name on an already-open port |
303
+ | `/health` | 503 until every required key is set | always 200: an in-process dispatcher short-circuits the readiness check, since a 503 here would let a load balancer restart a process that serves api-key traffic fine. It stays 200 with no `tokenEncryptionKey`, which is *not* inherited — read `configured` to know what is on |
304
+
305
+ **Registration order matters on Express and Connect-style hosts.** Mount the agent *before* any
306
+ body parser of your own:
307
+
308
+ ```ts
309
+ const app = express();
310
+ agent.mountOnExpress(app); // first
311
+ app.use(express.json()); // then yours
312
+ await agent.start();
236
313
  ```
237
314
 
238
- The body never discloses which config keys are present or missing — that would leak the internal
239
- config surface to an unauthenticated probe. Missing keys are logged once at startup (`Warn`) for
240
- operators. Every response carries the `X-Forest-Bff-Version` header, read from `package.json`.
315
+ A body parser that runs first has already consumed the request stream, and nothing downstream can
316
+ put it back: every BFF `POST` then answers `500 stream.not.readable`.
317
+
318
+ A permissive `cors()` registered ahead of the mount costs the preflight, not the allow-list. The
319
+ host answers `OPTIONS` with its own policy and that is unrecoverable, but the request that follows
320
+ still reaches this app, where an `Origin` outside `BFF_ALLOWED_ORIGINS` is refused with
321
+ `403 origin_not_allowed` — the browser reads the wrong preflight, and the collection is still never
322
+ read for that origin. A caller sending no `Origin` at all is untouched, which is every
323
+ server-to-server api-key call.
324
+
325
+ NestJS needs no special handling as long as you mount before `listen()`: `NestFactory.create()`
326
+ does not install its body parser, `init()` does, and `listen()` is what triggers `init()` — so the
327
+ middleware `mountOnNestJs()` registers is already ahead of it.
328
+
329
+ **`/bff/*` answers `503` while the BFF is not serving**, and the two ends of the lifecycle are told
330
+ apart: `bff_not_started` between `addBff()` and the end of `start()`, `bff_stopped` after `stop()`.
331
+ The agent claims the prefix as soon as it is mounted, and a 404 there would read as "wrong url"
332
+ rather than "not available". A probe should wait on the first and drain on the second.
333
+
334
+ **`addBff()` and `mountAiMcpServer({ basePath: '/bff' })` are mutually exclusive** — whichever is
335
+ called second throws on the spot, at the builder call and not from `start()`, rather than letting
336
+ the MCP server quietly claim `/bff/oauth` and `/bff/mcp`. Any `basePath` landing inside `/bff`
337
+ counts (`bff`, `/bff/`, `/bff/ai`), so mount the MCP server elsewhere.
338
+
339
+ **One security control does not apply to the embedded mode.** If the environment has Forest's IP
340
+ whitelist enabled, requests served under `/bff` are not subject to it: they reach the agent
341
+ in-process, over what the whitelist treats as a trusted loopback caller. This is deliberate —
342
+ applying it would filter the browsers a third-party UI is made of, and the standalone deployment
343
+ never filtered the end user either, only the BFF's own host. A resolved API key or a valid OAuth
344
+ session is still required, and the agent still logs a warning at startup naming the exemption.
345
+
346
+ When to prefer which: embedded for a single deployment, which is most of them. Standalone when
347
+ several agents share one BFF, or when the BFF and the agent have to scale separately.
@@ -1,4 +1,6 @@
1
1
  import type { AgentTransport } from './agent-transport';
2
+ /** A sentinel that never reaches the network: `query` answers before any socket is opened. */
3
+ export declare const IN_PROCESS_AGENT_URL = "http://in-process.agent";
2
4
  export interface AgentDispatchRequest {
3
5
  method: 'get' | 'post' | 'put' | 'delete';
4
6
  path: string;
@@ -1,17 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IN_PROCESS_AGENT_URL = void 0;
3
4
  exports.default = createInProcessTransport;
4
5
  const agent_client_1 = require("@forestadmin/agent-client");
5
6
  const bff_local_errors_1 = require("../http/bff-local-errors");
6
7
  /** A sentinel that never reaches the network: `query` answers before any socket is opened. */
7
- const IN_PROCESS_URL = 'http://in-process.agent';
8
+ exports.IN_PROCESS_AGENT_URL = 'http://in-process.agent';
8
9
  /**
9
10
  * Reaches the agent through the dispatcher instead of the network, reusing `HttpRequester`'s parse
10
11
  * helpers so results and error shape stay identical to the HTTP path.
11
12
  */
12
13
  class InProcessRequester extends agent_client_1.HttpRequester {
13
14
  constructor(bearerToken, dispatcher, defaultTimeoutMs) {
14
- super(bearerToken, { url: IN_PROCESS_URL });
15
+ super(bearerToken, { url: exports.IN_PROCESS_AGENT_URL });
15
16
  this.bearerToken = bearerToken;
16
17
  this.dispatcher = dispatcher;
17
18
  this.defaultTimeoutMs = defaultTimeoutMs;
@@ -60,18 +61,18 @@ class InProcessRequester extends agent_client_1.HttpRequester {
60
61
  * enough: `escapeUrlSlug` prefixes `+?*` with a backslash, which the WHATWG parser `buildUrl`
61
62
  * feeds reads as a path separator, and that parse also resolves `..` and splits a trailing query
62
63
  * off. Running it here is what keeps a given id addressing the same record over both transports —
63
- * both remain wrong for those three characters, which is PRD-1124's to fix on both at once.
64
+ * both remain wrong for those three characters — TODO(PRD-1124), to fix on both at once.
64
65
  */
65
66
  static toDispatchTarget(path) {
66
67
  const normalized = path.startsWith('/') ? path : `/${path}`;
67
- const url = new URL(`${IN_PROCESS_URL}${agent_client_1.HttpRequester.escapeUrlSlug(normalized)}`);
68
+ const url = new URL(`${exports.IN_PROCESS_AGENT_URL}${agent_client_1.HttpRequester.escapeUrlSlug(normalized)}`);
68
69
  return { path: url.pathname, query: Object.fromEntries(url.searchParams) };
69
70
  }
70
71
  }
71
72
  function createInProcessTransport({ dispatcher, timeoutMs, }) {
72
73
  return {
73
- url: IN_PROCESS_URL,
74
+ url: exports.IN_PROCESS_AGENT_URL,
74
75
  createRequester: token => new InProcessRequester(token, dispatcher, timeoutMs),
75
76
  };
76
77
  }
77
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW4tcHJvY2Vzcy10cmFuc3BvcnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWdlbnQvaW4tcHJvY2Vzcy10cmFuc3BvcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUEwSUEsMkNBUUM7QUFoSkQsNERBQTBEO0FBRTFELCtEQUFnRTtBQUVoRSw4RkFBOEY7QUFDOUYsTUFBTSxjQUFjLEdBQUcseUJBQXlCLENBQUM7QUErQmpEOzs7R0FHRztBQUNILE1BQU0sa0JBQW1CLFNBQVEsNEJBQWE7SUFDNUMsWUFDbUIsV0FBbUIsRUFDbkIsVUFBMkIsRUFDM0IsZ0JBQXlCO1FBRTFDLEtBQUssQ0FBQyxXQUFXLEVBQUUsRUFBRSxHQUFHLEVBQUUsY0FBYyxFQUFFLENBQUMsQ0FBQztRQUozQixnQkFBVyxHQUFYLFdBQVcsQ0FBUTtRQUNuQixlQUFVLEdBQVYsVUFBVSxDQUFpQjtRQUMzQixxQkFBZ0IsR0FBaEIsZ0JBQWdCLENBQVM7SUFHNUMsQ0FBQztJQUVELGdHQUFnRztJQUNoRyxnR0FBZ0c7SUFDaEcsd0RBQXdEO0lBQy9DLEtBQUssQ0FBQyxNQUFNO1FBQ25CLE1BQU0sSUFBQSx1Q0FBb0IsRUFBQywwREFBMEQsQ0FBQyxDQUFDO0lBQ3pGLENBQUM7SUFFUSxLQUFLLENBQUMsS0FBSyxDQUFpQixFQUNuQyxNQUFNLEVBQ04sSUFBSSxFQUNKLElBQUksRUFDSixLQUFLLEVBQ0wsY0FBYyxFQUNkLFdBQVcsRUFDWCxtQkFBbUIsR0FTcEI7UUFDQyxNQUFNLE1BQU0sR0FBRyxrQkFBa0IsQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUV6RCxNQUFNLEVBQ0osTUFBTSxFQUNOLElBQUksRUFBRSxZQUFZLEVBQ2xCLElBQUksR0FDTCxHQUFHLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQztZQUN0QixNQUFNO1lBQ04sSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJO1lBQ2pCLE9BQU8sRUFBRTtnQkFDUCxhQUFhLEVBQUUsVUFBVSxJQUFJLENBQUMsV0FBVyxFQUFFO2dCQUMzQyxjQUFjLEVBQUUsV0FBVyxJQUFJLGtCQUFrQjtnQkFDakQsTUFBTSxFQUFFLFdBQVcsSUFBSSxrQkFBa0I7YUFDMUM7WUFDRCxLQUFLLEVBQUUsRUFBRSxRQUFRLEVBQUUsY0FBYyxFQUFFLEdBQUcsTUFBTSxDQUFDLEtBQUssRUFBRSxHQUFHLEtBQUssRUFBRTtZQUM5RCxPQUFPLEVBQUUsSUFBSTtZQUNiLFNBQVMsRUFBRSxjQUFjLElBQUksSUFBSSxDQUFDLGdCQUFnQjtTQUNuRCxDQUFDLENBQUM7UUFFSCxJQUFJLE1BQU0sSUFBSSxHQUFHO1lBQUUsTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxZQUFZLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFFckUsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFPLFlBQVksRUFBRSxJQUFJLEVBQUUsbUJBQW1CLENBQUMsQ0FBQztJQUN6RSxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNLLEtBQUssQ0FBQyxRQUFRLENBQUMsT0FBNkI7UUFDbEQsSUFBSSxDQUFDO1lBQ0gsT0FBTyxNQUFNLElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2hELENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsTUFBTSxNQUFNLEdBQUcsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEtBQUssSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7WUFFckYsTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLEdBQUcsRUFBRSxFQUFFLE1BQU0sRUFBRSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDdkQsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0ssTUFBTSxDQUFDLGdCQUFnQixDQUFDLElBQVk7UUFJMUMsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksRUFBRSxDQUFDO1FBQzVELE1BQU0sR0FBRyxHQUFHLElBQUksR0FBRyxDQUFDLEdBQUcsY0FBYyxHQUFHLDRCQUFhLENBQUMsYUFBYSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUVuRixPQUFPLEVBQUUsSUFBSSxFQUFFLEdBQUcsQ0FBQyxRQUFRLEVBQUUsS0FBSyxFQUFFLE1BQU0sQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLFlBQVksQ0FBQyxFQUFFLENBQUM7SUFDN0UsQ0FBQztDQUNGO0FBT0QsU0FBd0Isd0JBQXdCLENBQUMsRUFDL0MsVUFBVSxFQUNWLFNBQVMsR0FDaUI7SUFDMUIsT0FBTztRQUNMLEdBQUcsRUFBRSxjQUFjO1FBQ25CLGVBQWUsRUFBRSxLQUFLLENBQUMsRUFBRSxDQUFDLElBQUksa0JBQWtCLENBQUMsS0FBSyxFQUFFLFVBQVUsRUFBRSxTQUFTLENBQUM7S0FDL0UsQ0FBQztBQUNKLENBQUMifQ==
78
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW4tcHJvY2Vzcy10cmFuc3BvcnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWdlbnQvaW4tcHJvY2Vzcy10cmFuc3BvcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBMElBLDJDQVFDO0FBaEpELDREQUEwRDtBQUUxRCwrREFBZ0U7QUFFaEUsOEZBQThGO0FBQ2pGLFFBQUEsb0JBQW9CLEdBQUcseUJBQXlCLENBQUM7QUErQjlEOzs7R0FHRztBQUNILE1BQU0sa0JBQW1CLFNBQVEsNEJBQWE7SUFDNUMsWUFDbUIsV0FBbUIsRUFDbkIsVUFBMkIsRUFDM0IsZ0JBQXlCO1FBRTFDLEtBQUssQ0FBQyxXQUFXLEVBQUUsRUFBRSxHQUFHLEVBQUUsNEJBQW9CLEVBQUUsQ0FBQyxDQUFDO1FBSmpDLGdCQUFXLEdBQVgsV0FBVyxDQUFRO1FBQ25CLGVBQVUsR0FBVixVQUFVLENBQWlCO1FBQzNCLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBUztJQUc1QyxDQUFDO0lBRUQsZ0dBQWdHO0lBQ2hHLGdHQUFnRztJQUNoRyx3REFBd0Q7SUFDL0MsS0FBSyxDQUFDLE1BQU07UUFDbkIsTUFBTSxJQUFBLHVDQUFvQixFQUFDLDBEQUEwRCxDQUFDLENBQUM7SUFDekYsQ0FBQztJQUVRLEtBQUssQ0FBQyxLQUFLLENBQWlCLEVBQ25DLE1BQU0sRUFDTixJQUFJLEVBQ0osSUFBSSxFQUNKLEtBQUssRUFDTCxjQUFjLEVBQ2QsV0FBVyxFQUNYLG1CQUFtQixHQVNwQjtRQUNDLE1BQU0sTUFBTSxHQUFHLGtCQUFrQixDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDO1FBRXpELE1BQU0sRUFDSixNQUFNLEVBQ04sSUFBSSxFQUFFLFlBQVksRUFDbEIsSUFBSSxHQUNMLEdBQUcsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDO1lBQ3RCLE1BQU07WUFDTixJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUk7WUFDakIsT0FBTyxFQUFFO2dCQUNQLGFBQWEsRUFBRSxVQUFVLElBQUksQ0FBQyxXQUFXLEVBQUU7Z0JBQzNDLGNBQWMsRUFBRSxXQUFXLElBQUksa0JBQWtCO2dCQUNqRCxNQUFNLEVBQUUsV0FBVyxJQUFJLGtCQUFrQjthQUMxQztZQUNELEtBQUssRUFBRSxFQUFFLFFBQVEsRUFBRSxjQUFjLEVBQUUsR0FBRyxNQUFNLENBQUMsS0FBSyxFQUFFLEdBQUcsS0FBSyxFQUFFO1lBQzlELE9BQU8sRUFBRSxJQUFJO1lBQ2IsU0FBUyxFQUFFLGNBQWMsSUFBSSxJQUFJLENBQUMsZ0JBQWdCO1NBQ25ELENBQUMsQ0FBQztRQUVILElBQUksTUFBTSxJQUFJLEdBQUc7WUFBRSxNQUFNLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFLFlBQVksRUFBRSxJQUFJLENBQUMsQ0FBQztRQUVyRSxPQUFPLElBQUksQ0FBQyxXQUFXLENBQU8sWUFBWSxFQUFFLElBQUksRUFBRSxtQkFBbUIsQ0FBQyxDQUFDO0lBQ3pFLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ssS0FBSyxDQUFDLFFBQVEsQ0FBQyxPQUE2QjtRQUNsRCxJQUFJLENBQUM7WUFDSCxPQUFPLE1BQU0sSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDaEQsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixNQUFNLE1BQU0sR0FBRyxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUVyRixNQUFNLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxFQUFFLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN2RCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7Ozs7O09BT0c7SUFDSyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsSUFBWTtRQUkxQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxFQUFFLENBQUM7UUFDNUQsTUFBTSxHQUFHLEdBQUcsSUFBSSxHQUFHLENBQUMsR0FBRyw0QkFBb0IsR0FBRyw0QkFBYSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDLENBQUM7UUFFekYsT0FBTyxFQUFFLElBQUksRUFBRSxHQUFHLENBQUMsUUFBUSxFQUFFLEtBQUssRUFBRSxNQUFNLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDO0lBQzdFLENBQUM7Q0FDRjtBQU9ELFNBQXdCLHdCQUF3QixDQUFDLEVBQy9DLFVBQVUsRUFDVixTQUFTLEdBQ2lCO0lBQzFCLE9BQU87UUFDTCxHQUFHLEVBQUUsNEJBQW9CO1FBQ3pCLGVBQWUsRUFBRSxLQUFLLENBQUMsRUFBRSxDQUFDLElBQUksa0JBQWtCLENBQUMsS0FBSyxFQUFFLFVBQVUsRUFBRSxTQUFTLENBQUM7S0FDL0UsQ0FBQztBQUNKLENBQUMifQ==
@@ -1,6 +1,8 @@
1
+ import type { AgentDispatcher } from './agent/in-process-transport';
1
2
  import type { BFFConfig } from './config/env-config';
2
3
  import type { UnfoldSource } from './openapi/unfolded-document';
3
4
  import type { Logger } from './ports/logger-port';
5
+ import type { Metrics } from './ports/metrics-port';
4
6
  import type { IncomingMessage, ServerResponse } from 'http';
5
7
  /** What a host must hand to `http.createServer`, or mount, to serve the BFF. */
6
8
  export type BffCallback = (req: IncomingMessage, res: ServerResponse) => void | Promise<void>;
@@ -16,6 +18,16 @@ export interface BuildBffOptions {
16
18
  * origin root. Normalized by `normalizeBasePath`, which throws on anything else.
17
19
  */
18
20
  basePath?: string;
21
+ /**
22
+ * Reaches an agent living in the same process, without a socket. When set, it replaces the HTTP
23
+ * transport entirely: `AGENT_URL` then only names where the agent answers, never how it is called.
24
+ */
25
+ dispatcher?: AgentDispatcher;
26
+ /**
27
+ * Where the read-model reports its gauges. Defaults to the console sink, which is what the
28
+ * standalone deployment wants; an embedding host passes its own, or a no-op.
29
+ */
30
+ metrics?: Metrics;
19
31
  }
20
32
  export interface Bff {
21
33
  callback: BffCallback;
@@ -41,6 +53,6 @@ export declare function resolveUnfoldSource(config: BFFConfig, logger: Logger):
41
53
  * answers 404 itself instead of yielding to a host `next()` — and every path it serves is absolute,
42
54
  * so a prefix mount would move `/health` and `/agent/...` off the paths the frontend calls.
43
55
  */
44
- export default function buildBff({ config, logger, basePath, }: BuildBffOptions): Promise<Bff>;
56
+ export default function buildBff({ config, logger, basePath, dispatcher, metrics, }: BuildBffOptions): Promise<Bff>;
45
57
  export {};
46
58
  //# sourceMappingURL=build-bff.d.ts.map
package/dist/build-bff.js CHANGED
@@ -45,6 +45,7 @@ const action_routes_middleware_1 = __importDefault(require("./action/action-rout
45
45
  const console_logger_1 = __importDefault(require("./adapters/console-logger"));
46
46
  const agent_stub_1 = __importDefault(require("./agent/agent-stub"));
47
47
  const agent_transport_1 = require("./agent/agent-transport");
48
+ const in_process_transport_1 = __importDefault(require("./agent/in-process-transport"));
48
49
  const ai_proxy_client_1 = __importDefault(require("./ai/ai-proxy-client"));
49
50
  const ai_routes_middleware_1 = __importStar(require("./ai/ai-routes-middleware"));
50
51
  const api_key_authenticator_1 = __importDefault(require("./api-key/api-key-authenticator"));
@@ -59,6 +60,7 @@ const cors_middleware_1 = __importDefault(require("./cors/cors-middleware"));
59
60
  const per_key_origin_1 = __importDefault(require("./cors/per-key-origin"));
60
61
  const data_routes_middleware_1 = __importDefault(require("./data/data-routes-middleware"));
61
62
  const docs_routes_1 = __importDefault(require("./docs/docs-routes"));
63
+ const access_log_middleware_1 = __importDefault(require("./http/access-log-middleware"));
62
64
  const bff_http_error_1 = require("./http/bff-http-error");
63
65
  const body_limit_1 = __importStar(require("./http/body-limit"));
64
66
  const error_middleware_1 = __importDefault(require("./http/error-middleware"));
@@ -209,23 +211,29 @@ function resolveReadModelBundle(config, logger, metrics) {
209
211
  });
210
212
  return { store, apiKeyConfig };
211
213
  }
214
+ /**
215
+ * How this deployment reaches the agent: in-process when a dispatcher was handed over, over HTTP
216
+ * when an AGENT_URL was configured, and not at all otherwise — which is what makes the data and
217
+ * action routes fall back to their stub.
218
+ */
219
+ function resolveTransport(config, dispatcher) {
220
+ const timeoutMs = config.agentTimeoutMs;
221
+ if (dispatcher)
222
+ return (0, in_process_transport_1.default)({ dispatcher, timeoutMs });
223
+ if (config.agentUrl)
224
+ return (0, agent_transport_1.createHttpTransport)({ agentUrl: config.agentUrl, timeoutMs });
225
+ return undefined;
226
+ }
212
227
  /**
213
228
  * When unfolding is possible, in ONE place: the document needs the AGENT_URL the store does not,
214
229
  * because a collection's field set comes from the agent capabilities. The server passes the bundle it
215
230
  * already built for the data routes; the CLI goes through `resolveUnfoldSource`. Silent on purpose —
216
231
  * the two report a missing configuration differently.
217
232
  */
218
- function toUnfoldSource(bundle, config, logger) {
219
- if (!bundle || !config.agentUrl)
233
+ function toUnfoldSource(bundle, transport, logger) {
234
+ if (!bundle || !transport)
220
235
  return undefined;
221
- return {
222
- store: bundle.store,
223
- transport: (0, agent_transport_1.createHttpTransport)({
224
- agentUrl: config.agentUrl,
225
- timeoutMs: config.agentTimeoutMs,
226
- }),
227
- logger,
228
- };
236
+ return { store: bundle.store, transport, logger };
229
237
  }
230
238
  /**
231
239
  * Metrics are dropped rather than logged: without an explicit sink `createReadModel` builds a console
@@ -235,16 +243,15 @@ function toUnfoldSource(bundle, config, logger) {
235
243
  */
236
244
  const UNMEASURED = { increment: () => undefined, gauge: () => undefined };
237
245
  function resolveUnfoldSource(config, logger) {
238
- return toUnfoldSource(resolveReadModelBundle(config, logger, UNMEASURED), config, logger);
246
+ return toUnfoldSource(resolveReadModelBundle(config, logger, UNMEASURED), resolveTransport(config, undefined), logger);
239
247
  }
240
248
  // The data middleware falls through to the action middleware on a non-data path.
241
- function buildAgentRouteMiddlewares(bundle, config, logger, permissionsCache) {
249
+ function buildAgentRouteMiddlewares(bundle, transport, logger, permissionsCache) {
242
250
  if (!bundle) {
243
251
  logger('Warn', 'Data, action and permissions endpoints disabled: FOREST_SERVER_URL, FOREST_ENV_SECRET or FOREST_AUTH_SECRET is missing');
244
252
  return [(0, agent_stub_1.default)()];
245
253
  }
246
254
  const { store, apiKeyConfig } = bundle;
247
- const { agentUrl, agentTimeoutMs: timeoutMs } = config;
248
255
  const permissionsMiddleware = (0, permissions_routes_middleware_1.default)({
249
256
  store,
250
257
  client: new permissions_client_1.default({
@@ -254,11 +261,10 @@ function buildAgentRouteMiddlewares(bundle, config, logger, permissionsCache) {
254
261
  cache: permissionsCache,
255
262
  logger,
256
263
  });
257
- if (!agentUrl) {
264
+ if (!transport) {
258
265
  logger('Warn', 'Data and action endpoints disabled: AGENT_URL is missing');
259
266
  return [permissionsMiddleware, (0, agent_stub_1.default)()];
260
267
  }
261
- const transport = (0, agent_transport_1.createHttpTransport)({ agentUrl, timeoutMs });
262
268
  return [
263
269
  permissionsMiddleware,
264
270
  (0, data_routes_middleware_1.default)({ store, transport, logger }),
@@ -285,7 +291,7 @@ function buildAiMiddlewares(config, oauth, logger) {
285
291
  }),
286
292
  ];
287
293
  }
288
- function buildAgentMiddlewares(config, logger, oauth, aiMiddlewares, basePath) {
294
+ function buildAgentMiddlewares(config, logger, oauth, aiMiddlewares, basePath, transport, metrics) {
289
295
  const { forestAuthSecret, defaultTimezone } = config;
290
296
  if (!forestAuthSecret) {
291
297
  logger('Warn', 'Agent edge disabled: FOREST_AUTH_SECRET is missing');
@@ -294,8 +300,8 @@ function buildAgentMiddlewares(config, logger, oauth, aiMiddlewares, basePath) {
294
300
  const apiKeyStep = buildApiKeyMiddleware(config, logger) ?? createApiKeyUnavailableGuard(logger);
295
301
  // One store for the whole edge. The document only unfolds when the agent is reachable too: with no
296
302
  // AGENT_URL every data path answers 501, so concrete paths would advertise a dead surface.
297
- const bundle = resolveReadModelBundle(config, logger);
298
- const source = toUnfoldSource(bundle, config, logger);
303
+ const bundle = resolveReadModelBundle(config, logger, metrics);
304
+ const source = toUnfoldSource(bundle, transport, logger);
299
305
  const permissionsCache = new permissions_cache_1.default();
300
306
  const chain = [
301
307
  (0, auth_mode_middleware_1.default)({ authSecret: forestAuthSecret }),
@@ -325,7 +331,7 @@ function buildAgentMiddlewares(config, logger, oauth, aiMiddlewares, basePath) {
325
331
  : []),
326
332
  ...aiMiddlewares,
327
333
  (0, timezone_middleware_1.default)({ defaultTimezone }),
328
- ...buildAgentRouteMiddlewares(bundle, config, logger, permissionsCache),
334
+ ...buildAgentRouteMiddlewares(bundle, transport, logger, permissionsCache),
329
335
  ];
330
336
  return {
331
337
  middlewares: chain.map(agentScoped),
@@ -345,7 +351,7 @@ function buildAgentMiddlewares(config, logger, oauth, aiMiddlewares, basePath) {
345
351
  * answers 404 itself instead of yielding to a host `next()` — and every path it serves is absolute,
346
352
  * so a prefix mount would move `/health` and `/agent/...` off the paths the frontend calls.
347
353
  */
348
- async function buildBff({ config, logger = (0, console_logger_1.default)(), basePath, }) {
354
+ async function buildBff({ config, logger = (0, console_logger_1.default)(), basePath, dispatcher, metrics, }) {
349
355
  // Before anything is assembled: a mount the host does not serve must fail at boot, not surface as
350
356
  // a docs page that cannot load itself.
351
357
  const mountPath = (0, base_path_1.default)(basePath);
@@ -355,16 +361,34 @@ async function buildBff({ config, logger = (0, console_logger_1.default)(), base
355
361
  });
356
362
  }
357
363
  (0, missing_config_warning_1.default)(config, logger);
364
+ if (config.allowedOrigins.length === 0) {
365
+ logger('Warn', 'No allowed origin: no browser can call this BFF. Set BFF_ALLOWED_ORIGINS, or `allowedOrigins`.');
366
+ }
367
+ const transport = resolveTransport(config, dispatcher);
358
368
  const oauth = buildOAuthMiddlewares(config, logger);
359
369
  const aiMiddlewares = buildAiMiddlewares(config, oauth, logger);
360
- const agentEdge = buildAgentMiddlewares(config, logger, oauth, aiMiddlewares, mountPath);
370
+ const agentEdge = buildAgentMiddlewares(config, logger, oauth, aiMiddlewares, mountPath, transport, metrics);
361
371
  const agentMiddlewares = agentEdge.middlewares;
362
372
  const hasAgentEdge = agentMiddlewares.length > 0;
363
373
  const agentErrorMiddleware = hasAgentEdge ? [agentScoped((0, error_middleware_1.default)({ logger }))] : [];
364
374
  const agentJsonOnlyGuard = hasAgentEdge ? [agentScoped(createJsonOnlyGuard())] : [];
365
375
  const middlewares = [
376
+ (0, access_log_middleware_1.default)({ logger, basePath: mountPath }),
366
377
  (0, version_header_middleware_1.default)(version_1.default),
367
- (0, health_route_1.default)({ config, version: version_1.default }),
378
+ (0, health_route_1.default)({
379
+ version: version_1.default,
380
+ // Embedded, the rest is inherited from the agent, so there is no gap to report: a 503 here
381
+ // would let a load balancer restart a process that serves api-key traffic fine. The auth
382
+ // secret is still required — without it the agent edge is a stub and nothing authenticated
383
+ // can be served, which is exactly what a probe must see.
384
+ healthy: (dispatcher !== undefined && Boolean(config.forestAuthSecret)) || config.hasAllRequired,
385
+ configured: {
386
+ oauth: oauth.middlewares.length > 0,
387
+ ai: aiMiddlewares.length > 0,
388
+ cors: config.allowedOrigins.length > 0,
389
+ openapi: config.openapiEnabled && agentMiddlewares.length > 0,
390
+ },
391
+ }),
368
392
  (0, cors_middleware_1.default)({ allowedOrigins: config.allowedOrigins, logger }),
369
393
  ...agentErrorMiddleware,
370
394
  ...agentJsonOnlyGuard,
@@ -386,4 +410,4 @@ async function buildBff({ config, logger = (0, console_logger_1.default)(), base
386
410
  app.use(middleware);
387
411
  return { callback: app.callback(), invalidate: agentEdge.invalidate };
388
412
  }
389
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVpbGQtYmZmLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2J1aWxkLWJmZi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXVKQSxnREFlQztBQXNKRCxrREFFQztBQW9KRCwyQkFpREM7QUF6ZkQsZ0RBQTZDO0FBQzdDLDhDQUFzQjtBQUV0QixpR0FBNkU7QUFDN0UsK0VBQTREO0FBQzVELG9FQUEyRDtBQUMzRCw2REFBOEQ7QUFDOUQsMkVBQWlEO0FBQ2pELGtGQUFxRjtBQUNyRiw0RkFBd0U7QUFDeEUsOEVBQW9EO0FBQ3BELHNGQUFrRTtBQUNsRSw0RUFBeUQ7QUFDekQsdUZBQW1FO0FBQ25FLDREQUE0QztBQUM1Qyw2RkFBZ0U7QUFDaEUsb0dBQWdGO0FBQ2hGLDZFQUEwRDtBQUMxRCwyRUFBaUU7QUFDakUsMkZBQXVFO0FBQ3ZFLHFFQUFrRDtBQUNsRCwwREFBMkU7QUFDM0UsZ0VBQThEO0FBQzlELCtFQUE0RDtBQUM1RCx1RUFBb0Q7QUFDcEQsaUdBQTZFO0FBQzdFLHlFQUFtRztBQUNuRyx3RkFBOEQ7QUFDOUQsd0VBQXFEO0FBQ3JELDBFQUErRDtBQUMvRCx3RUFBcUQ7QUFDckQsMkVBQTZFO0FBQzdFLHdGQUErRDtBQUMvRCwwRkFBaUU7QUFDakUsZ0hBQTRGO0FBQzVGLHlFQUFrRDtBQUNsRCwrRkFBMkU7QUFDM0UsdUZBQTZEO0FBQzdELHlGQUFzRTtBQUN0RSx3REFBZ0M7QUE0QmhDLE1BQU0sbUJBQW1CLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUM7QUFFekMsTUFBTSxZQUFZLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7QUFFdkQsTUFBTSxlQUFlLEdBQUcsQ0FBQyxrQkFBa0IsRUFBRSxvQkFBb0IsQ0FBQyxDQUFDO0FBRW5FLFNBQVMsT0FBTyxDQUFD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
413
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVpbGQtYmZmLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2J1aWxkLWJmZi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXFLQSxnREFlQztBQWdLRCxrREFNQztBQW1KRCwyQkFrRkM7QUFuakJELGdEQUE2QztBQUM3Qyw4Q0FBc0I7QUFFdEIsaUdBQTZFO0FBQzdFLCtFQUE0RDtBQUM1RCxvRUFBMkQ7QUFDM0QsNkRBQThEO0FBQzlELHdGQUFvRTtBQUNwRSwyRUFBaUQ7QUFDakQsa0ZBQXFGO0FBQ3JGLDRGQUF3RTtBQUN4RSw4RUFBb0Q7QUFDcEQsc0ZBQWtFO0FBQ2xFLDRFQUF5RDtBQUN6RCx1RkFBbUU7QUFDbkUsNERBQTRDO0FBQzVDLDZGQUFnRTtBQUNoRSxvR0FBZ0Y7QUFDaEYsNkVBQTBEO0FBQzFELDJFQUFpRTtBQUNqRSwyRkFBdUU7QUFDdkUscUVBQWtEO0FBQ2xELHlGQUFxRTtBQUNyRSwwREFBMkU7QUFDM0UsZ0VBQThEO0FBQzlELCtFQUE0RDtBQUM1RCx1RUFBb0Q7QUFDcEQsaUdBQTZFO0FBQzdFLHlFQUFtRztBQUNuRyx3RkFBOEQ7QUFDOUQsd0VBQXFEO0FBQ3JELDBFQUErRDtBQUMvRCx3RUFBcUQ7QUFDckQsMkVBQTZFO0FBQzdFLHdGQUErRDtBQUMvRCwwRkFBaUU7QUFDakUsZ0hBQTRGO0FBQzVGLHlFQUFrRDtBQUNsRCwrRkFBMkU7QUFDM0UsdUZBQTZEO0FBQzdELHlGQUFzRTtBQUN0RSx3REFBZ0M7QUFzQ2hDLE1BQU0sbUJBQW1CLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUM7QUFFekMsTUFBTSxZQUFZLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7QUFFdkQsTUFBTSxlQUFlLEdBQUcsQ0FBQyxrQkFBa0IsRUFBRSxvQkFBb0IsQ0FB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
@@ -136,7 +136,11 @@ function parseConfig(env) {
136
136
  }),
137
137
  httpPort: parsePort(env.HTTP_PORT),
138
138
  presence,
139
- hasAllRequired: exports.REQUIRED_KEYS.every(key => presence[key]) && tokenEncryptionKey !== undefined,
139
+ // The encryption key is deliberately not part of this: it gates OAuth, not boot, so a
140
+ // key-only deployment is fully operational and must not report degraded — `/health` would
141
+ // otherwise have a load balancer restart a process that serves its api-key and bearer traffic
142
+ // fine. Which optional surfaces are on is what `configured` reports.
143
+ hasAllRequired: exports.REQUIRED_KEYS.every(key => presence[key]),
140
144
  };
141
145
  }
142
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW52LWNvbmZpZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb25maWcvZW52LWNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUEwRkEsd0NBMEJDO0FBNENELGtDQStEQztBQS9ORCw2QkFBd0I7QUFFeEIsMkNBQXFEO0FBQ3JELDJEQUEyQztBQUMzQyxzQ0FBK0M7QUFDL0MsbURBQXVEO0FBRTFDLFFBQUEsYUFBYSxHQUFHO0lBQzNCLG9CQUFvQjtJQUNwQixtQkFBbUI7SUFDbkIsbUJBQW1CO0lBQ25CLGdCQUFnQjtJQUNoQixXQUFXO0NBQ0gsQ0FBQztBQUlYLE1BQU0sUUFBUSxHQUFHLENBQUMsbUJBQW1CLEVBQUUsZ0JBQWdCLEVBQUUsV0FBVyxDQUFVLENBQUM7QUF5Qi9FLE1BQU0sZUFBZSxHQUFHLE9BQU8sQ0FBQztBQUNuQixRQUFBLHdCQUF3QixHQUFHLEtBQU0sQ0FBQztBQUNsQyxRQUFBLHFCQUFxQixHQUFHLE1BQU8sQ0FBQztBQUNoQyxRQUFBLGNBQWMsR0FBRyxVQUFhLENBQUM7QUFDL0IsUUFBQSwrQkFBK0IsR0FBRyxHQUFHLENBQUM7QUFDdEMsUUFBQSw0QkFBNEIsR0FBRyxLQUFNLENBQUM7QUFDdEMsUUFBQSx1QkFBdUIsR0FBRyxLQUFNLENBQUM7QUFDakMsUUFBQSx3QkFBd0IsR0FBRyxJQUFLLENBQUM7QUFDOUMsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDO0FBQ3ZCLE1BQU0sb0JBQW9CLEdBQUcsRUFBRSxDQUFDO0FBQ2hDLE1BQU0sY0FBYyxHQUFHLHdCQUF3QixDQUFDO0FBQ2hELE1BQU0sZUFBZSxHQUFHLE9BQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQztBQUV4RCxTQUFTLFNBQVMsQ0FBQyxLQUF5QjtJQUMxQyxPQUFPLEtBQUssS0FBSyxTQUFTLElBQUksS0FBSyxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsQ0FB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
146
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW52LWNvbmZpZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb25maWcvZW52LWNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUEwRkEsd0NBMEJDO0FBNENELGtDQW1FQztBQW5PRCw2QkFBd0I7QUFFeEIsMkNBQXFEO0FBQ3JELDJEQUEyQztBQUMzQyxzQ0FBK0M7QUFDL0MsbURBQXVEO0FBRTFDLFFBQUEsYUFBYSxHQUFHO0lBQzNCLG9CQUFvQjtJQUNwQixtQkFBbUI7SUFDbkIsbUJBQW1CO0lBQ25CLGdCQUFnQjtJQUNoQixXQUFXO0NBQ0gsQ0FBQztBQUlYLE1BQU0sUUFBUSxHQUFHLENBQUMsbUJBQW1CLEVBQUUsZ0JBQWdCLEVBQUUsV0FBVyxDQUFVLENBQUM7QUF5Qi9FLE1BQU0sZUFBZSxHQUFHLE9BQU8sQ0FBQztBQUNuQixRQUFBLHdCQUF3QixHQUFHLEtBQU0sQ0FBQztBQUNsQyxRQUFBLHFCQUFxQixHQUFHLE1BQU8sQ0FBQztBQUNoQyxRQUFBLGNBQWMsR0FBRyxVQUFhLENBQUM7QUFDL0IsUUFBQSwrQkFBK0IsR0FBRyxHQUFHLENBQUM7QUFDdEMsUUFBQSw0QkFBNEIsR0FBRyxLQUFNLENBQUM7QUFDdEMsUUFBQSx1QkFBdUIsR0FBRyxLQUFNLENBQUM7QUFDakMsUUFBQSx3QkFBd0IsR0FBRyxJQUFLLENBQUM7QUFDOUMsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDO0FBQ3ZCLE1BQU0sb0JBQW9CLEdBQUcsRUFBRSxDQUFDO0FBQ2hDLE1BQU0sY0FBYyxHQUFHLHdCQUF3QixDQUFDO0FBQ2hELE1BQU0sZUFBZSxHQUFHLE9BQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQztBQUV4RCxTQUFTLFNBQVMsQ0FBQyxLQUF5QjtJQUMxQyxPQUFPLEtBQUssS0FBSyxTQUFTLElBQUksS0FBSyxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsQ0FB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
@@ -3,6 +3,16 @@ import type { Middleware } from 'koa';
3
3
  export declare const ALLOWED_METHODS = "GET, POST, PUT, PATCH, DELETE, OPTIONS";
4
4
  export declare const ALLOWED_HEADERS = "Authorization, Content-Type, X-Forest-Timezone, X-Forest-Bff-Key, X-Request-Id, Forest-Projection";
5
5
  export declare const PREFLIGHT_MAX_AGE_SECONDS = 600;
6
+ /**
7
+ * Shaped like every other BFF error so a consumer branches on `error.type`. Built here rather than
8
+ * thrown: this middleware runs before the agent-scoped error middleware, so a throw would surface
9
+ * as a bare 500 instead of the contract.
10
+ */
11
+ export declare const ORIGIN_NOT_ALLOWED: {
12
+ readonly type: "origin_not_allowed";
13
+ readonly status: 403;
14
+ readonly message: "This origin is not allowed to call this BFF";
15
+ };
6
16
  export interface CorsMiddlewareOptions {
7
17
  allowedOrigins: string[];
8
18
  logger: Logger;
@@ -1,11 +1,55 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PREFLIGHT_MAX_AGE_SECONDS = exports.ALLOWED_HEADERS = exports.ALLOWED_METHODS = void 0;
3
+ exports.ORIGIN_NOT_ALLOWED = exports.PREFLIGHT_MAX_AGE_SECONDS = exports.ALLOWED_HEADERS = exports.ALLOWED_METHODS = void 0;
4
4
  exports.default = createCorsMiddleware;
5
5
  const origin_1 = require("./origin");
6
+ /**
7
+ * Whether the caller is the very application serving this BFF. A same-origin request carries
8
+ * `Origin` too — the Fetch spec sends it on everything but GET and HEAD, and every BFF data route
9
+ * is a POST — so without this exemption a host serving its own UI next to a `/bff` mount would be
10
+ * refused on every request under the default empty allow-list, for an origin it has no reason to
11
+ * think it must name.
12
+ *
13
+ * Compared on host rather than on the full origin: a TLS-terminating proxy leaves `ctx.protocol` at
14
+ * `http` while the browser reports `https`, so matching the scheme would work in development and
15
+ * silently fail in production. The scheme is also the part a same-host attacker already controls.
16
+ *
17
+ * Forging the header buys nothing: a caller that can set `Origin` can simply omit it, which the
18
+ * allow-list lets through by design. What this cannot be reached by is the cross-site request the
19
+ * allow-list exists for — there the browser sets `Origin` to the attacking page, never to this host.
20
+ */
21
+ function isSameOrigin(origin, host) {
22
+ if (host === '')
23
+ return false;
24
+ let url;
25
+ try {
26
+ url = new URL(origin);
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ // `url.host` is already normalized by `new URL()` — lowercased, and stripped of a port that is
32
+ // the default for the scheme — while `ctx.host` is the raw `Host` header, which a client or proxy
33
+ // spells however it likes. Both differences have to be absorbed here, or the exemption refuses
34
+ // the very requests it exists for: `Host: APP.EXAMPLE.COM` or `Host: app.example.com:443` against
35
+ // `Origin: https://app.example.com`. A non-default port still has to match exactly.
36
+ const normalizedHost = host.toLowerCase();
37
+ const defaultPort = url.protocol === 'https:' ? '443' : '80';
38
+ return normalizedHost === url.host || normalizedHost === `${url.host}:${defaultPort}`;
39
+ }
6
40
  exports.ALLOWED_METHODS = 'GET, POST, PUT, PATCH, DELETE, OPTIONS';
7
41
  exports.ALLOWED_HEADERS = 'Authorization, Content-Type, X-Forest-Timezone, X-Forest-Bff-Key, X-Request-Id, Forest-Projection';
8
42
  exports.PREFLIGHT_MAX_AGE_SECONDS = 600;
43
+ /**
44
+ * Shaped like every other BFF error so a consumer branches on `error.type`. Built here rather than
45
+ * thrown: this middleware runs before the agent-scoped error middleware, so a throw would surface
46
+ * as a bare 500 instead of the contract.
47
+ */
48
+ exports.ORIGIN_NOT_ALLOWED = {
49
+ type: 'origin_not_allowed',
50
+ status: 403,
51
+ message: 'This origin is not allowed to call this BFF',
52
+ };
9
53
  function createCorsMiddleware({ allowedOrigins, logger, }) {
10
54
  return async function corsMiddleware(ctx, next) {
11
55
  const origin = ctx.get('Origin');
@@ -29,7 +73,19 @@ function createCorsMiddleware({ allowedOrigins, logger, }) {
29
73
  ctx.status = 204;
30
74
  return;
31
75
  }
76
+ // Refuse rather than run and let the browser discard the answer. Omitting the header only stops
77
+ // the caller from READING the response: the request still executed, so a list was read and an
78
+ // action was executed for an origin the allow-list names as unwelcome. It also stops being
79
+ // theoretical once a host sits in front of this app — a permissive `cors()` of its own answers
80
+ // the preflight with `*`, and the real request arrives here regardless.
81
+ // A caller with no `Origin` at all is untouched: that is every server-to-server api-key call.
82
+ // Nor is the host's own application, which the allow-list has nothing to say about.
83
+ if (origin && !allowed && !isSameOrigin(origin, ctx.host)) {
84
+ ctx.status = exports.ORIGIN_NOT_ALLOWED.status;
85
+ ctx.body = { error: exports.ORIGIN_NOT_ALLOWED };
86
+ return;
87
+ }
32
88
  await next();
33
89
  };
34
90
  }
35
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29ycy1taWRkbGV3YXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvcnMvY29ycy1taWRkbGV3YXJlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQWVBLHVDQWdDQztBQTVDRCxxQ0FBeUQ7QUFFNUMsUUFBQSxlQUFlLEdBQUcsd0NBQXdDLENBQUM7QUFDM0QsUUFBQSxlQUFlLEdBQzFCLG1HQUFtRyxDQUFDO0FBQ3pGLFFBQUEseUJBQXlCLEdBQUcsR0FBRyxDQUFDO0FBTzdDLFNBQXdCLG9CQUFvQixDQUFDLEVBQzNDLGNBQWMsRUFDZCxNQUFNLEdBQ2dCO0lBQ3RCLE9BQU8sS0FBSyxVQUFVLGNBQWMsQ0FBQyxHQUFHLEVBQUUsSUFBSTtRQUM1QyxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBRWpDLElBQUksTUFBTTtZQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFL0IsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFBLHNCQUFhLEVBQUMsTUFBTSxFQUFFLGNBQWMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7UUFFdkUsSUFBSSxPQUFPO1lBQUUsR0FBRyxDQUFDLEdBQUcsQ0FBQyw2QkFBNkIsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUU1RCxJQUFJLEdBQUcsQ0FBQyxNQUFNLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDN0IsSUFBSSxPQUFPLEVBQUUsQ0FBQztnQkFDWixHQUFHLENBQUMsR0FBRyxDQUFDLDhCQUE4QixFQUFFLHVCQUFlLENBQUMsQ0FBQztnQkFDekQsR0FBRyxDQUFDLEdBQUcsQ0FBQyw4QkFBOEIsRUFBRSx1QkFBZSxDQUFDLENBQUM7Z0JBQ3pELEdBQUcsQ0FBQyxHQUFHLENBQUMsd0JBQXdCLEVBQUUsTUFBTSxDQUFDLGlDQUF5QixDQUFDLENBQUMsQ0FBQztZQUN2RSxDQUFDO2lCQUFNLElBQUksTUFBTSxJQUFJLEdBQUcsQ0FBQyxHQUFHLENBQUMsK0JBQStCLENBQUMsRUFBRSxDQUFDO2dCQUM5RCxNQUFNLENBQUMsTUFBTSxFQUFFLCtCQUErQixFQUFFO29CQUM5QyxNQUFNLEVBQUUsSUFBQSx1QkFBYyxFQUFDLE1BQU0sQ0FBQztvQkFDOUIsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJO2lCQUNmLENBQUMsQ0FBQztZQUNMLENBQUM7WUFFRCxHQUFHLENBQUMsTUFBTSxHQUFHLEdBQUcsQ0FBQztZQUVqQixPQUFPO1FBQ1QsQ0FBQztRQUVELE1BQU0sSUFBSSxFQUFFLENBQUM7SUFDZixDQUFDLENBQUM7QUFDSixDQUFDIn0=
91
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29ycy1taWRkbGV3YXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvcnMvY29ycy1taWRkbGV3YXJlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQStEQSx1Q0E4Q0M7QUExR0QscUNBQXlEO0FBRXpEOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsU0FBUyxZQUFZLENBQUMsTUFBYyxFQUFFLElBQVk7SUFDaEQsSUFBSSxJQUFJLEtBQUssRUFBRTtRQUFFLE9BQU8sS0FBSyxDQUFDO0lBRTlCLElBQUksR0FBUSxDQUFDO0lBRWIsSUFBSSxDQUFDO1FBQ0gsR0FBRyxHQUFHLElBQUksR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3hCLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRCwrRkFBK0Y7SUFDL0Ysa0dBQWtHO0lBQ2xHLCtGQUErRjtJQUMvRixrR0FBa0c7SUFDbEcsb0ZBQW9GO0lBQ3BGLE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUMxQyxNQUFNLFdBQVcsR0FBRyxHQUFHLENBQUMsUUFBUSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7SUFFN0QsT0FBTyxjQUFjLEtBQUssR0FBRyxDQUFDLElBQUksSUFBSSxjQUFjLEtBQUssR0FBRyxHQUFHLENBQUMsSUFBSSxJQUFJLFdBQVcsRUFBRSxDQUFDO0FBQ3hGLENBQUM7QUFFWSxRQUFBLGVBQWUsR0FBRyx3Q0FBd0MsQ0FBQztBQUMzRCxRQUFBLGVBQWUsR0FDMUIsbUdBQW1HLENBQUM7QUFDekYsUUFBQSx5QkFBeUIsR0FBRyxHQUFHLENBQUM7QUFFN0M7Ozs7R0FJRztBQUNVLFFBQUEsa0JBQWtCLEdBQUc7SUFDaEMsSUFBSSxFQUFFLG9CQUFvQjtJQUMxQixNQUFNLEVBQUUsR0FBRztJQUNYLE9BQU8sRUFBRSw2Q0FBNkM7Q0FDOUMsQ0FBQztBQU9YLFNBQXdCLG9CQUFvQixDQUFDLEVBQzNDLGNBQWMsRUFDZCxNQUFNLEdBQ2dCO0lBQ3RCLE9BQU8sS0FBSyxVQUFVLGNBQWMsQ0FBQyxHQUFHLEVBQUUsSUFBSTtRQUM1QyxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBRWpDLElBQUksTUFBTTtZQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFL0IsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFBLHNCQUFhLEVBQUMsTUFBTSxFQUFFLGNBQWMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7UUFFdkUsSUFBSSxPQUFPO1lBQUUsR0FBRyxDQUFDLEdBQUcsQ0FBQyw2QkFBNkIsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUU1RCxJQUFJLEdBQUcsQ0FBQyxNQUFNLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDN0IsSUFBSSxPQUFPLEVBQUUsQ0FBQztnQkFDWixHQUFHLENBQUMsR0FBRyxDQUFDLDhCQUE4QixFQUFFLHVCQUFlLENBQUMsQ0FBQztnQkFDekQsR0FBRyxDQUFDLEdBQUcsQ0FBQyw4QkFBOEIsRUFBRSx1QkFBZSxDQUFDLENBQUM7Z0JBQ3pELEdBQUcsQ0FBQyxHQUFHLENBQUMsd0JBQXdCLEVBQUUsTUFBTSxDQUFDLGlDQUF5QixDQUFDLENBQUMsQ0FBQztZQUN2RSxDQUFDO2lCQUFNLElBQUksTUFBTSxJQUFJLEdBQUcsQ0FBQyxHQUFHLENBQUMsK0JBQStCLENBQUMsRUFBRSxDQUFDO2dCQUM5RCxNQUFNLENBQUMsTUFBTSxFQUFFLCtCQUErQixFQUFFO29CQUM5QyxNQUFNLEVBQUUsSUFBQSx1QkFBYyxFQUFDLE1BQU0sQ0FBQztvQkFDOUIsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJO2lCQUNmLENBQUMsQ0FBQztZQUNMLENBQUM7WUFFRCxHQUFHLENBQUMsTUFBTSxHQUFHLEdBQUcsQ0FBQztZQUVqQixPQUFPO1FBQ1QsQ0FBQztRQUVELGdHQUFnRztRQUNoRyw4RkFBOEY7UUFDOUYsMkZBQTJGO1FBQzNGLCtGQUErRjtRQUMvRix3RUFBd0U7UUFDeEUsOEZBQThGO1FBQzlGLG9GQUFvRjtRQUNwRixJQUFJLE1BQU0sSUFBSSxDQUFDLE9BQU8sSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLEVBQUUsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDMUQsR0FBRyxDQUFDLE1BQU0sR0FBRywwQkFBa0IsQ0FBQyxNQUFNLENBQUM7WUFDdkMsR0FBRyxDQUFDLElBQUksR0FBRyxFQUFFLEtBQUssRUFBRSwwQkFBa0IsRUFBRSxDQUFDO1lBRXpDLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxJQUFJLEVBQUUsQ0FBQztJQUNmLENBQUMsQ0FBQztBQUNKLENBQUMifQ==
@@ -0,0 +1,26 @@
1
+ import type { Logger } from '../ports/logger-port';
2
+ import type { Middleware } from 'koa';
3
+ export interface AccessLogMiddlewareOptions {
4
+ logger: Logger;
5
+ /** Prefix the host serves this BFF under, so the line names a url the caller can actually call. */
6
+ basePath: string;
7
+ }
8
+ /**
9
+ * One line per request, in the shape the agent's own request logger emits (`[200] GET /path - 12ms`)
10
+ * so both layers read the same way in an embedded deployment.
11
+ *
12
+ * Belongs at the head of the chain: everything below it — `/health`, `/docs`, `/oauth/*`, a 401 from
13
+ * the auth edge, a rejected origin — either never reaches the agent or never reaches the
14
+ * agent-scoped error middleware, and is exactly the traffic that used to leave no trace at all.
15
+ *
16
+ * The query string is deliberately absent. Nothing routed today needs it, and a line that carries it
17
+ * would leak whatever a future route accepts there without anyone revisiting this decision.
18
+ *
19
+ * Carries no cause either, so that a host logger which cannot take structured fields still reads as
20
+ * one line per request. The detail is not lost where it matters: `createErrorMiddleware` already
21
+ * reports it for everything under `/agent`. Outside it — a body parser rejection on an OAuth POST is
22
+ * the only route there today — the stack reaches stderr through Koa's own handler, as it did before
23
+ * this middleware existed.
24
+ */
25
+ export default function createAccessLogMiddleware({ logger, basePath, }: AccessLogMiddlewareOptions): Middleware;
26
+ //# sourceMappingURL=access-log-middleware.d.ts.map
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = createAccessLogMiddleware;
4
+ const base_path_1 = require("../base-path");
5
+ const CLIENT_ERROR = 400;
6
+ const SERVER_ERROR = 500;
7
+ function levelOf(status) {
8
+ if (status >= SERVER_ERROR)
9
+ return 'Error';
10
+ if (status >= CLIENT_ERROR)
11
+ return 'Warn';
12
+ return 'Info';
13
+ }
14
+ /**
15
+ * The status the caller will see, read off an exception rather than off the response: this
16
+ * middleware sits above every error handler the BFF has, so at the moment it logs, `ctx.status` is
17
+ * still the 404 Koa defaults to and would name a status nobody was ever answered.
18
+ */
19
+ function statusOf(error) {
20
+ if (typeof error !== 'object' || error === null)
21
+ return SERVER_ERROR;
22
+ const { status, statusCode } = error;
23
+ const value = typeof status === 'number' ? status : statusCode;
24
+ return typeof value === 'number' ? value : SERVER_ERROR;
25
+ }
26
+ /**
27
+ * One line per request, in the shape the agent's own request logger emits (`[200] GET /path - 12ms`)
28
+ * so both layers read the same way in an embedded deployment.
29
+ *
30
+ * Belongs at the head of the chain: everything below it — `/health`, `/docs`, `/oauth/*`, a 401 from
31
+ * the auth edge, a rejected origin — either never reaches the agent or never reaches the
32
+ * agent-scoped error middleware, and is exactly the traffic that used to leave no trace at all.
33
+ *
34
+ * The query string is deliberately absent. Nothing routed today needs it, and a line that carries it
35
+ * would leak whatever a future route accepts there without anyone revisiting this decision.
36
+ *
37
+ * Carries no cause either, so that a host logger which cannot take structured fields still reads as
38
+ * one line per request. The detail is not lost where it matters: `createErrorMiddleware` already
39
+ * reports it for everything under `/agent`. Outside it — a body parser rejection on an OAuth POST is
40
+ * the only route there today — the stack reaches stderr through Koa's own handler, as it did before
41
+ * this middleware existed.
42
+ */
43
+ function createAccessLogMiddleware({ logger, basePath, }) {
44
+ return async function accessLog(ctx, next) {
45
+ const startedAt = Date.now();
46
+ const write = (status) => {
47
+ const path = `${(0, base_path_1.emittedBaseOf)(ctx, basePath)}${ctx.path}`;
48
+ try {
49
+ logger(levelOf(status), `[${status}] ${ctx.method} ${path} - ${Date.now() - startedAt}ms`);
50
+ }
51
+ catch {
52
+ // The logger belongs to the host and nothing in the port forbids it from throwing. Left
53
+ // unguarded, a throw here would answer 500 to a request the route served, and on the error
54
+ // path it would replace the exception being reported with its own. There is nowhere to
55
+ // report this: the only sink available is the logger that just failed.
56
+ }
57
+ };
58
+ try {
59
+ await next();
60
+ }
61
+ catch (error) {
62
+ write(statusOf(error));
63
+ throw error;
64
+ }
65
+ write(ctx.response.status);
66
+ };
67
+ }
68
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNjZXNzLWxvZy1taWRkbGV3YXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2h0dHAvYWNjZXNzLWxvZy1taWRkbGV3YXJlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBb0RBLDRDQThCQztBQS9FRCw0Q0FBNkM7QUFRN0MsTUFBTSxZQUFZLEdBQUcsR0FBRyxDQUFDO0FBQ3pCLE1BQU0sWUFBWSxHQUFHLEdBQUcsQ0FBQztBQUV6QixTQUFTLE9BQU8sQ0FBQyxNQUFjO0lBQzdCLElBQUksTUFBTSxJQUFJLFlBQVk7UUFBRSxPQUFPLE9BQU8sQ0FBQztJQUMzQyxJQUFJLE1BQU0sSUFBSSxZQUFZO1FBQUUsT0FBTyxNQUFNLENBQUM7SUFFMUMsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxTQUFTLFFBQVEsQ0FBQyxLQUFjO0lBQzlCLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssS0FBSyxJQUFJO1FBQUUsT0FBTyxZQUFZLENBQUM7SUFFckUsTUFBTSxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsR0FBRyxLQUFtRCxDQUFDO0lBQ25GLE1BQU0sS0FBSyxHQUFHLE9BQU8sTUFBTSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUM7SUFFL0QsT0FBTyxPQUFPLEtBQUssS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDO0FBQzFELENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILFNBQXdCLHlCQUF5QixDQUFDLEVBQ2hELE1BQU0sRUFDTixRQUFRLEdBQ21CO0lBQzNCLE9BQU8sS0FBSyxVQUFVLFNBQVMsQ0FBQyxHQUFHLEVBQUUsSUFBSTtRQUN2QyxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFFN0IsTUFBTSxLQUFLLEdBQUcsQ0FBQyxNQUFjLEVBQUUsRUFBRTtZQUMvQixNQUFNLElBQUksR0FBRyxHQUFHLElBQUEseUJBQWEsRUFBQyxHQUFHLEVBQUUsUUFBUSxDQUFDLEdBQUcsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDO1lBRTFELElBQUksQ0FBQztnQkFDSCxNQUFNLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUFFLElBQUksTUFBTSxLQUFLLEdBQUcsQ0FBQyxNQUFNLElBQUksSUFBSSxNQUFNLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxTQUFTLElBQUksQ0FBQyxDQUFDO1lBQzdGLENBQUM7WUFBQyxNQUFNLENBQUM7Z0JBQ1Asd0ZBQXdGO2dCQUN4RiwyRkFBMkY7Z0JBQzNGLHVGQUF1RjtnQkFDdkYsdUVBQXVFO1lBQ3pFLENBQUM7UUFDSCxDQUFDLENBQUM7UUFFRixJQUFJLENBQUM7WUFDSCxNQUFNLElBQUksRUFBRSxDQUFDO1FBQ2YsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixLQUFLLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7WUFFdkIsTUFBTSxLQUFLLENBQUM7UUFDZCxDQUFDO1FBRUQsS0FBSyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDN0IsQ0FBQyxDQUFDO0FBQ0osQ0FBQyJ9
@@ -28,7 +28,16 @@ class BFFHttpServer {
28
28
  const { config, version } = options;
29
29
  const app = new koa_1.default();
30
30
  app.use((0, version_header_middleware_1.default)(version));
31
- app.use((0, health_route_1.default)({ config, version }));
31
+ app.use((0, health_route_1.default)({
32
+ version,
33
+ healthy: config.hasAllRequired,
34
+ configured: {
35
+ oauth: Boolean(config.tokenEncryptionKey),
36
+ ai: Boolean(config.tokenEncryptionKey),
37
+ cors: config.allowedOrigins.length > 0,
38
+ openapi: config.openapiEnabled,
39
+ },
40
+ }));
32
41
  for (const middleware of options.middlewares ?? []) {
33
42
  app.use(middleware);
34
43
  }
@@ -80,4 +89,4 @@ class BFFHttpServer {
80
89
  }
81
90
  }
82
91
  exports.default = BFFHttpServer;
83
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmZmLWh0dHAtc2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2h0dHAvYmZmLWh0dHAtc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBTUEsZ0RBQXdCO0FBQ3hCLDhDQUFzQjtBQUV0QixrRUFBK0M7QUFDL0MsNEZBQXdFO0FBQ3hFLGdGQUE2RDtBQUM3RCw4RkFBaUU7QUE0QmpFLFNBQVMsVUFBVSxDQUFDLE9BQTZCO0lBQy9DLE9BQU8sT0FBTyxDQUFDLFFBQVEsS0FBSyxTQUFTLENBQUM7QUFDeEMsQ0FBQztBQUVELE1BQXFCLGFBQWE7SUFNaEMsWUFBWSxPQUE2QjtRQUZqQyxXQUFNLEdBQWtCLElBQUksQ0FBQztRQUduQyxJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztRQUN2QixJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLElBQUksSUFBQSx3QkFBbUIsR0FBRSxDQUFDO1FBRXRELElBQUksVUFBVSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDeEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDO1lBRWhDLE9BQU87UUFDVCxDQUFDO1FBRUQsSUFBSSxDQUFDLE9BQU8sR0FBRyxhQUFhLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ25ELElBQUEsZ0NBQWlCLEVBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDakQsQ0FBQztJQUVPLE1BQU0sQ0FBQyxZQUFZLENBQUMsT0FBeUI7UUFDbkQsTUFBTSxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsR0FBRyxPQUFPLENBQUM7UUFDcEMsTUFBTSxHQUFHLEdBQUcsSUFBSSxhQUFHLEVBQUUsQ0FBQztRQUV0QixHQUFHLENBQUMsR0FBRyxDQUFDLElBQUEsbUNBQTZCLEVBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztRQUNoRCxHQUFHLENB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
92
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmZmLWh0dHAtc2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2h0dHAvYmZmLWh0dHAtc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBTUEsZ0RBQXdCO0FBQ3hCLDhDQUFzQjtBQUV0QixrRUFBK0M7QUFDL0MsNEZBQXdFO0FBQ3hFLGdGQUE2RDtBQUM3RCw4RkFBaUU7QUE0QmpFLFNBQVMsVUFBVSxDQUFDLE9BQTZCO0lBQy9DLE9BQU8sT0FBTyxDQUFDLFFBQVEsS0FBSyxTQUFTLENBQUM7QUFDeEMsQ0FBQztBQUVELE1BQXFCLGFBQWE7SUFNaEMsWUFBWSxPQUE2QjtRQUZqQyxXQUFNLEdBQWtCLElBQUksQ0FBQztRQUduQyxJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztRQUN2QixJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLElBQUksSUFBQSx3QkFBbUIsR0FBRSxDQUFDO1FBRXRELElBQUksVUFBVSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDeEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDO1lBRWhDLE9BQU87UUFDVCxDQUFDO1FBRUQsSUFBSSxDQUFDLE9BQU8sR0FBRyxhQUFhLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ25ELElBQUEsZ0NBQWlCLEVBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDakQsQ0FBQztJQUVPLE1BQU0sQ0FBQyxZQUFZLENBQUMsT0FBeUI7UUFDbkQsTUFBTSxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsR0FBRyxPQUFPLENBQUM7UUFDcEMsTUFBTSxHQUFHLEdBQUcsSUFBSSxhQUFHLEVBQUUsQ0FBQztRQUV0QixHQUFHLENBQUMsR0FBRyxDQUFDLElBQUEsbUNBQTZCLEVBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztRQUNoRCxHQUFHLENB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
@@ -1,9 +1,22 @@
1
- import type { BFFConfig } from '../config/env-config';
2
1
  import type { Middleware } from 'koa';
3
2
  export declare const HEALTH_PATH = "/health";
3
+ /**
4
+ * Which optional surfaces this deployment was CONFIGURED to serve. Deliberately not a statement
5
+ * that they work: OAuth is `true` as soon as an encryption key is set, whether or not the Forest
6
+ * server ever answers. Naming it `configured` is the whole point — a reader who takes `oauth: true`
7
+ * for "OAuth is functional" will believe a misconfigured deployment is healthy.
8
+ */
9
+ export interface HealthConfigured {
10
+ oauth: boolean;
11
+ ai: boolean;
12
+ cors: boolean;
13
+ openapi: boolean;
14
+ }
4
15
  export interface HealthRouteOptions {
5
- config: BFFConfig;
6
16
  version: string;
17
+ /** Whether everything this deployment needs is configured. Embedded, it always is. */
18
+ healthy: boolean;
19
+ configured: HealthConfigured;
7
20
  }
8
- export default function createHealthRoute({ config, version }: HealthRouteOptions): Middleware;
21
+ export default function createHealthRoute({ version, healthy, configured, }: HealthRouteOptions): Middleware;
9
22
  //# sourceMappingURL=health-route.d.ts.map
@@ -3,15 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HEALTH_PATH = void 0;
4
4
  exports.default = createHealthRoute;
5
5
  exports.HEALTH_PATH = '/health';
6
- function createHealthRoute({ config, version }) {
6
+ function createHealthRoute({ version, healthy, configured, }) {
7
7
  return async function health(ctx, next) {
8
8
  const isHealthRequest = (ctx.method === 'GET' || ctx.method === 'HEAD') && ctx.path === exports.HEALTH_PATH;
9
9
  if (!isHealthRequest) {
10
10
  await next();
11
11
  return;
12
12
  }
13
- ctx.status = config.hasAllRequired ? 200 : 503;
14
- ctx.body = { status: config.hasAllRequired ? 'ok' : 'degraded', version };
13
+ ctx.status = healthy ? 200 : 503;
14
+ ctx.body = { status: healthy ? 'ok' : 'degraded', version, configured };
15
15
  };
16
16
  }
17
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVhbHRoLXJvdXRlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2h0dHAvaGVhbHRoLXJvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQVVBLG9DQWNDO0FBckJZLFFBQUEsV0FBVyxHQUFHLFNBQVMsQ0FBQztBQU9yQyxTQUF3QixpQkFBaUIsQ0FBQyxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQXNCO0lBQy9FLE9BQU8sS0FBSyxVQUFVLE1BQU0sQ0FBQyxHQUFHLEVBQUUsSUFBSTtRQUNwQyxNQUFNLGVBQWUsR0FDbkIsQ0FBQyxHQUFHLENBQUMsTUFBTSxLQUFLLEtBQUssSUFBSSxHQUFHLENBQUMsTUFBTSxLQUFLLE1BQU0sQ0FBQyxJQUFJLEdBQUcsQ0FBQyxJQUFJLEtBQUssbUJBQVcsQ0FBQztRQUU5RSxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7WUFDckIsTUFBTSxJQUFJLEVBQUUsQ0FBQztZQUViLE9BQU87UUFDVCxDQUFDO1FBRUQsR0FBRyxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQztRQUMvQyxHQUFHLENBQUMsSUFBSSxHQUFHLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsVUFBVSxFQUFFLE9BQU8sRUFBRSxDQUFDO0lBQzVFLENBQUMsQ0FBQztBQUNKLENBQUMifQ==
17
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVhbHRoLXJvdXRlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2h0dHAvaGVhbHRoLXJvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQXdCQSxvQ0FrQkM7QUF4Q1ksUUFBQSxXQUFXLEdBQUcsU0FBUyxDQUFDO0FBc0JyQyxTQUF3QixpQkFBaUIsQ0FBQyxFQUN4QyxPQUFPLEVBQ1AsT0FBTyxFQUNQLFVBQVUsR0FDUztJQUNuQixPQUFPLEtBQUssVUFBVSxNQUFNLENBQUMsR0FBRyxFQUFFLElBQUk7UUFDcEMsTUFBTSxlQUFlLEdBQ25CLENBQUMsR0FBRyxDQUFDLE1BQU0sS0FBSyxLQUFLLElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxNQUFNLENBQUMsSUFBSSxHQUFHLENBQUMsSUFBSSxLQUFLLG1CQUFXLENBQUM7UUFFOUUsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1lBQ3JCLE1BQU0sSUFBSSxFQUFFLENBQUM7WUFFYixPQUFPO1FBQ1QsQ0FBQztRQUVELEdBQUcsQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQztRQUNqQyxHQUFHLENBQUMsSUFBSSxHQUFHLEVBQUUsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxVQUFVLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxDQUFDO0lBQzFFLENBQUMsQ0FBQztBQUNKLENBQUMifQ==
package/dist/index.d.ts CHANGED
@@ -3,6 +3,8 @@ export { parseConfig, REQUIRED_KEYS } from './config/env-config';
3
3
  export type { BFFConfig, PresenceMap, RequiredKey } from './config/env-config';
4
4
  export { default as runCli } from './cli-core';
5
5
  export { default as buildBff } from './build-bff';
6
+ export { default as createInProcessTransport, IN_PROCESS_AGENT_URL, } from './agent/in-process-transport';
7
+ export type { AgentDispatcher, AgentDispatchRequest, AgentDispatchResponse, } from './agent/in-process-transport';
6
8
  export type { Bff, BuildBffOptions, BffCallback } from './build-bff';
7
9
  export { ConfigurationError } from './errors';
8
10
  export { default as DEFAULT_BFF_PORT } from './defaults';
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.toApiKeyErrorBody = exports.keyResolutionUnavailable = exports.invalidRequest = exports.forestIdentityNotAllowed = exports.invalidApiKey = exports.ApiKeyError = exports.BFF_KEY_HEADER = exports.createApiKeyMiddleware = exports.createApiKeyAuthenticator = exports.createResolveCache = exports.ApiKeyResolveError = exports.ApiKeyClient = exports.AGENT_TOKEN_EXPIRES_IN = exports.issueAgentToken = exports.fingerprintApiKey = exports.hashApiKey = exports.parseApiKey = exports.OAuthRequestError = exports.createPkcePair = exports.BFF_ACCESS_TOKEN_TYPE = exports.issueBffAccessToken = exports.ensureFreshServerAccess = exports.createTokenCipher = exports.createInMemorySessionStore = exports.createOAuthRoutes = exports.OAuthExchangeError = exports.ForestServerClient = exports.version = exports.createConsoleLogger = exports.DEFAULT_BFF_PORT = exports.ConfigurationError = exports.buildBff = exports.runCli = exports.REQUIRED_KEYS = exports.parseConfig = exports.BFFHttpServer = void 0;
6
+ exports.toApiKeyErrorBody = exports.keyResolutionUnavailable = exports.invalidRequest = exports.forestIdentityNotAllowed = exports.invalidApiKey = exports.ApiKeyError = exports.BFF_KEY_HEADER = exports.createApiKeyMiddleware = exports.createApiKeyAuthenticator = exports.createResolveCache = exports.ApiKeyResolveError = exports.ApiKeyClient = exports.AGENT_TOKEN_EXPIRES_IN = exports.issueAgentToken = exports.fingerprintApiKey = exports.hashApiKey = exports.parseApiKey = exports.OAuthRequestError = exports.createPkcePair = exports.BFF_ACCESS_TOKEN_TYPE = exports.issueBffAccessToken = exports.ensureFreshServerAccess = exports.createTokenCipher = exports.createInMemorySessionStore = exports.createOAuthRoutes = exports.OAuthExchangeError = exports.ForestServerClient = exports.version = exports.createConsoleLogger = exports.DEFAULT_BFF_PORT = exports.ConfigurationError = exports.IN_PROCESS_AGENT_URL = exports.createInProcessTransport = exports.buildBff = exports.runCli = exports.REQUIRED_KEYS = exports.parseConfig = exports.BFFHttpServer = void 0;
7
7
  var bff_http_server_1 = require("./http/bff-http-server");
8
8
  Object.defineProperty(exports, "BFFHttpServer", { enumerable: true, get: function () { return __importDefault(bff_http_server_1).default; } });
9
9
  var env_config_1 = require("./config/env-config");
@@ -13,6 +13,9 @@ var cli_core_1 = require("./cli-core");
13
13
  Object.defineProperty(exports, "runCli", { enumerable: true, get: function () { return __importDefault(cli_core_1).default; } });
14
14
  var build_bff_1 = require("./build-bff");
15
15
  Object.defineProperty(exports, "buildBff", { enumerable: true, get: function () { return __importDefault(build_bff_1).default; } });
16
+ var in_process_transport_1 = require("./agent/in-process-transport");
17
+ Object.defineProperty(exports, "createInProcessTransport", { enumerable: true, get: function () { return __importDefault(in_process_transport_1).default; } });
18
+ Object.defineProperty(exports, "IN_PROCESS_AGENT_URL", { enumerable: true, get: function () { return in_process_transport_1.IN_PROCESS_AGENT_URL; } });
16
19
  var errors_1 = require("./errors");
17
20
  Object.defineProperty(exports, "ConfigurationError", { enumerable: true, get: function () { return errors_1.ConfigurationError; } });
18
21
  var defaults_1 = require("./defaults");
@@ -63,4 +66,4 @@ Object.defineProperty(exports, "forestIdentityNotAllowed", { enumerable: true, g
63
66
  Object.defineProperty(exports, "invalidRequest", { enumerable: true, get: function () { return api_key_error_1.invalidRequest; } });
64
67
  Object.defineProperty(exports, "keyResolutionUnavailable", { enumerable: true, get: function () { return api_key_error_1.keyResolutionUnavailable; } });
65
68
  Object.defineProperty(exports, "toApiKeyErrorBody", { enumerable: true, get: function () { return api_key_error_1.toErrorBody; } });
66
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsMERBQWtFO0FBQXpELGlJQUFBLE9BQU8sT0FBaUI7QUFDakMsa0RBQWlFO0FBQXhELHlHQUFBLFdBQVcsT0FBQTtBQUFFLDJHQUFBLGFBQWEsT0FBQTtBQUVuQyx1Q0FBK0M7QUFBdEMsbUhBQUEsT0FBTyxPQUFVO0FBQzFCLHlDQUFrRDtBQUF6QyxzSEFBQSxPQUFPLE9BQVk7QUFFNUIsbUNBQThDO0FBQXJDLDRHQUFBLGtCQUFrQixPQUFBO0FBQzNCLHVDQUF5RDtBQUFoRCw2SEFBQSxPQUFPLE9BQW9CO0FBQ3BDLDREQUEyRTtBQUFsRSxzSUFBQSxPQUFPLE9BQXVCO0FBRXZDLHFDQUErQztBQUF0QyxtSEFBQSxPQUFPLE9BQVc7QUFDM0IscUVBQWlHO0FBQXhGLDJJQUFBLE9BQU8sT0FBc0I7QUFBRSwwSEFBQSxrQkFBa0IsT0FBQTtBQU0xRCxxREFBb0U7QUFBM0Qsa0lBQUEsT0FBTyxPQUFxQjtBQUNyQyx1REFBOEU7QUFBckUsNElBQUEsT0FBTyxPQUE4QjtBQUU5QyxxREFBb0U7QUFBM0Qsa0lBQUEsT0FBTyxPQUFxQjtBQUNyQywrREFBK0U7QUFBdEUsNklBQUEsT0FBTyxPQUEyQjtBQUMzQywrQ0FBK0U7QUFBdEUsZ0hBQUEsbUJBQW1CLE9BQUE7QUFBRSxrSEFBQSxxQkFBcUIsT0FBQTtBQUVuRCxxQ0FBOEM7QUFBckMsc0dBQUEsY0FBYyxPQUFBO0FBQ3ZCLG1EQUF3RDtBQUEvQyxnSEFBQSxpQkFBaUIsT0FBQTtBQUMxQiw2Q0FBK0U7QUFBdEUsc0dBQUEsV0FBVyxPQUFBO0FBQUUscUdBQUEsVUFBVSxPQUFBO0FBQUUsNEdBQUEsaUJBQWlCLE9BQUE7QUFFbkQscURBQWdGO0FBQXZFLDhHQUFBLGVBQWUsT0FBQTtBQUFFLHFIQUFBLHNCQUFzQixPQUFBO0FBQ2hELDJEQUF1RjtBQUE5RSwrSEFBQSxPQUFPLE9BQWdCO0FBQUUsb0hBQUEsa0JBQWtCLE9BQUE7QUFNcEQseURBQXdFO0FBQS9ELG9JQUFBLE9BQU8sT0FBc0I7QUFFdEMseUVBQXVGO0FBQTlFLG1KQUFBLE9BQU8sT0FBNkI7QUFNN0MsbUVBQWlHO0FBQXhGLDZJQUFBLE9BQU8sT0FBMEI7QUFBRSxvSEFBQSxjQUFjLE9BQUE7QUFDMUQseURBT2lDO0FBTi9CLDRHQUFBLFdBQVcsT0FBQTtBQUNYLDhHQUFBLGFBQWEsT0FBQTtBQUNiLHlIQUFBLHdCQUF3QixPQUFBO0FBQ3hCLCtHQUFBLGNBQWMsT0FBQTtBQUNkLHlIQUFBLHdCQUF3QixPQUFBO0FBQ3hCLGtIQUFBLFdBQVcsT0FBcUIifQ==
69
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsMERBQWtFO0FBQXpELGlJQUFBLE9BQU8sT0FBaUI7QUFDakMsa0RBQWlFO0FBQXhELHlHQUFBLFdBQVcsT0FBQTtBQUFFLDJHQUFBLGFBQWEsT0FBQTtBQUVuQyx1Q0FBK0M7QUFBdEMsbUhBQUEsT0FBTyxPQUFVO0FBQzFCLHlDQUFrRDtBQUF6QyxzSEFBQSxPQUFPLE9BQVk7QUFDNUIscUVBR3NDO0FBRnBDLGlKQUFBLE9BQU8sT0FBNEI7QUFDbkMsNEhBQUEsb0JBQW9CLE9BQUE7QUFRdEIsbUNBQThDO0FBQXJDLDRHQUFBLGtCQUFrQixPQUFBO0FBQzNCLHVDQUF5RDtBQUFoRCw2SEFBQSxPQUFPLE9BQW9CO0FBQ3BDLDREQUEyRTtBQUFsRSxzSUFBQSxPQUFPLE9BQXVCO0FBRXZDLHFDQUErQztBQUF0QyxtSEFBQSxPQUFPLE9BQVc7QUFDM0IscUVBQWlHO0FBQXhGLDJJQUFBLE9BQU8sT0FBc0I7QUFBRSwwSEFBQSxrQkFBa0IsT0FBQTtBQU0xRCxxREFBb0U7QUFBM0Qsa0lBQUEsT0FBTyxPQUFxQjtBQUNyQyx1REFBOEU7QUFBckUsNElBQUEsT0FBTyxPQUE4QjtBQUU5QyxxREFBb0U7QUFBM0Qsa0lBQUEsT0FBTyxPQUFxQjtBQUNyQywrREFBK0U7QUFBdEUsNklBQUEsT0FBTyxPQUEyQjtBQUMzQywrQ0FBK0U7QUFBdEUsZ0hBQUEsbUJBQW1CLE9BQUE7QUFBRSxrSEFBQSxxQkFBcUIsT0FBQTtBQUVuRCxxQ0FBOEM7QUFBckMsc0dBQUEsY0FBYyxPQUFBO0FBQ3ZCLG1EQUF3RDtBQUEvQyxnSEFBQSxpQkFBaUIsT0FBQTtBQUMxQiw2Q0FBK0U7QUFBdEUsc0dBQUEsV0FBVyxPQUFBO0FBQUUscUdBQUEsVUFBVSxPQUFBO0FBQUUsNEdBQUEsaUJBQWlCLE9BQUE7QUFFbkQscURBQWdGO0FBQXZFLDhHQUFBLGVBQWUsT0FBQTtBQUFFLHFIQUFBLHNCQUFzQixPQUFBO0FBQ2hELDJEQUF1RjtBQUE5RSwrSEFBQSxPQUFPLE9BQWdCO0FBQUUsb0hBQUEsa0JBQWtCLE9BQUE7QUFNcEQseURBQXdFO0FBQS9ELG9JQUFBLE9BQU8sT0FBc0I7QUFFdEMseUVBQXVGO0FBQTlFLG1KQUFBLE9BQU8sT0FBNkI7QUFNN0MsbUVBQWlHO0FBQXhGLDZJQUFBLE9BQU8sT0FBMEI7QUFBRSxvSEFBQSxjQUFjLE9BQUE7QUFDMUQseURBT2lDO0FBTi9CLDRHQUFBLFdBQVcsT0FBQTtBQUNYLDhHQUFBLGFBQWEsT0FBQTtBQUNiLHlIQUFBLHdCQUF3QixPQUFBO0FBQ3hCLCtHQUFBLGNBQWMsT0FBQTtBQUNkLHlIQUFBLHdCQUF3QixPQUFBO0FBQ3hCLGtIQUFBLFdBQVcsT0FBcUIifQ==
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forestadmin/agent-bff",
3
- "version": "1.28.1",
3
+ "version": "1.30.0",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "forest-bff": "dist/cli.js"
@@ -44,8 +44,6 @@
44
44
  "zod": "4.3.6"
45
45
  },
46
46
  "devDependencies": {
47
- "@forestadmin/agent": "1.99.2",
48
- "@forestadmin/agent-testing": "1.2.16",
49
47
  "@hey-api/openapi-ts": "0.99.0",
50
48
  "@redocly/cli": "2.35.1",
51
49
  "@types/inflected": "^1.1.29",