@mannyc1/ts-release 0.0.7 → 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 (635) hide show
  1. package/ARCHITECTURE.md +124 -92
  2. package/CHANGELOG.md +115 -0
  3. package/LICENSE +21 -0
  4. package/README.md +278 -324
  5. package/SPEC.md +206 -310
  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 +60 -156
  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 -72
  440. package/templates/bun-cli-github/README.md +7 -9
  441. package/templates/bun-cli-github/release.config.json +35 -73
  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/artifacts/adapter.d.ts +0 -39
  455. package/dist/artifacts/adapter.d.ts.map +0 -1
  456. package/dist/artifacts/adapter.js +0 -23
  457. package/dist/artifacts/adapter.js.map +0 -1
  458. package/dist/artifacts/registry.d.ts +0 -21
  459. package/dist/artifacts/registry.d.ts.map +0 -1
  460. package/dist/artifacts/registry.js +0 -21
  461. package/dist/artifacts/registry.js.map +0 -1
  462. package/dist/config/errors.d.ts +0 -24
  463. package/dist/config/errors.d.ts.map +0 -1
  464. package/dist/config/errors.js +0 -19
  465. package/dist/config/errors.js.map +0 -1
  466. package/dist/config/load.d.ts +0 -9
  467. package/dist/config/load.d.ts.map +0 -1
  468. package/dist/config/load.js +0 -29
  469. package/dist/config/load.js.map +0 -1
  470. package/dist/config/schema.d.ts +0 -12
  471. package/dist/config/schema.d.ts.map +0 -1
  472. package/dist/config/schema.js +0 -19
  473. package/dist/config/schema.js.map +0 -1
  474. package/dist/domain/artifact.d.ts +0 -118
  475. package/dist/domain/artifact.d.ts.map +0 -1
  476. package/dist/domain/artifact.js +0 -197
  477. package/dist/domain/artifact.js.map +0 -1
  478. package/dist/domain/evidence.d.ts +0 -59
  479. package/dist/domain/evidence.d.ts.map +0 -1
  480. package/dist/domain/evidence.js +0 -51
  481. package/dist/domain/evidence.js.map +0 -1
  482. package/dist/domain/operation.d.ts +0 -143
  483. package/dist/domain/operation.d.ts.map +0 -1
  484. package/dist/domain/operation.js +0 -198
  485. package/dist/domain/operation.js.map +0 -1
  486. package/dist/domain/release.d.ts +0 -93
  487. package/dist/domain/release.d.ts.map +0 -1
  488. package/dist/domain/release.js +0 -82
  489. package/dist/domain/release.js.map +0 -1
  490. package/dist/domain/remote-state.d.ts +0 -58
  491. package/dist/domain/remote-state.d.ts.map +0 -1
  492. package/dist/domain/remote-state.js +0 -55
  493. package/dist/domain/remote-state.js.map +0 -1
  494. package/dist/domain/target.d.ts +0 -148
  495. package/dist/domain/target.d.ts.map +0 -1
  496. package/dist/domain/target.js +0 -151
  497. package/dist/domain/target.js.map +0 -1
  498. package/dist/host/host.d.ts +0 -32
  499. package/dist/host/host.d.ts.map +0 -1
  500. package/dist/host/host.js +0 -24
  501. package/dist/host/host.js.map +0 -1
  502. package/dist/host/http-live.d.ts +0 -6
  503. package/dist/host/http-live.d.ts.map +0 -1
  504. package/dist/host/http-live.js +0 -86
  505. package/dist/host/http-live.js.map +0 -1
  506. package/dist/host/http.d.ts +0 -43
  507. package/dist/host/http.d.ts.map +0 -1
  508. package/dist/host/http.js +0 -59
  509. package/dist/host/http.js.map +0 -1
  510. package/dist/host/platform.d.ts +0 -10
  511. package/dist/host/platform.d.ts.map +0 -1
  512. package/dist/host/platform.js +0 -114
  513. package/dist/host/platform.js.map +0 -1
  514. package/dist/host/test.d.ts +0 -23
  515. package/dist/host/test.d.ts.map +0 -1
  516. package/dist/host/test.js +0 -222
  517. package/dist/host/test.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 -86
  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 -67
  531. package/dist/planner/errors.d.ts.map +0 -1
  532. package/dist/planner/errors.js +0 -58
  533. package/dist/planner/errors.js.map +0 -1
  534. package/dist/planner/evidence-recorder.d.ts +0 -28
  535. package/dist/planner/evidence-recorder.d.ts.map +0 -1
  536. package/dist/planner/evidence-recorder.js +0 -362
  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 -147
  541. package/dist/planner/executor.js.map +0 -1
  542. package/dist/planner/normalize-release.d.ts +0 -19
  543. package/dist/planner/normalize-release.d.ts.map +0 -1
  544. package/dist/planner/normalize-release.js +0 -342
  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 -259
  549. package/dist/planner/reconcile.js.map +0 -1
  550. package/dist/planner/render-plan.d.ts +0 -15
  551. package/dist/planner/render-plan.d.ts.map +0 -1
  552. package/dist/planner/render-plan.js +0 -231
  553. package/dist/planner/render-plan.js.map +0 -1
  554. package/dist/targets/adapter-helpers.d.ts +0 -79
  555. package/dist/targets/adapter-helpers.d.ts.map +0 -1
  556. package/dist/targets/adapter-helpers.js +0 -194
  557. package/dist/targets/adapter-helpers.js.map +0 -1
  558. package/dist/targets/adapter.d.ts +0 -17
  559. package/dist/targets/adapter.d.ts.map +0 -1
  560. package/dist/targets/adapter.js +0 -2
  561. package/dist/targets/adapter.js.map +0 -1
  562. package/dist/targets/github-release.d.ts +0 -25
  563. package/dist/targets/github-release.d.ts.map +0 -1
  564. package/dist/targets/github-release.js +0 -82
  565. package/dist/targets/github-release.js.map +0 -1
  566. package/dist/targets/github.d.ts +0 -11
  567. package/dist/targets/github.d.ts.map +0 -1
  568. package/dist/targets/github.js +0 -117
  569. package/dist/targets/github.js.map +0 -1
  570. package/dist/targets/homebrew.d.ts +0 -11
  571. package/dist/targets/homebrew.d.ts.map +0 -1
  572. package/dist/targets/homebrew.js +0 -210
  573. package/dist/targets/homebrew.js.map +0 -1
  574. package/dist/targets/live.d.ts +0 -5
  575. package/dist/targets/live.d.ts.map +0 -1
  576. package/dist/targets/live.js +0 -50
  577. package/dist/targets/live.js.map +0 -1
  578. package/dist/targets/npm.d.ts +0 -10
  579. package/dist/targets/npm.d.ts.map +0 -1
  580. package/dist/targets/npm.js +0 -112
  581. package/dist/targets/npm.js.map +0 -1
  582. package/dist/targets/pypi.d.ts +0 -11
  583. package/dist/targets/pypi.d.ts.map +0 -1
  584. package/dist/targets/pypi.js +0 -141
  585. package/dist/targets/pypi.js.map +0 -1
  586. package/dist/targets/registry.d.ts +0 -25
  587. package/dist/targets/registry.d.ts.map +0 -1
  588. package/dist/targets/registry.js +0 -34
  589. package/dist/targets/registry.js.map +0 -1
  590. package/dist/targets/scoop.d.ts +0 -11
  591. package/dist/targets/scoop.d.ts.map +0 -1
  592. package/dist/targets/scoop.js +0 -88
  593. package/dist/targets/scoop.js.map +0 -1
  594. package/dist/types/effect-internal.d.ts +0 -5
  595. package/dist/types/effect-internal.d.ts.map +0 -1
  596. package/dist/types/effect-internal.js +0 -2
  597. package/dist/types/effect-internal.js.map +0 -1
  598. package/dist/workflows/config.d.ts +0 -202
  599. package/dist/workflows/config.d.ts.map +0 -1
  600. package/dist/workflows/config.js +0 -420
  601. package/dist/workflows/config.js.map +0 -1
  602. package/dist/workflows/diagnostics.d.ts +0 -62
  603. package/dist/workflows/diagnostics.d.ts.map +0 -1
  604. package/dist/workflows/diagnostics.js +0 -530
  605. package/dist/workflows/diagnostics.js.map +0 -1
  606. package/dist/workflows/distribution.d.ts +0 -25
  607. package/dist/workflows/distribution.d.ts.map +0 -1
  608. package/dist/workflows/distribution.js +0 -24
  609. package/dist/workflows/distribution.js.map +0 -1
  610. package/dist/workflows/evidence.d.ts +0 -17
  611. package/dist/workflows/evidence.d.ts.map +0 -1
  612. package/dist/workflows/evidence.js +0 -32
  613. package/dist/workflows/evidence.js.map +0 -1
  614. package/dist/workflows/index.d.ts +0 -8
  615. package/dist/workflows/index.d.ts.map +0 -1
  616. package/dist/workflows/index.js +0 -7
  617. package/dist/workflows/index.js.map +0 -1
  618. package/dist/workflows/init.d.ts +0 -84
  619. package/dist/workflows/init.d.ts.map +0 -1
  620. package/dist/workflows/init.js +0 -487
  621. package/dist/workflows/init.js.map +0 -1
  622. package/dist/workflows/live.d.ts +0 -9
  623. package/dist/workflows/live.d.ts.map +0 -1
  624. package/dist/workflows/live.js +0 -7
  625. package/dist/workflows/live.js.map +0 -1
  626. package/dist/workflows/options.d.ts +0 -30
  627. package/dist/workflows/options.d.ts.map +0 -1
  628. package/dist/workflows/options.js +0 -16
  629. package/dist/workflows/options.js.map +0 -1
  630. package/examples/non-strict-skips/artifacts/release-example-skips-0.1.0.tgz +0 -1
  631. package/examples/non-strict-skips/release.config.json +0 -31
  632. package/examples/pypi-registry/release.config.json +0 -31
  633. package/templates/github-actions/plan-and-approved-execute.yml +0 -58
  634. package/templates/github-actions/plan-only.yml +0 -33
  635. package/templates/github-actions/trusted-publishing.yml +0 -61
@@ -0,0 +1,193 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as Schema from "effect/Schema"
3
+ import { Sha256Hex } from "../model/digest.js"
4
+
5
+ const referenceSegmentPattern = /^[A-Za-z0-9._~-]{1,255}$/u
6
+ const positiveDecimalPattern = /^[1-9][0-9]*$/u
7
+
8
+ /** The lowercase hexadecimal payload of a SHA-256 digest. */
9
+ export const PreparedReleaseSha256 = Sha256Hex
10
+ export type PreparedReleaseSha256 = Sha256Hex
11
+
12
+ const GitHubReferenceSegment = Schema.String.check(
13
+ Schema.makeFilter((value: string) =>
14
+ referenceSegmentPattern.test(value) && value !== "." && value !== ".."
15
+ ? undefined
16
+ : "GitHub prepared-reference segments must be nonempty URI-unreserved tokens.")
17
+ ).pipe(Schema.brand("GitHubPreparedReferenceSegment"))
18
+
19
+ const GitHubRunCoordinate = Schema.String.check(
20
+ Schema.makeFilter((value: string) =>
21
+ positiveDecimalPattern.test(value)
22
+ ? undefined
23
+ : "GitHub run coordinates must be canonical positive decimal strings.")
24
+ ).pipe(Schema.brand("GitHubPreparedReferenceRunCoordinate"))
25
+
26
+ /** A complete bundle in the configured/default local content-addressed store. */
27
+ export class LocalCompletePreparedReleaseRef
28
+ extends Schema.TaggedClass<LocalCompletePreparedReleaseRef>()("LocalCompletePreparedReleaseRef", {
29
+ kind: Schema.Literal("complete"),
30
+ scheme: Schema.Literal("local"),
31
+ digest: PreparedReleaseSha256
32
+ }) {}
33
+
34
+ /** A complete bundle in a durable GitHub Actions artifact store. */
35
+ export class GitHubActionsCompletePreparedReleaseRef
36
+ extends Schema.TaggedClass<GitHubActionsCompletePreparedReleaseRef>()("GitHubActionsCompletePreparedReleaseRef", {
37
+ kind: Schema.Literal("complete"),
38
+ scheme: Schema.Literal("gha"),
39
+ owner: GitHubReferenceSegment,
40
+ repository: GitHubReferenceSegment,
41
+ runId: GitHubRunCoordinate,
42
+ attempt: GitHubRunCoordinate,
43
+ artifactName: GitHubReferenceSegment,
44
+ digest: PreparedReleaseSha256
45
+ }) {}
46
+
47
+ /** Every complete prepared-release reference accepted by the kernel. */
48
+ export const CompletePreparedReleaseRef = Schema.Union([
49
+ LocalCompletePreparedReleaseRef,
50
+ GitHubActionsCompletePreparedReleaseRef
51
+ ])
52
+ export type CompletePreparedReleaseRef = typeof CompletePreparedReleaseRef.Type
53
+
54
+ export class PreparedReleaseRefMalformedError
55
+ extends Schema.TaggedErrorClass<PreparedReleaseRefMalformedError>()("PreparedReleaseRefMalformedError", {
56
+ reason: Schema.String
57
+ }) {}
58
+
59
+ export class PreparedReleaseRefUnknownSchemeError
60
+ extends Schema.TaggedErrorClass<PreparedReleaseRefUnknownSchemeError>()("PreparedReleaseRefUnknownSchemeError", {
61
+ scheme: Schema.String
62
+ }) {}
63
+
64
+ export const PreparedReleaseRefCodecError = Schema.Union([
65
+ PreparedReleaseRefMalformedError,
66
+ PreparedReleaseRefUnknownSchemeError
67
+ ])
68
+ export type PreparedReleaseRefCodecError = typeof PreparedReleaseRefCodecError.Type
69
+
70
+ export interface GitHubActionsCompletePreparedReleaseRefInput {
71
+ readonly owner: string
72
+ readonly repository: string
73
+ readonly runId: string
74
+ readonly attempt: string
75
+ readonly artifactName: string
76
+ readonly digest: string
77
+ }
78
+
79
+ const malformed = (reason: string): PreparedReleaseRefMalformedError =>
80
+ PreparedReleaseRefMalformedError.make({ reason })
81
+
82
+ const validateDigest = (digest: string): Effect.Effect<PreparedReleaseSha256, PreparedReleaseRefMalformedError> =>
83
+ Schema.is(Sha256Hex)(digest)
84
+ ? Effect.succeed(digest)
85
+ : Effect.fail(malformed("The digest must contain exactly 64 lowercase hexadecimal characters."))
86
+
87
+ const validateSegment = (
88
+ value: string,
89
+ field: string
90
+ ): Effect.Effect<typeof GitHubReferenceSegment.Type, PreparedReleaseRefMalformedError> =>
91
+ referenceSegmentPattern.test(value) && value !== "." && value !== ".."
92
+ ? Effect.succeed(GitHubReferenceSegment.make(value))
93
+ : Effect.fail(malformed(`${field} must be a nonempty URI-unreserved token.`))
94
+
95
+ const validateRunCoordinate = (
96
+ value: string,
97
+ field: string
98
+ ): Effect.Effect<typeof GitHubRunCoordinate.Type, PreparedReleaseRefMalformedError> =>
99
+ positiveDecimalPattern.test(value)
100
+ ? Effect.succeed(GitHubRunCoordinate.make(value))
101
+ : Effect.fail(malformed(`${field} must be a canonical positive decimal string.`))
102
+
103
+ export const makeLocalCompletePreparedReleaseRef = Effect.fn("makeLocalCompletePreparedReleaseRef")(
104
+ function*(digest: string) {
105
+ return LocalCompletePreparedReleaseRef.make({
106
+ kind: "complete",
107
+ scheme: "local",
108
+ digest: yield* validateDigest(digest)
109
+ })
110
+ }
111
+ )
112
+
113
+ export const makeGitHubActionsCompletePreparedReleaseRef = Effect.fn(
114
+ "makeGitHubActionsCompletePreparedReleaseRef"
115
+ )(function*(input: GitHubActionsCompletePreparedReleaseRefInput) {
116
+ return GitHubActionsCompletePreparedReleaseRef.make({
117
+ kind: "complete",
118
+ scheme: "gha",
119
+ owner: yield* validateSegment(input.owner, "owner"),
120
+ repository: yield* validateSegment(input.repository, "repository"),
121
+ runId: yield* validateRunCoordinate(input.runId, "runId"),
122
+ attempt: yield* validateRunCoordinate(input.attempt, "attempt"),
123
+ artifactName: yield* validateSegment(input.artifactName, "artifactName"),
124
+ digest: yield* validateDigest(input.digest)
125
+ })
126
+ })
127
+
128
+ export const isLocalCompletePreparedReleaseRef = (
129
+ value: unknown
130
+ ): value is LocalCompletePreparedReleaseRef => Schema.is(LocalCompletePreparedReleaseRef)(value)
131
+
132
+ export const isGitHubActionsCompletePreparedReleaseRef = (
133
+ value: unknown
134
+ ): value is GitHubActionsCompletePreparedReleaseRef => Schema.is(GitHubActionsCompletePreparedReleaseRef)(value)
135
+
136
+ /** Encode a validated complete reference to its single canonical CLI representation. */
137
+ export const encodeCompletePreparedReleaseRef = (reference: CompletePreparedReleaseRef): string =>
138
+ reference.scheme === "local"
139
+ ? `prepared:local:sha256-${reference.digest}`
140
+ : `prepared:gha:${reference.owner}/${reference.repository}/runs/${reference.runId}/attempts/${reference.attempt}/artifacts/${reference.artifactName}#sha256-${reference.digest}`
141
+
142
+ const githubActionsPayloadPattern =
143
+ /^([A-Za-z0-9._~-]{1,255})\/([A-Za-z0-9._~-]{1,255})\/runs\/([1-9][0-9]*)\/attempts\/([1-9][0-9]*)\/artifacts\/([A-Za-z0-9._~-]{1,255})#sha256-([a-f0-9]{64})$/u
144
+
145
+ /** Decode only the canonical `prepared:local:` and `prepared:gha:` grammars. */
146
+ export const decodeCompletePreparedReleaseRef = Effect.fn("decodeCompletePreparedReleaseRef")(
147
+ function*(input: unknown) {
148
+ if (typeof input !== "string") {
149
+ return yield* malformed("A prepared reference must be a string.")
150
+ }
151
+ if (!input.startsWith("prepared:")) {
152
+ return yield* malformed("A prepared reference must start with 'prepared:'.")
153
+ }
154
+ const schemeEnd = input.indexOf(":", "prepared:".length)
155
+ if (schemeEnd < 0) {
156
+ return yield* malformed("A prepared reference must include a scheme and payload.")
157
+ }
158
+ const scheme = input.slice("prepared:".length, schemeEnd)
159
+ const payload = input.slice(schemeEnd + 1)
160
+ if (scheme !== "local" && scheme !== "gha") {
161
+ if (scheme.length === 0) {
162
+ return yield* malformed("A prepared reference scheme must be nonempty.")
163
+ }
164
+ return yield* PreparedReleaseRefUnknownSchemeError.make({ scheme })
165
+ }
166
+ if (scheme === "local") {
167
+ const match = /^sha256-([a-f0-9]{64})$/u.exec(payload)
168
+ if (match === null) {
169
+ return yield* malformed("A local reference payload must be 'sha256-' followed by 64 lowercase hexadecimal characters.")
170
+ }
171
+ return yield* makeLocalCompletePreparedReleaseRef(match[1]!)
172
+ }
173
+ const match = githubActionsPayloadPattern.exec(payload)
174
+ if (match === null || match[1] === "." || match[1] === ".." || match[2] === "." || match[2] === ".." ||
175
+ match[5] === "." || match[5] === "..") {
176
+ return yield* malformed(
177
+ "A GitHub Actions reference must contain canonical owner, repository, run, attempt, artifact, and SHA-256 coordinates."
178
+ )
179
+ }
180
+ const reference = yield* makeGitHubActionsCompletePreparedReleaseRef({
181
+ owner: match[1]!,
182
+ repository: match[2]!,
183
+ runId: match[3]!,
184
+ attempt: match[4]!,
185
+ artifactName: match[5]!,
186
+ digest: match[6]!
187
+ })
188
+ if (encodeCompletePreparedReleaseRef(reference) !== input) {
189
+ return yield* malformed("The prepared reference is not in canonical form.")
190
+ }
191
+ return reference
192
+ }
193
+ )
@@ -0,0 +1,377 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as Context from "effect/Context"
3
+ import * as Schema from "effect/Schema"
4
+ import { constants, existsSync, lstatSync, mkdirSync, openSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, chmodSync, closeSync, fsyncSync } from "node:fs"
5
+ import { randomUUID } from "node:crypto"
6
+ import { basename, dirname, join } from "node:path"
7
+ import { secureRead, secureWrite } from "../drivers/workspace.js"
8
+ import { digestEquals, sha256Digest, Sha256Hex } from "../model/digest.js"
9
+ import { PreparedArtifact, PreparedReleaseV2, decodePreparedRelease, encodePreparedRelease } from "./prepared.js"
10
+ import { CompletePreparedReleaseRef, makeLocalCompletePreparedReleaseRef } from "./prepared-ref.js"
11
+
12
+ export class PreparedStoreError
13
+ extends Schema.TaggedErrorClass<PreparedStoreError>()("PreparedStoreError", { reason: Schema.String }) {}
14
+
15
+ export class PreparedStoreProvenanceError
16
+ extends Schema.TaggedErrorClass<PreparedStoreProvenanceError>()("PreparedStoreProvenanceError", {
17
+ scheme: Schema.Literals(["local", "gha"]),
18
+ reason: Schema.String
19
+ }) {}
20
+
21
+ export class LocalPreparedStoreProvenance
22
+ extends Schema.TaggedClass<LocalPreparedStoreProvenance>()("LocalPreparedStoreProvenance", {
23
+ scheme: Schema.Literal("local"),
24
+ filesystemRoot: Schema.NonEmptyString,
25
+ operatorBoundary: Schema.NonEmptyString
26
+ }) {}
27
+
28
+ export class GitHubActionsPreparedStoreProvenance
29
+ extends Schema.TaggedClass<GitHubActionsPreparedStoreProvenance>()("GitHubActionsPreparedStoreProvenance", {
30
+ scheme: Schema.Literal("gha"),
31
+ repository: Schema.NonEmptyString,
32
+ workflowRef: Schema.NonEmptyString,
33
+ workflowSha: Schema.NonEmptyString,
34
+ runId: Schema.NonEmptyString,
35
+ attempt: Schema.NonEmptyString,
36
+ candidateCommit: Schema.NonEmptyString,
37
+ artifactName: Schema.NonEmptyString,
38
+ artifactDigest: Schema.NonEmptyString,
39
+ allowedWriter: Schema.Literal("repository-workflow")
40
+ }) {}
41
+
42
+ export const PreparedStoreProvenance = Schema.Union([
43
+ LocalPreparedStoreProvenance,
44
+ GitHubActionsPreparedStoreProvenance
45
+ ])
46
+ export type PreparedStoreProvenance = typeof PreparedStoreProvenance.Type
47
+
48
+ const canonicalPathEquals = (left: string, right: string): boolean => {
49
+ try { return realpathSync(left) === realpathSync(right) } catch { return false }
50
+ }
51
+
52
+ /**
53
+ * GitHub artifact lookup is name-based and may span every rerun attempt in a
54
+ * workflow run. Bind the attempt as well as the content digest so an older
55
+ * prepared reference cannot be shadowed by a newer rerun's duplicate name.
56
+ */
57
+ export const githubActionsPreparedArtifactName = (attempt: string, digest: string): string =>
58
+ `ts-release-prepared-${attempt}-${digest}`
59
+
60
+ export const verifyPreparedStoreProvenance = Effect.fn("verifyPreparedStoreProvenance")(function*(input: {
61
+ readonly reference: CompletePreparedReleaseRef
62
+ readonly bundle: PreparedBundle
63
+ readonly evidence: PreparedStoreProvenance
64
+ }) {
65
+ if (input.reference.kind !== "complete" || input.bundle.manifest.kind !== "complete") {
66
+ return yield* new PreparedStoreProvenanceError({
67
+ scheme: input.reference.scheme,
68
+ reason: "Prepared store provenance verifies only complete references and bundles."
69
+ })
70
+ }
71
+ const digest = sha256Digest(encodePreparedRelease(input.bundle.manifest)).hex
72
+ if (digest !== input.reference.digest.toString()) {
73
+ return yield* new PreparedStoreProvenanceError({
74
+ scheme: input.reference.scheme,
75
+ reason: "Prepared reference digest does not match canonical complete-bundle contents."
76
+ })
77
+ }
78
+ if (input.reference.scheme === "local") {
79
+ if (input.evidence.scheme !== "local" || input.evidence.filesystemRoot.trim().length === 0 ||
80
+ input.evidence.operatorBoundary.trim().length === 0 ||
81
+ !canonicalPathEquals(input.evidence.filesystemRoot, dirname(input.bundle.directory))) {
82
+ return yield* new PreparedStoreProvenanceError({
83
+ scheme: "local",
84
+ reason: "Local prepared bytes require an explicit filesystem/operator trust boundary."
85
+ })
86
+ }
87
+ return input.evidence
88
+ }
89
+ if (input.evidence.scheme !== "gha") {
90
+ return yield* new PreparedStoreProvenanceError({
91
+ scheme: "gha",
92
+ reason: "Local evidence cannot be promoted into GitHub Actions trust by copying bytes or a digest."
93
+ })
94
+ }
95
+ const coordinate = `${input.reference.owner}/${input.reference.repository}`
96
+ const mismatches = [
97
+ input.evidence.repository !== coordinate ? "repository" : undefined,
98
+ input.evidence.runId !== input.reference.runId.toString() ? "runId" : undefined,
99
+ input.evidence.attempt !== input.reference.attempt.toString() ? "attempt" : undefined,
100
+ input.evidence.candidateCommit !== input.bundle.manifest.source.commit.toString() ? "candidateCommit" : undefined,
101
+ input.evidence.artifactName !== input.reference.artifactName.toString() ? "artifactName" : undefined,
102
+ input.evidence.artifactDigest !== input.reference.digest.toString() ? "artifactDigest" : undefined,
103
+ !input.evidence.workflowRef.startsWith(`${coordinate}/.github/workflows/`) ||
104
+ !input.evidence.workflowRef.includes("@refs/") ? "workflowRef" : undefined,
105
+ !/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/u.test(input.evidence.workflowSha) ? "workflowSha" : undefined,
106
+ input.reference.artifactName.toString() !== githubActionsPreparedArtifactName(
107
+ input.reference.attempt.toString(),
108
+ input.reference.digest.toString()
109
+ ) ? "immutableArtifactName" : undefined
110
+ ].filter((field): field is string => field !== undefined)
111
+ if (mismatches.length > 0) {
112
+ return yield* new PreparedStoreProvenanceError({
113
+ scheme: "gha",
114
+ reason: `Hosted prepared-store provenance mismatched ${mismatches.join(", ")}.`
115
+ })
116
+ }
117
+ return input.evidence
118
+ })
119
+
120
+ /**
121
+ * The bytes are already durable and verified, but the host failed to expose
122
+ * their recovery reference before publication could continue. This is a
123
+ * post-commit abort, never a preparation failure.
124
+ */
125
+ export class PreparedCommitHandoffError
126
+ extends Schema.TaggedErrorClass<PreparedCommitHandoffError>()("PreparedCommitHandoffError", {
127
+ prepared: CompletePreparedReleaseRef,
128
+ reason: Schema.String
129
+ }) {}
130
+
131
+ export const PreparedStoreFaultPoint = Schema.Literals([
132
+ "before-blob-write",
133
+ "after-blob-write",
134
+ "before-manifest-write",
135
+ "after-manifest-write",
136
+ "before-blob-directory-fsync",
137
+ "after-blob-directory-fsync",
138
+ "before-bundle-directory-fsync",
139
+ "after-bundle-directory-fsync",
140
+ "before-promotion",
141
+ "after-promotion",
142
+ "before-store-fsync",
143
+ "after-store-fsync",
144
+ "before-cleanup",
145
+ "after-cleanup"
146
+ ])
147
+ export type PreparedStoreFaultPoint = typeof PreparedStoreFaultPoint.Type
148
+
149
+ /** Host-only fault seam used to prove the atomic store protocol. */
150
+ export interface PreparedStoreOptions {
151
+ readonly onFaultPoint?: (point: PreparedStoreFaultPoint) => void
152
+ }
153
+
154
+ const equal = (left: Uint8Array, right: Uint8Array): boolean => left.length === right.length && left.every((byte, index) => byte === right[index])
155
+ const fail = (reason: string): never => { throw PreparedStoreError.make({ reason }) }
156
+ const causeReason = (cause: unknown): string =>
157
+ typeof cause === "object" && cause !== null && "reason" in cause && typeof cause.reason === "string"
158
+ ? cause.reason
159
+ : cause instanceof Error
160
+ ? cause.message
161
+ : String(cause)
162
+ const canonicalDirectory = (directory: string): string => {
163
+ if (!existsSync(directory)) mkdirSync(directory, { recursive: true, mode: 0o700 })
164
+ const real = realpathSync(directory)
165
+ if (real !== directory) fail("Prepared store root must not be a symlink.")
166
+ return real
167
+ }
168
+ const syncDirectory = (directory: string): void => {
169
+ const descriptor = openSync(directory, constants.O_RDONLY)
170
+ try { fsyncSync(descriptor) } finally { closeSync(descriptor) }
171
+ }
172
+ const artifactsById = (manifest: PreparedReleaseV2): Map<string, PreparedArtifact> => {
173
+ if (manifest.kind !== "complete") fail("Prepared store accepts only a kind:'complete' publication bundle.")
174
+ const result = new Map<string, PreparedArtifact>()
175
+ for (const artifact of manifest.artifacts) {
176
+ const id = artifact.id.toString()
177
+ if (result.has(id)) fail(`Prepared manifest repeats artifact ${id}.`)
178
+ if (!digestEquals(artifact.digest, artifact.blob)) {
179
+ fail(`Prepared artifact ${id} does not carry a canonical SHA-256 blob reference.`)
180
+ }
181
+ result.set(id, artifact)
182
+ }
183
+ return result
184
+ }
185
+ const validatePublications = (manifest: PreparedReleaseV2, artifacts: Map<string, PreparedArtifact>): void => {
186
+ const ids = new Set<string>()
187
+ for (const publication of manifest.publications) {
188
+ if (ids.has(publication.id.toString())) fail(`Prepared manifest repeats publication ${publication.id}.`)
189
+ ids.add(publication.id.toString())
190
+ const references = publication._tag === "PreparedNpmPublication"
191
+ ? [publication.artifactId]
192
+ : publication._tag === "PreparedPyPiPublication"
193
+ ? publication.files.map((file) => file.artifactId)
194
+ : publication._tag === "PreparedGitHubPublication"
195
+ ? publication.assets.map((asset) => asset.artifactId)
196
+ : [publication.targetArtifactId, publication.stateArtifactId]
197
+ for (const id of references) if (!artifacts.has(id.toString())) fail(`Publication ${publication.id} references missing artifact ${id}.`)
198
+ if (publication._tag === "PreparedPyPiPublication") for (const file of publication.files) {
199
+ const artifact = artifacts.get(file.artifactId.toString())!
200
+ if (artifact.size !== file.size || !digestEquals(artifact.digest, file.sha256) ||
201
+ artifact.mediaType !== file.mediaType || artifact.path.toString().split("/").at(-1) !== file.filename.toString()) {
202
+ fail(`PyPI publication ${publication.id} file ${file.filename} disagrees with its prepared artifact.`)
203
+ }
204
+ }
205
+ if (publication._tag === "PreparedGitHubPublication" && publication.body !== undefined && publication.body.length === 0) {
206
+ fail(`GitHub publication ${publication.id} carries an empty body.`)
207
+ }
208
+ }
209
+ }
210
+
211
+ export interface PreparedBundle {
212
+ readonly directory: string
213
+ readonly manifest: PreparedReleaseV2
214
+ readonly blobs: ReadonlyMap<string, Uint8Array>
215
+ }
216
+
217
+ export interface CommittedPreparedRelease {
218
+ readonly ref: CompletePreparedReleaseRef
219
+ readonly bundle: PreparedBundle
220
+ }
221
+
222
+ const readBundle = (directory: string): PreparedBundle => {
223
+ const real = realpathSync(directory)
224
+ if (real !== directory || lstatSync(directory).isSymbolicLink()) fail("Prepared bundle directory must not be a symlink.")
225
+ const bytes = secureRead(directory, "prepared-release.json").bytes
226
+ const manifest = decodePreparedRelease(bytes)
227
+ if (manifest.kind !== "complete") fail("Prepared store refuses every unknown or partial bundle kind.")
228
+ const manifestDigest = sha256Digest(bytes).hex
229
+ if (basename(directory) !== manifestDigest) fail("Prepared bundle directory does not match its manifest digest.")
230
+ const entries = readdirSync(directory, { withFileTypes: true })
231
+ if (entries.some((entry) => entry.name !== "prepared-release.json" && entry.name !== "blobs")) fail("Prepared bundle contains an unexpected top-level entry.")
232
+ const blobDirectory = join(directory, "blobs")
233
+ if (!existsSync(blobDirectory) || lstatSync(blobDirectory).isSymbolicLink() || !statSync(blobDirectory).isDirectory()) fail("Prepared bundle is missing its real blobs directory.")
234
+ const artifacts = artifactsById(manifest)
235
+ validatePublications(manifest, artifacts)
236
+ const expectedBlobs = new Set([...artifacts.values()].map((artifact) => artifact.blob.hex))
237
+ const actualBlobs = new Set(readdirSync(blobDirectory, { withFileTypes: true }).map((entry) => {
238
+ if (!entry.isFile() || !Schema.is(Sha256Hex)(entry.name)) fail(`Prepared blob entry ${entry.name} is invalid.`)
239
+ return entry.name
240
+ }))
241
+ if (actualBlobs.size !== expectedBlobs.size || [...expectedBlobs].some((blob) => !actualBlobs.has(blob))) fail("Prepared bundle blob set does not match its manifest.")
242
+ const blobs = new Map<string, Uint8Array>()
243
+ for (const artifact of artifacts.values()) {
244
+ const blob = secureRead(directory, `blobs/${artifact.blob.hex}`).bytes
245
+ if (blob.length !== artifact.size || !digestEquals(sha256Digest(blob), artifact.digest)) fail(`Prepared blob ${artifact.id} failed size or digest verification.`)
246
+ blobs.set(artifact.id.toString(), new Uint8Array(blob))
247
+ }
248
+ return { directory: real, manifest, blobs }
249
+ }
250
+
251
+ const atomicWrite = (directory: string, target: string, bytes: Uint8Array): void => {
252
+ const temporary = `${target}.${randomUUID()}.tmp`
253
+ secureWrite(directory, temporary, bytes)
254
+ renameSync(join(directory, temporary), join(directory, target))
255
+ chmodSync(join(directory, target), 0o400)
256
+ }
257
+
258
+ const writeBundle = (
259
+ storeDirectory: string,
260
+ manifest: PreparedReleaseV2,
261
+ blobs: ReadonlyMap<string, Uint8Array>,
262
+ options: PreparedStoreOptions = {}
263
+ ): PreparedBundle => {
264
+ const fault = options.onFaultPoint ?? (() => undefined)
265
+ const store = canonicalDirectory(storeDirectory)
266
+ const manifestBytes = encodePreparedRelease(manifest)
267
+ if (manifest.kind !== "complete") fail("Prepared store commits only a kind:'complete' publication bundle.")
268
+ const manifestDigest = sha256Digest(manifestBytes).hex
269
+ const artifacts = artifactsById(manifest)
270
+ validatePublications(manifest, artifacts)
271
+ for (const artifact of artifacts.values()) {
272
+ const bytes = blobs.get(artifact.id.toString()) ?? fail(`No prepared bytes supplied for artifact ${artifact.id}.`)
273
+ if (bytes.length !== artifact.size || !digestEquals(sha256Digest(bytes), artifact.digest) || !digestEquals(artifact.blob, artifact.digest)) fail(`Prepared bytes do not match artifact ${artifact.id}.`)
274
+ }
275
+ const finalDirectory = join(store, manifestDigest)
276
+ if (existsSync(finalDirectory)) {
277
+ const existing = readBundle(finalDirectory)
278
+ if (!equal(secureRead(finalDirectory, "prepared-release.json").bytes, manifestBytes)) fail("Existing prepared bundle has a different manifest.")
279
+ return existing
280
+ }
281
+ const temporary = join(store, `.${manifestDigest}.${randomUUID()}.tmp`)
282
+ mkdirSync(join(temporary, "blobs"), { recursive: true, mode: 0o700 })
283
+ let promoted = false
284
+ try {
285
+ for (const artifact of artifacts.values()) {
286
+ fault("before-blob-write")
287
+ atomicWrite(join(temporary, "blobs"), artifact.blob.hex, blobs.get(artifact.id.toString())!)
288
+ fault("after-blob-write")
289
+ }
290
+ fault("before-manifest-write")
291
+ atomicWrite(temporary, "prepared-release.json", manifestBytes)
292
+ fault("after-manifest-write")
293
+ fault("before-blob-directory-fsync")
294
+ syncDirectory(join(temporary, "blobs"))
295
+ fault("after-blob-directory-fsync")
296
+ fault("before-bundle-directory-fsync")
297
+ syncDirectory(temporary)
298
+ fault("after-bundle-directory-fsync")
299
+ fault("before-promotion")
300
+ renameSync(temporary, finalDirectory)
301
+ promoted = true
302
+ fault("after-promotion")
303
+ fault("before-store-fsync")
304
+ syncDirectory(store)
305
+ fault("after-store-fsync")
306
+ } catch (cause) {
307
+ let cleanupFailure: unknown
308
+ if (existsSync(temporary)) {
309
+ try {
310
+ fault("before-cleanup")
311
+ rmSync(temporary, { recursive: true, force: true })
312
+ fault("after-cleanup")
313
+ } catch (cleanupCause) {
314
+ cleanupFailure = cleanupCause
315
+ }
316
+ }
317
+ if (!promoted && existsSync(finalDirectory)) {
318
+ const existing = readBundle(finalDirectory)
319
+ if (!equal(secureRead(finalDirectory, "prepared-release.json").bytes, manifestBytes)) {
320
+ fail("Concurrent prepared-store promotion produced a different manifest.")
321
+ }
322
+ return existing
323
+ }
324
+ if (cleanupFailure !== undefined) {
325
+ throw PreparedStoreError.make({
326
+ reason: `Prepared-store cleanup failed after ${cause instanceof Error ? cause.message : String(cause)}: ${cleanupFailure instanceof Error ? cleanupFailure.message : String(cleanupFailure)}`
327
+ })
328
+ }
329
+ if (cause instanceof PreparedStoreError) throw cause
330
+ throw PreparedStoreError.make({ reason: cause instanceof Error ? cause.message : String(cause) })
331
+ }
332
+ return readBundle(finalDirectory)
333
+ }
334
+
335
+ export const storePreparedRelease = Effect.fn("storePreparedRelease")((
336
+ storeDirectory: string,
337
+ manifest: PreparedReleaseV2,
338
+ blobs: ReadonlyMap<string, Uint8Array>,
339
+ options?: PreparedStoreOptions
340
+ ) => Effect.try({ try: () => writeBundle(storeDirectory, manifest, blobs, options), catch: (cause) =>
341
+ cause instanceof PreparedStoreError ? cause : PreparedStoreError.make({ reason: causeReason(cause) })
342
+ }))
343
+
344
+ export const loadPreparedRelease = Effect.fn("loadPreparedRelease")((directory: string) => Effect.try({
345
+ try: () => readBundle(directory),
346
+ catch: (cause) => cause instanceof PreparedStoreError ? cause : PreparedStoreError.make({ reason: causeReason(cause) })
347
+ }))
348
+
349
+ /** The durable store boundary used by coordinators and host projections. */
350
+ export interface PreparedReleaseStoreShape {
351
+ readonly commit: (
352
+ manifest: PreparedReleaseV2,
353
+ blobs: ReadonlyMap<string, Uint8Array>
354
+ ) => Effect.Effect<CommittedPreparedRelease, PreparedStoreError | PreparedCommitHandoffError>
355
+ readonly load: (reference: CompletePreparedReleaseRef) => Effect.Effect<PreparedBundle, PreparedStoreError>
356
+ }
357
+
358
+ export class PreparedReleaseStore
359
+ extends Context.Service<PreparedReleaseStore, PreparedReleaseStoreShape>()("ts-release/PreparedReleaseStore") {}
360
+
361
+ /** A local store resolves digest-only references against one explicit root. */
362
+ export const makeLocalPreparedReleaseStore = (
363
+ storeDirectory: string,
364
+ options?: PreparedStoreOptions
365
+ ): PreparedReleaseStoreShape => ({
366
+ commit: (manifest, blobs) => storePreparedRelease(storeDirectory, manifest, blobs, options).pipe(
367
+ Effect.flatMap((bundle) => makeLocalCompletePreparedReleaseRef(basename(bundle.directory)).pipe(
368
+ Effect.map((ref) => ({ ref, bundle })),
369
+ Effect.mapError((cause) => PreparedStoreError.make({ reason: cause.reason }))
370
+ ))
371
+ ),
372
+ load: (reference) => reference.scheme === "local"
373
+ ? loadPreparedRelease(join(storeDirectory, reference.digest))
374
+ : Effect.fail(PreparedStoreError.make({
375
+ reason: "A GitHub Actions prepared reference is not loadable by the local store; rerun the failed workflow publish job or dispatch its recovery workflow."
376
+ }))
377
+ })