@forestadmin/agent-bff 1.30.0 → 1.30.1

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 (37) hide show
  1. package/dist/action/action-routes-middleware.js +8 -1
  2. package/dist/context/build-context.d.ts +1 -0
  3. package/dist/context/build-context.js +18 -4
  4. package/dist/data/agent-query.js +23 -3
  5. package/dist/data/data-routes-middleware.js +7 -2
  6. package/dist/data/pack-id.d.ts +6 -1
  7. package/dist/data/pack-id.js +96 -3
  8. package/dist/data/response-mappers.js +2 -2
  9. package/dist/http/agent-error-mapper.js +42 -27
  10. package/dist/openapi/collect-unfolding.js +23 -9
  11. package/dist/openapi/schemas.d.ts +1 -0
  12. package/dist/openapi/schemas.js +22 -3
  13. package/dist/openapi/unfolded-document.js +7 -2
  14. package/dist/openapi/unfolded-paths.js +96 -16
  15. package/dist/openapi/unfolding.d.ts +12 -0
  16. package/dist/openapi/unfolding.js +1 -1
  17. package/dist/read-model/agent-capabilities-fetcher.d.ts +6 -1
  18. package/dist/read-model/agent-capabilities-fetcher.js +88 -6
  19. package/dist/read-model/capabilities-cache.d.ts +1 -0
  20. package/dist/read-model/capabilities-cache.js +1 -1
  21. package/dist/read-model/forest-schema-client.d.ts +3 -3
  22. package/dist/read-model/forest-schema-client.js +2 -2
  23. package/dist/read-model/read-model-store.d.ts +2 -1
  24. package/dist/read-model/read-model-store.js +3 -3
  25. package/dist/read-model/read-model.d.ts +34 -0
  26. package/dist/read-model/read-model.js +79 -4
  27. package/dist/read-model/schema-cache.d.ts +17 -1
  28. package/dist/read-model/schema-cache.js +19 -6
  29. package/dist/read-model/synthesize-capabilities.d.ts +35 -0
  30. package/dist/read-model/synthesize-capabilities.js +133 -0
  31. package/dist/validation/capabilities-validator.d.ts +6 -4
  32. package/dist/validation/capabilities-validator.js +12 -1
  33. package/dist/validation/operator-normalizer.d.ts +0 -6
  34. package/dist/validation/operator-normalizer.js +6 -14
  35. package/dist/validation/validation-errors.d.ts +1 -0
  36. package/dist/validation/validation-errors.js +7 -1
  37. package/package.json +3 -3
@@ -1,7 +1,69 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.default = createAgentCapabilitiesFetcher;
4
37
  const agent_client_1 = require("@forestadmin/agent-client");
38
+ const synthesize_capabilities_1 = __importStar(require("./synthesize-capabilities"));
39
+ /**
40
+ * The capabilities a legacy liana cannot serve, synthesized from the schema it published, or null
41
+ * when this agent is not one — a proxy blocking `/forest/_internal` answers 404 just the same, so
42
+ * the liana name decides, not the status.
43
+ */
44
+ async function synthesizeForLegacyLiana(collection, { transport, store, logger }) {
45
+ const { collections, meta } = await store.getSchemaSnapshot();
46
+ const schema = collections.find(entry => entry.name === collection);
47
+ if (!schema)
48
+ return null;
49
+ if (!meta.liana || !synthesize_capabilities_1.LEGACY_LIANAS.has(meta.liana)) {
50
+ logger('Error', 'Agent serves no capabilities route, and its liana is not a legacy one', {
51
+ agentUrl: transport.url,
52
+ collection,
53
+ liana: meta.liana ?? 'absent from the published schema',
54
+ lianaVersion: meta.liana_version ?? 'unknown',
55
+ causes: 'a proxy blocking /forest/_internal, or a collection the agent no longer serves',
56
+ });
57
+ return null;
58
+ }
59
+ logger('Warn', 'Legacy liana: synthesizing the capabilities from the apimap', {
60
+ agentUrl: transport.url,
61
+ collection,
62
+ liana: meta.liana,
63
+ lianaVersion: meta.liana_version ?? 'unknown',
64
+ });
65
+ return (0, synthesize_capabilities_1.default)(schema, logger);
66
+ }
5
67
  /**
6
68
  * Builds a capabilities fetcher bound to an agent token. The cache calls it only on a miss, so the
7
69
  * token of whichever request first populates a collection is the one used.
@@ -10,16 +72,36 @@ const agent_client_1 = require("@forestadmin/agent-client");
10
72
  * hundreds of collections can outlive a single one, and the tail of that fan-out would fail on an
11
73
  * expired token. A request can only borrow its caller's, so it passes the string.
12
74
  */
13
- function createAgentCapabilitiesFetcher({ transport, token, }) {
75
+ function createAgentCapabilitiesFetcher({ transport, token, store, logger, }) {
14
76
  const clientFor = (bearer) => (0, agent_client_1.createRemoteAgentClient)({
15
77
  url: transport.url,
16
78
  token: bearer,
17
79
  httpRequester: transport.createRequester(bearer),
18
80
  });
19
- if (typeof token === 'string') {
20
- const client = clientFor(token);
21
- return collection => client.collection(collection).capabilities();
81
+ // A borrowed token cannot be renewed, so its client is built once; a factory mints one per fetch.
82
+ function createFetch() {
83
+ if (typeof token === 'string') {
84
+ const client = clientFor(token);
85
+ return collection => client.collection(collection).capabilities();
86
+ }
87
+ return collection => clientFor(token()).collection(collection).capabilities();
22
88
  }
23
- return collection => clientFor(token()).collection(collection).capabilities();
89
+ const fetch = createFetch();
90
+ // The liana is read from the cached snapshot, so it lags a migration by at most a schema
91
+ // generation; the synthesis is returned like a normal result, letting the cache stop the doomed
92
+ // POST from repeating on every constrained request.
93
+ return async (collection) => {
94
+ try {
95
+ return await fetch(collection);
96
+ }
97
+ catch (error) {
98
+ if (!(error instanceof agent_client_1.AgentHttpError) || error.status !== 404)
99
+ throw error;
100
+ const synthesized = await synthesizeForLegacyLiana(collection, { transport, store, logger });
101
+ if (!synthesized)
102
+ throw error;
103
+ return synthesized;
104
+ }
105
+ };
24
106
  }
25
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtY2FwYWJpbGl0aWVzLWZldGNoZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcmVhZC1tb2RlbC9hZ2VudC1jYXBhYmlsaXRpZXMtZmV0Y2hlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQW1CQSxpREFrQkM7QUFsQ0QsNERBQW9FO0FBUXBFOzs7Ozs7O0dBT0c7QUFDSCxTQUF3Qiw4QkFBOEIsQ0FBQyxFQUNyRCxTQUFTLEVBQ1QsS0FBSyxHQUMyQjtJQUNoQyxNQUFNLFNBQVMsR0FBRyxDQUFDLE1BQWMsRUFBRSxFQUFFLENBQ25DLElBQUEsc0NBQXVCLEVBQUM7UUFDdEIsR0FBRyxFQUFFLFNBQVMsQ0FBQyxHQUFHO1FBQ2xCLEtBQUssRUFBRSxNQUFNO1FBQ2IsYUFBYSxFQUFFLFNBQVMsQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDO0tBQ2pELENBQUMsQ0FBQztJQUVMLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDOUIsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBRWhDLE9BQU8sVUFBVSxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDLFlBQVksRUFBRSxDQUFDO0lBQ3BFLENBQUM7SUFFRCxPQUFPLFVBQVUsQ0FBQyxFQUFFLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDLFlBQVksRUFBRSxDQUFDO0FBQ2hGLENBQUMifQ==
107
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtY2FwYWJpbGl0aWVzLWZldGNoZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcmVhZC1tb2RlbC9hZ2VudC1jYXBhYmlsaXRpZXMtZmV0Y2hlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQWdFQSxpREEwQ0M7QUFyR0QsNERBQW9GO0FBRXBGLHFGQUFrRjtBQWFsRjs7OztHQUlHO0FBQ0gsS0FBSyxVQUFVLHdCQUF3QixDQUNyQyxVQUFrQixFQUNsQixFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFjO0lBRXhDLE1BQU0sRUFBRSxXQUFXLEVBQUUsSUFBSSxFQUFFLEdBQUcsTUFBTSxLQUFLLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztJQUM5RCxNQUFNLE1BQU0sR0FBRyxXQUFXLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxVQUFVLENBQUMsQ0FBQztJQUVwRSxJQUFJLENBQUMsTUFBTTtRQUFFLE9BQU8sSUFBSSxDQUFDO0lBRXpCLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxJQUFJLENBQUMsdUNBQWEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDbEQsTUFBTSxDQUFDLE9BQU8sRUFBRSx1RUFBdUUsRUFBRTtZQUN2RixRQUFRLEVBQUUsU0FBUyxDQUFDLEdBQUc7WUFDdkIsVUFBVTtZQUNWLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSyxJQUFJLGtDQUFrQztZQUN2RCxZQUFZLEVBQUUsSUFBSSxDQUFDLGFBQWEsSUFBSSxTQUFTO1lBQzdDLE1BQU0sRUFBRSxnRkFBZ0Y7U0FDekYsQ0FBQyxDQUFDO1FBRUgsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsTUFBTSxDQUFDLE1BQU0sRUFBRSw2REFBNkQsRUFBRTtRQUM1RSxRQUFRLEVBQUUsU0FBUyxDQUFDLEdBQUc7UUFDdkIsVUFBVTtRQUNWLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSztRQUNqQixZQUFZLEVBQUUsSUFBSSxDQUFDLGFBQWEsSUFBSSxTQUFTO0tBQzlDLENBQUMsQ0FBQztJQUVILE9BQU8sSUFBQSxpQ0FBc0IsRUFBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUM7QUFDaEQsQ0FBQztBQUVEOzs7Ozs7O0dBT0c7QUFDSCxTQUF3Qiw4QkFBOEIsQ0FBQyxFQUNyRCxTQUFTLEVBQ1QsS0FBSyxFQUNMLEtBQUssRUFDTCxNQUFNLEdBQzBCO0lBQ2hDLE1BQU0sU0FBUyxHQUFHLENBQUMsTUFBYyxFQUFFLEVBQUUsQ0FDbkMsSUFBQSxzQ0FBdUIsRUFBQztRQUN0QixHQUFHLEVBQUUsU0FBUyxDQUFDLEdBQUc7UUFDbEIsS0FBSyxFQUFFLE1BQU07UUFDYixhQUFhLEVBQUUsU0FBUyxDQUFDLGVBQWUsQ0FBQyxNQUFNLENBQUM7S0FDakQsQ0FBQyxDQUFDO0lBRUwsa0dBQWtHO0lBQ2xHLFNBQVMsV0FBVztRQUNsQixJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQzlCLE1BQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUVoQyxPQUFPLFVBQVUsQ0FBQyxFQUFFLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUNwRSxDQUFDO1FBRUQsT0FBTyxVQUFVLENBQUMsRUFBRSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsQ0FBQyxZQUFZLEVBQUUsQ0FBQztJQUNoRixDQUFDO0lBRUQsTUFBTSxLQUFLLEdBQUcsV0FBVyxFQUFFLENBQUM7SUFFNUIseUZBQXlGO0lBQ3pGLGdHQUFnRztJQUNoRyxvREFBb0Q7SUFDcEQsT0FBTyxLQUFLLEVBQUUsVUFBa0IsRUFBRSxFQUFFO1FBQ2xDLElBQUksQ0FBQztZQUNILE9BQU8sTUFBTSxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDakMsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLENBQUMsQ0FBQyxLQUFLLFlBQVksNkJBQWMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxNQUFNLEtBQUssR0FBRztnQkFBRSxNQUFNLEtBQUssQ0FBQztZQUU1RSxNQUFNLFdBQVcsR0FBRyxNQUFNLHdCQUF3QixDQUFDLFVBQVUsRUFBRSxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQztZQUU3RixJQUFJLENBQUMsV0FBVztnQkFBRSxNQUFNLEtBQUssQ0FBQztZQUU5QixPQUFPLFdBQVcsQ0FBQztRQUNyQixDQUFDO0lBQ0gsQ0FBQyxDQUFDO0FBQ0osQ0FBQyJ9
@@ -4,6 +4,7 @@ export interface CapabilitiesResult {
4
4
  name: string;
5
5
  type: FieldType;
6
6
  operators?: string[];
7
+ sortable?: false;
7
8
  }[];
8
9
  }
9
10
  export type CapabilitiesFetcher = (collection: string) => Promise<CapabilitiesResult>;
@@ -44,4 +44,4 @@ class CapabilitiesCache {
44
44
  }
45
45
  }
46
46
  exports.default = CapabilitiesCache;
47
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FwYWJpbGl0aWVzLWNhY2hlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3JlYWQtbW9kZWwvY2FwYWJpbGl0aWVzLWNhY2hlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBRUEsaURBQTRDO0FBb0I1Qzs7O0dBR0c7QUFDSCxNQUFxQixpQkFBaUI7SUFRcEMsWUFBWSxFQUFFLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLEtBQUssR0FBRyx5QkFBVSxLQUErQixFQUFFO1FBSmhFLFlBQU8sR0FBRyxJQUFJLEdBQUcsRUFBc0IsQ0FBQztRQUN4QyxhQUFRLEdBQUcsSUFBSSxHQUFHLEVBQXVDLENBQUM7UUFDbkUsZUFBVSxHQUFHLENBQUMsQ0FBQztRQUdyQixJQUFJLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQztRQUNmLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO0lBQ3JCLENBQUM7SUFFRCxLQUFLLENBQUMsR0FBRyxDQUFDLFVBQWtCLEVBQUUsT0FBNEI7UUFDeEQsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDM0MsSUFBSSxLQUFLLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLEtBQUssQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLEtBQUs7WUFBRSxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUM7UUFFNUUsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDOUMsSUFBSSxPQUFPO1lBQUUsT0FBTyxPQUFPLENBQUM7UUFFNUIsTUFBTSxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUksQ0FBQztRQUM1QixNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDO2FBQzlCLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUNiLHlGQUF5RjtZQUN6RixxRkFBcUY7WUFDckYsSUFBSSxJQUFJLENBQUMsVUFBVSxLQUFLLFVBQVUsRUFBRSxDQUFDO2dCQUNuQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLEVBQUUsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDbEUsQ0FBQztZQUVELE9BQU8sTUFBTSxDQUFDO1FBQ2hCLENBQUMsQ0FBQzthQUNELE9BQU8sQ0FBQyxHQUFHLEVBQUU7WUFDWixJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxLQUFLLEtBQUs7Z0JBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDaEYsQ0FBQyxDQUFDLENBQUM7UUFFTCxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFckMsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRUQsS0FBSztRQUNILElBQUksQ0FBQyxVQUFVLElBQUksQ0FBQyxDQUFDO1FBQ3JCLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUN4QixDQUFDO0NBQ0Y7QUE3Q0Qsb0NBNkNDIn0=
47
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FwYWJpbGl0aWVzLWNhY2hlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3JlYWQtbW9kZWwvY2FwYWJpbGl0aWVzLWNhY2hlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBRUEsaURBQTRDO0FBdUI1Qzs7O0dBR0c7QUFDSCxNQUFxQixpQkFBaUI7SUFRcEMsWUFBWSxFQUFFLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLEtBQUssR0FBRyx5QkFBVSxLQUErQixFQUFFO1FBSmhFLFlBQU8sR0FBRyxJQUFJLEdBQUcsRUFBc0IsQ0FBQztRQUN4QyxhQUFRLEdBQUcsSUFBSSxHQUFHLEVBQXVDLENBQUM7UUFDbkUsZUFBVSxHQUFHLENBQUMsQ0FBQztRQUdyQixJQUFJLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQztRQUNmLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO0lBQ3JCLENBQUM7SUFFRCxLQUFLLENBQUMsR0FBRyxDQUFDLFVBQWtCLEVBQUUsT0FBNEI7UUFDeEQsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDM0MsSUFBSSxLQUFLLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLEtBQUssQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLEtBQUs7WUFBRSxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUM7UUFFNUUsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDOUMsSUFBSSxPQUFPO1lBQUUsT0FBTyxPQUFPLENBQUM7UUFFNUIsTUFBTSxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUksQ0FBQztRQUM1QixNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDO2FBQzlCLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUNiLHlGQUF5RjtZQUN6RixxRkFBcUY7WUFDckYsSUFBSSxJQUFJLENBQUMsVUFBVSxLQUFLLFVBQVUsRUFBRSxDQUFDO2dCQUNuQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLEVBQUUsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDbEUsQ0FBQztZQUVELE9BQU8sTUFBTSxDQUFDO1FBQ2hCLENBQUMsQ0FBQzthQUNELE9BQU8sQ0FBQyxHQUFHLEVBQUU7WUFDWixJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxLQUFLLEtBQUs7Z0JBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDaEYsQ0FBQyxDQUFDLENBQUM7UUFFTCxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFckMsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRUQsS0FBSztRQUNILElBQUksQ0FBQyxVQUFVLElBQUksQ0FBQyxDQUFDO1FBQ3JCLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUN4QixDQUFDO0NBQ0Y7QUE3Q0Qsb0NBNkNDIn0=
@@ -1,14 +1,14 @@
1
- import type { ForestSchemaCollection } from '@forestadmin/forestadmin-client';
1
+ import type { ForestSchemaWithMeta } from '@forestadmin/forestadmin-client';
2
2
  export interface ForestSchemaClientOptions {
3
3
  forestServerUrl: string;
4
4
  envSecret: string;
5
5
  }
6
6
  export interface SchemaFetcher {
7
- fetchSchema(): Promise<ForestSchemaCollection[]>;
7
+ fetchSchema(): Promise<ForestSchemaWithMeta>;
8
8
  }
9
9
  export default class ForestSchemaClient implements SchemaFetcher {
10
10
  private readonly schemaService;
11
11
  constructor({ forestServerUrl, envSecret }: ForestSchemaClientOptions);
12
- fetchSchema(): Promise<ForestSchemaCollection[]>;
12
+ fetchSchema(): Promise<ForestSchemaWithMeta>;
13
13
  }
14
14
  //# sourceMappingURL=forest-schema-client.d.ts.map
@@ -6,8 +6,8 @@ class ForestSchemaClient {
6
6
  this.schemaService = new forestadmin_client_1.SchemaService(new forestadmin_client_1.ForestHttpApi(), { forestServerUrl, envSecret });
7
7
  }
8
8
  async fetchSchema() {
9
- return this.schemaService.getSchema();
9
+ return this.schemaService.getSchemaWithMeta();
10
10
  }
11
11
  }
12
12
  exports.default = ForestSchemaClient;
13
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9yZXN0LXNjaGVtYS1jbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcmVhZC1tb2RlbC9mb3Jlc3Qtc2NoZW1hLWNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLHdFQUErRTtBQVcvRSxNQUFxQixrQkFBa0I7SUFHckMsWUFBWSxFQUFFLGVBQWUsRUFBRSxTQUFTLEVBQTZCO1FBQ25FLElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxrQ0FBYSxDQUFDLElBQUksa0NBQWEsRUFBRSxFQUFFLEVBQUUsZUFBZSxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUM7SUFDOUYsQ0FBQztJQUVELEtBQUssQ0FBQyxXQUFXO1FBQ2YsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLFNBQVMsRUFBRSxDQUFDO0lBQ3hDLENBQUM7Q0FDRjtBQVZELHFDQVVDIn0=
13
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9yZXN0LXNjaGVtYS1jbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcmVhZC1tb2RlbC9mb3Jlc3Qtc2NoZW1hLWNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLHdFQUErRTtBQVcvRSxNQUFxQixrQkFBa0I7SUFHckMsWUFBWSxFQUFFLGVBQWUsRUFBRSxTQUFTLEVBQTZCO1FBQ25FLElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxrQ0FBYSxDQUFDLElBQUksa0NBQWEsRUFBRSxFQUFFLEVBQUUsZUFBZSxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUM7SUFDOUYsQ0FBQztJQUVELEtBQUssQ0FBQyxXQUFXO1FBQ2YsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLGlCQUFpQixFQUFFLENBQUM7SUFDaEQsQ0FBQztDQUNGO0FBVkQscUNBVUMifQ==
@@ -1,10 +1,11 @@
1
1
  import type CapabilitiesCache from './capabilities-cache';
2
2
  import type { CapabilitiesFetcher, CapabilitiesResult } from './capabilities-cache';
3
3
  import type SchemaCache from './schema-cache';
4
- import type { ForestSchemaCollection } from '@forestadmin/forestadmin-client';
4
+ import type { ForestSchemaCollection, ForestSchemaMeta } from '@forestadmin/forestadmin-client';
5
5
  import ReadModel from './read-model';
6
6
  export interface SchemaSnapshot {
7
7
  collections: ForestSchemaCollection[];
8
+ meta: ForestSchemaMeta;
8
9
  readModel: ReadModel;
9
10
  revision: number;
10
11
  }
@@ -32,14 +32,14 @@ class ReadModelStore {
32
32
  this.builtRevision = -1;
33
33
  }
34
34
  async getSchemaSnapshot() {
35
- const collections = await this.schemaCache.get();
35
+ const { collections, meta } = await this.schemaCache.getPayload();
36
36
  const { revision } = this.schemaCache;
37
37
  if (revision !== this.builtRevision || !this.readModel) {
38
38
  this.readModel = new read_model_1.default(collections);
39
39
  this.builtRevision = revision;
40
40
  this.capabilitiesCache.clear();
41
41
  }
42
- return { collections, readModel: this.readModel, revision };
42
+ return { collections, meta, readModel: this.readModel, revision };
43
43
  }
44
44
  /**
45
45
  * Returns the capabilities together with the read-model they belong to, so a caller cannot mix a
@@ -70,4 +70,4 @@ class ReadModelStore {
70
70
  }
71
71
  }
72
72
  exports.default = ReadModelStore;
73
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhZC1tb2RlbC1zdG9yZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9yZWFkLW1vZGVsL3JlYWQtbW9kZWwtc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFLQSw4REFBcUM7QUFFckMsTUFBTSxzQkFBc0IsR0FBRyxDQUFDLENBQUM7QUFRakM7Ozs7R0FJRztBQUNILE1BQXFCLGNBQWM7SUFPakMsWUFBWSxXQUF3QixFQUFFLGlCQUFvQztRQUhsRSxrQkFBYSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ25CLGNBQVMsR0FBcUIsSUFBSSxDQUFDO1FBR3pDLElBQUksQ0FBQyxXQUFXLEdBQUcsV0FBVyxDQUFDO1FBQy9CLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxpQkFBaUIsQ0FBQztJQUM3QyxDQUFDO0lBRUQsS0FBSyxDQUFDLFlBQVk7UUFDaEIsT0FBTyxDQUFDLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFDcEQsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsVUFBVTtRQUNSLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDekIsSUFBSSxDQUFDLGlCQUFpQixDQUFDLEtBQUssRUFBRSxDQUFDO1FBQy9CLElBQUksQ0FBQyxhQUFhLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDMUIsQ0FBQztJQUVELEtBQUssQ0FBQyxpQkFBaUI7UUFDckIsTUFBTSxXQUFXLEdBQUcsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ2pELE1BQU0sRUFBRSxRQUFRLEVBQUUsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDO1FBRXRDLElBQUksUUFBUSxLQUFLLElBQUksQ0FBQyxhQUFhLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDdkQsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLG9CQUFTLENBQUMsV0FBVyxDQUFDLENBQUM7WUFDNUMsSUFBSSxDQUFDLGFBQWEsR0FBRyxRQUFRLENBQUM7WUFDOUIsSUFBSSxDQUFDLGlCQUFpQixDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ2pDLENBQUM7UUFFRCxPQUFPLEVBQUUsV0FBVyxFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsU0FBUyxFQUFFLFFBQVEsRUFBRSxDQUFDO0lBQzlELENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsS0FBSyxDQUFDLGVBQWUsQ0FDbkIsVUFBa0IsRUFDbEIsT0FBNEIsRUFDNUIsWUFBWSxHQUFHLHNCQUFzQjtRQUVyQyxJQUFJLFlBQVksSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUN0QixNQUFNLElBQUksS0FBSyxDQUNiLG1FQUFtRSxVQUFVLEdBQUcsQ0FDakYsQ0FBQztRQUNKLENBQUM7UUFFRCxvRkFBb0Y7UUFDcEYsTUFBTSxTQUFTLEdBQUcsTUFBTSxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDNUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUM7UUFDdEMsTUFBTSxZQUFZLEdBQUcsTUFBTSxJQUFJLENBQUMsaUJBQWlCLENBQUMsR0FBRyxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUUzRSw2RkFBNkY7UUFDN0YsaUdBQWlHO1FBQ2pHLCtGQUErRjtRQUMvRiw4RkFBOEY7UUFDOUYsNkZBQTZGO1FBQzdGLHVEQUF1RDtRQUN2RCxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQzNDLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQyxVQUFVLEVBQUUsT0FBTyxFQUFFLFlBQVksR0FBRyxDQUFDLENBQUMsQ0FBQztRQUNyRSxDQUFDO1FBRUQsT0FBTyxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsQ0FBQztJQUNyQyxDQUFDO0lBRUQsVUFBVTtRQUNSLE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsQ0FBQztJQUN2QyxDQUFDO0NBQ0Y7QUE5RUQsaUNBOEVDIn0=
73
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhZC1tb2RlbC1zdG9yZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9yZWFkLW1vZGVsL3JlYWQtbW9kZWwtc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFLQSw4REFBcUM7QUFFckMsTUFBTSxzQkFBc0IsR0FBRyxDQUFDLENBQUM7QUFTakM7Ozs7R0FJRztBQUNILE1BQXFCLGNBQWM7SUFPakMsWUFBWSxXQUF3QixFQUFFLGlCQUFvQztRQUhsRSxrQkFBYSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ25CLGNBQVMsR0FBcUIsSUFBSSxDQUFDO1FBR3pDLElBQUksQ0FBQyxXQUFXLEdBQUcsV0FBVyxDQUFDO1FBQy9CLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxpQkFBaUIsQ0FBQztJQUM3QyxDQUFDO0lBRUQsS0FBSyxDQUFDLFlBQVk7UUFDaEIsT0FBTyxDQUFDLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFDcEQsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsVUFBVTtRQUNSLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDekIsSUFBSSxDQUFDLGlCQUFpQixDQUFDLEtBQUssRUFBRSxDQUFDO1FBQy9CLElBQUksQ0FBQyxhQUFhLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDMUIsQ0FBQztJQUVELEtBQUssQ0FBQyxpQkFBaUI7UUFDckIsTUFBTSxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsR0FBRyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsVUFBVSxFQUFFLENBQUM7UUFDbEUsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUM7UUFFdEMsSUFBSSxRQUFRLEtBQUssSUFBSSxDQUFDLGFBQWEsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUN2RCxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksb0JBQVMsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUM1QyxJQUFJLENBQUMsYUFBYSxHQUFHLFFBQVEsQ0FBQztZQUM5QixJQUFJLENBQUMsaUJBQWlCLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDakMsQ0FBQztRQUVELE9BQU8sRUFBRSxXQUFXLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsU0FBUyxFQUFFLFFBQVEsRUFBRSxDQUFDO0lBQ3BFLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsS0FBSyxDQUFDLGVBQWUsQ0FDbkIsVUFBa0IsRUFDbEIsT0FBNEIsRUFDNUIsWUFBWSxHQUFHLHNCQUFzQjtRQUVyQyxJQUFJLFlBQVksSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUN0QixNQUFNLElBQUksS0FBSyxDQUNiLG1FQUFtRSxVQUFVLEdBQUcsQ0FDakYsQ0FBQztRQUNKLENBQUM7UUFFRCxvRkFBb0Y7UUFDcEYsTUFBTSxTQUFTLEdBQUcsTUFBTSxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDNUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUM7UUFDdEMsTUFBTSxZQUFZLEdBQUcsTUFBTSxJQUFJLENBQUMsaUJBQWlCLENBQUMsR0FBRyxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUUzRSw2RkFBNkY7UUFDN0YsaUdBQWlHO1FBQ2pHLCtGQUErRjtRQUMvRiw4RkFBOEY7UUFDOUYsNkZBQTZGO1FBQzdGLHVEQUF1RDtRQUN2RCxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQzNDLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQyxVQUFVLEVBQUUsT0FBTyxFQUFFLFlBQVksR0FBRyxDQUFDLENBQUMsQ0FBQztRQUNyRSxDQUFDO1FBRUQsT0FBTyxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsQ0FBQztJQUNyQyxDQUFDO0lBRUQsVUFBVTtRQUNSLE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsQ0FBQztJQUN2QyxDQUFDO0NBQ0Y7QUE5RUQsaUNBOEVDIn0=
@@ -14,6 +14,13 @@ export type RelationTarget = {
14
14
  export type PrimaryKeyField = {
15
15
  name: string;
16
16
  type: string;
17
+ derived?: true;
18
+ /**
19
+ * Set when this key shares its response key with another field of the collection. The record then
20
+ * holds one value under that key and there is no telling whose, so the key cannot be read back
21
+ * from a record — see `unpackPrimaryKey`.
22
+ */
23
+ ambiguousRecordKey?: true;
17
24
  };
18
25
  export type ListableRelation = {
19
26
  name: string;
@@ -34,6 +41,33 @@ export default class ReadModel {
34
41
  constructor(collections: ForestSchemaCollection[]);
35
42
  private buildRelations;
36
43
  private buildActionEndpoints;
44
+ /**
45
+ * A schema that declares no primary key at all gets one derived from its `id` field.
46
+ *
47
+ * `forest_liana` only started publishing `isPrimaryKey` in 9.17.6 (2026-06-04), and an older one
48
+ * leaves every collection without a key — which makes `unpackPrimaryKey` reject every record the
49
+ * agent returns, so a plain list answers `500 mapping_error`. The record id is there regardless:
50
+ * the agent serialises it as the JSON:API `id`, and 257 of the 269 collections in the schema this
51
+ * was measured against carry a field literally named `id`.
52
+ *
53
+ * Keyed on the shape of the schema, not on the liana: a collection that declares a key keeps it,
54
+ * and every v2 agent declares one, so this only fires where the alternative is a 500. `String`
55
+ * when no `id` field is declared, because the packed id survives a string round-trip untouched
56
+ * while a wrong numeric cast would not.
57
+ *
58
+ * The derived key is flagged, because its arity is a guess: the schema publishes no key, so a
59
+ * collection whose real one is composite packs `tenant|42` behind the same silence. Splitting
60
+ * that on the separator would find two values against one declared key and 500 the whole list,
61
+ * so `unpackPrimaryKey` passes a derived key through opaque instead of splitting it.
62
+ *
63
+ * That last case names a column the collection does not declare, which `__forest.primaryKey`
64
+ * otherwise promises is a real one. It is the deliberate trade: the 12 collections concerned are
65
+ * ordinary listable ones carrying ordinary data columns, and the alternative is a 500 on a plain
66
+ * list of them. Every route takes the id packed and opaque, so the invented name only misses for
67
+ * a consumer filtering ON the key — which a keyless collection could not do either way.
68
+ * `ForestRecordMeta` says so, and `buildContext` publishes no `isPrimaryKey` where there is no
69
+ * field to carry it.
70
+ */
37
71
  private buildPrimaryKeys;
38
72
  isCollectionAllowed(collection: string): boolean;
39
73
  /** A fresh copy per call, so a consumer cannot mutate the shared cached read-model. */
@@ -1,6 +1,40 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.RELATIONSHIP_TYPES = void 0;
37
+ const record_key_1 = __importStar(require("../data/record-key"));
4
38
  exports.RELATIONSHIP_TYPES = [
5
39
  'BelongsTo',
6
40
  'HasOne',
@@ -99,11 +133,52 @@ class ReadModel {
99
133
  };
100
134
  }
101
135
  }
136
+ /**
137
+ * A schema that declares no primary key at all gets one derived from its `id` field.
138
+ *
139
+ * `forest_liana` only started publishing `isPrimaryKey` in 9.17.6 (2026-06-04), and an older one
140
+ * leaves every collection without a key — which makes `unpackPrimaryKey` reject every record the
141
+ * agent returns, so a plain list answers `500 mapping_error`. The record id is there regardless:
142
+ * the agent serialises it as the JSON:API `id`, and 257 of the 269 collections in the schema this
143
+ * was measured against carry a field literally named `id`.
144
+ *
145
+ * Keyed on the shape of the schema, not on the liana: a collection that declares a key keeps it,
146
+ * and every v2 agent declares one, so this only fires where the alternative is a 500. `String`
147
+ * when no `id` field is declared, because the packed id survives a string round-trip untouched
148
+ * while a wrong numeric cast would not.
149
+ *
150
+ * The derived key is flagged, because its arity is a guess: the schema publishes no key, so a
151
+ * collection whose real one is composite packs `tenant|42` behind the same silence. Splitting
152
+ * that on the separator would find two values against one declared key and 500 the whole list,
153
+ * so `unpackPrimaryKey` passes a derived key through opaque instead of splitting it.
154
+ *
155
+ * That last case names a column the collection does not declare, which `__forest.primaryKey`
156
+ * otherwise promises is a real one. It is the deliberate trade: the 12 collections concerned are
157
+ * ordinary listable ones carrying ordinary data columns, and the alternative is a 500 on a plain
158
+ * list of them. Every route takes the id packed and opaque, so the invented name only misses for
159
+ * a consumer filtering ON the key — which a keyless collection could not do either way.
160
+ * `ForestRecordMeta` says so, and `buildContext` publishes no `isPrimaryKey` where there is no
161
+ * field to carry it.
162
+ */
102
163
  buildPrimaryKeys(collection) {
103
164
  const keys = [];
104
- for (const field of collection.fields ?? []) {
105
- if (field.isPrimaryKey)
106
- keys.push({ name: field.field, type: field.type });
165
+ const fields = collection.fields ?? [];
166
+ const ambiguous = new Set();
167
+ (0, record_key_1.groupByRecordKey)(fields, field => field.field).forEach((group, key) => {
168
+ if (group.length > 1)
169
+ ambiguous.add(key);
170
+ });
171
+ for (const field of fields) {
172
+ if (field.isPrimaryKey) {
173
+ const key = { name: field.field, type: field.type };
174
+ if (ambiguous.has((0, record_key_1.default)(field.field)))
175
+ key.ambiguousRecordKey = true;
176
+ keys.push(key);
177
+ }
178
+ }
179
+ if (keys.length === 0) {
180
+ const declaredId = (collection.fields ?? []).find(field => field.field === 'id');
181
+ keys.push({ name: 'id', type: declaredId?.type ?? 'String', derived: true });
107
182
  }
108
183
  this.primaryKeys.set(collection.name, keys);
109
184
  }
@@ -146,4 +221,4 @@ class ReadModel {
146
221
  }
147
222
  }
148
223
  exports.default = ReadModel;
149
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhZC1tb2RlbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9yZWFkLW1vZGVsL3JlYWQtbW9kZWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBR2EsUUFBQSxrQkFBa0IsR0FBRztJQUNoQyxXQUFXO0lBQ1gsUUFBUTtJQUNSLFNBQVM7SUFDVCxlQUFlO0NBQzZELENBQUM7QUFZL0Usb0dBQW9HO0FBQ3BHLHFHQUFxRztBQUNyRyxzR0FBc0c7QUFDdEcsb0VBQW9FO0FBQ3BFLFNBQVMsZ0JBQWdCLENBQUMsTUFBc0I7SUFDOUMsSUFBSSxDQUFDLENBQUMsUUFBUSxJQUFJLE1BQU0sQ0FBQztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQ3ZDLElBQUksTUFBTSxDQUFDLElBQUksS0FBSyxTQUFTLElBQUksTUFBTSxDQUFDLElBQUksS0FBSyxlQUFlO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFFOUUsT0FBTyxNQUFNLENBQUMsTUFBTSxDQUFDO0FBQ3ZCLENBQUM7QUFFRCxTQUFTLGdCQUFnQixDQUFDLEtBQXdCO0lBQ2hELElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWTtRQUFFLE9BQU8sSUFBSSxDQUFDO0lBRXJDLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxZQUFZLENBQUM7SUFFaEMsSUFBSSxLQUFLLENBQUMsMkJBQTJCLElBQUksS0FBSyxDQUFDLDJCQUEyQixDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN0RixPQUFPLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLENBQUMsR0FBRyxLQUFLLENBQUMsMkJBQTJCLENBQUMsRUFBRSxDQUFDO0lBQ3RGLENBQUM7SUFFRCxJQUFJLEtBQUssQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUNwQiwwRkFBMEY7UUFDMUYseUZBQXlGO1FBQ3pGLE9BQU8sRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsS0FB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
224
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhZC1tb2RlbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9yZWFkLW1vZGVsL3JlYWQtbW9kZWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBR0EsaUVBQWlFO0FBRXBELFFBQUEsa0JBQWtCLEdBQUc7SUFDaEMsV0FBVztJQUNYLFFBQVE7SUFDUixTQUFTO0lBQ1QsZUFBZTtDQUM2RCxDQUFDO0FBc0IvRSxvR0FBb0c7QUFDcEcscUdBQXFHO0FBQ3JHLHNHQUFzRztBQUN0RyxvRUFBb0U7QUFDcEUsU0FBUyxnQkFBZ0IsQ0FBQyxNQUFzQjtJQUM5QyxJQUFJLENBQUMsQ0FBQyxRQUFRLElBQUksTUFBTSxDQUFDO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDdkMsSUFBSSxNQUFNLENBQUMsSUFBSSxLQUFLLFNBQVMsSUFBSSxNQUFNLENBQUMsSUFBSSxLQUFLLGVBQWU7UUFBRSxPQUFPLElBQUksQ0FBQztJQUU5RSxPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUM7QUFDdkIsQ0FBQztBQUVELFNBQVMsZ0JBQWdCLENBQUMsS0FBd0I7SUFDaEQsSUFBSSxDQUFDLEtBQUssQ0FBQyxZQUFZO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFFckMsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLFlBQVksQ0FBQztJQUVoQyxJQUFJLEtBQUssQ0FBQywyQkFBMkIsSUFBSSxLQUFLLENBQUMsMkJBQTJCLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3RGLE9BQU8sRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsQ0FBQyxHQUFHLEtBQUssQ0FBQywyQkFBMkIsQ0FBQyxFQUFFLENBQUM7SUFDdEYsQ0FBQztJQUVELElBQUksS0FBSyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ3BCLDBGQUEwRjtRQUMxRix5RkFBeUY7UUFDekYsT0FBTyxF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
@@ -1,7 +1,7 @@
1
1
  import type { SchemaFetcher } from './forest-schema-client';
2
2
  import type { Logger } from '../ports/logger-port';
3
3
  import type { Metrics } from '../ports/metrics-port';
4
- import type { ForestSchemaCollection } from '@forestadmin/forestadmin-client';
4
+ import type { ForestSchemaCollection, ForestSchemaMeta } from '@forestadmin/forestadmin-client';
5
5
  export declare const ONE_DAY_MS: number;
6
6
  /**
7
7
  * How long after a `clear()` the cache keeps re-reading the schema, and how often. An invalidation
@@ -21,6 +21,18 @@ export interface SchemaCacheOptions {
21
21
  now?: () => number;
22
22
  ttlMs?: number;
23
23
  }
24
+ /**
25
+ * The collections and the liana that published them, always read from the same source. They must
26
+ * not be fetched through separate accessors: `clear()` detaches an in-flight refresh, which still
27
+ * resolves for whoever awaited it while a newer refresh writes the entry, so a caller reading
28
+ * collections from the resolved promise and `meta` from the current entry can pair two schema
29
+ * generations -- and the legacy-capabilities fallback would then pick a liana from one and
30
+ * synthesize from the other.
31
+ */
32
+ export interface SchemaPayload {
33
+ collections: ForestSchemaCollection[];
34
+ meta: ForestSchemaMeta;
35
+ }
24
36
  /**
25
37
  * Caches the agent schema for 24h. The TTL only *triggers* a refresh attempt: the last good
26
38
  * schema keeps being served until a refresh succeeds. A cold cache (no last good) surfaces a
@@ -40,6 +52,8 @@ export default class SchemaCache {
40
52
  private revalidatingUntil;
41
53
  constructor({ fetcher, metrics, logger, now, ttlMs, }: SchemaCacheOptions);
42
54
  get(): Promise<ForestSchemaCollection[]>;
55
+ /** The collections and their liana, guaranteed to come from the same schema generation. */
56
+ getPayload(): Promise<SchemaPayload>;
43
57
  /**
44
58
  * Expire what is cached and re-read it eagerly for a while. Called when something outside knows
45
59
  * the schema moved — an agent restarting on a customization refresh, say.
@@ -52,6 +66,8 @@ export default class SchemaCache {
52
66
  clear(): void;
53
67
  ageSeconds(): number | undefined;
54
68
  get revision(): number;
69
+ /** The liana that published the schema currently served, stale-serve included. */
70
+ get meta(): ForestSchemaMeta;
55
71
  private refresh;
56
72
  private doRefresh;
57
73
  private ttlFor;
@@ -37,9 +37,13 @@ class SchemaCache {
37
37
  this.ttlMs = ttlMs;
38
38
  }
39
39
  async get() {
40
+ return (await this.getPayload()).collections;
41
+ }
42
+ /** The collections and their liana, guaranteed to come from the same schema generation. */
43
+ async getPayload() {
40
44
  if (this.entry && this.now() < this.entry.expiresAt) {
41
45
  this.emitAge();
42
- return this.entry.collections;
46
+ return { collections: this.entry.collections, meta: this.entry.meta };
43
47
  }
44
48
  return this.refresh();
45
49
  }
@@ -67,6 +71,10 @@ class SchemaCache {
67
71
  get revision() {
68
72
  return this.revisionValue;
69
73
  }
74
+ /** The liana that published the schema currently served, stale-serve included. */
75
+ get meta() {
76
+ return this.entry?.meta ?? {};
77
+ }
70
78
  async refresh() {
71
79
  if (!this.inFlight) {
72
80
  // Identity-guarded, because `clear()` detaches the in-flight fetch: a read that lands after an
@@ -82,7 +90,7 @@ class SchemaCache {
82
90
  async doRefresh() {
83
91
  const { generation } = this;
84
92
  try {
85
- const collections = await this.fetcher.fetchSchema();
93
+ const { collections, meta } = await this.fetcher.fetchSchema();
86
94
  // An agent always exposes collections, so an empty result is far likelier a broken response
87
95
  // than a valid state. Caching it would silently deny everything for 24h — treat it as a
88
96
  // failed fetch and fall through to the failure path below.
@@ -92,11 +100,16 @@ class SchemaCache {
92
100
  // invalidation declared stale, and caching it now would undo the invalidation.
93
101
  if (this.generation === generation) {
94
102
  const fetchedAt = this.now();
95
- this.entry = { collections, fetchedAt, expiresAt: fetchedAt + this.ttlFor(fetchedAt) };
103
+ this.entry = {
104
+ collections,
105
+ meta,
106
+ fetchedAt,
107
+ expiresAt: fetchedAt + this.ttlFor(fetchedAt),
108
+ };
96
109
  this.revisionValue += 1;
97
110
  this.emitAge();
98
111
  }
99
- return collections;
112
+ return { collections, meta };
100
113
  }
101
114
  catch (error) {
102
115
  this.metrics.increment(exports.SCHEMA_CACHE_REFRESH_ERROR);
@@ -111,7 +124,7 @@ class SchemaCache {
111
124
  // re-attempts because `expiresAt` is unchanged and the entry stays expired.
112
125
  if (this.entry) {
113
126
  this.emitAge();
114
- return this.entry.collections;
127
+ return { collections: this.entry.collections, meta: this.entry.meta };
115
128
  }
116
129
  // Cold cache: nothing to serve.
117
130
  throw new errors_1.default(error);
@@ -127,4 +140,4 @@ class SchemaCache {
127
140
  }
128
141
  }
129
142
  exports.default = SchemaCache;
130
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hLWNhY2hlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3JlYWQtbW9kZWwvc2NoZW1hLWNhY2hlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUtBLHNEQUE4QztBQUVqQyxRQUFBLFVBQVUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7QUFFOUM7Ozs7OztHQU1HO0FBQ1UsUUFBQSxzQkFBc0IsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBQ25DLFFBQUEsbUJBQW1CLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQztBQUUvQixRQUFBLDBCQUEwQixHQUFHLDRCQUE0QixDQUFDO0FBQzFELFFBQUEsd0JBQXdCLEdBQUcsMEJBQTBCLENBQUM7QUFxQm5FOzs7OztHQUtHO0FBQ0gsTUFBcUIsV0FBVztJQWE5QixZQUFZLEVBQ1YsT0FBTyxFQUNQLE9BQU8sRUFDUCxNQUFNLEdBQUcsR0FBRyxFQUFFLENBQUMsU0FBUyxFQUN4QixHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFDZCxLQUFLLEdBQUcsa0JBQVUsR0FDQztRQVpiLFVBQUssR0FBc0IsSUFBSSxDQUFDO1FBQ2hDLGFBQVEsR0FBNkMsSUFBSSxDQUFDO1FBQzFELGtCQUFhLEdBQUcsQ0FBQyxDQUFDO1FBQ2xCLGVBQVUsR0FBRyxDQUFDLENBQUM7UUFDZixzQkFBaUIsR0FBRyxDQUFDLENBQUM7UUFTNUIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUM7UUFDZixJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztJQUNyQixDQUFDO0lBRUQsS0FBSyxDQUFDLEdBQUc7UUFDUCxJQUFJLElBQUksQ0FBQyxLQUFL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
143
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hLWNhY2hlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3JlYWQtbW9kZWwvc2NoZW1hLWNhY2hlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUtBLHNEQUE4QztBQUVqQyxRQUFBLFVBQVUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7QUFFOUM7Ozs7OztHQU1HO0FBQ1UsUUFBQSxzQkFBc0IsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBQ25DLFFBQUEsbUJBQW1CLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQztBQUUvQixRQUFBLDBCQUEwQixHQUFHLDRCQUE0QixDQUFDO0FBQzFELFFBQUEsd0JBQXdCLEdBQUcsMEJBQTBCLENBQUM7QUFtQ25FOzs7OztHQUtHO0FBQ0gsTUFBcUIsV0FBVztJQWE5QixZQUFZLEVBQ1YsT0FBTyxFQUNQLE9BQU8sRUFDUCxNQUFNLEdBQUcsR0FBRyxFQUFFLENBQUMsU0FBUyxFQUN4QixHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFDZCxLQUFLLEdBQUcsa0JBQVUsR0FDQztRQVpiLFVBQUssR0FBc0IsSUFBSSxDQUFDO1FBQ2hDLGFBQVEsR0FBa0MsSUFBSSxDQUFDO1FBQy9DLGtCQUFhLEdBQUcsQ0FBQyxDQUFDO1FBQ2xCLGVBQVUsR0FBRyxDQUFDLENBQUM7UUFDZixzQkFBaUIsR0FBRyxDQUFDLENBQUM7UUFTNUIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUM7UUFDZixJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztJQUNyQixDQUFDO0lBRUQsS0FBSyxDQUFDLEdBQUc7UUFDUCxPQUFPLENBQUMsTUFBTSxJQUFJ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
@@ -0,0 +1,35 @@
1
+ import type { CapabilitiesResult } from './capabilities-cache';
2
+ import type { Logger } from '../ports/logger-port';
3
+ import type { ForestSchemaCollection } from '@forestadmin/forestadmin-client';
4
+ /**
5
+ * The operators every supported legacy liana honours, measured against
6
+ * `forest-express-sequelize 9.6.10` and `forest_liana 9.21.0`: the scalar cases of their filter
7
+ * switch plus the date family their separate date parser handles. Names are the legacy snake_case
8
+ * wire format, which is also what a real capabilities response carries.
9
+ *
10
+ * This list exists because a legacy liana publishes a single `isFilterable` boolean per field, never
11
+ * an operator list. Advertising the column type's whole operator set instead would admit operators
12
+ * the liana rejects on its own, which the BFF maps to `422 unprocessable_entity` rather than the
13
+ * `400 invalid_filter_operator` a caller can act on.
14
+ *
15
+ * It is the intersection of the two, not the union: `includes_all` sits in the column type table for
16
+ * every array-capable type, and on a scalar column `forest-express-sequelize` answers it with a 500
17
+ * carrying SQL while `forest_liana` rejects it outright. `i_contains` goes the other way — Rails
18
+ * honours it, Express does not — so it stays out too.
19
+ */
20
+ export declare const LEGACY_LIANA_OPERATORS: ReadonlySet<string>;
21
+ /**
22
+ * The lianas that never served `/forest/_internal/capabilities`, as the Forest server itself
23
+ * enumerates them. The set is closed in practice — no new one is published — while the v2 agent
24
+ * family grows, so listing the legacy names keeps an unlisted agent on the failing path rather than
25
+ * silently downgrading it to the operator set above.
26
+ */
27
+ export declare const LEGACY_LIANAS: ReadonlySet<string>;
28
+ /**
29
+ * Build the capabilities a v1 liana would have answered, from the apimap it already pushed.
30
+ *
31
+ * The field classes mirror what the v2 agent's capabilities route emits, so the validator produces
32
+ * the same error for the same input on both generations.
33
+ */
34
+ export default function synthesizeCapabilities(collection: ForestSchemaCollection, logger: Logger): CapabilitiesResult;
35
+ //# sourceMappingURL=synthesize-capabilities.d.ts.map