alepha 0.20.2 → 0.20.4

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 (304) 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.js +49 -0
  7. package/dist/api/audits/index.js.map +1 -1
  8. package/dist/api/files/index.js.map +1 -1
  9. package/dist/api/jobs/index.d.ts +2 -61
  10. package/dist/api/jobs/index.d.ts.map +1 -1
  11. package/dist/api/jobs/index.js.map +1 -1
  12. package/dist/api/keys/index.d.ts +4 -4
  13. package/dist/api/keys/index.js.map +1 -1
  14. package/dist/api/notifications/index.d.ts +1 -10
  15. package/dist/api/notifications/index.d.ts.map +1 -1
  16. package/dist/api/parameters/index.browser.js +37 -0
  17. package/dist/api/parameters/index.browser.js.map +1 -1
  18. package/dist/api/parameters/index.d.ts +12 -68
  19. package/dist/api/parameters/index.d.ts.map +1 -1
  20. package/dist/api/parameters/index.js +57 -4
  21. package/dist/api/parameters/index.js.map +1 -1
  22. package/dist/api/payments/index.js.map +1 -1
  23. package/dist/api/users/index.browser.js +6 -0
  24. package/dist/api/users/index.browser.js.map +1 -1
  25. package/dist/api/users/index.d.ts +148 -227
  26. package/dist/api/users/index.d.ts.map +1 -1
  27. package/dist/api/users/index.js +60 -14
  28. package/dist/api/users/index.js.map +1 -1
  29. package/dist/api/verifications/index.d.ts.map +1 -1
  30. package/dist/api/verifications/index.js +2 -1
  31. package/dist/api/verifications/index.js.map +1 -1
  32. package/dist/bucket/index.d.ts +77 -107
  33. package/dist/bucket/index.d.ts.map +1 -1
  34. package/dist/bucket/index.js +153 -5
  35. package/dist/bucket/index.js.map +1 -1
  36. package/dist/bucket/index.workerd.js +12 -2
  37. package/dist/bucket/index.workerd.js.map +1 -1
  38. package/dist/cache/core/index.d.ts +26 -0
  39. package/dist/cache/core/index.d.ts.map +1 -1
  40. package/dist/cache/core/index.js +11 -1
  41. package/dist/cache/core/index.js.map +1 -1
  42. package/dist/cache/core/index.workerd.js +11 -1
  43. package/dist/cache/core/index.workerd.js.map +1 -1
  44. package/dist/captcha/index.js.map +1 -1
  45. package/dist/cli/config/index.d.ts +7 -5
  46. package/dist/cli/config/index.d.ts.map +1 -1
  47. package/dist/cli/config/index.js +2 -3
  48. package/dist/cli/config/index.js.map +1 -1
  49. package/dist/cli/core/index.d.ts +637 -11660
  50. package/dist/cli/core/index.d.ts.map +1 -1
  51. package/dist/cli/core/index.js +707 -532
  52. package/dist/cli/core/index.js.map +1 -1
  53. package/dist/cli/devtools/index.d.ts +4 -8
  54. package/dist/cli/devtools/index.d.ts.map +1 -1
  55. package/dist/cli/devtools/index.js +20 -16
  56. package/dist/cli/devtools/index.js.map +1 -1
  57. package/dist/cli/platform/index.d.ts +51 -77
  58. package/dist/cli/platform/index.d.ts.map +1 -1
  59. package/dist/cli/platform/index.js +65 -15
  60. package/dist/cli/platform/index.js.map +1 -1
  61. package/dist/cli/vendor/index.d.ts +10 -13
  62. package/dist/cli/vendor/index.d.ts.map +1 -1
  63. package/dist/cli/vendor/index.js +30 -12
  64. package/dist/cli/vendor/index.js.map +1 -1
  65. package/dist/command/index.js +1 -1
  66. package/dist/command/index.js.map +1 -1
  67. package/dist/core/index.browser.js +27 -3
  68. package/dist/core/index.browser.js.map +1 -1
  69. package/dist/core/index.d.ts +8 -11
  70. package/dist/core/index.d.ts.map +1 -1
  71. package/dist/core/index.js +27 -3
  72. package/dist/core/index.js.map +1 -1
  73. package/dist/core/index.native.js +27 -3
  74. package/dist/core/index.native.js.map +1 -1
  75. package/dist/core/index.workerd.js +27 -3
  76. package/dist/core/index.workerd.js.map +1 -1
  77. package/dist/crypto/index.js.map +1 -1
  78. package/dist/datetime/index.d.ts +69 -10
  79. package/dist/datetime/index.d.ts.map +1 -1
  80. package/dist/datetime/index.js +135 -13
  81. package/dist/datetime/index.js.map +1 -1
  82. package/dist/email/core/index.js.map +1 -1
  83. package/dist/email/smtp/index.js +130 -16
  84. package/dist/email/smtp/index.js.map +1 -1
  85. package/dist/fake/index.js.map +1 -1
  86. package/dist/lock/core/index.d.ts +30 -2
  87. package/dist/lock/core/index.d.ts.map +1 -1
  88. package/dist/lock/core/index.js +35 -12
  89. package/dist/lock/core/index.js.map +1 -1
  90. package/dist/lock/redis/index.js.map +1 -1
  91. package/dist/logger/index.js +32 -1
  92. package/dist/logger/index.js.map +1 -1
  93. package/dist/mcp/index.d.ts +238 -31
  94. package/dist/mcp/index.d.ts.map +1 -1
  95. package/dist/mcp/index.js +198 -67
  96. package/dist/mcp/index.js.map +1 -1
  97. package/dist/orm/core/index.browser.js +2 -362
  98. package/dist/orm/core/index.browser.js.map +1 -1
  99. package/dist/orm/core/index.bun.js +18 -409
  100. package/dist/orm/core/index.bun.js.map +1 -1
  101. package/dist/orm/core/index.d.ts +41 -194
  102. package/dist/orm/core/index.d.ts.map +1 -1
  103. package/dist/orm/core/index.js +27 -422
  104. package/dist/orm/core/index.js.map +1 -1
  105. package/dist/orm/postgres/index.bun.js +17 -20
  106. package/dist/orm/postgres/index.bun.js.map +1 -1
  107. package/dist/orm/postgres/index.d.ts +1 -5
  108. package/dist/orm/postgres/index.d.ts.map +1 -1
  109. package/dist/orm/postgres/index.js +17 -20
  110. package/dist/orm/postgres/index.js.map +1 -1
  111. package/dist/react/core/index.d.ts +102 -1
  112. package/dist/react/core/index.d.ts.map +1 -1
  113. package/dist/react/core/index.js +65 -1
  114. package/dist/react/core/index.js.map +1 -1
  115. package/dist/react/form/index.d.ts +6 -0
  116. package/dist/react/form/index.d.ts.map +1 -1
  117. package/dist/react/form/index.js +7 -7
  118. package/dist/react/form/index.js.map +1 -1
  119. package/dist/react/i18n/index.d.ts +7 -1
  120. package/dist/react/i18n/index.d.ts.map +1 -1
  121. package/dist/react/i18n/index.js +6 -0
  122. package/dist/react/i18n/index.js.map +1 -1
  123. package/dist/react/intro/index.js +22 -17
  124. package/dist/react/intro/index.js.map +1 -1
  125. package/dist/react/router/index.browser.js +98 -4
  126. package/dist/react/router/index.browser.js.map +1 -1
  127. package/dist/react/router/index.d.ts +58 -5
  128. package/dist/react/router/index.d.ts.map +1 -1
  129. package/dist/react/router/index.js +122 -6
  130. package/dist/react/router/index.js.map +1 -1
  131. package/dist/react/testing/{chunk-DBEY4PJZ.js → chunk-6Ep1yQYe.js} +1 -1
  132. package/dist/react/testing/index.js +1 -1
  133. package/dist/react/testing/index.js.map +1 -1
  134. package/dist/react/ui/index.d.ts +195 -1
  135. package/dist/react/ui/index.d.ts.map +1 -1
  136. package/dist/react/ui/index.js +64 -1
  137. package/dist/react/ui/index.js.map +1 -1
  138. package/dist/react/websocket/index.js.map +1 -1
  139. package/dist/redis/index.js.map +1 -1
  140. package/dist/scheduler/index.d.ts +1 -2
  141. package/dist/scheduler/index.d.ts.map +1 -1
  142. package/dist/scheduler/index.js +1 -1
  143. package/dist/scheduler/index.js.map +1 -1
  144. package/dist/scheduler/index.workerd.js +1 -1
  145. package/dist/scheduler/index.workerd.js.map +1 -1
  146. package/dist/security/index.browser.js.map +1 -1
  147. package/dist/security/index.d.ts.map +1 -1
  148. package/dist/security/index.js +2 -2
  149. package/dist/security/index.js.map +1 -1
  150. package/dist/server/auth/index.d.ts.map +1 -1
  151. package/dist/server/auth/index.js +24 -10
  152. package/dist/server/auth/index.js.map +1 -1
  153. package/dist/server/cookies/index.js.map +1 -1
  154. package/dist/server/core/index.browser.js +10 -3
  155. package/dist/server/core/index.browser.js.map +1 -1
  156. package/dist/server/core/index.d.ts +1 -4
  157. package/dist/server/core/index.d.ts.map +1 -1
  158. package/dist/server/core/index.js +47 -9
  159. package/dist/server/core/index.js.map +1 -1
  160. package/dist/server/links/index.browser.js.map +1 -1
  161. package/dist/server/links/index.js.map +1 -1
  162. package/dist/server/metrics/index.js +19 -1
  163. package/dist/server/metrics/index.js.map +1 -1
  164. package/dist/server/rate-limit/index.js.map +1 -1
  165. package/dist/server/static/index.js.map +1 -1
  166. package/dist/server/swagger/index.d.ts.map +1 -1
  167. package/dist/server/swagger/index.js +4 -5
  168. package/dist/server/swagger/index.js.map +1 -1
  169. package/dist/sms/index.js.map +1 -1
  170. package/dist/system/index.browser.js.map +1 -1
  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.map +1 -1
  174. package/dist/websocket/index.browser.js +32 -5
  175. package/dist/websocket/index.browser.js.map +1 -1
  176. package/dist/websocket/index.d.ts +3 -1
  177. package/dist/websocket/index.d.ts.map +1 -1
  178. package/dist/websocket/index.js +42 -6
  179. package/dist/websocket/index.js.map +1 -1
  180. package/package.json +685 -274
  181. package/src/api/files/__tests__/FileController.spec.ts +1 -1
  182. package/src/api/jobs/__tests__/$job.spec.ts +5 -1
  183. package/src/api/parameters/services/ParameterProvider.ts +21 -4
  184. package/src/api/users/__tests__/SessionService.spec.ts +99 -0
  185. package/src/api/users/__tests__/UserJobs.spec.ts +67 -0
  186. package/src/api/users/atoms/realmAuthSettingsAtom.ts +15 -0
  187. package/src/api/users/entities/sessions.ts +6 -0
  188. package/src/api/users/jobs/UserJobs.ts +44 -17
  189. package/src/api/users/providers/RealmProvider.ts +4 -0
  190. package/src/api/users/schemas/userQuerySchema.ts +0 -1
  191. package/src/api/users/services/SessionService.ts +27 -0
  192. package/src/api/users/services/UserService.ts +1 -5
  193. package/src/api/verifications/__tests__/CodeVerification.spec.ts +14 -0
  194. package/src/api/verifications/__tests__/LinkVerification.spec.ts +14 -0
  195. package/src/api/verifications/services/VerificationService.ts +1 -0
  196. package/src/bucket/__tests__/NodeS3BucketProvider.spec.ts +74 -0
  197. package/src/bucket/index.ts +19 -2
  198. package/src/bucket/primitives/$bucket.ts +9 -1
  199. package/src/bucket/providers/CloudflareR2Provider.ts +2 -137
  200. package/src/bucket/providers/NodeS3BucketProvider.ts +218 -0
  201. package/src/cache/core/index.ts +29 -0
  202. package/src/cache/core/primitives/$cache.ts +14 -1
  203. package/src/cli/config/defineConfig.ts +13 -15
  204. package/src/cli/core/__tests__/init.spec.ts +214 -7
  205. package/src/cli/core/commands/init.ts +12 -0
  206. package/src/cli/core/services/PackageManagerUtils.ts +23 -6
  207. package/src/cli/core/services/ProjectScaffolder.ts +315 -33
  208. package/src/cli/core/tasks/BuildCloudflareTask.ts +5 -0
  209. package/src/cli/core/tasks/BuildDockerTask.ts +9 -10
  210. package/src/cli/core/tasks/BuildServerTask.ts +8 -0
  211. package/src/cli/core/templates/agentMd.ts +2 -10
  212. package/src/cli/core/templates/apiIndexTs.ts +23 -1
  213. package/src/cli/core/templates/componentsJsonTs.ts +39 -0
  214. package/src/cli/core/templates/mainCss.ts +1 -0
  215. package/src/cli/core/templates/saasAdminLayoutTsx.ts +77 -0
  216. package/src/cli/core/templates/saasAdminPagesTsx.ts +26 -0
  217. package/src/cli/core/templates/saasAuthLayoutTsx.ts +20 -0
  218. package/src/cli/core/templates/saasAuthPagesTsx.ts +62 -0
  219. package/src/cli/core/templates/saasRealmProviderTs.ts +46 -0
  220. package/src/cli/core/templates/webAppRouterTs.ts +104 -1
  221. package/src/cli/core/templates/webIndexTs.ts +23 -1
  222. package/src/cli/devtools/index.ts +12 -26
  223. package/src/cli/platform/__tests__/SecretsCommand.spec.ts +2 -0
  224. package/src/cli/platform/index.ts +15 -24
  225. package/src/cli/vendor/atoms/vendorOptions.ts +1 -1
  226. package/src/cli/vendor/index.ts +14 -23
  227. package/src/command/providers/CliProvider.ts +1 -1
  228. package/src/core/Alepha.ts +11 -1
  229. package/src/core/helpers/ref.ts +18 -0
  230. package/src/core/index.shared.ts +1 -0
  231. package/src/core/interfaces/Service.ts +3 -1
  232. package/src/core/providers/SchemaValidator.ts +9 -1
  233. package/src/core/providers/TypeProvider.ts +2 -3
  234. package/src/datetime/REFACTORING.md +118 -0
  235. package/src/datetime/providers/DateTimeProvider.ts +203 -24
  236. package/src/lock/core/index.ts +31 -0
  237. package/src/lock/core/primitives/$lock.ts +14 -1
  238. package/src/logger/services/Logger.ts +1 -1
  239. package/src/mcp/__tests__/$resource.spec.ts +1 -1
  240. package/src/mcp/__tests__/$tool.spec.ts +1 -1
  241. package/src/mcp/__tests__/McpServerProvider.spec.ts +1 -1
  242. package/src/mcp/__tests__/jsonrpc.spec.ts +1 -1
  243. package/src/mcp/helpers/jsonrpc.ts +26 -1
  244. package/src/mcp/index.ts +10 -5
  245. package/src/mcp/interfaces/McpTypes.ts +83 -6
  246. package/src/mcp/primitives/$prompt.ts +18 -1
  247. package/src/mcp/primitives/$resource.ts +18 -1
  248. package/src/mcp/primitives/$tool.ts +83 -7
  249. package/src/mcp/providers/McpServerProvider.ts +74 -16
  250. package/src/mcp/transports/StreamableHttpMcpTransport.ts +226 -0
  251. package/src/orm/REFACTORING.md +330 -0
  252. package/src/orm/__tests__/$repository-tests.ts +1 -0
  253. package/src/orm/__tests__/orm-next-tests.ts +2 -67
  254. package/src/orm/__tests__/orm-next.spec.ts +0 -21
  255. package/src/orm/core/index.shared.ts +0 -2
  256. package/src/orm/core/index.ts +1 -2
  257. package/src/orm/core/primitives/$repository.ts +3 -6
  258. package/src/orm/core/primitives/$transactional.ts +11 -0
  259. package/src/orm/core/providers/drivers/DatabaseProvider.ts +0 -5
  260. package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +11 -13
  261. package/src/orm/core/schemas/updateSchema.ts +1 -1
  262. package/src/orm/core/services/ModelBuilder.ts +1 -13
  263. package/src/orm/core/services/PgRelationManager.ts +4 -2
  264. package/src/orm/core/services/Repository.ts +1 -42
  265. package/src/orm/core/services/SqliteModelBuilder.ts +2 -33
  266. package/src/orm/postgres/services/PostgresModelBuilder.ts +10 -45
  267. package/src/react/core/__tests__/useQuery.browser.spec.tsx +86 -0
  268. package/src/react/core/hooks/useQuery.ts +153 -0
  269. package/src/react/core/index.ts +1 -0
  270. package/src/react/form/services/FormModel.ts +15 -6
  271. package/src/react/form/services/parseField.ts +8 -0
  272. package/src/react/i18n/providers/I18nProvider.ts +8 -2
  273. package/src/react/intro/components/GettingStartedAuthSlide.tsx +11 -4
  274. package/src/react/router/__tests__/$page.spec.tsx +0 -16
  275. package/src/react/router/__tests__/ReactBrowserProvider.browser.spec.ts +213 -2
  276. package/src/react/router/__tests__/ssr.spec.tsx +339 -0
  277. package/src/react/router/primitives/$page.ts +28 -4
  278. package/src/react/router/providers/ReactBrowserProvider.ts +73 -0
  279. package/src/react/router/providers/ReactBrowserRouterProvider.ts +1 -1
  280. package/src/react/router/providers/ReactPageProvider.ts +27 -9
  281. package/src/react/router/providers/ReactPreloadProvider.ts +1 -1
  282. package/src/react/router/providers/ReactServerProvider.ts +1 -0
  283. package/src/react/ui/atoms/uiThemeListAtom.ts +36 -0
  284. package/src/react/ui/index.ts +6 -0
  285. package/src/react/ui/services/SchemaControl.ts +209 -0
  286. package/src/scheduler/providers/CronProvider.ts +1 -1
  287. package/src/security/primitives/$basicAuth.ts +1 -1
  288. package/src/security/primitives/$issuer.ts +6 -3
  289. package/src/server/auth/providers/ServerAuthProvider.ts +5 -1
  290. package/src/server/core/__tests__/ServerRouterProvider-serializationError.spec.ts +75 -0
  291. package/src/server/core/__tests__/ServerRouterProvider-validationError.spec.ts +306 -0
  292. package/src/server/core/errors/ValidationError.ts +13 -1
  293. package/src/server/core/interfaces/ServerRequest.ts +1 -0
  294. package/src/server/core/primitives/$action.ts +16 -5
  295. package/src/server/core/providers/ServerProvider.ts +1 -1
  296. package/src/server/core/providers/ServerRouterProvider.ts +28 -6
  297. package/src/server/core/services/HttpClient.ts +1 -1
  298. package/src/server/swagger/providers/ServerSwaggerProvider.ts +6 -8
  299. package/src/websocket/providers/NodeWebSocketServerProvider.ts +10 -4
  300. package/src/websocket/services/WebSocketClient.ts +11 -5
  301. package/src/mcp/transports/SseMcpTransport.ts +0 -182
  302. package/src/orm/core/__tests__/parseQueryString.spec.ts +0 -196
  303. package/src/orm/core/helpers/parseQueryString.ts +0 -502
  304. package/src/orm/core/primitives/$view.ts +0 -88
@@ -2,13 +2,14 @@ import { basename, dirname } from "node:path";
2
2
  import { $inject, AlephaError } from "alepha";
3
3
  import type { RunnerMethod } from "alepha/command";
4
4
  import { $logger, ConsoleColorProvider } from "alepha/logger";
5
- import { FileSystemProvider } from "alepha/system";
6
- import { type AgentMdOptions, agentMd } from "../templates/agentMd.ts";
5
+ import { FileSystemProvider, ShellProvider } from "alepha/system";
6
+ import { agentMd } from "../templates/agentMd.ts";
7
7
  import { alephaConfigTs } from "../templates/alephaConfigTs.ts";
8
8
  import { apiHelloControllerTs } from "../templates/apiHelloControllerTs.ts";
9
9
  import { apiHelloResponseSchemaTs } from "../templates/apiHelloResponseSchemaTs.ts";
10
10
  import { apiIndexTs } from "../templates/apiIndexTs.ts";
11
11
  import { biomeJson } from "../templates/biomeJson.ts";
12
+ import { componentsJsonTs } from "../templates/componentsJsonTs.ts";
12
13
  import { dummySpecTs } from "../templates/dummySpecTs.ts";
13
14
  import { editorconfig } from "../templates/editorconfig.ts";
14
15
  import { gitignore } from "../templates/gitignore.ts";
@@ -16,6 +17,19 @@ import { logoSvg } from "../templates/logoSvg.ts";
16
17
  import { mainBrowserTs } from "../templates/mainBrowserTs.ts";
17
18
  import { mainCss } from "../templates/mainCss.ts";
18
19
  import { mainServerTs } from "../templates/mainServerTs.ts";
20
+ import { saasAdminLayoutTsx } from "../templates/saasAdminLayoutTsx.ts";
21
+ import {
22
+ saasAdminSessionsTsx,
23
+ saasAdminUsersTsx,
24
+ } from "../templates/saasAdminPagesTsx.ts";
25
+ import { saasAuthLayoutTsx } from "../templates/saasAuthLayoutTsx.ts";
26
+ import {
27
+ saasAuthLoginTsx,
28
+ saasAuthRegisterTsx,
29
+ saasAuthResetPasswordTsx,
30
+ saasAuthVerifyEmailTsx,
31
+ } from "../templates/saasAuthPagesTsx.ts";
32
+ import { saasRealmProviderTs } from "../templates/saasRealmProviderTs.ts";
19
33
  import { tsconfigJson } from "../templates/tsconfigJson.ts";
20
34
  import { viteConfigTs } from "../templates/viteConfigTs.ts";
21
35
  import { vitestConfigTs } from "../templates/vitestConfigTs.ts";
@@ -41,6 +55,7 @@ export class ProjectScaffolder {
41
55
  protected readonly log = $logger();
42
56
  protected readonly colors = $inject(ConsoleColorProvider);
43
57
  protected readonly fs = $inject(FileSystemProvider);
58
+ protected readonly shell = $inject(ShellProvider);
44
59
  protected readonly pm = $inject(PackageManagerUtils);
45
60
  protected readonly utils = $inject(AlephaCliUtils);
46
61
 
@@ -70,10 +85,16 @@ export class ProjectScaffolder {
70
85
  */
71
86
  checkWorkspace?: boolean;
72
87
  packageJson?: boolean | DependencyModes;
73
- tsconfigJson?: boolean;
88
+ /**
89
+ * `true` writes a tsconfig.json if one doesn't already exist (parent
90
+ * dirs included). `"local"` writes one when none exists *in this
91
+ * directory* — used by shadcn since the CLI reads the local
92
+ * tsconfig directly for import-alias detection.
93
+ */
94
+ tsconfigJson?: boolean | "local";
74
95
  biomeJson?: boolean;
75
96
  editorconfig?: boolean;
76
- agentMd?: false | AgentMdOptions;
97
+ agentMd?: boolean;
77
98
  },
78
99
  ): Promise<void> {
79
100
  const tasks: Promise<void>[] = [];
@@ -91,7 +112,12 @@ export class ProjectScaffolder {
91
112
  );
92
113
  }
93
114
  if (opts.tsconfigJson) {
94
- tasks.push(this.ensureTsConfig(root, { force }));
115
+ tasks.push(
116
+ this.ensureTsConfig(root, {
117
+ force,
118
+ localOnly: opts.tsconfigJson === "local",
119
+ }),
120
+ );
95
121
  }
96
122
  if (opts.biomeJson) {
97
123
  tasks.push(this.ensureBiomeConfig(root, { force, checkWorkspace }));
@@ -100,7 +126,7 @@ export class ProjectScaffolder {
100
126
  tasks.push(this.ensureEditorConfig(root, { force, checkWorkspace }));
101
127
  }
102
128
  if (opts.agentMd) {
103
- tasks.push(this.ensureAgentMd(root, { ...opts.agentMd, force }));
129
+ tasks.push(this.ensureAgentMd(root, { force }));
104
130
  }
105
131
 
106
132
  await Promise.all(tasks);
@@ -112,10 +138,15 @@ export class ProjectScaffolder {
112
138
 
113
139
  public async ensureTsConfig(
114
140
  root: string,
115
- opts: { force?: boolean } = {},
141
+ opts: { force?: boolean; localOnly?: boolean } = {},
116
142
  ): Promise<void> {
117
- // Check if tsconfig.json exists in current or parent directories
118
- if (!opts.force && (await this.existsInParents(root, "tsconfig.json"))) {
143
+ // Check if tsconfig.json exists in current or parent directories.
144
+ // `localOnly: true` skips the parent walk — needed when a tool reads the
145
+ // local tsconfig directly (shadcn does this for import-alias detection).
146
+ const exists = opts.localOnly
147
+ ? await this.fs.exists(this.fs.join(root, "tsconfig.json"))
148
+ : await this.existsInParents(root, "tsconfig.json");
149
+ if (!opts.force && exists) {
119
150
  return;
120
151
  }
121
152
  await this.fs.writeFile(
@@ -183,12 +214,19 @@ export class ProjectScaffolder {
183
214
  return true;
184
215
  }
185
216
 
217
+ /**
218
+ * Ensure AGENTS.md (cross-tool standard, canonical source) exists, with a
219
+ * CLAUDE.md stub that imports it via Claude Code's `@` syntax. Single
220
+ * source of truth, cross-platform, no symlink needed.
221
+ */
186
222
  public async ensureAgentMd(
187
223
  root: string,
188
- options: AgentMdOptions & { force?: boolean },
224
+ options: { force?: boolean } = {},
189
225
  ): Promise<void> {
190
- const filename = options.type === "claude" ? "CLAUDE.md" : "AGENTS.md";
191
- await this.ensureFile(root, filename, agentMd(options), options.force);
226
+ await Promise.all([
227
+ this.ensureFile(root, "AGENTS.md", agentMd(), options.force),
228
+ this.ensureFile(root, "CLAUDE.md", "@AGENTS.md\n", options.force),
229
+ ]);
192
230
  }
193
231
 
194
232
  /**
@@ -240,7 +278,7 @@ export class ProjectScaffolder {
240
278
  */
241
279
  public async ensureApiProject(
242
280
  root: string,
243
- opts: { force?: boolean } = {},
281
+ opts: { saas?: boolean; force?: boolean } = {},
244
282
  ): Promise<void> {
245
283
  const appName = this.getAppName(root);
246
284
 
@@ -256,7 +294,7 @@ export class ProjectScaffolder {
256
294
  await this.ensureFile(
257
295
  root,
258
296
  "src/api/index.ts",
259
- apiIndexTs({ appName }),
297
+ apiIndexTs({ appName, saas: opts.saas }),
260
298
  opts.force,
261
299
  );
262
300
  await this.ensureFile(
@@ -271,6 +309,38 @@ export class ProjectScaffolder {
271
309
  apiHelloResponseSchemaTs(),
272
310
  opts.force,
273
311
  );
312
+
313
+ if (opts.saas) {
314
+ await this.fs.mkdir(this.fs.join(root, "src/api/providers"), {
315
+ recursive: true,
316
+ });
317
+ const adminEmail = await this.detectGitEmail();
318
+ await this.ensureFile(
319
+ root,
320
+ "src/api/providers/RealmProvider.ts",
321
+ saasRealmProviderTs({ adminEmail }),
322
+ opts.force,
323
+ );
324
+ }
325
+ }
326
+
327
+ /**
328
+ * Best-effort lookup for the developer's git email (used as the seeded
329
+ * `adminEmails` entry in the SaaS realm). Returns undefined if git isn't
330
+ * available or if `user.email` isn't configured — the template falls back
331
+ * to `admin@example.com` in that case.
332
+ */
333
+ protected async detectGitEmail(): Promise<string | undefined> {
334
+ try {
335
+ const stdout = (await this.shell.run("git config --get user.email", {
336
+ capture: true,
337
+ })) as unknown as string;
338
+ const email = (stdout ?? "").trim();
339
+ if (!email || !email.includes("@")) return undefined;
340
+ return email;
341
+ } catch {
342
+ return undefined;
343
+ }
274
344
  }
275
345
 
276
346
  // ===========================================
@@ -290,6 +360,8 @@ export class ProjectScaffolder {
290
360
  opts: {
291
361
  api?: boolean;
292
362
  tailwind?: boolean;
363
+ shadcn?: boolean;
364
+ saas?: boolean;
293
365
  force?: boolean;
294
366
  } = {},
295
367
  ): Promise<void> {
@@ -300,6 +372,15 @@ export class ProjectScaffolder {
300
372
  recursive: true,
301
373
  });
302
374
 
375
+ if (opts.saas) {
376
+ await this.fs.mkdir(this.fs.join(root, "src/web/components/auth"), {
377
+ recursive: true,
378
+ });
379
+ await this.fs.mkdir(this.fs.join(root, "src/web/components/admin"), {
380
+ recursive: true,
381
+ });
382
+ }
383
+
303
384
  // public/favicon.svg
304
385
  await this.fs.mkdir(this.fs.join(root, "public"), { recursive: true });
305
386
  await this.ensureFile(root, "public/favicon.svg", logoSvg, opts.force);
@@ -317,17 +398,32 @@ export class ProjectScaffolder {
317
398
  await this.ensureFile(root, "vite.config.ts", viteConfigTs(), opts.force);
318
399
  }
319
400
 
401
+ // shadcn/ui: write components.json before running `shadcn init` — the
402
+ // CLI respects an existing config and skips its interactive prompts,
403
+ // which lets us pin our aliases (`@/web/*`) and the `@alepha` registry.
404
+ // The CLI itself writes the cn() helper, theme tokens, and installs
405
+ // runtime deps (clsx, tailwind-merge, class-variance-authority,
406
+ // lucide-react, tw-animate-css) — see runShadcnInit below.
407
+ if (opts.shadcn) {
408
+ await this.ensureFile(
409
+ root,
410
+ "components.json",
411
+ componentsJsonTs(),
412
+ opts.force,
413
+ );
414
+ }
415
+
320
416
  // Web structure
321
417
  await this.ensureFile(
322
418
  root,
323
419
  "src/web/index.ts",
324
- webIndexTs({ appName }),
420
+ webIndexTs({ appName, saas: opts.saas }),
325
421
  opts.force,
326
422
  );
327
423
  await this.ensureFile(
328
424
  root,
329
425
  "src/web/AppRouter.ts",
330
- webAppRouterTs({ api: opts.api }),
426
+ webAppRouterTs({ api: opts.api, saas: opts.saas }),
331
427
  opts.force,
332
428
  );
333
429
  await this.ensureFile(
@@ -342,6 +438,62 @@ export class ProjectScaffolder {
342
438
  mainBrowserTs(),
343
439
  opts.force,
344
440
  );
441
+
442
+ if (opts.saas) {
443
+ // Auth — layout + 4 pages, each a thin wrapper around the registry
444
+ // component that `shadcn add @alepha/auth-*` drops at
445
+ // src/web/components/auth-*.tsx.
446
+ await this.ensureFile(
447
+ root,
448
+ "src/web/components/auth/AuthLayout.tsx",
449
+ saasAuthLayoutTsx(),
450
+ opts.force,
451
+ );
452
+ await this.ensureFile(
453
+ root,
454
+ "src/web/components/auth/Login.tsx",
455
+ saasAuthLoginTsx(),
456
+ opts.force,
457
+ );
458
+ await this.ensureFile(
459
+ root,
460
+ "src/web/components/auth/Register.tsx",
461
+ saasAuthRegisterTsx(),
462
+ opts.force,
463
+ );
464
+ await this.ensureFile(
465
+ root,
466
+ "src/web/components/auth/ResetPassword.tsx",
467
+ saasAuthResetPasswordTsx(),
468
+ opts.force,
469
+ );
470
+ await this.ensureFile(
471
+ root,
472
+ "src/web/components/auth/VerifyEmail.tsx",
473
+ saasAuthVerifyEmailTsx(),
474
+ opts.force,
475
+ );
476
+
477
+ // Admin — AppShell layout + 5 admin-* pages
478
+ await this.ensureFile(
479
+ root,
480
+ "src/web/components/admin/AdminLayout.tsx",
481
+ saasAdminLayoutTsx(),
482
+ opts.force,
483
+ );
484
+ await this.ensureFile(
485
+ root,
486
+ "src/web/components/admin/Users.tsx",
487
+ saasAdminUsersTsx(),
488
+ opts.force,
489
+ );
490
+ await this.ensureFile(
491
+ root,
492
+ "src/web/components/admin/Sessions.tsx",
493
+ saasAdminSessionsTsx(),
494
+ opts.force,
495
+ );
496
+ }
345
497
  }
346
498
 
347
499
  // ===========================================
@@ -394,6 +546,10 @@ export class ProjectScaffolder {
394
546
  api?: boolean;
395
547
  react?: boolean;
396
548
  tailwind?: boolean;
549
+ /** boolean toggle, or a string preset id (default `b0` when bare). */
550
+ shadcn?: boolean | string;
551
+ /** boolean toggle, or a string preset id (default `b0` when bare). */
552
+ saas?: boolean | string;
397
553
  test?: boolean;
398
554
  force?: boolean;
399
555
  };
@@ -404,13 +560,41 @@ export class ProjectScaffolder {
404
560
  await this.fs.mkdir(root, { force: true });
405
561
  }
406
562
 
407
- // Flag cascading: --tailwind--react
563
+ // `--shadcn` / `--saas` are union flags: baretrue, string → preset.
564
+ // Capture the preset string (default `b0`), then normalize both flags
565
+ // to plain booleans so the rest of the pipeline keeps its boolean
566
+ // contract with PackageManagerUtils + ensureWebProject etc.
567
+ const shadcnPreset =
568
+ (typeof flags.saas === "string" && flags.saas) ||
569
+ (typeof flags.shadcn === "string" && flags.shadcn) ||
570
+ "b0";
571
+
572
+ // Cast to a narrower view so downstream sees pure booleans.
573
+ const f = flags as Omit<typeof flags, "shadcn" | "saas"> & {
574
+ shadcn?: boolean;
575
+ saas?: boolean;
576
+ };
577
+ f.shadcn = !!flags.shadcn;
578
+ f.saas = !!flags.saas;
579
+
580
+ // Flag cascading:
581
+ // --saas → --shadcn + --api
582
+ // --shadcn → --tailwind
583
+ // --tailwind→ --react
584
+ if (f.saas) {
585
+ f.shadcn = true;
586
+ f.api = true;
587
+ }
588
+ if (f.shadcn) {
589
+ f.tailwind = true;
590
+ }
408
591
  if (flags.tailwind) {
409
592
  flags.react = true;
410
593
  }
411
594
 
412
595
  // When codegen flags are set, target directory must be empty (unless --force)
413
- const hasCodegenFlags = flags.api || flags.react || flags.tailwind;
596
+ const hasCodegenFlags =
597
+ flags.api || flags.react || flags.tailwind || flags.shadcn || flags.saas;
414
598
  if (hasCodegenFlags && !flags.force) {
415
599
  const files = await this.fs.ls(root);
416
600
  // Allow a directory that only has package.json (common for monorepo packages)
@@ -425,12 +609,9 @@ export class ProjectScaffolder {
425
609
  // Detect workspace context (are we inside packages/ or apps/ of a monorepo?)
426
610
  const workspace = await this.pm.getWorkspaceContext(root);
427
611
 
428
- // Detect agent type: claude CLI CLAUDE.md, else AGENTS.md
429
- let agentType: "claude" | "agents" | false = false;
430
- if (!workspace.isPackage) {
431
- const hasClaudeCli = await this.utils.isInstalledAsync("claude");
432
- agentType = hasClaudeCli ? "claude" : "agents";
433
- }
612
+ // Always emit both AGENTS.md and CLAUDE.md at project roots (skip for
613
+ // monorepo sub-packages where agent files live at workspace root).
614
+ const writeAgentMd = !workspace.isPackage;
434
615
 
435
616
  const isExpo = await this.pm.hasExpo(root);
436
617
 
@@ -441,12 +622,15 @@ export class ProjectScaffolder {
441
622
  handler: async () => {
442
623
  await this.ensureConfig(root, {
443
624
  force,
444
- packageJson: { ...flags, isPackage: workspace.isPackage },
445
- // Skip workspace-level configs if they exist at workspace root
446
- tsconfigJson: !workspace.config.tsconfigJson,
625
+ packageJson: { ...f, isPackage: workspace.isPackage },
626
+ // Skip workspace-level configs if they exist at workspace root
627
+ // unless --shadcn is set: the shadcn CLI reads the local
628
+ // tsconfig.json directly to detect import aliases (it doesn't
629
+ // follow `extends`), so we must ensure one exists in the package.
630
+ tsconfigJson: f.shadcn ? "local" : !workspace.config.tsconfigJson,
447
631
  biomeJson: true,
448
632
  editorconfig: !workspace.config.editorconfig,
449
- agentMd: agentType ? { type: agentType } : false,
633
+ agentMd: writeAgentMd,
450
634
  });
451
635
 
452
636
  // Create alepha.config.ts with documented options
@@ -459,12 +643,14 @@ export class ProjectScaffolder {
459
643
  force,
460
644
  });
461
645
  if (flags.api) {
462
- await this.ensureApiProject(root, { force });
646
+ await this.ensureApiProject(root, { saas: !!flags.saas, force });
463
647
  }
464
648
  if (flags.react && !isExpo) {
465
649
  await this.ensureWebProject(root, {
466
650
  api: !!flags.api,
467
651
  tailwind: !!flags.tailwind,
652
+ shadcn: !!flags.shadcn,
653
+ saas: !!flags.saas,
468
654
  force,
469
655
  });
470
656
  }
@@ -503,10 +689,76 @@ export class ProjectScaffolder {
503
689
  await this.ensureTestDir(root);
504
690
  }
505
691
 
506
- await run(`${pmName} run lint`, {
507
- alias: "running linter",
508
- root,
509
- });
692
+ // shadcn/ui: run `<pm> shadcn init` against the components.json we wrote
693
+ // earlier. shadcn detects the existing config, respects our aliases,
694
+ // injects theme tokens into src/main.css, writes src/web/lib/utils.ts,
695
+ // and installs runtime deps (clsx, tailwind-merge, etc.).
696
+ //
697
+ // Flags chosen to keep this fully non-interactive:
698
+ // --yes skip confirmation prompts (default in shadcn v4 but
699
+ // passed explicitly so older versions also behave)
700
+ // --no-monorepo skip the monorepo prompt — we ship a single-app
701
+ // layout; users opt into monorepo via `--monorepo`
702
+ // on the alepha side later
703
+ // --silent suppress shadcn's own progress output; alepha's
704
+ // runner already prints a status line
705
+ //
706
+ // We deliberately do NOT pass `--defaults` (would force Next.js +
707
+ // base-nova preset) or `--template` (only applies to scratch projects;
708
+ // ours already has main.server.ts / main.browser.ts).
709
+ // Each PM has a different way to exec a project-local binary.
710
+ const exec = pmExecPrefix(pmName);
711
+
712
+ if (flags.shadcn) {
713
+ // Fully non-interactive shadcn init. The `--preset` arg is what makes
714
+ // this work — without it shadcn falls back to interactive prompts even
715
+ // with --yes/--force. Defaults: vite template + radix base + reinstall
716
+ // (so the components.json we pre-wrote stays canonical).
717
+ await run(
718
+ `${exec} shadcn init --no-monorepo --base radix -t vite --yes --force --reinstall --preset ${escapeShellArg(shadcnPreset)}`,
719
+ { alias: `running shadcn init (preset ${shadcnPreset})`, root },
720
+ );
721
+ // Re-pin our aliases + alepha registry — `shadcn init --force`
722
+ // overwrites components.json with the template defaults.
723
+ await this.fs.writeFile(
724
+ this.fs.join(root, "components.json"),
725
+ componentsJsonTs(),
726
+ );
727
+ }
728
+
729
+ // SaaS preset: pull in the auth + admin registry components from the
730
+ // public alepha registry (already wired via components.json's
731
+ // `registries: { "@alepha": "https://alepha.dev/r/{name}.json" }`).
732
+ // Each `shadcn add` writes the component into src/web/components/* and
733
+ // pulls its peer primitives + dependencies (sonner, etc.).
734
+ if (flags.saas) {
735
+ // Pull the public SaaS bundle in one shot — it aggregates control,
736
+ // auto-form, alepha-table, use-dialog, app-shell, every auth-*, and
737
+ // every admin-* block. Definition lives at
738
+ // https://alepha.dev/r/saas.json (see @alepha/ui-registry).
739
+ // `--yes --overwrite` is the only combo that works non-interactively
740
+ // when registry items would replace files we pre-wrote (auth-login etc.
741
+ // overlap with shadcn primitives like button/input).
742
+ await run(`${exec} shadcn add @alepha/saas --yes --overwrite`, {
743
+ alias: "adding alepha saas registry bundle",
744
+ root,
745
+ });
746
+ }
747
+
748
+ // Best-effort lint: shadcn-imported registry components occasionally
749
+ // trip biome rules (e.g. noArrayIndexKey on a Fragment loop). The user
750
+ // can fix or silence these later — don't block the whole init.
751
+ try {
752
+ await run(`${pmName} run lint`, {
753
+ alias: "running linter",
754
+ root,
755
+ });
756
+ } catch (err) {
757
+ this.log.warn(
758
+ "Linter reported issues during init — continuing. Run `lint` again later to inspect.",
759
+ { error: err instanceof Error ? err.message : String(err) },
760
+ );
761
+ }
510
762
 
511
763
  // Initialize git repository if not in a workspace package
512
764
  if (!workspace.isPackage) {
@@ -587,3 +839,33 @@ export class ProjectScaffolder {
587
839
  }
588
840
  }
589
841
  }
842
+
843
+ /**
844
+ * Map a package manager name to the command that runs a project-local binary.
845
+ *
846
+ * - npm: `npx`
847
+ * - yarn: `yarn` (yarn auto-resolves binary names; `yarn shadcn ...` works)
848
+ * - pnpm: `pnpm exec`
849
+ * - bun: `bunx`
850
+ *
851
+ * Used to invoke `shadcn init` / `shadcn add` regardless of the user's PM —
852
+ * `npm shadcn ...` is invalid (it tries to run a script named `shadcn`).
853
+ */
854
+ /** Quote a value so it survives shell parsing. */
855
+ const escapeShellArg = (value: string): string => {
856
+ if (/^[A-Za-z0-9_./@:-]+$/.test(value)) return value;
857
+ return `'${value.replace(/'/g, "'\\''")}'`;
858
+ };
859
+
860
+ const pmExecPrefix = (pmName: string): string => {
861
+ switch (pmName) {
862
+ case "npm":
863
+ return "npx";
864
+ case "pnpm":
865
+ return "pnpm exec";
866
+ case "bun":
867
+ return "bunx";
868
+ default:
869
+ return "yarn";
870
+ }
871
+ };
@@ -73,6 +73,11 @@ export class BuildCloudflareTask extends BuildTask {
73
73
  };
74
74
  }
75
75
 
76
+ wrangler.observability ??= {
77
+ enabled: true,
78
+ head_sampling_rate: 1,
79
+ };
80
+
76
81
  this.enhanceDomain(wrangler);
77
82
  this.enhanceCron(ctx, wrangler);
78
83
  this.enhanceDatabase(wrangler);
@@ -8,7 +8,7 @@ import { BuildTask, type BuildTaskContext } from "./BuildTask.ts";
8
8
  *
9
9
  * Creates:
10
10
  * - Dockerfile with configurable base image
11
- * - Copies drizzle migrations if they exist
11
+ * - Copies migrations directory if it exists
12
12
  * - Builds Docker image when `--image` flag is provided
13
13
  */
14
14
  export class BuildDockerTask extends BuildTask {
@@ -32,7 +32,7 @@ export class BuildDockerTask extends BuildTask {
32
32
  await ctx.run({
33
33
  name: "generate deploy config (docker)",
34
34
  handler: async () => {
35
- await this.copyDrizzleMigrations(ctx.root, distDir);
35
+ await this.copyMigrations(ctx.root, distDir);
36
36
  await this.writeDockerfile(
37
37
  ctx.root,
38
38
  distDir,
@@ -47,14 +47,13 @@ export class BuildDockerTask extends BuildTask {
47
47
  }
48
48
  }
49
49
 
50
- protected async copyDrizzleMigrations(
51
- root: string,
52
- distDir: string,
53
- ): Promise<void> {
54
- const drizzleDir = this.fs.join(root, "drizzle");
55
- const hasMigrations = await this.fs.exists(drizzleDir);
56
- if (hasMigrations) {
57
- await this.fs.cp(drizzleDir, this.fs.join(root, distDir, "drizzle"));
50
+ protected async copyMigrations(root: string, distDir: string): Promise<void> {
51
+ const migrationsDir = this.fs.join(root, "migrations");
52
+ if (await this.fs.exists(migrationsDir)) {
53
+ await this.fs.cp(
54
+ migrationsDir,
55
+ this.fs.join(root, distDir, "migrations"),
56
+ );
58
57
  }
59
58
  }
60
59
 
@@ -135,6 +135,14 @@ export class BuildServerTask extends BuildTask {
135
135
  chunkFileNames: "[hash].js",
136
136
  assetFileNames: "[hash][extname]",
137
137
  format: "esm",
138
+ codeSplitting: {
139
+ groups: [
140
+ {
141
+ name: "react",
142
+ test: /node_modules\/react(\/|-dom\/)/,
143
+ },
144
+ ],
145
+ },
138
146
  // Rolldown/Oxc minifier: preserve class and function names
139
147
  minify: {
140
148
  mangle: { keepNames: true },
@@ -1,13 +1,5 @@
1
- export type AgentMdType = "claude" | "agents";
2
-
3
- export interface AgentMdOptions {
4
- type: AgentMdType;
5
- }
6
-
7
- export const agentMd = (options: AgentMdOptions): string => {
8
- const header = options.type === "claude" ? `# CLAUDE.md` : `# AGENTS.md`;
9
-
10
- return `${header}
1
+ export const agentMd = (): string => {
2
+ return `# AGENTS.md
11
3
 
12
4
  This is an **Alepha** project.
13
5
 
@@ -1,9 +1,31 @@
1
1
  export interface ApiIndexTsOptions {
2
2
  appName?: string;
3
+ /**
4
+ * Include `AlephaApiUsers` (realms, sessions, registration, identities,
5
+ * password reset, email verification, admin endpoints) plus the local
6
+ * `RealmProvider` that declares `$realm({ ... })`.
7
+ */
8
+ saas?: boolean;
3
9
  }
4
10
 
5
11
  export const apiIndexTs = (options: ApiIndexTsOptions = {}) => {
6
- const { appName = "app" } = options;
12
+ const { appName = "app", saas = false } = options;
13
+
14
+ if (saas) {
15
+ return `
16
+ import { $module } from "alepha";
17
+ import { AlephaApiUsers } from "alepha/api/users";
18
+ import { HelloController } from "./controllers/HelloController.ts";
19
+ import { RealmProvider } from "./providers/RealmProvider.ts";
20
+
21
+ export const ApiModule = $module({
22
+ name: "${appName}.api",
23
+ services: [HelloController, RealmProvider],
24
+ imports: [AlephaApiUsers],
25
+ });
26
+ `.trim();
27
+ }
28
+
7
29
  return `
8
30
  import { $module } from "alepha";
9
31
  import { HelloController } from "./controllers/HelloController.ts";
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `components.json` is the shadcn CLI's project config — it tells
3
+ * `shadcn add` where to drop primitives, which tailwind tokens to use,
4
+ * which icon library to wire up, and which custom registries to resolve.
5
+ *
6
+ * Aliases follow shadcn's defaults (`@/components`, `@/lib/utils`) so the
7
+ * CLI honors them across `init` + `add` calls. Alepha app code lives at
8
+ * `src/web/` (Home, AppRouter, …) and the shadcn primitives live at
9
+ * `src/components/` — kept separate to make the registry components
10
+ * trivially upgradable via `shadcn add --overwrite`.
11
+ *
12
+ * The `registries` block pre-wires the public Alepha registry — consumers
13
+ * can immediately run e.g. `shadcn add @alepha/auth-login`.
14
+ */
15
+ export const componentsJsonTs = () =>
16
+ `{
17
+ "$schema": "https://ui.shadcn.com/schema.json",
18
+ "style": "new-york",
19
+ "rsc": false,
20
+ "tsx": true,
21
+ "tailwind": {
22
+ "config": "",
23
+ "css": "src/main.css",
24
+ "baseColor": "neutral",
25
+ "cssVariables": true
26
+ },
27
+ "aliases": {
28
+ "components": "@/components",
29
+ "utils": "@/lib/utils",
30
+ "ui": "@/components/ui",
31
+ "lib": "@/lib",
32
+ "hooks": "@/hooks"
33
+ },
34
+ "iconLibrary": "lucide",
35
+ "registries": {
36
+ "@alepha": "https://alepha.dev/r/{name}.json"
37
+ }
38
+ }
39
+ `;