alepha 0.15.1 → 0.15.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (523) hide show
  1. package/README.md +68 -80
  2. package/dist/api/audits/index.d.ts +10 -33
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +10 -33
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +10 -3
  7. package/dist/api/files/index.d.ts.map +1 -1
  8. package/dist/api/files/index.js +10 -3
  9. package/dist/api/files/index.js.map +1 -1
  10. package/dist/api/jobs/index.d.ts +162 -155
  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.d.ts +10 -4
  19. package/dist/api/notifications/index.d.ts.map +1 -1
  20. package/dist/api/notifications/index.js +10 -4
  21. package/dist/api/notifications/index.js.map +1 -1
  22. package/dist/api/parameters/index.d.ts +43 -50
  23. package/dist/api/parameters/index.d.ts.map +1 -1
  24. package/dist/api/parameters/index.js +30 -37
  25. package/dist/api/parameters/index.js.map +1 -1
  26. package/dist/api/users/index.d.ts +1081 -760
  27. package/dist/api/users/index.d.ts.map +1 -1
  28. package/dist/api/users/index.js +2539 -218
  29. package/dist/api/users/index.js.map +1 -1
  30. package/dist/api/verifications/index.d.ts +138 -132
  31. package/dist/api/verifications/index.d.ts.map +1 -1
  32. package/dist/api/verifications/index.js +12 -4
  33. package/dist/api/verifications/index.js.map +1 -1
  34. package/dist/batch/index.d.ts +20 -40
  35. package/dist/batch/index.d.ts.map +1 -1
  36. package/dist/batch/index.js +31 -44
  37. package/dist/batch/index.js.map +1 -1
  38. package/dist/bucket/index.d.ts +440 -8
  39. package/dist/bucket/index.d.ts.map +1 -1
  40. package/dist/bucket/index.js +1861 -12
  41. package/dist/bucket/index.js.map +1 -1
  42. package/dist/cache/core/index.d.ts +179 -7
  43. package/dist/cache/core/index.d.ts.map +1 -1
  44. package/dist/cache/core/index.js +213 -7
  45. package/dist/cache/core/index.js.map +1 -1
  46. package/dist/cache/redis/index.d.ts +1 -0
  47. package/dist/cache/redis/index.d.ts.map +1 -1
  48. package/dist/cache/redis/index.js +4 -0
  49. package/dist/cache/redis/index.js.map +1 -1
  50. package/dist/cli/index.d.ts +638 -5645
  51. package/dist/cli/index.d.ts.map +1 -1
  52. package/dist/cli/index.js +2550 -368
  53. package/dist/cli/index.js.map +1 -1
  54. package/dist/command/index.d.ts +203 -45
  55. package/dist/command/index.d.ts.map +1 -1
  56. package/dist/command/index.js +2060 -71
  57. package/dist/command/index.js.map +1 -1
  58. package/dist/core/index.browser.js +70 -40
  59. package/dist/core/index.browser.js.map +1 -1
  60. package/dist/core/index.d.ts +34 -13
  61. package/dist/core/index.d.ts.map +1 -1
  62. package/dist/core/index.js +90 -40
  63. package/dist/core/index.js.map +1 -1
  64. package/dist/core/index.native.js +70 -40
  65. package/dist/core/index.native.js.map +1 -1
  66. package/dist/datetime/index.d.ts +15 -0
  67. package/dist/datetime/index.d.ts.map +1 -1
  68. package/dist/datetime/index.js +15 -0
  69. package/dist/datetime/index.js.map +1 -1
  70. package/dist/email/index.d.ts +323 -20
  71. package/dist/email/index.d.ts.map +1 -1
  72. package/dist/email/index.js +1857 -7
  73. package/dist/email/index.js.map +1 -1
  74. package/dist/fake/index.d.ts +90 -8
  75. package/dist/fake/index.d.ts.map +1 -1
  76. package/dist/fake/index.js +91 -20
  77. package/dist/fake/index.js.map +1 -1
  78. package/dist/lock/core/index.d.ts +11 -4
  79. package/dist/lock/core/index.d.ts.map +1 -1
  80. package/dist/lock/core/index.js +11 -4
  81. package/dist/lock/core/index.js.map +1 -1
  82. package/dist/logger/index.d.ts +17 -66
  83. package/dist/logger/index.d.ts.map +1 -1
  84. package/dist/logger/index.js +14 -63
  85. package/dist/logger/index.js.map +1 -1
  86. package/dist/mcp/index.d.ts +10 -30
  87. package/dist/mcp/index.d.ts.map +1 -1
  88. package/dist/mcp/index.js +12 -35
  89. package/dist/mcp/index.js.map +1 -1
  90. package/dist/orm/index.browser.js +3 -3
  91. package/dist/orm/index.browser.js.map +1 -1
  92. package/dist/orm/index.bun.js +39 -20
  93. package/dist/orm/index.bun.js.map +1 -1
  94. package/dist/orm/index.d.ts +517 -540
  95. package/dist/orm/index.d.ts.map +1 -1
  96. package/dist/orm/index.js +58 -71
  97. package/dist/orm/index.js.map +1 -1
  98. package/dist/queue/core/index.d.ts +18 -10
  99. package/dist/queue/core/index.d.ts.map +1 -1
  100. package/dist/queue/core/index.js +14 -6
  101. package/dist/queue/core/index.js.map +1 -1
  102. package/dist/react/auth/index.browser.js +108 -0
  103. package/dist/react/auth/index.browser.js.map +1 -0
  104. package/dist/react/auth/index.d.ts +100 -0
  105. package/dist/react/auth/index.d.ts.map +1 -0
  106. package/dist/react/auth/index.js +145 -0
  107. package/dist/react/auth/index.js.map +1 -0
  108. package/dist/react/core/index.d.ts +469 -0
  109. package/dist/react/core/index.d.ts.map +1 -0
  110. package/dist/react/core/index.js +464 -0
  111. package/dist/react/core/index.js.map +1 -0
  112. package/dist/react/form/index.d.ts +232 -0
  113. package/dist/react/form/index.d.ts.map +1 -0
  114. package/dist/react/form/index.js +432 -0
  115. package/dist/react/form/index.js.map +1 -0
  116. package/dist/react/head/index.browser.js +423 -0
  117. package/dist/react/head/index.browser.js.map +1 -0
  118. package/dist/react/head/index.d.ts +288 -0
  119. package/dist/react/head/index.d.ts.map +1 -0
  120. package/dist/react/head/index.js +465 -0
  121. package/dist/react/head/index.js.map +1 -0
  122. package/dist/react/i18n/index.d.ts +175 -0
  123. package/dist/react/i18n/index.d.ts.map +1 -0
  124. package/dist/react/i18n/index.js +224 -0
  125. package/dist/react/i18n/index.js.map +1 -0
  126. package/dist/react/router/index.browser.js +1974 -0
  127. package/dist/react/router/index.browser.js.map +1 -0
  128. package/dist/react/router/index.d.ts +1956 -0
  129. package/dist/react/router/index.d.ts.map +1 -0
  130. package/dist/react/router/index.js +4722 -0
  131. package/dist/react/router/index.js.map +1 -0
  132. package/dist/react/websocket/index.d.ts +117 -0
  133. package/dist/react/websocket/index.d.ts.map +1 -0
  134. package/dist/react/websocket/index.js +107 -0
  135. package/dist/react/websocket/index.js.map +1 -0
  136. package/dist/redis/index.bun.js +4 -0
  137. package/dist/redis/index.bun.js.map +1 -1
  138. package/dist/redis/index.d.ts +41 -44
  139. package/dist/redis/index.d.ts.map +1 -1
  140. package/dist/redis/index.js +16 -25
  141. package/dist/redis/index.js.map +1 -1
  142. package/dist/retry/index.d.ts +11 -2
  143. package/dist/retry/index.d.ts.map +1 -1
  144. package/dist/retry/index.js +11 -2
  145. package/dist/retry/index.js.map +1 -1
  146. package/dist/scheduler/index.d.ts +11 -2
  147. package/dist/scheduler/index.d.ts.map +1 -1
  148. package/dist/scheduler/index.js +11 -2
  149. package/dist/scheduler/index.js.map +1 -1
  150. package/dist/security/index.d.ts +140 -49
  151. package/dist/security/index.d.ts.map +1 -1
  152. package/dist/security/index.js +164 -32
  153. package/dist/security/index.js.map +1 -1
  154. package/dist/server/auth/index.d.ts +12 -7
  155. package/dist/server/auth/index.d.ts.map +1 -1
  156. package/dist/server/auth/index.js +12 -7
  157. package/dist/server/auth/index.js.map +1 -1
  158. package/dist/server/cache/index.d.ts +7 -22
  159. package/dist/server/cache/index.d.ts.map +1 -1
  160. package/dist/server/cache/index.js +7 -22
  161. package/dist/server/cache/index.js.map +1 -1
  162. package/dist/server/compress/index.d.ts +10 -2
  163. package/dist/server/compress/index.d.ts.map +1 -1
  164. package/dist/server/compress/index.js +10 -2
  165. package/dist/server/compress/index.js.map +1 -1
  166. package/dist/server/cookies/index.d.ts +40 -16
  167. package/dist/server/cookies/index.d.ts.map +1 -1
  168. package/dist/server/cookies/index.js +7 -5
  169. package/dist/server/cookies/index.js.map +1 -1
  170. package/dist/server/core/index.d.ts +124 -23
  171. package/dist/server/core/index.d.ts.map +1 -1
  172. package/dist/server/core/index.js +231 -14
  173. package/dist/server/core/index.js.map +1 -1
  174. package/dist/server/cors/index.d.ts +13 -23
  175. package/dist/server/cors/index.d.ts.map +1 -1
  176. package/dist/server/cors/index.js +7 -21
  177. package/dist/server/cors/index.js.map +1 -1
  178. package/dist/server/health/index.d.ts +8 -2
  179. package/dist/server/health/index.d.ts.map +1 -1
  180. package/dist/server/health/index.js +8 -2
  181. package/dist/server/health/index.js.map +1 -1
  182. package/dist/server/helmet/index.d.ts +11 -3
  183. package/dist/server/helmet/index.d.ts.map +1 -1
  184. package/dist/server/helmet/index.js +11 -3
  185. package/dist/server/helmet/index.js.map +1 -1
  186. package/dist/server/links/index.d.ts +11 -6
  187. package/dist/server/links/index.d.ts.map +1 -1
  188. package/dist/server/links/index.js +11 -6
  189. package/dist/server/links/index.js.map +1 -1
  190. package/dist/server/metrics/index.d.ts +10 -3
  191. package/dist/server/metrics/index.d.ts.map +1 -1
  192. package/dist/server/metrics/index.js +10 -3
  193. package/dist/server/metrics/index.js.map +1 -1
  194. package/dist/server/multipart/index.d.ts +9 -3
  195. package/dist/server/multipart/index.d.ts.map +1 -1
  196. package/dist/server/multipart/index.js +9 -3
  197. package/dist/server/multipart/index.js.map +1 -1
  198. package/dist/server/proxy/index.d.ts +8 -2
  199. package/dist/server/proxy/index.d.ts.map +1 -1
  200. package/dist/server/proxy/index.js +8 -2
  201. package/dist/server/proxy/index.js.map +1 -1
  202. package/dist/server/rate-limit/index.d.ts +30 -35
  203. package/dist/server/rate-limit/index.d.ts.map +1 -1
  204. package/dist/server/rate-limit/index.js +18 -55
  205. package/dist/server/rate-limit/index.js.map +1 -1
  206. package/dist/server/static/index.d.ts +137 -4
  207. package/dist/server/static/index.d.ts.map +1 -1
  208. package/dist/server/static/index.js +1853 -5
  209. package/dist/server/static/index.js.map +1 -1
  210. package/dist/server/swagger/index.d.ts +309 -6
  211. package/dist/server/swagger/index.d.ts.map +1 -1
  212. package/dist/server/swagger/index.js +1854 -6
  213. package/dist/server/swagger/index.js.map +1 -1
  214. package/dist/sms/index.d.ts +309 -7
  215. package/dist/sms/index.d.ts.map +1 -1
  216. package/dist/sms/index.js +1856 -7
  217. package/dist/sms/index.js.map +1 -1
  218. package/dist/system/index.browser.js +1218 -0
  219. package/dist/system/index.browser.js.map +1 -0
  220. package/dist/{file → system}/index.d.ts +343 -16
  221. package/dist/system/index.d.ts.map +1 -0
  222. package/dist/{file → system}/index.js +419 -22
  223. package/dist/system/index.js.map +1 -0
  224. package/dist/thread/index.d.ts +11 -2
  225. package/dist/thread/index.d.ts.map +1 -1
  226. package/dist/thread/index.js +11 -2
  227. package/dist/thread/index.js.map +1 -1
  228. package/dist/topic/core/index.d.ts +12 -5
  229. package/dist/topic/core/index.d.ts.map +1 -1
  230. package/dist/topic/core/index.js +12 -5
  231. package/dist/topic/core/index.js.map +1 -1
  232. package/dist/vite/index.d.ts +5 -6272
  233. package/dist/vite/index.d.ts.map +1 -1
  234. package/dist/vite/index.js +23 -10
  235. package/dist/vite/index.js.map +1 -1
  236. package/dist/websocket/index.d.ts +12 -8
  237. package/dist/websocket/index.d.ts.map +1 -1
  238. package/dist/websocket/index.js +12 -8
  239. package/dist/websocket/index.js.map +1 -1
  240. package/package.json +82 -11
  241. package/src/api/audits/index.ts +10 -33
  242. package/src/api/files/__tests__/$bucket.spec.ts +1 -1
  243. package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
  244. package/src/api/files/controllers/FileController.spec.ts +1 -1
  245. package/src/api/files/index.ts +10 -3
  246. package/src/api/files/jobs/FileJobs.spec.ts +1 -1
  247. package/src/api/files/services/FileService.spec.ts +1 -1
  248. package/src/api/jobs/index.ts +10 -3
  249. package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
  250. package/src/api/keys/controllers/ApiKeyController.ts +103 -0
  251. package/src/api/keys/entities/apiKeyEntity.ts +41 -0
  252. package/src/api/keys/index.ts +49 -0
  253. package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
  254. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
  255. package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
  256. package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
  257. package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
  258. package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
  259. package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
  260. package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
  261. package/src/api/keys/services/ApiKeyService.ts +306 -0
  262. package/src/api/logs/TODO.md +55 -0
  263. package/src/api/notifications/index.ts +10 -4
  264. package/src/api/parameters/index.ts +9 -30
  265. package/src/api/parameters/primitives/$config.ts +12 -4
  266. package/src/api/parameters/services/ConfigStore.ts +9 -3
  267. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
  268. package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
  269. package/src/api/users/index.ts +14 -3
  270. package/src/api/users/primitives/$realm.ts +33 -5
  271. package/src/api/users/providers/RealmProvider.ts +1 -12
  272. package/src/api/users/services/SessionService.ts +1 -1
  273. package/src/api/verifications/controllers/VerificationController.ts +2 -0
  274. package/src/api/verifications/index.ts +10 -4
  275. package/src/batch/index.ts +9 -36
  276. package/src/batch/primitives/$batch.ts +0 -8
  277. package/src/batch/providers/BatchProvider.ts +29 -2
  278. package/src/bucket/__tests__/shared.ts +1 -1
  279. package/src/bucket/index.ts +13 -6
  280. package/src/bucket/primitives/$bucket.ts +1 -1
  281. package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
  282. package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
  283. package/src/cache/core/__tests__/shared.ts +30 -0
  284. package/src/cache/core/index.ts +11 -6
  285. package/src/cache/core/primitives/$cache.spec.ts +5 -0
  286. package/src/cache/core/providers/CacheProvider.ts +17 -0
  287. package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
  288. package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
  289. package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
  290. package/src/cli/apps/AlephaCli.ts +1 -14
  291. package/src/cli/apps/AlephaPackageBuilderCli.ts +10 -1
  292. package/src/cli/atoms/buildOptions.ts +99 -9
  293. package/src/cli/commands/build.ts +150 -37
  294. package/src/cli/commands/db.ts +22 -18
  295. package/src/cli/commands/deploy.ts +1 -1
  296. package/src/cli/commands/dev.ts +1 -20
  297. package/src/cli/commands/gen/env.ts +5 -2
  298. package/src/cli/commands/gen/openapi.ts +5 -2
  299. package/src/cli/commands/init.spec.ts +588 -0
  300. package/src/cli/commands/init.ts +115 -58
  301. package/src/cli/commands/lint.ts +7 -1
  302. package/src/cli/commands/typecheck.ts +11 -0
  303. package/src/cli/providers/AppEntryProvider.ts +1 -1
  304. package/src/cli/providers/ViteBuildProvider.ts +8 -50
  305. package/src/cli/providers/ViteDevServerProvider.ts +35 -16
  306. package/src/cli/services/AlephaCliUtils.ts +52 -121
  307. package/src/cli/services/PackageManagerUtils.ts +129 -11
  308. package/src/cli/services/ProjectScaffolder.spec.ts +97 -0
  309. package/src/cli/services/ProjectScaffolder.ts +148 -81
  310. package/src/cli/services/ViteUtils.ts +82 -0
  311. package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +37 -24
  312. package/src/cli/templates/apiAppSecurityTs.ts +11 -0
  313. package/src/cli/templates/apiIndexTs.ts +30 -0
  314. package/src/cli/templates/gitignore.ts +39 -0
  315. package/src/cli/{assets → templates}/mainCss.ts +11 -2
  316. package/src/cli/templates/mainServerTs.ts +33 -0
  317. package/src/cli/templates/webAppRouterTs.ts +74 -0
  318. package/src/cli/templates/webHelloComponentTsx.ts +30 -0
  319. package/src/command/helpers/Runner.spec.ts +139 -0
  320. package/src/command/helpers/Runner.ts +7 -22
  321. package/src/command/index.ts +12 -4
  322. package/src/command/providers/CliProvider.spec.ts +1392 -0
  323. package/src/command/providers/CliProvider.ts +320 -47
  324. package/src/core/Alepha.ts +34 -27
  325. package/src/core/__tests__/Alepha-start.spec.ts +4 -4
  326. package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
  327. package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
  328. package/src/core/index.shared.ts +1 -0
  329. package/src/core/index.ts +20 -0
  330. package/src/core/providers/EventManager.spec.ts +0 -71
  331. package/src/core/providers/EventManager.ts +3 -15
  332. package/src/core/providers/Json.ts +2 -14
  333. package/src/datetime/index.ts +15 -0
  334. package/src/email/index.ts +10 -5
  335. package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
  336. package/src/email/providers/LocalEmailProvider.ts +1 -1
  337. package/src/fake/__tests__/keyName.example.ts +1 -1
  338. package/src/fake/__tests__/keyName.spec.ts +5 -5
  339. package/src/fake/index.ts +9 -6
  340. package/src/fake/providers/FakeProvider.spec.ts +258 -40
  341. package/src/fake/providers/FakeProvider.ts +133 -19
  342. package/src/lock/core/index.ts +11 -4
  343. package/src/logger/index.ts +17 -66
  344. package/src/mcp/index.ts +10 -27
  345. package/src/mcp/transports/SseMcpTransport.ts +0 -11
  346. package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
  347. package/src/orm/index.browser.ts +2 -2
  348. package/src/orm/index.bun.ts +5 -3
  349. package/src/orm/index.ts +23 -53
  350. package/src/orm/providers/drivers/BunSqliteProvider.ts +5 -1
  351. package/src/orm/providers/drivers/CloudflareD1Provider.ts +57 -30
  352. package/src/orm/providers/drivers/DatabaseProvider.ts +9 -1
  353. package/src/orm/providers/drivers/NodeSqliteProvider.ts +4 -1
  354. package/src/orm/services/Repository.ts +7 -3
  355. package/src/queue/core/index.ts +14 -6
  356. package/src/react/auth/__tests__/$auth.spec.ts +202 -0
  357. package/src/react/auth/hooks/useAuth.ts +32 -0
  358. package/src/react/auth/index.browser.ts +13 -0
  359. package/src/react/auth/index.shared.ts +2 -0
  360. package/src/react/auth/index.ts +48 -0
  361. package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
  362. package/src/react/auth/services/ReactAuth.ts +135 -0
  363. package/src/react/core/__tests__/Router.spec.tsx +169 -0
  364. package/src/react/core/components/ClientOnly.tsx +49 -0
  365. package/src/react/core/components/ErrorBoundary.tsx +73 -0
  366. package/src/react/core/contexts/AlephaContext.ts +7 -0
  367. package/src/react/core/contexts/AlephaProvider.tsx +42 -0
  368. package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
  369. package/src/react/core/hooks/useAction.ts +480 -0
  370. package/src/react/core/hooks/useAlepha.ts +26 -0
  371. package/src/react/core/hooks/useClient.ts +17 -0
  372. package/src/react/core/hooks/useEvents.ts +51 -0
  373. package/src/react/core/hooks/useInject.ts +12 -0
  374. package/src/react/core/hooks/useStore.ts +52 -0
  375. package/src/react/core/index.ts +90 -0
  376. package/src/react/form/components/FormState.tsx +17 -0
  377. package/src/react/form/errors/FormValidationError.ts +18 -0
  378. package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
  379. package/src/react/form/hooks/useForm.ts +47 -0
  380. package/src/react/form/hooks/useFormState.ts +130 -0
  381. package/src/react/form/index.ts +44 -0
  382. package/src/react/form/services/FormModel.ts +614 -0
  383. package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
  384. package/src/react/head/helpers/SeoExpander.ts +142 -0
  385. package/src/react/head/hooks/useHead.spec.tsx +288 -0
  386. package/src/react/head/hooks/useHead.ts +62 -0
  387. package/src/react/head/index.browser.ts +26 -0
  388. package/src/react/head/index.ts +44 -0
  389. package/src/react/head/interfaces/Head.ts +105 -0
  390. package/src/react/head/primitives/$head.ts +25 -0
  391. package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
  392. package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
  393. package/src/react/head/providers/HeadProvider.ts +168 -0
  394. package/src/react/head/providers/ServerHeadProvider.ts +31 -0
  395. package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
  396. package/src/react/i18n/components/Localize.spec.tsx +357 -0
  397. package/src/react/i18n/components/Localize.tsx +35 -0
  398. package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
  399. package/src/react/i18n/hooks/useI18n.ts +18 -0
  400. package/src/react/i18n/index.ts +41 -0
  401. package/src/react/i18n/primitives/$dictionary.ts +69 -0
  402. package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
  403. package/src/react/i18n/providers/I18nProvider.ts +278 -0
  404. package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
  405. package/src/react/router/__tests__/page-head.spec.ts +48 -0
  406. package/src/react/router/__tests__/seo-head.spec.ts +125 -0
  407. package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
  408. package/src/react/router/components/ErrorViewer.tsx +872 -0
  409. package/src/react/router/components/Link.tsx +23 -0
  410. package/src/react/router/components/NestedView.tsx +223 -0
  411. package/src/react/router/components/NotFound.tsx +30 -0
  412. package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  413. package/src/react/router/contexts/RouterLayerContext.ts +12 -0
  414. package/src/react/router/errors/Redirection.ts +28 -0
  415. package/src/react/router/hooks/useActive.ts +52 -0
  416. package/src/react/router/hooks/useQueryParams.ts +63 -0
  417. package/src/react/router/hooks/useRouter.ts +20 -0
  418. package/src/react/router/hooks/useRouterState.ts +11 -0
  419. package/src/react/router/index.browser.ts +45 -0
  420. package/src/react/router/index.shared.ts +19 -0
  421. package/src/react/router/index.ts +146 -0
  422. package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
  423. package/src/react/router/primitives/$page.spec.tsx +676 -0
  424. package/src/react/router/primitives/$page.ts +489 -0
  425. package/src/react/router/providers/ReactBrowserProvider.ts +312 -0
  426. package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
  427. package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
  428. package/src/react/router/providers/ReactPageProvider.ts +726 -0
  429. package/src/react/router/providers/ReactPreloadProvider.spec.ts +142 -0
  430. package/src/react/router/providers/ReactPreloadProvider.ts +85 -0
  431. package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
  432. package/src/react/router/providers/ReactServerProvider.ts +487 -0
  433. package/src/react/router/providers/ReactServerTemplateProvider.spec.ts +210 -0
  434. package/src/react/router/providers/ReactServerTemplateProvider.ts +542 -0
  435. package/src/react/router/providers/SSRManifestProvider.ts +334 -0
  436. package/src/react/router/services/ReactPageServerService.ts +48 -0
  437. package/src/react/router/services/ReactPageService.ts +27 -0
  438. package/src/react/router/services/ReactRouter.ts +262 -0
  439. package/src/react/websocket/hooks/useRoom.tsx +242 -0
  440. package/src/react/websocket/index.ts +7 -0
  441. package/src/redis/__tests__/redis.spec.ts +13 -0
  442. package/src/redis/index.ts +9 -25
  443. package/src/redis/providers/BunRedisProvider.ts +9 -0
  444. package/src/redis/providers/NodeRedisProvider.ts +8 -0
  445. package/src/redis/providers/RedisProvider.ts +16 -0
  446. package/src/retry/index.ts +11 -2
  447. package/src/router/index.ts +15 -0
  448. package/src/scheduler/index.ts +11 -2
  449. package/src/security/__tests__/BasicAuth.spec.ts +2 -0
  450. package/src/security/__tests__/ServerSecurityProvider.spec.ts +90 -5
  451. package/src/security/index.ts +15 -10
  452. package/src/security/interfaces/IssuerResolver.ts +27 -0
  453. package/src/security/primitives/$issuer.ts +55 -0
  454. package/src/security/providers/SecurityProvider.ts +179 -0
  455. package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
  456. package/src/security/providers/ServerSecurityProvider.ts +63 -41
  457. package/src/server/auth/index.ts +12 -7
  458. package/src/server/cache/index.ts +7 -22
  459. package/src/server/compress/index.ts +10 -2
  460. package/src/server/cookies/index.ts +7 -5
  461. package/src/server/cookies/primitives/$cookie.ts +33 -11
  462. package/src/server/core/index.ts +16 -6
  463. package/src/server/core/interfaces/ServerRequest.ts +83 -1
  464. package/src/server/core/primitives/$action.spec.ts +1 -1
  465. package/src/server/core/primitives/$action.ts +8 -3
  466. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +9 -3
  467. package/src/server/core/providers/NodeHttpServerProvider.ts +9 -3
  468. package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
  469. package/src/server/core/services/ServerRequestParser.ts +306 -13
  470. package/src/server/cors/index.ts +7 -21
  471. package/src/server/cors/primitives/$cors.ts +6 -2
  472. package/src/server/health/index.ts +8 -2
  473. package/src/server/helmet/index.ts +11 -3
  474. package/src/server/links/index.ts +11 -6
  475. package/src/server/metrics/index.ts +10 -3
  476. package/src/server/multipart/index.ts +9 -3
  477. package/src/server/proxy/index.ts +8 -2
  478. package/src/server/rate-limit/index.ts +21 -25
  479. package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
  480. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
  481. package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
  482. package/src/server/static/index.ts +8 -2
  483. package/src/server/static/providers/ServerStaticProvider.ts +1 -1
  484. package/src/server/swagger/index.ts +9 -4
  485. package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
  486. package/src/sms/index.ts +9 -5
  487. package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
  488. package/src/sms/providers/LocalSmsProvider.ts +1 -1
  489. package/src/system/index.browser.ts +36 -0
  490. package/src/system/index.ts +62 -0
  491. package/src/system/index.workerd.ts +1 -0
  492. package/src/{file → system}/providers/FileSystemProvider.ts +24 -0
  493. package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
  494. package/src/system/providers/MemoryShellProvider.ts +164 -0
  495. package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
  496. package/src/{file → system}/providers/NodeFileSystemProvider.ts +47 -2
  497. package/src/system/providers/NodeShellProvider.ts +184 -0
  498. package/src/system/providers/ShellProvider.ts +74 -0
  499. package/src/{file → system}/services/FileDetector.spec.ts +2 -2
  500. package/src/thread/index.ts +11 -2
  501. package/src/topic/core/index.ts +12 -5
  502. package/src/vite/tasks/buildClient.ts +2 -7
  503. package/src/vite/tasks/buildServer.ts +19 -13
  504. package/src/vite/tasks/generateCloudflare.ts +10 -7
  505. package/src/vite/tasks/generateDocker.ts +4 -0
  506. package/src/websocket/index.ts +12 -8
  507. package/dist/file/index.d.ts.map +0 -1
  508. package/dist/file/index.js.map +0 -1
  509. package/src/cli/assets/apiIndexTs.ts +0 -16
  510. package/src/cli/assets/mainServerTs.ts +0 -24
  511. package/src/cli/assets/webAppRouterTs.ts +0 -16
  512. package/src/cli/assets/webHelloComponentTsx.ts +0 -20
  513. package/src/cli/providers/ViteTemplateProvider.ts +0 -27
  514. package/src/file/index.ts +0 -43
  515. /package/src/cli/{assets → templates}/apiHelloControllerTs.ts +0 -0
  516. /package/src/cli/{assets → templates}/biomeJson.ts +0 -0
  517. /package/src/cli/{assets → templates}/dummySpecTs.ts +0 -0
  518. /package/src/cli/{assets → templates}/editorconfig.ts +0 -0
  519. /package/src/cli/{assets → templates}/mainBrowserTs.ts +0 -0
  520. /package/src/cli/{assets → templates}/tsconfigJson.ts +0 -0
  521. /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
  522. /package/src/{file → system}/errors/FileError.ts +0 -0
  523. /package/src/{file → system}/services/FileDetector.ts +0 -0
@@ -0,0 +1,142 @@
1
+ import { Alepha } from "alepha";
2
+ import { HttpClient, ServerProvider } from "alepha/server";
3
+ import { describe, it } from "vitest";
4
+ import { ssrManifestAtom } from "../atoms/ssrManifestAtom.ts";
5
+ import { $page } from "../index.ts";
6
+
7
+ describe("ReactPreloadProvider", () => {
8
+ class App {
9
+ home = $page({
10
+ path: "/",
11
+ component: () => "Hello World",
12
+ });
13
+ }
14
+
15
+ it("should add Link header with entry assets to HTML responses", async ({
16
+ expect,
17
+ }) => {
18
+ const alepha = Alepha.create({
19
+ env: { LOG_LEVEL: "error", SERVER_PORT: 0 },
20
+ }).with(App);
21
+
22
+ // Set up mock SSR manifest with entry assets
23
+ alepha.store.set(ssrManifestAtom, {
24
+ client: {
25
+ "src/entry.tsx": {
26
+ file: "assets/entry.abc123.js",
27
+ isEntry: true,
28
+ css: ["assets/style.def456.css"],
29
+ },
30
+ },
31
+ });
32
+
33
+ await alepha.start();
34
+
35
+ const server = alepha.inject(ServerProvider);
36
+ const http = alepha.inject(HttpClient);
37
+
38
+ const response = await http.fetch(`${server.hostname}/`);
39
+
40
+ // Check that the Link header is present
41
+ const linkHeader = response.headers.get("link");
42
+ expect(linkHeader).toBeTruthy();
43
+ expect(linkHeader).toContain(
44
+ "</assets/style.def456.css>; rel=preload; as=style",
45
+ );
46
+ expect(linkHeader).toContain(
47
+ "</assets/entry.abc123.js>; rel=modulepreload",
48
+ );
49
+
50
+ await alepha.stop();
51
+ });
52
+
53
+ it("should not add Link header when no SSR manifest is available", async ({
54
+ expect,
55
+ }) => {
56
+ const alepha = Alepha.create({
57
+ env: { LOG_LEVEL: "error", SERVER_PORT: 0 },
58
+ }).with(App);
59
+
60
+ // No SSR manifest set
61
+
62
+ await alepha.start();
63
+
64
+ const server = alepha.inject(ServerProvider);
65
+ const http = alepha.inject(HttpClient);
66
+
67
+ const response = await http.fetch(`${server.hostname}/`);
68
+
69
+ // Link header should not be present (or empty)
70
+ const linkHeader = response.headers.get("link");
71
+ expect(linkHeader).toBeNull();
72
+
73
+ await alepha.stop();
74
+ });
75
+
76
+ it("should handle base path in manifest", async ({ expect }) => {
77
+ const alepha = Alepha.create({
78
+ env: { LOG_LEVEL: "error", SERVER_PORT: 0 },
79
+ }).with(App);
80
+
81
+ // Set up mock SSR manifest with base path
82
+ alepha.store.set(ssrManifestAtom, {
83
+ base: "/app",
84
+ client: {
85
+ "src/entry.tsx": {
86
+ file: "assets/entry.abc123.js",
87
+ isEntry: true,
88
+ css: ["assets/style.def456.css"],
89
+ },
90
+ },
91
+ });
92
+
93
+ await alepha.start();
94
+
95
+ const server = alepha.inject(ServerProvider);
96
+ const http = alepha.inject(HttpClient);
97
+
98
+ const response = await http.fetch(`${server.hostname}/`);
99
+
100
+ // Check that the Link header includes base path
101
+ const linkHeader = response.headers.get("link");
102
+ expect(linkHeader).toBeTruthy();
103
+ expect(linkHeader).toContain(
104
+ "</app/assets/style.def456.css>; rel=preload; as=style",
105
+ );
106
+ expect(linkHeader).toContain(
107
+ "</app/assets/entry.abc123.js>; rel=modulepreload",
108
+ );
109
+
110
+ await alepha.stop();
111
+ });
112
+
113
+ it("should handle entry with only JS (no CSS)", async ({ expect }) => {
114
+ const alepha = Alepha.create({
115
+ env: { LOG_LEVEL: "error", SERVER_PORT: 0 },
116
+ }).with(App);
117
+
118
+ // Set up mock SSR manifest with only JS entry
119
+ alepha.store.set(ssrManifestAtom, {
120
+ client: {
121
+ "src/entry.tsx": {
122
+ file: "assets/entry.abc123.js",
123
+ isEntry: true,
124
+ },
125
+ },
126
+ });
127
+
128
+ await alepha.start();
129
+
130
+ const server = alepha.inject(ServerProvider);
131
+ const http = alepha.inject(HttpClient);
132
+
133
+ const response = await http.fetch(`${server.hostname}/`);
134
+
135
+ // Check that the Link header contains only JS modulepreload
136
+ const linkHeader = response.headers.get("link");
137
+ expect(linkHeader).toBeTruthy();
138
+ expect(linkHeader).toBe("</assets/entry.abc123.js>; rel=modulepreload");
139
+
140
+ await alepha.stop();
141
+ });
142
+ });
@@ -0,0 +1,85 @@
1
+ import { $hook, $inject, Alepha } from "alepha";
2
+ import { SSRManifestProvider } from "./SSRManifestProvider.ts";
3
+
4
+ /**
5
+ * Adds HTTP Link headers for preloading entry assets.
6
+ *
7
+ * Benefits:
8
+ * - Early Hints (103): Servers can send preload hints before the full response
9
+ * - CDN optimization: Many CDNs use Link headers to optimize asset delivery
10
+ * - Browser prefetching: Browsers can start fetching resources earlier
11
+ *
12
+ * The Link header is computed once at first request and cached for reuse.
13
+ */
14
+ export class ReactPreloadProvider {
15
+ protected readonly alepha = $inject(Alepha);
16
+ protected readonly ssrManifest = $inject(SSRManifestProvider);
17
+
18
+ /**
19
+ * Cached Link header value - computed once, reused for all requests.
20
+ */
21
+ protected cachedLinkHeader: string | null | undefined;
22
+
23
+ /**
24
+ * Build the Link header string from entry assets.
25
+ *
26
+ * Format: <url>; rel=preload; as=type, <url>; rel=modulepreload
27
+ *
28
+ * @returns Link header string or null if no assets
29
+ */
30
+ protected buildLinkHeader(): string | null {
31
+ const assets = this.ssrManifest.getEntryAssets();
32
+ if (!assets) return null;
33
+
34
+ const links: string[] = [];
35
+
36
+ // CSS - preload as style
37
+ for (const css of assets.css) {
38
+ links.push(`<${css}>; rel=preload; as=style`);
39
+ }
40
+
41
+ // JS - modulepreload for ES modules
42
+ if (assets.js) {
43
+ links.push(`<${assets.js}>; rel=modulepreload`);
44
+ }
45
+
46
+ return links.length > 0 ? links.join(", ") : null;
47
+ }
48
+
49
+ /**
50
+ * Get the cached Link header, computing it on first access.
51
+ */
52
+ protected getLinkHeader(): string | null {
53
+ if (this.cachedLinkHeader === undefined) {
54
+ this.cachedLinkHeader = this.buildLinkHeader();
55
+ }
56
+ return this.cachedLinkHeader;
57
+ }
58
+
59
+ /**
60
+ * Add Link header to HTML responses for asset preloading.
61
+ */
62
+ protected readonly onResponse = $hook({
63
+ on: "server:onResponse",
64
+ priority: "first",
65
+ handler: ({ response }) => {
66
+ // Only add to HTML responses (SSR pages)
67
+ const contentType = response.headers["content-type"];
68
+ if (!contentType || !contentType.includes("text/html")) {
69
+ return;
70
+ }
71
+
72
+ const linkHeader = this.getLinkHeader();
73
+ if (!linkHeader) {
74
+ return;
75
+ }
76
+
77
+ // Append to existing Link header if present
78
+ if (response.headers.link) {
79
+ response.headers.link = `${response.headers.link}, ${linkHeader}`;
80
+ } else {
81
+ response.headers.link = linkHeader;
82
+ }
83
+ },
84
+ });
85
+ }
@@ -0,0 +1,316 @@
1
+ import { test } from "vitest";
2
+
3
+ // Simple mock for testing template functionality without full Alepha setup
4
+ class MockReactServerProvider {
5
+ protected readonly env = { REACT_ROOT_ID: "root" };
6
+ protected readonly ROOT_DIV_REGEX = new RegExp(
7
+ `<div([^>]*)\\s+id=["']${this.env.REACT_ROOT_ID}["']([^>]*)>(.*?)<\\/div>`,
8
+ "is",
9
+ );
10
+ protected preprocessedTemplate: any = null;
11
+
12
+ public preprocessTemplate(template: string) {
13
+ // Find the body close tag for script injection
14
+ const bodyCloseMatch = template.match(/<\/body>/i);
15
+ const bodyCloseIndex = bodyCloseMatch?.index ?? template.length;
16
+
17
+ const beforeScript = template.substring(0, bodyCloseIndex);
18
+ const afterScript = template.substring(bodyCloseIndex);
19
+
20
+ // Check if there's an existing root div
21
+ const rootDivMatch = beforeScript.match(this.ROOT_DIV_REGEX);
22
+
23
+ if (rootDivMatch) {
24
+ // Split around the existing root div content
25
+ const beforeDiv = beforeScript.substring(0, rootDivMatch.index!);
26
+ const afterDivStart = rootDivMatch.index! + rootDivMatch[0].length;
27
+ const afterDiv = beforeScript.substring(afterDivStart);
28
+
29
+ const beforeApp = `${beforeDiv}<div${rootDivMatch[1]} id="${this.env.REACT_ROOT_ID}"${rootDivMatch[2]}>`;
30
+ const afterApp = `</div>${afterDiv}`;
31
+
32
+ return { beforeApp, afterApp, beforeScript: "", afterScript };
33
+ }
34
+
35
+ // No existing root div, find body tag to inject new div
36
+ const bodyMatch = beforeScript.match(/<body([^>]*)>/i);
37
+ if (bodyMatch) {
38
+ const beforeBody = beforeScript.substring(
39
+ 0,
40
+ bodyMatch.index! + bodyMatch[0].length,
41
+ );
42
+ const afterBody = beforeScript.substring(
43
+ bodyMatch.index! + bodyMatch[0].length,
44
+ );
45
+
46
+ const beforeApp = `${beforeBody}<div id="${this.env.REACT_ROOT_ID}">`;
47
+ const afterApp = `</div>${afterBody}`;
48
+
49
+ return { beforeApp, afterApp, beforeScript: "", afterScript };
50
+ }
51
+
52
+ // Fallback: no body tag found, just wrap everything
53
+ return {
54
+ beforeApp: `<div id="${this.env.REACT_ROOT_ID}">`,
55
+ afterApp: `</div>`,
56
+ beforeScript,
57
+ afterScript,
58
+ };
59
+ }
60
+
61
+ public fillTemplate(response: { html: string }, app: string, script: string) {
62
+ if (!this.preprocessedTemplate) {
63
+ // Fallback to old logic if preprocessing failed
64
+ this.preprocessedTemplate = this.preprocessTemplate(response.html);
65
+ }
66
+
67
+ // Pure concatenation - no regex replacements needed
68
+ response.html =
69
+ this.preprocessedTemplate.beforeApp +
70
+ app +
71
+ this.preprocessedTemplate.afterApp +
72
+ script +
73
+ this.preprocessedTemplate.afterScript;
74
+ }
75
+ }
76
+
77
+ const setup = (env?: any) => {
78
+ const provider = new MockReactServerProvider();
79
+ if (env?.REACT_ROOT_ID) {
80
+ (provider as any).env.REACT_ROOT_ID = env.REACT_ROOT_ID;
81
+ (provider as any).ROOT_DIV_REGEX = new RegExp(
82
+ `<div([^>]*)\\s+id=["']${env.REACT_ROOT_ID}["']([^>]*)>(.*?)<\\/div>`,
83
+ "is",
84
+ );
85
+ }
86
+ return { provider };
87
+ };
88
+
89
+ test("ReactServerProvider - preprocessTemplate with existing root div", async ({
90
+ expect,
91
+ }) => {
92
+ const { provider } = setup();
93
+
94
+ const template = `<!DOCTYPE html>
95
+ <html>
96
+ <head><title>Test</title></head>
97
+ <body>
98
+ <div id="root">existing content</div>
99
+ </body>
100
+ </html>`;
101
+
102
+ const preprocessed = provider.preprocessTemplate(template);
103
+
104
+ expect(preprocessed.beforeApp).toBe(
105
+ `<!DOCTYPE html>
106
+ <html>
107
+ <head><title>Test</title></head>
108
+ <body>
109
+ <div id="root">`,
110
+ );
111
+ expect(preprocessed.afterApp).toBe(`</div>
112
+ `);
113
+ expect(preprocessed.beforeScript).toBe("");
114
+ expect(preprocessed.afterScript).toBe(`</body>
115
+ </html>`);
116
+ });
117
+
118
+ test("ReactServerProvider - preprocessTemplate without root div", async ({
119
+ expect,
120
+ }) => {
121
+ const { provider } = setup();
122
+
123
+ const template = `<!DOCTYPE html>
124
+ <html>
125
+ <head><title>Test</title></head>
126
+ <body>
127
+ <h1>Welcome</h1>
128
+ </body>
129
+ </html>`;
130
+
131
+ const preprocessed = provider.preprocessTemplate(template);
132
+
133
+ expect(preprocessed.beforeApp).toBe(
134
+ `<!DOCTYPE html>
135
+ <html>
136
+ <head><title>Test</title></head>
137
+ <body><div id="root">`,
138
+ );
139
+ expect(preprocessed.afterApp).toBe(`</div>
140
+ <h1>Welcome</h1>
141
+ `);
142
+ expect(preprocessed.beforeScript).toBe("");
143
+ expect(preprocessed.afterScript).toBe(`</body>
144
+ </html>`);
145
+ });
146
+
147
+ test("ReactServerProvider - preprocessTemplate with custom root ID", async ({
148
+ expect,
149
+ }) => {
150
+ const { provider } = setup({ REACT_ROOT_ID: "app" });
151
+
152
+ const template = `<!DOCTYPE html>
153
+ <html>
154
+ <head><title>Test</title></head>
155
+ <body>
156
+ <div id="app">existing content</div>
157
+ </body>
158
+ </html>`;
159
+
160
+ const preprocessed = provider.preprocessTemplate(template);
161
+
162
+ expect(preprocessed.beforeApp).toBe(
163
+ `<!DOCTYPE html>
164
+ <html>
165
+ <head><title>Test</title></head>
166
+ <body>
167
+ <div id="app">`,
168
+ );
169
+ expect(preprocessed.afterApp).toBe(`</div>
170
+ `);
171
+ });
172
+
173
+ test("ReactServerProvider - preprocessTemplate with root div with attributes", async ({
174
+ expect,
175
+ }) => {
176
+ const { provider } = setup();
177
+
178
+ const template = `<!DOCTYPE html>
179
+ <html>
180
+ <body>
181
+ <div class="container" id="root" data-test="true">existing content</div>
182
+ </body>
183
+ </html>`;
184
+
185
+ const preprocessed = provider.preprocessTemplate(template);
186
+
187
+ expect(preprocessed.beforeApp).toBe(
188
+ `<!DOCTYPE html>
189
+ <html>
190
+ <body>
191
+ <div class="container" id="root" data-test="true">`,
192
+ );
193
+ expect(preprocessed.afterApp).toBe(`</div>
194
+ `);
195
+ });
196
+
197
+ test("ReactServerProvider - preprocessTemplate fallback (no body tag)", async ({
198
+ expect,
199
+ }) => {
200
+ const { provider } = setup();
201
+
202
+ const template = `<html><div>content</div></html>`;
203
+
204
+ const preprocessed = provider.preprocessTemplate(template);
205
+
206
+ expect(preprocessed.beforeApp).toBe(`<div id="root">`);
207
+ expect(preprocessed.afterApp).toBe(`</div>`);
208
+ expect(preprocessed.beforeScript).toBe(`<html><div>content</div></html>`);
209
+ expect(preprocessed.afterScript).toBe(``);
210
+ });
211
+
212
+ test("ReactServerProvider - fillTemplate concatenation", async ({ expect }) => {
213
+ const { provider } = setup();
214
+
215
+ const template = `<!DOCTYPE html>
216
+ <html>
217
+ <head><title>Test</title></head>
218
+ <body>
219
+ <div id="root">existing</div>
220
+ </body>
221
+ </html>`;
222
+
223
+ // Preprocess the template
224
+ const preprocessed = provider.preprocessTemplate(template);
225
+ (provider as any).preprocessedTemplate = preprocessed;
226
+
227
+ const response = { html: "" };
228
+ const app = "<h1>Hello World</h1>";
229
+ const script = '<script>window.__ssr={"test":true}</script>';
230
+
231
+ provider.fillTemplate(response, app, script);
232
+
233
+ const expected = `<!DOCTYPE html>
234
+ <html>
235
+ <head><title>Test</title></head>
236
+ <body>
237
+ <div id="root"><h1>Hello World</h1></div>
238
+ <script>window.__ssr={"test":true}</script></body>
239
+ </html>`;
240
+
241
+ expect(response.html).toBe(expected);
242
+ });
243
+
244
+ test("ReactServerProvider - fillTemplate without preprocessed template (fallback)", async ({
245
+ expect,
246
+ }) => {
247
+ const { provider } = setup();
248
+
249
+ const template = `<!DOCTYPE html>
250
+ <html>
251
+ <body>
252
+ <div id="root">existing</div>
253
+ </body>
254
+ </html>`;
255
+
256
+ const response = { html: template };
257
+ const app = "<h1>Hello World</h1>";
258
+ const script = '<script>window.__ssr={"test":true}</script>';
259
+
260
+ // Don't set preprocessedTemplate to test fallback
261
+ (provider as any).preprocessedTemplate = null;
262
+
263
+ provider.fillTemplate(response, app, script);
264
+
265
+ const expected = `<!DOCTYPE html>
266
+ <html>
267
+ <body>
268
+ <div id="root"><h1>Hello World</h1></div>
269
+ <script>window.__ssr={"test":true}</script></body>
270
+ </html>`;
271
+
272
+ expect(response.html).toBe(expected);
273
+ });
274
+
275
+ test("ReactServerProvider - fillTemplate performance comparison", async ({
276
+ expect,
277
+ }) => {
278
+ const { provider } = setup();
279
+
280
+ const template = `<!DOCTYPE html>
281
+ <html>
282
+ <head><title>Performance Test</title></head>
283
+ <body>
284
+ <div id="root">existing content</div>
285
+ <script>console.log('existing');</script>
286
+ </body>
287
+ </html>`;
288
+
289
+ const app = "<div><h1>Hello World</h1><p>This is a test</p></div>";
290
+ const script = '<script>window.__ssr={"data":"value","count":42}</script>';
291
+
292
+ // Test with preprocessing (should be faster)
293
+ const preprocessed = provider.preprocessTemplate(template);
294
+ (provider as any).preprocessedTemplate = preprocessed;
295
+
296
+ const response1 = { html: "" };
297
+ const start1 = performance.now();
298
+ provider.fillTemplate(response1, app, script);
299
+ const time1 = performance.now() - start1;
300
+
301
+ // Test fallback without preprocessing (should work but potentially slower)
302
+ (provider as any).preprocessedTemplate = null;
303
+ const response2 = { html: template };
304
+ const start2 = performance.now();
305
+ provider.fillTemplate(response2, app, script);
306
+ const time2 = performance.now() - start2;
307
+
308
+ // Both should produce the same result
309
+ expect(response1.html).toBe(response2.html);
310
+
311
+ // The result should contain the app content
312
+ expect(response1.html).toContain("<h1>Hello World</h1>");
313
+ expect(response1.html).toContain(
314
+ '<script>window.__ssr={"data":"value","count":42}</script>',
315
+ );
316
+ });