@xemahq/xema 0.38.0 → 0.39.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.
@@ -62628,6 +62628,17 @@ function subjectKind(value, where) {
62628
62628
  }
62629
62629
  return value;
62630
62630
  }
62631
+ var SupplementalLauncherKind = Object.freeze({
62632
+ Biome: "biome"
62633
+ });
62634
+ var LAUNCHER_KINDS = Object.values(SupplementalLauncherKind);
62635
+ function supplementalLauncher(value, where) {
62636
+ exactObject(value, where, ["kind", "biomeId"]);
62637
+ if (!LAUNCHER_KINDS.includes(value.kind)) {
62638
+ fail(`${where}.kind`, `must be one of ${LAUNCHER_KINDS.join(", ")}`);
62639
+ }
62640
+ return Object.freeze({ kind: value.kind, biomeId: text(value.biomeId, `${where}.biomeId`) });
62641
+ }
62631
62642
  function parseFirstPartySources(value) {
62632
62643
  const where = '"firstPartySources"';
62633
62644
  if (!isObject(value) || Object.keys(value).length === 0) {
@@ -62695,13 +62706,14 @@ function parseRuntimeImageSources(value) {
62695
62706
  const where = '"runtimeImageSources"';
62696
62707
  exactObject(value, where, ["supplementalSources", "crossRepoBuildInputs", "buildStepContextDirectories"]);
62697
62708
  const supplementalSources = list(value.supplementalSources, `${where}.supplementalSources`, (item, at) => {
62698
- exactObject(item, at, ["kind", "id", "repo", "path", "sourceManifestPath"]);
62709
+ exactObject(item, at, ["kind", "id", "repo", "path", "sourceManifestPath"], ["launchedBy"]);
62699
62710
  return {
62700
62711
  kind: subjectKind(item.kind, `${at}.kind`),
62701
62712
  id: text(item.id, `${at}.id`),
62702
62713
  repo: repoHandle(item.repo, `${at}.repo`),
62703
62714
  path: text(item.path, `${at}.path`),
62704
- sourceManifestPath: text(item.sourceManifestPath, `${at}.sourceManifestPath`)
62715
+ sourceManifestPath: text(item.sourceManifestPath, `${at}.sourceManifestPath`),
62716
+ ...Object.hasOwn(item, "launchedBy") ? { launchedBy: supplementalLauncher(item.launchedBy, `${at}.launchedBy`) } : {}
62705
62717
  };
62706
62718
  });
62707
62719
  uniqueBy(supplementalSources, `${where}.supplementalSources`, (item) => `${item.kind}/${item.id}`);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "artifactSha256": "sha256:a372353def55d2dc330427711ddb3024938a009b390a6888f752019072c44d5f",
3
+ "artifactSha256": "sha256:a0dea64dc420fa95f88ee79cc0a13927a8ef0b60d333d866a440f3d7a0c78250",
4
4
  "esbuild": {
5
5
  "version": "0.28.2"
6
6
  },
@@ -2383,229 +2383,229 @@
2383
2383
  },
2384
2384
  {
2385
2385
  "package": "@xemahq/xema",
2386
- "version": "0.38.0",
2386
+ "version": "0.39.0",
2387
2387
  "path": "edition/adapter-host-versions.mjs",
2388
2388
  "sha256": "sha256:1b7ebd3592f4da1225fba74aaba912e7d85fbceeb1626f7dcdac83491cbb4d69"
2389
2389
  },
2390
2390
  {
2391
2391
  "package": "@xemahq/xema",
2392
- "version": "0.38.0",
2392
+ "version": "0.39.0",
2393
2393
  "path": "edition/authorization-base.mjs",
2394
2394
  "sha256": "sha256:bbeac7ff01376cb89ed2140526d4795d8e559b3f01612b404a701f19aaa24c6a"
2395
2395
  },
2396
2396
  {
2397
2397
  "package": "@xemahq/xema",
2398
- "version": "0.38.0",
2398
+ "version": "0.39.0",
2399
2399
  "path": "edition/build-ledger.mjs",
2400
2400
  "sha256": "sha256:05202cbdd606aee086c8e9c62d28be68c429653a8b1d73a289ed0fe4ff14da85"
2401
2401
  },
2402
2402
  {
2403
2403
  "package": "@xemahq/xema",
2404
- "version": "0.38.0",
2404
+ "version": "0.39.0",
2405
2405
  "path": "edition/connector-source-authority.mjs",
2406
2406
  "sha256": "sha256:ea94475f4324737673accd7de1303c44db1cbc4dac61a865a06cd83abe310164"
2407
2407
  },
2408
2408
  {
2409
2409
  "package": "@xemahq/xema",
2410
- "version": "0.38.0",
2410
+ "version": "0.39.0",
2411
2411
  "path": "edition/distribution-paths.mjs",
2412
2412
  "sha256": "sha256:d78a5c8405bc6c25d36fcec61b0030e1fdacac7d528e698adb96f906f4b70279"
2413
2413
  },
2414
2414
  {
2415
2415
  "package": "@xemahq/xema",
2416
- "version": "0.38.0",
2416
+ "version": "0.39.0",
2417
2417
  "path": "edition/edition-artifact.mjs",
2418
2418
  "sha256": "sha256:287e4cf042626963e07396c706350e9668adbcacd238a7ad4db1d68bba3858b2"
2419
2419
  },
2420
2420
  {
2421
2421
  "package": "@xemahq/xema",
2422
- "version": "0.38.0",
2422
+ "version": "0.39.0",
2423
2423
  "path": "edition/edition-channel.mjs",
2424
2424
  "sha256": "sha256:3c0c1564dc50be133cad500452d953353aa5beb703142c05a17bbce8175ba9f3"
2425
2425
  },
2426
2426
  {
2427
2427
  "package": "@xemahq/xema",
2428
- "version": "0.38.0",
2428
+ "version": "0.39.0",
2429
2429
  "path": "edition/edition-chart-set.mjs",
2430
2430
  "sha256": "sha256:f9f16b58279c8c2619a39c5cc92bdbc334543d1a324304cd5d3b120d66dc8f3a"
2431
2431
  },
2432
2432
  {
2433
2433
  "package": "@xemahq/xema",
2434
- "version": "0.38.0",
2434
+ "version": "0.39.0",
2435
2435
  "path": "edition/edition-config.mjs",
2436
2436
  "sha256": "sha256:e3997bd9a0082e02d01760868b2a4f41ed3081e904126b30fdf86e7938dc6137"
2437
2437
  },
2438
2438
  {
2439
2439
  "package": "@xemahq/xema",
2440
- "version": "0.38.0",
2440
+ "version": "0.39.0",
2441
2441
  "path": "edition/edition-constants.mjs",
2442
2442
  "sha256": "sha256:71ae51028847ab3430ffec9b229b0516a7705b75a6de1fcfa665f5e1abf8e9bb"
2443
2443
  },
2444
2444
  {
2445
2445
  "package": "@xemahq/xema",
2446
- "version": "0.38.0",
2446
+ "version": "0.39.0",
2447
2447
  "path": "edition/edition-contract-parse.mjs",
2448
2448
  "sha256": "sha256:c575e96b70bda82899931eb0b18567988e2e13e664fa498bcbef96b571dcabb4"
2449
2449
  },
2450
2450
  {
2451
2451
  "package": "@xemahq/xema",
2452
- "version": "0.38.0",
2452
+ "version": "0.39.0",
2453
2453
  "path": "edition/edition-contract.json",
2454
2454
  "sha256": "sha256:aa1a05f1728813a7bdb1e02d9305e9adf641d695e6767305210c67a1d81b38a7"
2455
2455
  },
2456
2456
  {
2457
2457
  "package": "@xemahq/xema",
2458
- "version": "0.38.0",
2458
+ "version": "0.39.0",
2459
2459
  "path": "edition/edition-protocol.mjs",
2460
2460
  "sha256": "sha256:c488ac72ff3f23c0f28f8db159bf84d2c86ec5cda3633db1fe91ca3f21b12214"
2461
2461
  },
2462
2462
  {
2463
2463
  "package": "@xemahq/xema",
2464
- "version": "0.38.0",
2464
+ "version": "0.39.0",
2465
2465
  "path": "edition/edition-source-pins.mjs",
2466
2466
  "sha256": "sha256:b141474fff33b06734fb62cd2552752b991c8267bb1c9bccb22a81dd80e91c6c"
2467
2467
  },
2468
2468
  {
2469
2469
  "package": "@xemahq/xema",
2470
- "version": "0.38.0",
2470
+ "version": "0.39.0",
2471
2471
  "path": "edition/edition-workspace.mjs",
2472
2472
  "sha256": "sha256:18ba727e6b13ca252def25cc6bd9adf894cbc2018198c41ce522637eeac7f871"
2473
2473
  },
2474
2474
  {
2475
2475
  "package": "@xemahq/xema",
2476
- "version": "0.38.0",
2476
+ "version": "0.39.0",
2477
2477
  "path": "edition/evidence-checkout.mjs",
2478
2478
  "sha256": "sha256:31fb669769b31de4c6f2b2501bb96959548172ef166e7568edbbfed7f5079a11"
2479
2479
  },
2480
2480
  {
2481
2481
  "package": "@xemahq/xema",
2482
- "version": "0.38.0",
2482
+ "version": "0.39.0",
2483
2483
  "path": "edition/evidence-grammars.mjs",
2484
2484
  "sha256": "sha256:4dae22b61ade99219a482b971c9780d68a3d37d021e39cdfe860b245364b8b9f"
2485
2485
  },
2486
2486
  {
2487
2487
  "package": "@xemahq/xema",
2488
- "version": "0.38.0",
2488
+ "version": "0.39.0",
2489
2489
  "path": "edition/evidence-trust-pin.mjs",
2490
2490
  "sha256": "sha256:b9dd5c01903394436530783614efb0cd357d982ed2befd844a39328479773cbc"
2491
2491
  },
2492
2492
  {
2493
2493
  "package": "@xemahq/xema",
2494
- "version": "0.38.0",
2494
+ "version": "0.39.0",
2495
2495
  "path": "edition/git-json.mjs",
2496
2496
  "sha256": "sha256:d0d25cda0c19007becabc9a2827c3dcf759b295cd0b41596eb4480c9549a1f21"
2497
2497
  },
2498
2498
  {
2499
2499
  "package": "@xemahq/xema",
2500
- "version": "0.38.0",
2500
+ "version": "0.39.0",
2501
2501
  "path": "edition/image-delivery-entitlement-descriptor.mjs",
2502
2502
  "sha256": "sha256:4726b66116f01bf2523fafe2b7a356591eca8fb5ca6bd8b154dd4821873e921b"
2503
2503
  },
2504
2504
  {
2505
2505
  "package": "@xemahq/xema",
2506
- "version": "0.38.0",
2506
+ "version": "0.39.0",
2507
2507
  "path": "edition/image-evidence-replay-state.mjs",
2508
2508
  "sha256": "sha256:eba0cf1d38af0ab2f00c5099fa8bc2edc543136ce92f895544d4d284b3d6af0f"
2509
2509
  },
2510
2510
  {
2511
2511
  "package": "@xemahq/xema",
2512
- "version": "0.38.0",
2512
+ "version": "0.39.0",
2513
2513
  "path": "edition/image-evidence.mjs",
2514
2514
  "sha256": "sha256:433202eb2db84510acd11de6fe5ede81c8d96b8bc72991444940f7f84a48897f"
2515
2515
  },
2516
2516
  {
2517
2517
  "package": "@xemahq/xema",
2518
- "version": "0.38.0",
2518
+ "version": "0.39.0",
2519
2519
  "path": "edition/kernel-capability-domain.mjs",
2520
2520
  "sha256": "sha256:3ecf887dd8d399e91a8285f6f115f6e418fb59bbf3f004539fe65ec667219356"
2521
2521
  },
2522
2522
  {
2523
2523
  "package": "@xemahq/xema",
2524
- "version": "0.38.0",
2524
+ "version": "0.39.0",
2525
2525
  "path": "edition/kernel-contract-authority.mjs",
2526
2526
  "sha256": "sha256:20abdde62a7b037c01ea9bbe1ff1e6094e6b0efd0add2f656f1938a04409bf82"
2527
2527
  },
2528
2528
  {
2529
2529
  "package": "@xemahq/xema",
2530
- "version": "0.38.0",
2530
+ "version": "0.39.0",
2531
2531
  "path": "edition/lib.mjs",
2532
2532
  "sha256": "sha256:adb469d6a59dd4f326477973e2d6f2d11cc4b33fff4c438ae1cd6f1da3b46fb0"
2533
2533
  },
2534
2534
  {
2535
2535
  "package": "@xemahq/xema",
2536
- "version": "0.38.0",
2536
+ "version": "0.39.0",
2537
2537
  "path": "edition/no-follow-file.mjs",
2538
2538
  "sha256": "sha256:c17bdf5e441f3c079aa06d4b48bbed05cad087855b9ad2a5e6dc43d5ee2b17c3"
2539
2539
  },
2540
2540
  {
2541
2541
  "package": "@xemahq/xema",
2542
- "version": "0.38.0",
2542
+ "version": "0.39.0",
2543
2543
  "path": "edition/operation-requirements.mjs",
2544
2544
  "sha256": "sha256:e7fed25747ee92be84cdc864ca35a723356df5b3d08923cf71f4bc66a791eb5d"
2545
2545
  },
2546
2546
  {
2547
2547
  "package": "@xemahq/xema",
2548
- "version": "0.38.0",
2548
+ "version": "0.39.0",
2549
2549
  "path": "edition/pin-source.mjs",
2550
2550
  "sha256": "sha256:a23fef4698ef66ba9de4a1aabe3eebe6487d1f105f3fc161bafc5a33b99a2440"
2551
2551
  },
2552
2552
  {
2553
2553
  "package": "@xemahq/xema",
2554
- "version": "0.38.0",
2554
+ "version": "0.39.0",
2555
2555
  "path": "edition/source-authority.mjs",
2556
2556
  "sha256": "sha256:858b607dde94e2abd00a341f569e18cfa567d4bff139c64a2d60abbb7de4c85e"
2557
2557
  },
2558
2558
  {
2559
2559
  "package": "@xemahq/xema",
2560
- "version": "0.38.0",
2560
+ "version": "0.39.0",
2561
2561
  "path": "edition/strict-json.mjs",
2562
2562
  "sha256": "sha256:f95778396df7f38b086f55d2ec4f3b6644a8aeff2b32282a62829145fb544562"
2563
2563
  },
2564
2564
  {
2565
2565
  "package": "@xemahq/xema",
2566
- "version": "0.38.0",
2566
+ "version": "0.39.0",
2567
2567
  "path": "edition/string-order.mjs",
2568
2568
  "sha256": "sha256:b3210cd273e4b2e01fb8b1a91e28734cd211d4010c080d0ce95f5e5ff631cffb"
2569
2569
  },
2570
2570
  {
2571
2571
  "package": "@xemahq/xema",
2572
- "version": "0.38.0",
2572
+ "version": "0.39.0",
2573
2573
  "path": "edition/trusted-executable.mjs",
2574
2574
  "sha256": "sha256:37d46cdbc999ddd55d4ea8aeed4850ddd1efde89ed7b532f5a2ddec87105d796"
2575
2575
  },
2576
2576
  {
2577
2577
  "package": "@xemahq/xema",
2578
- "version": "0.38.0",
2578
+ "version": "0.39.0",
2579
2579
  "path": "edition/verifier-binding.mjs",
2580
2580
  "sha256": "sha256:80732658617f5255258c538eeb2383331ec8553b44b58d552b567845a7b916aa"
2581
2581
  },
2582
2582
  {
2583
2583
  "package": "@xemahq/xema",
2584
- "version": "0.38.0",
2584
+ "version": "0.39.0",
2585
2585
  "path": "edition/verifier-files.mjs",
2586
2586
  "sha256": "sha256:cdd14a20a63f0f194a8abb55680a5a7e3ced1f09f8b210a90d349e655f6de0c0"
2587
2587
  },
2588
2588
  {
2589
2589
  "package": "@xemahq/xema",
2590
- "version": "0.38.0",
2590
+ "version": "0.39.0",
2591
2591
  "path": "edition/verifier-provenance.mjs",
2592
2592
  "sha256": "sha256:3e2fb8e07cf1f66b8ad6c7ac2555fe3af64ec7abeb07cecbaf1939a18c2b3663"
2593
2593
  },
2594
2594
  {
2595
2595
  "package": "@xemahq/xema",
2596
- "version": "0.38.0",
2596
+ "version": "0.39.0",
2597
2597
  "path": "edition/verify-image-evidence.mjs",
2598
2598
  "sha256": "sha256:62f83f8bb835f360535402d12657156d2c9d6e651b426c2b30a04a2dc2f8d3e8"
2599
2599
  },
2600
2600
  {
2601
2601
  "package": "@xemahq/xema",
2602
- "version": "0.38.0",
2602
+ "version": "0.39.0",
2603
2603
  "path": "edition/workspace-sections.mjs",
2604
- "sha256": "sha256:058c849767434e9ba911d9310092bede42e3ef959c75bbd35ff8879dc21ce3ff"
2604
+ "sha256": "sha256:383f37ce066ea5573c30cedf124022095bf94d050f0a266a5881f587cff0ced0"
2605
2605
  },
2606
2606
  {
2607
2607
  "package": "@xemahq/xema",
2608
- "version": "0.38.0",
2608
+ "version": "0.39.0",
2609
2609
  "path": "scripts/build-evidence-verifier.mjs",
2610
2610
  "sha256": "sha256:5a688d20c47dc88aff37aa0c6cc02daf2c370a5bc610fef8446ce7be222d4c0b"
2611
2611
  },
@@ -73,6 +73,7 @@ import {
73
73
  resolveComposedProcessStructure,
74
74
  resolveImageStructure,
75
75
  sourceTreeLocator,
76
+ supplementalSourcesFor,
76
77
  } from './resolve-images.mjs';
77
78
  import { withoutSealedDigests } from './seal-image-digests.mjs';
78
79
  import { createPinnedSourceAuthority } from './source-authority.mjs';
@@ -301,7 +302,7 @@ export async function checkImagesLock(id, checkSourceEvidence) {
301
302
  schemas,
302
303
  requiredPlatforms,
303
304
  resolveDistributionDeclaredSources(schemas, id),
304
- { biomeIndex: pinned.biomeIndex },
305
+ { biomeIndex: pinned.biomeIndex, supplementalSources: supplementalSourcesFor(runtimeSourceIndex) },
305
306
  ),
306
307
  schemas,
307
308
  requiredPlatforms,
@@ -45,7 +45,7 @@ import {
45
45
  distributionOutputPath,
46
46
  readBoundDistributionFile,
47
47
  } from './distribution-paths.mjs';
48
- import { editionWorkspaceConfig } from './edition-config.mjs';
48
+ import { editionWorkspaceConfig, workspaceSection } from './edition-config.mjs';
49
49
  import { editionPinnedInputs } from './edition-pinned-inputs.mjs';
50
50
  import { dataRoot, editionsRoot, WorkspaceRoot, configureEditionWorkspaceFromProcess, isEntryModule, sourcePath, sourcesRoot } from './edition-workspace.mjs';
51
51
  import {
@@ -398,6 +398,14 @@ function missingArtifactDependencyMessage(lock, serviceName, binding, biomeIndex
398
398
  * `dispatchesRuntimeComponents`: the runtime components that biome launches,
399
399
  * whose images enter this roster as runtime-component rows although their
400
400
  * biomes are not rostered (see `dispatched-runtime-components.mjs`).
401
+ *
402
+ * `options.supplementalSources` is the edition workspace's
403
+ * `runtimeImageSources.supplementalSources`: the declarations the source
404
+ * index's supplemental subjects were built from, carrying each one's
405
+ * `launchedBy`. A subject launched by a biome ships, owned by that biome, in
406
+ * every edition whose roster contains it; one a platform service binds ships
407
+ * with that service. A declared subject with neither is refused by name: it
408
+ * could never enter any edition, and it used to be dropped silently.
401
409
  */
402
410
  export function resolveImageStructure(
403
411
  lock,
@@ -405,7 +413,7 @@ export function resolveImageStructure(
405
413
  enums,
406
414
  requiredPlatforms,
407
415
  declaredSources,
408
- { biomeIndex } = {},
416
+ { biomeIndex, supplementalSources } = {},
409
417
  ) {
410
418
  if (!Array.isArray(requiredPlatforms) || requiredPlatforms.length === 0) {
411
419
  throw new Error(
@@ -420,6 +428,12 @@ export function resolveImageStructure(
420
428
  'the runtime components a rostered biome dispatches, so a roster without it can omit images',
421
429
  );
422
430
  }
431
+ if (!Array.isArray(supplementalSources)) {
432
+ throw new Error(
433
+ `distribution "${lock.distributionId}" resolved its images without the supplemental source declarations — ` +
434
+ 'they say which biome launches a supplemental subject, so a roster without them can omit images',
435
+ );
436
+ }
423
437
  const subjects = new Map();
424
438
  const webBiomes = [];
425
439
  const platformServiceBindings = [];
@@ -436,6 +450,28 @@ export function resolveImageStructure(
436
450
  }
437
451
  return pin;
438
452
  };
453
+ // A supplemental subject of the runtime-image source index, built at its
454
+ // repository pin, owned by whatever brings it into this edition.
455
+ const addSupplementalSubject = (supplemental, owners) => {
456
+ const repository = requireRepositoryPin(supplemental.repo);
457
+ if (supplemental.sourceCommit !== repository.sourceCommit) {
458
+ throw new Error(
459
+ `runtime subject "${supplemental.kind}:${supplemental.id}" source commit does not match its repository pin`,
460
+ );
461
+ }
462
+ addSubject(subjects, {
463
+ kind: supplemental.kind,
464
+ id: supplemental.id,
465
+ imageRef: supplemental.imageRef,
466
+ repo: supplemental.repo,
467
+ path: supplemental.path,
468
+ sourceCommit: repository.sourceCommit,
469
+ sourceManifestHash: supplemental.sourceManifestHash,
470
+ sourceManifestPath: supplemental.sourceManifestPath,
471
+ requiredPlatforms,
472
+ owners,
473
+ });
474
+ };
439
475
 
440
476
  for (const biome of lock.biomes) {
441
477
  // WHERE THIS BIOME'S IMAGES LIVE, and WHICH COMMIT THEY ARE BUILT FROM.
@@ -674,6 +710,50 @@ export function resolveImageStructure(
674
710
  ]),
675
711
  );
676
712
 
713
+ // Supplemental subjects a BIOME launches (`launchedBy`): sealed, owned by
714
+ // that biome, in every edition whose roster contains it. The catalogue
715
+ // checks run for every edition, rostered launcher or not.
716
+ const boundSubjects = new Set(
717
+ runtimeSourceIndex.platformServiceBindings.map((binding) => bindingKey(binding)),
718
+ );
719
+ const launchers = new Map(
720
+ supplementalSources
721
+ .filter((source) => source.launchedBy !== undefined)
722
+ .map((source) => [subjectKey(source), source.launchedBy]),
723
+ );
724
+ for (const key of launchers.keys()) {
725
+ if (!supplementalSubjects.has(key)) {
726
+ throw new Error(
727
+ `supplemental source "${key.replace('\0', ':')}" declares a launcher, but the runtime image source index ` +
728
+ 'has no such supplemental subject — rebuild the index from the current declarations',
729
+ );
730
+ }
731
+ }
732
+ for (const [key, supplemental] of supplementalSubjects) {
733
+ const launcher = launchers.get(key);
734
+ if (launcher === undefined) {
735
+ if (!boundSubjects.has(key)) {
736
+ throw new Error(
737
+ `supplemental subject "${supplemental.kind}:${supplemental.id}" has neither a launchedBy nor a ` +
738
+ 'platform-service binding, so no edition could ever carry it — declare what launches it ' +
739
+ '(runtimeImageSources.supplementalSources[].launchedBy) or bind it to a platform service',
740
+ );
741
+ }
742
+ continue;
743
+ }
744
+ if (!biomeIndex.some((entry) => entry.id === launcher.biomeId && entry.source === undefined)) {
745
+ throw new Error(
746
+ `supplemental subject "${supplemental.kind}:${supplemental.id}" is launched by biome ` +
747
+ `"${launcher.biomeId}", which the first-party biome index does not carry`,
748
+ );
749
+ }
750
+ if (lock.biomes.some((biome) => biome.id === launcher.biomeId)) {
751
+ addSupplementalSubject(supplemental, [
752
+ { kind: enums.RuntimeImageOwnerKind.Biome, biomeId: launcher.biomeId },
753
+ ]);
754
+ }
755
+ }
756
+
677
757
  for (const service of lock.platformServices ?? []) {
678
758
  let binding;
679
759
  // The discriminator is the DECLARED runtime binding, never "does the lock
@@ -726,29 +806,12 @@ export function resolveImageStructure(
726
806
  const key = bindingKey(binding);
727
807
  const supplemental = supplementalSubjects.get(key);
728
808
  if (supplemental) {
729
- const repository = requireRepositoryPin(supplemental.repo);
730
- if (supplemental.sourceCommit !== repository.sourceCommit) {
731
- throw new Error(
732
- `runtime subject "${supplemental.kind}:${supplemental.id}" source commit does not match its repository pin`,
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
- });
809
+ addSupplementalSubject(supplemental, [
810
+ {
811
+ kind: enums.RuntimeImageOwnerKind.PlatformService,
812
+ platformService: service.name,
813
+ },
814
+ ]);
752
815
  } else {
753
816
  const target = subjects.get(key);
754
817
  if (!target) {
@@ -1523,6 +1586,18 @@ function snapshotImageInputs(schemas, { inputs, lock }) {
1523
1586
  };
1524
1587
  }
1525
1588
 
1589
+ /**
1590
+ * The supplemental source declarations an image resolve reads beside
1591
+ * `runtimeSourceIndex`. An index with no supplemental subjects (a customer
1592
+ * edition's own rows) needs none, and its workspace need not declare the
1593
+ * section; any other reads the edition workspace's.
1594
+ */
1595
+ export function supplementalSourcesFor(runtimeSourceIndex) {
1596
+ return runtimeSourceIndex.supplementalSubjects.length === 0
1597
+ ? []
1598
+ : workspaceSection('runtimeImageSources', 'resolving supplemental image subjects').supplementalSources;
1599
+ }
1600
+
1526
1601
  /**
1527
1602
  * The image ROWS of one edition: its subjects (hashed at the commits the lock
1528
1603
  * pins), its platform-service bindings and its bundled web biomes, after every
@@ -1555,7 +1630,7 @@ export async function resolveImageRows(id, { inputs, lock } = {}) {
1555
1630
  schemas,
1556
1631
  requiredPlatforms,
1557
1632
  declaredSources,
1558
- { biomeIndex },
1633
+ { biomeIndex, supplementalSources: supplementalSourcesFor(runtimeSourceIndex) },
1559
1634
  ),
1560
1635
  schemas,
1561
1636
  requiredPlatforms,
@@ -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.38.0",
3
+ "version": "0.39.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:6ceee7eaad77b410aeb26998f61349944a56481549d146a3bc42a7a325ab3890"
95
+ "sourceDigest": "sha256:7df2765700a493e798389fee5d75e5a405b33634922715a3d6bf56990bf15cd4"
96
96
  }
97
97
  }