@xemahq/edition-tooling 0.0.0-stage → 0.1.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 (219) hide show
  1. package/LICENSE +175 -0
  2. package/dist/cli.d.ts +7 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +47 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/edition.d.ts +4 -0
  7. package/dist/commands/edition.d.ts.map +1 -0
  8. package/dist/commands/edition.js +58 -0
  9. package/dist/commands/edition.js.map +1 -0
  10. package/dist/edition/verify-image-evidence.bundle.mjs +72104 -0
  11. package/dist/edition/verify-image-evidence.bundle.provenance.json +4119 -0
  12. package/dist/lib/exit-codes.d.ts +6 -0
  13. package/dist/lib/exit-codes.d.ts.map +1 -0
  14. package/dist/lib/exit-codes.js +21 -0
  15. package/dist/lib/exit-codes.js.map +1 -0
  16. package/dist/runtime/edition/run-entry.d.ts +10 -0
  17. package/dist/runtime/edition/run-entry.d.ts.map +1 -0
  18. package/dist/runtime/edition/run-entry.js +49 -0
  19. package/dist/runtime/edition/run-entry.js.map +1 -0
  20. package/dist/runtime/edition/verb-map.d.ts +70 -0
  21. package/dist/runtime/edition/verb-map.d.ts.map +1 -0
  22. package/dist/runtime/edition/verb-map.js +166 -0
  23. package/dist/runtime/edition/verb-map.js.map +1 -0
  24. package/edition/README.md +291 -0
  25. package/edition/adapter-bundled-versions.mjs +128 -0
  26. package/edition/adapter-host-versions.mjs +76 -0
  27. package/edition/assemble-producer-input.mjs +1423 -0
  28. package/edition/assemble-third-party-input.mjs +348 -0
  29. package/edition/authored-source-check.mjs +141 -0
  30. package/edition/authority-binding.mjs +23 -0
  31. package/edition/authorization-base.mjs +357 -0
  32. package/edition/biome-content.mjs +327 -0
  33. package/edition/build-biome-index.mjs +180 -0
  34. package/edition/build-ledger-completeness.mjs +67 -0
  35. package/edition/build-ledger.mjs +107 -0
  36. package/edition/build-platform-service-index.mjs +292 -0
  37. package/edition/build-runtime-image-source-index.mjs +1197 -0
  38. package/edition/built-verifier.mjs +55 -0
  39. package/edition/candidate-edition.mjs +735 -0
  40. package/edition/canonical-evidence-json.mjs +82 -0
  41. package/edition/canonical-repository-url.mjs +61 -0
  42. package/edition/catalogue-edition.mjs +49 -0
  43. package/edition/catalogue-evidence.mjs +228 -0
  44. package/edition/catalogue-fetch.mjs +124 -0
  45. package/edition/catalogue-lock-field.mjs +41 -0
  46. package/edition/catalogue-pin.mjs +71 -0
  47. package/edition/catalogue.mjs +237 -0
  48. package/edition/chart-set-source-commit.mjs +97 -0
  49. package/edition/check-committed-image-evidence.mjs +412 -0
  50. package/edition/checkout-indexed-source-repositories.mjs +283 -0
  51. package/edition/collected-refusals.mjs +59 -0
  52. package/edition/comment-blanker.mjs +75 -0
  53. package/edition/connector-source-authority.mjs +60 -0
  54. package/edition/contract-authority.mjs +122 -0
  55. package/edition/cosign-certificate-constraints.mjs +50 -0
  56. package/edition/cross-repo-build-inputs.mjs +158 -0
  57. package/edition/customer-definition.mjs +72 -0
  58. package/edition/customer-edition-verbs.mjs +610 -0
  59. package/edition/customer-flags.mjs +15 -0
  60. package/edition/definition-checkout.mjs +96 -0
  61. package/edition/develop-build-record.mjs +122 -0
  62. package/edition/develop-catalogue-free-resolve.mjs +282 -0
  63. package/edition/develop-catalogue-free-vendor.mjs +118 -0
  64. package/edition/develop-channel-evidence.mjs +23 -0
  65. package/edition/develop-channel-record.mjs +49 -0
  66. package/edition/develop-channel-satellites.mjs +65 -0
  67. package/edition/develop-chart-roster.mjs +96 -0
  68. package/edition/develop-chart-set.mjs +147 -0
  69. package/edition/develop-current-resolution.mjs +29 -0
  70. package/edition/develop-edition-invariants.mjs +142 -0
  71. package/edition/develop-edition-resolve.mjs +250 -0
  72. package/edition/develop-edition.mjs +332 -0
  73. package/edition/develop-engine-image.mjs +97 -0
  74. package/edition/develop-freeze.mjs +304 -0
  75. package/edition/develop-git-bundles.mjs +296 -0
  76. package/edition/develop-graph.mjs +34 -0
  77. package/edition/develop-image-coordinate.mjs +85 -0
  78. package/edition/develop-image-probe.mjs +122 -0
  79. package/edition/develop-image-record.mjs +260 -0
  80. package/edition/develop-resolve-report.mjs +159 -0
  81. package/edition/develop-select-records.mjs +90 -0
  82. package/edition/develop-selection-line.mjs +29 -0
  83. package/edition/develop-selection-pins.mjs +36 -0
  84. package/edition/develop-snapshot-inputs.mjs +112 -0
  85. package/edition/develop-store-root.mjs +20 -0
  86. package/edition/dispatched-runtime-components.mjs +172 -0
  87. package/edition/dist-check.mjs +1174 -0
  88. package/edition/distribution-evidence-set.mjs +1064 -0
  89. package/edition/distribution-paths.mjs +191 -0
  90. package/edition/edition-artifact.mjs +529 -0
  91. package/edition/edition-biome-manifests.mjs +85 -0
  92. package/edition/edition-ceiling.mjs +182 -0
  93. package/edition/edition-channel.mjs +27 -0
  94. package/edition/edition-chart-set.mjs +104 -0
  95. package/edition/edition-config.mjs +374 -0
  96. package/edition/edition-constants.mjs +110 -0
  97. package/edition/edition-contract-parse.mjs +179 -0
  98. package/edition/edition-contract.json +100 -0
  99. package/edition/edition-image-seal.mjs +115 -0
  100. package/edition/edition-kind.mjs +10 -0
  101. package/edition/edition-list.mjs +35 -0
  102. package/edition/edition-locks.mjs +78 -0
  103. package/edition/edition-pin-verbs.mjs +183 -0
  104. package/edition/edition-pin.mjs +17 -0
  105. package/edition/edition-pinned-inputs.mjs +116 -0
  106. package/edition/edition-protocol.mjs +285 -0
  107. package/edition/edition-publisher.mjs +21 -0
  108. package/edition/edition-seal.mjs +257 -0
  109. package/edition/edition-source-pins.mjs +211 -0
  110. package/edition/edition-unpin.mjs +16 -0
  111. package/edition/edition-workspace.mjs +205 -0
  112. package/edition/evidence-checkout.mjs +439 -0
  113. package/edition/evidence-grammars.mjs +18 -0
  114. package/edition/evidence-input-mode.mjs +136 -0
  115. package/edition/evidence-trust-pin.mjs +392 -0
  116. package/edition/evidence-trust-policy.mjs +374 -0
  117. package/edition/external-source-walk.mjs +486 -0
  118. package/edition/first-party-source-ids.mjs +62 -0
  119. package/edition/fleet-coordinate-finality.mjs +363 -0
  120. package/edition/fulcio-certificate-extensions.mjs +278 -0
  121. package/edition/git-json.mjs +54 -0
  122. package/edition/git-source-auth.mjs +131 -0
  123. package/edition/image-delivery-entitlement-descriptor.mjs +106 -0
  124. package/edition/image-evidence-replay-state.mjs +242 -0
  125. package/edition/image-evidence.mjs +1328 -0
  126. package/edition/image-lock-service-rows.mjs +26 -0
  127. package/edition/image-subject-identity.mjs +19 -0
  128. package/edition/images-from-record.mjs +216 -0
  129. package/edition/indexes-at-pins.mjs +13 -0
  130. package/edition/infra-images-lock.mjs +105 -0
  131. package/edition/kernel-capability-domain.mjs +30 -0
  132. package/edition/kernel-contract-authority.mjs +526 -0
  133. package/edition/kernel-contract-source-path-set.mjs +96 -0
  134. package/edition/kernel-package.mjs +237 -0
  135. package/edition/kernel-pin-source.mjs +63 -0
  136. package/edition/kernel-protocol-choice.mjs +239 -0
  137. package/edition/kernel-signer-kind.mjs +23 -0
  138. package/edition/lib.mjs +3322 -0
  139. package/edition/locked-bundle-signer.mjs +297 -0
  140. package/edition/map-with-concurrency.mjs +19 -0
  141. package/edition/no-follow-file.mjs +94 -0
  142. package/edition/oci-evidence-verbs.mjs +421 -0
  143. package/edition/oci-image-source-evidence.mjs +377 -0
  144. package/edition/operation-grant-admission.mjs +285 -0
  145. package/edition/operation-requirements.mjs +282 -0
  146. package/edition/oras-process.mjs +35 -0
  147. package/edition/oras.mjs +664 -0
  148. package/edition/pin-source.mjs +242 -0
  149. package/edition/pinned-cosign.mjs +86 -0
  150. package/edition/plan-image-evidence-refresh.mjs +428 -0
  151. package/edition/platform-lock-readability.mjs +903 -0
  152. package/edition/platform-service-class.mjs +85 -0
  153. package/edition/platform-service-ownership.mjs +91 -0
  154. package/edition/platform-service-source-authority.mjs +105 -0
  155. package/edition/positive-closure.mjs +162 -0
  156. package/edition/previous-image-lock.mjs +25 -0
  157. package/edition/produce-distribution-completeness-evidence.mjs +317 -0
  158. package/edition/produce-edition-catalogue-evidence.mjs +252 -0
  159. package/edition/produce-image-delivery-entitlement-evidence.mjs +302 -0
  160. package/edition/produce-image-source-evidence.mjs +788 -0
  161. package/edition/producer-evidence-bundle.mjs +201 -0
  162. package/edition/producer-evidence-index-file.mjs +4 -0
  163. package/edition/record-refused.mjs +11 -0
  164. package/edition/registry-client-factory.mjs +21 -0
  165. package/edition/registry-credentials.mjs +225 -0
  166. package/edition/release-catalogue.mjs +48 -0
  167. package/edition/release-channel-guard.mjs +108 -0
  168. package/edition/release-edition-artifact.mjs +152 -0
  169. package/edition/release-edition.mjs +54 -0
  170. package/edition/remedy-command.mjs +89 -0
  171. package/edition/repin-evidence-trust.mjs +109 -0
  172. package/edition/required-resource-provider.mjs +58 -0
  173. package/edition/resolve-chart-set.mjs +376 -0
  174. package/edition/resolve-customer-edition.mjs +374 -0
  175. package/edition/resolve-distribution.mjs +2642 -0
  176. package/edition/resolve-edition.mjs +192 -0
  177. package/edition/resolve-images.mjs +2344 -0
  178. package/edition/resolve-infra-images.mjs +88 -0
  179. package/edition/seal-image-digests.mjs +954 -0
  180. package/edition/sealed-distribution-lock.mjs +22 -0
  181. package/edition/sealed-evidence-accept.mjs +82 -0
  182. package/edition/shallow-checkout.mjs +18 -0
  183. package/edition/sigstore-bundle-media-type.mjs +4 -0
  184. package/edition/source-authority.mjs +713 -0
  185. package/edition/source-closure.mjs +115 -0
  186. package/edition/store-publisher-trust.mjs +40 -0
  187. package/edition/strict-json.mjs +121 -0
  188. package/edition/string-order.mjs +5 -0
  189. package/edition/third-party-admission.mjs +118 -0
  190. package/edition/third-party-bundle-registry.mjs +349 -0
  191. package/edition/third-party-certificate-extensions.mjs +58 -0
  192. package/edition/third-party-image-pins.mjs +41 -0
  193. package/edition/third-party-pins-trusted.mjs +24 -0
  194. package/edition/third-party-producer-evidence.mjs +76 -0
  195. package/edition/third-party-source-check.mjs +28 -0
  196. package/edition/third-party-source-context.mjs +41 -0
  197. package/edition/third-party-source-evidence.mjs +326 -0
  198. package/edition/third-party-source-flags.mjs +148 -0
  199. package/edition/third-party-source-pins.mjs +270 -0
  200. package/edition/third-party-source-uniqueness.mjs +54 -0
  201. package/edition/third-party-source-walk.mjs +118 -0
  202. package/edition/third-party-source-writer.mjs +228 -0
  203. package/edition/third-party-subjects.mjs +128 -0
  204. package/edition/trust-policy-arming-verb.mjs +60 -0
  205. package/edition/trust-policy-arming.mjs +144 -0
  206. package/edition/trust-policy-image-ownership.mjs +85 -0
  207. package/edition/trusted-executable.mjs +166 -0
  208. package/edition/verifier-binding.mjs +45 -0
  209. package/edition/verifier-digest-pin.mjs +17 -0
  210. package/edition/verifier-files.mjs +8 -0
  211. package/edition/verifier-kernel-schemas.mjs +22 -0
  212. package/edition/verifier-provenance.mjs +64 -0
  213. package/edition/verify-catalogue-evidence.mjs +247 -0
  214. package/edition/verify-image-evidence.mjs +493 -0
  215. package/edition/verify-image-platforms.mjs +523 -0
  216. package/edition/why.mjs +56 -0
  217. package/edition/workspace-sections.mjs +287 -0
  218. package/package.json +83 -4
  219. package/README.md +0 -3
@@ -0,0 +1,287 @@
1
+ // The strict parsers for the product-data sections of `edition-workspace.json`.
2
+ // The library owns these SCHEMAS; the VALUES (which repository holds which
3
+ // service, which sources the runtime images are built from, which repository
4
+ // key a pin role uses) are the data root's. Every parser refuses an unknown key,
5
+ // a missing key and a wrong type, naming the section and the path.
6
+
7
+ import { developImageRegistryProblem } from './develop-store-root.mjs';
8
+
9
+ const FILE = 'edition-workspace.json';
10
+
11
+ const isObject = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
12
+
13
+ function fail(where, message) {
14
+ throw new Error(`${FILE}: ${where} ${message}`);
15
+ }
16
+
17
+ /** An object with exactly `required` plus any of `optional` keys. */
18
+ function exactObject(value, where, required, optional = []) {
19
+ if (!isObject(value)) {fail(where, 'must be an object');}
20
+ for (const key of Object.keys(value)) {
21
+ if (!required.includes(key) && !optional.includes(key)) {fail(where, `has unknown key "${key}"`);}
22
+ }
23
+ for (const key of required) {
24
+ if (!Object.hasOwn(value, key)) {fail(where, `is missing "${key}"`);}
25
+ }
26
+ return value;
27
+ }
28
+
29
+ function text(value, where) {
30
+ if (typeof value !== 'string' || value.length === 0) {fail(where, 'must be a non-empty string');}
31
+ return value;
32
+ }
33
+
34
+ function textList(value, where) {
35
+ if (!Array.isArray(value)) {fail(where, 'must be an array of strings');}
36
+ return Object.freeze(value.map((item, i) => text(item, `${where}[${i}]`)));
37
+ }
38
+
39
+ function list(value, where, parseItem) {
40
+ if (!Array.isArray(value)) {fail(where, 'must be an array');}
41
+ return Object.freeze(value.map((item, i) => Object.freeze(parseItem(item, `${where}[${i}]`))));
42
+ }
43
+
44
+ function uniqueBy(items, where, keyOf) {
45
+ const seen = new Set();
46
+ for (const item of items) {
47
+ const key = keyOf(item);
48
+ if (seen.has(key)) {fail(where, `declares "${key}" twice`);}
49
+ seen.add(key);
50
+ }
51
+ return items;
52
+ }
53
+
54
+ /** A repository handle: `repos/<r>` or `submodules/<r>`, the sources-root layout. */
55
+ const REPO_HANDLE = /^(?:repos|submodules)\/[a-z0-9][a-z0-9-]*$/;
56
+ const SOURCE_ID = /^[a-z][a-z0-9-]*$/;
57
+
58
+ function repoHandle(value, where) {
59
+ if (typeof value !== 'string' || !REPO_HANDLE.test(value)) {
60
+ fail(where, `must be a source checkout handle repos/<name> or submodules/<name>; got ${JSON.stringify(value)}`);
61
+ }
62
+ return value;
63
+ }
64
+
65
+ export const RuntimeSubjectKind = Object.freeze({
66
+ Service: 'service',
67
+ Host: 'host',
68
+ RuntimeComponent: 'runtime-component',
69
+ });
70
+
71
+ const SUBJECT_KINDS = Object.values(RuntimeSubjectKind);
72
+
73
+ function subjectKind(value, where) {
74
+ if (!SUBJECT_KINDS.includes(value)) {fail(where, `must be one of ${SUBJECT_KINDS.join(', ')}`);}
75
+ return value;
76
+ }
77
+
78
+ /**
79
+ * What launches a supplemental subject that no platform service binds: the
80
+ * owner its image-lock row is sealed under. Closed, one member until a second
81
+ * real case exists: a BIOME, which launches the subject's image itself, so
82
+ * the subject ships in every edition whose roster contains that biome.
83
+ */
84
+ export const SupplementalLauncherKind = Object.freeze({
85
+ Biome: 'biome',
86
+ });
87
+
88
+ const LAUNCHER_KINDS = Object.values(SupplementalLauncherKind);
89
+
90
+ function supplementalLauncher(value, where) {
91
+ exactObject(value, where, ['kind', 'biomeId']);
92
+ if (!LAUNCHER_KINDS.includes(value.kind)) {fail(`${where}.kind`, `must be one of ${LAUNCHER_KINDS.join(', ')}`);}
93
+ return Object.freeze({ kind: value.kind, biomeId: text(value.biomeId, `${where}.biomeId`) });
94
+ }
95
+
96
+ /**
97
+ * The retired `{repo handle: source id}` string form of a `firstPartySources`
98
+ * entry. Refused, never read: the entry also declares where the repository
99
+ * lives, which the develop graph resolves its head from.
100
+ */
101
+ export class RetiredFirstPartySourceShapeError extends Error {
102
+ constructor(repo, id) {
103
+ super(
104
+ `${FILE}: "firstPartySources"["${repo}"] is the retired source-id string ${JSON.stringify(id)}. ` +
105
+ `Migrate it to { "sourceId": ${JSON.stringify(id)}, "repositoryUrl": "<the repository's canonical https URL>" }.`,
106
+ );
107
+ this.name = 'RetiredFirstPartySourceShapeError';
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Where a first-party source's images come from on the everyday lane: built by the job in the aggregator workspace,
113
+ * or a satellite producer's own signed develop digest (a source outside the workspace, such as
114
+ * submodules/xema-host-web). `--images-from` admits a record entry only from the list its source's origin names.
115
+ */
116
+ export const FirstPartyImageOrigin = Object.freeze({
117
+ Workspace: 'workspace',
118
+ Satellite: 'satellite',
119
+ });
120
+
121
+ const IMAGE_ORIGINS = Object.values(FirstPartyImageOrigin);
122
+
123
+ /** An issue key, `<PROJECT>-<number>`: the ticket that removes a member's current-resolution exception. */
124
+ const TICKET = /^[A-Z][A-Z0-9]*-[1-9][0-9]*$/;
125
+
126
+ /**
127
+ * `firstPartySources`: every first-party repository, keyed by its source
128
+ * checkout handle, with its declared source id (the identity a runtime subject
129
+ * is named after) and its repository URL (where it lives: the develop graph
130
+ * resolves its head there, and a release pin must name the same URL). The URL
131
+ * is checked canonical and under `sourceRepositoryUrlPrefix` with the whole
132
+ * config (edition-config.mjs). `imageOrigin` (FirstPartyImageOrigin) is optional
133
+ * here and has no default: `--images-from` refuses a source that does not declare it.
134
+ * `currentResolution` (a ticket, `<PROJECT>-<number>`) is optional and has no default: a member that declares it keeps
135
+ * the CURRENT develop resolution instead of a signed build record until that ticket removes it
136
+ * (develop-current-resolution.mjs); a member without it never does.
137
+ * `developImageRegistry` (a lowercase `<host[:port]>/<path>` store root, no scheme, tag, digest, trailing slash or
138
+ * `/dev`) is the member's develop store: develop images are `<root>/dev/<name>` and build records
139
+ * `<root>/build-records/<name>` (develop-image-coordinate.mjs). Required: a source declares its store explicitly, even when it is still the owner root of its release images.
140
+ */
141
+ export function parseFirstPartySources(value) {
142
+ const where = '"firstPartySources"';
143
+ if (!isObject(value) || Object.keys(value).length === 0) {
144
+ fail(where, 'must be a non-empty {repo handle: { sourceId, repositoryUrl }} object');
145
+ }
146
+ const entries = Object.entries(value).map(([repo, entry]) => {
147
+ repoHandle(repo, `${where} key`);
148
+ if (typeof entry === 'string') {throw new RetiredFirstPartySourceShapeError(repo, entry);}
149
+ const at = `${where}["${repo}"]`;
150
+ if (isObject(entry) && !Object.hasOwn(entry, 'developImageRegistry')) {
151
+ fail(at, `is missing "developImageRegistry": declare the develop store of ${repo} as <host[:port]>/<path> (for example registry.example.com/acme-dev)`);
152
+ }
153
+ exactObject(entry, at, ['sourceId', 'repositoryUrl', 'developImageRegistry'], ['imageOrigin', 'currentResolution']);
154
+ if (typeof entry.sourceId !== 'string' || !SOURCE_ID.test(entry.sourceId)) {
155
+ fail(`${at}.sourceId`, `must be a lowercase kebab-case source id; got ${JSON.stringify(entry.sourceId)}`);
156
+ }
157
+ if (Object.hasOwn(entry, 'imageOrigin') && !IMAGE_ORIGINS.includes(entry.imageOrigin)) {
158
+ fail(`${at}.imageOrigin`, `must be one of ${IMAGE_ORIGINS.join(', ')}; got ${JSON.stringify(entry.imageOrigin)}`);
159
+ }
160
+ if (Object.hasOwn(entry, 'currentResolution') && (typeof entry.currentResolution !== 'string' || !TICKET.test(entry.currentResolution))) {
161
+ fail(`${at}.currentResolution`, `must be the ticket that removes the exception, <PROJECT>-<number>; got ${JSON.stringify(entry.currentResolution)}`);
162
+ }
163
+ const registryProblem = developImageRegistryProblem(entry.developImageRegistry);
164
+ if (registryProblem !== null) {fail(`${at}.developImageRegistry`, registryProblem);}
165
+ return [repo, Object.freeze({
166
+ sourceId: entry.sourceId,
167
+ repositoryUrl: text(entry.repositoryUrl, `${at}.repositoryUrl`),
168
+ developImageRegistry: entry.developImageRegistry,
169
+ ...(Object.hasOwn(entry, 'imageOrigin') ? { imageOrigin: entry.imageOrigin } : {}),
170
+ ...(Object.hasOwn(entry, 'currentResolution') ? { currentResolution: entry.currentResolution } : {}),
171
+ })];
172
+ });
173
+ uniqueBy(entries, `${where} sourceId`, ([, entry]) => entry.sourceId);
174
+ uniqueBy(entries, `${where} repositoryUrl`, ([, entry]) => entry.repositoryUrl);
175
+ return Object.freeze(Object.fromEntries(entries));
176
+ }
177
+
178
+ function parseRuntimeBinding(value, where) {
179
+ if (value === null) {return null;}
180
+ exactObject(value, where, ['subjectKind', 'subjectId']);
181
+ return { subjectKind: subjectKind(value.subjectKind, `${where}.subjectKind`), subjectId: text(value.subjectId, `${where}.subjectId`) };
182
+ }
183
+
184
+ export function parsePlatformServiceSources(value) {
185
+ const where = '"platformServiceSources"';
186
+ const items = list(value, where, (item, at) => {
187
+ exactObject(item, at, ['name', 'tier', 'repo', 'manifestPath', 'manifestName', 'imageKey', 'runtimeBinding'], ['installationClass']);
188
+ return {
189
+ name: text(item.name, `${at}.name`),
190
+ tier: text(item.tier, `${at}.tier`),
191
+ ...(item.installationClass === undefined ? {} : { installationClass: text(item.installationClass, `${at}.installationClass`) }),
192
+ repo: repoHandle(item.repo, `${at}.repo`),
193
+ manifestPath: text(item.manifestPath, `${at}.manifestPath`),
194
+ manifestName: text(item.manifestName, `${at}.manifestName`),
195
+ imageKey: text(item.imageKey, `${at}.imageKey`),
196
+ runtimeBinding: parseRuntimeBinding(item.runtimeBinding, `${at}.runtimeBinding`),
197
+ };
198
+ });
199
+ if (items.length === 0) {fail(where, 'must declare at least one platform service');}
200
+ uniqueBy(items, where, (item) => item.name);
201
+ uniqueBy(items, `${where} imageKey`, (item) => item.imageKey);
202
+ return items;
203
+ }
204
+
205
+ export function parseConnectorSources(value) {
206
+ const where = '"connectorSources"';
207
+ exactObject(value, where, ['repository', 'bindings', 'nonProductionIds', 'buildInput']);
208
+ const bindings = list(value.bindings, `${where}.bindings`, (item, at) => {
209
+ exactObject(item, at, ['id', 'path']);
210
+ return { id: text(item.id, `${at}.id`), path: text(item.path, `${at}.path`) };
211
+ });
212
+ uniqueBy(bindings, `${where}.bindings`, (item) => item.id);
213
+ const buildInput = exactObject(value.buildInput, `${where}.buildInput`, ['consumerRepo', 'consumerService', 'destinationRoot']);
214
+ return Object.freeze({
215
+ repository: repoHandle(value.repository, `${where}.repository`),
216
+ bindings,
217
+ nonProductionIds: textList(value.nonProductionIds, `${where}.nonProductionIds`),
218
+ buildInput: Object.freeze({
219
+ consumerRepo: repoHandle(buildInput.consumerRepo, `${where}.buildInput.consumerRepo`),
220
+ consumerService: text(buildInput.consumerService, `${where}.buildInput.consumerService`),
221
+ destinationRoot: text(buildInput.destinationRoot, `${where}.buildInput.destinationRoot`),
222
+ }),
223
+ });
224
+ }
225
+
226
+ export function parseRuntimeImageSources(value) {
227
+ const where = '"runtimeImageSources"';
228
+ exactObject(value, where, ['supplementalSources', 'crossRepoBuildInputs', 'buildStepContextDirectories']);
229
+ const supplementalSources = list(value.supplementalSources, `${where}.supplementalSources`, (item, at) => {
230
+ exactObject(item, at, ['kind', 'id', 'repo', 'path', 'sourceManifestPath'], ['launchedBy']);
231
+ return {
232
+ kind: subjectKind(item.kind, `${at}.kind`),
233
+ id: text(item.id, `${at}.id`),
234
+ repo: repoHandle(item.repo, `${at}.repo`),
235
+ path: text(item.path, `${at}.path`),
236
+ sourceManifestPath: text(item.sourceManifestPath, `${at}.sourceManifestPath`),
237
+ ...(Object.hasOwn(item, 'launchedBy')
238
+ ? { launchedBy: supplementalLauncher(item.launchedBy, `${at}.launchedBy`) }
239
+ : {}),
240
+ };
241
+ });
242
+ uniqueBy(supplementalSources, `${where}.supplementalSources`, (item) => `${item.kind}/${item.id}`);
243
+ const crossRepoBuildInputs = list(value.crossRepoBuildInputs, `${where}.crossRepoBuildInputs`, (item, at) => {
244
+ exactObject(item, at, ['consumerRepo', 'consumerService', 'sourceRepo', 'sourcePath', 'destinationPath']);
245
+ return {
246
+ consumerRepo: repoHandle(item.consumerRepo, `${at}.consumerRepo`),
247
+ consumerService: item.consumerService === null ? null : text(item.consumerService, `${at}.consumerService`),
248
+ sourceRepo: repoHandle(item.sourceRepo, `${at}.sourceRepo`),
249
+ sourcePath: text(item.sourcePath, `${at}.sourcePath`),
250
+ destinationPath: text(item.destinationPath, `${at}.destinationPath`),
251
+ };
252
+ });
253
+ const buildStepContextDirectories = list(value.buildStepContextDirectories, `${where}.buildStepContextDirectories`, (item, at) => {
254
+ exactObject(item, at, ['repo', 'path', 'reason']);
255
+ return { repo: repoHandle(item.repo, `${at}.repo`), path: text(item.path, `${at}.path`), reason: text(item.reason, `${at}.reason`) };
256
+ });
257
+ return Object.freeze({ supplementalSources, crossRepoBuildInputs, buildStepContextDirectories });
258
+ }
259
+
260
+ /** The pin roles are a closed set the library defines; their repository keys are data. */
261
+ export const PinRole = Object.freeze({
262
+ Data: 'data',
263
+ Kernel: 'kernel',
264
+ Deploy: 'deploy',
265
+ });
266
+
267
+ export function parsePinRoles(value) {
268
+ const where = '"pinRoles"';
269
+ exactObject(value, where, Object.values(PinRole));
270
+ const keys = Object.values(PinRole).map((role) => [role, text(value[role], `${where}.${role}`)]);
271
+ uniqueBy(keys, where, ([, key]) => key);
272
+ return Object.freeze(Object.fromEntries(keys));
273
+ }
274
+
275
+ export function parseConfinedBiomes(value) {
276
+ const where = '"confinedBiomes"';
277
+ const items = list(value, where, (item, at) => {
278
+ exactObject(item, at, ['biomeId', 'holds', 'consequence', 'admittedEditions']);
279
+ return {
280
+ biomeId: text(item.biomeId, `${at}.biomeId`),
281
+ holds: text(item.holds, `${at}.holds`),
282
+ consequence: text(item.consequence, `${at}.consequence`),
283
+ admittedEditions: textList(item.admittedEditions, `${at}.admittedEditions`),
284
+ };
285
+ });
286
+ return uniqueBy(items, where, (item) => item.biomeId);
287
+ }
package/package.json CHANGED
@@ -1,6 +1,85 @@
1
1
  {
2
2
  "name": "@xemahq/edition-tooling",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "The Xema edition pipeline: resolve, seal, check, develop-snapshot and evidence verbs for a distribution data root. Ships the `xema-edition` bin and the `candidate-edition` library. Not an operator tool: the operator CLI is @xemahq/xema.",
5
+ "license": "LicenseRef-Xema-BSL-1.1",
6
+ "author": "Neuralchowder Inc. <developer@xema.dev> (https://xema.dev)",
7
+ "homepage": "https://xema.dev",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/xema-dev/xema-base.git",
11
+ "directory": "packages/edition/edition-tooling"
12
+ },
13
+ "bugs": "https://github.com/xema-dev/xema-base/issues",
14
+ "type": "commonjs",
15
+ "engines": {
16
+ "node": ">=22.20.0 <23"
17
+ },
18
+ "main": "dist/cli.js",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/cli.d.ts",
22
+ "require": "./dist/cli.js",
23
+ "default": "./dist/cli.js"
24
+ },
25
+ "./candidate-edition": {
26
+ "import": "./edition/candidate-edition.mjs",
27
+ "default": "./edition/candidate-edition.mjs"
28
+ },
29
+ "./package.json": "./package.json"
30
+ },
31
+ "bin": {
32
+ "xema-edition": "./dist/cli.js"
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "edition/**/*.mjs",
37
+ "edition/*.json",
38
+ "!edition/**/*.test.mjs",
39
+ "!edition/**/*-contract-tests.mjs",
40
+ "!edition/test-fixtures/**",
41
+ "edition/README.md"
42
+ ],
43
+ "publishConfig": {
44
+ "registry": "https://registry.npmjs.org/",
45
+ "access": "public"
46
+ },
47
+ "dependencies": {
48
+ "@xemahq/distribution-source-hash": "^0.2.2",
49
+ "commander": "^12.1.0",
50
+ "yaml": "^2.6.1",
51
+ "zod": "^4.6.1",
52
+ "@xemahq/biome-sdk": "^25.0.0",
53
+ "@xemahq/oci-closure": "^0.1.0",
54
+ "@xemahq/biome-supply-chain": "^0.9.0",
55
+ "@xemahq/contracts": "^0.120.0"
56
+ },
57
+ "devDependencies": {
58
+ "@types/jest": "^30.0.0",
59
+ "@types/node": "25.2.3",
60
+ "esbuild": "^0.28.2",
61
+ "eslint": "^9.39.4",
62
+ "jest": "^30.3.0",
63
+ "prettier": "3.6.2",
64
+ "ts-jest": "^29.4.9",
65
+ "ts-node": "^10.9.2",
66
+ "typescript": "5.9.3",
67
+ "@xemahq/biome-test-utils": "^12.4.6",
68
+ "@xemahq/eslint-config": "^0.14.3"
69
+ },
70
+ "scripts": {
71
+ "clean": "rm -rf dist",
72
+ "build": "tsc -p tsconfig.build.json && node ./scripts/post-build.cjs && node ./scripts/build-evidence-verifier.mjs",
73
+ "format": "prettier --write \"src/**/*.ts\"",
74
+ "lint": "eslint . --max-warnings 0",
75
+ "lint:fix": "eslint . --fix",
76
+ "test": "jest --config jest.config.cjs && pnpm run test:edition",
77
+ "test:edition": "node scripts/run-edition-tests.mjs \"edition/*.test.mjs\" \"edition/*-contract-tests.mjs\"",
78
+ "verify:pack": "node ./scripts/verify-packed-assets.cjs",
79
+ "typecheck": "tsc --noEmit"
80
+ },
81
+ "xemaRelease": {
82
+ "sourceDigest": "sha256:83a084363b21bcdf698f3de93377171f8b3dedd5ada007c4f96b472b6f787b80",
83
+ "sha": "a32f8f5509b7ea239deb38216e4996e6286b10e8"
84
+ }
85
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.