@forestadmin/agent-bff 1.8.5 → 1.9.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.
@@ -0,0 +1,30 @@
1
+ export interface ActionExecuteSuccessBody {
2
+ type: 'success';
3
+ message: string | null;
4
+ invalidated: string[];
5
+ html: string | null;
6
+ }
7
+ export interface ActionExecuteWebhookBody {
8
+ type: 'webhook';
9
+ url: unknown;
10
+ method: unknown;
11
+ headers: unknown;
12
+ body: unknown;
13
+ }
14
+ export interface ActionExecuteRedirectBody {
15
+ type: 'redirect';
16
+ path: unknown;
17
+ }
18
+ export interface ActionExecuteUnsupportedBody {
19
+ error: {
20
+ type: 'unsupported_action_result';
21
+ status: 501;
22
+ };
23
+ }
24
+ export type ActionExecuteMappedBody = ActionExecuteSuccessBody | ActionExecuteWebhookBody | ActionExecuteRedirectBody | ActionExecuteUnsupportedBody;
25
+ export interface ActionExecuteMapped {
26
+ status: number;
27
+ body: ActionExecuteMappedBody;
28
+ }
29
+ export declare function mapActionExecuteResult(raw: unknown): ActionExecuteMapped;
30
+ //# sourceMappingURL=action-execute-mapper.d.ts.map
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mapActionExecuteResult = mapActionExecuteResult;
4
+ // The agent always serializes the four webhook fields together
5
+ // (`agent/src/routes/modification/action/action.ts`), so a payload missing url/method is malformed
6
+ // and must reach the 501 path rather than surface as a 200 the client cannot act on.
7
+ function isWebhook(value) {
8
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
9
+ return false;
10
+ const { url, method } = value;
11
+ return typeof url === 'string' && typeof method === 'string';
12
+ }
13
+ // The agent serializes a Success refresh as `{ relationships: [...] }`, so anything else (an array,
14
+ // a bare object without relationships) is malformed and must not act as the Success discriminator.
15
+ function isRefresh(value) {
16
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
17
+ return false;
18
+ return Array.isArray(value.relationships);
19
+ }
20
+ // Normalizes the agent's 200 execute payload into the flat BFF wrapper. The execute result is
21
+ // untyped at the BFF boundary (`Action.execute(): Promise<unknown>`), so we discriminate on the
22
+ // agent HTTP payload shape. A File result streams a binary with no JSON marker, so any unrecognized
23
+ // 200 body falls through to a structured 501 rather than being mislabelled.
24
+ function mapActionExecuteResult(raw) {
25
+ const body = (typeof raw === 'object' && raw !== null ? raw : {});
26
+ // Each branch validates the value shape, not just key presence: a malformed payload
27
+ // (`{ webhook: null }`, `{ redirectTo: {} }`, `{ success: {} }`) must fall through to the 501
28
+ // path rather than be surfaced as a 200 with null fields the client cannot tell from a real one.
29
+ if (isWebhook(body.webhook)) {
30
+ const { url, method, headers, body: hookBody } = body.webhook;
31
+ return { status: 200, body: { type: 'webhook', url, method, headers, body: hookBody } };
32
+ }
33
+ if (typeof body.redirectTo === 'string') {
34
+ return { status: 200, body: { type: 'redirect', path: body.redirectTo } };
35
+ }
36
+ // A Success carries a string message and/or a well-formed refresh; the agent always sends the
37
+ // refresh, so a bare refresh (message absent) still normalizes to a success with a null message.
38
+ if (typeof body.success === 'string' || isRefresh(body.refresh)) {
39
+ const relationships = isRefresh(body.refresh) ? body.refresh.relationships : [];
40
+ return {
41
+ status: 200,
42
+ body: {
43
+ type: 'success',
44
+ message: typeof body.success === 'string' ? body.success : null,
45
+ invalidated: relationships.filter((name) => typeof name === 'string'),
46
+ html: typeof body.html === 'string' ? body.html : null,
47
+ },
48
+ };
49
+ }
50
+ return { status: 501, body: { error: { type: 'unsupported_action_result', status: 501 } } };
51
+ }
52
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWN0aW9uLWV4ZWN1dGUtbWFwcGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FjdGlvbi9hY3Rpb24tZXhlY3V0ZS1tYXBwZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFpRUEsd0RBaUNDO0FBeERELCtEQUErRDtBQUMvRCxtR0FBbUc7QUFDbkcscUZBQXFGO0FBQ3JGLFNBQVMsU0FBUyxDQUFDLEtBQWM7SUFDL0IsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksS0FBSyxLQUFLLElBQUksSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQztRQUFFLE9BQU8sS0FBSyxDQUFDO0lBRXRGLE1BQU0sRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLEdBQUcsS0FBZ0MsQ0FBQztJQUV6RCxPQUFPLE9BQU8sR0FBRyxLQUFLLFFBQVEsSUFBSSxPQUFPLE1BQU0sS0FBSyxRQUFRLENBQUM7QUFDL0QsQ0FBQztBQUVELG9HQUFvRztBQUNwRyxtR0FBbUc7QUFDbkcsU0FBUyxTQUFTLENBQUMsS0FBYztJQUMvQixJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFFdEYsT0FBTyxLQUFLLENBQUMsT0FBTyxDQUFFLEtBQXFDLENBQUMsYUFBYSxDQUFDLENBQUM7QUFDN0UsQ0FBQztBQUVELDhGQUE4RjtBQUM5RixnR0FBZ0c7QUFDaEcsb0dBQW9HO0FBQ3BHLDRFQUE0RTtBQUM1RSxTQUFnQixzQkFBc0IsQ0FBQyxHQUFZO0lBQ2pELE1BQU0sSUFBSSxHQUFHLENBQUMsT0FBTyxHQUFHLEtBQUssUUFBUSxJQUFJLEdBQUcsS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUE0QixDQUFDO0lBRTdGLG9GQUFvRjtJQUNwRiw4RkFBOEY7SUFDOUYsaUdBQWlHO0lBQ2pHLElBQUksU0FBUyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQzVCLE1BQU0sRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUU5RCxPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsRUFBRSxDQUFDO0lBQzFGLENBQUM7SUFFRCxJQUFJLE9BQU8sSUFBSSxDQUFDLFVBQVUsS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUN4QyxPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQztJQUM1RSxDQUFDO0lBRUQsOEZBQThGO0lBQzlGLGlHQUFpRztJQUNqRyxJQUFJLE9BQU8sSUFBSSxDQUFDLE9BQU8sS0FBSyxRQUFRLElBQUksU0FBUyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQ2hFLE1BQU0sYUFBYSxHQUFHLFNBQVMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFFaEYsT0FBTztZQUNMLE1BQU0sRUFBRSxHQUFHO1lBQ1gsSUFBSSxFQUFFO2dCQUNKLElBQUksRUFBRSxTQUFTO2dCQUNmLE9BQU8sRUFBRSxPQUFPLElBQUksQ0FBQyxPQUFPLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxJQUFJO2dCQUMvRCxXQUFXLEVBQUUsYUFBYSxDQUFDLE1BQU0sQ0FBQyxDQUFDLElBQUksRUFBa0IsRUFBRSxDQUFDLE9BQU8sSUFBSSxLQUFLLFFBQVEsQ0FBQztnQkFDckYsSUFBSSxFQUFFLE9BQU8sSUFBSSxDQUFDLElBQUksS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUk7YUFDdkQ7U0FDRixDQUFDO0lBQ0osQ0FBQztJQUVELE9BQU8sRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxFQUFFLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSwyQkFBMkIsRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLEVBQUUsRUFBRSxDQUFDO0FBQzlGLENBQUMifQ==
@@ -34,11 +34,14 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.default = createActionRoutesMiddleware;
37
+ const agent_client_1 = require("@forestadmin/agent-client");
38
+ const action_execute_mapper_1 = require("./action-execute-mapper");
37
39
  const action_form_mapper_1 = require("./action-form-mapper");
38
40
  const agent_action_client_1 = __importStar(require("./agent-action-client"));
41
+ const agent_error_mapper_1 = require("../http/agent-error-mapper");
39
42
  const agent_route_helpers_1 = require("../http/agent-route-helpers");
40
43
  const bff_local_errors_1 = require("../http/bff-local-errors");
41
- const ACTION_FORM_ROUTE = /^\/agent\/v1\/([^/]+)\/actions\/([^/]+)\/form$/;
44
+ const ACTION_ROUTE = /^\/agent\/v1\/([^/]+)\/actions\/([^/]+)\/(form|execute)$/;
42
45
  // recordIds are opaque BFF ids; only presence and array-ness are checked. An empty array is allowed
43
46
  // on purpose (global/bulk actions have no selected records); the spec AC only covers a missing
44
47
  // recordIds, so `[]` is an explicit BFF choice, not a spec requirement.
@@ -58,15 +61,85 @@ function parseValues(raw) {
58
61
  }
59
62
  return raw;
60
63
  }
64
+ // Only plain objects are enumerated: a File result arrives as a Buffer/stream whose every byte
65
+ // index is an enumerable key, so `Object.keys` on it would build a multi-million entry log.
66
+ function describePayloadShape(raw) {
67
+ if (raw === null)
68
+ return 'null';
69
+ if (typeof raw !== 'object')
70
+ return typeof raw;
71
+ if (Buffer.isBuffer(raw))
72
+ return `Buffer(${raw.length})`;
73
+ if (ArrayBuffer.isView(raw))
74
+ return `${raw.constructor.name}(${raw.byteLength})`;
75
+ if (Array.isArray(raw))
76
+ return `array(${raw.length})`;
77
+ if (Object.getPrototypeOf(raw) !== Object.prototype)
78
+ return raw.constructor?.name ?? 'object';
79
+ return `object{${Object.keys(raw).join(',')}}`;
80
+ }
81
+ async function handleForm(ctx, client, collection, actionName, recordIds, values, logger) {
82
+ // Each agent-hitting call is wrapped on its own; getFields/extractRawLayout/mapping stay outside
83
+ // callAgent so a local BFF bug surfaces as a 500, not a mislabelled agent error. Fields and
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
+ const skippedFields = await (0, agent_route_helpers_1.callAgent)(() => action.tryToSetFields(values), logger);
87
+ ctx.status = 200;
88
+ ctx.body = (0, action_form_mapper_1.mapActionForm)(action, skippedFields, (0, agent_action_client_1.extractRawLayout)(action));
89
+ }
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);
92
+ // setFields is strict: an unknown submitted field is a client error (400), not a 500. A transport
93
+ // failure from the change-hook it triggers is a genuine agent error, so it goes to the mapper.
94
+ try {
95
+ await action.setFields(values);
96
+ }
97
+ catch (error) {
98
+ if (error instanceof agent_client_1.UnknownActionFieldError)
99
+ throw (0, bff_local_errors_1.invalidRequest)(error.message);
100
+ throw (0, agent_error_mapper_1.mapAgentError)(error, { logger });
101
+ }
102
+ // execute() cannot go through the generic callAgent: agent-client turns the native action Error
103
+ // (HTTP 400) into ActionFormValidationError, a non-AgentHttpError the mapper would mislabel as a
104
+ // transport 502. So the semantic outcomes are caught here, everything else falls to the mapper.
105
+ let raw;
106
+ try {
107
+ raw = await action.execute();
108
+ }
109
+ catch (error) {
110
+ if (error instanceof agent_client_1.ActionRequiresApprovalError) {
111
+ throw (0, bff_local_errors_1.actionRequiresApproval)(error.message, error.roleIdsAllowedToApprove !== undefined
112
+ ? { roleIdsAllowedToApprove: error.roleIdsAllowedToApprove }
113
+ : undefined);
114
+ }
115
+ if (error instanceof agent_client_1.ActionFormValidationError) {
116
+ ctx.status = 400;
117
+ ctx.body = { type: 'error', status: 400, message: error.message, html: error.html ?? null };
118
+ return;
119
+ }
120
+ throw (0, agent_error_mapper_1.mapAgentError)(error, { logger });
121
+ }
122
+ const { status, body } = (0, action_execute_mapper_1.mapActionExecuteResult)(raw);
123
+ // An unrecognized payload (a File stream, or a new agent result type) maps to a generic 501 with
124
+ // no trace of what it was; log a short shape hint so the case can be diagnosed without the body.
125
+ if (status === 501) {
126
+ logger('Warn', 'Unrecognized action execute result mapped to 501', {
127
+ shape: describePayloadShape(raw),
128
+ });
129
+ }
130
+ ctx.status = status;
131
+ ctx.body = body;
132
+ }
61
133
  function createActionRoutesMiddleware({ store, agentUrl, logger, createClient = agent_action_client_1.default, }) {
62
134
  return async function actionRoutesMiddleware(ctx, next) {
63
- const match = ACTION_FORM_ROUTE.exec(ctx.path);
135
+ const match = ACTION_ROUTE.exec(ctx.path);
64
136
  if (!match || ctx.method !== 'POST') {
65
137
  await next();
66
138
  return;
67
139
  }
68
140
  const collection = (0, agent_route_helpers_1.decodeSegment)(match[1], 'collection name');
69
141
  const actionName = (0, agent_route_helpers_1.decodeSegment)(match[2], 'action name');
142
+ const verb = match[3];
70
143
  const token = (0, agent_route_helpers_1.requireAgentToken)(ctx);
71
144
  const readModel = await (0, agent_route_helpers_1.resolveReadModel)(store);
72
145
  // The read-model's action map IS the allow-list, so an absent action cannot be told from a
@@ -85,13 +158,12 @@ function createActionRoutesMiddleware({ store, agentUrl, logger, createClient =
85
158
  token,
86
159
  actionEndpoints: readModel.getActionEndpoints(),
87
160
  });
88
- // Each agent-hitting call is wrapped on its own; getFields/extractRawLayout/mapping stay outside
89
- // callAgent so a local BFF bug surfaces as a 500, not a mislabelled agent error. Fields and
90
- // layout are read AFTER tryToSetFields because a change hook rebuilds them in place.
91
- const action = await (0, agent_route_helpers_1.callAgent)(() => client.loadActionForm(collection, actionName, recordIds), logger);
92
- const skippedFields = await (0, agent_route_helpers_1.callAgent)(() => action.tryToSetFields(values), logger);
93
- ctx.status = 200;
94
- ctx.body = (0, action_form_mapper_1.mapActionForm)(action, skippedFields, (0, agent_action_client_1.extractRawLayout)(action));
161
+ if (verb === 'execute') {
162
+ await handleExecute(ctx, client, collection, actionName, recordIds, values, logger);
163
+ }
164
+ else {
165
+ await handleForm(ctx, client, collection, actionName, recordIds, values, logger);
166
+ }
95
167
  };
96
168
  }
97
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWN0aW9uLXJvdXRlcy1taWRkbGV3YXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FjdGlvbi9hY3Rpb24tcm91dGVzLW1pZGRsZXdhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFvREEsK0NBbURDO0FBbEdELDZEQUFxRDtBQUNyRCw2RUFBeUY7QUFDekYscUVBS3FDO0FBQ3JDLCtEQUF5RTtBQUV6RSxNQUFNLGlCQUFpQixHQUFHLGdEQUFnRCxDQUFDO0FBTzNFLG9HQUFvRztBQUNwRywrRkFBK0Y7QUFDL0Ysd0VBQXdFO0FBQ3hFLHFHQUFxRztBQUNyRyxxR0FBcUc7QUFDckcsU0FBUyxjQUFjLENBQUMsR0FBWTtJQUNsQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3hCLE1BQU0sSUFBQSxpQ0FBYyxFQUFDLDRDQUE0QyxDQUFDLENBQUM7SUFDckUsQ0FBQztJQUVELE9BQU8sR0FBRyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQ25DLENBQUM7QUFFRCxTQUFTLFdBQVcsQ0FBQyxHQUFZO0lBQy9CLElBQUksR0FBRyxLQUFLLFNBQVM7UUFBRSxPQUFPLEVBQUUsQ0FBQztJQUVqQyxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVEsSUFBSSxHQUFHLEtBQUssSUFBSSxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNsRSxNQUFNLElBQUEsaUNBQWMsRUFBQywwQkFBMEIsQ0FBQyxDQUFDO0lBQ25ELENBQUM7SUFFRCxPQUFPLEdBQThCLENBQUM7QUFDeEMsQ0FBQztBQVNELFNBQXdCLDRCQUE0QixDQUFDLEVBQ25ELEtBQUssRUFDTCxRQUFRLEVBQ1IsTUFBTSxFQUNOLFlBQVksR0FBRyw2QkFBOEIsR0FDZjtJQUM5QixPQUFPLEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxHQUFHLEVBQUUsSUFBSTtRQUNwRCxNQUFNLEtBQUssR0FBRyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBRS9DLElBQUksQ0FBQyxLQUFLLElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxNQUFNLEVBQUUsQ0FBQztZQUNwQyxNQUFNLElBQUksRUFBRSxDQUFDO1lBRWIsT0FBTztRQUNULENBQUM7UUFFRCxNQUFNLFVBQVUsR0FBRyxJQUFBLG1DQUFhLEVBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLGlCQUFpQixDQUFDLENBQUM7UUFDOUQsTUFBTSxVQUFVLEdBQUcsSUFBQSxtQ0FBYSxFQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxhQUFhLENBQUMsQ0FBQztRQUMxRCxNQUFNLEtBQUssR0FBRyxJQUFBLHVDQUFpQixFQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3JDLE1BQU0sU0FBUyxHQUFHLE1BQU0sSUFBQSxzQ0FBZ0IsRUFBQyxLQUFLLENBQUMsQ0FBQztRQUVoRCwyRkFBMkY7UUFDM0YsNkZBQTZGO1FBQzdGLDZGQUE2RjtRQUM3Riw2RkFBNkY7UUFDN0YsNkZBQTZGO1FBQzdGLElBQUksQ0FBQyxTQUFTLENBQUMsZUFBZSxDQUFDLFVBQVUsRUFBRSxVQUFVLENBQUMsRUFBRSxDQUFDO1lBQ3ZELE1BQU0sSUFBQSxnQ0FBYSxFQUFDLG1CQUFtQixVQUFVLElBQUksVUFBVSxFQUFFLENBQUMsQ0FBQztRQUNyRSxDQUFDO1FBRUQsTUFBTSxJQUFJLEdBQUcsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLElBQUksSUFBSSxFQUFFLENBQTBCLENBQUM7UUFDL0QsTUFBTSxTQUFTLEdBQUcsY0FBYyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUNqRCxNQUFNLE1BQU0sR0FBRyxXQUFXLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRXhDLE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQztZQUMxQixRQUFRO1lBQ1IsS0FBSztZQUNMLGVBQWUsRUFBRSxTQUFTLENBQUMsa0JBQWtCLEVBQUU7U0FDaEQsQ0FBQyxDQUFDO1FBRUgsaUdBQWlHO1FBQ2pHLDRGQUE0RjtRQUM1RixxRkFBcUY7UUFDckYsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFBLCtCQUFTLEVBQzVCLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsVUFBVSxFQUFFLFVBQVUsRUFBRSxTQUFTLENBQUMsRUFDOUQsTUFBTSxDQUNQLENBQUM7UUFDRixNQUFNLGFBQWEsR0FBRyxNQUFNLElBQUEsK0JBQVMsRUFBQyxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBRW5GLEdBQUcsQ0FBQyxNQUFNLEdBQUcsR0FBRyxDQUFDO1FBQ2pCLEdBQUcsQ0FBQyxJQUFJLEdBQUcsSUFBQSxrQ0FBYSxFQUFDLE1BQU0sRUFBRSxhQUFhLEVBQUUsSUFBQSxzQ0FBZ0IsRUFBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBQzVFLENBQUMsQ0FBQztBQUNKLENBQUMifQ==
169
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWN0aW9uLXJvdXRlcy1taWRkbGV3YXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FjdGlvbi9hY3Rpb24tcm91dGVzLW1pZGRsZXdhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUErSkEsK0NBOENDO0FBeE1ELDREQUltQztBQUVuQyxtRUFBaUU7QUFDakUsNkRBQXFEO0FBQ3JELDZFQUF5RjtBQUN6RixtRUFBMkQ7QUFDM0QscUVBS3FDO0FBQ3JDLCtEQUFpRztBQUVqRyxNQUFNLFlBQVksR0FBRywwREFBMEQsQ0FBQztBQU9oRixvR0FBb0c7QUFDcEcsK0ZBQStGO0FBQy9GLHdFQUF3RTtBQUN4RSxxR0FBcUc7QUFDckcscUdBQXFHO0FBQ3JHLFNBQVMsY0FBYyxDQUFDLEdBQVk7SUFDbEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN4QixNQUFNLElBQUEsaUNBQWMsRUFBQyw0Q0FBNEMsQ0FBQyxDQUFDO0lBQ3JFLENBQUM7SUFFRCxPQUFPLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztBQUNuQyxDQUFDO0FBRUQsU0FBUyxXQUFXLENBQUMsR0FBWTtJQUMvQixJQUFJLEdBQUcsS0FBSyxTQUFTO1FBQUUsT0FBTyxFQUFFLENBQUM7SUFFakMsSUFBSSxPQUFPLEdBQUcsS0FBSyxRQUFRLElBQUksR0FBRyxLQUFLLElBQUksSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDbEUsTUFBTSxJQUFBLGlDQUFjLEVBQUMsMEJBQTBCLENBQUMsQ0FBQztJQUNuRCxDQUFDO0lBRUQsT0FBTyxHQUE4QixDQUFDO0FBQ3hDLENBQUM7QUFFRCwrRkFBK0Y7QUFDL0YsNEZBQTRGO0FBQzVGLFNB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
@@ -14,8 +14,12 @@ export interface ActionForm {
14
14
  };
15
15
  getLayout(): unknown;
16
16
  }
17
+ export interface Action extends ActionForm {
18
+ setFields(values: Record<string, unknown>): Promise<void>;
19
+ execute(): Promise<unknown>;
20
+ }
17
21
  export interface AgentActionClient {
18
- loadActionForm(collection: string, action: string, recordIds: string[]): Promise<ActionForm>;
22
+ loadAction(collection: string, action: string, recordIds: string[]): Promise<Action>;
19
23
  }
20
24
  export interface AgentActionClientOptions {
21
25
  agentUrl: string;
@@ -20,7 +20,7 @@ function extractRawLayout(action) {
20
20
  function createAgentActionClient({ agentUrl, token, actionEndpoints, }) {
21
21
  const client = (0, agent_client_1.createRemoteAgentClient)({ url: agentUrl, token, actionEndpoints });
22
22
  return {
23
- loadActionForm: (collection, action, recordIds) => client.collection(collection).action(action, { recordIds }),
23
+ loadAction: (collection, action, recordIds) => client.collection(collection).action(action, { recordIds }),
24
24
  };
25
25
  }
26
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtYWN0aW9uLWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hY3Rpb24vYWdlbnQtYWN0aW9uLWNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQW9DQSw0Q0FJQztBQU9ELDBDQVdDO0FBdkRELDREQUFvRTtBQTRCcEUsbUdBQW1HO0FBQ25HLHFHQUFxRztBQUNyRyxnR0FBZ0c7QUFDaEcsa0dBQWtHO0FBQ2xHLDBGQUEwRjtBQUMxRixTQUFnQixnQkFBZ0IsQ0FBQyxNQUFrQjtJQUNqRCxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsU0FBUyxFQUF3RCxDQUFDO0lBRXRGLE9BQU8sSUFBSSxDQUFDLE1BQU0sSUFBSSxFQUFFLENBQUM7QUFDM0IsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxTQUF3Qix1QkFBdUIsQ0FBQyxFQUM5QyxRQUFRLEVBQ1IsS0FBSyxFQUNMLGVBQWUsR0FDVTtJQUN6QixNQUFNLE1BQU0sR0FBRyxJQUFBLHNDQUF1QixFQUFDLEVBQUUsR0FBRyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsZUFBZSxFQUFFLENBQUMsQ0FBQztJQUVsRixPQUFPO1FBQ0wsY0FBYyxFQUFFLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsRUFBRSxDQUNoRCxNQUFNLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsRUFBRSxTQUFTLEVBQUUsQ0FBQztLQUM5RCxDQUFDO0FBQ0osQ0FBQyJ9
26
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtYWN0aW9uLWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hY3Rpb24vYWdlbnQtYWN0aW9uLWNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQTJDQSw0Q0FJQztBQU9ELDBDQVdDO0FBOURELDREQUFvRTtBQW1DcEUsbUdBQW1HO0FBQ25HLHFHQUFxRztBQUNyRyxnR0FBZ0c7QUFDaEcsa0dBQWtHO0FBQ2xHLDBGQUEwRjtBQUMxRixTQUFnQixnQkFBZ0IsQ0FBQyxNQUFrQjtJQUNqRCxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsU0FBUyxFQUF3RCxDQUFDO0lBRXRGLE9BQU8sSUFBSSxDQUFDLE1BQU0sSUFBSSxFQUFFLENBQUM7QUFDM0IsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxTQUF3Qix1QkFBdUIsQ0FBQyxFQUM5QyxRQUFRLEVBQ1IsS0FBSyxFQUNMLGVBQWUsR0FDVTtJQUN6QixNQUFNLE1BQU0sR0FBRyxJQUFBLHNDQUF1QixFQUFDLEVBQUUsR0FBRyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsZUFBZSxFQUFFLENBQUMsQ0FBQztJQUVsRixPQUFPO1FBQ0wsVUFBVSxFQUFFLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsRUFBRSxDQUM1QyxNQUFNLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsRUFBRSxTQUFTLEVBQUUsQ0FBQztLQUM5RCxDQUFDO0FBQ0osQ0FBQyJ9
@@ -9,6 +9,8 @@ const agent_query_1 = require("./agent-query");
9
9
  const response_mappers_1 = require("./response-mappers");
10
10
  const agent_route_helpers_1 = require("../http/agent-route-helpers");
11
11
  const bff_local_errors_1 = require("../http/bff-local-errors");
12
+ const agent_capabilities_fetcher_1 = __importDefault(require("../read-model/agent-capabilities-fetcher"));
13
+ const capabilities_validator_1 = require("../validation/capabilities-validator");
12
14
  const relation_field_guard_1 = __importDefault(require("../validation/relation-field-guard"));
13
15
  const DATA_ROUTE = /^\/agent\/v1\/([^/]+)\/(list|count)$/;
14
16
  const RELATION_ROUTE = /^\/agent\/v1\/([^/]+)\/relations\/([^/]+)\/(list|count)$/;
@@ -21,15 +23,47 @@ function resolveForeignCollection(target) {
21
23
  return null;
22
24
  return target.target;
23
25
  }
26
+ // The route's allow-list check ran against the read-model captured before this request awaited the
27
+ // capabilities; a refresh in that window can drop the collection, so it is re-checked against the
28
+ // generation the capabilities belong to. The agent stays the authority (it asserts canBrowse itself)
29
+ // — this keeps the BFF from serving a collection its own current schema no longer exposes.
30
+ function assertCollectionStillAllowed(readModel, collection) {
31
+ if (!readModel.isCollectionAllowed(collection))
32
+ throw (0, bff_local_errors_1.unknownCollection)();
33
+ }
34
+ function resolveCapabilities(deps) {
35
+ return (0, agent_route_helpers_1.callAgent)(() => deps.store.getCapabilities(deps.collection, (0, agent_capabilities_fetcher_1.default)({ agentUrl: deps.agentUrl, token: deps.token })), deps.logger);
36
+ }
24
37
  async function handleList(ctx, body, deps) {
25
38
  (0, relation_field_guard_1.default)((0, agent_query_1.collectListFieldPaths)(body));
39
+ const validationInput = {
40
+ filter: body.filter,
41
+ sortFields: body.sort?.map(clause => clause.field),
42
+ projectionFields: body.projection,
43
+ };
44
+ // The store hands back the read-model the capabilities belong to; using it keeps the primary keys
45
+ // serialized below from a different schema generation than the fields just validated, and re-checks
46
+ // the allow-list in case that generation dropped the collection.
47
+ let { primaryKeys } = deps;
48
+ if ((0, capabilities_validator_1.hasCapabilityConstrainedInput)(validationInput)) {
49
+ const { capabilities, readModel } = await resolveCapabilities(deps);
50
+ assertCollectionStillAllowed(readModel, deps.collection);
51
+ (0, capabilities_validator_1.assertValidAgainstCapabilities)(validationInput, capabilities);
52
+ primaryKeys = readModel.getPrimaryKeys(deps.collection);
53
+ }
26
54
  const query = (0, agent_query_1.buildListAgentQuery)(deps.collection, deps.timezone, body);
27
55
  const records = await (0, agent_route_helpers_1.callAgent)(() => deps.client.list(deps.collection, query), deps.logger);
28
56
  ctx.status = 200;
29
- ctx.body = (0, response_mappers_1.mapListResponse)(deps.collection, records, deps.primaryKeys);
57
+ ctx.body = (0, response_mappers_1.mapListResponse)(deps.collection, records, primaryKeys);
30
58
  }
31
59
  async function handleCount(ctx, body, deps) {
32
60
  (0, relation_field_guard_1.default)((0, agent_query_1.collectCountFieldPaths)(body));
61
+ // Count carries only a filter (no sort/projection), so that is all there is to validate.
62
+ if (body.filter !== undefined) {
63
+ const { capabilities, readModel } = await resolveCapabilities(deps);
64
+ assertCollectionStillAllowed(readModel, deps.collection);
65
+ (0, capabilities_validator_1.assertValidAgainstCapabilities)({ filter: body.filter }, capabilities);
66
+ }
33
67
  const query = (0, agent_query_1.buildCountAgentQuery)(deps.timezone, body);
34
68
  const raw = await (0, agent_route_helpers_1.callAgent)(() => deps.client.countRaw(deps.collection, query), deps.logger);
35
69
  ctx.status = 200;
@@ -102,6 +136,9 @@ function createDataRoutesMiddleware({ store, agentUrl, logger, createClient = ag
102
136
  const deps = {
103
137
  collection,
104
138
  client: createClient({ agentUrl, token }),
139
+ store,
140
+ agentUrl,
141
+ token,
105
142
  timezone: ctx.state.timezone,
106
143
  logger,
107
144
  };
@@ -121,4 +158,4 @@ function createDataRoutesMiddleware({ store, agentUrl, logger, createClient = ag
121
158
  }
122
159
  };
123
160
  }
124
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YS1yb3V0ZXMtbWlkZGxld2FyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kYXRhL2RhdGEtcm91dGVzLW1pZGRsZXdhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUF3S0EsNkNBcURDO0FBaE5ELDRFQUErRDtBQUMvRCwrQ0FTdUI7QUFDdkIseURBQXVFO0FBQ3ZFLHFFQUtxQztBQUNyQywrREFBOEU7QUFDOUUsOEZBQTRFO0FBRTVFLE1BQU0sVUFBVSxHQUFHLHNDQUFzQyxDQUFDO0FBQzFELE1BQU0sY0FBYyxHQUFHLDBEQUEwRCxDQUFDO0FBRWxGLG9HQUFvRztBQUNwRyxxR0FBcUc7QUFDckcsU0FBUyx3QkFBd0IsQ0FBQyxNQUFrQztJQUNsRSxJQUFJLENBQUMsTUFBTSxJQUFJLENBQUMsQ0FBQyxRQUFRLElBQUksTUFBTSxDQUFDO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDbEQsSUFBSSxNQUFNLENBQUMsSUFBSSxLQUFLLFNBQVMsSUFBSSxNQUFNLENBQUMsSUFBSSxLQUFLLGVBQWU7UUFBRSxPQUFPLElBQUksQ0FBQztJQUU5RSxPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUM7QUFDdkIsQ0FBQztBQWtCRCxLQUFLLFVBQVUsVUFBVSxDQUFDLEdBQVksRUFBRSxJQUFxQixFQUFFLElBQXFCO0lBQ2xGLElBQUEsOEJBQTBCLEVBQUMsSUFBQSxtQ0FBcUIsRUFBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBRXhELE1BQU0sS0FBSyxHQUFHLElBQUEsaUNBQW1CLEVBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3hFLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBQSwrQkFBUyxFQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLEVBQUUsSUFBSSxDQUFD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
161
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YS1yb3V0ZXMtbWlkZGxld2FyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kYXRhL2RhdGEtcm91dGVzLW1pZGRsZXdhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUErTkEsNkNBd0RDO0FBelFELDRFQUErRDtBQUMvRCwrQ0FTdUI7QUFDdkIseURBQXVFO0FBQ3ZFLHFFQUtxQztBQUNyQywrREFBOEU7QUFDOUUsMEdBQXNGO0FBQ3RGLGlGQUc4QztBQUM5Qyw4RkFBNEU7QUFFNUUsTUFBTSxVQUFVLEdBQUcsc0NBQXNDLENBQUM7QUFDMUQsTUFBTSxjQUFjLEdBQUcsMERBQTBELENBQUM7QUFFbEYsb0dBQW9HO0FBQ3BHLHFHQUFxRztBQUNyRyxTQUFTLHdCQUF3QixDQUFDLE1BQWtDO0lBQ2xFLElBQUksQ0FBQyxNQUFNLElBQUksQ0FBQyxDQUFDLFFBQVEsSUFBSSxNQUFNLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUNsRCxJQUFJLE1BQU0sQ0FBQyxJQUFJLEtBQUssU0FBUyxJQUFJLE1BQU0sQ0FBQyxJQUFJLEtBQUssZUFBZTtRQUFFLE9BQU8sSUFBSSxDQUFDO0lBRTlFLE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQztBQUN2QixDQUFDO0FBcUJELG1HQUFtRztBQUNuRyxrR0FBa0c7QUFDbEcscUdBQXFHO0FBQ3JHLDJGQUEyRjtBQUMzRixTQUFTLDRCQUE0QixDQUFDLFNBQW9CLEVBQUUsVUFBa0I7SUFDNUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxtQkFBbUIsQ0FBQyxVQUFVLENBQUM7UUFBRSxNQUFNLElBQUEsb0NBQWlCLEdBQUUsQ0FBQztBQUM1RSxDQUFDO0FBRUQsU0FBUyxtQkFBbUIsQ0FDMUIsSUFBd0I7SUFFeEIsT0FBTyxJQUFBLCtCQUFTLEVBQ2QsR0FBRyxFQUFFLENBQ0gsSUFBSSxDQUFDLEtBQUssQ0FBQyxlQUFlLENBQ3hCLElBQUksQ0FBQyxVQUFVLEVBQ2YsSUFBQSxv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
@@ -9,4 +9,5 @@ export declare function invalidRequest(message?: string, details?: unknown): Bff
9
9
  export declare function mappingError(message?: string): BffHttpError;
10
10
  export declare function schemaUnavailable(message?: string): BffHttpError;
11
11
  export declare function unsupportedActionResult(message?: string): BffHttpError;
12
+ export declare function actionRequiresApproval(message?: string, details?: unknown): BffHttpError;
12
13
  //# sourceMappingURL=bff-local-errors.d.ts.map
@@ -10,6 +10,7 @@ exports.invalidRequest = invalidRequest;
10
10
  exports.mappingError = mappingError;
11
11
  exports.schemaUnavailable = schemaUnavailable;
12
12
  exports.unsupportedActionResult = unsupportedActionResult;
13
+ exports.actionRequiresApproval = actionRequiresApproval;
13
14
  const bff_http_error_1 = require("./bff-http-error");
14
15
  function unknownCollection(message = 'Unknown collection') {
15
16
  return new bff_http_error_1.BffHttpError(404, 'unknown_collection', message);
@@ -41,4 +42,7 @@ function schemaUnavailable(message = 'The agent schema is unavailable') {
41
42
  function unsupportedActionResult(message = 'Unsupported action result') {
42
43
  return new bff_http_error_1.BffHttpError(501, 'unsupported_action_result', message);
43
44
  }
44
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmZmLWxvY2FsLWVycm9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9odHRwL2JmZi1sb2NhbC1lcnJvcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFFQSw4Q0FFQztBQUVELDBDQUVDO0FBRUQsc0NBRUM7QUFFRCxvREFFQztBQUVELGdEQUVDO0FBRUQsNENBRUM7QUFFRCx3Q0FFQztBQUVELG9DQUVDO0FBRUQsOENBRUM7QUFFRCwwREFFQztBQXhDRCxxREFBZ0Q7QUFFaEQsU0FBZ0IsaUJBQWlCLENBQUMsT0FBTyxHQUFHLG9CQUFvQjtJQUM5RCxPQUFPLElBQUksNkJBQVksQ0FBQyxHQUFHLEVBQUUsb0JBQW9CLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDOUQsQ0FBQztBQUVELFNBQWdCLGVBQWUsQ0FBQyxPQUFPLEdBQUcsa0JBQWtCO0lBQzFELE9BQU8sSUFBSSw2QkFBWSxDQUFDLEdBQUcsRUFBRSxrQkFBa0IsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUM1RCxDQUFDO0FBRUQsU0FBZ0IsYUFBYSxDQUFDLE9BQU8sR0FBRyxnQkFBZ0I7SUFDdEQsT0FBTyxJQUFJLDZCQUFZLENBQUMsR0FBRyxFQUFFLGdCQUFnQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQzFELENBQUM7QUFFRCxTQUFnQixvQkFBb0IsQ0FBQyxPQUFPLEdBQUcsMkJBQTJCO0lBQ3hFLE9BQU8sSUFBSSw2QkFBWSxDQUFDLEdBQUcsRUFBRSx3QkFBd0IsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUNsRSxDQUFDO0FBRUQsU0FBZ0Isa0JBQWtCLENBQUMsT0FBTyxHQUFHLHlCQUF5QjtJQUNwRSxPQUFPLElBQUksNkJBQVksQ0FBQyxHQUFHLEVBQUUsc0JBQXNCLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDaEUsQ0FBQztBQUVELFNBQWdCLGdCQUFnQixDQUFDLE9BQU8sR0FBRyx1QkFBdUI7SUFDaEUsT0FBTyxJQUFJLDZCQUFZLENBQUMsR0FBRyxFQUFFLG9CQUFvQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQzlELENBQUM7QUFFRCxTQUFnQixjQUFjLENBQUMsT0FBTyxHQUFHLGlCQUFpQixFQUFFLE9BQWlCO0lBQzNFLE9BQU8sSUFBSSw2QkFBWSxDQUFDLEdBQUcsRUFBRSxpQkFBaUIsRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDcEUsQ0FBQztBQUVELFNBQWdCLFlBQVksQ0FBQyxPQUFPLEdBQUcsa0NBQWtDO0lBQ3ZFLE9BQU8sSUFBSSw2QkFBWSxDQUFDLEdBQUcsRUFBRSxlQUFlLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDekQsQ0FBQztBQUVELFNBQWdCLGlCQUFpQixDQUFDLE9BQU8sR0FBRyxpQ0FBaUM7SUFDM0UsT0FBTyxJQUFJLDZCQUFZLENBQUMsR0FBRyxFQUFFLG9CQUFvQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQzlELENBQUM7QUFFRCxTQUFnQix1QkFBdUIsQ0FBQyxPQUFPLEdBQUcsMkJBQTJCO0lBQzNFLE9BQU8sSUFBSSw2QkFBWSxDQUFDLEdBQUcsRUFBRSwyQkFBMkIsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUNyRSxDQUFDIn0=
45
+ function actionRequiresApproval(message = 'This action requires an approval before it can run', details) {
46
+ return new bff_http_error_1.BffHttpError(403, 'action_requires_approval', message, details);
47
+ }
48
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmZmLWxvY2FsLWVycm9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9odHRwL2JmZi1sb2NhbC1lcnJvcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFFQSw4Q0FFQztBQUVELDBDQUVDO0FBRUQsc0NBRUM7QUFFRCxvREFFQztBQUVELGdEQUVDO0FBRUQsNENBRUM7QUFFRCx3Q0FFQztBQUVELG9DQUVDO0FBRUQsOENBRUM7QUFFRCwwREFFQztBQUVELHdEQUtDO0FBL0NELHFEQUFnRDtBQUVoRCxTQUFnQixpQkFBaUIsQ0FBQyxPQUFPLEdBQUcsb0JBQW9CO0lBQzlELE9BQU8sSUFBSSw2QkFBWSxDQUFDLEdBQUcsRUFBRSxvQkFBb0IsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUM5RCxDQUFDO0FBRUQsU0FBZ0IsZUFBZSxDQUFDLE9BQU8sR0FBRyxrQkFBa0I7SUFDMUQsT0FBTyxJQUFJLDZCQUFZLENBQUMsR0FBRyxFQUFFLGtCQUFrQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQzVELENBQUM7QUFFRCxTQUFnQixhQUFhLENBQUMsT0FBTyxHQUFHLGdCQUFnQjtJQUN0RCxPQUFPLElBQUksNkJBQVksQ0FBQyxHQUFHLEVBQUUsZ0JBQWdCLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDMUQsQ0FBQztBQUVELFNBQWdCLG9CQUFvQixDQUFDLE9BQU8sR0FBRywyQkFBMkI7SUFDeEUsT0FBTyxJQUFJLDZCQUFZLENBQUMsR0FBRyxFQUFFLHdCQUF3QixFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQ2xFLENBQUM7QUFFRCxTQUFnQixrQkFBa0IsQ0FBQyxPQUFPLEdBQUcseUJBQXlCO0lBQ3BFLE9BQU8sSUFBSSw2QkFBWSxDQUFDLEdBQUcsRUFBRSxzQkFBc0IsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUNoRSxDQUFDO0FBRUQsU0FBZ0IsZ0JBQWdCLENBQUMsT0FBTyxHQUFHLHVCQUF1QjtJQUNoRSxPQUFPLElBQUksNkJBQVksQ0FBQyxHQUFHLEVBQUUsb0JBQW9CLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDOUQsQ0FBQztBQUVELFNBQWdCLGNBQWMsQ0FBQyxPQUFPLEdBQUcsaUJBQWlCLEVBQUUsT0FBaUI7SUFDM0UsT0FBTyxJQUFJLDZCQUFZLENBQUMsR0FBRyxFQUFFLGlCQUFpQixFQUFFLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQztBQUNwRSxDQUFDO0FBRUQsU0FBZ0IsWUFBWSxDQUFDLE9BQU8sR0FBRyxrQ0FBa0M7SUFDdkUsT0FBTyxJQUFJLDZCQUFZLENBQUMsR0FBRyxFQUFFLGVBQWUsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUN6RCxDQUFDO0FBRUQsU0FBZ0IsaUJBQWlCLENBQUMsT0FBTyxHQUFHLGlDQUFpQztJQUMzRSxPQUFPLElBQUksNkJBQVksQ0FBQyxHQUFHLEVBQUUsb0JBQW9CLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDOUQsQ0FBQztBQUVELFNBQWdCLHVCQUF1QixDQUFDLE9BQU8sR0FBRywyQkFBMkI7SUFDM0UsT0FBTyxJQUFJLDZCQUFZLENBQUMsR0FBRyxFQUFFLDJCQUEyQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQ3JFLENBQUM7QUFFRCxTQUFnQixzQkFBc0IsQ0FDcEMsT0FBTyxHQUFHLG9EQUFvRCxFQUM5RCxPQUFpQjtJQUVqQixPQUFPLElBQUksNkJBQVksQ0FBQyxHQUFHLEVBQUUsMEJBQTBCLEVBQUUsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQzdFLENBQUMifQ==
@@ -2,7 +2,7 @@ export interface CapabilitiesResult {
2
2
  fields: {
3
3
  name: string;
4
4
  type: string;
5
- operators: string[];
5
+ operators?: string[];
6
6
  }[];
7
7
  }
8
8
  export type CapabilitiesFetcher = (collection: string) => Promise<CapabilitiesResult>;
@@ -14,7 +14,15 @@ export default class ReadModelStore {
14
14
  private readModel;
15
15
  constructor(schemaCache: SchemaCache, capabilitiesCache: CapabilitiesCache);
16
16
  getReadModel(): Promise<ReadModel>;
17
- getCapabilities(collection: string, fetcher: CapabilitiesFetcher): Promise<CapabilitiesResult>;
17
+ /**
18
+ * Returns the capabilities together with the read-model they belong to, so a caller cannot mix a
19
+ * read-model captured earlier (allow-list, primary keys) with capabilities from a newer schema
20
+ * generation. Callers must use the returned `readModel`, not the one they held before this call.
21
+ */
22
+ getCapabilities(collection: string, fetcher: CapabilitiesFetcher, attemptsLeft?: number): Promise<{
23
+ capabilities: CapabilitiesResult;
24
+ readModel: ReadModel;
25
+ }>;
18
26
  ageSeconds(): number | undefined;
19
27
  }
20
28
  //# sourceMappingURL=read-model-store.d.ts.map
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const read_model_1 = __importDefault(require("./read-model"));
7
+ const MAX_GENERATION_RETRIES = 3;
7
8
  /**
8
9
  * Single owner of the coupled schema + capabilities lifecycle. A successful schema refresh (a bump
9
10
  * of the cache `revision`) rebuilds the read-model and clears capabilities atomically, so the
@@ -25,19 +26,33 @@ class ReadModelStore {
25
26
  }
26
27
  return this.readModel;
27
28
  }
28
- async getCapabilities(collection, fetcher) {
29
+ /**
30
+ * Returns the capabilities together with the read-model they belong to, so a caller cannot mix a
31
+ * read-model captured earlier (allow-list, primary keys) with capabilities from a newer schema
32
+ * generation. Callers must use the returned `readModel`, not the one they held before this call.
33
+ */
34
+ async getCapabilities(collection, fetcher, attemptsLeft = MAX_GENERATION_RETRIES) {
35
+ if (attemptsLeft <= 0) {
36
+ throw new Error(`Schema generation kept changing while reading capabilities for "${collection}"`);
37
+ }
29
38
  // Ensure any pending schema refresh (and its capabilities invalidation) runs first.
30
- await this.getReadModel();
31
- // TODO(wiring): possible TOCTOU once this is called from request handling. A concurrent schema
32
- // refresh can clear capabilities while this fetch is in flight, so the caller could receive
33
- // capabilities from the previous schema generation alongside the new allow-list. When wiring
34
- // the data endpoints, re-check `schemaCache.revision` after the fetch resolves and retry on a
35
- // mismatch so capabilities and schema stay atomically coupled.
36
- return this.capabilitiesCache.get(collection, fetcher);
39
+ const readModel = await this.getReadModel();
40
+ const { revision } = this.schemaCache;
41
+ const capabilities = await this.capabilitiesCache.get(collection, fetcher);
42
+ // A refresh can land while the fetch is in flight: the cache drops the stale write but still
43
+ // resolves with it, so both reads are retried until they observe one generation. The revision is
44
+ // the discriminator, not the read-model identity — `SchemaCache` bumps the revision inside its
45
+ // refresh, before an awaiting `getReadModel` caller resumes and installs the new model, so an
46
+ // identity comparison still sees the old object in that window. Bounded because a refresh is
47
+ // driven by the 24h schema TTL, not by request volume.
48
+ if (this.schemaCache.revision !== revision) {
49
+ return this.getCapabilities(collection, fetcher, attemptsLeft - 1);
50
+ }
51
+ return { capabilities, readModel };
37
52
  }
38
53
  ageSeconds() {
39
54
  return this.schemaCache.ageSeconds();
40
55
  }
41
56
  }
42
57
  exports.default = ReadModelStore;
43
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhZC1tb2RlbC1zdG9yZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9yZWFkLW1vZGVsL3JlYWQtbW9kZWwtc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFJQSw4REFBcUM7QUFFckM7Ozs7R0FJRztBQUNILE1BQXFCLGNBQWM7SUFPakMsWUFBWSxXQUF3QixFQUFFLGlCQUFvQztRQUhsRSxrQkFBYSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ25CLGNBQVMsR0FBcUIsSUFBSSxDQUFDO1FBR3pDLElBQUksQ0FBQyxXQUFXLEdBQUcsV0FBVyxDQUFDO1FBQy9CLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxpQkFBaUIsQ0FBQztJQUM3QyxDQUFDO0lBRUQsS0FBSyxDQUFDLFlBQVk7UUFDaEIsTUFBTSxXQUFXLEdBQUcsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBRWpELElBQUksSUFBSSxDQUFDLFdBQVcsQ0FBQyxRQUFRLEtBQUssSUFBSSxDQUFDLGFBQWEsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUN4RSxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksb0JBQVMsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUM1QyxJQUFJLENBQUMsYUFBYSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDO1lBQy9DLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNqQyxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDO0lBQ3hCLENBQUM7SUFFRCxLQUFLLENBQUMsZUFBZSxDQUNuQixVQUFrQixFQUNsQixPQUE0QjtRQUU1QixvRkFBb0Y7UUFDcEYsTUFBTSxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFFMUIsK0ZBQStGO1FBQy9GLDRGQUE0RjtRQUM1Riw2RkFBNkY7UUFDN0YsOEZBQThGO1FBQzlGLCtEQUErRDtRQUMvRCxPQUFPLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3pELENBQUM7SUFFRCxVQUFVO1FBQ1IsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxDQUFDO0lBQ3ZDLENBQUM7Q0FDRjtBQTFDRCxpQ0EwQ0MifQ==
58
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhZC1tb2RlbC1zdG9yZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9yZWFkLW1vZGVsL3JlYWQtbW9kZWwtc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFJQSw4REFBcUM7QUFFckMsTUFBTSxzQkFBc0IsR0FBRyxDQUFDLENBQUM7QUFFakM7Ozs7R0FJRztBQUNILE1BQXFCLGNBQWM7SUFPakMsWUFBWSxXQUF3QixFQUFFLGlCQUFvQztRQUhsRSxrQkFBYSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ25CLGNBQVMsR0FBcUIsSUFBSSxDQUFDO1FBR3pDLElBQUksQ0FBQyxXQUFXLEdBQUcsV0FBVyxDQUFDO1FBQy9CLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxpQkFBaUIsQ0FBQztJQUM3QyxDQUFDO0lBRUQsS0FBSyxDQUFDLFlBQVk7UUFDaEIsTUFBTSxXQUFXLEdBQUcsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBRWpELElBQUksSUFBSSxDQUFDLFdBQVcsQ0FBQyxRQUFRLEtBQUssSUFBSSxDQUFDLGFBQWEsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUN4RSxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksb0JBQVMsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUM1QyxJQUFJLENBQUMsYUFBYSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDO1lBQy9DLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNqQyxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDO0lBQ3hCLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsS0FBSyxDQUFDLGVBQWUsQ0FDbkIsVUFBa0IsRUFDbEIsT0FBNEIsRUFDNUIsWUFBWSxHQUFHLHNCQUFzQjtRQUVyQyxJQUFJLFlBQVksSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUN0QixNQUFNLElBQUksS0FBSyxDQUNiLG1FQUFtRSxVQUFVLEdBQUcsQ0FDakYsQ0FBQztRQUNKLENBQUM7UUFFRCxvRkFBb0Y7UUFDcEYsTUFBTSxTQUFTLEdBQUcsTUFBTSxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDNUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUM7UUFDdEMsTUFBTSxZQUFZLEdBQUcsTUFBTSxJQUFJLENBQUMsaUJBQWlCLENBQUMsR0FBRyxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUUzRSw2RkFBNkY7UUFDN0YsaUdBQWlHO1FBQ2pHLCtGQUErRjtRQUMvRiw4RkFBOEY7UUFDOUYsNkZBQTZGO1FBQzdGLHVEQUF1RDtRQUN2RCxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQzNDLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQyxVQUFVLEVBQUUsT0FBTyxFQUFFLFlBQVksR0FBRyxDQUFDLENBQUMsQ0FBQztRQUNyRSxDQUFDO1FBRUQsT0FBTyxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsQ0FBQztJQUNyQyxDQUFDO0lBRUQsVUFBVTtRQUNSLE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsQ0FBQztJQUN2QyxDQUFDO0NBQ0Y7QUE3REQsaUNBNkRDIn0=
@@ -0,0 +1,22 @@
1
+ import type { BffHttpError } from '../http/bff-http-error';
2
+ import type { CapabilitiesResult } from '../read-model/capabilities-cache';
3
+ export interface ValidateParams {
4
+ filter?: unknown;
5
+ sortFields?: string[];
6
+ projectionFields?: string[];
7
+ }
8
+ export declare const MAX_FILTER_DEPTH = 100;
9
+ /**
10
+ * True when the request carries something capabilities can invalidate. Callers use it to skip the
11
+ * capabilities fetch entirely, so a plain list/count still succeeds while that fetch is unavailable.
12
+ */
13
+ export declare function hasCapabilityConstrainedInput(params: ValidateParams): boolean;
14
+ /**
15
+ * Validates a request's filter, sort, and projection fields against the target collection's
16
+ * capabilities. Returns every offending field as a structured error (empty = valid); the caller
17
+ * surfaces the whole list or just the first. Throws a 500 mapping_error when a capabilities
18
+ * operator has no canonical mapping (a version skew between agent and BFF).
19
+ */
20
+ export declare function validateAgainstCapabilities(params: ValidateParams, capabilities: CapabilitiesResult): BffHttpError[];
21
+ export declare function assertValidAgainstCapabilities(params: ValidateParams, capabilities: CapabilitiesResult): void;
22
+ //# sourceMappingURL=capabilities-validator.d.ts.map
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_FILTER_DEPTH = void 0;
4
+ exports.hasCapabilityConstrainedInput = hasCapabilityConstrainedInput;
5
+ exports.validateAgainstCapabilities = validateAgainstCapabilities;
6
+ exports.assertValidAgainstCapabilities = assertValidAgainstCapabilities;
7
+ const operator_normalizer_1 = require("./operator-normalizer");
8
+ const validation_errors_1 = require("./validation-errors");
9
+ const bff_local_errors_1 = require("../http/bff-local-errors");
10
+ function isBranch(node) {
11
+ return (typeof node === 'object' &&
12
+ node !== null &&
13
+ Array.isArray(node.conditions));
14
+ }
15
+ function isLeaf(node) {
16
+ return (typeof node === 'object' &&
17
+ node !== null &&
18
+ typeof node.field === 'string');
19
+ }
20
+ exports.MAX_FILTER_DEPTH = 100;
21
+ function collectLeaves(node, acc, depth = 0) {
22
+ if (depth > exports.MAX_FILTER_DEPTH)
23
+ throw (0, validation_errors_1.filterTooDeep)(exports.MAX_FILTER_DEPTH);
24
+ if (isBranch(node)) {
25
+ node.conditions.forEach(condition => collectLeaves(condition, acc, depth + 1));
26
+ }
27
+ else if (isLeaf(node)) {
28
+ const { operator } = node;
29
+ acc.push({ field: node.field, operator: typeof operator === 'string' ? operator : undefined });
30
+ }
31
+ }
32
+ function indexOperators(capabilities) {
33
+ const index = new Map();
34
+ capabilities.fields.forEach(field => index.set(field.name, field.operators ?? []));
35
+ return index;
36
+ }
37
+ function normalizeFieldOperators(field, operators) {
38
+ return operators.map(operator => {
39
+ const normalized = (0, operator_normalizer_1.normalizeOperator)(operator);
40
+ if (!normalized) {
41
+ throw (0, bff_local_errors_1.mappingError)(`Capabilities operator "${operator}" on field "${field}" has no canonical mapping`);
42
+ }
43
+ return normalized;
44
+ });
45
+ }
46
+ function validateFilter(filter, index) {
47
+ const leaves = [];
48
+ collectLeaves(filter, leaves);
49
+ const errors = [];
50
+ for (const leaf of leaves) {
51
+ const operators = index.get(leaf.field);
52
+ if (operators === undefined) {
53
+ errors.push((0, validation_errors_1.unknownField)(leaf.field));
54
+ }
55
+ else if (operators.length === 0) {
56
+ errors.push((0, validation_errors_1.fieldNotFilterable)(leaf.field));
57
+ }
58
+ else {
59
+ const normalized = normalizeFieldOperators(leaf.field, operators);
60
+ if (leaf.operator === undefined || !normalized.includes(leaf.operator)) {
61
+ errors.push((0, validation_errors_1.invalidFilterOperator)(leaf.field, normalized));
62
+ }
63
+ }
64
+ }
65
+ return errors;
66
+ }
67
+ function validateExistence(fields, index) {
68
+ return fields.filter(field => !index.has(field)).map(validation_errors_1.unknownField);
69
+ }
70
+ function dedupe(errors) {
71
+ const seen = new Set();
72
+ const result = [];
73
+ for (const error of errors) {
74
+ const key = `${error.type}:${error.details.field}`;
75
+ if (!seen.has(key)) {
76
+ seen.add(key);
77
+ result.push(error);
78
+ }
79
+ }
80
+ return result;
81
+ }
82
+ /**
83
+ * True when the request carries something capabilities can invalidate. Callers use it to skip the
84
+ * capabilities fetch entirely, so a plain list/count still succeeds while that fetch is unavailable.
85
+ */
86
+ function hasCapabilityConstrainedInput(params) {
87
+ return (params.filter !== undefined ||
88
+ (params.sortFields?.length ?? 0) > 0 ||
89
+ (params.projectionFields?.length ?? 0) > 0);
90
+ }
91
+ /**
92
+ * Validates a request's filter, sort, and projection fields against the target collection's
93
+ * capabilities. Returns every offending field as a structured error (empty = valid); the caller
94
+ * surfaces the whole list or just the first. Throws a 500 mapping_error when a capabilities
95
+ * operator has no canonical mapping (a version skew between agent and BFF).
96
+ */
97
+ function validateAgainstCapabilities(params, capabilities) {
98
+ const index = indexOperators(capabilities);
99
+ return dedupe([
100
+ ...validateFilter(params.filter, index),
101
+ ...validateExistence(params.sortFields ?? [], index),
102
+ ...validateExistence(params.projectionFields ?? [], index),
103
+ ]);
104
+ }
105
+ function assertValidAgainstCapabilities(params, capabilities) {
106
+ const [first] = validateAgainstCapabilities(params, capabilities);
107
+ if (first)
108
+ throw first;
109
+ }
110
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FwYWJpbGl0aWVzLXZhbGlkYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy92YWxpZGF0aW9uL2NhcGFiaWxpdGllcy12YWxpZGF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBMEhBLHNFQU1DO0FBUUQsa0VBV0M7QUFFRCx3RUFPQztBQXpKRCwrREFBMEQ7QUFDMUQsMkRBSzZCO0FBQzdCLCtEQUF3RDtBQWF4RCxTQUFTLFFBQVEsQ0FBQyxJQUFhO0lBQzdCLE9BQU8sQ0FDTCxPQUFPLElBQUksS0FBSyxRQUFRO1FBQ3hCLElBQUksS0FBSyxJQUFJO1FBQ2IsS0FBSyxDQUFDLE9BQU8sQ0FBRSxJQUFpQyxDQUFDLFVBQVUsQ0FBQyxDQUM3RCxDQUFDO0FBQ0osQ0FBQztBQUVELFNBQVMsTUFBTSxDQUFDLElBQWE7SUFDM0IsT0FBTyxDQUNMLE9BQU8sSUFBSSxLQUFLLFFBQVE7UUFDeEIsSUFBSSxLQUFLLElBQUk7UUFDYixPQUFRLElBQTRCLENBQUMsS0FBSyxLQUFLLFFBQVEsQ0FDeEQsQ0FBQztBQUNKLENBQUM7QUFFWSxRQUFBLGdCQUFnQixHQUFHLEdBQUcsQ0FBQztBQUVwQyxTQUFTLGFBQWEsQ0FBQyxJQUFhLEVBQUUsR0FBaUIsRUFBRSxLQUFLLEdBQUcsQ0FBQztJQUNoRSxJQUFJLEtBQUssR0FBRyx3QkFBZ0I7UUFBRSxNQUFNLElBQUEsaUNBQWEsRUFBQyx3QkFBZ0IsQ0FBQyxDQUFDO0lBRXBFLElBQUksUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7UUFDbkIsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxhQUFhLENBQUMsU0FBUyxFQUFFLEdBQUcsRUFBRSxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNqRixDQUFDO1NBQU0sSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztRQUN4QixNQUFNLEVBQUUsUUFB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
@@ -0,0 +1,14 @@
1
+ import type { Operator } from '@forestadmin/datasource-toolkit';
2
+ /**
3
+ * Mirrors the agent's capabilities serialization (`packages/agent/src/routes/capabilities.ts`),
4
+ * which converts each PascalCase operator to snake_case before returning it. Kept identical so the
5
+ * inverse map below round-trips every operator the agent can emit.
6
+ */
7
+ export declare function toSnakeCaseOperator(operator: string): string;
8
+ /**
9
+ * Maps an agent snake_case capabilities operator to the canonical PascalCase operator from
10
+ * datasource-toolkit `allOperators`. Returns undefined when no canonical operator matches, which
11
+ * only happens when the agent runs a newer operator set than this package (a version skew).
12
+ */
13
+ export declare function normalizeOperator(snakeCaseOperator: string): Operator | undefined;
14
+ //# sourceMappingURL=operator-normalizer.d.ts.map
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toSnakeCaseOperator = toSnakeCaseOperator;
4
+ exports.normalizeOperator = normalizeOperator;
5
+ const datasource_toolkit_1 = require("@forestadmin/datasource-toolkit");
6
+ /**
7
+ * Mirrors the agent's capabilities serialization (`packages/agent/src/routes/capabilities.ts`),
8
+ * which converts each PascalCase operator to snake_case before returning it. Kept identical so the
9
+ * inverse map below round-trips every operator the agent can emit.
10
+ */
11
+ function toSnakeCaseOperator(operator) {
12
+ return operator
13
+ .split(/\.?(?=[A-Z])/)
14
+ .join('_')
15
+ .toLowerCase();
16
+ }
17
+ const SNAKE_TO_PASCAL = new Map(datasource_toolkit_1.allOperators.map(operator => [toSnakeCaseOperator(operator), operator]));
18
+ /**
19
+ * Maps an agent snake_case capabilities operator to the canonical PascalCase operator from
20
+ * datasource-toolkit `allOperators`. Returns undefined when no canonical operator matches, which
21
+ * only happens when the agent runs a newer operator set than this package (a version skew).
22
+ */
23
+ function normalizeOperator(snakeCaseOperator) {
24
+ return SNAKE_TO_PASCAL.get(snakeCaseOperator);
25
+ }
26
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlcmF0b3Itbm9ybWFsaXplci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy92YWxpZGF0aW9uL29wZXJhdG9yLW5vcm1hbGl6ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFTQSxrREFLQztBQVdELDhDQUVDO0FBekJELHdFQUErRDtBQUUvRDs7OztHQUlHO0FBQ0gsU0FBZ0IsbUJBQW1CLENBQUMsUUFBZ0I7SUFDbEQsT0FBTyxRQUFRO1NBQ1osS0FBSyxDQUFDLGNBQWMsQ0FBQztTQUNyQixJQUFJLENBQUMsR0FBRyxDQUFDO1NBQ1QsV0FBVyxFQUFFLENBQUM7QUFDbkIsQ0FBQztBQUVELE1BQU0sZUFBZSxHQUFHLElBQUksR0FBRyxDQUM3QixpQ0FBWSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUMsbUJBQW1CLENBQUMsUUFBUSxDQUFDLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FDeEUsQ0FBQztBQUVGOzs7O0dBSUc7QUFDSCxTQUFnQixpQkFBaUIsQ0FBQyxpQkFBeUI7SUFDekQsT0FBTyxlQUFlLENBQUMsR0FBRyxDQUFDLGlCQUFpQixDQUFDLENBQUM7QUFDaEQsQ0FBQyJ9
@@ -0,0 +1,6 @@
1
+ import { BffHttpError } from '../http/bff-http-error';
2
+ export declare function unknownField(field: string): BffHttpError;
3
+ export declare function fieldNotFilterable(field: string): BffHttpError;
4
+ export declare function filterTooDeep(maxDepth: number): BffHttpError;
5
+ export declare function invalidFilterOperator(field: string, validOperators: string[]): BffHttpError;
6
+ //# sourceMappingURL=validation-errors.d.ts.map
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unknownField = unknownField;
4
+ exports.fieldNotFilterable = fieldNotFilterable;
5
+ exports.filterTooDeep = filterTooDeep;
6
+ exports.invalidFilterOperator = invalidFilterOperator;
7
+ const bff_http_error_1 = require("../http/bff-http-error");
8
+ function unknownField(field) {
9
+ return new bff_http_error_1.BffHttpError(422, 'unknown_field', `Unknown field: ${field}`, { field });
10
+ }
11
+ function fieldNotFilterable(field) {
12
+ return new bff_http_error_1.BffHttpError(422, 'field_not_filterable', `Field is not filterable: ${field}`, {
13
+ field,
14
+ });
15
+ }
16
+ function filterTooDeep(maxDepth) {
17
+ return new bff_http_error_1.BffHttpError(400, 'filter_too_deep', `Filter nesting exceeds the maximum depth of ${maxDepth}`, { maxDepth });
18
+ }
19
+ function invalidFilterOperator(field, validOperators) {
20
+ return new bff_http_error_1.BffHttpError(400, 'invalid_filter_operator', `Unsupported operator for field: ${field}`, { field, validOperators });
21
+ }
22
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdGlvbi1lcnJvcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdmFsaWRhdGlvbi92YWxpZGF0aW9uLWVycm9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLG9DQUVDO0FBRUQsZ0RBSUM7QUFFRCxzQ0FPQztBQUVELHNEQU9DO0FBNUJELDJEQUFzRDtBQUV0RCxTQUFnQixZQUFZLENBQUMsS0FBYTtJQUN4QyxPQUFPLElBQUksNkJBQVksQ0FBQyxHQUFHLEVBQUUsZUFBZSxFQUFFLGtCQUFrQixLQUFLLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7QUFDdEYsQ0FBQztBQUVELFNBQWdCLGtCQUFrQixDQUFDLEtBQWE7SUFDOUMsT0FBTyxJQUFJLDZCQUFZLENBQUMsR0FBRyxFQUFFLHNCQUFzQixFQUFFLDRCQUE0QixLQUFLLEVBQUUsRUFBRTtRQUN4RixLQUFLO0tBQ04sQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQWdCLGFBQWEsQ0FBQyxRQUFnQjtJQUM1QyxPQUFPLElBQUksNkJBQVksQ0FDckIsR0FBRyxFQUNILGlCQUFpQixFQUNqQiwrQ0FBK0MsUUFBUSxFQUFFLEVBQ3pELEVBQUUsUUFBUSxFQUFFLENBQ2IsQ0FBQztBQUNKLENBQUM7QUFFRCxTQUFnQixxQkFBcUIsQ0FBQyxLQUFhLEVBQUUsY0FBd0I7SUFDM0UsT0FBTyxJQUFJLDZCQUFZLENBQ3JCLEdBQUcsRUFDSCx5QkFBeUIsRUFDekIsbUNBQW1DLEtBQUssRUFBRSxFQUMxQyxFQUFFLEtBQUssRUFBRSxjQUFjLEVBQUUsQ0FDMUIsQ0FBQztBQUNKLENBQUMifQ==
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forestadmin/agent-bff",
3
- "version": "1.8.5",
3
+ "version": "1.9.0",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "forest-bff": "dist/cli.js"
@@ -31,8 +31,9 @@
31
31
  "test": "jest"
32
32
  },
33
33
  "dependencies": {
34
- "@forestadmin/agent-client": "1.12.1",
35
- "@forestadmin/forestadmin-client": "1.41.2",
34
+ "@forestadmin/agent-client": "1.13.0",
35
+ "@forestadmin/datasource-toolkit": "1.54.0",
36
+ "@forestadmin/forestadmin-client": "1.41.3",
36
37
  "@koa/bodyparser": "^6.1.0",
37
38
  "jsonwebtoken": "^9.0.3",
38
39
  "koa": "^3.0.1",