alepha 0.15.0 → 0.15.2

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 (551) hide show
  1. package/README.md +43 -98
  2. package/dist/api/audits/index.d.ts +630 -653
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +12 -35
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +365 -358
  7. package/dist/api/files/index.d.ts.map +1 -1
  8. package/dist/api/files/index.js +12 -5
  9. package/dist/api/files/index.js.map +1 -1
  10. package/dist/api/jobs/index.d.ts +255 -248
  11. package/dist/api/jobs/index.d.ts.map +1 -1
  12. package/dist/api/jobs/index.js +10 -3
  13. package/dist/api/jobs/index.js.map +1 -1
  14. package/dist/api/keys/index.d.ts +413 -0
  15. package/dist/api/keys/index.d.ts.map +1 -0
  16. package/dist/api/keys/index.js +476 -0
  17. package/dist/api/keys/index.js.map +1 -0
  18. package/dist/api/notifications/index.browser.js +4 -4
  19. package/dist/api/notifications/index.browser.js.map +1 -1
  20. package/dist/api/notifications/index.d.ts +84 -78
  21. package/dist/api/notifications/index.d.ts.map +1 -1
  22. package/dist/api/notifications/index.js +14 -8
  23. package/dist/api/notifications/index.js.map +1 -1
  24. package/dist/api/parameters/index.d.ts +528 -535
  25. package/dist/api/parameters/index.d.ts.map +1 -1
  26. package/dist/api/parameters/index.js +30 -37
  27. package/dist/api/parameters/index.js.map +1 -1
  28. package/dist/api/users/index.d.ts +1221 -910
  29. package/dist/api/users/index.d.ts.map +1 -1
  30. package/dist/api/users/index.js +2556 -248
  31. package/dist/api/users/index.js.map +1 -1
  32. package/dist/api/verifications/index.d.ts +142 -136
  33. package/dist/api/verifications/index.d.ts.map +1 -1
  34. package/dist/api/verifications/index.js +12 -4
  35. package/dist/api/verifications/index.js.map +1 -1
  36. package/dist/batch/index.d.ts +142 -162
  37. package/dist/batch/index.d.ts.map +1 -1
  38. package/dist/batch/index.js +31 -44
  39. package/dist/batch/index.js.map +1 -1
  40. package/dist/bucket/index.d.ts +595 -171
  41. package/dist/bucket/index.d.ts.map +1 -1
  42. package/dist/bucket/index.js +1856 -12
  43. package/dist/bucket/index.js.map +1 -1
  44. package/dist/cache/core/index.d.ts +225 -53
  45. package/dist/cache/core/index.d.ts.map +1 -1
  46. package/dist/cache/core/index.js +213 -7
  47. package/dist/cache/core/index.js.map +1 -1
  48. package/dist/cache/redis/index.d.ts +1 -0
  49. package/dist/cache/redis/index.d.ts.map +1 -1
  50. package/dist/cache/redis/index.js +6 -2
  51. package/dist/cache/redis/index.js.map +1 -1
  52. package/dist/cli/index.d.ts +834 -226
  53. package/dist/cli/index.d.ts.map +1 -1
  54. package/dist/cli/index.js +2872 -417
  55. package/dist/cli/index.js.map +1 -1
  56. package/dist/command/index.d.ts +458 -310
  57. package/dist/command/index.d.ts.map +1 -1
  58. package/dist/command/index.js +2011 -76
  59. package/dist/command/index.js.map +1 -1
  60. package/dist/core/index.browser.js +309 -97
  61. package/dist/core/index.browser.js.map +1 -1
  62. package/dist/core/index.d.ts +796 -701
  63. package/dist/core/index.d.ts.map +1 -1
  64. package/dist/core/index.js +329 -97
  65. package/dist/core/index.js.map +1 -1
  66. package/dist/core/index.native.js +309 -97
  67. package/dist/core/index.native.js.map +1 -1
  68. package/dist/datetime/index.d.ts +59 -44
  69. package/dist/datetime/index.d.ts.map +1 -1
  70. package/dist/datetime/index.js +15 -0
  71. package/dist/datetime/index.js.map +1 -1
  72. package/dist/email/index.d.ts +314 -19
  73. package/dist/email/index.d.ts.map +1 -1
  74. package/dist/email/index.js +1852 -7
  75. package/dist/email/index.js.map +1 -1
  76. package/dist/fake/index.d.ts +5500 -5418
  77. package/dist/fake/index.d.ts.map +1 -1
  78. package/dist/fake/index.js +113 -42
  79. package/dist/fake/index.js.map +1 -1
  80. package/dist/lock/core/index.d.ts +219 -212
  81. package/dist/lock/core/index.d.ts.map +1 -1
  82. package/dist/lock/core/index.js +11 -4
  83. package/dist/lock/core/index.js.map +1 -1
  84. package/dist/lock/redis/index.d.ts.map +1 -1
  85. package/dist/logger/index.d.ts +41 -90
  86. package/dist/logger/index.d.ts.map +1 -1
  87. package/dist/logger/index.js +15 -68
  88. package/dist/logger/index.js.map +1 -1
  89. package/dist/mcp/index.d.ts +228 -230
  90. package/dist/mcp/index.d.ts.map +1 -1
  91. package/dist/mcp/index.js +32 -31
  92. package/dist/mcp/index.js.map +1 -1
  93. package/dist/orm/index.browser.js +12 -12
  94. package/dist/orm/index.browser.js.map +1 -1
  95. package/dist/orm/index.bun.js +90 -80
  96. package/dist/orm/index.bun.js.map +1 -1
  97. package/dist/orm/index.d.ts +1434 -1459
  98. package/dist/orm/index.d.ts.map +1 -1
  99. package/dist/orm/index.js +112 -130
  100. package/dist/orm/index.js.map +1 -1
  101. package/dist/queue/core/index.d.ts +262 -254
  102. package/dist/queue/core/index.d.ts.map +1 -1
  103. package/dist/queue/core/index.js +14 -6
  104. package/dist/queue/core/index.js.map +1 -1
  105. package/dist/queue/redis/index.d.ts.map +1 -1
  106. package/dist/react/auth/index.browser.js +108 -0
  107. package/dist/react/auth/index.browser.js.map +1 -0
  108. package/dist/react/auth/index.d.ts +100 -0
  109. package/dist/react/auth/index.d.ts.map +1 -0
  110. package/dist/react/auth/index.js +145 -0
  111. package/dist/react/auth/index.js.map +1 -0
  112. package/dist/react/core/index.d.ts +469 -0
  113. package/dist/react/core/index.d.ts.map +1 -0
  114. package/dist/react/core/index.js +464 -0
  115. package/dist/react/core/index.js.map +1 -0
  116. package/dist/react/form/index.d.ts +232 -0
  117. package/dist/react/form/index.d.ts.map +1 -0
  118. package/dist/react/form/index.js +432 -0
  119. package/dist/react/form/index.js.map +1 -0
  120. package/dist/react/head/index.browser.js +423 -0
  121. package/dist/react/head/index.browser.js.map +1 -0
  122. package/dist/react/head/index.d.ts +288 -0
  123. package/dist/react/head/index.d.ts.map +1 -0
  124. package/dist/react/head/index.js +465 -0
  125. package/dist/react/head/index.js.map +1 -0
  126. package/dist/react/i18n/index.d.ts +175 -0
  127. package/dist/react/i18n/index.d.ts.map +1 -0
  128. package/dist/react/i18n/index.js +224 -0
  129. package/dist/react/i18n/index.js.map +1 -0
  130. package/dist/react/router/index.browser.js +1980 -0
  131. package/dist/react/router/index.browser.js.map +1 -0
  132. package/dist/react/router/index.d.ts +2068 -0
  133. package/dist/react/router/index.d.ts.map +1 -0
  134. package/dist/react/router/index.js +4932 -0
  135. package/dist/react/router/index.js.map +1 -0
  136. package/dist/react/websocket/index.d.ts +117 -0
  137. package/dist/react/websocket/index.d.ts.map +1 -0
  138. package/dist/react/websocket/index.js +107 -0
  139. package/dist/react/websocket/index.js.map +1 -0
  140. package/dist/redis/index.bun.js +4 -0
  141. package/dist/redis/index.bun.js.map +1 -1
  142. package/dist/redis/index.d.ts +127 -130
  143. package/dist/redis/index.d.ts.map +1 -1
  144. package/dist/redis/index.js +16 -25
  145. package/dist/redis/index.js.map +1 -1
  146. package/dist/retry/index.d.ts +80 -71
  147. package/dist/retry/index.d.ts.map +1 -1
  148. package/dist/retry/index.js +11 -2
  149. package/dist/retry/index.js.map +1 -1
  150. package/dist/router/index.d.ts +6 -6
  151. package/dist/router/index.d.ts.map +1 -1
  152. package/dist/scheduler/index.d.ts +119 -28
  153. package/dist/scheduler/index.d.ts.map +1 -1
  154. package/dist/scheduler/index.js +404 -3
  155. package/dist/scheduler/index.js.map +1 -1
  156. package/dist/security/index.d.ts +642 -228
  157. package/dist/security/index.d.ts.map +1 -1
  158. package/dist/security/index.js +1579 -37
  159. package/dist/security/index.js.map +1 -1
  160. package/dist/server/auth/index.d.ts +1141 -111
  161. package/dist/server/auth/index.d.ts.map +1 -1
  162. package/dist/server/auth/index.js +1261 -25
  163. package/dist/server/auth/index.js.map +1 -1
  164. package/dist/server/cache/index.d.ts +63 -78
  165. package/dist/server/cache/index.d.ts.map +1 -1
  166. package/dist/server/cache/index.js +7 -22
  167. package/dist/server/cache/index.js.map +1 -1
  168. package/dist/server/compress/index.d.ts +13 -5
  169. package/dist/server/compress/index.d.ts.map +1 -1
  170. package/dist/server/compress/index.js +10 -2
  171. package/dist/server/compress/index.js.map +1 -1
  172. package/dist/server/cookies/index.d.ts +46 -22
  173. package/dist/server/cookies/index.d.ts.map +1 -1
  174. package/dist/server/cookies/index.js +7 -5
  175. package/dist/server/cookies/index.js.map +1 -1
  176. package/dist/server/core/index.d.ts +307 -196
  177. package/dist/server/core/index.d.ts.map +1 -1
  178. package/dist/server/core/index.js +271 -38
  179. package/dist/server/core/index.js.map +1 -1
  180. package/dist/server/cors/index.d.ts +24 -34
  181. package/dist/server/cors/index.d.ts.map +1 -1
  182. package/dist/server/cors/index.js +7 -21
  183. package/dist/server/cors/index.js.map +1 -1
  184. package/dist/server/health/index.d.ts +25 -19
  185. package/dist/server/health/index.d.ts.map +1 -1
  186. package/dist/server/health/index.js +8 -2
  187. package/dist/server/health/index.js.map +1 -1
  188. package/dist/server/helmet/index.d.ts +13 -5
  189. package/dist/server/helmet/index.d.ts.map +1 -1
  190. package/dist/server/helmet/index.js +11 -3
  191. package/dist/server/helmet/index.js.map +1 -1
  192. package/dist/server/links/index.browser.js +9 -1
  193. package/dist/server/links/index.browser.js.map +1 -1
  194. package/dist/server/links/index.d.ts +133 -128
  195. package/dist/server/links/index.d.ts.map +1 -1
  196. package/dist/server/links/index.js +24 -11
  197. package/dist/server/links/index.js.map +1 -1
  198. package/dist/server/metrics/index.d.ts +524 -4
  199. package/dist/server/metrics/index.d.ts.map +1 -1
  200. package/dist/server/metrics/index.js +4472 -7
  201. package/dist/server/metrics/index.js.map +1 -1
  202. package/dist/server/multipart/index.d.ts +15 -9
  203. package/dist/server/multipart/index.d.ts.map +1 -1
  204. package/dist/server/multipart/index.js +9 -3
  205. package/dist/server/multipart/index.js.map +1 -1
  206. package/dist/server/proxy/index.d.ts +110 -104
  207. package/dist/server/proxy/index.d.ts.map +1 -1
  208. package/dist/server/proxy/index.js +8 -2
  209. package/dist/server/proxy/index.js.map +1 -1
  210. package/dist/server/rate-limit/index.d.ts +46 -51
  211. package/dist/server/rate-limit/index.d.ts.map +1 -1
  212. package/dist/server/rate-limit/index.js +18 -55
  213. package/dist/server/rate-limit/index.js.map +1 -1
  214. package/dist/server/static/index.d.ts +181 -48
  215. package/dist/server/static/index.d.ts.map +1 -1
  216. package/dist/server/static/index.js +1848 -5
  217. package/dist/server/static/index.js.map +1 -1
  218. package/dist/server/swagger/index.d.ts +348 -53
  219. package/dist/server/swagger/index.d.ts.map +1 -1
  220. package/dist/server/swagger/index.js +1849 -6
  221. package/dist/server/swagger/index.js.map +1 -1
  222. package/dist/sms/index.d.ts +312 -18
  223. package/dist/sms/index.d.ts.map +1 -1
  224. package/dist/sms/index.js +1854 -10
  225. package/dist/sms/index.js.map +1 -1
  226. package/dist/system/index.browser.js +496 -0
  227. package/dist/system/index.browser.js.map +1 -0
  228. package/dist/system/index.d.ts +1158 -0
  229. package/dist/system/index.d.ts.map +1 -0
  230. package/dist/{file → system}/index.js +412 -20
  231. package/dist/system/index.js.map +1 -0
  232. package/dist/thread/index.d.ts +82 -73
  233. package/dist/thread/index.d.ts.map +1 -1
  234. package/dist/thread/index.js +13 -4
  235. package/dist/thread/index.js.map +1 -1
  236. package/dist/topic/core/index.d.ts +330 -323
  237. package/dist/topic/core/index.d.ts.map +1 -1
  238. package/dist/topic/core/index.js +12 -5
  239. package/dist/topic/core/index.js.map +1 -1
  240. package/dist/topic/redis/index.d.ts +6 -6
  241. package/dist/topic/redis/index.d.ts.map +1 -1
  242. package/dist/vite/index.d.ts +163 -5825
  243. package/dist/vite/index.d.ts.map +1 -1
  244. package/dist/vite/index.js +130 -477
  245. package/dist/vite/index.js.map +1 -1
  246. package/dist/websocket/index.browser.js +3 -3
  247. package/dist/websocket/index.browser.js.map +1 -1
  248. package/dist/websocket/index.d.ts +287 -283
  249. package/dist/websocket/index.d.ts.map +1 -1
  250. package/dist/websocket/index.js +15 -11
  251. package/dist/websocket/index.js.map +1 -1
  252. package/package.json +86 -17
  253. package/src/api/audits/index.ts +10 -33
  254. package/src/api/files/__tests__/$bucket.spec.ts +1 -1
  255. package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
  256. package/src/api/files/controllers/FileController.spec.ts +1 -1
  257. package/src/api/files/index.ts +10 -3
  258. package/src/api/files/jobs/FileJobs.spec.ts +1 -1
  259. package/src/api/files/services/FileService.spec.ts +1 -1
  260. package/src/api/jobs/index.ts +10 -3
  261. package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
  262. package/src/api/keys/controllers/ApiKeyController.ts +103 -0
  263. package/src/api/keys/entities/apiKeyEntity.ts +41 -0
  264. package/src/api/keys/index.ts +49 -0
  265. package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
  266. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
  267. package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
  268. package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
  269. package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
  270. package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
  271. package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
  272. package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
  273. package/src/api/keys/services/ApiKeyService.ts +306 -0
  274. package/src/api/logs/TODO.md +52 -0
  275. package/src/api/notifications/index.ts +10 -4
  276. package/src/api/parameters/index.ts +9 -30
  277. package/src/api/parameters/primitives/$config.ts +12 -4
  278. package/src/api/parameters/services/ConfigStore.ts +9 -3
  279. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
  280. package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
  281. package/src/api/users/index.ts +14 -3
  282. package/src/api/users/primitives/$realm.ts +33 -5
  283. package/src/api/users/providers/RealmProvider.ts +1 -12
  284. package/src/api/users/services/SessionService.ts +1 -11
  285. package/src/api/verifications/controllers/VerificationController.ts +2 -0
  286. package/src/api/verifications/index.ts +10 -4
  287. package/src/batch/index.ts +9 -36
  288. package/src/batch/primitives/$batch.ts +0 -8
  289. package/src/batch/providers/BatchProvider.ts +29 -2
  290. package/src/bucket/__tests__/shared.ts +1 -1
  291. package/src/bucket/index.ts +13 -6
  292. package/src/bucket/primitives/$bucket.ts +1 -1
  293. package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
  294. package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
  295. package/src/cache/core/__tests__/shared.ts +30 -0
  296. package/src/cache/core/index.ts +11 -6
  297. package/src/cache/core/primitives/$cache.spec.ts +5 -0
  298. package/src/cache/core/providers/CacheProvider.ts +17 -0
  299. package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
  300. package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
  301. package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
  302. package/src/cli/apps/AlephaCli.ts +3 -16
  303. package/src/cli/apps/AlephaPackageBuilderCli.ts +10 -2
  304. package/src/cli/atoms/appEntryOptions.ts +13 -0
  305. package/src/cli/atoms/buildOptions.ts +1 -1
  306. package/src/cli/atoms/changelogOptions.ts +1 -1
  307. package/src/cli/commands/build.ts +64 -52
  308. package/src/cli/commands/db.ts +17 -11
  309. package/src/cli/commands/deploy.ts +1 -1
  310. package/src/cli/commands/dev.ts +13 -49
  311. package/src/cli/commands/gen/env.ts +6 -3
  312. package/src/cli/commands/gen/openapi.ts +5 -2
  313. package/src/cli/commands/init.spec.ts +544 -0
  314. package/src/cli/commands/init.ts +101 -58
  315. package/src/cli/commands/lint.ts +8 -2
  316. package/src/cli/commands/typecheck.ts +11 -0
  317. package/src/cli/defineConfig.ts +9 -0
  318. package/src/cli/index.ts +2 -1
  319. package/src/cli/providers/AppEntryProvider.ts +131 -0
  320. package/src/cli/providers/ViteBuildProvider.ts +40 -0
  321. package/src/cli/providers/ViteDevServerProvider.ts +378 -0
  322. package/src/cli/services/AlephaCliUtils.ts +39 -93
  323. package/src/cli/services/PackageManagerUtils.ts +140 -17
  324. package/src/cli/services/ProjectScaffolder.ts +169 -101
  325. package/src/cli/services/ViteUtils.ts +82 -0
  326. package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +41 -28
  327. package/src/cli/{assets → templates}/apiHelloControllerTs.ts +2 -1
  328. package/src/cli/{assets → templates}/biomeJson.ts +2 -1
  329. package/src/cli/{assets → templates}/dummySpecTs.ts +2 -1
  330. package/src/cli/{assets → templates}/editorconfig.ts +2 -1
  331. package/src/cli/templates/gitignore.ts +39 -0
  332. package/src/cli/{assets → templates}/mainBrowserTs.ts +2 -1
  333. package/src/cli/templates/mainCss.ts +33 -0
  334. package/src/cli/templates/mainServerTs.ts +33 -0
  335. package/src/cli/{assets → templates}/tsconfigJson.ts +2 -1
  336. package/src/cli/templates/webAppRouterTs.ts +50 -0
  337. package/src/cli/templates/webHelloComponentTsx.ts +20 -0
  338. package/src/command/helpers/Runner.spec.ts +4 -0
  339. package/src/command/helpers/Runner.ts +3 -21
  340. package/src/command/index.ts +12 -4
  341. package/src/command/providers/CliProvider.spec.ts +1067 -0
  342. package/src/command/providers/CliProvider.ts +203 -40
  343. package/src/core/Alepha.ts +3 -9
  344. package/src/core/__tests__/Alepha-start.spec.ts +4 -4
  345. package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
  346. package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
  347. package/src/core/index.shared.ts +1 -0
  348. package/src/core/index.ts +20 -0
  349. package/src/core/primitives/$module.ts +12 -0
  350. package/src/core/providers/EventManager.spec.ts +0 -71
  351. package/src/core/providers/EventManager.ts +3 -15
  352. package/src/core/providers/Json.ts +2 -14
  353. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +257 -0
  354. package/src/core/providers/KeylessJsonSchemaCodec.ts +396 -14
  355. package/src/core/providers/SchemaValidator.spec.ts +236 -0
  356. package/src/datetime/index.ts +15 -0
  357. package/src/email/index.ts +10 -5
  358. package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
  359. package/src/email/providers/LocalEmailProvider.ts +1 -1
  360. package/src/fake/__tests__/keyName.example.ts +1 -1
  361. package/src/fake/__tests__/keyName.spec.ts +5 -5
  362. package/src/fake/index.ts +9 -6
  363. package/src/fake/providers/FakeProvider.spec.ts +258 -40
  364. package/src/fake/providers/FakeProvider.ts +133 -19
  365. package/src/lock/core/index.ts +11 -4
  366. package/src/logger/index.ts +17 -66
  367. package/src/logger/providers/PrettyFormatterProvider.ts +0 -9
  368. package/src/mcp/errors/McpError.ts +30 -0
  369. package/src/mcp/index.ts +13 -27
  370. package/src/mcp/transports/SseMcpTransport.ts +6 -7
  371. package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
  372. package/src/orm/index.browser.ts +2 -2
  373. package/src/orm/index.bun.ts +4 -2
  374. package/src/orm/index.ts +21 -47
  375. package/src/orm/providers/DrizzleKitProvider.ts +3 -5
  376. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -0
  377. package/src/orm/services/Repository.ts +18 -3
  378. package/src/queue/core/index.ts +14 -6
  379. package/src/react/auth/__tests__/$auth.spec.ts +202 -0
  380. package/src/react/auth/hooks/useAuth.ts +32 -0
  381. package/src/react/auth/index.browser.ts +13 -0
  382. package/src/react/auth/index.shared.ts +2 -0
  383. package/src/react/auth/index.ts +48 -0
  384. package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
  385. package/src/react/auth/services/ReactAuth.ts +135 -0
  386. package/src/react/core/__tests__/Router.spec.tsx +169 -0
  387. package/src/react/core/components/ClientOnly.tsx +49 -0
  388. package/src/react/core/components/ErrorBoundary.tsx +73 -0
  389. package/src/react/core/contexts/AlephaContext.ts +7 -0
  390. package/src/react/core/contexts/AlephaProvider.tsx +42 -0
  391. package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
  392. package/src/react/core/hooks/useAction.ts +480 -0
  393. package/src/react/core/hooks/useAlepha.ts +26 -0
  394. package/src/react/core/hooks/useClient.ts +17 -0
  395. package/src/react/core/hooks/useEvents.ts +51 -0
  396. package/src/react/core/hooks/useInject.ts +12 -0
  397. package/src/react/core/hooks/useStore.ts +52 -0
  398. package/src/react/core/index.ts +90 -0
  399. package/src/react/form/components/FormState.tsx +17 -0
  400. package/src/react/form/errors/FormValidationError.ts +18 -0
  401. package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
  402. package/src/react/form/hooks/useForm.ts +47 -0
  403. package/src/react/form/hooks/useFormState.ts +130 -0
  404. package/src/react/form/index.ts +44 -0
  405. package/src/react/form/services/FormModel.ts +614 -0
  406. package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
  407. package/src/react/head/helpers/SeoExpander.ts +142 -0
  408. package/src/react/head/hooks/useHead.spec.tsx +288 -0
  409. package/src/react/head/hooks/useHead.ts +62 -0
  410. package/src/react/head/index.browser.ts +26 -0
  411. package/src/react/head/index.ts +44 -0
  412. package/src/react/head/interfaces/Head.ts +105 -0
  413. package/src/react/head/primitives/$head.ts +25 -0
  414. package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
  415. package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
  416. package/src/react/head/providers/HeadProvider.ts +168 -0
  417. package/src/react/head/providers/ServerHeadProvider.ts +31 -0
  418. package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
  419. package/src/react/i18n/components/Localize.spec.tsx +357 -0
  420. package/src/react/i18n/components/Localize.tsx +35 -0
  421. package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
  422. package/src/react/i18n/hooks/useI18n.ts +18 -0
  423. package/src/react/i18n/index.ts +41 -0
  424. package/src/react/i18n/primitives/$dictionary.ts +69 -0
  425. package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
  426. package/src/react/i18n/providers/I18nProvider.ts +278 -0
  427. package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
  428. package/src/react/router/__tests__/page-head.spec.ts +48 -0
  429. package/src/react/router/__tests__/seo-head.spec.ts +125 -0
  430. package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
  431. package/src/react/router/components/ErrorViewer.tsx +872 -0
  432. package/src/react/router/components/Link.tsx +23 -0
  433. package/src/react/router/components/NestedView.tsx +223 -0
  434. package/src/react/router/components/NotFound.tsx +30 -0
  435. package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  436. package/src/react/router/contexts/RouterLayerContext.ts +12 -0
  437. package/src/react/router/errors/Redirection.ts +28 -0
  438. package/src/react/router/hooks/useActive.ts +52 -0
  439. package/src/react/router/hooks/useQueryParams.ts +63 -0
  440. package/src/react/router/hooks/useRouter.ts +20 -0
  441. package/src/react/router/hooks/useRouterState.ts +11 -0
  442. package/src/react/router/index.browser.ts +45 -0
  443. package/src/react/router/index.shared.ts +19 -0
  444. package/src/react/router/index.ts +142 -0
  445. package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
  446. package/src/react/router/primitives/$page.spec.tsx +708 -0
  447. package/src/react/router/primitives/$page.ts +497 -0
  448. package/src/react/router/providers/ReactBrowserProvider.ts +309 -0
  449. package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
  450. package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
  451. package/src/react/router/providers/ReactPageProvider.ts +726 -0
  452. package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
  453. package/src/react/router/providers/ReactServerProvider.ts +558 -0
  454. package/src/react/router/providers/ReactServerTemplateProvider.ts +979 -0
  455. package/src/react/router/providers/SSRManifestProvider.ts +334 -0
  456. package/src/react/router/services/ReactPageServerService.ts +48 -0
  457. package/src/react/router/services/ReactPageService.ts +27 -0
  458. package/src/react/router/services/ReactRouter.ts +262 -0
  459. package/src/react/websocket/hooks/useRoom.tsx +242 -0
  460. package/src/react/websocket/index.ts +7 -0
  461. package/src/redis/__tests__/redis.spec.ts +13 -0
  462. package/src/redis/index.ts +9 -25
  463. package/src/redis/providers/BunRedisProvider.ts +9 -0
  464. package/src/redis/providers/NodeRedisProvider.ts +8 -0
  465. package/src/redis/providers/RedisProvider.ts +16 -0
  466. package/src/retry/index.ts +11 -2
  467. package/src/router/index.ts +15 -0
  468. package/src/scheduler/index.ts +11 -2
  469. package/src/security/__tests__/BasicAuth.spec.ts +2 -0
  470. package/src/security/__tests__/ServerSecurityProvider.spec.ts +13 -5
  471. package/src/security/index.ts +15 -10
  472. package/src/security/interfaces/IssuerResolver.ts +27 -0
  473. package/src/security/primitives/$issuer.ts +55 -0
  474. package/src/security/providers/SecurityProvider.ts +179 -0
  475. package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
  476. package/src/security/providers/ServerSecurityProvider.ts +36 -22
  477. package/src/server/auth/index.ts +12 -7
  478. package/src/server/cache/index.ts +7 -22
  479. package/src/server/compress/index.ts +10 -2
  480. package/src/server/cookies/index.ts +7 -5
  481. package/src/server/cookies/primitives/$cookie.ts +33 -11
  482. package/src/server/core/index.ts +17 -7
  483. package/src/server/core/interfaces/ServerRequest.ts +83 -1
  484. package/src/server/core/primitives/$action.spec.ts +1 -1
  485. package/src/server/core/primitives/$action.ts +8 -3
  486. package/src/server/core/providers/BunHttpServerProvider.ts +1 -1
  487. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +125 -0
  488. package/src/server/core/providers/NodeHttpServerProvider.ts +77 -22
  489. package/src/server/core/providers/ServerLoggerProvider.ts +2 -2
  490. package/src/server/core/providers/ServerProvider.ts +9 -12
  491. package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
  492. package/src/server/core/services/ServerRequestParser.ts +306 -13
  493. package/src/server/cors/index.ts +7 -21
  494. package/src/server/cors/primitives/$cors.ts +6 -2
  495. package/src/server/health/index.ts +8 -2
  496. package/src/server/helmet/index.ts +11 -3
  497. package/src/server/links/atoms/apiLinksAtom.ts +7 -0
  498. package/src/server/links/index.browser.ts +2 -0
  499. package/src/server/links/index.ts +13 -6
  500. package/src/server/metrics/index.ts +10 -3
  501. package/src/server/multipart/index.ts +9 -3
  502. package/src/server/proxy/index.ts +8 -2
  503. package/src/server/rate-limit/index.ts +21 -25
  504. package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
  505. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
  506. package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
  507. package/src/server/static/index.ts +8 -2
  508. package/src/server/static/providers/ServerStaticProvider.ts +1 -1
  509. package/src/server/swagger/index.ts +9 -4
  510. package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
  511. package/src/sms/index.ts +9 -5
  512. package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
  513. package/src/sms/providers/LocalSmsProvider.ts +1 -1
  514. package/src/system/index.browser.ts +11 -0
  515. package/src/system/index.ts +62 -0
  516. package/src/{file → system}/providers/FileSystemProvider.ts +16 -0
  517. package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
  518. package/src/system/providers/MemoryShellProvider.ts +164 -0
  519. package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
  520. package/src/{file → system}/providers/NodeFileSystemProvider.ts +36 -0
  521. package/src/system/providers/NodeShellProvider.ts +184 -0
  522. package/src/system/providers/ShellProvider.ts +74 -0
  523. package/src/{file → system}/services/FileDetector.spec.ts +2 -2
  524. package/src/thread/index.ts +11 -2
  525. package/src/topic/core/index.ts +12 -5
  526. package/src/vite/index.ts +3 -2
  527. package/src/vite/tasks/buildClient.ts +2 -8
  528. package/src/vite/tasks/buildServer.ts +84 -21
  529. package/src/vite/tasks/copyAssets.ts +5 -4
  530. package/src/vite/tasks/generateSitemap.ts +64 -23
  531. package/src/vite/tasks/index.ts +0 -2
  532. package/src/vite/tasks/prerenderPages.ts +49 -24
  533. package/src/websocket/index.ts +12 -8
  534. package/dist/file/index.d.ts +0 -839
  535. package/dist/file/index.d.ts.map +0 -1
  536. package/dist/file/index.js.map +0 -1
  537. package/src/cli/assets/indexHtml.ts +0 -15
  538. package/src/cli/assets/mainServerTs.ts +0 -24
  539. package/src/cli/assets/webAppRouterTs.ts +0 -15
  540. package/src/cli/assets/webHelloComponentTsx.ts +0 -16
  541. package/src/cli/commands/format.ts +0 -23
  542. package/src/file/index.ts +0 -43
  543. package/src/vite/helpers/boot.ts +0 -117
  544. package/src/vite/plugins/viteAlephaDev.ts +0 -177
  545. package/src/vite/tasks/devServer.ts +0 -71
  546. package/src/vite/tasks/runAlepha.ts +0 -270
  547. /package/dist/orm/{chunk-DtkW-qnP.js → chunk-DH6iiROE.js} +0 -0
  548. /package/src/cli/{assets → templates}/apiIndexTs.ts +0 -0
  549. /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
  550. /package/src/{file → system}/errors/FileError.ts +0 -0
  551. /package/src/{file → system}/services/FileDetector.ts +0 -0
@@ -1,9 +1,37 @@
1
1
  import { $inject, Alepha } from "alepha";
2
2
  import type { RunnerMethod } from "alepha/command";
3
- import { FileSystemProvider } from "alepha/file";
4
3
  import { $logger } from "alepha/logger";
4
+ import alephaPackageJson from "alepha/package.json" with { type: "json" };
5
+ import { FileSystemProvider } from "alepha/system";
5
6
  import { version } from "../version.ts";
6
7
 
8
+ /**
9
+ * Context information about a workspace root.
10
+ * Used when initializing a package inside a monorepo.
11
+ */
12
+ export interface WorkspaceContext {
13
+ /**
14
+ * Whether we're inside a workspace package.
15
+ */
16
+ isPackage: boolean;
17
+ /**
18
+ * The workspace root directory (e.g., ../.. from packages/my-pkg).
19
+ */
20
+ workspaceRoot: string | null;
21
+ /**
22
+ * Package manager detected at workspace root.
23
+ */
24
+ packageManager: "yarn" | "pnpm" | "npm" | "bun" | null;
25
+ /**
26
+ * Config files present at workspace root.
27
+ */
28
+ config: {
29
+ biomeJson: boolean;
30
+ editorconfig: boolean;
31
+ tsconfigJson: boolean;
32
+ };
33
+ }
34
+
7
35
  /**
8
36
  * Utility service for package manager operations.
9
37
  *
@@ -23,12 +51,9 @@ export class PackageManagerUtils {
23
51
  */
24
52
  public async getPackageManager(
25
53
  root: string,
26
- flags?: { yarn?: boolean; pnpm?: boolean; npm?: boolean; bun?: boolean },
54
+ pm?: "yarn" | "pnpm" | "npm" | "bun",
27
55
  ): Promise<"yarn" | "pnpm" | "npm" | "bun"> {
28
- if (flags?.yarn) return "yarn";
29
- if (flags?.pnpm) return "pnpm";
30
- if (flags?.npm) return "npm";
31
- if (flags?.bun) return "bun";
56
+ if (pm) return pm;
32
57
  if (this.alepha.isBun()) return "bun";
33
58
  if (await this.fs.exists(this.fs.join(root, "bun.lock"))) return "bun";
34
59
  if (await this.fs.exists(this.fs.join(root, "yarn.lock"))) return "yarn";
@@ -37,6 +62,60 @@ export class PackageManagerUtils {
37
62
  return "npm";
38
63
  }
39
64
 
65
+ /**
66
+ * Detect workspace context when inside a monorepo package.
67
+ *
68
+ * Checks if we're inside a workspace package (e.g., packages/my-pkg or apps/my-app)
69
+ * by looking 2 levels up for workspace indicators like lockfiles and config files.
70
+ *
71
+ * @param root - The current package directory
72
+ * @returns Workspace context with root path, PM, and config presence
73
+ */
74
+ public async getWorkspaceContext(root: string): Promise<WorkspaceContext> {
75
+ // Workspace root is 2 levels up (e.g., packages/my-pkg → ..)
76
+ const workspaceRoot = this.fs.join(root, "..", "..");
77
+
78
+ // Check for lockfiles to detect PM
79
+ const [hasYarnLock, hasPnpmLock, hasNpmLock, hasBunLock] =
80
+ await Promise.all([
81
+ this.fs.exists(this.fs.join(workspaceRoot, "yarn.lock")),
82
+ this.fs.exists(this.fs.join(workspaceRoot, "pnpm-lock.yaml")),
83
+ this.fs.exists(this.fs.join(workspaceRoot, "package-lock.json")),
84
+ this.fs.exists(this.fs.join(workspaceRoot, "bun.lock")),
85
+ ]);
86
+
87
+ // Check for config files
88
+ const [hasBiome, hasEditorConfig, hasTsConfig, hasWorkspacePackageJson] =
89
+ await Promise.all([
90
+ this.fs.exists(this.fs.join(workspaceRoot, "biome.json")),
91
+ this.fs.exists(this.fs.join(workspaceRoot, ".editorconfig")),
92
+ this.fs.exists(this.fs.join(workspaceRoot, "tsconfig.json")),
93
+ this.fs.exists(this.fs.join(workspaceRoot, "package.json")),
94
+ ]);
95
+
96
+ // Determine if this looks like a workspace root
97
+ const hasLockfile = hasYarnLock || hasPnpmLock || hasNpmLock || hasBunLock;
98
+ const isPackage = hasLockfile && hasWorkspacePackageJson;
99
+
100
+ // Detect package manager from lockfile
101
+ let packageManager: "yarn" | "pnpm" | "npm" | "bun" | null = null;
102
+ if (hasYarnLock) packageManager = "yarn";
103
+ else if (hasPnpmLock) packageManager = "pnpm";
104
+ else if (hasBunLock) packageManager = "bun";
105
+ else if (hasNpmLock) packageManager = "npm";
106
+
107
+ return {
108
+ isPackage,
109
+ workspaceRoot: isPackage ? workspaceRoot : null,
110
+ packageManager,
111
+ config: {
112
+ biomeJson: hasBiome,
113
+ editorconfig: hasEditorConfig,
114
+ tsconfigJson: hasTsConfig,
115
+ },
116
+ };
117
+ }
118
+
40
119
  /**
41
120
  * Get the install command for a package.
42
121
  */
@@ -89,14 +168,26 @@ export class PackageManagerUtils {
89
168
  return this.hasDependency(root, "expo");
90
169
  }
91
170
 
171
+ /**
172
+ * Check if React is present in the project.
173
+ */
174
+ public async hasReact(root: string): Promise<boolean> {
175
+ return this.hasDependency(root, "react");
176
+ }
177
+
92
178
  /**
93
179
  * Install a dependency if it's missing from the project.
180
+ * Optionally checks workspace root for the dependency in monorepo setups.
94
181
  */
95
182
  public async ensureDependency(
96
183
  root: string,
97
184
  packageName: string,
98
185
  options: {
99
186
  dev?: boolean;
187
+ /**
188
+ * Also check workspace root for the dependency (for monorepo setups).
189
+ */
190
+ checkWorkspace?: boolean;
100
191
  run?: RunnerMethod;
101
192
  exec?: (
102
193
  cmd: string,
@@ -104,17 +195,31 @@ export class PackageManagerUtils {
104
195
  ) => Promise<void>;
105
196
  } = {},
106
197
  ): Promise<void> {
107
- const { dev = true } = options;
198
+ const { dev = true, checkWorkspace = false } = options;
108
199
 
200
+ // Check current package
109
201
  if (await this.hasDependency(root, packageName)) {
110
202
  this.log.debug(`Dependency '${packageName}' is already installed`);
111
203
  return;
112
204
  }
113
205
 
206
+ // Check workspace root (for monorepo setups)
207
+ if (checkWorkspace) {
208
+ const workspace = await this.getWorkspaceContext(root);
209
+ if (workspace.workspaceRoot) {
210
+ if (await this.hasDependency(workspace.workspaceRoot, packageName)) {
211
+ this.log.debug(
212
+ `Dependency '${packageName}' is already installed in workspace root`,
213
+ );
214
+ return;
215
+ }
216
+ }
217
+ }
218
+
114
219
  const cmd = await this.getInstallCommand(root, packageName, dev);
115
220
 
116
221
  if (options.run) {
117
- await options.run(cmd, { alias: `installing ${packageName}`, root });
222
+ await options.run(cmd, { alias: `add ${packageName}`, root });
118
223
  } else if (options.exec) {
119
224
  this.log.debug(`Installing ${packageName}`);
120
225
  await options.exec(cmd, { global: true, root });
@@ -247,12 +352,22 @@ export class PackageManagerUtils {
247
352
  scripts: Record<string, string>;
248
353
  type: "module";
249
354
  } {
355
+ const alephaDeps = alephaPackageJson.devDependencies;
356
+
250
357
  const dependencies: Record<string, string> = {
251
358
  alepha: `^${version}`,
252
359
  };
253
360
 
254
361
  const devDependencies: Record<string, string> = {};
255
362
 
363
+ // Add biome/vitest only if not a workspace package (workspace root has them)
364
+ if (!modes.isPackage) {
365
+ devDependencies["@biomejs/biome"] = alephaDeps["@biomejs/biome"];
366
+ if (modes.test) {
367
+ devDependencies.vitest = alephaDeps.vitest;
368
+ }
369
+ }
370
+
256
371
  const scripts: Record<string, string> = {
257
372
  dev: "alepha dev",
258
373
  build: "alepha build",
@@ -261,16 +376,19 @@ export class PackageManagerUtils {
261
376
  verify: "alepha verify",
262
377
  };
263
378
 
264
- if (modes.admin) {
379
+ if (modes.test) {
380
+ scripts.test = "vitest run";
381
+ }
382
+
383
+ if (modes.ui) {
265
384
  dependencies["@alepha/ui"] = `^${version}`;
266
- modes.web = true;
385
+ modes.react = true;
267
386
  }
268
387
 
269
- if (modes.web) {
270
- dependencies["@alepha/react"] = `^${version}`;
271
- dependencies.react = "^19.2.0";
272
- dependencies["react-dom"] = "^19.2.0";
273
- devDependencies["@types/react"] = "^19.2.0";
388
+ if (modes.react) {
389
+ dependencies.react = alephaDeps.react;
390
+ dependencies["react-dom"] = alephaDeps["react-dom"];
391
+ devDependencies["@types/react"] = alephaDeps["@types/react"];
274
392
  }
275
393
 
276
394
  return {
@@ -295,7 +413,12 @@ export class PackageManagerUtils {
295
413
  }
296
414
 
297
415
  export interface DependencyModes {
298
- web?: boolean;
299
- admin?: boolean;
416
+ react?: boolean;
417
+ ui?: boolean;
300
418
  expo?: boolean;
419
+ test?: boolean;
420
+ /**
421
+ * Skip biome/vitest when inside a workspace package (they're at root).
422
+ */
423
+ isPackage?: boolean;
301
424
  }
@@ -1,20 +1,26 @@
1
1
  import { basename, dirname } from "node:path";
2
2
  import { $inject } from "alepha";
3
- import { FileSystemProvider } from "alepha/file";
4
3
  import { $logger } from "alepha/logger";
5
- import { apiHelloControllerTs } from "../assets/apiHelloControllerTs.ts";
6
- import { apiIndexTs } from "../assets/apiIndexTs.ts";
7
- import { biomeJson } from "../assets/biomeJson.ts";
8
- import { type ClaudeMdOptions, claudeMd } from "../assets/claudeMd.ts";
9
- import { dummySpecTs } from "../assets/dummySpecTs.ts";
10
- import { editorconfig } from "../assets/editorconfig.ts";
11
- import { indexHtml } from "../assets/indexHtml.ts";
12
- import { mainBrowserTs } from "../assets/mainBrowserTs.ts";
13
- import { mainServerTs } from "../assets/mainServerTs.ts";
14
- import { tsconfigJson } from "../assets/tsconfigJson.ts";
15
- import { webAppRouterTs } from "../assets/webAppRouterTs.ts";
16
- import { webHelloComponentTsx } from "../assets/webHelloComponentTsx.ts";
17
- import { webIndexTs } from "../assets/webIndexTs.ts";
4
+ import { FileSystemProvider } from "alepha/system";
5
+ import {
6
+ type AgentMdOptions,
7
+ type AgentMdType,
8
+ agentMd,
9
+ } from "../templates/agentMd.ts";
10
+ import { apiHelloControllerTs } from "../templates/apiHelloControllerTs.ts";
11
+ import { apiIndexTs } from "../templates/apiIndexTs.ts";
12
+ import { biomeJson } from "../templates/biomeJson.ts";
13
+ import { dummySpecTs } from "../templates/dummySpecTs.ts";
14
+ import { editorconfig } from "../templates/editorconfig.ts";
15
+ import { gitignore } from "../templates/gitignore.ts";
16
+ import { mainBrowserTs } from "../templates/mainBrowserTs.ts";
17
+ import { mainCss } from "../templates/mainCss.ts";
18
+ import { mainServerTs } from "../templates/mainServerTs.ts";
19
+ import { tsconfigJson } from "../templates/tsconfigJson.ts";
20
+ import { webAppRouterTs } from "../templates/webAppRouterTs.ts";
21
+ import { webHelloComponentTsx } from "../templates/webHelloComponentTsx.ts";
22
+ import { webIndexTs } from "../templates/webIndexTs.ts";
23
+ import { AlephaCliUtils } from "./AlephaCliUtils.ts";
18
24
  import {
19
25
  type DependencyModes,
20
26
  PackageManagerUtils,
@@ -33,6 +39,7 @@ export class ProjectScaffolder {
33
39
  protected readonly log = $logger();
34
40
  protected readonly fs = $inject(FileSystemProvider);
35
41
  protected readonly pm = $inject(PackageManagerUtils);
42
+ protected readonly utils = $inject(AlephaCliUtils);
36
43
 
37
44
  /**
38
45
  * Get the app name from the directory name.
@@ -54,15 +61,21 @@ export class ProjectScaffolder {
54
61
  public async ensureConfig(
55
62
  root: string,
56
63
  opts: {
64
+ force?: boolean;
65
+ /**
66
+ * Check workspace root for existing config files.
67
+ */
68
+ checkWorkspace?: boolean;
57
69
  packageJson?: boolean | DependencyModes;
58
70
  tsconfigJson?: boolean;
59
- indexHtml?: boolean;
60
71
  biomeJson?: boolean;
61
72
  editorconfig?: boolean;
62
- claudeMd?: boolean | ClaudeMdOptions;
73
+ agentMd?: false | (AgentMdOptions & { type: AgentMdType });
63
74
  },
64
75
  ): Promise<void> {
65
76
  const tasks: Promise<void>[] = [];
77
+ const force = opts.force ?? false;
78
+ const checkWorkspace = opts.checkWorkspace ?? false;
66
79
 
67
80
  if (opts.packageJson) {
68
81
  tasks.push(
@@ -75,24 +88,16 @@ export class ProjectScaffolder {
75
88
  );
76
89
  }
77
90
  if (opts.tsconfigJson) {
78
- tasks.push(this.ensureTsConfig(root));
79
- }
80
- if (opts.indexHtml) {
81
- tasks.push(this.ensureReactProject(root));
91
+ tasks.push(this.ensureTsConfig(root, { force }));
82
92
  }
83
93
  if (opts.biomeJson) {
84
- tasks.push(this.ensureBiomeConfig(root));
94
+ tasks.push(this.ensureBiomeConfig(root, { force, checkWorkspace }));
85
95
  }
86
96
  if (opts.editorconfig) {
87
- tasks.push(this.ensureEditorConfig(root));
97
+ tasks.push(this.ensureEditorConfig(root, { force, checkWorkspace }));
88
98
  }
89
- if (opts.claudeMd) {
90
- tasks.push(
91
- this.ensureClaudeMd(
92
- root,
93
- typeof opts.claudeMd === "boolean" ? {} : opts.claudeMd,
94
- ),
95
- );
99
+ if (opts.agentMd) {
100
+ tasks.push(this.ensureAgentMd(root, { ...opts.agentMd, force }));
96
101
  }
97
102
 
98
103
  await Promise.all(tasks);
@@ -102,30 +107,111 @@ export class ProjectScaffolder {
102
107
  // Config Files
103
108
  // ===========================================
104
109
 
105
- public async ensureTsConfig(root: string): Promise<void> {
110
+ public async ensureTsConfig(
111
+ root: string,
112
+ opts: { force?: boolean } = {},
113
+ ): Promise<void> {
106
114
  // Check if tsconfig.json exists in current or parent directories
107
- if (await this.existsInParents(root, "tsconfig.json")) {
115
+ if (!opts.force && (await this.existsInParents(root, "tsconfig.json"))) {
108
116
  return;
109
117
  }
110
- await this.fs.writeFile(this.fs.join(root, "tsconfig.json"), tsconfigJson);
118
+ await this.fs.writeFile(
119
+ this.fs.join(root, "tsconfig.json"),
120
+ tsconfigJson(),
121
+ );
111
122
  }
112
123
 
113
- public async ensureBiomeConfig(root: string): Promise<void> {
114
- await this.ensureFileIfNotExists(root, "biome.json", biomeJson);
124
+ public async ensureBiomeConfig(
125
+ root: string,
126
+ opts: { force?: boolean; checkWorkspace?: boolean } = {},
127
+ ): Promise<void> {
128
+ if (
129
+ !opts.force &&
130
+ opts.checkWorkspace &&
131
+ (await this.existsInParents(root, "biome.json"))
132
+ ) {
133
+ return;
134
+ }
135
+ await this.ensureFile(root, "biome.json", biomeJson(), opts.force);
115
136
  }
116
137
 
117
- public async ensureEditorConfig(root: string): Promise<void> {
118
- await this.ensureFileIfNotExists(root, ".editorconfig", editorconfig);
138
+ public async ensureEditorConfig(
139
+ root: string,
140
+ opts: { force?: boolean; checkWorkspace?: boolean } = {},
141
+ ): Promise<void> {
142
+ if (
143
+ !opts.force &&
144
+ opts.checkWorkspace &&
145
+ (await this.existsInParents(root, ".editorconfig"))
146
+ ) {
147
+ return;
148
+ }
149
+ await this.ensureFile(root, ".editorconfig", editorconfig(), opts.force);
119
150
  }
120
151
 
121
- public async ensureClaudeMd(
152
+ /**
153
+ * Ensure git repository is initialized with .gitignore.
154
+ *
155
+ * @returns true if git was initialized, false if already exists or git unavailable
156
+ */
157
+ public async ensureGitRepo(
122
158
  root: string,
123
- options: ClaudeMdOptions = {},
124
- ): Promise<void> {
125
- const path = this.fs.join(root, "CLAUDE.md");
126
- if (!(await this.fs.exists(path))) {
127
- await this.fs.writeFile(path, claudeMd(options));
159
+ opts: { force?: boolean } = {},
160
+ ): Promise<boolean> {
161
+ const gitDir = this.fs.join(root, ".git");
162
+
163
+ // Skip if .git already exists
164
+ if (!opts.force && (await this.fs.exists(gitDir))) {
165
+ return false;
128
166
  }
167
+
168
+ // Check if git is available
169
+ const hasGit = await this.utils.isInstalledAsync("git");
170
+ if (!hasGit) {
171
+ return false;
172
+ }
173
+
174
+ // Initialize git repository
175
+ await this.utils.exec("git init", { root, global: true });
176
+
177
+ // Write .gitignore
178
+ await this.ensureFile(root, ".gitignore", gitignore(), opts.force);
179
+
180
+ return true;
181
+ }
182
+
183
+ public async ensureAgentMd(
184
+ root: string,
185
+ options: AgentMdOptions & { type: AgentMdType; force?: boolean },
186
+ ): Promise<void> {
187
+ const filename = options.type === "claude" ? "CLAUDE.md" : "AGENTS.md";
188
+ await this.ensureFile(
189
+ root,
190
+ filename,
191
+ agentMd(options.type, options),
192
+ options.force,
193
+ );
194
+ }
195
+
196
+ // ===========================================
197
+ // Minimal Project Structure
198
+ // ===========================================
199
+
200
+ /**
201
+ * Ensure src/main.server.ts exists with correct module imports.
202
+ */
203
+ public async ensureMainServerTs(
204
+ root: string,
205
+ opts: { api?: boolean; react?: boolean; force?: boolean } = {},
206
+ ): Promise<void> {
207
+ const srcDir = this.fs.join(root, "src");
208
+ await this.fs.mkdir(srcDir, { recursive: true });
209
+ await this.ensureFile(
210
+ srcDir,
211
+ "main.server.ts",
212
+ mainServerTs({ api: opts.api, react: opts.react }),
213
+ opts.force,
214
+ );
129
215
  }
130
216
 
131
217
  // ===========================================
@@ -133,22 +219,16 @@ export class ProjectScaffolder {
133
219
  // ===========================================
134
220
 
135
221
  /**
136
- * Ensure src/main.server.ts exists with full API structure.
222
+ * Ensure API module structure exists.
137
223
  *
138
224
  * Creates:
139
- * - src/main.server.ts (entry point)
140
225
  * - src/api/index.ts (API module)
141
226
  * - src/api/controllers/HelloController.ts (example controller)
142
227
  */
143
- public async ensureApiProject(root: string): Promise<void> {
144
- const srcDir = this.fs.join(root, "src");
145
-
146
- // Don't overwrite existing content
147
- if (await this.fs.exists(srcDir)) {
148
- const files = await this.fs.ls(srcDir);
149
- if (files.length > 0) return;
150
- }
151
-
228
+ public async ensureApiProject(
229
+ root: string,
230
+ opts: { force?: boolean } = {},
231
+ ): Promise<void> {
152
232
  const appName = this.getAppName(root);
153
233
 
154
234
  // Create directories
@@ -157,91 +237,75 @@ export class ProjectScaffolder {
157
237
  });
158
238
 
159
239
  // Create files
160
- await this.fs.writeFile(
161
- this.fs.join(srcDir, "main.server.ts"),
162
- mainServerTs(),
163
- );
164
- await this.fs.writeFile(
165
- this.fs.join(srcDir, "api/index.ts"),
240
+ await this.ensureFile(
241
+ root,
242
+ "src/api/index.ts",
166
243
  apiIndexTs({ appName }),
244
+ opts.force,
167
245
  );
168
- await this.fs.writeFile(
169
- this.fs.join(srcDir, "api/controllers/HelloController.ts"),
246
+ await this.ensureFile(
247
+ root,
248
+ "src/api/controllers/HelloController.ts",
170
249
  apiHelloControllerTs(),
250
+ opts.force,
171
251
  );
172
252
  }
173
253
 
174
254
  // ===========================================
175
- // React Project Structure
255
+ // Web Project Structure
176
256
  // ===========================================
177
257
 
178
258
  /**
179
- * Ensure full React project structure exists.
259
+ * Ensure web/React project structure exists.
180
260
  *
181
261
  * Creates:
182
- * - index.html
183
- * - src/main.server.ts, src/main.browser.ts
184
- * - src/api/index.ts, src/api/controllers/HelloController.ts
262
+ * - src/main.browser.ts
263
+ * - src/main.css
185
264
  * - src/web/index.ts, src/web/AppRouter.ts, src/web/components/Hello.tsx
186
265
  */
187
- public async ensureReactProject(root: string): Promise<void> {
188
- if (await this.fs.exists(this.fs.join(root, "index.html"))) {
189
- return;
190
- }
191
-
266
+ public async ensureWebProject(
267
+ root: string,
268
+ opts: { api?: boolean; ui?: boolean; force?: boolean } = {},
269
+ ): Promise<void> {
192
270
  const appName = this.getAppName(root);
193
271
 
194
272
  // Create directories
195
- await this.fs.mkdir(this.fs.join(root, "src/api/controllers"), {
196
- recursive: true,
197
- });
198
273
  await this.fs.mkdir(this.fs.join(root, "src/web/components"), {
199
274
  recursive: true,
200
275
  });
201
276
 
202
- // index.html
203
- await this.fs.writeFile(
204
- this.fs.join(root, "index.html"),
205
- indexHtml("src/main.browser.ts"),
206
- );
207
-
208
- // API structure
209
- await this.ensureFileIfNotExists(
277
+ // src/main.css
278
+ await this.ensureFile(
210
279
  root,
211
- "src/api/index.ts",
212
- apiIndexTs({ appName }),
213
- );
214
- await this.ensureFileIfNotExists(
215
- root,
216
- "src/api/controllers/HelloController.ts",
217
- apiHelloControllerTs(),
218
- );
219
- await this.ensureFileIfNotExists(
220
- root,
221
- "src/main.server.ts",
222
- mainServerTs({ react: true }),
280
+ "src/main.css",
281
+ mainCss({ ui: opts.ui }),
282
+ opts.force,
223
283
  );
224
284
 
225
285
  // Web structure
226
- await this.ensureFileIfNotExists(
286
+ await this.ensureFile(
227
287
  root,
228
288
  "src/web/index.ts",
229
289
  webIndexTs({ appName }),
290
+ opts.force,
230
291
  );
231
- await this.ensureFileIfNotExists(
292
+ await this.ensureFile(
232
293
  root,
233
294
  "src/web/AppRouter.ts",
234
- webAppRouterTs(),
295
+ webAppRouterTs({ api: opts.api, ui: opts.ui }),
296
+ opts.force,
235
297
  );
236
- await this.ensureFileIfNotExists(
298
+ await this.ensureFile(
237
299
  root,
238
300
  "src/web/components/Hello.tsx",
239
301
  webHelloComponentTsx(),
302
+ opts.force,
240
303
  );
241
- await this.ensureFileIfNotExists(
304
+ await this.ensureFile(
242
305
  root,
243
306
  "src/main.browser.ts",
244
307
  mainBrowserTs(),
308
+ opts.force,
245
309
  );
246
310
  }
247
311
 
@@ -272,13 +336,17 @@ export class ProjectScaffolder {
272
336
  // Helpers
273
337
  // ===========================================
274
338
 
275
- protected async ensureFileIfNotExists(
339
+ /**
340
+ * Write a file, optionally overriding if it exists.
341
+ */
342
+ protected async ensureFile(
276
343
  root: string,
277
344
  relativePath: string,
278
345
  content: string,
346
+ force?: boolean,
279
347
  ): Promise<void> {
280
348
  const fullPath = this.fs.join(root, relativePath);
281
- if (!(await this.fs.exists(fullPath))) {
349
+ if (force || !(await this.fs.exists(fullPath))) {
282
350
  await this.fs.writeFile(fullPath, content);
283
351
  }
284
352
  }