alepha 0.18.2 → 0.18.3

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 (397) hide show
  1. package/assets/devtools-ui/200.html +2 -2
  2. package/assets/devtools-ui/200.html.br +0 -0
  3. package/assets/devtools-ui/404.html +2 -2
  4. package/assets/devtools-ui/404.html.br +0 -0
  5. package/assets/devtools-ui/{asset.BfSBZ5Dd.css → asset.hG_f8HuK.css} +1 -1
  6. package/assets/devtools-ui/asset.hG_f8HuK.css.br +0 -0
  7. package/assets/devtools-ui/chunk.B3au4Lhg.js +1 -0
  8. package/assets/devtools-ui/chunk.B3au4Lhg.js.br +0 -0
  9. package/assets/devtools-ui/chunk.BLOrlnMB.js +1 -0
  10. package/assets/devtools-ui/chunk.BLOrlnMB.js.br +0 -0
  11. package/assets/devtools-ui/chunk.BLR01ljW.js +1 -0
  12. package/assets/devtools-ui/chunk.BLR01ljW.js.br +0 -0
  13. package/assets/devtools-ui/chunk.BTXaIUlA.js +1 -0
  14. package/assets/devtools-ui/chunk.BTXaIUlA.js.br +0 -0
  15. package/assets/devtools-ui/{chunk.lJL-lgnW.js → chunk.BhJaxmm8.js} +1 -1
  16. package/assets/devtools-ui/chunk.BhJaxmm8.js.br +0 -0
  17. package/assets/devtools-ui/chunk.BtoNxFuL.js +1 -0
  18. package/assets/devtools-ui/chunk.BtoNxFuL.js.br +0 -0
  19. package/assets/devtools-ui/chunk.C8YUV2Wd.js +1 -0
  20. package/assets/devtools-ui/chunk.C8YUV2Wd.js.br +0 -0
  21. package/assets/devtools-ui/{chunk.M6wyKO_3.js → chunk.CBbIgDzE.js} +2 -2
  22. package/assets/devtools-ui/chunk.CBbIgDzE.js.br +0 -0
  23. package/assets/devtools-ui/chunk.CFqIniwA.js +1 -0
  24. package/assets/devtools-ui/chunk.CFqIniwA.js.br +0 -0
  25. package/assets/devtools-ui/chunk.CLFF7f7-.js +1 -0
  26. package/assets/devtools-ui/chunk.CLFF7f7-.js.br +0 -0
  27. package/assets/devtools-ui/chunk.CRsBbA10.js +1 -0
  28. package/assets/devtools-ui/chunk.CRsBbA10.js.br +0 -0
  29. package/assets/devtools-ui/{chunk.DbEH1oOB.js → chunk.CZPo6v95.js} +1 -1
  30. package/assets/devtools-ui/chunk.CZPo6v95.js.br +0 -0
  31. package/assets/devtools-ui/chunk.D0fWgNos.js +1 -0
  32. package/assets/devtools-ui/chunk.D0fWgNos.js.br +1 -0
  33. package/assets/devtools-ui/chunk.D7-0ziQ6.js +1 -0
  34. package/assets/devtools-ui/chunk.D7-0ziQ6.js.br +0 -0
  35. package/assets/devtools-ui/chunk.DAewe0vm.js +1 -0
  36. package/assets/devtools-ui/chunk.DAewe0vm.js.br +0 -0
  37. package/assets/devtools-ui/chunk.DJRQEYqK.js +1 -0
  38. package/assets/devtools-ui/chunk.DJRQEYqK.js.br +0 -0
  39. package/assets/devtools-ui/{chunk.CZl6J9DF.js → chunk.DMAxv14p.js} +1 -1
  40. package/assets/devtools-ui/chunk.DMAxv14p.js.br +0 -0
  41. package/assets/devtools-ui/{chunk.BT2IiBkZ.js → chunk.DMImnNjU.js} +1 -1
  42. package/assets/devtools-ui/chunk.DMImnNjU.js.br +0 -0
  43. package/assets/devtools-ui/chunk.DeeQsidk.js +9 -0
  44. package/assets/devtools-ui/chunk.DeeQsidk.js.br +0 -0
  45. package/assets/devtools-ui/chunk.DqEwn9Vj.js +7 -0
  46. package/assets/devtools-ui/chunk.DqEwn9Vj.js.br +0 -0
  47. package/assets/devtools-ui/chunk.Dt8OsQey.js +1 -0
  48. package/assets/devtools-ui/chunk.Dt8OsQey.js.br +0 -0
  49. package/assets/devtools-ui/{chunk.B9pX3zit.js → chunk.Dtp8oa_f.js} +1 -1
  50. package/assets/devtools-ui/chunk.Dtp8oa_f.js.br +0 -0
  51. package/assets/devtools-ui/chunk.Dx3JzAYM.js +1 -0
  52. package/assets/devtools-ui/chunk.Dx3JzAYM.js.br +0 -0
  53. package/assets/devtools-ui/chunk.GCOj1-5E.js +1 -0
  54. package/assets/devtools-ui/chunk.GCOj1-5E.js.br +0 -0
  55. package/assets/devtools-ui/chunk.IC1LD8BH.js +1 -0
  56. package/assets/devtools-ui/chunk.IC1LD8BH.js.br +0 -0
  57. package/assets/devtools-ui/chunk.IwuB_TqW.js +1 -0
  58. package/assets/devtools-ui/chunk.IwuB_TqW.js.br +0 -0
  59. package/assets/devtools-ui/chunk.Qqapj2zq.js +1 -0
  60. package/assets/devtools-ui/chunk.Qqapj2zq.js.br +0 -0
  61. package/assets/devtools-ui/{chunk.C79YouPp.js → chunk.TKKKndOy.js} +1 -1
  62. package/assets/devtools-ui/chunk.TKKKndOy.js.br +0 -0
  63. package/assets/devtools-ui/chunk.YHTVhFQT.js +1 -0
  64. package/assets/devtools-ui/chunk.YHTVhFQT.js.br +0 -0
  65. package/assets/devtools-ui/chunk.fnod6uEi.js +1 -0
  66. package/assets/devtools-ui/chunk.fnod6uEi.js.br +0 -0
  67. package/assets/devtools-ui/chunk.mOCRmXjo.js +1 -0
  68. package/assets/devtools-ui/chunk.mOCRmXjo.js.br +0 -0
  69. package/assets/devtools-ui/chunk.qZTNEAK0.js +1 -0
  70. package/assets/devtools-ui/chunk.qZTNEAK0.js.br +0 -0
  71. package/assets/devtools-ui/chunk.rc9m0y4-.js +1 -0
  72. package/assets/devtools-ui/chunk.rc9m0y4-.js.br +0 -0
  73. package/assets/devtools-ui/entry.Cxc5QLCU.js +80 -0
  74. package/assets/devtools-ui/entry.Cxc5QLCU.js.br +0 -0
  75. package/assets/devtools-ui/index.html +2 -2
  76. package/assets/devtools-ui/index.html.br +0 -0
  77. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  78. package/assets/swagger-ui/swagger-ui.css +1 -1
  79. package/dist/api/audits/index.d.ts +61 -5
  80. package/dist/api/audits/index.d.ts.map +1 -1
  81. package/dist/api/files/index.d.ts +61 -5
  82. package/dist/api/files/index.d.ts.map +1 -1
  83. package/dist/api/jobs/index.d.ts +61 -5
  84. package/dist/api/jobs/index.d.ts.map +1 -1
  85. package/dist/api/jobs/index.js +4 -2
  86. package/dist/api/jobs/index.js.map +1 -1
  87. package/dist/api/keys/index.d.ts +5 -5
  88. package/dist/api/notifications/index.browser.js +44 -1
  89. package/dist/api/notifications/index.browser.js.map +1 -1
  90. package/dist/api/notifications/index.d.ts +187 -2
  91. package/dist/api/notifications/index.d.ts.map +1 -1
  92. package/dist/api/notifications/index.js +143 -8
  93. package/dist/api/notifications/index.js.map +1 -1
  94. package/dist/api/parameters/index.d.ts +61 -5
  95. package/dist/api/parameters/index.d.ts.map +1 -1
  96. package/dist/api/users/index.d.ts +330 -93
  97. package/dist/api/users/index.d.ts.map +1 -1
  98. package/dist/api/users/index.js +27 -36
  99. package/dist/api/users/index.js.map +1 -1
  100. package/dist/cli/config/index.d.ts +46 -0
  101. package/dist/cli/config/index.d.ts.map +1 -0
  102. package/dist/cli/config/index.js +20 -0
  103. package/dist/cli/config/index.js.map +1 -0
  104. package/dist/cli/core/index.d.ts +69 -66
  105. package/dist/cli/core/index.d.ts.map +1 -1
  106. package/dist/cli/core/index.js +329 -196
  107. package/dist/cli/core/index.js.map +1 -1
  108. package/dist/cli/platform/index.d.ts +302 -63
  109. package/dist/cli/platform/index.d.ts.map +1 -1
  110. package/dist/cli/platform/index.js +455 -25
  111. package/dist/cli/platform/index.js.map +1 -1
  112. package/dist/core/index.browser.js +125 -87
  113. package/dist/core/index.browser.js.map +1 -1
  114. package/dist/core/index.d.ts +62 -53
  115. package/dist/core/index.d.ts.map +1 -1
  116. package/dist/core/index.js +125 -87
  117. package/dist/core/index.js.map +1 -1
  118. package/dist/core/index.native.js +125 -87
  119. package/dist/core/index.native.js.map +1 -1
  120. package/dist/core/index.workerd.js +125 -87
  121. package/dist/core/index.workerd.js.map +1 -1
  122. package/dist/crypto/index.d.ts +18 -1
  123. package/dist/crypto/index.d.ts.map +1 -1
  124. package/dist/crypto/index.js +29 -3
  125. package/dist/crypto/index.js.map +1 -1
  126. package/dist/devtools/index.js +3 -12
  127. package/dist/devtools/index.js.map +1 -1
  128. package/dist/logger/index.d.ts +10 -1
  129. package/dist/logger/index.d.ts.map +1 -1
  130. package/dist/logger/index.js +19 -9
  131. package/dist/logger/index.js.map +1 -1
  132. package/dist/orm/core/index.browser.js +57 -1
  133. package/dist/orm/core/index.browser.js.map +1 -1
  134. package/dist/orm/core/index.bun.js +378 -19
  135. package/dist/orm/core/index.bun.js.map +1 -1
  136. package/dist/orm/core/index.d.ts +328 -9
  137. package/dist/orm/core/index.d.ts.map +1 -1
  138. package/dist/orm/core/index.js +384 -21
  139. package/dist/orm/core/index.js.map +1 -1
  140. package/dist/orm/postgres/index.bun.js +49 -17
  141. package/dist/orm/postgres/index.bun.js.map +1 -1
  142. package/dist/orm/postgres/index.d.ts +47 -21
  143. package/dist/orm/postgres/index.d.ts.map +1 -1
  144. package/dist/orm/postgres/index.js +52 -17
  145. package/dist/orm/postgres/index.js.map +1 -1
  146. package/dist/react/core/index.d.ts +1 -1
  147. package/dist/react/core/index.d.ts.map +1 -1
  148. package/dist/react/core/index.js +6 -1
  149. package/dist/react/core/index.js.map +1 -1
  150. package/dist/react/form/index.d.ts +28 -18
  151. package/dist/react/form/index.d.ts.map +1 -1
  152. package/dist/react/form/index.js +92 -56
  153. package/dist/react/form/index.js.map +1 -1
  154. package/dist/react/router/index.browser.js +448 -116
  155. package/dist/react/router/index.browser.js.map +1 -1
  156. package/dist/react/router/index.d.ts +102 -40
  157. package/dist/react/router/index.d.ts.map +1 -1
  158. package/dist/react/router/index.js +453 -92
  159. package/dist/react/router/index.js.map +1 -1
  160. package/dist/security/index.d.ts +3 -11
  161. package/dist/security/index.d.ts.map +1 -1
  162. package/dist/security/index.js +6 -11
  163. package/dist/security/index.js.map +1 -1
  164. package/dist/server/auth/index.d.ts +22 -24
  165. package/dist/server/auth/index.d.ts.map +1 -1
  166. package/dist/server/auth/index.js +102 -82
  167. package/dist/server/auth/index.js.map +1 -1
  168. package/dist/server/cookies/index.d.ts +7 -4
  169. package/dist/server/cookies/index.d.ts.map +1 -1
  170. package/dist/server/cookies/index.js +13 -12
  171. package/dist/server/cookies/index.js.map +1 -1
  172. package/dist/server/core/index.d.ts +288 -4
  173. package/dist/server/core/index.d.ts.map +1 -1
  174. package/dist/server/core/index.js +375 -2
  175. package/dist/server/core/index.js.map +1 -1
  176. package/dist/server/links/index.browser.js +10 -71
  177. package/dist/server/links/index.browser.js.map +1 -1
  178. package/dist/server/links/index.d.ts +32 -49
  179. package/dist/server/links/index.d.ts.map +1 -1
  180. package/dist/server/links/index.js +73 -100
  181. package/dist/server/links/index.js.map +1 -1
  182. package/dist/system/index.browser.js +221 -2
  183. package/dist/system/index.browser.js.map +1 -1
  184. package/dist/system/index.d.ts +63 -1
  185. package/dist/system/index.d.ts.map +1 -1
  186. package/dist/system/index.js +221 -1
  187. package/dist/system/index.js.map +1 -1
  188. package/dist/system/index.workerd.js +224 -4
  189. package/dist/system/index.workerd.js.map +1 -1
  190. package/package.json +10 -5
  191. package/src/api/jobs/providers/JobProvider.ts +6 -3
  192. package/src/api/notifications/controllers/AdminNotificationController.ts +83 -0
  193. package/src/api/notifications/index.browser.ts +3 -0
  194. package/src/api/notifications/index.ts +14 -2
  195. package/src/api/notifications/jobs/NotificationJobs.ts +11 -2
  196. package/src/api/notifications/schemas/notificationDetailResourceSchema.ts +20 -0
  197. package/src/api/notifications/schemas/notificationQuerySchema.ts +19 -0
  198. package/src/api/notifications/schemas/notificationResourceSchema.ts +18 -0
  199. package/src/api/notifications/services/NotificationSenderService.ts +15 -2
  200. package/src/api/users/atoms/realmAuthSettingsAtom.ts +28 -32
  201. package/src/api/users/buckets/UserBuckets.ts +1 -1
  202. package/src/api/users/jobs/UserJobs.ts +1 -1
  203. package/src/api/users/primitives/$realm.ts +8 -49
  204. package/src/api/users/providers/RealmProvider.ts +2 -3
  205. package/src/api/users/services/RegistrationService.spec.ts +7 -7
  206. package/src/api/users/services/RegistrationService.ts +3 -3
  207. package/src/api/users/services/SessionService.spec.ts +4 -4
  208. package/src/api/users/services/SessionService.ts +3 -3
  209. package/src/cli/{core → config}/defineConfig.ts +14 -20
  210. package/src/cli/config/index.ts +1 -0
  211. package/src/cli/core/commands/db.ts +65 -1
  212. package/src/cli/core/commands/dev.ts +1 -0
  213. package/src/cli/core/commands/init.ts +2 -192
  214. package/src/cli/core/index.ts +34 -11
  215. package/src/cli/core/providers/ViteDevServerProvider.ts +52 -13
  216. package/src/cli/core/services/PackageManagerUtils.ts +43 -21
  217. package/src/cli/core/services/ProjectScaffolder.ts +214 -2
  218. package/src/cli/core/services/ViteUtils.ts +57 -0
  219. package/src/cli/core/tasks/BuildClientTask.ts +7 -2
  220. package/src/cli/core/tasks/BuildCloudflareTask.ts +4 -12
  221. package/src/cli/core/tasks/BuildServerTask.ts +2 -0
  222. package/src/cli/core/tasks/BuildVercelTask.ts +165 -168
  223. package/src/cli/core/templates/alephaConfigTs.ts +1 -1
  224. package/src/cli/core/templates/apiAppSecurityTs.ts +5 -8
  225. package/src/cli/core/templates/tsconfigJson.ts +6 -1
  226. package/src/cli/platform/adapters/CloudflareAdapter.spec.ts +1 -1
  227. package/src/cli/platform/adapters/CloudflareAdapter.ts +30 -29
  228. package/src/cli/platform/atoms/platformOptions.ts +21 -0
  229. package/src/cli/platform/commands/SecretsCommand.spec.ts +298 -0
  230. package/src/cli/platform/commands/SecretsCommand.ts +283 -0
  231. package/src/cli/platform/commands/platform.ts +12 -0
  232. package/src/cli/platform/index.ts +14 -28
  233. package/src/cli/platform/providers/GitHubSecretStore.spec.ts +153 -0
  234. package/src/cli/platform/providers/GitHubSecretStore.ts +112 -0
  235. package/src/cli/platform/providers/MemorySecretStore.ts +114 -0
  236. package/src/cli/platform/providers/SecretStoreProvider.ts +39 -0
  237. package/src/cli/platform/schemas/cloudflare.ts +2 -0
  238. package/src/cli/platform/services/CloudflareApi.ts +5 -2
  239. package/src/cli/platform/services/DockerComposeGenerator.spec.ts +115 -0
  240. package/src/cli/platform/services/DockerComposeGenerator.ts +46 -1
  241. package/src/cli/platform/services/SecretFilterService.spec.ts +111 -0
  242. package/src/cli/platform/services/SecretFilterService.ts +54 -0
  243. package/src/core/Alepha.ts +94 -25
  244. package/src/core/__tests__/Alepha-parseEnv.spec.ts +20 -0
  245. package/src/core/primitives/$memoize.ts +38 -26
  246. package/src/core/providers/AlsProvider.ts +2 -0
  247. package/src/core/providers/EventManager.ts +4 -0
  248. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +1 -4
  249. package/src/core/providers/KeylessJsonSchemaCodec.ts +19 -125
  250. package/src/core/providers/SchemaValidator.spec.ts +36 -0
  251. package/src/core/providers/SchemaValidator.ts +9 -0
  252. package/src/crypto/index.ts +6 -1
  253. package/src/crypto/providers/SecretProvider.ts +36 -0
  254. package/src/devtools/providers/DevToolsProvider.ts +3 -12
  255. package/src/logger/index.ts +33 -6
  256. package/src/logger/providers/PrettyFormatterProvider.ts +5 -3
  257. package/src/orm/__tests__/orm-next-tests.ts +492 -0
  258. package/src/orm/__tests__/orm-next.spec.ts +140 -0
  259. package/src/orm/core/constants/PG_SYMBOLS.ts +17 -0
  260. package/src/orm/core/index.bun.ts +3 -6
  261. package/src/orm/core/index.shared-server.ts +2 -0
  262. package/src/orm/core/index.shared.ts +2 -0
  263. package/src/orm/core/index.ts +5 -7
  264. package/src/orm/core/interfaces/AggregateQuery.ts +103 -0
  265. package/src/orm/core/interfaces/PgQueryWhere.ts +7 -0
  266. package/src/orm/core/primitives/$entity.ts +8 -0
  267. package/src/orm/core/primitives/$repository.ts +6 -3
  268. package/src/orm/core/primitives/$view.ts +88 -0
  269. package/src/orm/core/providers/DbCacheProvider.ts +66 -0
  270. package/src/orm/core/providers/DrizzleKitProvider.ts +42 -0
  271. package/src/orm/core/providers/drivers/BunSqliteProvider.ts +2 -3
  272. package/src/orm/core/providers/drivers/CloudflareD1Provider.ts +12 -0
  273. package/src/orm/core/providers/drivers/DatabaseProvider.ts +39 -0
  274. package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +2 -3
  275. package/src/orm/core/schemas/databaseEnvSchema.ts +31 -0
  276. package/src/orm/core/schemas/insertSchema.ts +13 -3
  277. package/src/orm/core/schemas/updateSchema.ts +14 -3
  278. package/src/orm/core/services/ModelBuilder.ts +26 -14
  279. package/src/orm/core/services/QueryManager.ts +13 -0
  280. package/src/orm/core/services/Repository.ts +307 -5
  281. package/src/orm/core/services/SqliteModelBuilder.ts +38 -0
  282. package/src/orm/postgres/index.bun.ts +4 -7
  283. package/src/orm/postgres/index.ts +4 -7
  284. package/src/orm/postgres/providers/BunPostgresProvider.ts +12 -2
  285. package/src/orm/postgres/providers/NodePostgresProvider.ts +7 -0
  286. package/src/orm/postgres/providers/PglitePostgresProvider.ts +10 -17
  287. package/src/orm/postgres/providers/PostgresProvider.ts +7 -36
  288. package/src/orm/postgres/schemas/postgresEnvSchema.ts +32 -0
  289. package/src/orm/postgres/services/PostgresModelBuilder.ts +40 -0
  290. package/src/react/core/components/ErrorBoundary.tsx +5 -2
  291. package/src/react/form/hooks/useFieldValue.ts +34 -0
  292. package/src/react/form/hooks/useForm.browser.spec.tsx +94 -9
  293. package/src/react/form/hooks/useForm.ts +14 -2
  294. package/src/react/form/hooks/useFormState.ts +10 -10
  295. package/src/react/form/hooks/useFormValues.ts +29 -0
  296. package/src/react/form/index.ts +3 -1
  297. package/src/react/form/services/FormModel.ts +53 -122
  298. package/src/react/router/components/ErrorViewer.tsx +333 -34
  299. package/src/react/router/components/NestedView.tsx +10 -3
  300. package/src/react/router/primitives/$page.browser.spec.tsx +34 -0
  301. package/src/react/router/primitives/$page.spec.tsx +20 -0
  302. package/src/react/router/primitives/$page.ts +24 -0
  303. package/src/react/router/providers/ReactBrowserRouterProvider.ts +14 -2
  304. package/src/react/router/providers/ReactPageProvider.ts +156 -73
  305. package/src/react/router/providers/ReactServerProvider.ts +40 -2
  306. package/src/react/router/providers/ReactServerTemplateProvider.ts +13 -1
  307. package/src/security/providers/SecurityProvider.ts +5 -27
  308. package/src/server/auth/primitives/$auth.ts +52 -19
  309. package/src/server/auth/providers/ServerAuthProvider.ts +145 -139
  310. package/src/server/cookies/providers/ServerCookiesProvider.ts +12 -24
  311. package/src/server/core/index.ts +3 -1
  312. package/src/server/core/primitives/$sse.spec.ts +315 -0
  313. package/src/server/core/primitives/$sse.ts +715 -0
  314. package/src/server/links/index.browser.ts +1 -3
  315. package/src/server/links/index.ts +0 -3
  316. package/src/server/links/providers/LinkProvider.spec.ts +12 -21
  317. package/src/server/links/providers/LinkProvider.ts +20 -52
  318. package/src/server/links/providers/ServerLinksProvider.spec.ts +106 -0
  319. package/src/server/links/providers/ServerLinksProvider.ts +113 -73
  320. package/src/server/links/schemas/apiLinksResponseSchema.ts +4 -21
  321. package/src/server/links/services/BatchCollector.ts +5 -3
  322. package/src/system/index.browser.ts +1 -0
  323. package/src/system/index.ts +3 -0
  324. package/src/system/index.workerd.ts +39 -1
  325. package/src/system/providers/WorkerdFileSystemProvider.ts +365 -0
  326. package/assets/devtools-ui/asset.BfSBZ5Dd.css.br +0 -0
  327. package/assets/devtools-ui/chunk.2NYaoqWt.js +0 -1
  328. package/assets/devtools-ui/chunk.2NYaoqWt.js.br +0 -0
  329. package/assets/devtools-ui/chunk.B052Z_xQ.js +0 -1
  330. package/assets/devtools-ui/chunk.B052Z_xQ.js.br +0 -0
  331. package/assets/devtools-ui/chunk.B4kVY90C.js +0 -1
  332. package/assets/devtools-ui/chunk.B4kVY90C.js.br +0 -0
  333. package/assets/devtools-ui/chunk.B7QJXctB.js +0 -1
  334. package/assets/devtools-ui/chunk.B7QJXctB.js.br +0 -0
  335. package/assets/devtools-ui/chunk.B9pX3zit.js.br +0 -0
  336. package/assets/devtools-ui/chunk.BKF9JxIo.js +0 -1
  337. package/assets/devtools-ui/chunk.BKF9JxIo.js.br +0 -0
  338. package/assets/devtools-ui/chunk.BOHgdTP-.js +0 -1
  339. package/assets/devtools-ui/chunk.BOHgdTP-.js.br +0 -0
  340. package/assets/devtools-ui/chunk.BOVFxkYC.js +0 -1
  341. package/assets/devtools-ui/chunk.BOVFxkYC.js.br +0 -0
  342. package/assets/devtools-ui/chunk.BR842zj5.js +0 -1
  343. package/assets/devtools-ui/chunk.BR842zj5.js.br +0 -0
  344. package/assets/devtools-ui/chunk.BT2IiBkZ.js.br +0 -0
  345. package/assets/devtools-ui/chunk.C79YouPp.js.br +0 -0
  346. package/assets/devtools-ui/chunk.C8mlBrjW.js +0 -9
  347. package/assets/devtools-ui/chunk.C8mlBrjW.js.br +0 -0
  348. package/assets/devtools-ui/chunk.CK0ow3AZ.js +0 -1
  349. package/assets/devtools-ui/chunk.CK0ow3AZ.js.br +0 -0
  350. package/assets/devtools-ui/chunk.CZl6J9DF.js.br +0 -0
  351. package/assets/devtools-ui/chunk.CdNr0YzS.js +0 -1
  352. package/assets/devtools-ui/chunk.CdNr0YzS.js.br +0 -0
  353. package/assets/devtools-ui/chunk.Ce6_6iIF.js +0 -1
  354. package/assets/devtools-ui/chunk.Ce6_6iIF.js.br +0 -0
  355. package/assets/devtools-ui/chunk.CpyDMr6O.js +0 -1
  356. package/assets/devtools-ui/chunk.CpyDMr6O.js.br +0 -0
  357. package/assets/devtools-ui/chunk.CyPmvPnY.js +0 -1
  358. package/assets/devtools-ui/chunk.CyPmvPnY.js.br +0 -0
  359. package/assets/devtools-ui/chunk.DTI_geWu.js +0 -1
  360. package/assets/devtools-ui/chunk.DTI_geWu.js.br +0 -0
  361. package/assets/devtools-ui/chunk.DbEH1oOB.js.br +0 -0
  362. package/assets/devtools-ui/chunk.Ddeqj5gv.js +0 -1
  363. package/assets/devtools-ui/chunk.Ddeqj5gv.js.br +0 -0
  364. package/assets/devtools-ui/chunk.DpRnB4vJ.js +0 -1
  365. package/assets/devtools-ui/chunk.DpRnB4vJ.js.br +0 -0
  366. package/assets/devtools-ui/chunk.DxPGTlsg.js +0 -1
  367. package/assets/devtools-ui/chunk.DxPGTlsg.js.br +0 -0
  368. package/assets/devtools-ui/chunk.G7_MMBJS.js +0 -1
  369. package/assets/devtools-ui/chunk.G7_MMBJS.js.br +0 -0
  370. package/assets/devtools-ui/chunk.M6wyKO_3.js.br +0 -0
  371. package/assets/devtools-ui/chunk.OUxNGmQ6.js +0 -1
  372. package/assets/devtools-ui/chunk.OUxNGmQ6.js.br +0 -0
  373. package/assets/devtools-ui/chunk.T1kle-fF.js +0 -1
  374. package/assets/devtools-ui/chunk.T1kle-fF.js.br +0 -0
  375. package/assets/devtools-ui/chunk.WjpsbQAv.js +0 -1
  376. package/assets/devtools-ui/chunk.WjpsbQAv.js.br +0 -0
  377. package/assets/devtools-ui/chunk.c6YgVx86.js +0 -1
  378. package/assets/devtools-ui/chunk.c6YgVx86.js.br +0 -0
  379. package/assets/devtools-ui/chunk.dwU3E_MU.js +0 -1
  380. package/assets/devtools-ui/chunk.dwU3E_MU.js.br +0 -0
  381. package/assets/devtools-ui/chunk.lJL-lgnW.js.br +0 -0
  382. package/assets/devtools-ui/chunk.lPWRmvA-.js +0 -7
  383. package/assets/devtools-ui/chunk.lPWRmvA-.js.br +0 -0
  384. package/assets/devtools-ui/chunk.p3HJvugM.js +0 -1
  385. package/assets/devtools-ui/chunk.p3HJvugM.js.br +0 -0
  386. package/assets/devtools-ui/chunk.r_Xoa_CI.js +0 -1
  387. package/assets/devtools-ui/chunk.r_Xoa_CI.js.br +0 -0
  388. package/assets/devtools-ui/chunk.sRNuTYXb.js +0 -1
  389. package/assets/devtools-ui/chunk.sRNuTYXb.js.br +0 -0
  390. package/assets/devtools-ui/chunk.tUjcyX5C.js +0 -1
  391. package/assets/devtools-ui/chunk.tUjcyX5C.js.br +0 -0
  392. package/assets/devtools-ui/chunk.thjBxvCA.js +0 -1
  393. package/assets/devtools-ui/chunk.thjBxvCA.js.br +0 -0
  394. package/assets/devtools-ui/entry.GYhBVRpC.js +0 -78
  395. package/assets/devtools-ui/entry.GYhBVRpC.js.br +0 -0
  396. package/src/server/links/services/DefinitionsPool.spec.ts +0 -86
  397. package/src/server/links/services/DefinitionsPool.ts +0 -43
@@ -1,17 +1,12 @@
1
1
  import { $module } from "alepha";
2
- import {
3
- AlephaCli,
4
- type AlephaCliConfig,
5
- registerConfigProcessor,
6
- } from "alepha/cli";
2
+ import { AlephaCli } from "alepha/cli";
7
3
  import { CloudflareAdapter } from "./adapters/CloudflareAdapter.ts";
8
4
  import { DockerAdapter } from "./adapters/DockerAdapter.ts";
9
5
  import { VercelAdapter } from "./adapters/VercelAdapter.ts";
10
- import {
11
- type PlatformOptions,
12
- platformOptions,
13
- } from "./atoms/platformOptions.ts";
14
6
  import { PlatformCommand } from "./commands/platform.ts";
7
+ import { SecretsCommand } from "./commands/SecretsCommand.ts";
8
+ import { GitHubSecretStore } from "./providers/GitHubSecretStore.ts";
9
+ import { MemorySecretStore } from "./providers/MemorySecretStore.ts";
15
10
  import { PlatformCacheProvider } from "./providers/PlatformCacheProvider.ts";
16
11
  import { CloudflareApi } from "./services/CloudflareApi.ts";
17
12
  import { DockerComposeGenerator } from "./services/DockerComposeGenerator.ts";
@@ -19,29 +14,11 @@ import { DockerSshService } from "./services/DockerSshService.ts";
19
14
  import { NamingService } from "./services/NamingService.ts";
20
15
  import { PlatformInspector } from "./services/PlatformInspector.ts";
21
16
  import { PlatformOrchestrator } from "./services/PlatformOrchestrator.ts";
17
+ import { SecretFilterService } from "./services/SecretFilterService.ts";
22
18
  import { VercelApi } from "./services/VercelApi.ts";
23
19
  import { VercelCli } from "./services/VercelCli.ts";
24
20
  import { WranglerApi } from "./services/WranglerApi.ts";
25
21
 
26
- // ---------------------------------------------------------------------------
27
- // Module augmentation — extends AlephaCliConfig with platform options
28
- // ---------------------------------------------------------------------------
29
-
30
- declare module "alepha/cli" {
31
- interface AlephaCliConfig {
32
- /**
33
- * Platform deployment configuration.
34
- */
35
- platform?: PlatformOptions;
36
- }
37
- }
38
-
39
- registerConfigProcessor((alepha: any, config: AlephaCliConfig) => {
40
- if (config.platform) {
41
- alepha.set(platformOptions, config.platform);
42
- }
43
- });
44
-
45
22
  // ---------------------------------------------------------------------------
46
23
 
47
24
  export const AlephaCliPlatform = $module({
@@ -49,6 +26,7 @@ export const AlephaCliPlatform = $module({
49
26
  services: [
50
27
  AlephaCli,
51
28
  PlatformCommand,
29
+ SecretsCommand,
52
30
  CloudflareAdapter,
53
31
  CloudflareApi,
54
32
  DockerAdapter,
@@ -59,7 +37,10 @@ export const AlephaCliPlatform = $module({
59
37
  VercelCli,
60
38
  WranglerApi,
61
39
  PlatformCacheProvider,
40
+ GitHubSecretStore,
41
+ MemorySecretStore,
62
42
  NamingService,
43
+ SecretFilterService,
63
44
  PlatformInspector,
64
45
  PlatformOrchestrator,
65
46
  ],
@@ -73,7 +54,11 @@ export * from "./adapters/PlatformAdapter.ts";
73
54
  export * from "./adapters/VercelAdapter.ts";
74
55
  export * from "./atoms/platformOptions.ts";
75
56
  export * from "./commands/platform.ts";
57
+ export * from "./commands/SecretsCommand.ts";
58
+ export * from "./providers/GitHubSecretStore.ts";
59
+ export * from "./providers/MemorySecretStore.ts";
76
60
  export * from "./providers/PlatformCacheProvider.ts";
61
+ export * from "./providers/SecretStoreProvider.ts";
77
62
  export * from "./schemas/cloudflare.ts";
78
63
  export * from "./schemas/platform.ts";
79
64
  export * from "./schemas/vercel.ts";
@@ -83,6 +68,7 @@ export * from "./services/DockerSshService.ts";
83
68
  export * from "./services/NamingService.ts";
84
69
  export * from "./services/PlatformInspector.ts";
85
70
  export * from "./services/PlatformOrchestrator.ts";
71
+ export * from "./services/SecretFilterService.ts";
86
72
  export * from "./services/VercelApi.ts";
87
73
  export * from "./services/VercelCli.ts";
88
74
  export * from "./services/WranglerApi.ts";
@@ -0,0 +1,153 @@
1
+ import { Alepha } from "alepha";
2
+ import {
3
+ FileSystemProvider,
4
+ MemoryFileSystemProvider,
5
+ MemoryShellProvider,
6
+ ShellProvider,
7
+ } from "alepha/system";
8
+ import { describe, test } from "vitest";
9
+ import { GitHubSecretStore } from "./GitHubSecretStore.ts";
10
+
11
+ describe("GitHubSecretStore", () => {
12
+ const createTestEnv = () => {
13
+ const alepha = Alepha.create()
14
+ .with({
15
+ provide: ShellProvider,
16
+ use: MemoryShellProvider,
17
+ })
18
+ .with({
19
+ provide: FileSystemProvider,
20
+ use: MemoryFileSystemProvider,
21
+ });
22
+
23
+ const shell = alepha.inject(MemoryShellProvider);
24
+ const fs = alepha.inject(MemoryFileSystemProvider);
25
+ const store = alepha.inject(GitHubSecretStore);
26
+
27
+ return { alepha, shell, fs, store };
28
+ };
29
+
30
+ describe("ensureAvailable", () => {
31
+ test("succeeds when gh is installed and authenticated", async ({
32
+ expect,
33
+ }) => {
34
+ const { shell, store } = createTestEnv();
35
+ shell.installedCommands.add("gh");
36
+ shell.outputs.set("gh auth status", "Logged in");
37
+
38
+ await expect(store.ensureAvailable()).resolves.not.toThrow();
39
+ });
40
+
41
+ test("throws when gh is not installed", async ({ expect }) => {
42
+ const { store } = createTestEnv();
43
+
44
+ await expect(store.ensureAvailable()).rejects.toThrow(
45
+ "GitHub CLI (gh) is not installed",
46
+ );
47
+ });
48
+
49
+ test("throws when gh is not authenticated", async ({ expect }) => {
50
+ const { shell, store } = createTestEnv();
51
+ shell.installedCommands.add("gh");
52
+ shell.errors.set("gh auth status", "Not logged in");
53
+
54
+ await expect(store.ensureAvailable()).rejects.toThrow(
55
+ "GitHub CLI is not authenticated",
56
+ );
57
+ });
58
+ });
59
+
60
+ describe("list", () => {
61
+ test("parses JSON output from gh", async ({ expect }) => {
62
+ const { shell, store } = createTestEnv();
63
+ shell.outputs.set(
64
+ "gh secret list --env app-production --json name,updatedAt",
65
+ JSON.stringify([
66
+ { name: "API_KEY", updatedAt: "2026-01-01T00:00:00Z" },
67
+ { name: "DATABASE_URL", updatedAt: "2026-01-02T00:00:00Z" },
68
+ ]),
69
+ );
70
+
71
+ const result = await store.list("app-production");
72
+
73
+ expect(result).toEqual([
74
+ { name: "API_KEY", updatedAt: "2026-01-01T00:00:00Z" },
75
+ { name: "DATABASE_URL", updatedAt: "2026-01-02T00:00:00Z" },
76
+ ]);
77
+ });
78
+
79
+ test("returns empty array when no secrets", async ({ expect }) => {
80
+ const { shell, store } = createTestEnv();
81
+ shell.outputs.set(
82
+ "gh secret list --env app-staging --json name,updatedAt",
83
+ "[]",
84
+ );
85
+
86
+ const result = await store.list("app-staging");
87
+ expect(result).toEqual([]);
88
+ });
89
+
90
+ test("returns empty array on error", async ({ expect }) => {
91
+ const { shell, store } = createTestEnv();
92
+ shell.errors.set(
93
+ "gh secret list --env bad-env --json name,updatedAt",
94
+ "Environment not found",
95
+ );
96
+
97
+ const result = await store.list("bad-env");
98
+ expect(result).toEqual([]);
99
+ });
100
+ });
101
+
102
+ describe("set", () => {
103
+ test("writes dotenv file and calls gh secret set --env-file", async ({
104
+ expect,
105
+ }) => {
106
+ const { shell, fs, store } = createTestEnv();
107
+
108
+ await store.set("app-production", "API_KEY", "abc123");
109
+
110
+ // Should write dotenv format
111
+ const written = fs.writeFileCalls.find((c) =>
112
+ /\/tmp\/alepha-secret-API_KEY-/.test(c.path),
113
+ );
114
+ expect(written).toBeDefined();
115
+ expect(written!.data).toBe('API_KEY="abc123"\n');
116
+
117
+ // Should call gh with --env-file (no positional secret name)
118
+ expect(
119
+ shell.wasCalledMatching(
120
+ /gh secret set -f \/tmp\/alepha-secret-API_KEY-\d+ --env app-production/,
121
+ ),
122
+ ).toBe(true);
123
+
124
+ // Temp file should be cleaned up
125
+ expect(
126
+ fs.rmCalls.some((c) => /\/tmp\/alepha-secret-API_KEY-/.test(c.path)),
127
+ ).toBe(true);
128
+ });
129
+
130
+ test("escapes special characters in value", async ({ expect }) => {
131
+ const { fs, store } = createTestEnv();
132
+
133
+ await store.set("app-production", "KEY", 'val"ue\nwith\\special');
134
+
135
+ const written = fs.writeFileCalls.find((c) =>
136
+ /\/tmp\/alepha-secret-KEY-/.test(c.path),
137
+ );
138
+ expect(written!.data).toBe('KEY="val\\"ue\\nwith\\\\special"\n');
139
+ });
140
+ });
141
+
142
+ describe("delete", () => {
143
+ test("calls gh secret delete with correct args", async ({ expect }) => {
144
+ const { shell, store } = createTestEnv();
145
+
146
+ await store.delete("app-production", "OLD_KEY");
147
+
148
+ expect(
149
+ shell.wasCalled("gh secret delete OLD_KEY --env app-production"),
150
+ ).toBe(true);
151
+ });
152
+ });
153
+ });
@@ -0,0 +1,112 @@
1
+ import { $inject, AlephaError } from "alepha";
2
+ import { $logger } from "alepha/logger";
3
+ import { FileSystemProvider, ShellProvider } from "alepha/system";
4
+ import type {
5
+ RemoteSecret,
6
+ SecretStoreProvider,
7
+ } from "./SecretStoreProvider.ts";
8
+
9
+ /**
10
+ * GitHub Actions secret store backed by the `gh` CLI.
11
+ *
12
+ * Requires the GitHub CLI (`gh`) to be installed and authenticated.
13
+ * Pushes secrets into GitHub Actions environments.
14
+ */
15
+ export class GitHubSecretStore implements SecretStoreProvider {
16
+ protected readonly log = $logger();
17
+ protected readonly shell = $inject(ShellProvider);
18
+ protected readonly fs = $inject(FileSystemProvider);
19
+
20
+ /**
21
+ * Verify that `gh` is installed and authenticated.
22
+ */
23
+ public async ensureAvailable(): Promise<void> {
24
+ const installed = await this.shell.isInstalled("gh");
25
+ if (!installed) {
26
+ throw new AlephaError(
27
+ "GitHub CLI (gh) is not installed. Install it from https://cli.github.com",
28
+ );
29
+ }
30
+
31
+ try {
32
+ await this.shell.run("gh auth status", { capture: true });
33
+ } catch {
34
+ throw new AlephaError(
35
+ "GitHub CLI is not authenticated. Run `gh auth login` first.",
36
+ );
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Create the GitHub Actions environment if it doesn't exist.
42
+ */
43
+ public async ensureEnvironment(environment: string): Promise<void> {
44
+ await this.shell.run(
45
+ `gh api --method PUT /repos/{owner}/{repo}/environments/${environment} --silent`,
46
+ { capture: true },
47
+ );
48
+ this.log.debug(`Ensured environment "${environment}" exists`);
49
+ }
50
+
51
+ /**
52
+ * List all secrets in a GitHub Actions environment.
53
+ */
54
+ public async list(environment: string): Promise<RemoteSecret[]> {
55
+ try {
56
+ const output = await this.shell.run(
57
+ `gh secret list --env ${environment} --json name,updatedAt`,
58
+ { capture: true },
59
+ );
60
+
61
+ const parsed = JSON.parse(output || "[]") as Array<{
62
+ name: string;
63
+ updatedAt?: string;
64
+ }>;
65
+
66
+ return parsed.map((s) => ({
67
+ name: s.name,
68
+ updatedAt: s.updatedAt,
69
+ }));
70
+ } catch (error) {
71
+ this.log.debug("Failed to list secrets", { environment, error });
72
+ return [];
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Set a secret in a GitHub Actions environment.
78
+ *
79
+ * Writes a dotenv-formatted file and uses `gh secret set --env-file` to
80
+ * avoid shell pipe issues with NodeShellProvider escaping the `|` character.
81
+ */
82
+ public async set(
83
+ environment: string,
84
+ key: string,
85
+ value: string,
86
+ ): Promise<void> {
87
+ const tmpFile = `/tmp/alepha-secret-${key}-${Date.now()}`;
88
+ const escaped = value
89
+ .replace(/\\/g, "\\\\")
90
+ .replace(/"/g, '\\"')
91
+ .replace(/\n/g, "\\n");
92
+ await this.fs.writeFile(tmpFile, `${key}="${escaped}"\n`);
93
+ try {
94
+ const output = await this.shell.run(
95
+ `gh secret set -f ${tmpFile} --env ${environment}`,
96
+ { capture: true },
97
+ );
98
+ this.log.debug(`Secret set: ${key}`, { output });
99
+ } finally {
100
+ await this.fs.rm(tmpFile);
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Delete a secret from a GitHub Actions environment.
106
+ */
107
+ public async delete(environment: string, key: string): Promise<void> {
108
+ await this.shell.run(`gh secret delete ${key} --env ${environment}`, {
109
+ capture: true,
110
+ });
111
+ }
112
+ }
@@ -0,0 +1,114 @@
1
+ import type {
2
+ RemoteSecret,
3
+ SecretStoreProvider,
4
+ } from "./SecretStoreProvider.ts";
5
+
6
+ export interface MemorySecretStoreCall {
7
+ method: "ensureAvailable" | "ensureEnvironment" | "list" | "set" | "delete";
8
+ environment?: string;
9
+ key?: string;
10
+ value?: string;
11
+ }
12
+
13
+ /**
14
+ * In-memory implementation of SecretStoreProvider for testing.
15
+ * Records all operations and stores secrets in a nested Map.
16
+ */
17
+ export class MemorySecretStore implements SecretStoreProvider {
18
+ /**
19
+ * Secrets keyed by environment, then by key.
20
+ */
21
+ public secrets = new Map<string, Map<string, string>>();
22
+
23
+ /**
24
+ * All recorded operations.
25
+ */
26
+ public calls: MemorySecretStoreCall[] = [];
27
+
28
+ /**
29
+ * When set, ensureAvailable() will throw with this message.
30
+ */
31
+ public availableError: string | null = null;
32
+
33
+ public async ensureAvailable(): Promise<void> {
34
+ this.calls.push({ method: "ensureAvailable" });
35
+ if (this.availableError) {
36
+ throw new Error(this.availableError);
37
+ }
38
+ }
39
+
40
+ public async ensureEnvironment(environment: string): Promise<void> {
41
+ this.calls.push({ method: "ensureEnvironment", environment });
42
+ if (!this.secrets.has(environment)) {
43
+ this.secrets.set(environment, new Map());
44
+ }
45
+ }
46
+
47
+ public async list(environment: string): Promise<RemoteSecret[]> {
48
+ this.calls.push({ method: "list", environment });
49
+ const envSecrets = this.secrets.get(environment);
50
+ if (!envSecrets) return [];
51
+
52
+ return Array.from(envSecrets.keys()).map((name) => ({ name }));
53
+ }
54
+
55
+ public async set(
56
+ environment: string,
57
+ key: string,
58
+ value: string,
59
+ ): Promise<void> {
60
+ this.calls.push({ method: "set", environment, key, value });
61
+
62
+ let envSecrets = this.secrets.get(environment);
63
+ if (!envSecrets) {
64
+ envSecrets = new Map();
65
+ this.secrets.set(environment, envSecrets);
66
+ }
67
+ envSecrets.set(key, value);
68
+ }
69
+
70
+ public async delete(environment: string, key: string): Promise<void> {
71
+ this.calls.push({ method: "delete", environment, key });
72
+ this.secrets.get(environment)?.delete(key);
73
+ }
74
+
75
+ /**
76
+ * Check if set() was called for a given environment and key.
77
+ */
78
+ public wasSet(environment: string, key: string): boolean {
79
+ return this.calls.some(
80
+ (c) =>
81
+ c.method === "set" && c.environment === environment && c.key === key,
82
+ );
83
+ }
84
+
85
+ /**
86
+ * Check if delete() was called for a given environment and key.
87
+ */
88
+ public wasDeleted(environment: string, key: string): boolean {
89
+ return this.calls.some(
90
+ (c) =>
91
+ c.method === "delete" && c.environment === environment && c.key === key,
92
+ );
93
+ }
94
+
95
+ /**
96
+ * Get all set() calls for a given environment.
97
+ */
98
+ public getSetCalls(
99
+ environment: string,
100
+ ): Array<{ key: string; value: string }> {
101
+ return this.calls
102
+ .filter((c) => c.method === "set" && c.environment === environment)
103
+ .map((c) => ({ key: c.key!, value: c.value! }));
104
+ }
105
+
106
+ /**
107
+ * Reset all state.
108
+ */
109
+ public reset(): void {
110
+ this.secrets.clear();
111
+ this.calls = [];
112
+ this.availableError = null;
113
+ }
114
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * A secret stored in a remote secret store.
3
+ */
4
+ export interface RemoteSecret {
5
+ name: string;
6
+ updatedAt?: string;
7
+ }
8
+
9
+ /**
10
+ * Abstract provider for managing secrets in an external store.
11
+ *
12
+ * Implementations: GitHubSecretStore, MemorySecretStore
13
+ */
14
+ export abstract class SecretStoreProvider {
15
+ /**
16
+ * Verify the backing store is reachable and authenticated.
17
+ */
18
+ abstract ensureAvailable(): Promise<void>;
19
+
20
+ /**
21
+ * Ensure the target environment exists in the store, creating it if needed.
22
+ */
23
+ abstract ensureEnvironment(environment: string): Promise<void>;
24
+
25
+ /**
26
+ * List all secrets in a given environment.
27
+ */
28
+ abstract list(environment: string): Promise<RemoteSecret[]>;
29
+
30
+ /**
31
+ * Set (create or update) a secret in a given environment.
32
+ */
33
+ abstract set(environment: string, key: string, value: string): Promise<void>;
34
+
35
+ /**
36
+ * Delete a secret from a given environment.
37
+ */
38
+ abstract delete(environment: string, key: string): Promise<void>;
39
+ }
@@ -153,6 +153,8 @@ export type CloudflareSecret = Static<typeof cloudflareSecretSchema>;
153
153
 
154
154
  export const createD1BodySchema = t.object({
155
155
  name: t.string(),
156
+ primary_location_hint: t.optional(t.string()),
157
+ jurisdiction: t.optional(t.string()),
156
158
  });
157
159
 
158
160
  export const createKVBodySchema = t.object({
@@ -118,13 +118,16 @@ export class CloudflareApi {
118
118
  );
119
119
  }
120
120
 
121
- public async createD1(name: string): Promise<CloudflareD1> {
121
+ public async createD1(
122
+ name: string,
123
+ location = "weur", // TODO: move to config (or auto-resolve based on account info, or ask ?)
124
+ ): Promise<CloudflareD1> {
122
125
  const accountId = await this.resolveAccountId();
123
126
  return await this.fetch<CloudflareD1>(
124
127
  `/accounts/${accountId}/d1/database`,
125
128
  {
126
129
  method: "POST",
127
- body: { name },
130
+ body: { name, primary_location_hint: location },
128
131
  bodySchema: createD1BodySchema,
129
132
  schema: cloudflareD1Schema,
130
133
  },
@@ -154,6 +154,62 @@ describe("DockerComposeGenerator", () => {
154
154
  expect(result).toBeNull();
155
155
  });
156
156
 
157
+ test("generates rustfs service when app has bucket and no S3_ENDPOINT", ({
158
+ expect,
159
+ }) => {
160
+ const { generator, naming } = createTestEnv();
161
+ const namingCtx = naming.forContext("myapp", "local");
162
+
163
+ const result = generator.generateLocal({
164
+ project: "myapp",
165
+ env: "local",
166
+ naming: namingCtx,
167
+ apps: [
168
+ makeApp({
169
+ resources: {
170
+ hasDatabase: false,
171
+ hasBucket: true,
172
+ hasKV: false,
173
+ hasQueue: false,
174
+ hasCron: false,
175
+ },
176
+ }),
177
+ ],
178
+ envVars: {},
179
+ });
180
+
181
+ expect(result).toContain("rustfs:");
182
+ expect(result).toContain("image: rustfs/rustfs:latest");
183
+ expect(result).toContain("9000:9000");
184
+ expect(result).toContain("RUSTFS_ROOT_USER: alepha");
185
+ expect(result).toContain("rustfs_data:");
186
+ });
187
+
188
+ test("skips rustfs when S3_ENDPOINT is present", ({ expect }) => {
189
+ const { generator, naming } = createTestEnv();
190
+ const namingCtx = naming.forContext("myapp", "local");
191
+
192
+ const result = generator.generateLocal({
193
+ project: "myapp",
194
+ env: "local",
195
+ naming: namingCtx,
196
+ apps: [
197
+ makeApp({
198
+ resources: {
199
+ hasDatabase: false,
200
+ hasBucket: true,
201
+ hasKV: false,
202
+ hasQueue: false,
203
+ hasCron: false,
204
+ },
205
+ }),
206
+ ],
207
+ envVars: { S3_ENDPOINT: "https://xxx.r2.cloudflarestorage.com" },
208
+ });
209
+
210
+ expect(result).toBeNull();
211
+ });
212
+
157
213
  test("returns null when no services needed", ({ expect }) => {
158
214
  const { generator, naming } = createTestEnv();
159
215
  const namingCtx = naming.forContext("myapp", "local");
@@ -312,6 +368,65 @@ describe("DockerComposeGenerator", () => {
312
368
  expect(result).toContain("Host(`myshop.com`)");
313
369
  });
314
370
 
371
+ test("generates rustfs service when app has bucket", ({ expect }) => {
372
+ const { generator, naming } = createTestEnv();
373
+ const namingCtx = naming.forContext("myapp", "production");
374
+
375
+ const result = generator.generateRemote({
376
+ project: "myapp",
377
+ env: "production",
378
+ naming: namingCtx,
379
+ domain: "myapp.com",
380
+ apps: [
381
+ makeApp({
382
+ resources: {
383
+ hasDatabase: false,
384
+ hasBucket: true,
385
+ hasKV: false,
386
+ hasQueue: false,
387
+ hasCron: false,
388
+ },
389
+ }),
390
+ ],
391
+ envVars: {},
392
+ });
393
+
394
+ expect(result).toContain("rustfs:");
395
+ expect(result).toContain("image: rustfs/rustfs:latest");
396
+ expect(result).toContain("RUSTFS_ROOT_PASSWORD: ${S3_SECRET_KEY}");
397
+ expect(result).toContain("rustfs_data:");
398
+ expect(result).toContain("depends_on:");
399
+ expect(result).toContain("- rustfs");
400
+ // RustFS on internal network when domain is set
401
+ expect(result).toContain("internal");
402
+ });
403
+
404
+ test("skips rustfs when S3_ENDPOINT present", ({ expect }) => {
405
+ const { generator, naming } = createTestEnv();
406
+ const namingCtx = naming.forContext("myapp", "production");
407
+
408
+ const result = generator.generateRemote({
409
+ project: "myapp",
410
+ env: "production",
411
+ naming: namingCtx,
412
+ domain: "myapp.com",
413
+ apps: [
414
+ makeApp({
415
+ resources: {
416
+ hasDatabase: false,
417
+ hasBucket: true,
418
+ hasKV: false,
419
+ hasQueue: false,
420
+ hasCron: false,
421
+ },
422
+ }),
423
+ ],
424
+ envVars: { S3_ENDPOINT: "https://xxx.r2.cloudflarestorage.com" },
425
+ });
426
+
427
+ expect(result).not.toContain("rustfs:");
428
+ });
429
+
315
430
  test("multi-app without domain exposes incremental ports", ({ expect }) => {
316
431
  const { generator, naming } = createTestEnv();
317
432
  const namingCtx = naming.forContext("myproject", "staging");