@xemahq/edition-tooling 0.0.0-stage → 0.1.1

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,282 @@
1
+ import { createRequire } from 'node:module';
2
+
3
+ import { kernelDistPath } from './edition-workspace.mjs';
4
+ import { compareStrings } from './string-order.mjs';
5
+
6
+ const requireFromHere = createRequire(import.meta.url);
7
+
8
+ let kernelPrivilegeGrammars;
9
+ let kernelAudienceGrammars;
10
+
11
+ /**
12
+ * The kernel's ONE operation-id grammar (`OperationIdSchema`) and closed
13
+ * `PlatformSweep` vocabulary, from the distribution subpath: the only plane
14
+ * the edition protocol exposes to this library.
15
+ */
16
+ function loadKernelPrivilegeGrammars() {
17
+ if (kernelPrivilegeGrammars) {return kernelPrivilegeGrammars;}
18
+ const mod = requireFromHere(kernelDistPath('distribution', 'index.js'));
19
+ if (
20
+ typeof mod.OperationIdSchema?.safeParse !== 'function' ||
21
+ typeof mod.PlatformSweepSchema?.safeParse !== 'function' ||
22
+ typeof mod.DeclaredEventSubscriptionClauseSchema?.safeParse !== 'function' ||
23
+ typeof mod.canonicalEventSubscriptionClauses !== 'function'
24
+ ) {
25
+ throw new Error(
26
+ `Built kernel distribution module at ${kernelDistPath('distribution', 'index.js')} does not export ` +
27
+ 'OperationIdSchema, PlatformSweepSchema, DeclaredEventSubscriptionClauseSchema and canonicalEventSubscriptionClauses — the dist is stale; rebuild kernel-contracts at a commit that carries them.',
28
+ );
29
+ }
30
+ kernelPrivilegeGrammars = mod;
31
+ return mod;
32
+ }
33
+
34
+ /**
35
+ * The kernel's exact-audience and audience-namespace grammars, as ELEMENT schemas.
36
+ * The edition protocol exposes no subpath that exports the element grammars, so each is read
37
+ * from the one list schema the distribution subpath does export
38
+ * (`PlatformServiceSourceIdentitySchema.shape.requiresAudiences` / `.ownsAudienceNamespaces`):
39
+ * a value is graded as a one-element list, and the parsed element is returned.
40
+ */
41
+ function loadKernelAudienceGrammars() {
42
+ if (kernelAudienceGrammars) {return kernelAudienceGrammars;}
43
+ const mod = requireFromHere(kernelDistPath('distribution', 'index.js'));
44
+ const shape = mod.PlatformServiceSourceIdentitySchema?.shape;
45
+ const elementSchema = (listField) => {
46
+ const list = shape?.[listField]?.unwrap?.();
47
+ if (typeof list?.safeParse !== 'function') {
48
+ throw new Error(
49
+ `Built kernel distribution module at ${kernelDistPath('distribution', 'index.js')} has no PlatformServiceSourceIdentitySchema.${listField} — ` +
50
+ 'the dist is stale; rebuild kernel-contracts at a commit that carries the index-row audience fields.',
51
+ );
52
+ }
53
+ return {
54
+ safeParse: (value) => {
55
+ const parsed = list.safeParse([value]);
56
+ return parsed.success ? { success: true, data: parsed.data[0] } : parsed;
57
+ },
58
+ };
59
+ };
60
+ kernelAudienceGrammars = {
61
+ ExactAudienceSchema: elementSchema('requiresAudiences'),
62
+ AudienceNamespaceSchema: elementSchema('ownsAudienceNamespaces'),
63
+ PeerServiceNameSchema: elementSchema('optionalPeers'),
64
+ };
65
+ return kernelAudienceGrammars;
66
+ }
67
+
68
+ /**
69
+ * One component's declared privilege list, as the index carries it: sorted,
70
+ * unique, every entry graded by the kernel schema. `null` when undeclared. An
71
+ * empty list, a repeat or an invalid entry REFUSES naming the manifest: this
72
+ * reads the RAW manifest, and the kernel's component schema is not applied on
73
+ * this path.
74
+ */
75
+ export function declaredPrivilegeList(component, where, field, schema, what) {
76
+ const declared = component[field];
77
+ if (declared === undefined) {return null;}
78
+ if (!Array.isArray(declared) || declared.length === 0) {
79
+ throw new Error(
80
+ `${where} declares ${field} that is not a non-empty array of ${what}s — ` +
81
+ 'a list that names nothing states nothing, so omit the field',
82
+ );
83
+ }
84
+ const values = declared.map((value, position) => {
85
+ const parsed = schema.safeParse(value);
86
+ if (!parsed.success) {
87
+ throw new Error(
88
+ `${where} ${field}[${position}] ${JSON.stringify(value)} is not ${/^[aeiou]/.test(what) ? 'an' : 'a'} ${what}: ` +
89
+ parsed.error.issues.map((issue) => issue.message).join('; '),
90
+ );
91
+ }
92
+ return parsed.data;
93
+ });
94
+ const unique = [...new Set(values)];
95
+ if (unique.length !== values.length) {
96
+ const repeated = values.find((value, position) => values.indexOf(value) !== position);
97
+ throw new Error(`${where} ${field} names ${repeated} more than once`);
98
+ }
99
+ return unique.sort(compareStrings);
100
+ }
101
+
102
+ /**
103
+ * The service-to-service operations one workload component declares it needs
104
+ * (`requiresOperations`), graded by the kernel's one operation-id grammar.
105
+ * Declared INTENT only; the seal admits or refuses each (`admitOperationGrants`).
106
+ */
107
+ export function componentOperationRequirements(component, where) {
108
+ const { OperationIdSchema } = loadKernelPrivilegeGrammars();
109
+ return declaredPrivilegeList(component, where, 'requiresOperations', OperationIdSchema, 'operation id');
110
+ }
111
+
112
+ /**
113
+ * The operations one workload component uses only when their owner is served
114
+ * (`optionalOperations`), graded exactly like `requiresOperations`. Declared
115
+ * INTENT only: the seal grants each one whose owner is served, leaves it
116
+ * ungranted when the owner is absent, and refuses one listed in both lists
117
+ * (`admitOperationGrants`).
118
+ */
119
+ export function componentOptionalOperationRequirements(component, where) {
120
+ const { OperationIdSchema } = loadKernelPrivilegeGrammars();
121
+ return declaredPrivilegeList(component, where, 'optionalOperations', OperationIdSchema, 'operation id');
122
+ }
123
+
124
+ /**
125
+ * The operations one workload component asks to exercise for the organization
126
+ * that installs its biome (`requestsInstalledOperations`), graded by the same
127
+ * operation-id grammar. Declared INTENT only; the lock admits the ceiling of
128
+ * the request (`admitInstalledOperationCeiling`), and an installed operation is
129
+ * never a deployment grant.
130
+ */
131
+ export function componentInstalledOperationRequests(component, where) {
132
+ const { OperationIdSchema } = loadKernelPrivilegeGrammars();
133
+ return declaredPrivilegeList(component, where, 'requestsInstalledOperations', OperationIdSchema, 'operation id');
134
+ }
135
+
136
+ /**
137
+ * The organization sweeps one workload component declares it may hold
138
+ * (`requiresOrganizationSweeps`), each a member of the kernel's closed
139
+ * `PlatformSweep`. A sweep's reach is fixed by the kernel, never declared.
140
+ * Declared INTENT only; the seal admits or refuses each (`admitOrganizationSweeps`).
141
+ */
142
+ export function componentOrganizationSweepRequirements(component, where) {
143
+ const { PlatformSweepSchema } = loadKernelPrivilegeGrammars();
144
+ return declaredPrivilegeList(component, where, 'requiresOrganizationSweeps', PlatformSweepSchema, 'platform sweep');
145
+ }
146
+
147
+ /**
148
+ * The exact audiences one workload component requires (`requiresAudiences`),
149
+ * graded by the kernel's exact-audience grammar (no wildcard, no prefix).
150
+ * Declared INTENT only; `admitExactAudiences` admits or refuses each.
151
+ */
152
+ export function componentAudienceRequirements(component, where) {
153
+ const { ExactAudienceSchema } = loadKernelAudienceGrammars();
154
+ return declaredPrivilegeList(component, where, 'requiresAudiences', ExactAudienceSchema, 'exact audience');
155
+ }
156
+
157
+ /**
158
+ * The audience namespaces one workload component owns (`ownsAudienceNamespaces`),
159
+ * graded by the kernel's namespace grammar.
160
+ */
161
+ export function componentOwnedAudienceNamespaces(component, where) {
162
+ const { AudienceNamespaceSchema } = loadKernelAudienceGrammars();
163
+ return declaredPrivilegeList(component, where, 'ownsAudienceNamespaces', AudienceNamespaceSchema, 'audience namespace');
164
+ }
165
+
166
+ /**
167
+ * The peer services one platform service uses only when they are served
168
+ * (`optionalPeers`, edition protocol v38), each graded by the kernel's peer
169
+ * service name grammar. Declared INTENT only; `null` when undeclared.
170
+ */
171
+ export function componentOptionalPeers(component, where) {
172
+ const { PeerServiceNameSchema } = loadKernelAudienceGrammars();
173
+ return declaredPrivilegeList(component, where, 'optionalPeers', PeerServiceNameSchema, 'peer service name');
174
+ }
175
+
176
+ /**
177
+ * The event subscription clauses one workload component declares
178
+ * (`subscribesEvents`), each graded by the kernel's clause grammar and returned
179
+ * in the kernel's canonical order, as the lock carries them
180
+ * (`eventSubscriptionRequirements`). An empty list, a repeated clause or an
181
+ * invalid one REFUSES naming the manifest; `null` when undeclared. Declared
182
+ * INTENT only; the lock admits it (`admitEventSubscriptions`).
183
+ */
184
+ export function componentEventSubscriptionRequirements(component, where) {
185
+ const { DeclaredEventSubscriptionClauseSchema, canonicalEventSubscriptionClauses } = loadKernelPrivilegeGrammars();
186
+ const declared = component.subscribesEvents;
187
+ if (declared === undefined) {return null;}
188
+ if (!Array.isArray(declared) || declared.length === 0) {
189
+ throw new Error(
190
+ `${where} declares subscribesEvents that is not a non-empty array of event subscription clauses — ` +
191
+ 'a list that names nothing states nothing, so omit the field',
192
+ );
193
+ }
194
+ const clauses = declared.map((value, position) => {
195
+ const parsed = DeclaredEventSubscriptionClauseSchema.safeParse(value);
196
+ if (!parsed.success) {
197
+ throw new Error(
198
+ `${where} subscribesEvents[${position}] ${JSON.stringify(value)} is not an event subscription clause: ` +
199
+ parsed.error.issues.map((issue) => issue.message).join('; '),
200
+ );
201
+ }
202
+ return parsed.data;
203
+ });
204
+ const canonical = canonicalEventSubscriptionClauses(clauses);
205
+ if (canonical.length !== clauses.length) {
206
+ throw new Error(`${where} subscribesEvents lists the same clause more than once`);
207
+ }
208
+ return canonical;
209
+ }
210
+
211
+ /**
212
+ * Whether one workload component declares `relaysOrgContext`: the privilege to
213
+ * relay an organization context its caller handed it. The only legal value is
214
+ * `true` (the kernel's component grammar); absent means it does not relay.
215
+ * Declared INTENT only; the lock admits it (`admitOrgContextRelays`).
216
+ */
217
+ export function componentRelaysOrgContext(component, where) {
218
+ const declared = component.relaysOrgContext;
219
+ if (declared === undefined) {return false;}
220
+ if (declared !== true) {
221
+ throw new Error(
222
+ `${where} declares relaysOrgContext ${JSON.stringify(declared)} — the only legal value is true; omit the field to not relay`,
223
+ );
224
+ }
225
+ return true;
226
+ }
227
+
228
+ /**
229
+ * The privilege declarations a component may carry only if it is a workload
230
+ * principal in the lock.
231
+ */
232
+ export const PRINCIPAL_PRIVILEGE_FIELDS = Object.freeze([
233
+ 'requiresOperations',
234
+ 'optionalOperations',
235
+ 'requestsInstalledOperations',
236
+ 'relaysOrgContext',
237
+ 'subscribesEvents',
238
+ 'requiresOrganizationSweeps',
239
+ 'requiresAudiences',
240
+ 'ownsAudienceNamespaces',
241
+ ]);
242
+
243
+ /**
244
+ * Thrown when a component that has no workload principal in the lock declares
245
+ * a privilege. A principal is a rostered image component: a service (under its
246
+ * `protocol.serviceName`) or a worker (under its key). A job, an imageless
247
+ * component or a content/web/adapter component is never one, so its
248
+ * declaration could never be admitted; it is refused rather than dropped.
249
+ */
250
+ export class PrivilegeWithoutPrincipalError extends Error {
251
+ /**
252
+ * @param {string} manifest the manifest path
253
+ * @param {string} component the component key
254
+ * @param {string} field the privilege field it declares
255
+ */
256
+ constructor(manifest, component, field) {
257
+ super(
258
+ `${manifest}: xema.components[] "${component}" declares ${field}, but it has no ` +
259
+ 'workload principal in the lock (only a service or worker image component is one), ' +
260
+ 'so the declaration could never be admitted',
261
+ );
262
+ this.name = 'PrivilegeWithoutPrincipalError';
263
+ this.manifest = manifest;
264
+ this.component = component;
265
+ this.field = field;
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Refuse every privilege declaration on a component outside `principals`
271
+ * (the components that become lock workloads). Never drops one silently.
272
+ */
273
+ export function assertPrivilegesHavePrincipals(components, principals, manifest) {
274
+ for (const component of components) {
275
+ if (component === null || typeof component !== 'object' || principals.includes(component)) {continue;}
276
+ for (const field of PRINCIPAL_PRIVILEGE_FIELDS) {
277
+ if (component[field] !== undefined) {
278
+ throw new PrivilegeWithoutPrincipalError(manifest, String(component.key ?? component.name ?? '?'), field);
279
+ }
280
+ }
281
+ }
282
+ }
@@ -0,0 +1,35 @@
1
+ // oras-process — run the oras binary WITHOUT blocking the event loop.
2
+ //
3
+ // A blocking `execFileSync` turns every caller's worker pool into a queue of
4
+ // one: six "concurrent" subjects still ran their registry calls one after the
5
+ // other. This runner spawns the child and resolves on its exit, so the pool's
6
+ // width is the real concurrency.
7
+
8
+ import { execFile } from 'node:child_process';
9
+
10
+ /**
11
+ * Run `file` with `args` (in `cwd` when given); resolve with stdout bytes. A failure rejects with the
12
+ * child's stderr text on `error.stderr` (a string), so the caller reads the
13
+ * registry status from what oras itself wrote.
14
+ */
15
+ export function runExecutable(file, args, { input, env, cwd, timeoutMs, maxBufferBytes }) {
16
+ return new Promise((resolve, reject) => {
17
+ const child = execFile(
18
+ file,
19
+ args,
20
+ { env, ...(cwd === undefined ? {} : { cwd }), timeout: timeoutMs, maxBuffer: maxBufferBytes, encoding: 'buffer' },
21
+ (error, stdout, stderr) => {
22
+ if (error) {
23
+ error.stderr = stderr.toString('utf8');
24
+ reject(error);
25
+ return;
26
+ }
27
+ resolve(stdout);
28
+ },
29
+ );
30
+ // A child that exits without reading its stdin must not crash the process
31
+ // with EPIPE; the exit status already carries the failure.
32
+ child.stdin.on('error', () => {});
33
+ child.stdin.end(input);
34
+ });
35
+ }