@forestadmin/agent-bff 1.13.0 → 1.14.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
@@ -5,8 +5,8 @@ agent from a browser without learning MCP or JSON:API.
5
5
 
6
6
  It is a bootable Koa 3 server with a `/health` endpoint, a version header, env-driven config
7
7
  validation, OAuth (Mode 1) + API-key (Mode 2) auth, and a hardened request edge (timezone, CORS,
8
- auth-mode precedence, structured error contract). The data-endpoint proxy and OpenAPI generation
9
- land in later slices.
8
+ auth-mode precedence, structured error contract), and it serves and exports its own OpenAPI
9
+ document. The data-endpoint proxy lands in a later slice.
10
10
 
11
11
  ## Usage
12
12
 
@@ -16,6 +16,25 @@ Packaged / production — run the bin:
16
16
  forest-bff
17
17
  ```
18
18
 
19
+ Export the OpenAPI document without booting the server. Needs no configuration, so
20
+ it works in CI to commit the document, diff it, or generate a client:
21
+
22
+ ```bash
23
+ forest-bff openapi > openapi.json # stdout, redirected
24
+ forest-bff openapi --output # writes ./openapi.json
25
+ forest-bff openapi --output docs/api.json # writes that path
26
+ ```
27
+
28
+ `--output` takes the next argument as the destination unless it is empty or starts with
29
+ `-`; the default `openapi.json` is written only when `--output` is the last argument, and
30
+ a leftover token is rejected like any other extra. A missing parent directory is created,
31
+ an existing file is overwritten, and a destination that cannot be written exits 1 with the
32
+ path on stderr. The path is confirmed on stderr, so stdout stays empty and pipeable.
33
+
34
+ `forest-bff --help` and `forest-bff --version` print to stdout and exit 0, ignoring
35
+ anything that follows. An unknown command, or an argument other than `--output` after
36
+ `openapi`, exits 1 with the reason on stderr.
37
+
19
38
  Local development — copy the env template, fill it in, and start with it loaded:
20
39
 
21
40
  ```bash
@@ -0,0 +1,12 @@
1
+ import type BFFHttpServer from './http/bff-http-server';
2
+ import type { Logger } from './ports/logger-port';
3
+ export declare const DEFAULT_OUTPUT_FILE = "openapi.json";
4
+ export declare const USAGE = "Usage: forest-bff [command]\n\nCommands:\n (none) Start the BFF server, configured from the environment.\n openapi Write the OpenAPI document to stdout. Needs no configuration.\n --output [file] Write to a file instead of stdout, defaulting\n to openapi.json in the current directory.\n\nOptions:\n -h, --help Show this help and exit.\n -v, --version Show the package version and exit.";
5
+ export declare const HINT = "Run 'forest-bff --help' for usage.";
6
+ export interface DispatchOutcome {
7
+ exitCode: number;
8
+ server?: BFFHttpServer;
9
+ }
10
+ export declare function renderOpenApi(): string;
11
+ export default function dispatchCli(argv: string[], env: NodeJS.ProcessEnv, logger?: Logger): Promise<DispatchOutcome>;
12
+ //# sourceMappingURL=cli-dispatch.d.ts.map
@@ -0,0 +1,91 @@
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
+ exports.HINT = exports.USAGE = exports.DEFAULT_OUTPUT_FILE = void 0;
7
+ exports.renderOpenApi = renderOpenApi;
8
+ exports.default = dispatchCli;
9
+ const fs_1 = require("fs");
10
+ const path_1 = __importDefault(require("path"));
11
+ const cli_core_1 = __importDefault(require("./cli-core"));
12
+ const errors_1 = require("./errors");
13
+ const openapi_document_1 = require("./openapi/openapi-document");
14
+ const version_1 = __importDefault(require("./version"));
15
+ exports.DEFAULT_OUTPUT_FILE = 'openapi.json';
16
+ const OUTPUT_FLAG = '--output';
17
+ exports.USAGE = `Usage: forest-bff [command]
18
+
19
+ Commands:
20
+ (none) Start the BFF server, configured from the environment.
21
+ openapi Write the OpenAPI document to stdout. Needs no configuration.
22
+ --output [file] Write to a file instead of stdout, defaulting
23
+ to ${exports.DEFAULT_OUTPUT_FILE} in the current directory.
24
+
25
+ Options:
26
+ -h, --help Show this help and exit.
27
+ -v, --version Show the package version and exit.`;
28
+ exports.HINT = "Run 'forest-bff --help' for usage.";
29
+ const HELP_FLAGS = new Set(['-h', '--help']);
30
+ const VERSION_FLAGS = new Set(['-v', '--version']);
31
+ function renderOpenApi() {
32
+ return `${(0, openapi_document_1.serializeOpenApi)((0, openapi_document_1.generateOpenApiDocument)(version_1.default))}\n`;
33
+ }
34
+ function rejectCli(reason) {
35
+ process.stderr.write(`${reason}\n${exports.HINT}\n`);
36
+ return { exitCode: 1 };
37
+ }
38
+ function parseOutputOption(rest) {
39
+ const index = rest.indexOf(OUTPUT_FLAG);
40
+ if (index === -1)
41
+ return { extras: rest };
42
+ const candidate = rest[index + 1];
43
+ const takesValue = candidate !== undefined && candidate !== '' && !candidate.startsWith('-');
44
+ return {
45
+ file: takesValue ? candidate : exports.DEFAULT_OUTPUT_FILE,
46
+ extras: [...rest.slice(0, index), ...rest.slice(index + (takesValue ? 2 : 1))],
47
+ };
48
+ }
49
+ function writeOpenApiFile(file) {
50
+ const asDirectory = file.endsWith('/') || file.endsWith(path_1.default.sep);
51
+ const destination = path_1.default.resolve(process.cwd(), asDirectory ? path_1.default.join(file, exports.DEFAULT_OUTPUT_FILE) : file);
52
+ try {
53
+ (0, fs_1.mkdirSync)(path_1.default.dirname(destination), { recursive: true });
54
+ (0, fs_1.writeFileSync)(destination, renderOpenApi());
55
+ }
56
+ catch (error) {
57
+ process.stderr.write(`Cannot write ${destination}: ${(0, errors_1.extractErrorMessage)(error)}\n`);
58
+ return { exitCode: 1 };
59
+ }
60
+ process.stderr.write(`Wrote the OpenAPI document to ${destination}\n`);
61
+ return { exitCode: 0 };
62
+ }
63
+ async function dispatchCli(argv, env, logger) {
64
+ const [subcommand, ...rest] = argv;
65
+ if (subcommand === undefined) {
66
+ return { exitCode: 0, server: await (0, cli_core_1.default)(env, logger) };
67
+ }
68
+ if (HELP_FLAGS.has(subcommand)) {
69
+ process.stdout.write(`${exports.USAGE}\n`);
70
+ return { exitCode: 0 };
71
+ }
72
+ if (VERSION_FLAGS.has(subcommand)) {
73
+ process.stdout.write(`${version_1.default}\n`);
74
+ return { exitCode: 0 };
75
+ }
76
+ if (subcommand !== 'openapi') {
77
+ return rejectCli(subcommand === OUTPUT_FLAG
78
+ ? `${OUTPUT_FLAG} only applies to the openapi command`
79
+ : `Unknown command: ${subcommand}`);
80
+ }
81
+ const { file, extras } = parseOutputOption(rest);
82
+ if (extras.length > 0) {
83
+ return rejectCli(`openapi accepts only --output, got: ${extras.map(extra => JSON.stringify(extra)).join(' ')}`);
84
+ }
85
+ if (file !== undefined) {
86
+ return writeOpenApiFile(file);
87
+ }
88
+ process.stdout.write(renderOpenApi());
89
+ return { exitCode: 0 };
90
+ }
91
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLWRpc3BhdGNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NsaS1kaXNwYXRjaC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFxQ0Esc0NBRUM7QUFnREQsOEJBOENDO0FBbElELDJCQUE4QztBQUM5QyxnREFBd0I7QUFFeEIsMERBQWdDO0FBQ2hDLHFDQUErQztBQUMvQyxpRUFBdUY7QUFDdkYsd0RBQWdDO0FBRW5CLFFBQUEsbUJBQW1CLEdBQUcsY0FBYyxDQUFDO0FBRWxELE1BQU0sV0FBVyxHQUFHLFVBQVUsQ0FBQztBQUVsQixRQUFBLEtBQUssR0FBRzs7Ozs7O29DQU1lLDJCQUFtQjs7OztvREFJSCxDQUFDO0FBRXhDLFFBQUEsSUFBSSxHQUFHLG9DQUFvQyxDQUFDO0FBRXpELE1BQU0sVUFBVSxHQUFHLElBQUksR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUM7QUFDN0MsTUFBTSxhQUFhLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsV0FBVyxDQUFDLENBQUMsQ0FBQztBQU9uRCxTQUFnQixhQUFhO0lBQzNCLE9BQU8sR0FBRyxJQUFBLG1DQUFnQixFQUFDLElBQUEsMENBQXVCLEVBQUMsaUJBQU8sQ0FBQyxDQUFDLElBQUksQ0FBQztBQUNuRSxDQUFDO0FBRUQsU0FBUyxTQUFTLENBQUMsTUFBYztJQUMvQixPQUFPLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLE1BQU0sS0FBSyxZQUFJLElBQUksQ0FBQyxDQUFDO0lBRTdDLE9BQU8sRUFBRSxRQUFRLEVBQUUsQ0FBQyxFQUFFLENBQUM7QUFDekIsQ0FBQztBQU9ELFNBQVMsaUJBQWlCLENBQUMsSUFBYztJQUN2QyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBRXhDLElBQUksS0FBSyxLQUFLLENBQUMsQ0FBQztRQUFFLE9BQU8sRUFBRSxNQUFN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
package/dist/cli.js CHANGED
@@ -1,42 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
- if (k2 === undefined) k2 = k;
5
- var desc = Object.getOwnPropertyDescriptor(m, k);
6
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
- desc = { enumerable: true, get: function() { return m[k]; } };
8
- }
9
- Object.defineProperty(o, k2, desc);
10
- }) : (function(o, m, k, k2) {
11
- if (k2 === undefined) k2 = k;
12
- o[k2] = m[k];
13
- }));
14
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
- Object.defineProperty(o, "default", { enumerable: true, value: v });
16
- }) : function(o, v) {
17
- o["default"] = v;
18
- });
19
- var __importStar = (this && this.__importStar) || (function () {
20
- var ownKeys = function(o) {
21
- ownKeys = Object.getOwnPropertyNames || function (o) {
22
- var ar = [];
23
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
- return ar;
25
- };
26
- return ownKeys(o);
27
- };
28
- return function (mod) {
29
- if (mod && mod.__esModule) return mod;
30
- var result = {};
31
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
- __setModuleDefault(result, mod);
33
- return result;
34
- };
35
- })();
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
36
6
  Object.defineProperty(exports, "__esModule", { value: true });
37
7
  /* istanbul ignore file */
38
- const cli_core_1 = __importStar(require("./cli-core"));
8
+ const cli_core_1 = require("./cli-core");
9
+ const cli_dispatch_1 = __importDefault(require("./cli-dispatch"));
39
10
  if (require.main === module) {
40
- (0, cli_core_1.default)(process.env).catch(cli_core_1.reportFatalError);
11
+ (0, cli_dispatch_1.default)(process.argv.slice(2), process.env)
12
+ .then(({ exitCode }) => {
13
+ if (exitCode !== 0)
14
+ process.exitCode = exitCode;
15
+ })
16
+ .catch(cli_core_1.reportFatalError);
41
17
  }
42
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NsaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFDQSwwQkFBMEI7QUFDMUIsdURBQXNEO0FBRXRELElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxNQUFNLEVBQUUsQ0FBQztJQUM1QixJQUFBLGtCQUFNLEVBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQywyQkFBZ0IsQ0FBQyxDQUFDO0FBQzlDLENBQUMifQ==
18
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NsaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFDQSwwQkFBMEI7QUFDMUIseUNBQThDO0FBQzlDLGtFQUF5QztBQUV6QyxJQUFJLE9BQU8sQ0FBQyxJQUFJLEtBQUssTUFBTSxFQUFFLENBQUM7SUFDNUIsSUFBQSxzQkFBVyxFQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUM7U0FDNUMsSUFBSSxDQUFDLENBQUMsRUFBRSxRQUFRLEVBQUUsRUFBRSxFQUFFO1FBQ3JCLElBQUksUUFBUSxLQUFLLENBQUM7WUFBRSxPQUFPLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQztJQUNsRCxDQUFDLENBQUM7U0FDRCxLQUFLLENBQUMsMkJBQWdCLENBQUMsQ0FBQztBQUM3QixDQUFDIn0=
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forestadmin/agent-bff",
3
- "version": "1.13.0",
3
+ "version": "1.14.0",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "forest-bff": "dist/cli.js"