@forestadmin/agent-bff 1.16.0 → 1.18.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 (41) hide show
  1. package/README.md +15 -1
  2. package/dist/action/action-form-mapper.d.ts +2 -1
  3. package/dist/action/action-form-mapper.js +1 -1
  4. package/dist/action/agent-action-client.d.ts +2 -1
  5. package/dist/action/agent-action-client.js +1 -1
  6. package/dist/cli-core.d.ts +3 -0
  7. package/dist/cli-core.js +52 -10
  8. package/dist/cli-dispatch.d.ts +8 -2
  9. package/dist/cli-dispatch.js +90 -9
  10. package/dist/data/data-routes-middleware.js +3 -12
  11. package/dist/data/pack-id.d.ts +1 -0
  12. package/dist/data/pack-id.js +4 -3
  13. package/dist/openapi/collect-unfolding.d.ts +23 -0
  14. package/dist/openapi/collect-unfolding.js +144 -0
  15. package/dist/openapi/component-pool.d.ts +18 -0
  16. package/dist/openapi/component-pool.js +28 -0
  17. package/dist/openapi/field-schemas.d.ts +9 -0
  18. package/dist/openapi/field-schemas.js +43 -0
  19. package/dist/openapi/names.d.ts +16 -0
  20. package/dist/openapi/names.js +28 -0
  21. package/dist/openapi/openapi-document.d.ts +2 -1
  22. package/dist/openapi/openapi-document.js +112 -66
  23. package/dist/openapi/openapi-routes.d.ts +4 -1
  24. package/dist/openapi/openapi-routes.js +46 -9
  25. package/dist/openapi/schemas.d.ts +14 -1
  26. package/dist/openapi/schemas.js +11 -10
  27. package/dist/openapi/unfolded-document.d.ts +28 -0
  28. package/dist/openapi/unfolded-document.js +52 -0
  29. package/dist/openapi/unfolded-paths.d.ts +20 -0
  30. package/dist/openapi/unfolded-paths.js +408 -0
  31. package/dist/openapi/unfolding.d.ts +66 -0
  32. package/dist/openapi/unfolding.js +17 -0
  33. package/dist/read-model/agent-capabilities-fetcher.d.ts +8 -3
  34. package/dist/read-model/agent-capabilities-fetcher.js +15 -7
  35. package/dist/read-model/capabilities-cache.d.ts +11 -1
  36. package/dist/read-model/capabilities-cache.js +1 -1
  37. package/dist/read-model/read-model.d.ts +14 -0
  38. package/dist/read-model/read-model.js +36 -2
  39. package/dist/validation/operator-normalizer.d.ts +6 -0
  40. package/dist/validation/operator-normalizer.js +11 -1
  41. package/package.json +4 -4
@@ -0,0 +1,408 @@
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.default = registerUnfoldedPaths;
7
+ const field_schemas_1 = __importDefault(require("./field-schemas"));
8
+ const names_1 = __importDefault(require("./names"));
9
+ const schemas_1 = require("./schemas");
10
+ const pack_id_1 = require("../data/pack-id");
11
+ const AGGREGATORS = ['And', 'Or'];
12
+ const DEGRADED_NOTE = {
13
+ capabilities_unavailable: 'The field names are NOT enumerated here: the agent could not be asked for this ' +
14
+ "collection's capabilities when the document was built. Any field the collection really " +
15
+ 'exposes is still accepted.',
16
+ no_fields: 'The field names are NOT enumerated here: the agent reports no filterable or projectable ' +
17
+ 'field on this collection. Any field sent in `projection`, `sort` or `filter` is therefore ' +
18
+ 'rejected with 422 unknown_field — call this path without them.',
19
+ };
20
+ function quoted(name) {
21
+ return JSON.stringify(name);
22
+ }
23
+ // A name reaches the runtime through decodeURIComponent, so the document must carry it encoded —
24
+ // including the separators that would otherwise change the route, such as a slash inside an action
25
+ // name (`a/b` is reachable only as `a%2Fb`).
26
+ function segment(name) {
27
+ return encodeURIComponent(name);
28
+ }
29
+ function fieldsEnum(pool, name, fields, description) {
30
+ // An empty enum would forbid every value, so a collection with no known field set keeps a plain
31
+ // string — the runtime is the one that rejects an unknown field, and it says which.
32
+ if (fields.length === 0)
33
+ return { type: 'string' };
34
+ return pool.add(name, { type: 'string', enum: fields, description });
35
+ }
36
+ /**
37
+ * Groups the filterable fields by the operator set they share. One leaf per field would be as exact
38
+ * but would multiply the document by the field count; the number of distinct operator sets is bounded
39
+ * by the column types a collection really uses, and the cross product of a group is precisely the set
40
+ * of valid `(field, operator)` pairs. The grouping is derived from what capabilities reported — two
41
+ * fields of DIFFERENT types sharing an operator set land in one group, so nothing here is a type
42
+ * table.
43
+ */
44
+ function groupByOperators(filterable) {
45
+ const groups = new Map();
46
+ filterable.forEach(field => {
47
+ // An empty set would emit `enum: []`, which forbids every value and makes the leaf unsatisfiable —
48
+ // the trap `fieldsEnum` guards above. `collectFilterableFields` cannot produce one, but `Unfolding`
49
+ // is hand-constructible plain data, so the generator holds the invariant rather than assume it.
50
+ if (field.operators.length === 0)
51
+ return;
52
+ // Safe as a key because the operators are normalized to one canonical order at collect time.
53
+ const signature = field.operators.join(',');
54
+ const group = groups.get(signature);
55
+ if (group)
56
+ group.fields.push(field.name);
57
+ else
58
+ groups.set(signature, { fields: [field.name], operators: [...field.operators] });
59
+ });
60
+ return [...groups.values()];
61
+ }
62
+ // A node readable as BOTH a leaf and a branch is rejected with 400 (`agent-query.ts`
63
+ // `assertNoNodeReadableAsBothLeafAndBranch`), so each alternative excludes the other's discriminator.
64
+ // The exclusion carries the TYPE the runtime looks for — `isBranch` needs an ARRAY `conditions` and
65
+ // `isLeaf` a STRING `field` — because `{field, operator, conditions: "x"}` is a plain leaf the runtime
66
+ // accepts. Expressed as `not: { required }` rather than `additionalProperties: false`, which would
67
+ // also forbid an unknown extra key the runtime strips.
68
+ function leafShape(field, operators, description) {
69
+ return {
70
+ type: 'object',
71
+ description,
72
+ properties: {
73
+ field,
74
+ operator: { type: 'string', enum: operators },
75
+ value: {},
76
+ },
77
+ required: ['field', 'operator'],
78
+ not: { properties: { conditions: { type: 'array' } }, required: ['conditions'] },
79
+ };
80
+ }
81
+ /**
82
+ * One leaf alternative per operator set. A field absent from `filterable` is in none of them, which is
83
+ * the only honest way to document it: a `ManyToOne` answers 422 field_not_filterable, and a field the
84
+ * collector dropped for an unmappable operator answers 500 mapping_error on every filter.
85
+ */
86
+ function filterLeaves(pool, plan) {
87
+ const { name, fields } = plan.collection;
88
+ const groups = groupByOperators(fields.filterable);
89
+ // No known filterable field: the field stays free-form and every operator stays allowed, because a
90
+ // collection whose capabilities could not be read still accepts whatever it really exposes.
91
+ if (groups.length === 0) {
92
+ return [
93
+ pool.add(`FilterLeaf_${plan.key}`, leafShape({ type: 'string' }, schemas_1.OPERATORS, `A single condition on ${quoted(name)}.`)),
94
+ ];
95
+ }
96
+ // `-` rather than `_` before the index, and the difference is load-bearing: `sanitizeIdentifier` maps
97
+ // every character outside `[A-Za-z0-9_]` to `_`, so a collection key can end in `_<digit>` — either
98
+ // because the customer named a collection `Invoice 1`, or because `createNamer` suffixed a name that
99
+ // collapsed. With `_`, collection `Invoice 1` (no group, so the bare name below) and collection
100
+ // `Invoice` (first group) both claim `FilterLeaf_Invoice_1`, and `ComponentPool.add` settles it
101
+ // last-wins in silence — one collection would document the other's fields and operators. Sanitizing
102
+ // can never produce a `-`, so this separator makes the two namespaces disjoint by construction.
103
+ return groups.map((group, index) => pool.add(`FilterLeaf_${plan.key}-${index + 1}`, leafShape({ type: 'string', enum: group.fields }, group.operators, `A single condition on ${quoted(name)}. These fields share one operator set, as the ` +
104
+ "collection's capabilities report it.")));
105
+ }
106
+ function filterSchema({ pool }, plan) {
107
+ const { name, fields } = plan.collection;
108
+ const treeName = `Filter_${plan.key}`;
109
+ const treeRef = { $ref: `#/components/schemas/${treeName}` };
110
+ const leaves = filterLeaves(pool, plan);
111
+ const pairing = fields.filterable.length > 0
112
+ ? ' There is one leaf alternative per operator set: a field accepts only the operators of ' +
113
+ 'the alternative listing it, and an operator it does not support answers 400 ' +
114
+ 'invalid_filter_operator.'
115
+ : '';
116
+ return pool.add(treeName, {
117
+ description: `A filter on ${quoted(name)}: either a leaf condition or a branch nesting more ` +
118
+ 'conditions. A branch must carry its aggregator. A node carrying both `field` and ' +
119
+ `\`conditions\` is rejected with 400, so the two shapes are mutually exclusive.${pairing}`,
120
+ anyOf: [
121
+ ...leaves,
122
+ {
123
+ type: 'object',
124
+ properties: {
125
+ aggregator: { type: 'string', enum: AGGREGATORS },
126
+ conditions: { type: 'array', items: treeRef },
127
+ },
128
+ required: ['aggregator', 'conditions'],
129
+ not: { properties: { field: { type: 'string' } }, required: ['field'] },
130
+ },
131
+ ],
132
+ });
133
+ }
134
+ function fieldRefs(deps, plan) {
135
+ const { pool } = deps;
136
+ const { name, fields } = plan.collection;
137
+ const projectable = fieldsEnum(pool, `Fields_${plan.key}`, fields.projectable, `A field of ${quoted(name)}.`);
138
+ // Filterable is a strict subset of projectable: the agent reports a ManyToOne without operators, so
139
+ // projecting or sorting on it works while filtering on it answers 422 field_not_filterable. Which is
140
+ // why the filterable fields are not enumerated here but inside the filter leaves, where each one
141
+ // sits next to the operators it actually supports.
142
+ return {
143
+ projectable,
144
+ filter: filterSchema(deps, plan),
145
+ sort: fields.projectable.length === 0
146
+ ? pool.reuse('SortClause', schemas_1.SortClauseSchema)
147
+ : pool.add(`SortClause_${plan.key}`, {
148
+ type: 'object',
149
+ description: 'Omitting `direction` sorts ascending.',
150
+ properties: {
151
+ field: projectable,
152
+ direction: { type: 'string', enum: ['asc', 'desc'] },
153
+ },
154
+ required: ['field'],
155
+ }),
156
+ };
157
+ }
158
+ function requestDescription(collection, subject) {
159
+ const note = collection.fields.degraded ? ` ${DEGRADED_NOTE[collection.fields.degraded]}` : '';
160
+ return `${subject}${note}`;
161
+ }
162
+ function registerRequests(deps, plan) {
163
+ const { pool } = deps;
164
+ const { collection, key } = plan;
165
+ const refs = fieldRefs(deps, plan);
166
+ const timezone = pool.reuse('Timezone', schemas_1.TimezoneSchema);
167
+ return {
168
+ list: pool.add(`ListRequest_${key}`, {
169
+ type: 'object',
170
+ description: requestDescription(collection, `Filter, sort and project records of ${quoted(collection.name)}.`),
171
+ properties: {
172
+ filter: refs.filter,
173
+ projection: { type: 'array', items: refs.projectable },
174
+ sort: { type: 'array', items: refs.sort },
175
+ page: pool.reuse('Page', schemas_1.PageSchema),
176
+ timezone,
177
+ },
178
+ }),
179
+ count: pool.add(`CountRequest_${key}`, {
180
+ type: 'object',
181
+ description: requestDescription(collection, `Count records of ${quoted(collection.name)} matching a filter.`),
182
+ properties: { filter: refs.filter, timezone },
183
+ }),
184
+ };
185
+ }
186
+ const PARENT_ID_SHAPE = {
187
+ anyOf: [{ type: 'string', pattern: '\\S' }, { type: 'number' }],
188
+ };
189
+ /**
190
+ * `parentId` is opaque on the wire: `parseParentId` takes a non-blank string or a finite number
191
+ * whatever the key's type is, and forwards it to the agent as a string. So the SHAPE stays that
192
+ * union — narrowing a numeric key to `number` would make a generated client reject `"123"`, which
193
+ * the endpoint accepts. What the read-model knows goes in the description: which column the id
194
+ * belongs to, and, for a composite key, the `|`-joined order `unpackPrimaryKey` expects.
195
+ */
196
+ function parentIdSchema(pool, parent, primaryKeys) {
197
+ if (primaryKeys.length === 0)
198
+ return pool.reuse('ParentId', schemas_1.ParentIdSchema);
199
+ if (primaryKeys.length > 1) {
200
+ return {
201
+ // A composite id only works as its packed string: a number could never carry the separator.
202
+ type: 'string',
203
+ pattern: '\\S',
204
+ description: `The composite id of the parent ${quoted(parent)} record: the values of ` +
205
+ `${primaryKeys.map(key => key.name).join(', ')} joined by ` +
206
+ `${quoted(pack_id_1.PACKED_ID_SEPARATOR)}, in that order.`,
207
+ };
208
+ }
209
+ const [key] = primaryKeys;
210
+ return {
211
+ ...PARENT_ID_SHAPE,
212
+ description: `The parent ${quoted(parent)} record id (${key.name}, ${key.type}). A number or its ` +
213
+ 'string form are both accepted; the BFF forwards it to the agent as a string.',
214
+ };
215
+ }
216
+ function registerRelationRequests(deps, plan, relation, relationKey, foreign) {
217
+ const { pool } = deps;
218
+ const parentId = parentIdSchema(pool, plan.collection.name, plan.collection.primaryKeys);
219
+ const parentProperties = {
220
+ type: 'object',
221
+ properties: { parentId },
222
+ required: ['parentId'],
223
+ };
224
+ const description = `Filter, sort and projection apply to ${quoted(foreign.collection.name)}, the foreign ` +
225
+ `collection of ${quoted(plan.collection.name)}.${quoted(relation.name)}; the parent only ` +
226
+ 'resolves which records are related.';
227
+ return {
228
+ list: pool.add(`RelationListRequest_${relationKey}`, {
229
+ description,
230
+ allOf: [foreign.requests.list, parentProperties],
231
+ }),
232
+ count: pool.add(`RelationCountRequest_${relationKey}`, {
233
+ description,
234
+ allOf: [foreign.requests.count, parentProperties],
235
+ }),
236
+ };
237
+ }
238
+ function actionFieldSchema(field) {
239
+ const base = field.enums
240
+ ? { type: 'string', enum: field.enums }
241
+ : (0, field_schemas_1.default)(field.type);
242
+ return field.isRequired
243
+ ? { ...base, description: 'The agent declares this field required on the static form.' }
244
+ : base;
245
+ }
246
+ function actionValuesSchema(action) {
247
+ return {
248
+ type: 'object',
249
+ // No property is required and no additional one is forbidden on purpose: a load or change hook
250
+ // rebuilds the form at call time, so the schema's static fields are an indication, not the
251
+ // contract — and the read-model cannot tell a hookless action from a legacy schema that simply
252
+ // omitted its hooks, so "static" is never certain.
253
+ description: 'The submitted action fields. These are the fields the schema declares statically; a load ' +
254
+ 'or change hook can add, drop or require others at call time.',
255
+ properties: Object.fromEntries(action.fields.map(field => [field.name, actionFieldSchema(field)])),
256
+ };
257
+ }
258
+ function registerActionRequest(deps, plan, action, actionKey) {
259
+ const { pool } = deps;
260
+ return pool.add(`ActionRequest_${actionKey}`, {
261
+ type: 'object',
262
+ description: `The body of action ${quoted(action.name)} on ${quoted(plan.collection.name)}. ` +
263
+ '`recordIds` is required even on a global action: send an empty array when the action ' +
264
+ 'targets no record.',
265
+ properties: {
266
+ recordIds: { type: 'array', items: { anyOf: [{ type: 'string' }, { type: 'number' }] } },
267
+ values: actionValuesSchema(action),
268
+ timezone: pool.reuse('Timezone', schemas_1.TimezoneSchema),
269
+ },
270
+ required: ['recordIds'],
271
+ });
272
+ }
273
+ function registerOperation(deps, options) {
274
+ deps.registry.registerPath({
275
+ method: 'post',
276
+ path: `${deps.prefix}/${options.path}`,
277
+ operationId: options.operationId,
278
+ summary: options.summary,
279
+ description: options.description,
280
+ security: deps.security,
281
+ request: {
282
+ headers: deps.timezoneHeader,
283
+ body: {
284
+ required: options.bodyRequired,
285
+ content: { 'application/json': { schema: options.request } },
286
+ },
287
+ },
288
+ responses: {
289
+ 200: {
290
+ description: options.responseDescription,
291
+ content: { 'application/json': { schema: options.response } },
292
+ },
293
+ ...deps.errorResponses(options.executeResults === true),
294
+ },
295
+ });
296
+ }
297
+ function registerCollectionOperations(deps, plan) {
298
+ const { pool } = deps;
299
+ const { name } = plan.collection;
300
+ const listResponse = pool.reuse('ListResponse', schemas_1.ListResponseSchema);
301
+ const countResponse = pool.reuse('CountResponse', schemas_1.CountResponseSchema);
302
+ registerOperation(deps, {
303
+ path: `${segment(name)}/list`,
304
+ operationId: `listRecords_${plan.key}`,
305
+ summary: `List records of ${name}`,
306
+ description: `Lists records of the ${quoted(name)} collection.`,
307
+ request: plan.requests.list,
308
+ response: listResponse,
309
+ responseDescription: `A page of ${quoted(name)} records`,
310
+ bodyRequired: false,
311
+ });
312
+ registerOperation(deps, {
313
+ path: `${segment(name)}/count`,
314
+ operationId: `countRecords_${plan.key}`,
315
+ summary: `Count records of ${name}`,
316
+ description: `Counts records of the ${quoted(name)} collection.`,
317
+ request: plan.requests.count,
318
+ response: countResponse,
319
+ responseDescription: `The matching ${quoted(name)} record count`,
320
+ bodyRequired: false,
321
+ });
322
+ }
323
+ function registerRelationOperations(deps, plan, plansByName, namer) {
324
+ const { pool } = deps;
325
+ plan.collection.relations.forEach(relation => {
326
+ const foreign = plansByName.get(relation.foreignCollection);
327
+ if (!foreign)
328
+ return;
329
+ const relationKey = namer(`${plan.key}_${relation.name}`);
330
+ const requests = registerRelationRequests(deps, plan, relation, relationKey, foreign);
331
+ const parent = `${quoted(plan.collection.name)}.${quoted(relation.name)}`;
332
+ registerOperation(deps, {
333
+ path: `${segment(plan.collection.name)}/relations/${segment(relation.name)}/list`,
334
+ operationId: `listRelatedRecords_${relationKey}`,
335
+ summary: `List ${relation.name} of ${plan.collection.name}`,
336
+ description: `Lists the ${quoted(foreign.collection.name)} records related to a ${quoted(plan.collection.name)} record through ${parent}.`,
337
+ request: requests.list,
338
+ response: pool.reuse('ListResponse', schemas_1.ListResponseSchema),
339
+ responseDescription: `A page of related ${quoted(foreign.collection.name)} records`,
340
+ bodyRequired: true,
341
+ });
342
+ registerOperation(deps, {
343
+ path: `${segment(plan.collection.name)}/relations/${segment(relation.name)}/count`,
344
+ operationId: `countRelatedRecords_${relationKey}`,
345
+ summary: `Count ${relation.name} of ${plan.collection.name}`,
346
+ description: `Counts the ${quoted(foreign.collection.name)} records related through ${parent}.`,
347
+ request: requests.count,
348
+ response: pool.reuse('CountResponse', schemas_1.CountResponseSchema),
349
+ responseDescription: `The matching related ${quoted(foreign.collection.name)} record count`,
350
+ bodyRequired: true,
351
+ });
352
+ });
353
+ }
354
+ function registerActionOperations(deps, plan, namer) {
355
+ plan.collection.actions.forEach(action => {
356
+ const actionKey = namer(`${plan.key}_${action.name}`);
357
+ const request = registerActionRequest(deps, plan, action, actionKey);
358
+ // The path segment is the exact schema action name, URL-encoded — that is what the middleware
359
+ // decodes and looks up. The operationId is sanitized, so the exact name lives in prose.
360
+ const identity = `The action name is exactly ${quoted(action.name)}.`;
361
+ const base = `${segment(plan.collection.name)}/actions/${segment(action.name)}`;
362
+ registerOperation(deps, {
363
+ path: `${base}/form`,
364
+ operationId: `getActionForm_${actionKey}`,
365
+ summary: `Load the form of ${action.name} on ${plan.collection.name}`,
366
+ description: `Loads the form of the custom action. ${identity} An unknown submitted field is skipped here, not rejected.`,
367
+ request,
368
+ response: {},
369
+ responseDescription: 'The action form fields',
370
+ bodyRequired: true,
371
+ });
372
+ registerOperation(deps, {
373
+ path: `${base}/execute`,
374
+ operationId: `executeAction_${actionKey}`,
375
+ summary: `Execute ${action.name} on ${plan.collection.name}`,
376
+ description: `Executes the custom action. ${identity} A submitted field the loaded form does not carry is rejected with 400.`,
377
+ request,
378
+ response: {},
379
+ responseDescription: 'The normalized action result',
380
+ bodyRequired: true,
381
+ executeResults: true,
382
+ });
383
+ });
384
+ }
385
+ /**
386
+ * Emits one path per exposed collection, to-many relation and action. The runtime routes stay the
387
+ * generic regexes — only the document unfolds.
388
+ */
389
+ function registerUnfoldedPaths(deps, unfolding) {
390
+ // One namer per identifier family, fed the WHOLE composed name. Deduplicating each segment on its
391
+ // own would not be enough: sanitizing produces `_`, which is also the separator, so collection
392
+ // `A_B` with action `C` and collection `A` with action `B_C` both compose `A_B_C` — one action
393
+ // would then get the other's request schema.
394
+ const collections = (0, names_1.default)();
395
+ const relations = (0, names_1.default)();
396
+ const actions = (0, names_1.default)();
397
+ const plans = unfolding.collections.map(collection => {
398
+ const key = collections(collection.name);
399
+ return { collection, key, requests: registerRequests(deps, { collection, key }) };
400
+ });
401
+ const plansByName = new Map(plans.map(plan => [plan.collection.name, plan]));
402
+ plans.forEach(plan => {
403
+ registerCollectionOperations(deps, plan);
404
+ registerRelationOperations(deps, plan, plansByName, relations);
405
+ registerActionOperations(deps, plan, actions);
406
+ });
407
+ }
408
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW5mb2xkZWQtcGF0aHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3BlbmFwaS91bmZvbGRlZC1wYXRocy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQWtqQkEsd0NBcUJDO0FBeGpCRCxvRUFBNEM7QUFDNUMsb0RBQWtDO0FBQ2xDLHVDQVFtQjtBQUNuQiw2Q0FBc0Q7QUFFdEQsTUFBTSxXQUFXLEdBQUcsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFFbEMsTUFBTSxhQUFhLEdBQThEO0lBQy9FLHdCQUF3QixFQUN0QixpRkFBaUY7UUFDakYseUZBQXlGO1FBQ3pGLDRCQUE0QjtJQUM5QixTQUFTLEVBQ1AsMEZBQTBGO1FBQzFGLDRGQUE0RjtRQUM1RixnRUFBZ0U7Q0FDbkUsQ0FBQztBQXNCRixTQUFTLE1BQU0sQ0FBQyxJQUFZO0lBQzFCLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUM5QixDQUFDO0FBRUQsaUdBQWlHO0FBQ2pHLG1HQUFtRztBQUNuRyw2Q0FBNkM7QUFDN0MsU0FBUyxPQUFPLENBQUMsSUFBWTtJQUMzQixPQUFPLGtCQUFrQixDQUFDLElBQUksQ0FBQyxDQUFDO0FBQ2xDLENBQUM7QUFFRCxTQUFTLFVBQVUsQ0FDakIsSUFBbUIsRUFDbkIsSUFBWSxFQUNaLE1BQWdCLEVBQ2hCLFdBQW1CO0lBRW5CLGdHQUFnRztJQUNoRyxvRkFBb0Y7SUFDcEYsSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUM7UUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxDQUFDO0lBRW5ELE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLENBQUMsQ0FBQztBQUN2RSxDQUFDO0FBT0Q7Ozs7Ozs7R0FPRztBQUNILFNBQVMsZ0JBQWdCLENBQUMs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
@@ -0,0 +1,66 @@
1
+ import type { FieldType } from '../read-model/capabilities-cache';
2
+ import type { PrimaryKeyField } from '../read-model/read-model';
3
+ import type { Operator } from '@forestadmin/datasource-toolkit';
4
+ /**
5
+ * The snapshot the OpenAPI generator unfolds. It is plain data on purpose: collecting it hits the
6
+ * network (schema + capabilities per collection), generating from it does not, so the route and the
7
+ * CLI can be proven to emit the same document by feeding them one snapshot.
8
+ */
9
+ export interface Unfolding {
10
+ collections: UnfoldedCollection[];
11
+ }
12
+ export interface UnfoldedCollection {
13
+ name: string;
14
+ fields: CollectionFields;
15
+ primaryKeys: PrimaryKeyField[];
16
+ /** To-many relations whose foreign collection is exposed too — the only ones with a route. */
17
+ relations: UnfoldedRelation[];
18
+ actions: UnfoldedAction[];
19
+ }
20
+ export interface UnfoldedRelation {
21
+ name: string;
22
+ foreignCollection: string;
23
+ }
24
+ /**
25
+ * `projectable` is every capability field; `filterable` is the subset carrying operators. They
26
+ * differ: a `ManyToOne` is reported by the agent without operators, so projecting or sorting on it
27
+ * works while filtering on it answers 422 field_not_filterable.
28
+ *
29
+ * `degraded` is set when the field set could not be established. The collection keeps its paths, but
30
+ * their field schemas stay free-form strings — an empty enum would forbid every valid call.
31
+ */
32
+ export interface CollectionFields {
33
+ projectable: string[];
34
+ filterable: FilterableField[];
35
+ degraded: DegradedReason | null;
36
+ }
37
+ /**
38
+ * A field the agent reports at least one operator for, with that operator set — the very set the
39
+ * runtime validates a filter leaf against. Normalized to canonical PascalCase and to the
40
+ * `allOperators` order at collect time, so the same capabilities answer always yields the same
41
+ * document whatever order the agent serialized the operators in.
42
+ */
43
+ export interface FilterableField {
44
+ name: string;
45
+ operators: Operator[];
46
+ }
47
+ export type DegradedReason = 'capabilities_unavailable' | 'no_fields';
48
+ /** True when at least one collection went out without its field set. */
49
+ export declare function hasDegradedCollection(unfolding: Unfolding): boolean;
50
+ /**
51
+ * True when NO collection got its field set — the signature of an unreachable agent rather than of
52
+ * one odd collection. An empty schema is not degraded: there was nothing to enumerate.
53
+ */
54
+ export declare function isFullyDegraded(unfolding: Unfolding): boolean;
55
+ export interface UnfoldedAction {
56
+ name: string;
57
+ fields: UnfoldedActionField[];
58
+ }
59
+ export interface UnfoldedActionField {
60
+ name: string;
61
+ type: FieldType;
62
+ isRequired: boolean;
63
+ enums: string[] | null;
64
+ }
65
+ export type { FieldType };
66
+ //# sourceMappingURL=unfolding.d.ts.map
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasDegradedCollection = hasDegradedCollection;
4
+ exports.isFullyDegraded = isFullyDegraded;
5
+ /** True when at least one collection went out without its field set. */
6
+ function hasDegradedCollection(unfolding) {
7
+ return unfolding.collections.some(collection => collection.fields.degraded !== null);
8
+ }
9
+ /**
10
+ * True when NO collection got its field set — the signature of an unreachable agent rather than of
11
+ * one odd collection. An empty schema is not degraded: there was nothing to enumerate.
12
+ */
13
+ function isFullyDegraded(unfolding) {
14
+ return (unfolding.collections.length > 0 &&
15
+ unfolding.collections.every(collection => collection.fields.degraded !== null));
16
+ }
17
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW5mb2xkaW5nLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL29wZW5hcGkvdW5mb2xkaW5nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBdURBLHNEQUVDO0FBTUQsMENBS0M7QUFkRCx3RUFBd0U7QUFDeEUsU0FBZ0IscUJBQXFCLENBQUMsU0FBb0I7SUFDeEQsT0FBTyxTQUFTLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsUUFBUSxLQUFLLElBQUksQ0FBQyxDQUFDO0FBQ3ZGLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxTQUFnQixlQUFlLENBQUMsU0FBb0I7SUFDbEQsT0FBTyxDQUNMLFNBQVMsQ0FBQyxXQUFXLENBQUMsTUFBTSxHQUFHLENBQUM7UUFDaEMsU0FBUyxDQUFDLFdBQVcsQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLFFBQVEsS0FBSyxJQUFJLENBQUMsQ0FDL0UsQ0FBQztBQUNKLENBQUMifQ==
@@ -1,12 +1,17 @@
1
1
  import type { CapabilitiesFetcher } from './capabilities-cache';
2
2
  export interface AgentCapabilitiesFetcherOptions {
3
3
  agentUrl: string;
4
- token: string;
4
+ /** A borrowed request token, or a factory when the caller can mint one per fetch. */
5
+ token: string | (() => string);
5
6
  timeoutMs?: number;
6
7
  }
7
8
  /**
8
- * Builds a capabilities fetcher bound to a request's agent token. The cache calls it only on a
9
- * miss, so the token of whichever request first populates a collection is the one used.
9
+ * Builds a capabilities fetcher bound to an agent token. The cache calls it only on a miss, so the
10
+ * token of whichever request first populates a collection is the one used.
11
+ *
12
+ * A factory exists for the token because an agent token lives 5 minutes: a caller fanning out over
13
+ * hundreds of collections can outlive a single one, and the tail of that fan-out would fail on an
14
+ * expired token. A request can only borrow its caller's, so it passes the string.
10
15
  */
11
16
  export default function createAgentCapabilitiesFetcher({ agentUrl, token, timeoutMs, }: AgentCapabilitiesFetcherOptions): CapabilitiesFetcher;
12
17
  //# sourceMappingURL=agent-capabilities-fetcher.d.ts.map
@@ -7,15 +7,23 @@ exports.default = createAgentCapabilitiesFetcher;
7
7
  const agent_client_1 = require("@forestadmin/agent-client");
8
8
  const create_agent_http_requester_1 = __importDefault(require("../agent/create-agent-http-requester"));
9
9
  /**
10
- * Builds a capabilities fetcher bound to a request's agent token. The cache calls it only on a
11
- * miss, so the token of whichever request first populates a collection is the one used.
10
+ * Builds a capabilities fetcher bound to an agent token. The cache calls it only on a miss, so the
11
+ * token of whichever request first populates a collection is the one used.
12
+ *
13
+ * A factory exists for the token because an agent token lives 5 minutes: a caller fanning out over
14
+ * hundreds of collections can outlive a single one, and the tail of that fan-out would fail on an
15
+ * expired token. A request can only borrow its caller's, so it passes the string.
12
16
  */
13
17
  function createAgentCapabilitiesFetcher({ agentUrl, token, timeoutMs, }) {
14
- const client = (0, agent_client_1.createRemoteAgentClient)({
18
+ const clientFor = (bearer) => (0, agent_client_1.createRemoteAgentClient)({
15
19
  url: agentUrl,
16
- token,
17
- httpRequester: (0, create_agent_http_requester_1.default)(token, agentUrl, timeoutMs),
20
+ token: bearer,
21
+ httpRequester: (0, create_agent_http_requester_1.default)(bearer, agentUrl, timeoutMs),
18
22
  });
19
- return collection => client.collection(collection).capabilities();
23
+ if (typeof token === 'string') {
24
+ const client = clientFor(token);
25
+ return collection => client.collection(collection).capabilities();
26
+ }
27
+ return collection => clientFor(token()).collection(collection).capabilities();
20
28
  }
21
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtY2FwYWJpbGl0aWVzLWZldGNoZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcmVhZC1tb2RlbC9hZ2VudC1jYXBhYmlsaXRpZXMtZmV0Y2hlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQWdCQSxpREFZQztBQTFCRCw0REFBb0U7QUFFcEUsdUdBQTRFO0FBUTVFOzs7R0FHRztBQUNILFNBQXdCLDhCQUE4QixDQUFDLEVBQ3JELFFBQVEsRUFDUixLQUFLLEVBQ0wsU0FBUyxHQUN1QjtJQUNoQyxNQUFNLE1BQU0sR0FBRyxJQUFBLHNDQUF1QixFQUFDO1FBQ3JDLEdBQUcsRUFBRSxRQUFRO1FBQ2IsS0FBSztRQUNMLGFBQWEsRUFBRSxJQUFBLHFDQUF3QixFQUFDLEtBQUssRUFBRSxRQUFRLEVBQUUsU0FBUyxDQUFDO0tBQ3BFLENBQUMsQ0FBQztJQUVILE9BQU8sVUFBVSxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDLFlBQVksRUFBRSxDQUFDO0FBQ3BFLENBQUMifQ==
29
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtY2FwYWJpbGl0aWVzLWZldGNoZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcmVhZC1tb2RlbC9hZ2VudC1jYXBhYmlsaXRpZXMtZmV0Y2hlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQXFCQSxpREFtQkM7QUF0Q0QsNERBQW9FO0FBRXBFLHVHQUE0RTtBQVM1RTs7Ozs7OztHQU9HO0FBQ0gsU0FBd0IsOEJBQThCLENBQUMsRUFDckQsUUFBUSxFQUNSLEtBQUssRUFDTCxTQUFTLEdBQ3VCO0lBQ2hDLE1BQU0sU0FBUyxHQUFHLENBQUMsTUFBYyxFQUFFLEVBQUUsQ0FDbkMsSUFBQSxzQ0FBdUIsRUFBQztRQUN0QixHQUFHLEVBQUUsUUFBUTtRQUNiLEtBQUssRUFBRSxNQUFNO1FBQ2IsYUFBYSxFQUFFLElBQUEscUNBQXdCLEVBQUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxTQUFTLENBQUM7S0FDckUsQ0FBQyxDQUFDO0lBRUwsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUM5QixNQUFNLE1BQU0sR0FBRyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7UUFFaEMsT0FBTyxVQUFVLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLENBQUMsWUFBWSxFQUFFLENBQUM7SUFDcEUsQ0FBQztJQUVELE9BQU8sVUFBVSxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLENBQUMsWUFBWSxFQUFFLENBQUM7QUFDaEYsQ0FBQyJ9
@@ -1,7 +1,17 @@
1
+ /**
2
+ * A Forest column type: a primitive name, a one-element array wrapping another type, a composite
3
+ * `{ fields }`, or a relation marker like `ManyToOne`.
4
+ */
5
+ export type FieldType = string | FieldType[] | {
6
+ fields: {
7
+ field: string;
8
+ type: FieldType;
9
+ }[];
10
+ };
1
11
  export interface CapabilitiesResult {
2
12
  fields: {
3
13
  name: string;
4
- type: string;
14
+ type: FieldType;
5
15
  operators?: string[];
6
16
  }[];
7
17
  }
@@ -44,4 +44,4 @@ class CapabilitiesCache {
44
44
  }
45
45
  }
46
46
  exports.default = CapabilitiesCache;
47
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FwYWJpbGl0aWVzLWNhY2hlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3JlYWQtbW9kZWwvY2FwYWJpbGl0aWVzLWNhY2hlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsaURBQTRDO0FBa0I1Qzs7O0dBR0c7QUFDSCxNQUFxQixpQkFBaUI7SUFRcEMsWUFBWSxFQUFFLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLEtBQUssR0FBRyx5QkFBVSxLQUErQixFQUFFO1FBSmhFLFlBQU8sR0FBRyxJQUFJLEdBQUcsRUFBc0IsQ0FBQztRQUN4QyxhQUFRLEdBQUcsSUFBSSxHQUFHLEVBQXVDLENBQUM7UUFDbkUsZUFBVSxHQUFHLENBQUMsQ0FBQztRQUdyQixJQUFJLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQztRQUNmLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO0lBQ3JCLENBQUM7SUFFRCxLQUFLLENBQUMsR0FBRyxDQUFDLFVBQWtCLEVBQUUsT0FBNEI7UUFDeEQsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDM0MsSUFBSSxLQUFLLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLEtBQUssQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLEtBQUs7WUFBRSxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUM7UUFFNUUsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDOUMsSUFBSSxPQUFPO1lBQUUsT0FBTyxPQUFPLENBQUM7UUFFNUIsTUFBTSxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUksQ0FBQztRQUM1QixNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDO2FBQzlCLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUNiLHlGQUF5RjtZQUN6RixxRkFBcUY7WUFDckYsSUFBSSxJQUFJLENBQUMsVUFBVSxLQUFLLFVBQVUsRUFBRSxDQUFDO2dCQUNuQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLEVBQUUsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDbEUsQ0FBQztZQUVELE9BQU8sTUFBTSxDQUFDO1FBQ2hCLENBQUMsQ0FBQzthQUNELE9BQU8sQ0FBQyxHQUFHLEVBQUU7WUFDWixJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxLQUFLLEtBQUs7Z0JBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDaEYsQ0FBQyxDQUFDLENBQUM7UUFFTCxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFckMsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRUQsS0FBSztRQUNILElBQUksQ0FBQyxVQUFVLElBQUksQ0FBQyxDQUFDO1FBQ3JCLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUN4QixDQUFDO0NBQ0Y7QUE3Q0Qsb0NBNkNDIn0=
47
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FwYWJpbGl0aWVzLWNhY2hlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3JlYWQtbW9kZWwvY2FwYWJpbGl0aWVzLWNhY2hlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsaURBQTRDO0FBMEI1Qzs7O0dBR0c7QUFDSCxNQUFxQixpQkFBaUI7SUFRcEMsWUFBWSxFQUFFLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLEtBQUssR0FBRyx5QkFBVSxLQUErQixFQUFFO1FBSmhFLFlBQU8sR0FBRyxJQUFJLEdBQUcsRUFBc0IsQ0FBQztRQUN4QyxhQUFRLEdBQUcsSUFBSSxHQUFHLEVBQXVDLENBQUM7UUFDbkUsZUFBVSxHQUFHLENBQUMsQ0FBQztRQUdyQixJQUFJLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQztRQUNmLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO0lBQ3JCLENBQUM7SUFFRCxLQUFLLENBQUMsR0FBRyxDQUFDLFVBQWtCLEVBQUUsT0FBNEI7UUFDeEQsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDM0MsSUFBSSxLQUFLLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLEtBQUssQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLEtBQUs7WUFBRSxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUM7UUFFNUUsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDOUMsSUFBSSxPQUFPO1lBQUUsT0FBTyxPQUFPLENBQUM7UUFFNUIsTUFBTSxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUksQ0FBQztRQUM1QixNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDO2FBQzlCLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUNiLHlGQUF5RjtZQUN6RixxRkFBcUY7WUFDckYsSUFBSSxJQUFJLENBQUMsVUFBVSxLQUFLLFVBQVUsRUFBRSxDQUFDO2dCQUNuQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLEVBQUUsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDbEUsQ0FBQztZQUVELE9BQU8sTUFBTSxDQUFDO1FBQ2hCLENBQUMsQ0FBQzthQUNELE9BQU8sQ0FBQyxHQUFHLEVBQUU7WUFDWixJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxLQUFLLEtBQUs7Z0JBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDaEYsQ0FBQyxDQUFDLENBQUM7UUFFTCxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFckMsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRUQsS0FBSztRQUNILElBQUksQ0FBQyxVQUFVLElBQUksQ0FBQyxDQUFDO1FBQ3JCLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUN4QixDQUFDO0NBQ0Y7QUE3Q0Qsb0NBNkNDIn0=