@xemahq/xema 0.41.1 → 0.43.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/commands/edition.d.ts.map +1 -1
- package/dist/commands/edition.js +2 -1
- package/dist/commands/edition.js.map +1 -1
- package/dist/edition/verify-image-evidence.bundle.mjs +1289 -840
- package/dist/edition/verify-image-evidence.bundle.provenance.json +494 -458
- package/dist/runtime/edition/verb-map.d.ts +1 -0
- package/dist/runtime/edition/verb-map.d.ts.map +1 -1
- package/dist/runtime/edition/verb-map.js +2 -0
- package/dist/runtime/edition/verb-map.js.map +1 -1
- package/edition/README.md +31 -1
- package/edition/authored-source-check.mjs +135 -0
- package/edition/biome-content.mjs +4 -0
- package/edition/customer-edition-verbs.mjs +1 -0
- package/edition/develop-edition-invariants.mjs +141 -0
- package/edition/develop-edition-resolve.mjs +7 -1
- package/edition/develop-edition.mjs +22 -2
- package/edition/dispatched-runtime-components.mjs +4 -3
- package/edition/dist-check.mjs +108 -63
- package/edition/edition-protocol.mjs +36 -9
- package/edition/edition-workspace.mjs +2 -0
- package/edition/kernel-protocol-choice.mjs +239 -0
- package/edition/lib.mjs +100 -3
- package/edition/operation-grant-admission.mjs +65 -1
- package/edition/operation-requirements.mjs +18 -1
- package/edition/resolve-customer-edition.mjs +4 -3
- package/edition/resolve-distribution.mjs +16 -0
- package/edition/resolve-images.mjs +124 -15
- package/package.json +4 -4
package/edition/lib.mjs
CHANGED
|
@@ -40,6 +40,7 @@ import { isKernelCapabilityDomain } from './kernel-capability-domain.mjs';
|
|
|
40
40
|
import {
|
|
41
41
|
PRINCIPAL_PRIVILEGE_FIELDS,
|
|
42
42
|
assertPrivilegesHavePrincipals,
|
|
43
|
+
componentInstalledOperationRequests,
|
|
43
44
|
componentOperationRequirements,
|
|
44
45
|
componentOptionalOperationRequirements,
|
|
45
46
|
componentOrganizationSweepRequirements,
|
|
@@ -686,6 +687,64 @@ const kernelDistributionDist = () => kernelDistPath('distribution', 'index.js');
|
|
|
686
687
|
// through. A sibling of the distribution dist, loaded beside it.
|
|
687
688
|
const kernelProcessTopologyDist = () => kernelDistPath('process-topology', 'index.js');
|
|
688
689
|
|
|
690
|
+
/** A composed-process subject, on a kernel that cannot write image-lock schemaVersion 4. */
|
|
691
|
+
export class ComposedProcessNeedsLockV4Error extends Error {
|
|
692
|
+
constructor(message) {
|
|
693
|
+
super(message);
|
|
694
|
+
this.name = 'ComposedProcessNeedsLockV4Error';
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/** The image-lock revision of a kernel that predates `IMAGE_LOCK_WRITE_SCHEMA_VERSION` (edition protocol < v14). */
|
|
699
|
+
const PRE_V14_IMAGE_LOCK_SCHEMA_VERSION = 3;
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Refuse a composed-process subject when the loaded kernel predates image-lock
|
|
703
|
+
* schemaVersion 4 (it exports no `IMAGE_LOCK_WRITE_SCHEMA_VERSION`): such a
|
|
704
|
+
* kernel has no `members[]`, so it cannot say which services the composed
|
|
705
|
+
* image carries. There is no fallback.
|
|
706
|
+
*/
|
|
707
|
+
export function assertKernelSealsComposedMembership(schemas, subjects, label) {
|
|
708
|
+
if (Number.isInteger(schemas.IMAGE_LOCK_WRITE_SCHEMA_VERSION)) {return;}
|
|
709
|
+
const composed = (subjects ?? []).find((subject) => subject.kind === 'composed-process');
|
|
710
|
+
if (composed === undefined) {return;}
|
|
711
|
+
throw new ComposedProcessNeedsLockV4Error(
|
|
712
|
+
`${label}: composed-process subject "${composed.id}" — a composed-process subject needs a kernel that ` +
|
|
713
|
+
'writes image-lock schemaVersion 4 (edition protocol >= v14); the loaded kernel ' +
|
|
714
|
+
`(${kernelRepoDir()}) exports no IMAGE_LOCK_WRITE_SCHEMA_VERSION`,
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* The READ side of the same rule, for a kernel before image-lock
|
|
720
|
+
* schemaVersion 4 (no `ImageLockSchemaVersion`): it reads only schemaVersion
|
|
721
|
+
* 3, which names no composed members, so a v3 lock carrying a composed-process
|
|
722
|
+
* subject cannot say which services run from it and is refused (worded as the
|
|
723
|
+
* kernel's own `LegacyComposedImageLockError`).
|
|
724
|
+
*/
|
|
725
|
+
export function assertLegacyLockHasNoComposedSubject(lock) {
|
|
726
|
+
const composed = (lock.subjects ?? []).find((subject) => subject.kind === 'composed-process');
|
|
727
|
+
if (composed === undefined) {return;}
|
|
728
|
+
throw new ComposedProcessNeedsLockV4Error(
|
|
729
|
+
`distribution "${lock.distributionId}": subjects "${composed.id}" is a schemaVersion ` +
|
|
730
|
+
`${String(lock.schemaVersion)} composed-process subject, which names no members: regenerate with a CLI ` +
|
|
731
|
+
'that writes image-lock schemaVersion 4, against a kernel at edition protocol >= v14',
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* The image-lock schemaVersion a writer emits for `subjects`: the loaded
|
|
737
|
+
* kernel's `IMAGE_LOCK_WRITE_SCHEMA_VERSION`. A kernel before edition protocol
|
|
738
|
+
* v14 exports none and writes only schemaVersion 3, which is allowed only when
|
|
739
|
+
* there is NO composed-process subject
|
|
740
|
+
* ({@link ComposedProcessNeedsLockV4Error} otherwise).
|
|
741
|
+
*/
|
|
742
|
+
export function imageLockWriteSchemaVersion(schemas, subjects, label = 'images.lock.json') {
|
|
743
|
+
assertKernelSealsComposedMembership(schemas, subjects, label);
|
|
744
|
+
const version = schemas.IMAGE_LOCK_WRITE_SCHEMA_VERSION;
|
|
745
|
+
return Number.isInteger(version) ? version : PRE_V14_IMAGE_LOCK_SCHEMA_VERSION;
|
|
746
|
+
}
|
|
747
|
+
|
|
689
748
|
/**
|
|
690
749
|
* Dynamically import the BUILT kernel distribution schemas. The kernel package
|
|
691
750
|
* is NOT linked into the repo-root node_modules (it lives in its own sub-
|
|
@@ -737,6 +796,8 @@ export async function loadKernelDistributionSchemas() {
|
|
|
737
796
|
parseOciDigest,
|
|
738
797
|
parseGitTreeId,
|
|
739
798
|
parseImageLock,
|
|
799
|
+
IMAGE_LOCK_WRITE_SCHEMA_VERSION,
|
|
800
|
+
ImageLockSchemaVersion,
|
|
740
801
|
parseImageProducerStatement,
|
|
741
802
|
parseDistributionImagesStatement,
|
|
742
803
|
parseImageDeliveryEntitlementStatement,
|
|
@@ -789,6 +850,8 @@ export async function loadKernelDistributionSchemas() {
|
|
|
789
850
|
admitOperationGrants,
|
|
790
851
|
admitOrganizationSweeps,
|
|
791
852
|
contractRequiredServiceTargets,
|
|
853
|
+
admitInstalledOperationCeiling,
|
|
854
|
+
admitStoreWorkloadCeiling,
|
|
792
855
|
} = mod;
|
|
793
856
|
let topology;
|
|
794
857
|
try {
|
|
@@ -886,6 +949,11 @@ export async function loadKernelDistributionSchemas() {
|
|
|
886
949
|
typeof admitOperationGrants !== 'function' ||
|
|
887
950
|
typeof admitOrganizationSweeps !== 'function' ||
|
|
888
951
|
typeof contractRequiredServiceTargets !== 'function' ||
|
|
952
|
+
// The installed-operation ceiling and the edition's store-workload ceiling,
|
|
953
|
+
// both admitted at the lock (`assertInstalledOperationCeilingAdmitted`,
|
|
954
|
+
// `assertStoreWorkloadCeilingAdmitted`).
|
|
955
|
+
typeof admitInstalledOperationCeiling !== 'function' ||
|
|
956
|
+
typeof admitStoreWorkloadCeiling !== 'function' ||
|
|
889
957
|
typeof distributionImagesPredicateLockRefusal !== 'function' ||
|
|
890
958
|
typeof distributionLockDigest !== 'function' ||
|
|
891
959
|
typeof imageDeliveryEntitlementRefusal !== 'function' ||
|
|
@@ -907,7 +975,7 @@ export async function loadKernelDistributionSchemas() {
|
|
|
907
975
|
`resolveRuntimeImagePlatforms/RuntimeImagePlatformKey/` +
|
|
908
976
|
`kernel contract-authority digest SSOT/compareLockedServiceResources/resolveNexusBindings/describeNexusBindingRefusal/canonicalNexusFacts/` +
|
|
909
977
|
`positive closure (resolveBiomeClosure/platformInvariantRoots/DistributionResolutionMode/provenance)/` +
|
|
910
|
-
`deriveWorkflowSubjects/deriveTrustCeiling (>= 0.71.0)/admitOperationGrants/admitOrganizationSweeps/` +
|
|
978
|
+
`deriveWorkflowSubjects/deriveTrustCeiling (>= 0.71.0)/admitOperationGrants/admitOrganizationSweeps/admitInstalledOperationCeiling/admitStoreWorkloadCeiling/` +
|
|
911
979
|
`defaultTrustDomain/FIRST_PARTY_TRUST_DOMAIN/ProcessTrustPolicySchema/DistributionTrustPolicySchema/LockedStorePublisherSchema/` +
|
|
912
980
|
`resolveFirstPartyCapabilityDomains/DistributionVisibility/` +
|
|
913
981
|
`BiomeInstallPolicy/BiomeLaunchMode/ImageSourceHashAlgorithm/runtime-image enums — ` +
|
|
@@ -933,6 +1001,8 @@ export async function loadKernelDistributionSchemas() {
|
|
|
933
1001
|
parseOciDigest,
|
|
934
1002
|
parseGitTreeId,
|
|
935
1003
|
parseImageLock,
|
|
1004
|
+
IMAGE_LOCK_WRITE_SCHEMA_VERSION,
|
|
1005
|
+
ImageLockSchemaVersion,
|
|
936
1006
|
parseImageProducerStatement,
|
|
937
1007
|
parseDistributionImagesStatement,
|
|
938
1008
|
parseImageDeliveryEntitlementStatement,
|
|
@@ -992,6 +1062,8 @@ export async function loadKernelDistributionSchemas() {
|
|
|
992
1062
|
admitOperationGrants,
|
|
993
1063
|
admitOrganizationSweeps,
|
|
994
1064
|
contractRequiredServiceTargets,
|
|
1065
|
+
admitInstalledOperationCeiling,
|
|
1066
|
+
admitStoreWorkloadCeiling,
|
|
995
1067
|
distributionImagesPredicateLockRefusal,
|
|
996
1068
|
imageDeliveryEntitlementRefusal,
|
|
997
1069
|
producerWorkflowPinRefusal,
|
|
@@ -1623,7 +1695,7 @@ export async function manifestToIndexEntry({ raw, generatedIndex, rel, repo, pat
|
|
|
1623
1695
|
'resolve no worker image: a worker declares artifact.kind "oci-image" or "shared-oci-image"',
|
|
1624
1696
|
);
|
|
1625
1697
|
}
|
|
1626
|
-
// A privilege (`requiresOperations`, `optionalOperations`, `requiresOrganizationSweeps`) is admitted for a lock workload
|
|
1698
|
+
// A privilege (`requiresOperations`, `optionalOperations`, `requestsInstalledOperations`, `requiresOrganizationSweeps`) is admitted for a lock workload
|
|
1627
1699
|
// principal only; on any other component it is refused, never dropped.
|
|
1628
1700
|
assertPrivilegesHavePrincipals(components, imageComponents, rel);
|
|
1629
1701
|
// A biome must declare its images in EXACTLY ONE shape. Declaring both a
|
|
@@ -1674,6 +1746,10 @@ export async function manifestToIndexEntry({ raw, generatedIndex, rel, repo, pat
|
|
|
1674
1746
|
// (`optionalOperations`), keyed like `operationRequirements`. Declared
|
|
1675
1747
|
// intent only: the seal grants, leaves ungranted or refuses each one.
|
|
1676
1748
|
const optionalOperationRequirements = {};
|
|
1749
|
+
// `component → the operations it asks to exercise for the installing
|
|
1750
|
+
// organization` (`requestsInstalledOperations`), keyed like
|
|
1751
|
+
// `operationRequirements`. Declared intent only: the lock admits the ceiling.
|
|
1752
|
+
const installedOperationRequests = {};
|
|
1677
1753
|
// `component → the organization sweeps it may hold`
|
|
1678
1754
|
// (`requiresOrganizationSweeps`), keyed like `operationRequirements`.
|
|
1679
1755
|
// Declared intent only: the seal admits or refuses each one.
|
|
@@ -1867,6 +1943,8 @@ export async function manifestToIndexEntry({ raw, generatedIndex, rel, repo, pat
|
|
|
1867
1943
|
if (operations !== null) {operationRequirements[name] = operations;}
|
|
1868
1944
|
const optionalOperations = componentOptionalOperationRequirements(component, where);
|
|
1869
1945
|
if (optionalOperations !== null) {optionalOperationRequirements[name] = optionalOperations;}
|
|
1946
|
+
const installed = componentInstalledOperationRequests(component, where);
|
|
1947
|
+
if (installed !== null) {installedOperationRequests[name] = installed;}
|
|
1870
1948
|
const sweeps = componentOrganizationSweepRequirements(component, where);
|
|
1871
1949
|
if (sweeps !== null) {organizationSweepRequirements[name] = sweeps;}
|
|
1872
1950
|
const queues = componentWorkerTaskQueues(component, protocol, where);
|
|
@@ -2311,6 +2389,17 @@ export async function manifestToIndexEntry({ raw, generatedIndex, rel, repo, pat
|
|
|
2311
2389
|
),
|
|
2312
2390
|
}
|
|
2313
2391
|
: {}),
|
|
2392
|
+
// The operations each workload component asks to exercise for the
|
|
2393
|
+
// installing organization; omitted when none declares any.
|
|
2394
|
+
...(Object.keys(installedOperationRequests).length > 0
|
|
2395
|
+
? {
|
|
2396
|
+
installedOperationRequests: Object.fromEntries(
|
|
2397
|
+
Object.entries(installedOperationRequests).sort(([a], [b]) =>
|
|
2398
|
+
compareStrings(a, b),
|
|
2399
|
+
),
|
|
2400
|
+
),
|
|
2401
|
+
}
|
|
2402
|
+
: {}),
|
|
2314
2403
|
// The organization sweeps each workload component may hold; omitted when
|
|
2315
2404
|
// none declares any.
|
|
2316
2405
|
...(Object.keys(organizationSweepRequirements).length > 0
|
|
@@ -2920,8 +3009,16 @@ export function validateImmutableImageLock(lock, schemas) {
|
|
|
2920
3009
|
if (!schemas) {
|
|
2921
3010
|
throw new Error('trusted kernel distribution schemas are required');
|
|
2922
3011
|
}
|
|
3012
|
+
assertKernelSealsComposedMembership(schemas, lock.subjects, `distribution "${lock.distributionId}"`);
|
|
3013
|
+
// The revisions the loaded kernel reads (`ImageLockSchemaVersion`: 3 without
|
|
3014
|
+
// composed membership, 4 with it); a kernel before image-lock v4 reads only 3.
|
|
3015
|
+
if (schemas.ImageLockSchemaVersion === undefined) {assertLegacyLockHasNoComposedSubject(lock);}
|
|
3016
|
+
const readableVersions =
|
|
3017
|
+
schemas.ImageLockSchemaVersion === undefined
|
|
3018
|
+
? [PRE_V14_IMAGE_LOCK_SCHEMA_VERSION]
|
|
3019
|
+
: Object.values(schemas.ImageLockSchemaVersion).filter((version) => Number.isInteger(version));
|
|
2923
3020
|
if (
|
|
2924
|
-
lock.schemaVersion
|
|
3021
|
+
!readableVersions.includes(lock.schemaVersion) ||
|
|
2925
3022
|
lock.sourceHashAlgorithm !== schemas.ImageSourceHashAlgorithm.ImageInputV2
|
|
2926
3023
|
) {
|
|
2927
3024
|
throw new Error(
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* `operationRequirements`); the kernel's `admitOperationGrants` admits or
|
|
6
6
|
* refuses every requirement, and a refusal is never a warning. The resolve
|
|
7
7
|
* and the seal both call {@link assertOperationGrantsAdmitted}, so a lock with
|
|
8
|
-
* a refused requirement is neither written nor sealed.
|
|
8
|
+
* a refused requirement is neither written nor sealed. The installed-operation
|
|
9
|
+
* ceiling (`requestsInstalledOperations`, carried as `installedOperationRequests`)
|
|
10
|
+
* and the edition's store-workload ceiling are admitted the same way.
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
/** Thrown when one or more declared operation requirements are not admitted. */
|
|
@@ -83,3 +85,65 @@ export function assertOrganizationSweepsAdmitted(schemas, lock) {
|
|
|
83
85
|
}
|
|
84
86
|
return grants;
|
|
85
87
|
}
|
|
88
|
+
|
|
89
|
+
/** Thrown when one or more requested installed operations are not admitted into the ceiling. */
|
|
90
|
+
export class InstalledOperationCeilingAdmissionError extends Error {
|
|
91
|
+
/**
|
|
92
|
+
* @param {string} distributionId
|
|
93
|
+
* @param {ReadonlyArray<{code: string, biome: string, component: string, operation: string, message: string}>} refusals
|
|
94
|
+
*/
|
|
95
|
+
constructor(distributionId, refusals) {
|
|
96
|
+
super(
|
|
97
|
+
`distribution "${distributionId}" declares installed operation requests that are not admitted:\n` +
|
|
98
|
+
refusals.map((refusal) => `[${refusal.code}] ${refusal.message}`).join('\n'),
|
|
99
|
+
);
|
|
100
|
+
this.name = 'InstalledOperationCeilingAdmissionError';
|
|
101
|
+
this.distributionId = distributionId;
|
|
102
|
+
this.refusals = refusals;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Admit every installed-operation request of `lock` through the kernel
|
|
108
|
+
* (`admitInstalledOperationCeiling`), or throw
|
|
109
|
+
* {@link InstalledOperationCeilingAdmissionError} listing every refusal.
|
|
110
|
+
* Returns the admitted ceiling rows.
|
|
111
|
+
*/
|
|
112
|
+
export function assertInstalledOperationCeilingAdmitted(schemas, lock) {
|
|
113
|
+
const { ceilings, refusals } = schemas.admitInstalledOperationCeiling(lock);
|
|
114
|
+
if (refusals.length > 0) {
|
|
115
|
+
throw new InstalledOperationCeilingAdmissionError(lock.distributionId, refusals);
|
|
116
|
+
}
|
|
117
|
+
return ceilings;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Thrown when one or more domains of the edition's store-workload ceiling are not admitted. */
|
|
121
|
+
export class StoreWorkloadCeilingAdmissionError extends Error {
|
|
122
|
+
/**
|
|
123
|
+
* @param {string} distributionId
|
|
124
|
+
* @param {ReadonlyArray<{code: string, domain: string, message: string}>} refusals
|
|
125
|
+
*/
|
|
126
|
+
constructor(distributionId, refusals) {
|
|
127
|
+
super(
|
|
128
|
+
`distribution "${distributionId}" declares a store-workload ceiling that is not admitted:\n` +
|
|
129
|
+
refusals.map((refusal) => `[${refusal.code}] ${refusal.message}`).join('\n'),
|
|
130
|
+
);
|
|
131
|
+
this.name = 'StoreWorkloadCeilingAdmissionError';
|
|
132
|
+
this.distributionId = distributionId;
|
|
133
|
+
this.refusals = refusals;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Admit the store-workload ceiling of `lock` through the kernel
|
|
139
|
+
* (`admitStoreWorkloadCeiling`), or throw
|
|
140
|
+
* {@link StoreWorkloadCeilingAdmissionError} listing every refusal. Returns the
|
|
141
|
+
* resolved ceiling.
|
|
142
|
+
*/
|
|
143
|
+
export function assertStoreWorkloadCeilingAdmitted(schemas, lock) {
|
|
144
|
+
const { ceiling, refusals } = schemas.admitStoreWorkloadCeiling(lock);
|
|
145
|
+
if (refusals.length > 0) {
|
|
146
|
+
throw new StoreWorkloadCeilingAdmissionError(lock.distributionId, refusals);
|
|
147
|
+
}
|
|
148
|
+
return ceiling;
|
|
149
|
+
}
|
|
@@ -81,6 +81,18 @@ export function componentOptionalOperationRequirements(component, where) {
|
|
|
81
81
|
return declaredPrivilegeList(component, where, 'optionalOperations', OperationIdSchema, 'operation id');
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
/**
|
|
85
|
+
* The operations one workload component asks to exercise for the organization
|
|
86
|
+
* that installs its biome (`requestsInstalledOperations`), graded by the same
|
|
87
|
+
* operation-id grammar. Declared INTENT only; the lock admits the ceiling of
|
|
88
|
+
* the request (`admitInstalledOperationCeiling`), and an installed operation is
|
|
89
|
+
* never a deployment grant.
|
|
90
|
+
*/
|
|
91
|
+
export function componentInstalledOperationRequests(component, where) {
|
|
92
|
+
const { OperationIdSchema } = loadKernelPrivilegeGrammars();
|
|
93
|
+
return declaredPrivilegeList(component, where, 'requestsInstalledOperations', OperationIdSchema, 'operation id');
|
|
94
|
+
}
|
|
95
|
+
|
|
84
96
|
/**
|
|
85
97
|
* The organization sweeps one workload component declares it may hold
|
|
86
98
|
* (`requiresOrganizationSweeps`), each a member of the kernel's closed
|
|
@@ -96,7 +108,12 @@ export function componentOrganizationSweepRequirements(component, where) {
|
|
|
96
108
|
* The privilege declarations a component may carry only if it is a workload
|
|
97
109
|
* principal in the lock.
|
|
98
110
|
*/
|
|
99
|
-
export const PRINCIPAL_PRIVILEGE_FIELDS = Object.freeze([
|
|
111
|
+
export const PRINCIPAL_PRIVILEGE_FIELDS = Object.freeze([
|
|
112
|
+
'requiresOperations',
|
|
113
|
+
'optionalOperations',
|
|
114
|
+
'requestsInstalledOperations',
|
|
115
|
+
'requiresOrganizationSweeps',
|
|
116
|
+
]);
|
|
100
117
|
|
|
101
118
|
/**
|
|
102
119
|
* Thrown when a component that has no workload principal in the lock declares
|
|
@@ -37,7 +37,7 @@ import { assertRosteredChartSetLock } from './edition-chart-set.mjs';
|
|
|
37
37
|
import { EditionLockFile } from './edition-constants.mjs';
|
|
38
38
|
import { editionsRoot } from './edition-workspace.mjs';
|
|
39
39
|
import { adoptCatalogueKernel } from './kernel-package.mjs';
|
|
40
|
-
import { compareStrings, serializeJson, validateImmutableImageLock } from './lib.mjs';
|
|
40
|
+
import { compareStrings, imageLockWriteSchemaVersion, serializeJson, validateImmutableImageLock } from './lib.mjs';
|
|
41
41
|
import { pinnedSource, snapshotPinSource } from './pin-source.mjs';
|
|
42
42
|
import { composeAuthorizationBase, composeManifest, resolveDistributionWithIndex, UnknownSelectionError } from './resolve-distribution.mjs';
|
|
43
43
|
import { resolveImageRows } from './resolve-images.mjs';
|
|
@@ -238,13 +238,14 @@ export async function resolveCustomerImages({ id, catalogue, lock, composed, sch
|
|
|
238
238
|
const seen = new Set(vendorSubjects.map(subjectKey));
|
|
239
239
|
const clash = own.subjects.find((subject) => seen.has(subjectKey(subject)));
|
|
240
240
|
if (clash !== undefined) {throw new Error(`the customer's own image ${clash.kind}:${clash.id} collides with a catalogue image of the same identity`);}
|
|
241
|
+
const mergedSubjects = [...vendorSubjects, ...own.subjects].sort(compareSubjects);
|
|
241
242
|
const merged = validateImmutableImageLock(
|
|
242
243
|
schemas.parseImageLock({
|
|
243
|
-
schemaVersion:
|
|
244
|
+
schemaVersion: imageLockWriteSchemaVersion(schemas, mergedSubjects, `distribution "${id}"`),
|
|
244
245
|
sourceHashAlgorithm: catalogue.images.sourceHashAlgorithm,
|
|
245
246
|
distributionId: id,
|
|
246
247
|
contractAuthority: catalogue.contractAuthority,
|
|
247
|
-
subjects:
|
|
248
|
+
subjects: mergedSubjects,
|
|
248
249
|
platformServiceBindings: [...vendor.platformServiceBindings, ...own.platformServiceBindings].sort((a, b) => compareStrings(a.platformService, b.platformService)),
|
|
249
250
|
webBiomes: [...vendor.webBiomes, ...own.webBiomes].sort((a, b) => compareStrings(a.biomeId, b.biomeId)),
|
|
250
251
|
}),
|
|
@@ -95,8 +95,10 @@ import {
|
|
|
95
95
|
validateServiceDependencyOwnerParity,
|
|
96
96
|
} from './lib.mjs';
|
|
97
97
|
import {
|
|
98
|
+
assertInstalledOperationCeilingAdmitted,
|
|
98
99
|
assertOperationGrantsAdmitted,
|
|
99
100
|
assertOrganizationSweepsAdmitted,
|
|
101
|
+
assertStoreWorkloadCeilingAdmitted,
|
|
100
102
|
} from './operation-grant-admission.mjs';
|
|
101
103
|
import { PUBLISHED_PIN_SOURCE, pinnedSource } from './pin-source.mjs';
|
|
102
104
|
import { verifiedPlatformServiceIndex } from './platform-service-ownership.mjs';
|
|
@@ -307,6 +309,7 @@ export function composeManifest(manifest, pinSource = PUBLISHED_PIN_SOURCE) {
|
|
|
307
309
|
// Visibility is a RESTRICTION; the caller (which holds the kernel enum)
|
|
308
310
|
// reduces the declarations.
|
|
309
311
|
declaredVisibilities: manifest.visibility === undefined ? [] : [manifest.visibility],
|
|
312
|
+
storeWorkloadCeiling: manifest.storeWorkloadCeiling,
|
|
310
313
|
declaredSourceNames: new Set(sources.map((source) => source.name)),
|
|
311
314
|
declaredSources: sources.map((source) => pinnedSource(source, pinSource)),
|
|
312
315
|
owner: manifest.owner,
|
|
@@ -1159,6 +1162,7 @@ export function resolveDistributionFromInputs(
|
|
|
1159
1162
|
trustPolicy,
|
|
1160
1163
|
trustCeiling,
|
|
1161
1164
|
declaredVisibilities,
|
|
1165
|
+
storeWorkloadCeiling,
|
|
1162
1166
|
owner,
|
|
1163
1167
|
leaf,
|
|
1164
1168
|
// Absent on a selector-model composition — including every `composed` a
|
|
@@ -2047,6 +2051,13 @@ export function resolveDistributionFromInputs(
|
|
|
2047
2051
|
Object.keys(e.optionalOperationRequirements).length > 0
|
|
2048
2052
|
? { optionalOperationRequirements: e.optionalOperationRequirements }
|
|
2049
2053
|
: {}),
|
|
2054
|
+
// The operations each workload component asks to exercise for the
|
|
2055
|
+
// installing organization, forwarded verbatim; the lock admits their
|
|
2056
|
+
// ceiling (`admitInstalledOperationCeiling`).
|
|
2057
|
+
...(e.installedOperationRequests !== undefined &&
|
|
2058
|
+
Object.keys(e.installedOperationRequests).length > 0
|
|
2059
|
+
? { installedOperationRequests: e.installedOperationRequests }
|
|
2060
|
+
: {}),
|
|
2050
2061
|
// The organization sweeps each workload component may hold, forwarded
|
|
2051
2062
|
// verbatim; the seal admits them (`admitOrganizationSweeps`).
|
|
2052
2063
|
...(e.organizationSweepRequirements !== undefined &&
|
|
@@ -2202,6 +2213,9 @@ export function resolveDistributionFromInputs(
|
|
|
2202
2213
|
// last for the same reason: an edition without it carries exactly the bytes it
|
|
2203
2214
|
// always did. Not in `inputHash`; the kernel lock schema validates it.
|
|
2204
2215
|
if (composed?.storePublisherTrust !== undefined) {lock.storePublisherTrust = composed.storePublisherTrust;}
|
|
2216
|
+
// The edition's store-workload ceiling — written ONLY when the edition
|
|
2217
|
+
// declares one (the kernel refuses an empty list), carried unchanged.
|
|
2218
|
+
if (storeWorkloadCeiling !== undefined) {lock.storeWorkloadCeiling = storeWorkloadCeiling;}
|
|
2205
2219
|
|
|
2206
2220
|
// (7) Re-derive the ownership index through the KERNEL so the table this
|
|
2207
2221
|
// resolver emits is validated by the exact code every consumer will read
|
|
@@ -2221,6 +2235,8 @@ export function resolveDistributionFromInputs(
|
|
|
2221
2235
|
// never fails (the resolve and `edition check` both run through here).
|
|
2222
2236
|
assertOperationGrantsAdmitted(schemas, parsed, { report: (line) => console.log(`[operations] ${line}`) });
|
|
2223
2237
|
assertOrganizationSweepsAdmitted(schemas, parsed);
|
|
2238
|
+
assertInstalledOperationCeilingAdmitted(schemas, parsed);
|
|
2239
|
+
assertStoreWorkloadCeilingAdmitted(schemas, parsed);
|
|
2224
2240
|
return validateImmutableDistributionLock(lock);
|
|
2225
2241
|
}
|
|
2226
2242
|
|
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
compareStrings,
|
|
62
62
|
hashImageBuildInputs,
|
|
63
63
|
imageInputSourceHashAlgorithm,
|
|
64
|
+
imageLockWriteSchemaVersion,
|
|
64
65
|
loadKernelDistributionSchemas,
|
|
65
66
|
parseResolvedDistributionLock,
|
|
66
67
|
readGitJsonAtCommit,
|
|
@@ -970,6 +971,13 @@ export function editionSourceIds(lock) {
|
|
|
970
971
|
* caller can tell "this image's inputs are unknown" from any other refusal:
|
|
971
972
|
* the carried set is evidence, and with none there is no owner to derive.
|
|
972
973
|
*/
|
|
974
|
+
export class ComposedProcessWorkerError extends Error {
|
|
975
|
+
constructor(message) {
|
|
976
|
+
super(message);
|
|
977
|
+
this.name = 'ComposedProcessWorkerError';
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
973
981
|
export class ComposedProcessInputsError extends Error {
|
|
974
982
|
constructor(message) {
|
|
975
983
|
super(message);
|
|
@@ -1009,16 +1017,31 @@ function composedProcessCarriage(structure, enums, repo, sourceCommit, locate) {
|
|
|
1009
1017
|
const byBiome = new Map();
|
|
1010
1018
|
for (const subject of structure.subjects) {
|
|
1011
1019
|
if (subject.kind !== enums.RuntimeImageSubjectKind.Service || subject.repo !== repo) {continue;}
|
|
1012
|
-
const servicePath =
|
|
1020
|
+
const { path: servicePath, componentKind } = subjectServiceSource(subject, sourceCommit, locate);
|
|
1013
1021
|
for (const owner of subject.owners) {
|
|
1014
1022
|
if (owner.kind !== enums.RuntimeImageOwnerKind.Biome) {continue;}
|
|
1015
|
-
const row = byBiome.get(owner.biomeId) ??
|
|
1016
|
-
|
|
1023
|
+
const row = byBiome.get(owner.biomeId) ??
|
|
1024
|
+
{ biomeId: owner.biomeId, carried: [], uncarried: [], carriedWorkers: [], packageDirs: {} };
|
|
1025
|
+
if (!carried.has(servicePath)) {
|
|
1026
|
+
row.uncarried.push(subject.id);
|
|
1027
|
+
} else if (componentKind === 'worker') {
|
|
1028
|
+
// A worker is never a member: it runs as its own Deployment.
|
|
1029
|
+
row.carriedWorkers.push(subject.id);
|
|
1030
|
+
} else {
|
|
1031
|
+
row.carried.push(subject.id);
|
|
1032
|
+
// The member's package directory IS the composed build's own input entry.
|
|
1033
|
+
row.packageDirs[subject.id] = servicePath;
|
|
1034
|
+
}
|
|
1017
1035
|
byBiome.set(owner.biomeId, row);
|
|
1018
1036
|
}
|
|
1019
1037
|
}
|
|
1020
1038
|
return [...byBiome.values()]
|
|
1021
|
-
.map((row) => ({
|
|
1039
|
+
.map((row) => ({
|
|
1040
|
+
...row,
|
|
1041
|
+
carried: row.carried.sort(compareStrings),
|
|
1042
|
+
uncarried: row.uncarried.sort(compareStrings),
|
|
1043
|
+
carriedWorkers: row.carriedWorkers.sort(compareStrings),
|
|
1044
|
+
}))
|
|
1022
1045
|
.sort((a, b) => compareStrings(a.biomeId, b.biomeId));
|
|
1023
1046
|
}
|
|
1024
1047
|
|
|
@@ -1087,8 +1110,15 @@ export function readComposedProcessDeclarations(
|
|
|
1087
1110
|
* resolve derive the same subject from the same declarations.
|
|
1088
1111
|
*
|
|
1089
1112
|
* Owners are the biomes the image CARRIES, derived from the composed build's
|
|
1090
|
-
* inputs at the sealed commit (`declaration.carriage`):
|
|
1091
|
-
* services
|
|
1113
|
+
* inputs at the sealed commit (`declaration.carriage`): every biome at least one
|
|
1114
|
+
* of whose services is a member, never every biome with a service in the
|
|
1115
|
+
* repository. The members leave the structure and are sealed on the subject
|
|
1116
|
+
* as `members[{ service, packageDir }]` — the per-service membership truth: a
|
|
1117
|
+
* service runs from the composed digest if and only if it is a row (owners
|
|
1118
|
+
* never resolve a service). The edition carries the composed subject plus the
|
|
1119
|
+
* services that are no row (the specials, which keep their own subjects even
|
|
1120
|
+
* inside an owner biome), and nothing addresses a member's own image. A worker
|
|
1121
|
+
* is never a row; an own-image worker the composed build carries is refused.
|
|
1092
1122
|
*/
|
|
1093
1123
|
export function withComposedProcessSubjects(
|
|
1094
1124
|
structure,
|
|
@@ -1099,6 +1129,9 @@ export function withComposedProcessSubjects(
|
|
|
1099
1129
|
) {
|
|
1100
1130
|
if (declarations.length === 0) {return structure;}
|
|
1101
1131
|
const subjects = new Map(structure.subjects.map((subject) => [subjectKey(subject), subject]));
|
|
1132
|
+
// A biome's members run from ONE composed image: a biome owning two composed
|
|
1133
|
+
// subjects would leave its pods two images to choose between.
|
|
1134
|
+
const composedOwnerOf = new Map();
|
|
1102
1135
|
for (const declaration of declarations) {
|
|
1103
1136
|
const services = structure.subjects.filter(
|
|
1104
1137
|
(subject) =>
|
|
@@ -1110,21 +1143,47 @@ export function withComposedProcessSubjects(
|
|
|
1110
1143
|
'biomes the image carries — refusing to guess its owners',
|
|
1111
1144
|
);
|
|
1112
1145
|
}
|
|
1113
|
-
// Owners are the biomes
|
|
1114
|
-
// with only some carried
|
|
1115
|
-
//
|
|
1146
|
+
// Owners are the biomes ANY of whose services the image carries. A biome
|
|
1147
|
+
// with only some carried is an owner too; its services the image does not
|
|
1148
|
+
// carry are specials and keep their own subjects (named here). A biome with
|
|
1149
|
+
// none carried is simply not one.
|
|
1116
1150
|
const biomeOwners = [];
|
|
1151
|
+
const members = new Set();
|
|
1152
|
+
const packageDirOf = new Map();
|
|
1117
1153
|
for (const row of declaration.carriage) {
|
|
1154
|
+
// A worker is never a member. One the composed build carries must run
|
|
1155
|
+
// from a carried sibling's image (`workerImages`, declared in the
|
|
1156
|
+
// manifest), never be dropped into the composed process or fall back.
|
|
1157
|
+
for (const worker of row.carriedWorkers ?? []) {
|
|
1158
|
+
throw new ComposedProcessWorkerError(
|
|
1159
|
+
row.carried.length === 0
|
|
1160
|
+
? `composed-process "${declaration.repo}" carries worker "${worker}" of biome "${row.biomeId}", ` +
|
|
1161
|
+
'and that biome has no carried service whose image it could run — a worker is never a ' +
|
|
1162
|
+
'composed member; carry a service of its biome or take the worker out of the composed inputs'
|
|
1163
|
+
: `composed-process "${declaration.repo}" carries worker "${worker}" of biome "${row.biomeId}" as ` +
|
|
1164
|
+
'its own oci-image — a worker is never a composed member; declare it `artifact.kind: ' +
|
|
1165
|
+
`shared-oci-image\` of a carried service (${row.carried.join(', ')}) or take it out of the composed inputs`,
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1118
1168
|
if (row.carried.length === 0) {continue;}
|
|
1119
1169
|
if (row.uncarried.length > 0) {
|
|
1120
1170
|
onNotice(
|
|
1121
|
-
`composed-process "${declaration.repo}": biome "${row.biomeId}" is
|
|
1171
|
+
`composed-process "${declaration.repo}": biome "${row.biomeId}" is partly carried ` +
|
|
1122
1172
|
`(carried: ${row.carried.join(', ')}; not carried: ${row.uncarried.join(', ')}) — ` +
|
|
1123
|
-
'
|
|
1173
|
+
'its uncarried services keep their own subjects',
|
|
1124
1174
|
);
|
|
1125
|
-
continue;
|
|
1126
1175
|
}
|
|
1176
|
+
const other = composedOwnerOf.get(row.biomeId);
|
|
1177
|
+
if (other !== undefined) {
|
|
1178
|
+
throw new Error(
|
|
1179
|
+
`biome "${row.biomeId}" is carried by the composed-process images of both "${other}" and ` +
|
|
1180
|
+
`"${declaration.repo}" — a biome is owned by exactly one composed subject`,
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
composedOwnerOf.set(row.biomeId, declaration.repo);
|
|
1127
1184
|
biomeOwners.push({ kind: enums.RuntimeImageOwnerKind.Biome, biomeId: row.biomeId });
|
|
1185
|
+
for (const id of row.carried) {members.add(id);}
|
|
1186
|
+
for (const [id, packageDir] of Object.entries(row.packageDirs ?? {})) {packageDirOf.set(id, packageDir);}
|
|
1128
1187
|
}
|
|
1129
1188
|
if (biomeOwners.length === 0) {
|
|
1130
1189
|
throw new Error(
|
|
@@ -1162,6 +1221,43 @@ export function withComposedProcessSubjects(
|
|
|
1162
1221
|
`(${[...namespaces].join(', ')}) — a composed image of one build has one`,
|
|
1163
1222
|
);
|
|
1164
1223
|
}
|
|
1224
|
+
// A MEMBER reaches its pods through this one subject (every member runs as a
|
|
1225
|
+
// group-of-one of the composed image), so it carries no subject of its own:
|
|
1226
|
+
// no `<imageRef>:<contentHash>` of its own is probed, verified or sealed. A
|
|
1227
|
+
// member is a service of this build the composed build's inputs carry
|
|
1228
|
+
// (`declaration.carriage`), every owner of which is a biome owning the
|
|
1229
|
+
// composed image. Any other service (not carried, or owned by anything but
|
|
1230
|
+
// a biome, e.g. shared with a platform service) keeps its own.
|
|
1231
|
+
const composedOwners = new Set(biomeOwners.map((owner) => owner.biomeId));
|
|
1232
|
+
const memberRows = [];
|
|
1233
|
+
for (const subject of services) {
|
|
1234
|
+
if (
|
|
1235
|
+
members.has(subject.id) &&
|
|
1236
|
+
subject.owners.length > 0 &&
|
|
1237
|
+
subject.owners.every(
|
|
1238
|
+
(owner) => owner.kind === enums.RuntimeImageOwnerKind.Biome && composedOwners.has(owner.biomeId),
|
|
1239
|
+
)
|
|
1240
|
+
) {
|
|
1241
|
+
subjects.delete(subjectKey(subject));
|
|
1242
|
+
const packageDir = packageDirOf.get(subject.id);
|
|
1243
|
+
if (packageDir === undefined) {
|
|
1244
|
+
throw new ComposedProcessInputsError(
|
|
1245
|
+
`composed-process "${declaration.repo}": member "${subject.id}" has no package directory in ` +
|
|
1246
|
+
'the composed build inputs — its shared-image workers would have nowhere to run from',
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1249
|
+
memberRows.push({ service: subject.id, packageDir });
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
if (memberRows.length === 0) {
|
|
1253
|
+
throw new ComposedProcessInputsError(
|
|
1254
|
+
`composed-process "${declaration.repo}" has no member: every service its build carries ` +
|
|
1255
|
+
`(${[...members].sort(compareStrings).join(', ')}) is also owned by something other than a biome ` +
|
|
1256
|
+
'it owns (e.g. a platform service), so each keeps its own subject — a composed image with no ' +
|
|
1257
|
+
'member runs nothing; take it out of the edition or carry a biome-owned service',
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1260
|
+
memberRows.sort((a, b) => compareStrings(a.service, b.service));
|
|
1165
1261
|
const id = composedProcessSubjectId(declaration.sourceId);
|
|
1166
1262
|
addSubject(subjects, {
|
|
1167
1263
|
kind: enums.RuntimeImageSubjectKind.ComposedProcess,
|
|
@@ -1174,6 +1270,7 @@ export function withComposedProcessSubjects(
|
|
|
1174
1270
|
sourceManifestPath: declaration.sourceManifestPath,
|
|
1175
1271
|
requiredPlatforms,
|
|
1176
1272
|
owners: biomeOwners,
|
|
1273
|
+
members: memberRows,
|
|
1177
1274
|
});
|
|
1178
1275
|
}
|
|
1179
1276
|
return {
|
|
@@ -1275,11 +1372,21 @@ function sourceCommitMap(structure, locate = FIRST_PARTY_TREES) {
|
|
|
1275
1372
|
* service directory to hash the SAME build-input closure the lock names.
|
|
1276
1373
|
*/
|
|
1277
1374
|
export function subjectServicePath(subject, sourceCommit, locate = FIRST_PARTY_TREES) {
|
|
1375
|
+
return subjectServiceSource(subject, sourceCommit, locate).path;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
/**
|
|
1379
|
+
* The repository-relative source directory of a subject and the kind of the
|
|
1380
|
+
* manifest component it is built from (`worker` for a worker component,
|
|
1381
|
+
* `service` otherwise — a legacy `ships.apis[]` entry or a subject with its own
|
|
1382
|
+
* source manifest is not a worker).
|
|
1383
|
+
*/
|
|
1384
|
+
function subjectServiceSource(subject, sourceCommit, locate = FIRST_PARTY_TREES) {
|
|
1278
1385
|
const { dir: repoDirectory, base } = locate(subject.repo);
|
|
1279
1386
|
const toRepoRelative = (absolute) =>
|
|
1280
1387
|
relative(repoDirectory, absolute).split(sep).join('/') || '.';
|
|
1281
1388
|
if (subject.sourceManifestPath) {
|
|
1282
|
-
return toRepoRelative(join(base, subject.path));
|
|
1389
|
+
return { path: toRepoRelative(join(base, subject.path)), componentKind: 'service' };
|
|
1283
1390
|
}
|
|
1284
1391
|
const biomePath = toRepoRelative(join(base, subject.path));
|
|
1285
1392
|
const manifestPath =
|
|
@@ -1317,6 +1424,7 @@ export function subjectServicePath(subject, sourceCommit, locate = FIRST_PARTY_T
|
|
|
1317
1424
|
);
|
|
1318
1425
|
}
|
|
1319
1426
|
let servicePath;
|
|
1427
|
+
let componentKind = 'service';
|
|
1320
1428
|
if (v4ServiceComponents.length > 0) {
|
|
1321
1429
|
// A SERVICE is identified by `protocol.serviceName`; a WORKER has no
|
|
1322
1430
|
// service protocol and is identified by its component `key`. Matching only
|
|
@@ -1340,6 +1448,7 @@ export function subjectServicePath(subject, sourceCommit, locate = FIRST_PARTY_T
|
|
|
1340
1448
|
);
|
|
1341
1449
|
}
|
|
1342
1450
|
servicePath = component.artifact.path;
|
|
1451
|
+
componentKind = component.kind;
|
|
1343
1452
|
} else {
|
|
1344
1453
|
if (legacyApis.length === 0) {
|
|
1345
1454
|
throw new Error(
|
|
@@ -1357,7 +1466,7 @@ export function subjectServicePath(subject, sourceCommit, locate = FIRST_PARTY_T
|
|
|
1357
1466
|
}
|
|
1358
1467
|
servicePath = api.path;
|
|
1359
1468
|
}
|
|
1360
|
-
return toRepoRelative(join(base, subject.path, servicePath));
|
|
1469
|
+
return { path: toRepoRelative(join(base, subject.path, servicePath)), componentKind };
|
|
1361
1470
|
}
|
|
1362
1471
|
|
|
1363
1472
|
function assertSourceManifestParity(structure, commitByRepo, locate = FIRST_PARTY_TREES) {
|
|
@@ -1815,7 +1924,7 @@ export async function resolveImages(id, { inputs, lock, onSubjectHashed } = {})
|
|
|
1815
1924
|
const schemas = await loadKernelDistributionSchemas();
|
|
1816
1925
|
const imageLock = validateImmutableImageLock(
|
|
1817
1926
|
schemas.parseImageLock({
|
|
1818
|
-
schemaVersion:
|
|
1927
|
+
schemaVersion: imageLockWriteSchemaVersion(schemas, subjects, `distribution "${id}"`),
|
|
1819
1928
|
sourceHashAlgorithm: schemas.ImageSourceHashAlgorithm.ImageInputV2,
|
|
1820
1929
|
distributionId: id,
|
|
1821
1930
|
contractAuthority,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xemahq/xema",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/xema-dev/xema-cli.git",
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
"@nestjs/common": "^11.1.18",
|
|
50
50
|
"@nestjs/core": "^11.1.18",
|
|
51
51
|
"@nestjs/platform-express": "^11.1.28",
|
|
52
|
-
"@xemahq/biome-sdk": "^
|
|
52
|
+
"@xemahq/biome-sdk": "^22.0.0",
|
|
53
53
|
"@xemahq/biome-supply-chain": "^0.5.0",
|
|
54
54
|
"@xemahq/distribution-source-hash": "^0.2.2",
|
|
55
55
|
"@xemahq/dsl": "^0.24.0",
|
|
56
56
|
"@xemahq/identity-client": "^0.16.0",
|
|
57
|
-
"@xemahq/contracts": "^0.
|
|
57
|
+
"@xemahq/contracts": "^0.94.0",
|
|
58
58
|
"@xemahq/kernel-state-runtime": "^0.8.0",
|
|
59
59
|
"@xemahq/license-verifier": "^0.2.0",
|
|
60
60
|
"@xemahq/service-registry-nest": "^0.12.2",
|
|
@@ -92,6 +92,6 @@
|
|
|
92
92
|
"typecheck": "tsc --noEmit"
|
|
93
93
|
},
|
|
94
94
|
"xemaRelease": {
|
|
95
|
-
"sourceDigest": "sha256:
|
|
95
|
+
"sourceDigest": "sha256:abf054b00865c8a4b0bb44bccd5cc3c65162b8ea8dec47126bc0dfd837392fcd"
|
|
96
96
|
}
|
|
97
97
|
}
|