@supatype/cli 0.1.0-alpha.9 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/.turbo/turbo-test.log +285 -69
  3. package/.turbo/turbo-typecheck.log +1 -1
  4. package/assets/supatype-logo-wordmark.ascii.txt +6 -0
  5. package/bin/dev-entry.ts +2 -1
  6. package/dist/app/framework.js +1 -3
  7. package/dist/app/framework.js.map +1 -1
  8. package/dist/app/proxy-dev-app.d.ts +14 -0
  9. package/dist/app/proxy-dev-app.d.ts.map +1 -1
  10. package/dist/app/proxy-dev-app.js +110 -6
  11. package/dist/app/proxy-dev-app.js.map +1 -1
  12. package/dist/app-config.d.ts +10 -0
  13. package/dist/app-config.d.ts.map +1 -1
  14. package/dist/app-config.js +72 -0
  15. package/dist/app-config.js.map +1 -1
  16. package/dist/assets/supatype-logo-wordmark.ascii.txt +6 -0
  17. package/dist/binary-cache.d.ts +19 -7
  18. package/dist/binary-cache.d.ts.map +1 -1
  19. package/dist/binary-cache.js +92 -46
  20. package/dist/binary-cache.js.map +1 -1
  21. package/dist/cli.d.ts +1 -1
  22. package/dist/cli.d.ts.map +1 -1
  23. package/dist/cli.js +17 -2
  24. package/dist/cli.js.map +1 -1
  25. package/dist/commands/add.d.ts +3 -0
  26. package/dist/commands/add.d.ts.map +1 -0
  27. package/dist/commands/add.js +86 -0
  28. package/dist/commands/add.js.map +1 -0
  29. package/dist/commands/admin.d.ts +28 -1
  30. package/dist/commands/admin.d.ts.map +1 -1
  31. package/dist/commands/admin.js +297 -149
  32. package/dist/commands/admin.js.map +1 -1
  33. package/dist/commands/adopt.d.ts +3 -0
  34. package/dist/commands/adopt.d.ts.map +1 -0
  35. package/dist/commands/adopt.js +55 -0
  36. package/dist/commands/adopt.js.map +1 -0
  37. package/dist/commands/app.d.ts.map +1 -1
  38. package/dist/commands/app.js +20 -17
  39. package/dist/commands/app.js.map +1 -1
  40. package/dist/commands/cache.d.ts.map +1 -1
  41. package/dist/commands/cache.js +11 -10
  42. package/dist/commands/cache.js.map +1 -1
  43. package/dist/commands/cloud.d.ts +4 -9
  44. package/dist/commands/cloud.d.ts.map +1 -1
  45. package/dist/commands/cloud.js +75 -125
  46. package/dist/commands/cloud.js.map +1 -1
  47. package/dist/commands/db.d.ts.map +1 -1
  48. package/dist/commands/db.js +37 -58
  49. package/dist/commands/db.js.map +1 -1
  50. package/dist/commands/deploy.d.ts.map +1 -1
  51. package/dist/commands/deploy.js +140 -96
  52. package/dist/commands/deploy.js.map +1 -1
  53. package/dist/commands/dev.d.ts.map +1 -1
  54. package/dist/commands/dev.js +74 -39
  55. package/dist/commands/dev.js.map +1 -1
  56. package/dist/commands/diff.d.ts.map +1 -1
  57. package/dist/commands/diff.js +39 -39
  58. package/dist/commands/diff.js.map +1 -1
  59. package/dist/commands/doctor.d.ts +3 -0
  60. package/dist/commands/doctor.d.ts.map +1 -0
  61. package/dist/commands/doctor.js +78 -0
  62. package/dist/commands/doctor.js.map +1 -0
  63. package/dist/commands/engine.d.ts.map +1 -1
  64. package/dist/commands/engine.js +5 -4
  65. package/dist/commands/engine.js.map +1 -1
  66. package/dist/commands/functions.d.ts.map +1 -1
  67. package/dist/commands/functions.js +172 -119
  68. package/dist/commands/functions.js.map +1 -1
  69. package/dist/commands/generate.d.ts.map +1 -1
  70. package/dist/commands/generate.js +5 -4
  71. package/dist/commands/generate.js.map +1 -1
  72. package/dist/commands/init.d.ts +35 -1
  73. package/dist/commands/init.d.ts.map +1 -1
  74. package/dist/commands/init.js +883 -107
  75. package/dist/commands/init.js.map +1 -1
  76. package/dist/commands/introspect.d.ts +3 -0
  77. package/dist/commands/introspect.d.ts.map +1 -0
  78. package/dist/commands/introspect.js +35 -0
  79. package/dist/commands/introspect.js.map +1 -0
  80. package/dist/commands/keys.d.ts +15 -1
  81. package/dist/commands/keys.d.ts.map +1 -1
  82. package/dist/commands/keys.js +46 -10
  83. package/dist/commands/keys.js.map +1 -1
  84. package/dist/commands/link-helpers.d.ts +15 -0
  85. package/dist/commands/link-helpers.d.ts.map +1 -0
  86. package/dist/commands/link-helpers.js +225 -0
  87. package/dist/commands/link-helpers.js.map +1 -0
  88. package/dist/commands/logs.d.ts.map +1 -1
  89. package/dist/commands/logs.js +5 -4
  90. package/dist/commands/logs.js.map +1 -1
  91. package/dist/commands/migrate-from-v1.d.ts.map +1 -1
  92. package/dist/commands/migrate-from-v1.js +3 -2
  93. package/dist/commands/migrate-from-v1.js.map +1 -1
  94. package/dist/commands/migrate.d.ts.map +1 -1
  95. package/dist/commands/migrate.js +119 -26
  96. package/dist/commands/migrate.js.map +1 -1
  97. package/dist/commands/pg.d.ts.map +1 -1
  98. package/dist/commands/pg.js +11 -12
  99. package/dist/commands/pg.js.map +1 -1
  100. package/dist/commands/plugins.d.ts.map +1 -1
  101. package/dist/commands/plugins.js +55 -46
  102. package/dist/commands/plugins.js.map +1 -1
  103. package/dist/commands/pull.d.ts.map +1 -1
  104. package/dist/commands/pull.js +33 -5
  105. package/dist/commands/pull.js.map +1 -1
  106. package/dist/commands/push.d.ts.map +1 -1
  107. package/dist/commands/push.js +111 -138
  108. package/dist/commands/push.js.map +1 -1
  109. package/dist/commands/seed.d.ts.map +1 -1
  110. package/dist/commands/seed.js +4 -3
  111. package/dist/commands/seed.js.map +1 -1
  112. package/dist/commands/self-host.d.ts +2 -2
  113. package/dist/commands/self-host.d.ts.map +1 -1
  114. package/dist/commands/self-host.js +65 -50
  115. package/dist/commands/self-host.js.map +1 -1
  116. package/dist/commands/self-update.d.ts.map +1 -1
  117. package/dist/commands/self-update.js +3 -2
  118. package/dist/commands/self-update.js.map +1 -1
  119. package/dist/commands/status.d.ts +1 -1
  120. package/dist/commands/status.d.ts.map +1 -1
  121. package/dist/commands/status.js +95 -29
  122. package/dist/commands/status.js.map +1 -1
  123. package/dist/commands/types.d.ts.map +1 -1
  124. package/dist/commands/types.js +3 -2
  125. package/dist/commands/types.js.map +1 -1
  126. package/dist/commands/update.d.ts.map +1 -1
  127. package/dist/commands/update.js +54 -21
  128. package/dist/commands/update.js.map +1 -1
  129. package/dist/compose-rename.d.ts +10 -0
  130. package/dist/compose-rename.d.ts.map +1 -0
  131. package/dist/compose-rename.js +67 -0
  132. package/dist/compose-rename.js.map +1 -0
  133. package/dist/config.d.ts +2 -1
  134. package/dist/config.d.ts.map +1 -1
  135. package/dist/config.js.map +1 -1
  136. package/dist/dev-compose.d.ts +26 -0
  137. package/dist/dev-compose.d.ts.map +1 -1
  138. package/dist/dev-compose.js +357 -79
  139. package/dist/dev-compose.js.map +1 -1
  140. package/dist/dev-log-bus.d.ts +30 -0
  141. package/dist/dev-log-bus.d.ts.map +1 -0
  142. package/dist/dev-log-bus.js +87 -0
  143. package/dist/dev-log-bus.js.map +1 -0
  144. package/dist/dev-log-filter.d.ts +10 -0
  145. package/dist/dev-log-filter.d.ts.map +1 -0
  146. package/dist/dev-log-filter.js +36 -0
  147. package/dist/dev-log-filter.js.map +1 -0
  148. package/dist/dev-logo.d.ts +12 -0
  149. package/dist/dev-logo.d.ts.map +1 -0
  150. package/dist/dev-logo.js +56 -0
  151. package/dist/dev-logo.js.map +1 -0
  152. package/dist/dev-ports.d.ts +27 -0
  153. package/dist/dev-ports.d.ts.map +1 -0
  154. package/dist/dev-ports.js +171 -0
  155. package/dist/dev-ports.js.map +1 -0
  156. package/dist/dev-session-lock.d.ts +25 -0
  157. package/dist/dev-session-lock.d.ts.map +1 -0
  158. package/dist/dev-session-lock.js +81 -0
  159. package/dist/dev-session-lock.js.map +1 -0
  160. package/dist/dev-session.d.ts +26 -0
  161. package/dist/dev-session.d.ts.map +1 -0
  162. package/dist/dev-session.js +106 -0
  163. package/dist/dev-session.js.map +1 -0
  164. package/dist/dev-shutdown.d.ts +25 -0
  165. package/dist/dev-shutdown.d.ts.map +1 -0
  166. package/dist/dev-shutdown.js +114 -0
  167. package/dist/dev-shutdown.js.map +1 -0
  168. package/dist/dev-task-colors.d.ts +13 -0
  169. package/dist/dev-task-colors.d.ts.map +1 -0
  170. package/dist/dev-task-colors.js +43 -0
  171. package/dist/dev-task-colors.js.map +1 -0
  172. package/dist/dev-tui.d.ts +24 -0
  173. package/dist/dev-tui.d.ts.map +1 -0
  174. package/dist/dev-tui.js +188 -0
  175. package/dist/dev-tui.js.map +1 -0
  176. package/dist/diff-output.d.ts +5 -1
  177. package/dist/diff-output.d.ts.map +1 -1
  178. package/dist/diff-output.js +69 -0
  179. package/dist/diff-output.js.map +1 -1
  180. package/dist/docker-runtime.d.ts +30 -0
  181. package/dist/docker-runtime.d.ts.map +1 -0
  182. package/dist/docker-runtime.js +118 -0
  183. package/dist/docker-runtime.js.map +1 -0
  184. package/dist/engine-client.d.ts +10 -1
  185. package/dist/engine-client.d.ts.map +1 -1
  186. package/dist/engine-client.js +76 -17
  187. package/dist/engine-client.js.map +1 -1
  188. package/dist/engine-push-output.d.ts +17 -0
  189. package/dist/engine-push-output.d.ts.map +1 -0
  190. package/dist/engine-push-output.js +64 -0
  191. package/dist/engine-push-output.js.map +1 -0
  192. package/dist/ensure-binary.js +2 -2
  193. package/dist/ensure-binary.js.map +1 -1
  194. package/dist/env-file.d.ts +5 -0
  195. package/dist/env-file.d.ts.map +1 -0
  196. package/dist/env-file.js +33 -0
  197. package/dist/env-file.js.map +1 -0
  198. package/dist/gitignore.d.ts +8 -0
  199. package/dist/gitignore.d.ts.map +1 -0
  200. package/dist/gitignore.js +41 -0
  201. package/dist/gitignore.js.map +1 -0
  202. package/dist/kong-config.d.ts +9 -0
  203. package/dist/kong-config.d.ts.map +1 -1
  204. package/dist/kong-config.js +18 -1
  205. package/dist/kong-config.js.map +1 -1
  206. package/dist/link.d.ts +66 -0
  207. package/dist/link.d.ts.map +1 -0
  208. package/dist/link.js +160 -0
  209. package/dist/link.js.map +1 -0
  210. package/dist/process-manager.d.ts +8 -0
  211. package/dist/process-manager.d.ts.map +1 -1
  212. package/dist/process-manager.js +53 -9
  213. package/dist/process-manager.js.map +1 -1
  214. package/dist/project-config.d.ts +30 -3
  215. package/dist/project-config.d.ts.map +1 -1
  216. package/dist/project-config.js +37 -4
  217. package/dist/project-config.js.map +1 -1
  218. package/dist/prompts.d.ts +3 -0
  219. package/dist/prompts.d.ts.map +1 -0
  220. package/dist/prompts.js +3 -0
  221. package/dist/prompts.js.map +1 -0
  222. package/dist/pull-utils.d.ts +50 -14
  223. package/dist/pull-utils.d.ts.map +1 -1
  224. package/dist/pull-utils.js +152 -12
  225. package/dist/pull-utils.js.map +1 -1
  226. package/dist/resolve-target.d.ts +86 -0
  227. package/dist/resolve-target.d.ts.map +1 -0
  228. package/dist/resolve-target.js +291 -0
  229. package/dist/resolve-target.js.map +1 -0
  230. package/dist/restore-system-relation-targets.d.ts +3 -0
  231. package/dist/restore-system-relation-targets.d.ts.map +1 -0
  232. package/dist/restore-system-relation-targets.js +45 -0
  233. package/dist/restore-system-relation-targets.js.map +1 -0
  234. package/dist/runtime-routes.d.ts.map +1 -1
  235. package/dist/runtime-routes.js +7 -0
  236. package/dist/runtime-routes.js.map +1 -1
  237. package/dist/schema-ast-v2.d.ts +1 -1
  238. package/dist/schema-ast-v2.d.ts.map +1 -1
  239. package/dist/schema-ast-v2.js +2 -2
  240. package/dist/schema-ast-v2.js.map +1 -1
  241. package/dist/schema-sources.d.ts +40 -0
  242. package/dist/schema-sources.d.ts.map +1 -0
  243. package/dist/schema-sources.js +183 -0
  244. package/dist/schema-sources.js.map +1 -0
  245. package/dist/scripts/postinstall.js +5 -1
  246. package/dist/scripts/postinstall.js.map +1 -1
  247. package/dist/self-host-compose.d.ts +37 -1
  248. package/dist/self-host-compose.d.ts.map +1 -1
  249. package/dist/self-host-compose.js +234 -43
  250. package/dist/self-host-compose.js.map +1 -1
  251. package/dist/storage-provision.d.ts +4 -0
  252. package/dist/storage-provision.d.ts.map +1 -1
  253. package/dist/storage-provision.js +24 -2
  254. package/dist/storage-provision.js.map +1 -1
  255. package/dist/supatype-eval-1781522769253.d.mts +2 -0
  256. package/dist/supatype-eval-1781522769253.d.mts.map +1 -0
  257. package/dist/supatype-eval-1781522769253.mjs +3 -0
  258. package/dist/supatype-eval-1781522769253.mjs.map +1 -0
  259. package/dist/systemd.js +2 -2
  260. package/dist/systemd.js.map +1 -1
  261. package/dist/target-client.d.ts +10 -0
  262. package/dist/target-client.d.ts.map +1 -0
  263. package/dist/target-client.js +22 -0
  264. package/dist/target-client.js.map +1 -0
  265. package/dist/type-extractor.d.ts +11 -0
  266. package/dist/type-extractor.d.ts.map +1 -1
  267. package/dist/type-extractor.js +95 -8
  268. package/dist/type-extractor.js.map +1 -1
  269. package/dist/ui/brand.d.ts +9 -0
  270. package/dist/ui/brand.d.ts.map +1 -0
  271. package/dist/ui/brand.js +11 -0
  272. package/dist/ui/brand.js.map +1 -0
  273. package/dist/ui/confirm.d.ts +12 -0
  274. package/dist/ui/confirm.d.ts.map +1 -0
  275. package/dist/ui/confirm.js +28 -0
  276. package/dist/ui/confirm.js.map +1 -0
  277. package/dist/ui/fatal.d.ts +10 -0
  278. package/dist/ui/fatal.d.ts.map +1 -0
  279. package/dist/ui/fatal.js +34 -0
  280. package/dist/ui/fatal.js.map +1 -0
  281. package/dist/ui/index.d.ts +9 -0
  282. package/dist/ui/index.d.ts.map +1 -0
  283. package/dist/ui/index.js +9 -0
  284. package/dist/ui/index.js.map +1 -0
  285. package/dist/ui/interactive.d.ts +3 -0
  286. package/dist/ui/interactive.d.ts.map +1 -0
  287. package/dist/ui/interactive.js +5 -0
  288. package/dist/ui/interactive.js.map +1 -0
  289. package/dist/ui/messages.d.ts +10 -0
  290. package/dist/ui/messages.d.ts.map +1 -0
  291. package/dist/ui/messages.js +35 -0
  292. package/dist/ui/messages.js.map +1 -0
  293. package/dist/ui/next-steps.d.ts +3 -0
  294. package/dist/ui/next-steps.d.ts.map +1 -0
  295. package/dist/ui/next-steps.js +10 -0
  296. package/dist/ui/next-steps.js.map +1 -0
  297. package/dist/ui/progress.d.ts +5 -0
  298. package/dist/ui/progress.d.ts.map +1 -0
  299. package/dist/ui/progress.js +24 -0
  300. package/dist/ui/progress.js.map +1 -0
  301. package/dist/ui/prompts.d.ts +14 -0
  302. package/dist/ui/prompts.d.ts.map +1 -0
  303. package/dist/ui/prompts.js +34 -0
  304. package/dist/ui/prompts.js.map +1 -0
  305. package/package.json +5 -2
  306. package/src/app/framework.ts +1 -3
  307. package/src/app/proxy-dev-app.ts +114 -6
  308. package/src/app-config.ts +80 -0
  309. package/src/binary-cache.ts +102 -52
  310. package/src/cli.ts +16 -2
  311. package/src/commands/add.ts +97 -0
  312. package/src/commands/admin.ts +381 -190
  313. package/src/commands/adopt.ts +82 -0
  314. package/src/commands/app.ts +20 -17
  315. package/src/commands/cache.ts +11 -10
  316. package/src/commands/cloud.ts +91 -142
  317. package/src/commands/db.ts +40 -63
  318. package/src/commands/deploy.ts +186 -126
  319. package/src/commands/dev.ts +98 -55
  320. package/src/commands/diff.ts +52 -43
  321. package/src/commands/doctor.ts +103 -0
  322. package/src/commands/engine.ts +5 -4
  323. package/src/commands/functions.ts +187 -123
  324. package/src/commands/generate.ts +5 -4
  325. package/src/commands/init.ts +1087 -104
  326. package/src/commands/introspect.ts +48 -0
  327. package/src/commands/keys.ts +56 -14
  328. package/src/commands/link-helpers.ts +273 -0
  329. package/src/commands/logs.ts +5 -4
  330. package/src/commands/migrate-from-v1.ts +3 -2
  331. package/src/commands/migrate.ts +167 -27
  332. package/src/commands/pg.ts +13 -18
  333. package/src/commands/plugins.ts +55 -46
  334. package/src/commands/pull.ts +38 -9
  335. package/src/commands/push.ts +148 -175
  336. package/src/commands/seed.ts +5 -4
  337. package/src/commands/self-host.ts +85 -54
  338. package/src/commands/self-update.ts +3 -2
  339. package/src/commands/status.ts +102 -33
  340. package/src/commands/types.ts +3 -2
  341. package/src/commands/update.ts +59 -23
  342. package/src/compose-rename.ts +76 -0
  343. package/src/config.ts +2 -1
  344. package/src/dev-compose.ts +462 -76
  345. package/src/dev-log-bus.ts +101 -0
  346. package/src/dev-log-filter.ts +32 -0
  347. package/src/dev-logo.ts +61 -0
  348. package/src/dev-ports.ts +212 -0
  349. package/src/dev-session-lock.ts +101 -0
  350. package/src/dev-session.ts +130 -0
  351. package/src/dev-shutdown.ts +147 -0
  352. package/src/dev-task-colors.ts +47 -0
  353. package/src/dev-tui.ts +232 -0
  354. package/src/diff-output.ts +79 -1
  355. package/src/docker-runtime.ts +151 -0
  356. package/src/engine-client.ts +81 -17
  357. package/src/engine-push-output.ts +75 -0
  358. package/src/ensure-binary.ts +2 -2
  359. package/src/env-file.ts +37 -0
  360. package/src/gitignore.ts +48 -0
  361. package/src/kong-config.ts +24 -1
  362. package/src/link.ts +243 -0
  363. package/src/process-manager.ts +66 -10
  364. package/src/project-config.ts +62 -7
  365. package/src/prompts.ts +2 -0
  366. package/src/pull-utils.ts +217 -23
  367. package/src/resolve-target.ts +419 -0
  368. package/src/restore-system-relation-targets.ts +45 -0
  369. package/src/runtime-routes.ts +7 -0
  370. package/src/schema-ast-v2.ts +2 -1
  371. package/src/schema-sources.ts +248 -0
  372. package/src/scripts/postinstall.ts +7 -1
  373. package/src/self-host-compose.ts +262 -46
  374. package/src/storage-provision.ts +33 -1
  375. package/src/supatype-eval-1781522769253.mts +1 -0
  376. package/src/systemd.ts +2 -2
  377. package/src/target-client.ts +40 -0
  378. package/src/type-extractor.ts +124 -11
  379. package/src/ui/README.md +17 -0
  380. package/src/ui/brand.ts +12 -0
  381. package/src/ui/confirm.ts +38 -0
  382. package/src/ui/fatal.ts +43 -0
  383. package/src/ui/index.ts +8 -0
  384. package/src/ui/interactive.ts +4 -0
  385. package/src/ui/messages.ts +43 -0
  386. package/src/ui/next-steps.ts +10 -0
  387. package/src/ui/progress.ts +28 -0
  388. package/src/ui/prompts.ts +40 -0
  389. package/tests/admin-ensure.test.ts +59 -0
  390. package/tests/cli-help.test.ts +27 -2
  391. package/tests/config.test.ts +29 -2
  392. package/tests/dev-ports.test.ts +41 -0
  393. package/tests/dev-session-lock.test.ts +54 -0
  394. package/tests/dev-ui.test.ts +162 -0
  395. package/tests/docker-runtime.test.ts +236 -0
  396. package/tests/engine-push-output.test.ts +67 -0
  397. package/tests/init.test.ts +197 -18
  398. package/tests/link.test.ts +148 -0
  399. package/tests/minisign.test.ts +102 -0
  400. package/tests/proxy-dev-app.test.ts +45 -1
  401. package/tests/pull-utils.test.ts +5 -4
  402. package/tests/runtime-contract.test.ts +186 -2
  403. package/tests/schema-sources.test.ts +119 -0
  404. package/tests/storage-provision.test.ts +100 -0
  405. package/tests/ui-confirm.test.ts +41 -0
  406. package/tests/ui-messages.test.ts +66 -0
  407. package/tsconfig.tsbuildinfo +1 -1
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from "vitest"
2
2
  import { mkdirSync, rmSync, existsSync, readFileSync, writeFileSync } from "node:fs"
3
3
  import { join } from "node:path"
4
4
  import { tmpdir } from "node:os"
5
- import { scaffold } from "../src/commands/init.js"
5
+ import { scaffold, defaultScaffoldOptions } from "../src/commands/init.js"
6
6
 
7
7
  let tmpRoot: string
8
8
 
@@ -17,7 +17,7 @@ afterEach(() => {
17
17
 
18
18
  describe("scaffold()", () => {
19
19
  it("creates all expected files", () => {
20
- scaffold(tmpRoot, "my-app")
20
+ scaffold(tmpRoot, defaultScaffoldOptions("my-app"))
21
21
 
22
22
  const expected = [
23
23
  "package.json",
@@ -35,17 +35,18 @@ describe("scaffold()", () => {
35
35
  })
36
36
 
37
37
  it("supatype.config.ts embeds the project name and exports defineConfig", () => {
38
- scaffold(tmpRoot, "blog-app")
38
+ scaffold(tmpRoot, defaultScaffoldOptions("blog-app"))
39
39
  const content = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
40
40
  expect(content).toContain("blog-app")
41
41
  expect(content).toContain("defineConfig")
42
- expect(content).toContain('provider: "native"')
42
+ expect(content).toContain('provider: "docker"')
43
43
  expect(content).toContain("schema:")
44
- expect(content).toContain("versions:")
44
+ expect(content).toContain("Optional: pin component versions")
45
+ expect(content).not.toMatch(/^\s*versions:\s*\{/m)
45
46
  })
46
47
 
47
48
  it("package.json includes @supatype/cli and @supatype/types", () => {
48
- scaffold(tmpRoot, "pkg-app")
49
+ scaffold(tmpRoot, defaultScaffoldOptions("pkg-app"))
49
50
  const content = readFileSync(join(tmpRoot, "package.json"), "utf8")
50
51
  expect(content).toContain("@supatype/cli")
51
52
  expect(content).toContain("@supatype/types")
@@ -55,18 +56,18 @@ describe("scaffold()", () => {
55
56
  it("skips package.json when it already exists", () => {
56
57
  const pkgPath = join(tmpRoot, "package.json")
57
58
  writeFileSync(pkgPath, '{"name":"existing"}', "utf8")
58
- scaffold(tmpRoot, "my-app")
59
+ scaffold(tmpRoot, defaultScaffoldOptions("my-app"))
59
60
  expect(readFileSync(pkgPath, "utf8")).toBe('{"name":"existing"}')
60
61
  })
61
62
 
62
63
  it("supatype.config.ts documents self-host workflow", () => {
63
- scaffold(tmpRoot, "my-app")
64
+ scaffold(tmpRoot, defaultScaffoldOptions("my-app"))
64
65
  const content = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
65
66
  expect(content).toContain("self-host")
66
67
  })
67
68
 
68
69
  it(".env contains DATABASE_URL, JWT_SECRET, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB", () => {
69
- scaffold(tmpRoot, "my-app")
70
+ scaffold(tmpRoot, defaultScaffoldOptions("my-app"))
70
71
  const content = readFileSync(join(tmpRoot, ".env"), "utf8")
71
72
  expect(content).toContain("DATABASE_URL=")
72
73
  expect(content).toContain("JWT_SECRET=")
@@ -76,44 +77,45 @@ describe("scaffold()", () => {
76
77
  })
77
78
 
78
79
  it(".env contains ANON_KEY, SERVICE_ROLE_KEY, and SITE_URL placeholders", () => {
79
- scaffold(tmpRoot, "my-app")
80
+ scaffold(tmpRoot, defaultScaffoldOptions("my-app"))
80
81
  const content = readFileSync(join(tmpRoot, ".env"), "utf8")
81
82
  expect(content).toContain("ANON_KEY=")
82
83
  expect(content).toContain("SERVICE_ROLE_KEY=")
83
84
  expect(content).toContain("SITE_URL=")
84
85
  })
85
86
 
86
- it("schema/index.ts exports a User model using RFC v2 Model<>", () => {
87
- scaffold(tmpRoot, "my-app")
87
+ it("schema/index.ts exports a Profile model using RFC v2 Model<>", () => {
88
+ scaffold(tmpRoot, defaultScaffoldOptions("my-app"))
88
89
  const content = readFileSync(join(tmpRoot, "schema/index.ts"), "utf8")
89
- expect(content).toContain("export type User")
90
+ expect(content).toContain("export type Profile")
91
+ expect(content).toContain("display_name")
90
92
  expect(content).toContain("Model<")
91
93
  expect(content).toContain("access:")
92
94
  })
93
95
 
94
96
  it(".gitignore excludes .env, node_modules, and engine binary", () => {
95
- scaffold(tmpRoot, "my-app")
97
+ scaffold(tmpRoot, defaultScaffoldOptions("my-app"))
96
98
  const content = readFileSync(join(tmpRoot, ".gitignore"), "utf8")
97
99
  expect(content).toContain(".env")
98
100
  expect(content).toContain("node_modules/")
99
- expect(content).toContain(".supatype/engine/")
101
+ expect(content).toContain(".supatype/")
100
102
  expect(content).toContain("supatype.local.config.ts")
101
103
  })
102
104
 
103
105
  it("seed.ts references the project name", () => {
104
- scaffold(tmpRoot, "acme")
106
+ scaffold(tmpRoot, defaultScaffoldOptions("acme"))
105
107
  const content = readFileSync(join(tmpRoot, "seed.ts"), "utf8")
106
108
  expect(content).toContain("acme")
107
109
  })
108
110
 
109
111
  it("different project names produce different config bodies", () => {
110
- scaffold(tmpRoot, "alpha")
112
+ scaffold(tmpRoot, defaultScaffoldOptions("alpha"))
111
113
  const alpha = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
112
114
 
113
115
  const tmp2 = join(tmpdir(), `dt-init-test2-${Date.now()}`)
114
116
  mkdirSync(tmp2, { recursive: true })
115
117
  try {
116
- scaffold(tmp2, "beta")
118
+ scaffold(tmp2, defaultScaffoldOptions("beta"))
117
119
  const beta = readFileSync(join(tmp2, "supatype.config.ts"), "utf8")
118
120
  expect(alpha).toContain("alpha")
119
121
  expect(beta).toContain("beta")
@@ -123,4 +125,181 @@ describe("scaffold()", () => {
123
125
  rmSync(tmp2, { recursive: true, force: true })
124
126
  }
125
127
  })
128
+
129
+ it("self-host target emits standalone mode + domain and a local override", () => {
130
+ scaffold(tmpRoot, { ...defaultScaffoldOptions("my-app", "self-host"), domain: "api.example.com" })
131
+ const content = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
132
+ expect(content).toContain('mode: "standalone"')
133
+ expect(content).toContain('domain: "api.example.com"')
134
+ expect(content).toContain('environments: { default: "production" }')
135
+ expect(existsSync(join(tmpRoot, "supatype.local.config.ts"))).toBe(true)
136
+ const local = readFileSync(join(tmpRoot, "supatype.local.config.ts"), "utf8")
137
+ expect(local).toContain('mode: "dev"')
138
+ expect(local).toContain("Partial<SupatypeConfig>")
139
+ })
140
+
141
+ it("self-host with a TLS email emits an active tls block", () => {
142
+ scaffold(tmpRoot, {
143
+ ...defaultScaffoldOptions("my-app", "self-host"),
144
+ domain: "api.example.com",
145
+ tlsEmail: "ops@example.com",
146
+ })
147
+ const content = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
148
+ expect(content).toContain('tls: { email: "ops@example.com" }')
149
+ })
150
+
151
+ it("self-host without a TLS email emits a commented tls hint", () => {
152
+ scaffold(tmpRoot, { ...defaultScaffoldOptions("my-app", "self-host"), domain: "api.example.com" })
153
+ const content = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
154
+ expect(content).toContain('// tls: { email: "you@example.com" }')
155
+ })
156
+
157
+ it("cloud target emits managed mode + environments and a local override", () => {
158
+ scaffold(tmpRoot, defaultScaffoldOptions("my-app", "cloud"))
159
+ const content = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
160
+ expect(content).toContain('mode: "managed"')
161
+ expect(content).toContain('environments: { default: "production" }')
162
+ expect(existsSync(join(tmpRoot, "supatype.local.config.ts"))).toBe(true)
163
+ })
164
+
165
+ it("later target stays in dev mode with no local override", () => {
166
+ scaffold(tmpRoot, defaultScaffoldOptions("my-app", "later"))
167
+ const content = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
168
+ expect(content).toContain('mode: "dev"')
169
+ expect(content).not.toContain("environments:")
170
+ expect(existsSync(join(tmpRoot, "supatype.local.config.ts"))).toBe(false)
171
+ })
172
+
173
+ it("static app mode writes the static dir and config block", () => {
174
+ scaffold(tmpRoot, {
175
+ ...defaultScaffoldOptions("my-app"),
176
+ app: { mode: "static", staticDir: "./dist" },
177
+ })
178
+ const content = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
179
+ expect(content).toContain('mode: "static"')
180
+ expect(content).toContain('static_dir: "./dist"')
181
+ expect(existsSync(join(tmpRoot, "dist/index.html"))).toBe(true)
182
+ const html = readFileSync(join(tmpRoot, "dist/index.html"), "utf8")
183
+ expect(html).toContain("Supatype")
184
+ expect(html).toContain("supatype.github.io/supatype/")
185
+ expect(html).toContain("github.com/supatype")
186
+ })
187
+
188
+ it("static app mode with Vite scaffolds root index.html and vite.config.ts", () => {
189
+ scaffold(tmpRoot, {
190
+ ...defaultScaffoldOptions("my-app"),
191
+ app: {
192
+ mode: "static",
193
+ staticDir: "./public",
194
+ viteDevUrl: "http://127.0.0.1:5173",
195
+ },
196
+ })
197
+ expect(existsSync(join(tmpRoot, "public/index.html"))).toBe(true)
198
+ expect(existsSync(join(tmpRoot, "index.html"))).toBe(true)
199
+ expect(existsSync(join(tmpRoot, "vite.config.ts"))).toBe(true)
200
+ const pkg = readFileSync(join(tmpRoot, "package.json"), "utf8")
201
+ expect(pkg).toContain('"vite": "vite"')
202
+ expect(pkg).toContain('"vite": "^6"')
203
+ const viteConfig = readFileSync(join(tmpRoot, "vite.config.ts"), "utf8")
204
+ expect(viteConfig).toContain("port: 5173")
205
+ })
206
+
207
+ it("proxy app mode writes upstream and start in the config when target is later", () => {
208
+ scaffold(tmpRoot, {
209
+ ...defaultScaffoldOptions("my-app"),
210
+ app: { mode: "proxy", upstream: "http://localhost:4000", start: "dev" },
211
+ })
212
+ const content = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
213
+ expect(content).toContain('mode: "proxy"')
214
+ expect(content).toContain('upstream: "http://localhost:4000"')
215
+ expect(content).toContain('start: "dev"')
216
+ expect(existsSync(join(tmpRoot, "supatype.local.config.ts"))).toBe(false)
217
+ })
218
+
219
+ it("proxy app mode with self-host splits static production config and local proxy override", () => {
220
+ scaffold(tmpRoot, {
221
+ ...defaultScaffoldOptions("my-app", "self-host"),
222
+ domain: "api.example.com",
223
+ app: {
224
+ mode: "proxy",
225
+ upstream: "http://127.0.0.1:5173",
226
+ start: "vite",
227
+ viteDevUrl: "http://127.0.0.1:5173",
228
+ },
229
+ })
230
+ const committed = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
231
+ expect(committed).toContain('mode: "static"')
232
+ expect(committed).toContain('static_dir: "./dist"')
233
+ expect(committed).not.toContain('mode: "proxy"')
234
+
235
+ const local = readFileSync(join(tmpRoot, "supatype.local.config.ts"), "utf8")
236
+ expect(local).toContain('mode: "dev"')
237
+ expect(local).toContain('mode: "proxy"')
238
+ expect(local).toContain('upstream: "http://127.0.0.1:5173"')
239
+ expect(local).toContain('start: "vite"')
240
+ expect(local).toContain('vite_dev_url: "http://127.0.0.1:5173"')
241
+ expect(existsSync(join(tmpRoot, "dist/index.html"))).toBe(true)
242
+ expect(existsSync(join(tmpRoot, "index.html"))).toBe(true)
243
+ expect(existsSync(join(tmpRoot, "vite.config.ts"))).toBe(true)
244
+ })
245
+
246
+ it("proxy app mode with cloud target splits static production config and local proxy override", () => {
247
+ scaffold(tmpRoot, {
248
+ ...defaultScaffoldOptions("my-app", "cloud"),
249
+ app: { mode: "proxy", upstream: "http://localhost:3000", start: "dev" },
250
+ })
251
+ const committed = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
252
+ expect(committed).toContain('mode: "static"')
253
+ expect(committed).not.toContain('mode: "proxy"')
254
+
255
+ const local = readFileSync(join(tmpRoot, "supatype.local.config.ts"), "utf8")
256
+ expect(local).toContain('mode: "proxy"')
257
+ expect(local).toContain('upstream: "http://localhost:3000"')
258
+ })
259
+
260
+ it("hello-world function scaffolds function files and a functions script", () => {
261
+ scaffold(tmpRoot, { ...defaultScaffoldOptions("my-app"), helloFunction: true })
262
+ expect(existsSync(join(tmpRoot, "functions/hello/index.ts"))).toBe(true)
263
+ expect(existsSync(join(tmpRoot, "functions/_shared/README.md"))).toBe(true)
264
+ expect(existsSync(join(tmpRoot, "functions/.env.local"))).toBe(true)
265
+ const pkg = readFileSync(join(tmpRoot, "package.json"), "utf8")
266
+ expect(pkg).toContain("supatype functions serve")
267
+ })
268
+
269
+ it("custom schema path is honored", () => {
270
+ scaffold(tmpRoot, { ...defaultScaffoldOptions("my-app"), schemaPath: "db/schema.ts" })
271
+ expect(existsSync(join(tmpRoot, "db/schema.ts"))).toBe(true)
272
+ const content = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
273
+ expect(content).toContain('path: "db/schema.ts"')
274
+ })
275
+
276
+ it("s3 storage and resend email reflect in config and .env", () => {
277
+ scaffold(tmpRoot, {
278
+ ...defaultScaffoldOptions("my-app"),
279
+ email: "resend",
280
+ storageLocal: "s3",
281
+ storageProduction: "s3",
282
+ })
283
+ const config = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
284
+ expect(config).toContain('email: { provider: "resend" }')
285
+ expect(config).toContain('storage: { provider: "s3" }')
286
+ const env = readFileSync(join(tmpRoot, ".env"), "utf8")
287
+ expect(env).toContain("RESEND_API_KEY=")
288
+ expect(env).toContain("S3_BUCKET=")
289
+ expect(env).toContain("local development and production")
290
+ })
291
+
292
+ it("mixed local dev and s3 production writes both storage sections", () => {
293
+ scaffold(tmpRoot, {
294
+ ...defaultScaffoldOptions("my-app"),
295
+ storageLocal: "local",
296
+ storageProduction: "s3",
297
+ })
298
+ const config = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
299
+ expect(config).toContain('provider: "local"')
300
+ expect(config).toContain("Production storage: external S3")
301
+ const env = readFileSync(join(tmpRoot, ".env"), "utf8")
302
+ expect(env).toContain("local development — MinIO")
303
+ expect(env).toContain("production — external bucket")
304
+ })
126
305
  })
@@ -0,0 +1,148 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"
2
+ import { mkdtempSync, writeFileSync, rmSync, mkdirSync } from "node:fs"
3
+ import { join } from "node:path"
4
+ import { tmpdir } from "node:os"
5
+ import { resolveTarget, targetSchemaRollback } from "../src/resolve-target.js"
6
+ import { loadProjectLink, migrateLegacyLinkFiles } from "../src/link.js"
7
+ import { scaffold } from "../src/commands/init.js"
8
+
9
+ function writePlainConfig(dir: string): void {
10
+ writeFileSync(
11
+ join(dir, "supatype.config.ts"),
12
+ `export default ${JSON.stringify({
13
+ project: { name: "demo" },
14
+ database: { provider: "docker" },
15
+ server: { mode: "dev" },
16
+ app: { mode: "none" },
17
+ schema: { path: "schema/index.ts", pg_schema: "public" },
18
+ })}
19
+ `,
20
+ )
21
+ }
22
+
23
+ describe("link model", () => {
24
+ let tmp: string
25
+
26
+ beforeEach(() => {
27
+ tmp = mkdtempSync(join(tmpdir(), "supatype-link-"))
28
+ })
29
+
30
+ afterEach(() => {
31
+ rmSync(tmp, { recursive: true, force: true })
32
+ })
33
+
34
+ it("migrates cloud.json into link.json", () => {
35
+ scaffold(tmp, "demo")
36
+ mkdirSync(join(tmp, ".supatype"), { recursive: true })
37
+ writeFileSync(join(tmp, ".supatype", "cloud.json"), JSON.stringify({
38
+ apiUrl: "https://api.example.com",
39
+ token: "pat",
40
+ projectSlug: "myproj",
41
+ orgId: "org1",
42
+ }), { encoding: "utf8" })
43
+
44
+ migrateLegacyLinkFiles(tmp)
45
+ const link = loadProjectLink(tmp)
46
+ expect(link?.kind).toBe("cloud")
47
+ expect(link?.projectRef).toBe("myproj")
48
+ expect(link?.environments.production?.apiUrl).toBe("https://api.example.com")
49
+ })
50
+
51
+ it("resolveTarget returns cloud mode with /api/v1 prefix", () => {
52
+ writePlainConfig(tmp)
53
+ mkdirSync(join(tmp, ".supatype"), { recursive: true })
54
+ writeFileSync(join(tmp, ".supatype", "link.json"), JSON.stringify({
55
+ version: 1,
56
+ kind: "cloud",
57
+ projectRef: "myproj",
58
+ defaultEnvironment: "production",
59
+ token: "pat",
60
+ orgId: "org1",
61
+ cloudApiUrl: "https://api.example.com",
62
+ linkedAt: new Date().toISOString(),
63
+ environments: {
64
+ production: {
65
+ name: "production",
66
+ apiUrl: "https://myproj.supatype.dev",
67
+ linkedAt: new Date().toISOString(),
68
+ },
69
+ },
70
+ }))
71
+
72
+ const target = resolveTarget(tmp)
73
+ expect(target.mode).toBe("cloud")
74
+ expect(target.apiPrefix).toBe("/api/v1")
75
+ expect(target.apiBaseUrl).toBe("https://api.example.com")
76
+ })
77
+
78
+ it("resolveTarget returns self-host mode with /platform/v1 prefix", () => {
79
+ writePlainConfig(tmp)
80
+ mkdirSync(join(tmp, ".supatype"), { recursive: true })
81
+ writeFileSync(join(tmp, ".supatype", "link.json"), JSON.stringify({
82
+ version: 1,
83
+ kind: "self-host",
84
+ projectRef: "demo",
85
+ defaultEnvironment: "production",
86
+ linkedAt: new Date().toISOString(),
87
+ environments: {
88
+ production: {
89
+ name: "production",
90
+ apiUrl: "https://app.example.com",
91
+ token: "srk",
92
+ linkedAt: new Date().toISOString(),
93
+ },
94
+ },
95
+ }))
96
+
97
+ const target = resolveTarget(tmp)
98
+ expect(target.mode).toBe("self-host")
99
+ expect(target.apiPrefix).toBe("/platform/v1")
100
+ expect(target.token).toBe("srk")
101
+ })
102
+
103
+ it("targetSchemaRollback posts to /platform/v1 when linked", async () => {
104
+ writePlainConfig(tmp)
105
+ mkdirSync(join(tmp, ".supatype"), { recursive: true })
106
+ writeFileSync(join(tmp, ".supatype", "link.json"), JSON.stringify({
107
+ version: 1,
108
+ kind: "self-host",
109
+ projectRef: "demo",
110
+ defaultEnvironment: "production",
111
+ linkedAt: new Date().toISOString(),
112
+ environments: {
113
+ production: {
114
+ name: "production",
115
+ apiUrl: "https://app.example.com",
116
+ token: "srk",
117
+ linkedAt: new Date().toISOString(),
118
+ },
119
+ },
120
+ }))
121
+
122
+ const fetchMock = vi.fn().mockResolvedValue({
123
+ ok: true,
124
+ json: async () => ({
125
+ data: {
126
+ status: "rolled_back",
127
+ name: "push_test",
128
+ message: "Rolled back migration push_test.",
129
+ },
130
+ }),
131
+ })
132
+ vi.stubGlobal("fetch", fetchMock)
133
+
134
+ const target = resolveTarget(tmp)
135
+ await targetSchemaRollback(target, { schema: "public" })
136
+
137
+ expect(fetchMock).toHaveBeenCalledOnce()
138
+ const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit]
139
+ expect(url).toBe("https://app.example.com/platform/v1/projects/demo/schema/rollback")
140
+ expect(init.method).toBe("POST")
141
+ expect(init.headers).toMatchObject({
142
+ Authorization: "Bearer srk",
143
+ "Content-Type": "application/json",
144
+ })
145
+
146
+ vi.unstubAllGlobals()
147
+ })
148
+ })
@@ -0,0 +1,102 @@
1
+ import { describe, it, expect } from "vitest"
2
+ import {
3
+ createHash,
4
+ generateKeyPairSync,
5
+ randomBytes,
6
+ sign as edSign,
7
+ type KeyObject,
8
+ } from "node:crypto"
9
+ import { verifyMinisign } from "../src/binary-cache.js"
10
+
11
+ /**
12
+ * Build a minisign public-key string + signature file for the given mode so we
13
+ * can exercise {@link verifyMinisign} without shelling out to the minisign tool.
14
+ *
15
+ * Layout mirrors the minisign format the verifier parses:
16
+ * public key payload: [2 algo]["Ed"][8 keyId][32 raw ed25519 pubkey]
17
+ * signature payload : [2 algo][8 keyId][64 ed25519 signature]
18
+ */
19
+ function makeMinisign(
20
+ data: Buffer,
21
+ mode: "legacy" | "prehashed",
22
+ opts: { keyId?: Buffer; privateKey?: KeyObject; publicKey?: KeyObject } = {},
23
+ ): { pubKeyStr: string; sigFile: string } {
24
+ const { publicKey, privateKey } =
25
+ opts.privateKey && opts.publicKey
26
+ ? { publicKey: opts.publicKey, privateKey: opts.privateKey }
27
+ : generateKeyPairSync("ed25519")
28
+
29
+ const keyId = opts.keyId ?? randomBytes(8)
30
+
31
+ // Raw 32-byte ed25519 key = SPKI DER minus its 12-byte prefix.
32
+ const rawPub = publicKey.export({ format: "der", type: "spki" }).subarray(12)
33
+ const pkPayload = Buffer.concat([Buffer.from("Ed"), keyId, rawPub])
34
+ const pubKeyStr = `untrusted comment: test\n${pkPayload.toString("base64")}`
35
+
36
+ const prehashed = mode === "prehashed"
37
+ const signedData = prehashed
38
+ ? createHash("blake2b512").update(data).digest()
39
+ : data
40
+ const signature = edSign(null, signedData, privateKey)
41
+
42
+ const algo = prehashed ? Buffer.from("ED") : Buffer.from("Ed")
43
+ const sigPayload = Buffer.concat([algo, keyId, signature])
44
+ const sigFile =
45
+ `untrusted comment: signature\n${sigPayload.toString("base64")}\n` +
46
+ `trusted comment: timestamp\n${Buffer.alloc(64).toString("base64")}`
47
+
48
+ return { pubKeyStr, sigFile }
49
+ }
50
+
51
+ describe("verifyMinisign", () => {
52
+ const data = Buffer.from("the quick brown fox\n")
53
+
54
+ it("accepts a valid legacy ('Ed') signature", () => {
55
+ const keyId = randomBytes(8)
56
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519")
57
+ const { pubKeyStr, sigFile } = makeMinisign(data, "legacy", { keyId, privateKey, publicKey })
58
+ expect(() => verifyMinisign(data, sigFile, pubKeyStr)).not.toThrow()
59
+ })
60
+
61
+ it("accepts a valid prehashed ('ED', BLAKE2b-512) signature", () => {
62
+ const keyId = randomBytes(8)
63
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519")
64
+ const { pubKeyStr, sigFile } = makeMinisign(data, "prehashed", { keyId, privateKey, publicKey })
65
+ expect(() => verifyMinisign(data, sigFile, pubKeyStr)).not.toThrow()
66
+ })
67
+
68
+ it("rejects a prehashed signature when the file was tampered", () => {
69
+ const { pubKeyStr, sigFile } = makeMinisign(data, "prehashed")
70
+ const tampered = Buffer.from("the quick brown dog\n")
71
+ expect(() => verifyMinisign(tampered, sigFile, pubKeyStr)).toThrow(/verification FAILED/i)
72
+ })
73
+
74
+ it("rejects when the signature key id does not match the public key", () => {
75
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519")
76
+ const { sigFile } = makeMinisign(data, "prehashed", {
77
+ keyId: randomBytes(8),
78
+ privateKey,
79
+ publicKey,
80
+ })
81
+ // Public key advertising a different key id.
82
+ const { pubKeyStr } = makeMinisign(data, "prehashed", {
83
+ keyId: randomBytes(8),
84
+ privateKey,
85
+ publicKey,
86
+ })
87
+ expect(() => verifyMinisign(data, sigFile, pubKeyStr)).toThrow(/key ID mismatch/i)
88
+ })
89
+
90
+ it("rejects an unsupported algorithm", () => {
91
+ const { pubKeyStr, sigFile } = makeMinisign(data, "legacy")
92
+ // Corrupt the algorithm bytes in the signature payload to "XX".
93
+ const lines = sigFile.split("\n")
94
+ const sigBytes = Buffer.from(lines[1]!, "base64")
95
+ sigBytes[0] = 0x58
96
+ sigBytes[1] = 0x58
97
+ lines[1] = sigBytes.toString("base64")
98
+ expect(() => verifyMinisign(data, lines.join("\n"), pubKeyStr)).toThrow(
99
+ /Unsupported minisign algorithm/i,
100
+ )
101
+ })
102
+ })
@@ -1,5 +1,12 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs"
2
+ import { join } from "node:path"
3
+ import { tmpdir } from "node:os"
1
4
  import { describe, expect, it } from "vitest"
2
- import { resolveProxyDevScript } from "../src/app/proxy-dev-app.js"
5
+ import {
6
+ portFromUpstream,
7
+ resolveProxyDevScript,
8
+ resolveViteDirectSpawn,
9
+ } from "../src/app/proxy-dev-app.js"
3
10
  import type { SupatypeProjectConfig } from "../src/project-config.js"
4
11
 
5
12
  const base = {
@@ -31,3 +38,40 @@ describe("resolveProxyDevScript()", () => {
31
38
  expect(resolveProxyDevScript(config)).toBe("dev:site")
32
39
  })
33
40
  })
41
+
42
+ describe("portFromUpstream()", () => {
43
+ it("parses port from upstream URL", () => {
44
+ const config = {
45
+ ...base,
46
+ app: { mode: "proxy" as const, upstream: "http://localhost:5285" },
47
+ } satisfies SupatypeProjectConfig
48
+ expect(portFromUpstream(config)).toBe(5285)
49
+ })
50
+
51
+ it("returns null when upstream has no port", () => {
52
+ const config = {
53
+ ...base,
54
+ app: { mode: "proxy" as const, upstream: "http://localhost" },
55
+ } satisfies SupatypeProjectConfig
56
+ expect(portFromUpstream(config)).toBeNull()
57
+ })
58
+ })
59
+
60
+ describe("resolveViteDirectSpawn()", () => {
61
+ it("returns node + vite.js for a plain vite script", () => {
62
+ const appDir = join(tmpdir(), `supatype-vite-spawn-${Date.now()}`)
63
+ const viteJs = join(appDir, "node_modules", "vite", "bin", "vite.js")
64
+ mkdirSync(join(appDir, "node_modules", "vite", "bin"), { recursive: true })
65
+ writeFileSync(viteJs, "")
66
+ const result = resolveViteDirectSpawn(appDir, "dev:vite", { "dev:vite": "vite" })
67
+ expect(result).not.toBeNull()
68
+ expect(result?.bin).toBe(process.execPath)
69
+ expect(result?.args[0]).toBe(viteJs)
70
+ expect(result?.shell).toBe(false)
71
+ })
72
+
73
+ it("returns null for non-vite scripts", () => {
74
+ const result = resolveViteDirectSpawn(process.cwd(), "dev", { dev: "next dev" })
75
+ expect(result).toBeNull()
76
+ })
77
+ })
@@ -106,12 +106,12 @@ describe("pgTypeToField()", () => {
106
106
  })
107
107
 
108
108
  describe("introspectColumnToColumnInfo()", () => {
109
- it("maps engine type → pgType and flags", () => {
109
+ it("maps engine column → pgType and flags", () => {
110
110
  const out = introspectColumnToColumnInfo({
111
111
  name: "id",
112
- type: "uuid",
112
+ dataType: "uuid",
113
+ udtName: "uuid",
113
114
  nullable: false,
114
- primaryKey: true,
115
115
  default: "gen_random_uuid()",
116
116
  })
117
117
  expect(out).toEqual({
@@ -127,7 +127,8 @@ describe("introspectColumnToColumnInfo()", () => {
127
127
  it("treats empty default as no default", () => {
128
128
  const out = introspectColumnToColumnInfo({
129
129
  name: "x",
130
- type: "text",
130
+ dataType: "text",
131
+ udtName: "text",
131
132
  nullable: true,
132
133
  default: "",
133
134
  })