@xemahq/xema 0.33.2 → 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.
@@ -59664,7 +59664,7 @@ var EDITION_LOCKS = Object.freeze([...EDITION_CONTRACT.locks]);
59664
59664
  var EDITION_FILES = Object.freeze([EDITION_DESCRIPTOR_FILE, ...EDITION_LOCKS]);
59665
59665
 
59666
59666
  // edition/lib.mjs
59667
- import { createRequire as createRequire3 } from "node:module";
59667
+ import { createRequire as createRequire4 } from "node:module";
59668
59668
  import { isIP } from "node:net";
59669
59669
  var import_yaml = __toESM(require_dist(), 1);
59670
59670
 
@@ -60350,12 +60350,16 @@ var COMMIT_PATTERN = /^[a-f0-9]{40}$/;
60350
60350
  var SHA256_HEX_PATTERN = /^[a-f0-9]{64}$/;
60351
60351
  var SHA256_DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/;
60352
60352
 
60353
- // edition/kernel-capability-domain.mjs
60353
+ // edition/git-json.mjs
60354
60354
  import { createRequire } from "node:module";
60355
60355
  var requireFromHere = createRequire(import.meta.url);
60356
60356
 
60357
- // edition/operation-requirements.mjs
60357
+ // edition/kernel-capability-domain.mjs
60358
60358
  import { createRequire as createRequire2 } from "node:module";
60359
+ var requireFromHere2 = createRequire2(import.meta.url);
60360
+
60361
+ // edition/operation-requirements.mjs
60362
+ import { createRequire as createRequire3 } from "node:module";
60359
60363
 
60360
60364
  // edition/string-order.mjs
60361
60365
  function compareStrings(a, b) {
@@ -60366,7 +60370,7 @@ function compareStrings(a, b) {
60366
60370
  }
60367
60371
 
60368
60372
  // edition/operation-requirements.mjs
60369
- var requireFromHere2 = createRequire2(import.meta.url);
60373
+ var requireFromHere3 = createRequire3(import.meta.url);
60370
60374
  var PRINCIPAL_PRIVILEGE_FIELDS = Object.freeze(["requiresOperations", "requiresOrganizationSweeps"]);
60371
60375
 
60372
60376
  // edition/source-authority.mjs
@@ -60376,7 +60380,7 @@ var DistributionSourceMode = Object.freeze({
60376
60380
  });
60377
60381
 
60378
60382
  // edition/lib.mjs
60379
- var requireFromLib = createRequire3(import.meta.url);
60383
+ var requireFromLib = createRequire4(import.meta.url);
60380
60384
  var WorkerProtocolKind = Object.freeze({ Worker: "worker", None: "none" });
60381
60385
  var GIT_LS_TREE_MAX_BUFFER = 256 * 1024 * 1024;
60382
60386
  function serializeJson(value) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "artifactSha256": "sha256:172a95b35134b37d8f25d140bb3f8f955c3965d11b2e5ad56aa534d0c1556b3f",
3
+ "artifactSha256": "sha256:09f40c552224a946fef9f54e2d2c2de6885a76b386daece39862f2e42382235b",
4
4
  "esbuild": {
5
5
  "version": "0.28.2"
6
6
  },
@@ -2203,223 +2203,229 @@
2203
2203
  },
2204
2204
  {
2205
2205
  "package": "@xemahq/xema",
2206
- "version": "0.33.2",
2206
+ "version": "0.33.3",
2207
2207
  "path": "edition/adapter-host-versions.mjs",
2208
2208
  "sha256": "sha256:1b7ebd3592f4da1225fba74aaba912e7d85fbceeb1626f7dcdac83491cbb4d69"
2209
2209
  },
2210
2210
  {
2211
2211
  "package": "@xemahq/xema",
2212
- "version": "0.33.2",
2212
+ "version": "0.33.3",
2213
2213
  "path": "edition/authorization-base.mjs",
2214
2214
  "sha256": "sha256:bbeac7ff01376cb89ed2140526d4795d8e559b3f01612b404a701f19aaa24c6a"
2215
2215
  },
2216
2216
  {
2217
2217
  "package": "@xemahq/xema",
2218
- "version": "0.33.2",
2218
+ "version": "0.33.3",
2219
2219
  "path": "edition/build-ledger.mjs",
2220
2220
  "sha256": "sha256:05202cbdd606aee086c8e9c62d28be68c429653a8b1d73a289ed0fe4ff14da85"
2221
2221
  },
2222
2222
  {
2223
2223
  "package": "@xemahq/xema",
2224
- "version": "0.33.2",
2224
+ "version": "0.33.3",
2225
2225
  "path": "edition/connector-source-authority.mjs",
2226
2226
  "sha256": "sha256:ea94475f4324737673accd7de1303c44db1cbc4dac61a865a06cd83abe310164"
2227
2227
  },
2228
2228
  {
2229
2229
  "package": "@xemahq/xema",
2230
- "version": "0.33.2",
2230
+ "version": "0.33.3",
2231
2231
  "path": "edition/distribution-paths.mjs",
2232
2232
  "sha256": "sha256:d78a5c8405bc6c25d36fcec61b0030e1fdacac7d528e698adb96f906f4b70279"
2233
2233
  },
2234
2234
  {
2235
2235
  "package": "@xemahq/xema",
2236
- "version": "0.33.2",
2236
+ "version": "0.33.3",
2237
2237
  "path": "edition/edition-artifact.mjs",
2238
2238
  "sha256": "sha256:161f522eb39e3db705bc709f8512988dd3e6752851d972521354eeccffd8075d"
2239
2239
  },
2240
2240
  {
2241
2241
  "package": "@xemahq/xema",
2242
- "version": "0.33.2",
2242
+ "version": "0.33.3",
2243
2243
  "path": "edition/edition-channel.mjs",
2244
2244
  "sha256": "sha256:3c0c1564dc50be133cad500452d953353aa5beb703142c05a17bbce8175ba9f3"
2245
2245
  },
2246
2246
  {
2247
2247
  "package": "@xemahq/xema",
2248
- "version": "0.33.2",
2248
+ "version": "0.33.3",
2249
2249
  "path": "edition/edition-chart-set.mjs",
2250
2250
  "sha256": "sha256:fbda500315d45e35eb974f4701bbf36729c3b642a5d735bc1b07085c91d25b23"
2251
2251
  },
2252
2252
  {
2253
2253
  "package": "@xemahq/xema",
2254
- "version": "0.33.2",
2254
+ "version": "0.33.3",
2255
2255
  "path": "edition/edition-config.mjs",
2256
2256
  "sha256": "sha256:e3997bd9a0082e02d01760868b2a4f41ed3081e904126b30fdf86e7938dc6137"
2257
2257
  },
2258
2258
  {
2259
2259
  "package": "@xemahq/xema",
2260
- "version": "0.33.2",
2260
+ "version": "0.33.3",
2261
2261
  "path": "edition/edition-constants.mjs",
2262
2262
  "sha256": "sha256:71ae51028847ab3430ffec9b229b0516a7705b75a6de1fcfa665f5e1abf8e9bb"
2263
2263
  },
2264
2264
  {
2265
2265
  "package": "@xemahq/xema",
2266
- "version": "0.33.2",
2266
+ "version": "0.33.3",
2267
2267
  "path": "edition/edition-contract-parse.mjs",
2268
2268
  "sha256": "sha256:c575e96b70bda82899931eb0b18567988e2e13e664fa498bcbef96b571dcabb4"
2269
2269
  },
2270
2270
  {
2271
2271
  "package": "@xemahq/xema",
2272
- "version": "0.33.2",
2272
+ "version": "0.33.3",
2273
2273
  "path": "edition/edition-contract.json",
2274
2274
  "sha256": "sha256:aa1a05f1728813a7bdb1e02d9305e9adf641d695e6767305210c67a1d81b38a7"
2275
2275
  },
2276
2276
  {
2277
2277
  "package": "@xemahq/xema",
2278
- "version": "0.33.2",
2278
+ "version": "0.33.3",
2279
2279
  "path": "edition/edition-protocol.mjs",
2280
2280
  "sha256": "sha256:2940a0ee8662970722aa2618a1635f8d5b4f5e54b85f09667f71ccf7c2c81b03"
2281
2281
  },
2282
2282
  {
2283
2283
  "package": "@xemahq/xema",
2284
- "version": "0.33.2",
2284
+ "version": "0.33.3",
2285
2285
  "path": "edition/edition-source-pins.mjs",
2286
2286
  "sha256": "sha256:b2190ace174a29875e2a9bd7209460b0ef6de186aca9b31bcd45dc17af57c1ba"
2287
2287
  },
2288
2288
  {
2289
2289
  "package": "@xemahq/xema",
2290
- "version": "0.33.2",
2290
+ "version": "0.33.3",
2291
2291
  "path": "edition/edition-workspace.mjs",
2292
2292
  "sha256": "sha256:f7267e93fd7dc07cf65fb8cac2c4439d28a72c1e0acbdeca1648c6859440eb32"
2293
2293
  },
2294
2294
  {
2295
2295
  "package": "@xemahq/xema",
2296
- "version": "0.33.2",
2296
+ "version": "0.33.3",
2297
2297
  "path": "edition/evidence-checkout.mjs",
2298
2298
  "sha256": "sha256:31fb669769b31de4c6f2b2501bb96959548172ef166e7568edbbfed7f5079a11"
2299
2299
  },
2300
2300
  {
2301
2301
  "package": "@xemahq/xema",
2302
- "version": "0.33.2",
2302
+ "version": "0.33.3",
2303
2303
  "path": "edition/evidence-grammars.mjs",
2304
2304
  "sha256": "sha256:4dae22b61ade99219a482b971c9780d68a3d37d021e39cdfe860b245364b8b9f"
2305
2305
  },
2306
2306
  {
2307
2307
  "package": "@xemahq/xema",
2308
- "version": "0.33.2",
2308
+ "version": "0.33.3",
2309
2309
  "path": "edition/evidence-trust-pin.mjs",
2310
2310
  "sha256": "sha256:b9dd5c01903394436530783614efb0cd357d982ed2befd844a39328479773cbc"
2311
2311
  },
2312
2312
  {
2313
2313
  "package": "@xemahq/xema",
2314
- "version": "0.33.2",
2314
+ "version": "0.33.3",
2315
+ "path": "edition/git-json.mjs",
2316
+ "sha256": "sha256:d0d25cda0c19007becabc9a2827c3dcf759b295cd0b41596eb4480c9549a1f21"
2317
+ },
2318
+ {
2319
+ "package": "@xemahq/xema",
2320
+ "version": "0.33.3",
2315
2321
  "path": "edition/image-delivery-entitlement-descriptor.mjs",
2316
2322
  "sha256": "sha256:4726b66116f01bf2523fafe2b7a356591eca8fb5ca6bd8b154dd4821873e921b"
2317
2323
  },
2318
2324
  {
2319
2325
  "package": "@xemahq/xema",
2320
- "version": "0.33.2",
2326
+ "version": "0.33.3",
2321
2327
  "path": "edition/image-evidence-replay-state.mjs",
2322
2328
  "sha256": "sha256:eba0cf1d38af0ab2f00c5099fa8bc2edc543136ce92f895544d4d284b3d6af0f"
2323
2329
  },
2324
2330
  {
2325
2331
  "package": "@xemahq/xema",
2326
- "version": "0.33.2",
2332
+ "version": "0.33.3",
2327
2333
  "path": "edition/image-evidence.mjs",
2328
2334
  "sha256": "sha256:09945712ecc6524d5f52cfd82141ef4cade45be35210777f6e32818792068881"
2329
2335
  },
2330
2336
  {
2331
2337
  "package": "@xemahq/xema",
2332
- "version": "0.33.2",
2338
+ "version": "0.33.3",
2333
2339
  "path": "edition/kernel-capability-domain.mjs",
2334
2340
  "sha256": "sha256:3ecf887dd8d399e91a8285f6f115f6e418fb59bbf3f004539fe65ec667219356"
2335
2341
  },
2336
2342
  {
2337
2343
  "package": "@xemahq/xema",
2338
- "version": "0.33.2",
2344
+ "version": "0.33.3",
2339
2345
  "path": "edition/kernel-contract-authority.mjs",
2340
2346
  "sha256": "sha256:20abdde62a7b037c01ea9bbe1ff1e6094e6b0efd0add2f656f1938a04409bf82"
2341
2347
  },
2342
2348
  {
2343
2349
  "package": "@xemahq/xema",
2344
- "version": "0.33.2",
2350
+ "version": "0.33.3",
2345
2351
  "path": "edition/lib.mjs",
2346
- "sha256": "sha256:4777627b3665e154f466251eb684ea7299ecd3c683447f65f2cc5b4041d29bdb"
2352
+ "sha256": "sha256:0e9045632a14cb1a035bce7aac3ad26e7b2211daa3f9c2cd27a55dfd772e3b6b"
2347
2353
  },
2348
2354
  {
2349
2355
  "package": "@xemahq/xema",
2350
- "version": "0.33.2",
2356
+ "version": "0.33.3",
2351
2357
  "path": "edition/no-follow-file.mjs",
2352
2358
  "sha256": "sha256:c17bdf5e441f3c079aa06d4b48bbed05cad087855b9ad2a5e6dc43d5ee2b17c3"
2353
2359
  },
2354
2360
  {
2355
2361
  "package": "@xemahq/xema",
2356
- "version": "0.33.2",
2362
+ "version": "0.33.3",
2357
2363
  "path": "edition/operation-requirements.mjs",
2358
2364
  "sha256": "sha256:ffe6fdc9da6b2974270fc86c11be0c41d0bc6d85c8e3c137d3c3bc00a72fc4f6"
2359
2365
  },
2360
2366
  {
2361
2367
  "package": "@xemahq/xema",
2362
- "version": "0.33.2",
2368
+ "version": "0.33.3",
2363
2369
  "path": "edition/pin-source.mjs",
2364
2370
  "sha256": "sha256:fbaa6dffbdf4a262a13127bcf37bca791234fae74e327ac250e56ff4b78c1244"
2365
2371
  },
2366
2372
  {
2367
2373
  "package": "@xemahq/xema",
2368
- "version": "0.33.2",
2374
+ "version": "0.33.3",
2369
2375
  "path": "edition/source-authority.mjs",
2370
2376
  "sha256": "sha256:858b607dde94e2abd00a341f569e18cfa567d4bff139c64a2d60abbb7de4c85e"
2371
2377
  },
2372
2378
  {
2373
2379
  "package": "@xemahq/xema",
2374
- "version": "0.33.2",
2380
+ "version": "0.33.3",
2375
2381
  "path": "edition/strict-json.mjs",
2376
2382
  "sha256": "sha256:f95778396df7f38b086f55d2ec4f3b6644a8aeff2b32282a62829145fb544562"
2377
2383
  },
2378
2384
  {
2379
2385
  "package": "@xemahq/xema",
2380
- "version": "0.33.2",
2386
+ "version": "0.33.3",
2381
2387
  "path": "edition/string-order.mjs",
2382
2388
  "sha256": "sha256:b3210cd273e4b2e01fb8b1a91e28734cd211d4010c080d0ce95f5e5ff631cffb"
2383
2389
  },
2384
2390
  {
2385
2391
  "package": "@xemahq/xema",
2386
- "version": "0.33.2",
2392
+ "version": "0.33.3",
2387
2393
  "path": "edition/trusted-executable.mjs",
2388
2394
  "sha256": "sha256:37d46cdbc999ddd55d4ea8aeed4850ddd1efde89ed7b532f5a2ddec87105d796"
2389
2395
  },
2390
2396
  {
2391
2397
  "package": "@xemahq/xema",
2392
- "version": "0.33.2",
2398
+ "version": "0.33.3",
2393
2399
  "path": "edition/verifier-binding.mjs",
2394
2400
  "sha256": "sha256:80732658617f5255258c538eeb2383331ec8553b44b58d552b567845a7b916aa"
2395
2401
  },
2396
2402
  {
2397
2403
  "package": "@xemahq/xema",
2398
- "version": "0.33.2",
2404
+ "version": "0.33.3",
2399
2405
  "path": "edition/verifier-files.mjs",
2400
2406
  "sha256": "sha256:cdd14a20a63f0f194a8abb55680a5a7e3ced1f09f8b210a90d349e655f6de0c0"
2401
2407
  },
2402
2408
  {
2403
2409
  "package": "@xemahq/xema",
2404
- "version": "0.33.2",
2410
+ "version": "0.33.3",
2405
2411
  "path": "edition/verifier-provenance.mjs",
2406
2412
  "sha256": "sha256:3e2fb8e07cf1f66b8ad6c7ac2555fe3af64ec7abeb07cecbaf1939a18c2b3663"
2407
2413
  },
2408
2414
  {
2409
2415
  "package": "@xemahq/xema",
2410
- "version": "0.33.2",
2416
+ "version": "0.33.3",
2411
2417
  "path": "edition/verify-image-evidence.mjs",
2412
2418
  "sha256": "sha256:62f83f8bb835f360535402d12657156d2c9d6e651b426c2b30a04a2dc2f8d3e8"
2413
2419
  },
2414
2420
  {
2415
2421
  "package": "@xemahq/xema",
2416
- "version": "0.33.2",
2422
+ "version": "0.33.3",
2417
2423
  "path": "edition/workspace-sections.mjs",
2418
2424
  "sha256": "sha256:058c849767434e9ba911d9310092bede42e3ef959c75bbd35ff8879dc21ce3ff"
2419
2425
  },
2420
2426
  {
2421
2427
  "package": "@xemahq/xema",
2422
- "version": "0.33.2",
2428
+ "version": "0.33.3",
2423
2429
  "path": "scripts/build-evidence-verifier.mjs",
2424
2430
  "sha256": "sha256:5a688d20c47dc88aff37aa0c6cc02daf2c370a5bc610fef8446ce7be222d4c0b"
2425
2431
  },
@@ -4,6 +4,7 @@ import { execFileSync } from 'node:child_process';
4
4
  import { existsSync, mkdirSync, readFileSync } from 'node:fs';
5
5
  import { dirname, resolve } from 'node:path';
6
6
 
7
+ import { readCrossRepoBuildInputsRecord, splitCrossRepoInputKey } from './cross-repo-build-inputs.mjs';
7
8
  import { discoverDistributionIds } from './distribution-paths.mjs';
8
9
  import { editionWorkspaceConfig } from './edition-config.mjs';
9
10
  import { readEditionSourcePins } from './edition-source-pins.mjs';
@@ -120,6 +121,53 @@ export function editionHeldCommits(plan, sourcePinsByEdition) {
120
121
  return new Map([...held].map(([repo, commits]) => [repo, [...commits]]));
121
122
  }
122
123
 
124
+ /**
125
+ * The commits each checkout must also hold because a consumer's cross-repo
126
+ * build-input record vendors bytes from them. The resolve hashes those paths at
127
+ * the record's commit (`resolveCrossRepoVendoredInputs`), and that commit lags
128
+ * or leads the source repository's own pin. The record is read AT THE
129
+ * CONSUMER'S PIN, by the same reader the resolve uses, from the consumer's
130
+ * checkout under `workspaceRoot` (so every repository must already be checked
131
+ * out). Maps source repo to the distinct such commits, excluding the commits
132
+ * the checkout already holds. A consumer or source repository without a
133
+ * repository pin is refused: there is no checkout to read or fetch into.
134
+ *
135
+ * @param {{ repo: string, sourceCommit: string, vendoredSourceCommit: string }[]} plan
136
+ * @returns {Map<string, string[]>}
137
+ */
138
+ export function crossRepoVendoredCommits(plan, index, workspaceRoot) {
139
+ const pins = new Map(plan.map((entry) => [entry.repo, entry]));
140
+ const consumers = [...new Set((index.crossRepoBuildInputs ?? []).map((input) => input.consumerRepo))].sort();
141
+ const extra = new Map();
142
+ for (const consumerRepo of consumers) {
143
+ const consumer = pins.get(consumerRepo);
144
+ if (!consumer) {
145
+ throw new Error(`cross-repo build inputs name consumer ${JSON.stringify(consumerRepo)}, which has no repository pin in the runtime image source index`);
146
+ }
147
+ const record = readCrossRepoBuildInputsRecord({
148
+ consumerRepo,
149
+ consumerRepoDir: resolve(workspaceRoot, consumerRepo),
150
+ consumerCommit: consumer.sourceCommit,
151
+ });
152
+ for (const [key, commit] of record) {
153
+ const [sourceRepo] = splitCrossRepoInputKey(key);
154
+ const source = pins.get(sourceRepo);
155
+ if (!source) {
156
+ throw new Error(
157
+ `${consumerRepo} at ${consumer.sourceCommit} vendors from ${JSON.stringify(sourceRepo)}, which has no repository pin in the runtime image source index`,
158
+ );
159
+ }
160
+ if (!SHA.test(String(commit ?? ''))) {
161
+ throw new Error(`${consumerRepo} at ${consumer.sourceCommit} vendors from ${sourceRepo} at a sourceCommit that is not exact lowercase 40-hex`);
162
+ }
163
+ if (commit === source.sourceCommit || commit === source.vendoredSourceCommit) {continue;}
164
+ if (!extra.has(sourceRepo)) {extra.set(sourceRepo, new Set());}
165
+ extra.get(sourceRepo).add(commit);
166
+ }
167
+ }
168
+ return new Map([...extra].map(([repo, commits]) => [repo, [...commits].sort()]));
169
+ }
170
+
123
171
  /** The `sourcePins` of every edition under the configured editions root. */
124
172
  export function readEditionsSourcePins() {
125
173
  return new Map(
@@ -129,6 +177,24 @@ export function readEditionsSourcePins() {
129
177
  );
130
178
  }
131
179
 
180
+ /**
181
+ * Second pass, once every repository sits at its pin: fetch the commits the
182
+ * consumers' cross-repo build-input records vendor from.
183
+ */
184
+ export function fetchCrossRepoVendoredCommits({ workspaceRoot, plan, index, workspace, env, runGit }) {
185
+ const vendored = crossRepoVendoredCommits(plan, index, workspaceRoot);
186
+ for (const entry of plan) {
187
+ const commits = vendored.get(entry.repo);
188
+ if (!commits) {continue;}
189
+ withSourceGitEnv({ name: entry.repo, url: entry.repositoryUrl }, { workspace, env }, (gitEnv) => {
190
+ for (const commit of commits) {
191
+ runGit(['-C', resolve(workspaceRoot, entry.repo), 'fetch', '--quiet', '--depth=1', 'origin', commit], gitEnv);
192
+ console.log(`[source-checkout] ${entry.repo} also holds cross-repo vendored commit ${commit}`);
193
+ }
194
+ });
195
+ }
196
+ }
197
+
132
198
  export function checkoutIndexedSources({ workspaceRoot, index, platformServiceIndex, editionSourcePins = new Map(), env = process.env, runGit = git }) {
133
199
  if (!workspaceRoot) {throw new Error('workspaceRoot is required');}
134
200
  const workspace = editionWorkspaceConfig();
@@ -176,6 +242,7 @@ export function checkoutIndexedSources({ workspaceRoot, index, platformServiceIn
176
242
  });
177
243
  console.log(`[source-checkout] ${entry.repo} @ ${entry.sourceCommit}`);
178
244
  }
245
+ fetchCrossRepoVendoredCommits({ workspaceRoot, plan, index, workspace, env, runGit });
179
246
  return plan.length;
180
247
  }
181
248
 
@@ -0,0 +1,158 @@
1
+ /**
2
+ * The ONE reader of a consumer repository's cross-repo build-input record, and
3
+ * the resolver built on it. The source checkout (which commits to fetch) and the
4
+ * image resolve (which bytes to hash) both read the record here, at the
5
+ * consumer's pin, so the plan and the resolve cannot diverge.
6
+ */
7
+ import { sourcePath } from './edition-workspace.mjs';
8
+ import { readGitJsonAtCommit } from './git-json.mjs';
9
+
10
+ /**
11
+ * The producing repository's OWN committed record of what it vendored.
12
+ *
13
+ * `vendor-cross-repo-inputs.mjs` reads exactly this file, at the commit being
14
+ * built, to emit the `--vendored <dest>=<repoDir>@<commit>:<src>` arguments it
15
+ * hands `compute-image-tag.mjs`. It is therefore the ONLY source of the vendored
16
+ * commit that can agree with the tag CI pushes.
17
+ */
18
+ export const CROSS_REPO_BUILD_INPUTS_PATH =
19
+ 'tooling/release/cross-repo-build-inputs.json';
20
+
21
+ /** Identity of one vendored input — the tuple that survives a pin bump. */
22
+ export function crossRepoInputKey(input) {
23
+ return [input.sourceRepo, input.sourcePath, input.destinationPath].join('\u0000');
24
+ }
25
+
26
+ /** The `[sourceRepo, sourcePath, destinationPath]` a `crossRepoInputKey` frames. */
27
+ export function splitCrossRepoInputKey(key) {
28
+ return key.split('\u0000');
29
+ }
30
+
31
+ /**
32
+ * The consumer's record of what it vendored, read at ONE commit of the consumer
33
+ * (never the work tree): a map from `crossRepoInputKey` to the vendored
34
+ * `sourceCommit`. Unreadable is an error, not an empty record.
35
+ */
36
+ export function readCrossRepoBuildInputsRecord({
37
+ consumerRepo,
38
+ consumerRepoDir,
39
+ consumerCommit,
40
+ }) {
41
+ let record;
42
+ try {
43
+ record = readGitJsonAtCommit(
44
+ consumerRepoDir,
45
+ CROSS_REPO_BUILD_INPUTS_PATH,
46
+ consumerCommit,
47
+ );
48
+ } catch (cause) {
49
+ throw new Error(
50
+ `"${consumerRepo}" declares cross-repo build inputs but ` +
51
+ `${CROSS_REPO_BUILD_INPUTS_PATH} is unreadable at ${consumerCommit}. ` +
52
+ `That file is what its CI feeds to compute-image-tag.mjs; without it ` +
53
+ `no coordinate this tooling derives can match the tag CI pushes.`,
54
+ { cause },
55
+ );
56
+ }
57
+ const mirrored = new Map();
58
+ for (const input of record?.inputs ?? []) {
59
+ mirrored.set(crossRepoInputKey(input), input.sourceCommit);
60
+ }
61
+ return mirrored;
62
+ }
63
+
64
+ /**
65
+ * Resolve the vendored inputs of ONE consumer repository at ONE commit.
66
+ *
67
+ * WHY THE COMMIT COMES FROM THE CONSUMER, NOT FROM THE INDEX
68
+ *
69
+ * The index's `crossRepoBuildInputs` carry no `sourceCommit`; they name only
70
+ * WHICH content is vendored. Both this tooling and the parity gate used to
71
+ * supply the missing commit from `runtimeSourceIndex.repositoryPins`, and CI
72
+ * supplies it from the file above — so the two disagreed the moment a consumer
73
+ * repository's mirror lagged the index by one pin, which is its NORMAL resting
74
+ * state: the mirror is generated FROM the index and committed separately, so it
75
+ * is stale by construction between the index moving and the consumer
76
+ * regenerating.
77
+ *
78
+ * That is not a cosmetic disagreement. `crossRepo.sourceCommit` is framed into
79
+ * the component KEY of the image-input hash, so a pin that differs by one
80
+ * commit yields a different `contentHash` FOR BYTE-IDENTICAL VENDORED CONTENT —
81
+ * a coordinate no CI run has ever pushed or ever will. Measured 2026-09-07 on
82
+ * `origin/main`: five of the sixty-eight coordinates in the eight editions
83
+ * (`backlog-api`, `channel-gateway-api`, `control-plane-api`,
84
+ * `document-render-api` from repos/xema-community, and `biome-host-api` from
85
+ * repos/xema-base) 404'd in the registry for exactly this reason, while the tags
86
+ * their producing CI DID push were present and served on both platforms.
87
+ *
88
+ * Reading the index pin also imposed a requirement that cannot be satisfied.
89
+ * `repos/xema-base` vendors from `repos/xema-community` and vice versa, so
90
+ * "every mirror agrees with the index" is a mutual fixed point over a 2-cycle,
91
+ * and bumping either side invalidates the other. No such pair exists: scanning
92
+ * eighty commits of each on `origin/main` found ZERO mutually coherent pairs.
93
+ * Reading the consumer's own record removes the fixed point entirely — an
94
+ * image's identity depends only on the repository that builds it.
95
+ *
96
+ * It stays edition-INDEPENDENT, which is the property the previous design was
97
+ * protecting: the record is a property of the consumer's COMMIT, not of any
98
+ * edition's resolved commit map, so every edition shipping a subject still
99
+ * derives the same `contentHash` for it.
100
+ */
101
+ export function resolveCrossRepoVendoredInputs({
102
+ indexInputs,
103
+ consumerRepo,
104
+ consumerRepoDir,
105
+ consumerCommit,
106
+ }) {
107
+ const declared = indexInputs.filter(
108
+ (input) => input.consumerRepo === consumerRepo,
109
+ );
110
+ if (declared.length === 0) {return [];}
111
+ if (!consumerCommit) {
112
+ throw new Error(
113
+ `cross-repo build inputs are declared for "${consumerRepo}", but this ` +
114
+ `distribution resolves no commit for it — the vendored content cannot ` +
115
+ `be pinned, so the image cannot be addressed`,
116
+ );
117
+ }
118
+ const mirrored = readCrossRepoBuildInputsRecord({
119
+ consumerRepo,
120
+ consumerRepoDir,
121
+ consumerCommit,
122
+ });
123
+ // Set equality in BOTH directions. A commit that LAGS is normal and is now
124
+ // honoured; a differing input SET is a genuine declaration disagreement, and
125
+ // it moves the hash just as surely — so it must fail rather than resolve to a
126
+ // coordinate built from a different closure.
127
+ const declaredKeys = new Set(declared.map((input) => crossRepoInputKey(input)));
128
+ const resolved = declared.map((input) => {
129
+ const sourceCommit = mirrored.get(crossRepoInputKey(input));
130
+ if (!sourceCommit) {
131
+ throw new Error(
132
+ `the runtime image source index declares a cross-repo build input for ` +
133
+ `"${consumerRepo}" vendoring "${input.sourceRepo}:${input.sourcePath}" ` +
134
+ `to "${input.destinationPath}", but ${CROSS_REPO_BUILD_INPUTS_PATH} at ` +
135
+ `${consumerCommit} declares no such input — its CI does not vendor it, ` +
136
+ `so the coordinate this tooling would derive is one nothing builds`,
137
+ );
138
+ }
139
+ return {
140
+ consumerService: input.consumerService ?? null,
141
+ destinationPath: input.destinationPath,
142
+ sourceRepoDir: sourcePath(input.sourceRepo),
143
+ sourcePath: input.sourcePath,
144
+ sourceCommit,
145
+ };
146
+ });
147
+ for (const key of mirrored.keys()) {
148
+ if (declaredKeys.has(key)) {continue;}
149
+ const [sourceRepo, sourcePath, destinationPath] = splitCrossRepoInputKey(key);
150
+ throw new Error(
151
+ `${CROSS_REPO_BUILD_INPUTS_PATH} at ${consumerCommit} vendors ` +
152
+ `"${sourceRepo}:${sourcePath}" to "${destinationPath}" for ` +
153
+ `"${consumerRepo}", which the runtime image source index does not ` +
154
+ `declare — its CI bakes content this tooling does not hash`,
155
+ );
156
+ }
157
+ return resolved;
158
+ }
@@ -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
- }
@@ -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.2",
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:de506f4bb207dcf31fb135d585486f8fa5bf4fc11d0d1841506d8773393fa423"
95
+ "sourceDigest": "sha256:fddbe95c5e82a8ecc251e4d62f65718c96630117ec28d6fb9c8f0f05174432bc"
96
96
  }
97
97
  }