@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,191 @@
1
+ import { existsSync, readFileSync, readdirSync, realpathSync } from 'node:fs';
2
+ import { isAbsolute, join, relative, resolve, sep } from 'node:path';
3
+
4
+ const DISTRIBUTION_ID_PATTERN =
5
+ /^[a-zA-Z0-9](?:[a-zA-Z0-9._-]{0,126}[a-zA-Z0-9])?$/;
6
+
7
+ export function assertCanonicalDistributionId(distributionId) {
8
+ if (
9
+ typeof distributionId !== 'string' ||
10
+ !DISTRIBUTION_ID_PATTERN.test(distributionId)
11
+ ) {
12
+ throw new Error(
13
+ 'distribution id must be a canonical 1-128 character identifier',
14
+ );
15
+ }
16
+ return distributionId;
17
+ }
18
+
19
+ function assertContained(root, candidate, label) {
20
+ const rel = relative(root, candidate);
21
+ if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
22
+ throw new Error(`${label} escapes the distributions root`);
23
+ }
24
+ }
25
+
26
+ export function resolveDistributionDirectory(
27
+ distributionsRoot,
28
+ distributionId,
29
+ ) {
30
+ assertCanonicalDistributionId(distributionId);
31
+ const root = realpathSync(distributionsRoot);
32
+ const expectedDirectory = join(root, distributionId);
33
+ if (!existsSync(expectedDirectory)) {
34
+ throw new Error(`distribution "${distributionId}" does not exist`);
35
+ }
36
+ const directory = realpathSync(expectedDirectory);
37
+ assertContained(root, directory, `distribution "${distributionId}"`);
38
+ if (directory !== expectedDirectory) {
39
+ throw new Error(
40
+ `distribution "${distributionId}" directory must not be a symlink`,
41
+ );
42
+ }
43
+ return { root, directory };
44
+ }
45
+
46
+ export function resolveDistributionFile(
47
+ distributionsRoot,
48
+ distributionId,
49
+ fileName,
50
+ ) {
51
+ const { directory } = resolveDistributionDirectory(
52
+ distributionsRoot,
53
+ distributionId,
54
+ );
55
+ const expectedPath = join(directory, fileName);
56
+ if (!existsSync(expectedPath)) {
57
+ throw new Error(
58
+ `distribution "${distributionId}" has no ${fileName} (${expectedPath})`,
59
+ );
60
+ }
61
+ const path = realpathSync(expectedPath);
62
+ assertContained(directory, path, `${fileName} for "${distributionId}"`);
63
+ if (path !== expectedPath) {
64
+ throw new Error(
65
+ `${fileName} for distribution "${distributionId}" must not be a symlink`,
66
+ );
67
+ }
68
+ return path;
69
+ }
70
+
71
+ export function readBoundDistributionFile({
72
+ distributionsRoot,
73
+ distributionId,
74
+ fileName,
75
+ parse,
76
+ }) {
77
+ const path = resolveDistributionFile(
78
+ distributionsRoot,
79
+ distributionId,
80
+ fileName,
81
+ );
82
+ const value = parse(JSON.parse(readFileSync(path, 'utf8')));
83
+ if (value.distributionId !== distributionId) {
84
+ throw new Error(
85
+ `${fileName} distributionId "${value.distributionId}" does not match requested distribution "${distributionId}"`,
86
+ );
87
+ }
88
+ return { path, value };
89
+ }
90
+
91
+ /**
92
+ * Every edition under `distributionsRoot`, sorted — DISCOVERED, never listed.
93
+ *
94
+ * An edition is a directory carrying `xema-distribution.json`; that file is
95
+ * what makes the directory an edition, so nothing else can be mistaken for one
96
+ * and a new edition needs no registration anywhere.
97
+ *
98
+ * Throws on an empty scan. A gate that iterates editions and finds none must
99
+ * fail rather than report success over nothing — the repeated defect in this
100
+ * tree is a check that reports green while executing nothing, and a literal
101
+ * id list is the other half of it: a narrowed list reads as coverage.
102
+ *
103
+ * `refresh-distribution-plane.mjs` carried this three-line scan privately;
104
+ * it lives here so the write path and the read paths cannot disagree about
105
+ * what an edition is.
106
+ */
107
+ export function discoverDistributionIds(distributionsRoot) {
108
+ const root = realpathSync(distributionsRoot);
109
+ const ids = readdirSync(root, { withFileTypes: true })
110
+ .filter((entry) => entry.isDirectory())
111
+ .map((entry) => entry.name)
112
+ .filter((name) => existsSync(join(root, name, 'xema-distribution.json')))
113
+ .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
114
+ if (ids.length === 0) {
115
+ throw new Error(
116
+ `found no edition under ${root} — an empty scan is not a result`,
117
+ );
118
+ }
119
+ return ids;
120
+ }
121
+
122
+ export function distributionOutputPath(
123
+ distributionsRoot,
124
+ distributionId,
125
+ fileName,
126
+ ) {
127
+ const { directory } = resolveDistributionDirectory(
128
+ distributionsRoot,
129
+ distributionId,
130
+ );
131
+ const path = resolve(directory, fileName);
132
+ assertContained(directory, path, `${fileName} output`);
133
+ if (existsSync(path) && realpathSync(path) !== path) {
134
+ throw new Error(
135
+ `${fileName} output for distribution "${distributionId}" must not be a symlink`,
136
+ );
137
+ }
138
+ return path;
139
+ }
140
+
141
+ /**
142
+ * The directory name holding editions that are NOT yet promoted.
143
+ *
144
+ * `discoverDistributionIds` is deliberately a single-level scan, so an edition
145
+ * one level down is invisible to it and to `dist:check` — which is the whole
146
+ * mechanism of the holding pen, not an oversight.
147
+ */
148
+ export const PENDING_EDITIONS_DIRNAME = 'editions-pending-merge';
149
+
150
+ /**
151
+ * Every edition under `distributionsRoot`, in BOTH tiers, as
152
+ * `{ id, directory, pending }` — sorted, promoted tier first.
153
+ *
154
+ * WHY BOTH TIERS. A generic test suite must be able to name its subjects
155
+ * without naming a customer, and a customer's edition is exactly the thing that
156
+ * is either pending here or has moved to a root of its own. Discovering both
157
+ * means one expression covers the edition wherever it currently sits, so
158
+ * promoting it — or removing it — changes no test.
159
+ *
160
+ * THE EMPTY CASES ARE ASYMMETRIC ON PURPOSE.
161
+ *
162
+ * * The PROMOTED tier delegates to {@link discoverDistributionIds}, which
163
+ * THROWS on an empty scan. An empty promoted tier means the root is wrong,
164
+ * and a checker that iterated zero editions and reported success is this
165
+ * tree's most-repeated defect.
166
+ * * The PENDING tier may legitimately be empty — that is what a fully
167
+ * promoted fleet looks like — so it is not an error. A CALLER that narrows
168
+ * to a subset of these and finds nothing must say so out loud rather than
169
+ * pass in silence; that reporting duty is the caller's, because only the
170
+ * caller knows what it narrowed to.
171
+ */
172
+ export function discoverEditionDirectories(distributionsRoot) {
173
+ const root = realpathSync(distributionsRoot);
174
+ const promoted = discoverDistributionIds(root).map((id) => ({
175
+ id,
176
+ directory: join(root, id),
177
+ pending: false,
178
+ }));
179
+ const pendingRoot = join(root, PENDING_EDITIONS_DIRNAME);
180
+ const pending = !existsSync(pendingRoot)
181
+ ? []
182
+ : readdirSync(pendingRoot, { withFileTypes: true })
183
+ .filter((entry) => entry.isDirectory())
184
+ .map((entry) => entry.name)
185
+ .filter((name) =>
186
+ existsSync(join(pendingRoot, name, 'xema-distribution.json')),
187
+ )
188
+ .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
189
+ .map((id) => ({ id, directory: join(pendingRoot, id), pending: true }));
190
+ return [...promoted, ...pending];
191
+ }