alchemy 0.45.5 → 0.46.0

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 (346) hide show
  1. package/README.md +2 -2
  2. package/bin/alchemy.mjs +19285 -15137
  3. package/bin/alchemy.ts +14 -56
  4. package/bin/commands/create.ts +307 -137
  5. package/bin/commands/deploy.ts +23 -0
  6. package/bin/commands/destroy.ts +19 -0
  7. package/bin/commands/dev.ts +21 -0
  8. package/bin/commands/login.ts +66 -0
  9. package/bin/commands/run.ts +29 -0
  10. package/bin/services/dependencies.ts +0 -5
  11. package/bin/services/execute-alchemy.ts +160 -0
  12. package/bin/services/git.ts +27 -0
  13. package/bin/services/github-workflow.ts +229 -0
  14. package/bin/services/package-manager.ts +51 -68
  15. package/bin/services/template-manager.ts +10 -21
  16. package/bin/trpc.ts +3 -0
  17. package/bin/types.ts +4 -2
  18. package/lib/apply.d.ts +2 -0
  19. package/lib/apply.d.ts.map +1 -1
  20. package/lib/apply.js +26 -8
  21. package/lib/apply.js.map +1 -1
  22. package/lib/build-date.d.ts +1 -1
  23. package/lib/build-date.js +1 -1
  24. package/lib/cloudflare/ai.d.ts +8 -4
  25. package/lib/cloudflare/ai.d.ts.map +1 -1
  26. package/lib/cloudflare/ai.js +6 -8
  27. package/lib/cloudflare/ai.js.map +1 -1
  28. package/lib/cloudflare/analytics-engine.d.ts +11 -7
  29. package/lib/cloudflare/analytics-engine.d.ts.map +1 -1
  30. package/lib/cloudflare/analytics-engine.js +11 -9
  31. package/lib/cloudflare/analytics-engine.js.map +1 -1
  32. package/lib/cloudflare/{types.d.ts → api-response.d.ts} +6 -1
  33. package/lib/cloudflare/api-response.d.ts.map +1 -0
  34. package/lib/cloudflare/api-response.js +38 -0
  35. package/lib/cloudflare/api-response.js.map +1 -0
  36. package/lib/cloudflare/auth-wrangler.d.ts +25 -0
  37. package/lib/cloudflare/auth-wrangler.d.ts.map +1 -0
  38. package/lib/cloudflare/auth-wrangler.js +224 -0
  39. package/lib/cloudflare/auth-wrangler.js.map +1 -0
  40. package/lib/cloudflare/auth.d.ts.map +1 -1
  41. package/lib/cloudflare/auth.js +7 -116
  42. package/lib/cloudflare/auth.js.map +1 -1
  43. package/lib/cloudflare/browser-rendering.d.ts +12 -3
  44. package/lib/cloudflare/browser-rendering.d.ts.map +1 -1
  45. package/lib/cloudflare/browser-rendering.js +12 -2
  46. package/lib/cloudflare/browser-rendering.js.map +1 -1
  47. package/lib/cloudflare/bundle/fs.d.ts.map +1 -1
  48. package/lib/cloudflare/bundle/fs.js +3 -0
  49. package/lib/cloudflare/bundle/fs.js.map +1 -1
  50. package/lib/cloudflare/bundle/internal-worker-bundle.d.ts +6 -0
  51. package/lib/cloudflare/bundle/internal-worker-bundle.d.ts.map +1 -0
  52. package/lib/cloudflare/bundle/internal-worker-bundle.js +14 -0
  53. package/lib/cloudflare/bundle/internal-worker-bundle.js.map +1 -0
  54. package/lib/cloudflare/compatibility-presets.d.ts +22 -0
  55. package/lib/cloudflare/compatibility-presets.d.ts.map +1 -0
  56. package/lib/cloudflare/compatibility-presets.js +27 -0
  57. package/lib/cloudflare/compatibility-presets.js.map +1 -0
  58. package/lib/cloudflare/container.d.ts +6 -2
  59. package/lib/cloudflare/container.d.ts.map +1 -1
  60. package/lib/cloudflare/container.js +48 -4
  61. package/lib/cloudflare/container.js.map +1 -1
  62. package/lib/cloudflare/dispatch-namespace.d.ts +5 -0
  63. package/lib/cloudflare/dispatch-namespace.d.ts.map +1 -1
  64. package/lib/cloudflare/dispatch-namespace.js +8 -0
  65. package/lib/cloudflare/dispatch-namespace.js.map +1 -1
  66. package/lib/cloudflare/durable-object-namespace.d.ts +23 -15
  67. package/lib/cloudflare/durable-object-namespace.d.ts.map +1 -1
  68. package/lib/cloudflare/durable-object-namespace.js +22 -21
  69. package/lib/cloudflare/durable-object-namespace.js.map +1 -1
  70. package/lib/cloudflare/hyperdrive.d.ts +19 -5
  71. package/lib/cloudflare/hyperdrive.d.ts.map +1 -1
  72. package/lib/cloudflare/hyperdrive.js +14 -0
  73. package/lib/cloudflare/hyperdrive.js.map +1 -1
  74. package/lib/cloudflare/images.d.ts +27 -17
  75. package/lib/cloudflare/images.d.ts.map +1 -1
  76. package/lib/cloudflare/images.js +13 -8
  77. package/lib/cloudflare/images.js.map +1 -1
  78. package/lib/cloudflare/index.d.ts +1 -3
  79. package/lib/cloudflare/index.d.ts.map +1 -1
  80. package/lib/cloudflare/index.js +1 -3
  81. package/lib/cloudflare/index.js.map +1 -1
  82. package/lib/cloudflare/miniflare/miniflare.d.ts +3 -3
  83. package/lib/cloudflare/miniflare/miniflare.d.ts.map +1 -1
  84. package/lib/cloudflare/miniflare/miniflare.js +7 -7
  85. package/lib/cloudflare/miniflare/miniflare.js.map +1 -1
  86. package/lib/cloudflare/miniflare/{mixed-mode.d.ts → remote-binding-proxy.d.ts} +5 -5
  87. package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts.map +1 -0
  88. package/lib/cloudflare/miniflare/{mixed-mode.js → remote-binding-proxy.js} +16 -52
  89. package/lib/cloudflare/miniflare/remote-binding-proxy.js.map +1 -0
  90. package/lib/cloudflare/state.d.ts +26 -0
  91. package/lib/cloudflare/state.d.ts.map +1 -0
  92. package/lib/cloudflare/state.js +14 -0
  93. package/lib/cloudflare/state.js.map +1 -0
  94. package/lib/cloudflare/user.d.ts +2 -2
  95. package/lib/cloudflare/user.d.ts.map +1 -1
  96. package/lib/cloudflare/user.js +7 -21
  97. package/lib/cloudflare/user.js.map +1 -1
  98. package/lib/cloudflare/vectorize-index.d.ts.map +1 -1
  99. package/lib/cloudflare/vectorize-index.js +5 -2
  100. package/lib/cloudflare/vectorize-index.js.map +1 -1
  101. package/lib/cloudflare/version-metadata.d.ts +15 -3
  102. package/lib/cloudflare/version-metadata.d.ts.map +1 -1
  103. package/lib/cloudflare/version-metadata.js +11 -2
  104. package/lib/cloudflare/version-metadata.js.map +1 -1
  105. package/lib/cloudflare/vite.d.ts.map +1 -1
  106. package/lib/cloudflare/vite.js +2 -1
  107. package/lib/cloudflare/vite.js.map +1 -1
  108. package/lib/cloudflare/worker-metadata.d.ts +13 -2
  109. package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
  110. package/lib/cloudflare/worker-metadata.js +10 -15
  111. package/lib/cloudflare/worker-metadata.js.map +1 -1
  112. package/lib/cloudflare/worker-subdomain.d.ts +7 -0
  113. package/lib/cloudflare/worker-subdomain.d.ts.map +1 -1
  114. package/lib/cloudflare/worker-subdomain.js +17 -22
  115. package/lib/cloudflare/worker-subdomain.js.map +1 -1
  116. package/lib/cloudflare/{worker/tail.d.ts → worker-tail.d.ts} +2 -2
  117. package/lib/cloudflare/worker-tail.d.ts.map +1 -0
  118. package/lib/cloudflare/{worker/tail.js → worker-tail.js} +11 -9
  119. package/lib/cloudflare/worker-tail.js.map +1 -0
  120. package/lib/cloudflare/worker.d.ts +18 -8
  121. package/lib/cloudflare/worker.d.ts.map +1 -1
  122. package/lib/cloudflare/worker.js +32 -8
  123. package/lib/cloudflare/worker.js.map +1 -1
  124. package/lib/cloudflare/workflow.d.ts +22 -11
  125. package/lib/cloudflare/workflow.d.ts.map +1 -1
  126. package/lib/cloudflare/workflow.js +23 -18
  127. package/lib/cloudflare/workflow.js.map +1 -1
  128. package/lib/context.d.ts +2 -2
  129. package/lib/context.d.ts.map +1 -1
  130. package/lib/docker/api.d.ts.map +1 -1
  131. package/lib/docker/api.js +30 -8
  132. package/lib/docker/api.js.map +1 -1
  133. package/lib/fs/file-system-state-store.d.ts +5 -20
  134. package/lib/fs/file-system-state-store.d.ts.map +1 -1
  135. package/lib/fs/file-system-state-store.js +5 -121
  136. package/lib/fs/file-system-state-store.js.map +1 -1
  137. package/lib/fs/index.d.ts +0 -1
  138. package/lib/fs/index.d.ts.map +1 -1
  139. package/lib/fs/index.js +0 -1
  140. package/lib/fs/index.js.map +1 -1
  141. package/lib/llms.cloudflare.txt +6 -5
  142. package/lib/planetscale/password.js +1 -1
  143. package/lib/planetscale/password.js.map +1 -1
  144. package/lib/scope.d.ts.map +1 -1
  145. package/lib/scope.js +3 -6
  146. package/lib/scope.js.map +1 -1
  147. package/lib/state/cloudflare-state-store.d.ts +34 -0
  148. package/lib/state/cloudflare-state-store.d.ts.map +1 -0
  149. package/lib/state/cloudflare-state-store.js +116 -0
  150. package/lib/state/cloudflare-state-store.js.map +1 -0
  151. package/lib/state/d1-state-store.d.ts +15 -0
  152. package/lib/state/d1-state-store.d.ts.map +1 -0
  153. package/lib/{cloudflare → state}/d1-state-store.js +31 -27
  154. package/lib/state/d1-state-store.js.map +1 -0
  155. package/lib/{cloudflare/do-state-store/types.d.ts → state/dofs-state-store/api.d.ts} +1 -1
  156. package/lib/state/dofs-state-store/api.d.ts.map +1 -0
  157. package/lib/state/dofs-state-store/api.js +1 -0
  158. package/lib/state/dofs-state-store/api.js.map +1 -0
  159. package/lib/{cloudflare/do-state-store/store.d.ts → state/dofs-state-store/index.d.ts} +10 -5
  160. package/lib/state/dofs-state-store/index.d.ts.map +1 -0
  161. package/lib/{cloudflare/do-state-store/store.js → state/dofs-state-store/index.js} +15 -11
  162. package/lib/state/dofs-state-store/index.js.map +1 -0
  163. package/lib/{cloudflare/do-state-store/internal.d.ts → state/dofs-state-store/store.d.ts} +4 -5
  164. package/lib/state/dofs-state-store/store.d.ts.map +1 -0
  165. package/lib/{cloudflare/do-state-store/internal.js → state/dofs-state-store/store.js} +7 -22
  166. package/lib/state/dofs-state-store/store.js.map +1 -0
  167. package/lib/state/file-system-state-store.d.ts +21 -0
  168. package/lib/state/file-system-state-store.d.ts.map +1 -0
  169. package/lib/state/file-system-state-store.js +122 -0
  170. package/lib/state/file-system-state-store.js.map +1 -0
  171. package/lib/state/index.d.ts +7 -0
  172. package/lib/state/index.d.ts.map +1 -0
  173. package/lib/state/index.js +7 -0
  174. package/lib/state/index.js.map +1 -0
  175. package/lib/state/migrations.d.ts +2 -0
  176. package/lib/state/migrations.d.ts.map +1 -0
  177. package/lib/state/migrations.js +3 -0
  178. package/lib/state/migrations.js.map +1 -0
  179. package/lib/state/operations.d.ts +36 -0
  180. package/lib/state/operations.d.ts.map +1 -0
  181. package/lib/state/operations.js +126 -0
  182. package/lib/state/operations.js.map +1 -0
  183. package/lib/state/proxy.d.ts +53 -0
  184. package/lib/state/proxy.d.ts.map +1 -0
  185. package/lib/state/proxy.js +65 -0
  186. package/lib/state/proxy.js.map +1 -0
  187. package/lib/{cloudflare → state}/r2-rest-state-store.d.ts +3 -1
  188. package/lib/state/r2-rest-state-store.d.ts.map +1 -0
  189. package/lib/{cloudflare → state}/r2-rest-state-store.js +6 -4
  190. package/lib/state/r2-rest-state-store.js.map +1 -0
  191. package/lib/state/schema.d.ts.map +1 -0
  192. package/lib/state/schema.js.map +1 -0
  193. package/lib/{sqlite → state}/sqlite-state-store.d.ts +5 -3
  194. package/lib/state/sqlite-state-store.d.ts.map +1 -0
  195. package/lib/{sqlite → state}/sqlite-state-store.js +15 -6
  196. package/lib/state/sqlite-state-store.js.map +1 -0
  197. package/lib/test/vitest.d.ts.map +1 -1
  198. package/lib/test/vitest.js +3 -6
  199. package/lib/test/vitest.js.map +1 -1
  200. package/lib/util/detect-node-runtime.d.ts +3 -0
  201. package/lib/util/detect-node-runtime.d.ts.map +1 -0
  202. package/lib/util/detect-node-runtime.js +10 -0
  203. package/lib/util/detect-node-runtime.js.map +1 -0
  204. package/lib/util/detect-package-manager.d.ts +2 -2
  205. package/lib/util/detect-package-manager.d.ts.map +1 -1
  206. package/lib/util/detect-package-manager.js +15 -5
  207. package/lib/util/detect-package-manager.js.map +1 -1
  208. package/lib/util/http-server.d.ts.map +1 -0
  209. package/lib/{cloudflare/miniflare → util}/http-server.js +1 -1
  210. package/lib/util/http-server.js.map +1 -0
  211. package/lib/util/memoize.d.ts +2 -0
  212. package/lib/util/memoize.d.ts.map +1 -1
  213. package/lib/util/memoize.js +14 -7
  214. package/lib/util/memoize.js.map +1 -1
  215. package/lib/util/neverthrow.d.ts +9 -0
  216. package/lib/util/neverthrow.d.ts.map +1 -0
  217. package/lib/util/neverthrow.js +29 -0
  218. package/lib/util/neverthrow.js.map +1 -0
  219. package/package.json +20 -20
  220. package/src/apply.ts +31 -10
  221. package/src/build-date.ts +1 -1
  222. package/src/cloudflare/ai.ts +10 -5
  223. package/src/cloudflare/analytics-engine.ts +20 -11
  224. package/src/cloudflare/{types.ts → api-response.ts} +31 -0
  225. package/src/cloudflare/auth-wrangler.ts +314 -0
  226. package/src/cloudflare/auth.ts +8 -156
  227. package/src/cloudflare/browser-rendering.ts +16 -2
  228. package/src/cloudflare/bundle/fs.ts +5 -0
  229. package/src/cloudflare/bundle/internal-worker-bundle.ts +21 -0
  230. package/src/cloudflare/compatibility-presets.ts +34 -0
  231. package/src/cloudflare/container.ts +77 -6
  232. package/src/cloudflare/dispatch-namespace.ts +28 -0
  233. package/src/cloudflare/durable-object-namespace.ts +38 -27
  234. package/src/cloudflare/hyperdrive.ts +19 -5
  235. package/src/cloudflare/images.ts +32 -17
  236. package/src/cloudflare/index.ts +1 -3
  237. package/src/cloudflare/miniflare/miniflare.ts +10 -7
  238. package/src/cloudflare/miniflare/{mixed-mode.ts → remote-binding-proxy.ts} +25 -75
  239. package/src/cloudflare/state.ts +35 -0
  240. package/src/cloudflare/user.ts +55 -70
  241. package/src/cloudflare/vectorize-index.ts +7 -2
  242. package/src/cloudflare/version-metadata.ts +19 -2
  243. package/src/cloudflare/vite.ts +2 -1
  244. package/src/cloudflare/worker-metadata.ts +18 -43
  245. package/src/cloudflare/worker-subdomain.ts +43 -37
  246. package/src/cloudflare/{worker/tail.ts → worker-tail.ts} +13 -10
  247. package/src/cloudflare/worker.ts +66 -20
  248. package/src/cloudflare/workflow.ts +38 -19
  249. package/src/context.ts +2 -2
  250. package/src/docker/api.ts +33 -8
  251. package/src/fs/file-system-state-store.ts +5 -148
  252. package/src/fs/index.ts +0 -1
  253. package/src/llms.cloudflare.txt +6 -5
  254. package/src/planetscale/password.ts +1 -1
  255. package/src/scope.ts +3 -6
  256. package/src/state/cloudflare-state-store.ts +170 -0
  257. package/src/state/d1-state-store.ts +102 -0
  258. package/src/{cloudflare/do-state-store/store.ts → state/dofs-state-store/index.ts} +20 -13
  259. package/src/{cloudflare/do-state-store/internal.ts → state/dofs-state-store/store.ts} +9 -27
  260. package/src/state/file-system-state-store.ts +149 -0
  261. package/src/state/index.ts +6 -0
  262. package/src/state/migrations.ts +9 -0
  263. package/src/state/operations.ts +152 -0
  264. package/src/state/proxy.ts +132 -0
  265. package/src/{cloudflare → state}/r2-rest-state-store.ts +11 -8
  266. package/src/{sqlite → state}/sqlite-state-store.ts +18 -10
  267. package/src/test/vitest.ts +8 -6
  268. package/src/util/detect-node-runtime.ts +12 -0
  269. package/src/util/detect-package-manager.ts +19 -6
  270. package/src/{cloudflare/miniflare → util}/http-server.ts +1 -1
  271. package/src/util/memoize.ts +18 -7
  272. package/src/util/neverthrow.ts +36 -0
  273. package/templates/astro/package.json +3 -3
  274. package/templates/nuxt/package.json +3 -3
  275. package/templates/react-router/package.json +3 -3
  276. package/templates/rwsdk/alchemy.run.ts +1 -1
  277. package/templates/rwsdk/package.json +3 -3
  278. package/templates/sveltekit/package.json +3 -3
  279. package/templates/tanstack-start/package.json +3 -3
  280. package/templates/typescript/package.json +3 -2
  281. package/templates/typescript/tsconfig.json +1 -0
  282. package/templates/vite/package.json +3 -3
  283. package/workers/cloudflare-state-store.js +5638 -0
  284. package/workers/cloudflare-state-store.ts +108 -0
  285. package/workers/{do-state-store.js → dofs-state-store.js} +4 -4
  286. package/workers/{do-state-store.ts → dofs-state-store.ts} +12 -8
  287. package/workers/{mixed-mode-proxy-worker.js → remote-binding-proxy.js} +3 -3
  288. package/workers/remote-binding-proxy.ts +45 -0
  289. package/lib/cloudflare/d1-state-store.d.ts +0 -10
  290. package/lib/cloudflare/d1-state-store.d.ts.map +0 -1
  291. package/lib/cloudflare/d1-state-store.js.map +0 -1
  292. package/lib/cloudflare/do-state-store/index.d.ts +0 -2
  293. package/lib/cloudflare/do-state-store/index.d.ts.map +0 -1
  294. package/lib/cloudflare/do-state-store/index.js +0 -2
  295. package/lib/cloudflare/do-state-store/index.js.map +0 -1
  296. package/lib/cloudflare/do-state-store/internal.d.ts.map +0 -1
  297. package/lib/cloudflare/do-state-store/internal.js.map +0 -1
  298. package/lib/cloudflare/do-state-store/store.d.ts.map +0 -1
  299. package/lib/cloudflare/do-state-store/store.js.map +0 -1
  300. package/lib/cloudflare/do-state-store/types.d.ts.map +0 -1
  301. package/lib/cloudflare/do-state-store/types.js +0 -1
  302. package/lib/cloudflare/do-state-store/types.js.map +0 -1
  303. package/lib/cloudflare/miniflare/http-server.d.ts.map +0 -1
  304. package/lib/cloudflare/miniflare/http-server.js.map +0 -1
  305. package/lib/cloudflare/miniflare/mixed-mode.d.ts.map +0 -1
  306. package/lib/cloudflare/miniflare/mixed-mode.js.map +0 -1
  307. package/lib/cloudflare/r2-rest-state-store.d.ts.map +0 -1
  308. package/lib/cloudflare/r2-rest-state-store.js.map +0 -1
  309. package/lib/cloudflare/types.d.ts.map +0 -1
  310. package/lib/cloudflare/types.js +0 -19
  311. package/lib/cloudflare/types.js.map +0 -1
  312. package/lib/cloudflare/worker/shared.d.ts +0 -4
  313. package/lib/cloudflare/worker/shared.d.ts.map +0 -1
  314. package/lib/cloudflare/worker/shared.js +0 -21
  315. package/lib/cloudflare/worker/shared.js.map +0 -1
  316. package/lib/cloudflare/worker/tail.d.ts.map +0 -1
  317. package/lib/cloudflare/worker/tail.js.map +0 -1
  318. package/lib/sqlite/base-sqlite-state-store.d.ts +0 -31
  319. package/lib/sqlite/base-sqlite-state-store.d.ts.map +0 -1
  320. package/lib/sqlite/base-sqlite-state-store.js +0 -124
  321. package/lib/sqlite/base-sqlite-state-store.js.map +0 -1
  322. package/lib/sqlite/index.d.ts +0 -2
  323. package/lib/sqlite/index.d.ts.map +0 -1
  324. package/lib/sqlite/index.js +0 -2
  325. package/lib/sqlite/index.js.map +0 -1
  326. package/lib/sqlite/schema.d.ts.map +0 -1
  327. package/lib/sqlite/schema.js.map +0 -1
  328. package/lib/sqlite/sqlite-state-store.d.ts.map +0 -1
  329. package/lib/sqlite/sqlite-state-store.js.map +0 -1
  330. package/src/cloudflare/d1-state-store.ts +0 -105
  331. package/src/cloudflare/do-state-store/index.ts +0 -1
  332. package/src/cloudflare/worker/shared.ts +0 -30
  333. package/src/sqlite/base-sqlite-state-store.ts +0 -199
  334. package/src/sqlite/index.ts +0 -1
  335. package/templates/typescript/src/worker.js +0 -5
  336. package/workers/mixed-mode-proxy-worker.ts +0 -46
  337. /package/drizzle/{0000_busy_mauler.sql → default/0000_busy_mauler.sql} +0 -0
  338. /package/drizzle/{0001_concerned_iceman.sql → default/0001_concerned_iceman.sql} +0 -0
  339. /package/drizzle/{meta → default/meta}/0000_snapshot.json +0 -0
  340. /package/drizzle/{meta → default/meta}/0001_snapshot.json +0 -0
  341. /package/drizzle/{meta → default/meta}/_journal.json +0 -0
  342. /package/lib/{sqlite → state}/schema.d.ts +0 -0
  343. /package/lib/{sqlite → state}/schema.js +0 -0
  344. /package/lib/{cloudflare/miniflare → util}/http-server.d.ts +0 -0
  345. /package/src/{cloudflare/do-state-store/types.ts → state/dofs-state-store/api.ts} +0 -0
  346. /package/src/{sqlite → state}/schema.ts +0 -0
package/bin/alchemy.ts CHANGED
@@ -1,64 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { createCli, trpcServer, zod as z } from "trpc-cli";
4
- import { createAlchemy } from "./commands/create.ts";
3
+ import { createCli } from "trpc-cli";
4
+ import { create } from "./commands/create.ts";
5
+ import { deploy } from "./commands/deploy.ts";
6
+ import { destroy } from "./commands/destroy.ts";
7
+ import { dev } from "./commands/dev.ts";
8
+ import { login } from "./commands/login.ts";
9
+ import { run } from "./commands/run.ts";
5
10
  import { getPackageVersion } from "./services/get-package-version.ts";
6
- import {
7
- EditorSchema,
8
- PackageManagerSchema,
9
- ProjectNameSchema,
10
- TemplateSchema,
11
- type CreateInput,
12
- } from "./types.ts";
13
-
14
- const t = trpcServer.initTRPC.create();
11
+ import { t } from "./trpc.ts";
15
12
 
16
13
  const router = t.router({
17
- create: t.procedure
18
- .meta({
19
- description: "Create a new Alchemy project",
20
- })
21
- .input(
22
- z.tuple([
23
- ProjectNameSchema.optional(),
24
- z
25
- .object({
26
- template: TemplateSchema.optional(),
27
- yes: z
28
- .boolean()
29
- .optional()
30
- .default(false)
31
- .describe("Skip prompts and use defaults"),
32
- overwrite: z
33
- .boolean()
34
- .optional()
35
- .default(false)
36
- .describe("Overwrite existing directory"),
37
- install: z
38
- .boolean()
39
- .optional()
40
- .describe("Install dependencies after scaffolding"),
41
- pm: PackageManagerSchema.optional().describe(
42
- "Package manager to use (bun, npm, pnpm, yarn)",
43
- ),
44
- vibeRules: EditorSchema.optional().describe(
45
- "Setup vibe-rules for the specified editor (cursor, windsurf, vscode, zed, claude-code, gemini, codex, amp, clinerules, roo, unified)",
46
- ),
47
- })
48
- .optional()
49
- .default({}),
50
- ]),
51
- )
52
- .mutation(async ({ input }) => {
53
- const [name, options] = input;
54
- const isTest = process.env.NODE_ENV === "test";
55
- const combinedInput: CreateInput = {
56
- name,
57
- ...options,
58
- yes: isTest || options.yes,
59
- };
60
- await createAlchemy(combinedInput);
61
- }),
14
+ create,
15
+ login,
16
+ deploy,
17
+ destroy,
18
+ dev,
19
+ run,
62
20
  });
63
21
 
64
22
  export type AppRouter = typeof router;
@@ -13,122 +13,214 @@ import {
13
13
  import * as fs from "fs-extra";
14
14
  import { resolve } from "node:path";
15
15
  import pc from "picocolors";
16
-
16
+ import z from "zod";
17
+ import { detectPackageManager } from "../../src/util/detect-package-manager.ts";
17
18
  import { throwWithContext } from "../errors.ts";
18
- import {
19
- detectPackageManager,
20
- installDependencies,
21
- } from "../services/package-manager.ts";
19
+ import { initializeGitRepo, isGitInstalled } from "../services/git.ts";
20
+ import { addGitHubWorkflowToAlchemy } from "../services/github-workflow.ts";
21
+ import { installDependencies } from "../services/package-manager.ts";
22
22
  import { copyTemplate } from "../services/template-manager.ts";
23
23
  import { ensureVibeRulesPostinstall } from "../services/vibe-rules.ts";
24
+ import { t } from "../trpc.ts";
24
25
  import type {
25
26
  CreateInput,
26
27
  EditorType,
27
28
  ProjectContext,
28
29
  TemplateType,
29
30
  } from "../types.ts";
30
- import { ProjectNameSchema, TEMPLATE_DEFINITIONS } from "../types.ts";
31
+ import {
32
+ EditorSchema,
33
+ PackageManagerSchema,
34
+ ProjectNameSchema,
35
+ TEMPLATE_DEFINITIONS,
36
+ TemplateSchema,
37
+ } from "../types.ts";
31
38
 
32
39
  const isTest = process.env.NODE_ENV === "test";
33
40
 
41
+ export const create = t.procedure
42
+ .meta({
43
+ description: "Create a new Alchemy project",
44
+ negateBooleans: true,
45
+ })
46
+ .input(
47
+ z.tuple([
48
+ ProjectNameSchema.optional(),
49
+ z.object({
50
+ template: TemplateSchema.optional(),
51
+ yes: z.boolean().optional().describe("Skip prompts and use defaults"),
52
+ overwrite: z
53
+ .boolean()
54
+ .optional()
55
+ .describe("Overwrite existing directory"),
56
+ install: z
57
+ .boolean()
58
+ .optional()
59
+ .describe("Install dependencies after scaffolding"),
60
+ pm: PackageManagerSchema.optional().describe(
61
+ "Package manager to use (bun, npm, pnpm, yarn)",
62
+ ),
63
+ vibeRules: EditorSchema.optional().describe(
64
+ "Setup vibe-rules for the specified editor (cursor, windsurf, vscode, zed, claude-code, gemini, codex, amp, clinerules, roo, unified)",
65
+ ),
66
+ githubActions: z
67
+ .boolean()
68
+ .optional()
69
+ .describe("Setup GitHub Actions for PR previews"),
70
+ git: z.boolean().optional().describe("Initialise a git repository"),
71
+ }),
72
+ ]),
73
+ )
74
+ .mutation(async ({ input }) => {
75
+ const [name, options] = input;
76
+ const isTest = process.env.NODE_ENV === "test";
77
+ const combinedInput: CreateInput = {
78
+ name,
79
+ ...options,
80
+ yes: isTest || options.yes,
81
+ };
82
+ await createAlchemy(combinedInput);
83
+ });
84
+
85
+ async function createAlchemy(cliOptions: CreateInput): Promise<void> {
86
+ try {
87
+ intro(pc.cyan("🧪 Welcome to Alchemy!"));
88
+
89
+ const context = await createProjectContext(cliOptions);
90
+
91
+ await handleDirectoryOverwrite(context);
92
+ await initializeTemplate(context);
93
+ await setupVibeRules(context);
94
+ await setupGitHubActions(context);
95
+ await setupGit(context);
96
+
97
+ displayNextSteps(context);
98
+ displaySuccessMessage(context);
99
+ } catch (error) {
100
+ handleError(error);
101
+ }
102
+ }
103
+
34
104
  async function createProjectContext(
35
105
  cliOptions: CreateInput,
36
106
  ): Promise<ProjectContext> {
37
- const detectedPm = detectPackageManager();
107
+ const detectedPm = await detectPackageManager();
38
108
  const options = { yes: isTest, ...cliOptions };
39
109
 
40
- let name: string;
110
+ const name = await getProjectName(options);
111
+ const selectedTemplate = await getSelectedTemplate(options);
112
+ const templateDefinition = getTemplateDefinition(selectedTemplate);
113
+ const packageManager = options.pm || detectedPm;
114
+ const shouldInstall = await getInstallPreference(options, packageManager);
115
+
116
+ const path = resolve(process.cwd(), name);
117
+
118
+ return {
119
+ name,
120
+ path,
121
+ template: selectedTemplate,
122
+ packageManager,
123
+ isTest,
124
+ options: {
125
+ ...options,
126
+ install: shouldInstall,
127
+ },
128
+ };
129
+ }
130
+
131
+ async function getProjectName(options: CreateInput): Promise<string> {
41
132
  if (options.name) {
42
- const result = ProjectNameSchema.safeParse(options.name);
43
- if (!result.success) {
44
- throw new Error(
45
- `Invalid project name: ${result.error.errors[0]?.message}`,
46
- );
47
- }
48
- name = options.name;
49
- log.info(`Using project name: ${pc.yellow(name)}`);
50
- } else {
51
- const nameResult = await text({
52
- message: "What is your project name?",
53
- placeholder: "my-alchemy-app",
54
- validate: (value) => {
55
- const result = ProjectNameSchema.safeParse(value);
56
- return result.success ? undefined : result.error.errors[0]?.message;
57
- },
58
- });
133
+ return options.name;
134
+ }
59
135
 
60
- if (isCancel(nameResult)) {
61
- cancel(pc.red("Operation cancelled."));
62
- process.exit(0);
63
- }
136
+ if (options.yes) {
137
+ return "my-alchemy-app";
138
+ }
64
139
 
65
- name = nameResult;
140
+ const nameResult = await text({
141
+ message: "What is your project name?",
142
+ placeholder: "my-alchemy-app",
143
+ validate: (value) => {
144
+ const result = ProjectNameSchema.safeParse(value);
145
+ if (!result.success) {
146
+ return result.error.issues[0]?.message || "Invalid project name";
147
+ }
148
+ return undefined;
149
+ },
150
+ });
151
+
152
+ if (isCancel(nameResult)) {
153
+ cancel(pc.red("Operation cancelled."));
154
+ process.exit(0);
66
155
  }
67
156
 
68
- let selectedTemplate: TemplateType;
157
+ return nameResult;
158
+ }
159
+
160
+ async function getSelectedTemplate(
161
+ options: CreateInput,
162
+ ): Promise<TemplateType> {
69
163
  if (options.template) {
70
- selectedTemplate = options.template;
71
- log.info(`Using template: ${pc.yellow(selectedTemplate)}`);
72
- } else {
73
- const templateResult = await select({
74
- message: "Which template would you like to use?",
75
- options: TEMPLATE_DEFINITIONS.map((t) => ({
76
- label: t.description,
77
- value: t.name as TemplateType,
78
- })),
79
- });
164
+ return options.template;
165
+ }
80
166
 
81
- if (isCancel(templateResult)) {
82
- cancel(pc.red("Operation cancelled."));
83
- process.exit(0);
84
- }
167
+ if (options.yes) {
168
+ return "typescript";
169
+ }
170
+
171
+ const templateResult = await select({
172
+ message: "Which template would you like to use?",
173
+ options: TEMPLATE_DEFINITIONS.map((t) => ({
174
+ label: t.description,
175
+ value: t.name as TemplateType,
176
+ })),
177
+ });
85
178
 
86
- selectedTemplate = templateResult;
179
+ if (isCancel(templateResult)) {
180
+ cancel(pc.red("Operation cancelled."));
181
+ process.exit(0);
87
182
  }
88
183
 
184
+ return templateResult;
185
+ }
186
+
187
+ function getTemplateDefinition(selectedTemplate: TemplateType) {
89
188
  const templateDefinition = TEMPLATE_DEFINITIONS.find(
90
189
  (t) => t.name === selectedTemplate,
91
190
  );
191
+
92
192
  if (!templateDefinition) {
93
193
  throw new Error(
94
194
  `Template '${pc.yellow(selectedTemplate)}' not found. Available templates: ${TEMPLATE_DEFINITIONS.map((t) => pc.cyan(t.name)).join(", ")}`,
95
195
  );
96
196
  }
97
197
 
98
- const path = resolve(process.cwd(), name);
99
- let packageManager = options.pm || detectedPm;
198
+ return templateDefinition;
199
+ }
100
200
 
101
- let shouldInstall = true;
201
+ async function getInstallPreference(
202
+ options: CreateInput,
203
+ packageManager: string,
204
+ ): Promise<boolean> {
102
205
  if (options.install !== undefined) {
103
- shouldInstall = options.install;
104
- log.info(
105
- `Dependencies installation: ${pc.yellow(shouldInstall ? "enabled" : "disabled")}`,
106
- );
107
- } else if (!options.yes) {
108
- const installResult = await confirm({
109
- message: "Install dependencies?",
110
- initialValue: true,
111
- });
206
+ return options.install;
207
+ }
112
208
 
113
- if (isCancel(installResult)) {
114
- cancel(pc.red("Operation cancelled."));
115
- process.exit(0);
116
- }
209
+ if (options.yes) {
210
+ return true;
211
+ }
117
212
 
118
- shouldInstall = installResult;
213
+ const installResult = await confirm({
214
+ message: `Install dependencies? ${pc.cyan(packageManager)}`,
215
+ initialValue: true,
216
+ });
217
+
218
+ if (isCancel(installResult)) {
219
+ cancel(pc.red("Operation cancelled."));
220
+ process.exit(0);
119
221
  }
120
222
 
121
- return {
122
- name,
123
- path,
124
- template: selectedTemplate,
125
- packageManager,
126
- isTest,
127
- options: {
128
- ...options,
129
- install: shouldInstall,
130
- },
131
- };
223
+ return installResult;
132
224
  }
133
225
 
134
226
  async function handleDirectoryOverwrite(
@@ -138,34 +230,41 @@ async function handleDirectoryOverwrite(
138
230
  return;
139
231
  }
140
232
 
141
- let shouldOverwrite = false;
233
+ const shouldOverwrite = await getShouldOverwrite(context);
142
234
 
235
+ if (!shouldOverwrite) {
236
+ cancel(pc.red("Operation cancelled."));
237
+ process.exit(0);
238
+ }
239
+
240
+ await removeExistingDirectory(context);
241
+ }
242
+
243
+ async function getShouldOverwrite(context: ProjectContext): Promise<boolean> {
143
244
  if (context.options.overwrite) {
144
- shouldOverwrite = true;
145
245
  log.warn(
146
246
  `Directory ${pc.yellow(context.name)} already exists. Overwriting due to ${pc.cyan("--overwrite")} flag.`,
147
247
  );
148
- } else {
149
- const overwriteResult = await confirm({
150
- message: `Directory ${pc.yellow(context.name)} already exists. Overwrite?`,
151
- initialValue: false,
152
- });
153
-
154
- if (isCancel(overwriteResult)) {
155
- cancel(pc.red("Operation cancelled."));
156
- process.exit(0);
157
- }
158
-
159
- shouldOverwrite = overwriteResult;
248
+ return true;
160
249
  }
161
250
 
162
- if (!shouldOverwrite) {
251
+ const overwriteResult = await confirm({
252
+ message: `Directory ${pc.yellow(context.name)} already exists. Overwrite?`,
253
+ initialValue: false,
254
+ });
255
+
256
+ if (isCancel(overwriteResult)) {
163
257
  cancel(pc.red("Operation cancelled."));
164
258
  process.exit(0);
165
259
  }
166
260
 
261
+ return overwriteResult;
262
+ }
263
+
264
+ async function removeExistingDirectory(context: ProjectContext): Promise<void> {
167
265
  const s = spinner();
168
266
  s.start(`Removing existing directory: ${pc.yellow(context.path)}`);
267
+
169
268
  try {
170
269
  await fs.rm(context.path, { recursive: true, force: true });
171
270
  s.stop(`Directory ${pc.yellow(context.path)} removed.`);
@@ -179,6 +278,7 @@ async function initializeTemplate(context: ProjectContext): Promise<void> {
179
278
  const templateDefinition = TEMPLATE_DEFINITIONS.find(
180
279
  (t) => t.name === context.template,
181
280
  );
281
+
182
282
  if (!templateDefinition) {
183
283
  throw new Error(`Template definition not found for: ${context.template}`);
184
284
  }
@@ -194,6 +294,32 @@ async function initializeTemplate(context: ProjectContext): Promise<void> {
194
294
  }
195
295
 
196
296
  async function setupVibeRules(context: ProjectContext): Promise<void> {
297
+ const selectedEditor = await getSelectedEditor(context);
298
+
299
+ if (!selectedEditor) {
300
+ return;
301
+ }
302
+
303
+ const s = spinner();
304
+ s.start("Configuring vibe-rules...");
305
+
306
+ try {
307
+ await ensureVibeRulesPostinstall(context.path, selectedEditor);
308
+ await installDependencies(context, {
309
+ devDependencies: ["vibe-rules"],
310
+ });
311
+ // we need to install dependencies to trigger the postinstall script
312
+ await installDependencies(context);
313
+ s.stop("vibe-rules configured");
314
+ } catch (error) {
315
+ s.stop("Failed to configure vibe-rules");
316
+ throwWithContext(error, "Failed to configure vibe-rules");
317
+ }
318
+ }
319
+
320
+ async function getSelectedEditor(
321
+ context: ProjectContext,
322
+ ): Promise<EditorType | undefined> {
197
323
  let selectedEditor: EditorType | undefined = context.options.vibeRules;
198
324
 
199
325
  if (!selectedEditor && !context.isTest && !context.options.yes) {
@@ -203,7 +329,7 @@ async function setupVibeRules(context: ProjectContext): Promise<void> {
203
329
  });
204
330
 
205
331
  if (isCancel(setupResult) || !setupResult) {
206
- return;
332
+ return undefined;
207
333
  }
208
334
 
209
335
  const editorResult = await select({
@@ -224,63 +350,109 @@ async function setupVibeRules(context: ProjectContext): Promise<void> {
224
350
  });
225
351
 
226
352
  if (isCancel(editorResult)) {
227
- return;
353
+ return undefined;
228
354
  }
229
355
 
230
356
  selectedEditor = editorResult;
231
357
  }
232
358
 
233
- if (!selectedEditor) {
359
+ return selectedEditor;
360
+ }
361
+
362
+ async function setupGitHubActions(context: ProjectContext): Promise<void> {
363
+ const shouldSetup = await getShouldSetupGitHubActions(context);
364
+
365
+ if (!shouldSetup) {
234
366
  return;
235
367
  }
236
368
 
237
- const s = spinner();
238
- s.start("Configuring vibe-rules...");
239
-
240
369
  try {
241
- await ensureVibeRulesPostinstall(context.path, selectedEditor);
370
+ await addGitHubWorkflowToAlchemy(context);
371
+ } catch (error) {
372
+ throwWithContext(error, "GitHub workflow setup failed");
373
+ }
374
+ }
242
375
 
243
- await installDependencies(context, {
244
- devDependencies: ["vibe-rules"],
376
+ async function getShouldSetupGitHubActions(
377
+ context: ProjectContext,
378
+ ): Promise<boolean> {
379
+ let shouldSetupGitHub = context.options.githubActions;
380
+
381
+ if (
382
+ shouldSetupGitHub === undefined &&
383
+ !context.isTest &&
384
+ !context.options.yes
385
+ ) {
386
+ const setupResult = await confirm({
387
+ message: "Add GitHub Actions for PR previews?",
388
+ initialValue: true,
245
389
  });
246
390
 
247
- // we need to install dependencies to trigger the postinstall script
248
- await installDependencies(context);
391
+ if (isCancel(setupResult) || !setupResult) {
392
+ return false;
393
+ }
249
394
 
250
- s.stop("vibe-rules configured");
251
- } catch (error) {
252
- s.stop("Failed to configure vibe-rules");
253
- throwWithContext(error, "Failed to configure vibe-rules");
395
+ shouldSetupGitHub = true;
254
396
  }
397
+
398
+ return shouldSetupGitHub ?? false;
255
399
  }
256
400
 
257
- export async function createAlchemy(cliOptions: CreateInput): Promise<void> {
401
+ async function setupGit(context: ProjectContext): Promise<void> {
402
+ const gitAvailable = await isGitInstalled();
403
+
404
+ if (!gitAvailable) {
405
+ log.warn("Git is not installed. Skipping git initialisation.");
406
+ return;
407
+ }
408
+
409
+ const shouldInit = await getShouldInitGit(context);
410
+
411
+ if (!shouldInit) {
412
+ return;
413
+ }
414
+
415
+ const s = spinner();
416
+ s.start("Initialising git repository...");
417
+
258
418
  try {
259
- intro(pc.cyan("🧪 Welcome to Alchemy!"));
260
- log.info("Creating a new Alchemy project...");
419
+ await initializeGitRepo(context);
420
+ s.stop("Git repository initialised.");
421
+ } catch (error) {
422
+ s.stop(pc.red("Failed to initialise git repository"));
423
+ throwWithContext(error, "Git initialisation failed");
424
+ }
425
+ }
261
426
 
262
- const context = await createProjectContext(cliOptions);
427
+ async function getShouldInitGit(context: ProjectContext): Promise<boolean> {
428
+ let shouldInit = context.options.git;
263
429
 
264
- log.info(`Detected package manager: ${pc.green(context.packageManager)}`);
430
+ if (shouldInit === undefined && !context.isTest && !context.options.yes) {
431
+ const initResult = await confirm({
432
+ message: "Initialise a git repository?",
433
+ initialValue: true,
434
+ });
265
435
 
266
- await handleDirectoryOverwrite(context);
436
+ if (isCancel(initResult) || !initResult) {
437
+ return false;
438
+ }
439
+ shouldInit = initResult;
440
+ }
267
441
 
268
- await initializeTemplate(context);
442
+ return shouldInit ?? false;
443
+ }
269
444
 
270
- const installInstructions =
271
- context.options.install === false
272
- ? `
445
+ function displayNextSteps(context: ProjectContext): void {
446
+ const installInstructions =
447
+ context.options.install === false
448
+ ? `
273
449
  ${pc.cyan("📦 Install dependencies:")}
274
- cd ${context.name}
275
450
  ${context.packageManager} install
276
-
277
451
  `
278
- : "";
279
-
280
- await setupVibeRules(context);
452
+ : "";
281
453
 
282
- note(
283
- `
454
+ note(
455
+ `
284
456
  ${pc.cyan("📁 Navigate to your project:")}
285
457
  cd ${context.name}
286
458
 
@@ -293,22 +465,20 @@ ${pc.cyan("🧹 Destroy your project:")}
293
465
  ${pc.cyan("📚 Learn more:")}
294
466
  https://alchemy.run
295
467
  `,
296
- "Next Steps:",
297
- );
468
+ "Next Steps:",
469
+ );
470
+ }
298
471
 
299
- outro(
300
- pc.green(`✅ Project ${pc.yellow(context.name)} created successfully!`),
301
- );
302
- } catch (error) {
303
- log.error("An unexpected error occurred:");
304
- if (error instanceof Error) {
305
- log.error(`${pc.red("Error:")} ${error.message}`);
306
- if (error.stack && process.env.DEBUG) {
307
- log.error(`${pc.gray("Stack trace:")}\n${error.stack}`);
308
- }
309
- } else {
310
- log.error(pc.red(String(error)));
311
- }
312
- process.exit(1);
472
+ function displaySuccessMessage(context: ProjectContext): void {
473
+ outro(
474
+ pc.green(`✅ Project ${pc.yellow(context.name)} created successfully!`),
475
+ );
476
+ }
477
+
478
+ function handleError(error: unknown): void {
479
+ if (error instanceof Error) {
480
+ throwWithContext(error, "Project creation failed");
481
+ } else {
482
+ throwWithContext(new Error(String(error)), "Project creation failed");
313
483
  }
314
484
  }
@@ -0,0 +1,23 @@
1
+ import z from "zod";
2
+ import {
3
+ entrypoint,
4
+ execAlchemy,
5
+ execArgs,
6
+ watch,
7
+ } from "../services/execute-alchemy.ts";
8
+ import { t } from "../trpc.ts";
9
+
10
+ export const deploy = t.procedure
11
+ .meta({
12
+ description: "Deploy an Alchemy project",
13
+ })
14
+ .input(
15
+ z.tuple([
16
+ entrypoint,
17
+ z.object({
18
+ ...execArgs,
19
+ watch,
20
+ }),
21
+ ]),
22
+ )
23
+ .mutation(async ({ input }) => execAlchemy(...input));
@@ -0,0 +1,19 @@
1
+ import z from "zod";
2
+ import {
3
+ entrypoint,
4
+ execAlchemy,
5
+ execArgs,
6
+ } from "../services/execute-alchemy.ts";
7
+ import { t } from "../trpc.ts";
8
+
9
+ export const destroy = t.procedure
10
+ .meta({
11
+ description: "Deploy an Alchemy project",
12
+ })
13
+ .input(z.tuple([entrypoint, z.object(execArgs)]))
14
+ .mutation(async ({ input: [main, options] }) =>
15
+ execAlchemy(main, {
16
+ ...options,
17
+ destroy: true,
18
+ }),
19
+ );