@xemahq/xema 0.38.0 → 0.40.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/README.md +1 -1
- package/dist/assets/distribution-assets.manifest.json +2 -70
- package/dist/commands/biome-dev.js +2 -3
- package/dist/commands/biome-dev.js.map +1 -1
- package/dist/commands/dev.js +2 -3
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +3 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.js +21 -9
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/installation.d.ts.map +1 -1
- package/dist/commands/installation.js +7 -5
- package/dist/commands/installation.js.map +1 -1
- package/dist/commands/license.d.ts.map +1 -1
- package/dist/commands/license.js +2 -1
- package/dist/commands/license.js.map +1 -1
- package/dist/commands/serve.js +1 -1
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +3 -5
- package/dist/commands/up.js.map +1 -1
- package/dist/edition/verify-image-evidence.bundle.mjs +14 -2
- package/dist/edition/verify-image-evidence.bundle.provenance.json +41 -41
- package/dist/runtime/config/init-presets.d.ts.map +1 -1
- package/dist/runtime/config/init-presets.js +3 -5
- package/dist/runtime/config/init-presets.js.map +1 -1
- package/dist/runtime/config/loader.d.ts +2 -2
- package/dist/runtime/config/loader.d.ts.map +1 -1
- package/dist/runtime/config/loader.js +1 -1
- package/dist/runtime/config/loader.js.map +1 -1
- package/dist/runtime/config/schema.d.ts +149 -1
- package/dist/runtime/config/schema.d.ts.map +1 -1
- package/dist/runtime/config/schema.js +17 -4
- package/dist/runtime/config/schema.js.map +1 -1
- package/dist/runtime/installation/setup-profile.d.ts +1 -1
- package/dist/runtime/installation/setup-profile.d.ts.map +1 -1
- package/dist/runtime/installation/setup-profile.js +1 -1
- package/dist/runtime/installation/setup-profile.js.map +1 -1
- package/edition/biome-content.mjs +35 -6
- package/edition/build-platform-service-index.mjs +1 -1
- package/edition/develop-edition-resolve.mjs +58 -20
- package/edition/develop-image-probe.mjs +117 -0
- package/edition/develop-seal.mjs +4 -12
- package/edition/dispatched-runtime-components.mjs +1 -1
- package/edition/dist-check.mjs +3 -2
- package/edition/lib.mjs +1 -1
- package/edition/map-with-concurrency.mjs +19 -0
- package/edition/oras-process.mjs +35 -0
- package/edition/oras.mjs +123 -94
- package/edition/resolve-chart-set.mjs +12 -8
- package/edition/resolve-images.mjs +119 -34
- package/edition/verify-image-platforms.mjs +28 -26
- package/edition/workspace-sections.mjs +22 -1
- package/package.json +2 -2
- package/dist/assets/cloud/distribution.lock.json +0 -6447
- package/dist/assets/cloud/images.lock.json +0 -1831
- package/dist/assets/minimal/distribution.lock.json +0 -3297
- package/dist/assets/minimal/images.lock.json +0 -1189
- package/dist/assets/oss/distribution.lock.json +0 -6266
- package/dist/assets/oss/images.lock.json +0 -1750
- package/dist/assets/starter/distribution.lock.json +0 -4165
- package/dist/assets/starter/images.lock.json +0 -1454
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
import { execFileSync } from 'node:child_process';
|
|
36
36
|
import { writeFileSync } from 'node:fs';
|
|
37
37
|
import { join, relative, sep } from 'node:path';
|
|
38
|
+
import { setImmediate as yieldToEventLoop } from 'node:timers/promises';
|
|
38
39
|
|
|
39
40
|
import { assertBuildLedgerComplete } from './build-ledger-completeness.mjs';
|
|
40
41
|
import { BUILD_LEDGER_FILE, readBuildLedger } from './build-ledger.mjs';
|
|
@@ -45,7 +46,7 @@ import {
|
|
|
45
46
|
distributionOutputPath,
|
|
46
47
|
readBoundDistributionFile,
|
|
47
48
|
} from './distribution-paths.mjs';
|
|
48
|
-
import { editionWorkspaceConfig } from './edition-config.mjs';
|
|
49
|
+
import { editionWorkspaceConfig, workspaceSection } from './edition-config.mjs';
|
|
49
50
|
import { editionPinnedInputs } from './edition-pinned-inputs.mjs';
|
|
50
51
|
import { dataRoot, editionsRoot, WorkspaceRoot, configureEditionWorkspaceFromProcess, isEntryModule, sourcePath, sourcesRoot } from './edition-workspace.mjs';
|
|
51
52
|
import {
|
|
@@ -398,6 +399,14 @@ function missingArtifactDependencyMessage(lock, serviceName, binding, biomeIndex
|
|
|
398
399
|
* `dispatchesRuntimeComponents`: the runtime components that biome launches,
|
|
399
400
|
* whose images enter this roster as runtime-component rows although their
|
|
400
401
|
* biomes are not rostered (see `dispatched-runtime-components.mjs`).
|
|
402
|
+
*
|
|
403
|
+
* `options.supplementalSources` is the edition workspace's
|
|
404
|
+
* `runtimeImageSources.supplementalSources`: the declarations the source
|
|
405
|
+
* index's supplemental subjects were built from, carrying each one's
|
|
406
|
+
* `launchedBy`. A subject launched by a biome ships, owned by that biome, in
|
|
407
|
+
* every edition whose roster contains it; one a platform service binds ships
|
|
408
|
+
* with that service. A declared subject with neither is refused by name: it
|
|
409
|
+
* could never enter any edition, and it used to be dropped silently.
|
|
401
410
|
*/
|
|
402
411
|
export function resolveImageStructure(
|
|
403
412
|
lock,
|
|
@@ -405,7 +414,7 @@ export function resolveImageStructure(
|
|
|
405
414
|
enums,
|
|
406
415
|
requiredPlatforms,
|
|
407
416
|
declaredSources,
|
|
408
|
-
{ biomeIndex } = {},
|
|
417
|
+
{ biomeIndex, supplementalSources } = {},
|
|
409
418
|
) {
|
|
410
419
|
if (!Array.isArray(requiredPlatforms) || requiredPlatforms.length === 0) {
|
|
411
420
|
throw new Error(
|
|
@@ -420,6 +429,12 @@ export function resolveImageStructure(
|
|
|
420
429
|
'the runtime components a rostered biome dispatches, so a roster without it can omit images',
|
|
421
430
|
);
|
|
422
431
|
}
|
|
432
|
+
if (!Array.isArray(supplementalSources)) {
|
|
433
|
+
throw new Error(
|
|
434
|
+
`distribution "${lock.distributionId}" resolved its images without the supplemental source declarations — ` +
|
|
435
|
+
'they say which biome launches a supplemental subject, so a roster without them can omit images',
|
|
436
|
+
);
|
|
437
|
+
}
|
|
423
438
|
const subjects = new Map();
|
|
424
439
|
const webBiomes = [];
|
|
425
440
|
const platformServiceBindings = [];
|
|
@@ -436,6 +451,28 @@ export function resolveImageStructure(
|
|
|
436
451
|
}
|
|
437
452
|
return pin;
|
|
438
453
|
};
|
|
454
|
+
// A supplemental subject of the runtime-image source index, built at its
|
|
455
|
+
// repository pin, owned by whatever brings it into this edition.
|
|
456
|
+
const addSupplementalSubject = (supplemental, owners) => {
|
|
457
|
+
const repository = requireRepositoryPin(supplemental.repo);
|
|
458
|
+
if (supplemental.sourceCommit !== repository.sourceCommit) {
|
|
459
|
+
throw new Error(
|
|
460
|
+
`runtime subject "${supplemental.kind}:${supplemental.id}" source commit does not match its repository pin`,
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
addSubject(subjects, {
|
|
464
|
+
kind: supplemental.kind,
|
|
465
|
+
id: supplemental.id,
|
|
466
|
+
imageRef: supplemental.imageRef,
|
|
467
|
+
repo: supplemental.repo,
|
|
468
|
+
path: supplemental.path,
|
|
469
|
+
sourceCommit: repository.sourceCommit,
|
|
470
|
+
sourceManifestHash: supplemental.sourceManifestHash,
|
|
471
|
+
sourceManifestPath: supplemental.sourceManifestPath,
|
|
472
|
+
requiredPlatforms,
|
|
473
|
+
owners,
|
|
474
|
+
});
|
|
475
|
+
};
|
|
439
476
|
|
|
440
477
|
for (const biome of lock.biomes) {
|
|
441
478
|
// WHERE THIS BIOME'S IMAGES LIVE, and WHICH COMMIT THEY ARE BUILT FROM.
|
|
@@ -674,6 +711,50 @@ export function resolveImageStructure(
|
|
|
674
711
|
]),
|
|
675
712
|
);
|
|
676
713
|
|
|
714
|
+
// Supplemental subjects a BIOME launches (`launchedBy`): sealed, owned by
|
|
715
|
+
// that biome, in every edition whose roster contains it. The catalogue
|
|
716
|
+
// checks run for every edition, rostered launcher or not.
|
|
717
|
+
const boundSubjects = new Set(
|
|
718
|
+
runtimeSourceIndex.platformServiceBindings.map((binding) => bindingKey(binding)),
|
|
719
|
+
);
|
|
720
|
+
const launchers = new Map(
|
|
721
|
+
supplementalSources
|
|
722
|
+
.filter((source) => source.launchedBy !== undefined)
|
|
723
|
+
.map((source) => [subjectKey(source), source.launchedBy]),
|
|
724
|
+
);
|
|
725
|
+
for (const key of launchers.keys()) {
|
|
726
|
+
if (!supplementalSubjects.has(key)) {
|
|
727
|
+
throw new Error(
|
|
728
|
+
`supplemental source "${key.replace('\0', ':')}" declares a launcher, but the runtime image source index ` +
|
|
729
|
+
'has no such supplemental subject — rebuild the index from the current declarations',
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
for (const [key, supplemental] of supplementalSubjects) {
|
|
734
|
+
const launcher = launchers.get(key);
|
|
735
|
+
if (launcher === undefined) {
|
|
736
|
+
if (!boundSubjects.has(key)) {
|
|
737
|
+
throw new Error(
|
|
738
|
+
`supplemental subject "${supplemental.kind}:${supplemental.id}" has neither a launchedBy nor a ` +
|
|
739
|
+
'platform-service binding, so no edition could ever carry it — declare what launches it ' +
|
|
740
|
+
'(runtimeImageSources.supplementalSources[].launchedBy) or bind it to a platform service',
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
if (!biomeIndex.some((entry) => entry.id === launcher.biomeId && entry.source === undefined)) {
|
|
746
|
+
throw new Error(
|
|
747
|
+
`supplemental subject "${supplemental.kind}:${supplemental.id}" is launched by biome ` +
|
|
748
|
+
`"${launcher.biomeId}", which the first-party biome index does not carry`,
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
if (lock.biomes.some((biome) => biome.id === launcher.biomeId)) {
|
|
752
|
+
addSupplementalSubject(supplemental, [
|
|
753
|
+
{ kind: enums.RuntimeImageOwnerKind.Biome, biomeId: launcher.biomeId },
|
|
754
|
+
]);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
677
758
|
for (const service of lock.platformServices ?? []) {
|
|
678
759
|
let binding;
|
|
679
760
|
// The discriminator is the DECLARED runtime binding, never "does the lock
|
|
@@ -726,29 +807,12 @@ export function resolveImageStructure(
|
|
|
726
807
|
const key = bindingKey(binding);
|
|
727
808
|
const supplemental = supplementalSubjects.get(key);
|
|
728
809
|
if (supplemental) {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
addSubject(subjects, {
|
|
736
|
-
kind: supplemental.kind,
|
|
737
|
-
id: supplemental.id,
|
|
738
|
-
imageRef: supplemental.imageRef,
|
|
739
|
-
repo: supplemental.repo,
|
|
740
|
-
path: supplemental.path,
|
|
741
|
-
sourceCommit: repository.sourceCommit,
|
|
742
|
-
sourceManifestHash: supplemental.sourceManifestHash,
|
|
743
|
-
sourceManifestPath: supplemental.sourceManifestPath,
|
|
744
|
-
requiredPlatforms,
|
|
745
|
-
owners: [
|
|
746
|
-
{
|
|
747
|
-
kind: enums.RuntimeImageOwnerKind.PlatformService,
|
|
748
|
-
platformService: service.name,
|
|
749
|
-
},
|
|
750
|
-
],
|
|
751
|
-
});
|
|
810
|
+
addSupplementalSubject(supplemental, [
|
|
811
|
+
{
|
|
812
|
+
kind: enums.RuntimeImageOwnerKind.PlatformService,
|
|
813
|
+
platformService: service.name,
|
|
814
|
+
},
|
|
815
|
+
]);
|
|
752
816
|
} else {
|
|
753
817
|
const target = subjects.get(key);
|
|
754
818
|
if (!target) {
|
|
@@ -1523,6 +1587,18 @@ function snapshotImageInputs(schemas, { inputs, lock }) {
|
|
|
1523
1587
|
};
|
|
1524
1588
|
}
|
|
1525
1589
|
|
|
1590
|
+
/**
|
|
1591
|
+
* The supplemental source declarations an image resolve reads beside
|
|
1592
|
+
* `runtimeSourceIndex`. An index with no supplemental subjects (a customer
|
|
1593
|
+
* edition's own rows) needs none, and its workspace need not declare the
|
|
1594
|
+
* section; any other reads the edition workspace's.
|
|
1595
|
+
*/
|
|
1596
|
+
export function supplementalSourcesFor(runtimeSourceIndex) {
|
|
1597
|
+
return runtimeSourceIndex.supplementalSubjects.length === 0
|
|
1598
|
+
? []
|
|
1599
|
+
: workspaceSection('runtimeImageSources', 'resolving supplemental image subjects').supplementalSources;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1526
1602
|
/**
|
|
1527
1603
|
* The image ROWS of one edition: its subjects (hashed at the commits the lock
|
|
1528
1604
|
* pins), its platform-service bindings and its bundled web biomes, after every
|
|
@@ -1531,7 +1607,7 @@ function snapshotImageInputs(schemas, { inputs, lock }) {
|
|
|
1531
1607
|
* of one (a customer's own rows, merged with the catalogue's) parses the lock
|
|
1532
1608
|
* it completes. `resolveImages` is these rows parsed as the whole lock.
|
|
1533
1609
|
*/
|
|
1534
|
-
export async function resolveImageRows(id, { inputs, lock } = {}) {
|
|
1610
|
+
export async function resolveImageRows(id, { inputs, lock, onSubjectHashed } = {}) {
|
|
1535
1611
|
assertCanonicalDistributionId(id);
|
|
1536
1612
|
if ((inputs === undefined) !== (lock === undefined)) {
|
|
1537
1613
|
throw new Error('resolveImages takes the snapshot `inputs` and its distribution `lock` together, or neither');
|
|
@@ -1555,7 +1631,7 @@ export async function resolveImageRows(id, { inputs, lock } = {}) {
|
|
|
1555
1631
|
schemas,
|
|
1556
1632
|
requiredPlatforms,
|
|
1557
1633
|
declaredSources,
|
|
1558
|
-
{ biomeIndex },
|
|
1634
|
+
{ biomeIndex, supplementalSources: supplementalSourcesFor(runtimeSourceIndex) },
|
|
1559
1635
|
),
|
|
1560
1636
|
schemas,
|
|
1561
1637
|
requiredPlatforms,
|
|
@@ -1587,8 +1663,8 @@ export async function resolveImageRows(id, { inputs, lock } = {}) {
|
|
|
1587
1663
|
// made that edition-independent quantity depend on the edition, which failed
|
|
1588
1664
|
// in both directions at once:
|
|
1589
1665
|
// • it is UNCOMPUTABLE for an edition that does not itself resolve the
|
|
1590
|
-
// source repo — `appliance-lean`
|
|
1591
|
-
// biome, so re-resolving
|
|
1666
|
+
// source repo — `appliance-lean` does not ship the xema-store
|
|
1667
|
+
// biome, so re-resolving it threw here and their locks could not
|
|
1592
1668
|
// be regenerated at all, even though the image they name is the very one
|
|
1593
1669
|
// the other six editions resolve fine;
|
|
1594
1670
|
// • had it not thrown it would have been WORSE, silently minting a second
|
|
@@ -1629,14 +1705,15 @@ export async function resolveImageRows(id, { inputs, lock } = {}) {
|
|
|
1629
1705
|
...(crossRepoInputsByConsumer.get(`${subject.repo}\u0000`) ?? []),
|
|
1630
1706
|
...(crossRepoInputsByConsumer.get(`${subject.repo}\u0000${subject.id}`) ?? []),
|
|
1631
1707
|
];
|
|
1632
|
-
const subjects =
|
|
1708
|
+
const subjects = [];
|
|
1709
|
+
for (const subject of structure.subjects) {
|
|
1633
1710
|
const sourceCommit = commitsBefore.get(subject.repo);
|
|
1634
1711
|
const repoDirectory = locate(subject.repo).dir;
|
|
1635
1712
|
const servicePath = subjectServicePath(subject, sourceCommit, locate);
|
|
1636
1713
|
const { sourceManifestPath: ignoredSourceManifestPath, ...lockedSubject } =
|
|
1637
1714
|
subject;
|
|
1638
1715
|
void ignoredSourceManifestPath;
|
|
1639
|
-
|
|
1716
|
+
const hashed = {
|
|
1640
1717
|
...lockedSubject,
|
|
1641
1718
|
sourceCommit,
|
|
1642
1719
|
contentHash: exactImageBuildInputHash(
|
|
@@ -1652,7 +1729,15 @@ export async function resolveImageRows(id, { inputs, lock } = {}) {
|
|
|
1652
1729
|
imageInputSourceHashAlgorithm(),
|
|
1653
1730
|
),
|
|
1654
1731
|
};
|
|
1655
|
-
|
|
1732
|
+
subjects.push(hashed);
|
|
1733
|
+
// The hash is the last thing a registry probe waits for: hand the subject
|
|
1734
|
+
// over now, and let the event loop run so the probes of the subjects
|
|
1735
|
+
// already hashed progress while the rest hash (hashing is synchronous).
|
|
1736
|
+
if (onSubjectHashed !== undefined) {
|
|
1737
|
+
onSubjectHashed(hashed);
|
|
1738
|
+
await yieldToEventLoop();
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1656
1741
|
// Release lane only: a develop snapshot or customer overlay is not covered by
|
|
1657
1742
|
// the committed ledger.
|
|
1658
1743
|
if (inputs === undefined) {
|
|
@@ -1690,8 +1775,8 @@ export async function resolveImageRows(id, { inputs, lock } = {}) {
|
|
|
1690
1775
|
}
|
|
1691
1776
|
|
|
1692
1777
|
/** One edition's COMPLETE image lock: its rows, parsed and validated as the kernel's image lock. */
|
|
1693
|
-
export async function resolveImages(id, { inputs, lock } = {}) {
|
|
1694
|
-
const { subjects, platformServiceBindings, webBiomes, contractAuthority, commits } = await resolveImageRows(id, { inputs, lock });
|
|
1778
|
+
export async function resolveImages(id, { inputs, lock, onSubjectHashed } = {}) {
|
|
1779
|
+
const { subjects, platformServiceBindings, webBiomes, contractAuthority, commits } = await resolveImageRows(id, { inputs, lock, onSubjectHashed });
|
|
1695
1780
|
const schemas = await loadKernelDistributionSchemas();
|
|
1696
1781
|
const imageLock = validateImmutableImageLock(
|
|
1697
1782
|
schemas.parseImageLock({
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
loadKernelDistributionSchemas,
|
|
47
47
|
validateImmutableImageLock,
|
|
48
48
|
} from './lib.mjs';
|
|
49
|
+
import { mapWithConcurrency } from './map-with-concurrency.mjs';
|
|
49
50
|
import { OrasRegistry } from './oras.mjs';
|
|
50
51
|
import {
|
|
51
52
|
REGISTRY_CREDENTIALS_FILE,
|
|
@@ -284,18 +285,20 @@ function missingPlatformMessage({ distributionId, subject, coordinate, missing,
|
|
|
284
285
|
);
|
|
285
286
|
}
|
|
286
287
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
288
|
+
/**
|
|
289
|
+
* One `OrasRegistry` per registry authority of `subjects`, every credential
|
|
290
|
+
* resolved BEFORE the first request: a run that would die on subject 300 for a
|
|
291
|
+
* missing token must die on subject 0 instead.
|
|
292
|
+
*/
|
|
293
|
+
export function registryClientsFor(subjects, { declarations, env, fetchImpl, run }) {
|
|
294
|
+
const clients = new Map();
|
|
295
|
+
for (const subject of subjects) {
|
|
296
|
+
const { authority } = splitImageRef(subject.imageRef);
|
|
297
|
+
if (clients.has(authority)) {continue;}
|
|
298
|
+
const credential = resolveRegistryCredential({ authority, declarations, env });
|
|
299
|
+
clients.set(authority, new OrasRegistry({ credential, fetchImpl, ...(run ? { run } : {}) }));
|
|
300
|
+
}
|
|
301
|
+
return clients;
|
|
299
302
|
}
|
|
300
303
|
|
|
301
304
|
/**
|
|
@@ -335,30 +338,29 @@ export async function verifyDistributionPlatforms({
|
|
|
335
338
|
// successes, so a caller can name every missing coordinate at once. It never
|
|
336
339
|
// swallows one: the caller decides what each failure means.
|
|
337
340
|
collectFailures = false,
|
|
341
|
+
// `(line) => void`: one line per verified subject, with its elapsed ms; null is silent.
|
|
342
|
+
progress = null,
|
|
338
343
|
}) {
|
|
339
344
|
const declarations = loadRegistryCredentialDeclarations(distributionsRoot);
|
|
340
345
|
|
|
341
|
-
|
|
342
|
-
// subject 300 for a missing token must die on subject 0 instead.
|
|
343
|
-
const clients = new Map();
|
|
344
|
-
for (const subject of imageLock.subjects) {
|
|
345
|
-
const { authority } = splitImageRef(subject.imageRef);
|
|
346
|
-
if (clients.has(authority)) {continue;}
|
|
347
|
-
const credential = resolveRegistryCredential({ authority, declarations, env });
|
|
348
|
-
clients.set(
|
|
349
|
-
authority,
|
|
350
|
-
new OrasRegistry({ credential, fetchImpl, ...(run ? { run } : {}) }),
|
|
351
|
-
);
|
|
352
|
-
}
|
|
346
|
+
const clients = registryClientsFor(imageLock.subjects, { declarations, env, fetchImpl, run });
|
|
353
347
|
|
|
354
348
|
const failures = [];
|
|
355
349
|
const verified = await mapWithConcurrency(
|
|
356
350
|
imageLock.subjects,
|
|
357
351
|
concurrency,
|
|
358
|
-
async (subject) => {
|
|
352
|
+
async (subject, index) => {
|
|
353
|
+
const startedAt = Date.now();
|
|
359
354
|
try {
|
|
360
|
-
|
|
355
|
+
const verifiedSubject = await verifySubject(subject);
|
|
356
|
+
progress?.(
|
|
357
|
+
`[seal] ${index + 1}/${imageLock.subjects.length} ${subject.kind}:${subject.id} ok ${Date.now() - startedAt}ms`,
|
|
358
|
+
);
|
|
359
|
+
return verifiedSubject;
|
|
361
360
|
} catch (error) {
|
|
361
|
+
progress?.(
|
|
362
|
+
`[seal] ${index + 1}/${imageLock.subjects.length} ${subject.kind}:${subject.id} FAILED ${Date.now() - startedAt}ms`,
|
|
363
|
+
);
|
|
362
364
|
if (!collectFailures) {throw error;}
|
|
363
365
|
failures.push({ subject, error });
|
|
364
366
|
return undefined;
|
|
@@ -73,6 +73,24 @@ function subjectKind(value, where) {
|
|
|
73
73
|
return value;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* What launches a supplemental subject that no platform service binds: the
|
|
78
|
+
* owner its image-lock row is sealed under. Closed, one member until a second
|
|
79
|
+
* real case exists: a BIOME, which launches the subject's image itself, so
|
|
80
|
+
* the subject ships in every edition whose roster contains that biome.
|
|
81
|
+
*/
|
|
82
|
+
export const SupplementalLauncherKind = Object.freeze({
|
|
83
|
+
Biome: 'biome',
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const LAUNCHER_KINDS = Object.values(SupplementalLauncherKind);
|
|
87
|
+
|
|
88
|
+
function supplementalLauncher(value, where) {
|
|
89
|
+
exactObject(value, where, ['kind', 'biomeId']);
|
|
90
|
+
if (!LAUNCHER_KINDS.includes(value.kind)) {fail(`${where}.kind`, `must be one of ${LAUNCHER_KINDS.join(', ')}`);}
|
|
91
|
+
return Object.freeze({ kind: value.kind, biomeId: text(value.biomeId, `${where}.biomeId`) });
|
|
92
|
+
}
|
|
93
|
+
|
|
76
94
|
export function parseFirstPartySources(value) {
|
|
77
95
|
const where = '"firstPartySources"';
|
|
78
96
|
if (!isObject(value) || Object.keys(value).length === 0) {fail(where, 'must be a non-empty {repo handle: source id} object');}
|
|
@@ -138,13 +156,16 @@ export function parseRuntimeImageSources(value) {
|
|
|
138
156
|
const where = '"runtimeImageSources"';
|
|
139
157
|
exactObject(value, where, ['supplementalSources', 'crossRepoBuildInputs', 'buildStepContextDirectories']);
|
|
140
158
|
const supplementalSources = list(value.supplementalSources, `${where}.supplementalSources`, (item, at) => {
|
|
141
|
-
exactObject(item, at, ['kind', 'id', 'repo', 'path', 'sourceManifestPath']);
|
|
159
|
+
exactObject(item, at, ['kind', 'id', 'repo', 'path', 'sourceManifestPath'], ['launchedBy']);
|
|
142
160
|
return {
|
|
143
161
|
kind: subjectKind(item.kind, `${at}.kind`),
|
|
144
162
|
id: text(item.id, `${at}.id`),
|
|
145
163
|
repo: repoHandle(item.repo, `${at}.repo`),
|
|
146
164
|
path: text(item.path, `${at}.path`),
|
|
147
165
|
sourceManifestPath: text(item.sourceManifestPath, `${at}.sourceManifestPath`),
|
|
166
|
+
...(Object.hasOwn(item, 'launchedBy')
|
|
167
|
+
? { launchedBy: supplementalLauncher(item.launchedBy, `${at}.launchedBy`) }
|
|
168
|
+
: {}),
|
|
148
169
|
};
|
|
149
170
|
});
|
|
150
171
|
uniqueBy(supplementalSources, `${where}.supplementalSources`, (item) => `${item.kind}/${item.id}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xemahq/xema",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/xema-dev/xema-cli.git",
|
|
@@ -92,6 +92,6 @@
|
|
|
92
92
|
"typecheck": "tsc --noEmit"
|
|
93
93
|
},
|
|
94
94
|
"xemaRelease": {
|
|
95
|
-
"sourceDigest": "sha256:
|
|
95
|
+
"sourceDigest": "sha256:e3d77e6ac0d871437aec5ec5749446091e4a65c86ce04df7900b22954ae71308"
|
|
96
96
|
}
|
|
97
97
|
}
|