alepha 0.15.1 → 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 (507) hide show
  1. package/dist/api/audits/index.d.ts +342 -365
  2. package/dist/api/audits/index.d.ts.map +1 -1
  3. package/dist/api/audits/index.js +10 -33
  4. package/dist/api/audits/index.js.map +1 -1
  5. package/dist/api/files/index.d.ts +180 -173
  6. package/dist/api/files/index.d.ts.map +1 -1
  7. package/dist/api/files/index.js +10 -3
  8. package/dist/api/files/index.js.map +1 -1
  9. package/dist/api/jobs/index.d.ts +162 -155
  10. package/dist/api/jobs/index.d.ts.map +1 -1
  11. package/dist/api/jobs/index.js +10 -3
  12. package/dist/api/jobs/index.js.map +1 -1
  13. package/dist/api/keys/index.d.ts +413 -0
  14. package/dist/api/keys/index.d.ts.map +1 -0
  15. package/dist/api/keys/index.js +476 -0
  16. package/dist/api/keys/index.js.map +1 -0
  17. package/dist/api/notifications/index.d.ts +10 -4
  18. package/dist/api/notifications/index.d.ts.map +1 -1
  19. package/dist/api/notifications/index.js +10 -4
  20. package/dist/api/notifications/index.js.map +1 -1
  21. package/dist/api/parameters/index.d.ts +294 -301
  22. package/dist/api/parameters/index.d.ts.map +1 -1
  23. package/dist/api/parameters/index.js +30 -37
  24. package/dist/api/parameters/index.js.map +1 -1
  25. package/dist/api/users/index.d.ts +1079 -769
  26. package/dist/api/users/index.d.ts.map +1 -1
  27. package/dist/api/users/index.js +2534 -218
  28. package/dist/api/users/index.js.map +1 -1
  29. package/dist/api/verifications/index.d.ts +10 -4
  30. package/dist/api/verifications/index.d.ts.map +1 -1
  31. package/dist/api/verifications/index.js +12 -4
  32. package/dist/api/verifications/index.js.map +1 -1
  33. package/dist/batch/index.d.ts +20 -40
  34. package/dist/batch/index.d.ts.map +1 -1
  35. package/dist/batch/index.js +31 -44
  36. package/dist/batch/index.js.map +1 -1
  37. package/dist/bucket/index.d.ts +432 -8
  38. package/dist/bucket/index.d.ts.map +1 -1
  39. package/dist/bucket/index.js +1856 -12
  40. package/dist/bucket/index.js.map +1 -1
  41. package/dist/cache/core/index.d.ts +179 -7
  42. package/dist/cache/core/index.d.ts.map +1 -1
  43. package/dist/cache/core/index.js +213 -7
  44. package/dist/cache/core/index.js.map +1 -1
  45. package/dist/cache/redis/index.d.ts +1 -0
  46. package/dist/cache/redis/index.d.ts.map +1 -1
  47. package/dist/cache/redis/index.js +4 -0
  48. package/dist/cache/redis/index.js.map +1 -1
  49. package/dist/cli/index.d.ts +488 -5612
  50. package/dist/cli/index.d.ts.map +1 -1
  51. package/dist/cli/index.js +2326 -311
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/command/index.d.ts +194 -46
  54. package/dist/command/index.d.ts.map +1 -1
  55. package/dist/command/index.js +1995 -60
  56. package/dist/command/index.js.map +1 -1
  57. package/dist/core/index.browser.js +42 -19
  58. package/dist/core/index.browser.js.map +1 -1
  59. package/dist/core/index.d.ts +34 -13
  60. package/dist/core/index.d.ts.map +1 -1
  61. package/dist/core/index.js +62 -19
  62. package/dist/core/index.js.map +1 -1
  63. package/dist/core/index.native.js +42 -19
  64. package/dist/core/index.native.js.map +1 -1
  65. package/dist/datetime/index.d.ts +15 -0
  66. package/dist/datetime/index.d.ts.map +1 -1
  67. package/dist/datetime/index.js +15 -0
  68. package/dist/datetime/index.js.map +1 -1
  69. package/dist/email/index.d.ts +315 -20
  70. package/dist/email/index.d.ts.map +1 -1
  71. package/dist/email/index.js +1852 -7
  72. package/dist/email/index.js.map +1 -1
  73. package/dist/fake/index.d.ts +90 -8
  74. package/dist/fake/index.d.ts.map +1 -1
  75. package/dist/fake/index.js +91 -20
  76. package/dist/fake/index.js.map +1 -1
  77. package/dist/lock/core/index.d.ts +11 -4
  78. package/dist/lock/core/index.d.ts.map +1 -1
  79. package/dist/lock/core/index.js +11 -4
  80. package/dist/lock/core/index.js.map +1 -1
  81. package/dist/logger/index.d.ts +17 -66
  82. package/dist/logger/index.d.ts.map +1 -1
  83. package/dist/logger/index.js +14 -63
  84. package/dist/logger/index.js.map +1 -1
  85. package/dist/mcp/index.d.ts +15 -35
  86. package/dist/mcp/index.d.ts.map +1 -1
  87. package/dist/mcp/index.js +12 -35
  88. package/dist/mcp/index.js.map +1 -1
  89. package/dist/orm/index.browser.js +3 -3
  90. package/dist/orm/index.browser.js.map +1 -1
  91. package/dist/orm/index.bun.js +7 -4
  92. package/dist/orm/index.bun.js.map +1 -1
  93. package/dist/orm/index.d.ts +514 -540
  94. package/dist/orm/index.d.ts.map +1 -1
  95. package/dist/orm/index.js +24 -49
  96. package/dist/orm/index.js.map +1 -1
  97. package/dist/queue/core/index.d.ts +18 -10
  98. package/dist/queue/core/index.d.ts.map +1 -1
  99. package/dist/queue/core/index.js +14 -6
  100. package/dist/queue/core/index.js.map +1 -1
  101. package/dist/react/auth/index.browser.js +108 -0
  102. package/dist/react/auth/index.browser.js.map +1 -0
  103. package/dist/react/auth/index.d.ts +100 -0
  104. package/dist/react/auth/index.d.ts.map +1 -0
  105. package/dist/react/auth/index.js +145 -0
  106. package/dist/react/auth/index.js.map +1 -0
  107. package/dist/react/core/index.d.ts +469 -0
  108. package/dist/react/core/index.d.ts.map +1 -0
  109. package/dist/react/core/index.js +464 -0
  110. package/dist/react/core/index.js.map +1 -0
  111. package/dist/react/form/index.d.ts +232 -0
  112. package/dist/react/form/index.d.ts.map +1 -0
  113. package/dist/react/form/index.js +432 -0
  114. package/dist/react/form/index.js.map +1 -0
  115. package/dist/react/head/index.browser.js +423 -0
  116. package/dist/react/head/index.browser.js.map +1 -0
  117. package/dist/react/head/index.d.ts +288 -0
  118. package/dist/react/head/index.d.ts.map +1 -0
  119. package/dist/react/head/index.js +465 -0
  120. package/dist/react/head/index.js.map +1 -0
  121. package/dist/react/i18n/index.d.ts +175 -0
  122. package/dist/react/i18n/index.d.ts.map +1 -0
  123. package/dist/react/i18n/index.js +224 -0
  124. package/dist/react/i18n/index.js.map +1 -0
  125. package/dist/react/router/index.browser.js +1980 -0
  126. package/dist/react/router/index.browser.js.map +1 -0
  127. package/dist/react/router/index.d.ts +2068 -0
  128. package/dist/react/router/index.d.ts.map +1 -0
  129. package/dist/react/router/index.js +4932 -0
  130. package/dist/react/router/index.js.map +1 -0
  131. package/dist/react/websocket/index.d.ts +117 -0
  132. package/dist/react/websocket/index.d.ts.map +1 -0
  133. package/dist/react/websocket/index.js +107 -0
  134. package/dist/react/websocket/index.js.map +1 -0
  135. package/dist/redis/index.bun.js +4 -0
  136. package/dist/redis/index.bun.js.map +1 -1
  137. package/dist/redis/index.d.ts +22 -25
  138. package/dist/redis/index.d.ts.map +1 -1
  139. package/dist/redis/index.js +16 -25
  140. package/dist/redis/index.js.map +1 -1
  141. package/dist/retry/index.d.ts +11 -2
  142. package/dist/retry/index.d.ts.map +1 -1
  143. package/dist/retry/index.js +11 -2
  144. package/dist/retry/index.js.map +1 -1
  145. package/dist/scheduler/index.d.ts +11 -2
  146. package/dist/scheduler/index.d.ts.map +1 -1
  147. package/dist/scheduler/index.js +11 -2
  148. package/dist/scheduler/index.js.map +1 -1
  149. package/dist/security/index.d.ts +110 -19
  150. package/dist/security/index.d.ts.map +1 -1
  151. package/dist/security/index.js +157 -26
  152. package/dist/security/index.js.map +1 -1
  153. package/dist/server/auth/index.d.ts +179 -174
  154. package/dist/server/auth/index.d.ts.map +1 -1
  155. package/dist/server/auth/index.js +12 -7
  156. package/dist/server/auth/index.js.map +1 -1
  157. package/dist/server/cache/index.d.ts +7 -22
  158. package/dist/server/cache/index.d.ts.map +1 -1
  159. package/dist/server/cache/index.js +7 -22
  160. package/dist/server/cache/index.js.map +1 -1
  161. package/dist/server/compress/index.d.ts +10 -2
  162. package/dist/server/compress/index.d.ts.map +1 -1
  163. package/dist/server/compress/index.js +10 -2
  164. package/dist/server/compress/index.js.map +1 -1
  165. package/dist/server/cookies/index.d.ts +40 -16
  166. package/dist/server/cookies/index.d.ts.map +1 -1
  167. package/dist/server/cookies/index.js +7 -5
  168. package/dist/server/cookies/index.js.map +1 -1
  169. package/dist/server/core/index.d.ts +115 -14
  170. package/dist/server/core/index.d.ts.map +1 -1
  171. package/dist/server/core/index.js +231 -14
  172. package/dist/server/core/index.js.map +1 -1
  173. package/dist/server/cors/index.d.ts +13 -23
  174. package/dist/server/cors/index.d.ts.map +1 -1
  175. package/dist/server/cors/index.js +7 -21
  176. package/dist/server/cors/index.js.map +1 -1
  177. package/dist/server/health/index.d.ts +25 -19
  178. package/dist/server/health/index.d.ts.map +1 -1
  179. package/dist/server/health/index.js +8 -2
  180. package/dist/server/health/index.js.map +1 -1
  181. package/dist/server/helmet/index.d.ts +11 -3
  182. package/dist/server/helmet/index.d.ts.map +1 -1
  183. package/dist/server/helmet/index.js +11 -3
  184. package/dist/server/helmet/index.js.map +1 -1
  185. package/dist/server/links/index.d.ts +50 -45
  186. package/dist/server/links/index.d.ts.map +1 -1
  187. package/dist/server/links/index.js +11 -6
  188. package/dist/server/links/index.js.map +1 -1
  189. package/dist/server/metrics/index.d.ts +10 -3
  190. package/dist/server/metrics/index.d.ts.map +1 -1
  191. package/dist/server/metrics/index.js +10 -3
  192. package/dist/server/metrics/index.js.map +1 -1
  193. package/dist/server/multipart/index.d.ts +9 -3
  194. package/dist/server/multipart/index.d.ts.map +1 -1
  195. package/dist/server/multipart/index.js +9 -3
  196. package/dist/server/multipart/index.js.map +1 -1
  197. package/dist/server/proxy/index.d.ts +8 -2
  198. package/dist/server/proxy/index.d.ts.map +1 -1
  199. package/dist/server/proxy/index.js +8 -2
  200. package/dist/server/proxy/index.js.map +1 -1
  201. package/dist/server/rate-limit/index.d.ts +30 -35
  202. package/dist/server/rate-limit/index.d.ts.map +1 -1
  203. package/dist/server/rate-limit/index.js +18 -55
  204. package/dist/server/rate-limit/index.js.map +1 -1
  205. package/dist/server/static/index.d.ts +137 -4
  206. package/dist/server/static/index.d.ts.map +1 -1
  207. package/dist/server/static/index.js +1848 -5
  208. package/dist/server/static/index.js.map +1 -1
  209. package/dist/server/swagger/index.d.ts +301 -6
  210. package/dist/server/swagger/index.d.ts.map +1 -1
  211. package/dist/server/swagger/index.js +1849 -6
  212. package/dist/server/swagger/index.js.map +1 -1
  213. package/dist/sms/index.d.ts +301 -7
  214. package/dist/sms/index.d.ts.map +1 -1
  215. package/dist/sms/index.js +1851 -7
  216. package/dist/sms/index.js.map +1 -1
  217. package/dist/system/index.browser.js +496 -0
  218. package/dist/system/index.browser.js.map +1 -0
  219. package/dist/{file → system}/index.d.ts +335 -16
  220. package/dist/system/index.d.ts.map +1 -0
  221. package/dist/{file → system}/index.js +412 -20
  222. package/dist/system/index.js.map +1 -0
  223. package/dist/thread/index.d.ts +11 -2
  224. package/dist/thread/index.d.ts.map +1 -1
  225. package/dist/thread/index.js +11 -2
  226. package/dist/thread/index.js.map +1 -1
  227. package/dist/topic/core/index.d.ts +12 -5
  228. package/dist/topic/core/index.d.ts.map +1 -1
  229. package/dist/topic/core/index.js +12 -5
  230. package/dist/topic/core/index.js.map +1 -1
  231. package/dist/vite/index.d.ts +4 -6271
  232. package/dist/vite/index.d.ts.map +1 -1
  233. package/dist/vite/index.js +8 -3
  234. package/dist/vite/index.js.map +1 -1
  235. package/dist/websocket/index.d.ts +12 -8
  236. package/dist/websocket/index.d.ts.map +1 -1
  237. package/dist/websocket/index.js +12 -8
  238. package/dist/websocket/index.js.map +1 -1
  239. package/package.json +80 -11
  240. package/src/api/audits/index.ts +10 -33
  241. package/src/api/files/__tests__/$bucket.spec.ts +1 -1
  242. package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
  243. package/src/api/files/controllers/FileController.spec.ts +1 -1
  244. package/src/api/files/index.ts +10 -3
  245. package/src/api/files/jobs/FileJobs.spec.ts +1 -1
  246. package/src/api/files/services/FileService.spec.ts +1 -1
  247. package/src/api/jobs/index.ts +10 -3
  248. package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
  249. package/src/api/keys/controllers/ApiKeyController.ts +103 -0
  250. package/src/api/keys/entities/apiKeyEntity.ts +41 -0
  251. package/src/api/keys/index.ts +49 -0
  252. package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
  253. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
  254. package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
  255. package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
  256. package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
  257. package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
  258. package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
  259. package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
  260. package/src/api/keys/services/ApiKeyService.ts +306 -0
  261. package/src/api/logs/TODO.md +52 -0
  262. package/src/api/notifications/index.ts +10 -4
  263. package/src/api/parameters/index.ts +9 -30
  264. package/src/api/parameters/primitives/$config.ts +12 -4
  265. package/src/api/parameters/services/ConfigStore.ts +9 -3
  266. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
  267. package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
  268. package/src/api/users/index.ts +14 -3
  269. package/src/api/users/primitives/$realm.ts +33 -5
  270. package/src/api/users/providers/RealmProvider.ts +1 -12
  271. package/src/api/users/services/SessionService.ts +1 -1
  272. package/src/api/verifications/controllers/VerificationController.ts +2 -0
  273. package/src/api/verifications/index.ts +10 -4
  274. package/src/batch/index.ts +9 -36
  275. package/src/batch/primitives/$batch.ts +0 -8
  276. package/src/batch/providers/BatchProvider.ts +29 -2
  277. package/src/bucket/__tests__/shared.ts +1 -1
  278. package/src/bucket/index.ts +13 -6
  279. package/src/bucket/primitives/$bucket.ts +1 -1
  280. package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
  281. package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
  282. package/src/cache/core/__tests__/shared.ts +30 -0
  283. package/src/cache/core/index.ts +11 -6
  284. package/src/cache/core/primitives/$cache.spec.ts +5 -0
  285. package/src/cache/core/providers/CacheProvider.ts +17 -0
  286. package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
  287. package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
  288. package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
  289. package/src/cli/apps/AlephaCli.ts +1 -14
  290. package/src/cli/apps/AlephaPackageBuilderCli.ts +1 -1
  291. package/src/cli/commands/build.ts +1 -5
  292. package/src/cli/commands/db.ts +17 -11
  293. package/src/cli/commands/deploy.ts +1 -1
  294. package/src/cli/commands/dev.ts +1 -20
  295. package/src/cli/commands/gen/env.ts +5 -2
  296. package/src/cli/commands/gen/openapi.ts +5 -2
  297. package/src/cli/commands/init.spec.ts +544 -0
  298. package/src/cli/commands/init.ts +89 -55
  299. package/src/cli/commands/lint.ts +7 -1
  300. package/src/cli/commands/typecheck.ts +11 -0
  301. package/src/cli/providers/AppEntryProvider.ts +1 -1
  302. package/src/cli/providers/ViteBuildProvider.ts +8 -50
  303. package/src/cli/providers/ViteDevServerProvider.ts +36 -8
  304. package/src/cli/services/AlephaCliUtils.ts +37 -122
  305. package/src/cli/services/PackageManagerUtils.ts +127 -11
  306. package/src/cli/services/ProjectScaffolder.ts +122 -77
  307. package/src/cli/services/ViteUtils.ts +82 -0
  308. package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +32 -24
  309. package/src/cli/templates/gitignore.ts +39 -0
  310. package/src/cli/{assets → templates}/mainCss.ts +11 -2
  311. package/src/cli/templates/mainServerTs.ts +33 -0
  312. package/src/cli/templates/webAppRouterTs.ts +50 -0
  313. package/src/cli/{assets → templates}/webHelloComponentTsx.ts +2 -2
  314. package/src/command/helpers/Runner.spec.ts +4 -0
  315. package/src/command/helpers/Runner.ts +3 -21
  316. package/src/command/index.ts +12 -4
  317. package/src/command/providers/CliProvider.spec.ts +1067 -0
  318. package/src/command/providers/CliProvider.ts +203 -40
  319. package/src/core/Alepha.ts +2 -2
  320. package/src/core/__tests__/Alepha-start.spec.ts +4 -4
  321. package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
  322. package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
  323. package/src/core/index.shared.ts +1 -0
  324. package/src/core/index.ts +20 -0
  325. package/src/core/providers/EventManager.spec.ts +0 -71
  326. package/src/core/providers/EventManager.ts +3 -15
  327. package/src/core/providers/Json.ts +2 -14
  328. package/src/datetime/index.ts +15 -0
  329. package/src/email/index.ts +10 -5
  330. package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
  331. package/src/email/providers/LocalEmailProvider.ts +1 -1
  332. package/src/fake/__tests__/keyName.example.ts +1 -1
  333. package/src/fake/__tests__/keyName.spec.ts +5 -5
  334. package/src/fake/index.ts +9 -6
  335. package/src/fake/providers/FakeProvider.spec.ts +258 -40
  336. package/src/fake/providers/FakeProvider.ts +133 -19
  337. package/src/lock/core/index.ts +11 -4
  338. package/src/logger/index.ts +17 -66
  339. package/src/mcp/index.ts +10 -27
  340. package/src/mcp/transports/SseMcpTransport.ts +0 -11
  341. package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
  342. package/src/orm/index.browser.ts +2 -2
  343. package/src/orm/index.bun.ts +4 -2
  344. package/src/orm/index.ts +21 -47
  345. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -0
  346. package/src/orm/services/Repository.ts +7 -3
  347. package/src/queue/core/index.ts +14 -6
  348. package/src/react/auth/__tests__/$auth.spec.ts +202 -0
  349. package/src/react/auth/hooks/useAuth.ts +32 -0
  350. package/src/react/auth/index.browser.ts +13 -0
  351. package/src/react/auth/index.shared.ts +2 -0
  352. package/src/react/auth/index.ts +48 -0
  353. package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
  354. package/src/react/auth/services/ReactAuth.ts +135 -0
  355. package/src/react/core/__tests__/Router.spec.tsx +169 -0
  356. package/src/react/core/components/ClientOnly.tsx +49 -0
  357. package/src/react/core/components/ErrorBoundary.tsx +73 -0
  358. package/src/react/core/contexts/AlephaContext.ts +7 -0
  359. package/src/react/core/contexts/AlephaProvider.tsx +42 -0
  360. package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
  361. package/src/react/core/hooks/useAction.ts +480 -0
  362. package/src/react/core/hooks/useAlepha.ts +26 -0
  363. package/src/react/core/hooks/useClient.ts +17 -0
  364. package/src/react/core/hooks/useEvents.ts +51 -0
  365. package/src/react/core/hooks/useInject.ts +12 -0
  366. package/src/react/core/hooks/useStore.ts +52 -0
  367. package/src/react/core/index.ts +90 -0
  368. package/src/react/form/components/FormState.tsx +17 -0
  369. package/src/react/form/errors/FormValidationError.ts +18 -0
  370. package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
  371. package/src/react/form/hooks/useForm.ts +47 -0
  372. package/src/react/form/hooks/useFormState.ts +130 -0
  373. package/src/react/form/index.ts +44 -0
  374. package/src/react/form/services/FormModel.ts +614 -0
  375. package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
  376. package/src/react/head/helpers/SeoExpander.ts +142 -0
  377. package/src/react/head/hooks/useHead.spec.tsx +288 -0
  378. package/src/react/head/hooks/useHead.ts +62 -0
  379. package/src/react/head/index.browser.ts +26 -0
  380. package/src/react/head/index.ts +44 -0
  381. package/src/react/head/interfaces/Head.ts +105 -0
  382. package/src/react/head/primitives/$head.ts +25 -0
  383. package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
  384. package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
  385. package/src/react/head/providers/HeadProvider.ts +168 -0
  386. package/src/react/head/providers/ServerHeadProvider.ts +31 -0
  387. package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
  388. package/src/react/i18n/components/Localize.spec.tsx +357 -0
  389. package/src/react/i18n/components/Localize.tsx +35 -0
  390. package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
  391. package/src/react/i18n/hooks/useI18n.ts +18 -0
  392. package/src/react/i18n/index.ts +41 -0
  393. package/src/react/i18n/primitives/$dictionary.ts +69 -0
  394. package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
  395. package/src/react/i18n/providers/I18nProvider.ts +278 -0
  396. package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
  397. package/src/react/router/__tests__/page-head.spec.ts +48 -0
  398. package/src/react/router/__tests__/seo-head.spec.ts +125 -0
  399. package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
  400. package/src/react/router/components/ErrorViewer.tsx +872 -0
  401. package/src/react/router/components/Link.tsx +23 -0
  402. package/src/react/router/components/NestedView.tsx +223 -0
  403. package/src/react/router/components/NotFound.tsx +30 -0
  404. package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  405. package/src/react/router/contexts/RouterLayerContext.ts +12 -0
  406. package/src/react/router/errors/Redirection.ts +28 -0
  407. package/src/react/router/hooks/useActive.ts +52 -0
  408. package/src/react/router/hooks/useQueryParams.ts +63 -0
  409. package/src/react/router/hooks/useRouter.ts +20 -0
  410. package/src/react/router/hooks/useRouterState.ts +11 -0
  411. package/src/react/router/index.browser.ts +45 -0
  412. package/src/react/router/index.shared.ts +19 -0
  413. package/src/react/router/index.ts +142 -0
  414. package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
  415. package/src/react/router/primitives/$page.spec.tsx +708 -0
  416. package/src/react/router/primitives/$page.ts +497 -0
  417. package/src/react/router/providers/ReactBrowserProvider.ts +309 -0
  418. package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
  419. package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
  420. package/src/react/router/providers/ReactPageProvider.ts +726 -0
  421. package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
  422. package/src/react/router/providers/ReactServerProvider.ts +558 -0
  423. package/src/react/router/providers/ReactServerTemplateProvider.ts +979 -0
  424. package/src/react/router/providers/SSRManifestProvider.ts +334 -0
  425. package/src/react/router/services/ReactPageServerService.ts +48 -0
  426. package/src/react/router/services/ReactPageService.ts +27 -0
  427. package/src/react/router/services/ReactRouter.ts +262 -0
  428. package/src/react/websocket/hooks/useRoom.tsx +242 -0
  429. package/src/react/websocket/index.ts +7 -0
  430. package/src/redis/__tests__/redis.spec.ts +13 -0
  431. package/src/redis/index.ts +9 -25
  432. package/src/redis/providers/BunRedisProvider.ts +9 -0
  433. package/src/redis/providers/NodeRedisProvider.ts +8 -0
  434. package/src/redis/providers/RedisProvider.ts +16 -0
  435. package/src/retry/index.ts +11 -2
  436. package/src/router/index.ts +15 -0
  437. package/src/scheduler/index.ts +11 -2
  438. package/src/security/__tests__/BasicAuth.spec.ts +2 -0
  439. package/src/security/__tests__/ServerSecurityProvider.spec.ts +13 -5
  440. package/src/security/index.ts +15 -10
  441. package/src/security/interfaces/IssuerResolver.ts +27 -0
  442. package/src/security/primitives/$issuer.ts +55 -0
  443. package/src/security/providers/SecurityProvider.ts +179 -0
  444. package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
  445. package/src/security/providers/ServerSecurityProvider.ts +36 -22
  446. package/src/server/auth/index.ts +12 -7
  447. package/src/server/cache/index.ts +7 -22
  448. package/src/server/compress/index.ts +10 -2
  449. package/src/server/cookies/index.ts +7 -5
  450. package/src/server/cookies/primitives/$cookie.ts +33 -11
  451. package/src/server/core/index.ts +16 -6
  452. package/src/server/core/interfaces/ServerRequest.ts +83 -1
  453. package/src/server/core/primitives/$action.spec.ts +1 -1
  454. package/src/server/core/primitives/$action.ts +8 -3
  455. package/src/server/core/providers/NodeHttpServerProvider.ts +9 -3
  456. package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
  457. package/src/server/core/services/ServerRequestParser.ts +306 -13
  458. package/src/server/cors/index.ts +7 -21
  459. package/src/server/cors/primitives/$cors.ts +6 -2
  460. package/src/server/health/index.ts +8 -2
  461. package/src/server/helmet/index.ts +11 -3
  462. package/src/server/links/index.ts +11 -6
  463. package/src/server/metrics/index.ts +10 -3
  464. package/src/server/multipart/index.ts +9 -3
  465. package/src/server/proxy/index.ts +8 -2
  466. package/src/server/rate-limit/index.ts +21 -25
  467. package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
  468. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
  469. package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
  470. package/src/server/static/index.ts +8 -2
  471. package/src/server/static/providers/ServerStaticProvider.ts +1 -1
  472. package/src/server/swagger/index.ts +9 -4
  473. package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
  474. package/src/sms/index.ts +9 -5
  475. package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
  476. package/src/sms/providers/LocalSmsProvider.ts +1 -1
  477. package/src/system/index.browser.ts +11 -0
  478. package/src/system/index.ts +62 -0
  479. package/src/{file → system}/providers/FileSystemProvider.ts +16 -0
  480. package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
  481. package/src/system/providers/MemoryShellProvider.ts +164 -0
  482. package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
  483. package/src/{file → system}/providers/NodeFileSystemProvider.ts +36 -0
  484. package/src/system/providers/NodeShellProvider.ts +184 -0
  485. package/src/system/providers/ShellProvider.ts +74 -0
  486. package/src/{file → system}/services/FileDetector.spec.ts +2 -2
  487. package/src/thread/index.ts +11 -2
  488. package/src/topic/core/index.ts +12 -5
  489. package/src/vite/tasks/buildClient.ts +2 -7
  490. package/src/vite/tasks/buildServer.ts +17 -1
  491. package/src/websocket/index.ts +12 -8
  492. package/dist/file/index.d.ts.map +0 -1
  493. package/dist/file/index.js.map +0 -1
  494. package/src/cli/assets/mainServerTs.ts +0 -24
  495. package/src/cli/assets/webAppRouterTs.ts +0 -16
  496. package/src/cli/providers/ViteTemplateProvider.ts +0 -27
  497. package/src/file/index.ts +0 -43
  498. /package/src/cli/{assets → templates}/apiHelloControllerTs.ts +0 -0
  499. /package/src/cli/{assets → templates}/apiIndexTs.ts +0 -0
  500. /package/src/cli/{assets → templates}/biomeJson.ts +0 -0
  501. /package/src/cli/{assets → templates}/dummySpecTs.ts +0 -0
  502. /package/src/cli/{assets → templates}/editorconfig.ts +0 -0
  503. /package/src/cli/{assets → templates}/mainBrowserTs.ts +0 -0
  504. /package/src/cli/{assets → templates}/tsconfigJson.ts +0 -0
  505. /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
  506. /package/src/{file → system}/errors/FileError.ts +0 -0
  507. /package/src/{file → system}/services/FileDetector.ts +0 -0
@@ -0,0 +1,558 @@
1
+ import { join } from "node:path";
2
+ import {
3
+ $atom,
4
+ $env,
5
+ $hook,
6
+ $inject,
7
+ $use,
8
+ Alepha,
9
+ AlephaError,
10
+ type Static,
11
+ t,
12
+ } from "alepha";
13
+ import { $logger } from "alepha/logger";
14
+ import { ServerHeadProvider } from "alepha/react/head";
15
+ import { type ServerHandler, ServerRouterProvider } from "alepha/server";
16
+ import { ServerLinksProvider } from "alepha/server/links";
17
+ import { ServerStaticProvider } from "alepha/server/static";
18
+ import { FileSystemProvider } from "alepha/system";
19
+ import { renderToReadableStream } from "react-dom/server";
20
+ import { Redirection } from "../errors/Redirection.ts";
21
+ import {
22
+ $page,
23
+ type PagePrimitiveRenderOptions,
24
+ type PagePrimitiveRenderResult,
25
+ } from "../primitives/$page.ts";
26
+ import {
27
+ type PageRoute,
28
+ ReactPageProvider,
29
+ type ReactRouterState,
30
+ } from "./ReactPageProvider.ts";
31
+ import { ReactServerTemplateProvider } from "./ReactServerTemplateProvider.ts";
32
+ import { SSRManifestProvider } from "./SSRManifestProvider.ts";
33
+
34
+ /**
35
+ * React server provider responsible for SSR and static file serving.
36
+ *
37
+ * Coordinates between:
38
+ * - ReactPageProvider: Page routing and layer resolution
39
+ * - ReactServerTemplateProvider: HTML template parsing and streaming
40
+ * - ServerHeadProvider: Head content management
41
+ * - SSRManifestProvider: Module preload link collection
42
+ *
43
+ * Uses `react-dom/server` under the hood.
44
+ */
45
+ export class ReactServerProvider {
46
+ /**
47
+ * SSR response headers - pre-allocated to avoid object creation per request.
48
+ */
49
+ protected readonly SSR_HEADERS = {
50
+ "content-type": "text/html",
51
+ "cache-control": "no-store, no-cache, must-revalidate, proxy-revalidate",
52
+ pragma: "no-cache",
53
+ expires: "0",
54
+ } as const;
55
+
56
+ protected readonly fs = $inject(FileSystemProvider);
57
+ protected readonly log = $logger();
58
+ protected readonly alepha = $inject(Alepha);
59
+ protected readonly env = $env(envSchema);
60
+ protected readonly pageApi = $inject(ReactPageProvider);
61
+ protected readonly templateProvider = $inject(ReactServerTemplateProvider);
62
+ protected readonly serverHeadProvider = $inject(ServerHeadProvider);
63
+ protected readonly serverStaticProvider = $inject(ServerStaticProvider);
64
+ protected readonly serverRouterProvider = $inject(ServerRouterProvider);
65
+ protected readonly ssrManifestProvider = $inject(SSRManifestProvider);
66
+
67
+ /**
68
+ * Cached check for ServerLinksProvider - avoids has() lookup per request.
69
+ */
70
+ protected hasServerLinksProvider = false;
71
+
72
+ protected readonly options = $use(reactServerOptions);
73
+
74
+ /**
75
+ * Configure the React server provider.
76
+ */
77
+ public readonly onConfigure = $hook({
78
+ on: "configure",
79
+ handler: async () => {
80
+ const pages = this.alepha.primitives($page);
81
+
82
+ const ssrEnabled =
83
+ pages.length > 0 && this.env.REACT_SSR_ENABLED !== false;
84
+
85
+ this.alepha.store.set("alepha.react.server.ssr", ssrEnabled);
86
+
87
+ // production mode
88
+ let root = "";
89
+
90
+ // non-serverless mode only -> serve static files
91
+ if (!this.alepha.isServerless() && !this.alepha.isViteDev()) {
92
+ root = await this.getPublicDirectory();
93
+ if (!root) {
94
+ this.log.warn(
95
+ "Missing static files, static file server will be disabled",
96
+ );
97
+ } else {
98
+ this.log.debug(`Using static files from: ${root}`);
99
+ await this.configureStaticServer(root);
100
+ }
101
+ }
102
+
103
+ if (ssrEnabled) {
104
+ await this.registerPages(async () => this.template);
105
+ this.log.info("SSR OK");
106
+ return;
107
+ }
108
+
109
+ // no SSR enabled, serve index.html for all unmatched routes
110
+ this.log.info("SSR is disabled, use History API fallback");
111
+ this.serverRouterProvider.createRoute({
112
+ path: "*",
113
+ handler: async ({ url, reply }) => {
114
+ if (url.pathname.includes(".")) {
115
+ // If the request is for a file (e.g., /style.css), do not fallback
116
+ reply.headers["content-type"] = "text/plain";
117
+ reply.body = "Not Found";
118
+ reply.status = 404;
119
+ return;
120
+ }
121
+
122
+ reply.headers["content-type"] = "text/html";
123
+
124
+ // serve index.html for all unmatched routes
125
+ return this.template;
126
+ },
127
+ });
128
+ },
129
+ });
130
+
131
+ /**
132
+ * Get the current HTML template.
133
+ */
134
+ public get template() {
135
+ return (
136
+ this.alepha.store.get("alepha.react.server.template") ??
137
+ "<!DOCTYPE html><html lang='en'><head></head><body><div id='root'></div></body></html>"
138
+ );
139
+ }
140
+
141
+ /**
142
+ * Register all pages as server routes.
143
+ */
144
+ protected async registerPages(templateLoader: TemplateLoader) {
145
+ // Parse template once at startup
146
+ const template = await templateLoader();
147
+ if (template) {
148
+ this.templateProvider.parseTemplate(template);
149
+ }
150
+
151
+ // Set up early head content (entry assets preloads)
152
+ this.setupEarlyHeadContent();
153
+
154
+ // Cache ServerLinksProvider check at startup
155
+ this.hasServerLinksProvider = this.alepha.has(ServerLinksProvider);
156
+
157
+ for (const page of this.pageApi.getPages()) {
158
+ if (page.component || page.lazy) {
159
+ this.log.debug(`+ ${page.match} -> ${page.name}`);
160
+
161
+ this.serverRouterProvider.createRoute({
162
+ ...page,
163
+ schema: undefined, // schema is handled by the page primitive provider
164
+ method: "GET",
165
+ path: page.match,
166
+ handler: this.createHandler(page, templateLoader),
167
+ });
168
+ }
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Set up early head content with entry assets.
174
+ *
175
+ * This content is sent immediately when streaming starts, before page loaders run,
176
+ * allowing the browser to start downloading entry.js and CSS files early.
177
+ *
178
+ * Uses <script type="module"> instead of <link rel="modulepreload"> for JS
179
+ * because the script needs to execute anyway - this way the browser starts
180
+ * downloading, parsing, AND will execute as soon as ready.
181
+ *
182
+ * Also injects critical meta tags (charset, viewport) if not specified in $head,
183
+ * and strips these assets from the original template head to avoid duplicates.
184
+ */
185
+ protected setupEarlyHeadContent(): void {
186
+ const assets = this.ssrManifestProvider.getEntryAssets();
187
+ const globalHead = this.serverHeadProvider.resolveGlobalHead();
188
+
189
+ const parts: string[] = [];
190
+
191
+ if (assets) {
192
+ // Add CSS stylesheets (critical for rendering)
193
+ for (const css of assets.css) {
194
+ parts.push(`<link rel="stylesheet" href="${css}" crossorigin="">`);
195
+ }
196
+
197
+ // Add entry JS as script module (not just modulepreload)
198
+ // This starts download, parse, AND execution immediately
199
+ if (assets.js) {
200
+ parts.push(
201
+ `<script type="module" crossorigin="" src="${assets.js}"></script>`,
202
+ );
203
+ }
204
+ }
205
+
206
+ // Pass global head so critical meta tags can be injected if missing
207
+ this.templateProvider.setEarlyHeadContent(
208
+ parts.length > 0 ? `${parts.join("\n")}\n` : "",
209
+ globalHead,
210
+ assets ?? undefined,
211
+ );
212
+
213
+ this.log.debug("Early head content set", {
214
+ css: assets?.css.length ?? 0,
215
+ js: assets?.js ? 1 : 0,
216
+ });
217
+ }
218
+
219
+ /**
220
+ * Get the public directory path where static files are located.
221
+ */
222
+ protected async getPublicDirectory(): Promise<string> {
223
+ const maybe = [
224
+ join(process.cwd(), `dist/${this.options.publicDir}`),
225
+ join(process.cwd(), this.options.publicDir),
226
+ ];
227
+
228
+ for (const it of maybe) {
229
+ if (await this.fs.exists(it)) {
230
+ return it;
231
+ }
232
+ }
233
+
234
+ return "";
235
+ }
236
+
237
+ /**
238
+ * Configure the static file server to serve files from the given root directory.
239
+ */
240
+ protected async configureStaticServer(root: string) {
241
+ await this.serverStaticProvider.createStaticServer({
242
+ root,
243
+ cacheControl: {
244
+ maxAge: 3600,
245
+ immutable: true,
246
+ },
247
+ ...this.options.staticServer,
248
+ });
249
+ }
250
+
251
+ /**
252
+ * Create the request handler for a page route.
253
+ */
254
+ protected createHandler(
255
+ route: PageRoute,
256
+ templateLoader: TemplateLoader,
257
+ ): ServerHandler {
258
+ return async (serverRequest) => {
259
+ const { url, reply, query, params } = serverRequest;
260
+
261
+ // Ensure template is parsed (handles dev mode where template may change)
262
+ if (!this.templateProvider.isReady()) {
263
+ const template = await templateLoader();
264
+ if (!template) {
265
+ throw new AlephaError("Missing template for SSR rendering");
266
+ }
267
+ this.templateProvider.parseTemplate(template);
268
+ this.setupEarlyHeadContent();
269
+ }
270
+
271
+ this.log.trace("Rendering page", { name: route.name });
272
+
273
+ // Initialize router state
274
+ const state: ReactRouterState = {
275
+ url,
276
+ params,
277
+ query,
278
+ name: route.name,
279
+ onError: () => null,
280
+ layers: [],
281
+ meta: {},
282
+ head: {},
283
+ };
284
+
285
+ // Set up API links if available
286
+ if (this.hasServerLinksProvider) {
287
+ this.alepha.store.set(
288
+ "alepha.server.request.apiLinks",
289
+ await this.alepha.inject(ServerLinksProvider).getUserApiLinks({
290
+ user: (serverRequest as any).user, // TODO: fix type
291
+ authorization: serverRequest.headers.authorization,
292
+ }),
293
+ );
294
+ }
295
+
296
+ // Check access permissions
297
+ let target: PageRoute | undefined = route;
298
+ while (target) {
299
+ if (route.can && !route.can()) {
300
+ this.log.warn(
301
+ `Access to page '${route.name}' is forbidden by can() check`,
302
+ );
303
+ reply.status = 403;
304
+ reply.headers["content-type"] = "text/plain";
305
+ return "Forbidden";
306
+ }
307
+ target = target.parent;
308
+ }
309
+
310
+ await this.alepha.events.emit("react:server:render:begin", {
311
+ request: serverRequest,
312
+ state,
313
+ });
314
+
315
+ // Apply SSR headers early
316
+ Object.assign(reply.headers, this.SSR_HEADERS);
317
+
318
+ // Resolve global head for early streaming (htmlAttributes only)
319
+ const globalHead = this.serverHeadProvider.resolveGlobalHead();
320
+
321
+ // Create optimized HTML stream with early head
322
+ const htmlStream = this.templateProvider.createEarlyHtmlStream(
323
+ globalHead,
324
+ async () => {
325
+ // === ASYNC WORK (runs while early head is being sent) ===
326
+ const result = await this.renderPage(route, state);
327
+
328
+ if (result.redirect) {
329
+ // Return redirect URL - template provider will inject meta refresh
330
+ // since HTTP headers have already been sent
331
+ return { redirect: result.redirect };
332
+ }
333
+
334
+ return { state, reactStream: result.reactStream! };
335
+ },
336
+ {
337
+ hydration: true,
338
+ onError: (error) => {
339
+ if (error instanceof Redirection) {
340
+ this.log.debug("Streaming resulted in redirection", {
341
+ redirect: error.redirect,
342
+ });
343
+ // Can't do redirect after streaming started - already handled above
344
+ } else {
345
+ // disable logging here, it's noisy and duplicate
346
+ // this.log.error("HTML stream error", error);
347
+ }
348
+ },
349
+ },
350
+ );
351
+
352
+ this.log.trace("Page streaming started (early head optimization)");
353
+ route.onServerResponse?.(serverRequest);
354
+ reply.body = htmlStream;
355
+ };
356
+ }
357
+
358
+ // ---------------------------------------------------------------------------
359
+ // Core rendering logic - shared between SSR handler and static prerendering
360
+ // ---------------------------------------------------------------------------
361
+
362
+ /**
363
+ * Core page rendering logic shared between SSR handler and static prerendering.
364
+ *
365
+ * Handles:
366
+ * - Layer resolution (loaders)
367
+ * - Redirect detection
368
+ * - Head content filling
369
+ * - Preload link collection
370
+ * - React stream rendering
371
+ *
372
+ * @param route - The page route to render
373
+ * @param state - The router state
374
+ * @returns Render result with redirect or React stream
375
+ */
376
+ protected async renderPage(
377
+ route: PageRoute,
378
+ state: ReactRouterState,
379
+ ): Promise<{ redirect?: string; reactStream?: ReadableStream<Uint8Array> }> {
380
+ // Resolve page layers (loaders)
381
+ const { redirect } = await this.pageApi.createLayers(route, state);
382
+ if (redirect) {
383
+ this.log.debug("Resolver resulted in redirection", { redirect });
384
+ return { redirect };
385
+ }
386
+
387
+ // Fill head from route config
388
+ this.serverHeadProvider.fillHead(state);
389
+
390
+ // Collect and inject modulepreload links for page-specific chunks
391
+ const preloadLinks = this.ssrManifestProvider.collectPreloadLinks(route);
392
+ if (preloadLinks.length > 0) {
393
+ state.head ??= {};
394
+ state.head.link = [...(state.head.link ?? []), ...preloadLinks];
395
+ }
396
+
397
+ // Render React to stream
398
+
399
+ const element = this.pageApi.root(state);
400
+ this.alepha.store.set("alepha.react.router.state", state);
401
+
402
+ const reactStream = await renderToReadableStream(element, {
403
+ onError: (error: unknown) => {
404
+ if (error instanceof Redirection) {
405
+ this.log.warn("Redirect during streaming ignored", {
406
+ redirect: error.redirect,
407
+ });
408
+ } else {
409
+ // disable logging here, it's noisy and duplicate
410
+ // this.log.error("Streaming render error", error);
411
+ }
412
+ },
413
+ });
414
+
415
+ return { reactStream };
416
+ }
417
+
418
+ // ---------------------------------------------------------------------------
419
+ // Testing utilities - kept for backwards compatibility with tests
420
+ // ---------------------------------------------------------------------------
421
+
422
+ /**
423
+ * For testing purposes, renders a page to HTML string.
424
+ * Uses the same streaming code path as production, then collects to string.
425
+ *
426
+ * @param name - Page name to render
427
+ * @param options - Render options (params, query, html, hydration)
428
+ */
429
+ public async render(
430
+ name: string,
431
+ options: PagePrimitiveRenderOptions = {},
432
+ ): Promise<PagePrimitiveRenderResult> {
433
+ const page = this.pageApi.page(name);
434
+ const url = new URL(this.pageApi.url(name, options));
435
+ const state: ReactRouterState = {
436
+ url,
437
+ params: options.params ?? {},
438
+ query: options.query ?? {},
439
+ onError: () => null,
440
+ layers: [],
441
+ meta: {},
442
+ head: {},
443
+ };
444
+
445
+ this.log.trace("Rendering", { url });
446
+
447
+ await this.alepha.events.emit("react:server:render:begin", { state });
448
+
449
+ // Ensure template is parsed with early head content (entry.js, CSS)
450
+ if (!this.templateProvider.isReady()) {
451
+ this.templateProvider.parseTemplate(this.template);
452
+ this.setupEarlyHeadContent();
453
+ }
454
+
455
+ // Use shared rendering logic
456
+ const result = await this.renderPage(page, state);
457
+
458
+ if (result.redirect) {
459
+ return { state, html: "", redirect: result.redirect };
460
+ }
461
+
462
+ const reactStream = result.reactStream!;
463
+
464
+ // If full HTML page not requested, collect just the React content
465
+ if (!options.html) {
466
+ const html = await this.streamToString(reactStream);
467
+ return { state, html };
468
+ }
469
+
470
+ // Create full HTML stream and collect to string
471
+ const htmlStream = this.templateProvider.createHtmlStream(
472
+ reactStream,
473
+ state,
474
+ { hydration: options.hydration ?? true },
475
+ );
476
+
477
+ const html = await this.streamToString(htmlStream);
478
+
479
+ await this.alepha.events.emit("react:server:render:end", { state, html });
480
+
481
+ return { state, html };
482
+ }
483
+
484
+ /**
485
+ * Collect a ReadableStream into a string.
486
+ */
487
+ protected async streamToString(
488
+ stream: ReadableStream<Uint8Array>,
489
+ ): Promise<string> {
490
+ const reader = stream.getReader();
491
+ const decoder = new TextDecoder();
492
+ const chunks: string[] = [];
493
+
494
+ try {
495
+ while (true) {
496
+ const { done, value } = await reader.read();
497
+ if (done) break;
498
+ chunks.push(decoder.decode(value, { stream: true }));
499
+ }
500
+ chunks.push(decoder.decode()); // Flush remaining
501
+ } finally {
502
+ reader.releaseLock();
503
+ }
504
+
505
+ return chunks.join("");
506
+ }
507
+ }
508
+
509
+ // ---------------------------------------------------------------------------------------------------------------------
510
+
511
+ type TemplateLoader = () => Promise<string | undefined>;
512
+
513
+ // ---------------------------------------------------------------------------------------------------------------------
514
+
515
+ const envSchema = t.object({
516
+ REACT_SSR_ENABLED: t.optional(t.boolean()),
517
+ });
518
+
519
+ declare module "alepha" {
520
+ interface Env extends Partial<Static<typeof envSchema>> {}
521
+ interface State {
522
+ "alepha.react.server.ssr"?: boolean;
523
+ "alepha.react.server.template"?: string;
524
+ }
525
+ }
526
+
527
+ /**
528
+ * React server provider configuration atom
529
+ */
530
+ export const reactServerOptions = $atom({
531
+ name: "alepha.react.server.options",
532
+ schema: t.object({
533
+ publicDir: t.string(),
534
+ staticServer: t.object({
535
+ disabled: t.boolean(),
536
+ path: t.string({
537
+ description: "URL path where static files will be served.",
538
+ }),
539
+ }),
540
+ }),
541
+ default: {
542
+ publicDir: "public",
543
+ staticServer: {
544
+ disabled: false,
545
+ path: "/",
546
+ },
547
+ },
548
+ });
549
+
550
+ export type ReactServerProviderOptions = Static<
551
+ typeof reactServerOptions.schema
552
+ >;
553
+
554
+ declare module "alepha" {
555
+ interface State {
556
+ [reactServerOptions.key]: ReactServerProviderOptions;
557
+ }
558
+ }