@supatype/cli 0.1.0-alpha.10

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 (416) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test.log +221 -0
  3. package/.turbo/turbo-typecheck.log +4 -0
  4. package/assets/supatype-logo-wordmark.ascii.txt +6 -0
  5. package/bin/dev-entry.ts +2 -0
  6. package/bin/supatype.js +5 -0
  7. package/dist/app/framework.d.ts +44 -0
  8. package/dist/app/framework.d.ts.map +1 -0
  9. package/dist/app/framework.js +200 -0
  10. package/dist/app/framework.js.map +1 -0
  11. package/dist/app/proxy-dev-app.d.ts +13 -0
  12. package/dist/app/proxy-dev-app.d.ts.map +1 -0
  13. package/dist/app/proxy-dev-app.js +54 -0
  14. package/dist/app/proxy-dev-app.js.map +1 -0
  15. package/dist/app-config.d.ts +7 -0
  16. package/dist/app-config.d.ts.map +1 -0
  17. package/dist/app-config.js +113 -0
  18. package/dist/app-config.js.map +1 -0
  19. package/dist/assets/supatype-logo-wordmark.ascii.txt +6 -0
  20. package/dist/augmentation-generator.d.ts +2 -0
  21. package/dist/augmentation-generator.d.ts.map +1 -0
  22. package/dist/augmentation-generator.js +111 -0
  23. package/dist/augmentation-generator.js.map +1 -0
  24. package/dist/binary-cache.d.ts +98 -0
  25. package/dist/binary-cache.d.ts.map +1 -0
  26. package/dist/binary-cache.js +687 -0
  27. package/dist/binary-cache.js.map +1 -0
  28. package/dist/cli.d.ts +2 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +61 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/commands/admin.d.ts +4 -0
  33. package/dist/commands/admin.d.ts.map +1 -0
  34. package/dist/commands/admin.js +271 -0
  35. package/dist/commands/admin.js.map +1 -0
  36. package/dist/commands/app.d.ts +3 -0
  37. package/dist/commands/app.d.ts.map +1 -0
  38. package/dist/commands/app.js +82 -0
  39. package/dist/commands/app.js.map +1 -0
  40. package/dist/commands/cache.d.ts +6 -0
  41. package/dist/commands/cache.d.ts.map +1 -0
  42. package/dist/commands/cache.js +105 -0
  43. package/dist/commands/cache.js.map +1 -0
  44. package/dist/commands/cloud.d.ts +23 -0
  45. package/dist/commands/cloud.d.ts.map +1 -0
  46. package/dist/commands/cloud.js +254 -0
  47. package/dist/commands/cloud.js.map +1 -0
  48. package/dist/commands/db.d.ts +8 -0
  49. package/dist/commands/db.d.ts.map +1 -0
  50. package/dist/commands/db.js +116 -0
  51. package/dist/commands/db.js.map +1 -0
  52. package/dist/commands/deploy-types.d.ts +14 -0
  53. package/dist/commands/deploy-types.d.ts.map +1 -0
  54. package/dist/commands/deploy-types.js +38 -0
  55. package/dist/commands/deploy-types.js.map +1 -0
  56. package/dist/commands/deploy.d.ts +15 -0
  57. package/dist/commands/deploy.d.ts.map +1 -0
  58. package/dist/commands/deploy.js +322 -0
  59. package/dist/commands/deploy.js.map +1 -0
  60. package/dist/commands/dev.d.ts +14 -0
  61. package/dist/commands/dev.d.ts.map +1 -0
  62. package/dist/commands/dev.js +806 -0
  63. package/dist/commands/dev.js.map +1 -0
  64. package/dist/commands/diff.d.ts +3 -0
  65. package/dist/commands/diff.d.ts.map +1 -0
  66. package/dist/commands/diff.js +54 -0
  67. package/dist/commands/diff.js.map +1 -0
  68. package/dist/commands/engine.d.ts +7 -0
  69. package/dist/commands/engine.d.ts.map +1 -0
  70. package/dist/commands/engine.js +27 -0
  71. package/dist/commands/engine.js.map +1 -0
  72. package/dist/commands/functions.d.ts +3 -0
  73. package/dist/commands/functions.d.ts.map +1 -0
  74. package/dist/commands/functions.js +749 -0
  75. package/dist/commands/functions.js.map +1 -0
  76. package/dist/commands/generate.d.ts +3 -0
  77. package/dist/commands/generate.d.ts.map +1 -0
  78. package/dist/commands/generate.js +38 -0
  79. package/dist/commands/generate.js.map +1 -0
  80. package/dist/commands/init.d.ts +7 -0
  81. package/dist/commands/init.d.ts.map +1 -0
  82. package/dist/commands/init.js +228 -0
  83. package/dist/commands/init.js.map +1 -0
  84. package/dist/commands/keys.d.ts +4 -0
  85. package/dist/commands/keys.d.ts.map +1 -0
  86. package/dist/commands/keys.js +57 -0
  87. package/dist/commands/keys.js.map +1 -0
  88. package/dist/commands/logs.d.ts +6 -0
  89. package/dist/commands/logs.d.ts.map +1 -0
  90. package/dist/commands/logs.js +52 -0
  91. package/dist/commands/logs.js.map +1 -0
  92. package/dist/commands/migrate-from-v1.d.ts +5 -0
  93. package/dist/commands/migrate-from-v1.d.ts.map +1 -0
  94. package/dist/commands/migrate-from-v1.js +125 -0
  95. package/dist/commands/migrate-from-v1.js.map +1 -0
  96. package/dist/commands/migrate.d.ts +3 -0
  97. package/dist/commands/migrate.d.ts.map +1 -0
  98. package/dist/commands/migrate.js +75 -0
  99. package/dist/commands/migrate.js.map +1 -0
  100. package/dist/commands/pg.d.ts +8 -0
  101. package/dist/commands/pg.d.ts.map +1 -0
  102. package/dist/commands/pg.js +102 -0
  103. package/dist/commands/pg.js.map +1 -0
  104. package/dist/commands/plugins.d.ts +3 -0
  105. package/dist/commands/plugins.d.ts.map +1 -0
  106. package/dist/commands/plugins.js +431 -0
  107. package/dist/commands/plugins.js.map +1 -0
  108. package/dist/commands/pull.d.ts +3 -0
  109. package/dist/commands/pull.d.ts.map +1 -0
  110. package/dist/commands/pull.js +12 -0
  111. package/dist/commands/pull.js.map +1 -0
  112. package/dist/commands/push.d.ts +3 -0
  113. package/dist/commands/push.d.ts.map +1 -0
  114. package/dist/commands/push.js +179 -0
  115. package/dist/commands/push.js.map +1 -0
  116. package/dist/commands/seed.d.ts +5 -0
  117. package/dist/commands/seed.d.ts.map +1 -0
  118. package/dist/commands/seed.js +55 -0
  119. package/dist/commands/seed.js.map +1 -0
  120. package/dist/commands/self-host.d.ts +9 -0
  121. package/dist/commands/self-host.d.ts.map +1 -0
  122. package/dist/commands/self-host.js +310 -0
  123. package/dist/commands/self-host.js.map +1 -0
  124. package/dist/commands/self-update.d.ts +9 -0
  125. package/dist/commands/self-update.d.ts.map +1 -0
  126. package/dist/commands/self-update.js +33 -0
  127. package/dist/commands/self-update.js.map +1 -0
  128. package/dist/commands/status.d.ts +6 -0
  129. package/dist/commands/status.d.ts.map +1 -0
  130. package/dist/commands/status.js +70 -0
  131. package/dist/commands/status.js.map +1 -0
  132. package/dist/commands/types.d.ts +3 -0
  133. package/dist/commands/types.d.ts.map +1 -0
  134. package/dist/commands/types.js +62 -0
  135. package/dist/commands/types.js.map +1 -0
  136. package/dist/commands/update.d.ts +7 -0
  137. package/dist/commands/update.d.ts.map +1 -0
  138. package/dist/commands/update.js +118 -0
  139. package/dist/commands/update.js.map +1 -0
  140. package/dist/components.d.ts +5 -0
  141. package/dist/components.d.ts.map +1 -0
  142. package/dist/components.js +3 -0
  143. package/dist/components.js.map +1 -0
  144. package/dist/config.d.ts +65 -0
  145. package/dist/config.d.ts.map +1 -0
  146. package/dist/config.js +134 -0
  147. package/dist/config.js.map +1 -0
  148. package/dist/dev-compose.d.ts +19 -0
  149. package/dist/dev-compose.d.ts.map +1 -0
  150. package/dist/dev-compose.js +468 -0
  151. package/dist/dev-compose.js.map +1 -0
  152. package/dist/dev-log-bus.d.ts +30 -0
  153. package/dist/dev-log-bus.d.ts.map +1 -0
  154. package/dist/dev-log-bus.js +87 -0
  155. package/dist/dev-log-bus.js.map +1 -0
  156. package/dist/dev-log-filter.d.ts +10 -0
  157. package/dist/dev-log-filter.d.ts.map +1 -0
  158. package/dist/dev-log-filter.js +36 -0
  159. package/dist/dev-log-filter.js.map +1 -0
  160. package/dist/dev-logo.d.ts +12 -0
  161. package/dist/dev-logo.d.ts.map +1 -0
  162. package/dist/dev-logo.js +57 -0
  163. package/dist/dev-logo.js.map +1 -0
  164. package/dist/dev-session.d.ts +26 -0
  165. package/dist/dev-session.d.ts.map +1 -0
  166. package/dist/dev-session.js +106 -0
  167. package/dist/dev-session.js.map +1 -0
  168. package/dist/dev-shutdown.d.ts +9 -0
  169. package/dist/dev-shutdown.d.ts.map +1 -0
  170. package/dist/dev-shutdown.js +50 -0
  171. package/dist/dev-shutdown.js.map +1 -0
  172. package/dist/dev-task-colors.d.ts +14 -0
  173. package/dist/dev-task-colors.d.ts.map +1 -0
  174. package/dist/dev-task-colors.js +44 -0
  175. package/dist/dev-task-colors.js.map +1 -0
  176. package/dist/dev-tui.d.ts +24 -0
  177. package/dist/dev-tui.d.ts.map +1 -0
  178. package/dist/dev-tui.js +188 -0
  179. package/dist/dev-tui.js.map +1 -0
  180. package/dist/diff-output.d.ts +4 -0
  181. package/dist/diff-output.d.ts.map +1 -0
  182. package/dist/diff-output.js +12 -0
  183. package/dist/diff-output.js.map +1 -0
  184. package/dist/docker-postgres.d.ts +57 -0
  185. package/dist/docker-postgres.d.ts.map +1 -0
  186. package/dist/docker-postgres.js +208 -0
  187. package/dist/docker-postgres.js.map +1 -0
  188. package/dist/engine-client.d.ts +69 -0
  189. package/dist/engine-client.d.ts.map +1 -0
  190. package/dist/engine-client.js +157 -0
  191. package/dist/engine-client.js.map +1 -0
  192. package/dist/engine-push-output.d.ts +16 -0
  193. package/dist/engine-push-output.d.ts.map +1 -0
  194. package/dist/engine-push-output.js +61 -0
  195. package/dist/engine-push-output.js.map +1 -0
  196. package/dist/ensure-binary.d.ts +7 -0
  197. package/dist/ensure-binary.d.ts.map +1 -0
  198. package/dist/ensure-binary.js +17 -0
  199. package/dist/ensure-binary.js.map +1 -0
  200. package/dist/functions-router-gen.d.ts +14 -0
  201. package/dist/functions-router-gen.d.ts.map +1 -0
  202. package/dist/functions-router-gen.js +199 -0
  203. package/dist/functions-router-gen.js.map +1 -0
  204. package/dist/index.d.ts +11 -0
  205. package/dist/index.d.ts.map +1 -0
  206. package/dist/index.js +9 -0
  207. package/dist/index.js.map +1 -0
  208. package/dist/jwt.d.ts +3 -0
  209. package/dist/jwt.d.ts.map +1 -0
  210. package/dist/jwt.js +13 -0
  211. package/dist/jwt.js.map +1 -0
  212. package/dist/kong-config.d.ts +25 -0
  213. package/dist/kong-config.d.ts.map +1 -0
  214. package/dist/kong-config.js +71 -0
  215. package/dist/kong-config.js.map +1 -0
  216. package/dist/local-gateway.d.ts +7 -0
  217. package/dist/local-gateway.d.ts.map +1 -0
  218. package/dist/local-gateway.js +9 -0
  219. package/dist/local-gateway.js.map +1 -0
  220. package/dist/local-storage.d.ts +8 -0
  221. package/dist/local-storage.d.ts.map +1 -0
  222. package/dist/local-storage.js +14 -0
  223. package/dist/local-storage.js.map +1 -0
  224. package/dist/pgbouncer-userlist.d.ts +5 -0
  225. package/dist/pgbouncer-userlist.d.ts.map +1 -0
  226. package/dist/pgbouncer-userlist.js +14 -0
  227. package/dist/pgbouncer-userlist.js.map +1 -0
  228. package/dist/postgres-ctl.d.ts +44 -0
  229. package/dist/postgres-ctl.d.ts.map +1 -0
  230. package/dist/postgres-ctl.js +137 -0
  231. package/dist/postgres-ctl.js.map +1 -0
  232. package/dist/process-manager.d.ts +49 -0
  233. package/dist/process-manager.d.ts.map +1 -0
  234. package/dist/process-manager.js +177 -0
  235. package/dist/process-manager.js.map +1 -0
  236. package/dist/project-config.d.ts +238 -0
  237. package/dist/project-config.d.ts.map +1 -0
  238. package/dist/project-config.js +159 -0
  239. package/dist/project-config.js.map +1 -0
  240. package/dist/pull-utils.d.ts +31 -0
  241. package/dist/pull-utils.d.ts.map +1 -0
  242. package/dist/pull-utils.js +77 -0
  243. package/dist/pull-utils.js.map +1 -0
  244. package/dist/release-pins.d.ts +7 -0
  245. package/dist/release-pins.d.ts.map +1 -0
  246. package/dist/release-pins.js +27 -0
  247. package/dist/release-pins.js.map +1 -0
  248. package/dist/release-public-key.d.ts +8 -0
  249. package/dist/release-public-key.d.ts.map +1 -0
  250. package/dist/release-public-key.js +13 -0
  251. package/dist/release-public-key.js.map +1 -0
  252. package/dist/restore-system-relation-targets.d.ts +3 -0
  253. package/dist/restore-system-relation-targets.d.ts.map +1 -0
  254. package/dist/restore-system-relation-targets.js +45 -0
  255. package/dist/restore-system-relation-targets.js.map +1 -0
  256. package/dist/runtime-routes.d.ts +34 -0
  257. package/dist/runtime-routes.d.ts.map +1 -0
  258. package/dist/runtime-routes.js +252 -0
  259. package/dist/runtime-routes.js.map +1 -0
  260. package/dist/schema-ast-v2.d.ts +127 -0
  261. package/dist/schema-ast-v2.d.ts.map +1 -0
  262. package/dist/schema-ast-v2.js +226 -0
  263. package/dist/schema-ast-v2.js.map +1 -0
  264. package/dist/scripts/postinstall.d.ts +11 -0
  265. package/dist/scripts/postinstall.d.ts.map +1 -0
  266. package/dist/scripts/postinstall.js +47 -0
  267. package/dist/scripts/postinstall.js.map +1 -0
  268. package/dist/seed.d.ts +8 -0
  269. package/dist/seed.d.ts.map +1 -0
  270. package/dist/seed.js +32 -0
  271. package/dist/seed.js.map +1 -0
  272. package/dist/self-host-compose.d.ts +43 -0
  273. package/dist/self-host-compose.d.ts.map +1 -0
  274. package/dist/self-host-compose.js +400 -0
  275. package/dist/self-host-compose.js.map +1 -0
  276. package/dist/storage-provision.d.ts +24 -0
  277. package/dist/storage-provision.d.ts.map +1 -0
  278. package/dist/storage-provision.js +44 -0
  279. package/dist/storage-provision.js.map +1 -0
  280. package/dist/studio-admin-roles.d.ts +7 -0
  281. package/dist/studio-admin-roles.d.ts.map +1 -0
  282. package/dist/studio-admin-roles.js +14 -0
  283. package/dist/studio-admin-roles.js.map +1 -0
  284. package/dist/studio-dev-server.d.ts +22 -0
  285. package/dist/studio-dev-server.d.ts.map +1 -0
  286. package/dist/studio-dev-server.js +28 -0
  287. package/dist/studio-dev-server.js.map +1 -0
  288. package/dist/supatype-eval-1781522769253.d.mts +2 -0
  289. package/dist/supatype-eval-1781522769253.d.mts.map +1 -0
  290. package/dist/supatype-eval-1781522769253.mjs +3 -0
  291. package/dist/supatype-eval-1781522769253.mjs.map +1 -0
  292. package/dist/systemd.d.ts +26 -0
  293. package/dist/systemd.d.ts.map +1 -0
  294. package/dist/systemd.js +102 -0
  295. package/dist/systemd.js.map +1 -0
  296. package/dist/tsx-runner.d.ts +18 -0
  297. package/dist/tsx-runner.d.ts.map +1 -0
  298. package/dist/tsx-runner.js +69 -0
  299. package/dist/tsx-runner.js.map +1 -0
  300. package/dist/type-extractor.d.ts +4 -0
  301. package/dist/type-extractor.d.ts.map +1 -0
  302. package/dist/type-extractor.js +1213 -0
  303. package/dist/type-extractor.js.map +1 -0
  304. package/dist/type-resolver.d.ts +33 -0
  305. package/dist/type-resolver.d.ts.map +1 -0
  306. package/dist/type-resolver.js +338 -0
  307. package/dist/type-resolver.js.map +1 -0
  308. package/package.json +41 -0
  309. package/releases/deno/VERSION +1 -0
  310. package/scripts/mirror-deno-release.sh +76 -0
  311. package/src/TYPE-RESOLUTION.md +294 -0
  312. package/src/app/framework.ts +249 -0
  313. package/src/app/proxy-dev-app.ts +68 -0
  314. package/src/app-config.ts +128 -0
  315. package/src/augmentation-generator.ts +126 -0
  316. package/src/binary-cache.ts +845 -0
  317. package/src/cli.ts +63 -0
  318. package/src/commands/admin.ts +372 -0
  319. package/src/commands/app.ts +97 -0
  320. package/src/commands/cache.ts +117 -0
  321. package/src/commands/cloud.ts +325 -0
  322. package/src/commands/db.ts +136 -0
  323. package/src/commands/deploy-types.ts +49 -0
  324. package/src/commands/deploy.ts +400 -0
  325. package/src/commands/dev.ts +1009 -0
  326. package/src/commands/diff.ts +63 -0
  327. package/src/commands/engine.ts +30 -0
  328. package/src/commands/functions.ts +901 -0
  329. package/src/commands/generate.ts +44 -0
  330. package/src/commands/init.ts +253 -0
  331. package/src/commands/keys.ts +66 -0
  332. package/src/commands/logs.ts +58 -0
  333. package/src/commands/migrate-from-v1.ts +131 -0
  334. package/src/commands/migrate.ts +87 -0
  335. package/src/commands/pg.ts +133 -0
  336. package/src/commands/plugins.ts +508 -0
  337. package/src/commands/pull.ts +17 -0
  338. package/src/commands/push.ts +226 -0
  339. package/src/commands/seed.ts +68 -0
  340. package/src/commands/self-host.ts +364 -0
  341. package/src/commands/self-update.ts +45 -0
  342. package/src/commands/status.ts +84 -0
  343. package/src/commands/types.ts +76 -0
  344. package/src/commands/update.ts +136 -0
  345. package/src/components.ts +6 -0
  346. package/src/config.ts +223 -0
  347. package/src/dev-compose.ts +583 -0
  348. package/src/dev-log-bus.ts +101 -0
  349. package/src/dev-log-filter.ts +32 -0
  350. package/src/dev-logo.ts +62 -0
  351. package/src/dev-session.ts +130 -0
  352. package/src/dev-shutdown.ts +54 -0
  353. package/src/dev-task-colors.ts +47 -0
  354. package/src/dev-tui.ts +232 -0
  355. package/src/diff-output.ts +12 -0
  356. package/src/docker-postgres.ts +295 -0
  357. package/src/engine-client.ts +236 -0
  358. package/src/engine-push-output.ts +71 -0
  359. package/src/ensure-binary.ts +28 -0
  360. package/src/functions-router-gen.ts +224 -0
  361. package/src/index.ts +11 -0
  362. package/src/jwt.ts +14 -0
  363. package/src/kong-config.ts +93 -0
  364. package/src/local-gateway.ts +9 -0
  365. package/src/local-storage.ts +14 -0
  366. package/src/pgbouncer-userlist.ts +15 -0
  367. package/src/postgres-ctl.ts +171 -0
  368. package/src/process-manager.ts +220 -0
  369. package/src/project-config.ts +388 -0
  370. package/src/pull-utils.ts +81 -0
  371. package/src/release-pins.ts +31 -0
  372. package/src/release-public-key.ts +12 -0
  373. package/src/restore-system-relation-targets.ts +45 -0
  374. package/src/runtime-routes.ts +291 -0
  375. package/src/schema-ast-v2.ts +324 -0
  376. package/src/scripts/postinstall.ts +51 -0
  377. package/src/seed.ts +43 -0
  378. package/src/self-host-compose.ts +452 -0
  379. package/src/storage-provision.ts +58 -0
  380. package/src/studio-admin-roles.ts +16 -0
  381. package/src/studio-dev-server.ts +53 -0
  382. package/src/supatype-eval-1781522769253.mts +1 -0
  383. package/src/systemd.ts +137 -0
  384. package/src/tsx-runner.ts +89 -0
  385. package/src/type-extractor.ts +1479 -0
  386. package/src/type-resolver.ts +457 -0
  387. package/tests/app-command.test.ts +54 -0
  388. package/tests/augmentation-generator.test.ts +59 -0
  389. package/tests/binary-cache-cloud-overrides.test.ts +123 -0
  390. package/tests/cached-artifact-format.test.ts +84 -0
  391. package/tests/cli-help.test.ts +133 -0
  392. package/tests/config.test.ts +252 -0
  393. package/tests/dev-ui.test.ts +139 -0
  394. package/tests/docker-postgres.test.ts +39 -0
  395. package/tests/engine-distribution.test.ts +418 -0
  396. package/tests/engine-push-output.test.ts +67 -0
  397. package/tests/ensure-binary.test.ts +59 -0
  398. package/tests/init.test.ts +127 -0
  399. package/tests/keys.test.ts +160 -0
  400. package/tests/migrate-from-v1.test.ts +29 -0
  401. package/tests/normalize-admin-config.test.ts +48 -0
  402. package/tests/pg-spawn-env.test.ts +18 -0
  403. package/tests/postgres-archive-tag.test.ts +9 -0
  404. package/tests/proxy-dev-app.test.ts +33 -0
  405. package/tests/pull-utils.test.ts +150 -0
  406. package/tests/release-pins.test.ts +28 -0
  407. package/tests/runtime-contract.test.ts +370 -0
  408. package/tests/seed-discover.test.ts +31 -0
  409. package/tests/studio-admin-roles.test.ts +27 -0
  410. package/tests/tsconfig.json +9 -0
  411. package/tests/tsx-runner.test.ts +66 -0
  412. package/tests/type-extractor.test.ts +985 -0
  413. package/tests/type-resolver.test.ts +59 -0
  414. package/tsconfig.json +10 -0
  415. package/tsconfig.tsbuildinfo +1 -0
  416. package/vitest.config.ts +12 -0
@@ -0,0 +1,687 @@
1
+ /**
2
+ * Binary cache — manages supatype component binaries.
3
+ *
4
+ * Components: engine, server, postgres, deno.
5
+ * Cache root: ~/.supatype/cache/{component}/{version}/
6
+ * Override path: config.overrides?.{component} (local build path).
7
+ *
8
+ * Security model:
9
+ * 1. Download checksums.sha256 + checksums.sha256.minisig from CDN.
10
+ * 2. Verify Ed25519 minisign signature on the checksum file using the
11
+ * embedded public key (SUPATYPE_RELEASE_PUBLIC_KEY).
12
+ * 3. Verify SHA256 of the downloaded binary against the signed checksum.
13
+ * Both checks are mandatory when SUPATYPE_RELEASE_PUBLIC_KEY is set.
14
+ */
15
+ import { createHash, createPublicKey, verify as cryptoVerify } from "node:crypto";
16
+ import { closeSync, copyFileSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
17
+ import { chmod } from "node:fs/promises";
18
+ import { homedir } from "node:os";
19
+ import { basename, join, resolve, isAbsolute } from "node:path";
20
+ import { releasePublicKey } from "./release-public-key.js";
21
+ /**
22
+ * Set `versions.{engine|server|postgres|deno}: VERSION_PIN_LOCAL` to mean “use `overrides.*` only”
23
+ * without duplicating the path string (Phase 10.7). Requires the matching `overrides` entry.
24
+ */
25
+ export const VERSION_PIN_LOCAL = "local";
26
+ /** True if `overrides.engine` points at a local engine binary (contributor dev). */
27
+ export function hasEngineOverride(config) {
28
+ const path = config.overrides?.engine;
29
+ return typeof path === "string" && path.trim() !== "";
30
+ }
31
+ export function hasStudioOverride(config) {
32
+ const path = config.overrides?.studio;
33
+ return typeof path === "string" && path.trim() !== "";
34
+ }
35
+ /** True if `overrides` contains any non-empty string path (contributor local builds). */
36
+ export function hasMeaningfulOverrides(config) {
37
+ const o = config.overrides;
38
+ if (!o)
39
+ return false;
40
+ for (const v of Object.values(o)) {
41
+ if (typeof v === "string" && v.trim() !== "")
42
+ return true;
43
+ }
44
+ return false;
45
+ }
46
+ /** Lines for a startup banner — non-empty override paths only. */
47
+ export function describeActiveOverrides(config) {
48
+ const o = config.overrides;
49
+ if (!o)
50
+ return [];
51
+ const lines = [];
52
+ const add = (label, v) => {
53
+ if (typeof v === "string" && v.trim() !== "") {
54
+ lines.push(` ${label.padEnd(12)} → ${v.trim()}`);
55
+ }
56
+ };
57
+ add("engine", o.engine);
58
+ add("server", o.server);
59
+ add("postgres_dir", o.postgres_dir);
60
+ add("deno", o.deno);
61
+ add("studio", o.studio);
62
+ add("postgrest", o.postgrest);
63
+ return lines;
64
+ }
65
+ /**
66
+ * True when this working tree is associated with a remote Supatype Cloud project:
67
+ * `project.ref`, `.supatype/cloud.json` (schema deploy link), or `.supatype/linked.json` (functions link).
68
+ */
69
+ export function isLinkedToCloudProject(cwd, config) {
70
+ const ref = config.project.ref;
71
+ if (typeof ref === "string" && ref.trim() !== "")
72
+ return true;
73
+ const linkedPath = join(cwd, ".supatype", "linked.json");
74
+ if (existsSync(linkedPath)) {
75
+ try {
76
+ const data = JSON.parse(readFileSync(linkedPath, "utf8"));
77
+ if (typeof data["ref"] === "string" && data["ref"].trim() !== "")
78
+ return true;
79
+ }
80
+ catch { /* ignore */ }
81
+ }
82
+ const cloudPath = join(cwd, ".supatype", "cloud.json");
83
+ if (existsSync(cloudPath)) {
84
+ try {
85
+ const data = JSON.parse(readFileSync(cloudPath, "utf8"));
86
+ if (typeof data.projectSlug === "string" && data.projectSlug.trim() !== "")
87
+ return true;
88
+ }
89
+ catch { /* ignore */ }
90
+ }
91
+ return false;
92
+ }
93
+ export { BINARY_COMPONENTS } from "./components.js";
94
+ import { BINARY_COMPONENTS } from "./components.js";
95
+ // ---------------------------------------------------------------------------
96
+ // CDN base URL + release signing public key
97
+ // ---------------------------------------------------------------------------
98
+ const CDN_BASE = "https://releases.supatype.com";
99
+ /** Postgres CDN archives use PG major in the basename (17.2 → `supatype-pg-17-…`). */
100
+ export function postgresArchiveTag(version) {
101
+ return version.split(".")[0];
102
+ }
103
+ /**
104
+ * Supatype release signing public key (minisign format).
105
+ * Generated with: minisign -G
106
+ * Rotate by: generating a new pair, updating this constant, and updating
107
+ * the MINISIGN_PRIVATE_KEY GitHub Actions secret.
108
+ *
109
+ * ⚠ PLACEHOLDER — replace with actual public key before first release.
110
+ * When empty, minisign verification is skipped with a warning (SHA256 only).
111
+ */
112
+ const SUPATYPE_RELEASE_PUBLIC_KEY = "";
113
+ // CDN path templates per component.
114
+ const CDN_PATHS = {
115
+ engine: (v, p) => `/engine/v${v}/supatype-engine-${p.os}-${p.arch}${p.os === "windows" ? ".exe" : ""}`,
116
+ server: (v, p) => `/server/v${v}/supatype-server-${p.os}-${p.arch}${p.os === "windows" ? ".exe" : ""}`,
117
+ postgres: (v, p) => `/postgres/v${v}/supatype-pg-${postgresArchiveTag(v)}-${p.os}-${p.arch}${p.os === "windows" ? ".zip" : ".tar.gz"}`,
118
+ deno: (v, p) => `/deno/v${v}/deno-${p.os}-${p.arch}${p.os === "windows" ? ".exe" : ""}`,
119
+ };
120
+ // Checksums file path (one per version directory, covers all platform binaries).
121
+ const checksumsDirPath = (component, version) => `/${component}/v${version}/checksums.sha256`;
122
+ // ---------------------------------------------------------------------------
123
+ // Cache paths
124
+ // ---------------------------------------------------------------------------
125
+ export function cacheRoot() {
126
+ return join(homedir(), ".supatype", "cache");
127
+ }
128
+ export function cachePath(component, version) {
129
+ return join(cacheRoot(), component, version);
130
+ }
131
+ export function cachedBinaryPath(component, version, platform) {
132
+ return join(cachePath(component, version), binaryName(component, version, platform));
133
+ }
134
+ function binaryName(component, version, platform) {
135
+ const win = platform.os === "windows";
136
+ switch (component) {
137
+ case "engine": return `supatype-engine-${platform.os}-${platform.arch}${win ? ".exe" : ""}`;
138
+ case "server": return `supatype-server-${platform.os}-${platform.arch}${win ? ".exe" : ""}`;
139
+ case "postgres": return `supatype-pg-${postgresArchiveTag(version)}-${platform.os}-${platform.arch}${win ? ".zip" : ".tar.gz"}`;
140
+ case "deno": return `deno-${platform.os}-${platform.arch}${win ? ".exe" : ""}`;
141
+ }
142
+ }
143
+ // ---------------------------------------------------------------------------
144
+ // Platform detection
145
+ // ---------------------------------------------------------------------------
146
+ export function currentPlatform() {
147
+ let os;
148
+ if (process.platform === "darwin")
149
+ os = "darwin";
150
+ else if (process.platform === "win32")
151
+ os = "windows";
152
+ else
153
+ os = "linux";
154
+ const rawArch = process.arch;
155
+ let arch;
156
+ if (rawArch === "arm64")
157
+ arch = "arm64";
158
+ else if (rawArch === "x64")
159
+ arch = "amd64";
160
+ else
161
+ throw new Error(`Unsupported architecture: ${rawArch}`);
162
+ return { os, arch };
163
+ }
164
+ // ---------------------------------------------------------------------------
165
+ // Override validation
166
+ // ---------------------------------------------------------------------------
167
+ /**
168
+ * Resolve the binary path for a component.
169
+ *
170
+ * Resolution order:
171
+ * 1. config.overrides?.[component] — local build path (must exist)
172
+ * 2. Cached binary at ~/.supatype/cache/{component}/{version}/
173
+ * 3. Throws — caller should call download() first.
174
+ *
175
+ * Hard error if any meaningful `overrides` entry is set while the project is linked to cloud
176
+ * (`project.ref`, `.supatype/cloud.json`, or `.supatype/linked.json`).
177
+ */
178
+ export async function resolveBinary(component, config) {
179
+ const cwd = process.cwd();
180
+ if (hasMeaningfulOverrides(config) && isLinkedToCloudProject(cwd, config)) {
181
+ throw new Error("[overrides] cannot be used while this project is linked to Supatype Cloud " +
182
+ "(project.ref, .supatype/cloud.json, or .supatype/linked.json).\n" +
183
+ "Remove overrides from supatype.config.ts / supatype.local.config.ts, or remove the cloud link files / clear project.ref.");
184
+ }
185
+ const overridePath = config.overrides?.[component === "postgres" ? "postgres_dir" : component];
186
+ const version = await resolveVersionFor(component, config);
187
+ if (version === VERSION_PIN_LOCAL && !overridePath) {
188
+ const key = component === "postgres" ? "postgres_dir" : component;
189
+ throw new Error(`[versions] versions.${component} is "${VERSION_PIN_LOCAL}" but overrides.${key} is not set. ` +
190
+ `Set overrides.${key} to your local build path, or pin a semver in versions.${component}.`);
191
+ }
192
+ if (overridePath) {
193
+ const normalised = normalisePlatformPath(overridePath);
194
+ let resolvedOverride = isAbsolute(normalised)
195
+ ? normalised
196
+ : resolve(process.cwd(), normalised);
197
+ if (process.platform === "win32" && !/\.\w+$/.test(resolvedOverride) && !existsSync(resolvedOverride)) {
198
+ const withExe = resolvedOverride + ".exe";
199
+ if (existsSync(withExe))
200
+ resolvedOverride = withExe;
201
+ }
202
+ // On Windows, CreateProcess automatically appends .exe to extensionless paths.
203
+ // If the override binary exists without .exe, copy it to path.exe so it
204
+ // spawns correctly (and takes precedence over any stale .exe at that path).
205
+ if (process.platform === "win32" && !/\.\w+$/.test(resolvedOverride) && existsSync(resolvedOverride)) {
206
+ const withExe = resolvedOverride + ".exe";
207
+ const srcStat = statSync(resolvedOverride);
208
+ const dstStat = existsSync(withExe) ? statSync(withExe) : null;
209
+ if (!dstStat || dstStat.size !== srcStat.size || dstStat.mtimeMs < srcStat.mtimeMs) {
210
+ copyFileSync(resolvedOverride, withExe);
211
+ }
212
+ resolvedOverride = withExe;
213
+ }
214
+ if (!existsSync(resolvedOverride)) {
215
+ throw new Error(`[overrides] ${component} path does not exist: ${resolvedOverride}`);
216
+ }
217
+ const stat = statSync(resolvedOverride);
218
+ if (!stat.isFile() && !stat.isDirectory()) {
219
+ throw new Error(`[overrides] ${component} path is not a file or directory: ${resolvedOverride}`);
220
+ }
221
+ return resolvedOverride;
222
+ }
223
+ const platform = currentPlatform();
224
+ const binPath = cachedBinaryPath(component, version, platform);
225
+ if (existsSync(binPath))
226
+ return binPath;
227
+ throw new Error(`${component} v${version} not found in cache. Run: supatype update`);
228
+ }
229
+ // ---------------------------------------------------------------------------
230
+ // Download + verify
231
+ // ---------------------------------------------------------------------------
232
+ /**
233
+ * Download a component binary to the cache.
234
+ *
235
+ * Verification order:
236
+ * 1. Fetch checksums.sha256 + checksums.sha256.minisig from CDN.
237
+ * 2. If SUPATYPE_RELEASE_PUBLIC_KEY is set: verify minisign signature.
238
+ * 3. Verify SHA256 of downloaded binary against signed checksum.
239
+ */
240
+ /** Download if missing or invalid; return cached path for the given platform. */
241
+ export async function ensureCachedBinary(component, version, platform) {
242
+ return download(component, version, platform);
243
+ }
244
+ export async function download(component, version, platform) {
245
+ if (version === VERSION_PIN_LOCAL) {
246
+ throw new Error(`cannot download CDN binary when version is "${VERSION_PIN_LOCAL}" — set overrides.${component === "postgres" ? "postgres_dir" : component} or pin a semver`);
247
+ }
248
+ const dir = cachePath(component, version);
249
+ mkdirSync(dir, { recursive: true });
250
+ const name = binaryName(component, version, platform);
251
+ const destPath = join(dir, name);
252
+ if (existsSync(destPath)) {
253
+ if (cachedArtifactLooksValid(component, destPath)) {
254
+ console.log(`[supatype] ${component} v${version} already cached.`);
255
+ return destPath;
256
+ }
257
+ console.warn(`[supatype] ${component} v${version} cache invalid — re-downloading (${destPath}).`);
258
+ unlinkSync(destPath);
259
+ }
260
+ const binaryUrl = `${CDN_BASE}${CDN_PATHS[component](version, platform)}`;
261
+ const checksumsUrl = `${CDN_BASE}${checksumsDirPath(component, version)}`;
262
+ const minisigUrl = `${checksumsUrl}.minisig`;
263
+ console.log(`[supatype] Downloading ${component} v${version} (${platform.os}/${platform.arch})...`);
264
+ // ── Fetch checksums + optional minisig ────────────────────────────────────
265
+ const expectedChecksum = await withRetry(() => fetchChecksums(checksumsUrl, minisigUrl, name));
266
+ // ── Stream-download binary with progress ─────────────────────────────────
267
+ const tmpPath = destPath + ".tmp";
268
+ try {
269
+ await withRetry(() => streamToFileWithProgress(binaryUrl, tmpPath));
270
+ // ── Verify SHA256 ────────────────────────────────────────────────────────
271
+ await verifyChecksum(tmpPath, expectedChecksum, component);
272
+ writeFileSync(destPath, readFileSync(tmpPath));
273
+ assertArtifactFormat(component, destPath, platform);
274
+ if (process.platform !== "win32" && EXECUTABLE_COMPONENTS.has(component)) {
275
+ await chmod(destPath, 0o755);
276
+ }
277
+ }
278
+ finally {
279
+ if (existsSync(tmpPath)) {
280
+ try {
281
+ require("node:fs").unlinkSync(tmpPath);
282
+ }
283
+ catch { /* ignore */ }
284
+ }
285
+ }
286
+ return destPath;
287
+ }
288
+ /**
289
+ * Fetch checksums.sha256, optionally verify its minisign signature, and
290
+ * return the expected SHA256 for `binaryFilename`.
291
+ */
292
+ async function fetchChecksums(checksumsUrl, minisigUrl, binaryFilename) {
293
+ const csResp = await fetch(checksumsUrl);
294
+ if (!csResp.ok) {
295
+ throw new Error(`Failed to fetch checksums from ${checksumsUrl}: HTTP ${csResp.status}`);
296
+ }
297
+ const checksumsText = await csResp.text();
298
+ const pubKey = releasePublicKey();
299
+ if (pubKey) {
300
+ // Minisign signature is required when a public key is embedded.
301
+ const sigResp = await fetch(minisigUrl);
302
+ if (!sigResp.ok) {
303
+ throw new Error(`Failed to fetch checksum signature from ${minisigUrl}: HTTP ${sigResp.status}\n` +
304
+ "Cannot verify release integrity. Aborting download.");
305
+ }
306
+ const sigText = await sigResp.text();
307
+ verifyMinisign(Buffer.from(checksumsText, "utf8"), sigText, pubKey);
308
+ }
309
+ else {
310
+ console.warn("[supatype] \u26a0 Minisign public key not configured — " +
311
+ "skipping signature verification (SHA256 only).");
312
+ }
313
+ return extractChecksum(checksumsText, binaryFilename);
314
+ }
315
+ // ---------------------------------------------------------------------------
316
+ // Minisign signature verification (pure Node.js, no external deps)
317
+ // ---------------------------------------------------------------------------
318
+ /**
319
+ * Ed25519 SPKI DER prefix — wraps a raw 32-byte public key into the
320
+ * SubjectPublicKeyInfo structure that Node.js crypto.createPublicKey expects.
321
+ *
322
+ * Breakdown:
323
+ * 30 2a SEQUENCE (42 bytes)
324
+ * 30 05 SEQUENCE (5 bytes)
325
+ * 06 03 OID (3 bytes)
326
+ * 2b 65 70 OID value: 1.3.101.112 (id-Ed25519)
327
+ * 03 21 BIT STRING (33 bytes)
328
+ * 00 0 unused bits
329
+ * <32 bytes Ed25519 public key>
330
+ */
331
+ const ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
332
+ /**
333
+ * Verify a minisign signature (Ed25519 legacy mode, algorithm bytes "Ed").
334
+ * Throws if verification fails.
335
+ */
336
+ function verifyMinisign(fileBytes, sigFileContent, pubKeyStr) {
337
+ // Parse public key: [2 algo][8 keyId][32 ed25519 key]
338
+ const pkLines = pubKeyStr.trim().split("\n");
339
+ const pkBytes = Buffer.from(pkLines[pkLines.length - 1].trim(), "base64");
340
+ if (pkBytes.length < 42)
341
+ throw new Error("Invalid minisign public key");
342
+ const pkKeyId = pkBytes.subarray(2, 10);
343
+ const pkEd25519 = pkBytes.subarray(10, 42);
344
+ // Parse signature file:
345
+ // line 0: untrusted comment
346
+ // line 1: base64 sig bytes — [2 algo][8 keyId][64 Ed25519 sig]
347
+ // line 2: trusted comment
348
+ // line 3: base64 global sig (over sig bytes + trusted comment)
349
+ const sigLines = sigFileContent.trim().split("\n");
350
+ if (sigLines.length < 4)
351
+ throw new Error("Malformed minisign signature file");
352
+ const sigBytes = Buffer.from(sigLines[1].trim(), "base64");
353
+ if (sigBytes.length < 74)
354
+ throw new Error("Invalid minisign signature length");
355
+ const algo = sigBytes.subarray(0, 2);
356
+ const sigKeyId = sigBytes.subarray(2, 10);
357
+ const signature = sigBytes.subarray(10, 74);
358
+ // Only Ed25519 legacy mode ("Ed" = 0x45, 0x64) is supported.
359
+ // Hashed mode ("ED") requires BLAKE2b prehashing — not implemented.
360
+ if (algo[0] !== 0x45 || algo[1] !== 0x64) {
361
+ throw new Error("Unsupported minisign algorithm — only Ed25519 legacy mode supported.\n" +
362
+ `Got: 0x${algo[0]?.toString(16)}${algo[1]?.toString(16)}`);
363
+ }
364
+ if (!sigKeyId.equals(pkKeyId)) {
365
+ throw new Error("Minisign key ID mismatch — signature was produced with a different key.\n" +
366
+ "This could indicate a compromised release. Do not proceed.");
367
+ }
368
+ const spkiDer = Buffer.concat([ED25519_SPKI_PREFIX, pkEd25519]);
369
+ const keyObject = createPublicKey({ key: spkiDer, format: "der", type: "spki" });
370
+ const valid = cryptoVerify(null, fileBytes, keyObject, signature);
371
+ if (!valid) {
372
+ throw new Error("Minisign signature verification FAILED — the checksum file may have been tampered with.\n" +
373
+ "This could indicate a supply chain attack. Aborting download.");
374
+ }
375
+ }
376
+ /**
377
+ * Extract the SHA256 hash for `filename` from a checksums.sha256 file.
378
+ * Format: `<hash> <filename>` (sha256sum output, two spaces).
379
+ */
380
+ function extractChecksum(checksumsText, filename) {
381
+ const target = basename(filename);
382
+ for (const line of checksumsText.split("\n")) {
383
+ const parts = line.trim().split(/\s+/);
384
+ if (parts.length >= 2 && parts[1] === target) {
385
+ return parts[0];
386
+ }
387
+ }
388
+ throw new Error(`Checksum not found for "${target}" in checksums.sha256.\n` +
389
+ "The checksums file may be from a different release.");
390
+ }
391
+ // ---------------------------------------------------------------------------
392
+ // Streaming download with progress bar
393
+ // ---------------------------------------------------------------------------
394
+ async function streamToFileWithProgress(url, destPath) {
395
+ const resp = await fetch(url);
396
+ if (!resp.ok)
397
+ throw new Error(`Failed to download from ${url}: HTTP ${resp.status}`);
398
+ if (!resp.body)
399
+ throw new Error("Response body is null");
400
+ const totalStr = resp.headers.get("content-length");
401
+ const total = totalStr ? parseInt(totalStr, 10) : null;
402
+ let downloaded = 0;
403
+ const file = createWriteStream(destPath);
404
+ const reader = resp.body.getReader();
405
+ try {
406
+ while (true) {
407
+ const { done, value } = await reader.read();
408
+ if (done)
409
+ break;
410
+ await new Promise((res, rej) => {
411
+ file.write(value, (err) => (err ? rej(err) : res()));
412
+ });
413
+ downloaded += value.length;
414
+ if (total && process.stdout.isTTY) {
415
+ const pct = Math.min(100, Math.floor((downloaded / total) * 100));
416
+ const filled = Math.floor(pct / 5);
417
+ const bar = "=".repeat(filled).padEnd(20);
418
+ process.stdout.write(`\r [${bar}] ${pct}% ${(downloaded / 1_000_000).toFixed(1)} / ${(total / 1_000_000).toFixed(1)} MB`);
419
+ }
420
+ }
421
+ if (total && process.stdout.isTTY)
422
+ process.stdout.write("\n");
423
+ await new Promise((res, rej) => {
424
+ file.end((err) => (err ? rej(err) : res()));
425
+ });
426
+ }
427
+ catch (err) {
428
+ file.destroy();
429
+ throw err;
430
+ }
431
+ }
432
+ // ---------------------------------------------------------------------------
433
+ // SHA256 verification
434
+ // ---------------------------------------------------------------------------
435
+ const EXECUTABLE_COMPONENTS = new Set(["engine", "server", "deno"]);
436
+ /** True when a cached file matches expected format for the current platform. */
437
+ function cachedArtifactLooksValid(component, filePath) {
438
+ try {
439
+ const st = statSync(filePath);
440
+ if (!st.isFile() || st.size < 64)
441
+ return false;
442
+ assertArtifactFormat(component, filePath, currentPlatform());
443
+ return true;
444
+ }
445
+ catch {
446
+ return false;
447
+ }
448
+ }
449
+ /** Confirm a downloaded/cached artifact matches the expected CDN format (tests, CI). */
450
+ export function validateArtifactFormat(component, filePath, platform) {
451
+ assertArtifactFormat(component, filePath, platform);
452
+ }
453
+ /**
454
+ * Per-component CDN artifact shapes:
455
+ * engine, server, deno — native executable (ELF / Mach-O / PE)
456
+ * postgres (unix) — .tar.gz (gzip)
457
+ * postgres (windows) — .zip
458
+ */
459
+ function assertArtifactFormat(component, filePath, platform) {
460
+ if (component === "postgres") {
461
+ if (platform.os === "windows")
462
+ assertZipArchive(filePath);
463
+ else
464
+ assertGzipArchive(filePath);
465
+ return;
466
+ }
467
+ if (EXECUTABLE_COMPONENTS.has(component)) {
468
+ assertNativeExecutable(filePath, component, platform);
469
+ return;
470
+ }
471
+ }
472
+ /** Reject HTML/error pages or corrupt postgres .tar.gz on CDN. */
473
+ function assertGzipArchive(filePath) {
474
+ const fd = openSync(filePath, "r");
475
+ try {
476
+ const magic = Buffer.alloc(2);
477
+ readSync(fd, magic, 0, 2, 0);
478
+ if (magic[0] !== 0x1f || magic[1] !== 0x8b) {
479
+ throw new Error("Downloaded postgres file is not a gzip archive (bad magic bytes). " +
480
+ "The CDN object may be corrupt or cached HTML — delete ~/.supatype/cache and retry.");
481
+ }
482
+ }
483
+ finally {
484
+ closeSync(fd);
485
+ }
486
+ }
487
+ /** Reject corrupt postgres .zip on CDN (Windows bundles). */
488
+ function assertZipArchive(filePath) {
489
+ const fd = openSync(filePath, "r");
490
+ try {
491
+ const magic = Buffer.alloc(4);
492
+ readSync(fd, magic, 0, 4, 0);
493
+ if (magic[0] !== 0x50 || magic[1] !== 0x4b) {
494
+ throw new Error("Downloaded postgres file is not a zip archive (bad magic bytes). " +
495
+ "The CDN object may be corrupt or cached HTML — delete ~/.supatype/cache and retry.");
496
+ }
497
+ }
498
+ finally {
499
+ closeSync(fd);
500
+ }
501
+ }
502
+ /** Reject HTML/error pages, Go c-archives, or wrong-OS executables on CDN. */
503
+ function assertNativeExecutable(filePath, component, platform) {
504
+ const fd = openSync(filePath, "r");
505
+ try {
506
+ const magic = Buffer.alloc(4);
507
+ readSync(fd, magic, 0, 4, 0);
508
+ const goCArchive = magic[0] === 0x21 && magic[1] === 0x3c && magic[2] === 0x61 && magic[3] === 0x72;
509
+ if (goCArchive) {
510
+ throw new Error(`Downloaded ${component} file is a Go static archive (c-archive), not an executable. ` +
511
+ "The CDN object may be from a bad release build — delete ~/.supatype/cache and retry.");
512
+ }
513
+ if (platform.os === "windows") {
514
+ if (magic[0] !== 0x4d || magic[1] !== 0x5a) {
515
+ throw new Error(`Downloaded ${component} file is not a Windows PE executable (bad magic bytes). ` +
516
+ "The CDN object may be corrupt or cached HTML — delete ~/.supatype/cache and retry.");
517
+ }
518
+ return;
519
+ }
520
+ if (platform.os === "linux") {
521
+ const elf = magic[0] === 0x7f && magic[1] === 0x45 && magic[2] === 0x4c && magic[3] === 0x46;
522
+ if (!elf) {
523
+ throw new Error(`Downloaded ${component} file is not an ELF executable (bad magic bytes). ` +
524
+ "The CDN object may be corrupt or cached HTML — delete ~/.supatype/cache and retry.");
525
+ }
526
+ return;
527
+ }
528
+ const macho = magic.readUInt32BE(0) === 0xfe_ed_fa_ce ||
529
+ magic.readUInt32BE(0) === 0xfe_ed_fa_cf ||
530
+ magic.readUInt32LE(0) === 0xfe_ed_fa_ce ||
531
+ magic.readUInt32LE(0) === 0xfe_ed_fa_cf ||
532
+ magic.readUInt32BE(0) === 0xca_fe_ba_be;
533
+ if (!macho) {
534
+ throw new Error(`Downloaded ${component} file is not a Mach-O executable (bad magic bytes). ` +
535
+ "The CDN object may be corrupt or cached HTML — delete ~/.supatype/cache and retry.");
536
+ }
537
+ }
538
+ finally {
539
+ closeSync(fd);
540
+ }
541
+ }
542
+ async function verifyChecksum(filePath, expected, component) {
543
+ const data = readFileSync(filePath);
544
+ const actual = createHash("sha256").update(data).digest("hex");
545
+ if (actual !== expected) {
546
+ throw new Error(`Checksum mismatch for ${component}.\n` +
547
+ ` Expected: ${expected}\n` +
548
+ ` Got: ${actual}`);
549
+ }
550
+ }
551
+ // ---------------------------------------------------------------------------
552
+ // Retry with exponential backoff
553
+ // ---------------------------------------------------------------------------
554
+ async function withRetry(fn, attempts = 3) {
555
+ for (let i = 1; i <= attempts; i++) {
556
+ try {
557
+ return await fn();
558
+ }
559
+ catch (err) {
560
+ if (i === attempts)
561
+ throw err;
562
+ const delay = Math.pow(3, i - 1) * 1_000; // 1 s, 3 s, 9 s
563
+ console.error(`[supatype] Download attempt ${i}/${attempts} failed: ${err.message}. ` +
564
+ `Retrying in ${delay / 1_000}s...`);
565
+ await new Promise((r) => setTimeout(r, delay));
566
+ }
567
+ }
568
+ throw new Error("unreachable");
569
+ }
570
+ // ---------------------------------------------------------------------------
571
+ // Helpers
572
+ // ---------------------------------------------------------------------------
573
+ /**
574
+ * On Windows, Git Bash represents paths as /c/Users/... — convert to C:\Users\...
575
+ */
576
+ export function normalisePlatformPath(p) {
577
+ let result = p;
578
+ if (process.platform === "win32" && /^\/[a-zA-Z]\//.test(result)) {
579
+ result = result
580
+ .replace(/^\/([a-zA-Z])\//, (_, drive) => `${drive.toUpperCase()}:\\`)
581
+ .replace(/\//g, "\\");
582
+ }
583
+ if (process.platform === "win32" && !/\.\w+$/.test(result) && !existsSync(result)) {
584
+ const withExe = result + ".exe";
585
+ if (existsSync(withExe))
586
+ return withExe;
587
+ }
588
+ return result;
589
+ }
590
+ export function pinnedVersion(component, config) {
591
+ const version = config.versions?.[component];
592
+ if (typeof version !== "string")
593
+ return undefined;
594
+ const trimmed = version.trim();
595
+ return trimmed === "" ? undefined : trimmed;
596
+ }
597
+ /** Pinned version from config, or latest from CDN when unset. */
598
+ export async function resolveVersionFor(component, config) {
599
+ const pinned = pinnedVersion(component, config);
600
+ if (pinned)
601
+ return pinned;
602
+ return fetchLatestVersion(component);
603
+ }
604
+ /** @deprecated Prefer {@link pinnedVersion} or {@link resolveVersionFor}. */
605
+ export function versionFor(component, config) {
606
+ const version = pinnedVersion(component, config);
607
+ if (!version) {
608
+ throw new Error(`[supatype] versions.${component} is not pinned in supatype.config.ts (omit versions to use latest)`);
609
+ }
610
+ return version;
611
+ }
612
+ // ---------------------------------------------------------------------------
613
+ // Latest version resolution from CDN
614
+ // ---------------------------------------------------------------------------
615
+ /**
616
+ * Fetch the latest available version for a component.
617
+ * Each component directory on the CDN exposes `latest.json` → `{"version":"x.y.z"}`.
618
+ */
619
+ export async function fetchLatestVersion(component) {
620
+ const url = `${CDN_BASE}/${component}/latest.json`;
621
+ const resp = await fetch(url);
622
+ if (!resp.ok) {
623
+ throw new Error(`Failed to fetch latest version for ${component} from ${url}: HTTP ${resp.status}`);
624
+ }
625
+ const data = await resp.json();
626
+ if (typeof data.version !== "string" || data.version.trim() === "") {
627
+ throw new Error(`Invalid latest.json for ${component}: missing "version" field`);
628
+ }
629
+ return data.version.trim();
630
+ }
631
+ /** Fetch the latest version for all components concurrently. */
632
+ export async function fetchAllLatestVersions() {
633
+ const results = await Promise.all(BINARY_COMPONENTS.map(async (c) => [c, await fetchLatestVersion(c)]));
634
+ return Object.fromEntries(results);
635
+ }
636
+ // ---------------------------------------------------------------------------
637
+ // Download all components (used by postinstall + supatype update)
638
+ // ---------------------------------------------------------------------------
639
+ /**
640
+ * Download all component binaries for the current platform.
641
+ * Skips components that are already cached.
642
+ * Fails gracefully when graceful=true (suitable for postinstall).
643
+ */
644
+ /**
645
+ * Verify all cached binaries for the current platform (used by integration CI).
646
+ * Throws if any cached component is missing or fails format checks.
647
+ */
648
+ export function verifyCachedBinaries(versions) {
649
+ if (!versions) {
650
+ throw new Error("[supatype] verifyCachedBinaries requires pinned versions");
651
+ }
652
+ const platform = currentPlatform();
653
+ for (const component of BINARY_COMPONENTS) {
654
+ const version = versions[component];
655
+ if (typeof version !== "string" || version.trim() === "") {
656
+ throw new Error(`[supatype] versions.${component} must be set`);
657
+ }
658
+ const destPath = join(cachePath(component, version), binaryName(component, version, platform));
659
+ if (!cachedArtifactLooksValid(component, destPath)) {
660
+ throw new Error(`[supatype] Cached ${component} v${version} is missing or invalid at ${destPath}. ` +
661
+ "Run: supatype update (or delete ~/.supatype/cache and retry).");
662
+ }
663
+ }
664
+ }
665
+ export async function downloadAll(versions, graceful = false) {
666
+ const platform = currentPlatform();
667
+ const components = [...BINARY_COMPONENTS];
668
+ const latest = await fetchAllLatestVersions();
669
+ for (const component of components) {
670
+ const version = versions?.[component] ?? latest[component];
671
+ if (version === VERSION_PIN_LOCAL)
672
+ continue;
673
+ try {
674
+ await download(component, version, platform);
675
+ }
676
+ catch (err) {
677
+ const msg = `[supatype] Failed to download ${component}: ${err.message}`;
678
+ if (graceful) {
679
+ console.error(msg);
680
+ }
681
+ else {
682
+ throw new Error(msg);
683
+ }
684
+ }
685
+ }
686
+ }
687
+ //# sourceMappingURL=binary-cache.js.map