@forestadmin/agent-bff 1.21.0 → 1.23.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.
Files changed (35) hide show
  1. package/README.md +40 -3
  2. package/dist/action/action-routes-middleware.js +12 -7
  3. package/dist/action/agent-action-client.d.ts +7 -1
  4. package/dist/action/agent-action-client.js +3 -3
  5. package/dist/ai/ai-proxy-client.d.ts +26 -0
  6. package/dist/ai/ai-proxy-client.js +74 -0
  7. package/dist/ai/ai-proxy-timeout-error.d.ts +5 -0
  8. package/dist/ai/ai-proxy-timeout-error.js +11 -0
  9. package/dist/ai/ai-routes-middleware.d.ts +15 -0
  10. package/dist/ai/ai-routes-middleware.js +96 -0
  11. package/dist/api-key/agent-token.js +4 -7
  12. package/dist/auth/auth-mode.d.ts +2 -0
  13. package/dist/auth/auth-mode.js +9 -1
  14. package/dist/cli-core.d.ts +9 -0
  15. package/dist/cli-core.js +55 -7
  16. package/dist/cli-dispatch.js +15 -3
  17. package/dist/config/env-config.d.ts +3 -1
  18. package/dist/config/env-config.js +10 -8
  19. package/dist/docs/docs-samples.js +24 -16
  20. package/dist/http/bff-local-errors.d.ts +4 -0
  21. package/dist/http/bff-local-errors.js +19 -1
  22. package/dist/http/body-limit.d.ts +1 -0
  23. package/dist/http/body-limit.js +3 -1
  24. package/dist/oauth/oauth-error.d.ts +3 -2
  25. package/dist/oauth/oauth-error.js +5 -4
  26. package/dist/oauth/session-lifecycle.js +2 -2
  27. package/dist/openapi/openapi-document.d.ts +5 -1
  28. package/dist/openapi/openapi-document.js +133 -17
  29. package/dist/openapi/openapi-routes.d.ts +2 -1
  30. package/dist/openapi/openapi-routes.js +9 -4
  31. package/dist/openapi/schemas.d.ts +1 -0
  32. package/dist/openapi/schemas.js +10 -2
  33. package/dist/openapi/unfolded-document.d.ts +5 -1
  34. package/dist/openapi/unfolded-document.js +6 -3
  35. package/package.json +4 -4
package/README.md CHANGED
@@ -93,6 +93,7 @@ yarn start:dev # node --env-file=.env dist/cli.js
93
93
  | `HTTP_PORT` | no | Server port, integer 0–65535. Defaults to `3450`. `0` binds an OS-assigned ephemeral port. |
94
94
  | `BFF_ALLOWED_ORIGINS`| no | Comma-separated CORS allow-list of exact origins (scheme + host + port). No wildcard. Empty ⇒ no cross-origin browser access. |
95
95
  | `BFF_DEFAULT_TIMEZONE`| no | Fallback IANA timezone used when a request carries neither an `X-Forest-Timezone` header nor a body `timezone`. |
96
+ | `BFF_AI_TIMEOUT_MS` | no | How long `POST /agent/v1/ai/query` waits for the Forest server on the relay itself, in milliseconds. Defaults to `120000` — an AI generation is slow, and neither the app nor the Forest server bounds it. Past it the route answers `504`. It is not the route's end-to-end cap: an expired session is refreshed first, under a separate hard-coded 60 s ceiling that answers `502`, so a request can exceed this value. A malformed value (non-integer, `0`, or above 2147483647) fails the boot, unlike an absent one which takes the default. |
96
97
  | `BFF_OPENAPI_ENABLED` | no | Serve `GET/HEAD /agent/openapi.json` (auth-gated) when `true`. Defaults to `true`. Set to `false` for customers who do not want the HTTP surface exposed: an authenticated `GET`/`HEAD` then gets `404 openapi_disabled`, other methods fall through to the agent routes exactly as they do when enabled, and `forest-bff openapi` keeps working either way. Accepted values: `true`/`false`. **The served document is unfolded and is not filtered per caller**: any authenticated caller, whatever their role, reads the name of every exposed collection, relation and field. Set this to `false` if that surface must not be reachable over HTTP. |
97
98
 
98
99
  ### Config validation
@@ -111,6 +112,13 @@ Every agent call flows through a request edge that enforces three cross-cutting
111
112
  call is proxied to the agent. Errors use a structured, type-first contract — `{ error: { type, status,
112
113
  message, details? } }` — so consumers branch on `error.type`, never on message text.
113
114
 
115
+ Two paths sit before the timezone layer and so skip it: `/agent/v1/context`, and the AI relay
116
+ described below. Neither reaches an agent, so neither has a timezone to resolve. This holds only
117
+ where they are actually mounted — `/agent/v1/context` needs the read-model bundle and the AI relay
118
+ needs an OAuth session. In a deployment missing either, the request falls through to the timezone
119
+ layer instead, so a caller sending no timezone against a BFF with no `BFF_DEFAULT_TIMEZONE` gets
120
+ `400 missing_timezone` rather than the `404` it would expect from an unmounted route.
121
+
114
122
  ### Auth-mode precedence
115
123
 
116
124
  | Presented credentials | Result |
@@ -142,9 +150,38 @@ normalized away, no trailing slash, no wildcard, no subdomain matching):
142
150
 
143
151
  ### Timezone
144
152
 
145
- The BFF always forwards an explicit `timezone` to the agent, resolved in order: (1) `X-Forest-Timezone`
146
- header, (2) body `timezone` field, (3) `BFF_DEFAULT_TIMEZONE`. None → `400 missing_timezone`; a
147
- non-IANA value → `400 invalid_timezone`.
153
+ On every route that reaches an agent, the BFF forwards an explicit `timezone`, resolved in order:
154
+ (1) `X-Forest-Timezone` header, (2) body `timezone` field, (3) `BFF_DEFAULT_TIMEZONE`. None →
155
+ `400 missing_timezone`; a non-IANA value → `400 invalid_timezone`. The two paths listed above are
156
+ mounted ahead of this layer and answer without it.
157
+
158
+ ### AI query relay
159
+
160
+ `POST /agent/v1/ai/query` relays its body to the Forest AI proxy under the OAuth session the BFF
161
+ holds, and relays the answer back:
162
+
163
+ - **Session only.** An API key is refused with `403 oauth_required`: only the OAuth flow yields the
164
+ Forest access token this route forwards. The environment and rendering are derived server-side, so
165
+ sending `forest-*` headers has no effect.
166
+ - **Body.** `1mb` instead of the `16kb` of every other route, and `application/json` only — any other
167
+ content type arrives as an empty body, is relayed upstream as `{}`, and the Forest server rejects
168
+ the query. Over the limit → `413`. The parser is selected on the request path alone and runs ahead
169
+ of authentication, so an unauthenticated caller can make the BFF buffer and JSON-parse 1 MB on this
170
+ path — 64x the pre-auth surface of every other route. Bounding that is a deployment concern (rate
171
+ limiting or a body cap at the reverse proxy), not a BFF one.
172
+ - **Timeout.** `BFF_AI_TIMEOUT_MS` (default `120000`) caps the relay itself; past it → `504`. It is
173
+ not the only cap on the request: when the session's access token has expired the route first
174
+ refreshes it against the Forest server, and that hop has its own hard-coded 60 s ceiling which
175
+ answers `502 network_error`, not `504`.
176
+ - **Error shape.** Below 500 the upstream JSON body is relayed unchanged, so its shape is the Forest
177
+ AI proxy's contract and not the BFF's; when the upstream answer carried no JSON body the BFF
178
+ substitutes its own envelope, keeping the status only when it was 4xx and reporting `502` for a
179
+ 2xx or 3xx that carried nothing to relay. At 500 and above the body is always the BFF
180
+ envelope, so no upstream infrastructure detail reaches the caller. **This is the one route where a
181
+ sub-500 body is not guaranteed to be `{ error: { type, … } }`**, so a consumer cannot branch on
182
+ `error.type` here without checking that the field exists.
183
+ - **Mounted only with OAuth.** Without a complete OAuth configuration the route is absent (`404`) and
184
+ `forest-bff openapi` omits it from the document.
148
185
 
149
186
  ## Sessions & token rotation
150
187
 
@@ -78,17 +78,15 @@ function describePayloadShape(raw) {
78
78
  return raw.constructor?.name ?? 'object';
79
79
  return `object{${Object.keys(raw).join(',')}}`;
80
80
  }
81
- async function handleForm(ctx, client, collection, actionName, recordIds, values, logger) {
81
+ async function handleForm({ ctx, action, values, logger, }) {
82
82
  // Each agent-hitting call is wrapped on its own; getFields/extractRawLayout/mapping stay outside
83
83
  // callAgent so a local BFF bug surfaces as a 500, not a mislabelled agent error. Fields and
84
84
  // layout are read AFTER tryToSetFields because a change hook rebuilds them in place.
85
- const action = await (0, agent_route_helpers_1.callAgent)(() => client.loadAction(collection, actionName, recordIds), logger);
86
85
  const skippedFields = await (0, agent_route_helpers_1.callAgent)(() => action.tryToSetFields(values), logger);
87
86
  ctx.status = 200;
88
87
  ctx.body = (0, action_form_mapper_1.mapActionForm)(action, skippedFields, (0, agent_action_client_1.extractRawLayout)(action));
89
88
  }
90
- async function handleExecute(ctx, client, collection, actionName, recordIds, values, logger) {
91
- const action = await (0, agent_route_helpers_1.callAgent)(() => client.loadAction(collection, actionName, recordIds), logger);
89
+ async function handleExecute({ ctx, action, values, logger, }) {
92
90
  // setFields is strict: an unknown submitted field is a client error (400), not a 500. A transport
93
91
  // failure from the change-hook it triggers is a genuine agent error, so it goes to the mapper.
94
92
  try {
@@ -157,12 +155,19 @@ function createActionRoutesMiddleware({ store, agentUrl, timeoutMs, logger, crea
157
155
  actionEndpoints: readModel.getActionEndpoints(),
158
156
  timeoutMs,
159
157
  });
158
+ const action = await (0, agent_route_helpers_1.callAgent)(() => client.loadAction({
159
+ collection,
160
+ actionName,
161
+ recordIds,
162
+ timezone: ctx.state.timezone,
163
+ }), logger);
164
+ const handlerArgs = { ctx, action, values, logger };
160
165
  if (verb === 'execute') {
161
- await handleExecute(ctx, client, collection, actionName, recordIds, values, logger);
166
+ await handleExecute(handlerArgs);
162
167
  }
163
168
  else {
164
- await handleForm(ctx, client, collection, actionName, recordIds, values, logger);
169
+ await handleForm(handlerArgs);
165
170
  }
166
171
  };
167
172
  }
168
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWN0aW9uLXJvdXRlcy1taWRkbGV3YXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FjdGlvbi9hY3Rpb24tcm91dGVzLW1pZGRsZXdhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFrS0EsK0NBZ0RDO0FBN01ELDREQUltQztBQUVuQyxtRUFBaUU7QUFDakUsNkRBQXFEO0FBQ3JELDZFQUF5RjtBQUN6RixtRUFBMkQ7QUFDM0QscUVBS3FDO0FBQ3JDLCtEQUtrQztBQUVsQyxNQUFNLFlBQVksR0FBRywwREFBMEQsQ0FBQztBQU9oRixvR0FBb0c7QUFDcEcsK0ZBQStGO0FBQy9GLHdFQUF3RTtBQUN4RSxxR0FBcUc7QUFDckcscUdBQXFHO0FBQ3JHLFNBQVMsY0FBYyxDQUFDLEdBQVk7SUFDbEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN4QixNQUFNLElBQUEsaUNBQWMsRUFBQyw0Q0FBNEMsQ0FBQyxDQUFDO0lBQ3JFLENBQUM7SUFFRCxPQUFPLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztBQUNuQyxDQUFDO0FBRUQsU0FBUyxXQUFXLENBQUMsR0FBWTtJQUMvQixJQUFJLEdBQUcsS0FBSyxTQUFTO1FBQUUsT0FBTyxFQUFFLENBQUM7SUFFakMsSUFBSSxPQUFPLEdBQUcsS0FBSyxRQUFRLElBQUksR0FBRyxLQUFLLElBQUksSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDbEUsTUFBTSxJQUFBLGlDQUFjLEVBQUMsMEJBQTBCLENBQUMsQ0FBQztJQUNuRCxDQUFDO0lBRUQsT0FBTyxHQUE4QixDQUFDO0FBQ3hDLENBQUM7QUFFRCwrRkFBK0Y7QUFDL0YsNEZBQTRGO0FBQzVGLFNB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
173
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWN0aW9uLXJvdXRlcy1taWRkbGV3YXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FjdGlvbi9hY3Rpb24tcm91dGVzLW1pZGRsZXdhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUErSkEsK0NBNkRDO0FBbE5ELDREQUltQztBQUVuQyxtRUFBaUU7QUFDakUsNkRBQXFEO0FBQ3JELDZFQUF5RjtBQUN6RixtRUFBMkQ7QUFDM0QscUVBS3FDO0FBQ3JDLCtEQUtrQztBQUVsQyxNQUFNLFlBQVksR0FBRywwREFBMEQsQ0FBQztBQU9oRixvR0FBb0c7QUFDcEcsK0ZBQStGO0FBQy9GLHdFQUF3RTtBQUN4RSxxR0FBcUc7QUFDckcscUdBQXFHO0FBQ3JHLFNBQVMsY0FBYyxDQUFDLEdBQVk7SUFDbEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN4QixNQUFNLElBQUEsaUNBQWMsRUFBQyw0Q0FBNEMsQ0FBQyxDQUFDO0lBQ3JFLENBQUM7SUFFRCxPQUFPLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztBQUNuQyxDQUFDO0FBRUQsU0FBUyxXQUFXLENBQUMsR0FBWTtJQUMvQixJQUFJLEdBQUcsS0FBSyxTQUFTO1FBQUUsT0FBTyxFQUFFLENBQUM7SUFFakMsSUFBSSxPQUFPLEdBQUcsS0FBSyxRQUFRLElBQUksR0FBRyxLQUFLLElBQUksSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDbEUsTUFBTSxJQUFBLGlDQUFjLEVBQUMsMEJBQTBCLENBQUMsQ0FBQztJQUNuRCxDQUFDO0lBRUQsT0FBTyxHQUE4QixDQUFDO0FBQ3hDLENBQUM7QUFFRCwrRkFBK0Y7QUFDL0YsNEZBQTRGO0FBQzVGLFNB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
@@ -19,8 +19,14 @@ export interface Action extends ActionForm {
19
19
  setFields(values: Record<string, unknown>): Promise<void>;
20
20
  execute(): Promise<unknown>;
21
21
  }
22
+ export interface LoadActionParams {
23
+ collection: string;
24
+ actionName: string;
25
+ recordIds: string[];
26
+ timezone: string;
27
+ }
22
28
  export interface AgentActionClient {
23
- loadAction(collection: string, action: string, recordIds: string[]): Promise<Action>;
29
+ loadAction(params: LoadActionParams): Promise<Action>;
24
30
  }
25
31
  export interface AgentActionClientOptions {
26
32
  agentUrl: string;
@@ -9,7 +9,7 @@ const agent_client_1 = require("@forestadmin/agent-client");
9
9
  const create_agent_http_requester_1 = __importDefault(require("../agent/create-agent-http-requester"));
10
10
  // The raw layout must be read AFTER tryToSetFields: a change hook rebuilds fields+layout in place.
11
11
  // agent-client's `Action.getLayout()` only returns an `ActionLayoutRoot` wrapper whose element array
12
- // lives in a protected field. The rollback contract forbids agent-client changes, so we read it
12
+ // lives in a protected field. The rollback contract forbade agent-client changes, so we read it
13
13
  // through a cast rather than adding a public accessor. `extract-raw-layout.test.ts` builds a real
14
14
  // `ActionLayoutRoot` and asserts this unwraps it, so a rename of that field fails a test.
15
15
  function extractRawLayout(action) {
@@ -29,7 +29,7 @@ function createAgentActionClient({ agentUrl, token, actionEndpoints, timeoutMs,
29
29
  httpRequester: (0, create_agent_http_requester_1.default)(token, agentUrl, timeoutMs),
30
30
  });
31
31
  return {
32
- loadAction: (collection, action, recordIds) => client.collection(collection).action(action, { recordIds }),
32
+ loadAction: ({ collection, actionName, recordIds, timezone }) => client.collection(collection).action(actionName, { recordIds, timezone }),
33
33
  };
34
34
  }
35
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtYWN0aW9uLWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hY3Rpb24vYWdlbnQtYWN0aW9uLWNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQStDQSw0Q0FJQztBQU9ELDBDQWlCQztBQXhFRCw0REFBb0U7QUFFcEUsdUdBQTRFO0FBcUM1RSxtR0FBbUc7QUFDbkcscUdBQXFHO0FBQ3JHLGdHQUFnRztBQUNoRyxrR0FBa0c7QUFDbEcsMEZBQTBGO0FBQzFGLFNBQWdCLGdCQUFnQixDQUFDLE1BQWtCO0lBQ2pELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxTQUFTLEVBQXdELENBQUM7SUFFdEYsT0FBTyxJQUFJLENBQUMsTUFBTSxJQUFJLEVBQUUsQ0FBQztBQUMzQixDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILFNBQXdCLHVCQUF1QixDQUFDLEVBQzlDLFFBQVEsRUFDUixLQUFLLEVBQ0wsZUFBZSxFQUNmLFNBQVMsR0FDZ0I7SUFDekIsTUFBTSxNQUFNLEdBQUcsSUFBQSxzQ0FBdUIsRUFBQztRQUNyQyxHQUFHLEVBQUUsUUFBUTtRQUNiLEtBQUs7UUFDTCxlQUFlO1FBQ2YsYUFBYSxFQUFFLElBQUEscUNBQXdCLEVBQUMsS0FBSyxFQUFFLFFBQVEsRUFBRSxTQUFTLENBQUM7S0FDcEUsQ0FBQyxDQUFDO0lBRUgsT0FBTztRQUNMLFVBQVUsRUFBRSxDQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLEVBQUUsQ0FDNUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLEVBQUUsU0FBUyxFQUFFLENBQUM7S0FDOUQsQ0FBQztBQUNKLENBQUMifQ==
35
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtYWN0aW9uLWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hY3Rpb24vYWdlbnQtYWN0aW9uLWNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQXNEQSw0Q0FJQztBQU9ELDBDQWlCQztBQS9FRCw0REFBb0U7QUFFcEUsdUdBQTRFO0FBNEM1RSxtR0FBbUc7QUFDbkcscUdBQXFHO0FBQ3JHLGdHQUFnRztBQUNoRyxrR0FBa0c7QUFDbEcsMEZBQTBGO0FBQzFGLFNBQWdCLGdCQUFnQixDQUFDLE1BQWtCO0lBQ2pELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxTQUFTLEVBQXdELENBQUM7SUFFdEYsT0FBTyxJQUFJLENBQUMsTUFBTSxJQUFJLEVBQUUsQ0FBQztBQUMzQixDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILFNBQXdCLHVCQUF1QixDQUFDLEVBQzlDLFFBQVEsRUFDUixLQUFLLEVBQ0wsZUFBZSxFQUNmLFNBQVMsR0FDZ0I7SUFDekIsTUFBTSxNQUFNLEdBQUcsSUFBQSxzQ0FBdUIsRUFBQztRQUNyQyxHQUFHLEVBQUUsUUFBUTtRQUNiLEtBQUs7UUFDTCxlQUFlO1FBQ2YsYUFBYSxFQUFFLElBQUEscUNBQXdCLEVBQUMsS0FBSyxFQUFFLFFBQVEsRUFBRSxTQUFTLENBQUM7S0FDcEUsQ0FBQyxDQUFDO0lBRUgsT0FBTztRQUNMLFVBQVUsRUFBRSxDQUFDLEVBQUUsVUFBVSxFQUFFLFVBQVUsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLEVBQUUsRUFBRSxDQUM5RCxNQUFNLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUUsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLENBQUM7S0FDNUUsQ0FBQztBQUNKLENBQUMifQ==
@@ -0,0 +1,26 @@
1
+ export { default as AiProxyTimeoutError } from './ai-proxy-timeout-error';
2
+ export interface AiProxyClientOptions {
3
+ forestServerUrl: string;
4
+ timeoutMs: number;
5
+ }
6
+ export interface AiQueryParams {
7
+ saasAccessToken: string;
8
+ environmentId?: number;
9
+ renderingId: number;
10
+ body: unknown;
11
+ }
12
+ export interface AiProxyResponse {
13
+ status: number;
14
+ body: unknown;
15
+ isJson: boolean;
16
+ unparseableBodyError?: unknown;
17
+ }
18
+ export default class AiProxyClient {
19
+ private readonly forestServerUrl;
20
+ private readonly timeoutMs;
21
+ constructor({ forestServerUrl, timeoutMs }: AiProxyClientOptions);
22
+ private url;
23
+ private static headers;
24
+ query(params: AiQueryParams): Promise<AiProxyResponse>;
25
+ }
26
+ //# sourceMappingURL=ai-proxy-client.d.ts.map
@@ -0,0 +1,74 @@
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.AiProxyTimeoutError = void 0;
7
+ const ai_proxy_timeout_error_1 = __importDefault(require("./ai-proxy-timeout-error"));
8
+ var ai_proxy_timeout_error_2 = require("./ai-proxy-timeout-error");
9
+ Object.defineProperty(exports, "AiProxyTimeoutError", { enumerable: true, get: function () { return __importDefault(ai_proxy_timeout_error_2).default; } });
10
+ const AI_NAME = 'zendesk';
11
+ const AI_QUERY_PATH = '/api/ai-proxy/ai-query';
12
+ const JSON_CONTENT_TYPE = 'application/json';
13
+ const TIMEOUT_ERROR_NAME = 'TimeoutError';
14
+ function isTimeout(error) {
15
+ if (typeof error !== 'object' || error === null)
16
+ return false;
17
+ return error.name === TIMEOUT_ERROR_NAME;
18
+ }
19
+ class AiProxyClient {
20
+ constructor({ forestServerUrl, timeoutMs }) {
21
+ this.forestServerUrl = forestServerUrl;
22
+ this.timeoutMs = timeoutMs;
23
+ }
24
+ url() {
25
+ const url = new URL(AI_QUERY_PATH, this.forestServerUrl);
26
+ url.searchParams.set('ai-name', AI_NAME);
27
+ return url.toString();
28
+ }
29
+ static headers({ saasAccessToken, environmentId, renderingId }) {
30
+ const headers = {
31
+ Authorization: `Bearer ${saasAccessToken}`,
32
+ 'Content-Type': JSON_CONTENT_TYPE,
33
+ 'forest-rendering-id': String(renderingId),
34
+ };
35
+ if (environmentId !== undefined)
36
+ headers['forest-environment-id'] = String(environmentId);
37
+ return headers;
38
+ }
39
+ async query(params) {
40
+ let response;
41
+ try {
42
+ response = await fetch(this.url(), {
43
+ method: 'POST',
44
+ headers: AiProxyClient.headers(params),
45
+ body: JSON.stringify(params.body),
46
+ signal: AbortSignal.timeout(this.timeoutMs),
47
+ });
48
+ }
49
+ catch (error) {
50
+ if (isTimeout(error))
51
+ throw new ai_proxy_timeout_error_1.default(error);
52
+ throw error;
53
+ }
54
+ const contentType = response.headers.get('content-type')?.toLowerCase() ?? '';
55
+ const declaresJson = contentType.includes(JSON_CONTENT_TYPE);
56
+ if (!declaresJson)
57
+ return { status: response.status, body: undefined, isJson: false };
58
+ try {
59
+ return { status: response.status, body: await response.json(), isJson: true };
60
+ }
61
+ catch (error) {
62
+ if (isTimeout(error))
63
+ throw new ai_proxy_timeout_error_1.default(error);
64
+ return {
65
+ status: response.status,
66
+ body: undefined,
67
+ isJson: false,
68
+ unparseableBodyError: error,
69
+ };
70
+ }
71
+ }
72
+ }
73
+ exports.default = AiProxyClient;
74
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWktcHJveHktY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FpL2FpLXByb3h5LWNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxzRkFBMkQ7QUFFM0QsbUVBQTBFO0FBQWpFLDhJQUFBLE9BQU8sT0FBdUI7QUFFdkMsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDO0FBQzFCLE1BQU0sYUFBYSxHQUFHLHdCQUF3QixDQUFDO0FBQy9DLE1BQU0saUJBQWlCLEdBQUcsa0JBQWtCLENBQUM7QUFDN0MsTUFBTSxrQkFBa0IsR0FBRyxjQUFjLENBQUM7QUFFMUMsU0FBUyxTQUFTLENBQUMsS0FBYztJQUMvQixJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLEtBQUssSUFBSTtRQUFFLE9BQU8sS0FBSyxDQUFDO0lBRTlELE9BQVEsS0FBNEIsQ0FBQyxJQUFJLEtBQUssa0JBQWtCLENBQUM7QUFDbkUsQ0FBQztBQXFCRCxNQUFxQixhQUFhO0lBSWhDLFlBQVksRUFBRSxlQUFlLEVBQUUsU0FBUyxFQUF3QjtRQUM5RCxJQUFJLENBQUMsZUFBZSxHQUFHLGVBQWUsQ0FBQztRQUN2QyxJQUFJLENBQUMsU0FBUyxHQUFHLFNBQVMsQ0FBQztJQUM3QixDQUFDO0lBRU8sR0FBRztRQUNULE1BQU0sR0FBRyxHQUFHLElBQUksR0FBRyxDQUFDLGFBQWEsRUFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDekQsR0FBRyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRXpDLE9BQU8sR0FBRyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ3hCLENBQUM7SUFFTyxNQUFNLENBQUMsT0FBTyxDQUFDLEVBQUUsZUFBZSxFQUFFLGFBQWEsRUFBRSxXQUFXLEVBQWlCO1FBQ25GLE1BQU0sT0FBTyxHQUEyQjtZQUN0QyxhQUFhLEVBQUUsVUFBVSxlQUFlLEVBQUU7WUFDMUMsY0FBYyxFQUFFLGlCQUFpQjtZQUNqQyxxQkFBcUIsRUFBRSxNQUFNLENBQUMsV0FBVyxDQUFDO1NBQzNDLENBQUM7UUFFRixJQUFJLGFBQWEsS0FBSyxTQUFTO1lBQUUsT0FBTyxDQUFDLHVCQUF1QixDQUFDLEdBQUcsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBRTFGLE9BQU8sT0FBTyxDQUFDO0lBQ2pCLENBQUM7SUFFRCxLQUFLLENBQUMsS0FBSyxDQUFDLE1BQXFCO1FBQy9CLElBQUksUUFBa0IsQ0FBQztRQUV2QixJQUFJLENBQUM7WUFDSCxRQUFRLEdBQUcsTUFBTSxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxFQUFFO2dCQUNqQyxNQUFNLEVBQUUsTUFBTTtnQkFDZCxPQUFPLEVBQUUsYUFBYSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUM7Z0JBQ3RDLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUM7Z0JBQ2pDLE1BQU0sRUFBRSxXQUFXLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUM7YUFDNUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLFNBQVMsQ0FBQyxLQUFLLENBQUM7Z0JBQUUsTUFBTSxJQUFJLGdDQUFtQixDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQzNELE1BQU0sS0FBSyxDQUFDO1FBQ2QsQ0FBQztRQUVELE1BQU0sV0FBVyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsQ0FBQztRQUM5RSxNQUFNLFlBQVksR0FBRyxXQUFXLENBQUMsUUFBUSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFFN0QsSUFBSSxDQUFDLFlBQVk7WUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLFFBQVEsQ0FBQyxNQUFNLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLENBQUM7UUFFdEYsSUFBSSxDQUFDO1lBQ0gsT0FBTyxFQUFFLE1BQU0sRUFBRSxRQUFRLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxNQUFNLFFBQVEsQ0FBQyxJQUFJLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLENBQUM7UUFDaEYsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLFNBQVMsQ0FBQyxLQUFLLENBQUM7Z0JBQUUsTUFBTSxJQUFJLGdDQUFtQixDQUFDLEtBQUssQ0FBQyxDQUFDO1lBRTNELE9BQU87Z0JBQ0wsTUFBTSxFQUFFLFFBQVEsQ0FBQyxNQUFNO2dCQUN2QixJQUFJLEVBQUUsU0FBUztnQkFDZixNQUFNLEVBQUUsS0FBSztnQkFDYixvQkFBb0IsRUFBRSxLQUFLO2FBQzVCLENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBN0RELGdDQTZEQyJ9
@@ -0,0 +1,5 @@
1
+ export default class AiProxyTimeoutError extends Error {
2
+ readonly cause?: unknown;
3
+ constructor(cause?: unknown);
4
+ }
5
+ //# sourceMappingURL=ai-proxy-timeout-error.d.ts.map
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class AiProxyTimeoutError extends Error {
4
+ constructor(cause) {
5
+ super('The Forest server did not respond in time');
6
+ this.name = 'AiProxyTimeoutError';
7
+ this.cause = cause;
8
+ }
9
+ }
10
+ exports.default = AiProxyTimeoutError;
11
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWktcHJveHktdGltZW91dC1lcnJvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9haS9haS1wcm94eS10aW1lb3V0LWVycm9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsTUFBcUIsbUJBQW9CLFNBQVEsS0FBSztJQUdwRCxZQUFZLEtBQWU7UUFDekIsS0FBSyxDQUFDLDJDQUEyQyxDQUFDLENBQUM7UUFDbkQsSUFBSSxDQUFDLElBQUksR0FBRyxxQkFBcUIsQ0FBQztRQUNsQyxJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztJQUNyQixDQUFDO0NBQ0Y7QUFSRCxzQ0FRQyJ9
@@ -0,0 +1,15 @@
1
+ import type AiProxyClient from './ai-proxy-client';
2
+ import type ForestServerClient from '../oauth/forest-server-client';
3
+ import type { SessionStore } from '../oauth/session-store';
4
+ import type { Logger } from '../ports/logger-port';
5
+ import type { Middleware } from 'koa';
6
+ export declare const AI_QUERY_ROUTE = "/agent/v1/ai/query";
7
+ export interface AiRoutesMiddlewareOptions {
8
+ client: AiProxyClient;
9
+ sessionStore: SessionStore;
10
+ serverClient: ForestServerClient;
11
+ environmentId?: number;
12
+ logger: Logger;
13
+ }
14
+ export default function createAiRoutesMiddleware({ client, sessionStore, serverClient, environmentId, logger, }: AiRoutesMiddlewareOptions): Middleware;
15
+ //# sourceMappingURL=ai-routes-middleware.d.ts.map
@@ -0,0 +1,96 @@
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.AI_QUERY_ROUTE = void 0;
7
+ exports.default = createAiRoutesMiddleware;
8
+ const ai_proxy_client_1 = require("./ai-proxy-client");
9
+ const auth_mode_1 = require("../auth/auth-mode");
10
+ const bff_http_error_1 = require("../http/bff-http-error");
11
+ const bff_local_errors_1 = require("../http/bff-local-errors");
12
+ const oauth_error_1 = require("../oauth/oauth-error");
13
+ const session_lifecycle_1 = __importDefault(require("../oauth/session-lifecycle"));
14
+ exports.AI_QUERY_ROUTE = '/agent/v1/ai/query';
15
+ const JSON_CONTENT_TYPE = 'application/json';
16
+ const MAX_CAUSE_DEPTH = 3;
17
+ const MAX_CAUSE_MESSAGE = 512;
18
+ function requireSessionPrincipal(ctx) {
19
+ if (ctx.state.authMode === 'api-key')
20
+ throw (0, bff_local_errors_1.oauthRequired)();
21
+ const principal = ctx.state.principal;
22
+ if (!principal)
23
+ throw (0, bff_http_error_1.unauthorized)('No session for this request');
24
+ return principal;
25
+ }
26
+ function clamp(message) {
27
+ return message.length > MAX_CAUSE_MESSAGE ? `${message.slice(0, MAX_CAUSE_MESSAGE)}…` : message;
28
+ }
29
+ function describeFailure(error, depthLeft = MAX_CAUSE_DEPTH) {
30
+ if (!(error instanceof Error))
31
+ return clamp(String(error));
32
+ const { cause } = error;
33
+ const walkable = depthLeft > 0 && cause !== undefined;
34
+ const tail = walkable ? ` (cause: ${describeFailure(cause, depthLeft - 1)})` : '';
35
+ return `${error.name}: ${clamp(error.message)}${tail}`;
36
+ }
37
+ async function resolveSessionAccessToken(principal, store, serverClient, logger) {
38
+ try {
39
+ return await (0, session_lifecycle_1.default)({ sid: principal.sid, store, serverClient });
40
+ }
41
+ catch (error) {
42
+ if (error instanceof oauth_error_1.OAuthRequestError && error.status >= 500) {
43
+ logger('Warn', 'AI query refused: the Forest server could not refresh the session', {
44
+ cause: describeFailure(error),
45
+ });
46
+ throw (0, bff_local_errors_1.upstreamUnreachable)();
47
+ }
48
+ throw error;
49
+ }
50
+ }
51
+ function relayJsonBodyOrGenericError(ctx, response, logger) {
52
+ const bodyWouldLeakInfrastructure = response.status >= 500 || !response.isJson;
53
+ if (bodyWouldLeakInfrastructure) {
54
+ logger('Warn', 'AI proxy upstream body withheld; client message is generic', {
55
+ status: response.status,
56
+ isJson: response.isJson,
57
+ ...(response.unparseableBodyError !== undefined
58
+ ? { cause: describeFailure(response.unparseableBodyError) }
59
+ : {}),
60
+ });
61
+ throw (0, bff_local_errors_1.upstreamError)(response.status);
62
+ }
63
+ ctx.status = response.status;
64
+ ctx.type = JSON_CONTENT_TYPE;
65
+ ctx.body = JSON.stringify(response.body);
66
+ }
67
+ function createAiRoutesMiddleware({ client, sessionStore, serverClient, environmentId, logger, }) {
68
+ return async function aiRoutesMiddleware(ctx, next) {
69
+ if (ctx.path !== exports.AI_QUERY_ROUTE || ctx.method !== 'POST') {
70
+ await next();
71
+ return;
72
+ }
73
+ const principal = requireSessionPrincipal(ctx);
74
+ const renderingId = (0, auth_mode_1.requireRenderingId)(principal);
75
+ const saasAccessToken = await resolveSessionAccessToken(principal, sessionStore, serverClient, logger);
76
+ let response;
77
+ try {
78
+ response = await client.query({
79
+ saasAccessToken,
80
+ environmentId,
81
+ renderingId,
82
+ body: ctx.request.body,
83
+ });
84
+ }
85
+ catch (error) {
86
+ logger('Warn', 'AI query failed against the Forest server', {
87
+ cause: describeFailure(error),
88
+ });
89
+ if (error instanceof ai_proxy_client_1.AiProxyTimeoutError)
90
+ throw (0, bff_local_errors_1.upstreamTimeout)();
91
+ throw (0, bff_local_errors_1.upstreamUnreachable)();
92
+ }
93
+ relayJsonBodyOrGenericError(ctx, response, logger);
94
+ };
95
+ }
96
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWktcm91dGVzLW1pZGRsZXdhcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWkvYWktcm91dGVzLW1pZGRsZXdhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBc0dBLDJDQTZDQztBQTNJRCx1REFBd0Q7QUFDeEQsaURBQXVEO0FBQ3ZELDJEQUFzRDtBQUN0RCwrREFLa0M7QUFDbEMsc0RBQXlEO0FBQ3pELG1GQUFpRTtBQUVwRCxRQUFBLGNBQWMsR0FBRyxvQkFBb0IsQ0FBQztBQUVuRCxNQUFNLGlCQUFpQixHQUFHLGtCQUFrQixDQUFDO0FBQzdDLE1BQU0sZUFBZSxHQUFHLENBQUMsQ0FBQztBQUMxQixNQUFNLGlCQUFpQixHQUFHLEdBQUcsQ0FBQztBQVU5QixTQUFTLHVCQUF1QixDQUFDLEdBQVk7SUFDM0MsSUFBSSxHQUFHLENBQUMsS0FBSyxDQUFDLFFBQVEsS0FBSyxTQUFTO1FBQUUsTUFBTSxJQUFBLGdDQUFhLEdBQUUsQ0FBQztJQUU1RCxNQUFNLFNBQVMsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLFNBQThDLENBQUM7SUFDM0UsSUFBSSxDQUFDLFNBQVM7UUFBRSxNQUFNLElBQUEsNkJBQVksRUFBQyw2QkFBNkIsQ0FBQyxDQUFDO0lBRWxFLE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUM7QUFFRCxTQUFTLEtBQUssQ0FBQyxPQUFlO0lBQzVCLE9BQU8sT0FBTyxDQUFDLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQztBQUNsRyxDQUFDO0FBRUQsU0FBUyxlQUFlLENBQUMsS0FBYyxFQUFFLFNBQVMsR0FBRyxlQUFlO0lBQ2xFLElBQUksQ0FBQyxDQUFDLEtBQUssWUFBWSxLQUFLLENBQUM7UUFBRSxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUMs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
@@ -7,9 +7,8 @@ exports.AGENT_TOKEN_EXPIRES_IN = void 0;
7
7
  exports.issueAgentToken = issueAgentToken;
8
8
  exports.issueAgentTokenFromPrincipal = issueAgentTokenFromPrincipal;
9
9
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
10
- const bff_http_error_1 = require("../http/bff-http-error");
10
+ const auth_mode_1 = require("../auth/auth-mode");
11
11
  exports.AGENT_TOKEN_EXPIRES_IN = '5m';
12
- const POSITIVE_INTEGER = /^[1-9]\d*$/;
13
12
  function tagsToRecord(tags) {
14
13
  return tags.reduce((memo, { key, value }) => ({ ...memo, [key]: value }), {});
15
14
  }
@@ -40,19 +39,17 @@ function issueAgentToken({ identity, authSecret }) {
40
39
  }, authSecret);
41
40
  }
42
41
  function issueAgentTokenFromPrincipal({ principal, authSecret, }) {
43
- if (!POSITIVE_INTEGER.test(String(principal.rendering_id))) {
44
- throw (0, bff_http_error_1.unauthorized)('The session carries no usable rendering');
45
- }
42
+ const renderingId = (0, auth_mode_1.requireRenderingId)(principal);
46
43
  return signWithSnakeCaseAliasesForRubyAndPythonAgents({
47
44
  id: principal.id,
48
45
  email: principal.email,
49
46
  firstName: principal.first_name,
50
47
  lastName: principal.last_name,
51
48
  team: principal.team,
52
- renderingId: Number(principal.rendering_id),
49
+ renderingId,
53
50
  tags: principal.tags ?? {},
54
51
  permissionLevel: principal.permission_level,
55
52
  role: principal.role,
56
53
  }, authSecret);
57
54
  }
58
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtdG9rZW4uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpLWtleS9hZ2VudC10b2tlbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUEyREEsMENBZ0JDO0FBRUQsb0VBc0JDO0FBaEdELGdFQUF3QztBQUV4QywyREFBc0Q7QUFFekMsUUFBQSxzQkFBc0IsR0FBRyxJQUFJLENBQUM7QUFZM0MsTUFBTSxnQkFBZ0IsR0FBRyxZQUFZLENBQUM7QUFFdEMsU0FBUyxZQUFZLENBQUMsSUFBc0M7SUFDMUQsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsR0FBRyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ2hGLENBQUM7QUFjRCxTQUFTLDhDQUE4QyxDQUNyRCxNQUF5QixFQUN6QixVQUFrQjtJQUVsQixNQUFNLFNBQVMsR0FBRyxNQUFNLENBQUMsU0FBUyxJQUFJLEVBQUUsQ0FBQztJQUN6QyxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBUSxJQUFJLEVBQUUsQ0FBQztJQUV2QyxPQUFPLHNCQUFZLENBQUMsSUFBSSxDQUN0QjtRQUNFLEdBQUcsTUFBTTtRQUNULFNBQVM7UUFDVCxRQUFRO1FBQ1IsVUFBVSxFQUFFLFNBQVM7UUFDckIsU0FBUyxFQUFFLFFBQVE7UUFDbkIsWUFBWSxFQUFFLE1BQU0sQ0FBQyxXQUFXO1FBQ2hDLGdCQUFnQixFQUFFLE1BQU0sQ0FBQyxlQUFlO0tBQ3pDLEVBQ0QsVUFBVSxFQUNWLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsOEJBQXNCLEVBQUUsQ0FDMUQsQ0FBQztBQUNKLENBQUM7QUFFRCxTQUFnQixlQUFlLENBQUMsRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUF5QjtJQUM3RSxNQUFNLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxHQUFHLFFBQVEsQ0FBQztJQUV2QyxPQUFPLDhDQUE4QyxDQUNuRDtRQUNFLEVBQUUsRUFBRSxJQUFJLENBQUMsRUFBRTtRQUNYLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSztRQUNqQixTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVM7UUFDekIsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRO1FBQ3ZCLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSTtRQUNmLFdBQVc7UUFDWCxJQUFJLEVBQUUsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7UUFDN0IsZUFBZSxFQUFFLElBQUksQ0FBQyxlQUFlO0tBQ3RDLEVBQ0QsVUFBVSxDQUNYLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBZ0IsNEJBQTRCLENBQUMsRUFDM0MsU0FBUyxFQUNULFVBQVUsR0FDeUI7SUFDbkMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLFlBQVksQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUMzRCxNQUFNLElBQUEsNkJBQVksRUFBQyx5Q0FBeUMsQ0FBQyxDQUFDO0lBQ2hFLENBQUM7SUFFRCxPQUFPLDhDQUE4QyxDQUNuRDtRQUNFLEVBQUUsRUFBRSxTQUFTLENBQUMsRUFBRTtRQUNoQixLQUFLLEVBQUUsU0FBUyxDQUFDLEtBQUs7UUFDdEIsU0FBUyxFQUFFLFNBQVMsQ0FBQyxVQUFVO1FBQy9CLFFBQVEsRUFBRSxTQUFTLENBQUMsU0FBUztRQUM3QixJQUFJLEVBQUUsU0FBUyxDQUFDLElBQUk7UUFDcEIsV0FBVyxFQUFFLE1BQU0sQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDO1FBQzNDLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSSxJQUFJLEVBQUU7UUFDMUIsZUFBZSxFQUFFLFNBQVMsQ0FBQyxnQkFBZ0I7UUFDM0MsSUFBSSxFQUFFLFNBQVMsQ0FBQyxJQUFJO0tBQ3JCLEVBQ0QsVUFBVSxDQUNYLENBQUM7QUFDSixDQUFDIn0=
55
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtdG9rZW4uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpLWtleS9hZ2VudC10b2tlbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUF5REEsMENBZ0JDO0FBRUQsb0VBb0JDO0FBNUZELGdFQUF3QztBQUV4QyxpREFBdUQ7QUFFMUMsUUFBQSxzQkFBc0IsR0FBRyxJQUFJLENBQUM7QUFZM0MsU0FBUyxZQUFZLENBQUMsSUFBc0M7SUFDMUQsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsR0FBRyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ2hGLENBQUM7QUFjRCxTQUFTLDhDQUE4QyxDQUNyRCxNQUF5QixFQUN6QixVQUFrQjtJQUVsQixNQUFNLFNBQVMsR0FBRyxNQUFNLENBQUMsU0FBUyxJQUFJLEVBQUUsQ0FBQztJQUN6QyxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBUSxJQUFJLEVBQUUsQ0FBQztJQUV2QyxPQUFPLHNCQUFZLENBQUMsSUFBSSxDQUN0QjtRQUNFLEdBQUcsTUFBTTtRQUNULFNBQVM7UUFDVCxRQUFRO1FBQ1IsVUFBVSxFQUFFLFNBQVM7UUFDckIsU0FBUyxFQUFFLFFBQVE7UUFDbkIsWUFBWSxFQUFFLE1BQU0sQ0FBQyxXQUFXO1FBQ2hDLGdCQUFnQixFQUFFLE1BQU0sQ0FBQyxlQUFlO0tBQ3pDLEVBQ0QsVUFBVSxFQUNWLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsOEJBQXNCLEVBQUUsQ0FDMUQsQ0FBQztBQUNKLENBQUM7QUFFRCxTQUFnQixlQUFlLENBQUMsRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUF5QjtJQUM3RSxNQUFNLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxHQUFHLFFBQVEsQ0FBQztJQUV2QyxPQUFPLDhDQUE4QyxDQUNuRDtRQUNFLEVBQUUsRUFBRSxJQUFJLENBQUMsRUFBRTtRQUNYLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSztRQUNqQixTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVM7UUFDekIsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRO1FBQ3ZCLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSTtRQUNmLFdBQVc7UUFDWCxJQUFJLEVBQUUsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7UUFDN0IsZUFBZSxFQUFFLElBQUksQ0FBQyxlQUFlO0tBQ3RDLEVBQ0QsVUFBVSxDQUNYLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBZ0IsNEJBQTRCLENBQUMsRUFDM0MsU0FBUyxFQUNULFVBQVUsR0FDeUI7SUFDbkMsTUFBTSxXQUFXLEdBQUcsSUFBQSw4QkFBa0IsRUFBQyxTQUFTLENBQUMsQ0FBQztJQUVsRCxPQUFPLDhDQUE4QyxDQUNuRDtRQUNFLEVBQUUsRUFBRSxTQUFTLENBQUMsRUFBRTtRQUNoQixLQUFLLEVBQUUsU0FBUyxDQUFDLEtBQUs7UUFDdEIsU0FBUyxFQUFFLFNBQVMsQ0FBQyxVQUFVO1FBQy9CLFFBQVEsRUFBRSxTQUFTLENBQUMsU0FBUztRQUM3QixJQUFJLEVBQUUsU0FBUyxDQUFDLElBQUk7UUFDcEIsV0FBVztRQUNYLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSSxJQUFJLEVBQUU7UUFDMUIsZUFBZSxFQUFFLFNBQVMsQ0FBQyxnQkFBZ0I7UUFDM0MsSUFBSSxFQUFFLFNBQVMsQ0FBQyxJQUFJO0tBQ3JCLEVBQ0QsVUFBVSxDQUNYLENBQUM7QUFDSixDQUFDIn0=
@@ -1,4 +1,6 @@
1
+ import type { BffAccessTokenPayload } from '../oauth/bff-token';
1
2
  export type AuthMode = 'oauth' | 'api-key';
3
+ export declare function requireRenderingId(principal: BffAccessTokenPayload): number;
2
4
  export declare function extractBearerToken(authorization: string | undefined): string | undefined;
3
5
  export declare function resolveAuthMode({ hasBearer, hasApiKey, }: {
4
6
  hasBearer: boolean;
@@ -1,9 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireRenderingId = requireRenderingId;
3
4
  exports.extractBearerToken = extractBearerToken;
4
5
  exports.resolveAuthMode = resolveAuthMode;
5
6
  const bff_http_error_1 = require("../http/bff-http-error");
6
7
  const BEARER_PATTERN = /^Bearer[ \t]+(.+)$/i;
8
+ const POSITIVE_INTEGER = /^[1-9]\d*$/;
9
+ function requireRenderingId(principal) {
10
+ if (!POSITIVE_INTEGER.test(String(principal.rendering_id))) {
11
+ throw (0, bff_http_error_1.unauthorized)('The session carries no usable rendering');
12
+ }
13
+ return Number(principal.rendering_id);
14
+ }
7
15
  function extractBearerToken(authorization) {
8
16
  if (!authorization)
9
17
  return undefined;
@@ -22,4 +30,4 @@ function resolveAuthMode({ hasBearer, hasApiKey, }) {
22
30
  return 'oauth';
23
31
  throw (0, bff_http_error_1.unauthorized)();
24
32
  }
25
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aC1tb2RlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2F1dGgvYXV0aC1tb2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBTUEsZ0RBU0M7QUFFRCwwQ0FZQztBQTdCRCwyREFBNEU7QUFJNUUsTUFBTSxjQUFjLEdBQUcscUJBQXFCLENBQUM7QUFFN0MsU0FBZ0Isa0JBQWtCLENBQUMsYUFBaUM7SUFDbEUsSUFBSSxDQUFDLGFBQWE7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUVyQyxNQUFNLEtBQUssR0FBRyxjQUFjLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3hELElBQUksQ0FBQyxLQUFLO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFFN0IsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO0lBRTlCLE9BQU8sS0FBSyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7QUFDMUMsQ0FBQztBQUVELFNBQWdCLGVBQWUsQ0FBQyxFQUM5QixTQUFTLEVBQ1QsU0FBUyxHQUlWO0lBQ0MsSUFBSSxTQUFTLElBQUksU0FBUztRQUFFLE1BQU0sSUFBQSxxQ0FBb0IsR0FBRSxDQUFDO0lBQ3pELElBQUksU0FBUztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ2hDLElBQUksU0FBUztRQUFFLE9BQU8sT0FBTyxDQUFDO0lBRTlCLE1BQU0sSUFBQSw2QkFBWSxHQUFFLENBQUM7QUFDdkIsQ0FBQyJ9
33
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aC1tb2RlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2F1dGgvYXV0aC1tb2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBU0EsZ0RBTUM7QUFFRCxnREFTQztBQUVELDBDQVlDO0FBdENELDJEQUE0RTtBQUk1RSxNQUFNLGNBQWMsR0FBRyxxQkFBcUIsQ0FBQztBQUM3QyxNQUFNLGdCQUFnQixHQUFHLFlBQVksQ0FBQztBQUV0QyxTQUFnQixrQkFBa0IsQ0FBQyxTQUFnQztJQUNqRSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzNELE1BQU0sSUFBQSw2QkFBWSxFQUFDLHlDQUF5QyxDQUFDLENBQUM7SUFDaEUsQ0FBQztJQUVELE9BQU8sTUFBTSxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsQ0FBQztBQUN4QyxDQUFDO0FBRUQsU0FBZ0Isa0JBQWtCLENBQUMsYUFBaUM7SUFDbEUsSUFBSSxDQUFDLGFBQWE7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUVyQyxNQUFNLEtBQUssR0FBRyxjQUFjLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3hELElBQUksQ0FBQyxLQUFLO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFFN0IsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO0lBRTlCLE9BQU8sS0FBSyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7QUFDMUMsQ0FBQztBQUVELFNBQWdCLGVBQWUsQ0FBQyxFQUM5QixTQUFTLEVBQ1QsU0FBUyxHQUlWO0lBQ0MsSUFBSSxTQUFTLElBQUksU0FBUztRQUFFLE1BQU0sSUFBQSxxQ0FBb0IsR0FBRSxDQUFDO0lBQ3pELElBQUksU0FBUztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ2hDLElBQUksU0FBUztRQUFFLE9BQU8sT0FBTyxDQUFDO0lBRTlCLE1BQU0sSUFBQSw2QkFBWSxHQUFFLENBQUM7QUFDdkIsQ0FBQyJ9
@@ -2,7 +2,16 @@ import type { BFFConfig } from './config/env-config';
2
2
  import type { UnfoldSource } from './openapi/unfolded-document';
3
3
  import type { Logger } from './ports/logger-port';
4
4
  import BFFHttpServer from './http/bff-http-server';
5
+ interface ResolvedOAuthConfig {
6
+ forestServerUrl: string;
7
+ forestEnvSecret: string;
8
+ forestAppUrl: string;
9
+ forestAuthSecret: string;
10
+ tokenEncryptionKey: string;
11
+ }
12
+ export declare function resolveOAuthConfig(config: BFFConfig): ResolvedOAuthConfig | undefined;
5
13
  export declare function resolveUnfoldSource(config: BFFConfig, logger: Logger): UnfoldSource | undefined;
6
14
  export default function runCli(env: NodeJS.ProcessEnv, logger?: Logger): Promise<BFFHttpServer>;
7
15
  export declare function reportFatalError(err: unknown): void;
16
+ export {};
8
17
  //# sourceMappingURL=cli-core.d.ts.map