@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,772 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { chmod, copyFile, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { basename, isAbsolute, join, resolve } from "node:path";
5
+ import * as Config from "effect/Config";
6
+ import * as Effect from "effect/Effect";
7
+ import * as Exit from "effect/Exit";
8
+ import * as Layer from "effect/Layer";
9
+ import * as Option from "effect/Option";
10
+ import * as Redacted from "effect/Redacted";
11
+ import * as Schema from "effect/Schema";
12
+ import * as Stream from "effect/Stream";
13
+ import * as ChildProcess from "effect/unstable/process/ChildProcess";
14
+ import * as Semver from "semver";
15
+ import { EnvironmentName } from "../model/authority.js";
16
+ import { redactOutput } from "../drivers/redact.js";
17
+ import { CredentialAudienceMismatch, CredentialProvider, CredentialPurposeMismatch, CredentialStrategyUnsupported, CredentialSubjectMismatch, CredentialUnavailable, makeCredentialProvider, validateGrantForOperation } from "../publication/authority.js";
18
+ import { AuthorizedMutationHttp, HttpAuthorizer } from "../publication/http.js";
19
+ import { CertifiedPublisherSpawn, NpmUserConfigResource, PublisherExited, PublisherOutcomeUnknown, RejectedBeforeStart, makeNpmUserConfigHandle, npmPublishArgv } from "../publication/publisher.js";
20
+ import { canonicalizeRegistryUrl } from "../release/graph.js";
21
+ export { AuthorizedMutationHttp, CertifiedPublisherSpawn, HttpAuthorizer, NpmUserConfigResource, PublisherExited, PublisherOutcomeUnknown, RejectedBeforeStart };
22
+ export class CredentialPlatformError extends Schema.TaggedErrorClass()("CredentialPlatformError", {
23
+ phase: Schema.Literals(["observe", "mutate", "resource", "spawn"]),
24
+ commitment: Schema.Literals(["before-dispatch", "unknown"]),
25
+ reason: Schema.NonEmptyString
26
+ }) {
27
+ }
28
+ // These are deliberately the only literal spellings in production code. The
29
+ // import-rules AST gate keeps OIDC request material in this host module.
30
+ const oidcRequestUrlName = EnvironmentName.make("ACTIONS_ID_TOKEN_REQUEST_URL");
31
+ const oidcRequestTokenName = EnvironmentName.make("ACTIONS_ID_TOKEN_REQUEST_TOKEN");
32
+ const oidcNames = [oidcRequestUrlName, oidcRequestTokenName];
33
+ const githubActionsName = "GITHUB_ACTIONS";
34
+ const githubRepositoryName = "GITHUB_REPOSITORY";
35
+ const githubWorkflowRefName = "GITHUB_WORKFLOW_REF";
36
+ const githubServerUrlName = "GITHUB_SERVER_URL";
37
+ const githubEventName = "GITHUB_EVENT_NAME";
38
+ const githubRepositoryIdName = "GITHUB_REPOSITORY_ID";
39
+ const githubRepositoryOwnerIdName = "GITHUB_REPOSITORY_OWNER_ID";
40
+ const githubRefName = "GITHUB_REF";
41
+ const githubShaName = "GITHUB_SHA";
42
+ const runnerEnvironmentName = "RUNNER_ENVIRONMENT";
43
+ const githubRunIdName = "GITHUB_RUN_ID";
44
+ const githubRunAttemptName = "GITHUB_RUN_ATTEMPT";
45
+ const portableEnvironmentName = /^[A-Za-z_][A-Za-z0-9_]*$/u;
46
+ const certifiedWorkflow = /^\.github\/workflows\/[A-Za-z0-9_.-]+\.ya?ml$/u;
47
+ const certifiedRepository = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/u;
48
+ const certifiedWorkflowRef = /^(?:refs\/(?:heads|tags)\/[A-Za-z0-9][A-Za-z0-9._/-]*|[a-f0-9]{40}(?:[a-f0-9]{24})?)$/u;
49
+ const certifiedSourceCommit = /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/u;
50
+ const certifiedEvent = /^[a-z][a-z0-9_]*$/u;
51
+ const certifiedDecimalId = /^[1-9][0-9]*$/u;
52
+ const certifiedSourceRef = /^refs\/(?:heads|tags)\/[A-Za-z0-9][A-Za-z0-9._/-]*$/u;
53
+ const rememberObservationGrant = (observationGrants, grant) => {
54
+ observationGrants.set(grant, grant._tag === "AnonymousAccess"
55
+ ? {
56
+ _tag: grant._tag,
57
+ subject: grant.subject,
58
+ audience: grant.audience,
59
+ purposes: new Set(grant.purposes)
60
+ }
61
+ : {
62
+ _tag: grant._tag,
63
+ subject: grant.subject,
64
+ audience: grant.audience,
65
+ purposes: new Set(grant.purposes),
66
+ ref: grant.ref
67
+ });
68
+ };
69
+ const vaultKey = (subject, name) => `${subject}\u0000${name}`;
70
+ const platformError = (phase, commitment, reason) => new CredentialPlatformError({ phase, commitment, reason });
71
+ const unavailable = (request, reason) => new CredentialUnavailable({
72
+ subject: request.subject,
73
+ provider: request.provider,
74
+ purpose: request.purpose,
75
+ reason
76
+ });
77
+ const unsupported = (request, reason) => new CredentialStrategyUnsupported({
78
+ subject: request.subject,
79
+ provider: request.provider,
80
+ strategy: request.strategy.kind,
81
+ reason
82
+ });
83
+ const readSecret = (request, name) => Config.redacted(name).pipe(Effect.mapError(() => unavailable(request, `Credential reference ${name} is unavailable.`)));
84
+ const trustedStrategyIssue = (request) => {
85
+ const strategy = request.strategy;
86
+ if (strategy.kind !== "trusted-publishing")
87
+ return "The request does not carry trusted-publishing intent.";
88
+ if (request.provider !== "npm" || request.audience !== "https://registry.npmjs.org/" ||
89
+ request.purpose !== "publish") {
90
+ return "Trusted publishing is certified only for npm publish at the canonical npm registry.";
91
+ }
92
+ if (strategy.identityProvider !== "github-actions" || strategy.runnerClass !== "github-hosted" ||
93
+ !certifiedRepository.test(strategy.repository) || !certifiedWorkflow.test(strategy.workflow) ||
94
+ !certifiedWorkflowRef.test(strategy.workflowRef) ||
95
+ !certifiedSourceCommit.test(strategy.sourceCommit) ||
96
+ strategy.provenanceEnvironmentContract !== "github-actions-npm-provenance-v1" ||
97
+ strategy.allowedAction !== "npm-publish-direct" || strategy.publisherSink !== "certified-npm-cli") {
98
+ return "Trusted publishing requires one exact GitHub-hosted workflow/ref, source commit, provenance environment contract, and certified direct npm sink.";
99
+ }
100
+ return undefined;
101
+ };
102
+ /** Validate non-secret GitHub host facts before either OIDC request value is read. */
103
+ const verifyTrustedHostIdentity = Effect.fn("EnvironmentCredentialProvider.verifyTrustedHostIdentity")(function* (request) {
104
+ const issue = trustedStrategyIssue(request);
105
+ if (issue !== undefined)
106
+ return yield* unsupported(request, issue);
107
+ if (request.strategy.kind !== "trusted-publishing")
108
+ return yield* unsupported(request, issue ?? "Invalid strategy.");
109
+ const expected = [
110
+ [githubActionsName, "true"],
111
+ [githubRepositoryName, request.strategy.repository],
112
+ [githubWorkflowRefName,
113
+ `${request.strategy.repository}/${request.strategy.workflow}@${request.strategy.workflowRef}`],
114
+ [githubServerUrlName, "https://github.com"],
115
+ [githubShaName, request.strategy.sourceCommit],
116
+ [runnerEnvironmentName, "github-hosted"]
117
+ ];
118
+ const values = {};
119
+ for (const [name, value] of expected) {
120
+ const observed = yield* Config.string(name).pipe(Effect.mapError(() => unsupported(request, `Trusted publishing host identity ${name} is unavailable.`)));
121
+ if (observed !== value) {
122
+ return yield* unsupported(request, `Trusted publishing host identity ${name} does not match prepared intent.`);
123
+ }
124
+ values[name] = observed;
125
+ }
126
+ const shaped = [
127
+ [githubEventName, certifiedEvent, "a canonical GitHub event name"],
128
+ [githubRepositoryIdName, certifiedDecimalId, "a canonical positive decimal repository ID"],
129
+ [githubRepositoryOwnerIdName, certifiedDecimalId, "a canonical positive decimal repository-owner ID"],
130
+ [githubRefName, certifiedSourceRef, "a canonical heads/tags source ref"],
131
+ [githubRunIdName, certifiedDecimalId, "a canonical positive decimal run ID"],
132
+ [githubRunAttemptName, certifiedDecimalId, "a canonical positive decimal run attempt"]
133
+ ];
134
+ for (const [name, pattern, description] of shaped) {
135
+ const observed = yield* Config.string(name).pipe(Effect.mapError(() => unsupported(request, `Trusted publishing provenance fact ${name} is unavailable.`)));
136
+ if (!pattern.test(observed)) {
137
+ return yield* unsupported(request, `Trusted publishing provenance fact ${name} is not ${description}.`);
138
+ }
139
+ values[name] = observed;
140
+ }
141
+ return Object.freeze({
142
+ GITHUB_ACTIONS: values[githubActionsName],
143
+ GITHUB_REPOSITORY: values[githubRepositoryName],
144
+ GITHUB_WORKFLOW_REF: values[githubWorkflowRefName],
145
+ GITHUB_SERVER_URL: values[githubServerUrlName],
146
+ GITHUB_EVENT_NAME: values[githubEventName],
147
+ GITHUB_REPOSITORY_ID: values[githubRepositoryIdName],
148
+ GITHUB_REPOSITORY_OWNER_ID: values[githubRepositoryOwnerIdName],
149
+ GITHUB_REF: values[githubRefName],
150
+ GITHUB_SHA: values[githubShaName],
151
+ RUNNER_ENVIRONMENT: values[runnerEnvironmentName],
152
+ GITHUB_RUN_ID: values[githubRunIdName],
153
+ GITHUB_RUN_ATTEMPT: values[githubRunAttemptName]
154
+ });
155
+ });
156
+ /**
157
+ * A raw environment token has no host-verifiable downscope metadata. Treat it
158
+ * conservatively as bundled provider authority instead of relabeling the same
159
+ * bearer as a purpose-specific secret merely because one method requested it.
160
+ */
161
+ const environmentTokenPurposes = (purpose) => purpose === "correct"
162
+ ? ["observe", "publish", "correct"]
163
+ : ["observe", "publish"];
164
+ const makeEnvironmentAcquirer = (vault, onTrustedWorkload) => ({
165
+ acquire: Effect.fn("EnvironmentCredentialProvider.acquire")(function* (request) {
166
+ switch (request.strategy.kind) {
167
+ case "anonymous":
168
+ return { _tag: "AnonymousAccess", purposes: ["observe"] };
169
+ case "token": {
170
+ if (request.provider === "npm" && request.purpose === "observe") {
171
+ return yield* unsupported(request, "Authenticated npm observation is unsupported without a distinct typed read credential.");
172
+ }
173
+ const name = request.strategy.credential.toString();
174
+ if (!portableEnvironmentName.test(name)) {
175
+ return yield* unsupported(request, "Token credential references must be portable environment names.");
176
+ }
177
+ const value = yield* readSecret(request, name);
178
+ vault.set(vaultKey(request.subject, name), value);
179
+ return {
180
+ _tag: "ScopedSecret",
181
+ purposes: environmentTokenPurposes(request.purpose),
182
+ ref: request.strategy.credential
183
+ };
184
+ }
185
+ case "trusted-publishing": {
186
+ if (onTrustedWorkload === undefined) {
187
+ return yield* unsupported(request, "Trusted workload acquisition requires a private host-material binding.");
188
+ }
189
+ const snapshot = yield* verifyTrustedHostIdentity(request);
190
+ const oidcValues = new Map();
191
+ for (const name of oidcNames) {
192
+ const value = yield* readSecret(request, name);
193
+ oidcValues.set(name, value);
194
+ }
195
+ onTrustedWorkload(Object.freeze({ snapshot, oidc: oidcValues }));
196
+ return {
197
+ _tag: "WorkloadIdentity",
198
+ purposes: [request.purpose],
199
+ names: oidcNames
200
+ };
201
+ }
202
+ }
203
+ })
204
+ });
205
+ const makeProvider = (vault, trustedWorkloads, observationGrants) => {
206
+ const provider = makeCredentialProvider(makeEnvironmentAcquirer(vault));
207
+ return {
208
+ acquireForObservation: Effect.fn("EnvironmentCredentialProvider.acquireForObservation")(function* (request) {
209
+ const grant = yield* provider.acquireForObservation(request);
210
+ if (grant._tag !== "WorkloadIdentity") {
211
+ rememberObservationGrant(observationGrants, grant);
212
+ }
213
+ return grant;
214
+ }),
215
+ acquireForMutation: Effect.fn("EnvironmentCredentialProvider.acquireForMutation")(function* (request, decision) {
216
+ let grant;
217
+ if (request.strategy.kind === "trusted-publishing") {
218
+ let material;
219
+ const trustedProvider = makeCredentialProvider(makeEnvironmentAcquirer(vault, (acquired) => { material = acquired; }));
220
+ grant = yield* trustedProvider.acquireForMutation(request, decision);
221
+ if (grant._tag !== "WorkloadIdentity" || material === undefined) {
222
+ return yield* unavailable(request, "Trusted publishing host evidence was not bound to its workload grant.");
223
+ }
224
+ trustedWorkloads.set(grant, material);
225
+ }
226
+ else {
227
+ grant = yield* provider.acquireForMutation(request, decision);
228
+ }
229
+ // npm publication tokens never become observation capabilities. GitHub
230
+ // retains its truthfully bundled installation-token read/write model.
231
+ if (grant._tag === "ScopedSecret" && request.provider !== "npm") {
232
+ rememberObservationGrant(observationGrants, grant);
233
+ }
234
+ return grant;
235
+ })
236
+ };
237
+ };
238
+ /** Environment-backed provider projection for hosts that need no sinks. */
239
+ export const makeEnvironmentCredentialProvider = () => makeProvider(new Map(), new WeakMap(), new WeakMap());
240
+ const hasAuthorizationHeader = (headers) => headers !== undefined && Object.keys(headers).some((name) => name.toLowerCase() === "authorization");
241
+ const audienceAllows = (audience, requested) => {
242
+ try {
243
+ const expected = new URL(audience);
244
+ const observed = new URL(requested);
245
+ if (expected.protocol !== "https:" || observed.protocol !== "https:")
246
+ return false;
247
+ if (expected.username !== "" || expected.password !== "" || expected.search !== "" || expected.hash !== "")
248
+ return false;
249
+ if (expected.origin !== observed.origin)
250
+ return false;
251
+ const base = expected.pathname.endsWith("/") ? expected.pathname : `${expected.pathname}/`;
252
+ return observed.pathname === expected.pathname || observed.pathname.startsWith(base);
253
+ }
254
+ catch {
255
+ return false;
256
+ }
257
+ };
258
+ const githubUploadAudienceAllows = (audience, requested) => {
259
+ try {
260
+ const expected = new URL(audience);
261
+ const observed = new URL(requested);
262
+ if (expected.protocol !== "https:" || expected.hostname !== "api.github.com" ||
263
+ expected.port !== "" || expected.username !== "" || expected.password !== "" ||
264
+ expected.search !== "" || expected.hash !== "")
265
+ return false;
266
+ if (observed.protocol !== "https:" || observed.hostname !== "uploads.github.com" ||
267
+ observed.port !== "" || observed.username !== "" || observed.password !== "" ||
268
+ observed.hash !== "")
269
+ return false;
270
+ const repository = /^\/repos\/[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/u.exec(expected.pathname)?.[0];
271
+ if (repository === undefined ||
272
+ !new RegExp(`^${repository.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}\/releases\/[1-9][0-9]*\/assets$`, "u")
273
+ .test(observed.pathname))
274
+ return false;
275
+ const keys = [...observed.searchParams.keys()];
276
+ return /^\?name=[^&]+(?:&label=[^&]*)?$/u.test(observed.search) &&
277
+ new Set(keys).size === keys.length && observed.searchParams.get("name").length > 0;
278
+ }
279
+ catch {
280
+ return false;
281
+ }
282
+ };
283
+ const checkAudience = (subject, audience, requested, allowGithubUpload = false) => audienceAllows(audience, requested) ||
284
+ (allowGithubUpload && githubUploadAudienceAllows(audience, requested))
285
+ ? Effect.void
286
+ : Effect.fail(new CredentialAudienceMismatch({
287
+ subject,
288
+ expected: audience,
289
+ observed: requested
290
+ }));
291
+ const lookupToken = (vault, subject, ref) => {
292
+ const value = vault.get(vaultKey(subject, ref));
293
+ return value === undefined
294
+ ? Effect.fail(new CredentialUnavailable({
295
+ subject,
296
+ provider: "unknown",
297
+ purpose: "unknown",
298
+ reason: "The credential grant has no host-owned secret in this platform boundary."
299
+ }))
300
+ : Effect.succeed(value);
301
+ };
302
+ const validateObservationGrant = Effect.fn("HttpAuthorizer.validateGrant")(function* (input, grant, observationGrants) {
303
+ const issued = observationGrants.get(grant);
304
+ if (issued === undefined) {
305
+ return yield* new CredentialUnavailable({
306
+ subject: input.subject,
307
+ provider: "unknown",
308
+ purpose: "observe",
309
+ reason: "The supplied observation grant was not issued by this platform boundary."
310
+ });
311
+ }
312
+ if (issued.subject !== input.subject) {
313
+ return yield* new CredentialSubjectMismatch({
314
+ expected: input.subject,
315
+ observed: issued.subject
316
+ });
317
+ }
318
+ if (!issued.purposes.has("observe")) {
319
+ return yield* new CredentialPurposeMismatch({
320
+ subject: input.subject,
321
+ required: "observe",
322
+ granted: [...issued.purposes]
323
+ });
324
+ }
325
+ yield* checkAudience(input.subject, issued.audience, input.url);
326
+ return issued;
327
+ });
328
+ const sendAnonymous = (http, request) => {
329
+ if (hasAuthorizationHeader(request.headers)) {
330
+ return Effect.fail(platformError("observe", "before-dispatch", "Callers may not supply an Authorization header."));
331
+ }
332
+ return http.request({
333
+ method: request.method,
334
+ url: request.url,
335
+ ...(request.headers === undefined ? {} : { headers: request.headers }),
336
+ ...(request.body === undefined ? {} : { body: request.body })
337
+ }).pipe(Effect.mapError(() => platformError("observe", "unknown", "Observation HTTP transport failed after dispatch.")));
338
+ };
339
+ const sendAuthorized = (http, request, token, phase, credentialScheme = "bearer") => {
340
+ if (hasAuthorizationHeader(request.headers)) {
341
+ return Effect.fail(platformError(phase, "before-dispatch", "Callers may not supply an Authorization header."));
342
+ }
343
+ const headers = {
344
+ ...(request.headers ?? {}),
345
+ authorization: credentialScheme === "pypi-token-basic"
346
+ ? `Basic ${Buffer.from(`__token__:${Redacted.value(token)}`, "utf8").toString("base64")}`
347
+ : `Bearer ${Redacted.value(token)}`
348
+ };
349
+ return http.request({
350
+ method: request.method,
351
+ url: request.url,
352
+ headers,
353
+ ...(request.body === undefined ? {} : { body: request.body })
354
+ }).pipe(Effect.mapError(() => platformError(phase, "unknown", "Authorized HTTP transport failed after dispatch.")));
355
+ };
356
+ const makeHttpAuthorizer = (http, vault, observationGrants) => ({
357
+ execute: Effect.fn("HttpAuthorizer.execute")(function* (input, grant) {
358
+ const issued = yield* validateObservationGrant(input, grant, observationGrants);
359
+ if (issued._tag === "AnonymousAccess") {
360
+ return yield* sendAnonymous(http, input);
361
+ }
362
+ const token = yield* lookupToken(vault, issued.subject, issued.ref);
363
+ return yield* sendAuthorized(http, input, token, "observe");
364
+ })
365
+ });
366
+ const makeAuthorizedMutationHttp = (http, vault) => ({
367
+ execute: Effect.fn("AuthorizedMutationHttp.execute")(function* (operation, request, grant) {
368
+ yield* validateGrantForOperation(operation, grant);
369
+ if ((operation.provider === "pypi") !== (request.credentialScheme === "pypi-token-basic")) {
370
+ return yield* platformError("mutate", "before-dispatch", "The PyPI Basic credential projection is accepted only for typed PyPI mutations and is mandatory for them.");
371
+ }
372
+ yield* checkAudience(operation.subject, operation.audience, request.url, operation.provider === "github");
373
+ const token = yield* lookupToken(vault, grant.subject, grant.ref);
374
+ return yield* sendAuthorized(http, request, token, "mutate", request.credentialScheme);
375
+ })
376
+ });
377
+ const npmUserConfigs = new WeakMap();
378
+ const npmAuthLine = (registryUrl, token) => {
379
+ const registry = new URL(registryUrl);
380
+ const pathname = registry.pathname.endsWith("/") ? registry.pathname : `${registry.pathname}/`;
381
+ return `//${registry.host}${pathname}:_authToken=${Redacted.value(token)}`;
382
+ };
383
+ const makeNpmUserConfigResource = (vault, temporaryRoot) => ({
384
+ acquire: Effect.fn("NpmUserConfigResource.acquire")(function* (input, grant) {
385
+ yield* validateGrantForOperation(input.operation, grant);
386
+ if (input.operation.provider !== "npm") {
387
+ return yield* platformError("resource", "before-dispatch", "npm user config accepts only the npm provider.");
388
+ }
389
+ yield* checkAudience(input.operation.subject, input.operation.audience, input.registryUrl);
390
+ const token = yield* lookupToken(vault, grant.subject, grant.ref);
391
+ const root = temporaryRoot ?? tmpdir();
392
+ const handle = makeNpmUserConfigHandle();
393
+ const acquired = Effect.tryPromise({
394
+ try: async () => {
395
+ await mkdir(root, { recursive: true, mode: 0o700 });
396
+ const directory = await mkdtemp(join(root, "ts-release-npm-"));
397
+ try {
398
+ const path = join(directory, "userconfig");
399
+ const contents = `${npmAuthLine(input.registryUrl, token)}\nignore-scripts=true\n`;
400
+ await writeFile(path, contents, { mode: 0o600, flag: "wx" });
401
+ npmUserConfigs.set(handle, { directory, path, subject: input.operation.subject, grant, token });
402
+ return handle;
403
+ }
404
+ catch (cause) {
405
+ await rm(directory, { recursive: true, force: true });
406
+ throw cause;
407
+ }
408
+ },
409
+ catch: () => platformError("resource", "before-dispatch", "Unable to create the scoped npm user config.")
410
+ });
411
+ return yield* Effect.acquireRelease(acquired, (resource) => {
412
+ const metadata = npmUserConfigs.get(resource);
413
+ npmUserConfigs.delete(resource);
414
+ return metadata === undefined
415
+ ? Effect.void
416
+ : Effect.tryPromise({
417
+ try: () => rm(metadata.directory, { recursive: true, force: true }),
418
+ catch: () => undefined
419
+ }).pipe(Effect.catch(() => Effect.void));
420
+ });
421
+ })
422
+ });
423
+ const collect = (stream) => Stream.mkString(Stream.decodeText(stream));
424
+ const minimumTrustedNode = [22, 14, 0];
425
+ const minimumTrustedNpm = [11, 5, 1];
426
+ const parseVersion = (value) => {
427
+ const match = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?\s*$/u.exec(value);
428
+ if (match === null)
429
+ return undefined;
430
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
431
+ };
432
+ const versionAtLeast = (value, minimum) => value[0] > minimum[0] ||
433
+ (value[0] === minimum[0] && (value[1] > minimum[1] ||
434
+ (value[1] === minimum[1] && value[2] >= minimum[2])));
435
+ const optionalPath = Config.option(Config.string("PATH")).pipe(Effect.map(Option.getOrUndefined), Effect.orElseSucceed(() => undefined));
436
+ const closedBaseEnvironment = (path) => path === undefined ? {} : { PATH: path };
437
+ const workloadEnvironment = Effect.fn("CertifiedPublisherSpawn.workloadEnvironment")(function* (trustedWorkloads, grant) {
438
+ if (grant.names.size !== oidcNames.length || oidcNames.some((name) => !grant.names.has(name))) {
439
+ return yield* new CredentialStrategyUnsupported({
440
+ subject: grant.subject,
441
+ provider: grant.provider,
442
+ strategy: "trusted-publishing",
443
+ reason: "Workload identity lacks the exact certified GitHub Actions OIDC names."
444
+ });
445
+ }
446
+ const material = trustedWorkloads.get(grant);
447
+ if (material === undefined) {
448
+ return yield* new CredentialUnavailable({
449
+ subject: grant.subject,
450
+ provider: grant.provider,
451
+ purpose: "publish",
452
+ reason: "Certified GitHub Actions provenance facts are unavailable in this platform boundary."
453
+ });
454
+ }
455
+ const path = yield* optionalPath;
456
+ const env = closedBaseEnvironment(path);
457
+ for (const name of oidcNames) {
458
+ const value = material.oidc.get(name);
459
+ if (value === undefined) {
460
+ return yield* new CredentialUnavailable({
461
+ subject: grant.subject,
462
+ provider: grant.provider,
463
+ purpose: "publish",
464
+ reason: "Certified workload identity material is unavailable in this platform boundary."
465
+ });
466
+ }
467
+ env[name] = Redacted.value(value);
468
+ }
469
+ // npm's OIDC exchange consumes the two host-held request values. Its
470
+ // libnpmpublish provenance builder separately consumes this exact validated
471
+ // non-secret GitHub run snapshot. No ambient environment is inherited.
472
+ Object.assign(env, material.snapshot);
473
+ env.NPM_CONFIG_IGNORE_SCRIPTS = "true";
474
+ return env;
475
+ });
476
+ const validateTrustedNpmOperation = Effect.fn("CertifiedPublisherSpawn.validateTrustedNpmOperation")(function* (operation, grant) {
477
+ if (operation.provider !== "npm" || operation.purpose !== "publish" ||
478
+ operation.audience !== "https://registry.npmjs.org/" ||
479
+ grant.strategy.identityProvider !== "github-actions" ||
480
+ grant.strategy.runnerClass !== "github-hosted" ||
481
+ grant.strategy.allowedAction !== "npm-publish-direct" ||
482
+ grant.strategy.publisherSink !== "certified-npm-cli") {
483
+ return yield* new CredentialStrategyUnsupported({
484
+ subject: operation.subject,
485
+ provider: operation.provider,
486
+ strategy: "trusted-publishing",
487
+ reason: "The workload grant is not bound to the certified direct npm publisher sink."
488
+ });
489
+ }
490
+ });
491
+ const publisherSpecKeys = Object.freeze({
492
+ NpmPublisherSpec: [
493
+ "_tag", "access", "cwd", "distTag", "operation", "packageName", "provenance",
494
+ "registryUrl", "tarballPath", "userConfig", "version"
495
+ ],
496
+ WorkloadPublisherSpec: [
497
+ "_tag", "access", "cwd", "distTag", "operation", "packageName", "provenance",
498
+ "registryUrl", "tarballPath", "version"
499
+ ]
500
+ });
501
+ const sameKeys = (value, expected) => {
502
+ const observed = Object.keys(value).sort();
503
+ return observed.length === expected.length && observed.every((key, index) => key === expected[index]);
504
+ };
505
+ const certifiedNpmSpecIssue = (spec) => {
506
+ const tagDescriptor = Object.getOwnPropertyDescriptor(spec, "_tag");
507
+ const tag = tagDescriptor !== undefined && "value" in tagDescriptor
508
+ ? tagDescriptor.value
509
+ : undefined;
510
+ if (tag !== "NpmPublisherSpec" && tag !== "WorkloadPublisherSpec") {
511
+ return "The certified npm publisher accepts only its two closed typed spec variants.";
512
+ }
513
+ const expectedKeys = publisherSpecKeys[tag];
514
+ const descriptors = Object.getOwnPropertyDescriptors(spec);
515
+ if (!sameKeys(spec, expectedKeys) || expectedKeys.some((key) => {
516
+ const descriptor = descriptors[key];
517
+ return descriptor === undefined || !("value" in descriptor);
518
+ })) {
519
+ return "The certified npm publisher accepts only its exact typed fields; generic argv and override fields are forbidden.";
520
+ }
521
+ const operation = spec.operation;
522
+ if (operation._tag !== "PublishOperation" || operation.provider !== "npm" || operation.purpose !== "publish") {
523
+ return "The certified npm publisher accepts only an npm publish operation.";
524
+ }
525
+ if (typeof spec.packageName !== "string" || spec.packageName.length === 0 ||
526
+ typeof spec.version !== "string" || Semver.valid(spec.version) !== spec.version) {
527
+ return "The certified npm publisher requires one nonempty package name and canonical semantic version.";
528
+ }
529
+ if (operation.subject !== `npm:${spec.packageName}@${spec.version}`) {
530
+ return "The certified npm publisher package and version do not match the authorized operation subject.";
531
+ }
532
+ try {
533
+ if (canonicalizeRegistryUrl(spec.registryUrl) !== spec.registryUrl ||
534
+ operation.audience.toString() !== spec.registryUrl.toString()) {
535
+ return "The certified npm publisher registry does not match the authorized canonical audience.";
536
+ }
537
+ }
538
+ catch {
539
+ return "The certified npm publisher requires a canonical registry and npm distribution tag.";
540
+ }
541
+ if (Semver.validRange(spec.distTag) !== null || spec.distTag.trim() !== spec.distTag ||
542
+ encodeURIComponent(spec.distTag) !== spec.distTag) {
543
+ return "The certified npm publisher requires a canonical registry and npm distribution tag.";
544
+ }
545
+ if ((spec.access !== "public" && spec.access !== "restricted") ||
546
+ (spec.provenance !== "required" && spec.provenance !== "automatic" && spec.provenance !== "disabled") ||
547
+ (spec._tag === "NpmPublisherSpec" && spec.provenance === "automatic") ||
548
+ (spec.access === "restricted" && !spec.packageName.startsWith("@")) ||
549
+ (Semver.prerelease(spec.version) !== null && spec.distTag === "latest")) {
550
+ return "The certified npm publisher access, provenance, or dist-tag policy is not an allowed canonical value.";
551
+ }
552
+ try {
553
+ const fileName = basename(spec.tarballPath);
554
+ if (!isAbsolute(spec.cwd) || resolve(spec.cwd) !== spec.cwd ||
555
+ !/^[a-f0-9]{64}$/u.test(fileName) ||
556
+ resolve(spec.cwd, "blobs", fileName) !== spec.tarballPath) {
557
+ return "The certified npm publisher tarball must be the canonical prepared-store blob path.";
558
+ }
559
+ }
560
+ catch {
561
+ return "The certified npm publisher tarball must be the canonical prepared-store blob path.";
562
+ }
563
+ return undefined;
564
+ };
565
+ const validateCertifiedNpmSpec = Effect.fn("CertifiedPublisherSpawn.validateCertifiedNpmSpec")(function* (spec) {
566
+ const issue = certifiedNpmSpecIssue(spec);
567
+ if (issue !== undefined) {
568
+ return yield* new CredentialStrategyUnsupported({
569
+ subject: spec.operation.subject,
570
+ provider: spec.operation.provider,
571
+ strategy: spec._tag === "WorkloadPublisherSpec" ? "trusted-publishing" : "token",
572
+ reason: issue
573
+ });
574
+ }
575
+ });
576
+ /** Snapshot caller data once so accessors or concurrent mutation cannot change the admitted command after validation. */
577
+ const snapshotCertifiedNpmSpec = (spec) => {
578
+ const operation = Object.freeze({
579
+ ...spec.operation,
580
+ decision: Object.freeze({ ...spec.operation.decision })
581
+ });
582
+ return Object.freeze({ ...spec, operation });
583
+ };
584
+ const publisherEnvironment = Effect.fn("CertifiedPublisherSpawn.environment")(function* (vault, trustedWorkloads, spec, grant) {
585
+ yield* validateCertifiedNpmSpec(spec);
586
+ if (spec._tag === "NpmPublisherSpec") {
587
+ if (grant._tag !== "ScopedSecret") {
588
+ return yield* new CredentialStrategyUnsupported({
589
+ subject: spec.operation.subject,
590
+ provider: spec.operation.provider,
591
+ strategy: "trusted-publishing",
592
+ reason: "A scoped npm user config requires a scoped-secret grant."
593
+ });
594
+ }
595
+ const metadata = npmUserConfigs.get(spec.userConfig);
596
+ if (metadata === undefined || metadata.subject !== spec.operation.subject || metadata.grant !== grant) {
597
+ return yield* new CredentialUnavailable({
598
+ subject: spec.operation.subject,
599
+ provider: spec.operation.provider,
600
+ purpose: spec.operation.purpose,
601
+ reason: "The npm user config is not active for this exact operation and grant."
602
+ });
603
+ }
604
+ const path = yield* optionalPath;
605
+ return {
606
+ ...closedBaseEnvironment(path),
607
+ NPM_CONFIG_USERCONFIG: metadata.path,
608
+ NPM_CONFIG_IGNORE_SCRIPTS: "true"
609
+ };
610
+ }
611
+ if (grant._tag !== "WorkloadIdentity") {
612
+ return yield* new CredentialStrategyUnsupported({
613
+ subject: spec.operation.subject,
614
+ provider: spec.operation.provider,
615
+ strategy: "token",
616
+ reason: "The workload publisher requires a workload-identity grant."
617
+ });
618
+ }
619
+ yield* validateTrustedNpmOperation(spec.operation, grant);
620
+ return yield* workloadEnvironment(trustedWorkloads, grant);
621
+ });
622
+ const redactPublisherOutput = (value, env, spec) => {
623
+ const metadata = spec._tag === "NpmPublisherSpec" ? npmUserConfigs.get(spec.userConfig) : undefined;
624
+ const known = metadata === undefined
625
+ ? env
626
+ : { ...env, PUBLISH_CREDENTIAL: Redacted.value(metadata.token) };
627
+ return redactOutput(value, known);
628
+ };
629
+ /**
630
+ * npm classifies a suffixless local path as a package directory. Prepared
631
+ * artifacts deliberately use digest-only blob names, so expose the already
632
+ * admitted bytes through a private `.tgz` transport name only while npm runs.
633
+ */
634
+ const makeNpmPublishTransport = (sourcePath, temporaryRoot) => {
635
+ const root = temporaryRoot ?? tmpdir();
636
+ const acquired = Effect.tryPromise({
637
+ try: async () => {
638
+ await mkdir(root, { recursive: true, mode: 0o700 });
639
+ const directory = await mkdtemp(join(root, "ts-release-npm-tarball-"));
640
+ try {
641
+ const path = join(directory, "package.tgz");
642
+ await copyFile(sourcePath, path);
643
+ await chmod(path, 0o600);
644
+ return { directory, path };
645
+ }
646
+ catch (cause) {
647
+ await rm(directory, { recursive: true, force: true });
648
+ throw cause;
649
+ }
650
+ },
651
+ catch: () => platformError("resource", "before-dispatch", "Unable to materialize the verified npm tarball transport path.")
652
+ });
653
+ return Effect.acquireRelease(acquired, ({ directory }) => Effect.tryPromise({
654
+ try: () => rm(directory, { recursive: true, force: true }),
655
+ catch: () => undefined
656
+ }).pipe(Effect.catch(() => Effect.void)));
657
+ };
658
+ const makeCertifiedPublisherSpawn = (spawner, vault, trustedWorkloads, temporaryRoot) => {
659
+ const runClosed = Effect.fn("CertifiedPublisherSpawn.runClosed")(function* (argv) {
660
+ const path = yield* optionalPath;
661
+ const command = ChildProcess.make(argv[0], [...argv.slice(1)], {
662
+ env: closedBaseEnvironment(path),
663
+ extendEnv: false,
664
+ stdin: "ignore",
665
+ stdout: "pipe",
666
+ stderr: "pipe"
667
+ });
668
+ return yield* Effect.scoped(Effect.gen(function* () {
669
+ const spawned = yield* Effect.exit(spawner.spawn(command));
670
+ if (Exit.isFailure(spawned)) {
671
+ return yield* platformError("spawn", "before-dispatch", "Trusted npm preflight could not start its version probe.");
672
+ }
673
+ const output = yield* Effect.exit(Effect.all({
674
+ stdout: collect(spawned.value.stdout),
675
+ stderr: collect(spawned.value.stderr),
676
+ exitCode: spawned.value.exitCode
677
+ }, { concurrency: "unbounded" }));
678
+ if (Exit.isFailure(output)) {
679
+ return yield* platformError("spawn", "before-dispatch", "Trusted npm preflight could not observe its version probe.");
680
+ }
681
+ if (Number(output.value.exitCode) !== 0) {
682
+ return yield* platformError("spawn", "before-dispatch", "Trusted npm preflight version probe was rejected.");
683
+ }
684
+ return output.value.stdout;
685
+ }));
686
+ });
687
+ return {
688
+ preflightTrustedNpm: Effect.fn("CertifiedPublisherSpawn.preflightTrustedNpm")(function* (operation, grant) {
689
+ yield* validateGrantForOperation(operation, grant);
690
+ if (grant._tag !== "WorkloadIdentity") {
691
+ return yield* new CredentialStrategyUnsupported({
692
+ subject: operation.subject,
693
+ provider: operation.provider,
694
+ strategy: "token",
695
+ reason: "Trusted npm preflight requires a workload-identity grant."
696
+ });
697
+ }
698
+ yield* validateTrustedNpmOperation(operation, grant);
699
+ // Validate the exact host-held OIDC names before any provider authority is dispatched.
700
+ yield* workloadEnvironment(trustedWorkloads, grant);
701
+ const node = parseVersion(yield* runClosed(["node", "--version"]));
702
+ const npm = parseVersion(yield* runClosed(["npm", "--version"]));
703
+ if (node === undefined || !versionAtLeast(node, minimumTrustedNode) ||
704
+ npm === undefined || !versionAtLeast(npm, minimumTrustedNpm)) {
705
+ return yield* platformError("spawn", "before-dispatch", "Trusted npm publishing requires Node >=22.14.0 and npm >=11.5.1.");
706
+ }
707
+ }),
708
+ spawn: Effect.fn("CertifiedPublisherSpawn.spawn")(function* (inputSpec, grant) {
709
+ yield* validateGrantForOperation(inputSpec.operation, grant);
710
+ yield* validateCertifiedNpmSpec(inputSpec);
711
+ const spec = snapshotCertifiedNpmSpec(inputSpec);
712
+ const env = yield* publisherEnvironment(vault, trustedWorkloads, spec, grant);
713
+ return yield* Effect.scoped(Effect.gen(function* () {
714
+ const transport = yield* makeNpmPublishTransport(spec.tarballPath, temporaryRoot);
715
+ const argv = npmPublishArgv({ ...spec, tarballPath: transport.path });
716
+ const command = ChildProcess.make(argv[0], [...argv.slice(1)], {
717
+ cwd: spec.cwd,
718
+ env,
719
+ extendEnv: false,
720
+ stdin: "ignore",
721
+ stdout: "pipe",
722
+ stderr: "pipe"
723
+ });
724
+ const spawned = yield* Effect.exit(spawner.spawn(command));
725
+ if (Exit.isFailure(spawned)) {
726
+ return {
727
+ _tag: "RejectedBeforeStart",
728
+ commitment: "before-dispatch",
729
+ reason: "The certified publisher process did not return a started handle."
730
+ };
731
+ }
732
+ const output = yield* Effect.exit(Effect.all({
733
+ stdout: collect(spawned.value.stdout),
734
+ stderr: collect(spawned.value.stderr),
735
+ exitCode: spawned.value.exitCode
736
+ }, { concurrency: "unbounded" }));
737
+ if (Exit.isFailure(output)) {
738
+ return {
739
+ _tag: "PublisherOutcomeUnknown",
740
+ commitment: "unknown",
741
+ reason: "The certified publisher started, but its final outcome could not be observed."
742
+ };
743
+ }
744
+ return {
745
+ _tag: "PublisherExited",
746
+ commitment: "started",
747
+ exitCode: Number(output.value.exitCode),
748
+ stdout: redactPublisherOutput(output.value.stdout, env, spec),
749
+ stderr: redactPublisherOutput(output.value.stderr, env, spec)
750
+ };
751
+ }));
752
+ })
753
+ };
754
+ };
755
+ /** All host sinks share one private vault; no secret is returned from it. */
756
+ export const makeEnvironmentCredentialPlatform = (http, spawner, options = {}) => {
757
+ const vault = new Map();
758
+ const trustedWorkloads = new WeakMap();
759
+ const observationGrants = new WeakMap();
760
+ return {
761
+ credentialProvider: makeProvider(vault, trustedWorkloads, observationGrants),
762
+ httpAuthorizer: makeHttpAuthorizer(http, vault, observationGrants),
763
+ authorizedMutationHttp: makeAuthorizedMutationHttp(http, vault),
764
+ npmUserConfigResource: makeNpmUserConfigResource(vault, options.temporaryRoot),
765
+ certifiedPublisherSpawn: makeCertifiedPublisherSpawn(spawner, vault, trustedWorkloads, options.temporaryRoot)
766
+ };
767
+ };
768
+ export const makeEnvironmentCredentialPlatformLayer = (http, spawner, options = {}) => {
769
+ const platform = makeEnvironmentCredentialPlatform(http, spawner, options);
770
+ return Layer.mergeAll(Layer.succeed(CredentialProvider)(platform.credentialProvider), Layer.succeed(HttpAuthorizer)(platform.httpAuthorizer), Layer.succeed(AuthorizedMutationHttp)(platform.authorizedMutationHttp), Layer.succeed(NpmUserConfigResource)(platform.npmUserConfigResource), Layer.succeed(CertifiedPublisherSpawn)(platform.certifiedPublisherSpawn));
771
+ };
772
+ //# sourceMappingURL=credentials.js.map