@xemahq/xema 0.31.2 → 0.33.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.
Files changed (86) hide show
  1. package/dist/commands/biome.js +1 -1
  2. package/dist/commands/biome.js.map +1 -1
  3. package/dist/commands/edition.d.ts.map +1 -1
  4. package/dist/commands/edition.js +3 -1
  5. package/dist/commands/edition.js.map +1 -1
  6. package/dist/commands/installation.js +3 -3
  7. package/dist/commands/installation.js.map +1 -1
  8. package/dist/commands/objects.js +1 -1
  9. package/dist/commands/objects.js.map +1 -1
  10. package/dist/dev/forked-worker.d.ts.map +1 -1
  11. package/dist/dev/forked-worker.js +9 -6
  12. package/dist/dev/forked-worker.js.map +1 -1
  13. package/dist/edition/verify-image-evidence.bundle.mjs +243 -160
  14. package/dist/edition/verify-image-evidence.bundle.provenance.json +50 -38
  15. package/dist/lib/control-plane.js +2 -2
  16. package/dist/lib/control-plane.js.map +1 -1
  17. package/dist/lib/plan-format.js.map +1 -1
  18. package/dist/lib/promise-from-sync.d.ts +2 -0
  19. package/dist/lib/promise-from-sync.d.ts.map +1 -0
  20. package/dist/lib/promise-from-sync.js +9 -0
  21. package/dist/lib/promise-from-sync.js.map +1 -0
  22. package/dist/runtime/biome-local-install.js +1 -1
  23. package/dist/runtime/biome-local-install.js.map +1 -1
  24. package/dist/runtime/boot-graph-loader.js.map +1 -1
  25. package/dist/runtime/config/sources-config.js.map +1 -1
  26. package/dist/runtime/edition/verb-map.d.ts +3 -1
  27. package/dist/runtime/edition/verb-map.d.ts.map +1 -1
  28. package/dist/runtime/edition/verb-map.js +4 -0
  29. package/dist/runtime/edition/verb-map.js.map +1 -1
  30. package/dist/runtime/infra/infra-catalog.d.ts +3 -1
  31. package/dist/runtime/infra/infra-catalog.d.ts.map +1 -1
  32. package/dist/runtime/infra/infra-catalog.js.map +1 -1
  33. package/dist/runtime/installation/lifecycle.d.ts.map +1 -1
  34. package/dist/runtime/installation/lifecycle.js +7 -6
  35. package/dist/runtime/installation/lifecycle.js.map +1 -1
  36. package/dist/runtime/installation/state-store.d.ts.map +1 -1
  37. package/dist/runtime/installation/state-store.js +2 -1
  38. package/dist/runtime/installation/state-store.js.map +1 -1
  39. package/dist/runtime/read-only-no-follow-flags.d.ts +2 -0
  40. package/dist/runtime/read-only-no-follow-flags.d.ts.map +1 -0
  41. package/dist/runtime/read-only-no-follow-flags.js +6 -0
  42. package/dist/runtime/read-only-no-follow-flags.js.map +1 -0
  43. package/dist/runtime/roster.d.ts.map +1 -1
  44. package/dist/runtime/roster.js +3 -5
  45. package/dist/runtime/roster.js.map +1 -1
  46. package/dist/runtime/run-manifest.d.ts.map +1 -1
  47. package/dist/runtime/run-manifest.js +1 -3
  48. package/dist/runtime/run-manifest.js.map +1 -1
  49. package/dist/runtime/secrets/managed-local-files.d.ts.map +1 -1
  50. package/dist/runtime/secrets/managed-local-files.js +2 -1
  51. package/dist/runtime/secrets/managed-local-files.js.map +1 -1
  52. package/dist/runtime/secrets/managed-local-key-provider.d.ts.map +1 -1
  53. package/dist/runtime/secrets/managed-local-key-provider.js +13 -10
  54. package/dist/runtime/secrets/managed-local-key-provider.js.map +1 -1
  55. package/dist/runtime/secrets/resolver.d.ts.map +1 -1
  56. package/dist/runtime/secrets/resolver.js +2 -1
  57. package/dist/runtime/secrets/resolver.js.map +1 -1
  58. package/dist/runtime/sources/credentials.d.ts.map +1 -1
  59. package/dist/runtime/sources/credentials.js +6 -9
  60. package/dist/runtime/sources/credentials.js.map +1 -1
  61. package/dist/runtime/store-client.d.ts.map +1 -1
  62. package/dist/runtime/store-client.js.map +1 -1
  63. package/dist/runtime/supervisor.js +2 -2
  64. package/dist/runtime/supervisor.js.map +1 -1
  65. package/edition/README.md +23 -0
  66. package/edition/build-ledger-completeness.mjs +67 -0
  67. package/edition/build-ledger.mjs +107 -0
  68. package/edition/checkout-indexed-source-repositories.mjs +57 -2
  69. package/edition/customer-definition.mjs +8 -0
  70. package/edition/develop-snapshot-inputs.mjs +4 -15
  71. package/edition/dist-check.mjs +11 -7
  72. package/edition/edition-biome-manifests.mjs +3 -2
  73. package/edition/edition-pin-verbs.mjs +183 -0
  74. package/edition/edition-pin.mjs +17 -0
  75. package/edition/edition-pinned-inputs.mjs +116 -0
  76. package/edition/edition-source-pins.mjs +209 -0
  77. package/edition/edition-unpin.mjs +16 -0
  78. package/edition/image-evidence.mjs +13 -7
  79. package/edition/indexes-at-pins.mjs +29 -0
  80. package/edition/release-catalogue.mjs +9 -8
  81. package/edition/release-edition-artifact.mjs +3 -2
  82. package/edition/resolve-distribution.mjs +16 -25
  83. package/edition/resolve-images.mjs +25 -11
  84. package/edition/seal-image-digests.mjs +12 -5
  85. package/edition/source-authority.mjs +8 -21
  86. package/package.json +3 -3
@@ -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]);
@@ -1473,6 +1455,9 @@ export function resolveDistributionFromInputs(
1473
1455
  // lose its UI. A web joins once EVERY server it requires is in the edition
1474
1456
  // (the last of them to be processed completes it), so a UI is never pulled
1475
1457
  // in ahead of the capability it administers.
1458
+ // An operator-audience web is skipped here on purpose: operator biomes enter an edition only by explicit
1459
+ // selection (selectors skip them in applySelectors; naming one forces it in; a required service owned by
1460
+ // one must be selected explicitly). Pulling one in implicitly from a server biome would bypass that rule.
1476
1461
  const webBiomesByRequiredServer = new Map();
1477
1462
  for (const candidate of resourceProviderCandidates) {
1478
1463
  if (
@@ -2226,8 +2211,14 @@ export async function resolveDistributionWithIndex(id, { materialize, inputs, re
2226
2211
  // THE RELEASE READS THESE FROM DISK; A DEVELOP SNAPSHOT INJECTS THEM
2227
2212
  // (`develop-snapshot-inputs.mjs`). Either way they pass through the one
2228
2213
  // validation below, so there is a single resolve path and two sources.
2229
- const claimedPlatformServiceIndex =
2230
- 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;
2231
2222
  // OWNERSHIP IS THE SERVICE'S OWN STATEMENT: each entry's capability domains
2232
2223
  // are re-derived from its pinned source manifest (hash-verified), so a
2233
2224
  // hand-edited index cannot move where grants land. Only the customer resolve
@@ -2239,7 +2230,7 @@ export async function resolveDistributionWithIndex(id, { materialize, inputs, re
2239
2230
  ? claimedPlatformServiceIndex
2240
2231
  : verifiedPlatformServiceIndex(claimedPlatformServiceIndex, { readManifest });
2241
2232
  const platformIndex = validateServiceDependencyOwnerParity(
2242
- validateImmutableBiomeIndex(inputs?.biomeIndex ?? readBiomeIndex()),
2233
+ validateImmutableBiomeIndex(inputs?.biomeIndex ?? pinned.biomeIndex),
2243
2234
  platformServiceIndex,
2244
2235
  );
2245
2236
  const { entries, sourceRecords } = await walkDeclaredSources({
@@ -36,6 +36,8 @@ import { execFileSync } from 'node:child_process';
36
36
  import { writeFileSync } from 'node:fs';
37
37
  import { join, relative, sep } from 'node:path';
38
38
 
39
+ import { assertBuildLedgerComplete } from './build-ledger-completeness.mjs';
40
+ import { BUILD_LEDGER_FILE, readBuildLedger } from './build-ledger.mjs';
39
41
  import { resolveDispatchedRuntimeComponents } from './dispatched-runtime-components.mjs';
40
42
  import {
41
43
  assertCanonicalDistributionId,
@@ -43,7 +45,8 @@ import {
43
45
  readBoundDistributionFile,
44
46
  } from './distribution-paths.mjs';
45
47
  import { editionWorkspaceConfig } from './edition-config.mjs';
46
- import { biomeIndexPath, 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';
47
50
  import {
48
51
  externalSourceCacheDir,
49
52
  materializeGitSource,
@@ -66,7 +69,6 @@ import {
66
69
  } from './lib.mjs';
67
70
  import { assertReleaseContent } from './release-channel-guard.mjs';
68
71
  import { composeEdition } from './resolve-distribution.mjs';
69
- import { readStrictJsonFile } from './strict-json.mjs';
70
72
 
71
73
  if (isEntryModule(import.meta.url)) {
72
74
  configureEditionWorkspaceFromProcess([WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.Kernel, WorkspaceRoot.Cache]);
@@ -162,10 +164,6 @@ const FIRST_PARTY_TREES = (repo) => ({ dir: sourcePath(repo), base: sourcesRoot(
162
164
  const imageBuildInputHashByIdentity = new Map();
163
165
  const sourceManifestHashByIdentity = new Map();
164
166
 
165
- function readJson(path) {
166
- return readStrictJsonFile(path);
167
- }
168
-
169
167
  /**
170
168
  * Memoized image build-input closure hash.
171
169
  *
@@ -1487,8 +1485,13 @@ export function resolveCrossRepoInputsForRoster({
1487
1485
  return crossRepoInputsByConsumer;
1488
1486
  }
1489
1487
 
1490
- /** The release's inputs: the COMMITTED locks and indexes, read from disk. */
1491
- 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 });
1492
1495
  return {
1493
1496
  lock: validateImmutableDistributionLock(
1494
1497
  readBoundDistributionFile({
@@ -1499,8 +1502,8 @@ function releaseImageInputs(schemas, id) {
1499
1502
  parse: (raw) => parseResolvedDistributionLock(schemas, raw),
1500
1503
  }).value,
1501
1504
  ),
1502
- runtimeSourceIndex: schemas.parseRuntimeImageSourceIndex(readJson(runtimeImageSourceIndexPath())),
1503
- biomeIndex: readJson(biomeIndexPath()),
1505
+ runtimeSourceIndex: pinned.runtimeImageSourceIndex,
1506
+ biomeIndex: pinned.biomeIndex,
1504
1507
  requiredPlatforms: resolveDistributionRequiredPlatforms(schemas, id),
1505
1508
  declaredSources: resolveDistributionDeclaredSources(schemas, id),
1506
1509
  };
@@ -1542,7 +1545,7 @@ export async function resolveImageRows(id, { inputs, lock } = {}) {
1542
1545
  biomeIndex,
1543
1546
  requiredPlatforms,
1544
1547
  declaredSources,
1545
- } = inputs === undefined ? releaseImageInputs(schemas, id) : snapshotImageInputs(schemas, { inputs, lock });
1548
+ } = inputs === undefined ? await releaseImageInputs(schemas, id) : snapshotImageInputs(schemas, { inputs, lock });
1546
1549
  // Materialise every EXTERNAL source at the commit the lock pins, and resolve
1547
1550
  // every `repo` handle through one locator from here down.
1548
1551
  const locate = sourceTreeLocator(externalSourceTrees(distributionLock));
@@ -1652,6 +1655,17 @@ export async function resolveImageRows(id, { inputs, lock } = {}) {
1652
1655
  ),
1653
1656
  };
1654
1657
  });
1658
+ // Release lane only: a develop snapshot or customer overlay is not covered by
1659
+ // the committed ledger.
1660
+ if (inputs === undefined) {
1661
+ assertBuildLedgerComplete({
1662
+ ledger: readBuildLedger(),
1663
+ distributionId: id,
1664
+ subjects,
1665
+ requiredPlatforms,
1666
+ expectedAt: join(dataRoot(), BUILD_LEDGER_FILE),
1667
+ });
1668
+ }
1655
1669
  const webBiomes = structure.webBiomes.map((biome) => ({
1656
1670
  ...biome,
1657
1671
  sourceCommit: commitsBefore.get(biome.repo),
@@ -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({
@@ -9,6 +9,7 @@ import {
9
9
  import { tmpdir } from 'node:os';
10
10
  import { join, relative, resolve, sep } from 'node:path';
11
11
 
12
+ import { BUILD_LEDGER_FILE, readBuildLedger } from './build-ledger.mjs';
12
13
  import { dataRoot, sourcesRoot } from './edition-workspace.mjs';
13
14
 
14
15
  export const PUBLISHED_SOURCE_REF = 'refs/remotes/origin/main';
@@ -91,7 +92,7 @@ const BUILT_COMMIT_SEARCH_LIMIT = 500;
91
92
  * A pin frozen eight days in the past is what kept a fix for a production
92
93
  * crashloop out of every image built from it.
93
94
  */
94
- export const BUILD_LEDGER_FILE = 'build-ledger.json';
95
+ export { BUILD_LEDGER_FILE };
95
96
 
96
97
  /**
97
98
  * Read the committed ledger. A caller that resolves many repositories should
@@ -106,25 +107,11 @@ export const BUILD_LEDGER_FILE = 'build-ledger.json';
106
107
  export function loadBuildLedger(
107
108
  distributionsDir = dataRoot(),
108
109
  ) {
109
- const path = join(distributionsDir, BUILD_LEDGER_FILE);
110
- let raw;
111
- try {
112
- raw = JSON.parse(readFileSync(path, 'utf8'));
113
- } catch (error) {
114
- if (error?.code === 'ENOENT') {return null;}
115
- throw error;
116
- }
117
- if (
118
- raw === null ||
119
- typeof raw !== 'object' ||
120
- raw.schemaVersion !== 1 ||
121
- typeof raw.repositories !== 'object' ||
122
- raw.repositories === null ||
123
- Array.isArray(raw.repositories)
124
- ) {
125
- throw new Error(`${BUILD_LEDGER_FILE} is not strict schema v1`);
126
- }
127
- return raw.repositories;
110
+ const ledger = readBuildLedger(distributionsDir);
111
+ if (ledger === null) {return null;}
112
+ return Object.fromEntries(
113
+ Object.entries(ledger.repositories).map(([repo, entry]) => [repo, entry.commits]),
114
+ );
128
115
  }
129
116
 
130
117
  /**
@@ -197,7 +184,7 @@ function ledgerCommits(repo, ledger) {
197
184
  * positive — each is a record that a build actually happened — while either
198
185
  * alone has been measured missing whole repositories.
199
186
  */
200
- function builtCommits(directory, ref, repo, ledger) {
187
+ export function builtCommits(directory, ref, repo, ledger) {
201
188
  const fromTree = evidenceTreeCommits(directory, ref);
202
189
  const fromLedger = ledgerCommits(repo, ledger);
203
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.31.2",
3
+ "version": "0.33.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/xema-dev/xema-cli.git",
@@ -85,13 +85,13 @@
85
85
  "verify:pack": "node ./scripts/verify-packed-assets.cjs",
86
86
  "format": "prettier --write \"src/**/*.ts\"",
87
87
  "start": "node ./dist/cli.js",
88
- "lint": "eslint .",
88
+ "lint": "eslint . --max-warnings 0",
89
89
  "lint:fix": "eslint . --fix",
90
90
  "test": "jest --config jest.config.cjs && pnpm run test:edition",
91
91
  "test:edition": "node --test \"edition/*.test.mjs\" \"edition/*-contract-tests.mjs\"",
92
92
  "typecheck": "tsc --noEmit"
93
93
  },
94
94
  "xemaRelease": {
95
- "sourceDigest": "sha256:98d3df3b954a3e3149f8099e228a7649cbba3f0ed37fbfc59c2c1e3cc3a2acef"
95
+ "sourceDigest": "sha256:ec5750fec69809f0c18307f59b655b145349cffd670741ac33dbb1aac0029d62"
96
96
  }
97
97
  }