@forestadmin/agent-bff 1.32.0 → 1.34.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
@@ -36,6 +36,108 @@ dropped collection reachable.
36
36
  Two ways to run it: embedded in a Forest agent (`agent.addBff()`, see
37
37
  [Embedded in an agent](#embedded-in-an-agent)) or standalone, described here.
38
38
 
39
+ ### Docker (recommended)
40
+
41
+ ```bash
42
+ cp .env.example .env # then fill in the secrets
43
+ docker compose up
44
+ ```
45
+
46
+ The template targets a local, non-containerised run, so one value has to change for Docker:
47
+ set `AGENT_URL=http://host.docker.internal:3351`. Left at `localhost`, it resolves to the BFF
48
+ container itself and every agent call fails (see the note below).
49
+
50
+ The `docker-compose.yml` at the root of this package starts a single BFF instance. See
51
+ `.env.example` for the full list of environment variables and their descriptions.
52
+
53
+ Or run the image directly:
54
+
55
+ ```bash
56
+ docker run -d \
57
+ -p 3450:3450 \
58
+ --stop-timeout 15 \
59
+ --add-host host.docker.internal:host-gateway \
60
+ -e FOREST_AUTH_SECRET="..." \
61
+ -e FOREST_ENV_SECRET="..." \
62
+ -e AGENT_URL="http://host.docker.internal:3351" \
63
+ -e BFF_TOKEN_ENCRYPTION_KEY="$(openssl rand -base64 32)" \
64
+ ghcr.io/forestadmin/agent-bff:latest
65
+ ```
66
+
67
+ > **Note:** When the BFF runs in Docker and your agent runs on the host machine, use
68
+ > `host.docker.internal` instead of `localhost` in `AGENT_URL`. Docker Desktop resolves that
69
+ > name natively; on Docker Engine for Linux it does not exist unless you map it, hence the
70
+ > `--add-host` above (the Compose setup does the same through `extra_hosts`).
71
+
72
+ The image's entry point is the CLI, so the subcommands below work the same way:
73
+
74
+ ```bash
75
+ docker run --rm ghcr.io/forestadmin/agent-bff:latest openapi > openapi.json
76
+ ```
77
+
78
+ Tags follow the npm package: `:latest`, `:1`, `:1.20` and the immutable `:1.20.2`.
79
+
80
+ The package is public, so none of the commands above need a login. That visibility is set once,
81
+ by hand, on the GHCR package: a package GHCR creates on its first push is private, and the
82
+ workflow's `GITHUB_TOKEN` can push to it but not change what it is. Until someone flips it (the
83
+ same step `workflow-executor` went through), pulls need
84
+ `docker login ghcr.io -u <user> -p <token-with-read:packages>`.
85
+
86
+ On `SIGTERM` or `SIGINT` the BFF stops accepting connections and gives the requests already in
87
+ flight 10 seconds to finish before cutting their sockets, then exits 0. A second signal gives up on
88
+ the wait and exits 1.
89
+
90
+ Allow for that in your orchestrator's grace period. The whole budget is up to 11 seconds — the 10
91
+ second deadline plus a 1 second fallback for the exit itself — and `docker stop` defaults to 10,
92
+ so under load it would SIGKILL exactly when the shutdown is doing its job. Hence `--stop-timeout 15`
93
+ above and `stop_grace_period: 15s` in the Compose file; on Kubernetes the default
94
+ `terminationGracePeriodSeconds` of 30 already covers it.
95
+
96
+ ### Observability (OpenTelemetry)
97
+
98
+ The Docker image ships with [OpenTelemetry](https://opentelemetry.io/) APM built in, and works with
99
+ any OTLP-compatible backend (Datadog, Grafana Tempo, Jaeger, Honeycomb, etc.). It is **off by
100
+ default** and turns on as soon as you point it at an OTLP receiver — no code changes or extra
101
+ installs required. A setup that cannot start logs a warning and runs untraced rather than taking
102
+ the process down with it. Tracing is set up before the app starts (auto-instrumentation for HTTP and the
103
+ outbound calls to the agent and the Forest SaaS). The graceful shutdown described above waits for
104
+ the buffered spans to be exported before it exits, but gives that its own 2 second deadline rather
105
+ than the 10 seconds in-flight requests get: an unreachable collector costs you the last spans, never
106
+ the ability to stop. Worst case it adds ~3 seconds to a shutdown.
107
+
108
+ Configure it entirely through the standard OTel environment variables:
109
+
110
+ | Variable | Description |
111
+ | --- | --- |
112
+ | `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP receiver URL (e.g. `http://collector:4318`). **Tracing stays off until this or `OTEL_TRACES_EXPORTER` is set.** |
113
+ | `OTEL_SERVICE_NAME` | Service name reported in traces. Falls back to `service.name` in `OTEL_RESOURCE_ATTRIBUTES`, then to `forestadmin-agent-bff`. |
114
+ | `OTEL_RESOURCE_ATTRIBUTES` | Extra resource attributes, e.g. `deployment.environment=production`. A `service.name` here is honoured when `OTEL_SERVICE_NAME` is unset. |
115
+ | `OTEL_SDK_DISABLED` | Set to `true` (case-insensitive) to force-disable tracing whatever else is configured. |
116
+ | `OTEL_TRACES_EXPORTER` | Which exporter the SDK builds: `otlp` (the default), `console`, `zipkin`, `none`, or a list. Setting it alone turns tracing on without an OTLP endpoint, which is what makes `console` usable for debugging. `none` keeps instrumentation running with nothing exported, so trace context still propagates to the agent and the Forest SaaS. |
117
+ | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | Per-signal endpoint, taking precedence over the generic one above. Setting either turns tracing on. |
118
+ | `OTEL_EXPORTER_OTLP_PROTOCOL` | `http/protobuf` (the default), `http/json` or `grpc`, with `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` for traces alone. |
119
+ | `OTEL_METRICS_EXPORTER` / `OTEL_LOGS_EXPORTER` | Default to `none` here, against the SDK's own `otlp`: this image arms tracing, and leaving them unset would otherwise export metrics and logs to `http://localhost:4318` on the side. Set either one (`otlp`, `console`, …) to opt that signal back in. |
120
+
121
+ ```bash
122
+ docker run -d \
123
+ -p 3450:3450 \
124
+ --stop-timeout 15 \
125
+ --add-host host.docker.internal:host-gateway \
126
+ -e FOREST_AUTH_SECRET="..." \
127
+ -e FOREST_ENV_SECRET="..." \
128
+ -e AGENT_URL="http://host.docker.internal:3351" \
129
+ -e BFF_TOKEN_ENCRYPTION_KEY="$(openssl rand -base64 32)" \
130
+ -e OTEL_EXPORTER_OTLP_ENDPOINT="http://collector:4318" \
131
+ ghcr.io/forestadmin/agent-bff:latest
132
+ ```
133
+
134
+ > **Note:** These variables only do anything in the Docker image. The image's entry point loads
135
+ > the tracing preload before the CLI; the npm `forest-bff` bin runs the CLI on its own, and the
136
+ > OpenTelemetry packages are not npm dependencies — so outside Docker an `OTEL_*` variable is
137
+ > read by nothing and the process starts untraced, silently.
138
+
139
+ ### Without Docker
140
+
39
141
  Packaged / production — run the bin:
40
142
 
41
143
  ```bash
@@ -57,7 +159,7 @@ without it a client generated from the export has no base URL at all.
57
159
 
58
160
  The document comes in two forms, and the command picks one from the environment:
59
161
 
60
- - **Unfolded** when `FOREST_SERVER_URL`, `FOREST_ENV_SECRET`, `FOREST_AUTH_SECRET` and `AGENT_URL`
162
+ - **Unfolded** when `FOREST_ENV_SECRET`, `FOREST_AUTH_SECRET` and `AGENT_URL`
61
163
  are all set: one path per exposed collection, per to-many relation and per action, each carrying
62
164
  the collection's real field set. This is the form to generate a client from. The command reads the
63
165
  Forest schema and asks the agent for each collection's capabilities, signing its own short-lived
@@ -95,11 +197,11 @@ yarn start:dev # node --env-file=.env dist/cli.js
95
197
  | ----------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
96
198
  | `FOREST_AUTH_SECRET` | yes | Agent JWT signing secret (never logged or echoed). |
97
199
  | `FOREST_ENV_SECRET` | yes | Forest SaaS environment secret (`forest-secret-key`), server-to-server. |
98
- | `FOREST_SERVER_URL` | yes | Forest SaaS API base URL. |
99
- | `FOREST_APP_URL` | yes | Forest front base URL, used to build the OAuth front-channel redirect (`src/oauth/oauth-routes.ts`). |
200
+ | `FOREST_SERVER_URL` | no | Forest SaaS API base URL. Defaults to `https://api.forestadmin.com`. |
201
+ | `FOREST_APP_URL` | no | Forest front base URL, used to build the OAuth front-channel redirect (`src/oauth/oauth-routes.ts`). Defaults to `https://app.forestadmin.com`. |
100
202
  | `AGENT_URL` | yes | The customer agent base URL the BFF calls via agent-client. |
101
203
  | `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. |
102
- | `HTTP_PORT` | no | Server port, integer 0–65535. Defaults to `3450`. `0` binds an OS-assigned ephemeral port. |
204
+ | `HTTP_PORT` | no | Server port, integer 0–65535. Defaults to `3450`. `0` binds an OS-assigned ephemeral port — useful for a local run, unusable in the Docker image, where nothing outside the process learns which port it got: it can be neither published nor probed, and the image's healthcheck would report the container unhealthy forever. |
103
205
  | `BFF_ALLOWED_ORIGINS` | no | Comma-separated CORS allow-list of origins (scheme + host + port). An entry may carry a single `*` as the leading host label — `https://*.apps.zdusercontent.com` — which matches exactly one DNS label there, and nothing else: not two labels, not the apex, and never the scheme or the port. The host left after `*.` must be at least two non-empty labels, so `https://*.com` is refused; a two-label public suffix such as `https://*.co.uk` is not, and would allow every site under it. Any other `*` in the host is refused and warned about at boot; a `*` outside the host — in userinfo, a path or a query — is stripped along with the rest of the URL, so `https://*@example.com` is simply the exact origin `https://example.com`. Empty ⇒ no cross-origin browser access. |
104
206
  | `BFF_DEFAULT_TIMEZONE` | no | Fallback IANA timezone used when a request carries neither an `X-Forest-Timezone` header nor a body `timezone`. |
105
207
  | `BFF_PUBLIC_URL` | no | The BFF's own external base URL, published as `servers[0].url` in the OpenAPI document so a generated client resolves endpoints without being configured by hand. Absent, `servers[0].url` stays `/`, which a consumer that fetched the document over HTTP resolves against that URL — but which leaves a client generated from an offline `forest-bff openapi` export with no base URL at all. Trailing slashes are stripped. A malformed value fails the boot, and so does one carrying credentials, a query string or a fragment: credentials would be published to every reader of the document, and anything behind a `?` or `#` swallows the path a generated client appends. |
@@ -274,7 +376,7 @@ answers. They form a chain, not four independent switches:
274
376
 
275
377
  | Surface | Switched on by |
276
378
  | --- | --- |
277
- | `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 |
379
+ | `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. The two urls default to production, so standalone only the two secrets and the key are yours to set. Embedded, only `tokenEncryptionKey` is yours to set: the other four are inherited |
278
380
  | `ai` | `oauth` — the relay needs a session, and only the OAuth flow creates one |
279
381
  | `cors` | a non-empty `BFF_ALLOWED_ORIGINS` (`allowedOrigins`) |
280
382
  | `openapi` | `BFF_OPENAPI_ENABLED` (`openapiEnabled`), and a mounted agent edge |
@@ -1,7 +1,7 @@
1
1
  import type BFFHttpServer from './http/bff-http-server';
2
2
  import type { Logger } from './ports/logger-port';
3
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, but\n a deployment configured to reach its Forest schema and its agent\n (FOREST_SERVER_URL, FOREST_ENV_SECRET, FOREST_AUTH_SECRET,\n AGENT_URL) unfolds one path per collection, relation and action\n instead of the generic ones.\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.";
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, but\n a deployment configured to reach its Forest schema and its agent\n (FOREST_ENV_SECRET, FOREST_AUTH_SECRET, AGENT_URL) unfolds one\n path per collection, relation and action instead of the generic\n ones.\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
5
  export declare const HINT = "Run 'forest-bff --help' for usage.";
6
6
  export interface DispatchOutcome {
7
7
  exitCode: number;
@@ -59,9 +59,9 @@ Commands:
59
59
  (none) Start the BFF server, configured from the environment.
60
60
  openapi Write the OpenAPI document to stdout. Needs no configuration, but
61
61
  a deployment configured to reach its Forest schema and its agent
62
- (FOREST_SERVER_URL, FOREST_ENV_SECRET, FOREST_AUTH_SECRET,
63
- AGENT_URL) unfolds one path per collection, relation and action
64
- instead of the generic ones.
62
+ (FOREST_ENV_SECRET, FOREST_AUTH_SECRET, AGENT_URL) unfolds one
63
+ path per collection, relation and action instead of the generic
64
+ ones.
65
65
  --output [file] Write to a file instead of stdout, defaulting
66
66
  to ${exports.DEFAULT_OUTPUT_FILE} in the current directory.
67
67
 
@@ -71,12 +71,7 @@ Options:
71
71
  exports.HINT = "Run 'forest-bff --help' for usage.";
72
72
  const HELP_FLAGS = new Set(['-h', '--help']);
73
73
  const VERSION_FLAGS = new Set(['-v', '--version']);
74
- const UNFOLD_VARS = [
75
- 'FOREST_SERVER_URL',
76
- 'FOREST_ENV_SECRET',
77
- 'FOREST_AUTH_SECRET',
78
- 'AGENT_URL',
79
- ];
74
+ const UNFOLD_VARS = ['FOREST_ENV_SECRET', 'FOREST_AUTH_SECRET', 'AGENT_URL'];
80
75
  const NOTHING_TO_UNFOLD = `${UNFOLD_VARS.join(', ')} must all be set to unfold it`;
81
76
  function wantsUnfolding(env) {
82
77
  return UNFOLD_VARS.every(name => (env[name] ?? '').trim() !== '');
@@ -102,7 +97,7 @@ async function renderOpenApi(env, logger) {
102
97
  // `parseConfig` validates the WHOLE server configuration, including settings the export has nothing
103
98
  // to do with (HTTP_PORT, the OAuth keys, the default timezone). A deployment that never asked for an
104
99
  // unfolded document must not see its export die on one of those, so the config is parsed only once
105
- // the four variables unfolding needs are all present — and from there a bad value is a real failure.
100
+ // the variables unfolding needs are all present — and from there a bad value is a real failure.
106
101
  const unfoldable = authSecret && wantsUnfolding(env)
107
102
  ? { source: (0, build_bff_1.resolveUnfoldSource)((0, env_config_1.parseConfig)(env), logger), authSecret }
108
103
  : undefined;
@@ -183,4 +178,4 @@ async function dispatchCli(argv, env, logger) {
183
178
  process.stdout.write(document);
184
179
  return { exitCode: 0 };
185
180
  }
186
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLWRpc3BhdGNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NsaS1kaXNwYXRjaC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFpRkEsc0NBNENDO0FBZ0RELDhCQWdEQztBQTFORCwyQkFBOEM7QUFDOUMsZ0RBQXdCO0FBRXhCLCtFQUE0RDtBQUM1RCxvRUFBMkQ7QUFDM0QsMkNBQXNFO0FBQ3RFLDBEQUFnQztBQUNoQyxvREFBa0U7QUFDbEUscUNBQStDO0FBQy9DLGlFQUF1RjtBQUN2RixpRkFBNEY7QUFDNUYsbURBQXNEO0FBQ3RELHdEQUFnQztBQUVuQixRQUFBLG1CQUFtQixHQUFHLGNBQWMsQ0FBQztBQUVsRCxNQUFNLFdBQVcsR0FBRyxVQUFVLENBQUM7QUFFbEIsUUFBQSxLQUFLLEdBQUc7Ozs7Ozs7Ozs7b0NBVWUsMkJBQW1COzs7O29EQUlILENBQUM7QUFFeEMsUUFBQSxJQUFJLEdBQUcsb0NBQW9DLENBQUM7QUFFekQsTUFBTSxVQUFVLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztBQUM3QyxNQUFNLGFBQWEsR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxXQUFXLENBQUMsQ0FBQyxDQUFDO0FBT25ELE1BQU0sV0FBVyxHQUFHO0lBQ2xCLG1CQUFtQjtJQUNuQixtQkFBbUI7SUFDbkIsb0JBQW9CO0lBQ3BCLFdBQVc7Q0FDSCxDQUFDO0FBRVgsTUFBTSxpQkFBaUIsR0FBRyxHQUFHLFdBQVcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLCtCQUErQixDQUFDO0FBRW5GLFNBQVMsY0FBYyxDQUFDLEdBQXNCO0lBQzVDLE9BQU8sV0FBVyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxD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
181
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLWRpc3BhdGNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NsaS1kaXNwYXRjaC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUE0RUEsc0NBNENDO0FBZ0RELDhCQWdEQztBQXJORCwyQkFBOEM7QUFDOUMsZ0RBQXdCO0FBRXhCLCtFQUE0RDtBQUM1RCxvRUFBMkQ7QUFDM0QsMkNBQXNFO0FBQ3RFLDBEQUFnQztBQUNoQyxvREFBa0U7QUFDbEUscUNBQStDO0FBQy9DLGlFQUF1RjtBQUN2RixpRkFBNEY7QUFDNUYsbURBQXNEO0FBQ3RELHdEQUFnQztBQUVuQixRQUFBLG1CQUFtQixHQUFHLGNBQWMsQ0FBQztBQUVsRCxNQUFNLFdBQVcsR0FBRyxVQUFVLENBQUM7QUFFbEIsUUFBQSxLQUFLLEdBQUc7Ozs7Ozs7Ozs7b0NBVWUsMkJBQW1COzs7O29EQUlILENBQUM7QUFFeEMsUUFBQSxJQUFJLEdBQUcsb0NBQW9DLENBQUM7QUFFekQsTUFBTSxVQUFVLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztBQUM3QyxNQUFNLGFBQWEsR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxXQUFXLENBQUMsQ0FBQyxDQUFDO0FBT25ELE1BQU0sV0FBVyxHQUFHLENBQUMsbUJBQW1CLEVBQUUsb0JBQW9CLEVBQUUsV0FBVyxDQUFVLENBQUM7QUFFdEYsTUFBTSxpQkFBaUIsR0FBRyxHQUFHLFdBQVcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLCtCQUErQixDQUFDO0FBRW5GLFNBQVMsY0FBYyxDQUFDLEdBQXNCO0lBQzVDLE9BQU8sV0FBVyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUksRUFBRSxL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
@@ -1,5 +1,6 @@
1
1
  export declare const REQUIRED_KEYS: readonly ["FOREST_AUTH_SECRET", "FOREST_ENV_SECRET", "FOREST_SERVER_URL", "FOREST_APP_URL", "AGENT_URL"];
2
2
  export type RequiredKey = (typeof REQUIRED_KEYS)[number];
3
+ export declare const DEFAULTS: Partial<Record<RequiredKey, string>>;
3
4
  export type PresenceMap = Record<RequiredKey, boolean>;
4
5
  export interface BFFConfig {
5
6
  forestAuthSecret?: string;
@@ -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.MIN_RATE_LIMIT_WINDOW_MS = exports.MAX_RATE_LIMIT_REQUESTS = exports.DEFAULT_RATE_LIMIT_WINDOW_MS = exports.DEFAULT_RATE_LIMIT_MAX_REQUESTS = exports.MAX_TIMEOUT_MS = exports.DEFAULT_AI_TIMEOUT_MS = exports.DEFAULT_AGENT_TIMEOUT_MS = exports.REQUIRED_KEYS = void 0;
6
+ exports.MIN_RATE_LIMIT_WINDOW_MS = exports.MAX_RATE_LIMIT_REQUESTS = exports.DEFAULT_RATE_LIMIT_WINDOW_MS = exports.DEFAULT_RATE_LIMIT_MAX_REQUESTS = exports.MAX_TIMEOUT_MS = exports.DEFAULT_AI_TIMEOUT_MS = exports.DEFAULT_AGENT_TIMEOUT_MS = exports.DEFAULTS = exports.REQUIRED_KEYS = void 0;
7
7
  exports.parsePublicUrl = parsePublicUrl;
8
8
  exports.parseConfig = parseConfig;
9
9
  const zod_1 = require("zod");
@@ -18,6 +18,10 @@ exports.REQUIRED_KEYS = [
18
18
  'FOREST_APP_URL',
19
19
  'AGENT_URL',
20
20
  ];
21
+ exports.DEFAULTS = {
22
+ FOREST_SERVER_URL: 'https://api.forestadmin.com',
23
+ FOREST_APP_URL: 'https://app.forestadmin.com',
24
+ };
21
25
  const URL_KEYS = ['FOREST_SERVER_URL', 'FOREST_APP_URL', 'AGENT_URL'];
22
26
  const DECIMAL_INTEGER = /^\d+$/;
23
27
  exports.DEFAULT_AGENT_TIMEOUT_MS = 10000;
@@ -99,7 +103,7 @@ function parseOpenApiEnabled(raw) {
99
103
  throw new errors_1.ConfigurationError('Invalid configuration: BFF_OPENAPI_ENABLED must be a boolean (true/false).');
100
104
  }
101
105
  function parseConfig(env) {
102
- const normalized = Object.fromEntries(exports.REQUIRED_KEYS.map(key => [key, normalize(env[key])]));
106
+ const normalized = Object.fromEntries(exports.REQUIRED_KEYS.map(key => [key, normalize(env[key]) ?? exports.DEFAULTS[key]]));
103
107
  for (const key of URL_KEYS) {
104
108
  const value = normalized[key];
105
109
  if (value !== undefined && !isHttpUrl(value)) {
@@ -143,4 +147,4 @@ function parseConfig(env) {
143
147
  hasAllRequired: exports.REQUIRED_KEYS.every(key => presence[key]),
144
148
  };
145
149
  }
146
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW52LWNvbmZpZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb25maWcvZW52LWNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUEwRkEsd0NBMEJDO0FBNENELGtDQW1FQztBQW5PRCw2QkFBd0I7QUFFeEIsMkNBQXFEO0FBQ3JELDJEQUEyQztBQUMzQyxzQ0FBK0M7QUFDL0MsbURBQXVEO0FBRTFDLFFBQUEsYUFBYSxHQUFHO0lBQzNCLG9CQUFvQjtJQUNwQixtQkFBbUI7SUFDbkIsbUJBQW1CO0lBQ25CLGdCQUFnQjtJQUNoQixXQUFXO0NBQ0gsQ0FBQztBQUlYLE1BQU0sUUFBUSxHQUFHLENBQUMsbUJBQW1CLEVBQUUsZ0JBQWdCLEVBQUUsV0FBVyxDQUFVLENBQUM7QUF5Qi9FLE1BQU0sZUFBZSxHQUFHLE9BQU8sQ0FBQztBQUNuQixRQUFBLHdCQUF3QixHQUFHLEtBQU0sQ0FBQztBQUNsQyxRQUFBLHFCQUFxQixHQUFHLE1BQU8sQ0FBQztBQUNoQyxRQUFBLGNBQWMsR0FBRyxVQUFhLENBQUM7QUFDL0IsUUFBQSwrQkFBK0IsR0FBRyxHQUFHLENBQUM7QUFDdEMsUUFBQSw0QkFBNEIsR0FBRyxLQUFNLENBQUM7QUFDdEMsUUFBQSx1QkFBdUIsR0FBRyxLQUFNLENBQUM7QUFDakMsUUFBQSx3QkFBd0IsR0FBRyxJQUFLLENBQUM7QUFDOUMsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDO0FBQ3ZCLE1BQU0sb0JBQW9CLEdBQUcsRUFBRSxDQUFDO0FBQ2hDLE1BQU0sY0FBYyxHQUFHLHdCQUF3QixDQUFDO0FBQ2hELE1BQU0sZUFBZSxHQUFHLE9BQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQztBQUV4RCxTQUFTLFNBQVMsQ0FBQyxLQUF5QjtJQUMxQyxPQUFPLEtBQUssS0FBSyxTQUFTLElBQUksS0FBSyxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7QUFDeEUsQ0FBQztBQUVELFNBQVMsbUJBQW1CLENBQzFCLEdBQXVCLEVBQ3ZCLE9BQWUsRUFDZixFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFrRDtJQUV0RSxNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDN0IsSUFBSSxLQUFLLEtBQUssU0FBUztRQUFFLE9BQU8sUUFBUSxDQUFDO0lBRXpDLE1BQU0sTUFBTSxHQUFHLGVBQWUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDO0lBRS9FLElBQUksTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxNQUFNLEdBQUcsR0FBRyxJQUFJLE1BQU0sR0FBRyxHQUFHLEVBQUUsQ0FBQztRQUN6RCxNQUFNLElBQUksMkJBQWtCLENBQzFCLDBCQUEwQixPQUFPLCtCQUErQixHQUFHLFFBQVEsR0FBRyxHQUFHLENBQ2xGLENBQUM7SUFDSixDQUFDO0lBRUQsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQztBQUVELFNBQVMsU0FBUyxDQUFDLEdBQVk7SUFDN0IsT0FBTyxtQkFBbUIsQ0FBQyxHQUFHLEVBQUUsV0FBVyxFQUFFO1FBQzNDLEdBQUcsRUFBRSxDQUFDO1FBQ04sR0FBRyxFQUFFLFFBQVE7UUFDYixRQUFRLEVBQUUsa0JBQWdCO0tBQzNCLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCxTQUFTLFNBQVMsQ0FBQyxLQUFhO0lBQzlCLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLGVBQWUsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsT0FBTyxDQUFDO0FBQ3ZFLENBQUM7QUFFRCxTQUFnQixjQUFjLENBQUMsR0FBWTtJQUN6QyxNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUM7SUFFN0IsSUFBSSxLQUFLLEtBQUssU0FBUztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBRTFDLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN0QixNQUFNLElBQUksMkJBQWtCLENBQzFCLG9FQUFvRSxDQUNyRSxDQUFDO0lBQ0osQ0FBQztJQUVELElBQUksS0FBSyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxLQUFLLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDL0MsTUFBTSxJQUFJLDJCQUFrQixDQUMxQixrRkFBa0YsQ0FDbkYsQ0FBQztJQUNKLENBQUM7SUFFRCxNQUFNLEdBQUcsR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUUzQixJQUFJLEdBQUcsQ0FBQyxRQUFRLEtBQUssRUFBRSxJQUFJLEdBQUcsQ0FBQyxRQUFRLEtBQUssRUFBRSxFQUFFLENBQUM7UUFDL0MsTUFBTSxJQUFJLDJCQUFrQixDQUMxQixtRUFBbUUsQ0FDcEUsQ0FBQztJQUNKLENBQUM7SUFFRCxPQUFPLEdBQUcsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsQ0FBQztBQUN0QyxDQUFDO0FBRUQsU0FBUyxvQkFBb0IsQ0FBQyxLQUFhO0lBQ3pDLE9BQU8sY0FBYyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxRQUFRLENBQUMsQ0FBQyxNQUFNLEtBQUssb0JBQW9CLENBQUM7QUFDcEcsQ0FBQztBQUVELFNBQVMsa0JBQWtCLENBQUMsR0FBWTtJQUN0QyxNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUM7SUFFN0IsSUFBSSxLQUFLLEtBQUssU0FBUyxJQUFJLENBQUMsb0JBQW9CLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN4RCxNQUFNLElBQUksMkJBQWtCLENBQzFCLHNGQUFzRixvQkFBb0IsbUJBQW1CLENBQzlILENBQUM7SUFDSixDQUFDO0lBRUQsT0FBTyxLQUFLLENBQUM7QUFDZixDQUFDO0FBRUQsU0FBUyxjQUFjLENBQUMsR0FBdUIsRUFBRSxPQUFlLEVBQUUsU0FBaUI7SUFDakYsT0FBTyxtQkFBbUIsQ0FBQyxHQUFHLEVBQUUsT0FBTyxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxHQUFHLEVBQUUsc0JBQWMsRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQztBQUNqRyxDQUFDO0FBRUQsU0FBUyxvQkFBb0IsQ0FBQyxHQUFZO0lBQ3hDLE1BQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUU3QixJQUFJLEtBQUssS0FBSyxTQUFTLElBQUksQ0FBQyxJQUFBLDBCQUFlLEVBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUNuRCxNQUFNLElBQUksMkJBQWtCLENBQzFCLDRFQUE0RSxDQUM3RSxDQUFDO0lBQ0osQ0FBQztJQUVELE9BQU8sS0FBSyxDQUFDO0FBQ2YsQ0FBQztBQUVELFNBQVMsbUJBQW1CLENBQUMsR0FBWTtJQUN2QyxNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsR0FBRyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDbkQsSUFBSSxLQUFLLEtBQUssU0FBUyxJQUFJLEtBQUssS0FBSyxNQUFNO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDekQsSUFBSSxLQUFLLEtBQUssT0FBTztRQUFFLE9BQU8sS0FBSyxDQUFDO0lBRXBDLE1BQU0sSUFBSSwyQkFBa0IsQ0FDMUIsNEVBQTRFLENBQzdFLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBZ0IsV0FBVyxDQUFDLEdBQXNCO0lBQ2hELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQ25DLHFCQUFhLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxHQUFHLEVBQUUsU0FBUyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FDVixDQUFDO0lBRTdDLEtBQUssTUFBTSxHQUFHLElBQUksUUFBUSxFQUFFLENBQUM7UUFDM0IsTUFBTSxLQUFLLEdBQUcsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBRTlCLElBQUksS0FBSyxLQUFLLFNBQVMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzdDLE1BQU0sSUFBSSwyQkFBa0IsQ0FBQywwQkFBMEIsR0FBRywrQkFBK0IsQ0FBQyxDQUFDO1FBQzdGLENBQUM7SUFDSCxDQUFDO0lBRUQsTUFBTSxRQUFRLEdBQUcsTUFBTSxDQUFDLFdBQVcsQ0FDakMscUJBQWEsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsR0FBRyxDQUFDLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FDaEQsQ0FBQztJQUVqQixNQUFNLGtCQUFrQixHQUFHLGtCQUFrQixDQUFDLEdBQUcsQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO0lBQzVFLE1BQU0sRUFBRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxHQUFHLElBQUEsNEJBQW1CLEVBQ3JGLEdBQUcsQ0FBQyxtQkFBbUIsQ0FDeEIsQ0FBQztJQUNGLE1BQU0sZUFBZSxHQUFHLG9CQUFvQixDQUFDLEdBQUcsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO0lBRXZFLE9BQU87UUFDTCxnQkFBZ0IsRUFBRSxVQUFVLENBQUMsa0JBQWtCO1FBQy9DLGVBQWUsRUFBRSxVQUFVLENBQUMsaUJBQWlCO1FBQzdDLGVBQWUsRUFBRSxVQUFVLENBQUMsaUJBQWlCO1FBQzdDLFlBQVksRUFBRSxVQUFVLENBQUMsY0FBYztRQUN2QyxRQUFRLEVBQUUsVUFBVSxDQUFDLFNBQVM7UUFDOUIsU0FBUyxFQUFFLGNBQWMsQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDO1FBQzdDLGtCQUFrQjtRQUNsQixjQUFjO1FBQ2QscUJBQXFCO1FBQ3JCLGVBQWU7UUFDZixjQUFjLEVBQUUsY0FBYyxDQUM1QixHQUFHLENBQUMsb0JBQW9CLEVBQ3hCLHNCQUFzQixFQUN0QixnQ0FBd0IsQ0FDekI7UUFDRCxXQUFXLEVBQUUsY0FBYyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsRUFBRSxtQkFBbUIsRUFBRSw2QkFBcUIsQ0FBQztRQUM5RixjQUFjLEVBQUUsbUJBQW1CLENBQUMsR0FBRyxDQUFDLG1CQUFtQixDQUFDO1FBQzVELG9CQUFvQixFQUFFLG1CQUFtQixDQUN2QyxHQUFHLENBQUMsMkJBQTJCLEVBQy9CLDZCQUE2QixFQUM3QjtZQUNFLEdBQUcsRUFBRSxDQUFDO1lBQ04sR0FBRyxFQUFFLCtCQUF1QjtZQUM1QixRQUFRLEVBQUUsdUNBQStCO1NBQzFDLENBQ0Y7UUFDRCxpQkFBaUIsRUFBRSxtQkFBbUIsQ0FDcEMsR0FBRyxDQUFDLHdCQUF3QixFQUM1QiwwQkFBMEIsRUFDMUI7WUFDRSxHQUFHLEVBQUUsZ0NBQXdCO1lBQzdCLEdBQUcsRUFBRSxzQkFBYztZQUNuQixRQUFRLEVBQUUsb0NBQTRCO1NBQ3ZDLENBQ0Y7UUFDRCxRQUFRLEVBQUUsU0FBUyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUM7UUFDbEMsUUFBUTtRQUNSLHNGQUFzRjtRQUN0RiwwRkFBMEY7UUFDMUYsOEZBQThGO1FBQzlGLHFFQUFxRTtRQUNyRSxjQUFjLEVBQUUscUJBQWEsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUM7S0FDMUQsQ0FBQztBQUNKLENBQUMifQ==
150
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW52LWNvbmZpZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb25maWcvZW52LWNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUErRkEsd0NBMEJDO0FBNENELGtDQW1FQztBQXhPRCw2QkFBd0I7QUFFeEIsMkNBQXFEO0FBQ3JELDJEQUEyQztBQUMzQyxzQ0FBK0M7QUFDL0MsbURBQXVEO0FBRTFDLFFBQUEsYUFBYSxHQUFHO0lBQzNCLG9CQUFvQjtJQUNwQixtQkFBbUI7SUFDbkIsbUJBQW1CO0lBQ25CLGdCQUFnQjtJQUNoQixXQUFXO0NBQ0gsQ0FBQztBQUlFLFFBQUEsUUFBUSxHQUF5QztJQUM1RCxpQkFBaUIsRUFBRSw2QkFBNkI7SUFDaEQsY0FBYyxFQUFFLDZCQUE2QjtDQUM5QyxDQUFDO0FBRUYsTUFBTSxRQUFRLEdBQUcsQ0FBQyxtQkFBbUIsRUFBRSxnQkFBZ0IsRUFBRSxXQUFXLENBQVUsQ0FBQztBQXlCL0UsTUFBTSxlQUFlLEdBQUcsT0FBTyxDQUFDO0FBQ25CLFFBQUEsd0JBQXdCLEdBQUcsS0FBTSxDQUFDO0FBQ2xDLFFBQUEscUJBQXFCLEdBQUcsTUFBTyxDQUFDO0FBQ2hDLFFBQUEsY0FBYyxHQUFHLFVBQWEsQ0FBQztBQUMvQixRQUFBLCtCQUErQixHQUFHLEdBQUcsQ0FBQztBQUN0QyxRQUFBLDRCQUE0QixHQUFHLEtBQU0sQ0FBQztBQUN0QyxRQUFBLHVCQUF1QixHQUFHLEtBQU0sQ0FBQztBQUNqQyxRQUFBLHdCQUF3QixHQUFHLElBQUssQ0FBQztBQUM5QyxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUM7QUFDdkIsTUFBTSxvQkFBb0IsR0FBRyxFQUFFLENBQUM7QUFDaEMsTUFBTSxjQUFjLEdBQUcsd0JBQXdCLENBQUM7QUFDaEQsTUFBTSxlQUFlLEdBQUcsT0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLFFBQVEsRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUFDO0FBRXhELFNBQVMs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
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The one piece of state the preload and the CLI have to share.
3
+ *
4
+ * `--require` runs the preload in its own module, before `cli.js` is even loaded, so the SDK it
5
+ * builds has nowhere to go but a module both sides resolve to the same instance. Keeping it here —
6
+ * rather than having the preload arm a signal handler of its own — is what lets the shutdown path
7
+ * WAIT for the flush instead of racing it, and keeps the preload from consuming a signal the CLI
8
+ * has not armed a handler for yet.
9
+ *
10
+ * Nothing OpenTelemetry-specific is imported here, so the npm bin pays only for an empty variable.
11
+ */
12
+ export interface TracingHandle {
13
+ shutdown(): Promise<void>;
14
+ }
15
+ export declare function setTracingHandle(sdk: TracingHandle | undefined): void;
16
+ export declare function getTracingHandle(): TracingHandle | undefined;
17
+ /**
18
+ * Flushes buffered spans, or resolves immediately when tracing was never armed — the shutdown path
19
+ * should not have to know which. A failing export is swallowed: a dead collector must not turn a
20
+ * clean shutdown into a failed one.
21
+ *
22
+ * No deadline here on purpose: `armShutdown` bounds whatever flush it is given, and it is the only
23
+ * caller. Bounding it twice would say the deadline lives in two places when it does not.
24
+ */
25
+ export declare function flushTracing(): Promise<void>;
26
+ //# sourceMappingURL=tracing-handle.d.ts.map
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ /**
3
+ * The one piece of state the preload and the CLI have to share.
4
+ *
5
+ * `--require` runs the preload in its own module, before `cli.js` is even loaded, so the SDK it
6
+ * builds has nowhere to go but a module both sides resolve to the same instance. Keeping it here —
7
+ * rather than having the preload arm a signal handler of its own — is what lets the shutdown path
8
+ * WAIT for the flush instead of racing it, and keeps the preload from consuming a signal the CLI
9
+ * has not armed a handler for yet.
10
+ *
11
+ * Nothing OpenTelemetry-specific is imported here, so the npm bin pays only for an empty variable.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.setTracingHandle = setTracingHandle;
15
+ exports.getTracingHandle = getTracingHandle;
16
+ exports.flushTracing = flushTracing;
17
+ let handle;
18
+ function setTracingHandle(sdk) {
19
+ handle = sdk;
20
+ }
21
+ function getTracingHandle() {
22
+ return handle;
23
+ }
24
+ /**
25
+ * Flushes buffered spans, or resolves immediately when tracing was never armed — the shutdown path
26
+ * should not have to know which. A failing export is swallowed: a dead collector must not turn a
27
+ * clean shutdown into a failed one.
28
+ *
29
+ * No deadline here on purpose: `armShutdown` bounds whatever flush it is given, and it is the only
30
+ * caller. Bounding it twice would say the deadline lives in two places when it does not.
31
+ */
32
+ async function flushTracing() {
33
+ try {
34
+ await handle?.shutdown();
35
+ }
36
+ catch {
37
+ /* istanbul ignore next — nothing to do about it, and it must not change the exit code. */
38
+ }
39
+ }
40
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhY2luZy1oYW5kbGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdHJhY2luZy1oYW5kbGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7O0dBVUc7O0FBUUgsNENBRUM7QUFFRCw0Q0FFQztBQVVELG9DQU1DO0FBeEJELElBQUksTUFBaUMsQ0FBQztBQUV0QyxTQUFnQixnQkFBZ0IsQ0FBQyxHQUE4QjtJQUM3RCxNQUFNLEdBQUcsR0FBRyxDQUFDO0FBQ2YsQ0FBQztBQUVELFNBQWdCLGdCQUFnQjtJQUM5QixPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNJLEtBQUssVUFBVSxZQUFZO0lBQ2hDLElBQUksQ0FBQztRQUNILE1BQU0sTUFBTSxFQUFFLFFBQVEsRUFBRSxDQUFDO0lBQzNCLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCwwRkFBMEY7SUFDNUYsQ0FBQztBQUNILENBQUMifQ==
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tracing-preload.d.ts.map
@@ -0,0 +1,27 @@
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
+ // The `--require` entry point of the Docker image (see the Dockerfile's ENTRYPOINT). Separate from
7
+ // `tracing.ts` on purpose: importing the setup must never arm an SDK, only calling it should.
8
+ //
9
+ // The SDK is handed to `tracing-handle` rather than wired to a signal here. The CLI arms the only
10
+ // termination handler, and it flushes through that handle — so a signal arriving before the CLI is
11
+ // up cannot be swallowed by a listener that does not terminate anything.
12
+ const console_logger_1 = __importDefault(require("./adapters/console-logger"));
13
+ const tracing_1 = __importDefault(require("./tracing"));
14
+ const tracing_handle_1 = require("./tracing-handle");
15
+ // Nothing here may throw. A `--require` runs before the entry point, so an exception aborts the
16
+ // process before the BFF exists at all — a dead container in exchange for optional, best-effort
17
+ // telemetry. `initTracing` degrades on its own for the cases it knows about; this catches the rest,
18
+ // including whatever a future SDK version decides to throw from `start()`.
19
+ try {
20
+ (0, tracing_handle_1.setTracingHandle)((0, tracing_1.default)());
21
+ }
22
+ catch (error) {
23
+ (0, console_logger_1.default)()('Warn', 'OpenTelemetry failed to initialise, starting untraced', {
24
+ reason: error instanceof Error ? error.message : String(error),
25
+ });
26
+ }
27
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhY2luZy1wcmVsb2FkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3RyYWNpbmctcHJlbG9hZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLG1HQUFtRztBQUNuRyw4RkFBOEY7QUFDOUYsRUFBRTtBQUNGLGtHQUFrRztBQUNsRyxtR0FBbUc7QUFDbkcseUVBQXlFO0FBQ3pFLCtFQUE0RDtBQUM1RCx3REFBb0M7QUFDcEMscURBQW9EO0FBRXBELGdHQUFnRztBQUNoRyxnR0FBZ0c7QUFDaEcsb0dBQW9HO0FBQ3BHLDJFQUEyRTtBQUMzRSxJQUFJLENBQUM7SUFDSCxJQUFBLGlDQUFnQixFQUFDLElBQUEsaUJBQVcsR0FBRSxDQUFDLENBQUM7QUFDbEMsQ0FBQztBQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7SUFDZixJQUFBLHdCQUFtQixHQUFFLENBQUMsTUFBTSxFQUFFLHVEQUF1RCxFQUFFO1FBQ3JGLE1BQU0sRUFBRSxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDO0tBQy9ELENBQUMsQ0FBQztBQUNMLENBQUMifQ==
@@ -0,0 +1,79 @@
1
+ import type { Logger } from './ports/logger-port';
2
+ /**
3
+ * The OpenTelemetry setup. `tracing-preload.ts` is what actually runs it, via `--require` before
4
+ * cli.js so the auto-instrumentation patches land before anything the app imports; keeping the two
5
+ * apart is what lets this module be imported by a test without arming an SDK.
6
+ *
7
+ * The SDK is initialised only when `OTEL_EXPORTER_OTLP_ENDPOINT` is set, so an install that never
8
+ * opted into APM pays nothing. Neither ending the process nor flushing on the way out is this
9
+ * module's business: it hands the SDK back, the preload parks it in `tracing-handle`, and the
10
+ * shutdown path flushes through that. Arming a signal handler here would consume a signal the CLI
11
+ * has not armed its own handler for yet, and nothing would then terminate the process.
12
+ * All configuration goes through the standard OTel environment variables:
13
+ *
14
+ * OTEL_EXPORTER_OTLP_ENDPOINT OTLP receiver (e.g. http://localhost:4318)
15
+ * OTEL_SERVICE_NAME default: forestadmin-agent-bff
16
+ * OTEL_SDK_DISABLED set to "true" to force-disable
17
+ * OTEL_RESOURCE_ATTRIBUTES e.g. deployment.environment=production
18
+ * OTEL_METRICS_EXPORTER default: none (the SDK's own default is otlp)
19
+ * OTEL_LOGS_EXPORTER default: none (the SDK's own default is otlp)
20
+ *
21
+ * The OTel packages are installed only into the Docker image's isolated deps (see
22
+ * `packages/agent-bff/docker/`), never shipped to npm consumers of the CLI — hence the dynamic
23
+ * require behind a seam, and the warning rather than a crash when they are absent.
24
+ */
25
+ export declare const DEFAULT_SERVICE_NAME = "forestadmin-agent-bff";
26
+ export interface OtelSdk {
27
+ start(): void;
28
+ shutdown(): Promise<void>;
29
+ }
30
+ export interface OtelModules {
31
+ NodeSDK: new (options: Record<string, unknown>) => OtelSdk;
32
+ getNodeAutoInstrumentations: () => unknown;
33
+ }
34
+ export interface TracingOptions {
35
+ /**
36
+ * The environment OUR decisions read: whether to arm at all, and what to name the service when
37
+ * nothing else does. It stops there — the SDK reads the real `process.env` for everything about
38
+ * the export itself, so a test injecting `env` cannot assert where spans go. Identical objects in
39
+ * production, so this is a limit on what the seam can test, not a behaviour.
40
+ */
41
+ env?: NodeJS.ProcessEnv;
42
+ logger?: Logger;
43
+ /** The dynamic require, as a seam: the packages exist only in the Docker image. */
44
+ load?: () => OtelModules | undefined;
45
+ }
46
+ /** The packages this image installs for APM, and the export it takes from each. */
47
+ export declare const OTEL_MODULE_IDS: {
48
+ readonly sdk: "@opentelemetry/sdk-node";
49
+ readonly instrumentations: "@opentelemetry/auto-instrumentations-node";
50
+ };
51
+ /**
52
+ * The module resolution, as a seam. These packages are absent everywhere except the Docker image,
53
+ * so the real `require` can only ever take the failure branch here — passing a loader is what lets
54
+ * the success branch, and the package ids it asks for, be exercised at all. A typo in one of them
55
+ * would otherwise surface as an untraced image and nothing else.
56
+ */
57
+ export type ModuleLoader = (id: string) => Record<string, unknown>;
58
+ export declare function loadOtelModules(load?: ModuleLoader): OtelModules | undefined;
59
+ /**
60
+ * The service name the environment already carries, by the spec's precedence: `OTEL_SERVICE_NAME`
61
+ * wins, then a `service.name` entry in the comma-separated `OTEL_RESOURCE_ATTRIBUTES` list.
62
+ * Undefined when neither names one, which is the only case where our own default should apply.
63
+ */
64
+ export declare function serviceNameFromEnv(env: NodeJS.ProcessEnv): string | undefined;
65
+ /**
66
+ * Strips the credentials out of the endpoint before it reaches the logs. `https://user:token@host`
67
+ * is a legitimate way to reach a collector behind basic auth, and container logs are the last place
68
+ * that token should end up — this package does not echo a secret anywhere else either.
69
+ *
70
+ * The query string goes with them, unread: collectors that take their key as `?api_key=` exist, and
71
+ * telling them apart from a harmless parameter means knowing every vendor's spelling. Nothing about
72
+ * the destination is lost — the host and path are what the line is for.
73
+ *
74
+ * An endpoint that does not parse is dropped from the log entirely rather than passed through: it
75
+ * cannot be redacted, so it cannot be shown. The SDK will fail on it soon enough on its own.
76
+ */
77
+ export declare function redactEndpoint(endpoint: string): string | undefined;
78
+ export default function initTracing(options?: TracingOptions): OtelSdk | undefined;
79
+ //# sourceMappingURL=tracing.d.ts.map
@@ -0,0 +1,168 @@
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.OTEL_MODULE_IDS = exports.DEFAULT_SERVICE_NAME = void 0;
7
+ exports.loadOtelModules = loadOtelModules;
8
+ exports.serviceNameFromEnv = serviceNameFromEnv;
9
+ exports.redactEndpoint = redactEndpoint;
10
+ exports.default = initTracing;
11
+ const console_logger_1 = __importDefault(require("./adapters/console-logger"));
12
+ /**
13
+ * The OpenTelemetry setup. `tracing-preload.ts` is what actually runs it, via `--require` before
14
+ * cli.js so the auto-instrumentation patches land before anything the app imports; keeping the two
15
+ * apart is what lets this module be imported by a test without arming an SDK.
16
+ *
17
+ * The SDK is initialised only when `OTEL_EXPORTER_OTLP_ENDPOINT` is set, so an install that never
18
+ * opted into APM pays nothing. Neither ending the process nor flushing on the way out is this
19
+ * module's business: it hands the SDK back, the preload parks it in `tracing-handle`, and the
20
+ * shutdown path flushes through that. Arming a signal handler here would consume a signal the CLI
21
+ * has not armed its own handler for yet, and nothing would then terminate the process.
22
+ * All configuration goes through the standard OTel environment variables:
23
+ *
24
+ * OTEL_EXPORTER_OTLP_ENDPOINT OTLP receiver (e.g. http://localhost:4318)
25
+ * OTEL_SERVICE_NAME default: forestadmin-agent-bff
26
+ * OTEL_SDK_DISABLED set to "true" to force-disable
27
+ * OTEL_RESOURCE_ATTRIBUTES e.g. deployment.environment=production
28
+ * OTEL_METRICS_EXPORTER default: none (the SDK's own default is otlp)
29
+ * OTEL_LOGS_EXPORTER default: none (the SDK's own default is otlp)
30
+ *
31
+ * The OTel packages are installed only into the Docker image's isolated deps (see
32
+ * `packages/agent-bff/docker/`), never shipped to npm consumers of the CLI — hence the dynamic
33
+ * require behind a seam, and the warning rather than a crash when they are absent.
34
+ */
35
+ exports.DEFAULT_SERVICE_NAME = 'forestadmin-agent-bff';
36
+ /** The packages this image installs for APM, and the export it takes from each. */
37
+ exports.OTEL_MODULE_IDS = {
38
+ sdk: '@opentelemetry/sdk-node',
39
+ instrumentations: '@opentelemetry/auto-instrumentations-node',
40
+ };
41
+ function loadOtelModules(load = require) {
42
+ try {
43
+ const modules = {
44
+ NodeSDK: load(exports.OTEL_MODULE_IDS.sdk).NodeSDK,
45
+ getNodeAutoInstrumentations: load(exports.OTEL_MODULE_IDS.instrumentations)
46
+ .getNodeAutoInstrumentations,
47
+ };
48
+ // Resolving is not the same as finding what we came for. A package that loads but no longer
49
+ // exports the name we read — a rename on a version bump — would otherwise hand back a truthy
50
+ // object of undefined members, skip the "packages not available" branch, and throw a
51
+ // TypeError inside a `--require`, before the entry point runs at all. That is a dead container
52
+ // for an APM layer that is supposed to degrade to a warning.
53
+ if (Object.values(modules).some(exported => typeof exported !== 'function'))
54
+ return undefined;
55
+ return modules;
56
+ }
57
+ catch {
58
+ return undefined;
59
+ }
60
+ }
61
+ /**
62
+ * The OTel specification defines its boolean environment variables as case-insensitive, and this
63
+ * one is the kill switch: reading `TRUE` as "not disabled" would leave tracing running for someone
64
+ * who just asked for it to stop.
65
+ */
66
+ function isDisabled(raw) {
67
+ return raw?.trim().toLowerCase() === 'true';
68
+ }
69
+ /**
70
+ * The service name the environment already carries, by the spec's precedence: `OTEL_SERVICE_NAME`
71
+ * wins, then a `service.name` entry in the comma-separated `OTEL_RESOURCE_ATTRIBUTES` list.
72
+ * Undefined when neither names one, which is the only case where our own default should apply.
73
+ */
74
+ function serviceNameFromEnv(env) {
75
+ const explicit = env.OTEL_SERVICE_NAME?.trim();
76
+ if (explicit)
77
+ return explicit;
78
+ return env.OTEL_RESOURCE_ATTRIBUTES?.split(',')
79
+ .map(entry => entry.split('='))
80
+ .filter(([key, value]) => key?.trim() === 'service.name' && value?.trim())
81
+ .map(([, value]) => value.trim())
82
+ .pop();
83
+ }
84
+ /**
85
+ * Strips the credentials out of the endpoint before it reaches the logs. `https://user:token@host`
86
+ * is a legitimate way to reach a collector behind basic auth, and container logs are the last place
87
+ * that token should end up — this package does not echo a secret anywhere else either.
88
+ *
89
+ * The query string goes with them, unread: collectors that take their key as `?api_key=` exist, and
90
+ * telling them apart from a harmless parameter means knowing every vendor's spelling. Nothing about
91
+ * the destination is lost — the host and path are what the line is for.
92
+ *
93
+ * An endpoint that does not parse is dropped from the log entirely rather than passed through: it
94
+ * cannot be redacted, so it cannot be shown. The SDK will fail on it soon enough on its own.
95
+ */
96
+ function redactEndpoint(endpoint) {
97
+ try {
98
+ const url = new URL(endpoint);
99
+ if (!url.username && !url.password && !url.search)
100
+ return endpoint;
101
+ url.username = '';
102
+ url.password = '';
103
+ url.search = '';
104
+ return url.toString();
105
+ }
106
+ catch {
107
+ return undefined;
108
+ }
109
+ }
110
+ function initTracing(options = {}) {
111
+ const { env = process.env, logger = (0, console_logger_1.default)(), load = loadOtelModules } = options;
112
+ // Any of the three standard ways to say "export traces somewhere" turns tracing on. The per-signal
113
+ // endpoint counts as much as the generic one, and OTEL_TRACES_EXPORTER counts on its own —
114
+ // requiring an OTLP collector before `console` does anything would be nonsense for an exporter
115
+ // that writes to stdout, and it is the obvious first thing to reach for when debugging.
116
+ //
117
+ // A blank value counts as unset throughout, not as "export to the OTel default": `env_file` hands
118
+ // an empty string through for a variable left blank in a `.env`, and arming the SDK against
119
+ // localhost:4318 is not what leaving the line empty asks for.
120
+ const endpoint = env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT?.trim() || env.OTEL_EXPORTER_OTLP_ENDPOINT?.trim();
121
+ const requestedExporter = env.OTEL_TRACES_EXPORTER?.trim().toLowerCase();
122
+ if ((!endpoint && !requestedExporter) || isDisabled(env.OTEL_SDK_DISABLED))
123
+ return undefined;
124
+ const modules = load();
125
+ if (!modules) {
126
+ logger('Warn', 'OpenTelemetry packages not available, skipping APM initialisation');
127
+ return undefined;
128
+ }
129
+ const { NodeSDK, getNodeAutoInstrumentations } = modules;
130
+ // Passing `serviceName` unconditionally would override OTEL_RESOURCE_ATTRIBUTES=service.name=...,
131
+ // which is a documented knob — someone setting only that would silently get our default. So we
132
+ // fill it in only when the environment names no service at all, and otherwise leave the SDK to
133
+ // apply the spec's own precedence (OTEL_SERVICE_NAME first, then the resource attribute).
134
+ const named = serviceNameFromEnv(env);
135
+ // NodeSDK reads OTEL_METRICS_EXPORTER and OTEL_LOGS_EXPORTER itself, and an unset one does not
136
+ // mean "off" — it means otlp. So arming traces alone also starts a metric reader and a log
137
+ // processor aimed at the OTLP default, http://localhost:4318. Under OTEL_TRACES_EXPORTER=console,
138
+ // which asks for no collector at all, that is a POST every minute to a port nobody named; where
139
+ // something else on the host does listen there, it is telemetry that install never opted into.
140
+ // This module arms tracing. The other two signals stay opt-in, through their own variable, which
141
+ // still works: we only fill in what the environment left blank.
142
+ env.OTEL_METRICS_EXPORTER = env.OTEL_METRICS_EXPORTER?.trim() || 'none';
143
+ env.OTEL_LOGS_EXPORTER = env.OTEL_LOGS_EXPORTER?.trim() || 'none';
144
+ // No `traceExporter`, deliberately. Passing one puts NodeSDK on its manual-configuration path,
145
+ // where it stops reading the environment — which is how OTEL_TRACES_EXPORTER, then
146
+ // OTEL_EXPORTER_OTLP_PROTOCOL, then the per-signal endpoint each turned out to be silently
147
+ // ignored, one review round after another. They were three symptoms of doing the SDK's job.
148
+ //
149
+ // sdk-node depends on every OTLP exporter and on the Zipkin one, so all of them are in the image
150
+ // already; leaving the choice to the SDK costs nothing and makes the whole standard surface work,
151
+ // protocol and per-signal overrides included. Our only decisions are whether to arm at all, and
152
+ // the service name when nothing else supplies one.
153
+ const sdk = new NodeSDK({
154
+ ...(named ? {} : { serviceName: exports.DEFAULT_SERVICE_NAME }),
155
+ instrumentations: [getNodeAutoInstrumentations()],
156
+ });
157
+ sdk.start();
158
+ // What was ASKED for, not what the SDK settled on — we no longer choose, so claiming a
159
+ // destination we did not pick would be inventing one. The endpoint is dropped when an exporter
160
+ // was named instead, since it would read as a promise this line cannot keep.
161
+ logger('Info', 'OpenTelemetry tracing enabled', {
162
+ serviceName: named ?? exports.DEFAULT_SERVICE_NAME,
163
+ ...(requestedExporter ? { exporter: requestedExporter } : {}),
164
+ ...(endpoint && !requestedExporter ? { endpoint: redactEndpoint(endpoint) } : {}),
165
+ });
166
+ return sdk;
167
+ }
168
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhY2luZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy90cmFjaW5nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQW1FQSwwQ0FtQkM7QUFnQkQsZ0RBVUM7QUFjRCx3Q0FjQztBQUVELDhCQXFFQztBQWpORCwrRUFBNEQ7QUFFNUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FzQkc7QUFFVSxRQUFBLG9CQUFvQixHQUFHLHVCQUF1QixDQUFDO0FBeUI1RCxtRkFBbUY7QUFDdEUsUUFBQSxlQUFlLEdBQUc7SUFDN0IsR0FBRyxFQUFFLHlCQUF5QjtJQUM5QixnQkFBZ0IsRUFBRSwyQ0FBMkM7Q0FDckQsQ0FBQztBQVVYLFNBQWdCLGVBQWUsQ0FBQyxPQUFxQixPQUFPO0lBQzFELElBQUksQ0FBQztRQUNILE1BQU0sT0FBTyxHQUFHO1lBQ2QsT0FBTyxFQUFFLElBQUksQ0FBQyx1QkFBZSxDQUFDLEdBQUcsQ0FBQyxDQUFDLE9BQU87WUFDMUMsMkJBQTJCLEVBQUUsSUFBSSxDQUFDLHVCQUFlLENBQUMsZ0JBQWdCLENBQUM7aUJBQ2hFLDJCQUEyQjtTQUMvQixDQUFDO1FBRUYsNEZBQTRGO1FBQzVGLDZGQUE2RjtRQUM3RixxRkFBcUY7UUFDckYsK0ZBQStGO1FBQy9GLDZEQUE2RDtRQUM3RCxJQUFJLE1BQU0sQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsT0FBTyxRQUFRLEtBQUssVUFBVSxDQUFDO1lBQUUsT0FBTyxTQUFTLENBQUM7UUFFOUYsT0FBTyxPQUFzQixDQUFDO0lBQ2hDLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0FBQ0gsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxTQUFTLFVBQVUsQ0FBQyxHQUF1QjtJQUN6QyxPQUFPLEdBQUcsRUFBRSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsS0FBSyxNQUFN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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forestadmin/agent-bff",
3
- "version": "1.32.0",
3
+ "version": "1.34.0",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "forest-bff": "dist/cli.js"