@xemahq/xema 0.32.0 → 0.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,22 @@
1
1
  // release-catalogue — the catalogue of a RELEASE offering, from its committed, sealed locks.
2
2
 
3
- import { readFileSync } from 'node:fs';
4
-
5
3
  import { biomeSourceTreeIds } from './biome-content.mjs';
6
4
  import { buildCatalogue } from './catalogue.mjs';
7
5
  import { EditionChannel } from './edition-constants.mjs';
8
- import { biomeIndexPath, kernelPackageDir, platformServiceIndexPath, runtimeImageSourceIndexPath } from './edition-workspace.mjs';
6
+ import { editionPinnedInputs } from './edition-pinned-inputs.mjs';
7
+ import { kernelPackageDir } from './edition-workspace.mjs';
9
8
  import { kernelPackageOfAuthority } from './kernel-package.mjs';
10
9
  import { loadKernelDistributionSchemas } from './lib.mjs';
11
10
  import { loadEditionManifest } from './resolve-distribution.mjs';
12
11
  import { externalSourceTrees, sourceTreeLocator } from './resolve-images.mjs';
13
- import { committedRepositoryPins, createPinnedSourceAuthority } from './source-authority.mjs';
12
+ import { createPinnedSourceAuthority } from './source-authority.mjs';
14
13
 
15
14
  /** The catalogue of release offering `id`, from the committed locks (`locks` as `readCommittedEdition` returns them). */
16
15
  export async function releaseCatalogue(id, locks, { fetchKernelTarball } = {}) {
17
16
  const schemas = await loadKernelDistributionSchemas();
18
- const authority = createPinnedSourceAuthority({ pins: committedRepositoryPins() });
17
+ // The EDITION's indexes and pins: a held repository is catalogued at the held commit.
18
+ const pinned = await editionPinnedInputs(id, { schemas });
19
+ const authority = createPinnedSourceAuthority({ pins: pinned.repositoryPins });
19
20
  // The WRITER of the catalogue's tree ids: the real `git rev-parse <commit>:<path>` of every biome.
20
21
  const sourceTreeIds = await biomeSourceTreeIds({
21
22
  schemas,
@@ -24,7 +25,7 @@ export async function releaseCatalogue(id, locks, { fetchKernelTarball } = {}) {
24
25
  locate: sourceTreeLocator(externalSourceTrees(locks.distributionLock)),
25
26
  vendoredCommit: (repo) => authority.vendoredSourceCommitForRepository(repo),
26
27
  });
27
- const contractAuthority = schemas.parseRuntimeImageSourceIndex(JSON.parse(readFileSync(runtimeImageSourceIndexPath(), 'utf8'))).contractAuthority;
28
+ const { contractAuthority } = pinned.runtimeImageSourceIndex;
28
29
  return buildCatalogue({
29
30
  sourceTreeIds,
30
31
  offeringId: id,
@@ -32,8 +33,8 @@ export async function releaseCatalogue(id, locks, { fetchKernelTarball } = {}) {
32
33
  distributionLock: locks.distributionLock,
33
34
  imageLock: locks.imageLock,
34
35
  chartSetLock: locks.chartSetLock,
35
- biomeIndex: JSON.parse(readFileSync(biomeIndexPath(), 'utf8')),
36
- platformServiceIndex: schemas.parsePlatformServiceSourceIndex(JSON.parse(readFileSync(platformServiceIndexPath(), 'utf8'))),
36
+ biomeIndex: pinned.biomeIndex,
37
+ platformServiceIndex: pinned.platformServiceIndex,
37
38
  contractAuthority,
38
39
  kernel: await kernelPackageOfAuthority({
39
40
  kernelPackageDir: kernelPackageDir(),
@@ -21,13 +21,14 @@ import { assertCanonicalDistributionId } from './distribution-paths.mjs';
21
21
  import { LOCAL_WORKFLOW, assertEngineImage, editionTarget, workflowFromEnv, writeEditionArtifact } from './edition-artifact.mjs';
22
22
  import { assertRosteredChartSetLock } from './edition-chart-set.mjs';
23
23
  import { EditionChannel, EditionLockFile } from './edition-constants.mjs';
24
+ import { editionPinnedInputs } from './edition-pinned-inputs.mjs';
24
25
  import { WorkspaceRoot, configureEditionWorkspaceFromProcess, editionsRoot, isEntryModule } from './edition-workspace.mjs';
25
26
  import { descriptorEvidenceTrust } from './evidence-trust-policy.mjs';
26
27
  import { loadKernelDistributionSchemas, validateImmutableImageLock } from './lib.mjs';
27
28
  import { releaseCatalogue } from './release-catalogue.mjs';
28
29
  import { assertReleaseEditionTree } from './release-channel-guard.mjs';
29
30
  import { externalSourceTrees, sourceTreeLocator } from './resolve-images.mjs';
30
- import { committedRepositoryPins, createPinnedSourceAuthority } from './source-authority.mjs';
31
+ import { createPinnedSourceAuthority } from './source-authority.mjs';
31
32
 
32
33
  if (isEntryModule(import.meta.url)) {
33
34
  configureEditionWorkspaceFromProcess([WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.Kernel]);
@@ -110,7 +111,7 @@ export async function readCommittedEdition(id, root = editionsRoot()) {
110
111
  { cause: error },
111
112
  );
112
113
  }
113
- const authority = createPinnedSourceAuthority({ pins: committedRepositoryPins() });
114
+ const authority = createPinnedSourceAuthority({ pins: (await editionPinnedInputs(id, { schemas })).repositoryPins });
114
115
  await verifyBiomeContentHashes({
115
116
  schemas,
116
117
  lock: sealed,
@@ -77,7 +77,9 @@ import {
77
77
  import { resolveDispatchedRuntimeComponents } from './dispatched-runtime-components.mjs';
78
78
  import { fleetChartSetLockPath, writeEditionChartSet } from './edition-chart-set.mjs';
79
79
  import { workspaceSection } from './edition-config.mjs';
80
- import { WorkspaceRoot, biomeIndexPath, configureEditionWorkspaceFromProcess, editionsRoot, isEntryModule, kernelPackageDir, platformServiceIndexPath } from './edition-workspace.mjs';
80
+ import { editionPinnedInputs } from './edition-pinned-inputs.mjs';
81
+ import { splitEditionDefinition } from './edition-source-pins.mjs';
82
+ import { WorkspaceRoot, configureEditionWorkspaceFromProcess, editionsRoot, isEntryModule, kernelPackageDir } from './edition-workspace.mjs';
81
83
  import { walkDeclaredSources } from './external-source-walk.mjs';
82
84
  import {
83
85
  biomeTierOrder,
@@ -260,7 +262,7 @@ export function loadEditionManifest(parseDistribution, id) {
260
262
  "(core class, the classes the edition's include names, and services a shipped biome's API requires)",
261
263
  );
262
264
  }
263
- const manifest = parseDistribution(raw);
265
+ const manifest = parseDistribution(splitEditionDefinition(raw, id).document);
264
266
  if (manifest.id !== id) {
265
267
  throw new Error(`distribution at ${path} declares id "${manifest.id}", but it was addressed as "${id}"`);
266
268
  }
@@ -314,26 +316,6 @@ export function composeManifest(manifest, pinSource = PUBLISHED_PIN_SOURCE) {
314
316
  };
315
317
  }
316
318
 
317
- function readBiomeIndex() {
318
- if (!existsSync(biomeIndexPath())) {
319
- throw new Error(
320
- `${biomeIndexPath()} not found — run \`${commandFor('build-biome-index.mjs')}\` first`,
321
- );
322
- }
323
- return JSON.parse(readFileSync(biomeIndexPath(), 'utf8'));
324
- }
325
-
326
- function readPlatformServiceIndex(parsePlatformServiceSourceIndex) {
327
- if (!existsSync(platformServiceIndexPath())) {
328
- throw new Error(
329
- `${platformServiceIndexPath()} not found — run the trusted platform-service index generator first`,
330
- );
331
- }
332
- return parsePlatformServiceSourceIndex(
333
- JSON.parse(readFileSync(platformServiceIndexPath(), 'utf8')),
334
- );
335
- }
336
-
337
319
  /** Unique key for an index/lock entry (id+target+repo distinguishes cultivar/reference + web/server pairs). */
338
320
  function entryKey(e) {
339
321
  return JSON.stringify([e.id, e.target, e.repo]);
@@ -2229,8 +2211,14 @@ export async function resolveDistributionWithIndex(id, { materialize, inputs, re
2229
2211
  // THE RELEASE READS THESE FROM DISK; A DEVELOP SNAPSHOT INJECTS THEM
2230
2212
  // (`develop-snapshot-inputs.mjs`). Either way they pass through the one
2231
2213
  // validation below, so there is a single resolve path and two sources.
2232
- const claimedPlatformServiceIndex =
2233
- inputs?.platformServiceIndex ?? readPlatformServiceIndex(schemas.parsePlatformServiceSourceIndex);
2214
+ // The edition's own indexes (editionPinnedInputs): the committed ones, or
2215
+ // rebuilt at the repositories it holds. Read only when the caller injected
2216
+ // neither.
2217
+ const pinned =
2218
+ inputs?.platformServiceIndex === undefined || inputs?.biomeIndex === undefined
2219
+ ? await editionPinnedInputs(id, { schemas })
2220
+ : undefined;
2221
+ const claimedPlatformServiceIndex = inputs?.platformServiceIndex ?? pinned.platformServiceIndex;
2234
2222
  // OWNERSHIP IS THE SERVICE'S OWN STATEMENT: each entry's capability domains
2235
2223
  // are re-derived from its pinned source manifest (hash-verified), so a
2236
2224
  // hand-edited index cannot move where grants land. Only the customer resolve
@@ -2242,7 +2230,7 @@ export async function resolveDistributionWithIndex(id, { materialize, inputs, re
2242
2230
  ? claimedPlatformServiceIndex
2243
2231
  : verifiedPlatformServiceIndex(claimedPlatformServiceIndex, { readManifest });
2244
2232
  const platformIndex = validateServiceDependencyOwnerParity(
2245
- validateImmutableBiomeIndex(inputs?.biomeIndex ?? readBiomeIndex()),
2233
+ validateImmutableBiomeIndex(inputs?.biomeIndex ?? pinned.biomeIndex),
2246
2234
  platformServiceIndex,
2247
2235
  );
2248
2236
  const { entries, sourceRecords } = await walkDeclaredSources({
@@ -45,7 +45,8 @@ import {
45
45
  readBoundDistributionFile,
46
46
  } from './distribution-paths.mjs';
47
47
  import { editionWorkspaceConfig } from './edition-config.mjs';
48
- import { biomeIndexPath, dataRoot, editionsRoot, runtimeImageSourceIndexPath, WorkspaceRoot, configureEditionWorkspaceFromProcess, isEntryModule, sourcePath, sourcesRoot } from './edition-workspace.mjs';
48
+ import { editionPinnedInputs } from './edition-pinned-inputs.mjs';
49
+ import { dataRoot, editionsRoot, WorkspaceRoot, configureEditionWorkspaceFromProcess, isEntryModule, sourcePath, sourcesRoot } from './edition-workspace.mjs';
49
50
  import {
50
51
  externalSourceCacheDir,
51
52
  materializeGitSource,
@@ -68,7 +69,6 @@ import {
68
69
  } from './lib.mjs';
69
70
  import { assertReleaseContent } from './release-channel-guard.mjs';
70
71
  import { composeEdition } from './resolve-distribution.mjs';
71
- import { readStrictJsonFile } from './strict-json.mjs';
72
72
 
73
73
  if (isEntryModule(import.meta.url)) {
74
74
  configureEditionWorkspaceFromProcess([WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.Kernel, WorkspaceRoot.Cache]);
@@ -164,10 +164,6 @@ const FIRST_PARTY_TREES = (repo) => ({ dir: sourcePath(repo), base: sourcesRoot(
164
164
  const imageBuildInputHashByIdentity = new Map();
165
165
  const sourceManifestHashByIdentity = new Map();
166
166
 
167
- function readJson(path) {
168
- return readStrictJsonFile(path);
169
- }
170
-
171
167
  /**
172
168
  * Memoized image build-input closure hash.
173
169
  *
@@ -1489,8 +1485,13 @@ export function resolveCrossRepoInputsForRoster({
1489
1485
  return crossRepoInputsByConsumer;
1490
1486
  }
1491
1487
 
1492
- /** The release's inputs: the COMMITTED locks and indexes, read from disk. */
1493
- function releaseImageInputs(schemas, id) {
1488
+ /**
1489
+ * The release's inputs: the COMMITTED lock, and the edition's own indexes
1490
+ * (`editionPinnedInputs`: the committed ones, or rebuilt at the repositories the
1491
+ * edition holds).
1492
+ */
1493
+ async function releaseImageInputs(schemas, id) {
1494
+ const pinned = await editionPinnedInputs(id, { schemas });
1494
1495
  return {
1495
1496
  lock: validateImmutableDistributionLock(
1496
1497
  readBoundDistributionFile({
@@ -1501,8 +1502,8 @@ function releaseImageInputs(schemas, id) {
1501
1502
  parse: (raw) => parseResolvedDistributionLock(schemas, raw),
1502
1503
  }).value,
1503
1504
  ),
1504
- runtimeSourceIndex: schemas.parseRuntimeImageSourceIndex(readJson(runtimeImageSourceIndexPath())),
1505
- biomeIndex: readJson(biomeIndexPath()),
1505
+ runtimeSourceIndex: pinned.runtimeImageSourceIndex,
1506
+ biomeIndex: pinned.biomeIndex,
1506
1507
  requiredPlatforms: resolveDistributionRequiredPlatforms(schemas, id),
1507
1508
  declaredSources: resolveDistributionDeclaredSources(schemas, id),
1508
1509
  };
@@ -1544,7 +1545,7 @@ export async function resolveImageRows(id, { inputs, lock } = {}) {
1544
1545
  biomeIndex,
1545
1546
  requiredPlatforms,
1546
1547
  declaredSources,
1547
- } = inputs === undefined ? releaseImageInputs(schemas, id) : snapshotImageInputs(schemas, { inputs, lock });
1548
+ } = inputs === undefined ? await releaseImageInputs(schemas, id) : snapshotImageInputs(schemas, { inputs, lock });
1548
1549
  // Materialise every EXTERNAL source at the commit the lock pins, and resolve
1549
1550
  // every `repo` handle through one locator from here down.
1550
1551
  const locate = sourceTreeLocator(externalSourceTrees(distributionLock));
@@ -118,6 +118,7 @@ import { fetchCatalogue } from './catalogue-fetch.mjs';
118
118
  import { readCataloguePin } from './catalogue-pin.mjs';
119
119
  import { CATALOGUE_TOKEN_ENV_FLAG, CATALOGUE_USERNAME_FLAG, DEFINITION_FLAG } from './customer-flags.mjs';
120
120
  import { EditionChannel } from './edition-constants.mjs';
121
+ import { editionPinnedInputs } from './edition-pinned-inputs.mjs';
121
122
  import { WorkspaceRoot, biomeIndexPath, configureEditionWorkspaceFromProcess, editionsRoot, isEntryModule, runtimeImageSourceIndexPath } from './edition-workspace.mjs';
122
123
  import {
123
124
  assertCoordinatesFinal,
@@ -508,11 +509,14 @@ export async function sealEditionLocks({ distributionId, root, digests = new Map
508
509
  }
509
510
  const raw = JSON.parse(readFileSync(lockPath, 'utf8'));
510
511
  const imageLock = schemas.parseImageLock(rawImages);
511
- const pinned = vendoredCommit ?? ((repo) => createPinnedSourceAuthority({ pins: committedRepositoryPins() }).vendoredSourceCommitForRepository(repo));
512
+ // A release seal reads the EDITION's indexes and pins (a held repository is sealed at the held commit); a
513
+ // customer seal (catalogue) and a develop seal (injected `vendoredCommit`) are not vendor release editions.
514
+ const editionInputs = catalogue === undefined && vendoredCommit === undefined ? await editionPinnedInputs(distributionId, { schemas }) : undefined;
515
+ const pinned = vendoredCommit ?? ((repo) => createPinnedSourceAuthority({ pins: editionInputs?.repositoryPins ?? committedRepositoryPins() }).vendoredSourceCommitForRepository(repo));
512
516
  const lockDocument = raw !== null && typeof raw === 'object' && 'unsealedSchema' in raw ? schemas.parseUnsealedDistributionLock(raw) : schemas.parseDistributionLock(raw);
513
517
  if (lockMountsAdapters(lockDocument)) {
514
518
  // The adapter host's row: the verified catalogue's for a customer, the data root's biome index for the vendor.
515
- const rows = catalogue === undefined ? JSON.parse(readFileSync(biomeIndexPath(), 'utf8')) : catalogue.biomes;
519
+ const rows = catalogue !== undefined ? catalogue.biomes : (editionInputs?.biomeIndex ?? JSON.parse(readFileSync(biomeIndexPath(), 'utf8')));
516
520
  await assertAdapterBundlesMatchHost({ lock: lockDocument, hostRow: adapterHostRow(rows), fetchManifest });
517
521
  }
518
522
  const result = await sealOrVerifyLock({
@@ -762,9 +766,12 @@ async function main() {
762
766
  ),
763
767
  imageLock,
764
768
  distributionId,
765
- runtimeSourceIndex: JSON.parse(
766
- readFileSync(runtimeImageSourceIndexPath(), 'utf8'),
767
- ),
769
+ // A vendor edition grades the pins it RESOLVES at (a held repository is graded at the held commit);
770
+ // a customer seal grades the data root's committed index.
771
+ runtimeSourceIndex:
772
+ catalogue === undefined
773
+ ? (await editionPinnedInputs(distributionId, { schemas: await loadKernelDistributionSchemas() })).runtimeImageSourceIndex
774
+ : JSON.parse(readFileSync(runtimeImageSourceIndexPath(), 'utf8')),
768
775
  });
769
776
 
770
777
  const { writes, unchanged } = planDigestSeal({
@@ -184,7 +184,7 @@ function ledgerCommits(repo, ledger) {
184
184
  * positive — each is a record that a build actually happened — while either
185
185
  * alone has been measured missing whole repositories.
186
186
  */
187
- function builtCommits(directory, ref, repo, ledger) {
187
+ export function builtCommits(directory, ref, repo, ledger) {
188
188
  const fromTree = evidenceTreeCommits(directory, ref);
189
189
  const fromLedger = ledgerCommits(repo, ledger);
190
190
  // Null from BOTH means neither record has ever heard of this repository, so
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xemahq/xema",
3
- "version": "0.32.0",
3
+ "version": "0.33.1",
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:94a9ed139c70716ccb6c498689b91e849e0f1576901722f0bef67403ba7fe593"
95
+ "sourceDigest": "sha256:a6f12761c7c95f7810ac5134c53e5e9712d2822e7f3d0a91f96eed09741f9605"
96
96
  }
97
97
  }