@mannyc1/ts-release 0.0.6 → 0.2.2

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 (639) hide show
  1. package/ARCHITECTURE.md +124 -87
  2. package/CHANGELOG.md +115 -0
  3. package/LICENSE +21 -0
  4. package/README.md +272 -540
  5. package/SPEC.md +206 -305
  6. package/apps/release-ts/README.md +32 -0
  7. package/apps/release-ts/src/cli/command.ts +164 -0
  8. package/apps/release-ts/src/cli/commands.ts +351 -0
  9. package/apps/release-ts/src/cli/main.ts +43 -0
  10. package/apps/release-ts/src/cli/node-main.ts +49 -0
  11. package/apps/ts-release-agents/LICENSE +21 -0
  12. package/apps/ts-release-agents/README.md +21 -0
  13. package/apps/ts-release-agents/evals/cases.json +11 -0
  14. package/apps/ts-release-agents/manifests/claude.json +5 -0
  15. package/apps/ts-release-agents/manifests/codex.json +5 -0
  16. package/apps/ts-release-agents/package.json +11 -0
  17. package/apps/ts-release-agents/skills/release/SKILL.md +48 -0
  18. package/apps/ts-release-agents/skills/release/references/configuration.md +18 -0
  19. package/apps/ts-release-agents/skills/release/references/recovery.md +10 -0
  20. package/apps/ts-release-agents/skills/release/references/staged-workflow.md +12 -0
  21. package/apps/ts-release-agents/skills/release/references/target-selection.md +13 -0
  22. package/apps/ts-release-agents/skills/release/references/verification.md +14 -0
  23. package/apps/ts-release-agents/src/build.ts +105 -0
  24. package/apps/ts-release-agents/src/check.ts +117 -0
  25. package/apps/ts-release-agents/src/install.ts +121 -0
  26. package/apps/ts-release-agents/test/contract.test.ts +56 -0
  27. package/apps/ts-release-agents/tsconfig.json +5 -0
  28. package/dist/api/api.d.ts +15 -0
  29. package/dist/api/api.d.ts.map +1 -0
  30. package/dist/api/api.js +244 -0
  31. package/dist/api/api.js.map +1 -0
  32. package/dist/api/errors.d.ts +39 -0
  33. package/dist/api/errors.d.ts.map +1 -0
  34. package/dist/api/errors.js +32 -0
  35. package/dist/api/errors.js.map +1 -0
  36. package/dist/api/input.d.ts +10 -0
  37. package/dist/api/input.d.ts.map +1 -0
  38. package/dist/api/input.js +73 -0
  39. package/dist/api/input.js.map +1 -0
  40. package/dist/api/runtime.d.ts +18 -0
  41. package/dist/api/runtime.d.ts.map +1 -0
  42. package/dist/api/runtime.js +7 -0
  43. package/dist/api/runtime.js.map +1 -0
  44. package/dist/api/types.d.ts +76 -0
  45. package/dist/api/types.d.ts.map +1 -0
  46. package/dist/api/types.js +17 -0
  47. package/dist/api/types.js.map +1 -0
  48. package/dist/bin/ts-release.js +39239 -0
  49. package/dist/capabilities/bun-targets.d.ts +3 -0
  50. package/dist/capabilities/bun-targets.d.ts.map +1 -0
  51. package/dist/capabilities/bun-targets.js +3 -0
  52. package/dist/capabilities/bun-targets.js.map +1 -0
  53. package/dist/capabilities/field-ownership.d.ts +17 -0
  54. package/dist/capabilities/field-ownership.d.ts.map +1 -0
  55. package/dist/capabilities/field-ownership.js +40 -0
  56. package/dist/capabilities/field-ownership.js.map +1 -0
  57. package/dist/capabilities/module.d.ts +91 -0
  58. package/dist/capabilities/module.d.ts.map +1 -0
  59. package/dist/capabilities/module.js +2 -0
  60. package/dist/capabilities/module.js.map +1 -0
  61. package/dist/capabilities/registry.d.ts +829 -0
  62. package/dist/capabilities/registry.d.ts.map +1 -0
  63. package/dist/capabilities/registry.js +291 -0
  64. package/dist/capabilities/registry.js.map +1 -0
  65. package/dist/config/config.d.ts +6 -0
  66. package/dist/config/config.d.ts.map +1 -0
  67. package/dist/config/config.js +42 -0
  68. package/dist/config/config.js.map +1 -0
  69. package/dist/correction/coordinator.d.ts +248 -0
  70. package/dist/correction/coordinator.d.ts.map +1 -0
  71. package/dist/correction/coordinator.js +274 -0
  72. package/dist/correction/coordinator.js.map +1 -0
  73. package/dist/correction/index.d.ts +3 -0
  74. package/dist/correction/index.d.ts.map +1 -0
  75. package/dist/correction/index.js +3 -0
  76. package/dist/correction/index.js.map +1 -0
  77. package/dist/correction/intent.d.ts +111 -0
  78. package/dist/correction/intent.d.ts.map +1 -0
  79. package/dist/correction/intent.js +146 -0
  80. package/dist/correction/intent.js.map +1 -0
  81. package/dist/drivers/archive.d.ts +8 -0
  82. package/dist/drivers/archive.d.ts.map +1 -0
  83. package/dist/drivers/archive.js +101 -0
  84. package/dist/drivers/archive.js.map +1 -0
  85. package/dist/drivers/contain.d.ts +6 -0
  86. package/dist/drivers/contain.d.ts.map +1 -0
  87. package/dist/drivers/contain.js +17 -0
  88. package/dist/drivers/contain.js.map +1 -0
  89. package/dist/drivers/environment.d.ts +4 -0
  90. package/dist/drivers/environment.d.ts.map +1 -0
  91. package/dist/drivers/environment.js +18 -0
  92. package/dist/drivers/environment.js.map +1 -0
  93. package/dist/drivers/errors.d.ts +9 -0
  94. package/dist/drivers/errors.d.ts.map +1 -0
  95. package/dist/drivers/errors.js +6 -0
  96. package/dist/drivers/errors.js.map +1 -0
  97. package/dist/drivers/glob.d.ts +2 -0
  98. package/dist/drivers/glob.d.ts.map +1 -0
  99. package/dist/drivers/glob.js +162 -0
  100. package/dist/drivers/glob.js.map +1 -0
  101. package/dist/drivers/process.d.ts +48 -0
  102. package/dist/drivers/process.d.ts.map +1 -0
  103. package/dist/drivers/process.js +257 -0
  104. package/dist/drivers/process.js.map +1 -0
  105. package/dist/drivers/redact.d.ts +4 -0
  106. package/dist/drivers/redact.d.ts.map +1 -0
  107. package/dist/drivers/redact.js +24 -0
  108. package/dist/drivers/redact.js.map +1 -0
  109. package/dist/drivers/seccomp-helper-source.d.ts +7 -0
  110. package/dist/drivers/seccomp-helper-source.d.ts.map +1 -0
  111. package/dist/drivers/seccomp-helper-source.js +121 -0
  112. package/dist/drivers/seccomp-helper-source.js.map +1 -0
  113. package/dist/drivers/utils.d.ts +4 -0
  114. package/dist/drivers/utils.d.ts.map +1 -0
  115. package/dist/drivers/utils.js +5 -0
  116. package/dist/drivers/utils.js.map +1 -0
  117. package/dist/drivers/workspace.d.ts +6 -0
  118. package/dist/drivers/workspace.d.ts.map +1 -0
  119. package/dist/drivers/workspace.js +49 -0
  120. package/dist/drivers/workspace.js.map +1 -0
  121. package/dist/extensions/provider-adapter.d.ts +48 -0
  122. package/dist/extensions/provider-adapter.d.ts.map +1 -0
  123. package/dist/extensions/provider-adapter.js +64 -0
  124. package/dist/extensions/provider-adapter.js.map +1 -0
  125. package/dist/host.d.ts +33 -0
  126. package/dist/host.d.ts.map +1 -0
  127. package/dist/host.js +20 -0
  128. package/dist/host.js.map +1 -0
  129. package/dist/index.d.ts +19 -1
  130. package/dist/index.d.ts.map +1 -1
  131. package/dist/index.js +12 -1
  132. package/dist/index.js.map +1 -1
  133. package/dist/model/artifact-collection.d.ts +84 -0
  134. package/dist/model/artifact-collection.d.ts.map +1 -0
  135. package/dist/model/artifact-collection.js +185 -0
  136. package/dist/model/artifact-collection.js.map +1 -0
  137. package/dist/model/authority.d.ts +89 -0
  138. package/dist/model/authority.d.ts.map +1 -0
  139. package/dist/model/authority.js +80 -0
  140. package/dist/model/authority.js.map +1 -0
  141. package/dist/model/bun-targets.d.ts +34 -0
  142. package/dist/model/bun-targets.d.ts.map +1 -0
  143. package/dist/model/bun-targets.js +17 -0
  144. package/dist/model/bun-targets.js.map +1 -0
  145. package/dist/model/canonical.d.ts +8 -0
  146. package/dist/model/canonical.d.ts.map +1 -0
  147. package/dist/model/canonical.js +205 -0
  148. package/dist/model/canonical.js.map +1 -0
  149. package/dist/model/catalog.d.ts +76 -0
  150. package/dist/model/catalog.d.ts.map +1 -0
  151. package/dist/model/catalog.js +174 -0
  152. package/dist/model/catalog.js.map +1 -0
  153. package/dist/model/digest.d.ts +53 -0
  154. package/dist/model/digest.d.ts.map +1 -0
  155. package/dist/model/digest.js +127 -0
  156. package/dist/model/digest.js.map +1 -0
  157. package/dist/model/errors.d.ts +14 -0
  158. package/dist/model/errors.d.ts.map +1 -0
  159. package/dist/model/errors.js +8 -0
  160. package/dist/model/errors.js.map +1 -0
  161. package/dist/model/primitives.d.ts +19 -0
  162. package/dist/model/primitives.d.ts.map +1 -0
  163. package/dist/model/primitives.js +18 -0
  164. package/dist/model/primitives.js.map +1 -0
  165. package/dist/model/pypi.d.ts +12 -0
  166. package/dist/model/pypi.d.ts.map +1 -0
  167. package/dist/model/pypi.js +20 -0
  168. package/dist/model/pypi.js.map +1 -0
  169. package/dist/model/python-distribution.d.ts +33 -0
  170. package/dist/model/python-distribution.d.ts.map +1 -0
  171. package/dist/model/python-distribution.js +367 -0
  172. package/dist/model/python-distribution.js.map +1 -0
  173. package/dist/model/secret-patterns.d.ts +2 -0
  174. package/dist/model/secret-patterns.d.ts.map +1 -0
  175. package/dist/model/secret-patterns.js +12 -0
  176. package/dist/model/secret-patterns.js.map +1 -0
  177. package/dist/platform/bun.d.ts +7 -0
  178. package/dist/platform/bun.d.ts.map +1 -0
  179. package/dist/platform/bun.js +15 -0
  180. package/dist/platform/bun.js.map +1 -0
  181. package/dist/platform/credentials.d.ts +32 -0
  182. package/dist/platform/credentials.d.ts.map +1 -0
  183. package/dist/platform/credentials.js +772 -0
  184. package/dist/platform/credentials.js.map +1 -0
  185. package/dist/platform/host-support.d.ts +2 -0
  186. package/dist/platform/host-support.d.ts.map +1 -0
  187. package/dist/platform/host-support.js +4 -0
  188. package/dist/platform/host-support.js.map +1 -0
  189. package/dist/platform/node.d.ts +7 -0
  190. package/dist/platform/node.d.ts.map +1 -0
  191. package/dist/platform/node.js +15 -0
  192. package/dist/platform/node.js.map +1 -0
  193. package/dist/platform/release-runtime.d.ts +11 -0
  194. package/dist/platform/release-runtime.d.ts.map +1 -0
  195. package/dist/platform/release-runtime.js +34 -0
  196. package/dist/platform/release-runtime.js.map +1 -0
  197. package/dist/platform/services.d.ts +15 -0
  198. package/dist/platform/services.d.ts.map +1 -0
  199. package/dist/platform/services.js +30 -0
  200. package/dist/platform/services.js.map +1 -0
  201. package/dist/platform/source-observer.d.ts +5 -0
  202. package/dist/platform/source-observer.d.ts.map +1 -0
  203. package/dist/platform/source-observer.js +159 -0
  204. package/dist/platform/source-observer.js.map +1 -0
  205. package/dist/provider-sdk.d.ts +14 -0
  206. package/dist/provider-sdk.d.ts.map +1 -0
  207. package/dist/provider-sdk.js +10 -0
  208. package/dist/provider-sdk.js.map +1 -0
  209. package/dist/publication/adapter.d.ts +212 -0
  210. package/dist/publication/adapter.d.ts.map +1 -0
  211. package/dist/publication/adapter.js +83 -0
  212. package/dist/publication/adapter.js.map +1 -0
  213. package/dist/publication/authority.d.ts +137 -0
  214. package/dist/publication/authority.d.ts.map +1 -0
  215. package/dist/publication/authority.js +233 -0
  216. package/dist/publication/authority.js.map +1 -0
  217. package/dist/publication/catalog-git.d.ts +37 -0
  218. package/dist/publication/catalog-git.d.ts.map +1 -0
  219. package/dist/publication/catalog-git.js +451 -0
  220. package/dist/publication/catalog-git.js.map +1 -0
  221. package/dist/publication/claim.d.ts +39 -0
  222. package/dist/publication/claim.d.ts.map +1 -0
  223. package/dist/publication/claim.js +31 -0
  224. package/dist/publication/claim.js.map +1 -0
  225. package/dist/publication/coordinator.d.ts +246 -0
  226. package/dist/publication/coordinator.d.ts.map +1 -0
  227. package/dist/publication/coordinator.js +370 -0
  228. package/dist/publication/coordinator.js.map +1 -0
  229. package/dist/publication/github.d.ts +36 -0
  230. package/dist/publication/github.d.ts.map +1 -0
  231. package/dist/publication/github.js +644 -0
  232. package/dist/publication/github.js.map +1 -0
  233. package/dist/publication/http.d.ts +69 -0
  234. package/dist/publication/http.d.ts.map +1 -0
  235. package/dist/publication/http.js +29 -0
  236. package/dist/publication/http.js.map +1 -0
  237. package/dist/publication/index.d.ts +5 -0
  238. package/dist/publication/index.d.ts.map +1 -0
  239. package/dist/publication/index.js +5 -0
  240. package/dist/publication/index.js.map +1 -0
  241. package/dist/publication/npm.d.ts +40 -0
  242. package/dist/publication/npm.d.ts.map +1 -0
  243. package/dist/publication/npm.js +359 -0
  244. package/dist/publication/npm.js.map +1 -0
  245. package/dist/publication/profiles.d.ts +136 -0
  246. package/dist/publication/profiles.d.ts.map +1 -0
  247. package/dist/publication/profiles.js +90 -0
  248. package/dist/publication/profiles.js.map +1 -0
  249. package/dist/publication/publisher.d.ts +82 -0
  250. package/dist/publication/publisher.d.ts.map +1 -0
  251. package/dist/publication/publisher.js +53 -0
  252. package/dist/publication/publisher.js.map +1 -0
  253. package/dist/publication/pypi.d.ts +45 -0
  254. package/dist/publication/pypi.d.ts.map +1 -0
  255. package/dist/publication/pypi.js +369 -0
  256. package/dist/publication/pypi.js.map +1 -0
  257. package/dist/publication/recovery.d.ts +663 -0
  258. package/dist/publication/recovery.d.ts.map +1 -0
  259. package/dist/publication/recovery.js +252 -0
  260. package/dist/publication/recovery.js.map +1 -0
  261. package/dist/publication/report.d.ts +1061 -0
  262. package/dist/publication/report.d.ts.map +1 -0
  263. package/dist/publication/report.js +441 -0
  264. package/dist/publication/report.js.map +1 -0
  265. package/dist/recipes/config.d.ts +296 -0
  266. package/dist/recipes/config.d.ts.map +1 -0
  267. package/dist/recipes/config.js +325 -0
  268. package/dist/recipes/config.js.map +1 -0
  269. package/dist/release/capabilities.d.ts +12 -0
  270. package/dist/release/capabilities.d.ts.map +1 -0
  271. package/dist/release/capabilities.js +395 -0
  272. package/dist/release/capabilities.js.map +1 -0
  273. package/dist/release/compiler.d.ts +5 -0
  274. package/dist/release/compiler.d.ts.map +1 -0
  275. package/dist/release/compiler.js +19 -0
  276. package/dist/release/compiler.js.map +1 -0
  277. package/dist/release/config.d.ts +5 -0
  278. package/dist/release/config.d.ts.map +1 -0
  279. package/dist/release/config.js +10 -0
  280. package/dist/release/config.js.map +1 -0
  281. package/dist/release/context.d.ts +96 -0
  282. package/dist/release/context.d.ts.map +1 -0
  283. package/dist/release/context.js +168 -0
  284. package/dist/release/context.js.map +1 -0
  285. package/dist/release/graph.d.ts +271 -0
  286. package/dist/release/graph.d.ts.map +1 -0
  287. package/dist/release/graph.js +766 -0
  288. package/dist/release/graph.js.map +1 -0
  289. package/dist/release/inspect.d.ts +88 -0
  290. package/dist/release/inspect.d.ts.map +1 -0
  291. package/dist/release/inspect.js +75 -0
  292. package/dist/release/inspect.js.map +1 -0
  293. package/dist/release/prepare.d.ts +24 -0
  294. package/dist/release/prepare.d.ts.map +1 -0
  295. package/dist/release/prepare.js +1116 -0
  296. package/dist/release/prepare.js.map +1 -0
  297. package/dist/release/prepared-ref.d.ts +60 -0
  298. package/dist/release/prepared-ref.d.ts.map +1 -0
  299. package/dist/release/prepared-ref.js +131 -0
  300. package/dist/release/prepared-ref.js.map +1 -0
  301. package/dist/release/prepared-store.d.ts +90 -0
  302. package/dist/release/prepared-store.d.ts.map +1 -0
  303. package/dist/release/prepared-store.js +337 -0
  304. package/dist/release/prepared-store.js.map +1 -0
  305. package/dist/release/prepared.d.ts +172 -0
  306. package/dist/release/prepared.d.ts.map +1 -0
  307. package/dist/release/prepared.js +268 -0
  308. package/dist/release/prepared.js.map +1 -0
  309. package/dist/release/staging.d.ts +31 -0
  310. package/dist/release/staging.d.ts.map +1 -0
  311. package/dist/release/staging.js +218 -0
  312. package/dist/release/staging.js.map +1 -0
  313. package/dist/resolve/authored.d.ts +53 -0
  314. package/dist/resolve/authored.d.ts.map +1 -0
  315. package/dist/resolve/authored.js +55 -0
  316. package/dist/resolve/authored.js.map +1 -0
  317. package/dist/resolve/encode.d.ts +3 -0
  318. package/dist/resolve/encode.d.ts.map +1 -0
  319. package/dist/resolve/encode.js +21 -0
  320. package/dist/resolve/encode.js.map +1 -0
  321. package/dist/resolve/errors.d.ts +7 -0
  322. package/dist/resolve/errors.d.ts.map +1 -0
  323. package/dist/resolve/errors.js +17 -0
  324. package/dist/resolve/errors.js.map +1 -0
  325. package/dist/resolve/facts.d.ts +12 -0
  326. package/dist/resolve/facts.d.ts.map +1 -0
  327. package/dist/resolve/facts.js +18 -0
  328. package/dist/resolve/facts.js.map +1 -0
  329. package/dist/resolve/resolve.d.ts +10 -0
  330. package/dist/resolve/resolve.d.ts.map +1 -0
  331. package/dist/resolve/resolve.js +242 -0
  332. package/dist/resolve/resolve.js.map +1 -0
  333. package/dist/store.d.ts +11 -0
  334. package/dist/store.d.ts.map +1 -0
  335. package/dist/store.js +9 -0
  336. package/dist/store.js.map +1 -0
  337. package/examples/README.md +20 -30
  338. package/examples/github-release/release.config.json +11 -18
  339. package/examples/homebrew-tap/README.md +11 -0
  340. package/examples/homebrew-tap/release.config.json +40 -28
  341. package/examples/multi-target/release.config.json +28 -53
  342. package/examples/npm-first-publish/release.config.json +23 -25
  343. package/examples/npm-only/release.config.json +22 -30
  344. package/examples/portable-cli/README.md +17 -0
  345. package/examples/portable-cli/artifacts/portable-example-0.1.0-darwin-arm64 +1 -0
  346. package/examples/portable-cli/artifacts/portable-example-0.1.0-darwin-x64 +1 -0
  347. package/examples/portable-cli/artifacts/portable-example-0.1.0-linux-arm64 +1 -0
  348. package/examples/portable-cli/artifacts/portable-example-0.1.0-linux-x64 +1 -0
  349. package/examples/portable-cli/artifacts/portable-example-0.1.0-windows-x64.exe +1 -0
  350. package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-macosx_10_15_x86_64.whl +1 -0
  351. package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-macosx_11_0_arm64.whl +1 -0
  352. package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-manylinux2014_aarch64.whl +1 -0
  353. package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-manylinux2014_x86_64.whl +1 -0
  354. package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-win_amd64.whl +1 -0
  355. package/examples/portable-cli/package.json +9 -0
  356. package/examples/portable-cli/release.config.json +53 -0
  357. package/examples/portable-cli/src/cli.ts +3 -0
  358. package/examples/pypi-registry/README.md +8 -0
  359. package/examples/scoop-bucket/README.md +10 -0
  360. package/examples/scoop-bucket/release.config.json +40 -30
  361. package/package.json +61 -154
  362. package/src/api/api.ts +365 -0
  363. package/src/api/errors.ts +36 -0
  364. package/src/api/input.ts +92 -0
  365. package/src/api/runtime.ts +14 -0
  366. package/src/api/types.ts +103 -0
  367. package/src/capabilities/bun-targets.ts +2 -0
  368. package/src/capabilities/field-ownership.ts +60 -0
  369. package/src/capabilities/module.ts +158 -0
  370. package/src/capabilities/registry.ts +333 -0
  371. package/src/config/config.ts +48 -0
  372. package/src/correction/coordinator.ts +336 -0
  373. package/src/correction/index.ts +2 -0
  374. package/src/correction/intent.ts +172 -0
  375. package/src/drivers/archive.ts +102 -0
  376. package/src/drivers/contain.ts +20 -0
  377. package/src/drivers/environment.ts +28 -0
  378. package/src/drivers/errors.ts +6 -0
  379. package/src/drivers/glob.ts +137 -0
  380. package/src/drivers/process.ts +340 -0
  381. package/src/drivers/redact.ts +31 -0
  382. package/src/drivers/seccomp-helper-source.ts +121 -0
  383. package/src/drivers/utils.ts +10 -0
  384. package/src/drivers/workspace.ts +40 -0
  385. package/src/extensions/provider-adapter.ts +99 -0
  386. package/src/host.ts +140 -0
  387. package/src/index.ts +64 -0
  388. package/src/model/artifact-collection.ts +237 -0
  389. package/src/model/authority.ts +116 -0
  390. package/src/model/bun-targets.ts +34 -0
  391. package/src/model/canonical.ts +205 -0
  392. package/src/model/catalog.ts +220 -0
  393. package/src/model/digest.ts +172 -0
  394. package/src/model/errors.ts +6 -0
  395. package/src/model/primitives.ts +37 -0
  396. package/src/model/pypi.ts +30 -0
  397. package/src/model/python-distribution.ts +360 -0
  398. package/src/model/secret-patterns.ts +11 -0
  399. package/src/platform/bun.ts +38 -0
  400. package/src/platform/credentials.ts +1083 -0
  401. package/src/platform/host-support.ts +4 -0
  402. package/src/platform/node.ts +38 -0
  403. package/src/platform/release-runtime.ts +47 -0
  404. package/src/platform/services.ts +57 -0
  405. package/src/platform/source-observer.ts +193 -0
  406. package/src/provider-sdk.ts +48 -0
  407. package/src/publication/adapter.ts +132 -0
  408. package/src/publication/authority.ts +397 -0
  409. package/src/publication/catalog-git.ts +603 -0
  410. package/src/publication/claim.ts +51 -0
  411. package/src/publication/coordinator.ts +578 -0
  412. package/src/publication/github.ts +954 -0
  413. package/src/publication/http.ts +101 -0
  414. package/src/publication/index.ts +4 -0
  415. package/src/publication/npm.ts +525 -0
  416. package/src/publication/profiles.ts +94 -0
  417. package/src/publication/publisher.ts +130 -0
  418. package/src/publication/pypi.ts +463 -0
  419. package/src/publication/recovery.ts +349 -0
  420. package/src/publication/report.ts +628 -0
  421. package/src/recipes/config.ts +395 -0
  422. package/src/release/capabilities.ts +459 -0
  423. package/src/release/compiler.ts +28 -0
  424. package/src/release/config.ts +12 -0
  425. package/src/release/context.ts +223 -0
  426. package/src/release/graph.ts +866 -0
  427. package/src/release/inspect.ts +83 -0
  428. package/src/release/prepare.ts +1331 -0
  429. package/src/release/prepared-ref.ts +193 -0
  430. package/src/release/prepared-store.ts +377 -0
  431. package/src/release/prepared.ts +299 -0
  432. package/src/release/staging.ts +257 -0
  433. package/src/resolve/authored.ts +64 -0
  434. package/src/resolve/encode.ts +22 -0
  435. package/src/resolve/errors.ts +15 -0
  436. package/src/resolve/facts.ts +18 -0
  437. package/src/resolve/resolve.ts +328 -0
  438. package/src/store.ts +28 -0
  439. package/templates/README.md +55 -59
  440. package/templates/bun-cli-github/README.md +12 -0
  441. package/templates/bun-cli-github/release.config.json +51 -0
  442. package/templates/github-actions/release.yml +57 -0
  443. package/templates/github-actions/reviewed-release.yml +80 -0
  444. package/templates/multi-target-homebrew/README.md +10 -0
  445. package/templates/multi-target-homebrew/release.config.json +43 -60
  446. package/templates/multi-target-scoop/README.md +10 -0
  447. package/templates/multi-target-scoop/release.config.json +43 -62
  448. package/templates/npm-github/release.config.json +25 -38
  449. package/templates/npm-github/reviewed-release.config.json +37 -0
  450. package/templates/npm-only/README.md +10 -0
  451. package/templates/npm-only/release.config.json +15 -30
  452. package/templates/portable-cli/README.md +13 -0
  453. package/templates/portable-cli/release.config.json +53 -0
  454. package/dist/config/errors.d.ts +0 -24
  455. package/dist/config/errors.d.ts.map +0 -1
  456. package/dist/config/errors.js +0 -19
  457. package/dist/config/errors.js.map +0 -1
  458. package/dist/config/load.d.ts +0 -9
  459. package/dist/config/load.d.ts.map +0 -1
  460. package/dist/config/load.js +0 -29
  461. package/dist/config/load.js.map +0 -1
  462. package/dist/config/schema.d.ts +0 -12
  463. package/dist/config/schema.d.ts.map +0 -1
  464. package/dist/config/schema.js +0 -19
  465. package/dist/config/schema.js.map +0 -1
  466. package/dist/domain/artifact.d.ts +0 -36
  467. package/dist/domain/artifact.d.ts.map +0 -1
  468. package/dist/domain/artifact.js +0 -29
  469. package/dist/domain/artifact.js.map +0 -1
  470. package/dist/domain/evidence.d.ts +0 -108
  471. package/dist/domain/evidence.d.ts.map +0 -1
  472. package/dist/domain/evidence.js +0 -96
  473. package/dist/domain/evidence.js.map +0 -1
  474. package/dist/domain/operation.d.ts +0 -153
  475. package/dist/domain/operation.d.ts.map +0 -1
  476. package/dist/domain/operation.js +0 -320
  477. package/dist/domain/operation.js.map +0 -1
  478. package/dist/domain/release.d.ts +0 -141
  479. package/dist/domain/release.d.ts.map +0 -1
  480. package/dist/domain/release.js +0 -127
  481. package/dist/domain/release.js.map +0 -1
  482. package/dist/domain/remote-state.d.ts +0 -85
  483. package/dist/domain/remote-state.d.ts.map +0 -1
  484. package/dist/domain/remote-state.js +0 -77
  485. package/dist/domain/remote-state.js.map +0 -1
  486. package/dist/domain/status.d.ts +0 -43
  487. package/dist/domain/status.d.ts.map +0 -1
  488. package/dist/domain/status.js +0 -51
  489. package/dist/domain/status.js.map +0 -1
  490. package/dist/domain/target.d.ts +0 -135
  491. package/dist/domain/target.d.ts.map +0 -1
  492. package/dist/domain/target.js +0 -137
  493. package/dist/domain/target.js.map +0 -1
  494. package/dist/host/host.d.ts +0 -32
  495. package/dist/host/host.d.ts.map +0 -1
  496. package/dist/host/host.js +0 -24
  497. package/dist/host/host.js.map +0 -1
  498. package/dist/host/http-live.d.ts +0 -6
  499. package/dist/host/http-live.d.ts.map +0 -1
  500. package/dist/host/http-live.js +0 -86
  501. package/dist/host/http-live.js.map +0 -1
  502. package/dist/host/http.d.ts +0 -43
  503. package/dist/host/http.d.ts.map +0 -1
  504. package/dist/host/http.js +0 -59
  505. package/dist/host/http.js.map +0 -1
  506. package/dist/host/platform.d.ts +0 -10
  507. package/dist/host/platform.d.ts.map +0 -1
  508. package/dist/host/platform.js +0 -114
  509. package/dist/host/platform.js.map +0 -1
  510. package/dist/host/test.d.ts +0 -23
  511. package/dist/host/test.d.ts.map +0 -1
  512. package/dist/host/test.js +0 -222
  513. package/dist/host/test.js.map +0 -1
  514. package/dist/internal/workflow-phases.d.ts +0 -3
  515. package/dist/internal/workflow-phases.d.ts.map +0 -1
  516. package/dist/internal/workflow-phases.js +0 -2
  517. package/dist/internal/workflow-phases.js.map +0 -1
  518. package/dist/internal/workspace-path.d.ts +0 -15
  519. package/dist/internal/workspace-path.d.ts.map +0 -1
  520. package/dist/internal/workspace-path.js +0 -36
  521. package/dist/internal/workspace-path.js.map +0 -1
  522. package/dist/planner/artifact-inventory.d.ts +0 -9
  523. package/dist/planner/artifact-inventory.d.ts.map +0 -1
  524. package/dist/planner/artifact-inventory.js +0 -84
  525. package/dist/planner/artifact-inventory.js.map +0 -1
  526. package/dist/planner/create-release-plan.d.ts +0 -5
  527. package/dist/planner/create-release-plan.d.ts.map +0 -1
  528. package/dist/planner/create-release-plan.js +0 -25
  529. package/dist/planner/create-release-plan.js.map +0 -1
  530. package/dist/planner/errors.d.ts +0 -81
  531. package/dist/planner/errors.d.ts.map +0 -1
  532. package/dist/planner/errors.js +0 -70
  533. package/dist/planner/errors.js.map +0 -1
  534. package/dist/planner/evidence-recorder.d.ts +0 -26
  535. package/dist/planner/evidence-recorder.d.ts.map +0 -1
  536. package/dist/planner/evidence-recorder.js +0 -330
  537. package/dist/planner/evidence-recorder.js.map +0 -1
  538. package/dist/planner/executor.d.ts +0 -58
  539. package/dist/planner/executor.d.ts.map +0 -1
  540. package/dist/planner/executor.js +0 -159
  541. package/dist/planner/executor.js.map +0 -1
  542. package/dist/planner/normalize-release.d.ts +0 -16
  543. package/dist/planner/normalize-release.d.ts.map +0 -1
  544. package/dist/planner/normalize-release.js +0 -218
  545. package/dist/planner/normalize-release.js.map +0 -1
  546. package/dist/planner/reconcile.d.ts +0 -17
  547. package/dist/planner/reconcile.d.ts.map +0 -1
  548. package/dist/planner/reconcile.js +0 -261
  549. package/dist/planner/reconcile.js.map +0 -1
  550. package/dist/planner/release-eligibility.d.ts +0 -39
  551. package/dist/planner/release-eligibility.d.ts.map +0 -1
  552. package/dist/planner/release-eligibility.js +0 -578
  553. package/dist/planner/release-eligibility.js.map +0 -1
  554. package/dist/planner/render-plan.d.ts +0 -15
  555. package/dist/planner/render-plan.d.ts.map +0 -1
  556. package/dist/planner/render-plan.js +0 -238
  557. package/dist/planner/render-plan.js.map +0 -1
  558. package/dist/planner/status.d.ts +0 -40
  559. package/dist/planner/status.d.ts.map +0 -1
  560. package/dist/planner/status.js +0 -313
  561. package/dist/planner/status.js.map +0 -1
  562. package/dist/targets/adapter-helpers.d.ts +0 -64
  563. package/dist/targets/adapter-helpers.d.ts.map +0 -1
  564. package/dist/targets/adapter-helpers.js +0 -124
  565. package/dist/targets/adapter-helpers.js.map +0 -1
  566. package/dist/targets/adapter.d.ts +0 -17
  567. package/dist/targets/adapter.d.ts.map +0 -1
  568. package/dist/targets/adapter.js +0 -2
  569. package/dist/targets/adapter.js.map +0 -1
  570. package/dist/targets/github-release.d.ts +0 -25
  571. package/dist/targets/github-release.d.ts.map +0 -1
  572. package/dist/targets/github-release.js +0 -82
  573. package/dist/targets/github-release.js.map +0 -1
  574. package/dist/targets/github.d.ts +0 -11
  575. package/dist/targets/github.d.ts.map +0 -1
  576. package/dist/targets/github.js +0 -125
  577. package/dist/targets/github.js.map +0 -1
  578. package/dist/targets/homebrew.d.ts +0 -11
  579. package/dist/targets/homebrew.d.ts.map +0 -1
  580. package/dist/targets/homebrew.js +0 -97
  581. package/dist/targets/homebrew.js.map +0 -1
  582. package/dist/targets/live.d.ts +0 -5
  583. package/dist/targets/live.d.ts.map +0 -1
  584. package/dist/targets/live.js +0 -50
  585. package/dist/targets/live.js.map +0 -1
  586. package/dist/targets/npm.d.ts +0 -10
  587. package/dist/targets/npm.d.ts.map +0 -1
  588. package/dist/targets/npm.js +0 -118
  589. package/dist/targets/npm.js.map +0 -1
  590. package/dist/targets/pypi.d.ts +0 -11
  591. package/dist/targets/pypi.d.ts.map +0 -1
  592. package/dist/targets/pypi.js +0 -103
  593. package/dist/targets/pypi.js.map +0 -1
  594. package/dist/targets/registry.d.ts +0 -25
  595. package/dist/targets/registry.d.ts.map +0 -1
  596. package/dist/targets/registry.js +0 -34
  597. package/dist/targets/registry.js.map +0 -1
  598. package/dist/targets/scoop.d.ts +0 -11
  599. package/dist/targets/scoop.d.ts.map +0 -1
  600. package/dist/targets/scoop.js +0 -78
  601. package/dist/targets/scoop.js.map +0 -1
  602. package/dist/types/effect-internal.d.ts +0 -5
  603. package/dist/types/effect-internal.d.ts.map +0 -1
  604. package/dist/types/effect-internal.js +0 -2
  605. package/dist/types/effect-internal.js.map +0 -1
  606. package/dist/workflows/config.d.ts +0 -252
  607. package/dist/workflows/config.d.ts.map +0 -1
  608. package/dist/workflows/config.js +0 -465
  609. package/dist/workflows/config.js.map +0 -1
  610. package/dist/workflows/diagnostics.d.ts +0 -62
  611. package/dist/workflows/diagnostics.d.ts.map +0 -1
  612. package/dist/workflows/diagnostics.js +0 -527
  613. package/dist/workflows/diagnostics.js.map +0 -1
  614. package/dist/workflows/evidence.d.ts +0 -26
  615. package/dist/workflows/evidence.d.ts.map +0 -1
  616. package/dist/workflows/evidence.js +0 -44
  617. package/dist/workflows/evidence.js.map +0 -1
  618. package/dist/workflows/index.d.ts +0 -7
  619. package/dist/workflows/index.d.ts.map +0 -1
  620. package/dist/workflows/index.js +0 -6
  621. package/dist/workflows/index.js.map +0 -1
  622. package/dist/workflows/init.d.ts +0 -84
  623. package/dist/workflows/init.d.ts.map +0 -1
  624. package/dist/workflows/init.js +0 -441
  625. package/dist/workflows/init.js.map +0 -1
  626. package/dist/workflows/live.d.ts +0 -9
  627. package/dist/workflows/live.d.ts.map +0 -1
  628. package/dist/workflows/live.js +0 -7
  629. package/dist/workflows/live.js.map +0 -1
  630. package/dist/workflows/options.d.ts +0 -30
  631. package/dist/workflows/options.d.ts.map +0 -1
  632. package/dist/workflows/options.js +0 -16
  633. package/dist/workflows/options.js.map +0 -1
  634. package/examples/non-strict-skips/artifacts/release-example-skips-0.1.0.tgz +0 -1
  635. package/examples/non-strict-skips/release.config.json +0 -31
  636. package/examples/pypi-registry/release.config.json +0 -31
  637. package/templates/github-actions/plan-and-approved-execute.yml +0 -58
  638. package/templates/github-actions/plan-only.yml +0 -33
  639. package/templates/github-actions/trusted-publishing.yml +0 -61
@@ -0,0 +1,299 @@
1
+ import * as Schema from "effect/Schema"
2
+ import { encodeCanonicalJson, parseStrictJson } from "../model/canonical.js"
3
+ import {
4
+ ArtifactCollectionContract,
5
+ ArtifactCollectionMember,
6
+ cardinalityAccepts,
7
+ cardinalityIssue,
8
+ collectionContractIssue,
9
+ collectionMemberId,
10
+ collectionMemberPath
11
+ } from "../model/artifact-collection.js"
12
+ import { Sha256Digest } from "../model/digest.js"
13
+ import {
14
+ CatalogRepositoryPath,
15
+ GitBranchName,
16
+ GitHubRepositoryCoordinate,
17
+ PreparedCatalogRenderer
18
+ } from "../model/catalog.js"
19
+ import { PythonDistribution } from "../model/python-distribution.js"
20
+ import { NonEmptyName, OutputId, SafeRelativePath, Version } from "../model/primitives.js"
21
+ import { ExplicitInputSnapshot, StagingSnapshot } from "./context.js"
22
+ import {
23
+ CanonicalNpmRegistryEndpoint,
24
+ NpmAccess,
25
+ NpmAuthentication,
26
+ NpmDistTag,
27
+ NpmProvenancePolicy,
28
+ PyPiAuthentication,
29
+ PyPiProjectName,
30
+ PyPiRepository
31
+ } from "../recipes/config.js"
32
+ import {
33
+ PublicationAuthorityIntent,
34
+ catalogPublicationAuthorityIssue,
35
+ githubPublicationAuthorityIssue,
36
+ npmPublicationAuthorityIssue,
37
+ pyPiPublicationAuthorityIssue
38
+ } from "./graph.js"
39
+
40
+ const optional = Schema.optionalKey
41
+ const artifactKind = Schema.Literals(["file", "executable", "archive", "package", "digest"])
42
+
43
+ export class PreparedSource extends Schema.Class<PreparedSource>("PreparedSource")({
44
+ commit: NonEmptyName, tree: NonEmptyName, clean: Schema.Literal(true),
45
+ packageManifestPath: SafeRelativePath, packageManifestDigest: Sha256Digest,
46
+ materialized: StagingSnapshot
47
+ }) {}
48
+
49
+ export class PreparedProject extends Schema.Class<PreparedProject>("PreparedProject")({
50
+ name: NonEmptyName, packageName: optional(NonEmptyName), version: Version, tag: NonEmptyName,
51
+ repository: optional(Schema.NonEmptyString)
52
+ }) {}
53
+
54
+ export class PreparedArtifact extends Schema.Class<PreparedArtifact>("PreparedArtifact")({
55
+ id: OutputId, path: SafeRelativePath, kind: artifactKind, size: Schema.Number.check(
56
+ Schema.makeFilter((value: number) => Number.isSafeInteger(value) && value >= 0
57
+ ? undefined : "Prepared artifact size must be a nonnegative safe integer.")
58
+ ), digest: Sha256Digest, blob: Sha256Digest, mediaType: optional(Schema.NonEmptyString),
59
+ producer: NonEmptyName,
60
+ inputBasis: Sha256Digest
61
+ }) {}
62
+
63
+ /** Durable collection provenance; the graph itself remains ephemeral. */
64
+ export class PreparedArtifactCollection
65
+ extends Schema.Class<PreparedArtifactCollection>("PreparedArtifactCollection")({
66
+ contract: ArtifactCollectionContract,
67
+ members: Schema.Array(ArtifactCollectionMember)
68
+ }) {}
69
+
70
+ export class PreparedExecutionInputs extends Schema.Class<PreparedExecutionInputs>("PreparedExecutionInputs")({
71
+ environment: Schema.Literal("closed"),
72
+ network: Schema.Literal("prohibited"),
73
+ timezone: Schema.Literal("UTC"),
74
+ locale: Schema.Literal("C"),
75
+ clock: Schema.Literal("source-date-epoch=0;host-clock-not-isolated"),
76
+ randomness: Schema.Literal("host-randomness-not-isolated"),
77
+ platform: Schema.NonEmptyString,
78
+ runtime: Schema.NonEmptyString,
79
+ networkIsolation: Schema.NonEmptyString,
80
+ bunCompileRuntimes: Schema.NonEmptyString,
81
+ npmPack: Schema.NonEmptyString,
82
+ releaseGraph: Sha256Digest,
83
+ preparer: Schema.NonEmptyString
84
+ }) {}
85
+
86
+ export class PreparedProvenance extends Schema.Class<PreparedProvenance>("PreparedProvenance")({
87
+ source: StagingSnapshot,
88
+ externalInputs: Schema.Array(ExplicitInputSnapshot),
89
+ execution: PreparedExecutionInputs,
90
+ inputBasis: Sha256Digest,
91
+ reproducibility: Schema.Literal("not-asserted")
92
+ }) {}
93
+
94
+ export class PreparedNpmPublication extends Schema.TaggedClass<PreparedNpmPublication>()("PreparedNpmPublication", {
95
+ id: NonEmptyName,
96
+ artifactId: OutputId,
97
+ packageName: NonEmptyName,
98
+ version: Version,
99
+ registryUrl: CanonicalNpmRegistryEndpoint,
100
+ distTag: NpmDistTag,
101
+ access: NpmAccess,
102
+ authentication: NpmAuthentication,
103
+ provenance: NpmProvenancePolicy,
104
+ authority: PublicationAuthorityIntent
105
+ }) {}
106
+
107
+ export class PreparedGitHubAsset extends Schema.Class<PreparedGitHubAsset>("PreparedGitHubAsset")({
108
+ artifactId: OutputId, name: Schema.NonEmptyString, mediaType: Schema.NonEmptyString
109
+ }) {}
110
+
111
+ export class PreparedGitHubPublication extends Schema.TaggedClass<PreparedGitHubPublication>()("PreparedGitHubPublication", {
112
+ id: NonEmptyName, repository: Schema.NonEmptyString, tag: NonEmptyName, title: NonEmptyName,
113
+ draft: Schema.Boolean, prerelease: Schema.Boolean, targetCommit: NonEmptyName, body: optional(Schema.String),
114
+ assets: Schema.Array(PreparedGitHubAsset), authority: PublicationAuthorityIntent
115
+ }) {}
116
+
117
+ export class PreparedPyPiFile extends Schema.Class<PreparedPyPiFile>("PreparedPyPiFile")({
118
+ artifactId: OutputId,
119
+ filename: NonEmptyName,
120
+ size: Schema.Number.check(Schema.makeFilter((value: number) =>
121
+ Number.isSafeInteger(value) && value >= 0 ? undefined : "PyPI file size must be a nonnegative safe integer.")),
122
+ sha256: Sha256Digest,
123
+ mediaType: Schema.Literals(["application/zip", "application/gzip"]),
124
+ distribution: PythonDistribution,
125
+ authority: PublicationAuthorityIntent
126
+ }) {}
127
+
128
+ export class PreparedPyPiPublication extends Schema.TaggedClass<PreparedPyPiPublication>()("PreparedPyPiPublication", {
129
+ id: NonEmptyName,
130
+ project: PyPiProjectName,
131
+ version: Version,
132
+ repository: PyPiRepository,
133
+ simpleBaseUrl: Schema.NonEmptyString,
134
+ projectUrl: Schema.NonEmptyString,
135
+ uploadUrl: Schema.NonEmptyString,
136
+ authentication: PyPiAuthentication,
137
+ files: Schema.NonEmptyArray(PreparedPyPiFile)
138
+ }) {}
139
+
140
+ export class PreparedCatalogPublication
141
+ extends Schema.TaggedClass<PreparedCatalogPublication>()("PreparedCatalogPublication", {
142
+ id: NonEmptyName,
143
+ catalogId: NonEmptyName,
144
+ targetArtifactId: OutputId,
145
+ stateArtifactId: OutputId,
146
+ targetDigest: Sha256Digest,
147
+ stateDigest: Sha256Digest,
148
+ repository: GitHubRepositoryCoordinate,
149
+ branch: GitBranchName,
150
+ targetPath: CatalogRepositoryPath,
151
+ statePath: CatalogRepositoryPath,
152
+ version: Version,
153
+ sourceRepository: GitHubRepositoryCoordinate,
154
+ sourceTag: NonEmptyName,
155
+ renderer: PreparedCatalogRenderer,
156
+ authority: PublicationAuthorityIntent
157
+ }) {}
158
+
159
+ const PreparedPublicationVariants = Schema.Union([
160
+ PreparedNpmPublication, PreparedPyPiPublication, PreparedGitHubPublication, PreparedCatalogPublication
161
+ ])
162
+ export const PreparedPublication = PreparedPublicationVariants.pipe(Schema.check(
163
+ Schema.makeFilter((publication) => publication._tag === "PreparedNpmPublication"
164
+ ? npmPublicationAuthorityIssue(publication)
165
+ : publication._tag === "PreparedPyPiPublication"
166
+ ? pyPiPublicationAuthorityIssue(publication)
167
+ : publication._tag === "PreparedGitHubPublication"
168
+ ? githubPublicationAuthorityIssue(publication)
169
+ : catalogPublicationAuthorityIssue(publication))
170
+ ))
171
+ export type PreparedPublication = typeof PreparedPublication.Type
172
+
173
+ export class PreparedReleaseV2 extends Schema.Class<PreparedReleaseV2>("PreparedReleaseV2")({
174
+ kind: Schema.Literal("complete"),
175
+ schemaVersion: Schema.Literal("prepared-release/v2"), source: PreparedSource, project: PreparedProject,
176
+ provenance: PreparedProvenance,
177
+ artifacts: Schema.Array(PreparedArtifact),
178
+ collections: Schema.Array(PreparedArtifactCollection),
179
+ publications: Schema.Array(PreparedPublication)
180
+ }) {}
181
+
182
+ export class PreparedManifestError
183
+ extends Schema.TaggedErrorClass<PreparedManifestError>()("PreparedManifestError", {
184
+ reason: Schema.String
185
+ }) {}
186
+
187
+ const assertCompletePreparedRelease = (manifest: PreparedReleaseV2): void => {
188
+ if (manifest.kind !== "complete") throw new Error("prepared manifest kind must be 'complete'")
189
+ if (manifest.source.materialized.digest.hex !== manifest.provenance.source.digest.hex) {
190
+ throw new Error("prepared source materialization must match provenance source")
191
+ }
192
+ for (const publication of manifest.publications) {
193
+ if (publication._tag === "PreparedNpmPublication" &&
194
+ publication.authority.publishStrategy.kind === "trusted-publishing" &&
195
+ publication.authority.publishStrategy.sourceCommit.toString() !== manifest.source.commit.toString()) {
196
+ throw new Error(`prepared npm publication ${publication.id} source commit disagrees with the verified source`)
197
+ }
198
+ if (publication._tag === "PreparedPyPiPublication" &&
199
+ publication.authentication.strategy === "trusted-publishing" &&
200
+ publication.files.some((file) => file.authority.publishStrategy.kind !== "trusted-publishing" ||
201
+ file.authority.publishStrategy.sourceCommit.toString() !== manifest.source.commit.toString())) {
202
+ throw new Error(`prepared PyPI publication ${publication.id} source commit disagrees with the verified source`)
203
+ }
204
+ }
205
+ for (const artifact of manifest.artifacts) {
206
+ if (artifact.inputBasis.hex !== manifest.provenance.inputBasis.hex) {
207
+ throw new Error(`prepared artifact ${artifact.id} has a foreign input basis`)
208
+ }
209
+ }
210
+ const artifacts = new Map(manifest.artifacts.map((artifact) => [artifact.id.toString(), artifact]))
211
+ for (const publication of manifest.publications) {
212
+ if (publication._tag !== "PreparedCatalogPublication") continue
213
+ const target = artifacts.get(publication.targetArtifactId.toString())
214
+ const state = artifacts.get(publication.stateArtifactId.toString())
215
+ if (target === undefined || state === undefined || target.digest.hex !== publication.targetDigest.hex ||
216
+ state.digest.hex !== publication.stateDigest.hex || target.kind !== "file" || state.kind !== "file") {
217
+ throw new Error(`prepared catalog publication ${publication.id} does not bind its exact target/state artifacts`)
218
+ }
219
+ }
220
+ const collectionIds = new Set<string>()
221
+ const collectionRoots = new Map<string, string>()
222
+ const collectionProducers = new Set<string>()
223
+ const memberArtifacts = new Set<string>()
224
+ let previousCollection = ""
225
+ for (const collection of manifest.collections) {
226
+ const { contract } = collection
227
+ const id = contract.id.toString()
228
+ if (id <= previousCollection) throw new Error("prepared artifact collections must have unique code-point-sorted ids")
229
+ previousCollection = id
230
+ const foldedId = id.toLocaleLowerCase("en-US")
231
+ if (collectionIds.has(foldedId)) throw new Error(`prepared artifact collection ${id} has a case-colliding id`)
232
+ collectionIds.add(foldedId)
233
+ const foldedRoot = contract.root.toString().toLocaleLowerCase("en-US")
234
+ const overlappingRoot = [...collectionRoots.entries()].find(([root]) =>
235
+ foldedRoot === root || foldedRoot.startsWith(`${root}/`) || root.startsWith(`${foldedRoot}/`))
236
+ if (overlappingRoot !== undefined) {
237
+ throw new Error(`prepared artifact collection ${id} root overlaps collection ${overlappingRoot[1]}`)
238
+ }
239
+ collectionRoots.set(foldedRoot, id)
240
+ if (collectionProducers.has(contract.producer.toString())) {
241
+ throw new Error(`prepared artifact collection ${id} repeats producer ${contract.producer}`)
242
+ }
243
+ collectionProducers.add(contract.producer.toString())
244
+ const issue = cardinalityIssue(contract.cardinality)
245
+ if (issue !== undefined) throw new Error(`prepared artifact collection ${id}: ${issue}`)
246
+ const contractIssue = collectionContractIssue(contract)
247
+ if (contractIssue !== undefined) throw new Error(`prepared artifact collection ${id}: ${contractIssue}`)
248
+ if (!cardinalityAccepts(contract.cardinality, collection.members.length)) {
249
+ throw new Error(`prepared artifact collection ${id} has ${collection.members.length} members outside its declared cardinality`)
250
+ }
251
+ const keys = new Set<string>()
252
+ let previousKey = ""
253
+ for (const member of collection.members) {
254
+ const key = member.key.toString()
255
+ if (key <= previousKey) throw new Error(`prepared artifact collection ${id} member keys must be unique and code-point sorted`)
256
+ previousKey = key
257
+ const foldedKey = key.toLocaleLowerCase("en-US")
258
+ if (keys.has(foldedKey)) throw new Error(`prepared artifact collection ${id} has duplicate normalized member key ${key}`)
259
+ keys.add(foldedKey)
260
+ const expectedId = collectionMemberId(contract, member.key)
261
+ if (member.artifactId !== expectedId) throw new Error(`prepared artifact collection ${id} member ${key} has an invalid stable artifact id`)
262
+ const artifact = artifacts.get(member.artifactId.toString())
263
+ if (artifact === undefined) throw new Error(`prepared artifact collection ${id} member ${key} references no artifact`)
264
+ if (artifact.path !== collectionMemberPath(contract, member.key) || artifact.kind !== contract.artifactKind ||
265
+ artifact.mediaType !== contract.mediaType || artifact.producer.toString() !== contract.producer.toString()) {
266
+ throw new Error(`prepared artifact collection ${id} member ${key} disagrees with its contract`)
267
+ }
268
+ if (memberArtifacts.has(member.artifactId.toString())) {
269
+ throw new Error(`prepared artifact ${member.artifactId} belongs to more than one collection`)
270
+ }
271
+ memberArtifacts.add(member.artifactId.toString())
272
+ }
273
+ }
274
+ for (const artifact of manifest.artifacts) {
275
+ if (collectionProducers.has(artifact.producer.toString()) && !memberArtifacts.has(artifact.id.toString())) {
276
+ throw new Error(`prepared collection producer ${artifact.producer} has unclaimed artifact ${artifact.id}`)
277
+ }
278
+ }
279
+ }
280
+
281
+ export const encodePreparedRelease = (manifest: PreparedReleaseV2): Uint8Array => {
282
+ assertCompletePreparedRelease(manifest)
283
+ return new TextEncoder().encode(encodeCanonicalJson(Schema.encodeSync(PreparedReleaseV2)(manifest)))
284
+ }
285
+
286
+ export const decodePreparedRelease = (bytes: Uint8Array): PreparedReleaseV2 => {
287
+ try {
288
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes)
289
+ const value = Schema.decodeUnknownSync(PreparedReleaseV2, { onExcessProperty: "error" })(parseStrictJson(text))
290
+ assertCompletePreparedRelease(value)
291
+ const canonical = encodePreparedRelease(value)
292
+ if (canonical.length !== bytes.length || canonical.some((byte, index) => byte !== bytes[index])) {
293
+ throw new Error("manifest bytes are not canonical")
294
+ }
295
+ return value
296
+ } catch (cause) {
297
+ throw PreparedManifestError.make({ reason: cause instanceof Error ? cause.message : String(cause) })
298
+ }
299
+ }
@@ -0,0 +1,257 @@
1
+ import {
2
+ constants,
3
+ copyFileSync,
4
+ existsSync,
5
+ lstatSync,
6
+ mkdirSync,
7
+ readFileSync,
8
+ readlinkSync,
9
+ readdirSync,
10
+ realpathSync,
11
+ symlinkSync
12
+ } from "node:fs"
13
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path"
14
+ import { encodeCanonicalJson } from "../model/canonical.js"
15
+ import { sha256Digest, type Sha256Digest } from "../model/digest.js"
16
+ import { SafeRelativePath } from "../model/primitives.js"
17
+ import {
18
+ ExplicitInputSnapshot,
19
+ StagingEntry,
20
+ StagingSnapshot
21
+ } from "./context.js"
22
+
23
+ const utf8 = new TextEncoder()
24
+ const byCodepoint = (left: string, right: string): number => left < right ? -1 : left > right ? 1 : 0
25
+
26
+ const fail = (reason: string): never => { throw new Error(reason) }
27
+
28
+ /** Paths in staging manifests are portable POSIX-style relative paths. */
29
+ export const canonicalStagingPath = (value: string): SafeRelativePath => {
30
+ const path = value.split(sep).join("/")
31
+ if (path === "." || path.includes("\\") || path.includes("\0") ||
32
+ path.split("/").some((part) => part.length === 0 || part === "." || part === "..")) {
33
+ return fail(`Staging path ${JSON.stringify(value)} is not canonical and portable.`)
34
+ }
35
+ return SafeRelativePath.make(path)
36
+ }
37
+
38
+ const lexicalContainment = (root: string, candidate: string): boolean => {
39
+ const path = relative(root, candidate)
40
+ return path === "" || (path !== ".." && !path.startsWith(`..${sep}`) && !isAbsolute(path))
41
+ }
42
+
43
+ const relativePath = (root: string, path: string): SafeRelativePath => canonicalStagingPath(relative(root, path))
44
+
45
+ const entryDigest = (kind: StagingEntry["kind"], path: string): { readonly digest: ReturnType<typeof sha256Digest>, readonly size: number } => {
46
+ if (kind === "directory") return { digest: sha256Digest(utf8.encode("")), size: 0 }
47
+ const bytes = kind === "symlink" ? utf8.encode(readlinkSync(path)) : new Uint8Array(readFileSync(path))
48
+ return { digest: sha256Digest(bytes), size: bytes.length }
49
+ }
50
+
51
+ const assertSafeSymlink = (root: string, path: string): void => {
52
+ const target = readlinkSync(path)
53
+ if (target.length === 0 || isAbsolute(target)) fail(`Staging symlink ${relative(root, path)} has an absolute or empty target.`)
54
+ const landed = resolve(dirname(path), target)
55
+ if (!lexicalContainment(root, landed)) fail(`Staging symlink ${relative(root, path)} escapes the private root.`)
56
+ if (!existsSync(landed)) fail(`Staging symlink ${relative(root, path)} is dangling.`)
57
+ const real = realpathSync(path)
58
+ if (!lexicalContainment(root, real)) fail(`Staging symlink ${relative(root, path)} resolves outside the private root.`)
59
+ }
60
+
61
+ const isExcluded = (path: string, excluded: ReadonlyArray<string>): boolean =>
62
+ excluded.some((root) => path === root || path.startsWith(`${root}/`))
63
+
64
+ const isExcludedAncestor = (path: string, excluded: ReadonlyArray<string>): boolean =>
65
+ excluded.some((root) => root.startsWith(`${path}/`))
66
+
67
+ export interface SnapshotOptions {
68
+ /** Canonical roots omitted from a mutation comparison (declared outputs and scratch only). */
69
+ readonly exclude?: ReadonlyArray<SafeRelativePath | string>
70
+ }
71
+
72
+ /** Canonical path/type/digest manifest for a private staging tree. */
73
+ export const snapshotStaging = (rootValue: string, options: SnapshotOptions = {}): StagingSnapshot => {
74
+ const root = realpathSync(rootValue)
75
+ if (lstatSync(root).isSymbolicLink() || !lstatSync(root).isDirectory()) fail("Staging root must be a real directory.")
76
+ const excluded = (options.exclude ?? []).map(String).sort(byCodepoint)
77
+ const entries: StagingEntry[] = []
78
+ const folded = new Map<string, string>()
79
+ const walk = (directory: string): void => {
80
+ for (const child of readdirSync(directory, { withFileTypes: true }).sort((left, right) => byCodepoint(left.name, right.name))) {
81
+ const path = join(directory, child.name)
82
+ const name = relativePath(root, path).toString()
83
+ if (isExcluded(name, excluded)) continue
84
+ const collision = folded.get(name.toLocaleLowerCase("en-US"))
85
+ if (collision !== undefined && collision !== name) fail(`Staging paths ${collision} and ${name} collide under case folding.`)
86
+ folded.set(name.toLocaleLowerCase("en-US"), name)
87
+ const stat = lstatSync(path)
88
+ const kind: StagingEntry["kind"] = stat.isSymbolicLink()
89
+ ? "symlink"
90
+ : stat.isDirectory()
91
+ ? "directory"
92
+ : stat.isFile()
93
+ ? (stat.mode & 0o111) === 0 ? "file" : "executable"
94
+ : fail(`Staging path ${name} has an unsupported filesystem kind.`)
95
+ if (kind === "symlink") assertSafeSymlink(root, path)
96
+ const value = entryDigest(kind, path)
97
+ // Ancestor directories of an excluded writable root are traversal-only:
98
+ // creating `.release` in order to create `.release/assets` must not look
99
+ // like an undeclared mutation, while siblings under `.release` remain
100
+ // fully snapshotted and protected.
101
+ if (!(kind === "directory" && isExcludedAncestor(name, excluded))) {
102
+ entries.push(StagingEntry.make({
103
+ path: canonicalStagingPath(name),
104
+ kind,
105
+ mode: kind === "executable" ? 0o755 : kind === "directory" ? 0o755 : kind === "symlink" ? 0o777 : 0o644,
106
+ size: value.size,
107
+ digest: value.digest
108
+ }))
109
+ }
110
+ if (kind === "directory") walk(path)
111
+ }
112
+ }
113
+ walk(root)
114
+ const canonical = encodeCanonicalJson(entries.map((entry) => ({
115
+ path: entry.path.toString(), kind: entry.kind, mode: entry.mode, size: entry.size,
116
+ digest: entry.digest.hex
117
+ })))
118
+ return StagingSnapshot.make({ entries, digest: sha256Digest(utf8.encode(canonical)) })
119
+ }
120
+
121
+ export const snapshotsEqual = (left: StagingSnapshot, right: StagingSnapshot): boolean =>
122
+ left.digest.hex === right.digest.hex
123
+
124
+ const snapshotDigest = (entries: ReadonlyArray<StagingEntry>): Sha256Digest => sha256Digest(utf8.encode(encodeCanonicalJson(
125
+ entries.map((entry) => ({
126
+ path: entry.path.toString(), kind: entry.kind, mode: entry.mode, size: entry.size,
127
+ digest: entry.digest.hex
128
+ }))
129
+ )))
130
+
131
+ /** Select canonical roots from a whole-stage snapshot without following links again. */
132
+ export const selectStagingSnapshot = (
133
+ snapshot: StagingSnapshot,
134
+ roots: ReadonlyArray<SafeRelativePath | string>
135
+ ): StagingSnapshot => {
136
+ const names = roots.map(String)
137
+ const selected = snapshot.entries.map((entry) => {
138
+ const root = names.find((candidate) => entry.path.toString().startsWith(`${candidate}/`))
139
+ if (root === undefined) return undefined
140
+ const path = canonicalStagingPath(entry.path.toString().slice(root.length + 1))
141
+ return StagingEntry.make({ ...entry, path })
142
+ }).filter((entry): entry is StagingEntry => entry !== undefined)
143
+ return StagingSnapshot.make({ entries: selected, digest: snapshotDigest(selected) })
144
+ }
145
+
146
+ export const snapshotDifference = (before: StagingSnapshot, after: StagingSnapshot): string => {
147
+ const left = new Map(before.entries.map((entry) => [entry.path.toString(), entry]))
148
+ const right = new Map(after.entries.map((entry) => [entry.path.toString(), entry]))
149
+ const paths = [...new Set([...left.keys(), ...right.keys()])].sort(byCodepoint)
150
+ const changed = paths.filter((path) => {
151
+ const a = left.get(path); const b = right.get(path)
152
+ return a === undefined || b === undefined || a.kind !== b.kind || a.mode !== b.mode ||
153
+ a.size !== b.size || a.digest.hex !== b.digest.hex
154
+ })
155
+ return changed.slice(0, 8).join(", ") + (changed.length > 8 ? ` (+${changed.length - 8} more)` : "")
156
+ }
157
+
158
+ export const assertSnapshotPresent = (
159
+ root: string,
160
+ expected: StagingSnapshot,
161
+ label: string,
162
+ options: SnapshotOptions = {}
163
+ ): void => {
164
+ const current = snapshotStaging(root, options)
165
+ const available = new Map(current.entries.map((entry) => [entry.path.toString(), entry]))
166
+ const excluded = (options.exclude ?? []).map(String)
167
+ for (const entry of expected.entries) {
168
+ if (isExcluded(entry.path.toString(), excluded)) continue
169
+ const actual = available.get(entry.path.toString())
170
+ if (actual === undefined || actual.kind !== entry.kind || actual.mode !== entry.mode ||
171
+ actual.size !== entry.size || actual.digest.hex !== entry.digest.hex) {
172
+ fail(`${label} changed at ${entry.path}.`)
173
+ }
174
+ }
175
+ }
176
+
177
+ const copyPrivateTree = (sourceRoot: string, destinationRoot: string): void => {
178
+ const source = realpathSync(sourceRoot)
179
+ const walk = (from: string, to: string): void => {
180
+ const stat = lstatSync(from)
181
+ if (stat.isSymbolicLink()) {
182
+ const landed = realpathSync(from)
183
+ if (!lexicalContainment(source, landed)) fail(`Explicit input symlink ${from} escapes its declared root.`)
184
+ const target = readlinkSync(from)
185
+ symlinkSync(target, to, stat.isDirectory() ? "dir" : "file")
186
+ return
187
+ }
188
+ if (stat.isDirectory()) {
189
+ mkdirSync(to, { recursive: false, mode: 0o700 })
190
+ for (const child of readdirSync(from).sort(byCodepoint)) walk(join(from, child), join(to, child))
191
+ return
192
+ }
193
+ if (!stat.isFile()) fail(`Explicit input ${from} has an unsupported filesystem kind.`)
194
+ copyFileSync(from, to, constants.COPYFILE_FICLONE)
195
+ }
196
+ walk(sourceRoot, destinationRoot)
197
+ // Re-snapshot to reject copied links that only appeared safe in the source topology.
198
+ if (lstatSync(destinationRoot).isDirectory()) snapshotStaging(destinationRoot)
199
+ }
200
+
201
+ /**
202
+ * Copy/reflink one declared non-Git input into private staging and return the
203
+ * digest that becomes part of prepared provenance. The destination must not
204
+ * already exist; source bytes can therefore never overwrite verified Git
205
+ * materialization.
206
+ */
207
+ export const materializeExplicitInput = (input: {
208
+ readonly id: string
209
+ readonly sourceWorkspace: string
210
+ readonly stageRoot: string
211
+ readonly path: SafeRelativePath
212
+ }): ExplicitInputSnapshot => {
213
+ const sourceRoot = realpathSync(input.sourceWorkspace)
214
+ const source = join(sourceRoot, input.path.toString())
215
+ const destination = join(input.stageRoot, input.path.toString())
216
+ if (!lexicalContainment(sourceRoot, source) || !lexicalContainment(realpathSync(input.stageRoot), destination)) {
217
+ return fail(`Explicit input ${input.id} escapes its declared root.`)
218
+ }
219
+ if (!existsSync(source)) return fail(`Explicit input ${input.id} does not exist at ${input.path}.`)
220
+ if (existsSync(destination)) return fail(`Explicit input ${input.id} overlaps verified source at ${input.path}.`)
221
+ mkdirSync(dirname(destination), { recursive: true, mode: 0o700 })
222
+ const sourceStat = lstatSync(source)
223
+ if (sourceStat.isSymbolicLink()) return fail(`Explicit input ${input.id} cannot be a symlink.`)
224
+ copyPrivateTree(source, destination)
225
+ const kind = sourceStat.isDirectory() ? "directory" : "file"
226
+ const snapshot = sourceStat.isDirectory() ? snapshotStaging(destination) : undefined
227
+ const size = sourceStat.isDirectory()
228
+ ? snapshot!.entries.filter((entry) => entry.kind !== "directory").reduce((sum, entry) => sum + entry.size, 0)
229
+ : sourceStat.size
230
+ const digest = sourceStat.isDirectory()
231
+ ? snapshot!.digest
232
+ : sha256Digest(new Uint8Array(readFileSync(destination)))
233
+ return ExplicitInputSnapshot.make({
234
+ id: input.id,
235
+ path: input.path,
236
+ kind,
237
+ size,
238
+ digest,
239
+ materializer: "private-copy-or-reflink/v1",
240
+ materializationBasis: [digest]
241
+ })
242
+ }
243
+
244
+ /** Digest a set of preparation influences without asserting reproducibility. */
245
+ export const preparationBasisDigest = (
246
+ source: StagingSnapshot,
247
+ inputs: ReadonlyArray<ExplicitInputSnapshot>,
248
+ execution: Readonly<Record<string, string>>
249
+ ) => sha256Digest(utf8.encode(encodeCanonicalJson({
250
+ source: source.digest.hex,
251
+ inputs: [...inputs].sort((left, right) => byCodepoint(left.id, right.id)).map((input) => ({
252
+ id: input.id, path: input.path.toString(), kind: input.kind, size: input.size, digest: input.digest.hex,
253
+ materializer: input.materializer,
254
+ materializationBasis: input.materializationBasis.map((digest) => digest.hex)
255
+ })),
256
+ execution
257
+ })))
@@ -0,0 +1,64 @@
1
+ // The shape a HUMAN writes. It is the canonical config with the facts a
2
+ // repository already knows made optional, plus the two directives that say how
3
+ // to fill them. Nothing here is inferred at plan time: resolution happens at
4
+ // the application boundary and produces a canonical value before `plan` runs.
5
+ //
6
+ // The field lists are REUSED from the canonical classes, never restated — a
7
+ // hand-mirrored schema is a drift machine.
8
+ import * as Schema from "effect/Schema"
9
+ import { NonEmptyName, Version } from "../model/primitives.js"
10
+ import {
11
+ CandidateConfig,
12
+ CandidatePublish,
13
+ CandidateProject,
14
+ NpmAccess,
15
+ NpmAuthentication,
16
+ NpmProvenancePolicy,
17
+ PyPiAuthentication,
18
+ PyPiRepository
19
+ } from "../recipes/config.js"
20
+
21
+ const optional = Schema.optionalKey
22
+
23
+ export class AuthoredProject extends Schema.Class<AuthoredProject>("AuthoredProject")({
24
+ ...CandidateProject.fields,
25
+ // Filled from the package manifest's name when omitted; still required if
26
+ // nothing observes one.
27
+ name: optional(NonEmptyName),
28
+ // Observed or derived when omitted: version from a manifest or the tag at
29
+ // HEAD, and tag from the template. Source commit is always observed; a
30
+ // human-authored commit expectation would only validate and then disappear.
31
+ version: optional(Version),
32
+ tag: optional(NonEmptyName),
33
+ // `{version}` is the only token, and it is the only one this repo will ever
34
+ // accept: a template grammar is the rejected direction.
35
+ tagTemplate: optional(Schema.NonEmptyString)
36
+ }) {}
37
+
38
+ /** Human-authored npm policy. Resolution fills only documented deterministic defaults. */
39
+ export class AuthoredNpmPublish extends Schema.Class<AuthoredNpmPublish>("AuthoredNpmPublish")({
40
+ registry: optional(Schema.NonEmptyString),
41
+ distTag: optional(Schema.NonEmptyString),
42
+ access: optional(NpmAccess),
43
+ authentication: NpmAuthentication,
44
+ provenance: optional(NpmProvenancePolicy)
45
+ }) {}
46
+
47
+ export class AuthoredPyPiPublish extends Schema.Class<AuthoredPyPiPublish>("AuthoredPyPiPublish")({
48
+ artifacts: Schema.NonEmptyArray(Schema.NonEmptyString),
49
+ repository: optional(PyPiRepository),
50
+ authentication: PyPiAuthentication
51
+ }) {}
52
+
53
+ export class AuthoredPublish extends Schema.Class<AuthoredPublish>("AuthoredPublish")({
54
+ ...CandidatePublish.fields,
55
+ npm: optional(AuthoredNpmPublish),
56
+ pypi: optional(AuthoredPyPiPublish)
57
+ }) {}
58
+
59
+ export class AuthoredConfig extends Schema.Class<AuthoredConfig>("AuthoredConfig")({
60
+ ...CandidateConfig.fields,
61
+ project: AuthoredProject,
62
+ publish: optional(AuthoredPublish),
63
+ versionFrom: optional(Schema.Literals(["manifest", "git-tag"]))
64
+ }) {}
@@ -0,0 +1,22 @@
1
+ // The reviewable IR's byte shape: readable JSON with sorted keys, so two
2
+ // resolutions of the same inputs produce the same file and a diff shows only
3
+ // what actually changed.
4
+ //
5
+ // This is deliberately NOT the plan-bytes canonical encoder. Canonical encoding
6
+ // is a durable-document discipline that plan identity depends on; this is a
7
+ // convenience artifact that nothing ever reads back.
8
+ // Also the plain-JSON conversion the resolver needs: decoding produces Schema
9
+ // class INSTANCES, and the canonical decoder refuses non-plain objects.
10
+ export const toPlainJson = (value: unknown): unknown => {
11
+ if (Array.isArray(value)) return value.map(toPlainJson)
12
+ if (typeof value !== "object" || value === null) return value
13
+ return Object.fromEntries(
14
+ Object.entries(value as Record<string, unknown>)
15
+ .filter(([, entry]) => entry !== undefined)
16
+ .sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
17
+ .map(([key, entry]) => [key, toPlainJson(entry)])
18
+ )
19
+ }
20
+
21
+ export const encodeResolvedConfig = (config: unknown): string =>
22
+ `${JSON.stringify(toPlainJson(config), null, 2)}\n`
@@ -0,0 +1,15 @@
1
+ // `resolveConfig` is a PUBLIC boundary function that apps call directly, so its
2
+ // refusals follow the boundary rule SPEC §13 states: a stable plain error
3
+ // carrying a reason, readable as-is when a CLI prints it. (The internal
4
+ // ConfigValueError family is a decode-time vocabulary; its `message` is empty
5
+ // by construction, which would reach a user as a blank line.)
6
+ export class ResolveError extends Error {
7
+ readonly _tag = "ResolveError"
8
+ constructor(
9
+ readonly field: string,
10
+ readonly reason: string
11
+ ) {
12
+ super(reason)
13
+ this.name = "ResolveError"
14
+ }
15
+ }
@@ -0,0 +1,18 @@
1
+ // What the world can tell us about a release, observed by an app and passed in
2
+ // as a value. The library never looks: no git, no filesystem, no environment —
3
+ // observation is the imperative shell's job, resolution is pure.
4
+ import * as Schema from "effect/Schema"
5
+ import { NonEmptyName, Version } from "../model/primitives.js"
6
+
7
+ const optional = Schema.optionalKey
8
+
9
+ export class ObservedFacts extends Schema.Class<ObservedFacts>("ObservedFacts")({
10
+ commit: optional(NonEmptyName),
11
+ manifestName: optional(Schema.NonEmptyString),
12
+ manifestVersion: optional(Version),
13
+ repository: optional(Schema.NonEmptyString),
14
+ // The version carried by the single release-shaped tag at HEAD. Ambiguity is
15
+ // resolved by the observer: several candidate tags means NO fact, so the
16
+ // resolver refuses instead of picking one.
17
+ headTagVersion: optional(Version)
18
+ }) {}