@inf-monkeys-tech/monkeys 1.0.72 → 1.1.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.
- package/lib/contracts/aggregate-commit.d.ts +155 -0
- package/lib/contracts/aggregate-commit.d.ts.map +1 -0
- package/lib/contracts/aggregate-commit.js +112 -0
- package/lib/contracts/aggregate-commit.js.map +1 -0
- package/lib/contracts/capability.d.ts +3 -0
- package/lib/contracts/capability.d.ts.map +1 -1
- package/lib/contracts/capability.js +1 -1
- package/lib/contracts/capability.js.map +1 -1
- package/lib/contracts/common.d.ts +2 -0
- package/lib/contracts/common.d.ts.map +1 -1
- package/lib/contracts/common.js +34 -1
- package/lib/contracts/common.js.map +1 -1
- package/lib/contracts/component-assembly.d.ts +118 -0
- package/lib/contracts/component-assembly.d.ts.map +1 -0
- package/lib/contracts/component-assembly.js +61 -0
- package/lib/contracts/component-assembly.js.map +1 -0
- package/lib/contracts/declarative-control-http.d.ts +80610 -41684
- package/lib/contracts/declarative-control-http.d.ts.map +1 -1
- package/lib/contracts/declarative-control-http.js +1596 -165
- package/lib/contracts/declarative-control-http.js.map +1 -1
- package/lib/contracts/declarative-control.d.ts +12525 -4613
- package/lib/contracts/declarative-control.d.ts.map +1 -1
- package/lib/contracts/declarative-control.js +3792 -422
- package/lib/contracts/declarative-control.js.map +1 -1
- package/lib/contracts/index.d.ts +6 -0
- package/lib/contracts/index.d.ts.map +1 -1
- package/lib/contracts/index.js +6 -0
- package/lib/contracts/index.js.map +1 -1
- package/lib/contracts/legacy-route.d.ts +15 -0
- package/lib/contracts/legacy-route.d.ts.map +1 -0
- package/lib/contracts/legacy-route.js +13 -0
- package/lib/contracts/legacy-route.js.map +1 -0
- package/lib/contracts/menu.d.ts +12 -0
- package/lib/contracts/menu.d.ts.map +1 -1
- package/lib/contracts/page-binding-dependencies.d.ts +25 -0
- package/lib/contracts/page-binding-dependencies.d.ts.map +1 -0
- package/lib/contracts/page-binding-dependencies.js +54 -0
- package/lib/contracts/page-binding-dependencies.js.map +1 -0
- package/lib/contracts/page-expression.d.ts +106 -0
- package/lib/contracts/page-expression.d.ts.map +1 -0
- package/lib/contracts/page-expression.js +136 -0
- package/lib/contracts/page-expression.js.map +1 -0
- package/lib/contracts/page-stream.d.ts +101 -0
- package/lib/contracts/page-stream.d.ts.map +1 -0
- package/lib/contracts/page-stream.js +66 -0
- package/lib/contracts/page-stream.js.map +1 -0
- package/lib/contracts/page.d.ts +24 -0
- package/lib/contracts/page.d.ts.map +1 -1
- package/lib/contracts/page.js +2 -0
- package/lib/contracts/page.js.map +1 -1
- package/lib/contracts/record-editor-draft.d.ts +95 -0
- package/lib/contracts/record-editor-draft.d.ts.map +1 -0
- package/lib/contracts/record-editor-draft.js +35 -0
- package/lib/contracts/record-editor-draft.js.map +1 -0
- package/lib/contracts/render.d.ts +607 -1
- package/lib/contracts/render.d.ts.map +1 -1
- package/lib/contracts/render.js +332 -133
- package/lib/contracts/render.js.map +1 -1
- package/lib/contracts/semantic.d.ts +48 -1
- package/lib/contracts/semantic.d.ts.map +1 -1
- package/lib/contracts/semantic.js +22 -19
- package/lib/contracts/semantic.js.map +1 -1
- package/lib/contracts/tenant.d.ts +115 -9
- package/lib/contracts/tenant.d.ts.map +1 -1
- package/lib/contracts/tenant.js +14 -1
- package/lib/contracts/tenant.js.map +1 -1
- package/lib/contracts/theme.d.ts +1 -1
- package/lib/esm/contracts/index.mjs +8466 -3114
- package/lib/esm/contracts/index.mjs.map +1 -1
- package/lib/esm/index.mjs +13809 -5067
- package/lib/esm/index.mjs.map +1 -1
- package/lib/esm/runtime/index.mjs +13964 -4515
- package/lib/esm/runtime/index.mjs.map +1 -1
- package/lib/esm/schemas/index.mjs +8779 -2614
- package/lib/esm/schemas/index.mjs.map +1 -1
- package/lib/json-schema/aggregate-commit-request.schema.json +426 -0
- package/lib/json-schema/capability-manifest.schema.json +1 -0
- package/lib/json-schema/capability-registry.schema.json +1 -0
- package/lib/json-schema/declarative-route-owner-index.schema.json +1092 -0
- package/lib/json-schema/declarative-workbench-catalog.schema.json +769 -0
- package/lib/json-schema/design-capability-catalog-artifact.schema.json +2787 -0
- package/lib/json-schema/domain-command-definition.schema.json +18 -0
- package/lib/json-schema/domain-query-definition.schema.json +1406 -0
- package/lib/json-schema/index.json +7 -0
- package/lib/json-schema/legacy-route-takeover-authorization.schema.json +601 -0
- package/lib/json-schema/navigation-release.schema.json +296 -0
- package/lib/json-schema/navigation-runtime-bundle.schema.json +48 -8
- package/lib/json-schema/navigation.schema.json +47 -8
- package/lib/json-schema/overlay-node.schema.json +883 -0
- package/lib/json-schema/page-definition.schema.json +34 -0
- package/lib/json-schema/page-release.schema.json +1520 -626
- package/lib/json-schema/page-runtime-bundle.schema.json +7334 -1153
- package/lib/json-schema/page-runtime-descriptor.schema.json +34 -0
- package/lib/json-schema/page.schema.json +6329 -6602
- package/lib/json-schema/permission-alternative-policy.schema.json +60 -0
- package/lib/json-schema/product-declaration.schema.json +53 -0
- package/lib/json-schema/render-node.schema.json +883 -0
- package/lib/json-schema/render-tree.schema.json +883 -0
- package/lib/json-schema/tenant-product-config.schema.json +88 -0
- package/lib/json-schema/tenant-runtime-config.schema.json +88 -0
- package/lib/json-schema/view-provider-descriptor.schema.json +1 -0
- package/lib/json-schema/workbench-release.schema.json +1520 -626
- package/lib/json-schema/workbench-runtime-bundle.schema.json +686 -20
- package/lib/json-schema/workbench.schema.json +1196 -0
- package/lib/runtime/component-assembly.d.ts +4 -0
- package/lib/runtime/component-assembly.d.ts.map +1 -0
- package/lib/runtime/component-assembly.js +126 -0
- package/lib/runtime/component-assembly.js.map +1 -0
- package/lib/runtime/declarative-control-compiler.d.ts +106 -20
- package/lib/runtime/declarative-control-compiler.d.ts.map +1 -1
- package/lib/runtime/declarative-control-compiler.js +1525 -280
- package/lib/runtime/declarative-control-compiler.js.map +1 -1
- package/lib/runtime/index.d.ts +5 -0
- package/lib/runtime/index.d.ts.map +1 -1
- package/lib/runtime/index.js +7 -0
- package/lib/runtime/index.js.map +1 -1
- package/lib/runtime/page-expression.d.ts +9 -0
- package/lib/runtime/page-expression.d.ts.map +1 -0
- package/lib/runtime/page-expression.js +257 -0
- package/lib/runtime/page-expression.js.map +1 -0
- package/lib/runtime/page-stream.d.ts +24 -0
- package/lib/runtime/page-stream.d.ts.map +1 -0
- package/lib/runtime/page-stream.js +65 -0
- package/lib/runtime/page-stream.js.map +1 -0
- package/lib/runtime/record-editor-draft.d.ts +29 -0
- package/lib/runtime/record-editor-draft.d.ts.map +1 -0
- package/lib/runtime/record-editor-draft.js +161 -0
- package/lib/runtime/record-editor-draft.js.map +1 -0
- package/lib/runtime/target-render-model.d.ts +4 -0
- package/lib/runtime/target-render-model.d.ts.map +1 -0
- package/lib/runtime/target-render-model.js +28 -0
- package/lib/runtime/target-render-model.js.map +1 -0
- package/lib/runtime/tenant-config.d.ts +2 -0
- package/lib/runtime/tenant-config.d.ts.map +1 -1
- package/lib/runtime/tenant-config.js +6 -1
- package/lib/runtime/tenant-config.js.map +1 -1
- package/lib/schemas/index.d.ts +5969 -2731
- package/lib/schemas/index.d.ts.map +1 -1
- package/lib/schemas/index.js +174 -95
- package/lib/schemas/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveWorkbenchEntry = exports.validateWorkbenchRuntimeSet = exports.compileNavigationRuntimeBundle = exports.compileWorkbenchRuntimeBundle = exports.compilePageRuntimeBundle = exports.materializeRouteSpacePath = exports.compileRouteClaims = exports.accessPolicyChainImplies = exports.declarativeProductResourceRevisionHash = exports.declarativeProductResourceSourceHash = exports.declarativeRouteSpaceRevisionHash = exports.declarativeRouteSpaceSourceHash = exports.declarativeCreateTemplateRevisionHash = exports.declarativeCreateTemplateSourceHash = exports.canonicalContentHash = exports.sameRevisionRef = exports.sameStableRef = exports.revisionRefKey = exports.stableRefKey = exports.DeclarativeControlCompilationError = void 0;
|
|
3
|
+
exports.resolveWorkbenchEntry = exports.validateWorkbenchRuntimeSet = exports.compileNavigationRuntimeBundle = exports.compileWorkbenchRuntimeBundle = exports.compilePageRuntimeBundle = exports.resolvePage = exports.materializeRouteSpacePath = exports.matchCompiledRouteClaim = exports.compileRouteClaims = exports.compiledRouteClaimsOverlap = exports.accessPolicyChainImplies = exports.parseExactDesignCapabilityCatalogArtifact = exports.parseExactProductDeclarativeCapabilityRegistration = exports.compileDesignCapabilityCatalogArtifact = exports.designCapabilityCatalogArtifactHash = exports.declarativeProductResourceRevisionHash = exports.declarativeProductResourceSourceHash = exports.declarativeRouteSpaceRevisionHash = exports.declarativeRouteSpaceSourceHash = exports.declarativeCreateTemplateRevisionHash = exports.declarativeCreateTemplateSourceHash = exports.compileDeclarativeWorkbenchCatalog = exports.compileDeclarativeRouteOwnerIndex = exports.permissionAlternativePolicyAllows = exports.parseExactLegacyRouteTakeoverAuthorization = exports.legacyRouteTakeoverAuthorizationHash = exports.compileLegacyRouteTakeoverAuthorization = exports.declarativeWorkbenchCatalogProjectionId = exports.declarativeRouteOwnerIndexProjectionId = exports.declarativeRuntimeProjectionId = exports.canonicalContentHash = exports.sameRevisionRef = exports.sameStableRef = exports.revisionRefKey = exports.stableRefKey = exports.DeclarativeControlCompilationError = void 0;
|
|
4
4
|
const common_1 = require("../contracts/common");
|
|
5
|
+
const declarative_control_http_1 = require("../contracts/declarative-control-http");
|
|
5
6
|
const render_tree_compiler_1 = require("./render-tree-compiler");
|
|
7
|
+
const page_expression_1 = require("../contracts/page-expression");
|
|
6
8
|
const declarative_control_1 = require("../contracts/declarative-control");
|
|
7
9
|
class DeclarativeControlCompilationError extends Error {
|
|
8
10
|
constructor(code, path, message) {
|
|
9
11
|
super(`${code} at ${path}: ${message}`);
|
|
10
12
|
this.code = code;
|
|
11
13
|
this.path = path;
|
|
12
|
-
this.name =
|
|
14
|
+
this.name = "DeclarativeControlCompilationError";
|
|
13
15
|
}
|
|
14
16
|
}
|
|
15
17
|
exports.DeclarativeControlCompilationError = DeclarativeControlCompilationError;
|
|
16
18
|
const scopedIdentityKey = (reference) => `${reference.kind}:${reference.id}:${reference.ownerRepo}`;
|
|
17
|
-
const scopeKey = (scope) => `${scopedIdentityKey(scope.tenantRef)}:${scopedIdentityKey(scope.dataSpaceRef)}:${scope.teamRef ? scopedIdentityKey(scope.teamRef) :
|
|
18
|
-
const stableRefKey = (reference) => `${reference.visibility}:${reference.tenantScope ? scopeKey(reference.tenantScope) :
|
|
19
|
+
const scopeKey = (scope) => `${scopedIdentityKey(scope.tenantRef)}:${scopedIdentityKey(scope.dataSpaceRef)}:${scope.teamRef ? scopedIdentityKey(scope.teamRef) : ""}`;
|
|
20
|
+
const stableRefKey = (reference) => `${reference.visibility}:${reference.tenantScope ? scopeKey(reference.tenantScope) : ""}:${reference.kind}:${reference.id}:${reference.ownerRepo}`;
|
|
19
21
|
exports.stableRefKey = stableRefKey;
|
|
20
22
|
const revisionRefKey = (reference) => `${(0, exports.stableRefKey)(reference)}@${reference.revision}:v${reference.schemaVersion}:${reference.contentHash.toLowerCase()}`;
|
|
21
23
|
exports.revisionRefKey = revisionRefKey;
|
|
@@ -27,38 +29,41 @@ const assertUniqueRegistryKeys = (keys, path) => {
|
|
|
27
29
|
const seen = new Set();
|
|
28
30
|
keys.forEach((key, index) => {
|
|
29
31
|
if (seen.has(key)) {
|
|
30
|
-
throw new DeclarativeControlCompilationError(
|
|
32
|
+
throw new DeclarativeControlCompilationError("REGISTRY_CONFLICT", `${path}[${index}]`, `Duplicate registry identity: ${key}`);
|
|
31
33
|
}
|
|
32
34
|
seen.add(key);
|
|
33
35
|
});
|
|
34
36
|
};
|
|
35
37
|
const stableSerialize = (value) => {
|
|
36
38
|
if (value === undefined)
|
|
37
|
-
return
|
|
38
|
-
if (value === null || typeof value !==
|
|
39
|
-
return JSON.stringify(value) ??
|
|
39
|
+
return "null";
|
|
40
|
+
if (value === null || typeof value !== "object")
|
|
41
|
+
return JSON.stringify(value) ?? "null";
|
|
40
42
|
if (Array.isArray(value))
|
|
41
|
-
return `[${value.map((item) => stableSerialize(item)).join(
|
|
43
|
+
return `[${value.map((item) => stableSerialize(item)).join(",")}]`;
|
|
42
44
|
const record = value;
|
|
43
45
|
return `{${Object.keys(record)
|
|
44
46
|
.filter((key) => record[key] !== undefined)
|
|
45
47
|
.sort()
|
|
46
48
|
.map((key) => `${JSON.stringify(key)}:${stableSerialize(record[key])}`)
|
|
47
|
-
.join(
|
|
49
|
+
.join(",")}}`;
|
|
48
50
|
};
|
|
49
51
|
const SHA256_INITIAL = [
|
|
50
|
-
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
|
51
|
-
|
|
52
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,
|
|
53
|
+
0x1f83d9ab, 0x5be0cd19,
|
|
52
54
|
];
|
|
53
55
|
const SHA256_ROUND = [
|
|
54
|
-
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
|
55
|
-
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
|
57
|
+
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
|
58
|
+
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
|
59
|
+
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
60
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
|
|
61
|
+
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
|
62
|
+
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
|
|
63
|
+
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
64
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
|
|
65
|
+
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
|
66
|
+
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
62
67
|
];
|
|
63
68
|
const rotateRight = (value, amount) => (value >>> amount) | (value << (32 - amount));
|
|
64
69
|
const sha256Hex = (value) => {
|
|
@@ -78,14 +83,20 @@ const sha256Hex = (value) => {
|
|
|
78
83
|
for (let offset = 0; offset < source.length; offset += 64) {
|
|
79
84
|
for (let index = 0; index < 16; index += 1) {
|
|
80
85
|
const start = offset + index * 4;
|
|
81
|
-
words[index] =
|
|
86
|
+
words[index] =
|
|
87
|
+
((source[start] << 24) |
|
|
88
|
+
(source[start + 1] << 16) |
|
|
89
|
+
(source[start + 2] << 8) |
|
|
90
|
+
source[start + 3]) >>>
|
|
91
|
+
0;
|
|
82
92
|
}
|
|
83
93
|
for (let index = 16; index < 64; index += 1) {
|
|
84
94
|
const previous = words[index - 15];
|
|
85
95
|
const recent = words[index - 2];
|
|
86
96
|
const small0 = rotateRight(previous, 7) ^ rotateRight(previous, 18) ^ (previous >>> 3);
|
|
87
97
|
const small1 = rotateRight(recent, 17) ^ rotateRight(recent, 19) ^ (recent >>> 10);
|
|
88
|
-
words[index] =
|
|
98
|
+
words[index] =
|
|
99
|
+
(words[index - 16] + small0 + words[index - 7] + small1) >>> 0;
|
|
89
100
|
}
|
|
90
101
|
let [a, b, c, d, e, f, g, h] = hash;
|
|
91
102
|
for (let index = 0; index < 64; index += 1) {
|
|
@@ -113,10 +124,113 @@ const sha256Hex = (value) => {
|
|
|
113
124
|
hash[6] = (hash[6] + g) >>> 0;
|
|
114
125
|
hash[7] = (hash[7] + h) >>> 0;
|
|
115
126
|
}
|
|
116
|
-
return hash.map((part) => part.toString(16).padStart(8,
|
|
127
|
+
return hash.map((part) => part.toString(16).padStart(8, "0")).join("");
|
|
117
128
|
};
|
|
118
129
|
const canonicalContentHash = (value) => sha256Hex(stableSerialize(value));
|
|
119
130
|
exports.canonicalContentHash = canonicalContentHash;
|
|
131
|
+
/**
|
|
132
|
+
* Names one immutable Runtime Bundle projection by its exact Release evidence.
|
|
133
|
+
* The fixed-size digest stays below Data Server's 256-byte projection id limit
|
|
134
|
+
* even when a governed release slot uses the maximum contract identifier size.
|
|
135
|
+
*/
|
|
136
|
+
const declarativeRuntimeProjectionId = (resourceKind, releaseSlotId, releaseRevisionRef) => `drb:${(0, exports.canonicalContentHash)({ resourceKind, releaseSlotId, releaseRevisionRef })}`;
|
|
137
|
+
exports.declarativeRuntimeProjectionId = declarativeRuntimeProjectionId;
|
|
138
|
+
/** Names the rebuildable route-owner index for one tenant environment surface. */
|
|
139
|
+
const declarativeRouteOwnerIndexProjectionId = (tenantScope, environmentRef, surface) => `dri:${(0, exports.canonicalContentHash)({
|
|
140
|
+
tenantScope: {
|
|
141
|
+
teamRef: tenantScope.teamRef
|
|
142
|
+
? { kind: tenantScope.teamRef.kind, id: tenantScope.teamRef.id }
|
|
143
|
+
: null,
|
|
144
|
+
},
|
|
145
|
+
environmentRef,
|
|
146
|
+
surface,
|
|
147
|
+
})}`;
|
|
148
|
+
exports.declarativeRouteOwnerIndexProjectionId = declarativeRouteOwnerIndexProjectionId;
|
|
149
|
+
const declarativeWorkbenchCatalogProjectionId = (tenantScope, environmentRef, surface) => `dwc:${(0, exports.canonicalContentHash)({
|
|
150
|
+
tenantScope: {
|
|
151
|
+
teamRef: tenantScope.teamRef
|
|
152
|
+
? { kind: tenantScope.teamRef.kind, id: tenantScope.teamRef.id }
|
|
153
|
+
: null,
|
|
154
|
+
},
|
|
155
|
+
environmentRef,
|
|
156
|
+
surface,
|
|
157
|
+
})}`;
|
|
158
|
+
exports.declarativeWorkbenchCatalogProjectionId = declarativeWorkbenchCatalogProjectionId;
|
|
159
|
+
const takeoverHashSource = (authorization) => authorization;
|
|
160
|
+
const compileLegacyRouteTakeoverAuthorization = (input) => {
|
|
161
|
+
const unsigned = {
|
|
162
|
+
...input,
|
|
163
|
+
contract: "LegacyRouteTakeoverAuthorization",
|
|
164
|
+
schemaVersion: 1,
|
|
165
|
+
};
|
|
166
|
+
return deepFreeze(declarative_control_1.LegacyRouteTakeoverAuthorizationSchema.parse({
|
|
167
|
+
...unsigned,
|
|
168
|
+
contentHash: (0, exports.canonicalContentHash)(takeoverHashSource(unsigned)),
|
|
169
|
+
}));
|
|
170
|
+
};
|
|
171
|
+
exports.compileLegacyRouteTakeoverAuthorization = compileLegacyRouteTakeoverAuthorization;
|
|
172
|
+
const legacyRouteTakeoverAuthorizationHash = (input) => (0, exports.canonicalContentHash)(takeoverHashSource(input));
|
|
173
|
+
exports.legacyRouteTakeoverAuthorizationHash = legacyRouteTakeoverAuthorizationHash;
|
|
174
|
+
const parseExactLegacyRouteTakeoverAuthorization = (input) => {
|
|
175
|
+
const parsed = declarative_control_1.LegacyRouteTakeoverAuthorizationSchema.parse(input);
|
|
176
|
+
const { contentHash, ...unsigned } = parsed;
|
|
177
|
+
if (contentHash !== (0, exports.legacyRouteTakeoverAuthorizationHash)(unsigned)) {
|
|
178
|
+
throw new DeclarativeControlCompilationError("ROUTE_TAKEOVER_INVALID", "contentHash", "Legacy route takeover authorization content hash is not exact.");
|
|
179
|
+
}
|
|
180
|
+
return deepFreeze(parsed);
|
|
181
|
+
};
|
|
182
|
+
exports.parseExactLegacyRouteTakeoverAuthorization = parseExactLegacyRouteTakeoverAuthorization;
|
|
183
|
+
const permissionAlternativePolicyAllows = (policyInput, permissionCodes) => {
|
|
184
|
+
const parsed = declarative_control_1.PermissionAlternativePolicySchema.parse(policyInput);
|
|
185
|
+
const available = permissionCodes instanceof Set ? permissionCodes : new Set(permissionCodes);
|
|
186
|
+
return parsed.alternatives.some((alternative) => alternative.every((permission) => available.has(permission)));
|
|
187
|
+
};
|
|
188
|
+
exports.permissionAlternativePolicyAllows = permissionAlternativePolicyAllows;
|
|
189
|
+
const compileDeclarativeRouteOwnerIndex = (input) => {
|
|
190
|
+
const entries = input.entries
|
|
191
|
+
.map((entry) => ({ ...entry }))
|
|
192
|
+
.sort((left, right) => {
|
|
193
|
+
const leftKey = `${left.resourceKind}:${left.releaseSlotId}`;
|
|
194
|
+
const rightKey = `${right.resourceKind}:${right.releaseSlotId}`;
|
|
195
|
+
return leftKey.localeCompare(rightKey);
|
|
196
|
+
});
|
|
197
|
+
const unsigned = {
|
|
198
|
+
contract: "DeclarativeRouteOwnerIndex",
|
|
199
|
+
schemaVersion: 1,
|
|
200
|
+
tenantScope: declarative_control_1.TenantScopeSchema.parse(input.tenantScope),
|
|
201
|
+
environmentRef: declarative_control_1.StableRefSchema.parse(input.environmentRef),
|
|
202
|
+
surface: input.surface,
|
|
203
|
+
generation: input.generation,
|
|
204
|
+
entries,
|
|
205
|
+
rebuildable: true,
|
|
206
|
+
};
|
|
207
|
+
return deepFreeze(declarative_control_1.DeclarativeRouteOwnerIndexSchema.parse({
|
|
208
|
+
...unsigned,
|
|
209
|
+
contentHash: (0, exports.canonicalContentHash)(unsigned),
|
|
210
|
+
}));
|
|
211
|
+
};
|
|
212
|
+
exports.compileDeclarativeRouteOwnerIndex = compileDeclarativeRouteOwnerIndex;
|
|
213
|
+
const compileDeclarativeWorkbenchCatalog = (input) => {
|
|
214
|
+
const entries = input.entries
|
|
215
|
+
.map((entry) => ({ ...entry }))
|
|
216
|
+
.sort((left, right) => left.order - right.order ||
|
|
217
|
+
left.workbenchId.localeCompare(right.workbenchId));
|
|
218
|
+
const unsigned = {
|
|
219
|
+
contract: "DeclarativeWorkbenchCatalog",
|
|
220
|
+
schemaVersion: 1,
|
|
221
|
+
tenantScope: declarative_control_1.TenantScopeSchema.parse(input.tenantScope),
|
|
222
|
+
environmentRef: declarative_control_1.StableRefSchema.parse(input.environmentRef),
|
|
223
|
+
surface: input.surface,
|
|
224
|
+
generation: input.generation,
|
|
225
|
+
entries,
|
|
226
|
+
rebuildable: true,
|
|
227
|
+
};
|
|
228
|
+
return deepFreeze(declarative_control_1.DeclarativeWorkbenchCatalogSchema.parse({
|
|
229
|
+
...unsigned,
|
|
230
|
+
contentHash: (0, exports.canonicalContentHash)(unsigned),
|
|
231
|
+
}));
|
|
232
|
+
};
|
|
233
|
+
exports.compileDeclarativeWorkbenchCatalog = compileDeclarativeWorkbenchCatalog;
|
|
120
234
|
const declarativeCreateTemplateSourceHash = (document) => (0, exports.canonicalContentHash)(document);
|
|
121
235
|
exports.declarativeCreateTemplateSourceHash = declarativeCreateTemplateSourceHash;
|
|
122
236
|
const declarativeCreateTemplateRevisionHash = (registration) => (0, exports.canonicalContentHash)(registration.document);
|
|
@@ -129,20 +243,124 @@ const declarativeProductResourceSourceHash = (document) => (0, exports.canonical
|
|
|
129
243
|
exports.declarativeProductResourceSourceHash = declarativeProductResourceSourceHash;
|
|
130
244
|
const declarativeProductResourceRevisionHash = (registration) => (0, exports.canonicalContentHash)(registration.document);
|
|
131
245
|
exports.declarativeProductResourceRevisionHash = declarativeProductResourceRevisionHash;
|
|
246
|
+
const designCapabilityCatalogHashSource = (input) => ({
|
|
247
|
+
contract: "DesignCapabilityCatalogArtifact",
|
|
248
|
+
schemaVersion: 1,
|
|
249
|
+
sourceRef: input.sourceRef,
|
|
250
|
+
entries: input.entries,
|
|
251
|
+
});
|
|
252
|
+
const designCapabilityCatalogArtifactHash = (input) => (0, exports.canonicalContentHash)(designCapabilityCatalogHashSource(input));
|
|
253
|
+
exports.designCapabilityCatalogArtifactHash = designCapabilityCatalogArtifactHash;
|
|
254
|
+
const compileDesignCapabilityCatalogArtifact = (input) => {
|
|
255
|
+
const entries = input.entries
|
|
256
|
+
.map((entry) => ({ ...entry }))
|
|
257
|
+
.sort((left, right) => left.manifest.id.localeCompare(right.manifest.id));
|
|
258
|
+
entries.forEach((entry, index) => {
|
|
259
|
+
const registration = entry.declarativeRegistration;
|
|
260
|
+
const { registrationRevisionRef: _registrationRevisionRef, sourceContentHash: _sourceContentHash, ...unsignedRegistration } = registration;
|
|
261
|
+
const registrationHash = (0, exports.canonicalContentHash)(unsignedRegistration);
|
|
262
|
+
if (registration.capabilityRevisionRef.id !== entry.manifest.id ||
|
|
263
|
+
registration.capabilityRevisionRef.ownerRepo !==
|
|
264
|
+
entry.manifest.ownerRepo ||
|
|
265
|
+
String(registration.capabilityRevisionRef.revision) !==
|
|
266
|
+
String(entry.manifest.capabilityVersion) ||
|
|
267
|
+
registration.capabilityRevisionRef.contentHash !==
|
|
268
|
+
(0, exports.canonicalContentHash)(entry.manifest) ||
|
|
269
|
+
registration.providerRevisionRef.id !== entry.provider.providerId ||
|
|
270
|
+
registration.providerRevisionRef.ownerRepo !== entry.provider.ownerRepo ||
|
|
271
|
+
String(registration.providerRevisionRef.revision) !==
|
|
272
|
+
String(entry.provider.providerVersion) ||
|
|
273
|
+
registration.providerRevisionRef.contentHash !==
|
|
274
|
+
(0, exports.canonicalContentHash)(entry.provider) ||
|
|
275
|
+
registration.sourceContentHash !== registrationHash ||
|
|
276
|
+
registration.registrationRevisionRef.contentHash !== registrationHash) {
|
|
277
|
+
throw new DeclarativeControlCompilationError("DESIGN_CATALOG_INVALID", `entries[${index}]`, "Design capability manifest, provider and authoring registration are not exact.");
|
|
278
|
+
}
|
|
279
|
+
registration.schemaDocuments.forEach((schema, schemaIndex) => {
|
|
280
|
+
if (schema.schemaRevisionRef.ownerRepo !== "monkeys-design" ||
|
|
281
|
+
schema.schemaRevisionRef.visibility !== "global" ||
|
|
282
|
+
schema.schemaRevisionRef.contentHash !==
|
|
283
|
+
(0, exports.canonicalContentHash)(schema.document)) {
|
|
284
|
+
throw new DeclarativeControlCompilationError("DESIGN_CATALOG_INVALID", `entries[${index}].declarativeRegistration.schemaDocuments[${schemaIndex}]`, "Design Schema documents require exact global monkeys-design authority.");
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
const sourceContentHash = (0, exports.designCapabilityCatalogArtifactHash)({
|
|
289
|
+
sourceRef: input.sourceRef,
|
|
290
|
+
entries,
|
|
291
|
+
});
|
|
292
|
+
const sourceRevisionRef = declarative_control_1.RevisionRefSchema.parse({
|
|
293
|
+
kind: "design-capability-catalog",
|
|
294
|
+
id: input.sourceRef,
|
|
295
|
+
ownerRepo: "monkeys-design",
|
|
296
|
+
visibility: "global",
|
|
297
|
+
revision: input.revision,
|
|
298
|
+
schemaVersion: 1,
|
|
299
|
+
contentHash: sourceContentHash,
|
|
300
|
+
});
|
|
301
|
+
return deepFreeze(declarative_control_http_1.DesignCapabilityCatalogArtifactSchema.parse({
|
|
302
|
+
contract: "DesignCapabilityCatalogArtifact",
|
|
303
|
+
schemaVersion: 1,
|
|
304
|
+
sourceRef: input.sourceRef,
|
|
305
|
+
sourceRevisionRef,
|
|
306
|
+
entries,
|
|
307
|
+
sourceContentHash,
|
|
308
|
+
}));
|
|
309
|
+
};
|
|
310
|
+
exports.compileDesignCapabilityCatalogArtifact = compileDesignCapabilityCatalogArtifact;
|
|
311
|
+
/** Validate immutable product-owned capability provenance from an application release. */
|
|
312
|
+
const parseExactProductDeclarativeCapabilityRegistration = (input) => {
|
|
313
|
+
const entry = declarative_control_http_1.ProductDeclarativeCapabilityRegistrationSchema.parse(input);
|
|
314
|
+
const registration = entry.declarativeRegistration;
|
|
315
|
+
const { registrationRevisionRef: _registrationRevisionRef, sourceContentHash: _sourceContentHash, ...unsignedRegistration } = registration;
|
|
316
|
+
if (registration.capabilityRevisionRef.contentHash !==
|
|
317
|
+
(0, exports.canonicalContentHash)(entry.manifest) ||
|
|
318
|
+
registration.providerRevisionRef.contentHash !==
|
|
319
|
+
(0, exports.canonicalContentHash)(entry.provider) ||
|
|
320
|
+
registration.sourceContentHash !==
|
|
321
|
+
(0, exports.canonicalContentHash)(unsignedRegistration) ||
|
|
322
|
+
registration.registrationRevisionRef.contentHash !==
|
|
323
|
+
registration.sourceContentHash) {
|
|
324
|
+
throw new DeclarativeControlCompilationError("PRODUCT_CAPABILITY_CATALOG_INVALID", "declarativeRegistration", "Product capability manifest, provider and authoring registration are not exact.");
|
|
325
|
+
}
|
|
326
|
+
registration.schemaDocuments.forEach((schema, schemaIndex) => {
|
|
327
|
+
if (schema.schemaRevisionRef.contentHash !==
|
|
328
|
+
(0, exports.canonicalContentHash)(schema.document)) {
|
|
329
|
+
throw new DeclarativeControlCompilationError("PRODUCT_CAPABILITY_CATALOG_INVALID", `declarativeRegistration.schemaDocuments[${schemaIndex}]`, "Product capability Schema document does not match its exact RevisionRef.");
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
return deepFreeze(entry);
|
|
333
|
+
};
|
|
334
|
+
exports.parseExactProductDeclarativeCapabilityRegistration = parseExactProductDeclarativeCapabilityRegistration;
|
|
335
|
+
const parseExactDesignCapabilityCatalogArtifact = (input) => {
|
|
336
|
+
const parsed = declarative_control_http_1.DesignCapabilityCatalogArtifactSchema.parse(input);
|
|
337
|
+
const expected = (0, exports.compileDesignCapabilityCatalogArtifact)({
|
|
338
|
+
sourceRef: parsed.sourceRef,
|
|
339
|
+
revision: parsed.sourceRevisionRef.revision,
|
|
340
|
+
entries: parsed.entries,
|
|
341
|
+
});
|
|
342
|
+
if (parsed.sourceContentHash !== expected.sourceContentHash ||
|
|
343
|
+
!(0, exports.sameRevisionRef)(parsed.sourceRevisionRef, expected.sourceRevisionRef)) {
|
|
344
|
+
throw new DeclarativeControlCompilationError("DESIGN_CATALOG_INVALID", "sourceRevisionRef", "Design capability catalog artifact provenance or content hash is not exact.");
|
|
345
|
+
}
|
|
346
|
+
return deepFreeze(parsed);
|
|
347
|
+
};
|
|
348
|
+
exports.parseExactDesignCapabilityCatalogArtifact = parseExactDesignCapabilityCatalogArtifact;
|
|
132
349
|
const deepFreeze = (value) => {
|
|
133
|
-
if (value === null || typeof value !==
|
|
350
|
+
if (value === null || typeof value !== "object" || Object.isFrozen(value))
|
|
134
351
|
return value;
|
|
135
352
|
Object.values(value).forEach((child) => deepFreeze(child));
|
|
136
353
|
return Object.freeze(value);
|
|
137
354
|
};
|
|
138
355
|
const assertTenantCompatible = (reference, scope, path) => {
|
|
139
|
-
if (reference.visibility ===
|
|
140
|
-
|
|
356
|
+
if (reference.visibility === "tenant" &&
|
|
357
|
+
scopeKey(reference.tenantScope) !== scopeKey(scope)) {
|
|
358
|
+
throw new DeclarativeControlCompilationError("CROSS_TENANT_REFERENCE", path, `Reference ${reference.kind}/${reference.id} belongs to another tenant scope.`);
|
|
141
359
|
}
|
|
142
360
|
};
|
|
143
361
|
const assertSameTenantScope = (candidate, expected, path) => {
|
|
144
362
|
if (scopeKey(candidate) !== scopeKey(expected)) {
|
|
145
|
-
throw new DeclarativeControlCompilationError(
|
|
363
|
+
throw new DeclarativeControlCompilationError("CROSS_TENANT_REFERENCE", path, "Record and release tenant scopes do not match.");
|
|
146
364
|
}
|
|
147
365
|
};
|
|
148
366
|
const stableFromRevision = (reference) => declarative_control_1.StableRefSchema.parse({
|
|
@@ -154,7 +372,7 @@ const stableFromRevision = (reference) => declarative_control_1.StableRefSchema.
|
|
|
154
372
|
});
|
|
155
373
|
const assertRevisionIdentity = (reference, expectedKind, expectedId, scope, path) => {
|
|
156
374
|
if (reference.kind !== expectedKind || reference.id !== expectedId) {
|
|
157
|
-
throw new DeclarativeControlCompilationError(
|
|
375
|
+
throw new DeclarativeControlCompilationError("REVISION_MISMATCH", path, `Expected ${expectedKind}/${expectedId}, received ${reference.kind}/${reference.id}.`);
|
|
158
376
|
}
|
|
159
377
|
assertTenantCompatible(reference, scope, path);
|
|
160
378
|
};
|
|
@@ -164,22 +382,33 @@ const requireDependencies = (snapshot, required) => {
|
|
|
164
382
|
for (const dependency of required) {
|
|
165
383
|
const key = dependencyKey(dependency.role, dependency.revisionRef);
|
|
166
384
|
if (!available.has(key)) {
|
|
167
|
-
throw new DeclarativeControlCompilationError(
|
|
385
|
+
throw new DeclarativeControlCompilationError("DEPENDENCY_MISSING", "dependencySnapshot", `Missing exact ${dependency.role} dependency ${(0, exports.revisionRefKey)(dependency.revisionRef)}.`);
|
|
168
386
|
}
|
|
169
387
|
}
|
|
170
388
|
};
|
|
171
389
|
const assertDependencyScopes = (snapshot, scope) => snapshot.forEach((dependency, index) => assertTenantCompatible(dependency.revisionRef, scope, `dependencySnapshot[${index}].revisionRef`));
|
|
172
390
|
const requireReleaseReady = (release) => {
|
|
173
|
-
if (release.
|
|
174
|
-
throw new DeclarativeControlCompilationError(
|
|
391
|
+
if (release.operation === "deactivate") {
|
|
392
|
+
throw new DeclarativeControlCompilationError("RELEASE_OPERATION_INVALID", "operation", "A deactivation Release cannot produce an active runtime bundle.");
|
|
393
|
+
}
|
|
394
|
+
if (release.evidence.validation.result !== "pass") {
|
|
395
|
+
throw new DeclarativeControlCompilationError("RELEASE_VALIDATION_FAILED", "evidence.validation.result", "Runtime bundles require a passing release validation result.");
|
|
175
396
|
}
|
|
176
397
|
};
|
|
177
398
|
const refSet = (values) => new Set(values.map(exports.stableRefKey));
|
|
178
399
|
const chainRequiresAll = (policies, field) => {
|
|
179
|
-
if (field ===
|
|
180
|
-
return new Set(policies.flatMap((policy) =>
|
|
181
|
-
|
|
182
|
-
|
|
400
|
+
if (field === "permissionAllOf") {
|
|
401
|
+
return new Set(policies.flatMap((policy) => [
|
|
402
|
+
...policy.permissionAllOf,
|
|
403
|
+
...(policy.permissionAnyOf.length === 1 ? policy.permissionAnyOf : []),
|
|
404
|
+
]));
|
|
405
|
+
}
|
|
406
|
+
if (field === "groupAllOf") {
|
|
407
|
+
return refSet(policies.flatMap((policy) => [
|
|
408
|
+
...policy.groupAllOf,
|
|
409
|
+
...(policy.groupAnyOf.length === 1 ? policy.groupAnyOf : []),
|
|
410
|
+
]));
|
|
411
|
+
}
|
|
183
412
|
return new Set(policies.flatMap((policy) => policy.conditionAllOf.map(exports.revisionRefKey)));
|
|
184
413
|
};
|
|
185
414
|
const chainImpliesAny = (policies, targetAny, allValues, anyValues) => {
|
|
@@ -197,17 +426,17 @@ const accessPolicyChainImplies = (policyInputs, targetInput) => {
|
|
|
197
426
|
const target = declarative_control_1.AccessPolicySchema.parse(targetInput);
|
|
198
427
|
if (target.authenticated && !policies.some((policy) => policy.authenticated))
|
|
199
428
|
return false;
|
|
200
|
-
const permissionAll = chainRequiresAll(policies,
|
|
429
|
+
const permissionAll = chainRequiresAll(policies, "permissionAllOf");
|
|
201
430
|
if (target.permissionAllOf.some((permission) => !permissionAll.has(permission)))
|
|
202
431
|
return false;
|
|
203
432
|
if (!chainImpliesAny(policies, new Set(target.permissionAnyOf), permissionAll, (policy) => policy.permissionAnyOf))
|
|
204
433
|
return false;
|
|
205
|
-
const groupAll = chainRequiresAll(policies,
|
|
434
|
+
const groupAll = chainRequiresAll(policies, "groupAllOf");
|
|
206
435
|
if (target.groupAllOf.some((group) => !groupAll.has((0, exports.stableRefKey)(group))))
|
|
207
436
|
return false;
|
|
208
437
|
if (!chainImpliesAny(policies, refSet(target.groupAnyOf), groupAll, (policy) => policy.groupAnyOf.map(exports.stableRefKey)))
|
|
209
438
|
return false;
|
|
210
|
-
const conditionAll = chainRequiresAll(policies,
|
|
439
|
+
const conditionAll = chainRequiresAll(policies, "conditionAllOf");
|
|
211
440
|
if (target.conditionAllOf.some((condition) => !conditionAll.has((0, exports.revisionRefKey)(condition))))
|
|
212
441
|
return false;
|
|
213
442
|
return true;
|
|
@@ -217,91 +446,260 @@ const normalizePath = (path, routeSpace) => {
|
|
|
217
446
|
let normalized = path;
|
|
218
447
|
if (!routeSpace.caseSensitive) {
|
|
219
448
|
normalized = normalized
|
|
220
|
-
.split(
|
|
221
|
-
.map((segment) => segment.startsWith(
|
|
222
|
-
.join(
|
|
223
|
-
}
|
|
224
|
-
if (routeSpace.trailingSlash ===
|
|
225
|
-
normalized = normalized.replace(/\/+$/,
|
|
226
|
-
if (routeSpace.trailingSlash ===
|
|
227
|
-
normalized
|
|
449
|
+
.split("/")
|
|
450
|
+
.map((segment) => segment.startsWith(":") ? segment : segment.toLowerCase())
|
|
451
|
+
.join("/");
|
|
452
|
+
}
|
|
453
|
+
if (routeSpace.trailingSlash === "remove" && normalized.length > 1)
|
|
454
|
+
normalized = normalized.replace(/\/+$/, "");
|
|
455
|
+
if (routeSpace.trailingSlash === "require" &&
|
|
456
|
+
normalized.length > 1 &&
|
|
457
|
+
!normalized.endsWith("/"))
|
|
458
|
+
normalized += "/";
|
|
228
459
|
return normalized;
|
|
229
460
|
};
|
|
461
|
+
const acceptedTrailingSlashStates = (claim) => {
|
|
462
|
+
if (claim.matcher.trailingSlash === "remove")
|
|
463
|
+
return new Set([false, true]);
|
|
464
|
+
if (claim.matcher.trailingSlash === "require")
|
|
465
|
+
return new Set([true]);
|
|
466
|
+
return new Set([
|
|
467
|
+
claim.normalizedPath.length > 1 && claim.normalizedPath.endsWith("/"),
|
|
468
|
+
]);
|
|
469
|
+
};
|
|
470
|
+
const routeSegmentParameter = (segment) => segment.match(/^[:$]([A-Za-z][A-Za-z0-9_-]*)$/)?.[1];
|
|
471
|
+
const parameterTypeFor = (claim, name) => claim.matcher.parameters.find((parameter) => parameter.name === name)?.type;
|
|
472
|
+
const compiledRouteClaimsOverlap = (leftInput, rightInput) => {
|
|
473
|
+
const left = declarative_control_1.CompiledRouteClaimSchema.parse(leftInput);
|
|
474
|
+
const right = declarative_control_1.CompiledRouteClaimSchema.parse(rightInput);
|
|
475
|
+
if (![...acceptedTrailingSlashStates(left)].some((state) => acceptedTrailingSlashStates(right).has(state)))
|
|
476
|
+
return false;
|
|
477
|
+
const leftSegments = left.normalizedPath.split("/").filter(Boolean);
|
|
478
|
+
const rightSegments = right.normalizedPath.split("/").filter(Boolean);
|
|
479
|
+
if (leftSegments.length !== rightSegments.length)
|
|
480
|
+
return false;
|
|
481
|
+
return leftSegments.every((leftSegment, index) => {
|
|
482
|
+
const rightSegment = rightSegments[index];
|
|
483
|
+
const leftParameter = routeSegmentParameter(leftSegment);
|
|
484
|
+
const rightParameter = routeSegmentParameter(rightSegment);
|
|
485
|
+
if (!leftParameter && !rightParameter) {
|
|
486
|
+
if (left.matcher.caseSensitive && right.matcher.caseSensitive)
|
|
487
|
+
return leftSegment === rightSegment;
|
|
488
|
+
return leftSegment.toLowerCase() === rightSegment.toLowerCase();
|
|
489
|
+
}
|
|
490
|
+
if (leftParameter && !rightParameter) {
|
|
491
|
+
const type = parameterTypeFor(left, leftParameter);
|
|
492
|
+
return !!type && routeParameterPattern(type).test(rightSegment);
|
|
493
|
+
}
|
|
494
|
+
if (!leftParameter && rightParameter) {
|
|
495
|
+
const type = parameterTypeFor(right, rightParameter);
|
|
496
|
+
return !!type && routeParameterPattern(type).test(leftSegment);
|
|
497
|
+
}
|
|
498
|
+
const leftType = parameterTypeFor(left, leftParameter);
|
|
499
|
+
const rightType = parameterTypeFor(right, rightParameter);
|
|
500
|
+
if (!leftType || !rightType)
|
|
501
|
+
return false;
|
|
502
|
+
return (!(leftType === "integer" && rightType === "uuid") &&
|
|
503
|
+
!(leftType === "uuid" && rightType === "integer"));
|
|
504
|
+
});
|
|
505
|
+
};
|
|
506
|
+
exports.compiledRouteClaimsOverlap = compiledRouteClaimsOverlap;
|
|
230
507
|
const compileRouteClaims = (input) => {
|
|
508
|
+
const embeddedTakeovers = input.claims.flatMap((claim) => {
|
|
509
|
+
const authorization = "legacyRouteTakeoverAuthorization" in claim
|
|
510
|
+
? claim.legacyRouteTakeoverAuthorization
|
|
511
|
+
: undefined;
|
|
512
|
+
return authorization ? [authorization] : [];
|
|
513
|
+
});
|
|
514
|
+
const takeoverAuthorizations = [
|
|
515
|
+
...(input.legacyRouteTakeoverAuthorizations ?? []),
|
|
516
|
+
...embeddedTakeovers,
|
|
517
|
+
].map((authorization, index) => {
|
|
518
|
+
try {
|
|
519
|
+
return (0, exports.parseExactLegacyRouteTakeoverAuthorization)(authorization);
|
|
520
|
+
}
|
|
521
|
+
catch (error) {
|
|
522
|
+
if (error instanceof DeclarativeControlCompilationError) {
|
|
523
|
+
throw new DeclarativeControlCompilationError(error.code, `legacyRouteTakeoverAuthorizations[${index}].${error.path}`, error.message);
|
|
524
|
+
}
|
|
525
|
+
throw error;
|
|
526
|
+
}
|
|
527
|
+
});
|
|
231
528
|
const registrations = input.routeSpaces.map((entry, index) => {
|
|
232
529
|
const revisionRef = declarative_control_1.RevisionRefSchema.parse(entry.revisionRef);
|
|
233
530
|
const routeSpace = declarative_control_1.RouteSpaceSchema.parse(entry.routeSpace);
|
|
234
|
-
if (revisionRef.kind !==
|
|
235
|
-
|
|
531
|
+
if (revisionRef.kind !== "route-space" ||
|
|
532
|
+
revisionRef.id !== routeSpace.routeSpaceId) {
|
|
533
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `routeSpaces[${index}]`, "RouteSpace registration identity does not match its pinned revision.");
|
|
236
534
|
}
|
|
237
535
|
return { revisionRef, routeSpace };
|
|
238
536
|
});
|
|
239
|
-
assertUniqueRegistryKeys(registrations.map((entry) => (0, exports.revisionRefKey)(entry.revisionRef)),
|
|
240
|
-
const routeSpacesByRevision = new Map(registrations.map((entry) => [
|
|
241
|
-
|
|
537
|
+
assertUniqueRegistryKeys(registrations.map((entry) => (0, exports.revisionRefKey)(entry.revisionRef)), "routeSpaces");
|
|
538
|
+
const routeSpacesByRevision = new Map(registrations.map((entry) => [
|
|
539
|
+
(0, exports.revisionRefKey)(entry.revisionRef),
|
|
540
|
+
entry.routeSpace,
|
|
541
|
+
]));
|
|
242
542
|
const compiled = [];
|
|
243
543
|
input.claims.forEach((inputClaim, index) => {
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
544
|
+
const { legacyRouteTakeoverAuthorization: _authorization, ...runtimeClaim } = inputClaim;
|
|
545
|
+
const legacyClaim = "surface" in runtimeClaim
|
|
546
|
+
? declarative_control_1.RouteClaimSchema.parse(runtimeClaim)
|
|
547
|
+
: declarative_control_1.LegacyRouteClaimSchema.parse(runtimeClaim);
|
|
548
|
+
const routeSpace = routeSpacesByRevision.get((0, exports.revisionRefKey)(legacyClaim.routeSpaceRevisionRef));
|
|
248
549
|
if (!routeSpace)
|
|
249
|
-
throw new DeclarativeControlCompilationError(
|
|
550
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISSING", `routeClaims[${index}].routeSpaceRevisionRef`, "No exact RouteSpace registration exists.");
|
|
250
551
|
const claim = declarative_control_1.RouteClaimSchema.parse({
|
|
251
|
-
...
|
|
252
|
-
surface:
|
|
552
|
+
...legacyClaim,
|
|
553
|
+
surface: "surface" in legacyClaim
|
|
554
|
+
? legacyClaim.surface
|
|
555
|
+
: routeSpace.supportedSurface,
|
|
253
556
|
});
|
|
254
557
|
if (routeSpace.supportedSurface !== claim.surface) {
|
|
255
|
-
throw new DeclarativeControlCompilationError(
|
|
558
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `routeClaims[${index}].surface`, `Route claim surface ${claim.surface} does not match RouteSpace ${routeSpace.routeSpaceId}.`);
|
|
256
559
|
}
|
|
257
560
|
if (claim.surface !== input.surface)
|
|
258
561
|
return;
|
|
259
562
|
const normalizedPath = normalizePath(claim.pathTemplate, routeSpace);
|
|
260
563
|
const knownParameters = new Set(routeSpace.parameters.map((parameter) => parameter.name));
|
|
261
|
-
const pathParameters = [
|
|
564
|
+
const pathParameters = [
|
|
565
|
+
...normalizedPath.matchAll(/:([A-Za-z][A-Za-z0-9_-]*)/g),
|
|
566
|
+
].map((match) => match[1]);
|
|
262
567
|
if (new Set(pathParameters).size !== pathParameters.length) {
|
|
263
|
-
throw new DeclarativeControlCompilationError(
|
|
568
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `routeClaims[${index}].pathTemplate`, "Route parameters must be unique within a path.");
|
|
264
569
|
}
|
|
265
570
|
const unknownParameter = pathParameters.find((parameter) => !knownParameters.has(parameter));
|
|
266
571
|
if (unknownParameter) {
|
|
267
|
-
throw new DeclarativeControlCompilationError(
|
|
572
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `routeClaims[${index}].pathTemplate`, `Route parameter ${unknownParameter} is not declared by ${routeSpace.routeSpaceId}.`);
|
|
268
573
|
}
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
if (normalizedPaths.has(routeIdentity))
|
|
277
|
-
throw new DeclarativeControlCompilationError('ROUTE_CONFLICT', `routeClaims[${index}].pathTemplate`, `Duplicate normalized route ${normalizedPath}.`);
|
|
278
|
-
normalizedPaths.add(routeIdentity);
|
|
279
|
-
compiled.push(declarative_control_1.CompiledRouteClaimSchema.parse({
|
|
574
|
+
const matcher = {
|
|
575
|
+
surface: claim.surface,
|
|
576
|
+
caseSensitive: routeSpace.caseSensitive,
|
|
577
|
+
trailingSlash: routeSpace.trailingSlash,
|
|
578
|
+
parameters: routeSpace.parameters,
|
|
579
|
+
};
|
|
580
|
+
const compiledClaim = declarative_control_1.CompiledRouteClaimSchema.parse({
|
|
280
581
|
...claim,
|
|
281
582
|
normalizedPath,
|
|
282
|
-
matcher
|
|
583
|
+
matcher,
|
|
584
|
+
});
|
|
585
|
+
const reserved = routeSpace.reservedPaths.some((candidate) => {
|
|
586
|
+
const normalizedReserved = normalizePath(candidate, routeSpace);
|
|
587
|
+
const reservedClaim = declarative_control_1.CompiledRouteClaimSchema.parse({
|
|
588
|
+
kind: "canonical",
|
|
589
|
+
routeSpaceRevisionRef: claim.routeSpaceRevisionRef,
|
|
590
|
+
pathTemplate: candidate,
|
|
283
591
|
surface: claim.surface,
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
592
|
+
normalizedPath: normalizedReserved,
|
|
593
|
+
matcher,
|
|
594
|
+
});
|
|
595
|
+
return ((0, exports.compiledRouteClaimsOverlap)(compiledClaim, reservedClaim) ||
|
|
596
|
+
normalizedPath.startsWith(`${normalizedReserved.replace(/\/+$/, "")}/`));
|
|
597
|
+
});
|
|
598
|
+
if (reserved) {
|
|
599
|
+
const takeover = takeoverAuthorizations.find((authorization) => compiledClaim.kind === "canonical" &&
|
|
600
|
+
authorization.normalizedPath === compiledClaim.normalizedPath &&
|
|
601
|
+
(0, exports.sameRevisionRef)(authorization.routeSpaceRevisionRef, compiledClaim.routeSpaceRevisionRef));
|
|
602
|
+
if (!takeover) {
|
|
603
|
+
throw new DeclarativeControlCompilationError("ROUTE_RESERVED", `routeClaims[${index}].pathTemplate`, `Route ${normalizedPath} is reserved by ${routeSpace.routeSpaceId}.`);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
const conflict = compiled.find((candidate) => (0, exports.sameRevisionRef)(candidate.routeSpaceRevisionRef, claim.routeSpaceRevisionRef) && (0, exports.compiledRouteClaimsOverlap)(candidate, compiledClaim));
|
|
607
|
+
if (conflict)
|
|
608
|
+
throw new DeclarativeControlCompilationError("ROUTE_CONFLICT", `routeClaims[${index}].pathTemplate`, `Route ${normalizedPath} overlaps another active claim pattern.`);
|
|
609
|
+
compiled.push(compiledClaim);
|
|
289
610
|
});
|
|
290
|
-
if (!compiled.some((claim) => claim.kind ===
|
|
291
|
-
throw new DeclarativeControlCompilationError(
|
|
611
|
+
if (!compiled.some((claim) => claim.kind === "canonical")) {
|
|
612
|
+
throw new DeclarativeControlCompilationError("ROUTE_CONFLICT", "routeClaims", "At least one canonical route claim is required.");
|
|
292
613
|
}
|
|
293
614
|
return Object.freeze(compiled);
|
|
294
615
|
};
|
|
295
616
|
exports.compileRouteClaims = compileRouteClaims;
|
|
296
|
-
|
|
297
|
-
|
|
617
|
+
/**
|
|
618
|
+
* Matches an application-relative request path using only the policy frozen into
|
|
619
|
+
* a CompiledRouteClaim. Runtime consumers must not reconstruct RouteSpace policy
|
|
620
|
+
* from product ids or deployment configuration.
|
|
621
|
+
*/
|
|
622
|
+
const matchCompiledRouteClaim = (claimInput, pathInput) => {
|
|
623
|
+
const claim = declarative_control_1.CompiledRouteClaimSchema.parse(claimInput);
|
|
624
|
+
const rawPath = String(pathInput || "").trim();
|
|
625
|
+
if (!rawPath.startsWith("/") ||
|
|
626
|
+
rawPath.includes("?") ||
|
|
627
|
+
rawPath.includes("#") ||
|
|
628
|
+
rawPath.includes("\\") ||
|
|
629
|
+
rawPath.includes("://")) {
|
|
630
|
+
return { matched: false, parameters: {} };
|
|
631
|
+
}
|
|
632
|
+
const rawSegments = rawPath.split("/");
|
|
633
|
+
if (rawSegments.some((segment) => segment === "." || segment === ".."))
|
|
634
|
+
return { matched: false, parameters: {} };
|
|
635
|
+
const requestHasTrailingSlash = rawPath.length > 1 && rawPath.endsWith("/");
|
|
636
|
+
if (claim.matcher.trailingSlash === "require" && !requestHasTrailingSlash)
|
|
637
|
+
return { matched: false, parameters: {} };
|
|
638
|
+
if (claim.matcher.trailingSlash === "preserve" &&
|
|
639
|
+
requestHasTrailingSlash !==
|
|
640
|
+
(claim.normalizedPath.length > 1 && claim.normalizedPath.endsWith("/"))) {
|
|
641
|
+
return { matched: false, parameters: {} };
|
|
642
|
+
}
|
|
643
|
+
const normalizeTrailing = (value) => {
|
|
644
|
+
if (claim.matcher.trailingSlash === "remove" && value.length > 1)
|
|
645
|
+
return value.replace(/\/+$/, "");
|
|
646
|
+
if (claim.matcher.trailingSlash === "require" &&
|
|
647
|
+
value.length > 1 &&
|
|
648
|
+
!value.endsWith("/"))
|
|
649
|
+
return `${value}/`;
|
|
650
|
+
return value;
|
|
651
|
+
};
|
|
652
|
+
const requestPath = normalizeTrailing(rawPath);
|
|
653
|
+
const templatePath = normalizeTrailing(claim.normalizedPath);
|
|
654
|
+
const requestSegments = requestPath.split("/").filter(Boolean);
|
|
655
|
+
const templateSegments = templatePath.split("/").filter(Boolean);
|
|
656
|
+
if (requestSegments.length !== templateSegments.length)
|
|
657
|
+
return { matched: false, parameters: {} };
|
|
658
|
+
const parameterDefinitions = new Map(claim.matcher.parameters.map((parameter) => [parameter.name, parameter]));
|
|
659
|
+
const parameters = {};
|
|
660
|
+
const normalizedSegments = [];
|
|
661
|
+
for (let index = 0; index < templateSegments.length; index += 1) {
|
|
662
|
+
const template = templateSegments[index];
|
|
663
|
+
const rawValue = requestSegments[index];
|
|
664
|
+
const match = template.match(/^[:$]([A-Za-z][A-Za-z0-9_-]*)$/);
|
|
665
|
+
if (!match) {
|
|
666
|
+
if ((claim.matcher.caseSensitive ? template : template.toLowerCase()) !==
|
|
667
|
+
(claim.matcher.caseSensitive ? rawValue : rawValue.toLowerCase())) {
|
|
668
|
+
return { matched: false, parameters: {} };
|
|
669
|
+
}
|
|
670
|
+
normalizedSegments.push(template);
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
const definition = parameterDefinitions.get(match[1]);
|
|
674
|
+
let value;
|
|
675
|
+
try {
|
|
676
|
+
value = decodeURIComponent(rawValue);
|
|
677
|
+
}
|
|
678
|
+
catch {
|
|
679
|
+
return { matched: false, parameters: {} };
|
|
680
|
+
}
|
|
681
|
+
if (!definition || !routeParameterPattern(definition.type).test(value))
|
|
682
|
+
return { matched: false, parameters: {} };
|
|
683
|
+
parameters[definition.name] = value;
|
|
684
|
+
normalizedSegments.push(rawValue);
|
|
685
|
+
}
|
|
686
|
+
const canonicalPath = normalizeTrailing(`/${normalizedSegments.join("/")}` || "/");
|
|
687
|
+
return {
|
|
688
|
+
matched: true,
|
|
689
|
+
normalizedPath: canonicalPath,
|
|
690
|
+
parameters: Object.freeze(parameters),
|
|
691
|
+
};
|
|
692
|
+
};
|
|
693
|
+
exports.matchCompiledRouteClaim = matchCompiledRouteClaim;
|
|
694
|
+
function routeParameterPattern(type) {
|
|
695
|
+
if (type === "integer")
|
|
298
696
|
return /^-?\d+$/;
|
|
299
|
-
if (type ===
|
|
697
|
+
if (type === "uuid")
|
|
300
698
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
301
|
-
if (type ===
|
|
699
|
+
if (type === "slug")
|
|
302
700
|
return /^[A-Za-z0-9]+(?:[._-][A-Za-z0-9]+)*$/;
|
|
303
701
|
return /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
304
|
-
}
|
|
702
|
+
}
|
|
305
703
|
/**
|
|
306
704
|
* Materializes the browser URL owned by a RouteSpace from an application-relative
|
|
307
705
|
* compiled route. Declarations and runtime resolution remain application-relative;
|
|
@@ -310,26 +708,31 @@ const routeParameterPattern = (type) => {
|
|
|
310
708
|
const materializeRouteSpacePath = (input) => {
|
|
311
709
|
const routeSpace = declarative_control_1.RouteSpaceSchema.parse(input.routeSpace);
|
|
312
710
|
const applicationPath = declarative_control_1.RoutePathTemplateSchema.parse(input.applicationPath);
|
|
313
|
-
const basePath = routeSpace.basePath ===
|
|
314
|
-
let browserTemplate = normalizePath(`${basePath}${applicationPath ===
|
|
711
|
+
const basePath = routeSpace.basePath === "/" ? "" : routeSpace.basePath.replace(/\/+$/, "");
|
|
712
|
+
let browserTemplate = normalizePath(`${basePath}${applicationPath === "/" ? "/" : applicationPath}` || "/", routeSpace);
|
|
315
713
|
const parameters = input.parameters || {};
|
|
316
714
|
const declared = new Map(routeSpace.parameters.map((parameter) => [parameter.name, parameter]));
|
|
317
|
-
const referenced = [
|
|
715
|
+
const referenced = [
|
|
716
|
+
...browserTemplate.matchAll(/:([A-Za-z][A-Za-z0-9_-]*)/g),
|
|
717
|
+
].map((match) => match[1]);
|
|
318
718
|
for (const name of referenced) {
|
|
319
719
|
const definition = declared.get(name);
|
|
320
720
|
const raw = parameters[name];
|
|
321
|
-
if (!definition ||
|
|
322
|
-
|
|
721
|
+
if (!definition ||
|
|
722
|
+
raw === undefined ||
|
|
723
|
+
raw === null ||
|
|
724
|
+
String(raw).trim() === "") {
|
|
725
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", "parameters", `Route parameter ${name} is not resolved by ${routeSpace.routeSpaceId}.`);
|
|
323
726
|
}
|
|
324
727
|
const value = String(raw);
|
|
325
728
|
if (!routeParameterPattern(definition.type).test(value)) {
|
|
326
|
-
throw new DeclarativeControlCompilationError(
|
|
729
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `parameters.${name}`, `Route parameter ${name} does not satisfy ${definition.type}.`);
|
|
327
730
|
}
|
|
328
731
|
browserTemplate = browserTemplate.replace(`:${name}`, encodeURIComponent(value));
|
|
329
732
|
}
|
|
330
733
|
const unknown = Object.keys(parameters).find((name) => !declared.has(name));
|
|
331
734
|
if (unknown)
|
|
332
|
-
throw new DeclarativeControlCompilationError(
|
|
735
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `parameters.${unknown}`, `RouteSpace ${routeSpace.routeSpaceId} does not declare ${unknown}.`);
|
|
333
736
|
return browserTemplate;
|
|
334
737
|
};
|
|
335
738
|
exports.materializeRouteSpacePath = materializeRouteSpacePath;
|
|
@@ -339,7 +742,12 @@ const renderTreeDepth = (tree) => {
|
|
|
339
742
|
tree.nodes.forEach((node) => {
|
|
340
743
|
let depth = 1;
|
|
341
744
|
let parentId = node.parentNodeId;
|
|
745
|
+
const ancestors = new Set([node.nodeId]);
|
|
342
746
|
while (parentId) {
|
|
747
|
+
if (ancestors.has(parentId)) {
|
|
748
|
+
throw new DeclarativeControlCompilationError("RENDER_TREE_INVALID", "page.renderTree.nodes", `RenderTree contains a parent cycle at ${parentId}.`);
|
|
749
|
+
}
|
|
750
|
+
ancestors.add(parentId);
|
|
343
751
|
depth += 1;
|
|
344
752
|
parentId = byId.get(parentId)?.parentNodeId;
|
|
345
753
|
}
|
|
@@ -347,56 +755,590 @@ const renderTreeDepth = (tree) => {
|
|
|
347
755
|
});
|
|
348
756
|
return maximum;
|
|
349
757
|
};
|
|
350
|
-
const compareLegacyEntityRef = (reference, revision) => reference.kind === revision.kind
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
758
|
+
const compareLegacyEntityRef = (reference, revision) => reference.kind === revision.kind &&
|
|
759
|
+
reference.id === revision.id &&
|
|
760
|
+
String(reference.version) === String(revision.revision) &&
|
|
761
|
+
reference.ownerRepo === revision.ownerRepo;
|
|
354
762
|
const pageDependencies = (page, compilerRevisionRef) => [
|
|
355
|
-
...page.routeClaims.map((claim) => ({
|
|
356
|
-
|
|
763
|
+
...page.routeClaims.map((claim) => ({
|
|
764
|
+
role: "route-space",
|
|
765
|
+
revisionRef: claim.routeSpaceRevisionRef,
|
|
766
|
+
})),
|
|
767
|
+
{ role: "shell", revisionRef: page.shellRevisionRef },
|
|
357
768
|
...page.capabilityInstances.flatMap((instance) => [
|
|
358
|
-
{
|
|
359
|
-
|
|
360
|
-
|
|
769
|
+
{
|
|
770
|
+
role: "capability",
|
|
771
|
+
revisionRef: instance.capabilityRevisionRef,
|
|
772
|
+
},
|
|
773
|
+
{ role: "provider", revisionRef: instance.providerRevisionRef },
|
|
774
|
+
...(instance.eventEffects ?? []).map((effect) => ({
|
|
775
|
+
role: "schema",
|
|
776
|
+
revisionRef: effect.sourceIntentSchemaRevisionRef,
|
|
777
|
+
})),
|
|
778
|
+
{
|
|
779
|
+
role: "schema",
|
|
780
|
+
revisionRef: instance.propertySchemaRevisionRef,
|
|
781
|
+
},
|
|
361
782
|
]),
|
|
783
|
+
...(page.stateDefinitions ?? []).map((state) => ({
|
|
784
|
+
role: "schema",
|
|
785
|
+
revisionRef: state.schemaRevisionRef,
|
|
786
|
+
})),
|
|
787
|
+
...(page.interactionBindings ?? []).map((binding) => ({
|
|
788
|
+
role: "schema",
|
|
789
|
+
revisionRef: binding.sourceIntentSchemaRevisionRef,
|
|
790
|
+
})),
|
|
362
791
|
...page.ontologyBindings.flatMap((binding) => [
|
|
363
|
-
{
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
792
|
+
{
|
|
793
|
+
role: "ontology-definition",
|
|
794
|
+
revisionRef: binding.ontologyDefinitionRevisionRef,
|
|
795
|
+
},
|
|
796
|
+
{ role: "view", revisionRef: binding.viewRevisionRef },
|
|
797
|
+
{
|
|
798
|
+
role: "view",
|
|
799
|
+
revisionRef: binding.canonicalDataViewRevisionRef,
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
role: "schema",
|
|
803
|
+
revisionRef: binding.renderModelSchemaRevisionRef,
|
|
804
|
+
},
|
|
805
|
+
...(binding.cursorWindow
|
|
806
|
+
? [
|
|
807
|
+
{
|
|
808
|
+
role: "schema",
|
|
809
|
+
revisionRef: binding.cursorWindow.pageChangeIntentSchemaRevisionRef,
|
|
810
|
+
},
|
|
811
|
+
]
|
|
812
|
+
: []),
|
|
813
|
+
]),
|
|
814
|
+
...(page.queryBindings ?? []).flatMap((binding) => [
|
|
815
|
+
{
|
|
816
|
+
role: "query-definition",
|
|
817
|
+
revisionRef: binding.queryDefinitionRevisionRef,
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
role: "schema",
|
|
821
|
+
revisionRef: binding.renderModelSchemaRevisionRef,
|
|
822
|
+
},
|
|
823
|
+
...(binding.targetProjection
|
|
824
|
+
? [
|
|
825
|
+
{
|
|
826
|
+
role: "schema",
|
|
827
|
+
revisionRef: binding.targetProjection.schemaRevisionRef,
|
|
828
|
+
},
|
|
829
|
+
]
|
|
830
|
+
: []),
|
|
831
|
+
...(binding.streamTargetProjection
|
|
832
|
+
? [
|
|
833
|
+
{
|
|
834
|
+
role: "schema",
|
|
835
|
+
revisionRef: binding.streamTargetProjection.schemaRevisionRef,
|
|
836
|
+
},
|
|
837
|
+
]
|
|
838
|
+
: []),
|
|
839
|
+
...(binding.cursorWindow
|
|
840
|
+
? [
|
|
841
|
+
{
|
|
842
|
+
role: "schema",
|
|
843
|
+
revisionRef: binding.cursorWindow.pageChangeIntentSchemaRevisionRef,
|
|
844
|
+
},
|
|
845
|
+
]
|
|
846
|
+
: []),
|
|
367
847
|
]),
|
|
368
848
|
...page.actionBindings.flatMap((binding) => [
|
|
369
|
-
{ role:
|
|
370
|
-
{
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
849
|
+
{ role: "action", revisionRef: binding.commandRevisionRef },
|
|
850
|
+
{
|
|
851
|
+
role: "schema",
|
|
852
|
+
revisionRef: binding.sourceIntentSchemaRevisionRef,
|
|
853
|
+
},
|
|
854
|
+
{ role: "schema", revisionRef: binding.inputSchemaRevisionRef },
|
|
855
|
+
{ role: "schema", revisionRef: binding.resultSchemaRevisionRef },
|
|
856
|
+
...(binding.compensationCommandRevisionRef
|
|
857
|
+
? [
|
|
858
|
+
{
|
|
859
|
+
role: "action",
|
|
860
|
+
revisionRef: binding.compensationCommandRevisionRef,
|
|
861
|
+
},
|
|
862
|
+
]
|
|
863
|
+
: []),
|
|
374
864
|
]),
|
|
375
|
-
...page.tokenRevisionRefs.map((revisionRef) => ({
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
865
|
+
...page.tokenRevisionRefs.map((revisionRef) => ({
|
|
866
|
+
role: "token",
|
|
867
|
+
revisionRef,
|
|
868
|
+
})),
|
|
869
|
+
{ role: "performance-budget", revisionRef: page.performanceBudgetRef },
|
|
870
|
+
{
|
|
871
|
+
role: "observation-policy",
|
|
872
|
+
revisionRef: page.observationPolicyRevisionRef,
|
|
873
|
+
},
|
|
874
|
+
{ role: "compiler", revisionRef: compilerRevisionRef },
|
|
379
875
|
];
|
|
380
|
-
const
|
|
876
|
+
const domainQueryDependencies = (definition) => [
|
|
877
|
+
...(definition.ontologyDefinitionRevisionRef
|
|
878
|
+
? [
|
|
879
|
+
{
|
|
880
|
+
role: "ontology-definition",
|
|
881
|
+
revisionRef: definition.ontologyDefinitionRevisionRef,
|
|
882
|
+
},
|
|
883
|
+
]
|
|
884
|
+
: []),
|
|
885
|
+
...(definition.viewRevisionRef
|
|
886
|
+
? [{ role: "view", revisionRef: definition.viewRevisionRef }]
|
|
887
|
+
: []),
|
|
888
|
+
...(definition.canonicalDataViewRevisionRef
|
|
889
|
+
? [
|
|
890
|
+
{
|
|
891
|
+
role: "view",
|
|
892
|
+
revisionRef: definition.canonicalDataViewRevisionRef,
|
|
893
|
+
},
|
|
894
|
+
]
|
|
895
|
+
: []),
|
|
896
|
+
{ role: "schema", revisionRef: definition.inputSchemaRevisionRef },
|
|
897
|
+
{ role: "schema", revisionRef: definition.resultSchemaRevisionRef },
|
|
898
|
+
];
|
|
899
|
+
const singleByStableIdentity = (registrations, kind, id, path) => {
|
|
900
|
+
const matches = registrations.filter(({ revisionRef }) => revisionRef.kind === kind && revisionRef.id === id);
|
|
901
|
+
if (matches.length !== 1) {
|
|
902
|
+
throw new DeclarativeControlCompilationError("DEPENDENCY_MISSING", path, `Expected exactly one active ${kind} registration for ${id}; found ${matches.length}.`);
|
|
903
|
+
}
|
|
904
|
+
return matches[0];
|
|
905
|
+
};
|
|
906
|
+
const usesResultValue = (mapping) => Object.values(mapping).some((source) => source.kind === "result-field" ||
|
|
907
|
+
(source.kind === "expression" &&
|
|
908
|
+
source.expression !== undefined &&
|
|
909
|
+
(0, page_expression_1.collectPageExpressionReads)(source.expression).some((read) => read.root === "result")));
|
|
910
|
+
const simpleEntityRef = (revisionRef) => ({
|
|
911
|
+
kind: revisionRef.kind,
|
|
912
|
+
id: revisionRef.id,
|
|
913
|
+
version: revisionRef.revision,
|
|
914
|
+
ownerRepo: revisionRef.ownerRepo,
|
|
915
|
+
});
|
|
916
|
+
const defaultRenderSurface = {
|
|
917
|
+
frameOwner: "host",
|
|
918
|
+
density: "default",
|
|
919
|
+
};
|
|
920
|
+
const defaultRenderScroll = {
|
|
921
|
+
owner: "surface",
|
|
922
|
+
axis: "none",
|
|
923
|
+
virtualizationBoundary: false,
|
|
924
|
+
};
|
|
925
|
+
const defaultRenderLifecycle = {
|
|
926
|
+
mountPolicy: "always",
|
|
927
|
+
queryPolicy: "always",
|
|
928
|
+
retainOnDeactivate: false,
|
|
929
|
+
deepLink: false,
|
|
930
|
+
focusReturn: false,
|
|
931
|
+
};
|
|
932
|
+
const resolvePage = (input) => {
|
|
381
933
|
const page = declarative_control_1.PageSchema.parse(input.page);
|
|
934
|
+
const defaults = {
|
|
935
|
+
routeSpace: declarative_control_1.RevisionRefSchema.parse(input.defaults.routeSpace),
|
|
936
|
+
shell: declarative_control_1.RevisionRefSchema.parse(input.defaults.shell),
|
|
937
|
+
tokens: input.defaults.tokens.map((reference) => declarative_control_1.RevisionRefSchema.parse(reference)),
|
|
938
|
+
performanceBudget: declarative_control_1.RevisionRefSchema.parse(input.defaults.performanceBudget),
|
|
939
|
+
observationPolicy: declarative_control_1.RevisionRefSchema.parse(input.defaults.observationPolicy),
|
|
940
|
+
};
|
|
941
|
+
const resourceRegistrations = (input.productResources ?? []).map((registration) => ({
|
|
942
|
+
revisionRef: declarative_control_1.RevisionRefSchema.parse(registration.resourceRevisionRef),
|
|
943
|
+
registration: declarative_control_http_1.DeclarativeProductResourceRegistrationSchema.parse(registration),
|
|
944
|
+
}));
|
|
945
|
+
const resolveResource = (kind, id, fallback, path) => {
|
|
946
|
+
const targetId = id ?? fallback.id;
|
|
947
|
+
if (resourceRegistrations.length === 0) {
|
|
948
|
+
if (targetId !== fallback.id || fallback.kind !== kind) {
|
|
949
|
+
throw new DeclarativeControlCompilationError("DEPENDENCY_MISSING", path, `No ${kind} registry was supplied for ${targetId}.`);
|
|
950
|
+
}
|
|
951
|
+
return fallback;
|
|
952
|
+
}
|
|
953
|
+
return singleByStableIdentity(resourceRegistrations, kind, targetId, path)
|
|
954
|
+
.revisionRef;
|
|
955
|
+
};
|
|
956
|
+
const routeSpaceId = page.routeSpace ?? defaults.routeSpace.id;
|
|
957
|
+
const routeRegistrations = (input.routeSpaces ?? []).map((registration) => ({
|
|
958
|
+
revisionRef: declarative_control_1.RevisionRefSchema.parse(registration.revisionRef),
|
|
959
|
+
routeSpace: registration.routeSpace,
|
|
960
|
+
}));
|
|
961
|
+
const routeSpaceRevisionRef = routeRegistrations.length === 0
|
|
962
|
+
? (() => {
|
|
963
|
+
if (routeSpaceId !== defaults.routeSpace.id) {
|
|
964
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISSING", "page.routeSpace", `No RouteSpace registry was supplied for ${routeSpaceId}.`);
|
|
965
|
+
}
|
|
966
|
+
return defaults.routeSpace;
|
|
967
|
+
})()
|
|
968
|
+
: singleByStableIdentity(routeRegistrations, "route-space", routeSpaceId, "page.routeSpace").revisionRef;
|
|
969
|
+
const shellRevisionRef = resolveResource("shell", page.shell, defaults.shell, "page.shell");
|
|
970
|
+
const tokenRevisionRefs = page.tokens?.map((id, index) => resolveResource("design-token", id, defaults.tokens[0], `page.tokens[${index}]`)) ?? [...defaults.tokens];
|
|
971
|
+
const performanceBudgetRef = resolveResource("performance-budget", page.performanceBudget, defaults.performanceBudget, "page.performanceBudget");
|
|
972
|
+
const observationPolicyRevisionRef = resolveResource("observation-policy", page.observationPolicy, defaults.observationPolicy, "page.observationPolicy");
|
|
973
|
+
const capabilityRegistrations = input.capabilityRegistry.map((registration) => ({
|
|
974
|
+
...registration,
|
|
975
|
+
revisionRef: declarative_control_1.RevisionRefSchema.parse(registration.capabilityRevisionRef),
|
|
976
|
+
providerRevisionRef: declarative_control_1.RevisionRefSchema.parse(registration.providerRevisionRef),
|
|
977
|
+
propertySchemaRevisionRef: declarative_control_1.RevisionRefSchema.parse(registration.propertySchemaRevisionRef),
|
|
978
|
+
inputPorts: registration.inputPorts.map((port) => ({
|
|
979
|
+
...port,
|
|
980
|
+
schemaRevisionRef: declarative_control_1.RevisionRefSchema.parse(port.schemaRevisionRef),
|
|
981
|
+
})),
|
|
982
|
+
outputPorts: registration.outputPorts.map((port) => ({
|
|
983
|
+
...port,
|
|
984
|
+
schemaRevisionRef: declarative_control_1.RevisionRefSchema.parse(port.schemaRevisionRef),
|
|
985
|
+
})),
|
|
986
|
+
}));
|
|
987
|
+
const queryRegistrations = (input.domainQueryRegistry ?? []).map((registration) => ({
|
|
988
|
+
revisionRef: declarative_control_1.RevisionRefSchema.parse(registration.definitionRevisionRef),
|
|
989
|
+
definition: declarative_control_1.DomainQueryDefinitionSchema.parse(registration.definition),
|
|
990
|
+
}));
|
|
991
|
+
const commandRegistrations = (input.domainCommandRegistry ?? []).map((registration) => ({
|
|
992
|
+
revisionRef: declarative_control_1.RevisionRefSchema.parse(registration.definitionRevisionRef),
|
|
993
|
+
definition: registration.definition,
|
|
994
|
+
}));
|
|
995
|
+
const schemaRegistrations = (input.schemaRegistry ?? []).map((registration) => ({
|
|
996
|
+
revisionRef: declarative_control_1.RevisionRefSchema.parse(registration.definitionRevisionRef),
|
|
997
|
+
definition: registration.definition,
|
|
998
|
+
}));
|
|
999
|
+
const stateSchemaById = new Map(schemaRegistrations.map((registration) => [
|
|
1000
|
+
registration.definition.schemaId,
|
|
1001
|
+
registration.revisionRef,
|
|
1002
|
+
]));
|
|
1003
|
+
const stateDefinitions = (page.state ?? []).map(({ schema, ...state }, index) => {
|
|
1004
|
+
const schemaRevisionRef = stateSchemaById.get(schema);
|
|
1005
|
+
if (!schemaRevisionRef) {
|
|
1006
|
+
throw new DeclarativeControlCompilationError("SCHEMA_DEFINITION_MISMATCH", `page.state[${index}].schema`, `No active Schema registration exists for ${schema}.`);
|
|
1007
|
+
}
|
|
1008
|
+
return { ...state, schemaRevisionRef };
|
|
1009
|
+
});
|
|
1010
|
+
const stateSchemasByStateId = new Map(stateDefinitions.map((definition) => [
|
|
1011
|
+
definition.stateId,
|
|
1012
|
+
definition.schemaRevisionRef,
|
|
1013
|
+
]));
|
|
1014
|
+
const renderNodes = [];
|
|
1015
|
+
const capabilityInstances = [];
|
|
1016
|
+
const queryBindings = [];
|
|
1017
|
+
const actionBindings = [];
|
|
1018
|
+
const interactionBindings = [];
|
|
1019
|
+
const visitNode = (node, parentNodeId) => {
|
|
1020
|
+
const registration = singleByStableIdentity(capabilityRegistrations, "capability", node.component, `page.body.${node.id}.component`);
|
|
1021
|
+
const children = node.children ?? [];
|
|
1022
|
+
const defaultActivation = {
|
|
1023
|
+
activationId: node.id,
|
|
1024
|
+
mode: "inline",
|
|
1025
|
+
};
|
|
1026
|
+
renderNodes.push({
|
|
1027
|
+
contract: "RenderNode",
|
|
1028
|
+
nodeId: node.id,
|
|
1029
|
+
kind: node.kind ?? "view",
|
|
1030
|
+
version: 1,
|
|
1031
|
+
ownerRepo: registration.providerRevisionRef.ownerRepo,
|
|
1032
|
+
...(parentNodeId ? { parentNodeId } : {}),
|
|
1033
|
+
children: children.map((child) => child.id),
|
|
1034
|
+
...(node.slot ? { slot: node.slot } : {}),
|
|
1035
|
+
pageRef: {
|
|
1036
|
+
kind: "page",
|
|
1037
|
+
id: page.pageId,
|
|
1038
|
+
version: 1,
|
|
1039
|
+
ownerRepo: input.pageOwnerRepo,
|
|
1040
|
+
},
|
|
1041
|
+
capabilityRef: simpleEntityRef(registration.revisionRef),
|
|
1042
|
+
providerRef: simpleEntityRef(registration.providerRevisionRef),
|
|
1043
|
+
surface: node.surface ?? defaultRenderSurface,
|
|
1044
|
+
scroll: node.scroll ?? defaultRenderScroll,
|
|
1045
|
+
activation: node.activation ?? defaultActivation,
|
|
1046
|
+
lifecycle: node.lifecycle ?? defaultRenderLifecycle,
|
|
1047
|
+
layout: node.layout ?? { mode: "contents" },
|
|
1048
|
+
...(node.style ? { style: node.style } : {}),
|
|
1049
|
+
responsive: node.responsive ?? [],
|
|
1050
|
+
state: node.state ?? "idle",
|
|
1051
|
+
renderModel: node.model ?? {},
|
|
1052
|
+
...(node.repeat
|
|
1053
|
+
? { repeat: { ...node.repeat, limit: node.repeat.limit ?? 200 } }
|
|
1054
|
+
: {}),
|
|
1055
|
+
});
|
|
1056
|
+
capabilityInstances.push({
|
|
1057
|
+
instanceId: node.id,
|
|
1058
|
+
nodeId: node.id,
|
|
1059
|
+
capabilityRevisionRef: registration.revisionRef,
|
|
1060
|
+
providerRevisionRef: registration.providerRevisionRef,
|
|
1061
|
+
propertySchemaRevisionRef: registration.propertySchemaRevisionRef,
|
|
1062
|
+
properties: node.props ?? {},
|
|
1063
|
+
...(node.bindings ? { propertyBindings: node.bindings } : {}),
|
|
1064
|
+
...(node.eventPayloads
|
|
1065
|
+
? { eventPayloadBindings: node.eventPayloads }
|
|
1066
|
+
: {}),
|
|
1067
|
+
...(node.effects
|
|
1068
|
+
? {
|
|
1069
|
+
eventEffects: node.effects.map((effect, index) => {
|
|
1070
|
+
const port = registration.outputPorts.find((candidate) => candidate.name === effect.port);
|
|
1071
|
+
if (!port) {
|
|
1072
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `page.body.${node.id}.effects[${index}].port`, `No output port ${effect.port} exists on ${node.component}.`);
|
|
1073
|
+
}
|
|
1074
|
+
return {
|
|
1075
|
+
sourcePort: effect.port,
|
|
1076
|
+
sourceIntentSchemaRevisionRef: port.schemaRevisionRef,
|
|
1077
|
+
refreshBindingIds: effect.refresh,
|
|
1078
|
+
};
|
|
1079
|
+
}),
|
|
1080
|
+
}
|
|
1081
|
+
: {}),
|
|
1082
|
+
...(node.access ? { accessPolicy: node.access } : {}),
|
|
1083
|
+
...(node.activeWhen ? { activationWhen: node.activeWhen } : {}),
|
|
1084
|
+
allowedSideEffects: [...registration.allowedSideEffects],
|
|
1085
|
+
});
|
|
1086
|
+
for (const [index, binding] of (node.data ?? []).entries()) {
|
|
1087
|
+
const queryRegistration = singleByStableIdentity(queryRegistrations, "domain-query-definition", binding.query, `page.body.${node.id}.data[${index}].query`);
|
|
1088
|
+
const port = registration.inputPorts.find((candidate) => candidate.name === binding.port);
|
|
1089
|
+
if (!port) {
|
|
1090
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `page.body.${node.id}.data[${index}].port`, `No input port ${binding.port} exists on ${node.component}.`);
|
|
1091
|
+
}
|
|
1092
|
+
const { query, port: _port, targetProjection, streamTargetProjection, cursorWindow, resultStateBindings, ...rest } = binding;
|
|
1093
|
+
queryBindings.push({
|
|
1094
|
+
...rest,
|
|
1095
|
+
queryDefinitionRevisionRef: queryRegistration.revisionRef,
|
|
1096
|
+
target: { capabilityInstanceId: node.id, port: binding.port },
|
|
1097
|
+
renderModelSchemaRevisionRef: queryRegistration.definition.resultSchemaRevisionRef,
|
|
1098
|
+
...(targetProjection
|
|
1099
|
+
? {
|
|
1100
|
+
targetProjection: {
|
|
1101
|
+
...targetProjection,
|
|
1102
|
+
schemaRevisionRef: port.schemaRevisionRef,
|
|
1103
|
+
},
|
|
1104
|
+
}
|
|
1105
|
+
: {}),
|
|
1106
|
+
...(streamTargetProjection
|
|
1107
|
+
? {
|
|
1108
|
+
streamTargetProjection: {
|
|
1109
|
+
...streamTargetProjection,
|
|
1110
|
+
schemaRevisionRef: port.schemaRevisionRef,
|
|
1111
|
+
},
|
|
1112
|
+
}
|
|
1113
|
+
: {}),
|
|
1114
|
+
...(cursorWindow
|
|
1115
|
+
? {
|
|
1116
|
+
cursorWindow: {
|
|
1117
|
+
...cursorWindow,
|
|
1118
|
+
pageChangeIntentSchemaRevisionRef: (() => {
|
|
1119
|
+
const sourceInstanceId = cursorWindow.sourceCapabilityInstanceId ?? node.id;
|
|
1120
|
+
const sourceRegistration = capabilityInstances.find((instance) => instance.instanceId === sourceInstanceId);
|
|
1121
|
+
const sourceCapability = sourceRegistration &&
|
|
1122
|
+
capabilityRegistrations.find((candidate) => (0, exports.sameRevisionRef)(candidate.revisionRef, sourceRegistration.capabilityRevisionRef));
|
|
1123
|
+
const sourcePort = sourceCapability?.outputPorts.find((candidate) => candidate.name === cursorWindow.pageChangePort);
|
|
1124
|
+
if (!sourcePort) {
|
|
1125
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `page.body.${node.id}.data[${index}].cursorWindow.pageChangePort`, `No output port ${cursorWindow.pageChangePort} exists on the cursor source component.`);
|
|
1126
|
+
}
|
|
1127
|
+
return sourcePort.schemaRevisionRef;
|
|
1128
|
+
})(),
|
|
1129
|
+
},
|
|
1130
|
+
}
|
|
1131
|
+
: {}),
|
|
1132
|
+
...(resultStateBindings
|
|
1133
|
+
? {
|
|
1134
|
+
resultStateBindings: resultStateBindings.map((stateBinding, stateIndex) => {
|
|
1135
|
+
const targetStateSchemaRevisionRef = stateSchemasByStateId.get(stateBinding.targetStateId);
|
|
1136
|
+
if (!targetStateSchemaRevisionRef) {
|
|
1137
|
+
throw new DeclarativeControlCompilationError("SCHEMA_DEFINITION_MISMATCH", `page.body.${node.id}.data[${index}].resultStateBindings[${stateIndex}].targetStateId`, `Unknown Page state ${stateBinding.targetStateId}.`);
|
|
1138
|
+
}
|
|
1139
|
+
return { ...stateBinding, targetStateSchemaRevisionRef };
|
|
1140
|
+
}),
|
|
1141
|
+
}
|
|
1142
|
+
: {}),
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
for (const [index, binding] of (node.actions ?? []).entries()) {
|
|
1146
|
+
const commandRegistration = singleByStableIdentity(commandRegistrations, "domain-command", binding.command, `page.body.${node.id}.actions[${index}].command`);
|
|
1147
|
+
const port = registration.outputPorts.find((candidate) => candidate.name === binding.port);
|
|
1148
|
+
if (!port) {
|
|
1149
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `page.body.${node.id}.actions[${index}].port`, `No output port ${binding.port} exists on ${node.component}.`);
|
|
1150
|
+
}
|
|
1151
|
+
const inputSchemaRevisionRef = stateSchemaById.get(commandRegistration.definition.inputSchemaRef);
|
|
1152
|
+
const resultSchemaRevisionRef = commandRegistration.definition
|
|
1153
|
+
.outputSchemaRef
|
|
1154
|
+
? stateSchemaById.get(commandRegistration.definition.outputSchemaRef)
|
|
1155
|
+
: undefined;
|
|
1156
|
+
if (!inputSchemaRevisionRef || !resultSchemaRevisionRef) {
|
|
1157
|
+
throw new DeclarativeControlCompilationError("COMMAND_DEFINITION_MISMATCH", `page.body.${node.id}.actions[${index}].command`, `Command ${binding.command} must resolve active input and output Schemas.`);
|
|
1158
|
+
}
|
|
1159
|
+
const compensationCommandRevisionRef = binding.compensationCommand
|
|
1160
|
+
? singleByStableIdentity(commandRegistrations, "domain-command", binding.compensationCommand, `page.body.${node.id}.actions[${index}].compensationCommand`).revisionRef
|
|
1161
|
+
: undefined;
|
|
1162
|
+
const { command, port: _port, compensationCommand, ...rest } = binding;
|
|
1163
|
+
actionBindings.push({
|
|
1164
|
+
...rest,
|
|
1165
|
+
commandRevisionRef: commandRegistration.revisionRef,
|
|
1166
|
+
source: { capabilityInstanceId: node.id, port: binding.port },
|
|
1167
|
+
sourceIntentSchemaRevisionRef: port.schemaRevisionRef,
|
|
1168
|
+
inputSchemaRevisionRef,
|
|
1169
|
+
...(compensationCommandRevisionRef
|
|
1170
|
+
? { compensationCommandRevisionRef }
|
|
1171
|
+
: {}),
|
|
1172
|
+
resultSchemaRevisionRef,
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
for (const [index, binding] of (node.on ?? []).entries()) {
|
|
1176
|
+
const port = registration.outputPorts.find((candidate) => candidate.name === binding.port);
|
|
1177
|
+
if (!port) {
|
|
1178
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `page.body.${node.id}.on[${index}].port`, `No output port ${binding.port} exists on ${node.component}.`);
|
|
1179
|
+
}
|
|
1180
|
+
const matchingAction = actionBindings.find((action) => action.source.capabilityInstanceId === node.id &&
|
|
1181
|
+
action.source.port === binding.port);
|
|
1182
|
+
const { port: _port, ...rest } = binding;
|
|
1183
|
+
interactionBindings.push({
|
|
1184
|
+
...rest,
|
|
1185
|
+
source: { capabilityInstanceId: node.id, port: binding.port },
|
|
1186
|
+
sourceIntentSchemaRevisionRef: port.schemaRevisionRef,
|
|
1187
|
+
...(usesResultValue(binding.inputMapping)
|
|
1188
|
+
? (() => {
|
|
1189
|
+
if (!matchingAction) {
|
|
1190
|
+
throw new DeclarativeControlCompilationError("COMMAND_DEFINITION_MISMATCH", `page.body.${node.id}.on[${index}].inputMapping`, "A result-driven interaction requires an Action on the same output port.");
|
|
1191
|
+
}
|
|
1192
|
+
return {
|
|
1193
|
+
sourceResultSchemaRevisionRef: matchingAction.resultSchemaRevisionRef,
|
|
1194
|
+
};
|
|
1195
|
+
})()
|
|
1196
|
+
: {}),
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
children.forEach((child) => visitNode(child, node.id));
|
|
1200
|
+
};
|
|
1201
|
+
visitNode(page.body);
|
|
1202
|
+
const resolved = {
|
|
1203
|
+
contract: "Page",
|
|
1204
|
+
schemaVersion: 1,
|
|
1205
|
+
pageId: page.pageId,
|
|
1206
|
+
tenantScope: page.tenantScope,
|
|
1207
|
+
identity: page.identity,
|
|
1208
|
+
supportedSurfaces: [page.surface],
|
|
1209
|
+
lifecycle: page.lifecycle,
|
|
1210
|
+
...(page.routeStatePresentations
|
|
1211
|
+
? { routeStatePresentations: page.routeStatePresentations }
|
|
1212
|
+
: {}),
|
|
1213
|
+
routeClaims: [
|
|
1214
|
+
{
|
|
1215
|
+
kind: "canonical",
|
|
1216
|
+
surface: page.surface,
|
|
1217
|
+
routeSpaceRevisionRef,
|
|
1218
|
+
pathTemplate: page.route,
|
|
1219
|
+
},
|
|
1220
|
+
],
|
|
1221
|
+
shellRevisionRef,
|
|
1222
|
+
renderTree: {
|
|
1223
|
+
contract: "RenderTree",
|
|
1224
|
+
treeId: page.pageId,
|
|
1225
|
+
product: page.surface,
|
|
1226
|
+
rootNodeId: page.body.id,
|
|
1227
|
+
nodes: renderNodes,
|
|
1228
|
+
},
|
|
1229
|
+
capabilityInstances,
|
|
1230
|
+
...(stateDefinitions.length ? { stateDefinitions } : {}),
|
|
1231
|
+
...(page.entryTransitions
|
|
1232
|
+
? {
|
|
1233
|
+
entryTransitions: page.entryTransitions.map(({ target, ...transition }, index) => {
|
|
1234
|
+
const matches = (input.targetRegistry ?? []).filter((registration) => registration.stableTargetRef.kind === target.kind &&
|
|
1235
|
+
registration.stableTargetRef.id === target.id);
|
|
1236
|
+
if (matches.length !== 1) {
|
|
1237
|
+
throw new DeclarativeControlCompilationError("PAGE_ENTRY_TARGET_UNRELEASED", `page.entryTransitions[${index}].target`, `Expected one active ${target.kind} target for ${target.id}; found ${matches.length}.`);
|
|
1238
|
+
}
|
|
1239
|
+
return { ...transition, targetRef: matches[0].stableTargetRef };
|
|
1240
|
+
}),
|
|
1241
|
+
}
|
|
1242
|
+
: {}),
|
|
1243
|
+
...(interactionBindings.length ? { interactionBindings } : {}),
|
|
1244
|
+
ontologyBindings: [],
|
|
1245
|
+
...(queryBindings.length ? { queryBindings } : {}),
|
|
1246
|
+
actionBindings,
|
|
1247
|
+
pageAccessPolicy: page.access,
|
|
1248
|
+
managementAccess: page.managementAccess,
|
|
1249
|
+
tokenRevisionRefs,
|
|
1250
|
+
performanceBudgetRef,
|
|
1251
|
+
observationPolicyRevisionRef,
|
|
1252
|
+
privacyClassification: page.privacyClassification,
|
|
1253
|
+
};
|
|
1254
|
+
return declarative_control_1.ResolvedPageSchema.parse(resolved);
|
|
1255
|
+
};
|
|
1256
|
+
exports.resolvePage = resolvePage;
|
|
1257
|
+
const compilePageRuntimeBundle = (input) => {
|
|
1258
|
+
const parsedPage = declarative_control_1.ResolvedPageSchema.parse(input.page);
|
|
1259
|
+
const page = {
|
|
1260
|
+
...parsedPage,
|
|
1261
|
+
routeStatePresentations: parsedPage.routeStatePresentations ?? [],
|
|
1262
|
+
stateDefinitions: parsedPage.stateDefinitions ?? [],
|
|
1263
|
+
entryTransitions: parsedPage.entryTransitions ?? [],
|
|
1264
|
+
interactionBindings: parsedPage.interactionBindings ?? [],
|
|
1265
|
+
queryBindings: parsedPage.queryBindings ?? [],
|
|
1266
|
+
};
|
|
382
1267
|
const pageRevisionRef = declarative_control_1.RevisionRefSchema.parse(input.pageRevisionRef);
|
|
383
1268
|
const release = declarative_control_1.PageReleaseSchema.parse(input.release);
|
|
384
1269
|
const releaseRevisionRef = declarative_control_1.RevisionRefSchema.parse(input.releaseRevisionRef);
|
|
385
1270
|
const compilerRevisionRef = declarative_control_1.RevisionRefSchema.parse(input.compilerRevisionRef);
|
|
386
1271
|
const scope = declarative_control_1.TenantScopeSchema.parse(page.tenantScope);
|
|
387
|
-
assertSameTenantScope(release.tenantScope, scope,
|
|
388
|
-
assertRevisionIdentity(pageRevisionRef,
|
|
389
|
-
assertRevisionIdentity(release.pageRevisionRef,
|
|
1272
|
+
assertSameTenantScope(release.tenantScope, scope, "release.tenantScope");
|
|
1273
|
+
assertRevisionIdentity(pageRevisionRef, "page", page.pageId, scope, "pageRevisionRef");
|
|
1274
|
+
assertRevisionIdentity(release.pageRevisionRef, "page", page.pageId, scope, "release.pageRevisionRef");
|
|
390
1275
|
if (!(0, exports.sameRevisionRef)(pageRevisionRef, release.pageRevisionRef))
|
|
391
|
-
throw new DeclarativeControlCompilationError(
|
|
392
|
-
assertRevisionIdentity(releaseRevisionRef,
|
|
1276
|
+
throw new DeclarativeControlCompilationError("REVISION_MISMATCH", "release.pageRevisionRef", "Release does not pin the supplied Page revision.");
|
|
1277
|
+
assertRevisionIdentity(releaseRevisionRef, "page-release", release.releaseSlotId, scope, "releaseRevisionRef");
|
|
393
1278
|
requireReleaseReady(release);
|
|
394
1279
|
requireDependencies(release.dependencySnapshot, pageDependencies(page, compilerRevisionRef));
|
|
395
1280
|
assertDependencyScopes(release.dependencySnapshot, scope);
|
|
1281
|
+
const queryRegistrations = (input.domainQueryRegistry ?? []).map((registration, index) => {
|
|
1282
|
+
const definitionRevisionRef = declarative_control_1.RevisionRefSchema.parse(registration.definitionRevisionRef);
|
|
1283
|
+
const definition = declarative_control_1.DomainQueryDefinitionSchema.parse(registration.definition);
|
|
1284
|
+
assertSameTenantScope(definition.tenantScope, scope, `domainQueryRegistry[${index}].definition.tenantScope`);
|
|
1285
|
+
assertRevisionIdentity(definitionRevisionRef, "domain-query-definition", definition.queryId, scope, `domainQueryRegistry[${index}].definitionRevisionRef`);
|
|
1286
|
+
if (definitionRevisionRef.contentHash !== (0, exports.canonicalContentHash)(definition)) {
|
|
1287
|
+
throw new DeclarativeControlCompilationError("QUERY_DEFINITION_MISMATCH", `domainQueryRegistry[${index}].definitionRevisionRef.contentHash`, "Domain Query registry entry does not match its exact definition content hash.");
|
|
1288
|
+
}
|
|
1289
|
+
return { definitionRevisionRef, definition };
|
|
1290
|
+
});
|
|
1291
|
+
assertUniqueRegistryKeys(queryRegistrations.map((registration) => (0, exports.revisionRefKey)(registration.definitionRevisionRef)), "domainQueryRegistry");
|
|
1292
|
+
const queryDefinitionsByRevision = new Map(queryRegistrations.map((registration) => [
|
|
1293
|
+
(0, exports.revisionRefKey)(registration.definitionRevisionRef),
|
|
1294
|
+
registration.definition,
|
|
1295
|
+
]));
|
|
1296
|
+
page.queryBindings.forEach((binding, index) => {
|
|
1297
|
+
const definition = queryDefinitionsByRevision.get((0, exports.revisionRefKey)(binding.queryDefinitionRevisionRef));
|
|
1298
|
+
if (!definition) {
|
|
1299
|
+
throw new DeclarativeControlCompilationError("QUERY_DEFINITION_MISMATCH", `queryBindings[${index}].queryDefinitionRevisionRef`, "Query binding does not resolve to one exact governed Domain Query definition.");
|
|
1300
|
+
}
|
|
1301
|
+
if (!(0, exports.sameRevisionRef)(definition.resultSchemaRevisionRef, binding.renderModelSchemaRevisionRef)) {
|
|
1302
|
+
throw new DeclarativeControlCompilationError("QUERY_DEFINITION_MISMATCH", `queryBindings[${index}].renderModelSchemaRevisionRef`, "Query binding render model does not match the exact Domain Query result schema.");
|
|
1303
|
+
}
|
|
1304
|
+
if (!(0, exports.accessPolicyChainImplies)([page.pageAccessPolicy], definition.accessPolicy) &&
|
|
1305
|
+
binding.accessFailure !== "render-forbidden") {
|
|
1306
|
+
throw new DeclarativeControlCompilationError("AUDIENCE_WIDER_THAN_TARGET", `queryBindings[${index}]`, "Page access is wider than the governed Domain Query audience.");
|
|
1307
|
+
}
|
|
1308
|
+
const sourceKind = definition.dataSource?.kind ?? "exact-view";
|
|
1309
|
+
if (binding.execution === "local-state" && sourceKind !== "page-state") {
|
|
1310
|
+
throw new DeclarativeControlCompilationError("QUERY_DEFINITION_MISMATCH", `queryBindings[${index}].execution`, "A local-state binding must resolve to one page-state Domain Query definition.");
|
|
1311
|
+
}
|
|
1312
|
+
if (binding.execution === "server" && sourceKind === "page-state") {
|
|
1313
|
+
throw new DeclarativeControlCompilationError("QUERY_DEFINITION_MISMATCH", `queryBindings[${index}].execution`, "A page-state Domain Query cannot execute on the Server.");
|
|
1314
|
+
}
|
|
1315
|
+
requireDependencies(release.dependencySnapshot, domainQueryDependencies(definition));
|
|
1316
|
+
});
|
|
1317
|
+
const shellRegistration = declarative_control_http_1.DeclarativeProductResourceRegistrationSchema.parse(input.shellRegistration);
|
|
1318
|
+
if (shellRegistration.resourceKind !== "shell" ||
|
|
1319
|
+
!(0, exports.sameRevisionRef)(shellRegistration.resourceRevisionRef, page.shellRevisionRef) ||
|
|
1320
|
+
(0, exports.declarativeProductResourceRevisionHash)(shellRegistration) !==
|
|
1321
|
+
shellRegistration.resourceRevisionRef.contentHash ||
|
|
1322
|
+
(0, exports.declarativeProductResourceSourceHash)(shellRegistration.document) !==
|
|
1323
|
+
shellRegistration.sourceContentHash) {
|
|
1324
|
+
throw new DeclarativeControlCompilationError("SHELL_REVISION_MISMATCH", "shellRegistration", "The active Shell registration does not match the Page exact Shell revision.");
|
|
1325
|
+
}
|
|
1326
|
+
if (!shellRegistration.supportedSurfaces.includes(release.target.surface)) {
|
|
1327
|
+
throw new DeclarativeControlCompilationError("SHELL_SURFACE_MISMATCH", "shellRegistration.supportedSurfaces", "The active Shell registration does not support the release surface.");
|
|
1328
|
+
}
|
|
1329
|
+
const shellDocument = declarative_control_http_1.DeclarativeShellResourceDocumentSchema.parse(shellRegistration.document);
|
|
1330
|
+
const shellChrome = shellDocument.surfaceChrome.find((entry) => entry.surface === release.target.surface);
|
|
1331
|
+
if (!shellChrome) {
|
|
1332
|
+
throw new DeclarativeControlCompilationError("SHELL_CHROME_UNAVAILABLE", "shellRegistration.document.surfaceChrome", "The active Shell does not declare chrome for the release surface.");
|
|
1333
|
+
}
|
|
1334
|
+
const shellDescriptor = {
|
|
1335
|
+
shellRevisionRef: page.shellRevisionRef,
|
|
1336
|
+
header: shellChrome.header,
|
|
1337
|
+
};
|
|
396
1338
|
if (page.renderTree.nodes.length > input.limits.maxRenderNodes)
|
|
397
|
-
throw new DeclarativeControlCompilationError(
|
|
1339
|
+
throw new DeclarativeControlCompilationError("RENDER_NODE_LIMIT_EXCEEDED", "page.renderTree.nodes", "RenderTree exceeds the caller-approved node limit.");
|
|
398
1340
|
if (renderTreeDepth(page.renderTree) > input.limits.maxRenderDepth)
|
|
399
|
-
throw new DeclarativeControlCompilationError(
|
|
1341
|
+
throw new DeclarativeControlCompilationError("RENDER_DEPTH_EXCEEDED", "page.renderTree.nodes", "RenderTree exceeds the caller-approved depth limit.");
|
|
400
1342
|
const renderTree = (0, render_tree_compiler_1.compileRenderTree)(page.renderTree).tree;
|
|
401
1343
|
const instancesByNode = new Map(page.capabilityInstances.map((instance) => [instance.nodeId, instance]));
|
|
402
1344
|
const registrations = input.capabilityRegistry.map((registration, registrationIndex) => ({
|
|
@@ -405,65 +1347,234 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
405
1347
|
providerRevisionRef: declarative_control_1.RevisionRefSchema.parse(registration.providerRevisionRef),
|
|
406
1348
|
inputPorts: registration.inputPorts.map((port, portIndex) => {
|
|
407
1349
|
const schemaRevisionRef = declarative_control_1.RevisionRefSchema.parse(port.schemaRevisionRef);
|
|
408
|
-
if (schemaRevisionRef.kind !==
|
|
409
|
-
throw new DeclarativeControlCompilationError(
|
|
1350
|
+
if (schemaRevisionRef.kind !== "schema") {
|
|
1351
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `capabilityRegistry[${registrationIndex}].inputPorts[${portIndex}].schemaRevisionRef`, "Capability ports must pin schema revisions.");
|
|
410
1352
|
}
|
|
411
1353
|
return { ...port, schemaRevisionRef };
|
|
412
1354
|
}),
|
|
413
1355
|
outputPorts: registration.outputPorts.map((port, portIndex) => {
|
|
414
1356
|
const schemaRevisionRef = declarative_control_1.RevisionRefSchema.parse(port.schemaRevisionRef);
|
|
415
|
-
if (schemaRevisionRef.kind !==
|
|
416
|
-
throw new DeclarativeControlCompilationError(
|
|
1357
|
+
if (schemaRevisionRef.kind !== "schema") {
|
|
1358
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `capabilityRegistry[${registrationIndex}].outputPorts[${portIndex}].schemaRevisionRef`, "Capability ports must pin schema revisions.");
|
|
417
1359
|
}
|
|
418
1360
|
return { ...port, schemaRevisionRef };
|
|
419
1361
|
}),
|
|
420
1362
|
}));
|
|
421
|
-
assertUniqueRegistryKeys(registrations.map((registration) => `${(0, exports.revisionRefKey)(registration.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(registration.providerRevisionRef)}`),
|
|
1363
|
+
assertUniqueRegistryKeys(registrations.map((registration) => `${(0, exports.revisionRefKey)(registration.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(registration.providerRevisionRef)}`), "capabilityRegistry");
|
|
422
1364
|
const registrationsByPair = new Map(registrations.map((registration) => [
|
|
423
1365
|
`${(0, exports.revisionRefKey)(registration.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(registration.providerRevisionRef)}`,
|
|
424
1366
|
registration,
|
|
425
1367
|
]));
|
|
426
1368
|
page.capabilityInstances.forEach((instance, index) => {
|
|
427
1369
|
const node = renderTree.nodes.find((candidate) => candidate.nodeId === instance.nodeId);
|
|
428
|
-
if (!node ||
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
1370
|
+
if (!node ||
|
|
1371
|
+
!compareLegacyEntityRef(node.capabilityRef, instance.capabilityRevisionRef) ||
|
|
1372
|
+
!node.providerRef ||
|
|
1373
|
+
!compareLegacyEntityRef(node.providerRef, instance.providerRevisionRef) ||
|
|
1374
|
+
node.pageRef.id !== page.pageId) {
|
|
1375
|
+
throw new DeclarativeControlCompilationError("CAPABILITY_BINDING_MISMATCH", `capabilityInstances[${index}]`, "RenderTree node identity does not match the declared capability/provider instance.");
|
|
432
1376
|
}
|
|
433
1377
|
const registration = registrationsByPair.get(`${(0, exports.revisionRefKey)(instance.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(instance.providerRevisionRef)}`);
|
|
434
|
-
if (!registration
|
|
435
|
-
throw new DeclarativeControlCompilationError(
|
|
1378
|
+
if (!registration) {
|
|
1379
|
+
throw new DeclarativeControlCompilationError("CAPABILITY_BINDING_MISMATCH", `capabilityInstances[${index}]`, "Capability/provider pair has no exact governed Design registration.");
|
|
436
1380
|
}
|
|
437
1381
|
if (instance.allowedSideEffects.some((sideEffect) => !registration.allowedSideEffects.includes(sideEffect))) {
|
|
438
|
-
throw new DeclarativeControlCompilationError(
|
|
1382
|
+
throw new DeclarativeControlCompilationError("CAPABILITY_BINDING_MISMATCH", `capabilityInstances[${index}].allowedSideEffects`, "Page requests undeclared provider side effects.");
|
|
1383
|
+
}
|
|
1384
|
+
if (!(0, exports.sameRevisionRef)(instance.propertySchemaRevisionRef, registration.propertySchemaRevisionRef)) {
|
|
1385
|
+
throw new DeclarativeControlCompilationError("CAPABILITY_BINDING_MISMATCH", `capabilityInstances[${index}].propertySchemaRevisionRef`, "Capability properties do not pin the exact schema registered by the active Design catalog.");
|
|
1386
|
+
}
|
|
1387
|
+
if (!(0, exports.accessPolicyChainImplies)([
|
|
1388
|
+
page.pageAccessPolicy,
|
|
1389
|
+
...(instance.accessPolicy ? [instance.accessPolicy] : []),
|
|
1390
|
+
], registration.accessPolicy)) {
|
|
1391
|
+
throw new DeclarativeControlCompilationError("AUDIENCE_WIDER_THAN_TARGET", `capabilityInstances[${index}]`, "Page access is wider than the governed capability audience.");
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1394
|
+
if (instancesByNode.size !== page.capabilityInstances.length) {
|
|
1395
|
+
throw new DeclarativeControlCompilationError("CAPABILITY_BINDING_MISMATCH", "capabilityInstances", "Each executable RenderTree node must be governed by at most one capability instance.");
|
|
1396
|
+
}
|
|
1397
|
+
renderTree.nodes.forEach((node, index) => {
|
|
1398
|
+
if (!node.providerRef)
|
|
1399
|
+
return;
|
|
1400
|
+
if (!instancesByNode.has(node.nodeId)) {
|
|
1401
|
+
throw new DeclarativeControlCompilationError("CAPABILITY_BINDING_MISMATCH", `renderTree.nodes[${index}]`, "Every provider-backed RenderTree node must be governed by one capability instance.");
|
|
439
1402
|
}
|
|
440
1403
|
});
|
|
441
1404
|
page.ontologyBindings.forEach((binding, index) => {
|
|
442
1405
|
const instance = page.capabilityInstances.find((candidate) => candidate.instanceId === binding.target.capabilityInstanceId);
|
|
443
|
-
const registration = instance &&
|
|
1406
|
+
const registration = instance &&
|
|
1407
|
+
registrationsByPair.get(`${(0, exports.revisionRefKey)(instance.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(instance.providerRevisionRef)}`);
|
|
1408
|
+
const port = registration?.inputPorts.find((candidate) => candidate.name === binding.target.port);
|
|
1409
|
+
if (!port ||
|
|
1410
|
+
!(0, exports.sameRevisionRef)(port.schemaRevisionRef, binding.renderModelSchemaRevisionRef)) {
|
|
1411
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `ontologyBindings[${index}].target.port`, "Ontology render model does not match the registered capability input port.");
|
|
1412
|
+
}
|
|
1413
|
+
if (binding.cursorWindow) {
|
|
1414
|
+
const sourceInstance = page.capabilityInstances.find((candidate) => candidate.instanceId ===
|
|
1415
|
+
(binding.cursorWindow.sourceCapabilityInstanceId ??
|
|
1416
|
+
binding.target.capabilityInstanceId));
|
|
1417
|
+
const sourceRegistration = sourceInstance &&
|
|
1418
|
+
registrationsByPair.get(`${(0, exports.revisionRefKey)(sourceInstance.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(sourceInstance.providerRevisionRef)}`);
|
|
1419
|
+
const pageChangePort = sourceRegistration?.outputPorts.find((candidate) => candidate.name === binding.cursorWindow.pageChangePort);
|
|
1420
|
+
if (!pageChangePort ||
|
|
1421
|
+
!(0, exports.sameRevisionRef)(pageChangePort.schemaRevisionRef, binding.cursorWindow.pageChangeIntentSchemaRevisionRef)) {
|
|
1422
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `ontologyBindings[${index}].cursorWindow.pageChangePort`, "Cursor-window page-change intent does not match the registered capability output port.");
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1426
|
+
page.queryBindings.forEach((binding, index) => {
|
|
1427
|
+
const instance = page.capabilityInstances.find((candidate) => candidate.instanceId === binding.target.capabilityInstanceId);
|
|
1428
|
+
const registration = instance &&
|
|
1429
|
+
registrationsByPair.get(`${(0, exports.revisionRefKey)(instance.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(instance.providerRevisionRef)}`);
|
|
444
1430
|
const port = registration?.inputPorts.find((candidate) => candidate.name === binding.target.port);
|
|
445
|
-
if (!port ||
|
|
446
|
-
|
|
1431
|
+
if (!port ||
|
|
1432
|
+
!(0, exports.sameRevisionRef)(port.schemaRevisionRef, binding.targetProjection?.schemaRevisionRef ??
|
|
1433
|
+
binding.renderModelSchemaRevisionRef)) {
|
|
1434
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `queryBindings[${index}].target.port`, "Domain Query render model does not match the registered capability input port.");
|
|
1435
|
+
}
|
|
1436
|
+
if (binding.streamTargetProjection &&
|
|
1437
|
+
(!port ||
|
|
1438
|
+
!(0, exports.sameRevisionRef)(port.schemaRevisionRef, binding.streamTargetProjection.schemaRevisionRef))) {
|
|
1439
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `queryBindings[${index}].streamTargetProjection`, "Stream render model does not match the registered capability input port.");
|
|
1440
|
+
}
|
|
1441
|
+
if (binding.cursorWindow) {
|
|
1442
|
+
const sourceInstance = page.capabilityInstances.find((candidate) => candidate.instanceId ===
|
|
1443
|
+
(binding.cursorWindow.sourceCapabilityInstanceId ??
|
|
1444
|
+
binding.target.capabilityInstanceId));
|
|
1445
|
+
const sourceRegistration = sourceInstance &&
|
|
1446
|
+
registrationsByPair.get(`${(0, exports.revisionRefKey)(sourceInstance.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(sourceInstance.providerRevisionRef)}`);
|
|
1447
|
+
const pageChangePort = sourceRegistration?.outputPorts.find((candidate) => candidate.name === binding.cursorWindow.pageChangePort);
|
|
1448
|
+
if (!pageChangePort ||
|
|
1449
|
+
!(0, exports.sameRevisionRef)(pageChangePort.schemaRevisionRef, binding.cursorWindow.pageChangeIntentSchemaRevisionRef)) {
|
|
1450
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `queryBindings[${index}].cursorWindow.pageChangePort`, "Cursor-window page-change intent does not match the registered capability output port.");
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
});
|
|
1454
|
+
page.capabilityInstances.forEach((instance, index) => {
|
|
1455
|
+
const registration = registrationsByPair.get(`${(0, exports.revisionRefKey)(instance.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(instance.providerRevisionRef)}`);
|
|
1456
|
+
instance.eventPayloadBindings?.forEach((binding, bindingIndex) => {
|
|
1457
|
+
if (!registration?.outputPorts.some((port) => port.name === binding.port))
|
|
1458
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `capabilityInstances[${index}].eventPayloadBindings[${bindingIndex}]`, "Event payload projection must reference a registered output port.");
|
|
1459
|
+
});
|
|
1460
|
+
instance.eventEffects?.forEach((effect, effectIndex) => {
|
|
1461
|
+
const port = registration?.outputPorts.find((candidate) => candidate.name === effect.sourcePort);
|
|
1462
|
+
if (!port ||
|
|
1463
|
+
!(0, exports.sameRevisionRef)(port.schemaRevisionRef, effect.sourceIntentSchemaRevisionRef))
|
|
1464
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `capabilityInstances[${index}].eventEffects[${effectIndex}]`, "Event effect source must match an exact registered output port schema.");
|
|
1465
|
+
});
|
|
1466
|
+
});
|
|
1467
|
+
page.interactionBindings.forEach((binding, index) => {
|
|
1468
|
+
const instance = page.capabilityInstances.find((candidate) => candidate.instanceId === binding.source.capabilityInstanceId);
|
|
1469
|
+
const registration = instance &&
|
|
1470
|
+
registrationsByPair.get(`${(0, exports.revisionRefKey)(instance.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(instance.providerRevisionRef)}`);
|
|
1471
|
+
const port = registration?.outputPorts.find((candidate) => candidate.name === binding.source.port);
|
|
1472
|
+
if (!port ||
|
|
1473
|
+
!(0, exports.sameRevisionRef)(port.schemaRevisionRef, binding.sourceIntentSchemaRevisionRef)) {
|
|
1474
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `interactionBindings[${index}].sourceIntentSchemaRevisionRef`, "Interaction source intent does not match the registered capability output port.");
|
|
447
1475
|
}
|
|
448
1476
|
});
|
|
449
1477
|
page.actionBindings.forEach((binding, index) => {
|
|
450
1478
|
const instance = page.capabilityInstances.find((candidate) => candidate.instanceId === binding.source.capabilityInstanceId);
|
|
451
|
-
const registration = instance &&
|
|
1479
|
+
const registration = instance &&
|
|
1480
|
+
registrationsByPair.get(`${(0, exports.revisionRefKey)(instance.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(instance.providerRevisionRef)}`);
|
|
452
1481
|
const port = registration?.outputPorts.find((candidate) => candidate.name === binding.source.port);
|
|
453
|
-
if (!port ||
|
|
454
|
-
|
|
1482
|
+
if (!port ||
|
|
1483
|
+
!(0, exports.sameRevisionRef)(port.schemaRevisionRef, binding.sourceIntentSchemaRevisionRef)) {
|
|
1484
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `actionBindings[${index}].sourceIntentSchemaRevisionRef`, "Action source intent does not match the registered capability output port.");
|
|
455
1485
|
}
|
|
456
1486
|
});
|
|
457
|
-
const routeClaims = (0, exports.compileRouteClaims)({
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
1487
|
+
const routeClaims = (0, exports.compileRouteClaims)({
|
|
1488
|
+
claims: page.routeClaims,
|
|
1489
|
+
routeSpaces: input.routeSpaces,
|
|
1490
|
+
surface: release.target.surface,
|
|
1491
|
+
legacyRouteTakeoverAuthorizations: release.target
|
|
1492
|
+
.legacyRouteTakeoverAuthorization
|
|
1493
|
+
? [release.target.legacyRouteTakeoverAuthorization]
|
|
1494
|
+
: [],
|
|
1495
|
+
});
|
|
1496
|
+
const releaseRoute = routeClaims.find((claim) => claim.kind === "canonical" &&
|
|
1497
|
+
claim.normalizedPath === release.target.normalizedPath);
|
|
1498
|
+
if (!releaseRoute ||
|
|
1499
|
+
!(0, exports.sameRevisionRef)(releaseRoute.routeSpaceRevisionRef, release.target.routeSpaceRevisionRef)) {
|
|
1500
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", "release.target", "Release target does not match an exact Page route claim.");
|
|
461
1501
|
}
|
|
462
1502
|
if (!page.supportedSurfaces.includes(release.target.surface)) {
|
|
463
|
-
throw new DeclarativeControlCompilationError(
|
|
1503
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", "release.target.surface", "Page does not support the release surface.");
|
|
464
1504
|
}
|
|
1505
|
+
const routeTargets = (input.targetRegistry ?? []).map((registration, index) => ({
|
|
1506
|
+
...registration,
|
|
1507
|
+
stableTargetRef: declarative_control_1.StableRefSchema.parse(registration.stableTargetRef),
|
|
1508
|
+
targetRevisionRef: declarative_control_1.RevisionRefSchema.parse(registration.targetRevisionRef),
|
|
1509
|
+
...(registration.releaseRevisionRef
|
|
1510
|
+
? {
|
|
1511
|
+
releaseRevisionRef: declarative_control_1.RevisionRefSchema.parse(registration.releaseRevisionRef),
|
|
1512
|
+
}
|
|
1513
|
+
: {}),
|
|
1514
|
+
accessPolicy: declarative_control_1.AccessPolicySchema.parse(registration.accessPolicy),
|
|
1515
|
+
routeClaim: declarative_control_1.CompiledRouteClaimSchema.parse(registration.routeClaim),
|
|
1516
|
+
sourceIndex: index,
|
|
1517
|
+
}));
|
|
1518
|
+
assertUniqueRegistryKeys(routeTargets.map((registration) => (0, exports.stableRefKey)(registration.stableTargetRef)), "targetRegistry");
|
|
1519
|
+
const entryTransitions = page.entryTransitions.map((transition, index) => {
|
|
1520
|
+
const matches = routeTargets.filter((registration) => registration.kind === "route" &&
|
|
1521
|
+
(0, exports.sameStableRef)(registration.stableTargetRef, transition.targetRef) &&
|
|
1522
|
+
registration.surface === release.target.surface);
|
|
1523
|
+
if (matches.length !== 1) {
|
|
1524
|
+
throw new DeclarativeControlCompilationError("PAGE_ENTRY_TARGET_UNRELEASED", `entryTransitions[${index}].targetRef`, "Entry transition target must resolve to one exact active route registration on the same surface.");
|
|
1525
|
+
}
|
|
1526
|
+
const target = matches[0];
|
|
1527
|
+
if (target.releaseRevisionRef &&
|
|
1528
|
+
!["page-release", "workbench-release"].includes(target.releaseRevisionRef.kind)) {
|
|
1529
|
+
throw new DeclarativeControlCompilationError("PAGE_ENTRY_TARGET_UNRELEASED", `entryTransitions[${index}].targetRef`, "Entry transition Release provenance must identify a Page or Workbench Release.");
|
|
1530
|
+
}
|
|
1531
|
+
if (target.routeClaim.kind !== "canonical") {
|
|
1532
|
+
throw new DeclarativeControlCompilationError("PAGE_ENTRY_TARGET_UNRELEASED", `entryTransitions[${index}].targetRef`, "Entry transition targets must expose one canonical route claim.");
|
|
1533
|
+
}
|
|
1534
|
+
if (!(0, exports.sameStableRef)(target.stableTargetRef, target.targetRevisionRef)) {
|
|
1535
|
+
throw new DeclarativeControlCompilationError("PAGE_ENTRY_TARGET_UNRELEASED", `targetRegistry[${target.sourceIndex}]`, "Entry transition target registration identity is not exact.");
|
|
1536
|
+
}
|
|
1537
|
+
// Entry transitions may narrow into a separately protected route. The target
|
|
1538
|
+
// route remains authoritative and re-evaluates its own access policy after
|
|
1539
|
+
// navigation, so a source Page must not inherit the target's permissions.
|
|
1540
|
+
const requiredPathParameters = [
|
|
1541
|
+
...target.routeClaim.normalizedPath.matchAll(/:([A-Za-z][A-Za-z0-9_-]*)/g),
|
|
1542
|
+
].map((match) => match[1]);
|
|
1543
|
+
const mappedPathParameters = Object.keys(transition.pathParameters);
|
|
1544
|
+
const missing = requiredPathParameters.find((name) => !mappedPathParameters.includes(name));
|
|
1545
|
+
const unknown = mappedPathParameters.find((name) => !requiredPathParameters.includes(name));
|
|
1546
|
+
if (missing || unknown) {
|
|
1547
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `entryTransitions[${index}].pathParameters`, missing
|
|
1548
|
+
? `Entry transition does not resolve target route parameter ${missing}.`
|
|
1549
|
+
: `Entry transition maps unknown target route parameter ${unknown}.`);
|
|
1550
|
+
}
|
|
1551
|
+
requireDependencies(release.dependencySnapshot, [
|
|
1552
|
+
{
|
|
1553
|
+
role: target.targetRevisionRef.kind,
|
|
1554
|
+
revisionRef: target.targetRevisionRef,
|
|
1555
|
+
},
|
|
1556
|
+
...(target.releaseRevisionRef
|
|
1557
|
+
? [
|
|
1558
|
+
{
|
|
1559
|
+
role: target.releaseRevisionRef.kind,
|
|
1560
|
+
revisionRef: target.releaseRevisionRef,
|
|
1561
|
+
},
|
|
1562
|
+
]
|
|
1563
|
+
: []),
|
|
1564
|
+
]);
|
|
1565
|
+
return {
|
|
1566
|
+
...transition,
|
|
1567
|
+
browsingContext: transition.browsingContext ?? "same",
|
|
1568
|
+
targetRevisionRef: target.targetRevisionRef,
|
|
1569
|
+
...(target.releaseRevisionRef
|
|
1570
|
+
? { targetReleaseRevisionRef: target.releaseRevisionRef }
|
|
1571
|
+
: {}),
|
|
1572
|
+
targetAccessPolicy: target.accessPolicy,
|
|
1573
|
+
targetRouteClaim: target.routeClaim,
|
|
1574
|
+
};
|
|
1575
|
+
});
|
|
465
1576
|
const unsigned = {
|
|
466
|
-
contract:
|
|
1577
|
+
contract: "PageRuntimeBundle",
|
|
467
1578
|
schemaVersion: 1,
|
|
468
1579
|
tenantScope: scope,
|
|
469
1580
|
releaseRevisionRef,
|
|
@@ -476,10 +1587,17 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
476
1587
|
pageRevisionRef,
|
|
477
1588
|
surface: release.target.surface,
|
|
478
1589
|
routeClaims,
|
|
1590
|
+
routeStatePresentations: page.routeStatePresentations.filter((presentation) => presentation.surface === release.target.surface),
|
|
479
1591
|
shellRevisionRef: page.shellRevisionRef,
|
|
1592
|
+
shellDescriptor,
|
|
480
1593
|
renderTree,
|
|
481
1594
|
capabilityInstances: page.capabilityInstances,
|
|
1595
|
+
deniedCapabilityInstanceIds: [],
|
|
1596
|
+
stateDefinitions: page.stateDefinitions,
|
|
1597
|
+
entryTransitions,
|
|
1598
|
+
interactionBindings: page.interactionBindings,
|
|
482
1599
|
ontologyBindings: page.ontologyBindings,
|
|
1600
|
+
queryBindings: page.queryBindings,
|
|
483
1601
|
actionBindings: page.actionBindings,
|
|
484
1602
|
pageAccessPolicy: page.pageAccessPolicy,
|
|
485
1603
|
tokenRevisionRefs: page.tokenRevisionRefs,
|
|
@@ -487,33 +1605,60 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
487
1605
|
observationPolicyRevisionRef: page.observationPolicyRevisionRef,
|
|
488
1606
|
privacyClassification: page.privacyClassification,
|
|
489
1607
|
};
|
|
490
|
-
return deepFreeze(declarative_control_1.PageRuntimeBundleSchema.parse({
|
|
1608
|
+
return deepFreeze(declarative_control_1.PageRuntimeBundleSchema.parse({
|
|
1609
|
+
...unsigned,
|
|
1610
|
+
contentHash: (0, exports.canonicalContentHash)(unsigned),
|
|
1611
|
+
}));
|
|
491
1612
|
};
|
|
492
1613
|
exports.compilePageRuntimeBundle = compilePageRuntimeBundle;
|
|
493
1614
|
const workbenchDependencyRole = (kind) => {
|
|
494
1615
|
switch (kind) {
|
|
495
|
-
case
|
|
496
|
-
|
|
497
|
-
case
|
|
498
|
-
|
|
499
|
-
case
|
|
500
|
-
|
|
1616
|
+
case "workflow":
|
|
1617
|
+
return "workflow";
|
|
1618
|
+
case "agent":
|
|
1619
|
+
return "agent";
|
|
1620
|
+
case "capability":
|
|
1621
|
+
return "capability";
|
|
1622
|
+
case "design":
|
|
1623
|
+
return "design";
|
|
1624
|
+
case "built-in-application":
|
|
1625
|
+
return "built-in-application";
|
|
1626
|
+
default:
|
|
1627
|
+
return "workflow";
|
|
501
1628
|
}
|
|
502
1629
|
};
|
|
503
1630
|
const workbenchDependencies = (workbench, compilerRevisionRef) => [
|
|
504
|
-
...workbench.routeClaims.map((claim) => ({
|
|
1631
|
+
...workbench.routeClaims.map((claim) => ({
|
|
1632
|
+
role: "route-space",
|
|
1633
|
+
revisionRef: claim.routeSpaceRevisionRef,
|
|
1634
|
+
})),
|
|
505
1635
|
...workbench.appInstances.flatMap((instance) => [
|
|
506
|
-
{
|
|
507
|
-
|
|
508
|
-
|
|
1636
|
+
{
|
|
1637
|
+
role: workbenchDependencyRole(instance.targetRevisionRef.kind),
|
|
1638
|
+
revisionRef: instance.targetRevisionRef,
|
|
1639
|
+
},
|
|
1640
|
+
{ role: "provider", revisionRef: instance.providerRevisionRef },
|
|
1641
|
+
{ role: "schema", revisionRef: instance.inputSchemaRevisionRef },
|
|
509
1642
|
]),
|
|
510
|
-
{
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
{ role:
|
|
515
|
-
{
|
|
516
|
-
|
|
1643
|
+
{
|
|
1644
|
+
role: "capability",
|
|
1645
|
+
revisionRef: workbench.layout.hostCapabilityRevisionRef,
|
|
1646
|
+
},
|
|
1647
|
+
{ role: "provider", revisionRef: workbench.layout.hostProviderRevisionRef },
|
|
1648
|
+
{
|
|
1649
|
+
role: "layout-policy",
|
|
1650
|
+
revisionRef: workbench.layout.layoutPolicyRevisionRef,
|
|
1651
|
+
},
|
|
1652
|
+
...workbench.layout.tokenRevisionRefs.map((revisionRef) => ({
|
|
1653
|
+
role: "token",
|
|
1654
|
+
revisionRef,
|
|
1655
|
+
})),
|
|
1656
|
+
{ role: "performance-budget", revisionRef: workbench.performanceBudgetRef },
|
|
1657
|
+
{
|
|
1658
|
+
role: "observation-policy",
|
|
1659
|
+
revisionRef: workbench.observationPolicyRevisionRef,
|
|
1660
|
+
},
|
|
1661
|
+
{ role: "compiler", revisionRef: compilerRevisionRef },
|
|
517
1662
|
];
|
|
518
1663
|
const compileWorkbenchRuntimeBundle = (input) => {
|
|
519
1664
|
const workbench = declarative_control_1.WorkbenchSchema.parse(input.workbench);
|
|
@@ -522,34 +1667,48 @@ const compileWorkbenchRuntimeBundle = (input) => {
|
|
|
522
1667
|
const releaseRevisionRef = declarative_control_1.RevisionRefSchema.parse(input.releaseRevisionRef);
|
|
523
1668
|
const compilerRevisionRef = declarative_control_1.RevisionRefSchema.parse(input.compilerRevisionRef);
|
|
524
1669
|
const scope = declarative_control_1.TenantScopeSchema.parse(workbench.tenantScope);
|
|
525
|
-
assertSameTenantScope(release.tenantScope, scope,
|
|
526
|
-
if (workbench.purpose ===
|
|
527
|
-
throw new DeclarativeControlCompilationError(
|
|
528
|
-
if (workbench.groups.length > input.limits.maxWorkbenchGroups ||
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
assertRevisionIdentity(
|
|
1670
|
+
assertSameTenantScope(release.tenantScope, scope, "release.tenantScope");
|
|
1671
|
+
if (workbench.purpose === "template")
|
|
1672
|
+
throw new DeclarativeControlCompilationError("WORKBENCH_TEMPLATE_UNRELEASABLE", "workbench.purpose", "Workbench templates cannot be activated.");
|
|
1673
|
+
if (workbench.groups.length > input.limits.maxWorkbenchGroups ||
|
|
1674
|
+
workbench.appInstances.length > input.limits.maxWorkbenchInstances) {
|
|
1675
|
+
throw new DeclarativeControlCompilationError("WORKBENCH_INSTANCE_LIMIT_EXCEEDED", "workbench", "Workbench exceeds caller-approved compilation limits.");
|
|
1676
|
+
}
|
|
1677
|
+
assertRevisionIdentity(workbenchRevisionRef, "workbench", workbench.workbenchId, scope, "workbenchRevisionRef");
|
|
1678
|
+
assertRevisionIdentity(release.workbenchRevisionRef, "workbench", workbench.workbenchId, scope, "release.workbenchRevisionRef");
|
|
533
1679
|
if (!(0, exports.sameRevisionRef)(workbenchRevisionRef, release.workbenchRevisionRef))
|
|
534
|
-
throw new DeclarativeControlCompilationError(
|
|
535
|
-
assertRevisionIdentity(releaseRevisionRef,
|
|
1680
|
+
throw new DeclarativeControlCompilationError("REVISION_MISMATCH", "release.workbenchRevisionRef", "Release does not pin the supplied Workbench revision.");
|
|
1681
|
+
assertRevisionIdentity(releaseRevisionRef, "workbench-release", release.releaseSlotId, scope, "releaseRevisionRef");
|
|
536
1682
|
if (release.target.workbenchId !== workbench.workbenchId)
|
|
537
|
-
throw new DeclarativeControlCompilationError(
|
|
1683
|
+
throw new DeclarativeControlCompilationError("REVISION_MISMATCH", "release.target.workbenchId", "Release target does not match Workbench identity.");
|
|
538
1684
|
requireReleaseReady(release);
|
|
539
1685
|
requireDependencies(release.dependencySnapshot, workbenchDependencies(workbench, compilerRevisionRef));
|
|
540
1686
|
assertDependencyScopes(release.dependencySnapshot, scope);
|
|
541
|
-
const routeClaims = (0, exports.compileRouteClaims)({
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
1687
|
+
const routeClaims = (0, exports.compileRouteClaims)({
|
|
1688
|
+
claims: workbench.routeClaims,
|
|
1689
|
+
routeSpaces: input.routeSpaces,
|
|
1690
|
+
surface: release.target.surface,
|
|
1691
|
+
legacyRouteTakeoverAuthorizations: release.target
|
|
1692
|
+
.legacyRouteTakeoverAuthorization
|
|
1693
|
+
? [release.target.legacyRouteTakeoverAuthorization]
|
|
1694
|
+
: [],
|
|
1695
|
+
});
|
|
1696
|
+
const releaseRoute = routeClaims.find((claim) => claim.kind === "canonical" &&
|
|
1697
|
+
claim.normalizedPath === release.target.normalizedPath);
|
|
1698
|
+
if (!releaseRoute ||
|
|
1699
|
+
!(0, exports.sameRevisionRef)(releaseRoute.routeSpaceRevisionRef, release.target.routeSpaceRevisionRef)) {
|
|
1700
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", "release.target", "Release target does not match an exact Workbench route claim.");
|
|
545
1701
|
}
|
|
546
1702
|
const targetRegistry = input.targetRegistry.map((registration) => ({
|
|
547
1703
|
stableTargetRef: declarative_control_1.StableRefSchema.parse(registration.stableTargetRef),
|
|
548
1704
|
targetRevisionRef: declarative_control_1.RevisionRefSchema.parse(registration.targetRevisionRef),
|
|
549
1705
|
accessPolicy: declarative_control_1.AccessPolicySchema.parse(registration.accessPolicy),
|
|
550
1706
|
}));
|
|
551
|
-
assertUniqueRegistryKeys(targetRegistry.map((registration) => (0, exports.stableRefKey)(registration.stableTargetRef)),
|
|
552
|
-
const targetsByStableRef = new Map(targetRegistry.map((registration) => [
|
|
1707
|
+
assertUniqueRegistryKeys(targetRegistry.map((registration) => (0, exports.stableRefKey)(registration.stableTargetRef)), "targetRegistry");
|
|
1708
|
+
const targetsByStableRef = new Map(targetRegistry.map((registration) => [
|
|
1709
|
+
(0, exports.stableRefKey)(registration.stableTargetRef),
|
|
1710
|
+
registration,
|
|
1711
|
+
]));
|
|
553
1712
|
const groupsById = new Map(workbench.groups.map((group) => [group.groupId, group]));
|
|
554
1713
|
const appInstances = workbench.appInstances.map((instance, index) => {
|
|
555
1714
|
const stableTarget = declarative_control_1.StableRefSchema.parse({
|
|
@@ -557,23 +1716,30 @@ const compileWorkbenchRuntimeBundle = (input) => {
|
|
|
557
1716
|
id: instance.targetRevisionRef.id,
|
|
558
1717
|
ownerRepo: instance.targetRevisionRef.ownerRepo,
|
|
559
1718
|
visibility: instance.targetRevisionRef.visibility,
|
|
560
|
-
...(instance.targetRevisionRef.tenantScope
|
|
1719
|
+
...(instance.targetRevisionRef.tenantScope
|
|
1720
|
+
? { tenantScope: instance.targetRevisionRef.tenantScope }
|
|
1721
|
+
: {}),
|
|
561
1722
|
});
|
|
562
1723
|
const registration = targetsByStableRef.get((0, exports.stableRefKey)(stableTarget));
|
|
563
|
-
if (!registration ||
|
|
564
|
-
|
|
565
|
-
|
|
1724
|
+
if (!registration ||
|
|
1725
|
+
!(0, exports.sameStableRef)(registration.stableTargetRef, stableFromRevision(registration.targetRevisionRef)) ||
|
|
1726
|
+
!(0, exports.sameRevisionRef)(registration.targetRevisionRef, instance.targetRevisionRef)) {
|
|
1727
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", `appInstances[${index}].targetRevisionRef`, "Workbench app target is not present at the exact registered revision.");
|
|
566
1728
|
}
|
|
567
1729
|
assertTenantCompatible(instance.targetRevisionRef, scope, `appInstances[${index}].targetRevisionRef`);
|
|
568
1730
|
const group = groupsById.get(instance.groupId);
|
|
569
|
-
const chain = [
|
|
1731
|
+
const chain = [
|
|
1732
|
+
workbench.workbenchAccessPolicy,
|
|
1733
|
+
...(group?.accessPolicy ? [group.accessPolicy] : []),
|
|
1734
|
+
instance.accessPolicy,
|
|
1735
|
+
];
|
|
570
1736
|
if (!(0, exports.accessPolicyChainImplies)(chain, registration.accessPolicy)) {
|
|
571
|
-
throw new DeclarativeControlCompilationError(
|
|
1737
|
+
throw new DeclarativeControlCompilationError("AUDIENCE_WIDER_THAN_TARGET", `appInstances[${index}].accessPolicy`, "Workbench and instance audience is wider than the target audience.");
|
|
572
1738
|
}
|
|
573
1739
|
return { ...instance, targetAccessPolicy: registration.accessPolicy };
|
|
574
1740
|
});
|
|
575
1741
|
const unsigned = {
|
|
576
|
-
contract:
|
|
1742
|
+
contract: "WorkbenchRuntimeBundle",
|
|
577
1743
|
schemaVersion: 1,
|
|
578
1744
|
tenantScope: scope,
|
|
579
1745
|
releaseRevisionRef,
|
|
@@ -584,6 +1750,12 @@ const compileWorkbenchRuntimeBundle = (input) => {
|
|
|
584
1750
|
diagnostics: [],
|
|
585
1751
|
workbenchId: workbench.workbenchId,
|
|
586
1752
|
workbenchRevisionRef,
|
|
1753
|
+
identity: {
|
|
1754
|
+
name: workbench.identity.name,
|
|
1755
|
+
...(workbench.identity.description
|
|
1756
|
+
? { description: workbench.identity.description }
|
|
1757
|
+
: {}),
|
|
1758
|
+
},
|
|
587
1759
|
surface: release.target.surface,
|
|
588
1760
|
routeClaims,
|
|
589
1761
|
groups: workbench.groups,
|
|
@@ -597,7 +1769,10 @@ const compileWorkbenchRuntimeBundle = (input) => {
|
|
|
597
1769
|
observationPolicyRevisionRef: workbench.observationPolicyRevisionRef,
|
|
598
1770
|
privacyClassification: workbench.privacyClassification,
|
|
599
1771
|
};
|
|
600
|
-
return deepFreeze(declarative_control_1.WorkbenchRuntimeBundleSchema.parse({
|
|
1772
|
+
return deepFreeze(declarative_control_1.WorkbenchRuntimeBundleSchema.parse({
|
|
1773
|
+
...unsigned,
|
|
1774
|
+
contentHash: (0, exports.canonicalContentHash)(unsigned),
|
|
1775
|
+
}));
|
|
601
1776
|
};
|
|
602
1777
|
exports.compileWorkbenchRuntimeBundle = compileWorkbenchRuntimeBundle;
|
|
603
1778
|
const navigationDepth = (navigation) => {
|
|
@@ -617,7 +1792,7 @@ const navigationDepth = (navigation) => {
|
|
|
617
1792
|
const orderedNavigationNodes = (navigation) => {
|
|
618
1793
|
const children = new Map();
|
|
619
1794
|
navigation.nodes.forEach((node) => {
|
|
620
|
-
const parent = node.parentNodeId ??
|
|
1795
|
+
const parent = node.parentNodeId ?? "";
|
|
621
1796
|
const siblings = children.get(parent) ?? [];
|
|
622
1797
|
children.set(parent, [...siblings, node]);
|
|
623
1798
|
});
|
|
@@ -629,17 +1804,32 @@ const orderedNavigationNodes = (navigation) => {
|
|
|
629
1804
|
append(node.nodeId);
|
|
630
1805
|
}
|
|
631
1806
|
};
|
|
632
|
-
append(
|
|
1807
|
+
append("");
|
|
633
1808
|
return ordered;
|
|
634
1809
|
};
|
|
635
1810
|
const navigationDependencies = (navigation, release, compilerRevisionRef) => [
|
|
636
1811
|
...release.resolvedTargets.flatMap((target) => [
|
|
637
|
-
{
|
|
638
|
-
|
|
1812
|
+
{
|
|
1813
|
+
role: target.targetRevisionRef.kind === "domain-command"
|
|
1814
|
+
? "action"
|
|
1815
|
+
: target.targetRevisionRef.kind,
|
|
1816
|
+
revisionRef: target.targetRevisionRef,
|
|
1817
|
+
},
|
|
1818
|
+
...(target.releaseRevisionRef
|
|
1819
|
+
? [
|
|
1820
|
+
{
|
|
1821
|
+
role: target.releaseRevisionRef.kind,
|
|
1822
|
+
revisionRef: target.releaseRevisionRef,
|
|
1823
|
+
},
|
|
1824
|
+
]
|
|
1825
|
+
: []),
|
|
639
1826
|
]),
|
|
640
|
-
{ role:
|
|
641
|
-
{
|
|
642
|
-
|
|
1827
|
+
{ role: "performance-budget", revisionRef: navigation.performanceBudgetRef },
|
|
1828
|
+
{
|
|
1829
|
+
role: "observation-policy",
|
|
1830
|
+
revisionRef: navigation.observationPolicyRevisionRef,
|
|
1831
|
+
},
|
|
1832
|
+
{ role: "compiler", revisionRef: compilerRevisionRef },
|
|
643
1833
|
];
|
|
644
1834
|
const compileNavigationRuntimeBundle = (input) => {
|
|
645
1835
|
const navigation = declarative_control_1.NavigationSchema.parse(input.navigation);
|
|
@@ -648,19 +1838,20 @@ const compileNavigationRuntimeBundle = (input) => {
|
|
|
648
1838
|
const releaseRevisionRef = declarative_control_1.RevisionRefSchema.parse(input.releaseRevisionRef);
|
|
649
1839
|
const compilerRevisionRef = declarative_control_1.RevisionRefSchema.parse(input.compilerRevisionRef);
|
|
650
1840
|
const scope = declarative_control_1.TenantScopeSchema.parse(navigation.tenantScope);
|
|
651
|
-
assertSameTenantScope(release.tenantScope, scope,
|
|
1841
|
+
assertSameTenantScope(release.tenantScope, scope, "release.tenantScope");
|
|
652
1842
|
if (navigation.nodes.length > input.limits.maxNavigationNodes)
|
|
653
|
-
throw new DeclarativeControlCompilationError(
|
|
1843
|
+
throw new DeclarativeControlCompilationError("NAV_NODE_LIMIT_EXCEEDED", "navigation.nodes", "Navigation exceeds the caller-approved node limit.");
|
|
654
1844
|
if (navigationDepth(navigation) > input.limits.maxNavigationDepth)
|
|
655
|
-
throw new DeclarativeControlCompilationError(
|
|
656
|
-
assertRevisionIdentity(navigationRevisionRef,
|
|
657
|
-
assertRevisionIdentity(release.navigationRevisionRef,
|
|
1845
|
+
throw new DeclarativeControlCompilationError("NAV_DEPTH_EXCEEDED", "navigation.nodes", "Navigation exceeds the caller-approved depth limit.");
|
|
1846
|
+
assertRevisionIdentity(navigationRevisionRef, "navigation", navigation.navigationId, scope, "navigationRevisionRef");
|
|
1847
|
+
assertRevisionIdentity(release.navigationRevisionRef, "navigation", navigation.navigationId, scope, "release.navigationRevisionRef");
|
|
658
1848
|
if (!(0, exports.sameRevisionRef)(navigationRevisionRef, release.navigationRevisionRef))
|
|
659
|
-
throw new DeclarativeControlCompilationError(
|
|
660
|
-
assertRevisionIdentity(releaseRevisionRef,
|
|
661
|
-
if (release.target.surface !==
|
|
662
|
-
|
|
663
|
-
|
|
1849
|
+
throw new DeclarativeControlCompilationError("REVISION_MISMATCH", "release.navigationRevisionRef", "Release does not pin the supplied Navigation revision.");
|
|
1850
|
+
assertRevisionIdentity(releaseRevisionRef, "navigation-release", release.releaseSlotId, scope, "releaseRevisionRef");
|
|
1851
|
+
if (release.target.surface !==
|
|
1852
|
+
navigation.supportedSurfaces.find((surface) => surface === release.target.surface) ||
|
|
1853
|
+
!navigation.placements.includes(release.target.placement)) {
|
|
1854
|
+
throw new DeclarativeControlCompilationError("REVISION_MISMATCH", "release.target", "Navigation release target is not declared by the Navigation record.");
|
|
664
1855
|
}
|
|
665
1856
|
requireReleaseReady(release);
|
|
666
1857
|
requireDependencies(release.dependencySnapshot, navigationDependencies(navigation, release, compilerRevisionRef));
|
|
@@ -672,49 +1863,70 @@ const compileNavigationRuntimeBundle = (input) => {
|
|
|
672
1863
|
surface: declarative_control_1.ProductSurfaceSchema.parse(registration.surface),
|
|
673
1864
|
accessPolicy: declarative_control_1.AccessPolicySchema.parse(registration.accessPolicy),
|
|
674
1865
|
};
|
|
675
|
-
if (registration.kind ===
|
|
1866
|
+
if (registration.kind === "route") {
|
|
676
1867
|
const routeClaim = declarative_control_1.CompiledRouteClaimSchema.parse(registration.routeClaim);
|
|
677
1868
|
if (routeClaim.surface !== common.surface) {
|
|
678
|
-
throw new DeclarativeControlCompilationError(
|
|
1869
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `targetRegistry[${index}].routeClaim.surface`, "Navigation route claim must match its registered surface.");
|
|
679
1870
|
}
|
|
680
1871
|
return {
|
|
681
1872
|
...common,
|
|
682
|
-
kind:
|
|
683
|
-
releaseRevisionRef: registration.releaseRevisionRef
|
|
1873
|
+
kind: "route",
|
|
1874
|
+
releaseRevisionRef: registration.releaseRevisionRef
|
|
1875
|
+
? declarative_control_1.RevisionRefSchema.parse(registration.releaseRevisionRef)
|
|
1876
|
+
: undefined,
|
|
684
1877
|
routeClaim,
|
|
685
1878
|
};
|
|
686
1879
|
}
|
|
687
|
-
if (registration.kind ===
|
|
1880
|
+
if (registration.kind === "registered-menu-action") {
|
|
688
1881
|
const sourceCatalogRevisionRef = declarative_control_1.RevisionRefSchema.parse(registration.sourceCatalogRevisionRef);
|
|
689
|
-
if (sourceCatalogRevisionRef.kind !==
|
|
690
|
-
|
|
1882
|
+
if (sourceCatalogRevisionRef.kind !== "application-menu-catalog" ||
|
|
1883
|
+
registration.targetRevisionRef.kind !== "menu-action") {
|
|
1884
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", "targetRegistry", "Registered Menu Actions require exact menu-action and application-menu-catalog revisions.");
|
|
691
1885
|
}
|
|
692
1886
|
if (registration.inputSchemaRef && !registration.validateInput) {
|
|
693
|
-
throw new DeclarativeControlCompilationError(
|
|
1887
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", "targetRegistry", "Registered Menu Actions with input schemas require the active catalog validator.");
|
|
694
1888
|
}
|
|
695
|
-
return {
|
|
1889
|
+
return {
|
|
1890
|
+
...common,
|
|
1891
|
+
kind: "registered-menu-action",
|
|
1892
|
+
applicationId: registration.applicationId,
|
|
1893
|
+
actionRef: registration.actionRef,
|
|
1894
|
+
inputSchemaRef: registration.inputSchemaRef,
|
|
1895
|
+
validateInput: registration.validateInput,
|
|
1896
|
+
sourceCatalogRevisionRef,
|
|
1897
|
+
};
|
|
696
1898
|
}
|
|
697
|
-
if (registration.targetRevisionRef.kind !==
|
|
698
|
-
throw new DeclarativeControlCompilationError(
|
|
1899
|
+
if (registration.targetRevisionRef.kind !== "domain-command") {
|
|
1900
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", "targetRegistry", "Governed DomainCommand actions require an exact domain-command revision.");
|
|
699
1901
|
}
|
|
700
1902
|
const inputSchemaRevisionRef = declarative_control_1.RevisionRefSchema.parse(registration.inputSchemaRevisionRef);
|
|
701
1903
|
const resultSchemaRevisionRef = registration.resultSchemaRevisionRef
|
|
702
1904
|
? declarative_control_1.RevisionRefSchema.parse(registration.resultSchemaRevisionRef)
|
|
703
1905
|
: undefined;
|
|
704
|
-
if (inputSchemaRevisionRef.kind !==
|
|
705
|
-
|
|
1906
|
+
if (inputSchemaRevisionRef.kind !== "schema" ||
|
|
1907
|
+
(resultSchemaRevisionRef && resultSchemaRevisionRef.kind !== "schema")) {
|
|
1908
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", "targetRegistry", "Governed DomainCommand actions require exact input and output Schema revisions.");
|
|
706
1909
|
}
|
|
707
|
-
return {
|
|
1910
|
+
return {
|
|
1911
|
+
...common,
|
|
1912
|
+
kind: "governed-domain-command",
|
|
1913
|
+
inputSchemaRevisionRef,
|
|
1914
|
+
resultSchemaRevisionRef,
|
|
1915
|
+
};
|
|
708
1916
|
});
|
|
709
|
-
assertUniqueRegistryKeys(targetRegistry.map((registration) => (0, exports.stableRefKey)(registration.stableTargetRef)),
|
|
710
|
-
const targetsByStableRef = new Map(targetRegistry.map((registration) => [
|
|
1917
|
+
assertUniqueRegistryKeys(targetRegistry.map((registration) => (0, exports.stableRefKey)(registration.stableTargetRef)), "targetRegistry");
|
|
1918
|
+
const targetsByStableRef = new Map(targetRegistry.map((registration) => [
|
|
1919
|
+
(0, exports.stableRefKey)(registration.stableTargetRef),
|
|
1920
|
+
registration,
|
|
1921
|
+
]));
|
|
711
1922
|
const releasedByNodeId = new Map(release.resolvedTargets.map((target) => [target.nodeId, target]));
|
|
712
1923
|
const unavailableByNodeId = new Map((release.unavailableTargets ?? []).map((target) => [target.nodeId, target]));
|
|
713
1924
|
const nodesById = new Map(navigation.nodes.map((node) => [node.nodeId, node]));
|
|
714
1925
|
for (const target of release.unavailableTargets ?? []) {
|
|
715
1926
|
const node = nodesById.get(target.nodeId);
|
|
716
|
-
if (node?.kind !==
|
|
717
|
-
|
|
1927
|
+
if (node?.kind !== "target" ||
|
|
1928
|
+
!(0, exports.sameStableRef)(node.targetRef, target.stableTargetRef)) {
|
|
1929
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", "release.unavailableTargets", "An unavailable target must identify its source Page node exactly.");
|
|
718
1930
|
}
|
|
719
1931
|
}
|
|
720
1932
|
const nodes = orderedNavigationNodes(navigation).map((node, index) => {
|
|
@@ -722,22 +1934,26 @@ const compileNavigationRuntimeBundle = (input) => {
|
|
|
722
1934
|
let parentId = node.parentNodeId;
|
|
723
1935
|
while (parentId) {
|
|
724
1936
|
const parent = nodesById.get(parentId);
|
|
725
|
-
if (parent?.kind ===
|
|
1937
|
+
if (parent?.kind === "group")
|
|
726
1938
|
ancestors.unshift(parent.audience);
|
|
727
1939
|
parentId = parent?.parentNodeId ?? null;
|
|
728
1940
|
}
|
|
729
|
-
if (node.kind !==
|
|
1941
|
+
if (node.kind !== "target")
|
|
730
1942
|
return { ...node, ancestorAccessPolicies: ancestors };
|
|
731
|
-
if (![
|
|
732
|
-
throw new DeclarativeControlCompilationError(
|
|
1943
|
+
if (!["page", "workbench", "menu-action", "domain-command"].includes(node.targetRef.kind)) {
|
|
1944
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", `nodes[${index}].targetRef.kind`, "Navigation targets may reference only Page, Workbench, registered Menu Action, or DomainCommand identities.");
|
|
733
1945
|
}
|
|
734
1946
|
const registration = targetsByStableRef.get((0, exports.stableRefKey)(node.targetRef));
|
|
735
1947
|
const released = releasedByNodeId.get(node.nodeId);
|
|
736
1948
|
const unavailable = unavailableByNodeId.get(node.nodeId);
|
|
737
|
-
if (!registration &&
|
|
1949
|
+
if (!registration &&
|
|
1950
|
+
unavailable &&
|
|
1951
|
+
node.targetRef.kind === "page" &&
|
|
1952
|
+
(0, exports.sameStableRef)(unavailable.stableTargetRef, node.targetRef)) {
|
|
738
1953
|
assertTenantCompatible(node.targetRef, scope, `nodes[${index}].targetRef`);
|
|
739
|
-
if (unavailable.accessPolicy &&
|
|
740
|
-
|
|
1954
|
+
if (unavailable.accessPolicy &&
|
|
1955
|
+
!(0, exports.accessPolicyChainImplies)([...ancestors, node.audience], unavailable.accessPolicy)) {
|
|
1956
|
+
throw new DeclarativeControlCompilationError("AUDIENCE_WIDER_THAN_TARGET", `nodes[${index}].audience`, "Effective Navigation audience is wider than the last verified target audience.");
|
|
741
1957
|
}
|
|
742
1958
|
return {
|
|
743
1959
|
...node,
|
|
@@ -745,80 +1961,100 @@ const compileNavigationRuntimeBundle = (input) => {
|
|
|
745
1961
|
parameterMapping: {},
|
|
746
1962
|
ancestorAccessPolicies: ancestors,
|
|
747
1963
|
resolvedTarget: {
|
|
748
|
-
kind:
|
|
1964
|
+
kind: "unavailable",
|
|
749
1965
|
nodeId: node.nodeId,
|
|
750
1966
|
stableTargetRef: node.targetRef,
|
|
751
1967
|
accessPolicy: unavailable.accessPolicy,
|
|
752
1968
|
},
|
|
753
1969
|
};
|
|
754
1970
|
}
|
|
755
|
-
if (!registration ||
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
1971
|
+
if (!registration ||
|
|
1972
|
+
!released ||
|
|
1973
|
+
!(0, exports.sameStableRef)(registration.stableTargetRef, stableFromRevision(registration.targetRevisionRef)) ||
|
|
1974
|
+
!(0, exports.sameStableRef)(registration.stableTargetRef, released.stableTargetRef) ||
|
|
1975
|
+
!(0, exports.sameRevisionRef)(registration.targetRevisionRef, released.targetRevisionRef) ||
|
|
1976
|
+
(registration.kind === "route" &&
|
|
1977
|
+
(Boolean(registration.releaseRevisionRef) !==
|
|
1978
|
+
Boolean(released.releaseRevisionRef) ||
|
|
1979
|
+
(registration.releaseRevisionRef &&
|
|
1980
|
+
released.releaseRevisionRef &&
|
|
1981
|
+
!(0, exports.sameRevisionRef)(registration.releaseRevisionRef, released.releaseRevisionRef))))) {
|
|
1982
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", `nodes[${index}].targetRef`, "Navigation target is not resolved to the exact active release recorded by NavigationRelease.");
|
|
762
1983
|
}
|
|
763
1984
|
if (registration.surface !== release.target.surface) {
|
|
764
|
-
throw new DeclarativeControlCompilationError(
|
|
1985
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", `nodes[${index}].targetRef`, "Navigation target is active on another surface.");
|
|
765
1986
|
}
|
|
766
1987
|
assertTenantCompatible(registration.targetRevisionRef, scope, `nodes[${index}].targetRef`);
|
|
767
1988
|
if (!(0, exports.accessPolicyChainImplies)([...ancestors, node.audience], registration.accessPolicy)) {
|
|
768
|
-
throw new DeclarativeControlCompilationError(
|
|
1989
|
+
throw new DeclarativeControlCompilationError("AUDIENCE_WIDER_THAN_TARGET", `nodes[${index}].audience`, "Effective Navigation audience is wider than the target audience.");
|
|
769
1990
|
}
|
|
770
1991
|
const mappedInput = Object.fromEntries(Object.entries(node.parameterMapping).map(([name, source]) => {
|
|
771
|
-
if (source.kind !==
|
|
772
|
-
throw new DeclarativeControlCompilationError(
|
|
1992
|
+
if (source.kind !== "constant") {
|
|
1993
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", `nodes[${index}].parameterMapping.${name}`, "Navigation actions accept only publication-time constant input.");
|
|
773
1994
|
}
|
|
774
1995
|
return [name, source.value];
|
|
775
1996
|
}));
|
|
776
1997
|
let resolvedTarget;
|
|
777
|
-
if (registration.kind ===
|
|
778
|
-
resolvedTarget = {
|
|
1998
|
+
if (registration.kind === "route") {
|
|
1999
|
+
resolvedTarget = {
|
|
2000
|
+
...released,
|
|
2001
|
+
kind: "route",
|
|
2002
|
+
accessPolicy: registration.accessPolicy,
|
|
2003
|
+
routeClaim: registration.routeClaim,
|
|
2004
|
+
};
|
|
779
2005
|
}
|
|
780
|
-
else if (registration.kind ===
|
|
2006
|
+
else if (registration.kind === "registered-menu-action") {
|
|
781
2007
|
if (!registration.inputSchemaRef && Object.keys(mappedInput).length > 0) {
|
|
782
|
-
throw new DeclarativeControlCompilationError(
|
|
2008
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", `nodes[${index}].parameterMapping`, "The registered Menu Action does not accept input.");
|
|
783
2009
|
}
|
|
784
2010
|
let input;
|
|
785
2011
|
try {
|
|
786
|
-
input = common_1.JsonObjectSchema.parse(registration.validateInput
|
|
2012
|
+
input = common_1.JsonObjectSchema.parse(registration.validateInput
|
|
2013
|
+
? registration.validateInput(mappedInput)
|
|
2014
|
+
: mappedInput);
|
|
787
2015
|
}
|
|
788
2016
|
catch (error) {
|
|
789
|
-
throw new DeclarativeControlCompilationError(
|
|
2017
|
+
throw new DeclarativeControlCompilationError("NAV_ACTION_INPUT_INVALID", `nodes[${index}].parameterMapping`, `Registered Menu Action input failed its active catalog schema: ${error instanceof Error ? error.message : String(error)}`);
|
|
790
2018
|
}
|
|
791
|
-
const sourceDependency = release.dependencySnapshot.find((dependency) => dependency.role ===
|
|
2019
|
+
const sourceDependency = release.dependencySnapshot.find((dependency) => dependency.role === "application-menu-catalog" &&
|
|
2020
|
+
(0, exports.sameRevisionRef)(dependency.revisionRef, registration.sourceCatalogRevisionRef));
|
|
792
2021
|
if (!sourceDependency)
|
|
793
|
-
throw new DeclarativeControlCompilationError(
|
|
2022
|
+
throw new DeclarativeControlCompilationError("DEPENDENCY_MISSING", `nodes[${index}].targetRef`, "Registered Menu Action source catalog is not pinned by NavigationRelease.");
|
|
794
2023
|
resolvedTarget = {
|
|
795
2024
|
...released,
|
|
796
|
-
kind:
|
|
2025
|
+
kind: "registered-menu-action",
|
|
797
2026
|
accessPolicy: registration.accessPolicy,
|
|
798
|
-
execution:
|
|
2027
|
+
execution: "client",
|
|
799
2028
|
applicationId: registration.applicationId,
|
|
800
2029
|
actionRef: registration.actionRef,
|
|
801
|
-
...(registration.inputSchemaRef
|
|
2030
|
+
...(registration.inputSchemaRef
|
|
2031
|
+
? { inputSchemaRef: registration.inputSchemaRef }
|
|
2032
|
+
: {}),
|
|
802
2033
|
input,
|
|
803
2034
|
sourceCatalogRevisionRef: registration.sourceCatalogRevisionRef,
|
|
804
2035
|
};
|
|
805
2036
|
}
|
|
806
2037
|
else {
|
|
807
|
-
const inputDependency = release.dependencySnapshot.find((dependency) => dependency.role ===
|
|
2038
|
+
const inputDependency = release.dependencySnapshot.find((dependency) => dependency.role === "schema" &&
|
|
2039
|
+
(0, exports.sameRevisionRef)(dependency.revisionRef, registration.inputSchemaRevisionRef));
|
|
808
2040
|
const resultDependency = registration.resultSchemaRevisionRef
|
|
809
|
-
? release.dependencySnapshot.find((dependency) => dependency.role ===
|
|
2041
|
+
? release.dependencySnapshot.find((dependency) => dependency.role === "schema" &&
|
|
2042
|
+
(0, exports.sameRevisionRef)(dependency.revisionRef, registration.resultSchemaRevisionRef))
|
|
810
2043
|
: undefined;
|
|
811
|
-
if (!inputDependency ||
|
|
812
|
-
|
|
2044
|
+
if (!inputDependency ||
|
|
2045
|
+
(registration.resultSchemaRevisionRef && !resultDependency)) {
|
|
2046
|
+
throw new DeclarativeControlCompilationError("DEPENDENCY_MISSING", `nodes[${index}].targetRef`, "Governed DomainCommand schemas are not pinned by NavigationRelease.");
|
|
813
2047
|
}
|
|
814
2048
|
resolvedTarget = {
|
|
815
2049
|
...released,
|
|
816
|
-
kind:
|
|
2050
|
+
kind: "governed-domain-command",
|
|
817
2051
|
accessPolicy: registration.accessPolicy,
|
|
818
|
-
execution:
|
|
2052
|
+
execution: "server",
|
|
819
2053
|
commandRevisionRef: registration.targetRevisionRef,
|
|
820
2054
|
inputSchemaRevisionRef: registration.inputSchemaRevisionRef,
|
|
821
|
-
...(registration.resultSchemaRevisionRef
|
|
2055
|
+
...(registration.resultSchemaRevisionRef
|
|
2056
|
+
? { resultSchemaRevisionRef: registration.resultSchemaRevisionRef }
|
|
2057
|
+
: {}),
|
|
822
2058
|
input: common_1.JsonObjectSchema.parse(mappedInput),
|
|
823
2059
|
};
|
|
824
2060
|
}
|
|
@@ -829,7 +2065,7 @@ const compileNavigationRuntimeBundle = (input) => {
|
|
|
829
2065
|
};
|
|
830
2066
|
});
|
|
831
2067
|
const unsigned = {
|
|
832
|
-
contract:
|
|
2068
|
+
contract: "NavigationRuntimeBundle",
|
|
833
2069
|
schemaVersion: 1,
|
|
834
2070
|
tenantScope: scope,
|
|
835
2071
|
releaseRevisionRef,
|
|
@@ -846,56 +2082,65 @@ const compileNavigationRuntimeBundle = (input) => {
|
|
|
846
2082
|
performanceBudgetRef: navigation.performanceBudgetRef,
|
|
847
2083
|
observationPolicyRevisionRef: navigation.observationPolicyRevisionRef,
|
|
848
2084
|
};
|
|
849
|
-
return deepFreeze(declarative_control_1.NavigationRuntimeBundleSchema.parse({
|
|
2085
|
+
return deepFreeze(declarative_control_1.NavigationRuntimeBundleSchema.parse({
|
|
2086
|
+
...unsigned,
|
|
2087
|
+
contentHash: (0, exports.canonicalContentHash)(unsigned),
|
|
2088
|
+
}));
|
|
850
2089
|
};
|
|
851
2090
|
exports.compileNavigationRuntimeBundle = compileNavigationRuntimeBundle;
|
|
852
2091
|
const validateWorkbenchRuntimeSet = (bundleInputs) => {
|
|
853
2092
|
const bundles = bundleInputs.map((bundle) => declarative_control_1.WorkbenchRuntimeBundleSchema.parse(bundle));
|
|
854
2093
|
if (bundles.length === 0) {
|
|
855
|
-
throw new DeclarativeControlCompilationError(
|
|
2094
|
+
throw new DeclarativeControlCompilationError("WORKBENCH_RELEASE_SET_INVALID", "bundles", "An active Workbench release set must retain at least one Workbench.");
|
|
856
2095
|
}
|
|
857
2096
|
const first = bundles[0];
|
|
858
2097
|
const seenIds = new Set();
|
|
859
2098
|
const seenOrders = new Set();
|
|
860
2099
|
let defaultCount = 0;
|
|
861
2100
|
bundles.forEach((bundle, index) => {
|
|
862
|
-
if (scopeKey(bundle.tenantScope) !== scopeKey(first.tenantScope)
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
throw new DeclarativeControlCompilationError(
|
|
2101
|
+
if (scopeKey(bundle.tenantScope) !== scopeKey(first.tenantScope) ||
|
|
2102
|
+
bundle.surface !== first.surface ||
|
|
2103
|
+
!(0, exports.sameStableRef)(bundle.target.environmentRef, first.target.environmentRef)) {
|
|
2104
|
+
throw new DeclarativeControlCompilationError("WORKBENCH_RELEASE_SET_INVALID", `bundles[${index}]`, "Workbench release set entries must share tenant, environment, and surface.");
|
|
866
2105
|
}
|
|
867
2106
|
if (seenIds.has(bundle.workbenchId))
|
|
868
|
-
throw new DeclarativeControlCompilationError(
|
|
2107
|
+
throw new DeclarativeControlCompilationError("WORKBENCH_RELEASE_SET_INVALID", `bundles[${index}].workbenchId`, "Workbench identities must be unique in an active release set.");
|
|
869
2108
|
if (seenOrders.has(bundle.target.order))
|
|
870
|
-
throw new DeclarativeControlCompilationError(
|
|
2109
|
+
throw new DeclarativeControlCompilationError("WORKBENCH_RELEASE_SET_INVALID", `bundles[${index}].target.order`, "Workbench order values must be unique in an active release set.");
|
|
871
2110
|
seenIds.add(bundle.workbenchId);
|
|
872
2111
|
seenOrders.add(bundle.target.order);
|
|
873
2112
|
if (bundle.target.isDefaultCandidate)
|
|
874
2113
|
defaultCount += 1;
|
|
875
2114
|
});
|
|
876
2115
|
if (defaultCount > 1)
|
|
877
|
-
throw new DeclarativeControlCompilationError(
|
|
878
|
-
return Object.freeze([...bundles].sort((left, right) => left.target.order - right.target.order ||
|
|
2116
|
+
throw new DeclarativeControlCompilationError("WORKBENCH_RELEASE_SET_INVALID", "bundles", "An active Workbench release set may have at most one default candidate.");
|
|
2117
|
+
return Object.freeze([...bundles].sort((left, right) => left.target.order - right.target.order ||
|
|
2118
|
+
left.workbenchId.localeCompare(right.workbenchId)));
|
|
879
2119
|
};
|
|
880
2120
|
exports.validateWorkbenchRuntimeSet = validateWorkbenchRuntimeSet;
|
|
881
2121
|
const resolveWorkbenchEntry = (input) => {
|
|
882
2122
|
if (input.requestedWorkbenchId) {
|
|
883
2123
|
const requested = input.bundles.find((bundle) => bundle.workbenchId === input.requestedWorkbenchId);
|
|
884
2124
|
if (!requested)
|
|
885
|
-
return { status:
|
|
2125
|
+
return { status: "not-found", workbenchId: input.requestedWorkbenchId };
|
|
886
2126
|
if (!input.isAllowed(requested))
|
|
887
|
-
return { status:
|
|
888
|
-
return { status:
|
|
2127
|
+
return { status: "forbidden", workbenchId: input.requestedWorkbenchId };
|
|
2128
|
+
return { status: "resolved", bundle: requested, source: "explicit" };
|
|
889
2129
|
}
|
|
890
2130
|
const allowed = input.bundles
|
|
891
2131
|
.filter(input.isAllowed)
|
|
892
|
-
.sort((left, right) => left.target.order - right.target.order ||
|
|
2132
|
+
.sort((left, right) => left.target.order - right.target.order ||
|
|
2133
|
+
left.workbenchId.localeCompare(right.workbenchId));
|
|
893
2134
|
const defaultBundle = allowed.find((bundle) => bundle.target.isDefaultCandidate);
|
|
894
2135
|
if (defaultBundle)
|
|
895
|
-
return { status:
|
|
2136
|
+
return { status: "resolved", bundle: defaultBundle, source: "default" };
|
|
896
2137
|
if (allowed[0])
|
|
897
|
-
return {
|
|
898
|
-
|
|
2138
|
+
return {
|
|
2139
|
+
status: "resolved",
|
|
2140
|
+
bundle: allowed[0],
|
|
2141
|
+
source: "first-accessible",
|
|
2142
|
+
};
|
|
2143
|
+
return { status: "forbidden", workbenchId: "" };
|
|
899
2144
|
};
|
|
900
2145
|
exports.resolveWorkbenchEntry = resolveWorkbenchEntry;
|
|
901
2146
|
//# sourceMappingURL=declarative-control-compiler.js.map
|