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,726 @@
1
+ import {
2
+ $env,
3
+ $hook,
4
+ $inject,
5
+ Alepha,
6
+ AlephaError,
7
+ type Static,
8
+ type TSchema,
9
+ t,
10
+ } from "alepha";
11
+ import { $logger } from "alepha/logger";
12
+ import { AlephaContext, ClientOnly } from "alepha/react";
13
+ import type { Head } from "alepha/react/head";
14
+ import { createElement, type ReactNode, StrictMode } from "react";
15
+ import ErrorViewer from "../components/ErrorViewer.tsx";
16
+ import NestedView from "../components/NestedView.tsx";
17
+ import NotFoundPage from "../components/NotFound.tsx";
18
+ import { RouterLayerContext } from "../contexts/RouterLayerContext.ts";
19
+ import { Redirection } from "../errors/Redirection.ts";
20
+ import {
21
+ $page,
22
+ type ErrorHandler,
23
+ type PagePrimitive,
24
+ type PagePrimitiveOptions,
25
+ } from "../primitives/$page.ts";
26
+
27
+ const envSchema = t.object({
28
+ REACT_STRICT_MODE: t.boolean({ default: true }),
29
+ });
30
+
31
+ declare module "alepha" {
32
+ export interface Env extends Partial<Static<typeof envSchema>> {}
33
+ }
34
+
35
+ /**
36
+ * Handle page routes for React applications. (Browser and Server)
37
+ */
38
+ export class ReactPageProvider {
39
+ protected readonly log = $logger();
40
+ protected readonly env = $env(envSchema);
41
+ protected readonly alepha = $inject(Alepha);
42
+ protected readonly pages: PageRoute[] = [];
43
+
44
+ public getPages(): PageRoute[] {
45
+ return this.pages;
46
+ }
47
+
48
+ public getConcretePages(): ConcretePageRoute[] {
49
+ const pages: ConcretePageRoute[] = [];
50
+ for (const page of this.pages) {
51
+ if (page.children && page.children.length > 0) {
52
+ continue;
53
+ }
54
+
55
+ // check if the page has dynamic params
56
+ const fullPath = this.pathname(page.name);
57
+ if (fullPath.includes(":") || fullPath.includes("*")) {
58
+ if (typeof page.static === "object") {
59
+ const entries = page.static.entries;
60
+ if (entries && entries.length > 0) {
61
+ for (const entry of entries) {
62
+ const params = entry.params as Record<string, string>;
63
+ const path = this.compile(page.path ?? "", params);
64
+ if (!path.includes(":") && !path.includes("*")) {
65
+ pages.push({
66
+ ...page,
67
+ name: params[Object.keys(params)[0]],
68
+ staticName: page.name,
69
+ path,
70
+ ...entry,
71
+ });
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ continue;
78
+ }
79
+
80
+ pages.push(page);
81
+ }
82
+ return pages;
83
+ }
84
+
85
+ public page(name: string): PageRoute {
86
+ for (const page of this.pages) {
87
+ if (page.name === name) {
88
+ return page;
89
+ }
90
+ }
91
+
92
+ throw new AlephaError(`Page '${name}' not found`);
93
+ }
94
+
95
+ public pathname(
96
+ name: string,
97
+ options: {
98
+ params?: Record<string, string>;
99
+ query?: Record<string, string>;
100
+ } = {},
101
+ ) {
102
+ const page = this.page(name);
103
+ if (!page) {
104
+ throw new AlephaError(`Page ${name} not found`);
105
+ }
106
+
107
+ let url = page.path ?? "";
108
+ let parent = page.parent;
109
+ while (parent) {
110
+ url = `${parent.path ?? ""}/${url}`;
111
+ parent = parent.parent;
112
+ }
113
+
114
+ url = this.compile(url, options.params ?? {});
115
+
116
+ if (options.query) {
117
+ const query = new URLSearchParams(options.query);
118
+ if (query.toString()) {
119
+ url += `?${query.toString()}`;
120
+ }
121
+ }
122
+
123
+ return url.replace(/\/\/+/g, "/") || "/";
124
+ }
125
+
126
+ public url(
127
+ name: string,
128
+ options: { params?: Record<string, string>; host?: string } = {},
129
+ ): URL {
130
+ return new URL(
131
+ this.pathname(name, options),
132
+ // use provided base or default to http://localhost
133
+ options.host ?? `http://localhost`,
134
+ );
135
+ }
136
+
137
+ public root(state: ReactRouterState): ReactNode {
138
+ const root = createElement(
139
+ AlephaContext.Provider,
140
+ { value: this.alepha },
141
+ createElement(NestedView, {}, state.layers[0]?.element),
142
+ );
143
+
144
+ if (this.env.REACT_STRICT_MODE) {
145
+ return createElement(StrictMode, {}, root);
146
+ }
147
+
148
+ return root;
149
+ }
150
+
151
+ protected convertStringObjectToObject = (
152
+ schema?: TSchema,
153
+ value?: any,
154
+ ): any => {
155
+ if (t.schema.isObject(schema) && typeof value === "object") {
156
+ for (const key in schema.properties) {
157
+ if (
158
+ t.schema.isObject(schema.properties[key]) &&
159
+ typeof value[key] === "string"
160
+ ) {
161
+ try {
162
+ value[key] = this.alepha.codec.decode(
163
+ schema.properties[key],
164
+ decodeURIComponent(value[key]),
165
+ );
166
+ } catch (e) {
167
+ // ignore
168
+ }
169
+ }
170
+ }
171
+ }
172
+ return value;
173
+ };
174
+
175
+ /**
176
+ * Create a new RouterState based on a given route and request.
177
+ * This method resolves the layers for the route, applying any query and params schemas defined in the route.
178
+ * It also handles errors and redirects.
179
+ */
180
+ public async createLayers(
181
+ route: PageRoute,
182
+ state: ReactRouterState,
183
+ previous: PreviousLayerData[] = [],
184
+ ): Promise<CreateLayersResult> {
185
+ let context: Record<string, any> = {}; // all props
186
+ const stack: Array<RouterStackItem> = [{ route }]; // stack of routes
187
+
188
+ let parent = route.parent;
189
+ while (parent) {
190
+ stack.unshift({ route: parent });
191
+ parent = parent.parent;
192
+ }
193
+
194
+ let forceRefresh = false;
195
+
196
+ for (let i = 0; i < stack.length; i++) {
197
+ const it = stack[i];
198
+ const route = it.route;
199
+ const config: Record<string, any> = {};
200
+
201
+ try {
202
+ this.convertStringObjectToObject(route.schema?.query, state.query);
203
+ config.query = route.schema?.query
204
+ ? this.alepha.codec.decode(route.schema.query, state.query)
205
+ : {};
206
+ } catch (e) {
207
+ it.error = e as Error;
208
+ break;
209
+ }
210
+
211
+ try {
212
+ config.params = route.schema?.params
213
+ ? this.alepha.codec.decode(route.schema.params, state.params)
214
+ : {};
215
+ } catch (e) {
216
+ it.error = e as Error;
217
+ break;
218
+ }
219
+
220
+ // save config
221
+ it.config = {
222
+ ...config,
223
+ };
224
+
225
+ // check if previous layer is the same, reuse if possible
226
+ if (previous?.[i] && !forceRefresh && previous[i].name === route.name) {
227
+ const url = (str?: string) => (str ? str.replace(/\/\/+/g, "/") : "/");
228
+
229
+ const prev = JSON.stringify({
230
+ part: url(previous[i].part),
231
+ params: previous[i].config?.params ?? {},
232
+ });
233
+
234
+ const curr = JSON.stringify({
235
+ part: url(route.path),
236
+ params: config.params ?? {},
237
+ });
238
+
239
+ if (prev === curr) {
240
+ // part is the same, reuse previous layer
241
+ it.props = previous[i].props;
242
+ it.error = previous[i].error;
243
+ it.cache = true;
244
+ context = {
245
+ ...context,
246
+ ...it.props,
247
+ };
248
+ continue;
249
+ }
250
+
251
+ // part is different, force refresh of next layers
252
+ forceRefresh = true;
253
+ }
254
+
255
+ // no loader, render a basic view by default
256
+ if (!route.loader) {
257
+ continue;
258
+ }
259
+
260
+ try {
261
+ const args = Object.create(state);
262
+ Object.assign(args, config, context);
263
+ const props = (await route.loader?.(args)) ?? {};
264
+
265
+ // save props
266
+ it.props = {
267
+ ...props,
268
+ };
269
+
270
+ // add props to context
271
+ context = {
272
+ ...context,
273
+ ...props,
274
+ };
275
+ } catch (e) {
276
+ // check if we need to redirect
277
+ if (e instanceof Redirection) {
278
+ return {
279
+ redirect: e.redirect,
280
+ };
281
+ }
282
+
283
+ this.log.error("Page loader has failed", e);
284
+
285
+ it.error = e as Error;
286
+ break;
287
+ }
288
+ }
289
+
290
+ let acc = "";
291
+ for (let i = 0; i < stack.length; i++) {
292
+ const it = stack[i];
293
+ const props = it.props ?? {};
294
+
295
+ const params = { ...it.config?.params };
296
+ for (const key of Object.keys(params)) {
297
+ params[key] = String(params[key]);
298
+ }
299
+
300
+ acc += "/";
301
+ acc += it.route.path ? this.compile(it.route.path, params) : "";
302
+ const path = acc.replace(/\/+/, "/");
303
+ const localErrorHandler = this.getErrorHandler(it.route);
304
+ if (localErrorHandler) {
305
+ const onErrorParent = state.onError;
306
+ state.onError = (error, context) => {
307
+ const result = localErrorHandler(error, context);
308
+ // if nothing happen, call the parent
309
+ if (result === undefined) {
310
+ return onErrorParent(error, context);
311
+ }
312
+ return result;
313
+ };
314
+ }
315
+
316
+ // normal use case
317
+ if (!it.error) {
318
+ try {
319
+ const element = await this.createElement(it.route, {
320
+ // default props attached to page
321
+ ...(it.route.props ? it.route.props() : {}),
322
+ // resolved props
323
+ ...props,
324
+ // context props (from previous layers)
325
+ ...context,
326
+ });
327
+
328
+ state.layers.push({
329
+ name: it.route.name,
330
+ props,
331
+ part: it.route.path,
332
+ config: it.config,
333
+ element: this.renderView(i + 1, path, element, it.route),
334
+ index: i + 1,
335
+ path,
336
+ route: it.route,
337
+ cache: it.cache,
338
+ });
339
+ } catch (e) {
340
+ it.error = e as Error;
341
+ }
342
+ }
343
+
344
+ // handler has thrown an error, render an error view
345
+ if (it.error) {
346
+ try {
347
+ let element: ReactNode | Redirection | undefined =
348
+ await state.onError(it.error, state);
349
+
350
+ if (element === undefined) {
351
+ throw it.error;
352
+ }
353
+
354
+ if (element instanceof Redirection) {
355
+ return {
356
+ redirect: element.redirect,
357
+ };
358
+ }
359
+
360
+ if (element === null) {
361
+ element = this.renderError(it.error);
362
+ }
363
+
364
+ state.layers.push({
365
+ props,
366
+ error: it.error,
367
+ name: it.route.name,
368
+ part: it.route.path,
369
+ config: it.config,
370
+ element: this.renderView(i + 1, path, element, it.route),
371
+ index: i + 1,
372
+ path,
373
+ route: it.route,
374
+ });
375
+ break;
376
+ } catch (e) {
377
+ if (e instanceof Redirection) {
378
+ return {
379
+ redirect: e.redirect,
380
+ };
381
+ }
382
+ throw e;
383
+ }
384
+ }
385
+ }
386
+
387
+ return { state };
388
+ }
389
+
390
+ protected getErrorHandler(route: PageRoute): ErrorHandler | undefined {
391
+ if (route.errorHandler) return route.errorHandler;
392
+ let parent = route.parent;
393
+ while (parent) {
394
+ if (parent.errorHandler) return parent.errorHandler;
395
+ parent = parent.parent;
396
+ }
397
+ }
398
+
399
+ protected async createElement(
400
+ page: PageRoute,
401
+ props: Record<string, any>,
402
+ ): Promise<ReactNode> {
403
+ if (page.lazy && page.component) {
404
+ this.log.warn(
405
+ `Page ${page.name} has both lazy and component options, lazy will be used`,
406
+ );
407
+ }
408
+
409
+ if (page.lazy) {
410
+ const component = await page.lazy(); // load component
411
+ return createElement(component.default, props);
412
+ }
413
+
414
+ if (page.component) {
415
+ return createElement(page.component, props);
416
+ }
417
+
418
+ return undefined;
419
+ }
420
+
421
+ public renderError(error: Error): ReactNode {
422
+ return createElement(ErrorViewer, { error, alepha: this.alepha });
423
+ }
424
+
425
+ public renderEmptyView(): ReactNode {
426
+ return createElement(NestedView, {});
427
+ }
428
+
429
+ public href(
430
+ page: { options: { name?: string } },
431
+ params: Record<string, any> = {},
432
+ ): string {
433
+ const found = this.pages.find((it) => it.name === page.options.name);
434
+ if (!found) {
435
+ throw new AlephaError(`Page ${page.options.name} not found`);
436
+ }
437
+
438
+ let url = found.path ?? "";
439
+ let parent = found.parent;
440
+ while (parent) {
441
+ url = `${parent.path ?? ""}/${url}`;
442
+ parent = parent.parent;
443
+ }
444
+
445
+ url = this.compile(url, params);
446
+
447
+ return url.replace(/\/\/+/g, "/") || "/";
448
+ }
449
+
450
+ public compile(path: string, params: Record<string, string> = {}) {
451
+ for (const [key, value] of Object.entries(params)) {
452
+ path = path.replace(`:${key}`, value);
453
+ }
454
+ return path;
455
+ }
456
+
457
+ protected renderView(
458
+ index: number,
459
+ path: string,
460
+ view: ReactNode | undefined,
461
+ page: PageRoute,
462
+ ): ReactNode {
463
+ view ??= this.renderEmptyView();
464
+
465
+ const element = page.client
466
+ ? createElement(
467
+ ClientOnly,
468
+ typeof page.client === "object" ? page.client : {},
469
+ view,
470
+ )
471
+ : view;
472
+
473
+ return createElement(
474
+ RouterLayerContext.Provider,
475
+ {
476
+ value: {
477
+ index,
478
+ path,
479
+ onError:
480
+ this.getErrorHandler(page) ?? ((error) => this.renderError(error)),
481
+ },
482
+ },
483
+ element,
484
+ );
485
+ }
486
+
487
+ protected readonly configure = $hook({
488
+ on: "configure",
489
+ handler: () => {
490
+ let hasNotFoundHandler = false;
491
+ const pages = this.alepha.primitives($page);
492
+
493
+ const hasParent = (it: PagePrimitive) => {
494
+ if (it.options.parent) {
495
+ return true;
496
+ }
497
+
498
+ for (const page of pages) {
499
+ const children = page.options.children
500
+ ? Array.isArray(page.options.children)
501
+ ? page.options.children
502
+ : page.options.children()
503
+ : [];
504
+ if (children.includes(it)) {
505
+ return true;
506
+ }
507
+ }
508
+ };
509
+
510
+ for (const page of pages) {
511
+ if (page.options.path === "/*") {
512
+ hasNotFoundHandler = true;
513
+ }
514
+
515
+ // skip children, we only want root pages
516
+ if (hasParent(page)) {
517
+ continue;
518
+ }
519
+
520
+ this.add(this.map(pages, page));
521
+ }
522
+
523
+ if (!hasNotFoundHandler && pages.length > 0) {
524
+ // add a default 404 page if not already defined
525
+ this.add({
526
+ path: "/*",
527
+ name: "notFound",
528
+ cache: true,
529
+ component: NotFoundPage,
530
+ onServerResponse: ({ reply }) => {
531
+ reply.status = 404;
532
+ },
533
+ });
534
+ }
535
+ },
536
+ });
537
+
538
+ protected map(
539
+ pages: Array<PagePrimitive>,
540
+ target: PagePrimitive,
541
+ ): PageRouteEntry {
542
+ const children = target.options.children
543
+ ? Array.isArray(target.options.children)
544
+ ? target.options.children
545
+ : target.options.children()
546
+ : [];
547
+
548
+ const getChildrenFromParent = (it: PagePrimitive): PagePrimitive[] => {
549
+ const children = [];
550
+ for (const page of pages) {
551
+ if (page.options.parent === it) {
552
+ children.push(page);
553
+ }
554
+ }
555
+ return children;
556
+ };
557
+
558
+ children.push(...getChildrenFromParent(target));
559
+
560
+ return {
561
+ ...target.options,
562
+ name: target.name,
563
+ parent: undefined,
564
+ children: children.map((it) => this.map(pages, it)),
565
+ } as PageRoute;
566
+ }
567
+
568
+ public add(entry: PageRouteEntry) {
569
+ if (this.alepha.isReady()) {
570
+ throw new AlephaError("Router is already initialized");
571
+ }
572
+
573
+ entry.name ??= this.nextId();
574
+ const page = entry as PageRoute;
575
+
576
+ page.match = this.createMatch(page);
577
+ this.pages.push(page);
578
+
579
+ if (page.children) {
580
+ for (const child of page.children) {
581
+ (child as PageRoute).parent = page;
582
+ this.add(child);
583
+ }
584
+ }
585
+ }
586
+
587
+ protected createMatch(page: PageRoute): string {
588
+ let url = page.path ?? "/";
589
+ let target = page.parent;
590
+ while (target) {
591
+ url = `${target.path ?? ""}/${url}`;
592
+ target = target.parent;
593
+ }
594
+
595
+ let path = url.replace(/\/\/+/g, "/");
596
+
597
+ if (path.endsWith("/") && path !== "/") {
598
+ // remove trailing slash
599
+ path = path.slice(0, -1);
600
+ }
601
+
602
+ return path;
603
+ }
604
+
605
+ protected _next = 0;
606
+
607
+ protected nextId(): string {
608
+ this._next += 1;
609
+ return `P${this._next}`;
610
+ }
611
+ }
612
+
613
+ export const isPageRoute = (it: any): it is PageRoute => {
614
+ return (
615
+ it &&
616
+ typeof it === "object" &&
617
+ typeof it.path === "string" &&
618
+ typeof it.page === "object"
619
+ );
620
+ };
621
+
622
+ export interface PageRouteEntry
623
+ extends Omit<PagePrimitiveOptions, "children" | "parent"> {
624
+ children?: PageRouteEntry[];
625
+ }
626
+
627
+ export interface ConcretePageRoute extends PageRoute {
628
+ /**
629
+ * When exported, static routes can be split into multiple pages with different params.
630
+ * We replace 'name' by the new name for each static entry, and old 'name' becomes 'staticName'.
631
+ */
632
+ staticName?: string;
633
+
634
+ params?: Record<string, string>;
635
+ }
636
+
637
+ export interface PageRoute extends PageRouteEntry {
638
+ type: "page";
639
+ name: string;
640
+ parent?: PageRoute;
641
+ match: string;
642
+ }
643
+
644
+ export interface Layer {
645
+ config?: {
646
+ query?: Record<string, any>;
647
+ params?: Record<string, any>;
648
+ // stack of resolved props
649
+ context?: Record<string, any>;
650
+ };
651
+
652
+ name: string;
653
+ props?: Record<string, any>;
654
+ error?: Error;
655
+ part?: string;
656
+ element: ReactNode;
657
+ index: number;
658
+ path: string;
659
+ route?: PageRoute;
660
+ cache?: boolean;
661
+ }
662
+
663
+ export type PreviousLayerData = Omit<Layer, "element" | "index" | "path">;
664
+
665
+ export interface AnchorProps {
666
+ href: string;
667
+ onClick: (ev?: any) => any;
668
+ }
669
+
670
+ export interface ReactRouterState {
671
+ /**
672
+ * Stack of layers for the current page.
673
+ */
674
+ layers: Array<Layer>;
675
+
676
+ /**
677
+ * URL of the current page.
678
+ */
679
+ url: URL;
680
+
681
+ /**
682
+ * Error handler for the current page.
683
+ */
684
+ onError: ErrorHandler;
685
+
686
+ /**
687
+ * Params extracted from the URL for the current page.
688
+ */
689
+ params: Record<string, any>;
690
+
691
+ /**
692
+ * Query parameters extracted from the URL for the current page.
693
+ */
694
+ query: Record<string, string>;
695
+
696
+ /**
697
+ * Optional meta information associated with the current page.
698
+ */
699
+ meta: Record<string, any>;
700
+
701
+ /**
702
+ * Head configuration for the current page (title, meta tags, etc.).
703
+ * Populated by HeadProvider during SSR.
704
+ */
705
+ head: Head;
706
+
707
+ //
708
+ name?: string;
709
+ }
710
+
711
+ export interface RouterStackItem {
712
+ route: PageRoute;
713
+ config?: Record<string, any>;
714
+ props?: Record<string, any>;
715
+ error?: Error;
716
+ cache?: boolean;
717
+ }
718
+
719
+ export interface TransitionOptions {
720
+ previous?: PreviousLayerData[];
721
+ }
722
+
723
+ export interface CreateLayersResult {
724
+ redirect?: string;
725
+ state?: ReactRouterState;
726
+ }