@xemahq/xema 0.33.1 → 0.33.3

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.
@@ -0,0 +1,54 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { createRequire } from 'node:module';
3
+
4
+ import { parseStrictJson } from './strict-json.mjs';
5
+
6
+ const requireFromHere = createRequire(import.meta.url);
7
+
8
+ let loadedSourceHash;
9
+
10
+ /** Git-object primitives of `@xemahq/distribution-source-hash`; no kernel is consulted. */
11
+ function gitObjectReader() {
12
+ loadedSourceHash ??= requireFromHere('@xemahq/distribution-source-hash');
13
+ return loadedSourceHash;
14
+ }
15
+
16
+ /**
17
+ * Read + parse a JSON file out of an exact Git commit (never the work tree).
18
+ *
19
+ * This is the seam every authored source file crosses on its way into a lock
20
+ * or an index — `xema-biome.json`, a platform service's `package.json`,
21
+ * `cross-repo-build-inputs.json`, the committed generated biome index — so it
22
+ * parses STRICTLY: a duplicated object key is refused, naming the commit, the
23
+ * path and the JSON pointer of the object. `JSON.parse` would keep the last
24
+ * occurrence, which is how a `resources` declaration was dropped from a sealed
25
+ * lock with every reader agreeing (see `strict-json.mjs`).
26
+ *
27
+ * The commit check, the buffer ceiling and the exact-UTF-8 decode are the
28
+ * source-hash package's own (loaded WITHOUT the kernel check hashing needs, so
29
+ * the source checkout, which runs before any kernel is built, can read a record
30
+ * too); only the parse differs from its
31
+ * `readGitJsonAtCommit`, which is lenient and is therefore not delegated to.
32
+ */
33
+ export function readGitJsonAtCommit(repoDir, repoRelativePath, commit) {
34
+ const sourceHash = gitObjectReader();
35
+ sourceHash.assertGitCommitExists(repoDir, commit);
36
+ let raw;
37
+ try {
38
+ raw = execFileSync(
39
+ 'git',
40
+ ['-C', repoDir, 'show', `${commit}:${repoRelativePath}`],
41
+ {
42
+ encoding: 'buffer',
43
+ maxBuffer: sourceHash.GIT_OBJECT_MAX_BUFFER_BYTES,
44
+ stdio: ['ignore', 'pipe', 'pipe'],
45
+ },
46
+ );
47
+ } catch {
48
+ throw new Error(
49
+ `cannot read "${repoRelativePath}" from Git commit "${commit}"`,
50
+ );
51
+ }
52
+ const context = `Git object "${commit}:${repoRelativePath}"`;
53
+ return parseStrictJson(sourceHash.decodeExactUtf8(raw, context), context);
54
+ }
package/edition/lib.mjs CHANGED
@@ -30,6 +30,7 @@ import {
30
30
  sourcePath,
31
31
  } from './edition-workspace.mjs';
32
32
  import { SHA256_HEX_PATTERN } from './evidence-grammars.mjs';
33
+ import { readGitJsonAtCommit } from './git-json.mjs';
33
34
  import { isKernelCapabilityDomain } from './kernel-capability-domain.mjs';
34
35
  import {
35
36
  assertPrivilegesHavePrincipals,
@@ -42,7 +43,7 @@ import {
42
43
  PUBLISHED_SOURCE_REF,
43
44
  publishedSourceCommit,
44
45
  } from './source-authority.mjs';
45
- import { parseStrictJson, readStrictJsonFile } from './strict-json.mjs';
46
+ import { readStrictJsonFile } from './strict-json.mjs';
46
47
  import { compareStrings } from './string-order.mjs';
47
48
 
48
49
  export {
@@ -51,6 +52,7 @@ export {
51
52
  DistributionSourceMode,
52
53
  PUBLISHED_SOURCE_REF,
53
54
  publishedSourceCommit,
55
+ readGitJsonAtCommit,
54
56
  };
55
57
 
56
58
 
@@ -3063,167 +3065,3 @@ export function hashImageBuildInputs(request, algorithm) {
3063
3065
  return distributionSourceHash().hashImageBuildInputs(request, algorithm);
3064
3066
  }
3065
3067
 
3066
- /**
3067
- * Read + parse a JSON file out of an exact Git commit (never the work tree).
3068
- *
3069
- * This is the seam every authored source file crosses on its way into a lock
3070
- * or an index — `xema-biome.json`, a platform service's `package.json`,
3071
- * `cross-repo-build-inputs.json`, the committed generated biome index — so it
3072
- * parses STRICTLY: a duplicated object key is refused, naming the commit, the
3073
- * path and the JSON pointer of the object. `JSON.parse` would keep the last
3074
- * occurrence, which is how a `resources` declaration was dropped from a sealed
3075
- * lock with every reader agreeing (see `strict-json.mjs`).
3076
- *
3077
- * The commit check, the buffer ceiling and the exact-UTF-8 decode are the
3078
- * source-hash package's own; only the parse differs from its
3079
- * `readGitJsonAtCommit`, which is lenient and is therefore not delegated to.
3080
- */
3081
- export function readGitJsonAtCommit(repoDir, repoRelativePath, commit) {
3082
- const sourceHash = distributionSourceHash();
3083
- sourceHash.assertGitCommitExists(repoDir, commit);
3084
- let raw;
3085
- try {
3086
- raw = execFileSync(
3087
- 'git',
3088
- ['-C', repoDir, 'show', `${commit}:${repoRelativePath}`],
3089
- {
3090
- encoding: 'buffer',
3091
- maxBuffer: sourceHash.GIT_OBJECT_MAX_BUFFER_BYTES,
3092
- stdio: ['ignore', 'pipe', 'pipe'],
3093
- },
3094
- );
3095
- } catch {
3096
- throw new Error(
3097
- `cannot read "${repoRelativePath}" from Git commit "${commit}"`,
3098
- );
3099
- }
3100
- const context = `Git object "${commit}:${repoRelativePath}"`;
3101
- return parseStrictJson(sourceHash.decodeExactUtf8(raw, context), context);
3102
- }
3103
-
3104
- /**
3105
- * The producing repository's OWN committed record of what it vendored.
3106
- *
3107
- * `vendor-cross-repo-inputs.mjs` reads exactly this file, at the commit being
3108
- * built, to emit the `--vendored <dest>=<repoDir>@<commit>:<src>` arguments it
3109
- * hands `compute-image-tag.mjs`. It is therefore the ONLY source of the vendored
3110
- * commit that can agree with the tag CI pushes.
3111
- */
3112
- export const CROSS_REPO_BUILD_INPUTS_PATH =
3113
- 'tooling/release/cross-repo-build-inputs.json';
3114
-
3115
- /** Identity of one vendored input — the tuple that survives a pin bump. */
3116
- export function crossRepoInputKey(input) {
3117
- return [input.sourceRepo, input.sourcePath, input.destinationPath].join('\u0000');
3118
- }
3119
-
3120
- /**
3121
- * Resolve the vendored inputs of ONE consumer repository at ONE commit.
3122
- *
3123
- * WHY THE COMMIT COMES FROM THE CONSUMER, NOT FROM THE INDEX
3124
- *
3125
- * The index's `crossRepoBuildInputs` carry no `sourceCommit`; they name only
3126
- * WHICH content is vendored. Both this tooling and the parity gate used to
3127
- * supply the missing commit from `runtimeSourceIndex.repositoryPins`, and CI
3128
- * supplies it from the file above — so the two disagreed the moment a consumer
3129
- * repository's mirror lagged the index by one pin, which is its NORMAL resting
3130
- * state: the mirror is generated FROM the index and committed separately, so it
3131
- * is stale by construction between the index moving and the consumer
3132
- * regenerating.
3133
- *
3134
- * That is not a cosmetic disagreement. `crossRepo.sourceCommit` is framed into
3135
- * the component KEY of the image-input hash, so a pin that differs by one
3136
- * commit yields a different `contentHash` FOR BYTE-IDENTICAL VENDORED CONTENT —
3137
- * a coordinate no CI run has ever pushed or ever will. Measured 2026-09-07 on
3138
- * `origin/main`: five of the sixty-eight coordinates in the eight editions
3139
- * (`backlog-api`, `channel-gateway-api`, `control-plane-api`,
3140
- * `document-render-api` from repos/xema-community, and `biome-host-api` from
3141
- * repos/xema-base) 404'd in the registry for exactly this reason, while the tags
3142
- * their producing CI DID push were present and served on both platforms.
3143
- *
3144
- * Reading the index pin also imposed a requirement that cannot be satisfied.
3145
- * `repos/xema-base` vendors from `repos/xema-community` and vice versa, so
3146
- * "every mirror agrees with the index" is a mutual fixed point over a 2-cycle,
3147
- * and bumping either side invalidates the other. No such pair exists: scanning
3148
- * eighty commits of each on `origin/main` found ZERO mutually coherent pairs.
3149
- * Reading the consumer's own record removes the fixed point entirely — an
3150
- * image's identity depends only on the repository that builds it.
3151
- *
3152
- * It stays edition-INDEPENDENT, which is the property the previous design was
3153
- * protecting: the record is a property of the consumer's COMMIT, not of any
3154
- * edition's resolved commit map, so every edition shipping a subject still
3155
- * derives the same `contentHash` for it.
3156
- */
3157
- export function resolveCrossRepoVendoredInputs({
3158
- indexInputs,
3159
- consumerRepo,
3160
- consumerRepoDir,
3161
- consumerCommit,
3162
- }) {
3163
- const declared = indexInputs.filter(
3164
- (input) => input.consumerRepo === consumerRepo,
3165
- );
3166
- if (declared.length === 0) {return [];}
3167
- if (!consumerCommit) {
3168
- throw new Error(
3169
- `cross-repo build inputs are declared for "${consumerRepo}", but this ` +
3170
- `distribution resolves no commit for it — the vendored content cannot ` +
3171
- `be pinned, so the image cannot be addressed`,
3172
- );
3173
- }
3174
- let record;
3175
- try {
3176
- record = readGitJsonAtCommit(
3177
- consumerRepoDir,
3178
- CROSS_REPO_BUILD_INPUTS_PATH,
3179
- consumerCommit,
3180
- );
3181
- } catch (cause) {
3182
- throw new Error(
3183
- `"${consumerRepo}" declares cross-repo build inputs but ` +
3184
- `${CROSS_REPO_BUILD_INPUTS_PATH} is unreadable at ${consumerCommit}. ` +
3185
- `That file is what its CI feeds to compute-image-tag.mjs; without it ` +
3186
- `no coordinate this tooling derives can match the tag CI pushes.`,
3187
- { cause },
3188
- );
3189
- }
3190
- const mirrored = new Map();
3191
- for (const input of record?.inputs ?? []) {
3192
- mirrored.set(crossRepoInputKey(input), input.sourceCommit);
3193
- }
3194
- // Set equality in BOTH directions. A commit that LAGS is normal and is now
3195
- // honoured; a differing input SET is a genuine declaration disagreement, and
3196
- // it moves the hash just as surely — so it must fail rather than resolve to a
3197
- // coordinate built from a different closure.
3198
- const declaredKeys = new Set(declared.map((input) => crossRepoInputKey(input)));
3199
- const resolved = declared.map((input) => {
3200
- const sourceCommit = mirrored.get(crossRepoInputKey(input));
3201
- if (!sourceCommit) {
3202
- throw new Error(
3203
- `the runtime image source index declares a cross-repo build input for ` +
3204
- `"${consumerRepo}" vendoring "${input.sourceRepo}:${input.sourcePath}" ` +
3205
- `to "${input.destinationPath}", but ${CROSS_REPO_BUILD_INPUTS_PATH} at ` +
3206
- `${consumerCommit} declares no such input — its CI does not vendor it, ` +
3207
- `so the coordinate this tooling would derive is one nothing builds`,
3208
- );
3209
- }
3210
- return {
3211
- consumerService: input.consumerService ?? null,
3212
- destinationPath: input.destinationPath,
3213
- sourceRepoDir: sourcePath(input.sourceRepo),
3214
- sourcePath: input.sourcePath,
3215
- sourceCommit,
3216
- };
3217
- });
3218
- for (const key of mirrored.keys()) {
3219
- if (declaredKeys.has(key)) {continue;}
3220
- const [sourceRepo, sourcePath, destinationPath] = key.split('\0');
3221
- throw new Error(
3222
- `${CROSS_REPO_BUILD_INPUTS_PATH} at ${consumerCommit} vendors ` +
3223
- `"${sourceRepo}:${sourcePath}" to "${destinationPath}" for ` +
3224
- `"${consumerRepo}", which the runtime image source index does not ` +
3225
- `declare — its CI bakes content this tooling does not hash`,
3226
- );
3227
- }
3228
- return resolved;
3229
- }
@@ -222,10 +222,16 @@ function applySnapshot(runtimeSourceIndex, pins) {
222
222
  ...runtimeSourceIndex,
223
223
  repositoryPins: graph.map((pin) => ({
224
224
  ...pin,
225
- // The IMAGE axis only. `vendoredSourceCommit` is the SOURCE axis (the
226
- // published tip vendored bytes come from) and is deliberately NOT fused
227
- // with it: see `build-runtime-image-source-index.mjs#repositoryIdentity`
228
- // for the measured production failure that fusing the two caused.
225
+ // A develop snapshot pins every repository at its develop head, so that one
226
+ // SHA is both the built commit (image axis, `sourceCommit`) and the source
227
+ // tip (`vendoredSourceCommit`): the image pin lagging the source tip cannot
228
+ // arise, and the snapshot is "one frozen snapshot of pins and nothing else".
229
+ // Fusing both axes is the rule `effectiveRepositoryPins` and
230
+ // `createExactCommitSourceAuthority` already apply for an exact commit.
231
+ // The COMMITTED index keeps the axes apart (release channel): see
232
+ // `source-authority.mjs#publishedSourceTip` and
233
+ // `build-runtime-image-source-index.mjs#repositoryIdentity`.
234
+ vendoredSourceCommit: pins[pin.repo],
229
235
  sourceCommit: pins[pin.repo],
230
236
  })),
231
237
  };
@@ -38,6 +38,7 @@ import { join, relative, sep } from 'node:path';
38
38
 
39
39
  import { assertBuildLedgerComplete } from './build-ledger-completeness.mjs';
40
40
  import { BUILD_LEDGER_FILE, readBuildLedger } from './build-ledger.mjs';
41
+ import { CROSS_REPO_BUILD_INPUTS_PATH, crossRepoInputKey, resolveCrossRepoVendoredInputs } from './cross-repo-build-inputs.mjs';
41
42
  import { resolveDispatchedRuntimeComponents } from './dispatched-runtime-components.mjs';
42
43
  import {
43
44
  assertCanonicalDistributionId,
@@ -53,15 +54,12 @@ import {
53
54
  } from './external-source-walk.mjs';
54
55
  import { assertDeclaredSourceId, firstPartySourceId } from './first-party-source-ids.mjs';
55
56
  import {
56
- CROSS_REPO_BUILD_INPUTS_PATH,
57
57
  compareStrings,
58
- crossRepoInputKey,
59
58
  hashImageBuildInputs,
60
59
  imageInputSourceHashAlgorithm,
61
60
  loadKernelDistributionSchemas,
62
61
  parseResolvedDistributionLock,
63
62
  readGitJsonAtCommit,
64
- resolveCrossRepoVendoredInputs,
65
63
  serializeJson,
66
64
  sha256Hex,
67
65
  validateImmutableDistributionLock,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xemahq/xema",
3
- "version": "0.33.1",
3
+ "version": "0.33.3",
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:a6f12761c7c95f7810ac5134c53e5e9712d2822e7f3d0a91f96eed09741f9605"
95
+ "sourceDigest": "sha256:fddbe95c5e82a8ecc251e4d62f65718c96630117ec28d6fb9c8f0f05174432bc"
96
96
  }
97
97
  }