alepha 0.18.2 → 0.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (397) hide show
  1. package/assets/devtools-ui/200.html +2 -2
  2. package/assets/devtools-ui/200.html.br +0 -0
  3. package/assets/devtools-ui/404.html +2 -2
  4. package/assets/devtools-ui/404.html.br +0 -0
  5. package/assets/devtools-ui/{asset.BfSBZ5Dd.css → asset.hG_f8HuK.css} +1 -1
  6. package/assets/devtools-ui/asset.hG_f8HuK.css.br +0 -0
  7. package/assets/devtools-ui/chunk.B3au4Lhg.js +1 -0
  8. package/assets/devtools-ui/chunk.B3au4Lhg.js.br +0 -0
  9. package/assets/devtools-ui/chunk.BLOrlnMB.js +1 -0
  10. package/assets/devtools-ui/chunk.BLOrlnMB.js.br +0 -0
  11. package/assets/devtools-ui/chunk.BLR01ljW.js +1 -0
  12. package/assets/devtools-ui/chunk.BLR01ljW.js.br +0 -0
  13. package/assets/devtools-ui/chunk.BTXaIUlA.js +1 -0
  14. package/assets/devtools-ui/chunk.BTXaIUlA.js.br +0 -0
  15. package/assets/devtools-ui/{chunk.lJL-lgnW.js → chunk.BhJaxmm8.js} +1 -1
  16. package/assets/devtools-ui/chunk.BhJaxmm8.js.br +0 -0
  17. package/assets/devtools-ui/chunk.BtoNxFuL.js +1 -0
  18. package/assets/devtools-ui/chunk.BtoNxFuL.js.br +0 -0
  19. package/assets/devtools-ui/chunk.C8YUV2Wd.js +1 -0
  20. package/assets/devtools-ui/chunk.C8YUV2Wd.js.br +0 -0
  21. package/assets/devtools-ui/{chunk.M6wyKO_3.js → chunk.CBbIgDzE.js} +2 -2
  22. package/assets/devtools-ui/chunk.CBbIgDzE.js.br +0 -0
  23. package/assets/devtools-ui/chunk.CFqIniwA.js +1 -0
  24. package/assets/devtools-ui/chunk.CFqIniwA.js.br +0 -0
  25. package/assets/devtools-ui/chunk.CLFF7f7-.js +1 -0
  26. package/assets/devtools-ui/chunk.CLFF7f7-.js.br +0 -0
  27. package/assets/devtools-ui/chunk.CRsBbA10.js +1 -0
  28. package/assets/devtools-ui/chunk.CRsBbA10.js.br +0 -0
  29. package/assets/devtools-ui/{chunk.DbEH1oOB.js → chunk.CZPo6v95.js} +1 -1
  30. package/assets/devtools-ui/chunk.CZPo6v95.js.br +0 -0
  31. package/assets/devtools-ui/chunk.D0fWgNos.js +1 -0
  32. package/assets/devtools-ui/chunk.D0fWgNos.js.br +1 -0
  33. package/assets/devtools-ui/chunk.D7-0ziQ6.js +1 -0
  34. package/assets/devtools-ui/chunk.D7-0ziQ6.js.br +0 -0
  35. package/assets/devtools-ui/chunk.DAewe0vm.js +1 -0
  36. package/assets/devtools-ui/chunk.DAewe0vm.js.br +0 -0
  37. package/assets/devtools-ui/chunk.DJRQEYqK.js +1 -0
  38. package/assets/devtools-ui/chunk.DJRQEYqK.js.br +0 -0
  39. package/assets/devtools-ui/{chunk.CZl6J9DF.js → chunk.DMAxv14p.js} +1 -1
  40. package/assets/devtools-ui/chunk.DMAxv14p.js.br +0 -0
  41. package/assets/devtools-ui/{chunk.BT2IiBkZ.js → chunk.DMImnNjU.js} +1 -1
  42. package/assets/devtools-ui/chunk.DMImnNjU.js.br +0 -0
  43. package/assets/devtools-ui/chunk.DeeQsidk.js +9 -0
  44. package/assets/devtools-ui/chunk.DeeQsidk.js.br +0 -0
  45. package/assets/devtools-ui/chunk.DqEwn9Vj.js +7 -0
  46. package/assets/devtools-ui/chunk.DqEwn9Vj.js.br +0 -0
  47. package/assets/devtools-ui/chunk.Dt8OsQey.js +1 -0
  48. package/assets/devtools-ui/chunk.Dt8OsQey.js.br +0 -0
  49. package/assets/devtools-ui/{chunk.B9pX3zit.js → chunk.Dtp8oa_f.js} +1 -1
  50. package/assets/devtools-ui/chunk.Dtp8oa_f.js.br +0 -0
  51. package/assets/devtools-ui/chunk.Dx3JzAYM.js +1 -0
  52. package/assets/devtools-ui/chunk.Dx3JzAYM.js.br +0 -0
  53. package/assets/devtools-ui/chunk.GCOj1-5E.js +1 -0
  54. package/assets/devtools-ui/chunk.GCOj1-5E.js.br +0 -0
  55. package/assets/devtools-ui/chunk.IC1LD8BH.js +1 -0
  56. package/assets/devtools-ui/chunk.IC1LD8BH.js.br +0 -0
  57. package/assets/devtools-ui/chunk.IwuB_TqW.js +1 -0
  58. package/assets/devtools-ui/chunk.IwuB_TqW.js.br +0 -0
  59. package/assets/devtools-ui/chunk.Qqapj2zq.js +1 -0
  60. package/assets/devtools-ui/chunk.Qqapj2zq.js.br +0 -0
  61. package/assets/devtools-ui/{chunk.C79YouPp.js → chunk.TKKKndOy.js} +1 -1
  62. package/assets/devtools-ui/chunk.TKKKndOy.js.br +0 -0
  63. package/assets/devtools-ui/chunk.YHTVhFQT.js +1 -0
  64. package/assets/devtools-ui/chunk.YHTVhFQT.js.br +0 -0
  65. package/assets/devtools-ui/chunk.fnod6uEi.js +1 -0
  66. package/assets/devtools-ui/chunk.fnod6uEi.js.br +0 -0
  67. package/assets/devtools-ui/chunk.mOCRmXjo.js +1 -0
  68. package/assets/devtools-ui/chunk.mOCRmXjo.js.br +0 -0
  69. package/assets/devtools-ui/chunk.qZTNEAK0.js +1 -0
  70. package/assets/devtools-ui/chunk.qZTNEAK0.js.br +0 -0
  71. package/assets/devtools-ui/chunk.rc9m0y4-.js +1 -0
  72. package/assets/devtools-ui/chunk.rc9m0y4-.js.br +0 -0
  73. package/assets/devtools-ui/entry.Cxc5QLCU.js +80 -0
  74. package/assets/devtools-ui/entry.Cxc5QLCU.js.br +0 -0
  75. package/assets/devtools-ui/index.html +2 -2
  76. package/assets/devtools-ui/index.html.br +0 -0
  77. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  78. package/assets/swagger-ui/swagger-ui.css +1 -1
  79. package/dist/api/audits/index.d.ts +61 -5
  80. package/dist/api/audits/index.d.ts.map +1 -1
  81. package/dist/api/files/index.d.ts +61 -5
  82. package/dist/api/files/index.d.ts.map +1 -1
  83. package/dist/api/jobs/index.d.ts +61 -5
  84. package/dist/api/jobs/index.d.ts.map +1 -1
  85. package/dist/api/jobs/index.js +4 -2
  86. package/dist/api/jobs/index.js.map +1 -1
  87. package/dist/api/keys/index.d.ts +5 -5
  88. package/dist/api/notifications/index.browser.js +44 -1
  89. package/dist/api/notifications/index.browser.js.map +1 -1
  90. package/dist/api/notifications/index.d.ts +187 -2
  91. package/dist/api/notifications/index.d.ts.map +1 -1
  92. package/dist/api/notifications/index.js +143 -8
  93. package/dist/api/notifications/index.js.map +1 -1
  94. package/dist/api/parameters/index.d.ts +61 -5
  95. package/dist/api/parameters/index.d.ts.map +1 -1
  96. package/dist/api/users/index.d.ts +330 -93
  97. package/dist/api/users/index.d.ts.map +1 -1
  98. package/dist/api/users/index.js +27 -36
  99. package/dist/api/users/index.js.map +1 -1
  100. package/dist/cli/config/index.d.ts +46 -0
  101. package/dist/cli/config/index.d.ts.map +1 -0
  102. package/dist/cli/config/index.js +20 -0
  103. package/dist/cli/config/index.js.map +1 -0
  104. package/dist/cli/core/index.d.ts +69 -66
  105. package/dist/cli/core/index.d.ts.map +1 -1
  106. package/dist/cli/core/index.js +329 -196
  107. package/dist/cli/core/index.js.map +1 -1
  108. package/dist/cli/platform/index.d.ts +302 -63
  109. package/dist/cli/platform/index.d.ts.map +1 -1
  110. package/dist/cli/platform/index.js +455 -25
  111. package/dist/cli/platform/index.js.map +1 -1
  112. package/dist/core/index.browser.js +125 -87
  113. package/dist/core/index.browser.js.map +1 -1
  114. package/dist/core/index.d.ts +62 -53
  115. package/dist/core/index.d.ts.map +1 -1
  116. package/dist/core/index.js +125 -87
  117. package/dist/core/index.js.map +1 -1
  118. package/dist/core/index.native.js +125 -87
  119. package/dist/core/index.native.js.map +1 -1
  120. package/dist/core/index.workerd.js +125 -87
  121. package/dist/core/index.workerd.js.map +1 -1
  122. package/dist/crypto/index.d.ts +18 -1
  123. package/dist/crypto/index.d.ts.map +1 -1
  124. package/dist/crypto/index.js +29 -3
  125. package/dist/crypto/index.js.map +1 -1
  126. package/dist/devtools/index.js +3 -12
  127. package/dist/devtools/index.js.map +1 -1
  128. package/dist/logger/index.d.ts +10 -1
  129. package/dist/logger/index.d.ts.map +1 -1
  130. package/dist/logger/index.js +19 -9
  131. package/dist/logger/index.js.map +1 -1
  132. package/dist/orm/core/index.browser.js +57 -1
  133. package/dist/orm/core/index.browser.js.map +1 -1
  134. package/dist/orm/core/index.bun.js +378 -19
  135. package/dist/orm/core/index.bun.js.map +1 -1
  136. package/dist/orm/core/index.d.ts +328 -9
  137. package/dist/orm/core/index.d.ts.map +1 -1
  138. package/dist/orm/core/index.js +384 -21
  139. package/dist/orm/core/index.js.map +1 -1
  140. package/dist/orm/postgres/index.bun.js +49 -17
  141. package/dist/orm/postgres/index.bun.js.map +1 -1
  142. package/dist/orm/postgres/index.d.ts +47 -21
  143. package/dist/orm/postgres/index.d.ts.map +1 -1
  144. package/dist/orm/postgres/index.js +52 -17
  145. package/dist/orm/postgres/index.js.map +1 -1
  146. package/dist/react/core/index.d.ts +1 -1
  147. package/dist/react/core/index.d.ts.map +1 -1
  148. package/dist/react/core/index.js +6 -1
  149. package/dist/react/core/index.js.map +1 -1
  150. package/dist/react/form/index.d.ts +28 -18
  151. package/dist/react/form/index.d.ts.map +1 -1
  152. package/dist/react/form/index.js +92 -56
  153. package/dist/react/form/index.js.map +1 -1
  154. package/dist/react/router/index.browser.js +448 -116
  155. package/dist/react/router/index.browser.js.map +1 -1
  156. package/dist/react/router/index.d.ts +102 -40
  157. package/dist/react/router/index.d.ts.map +1 -1
  158. package/dist/react/router/index.js +453 -92
  159. package/dist/react/router/index.js.map +1 -1
  160. package/dist/security/index.d.ts +3 -11
  161. package/dist/security/index.d.ts.map +1 -1
  162. package/dist/security/index.js +6 -11
  163. package/dist/security/index.js.map +1 -1
  164. package/dist/server/auth/index.d.ts +22 -24
  165. package/dist/server/auth/index.d.ts.map +1 -1
  166. package/dist/server/auth/index.js +102 -82
  167. package/dist/server/auth/index.js.map +1 -1
  168. package/dist/server/cookies/index.d.ts +7 -4
  169. package/dist/server/cookies/index.d.ts.map +1 -1
  170. package/dist/server/cookies/index.js +13 -12
  171. package/dist/server/cookies/index.js.map +1 -1
  172. package/dist/server/core/index.d.ts +288 -4
  173. package/dist/server/core/index.d.ts.map +1 -1
  174. package/dist/server/core/index.js +375 -2
  175. package/dist/server/core/index.js.map +1 -1
  176. package/dist/server/links/index.browser.js +10 -71
  177. package/dist/server/links/index.browser.js.map +1 -1
  178. package/dist/server/links/index.d.ts +32 -49
  179. package/dist/server/links/index.d.ts.map +1 -1
  180. package/dist/server/links/index.js +73 -100
  181. package/dist/server/links/index.js.map +1 -1
  182. package/dist/system/index.browser.js +221 -2
  183. package/dist/system/index.browser.js.map +1 -1
  184. package/dist/system/index.d.ts +63 -1
  185. package/dist/system/index.d.ts.map +1 -1
  186. package/dist/system/index.js +221 -1
  187. package/dist/system/index.js.map +1 -1
  188. package/dist/system/index.workerd.js +224 -4
  189. package/dist/system/index.workerd.js.map +1 -1
  190. package/package.json +10 -5
  191. package/src/api/jobs/providers/JobProvider.ts +6 -3
  192. package/src/api/notifications/controllers/AdminNotificationController.ts +83 -0
  193. package/src/api/notifications/index.browser.ts +3 -0
  194. package/src/api/notifications/index.ts +14 -2
  195. package/src/api/notifications/jobs/NotificationJobs.ts +11 -2
  196. package/src/api/notifications/schemas/notificationDetailResourceSchema.ts +20 -0
  197. package/src/api/notifications/schemas/notificationQuerySchema.ts +19 -0
  198. package/src/api/notifications/schemas/notificationResourceSchema.ts +18 -0
  199. package/src/api/notifications/services/NotificationSenderService.ts +15 -2
  200. package/src/api/users/atoms/realmAuthSettingsAtom.ts +28 -32
  201. package/src/api/users/buckets/UserBuckets.ts +1 -1
  202. package/src/api/users/jobs/UserJobs.ts +1 -1
  203. package/src/api/users/primitives/$realm.ts +8 -49
  204. package/src/api/users/providers/RealmProvider.ts +2 -3
  205. package/src/api/users/services/RegistrationService.spec.ts +7 -7
  206. package/src/api/users/services/RegistrationService.ts +3 -3
  207. package/src/api/users/services/SessionService.spec.ts +4 -4
  208. package/src/api/users/services/SessionService.ts +3 -3
  209. package/src/cli/{core → config}/defineConfig.ts +14 -20
  210. package/src/cli/config/index.ts +1 -0
  211. package/src/cli/core/commands/db.ts +65 -1
  212. package/src/cli/core/commands/dev.ts +1 -0
  213. package/src/cli/core/commands/init.ts +2 -192
  214. package/src/cli/core/index.ts +34 -11
  215. package/src/cli/core/providers/ViteDevServerProvider.ts +52 -13
  216. package/src/cli/core/services/PackageManagerUtils.ts +43 -21
  217. package/src/cli/core/services/ProjectScaffolder.ts +214 -2
  218. package/src/cli/core/services/ViteUtils.ts +57 -0
  219. package/src/cli/core/tasks/BuildClientTask.ts +7 -2
  220. package/src/cli/core/tasks/BuildCloudflareTask.ts +4 -12
  221. package/src/cli/core/tasks/BuildServerTask.ts +2 -0
  222. package/src/cli/core/tasks/BuildVercelTask.ts +165 -168
  223. package/src/cli/core/templates/alephaConfigTs.ts +1 -1
  224. package/src/cli/core/templates/apiAppSecurityTs.ts +5 -8
  225. package/src/cli/core/templates/tsconfigJson.ts +6 -1
  226. package/src/cli/platform/adapters/CloudflareAdapter.spec.ts +1 -1
  227. package/src/cli/platform/adapters/CloudflareAdapter.ts +30 -29
  228. package/src/cli/platform/atoms/platformOptions.ts +21 -0
  229. package/src/cli/platform/commands/SecretsCommand.spec.ts +298 -0
  230. package/src/cli/platform/commands/SecretsCommand.ts +283 -0
  231. package/src/cli/platform/commands/platform.ts +12 -0
  232. package/src/cli/platform/index.ts +14 -28
  233. package/src/cli/platform/providers/GitHubSecretStore.spec.ts +153 -0
  234. package/src/cli/platform/providers/GitHubSecretStore.ts +112 -0
  235. package/src/cli/platform/providers/MemorySecretStore.ts +114 -0
  236. package/src/cli/platform/providers/SecretStoreProvider.ts +39 -0
  237. package/src/cli/platform/schemas/cloudflare.ts +2 -0
  238. package/src/cli/platform/services/CloudflareApi.ts +5 -2
  239. package/src/cli/platform/services/DockerComposeGenerator.spec.ts +115 -0
  240. package/src/cli/platform/services/DockerComposeGenerator.ts +46 -1
  241. package/src/cli/platform/services/SecretFilterService.spec.ts +111 -0
  242. package/src/cli/platform/services/SecretFilterService.ts +54 -0
  243. package/src/core/Alepha.ts +94 -25
  244. package/src/core/__tests__/Alepha-parseEnv.spec.ts +20 -0
  245. package/src/core/primitives/$memoize.ts +38 -26
  246. package/src/core/providers/AlsProvider.ts +2 -0
  247. package/src/core/providers/EventManager.ts +4 -0
  248. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +1 -4
  249. package/src/core/providers/KeylessJsonSchemaCodec.ts +19 -125
  250. package/src/core/providers/SchemaValidator.spec.ts +36 -0
  251. package/src/core/providers/SchemaValidator.ts +9 -0
  252. package/src/crypto/index.ts +6 -1
  253. package/src/crypto/providers/SecretProvider.ts +36 -0
  254. package/src/devtools/providers/DevToolsProvider.ts +3 -12
  255. package/src/logger/index.ts +33 -6
  256. package/src/logger/providers/PrettyFormatterProvider.ts +5 -3
  257. package/src/orm/__tests__/orm-next-tests.ts +492 -0
  258. package/src/orm/__tests__/orm-next.spec.ts +140 -0
  259. package/src/orm/core/constants/PG_SYMBOLS.ts +17 -0
  260. package/src/orm/core/index.bun.ts +3 -6
  261. package/src/orm/core/index.shared-server.ts +2 -0
  262. package/src/orm/core/index.shared.ts +2 -0
  263. package/src/orm/core/index.ts +5 -7
  264. package/src/orm/core/interfaces/AggregateQuery.ts +103 -0
  265. package/src/orm/core/interfaces/PgQueryWhere.ts +7 -0
  266. package/src/orm/core/primitives/$entity.ts +8 -0
  267. package/src/orm/core/primitives/$repository.ts +6 -3
  268. package/src/orm/core/primitives/$view.ts +88 -0
  269. package/src/orm/core/providers/DbCacheProvider.ts +66 -0
  270. package/src/orm/core/providers/DrizzleKitProvider.ts +42 -0
  271. package/src/orm/core/providers/drivers/BunSqliteProvider.ts +2 -3
  272. package/src/orm/core/providers/drivers/CloudflareD1Provider.ts +12 -0
  273. package/src/orm/core/providers/drivers/DatabaseProvider.ts +39 -0
  274. package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +2 -3
  275. package/src/orm/core/schemas/databaseEnvSchema.ts +31 -0
  276. package/src/orm/core/schemas/insertSchema.ts +13 -3
  277. package/src/orm/core/schemas/updateSchema.ts +14 -3
  278. package/src/orm/core/services/ModelBuilder.ts +26 -14
  279. package/src/orm/core/services/QueryManager.ts +13 -0
  280. package/src/orm/core/services/Repository.ts +307 -5
  281. package/src/orm/core/services/SqliteModelBuilder.ts +38 -0
  282. package/src/orm/postgres/index.bun.ts +4 -7
  283. package/src/orm/postgres/index.ts +4 -7
  284. package/src/orm/postgres/providers/BunPostgresProvider.ts +12 -2
  285. package/src/orm/postgres/providers/NodePostgresProvider.ts +7 -0
  286. package/src/orm/postgres/providers/PglitePostgresProvider.ts +10 -17
  287. package/src/orm/postgres/providers/PostgresProvider.ts +7 -36
  288. package/src/orm/postgres/schemas/postgresEnvSchema.ts +32 -0
  289. package/src/orm/postgres/services/PostgresModelBuilder.ts +40 -0
  290. package/src/react/core/components/ErrorBoundary.tsx +5 -2
  291. package/src/react/form/hooks/useFieldValue.ts +34 -0
  292. package/src/react/form/hooks/useForm.browser.spec.tsx +94 -9
  293. package/src/react/form/hooks/useForm.ts +14 -2
  294. package/src/react/form/hooks/useFormState.ts +10 -10
  295. package/src/react/form/hooks/useFormValues.ts +29 -0
  296. package/src/react/form/index.ts +3 -1
  297. package/src/react/form/services/FormModel.ts +53 -122
  298. package/src/react/router/components/ErrorViewer.tsx +333 -34
  299. package/src/react/router/components/NestedView.tsx +10 -3
  300. package/src/react/router/primitives/$page.browser.spec.tsx +34 -0
  301. package/src/react/router/primitives/$page.spec.tsx +20 -0
  302. package/src/react/router/primitives/$page.ts +24 -0
  303. package/src/react/router/providers/ReactBrowserRouterProvider.ts +14 -2
  304. package/src/react/router/providers/ReactPageProvider.ts +156 -73
  305. package/src/react/router/providers/ReactServerProvider.ts +40 -2
  306. package/src/react/router/providers/ReactServerTemplateProvider.ts +13 -1
  307. package/src/security/providers/SecurityProvider.ts +5 -27
  308. package/src/server/auth/primitives/$auth.ts +52 -19
  309. package/src/server/auth/providers/ServerAuthProvider.ts +145 -139
  310. package/src/server/cookies/providers/ServerCookiesProvider.ts +12 -24
  311. package/src/server/core/index.ts +3 -1
  312. package/src/server/core/primitives/$sse.spec.ts +315 -0
  313. package/src/server/core/primitives/$sse.ts +715 -0
  314. package/src/server/links/index.browser.ts +1 -3
  315. package/src/server/links/index.ts +0 -3
  316. package/src/server/links/providers/LinkProvider.spec.ts +12 -21
  317. package/src/server/links/providers/LinkProvider.ts +20 -52
  318. package/src/server/links/providers/ServerLinksProvider.spec.ts +106 -0
  319. package/src/server/links/providers/ServerLinksProvider.ts +113 -73
  320. package/src/server/links/schemas/apiLinksResponseSchema.ts +4 -21
  321. package/src/server/links/services/BatchCollector.ts +5 -3
  322. package/src/system/index.browser.ts +1 -0
  323. package/src/system/index.ts +3 -0
  324. package/src/system/index.workerd.ts +39 -1
  325. package/src/system/providers/WorkerdFileSystemProvider.ts +365 -0
  326. package/assets/devtools-ui/asset.BfSBZ5Dd.css.br +0 -0
  327. package/assets/devtools-ui/chunk.2NYaoqWt.js +0 -1
  328. package/assets/devtools-ui/chunk.2NYaoqWt.js.br +0 -0
  329. package/assets/devtools-ui/chunk.B052Z_xQ.js +0 -1
  330. package/assets/devtools-ui/chunk.B052Z_xQ.js.br +0 -0
  331. package/assets/devtools-ui/chunk.B4kVY90C.js +0 -1
  332. package/assets/devtools-ui/chunk.B4kVY90C.js.br +0 -0
  333. package/assets/devtools-ui/chunk.B7QJXctB.js +0 -1
  334. package/assets/devtools-ui/chunk.B7QJXctB.js.br +0 -0
  335. package/assets/devtools-ui/chunk.B9pX3zit.js.br +0 -0
  336. package/assets/devtools-ui/chunk.BKF9JxIo.js +0 -1
  337. package/assets/devtools-ui/chunk.BKF9JxIo.js.br +0 -0
  338. package/assets/devtools-ui/chunk.BOHgdTP-.js +0 -1
  339. package/assets/devtools-ui/chunk.BOHgdTP-.js.br +0 -0
  340. package/assets/devtools-ui/chunk.BOVFxkYC.js +0 -1
  341. package/assets/devtools-ui/chunk.BOVFxkYC.js.br +0 -0
  342. package/assets/devtools-ui/chunk.BR842zj5.js +0 -1
  343. package/assets/devtools-ui/chunk.BR842zj5.js.br +0 -0
  344. package/assets/devtools-ui/chunk.BT2IiBkZ.js.br +0 -0
  345. package/assets/devtools-ui/chunk.C79YouPp.js.br +0 -0
  346. package/assets/devtools-ui/chunk.C8mlBrjW.js +0 -9
  347. package/assets/devtools-ui/chunk.C8mlBrjW.js.br +0 -0
  348. package/assets/devtools-ui/chunk.CK0ow3AZ.js +0 -1
  349. package/assets/devtools-ui/chunk.CK0ow3AZ.js.br +0 -0
  350. package/assets/devtools-ui/chunk.CZl6J9DF.js.br +0 -0
  351. package/assets/devtools-ui/chunk.CdNr0YzS.js +0 -1
  352. package/assets/devtools-ui/chunk.CdNr0YzS.js.br +0 -0
  353. package/assets/devtools-ui/chunk.Ce6_6iIF.js +0 -1
  354. package/assets/devtools-ui/chunk.Ce6_6iIF.js.br +0 -0
  355. package/assets/devtools-ui/chunk.CpyDMr6O.js +0 -1
  356. package/assets/devtools-ui/chunk.CpyDMr6O.js.br +0 -0
  357. package/assets/devtools-ui/chunk.CyPmvPnY.js +0 -1
  358. package/assets/devtools-ui/chunk.CyPmvPnY.js.br +0 -0
  359. package/assets/devtools-ui/chunk.DTI_geWu.js +0 -1
  360. package/assets/devtools-ui/chunk.DTI_geWu.js.br +0 -0
  361. package/assets/devtools-ui/chunk.DbEH1oOB.js.br +0 -0
  362. package/assets/devtools-ui/chunk.Ddeqj5gv.js +0 -1
  363. package/assets/devtools-ui/chunk.Ddeqj5gv.js.br +0 -0
  364. package/assets/devtools-ui/chunk.DpRnB4vJ.js +0 -1
  365. package/assets/devtools-ui/chunk.DpRnB4vJ.js.br +0 -0
  366. package/assets/devtools-ui/chunk.DxPGTlsg.js +0 -1
  367. package/assets/devtools-ui/chunk.DxPGTlsg.js.br +0 -0
  368. package/assets/devtools-ui/chunk.G7_MMBJS.js +0 -1
  369. package/assets/devtools-ui/chunk.G7_MMBJS.js.br +0 -0
  370. package/assets/devtools-ui/chunk.M6wyKO_3.js.br +0 -0
  371. package/assets/devtools-ui/chunk.OUxNGmQ6.js +0 -1
  372. package/assets/devtools-ui/chunk.OUxNGmQ6.js.br +0 -0
  373. package/assets/devtools-ui/chunk.T1kle-fF.js +0 -1
  374. package/assets/devtools-ui/chunk.T1kle-fF.js.br +0 -0
  375. package/assets/devtools-ui/chunk.WjpsbQAv.js +0 -1
  376. package/assets/devtools-ui/chunk.WjpsbQAv.js.br +0 -0
  377. package/assets/devtools-ui/chunk.c6YgVx86.js +0 -1
  378. package/assets/devtools-ui/chunk.c6YgVx86.js.br +0 -0
  379. package/assets/devtools-ui/chunk.dwU3E_MU.js +0 -1
  380. package/assets/devtools-ui/chunk.dwU3E_MU.js.br +0 -0
  381. package/assets/devtools-ui/chunk.lJL-lgnW.js.br +0 -0
  382. package/assets/devtools-ui/chunk.lPWRmvA-.js +0 -7
  383. package/assets/devtools-ui/chunk.lPWRmvA-.js.br +0 -0
  384. package/assets/devtools-ui/chunk.p3HJvugM.js +0 -1
  385. package/assets/devtools-ui/chunk.p3HJvugM.js.br +0 -0
  386. package/assets/devtools-ui/chunk.r_Xoa_CI.js +0 -1
  387. package/assets/devtools-ui/chunk.r_Xoa_CI.js.br +0 -0
  388. package/assets/devtools-ui/chunk.sRNuTYXb.js +0 -1
  389. package/assets/devtools-ui/chunk.sRNuTYXb.js.br +0 -0
  390. package/assets/devtools-ui/chunk.tUjcyX5C.js +0 -1
  391. package/assets/devtools-ui/chunk.tUjcyX5C.js.br +0 -0
  392. package/assets/devtools-ui/chunk.thjBxvCA.js +0 -1
  393. package/assets/devtools-ui/chunk.thjBxvCA.js.br +0 -0
  394. package/assets/devtools-ui/entry.GYhBVRpC.js +0 -78
  395. package/assets/devtools-ui/entry.GYhBVRpC.js.br +0 -0
  396. package/src/server/links/services/DefinitionsPool.spec.ts +0 -86
  397. package/src/server/links/services/DefinitionsPool.ts +0 -43
@@ -1,43 +1,142 @@
1
1
  import type { Alepha } from "alepha";
2
- import type { CSSProperties } from "react";
2
+ import { type CSSProperties, useState } from "react";
3
+ import { useRouterState } from "../hooks/useRouterState.ts";
3
4
 
4
- interface ErrorViewerProps {
5
+ export interface ErrorViewerProps {
5
6
  error: Error;
6
7
  alepha: Alepha;
8
+ onRetry?: () => void;
7
9
  }
8
10
 
9
11
  const mono =
10
12
  'ui-monospace, "JetBrains Mono", SFMono-Regular, Menlo, Monaco, Consolas, monospace';
11
13
 
12
- const ErrorViewer = ({ error, alepha }: ErrorViewerProps) => {
13
- if (alepha.isProduction()) {
14
- return <div style={s.root}>Something went wrong.</div>;
14
+ const ErrorViewer = (props: ErrorViewerProps) => {
15
+ if (props.alepha.isProduction()) {
16
+ return <ProdErrorPage error={props.error} />;
15
17
  }
16
18
 
19
+ return <DevErrorPage {...props} />;
20
+ };
21
+
22
+ export default ErrorViewer;
23
+
24
+ // ----- Dev Error Page -----
25
+
26
+ const DevErrorPage = (props: ErrorViewerProps) => {
27
+ const [copied, setCopied] = useState(false);
28
+ const state = useRouterState();
29
+ const pathname = state.url.pathname;
30
+
31
+ const handleCopy = () => {
32
+ const text = buildErrorText(props.error);
33
+ if (typeof navigator !== "undefined" && navigator.clipboard) {
34
+ navigator.clipboard.writeText(text);
35
+ setCopied(true);
36
+ setTimeout(() => setCopied(false), 2000);
37
+ }
38
+ };
39
+
40
+ const status = getHttpStatus(props.error);
41
+
17
42
  return (
18
- <div style={s.root}>
19
- <ErrorBlock error={error} />
43
+ <div style={dev.overlay}>
44
+ <div style={dev.card}>
45
+ <div style={dev.header}>
46
+ <div style={dev.headerLeft}>
47
+ <div style={dev.icon}>!</div>
48
+ <div style={dev.title}>{props.error.name || "Error"}</div>
49
+ </div>
50
+ <div style={dev.headerRight}>
51
+ {props.onRetry && (
52
+ <button
53
+ type="button"
54
+ style={dev.retryBtn}
55
+ onClick={props.onRetry}
56
+ >
57
+ Retry
58
+ </button>
59
+ )}
60
+ <button type="button" style={dev.copyBtn} onClick={handleCopy}>
61
+ {copied ? "Copied" : "Copy"}
62
+ </button>
63
+ </div>
64
+ </div>
65
+
66
+ <ErrorBlock error={props.error} />
67
+
68
+ <div style={dev.meta}>
69
+ {pathname && <span style={dev.metaItem}>{pathname}</span>}
70
+ <span style={dev.metaItem}>{new Date().toLocaleTimeString()}</span>
71
+ {status && <span style={dev.statusBadge}>{status}</span>}
72
+ </div>
73
+ </div>
20
74
  </div>
21
75
  );
22
76
  };
23
77
 
24
- export default ErrorViewer;
78
+ // ----- Error Block (recursive) -----
79
+
80
+ const ErrorBlock = (props: { error: Error; depth?: number }) => {
81
+ const { error, depth = 0 } = props;
25
82
 
26
- function ErrorBlock({ error, depth = 0 }: { error: Error; depth?: number }) {
27
83
  return (
28
84
  <>
29
- {depth > 0 && <div style={s.caused}>Caused by:</div>}
30
- <div style={s.name}>
85
+ {depth > 0 && <div style={dev.causedBy}>Caused by:</div>}
86
+ <pre style={dev.messageBlock}>
31
87
  {error.name}: {error.message}
32
- </div>
33
- {error.stack && <pre style={s.stack}>{cleanStack(error.stack)}</pre>}
88
+ </pre>
89
+ {error.stack && (
90
+ <>
91
+ <div style={dev.stackLabel}>STACK TRACE</div>
92
+ <pre style={dev.stackBlock}>{cleanStack(error.stack)}</pre>
93
+ </>
94
+ )}
34
95
  {error.cause instanceof Error && (
35
96
  <ErrorBlock error={error.cause} depth={depth + 1} />
36
97
  )}
37
98
  </>
38
99
  );
100
+ };
101
+
102
+ // ----- Prod Error Page -----
103
+
104
+ interface ProdErrorPageProps {
105
+ error: Error;
39
106
  }
40
107
 
108
+ const ProdErrorPage = (props: ProdErrorPageProps) => {
109
+ const requestId = (props.error as any).requestId;
110
+
111
+ const handleReload = () => {
112
+ if (typeof window !== "undefined") {
113
+ window.location.reload();
114
+ }
115
+ };
116
+
117
+ return (
118
+ <div style={prod.page}>
119
+ <div style={prod.card}>
120
+ <div style={prod.heading}>Something went wrong</div>
121
+ <div style={prod.subtitle}>
122
+ We're having trouble processing your request.
123
+ </div>
124
+ {requestId && <div style={prod.refText}>Reference: {requestId}</div>}
125
+ <div style={prod.actions}>
126
+ <button type="button" style={prod.reloadBtn} onClick={handleReload}>
127
+ Reload page
128
+ </button>
129
+ <a href="/" style={prod.homeLink}>
130
+ Go home
131
+ </a>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ );
136
+ };
137
+
138
+ // ----- Helpers -----
139
+
41
140
  function cleanStack(stack: string): string {
42
141
  return stack
43
142
  .split("\n")
@@ -46,41 +145,241 @@ function cleanStack(stack: string): string {
46
145
  .join("\n");
47
146
  }
48
147
 
49
- const s: Record<string, CSSProperties> = {
50
- root: {
148
+ function buildErrorText(error: Error): string {
149
+ const parts: string[] = [];
150
+
151
+ const append = (err: Error, depth: number) => {
152
+ if (depth > 0) {
153
+ parts.push(`\nCaused by:`);
154
+ }
155
+ parts.push(`${err.name}: ${err.message}`);
156
+ if (err.stack) {
157
+ parts.push(cleanStack(err.stack));
158
+ }
159
+ if (err.cause instanceof Error) {
160
+ append(err.cause, depth + 1);
161
+ }
162
+ };
163
+
164
+ append(error, 0);
165
+ return parts.join("\n");
166
+ }
167
+
168
+ function getHttpStatus(error: Error): number | undefined {
169
+ if ("status" in error && typeof (error as any).status === "number") {
170
+ return (error as any).status;
171
+ }
172
+ return undefined;
173
+ }
174
+
175
+ // ----- Dev Styles -----
176
+
177
+ const dev: Record<string, CSSProperties> = {
178
+ overlay: {
179
+ position: "fixed",
180
+ inset: 0,
181
+ zIndex: 2147483647,
182
+ display: "flex",
183
+ alignItems: "center",
184
+ justifyContent: "center",
51
185
  padding: "24px",
186
+ backgroundColor: "rgba(0, 0, 0, 0.75)",
187
+ backdropFilter: "blur(4px)",
52
188
  fontFamily: mono,
53
189
  fontSize: "13px",
54
- color: "#333",
55
- backgroundColor: "#fff",
190
+ color: "#e5e7eb",
191
+ boxSizing: "border-box",
192
+ overflow: "auto",
56
193
  },
57
- name: {
58
- color: "#dc2626",
59
- fontSize: "14px",
194
+ card: {
195
+ width: "100%",
196
+ maxWidth: "900px",
197
+ maxHeight: "90vh",
198
+ overflow: "auto",
199
+ padding: "24px",
200
+ backgroundColor: "#111",
201
+ borderLeft: "4px solid #ef4444",
202
+ borderRadius: "8px",
203
+ boxShadow: "0 25px 50px rgba(0, 0, 0, 0.5)",
204
+ },
205
+ header: {
206
+ display: "flex",
207
+ alignItems: "center",
208
+ justifyContent: "space-between",
209
+ marginBottom: "20px",
210
+ flexWrap: "wrap",
211
+ gap: "12px",
212
+ },
213
+ headerLeft: {
214
+ display: "flex",
215
+ alignItems: "center",
216
+ gap: "12px",
217
+ minWidth: 0,
218
+ },
219
+ headerRight: {
220
+ display: "flex",
221
+ gap: "8px",
222
+ flexShrink: 0,
223
+ },
224
+ icon: {
225
+ width: "28px",
226
+ height: "28px",
227
+ borderRadius: "50%",
228
+ backgroundColor: "#ef4444",
229
+ color: "#fff",
230
+ display: "flex",
231
+ alignItems: "center",
232
+ justifyContent: "center",
233
+ fontWeight: 700,
234
+ fontSize: "16px",
235
+ flexShrink: 0,
236
+ },
237
+ title: {
238
+ color: "#f87171",
239
+ fontSize: "18px",
240
+ fontWeight: 700,
241
+ },
242
+ retryBtn: {
243
+ padding: "6px 16px",
244
+ backgroundColor: "#2563eb",
245
+ color: "#fff",
246
+ border: "none",
247
+ borderRadius: "6px",
248
+ cursor: "pointer",
249
+ fontFamily: mono,
250
+ fontSize: "12px",
60
251
  fontWeight: 600,
61
- marginBottom: "8px",
252
+ },
253
+ copyBtn: {
254
+ padding: "6px 16px",
255
+ backgroundColor: "transparent",
256
+ color: "#9ca3af",
257
+ border: "1px solid #374151",
258
+ borderRadius: "6px",
259
+ cursor: "pointer",
260
+ fontFamily: mono,
261
+ fontSize: "12px",
262
+ fontWeight: 600,
263
+ },
264
+ messageBlock: {
265
+ margin: "0 0 12px",
266
+ padding: "12px",
267
+ backgroundColor: "#1f2937",
268
+ borderRadius: "6px",
269
+ color: "#f9fafb",
270
+ fontSize: "13px",
271
+ lineHeight: 1.6,
272
+ whiteSpace: "pre-wrap",
62
273
  wordBreak: "break-word",
274
+ overflow: "auto",
63
275
  },
64
- stack: {
276
+ stackLabel: {
277
+ fontSize: "10px",
278
+ color: "#6b7280",
279
+ textTransform: "uppercase",
280
+ letterSpacing: "1.5px",
281
+ marginBottom: "6px",
282
+ fontWeight: 600,
283
+ },
284
+ stackBlock: {
65
285
  margin: "0 0 20px",
66
286
  padding: "12px",
67
- backgroundColor: "#f5f5f5",
68
- borderRadius: "4px",
69
- border: "1px solid #e0e0e0",
287
+ backgroundColor: "#0f172a",
288
+ borderRadius: "6px",
289
+ color: "#94a3b8",
70
290
  fontSize: "12px",
71
291
  lineHeight: 1.6,
72
- color: "#666",
73
- overflow: "auto",
74
292
  whiteSpace: "pre-wrap",
75
293
  wordBreak: "break-all",
294
+ overflow: "auto",
76
295
  },
77
- caused: {
78
- fontSize: "11px",
79
- color: "#999",
296
+ causedBy: {
297
+ fontSize: "10px",
298
+ color: "#6b7280",
80
299
  textTransform: "uppercase",
81
- letterSpacing: "1px",
82
- marginBottom: "6px",
83
- paddingTop: "4px",
84
- borderTop: "1px solid #e0e0e0",
300
+ letterSpacing: "1.5px",
301
+ marginBottom: "8px",
302
+ paddingTop: "12px",
303
+ borderTop: "1px solid #374151",
304
+ fontWeight: 600,
305
+ },
306
+ meta: {
307
+ display: "flex",
308
+ gap: "16px",
309
+ alignItems: "center",
310
+ paddingTop: "12px",
311
+ borderTop: "1px solid #1f2937",
312
+ flexWrap: "wrap",
313
+ },
314
+ metaItem: {
315
+ fontSize: "11px",
316
+ color: "#6b7280",
317
+ },
318
+ statusBadge: {
319
+ fontSize: "11px",
320
+ color: "#fbbf24",
321
+ backgroundColor: "#422006",
322
+ padding: "2px 8px",
323
+ borderRadius: "4px",
324
+ fontWeight: 600,
325
+ },
326
+ };
327
+
328
+ // ----- Prod Styles -----
329
+
330
+ const prod: Record<string, CSSProperties> = {
331
+ page: {
332
+ minHeight: "100vh",
333
+ display: "flex",
334
+ alignItems: "center",
335
+ justifyContent: "center",
336
+ backgroundColor: "#0f172a",
337
+ fontFamily:
338
+ '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
339
+ padding: "24px",
340
+ },
341
+ card: {
342
+ textAlign: "center",
343
+ maxWidth: "440px",
344
+ },
345
+ heading: {
346
+ fontSize: "28px",
347
+ fontWeight: 700,
348
+ color: "#f1f5f9",
349
+ marginBottom: "12px",
350
+ },
351
+ subtitle: {
352
+ fontSize: "16px",
353
+ color: "#94a3b8",
354
+ marginBottom: "24px",
355
+ lineHeight: 1.5,
356
+ },
357
+ refText: {
358
+ fontSize: "13px",
359
+ color: "#64748b",
360
+ marginBottom: "24px",
361
+ fontFamily: mono,
362
+ },
363
+ actions: {
364
+ display: "flex",
365
+ gap: "12px",
366
+ justifyContent: "center",
367
+ alignItems: "center",
368
+ },
369
+ reloadBtn: {
370
+ padding: "10px 24px",
371
+ backgroundColor: "#2563eb",
372
+ color: "#fff",
373
+ border: "none",
374
+ borderRadius: "8px",
375
+ cursor: "pointer",
376
+ fontSize: "14px",
377
+ fontWeight: 600,
378
+ },
379
+ homeLink: {
380
+ padding: "10px 24px",
381
+ color: "#94a3b8",
382
+ textDecoration: "none",
383
+ fontSize: "14px",
85
384
  },
86
385
  };
@@ -9,7 +9,7 @@ import ErrorViewer from "./ErrorViewer.tsx";
9
9
 
10
10
  export interface NestedViewProps {
11
11
  children?: ReactNode;
12
- errorBoundary?: false | ((error: Error) => ReactNode);
12
+ errorBoundary?: false | ((error: Error, reset: () => void) => ReactNode);
13
13
  }
14
14
 
15
15
  /**
@@ -39,6 +39,7 @@ const NestedView = (props: NestedViewProps) => {
39
39
  const onError = routerLayer?.onError;
40
40
  const state = useRouterState();
41
41
  const alepha = useAlepha();
42
+ const [boundaryKey, setBoundaryKey] = useState(0);
42
43
 
43
44
  const [view, setView] = useState<ReactNode | undefined>(
44
45
  state.layers[index]?.element,
@@ -147,7 +148,9 @@ const NestedView = (props: NestedViewProps) => {
147
148
 
148
149
  if (props.errorBoundary) {
149
150
  return (
150
- <ErrorBoundary fallback={props.errorBoundary}>{element}</ErrorBoundary>
151
+ <ErrorBoundary key={boundaryKey} fallback={props.errorBoundary}>
152
+ {element}
153
+ </ErrorBoundary>
151
154
  );
152
155
  }
153
156
 
@@ -161,7 +164,11 @@ const NestedView = (props: NestedViewProps) => {
161
164
  return result as ReactNode;
162
165
  };
163
166
 
164
- return <ErrorBoundary fallback={fallback}>{element}</ErrorBoundary>;
167
+ return (
168
+ <ErrorBoundary key={boundaryKey} fallback={fallback}>
169
+ {element}
170
+ </ErrorBoundary>
171
+ );
165
172
  };
166
173
 
167
174
  export default memo(NestedView);
@@ -167,6 +167,40 @@ describe("$page browser tests", () => {
167
167
  });
168
168
  });
169
169
 
170
+ describe("redirect", () => {
171
+ it("should redirect to another page", async () => {
172
+ class App {
173
+ home = $page({
174
+ path: "/",
175
+ redirect: "/dashboard",
176
+ });
177
+
178
+ dashboard = $page({
179
+ path: "/dashboard",
180
+ component: () => <div data-testid="dashboard">Dashboard</div>,
181
+ });
182
+ }
183
+
184
+ alepha = Alepha.create().with(AlephaReact).with(App);
185
+ await alepha.start();
186
+
187
+ const router = alepha.inject(ReactRouter);
188
+
189
+ await act(async () => {
190
+ await router.push("/");
191
+ });
192
+
193
+ await waitFor(() => {
194
+ expect(
195
+ document.querySelector('[data-testid="dashboard"]'),
196
+ ).toBeDefined();
197
+ expect(
198
+ document.querySelector('[data-testid="dashboard"]')?.textContent,
199
+ ).toBe("Dashboard");
200
+ });
201
+ });
202
+ });
203
+
170
204
  describe("errorHandler", () => {
171
205
  it("should handle errors and redirect", async () => {
172
206
  let isAuthenticated = false;
@@ -649,6 +649,26 @@ describe("$page primitive tests", () => {
649
649
  expect(rendered.html).toBe("Child received: from parent and from parent");
650
650
  });
651
651
 
652
+ test("$page - redirect shorthand", async ({ expect }) => {
653
+ class App {
654
+ home = $page({
655
+ path: "/",
656
+ redirect: "/dashboard",
657
+ });
658
+
659
+ dashboard = $page({
660
+ path: "/dashboard",
661
+ component: () => "Dashboard",
662
+ });
663
+ }
664
+
665
+ const app = alepha.inject(App);
666
+ await alepha.start();
667
+
668
+ const rendered = await app.home.render();
669
+ expect(rendered.redirect).toBe("/dashboard");
670
+ });
671
+
652
672
  test("$page - cache configuration without static", async ({ expect }) => {
653
673
  class App {
654
674
  cached = $page({
@@ -372,6 +372,30 @@ export interface PagePrimitiveOptions<
372
372
  */
373
373
  head?: Head | ((props: TProps, previous?: Head) => Head);
374
374
 
375
+ /**
376
+ * Redirect to another path when this page is matched.
377
+ *
378
+ * This is a shorthand for throwing a `Redirection` in the loader.
379
+ * The redirect is performed before any loader or component rendering.
380
+ *
381
+ * @example
382
+ * ```ts
383
+ * home = $page({
384
+ * path: "/",
385
+ * redirect: "/dashboard",
386
+ * });
387
+ * ```
388
+ */
389
+ redirect?: string;
390
+
391
+ /**
392
+ * Label for the page, used for navigation menus or breadcrumbs.
393
+ *
394
+ * This is optional and can be used by the application to display a user-friendly name for the page.
395
+ * It has no functional impact on routing or rendering.
396
+ */
397
+ label?: string;
398
+
375
399
  /**
376
400
  * Source path for SSR module preloading.
377
401
  *
@@ -5,6 +5,7 @@ import { type Route, RouterProvider } from "alepha/router";
5
5
  import { ForbiddenError } from "alepha/server";
6
6
  import { createElement, type ReactNode } from "react";
7
7
  import NotFoundPage from "../components/NotFound.tsx";
8
+ import { Redirection } from "../errors/Redirection.ts";
8
9
  import {
9
10
  isPageRoute,
10
11
  type PageRoute,
@@ -36,7 +37,7 @@ export class ReactBrowserRouterProvider extends RouterProvider<BrowserRoute> {
36
37
  handler: async () => {
37
38
  for (const page of this.pageApi.getPages()) {
38
39
  // mount only if a view is provided
39
- if (page.component || page.lazy) {
40
+ if (page.component || page.lazy || page.redirect) {
40
41
  this.push({
41
42
  path: page.match,
42
43
  page,
@@ -120,10 +121,21 @@ export class ReactBrowserRouterProvider extends RouterProvider<BrowserRoute> {
120
121
  await this.alepha.events.emit("react:transition:success", { state });
121
122
  } catch (e) {
122
123
  this.log.error("Transition has failed", e);
124
+
125
+ let element: ReactNode | undefined;
126
+ try {
127
+ const result = state.onError(e as Error, state);
128
+ if (result != null && !(result instanceof Redirection)) {
129
+ element = result as ReactNode;
130
+ }
131
+ } catch {
132
+ // error handler itself failed, fall through to default
133
+ }
134
+
123
135
  state.layers = [
124
136
  {
125
137
  name: "error",
126
- element: this.pageApi.renderError(e as Error),
138
+ element: element ?? this.pageApi.renderError(e as Error),
127
139
  index: 0,
128
140
  path: "/",
129
141
  },