@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,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CAPABILITIES_CONCURRENCY = void 0;
4
+ exports.default = collectUnfolding;
5
+ const errors_1 = require("../errors");
6
+ const operator_normalizer_1 = require("../validation/operator-normalizer");
7
+ // A cold document costs one capabilities call per collection. They are capped rather than fired all
8
+ // at once so a 200-collection deployment cannot open 200 sockets to the agent at the same time; a
9
+ // warm BFF pays nothing, the per-collection cache is shared with the data routes.
10
+ exports.CAPABILITIES_CONCURRENCY = 10;
11
+ async function mapWithConcurrency(items, limit, run) {
12
+ const results = new Array(items.length);
13
+ let cursor = 0;
14
+ // Recursive rather than a while loop: each worker takes the next item only once the previous one
15
+ // settled, which is what caps the concurrency.
16
+ const worker = async () => {
17
+ if (cursor >= items.length)
18
+ return;
19
+ const index = cursor;
20
+ cursor += 1;
21
+ results[index] = await run(items[index]);
22
+ await worker();
23
+ };
24
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
25
+ return results;
26
+ }
27
+ const UNTYPED = (degraded) => ({
28
+ projectable: [],
29
+ filterable: [],
30
+ degraded,
31
+ });
32
+ /**
33
+ * The operators to document for one field, or null to leave the field out of the filterable set.
34
+ *
35
+ * An operator with no canonical mapping means the agent runs a newer operator set than this package.
36
+ * The field is dropped rather than widened, because `capabilities-validator` normalizes a field's
37
+ * WHOLE operator list before comparing the one that was sent: a skewed field answers 500
38
+ * `mapping_error` on ANY filter, even a plain `Equal`. Documenting it as filterable would promise a
39
+ * call the BFF never honours, and a raw snake_case operator must never reach the public document. The
40
+ * skew is surfaced in the log instead — dropping the field keeps the document truthful, so it stays
41
+ * cacheable.
42
+ */
43
+ function documentedOperators(collection, field, operators, logger) {
44
+ const normalized = [];
45
+ for (const operator of operators) {
46
+ const canonical = (0, operator_normalizer_1.normalizeOperator)(operator);
47
+ if (!canonical) {
48
+ logger('Warn', 'Documenting a field as not filterable: one of its operators has no mapping', {
49
+ collection,
50
+ field,
51
+ operator,
52
+ });
53
+ return null;
54
+ }
55
+ normalized.push(canonical);
56
+ }
57
+ return (0, operator_normalizer_1.toCanonicalOperatorSet)(normalized);
58
+ }
59
+ function collectFilterableFields(collection, capabilities, logger) {
60
+ return capabilities.fields.flatMap(field => {
61
+ if ((field.operators?.length ?? 0) === 0)
62
+ return [];
63
+ const operators = documentedOperators(collection, field.name, field.operators, logger);
64
+ return operators === null ? [] : [{ name: field.name, operators }];
65
+ });
66
+ }
67
+ async function collectFields(collection, { store, capabilitiesFetcher, logger }) {
68
+ try {
69
+ const { capabilities } = await store.getCapabilities(collection, capabilitiesFetcher);
70
+ if (capabilities.fields.length === 0) {
71
+ logger('Warn', 'Documenting a collection without a field set: capabilities returned none', {
72
+ collection,
73
+ });
74
+ return UNTYPED('no_fields');
75
+ }
76
+ return {
77
+ projectable: capabilities.fields.map(field => field.name),
78
+ filterable: collectFilterableFields(collection, capabilities, logger),
79
+ degraded: null,
80
+ };
81
+ }
82
+ catch (error) {
83
+ // A single unreachable collection must not cost the whole document: the collection keeps its
84
+ // paths, untyped. The alternative — dropping it — would hide a collection the runtime serves.
85
+ logger('Warn', 'Documenting a collection without a field set: capabilities are unavailable', {
86
+ collection,
87
+ error: (0, errors_1.extractErrorMessage)(error),
88
+ });
89
+ return UNTYPED('capabilities_unavailable');
90
+ }
91
+ }
92
+ // A to-many relation pointing at a collection absent from the allow-list is dropped: the runtime
93
+ // answers 404 on it (data-routes-middleware checks the foreign collection), so documenting it would
94
+ // promise a dead path.
95
+ function collectRelations(readModel, collection) {
96
+ const listable = readModel
97
+ .getListableRelations(collection)
98
+ .filter(relation => readModel.isCollectionAllowed(relation.foreignCollection));
99
+ // By name, in code-unit order like the collection and action levels — NOT `localeCompare`, which
100
+ // follows the process locale: the route and the CLI could then order one schema's relations
101
+ // differently and stop producing the same document.
102
+ return listable.sort((left, right) => left.name < right.name ? -1 : Number(left.name > right.name));
103
+ }
104
+ function collectActions(readModel, collection) {
105
+ const byName = readModel.getActionEndpoints()[collection] ?? {};
106
+ return Object.keys(byName)
107
+ .sort()
108
+ .map(name => ({
109
+ name,
110
+ // A form field with no usable name is dropped: the schema is cast from untyped JSON, and the
111
+ // read-model turns a malformed entry into `{}`, which would otherwise document a property
112
+ // literally called "undefined".
113
+ fields: (byName[name].fields ?? [])
114
+ .filter(field => typeof field?.field === 'string')
115
+ .map(field => ({
116
+ name: field.field,
117
+ type: field.type,
118
+ isRequired: field.isRequired === true,
119
+ enums: field.enums ?? null,
120
+ })),
121
+ }));
122
+ }
123
+ /**
124
+ * Collects everything the document unfolds. Sorted at every level so two runs over one schema
125
+ * produce byte-identical documents — the route/CLI comparison depends on it.
126
+ *
127
+ * A schema refresh landing mid-collect can mix the new generation's field sets into this snapshot's
128
+ * structure. Detecting it belongs to the caller, which knows whether the read-model it passed is
129
+ * still the current one once this resolves.
130
+ */
131
+ async function collectUnfolding(options) {
132
+ const { readModel, concurrency = exports.CAPABILITIES_CONCURRENCY } = options;
133
+ const names = readModel.getAllowedCollections().sort();
134
+ const fields = await mapWithConcurrency(names, concurrency, name => collectFields(name, options));
135
+ const collections = names.map((name, index) => ({
136
+ name,
137
+ fields: fields[index],
138
+ primaryKeys: readModel.getPrimaryKeys(name),
139
+ relations: collectRelations(readModel, name),
140
+ actions: collectActions(readModel, name),
141
+ }));
142
+ return { collections };
143
+ }
144
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29sbGVjdC11bmZvbGRpbmcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3BlbmFwaS9jb2xsZWN0LXVuZm9sZGluZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFtTUEsbUNBZ0JDO0FBck1ELHNDQUFnRDtBQUNoRCwyRUFBOEY7QUFFOUYsb0dBQW9HO0FBQ3BHLGtHQUFrRztBQUNsRyxrRkFBa0Y7QUFDckUsUUFBQSx3QkFBd0IsR0FBRyxFQUFFLENBQUM7QUFVM0MsS0FBSyxVQUFVLGtCQUFrQixDQUMvQixLQUFVLEVBQ1YsS0FBYSxFQUNiLEdBQTRCO0lBRTVCLE1BQU0sT0FBTyxHQUFHLElBQUksS0FBSyxDQUFJLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUMzQyxJQUFJLE1BQU0sR0FBRyxDQUFDLENBQUM7SUFFZixpR0FBaUc7SUFDakcsK0NBQStDO0lBQy9DLE1BQU0sTUFBTSxHQUFHLEtBQUssSUFBbUIsRUFBRTtRQUN2QyxJQUFJLE1BQU0sSUFBSSxLQUFLLENBQUMsTUFBTTtZQUFFLE9BQU87UUFFbkMsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDO1FBQ3JCLE1BQU0sSUFBSSxDQUFDLENBQUM7UUFDWixPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsTUFBTSxHQUFHLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFFekMsTUFBTSxNQUFNLEVBQUUsQ0FBQztJQUNqQixDQUFDLENBQUM7SUFFRixNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsTUFBTSxDQUFDLEVBQUUsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBRWpGLE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUM7QUFFRCxNQUFNLE9BQU8sR0FBRyxDQUFDLFFBQXNDLEVBQW9CLEVBQUUsQ0FB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
@@ -0,0 +1,18 @@
1
+ import type { z } from './zod-openapi';
2
+ import type { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
3
+ import type { ReferenceObject, SchemaObject } from 'openapi3-ts/oas31';
4
+ /**
5
+ * Registers schema components on demand. Nothing is registered eagerly: a component no path
6
+ * references trips redocly's unused-component rule, and which shared pieces an unfolded document
7
+ * needs depends on the schema it was built from.
8
+ */
9
+ export default class ComponentPool {
10
+ private readonly registry;
11
+ private readonly shared;
12
+ constructor(registry: OpenAPIRegistry);
13
+ /** A hand-written OpenAPI component. The caller owns name uniqueness. */
14
+ add(name: string, component: SchemaObject): ReferenceObject;
15
+ /** A zod schema shared by several paths, registered the first time it is referenced. */
16
+ reuse(name: string, schema: z.ZodType): ReferenceObject;
17
+ }
18
+ //# sourceMappingURL=component-pool.d.ts.map
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const SCHEMA_PREFIX = '#/components/schemas/';
4
+ /**
5
+ * Registers schema components on demand. Nothing is registered eagerly: a component no path
6
+ * references trips redocly's unused-component rule, and which shared pieces an unfolded document
7
+ * needs depends on the schema it was built from.
8
+ */
9
+ class ComponentPool {
10
+ constructor(registry) {
11
+ this.shared = new Set();
12
+ this.registry = registry;
13
+ }
14
+ /** A hand-written OpenAPI component. The caller owns name uniqueness. */
15
+ add(name, component) {
16
+ return this.registry.registerComponent('schemas', name, component).ref;
17
+ }
18
+ /** A zod schema shared by several paths, registered the first time it is referenced. */
19
+ reuse(name, schema) {
20
+ if (!this.shared.has(name)) {
21
+ this.registry.register(name, schema);
22
+ this.shared.add(name);
23
+ }
24
+ return { $ref: `${SCHEMA_PREFIX}${name}` };
25
+ }
26
+ }
27
+ exports.default = ComponentPool;
28
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcG9uZW50LXBvb2wuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3BlbmFwaS9jb21wb25lbnQtcG9vbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUlBLE1BQU0sYUFBYSxHQUFHLHVCQUF1QixDQUFDO0FBRTlDOzs7O0dBSUc7QUFDSCxNQUFxQixhQUFhO0lBSWhDLFlBQVksUUFBeUI7UUFGcEIsV0FBTSxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7UUFHMUMsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUM7SUFDM0IsQ0FBQztJQUVELHlFQUF5RTtJQUN6RSxHQUFHLENBQUMsSUFBWSxFQUFFLFNBQXVCO1FBQ3ZDLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTLEVBQUUsSUFBSSxFQUFFLFNBQVMsQ0FBQyxDQUFDLEdBQUcsQ0FBQztJQUN6RSxDQUFDO0lBRUQsd0ZBQXdGO0lBQ3hGLEtBQUssQ0FBQyxJQUFZLEVBQUUsTUFBaUI7UUFDbkMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDM0IsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBQ3JDLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3hCLENBQUM7UUFFRCxPQUFPLEVBQUUsSUFBSSxFQUFFLEdBQUcsYUFBYSxHQUFHLElBQUksRUFBRSxFQUFFLENBQUM7SUFDN0MsQ0FBQztDQUNGO0FBdEJELGdDQXNCQyJ9
@@ -0,0 +1,9 @@
1
+ import type { FieldType } from '../read-model/capabilities-cache';
2
+ import type { SchemaObject } from 'openapi3-ts/oas31';
3
+ /**
4
+ * Turns a Forest column type into a JSON Schema. Anything unrecognized — a relation marker such as
5
+ * `ManyToOne`, or a type a newer agent introduced — maps to an unconstrained schema rather than a
6
+ * guess, so the document never rejects a value the runtime accepts.
7
+ */
8
+ export default function toFieldSchema(type: FieldType): SchemaObject;
9
+ //# sourceMappingURL=field-schemas.d.ts.map
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = toFieldSchema;
4
+ // Forest primitives, mapped to the closest JSON Schema shape. `Json` maps to no constraint at all,
5
+ // which is honest: any JSON value is accepted there.
6
+ const PRIMITIVE_SCHEMAS = {
7
+ Binary: { type: 'string' },
8
+ Boolean: { type: 'boolean' },
9
+ Date: { type: 'string', format: 'date-time' },
10
+ Dateonly: { type: 'string', format: 'date' },
11
+ Enum: { type: 'string' },
12
+ File: { type: 'string', description: 'A data URI.' },
13
+ Json: {},
14
+ Number: { type: 'number' },
15
+ Point: { type: 'string' },
16
+ String: { type: 'string' },
17
+ Time: { type: 'string' },
18
+ Timeonly: { type: 'string' },
19
+ Uuid: { type: 'string', format: 'uuid' },
20
+ };
21
+ /**
22
+ * Turns a Forest column type into a JSON Schema. Anything unrecognized — a relation marker such as
23
+ * `ManyToOne`, or a type a newer agent introduced — maps to an unconstrained schema rather than a
24
+ * guess, so the document never rejects a value the runtime accepts.
25
+ */
26
+ function toFieldSchema(type) {
27
+ if (Array.isArray(type)) {
28
+ const [item] = type;
29
+ return { type: 'array', items: item === undefined ? {} : toFieldSchema(item) };
30
+ }
31
+ // Null-checked, and its elements checked rather than trusted: the payload a column type comes from
32
+ // is cast from untyped JSON, so nothing guarantees the declared shape. A malformed entry is dropped
33
+ // instead of aborting the whole document — one bad field is not worth losing the spec over.
34
+ if (typeof type === 'object' && type !== null && Array.isArray(type.fields)) {
35
+ const named = type.fields.filter(field => typeof field === 'object' && field !== null && typeof field.field === 'string');
36
+ return {
37
+ type: 'object',
38
+ properties: Object.fromEntries(named.map(field => [field.field, toFieldSchema(field.type)])),
39
+ };
40
+ }
41
+ return typeof type === 'string' ? PRIMITIVE_SCHEMAS[type] ?? {} : {};
42
+ }
43
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmllbGQtc2NoZW1hcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9vcGVuYXBpL2ZpZWxkLXNjaGVtYXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUEwQkEsZ0NBc0JDO0FBN0NELG1HQUFtRztBQUNuRyxxREFBcUQ7QUFDckQsTUFBTSxpQkFBaUIsR0FBaUM7SUFDdEQsTUFBTSxFQUFFLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRTtJQUMxQixPQUFPLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFO0lBQzVCLElBQUksRUFBRSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLFdBQVcsRUFBRTtJQUM3QyxRQUFRLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUU7SUFDNUMsSUFBSSxFQUFFLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRTtJQUN4QixJQUFJLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLFdBQVcsRUFBRSxhQUFhLEVBQUU7SUFDcEQsSUFBSSxFQUFFLEVBQUU7SUFDUixNQUFNLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFO0lBQzFCLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUU7SUFDekIsTUFBTSxFQUFFLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRTtJQUMxQixJQUFJLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFO0lBQ3hCLFFBQVEsRUFBRSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUU7SUFDNUIsSUFBSSxFQUFFLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFO0NBQ3pDLENBQUM7QUFFRjs7OztHQUlHO0FBQ0gsU0FBd0IsYUFBYSxDQUFDLElBQWU7SUFDbkQsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7UUFDeEIsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLElBQUksQ0FBQztRQUVwQixPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsSUFBSSxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztJQUNqRixDQUFDO0lBRUQsbUdBQW1HO0lBQ25HLG9HQUFvRztJQUNwRyw0RkFBNEY7SUFDNUYsSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksSUFBSSxLQUFLLElBQUksSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1FBQzVFLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUM5QixLQUFLLENBQUMsRUFBRSxDQUFDLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLE9BQU8sS0FBSyxDQUFDLEtBQUssS0FBSyxRQUFRLENBQ3hGLENBQUM7UUFFRixPQUFPO1lBQ0wsSUFBSSxFQUFFLFFBQVE7WUFDZCxVQUFVLEVBQUUsTUFBTSxDQUFDLFdBQVcsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLGFBQWEsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQzdGLENBQUM7SUFDSixDQUFDO0lBRUQsT0FBTyxPQUFPLElBQUksS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0FBQ3ZFLENBQUMifQ==
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Collection, relation and action names are arbitrary customer strings: spaces, dots, slashes,
3
+ * accents. A path segment carries them URL-encoded, but an `operationId` cannot — redocly rejects a
4
+ * non-URL-safe one, and a codegen tool turns it into a method name. So identifiers are sanitized,
5
+ * and the exact name is carried in prose instead.
6
+ */
7
+ export declare function sanitizeIdentifier(raw: string): string;
8
+ /**
9
+ * Hands out unique sanitized identifiers. Sanitizing collapses distinct names (`Mark as paid` and
10
+ * `Mark-as-paid` both become `Mark_as_paid`), so the second one gets a numeric suffix. Callers must
11
+ * feed names in a stable order — the suffix depends on it, and two runs over one schema have to
12
+ * produce the same document.
13
+ */
14
+ export type Namer = (raw: string) => string;
15
+ export default function createNamer(): Namer;
16
+ //# sourceMappingURL=names.d.ts.map
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sanitizeIdentifier = sanitizeIdentifier;
4
+ exports.default = createNamer;
5
+ /**
6
+ * Collection, relation and action names are arbitrary customer strings: spaces, dots, slashes,
7
+ * accents. A path segment carries them URL-encoded, but an `operationId` cannot — redocly rejects a
8
+ * non-URL-safe one, and a codegen tool turns it into a method name. So identifiers are sanitized,
9
+ * and the exact name is carried in prose instead.
10
+ */
11
+ function sanitizeIdentifier(raw) {
12
+ return raw.replace(/[^A-Za-z0-9_]/g, '_') || '_';
13
+ }
14
+ function createNamer() {
15
+ const used = new Set();
16
+ return (raw) => {
17
+ const base = sanitizeIdentifier(raw);
18
+ let candidate = base;
19
+ let counter = 2;
20
+ while (used.has(candidate)) {
21
+ candidate = `${base}_${counter}`;
22
+ counter += 1;
23
+ }
24
+ used.add(candidate);
25
+ return candidate;
26
+ };
27
+ }
28
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmFtZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3BlbmFwaS9uYW1lcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQU1BLGdEQUVDO0FBVUQsOEJBaUJDO0FBbkNEOzs7OztHQUtHO0FBQ0gsU0FBZ0Isa0JBQWtCLENBQUMsR0FBVztJQUM1QyxPQUFPLEdBQUcsQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLEVBQUUsR0FBRyxDQUFDLElBQUksR0FBRyxDQUFDO0FBQ25ELENBQUM7QUFVRCxTQUF3QixXQUFXO0lBQ2pDLE1BQU0sSUFBSSxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFFL0IsT0FBTyxDQUFDLEdBQVcsRUFBVSxFQUFFO1FBQzdCLE1BQU0sSUFBSSxHQUFHLGtCQUFrQixDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3JDLElBQUksU0FBUyxHQUFHLElBQUksQ0FBQztRQUNyQixJQUFJLE9BQU8sR0FBRyxDQUFDLENBQUM7UUFFaEIsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7WUFDM0IsU0FBUyxHQUFHLEdBQUcsSUFBSSxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ2pDLE9BQU8sSUFBSSxDQUFDLENBQUM7UUFDZixDQUFDO1FBRUQsSUFBSSxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUVwQixPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDLENBQUM7QUFDSixDQUFDIn0=
@@ -1,6 +1,7 @@
1
+ import type { Unfolding } from './unfolding';
1
2
  import type { OpenAPIObject } from 'openapi3-ts/oas31';
2
3
  export declare const OPENAPI_VERSION = "3.1.0";
3
4
  export declare const ROUTE_PREFIX = "/agent/v1";
4
- export declare function generateOpenApiDocument(version: string): OpenAPIObject;
5
+ export declare function generateOpenApiDocument(version: string, unfolding?: Unfolding): OpenAPIObject;
5
6
  export declare function serializeOpenApi(document: OpenAPIObject): string;
6
7
  //# sourceMappingURL=openapi-document.d.ts.map
@@ -7,7 +7,9 @@ exports.ROUTE_PREFIX = exports.OPENAPI_VERSION = void 0;
7
7
  exports.generateOpenApiDocument = generateOpenApiDocument;
8
8
  exports.serializeOpenApi = serializeOpenApi;
9
9
  const zod_to_openapi_1 = require("@asteasolutions/zod-to-openapi");
10
+ const component_pool_1 = __importDefault(require("./component-pool"));
10
11
  const schemas_1 = require("./schemas");
12
+ const unfolded_paths_1 = __importDefault(require("./unfolded-paths"));
11
13
  const zod_openapi_1 = require("./zod-openapi");
12
14
  const body_limit_1 = __importDefault(require("../http/body-limit"));
13
15
  exports.OPENAPI_VERSION = '3.1.0';
@@ -31,39 +33,53 @@ const ERROR_STATUSES = {
31
33
  };
32
34
  const UNSUPPORTED_RESULT_DESCRIPTION = 'Either the BFF runs without an agent configured, or the action returned a result shape the ' +
33
35
  'BFF cannot normalize. The second case carries no message field.';
34
- function errorResponses(statuses, executeResults) {
35
- return Object.fromEntries(statuses.map(status => {
36
+ const ERROR_RESPONSE_REF = '#/components/schemas/ErrorResponse';
37
+ const MESSAGELESS_ERROR_RESPONSE_REF = '#/components/schemas/MessagelessErrorResponse';
38
+ const UNSUPPORTED_ACTION_RESULT_COMPONENT = 'UnsupportedActionResult';
39
+ const RETRY_AFTER_HEADER = {
40
+ 'Retry-After': {
41
+ description: 'Seconds to wait before retrying. Set when the API key could not be resolved.',
42
+ required: false,
43
+ schema: { type: 'integer' },
44
+ },
45
+ };
46
+ // Every error body is identical across paths, and their descriptions are long: inlining them costs
47
+ // ~2.8kB per path, which the unfolded document multiplies by every collection, relation and action.
48
+ // Registering them once as components keeps a path's error block to a dozen refs.
49
+ function registerErrorResponses(registry, statuses, withActionResults) {
50
+ registry.register('ErrorResponse', schemas_1.ErrorResponseSchema);
51
+ const byStatus = {};
52
+ statuses.forEach(status => {
36
53
  const description = ERROR_STATUSES[status];
37
54
  if (!description)
38
55
  throw new Error(`No OpenAPI description for error status ${status}`);
39
- if (status === '501' && executeResults) {
40
- return [
41
- status,
42
- {
43
- description: UNSUPPORTED_RESULT_DESCRIPTION,
44
- content: {
45
- 'application/json': {
46
- schema: zod_openapi_1.z.union([schemas_1.ErrorResponseSchema, schemas_1.MessagelessErrorResponseSchema]),
47
- },
48
- },
49
- },
50
- ];
51
- }
52
- const response = {
56
+ byStatus[status] = registry.registerComponent('responses', `Error${status}`, {
53
57
  description,
54
- content: { 'application/json': { schema: schemas_1.ErrorResponseSchema } },
55
- };
56
- if (status === '503') {
57
- response.headers = {
58
- 'Retry-After': {
59
- description: 'Seconds to wait before retrying. Set when the API key could not be resolved.',
60
- required: false,
61
- schema: { type: 'integer' },
58
+ content: { 'application/json': { schema: { $ref: ERROR_RESPONSE_REF } } },
59
+ ...(status === '503' ? { headers: RETRY_AFTER_HEADER } : {}),
60
+ }).ref;
61
+ });
62
+ // A document with no action path must not carry this response, nor the messageless body it
63
+ // references: an unreferenced component trips redocly's unused-component rule.
64
+ if (!withActionResults)
65
+ return { byStatus, unsupportedActionResult: byStatus['501'] };
66
+ registry.register('MessagelessErrorResponse', schemas_1.MessagelessErrorResponseSchema);
67
+ const unsupportedActionResult = registry.registerComponent('responses', UNSUPPORTED_ACTION_RESULT_COMPONENT, {
68
+ description: UNSUPPORTED_RESULT_DESCRIPTION,
69
+ content: {
70
+ 'application/json': {
71
+ schema: {
72
+ anyOf: [{ $ref: ERROR_RESPONSE_REF }, { $ref: MESSAGELESS_ERROR_RESPONSE_REF }],
62
73
  },
63
- };
64
- }
65
- return [status, response];
66
- }));
74
+ },
75
+ },
76
+ }).ref;
77
+ return { byStatus, unsupportedActionResult };
78
+ }
79
+ function errorResponses(refs, executeResults) {
80
+ return executeResults
81
+ ? { ...refs.byStatus, 501: refs.unsupportedActionResult }
82
+ : { ...refs.byStatus };
67
83
  }
68
84
  const DATA_ERRORS = [
69
85
  '400',
@@ -148,17 +164,42 @@ const PARAM_DESCRIPTIONS = {
148
164
  function buildParams(names) {
149
165
  return zod_openapi_1.z.object(Object.fromEntries(names.map(name => [name, zod_openapi_1.z.string().openapi({ description: PARAM_DESCRIPTIONS[name] })])));
150
166
  }
151
- const TIMEZONE_HEADER_PARAM = zod_openapi_1.z.object({
152
- 'X-Forest-Timezone': zod_openapi_1.z
153
- .string()
154
- .optional()
155
- .openapi({
156
- description: 'An IANA timezone. Takes precedence over the `timezone` body field; the BFF default ' +
157
- 'applies when neither is sent.',
158
- }),
167
+ // Registered as a parameter component rather than inlined: it is the same header on every path, and
168
+ // its description costs a quarter of a path once the error responses are refs.
169
+ const TIMEZONE_HEADER_COMPONENT = 'XForestTimezone';
170
+ const TIMEZONE_HEADER = zod_openapi_1.z
171
+ .string()
172
+ .optional()
173
+ .openapi({
174
+ param: { name: 'X-Forest-Timezone', in: 'header' },
175
+ description: 'An IANA timezone. Takes precedence over the `timezone` body field; the BFF default ' +
176
+ 'applies when neither is sent.',
159
177
  });
160
- function generateOpenApiDocument(version) {
178
+ const SHARED_DESCRIPTION = 'The timezone is resolved from the `X-Forest-Timezone` header first, then a `timezone` body ' +
179
+ 'field, then the BFF default when one is configured. A deployment without a default rejects a ' +
180
+ 'request carrying neither with 400 missing_timezone, so send one of the two to be safe. Sending ' +
181
+ 'a content type other than application/json is not an error: a form-urlencoded body is parsed ' +
182
+ 'like JSON, while any other content type is read as absent, which silently drops any filter, ' +
183
+ 'sort, or page.';
184
+ const GENERIC_DESCRIPTION = 'Paths are generic: one per operation, with the collection, relation and action passed as path ' +
185
+ 'segments, and no field enumerated. This is the fallback form — a deployment configured to ' +
186
+ 'reach its Forest schema and its agent unfolds one path per real collection, relation and ' +
187
+ 'action instead, each carrying its own field set.';
188
+ const UNFOLDED_DESCRIPTION = 'Paths are unfolded: one per exposed collection, to-many relation and action, each carrying the ' +
189
+ "collection's real field set. The RUNTIME routes stay generic — a path here is the generic " +
190
+ 'route with its segments already filled in, so the collection, relation and action segments are ' +
191
+ 'the exact schema names, URL-encoded. Every operationId is sanitized to stay usable as a ' +
192
+ 'method name, so the exact name lives in the summary and description. A path whose fields are ' +
193
+ 'not enumerated says so in its request description: the document was built without that ' +
194
+ "collection's capabilities. Only to-many relations appear; a to-one or polymorphic relation has " +
195
+ 'no list or count route. This document describes the whole exposed schema regardless of the ' +
196
+ 'caller: it is not filtered by the permissions of whoever fetched it.';
197
+ function generateOpenApiDocument(version, unfolding) {
161
198
  const registry = new zod_to_openapi_1.OpenAPIRegistry();
199
+ const hasActions = unfolding === undefined ||
200
+ unfolding.collections.some(collection => collection.actions.length > 0);
201
+ const errorRefs = registerErrorResponses(registry, DATA_ERRORS, hasActions);
202
+ const timezoneHeader = [registry.registerParameter(TIMEZONE_HEADER_COMPONENT, TIMEZONE_HEADER)];
162
203
  registry.registerComponent('securitySchemes', SESSION_SCHEME, {
163
204
  type: 'http',
164
205
  scheme: 'bearer',
@@ -172,44 +213,49 @@ function generateOpenApiDocument(version) {
172
213
  name: 'X-Forest-Bff-Key',
173
214
  description: 'Mode 2: a BFF API key. Never send both this and an Authorization header.',
174
215
  });
175
- ROUTES.forEach(route => {
176
- registry.registerPath({
177
- method: 'post',
178
- path: `${exports.ROUTE_PREFIX}${route.path}`,
179
- operationId: route.operationId,
180
- summary: route.summary,
216
+ if (unfolding) {
217
+ (0, unfolded_paths_1.default)({
218
+ registry,
219
+ pool: new component_pool_1.default(registry),
220
+ prefix: exports.ROUTE_PREFIX,
181
221
  security: SECURITY,
182
- request: {
183
- params: buildParams(route.params),
184
- headers: TIMEZONE_HEADER_PARAM,
185
- body: {
186
- required: route.bodyRequired === true,
187
- content: { 'application/json': { schema: route.request } },
222
+ timezoneHeader,
223
+ errorResponses: executeResults => errorResponses(errorRefs, executeResults),
224
+ }, unfolding);
225
+ }
226
+ else {
227
+ ROUTES.forEach(route => {
228
+ registry.registerPath({
229
+ method: 'post',
230
+ path: `${exports.ROUTE_PREFIX}${route.path}`,
231
+ operationId: route.operationId,
232
+ summary: route.summary,
233
+ security: SECURITY,
234
+ request: {
235
+ params: buildParams(route.params),
236
+ headers: timezoneHeader,
237
+ body: {
238
+ required: route.bodyRequired === true,
239
+ content: { 'application/json': { schema: route.request } },
240
+ },
188
241
  },
189
- },
190
- responses: {
191
- 200: {
192
- description: route.responseDescription,
193
- content: { 'application/json': { schema: route.response } },
242
+ responses: {
243
+ 200: {
244
+ description: route.responseDescription,
245
+ content: { 'application/json': { schema: route.response } },
246
+ },
247
+ ...errorResponses(errorRefs, route.executeResults === true),
194
248
  },
195
- ...errorResponses(DATA_ERRORS, route.executeResults === true),
196
- },
249
+ });
197
250
  });
198
- });
251
+ }
199
252
  return new zod_to_openapi_1.OpenApiGeneratorV31(registry.definitions).generateDocument({
200
253
  openapi: exports.OPENAPI_VERSION,
201
254
  info: {
202
255
  title: 'Forest Admin BFF',
203
256
  version,
204
257
  license: { name: 'GPL-3.0', url: 'https://www.gnu.org/licenses/gpl-3.0.html' },
205
- description: 'Runtime routes are generic: one path per operation, with the collection, relation and ' +
206
- 'action passed as path segments. The timezone is resolved from the `X-Forest-Timezone` ' +
207
- 'header first, then a `timezone` body field, then the BFF default when one is ' +
208
- 'configured. A deployment without a default rejects a request carrying neither with ' +
209
- '400 missing_timezone, so send one of the two to be safe. Sending a content type other ' +
210
- 'than application/json is not an error: a form-urlencoded body is parsed like JSON, ' +
211
- 'while any other content type is read as absent, which silently drops any filter, ' +
212
- 'sort, or page.',
258
+ description: `${unfolding ? UNFOLDED_DESCRIPTION : GENERIC_DESCRIPTION} ${SHARED_DESCRIPTION}`,
213
259
  },
214
260
  servers: [{ url: '/' }],
215
261
  });
@@ -217,4 +263,4 @@ function generateOpenApiDocument(version) {
217
263
  function serializeOpenApi(document) {
218
264
  return JSON.stringify(document, null, 2);
219
265
  }
220
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbmFwaS1kb2N1bWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9vcGVuYXBpL29wZW5hcGktZG9jdW1lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBeU1BLDBEQTZEQztBQUVELDRDQUVDO0FBeFFELG1FQUFzRjtBQUV0Rix1Q0FVbUI7QUFDbkIsK0NBQWtDO0FBQ2xDLG9FQUE0QztBQUUvQixRQUFBLGVBQWUsR0FBRyxPQUFPLENBQUM7QUFDMUIsUUFBQSxZQUFZLEdBQUcsV0FBVyxDQUFDO0FBRXhDLE1BQU0sY0FBYyxHQUFHLFlBQVksQ0FBQztBQUNwQyxNQUFNLGNBQWMsR0FBRyxXQUFXLENBQUM7QUFFbkMsTUFBTSxRQUFRLEdBQUcsQ0FBQyxFQUFFLENBQUMsY0FBYyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztBQUU1QyxNQUFNLGNBQWMsR0FBMkI7SUFDN0MsR0FBRyxFQUFFLHlRQUF5UTtJQUM5USxHQUFHLEVBQUUsMENBQTBDO0lBQy9DLEdBQUcsRUFBRSwwT0FBME87SUFDL08sR0FBRyxFQUFFLHlDQUF5QztJQUM5QyxHQUFHLEVBQUUsNkNBQTZDLG9CQUFVLEVBQUU7SUFDOUQsR0FBRyxFQUFFLDJWQUEyVjtJQUNoVyxHQUFHLEVBQUUsa0VBQWtFO0lBQ3ZFLEdBQUcsRUFBRSxvQ0FBb0M7SUFDekMsR0FBRyxFQUFFLDJEQUEyRDtJQUNoRSxHQUFHLEVBQUUsaUZBQWlGO0lBQ3RGLEdBQUcsRUFBRSxnQ0FBZ0M7SUFDckMsR0FBRyxFQUFFLGlHQUFpRztDQUN2RyxDQUFDO0FBRUYsTUFBTSw4QkFBOEIsR0FDbEMsNkZBQTZGO0lBQzdGLGlFQUFpRSxDQUFDO0FBRXBFLFNBQVMsY0FBYyxDQUFDLFFBQWtCLEVBQUUsY0FBdUI7SUFDakUsT0FBTyxNQUFNLENBQUMsV0FBVyxDQUN2QixRQUFRLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFO1FBQ3BCLE1BQU0sV0FBVyxH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
266
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbmFwaS1kb2N1bWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9vcGVuYXBpL29wZW5hcGktZG9jdW1lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBMlFBLDBEQTBFQztBQUVELDRDQUVDO0FBdFZELG1FQUFzRjtBQUV0RixzRUFBNkM7QUFDN0MsdUNBVW1CO0FBQ25CLHNFQUFxRDtBQUNyRCwrQ0FBa0M7QUFDbEMsb0VBQTRDO0FBRS9CLFFBQUEsZUFBZSxHQUFHLE9BQU8sQ0FBQztBQUMxQixRQUFBLFlBQVksR0FBRyxXQUFXLENBQUM7QUFFeEMsTUFBTSxjQUFjLEdBQUcsWUFBWSxDQUFDO0FBQ3BDLE1BQU0sY0FBYyxHQUFHLFdBQVcsQ0FBQztBQUVuQyxNQUFNLFFBQVEsR0FBRyxDQUFDLEVBQUUsQ0FBQyxjQUFjLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBRTVDLE1BQU0sY0FBYyxHQUEyQjtJQUM3QyxHQUFHLEVBQUUseVFBQXlRO0lBQzlRLEdBQUcsRUFBRSwwQ0FBMEM7SUFDL0MsR0FBRyxFQUFFLDBPQUEwTztJQUMvTyxHQUFHLEVBQUUseUNBQXlDO0lBQzlDLEdBQUcsRUFBRSw2Q0FBNkMsb0JBQVUsRUFBRTtJQUM5RCxHQUFHLEVBQUUsMlZBQTJWO0lBQ2hXLEdBQUcsRUFBRSxrRUFBa0U7SUFDdkUsR0FBRyxFQUFFLG9DQUFvQztJQUN6QyxHQUFHLEVBQUUsMkRBQTJEO0lBQ2hFLEdBQUcsRUFBRSxpRkFBaUY7SUFDdEYsR0FBRyxFQUFFLGdDQUFnQztJQUNyQyxHQUFHLEVBQUUsaUdBQWlHO0NBQ3ZHLENBQUM7QUFFRixNQUFNLDhCQUE4QixHQUNsQyw2RkFBNkY7SUFDN0YsaUVBQWlFLENBQUM7QUFFcEUsTUFBTSxrQkFBa0IsR0FBRyxvQ0FBb0MsQ0FBQztBQUNoRSxNQUFNLDhCQUE4QixHQUFHLCtDQUErQyxDQUFDO0FBRXZGLE1BQU0sbUNBQW1DLEdBQUcseUJBQXlCLENBQUM7QUFFdEUsTUFBTSxrQkFBa0IsR0FBRztJQUN6QixhQUFhLEVBQUU7UUFDYixXQUFXLEVBQUUsOEVBQThFO1FBQzNGLFFBQVEsRUFBRSxLQUFLO1FBQ2YsTUFBTSxFQUFFLEVBQUUsSUFBSSxFQUFFLFNBQWtCLEVBQUU7S0FDckM7Q0FDRixDQUFDO0FBU0YsbUdBQW1HO0FBQ25HLG9HQUFvRztBQUNwRyxrRkFBa0Y7QUFDbEYsU0FBUyxzQkFBc0IsQ0FDN0IsUUFBeUIsRUFDekIsUUFBa0IsRUFDbEIsaUJBQTBCO0lBRTFCLFFBQVEsQ0FBQyxRQUFRLENBQUMsZUFBZSxFQUFFLDZCQUFtQixDQUFDLENBQUM7SUFFeEQsTUFBTSxRQUFRLEdBQWdDLEVBQUUsQ0FBQztJQUVqRCxRQUFRLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUFFO1FBQ3hCLE1BQU0sV0FBVyxHQUFHLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUUzQyxJQUFJLENBQUMsV0FBVztZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsMkNBQTJDLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFFdkYsUUFBUSxDQUFDLE1BQU0sQ0FBQyxHQUFHLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxXQUFXLEVBQUUsUUFBUSxNQUFNLEVBQUUsRUFBRTtZQUMzRSxXQUFXO1lBQ1gsT0FBTyxFQUFFLEVBQUUsa0JBQWtCLEVBQUUsRUFBRSxNQUFNLEVBQUUsRUFBRSxJQUFJLEVBQUUsa0JBQWtCLEVBQUUsRUFBRSxFQUFFO1lBQ3pFLEdBQUcsQ0FBQyxNQUFNLEtBQUssS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7U0FDN0QsQ0FBQyxDQUFDLEdBQUcsQ0FBQztJQUNULENBQUMsQ0FBQyxDQUFDO0lBRUgsMkZBQTJGO0lBQzNGLCtFQUErRTtJQUMvRSxJQUFJLENBQUMsaUJBQWlCO1FBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSx1QkFBdUIsRUFBRSxRQUFRLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztJQUV0RixRQUFRLENBQUMsUUFBUSxDQUFDLDBCQUEwQixFQUFFLHdDQUE4QixDQUFDLENBQUM7SUFFOUUsTUFBTSx1QkFBdUIsR0FBRyxRQUFRLENBQUMsaUJBQWlCLENBQ3hELFdBQVcsRUFDWCxtQ0FBbUMsRUFDbkM7UUFDRSxXQUFXLEVBQUUsOEJBQThCO1FBQzNDLE9BQU8sRUFBRTtZQUNQLGtCQUFrQixFQUFFO2dCQUNsQixNQUFNLEVBQUU7b0JBQ04sS0FBSyxFQUFFLENBQUMsRUFBRSxJQUFJLEVBQUUsa0JBQWtCLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSw4QkFBOEIsRUFBRSxDQUFDO2lCQUNoRjthQUNGO1NBQ0Y7S0FDRixDQUNGLENBQUMsR0FBRyxDQUFDO0lBRU4sT0FBTyxFQUFFLFFBQVEsRUFBRSx1QkFBdUIsRUFBRSxDQUFDO0FBQy9DLENBQUM7QUFFRCxTQUFTLGNBQWMsQ0FDckIsSUFBdUIsRUFDdkIsY0FBdUI7SUFFdkIsT0FBTyxjQUFjO1FBQ25CLENBQUMsQ0FBQyxFQUFFLEdBQUcsSUFBSSxDQUFDLFFBQVEsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLHVCQUF1QixFQUFFO1FBQ3pELENBQUMsQ0FBQyxFQUFFLEdBQUcsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO0FBQzNCLENBQUM7QUFFRCxNQUFNLFdBQVcsR0FBRztJQUNsQixLQUFLO0lBQ0wsS0FBSztJQUNMLEtBQUs7SUFDTCxLQUFLO0lBQ0wsS0FBSztJQUNMLEtBQUs7SUFDTCxLQUFLO0lBQ0wsS0FBSztJQUNMLEtBQUs7SUFDTCxLQUFLO0lBQ0wsS0FBSztJQUNMLEtBQUs7Q0FDTixDQUFDO0FBY0YsTUFBTSxNQUFNLEdBQXNCO0lBQ2hDO1FBQ0UsSUFBSSxFQUFFLG9CQUFvQjtRQUMxQixXQUFXLEVBQUUsYUFBYTtRQUMxQixPQUFPLEVBQUUsOEJBQThCO1FBQ3ZDLE9BQU8sRUFBRSwyQkFBaUI7UUFDMUIsUUFBUSxFQUFFLDRCQUFrQjtRQUM1QixtQkFBbUIsRUFBRSxtQkFBbUI7UUFDeEMsTUFBTSxFQUFFLENBQUMsWUFBWSxDQUFDO0tBQ3ZCO0lBQ0Q7UUFDRSxJQUFJLEVBQUUscUJBQXFCO1FBQzNCLFdBQVcsRUFBRSxjQUFjO1FBQzNCLE9BQU8sRUFBRSwrQkFBK0I7UUFDeEMsT0FBTyxFQUFFLDRCQUFrQjtRQUMzQixRQUFRLEVBQUUsNkJBQW1CO1FBQzdCLG1CQUFtQixFQUFFLDJCQUEyQjtRQUNoRCxNQUFNLEVBQUUsQ0FBQyxZQUFZLENBQUM7S0FDdkI7SUFDRDtRQUNFLElBQUksRUFBRSx5Q0FBeUM7UUFDL0MsV0FBVyxFQUFFLG9CQUFvQjtRQUNqQyxPQUFPLEVBQUUsb0NBQW9DO1FBQzdDLE9BQU8sRUFBRSxtQ0FBeUI7UUFDbEMsUUFBUSxFQUFFLDRCQUFrQjtRQUM1QixtQkFBbUIsRUFBRSwyQkFBMkI7UUFDaEQsTUFBTSxFQUFFLENBQUMsWUFBWSxFQUFFLFVBQVUsQ0FBQztRQUNsQyxZQUFZLEVBQUUsSUFBSTtLQUNuQjtJQUNEO1FBQ0UsSUFBSSxFQUFFLDBDQUEwQztRQUNoRCxXQUFXLEVBQUUscUJBQXFCO1FBQ2xDLE9BQU8sRUFBRSxxQ0FBcUM7UUFDOUMsT0FBTyxFQUFFLG9DQUEwQjtRQUNuQyxRQUFRLEVBQUUsNkJBQW1CO1FBQzdCLG1CQUFtQixFQUFFLG1DQUFtQztRQUN4RCxNQUFNLEVBQUUsQ0FBQyxZQUFZLEVBQUUsVUFBVSxDQUFDO1FBQ2xDLFlBQVksRUFBRSxJQUFJO0tBQ25CO0lBQ0Q7UUFDRSxJQUFJLEVBQUUscUNBQXFDO1FBQzNDLFdBQVcsRUFBRSxlQUFlO1FBQzVCLE9BQU8sRUFBRSxrQ0FBa0M7UUFDM0MsT0FBTyxFQUFFLDZCQUFtQjtRQUM1QixRQUFRLEVBQUUsZUFBQyxDQUFDLE9BQU8sRUFBRTtRQUNyQixtQkFBbUIsRUFBRSx3QkFBd0I7UUFDN0MsTUFBTSxFQUFFLENBQUMsWUFBWSxFQUFFLFFBQVEsQ0FBQztRQUNoQyxZQUFZLEVBQUUsSUFBSTtLQUNuQjtJQUNEO1FBQ0UsSUFBSSxFQUFFLHdDQUF3QztRQUM5QyxXQUFXLEVBQUUsZUFBZTtRQUM1QixPQUFPLEVBQUUseUJBQXlCO1FBQ2xDLE9BQU8sRUFBRSw2QkFBbUI7UUFDNUIsUUFBUSxFQUFFLGVBQUMsQ0FBQyxPQUFPLEVBQUU7UUFDckIsbUJBQW1CLEVBQUUsOEJBQThCO1FBQ25ELE1BQU0sRUFBRSxDQUFDLFlBQVksRUFBRSxRQUFRLENBQUM7UUFDaEMsWUFBWSxFQUFFLElBQUk7UUFDbEIsY0FBYyxFQUFFLElBQUk7S0FDckI7Q0FDRixDQUFDO0FBRUYsTUFBTSxrQkFBa0IsR0FBMkI7SUFDakQsVUFBVSxFQUFFLGtEQUFrRDtJQUM5RCxRQUFRLEVBQUUsd0VBQXdFO0lBQ2xGLE1BQU0sRUFBRSxxRkFBcUY7Q0FDOUYsQ0FBQztBQUVGLFNBQVMsV0FBVyxDQUFDLEtBQWU7SUFDbEMsT0FBTyxlQUFDLENBQUMsTUFBTSxDQUNiLE1BQU0sQ0FBQyxXQUFXLENBQ2hCLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksRUFBRSxlQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsT0FBTyxDQUFDLEVBQUUsV0FBVyxFQUFFLGtCQUFrQixDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQ3pGLENBQ0YsQ0FBQztBQUNKLENBQUM7QUFFRCxvR0FBb0c7QUFDcEcsK0VBQStFO0FBQy9FLE1BQU0seUJBQXlCLEdBQUcsaUJBQWlCLENBQUM7QUFFcEQsTUFBTSxlQUFlLEdBQUcsZUFBQztLQUN0QixNQUFNLEVBQUU7S0FDUixRQUFRLEVBQUU7S0FDVixPQUFPLENBQUM7SUFDUCxLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsbUJBQW1CLEVBQUUsRUFBRSxFQUFFLFFBQVEsRUFBRTtJQUNsRCxXQUFXLEVBQ1QscUZBQXFGO1FBQ3JGLCtCQUErQjtDQUNsQyxDQUFDLENBQUM7QUFFTCxNQUFNLGtCQUFrQixHQUN0Qiw2RkFBNkY7SUFDN0YsK0ZBQStGO0lBQy9GLGlHQUFpRztJQUNqRywrRkFBK0Y7SUFDL0YsOEZBQThGO0lBQzlGLGdCQUFnQixDQUFDO0FBRW5CLE1BQU0sbUJBQW1CLEdBQ3ZCLGdHQUFnRztJQUNoRyw0RkFBNEY7SUFDNUYsMkZBQTJGO0lBQzNGLGtEQUFrRCxDQUFDO0FBRXJELE1BQU0sb0JBQW9CLEdBQ3hCLGlHQUFpRztJQUNqRyw0RkFBNEY7SUFDNUYsaUdBQWlHO0lBQ2pHLDBGQUEwRjtJQUMxRiwrRkFBK0Y7SUFDL0YseUZBQXlGO0lBQ3pGLGlHQUFpRztJQUNqRyw2RkFBNkY7SUFDN0Ysc0VBQXNFLENBQUM7QUFFekUsU0FBZ0IsdUJBQXVCLENBQUMsT0FBZSxFQUFFLFNBQXFCO0lBQzVFLE1BQU0sUUFBUSxHQUFHLElBQUksZ0NBQWUsRUFBRSxDQUFDO0lBQ3ZDLE1BQU0sVUFBVSxHQUNkLFNBQVMsS0FBSyxTQUFTO1FBQ3ZCLFNBQVMsQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDMUUsTUFBTSxTQUFTLEdBQUcsc0JBQXNCLENBQUMsUUFBUSxFQUFFLFdBQVcsRUFBRSxVQUFVLENBQUMsQ0FBQztJQUM1RSxNQUFNLGNBQWMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyx5QkFBeUIsRUFBRSxlQUFlLENBQUMsQ0FBQyxDQUFDO0lBRWhHLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxpQkFBaUIsRUFBRSxjQUFjLEVBQUU7UUFDNUQsSUFBSSxFQUFFLE1BQU07UUFDWixNQUFNLEVBQUUsUUFBUTtRQUNoQixXQUFXLEVBQ1QsMEZBQTBGO1lBQzFGLHVGQUF1RjtZQUN2Rix1RUFBdUU7S0FDMUUsQ0FBQyxDQUFDO0lBQ0gsUUFBUSxDQUFDLGlCQUFpQixDQUFDLGlCQUFpQixFQUFFLGNBQWMsRUFBRTtRQUM1RCxJQUFJLEVBQUUsUUFBUTtRQUNkLEVBQUUsRUFBRSxRQUFRO1FBQ1osSUFBSSxFQUFFLGtCQUFrQjtRQUN4QixXQUFXLEVBQUUsMEVBQTBFO0tBQ3hGLENBQUMsQ0FBQztJQUVILElBQUksU0FBUyxFQUFFLENBQUM7UUFDZCxJQUFBLHdCQUFxQixFQUNuQjtZQUNFLFFBQVE7WUFDUixJQUFJLEVBQUUsSUFBSSx3QkFBYSxDQUFDLFFBQVEsQ0FBQztZQUNqQyxNQUFNLEVBQUUsb0JBQVk7WUFDcEIsUUFBUSxFQUFFLFFBQVE7WUFDbEIsY0FBYztZQUNkLGNBQWMsRUFBRSxjQUFjLENBQUMsRUFBRSxDQUFDLGNBQWMsQ0FBQyxTQUFTLEVBQUUsY0FBYyxDQUFDO1NBQzVFLEVBQ0QsU0FBUyxDQUNWLENBQUM7SUFDSixDQUFDO1NBQU0sQ0FBQztRQUNOLE1BQU0sQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUU7WUFDckIsUUFBUSxDQUFDLFlBQVksQ0FBQztnQkFDcEIsTUFBTSxFQUFFLE1BQU07Z0JBQ2QsSUFBSSxFQUFFLEdBQUcsb0JBQVksR0FBRyxLQUFLLENBQUMsSUFBSSxFQUFFO2dCQUNwQyxXQUFXLEVBQUUsS0FBSyxDQUFDLFdBQVc7Z0JBQzlCLE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTztnQkFDdEIsUUFBUSxFQUFFLFFBQVE7Z0JBQ2xCLE9BQU8sRUFBRTtvQkFDUCxNQUFNLEVBQUUsV0FBVyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUM7b0JBQ2pDLE9BQU8sRUFBRSxjQUFjO29CQUN2QixJQUFJLEVBQUU7d0JBQ0osUUFBUSxFQUFFLEtBQUssQ0FBQyxZQUFZLEtBQUssSUFBSTt3QkFDckMsT0FBTyxFQUFFLEVBQUUsa0JBQWtCLEVBQUUsRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLE9BQU8sRUFBRSxFQUFFO3FCQUMzRDtpQkFDRjtnQkFDRCxTQUFTLEVBQUU7b0JBQ1QsR0FBRyxFQUFFO3dCQUNILFdBQVcsRUFBRSxLQUFLLENBQUMsbUJBQW1CO3dCQUN0QyxPQUFPLEVBQUUsRUFBRSxrQkFBa0IsRUFBRSxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsUUFBUSxFQUFFLEVBQUU7cUJBQzVEO29CQUNELEdBQUcsY0FBYyxDQUFDLFNBQVMsRUFBRSxLQUFLLENBQUMsY0FBYyxLQUFLLElBQUksQ0FBQztpQkFDNUQ7YUFDRixDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxPQUFPLElBQUksb0NBQW1CLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxDQUFDLGdCQUFnQixDQUFDO1FBQ3BFLE9BQU8sRUFBRSx1QkFBZTtRQUN4QixJQUFJLEVBQUU7WUFDSixLQUFLLEVBQUUsa0JBQWtCO1lBQ3pCLE9BQU87WUFDUCxPQUFPLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSwyQ0FBMkMsRUFBRTtZQUM5RSxXQUFXLEVBQUUsR0FDWCxTQUFTLENBQUMsQ0FBQyxDQUFDLG9CQUFvQixDQUFDLENBQUMsQ0FBQyxtQkFDckMsSUFBSSxrQkFBa0IsRUFBRTtTQUN6QjtRQUNELE9BQU8sRUFBRSxDQUFDLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDO0tBQ3hCLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCxTQUFnQixnQkFBZ0IsQ0FBQyxRQUF1QjtJQUN0RCxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztBQUMzQyxDQUFDIn0=
@@ -1,8 +1,11 @@
1
+ import type { UnfoldSource } from './unfolded-document';
1
2
  import type { Middleware } from 'koa';
2
3
  export declare const OPENAPI_PATH = "/agent/openapi.json";
3
4
  export interface OpenApiRoutesOptions {
4
5
  version: string;
5
6
  enabled: boolean;
7
+ /** Absent (no agent or no read-model configuration) serves the generic document. */
8
+ source?: UnfoldSource;
6
9
  }
7
- export default function createOpenApiRoutes({ version, enabled, }: OpenApiRoutesOptions): Middleware;
10
+ export default function createOpenApiRoutes({ version, enabled, source, }: OpenApiRoutesOptions): Middleware;
8
11
  //# sourceMappingURL=openapi-routes.d.ts.map