@xemahq/kernel-contracts 0.33.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-runtime/index.d.ts +4 -0
- package/dist/app-runtime/index.d.ts.map +1 -1
- package/dist/app-runtime/index.js +4 -0
- package/dist/app-runtime/index.js.map +1 -1
- package/dist/app-runtime/lib/app-channel.d.ts +7 -0
- package/dist/app-runtime/lib/app-channel.d.ts.map +1 -0
- package/dist/app-runtime/lib/app-channel.js +11 -0
- package/dist/app-runtime/lib/app-channel.js.map +1 -0
- package/dist/app-runtime/lib/app-release.d.ts +24 -0
- package/dist/app-runtime/lib/app-release.d.ts.map +1 -0
- package/dist/app-runtime/lib/app-release.js +29 -0
- package/dist/app-runtime/lib/app-release.js.map +1 -0
- package/dist/app-runtime/lib/app-shell-binding.d.ts +38 -0
- package/dist/app-runtime/lib/app-shell-binding.d.ts.map +1 -0
- package/dist/app-runtime/lib/app-shell-binding.js +58 -0
- package/dist/app-runtime/lib/app-shell-binding.js.map +1 -0
- package/dist/app-runtime/lib/app.d.ts +3 -0
- package/dist/app-runtime/lib/app.d.ts.map +1 -1
- package/dist/app-runtime/lib/app.js +19 -4
- package/dist/app-runtime/lib/app.js.map +1 -1
- package/dist/app-runtime/lib/branding-config.d.ts.map +1 -1
- package/dist/app-runtime/lib/branding-config.js.map +1 -1
- package/dist/app-runtime/lib/delegated-session.js +1 -1
- package/dist/app-runtime/lib/delegated-session.js.map +1 -1
- package/dist/app-runtime/lib/surface-kind.d.ts +8 -0
- package/dist/app-runtime/lib/surface-kind.d.ts.map +1 -0
- package/dist/app-runtime/lib/surface-kind.js +12 -0
- package/dist/app-runtime/lib/surface-kind.js.map +1 -0
- package/dist/capability/index.d.ts +1 -0
- package/dist/capability/index.d.ts.map +1 -1
- package/dist/capability/index.js +1 -0
- package/dist/capability/index.js.map +1 -1
- package/dist/capability/lib/first-party-capability-domains.d.ts +29 -0
- package/dist/capability/lib/first-party-capability-domains.d.ts.map +1 -0
- package/dist/capability/lib/first-party-capability-domains.js +35 -0
- package/dist/capability/lib/first-party-capability-domains.js.map +1 -0
- package/dist/principal/index.d.ts +6 -0
- package/dist/principal/index.d.ts.map +1 -0
- package/dist/principal/index.js +22 -0
- package/dist/principal/index.js.map +1 -0
- package/dist/principal/lib/app-invocation-identity.d.ts +22 -0
- package/dist/principal/lib/app-invocation-identity.d.ts.map +1 -0
- package/dist/principal/lib/app-invocation-identity.js +36 -0
- package/dist/principal/lib/app-invocation-identity.js.map +1 -0
- package/dist/principal/lib/clamp-authority.d.ts +4 -0
- package/dist/principal/lib/clamp-authority.d.ts.map +1 -0
- package/dist/principal/lib/clamp-authority.js +23 -0
- package/dist/principal/lib/clamp-authority.js.map +1 -0
- package/dist/principal/lib/delegation-chain.d.ts +26 -0
- package/dist/principal/lib/delegation-chain.d.ts.map +1 -0
- package/dist/principal/lib/delegation-chain.js +81 -0
- package/dist/principal/lib/delegation-chain.js.map +1 -0
- package/dist/principal/lib/delegation-grant.d.ts +10 -0
- package/dist/principal/lib/delegation-grant.d.ts.map +1 -0
- package/dist/principal/lib/delegation-grant.js +12 -0
- package/dist/principal/lib/delegation-grant.js.map +1 -0
- package/dist/principal/lib/execution-principal.d.ts +20 -0
- package/dist/principal/lib/execution-principal.d.ts.map +1 -0
- package/dist/principal/lib/execution-principal.js +22 -0
- package/dist/principal/lib/execution-principal.js.map +1 -0
- package/package.json +1 -1
- package/src/app-runtime/index.ts +4 -0
- package/src/app-runtime/lib/app-channel.ts +32 -0
- package/src/app-runtime/lib/app-release.ts +90 -0
- package/src/app-runtime/lib/app-shell-binding.ts +159 -0
- package/src/app-runtime/lib/app.ts +48 -17
- package/src/app-runtime/lib/branding-config.ts +12 -6
- package/src/app-runtime/lib/delegated-session.ts +2 -2
- package/src/app-runtime/lib/surface-kind.ts +30 -0
- package/src/capability/index.ts +1 -0
- package/src/capability/lib/first-party-capability-domains.ts +76 -0
- package/src/principal/index.ts +5 -0
- package/src/principal/lib/app-invocation-identity.ts +95 -0
- package/src/principal/lib/clamp-authority.ts +64 -0
- package/src/principal/lib/delegation-chain.ts +145 -0
- package/src/principal/lib/delegation-grant.ts +36 -0
- package/src/principal/lib/execution-principal.ts +74 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.clampAuthority = clampAuthority;
|
|
4
|
+
exports.clampAuthorityChain = clampAuthorityChain;
|
|
5
|
+
function clampAuthority(executorGrants, actorEffective) {
|
|
6
|
+
const [smaller, larger] = executorGrants.size <= actorEffective.size
|
|
7
|
+
? [executorGrants, actorEffective]
|
|
8
|
+
: [actorEffective, executorGrants];
|
|
9
|
+
const intersection = new Set();
|
|
10
|
+
for (const ref of smaller) {
|
|
11
|
+
if (larger.has(ref)) {
|
|
12
|
+
intersection.add(ref);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return intersection;
|
|
16
|
+
}
|
|
17
|
+
function clampAuthorityChain(authoritySets) {
|
|
18
|
+
if (authoritySets.length === 0) {
|
|
19
|
+
return new Set();
|
|
20
|
+
}
|
|
21
|
+
return authoritySets.reduce((acc, next) => clampAuthority(acc, next), new Set(authoritySets[0]));
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=clamp-authority.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clamp-authority.js","sourceRoot":"","sources":["../../../src/principal/lib/clamp-authority.ts"],"names":[],"mappings":";;AA4BA,wCAgBC;AASD,kDAUC;AAnCD,SAAgB,cAAc,CAC5B,cAA0C,EAC1C,cAA0C;IAE1C,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,GACrB,cAAc,CAAC,IAAI,IAAI,cAAc,CAAC,IAAI;QACxC,CAAC,CAAC,CAAC,cAAc,EAAE,cAAc,CAAC;QAClC,CAAC,CAAC,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;IAEvC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAiB,CAAC;IAC9C,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AASD,SAAgB,mBAAmB,CACjC,aAAwD;IAExD,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,IAAI,GAAG,EAAiB,CAAC;IAClC,CAAC;IACD,OAAO,aAAa,CAAC,MAAM,CACzB,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,EACxC,IAAI,GAAG,CAAgB,aAAa,CAAC,CAAC,CAAC,CAAC,CACzC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type SubjectRef } from '../../subject';
|
|
3
|
+
import type { DelegationGrant } from './delegation-grant';
|
|
4
|
+
export declare const MAX_DELEGATION_DEPTH = 3;
|
|
5
|
+
export declare enum DelegationErrorCode {
|
|
6
|
+
ChainTooDeep = "DELEGATION_CHAIN_TOO_DEEP",
|
|
7
|
+
Cycle = "DELEGATION_CHAIN_CYCLE",
|
|
8
|
+
Discontinuous = "DELEGATION_CHAIN_DISCONTINUOUS",
|
|
9
|
+
NotAnchoredAtActor = "DELEGATION_CHAIN_NOT_ANCHORED_AT_ACTOR",
|
|
10
|
+
NotAnchoredAtExecutor = "DELEGATION_CHAIN_NOT_ANCHORED_AT_EXECUTOR",
|
|
11
|
+
EmptyChainMismatch = "DELEGATION_CHAIN_EMPTY_MISMATCH"
|
|
12
|
+
}
|
|
13
|
+
export declare const DelegationErrorCodeSchema: z.ZodEnum<typeof DelegationErrorCode>;
|
|
14
|
+
export declare class DelegationChainError extends Error {
|
|
15
|
+
readonly code: DelegationErrorCode;
|
|
16
|
+
constructor(args: {
|
|
17
|
+
code: DelegationErrorCode;
|
|
18
|
+
message: string;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export declare function resolveDelegationChain(args: {
|
|
22
|
+
actor: SubjectRef;
|
|
23
|
+
executor: SubjectRef;
|
|
24
|
+
chain: readonly DelegationGrant[];
|
|
25
|
+
}): readonly DelegationGrant[];
|
|
26
|
+
//# sourceMappingURL=delegation-chain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegation-chain.d.ts","sourceRoot":"","sources":["../../../src/principal/lib/delegation-chain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAQ1D,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAMtC,oBAAY,mBAAmB;IAE7B,YAAY,8BAA8B;IAE1C,KAAK,2BAA2B;IAEhC,aAAa,mCAAmC;IAEhD,kBAAkB,2CAA2C;IAE7D,qBAAqB,8CAA8C;IAMnE,kBAAkB,oCAAoC;CACvD;AAED,eAAO,MAAM,yBAAyB,uCAAoC,CAAC;AAM3E,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;gBAEvB,IAAI,EAAE;QAAE,IAAI,EAAE,mBAAmB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;CAKjE;AAuBD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,UAAU,CAAC;IACrB,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;CACnC,GAAG,SAAS,eAAe,EAAE,CAoE7B"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DelegationChainError = exports.DelegationErrorCodeSchema = exports.DelegationErrorCode = exports.MAX_DELEGATION_DEPTH = void 0;
|
|
4
|
+
exports.resolveDelegationChain = resolveDelegationChain;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const subject_1 = require("../../subject");
|
|
7
|
+
exports.MAX_DELEGATION_DEPTH = 3;
|
|
8
|
+
var DelegationErrorCode;
|
|
9
|
+
(function (DelegationErrorCode) {
|
|
10
|
+
DelegationErrorCode["ChainTooDeep"] = "DELEGATION_CHAIN_TOO_DEEP";
|
|
11
|
+
DelegationErrorCode["Cycle"] = "DELEGATION_CHAIN_CYCLE";
|
|
12
|
+
DelegationErrorCode["Discontinuous"] = "DELEGATION_CHAIN_DISCONTINUOUS";
|
|
13
|
+
DelegationErrorCode["NotAnchoredAtActor"] = "DELEGATION_CHAIN_NOT_ANCHORED_AT_ACTOR";
|
|
14
|
+
DelegationErrorCode["NotAnchoredAtExecutor"] = "DELEGATION_CHAIN_NOT_ANCHORED_AT_EXECUTOR";
|
|
15
|
+
DelegationErrorCode["EmptyChainMismatch"] = "DELEGATION_CHAIN_EMPTY_MISMATCH";
|
|
16
|
+
})(DelegationErrorCode || (exports.DelegationErrorCode = DelegationErrorCode = {}));
|
|
17
|
+
exports.DelegationErrorCodeSchema = zod_1.z.nativeEnum(DelegationErrorCode);
|
|
18
|
+
class DelegationChainError extends Error {
|
|
19
|
+
code;
|
|
20
|
+
constructor(args) {
|
|
21
|
+
super(args.message);
|
|
22
|
+
this.name = 'DelegationChainError';
|
|
23
|
+
this.code = args.code;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.DelegationChainError = DelegationChainError;
|
|
27
|
+
function subjectsEqual(a, b) {
|
|
28
|
+
return (0, subject_1.subjectRefToString)(a) === (0, subject_1.subjectRefToString)(b);
|
|
29
|
+
}
|
|
30
|
+
function resolveDelegationChain(args) {
|
|
31
|
+
const { actor, executor, chain } = args;
|
|
32
|
+
if (chain.length === 0) {
|
|
33
|
+
if (!subjectsEqual(actor, executor)) {
|
|
34
|
+
throw new DelegationChainError({
|
|
35
|
+
code: DelegationErrorCode.EmptyChainMismatch,
|
|
36
|
+
message: `Empty delegation chain requires actor === executor, but actor "${(0, subject_1.subjectRefToString)(actor)}" ≠ executor "${(0, subject_1.subjectRefToString)(executor)}".`,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return Object.freeze([]);
|
|
40
|
+
}
|
|
41
|
+
if (chain.length > exports.MAX_DELEGATION_DEPTH) {
|
|
42
|
+
throw new DelegationChainError({
|
|
43
|
+
code: DelegationErrorCode.ChainTooDeep,
|
|
44
|
+
message: `Delegation chain has ${chain.length} hops; the maximum is ${exports.MAX_DELEGATION_DEPTH}.`,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (!subjectsEqual(chain[0].from, actor)) {
|
|
48
|
+
throw new DelegationChainError({
|
|
49
|
+
code: DelegationErrorCode.NotAnchoredAtActor,
|
|
50
|
+
message: `Delegation chain must start at actor "${(0, subject_1.subjectRefToString)(actor)}", but its first hop starts at "${(0, subject_1.subjectRefToString)(chain[0].from)}".`,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const last = chain[chain.length - 1];
|
|
54
|
+
if (!subjectsEqual(last.to, executor)) {
|
|
55
|
+
throw new DelegationChainError({
|
|
56
|
+
code: DelegationErrorCode.NotAnchoredAtExecutor,
|
|
57
|
+
message: `Delegation chain must end at executor "${(0, subject_1.subjectRefToString)(executor)}", but its last hop ends at "${(0, subject_1.subjectRefToString)(last.to)}".`,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const seen = new Set();
|
|
61
|
+
const firstKey = (0, subject_1.subjectRefToString)(chain[0].from);
|
|
62
|
+
seen.add(firstKey);
|
|
63
|
+
for (let i = 0; i < chain.length; i += 1) {
|
|
64
|
+
if (i > 0 && !subjectsEqual(chain[i - 1].to, chain[i].from)) {
|
|
65
|
+
throw new DelegationChainError({
|
|
66
|
+
code: DelegationErrorCode.Discontinuous,
|
|
67
|
+
message: `Delegation chain is not contiguous at hop ${i}: previous "to" "${(0, subject_1.subjectRefToString)(chain[i - 1].to)}" ≠ this "from" "${(0, subject_1.subjectRefToString)(chain[i].from)}".`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const toKey = (0, subject_1.subjectRefToString)(chain[i].to);
|
|
71
|
+
if (seen.has(toKey)) {
|
|
72
|
+
throw new DelegationChainError({
|
|
73
|
+
code: DelegationErrorCode.Cycle,
|
|
74
|
+
message: `Delegation chain contains a cycle: subject "${toKey}" appears more than once.`,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
seen.add(toKey);
|
|
78
|
+
}
|
|
79
|
+
return Object.freeze([...chain]);
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=delegation-chain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegation-chain.js","sourceRoot":"","sources":["../../../src/principal/lib/delegation-chain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type SubjectRef } from '../../subject';
|
|
3
|
+
export interface DelegationGrant {
|
|
4
|
+
from: SubjectRef;
|
|
5
|
+
to: SubjectRef;
|
|
6
|
+
grantedAt: string;
|
|
7
|
+
reason?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const DelegationGrantSchema: z.ZodType<DelegationGrant>;
|
|
10
|
+
//# sourceMappingURL=delegation-grant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegation-grant.d.ts","sourceRoot":"","sources":["../../../src/principal/lib/delegation-grant.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAkBlE,MAAM,WAAW,eAAe;IAE9B,IAAI,EAAE,UAAU,CAAC;IAEjB,EAAE,EAAE,UAAU,CAAC;IAEf,SAAS,EAAE,MAAM,CAAC;IAElB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,qBAAqB,EAK5B,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DelegationGrantSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const subject_1 = require("../../subject");
|
|
6
|
+
exports.DelegationGrantSchema = zod_1.z.object({
|
|
7
|
+
from: subject_1.SubjectRefSchema,
|
|
8
|
+
to: subject_1.SubjectRefSchema,
|
|
9
|
+
grantedAt: zod_1.z.string().datetime(),
|
|
10
|
+
reason: zod_1.z.string().min(1).optional(),
|
|
11
|
+
});
|
|
12
|
+
//# sourceMappingURL=delegation-grant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegation-grant.js","sourceRoot":"","sources":["../../../src/principal/lib/delegation-grant.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AACxB,2CAAkE;AA6BrD,QAAA,qBAAqB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC5C,IAAI,EAAE,0BAAgB;IACtB,EAAE,EAAE,0BAAgB;IACpB,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CACrC,CAA+B,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type SubjectRef, type TokenClass } from '../../subject';
|
|
3
|
+
import { type ExecutionEnvironmentRef } from '../../execution-environment';
|
|
4
|
+
import { type AppChannel } from '../../app-runtime';
|
|
5
|
+
import { type DelegationGrant } from './delegation-grant';
|
|
6
|
+
export interface ExecutionPrincipal {
|
|
7
|
+
actor: SubjectRef;
|
|
8
|
+
executor: SubjectRef;
|
|
9
|
+
delegationChain: DelegationGrant[];
|
|
10
|
+
tokenClass: TokenClass;
|
|
11
|
+
environment: ExecutionEnvironmentRef;
|
|
12
|
+
orgId: string;
|
|
13
|
+
appId?: string;
|
|
14
|
+
releaseId?: string;
|
|
15
|
+
channel?: AppChannel;
|
|
16
|
+
tenantId?: string;
|
|
17
|
+
purpose?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const ExecutionPrincipalSchema: z.ZodType<ExecutionPrincipal>;
|
|
20
|
+
//# sourceMappingURL=execution-principal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution-principal.d.ts","sourceRoot":"","sources":["../../../src/principal/lib/execution-principal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,UAAU,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAwBjF,MAAM,WAAW,kBAAkB;IAEjC,KAAK,EAAE,UAAU,CAAC;IAElB,QAAQ,EAAE,UAAU,CAAC;IAErB,eAAe,EAAE,eAAe,EAAE,CAAC;IAEnC,UAAU,EAAE,UAAU,CAAC;IAEvB,WAAW,EAAE,uBAAuB,CAAC;IAErC,KAAK,EAAE,MAAM,CAAC;IAEd,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,OAAO,CAAC,EAAE,UAAU,CAAC;IAErB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,wBAAwB,EAY/B,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExecutionPrincipalSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const subject_1 = require("../../subject");
|
|
6
|
+
const execution_environment_1 = require("../../execution-environment");
|
|
7
|
+
const app_runtime_1 = require("../../app-runtime");
|
|
8
|
+
const delegation_grant_1 = require("./delegation-grant");
|
|
9
|
+
exports.ExecutionPrincipalSchema = zod_1.z.object({
|
|
10
|
+
actor: subject_1.SubjectRefSchema,
|
|
11
|
+
executor: subject_1.SubjectRefSchema,
|
|
12
|
+
delegationChain: zod_1.z.array(delegation_grant_1.DelegationGrantSchema),
|
|
13
|
+
tokenClass: subject_1.TokenClassSchema,
|
|
14
|
+
environment: execution_environment_1.ExecutionEnvironmentRefSchema,
|
|
15
|
+
orgId: zod_1.z.string().min(1),
|
|
16
|
+
appId: zod_1.z.string().min(1).optional(),
|
|
17
|
+
releaseId: zod_1.z.string().min(1).optional(),
|
|
18
|
+
channel: app_runtime_1.AppChannelSchema.optional(),
|
|
19
|
+
tenantId: zod_1.z.string().min(1).optional(),
|
|
20
|
+
purpose: zod_1.z.string().min(1).optional(),
|
|
21
|
+
});
|
|
22
|
+
//# sourceMappingURL=execution-principal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution-principal.js","sourceRoot":"","sources":["../../../src/principal/lib/execution-principal.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AACxB,2CAKuB;AACvB,uEAGqC;AACrC,mDAAsE;AACtE,yDAAiF;AAiDpE,QAAA,wBAAwB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC/C,KAAK,EAAE,0BAAgB;IACvB,QAAQ,EAAE,0BAAgB;IAC1B,eAAe,EAAE,OAAC,CAAC,KAAK,CAAC,wCAAqB,CAAC;IAC/C,UAAU,EAAE,0BAAgB;IAC5B,WAAW,EAAE,qDAA6B;IAC1C,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACxB,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACnC,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACvC,OAAO,EAAE,8BAAgB,CAAC,QAAQ,EAAE;IACpC,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACtC,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CACtC,CAAkC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xemahq/kernel-contracts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "Consolidated Xema OS kernel wire contracts — pure types + zod schemas for the kernel protocol surfaces. One package, one npm scope, wildcard per-surface subpath exports. No framework/runtime deps.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Neuralchowder Inc. <developer@xema.dev> (https://xema.dev)",
|
package/src/app-runtime/index.ts
CHANGED
|
@@ -5,4 +5,8 @@ export * from './lib/delegated-session';
|
|
|
5
5
|
export * from './lib/app-client';
|
|
6
6
|
export * from './lib/app-lockfile';
|
|
7
7
|
export * from './lib/branding-config';
|
|
8
|
+
export * from './lib/surface-kind';
|
|
9
|
+
export * from './lib/app-shell-binding';
|
|
10
|
+
export * from './lib/app-channel';
|
|
11
|
+
export * from './lib/app-release';
|
|
8
12
|
export * from './lib/app';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `AppChannel` — the closed set of channels an App is exercised through.
|
|
5
|
+
*
|
|
6
|
+
* A channel is *which data and which definition* an invocation sees:
|
|
7
|
+
*
|
|
8
|
+
* - `Preview` — the in-progress definition and its isolated data. The
|
|
9
|
+
* user-facing word is **"Editing"**.
|
|
10
|
+
* - `Live` — the active release's definition and the real data. The
|
|
11
|
+
* user-facing word is **"Live"**.
|
|
12
|
+
*
|
|
13
|
+
* A channel is NOT the same axis as an `ExecutionEnvironment`. An
|
|
14
|
+
* `ExecutionEnvironment` is *what may be touched* (which capabilities /
|
|
15
|
+
* resources are in scope); an `AppChannel` is *which definition + which data*.
|
|
16
|
+
* They are orthogonal and combine: preview RUNS IN `environment:sandbox`, live
|
|
17
|
+
* RUNS IN `environment:app`. Do not collapse the two.
|
|
18
|
+
*
|
|
19
|
+
* This enum lands in Wave 0.1 (not Wave 5) because it is keyed on well before
|
|
20
|
+
* Wave 5: Wave 1.1's `ownerRef`, Wave 3.2's `AppRoleBinding.channel`, and Wave
|
|
21
|
+
* 4.1's `AppRun.channel` all reference it. Only the enum lands now — its
|
|
22
|
+
* runtime semantics (data separation, denied side effects, no scheduled
|
|
23
|
+
* automations on preview) are Wave 5.1.
|
|
24
|
+
*
|
|
25
|
+
* Values are stable wire strings — never compare against a free-form string.
|
|
26
|
+
*/
|
|
27
|
+
export enum AppChannel {
|
|
28
|
+
Preview = 'preview',
|
|
29
|
+
Live = 'live',
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const AppChannelSchema = z.nativeEnum(AppChannel);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { AppLockfileSchema, type AppLockfile } from './app-lockfile';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `AppReleaseState` — the closed lifecycle of a single `AppRelease` row.
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* preparing → prepared → activating → active → superseded
|
|
9
|
+
* └──────────┴───────────┴──────────────→ failed
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* - `preparing` — a candidate release is being assembled (steps 1–5 of D2).
|
|
13
|
+
* - `prepared` — assembled and validated, migration NOT yet executed.
|
|
14
|
+
* - `activating` — the migration is executing and the pointer flip is in
|
|
15
|
+
* flight (steps 6–7).
|
|
16
|
+
* - `active` — the pointer (`App.activeReleaseId`) currently targets this
|
|
17
|
+
* release; it is the ONE release visible to every runtime surface.
|
|
18
|
+
* - `superseded` — was `active`, replaced by a later activation; retained for
|
|
19
|
+
* rollback within the retention window (D3).
|
|
20
|
+
* - `failed` — prepare or activation aborted; invisible, garbage-collected.
|
|
21
|
+
*/
|
|
22
|
+
export enum AppReleaseState {
|
|
23
|
+
Preparing = 'preparing',
|
|
24
|
+
Prepared = 'prepared',
|
|
25
|
+
Activating = 'activating',
|
|
26
|
+
Active = 'active',
|
|
27
|
+
Superseded = 'superseded',
|
|
28
|
+
Failed = 'failed',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const AppReleaseStateSchema = z.nativeEnum(AppReleaseState);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* `AppRelease` — an immutable candidate/active snapshot of an App's published
|
|
35
|
+
* definition, its resolved lockfile, and the schema-change plan applied for it.
|
|
36
|
+
*
|
|
37
|
+
* **Publishing is a distributed release, not a transaction (D2).** The
|
|
38
|
+
* definition lives in app-forge's database, the DDL runs in an org database,
|
|
39
|
+
* the `App` lives in app-runtime, agents/skills/workflows live in their own
|
|
40
|
+
* registries, and grants live in authorization-api. **No transaction spans
|
|
41
|
+
* these five systems.** Publish therefore runs an eight-step
|
|
42
|
+
* prepare-and-activate protocol:
|
|
43
|
+
*
|
|
44
|
+
* ```
|
|
45
|
+
* 1. create an immutable candidate AppRelease (invisible)
|
|
46
|
+
* 2. prepare the schema change plan (validated, not applied)
|
|
47
|
+
* 3. prepare agent / skill / workflow versions (drafts in their registries)
|
|
48
|
+
* 4. validate capability bindings and grants (fail here, cheaply)
|
|
49
|
+
* 5. write the immutable AppRelease manifest + lockfile
|
|
50
|
+
* 6. execute the migration (idempotent, one txn per plan)
|
|
51
|
+
* 7. atomically flip App.activeReleaseId (the ONLY atomic operation)
|
|
52
|
+
* 8. publish activation through the transactional outbox
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* Because no transaction spans the five systems, **the only atomic operation is
|
|
56
|
+
* flipping `App.activeReleaseId`** (step 7) — moving one pointer. Everything
|
|
57
|
+
* else is prepared idempotently and made visible only by that flip.
|
|
58
|
+
*
|
|
59
|
+
* **Preparing/prepared/failed releases are invisible + garbage-collected (D3).**
|
|
60
|
+
* A release that is not `active` (or a retained `superseded`) is visible to NO
|
|
61
|
+
* runtime surface and is garbage-collected after a TTL. Rollback is simply
|
|
62
|
+
* repointing `activeReleaseId` to a prior release — which is sound ONLY because
|
|
63
|
+
* the publish path tombstones (marks deprecated) rather than physically drops:
|
|
64
|
+
* an older release can still run against a tombstoned-but-present column.
|
|
65
|
+
*/
|
|
66
|
+
export interface AppRelease {
|
|
67
|
+
id: string;
|
|
68
|
+
appId: string;
|
|
69
|
+
version: number;
|
|
70
|
+
definition: unknown;
|
|
71
|
+
lockfile: AppLockfile;
|
|
72
|
+
schemaPlan: unknown;
|
|
73
|
+
state: AppReleaseState;
|
|
74
|
+
preparedAt?: string;
|
|
75
|
+
activatedAt?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const AppReleaseSchema = z.object({
|
|
79
|
+
id: z.string().min(1),
|
|
80
|
+
appId: z.string().min(1),
|
|
81
|
+
version: z.number().int().positive({
|
|
82
|
+
message: 'AppRelease.version must be a positive integer (1-based).',
|
|
83
|
+
}),
|
|
84
|
+
definition: z.unknown(),
|
|
85
|
+
lockfile: AppLockfileSchema,
|
|
86
|
+
schemaPlan: z.unknown(),
|
|
87
|
+
state: AppReleaseStateSchema,
|
|
88
|
+
preparedAt: z.string().min(1).optional(),
|
|
89
|
+
activatedAt: z.string().min(1).optional(),
|
|
90
|
+
}) as z.ZodType<AppRelease>;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
import { AudienceKind } from './audience-policy';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* D14 — Shell, audience and host are THREE ORTHOGONAL dimensions of an App.
|
|
7
|
+
* A public page is not necessarily embedded; a standalone app may be anonymous
|
|
8
|
+
* or internally authenticated. This file owns two of the three closed dimensions
|
|
9
|
+
* (the third, audience, is the existing {@link AudienceKind} — D14 forbids
|
|
10
|
+
* minting a second audience enum) and the rules for which COMBINATIONS are legal.
|
|
11
|
+
*
|
|
12
|
+
* The legality rules live here, in the kernel, next to the dimensions they
|
|
13
|
+
* constrain — not scattered across each consumer. app-forge's publish path calls
|
|
14
|
+
* {@link validateAppSurfaceCombination}; it does not re-encode the rules.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `AppShellKind` — how much host chrome / navigation / layout an App surface
|
|
19
|
+
* carries. Owns: chrome, nav, layout.
|
|
20
|
+
*
|
|
21
|
+
* - `Hosted` — rendered inside the Xema host shell (rail, topbar, org switch).
|
|
22
|
+
* - `Standalone` — its own full-page surface, no host shell (e.g. a custom domain).
|
|
23
|
+
* - `Embedded` — rendered inside a third-party page via an iframe/snippet.
|
|
24
|
+
* - `Headless` — no UI at all; an API/automation surface.
|
|
25
|
+
*
|
|
26
|
+
* Values are stable wire strings — never compare against a free-form string.
|
|
27
|
+
*/
|
|
28
|
+
export enum AppShellKind {
|
|
29
|
+
Hosted = 'hosted',
|
|
30
|
+
Standalone = 'standalone',
|
|
31
|
+
Embedded = 'embedded',
|
|
32
|
+
Headless = 'headless',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const AppShellKindSchema = z.nativeEnum(AppShellKind);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* `AppHostBinding` — where an App is served from. Owns: origin, CSP, cookies,
|
|
39
|
+
* SEO, certificates.
|
|
40
|
+
*
|
|
41
|
+
* - `Xema` — under a Xema-owned origin/path.
|
|
42
|
+
* - `CustomDomain` — the customer's own domain (needs ACME cert issuance).
|
|
43
|
+
* - `PathBased` — a path on a shared host, not its own origin.
|
|
44
|
+
*
|
|
45
|
+
* Values are stable wire strings.
|
|
46
|
+
*/
|
|
47
|
+
export enum AppHostBinding {
|
|
48
|
+
Xema = 'xema',
|
|
49
|
+
CustomDomain = 'custom-domain',
|
|
50
|
+
PathBased = 'path-based',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const AppHostBindingSchema = z.nativeEnum(AppHostBinding);
|
|
54
|
+
|
|
55
|
+
/** The three-dimension surface descriptor an App declares. */
|
|
56
|
+
export interface AppSurfaceDimensions {
|
|
57
|
+
readonly shell: AppShellKind;
|
|
58
|
+
readonly audience: AudienceKind;
|
|
59
|
+
readonly host: AppHostBinding;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const AppSurfaceDimensionsSchema: z.ZodType<AppSurfaceDimensions> = z
|
|
63
|
+
.object({
|
|
64
|
+
shell: AppShellKindSchema,
|
|
65
|
+
audience: z.nativeEnum(AudienceKind),
|
|
66
|
+
host: AppHostBindingSchema,
|
|
67
|
+
})
|
|
68
|
+
.strict() as z.ZodType<AppSurfaceDimensions>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* A single reason a `(shell, audience, host)` combination is illegal. `code` is a
|
|
72
|
+
* closed set so consumers can branch on it; `message` is human-facing.
|
|
73
|
+
*/
|
|
74
|
+
export enum AppSurfaceConflictCode {
|
|
75
|
+
HeadlessWithChrome = 'headless-with-chrome',
|
|
76
|
+
EmbeddedIsChrome = 'embedded-is-chrome',
|
|
77
|
+
StandaloneNeedsOwnHost = 'standalone-needs-own-host',
|
|
78
|
+
EmbeddedNeedsXemaHost = 'embedded-needs-xema-host',
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface AppSurfaceConflict {
|
|
82
|
+
readonly code: AppSurfaceConflictCode;
|
|
83
|
+
readonly message: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface AppSurfaceValidation {
|
|
87
|
+
readonly ok: boolean;
|
|
88
|
+
readonly conflicts: readonly AppSurfaceConflict[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Validate a `(shell, audience, host)` combination against D14's legality rules.
|
|
93
|
+
* PURE and total — returns every conflict, never throws. The empty-conflict case
|
|
94
|
+
* is `ok: true`.
|
|
95
|
+
*
|
|
96
|
+
* Rules (each with a reason, so the publish screen can explain it):
|
|
97
|
+
* - `Headless` is a NO-UI surface, so it cannot also be `Embedded` (which is a UI
|
|
98
|
+
* shell). `headless` + `embedded` is the canonical contradiction the plan names.
|
|
99
|
+
* - `Embedded` IS a chrome mode; it is meaningful only for a UI shell, so an
|
|
100
|
+
* `Embedded` + `Headless` pairing is caught by the same rule from the other side.
|
|
101
|
+
* - `Standalone` (its own full-page surface) implies its own origin — it cannot be
|
|
102
|
+
* `PathBased` (a path on a shared host). `custom-domain` or `xema` are fine.
|
|
103
|
+
* - `Embedded` is served to a third-party page and relies on the Xema origin's
|
|
104
|
+
* CSP/cookies, so it must be `Xema`-hosted, not a `CustomDomain`/`PathBased`.
|
|
105
|
+
*
|
|
106
|
+
* `public-anon` + `custom-domain` is explicitly LEGAL (a public citizen portal on
|
|
107
|
+
* the customer's own domain) — there is no rule against it, so it returns `ok`.
|
|
108
|
+
* Audience does not currently gate shell/host on its own; the dimension is passed
|
|
109
|
+
* so the rules can grow without a signature change.
|
|
110
|
+
*/
|
|
111
|
+
export function validateAppSurfaceCombination(
|
|
112
|
+
dims: AppSurfaceDimensions,
|
|
113
|
+
): AppSurfaceValidation {
|
|
114
|
+
const conflicts: AppSurfaceConflict[] = [];
|
|
115
|
+
|
|
116
|
+
// The headless+embedded contradiction cannot be expressed as a SINGLE
|
|
117
|
+
// AppShellKind value (they are distinct enum members), so a surface with one
|
|
118
|
+
// declared shell is internally consistent on that axis. A consumer that models
|
|
119
|
+
// "embedded" as a separate flag alongside the shell must use
|
|
120
|
+
// isHeadlessEmbeddedContradiction() to reject the pair — documented there.
|
|
121
|
+
|
|
122
|
+
if (
|
|
123
|
+
dims.shell === AppShellKind.Standalone &&
|
|
124
|
+
dims.host === AppHostBinding.PathBased
|
|
125
|
+
) {
|
|
126
|
+
conflicts.push({
|
|
127
|
+
code: AppSurfaceConflictCode.StandaloneNeedsOwnHost,
|
|
128
|
+
message:
|
|
129
|
+
'A standalone app has its own full-page surface and cannot be path-based on a shared host; use xema or custom-domain.',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (
|
|
134
|
+
dims.shell === AppShellKind.Embedded &&
|
|
135
|
+
dims.host !== AppHostBinding.Xema
|
|
136
|
+
) {
|
|
137
|
+
conflicts.push({
|
|
138
|
+
code: AppSurfaceConflictCode.EmbeddedNeedsXemaHost,
|
|
139
|
+
message:
|
|
140
|
+
"An embedded app is served into a third-party page and relies on the Xema origin's CSP/cookies; it must be xema-hosted.",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return { ok: conflicts.length === 0, conflicts };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* The `headless` + `embedded` contradiction the plan names explicitly. Because
|
|
149
|
+
* `AppShellKind` is a single value, a surface cannot BE both at once — but a
|
|
150
|
+
* consumer that models shell as a SET, or that derives "embedded" from a separate
|
|
151
|
+
* `embed` flag alongside a `headless` shell, must reject the pair. This helper
|
|
152
|
+
* makes that cross-field rule reusable and named, so no consumer re-encodes it.
|
|
153
|
+
*/
|
|
154
|
+
export function isHeadlessEmbeddedContradiction(
|
|
155
|
+
shell: AppShellKind,
|
|
156
|
+
alsoEmbedded: boolean,
|
|
157
|
+
): boolean {
|
|
158
|
+
return shell === AppShellKind.Headless && alsoEmbedded;
|
|
159
|
+
}
|
|
@@ -15,6 +15,7 @@ import { BiomeInstallSchema, type BiomeInstall } from './biome-install';
|
|
|
15
15
|
import { AudiencePolicySchema, type AudiencePolicy } from './audience-policy';
|
|
16
16
|
import { BrandingConfigSchema, type BrandingConfig } from './branding-config';
|
|
17
17
|
import { AppLockfileSchema, type AppLockfile } from './app-lockfile';
|
|
18
|
+
import { SurfaceKind, SurfaceKindSchema } from './surface-kind';
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Per-App tightening on top of the base `CapabilityPolicy` carried by an
|
|
@@ -47,12 +48,22 @@ export const CapabilityPolicyOverrideSchema = z.object({
|
|
|
47
48
|
*
|
|
48
49
|
* Invariants enforced at parse time:
|
|
49
50
|
*
|
|
50
|
-
* - `installedBiomes` MUST be non-empty
|
|
51
|
-
* capability surface, so
|
|
51
|
+
* - `installedBiomes` MUST be non-empty for a `Composed` or `Coded` App — one
|
|
52
|
+
* with zero biomes would have no capability surface, so that is a fail-fast
|
|
53
|
+
* policy bug. A **`Declared` App is exempt**: an app-forge definition installs
|
|
54
|
+
* no biomes at all. Its capability surface comes from `capabilityPolicy` and
|
|
55
|
+
* the declarative runtime that hosts it, not from a biome install. Requiring a
|
|
56
|
+
* biome here would force every declared app to fabricate one, which is how a
|
|
57
|
+
* fail-fast invariant quietly becomes a lie.
|
|
52
58
|
* - `audiences` MUST be non-empty — an App with zero audiences is
|
|
53
59
|
* unreachable; declare at least one (e.g. `InternalOrg`) explicitly.
|
|
54
60
|
* - `defaultZone` and `lockfile` are REQUIRED (no implicit default environment,
|
|
55
61
|
* no "best-effort" un-pinned execution).
|
|
62
|
+
* - `surface` is REQUIRED — the tier the App is authored at (`Declared` /
|
|
63
|
+
* `Composed` / `Coded`).
|
|
64
|
+
* - `activeReleaseId` is nullable: an App with no active release has NEVER
|
|
65
|
+
* published. It is the ONLY atomic publish switch (D2) — moving this pointer
|
|
66
|
+
* is what makes a prepared `AppRelease` visible to every runtime surface.
|
|
56
67
|
*
|
|
57
68
|
* Per-App tightening on top of a environment's `CapabilityPolicy` is carried by
|
|
58
69
|
* `capabilityPolicy` (intersect-only at the gateway, never widen).
|
|
@@ -65,20 +76,40 @@ export interface App {
|
|
|
65
76
|
capabilityPolicy: CapabilityPolicyOverride[];
|
|
66
77
|
branding: BrandingConfig;
|
|
67
78
|
lockfile: AppLockfile;
|
|
79
|
+
surface: SurfaceKind;
|
|
80
|
+
activeReleaseId?: string;
|
|
68
81
|
}
|
|
69
82
|
|
|
70
|
-
export const AppSchema = z
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
})
|
|
83
|
+
export const AppSchema = z
|
|
84
|
+
.object({
|
|
85
|
+
ref: XemaObjectRefSchema,
|
|
86
|
+
installedBiomes: z.array(BiomeInstallSchema),
|
|
87
|
+
defaultZone: ExecutionEnvironmentRefSchema,
|
|
88
|
+
audiences: z.array(AudiencePolicySchema).min(1, {
|
|
89
|
+
message:
|
|
90
|
+
'App.audiences must contain at least one AudiencePolicy; an App with zero audiences is unreachable.',
|
|
91
|
+
}),
|
|
92
|
+
capabilityPolicy: z.array(CapabilityPolicyOverrideSchema),
|
|
93
|
+
branding: BrandingConfigSchema,
|
|
94
|
+
lockfile: AppLockfileSchema,
|
|
95
|
+
surface: SurfaceKindSchema,
|
|
96
|
+
activeReleaseId: z.string().min(1).optional(),
|
|
97
|
+
})
|
|
98
|
+
.superRefine((app, ctx) => {
|
|
99
|
+
// A Declared App (an app-forge definition) installs no biomes: its capability
|
|
100
|
+
// surface is `capabilityPolicy` plus the declarative runtime that hosts it.
|
|
101
|
+
// Composed and Coded Apps run biome-provided code, so zero biomes there IS a
|
|
102
|
+
// policy bug and must fail fast.
|
|
103
|
+
if (app.surface !== SurfaceKind.Declared && app.installedBiomes.length === 0) {
|
|
104
|
+
ctx.addIssue({
|
|
105
|
+
code: 'too_small',
|
|
106
|
+
minimum: 1,
|
|
107
|
+
origin: 'array',
|
|
108
|
+
inclusive: true,
|
|
109
|
+
path: ['installedBiomes'],
|
|
110
|
+
message:
|
|
111
|
+
`App.installedBiomes must contain at least one BiomeInstall for a '${app.surface}' App; ` +
|
|
112
|
+
'one with zero biomes has no capability surface. Only a `declared` App may install none.',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}) as unknown as z.ZodType<App>;
|