alepha 0.15.0 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (551) hide show
  1. package/README.md +43 -98
  2. package/dist/api/audits/index.d.ts +630 -653
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +12 -35
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +365 -358
  7. package/dist/api/files/index.d.ts.map +1 -1
  8. package/dist/api/files/index.js +12 -5
  9. package/dist/api/files/index.js.map +1 -1
  10. package/dist/api/jobs/index.d.ts +255 -248
  11. package/dist/api/jobs/index.d.ts.map +1 -1
  12. package/dist/api/jobs/index.js +10 -3
  13. package/dist/api/jobs/index.js.map +1 -1
  14. package/dist/api/keys/index.d.ts +413 -0
  15. package/dist/api/keys/index.d.ts.map +1 -0
  16. package/dist/api/keys/index.js +476 -0
  17. package/dist/api/keys/index.js.map +1 -0
  18. package/dist/api/notifications/index.browser.js +4 -4
  19. package/dist/api/notifications/index.browser.js.map +1 -1
  20. package/dist/api/notifications/index.d.ts +84 -78
  21. package/dist/api/notifications/index.d.ts.map +1 -1
  22. package/dist/api/notifications/index.js +14 -8
  23. package/dist/api/notifications/index.js.map +1 -1
  24. package/dist/api/parameters/index.d.ts +528 -535
  25. package/dist/api/parameters/index.d.ts.map +1 -1
  26. package/dist/api/parameters/index.js +30 -37
  27. package/dist/api/parameters/index.js.map +1 -1
  28. package/dist/api/users/index.d.ts +1221 -910
  29. package/dist/api/users/index.d.ts.map +1 -1
  30. package/dist/api/users/index.js +2556 -248
  31. package/dist/api/users/index.js.map +1 -1
  32. package/dist/api/verifications/index.d.ts +142 -136
  33. package/dist/api/verifications/index.d.ts.map +1 -1
  34. package/dist/api/verifications/index.js +12 -4
  35. package/dist/api/verifications/index.js.map +1 -1
  36. package/dist/batch/index.d.ts +142 -162
  37. package/dist/batch/index.d.ts.map +1 -1
  38. package/dist/batch/index.js +31 -44
  39. package/dist/batch/index.js.map +1 -1
  40. package/dist/bucket/index.d.ts +595 -171
  41. package/dist/bucket/index.d.ts.map +1 -1
  42. package/dist/bucket/index.js +1856 -12
  43. package/dist/bucket/index.js.map +1 -1
  44. package/dist/cache/core/index.d.ts +225 -53
  45. package/dist/cache/core/index.d.ts.map +1 -1
  46. package/dist/cache/core/index.js +213 -7
  47. package/dist/cache/core/index.js.map +1 -1
  48. package/dist/cache/redis/index.d.ts +1 -0
  49. package/dist/cache/redis/index.d.ts.map +1 -1
  50. package/dist/cache/redis/index.js +6 -2
  51. package/dist/cache/redis/index.js.map +1 -1
  52. package/dist/cli/index.d.ts +834 -226
  53. package/dist/cli/index.d.ts.map +1 -1
  54. package/dist/cli/index.js +2872 -417
  55. package/dist/cli/index.js.map +1 -1
  56. package/dist/command/index.d.ts +458 -310
  57. package/dist/command/index.d.ts.map +1 -1
  58. package/dist/command/index.js +2011 -76
  59. package/dist/command/index.js.map +1 -1
  60. package/dist/core/index.browser.js +309 -97
  61. package/dist/core/index.browser.js.map +1 -1
  62. package/dist/core/index.d.ts +796 -701
  63. package/dist/core/index.d.ts.map +1 -1
  64. package/dist/core/index.js +329 -97
  65. package/dist/core/index.js.map +1 -1
  66. package/dist/core/index.native.js +309 -97
  67. package/dist/core/index.native.js.map +1 -1
  68. package/dist/datetime/index.d.ts +59 -44
  69. package/dist/datetime/index.d.ts.map +1 -1
  70. package/dist/datetime/index.js +15 -0
  71. package/dist/datetime/index.js.map +1 -1
  72. package/dist/email/index.d.ts +314 -19
  73. package/dist/email/index.d.ts.map +1 -1
  74. package/dist/email/index.js +1852 -7
  75. package/dist/email/index.js.map +1 -1
  76. package/dist/fake/index.d.ts +5500 -5418
  77. package/dist/fake/index.d.ts.map +1 -1
  78. package/dist/fake/index.js +113 -42
  79. package/dist/fake/index.js.map +1 -1
  80. package/dist/lock/core/index.d.ts +219 -212
  81. package/dist/lock/core/index.d.ts.map +1 -1
  82. package/dist/lock/core/index.js +11 -4
  83. package/dist/lock/core/index.js.map +1 -1
  84. package/dist/lock/redis/index.d.ts.map +1 -1
  85. package/dist/logger/index.d.ts +41 -90
  86. package/dist/logger/index.d.ts.map +1 -1
  87. package/dist/logger/index.js +15 -68
  88. package/dist/logger/index.js.map +1 -1
  89. package/dist/mcp/index.d.ts +228 -230
  90. package/dist/mcp/index.d.ts.map +1 -1
  91. package/dist/mcp/index.js +32 -31
  92. package/dist/mcp/index.js.map +1 -1
  93. package/dist/orm/index.browser.js +12 -12
  94. package/dist/orm/index.browser.js.map +1 -1
  95. package/dist/orm/index.bun.js +90 -80
  96. package/dist/orm/index.bun.js.map +1 -1
  97. package/dist/orm/index.d.ts +1434 -1459
  98. package/dist/orm/index.d.ts.map +1 -1
  99. package/dist/orm/index.js +112 -130
  100. package/dist/orm/index.js.map +1 -1
  101. package/dist/queue/core/index.d.ts +262 -254
  102. package/dist/queue/core/index.d.ts.map +1 -1
  103. package/dist/queue/core/index.js +14 -6
  104. package/dist/queue/core/index.js.map +1 -1
  105. package/dist/queue/redis/index.d.ts.map +1 -1
  106. package/dist/react/auth/index.browser.js +108 -0
  107. package/dist/react/auth/index.browser.js.map +1 -0
  108. package/dist/react/auth/index.d.ts +100 -0
  109. package/dist/react/auth/index.d.ts.map +1 -0
  110. package/dist/react/auth/index.js +145 -0
  111. package/dist/react/auth/index.js.map +1 -0
  112. package/dist/react/core/index.d.ts +469 -0
  113. package/dist/react/core/index.d.ts.map +1 -0
  114. package/dist/react/core/index.js +464 -0
  115. package/dist/react/core/index.js.map +1 -0
  116. package/dist/react/form/index.d.ts +232 -0
  117. package/dist/react/form/index.d.ts.map +1 -0
  118. package/dist/react/form/index.js +432 -0
  119. package/dist/react/form/index.js.map +1 -0
  120. package/dist/react/head/index.browser.js +423 -0
  121. package/dist/react/head/index.browser.js.map +1 -0
  122. package/dist/react/head/index.d.ts +288 -0
  123. package/dist/react/head/index.d.ts.map +1 -0
  124. package/dist/react/head/index.js +465 -0
  125. package/dist/react/head/index.js.map +1 -0
  126. package/dist/react/i18n/index.d.ts +175 -0
  127. package/dist/react/i18n/index.d.ts.map +1 -0
  128. package/dist/react/i18n/index.js +224 -0
  129. package/dist/react/i18n/index.js.map +1 -0
  130. package/dist/react/router/index.browser.js +1980 -0
  131. package/dist/react/router/index.browser.js.map +1 -0
  132. package/dist/react/router/index.d.ts +2068 -0
  133. package/dist/react/router/index.d.ts.map +1 -0
  134. package/dist/react/router/index.js +4932 -0
  135. package/dist/react/router/index.js.map +1 -0
  136. package/dist/react/websocket/index.d.ts +117 -0
  137. package/dist/react/websocket/index.d.ts.map +1 -0
  138. package/dist/react/websocket/index.js +107 -0
  139. package/dist/react/websocket/index.js.map +1 -0
  140. package/dist/redis/index.bun.js +4 -0
  141. package/dist/redis/index.bun.js.map +1 -1
  142. package/dist/redis/index.d.ts +127 -130
  143. package/dist/redis/index.d.ts.map +1 -1
  144. package/dist/redis/index.js +16 -25
  145. package/dist/redis/index.js.map +1 -1
  146. package/dist/retry/index.d.ts +80 -71
  147. package/dist/retry/index.d.ts.map +1 -1
  148. package/dist/retry/index.js +11 -2
  149. package/dist/retry/index.js.map +1 -1
  150. package/dist/router/index.d.ts +6 -6
  151. package/dist/router/index.d.ts.map +1 -1
  152. package/dist/scheduler/index.d.ts +119 -28
  153. package/dist/scheduler/index.d.ts.map +1 -1
  154. package/dist/scheduler/index.js +404 -3
  155. package/dist/scheduler/index.js.map +1 -1
  156. package/dist/security/index.d.ts +642 -228
  157. package/dist/security/index.d.ts.map +1 -1
  158. package/dist/security/index.js +1579 -37
  159. package/dist/security/index.js.map +1 -1
  160. package/dist/server/auth/index.d.ts +1141 -111
  161. package/dist/server/auth/index.d.ts.map +1 -1
  162. package/dist/server/auth/index.js +1261 -25
  163. package/dist/server/auth/index.js.map +1 -1
  164. package/dist/server/cache/index.d.ts +63 -78
  165. package/dist/server/cache/index.d.ts.map +1 -1
  166. package/dist/server/cache/index.js +7 -22
  167. package/dist/server/cache/index.js.map +1 -1
  168. package/dist/server/compress/index.d.ts +13 -5
  169. package/dist/server/compress/index.d.ts.map +1 -1
  170. package/dist/server/compress/index.js +10 -2
  171. package/dist/server/compress/index.js.map +1 -1
  172. package/dist/server/cookies/index.d.ts +46 -22
  173. package/dist/server/cookies/index.d.ts.map +1 -1
  174. package/dist/server/cookies/index.js +7 -5
  175. package/dist/server/cookies/index.js.map +1 -1
  176. package/dist/server/core/index.d.ts +307 -196
  177. package/dist/server/core/index.d.ts.map +1 -1
  178. package/dist/server/core/index.js +271 -38
  179. package/dist/server/core/index.js.map +1 -1
  180. package/dist/server/cors/index.d.ts +24 -34
  181. package/dist/server/cors/index.d.ts.map +1 -1
  182. package/dist/server/cors/index.js +7 -21
  183. package/dist/server/cors/index.js.map +1 -1
  184. package/dist/server/health/index.d.ts +25 -19
  185. package/dist/server/health/index.d.ts.map +1 -1
  186. package/dist/server/health/index.js +8 -2
  187. package/dist/server/health/index.js.map +1 -1
  188. package/dist/server/helmet/index.d.ts +13 -5
  189. package/dist/server/helmet/index.d.ts.map +1 -1
  190. package/dist/server/helmet/index.js +11 -3
  191. package/dist/server/helmet/index.js.map +1 -1
  192. package/dist/server/links/index.browser.js +9 -1
  193. package/dist/server/links/index.browser.js.map +1 -1
  194. package/dist/server/links/index.d.ts +133 -128
  195. package/dist/server/links/index.d.ts.map +1 -1
  196. package/dist/server/links/index.js +24 -11
  197. package/dist/server/links/index.js.map +1 -1
  198. package/dist/server/metrics/index.d.ts +524 -4
  199. package/dist/server/metrics/index.d.ts.map +1 -1
  200. package/dist/server/metrics/index.js +4472 -7
  201. package/dist/server/metrics/index.js.map +1 -1
  202. package/dist/server/multipart/index.d.ts +15 -9
  203. package/dist/server/multipart/index.d.ts.map +1 -1
  204. package/dist/server/multipart/index.js +9 -3
  205. package/dist/server/multipart/index.js.map +1 -1
  206. package/dist/server/proxy/index.d.ts +110 -104
  207. package/dist/server/proxy/index.d.ts.map +1 -1
  208. package/dist/server/proxy/index.js +8 -2
  209. package/dist/server/proxy/index.js.map +1 -1
  210. package/dist/server/rate-limit/index.d.ts +46 -51
  211. package/dist/server/rate-limit/index.d.ts.map +1 -1
  212. package/dist/server/rate-limit/index.js +18 -55
  213. package/dist/server/rate-limit/index.js.map +1 -1
  214. package/dist/server/static/index.d.ts +181 -48
  215. package/dist/server/static/index.d.ts.map +1 -1
  216. package/dist/server/static/index.js +1848 -5
  217. package/dist/server/static/index.js.map +1 -1
  218. package/dist/server/swagger/index.d.ts +348 -53
  219. package/dist/server/swagger/index.d.ts.map +1 -1
  220. package/dist/server/swagger/index.js +1849 -6
  221. package/dist/server/swagger/index.js.map +1 -1
  222. package/dist/sms/index.d.ts +312 -18
  223. package/dist/sms/index.d.ts.map +1 -1
  224. package/dist/sms/index.js +1854 -10
  225. package/dist/sms/index.js.map +1 -1
  226. package/dist/system/index.browser.js +496 -0
  227. package/dist/system/index.browser.js.map +1 -0
  228. package/dist/system/index.d.ts +1158 -0
  229. package/dist/system/index.d.ts.map +1 -0
  230. package/dist/{file → system}/index.js +412 -20
  231. package/dist/system/index.js.map +1 -0
  232. package/dist/thread/index.d.ts +82 -73
  233. package/dist/thread/index.d.ts.map +1 -1
  234. package/dist/thread/index.js +13 -4
  235. package/dist/thread/index.js.map +1 -1
  236. package/dist/topic/core/index.d.ts +330 -323
  237. package/dist/topic/core/index.d.ts.map +1 -1
  238. package/dist/topic/core/index.js +12 -5
  239. package/dist/topic/core/index.js.map +1 -1
  240. package/dist/topic/redis/index.d.ts +6 -6
  241. package/dist/topic/redis/index.d.ts.map +1 -1
  242. package/dist/vite/index.d.ts +163 -5825
  243. package/dist/vite/index.d.ts.map +1 -1
  244. package/dist/vite/index.js +130 -477
  245. package/dist/vite/index.js.map +1 -1
  246. package/dist/websocket/index.browser.js +3 -3
  247. package/dist/websocket/index.browser.js.map +1 -1
  248. package/dist/websocket/index.d.ts +287 -283
  249. package/dist/websocket/index.d.ts.map +1 -1
  250. package/dist/websocket/index.js +15 -11
  251. package/dist/websocket/index.js.map +1 -1
  252. package/package.json +86 -17
  253. package/src/api/audits/index.ts +10 -33
  254. package/src/api/files/__tests__/$bucket.spec.ts +1 -1
  255. package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
  256. package/src/api/files/controllers/FileController.spec.ts +1 -1
  257. package/src/api/files/index.ts +10 -3
  258. package/src/api/files/jobs/FileJobs.spec.ts +1 -1
  259. package/src/api/files/services/FileService.spec.ts +1 -1
  260. package/src/api/jobs/index.ts +10 -3
  261. package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
  262. package/src/api/keys/controllers/ApiKeyController.ts +103 -0
  263. package/src/api/keys/entities/apiKeyEntity.ts +41 -0
  264. package/src/api/keys/index.ts +49 -0
  265. package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
  266. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
  267. package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
  268. package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
  269. package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
  270. package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
  271. package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
  272. package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
  273. package/src/api/keys/services/ApiKeyService.ts +306 -0
  274. package/src/api/logs/TODO.md +52 -0
  275. package/src/api/notifications/index.ts +10 -4
  276. package/src/api/parameters/index.ts +9 -30
  277. package/src/api/parameters/primitives/$config.ts +12 -4
  278. package/src/api/parameters/services/ConfigStore.ts +9 -3
  279. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
  280. package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
  281. package/src/api/users/index.ts +14 -3
  282. package/src/api/users/primitives/$realm.ts +33 -5
  283. package/src/api/users/providers/RealmProvider.ts +1 -12
  284. package/src/api/users/services/SessionService.ts +1 -11
  285. package/src/api/verifications/controllers/VerificationController.ts +2 -0
  286. package/src/api/verifications/index.ts +10 -4
  287. package/src/batch/index.ts +9 -36
  288. package/src/batch/primitives/$batch.ts +0 -8
  289. package/src/batch/providers/BatchProvider.ts +29 -2
  290. package/src/bucket/__tests__/shared.ts +1 -1
  291. package/src/bucket/index.ts +13 -6
  292. package/src/bucket/primitives/$bucket.ts +1 -1
  293. package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
  294. package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
  295. package/src/cache/core/__tests__/shared.ts +30 -0
  296. package/src/cache/core/index.ts +11 -6
  297. package/src/cache/core/primitives/$cache.spec.ts +5 -0
  298. package/src/cache/core/providers/CacheProvider.ts +17 -0
  299. package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
  300. package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
  301. package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
  302. package/src/cli/apps/AlephaCli.ts +3 -16
  303. package/src/cli/apps/AlephaPackageBuilderCli.ts +10 -2
  304. package/src/cli/atoms/appEntryOptions.ts +13 -0
  305. package/src/cli/atoms/buildOptions.ts +1 -1
  306. package/src/cli/atoms/changelogOptions.ts +1 -1
  307. package/src/cli/commands/build.ts +64 -52
  308. package/src/cli/commands/db.ts +17 -11
  309. package/src/cli/commands/deploy.ts +1 -1
  310. package/src/cli/commands/dev.ts +13 -49
  311. package/src/cli/commands/gen/env.ts +6 -3
  312. package/src/cli/commands/gen/openapi.ts +5 -2
  313. package/src/cli/commands/init.spec.ts +544 -0
  314. package/src/cli/commands/init.ts +101 -58
  315. package/src/cli/commands/lint.ts +8 -2
  316. package/src/cli/commands/typecheck.ts +11 -0
  317. package/src/cli/defineConfig.ts +9 -0
  318. package/src/cli/index.ts +2 -1
  319. package/src/cli/providers/AppEntryProvider.ts +131 -0
  320. package/src/cli/providers/ViteBuildProvider.ts +40 -0
  321. package/src/cli/providers/ViteDevServerProvider.ts +378 -0
  322. package/src/cli/services/AlephaCliUtils.ts +39 -93
  323. package/src/cli/services/PackageManagerUtils.ts +140 -17
  324. package/src/cli/services/ProjectScaffolder.ts +169 -101
  325. package/src/cli/services/ViteUtils.ts +82 -0
  326. package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +41 -28
  327. package/src/cli/{assets → templates}/apiHelloControllerTs.ts +2 -1
  328. package/src/cli/{assets → templates}/biomeJson.ts +2 -1
  329. package/src/cli/{assets → templates}/dummySpecTs.ts +2 -1
  330. package/src/cli/{assets → templates}/editorconfig.ts +2 -1
  331. package/src/cli/templates/gitignore.ts +39 -0
  332. package/src/cli/{assets → templates}/mainBrowserTs.ts +2 -1
  333. package/src/cli/templates/mainCss.ts +33 -0
  334. package/src/cli/templates/mainServerTs.ts +33 -0
  335. package/src/cli/{assets → templates}/tsconfigJson.ts +2 -1
  336. package/src/cli/templates/webAppRouterTs.ts +50 -0
  337. package/src/cli/templates/webHelloComponentTsx.ts +20 -0
  338. package/src/command/helpers/Runner.spec.ts +4 -0
  339. package/src/command/helpers/Runner.ts +3 -21
  340. package/src/command/index.ts +12 -4
  341. package/src/command/providers/CliProvider.spec.ts +1067 -0
  342. package/src/command/providers/CliProvider.ts +203 -40
  343. package/src/core/Alepha.ts +3 -9
  344. package/src/core/__tests__/Alepha-start.spec.ts +4 -4
  345. package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
  346. package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
  347. package/src/core/index.shared.ts +1 -0
  348. package/src/core/index.ts +20 -0
  349. package/src/core/primitives/$module.ts +12 -0
  350. package/src/core/providers/EventManager.spec.ts +0 -71
  351. package/src/core/providers/EventManager.ts +3 -15
  352. package/src/core/providers/Json.ts +2 -14
  353. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +257 -0
  354. package/src/core/providers/KeylessJsonSchemaCodec.ts +396 -14
  355. package/src/core/providers/SchemaValidator.spec.ts +236 -0
  356. package/src/datetime/index.ts +15 -0
  357. package/src/email/index.ts +10 -5
  358. package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
  359. package/src/email/providers/LocalEmailProvider.ts +1 -1
  360. package/src/fake/__tests__/keyName.example.ts +1 -1
  361. package/src/fake/__tests__/keyName.spec.ts +5 -5
  362. package/src/fake/index.ts +9 -6
  363. package/src/fake/providers/FakeProvider.spec.ts +258 -40
  364. package/src/fake/providers/FakeProvider.ts +133 -19
  365. package/src/lock/core/index.ts +11 -4
  366. package/src/logger/index.ts +17 -66
  367. package/src/logger/providers/PrettyFormatterProvider.ts +0 -9
  368. package/src/mcp/errors/McpError.ts +30 -0
  369. package/src/mcp/index.ts +13 -27
  370. package/src/mcp/transports/SseMcpTransport.ts +6 -7
  371. package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
  372. package/src/orm/index.browser.ts +2 -2
  373. package/src/orm/index.bun.ts +4 -2
  374. package/src/orm/index.ts +21 -47
  375. package/src/orm/providers/DrizzleKitProvider.ts +3 -5
  376. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -0
  377. package/src/orm/services/Repository.ts +18 -3
  378. package/src/queue/core/index.ts +14 -6
  379. package/src/react/auth/__tests__/$auth.spec.ts +202 -0
  380. package/src/react/auth/hooks/useAuth.ts +32 -0
  381. package/src/react/auth/index.browser.ts +13 -0
  382. package/src/react/auth/index.shared.ts +2 -0
  383. package/src/react/auth/index.ts +48 -0
  384. package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
  385. package/src/react/auth/services/ReactAuth.ts +135 -0
  386. package/src/react/core/__tests__/Router.spec.tsx +169 -0
  387. package/src/react/core/components/ClientOnly.tsx +49 -0
  388. package/src/react/core/components/ErrorBoundary.tsx +73 -0
  389. package/src/react/core/contexts/AlephaContext.ts +7 -0
  390. package/src/react/core/contexts/AlephaProvider.tsx +42 -0
  391. package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
  392. package/src/react/core/hooks/useAction.ts +480 -0
  393. package/src/react/core/hooks/useAlepha.ts +26 -0
  394. package/src/react/core/hooks/useClient.ts +17 -0
  395. package/src/react/core/hooks/useEvents.ts +51 -0
  396. package/src/react/core/hooks/useInject.ts +12 -0
  397. package/src/react/core/hooks/useStore.ts +52 -0
  398. package/src/react/core/index.ts +90 -0
  399. package/src/react/form/components/FormState.tsx +17 -0
  400. package/src/react/form/errors/FormValidationError.ts +18 -0
  401. package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
  402. package/src/react/form/hooks/useForm.ts +47 -0
  403. package/src/react/form/hooks/useFormState.ts +130 -0
  404. package/src/react/form/index.ts +44 -0
  405. package/src/react/form/services/FormModel.ts +614 -0
  406. package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
  407. package/src/react/head/helpers/SeoExpander.ts +142 -0
  408. package/src/react/head/hooks/useHead.spec.tsx +288 -0
  409. package/src/react/head/hooks/useHead.ts +62 -0
  410. package/src/react/head/index.browser.ts +26 -0
  411. package/src/react/head/index.ts +44 -0
  412. package/src/react/head/interfaces/Head.ts +105 -0
  413. package/src/react/head/primitives/$head.ts +25 -0
  414. package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
  415. package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
  416. package/src/react/head/providers/HeadProvider.ts +168 -0
  417. package/src/react/head/providers/ServerHeadProvider.ts +31 -0
  418. package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
  419. package/src/react/i18n/components/Localize.spec.tsx +357 -0
  420. package/src/react/i18n/components/Localize.tsx +35 -0
  421. package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
  422. package/src/react/i18n/hooks/useI18n.ts +18 -0
  423. package/src/react/i18n/index.ts +41 -0
  424. package/src/react/i18n/primitives/$dictionary.ts +69 -0
  425. package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
  426. package/src/react/i18n/providers/I18nProvider.ts +278 -0
  427. package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
  428. package/src/react/router/__tests__/page-head.spec.ts +48 -0
  429. package/src/react/router/__tests__/seo-head.spec.ts +125 -0
  430. package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
  431. package/src/react/router/components/ErrorViewer.tsx +872 -0
  432. package/src/react/router/components/Link.tsx +23 -0
  433. package/src/react/router/components/NestedView.tsx +223 -0
  434. package/src/react/router/components/NotFound.tsx +30 -0
  435. package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  436. package/src/react/router/contexts/RouterLayerContext.ts +12 -0
  437. package/src/react/router/errors/Redirection.ts +28 -0
  438. package/src/react/router/hooks/useActive.ts +52 -0
  439. package/src/react/router/hooks/useQueryParams.ts +63 -0
  440. package/src/react/router/hooks/useRouter.ts +20 -0
  441. package/src/react/router/hooks/useRouterState.ts +11 -0
  442. package/src/react/router/index.browser.ts +45 -0
  443. package/src/react/router/index.shared.ts +19 -0
  444. package/src/react/router/index.ts +142 -0
  445. package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
  446. package/src/react/router/primitives/$page.spec.tsx +708 -0
  447. package/src/react/router/primitives/$page.ts +497 -0
  448. package/src/react/router/providers/ReactBrowserProvider.ts +309 -0
  449. package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
  450. package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
  451. package/src/react/router/providers/ReactPageProvider.ts +726 -0
  452. package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
  453. package/src/react/router/providers/ReactServerProvider.ts +558 -0
  454. package/src/react/router/providers/ReactServerTemplateProvider.ts +979 -0
  455. package/src/react/router/providers/SSRManifestProvider.ts +334 -0
  456. package/src/react/router/services/ReactPageServerService.ts +48 -0
  457. package/src/react/router/services/ReactPageService.ts +27 -0
  458. package/src/react/router/services/ReactRouter.ts +262 -0
  459. package/src/react/websocket/hooks/useRoom.tsx +242 -0
  460. package/src/react/websocket/index.ts +7 -0
  461. package/src/redis/__tests__/redis.spec.ts +13 -0
  462. package/src/redis/index.ts +9 -25
  463. package/src/redis/providers/BunRedisProvider.ts +9 -0
  464. package/src/redis/providers/NodeRedisProvider.ts +8 -0
  465. package/src/redis/providers/RedisProvider.ts +16 -0
  466. package/src/retry/index.ts +11 -2
  467. package/src/router/index.ts +15 -0
  468. package/src/scheduler/index.ts +11 -2
  469. package/src/security/__tests__/BasicAuth.spec.ts +2 -0
  470. package/src/security/__tests__/ServerSecurityProvider.spec.ts +13 -5
  471. package/src/security/index.ts +15 -10
  472. package/src/security/interfaces/IssuerResolver.ts +27 -0
  473. package/src/security/primitives/$issuer.ts +55 -0
  474. package/src/security/providers/SecurityProvider.ts +179 -0
  475. package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
  476. package/src/security/providers/ServerSecurityProvider.ts +36 -22
  477. package/src/server/auth/index.ts +12 -7
  478. package/src/server/cache/index.ts +7 -22
  479. package/src/server/compress/index.ts +10 -2
  480. package/src/server/cookies/index.ts +7 -5
  481. package/src/server/cookies/primitives/$cookie.ts +33 -11
  482. package/src/server/core/index.ts +17 -7
  483. package/src/server/core/interfaces/ServerRequest.ts +83 -1
  484. package/src/server/core/primitives/$action.spec.ts +1 -1
  485. package/src/server/core/primitives/$action.ts +8 -3
  486. package/src/server/core/providers/BunHttpServerProvider.ts +1 -1
  487. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +125 -0
  488. package/src/server/core/providers/NodeHttpServerProvider.ts +77 -22
  489. package/src/server/core/providers/ServerLoggerProvider.ts +2 -2
  490. package/src/server/core/providers/ServerProvider.ts +9 -12
  491. package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
  492. package/src/server/core/services/ServerRequestParser.ts +306 -13
  493. package/src/server/cors/index.ts +7 -21
  494. package/src/server/cors/primitives/$cors.ts +6 -2
  495. package/src/server/health/index.ts +8 -2
  496. package/src/server/helmet/index.ts +11 -3
  497. package/src/server/links/atoms/apiLinksAtom.ts +7 -0
  498. package/src/server/links/index.browser.ts +2 -0
  499. package/src/server/links/index.ts +13 -6
  500. package/src/server/metrics/index.ts +10 -3
  501. package/src/server/multipart/index.ts +9 -3
  502. package/src/server/proxy/index.ts +8 -2
  503. package/src/server/rate-limit/index.ts +21 -25
  504. package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
  505. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
  506. package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
  507. package/src/server/static/index.ts +8 -2
  508. package/src/server/static/providers/ServerStaticProvider.ts +1 -1
  509. package/src/server/swagger/index.ts +9 -4
  510. package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
  511. package/src/sms/index.ts +9 -5
  512. package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
  513. package/src/sms/providers/LocalSmsProvider.ts +1 -1
  514. package/src/system/index.browser.ts +11 -0
  515. package/src/system/index.ts +62 -0
  516. package/src/{file → system}/providers/FileSystemProvider.ts +16 -0
  517. package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
  518. package/src/system/providers/MemoryShellProvider.ts +164 -0
  519. package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
  520. package/src/{file → system}/providers/NodeFileSystemProvider.ts +36 -0
  521. package/src/system/providers/NodeShellProvider.ts +184 -0
  522. package/src/system/providers/ShellProvider.ts +74 -0
  523. package/src/{file → system}/services/FileDetector.spec.ts +2 -2
  524. package/src/thread/index.ts +11 -2
  525. package/src/topic/core/index.ts +12 -5
  526. package/src/vite/index.ts +3 -2
  527. package/src/vite/tasks/buildClient.ts +2 -8
  528. package/src/vite/tasks/buildServer.ts +84 -21
  529. package/src/vite/tasks/copyAssets.ts +5 -4
  530. package/src/vite/tasks/generateSitemap.ts +64 -23
  531. package/src/vite/tasks/index.ts +0 -2
  532. package/src/vite/tasks/prerenderPages.ts +49 -24
  533. package/src/websocket/index.ts +12 -8
  534. package/dist/file/index.d.ts +0 -839
  535. package/dist/file/index.d.ts.map +0 -1
  536. package/dist/file/index.js.map +0 -1
  537. package/src/cli/assets/indexHtml.ts +0 -15
  538. package/src/cli/assets/mainServerTs.ts +0 -24
  539. package/src/cli/assets/webAppRouterTs.ts +0 -15
  540. package/src/cli/assets/webHelloComponentTsx.ts +0 -16
  541. package/src/cli/commands/format.ts +0 -23
  542. package/src/file/index.ts +0 -43
  543. package/src/vite/helpers/boot.ts +0 -117
  544. package/src/vite/plugins/viteAlephaDev.ts +0 -177
  545. package/src/vite/tasks/devServer.ts +0 -71
  546. package/src/vite/tasks/runAlepha.ts +0 -270
  547. /package/dist/orm/{chunk-DtkW-qnP.js → chunk-DH6iiROE.js} +0 -0
  548. /package/src/cli/{assets → templates}/apiIndexTs.ts +0 -0
  549. /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
  550. /package/src/{file → system}/errors/FileError.ts +0 -0
  551. /package/src/{file → system}/services/FileDetector.ts +0 -0
@@ -0,0 +1,1980 @@
1
+ import { $atom, $env, $hook, $inject, $module, $use, Alepha, AlephaError, KIND, Primitive, createPrimitive, t } from "alepha";
2
+ import { AlephaDateTime, DateTimeProvider } from "alepha/datetime";
3
+ import { AlephaContext, AlephaReact, ClientOnly, ErrorBoundary, useAlepha, useEvents, useInject, useStore } from "alepha/react";
4
+ import { AlephaServer } from "alepha/server";
5
+ import { AlephaServerLinks, LinkProvider } from "alepha/server/links";
6
+ import { $logger } from "alepha/logger";
7
+ import { BrowserHeadProvider } from "alepha/react/head";
8
+ import { RouterProvider } from "alepha/router";
9
+ import { StrictMode, createContext, createElement, memo, use, useEffect, useRef, useState } from "react";
10
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
+ import { createRoot, hydrateRoot } from "react-dom/client";
12
+
13
+ //#region ../../src/react/router/constants/PAGE_PRELOAD_KEY.ts
14
+ /**
15
+ * Symbol key for SSR module preloading path.
16
+ * Using Symbol.for() allows the Vite plugin to inject this at build time.
17
+ * @internal
18
+ */
19
+ const PAGE_PRELOAD_KEY = Symbol.for("alepha.page.preload");
20
+
21
+ //#endregion
22
+ //#region ../../src/react/router/services/ReactPageService.ts
23
+ /**
24
+ * $page methods interface.
25
+ */
26
+ var ReactPageService = class {
27
+ fetch(pathname, options = {}) {
28
+ throw new AlephaError("Fetch is not available for this environment.");
29
+ }
30
+ render(name, options = {}) {
31
+ throw new AlephaError("Render is not available for this environment.");
32
+ }
33
+ };
34
+
35
+ //#endregion
36
+ //#region ../../src/react/router/primitives/$page.ts
37
+ /**
38
+ * Main primitive for defining a React route in the application.
39
+ *
40
+ * The $page primitive is the core building block for creating type-safe, SSR-enabled React routes.
41
+ * It provides a declarative way to define pages with powerful features:
42
+ *
43
+ * **Routing & Navigation**
44
+ * - URL pattern matching with parameters (e.g., `/users/:id`)
45
+ * - Nested routing with parent-child relationships
46
+ * - Type-safe URL parameter and query string validation
47
+ *
48
+ * **Data Loading**
49
+ * - Server-side data fetching with the `loader` function
50
+ * - Automatic serialization and hydration for SSR
51
+ * - Access to request context, URL params, and parent data
52
+ *
53
+ * **Component Loading**
54
+ * - Direct component rendering or lazy loading for code splitting
55
+ * - Client-only rendering when browser APIs are needed
56
+ * - Automatic fallback handling during hydration
57
+ *
58
+ * **Performance Optimization**
59
+ * - Static generation for pre-rendered pages at build time
60
+ * - Server-side caching with configurable TTL and providers
61
+ * - Code splitting through lazy component loading
62
+ *
63
+ * **Error Handling**
64
+ * - Custom error handlers with support for redirects
65
+ * - Hierarchical error handling (child → parent)
66
+ * - HTTP status code handling (404, 401, etc.)
67
+ *
68
+ * **Page Animations**
69
+ * - CSS-based enter/exit animations
70
+ * - Dynamic animations based on page state
71
+ * - Custom timing and easing functions
72
+ *
73
+ * **Lifecycle Management**
74
+ * - Server response hooks for headers and status codes
75
+ * - Page leave handlers for cleanup (browser only)
76
+ * - Permission-based access control
77
+ *
78
+ * @example Simple page with data fetching
79
+ * ```typescript
80
+ * const userProfile = $page({
81
+ * path: "/users/:id",
82
+ * schema: {
83
+ * params: t.object({ id: t.integer() }),
84
+ * query: t.object({ tab: t.optional(t.text()) })
85
+ * },
86
+ * loader: async ({ params }) => {
87
+ * const user = await userApi.getUser(params.id);
88
+ * return { user };
89
+ * },
90
+ * lazy: () => import("./UserProfile.tsx")
91
+ * });
92
+ * ```
93
+ *
94
+ * @example Nested routing with error handling
95
+ * ```typescript
96
+ * const projectSection = $page({
97
+ * path: "/projects/:id",
98
+ * children: () => [projectBoard, projectSettings],
99
+ * loader: async ({ params }) => {
100
+ * const project = await projectApi.get(params.id);
101
+ * return { project };
102
+ * },
103
+ * errorHandler: (error) => {
104
+ * if (HttpError.is(error, 404)) {
105
+ * return <ProjectNotFound />;
106
+ * }
107
+ * }
108
+ * });
109
+ * ```
110
+ *
111
+ * @example Static generation with caching
112
+ * ```typescript
113
+ * const blogPost = $page({
114
+ * path: "/blog/:slug",
115
+ * static: {
116
+ * entries: posts.map(p => ({ params: { slug: p.slug } }))
117
+ * },
118
+ * loader: async ({ params }) => {
119
+ * const post = await loadPost(params.slug);
120
+ * return { post };
121
+ * }
122
+ * });
123
+ * ```
124
+ */
125
+ const $page = (options) => {
126
+ return createPrimitive(PagePrimitive, options);
127
+ };
128
+ var PagePrimitive = class extends Primitive {
129
+ reactPageService = $inject(ReactPageService);
130
+ onInit() {
131
+ if (this.options.static) this.options.cache ??= { store: {
132
+ provider: "memory",
133
+ ttl: [1, "week"]
134
+ } };
135
+ }
136
+ get name() {
137
+ return this.options.name ?? this.config.propertyKey;
138
+ }
139
+ /**
140
+ * For testing or build purposes.
141
+ *
142
+ * This will render the page (HTML layout included or not) and return the HTML + context.
143
+ * Only valid for server-side rendering, it will throw an error if called on the client-side.
144
+ */
145
+ async render(options) {
146
+ return this.reactPageService.render(this.name, options);
147
+ }
148
+ async fetch(options) {
149
+ return this.reactPageService.fetch(this.options.path || "", options);
150
+ }
151
+ match(url) {
152
+ return false;
153
+ }
154
+ pathname(config) {
155
+ return this.options.path || "";
156
+ }
157
+ };
158
+ $page[KIND] = PagePrimitive;
159
+
160
+ //#endregion
161
+ //#region ../../src/react/router/components/NotFound.tsx
162
+ /**
163
+ * Default 404 Not Found page component.
164
+ */
165
+ const NotFound = (props) => /* @__PURE__ */ jsxs("div", {
166
+ style: {
167
+ width: "100%",
168
+ minHeight: "90vh",
169
+ boxSizing: "border-box",
170
+ display: "flex",
171
+ flexDirection: "column",
172
+ justifyContent: "center",
173
+ alignItems: "center",
174
+ textAlign: "center",
175
+ fontFamily: "system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif",
176
+ padding: "2rem",
177
+ ...props.style
178
+ },
179
+ children: [/* @__PURE__ */ jsx("div", {
180
+ style: {
181
+ fontSize: "6rem",
182
+ fontWeight: 200,
183
+ lineHeight: 1
184
+ },
185
+ children: "404"
186
+ }), /* @__PURE__ */ jsx("div", {
187
+ style: {
188
+ fontSize: "0.875rem",
189
+ marginTop: "1rem",
190
+ opacity: .6
191
+ },
192
+ children: "Page not found"
193
+ })]
194
+ });
195
+ var NotFound_default = NotFound;
196
+
197
+ //#endregion
198
+ //#region ../../src/react/router/components/ErrorViewer.tsx
199
+ const isBrowser = typeof window !== "undefined";
200
+ /**
201
+ * Error viewer component - Terminal/brutalist aesthetic
202
+ */
203
+ const ErrorViewer = ({ error, alepha }) => {
204
+ const [expanded, setExpanded] = useState(false);
205
+ const [showNodeModules, setShowNodeModules] = useState(false);
206
+ const [visible, setVisible] = useState(false);
207
+ const containerRef = useRef(null);
208
+ const isProduction = alepha.isProduction();
209
+ useEffect(() => {
210
+ const timer = setTimeout(() => setVisible(true), 10);
211
+ return () => clearTimeout(timer);
212
+ }, []);
213
+ useEffect(() => {
214
+ if (!isBrowser) return;
215
+ const handler = (e) => {
216
+ if (e.key === "c" && !e.metaKey && !e.ctrlKey) copyToClipboard(error.stack || error.message);
217
+ };
218
+ window.addEventListener("keydown", handler);
219
+ return () => window.removeEventListener("keydown", handler);
220
+ }, [error]);
221
+ if (isProduction) return /* @__PURE__ */ jsx(ErrorViewerProduction, {});
222
+ const frames = parseStackTrace(error.stack);
223
+ const appFrames = frames.filter((f) => !f.isNodeModules);
224
+ const nodeModulesFrames = frames.filter((f) => f.isNodeModules);
225
+ const visibleAppFrames = expanded ? appFrames : appFrames.slice(0, 5);
226
+ const hiddenAppCount = appFrames.length - 5;
227
+ const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", {
228
+ hour12: false,
229
+ hour: "2-digit",
230
+ minute: "2-digit",
231
+ second: "2-digit"
232
+ });
233
+ return /* @__PURE__ */ jsxs("div", {
234
+ ref: containerRef,
235
+ style: {
236
+ ...styles.overlay,
237
+ opacity: visible ? 1 : 0
238
+ },
239
+ role: "alertdialog",
240
+ "aria-modal": "true",
241
+ "aria-labelledby": "error-viewer-title",
242
+ children: [/* @__PURE__ */ jsx("div", {
243
+ style: styles.scanlines,
244
+ "aria-hidden": "true"
245
+ }), /* @__PURE__ */ jsxs("div", {
246
+ style: {
247
+ ...styles.container,
248
+ transform: visible ? "translateY(0)" : "translateY(-20px)",
249
+ opacity: visible ? 1 : 0
250
+ },
251
+ children: [
252
+ /* @__PURE__ */ jsxs("div", {
253
+ style: styles.terminalBar,
254
+ children: [
255
+ /* @__PURE__ */ jsxs("div", {
256
+ style: styles.terminalDots,
257
+ children: [
258
+ /* @__PURE__ */ jsx("span", { style: {
259
+ ...styles.dot,
260
+ backgroundColor: "#ff5f57"
261
+ } }),
262
+ /* @__PURE__ */ jsx("span", { style: {
263
+ ...styles.dot,
264
+ backgroundColor: "#febc2e"
265
+ } }),
266
+ /* @__PURE__ */ jsx("span", { style: {
267
+ ...styles.dot,
268
+ backgroundColor: "#28c840"
269
+ } })
270
+ ]
271
+ }),
272
+ /* @__PURE__ */ jsx("div", {
273
+ style: styles.terminalTitle,
274
+ children: /* @__PURE__ */ jsxs("span", {
275
+ style: styles.terminalTitleText,
276
+ children: ["error — ", timestamp]
277
+ })
278
+ }),
279
+ /* @__PURE__ */ jsxs("div", {
280
+ style: styles.terminalActions,
281
+ children: [/* @__PURE__ */ jsx("kbd", {
282
+ style: styles.kbd,
283
+ children: "C"
284
+ }), /* @__PURE__ */ jsx("span", {
285
+ style: styles.kbdLabel,
286
+ children: "copy"
287
+ })]
288
+ })
289
+ ]
290
+ }),
291
+ /* @__PURE__ */ jsx(Header, { error }),
292
+ /* @__PURE__ */ jsxs("div", {
293
+ style: styles.stackSection,
294
+ children: [/* @__PURE__ */ jsxs("div", {
295
+ style: styles.stackHeader,
296
+ children: [/* @__PURE__ */ jsx("span", {
297
+ style: styles.stackHeaderText,
298
+ children: "STACK TRACE"
299
+ }), /* @__PURE__ */ jsxs("span", {
300
+ style: styles.stackCount,
301
+ children: [
302
+ appFrames.length,
303
+ " frames",
304
+ nodeModulesFrames.length > 0 && ` · ${nodeModulesFrames.length} in node_modules`
305
+ ]
306
+ })]
307
+ }), /* @__PURE__ */ jsxs("div", {
308
+ style: styles.frameList,
309
+ children: [
310
+ visibleAppFrames.map((frame, i) => /* @__PURE__ */ jsx(StackFrameRow, {
311
+ frame,
312
+ index: i
313
+ }, `${frame.raw}-${i}`)),
314
+ hiddenAppCount > 0 && !expanded && /* @__PURE__ */ jsx(ExpandButton, {
315
+ onClick: () => setExpanded(true),
316
+ label: `Show ${hiddenAppCount} more frames`
317
+ }),
318
+ expanded && hiddenAppCount > 0 && /* @__PURE__ */ jsx(ExpandButton, {
319
+ onClick: () => setExpanded(false),
320
+ label: "Collapse"
321
+ }),
322
+ nodeModulesFrames.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("button", {
323
+ type: "button",
324
+ onClick: () => setShowNodeModules(!showNodeModules),
325
+ style: styles.nodeModulesToggle,
326
+ children: [
327
+ /* @__PURE__ */ jsx("span", {
328
+ style: styles.nodeModulesIcon,
329
+ children: showNodeModules ? "▼" : "▶"
330
+ }),
331
+ /* @__PURE__ */ jsx("span", {
332
+ style: styles.nodeModulesLabel,
333
+ children: "node_modules"
334
+ }),
335
+ /* @__PURE__ */ jsx("span", {
336
+ style: styles.nodeModulesCount,
337
+ children: nodeModulesFrames.length
338
+ })
339
+ ]
340
+ }), showNodeModules && /* @__PURE__ */ jsx("div", {
341
+ style: styles.nodeModulesFrames,
342
+ children: nodeModulesFrames.map((frame, i) => /* @__PURE__ */ jsx(StackFrameRow, {
343
+ frame,
344
+ index: appFrames.length + i,
345
+ dimmed: true
346
+ }, `nm-${frame.raw}-${i}`))
347
+ })] })
348
+ ]
349
+ })]
350
+ }),
351
+ /* @__PURE__ */ jsx("div", {
352
+ style: styles.footer,
353
+ children: /* @__PURE__ */ jsxs("span", {
354
+ style: styles.footerText,
355
+ children: [
356
+ "Press ",
357
+ /* @__PURE__ */ jsx("kbd", {
358
+ style: styles.kbdInline,
359
+ children: "C"
360
+ }),
361
+ " to copy stack trace"
362
+ ]
363
+ })
364
+ })
365
+ ]
366
+ })]
367
+ });
368
+ };
369
+ var ErrorViewer_default = ErrorViewer;
370
+ function parseStackTrace(stack) {
371
+ if (!stack) return [];
372
+ const lines = stack.split("\n").slice(1);
373
+ const frames = [];
374
+ for (const line of lines) {
375
+ const trimmed = line.trim();
376
+ if (!trimmed.startsWith("at ")) continue;
377
+ const frame = parseStackLine(trimmed);
378
+ if (frame) frames.push(frame);
379
+ }
380
+ return frames;
381
+ }
382
+ function parseStackLine(line) {
383
+ const isNodeModules = line.includes("node_modules") || line.includes("node:");
384
+ const withFn = line.match(/^at\s+(.+?)\s+\((.+):(\d+):(\d+)\)$/);
385
+ if (withFn) return {
386
+ fn: withFn[1],
387
+ file: withFn[2],
388
+ line: withFn[3],
389
+ col: withFn[4],
390
+ raw: line,
391
+ isNodeModules
392
+ };
393
+ const withoutFn = line.match(/^at\s+(.+):(\d+):(\d+)$/);
394
+ if (withoutFn) return {
395
+ fn: "<anonymous>",
396
+ file: withoutFn[1],
397
+ line: withoutFn[2],
398
+ col: withoutFn[3],
399
+ raw: line,
400
+ isNodeModules
401
+ };
402
+ return {
403
+ fn: "",
404
+ file: line.replace(/^at\s+/, ""),
405
+ line: "",
406
+ col: "",
407
+ raw: line,
408
+ isNodeModules
409
+ };
410
+ }
411
+ function copyToClipboard(text) {
412
+ if (!isBrowser || !navigator.clipboard) return Promise.resolve(false);
413
+ return navigator.clipboard.writeText(text).then(() => true).catch(() => false);
414
+ }
415
+ /**
416
+ * Header with error badge and message
417
+ */
418
+ function Header({ error }) {
419
+ const [copied, setCopied] = useState(false);
420
+ const [hovered, setHovered] = useState(false);
421
+ useEffect(() => {
422
+ if (!copied) return;
423
+ const timer = setTimeout(() => setCopied(false), 2e3);
424
+ return () => clearTimeout(timer);
425
+ }, [copied]);
426
+ const handleCopy = async () => {
427
+ if (await copyToClipboard(error.stack || error.message)) setCopied(true);
428
+ };
429
+ return /* @__PURE__ */ jsxs("div", {
430
+ style: styles.header,
431
+ children: [/* @__PURE__ */ jsxs("div", {
432
+ style: styles.headerRow,
433
+ children: [/* @__PURE__ */ jsxs("div", {
434
+ style: styles.errorIndicator,
435
+ children: [/* @__PURE__ */ jsx("div", { style: styles.errorGlow }), /* @__PURE__ */ jsx("div", {
436
+ style: styles.errorBadge,
437
+ children: error.name
438
+ })]
439
+ }), /* @__PURE__ */ jsx("button", {
440
+ type: "button",
441
+ onClick: handleCopy,
442
+ onMouseEnter: () => setHovered(true),
443
+ onMouseLeave: () => setHovered(false),
444
+ style: {
445
+ ...styles.copyBtn,
446
+ ...hovered ? styles.copyBtnHover : {}
447
+ },
448
+ children: copied ? "✓ Copied" : "Copy"
449
+ })]
450
+ }), /* @__PURE__ */ jsx("h1", {
451
+ id: "error-viewer-title",
452
+ style: styles.message,
453
+ children: error.message
454
+ })]
455
+ });
456
+ }
457
+ /**
458
+ * Single stack frame row
459
+ */
460
+ function StackFrameRow({ frame, index, dimmed = false }) {
461
+ const [hovered, setHovered] = useState(false);
462
+ const isFirst = index === 0 && !dimmed;
463
+ const fileName = frame.file.split("/").pop() || frame.file;
464
+ const dirPath = frame.file.substring(0, frame.file.length - fileName.length);
465
+ const vsCodeLink = frame.file && frame.line ? `vscode://file${frame.file}:${frame.line}:${frame.col || 1}` : null;
466
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
467
+ style: {
468
+ ...styles.frameIndex,
469
+ color: isFirst ? "#ff6b6b" : dimmed ? "#555" : "#666"
470
+ },
471
+ children: String(index + 1).padStart(2, "0")
472
+ }), /* @__PURE__ */ jsxs("div", {
473
+ style: styles.frameContent,
474
+ children: [frame.fn && /* @__PURE__ */ jsx("div", {
475
+ style: {
476
+ ...styles.fnName,
477
+ color: dimmed ? "#888" : "#f0f0f0"
478
+ },
479
+ children: formatFunctionName(frame.fn)
480
+ }), /* @__PURE__ */ jsxs("div", {
481
+ style: styles.filePath,
482
+ children: [
483
+ /* @__PURE__ */ jsx("span", {
484
+ style: {
485
+ ...styles.dirPath,
486
+ opacity: dimmed ? .6 : .8
487
+ },
488
+ children: dirPath
489
+ }),
490
+ /* @__PURE__ */ jsx("span", {
491
+ style: {
492
+ ...styles.fileName,
493
+ color: dimmed ? "#5a9aba" : "#7cc4eb"
494
+ },
495
+ children: fileName
496
+ }),
497
+ frame.line && /* @__PURE__ */ jsxs("span", {
498
+ style: {
499
+ ...styles.lineCol,
500
+ color: dimmed ? "#9a8a40" : "#e5b83a"
501
+ },
502
+ children: [
503
+ ":",
504
+ frame.line,
505
+ frame.col && `:${frame.col}`
506
+ ]
507
+ })
508
+ ]
509
+ })]
510
+ })] });
511
+ const rowStyles = {
512
+ ...styles.frame,
513
+ ...isFirst ? styles.frameFirst : {},
514
+ backgroundColor: hovered ? "rgba(255,255,255,0.03)" : "transparent"
515
+ };
516
+ if (vsCodeLink && isBrowser) return /* @__PURE__ */ jsx("a", {
517
+ href: vsCodeLink,
518
+ style: {
519
+ ...rowStyles,
520
+ textDecoration: "none"
521
+ },
522
+ onMouseEnter: () => setHovered(true),
523
+ onMouseLeave: () => setHovered(false),
524
+ children: content
525
+ });
526
+ return /* @__PURE__ */ jsx("div", {
527
+ style: rowStyles,
528
+ children: content
529
+ });
530
+ }
531
+ /**
532
+ * Format function name with syntax highlighting
533
+ */
534
+ function formatFunctionName(fn) {
535
+ const asyncMatch = fn.match(/^(async\s+)?(.+)$/);
536
+ if (asyncMatch?.[1]) return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
537
+ style: { color: "#c678dd" },
538
+ children: "async "
539
+ }), /* @__PURE__ */ jsx("span", { children: asyncMatch[2] })] });
540
+ const methodMatch = fn.match(/^(.+)\.([^.]+)$/);
541
+ if (methodMatch) return /* @__PURE__ */ jsxs(Fragment, { children: [
542
+ /* @__PURE__ */ jsx("span", {
543
+ style: { color: "#e5c07b" },
544
+ children: methodMatch[1]
545
+ }),
546
+ /* @__PURE__ */ jsx("span", {
547
+ style: { color: "#666" },
548
+ children: "."
549
+ }),
550
+ /* @__PURE__ */ jsx("span", { children: methodMatch[2] })
551
+ ] });
552
+ return fn;
553
+ }
554
+ /**
555
+ * Expand/collapse button
556
+ */
557
+ function ExpandButton({ onClick, label }) {
558
+ const [hovered, setHovered] = useState(false);
559
+ return /* @__PURE__ */ jsx("button", {
560
+ type: "button",
561
+ onClick,
562
+ onMouseEnter: () => setHovered(true),
563
+ onMouseLeave: () => setHovered(false),
564
+ style: {
565
+ ...styles.expandBtn,
566
+ backgroundColor: hovered ? "rgba(255,255,255,0.05)" : "transparent",
567
+ color: hovered ? "#aaa" : "#777"
568
+ },
569
+ children: label
570
+ });
571
+ }
572
+ /**
573
+ * Production error view - minimal, user-friendly
574
+ */
575
+ function ErrorViewerProduction() {
576
+ const [hovered, setHovered] = useState(false);
577
+ const handleReload = () => {
578
+ if (isBrowser) window.location.reload();
579
+ };
580
+ return /* @__PURE__ */ jsx("div", {
581
+ style: styles.overlay,
582
+ role: "alertdialog",
583
+ "aria-modal": "true",
584
+ children: /* @__PURE__ */ jsxs("div", {
585
+ style: styles.prodContainer,
586
+ children: [
587
+ /* @__PURE__ */ jsx("div", {
588
+ style: styles.prodIcon,
589
+ children: /* @__PURE__ */ jsxs("svg", {
590
+ width: "32",
591
+ height: "32",
592
+ viewBox: "0 0 24 24",
593
+ fill: "none",
594
+ stroke: "currentColor",
595
+ strokeWidth: "2",
596
+ children: [
597
+ /* @__PURE__ */ jsx("circle", {
598
+ cx: "12",
599
+ cy: "12",
600
+ r: "10"
601
+ }),
602
+ /* @__PURE__ */ jsx("line", {
603
+ x1: "12",
604
+ y1: "8",
605
+ x2: "12",
606
+ y2: "12"
607
+ }),
608
+ /* @__PURE__ */ jsx("line", {
609
+ x1: "12",
610
+ y1: "16",
611
+ x2: "12.01",
612
+ y2: "16"
613
+ })
614
+ ]
615
+ })
616
+ }),
617
+ /* @__PURE__ */ jsx("h1", {
618
+ style: styles.prodTitle,
619
+ children: "Something went wrong"
620
+ }),
621
+ /* @__PURE__ */ jsx("p", {
622
+ style: styles.prodMessage,
623
+ children: "We encountered an unexpected error. Please try again."
624
+ }),
625
+ /* @__PURE__ */ jsx("button", {
626
+ type: "button",
627
+ onClick: handleReload,
628
+ onMouseEnter: () => setHovered(true),
629
+ onMouseLeave: () => setHovered(false),
630
+ style: {
631
+ ...styles.prodButton,
632
+ backgroundColor: hovered ? "#333" : "#222",
633
+ borderColor: hovered ? "#555" : "#444"
634
+ },
635
+ children: "Reload page"
636
+ })
637
+ ]
638
+ })
639
+ });
640
+ }
641
+ const MONO_FONT = "ui-monospace, \"JetBrains Mono\", \"Fira Code\", SFMono-Regular, Menlo, Monaco, Consolas, monospace";
642
+ const styles = {
643
+ overlay: {
644
+ position: "fixed",
645
+ inset: 0,
646
+ backgroundColor: "rgba(0, 0, 0, 0.92)",
647
+ display: "flex",
648
+ alignItems: "flex-start",
649
+ justifyContent: "center",
650
+ padding: "40px 20px",
651
+ overflow: "auto",
652
+ fontFamily: MONO_FONT,
653
+ fontSize: "13px",
654
+ zIndex: 99999,
655
+ transition: "opacity 0.2s ease-out"
656
+ },
657
+ scanlines: {
658
+ position: "fixed",
659
+ inset: 0,
660
+ background: "repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,0,0,0.1) 2px, rgba(0,0,0,0.1) 4px)",
661
+ pointerEvents: "none",
662
+ zIndex: 1e5
663
+ },
664
+ container: {
665
+ width: "100%",
666
+ maxWidth: "900px",
667
+ backgroundColor: "#0d0d0d",
668
+ borderRadius: "8px",
669
+ overflow: "hidden",
670
+ boxShadow: "0 0 0 1px #333, 0 25px 80px -12px rgba(0, 0, 0, 0.8)",
671
+ transition: "transform 0.3s ease-out, opacity 0.3s ease-out"
672
+ },
673
+ terminalBar: {
674
+ display: "flex",
675
+ alignItems: "center",
676
+ padding: "12px 16px",
677
+ backgroundColor: "#1a1a1a",
678
+ borderBottom: "1px solid #333"
679
+ },
680
+ terminalDots: {
681
+ display: "flex",
682
+ gap: "8px"
683
+ },
684
+ dot: {
685
+ width: "12px",
686
+ height: "12px",
687
+ borderRadius: "50%"
688
+ },
689
+ terminalTitle: {
690
+ flex: 1,
691
+ textAlign: "center"
692
+ },
693
+ terminalTitleText: {
694
+ color: "#777",
695
+ fontSize: "12px",
696
+ letterSpacing: "0.5px"
697
+ },
698
+ terminalActions: {
699
+ display: "flex",
700
+ alignItems: "center",
701
+ gap: "6px"
702
+ },
703
+ kbd: {
704
+ display: "inline-block",
705
+ padding: "2px 6px",
706
+ backgroundColor: "#2a2a2a",
707
+ borderRadius: "4px",
708
+ fontSize: "11px",
709
+ color: "#aaa",
710
+ border: "1px solid #444"
711
+ },
712
+ kbdInline: {
713
+ display: "inline-block",
714
+ padding: "1px 5px",
715
+ backgroundColor: "#222",
716
+ borderRadius: "3px",
717
+ fontSize: "11px",
718
+ color: "#888",
719
+ border: "1px solid #444",
720
+ marginLeft: "4px",
721
+ marginRight: "4px"
722
+ },
723
+ kbdLabel: {
724
+ color: "#777",
725
+ fontSize: "11px"
726
+ },
727
+ header: {
728
+ padding: "24px",
729
+ borderBottom: "1px solid #333"
730
+ },
731
+ headerRow: {
732
+ display: "flex",
733
+ alignItems: "center",
734
+ justifyContent: "space-between",
735
+ marginBottom: "16px"
736
+ },
737
+ errorIndicator: {
738
+ position: "relative",
739
+ display: "inline-flex"
740
+ },
741
+ errorGlow: {
742
+ position: "absolute",
743
+ inset: "-4px",
744
+ background: "radial-gradient(ellipse at center, rgba(255,80,80,0.3) 0%, transparent 70%)",
745
+ borderRadius: "12px",
746
+ filter: "blur(8px)"
747
+ },
748
+ errorBadge: {
749
+ position: "relative",
750
+ display: "inline-block",
751
+ padding: "6px 14px",
752
+ backgroundColor: "#3d1a1a",
753
+ color: "#ff7b7b",
754
+ fontSize: "12px",
755
+ fontWeight: 600,
756
+ borderRadius: "6px",
757
+ border: "1px solid #5a2828",
758
+ letterSpacing: "0.5px"
759
+ },
760
+ copyBtn: {
761
+ padding: "8px 14px",
762
+ backgroundColor: "transparent",
763
+ color: "#888",
764
+ fontSize: "12px",
765
+ fontWeight: 500,
766
+ borderWidth: "1px",
767
+ borderStyle: "solid",
768
+ borderColor: "#444",
769
+ borderRadius: "6px",
770
+ cursor: "pointer",
771
+ transition: "all 0.15s",
772
+ fontFamily: MONO_FONT
773
+ },
774
+ copyBtnHover: {
775
+ backgroundColor: "#252525",
776
+ color: "#bbb",
777
+ borderColor: "#555"
778
+ },
779
+ message: {
780
+ margin: 0,
781
+ fontSize: "18px",
782
+ fontWeight: 400,
783
+ color: "#e8e8e8",
784
+ lineHeight: 1.6,
785
+ wordBreak: "break-word",
786
+ fontFamily: MONO_FONT
787
+ },
788
+ stackSection: { borderTop: "1px solid #2a2a2a" },
789
+ stackHeader: {
790
+ display: "flex",
791
+ alignItems: "center",
792
+ justifyContent: "space-between",
793
+ padding: "14px 24px",
794
+ borderBottom: "1px solid #2a2a2a"
795
+ },
796
+ stackHeaderText: {
797
+ fontSize: "10px",
798
+ fontWeight: 600,
799
+ color: "#666",
800
+ letterSpacing: "1.5px"
801
+ },
802
+ stackCount: {
803
+ fontSize: "11px",
804
+ color: "#555"
805
+ },
806
+ frameList: {
807
+ display: "flex",
808
+ flexDirection: "column"
809
+ },
810
+ frame: {
811
+ display: "flex",
812
+ alignItems: "flex-start",
813
+ padding: "12px 24px",
814
+ borderBottom: "1px solid #222",
815
+ transition: "background-color 0.1s",
816
+ cursor: "pointer"
817
+ },
818
+ frameFirst: {
819
+ backgroundColor: "rgba(255, 80, 80, 0.08)",
820
+ borderLeft: "2px solid #ff6b6b"
821
+ },
822
+ frameIndex: {
823
+ width: "28px",
824
+ flexShrink: 0,
825
+ fontSize: "11px",
826
+ fontWeight: 500,
827
+ fontFamily: MONO_FONT
828
+ },
829
+ frameContent: {
830
+ flex: 1,
831
+ minWidth: 0
832
+ },
833
+ fnName: {
834
+ fontSize: "13px",
835
+ fontWeight: 500,
836
+ marginBottom: "4px",
837
+ fontFamily: MONO_FONT
838
+ },
839
+ filePath: {
840
+ fontSize: "12px",
841
+ color: "#888",
842
+ fontFamily: MONO_FONT,
843
+ wordBreak: "break-all"
844
+ },
845
+ dirPath: { color: "#666" },
846
+ fileName: { color: "#7cc4eb" },
847
+ lineCol: { color: "#e5b83a" },
848
+ expandBtn: {
849
+ width: "100%",
850
+ padding: "14px 24px",
851
+ backgroundColor: "transparent",
852
+ color: "#777",
853
+ fontSize: "12px",
854
+ fontWeight: 500,
855
+ border: "none",
856
+ borderTop: "1px solid #2a2a2a",
857
+ cursor: "pointer",
858
+ textAlign: "left",
859
+ transition: "all 0.15s",
860
+ fontFamily: MONO_FONT
861
+ },
862
+ nodeModulesToggle: {
863
+ display: "flex",
864
+ alignItems: "center",
865
+ gap: "10px",
866
+ width: "100%",
867
+ padding: "12px 24px",
868
+ backgroundColor: "#0a0a0a",
869
+ color: "#666",
870
+ fontSize: "11px",
871
+ fontWeight: 500,
872
+ border: "none",
873
+ borderTop: "1px solid #2a2a2a",
874
+ cursor: "pointer",
875
+ textAlign: "left",
876
+ fontFamily: MONO_FONT
877
+ },
878
+ nodeModulesIcon: {
879
+ fontSize: "8px",
880
+ color: "#555"
881
+ },
882
+ nodeModulesLabel: {
883
+ flex: 1,
884
+ letterSpacing: "0.5px"
885
+ },
886
+ nodeModulesCount: { color: "#555" },
887
+ nodeModulesFrames: { backgroundColor: "#080808" },
888
+ footer: {
889
+ padding: "14px 24px",
890
+ borderTop: "1px solid #2a2a2a",
891
+ backgroundColor: "#0a0a0a"
892
+ },
893
+ footerText: {
894
+ fontSize: "11px",
895
+ color: "#555"
896
+ },
897
+ prodContainer: {
898
+ textAlign: "center",
899
+ padding: "60px 40px",
900
+ backgroundColor: "#0d0d0d",
901
+ borderRadius: "8px",
902
+ maxWidth: "400px",
903
+ border: "1px solid #333"
904
+ },
905
+ prodIcon: {
906
+ width: "64px",
907
+ height: "64px",
908
+ margin: "0 auto 24px",
909
+ color: "#666",
910
+ display: "flex",
911
+ alignItems: "center",
912
+ justifyContent: "center"
913
+ },
914
+ prodTitle: {
915
+ margin: "0 0 12px",
916
+ fontSize: "18px",
917
+ fontWeight: 500,
918
+ color: "#f0f0f0",
919
+ fontFamily: MONO_FONT
920
+ },
921
+ prodMessage: {
922
+ margin: "0 0 28px",
923
+ fontSize: "13px",
924
+ color: "#888",
925
+ lineHeight: 1.6,
926
+ fontFamily: MONO_FONT
927
+ },
928
+ prodButton: {
929
+ padding: "12px 24px",
930
+ backgroundColor: "#222",
931
+ color: "#bbb",
932
+ fontSize: "13px",
933
+ fontWeight: 500,
934
+ borderWidth: "1px",
935
+ borderStyle: "solid",
936
+ borderColor: "#444",
937
+ borderRadius: "6px",
938
+ cursor: "pointer",
939
+ transition: "all 0.15s",
940
+ fontFamily: MONO_FONT
941
+ }
942
+ };
943
+
944
+ //#endregion
945
+ //#region ../../src/react/router/contexts/RouterLayerContext.ts
946
+ const RouterLayerContext = createContext(void 0);
947
+
948
+ //#endregion
949
+ //#region ../../src/react/router/errors/Redirection.ts
950
+ /**
951
+ * Used for Redirection during the page loading.
952
+ *
953
+ * Depends on the context, it can be thrown or just returned.
954
+ *
955
+ * @example
956
+ * ```ts
957
+ * import { Redirection } from "alepha/react";
958
+ *
959
+ * const MyPage = $page({
960
+ * loader: async () => {
961
+ * if (needRedirect) {
962
+ * throw new Redirection("/new-path");
963
+ * }
964
+ * },
965
+ * });
966
+ * ```
967
+ */
968
+ var Redirection = class extends AlephaError {
969
+ redirect;
970
+ constructor(redirect) {
971
+ super("Redirection");
972
+ this.redirect = redirect;
973
+ }
974
+ };
975
+
976
+ //#endregion
977
+ //#region ../../src/react/router/hooks/useRouterState.ts
978
+ const useRouterState = () => {
979
+ const [state] = useStore("alepha.react.router.state");
980
+ if (!state) throw new AlephaError("Missing react router state");
981
+ return state;
982
+ };
983
+
984
+ //#endregion
985
+ //#region ../../src/react/router/components/NestedView.tsx
986
+ /**
987
+ * A component that renders the current view of the nested router layer.
988
+ *
989
+ * To be simple, it renders the `element` of the current child page of a parent page.
990
+ *
991
+ * @example
992
+ * ```tsx
993
+ * import { NestedView } from "alepha/react";
994
+ *
995
+ * class App {
996
+ * parent = $page({
997
+ * component: () => <NestedView />,
998
+ * });
999
+ *
1000
+ * child = $page({
1001
+ * parent: this.root,
1002
+ * component: () => <div>Child Page</div>,
1003
+ * });
1004
+ * }
1005
+ * ```
1006
+ */
1007
+ const NestedView = (props) => {
1008
+ const routerLayer = use(RouterLayerContext);
1009
+ const index = routerLayer?.index ?? 0;
1010
+ const onError = routerLayer?.onError;
1011
+ const state = useRouterState();
1012
+ const alepha = useAlepha();
1013
+ const [view, setView] = useState(state.layers[index]?.element);
1014
+ const [animation, setAnimation] = useState("");
1015
+ const animationExitDuration = useRef(0);
1016
+ const animationExitNow = useRef(0);
1017
+ useEvents({
1018
+ "react:transition:begin": async ({ previous, state }) => {
1019
+ const layer = previous.layers[index];
1020
+ if (!layer) return;
1021
+ if (`${state.url.pathname}/`.startsWith(`${layer.path}/`)) return;
1022
+ const animationExit = parseAnimation(layer.route?.animation, state, "exit");
1023
+ if (animationExit) {
1024
+ const duration = animationExit.duration || 200;
1025
+ animationExitNow.current = Date.now();
1026
+ animationExitDuration.current = duration;
1027
+ setAnimation(animationExit.animation);
1028
+ } else {
1029
+ animationExitNow.current = 0;
1030
+ animationExitDuration.current = 0;
1031
+ setAnimation("");
1032
+ }
1033
+ },
1034
+ "react:transition:end": async ({ state }) => {
1035
+ const layer = state.layers[index];
1036
+ if (animationExitNow.current) {
1037
+ const duration = animationExitDuration.current;
1038
+ const diff = Date.now() - animationExitNow.current;
1039
+ if (diff < duration) await new Promise((resolve) => setTimeout(resolve, duration - diff));
1040
+ }
1041
+ if (!layer?.cache) {
1042
+ setView(layer?.element);
1043
+ const animationEnter = parseAnimation(layer?.route?.animation, state, "enter");
1044
+ if (animationEnter) setAnimation(animationEnter.animation);
1045
+ else setAnimation("");
1046
+ }
1047
+ }
1048
+ }, []);
1049
+ let element = view ?? props.children ?? null;
1050
+ if (animation) element = /* @__PURE__ */ jsx("div", {
1051
+ style: {
1052
+ display: "flex",
1053
+ flex: 1,
1054
+ height: "100%",
1055
+ width: "100%",
1056
+ position: "relative",
1057
+ overflow: "hidden"
1058
+ },
1059
+ children: /* @__PURE__ */ jsx("div", {
1060
+ style: {
1061
+ height: "100%",
1062
+ width: "100%",
1063
+ display: "flex",
1064
+ animation
1065
+ },
1066
+ children: element
1067
+ })
1068
+ });
1069
+ if (props.errorBoundary === false) return /* @__PURE__ */ jsx(Fragment, { children: element });
1070
+ if (props.errorBoundary) return /* @__PURE__ */ jsx(ErrorBoundary, {
1071
+ fallback: props.errorBoundary,
1072
+ children: element
1073
+ });
1074
+ const fallback = (error) => {
1075
+ const result = onError?.(error, state) ?? /* @__PURE__ */ jsx(ErrorViewer_default, {
1076
+ error,
1077
+ alepha
1078
+ });
1079
+ if (result instanceof Redirection) return "Redirection inside ErrorBoundary is not allowed.";
1080
+ return result;
1081
+ };
1082
+ return /* @__PURE__ */ jsx(ErrorBoundary, {
1083
+ fallback,
1084
+ children: element
1085
+ });
1086
+ };
1087
+ var NestedView_default = memo(NestedView);
1088
+ function parseAnimation(animationLike, state, type = "enter") {
1089
+ if (!animationLike) return;
1090
+ const DEFAULT_DURATION = 300;
1091
+ const animation = typeof animationLike === "function" ? animationLike(state) : animationLike;
1092
+ if (typeof animation === "string") {
1093
+ if (type === "exit") return;
1094
+ return {
1095
+ duration: DEFAULT_DURATION,
1096
+ animation: `${DEFAULT_DURATION}ms ${animation}`
1097
+ };
1098
+ }
1099
+ if (typeof animation === "object") {
1100
+ const anim = animation[type];
1101
+ const duration = typeof anim === "object" ? anim.duration ?? DEFAULT_DURATION : DEFAULT_DURATION;
1102
+ const name = typeof anim === "object" ? anim.name : anim;
1103
+ if (type === "exit") return {
1104
+ duration,
1105
+ animation: `${duration}ms ${typeof anim === "object" ? anim.timing ?? "" : ""} ${name}`
1106
+ };
1107
+ return {
1108
+ duration,
1109
+ animation: `${duration}ms ${typeof anim === "object" ? anim.timing ?? "" : ""} ${name}`
1110
+ };
1111
+ }
1112
+ }
1113
+
1114
+ //#endregion
1115
+ //#region ../../src/react/router/providers/ReactPageProvider.ts
1116
+ const envSchema = t.object({ REACT_STRICT_MODE: t.boolean({ default: true }) });
1117
+ /**
1118
+ * Handle page routes for React applications. (Browser and Server)
1119
+ */
1120
+ var ReactPageProvider = class {
1121
+ log = $logger();
1122
+ env = $env(envSchema);
1123
+ alepha = $inject(Alepha);
1124
+ pages = [];
1125
+ getPages() {
1126
+ return this.pages;
1127
+ }
1128
+ getConcretePages() {
1129
+ const pages = [];
1130
+ for (const page of this.pages) {
1131
+ if (page.children && page.children.length > 0) continue;
1132
+ const fullPath = this.pathname(page.name);
1133
+ if (fullPath.includes(":") || fullPath.includes("*")) {
1134
+ if (typeof page.static === "object") {
1135
+ const entries = page.static.entries;
1136
+ if (entries && entries.length > 0) for (const entry of entries) {
1137
+ const params = entry.params;
1138
+ const path = this.compile(page.path ?? "", params);
1139
+ if (!path.includes(":") && !path.includes("*")) pages.push({
1140
+ ...page,
1141
+ name: params[Object.keys(params)[0]],
1142
+ staticName: page.name,
1143
+ path,
1144
+ ...entry
1145
+ });
1146
+ }
1147
+ }
1148
+ continue;
1149
+ }
1150
+ pages.push(page);
1151
+ }
1152
+ return pages;
1153
+ }
1154
+ page(name) {
1155
+ for (const page of this.pages) if (page.name === name) return page;
1156
+ throw new AlephaError(`Page '${name}' not found`);
1157
+ }
1158
+ pathname(name, options = {}) {
1159
+ const page = this.page(name);
1160
+ if (!page) throw new Error(`Page ${name} not found`);
1161
+ let url = page.path ?? "";
1162
+ let parent = page.parent;
1163
+ while (parent) {
1164
+ url = `${parent.path ?? ""}/${url}`;
1165
+ parent = parent.parent;
1166
+ }
1167
+ url = this.compile(url, options.params ?? {});
1168
+ if (options.query) {
1169
+ const query = new URLSearchParams(options.query);
1170
+ if (query.toString()) url += `?${query.toString()}`;
1171
+ }
1172
+ return url.replace(/\/\/+/g, "/") || "/";
1173
+ }
1174
+ url(name, options = {}) {
1175
+ return new URL(this.pathname(name, options), options.host ?? `http://localhost`);
1176
+ }
1177
+ root(state) {
1178
+ const root = createElement(AlephaContext.Provider, { value: this.alepha }, createElement(NestedView_default, {}, state.layers[0]?.element));
1179
+ if (this.env.REACT_STRICT_MODE) return createElement(StrictMode, {}, root);
1180
+ return root;
1181
+ }
1182
+ convertStringObjectToObject = (schema, value) => {
1183
+ if (t.schema.isObject(schema) && typeof value === "object") {
1184
+ for (const key in schema.properties) if (t.schema.isObject(schema.properties[key]) && typeof value[key] === "string") try {
1185
+ value[key] = this.alepha.codec.decode(schema.properties[key], decodeURIComponent(value[key]));
1186
+ } catch (e) {}
1187
+ }
1188
+ return value;
1189
+ };
1190
+ /**
1191
+ * Create a new RouterState based on a given route and request.
1192
+ * This method resolves the layers for the route, applying any query and params schemas defined in the route.
1193
+ * It also handles errors and redirects.
1194
+ */
1195
+ async createLayers(route, state, previous = []) {
1196
+ let context = {};
1197
+ const stack = [{ route }];
1198
+ let parent = route.parent;
1199
+ while (parent) {
1200
+ stack.unshift({ route: parent });
1201
+ parent = parent.parent;
1202
+ }
1203
+ let forceRefresh = false;
1204
+ for (let i = 0; i < stack.length; i++) {
1205
+ const it = stack[i];
1206
+ const route = it.route;
1207
+ const config = {};
1208
+ try {
1209
+ this.convertStringObjectToObject(route.schema?.query, state.query);
1210
+ config.query = route.schema?.query ? this.alepha.codec.decode(route.schema.query, state.query) : {};
1211
+ } catch (e) {
1212
+ it.error = e;
1213
+ break;
1214
+ }
1215
+ try {
1216
+ config.params = route.schema?.params ? this.alepha.codec.decode(route.schema.params, state.params) : {};
1217
+ } catch (e) {
1218
+ it.error = e;
1219
+ break;
1220
+ }
1221
+ it.config = { ...config };
1222
+ if (previous?.[i] && !forceRefresh && previous[i].name === route.name) {
1223
+ const url = (str) => str ? str.replace(/\/\/+/g, "/") : "/";
1224
+ if (JSON.stringify({
1225
+ part: url(previous[i].part),
1226
+ params: previous[i].config?.params ?? {}
1227
+ }) === JSON.stringify({
1228
+ part: url(route.path),
1229
+ params: config.params ?? {}
1230
+ })) {
1231
+ it.props = previous[i].props;
1232
+ it.error = previous[i].error;
1233
+ it.cache = true;
1234
+ context = {
1235
+ ...context,
1236
+ ...it.props
1237
+ };
1238
+ continue;
1239
+ }
1240
+ forceRefresh = true;
1241
+ }
1242
+ if (!route.loader) continue;
1243
+ try {
1244
+ const args = Object.create(state);
1245
+ Object.assign(args, config, context);
1246
+ const props = await route.loader?.(args) ?? {};
1247
+ it.props = { ...props };
1248
+ context = {
1249
+ ...context,
1250
+ ...props
1251
+ };
1252
+ } catch (e) {
1253
+ if (e instanceof Redirection) return { redirect: e.redirect };
1254
+ this.log.error("Page loader has failed", e);
1255
+ it.error = e;
1256
+ break;
1257
+ }
1258
+ }
1259
+ let acc = "";
1260
+ for (let i = 0; i < stack.length; i++) {
1261
+ const it = stack[i];
1262
+ const props = it.props ?? {};
1263
+ const params = { ...it.config?.params };
1264
+ for (const key of Object.keys(params)) params[key] = String(params[key]);
1265
+ acc += "/";
1266
+ acc += it.route.path ? this.compile(it.route.path, params) : "";
1267
+ const path = acc.replace(/\/+/, "/");
1268
+ const localErrorHandler = this.getErrorHandler(it.route);
1269
+ if (localErrorHandler) {
1270
+ const onErrorParent = state.onError;
1271
+ state.onError = (error, context) => {
1272
+ const result = localErrorHandler(error, context);
1273
+ if (result === void 0) return onErrorParent(error, context);
1274
+ return result;
1275
+ };
1276
+ }
1277
+ if (!it.error) try {
1278
+ const element = await this.createElement(it.route, {
1279
+ ...it.route.props ? it.route.props() : {},
1280
+ ...props,
1281
+ ...context
1282
+ });
1283
+ state.layers.push({
1284
+ name: it.route.name,
1285
+ props,
1286
+ part: it.route.path,
1287
+ config: it.config,
1288
+ element: this.renderView(i + 1, path, element, it.route),
1289
+ index: i + 1,
1290
+ path,
1291
+ route: it.route,
1292
+ cache: it.cache
1293
+ });
1294
+ } catch (e) {
1295
+ it.error = e;
1296
+ }
1297
+ if (it.error) try {
1298
+ let element = await state.onError(it.error, state);
1299
+ if (element === void 0) throw it.error;
1300
+ if (element instanceof Redirection) return { redirect: element.redirect };
1301
+ if (element === null) element = this.renderError(it.error);
1302
+ state.layers.push({
1303
+ props,
1304
+ error: it.error,
1305
+ name: it.route.name,
1306
+ part: it.route.path,
1307
+ config: it.config,
1308
+ element: this.renderView(i + 1, path, element, it.route),
1309
+ index: i + 1,
1310
+ path,
1311
+ route: it.route
1312
+ });
1313
+ break;
1314
+ } catch (e) {
1315
+ if (e instanceof Redirection) return { redirect: e.redirect };
1316
+ throw e;
1317
+ }
1318
+ }
1319
+ return { state };
1320
+ }
1321
+ getErrorHandler(route) {
1322
+ if (route.errorHandler) return route.errorHandler;
1323
+ let parent = route.parent;
1324
+ while (parent) {
1325
+ if (parent.errorHandler) return parent.errorHandler;
1326
+ parent = parent.parent;
1327
+ }
1328
+ }
1329
+ async createElement(page, props) {
1330
+ if (page.lazy && page.component) this.log.warn(`Page ${page.name} has both lazy and component options, lazy will be used`);
1331
+ if (page.lazy) return createElement((await page.lazy()).default, props);
1332
+ if (page.component) return createElement(page.component, props);
1333
+ }
1334
+ renderError(error) {
1335
+ return createElement(ErrorViewer_default, {
1336
+ error,
1337
+ alepha: this.alepha
1338
+ });
1339
+ }
1340
+ renderEmptyView() {
1341
+ return createElement(NestedView_default, {});
1342
+ }
1343
+ href(page, params = {}) {
1344
+ const found = this.pages.find((it) => it.name === page.options.name);
1345
+ if (!found) throw new AlephaError(`Page ${page.options.name} not found`);
1346
+ let url = found.path ?? "";
1347
+ let parent = found.parent;
1348
+ while (parent) {
1349
+ url = `${parent.path ?? ""}/${url}`;
1350
+ parent = parent.parent;
1351
+ }
1352
+ url = this.compile(url, params);
1353
+ return url.replace(/\/\/+/g, "/") || "/";
1354
+ }
1355
+ compile(path, params = {}) {
1356
+ for (const [key, value] of Object.entries(params)) path = path.replace(`:${key}`, value);
1357
+ return path;
1358
+ }
1359
+ renderView(index, path, view, page) {
1360
+ view ??= this.renderEmptyView();
1361
+ const element = page.client ? createElement(ClientOnly, typeof page.client === "object" ? page.client : {}, view) : view;
1362
+ return createElement(RouterLayerContext.Provider, { value: {
1363
+ index,
1364
+ path,
1365
+ onError: this.getErrorHandler(page) ?? ((error) => this.renderError(error))
1366
+ } }, element);
1367
+ }
1368
+ configure = $hook({
1369
+ on: "configure",
1370
+ handler: () => {
1371
+ let hasNotFoundHandler = false;
1372
+ const pages = this.alepha.primitives($page);
1373
+ const hasParent = (it) => {
1374
+ if (it.options.parent) return true;
1375
+ for (const page of pages) if ((page.options.children ? Array.isArray(page.options.children) ? page.options.children : page.options.children() : []).includes(it)) return true;
1376
+ };
1377
+ for (const page of pages) {
1378
+ if (page.options.path === "/*") hasNotFoundHandler = true;
1379
+ if (hasParent(page)) continue;
1380
+ this.add(this.map(pages, page));
1381
+ }
1382
+ if (!hasNotFoundHandler && pages.length > 0) this.add({
1383
+ path: "/*",
1384
+ name: "notFound",
1385
+ cache: true,
1386
+ component: NotFound_default,
1387
+ onServerResponse: ({ reply }) => {
1388
+ reply.status = 404;
1389
+ }
1390
+ });
1391
+ }
1392
+ });
1393
+ map(pages, target) {
1394
+ const children = target.options.children ? Array.isArray(target.options.children) ? target.options.children : target.options.children() : [];
1395
+ const getChildrenFromParent = (it) => {
1396
+ const children = [];
1397
+ for (const page of pages) if (page.options.parent === it) children.push(page);
1398
+ return children;
1399
+ };
1400
+ children.push(...getChildrenFromParent(target));
1401
+ return {
1402
+ ...target.options,
1403
+ name: target.name,
1404
+ parent: void 0,
1405
+ children: children.map((it) => this.map(pages, it))
1406
+ };
1407
+ }
1408
+ add(entry) {
1409
+ if (this.alepha.isReady()) throw new AlephaError("Router is already initialized");
1410
+ entry.name ??= this.nextId();
1411
+ const page = entry;
1412
+ page.match = this.createMatch(page);
1413
+ this.pages.push(page);
1414
+ if (page.children) for (const child of page.children) {
1415
+ child.parent = page;
1416
+ this.add(child);
1417
+ }
1418
+ }
1419
+ createMatch(page) {
1420
+ let url = page.path ?? "/";
1421
+ let target = page.parent;
1422
+ while (target) {
1423
+ url = `${target.path ?? ""}/${url}`;
1424
+ target = target.parent;
1425
+ }
1426
+ let path = url.replace(/\/\/+/g, "/");
1427
+ if (path.endsWith("/") && path !== "/") path = path.slice(0, -1);
1428
+ return path;
1429
+ }
1430
+ _next = 0;
1431
+ nextId() {
1432
+ this._next += 1;
1433
+ return `P${this._next}`;
1434
+ }
1435
+ };
1436
+ const isPageRoute = (it) => {
1437
+ return it && typeof it === "object" && typeof it.path === "string" && typeof it.page === "object";
1438
+ };
1439
+
1440
+ //#endregion
1441
+ //#region ../../src/react/router/providers/ReactBrowserRouterProvider.ts
1442
+ /**
1443
+ * Implementation of AlephaRouter for React in browser environment.
1444
+ */
1445
+ var ReactBrowserRouterProvider = class extends RouterProvider {
1446
+ log = $logger();
1447
+ alepha = $inject(Alepha);
1448
+ pageApi = $inject(ReactPageProvider);
1449
+ browserHeadProvider = $inject(BrowserHeadProvider);
1450
+ add(entry) {
1451
+ this.pageApi.add(entry);
1452
+ }
1453
+ configure = $hook({
1454
+ on: "configure",
1455
+ handler: async () => {
1456
+ for (const page of this.pageApi.getPages()) if (page.component || page.lazy) this.push({
1457
+ path: page.match,
1458
+ page
1459
+ });
1460
+ }
1461
+ });
1462
+ async transition(url, previous = [], meta = {}) {
1463
+ const { pathname, search } = url;
1464
+ const state = {
1465
+ url,
1466
+ query: {},
1467
+ params: {},
1468
+ layers: [],
1469
+ onError: () => null,
1470
+ meta
1471
+ };
1472
+ await this.alepha.events.emit("react:action:begin", { type: "transition" });
1473
+ await this.alepha.events.emit("react:transition:begin", {
1474
+ previous: this.alepha.store.get("alepha.react.router.state"),
1475
+ state
1476
+ });
1477
+ try {
1478
+ const { route, params } = this.match(pathname);
1479
+ const query = {};
1480
+ if (search) for (const [key, value] of new URLSearchParams(search).entries()) query[key] = String(value);
1481
+ state.name = route?.page.name;
1482
+ state.query = query;
1483
+ state.params = params ?? {};
1484
+ if (isPageRoute(route)) {
1485
+ const { redirect } = await this.pageApi.createLayers(route.page, state, previous);
1486
+ if (redirect) return redirect;
1487
+ }
1488
+ if (state.layers.length === 0) state.layers.push({
1489
+ name: "not-found",
1490
+ element: createElement(NotFound_default),
1491
+ index: 0,
1492
+ path: "/"
1493
+ });
1494
+ await this.alepha.events.emit("react:action:success", { type: "transition" });
1495
+ await this.alepha.events.emit("react:transition:success", { state });
1496
+ } catch (e) {
1497
+ this.log.error("Transition has failed", e);
1498
+ state.layers = [{
1499
+ name: "error",
1500
+ element: this.pageApi.renderError(e),
1501
+ index: 0,
1502
+ path: "/"
1503
+ }];
1504
+ await this.alepha.events.emit("react:action:error", {
1505
+ type: "transition",
1506
+ error: e
1507
+ });
1508
+ await this.alepha.events.emit("react:transition:error", {
1509
+ error: e,
1510
+ state
1511
+ });
1512
+ }
1513
+ if (previous) for (let i = 0; i < previous.length; i++) {
1514
+ const layer = previous[i];
1515
+ if (state.layers[i]?.name !== layer.name) this.pageApi.page(layer.name)?.onLeave?.();
1516
+ }
1517
+ for (let i = 0; i < state.layers.length; i++) {
1518
+ const layer = state.layers[i];
1519
+ if (previous?.[i]?.name !== layer.name) this.pageApi.page(layer.name)?.onEnter?.();
1520
+ }
1521
+ this.alepha.store.set("alepha.react.router.state", state);
1522
+ await this.alepha.events.emit("react:action:end", { type: "transition" });
1523
+ await this.alepha.events.emit("react:transition:end", { state });
1524
+ this.browserHeadProvider.fillAndRenderHead(state);
1525
+ }
1526
+ root(state) {
1527
+ return this.pageApi.root(state);
1528
+ }
1529
+ };
1530
+
1531
+ //#endregion
1532
+ //#region ../../src/react/router/providers/ReactBrowserProvider.ts
1533
+ /**
1534
+ * React browser renderer configuration atom
1535
+ */
1536
+ const reactBrowserOptions = $atom({
1537
+ name: "alepha.react.browser.options",
1538
+ schema: t.object({ scrollRestoration: t.enum(["top", "manual"]) }),
1539
+ default: { scrollRestoration: "top" }
1540
+ });
1541
+ var ReactBrowserProvider = class {
1542
+ log = $logger();
1543
+ client = $inject(LinkProvider);
1544
+ alepha = $inject(Alepha);
1545
+ router = $inject(ReactBrowserRouterProvider);
1546
+ dateTimeProvider = $inject(DateTimeProvider);
1547
+ browserHeadProvider = $inject(BrowserHeadProvider);
1548
+ options = $use(reactBrowserOptions);
1549
+ get rootId() {
1550
+ return "root";
1551
+ }
1552
+ getRootElement() {
1553
+ const root = this.document.getElementById(this.rootId);
1554
+ if (root) return root;
1555
+ const div = this.document.createElement("div");
1556
+ div.id = this.rootId;
1557
+ this.document.body.prepend(div);
1558
+ return div;
1559
+ }
1560
+ transitioning;
1561
+ get state() {
1562
+ return this.alepha.store.get("alepha.react.router.state");
1563
+ }
1564
+ /**
1565
+ * Accessor for Document DOM API.
1566
+ */
1567
+ get document() {
1568
+ return window.document;
1569
+ }
1570
+ /**
1571
+ * Accessor for History DOM API.
1572
+ */
1573
+ get history() {
1574
+ return window.history;
1575
+ }
1576
+ /**
1577
+ * Accessor for Location DOM API.
1578
+ */
1579
+ get location() {
1580
+ return window.location;
1581
+ }
1582
+ get base() {
1583
+ const base = import.meta.env?.BASE_URL;
1584
+ if (!base || base === "/") return "";
1585
+ return base;
1586
+ }
1587
+ get url() {
1588
+ const url = this.location.pathname + this.location.search;
1589
+ if (this.base) return url.replace(this.base, "");
1590
+ return url;
1591
+ }
1592
+ pushState(path, replace) {
1593
+ const url = this.base + path;
1594
+ if (replace) this.history.replaceState({}, "", url);
1595
+ else this.history.pushState({}, "", url);
1596
+ }
1597
+ async invalidate(props) {
1598
+ const previous = [];
1599
+ this.log.trace("Invalidating layers");
1600
+ if (props) {
1601
+ const [key] = Object.keys(props);
1602
+ const value = props[key];
1603
+ for (const layer of this.state.layers) {
1604
+ if (layer.props?.[key]) {
1605
+ previous.push({
1606
+ ...layer,
1607
+ props: {
1608
+ ...layer.props,
1609
+ [key]: value
1610
+ }
1611
+ });
1612
+ break;
1613
+ }
1614
+ previous.push(layer);
1615
+ }
1616
+ }
1617
+ await this.render({ previous });
1618
+ }
1619
+ async go(url, options = {}) {
1620
+ this.log.trace(`Going to ${url}`, {
1621
+ url,
1622
+ options
1623
+ });
1624
+ await this.render({
1625
+ url,
1626
+ previous: options.force ? [] : this.state.layers,
1627
+ meta: options.meta
1628
+ });
1629
+ if (this.state.url.pathname + this.state.url.search !== url) {
1630
+ this.pushState(this.state.url.pathname + this.state.url.search);
1631
+ return;
1632
+ }
1633
+ this.pushState(url, options.replace);
1634
+ }
1635
+ async render(options = {}) {
1636
+ const previous = options.previous ?? this.state.layers;
1637
+ const url = options.url ?? this.url;
1638
+ const start = this.dateTimeProvider.now();
1639
+ this.transitioning = {
1640
+ to: url,
1641
+ from: this.state?.url.pathname
1642
+ };
1643
+ this.log.debug("Transitioning...", { to: url });
1644
+ const redirect = await this.router.transition(new URL(`http://localhost${url}`), previous, options.meta);
1645
+ if (redirect) {
1646
+ this.log.info("Redirecting to", { redirect });
1647
+ if (redirect.startsWith("http")) window.location.href = redirect;
1648
+ else return await this.render({ url: redirect });
1649
+ }
1650
+ const ms = this.dateTimeProvider.now().diff(start);
1651
+ this.log.info(`Transition OK [${ms}ms]`, this.transitioning);
1652
+ this.transitioning = void 0;
1653
+ }
1654
+ /**
1655
+ * Get embedded layers from the server.
1656
+ */
1657
+ getHydrationState() {
1658
+ try {
1659
+ if ("__ssr" in window && typeof window.__ssr === "object") return window.__ssr;
1660
+ } catch (error) {
1661
+ console.error(error);
1662
+ }
1663
+ }
1664
+ onTransitionEnd = $hook({
1665
+ on: "react:transition:end",
1666
+ handler: () => {
1667
+ if (this.options.scrollRestoration === "top" && typeof window !== "undefined" && !this.alepha.isTest()) {
1668
+ this.log.trace("Restoring scroll position to top");
1669
+ window.scrollTo(0, 0);
1670
+ }
1671
+ }
1672
+ });
1673
+ ready = $hook({
1674
+ on: "ready",
1675
+ handler: async () => {
1676
+ const hydration = this.getHydrationState();
1677
+ const previous = hydration?.layers ?? [];
1678
+ if (hydration) {
1679
+ for (const [key, value] of Object.entries(hydration)) if (key !== "layers") this.alepha.set(key, value);
1680
+ }
1681
+ await this.render({ previous });
1682
+ const element = this.router.root(this.state);
1683
+ await this.alepha.events.emit("react:browser:render", {
1684
+ element,
1685
+ root: this.getRootElement(),
1686
+ hydration,
1687
+ state: this.state
1688
+ });
1689
+ this.browserHeadProvider.fillAndRenderHead(this.state);
1690
+ window.addEventListener("popstate", () => {
1691
+ if (this.base + this.state.url.pathname === this.location.pathname) return;
1692
+ this.log.debug("Popstate event triggered - rendering new state", { url: this.location.pathname + this.location.search });
1693
+ this.render();
1694
+ });
1695
+ }
1696
+ });
1697
+ };
1698
+
1699
+ //#endregion
1700
+ //#region ../../src/react/router/providers/ReactBrowserRendererProvider.ts
1701
+ /**
1702
+ * Browser specific React renderer (react-dom/client interface)
1703
+ */
1704
+ var ReactBrowserRendererProvider = class {
1705
+ log = $logger();
1706
+ root;
1707
+ onBrowserRender = $hook({
1708
+ on: "react:browser:render",
1709
+ handler: async ({ hydration, root, element }) => {
1710
+ if (hydration?.layers) {
1711
+ this.root = hydrateRoot(root, element);
1712
+ this.log.info("Hydrated root element");
1713
+ } else {
1714
+ this.root ??= createRoot(root);
1715
+ this.root.render(element);
1716
+ this.log.info("Created root element");
1717
+ }
1718
+ }
1719
+ });
1720
+ };
1721
+
1722
+ //#endregion
1723
+ //#region ../../src/react/router/services/ReactRouter.ts
1724
+ /**
1725
+ * Friendly browser router API.
1726
+ *
1727
+ * Can be safely used server-side, but most methods will be no-op.
1728
+ */
1729
+ var ReactRouter = class {
1730
+ alepha = $inject(Alepha);
1731
+ pageApi = $inject(ReactPageProvider);
1732
+ get state() {
1733
+ return this.alepha.store.get("alepha.react.router.state");
1734
+ }
1735
+ get pages() {
1736
+ return this.pageApi.getPages();
1737
+ }
1738
+ get concretePages() {
1739
+ return this.pageApi.getConcretePages();
1740
+ }
1741
+ get browser() {
1742
+ if (this.alepha.isBrowser()) return this.alepha.inject(ReactBrowserProvider);
1743
+ }
1744
+ isActive(href, options = {}) {
1745
+ const current = this.state.url.pathname;
1746
+ let isActive = current === href || current === `${href}/` || `${current}/` === href;
1747
+ if (options.startWith && !isActive) isActive = current.startsWith(href);
1748
+ return isActive;
1749
+ }
1750
+ node(name, config = {}) {
1751
+ const page = this.pageApi.page(name);
1752
+ if (!page.lazy && !page.component) return {
1753
+ ...page,
1754
+ label: page.label ?? page.name,
1755
+ children: void 0
1756
+ };
1757
+ return {
1758
+ ...page,
1759
+ label: page.label ?? page.name,
1760
+ href: this.path(name, config),
1761
+ children: void 0
1762
+ };
1763
+ }
1764
+ path(name, config = {}) {
1765
+ return this.pageApi.pathname(name, {
1766
+ params: {
1767
+ ...this.state?.params,
1768
+ ...config.params
1769
+ },
1770
+ query: config.query
1771
+ });
1772
+ }
1773
+ /**
1774
+ * Reload the current page.
1775
+ * This is equivalent to calling `go()` with the current pathname and search.
1776
+ */
1777
+ async reload() {
1778
+ if (!this.browser) return;
1779
+ await this.go(this.location.pathname + this.location.search, {
1780
+ replace: true,
1781
+ force: true
1782
+ });
1783
+ }
1784
+ getURL() {
1785
+ if (!this.browser) return this.state.url;
1786
+ return new URL(this.location.href);
1787
+ }
1788
+ get location() {
1789
+ if (!this.browser) throw new Error("Browser is required");
1790
+ return this.browser.location;
1791
+ }
1792
+ get current() {
1793
+ return this.state;
1794
+ }
1795
+ get pathname() {
1796
+ return this.state.url.pathname;
1797
+ }
1798
+ get query() {
1799
+ const query = {};
1800
+ for (const [key, value] of new URLSearchParams(this.state.url.search).entries()) query[key] = String(value);
1801
+ return query;
1802
+ }
1803
+ async back() {
1804
+ this.browser?.history.back();
1805
+ }
1806
+ async forward() {
1807
+ this.browser?.history.forward();
1808
+ }
1809
+ async invalidate(props) {
1810
+ await this.browser?.invalidate(props);
1811
+ }
1812
+ async go(path, options) {
1813
+ for (const page of this.pages) if (page.name === path) {
1814
+ await this.browser?.go(this.path(path, options), options);
1815
+ return;
1816
+ }
1817
+ await this.browser?.go(path, options);
1818
+ }
1819
+ anchor(path, options = {}) {
1820
+ let href = path;
1821
+ for (const page of this.pages) if (page.name === path) {
1822
+ href = this.path(path, options);
1823
+ break;
1824
+ }
1825
+ return {
1826
+ href: this.base(href),
1827
+ onClick: (ev) => {
1828
+ ev.stopPropagation();
1829
+ ev.preventDefault();
1830
+ this.go(href, options).catch(console.error);
1831
+ }
1832
+ };
1833
+ }
1834
+ base(path) {
1835
+ const base = import.meta.env?.BASE_URL;
1836
+ if (!base || base === "/") return path;
1837
+ return base + path;
1838
+ }
1839
+ /**
1840
+ * Set query params.
1841
+ *
1842
+ * @param record
1843
+ * @param options
1844
+ */
1845
+ setQueryParams(record, options = {}) {
1846
+ const func = typeof record === "function" ? record : () => record;
1847
+ const search = new URLSearchParams(func(this.query)).toString();
1848
+ const state = search ? `${this.pathname}?${search}` : this.pathname;
1849
+ if (options.push) window.history.pushState({}, "", state);
1850
+ else window.history.replaceState({}, "", state);
1851
+ }
1852
+ };
1853
+
1854
+ //#endregion
1855
+ //#region ../../src/react/router/hooks/useRouter.ts
1856
+ /**
1857
+ * Use this hook to access the React Router instance.
1858
+ *
1859
+ * You can add a type parameter to specify the type of your application.
1860
+ * This will allow you to use the router in a typesafe way.
1861
+ *
1862
+ * @example
1863
+ * class App {
1864
+ * home = $page();
1865
+ * }
1866
+ *
1867
+ * const router = useRouter<App>();
1868
+ * router.go("home"); // typesafe
1869
+ */
1870
+ const useRouter = () => {
1871
+ return useInject(ReactRouter);
1872
+ };
1873
+
1874
+ //#endregion
1875
+ //#region ../../src/react/router/components/Link.tsx
1876
+ /**
1877
+ * Link component for client-side navigation.
1878
+ *
1879
+ * It's a simple wrapper around an anchor (`<a>`) element using the `useRouter` hook.
1880
+ */
1881
+ const Link = (props) => {
1882
+ const router = useRouter();
1883
+ return createElement("a", {
1884
+ ...props,
1885
+ ...router.anchor(props.href)
1886
+ }, props.children);
1887
+ };
1888
+ var Link_default = Link;
1889
+
1890
+ //#endregion
1891
+ //#region ../../src/react/router/hooks/useActive.ts
1892
+ /**
1893
+ * Hook to determine if a given route is active and to provide anchor props for navigation.
1894
+ * This hook refreshes on router state changes.
1895
+ */
1896
+ const useActive = (args) => {
1897
+ useRouterState();
1898
+ const router = useRouter();
1899
+ const [isPending, setPending] = useState(false);
1900
+ const options = typeof args === "string" ? { href: args } : {
1901
+ ...args,
1902
+ href: args.href
1903
+ };
1904
+ const href = options.href;
1905
+ const isActive = router.isActive(href, options);
1906
+ return {
1907
+ isPending,
1908
+ isActive,
1909
+ anchorProps: {
1910
+ href: router.base(href),
1911
+ onClick: async (ev) => {
1912
+ ev?.stopPropagation();
1913
+ ev?.preventDefault();
1914
+ if (isActive) return;
1915
+ if (isPending) return;
1916
+ setPending(true);
1917
+ try {
1918
+ await router.go(href);
1919
+ } finally {
1920
+ setPending(false);
1921
+ }
1922
+ }
1923
+ }
1924
+ };
1925
+ };
1926
+
1927
+ //#endregion
1928
+ //#region ../../src/react/router/hooks/useQueryParams.ts
1929
+ /**
1930
+ * Hook to manage query parameters in the URL using a defined schema.
1931
+ */
1932
+ const useQueryParams = (schema, options = {}) => {
1933
+ const alepha = useAlepha();
1934
+ const key = options.key ?? "q";
1935
+ const router = useRouter();
1936
+ const querystring = router.query[key];
1937
+ const [queryParams = {}, setQueryParams] = useState(decode(alepha, schema, router.query[key]));
1938
+ useEffect(() => {
1939
+ setQueryParams(decode(alepha, schema, querystring));
1940
+ }, [querystring]);
1941
+ return [queryParams, (queryParams) => {
1942
+ setQueryParams(queryParams);
1943
+ router.setQueryParams((data) => {
1944
+ return {
1945
+ ...data,
1946
+ [key]: encode(alepha, schema, queryParams)
1947
+ };
1948
+ });
1949
+ }];
1950
+ };
1951
+ const encode = (alepha, schema, data) => {
1952
+ return btoa(JSON.stringify(alepha.codec.decode(schema, data)));
1953
+ };
1954
+ const decode = (alepha, schema, data) => {
1955
+ try {
1956
+ return alepha.codec.decode(schema, JSON.parse(atob(decodeURIComponent(data))));
1957
+ } catch {
1958
+ return;
1959
+ }
1960
+ };
1961
+
1962
+ //#endregion
1963
+ //#region ../../src/react/router/index.browser.ts
1964
+ const AlephaReactRouter = $module({
1965
+ name: "alepha.react.router",
1966
+ primitives: [$page],
1967
+ services: [
1968
+ ReactPageProvider,
1969
+ ReactBrowserRouterProvider,
1970
+ ReactBrowserProvider,
1971
+ ReactRouter,
1972
+ ReactBrowserRendererProvider,
1973
+ ReactPageService
1974
+ ],
1975
+ register: (alepha) => alepha.with(AlephaReact).with(AlephaDateTime).with(AlephaServer).with(AlephaServerLinks).with(ReactPageProvider).with(ReactBrowserProvider).with(ReactBrowserRouterProvider).with(ReactBrowserRendererProvider).with(ReactRouter)
1976
+ });
1977
+
1978
+ //#endregion
1979
+ export { $page, AlephaReactRouter, ErrorViewer_default as ErrorViewer, Link_default as Link, NestedView_default as NestedView, NotFound_default as NotFound, PAGE_PRELOAD_KEY, PagePrimitive, ReactBrowserProvider, ReactBrowserRendererProvider, ReactBrowserRouterProvider, ReactPageProvider, ReactPageService, ReactRouter, Redirection, RouterLayerContext, isPageRoute, reactBrowserOptions, useActive, useQueryParams, useRouter, useRouterState };
1980
+ //# sourceMappingURL=index.browser.js.map