alepha 0.21.2 → 0.23.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 (519) hide show
  1. package/README.md +0 -1
  2. package/dist/api/audits/index.browser.js.map +1 -1
  3. package/dist/api/audits/index.d.ts +393 -403
  4. package/dist/api/audits/index.d.ts.map +1 -1
  5. package/dist/api/audits/index.js +25 -56
  6. package/dist/api/audits/index.js.map +1 -1
  7. package/dist/api/files/index.browser.js +31 -1
  8. package/dist/api/files/index.browser.js.map +1 -1
  9. package/dist/api/files/index.d.ts +313 -208
  10. package/dist/api/files/index.d.ts.map +1 -1
  11. package/dist/api/files/index.js +152 -42
  12. package/dist/api/files/index.js.map +1 -1
  13. package/dist/api/jobs/index.browser.js +2 -2
  14. package/dist/api/jobs/index.browser.js.map +1 -1
  15. package/dist/api/jobs/index.d.ts +282 -285
  16. package/dist/api/jobs/index.d.ts.map +1 -1
  17. package/dist/api/jobs/index.js +39 -33
  18. package/dist/api/jobs/index.js.map +1 -1
  19. package/dist/api/keys/index.d.ts +217 -222
  20. package/dist/api/keys/index.d.ts.map +1 -1
  21. package/dist/api/keys/index.js.map +1 -1
  22. package/dist/api/notifications/index.browser.js.map +1 -1
  23. package/dist/api/notifications/index.d.ts +188 -195
  24. package/dist/api/notifications/index.d.ts.map +1 -1
  25. package/dist/api/notifications/index.js.map +1 -1
  26. package/dist/api/oauth/index.d.ts +71 -76
  27. package/dist/api/oauth/index.d.ts.map +1 -1
  28. package/dist/api/oauth/index.js.map +1 -1
  29. package/dist/api/organizations/index.browser.js.map +1 -1
  30. package/dist/api/organizations/index.d.ts +104 -109
  31. package/dist/api/organizations/index.d.ts.map +1 -1
  32. package/dist/api/organizations/index.js.map +1 -1
  33. package/dist/api/parameters/index.browser.js +43 -16
  34. package/dist/api/parameters/index.browser.js.map +1 -1
  35. package/dist/api/parameters/index.d.ts +488 -344
  36. package/dist/api/parameters/index.d.ts.map +1 -1
  37. package/dist/api/parameters/index.js +175 -35
  38. package/dist/api/parameters/index.js.map +1 -1
  39. package/dist/api/payments/index.d.ts +396 -402
  40. package/dist/api/payments/index.d.ts.map +1 -1
  41. package/dist/api/payments/index.js.map +1 -1
  42. package/dist/api/subscriptions/index.d.ts +644 -652
  43. package/dist/api/subscriptions/index.d.ts.map +1 -1
  44. package/dist/api/subscriptions/index.js +1 -1
  45. package/dist/api/subscriptions/index.js.map +1 -1
  46. package/dist/api/users/index.browser.js +7 -0
  47. package/dist/api/users/index.browser.js.map +1 -1
  48. package/dist/api/users/index.d.ts +1106 -1005
  49. package/dist/api/users/index.d.ts.map +1 -1
  50. package/dist/api/users/index.js +307 -64
  51. package/dist/api/users/index.js.map +1 -1
  52. package/dist/api/verifications/index.browser.js.map +1 -1
  53. package/dist/api/verifications/index.d.ts +137 -143
  54. package/dist/api/verifications/index.d.ts.map +1 -1
  55. package/dist/api/verifications/index.js.map +1 -1
  56. package/dist/background/index.d.ts +95 -0
  57. package/dist/background/index.d.ts.map +1 -0
  58. package/dist/background/index.js +121 -0
  59. package/dist/background/index.js.map +1 -0
  60. package/dist/background/index.workerd.js +110 -0
  61. package/dist/background/index.workerd.js.map +1 -0
  62. package/dist/batch/index.d.ts +5 -7
  63. package/dist/batch/index.d.ts.map +1 -1
  64. package/dist/batch/index.js.map +1 -1
  65. package/dist/bin/index.js.map +1 -1
  66. package/dist/bucket/index.d.ts +76 -54
  67. package/dist/bucket/index.d.ts.map +1 -1
  68. package/dist/bucket/index.js +58 -11
  69. package/dist/bucket/index.js.map +1 -1
  70. package/dist/bucket/index.workerd.js +200 -5
  71. package/dist/bucket/index.workerd.js.map +1 -1
  72. package/dist/cache/core/index.d.ts +7 -10
  73. package/dist/cache/core/index.d.ts.map +1 -1
  74. package/dist/cache/core/index.js.map +1 -1
  75. package/dist/cache/core/index.workerd.js.map +1 -1
  76. package/dist/cache/database/index.d.ts +22 -26
  77. package/dist/cache/database/index.d.ts.map +1 -1
  78. package/dist/cache/database/index.js.map +1 -1
  79. package/dist/cache/redis/index.d.ts +4 -7
  80. package/dist/cache/redis/index.d.ts.map +1 -1
  81. package/dist/cache/redis/index.js.map +1 -1
  82. package/dist/captcha/index.d.ts +3 -6
  83. package/dist/captcha/index.d.ts.map +1 -1
  84. package/dist/captcha/index.js.map +1 -1
  85. package/dist/cli/config/index.d.ts.map +1 -1
  86. package/dist/cli/config/index.js.map +1 -1
  87. package/dist/cli/core/index.d.ts +458 -249
  88. package/dist/cli/core/index.d.ts.map +1 -1
  89. package/dist/cli/core/index.js +372 -660
  90. package/dist/cli/core/index.js.map +1 -1
  91. package/dist/cli/devtools/index.d.ts +3 -5
  92. package/dist/cli/devtools/index.d.ts.map +1 -1
  93. package/dist/cli/devtools/index.js.map +1 -1
  94. package/dist/cli/i18n/index.d.ts +20 -17
  95. package/dist/cli/i18n/index.d.ts.map +1 -1
  96. package/dist/cli/i18n/index.js +45 -11
  97. package/dist/cli/i18n/index.js.map +1 -1
  98. package/dist/cli/platform/index.d.ts +126 -1342
  99. package/dist/cli/platform/index.d.ts.map +1 -1
  100. package/dist/cli/platform/index.js +136 -2374
  101. package/dist/cli/platform/index.js.map +1 -1
  102. package/dist/cli/platform-lib/index.d.ts +1472 -0
  103. package/dist/cli/platform-lib/index.d.ts.map +1 -0
  104. package/dist/cli/platform-lib/index.js +2660 -0
  105. package/dist/cli/platform-lib/index.js.map +1 -0
  106. package/dist/cli/vendor/index.d.ts +17 -21
  107. package/dist/cli/vendor/index.d.ts.map +1 -1
  108. package/dist/cli/vendor/index.js.map +1 -1
  109. package/dist/command/index.d.ts +20 -19
  110. package/dist/command/index.d.ts.map +1 -1
  111. package/dist/command/index.js +39 -10
  112. package/dist/command/index.js.map +1 -1
  113. package/dist/{containers → container}/core/index.d.ts +13 -15
  114. package/dist/container/core/index.d.ts.map +1 -0
  115. package/dist/{containers → container}/core/index.js +23 -14
  116. package/dist/container/core/index.js.map +1 -0
  117. package/dist/{containers → container}/core/index.workerd.js +37 -22
  118. package/dist/container/core/index.workerd.js.map +1 -0
  119. package/dist/core/index.browser.js +27 -1
  120. package/dist/core/index.browser.js.map +1 -1
  121. package/dist/core/index.d.ts +48 -24
  122. package/dist/core/index.d.ts.map +1 -1
  123. package/dist/core/index.js +27 -1
  124. package/dist/core/index.js.map +1 -1
  125. package/dist/core/index.native.js +27 -1
  126. package/dist/core/index.native.js.map +1 -1
  127. package/dist/core/index.workerd.js +27 -1
  128. package/dist/core/index.workerd.js.map +1 -1
  129. package/dist/crypto/index.browser.js.map +1 -1
  130. package/dist/crypto/index.d.ts +5 -8
  131. package/dist/crypto/index.d.ts.map +1 -1
  132. package/dist/crypto/index.js.map +1 -1
  133. package/dist/datetime/index.d.ts +3 -4
  134. package/dist/datetime/index.d.ts.map +1 -1
  135. package/dist/datetime/index.js.map +1 -1
  136. package/dist/email/brevo/index.d.ts +2 -4
  137. package/dist/email/brevo/index.d.ts.map +1 -1
  138. package/dist/email/brevo/index.js.map +1 -1
  139. package/dist/email/cloudflare/index.d.ts +20 -7
  140. package/dist/email/cloudflare/index.d.ts.map +1 -1
  141. package/dist/email/cloudflare/index.js +46 -9
  142. package/dist/email/cloudflare/index.js.map +1 -1
  143. package/dist/email/core/index.d.ts +6 -9
  144. package/dist/email/core/index.d.ts.map +1 -1
  145. package/dist/email/core/index.js.map +1 -1
  146. package/dist/email/core/index.workerd.js.map +1 -1
  147. package/dist/email/smtp/index.d.ts +10 -13
  148. package/dist/email/smtp/index.d.ts.map +1 -1
  149. package/dist/email/smtp/index.js +107 -32
  150. package/dist/email/smtp/index.js.map +1 -1
  151. package/dist/fake/index.d.ts +1 -2
  152. package/dist/fake/index.d.ts.map +1 -1
  153. package/dist/fake/index.js.map +1 -1
  154. package/dist/lock/core/index.d.ts +9 -14
  155. package/dist/lock/core/index.d.ts.map +1 -1
  156. package/dist/lock/core/index.js.map +1 -1
  157. package/dist/lock/redis/index.d.ts +2 -4
  158. package/dist/lock/redis/index.d.ts.map +1 -1
  159. package/dist/lock/redis/index.js.map +1 -1
  160. package/dist/logger/index.d.ts +105 -76
  161. package/dist/logger/index.d.ts.map +1 -1
  162. package/dist/logger/index.js +196 -174
  163. package/dist/logger/index.js.map +1 -1
  164. package/dist/mcp/index.d.ts +25 -20
  165. package/dist/mcp/index.d.ts.map +1 -1
  166. package/dist/mcp/index.js +23 -0
  167. package/dist/mcp/index.js.map +1 -1
  168. package/dist/orm/core/index.browser.js.map +1 -1
  169. package/dist/orm/core/index.bun.js +19 -1
  170. package/dist/orm/core/index.bun.js.map +1 -1
  171. package/dist/orm/core/index.d.ts +76 -62
  172. package/dist/orm/core/index.d.ts.map +1 -1
  173. package/dist/orm/core/index.js +20 -2
  174. package/dist/orm/core/index.js.map +1 -1
  175. package/dist/orm/postgres/index.bun.js.map +1 -1
  176. package/dist/orm/postgres/index.d.ts +28 -20
  177. package/dist/orm/postgres/index.d.ts.map +1 -1
  178. package/dist/orm/postgres/index.js.map +1 -1
  179. package/dist/queue/core/index.d.ts +12 -15
  180. package/dist/queue/core/index.d.ts.map +1 -1
  181. package/dist/queue/core/index.js.map +1 -1
  182. package/dist/queue/core/index.workerd.js.map +1 -1
  183. package/dist/queue/redis/index.d.ts +3 -5
  184. package/dist/queue/redis/index.d.ts.map +1 -1
  185. package/dist/queue/redis/index.js.map +1 -1
  186. package/dist/react/auth/index.browser.js +9 -2
  187. package/dist/react/auth/index.browser.js.map +1 -1
  188. package/dist/react/auth/index.d.ts +14 -9
  189. package/dist/react/auth/index.d.ts.map +1 -1
  190. package/dist/react/auth/index.js +9 -2
  191. package/dist/react/auth/index.js.map +1 -1
  192. package/dist/react/core/index.d.ts +7 -8
  193. package/dist/react/core/index.d.ts.map +1 -1
  194. package/dist/react/core/index.js +6 -3
  195. package/dist/react/core/index.js.map +1 -1
  196. package/dist/react/form/index.d.ts +2 -5
  197. package/dist/react/form/index.d.ts.map +1 -1
  198. package/dist/react/form/index.js +16 -15
  199. package/dist/react/form/index.js.map +1 -1
  200. package/dist/react/head/index.browser.js.map +1 -1
  201. package/dist/react/head/index.d.ts +2 -4
  202. package/dist/react/head/index.d.ts.map +1 -1
  203. package/dist/react/head/index.js.map +1 -1
  204. package/dist/react/i18n/index.d.ts +90 -11
  205. package/dist/react/i18n/index.d.ts.map +1 -1
  206. package/dist/react/i18n/index.js +147 -11
  207. package/dist/react/i18n/index.js.map +1 -1
  208. package/dist/react/intro/index.d.ts +1 -2
  209. package/dist/react/intro/index.d.ts.map +1 -1
  210. package/dist/react/intro/index.js +2 -2
  211. package/dist/react/intro/index.js.map +1 -1
  212. package/dist/react/router/index.browser.js +193 -24
  213. package/dist/react/router/index.browser.js.map +1 -1
  214. package/dist/react/router/index.d.ts +434 -222
  215. package/dist/react/router/index.d.ts.map +1 -1
  216. package/dist/react/router/index.js +249 -35
  217. package/dist/react/router/index.js.map +1 -1
  218. package/dist/react/sitemap/index.browser.js +35 -0
  219. package/dist/react/sitemap/index.browser.js.map +1 -0
  220. package/dist/react/sitemap/index.d.ts +92 -0
  221. package/dist/react/sitemap/index.d.ts.map +1 -0
  222. package/dist/react/sitemap/index.js +131 -0
  223. package/dist/react/sitemap/index.js.map +1 -0
  224. package/dist/react/testing/index.d.ts +1 -2
  225. package/dist/react/testing/index.d.ts.map +1 -1
  226. package/dist/react/testing/index.js +16 -17
  227. package/dist/react/testing/index.js.map +1 -1
  228. package/dist/react/ui/index.d.ts +20 -25
  229. package/dist/react/ui/index.d.ts.map +1 -1
  230. package/dist/react/ui/index.js.map +1 -1
  231. package/dist/redis/index.bun.js.map +1 -1
  232. package/dist/redis/index.d.ts +17 -19
  233. package/dist/redis/index.d.ts.map +1 -1
  234. package/dist/redis/index.js.map +1 -1
  235. package/dist/retry/index.d.ts +2 -4
  236. package/dist/retry/index.d.ts.map +1 -1
  237. package/dist/retry/index.js.map +1 -1
  238. package/dist/router/index.d.ts.map +1 -1
  239. package/dist/router/index.js.map +1 -1
  240. package/dist/scheduler/index.d.ts +10 -13
  241. package/dist/scheduler/index.d.ts.map +1 -1
  242. package/dist/scheduler/index.js.map +1 -1
  243. package/dist/scheduler/index.workerd.js.map +1 -1
  244. package/dist/security/index.browser.js.map +1 -1
  245. package/dist/security/index.d.ts +45 -48
  246. package/dist/security/index.d.ts.map +1 -1
  247. package/dist/security/index.js.map +1 -1
  248. package/dist/server/auth/index.browser.js.map +1 -1
  249. package/dist/server/auth/index.d.ts +272 -173
  250. package/dist/server/auth/index.d.ts.map +1 -1
  251. package/dist/server/auth/index.js +1608 -15
  252. package/dist/server/auth/index.js.map +1 -1
  253. package/dist/server/cookies/index.browser.js.map +1 -1
  254. package/dist/server/cookies/index.d.ts +20 -7
  255. package/dist/server/cookies/index.d.ts.map +1 -1
  256. package/dist/server/cookies/index.js +22 -3
  257. package/dist/server/cookies/index.js.map +1 -1
  258. package/dist/server/core/index.browser.js.map +1 -1
  259. package/dist/server/core/index.d.ts +106 -73
  260. package/dist/server/core/index.d.ts.map +1 -1
  261. package/dist/server/core/index.js +44 -0
  262. package/dist/server/core/index.js.map +1 -1
  263. package/dist/server/cors/index.d.ts +11 -14
  264. package/dist/server/cors/index.d.ts.map +1 -1
  265. package/dist/server/cors/index.js.map +1 -1
  266. package/dist/server/etag/index.d.ts +6 -9
  267. package/dist/server/etag/index.d.ts.map +1 -1
  268. package/dist/server/etag/index.js.map +1 -1
  269. package/dist/server/health/index.d.ts +18 -21
  270. package/dist/server/health/index.d.ts.map +1 -1
  271. package/dist/server/health/index.js.map +1 -1
  272. package/dist/server/links/index.browser.js +2 -0
  273. package/dist/server/links/index.browser.js.map +1 -1
  274. package/dist/server/links/index.d.ts +63 -67
  275. package/dist/server/links/index.d.ts.map +1 -1
  276. package/dist/server/links/index.js +2 -0
  277. package/dist/server/links/index.js.map +1 -1
  278. package/dist/server/metrics/index.d.ts +5 -7
  279. package/dist/server/metrics/index.d.ts.map +1 -1
  280. package/dist/server/metrics/index.js.map +1 -1
  281. package/dist/server/proxy/index.d.ts +3 -5
  282. package/dist/server/proxy/index.d.ts.map +1 -1
  283. package/dist/server/proxy/index.js.map +1 -1
  284. package/dist/server/rate-limit/index.d.ts +10 -13
  285. package/dist/server/rate-limit/index.d.ts.map +1 -1
  286. package/dist/server/rate-limit/index.js.map +1 -1
  287. package/dist/server/static/index.d.ts +3 -5
  288. package/dist/server/static/index.d.ts.map +1 -1
  289. package/dist/server/static/index.js.map +1 -1
  290. package/dist/server/swagger/index.d.ts +5 -8
  291. package/dist/server/swagger/index.d.ts.map +1 -1
  292. package/dist/server/swagger/index.js.map +1 -1
  293. package/dist/sms/index.d.ts +3 -5
  294. package/dist/sms/index.d.ts.map +1 -1
  295. package/dist/sms/index.js.map +1 -1
  296. package/dist/system/index.browser.js.map +1 -1
  297. package/dist/system/index.d.ts +2 -4
  298. package/dist/system/index.d.ts.map +1 -1
  299. package/dist/system/index.js.map +1 -1
  300. package/dist/system/index.workerd.js.map +1 -1
  301. package/dist/topic/core/index.d.ts +4 -6
  302. package/dist/topic/core/index.d.ts.map +1 -1
  303. package/dist/topic/core/index.js.map +1 -1
  304. package/dist/topic/redis/index.d.ts +5 -8
  305. package/dist/topic/redis/index.d.ts.map +1 -1
  306. package/dist/topic/redis/index.js.map +1 -1
  307. package/package.json +59 -23
  308. package/src/api/audits/__tests__/AuditService.spec.ts +18 -110
  309. package/src/api/audits/controllers/AdminAuditController.ts +14 -0
  310. package/src/api/audits/services/AuditService.ts +21 -88
  311. package/src/api/files/__tests__/FileService.spec.ts +207 -2
  312. package/src/api/files/index.ts +3 -0
  313. package/src/api/files/schemas/fileCreatorSummarySchema.ts +22 -0
  314. package/src/api/files/schemas/fileResourceSchema.ts +10 -1
  315. package/src/api/files/services/FileService.ts +170 -72
  316. package/src/api/jobs/__tests__/$job.spec.ts +24 -1
  317. package/src/api/jobs/index.ts +4 -3
  318. package/src/api/jobs/primitives/$job.ts +7 -3
  319. package/src/api/jobs/providers/DirectJobDispatcher.ts +17 -36
  320. package/src/api/jobs/providers/JobProvider.ts +53 -24
  321. package/src/api/jobs/schemas/jobConfigAtom.ts +1 -1
  322. package/src/api/jobs/schemas/jobExecutionResourceSchema.ts +4 -1
  323. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +3 -1
  324. package/src/api/parameters/__tests__/$parameter.spec.ts +19 -2
  325. package/src/api/parameters/audits/ParameterAudits.ts +17 -0
  326. package/src/api/parameters/controllers/AdminParameterController.ts +95 -19
  327. package/src/api/parameters/index.ts +3 -0
  328. package/src/api/parameters/schemas/activateParameterBodySchema.ts +3 -3
  329. package/src/api/parameters/schemas/createParameterVersionBodySchema.ts +3 -2
  330. package/src/api/parameters/schemas/parameterCreatorSummarySchema.ts +25 -0
  331. package/src/api/parameters/schemas/parameterResponseSchema.ts +5 -0
  332. package/src/api/parameters/schemas/rollbackParameterBodySchema.ts +4 -2
  333. package/src/api/parameters/services/ParameterProvider.ts +69 -6
  334. package/src/api/subscriptions/jobs/SubscriptionJobs.ts +1 -1
  335. package/src/api/users/__tests__/AdminSessionController.spec.ts +37 -0
  336. package/src/api/users/audits/SessionAudits.ts +33 -0
  337. package/src/api/users/audits/UserAudits.ts +19 -43
  338. package/src/api/users/controllers/AdminUserController.ts +66 -1
  339. package/src/api/users/controllers/RealmController.ts +1 -0
  340. package/src/api/users/entities/sessions.ts +6 -0
  341. package/src/api/users/entities/users.ts +2 -0
  342. package/src/api/users/index.ts +9 -1
  343. package/src/api/users/primitives/$realm.ts +29 -0
  344. package/src/api/users/providers/RealmProvider.ts +15 -0
  345. package/src/api/users/schemas/realmConfigSchema.ts +14 -0
  346. package/src/api/users/schemas/sessionResourceSchema.ts +16 -0
  347. package/src/api/users/schemas/updateUserSchema.ts +1 -8
  348. package/src/api/users/schemas/userQuerySchema.ts +7 -0
  349. package/src/api/users/services/CredentialService.ts +15 -6
  350. package/src/api/users/services/IdentityService.ts +2 -1
  351. package/src/api/users/services/RegistrationService.ts +2 -1
  352. package/src/api/users/services/SessionCrudService.ts +19 -2
  353. package/src/api/users/services/SessionService.ts +39 -19
  354. package/src/api/users/services/UserService.ts +106 -8
  355. package/src/background/__tests__/BackgroundTaskProvider.spec.ts +96 -0
  356. package/src/background/index.ts +37 -0
  357. package/src/background/index.workerd.ts +28 -0
  358. package/src/background/providers/BackgroundTaskProvider.ts +70 -0
  359. package/src/background/providers/WorkerdBackgroundTaskProvider.ts +43 -0
  360. package/src/bucket/__tests__/$bucket.spec.ts +18 -0
  361. package/src/bucket/__tests__/LocalFileStorageProvider.spec.ts +5 -0
  362. package/src/bucket/__tests__/MemoryFileStorageProvider.spec.ts +5 -0
  363. package/src/bucket/__tests__/NodeS3BucketProvider.spec.ts +23 -4
  364. package/src/bucket/__tests__/shared.ts +30 -0
  365. package/src/bucket/index.ts +5 -5
  366. package/src/bucket/index.workerd.ts +11 -4
  367. package/src/bucket/primitives/$bucket.ts +27 -0
  368. package/src/bucket/providers/FileStorageProvider.ts +13 -0
  369. package/src/bucket/providers/LocalFileStorageProvider.ts +17 -1
  370. package/src/bucket/providers/MemoryFileStorageProvider.ts +7 -0
  371. package/src/bucket/providers/{CloudflareR2Provider.ts → R2FileStorageProvider.ts} +10 -1
  372. package/src/bucket/providers/{NodeS3BucketProvider.ts → S3FileStorageProvider.ts} +27 -5
  373. package/src/cli/core/__tests__/BuildDockerTask.spec.ts +25 -1
  374. package/src/cli/core/__tests__/init.spec.ts +0 -219
  375. package/src/cli/core/atoms/buildOptions.ts +0 -12
  376. package/src/cli/core/commands/__tests__/BuildCommand.spec.ts +43 -0
  377. package/src/cli/core/commands/build.ts +105 -37
  378. package/src/cli/core/commands/init.ts +0 -12
  379. package/src/cli/core/commands/pack.ts +133 -0
  380. package/src/cli/core/index.ts +3 -3
  381. package/src/cli/core/providers/ViteDevServerProvider.ts +40 -16
  382. package/src/cli/core/services/PackageManagerUtils.ts +0 -16
  383. package/src/cli/core/services/ProjectScaffolder.ts +29 -291
  384. package/src/cli/core/tasks/BuildCloudflareTask.ts +382 -56
  385. package/src/cli/core/tasks/BuildDockerTask.ts +33 -3
  386. package/src/cli/core/tasks/BuildPrerenderTask.ts +44 -7
  387. package/src/cli/core/tasks/BuildTask.ts +34 -0
  388. package/src/cli/core/templates/apiIndexTs.ts +1 -22
  389. package/src/cli/core/templates/mainCss.ts +0 -1
  390. package/src/cli/core/templates/webAppRouterTs.ts +0 -99
  391. package/src/cli/core/templates/webIndexTs.ts +1 -22
  392. package/src/cli/i18n/__tests__/I18nCheckService.spec.ts +48 -0
  393. package/src/cli/i18n/services/I18nCheckService.ts +65 -11
  394. package/src/cli/platform/__tests__/SecretsCommand.spec.ts +5 -3
  395. package/src/cli/platform/commands/SecretsCommand.ts +8 -6
  396. package/src/cli/platform/commands/platform.ts +192 -46
  397. package/src/cli/platform/index.ts +12 -52
  398. package/src/cli/{platform → platform-lib}/__tests__/CloudflareAdapter.spec.ts +426 -169
  399. package/src/cli/{platform → platform-lib}/__tests__/NamingService.spec.ts +91 -4
  400. package/src/cli/{platform → platform-lib}/__tests__/VercelAdapter.spec.ts +56 -85
  401. package/src/cli/{platform → platform-lib}/adapters/CloudflareAdapter.ts +519 -190
  402. package/src/cli/{platform → platform-lib}/adapters/PlatformAdapter.ts +62 -35
  403. package/src/cli/{platform → platform-lib}/adapters/VercelAdapter.ts +6 -10
  404. package/src/cli/{platform → platform-lib}/atoms/platformOptions.ts +34 -1
  405. package/src/cli/platform-lib/index.ts +67 -0
  406. package/src/cli/platform-lib/services/NamingService.ts +136 -0
  407. package/src/cli/{platform → platform-lib}/services/PlatformInspector.ts +60 -13
  408. package/src/cli/{platform → platform-lib}/services/PlatformOrchestrator.ts +54 -43
  409. package/src/cli/{platform → platform-lib}/services/WranglerApi.ts +4 -2
  410. package/src/command/__tests__/Runner.spec.ts +20 -0
  411. package/src/command/helpers/EnvUtils.ts +19 -3
  412. package/src/command/helpers/Runner.ts +12 -2
  413. package/src/command/providers/CliProvider.ts +34 -1
  414. package/src/{containers → container}/core/__tests__/$container.spec.ts +5 -5
  415. package/src/{containers → container}/core/index.ts +4 -4
  416. package/src/{containers → container}/core/index.workerd.ts +19 -3
  417. package/src/{containers → container}/core/primitives/$container.ts +1 -1
  418. package/src/{containers → container}/core/providers/CloudflareContainerProvider.ts +17 -19
  419. package/src/{containers → container}/core/providers/ContainerProvider.ts +16 -2
  420. package/src/{containers → container}/core/providers/MockContainerProvider.ts +1 -1
  421. package/src/core/Alepha.ts +49 -1
  422. package/src/core/__tests__/$env.spec.ts +42 -0
  423. package/src/core/__tests__/dump.spec.ts +47 -0
  424. package/src/email/cloudflare/__tests__/CloudflareEmailProvider.spec.ts +42 -10
  425. package/src/email/cloudflare/index.ts +14 -5
  426. package/src/email/cloudflare/providers/CloudflareEmailProvider.ts +54 -9
  427. package/src/logger/__tests__/Logger.spec.ts +55 -0
  428. package/src/logger/index.ts +13 -0
  429. package/src/logger/services/Logger.ts +31 -1
  430. package/src/mcp/__tests__/McpServerProvider.spec.ts +71 -0
  431. package/src/mcp/providers/McpServerProvider.ts +55 -0
  432. package/src/orm/__tests__/orm-showcase-tests.ts +27 -0
  433. package/src/orm/__tests__/orm-showcase.spec.ts +12 -0
  434. package/src/orm/core/interfaces/PgQuery.ts +4 -1
  435. package/src/orm/core/services/Repository.ts +27 -11
  436. package/src/react/auth/hooks/useAuth.ts +10 -5
  437. package/src/react/core/__tests__/useQuery.browser.spec.tsx +25 -0
  438. package/src/react/core/hooks/useAction.ts +14 -3
  439. package/src/react/core/hooks/useQuery.ts +24 -4
  440. package/src/react/form/__tests__/FormModel-submit-loading.spec.ts +71 -0
  441. package/src/react/form/__tests__/form-submitting-reactive.browser.spec.tsx +96 -0
  442. package/src/react/form/services/FormModel.ts +57 -39
  443. package/src/react/i18n/__tests__/I18nProvider.spec.ts +89 -0
  444. package/src/react/i18n/__tests__/locale-routing.spec.ts +107 -0
  445. package/src/react/i18n/components/Translate.tsx +47 -0
  446. package/src/react/i18n/index.ts +2 -0
  447. package/src/react/i18n/providers/I18nProvider.ts +171 -12
  448. package/src/react/intro/components/GettingStartedAdminSlide.tsx +2 -2
  449. package/src/react/router/__tests__/$page.spec.tsx +3 -2
  450. package/src/react/router/__tests__/RouterLocaleProvider.spec.ts +127 -0
  451. package/src/react/router/__tests__/page-can.spec.ts +18 -13
  452. package/src/react/router/hooks/useQueryParams.ts +114 -14
  453. package/src/react/router/index.browser.ts +4 -0
  454. package/src/react/router/index.shared.ts +1 -0
  455. package/src/react/router/index.ts +9 -0
  456. package/src/react/router/primitives/$page.ts +85 -4
  457. package/src/react/router/providers/ReactBrowserRouterProvider.ts +18 -8
  458. package/src/react/router/providers/ReactPageProvider.ts +12 -1
  459. package/src/react/router/providers/ReactServerProvider.ts +96 -14
  460. package/src/react/router/providers/RootComponentsProvider.ts +13 -0
  461. package/src/react/router/providers/RouterLocaleProvider.ts +125 -0
  462. package/src/react/router/providers/__tests__/RootComponentsProvider.spec.ts +15 -0
  463. package/src/react/router/providers/__tests__/rootComponents.ssr.browser.spec.tsx +67 -0
  464. package/src/react/sitemap/__tests__/$sitemap.spec.ts +131 -0
  465. package/src/react/sitemap/index.browser.ts +21 -0
  466. package/src/react/sitemap/index.ts +25 -0
  467. package/src/react/sitemap/primitives/$sitemap.browser.ts +26 -0
  468. package/src/react/sitemap/primitives/$sitemap.ts +196 -0
  469. package/src/react/ui/services/SchemaControl.ts +3 -4
  470. package/src/server/auth/__tests__/appleClientSecret.spec.ts +34 -0
  471. package/src/server/auth/__tests__/authFederationClient.spec.ts +40 -0
  472. package/src/server/auth/__tests__/federationAssertion.spec.ts +146 -0
  473. package/src/server/auth/__tests__/federationRedirectReplay.spec.ts +44 -0
  474. package/src/server/auth/helpers/appleClientSecret.ts +24 -0
  475. package/src/server/auth/helpers/federationAssertion.ts +74 -0
  476. package/src/server/auth/helpers/jtiReplayGuard.ts +41 -0
  477. package/src/server/auth/helpers/safeRedirectPath.ts +19 -0
  478. package/src/server/auth/index.ts +4 -0
  479. package/src/server/auth/primitives/$authFederationBroker.ts +273 -0
  480. package/src/server/auth/primitives/$authFederationClient.ts +89 -0
  481. package/src/server/auth/providers/ServerAuthProvider.ts +18 -4
  482. package/src/server/cookies/__tests__/ServerCookiesProvider.spec.ts +70 -0
  483. package/src/server/cookies/providers/ServerCookiesProvider.ts +23 -3
  484. package/src/server/core/interfaces/ServerRequest.ts +8 -0
  485. package/src/server/core/primitives/$route.ts +27 -0
  486. package/src/server/core/providers/ServerMultipartProvider.ts +19 -0
  487. package/src/server/links/providers/LinkProvider.ts +10 -0
  488. package/dist/containers/core/index.d.ts.map +0 -1
  489. package/dist/containers/core/index.js.map +0 -1
  490. package/dist/containers/core/index.workerd.js.map +0 -1
  491. package/src/cli/core/tasks/BuildSitemapTask.ts +0 -130
  492. package/src/cli/core/templates/componentsJsonTs.ts +0 -39
  493. package/src/cli/core/templates/saasAdminLayoutTsx.ts +0 -77
  494. package/src/cli/core/templates/saasAdminPagesTsx.ts +0 -26
  495. package/src/cli/core/templates/saasAuthLayoutTsx.ts +0 -22
  496. package/src/cli/core/templates/saasAuthPagesTsx.ts +0 -62
  497. package/src/cli/core/templates/saasRealmProviderTs.ts +0 -52
  498. package/src/cli/platform/services/NamingService.ts +0 -54
  499. /package/dist/orm/core/{chunk-o8xxKEmq.js → chunk-B4FMCO8f.js} +0 -0
  500. /package/dist/react/testing/{chunk-6Ep1yQYe.js → chunk-BpyX8vjI.js} +0 -0
  501. /package/src/cli/{platform → platform-lib}/__tests__/GitHubSecretStore.spec.ts +0 -0
  502. /package/src/cli/{platform → platform-lib}/__tests__/PlatformCacheProvider.spec.ts +0 -0
  503. /package/src/cli/{platform → platform-lib}/__tests__/PlatformInspector.spec.ts +0 -0
  504. /package/src/cli/{platform → platform-lib}/__tests__/PlatformOrchestrator.spec.ts +0 -0
  505. /package/src/cli/{platform → platform-lib}/__tests__/SecretFilterService.spec.ts +0 -0
  506. /package/src/cli/{platform → platform-lib}/__tests__/detectResources.spec.ts +0 -0
  507. /package/src/cli/{platform → platform-lib}/providers/GitHubSecretStore.ts +0 -0
  508. /package/src/cli/{platform → platform-lib}/providers/MemorySecretStore.ts +0 -0
  509. /package/src/cli/{platform → platform-lib}/providers/PlatformCacheProvider.ts +0 -0
  510. /package/src/cli/{platform → platform-lib}/providers/SecretStoreProvider.ts +0 -0
  511. /package/src/cli/{platform → platform-lib}/schemas/cloudflare.ts +0 -0
  512. /package/src/cli/{platform → platform-lib}/schemas/platform.ts +0 -0
  513. /package/src/cli/{platform → platform-lib}/schemas/vercel.ts +0 -0
  514. /package/src/cli/{platform → platform-lib}/services/CloudflareApi.ts +0 -0
  515. /package/src/cli/{platform → platform-lib}/services/SecretFilterService.ts +0 -0
  516. /package/src/cli/{platform → platform-lib}/services/VercelApi.ts +0 -0
  517. /package/src/cli/{platform → platform-lib}/services/VercelCli.ts +0 -0
  518. /package/src/{containers → container}/core/interfaces/ContainerOptions.ts +0 -0
  519. /package/src/{containers → container}/core/providers/NodeContainerProvider.ts +0 -0
@@ -10,9 +10,7 @@ import { basename, dirname, isAbsolute, join, relative, resolve } from "node:pat
10
10
  import pkg from "alepha/package.json" with { type: "json" };
11
11
  import { analyzer } from "vite-bundle-analyzer";
12
12
  import { KV_DEFAULT_BINDING } from "alepha/cache";
13
- import { $container } from "alepha/containers";
14
- import { EmailProvider } from "alepha/email";
15
- import { CloudflareEmailProvider, SEND_EMAIL_DEFAULT_BINDING } from "alepha/email/cloudflare";
13
+ import { SEND_EMAIL_DEFAULT_BINDING } from "alepha/email/cloudflare";
16
14
  import { QUEUE_DEFAULT_BINDING } from "alepha/queue";
17
15
  import { promisify } from "node:util";
18
16
  import { brotliCompress, gzip } from "node:zlib";
@@ -289,15 +287,7 @@ domain: t.optional(t.string()) })),
289
287
  * TODO: Not yet implemented.
290
288
  */
291
289
  offline: t.optional(t.boolean())
292
- })),
293
- /**
294
- * Sitemap generation configuration.
295
- */
296
- sitemap: t.optional(t.object({
297
- /**
298
- * Base URL for sitemap entries.
299
- */
300
- hostname: t.string() }))
290
+ }))
301
291
  }),
302
292
  default: {}
303
293
  });
@@ -1109,7 +1099,6 @@ var PackageManagerUtils = class {
1109
1099
  devDependencies.tailwindcss = alephaDeps.tailwindcss;
1110
1100
  devDependencies["@tailwindcss/vite"] = alephaDeps["@tailwindcss/vite"];
1111
1101
  }
1112
- if (modes.shadcn) devDependencies.shadcn = alephaDeps.shadcn;
1113
1102
  if (modes.react) {
1114
1103
  dependencies.react = alephaDeps.react;
1115
1104
  dependencies["react-dom"] = alephaDeps["react-dom"];
@@ -1279,19 +1268,7 @@ export type HelloResponse = Static<typeof helloResponseSchema>;
1279
1268
  //#endregion
1280
1269
  //#region ../../src/cli/core/templates/apiIndexTs.ts
1281
1270
  const apiIndexTs = (options = {}) => {
1282
- const { appName = "app", saas = false } = options;
1283
- if (saas) return `
1284
- import { $module } from "alepha";
1285
- import { AlephaApiUsers } from "alepha/api/users";
1286
- import { HelloController } from "./controllers/HelloController.ts";
1287
- import { RealmProvider } from "./providers/RealmProvider.ts";
1288
-
1289
- export const ApiModule = $module({
1290
- name: "${appName}.api",
1291
- services: [HelloController, RealmProvider],
1292
- imports: [AlephaApiUsers],
1293
- });
1294
- `.trim();
1271
+ const { appName = "app" } = options;
1295
1272
  return `
1296
1273
  import { $module } from "alepha";
1297
1274
  import { HelloController } from "./controllers/HelloController.ts";
@@ -1362,46 +1339,6 @@ const biomeJson = () => `
1362
1339
  }
1363
1340
  `.trim();
1364
1341
  //#endregion
1365
- //#region ../../src/cli/core/templates/componentsJsonTs.ts
1366
- /**
1367
- * `components.json` is the shadcn CLI's project config — it tells
1368
- * `shadcn add` where to drop primitives, which tailwind tokens to use,
1369
- * which icon library to wire up, and which custom registries to resolve.
1370
- *
1371
- * Aliases follow shadcn's defaults (`@/components`, `@/lib/utils`) so the
1372
- * CLI honors them across `init` + `add` calls. Alepha app code lives at
1373
- * `src/web/` (Home, AppRouter, …) and the shadcn primitives live at
1374
- * `src/components/` — kept separate to make the registry components
1375
- * trivially upgradable via `shadcn add --overwrite`.
1376
- *
1377
- * The `registries` block pre-wires the public Alepha registry — consumers
1378
- * can immediately run e.g. `shadcn add @alepha/auth-login`.
1379
- */
1380
- const componentsJsonTs = () => `{
1381
- "$schema": "https://ui.shadcn.com/schema.json",
1382
- "style": "new-york",
1383
- "rsc": false,
1384
- "tsx": true,
1385
- "tailwind": {
1386
- "config": "",
1387
- "css": "src/main.css",
1388
- "baseColor": "neutral",
1389
- "cssVariables": true
1390
- },
1391
- "aliases": {
1392
- "components": "@/components",
1393
- "utils": "@/lib/utils",
1394
- "ui": "@/components/ui",
1395
- "lib": "@/lib",
1396
- "hooks": "@/hooks"
1397
- },
1398
- "iconLibrary": "lucide",
1399
- "registries": {
1400
- "@alepha": "https://alepha.dev/r/{name}.json"
1401
- }
1402
- }
1403
- `;
1404
- //#endregion
1405
1342
  //#region ../../src/cli/core/templates/dummySpecTs.ts
1406
1343
  /**
1407
1344
  * Starter spec written by `alepha init`. It doubles as a worked example:
@@ -1555,7 +1492,6 @@ const mainCss = (opts = {}) => {
1555
1492
  *
1556
1493
  * Options:
1557
1494
  * - Tailwind CSS: Use \`alepha init --tailwind\` to add Tailwind CSS
1558
- * - shadcn/ui: Use \`alepha init --shadcn\` to add shadcn/ui setup
1559
1495
  * - Raw CSS: Write your own styles below
1560
1496
  */
1561
1497
 
@@ -1586,241 +1522,6 @@ run(alepha);
1586
1522
  `.trim();
1587
1523
  };
1588
1524
  //#endregion
1589
- //#region ../../src/cli/core/templates/saasAdminLayoutTsx.ts
1590
- /**
1591
- * SaaS admin layout — full AppShell on /admin with a sidebar, breadcrumbs,
1592
- * a Sonner toaster, and a confirm provider. The page list grows with
1593
- * whatever `admin-*` registry components the user adds.
1594
- *
1595
- * All UI primitives come from `src/components/*` where `shadcn add` drops
1596
- * them; alepha app code lives in `src/web/*` and references them via the
1597
- * `@/components/*` alias.
1598
- */
1599
- const saasAdminLayoutTsx = () => `import { AppShell } from "@/components/app-shell/app-shell";
1600
- import { Toaster } from "@/components/ui/sonner";
1601
- import { TooltipProvider } from "@/components/ui/tooltip";
1602
- import { DialogProvider } from "@/components/use-dialog/use-dialog";
1603
- import { NestedView, useRouterState } from "alepha/react/router";
1604
- import { ShieldCheck, Users } from "lucide-react";
1605
-
1606
- const NAV = [
1607
- {
1608
- label: "Identity",
1609
- items: [
1610
- { href: "/admin/users", label: "Users", icon: Users },
1611
- { href: "/admin/sessions", label: "Sessions", icon: ShieldCheck },
1612
- ],
1613
- },
1614
- ] as const;
1615
-
1616
- const findCrumbs = (pathname: string): { label: string; href?: string }[] => {
1617
- for (const group of NAV) {
1618
- const match = group.items.find((it) => it.href === pathname);
1619
- if (match) return [{ label: group.label }, { label: match.label }];
1620
- }
1621
- return [];
1622
- };
1623
-
1624
- const AdminLayout = () => {
1625
- const state = useRouterState();
1626
- const crumbs = findCrumbs(state.url.pathname);
1627
-
1628
- return (
1629
- <TooltipProvider>
1630
- <DialogProvider>
1631
- <AppShell
1632
- brand={
1633
- <a
1634
- href="/admin"
1635
- className="flex items-center gap-2 px-2 py-2 font-semibold group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:px-0"
1636
- >
1637
- <span className="bg-primary text-primary-foreground flex size-7 shrink-0 items-center justify-center rounded">
1638
- α
1639
- </span>
1640
- <span className="truncate group-data-[collapsible=icon]:hidden">
1641
- Admin
1642
- </span>
1643
- </a>
1644
- }
1645
- nav={NAV.map((group) => ({
1646
- label: group.label,
1647
- items: group.items.map((it) => ({
1648
- href: it.href,
1649
- label: it.label,
1650
- icon: it.icon,
1651
- active: it.href === state.url.pathname,
1652
- })),
1653
- }))}
1654
- breadcrumbs={crumbs.length ? crumbs : undefined}
1655
- >
1656
- <NestedView />
1657
- </AppShell>
1658
- <Toaster />
1659
- </DialogProvider>
1660
- </TooltipProvider>
1661
- );
1662
- };
1663
-
1664
- export default AdminLayout;
1665
- `;
1666
- //#endregion
1667
- //#region ../../src/cli/core/templates/saasAdminPagesTsx.ts
1668
- /**
1669
- * Admin pages — each is a thin wrapper around the matching `admin-*`
1670
- * registry component (placed at `src/components/admin/*`). The starter
1671
- * ships with Users + Sessions; add more by `shadcn add @alepha/admin-…`
1672
- * and a matching `$page(...)` in AppRouter.
1673
- */
1674
- const saasAdminUsersTsx = () => `import { AdminUsers } from "@/components/admin/admin-users";
1675
-
1676
- const AdminUsersPage = () => {
1677
- return <AdminUsers />;
1678
- };
1679
-
1680
- export default AdminUsersPage;
1681
- `;
1682
- const saasAdminSessionsTsx = () => `import { AdminSessions } from "@/components/admin/admin-sessions";
1683
-
1684
- const AdminSessionsPage = () => {
1685
- return <AdminSessions />;
1686
- };
1687
-
1688
- export default AdminSessionsPage;
1689
- `;
1690
- //#endregion
1691
- //#region ../../src/cli/core/templates/saasAuthLayoutTsx.ts
1692
- /**
1693
- * SaaS auth layout — route parent for every /auth/* page (login, register,
1694
- * reset-password, verify-email), mounted as children so they share it.
1695
- *
1696
- * The auth-* blocks from the registry are self-contained full-page layouts
1697
- * (they own their own min-height, centering and padding), so this layout is
1698
- * just a passthrough — wrapping them in another centered, padded, full-height
1699
- * box would overflow the viewport and add a scrollbar.
1700
- */
1701
- const saasAuthLayoutTsx = () => `import { NestedView } from "alepha/react/router";
1702
-
1703
- const AuthLayout = () => {
1704
- return (
1705
- <div className="bg-background">
1706
- <NestedView />
1707
- </div>
1708
- );
1709
- };
1710
-
1711
- export default AuthLayout;
1712
- `;
1713
- //#endregion
1714
- //#region ../../src/cli/core/templates/saasAuthPagesTsx.ts
1715
- /**
1716
- * Per-page wrapper around the registry components. The registry component
1717
- * receives the realm config from the page loader; the page itself stays a
1718
- * thin shell so apps can layer their branding around it.
1719
- *
1720
- * Registry components land at `src/components/auth/*` (shadcn defaults).
1721
- */
1722
- const saasAuthLoginTsx = () => `import { AuthLogin } from "@/components/auth/auth-login";
1723
- import type { RealmConfig } from "alepha/api/users";
1724
-
1725
- export interface AuthLoginPageProps {
1726
- realmConfig: RealmConfig;
1727
- }
1728
-
1729
- const AuthLoginPage = (props: AuthLoginPageProps) => {
1730
- return <AuthLogin realmConfig={props.realmConfig} />;
1731
- };
1732
-
1733
- export default AuthLoginPage;
1734
- `;
1735
- const saasAuthRegisterTsx = () => `import { AuthRegister } from "@/components/auth/auth-register";
1736
- import type { RealmConfig } from "alepha/api/users";
1737
-
1738
- export interface AuthRegisterPageProps {
1739
- realmConfig: RealmConfig;
1740
- }
1741
-
1742
- const AuthRegisterPage = (props: AuthRegisterPageProps) => {
1743
- return <AuthRegister realmConfig={props.realmConfig} />;
1744
- };
1745
-
1746
- export default AuthRegisterPage;
1747
- `;
1748
- const saasAuthResetPasswordTsx = () => `import { AuthResetPassword } from "@/components/auth/auth-reset-password";
1749
- import type { RealmConfig } from "alepha/api/users";
1750
-
1751
- export interface AuthResetPasswordPageProps {
1752
- realmConfig: RealmConfig;
1753
- }
1754
-
1755
- const AuthResetPasswordPage = (props: AuthResetPasswordPageProps) => {
1756
- return <AuthResetPassword realmConfig={props.realmConfig} />;
1757
- };
1758
-
1759
- export default AuthResetPasswordPage;
1760
- `;
1761
- const saasAuthVerifyEmailTsx = () => `import { AuthVerifyEmail } from "@/components/auth/auth-verify-email";
1762
-
1763
- const AuthVerifyEmailPage = () => {
1764
- return <AuthVerifyEmail />;
1765
- };
1766
-
1767
- export default AuthVerifyEmailPage;
1768
- `;
1769
- //#endregion
1770
- //#region ../../src/cli/core/templates/saasRealmProviderTs.ts
1771
- /**
1772
- * Realm provider scaffolded by `alepha init --saas`.
1773
- *
1774
- * Minimal hello-world setup: credentials login with email, admins seeded
1775
- * from the `ADMIN_EMAILS` env var, and an `admin:ui` permission used by the
1776
- * AppRouter to gate `/admin/*`. The default `admin` role grants `*` (so it
1777
- * inherits `admin:ui`); the default `user` role excludes `admin:*` (so
1778
- * non-admins get a 403 before the shell renders).
1779
- *
1780
- * Admin emails are read from the environment, never hard-coded — they are
1781
- * deployment config (different per environment, and personal data) and
1782
- * belong in `.env`, not in committed source.
1783
- */
1784
- const saasRealmProviderTs = () => {
1785
- return `import { $env, t } from "alepha";
1786
- import { $realm } from "alepha/api/users";
1787
- import { $permission } from "alepha/security";
1788
-
1789
- export class RealmProvider {
1790
- /**
1791
- * Permission required to open the admin UI. Wired into AppRouter.adminLayout
1792
- * via \`$secure({ permissions: ["admin:ui"] })\`.
1793
- */
1794
- adminUi = $permission({
1795
- group: "admin",
1796
- name: "ui",
1797
- description: "Access to the admin UI shell",
1798
- });
1799
-
1800
- /**
1801
- * Admin emails — set \`ADMIN_EMAILS\` in \`.env\` (comma-separated for
1802
- * several). Keep emails out of source: they are config, not code.
1803
- */
1804
- protected readonly env = $env(
1805
- t.object({
1806
- ADMIN_EMAILS: t.text({ default: "" }),
1807
- }),
1808
- );
1809
-
1810
- realm = $realm({
1811
- settings: {
1812
- adminEmails: this.env.ADMIN_EMAILS.split(",")
1813
- .map((email) => email.trim())
1814
- .filter(Boolean),
1815
- },
1816
- identities: {
1817
- credentials: true,
1818
- },
1819
- });
1820
- }
1821
- `;
1822
- };
1823
- //#endregion
1824
1525
  //#region ../../src/cli/core/templates/tsconfigJson.ts
1825
1526
  const tsconfigJson = () => `
1826
1527
  {
@@ -1886,10 +1587,6 @@ const vscodeSettingsJson = () => `
1886
1587
  //#endregion
1887
1588
  //#region ../../src/cli/core/templates/webAppRouterTs.ts
1888
1589
  const webAppRouterTs = (options) => {
1889
- if (options.saas) return saasAppRouterTs();
1890
- return basicAppRouterTs(options);
1891
- };
1892
- const basicAppRouterTs = (options) => {
1893
1590
  const imports = ["import { $page } from \"alepha/react/router\";"];
1894
1591
  const classMembers = [];
1895
1592
  if (options.api) {
@@ -1911,95 +1608,6 @@ export class AppRouter {
1911
1608
  ${classMembers.join("\n\n")}
1912
1609
  }`;
1913
1610
  };
1914
- /**
1915
- * SaaS router wires three trees onto the app:
1916
- * / → Home
1917
- * /auth/* → AuthLayout + login / register / reset / verify
1918
- * /admin/* → AdminLayout + users / sessions / api-keys / parameters / audits
1919
- *
1920
- * Each auth page resolves the realm config from its loader, so the registry
1921
- * components render with everything they need on first paint.
1922
- */
1923
- const saasAppRouterTs = () => `import type { RealmController } from "alepha/api/users";
1924
- import { $page, NotFound } from "alepha/react/router";
1925
- import { $secure } from "alepha/security";
1926
- import { $client } from "alepha/server/links";
1927
- import type { HelloController } from "../api/controllers/HelloController.ts";
1928
-
1929
- export class AppRouter {
1930
- protected readonly api = $client<HelloController>();
1931
- protected readonly realmApi = $client<RealmController>();
1932
-
1933
- home = $page({
1934
- path: "/",
1935
- lazy: () => import("./components/Home.tsx"),
1936
- loader: () => this.api.hello(),
1937
- });
1938
-
1939
- // ── /auth — login, register, reset, verify ─────────────────────────────
1940
- authLayout = $page({
1941
- path: "/auth",
1942
- lazy: () => import("./components/auth/AuthLayout.tsx"),
1943
- });
1944
-
1945
- login = $page({
1946
- parent: this.authLayout,
1947
- path: "/login",
1948
- name: "login",
1949
- head: { title: "Sign in" },
1950
- lazy: () => import("./components/auth/Login.tsx"),
1951
- loader: async () => ({ realmConfig: await this.realmApi.getRealmConfig() }),
1952
- });
1953
-
1954
- register = $page({
1955
- parent: this.authLayout,
1956
- path: "/register",
1957
- name: "register",
1958
- head: { title: "Sign up" },
1959
- lazy: () => import("./components/auth/Register.tsx"),
1960
- loader: async () => ({ realmConfig: await this.realmApi.getRealmConfig() }),
1961
- });
1962
-
1963
- resetPassword = $page({
1964
- parent: this.authLayout,
1965
- path: "/reset-password",
1966
- head: { title: "Reset password" },
1967
- lazy: () => import("./components/auth/ResetPassword.tsx"),
1968
- loader: async () => ({ realmConfig: await this.realmApi.getRealmConfig() }),
1969
- });
1970
-
1971
- verifyEmail = $page({
1972
- parent: this.authLayout,
1973
- path: "/verify-email",
1974
- head: { title: "Verify email" },
1975
- lazy: () => import("./components/auth/VerifyEmail.tsx"),
1976
- });
1977
-
1978
- // ── /admin — gated by 'admin:ui' permission, declared in RealmProvider.
1979
- // Children inherit the gate via the parent chain.
1980
- adminLayout = $page({
1981
- path: "/admin",
1982
- use: [$secure({ permissions: ["admin:ui"] })],
1983
- lazy: () => import("./components/admin/AdminLayout.tsx"),
1984
- });
1985
-
1986
- adminUsers = $page({
1987
- parent: this.adminLayout,
1988
- path: "/users",
1989
- head: { title: "Users" },
1990
- lazy: () => import("./components/admin/Users.tsx"),
1991
- });
1992
-
1993
- adminSessions = $page({
1994
- parent: this.adminLayout,
1995
- path: "/sessions",
1996
- head: { title: "Sessions" },
1997
- lazy: () => import("./components/admin/Sessions.tsx"),
1998
- });
1999
-
2000
- notFound = $page({ path: "/*", component: NotFound });
2001
- }
2002
- `;
2003
1611
  //#endregion
2004
1612
  //#region ../../src/cli/core/templates/webHomeComponentTsx.ts
2005
1613
  const webHomeComponentTsx = (options = {}) => {
@@ -2028,19 +1636,7 @@ export default Home;
2028
1636
  //#endregion
2029
1637
  //#region ../../src/cli/core/templates/webIndexTs.ts
2030
1638
  const webIndexTs = (options = {}) => {
2031
- const { appName = "app", saas = false } = options;
2032
- if (saas) return `
2033
- import { $module } from "alepha";
2034
- import { AlephaReactAuth } from "alepha/react/auth";
2035
- import { AlephaReactI18n } from "alepha/react/i18n";
2036
- import { AppRouter } from "./AppRouter.ts";
2037
-
2038
- export const WebModule = $module({
2039
- name: "${appName}.web",
2040
- services: [AppRouter],
2041
- imports: [AlephaReactAuth, AlephaReactI18n],
2042
- });
2043
- `.trim();
1639
+ const { appName = "app" } = options;
2044
1640
  return `
2045
1641
  import { $module } from "alepha";
2046
1642
  import { AppRouter } from "./AppRouter.ts";
@@ -2066,7 +1662,6 @@ var ProjectScaffolder = class {
2066
1662
  log = $logger();
2067
1663
  colors = $inject(ConsoleColorProvider);
2068
1664
  fs = $inject(FileSystemProvider);
2069
- shell = $inject(ShellProvider);
2070
1665
  pm = $inject(PackageManagerUtils);
2071
1666
  utils = $inject(AlephaCliUtils);
2072
1667
  /**
@@ -2088,10 +1683,7 @@ var ProjectScaffolder = class {
2088
1683
  const force = opts.force ?? false;
2089
1684
  const checkWorkspace = opts.checkWorkspace ?? false;
2090
1685
  if (opts.packageJson) tasks.push(this.pm.ensurePackageJson(root, typeof opts.packageJson === "boolean" ? {} : opts.packageJson).then(() => {}));
2091
- if (opts.tsconfigJson) tasks.push(this.ensureTsConfig(root, {
2092
- force,
2093
- localOnly: opts.tsconfigJson === "local"
2094
- }));
1686
+ if (opts.tsconfigJson) tasks.push(this.ensureTsConfig(root, { force }));
2095
1687
  if (opts.biomeJson) tasks.push(this.ensureBiomeConfig(root, {
2096
1688
  force,
2097
1689
  checkWorkspace
@@ -2108,7 +1700,7 @@ var ProjectScaffolder = class {
2108
1700
  await Promise.all(tasks);
2109
1701
  }
2110
1702
  async ensureTsConfig(root, opts = {}) {
2111
- const exists = opts.localOnly ? await this.fs.exists(this.fs.join(root, "tsconfig.json")) : await this.existsInParents(root, "tsconfig.json");
1703
+ const exists = await this.existsInParents(root, "tsconfig.json");
2112
1704
  if (!opts.force && exists) return;
2113
1705
  await this.fs.writeFile(this.fs.join(root, "tsconfig.json"), tsconfigJson());
2114
1706
  }
@@ -2185,33 +1777,9 @@ var ProjectScaffolder = class {
2185
1777
  const appName = this.getAppName(root);
2186
1778
  await this.fs.mkdir(this.fs.join(root, "src/api/controllers"), { recursive: true });
2187
1779
  await this.fs.mkdir(this.fs.join(root, "src/api/schemas"), { recursive: true });
2188
- await this.ensureFile(root, "src/api/index.ts", apiIndexTs({
2189
- appName,
2190
- saas: opts.saas
2191
- }), opts.force);
1780
+ await this.ensureFile(root, "src/api/index.ts", apiIndexTs({ appName }), opts.force);
2192
1781
  await this.ensureFile(root, "src/api/controllers/HelloController.ts", apiHelloControllerTs({ appName }), opts.force);
2193
1782
  await this.ensureFile(root, "src/api/schemas/helloResponseSchema.ts", apiHelloResponseSchemaTs(), opts.force);
2194
- if (opts.saas) {
2195
- await this.fs.mkdir(this.fs.join(root, "src/api/providers"), { recursive: true });
2196
- await this.ensureFile(root, "src/api/providers/RealmProvider.ts", saasRealmProviderTs(), opts.force);
2197
- const adminEmail = await this.detectGitEmail() ?? "admin@alepha.dev";
2198
- await this.ensureFile(root, ".env", `ADMIN_EMAILS=${adminEmail}\n`, opts.force);
2199
- }
2200
- }
2201
- /**
2202
- * Best-effort lookup for the developer's git email — seeded as
2203
- * `ADMIN_EMAILS` in the SaaS project's `.env`. Returns undefined if git
2204
- * isn't available or `user.email` isn't configured; the caller then falls
2205
- * back to the neutral `admin@alepha.dev`.
2206
- */
2207
- async detectGitEmail() {
2208
- try {
2209
- const email = (await this.shell.run("git config --get user.email", { capture: true }) ?? "").trim();
2210
- if (!email || !email.includes("@")) return void 0;
2211
- return email;
2212
- } catch {
2213
- return;
2214
- }
2215
1783
  }
2216
1784
  /**
2217
1785
  * Ensure web/React project structure exists.
@@ -2224,35 +1792,14 @@ var ProjectScaffolder = class {
2224
1792
  async ensureWebProject(root, opts = {}) {
2225
1793
  const appName = this.getAppName(root);
2226
1794
  await this.fs.mkdir(this.fs.join(root, "src/web/components"), { recursive: true });
2227
- if (opts.saas) {
2228
- await this.fs.mkdir(this.fs.join(root, "src/web/components/auth"), { recursive: true });
2229
- await this.fs.mkdir(this.fs.join(root, "src/web/components/admin"), { recursive: true });
2230
- }
2231
1795
  await this.fs.mkdir(this.fs.join(root, "public"), { recursive: true });
2232
1796
  await this.ensureFile(root, "public/favicon.svg", logoSvg, opts.force);
2233
1797
  await this.ensureFile(root, "src/main.css", mainCss({ tailwind: opts.tailwind }), opts.force);
2234
1798
  if (opts.tailwind) await this.ensureFile(root, "vite.config.ts", viteConfigTs(), opts.force);
2235
- if (opts.shadcn) await this.ensureFile(root, "components.json", componentsJsonTs(), opts.force);
2236
- await this.ensureFile(root, "src/web/index.ts", webIndexTs({
2237
- appName,
2238
- saas: opts.saas
2239
- }), opts.force);
2240
- await this.ensureFile(root, "src/web/AppRouter.ts", webAppRouterTs({
2241
- api: opts.api,
2242
- saas: opts.saas
2243
- }), opts.force);
1799
+ await this.ensureFile(root, "src/web/index.ts", webIndexTs({ appName }), opts.force);
1800
+ await this.ensureFile(root, "src/web/AppRouter.ts", webAppRouterTs({ api: opts.api }), opts.force);
2244
1801
  await this.ensureFile(root, "src/web/components/Home.tsx", webHomeComponentTsx({ api: opts.api }), opts.force);
2245
1802
  await this.ensureFile(root, "src/main.browser.ts", mainBrowserTs(), opts.force);
2246
- if (opts.saas) {
2247
- await this.ensureFile(root, "src/web/components/auth/AuthLayout.tsx", saasAuthLayoutTsx(), opts.force);
2248
- await this.ensureFile(root, "src/web/components/auth/Login.tsx", saasAuthLoginTsx(), opts.force);
2249
- await this.ensureFile(root, "src/web/components/auth/Register.tsx", saasAuthRegisterTsx(), opts.force);
2250
- await this.ensureFile(root, "src/web/components/auth/ResetPassword.tsx", saasAuthResetPasswordTsx(), opts.force);
2251
- await this.ensureFile(root, "src/web/components/auth/VerifyEmail.tsx", saasAuthVerifyEmailTsx(), opts.force);
2252
- await this.ensureFile(root, "src/web/components/admin/AdminLayout.tsx", saasAdminLayoutTsx(), opts.force);
2253
- await this.ensureFile(root, "src/web/components/admin/Users.tsx", saasAdminUsersTsx(), opts.force);
2254
- await this.ensureFile(root, "src/web/components/admin/Sessions.tsx", saasAdminSessionsTsx(), opts.force);
2255
- }
2256
1803
  }
2257
1804
  /**
2258
1805
  * Ensure test directory exists with a dummy test file + a self-contained
@@ -2275,21 +1822,16 @@ var ProjectScaffolder = class {
2275
1822
  * Full project init — scaffolds files, installs deps, sets up PM and git.
2276
1823
  */
2277
1824
  async init({ run, root, flags, args }) {
1825
+ if (!args) {
1826
+ if (!await this.fs.exists(this.fs.join(root, "package.json"))) args = "my-app";
1827
+ }
2278
1828
  if (args) {
2279
1829
  root = this.fs.join(root, args);
2280
1830
  await this.fs.mkdir(root, { force: true });
2281
1831
  }
2282
- const shadcnPreset = typeof flags.saas === "string" && flags.saas || typeof flags.shadcn === "string" && flags.shadcn || "b0";
2283
- const f = flags;
2284
- f.shadcn = !!flags.shadcn;
2285
- f.saas = !!flags.saas;
2286
- if (f.saas) {
2287
- f.shadcn = true;
2288
- f.api = true;
2289
- }
2290
- if (f.shadcn) f.tailwind = true;
2291
1832
  if (flags.tailwind) flags.react = true;
2292
- if ((flags.api || flags.react || flags.tailwind || flags.shadcn || flags.saas) && !flags.force) {
1833
+ const f = flags;
1834
+ if ((flags.api || flags.react || flags.tailwind) && !flags.force) {
2293
1835
  if ((await this.fs.ls(root)).filter((f) => f !== "package.json").length > 0) throw new AlephaError(`Target directory is not empty (${root}). Use --force to overwrite existing files.`);
2294
1836
  }
2295
1837
  const workspace = await this.pm.getWorkspaceContext(root);
@@ -2305,7 +1847,7 @@ var ProjectScaffolder = class {
2305
1847
  ...f,
2306
1848
  isPackage: workspace.isPackage
2307
1849
  },
2308
- tsconfigJson: f.shadcn ? "local" : !workspace.config.tsconfigJson,
1850
+ tsconfigJson: !workspace.config.tsconfigJson,
2309
1851
  biomeJson: true,
2310
1852
  editorconfig: !workspace.config.editorconfig,
2311
1853
  agentMd: writeAgentMd,
@@ -2317,15 +1859,10 @@ var ProjectScaffolder = class {
2317
1859
  react: !!flags.react && !isExpo,
2318
1860
  force
2319
1861
  });
2320
- if (flags.api) await this.ensureApiProject(root, {
2321
- saas: !!flags.saas,
2322
- force
2323
- });
1862
+ if (flags.api) await this.ensureApiProject(root, { force });
2324
1863
  if (flags.react && !isExpo) await this.ensureWebProject(root, {
2325
1864
  api: !!flags.api,
2326
1865
  tailwind: !!flags.tailwind,
2327
- shadcn: !!flags.shadcn,
2328
- saas: !!flags.saas,
2329
1866
  force
2330
1867
  });
2331
1868
  }
@@ -2343,18 +1880,6 @@ var ProjectScaffolder = class {
2343
1880
  root: installRoot
2344
1881
  });
2345
1882
  await this.ensureTestDir(root);
2346
- const exec = pmExecPrefix(pmName);
2347
- if (flags.shadcn) {
2348
- await run(`${exec} shadcn init --no-monorepo --base radix -t vite --yes --force --reinstall --preset ${escapeShellArg(shadcnPreset)}`, {
2349
- alias: `running shadcn init (preset ${shadcnPreset})`,
2350
- root
2351
- });
2352
- await this.fs.writeFile(this.fs.join(root, "components.json"), componentsJsonTs());
2353
- }
2354
- if (flags.saas) await run(`${exec} shadcn add @alepha/saas --yes --overwrite`, {
2355
- alias: "adding alepha saas registry bundle",
2356
- root
2357
- });
2358
1883
  try {
2359
1884
  await run(`${pmName} run lint`, {
2360
1885
  alias: "running linter",
@@ -2401,30 +1926,6 @@ var ProjectScaffolder = class {
2401
1926
  }
2402
1927
  }
2403
1928
  };
2404
- /**
2405
- * Map a package manager name to the command that runs a project-local binary.
2406
- *
2407
- * - npm: `npx`
2408
- * - yarn: `yarn` (yarn auto-resolves binary names; `yarn shadcn ...` works)
2409
- * - pnpm: `pnpm exec`
2410
- * - bun: `bunx`
2411
- *
2412
- * Used to invoke `shadcn init` / `shadcn add` regardless of the user's PM —
2413
- * `npm shadcn ...` is invalid (it tries to run a script named `shadcn`).
2414
- */
2415
- /** Quote a value so it survives shell parsing. */
2416
- const escapeShellArg = (value) => {
2417
- if (/^[A-Za-z0-9_./@:-]+$/.test(value)) return value;
2418
- return `'${value.replace(/'/g, "'\\''")}'`;
2419
- };
2420
- const pmExecPrefix = (pmName) => {
2421
- switch (pmName) {
2422
- case "npm": return "npx";
2423
- case "pnpm": return "pnpm exec";
2424
- case "bun": return "bunx";
2425
- default: return "yarn";
2426
- }
2427
- };
2428
1929
  //#endregion
2429
1930
  //#region ../../src/cli/core/tasks/BuildTask.ts
2430
1931
  /**
@@ -2563,6 +2064,7 @@ var BuildClientTask = class extends BuildTask {
2563
2064
  };
2564
2065
  //#endregion
2565
2066
  //#region ../../src/cli/core/tasks/BuildCloudflareTask.ts
2067
+ const CLOUDFLARE_EMAIL_PROVIDER_NAME = "CloudflareEmailProvider";
2566
2068
  /**
2567
2069
  * Generate Cloudflare Workers deployment configuration.
2568
2070
  *
@@ -2586,7 +2088,7 @@ var BuildCloudflareTask = class BuildCloudflareTask extends BuildTask {
2586
2088
  }
2587
2089
  async generateCloudflare(ctx, distDir) {
2588
2090
  const root = ctx.root;
2589
- const name = basename(root);
2091
+ const name = basename(root).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63);
2590
2092
  const hasAssets = await this.fs.exists(this.fs.join(root, distDir, "public"));
2591
2093
  const wrangler = {
2592
2094
  name,
@@ -2617,8 +2119,77 @@ var BuildCloudflareTask = class BuildCloudflareTask extends BuildTask {
2617
2119
  this.enhanceEmail(ctx, wrangler);
2618
2120
  const containers = this.enhanceContainers(ctx, wrangler);
2619
2121
  await this.fs.writeFile(this.fs.join(root, distDir, "wrangler.jsonc"), JSON.stringify(wrangler, null, 2));
2122
+ if (!ctx.manifest) await this.writeManifest(ctx, root, distDir, name, containers);
2620
2123
  await this.writeWorkerEntryPoint(root, distDir, containers);
2621
2124
  }
2125
+ /**
2126
+ * Write `dist/manifest.json` — a build-time snapshot of everything
2127
+ * downstream tooling needs to know about the app without re-booting
2128
+ * it. Used by `alepha platform up --prebuilt` (and Alepha Rocket) so
2129
+ * the deploy path can skip the Vite-based introspection step and the
2130
+ * workspace's runtime npm install.
2131
+ */
2132
+ async writeManifest(ctx, root, distDir, name, containers) {
2133
+ let hasDatabase = false;
2134
+ let hasBucket = false;
2135
+ let hasKV = false;
2136
+ let hasQueue = false;
2137
+ let crons = [];
2138
+ try {
2139
+ hasDatabase = (ctx.alepha.inject("RepositoryProvider").getRepositories?.() ?? []).length > 0;
2140
+ } catch {}
2141
+ try {
2142
+ hasBucket = ctx.alepha.primitives("$bucket").length > 0;
2143
+ } catch {}
2144
+ try {
2145
+ hasKV = ctx.alepha.primitives("cache").filter((p) => p.options?.provider == null).length > 0;
2146
+ } catch {}
2147
+ try {
2148
+ hasQueue = ctx.alepha.primitives("$queue").length > 0;
2149
+ } catch {}
2150
+ try {
2151
+ const cronProvider = ctx.alepha.inject("CronProvider");
2152
+ crons = [...new Set((cronProvider.getCronJobs?.() ?? []).map((c) => c.expression))];
2153
+ } catch {}
2154
+ const defaultEnv = ctx.platformOptions?.default ?? "production";
2155
+ const environments = ctx.platformOptions?.environments ?? {};
2156
+ let env = [];
2157
+ try {
2158
+ env = Object.keys(ctx.alepha.dump().env).sort();
2159
+ } catch {}
2160
+ let email;
2161
+ try {
2162
+ ctx.alepha.inject(CLOUDFLARE_EMAIL_PROVIDER_NAME);
2163
+ email = { binding: SEND_EMAIL_DEFAULT_BINDING };
2164
+ } catch {}
2165
+ const manifest = {
2166
+ version: 1,
2167
+ project: name,
2168
+ defaultEnv,
2169
+ tenancy: ctx.platformOptions?.tenancy,
2170
+ environments,
2171
+ resources: {
2172
+ hasDatabase,
2173
+ hasBucket,
2174
+ hasKV,
2175
+ hasQueue,
2176
+ hasCron: crons.length > 0
2177
+ },
2178
+ crons,
2179
+ containers: containers.map((c) => ({
2180
+ name: c.name,
2181
+ className: c.className,
2182
+ image: c.image,
2183
+ port: c.port,
2184
+ sleepAfter: c.sleepAfter,
2185
+ instanceType: c.instanceType,
2186
+ maxInstances: c.maxInstances
2187
+ })),
2188
+ email,
2189
+ env
2190
+ };
2191
+ await this.fs.writeFile(this.fs.join(root, distDir, "manifest.json"), JSON.stringify(manifest, null, 2));
2192
+ }
2622
2193
  enhanceDomain(wrangler) {
2623
2194
  const domain = process.env.CLOUDFLARE_DOMAIN;
2624
2195
  if (!domain) return;
@@ -2637,16 +2208,20 @@ var BuildCloudflareTask = class BuildCloudflareTask extends BuildTask {
2637
2208
  }];
2638
2209
  }
2639
2210
  enhanceCron(ctx, wrangler) {
2640
- if (ctx.alepha.primitives("scheduler").length === 0) return;
2211
+ const cronExpressions = ctx.manifest ? ctx.manifest.crons : this.discoverCrons(ctx);
2212
+ if (cronExpressions.length === 0) return;
2213
+ wrangler.triggers ??= {};
2214
+ wrangler.triggers.crons = cronExpressions;
2215
+ }
2216
+ discoverCrons(ctx) {
2217
+ if (ctx.alepha.primitives("scheduler").length === 0) return [];
2641
2218
  let cronProvider;
2642
2219
  try {
2643
2220
  cronProvider = ctx.alepha.inject("CronProvider");
2644
2221
  } catch {}
2645
2222
  const crons = cronProvider?.getCronJobs();
2646
- if (!crons || crons.length === 0) return;
2647
- const cronExpressions = [...new Set(crons.map((c) => c.expression))];
2648
- wrangler.triggers ??= {};
2649
- wrangler.triggers.crons = cronExpressions;
2223
+ if (!crons || crons.length === 0) return [];
2224
+ return [...new Set(crons.map((c) => c.expression))];
2650
2225
  }
2651
2226
  enhanceDatabase(wrangler) {
2652
2227
  if (process.env.HYPERDRIVE_ID) {
@@ -2720,19 +2295,16 @@ var BuildCloudflareTask = class BuildCloudflareTask extends BuildTask {
2720
2295
  wrangler.queues.consumers.push({ queue: queueName });
2721
2296
  }
2722
2297
  enhanceEmail(ctx, wrangler) {
2723
- let provider;
2724
- try {
2725
- provider = ctx.alepha.inject(EmailProvider);
2726
- } catch {
2727
- return;
2728
- }
2729
- if (!(provider instanceof CloudflareEmailProvider)) return;
2298
+ let binding;
2299
+ if (ctx.manifest) binding = ctx.manifest.email?.binding;
2300
+ else if (ctx.alepha) try {
2301
+ ctx.alepha.inject(CLOUDFLARE_EMAIL_PROVIDER_NAME);
2302
+ binding = SEND_EMAIL_DEFAULT_BINDING;
2303
+ } catch {}
2304
+ if (!binding) return;
2730
2305
  wrangler.send_email = wrangler.send_email || [];
2731
- if (wrangler.send_email.some((b) => b.name === SEND_EMAIL_DEFAULT_BINDING)) return;
2732
- const entry = { name: SEND_EMAIL_DEFAULT_BINDING };
2733
- const destination = process.env.EMAIL_FROM;
2734
- if (destination) entry.destination_address = destination;
2735
- wrangler.send_email.push(entry);
2306
+ if (wrangler.send_email.some((b) => b.name === binding)) return;
2307
+ wrangler.send_email.push({ name: binding });
2736
2308
  }
2737
2309
  /**
2738
2310
  * Discover `$container` primitives and emit the matching Cloudflare
@@ -2744,19 +2316,45 @@ var BuildCloudflareTask = class BuildCloudflareTask extends BuildTask {
2744
2316
  * `writeWorkerEntryPoint` can emit `export class <NAME> extends
2745
2317
  * Container` declarations referencing them.
2746
2318
  */
2747
- enhanceContainers(ctx, wrangler) {
2748
- const primitives = ctx.alepha.primitives($container);
2749
- if (primitives.length === 0) return [];
2750
- const descriptors = primitives.map((p) => ({
2319
+ discoverContainers(ctx) {
2320
+ return ctx.alepha.primitives("container").map((p) => ({
2751
2321
  name: p.name.toUpperCase(),
2752
2322
  className: p.name.split(/[^a-zA-Z0-9]/).filter(Boolean).map((s) => s[0].toUpperCase() + s.slice(1)).join(""),
2753
2323
  image: p.options.image,
2754
2324
  port: p.options.port ?? 3e3,
2755
2325
  sleepAfter: typeof p.options.sleepAfter === "string" ? p.options.sleepAfter : "15m",
2756
- instanceType: p.options.instanceType ?? "dev",
2326
+ instanceType: p.options.instanceType === "dev" ? "lite" : p.options.instanceType ?? "lite",
2757
2327
  maxInstances: p.options.maxInstances ?? 5,
2758
2328
  envVars: p.options.envVars
2759
2329
  }));
2330
+ }
2331
+ /**
2332
+ * Expand a short image ref (e.g. `alepha-rocket:0.1.0`) into the
2333
+ * fully-qualified `registry.cloudflare.com/<account>/<image>:<tag>`
2334
+ * URL that wrangler validates at deploy time.
2335
+ *
2336
+ * Cloudflare Containers only pulls from `registry.cloudflare.com`;
2337
+ * wrangler accepts either a Dockerfile path or a fully-qualified
2338
+ * registry URL in the `image` field — not a bare DockerHub-style
2339
+ * name. We let `$container({ image })` callers write the short
2340
+ * form (it matches what `wrangler containers push <local>` accepts
2341
+ * + matches the local docker tag) and rewrite to the CF registry
2342
+ * URL here.
2343
+ *
2344
+ * Pass-through cases:
2345
+ * - already a full URL: starts with `registry.cloudflare.com/`
2346
+ * or contains a scheme/`://`
2347
+ * - looks like a Dockerfile path: starts with `./` or `/`
2348
+ */
2349
+ resolveContainerImage(image) {
2350
+ if (image.startsWith("./") || image.startsWith("/") || image.startsWith("registry.cloudflare.com/") || image.includes("://")) return image;
2351
+ const accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
2352
+ if (!accountId) return image;
2353
+ return `registry.cloudflare.com/${accountId}/${image}`;
2354
+ }
2355
+ enhanceContainers(ctx, wrangler) {
2356
+ const descriptors = ctx.manifest ? ctx.manifest.containers : this.discoverContainers(ctx);
2357
+ if (descriptors.length === 0) return [];
2760
2358
  wrangler.containers = wrangler.containers || [];
2761
2359
  wrangler.durable_objects = wrangler.durable_objects || {};
2762
2360
  wrangler.durable_objects.bindings = wrangler.durable_objects.bindings || [];
@@ -2765,7 +2363,7 @@ var BuildCloudflareTask = class BuildCloudflareTask extends BuildTask {
2765
2363
  for (const d of descriptors) {
2766
2364
  wrangler.containers.push({
2767
2365
  class_name: d.className,
2768
- image: d.image,
2366
+ image: this.resolveContainerImage(d.image),
2769
2367
  instance_type: d.instanceType,
2770
2368
  max_instances: d.maxInstances
2771
2369
  });
@@ -2784,9 +2382,8 @@ var BuildCloudflareTask = class BuildCloudflareTask extends BuildTask {
2784
2382
  async writeWorkerEntryPoint(root, distDir, containers = []) {
2785
2383
  const containerDeclarations = containers.map((c) => {
2786
2384
  const envVars = c.envVars ? ` envVars = ${JSON.stringify(c.envVars)};\n` : "";
2787
- return `export class ${c.className} extends Container {\n defaultPort = ${c.port};\n sleepAfter = "${c.sleepAfter}";\n${envVars}}`;
2385
+ return `export class ${c.className} extends globalThis.__alepha_CloudflareContainer {\n defaultPort = ${c.port};\n sleepAfter = "${c.sleepAfter}";\n${envVars}}`;
2788
2386
  }).join("\n\n");
2789
- const containerImport = containers.length > 0 ? `import { Container } from "@cloudflare/containers";\n\n${containerDeclarations}\n\n` : "";
2790
2387
  const workerCode = `
2791
2388
  import "./index.js";
2792
2389
 
@@ -2801,11 +2398,19 @@ const setWaitUntil = (executionCtx) => {
2801
2398
  }
2802
2399
  };
2803
2400
 
2401
+ // Bind the per-invocation Worker \`env\`: keep the full binding (D1, R2, KV, …)
2402
+ // in the store for providers, and lift its string values (secrets/vars like
2403
+ // PUBLIC_URL) into \`alepha.env\` so \`$env\` resolves them at runtime.
2404
+ const bindEnv = (env) => {
2405
+ __alepha.set("cloudflare.env", env);
2406
+ __alepha.loadEnv(env);
2407
+ };
2408
+
2804
2409
  export default {
2805
2410
  fetch: async (request, env, executionCtx) => {
2806
2411
  const ctx = { req: request, res: undefined };
2807
2412
 
2808
- __alepha.set("cloudflare.env", env);
2413
+ bindEnv(env);
2809
2414
  setWaitUntil(executionCtx);
2810
2415
 
2811
2416
  try {
@@ -2821,7 +2426,7 @@ export default {
2821
2426
  },
2822
2427
 
2823
2428
  scheduled: async (event, env, executionCtx) => {
2824
- __alepha.set("cloudflare.env", env);
2429
+ bindEnv(env);
2825
2430
  setWaitUntil(executionCtx);
2826
2431
 
2827
2432
  try {
@@ -2838,7 +2443,7 @@ export default {
2838
2443
  },
2839
2444
 
2840
2445
  queue: async (batch, env, executionCtx) => {
2841
- __alepha.set("cloudflare.env", env);
2446
+ bindEnv(env);
2842
2447
  setWaitUntil(executionCtx);
2843
2448
 
2844
2449
  try {
@@ -2859,7 +2464,8 @@ export default {
2859
2464
  },
2860
2465
  };
2861
2466
  `.trim();
2862
- await this.fs.writeFile(this.fs.join(root, distDir, "main.cloudflare.js"), `${this.warningComment}\n${containerImport}${workerCode}`.trim());
2467
+ const containerBlock = containers.length > 0 ? `${containerDeclarations}\n\n` : "";
2468
+ await this.fs.writeFile(this.fs.join(root, distDir, "main.cloudflare.js"), `${this.warningComment}\n${containerBlock}${workerCode}`.trim());
2863
2469
  }
2864
2470
  };
2865
2471
  //#endregion
@@ -2979,6 +2585,7 @@ var BuildDockerTask = class extends BuildTask {
2979
2585
  name: "generate deploy config (docker)",
2980
2586
  handler: async () => {
2981
2587
  const migrationsCopied = await this.copyMigrations(ctx.root, distDir);
2588
+ const hasDeps = await this.hasRuntimeDeps(ctx.root, distDir);
2982
2589
  await this.writeDockerfile(ctx.root, distDir, {
2983
2590
  compile,
2984
2591
  standard: {
@@ -2986,6 +2593,7 @@ var BuildDockerTask = class extends BuildTask {
2986
2593
  command: dockerCommand
2987
2594
  },
2988
2595
  hasMigrations: migrationsCopied,
2596
+ hasDeps,
2989
2597
  install: ctx.options.docker?.install ?? []
2990
2598
  });
2991
2599
  }
@@ -3071,6 +2679,21 @@ var BuildDockerTask = class extends BuildTask {
3071
2679
  }
3072
2680
  return false;
3073
2681
  }
2682
+ /**
2683
+ * Whether the produced `dist/package.json` declares any runtime
2684
+ * dependencies. Alepha apps normally bundle everything into the
2685
+ * server entry via Vite, leaving `dependencies: {}` — in which case
2686
+ * the generated Dockerfile's `RUN npm install` is wasted work
2687
+ * (and emits deprecation noise). Skip the line when empty.
2688
+ */
2689
+ async hasRuntimeDeps(root, distDir) {
2690
+ try {
2691
+ const pkg = await this.fs.readJsonFile(this.fs.join(root, distDir, "package.json"));
2692
+ return Object.keys(pkg.dependencies ?? {}).length > 0;
2693
+ } catch {
2694
+ return false;
2695
+ }
2696
+ }
3074
2697
  async writeDockerfile(root, distDir, opts) {
3075
2698
  const header = "# This file was automatically generated. DO NOT MODIFY.\n# Changes to this file will be lost when the code is regenerated.\n";
3076
2699
  const migrationsLine = opts.hasMigrations ? "COPY migrations ./migrations\n" : "";
@@ -3086,14 +2709,12 @@ ENTRYPOINT ["/app/app"]
3086
2709
  `;
3087
2710
  else {
3088
2711
  const { image, command } = opts.standard;
3089
- const installLine = opts.install.length ? `RUN npm install --no-save --no-fund --no-audit ${opts.install.join(" ")}\n` : "";
3090
2712
  dockerfile = `${header}FROM ${image}
3091
2713
  WORKDIR /app
3092
2714
 
3093
2715
  COPY . .
3094
2716
 
3095
- RUN ${command === "bun" ? "bun" : "npm"} install
3096
- ${installLine}
2717
+ ${opts.hasDeps ? `RUN ${command === "bun" ? "bun" : "npm"} install\n` : ""}${opts.install.length ? `RUN npm install --no-save --no-fund --no-audit ${opts.install.join(" ")}\n` : ""}
3097
2718
  ENV SERVER_HOST=0.0.0.0
3098
2719
 
3099
2720
  CMD ["${command}", "index.js"]
@@ -3137,11 +2758,18 @@ CMD ["${command}", "index.js"]
3137
2758
  //#endregion
3138
2759
  //#region ../../src/cli/core/tasks/BuildPrerenderTask.ts
3139
2760
  /**
3140
- * Pre-render static pages defined in the Alepha application.
2761
+ * Pre-render static pages and routes defined in the Alepha application.
3141
2762
  *
3142
- * Queries all page primitives with `static: true` and generates
3143
- * static HTML files for each page. Supports pages with parameterized
3144
- * routes via `static.entries` configuration.
2763
+ * Two passes, both writing into `dist/public`:
2764
+ * - **pages** every `$page` with `static: true` is rendered to an HTML file
2765
+ * (supports parameterized routes via `static.entries`).
2766
+ * - **routes** — every `static` route primitive (a `$route({ static: true })`
2767
+ * or a `$sitemap`) is invoked in-process and its body written verbatim to
2768
+ * `{path}` (e.g. `sitemap.xml`, `robots.txt`).
2769
+ *
2770
+ * Both passes read the primitive registry and call a method on the already
2771
+ * created primitive instances — no provider is re-injected, so this works in
2772
+ * the build's configured-but-not-started container.
3145
2773
  */
3146
2774
  var BuildPrerenderTask = class extends BuildTask {
3147
2775
  fs = $inject(FileSystemProvider);
@@ -3149,15 +2777,17 @@ var BuildPrerenderTask = class extends BuildTask {
3149
2777
  if (ctx.flags?.prebuilt) return;
3150
2778
  if (!ctx.hasClient) return;
3151
2779
  const pages = this.getStaticPages(ctx);
3152
- if (pages.length === 0) return;
2780
+ const routes = this.getStaticRoutePrimitives(ctx);
2781
+ if (pages.length === 0 && routes.length === 0) return;
3153
2782
  const distDir = ctx.options.output?.dist ?? "dist";
3154
2783
  const publicDir = ctx.options.output?.public ?? "public";
3155
2784
  const dist = this.fs.join(ctx.root, distDir, publicDir);
3156
2785
  await ctx.run({
3157
- name: "pre-render pages",
2786
+ name: "pre-render",
3158
2787
  handler: async () => {
3159
2788
  if (!ctx.alepha.isConfigured()) await ctx.alepha.events.emit("configure", ctx.alepha);
3160
- await this.prerenderFromAlepha(pages, dist);
2789
+ if (pages.length > 0) await this.prerenderFromAlepha(pages, dist);
2790
+ if (routes.length > 0) await this.prerenderRoutes(routes, dist);
3161
2791
  }
3162
2792
  });
3163
2793
  }
@@ -3167,6 +2797,15 @@ var BuildPrerenderTask = class extends BuildTask {
3167
2797
  return options.static && !options.children;
3168
2798
  });
3169
2799
  }
2800
+ /**
2801
+ * Static route primitives to snapshot: `$route({ static: true })` and every
2802
+ * `$sitemap`. Both expose an async `prerender(): { path, body }`.
2803
+ */
2804
+ getStaticRoutePrimitives(ctx) {
2805
+ const routes = ctx.alepha.primitives("route").filter((route) => route.options?.static === true);
2806
+ const sitemaps = ctx.alepha.primitives("sitemap");
2807
+ return [...routes, ...sitemaps];
2808
+ }
3170
2809
  async prerenderFromAlepha(pages, dist) {
3171
2810
  let count = 0;
3172
2811
  for (const page of pages) {
@@ -3184,6 +2823,14 @@ var BuildPrerenderTask = class extends BuildTask {
3184
2823
  }
3185
2824
  return count;
3186
2825
  }
2826
+ async prerenderRoutes(primitives, dist) {
2827
+ for (const primitive of primitives) {
2828
+ const { path, body } = await primitive.prerender();
2829
+ const filepath = this.fs.join(dist, path);
2830
+ await this.fs.mkdir(dirname(filepath));
2831
+ await this.fs.writeFile(filepath, body);
2832
+ }
2833
+ }
3187
2834
  async renderFile(page, options, dist) {
3188
2835
  const { html, state } = await page.render({
3189
2836
  html: true,
@@ -3457,76 +3104,6 @@ var BuildServerTask = class extends BuildTask {
3457
3104
  }
3458
3105
  };
3459
3106
  //#endregion
3460
- //#region ../../src/cli/core/tasks/BuildSitemapTask.ts
3461
- /**
3462
- * Generate sitemap.xml from Alepha page primitives.
3463
- *
3464
- * Queries all page primitives and generates a sitemap.xml
3465
- * containing URLs for all accessible pages.
3466
- */
3467
- var BuildSitemapTask = class extends BuildTask {
3468
- fs = $inject(FileSystemProvider);
3469
- async run(ctx) {
3470
- const hostname = ctx.options.sitemap?.hostname;
3471
- if (!hostname) return;
3472
- const pages = this.getSitemapPages(ctx);
3473
- if (pages.length === 0) return;
3474
- const distDir = ctx.options.output?.dist ?? "dist";
3475
- const publicDir = ctx.options.output?.public ?? "public";
3476
- const output = this.fs.join(ctx.root, distDir, publicDir, "sitemap.xml");
3477
- await ctx.run({
3478
- name: "generate sitemap",
3479
- handler: async () => {
3480
- const xml = this.generateSitemapFromPages(pages, hostname);
3481
- await this.fs.writeFile(output, xml);
3482
- }
3483
- });
3484
- }
3485
- getSitemapPages(ctx) {
3486
- return ctx.alepha.primitives("page").filter((page) => {
3487
- const options = page.options;
3488
- const path = options.path ?? "";
3489
- if (options.children) return false;
3490
- if (path.includes("*")) return false;
3491
- if (path === "/404") return false;
3492
- if (!options.schema?.params) return true;
3493
- if (options.static && typeof options.static === "object" && options.static.entries) return true;
3494
- return false;
3495
- });
3496
- }
3497
- generateSitemapFromPages(pages, baseUrl) {
3498
- const urls = [];
3499
- const normalizedBaseUrl = baseUrl.replace(/\/$/, "");
3500
- for (const page of pages) {
3501
- const options = page.options;
3502
- if (!options.schema?.params) {
3503
- const path = options.path || "";
3504
- const url = `${normalizedBaseUrl}${path === "" ? "/" : path}`;
3505
- urls.push(url);
3506
- } else if (options.static && typeof options.static === "object" && options.static.entries) for (const entry of options.static.entries) {
3507
- const url = `${normalizedBaseUrl}${this.buildPathFromParams(options.path || "", entry.params || {})}`;
3508
- urls.push(url);
3509
- }
3510
- }
3511
- return this.buildSitemapXml(urls);
3512
- }
3513
- buildPathFromParams(pathPattern, params) {
3514
- let path = pathPattern;
3515
- for (const [key, value] of Object.entries(params)) path = path.replace(`:${key}`, String(value));
3516
- return path || "/";
3517
- }
3518
- buildSitemapXml(urls) {
3519
- const lastMod = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
3520
- return `<?xml version="1.0" encoding="UTF-8"?>
3521
- <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
3522
- ${urls.map((url) => ` <url>\n <loc>${this.escapeXml(url)}</loc>\n <lastmod>${lastMod}</lastmod>\n </url>`).join("\n")}
3523
- </urlset>`;
3524
- }
3525
- escapeXml(str) {
3526
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
3527
- }
3528
- };
3529
- //#endregion
3530
3107
  //#region ../../src/cli/core/tasks/BuildStaticTask.ts
3531
3108
  /**
3532
3109
  * Generate a static site output.
@@ -3791,7 +3368,6 @@ var BuildCommand = class {
3791
3368
  $inject(BuildClientTask),
3792
3369
  $inject(BuildServerTask),
3793
3370
  $inject(BuildAssetsTask),
3794
- $inject(BuildSitemapTask),
3795
3371
  $inject(BuildPwaTask),
3796
3372
  $inject(BuildPrerenderTask),
3797
3373
  $inject(BuildVercelTask),
@@ -3801,6 +3377,23 @@ var BuildCommand = class {
3801
3377
  $inject(BuildCompressTask)
3802
3378
  ];
3803
3379
  /**
3380
+ * Value aliases accepted for `--target`.
3381
+ *
3382
+ * These let the CLI accept short forms (e.g. `--target cf`) that are
3383
+ * canonicalized to a real {@link BuildTarget} before they flow into the
3384
+ * pipeline. The enum in `flags.target` must also list the alias so it
3385
+ * passes schema validation.
3386
+ */
3387
+ targetAliases = { cf: "cloudflare" };
3388
+ /**
3389
+ * Canonicalize a raw `--target` value, mapping any known alias
3390
+ * (e.g. `cf` → `cloudflare`) to its real {@link BuildTarget}.
3391
+ */
3392
+ resolveTarget(target) {
3393
+ if (!target) return;
3394
+ return this.targetAliases[target] ?? target;
3395
+ }
3396
+ /**
3804
3397
  * Resolve the effective runtime based on target and explicit runtime flag.
3805
3398
  *
3806
3399
  * Some targets force a specific runtime:
@@ -3832,10 +3425,11 @@ var BuildCommand = class {
3832
3425
  "docker",
3833
3426
  "vercel",
3834
3427
  "cloudflare",
3428
+ "cf",
3835
3429
  "static"
3836
3430
  ], {
3837
3431
  aliases: ["t"],
3838
- description: "Deployment target"
3432
+ description: "Deployment target (cf = cloudflare)"
3839
3433
  })),
3840
3434
  runtime: t.optional(t.enum([
3841
3435
  "node",
@@ -3853,8 +3447,7 @@ var BuildCommand = class {
3853
3447
  aliases: ["c"],
3854
3448
  description: "Compile server to a single static binary (requires --target=docker --runtime=bun)"
3855
3449
  })),
3856
- prebuilt: t.optional(t.boolean({ description: "Skip the bundle steps (Vite client/server + asset compression). Only regenerates target-specific deploy config (e.g. wrangler.jsonc). Use when `dist/` is already built and you just need the config refreshed." })),
3857
- sitemap: t.optional(t.text({ description: "Generate sitemap.xml with base URL" }))
3450
+ prebuilt: t.optional(t.boolean({ description: "Skip the bundle steps (Vite client/server + asset compression). Only regenerates target-specific deploy config (e.g. wrangler.jsonc). Use when `dist/` is already built and you just need the config refreshed." }))
3858
3451
  }),
3859
3452
  handler: async ({ flags, run, root }) => {
3860
3453
  process.env.NODE_ENV = "production";
@@ -3862,17 +3455,19 @@ var BuildCommand = class {
3862
3455
  await this.scaffolder.ensureConfig(root, { tsconfigJson: true });
3863
3456
  const entry = await this.boot.getAppEntry(root);
3864
3457
  this.log.trace("Entry file found", { entry });
3865
- this.alepha.store.mut(buildOptions, (current) => ({
3866
- ...current,
3867
- stats: flags.stats ?? current.stats ?? false,
3868
- target: flags.target ?? current.target,
3869
- runtime: this.resolveRuntime(flags.target ?? current.target, flags.runtime ?? current.runtime),
3870
- ...flags.compile !== void 0 && { docker: {
3871
- ...current.docker,
3872
- compile: flags.compile ? current.docker?.compile ?? true : false
3873
- } },
3874
- ...flags.sitemap && { sitemap: { hostname: flags.sitemap } }
3875
- }));
3458
+ this.alepha.store.mut(buildOptions, (current) => {
3459
+ const target = this.resolveTarget(flags.target) ?? current.target;
3460
+ return {
3461
+ ...current,
3462
+ stats: flags.stats ?? current.stats ?? false,
3463
+ target,
3464
+ runtime: this.resolveRuntime(target, flags.runtime ?? current.runtime),
3465
+ ...flags.compile !== void 0 && { docker: {
3466
+ ...current.docker,
3467
+ compile: flags.compile ? current.docker?.compile ?? true : false
3468
+ } }
3469
+ };
3470
+ });
3876
3471
  const options = this.options;
3877
3472
  const distDir = options.output?.dist ?? "dist";
3878
3473
  if (!flags.prebuilt) await run.rm(distDir, { alias: "clean dist" });
@@ -3886,23 +3481,30 @@ var BuildCommand = class {
3886
3481
  target,
3887
3482
  runtime: options.runtime
3888
3483
  });
3484
+ let manifest = null;
3485
+ if (flags.prebuilt) manifest = await this.loadManifest(root);
3889
3486
  let appAlepha;
3890
3487
  let hasClient = false;
3891
- await run({
3892
- name: "analyze app",
3893
- handler: async () => {
3894
- appAlepha = await this.viteBuildProvider.init({ entry });
3895
- hasClient = this.viteBuildProvider.hasClient();
3896
- }
3897
- });
3898
- if (!appAlepha) throw new AlephaError("Alepha instance not found");
3488
+ if (!manifest) {
3489
+ await run({
3490
+ name: "analyze app",
3491
+ handler: async () => {
3492
+ appAlepha = await this.viteBuildProvider.init({ entry });
3493
+ hasClient = this.viteBuildProvider.hasClient();
3494
+ }
3495
+ });
3496
+ if (!appAlepha) throw new AlephaError("Alepha instance not found");
3497
+ }
3498
+ const platformOptions = this.alepha.store.get("alepha.cli.platform.options") ?? null;
3899
3499
  const ctx = {
3900
- alepha: appAlepha,
3500
+ alepha: appAlepha ?? null,
3901
3501
  options,
3902
3502
  root,
3903
3503
  run,
3904
3504
  entry,
3905
3505
  hasClient,
3506
+ manifest,
3507
+ platformOptions,
3906
3508
  flags: {
3907
3509
  image: flags.image,
3908
3510
  prebuilt: flags.prebuilt
@@ -3911,6 +3513,21 @@ var BuildCommand = class {
3911
3513
  for (const task of this.pipeline) await task.run(ctx);
3912
3514
  }
3913
3515
  });
3516
+ /**
3517
+ * Read `dist/manifest.json` produced by a previous `alepha build`.
3518
+ * Returns null when absent or unparseable — caller falls back to the
3519
+ * Vite-introspection path.
3520
+ */
3521
+ async loadManifest(root) {
3522
+ try {
3523
+ const fs = await import("node:fs/promises");
3524
+ const path = await import("node:path");
3525
+ const raw = await fs.readFile(path.join(root, "dist", "manifest.json"), "utf-8");
3526
+ return JSON.parse(raw);
3527
+ } catch {
3528
+ return null;
3529
+ }
3530
+ }
3914
3531
  };
3915
3532
  //#endregion
3916
3533
  //#region ../../src/cli/core/commands/clean.ts
@@ -4506,8 +4123,10 @@ var ViteDevServerProvider = class {
4506
4123
  handleHotUpdate: async (ctx) => {
4507
4124
  if (/[/\\]\.idea[/\\]/.test(ctx.file)) return [];
4508
4125
  if (this.waitingForRetry) return [];
4509
- const firstModule = ctx.modules[0];
4510
- if (firstModule && !firstModule._ssrModule) return;
4126
+ if (!/\.(tsx|jsx)$/.test(ctx.file)) {
4127
+ const firstModule = ctx.modules[0];
4128
+ if (firstModule && !firstModule._ssrModule) return;
4129
+ }
4511
4130
  this.changedFiles.add(ctx.file);
4512
4131
  if (/\.(tsx|jsx)$/.test(ctx.file)) {
4513
4132
  if (this.reloadDebounceTimer) {
@@ -4702,7 +4321,17 @@ if (import.meta.hot) {
4702
4321
  process.env.SERVER_PORT ??= `${port}`;
4703
4322
  }
4704
4323
  /**
4705
- * Invalidate modules and all their importers.
4324
+ * Invalidate modules and all their importers, across both client and
4325
+ * SSR module graphs.
4326
+ *
4327
+ * Vite registers a file under multiple module ids (one per query
4328
+ * variant — e.g. `?v=…`, `?import` and the bare path), and `getModuleById`
4329
+ * only matches one. For workspace-linked source files (`@alepha/ui/...`)
4330
+ * the SSR-graph entry is keyed by the absolute path while the client
4331
+ * may use the package-qualified id. `getModulesByFile` returns every
4332
+ * variant Vite knows about for a given absolute path — invalidating
4333
+ * each catches both halves of the dual graph and prevents stale SSR
4334
+ * compiled exports from surviving across edits.
4706
4335
  */
4707
4336
  invalidateModulesWithImporters(changedFiles) {
4708
4337
  const graph = this.server.moduleGraph;
@@ -4711,11 +4340,16 @@ if (import.meta.hot) {
4711
4340
  while (queue.length > 0) {
4712
4341
  const file = queue.pop();
4713
4342
  if (invalidated.has(file)) continue;
4714
- const mod = this.server.moduleGraph.getModuleById(file);
4715
- if (!mod) continue;
4716
- graph.invalidateModule(mod);
4717
4343
  invalidated.add(file);
4718
- for (const importer of mod.importers) if (importer.id && !invalidated.has(importer.id)) queue.push(importer.id);
4344
+ const mods = new Set([...graph.getModulesByFile(file) ?? [], ...graph.getModuleById(file) ? [graph.getModuleById(file)] : []]);
4345
+ if (mods.size === 0) continue;
4346
+ for (const mod of mods) {
4347
+ graph.invalidateModule(mod);
4348
+ for (const importer of mod.importers) {
4349
+ const key = importer.file ?? importer.id;
4350
+ if (key && !invalidated.has(key)) queue.push(key);
4351
+ }
4352
+ }
4719
4353
  }
4720
4354
  const entryPath = this.options.entry.server;
4721
4355
  const absoluteEntryPath = join(this.options.root, entryPath);
@@ -5299,8 +4933,6 @@ var InitCommand = class {
5299
4933
  description: "Include React dependencies and web module (src/web/)"
5300
4934
  })),
5301
4935
  tailwind: t.optional(t.boolean({ description: "Include Tailwind CSS with Vite plugin. Implies --react" })),
5302
- shadcn: t.optional(t.union([t.boolean(), t.text()], { description: "Set up shadcn/ui (components.json, cn helper, theme tokens, alepha registry). Pass an optional preset id (default: b0). Implies --react and --tailwind" })),
5303
- saas: t.optional(t.union([t.boolean(), t.text()], { description: "Scaffold a SaaS starter: auth (login/register/reset/verify) + admin panel (/admin AppShell with users/sessions/api-keys/parameters/audits). Pass an optional preset id (default: b0). Implies --shadcn and --api" })),
5304
4936
  force: t.optional(t.boolean({
5305
4937
  aliases: ["f"],
5306
4938
  description: "Override existing files"
@@ -5334,6 +4966,86 @@ var LintCommand = class {
5334
4966
  });
5335
4967
  };
5336
4968
  //#endregion
4969
+ //#region ../../src/cli/core/commands/pack.ts
4970
+ /**
4971
+ * Pack the workspace into a deployable `tar.gz`.
4972
+ *
4973
+ * The tar contains everything a remote runner (Alepha Rocket, or any
4974
+ * `alepha platform <op> --prebuilt` consumer) needs to deploy the app:
4975
+ *
4976
+ * dist/ pre-built output (incl. manifest.json)
4977
+ * migrations/ SQL files (if present)
4978
+ *
4979
+ * No source, no `alepha.config.ts`, no `package.json` — the deploy
4980
+ * side reads everything from `dist/manifest.json` and never touches
4981
+ * source. Excludes: `node_modules`, `.DS_Store`, macOS AppleDouble
4982
+ * (`._*`), `.alepha` build cache, `e2e`, `playwright-report`,
4983
+ * `coverage`.
4984
+ *
4985
+ * Output name: `<project-name>-<tag>.tar.gz` (default tag
4986
+ * "latest"). Project name comes from `package.json.name`. Naming
4987
+ * mirrors Docker tags — same artifact, different tag = different
4988
+ * file.
4989
+ */
4990
+ var PackCommand = class {
4991
+ log = $logger();
4992
+ fs = $inject(FileSystemProvider);
4993
+ shell = $inject(ShellProvider);
4994
+ pack = $command({
4995
+ name: "pack",
4996
+ description: "Pack the workspace into a deployable tar.gz (for `alepha platform --prebuilt` consumers like Alepha Rocket).",
4997
+ flags: t.object({
4998
+ tag: t.optional(t.text({
4999
+ aliases: ["t"],
5000
+ description: "Tag suffix for the artifact name (Docker-style). Defaults to `latest` → `<project>-latest.tar.gz`. Pass a real version like `0.0.2` for a pinned artifact."
5001
+ })),
5002
+ output: t.optional(t.text({
5003
+ aliases: ["o"],
5004
+ description: "Output directory for the tar.gz (default: current dir)."
5005
+ }))
5006
+ }),
5007
+ handler: async ({ flags, root, run }) => {
5008
+ const pkgPath = this.fs.join(root, "package.json");
5009
+ let project;
5010
+ try {
5011
+ const pkg = await this.fs.readJsonFile(pkgPath);
5012
+ if (!pkg.name) throw new AlephaError("Missing \"name\" in package.json — `alepha pack` needs it for the artifact filename.");
5013
+ project = pkg.name;
5014
+ } catch (err) {
5015
+ if (err instanceof AlephaError) throw err;
5016
+ throw new AlephaError(`Could not read package.json at ${pkgPath}. Run \`alepha pack\` from a workspace directory.`);
5017
+ }
5018
+ const tag = flags.tag ?? "latest";
5019
+ const outputDir = flags.output ?? root;
5020
+ const filename = `${project}-${tag}.tar.gz`;
5021
+ const outputPath = this.fs.join(outputDir, filename);
5022
+ const candidates = ["dist", "migrations"];
5023
+ const includes = [];
5024
+ for (const candidate of candidates) if (await this.fs.exists(this.fs.join(root, candidate))) includes.push(candidate);
5025
+ if (!includes.includes("dist")) throw new AlephaError("dist/ missing — run `alepha build --target=cloudflare` before `alepha pack`.");
5026
+ const manifestPath = this.fs.join(root, "dist", "manifest.json");
5027
+ if (!await this.fs.exists(manifestPath)) throw new AlephaError(`dist/manifest.json missing — required for prebuilt deploys. Rebuild with the current alepha version (\`alepha build --target=cloudflare\`).`);
5028
+ const cmd = `sh -c "COPYFILE_DISABLE=1 ${`tar -czf '${outputPath}' ${[
5029
+ "node_modules",
5030
+ ".DS_Store",
5031
+ "._*",
5032
+ ".alepha",
5033
+ "e2e",
5034
+ "playwright-report",
5035
+ "test-results",
5036
+ "coverage"
5037
+ ].map((p) => `--exclude='${p}'`).join(" ")} ${includes.map((p) => `'${p}'`).join(" ")}`}"`;
5038
+ await run({
5039
+ name: `pack → ${filename}`,
5040
+ handler: async () => {
5041
+ await this.shell.run(cmd, { root });
5042
+ }
5043
+ });
5044
+ this.log.info(`Packed ${filename} → ${outputPath}`);
5045
+ }
5046
+ });
5047
+ };
5048
+ //#endregion
5337
5049
  //#region ../../src/cli/core/commands/root.ts
5338
5050
  var RootCommand = class {
5339
5051
  log = $logger();
@@ -5497,6 +5209,7 @@ const AlephaCli = $module({
5497
5209
  DevCommand,
5498
5210
  InitCommand,
5499
5211
  LintCommand,
5212
+ PackCommand,
5500
5213
  RootCommand,
5501
5214
  TestCommand,
5502
5215
  TypecheckCommand,
@@ -5509,12 +5222,11 @@ const AlephaCli = $module({
5509
5222
  BuildDockerTask,
5510
5223
  BuildPrerenderTask,
5511
5224
  BuildServerTask,
5512
- BuildSitemapTask,
5513
5225
  BuildStaticTask,
5514
5226
  BuildVercelTask
5515
5227
  ]
5516
5228
  });
5517
5229
  //#endregion
5518
- export { AlephaCli, AlephaCliExtensionProvider, AlephaCliServices, AlephaCliUtils, AppEntryProvider, BuildAssetsTask, BuildClientTask, BuildCloudflareTask, BuildCommand, BuildCompressTask, BuildDockerTask, BuildPrerenderTask, BuildServerTask, BuildSitemapTask, BuildStaticTask, BuildTask, BuildVercelTask, ChangelogCommand, CleanCommand, DEFAULT_IGNORE, DbCommand, DevCommand, GitMessageParser, GitProvider, InitCommand, LintCommand, OpenApiCommand, PackageManagerUtils, ProjectScaffolder, RootCommand, TestCommand, TypecheckCommand, VerifyCommand, ViteBuildProvider, ViteDevServerProvider, ViteUtils, alephaPackageJson, appEntryOptions, buildOptions, changelogOptions, devOptions, version };
5230
+ export { AlephaCli, AlephaCliExtensionProvider, AlephaCliServices, AlephaCliUtils, AppEntryProvider, BuildAssetsTask, BuildClientTask, BuildCloudflareTask, BuildCommand, BuildCompressTask, BuildDockerTask, BuildPrerenderTask, BuildServerTask, BuildStaticTask, BuildTask, BuildVercelTask, ChangelogCommand, CleanCommand, DEFAULT_IGNORE, DbCommand, DevCommand, GitMessageParser, GitProvider, InitCommand, LintCommand, OpenApiCommand, PackCommand, PackageManagerUtils, ProjectScaffolder, RootCommand, TestCommand, TypecheckCommand, VerifyCommand, ViteBuildProvider, ViteDevServerProvider, ViteUtils, alephaPackageJson, appEntryOptions, buildOptions, changelogOptions, devOptions, version };
5519
5231
 
5520
5232
  //# sourceMappingURL=index.js.map