@inf-monkeys-tech/monkeys 1.1.0 → 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/declarative-control-http.d.ts +55903 -73794
- package/lib/contracts/declarative-control-http.d.ts.map +1 -1
- package/lib/contracts/declarative-control-http.js +703 -544
- package/lib/contracts/declarative-control-http.js.map +1 -1
- package/lib/contracts/declarative-control.d.ts +1623 -3
- package/lib/contracts/declarative-control.d.ts.map +1 -1
- package/lib/contracts/declarative-control.js +2107 -1136
- package/lib/contracts/declarative-control.js.map +1 -1
- package/lib/contracts/render.d.ts +591 -1
- package/lib/contracts/render.d.ts.map +1 -1
- package/lib/contracts/render.js +331 -134
- package/lib/contracts/render.js.map +1 -1
- package/lib/contracts/tenant.d.ts +3 -3
- package/lib/contracts/theme.d.ts +1 -1
- package/lib/esm/contracts/index.mjs +1872 -409
- package/lib/esm/contracts/index.mjs.map +1 -1
- package/lib/esm/index.mjs +3028 -672
- package/lib/esm/index.mjs.map +1 -1
- package/lib/esm/runtime/index.mjs +3016 -670
- package/lib/esm/runtime/index.mjs.map +1 -1
- package/lib/esm/schemas/index.mjs +1866 -412
- package/lib/esm/schemas/index.mjs.map +1 -1
- package/lib/json-schema/overlay-node.schema.json +602 -0
- package/lib/json-schema/page-runtime-bundle.schema.json +602 -0
- package/lib/json-schema/page.schema.json +5619 -11743
- package/lib/json-schema/render-node.schema.json +602 -0
- package/lib/json-schema/render-tree.schema.json +602 -0
- package/lib/runtime/declarative-control-compiler.d.ts +52 -27
- package/lib/runtime/declarative-control-compiler.d.ts.map +1 -1
- package/lib/runtime/declarative-control-compiler.js +858 -366
- package/lib/runtime/declarative-control-compiler.js.map +1 -1
- package/lib/schemas/index.d.ts +787 -2335
- package/lib/schemas/index.d.ts.map +1 -1
- package/lib/schemas/index.js +100 -99
- package/lib/schemas/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,22 +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.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;
|
|
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
5
|
const declarative_control_http_1 = require("../contracts/declarative-control-http");
|
|
6
6
|
const render_tree_compiler_1 = require("./render-tree-compiler");
|
|
7
|
+
const page_expression_1 = require("../contracts/page-expression");
|
|
7
8
|
const declarative_control_1 = require("../contracts/declarative-control");
|
|
8
9
|
class DeclarativeControlCompilationError extends Error {
|
|
9
10
|
constructor(code, path, message) {
|
|
10
11
|
super(`${code} at ${path}: ${message}`);
|
|
11
12
|
this.code = code;
|
|
12
13
|
this.path = path;
|
|
13
|
-
this.name =
|
|
14
|
+
this.name = "DeclarativeControlCompilationError";
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
exports.DeclarativeControlCompilationError = DeclarativeControlCompilationError;
|
|
17
18
|
const scopedIdentityKey = (reference) => `${reference.kind}:${reference.id}:${reference.ownerRepo}`;
|
|
18
|
-
const scopeKey = (scope) => `${scopedIdentityKey(scope.tenantRef)}:${scopedIdentityKey(scope.dataSpaceRef)}:${scope.teamRef ? scopedIdentityKey(scope.teamRef) :
|
|
19
|
-
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}`;
|
|
20
21
|
exports.stableRefKey = stableRefKey;
|
|
21
22
|
const revisionRefKey = (reference) => `${(0, exports.stableRefKey)(reference)}@${reference.revision}:v${reference.schemaVersion}:${reference.contentHash.toLowerCase()}`;
|
|
22
23
|
exports.revisionRefKey = revisionRefKey;
|
|
@@ -28,35 +29,41 @@ const assertUniqueRegistryKeys = (keys, path) => {
|
|
|
28
29
|
const seen = new Set();
|
|
29
30
|
keys.forEach((key, index) => {
|
|
30
31
|
if (seen.has(key)) {
|
|
31
|
-
throw new DeclarativeControlCompilationError(
|
|
32
|
+
throw new DeclarativeControlCompilationError("REGISTRY_CONFLICT", `${path}[${index}]`, `Duplicate registry identity: ${key}`);
|
|
32
33
|
}
|
|
33
34
|
seen.add(key);
|
|
34
35
|
});
|
|
35
36
|
};
|
|
36
37
|
const stableSerialize = (value) => {
|
|
37
38
|
if (value === undefined)
|
|
38
|
-
return
|
|
39
|
-
if (value === null || typeof value !==
|
|
40
|
-
return JSON.stringify(value) ??
|
|
39
|
+
return "null";
|
|
40
|
+
if (value === null || typeof value !== "object")
|
|
41
|
+
return JSON.stringify(value) ?? "null";
|
|
41
42
|
if (Array.isArray(value))
|
|
42
|
-
return `[${value.map((item) => stableSerialize(item)).join(
|
|
43
|
+
return `[${value.map((item) => stableSerialize(item)).join(",")}]`;
|
|
43
44
|
const record = value;
|
|
44
45
|
return `{${Object.keys(record)
|
|
45
46
|
.filter((key) => record[key] !== undefined)
|
|
46
47
|
.sort()
|
|
47
48
|
.map((key) => `${JSON.stringify(key)}:${stableSerialize(record[key])}`)
|
|
48
|
-
.join(
|
|
49
|
+
.join(",")}}`;
|
|
49
50
|
};
|
|
50
|
-
const SHA256_INITIAL = [
|
|
51
|
+
const SHA256_INITIAL = [
|
|
52
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,
|
|
53
|
+
0x1f83d9ab, 0x5be0cd19,
|
|
54
|
+
];
|
|
51
55
|
const SHA256_ROUND = [
|
|
52
|
-
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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,
|
|
60
67
|
];
|
|
61
68
|
const rotateRight = (value, amount) => (value >>> amount) | (value << (32 - amount));
|
|
62
69
|
const sha256Hex = (value) => {
|
|
@@ -77,14 +84,19 @@ const sha256Hex = (value) => {
|
|
|
77
84
|
for (let index = 0; index < 16; index += 1) {
|
|
78
85
|
const start = offset + index * 4;
|
|
79
86
|
words[index] =
|
|
80
|
-
((source[start] << 24) |
|
|
87
|
+
((source[start] << 24) |
|
|
88
|
+
(source[start + 1] << 16) |
|
|
89
|
+
(source[start + 2] << 8) |
|
|
90
|
+
source[start + 3]) >>>
|
|
91
|
+
0;
|
|
81
92
|
}
|
|
82
93
|
for (let index = 16; index < 64; index += 1) {
|
|
83
94
|
const previous = words[index - 15];
|
|
84
95
|
const recent = words[index - 2];
|
|
85
96
|
const small0 = rotateRight(previous, 7) ^ rotateRight(previous, 18) ^ (previous >>> 3);
|
|
86
97
|
const small1 = rotateRight(recent, 17) ^ rotateRight(recent, 19) ^ (recent >>> 10);
|
|
87
|
-
words[index] =
|
|
98
|
+
words[index] =
|
|
99
|
+
(words[index - 16] + small0 + words[index - 7] + small1) >>> 0;
|
|
88
100
|
}
|
|
89
101
|
let [a, b, c, d, e, f, g, h] = hash;
|
|
90
102
|
for (let index = 0; index < 64; index += 1) {
|
|
@@ -112,7 +124,7 @@ const sha256Hex = (value) => {
|
|
|
112
124
|
hash[6] = (hash[6] + g) >>> 0;
|
|
113
125
|
hash[7] = (hash[7] + h) >>> 0;
|
|
114
126
|
}
|
|
115
|
-
return hash.map((part) => part.toString(16).padStart(8,
|
|
127
|
+
return hash.map((part) => part.toString(16).padStart(8, "0")).join("");
|
|
116
128
|
};
|
|
117
129
|
const canonicalContentHash = (value) => sha256Hex(stableSerialize(value));
|
|
118
130
|
exports.canonicalContentHash = canonicalContentHash;
|
|
@@ -126,7 +138,9 @@ exports.declarativeRuntimeProjectionId = declarativeRuntimeProjectionId;
|
|
|
126
138
|
/** Names the rebuildable route-owner index for one tenant environment surface. */
|
|
127
139
|
const declarativeRouteOwnerIndexProjectionId = (tenantScope, environmentRef, surface) => `dri:${(0, exports.canonicalContentHash)({
|
|
128
140
|
tenantScope: {
|
|
129
|
-
teamRef: tenantScope.teamRef
|
|
141
|
+
teamRef: tenantScope.teamRef
|
|
142
|
+
? { kind: tenantScope.teamRef.kind, id: tenantScope.teamRef.id }
|
|
143
|
+
: null,
|
|
130
144
|
},
|
|
131
145
|
environmentRef,
|
|
132
146
|
surface,
|
|
@@ -134,7 +148,9 @@ const declarativeRouteOwnerIndexProjectionId = (tenantScope, environmentRef, sur
|
|
|
134
148
|
exports.declarativeRouteOwnerIndexProjectionId = declarativeRouteOwnerIndexProjectionId;
|
|
135
149
|
const declarativeWorkbenchCatalogProjectionId = (tenantScope, environmentRef, surface) => `dwc:${(0, exports.canonicalContentHash)({
|
|
136
150
|
tenantScope: {
|
|
137
|
-
teamRef: tenantScope.teamRef
|
|
151
|
+
teamRef: tenantScope.teamRef
|
|
152
|
+
? { kind: tenantScope.teamRef.kind, id: tenantScope.teamRef.id }
|
|
153
|
+
: null,
|
|
138
154
|
},
|
|
139
155
|
environmentRef,
|
|
140
156
|
surface,
|
|
@@ -144,7 +160,7 @@ const takeoverHashSource = (authorization) => authorization;
|
|
|
144
160
|
const compileLegacyRouteTakeoverAuthorization = (input) => {
|
|
145
161
|
const unsigned = {
|
|
146
162
|
...input,
|
|
147
|
-
contract:
|
|
163
|
+
contract: "LegacyRouteTakeoverAuthorization",
|
|
148
164
|
schemaVersion: 1,
|
|
149
165
|
};
|
|
150
166
|
return deepFreeze(declarative_control_1.LegacyRouteTakeoverAuthorizationSchema.parse({
|
|
@@ -159,7 +175,7 @@ const parseExactLegacyRouteTakeoverAuthorization = (input) => {
|
|
|
159
175
|
const parsed = declarative_control_1.LegacyRouteTakeoverAuthorizationSchema.parse(input);
|
|
160
176
|
const { contentHash, ...unsigned } = parsed;
|
|
161
177
|
if (contentHash !== (0, exports.legacyRouteTakeoverAuthorizationHash)(unsigned)) {
|
|
162
|
-
throw new DeclarativeControlCompilationError(
|
|
178
|
+
throw new DeclarativeControlCompilationError("ROUTE_TAKEOVER_INVALID", "contentHash", "Legacy route takeover authorization content hash is not exact.");
|
|
163
179
|
}
|
|
164
180
|
return deepFreeze(parsed);
|
|
165
181
|
};
|
|
@@ -179,7 +195,7 @@ const compileDeclarativeRouteOwnerIndex = (input) => {
|
|
|
179
195
|
return leftKey.localeCompare(rightKey);
|
|
180
196
|
});
|
|
181
197
|
const unsigned = {
|
|
182
|
-
contract:
|
|
198
|
+
contract: "DeclarativeRouteOwnerIndex",
|
|
183
199
|
schemaVersion: 1,
|
|
184
200
|
tenantScope: declarative_control_1.TenantScopeSchema.parse(input.tenantScope),
|
|
185
201
|
environmentRef: declarative_control_1.StableRefSchema.parse(input.environmentRef),
|
|
@@ -197,9 +213,10 @@ exports.compileDeclarativeRouteOwnerIndex = compileDeclarativeRouteOwnerIndex;
|
|
|
197
213
|
const compileDeclarativeWorkbenchCatalog = (input) => {
|
|
198
214
|
const entries = input.entries
|
|
199
215
|
.map((entry) => ({ ...entry }))
|
|
200
|
-
.sort((left, right) => left.order - right.order ||
|
|
216
|
+
.sort((left, right) => left.order - right.order ||
|
|
217
|
+
left.workbenchId.localeCompare(right.workbenchId));
|
|
201
218
|
const unsigned = {
|
|
202
|
-
contract:
|
|
219
|
+
contract: "DeclarativeWorkbenchCatalog",
|
|
203
220
|
schemaVersion: 1,
|
|
204
221
|
tenantScope: declarative_control_1.TenantScopeSchema.parse(input.tenantScope),
|
|
205
222
|
environmentRef: declarative_control_1.StableRefSchema.parse(input.environmentRef),
|
|
@@ -227,7 +244,7 @@ exports.declarativeProductResourceSourceHash = declarativeProductResourceSourceH
|
|
|
227
244
|
const declarativeProductResourceRevisionHash = (registration) => (0, exports.canonicalContentHash)(registration.document);
|
|
228
245
|
exports.declarativeProductResourceRevisionHash = declarativeProductResourceRevisionHash;
|
|
229
246
|
const designCapabilityCatalogHashSource = (input) => ({
|
|
230
|
-
contract:
|
|
247
|
+
contract: "DesignCapabilityCatalogArtifact",
|
|
231
248
|
schemaVersion: 1,
|
|
232
249
|
sourceRef: input.sourceRef,
|
|
233
250
|
entries: input.entries,
|
|
@@ -243,22 +260,28 @@ const compileDesignCapabilityCatalogArtifact = (input) => {
|
|
|
243
260
|
const { registrationRevisionRef: _registrationRevisionRef, sourceContentHash: _sourceContentHash, ...unsignedRegistration } = registration;
|
|
244
261
|
const registrationHash = (0, exports.canonicalContentHash)(unsignedRegistration);
|
|
245
262
|
if (registration.capabilityRevisionRef.id !== entry.manifest.id ||
|
|
246
|
-
registration.capabilityRevisionRef.ownerRepo !==
|
|
247
|
-
|
|
248
|
-
registration.capabilityRevisionRef.
|
|
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) ||
|
|
249
269
|
registration.providerRevisionRef.id !== entry.provider.providerId ||
|
|
250
270
|
registration.providerRevisionRef.ownerRepo !== entry.provider.ownerRepo ||
|
|
251
|
-
String(registration.providerRevisionRef.revision) !==
|
|
252
|
-
|
|
271
|
+
String(registration.providerRevisionRef.revision) !==
|
|
272
|
+
String(entry.provider.providerVersion) ||
|
|
273
|
+
registration.providerRevisionRef.contentHash !==
|
|
274
|
+
(0, exports.canonicalContentHash)(entry.provider) ||
|
|
253
275
|
registration.sourceContentHash !== registrationHash ||
|
|
254
276
|
registration.registrationRevisionRef.contentHash !== registrationHash) {
|
|
255
|
-
throw new DeclarativeControlCompilationError(
|
|
277
|
+
throw new DeclarativeControlCompilationError("DESIGN_CATALOG_INVALID", `entries[${index}]`, "Design capability manifest, provider and authoring registration are not exact.");
|
|
256
278
|
}
|
|
257
279
|
registration.schemaDocuments.forEach((schema, schemaIndex) => {
|
|
258
|
-
if (schema.schemaRevisionRef.ownerRepo !==
|
|
259
|
-
schema.schemaRevisionRef.visibility !==
|
|
260
|
-
schema.schemaRevisionRef.contentHash !==
|
|
261
|
-
|
|
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.");
|
|
262
285
|
}
|
|
263
286
|
});
|
|
264
287
|
});
|
|
@@ -267,16 +290,16 @@ const compileDesignCapabilityCatalogArtifact = (input) => {
|
|
|
267
290
|
entries,
|
|
268
291
|
});
|
|
269
292
|
const sourceRevisionRef = declarative_control_1.RevisionRefSchema.parse({
|
|
270
|
-
kind:
|
|
293
|
+
kind: "design-capability-catalog",
|
|
271
294
|
id: input.sourceRef,
|
|
272
|
-
ownerRepo:
|
|
273
|
-
visibility:
|
|
295
|
+
ownerRepo: "monkeys-design",
|
|
296
|
+
visibility: "global",
|
|
274
297
|
revision: input.revision,
|
|
275
298
|
schemaVersion: 1,
|
|
276
299
|
contentHash: sourceContentHash,
|
|
277
300
|
});
|
|
278
301
|
return deepFreeze(declarative_control_http_1.DesignCapabilityCatalogArtifactSchema.parse({
|
|
279
|
-
contract:
|
|
302
|
+
contract: "DesignCapabilityCatalogArtifact",
|
|
280
303
|
schemaVersion: 1,
|
|
281
304
|
sourceRef: input.sourceRef,
|
|
282
305
|
sourceRevisionRef,
|
|
@@ -290,15 +313,20 @@ const parseExactProductDeclarativeCapabilityRegistration = (input) => {
|
|
|
290
313
|
const entry = declarative_control_http_1.ProductDeclarativeCapabilityRegistrationSchema.parse(input);
|
|
291
314
|
const registration = entry.declarativeRegistration;
|
|
292
315
|
const { registrationRevisionRef: _registrationRevisionRef, sourceContentHash: _sourceContentHash, ...unsignedRegistration } = registration;
|
|
293
|
-
if (registration.capabilityRevisionRef.contentHash !==
|
|
294
|
-
|
|
295
|
-
registration.
|
|
296
|
-
|
|
297
|
-
|
|
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.");
|
|
298
325
|
}
|
|
299
326
|
registration.schemaDocuments.forEach((schema, schemaIndex) => {
|
|
300
|
-
if (schema.schemaRevisionRef.contentHash !==
|
|
301
|
-
|
|
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.");
|
|
302
330
|
}
|
|
303
331
|
});
|
|
304
332
|
return deepFreeze(entry);
|
|
@@ -313,25 +341,26 @@ const parseExactDesignCapabilityCatalogArtifact = (input) => {
|
|
|
313
341
|
});
|
|
314
342
|
if (parsed.sourceContentHash !== expected.sourceContentHash ||
|
|
315
343
|
!(0, exports.sameRevisionRef)(parsed.sourceRevisionRef, expected.sourceRevisionRef)) {
|
|
316
|
-
throw new DeclarativeControlCompilationError(
|
|
344
|
+
throw new DeclarativeControlCompilationError("DESIGN_CATALOG_INVALID", "sourceRevisionRef", "Design capability catalog artifact provenance or content hash is not exact.");
|
|
317
345
|
}
|
|
318
346
|
return deepFreeze(parsed);
|
|
319
347
|
};
|
|
320
348
|
exports.parseExactDesignCapabilityCatalogArtifact = parseExactDesignCapabilityCatalogArtifact;
|
|
321
349
|
const deepFreeze = (value) => {
|
|
322
|
-
if (value === null || typeof value !==
|
|
350
|
+
if (value === null || typeof value !== "object" || Object.isFrozen(value))
|
|
323
351
|
return value;
|
|
324
352
|
Object.values(value).forEach((child) => deepFreeze(child));
|
|
325
353
|
return Object.freeze(value);
|
|
326
354
|
};
|
|
327
355
|
const assertTenantCompatible = (reference, scope, path) => {
|
|
328
|
-
if (reference.visibility ===
|
|
329
|
-
|
|
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.`);
|
|
330
359
|
}
|
|
331
360
|
};
|
|
332
361
|
const assertSameTenantScope = (candidate, expected, path) => {
|
|
333
362
|
if (scopeKey(candidate) !== scopeKey(expected)) {
|
|
334
|
-
throw new DeclarativeControlCompilationError(
|
|
363
|
+
throw new DeclarativeControlCompilationError("CROSS_TENANT_REFERENCE", path, "Record and release tenant scopes do not match.");
|
|
335
364
|
}
|
|
336
365
|
};
|
|
337
366
|
const stableFromRevision = (reference) => declarative_control_1.StableRefSchema.parse({
|
|
@@ -343,7 +372,7 @@ const stableFromRevision = (reference) => declarative_control_1.StableRefSchema.
|
|
|
343
372
|
});
|
|
344
373
|
const assertRevisionIdentity = (reference, expectedKind, expectedId, scope, path) => {
|
|
345
374
|
if (reference.kind !== expectedKind || reference.id !== expectedId) {
|
|
346
|
-
throw new DeclarativeControlCompilationError(
|
|
375
|
+
throw new DeclarativeControlCompilationError("REVISION_MISMATCH", path, `Expected ${expectedKind}/${expectedId}, received ${reference.kind}/${reference.id}.`);
|
|
347
376
|
}
|
|
348
377
|
assertTenantCompatible(reference, scope, path);
|
|
349
378
|
};
|
|
@@ -353,28 +382,28 @@ const requireDependencies = (snapshot, required) => {
|
|
|
353
382
|
for (const dependency of required) {
|
|
354
383
|
const key = dependencyKey(dependency.role, dependency.revisionRef);
|
|
355
384
|
if (!available.has(key)) {
|
|
356
|
-
throw new DeclarativeControlCompilationError(
|
|
385
|
+
throw new DeclarativeControlCompilationError("DEPENDENCY_MISSING", "dependencySnapshot", `Missing exact ${dependency.role} dependency ${(0, exports.revisionRefKey)(dependency.revisionRef)}.`);
|
|
357
386
|
}
|
|
358
387
|
}
|
|
359
388
|
};
|
|
360
389
|
const assertDependencyScopes = (snapshot, scope) => snapshot.forEach((dependency, index) => assertTenantCompatible(dependency.revisionRef, scope, `dependencySnapshot[${index}].revisionRef`));
|
|
361
390
|
const requireReleaseReady = (release) => {
|
|
362
|
-
if (release.operation ===
|
|
363
|
-
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.");
|
|
364
393
|
}
|
|
365
|
-
if (release.evidence.validation.result !==
|
|
366
|
-
throw new DeclarativeControlCompilationError(
|
|
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.");
|
|
367
396
|
}
|
|
368
397
|
};
|
|
369
398
|
const refSet = (values) => new Set(values.map(exports.stableRefKey));
|
|
370
399
|
const chainRequiresAll = (policies, field) => {
|
|
371
|
-
if (field ===
|
|
400
|
+
if (field === "permissionAllOf") {
|
|
372
401
|
return new Set(policies.flatMap((policy) => [
|
|
373
402
|
...policy.permissionAllOf,
|
|
374
403
|
...(policy.permissionAnyOf.length === 1 ? policy.permissionAnyOf : []),
|
|
375
404
|
]));
|
|
376
405
|
}
|
|
377
|
-
if (field ===
|
|
406
|
+
if (field === "groupAllOf") {
|
|
378
407
|
return refSet(policies.flatMap((policy) => [
|
|
379
408
|
...policy.groupAllOf,
|
|
380
409
|
...(policy.groupAnyOf.length === 1 ? policy.groupAnyOf : []),
|
|
@@ -397,17 +426,17 @@ const accessPolicyChainImplies = (policyInputs, targetInput) => {
|
|
|
397
426
|
const target = declarative_control_1.AccessPolicySchema.parse(targetInput);
|
|
398
427
|
if (target.authenticated && !policies.some((policy) => policy.authenticated))
|
|
399
428
|
return false;
|
|
400
|
-
const permissionAll = chainRequiresAll(policies,
|
|
429
|
+
const permissionAll = chainRequiresAll(policies, "permissionAllOf");
|
|
401
430
|
if (target.permissionAllOf.some((permission) => !permissionAll.has(permission)))
|
|
402
431
|
return false;
|
|
403
432
|
if (!chainImpliesAny(policies, new Set(target.permissionAnyOf), permissionAll, (policy) => policy.permissionAnyOf))
|
|
404
433
|
return false;
|
|
405
|
-
const groupAll = chainRequiresAll(policies,
|
|
434
|
+
const groupAll = chainRequiresAll(policies, "groupAllOf");
|
|
406
435
|
if (target.groupAllOf.some((group) => !groupAll.has((0, exports.stableRefKey)(group))))
|
|
407
436
|
return false;
|
|
408
437
|
if (!chainImpliesAny(policies, refSet(target.groupAnyOf), groupAll, (policy) => policy.groupAnyOf.map(exports.stableRefKey)))
|
|
409
438
|
return false;
|
|
410
|
-
const conditionAll = chainRequiresAll(policies,
|
|
439
|
+
const conditionAll = chainRequiresAll(policies, "conditionAllOf");
|
|
411
440
|
if (target.conditionAllOf.some((condition) => !conditionAll.has((0, exports.revisionRefKey)(condition))))
|
|
412
441
|
return false;
|
|
413
442
|
return true;
|
|
@@ -417,22 +446,26 @@ const normalizePath = (path, routeSpace) => {
|
|
|
417
446
|
let normalized = path;
|
|
418
447
|
if (!routeSpace.caseSensitive) {
|
|
419
448
|
normalized = normalized
|
|
420
|
-
.split(
|
|
421
|
-
.map((segment) =>
|
|
422
|
-
.join(
|
|
449
|
+
.split("/")
|
|
450
|
+
.map((segment) => segment.startsWith(":") ? segment : segment.toLowerCase())
|
|
451
|
+
.join("/");
|
|
423
452
|
}
|
|
424
|
-
if (routeSpace.trailingSlash ===
|
|
425
|
-
normalized = normalized.replace(/\/+$/,
|
|
426
|
-
if (routeSpace.trailingSlash ===
|
|
427
|
-
normalized
|
|
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 += "/";
|
|
428
459
|
return normalized;
|
|
429
460
|
};
|
|
430
461
|
const acceptedTrailingSlashStates = (claim) => {
|
|
431
|
-
if (claim.matcher.trailingSlash ===
|
|
462
|
+
if (claim.matcher.trailingSlash === "remove")
|
|
432
463
|
return new Set([false, true]);
|
|
433
|
-
if (claim.matcher.trailingSlash ===
|
|
464
|
+
if (claim.matcher.trailingSlash === "require")
|
|
434
465
|
return new Set([true]);
|
|
435
|
-
return new Set([
|
|
466
|
+
return new Set([
|
|
467
|
+
claim.normalizedPath.length > 1 && claim.normalizedPath.endsWith("/"),
|
|
468
|
+
]);
|
|
436
469
|
};
|
|
437
470
|
const routeSegmentParameter = (segment) => segment.match(/^[:$]([A-Za-z][A-Za-z0-9_-]*)$/)?.[1];
|
|
438
471
|
const parameterTypeFor = (claim, name) => claim.matcher.parameters.find((parameter) => parameter.name === name)?.type;
|
|
@@ -441,8 +474,8 @@ const compiledRouteClaimsOverlap = (leftInput, rightInput) => {
|
|
|
441
474
|
const right = declarative_control_1.CompiledRouteClaimSchema.parse(rightInput);
|
|
442
475
|
if (![...acceptedTrailingSlashStates(left)].some((state) => acceptedTrailingSlashStates(right).has(state)))
|
|
443
476
|
return false;
|
|
444
|
-
const leftSegments = left.normalizedPath.split(
|
|
445
|
-
const rightSegments = right.normalizedPath.split(
|
|
477
|
+
const leftSegments = left.normalizedPath.split("/").filter(Boolean);
|
|
478
|
+
const rightSegments = right.normalizedPath.split("/").filter(Boolean);
|
|
446
479
|
if (leftSegments.length !== rightSegments.length)
|
|
447
480
|
return false;
|
|
448
481
|
return leftSegments.every((leftSegment, index) => {
|
|
@@ -466,16 +499,22 @@ const compiledRouteClaimsOverlap = (leftInput, rightInput) => {
|
|
|
466
499
|
const rightType = parameterTypeFor(right, rightParameter);
|
|
467
500
|
if (!leftType || !rightType)
|
|
468
501
|
return false;
|
|
469
|
-
return !(leftType ===
|
|
502
|
+
return (!(leftType === "integer" && rightType === "uuid") &&
|
|
503
|
+
!(leftType === "uuid" && rightType === "integer"));
|
|
470
504
|
});
|
|
471
505
|
};
|
|
472
506
|
exports.compiledRouteClaimsOverlap = compiledRouteClaimsOverlap;
|
|
473
507
|
const compileRouteClaims = (input) => {
|
|
474
508
|
const embeddedTakeovers = input.claims.flatMap((claim) => {
|
|
475
|
-
const authorization =
|
|
509
|
+
const authorization = "legacyRouteTakeoverAuthorization" in claim
|
|
510
|
+
? claim.legacyRouteTakeoverAuthorization
|
|
511
|
+
: undefined;
|
|
476
512
|
return authorization ? [authorization] : [];
|
|
477
513
|
});
|
|
478
|
-
const takeoverAuthorizations = [
|
|
514
|
+
const takeoverAuthorizations = [
|
|
515
|
+
...(input.legacyRouteTakeoverAuthorizations ?? []),
|
|
516
|
+
...embeddedTakeovers,
|
|
517
|
+
].map((authorization, index) => {
|
|
479
518
|
try {
|
|
480
519
|
return (0, exports.parseExactLegacyRouteTakeoverAuthorization)(authorization);
|
|
481
520
|
}
|
|
@@ -489,38 +528,48 @@ const compileRouteClaims = (input) => {
|
|
|
489
528
|
const registrations = input.routeSpaces.map((entry, index) => {
|
|
490
529
|
const revisionRef = declarative_control_1.RevisionRefSchema.parse(entry.revisionRef);
|
|
491
530
|
const routeSpace = declarative_control_1.RouteSpaceSchema.parse(entry.routeSpace);
|
|
492
|
-
if (revisionRef.kind !==
|
|
493
|
-
|
|
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.");
|
|
494
534
|
}
|
|
495
535
|
return { revisionRef, routeSpace };
|
|
496
536
|
});
|
|
497
|
-
assertUniqueRegistryKeys(registrations.map((entry) => (0, exports.revisionRefKey)(entry.revisionRef)),
|
|
498
|
-
const routeSpacesByRevision = new Map(registrations.map((entry) => [
|
|
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
|
+
]));
|
|
499
542
|
const compiled = [];
|
|
500
543
|
input.claims.forEach((inputClaim, index) => {
|
|
501
544
|
const { legacyRouteTakeoverAuthorization: _authorization, ...runtimeClaim } = inputClaim;
|
|
502
|
-
const legacyClaim =
|
|
545
|
+
const legacyClaim = "surface" in runtimeClaim
|
|
546
|
+
? declarative_control_1.RouteClaimSchema.parse(runtimeClaim)
|
|
547
|
+
: declarative_control_1.LegacyRouteClaimSchema.parse(runtimeClaim);
|
|
503
548
|
const routeSpace = routeSpacesByRevision.get((0, exports.revisionRefKey)(legacyClaim.routeSpaceRevisionRef));
|
|
504
549
|
if (!routeSpace)
|
|
505
|
-
throw new DeclarativeControlCompilationError(
|
|
550
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISSING", `routeClaims[${index}].routeSpaceRevisionRef`, "No exact RouteSpace registration exists.");
|
|
506
551
|
const claim = declarative_control_1.RouteClaimSchema.parse({
|
|
507
552
|
...legacyClaim,
|
|
508
|
-
surface:
|
|
553
|
+
surface: "surface" in legacyClaim
|
|
554
|
+
? legacyClaim.surface
|
|
555
|
+
: routeSpace.supportedSurface,
|
|
509
556
|
});
|
|
510
557
|
if (routeSpace.supportedSurface !== claim.surface) {
|
|
511
|
-
throw new DeclarativeControlCompilationError(
|
|
558
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `routeClaims[${index}].surface`, `Route claim surface ${claim.surface} does not match RouteSpace ${routeSpace.routeSpaceId}.`);
|
|
512
559
|
}
|
|
513
560
|
if (claim.surface !== input.surface)
|
|
514
561
|
return;
|
|
515
562
|
const normalizedPath = normalizePath(claim.pathTemplate, routeSpace);
|
|
516
563
|
const knownParameters = new Set(routeSpace.parameters.map((parameter) => parameter.name));
|
|
517
|
-
const pathParameters = [
|
|
564
|
+
const pathParameters = [
|
|
565
|
+
...normalizedPath.matchAll(/:([A-Za-z][A-Za-z0-9_-]*)/g),
|
|
566
|
+
].map((match) => match[1]);
|
|
518
567
|
if (new Set(pathParameters).size !== pathParameters.length) {
|
|
519
|
-
throw new DeclarativeControlCompilationError(
|
|
568
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `routeClaims[${index}].pathTemplate`, "Route parameters must be unique within a path.");
|
|
520
569
|
}
|
|
521
570
|
const unknownParameter = pathParameters.find((parameter) => !knownParameters.has(parameter));
|
|
522
571
|
if (unknownParameter) {
|
|
523
|
-
throw new DeclarativeControlCompilationError(
|
|
572
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `routeClaims[${index}].pathTemplate`, `Route parameter ${unknownParameter} is not declared by ${routeSpace.routeSpaceId}.`);
|
|
524
573
|
}
|
|
525
574
|
const matcher = {
|
|
526
575
|
surface: claim.surface,
|
|
@@ -536,7 +585,7 @@ const compileRouteClaims = (input) => {
|
|
|
536
585
|
const reserved = routeSpace.reservedPaths.some((candidate) => {
|
|
537
586
|
const normalizedReserved = normalizePath(candidate, routeSpace);
|
|
538
587
|
const reservedClaim = declarative_control_1.CompiledRouteClaimSchema.parse({
|
|
539
|
-
kind:
|
|
588
|
+
kind: "canonical",
|
|
540
589
|
routeSpaceRevisionRef: claim.routeSpaceRevisionRef,
|
|
541
590
|
pathTemplate: candidate,
|
|
542
591
|
surface: claim.surface,
|
|
@@ -544,24 +593,23 @@ const compileRouteClaims = (input) => {
|
|
|
544
593
|
matcher,
|
|
545
594
|
});
|
|
546
595
|
return ((0, exports.compiledRouteClaimsOverlap)(compiledClaim, reservedClaim) ||
|
|
547
|
-
normalizedPath.startsWith(`${normalizedReserved.replace(/\/+$/,
|
|
596
|
+
normalizedPath.startsWith(`${normalizedReserved.replace(/\/+$/, "")}/`));
|
|
548
597
|
});
|
|
549
598
|
if (reserved) {
|
|
550
|
-
const takeover = takeoverAuthorizations.find((authorization) => compiledClaim.kind ===
|
|
599
|
+
const takeover = takeoverAuthorizations.find((authorization) => compiledClaim.kind === "canonical" &&
|
|
551
600
|
authorization.normalizedPath === compiledClaim.normalizedPath &&
|
|
552
601
|
(0, exports.sameRevisionRef)(authorization.routeSpaceRevisionRef, compiledClaim.routeSpaceRevisionRef));
|
|
553
602
|
if (!takeover) {
|
|
554
|
-
throw new DeclarativeControlCompilationError(
|
|
603
|
+
throw new DeclarativeControlCompilationError("ROUTE_RESERVED", `routeClaims[${index}].pathTemplate`, `Route ${normalizedPath} is reserved by ${routeSpace.routeSpaceId}.`);
|
|
555
604
|
}
|
|
556
605
|
}
|
|
557
|
-
const conflict = compiled.find((candidate) => (0, exports.sameRevisionRef)(candidate.routeSpaceRevisionRef, claim.routeSpaceRevisionRef) &&
|
|
558
|
-
(0, exports.compiledRouteClaimsOverlap)(candidate, compiledClaim));
|
|
606
|
+
const conflict = compiled.find((candidate) => (0, exports.sameRevisionRef)(candidate.routeSpaceRevisionRef, claim.routeSpaceRevisionRef) && (0, exports.compiledRouteClaimsOverlap)(candidate, compiledClaim));
|
|
559
607
|
if (conflict)
|
|
560
|
-
throw new DeclarativeControlCompilationError(
|
|
608
|
+
throw new DeclarativeControlCompilationError("ROUTE_CONFLICT", `routeClaims[${index}].pathTemplate`, `Route ${normalizedPath} overlaps another active claim pattern.`);
|
|
561
609
|
compiled.push(compiledClaim);
|
|
562
610
|
});
|
|
563
|
-
if (!compiled.some((claim) => claim.kind ===
|
|
564
|
-
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.");
|
|
565
613
|
}
|
|
566
614
|
return Object.freeze(compiled);
|
|
567
615
|
};
|
|
@@ -573,35 +621,38 @@ exports.compileRouteClaims = compileRouteClaims;
|
|
|
573
621
|
*/
|
|
574
622
|
const matchCompiledRouteClaim = (claimInput, pathInput) => {
|
|
575
623
|
const claim = declarative_control_1.CompiledRouteClaimSchema.parse(claimInput);
|
|
576
|
-
const rawPath = String(pathInput ||
|
|
577
|
-
if (!rawPath.startsWith(
|
|
578
|
-
rawPath.includes(
|
|
579
|
-
rawPath.includes(
|
|
580
|
-
rawPath.includes(
|
|
581
|
-
rawPath.includes(
|
|
624
|
+
const rawPath = String(pathInput || "").trim();
|
|
625
|
+
if (!rawPath.startsWith("/") ||
|
|
626
|
+
rawPath.includes("?") ||
|
|
627
|
+
rawPath.includes("#") ||
|
|
628
|
+
rawPath.includes("\\") ||
|
|
629
|
+
rawPath.includes("://")) {
|
|
582
630
|
return { matched: false, parameters: {} };
|
|
583
631
|
}
|
|
584
|
-
const rawSegments = rawPath.split(
|
|
585
|
-
if (rawSegments.some((segment) => segment ===
|
|
632
|
+
const rawSegments = rawPath.split("/");
|
|
633
|
+
if (rawSegments.some((segment) => segment === "." || segment === ".."))
|
|
586
634
|
return { matched: false, parameters: {} };
|
|
587
|
-
const requestHasTrailingSlash = rawPath.length > 1 && rawPath.endsWith(
|
|
588
|
-
if (claim.matcher.trailingSlash ===
|
|
635
|
+
const requestHasTrailingSlash = rawPath.length > 1 && rawPath.endsWith("/");
|
|
636
|
+
if (claim.matcher.trailingSlash === "require" && !requestHasTrailingSlash)
|
|
589
637
|
return { matched: false, parameters: {} };
|
|
590
|
-
if (claim.matcher.trailingSlash ===
|
|
591
|
-
requestHasTrailingSlash !==
|
|
638
|
+
if (claim.matcher.trailingSlash === "preserve" &&
|
|
639
|
+
requestHasTrailingSlash !==
|
|
640
|
+
(claim.normalizedPath.length > 1 && claim.normalizedPath.endsWith("/"))) {
|
|
592
641
|
return { matched: false, parameters: {} };
|
|
593
642
|
}
|
|
594
643
|
const normalizeTrailing = (value) => {
|
|
595
|
-
if (claim.matcher.trailingSlash ===
|
|
596
|
-
return value.replace(/\/+$/,
|
|
597
|
-
if (claim.matcher.trailingSlash ===
|
|
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("/"))
|
|
598
649
|
return `${value}/`;
|
|
599
650
|
return value;
|
|
600
651
|
};
|
|
601
652
|
const requestPath = normalizeTrailing(rawPath);
|
|
602
653
|
const templatePath = normalizeTrailing(claim.normalizedPath);
|
|
603
|
-
const requestSegments = requestPath.split(
|
|
604
|
-
const templateSegments = templatePath.split(
|
|
654
|
+
const requestSegments = requestPath.split("/").filter(Boolean);
|
|
655
|
+
const templateSegments = templatePath.split("/").filter(Boolean);
|
|
605
656
|
if (requestSegments.length !== templateSegments.length)
|
|
606
657
|
return { matched: false, parameters: {} };
|
|
607
658
|
const parameterDefinitions = new Map(claim.matcher.parameters.map((parameter) => [parameter.name, parameter]));
|
|
@@ -632,7 +683,7 @@ const matchCompiledRouteClaim = (claimInput, pathInput) => {
|
|
|
632
683
|
parameters[definition.name] = value;
|
|
633
684
|
normalizedSegments.push(rawValue);
|
|
634
685
|
}
|
|
635
|
-
const canonicalPath = normalizeTrailing(`/${normalizedSegments.join(
|
|
686
|
+
const canonicalPath = normalizeTrailing(`/${normalizedSegments.join("/")}` || "/");
|
|
636
687
|
return {
|
|
637
688
|
matched: true,
|
|
638
689
|
normalizedPath: canonicalPath,
|
|
@@ -641,11 +692,11 @@ const matchCompiledRouteClaim = (claimInput, pathInput) => {
|
|
|
641
692
|
};
|
|
642
693
|
exports.matchCompiledRouteClaim = matchCompiledRouteClaim;
|
|
643
694
|
function routeParameterPattern(type) {
|
|
644
|
-
if (type ===
|
|
695
|
+
if (type === "integer")
|
|
645
696
|
return /^-?\d+$/;
|
|
646
|
-
if (type ===
|
|
697
|
+
if (type === "uuid")
|
|
647
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;
|
|
648
|
-
if (type ===
|
|
699
|
+
if (type === "slug")
|
|
649
700
|
return /^[A-Za-z0-9]+(?:[._-][A-Za-z0-9]+)*$/;
|
|
650
701
|
return /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
651
702
|
}
|
|
@@ -657,26 +708,31 @@ function routeParameterPattern(type) {
|
|
|
657
708
|
const materializeRouteSpacePath = (input) => {
|
|
658
709
|
const routeSpace = declarative_control_1.RouteSpaceSchema.parse(input.routeSpace);
|
|
659
710
|
const applicationPath = declarative_control_1.RoutePathTemplateSchema.parse(input.applicationPath);
|
|
660
|
-
const basePath = routeSpace.basePath ===
|
|
661
|
-
let browserTemplate = normalizePath(`${basePath}${applicationPath ===
|
|
711
|
+
const basePath = routeSpace.basePath === "/" ? "" : routeSpace.basePath.replace(/\/+$/, "");
|
|
712
|
+
let browserTemplate = normalizePath(`${basePath}${applicationPath === "/" ? "/" : applicationPath}` || "/", routeSpace);
|
|
662
713
|
const parameters = input.parameters || {};
|
|
663
714
|
const declared = new Map(routeSpace.parameters.map((parameter) => [parameter.name, parameter]));
|
|
664
|
-
const referenced = [
|
|
715
|
+
const referenced = [
|
|
716
|
+
...browserTemplate.matchAll(/:([A-Za-z][A-Za-z0-9_-]*)/g),
|
|
717
|
+
].map((match) => match[1]);
|
|
665
718
|
for (const name of referenced) {
|
|
666
719
|
const definition = declared.get(name);
|
|
667
720
|
const raw = parameters[name];
|
|
668
|
-
if (!definition ||
|
|
669
|
-
|
|
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}.`);
|
|
670
726
|
}
|
|
671
727
|
const value = String(raw);
|
|
672
728
|
if (!routeParameterPattern(definition.type).test(value)) {
|
|
673
|
-
throw new DeclarativeControlCompilationError(
|
|
729
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `parameters.${name}`, `Route parameter ${name} does not satisfy ${definition.type}.`);
|
|
674
730
|
}
|
|
675
731
|
browserTemplate = browserTemplate.replace(`:${name}`, encodeURIComponent(value));
|
|
676
732
|
}
|
|
677
733
|
const unknown = Object.keys(parameters).find((name) => !declared.has(name));
|
|
678
734
|
if (unknown)
|
|
679
|
-
throw new DeclarativeControlCompilationError(
|
|
735
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `parameters.${unknown}`, `RouteSpace ${routeSpace.routeSpaceId} does not declare ${unknown}.`);
|
|
680
736
|
return browserTemplate;
|
|
681
737
|
};
|
|
682
738
|
exports.materializeRouteSpacePath = materializeRouteSpacePath;
|
|
@@ -689,7 +745,7 @@ const renderTreeDepth = (tree) => {
|
|
|
689
745
|
const ancestors = new Set([node.nodeId]);
|
|
690
746
|
while (parentId) {
|
|
691
747
|
if (ancestors.has(parentId)) {
|
|
692
|
-
throw new DeclarativeControlCompilationError(
|
|
748
|
+
throw new DeclarativeControlCompilationError("RENDER_TREE_INVALID", "page.renderTree.nodes", `RenderTree contains a parent cycle at ${parentId}.`);
|
|
693
749
|
}
|
|
694
750
|
ancestors.add(parentId);
|
|
695
751
|
depth += 1;
|
|
@@ -705,48 +761,51 @@ const compareLegacyEntityRef = (reference, revision) => reference.kind === revis
|
|
|
705
761
|
reference.ownerRepo === revision.ownerRepo;
|
|
706
762
|
const pageDependencies = (page, compilerRevisionRef) => [
|
|
707
763
|
...page.routeClaims.map((claim) => ({
|
|
708
|
-
role:
|
|
764
|
+
role: "route-space",
|
|
709
765
|
revisionRef: claim.routeSpaceRevisionRef,
|
|
710
766
|
})),
|
|
711
|
-
{ role:
|
|
767
|
+
{ role: "shell", revisionRef: page.shellRevisionRef },
|
|
712
768
|
...page.capabilityInstances.flatMap((instance) => [
|
|
713
769
|
{
|
|
714
|
-
role:
|
|
770
|
+
role: "capability",
|
|
715
771
|
revisionRef: instance.capabilityRevisionRef,
|
|
716
772
|
},
|
|
717
|
-
{ role:
|
|
718
|
-
...(instance.eventEffects ?? []).map((effect) => ({
|
|
773
|
+
{ role: "provider", revisionRef: instance.providerRevisionRef },
|
|
774
|
+
...(instance.eventEffects ?? []).map((effect) => ({
|
|
775
|
+
role: "schema",
|
|
776
|
+
revisionRef: effect.sourceIntentSchemaRevisionRef,
|
|
777
|
+
})),
|
|
719
778
|
{
|
|
720
|
-
role:
|
|
779
|
+
role: "schema",
|
|
721
780
|
revisionRef: instance.propertySchemaRevisionRef,
|
|
722
781
|
},
|
|
723
782
|
]),
|
|
724
783
|
...(page.stateDefinitions ?? []).map((state) => ({
|
|
725
|
-
role:
|
|
784
|
+
role: "schema",
|
|
726
785
|
revisionRef: state.schemaRevisionRef,
|
|
727
786
|
})),
|
|
728
787
|
...(page.interactionBindings ?? []).map((binding) => ({
|
|
729
|
-
role:
|
|
788
|
+
role: "schema",
|
|
730
789
|
revisionRef: binding.sourceIntentSchemaRevisionRef,
|
|
731
790
|
})),
|
|
732
791
|
...page.ontologyBindings.flatMap((binding) => [
|
|
733
792
|
{
|
|
734
|
-
role:
|
|
793
|
+
role: "ontology-definition",
|
|
735
794
|
revisionRef: binding.ontologyDefinitionRevisionRef,
|
|
736
795
|
},
|
|
737
|
-
{ role:
|
|
796
|
+
{ role: "view", revisionRef: binding.viewRevisionRef },
|
|
738
797
|
{
|
|
739
|
-
role:
|
|
798
|
+
role: "view",
|
|
740
799
|
revisionRef: binding.canonicalDataViewRevisionRef,
|
|
741
800
|
},
|
|
742
801
|
{
|
|
743
|
-
role:
|
|
802
|
+
role: "schema",
|
|
744
803
|
revisionRef: binding.renderModelSchemaRevisionRef,
|
|
745
804
|
},
|
|
746
805
|
...(binding.cursorWindow
|
|
747
806
|
? [
|
|
748
807
|
{
|
|
749
|
-
role:
|
|
808
|
+
role: "schema",
|
|
750
809
|
revisionRef: binding.cursorWindow.pageChangeIntentSchemaRevisionRef,
|
|
751
810
|
},
|
|
752
811
|
]
|
|
@@ -754,75 +813,449 @@ const pageDependencies = (page, compilerRevisionRef) => [
|
|
|
754
813
|
]),
|
|
755
814
|
...(page.queryBindings ?? []).flatMap((binding) => [
|
|
756
815
|
{
|
|
757
|
-
role:
|
|
816
|
+
role: "query-definition",
|
|
758
817
|
revisionRef: binding.queryDefinitionRevisionRef,
|
|
759
818
|
},
|
|
760
819
|
{
|
|
761
|
-
role:
|
|
820
|
+
role: "schema",
|
|
762
821
|
revisionRef: binding.renderModelSchemaRevisionRef,
|
|
763
822
|
},
|
|
764
|
-
...(binding.targetProjection
|
|
765
|
-
|
|
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
|
+
: []),
|
|
766
839
|
...(binding.cursorWindow
|
|
767
840
|
? [
|
|
768
841
|
{
|
|
769
|
-
role:
|
|
842
|
+
role: "schema",
|
|
770
843
|
revisionRef: binding.cursorWindow.pageChangeIntentSchemaRevisionRef,
|
|
771
844
|
},
|
|
772
845
|
]
|
|
773
846
|
: []),
|
|
774
847
|
]),
|
|
775
848
|
...page.actionBindings.flatMap((binding) => [
|
|
776
|
-
{ role:
|
|
849
|
+
{ role: "action", revisionRef: binding.commandRevisionRef },
|
|
777
850
|
{
|
|
778
|
-
role:
|
|
851
|
+
role: "schema",
|
|
779
852
|
revisionRef: binding.sourceIntentSchemaRevisionRef,
|
|
780
853
|
},
|
|
781
|
-
{ role:
|
|
782
|
-
{ role:
|
|
854
|
+
{ role: "schema", revisionRef: binding.inputSchemaRevisionRef },
|
|
855
|
+
{ role: "schema", revisionRef: binding.resultSchemaRevisionRef },
|
|
783
856
|
...(binding.compensationCommandRevisionRef
|
|
784
857
|
? [
|
|
785
858
|
{
|
|
786
|
-
role:
|
|
859
|
+
role: "action",
|
|
787
860
|
revisionRef: binding.compensationCommandRevisionRef,
|
|
788
861
|
},
|
|
789
862
|
]
|
|
790
863
|
: []),
|
|
791
864
|
]),
|
|
792
865
|
...page.tokenRevisionRefs.map((revisionRef) => ({
|
|
793
|
-
role:
|
|
866
|
+
role: "token",
|
|
794
867
|
revisionRef,
|
|
795
868
|
})),
|
|
796
|
-
{ role:
|
|
869
|
+
{ role: "performance-budget", revisionRef: page.performanceBudgetRef },
|
|
797
870
|
{
|
|
798
|
-
role:
|
|
871
|
+
role: "observation-policy",
|
|
799
872
|
revisionRef: page.observationPolicyRevisionRef,
|
|
800
873
|
},
|
|
801
|
-
{ role:
|
|
874
|
+
{ role: "compiler", revisionRef: compilerRevisionRef },
|
|
802
875
|
];
|
|
803
876
|
const domainQueryDependencies = (definition) => [
|
|
804
877
|
...(definition.ontologyDefinitionRevisionRef
|
|
805
878
|
? [
|
|
806
879
|
{
|
|
807
|
-
role:
|
|
880
|
+
role: "ontology-definition",
|
|
808
881
|
revisionRef: definition.ontologyDefinitionRevisionRef,
|
|
809
882
|
},
|
|
810
883
|
]
|
|
811
884
|
: []),
|
|
812
|
-
...(definition.viewRevisionRef
|
|
885
|
+
...(definition.viewRevisionRef
|
|
886
|
+
? [{ role: "view", revisionRef: definition.viewRevisionRef }]
|
|
887
|
+
: []),
|
|
813
888
|
...(definition.canonicalDataViewRevisionRef
|
|
814
889
|
? [
|
|
815
890
|
{
|
|
816
|
-
role:
|
|
891
|
+
role: "view",
|
|
817
892
|
revisionRef: definition.canonicalDataViewRevisionRef,
|
|
818
893
|
},
|
|
819
894
|
]
|
|
820
895
|
: []),
|
|
821
|
-
{ role:
|
|
822
|
-
{ role:
|
|
896
|
+
{ role: "schema", revisionRef: definition.inputSchemaRevisionRef },
|
|
897
|
+
{ role: "schema", revisionRef: definition.resultSchemaRevisionRef },
|
|
823
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) => {
|
|
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;
|
|
824
1257
|
const compilePageRuntimeBundle = (input) => {
|
|
825
|
-
const parsedPage = declarative_control_1.
|
|
1258
|
+
const parsedPage = declarative_control_1.ResolvedPageSchema.parse(input.page);
|
|
826
1259
|
const page = {
|
|
827
1260
|
...parsedPage,
|
|
828
1261
|
routeStatePresentations: parsedPage.routeStatePresentations ?? [],
|
|
@@ -836,12 +1269,12 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
836
1269
|
const releaseRevisionRef = declarative_control_1.RevisionRefSchema.parse(input.releaseRevisionRef);
|
|
837
1270
|
const compilerRevisionRef = declarative_control_1.RevisionRefSchema.parse(input.compilerRevisionRef);
|
|
838
1271
|
const scope = declarative_control_1.TenantScopeSchema.parse(page.tenantScope);
|
|
839
|
-
assertSameTenantScope(release.tenantScope, scope,
|
|
840
|
-
assertRevisionIdentity(pageRevisionRef,
|
|
841
|
-
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");
|
|
842
1275
|
if (!(0, exports.sameRevisionRef)(pageRevisionRef, release.pageRevisionRef))
|
|
843
|
-
throw new DeclarativeControlCompilationError(
|
|
844
|
-
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");
|
|
845
1278
|
requireReleaseReady(release);
|
|
846
1279
|
requireDependencies(release.dependencySnapshot, pageDependencies(page, compilerRevisionRef));
|
|
847
1280
|
assertDependencyScopes(release.dependencySnapshot, scope);
|
|
@@ -849,13 +1282,13 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
849
1282
|
const definitionRevisionRef = declarative_control_1.RevisionRefSchema.parse(registration.definitionRevisionRef);
|
|
850
1283
|
const definition = declarative_control_1.DomainQueryDefinitionSchema.parse(registration.definition);
|
|
851
1284
|
assertSameTenantScope(definition.tenantScope, scope, `domainQueryRegistry[${index}].definition.tenantScope`);
|
|
852
|
-
assertRevisionIdentity(definitionRevisionRef,
|
|
1285
|
+
assertRevisionIdentity(definitionRevisionRef, "domain-query-definition", definition.queryId, scope, `domainQueryRegistry[${index}].definitionRevisionRef`);
|
|
853
1286
|
if (definitionRevisionRef.contentHash !== (0, exports.canonicalContentHash)(definition)) {
|
|
854
|
-
throw new DeclarativeControlCompilationError(
|
|
1287
|
+
throw new DeclarativeControlCompilationError("QUERY_DEFINITION_MISMATCH", `domainQueryRegistry[${index}].definitionRevisionRef.contentHash`, "Domain Query registry entry does not match its exact definition content hash.");
|
|
855
1288
|
}
|
|
856
1289
|
return { definitionRevisionRef, definition };
|
|
857
1290
|
});
|
|
858
|
-
assertUniqueRegistryKeys(queryRegistrations.map((registration) => (0, exports.revisionRefKey)(registration.definitionRevisionRef)),
|
|
1291
|
+
assertUniqueRegistryKeys(queryRegistrations.map((registration) => (0, exports.revisionRefKey)(registration.definitionRevisionRef)), "domainQueryRegistry");
|
|
859
1292
|
const queryDefinitionsByRevision = new Map(queryRegistrations.map((registration) => [
|
|
860
1293
|
(0, exports.revisionRefKey)(registration.definitionRevisionRef),
|
|
861
1294
|
registration.definition,
|
|
@@ -863,47 +1296,49 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
863
1296
|
page.queryBindings.forEach((binding, index) => {
|
|
864
1297
|
const definition = queryDefinitionsByRevision.get((0, exports.revisionRefKey)(binding.queryDefinitionRevisionRef));
|
|
865
1298
|
if (!definition) {
|
|
866
|
-
throw new DeclarativeControlCompilationError(
|
|
1299
|
+
throw new DeclarativeControlCompilationError("QUERY_DEFINITION_MISMATCH", `queryBindings[${index}].queryDefinitionRevisionRef`, "Query binding does not resolve to one exact governed Domain Query definition.");
|
|
867
1300
|
}
|
|
868
1301
|
if (!(0, exports.sameRevisionRef)(definition.resultSchemaRevisionRef, binding.renderModelSchemaRevisionRef)) {
|
|
869
|
-
throw new DeclarativeControlCompilationError(
|
|
1302
|
+
throw new DeclarativeControlCompilationError("QUERY_DEFINITION_MISMATCH", `queryBindings[${index}].renderModelSchemaRevisionRef`, "Query binding render model does not match the exact Domain Query result schema.");
|
|
870
1303
|
}
|
|
871
1304
|
if (!(0, exports.accessPolicyChainImplies)([page.pageAccessPolicy], definition.accessPolicy) &&
|
|
872
|
-
binding.accessFailure !==
|
|
873
|
-
throw new DeclarativeControlCompilationError(
|
|
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.");
|
|
874
1307
|
}
|
|
875
|
-
const sourceKind = definition.dataSource?.kind ??
|
|
876
|
-
if (binding.execution ===
|
|
877
|
-
throw new DeclarativeControlCompilationError(
|
|
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.");
|
|
878
1311
|
}
|
|
879
|
-
if (binding.execution ===
|
|
880
|
-
throw new DeclarativeControlCompilationError(
|
|
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.");
|
|
881
1314
|
}
|
|
882
1315
|
requireDependencies(release.dependencySnapshot, domainQueryDependencies(definition));
|
|
883
1316
|
});
|
|
884
1317
|
const shellRegistration = declarative_control_http_1.DeclarativeProductResourceRegistrationSchema.parse(input.shellRegistration);
|
|
885
|
-
if (shellRegistration.resourceKind !==
|
|
1318
|
+
if (shellRegistration.resourceKind !== "shell" ||
|
|
886
1319
|
!(0, exports.sameRevisionRef)(shellRegistration.resourceRevisionRef, page.shellRevisionRef) ||
|
|
887
|
-
(0, exports.declarativeProductResourceRevisionHash)(shellRegistration) !==
|
|
888
|
-
|
|
889
|
-
|
|
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.");
|
|
890
1325
|
}
|
|
891
1326
|
if (!shellRegistration.supportedSurfaces.includes(release.target.surface)) {
|
|
892
|
-
throw new DeclarativeControlCompilationError(
|
|
1327
|
+
throw new DeclarativeControlCompilationError("SHELL_SURFACE_MISMATCH", "shellRegistration.supportedSurfaces", "The active Shell registration does not support the release surface.");
|
|
893
1328
|
}
|
|
894
1329
|
const shellDocument = declarative_control_http_1.DeclarativeShellResourceDocumentSchema.parse(shellRegistration.document);
|
|
895
1330
|
const shellChrome = shellDocument.surfaceChrome.find((entry) => entry.surface === release.target.surface);
|
|
896
1331
|
if (!shellChrome) {
|
|
897
|
-
throw new DeclarativeControlCompilationError(
|
|
1332
|
+
throw new DeclarativeControlCompilationError("SHELL_CHROME_UNAVAILABLE", "shellRegistration.document.surfaceChrome", "The active Shell does not declare chrome for the release surface.");
|
|
898
1333
|
}
|
|
899
1334
|
const shellDescriptor = {
|
|
900
1335
|
shellRevisionRef: page.shellRevisionRef,
|
|
901
1336
|
header: shellChrome.header,
|
|
902
1337
|
};
|
|
903
1338
|
if (page.renderTree.nodes.length > input.limits.maxRenderNodes)
|
|
904
|
-
throw new DeclarativeControlCompilationError(
|
|
1339
|
+
throw new DeclarativeControlCompilationError("RENDER_NODE_LIMIT_EXCEEDED", "page.renderTree.nodes", "RenderTree exceeds the caller-approved node limit.");
|
|
905
1340
|
if (renderTreeDepth(page.renderTree) > input.limits.maxRenderDepth)
|
|
906
|
-
throw new DeclarativeControlCompilationError(
|
|
1341
|
+
throw new DeclarativeControlCompilationError("RENDER_DEPTH_EXCEEDED", "page.renderTree.nodes", "RenderTree exceeds the caller-approved depth limit.");
|
|
907
1342
|
const renderTree = (0, render_tree_compiler_1.compileRenderTree)(page.renderTree).tree;
|
|
908
1343
|
const instancesByNode = new Map(page.capabilityInstances.map((instance) => [instance.nodeId, instance]));
|
|
909
1344
|
const registrations = input.capabilityRegistry.map((registration, registrationIndex) => ({
|
|
@@ -912,20 +1347,20 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
912
1347
|
providerRevisionRef: declarative_control_1.RevisionRefSchema.parse(registration.providerRevisionRef),
|
|
913
1348
|
inputPorts: registration.inputPorts.map((port, portIndex) => {
|
|
914
1349
|
const schemaRevisionRef = declarative_control_1.RevisionRefSchema.parse(port.schemaRevisionRef);
|
|
915
|
-
if (schemaRevisionRef.kind !==
|
|
916
|
-
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.");
|
|
917
1352
|
}
|
|
918
1353
|
return { ...port, schemaRevisionRef };
|
|
919
1354
|
}),
|
|
920
1355
|
outputPorts: registration.outputPorts.map((port, portIndex) => {
|
|
921
1356
|
const schemaRevisionRef = declarative_control_1.RevisionRefSchema.parse(port.schemaRevisionRef);
|
|
922
|
-
if (schemaRevisionRef.kind !==
|
|
923
|
-
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.");
|
|
924
1359
|
}
|
|
925
1360
|
return { ...port, schemaRevisionRef };
|
|
926
1361
|
}),
|
|
927
1362
|
}));
|
|
928
|
-
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");
|
|
929
1364
|
const registrationsByPair = new Map(registrations.map((registration) => [
|
|
930
1365
|
`${(0, exports.revisionRefKey)(registration.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(registration.providerRevisionRef)}`,
|
|
931
1366
|
registration,
|
|
@@ -937,30 +1372,33 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
937
1372
|
!node.providerRef ||
|
|
938
1373
|
!compareLegacyEntityRef(node.providerRef, instance.providerRevisionRef) ||
|
|
939
1374
|
node.pageRef.id !== page.pageId) {
|
|
940
|
-
throw new DeclarativeControlCompilationError(
|
|
1375
|
+
throw new DeclarativeControlCompilationError("CAPABILITY_BINDING_MISMATCH", `capabilityInstances[${index}]`, "RenderTree node identity does not match the declared capability/provider instance.");
|
|
941
1376
|
}
|
|
942
1377
|
const registration = registrationsByPair.get(`${(0, exports.revisionRefKey)(instance.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(instance.providerRevisionRef)}`);
|
|
943
1378
|
if (!registration) {
|
|
944
|
-
throw new DeclarativeControlCompilationError(
|
|
1379
|
+
throw new DeclarativeControlCompilationError("CAPABILITY_BINDING_MISMATCH", `capabilityInstances[${index}]`, "Capability/provider pair has no exact governed Design registration.");
|
|
945
1380
|
}
|
|
946
1381
|
if (instance.allowedSideEffects.some((sideEffect) => !registration.allowedSideEffects.includes(sideEffect))) {
|
|
947
|
-
throw new DeclarativeControlCompilationError(
|
|
1382
|
+
throw new DeclarativeControlCompilationError("CAPABILITY_BINDING_MISMATCH", `capabilityInstances[${index}].allowedSideEffects`, "Page requests undeclared provider side effects.");
|
|
948
1383
|
}
|
|
949
1384
|
if (!(0, exports.sameRevisionRef)(instance.propertySchemaRevisionRef, registration.propertySchemaRevisionRef)) {
|
|
950
|
-
throw new DeclarativeControlCompilationError(
|
|
1385
|
+
throw new DeclarativeControlCompilationError("CAPABILITY_BINDING_MISMATCH", `capabilityInstances[${index}].propertySchemaRevisionRef`, "Capability properties do not pin the exact schema registered by the active Design catalog.");
|
|
951
1386
|
}
|
|
952
|
-
if (!(0, exports.accessPolicyChainImplies)([
|
|
953
|
-
|
|
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.");
|
|
954
1392
|
}
|
|
955
1393
|
});
|
|
956
1394
|
if (instancesByNode.size !== page.capabilityInstances.length) {
|
|
957
|
-
throw new DeclarativeControlCompilationError(
|
|
1395
|
+
throw new DeclarativeControlCompilationError("CAPABILITY_BINDING_MISMATCH", "capabilityInstances", "Each executable RenderTree node must be governed by at most one capability instance.");
|
|
958
1396
|
}
|
|
959
1397
|
renderTree.nodes.forEach((node, index) => {
|
|
960
1398
|
if (!node.providerRef)
|
|
961
1399
|
return;
|
|
962
1400
|
if (!instancesByNode.has(node.nodeId)) {
|
|
963
|
-
throw new DeclarativeControlCompilationError(
|
|
1401
|
+
throw new DeclarativeControlCompilationError("CAPABILITY_BINDING_MISMATCH", `renderTree.nodes[${index}]`, "Every provider-backed RenderTree node must be governed by one capability instance.");
|
|
964
1402
|
}
|
|
965
1403
|
});
|
|
966
1404
|
page.ontologyBindings.forEach((binding, index) => {
|
|
@@ -968,16 +1406,20 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
968
1406
|
const registration = instance &&
|
|
969
1407
|
registrationsByPair.get(`${(0, exports.revisionRefKey)(instance.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(instance.providerRevisionRef)}`);
|
|
970
1408
|
const port = registration?.inputPorts.find((candidate) => candidate.name === binding.target.port);
|
|
971
|
-
if (!port ||
|
|
972
|
-
|
|
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.");
|
|
973
1412
|
}
|
|
974
1413
|
if (binding.cursorWindow) {
|
|
975
|
-
const sourceInstance = page.capabilityInstances.find(candidate => candidate.instanceId ===
|
|
976
|
-
|
|
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)}`);
|
|
977
1419
|
const pageChangePort = sourceRegistration?.outputPorts.find((candidate) => candidate.name === binding.cursorWindow.pageChangePort);
|
|
978
1420
|
if (!pageChangePort ||
|
|
979
1421
|
!(0, exports.sameRevisionRef)(pageChangePort.schemaRevisionRef, binding.cursorWindow.pageChangeIntentSchemaRevisionRef)) {
|
|
980
|
-
throw new DeclarativeControlCompilationError(
|
|
1422
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `ontologyBindings[${index}].cursorWindow.pageChangePort`, "Cursor-window page-change intent does not match the registered capability output port.");
|
|
981
1423
|
}
|
|
982
1424
|
}
|
|
983
1425
|
});
|
|
@@ -986,19 +1428,26 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
986
1428
|
const registration = instance &&
|
|
987
1429
|
registrationsByPair.get(`${(0, exports.revisionRefKey)(instance.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(instance.providerRevisionRef)}`);
|
|
988
1430
|
const port = registration?.inputPorts.find((candidate) => candidate.name === binding.target.port);
|
|
989
|
-
if (!port ||
|
|
990
|
-
|
|
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.");
|
|
991
1435
|
}
|
|
992
|
-
if (binding.streamTargetProjection &&
|
|
993
|
-
|
|
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.");
|
|
994
1440
|
}
|
|
995
1441
|
if (binding.cursorWindow) {
|
|
996
|
-
const sourceInstance = page.capabilityInstances.find(candidate => candidate.instanceId ===
|
|
997
|
-
|
|
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)}`);
|
|
998
1447
|
const pageChangePort = sourceRegistration?.outputPorts.find((candidate) => candidate.name === binding.cursorWindow.pageChangePort);
|
|
999
1448
|
if (!pageChangePort ||
|
|
1000
1449
|
!(0, exports.sameRevisionRef)(pageChangePort.schemaRevisionRef, binding.cursorWindow.pageChangeIntentSchemaRevisionRef)) {
|
|
1001
|
-
throw new DeclarativeControlCompilationError(
|
|
1450
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `queryBindings[${index}].cursorWindow.pageChangePort`, "Cursor-window page-change intent does not match the registered capability output port.");
|
|
1002
1451
|
}
|
|
1003
1452
|
}
|
|
1004
1453
|
});
|
|
@@ -1006,12 +1455,13 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
1006
1455
|
const registration = registrationsByPair.get(`${(0, exports.revisionRefKey)(instance.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(instance.providerRevisionRef)}`);
|
|
1007
1456
|
instance.eventPayloadBindings?.forEach((binding, bindingIndex) => {
|
|
1008
1457
|
if (!registration?.outputPorts.some((port) => port.name === binding.port))
|
|
1009
|
-
throw new DeclarativeControlCompilationError(
|
|
1458
|
+
throw new DeclarativeControlCompilationError("PORT_TYPE_MISMATCH", `capabilityInstances[${index}].eventPayloadBindings[${bindingIndex}]`, "Event payload projection must reference a registered output port.");
|
|
1010
1459
|
});
|
|
1011
1460
|
instance.eventEffects?.forEach((effect, effectIndex) => {
|
|
1012
1461
|
const port = registration?.outputPorts.find((candidate) => candidate.name === effect.sourcePort);
|
|
1013
|
-
if (!port ||
|
|
1014
|
-
|
|
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.");
|
|
1015
1465
|
});
|
|
1016
1466
|
});
|
|
1017
1467
|
page.interactionBindings.forEach((binding, index) => {
|
|
@@ -1019,8 +1469,9 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
1019
1469
|
const registration = instance &&
|
|
1020
1470
|
registrationsByPair.get(`${(0, exports.revisionRefKey)(instance.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(instance.providerRevisionRef)}`);
|
|
1021
1471
|
const port = registration?.outputPorts.find((candidate) => candidate.name === binding.source.port);
|
|
1022
|
-
if (!port ||
|
|
1023
|
-
|
|
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.");
|
|
1024
1475
|
}
|
|
1025
1476
|
});
|
|
1026
1477
|
page.actionBindings.forEach((binding, index) => {
|
|
@@ -1028,24 +1479,28 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
1028
1479
|
const registration = instance &&
|
|
1029
1480
|
registrationsByPair.get(`${(0, exports.revisionRefKey)(instance.capabilityRevisionRef)}:${(0, exports.revisionRefKey)(instance.providerRevisionRef)}`);
|
|
1030
1481
|
const port = registration?.outputPorts.find((candidate) => candidate.name === binding.source.port);
|
|
1031
|
-
if (!port ||
|
|
1032
|
-
|
|
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.");
|
|
1033
1485
|
}
|
|
1034
1486
|
});
|
|
1035
1487
|
const routeClaims = (0, exports.compileRouteClaims)({
|
|
1036
1488
|
claims: page.routeClaims,
|
|
1037
1489
|
routeSpaces: input.routeSpaces,
|
|
1038
1490
|
surface: release.target.surface,
|
|
1039
|
-
legacyRouteTakeoverAuthorizations: release.target
|
|
1491
|
+
legacyRouteTakeoverAuthorizations: release.target
|
|
1492
|
+
.legacyRouteTakeoverAuthorization
|
|
1040
1493
|
? [release.target.legacyRouteTakeoverAuthorization]
|
|
1041
1494
|
: [],
|
|
1042
1495
|
});
|
|
1043
|
-
const releaseRoute = routeClaims.find((claim) => claim.kind ===
|
|
1044
|
-
|
|
1045
|
-
|
|
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.");
|
|
1046
1501
|
}
|
|
1047
1502
|
if (!page.supportedSurfaces.includes(release.target.surface)) {
|
|
1048
|
-
throw new DeclarativeControlCompilationError(
|
|
1503
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", "release.target.surface", "Page does not support the release surface.");
|
|
1049
1504
|
}
|
|
1050
1505
|
const routeTargets = (input.targetRegistry ?? []).map((registration, index) => ({
|
|
1051
1506
|
...registration,
|
|
@@ -1060,33 +1515,36 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
1060
1515
|
routeClaim: declarative_control_1.CompiledRouteClaimSchema.parse(registration.routeClaim),
|
|
1061
1516
|
sourceIndex: index,
|
|
1062
1517
|
}));
|
|
1063
|
-
assertUniqueRegistryKeys(routeTargets.map((registration) => (0, exports.stableRefKey)(registration.stableTargetRef)),
|
|
1518
|
+
assertUniqueRegistryKeys(routeTargets.map((registration) => (0, exports.stableRefKey)(registration.stableTargetRef)), "targetRegistry");
|
|
1064
1519
|
const entryTransitions = page.entryTransitions.map((transition, index) => {
|
|
1065
|
-
const matches = routeTargets.filter((registration) => registration.kind ===
|
|
1520
|
+
const matches = routeTargets.filter((registration) => registration.kind === "route" &&
|
|
1066
1521
|
(0, exports.sameStableRef)(registration.stableTargetRef, transition.targetRef) &&
|
|
1067
1522
|
registration.surface === release.target.surface);
|
|
1068
1523
|
if (matches.length !== 1) {
|
|
1069
|
-
throw new DeclarativeControlCompilationError(
|
|
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.");
|
|
1070
1525
|
}
|
|
1071
1526
|
const target = matches[0];
|
|
1072
|
-
if (target.releaseRevisionRef &&
|
|
1073
|
-
|
|
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.");
|
|
1074
1530
|
}
|
|
1075
|
-
if (target.routeClaim.kind !==
|
|
1076
|
-
throw new DeclarativeControlCompilationError(
|
|
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.");
|
|
1077
1533
|
}
|
|
1078
1534
|
if (!(0, exports.sameStableRef)(target.stableTargetRef, target.targetRevisionRef)) {
|
|
1079
|
-
throw new DeclarativeControlCompilationError(
|
|
1535
|
+
throw new DeclarativeControlCompilationError("PAGE_ENTRY_TARGET_UNRELEASED", `targetRegistry[${target.sourceIndex}]`, "Entry transition target registration identity is not exact.");
|
|
1080
1536
|
}
|
|
1081
1537
|
// Entry transitions may narrow into a separately protected route. The target
|
|
1082
1538
|
// route remains authoritative and re-evaluates its own access policy after
|
|
1083
1539
|
// navigation, so a source Page must not inherit the target's permissions.
|
|
1084
|
-
const requiredPathParameters = [
|
|
1540
|
+
const requiredPathParameters = [
|
|
1541
|
+
...target.routeClaim.normalizedPath.matchAll(/:([A-Za-z][A-Za-z0-9_-]*)/g),
|
|
1542
|
+
].map((match) => match[1]);
|
|
1085
1543
|
const mappedPathParameters = Object.keys(transition.pathParameters);
|
|
1086
1544
|
const missing = requiredPathParameters.find((name) => !mappedPathParameters.includes(name));
|
|
1087
1545
|
const unknown = mappedPathParameters.find((name) => !requiredPathParameters.includes(name));
|
|
1088
1546
|
if (missing || unknown) {
|
|
1089
|
-
throw new DeclarativeControlCompilationError(
|
|
1547
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `entryTransitions[${index}].pathParameters`, missing
|
|
1090
1548
|
? `Entry transition does not resolve target route parameter ${missing}.`
|
|
1091
1549
|
: `Entry transition maps unknown target route parameter ${unknown}.`);
|
|
1092
1550
|
}
|
|
@@ -1106,15 +1564,17 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
1106
1564
|
]);
|
|
1107
1565
|
return {
|
|
1108
1566
|
...transition,
|
|
1109
|
-
browsingContext: transition.browsingContext ??
|
|
1567
|
+
browsingContext: transition.browsingContext ?? "same",
|
|
1110
1568
|
targetRevisionRef: target.targetRevisionRef,
|
|
1111
|
-
...(target.releaseRevisionRef
|
|
1569
|
+
...(target.releaseRevisionRef
|
|
1570
|
+
? { targetReleaseRevisionRef: target.releaseRevisionRef }
|
|
1571
|
+
: {}),
|
|
1112
1572
|
targetAccessPolicy: target.accessPolicy,
|
|
1113
1573
|
targetRouteClaim: target.routeClaim,
|
|
1114
1574
|
};
|
|
1115
1575
|
});
|
|
1116
1576
|
const unsigned = {
|
|
1117
|
-
contract:
|
|
1577
|
+
contract: "PageRuntimeBundle",
|
|
1118
1578
|
schemaVersion: 1,
|
|
1119
1579
|
tenantScope: scope,
|
|
1120
1580
|
releaseRevisionRef,
|
|
@@ -1153,23 +1613,23 @@ const compilePageRuntimeBundle = (input) => {
|
|
|
1153
1613
|
exports.compilePageRuntimeBundle = compilePageRuntimeBundle;
|
|
1154
1614
|
const workbenchDependencyRole = (kind) => {
|
|
1155
1615
|
switch (kind) {
|
|
1156
|
-
case
|
|
1157
|
-
return
|
|
1158
|
-
case
|
|
1159
|
-
return
|
|
1160
|
-
case
|
|
1161
|
-
return
|
|
1162
|
-
case
|
|
1163
|
-
return
|
|
1164
|
-
case
|
|
1165
|
-
return
|
|
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";
|
|
1166
1626
|
default:
|
|
1167
|
-
return
|
|
1627
|
+
return "workflow";
|
|
1168
1628
|
}
|
|
1169
1629
|
};
|
|
1170
1630
|
const workbenchDependencies = (workbench, compilerRevisionRef) => [
|
|
1171
1631
|
...workbench.routeClaims.map((claim) => ({
|
|
1172
|
-
role:
|
|
1632
|
+
role: "route-space",
|
|
1173
1633
|
revisionRef: claim.routeSpaceRevisionRef,
|
|
1174
1634
|
})),
|
|
1175
1635
|
...workbench.appInstances.flatMap((instance) => [
|
|
@@ -1177,28 +1637,28 @@ const workbenchDependencies = (workbench, compilerRevisionRef) => [
|
|
|
1177
1637
|
role: workbenchDependencyRole(instance.targetRevisionRef.kind),
|
|
1178
1638
|
revisionRef: instance.targetRevisionRef,
|
|
1179
1639
|
},
|
|
1180
|
-
{ role:
|
|
1181
|
-
{ role:
|
|
1640
|
+
{ role: "provider", revisionRef: instance.providerRevisionRef },
|
|
1641
|
+
{ role: "schema", revisionRef: instance.inputSchemaRevisionRef },
|
|
1182
1642
|
]),
|
|
1183
1643
|
{
|
|
1184
|
-
role:
|
|
1644
|
+
role: "capability",
|
|
1185
1645
|
revisionRef: workbench.layout.hostCapabilityRevisionRef,
|
|
1186
1646
|
},
|
|
1187
|
-
{ role:
|
|
1647
|
+
{ role: "provider", revisionRef: workbench.layout.hostProviderRevisionRef },
|
|
1188
1648
|
{
|
|
1189
|
-
role:
|
|
1649
|
+
role: "layout-policy",
|
|
1190
1650
|
revisionRef: workbench.layout.layoutPolicyRevisionRef,
|
|
1191
1651
|
},
|
|
1192
1652
|
...workbench.layout.tokenRevisionRefs.map((revisionRef) => ({
|
|
1193
|
-
role:
|
|
1653
|
+
role: "token",
|
|
1194
1654
|
revisionRef,
|
|
1195
1655
|
})),
|
|
1196
|
-
{ role:
|
|
1656
|
+
{ role: "performance-budget", revisionRef: workbench.performanceBudgetRef },
|
|
1197
1657
|
{
|
|
1198
|
-
role:
|
|
1658
|
+
role: "observation-policy",
|
|
1199
1659
|
revisionRef: workbench.observationPolicyRevisionRef,
|
|
1200
1660
|
},
|
|
1201
|
-
{ role:
|
|
1661
|
+
{ role: "compiler", revisionRef: compilerRevisionRef },
|
|
1202
1662
|
];
|
|
1203
1663
|
const compileWorkbenchRuntimeBundle = (input) => {
|
|
1204
1664
|
const workbench = declarative_control_1.WorkbenchSchema.parse(input.workbench);
|
|
@@ -1207,20 +1667,20 @@ const compileWorkbenchRuntimeBundle = (input) => {
|
|
|
1207
1667
|
const releaseRevisionRef = declarative_control_1.RevisionRefSchema.parse(input.releaseRevisionRef);
|
|
1208
1668
|
const compilerRevisionRef = declarative_control_1.RevisionRefSchema.parse(input.compilerRevisionRef);
|
|
1209
1669
|
const scope = declarative_control_1.TenantScopeSchema.parse(workbench.tenantScope);
|
|
1210
|
-
assertSameTenantScope(release.tenantScope, scope,
|
|
1211
|
-
if (workbench.purpose ===
|
|
1212
|
-
throw new DeclarativeControlCompilationError(
|
|
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.");
|
|
1213
1673
|
if (workbench.groups.length > input.limits.maxWorkbenchGroups ||
|
|
1214
1674
|
workbench.appInstances.length > input.limits.maxWorkbenchInstances) {
|
|
1215
|
-
throw new DeclarativeControlCompilationError(
|
|
1675
|
+
throw new DeclarativeControlCompilationError("WORKBENCH_INSTANCE_LIMIT_EXCEEDED", "workbench", "Workbench exceeds caller-approved compilation limits.");
|
|
1216
1676
|
}
|
|
1217
|
-
assertRevisionIdentity(workbenchRevisionRef,
|
|
1218
|
-
assertRevisionIdentity(release.workbenchRevisionRef,
|
|
1677
|
+
assertRevisionIdentity(workbenchRevisionRef, "workbench", workbench.workbenchId, scope, "workbenchRevisionRef");
|
|
1678
|
+
assertRevisionIdentity(release.workbenchRevisionRef, "workbench", workbench.workbenchId, scope, "release.workbenchRevisionRef");
|
|
1219
1679
|
if (!(0, exports.sameRevisionRef)(workbenchRevisionRef, release.workbenchRevisionRef))
|
|
1220
|
-
throw new DeclarativeControlCompilationError(
|
|
1221
|
-
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");
|
|
1222
1682
|
if (release.target.workbenchId !== workbench.workbenchId)
|
|
1223
|
-
throw new DeclarativeControlCompilationError(
|
|
1683
|
+
throw new DeclarativeControlCompilationError("REVISION_MISMATCH", "release.target.workbenchId", "Release target does not match Workbench identity.");
|
|
1224
1684
|
requireReleaseReady(release);
|
|
1225
1685
|
requireDependencies(release.dependencySnapshot, workbenchDependencies(workbench, compilerRevisionRef));
|
|
1226
1686
|
assertDependencyScopes(release.dependencySnapshot, scope);
|
|
@@ -1228,21 +1688,27 @@ const compileWorkbenchRuntimeBundle = (input) => {
|
|
|
1228
1688
|
claims: workbench.routeClaims,
|
|
1229
1689
|
routeSpaces: input.routeSpaces,
|
|
1230
1690
|
surface: release.target.surface,
|
|
1231
|
-
legacyRouteTakeoverAuthorizations: release.target
|
|
1691
|
+
legacyRouteTakeoverAuthorizations: release.target
|
|
1692
|
+
.legacyRouteTakeoverAuthorization
|
|
1232
1693
|
? [release.target.legacyRouteTakeoverAuthorization]
|
|
1233
1694
|
: [],
|
|
1234
1695
|
});
|
|
1235
|
-
const releaseRoute = routeClaims.find((claim) => claim.kind ===
|
|
1236
|
-
|
|
1237
|
-
|
|
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.");
|
|
1238
1701
|
}
|
|
1239
1702
|
const targetRegistry = input.targetRegistry.map((registration) => ({
|
|
1240
1703
|
stableTargetRef: declarative_control_1.StableRefSchema.parse(registration.stableTargetRef),
|
|
1241
1704
|
targetRevisionRef: declarative_control_1.RevisionRefSchema.parse(registration.targetRevisionRef),
|
|
1242
1705
|
accessPolicy: declarative_control_1.AccessPolicySchema.parse(registration.accessPolicy),
|
|
1243
1706
|
}));
|
|
1244
|
-
assertUniqueRegistryKeys(targetRegistry.map((registration) => (0, exports.stableRefKey)(registration.stableTargetRef)),
|
|
1245
|
-
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
|
+
]));
|
|
1246
1712
|
const groupsById = new Map(workbench.groups.map((group) => [group.groupId, group]));
|
|
1247
1713
|
const appInstances = workbench.appInstances.map((instance, index) => {
|
|
1248
1714
|
const stableTarget = declarative_control_1.StableRefSchema.parse({
|
|
@@ -1250,13 +1716,15 @@ const compileWorkbenchRuntimeBundle = (input) => {
|
|
|
1250
1716
|
id: instance.targetRevisionRef.id,
|
|
1251
1717
|
ownerRepo: instance.targetRevisionRef.ownerRepo,
|
|
1252
1718
|
visibility: instance.targetRevisionRef.visibility,
|
|
1253
|
-
...(instance.targetRevisionRef.tenantScope
|
|
1719
|
+
...(instance.targetRevisionRef.tenantScope
|
|
1720
|
+
? { tenantScope: instance.targetRevisionRef.tenantScope }
|
|
1721
|
+
: {}),
|
|
1254
1722
|
});
|
|
1255
1723
|
const registration = targetsByStableRef.get((0, exports.stableRefKey)(stableTarget));
|
|
1256
1724
|
if (!registration ||
|
|
1257
1725
|
!(0, exports.sameStableRef)(registration.stableTargetRef, stableFromRevision(registration.targetRevisionRef)) ||
|
|
1258
1726
|
!(0, exports.sameRevisionRef)(registration.targetRevisionRef, instance.targetRevisionRef)) {
|
|
1259
|
-
throw new DeclarativeControlCompilationError(
|
|
1727
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", `appInstances[${index}].targetRevisionRef`, "Workbench app target is not present at the exact registered revision.");
|
|
1260
1728
|
}
|
|
1261
1729
|
assertTenantCompatible(instance.targetRevisionRef, scope, `appInstances[${index}].targetRevisionRef`);
|
|
1262
1730
|
const group = groupsById.get(instance.groupId);
|
|
@@ -1266,12 +1734,12 @@ const compileWorkbenchRuntimeBundle = (input) => {
|
|
|
1266
1734
|
instance.accessPolicy,
|
|
1267
1735
|
];
|
|
1268
1736
|
if (!(0, exports.accessPolicyChainImplies)(chain, registration.accessPolicy)) {
|
|
1269
|
-
throw new DeclarativeControlCompilationError(
|
|
1737
|
+
throw new DeclarativeControlCompilationError("AUDIENCE_WIDER_THAN_TARGET", `appInstances[${index}].accessPolicy`, "Workbench and instance audience is wider than the target audience.");
|
|
1270
1738
|
}
|
|
1271
1739
|
return { ...instance, targetAccessPolicy: registration.accessPolicy };
|
|
1272
1740
|
});
|
|
1273
1741
|
const unsigned = {
|
|
1274
|
-
contract:
|
|
1742
|
+
contract: "WorkbenchRuntimeBundle",
|
|
1275
1743
|
schemaVersion: 1,
|
|
1276
1744
|
tenantScope: scope,
|
|
1277
1745
|
releaseRevisionRef,
|
|
@@ -1284,7 +1752,9 @@ const compileWorkbenchRuntimeBundle = (input) => {
|
|
|
1284
1752
|
workbenchRevisionRef,
|
|
1285
1753
|
identity: {
|
|
1286
1754
|
name: workbench.identity.name,
|
|
1287
|
-
...(workbench.identity.description
|
|
1755
|
+
...(workbench.identity.description
|
|
1756
|
+
? { description: workbench.identity.description }
|
|
1757
|
+
: {}),
|
|
1288
1758
|
},
|
|
1289
1759
|
surface: release.target.surface,
|
|
1290
1760
|
routeClaims,
|
|
@@ -1322,7 +1792,7 @@ const navigationDepth = (navigation) => {
|
|
|
1322
1792
|
const orderedNavigationNodes = (navigation) => {
|
|
1323
1793
|
const children = new Map();
|
|
1324
1794
|
navigation.nodes.forEach((node) => {
|
|
1325
|
-
const parent = node.parentNodeId ??
|
|
1795
|
+
const parent = node.parentNodeId ?? "";
|
|
1326
1796
|
const siblings = children.get(parent) ?? [];
|
|
1327
1797
|
children.set(parent, [...siblings, node]);
|
|
1328
1798
|
});
|
|
@@ -1334,14 +1804,14 @@ const orderedNavigationNodes = (navigation) => {
|
|
|
1334
1804
|
append(node.nodeId);
|
|
1335
1805
|
}
|
|
1336
1806
|
};
|
|
1337
|
-
append(
|
|
1807
|
+
append("");
|
|
1338
1808
|
return ordered;
|
|
1339
1809
|
};
|
|
1340
1810
|
const navigationDependencies = (navigation, release, compilerRevisionRef) => [
|
|
1341
1811
|
...release.resolvedTargets.flatMap((target) => [
|
|
1342
1812
|
{
|
|
1343
|
-
role: target.targetRevisionRef.kind ===
|
|
1344
|
-
?
|
|
1813
|
+
role: target.targetRevisionRef.kind === "domain-command"
|
|
1814
|
+
? "action"
|
|
1345
1815
|
: target.targetRevisionRef.kind,
|
|
1346
1816
|
revisionRef: target.targetRevisionRef,
|
|
1347
1817
|
},
|
|
@@ -1354,12 +1824,12 @@ const navigationDependencies = (navigation, release, compilerRevisionRef) => [
|
|
|
1354
1824
|
]
|
|
1355
1825
|
: []),
|
|
1356
1826
|
]),
|
|
1357
|
-
{ role:
|
|
1827
|
+
{ role: "performance-budget", revisionRef: navigation.performanceBudgetRef },
|
|
1358
1828
|
{
|
|
1359
|
-
role:
|
|
1829
|
+
role: "observation-policy",
|
|
1360
1830
|
revisionRef: navigation.observationPolicyRevisionRef,
|
|
1361
1831
|
},
|
|
1362
|
-
{ role:
|
|
1832
|
+
{ role: "compiler", revisionRef: compilerRevisionRef },
|
|
1363
1833
|
];
|
|
1364
1834
|
const compileNavigationRuntimeBundle = (input) => {
|
|
1365
1835
|
const navigation = declarative_control_1.NavigationSchema.parse(input.navigation);
|
|
@@ -1368,19 +1838,20 @@ const compileNavigationRuntimeBundle = (input) => {
|
|
|
1368
1838
|
const releaseRevisionRef = declarative_control_1.RevisionRefSchema.parse(input.releaseRevisionRef);
|
|
1369
1839
|
const compilerRevisionRef = declarative_control_1.RevisionRefSchema.parse(input.compilerRevisionRef);
|
|
1370
1840
|
const scope = declarative_control_1.TenantScopeSchema.parse(navigation.tenantScope);
|
|
1371
|
-
assertSameTenantScope(release.tenantScope, scope,
|
|
1841
|
+
assertSameTenantScope(release.tenantScope, scope, "release.tenantScope");
|
|
1372
1842
|
if (navigation.nodes.length > input.limits.maxNavigationNodes)
|
|
1373
|
-
throw new DeclarativeControlCompilationError(
|
|
1843
|
+
throw new DeclarativeControlCompilationError("NAV_NODE_LIMIT_EXCEEDED", "navigation.nodes", "Navigation exceeds the caller-approved node limit.");
|
|
1374
1844
|
if (navigationDepth(navigation) > input.limits.maxNavigationDepth)
|
|
1375
|
-
throw new DeclarativeControlCompilationError(
|
|
1376
|
-
assertRevisionIdentity(navigationRevisionRef,
|
|
1377
|
-
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");
|
|
1378
1848
|
if (!(0, exports.sameRevisionRef)(navigationRevisionRef, release.navigationRevisionRef))
|
|
1379
|
-
throw new DeclarativeControlCompilationError(
|
|
1380
|
-
assertRevisionIdentity(releaseRevisionRef,
|
|
1381
|
-
if (release.target.surface !==
|
|
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) ||
|
|
1382
1853
|
!navigation.placements.includes(release.target.placement)) {
|
|
1383
|
-
throw new DeclarativeControlCompilationError(
|
|
1854
|
+
throw new DeclarativeControlCompilationError("REVISION_MISMATCH", "release.target", "Navigation release target is not declared by the Navigation record.");
|
|
1384
1855
|
}
|
|
1385
1856
|
requireReleaseReady(release);
|
|
1386
1857
|
requireDependencies(release.dependencySnapshot, navigationDependencies(navigation, release, compilerRevisionRef));
|
|
@@ -1392,32 +1863,32 @@ const compileNavigationRuntimeBundle = (input) => {
|
|
|
1392
1863
|
surface: declarative_control_1.ProductSurfaceSchema.parse(registration.surface),
|
|
1393
1864
|
accessPolicy: declarative_control_1.AccessPolicySchema.parse(registration.accessPolicy),
|
|
1394
1865
|
};
|
|
1395
|
-
if (registration.kind ===
|
|
1866
|
+
if (registration.kind === "route") {
|
|
1396
1867
|
const routeClaim = declarative_control_1.CompiledRouteClaimSchema.parse(registration.routeClaim);
|
|
1397
1868
|
if (routeClaim.surface !== common.surface) {
|
|
1398
|
-
throw new DeclarativeControlCompilationError(
|
|
1869
|
+
throw new DeclarativeControlCompilationError("ROUTE_SPACE_MISMATCH", `targetRegistry[${index}].routeClaim.surface`, "Navigation route claim must match its registered surface.");
|
|
1399
1870
|
}
|
|
1400
1871
|
return {
|
|
1401
1872
|
...common,
|
|
1402
|
-
kind:
|
|
1873
|
+
kind: "route",
|
|
1403
1874
|
releaseRevisionRef: registration.releaseRevisionRef
|
|
1404
1875
|
? declarative_control_1.RevisionRefSchema.parse(registration.releaseRevisionRef)
|
|
1405
1876
|
: undefined,
|
|
1406
1877
|
routeClaim,
|
|
1407
1878
|
};
|
|
1408
1879
|
}
|
|
1409
|
-
if (registration.kind ===
|
|
1880
|
+
if (registration.kind === "registered-menu-action") {
|
|
1410
1881
|
const sourceCatalogRevisionRef = declarative_control_1.RevisionRefSchema.parse(registration.sourceCatalogRevisionRef);
|
|
1411
|
-
if (sourceCatalogRevisionRef.kind !==
|
|
1412
|
-
registration.targetRevisionRef.kind !==
|
|
1413
|
-
throw new DeclarativeControlCompilationError(
|
|
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.");
|
|
1414
1885
|
}
|
|
1415
1886
|
if (registration.inputSchemaRef && !registration.validateInput) {
|
|
1416
|
-
throw new DeclarativeControlCompilationError(
|
|
1887
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", "targetRegistry", "Registered Menu Actions with input schemas require the active catalog validator.");
|
|
1417
1888
|
}
|
|
1418
1889
|
return {
|
|
1419
1890
|
...common,
|
|
1420
|
-
kind:
|
|
1891
|
+
kind: "registered-menu-action",
|
|
1421
1892
|
applicationId: registration.applicationId,
|
|
1422
1893
|
actionRef: registration.actionRef,
|
|
1423
1894
|
inputSchemaRef: registration.inputSchemaRef,
|
|
@@ -1425,33 +1896,37 @@ const compileNavigationRuntimeBundle = (input) => {
|
|
|
1425
1896
|
sourceCatalogRevisionRef,
|
|
1426
1897
|
};
|
|
1427
1898
|
}
|
|
1428
|
-
if (registration.targetRevisionRef.kind !==
|
|
1429
|
-
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.");
|
|
1430
1901
|
}
|
|
1431
1902
|
const inputSchemaRevisionRef = declarative_control_1.RevisionRefSchema.parse(registration.inputSchemaRevisionRef);
|
|
1432
1903
|
const resultSchemaRevisionRef = registration.resultSchemaRevisionRef
|
|
1433
1904
|
? declarative_control_1.RevisionRefSchema.parse(registration.resultSchemaRevisionRef)
|
|
1434
1905
|
: undefined;
|
|
1435
|
-
if (inputSchemaRevisionRef.kind !==
|
|
1436
|
-
(resultSchemaRevisionRef && resultSchemaRevisionRef.kind !==
|
|
1437
|
-
throw new DeclarativeControlCompilationError(
|
|
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.");
|
|
1438
1909
|
}
|
|
1439
1910
|
return {
|
|
1440
1911
|
...common,
|
|
1441
|
-
kind:
|
|
1912
|
+
kind: "governed-domain-command",
|
|
1442
1913
|
inputSchemaRevisionRef,
|
|
1443
1914
|
resultSchemaRevisionRef,
|
|
1444
1915
|
};
|
|
1445
1916
|
});
|
|
1446
|
-
assertUniqueRegistryKeys(targetRegistry.map((registration) => (0, exports.stableRefKey)(registration.stableTargetRef)),
|
|
1447
|
-
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
|
+
]));
|
|
1448
1922
|
const releasedByNodeId = new Map(release.resolvedTargets.map((target) => [target.nodeId, target]));
|
|
1449
1923
|
const unavailableByNodeId = new Map((release.unavailableTargets ?? []).map((target) => [target.nodeId, target]));
|
|
1450
1924
|
const nodesById = new Map(navigation.nodes.map((node) => [node.nodeId, node]));
|
|
1451
1925
|
for (const target of release.unavailableTargets ?? []) {
|
|
1452
1926
|
const node = nodesById.get(target.nodeId);
|
|
1453
|
-
if (node?.kind !==
|
|
1454
|
-
|
|
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.");
|
|
1455
1930
|
}
|
|
1456
1931
|
}
|
|
1457
1932
|
const nodes = orderedNavigationNodes(navigation).map((node, index) => {
|
|
@@ -1459,22 +1934,26 @@ const compileNavigationRuntimeBundle = (input) => {
|
|
|
1459
1934
|
let parentId = node.parentNodeId;
|
|
1460
1935
|
while (parentId) {
|
|
1461
1936
|
const parent = nodesById.get(parentId);
|
|
1462
|
-
if (parent?.kind ===
|
|
1937
|
+
if (parent?.kind === "group")
|
|
1463
1938
|
ancestors.unshift(parent.audience);
|
|
1464
1939
|
parentId = parent?.parentNodeId ?? null;
|
|
1465
1940
|
}
|
|
1466
|
-
if (node.kind !==
|
|
1941
|
+
if (node.kind !== "target")
|
|
1467
1942
|
return { ...node, ancestorAccessPolicies: ancestors };
|
|
1468
|
-
if (![
|
|
1469
|
-
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.");
|
|
1470
1945
|
}
|
|
1471
1946
|
const registration = targetsByStableRef.get((0, exports.stableRefKey)(node.targetRef));
|
|
1472
1947
|
const released = releasedByNodeId.get(node.nodeId);
|
|
1473
1948
|
const unavailable = unavailableByNodeId.get(node.nodeId);
|
|
1474
|
-
if (!registration &&
|
|
1949
|
+
if (!registration &&
|
|
1950
|
+
unavailable &&
|
|
1951
|
+
node.targetRef.kind === "page" &&
|
|
1952
|
+
(0, exports.sameStableRef)(unavailable.stableTargetRef, node.targetRef)) {
|
|
1475
1953
|
assertTenantCompatible(node.targetRef, scope, `nodes[${index}].targetRef`);
|
|
1476
|
-
if (unavailable.accessPolicy &&
|
|
1477
|
-
|
|
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.");
|
|
1478
1957
|
}
|
|
1479
1958
|
return {
|
|
1480
1959
|
...node,
|
|
@@ -1482,84 +1961,95 @@ const compileNavigationRuntimeBundle = (input) => {
|
|
|
1482
1961
|
parameterMapping: {},
|
|
1483
1962
|
ancestorAccessPolicies: ancestors,
|
|
1484
1963
|
resolvedTarget: {
|
|
1485
|
-
kind:
|
|
1964
|
+
kind: "unavailable",
|
|
1486
1965
|
nodeId: node.nodeId,
|
|
1487
1966
|
stableTargetRef: node.targetRef,
|
|
1488
1967
|
accessPolicy: unavailable.accessPolicy,
|
|
1489
1968
|
},
|
|
1490
1969
|
};
|
|
1491
1970
|
}
|
|
1492
|
-
if (!registration ||
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
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.");
|
|
1499
1983
|
}
|
|
1500
1984
|
if (registration.surface !== release.target.surface) {
|
|
1501
|
-
throw new DeclarativeControlCompilationError(
|
|
1985
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", `nodes[${index}].targetRef`, "Navigation target is active on another surface.");
|
|
1502
1986
|
}
|
|
1503
1987
|
assertTenantCompatible(registration.targetRevisionRef, scope, `nodes[${index}].targetRef`);
|
|
1504
1988
|
if (!(0, exports.accessPolicyChainImplies)([...ancestors, node.audience], registration.accessPolicy)) {
|
|
1505
|
-
throw new DeclarativeControlCompilationError(
|
|
1989
|
+
throw new DeclarativeControlCompilationError("AUDIENCE_WIDER_THAN_TARGET", `nodes[${index}].audience`, "Effective Navigation audience is wider than the target audience.");
|
|
1506
1990
|
}
|
|
1507
1991
|
const mappedInput = Object.fromEntries(Object.entries(node.parameterMapping).map(([name, source]) => {
|
|
1508
|
-
if (source.kind !==
|
|
1509
|
-
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.");
|
|
1510
1994
|
}
|
|
1511
1995
|
return [name, source.value];
|
|
1512
1996
|
}));
|
|
1513
1997
|
let resolvedTarget;
|
|
1514
|
-
if (registration.kind ===
|
|
1998
|
+
if (registration.kind === "route") {
|
|
1515
1999
|
resolvedTarget = {
|
|
1516
2000
|
...released,
|
|
1517
|
-
kind:
|
|
2001
|
+
kind: "route",
|
|
1518
2002
|
accessPolicy: registration.accessPolicy,
|
|
1519
2003
|
routeClaim: registration.routeClaim,
|
|
1520
2004
|
};
|
|
1521
2005
|
}
|
|
1522
|
-
else if (registration.kind ===
|
|
2006
|
+
else if (registration.kind === "registered-menu-action") {
|
|
1523
2007
|
if (!registration.inputSchemaRef && Object.keys(mappedInput).length > 0) {
|
|
1524
|
-
throw new DeclarativeControlCompilationError(
|
|
2008
|
+
throw new DeclarativeControlCompilationError("NAV_TARGET_UNRELEASED", `nodes[${index}].parameterMapping`, "The registered Menu Action does not accept input.");
|
|
1525
2009
|
}
|
|
1526
2010
|
let input;
|
|
1527
2011
|
try {
|
|
1528
|
-
input = common_1.JsonObjectSchema.parse(registration.validateInput
|
|
2012
|
+
input = common_1.JsonObjectSchema.parse(registration.validateInput
|
|
2013
|
+
? registration.validateInput(mappedInput)
|
|
2014
|
+
: mappedInput);
|
|
1529
2015
|
}
|
|
1530
2016
|
catch (error) {
|
|
1531
|
-
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)}`);
|
|
1532
2018
|
}
|
|
1533
|
-
const sourceDependency = release.dependencySnapshot.find((dependency) => dependency.role ===
|
|
2019
|
+
const sourceDependency = release.dependencySnapshot.find((dependency) => dependency.role === "application-menu-catalog" &&
|
|
1534
2020
|
(0, exports.sameRevisionRef)(dependency.revisionRef, registration.sourceCatalogRevisionRef));
|
|
1535
2021
|
if (!sourceDependency)
|
|
1536
|
-
throw new DeclarativeControlCompilationError(
|
|
2022
|
+
throw new DeclarativeControlCompilationError("DEPENDENCY_MISSING", `nodes[${index}].targetRef`, "Registered Menu Action source catalog is not pinned by NavigationRelease.");
|
|
1537
2023
|
resolvedTarget = {
|
|
1538
2024
|
...released,
|
|
1539
|
-
kind:
|
|
2025
|
+
kind: "registered-menu-action",
|
|
1540
2026
|
accessPolicy: registration.accessPolicy,
|
|
1541
|
-
execution:
|
|
2027
|
+
execution: "client",
|
|
1542
2028
|
applicationId: registration.applicationId,
|
|
1543
2029
|
actionRef: registration.actionRef,
|
|
1544
|
-
...(registration.inputSchemaRef
|
|
2030
|
+
...(registration.inputSchemaRef
|
|
2031
|
+
? { inputSchemaRef: registration.inputSchemaRef }
|
|
2032
|
+
: {}),
|
|
1545
2033
|
input,
|
|
1546
2034
|
sourceCatalogRevisionRef: registration.sourceCatalogRevisionRef,
|
|
1547
2035
|
};
|
|
1548
2036
|
}
|
|
1549
2037
|
else {
|
|
1550
|
-
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));
|
|
1551
2040
|
const resultDependency = registration.resultSchemaRevisionRef
|
|
1552
|
-
? release.dependencySnapshot.find((dependency) => dependency.role ===
|
|
2041
|
+
? release.dependencySnapshot.find((dependency) => dependency.role === "schema" &&
|
|
1553
2042
|
(0, exports.sameRevisionRef)(dependency.revisionRef, registration.resultSchemaRevisionRef))
|
|
1554
2043
|
: undefined;
|
|
1555
|
-
if (!inputDependency ||
|
|
1556
|
-
|
|
2044
|
+
if (!inputDependency ||
|
|
2045
|
+
(registration.resultSchemaRevisionRef && !resultDependency)) {
|
|
2046
|
+
throw new DeclarativeControlCompilationError("DEPENDENCY_MISSING", `nodes[${index}].targetRef`, "Governed DomainCommand schemas are not pinned by NavigationRelease.");
|
|
1557
2047
|
}
|
|
1558
2048
|
resolvedTarget = {
|
|
1559
2049
|
...released,
|
|
1560
|
-
kind:
|
|
2050
|
+
kind: "governed-domain-command",
|
|
1561
2051
|
accessPolicy: registration.accessPolicy,
|
|
1562
|
-
execution:
|
|
2052
|
+
execution: "server",
|
|
1563
2053
|
commandRevisionRef: registration.targetRevisionRef,
|
|
1564
2054
|
inputSchemaRevisionRef: registration.inputSchemaRevisionRef,
|
|
1565
2055
|
...(registration.resultSchemaRevisionRef
|
|
@@ -1575,7 +2065,7 @@ const compileNavigationRuntimeBundle = (input) => {
|
|
|
1575
2065
|
};
|
|
1576
2066
|
});
|
|
1577
2067
|
const unsigned = {
|
|
1578
|
-
contract:
|
|
2068
|
+
contract: "NavigationRuntimeBundle",
|
|
1579
2069
|
schemaVersion: 1,
|
|
1580
2070
|
tenantScope: scope,
|
|
1581
2071
|
releaseRevisionRef,
|
|
@@ -1601,7 +2091,7 @@ exports.compileNavigationRuntimeBundle = compileNavigationRuntimeBundle;
|
|
|
1601
2091
|
const validateWorkbenchRuntimeSet = (bundleInputs) => {
|
|
1602
2092
|
const bundles = bundleInputs.map((bundle) => declarative_control_1.WorkbenchRuntimeBundleSchema.parse(bundle));
|
|
1603
2093
|
if (bundles.length === 0) {
|
|
1604
|
-
throw new DeclarativeControlCompilationError(
|
|
2094
|
+
throw new DeclarativeControlCompilationError("WORKBENCH_RELEASE_SET_INVALID", "bundles", "An active Workbench release set must retain at least one Workbench.");
|
|
1605
2095
|
}
|
|
1606
2096
|
const first = bundles[0];
|
|
1607
2097
|
const seenIds = new Set();
|
|
@@ -1611,44 +2101,46 @@ const validateWorkbenchRuntimeSet = (bundleInputs) => {
|
|
|
1611
2101
|
if (scopeKey(bundle.tenantScope) !== scopeKey(first.tenantScope) ||
|
|
1612
2102
|
bundle.surface !== first.surface ||
|
|
1613
2103
|
!(0, exports.sameStableRef)(bundle.target.environmentRef, first.target.environmentRef)) {
|
|
1614
|
-
throw new DeclarativeControlCompilationError(
|
|
2104
|
+
throw new DeclarativeControlCompilationError("WORKBENCH_RELEASE_SET_INVALID", `bundles[${index}]`, "Workbench release set entries must share tenant, environment, and surface.");
|
|
1615
2105
|
}
|
|
1616
2106
|
if (seenIds.has(bundle.workbenchId))
|
|
1617
|
-
throw new DeclarativeControlCompilationError(
|
|
2107
|
+
throw new DeclarativeControlCompilationError("WORKBENCH_RELEASE_SET_INVALID", `bundles[${index}].workbenchId`, "Workbench identities must be unique in an active release set.");
|
|
1618
2108
|
if (seenOrders.has(bundle.target.order))
|
|
1619
|
-
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.");
|
|
1620
2110
|
seenIds.add(bundle.workbenchId);
|
|
1621
2111
|
seenOrders.add(bundle.target.order);
|
|
1622
2112
|
if (bundle.target.isDefaultCandidate)
|
|
1623
2113
|
defaultCount += 1;
|
|
1624
2114
|
});
|
|
1625
2115
|
if (defaultCount > 1)
|
|
1626
|
-
throw new DeclarativeControlCompilationError(
|
|
1627
|
-
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)));
|
|
1628
2119
|
};
|
|
1629
2120
|
exports.validateWorkbenchRuntimeSet = validateWorkbenchRuntimeSet;
|
|
1630
2121
|
const resolveWorkbenchEntry = (input) => {
|
|
1631
2122
|
if (input.requestedWorkbenchId) {
|
|
1632
2123
|
const requested = input.bundles.find((bundle) => bundle.workbenchId === input.requestedWorkbenchId);
|
|
1633
2124
|
if (!requested)
|
|
1634
|
-
return { status:
|
|
2125
|
+
return { status: "not-found", workbenchId: input.requestedWorkbenchId };
|
|
1635
2126
|
if (!input.isAllowed(requested))
|
|
1636
|
-
return { status:
|
|
1637
|
-
return { status:
|
|
2127
|
+
return { status: "forbidden", workbenchId: input.requestedWorkbenchId };
|
|
2128
|
+
return { status: "resolved", bundle: requested, source: "explicit" };
|
|
1638
2129
|
}
|
|
1639
2130
|
const allowed = input.bundles
|
|
1640
2131
|
.filter(input.isAllowed)
|
|
1641
|
-
.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));
|
|
1642
2134
|
const defaultBundle = allowed.find((bundle) => bundle.target.isDefaultCandidate);
|
|
1643
2135
|
if (defaultBundle)
|
|
1644
|
-
return { status:
|
|
2136
|
+
return { status: "resolved", bundle: defaultBundle, source: "default" };
|
|
1645
2137
|
if (allowed[0])
|
|
1646
2138
|
return {
|
|
1647
|
-
status:
|
|
2139
|
+
status: "resolved",
|
|
1648
2140
|
bundle: allowed[0],
|
|
1649
|
-
source:
|
|
2141
|
+
source: "first-accessible",
|
|
1650
2142
|
};
|
|
1651
|
-
return { status:
|
|
2143
|
+
return { status: "forbidden", workbenchId: "" };
|
|
1652
2144
|
};
|
|
1653
2145
|
exports.resolveWorkbenchEntry = resolveWorkbenchEntry;
|
|
1654
2146
|
//# sourceMappingURL=declarative-control-compiler.js.map
|