@forestadmin/agent-bff 1.15.1 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -1
- package/dist/cli-core.d.ts +3 -0
- package/dist/cli-core.js +69 -10
- package/dist/cli-dispatch.d.ts +8 -2
- package/dist/cli-dispatch.js +90 -9
- package/dist/data/agent-query.d.ts +2 -0
- package/dist/data/agent-query.js +28 -17
- package/dist/data/data-routes-middleware.js +66 -26
- package/dist/data/pack-id.d.ts +1 -0
- package/dist/data/pack-id.js +4 -3
- package/dist/http/agent-error-mapper.js +15 -7
- package/dist/http/bff-http-error.d.ts +6 -1
- package/dist/http/bff-http-error.js +5 -4
- package/dist/http/bff-local-errors.d.ts +2 -0
- package/dist/http/bff-local-errors.js +12 -4
- package/dist/openapi/collect-unfolding.d.ts +23 -0
- package/dist/openapi/collect-unfolding.js +144 -0
- package/dist/openapi/component-pool.d.ts +18 -0
- package/dist/openapi/component-pool.js +28 -0
- package/dist/openapi/field-schemas.d.ts +9 -0
- package/dist/openapi/field-schemas.js +43 -0
- package/dist/openapi/names.d.ts +16 -0
- package/dist/openapi/names.js +28 -0
- package/dist/openapi/openapi-document.d.ts +2 -1
- package/dist/openapi/openapi-document.js +112 -66
- package/dist/openapi/openapi-routes.d.ts +4 -1
- package/dist/openapi/openapi-routes.js +46 -9
- package/dist/openapi/schemas.d.ts +14 -1
- package/dist/openapi/schemas.js +11 -10
- package/dist/openapi/unfolded-document.d.ts +28 -0
- package/dist/openapi/unfolded-document.js +52 -0
- package/dist/openapi/unfolded-paths.d.ts +20 -0
- package/dist/openapi/unfolded-paths.js +408 -0
- package/dist/openapi/unfolding.d.ts +66 -0
- package/dist/openapi/unfolding.js +17 -0
- package/dist/permissions/build-permission-hints.d.ts +40 -0
- package/dist/permissions/build-permission-hints.js +73 -0
- package/dist/permissions/can-role-perform.d.ts +12 -0
- package/dist/permissions/can-role-perform.js +18 -0
- package/dist/permissions/permissions-cache.d.ts +24 -0
- package/dist/permissions/permissions-cache.js +51 -0
- package/dist/permissions/permissions-client.d.ts +21 -0
- package/dist/permissions/permissions-client.js +27 -0
- package/dist/permissions/permissions-routes-middleware.d.ts +13 -0
- package/dist/permissions/permissions-routes-middleware.js +99 -0
- package/dist/read-model/agent-capabilities-fetcher.d.ts +8 -3
- package/dist/read-model/agent-capabilities-fetcher.js +15 -7
- package/dist/read-model/capabilities-cache.d.ts +11 -1
- package/dist/read-model/capabilities-cache.js +1 -1
- package/dist/read-model/read-model.d.ts +15 -0
- package/dist/read-model/read-model.js +39 -2
- package/dist/validation/capabilities-validator.d.ts +9 -0
- package/dist/validation/capabilities-validator.js +3 -1
- package/dist/validation/operator-normalizer.d.ts +6 -0
- package/dist/validation/operator-normalizer.js +11 -1
- package/dist/validation/validation-errors.js +7 -5
- package/package.json +1 -1
package/dist/openapi/schemas.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.OPERATORS = exports.ConditionTreeSchema = exports.MessagelessErrorResponseSchema = exports.ErrorResponseSchema = exports.CountResponseSchema = exports.ListResponseSchema = exports.ActionRequestSchema = exports.RelationCountRequestSchema = exports.RelationListRequestSchema = exports.CountRequestSchema = exports.ListRequestSchema = void 0;
|
|
3
|
+
exports.OPERATORS = exports.ParentIdSchema = exports.ConditionTreeSchema = exports.MessagelessErrorResponseSchema = exports.ErrorResponseSchema = exports.CountResponseSchema = exports.ListResponseSchema = exports.ActionRequestSchema = exports.RelationCountRequestSchema = exports.RelationListRequestSchema = exports.CountRequestSchema = exports.ListRequestSchema = exports.TimezoneSchema = exports.PageSchema = exports.SortClauseSchema = void 0;
|
|
4
4
|
const datasource_toolkit_1 = require("@forestadmin/datasource-toolkit");
|
|
5
5
|
const zod_openapi_1 = require("./zod-openapi");
|
|
6
6
|
const capabilities_validator_1 = require("../validation/capabilities-validator");
|
|
@@ -32,13 +32,13 @@ const ConditionTreeSchema = zod_openapi_1.z
|
|
|
32
32
|
'collection capabilities; relation list and count forward the filter without either check.',
|
|
33
33
|
});
|
|
34
34
|
exports.ConditionTreeSchema = ConditionTreeSchema;
|
|
35
|
-
|
|
35
|
+
exports.SortClauseSchema = zod_openapi_1.z
|
|
36
36
|
.object({
|
|
37
37
|
field: zod_openapi_1.z.string(),
|
|
38
38
|
direction: zod_openapi_1.z.enum(['asc', 'desc']).optional(),
|
|
39
39
|
})
|
|
40
40
|
.openapi('SortClause', { description: 'Omitting `direction` sorts ascending.' });
|
|
41
|
-
|
|
41
|
+
exports.PageSchema = zod_openapi_1.z
|
|
42
42
|
.object({
|
|
43
43
|
limit: zod_openapi_1.z.number().int().positive(),
|
|
44
44
|
offset: zod_openapi_1.z.number().int().nonnegative(),
|
|
@@ -47,7 +47,7 @@ const PageSchema = zod_openapi_1.z
|
|
|
47
47
|
description: 'The agent paginates by page number, so `offset` must be a whole multiple of `limit`. ' +
|
|
48
48
|
'Any other offset is rejected with 400 invalid_request.',
|
|
49
49
|
});
|
|
50
|
-
|
|
50
|
+
exports.TimezoneSchema = zod_openapi_1.z.string().openapi('Timezone', {
|
|
51
51
|
description: 'Used when the X-Forest-Timezone header is absent. The header wins when both are sent. A ' +
|
|
52
52
|
'deployment with no configured default rejects a request carrying neither with 400 ' +
|
|
53
53
|
'missing_timezone.',
|
|
@@ -56,21 +56,22 @@ exports.ListRequestSchema = zod_openapi_1.z
|
|
|
56
56
|
.object({
|
|
57
57
|
filter: ConditionTreeSchema.optional(),
|
|
58
58
|
projection: zod_openapi_1.z.array(zod_openapi_1.z.string()).optional(),
|
|
59
|
-
sort: zod_openapi_1.z.array(SortClauseSchema).optional(),
|
|
60
|
-
page: PageSchema.optional(),
|
|
61
|
-
timezone: TimezoneSchema.optional(),
|
|
59
|
+
sort: zod_openapi_1.z.array(exports.SortClauseSchema).optional(),
|
|
60
|
+
page: exports.PageSchema.optional(),
|
|
61
|
+
timezone: exports.TimezoneSchema.optional(),
|
|
62
62
|
})
|
|
63
63
|
.openapi('ListRequest');
|
|
64
64
|
exports.CountRequestSchema = zod_openapi_1.z
|
|
65
65
|
.object({
|
|
66
66
|
filter: ConditionTreeSchema.optional(),
|
|
67
|
-
timezone: TimezoneSchema.optional(),
|
|
67
|
+
timezone: exports.TimezoneSchema.optional(),
|
|
68
68
|
})
|
|
69
69
|
.openapi('CountRequest');
|
|
70
70
|
const ParentIdSchema = zod_openapi_1.z.union([zod_openapi_1.z.string().regex(/\S/), zod_openapi_1.z.number()]).openapi('ParentId', {
|
|
71
71
|
description: 'The parent record id, opaque: a composite or packed id must be passed unchanged. A ' +
|
|
72
72
|
'blank string is rejected.',
|
|
73
73
|
});
|
|
74
|
+
exports.ParentIdSchema = ParentIdSchema;
|
|
74
75
|
exports.RelationListRequestSchema = exports.ListRequestSchema.extend({
|
|
75
76
|
parentId: ParentIdSchema,
|
|
76
77
|
}).openapi('RelationListRequest', {
|
|
@@ -84,7 +85,7 @@ exports.ActionRequestSchema = zod_openapi_1.z
|
|
|
84
85
|
.object({
|
|
85
86
|
recordIds: zod_openapi_1.z.array(zod_openapi_1.z.union([zod_openapi_1.z.string(), zod_openapi_1.z.number()])),
|
|
86
87
|
values: zod_openapi_1.z.record(zod_openapi_1.z.string(), zod_openapi_1.z.unknown()).optional(),
|
|
87
|
-
timezone: TimezoneSchema.optional(),
|
|
88
|
+
timezone: exports.TimezoneSchema.optional(),
|
|
88
89
|
})
|
|
89
90
|
.openapi('ActionRequest', {
|
|
90
91
|
description: '`recordIds` is required, even on a global action: send an empty array when the action ' +
|
|
@@ -138,4 +139,4 @@ exports.MessagelessErrorResponseSchema = zod_openapi_1.z
|
|
|
138
139
|
.openapi('MessagelessErrorResponse', {
|
|
139
140
|
description: 'The unsupported-action-result body, which carries no message field unlike every other error.',
|
|
140
141
|
});
|
|
141
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
142
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9vcGVuYXBpL3NjaGVtYXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsd0VBQStEO0FBRS9ELCtDQUFrQztBQUNsQyxpRkFBd0U7QUFFeEUsTUFBTSxTQUFTLEdBQUcsQ0FBQyxHQUFHLGlDQUFZLENBQTBCLENBQUM7QUFvS2YsOEJBQVM7QUFsS3ZELE1BQU0sdUJBQXVCLEdBQUcsZUFBQztLQUM5QixNQUFNLENBQUM7SUFDTixLQUFLLEVBQUUsZUFBQyxDQUFDLE1BQU0sRUFBRTtJQUNqQixRQUFRLEVBQUUsZUFBQyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUM7SUFDM0IsS0FBSyxFQUFFLGVBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxRQUFRLEVBQUU7Q0FDOUIsQ0FBQztLQUNELE9BQU8sQ0FBQyxtQkFBbUIsRUFBRTtJQUM1QixXQUFXLEVBQ1QsMEZBQTBGO1FBQzFGLHFGQUFxRjtDQUN4RixDQUFDLENBQUM7QUFFTCxNQUFNLG1CQUFtQixHQUFjLGVBQUM7S0FDckMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUNULGVBQUMsQ0FBQyxLQUFLLENBQUM7SUFDTix1QkFBdUI7SUFDdkIsZUFBQyxDQUFDLE1BQU0sQ0FBQztRQUNQLFVBQVUsRUFBRSxlQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQ2pDLFVBQVUsRUFBRSxlQUFDLENBQUMsS0FBSyxDQUFDLG1CQUFtQixDQUFDO0tBQ3pDLENBQUM7Q0FDSCxDQUFDLENBQ0g7S0FDQSxPQUFPLENBQUMsZUFBZSxFQUFFO0lBQ3hCLFdBQVcsRUFDVCx1RkFBdUY7UUFDdkYsdUZBQXVGO1FBQ3ZGLHVGQUF1RjtRQUN2RixRQUFRLHlDQUFnQiw0REFBNEQ7UUFDcEYsMkZBQTJGO0NBQzlGLENBQUMsQ0FBQztBQXFJSSxrREFBbUI7QUFuSWYsUUFBQSxnQkFBZ0IsR0FB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
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Unfolding } from './unfolding';
|
|
2
|
+
import type { Logger } from '../ports/logger-port';
|
|
3
|
+
import type ReadModel from '../read-model/read-model';
|
|
4
|
+
import type ReadModelStore from '../read-model/read-model-store';
|
|
5
|
+
/** Everything needed to unfold. Absent when the deployment cannot reach its schema or its agent. */
|
|
6
|
+
export interface UnfoldSource {
|
|
7
|
+
store: ReadModelStore;
|
|
8
|
+
agentUrl: string;
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
logger: Logger;
|
|
11
|
+
}
|
|
12
|
+
export interface UnfoldedDocument {
|
|
13
|
+
document: string;
|
|
14
|
+
/** Handed back so a caller can react to a collection that went out without its field set. */
|
|
15
|
+
unfolding: Unfolding;
|
|
16
|
+
}
|
|
17
|
+
export default function buildUnfoldedDocument(source: UnfoldSource, readModel: ReadModel, token: string | (() => string), version: string): Promise<UnfoldedDocument>;
|
|
18
|
+
/**
|
|
19
|
+
* The CLI has no caller to borrow an agent token from, so it signs its own with the deployment's
|
|
20
|
+
* auth secret — the only thing the agent verifies, and the capabilities route it calls runs no
|
|
21
|
+
* permission check. The identity is inert on purpose: it is never used to read or write records.
|
|
22
|
+
*
|
|
23
|
+
* Minted per capabilities call rather than once: an agent token lives 5 minutes
|
|
24
|
+
* (`AGENT_TOKEN_EXPIRES_IN`) and a fan-out over hundreds of collections against a slow agent can
|
|
25
|
+
* outlive that, which would degrade the tail of the document for no reason.
|
|
26
|
+
*/
|
|
27
|
+
export declare function issueOpenApiAgentToken(authSecret: string): string;
|
|
28
|
+
//# sourceMappingURL=unfolded-document.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
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 = buildUnfoldedDocument;
|
|
7
|
+
exports.issueOpenApiAgentToken = issueOpenApiAgentToken;
|
|
8
|
+
const collect_unfolding_1 = __importDefault(require("./collect-unfolding"));
|
|
9
|
+
const openapi_document_1 = require("./openapi-document");
|
|
10
|
+
const agent_token_1 = require("../api-key/agent-token");
|
|
11
|
+
const agent_capabilities_fetcher_1 = __importDefault(require("../read-model/agent-capabilities-fetcher"));
|
|
12
|
+
async function buildUnfoldedDocument(source, readModel, token, version) {
|
|
13
|
+
const unfolding = await (0, collect_unfolding_1.default)({
|
|
14
|
+
readModel,
|
|
15
|
+
store: source.store,
|
|
16
|
+
capabilitiesFetcher: (0, agent_capabilities_fetcher_1.default)({
|
|
17
|
+
agentUrl: source.agentUrl,
|
|
18
|
+
token,
|
|
19
|
+
timeoutMs: source.timeoutMs,
|
|
20
|
+
}),
|
|
21
|
+
logger: source.logger,
|
|
22
|
+
});
|
|
23
|
+
return { document: (0, openapi_document_1.serializeOpenApi)((0, openapi_document_1.generateOpenApiDocument)(version, unfolding)), unfolding };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The CLI has no caller to borrow an agent token from, so it signs its own with the deployment's
|
|
27
|
+
* auth secret — the only thing the agent verifies, and the capabilities route it calls runs no
|
|
28
|
+
* permission check. The identity is inert on purpose: it is never used to read or write records.
|
|
29
|
+
*
|
|
30
|
+
* Minted per capabilities call rather than once: an agent token lives 5 minutes
|
|
31
|
+
* (`AGENT_TOKEN_EXPIRES_IN`) and a fan-out over hundreds of collections against a slow agent can
|
|
32
|
+
* outlive that, which would degrade the tail of the document for no reason.
|
|
33
|
+
*/
|
|
34
|
+
function issueOpenApiAgentToken(authSecret) {
|
|
35
|
+
return (0, agent_token_1.issueAgentToken)({
|
|
36
|
+
authSecret,
|
|
37
|
+
identity: {
|
|
38
|
+
renderingId: 0,
|
|
39
|
+
allowedOrigins: [],
|
|
40
|
+
user: {
|
|
41
|
+
id: 0,
|
|
42
|
+
email: 'openapi@agent-bff.forestadmin.com',
|
|
43
|
+
firstName: 'Forest',
|
|
44
|
+
lastName: 'BFF',
|
|
45
|
+
team: 'Operations',
|
|
46
|
+
tags: [],
|
|
47
|
+
permissionLevel: 'admin',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW5mb2xkZWQtZG9jdW1lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3BlbmFwaS91bmZvbGRlZC1kb2N1bWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQXdCQSx3Q0FrQkM7QUFXRCx3REFpQkM7QUFqRUQsNEVBQW1EO0FBQ25ELHlEQUErRTtBQUMvRSx3REFBeUQ7QUFDekQsMEdBQXNGO0FBZ0J2RSxLQUFLLFVBQVUscUJBQXFCLENBQ2pELE1BQW9CLEVBQ3BCLFNBQW9CLEVBQ3BCLEtBQThCLEVBQzlCLE9BQWU7SUFFZixNQUFNLFNBQVMsR0FBRyxNQUFNLElBQUEsMkJBQWdCLEVBQUM7UUFDdkMsU0FBUztRQUNULEtBQUssRUFBRSxNQUFNLENBQUMsS0FBSztRQUNuQixtQkFBbUIsRUFBRSxJQUFBLG9DQUE4QixFQUFDO1lBQ2xELFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUTtZQUN6QixLQUFLO1lBQ0wsU0FBUyxFQUFFLE1BQU0sQ0FBQyxTQUFTO1NBQzVCLENBQUM7UUFDRixNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU07S0FDdEIsQ0FBQyxDQUFDO0lBRUgsT0FBTyxFQUFFLFFBQVEsRUFBRSxJQUFBLG1DQUFnQixFQUFDLElBQUEsMENBQXVCLEVBQUMsT0FBTyxFQUFFLFNBQVMsQ0FBQyxDQUFDLEVBQUUsU0FBUyxFQUFFLENBQUM7QUFDaEcsQ0FBQztBQUVEOzs7Ozs7OztHQVFHO0FBQ0gsU0FBZ0Isc0JBQXNCLENBQUMsVUFBa0I7SUFDdkQsT0FBTyxJQUFBLDZCQUFlLEVBQUM7UUFDckIsVUFBVTtRQUNWLFFBQVEsRUFBRTtZQUNSLFdBQVcsRUFBRSxDQUFDO1lBQ2QsY0FBYyxFQUFFLEVBQUU7WUFDbEIsSUFBSSxFQUFFO2dCQUNKLEVBQUUsRUFBRSxDQUFDO2dCQUNMLEtBQUssRUFBRSxtQ0FBbUM7Z0JBQzFDLFNBQVMsRUFBRSxRQUFRO2dCQUNuQixRQUFRLEVBQUUsS0FBSztnQkFDZixJQUFJLEVBQUUsWUFBWTtnQkFDbEIsSUFBSSxFQUFFLEVBQUU7Z0JBQ1IsZUFBZSxFQUFFLE9BQU87YUFDekI7U0FDRjtLQUNGLENBQUMsQ0FBQztBQUNMLENBQUMifQ==
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type ComponentPool from './component-pool';
|
|
2
|
+
import type { Unfolding } from './unfolding';
|
|
3
|
+
import type { z } from './zod-openapi';
|
|
4
|
+
import type { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
|
|
5
|
+
import type { ReferenceObject } from 'openapi3-ts/oas31';
|
|
6
|
+
interface Deps {
|
|
7
|
+
registry: OpenAPIRegistry;
|
|
8
|
+
pool: ComponentPool;
|
|
9
|
+
prefix: string;
|
|
10
|
+
security: Record<string, string[]>[];
|
|
11
|
+
timezoneHeader: z.ZodType[];
|
|
12
|
+
errorResponses: (executeResults: boolean) => Record<string, ReferenceObject>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Emits one path per exposed collection, to-many relation and action. The runtime routes stay the
|
|
16
|
+
* generic regexes — only the document unfolds.
|
|
17
|
+
*/
|
|
18
|
+
export default function registerUnfoldedPaths(deps: Deps, unfolding: Unfolding): void;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=unfolded-paths.d.ts.map
|
|
@@ -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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
|