alepha 0.20.1 → 0.20.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (393) hide show
  1. package/README.md +0 -1
  2. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  3. package/assets/swagger-ui/swagger-ui.css +1 -1
  4. package/dist/api/audits/index.browser.js +49 -0
  5. package/dist/api/audits/index.browser.js.map +1 -1
  6. package/dist/api/audits/index.d.ts.map +1 -1
  7. package/dist/api/audits/index.js +49 -0
  8. package/dist/api/audits/index.js.map +1 -1
  9. package/dist/api/files/index.d.ts.map +1 -1
  10. package/dist/api/files/index.js +2 -1
  11. package/dist/api/files/index.js.map +1 -1
  12. package/dist/api/jobs/index.browser.js +64 -148
  13. package/dist/api/jobs/index.browser.js.map +1 -1
  14. package/dist/api/jobs/index.d.ts +339 -600
  15. package/dist/api/jobs/index.d.ts.map +1 -1
  16. package/dist/api/jobs/index.js +605 -1012
  17. package/dist/api/jobs/index.js.map +1 -1
  18. package/dist/api/keys/index.js.map +1 -1
  19. package/dist/api/notifications/index.d.ts +79 -27
  20. package/dist/api/notifications/index.d.ts.map +1 -1
  21. package/dist/api/notifications/index.js +90 -23
  22. package/dist/api/notifications/index.js.map +1 -1
  23. package/dist/api/organizations/index.d.ts.map +1 -1
  24. package/dist/api/parameters/index.browser.js +37 -0
  25. package/dist/api/parameters/index.browser.js.map +1 -1
  26. package/dist/api/parameters/index.d.ts +4 -65
  27. package/dist/api/parameters/index.d.ts.map +1 -1
  28. package/dist/api/parameters/index.js +37 -0
  29. package/dist/api/parameters/index.js.map +1 -1
  30. package/dist/api/payments/index.d.ts +2 -1
  31. package/dist/api/payments/index.d.ts.map +1 -1
  32. package/dist/api/payments/index.js +4 -2
  33. package/dist/api/payments/index.js.map +1 -1
  34. package/dist/api/users/index.d.ts +225 -5199
  35. package/dist/api/users/index.d.ts.map +1 -1
  36. package/dist/api/users/index.js +15 -11
  37. package/dist/api/users/index.js.map +1 -1
  38. package/dist/api/verifications/index.d.ts.map +1 -1
  39. package/dist/api/verifications/index.js +4 -2
  40. package/dist/api/verifications/index.js.map +1 -1
  41. package/dist/bucket/index.js +5 -1
  42. package/dist/bucket/index.js.map +1 -1
  43. package/dist/bucket/index.workerd.js +5 -1
  44. package/dist/bucket/index.workerd.js.map +1 -1
  45. package/dist/cache/core/index.js.map +1 -1
  46. package/dist/cache/core/index.workerd.js.map +1 -1
  47. package/dist/captcha/index.js.map +1 -1
  48. package/dist/cli/core/index.d.ts +225 -11681
  49. package/dist/cli/core/index.d.ts.map +1 -1
  50. package/dist/cli/core/index.js +732 -257
  51. package/dist/cli/core/index.js.map +1 -1
  52. package/dist/cli/devtools/index.js +7 -1
  53. package/dist/cli/devtools/index.js.map +1 -1
  54. package/dist/cli/platform/index.d.ts +65 -63
  55. package/dist/cli/platform/index.d.ts.map +1 -1
  56. package/dist/cli/platform/index.js +140 -27
  57. package/dist/cli/platform/index.js.map +1 -1
  58. package/dist/cli/vendor/index.js +15 -0
  59. package/dist/cli/vendor/index.js.map +1 -1
  60. package/dist/command/index.d.ts +1 -1
  61. package/dist/command/index.js +1 -1
  62. package/dist/command/index.js.map +1 -1
  63. package/dist/core/index.browser.js +6 -0
  64. package/dist/core/index.browser.js.map +1 -1
  65. package/dist/core/index.d.ts +8 -8
  66. package/dist/core/index.d.ts.map +1 -1
  67. package/dist/core/index.js +6 -0
  68. package/dist/core/index.js.map +1 -1
  69. package/dist/core/index.native.js +6 -0
  70. package/dist/core/index.native.js.map +1 -1
  71. package/dist/core/index.workerd.js +6 -0
  72. package/dist/core/index.workerd.js.map +1 -1
  73. package/dist/crypto/index.js.map +1 -1
  74. package/dist/datetime/index.js.map +1 -1
  75. package/dist/email/core/index.js.map +1 -1
  76. package/dist/email/smtp/index.js +2 -10522
  77. package/dist/email/smtp/index.js.map +1 -1
  78. package/dist/fake/index.d.ts +4 -8085
  79. package/dist/fake/index.d.ts.map +1 -1
  80. package/dist/fake/index.js +3 -33554
  81. package/dist/fake/index.js.map +1 -1
  82. package/dist/lock/core/index.js.map +1 -1
  83. package/dist/lock/redis/index.js.map +1 -1
  84. package/dist/logger/index.js +32 -1
  85. package/dist/logger/index.js.map +1 -1
  86. package/dist/mcp/index.js +5 -1
  87. package/dist/mcp/index.js.map +1 -1
  88. package/dist/orm/core/index.browser.js +1 -361
  89. package/dist/orm/core/index.browser.js.map +1 -1
  90. package/dist/orm/core/index.bun.js +14 -406
  91. package/dist/orm/core/index.bun.js.map +1 -1
  92. package/dist/orm/core/index.d.ts +96 -5117
  93. package/dist/orm/core/index.d.ts.map +1 -1
  94. package/dist/orm/core/index.js +23 -419
  95. package/dist/orm/core/index.js.map +1 -1
  96. package/dist/orm/postgres/index.bun.js +17 -20
  97. package/dist/orm/postgres/index.bun.js.map +1 -1
  98. package/dist/orm/postgres/index.d.ts +2 -613
  99. package/dist/orm/postgres/index.d.ts.map +1 -1
  100. package/dist/orm/postgres/index.js +17 -20
  101. package/dist/orm/postgres/index.js.map +1 -1
  102. package/dist/react/core/index.js.map +1 -1
  103. package/dist/react/form/index.d.ts +60 -1
  104. package/dist/react/form/index.d.ts.map +1 -1
  105. package/dist/react/form/index.js +86 -1
  106. package/dist/react/form/index.js.map +1 -1
  107. package/dist/react/head/index.browser.js +16 -1
  108. package/dist/react/head/index.browser.js.map +1 -1
  109. package/dist/react/head/index.d.ts +6 -0
  110. package/dist/react/head/index.d.ts.map +1 -1
  111. package/dist/react/head/index.js +16 -1
  112. package/dist/react/head/index.js.map +1 -1
  113. package/dist/react/i18n/index.js.map +1 -1
  114. package/dist/react/intro/index.js +22 -17
  115. package/dist/react/intro/index.js.map +1 -1
  116. package/dist/react/router/index.browser.js +78 -12
  117. package/dist/react/router/index.browser.js.map +1 -1
  118. package/dist/react/router/index.d.ts +57 -13
  119. package/dist/react/router/index.d.ts.map +1 -1
  120. package/dist/react/router/index.js +102 -14
  121. package/dist/react/router/index.js.map +1 -1
  122. package/dist/react/testing/index.d.ts +1 -411
  123. package/dist/react/testing/index.d.ts.map +1 -1
  124. package/dist/react/testing/index.js +13 -12293
  125. package/dist/react/testing/index.js.map +1 -1
  126. package/dist/react/ui/index.d.ts +124 -0
  127. package/dist/react/ui/index.d.ts.map +1 -0
  128. package/dist/react/ui/index.js +209 -0
  129. package/dist/react/ui/index.js.map +1 -0
  130. package/dist/react/websocket/index.js.map +1 -1
  131. package/dist/redis/index.js.map +1 -1
  132. package/dist/router/index.d.ts +13 -13
  133. package/dist/router/index.d.ts.map +1 -1
  134. package/dist/router/index.js +45 -32
  135. package/dist/router/index.js.map +1 -1
  136. package/dist/scheduler/index.d.ts +1 -83
  137. package/dist/scheduler/index.d.ts.map +1 -1
  138. package/dist/scheduler/index.js +2 -391
  139. package/dist/scheduler/index.js.map +1 -1
  140. package/dist/scheduler/index.workerd.js +2 -391
  141. package/dist/scheduler/index.workerd.js.map +1 -1
  142. package/dist/security/index.browser.js.map +1 -1
  143. package/dist/security/index.d.ts +2 -325
  144. package/dist/security/index.d.ts.map +1 -1
  145. package/dist/security/index.js +3 -1362
  146. package/dist/security/index.js.map +1 -1
  147. package/dist/server/auth/index.d.ts +1 -1054
  148. package/dist/server/auth/index.d.ts.map +1 -1
  149. package/dist/server/auth/index.js +16 -1224
  150. package/dist/server/auth/index.js.map +1 -1
  151. package/dist/server/cookies/index.js.map +1 -1
  152. package/dist/server/core/index.browser.js.map +1 -1
  153. package/dist/server/core/index.d.ts +1 -4
  154. package/dist/server/core/index.d.ts.map +1 -1
  155. package/dist/server/core/index.js +19 -4
  156. package/dist/server/core/index.js.map +1 -1
  157. package/dist/server/links/index.browser.js.map +1 -1
  158. package/dist/server/links/index.js.map +1 -1
  159. package/dist/server/metrics/index.d.ts +1 -514
  160. package/dist/server/metrics/index.d.ts.map +1 -1
  161. package/dist/server/metrics/index.js +4 -4356
  162. package/dist/server/metrics/index.js.map +1 -1
  163. package/dist/server/rate-limit/index.js.map +1 -1
  164. package/dist/server/static/index.js.map +1 -1
  165. package/dist/server/swagger/index.js +1 -1
  166. package/dist/server/swagger/index.js.map +1 -1
  167. package/dist/sms/index.js.map +1 -1
  168. package/dist/system/index.browser.js.map +1 -1
  169. package/dist/system/index.d.ts.map +1 -1
  170. package/dist/system/index.js +1 -0
  171. package/dist/system/index.js.map +1 -1
  172. package/dist/system/index.workerd.js.map +1 -1
  173. package/dist/topic/core/index.js +1 -1
  174. package/dist/topic/core/index.js.map +1 -1
  175. package/dist/websocket/index.browser.js +21 -0
  176. package/dist/websocket/index.browser.js.map +1 -1
  177. package/dist/websocket/index.js +21 -0
  178. package/dist/websocket/index.js.map +1 -1
  179. package/package.json +23 -37
  180. package/src/api/files/__tests__/FileController.spec.ts +1 -1
  181. package/src/api/files/jobs/FileJobs.ts +2 -1
  182. package/src/api/jobs/__tests__/$job.spec.ts +320 -2867
  183. package/src/api/jobs/controllers/AdminJobController.ts +29 -138
  184. package/src/api/jobs/entities/jobExecutionEntity.ts +27 -19
  185. package/src/api/jobs/index.browser.ts +5 -7
  186. package/src/api/jobs/index.ts +23 -51
  187. package/src/api/jobs/primitives/$job.ts +66 -58
  188. package/src/api/jobs/providers/JobProvider.ts +561 -566
  189. package/src/api/jobs/providers/JobQueueProvider.ts +18 -19
  190. package/src/api/jobs/schemas/jobConfigAtom.ts +20 -23
  191. package/src/api/jobs/schemas/jobExecutionQuerySchema.ts +3 -27
  192. package/src/api/jobs/schemas/jobExecutionResourceSchema.ts +5 -7
  193. package/src/api/jobs/schemas/jobRegistrationSchema.ts +7 -4
  194. package/src/api/jobs/schemas/triggerJobSchema.ts +0 -1
  195. package/src/api/jobs/services/JobService.ts +90 -483
  196. package/src/api/notifications/controllers/AdminNotificationController.ts +19 -12
  197. package/src/api/notifications/index.ts +7 -4
  198. package/src/api/notifications/jobs/NotificationJobs.ts +83 -12
  199. package/src/api/payments/services/PaymentService.ts +4 -2
  200. package/src/api/users/__tests__/UserJobs.spec.ts +10 -49
  201. package/src/api/users/audits/UserAudits.ts +3 -1
  202. package/src/api/users/buckets/UserBuckets.ts +2 -1
  203. package/src/api/users/index.ts +1 -4
  204. package/src/api/users/jobs/UserJobs.ts +5 -4
  205. package/src/api/users/schemas/userQuerySchema.ts +0 -1
  206. package/src/api/users/services/UserService.ts +1 -5
  207. package/src/api/verifications/__tests__/CodeVerification.spec.ts +14 -0
  208. package/src/api/verifications/__tests__/LinkVerification.spec.ts +14 -0
  209. package/src/api/verifications/jobs/VerificationJobs.ts +2 -1
  210. package/src/api/verifications/services/VerificationService.ts +1 -0
  211. package/src/cli/core/__tests__/init.spec.ts +209 -1
  212. package/src/cli/core/commands/init.ts +9 -9
  213. package/src/cli/core/services/PackageManagerUtils.ts +22 -12
  214. package/src/cli/core/services/ProjectScaffolder.ts +300 -70
  215. package/src/cli/core/tasks/BuildDockerTask.ts +9 -10
  216. package/src/cli/core/tasks/BuildServerTask.ts +8 -0
  217. package/src/cli/core/templates/agentMd.ts +2 -8
  218. package/src/cli/core/templates/apiIndexTs.ts +22 -14
  219. package/src/cli/core/templates/componentsJsonTs.ts +39 -0
  220. package/src/cli/core/templates/mainCss.ts +2 -36
  221. package/src/cli/core/templates/saasAdminLayoutTsx.ts +77 -0
  222. package/src/cli/core/templates/saasAdminPagesTsx.ts +26 -0
  223. package/src/cli/core/templates/saasAuthLayoutTsx.ts +20 -0
  224. package/src/cli/core/templates/saasAuthPagesTsx.ts +62 -0
  225. package/src/cli/core/templates/saasRealmProviderTs.ts +46 -0
  226. package/src/cli/core/templates/vitestConfigTs.ts +17 -0
  227. package/src/cli/core/templates/webAppRouterTs.ts +102 -82
  228. package/src/cli/core/templates/webIndexTs.ts +23 -1
  229. package/src/cli/platform/__tests__/CloudflareAdapter.spec.ts +22 -71
  230. package/src/cli/platform/__tests__/SecretsCommand.spec.ts +2 -0
  231. package/src/cli/platform/adapters/CloudflareAdapter.ts +12 -11
  232. package/src/cli/platform/atoms/platformOptions.ts +9 -0
  233. package/src/cli/platform/schemas/cloudflare.ts +3 -2
  234. package/src/cli/platform/services/CloudflareApi.ts +164 -25
  235. package/src/cli/platform/services/WranglerApi.ts +0 -17
  236. package/src/command/providers/CliProvider.ts +1 -1
  237. package/src/core/Alepha.ts +9 -0
  238. package/src/core/interfaces/Service.ts +3 -1
  239. package/src/core/providers/TypeProvider.ts +1 -1
  240. package/src/logger/services/Logger.ts +1 -1
  241. package/src/mcp/__tests__/$resource.spec.ts +1 -1
  242. package/src/mcp/__tests__/$tool.spec.ts +1 -1
  243. package/src/mcp/__tests__/McpServerProvider.spec.ts +1 -1
  244. package/src/orm/__tests__/$repository-tests.ts +1 -0
  245. package/src/orm/__tests__/orm-next-tests.ts +2 -67
  246. package/src/orm/__tests__/orm-next.spec.ts +0 -21
  247. package/src/orm/core/index.shared.ts +0 -2
  248. package/src/orm/core/index.ts +1 -2
  249. package/src/orm/core/primitives/$repository.ts +3 -6
  250. package/src/orm/core/providers/drivers/DatabaseProvider.ts +0 -5
  251. package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +11 -13
  252. package/src/orm/core/services/ModelBuilder.ts +1 -13
  253. package/src/orm/core/services/Repository.ts +1 -42
  254. package/src/orm/core/services/SqliteModelBuilder.ts +2 -33
  255. package/src/orm/postgres/services/PostgresModelBuilder.ts +10 -45
  256. package/src/react/form/index.ts +2 -0
  257. package/src/react/form/services/parseField.ts +163 -0
  258. package/src/react/form/services/prettyName.ts +19 -0
  259. package/src/react/head/providers/BrowserHeadProvider.ts +31 -10
  260. package/src/react/intro/components/GettingStartedAuthSlide.tsx +11 -4
  261. package/src/react/router/__tests__/ReactBrowserProvider.browser.spec.ts +213 -2
  262. package/src/react/router/primitives/$page.ts +35 -12
  263. package/src/react/router/providers/ReactBrowserProvider.ts +73 -0
  264. package/src/react/router/providers/ReactBrowserRouterProvider.ts +1 -1
  265. package/src/react/router/providers/ReactPreloadProvider.ts +1 -1
  266. package/src/react/router/providers/ReactServerProvider.ts +1 -0
  267. package/src/react/ui/atoms/uiAtom.ts +28 -0
  268. package/src/react/ui/components/ColorScheme.tsx +36 -0
  269. package/src/react/ui/hooks/useColorMode.ts +49 -0
  270. package/src/react/ui/hooks/useSidebarState.ts +26 -0
  271. package/src/react/ui/hooks/useTheme.ts +22 -0
  272. package/src/react/ui/index.ts +35 -0
  273. package/src/react/ui/services/UiPersistence.ts +41 -0
  274. package/src/router/TemplatedPathParser.ts +50 -51
  275. package/src/router/__tests__/RouterProvider.spec.ts +62 -0
  276. package/src/router/__tests__/TemplatedPathParser.spec.ts +18 -0
  277. package/src/router/providers/RouterProvider.ts +10 -5
  278. package/src/scheduler/providers/CronProvider.ts +1 -1
  279. package/src/security/primitives/$basicAuth.ts +1 -1
  280. package/src/server/auth/providers/ServerAuthProvider.ts +5 -1
  281. package/src/server/core/interfaces/ServerRequest.ts +1 -0
  282. package/src/server/core/providers/ServerProvider.ts +1 -1
  283. package/src/server/core/providers/ServerRouterProvider.ts +2 -2
  284. package/src/server/core/services/HttpClient.ts +1 -1
  285. package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
  286. package/src/system/providers/NodeShellProvider.ts +1 -0
  287. package/src/topic/core/providers/TopicProvider.ts +1 -1
  288. package/dist/api/invitations/index.d.ts +0 -790
  289. package/dist/api/invitations/index.d.ts.map +0 -1
  290. package/dist/api/invitations/index.js +0 -662
  291. package/dist/api/invitations/index.js.map +0 -1
  292. package/dist/api/issues/index.d.ts +0 -810
  293. package/dist/api/issues/index.d.ts.map +0 -1
  294. package/dist/api/issues/index.js +0 -444
  295. package/dist/api/issues/index.js.map +0 -1
  296. package/dist/api/subscriptions/index.d.ts +0 -1692
  297. package/dist/api/subscriptions/index.d.ts.map +0 -1
  298. package/dist/api/subscriptions/index.js +0 -1867
  299. package/dist/api/subscriptions/index.js.map +0 -1
  300. package/dist/api/workflows/index.browser.js +0 -246
  301. package/dist/api/workflows/index.browser.js.map +0 -1
  302. package/dist/api/workflows/index.d.ts +0 -1618
  303. package/dist/api/workflows/index.d.ts.map +0 -1
  304. package/dist/api/workflows/index.js +0 -1495
  305. package/dist/api/workflows/index.js.map +0 -1
  306. package/dist/react/testing/chunk-DBEY4PJZ.js +0 -16
  307. package/src/api/invitations/__tests__/InvitationService.spec.ts +0 -439
  308. package/src/api/invitations/controllers/AdminInvitationController.ts +0 -86
  309. package/src/api/invitations/controllers/InvitationController.ts +0 -84
  310. package/src/api/invitations/entities/invitations.ts +0 -33
  311. package/src/api/invitations/index.ts +0 -58
  312. package/src/api/invitations/jobs/InvitationJobs.ts +0 -37
  313. package/src/api/invitations/providers/InvitationProvider.ts +0 -45
  314. package/src/api/invitations/schemas/createInvitationSchema.ts +0 -12
  315. package/src/api/invitations/schemas/invitationConfigAtom.ts +0 -20
  316. package/src/api/invitations/schemas/invitationQuerySchema.ts +0 -15
  317. package/src/api/invitations/schemas/invitationResourceSchema.ts +0 -6
  318. package/src/api/invitations/schemas/invitationWithResourceInfoSchema.ts +0 -22
  319. package/src/api/invitations/schemas/myInvitationsQuerySchema.ts +0 -10
  320. package/src/api/invitations/services/InvitationService.ts +0 -556
  321. package/src/api/issues/__tests__/IssueService.spec.ts +0 -263
  322. package/src/api/issues/controllers/AdminIssueController.ts +0 -149
  323. package/src/api/issues/controllers/IssueController.ts +0 -44
  324. package/src/api/issues/entities/issues.ts +0 -49
  325. package/src/api/issues/index.ts +0 -50
  326. package/src/api/issues/schemas/createIssueSchema.ts +0 -13
  327. package/src/api/issues/schemas/issueConfigAtom.ts +0 -13
  328. package/src/api/issues/schemas/issueQuerySchema.ts +0 -18
  329. package/src/api/issues/schemas/issueResourceSchema.ts +0 -6
  330. package/src/api/issues/schemas/myIssueQuerySchema.ts +0 -10
  331. package/src/api/issues/schemas/updateIssueSchema.ts +0 -13
  332. package/src/api/issues/services/IssueService.ts +0 -264
  333. package/src/api/jobs/__tests__/$job-middleware.spec.ts +0 -126
  334. package/src/api/jobs/__tests__/JobService.spec.ts +0 -31
  335. package/src/api/jobs/entities/jobExecutionLogEntity.ts +0 -13
  336. package/src/api/jobs/schemas/jobActivitySchema.ts +0 -15
  337. package/src/api/jobs/schemas/jobCronInfoSchema.ts +0 -22
  338. package/src/api/jobs/schemas/jobExecutionDetailResourceSchema.ts +0 -20
  339. package/src/api/jobs/schemas/jobFailureSchema.ts +0 -9
  340. package/src/api/jobs/schemas/jobQueueDepthSchema.ts +0 -14
  341. package/src/api/jobs/schemas/jobStatsSchema.ts +0 -14
  342. package/src/api/jobs/services/JobService-tests.ts +0 -157
  343. package/src/api/subscriptions/__tests__/BillingService.spec.ts +0 -218
  344. package/src/api/subscriptions/__tests__/SubscriptionService.spec.ts +0 -278
  345. package/src/api/subscriptions/controllers/AdminSubscriptionController.ts +0 -212
  346. package/src/api/subscriptions/controllers/SubscriptionController.ts +0 -189
  347. package/src/api/subscriptions/entities/subscriptionEvents.ts +0 -54
  348. package/src/api/subscriptions/entities/subscriptions.ts +0 -68
  349. package/src/api/subscriptions/index.ts +0 -133
  350. package/src/api/subscriptions/jobs/SubscriptionJobs.ts +0 -382
  351. package/src/api/subscriptions/middleware/$requireLimit.ts +0 -50
  352. package/src/api/subscriptions/middleware/$requirePlan.ts +0 -49
  353. package/src/api/subscriptions/notifications/SubscriptionNotifications.ts +0 -110
  354. package/src/api/subscriptions/schemas/cancelSubscriptionSchema.ts +0 -8
  355. package/src/api/subscriptions/schemas/changePlanSchema.ts +0 -9
  356. package/src/api/subscriptions/schemas/createSubscriptionSchema.ts +0 -11
  357. package/src/api/subscriptions/schemas/entitlementsSchema.ts +0 -21
  358. package/src/api/subscriptions/schemas/mrrSchema.ts +0 -13
  359. package/src/api/subscriptions/schemas/planDefinitionSchema.ts +0 -71
  360. package/src/api/subscriptions/schemas/planResourceSchema.ts +0 -25
  361. package/src/api/subscriptions/schemas/subscriptionEventResourceSchema.ts +0 -8
  362. package/src/api/subscriptions/schemas/subscriptionQuerySchema.ts +0 -19
  363. package/src/api/subscriptions/schemas/subscriptionResourceSchema.ts +0 -6
  364. package/src/api/subscriptions/schemas/subscriptionSettingsSchema.ts +0 -32
  365. package/src/api/subscriptions/schemas/subscriptionStatsSchema.ts +0 -23
  366. package/src/api/subscriptions/services/BillingService.ts +0 -437
  367. package/src/api/subscriptions/services/SubscriptionConfig.ts +0 -56
  368. package/src/api/subscriptions/services/SubscriptionService.ts +0 -867
  369. package/src/api/subscriptions/services/UsageService.ts +0 -118
  370. package/src/api/workflows/__tests__/$workflow.spec.ts +0 -616
  371. package/src/api/workflows/controllers/AdminWorkflowController.ts +0 -191
  372. package/src/api/workflows/entities/workflowExecutions.ts +0 -74
  373. package/src/api/workflows/entities/workflowStepExecutions.ts +0 -74
  374. package/src/api/workflows/entities/workflowStepLogs.ts +0 -13
  375. package/src/api/workflows/index.browser.ts +0 -22
  376. package/src/api/workflows/index.ts +0 -115
  377. package/src/api/workflows/jobs/WorkflowJobs.ts +0 -77
  378. package/src/api/workflows/primitives/$workflow.ts +0 -202
  379. package/src/api/workflows/providers/WorkflowProvider.ts +0 -1284
  380. package/src/api/workflows/schemas/workflowActivitySchema.ts +0 -15
  381. package/src/api/workflows/schemas/workflowConfigAtom.ts +0 -51
  382. package/src/api/workflows/schemas/workflowExecutionDetailSchema.ts +0 -18
  383. package/src/api/workflows/schemas/workflowExecutionQuerySchema.ts +0 -26
  384. package/src/api/workflows/schemas/workflowExecutionResourceSchema.ts +0 -30
  385. package/src/api/workflows/schemas/workflowRegistrationSchema.ts +0 -26
  386. package/src/api/workflows/schemas/workflowStatsSchema.ts +0 -16
  387. package/src/api/workflows/schemas/workflowStepExecutionResourceSchema.ts +0 -15
  388. package/src/api/workflows/services/WorkflowService.ts +0 -382
  389. package/src/cli/core/templates/apiAppSecurityTs.ts +0 -43
  390. package/src/cli/core/templates/webAdminDashboardTsx.ts +0 -17
  391. package/src/orm/core/__tests__/parseQueryString.spec.ts +0 -196
  392. package/src/orm/core/helpers/parseQueryString.ts +0 -502
  393. package/src/orm/core/primitives/$view.ts +0 -88
@@ -37,7 +37,20 @@ const buildOptions = $atom({
37
37
  name: "alepha.cli.build.options",
38
38
  description: "Build configuration options",
39
39
  schema: t.object({
40
+ /**
41
+ * Generate build stats report.
42
+ *
43
+ * - `true` - Generate a static HTML report
44
+ * - `"json"` - Generate a JSON report
45
+ */
40
46
  stats: t.optional(t.union([t.boolean(), t.enum(["json"])])),
47
+ /**
48
+ * Deployment target for the build output.
49
+ *
50
+ * - `docker` - Generate Dockerfile for containerized deployment
51
+ * - `vercel` - Generate Vercel deployment configuration (forces node runtime)
52
+ * - `cloudflare` - Generate Cloudflare Workers configuration (forces workerd runtime)
53
+ */
41
54
  target: t.optional(t.enum([
42
55
  "bare",
43
56
  "docker",
@@ -45,15 +58,45 @@ const buildOptions = $atom({
45
58
  "cloudflare",
46
59
  "static"
47
60
  ])),
61
+ /**
62
+ * JavaScript runtime for the build output.
63
+ *
64
+ * - `node` - Node.js runtime (default)
65
+ * - `bun` - Bun runtime (uses bun export conditions)
66
+ * - `workerd` - Cloudflare Workers runtime (auto-set with cloudflare target)
67
+ *
68
+ * Note: Some targets force a specific runtime:
69
+ * - `cloudflare` always uses `workerd`
70
+ * - `vercel` always uses `node`
71
+ */
48
72
  runtime: t.optional(t.enum([
49
73
  "node",
50
74
  "bun",
51
75
  "workerd"
52
76
  ])),
77
+ /**
78
+ * Output directory configuration.
79
+ */
53
80
  output: t.optional(t.object({
81
+ /**
82
+ * Root dist directory.
83
+ *
84
+ * @default "dist"
85
+ */
54
86
  dist: t.optional(t.string({ default: "dist" })),
87
+ /**
88
+ * Public/client subdirectory.
89
+ *
90
+ * @default "public"
91
+ */
55
92
  public: t.optional(t.string({ default: "public" }))
56
93
  })),
94
+ /**
95
+ * Vercel-specific deployment configuration.
96
+ *
97
+ * Note: Set `target: "vercel"` to enable Vercel deployment.
98
+ * This object is only for additional configuration.
99
+ */
57
100
  vercel: t.optional(t.object({
58
101
  projectName: t.optional(t.string()),
59
102
  orgId: t.optional(t.string()),
@@ -63,31 +106,143 @@ const buildOptions = $atom({
63
106
  schedule: t.string()
64
107
  }))) }))
65
108
  })),
109
+ /**
110
+ * Cloudflare-specific deployment configuration.
111
+ *
112
+ * Note: Set `target: "cloudflare"` to enable Cloudflare deployment.
113
+ * This object is only for additional configuration.
114
+ */
66
115
  cloudflare: t.optional(t.object({ config: t.optional(t.json()) })),
116
+ /**
117
+ * Docker-specific deployment configuration.
118
+ *
119
+ * Note: Set `target: "docker"` to enable Docker deployment.
120
+ * This object is only for additional configuration.
121
+ */
67
122
  docker: t.optional(t.object({
123
+ /**
124
+ * Base image for the Dockerfile (FROM instruction).
125
+ *
126
+ * @default "node:24-alpine" for node runtime
127
+ * @default "oven/bun:alpine" for bun runtime
128
+ */
68
129
  from: t.optional(t.string()),
130
+ /**
131
+ * Command to run in the Docker container.
132
+ *
133
+ * @default "node" for node runtime
134
+ * @default "bun" for bun runtime
135
+ */
69
136
  command: t.optional(t.string()),
137
+ /**
138
+ * Docker build options (used when --image flag is passed).
139
+ */
70
140
  image: t.optional(t.object({
141
+ /**
142
+ * Default image tag (name without version).
143
+ *
144
+ * Used when --image is provided without a full override:
145
+ * - `--image` → `tag:latest`
146
+ * - `--image=1.3.4` → `tag:1.3.4`
147
+ * - `--image=other/img:v1` → `other/img:v1` (full override)
148
+ *
149
+ * @example "myproject/myapp"
150
+ * @example "ghcr.io/myorg/myapp"
151
+ */
71
152
  tag: t.string(),
153
+ /**
154
+ * Additional arguments to pass to `docker build`.
155
+ *
156
+ * @example '--platform linux/amd64 --no-cache'
157
+ */
72
158
  args: t.optional(t.string()),
159
+ /**
160
+ * Auto-add OCI standard labels (revision, created, version).
161
+ *
162
+ * Adds:
163
+ * - org.opencontainers.image.revision (git commit SHA)
164
+ * - org.opencontainers.image.created (build timestamp)
165
+ * - org.opencontainers.image.version (from image tag)
166
+ */
73
167
  oci: t.optional(t.boolean())
74
168
  }))
75
169
  })),
76
- static: t.optional(t.object({ domain: t.optional(t.string()) })),
170
+ /**
171
+ * Static site deployment configuration.
172
+ *
173
+ * Note: Set `target: "static"` to enable static site generation.
174
+ */
175
+ static: t.optional(t.object({
176
+ /**
177
+ * Surge domain for deployment.
178
+ *
179
+ * If set, a CNAME file is written to dist/public/.
180
+ * If not set, a domain is auto-generated from package.json name.
181
+ *
182
+ * @example "my-app.surge.sh"
183
+ * @example "my-custom-domain.com"
184
+ */
185
+ domain: t.optional(t.string()) })),
186
+ /**
187
+ * PWA (Progressive Web App) configuration.
188
+ *
189
+ * Generates a web app manifest and enables installability.
190
+ * Requires a client-side bundle (React).
191
+ */
77
192
  pwa: t.optional(t.object({
193
+ /**
194
+ * Full application name displayed on the splash screen
195
+ * and in the OS app switcher.
196
+ */
78
197
  name: t.string(),
198
+ /**
199
+ * Short name displayed on the home screen icon.
200
+ * Falls back to `name` if omitted.
201
+ */
79
202
  shortName: t.optional(t.string()),
203
+ /**
204
+ * Theme color used for the browser toolbar and OS chrome.
205
+ *
206
+ * @default "#ffffff"
207
+ */
80
208
  themeColor: t.optional(t.string()),
209
+ /**
210
+ * Background color for the splash screen.
211
+ *
212
+ * @default "#ffffff"
213
+ */
81
214
  backgroundColor: t.optional(t.string()),
215
+ /**
216
+ * Display mode for the installed PWA.
217
+ *
218
+ * - `standalone` - Looks like a native app (default)
219
+ * - `fullscreen` - Uses entire screen (games, immersive)
220
+ * - `minimal-ui` - Like standalone with minimal browser UI
221
+ * - `browser` - Standard browser tab
222
+ *
223
+ * @default "standalone"
224
+ */
82
225
  display: t.optional(t.enum([
83
226
  "standalone",
84
227
  "fullscreen",
85
228
  "minimal-ui",
86
229
  "browser"
87
230
  ])),
231
+ /**
232
+ * Enable offline support via service worker.
233
+ *
234
+ * TODO: Not yet implemented.
235
+ */
88
236
  offline: t.optional(t.boolean())
89
237
  })),
90
- sitemap: t.optional(t.object({ hostname: t.string() }))
238
+ /**
239
+ * Sitemap generation configuration.
240
+ */
241
+ sitemap: t.optional(t.object({
242
+ /**
243
+ * Base URL for sitemap entries.
244
+ */
245
+ hostname: t.string() }))
91
246
  }),
92
247
  default: {}
93
248
  });
@@ -102,7 +257,11 @@ const buildOptions = $atom({
102
257
  const devOptions = $atom({
103
258
  name: "alepha.cli.dev.options",
104
259
  description: "Dev configuration options",
105
- schema: t.object({ noViteReactPlugin: t.optional(t.boolean({ default: false })) }),
260
+ schema: t.object({
261
+ /**
262
+ * Disable Vite React plugin.
263
+ */
264
+ noViteReactPlugin: t.optional(t.boolean({ default: false })) }),
106
265
  default: {}
107
266
  });
108
267
  //#endregion
@@ -598,7 +757,7 @@ var AlephaCliUtils = class {
598
757
  };
599
758
  //#endregion
600
759
  //#region ../../package.json
601
- var version$1 = "0.20.1";
760
+ var version$1 = "0.20.3";
602
761
  //#endregion
603
762
  //#region ../../src/cli/core/alephaPackageJson.ts
604
763
  const alephaPackageJson = {
@@ -627,18 +786,19 @@ const alephaPackageJson = {
627
786
  "drizzle-orm": "^0.45.2",
628
787
  "postgres": "^3.4.9",
629
788
  "tsx": "^4.21.0",
630
- "typebox": "^1.1.23",
631
- "typescript": "^6.0.2",
632
- "vite-bundle-analyzer": "^1.3.7",
789
+ "typebox": "^1.1.34",
790
+ "typescript": "^6.0.3",
791
+ "vite-bundle-analyzer": "^1.3.8",
633
792
  "ws": "^8.20.0"
634
793
  },
635
794
  devDependencies: {
636
- "@biomejs/biome": "^2.4.12",
637
- "@electric-sql/pglite": "^0.4.4",
795
+ "@biomejs/biome": "^2.4.13",
796
+ "@electric-sql/pglite": "^0.4.5",
638
797
  "@faker-js/faker": "^10.4.0",
798
+ "@tailwindcss/vite": "^4.2.4",
639
799
  "@testing-library/dom": "^10.4.1",
640
800
  "@testing-library/react": "^16.3.2",
641
- "@types/bun": "^1.3.12",
801
+ "@types/bun": "^1.3.13",
642
802
  "@types/node": "^25.6.0",
643
803
  "@types/nodemailer": "^8.0.0",
644
804
  "@types/react": "^19.2.14",
@@ -646,17 +806,19 @@ const alephaPackageJson = {
646
806
  "@types/ws": "^8.18.1",
647
807
  "cron-schedule": "^6.0.0",
648
808
  "drizzle-kit": "^0.31.10",
649
- "jose": "^6.2.2",
650
- "jsdom": "^29.0.2",
651
- "nodemailer": "^8.0.5",
652
- "openid-client": "^6.8.3",
809
+ "jose": "^6.2.3",
810
+ "jsdom": "^29.1.0",
811
+ "nodemailer": "^8.0.7",
812
+ "openid-client": "^6.8.4",
653
813
  "prom-client": "^15.1.3",
654
814
  "react": "^19.2.5",
655
815
  "react-dom": "^19.2.5",
656
- "swagger-ui-dist": "^5.32.4",
657
- "tsdown": "^0.21.8",
658
- "vite": "^8.0.8",
659
- "vitest": "^4.1.4"
816
+ "shadcn": "^4.6.0",
817
+ "swagger-ui-dist": "^5.32.5",
818
+ "tailwindcss": "^4.2.4",
819
+ "tsdown": "^0.21.10",
820
+ "vite": "^8.0.10",
821
+ "vitest": "^4.1.5"
660
822
  },
661
823
  peerDependencies: {
662
824
  "react": "^19",
@@ -703,16 +865,6 @@ const alephaPackageJson = {
703
865
  "import": "./src/api/files/index.ts",
704
866
  "default": "./src/api/files/index.ts"
705
867
  },
706
- "./api/invitations": {
707
- "types": "./src/api/invitations/index.ts",
708
- "import": "./src/api/invitations/index.ts",
709
- "default": "./src/api/invitations/index.ts"
710
- },
711
- "./api/issues": {
712
- "types": "./src/api/issues/index.ts",
713
- "import": "./src/api/issues/index.ts",
714
- "default": "./src/api/issues/index.ts"
715
- },
716
868
  "./api/jobs": {
717
869
  "types": "./src/api/jobs/index.ts",
718
870
  "react-native": "./src/api/jobs/index.browser.ts",
@@ -751,11 +903,6 @@ const alephaPackageJson = {
751
903
  "import": "./src/api/payments/index.ts",
752
904
  "default": "./src/api/payments/index.ts"
753
905
  },
754
- "./api/subscriptions": {
755
- "types": "./src/api/subscriptions/index.ts",
756
- "import": "./src/api/subscriptions/index.ts",
757
- "default": "./src/api/subscriptions/index.ts"
758
- },
759
906
  "./api/users": {
760
907
  "types": "./src/api/users/index.ts",
761
908
  "react-native": "./src/api/users/index.browser.ts",
@@ -770,13 +917,6 @@ const alephaPackageJson = {
770
917
  "import": "./src/api/verifications/index.ts",
771
918
  "default": "./src/api/verifications/index.ts"
772
919
  },
773
- "./api/workflows": {
774
- "types": "./src/api/workflows/index.ts",
775
- "react-native": "./src/api/workflows/index.browser.ts",
776
- "browser": "./src/api/workflows/index.browser.ts",
777
- "import": "./src/api/workflows/index.ts",
778
- "default": "./src/api/workflows/index.ts"
779
- },
780
920
  "./batch": {
781
921
  "types": "./src/batch/index.ts",
782
922
  "import": "./src/batch/index.ts",
@@ -971,6 +1111,11 @@ const alephaPackageJson = {
971
1111
  "import": "./src/react/testing/index.ts",
972
1112
  "default": "./src/react/testing/index.ts"
973
1113
  },
1114
+ "./react/ui": {
1115
+ "types": "./src/react/ui/index.ts",
1116
+ "import": "./src/react/ui/index.ts",
1117
+ "default": "./src/react/ui/index.ts"
1118
+ },
974
1119
  "./react/websocket": {
975
1120
  "types": "./src/react/websocket/index.ts",
976
1121
  "import": "./src/react/websocket/index.ts",
@@ -1302,14 +1447,14 @@ var PackageManagerUtils = class {
1302
1447
  "yarn.lock"
1303
1448
  ]);
1304
1449
  await this.editPackageJson(root, (pkg) => {
1305
- delete pkg.packageManager;
1450
+ pkg.packageManager = void 0;
1306
1451
  return pkg;
1307
1452
  });
1308
1453
  }
1309
1454
  async removePnpm(root) {
1310
1455
  await this.removeFiles(root, ["pnpm-lock.yaml", "pnpm-workspace.yaml"]);
1311
1456
  await this.editPackageJson(root, (pkg) => {
1312
- delete pkg.packageManager;
1457
+ pkg.packageManager = void 0;
1313
1458
  return pkg;
1314
1459
  });
1315
1460
  }
@@ -1359,7 +1504,7 @@ var PackageManagerUtils = class {
1359
1504
  const alephaDeps = alephaPackageJson.devDependencies;
1360
1505
  const dependencies = { alepha: `^${version}` };
1361
1506
  const devDependencies = { vite: alephaDeps.vite };
1362
- if (!(modes.react && !modes.ui)) devDependencies["drizzle-kit"] = alephaDeps["drizzle-kit"];
1507
+ if (!modes.react || modes.saas) devDependencies["drizzle-kit"] = alephaDeps["drizzle-kit"];
1363
1508
  if (!modes.isPackage) {
1364
1509
  devDependencies["@biomejs/biome"] = alephaDeps["@biomejs/biome"];
1365
1510
  if (modes.test) devDependencies.vitest = alephaDeps.vitest;
@@ -1372,14 +1517,11 @@ var PackageManagerUtils = class {
1372
1517
  verify: "alepha verify"
1373
1518
  };
1374
1519
  if (modes.test) scripts.test = "vitest run";
1375
- if (modes.ui) {
1376
- dependencies["@alepha/ui"] = `^${version}`;
1377
- modes.react = true;
1378
- }
1379
1520
  if (modes.tailwind) {
1380
- devDependencies.tailwindcss = "^4.2.0";
1381
- devDependencies["@tailwindcss/vite"] = "^4.2.0";
1521
+ devDependencies.tailwindcss = alephaDeps.tailwindcss;
1522
+ devDependencies["@tailwindcss/vite"] = alephaDeps["@tailwindcss/vite"];
1382
1523
  }
1524
+ if (modes.shadcn) devDependencies.shadcn = alephaDeps.shadcn;
1383
1525
  if (modes.react) {
1384
1526
  dependencies.react = alephaDeps.react;
1385
1527
  dependencies["react-dom"] = alephaDeps["react-dom"];
@@ -1402,11 +1544,7 @@ var PackageManagerUtils = class {
1402
1544
  //#endregion
1403
1545
  //#region ../../src/cli/core/templates/agentMd.ts
1404
1546
  const agentMd = (options) => {
1405
- const header = options.type === "claude" ? `# CLAUDE.md` : `# AGENTS.md`;
1406
- const docs = [`- Framework source: \`node_modules/alepha/src/\``];
1407
- if (options.ui) docs.push(`- UI components: \`node_modules/@alepha/ui/src/\``);
1408
- docs.push(`- Docs: https://alepha.dev/llms.txt`);
1409
- return `${header}
1547
+ return `${options.type === "claude" ? `# CLAUDE.md` : `# AGENTS.md`}
1410
1548
 
1411
1549
  This is an **Alepha** project.
1412
1550
 
@@ -1428,7 +1566,8 @@ alepha build # Build
1428
1566
 
1429
1567
  ## Documentation
1430
1568
 
1431
- ${docs.join("\n")}
1569
+ - Framework source: \`node_modules/alepha/src/\`
1570
+ - Docs: https://alepha.dev/llms.txt
1432
1571
  `.trim();
1433
1572
  };
1434
1573
  //#endregion
@@ -1460,49 +1599,6 @@ export default defineConfig({
1460
1599
  `;
1461
1600
  };
1462
1601
  //#endregion
1463
- //#region ../../src/cli/core/templates/apiAppSecurityTs.ts
1464
- const apiAppSecurityTs = (opts = {}) => {
1465
- return `
1466
- import { $realm } from "alepha/api/users";
1467
-
1468
- export class AppSecurity {
1469
- users = $realm({
1470
- settings: {
1471
- // Auto-promote these users to admin on login
1472
- adminEmails: ${opts.adminEmail ? `["${opts.adminEmail}"]` : "[]"},
1473
- adminUsernames: [],
1474
-
1475
- // Registration & login options
1476
- registrationAllowed: true,
1477
- email: "required",
1478
- username: "none",
1479
- phoneNumber: "none",
1480
-
1481
- // Verification (requires notifications feature)
1482
- verifyEmailRequired: false,
1483
- verifyPhoneRequired: false,
1484
- resetPasswordAllowed: false,
1485
- },
1486
- features: {
1487
- // Enable additional features
1488
- notifications: false,
1489
- audits: false,
1490
- apiKeys: false,
1491
- sessionPurge: false,
1492
- avatars: false,
1493
- parameters: false,
1494
- },
1495
- identities: {
1496
- // Enable authentication providers
1497
- credentials: true,
1498
- // google: true,
1499
- // github: true,
1500
- },
1501
- });
1502
- }
1503
- `.trim();
1504
- };
1505
- //#endregion
1506
1602
  //#region ../../src/cli/core/templates/apiHelloControllerTs.ts
1507
1603
  const apiHelloControllerTs = (options = {}) => {
1508
1604
  return `import { $action } from "alepha/server";
@@ -1539,21 +1635,26 @@ export type HelloResponse = Static<typeof helloResponseSchema>;
1539
1635
  //#endregion
1540
1636
  //#region ../../src/cli/core/templates/apiIndexTs.ts
1541
1637
  const apiIndexTs = (options = {}) => {
1542
- const { appName = "app", auth = false } = options;
1543
- const imports = ["import { $module } from \"alepha\";"];
1544
- const services = [];
1545
- if (auth) {
1546
- imports.push("import { AppSecurity } from \"./AppSecurity.ts\";");
1547
- services.push("AppSecurity");
1548
- }
1549
- imports.push("import { HelloController } from \"./controllers/HelloController.ts\";");
1550
- services.push("HelloController");
1638
+ const { appName = "app", saas = false } = options;
1639
+ if (saas) return `
1640
+ import { $module } from "alepha";
1641
+ import { AlephaApiUsers } from "alepha/api/users";
1642
+ import { HelloController } from "./controllers/HelloController.ts";
1643
+ import { RealmProvider } from "./providers/RealmProvider.ts";
1644
+
1645
+ export const ApiModule = $module({
1646
+ name: "${appName}.api",
1647
+ services: [HelloController, RealmProvider],
1648
+ imports: [AlephaApiUsers],
1649
+ });
1650
+ `.trim();
1551
1651
  return `
1552
- ${imports.join("\n")}
1652
+ import { $module } from "alepha";
1653
+ import { HelloController } from "./controllers/HelloController.ts";
1553
1654
 
1554
1655
  export const ApiModule = $module({
1555
1656
  name: "${appName}.api",
1556
- services: [${services.join(", ")}],
1657
+ services: [HelloController],
1557
1658
  });
1558
1659
  `.trim();
1559
1660
  };
@@ -1593,6 +1694,46 @@ const biomeJson = () => `
1593
1694
  }
1594
1695
  `.trim();
1595
1696
  //#endregion
1697
+ //#region ../../src/cli/core/templates/componentsJsonTs.ts
1698
+ /**
1699
+ * `components.json` is the shadcn CLI's project config — it tells
1700
+ * `shadcn add` where to drop primitives, which tailwind tokens to use,
1701
+ * which icon library to wire up, and which custom registries to resolve.
1702
+ *
1703
+ * Aliases follow shadcn's defaults (`@/components`, `@/lib/utils`) so the
1704
+ * CLI honors them across `init` + `add` calls. Alepha app code lives at
1705
+ * `src/web/` (Home, AppRouter, …) and the shadcn primitives live at
1706
+ * `src/components/` — kept separate to make the registry components
1707
+ * trivially upgradable via `shadcn add --overwrite`.
1708
+ *
1709
+ * The `registries` block pre-wires the public Alepha registry — consumers
1710
+ * can immediately run e.g. `shadcn add @alepha/auth-login`.
1711
+ */
1712
+ const componentsJsonTs = () => `{
1713
+ "$schema": "https://ui.shadcn.com/schema.json",
1714
+ "style": "new-york",
1715
+ "rsc": false,
1716
+ "tsx": true,
1717
+ "tailwind": {
1718
+ "config": "",
1719
+ "css": "src/main.css",
1720
+ "baseColor": "neutral",
1721
+ "cssVariables": true
1722
+ },
1723
+ "aliases": {
1724
+ "components": "@/components",
1725
+ "utils": "@/lib/utils",
1726
+ "ui": "@/components/ui",
1727
+ "lib": "@/lib",
1728
+ "hooks": "@/hooks"
1729
+ },
1730
+ "iconLibrary": "lucide",
1731
+ "registries": {
1732
+ "@alepha": "https://alepha.dev/r/{name}.json"
1733
+ }
1734
+ }
1735
+ `;
1736
+ //#endregion
1596
1737
  //#region ../../src/cli/core/templates/dummySpecTs.ts
1597
1738
  const dummySpecTs = () => `
1598
1739
  import { test, expect } from "vitest";
@@ -1725,37 +1866,6 @@ run(alepha);
1725
1866
  //#endregion
1726
1867
  //#region ../../src/cli/core/templates/mainCss.ts
1727
1868
  const mainCss = (opts = {}) => {
1728
- if (opts.ui) return `/**
1729
- * Alepha UI - Based on Mantine component library
1730
- * Mantine Docs: https://mantine.dev
1731
- * Mantine LLM context: https://mantine.dev/llms.txt
1732
- *
1733
- * Switch theme index: alepha.set(alephaThemeAtom, { index: 1 })
1734
- *
1735
- * Custom themes (in src/web/index.ts):
1736
- *
1737
- * import { alephaThemeListAtom } from "@alepha/ui";
1738
- *
1739
- * export const WebModule = $module({
1740
- * name: "app.web",
1741
- * services: [AppRouter],
1742
- * register(alepha) {
1743
- * alepha.register(AppRouter);
1744
- * alepha.set(alephaThemeListAtom, [{
1745
- * name: "My Theme",
1746
- * description: "Custom theme",
1747
- * primaryColor: "blue",
1748
- * defaultColorScheme: "dark",
1749
- * // ...MantineThemeOverride options
1750
- * }]);
1751
- * },
1752
- * });
1753
- *
1754
- * Alternatives (remove the import below):
1755
- * - Tailwind CSS: https://tailwindcss.com/docs/installation/using-vite
1756
- * - Raw CSS: Write your own styles
1757
- */
1758
- @import "@alepha/ui/styles";`;
1759
1869
  if (opts.tailwind) return `@import "tailwindcss";
1760
1870
 
1761
1871
  /* Add your styles here */
@@ -1764,8 +1874,8 @@ const mainCss = (opts = {}) => {
1764
1874
  * Global styles for your application.
1765
1875
  *
1766
1876
  * Options:
1767
- * - @alepha/ui: Use \`alepha init --ui\` to add Mantine-based components
1768
1877
  * - Tailwind CSS: Use \`alepha init --tailwind\` to add Tailwind CSS
1878
+ * - shadcn/ui: Use \`alepha init --shadcn\` to add shadcn/ui setup
1769
1879
  * - Raw CSS: Write your own styles below
1770
1880
  */
1771
1881
 
@@ -1796,6 +1906,225 @@ run(alepha);
1796
1906
  `.trim();
1797
1907
  };
1798
1908
  //#endregion
1909
+ //#region ../../src/cli/core/templates/saasAdminLayoutTsx.ts
1910
+ /**
1911
+ * SaaS admin layout — full AppShell on /admin with a sidebar, breadcrumbs,
1912
+ * a Sonner toaster, and a confirm provider. The page list grows with
1913
+ * whatever `admin-*` registry components the user adds.
1914
+ *
1915
+ * All UI primitives come from `src/components/*` where `shadcn add` drops
1916
+ * them; alepha app code lives in `src/web/*` and references them via the
1917
+ * `@/components/*` alias.
1918
+ */
1919
+ const saasAdminLayoutTsx = () => `import { AppShell } from "@/components/app-shell";
1920
+ import { Toaster } from "@/components/ui/sonner";
1921
+ import { TooltipProvider } from "@/components/ui/tooltip";
1922
+ import { ConfirmProvider } from "@/components/use-confirm";
1923
+ import { NestedView, useRouterState } from "alepha/react/router";
1924
+ import { ShieldCheck, Users } from "lucide-react";
1925
+
1926
+ const NAV = [
1927
+ {
1928
+ label: "Identity",
1929
+ items: [
1930
+ { href: "/admin/users", label: "Users", icon: Users },
1931
+ { href: "/admin/sessions", label: "Sessions", icon: ShieldCheck },
1932
+ ],
1933
+ },
1934
+ ] as const;
1935
+
1936
+ const findCrumbs = (pathname: string): { label: string; href?: string }[] => {
1937
+ for (const group of NAV) {
1938
+ const match = group.items.find((it) => it.href === pathname);
1939
+ if (match) return [{ label: group.label }, { label: match.label }];
1940
+ }
1941
+ return [];
1942
+ };
1943
+
1944
+ const AdminLayout = () => {
1945
+ const state = useRouterState();
1946
+ const crumbs = findCrumbs(state.url.pathname);
1947
+
1948
+ return (
1949
+ <TooltipProvider>
1950
+ <ConfirmProvider>
1951
+ <AppShell
1952
+ brand={
1953
+ <a
1954
+ href="/admin"
1955
+ className="flex items-center gap-2 px-2 py-2 font-semibold group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:px-0"
1956
+ >
1957
+ <span className="bg-primary text-primary-foreground flex size-7 shrink-0 items-center justify-center rounded">
1958
+ α
1959
+ </span>
1960
+ <span className="truncate group-data-[collapsible=icon]:hidden">
1961
+ Admin
1962
+ </span>
1963
+ </a>
1964
+ }
1965
+ nav={NAV.map((group) => ({
1966
+ label: group.label,
1967
+ items: group.items.map((it) => ({
1968
+ href: it.href,
1969
+ label: it.label,
1970
+ icon: it.icon,
1971
+ active: it.href === state.url.pathname,
1972
+ })),
1973
+ }))}
1974
+ breadcrumbs={crumbs.length ? crumbs : undefined}
1975
+ >
1976
+ <NestedView />
1977
+ </AppShell>
1978
+ <Toaster />
1979
+ </ConfirmProvider>
1980
+ </TooltipProvider>
1981
+ );
1982
+ };
1983
+
1984
+ export default AdminLayout;
1985
+ `;
1986
+ //#endregion
1987
+ //#region ../../src/cli/core/templates/saasAdminPagesTsx.ts
1988
+ /**
1989
+ * Admin pages — each is a thin wrapper around the matching `admin-*`
1990
+ * registry component (placed at `src/components/admin/*`). The starter
1991
+ * ships with Users + Sessions; add more by `shadcn add @alepha/admin-…`
1992
+ * and a matching `$page(...)` in AppRouter.
1993
+ */
1994
+ const saasAdminUsersTsx = () => `import { AdminUsers } from "@/components/admin/admin-users";
1995
+
1996
+ const AdminUsersPage = () => {
1997
+ return <AdminUsers />;
1998
+ };
1999
+
2000
+ export default AdminUsersPage;
2001
+ `;
2002
+ const saasAdminSessionsTsx = () => `import { AdminSessions } from "@/components/admin/admin-sessions";
2003
+
2004
+ const AdminSessionsPage = () => {
2005
+ return <AdminSessions />;
2006
+ };
2007
+
2008
+ export default AdminSessionsPage;
2009
+ `;
2010
+ //#endregion
2011
+ //#region ../../src/cli/core/templates/saasAuthLayoutTsx.ts
2012
+ /**
2013
+ * SaaS auth layout — wraps every /auth/* page with a centered card.
2014
+ * Routes (login, register, reset-password, verify-email) are mounted as
2015
+ * children so they share this shell.
2016
+ */
2017
+ const saasAuthLayoutTsx = () => `import { NestedView } from "alepha/react/router";
2018
+
2019
+ const AuthLayout = () => {
2020
+ return (
2021
+ <div className="bg-background flex min-h-screen items-center justify-center p-4">
2022
+ <div className="w-full max-w-md">
2023
+ <NestedView />
2024
+ </div>
2025
+ </div>
2026
+ );
2027
+ };
2028
+
2029
+ export default AuthLayout;
2030
+ `;
2031
+ //#endregion
2032
+ //#region ../../src/cli/core/templates/saasAuthPagesTsx.ts
2033
+ /**
2034
+ * Per-page wrapper around the registry components. The registry component
2035
+ * receives the realm config from the page loader; the page itself stays a
2036
+ * thin shell so apps can layer their branding around it.
2037
+ *
2038
+ * Registry components land at `src/components/auth/*` (shadcn defaults).
2039
+ */
2040
+ const saasAuthLoginTsx = () => `import { AuthLogin } from "@/components/auth/auth-login";
2041
+ import type { RealmConfig } from "alepha/api/users";
2042
+
2043
+ export interface AuthLoginPageProps {
2044
+ realmConfig: RealmConfig;
2045
+ }
2046
+
2047
+ const AuthLoginPage = (props: AuthLoginPageProps) => {
2048
+ return <AuthLogin realmConfig={props.realmConfig} />;
2049
+ };
2050
+
2051
+ export default AuthLoginPage;
2052
+ `;
2053
+ const saasAuthRegisterTsx = () => `import { AuthRegister } from "@/components/auth/auth-register";
2054
+ import type { RealmConfig } from "alepha/api/users";
2055
+
2056
+ export interface AuthRegisterPageProps {
2057
+ realmConfig: RealmConfig;
2058
+ }
2059
+
2060
+ const AuthRegisterPage = (props: AuthRegisterPageProps) => {
2061
+ return <AuthRegister realmConfig={props.realmConfig} />;
2062
+ };
2063
+
2064
+ export default AuthRegisterPage;
2065
+ `;
2066
+ const saasAuthResetPasswordTsx = () => `import { AuthResetPassword } from "@/components/auth/auth-reset-password";
2067
+ import type { RealmConfig } from "alepha/api/users";
2068
+
2069
+ export interface AuthResetPasswordPageProps {
2070
+ realmConfig: RealmConfig;
2071
+ }
2072
+
2073
+ const AuthResetPasswordPage = (props: AuthResetPasswordPageProps) => {
2074
+ return <AuthResetPassword realmConfig={props.realmConfig} />;
2075
+ };
2076
+
2077
+ export default AuthResetPasswordPage;
2078
+ `;
2079
+ const saasAuthVerifyEmailTsx = () => `import { AuthVerifyEmail } from "@/components/auth/auth-verify-email";
2080
+
2081
+ const AuthVerifyEmailPage = () => {
2082
+ return <AuthVerifyEmail />;
2083
+ };
2084
+
2085
+ export default AuthVerifyEmailPage;
2086
+ `;
2087
+ //#endregion
2088
+ //#region ../../src/cli/core/templates/saasRealmProviderTs.ts
2089
+ /**
2090
+ * Realm provider scaffolded by `alepha init --saas`.
2091
+ *
2092
+ * Minimal hello-world setup: credentials login with email, one admin seeded
2093
+ * with the developer's git email at scaffold time, and an `admin:ui`
2094
+ * permission used by the AppRouter to gate `/admin/*`. The default `admin`
2095
+ * role grants `*` (so it inherits `admin:ui`); the default `user` role
2096
+ * excludes `admin:*` (so non-admins get a 403 before the shell renders).
2097
+ *
2098
+ * Add `$env`, more permissions, or stricter settings as the project grows.
2099
+ */
2100
+ const saasRealmProviderTs = (options = {}) => {
2101
+ const adminEmail = options.adminEmail ?? "admin@example.com";
2102
+ return `import { $realm } from "alepha/api/users";
2103
+ import { $permission } from "alepha/security";
2104
+
2105
+ export class RealmProvider {
2106
+ /**
2107
+ * Permission required to open the admin UI. Wired into AppRouter.adminLayout
2108
+ * via \`$secure({ permissions: ["admin:ui"] })\`.
2109
+ */
2110
+ adminUi = $permission({
2111
+ group: "admin",
2112
+ name: "ui",
2113
+ description: "Access to the admin UI shell",
2114
+ });
2115
+
2116
+ realm = $realm({
2117
+ settings: {
2118
+ adminEmails: [${JSON.stringify(adminEmail)}],
2119
+ },
2120
+ identities: {
2121
+ credentials: true,
2122
+ },
2123
+ });
2124
+ }
2125
+ `;
2126
+ };
2127
+ //#endregion
1799
2128
  //#region ../../src/cli/core/templates/tsconfigJson.ts
1800
2129
  const tsconfigJson = () => `
1801
2130
  {
@@ -1819,89 +2148,42 @@ export default defineConfig({
1819
2148
  `;
1820
2149
  };
1821
2150
  //#endregion
1822
- //#region ../../src/cli/core/templates/webAdminDashboardTsx.ts
1823
- const webAdminDashboardTsx = () => {
1824
- return `import { Flex, Text } from "@alepha/ui";
1825
-
1826
- const AdminDashboard = () => {
1827
- return (
1828
- <Flex direction="column" align="center" justify="center" mih="60vh" gap="md">
1829
- <Text size="xl" fw={600}>
1830
- Admin Panel
1831
- </Text>
1832
- <Text c="dimmed">Welcome to the admin panel.</Text>
1833
- </Flex>
1834
- );
1835
- };
2151
+ //#region ../../src/cli/core/templates/vitestConfigTs.ts
2152
+ /**
2153
+ * Minimal Vitest config for a freshly scaffolded Alepha project.
2154
+ *
2155
+ * Defines `test.root` explicitly so Vitest doesn't walk up the directory
2156
+ * tree and inherit a parent monorepo config (which can pull in unrelated
2157
+ * setup files, database connections, etc.).
2158
+ */
2159
+ const vitestConfigTs = () => `import { defineConfig } from "vitest/config";
1836
2160
 
1837
- export default AdminDashboard;
2161
+ export default defineConfig({
2162
+ test: {
2163
+ root: ".",
2164
+ globals: true,
2165
+ },
2166
+ });
1838
2167
  `;
1839
- };
1840
2168
  //#endregion
1841
2169
  //#region ../../src/cli/core/templates/webAppRouterTs.ts
1842
2170
  const webAppRouterTs = (options) => {
1843
- const imports = [];
2171
+ if (options.saas) return saasAppRouterTs();
2172
+ return basicAppRouterTs(options);
2173
+ };
2174
+ const basicAppRouterTs = (options) => {
2175
+ const imports = ["import { $page } from \"alepha/react/router\";"];
1844
2176
  const classMembers = [];
1845
- if (options.ui) imports.push("import { $ui } from \"@alepha/ui\";");
1846
- if (options.auth) imports.push("import { $uiAuth } from \"@alepha/ui/auth\";");
1847
- if (options.admin) {
1848
- imports.push("import { $uiAdmin } from \"@alepha/ui/admin\";");
1849
- imports.push("import { AdminUserRouter } from \"@alepha/ui/admin-users\";");
1850
- imports.push("import { AdminSessionRouter } from \"@alepha/ui/admin-sessions\";");
1851
- imports.push("import { $inject } from \"alepha\";");
1852
- imports.push("import { IconLayoutDashboard, IconUsers } from \"@tabler/icons-react\";");
1853
- }
1854
- imports.push("import { $page } from \"alepha/react/router\";");
1855
2177
  if (options.api) {
1856
2178
  imports.push("import { $client } from \"alepha/server/links\";");
1857
2179
  imports.push("import type { HelloController } from \"../api/controllers/HelloController.ts\";");
1858
2180
  classMembers.push(" api = $client<HelloController>();");
1859
- }
1860
- if (options.ui) {
1861
- classMembers.push(" ui = $ui();");
1862
- if (options.auth) classMembers.push(" uiAuth = $uiAuth();");
1863
- if (options.admin) classMembers.push(` // ── Admin Dashboard ─────────────────────────────
1864
- // Defined first so it can be referenced by the admin panel below.
1865
- // \`$uiAdmin\` auto-parents listed pages under the admin layout.
1866
- adminDashboard = $page({
1867
- path: "/",
1868
- label: "Dashboard",
1869
- icon: IconLayoutDashboard,
1870
- lazy: () => import("./components/AdminDashboard.tsx"),
1871
- });
1872
-
1873
- // ── Admin Domain Routers ──────────────────────────
1874
- protected users = $inject(AdminUserRouter);
1875
- protected sessions = $inject(AdminSessionRouter);
1876
-
1877
- // ── Admin Panel ─────────────────────────────────
1878
- admin = $uiAdmin({
1879
- pages: [
1880
- this.adminDashboard,
1881
- this.users.adminUsers,
1882
- this.users.adminUserLayout,
1883
- this.sessions.adminSessions,
1884
- ],
1885
- sidebarItems: [
1886
- this.adminDashboard,
1887
- {
1888
- label: "Identity",
1889
- icon: IconUsers,
1890
- children: [this.users.adminUsers, this.sessions.adminSessions],
1891
- },
1892
- ],
1893
- });`);
1894
- classMembers.push(` layout = $page({
1895
- parent: this.ui.root,
1896
- children: () => [this.home],
1897
- });`);
1898
- }
1899
- if (options.api) classMembers.push(` home = $page({
2181
+ classMembers.push(` home = $page({
1900
2182
  path: "/",
1901
2183
  lazy: () => import("./components/Home.tsx"),
1902
2184
  loader: () => this.api.hello(),
1903
2185
  });`);
1904
- else classMembers.push(` home = $page({
2186
+ } else classMembers.push(` home = $page({
1905
2187
  path: "/",
1906
2188
  lazy: () => import("./components/Home.tsx"),
1907
2189
  });`);
@@ -1911,6 +2193,95 @@ export class AppRouter {
1911
2193
  ${classMembers.join("\n\n")}
1912
2194
  }`;
1913
2195
  };
2196
+ /**
2197
+ * SaaS router wires three trees onto the app:
2198
+ * / → Home
2199
+ * /auth/* → AuthLayout + login / register / reset / verify
2200
+ * /admin/* → AdminLayout + users / sessions / api-keys / parameters / audits
2201
+ *
2202
+ * Each auth page resolves the realm config from its loader, so the registry
2203
+ * components render with everything they need on first paint.
2204
+ */
2205
+ const saasAppRouterTs = () => `import type { RealmController } from "alepha/api/users";
2206
+ import { $page, NotFound } from "alepha/react/router";
2207
+ import { $secure } from "alepha/security";
2208
+ import { $client } from "alepha/server/links";
2209
+ import type { HelloController } from "../api/controllers/HelloController.ts";
2210
+
2211
+ export class AppRouter {
2212
+ protected readonly api = $client<HelloController>();
2213
+ protected readonly realmApi = $client<RealmController>();
2214
+
2215
+ home = $page({
2216
+ path: "/",
2217
+ lazy: () => import("./components/Home.tsx"),
2218
+ loader: () => this.api.hello(),
2219
+ });
2220
+
2221
+ // ── /auth — login, register, reset, verify ─────────────────────────────
2222
+ authLayout = $page({
2223
+ path: "/auth",
2224
+ lazy: () => import("./components/auth/AuthLayout.tsx"),
2225
+ });
2226
+
2227
+ login = $page({
2228
+ parent: this.authLayout,
2229
+ path: "/login",
2230
+ name: "login",
2231
+ head: { title: "Sign in" },
2232
+ lazy: () => import("./components/auth/Login.tsx"),
2233
+ loader: async () => ({ realmConfig: await this.realmApi.getRealmConfig() }),
2234
+ });
2235
+
2236
+ register = $page({
2237
+ parent: this.authLayout,
2238
+ path: "/register",
2239
+ name: "register",
2240
+ head: { title: "Sign up" },
2241
+ lazy: () => import("./components/auth/Register.tsx"),
2242
+ loader: async () => ({ realmConfig: await this.realmApi.getRealmConfig() }),
2243
+ });
2244
+
2245
+ resetPassword = $page({
2246
+ parent: this.authLayout,
2247
+ path: "/reset-password",
2248
+ head: { title: "Reset password" },
2249
+ lazy: () => import("./components/auth/ResetPassword.tsx"),
2250
+ loader: async () => ({ realmConfig: await this.realmApi.getRealmConfig() }),
2251
+ });
2252
+
2253
+ verifyEmail = $page({
2254
+ parent: this.authLayout,
2255
+ path: "/verify-email",
2256
+ head: { title: "Verify email" },
2257
+ lazy: () => import("./components/auth/VerifyEmail.tsx"),
2258
+ });
2259
+
2260
+ // ── /admin — gated by 'admin:ui' permission, declared in RealmProvider.
2261
+ // Children inherit the gate via the parent chain.
2262
+ adminLayout = $page({
2263
+ path: "/admin",
2264
+ use: [$secure({ permissions: ["admin:ui"] })],
2265
+ lazy: () => import("./components/admin/AdminLayout.tsx"),
2266
+ });
2267
+
2268
+ adminUsers = $page({
2269
+ parent: this.adminLayout,
2270
+ path: "/users",
2271
+ head: { title: "Users" },
2272
+ lazy: () => import("./components/admin/Users.tsx"),
2273
+ });
2274
+
2275
+ adminSessions = $page({
2276
+ parent: this.adminLayout,
2277
+ path: "/sessions",
2278
+ head: { title: "Sessions" },
2279
+ lazy: () => import("./components/admin/Sessions.tsx"),
2280
+ });
2281
+
2282
+ notFound = $page({ path: "/*", component: NotFound });
2283
+ }
2284
+ `;
1914
2285
  //#endregion
1915
2286
  //#region ../../src/cli/core/templates/webHomeComponentTsx.ts
1916
2287
  const webHomeComponentTsx = (options = {}) => {
@@ -1939,7 +2310,19 @@ export default Home;
1939
2310
  //#endregion
1940
2311
  //#region ../../src/cli/core/templates/webIndexTs.ts
1941
2312
  const webIndexTs = (options = {}) => {
1942
- const { appName = "app" } = options;
2313
+ const { appName = "app", saas = false } = options;
2314
+ if (saas) return `
2315
+ import { $module } from "alepha";
2316
+ import { AlephaReactAuth } from "alepha/react/auth";
2317
+ import { AlephaReactI18n } from "alepha/react/i18n";
2318
+ import { AppRouter } from "./AppRouter.ts";
2319
+
2320
+ export const WebModule = $module({
2321
+ name: "${appName}.web",
2322
+ services: [AppRouter],
2323
+ imports: [AlephaReactAuth, AlephaReactI18n],
2324
+ });
2325
+ `.trim();
1943
2326
  return `
1944
2327
  import { $module } from "alepha";
1945
2328
  import { AppRouter } from "./AppRouter.ts";
@@ -1965,6 +2348,7 @@ var ProjectScaffolder = class {
1965
2348
  log = $logger();
1966
2349
  colors = $inject(ConsoleColorProvider);
1967
2350
  fs = $inject(FileSystemProvider);
2351
+ shell = $inject(ShellProvider);
1968
2352
  pm = $inject(PackageManagerUtils);
1969
2353
  utils = $inject(AlephaCliUtils);
1970
2354
  /**
@@ -1986,7 +2370,10 @@ var ProjectScaffolder = class {
1986
2370
  const force = opts.force ?? false;
1987
2371
  const checkWorkspace = opts.checkWorkspace ?? false;
1988
2372
  if (opts.packageJson) tasks.push(this.pm.ensurePackageJson(root, typeof opts.packageJson === "boolean" ? {} : opts.packageJson).then(() => {}));
1989
- if (opts.tsconfigJson) tasks.push(this.ensureTsConfig(root, { force }));
2373
+ if (opts.tsconfigJson) tasks.push(this.ensureTsConfig(root, {
2374
+ force,
2375
+ localOnly: opts.tsconfigJson === "local"
2376
+ }));
1990
2377
  if (opts.biomeJson) tasks.push(this.ensureBiomeConfig(root, {
1991
2378
  force,
1992
2379
  checkWorkspace
@@ -2002,7 +2389,8 @@ var ProjectScaffolder = class {
2002
2389
  await Promise.all(tasks);
2003
2390
  }
2004
2391
  async ensureTsConfig(root, opts = {}) {
2005
- if (!opts.force && await this.existsInParents(root, "tsconfig.json")) return;
2392
+ const exists = opts.localOnly ? await this.fs.exists(this.fs.join(root, "tsconfig.json")) : await this.existsInParents(root, "tsconfig.json");
2393
+ if (!opts.force && exists) return;
2006
2394
  await this.fs.writeFile(this.fs.join(root, "tsconfig.json"), tsconfigJson());
2007
2395
  }
2008
2396
  async ensureBiomeConfig(root, opts = {}) {
@@ -2063,11 +2451,30 @@ var ProjectScaffolder = class {
2063
2451
  await this.fs.mkdir(this.fs.join(root, "src/api/schemas"), { recursive: true });
2064
2452
  await this.ensureFile(root, "src/api/index.ts", apiIndexTs({
2065
2453
  appName,
2066
- auth: opts.auth
2454
+ saas: opts.saas
2067
2455
  }), opts.force);
2068
2456
  await this.ensureFile(root, "src/api/controllers/HelloController.ts", apiHelloControllerTs({ appName }), opts.force);
2069
2457
  await this.ensureFile(root, "src/api/schemas/helloResponseSchema.ts", apiHelloResponseSchemaTs(), opts.force);
2070
- if (opts.auth) await this.ensureFile(root, "src/api/AppSecurity.ts", apiAppSecurityTs({ adminEmail: opts.adminEmail }), opts.force);
2458
+ if (opts.saas) {
2459
+ await this.fs.mkdir(this.fs.join(root, "src/api/providers"), { recursive: true });
2460
+ const adminEmail = await this.detectGitEmail();
2461
+ await this.ensureFile(root, "src/api/providers/RealmProvider.ts", saasRealmProviderTs({ adminEmail }), opts.force);
2462
+ }
2463
+ }
2464
+ /**
2465
+ * Best-effort lookup for the developer's git email (used as the seeded
2466
+ * `adminEmails` entry in the SaaS realm). Returns undefined if git isn't
2467
+ * available or if `user.email` isn't configured — the template falls back
2468
+ * to `admin@example.com` in that case.
2469
+ */
2470
+ async detectGitEmail() {
2471
+ try {
2472
+ const email = (await this.shell.run("git config --get user.email", { capture: true }) ?? "").trim();
2473
+ if (!email || !email.includes("@")) return void 0;
2474
+ return email;
2475
+ } catch {
2476
+ return;
2477
+ }
2071
2478
  }
2072
2479
  /**
2073
2480
  * Ensure web/React project structure exists.
@@ -2080,30 +2487,46 @@ var ProjectScaffolder = class {
2080
2487
  async ensureWebProject(root, opts = {}) {
2081
2488
  const appName = this.getAppName(root);
2082
2489
  await this.fs.mkdir(this.fs.join(root, "src/web/components"), { recursive: true });
2490
+ if (opts.saas) {
2491
+ await this.fs.mkdir(this.fs.join(root, "src/web/components/auth"), { recursive: true });
2492
+ await this.fs.mkdir(this.fs.join(root, "src/web/components/admin"), { recursive: true });
2493
+ }
2083
2494
  await this.fs.mkdir(this.fs.join(root, "public"), { recursive: true });
2084
2495
  await this.ensureFile(root, "public/favicon.svg", logoSvg, opts.force);
2085
- await this.ensureFile(root, "src/main.css", mainCss({
2086
- ui: opts.ui,
2087
- tailwind: opts.tailwind
2088
- }), opts.force);
2496
+ await this.ensureFile(root, "src/main.css", mainCss({ tailwind: opts.tailwind }), opts.force);
2089
2497
  if (opts.tailwind) await this.ensureFile(root, "vite.config.ts", viteConfigTs(), opts.force);
2090
- await this.ensureFile(root, "src/web/index.ts", webIndexTs({ appName }), opts.force);
2498
+ if (opts.shadcn) await this.ensureFile(root, "components.json", componentsJsonTs(), opts.force);
2499
+ await this.ensureFile(root, "src/web/index.ts", webIndexTs({
2500
+ appName,
2501
+ saas: opts.saas
2502
+ }), opts.force);
2091
2503
  await this.ensureFile(root, "src/web/AppRouter.ts", webAppRouterTs({
2092
2504
  api: opts.api,
2093
- ui: opts.ui,
2094
- auth: opts.auth,
2095
- admin: opts.admin
2505
+ saas: opts.saas
2096
2506
  }), opts.force);
2097
2507
  await this.ensureFile(root, "src/web/components/Home.tsx", webHomeComponentTsx({ api: opts.api }), opts.force);
2098
- if (opts.admin) await this.ensureFile(root, "src/web/components/AdminDashboard.tsx", webAdminDashboardTsx(), opts.force);
2099
2508
  await this.ensureFile(root, "src/main.browser.ts", mainBrowserTs(), opts.force);
2509
+ if (opts.saas) {
2510
+ await this.ensureFile(root, "src/web/components/auth/AuthLayout.tsx", saasAuthLayoutTsx(), opts.force);
2511
+ await this.ensureFile(root, "src/web/components/auth/Login.tsx", saasAuthLoginTsx(), opts.force);
2512
+ await this.ensureFile(root, "src/web/components/auth/Register.tsx", saasAuthRegisterTsx(), opts.force);
2513
+ await this.ensureFile(root, "src/web/components/auth/ResetPassword.tsx", saasAuthResetPasswordTsx(), opts.force);
2514
+ await this.ensureFile(root, "src/web/components/auth/VerifyEmail.tsx", saasAuthVerifyEmailTsx(), opts.force);
2515
+ await this.ensureFile(root, "src/web/components/admin/AdminLayout.tsx", saasAdminLayoutTsx(), opts.force);
2516
+ await this.ensureFile(root, "src/web/components/admin/Users.tsx", saasAdminUsersTsx(), opts.force);
2517
+ await this.ensureFile(root, "src/web/components/admin/Sessions.tsx", saasAdminSessionsTsx(), opts.force);
2518
+ }
2100
2519
  }
2101
2520
  /**
2102
- * Ensure test directory exists with a dummy test file.
2521
+ * Ensure test directory exists with a dummy test file + a self-contained
2522
+ * `vitest.config.ts`. Pinning `test.root` prevents Vitest from walking up
2523
+ * to a parent monorepo config (e.g. one that boots a Postgres container).
2103
2524
  */
2104
2525
  async ensureTestDir(root) {
2105
2526
  const testDir = this.fs.join(root, "test");
2106
2527
  const dummyPath = this.fs.join(testDir, "dummy.spec.ts");
2528
+ const vitestConfigPath = this.fs.join(root, "vitest.config.ts");
2529
+ if (!await this.fs.exists(vitestConfigPath)) await this.fs.writeFile(vitestConfigPath, vitestConfigTs());
2107
2530
  if (!await this.fs.exists(testDir)) {
2108
2531
  await this.fs.mkdir(testDir, { recursive: true });
2109
2532
  await this.fs.writeFile(dummyPath, dummySpecTs());
@@ -2119,20 +2542,23 @@ var ProjectScaffolder = class {
2119
2542
  root = this.fs.join(root, args);
2120
2543
  await this.fs.mkdir(root, { force: true });
2121
2544
  }
2122
- if (flags.saas) {
2123
- flags.api = true;
2124
- flags.ui = true;
2545
+ const shadcnPreset = typeof flags.saas === "string" && flags.saas || typeof flags.shadcn === "string" && flags.shadcn || "b0";
2546
+ const f = flags;
2547
+ f.shadcn = !!flags.shadcn;
2548
+ f.saas = !!flags.saas;
2549
+ if (f.saas) {
2550
+ f.shadcn = true;
2551
+ f.api = true;
2125
2552
  }
2126
- if (flags.ui) flags.react = true;
2553
+ if (f.shadcn) f.tailwind = true;
2127
2554
  if (flags.tailwind) flags.react = true;
2128
- if ((flags.saas || flags.api || flags.ui || flags.react || flags.tailwind) && !flags.force) {
2555
+ if ((flags.api || flags.react || flags.tailwind || flags.shadcn || flags.saas) && !flags.force) {
2129
2556
  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.`);
2130
2557
  }
2131
2558
  const workspace = await this.pm.getWorkspaceContext(root);
2132
2559
  let agentType = false;
2133
2560
  if (!workspace.isPackage) agentType = await this.utils.isInstalledAsync("claude") ? "claude" : "agents";
2134
2561
  const isExpo = await this.pm.hasExpo(root);
2135
- const adminEmail = flags.saas ? await this.utils.getGitEmail() : void 0;
2136
2562
  const force = !!flags.force;
2137
2563
  await run({
2138
2564
  name: "ensuring configuration files",
@@ -2140,16 +2566,13 @@ var ProjectScaffolder = class {
2140
2566
  await this.ensureConfig(root, {
2141
2567
  force,
2142
2568
  packageJson: {
2143
- ...flags,
2569
+ ...f,
2144
2570
  isPackage: workspace.isPackage
2145
2571
  },
2146
- tsconfigJson: !workspace.config.tsconfigJson,
2572
+ tsconfigJson: f.shadcn ? "local" : !workspace.config.tsconfigJson,
2147
2573
  biomeJson: true,
2148
2574
  editorconfig: !workspace.config.editorconfig,
2149
- agentMd: agentType ? {
2150
- type: agentType,
2151
- ui: !!flags.ui
2152
- } : false
2575
+ agentMd: agentType ? { type: agentType } : false
2153
2576
  });
2154
2577
  await this.ensureAlephaConfig(root, { force });
2155
2578
  await this.ensureMainServerTs(root, {
@@ -2158,16 +2581,14 @@ var ProjectScaffolder = class {
2158
2581
  force
2159
2582
  });
2160
2583
  if (flags.api) await this.ensureApiProject(root, {
2161
- auth: !!flags.saas,
2162
- adminEmail,
2584
+ saas: !!flags.saas,
2163
2585
  force
2164
2586
  });
2165
2587
  if (flags.react && !isExpo) await this.ensureWebProject(root, {
2166
2588
  api: !!flags.api,
2167
- ui: !!flags.ui,
2168
- auth: !!flags.saas,
2169
- admin: !!flags.saas,
2170
2589
  tailwind: !!flags.tailwind,
2590
+ shadcn: !!flags.shadcn,
2591
+ saas: !!flags.saas,
2171
2592
  force
2172
2593
  });
2173
2594
  }
@@ -2185,10 +2606,26 @@ var ProjectScaffolder = class {
2185
2606
  root: installRoot
2186
2607
  });
2187
2608
  if (flags.test) await this.ensureTestDir(root);
2188
- await run(`${pmName} run lint`, {
2189
- alias: "running linter",
2609
+ const exec = pmExecPrefix(pmName);
2610
+ if (flags.shadcn) {
2611
+ await run(`${exec} shadcn init --no-monorepo --base radix -t vite --yes --force --reinstall --preset ${escapeShellArg(shadcnPreset)}`, {
2612
+ alias: `running shadcn init (preset ${shadcnPreset})`,
2613
+ root
2614
+ });
2615
+ await this.fs.writeFile(this.fs.join(root, "components.json"), componentsJsonTs());
2616
+ }
2617
+ if (flags.saas) await run(`${exec} shadcn add @alepha/saas --yes --overwrite`, {
2618
+ alias: "adding alepha saas registry bundle",
2190
2619
  root
2191
2620
  });
2621
+ try {
2622
+ await run(`${pmName} run lint`, {
2623
+ alias: "running linter",
2624
+ root
2625
+ });
2626
+ } catch (err) {
2627
+ this.log.warn("Linter reported issues during init — continuing. Run `lint` again later to inspect.", { error: err instanceof Error ? err.message : String(err) });
2628
+ }
2192
2629
  if (!workspace.isPackage) {
2193
2630
  if (await this.ensureGitRepo(root, { force })) await run("git add .", {
2194
2631
  alias: "staging generated files",
@@ -2205,11 +2642,6 @@ var ProjectScaffolder = class {
2205
2642
  this.log.info("");
2206
2643
  this.log.info(` ${c.set("GREY_DARK", "$")} cd ${c.set("CYAN", projectName)}`);
2207
2644
  this.log.info(` ${c.set("GREY_DARK", "$")} ${c.set("CYAN", `${pmRun} dev`)}`);
2208
- if (adminEmail) {
2209
- this.log.info("");
2210
- this.log.info(` Admin email: ${c.set("GREEN", adminEmail)}`);
2211
- this.log.info(` ${c.set("GREY_DARK", "(from git config, change in src/api/AppSecurity.ts)")}`);
2212
- }
2213
2645
  this.log.info("");
2214
2646
  }
2215
2647
  /**
@@ -2232,6 +2664,30 @@ var ProjectScaffolder = class {
2232
2664
  }
2233
2665
  }
2234
2666
  };
2667
+ /**
2668
+ * Map a package manager name to the command that runs a project-local binary.
2669
+ *
2670
+ * - npm: `npx`
2671
+ * - yarn: `yarn` (yarn auto-resolves binary names; `yarn shadcn ...` works)
2672
+ * - pnpm: `pnpm exec`
2673
+ * - bun: `bunx`
2674
+ *
2675
+ * Used to invoke `shadcn init` / `shadcn add` regardless of the user's PM —
2676
+ * `npm shadcn ...` is invalid (it tries to run a script named `shadcn`).
2677
+ */
2678
+ /** Quote a value so it survives shell parsing. */
2679
+ const escapeShellArg = (value) => {
2680
+ if (/^[A-Za-z0-9_./@:-]+$/.test(value)) return value;
2681
+ return `'${value.replace(/'/g, "'\\''")}'`;
2682
+ };
2683
+ const pmExecPrefix = (pmName) => {
2684
+ switch (pmName) {
2685
+ case "npm": return "npx";
2686
+ case "pnpm": return "pnpm exec";
2687
+ case "bun": return "bunx";
2688
+ default: return "yarn";
2689
+ }
2690
+ };
2235
2691
  //#endregion
2236
2692
  //#region ../../src/cli/core/tasks/BuildTask.ts
2237
2693
  /**
@@ -2643,7 +3099,7 @@ var BuildCompressTask = class extends BuildTask {
2643
3099
  *
2644
3100
  * Creates:
2645
3101
  * - Dockerfile with configurable base image
2646
- * - Copies drizzle migrations if they exist
3102
+ * - Copies migrations directory if it exists
2647
3103
  * - Builds Docker image when `--image` flag is provided
2648
3104
  */
2649
3105
  var BuildDockerTask = class extends BuildTask {
@@ -2658,15 +3114,15 @@ var BuildDockerTask = class extends BuildTask {
2658
3114
  await ctx.run({
2659
3115
  name: "generate deploy config (docker)",
2660
3116
  handler: async () => {
2661
- await this.copyDrizzleMigrations(ctx.root, distDir);
3117
+ await this.copyMigrations(ctx.root, distDir);
2662
3118
  await this.writeDockerfile(ctx.root, distDir, dockerFrom, dockerCommand);
2663
3119
  }
2664
3120
  });
2665
3121
  if (ctx.flags?.image) await this.buildDockerImage(ctx, distDir);
2666
3122
  }
2667
- async copyDrizzleMigrations(root, distDir) {
2668
- const drizzleDir = this.fs.join(root, "drizzle");
2669
- if (await this.fs.exists(drizzleDir)) await this.fs.cp(drizzleDir, this.fs.join(root, distDir, "drizzle"));
3123
+ async copyMigrations(root, distDir) {
3124
+ const migrationsDir = this.fs.join(root, "migrations");
3125
+ if (await this.fs.exists(migrationsDir)) await this.fs.cp(migrationsDir, this.fs.join(root, distDir, "migrations"));
2670
3126
  }
2671
3127
  async writeDockerfile(root, distDir, image, command) {
2672
3128
  const dockerfile = `# This file was automatically generated. DO NOT MODIFY.
@@ -2927,6 +3383,10 @@ var BuildServerTask = class extends BuildTask {
2927
3383
  chunkFileNames: "[hash].js",
2928
3384
  assetFileNames: "[hash][extname]",
2929
3385
  format: "esm",
3386
+ codeSplitting: { groups: [{
3387
+ name: "react",
3388
+ test: /node_modules\/react(\/|-dom\/)/
3389
+ }] },
2930
3390
  minify: {
2931
3391
  mangle: { keepNames: true },
2932
3392
  compress: { keepNames: {
@@ -4467,7 +4927,12 @@ const DEFAULT_IGNORE = [
4467
4927
  */
4468
4928
  const changelogOptions = $atom({
4469
4929
  name: "alepha.cli.changelog.options",
4470
- schema: t.object({ ignore: t.optional(t.array(t.string())) }),
4930
+ schema: t.object({
4931
+ /**
4932
+ * Scopes to ignore (e.g., "project", "release", "chore").
4933
+ * Commits like `feat(chore): ...` will be excluded from changelog.
4934
+ */
4935
+ ignore: t.optional(t.array(t.string())) }),
4471
4936
  default: { ignore: DEFAULT_IGNORE }
4472
4937
  });
4473
4938
  //#endregion
@@ -4609,10 +5074,20 @@ var ChangelogCommand = class {
4609
5074
  name: "changelog",
4610
5075
  description: "Generate changelog from conventional commits (outputs to stdout)",
4611
5076
  flags: t.object({
5077
+ /**
5078
+ * Show changes from this ref (tag, commit, branch).
5079
+ * Defaults to the latest version tag.
5080
+ * Example: --from=1.0.0
5081
+ */
4612
5082
  from: t.optional(t.string({
4613
5083
  aliases: ["f"],
4614
5084
  description: "Starting ref (default: latest tag)"
4615
5085
  })),
5086
+ /**
5087
+ * Show changes up to this ref (tag, commit, branch).
5088
+ * Defaults to HEAD.
5089
+ * Example: --to=main
5090
+ */
4616
5091
  to: t.optional(t.string({
4617
5092
  aliases: ["t"],
4618
5093
  description: "Ending ref (default: HEAD)"
@@ -4774,9 +5249,9 @@ var InitCommand = class {
4774
5249
  aliases: ["r"],
4775
5250
  description: "Include React dependencies and web module (src/web/)"
4776
5251
  })),
4777
- ui: t.optional(t.boolean({ description: "Include @alepha/ui (components, auth portal, admin portal)" })),
4778
- saas: t.optional(t.boolean({ description: "Include authentication, admin portal, API, UI, and React. Everything you need for a SaaS app." })),
4779
5252
  tailwind: t.optional(t.boolean({ description: "Include Tailwind CSS with Vite plugin. Implies --react" })),
5253
+ 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" })),
5254
+ 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" })),
4780
5255
  test: t.optional(t.boolean({ description: "Include Vitest and create test directory" })),
4781
5256
  force: t.optional(t.boolean({
4782
5257
  aliases: ["f"],