@alepha/react 0.9.3 → 0.9.5

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 (39) hide show
  1. package/README.md +64 -6
  2. package/dist/index.browser.js +442 -328
  3. package/dist/index.browser.js.map +1 -1
  4. package/dist/index.cjs +644 -482
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +402 -339
  7. package/dist/index.d.cts.map +1 -1
  8. package/dist/index.d.ts +412 -349
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +641 -484
  11. package/dist/index.js.map +1 -1
  12. package/package.json +16 -11
  13. package/src/components/Link.tsx +2 -5
  14. package/src/components/NestedView.tsx +164 -19
  15. package/src/components/NotFound.tsx +1 -1
  16. package/src/descriptors/$page.ts +100 -5
  17. package/src/errors/Redirection.ts +8 -5
  18. package/src/hooks/useActive.ts +25 -35
  19. package/src/hooks/useAlepha.ts +16 -2
  20. package/src/hooks/useClient.ts +7 -4
  21. package/src/hooks/useInject.ts +4 -1
  22. package/src/hooks/useQueryParams.ts +9 -6
  23. package/src/hooks/useRouter.ts +18 -31
  24. package/src/hooks/useRouterEvents.ts +30 -22
  25. package/src/hooks/useRouterState.ts +8 -20
  26. package/src/hooks/useSchema.ts +10 -15
  27. package/src/hooks/useStore.ts +0 -7
  28. package/src/index.browser.ts +14 -11
  29. package/src/index.shared.ts +2 -3
  30. package/src/index.ts +27 -31
  31. package/src/providers/ReactBrowserProvider.ts +151 -62
  32. package/src/providers/ReactBrowserRendererProvider.ts +22 -0
  33. package/src/providers/ReactBrowserRouterProvider.ts +137 -0
  34. package/src/providers/{PageDescriptorProvider.ts → ReactPageProvider.ts} +121 -104
  35. package/src/providers/ReactServerProvider.ts +90 -76
  36. package/src/{hooks/RouterHookApi.ts → services/ReactRouter.ts} +49 -62
  37. package/src/contexts/RouterContext.ts +0 -14
  38. package/src/providers/BrowserRouterProvider.ts +0 -155
  39. package/src/providers/ReactBrowserRenderer.ts +0 -93
@@ -4,14 +4,15 @@ import {
4
4
  $env,
5
5
  $hook,
6
6
  $inject,
7
- $logger,
8
7
  Alepha,
8
+ AlephaError,
9
9
  type Static,
10
10
  t,
11
11
  } from "@alepha/core";
12
+ import { $logger } from "@alepha/logger";
12
13
  import {
13
- apiLinksResponseSchema,
14
14
  type ServerHandler,
15
+ ServerProvider,
15
16
  ServerRouterProvider,
16
17
  ServerTimingProvider,
17
18
  } from "@alepha/server";
@@ -21,16 +22,15 @@ import { renderToString } from "react-dom/server";
21
22
  import {
22
23
  $page,
23
24
  type PageDescriptorRenderOptions,
25
+ type PageDescriptorRenderResult,
24
26
  } from "../descriptors/$page.ts";
25
27
  import { Redirection } from "../errors/Redirection.ts";
28
+ import type { ReactHydrationState } from "./ReactBrowserProvider.ts";
26
29
  import {
27
- PageDescriptorProvider,
28
- type PageReactContext,
29
- type PageRequest,
30
30
  type PageRoute,
31
- type RouterState,
32
- } from "./PageDescriptorProvider.ts";
33
- import type { ReactHydrationState } from "./ReactBrowserProvider.ts";
31
+ ReactPageProvider,
32
+ type ReactRouterState,
33
+ } from "./ReactPageProvider.ts";
34
34
 
35
35
  const envSchema = t.object({
36
36
  REACT_SERVER_DIST: t.string({ default: "public" }),
@@ -54,7 +54,8 @@ declare module "@alepha/core" {
54
54
  export class ReactServerProvider {
55
55
  protected readonly log = $logger();
56
56
  protected readonly alepha = $inject(Alepha);
57
- protected readonly pageDescriptorProvider = $inject(PageDescriptorProvider);
57
+ protected readonly pageApi = $inject(ReactPageProvider);
58
+ protected readonly serverProvider = $inject(ServerProvider);
58
59
  protected readonly serverStaticProvider = $inject(ServerStaticProvider);
59
60
  protected readonly serverRouterProvider = $inject(ServerRouterProvider);
60
61
  protected readonly serverTimingProvider = $inject(ServerTimingProvider);
@@ -76,6 +77,19 @@ export class ReactServerProvider {
76
77
 
77
78
  for (const page of pages) {
78
79
  page.render = this.createRenderFunction(page.name);
80
+ page.fetch = async (options) => {
81
+ const response = await fetch(
82
+ `${this.serverProvider.hostname}/${page.pathname(options)}`,
83
+ );
84
+ const html = await response.text();
85
+ if (options?.html) return { html, response };
86
+ // take only text inside the root div
87
+ const match = html.match(this.ROOT_DIV_REGEX);
88
+ if (match) {
89
+ return { html: match[3], response };
90
+ }
91
+ throw new AlephaError("Invalid HTML response");
92
+ };
79
93
  }
80
94
 
81
95
  // development mode
@@ -136,7 +150,7 @@ export class ReactServerProvider {
136
150
  }
137
151
 
138
152
  protected async registerPages(templateLoader: TemplateLoader) {
139
- for (const page of this.pageDescriptorProvider.getPages()) {
153
+ for (const page of this.pageApi.getPages()) {
140
154
  if (page.children?.length) {
141
155
  continue;
142
156
  }
@@ -196,48 +210,60 @@ export class ReactServerProvider {
196
210
  * For testing purposes, creates a render function that can be used.
197
211
  */
198
212
  protected createRenderFunction(name: string, withIndex = false) {
199
- return async (options: PageDescriptorRenderOptions = {}) => {
200
- const page = this.pageDescriptorProvider.page(name);
201
- const url = new URL(this.pageDescriptorProvider.url(name, options));
202
- const context: PageRequest = {
213
+ return async (
214
+ options: PageDescriptorRenderOptions = {},
215
+ ): Promise<PageDescriptorRenderResult> => {
216
+ const page = this.pageApi.page(name);
217
+ const url = new URL(this.pageApi.url(name, options));
218
+
219
+ const entry: Partial<ReactRouterState> = {
203
220
  url,
204
221
  params: options.params ?? {},
205
222
  query: options.query ?? {},
206
- head: {},
207
223
  onError: () => null,
224
+ layers: [],
225
+ meta: {},
208
226
  };
209
227
 
228
+ const state = entry as ReactRouterState;
229
+
230
+ this.log.trace("Rendering", {
231
+ url,
232
+ });
233
+
210
234
  await this.alepha.emit("react:server:render:begin", {
211
- context,
235
+ state,
212
236
  });
213
237
 
214
- const state = await this.pageDescriptorProvider.createLayers(
238
+ const { redirect } = await this.pageApi.createLayers(
215
239
  page,
216
- context,
240
+ state as ReactRouterState,
217
241
  );
218
242
 
243
+ if (redirect) {
244
+ return { state, html: "", redirect };
245
+ }
246
+
219
247
  if (!withIndex && !options.html) {
248
+ this.alepha.state("react.router.state", state);
249
+
220
250
  return {
221
- context,
222
- html: renderToString(
223
- this.pageDescriptorProvider.root(state, context),
224
- ),
251
+ state,
252
+ html: renderToString(this.pageApi.root(state)),
225
253
  };
226
254
  }
227
255
 
228
256
  const html = this.renderToHtml(
229
257
  this.template ?? "",
230
258
  state,
231
- context,
232
259
  options.hydration,
233
260
  );
234
261
 
235
262
  if (html instanceof Redirection) {
236
- throw new Error("Redirection is not supported in this context");
263
+ return { state, html: "", redirect };
237
264
  }
238
265
 
239
266
  const result = {
240
- context,
241
267
  state,
242
268
  html,
243
269
  };
@@ -249,7 +275,7 @@ export class ReactServerProvider {
249
275
  }
250
276
 
251
277
  protected createHandler(
252
- page: PageRoute,
278
+ route: PageRoute,
253
279
  templateLoader: TemplateLoader,
254
280
  ): ServerHandler {
255
281
  return async (serverRequest) => {
@@ -260,36 +286,32 @@ export class ReactServerProvider {
260
286
  }
261
287
 
262
288
  this.log.trace("Rendering page", {
263
- name: page.name,
289
+ name: route.name,
264
290
  });
265
291
 
266
- const context: PageRequest = {
292
+ const entry: Partial<ReactRouterState> = {
267
293
  url,
268
294
  params,
269
295
  query,
270
- // plugins
271
- head: {},
272
296
  onError: () => null,
297
+ layers: [],
273
298
  };
274
299
 
275
- if (this.alepha.has(ServerLinksProvider)) {
276
- const srv = this.alepha.inject(ServerLinksProvider);
277
- const schema = apiLinksResponseSchema as any;
300
+ const state = entry as ReactRouterState;
278
301
 
279
- context.links = this.alepha.parse(
280
- schema,
281
- await srv.getLinks({
282
- user: serverRequest.user,
302
+ if (this.alepha.has(ServerLinksProvider)) {
303
+ this.alepha.state(
304
+ "api",
305
+ await this.alepha.inject(ServerLinksProvider).getUserApiLinks({
306
+ user: (serverRequest as any).user, // TODO: fix type
283
307
  authorization: serverRequest.headers.authorization,
284
308
  }),
285
- ) as any;
286
-
287
- this.alepha.context.set("links", context.links);
309
+ );
288
310
  }
289
311
 
290
- let target: PageRoute | undefined = page; // TODO: move to PageDescriptorProvider
312
+ let target: PageRoute | undefined = route; // TODO: move to PageDescriptorProvider
291
313
  while (target) {
292
- if (page.can && !page.can()) {
314
+ if (route.can && !route.can()) {
293
315
  // if the page is not accessible, return 403
294
316
  reply.status = 403;
295
317
  reply.headers["content-type"] = "text/plain";
@@ -309,27 +331,19 @@ export class ReactServerProvider {
309
331
  // return;
310
332
  // }
311
333
 
312
- await this.alepha.emit("react:transition:begin", {
313
- request: serverRequest,
314
- context,
315
- });
316
-
317
334
  await this.alepha.emit("react:server:render:begin", {
318
335
  request: serverRequest,
319
- context,
336
+ state,
320
337
  });
321
338
 
322
339
  this.serverTimingProvider.beginTiming("createLayers");
323
340
 
324
- const state = await this.pageDescriptorProvider.createLayers(
325
- page,
326
- context,
327
- );
341
+ const { redirect } = await this.pageApi.createLayers(route, state);
328
342
 
329
343
  this.serverTimingProvider.endTiming("createLayers");
330
344
 
331
- if (state.redirect) {
332
- return reply.redirect(state.redirect);
345
+ if (redirect) {
346
+ return reply.redirect(redirect);
333
347
  }
334
348
 
335
349
  reply.headers["content-type"] = "text/html";
@@ -341,34 +355,28 @@ export class ReactServerProvider {
341
355
  reply.headers.pragma = "no-cache";
342
356
  reply.headers.expires = "0";
343
357
 
344
- // don't cache user links
345
- if (page.cache && serverRequest.user) {
346
- delete context.links;
347
- }
348
-
349
- const html = this.renderToHtml(template, state, context);
358
+ const html = this.renderToHtml(template, state);
350
359
  if (html instanceof Redirection) {
351
360
  reply.redirect(
352
- typeof html.page === "string"
353
- ? html.page
354
- : this.pageDescriptorProvider.href(html.page),
361
+ typeof html.redirect === "string"
362
+ ? html.redirect
363
+ : this.pageApi.href(html.redirect),
355
364
  );
356
365
  return;
357
366
  }
358
367
 
359
368
  const event = {
360
369
  request: serverRequest,
361
- context,
362
370
  state,
363
371
  html,
364
372
  };
365
373
 
366
374
  await this.alepha.emit("react:server:render:end", event);
367
375
 
368
- page.onServerResponse?.(serverRequest);
376
+ route.onServerResponse?.(serverRequest);
369
377
 
370
378
  this.log.trace("Page rendered", {
371
- name: page.name,
379
+ name: route.name,
372
380
  });
373
381
 
374
382
  return event.html;
@@ -377,28 +385,32 @@ export class ReactServerProvider {
377
385
 
378
386
  public renderToHtml(
379
387
  template: string,
380
- state: RouterState,
381
- context: PageReactContext,
388
+ state: ReactRouterState,
382
389
  hydration = true,
383
390
  ): string | Redirection {
384
- const element = this.pageDescriptorProvider.root(state, context);
391
+ const element = this.pageApi.root(state);
385
392
 
386
- this.serverTimingProvider.beginTiming("renderToString");
393
+ // attach react router state to the http request context
394
+ this.alepha.state("react.router.state", state);
387
395
 
396
+ this.serverTimingProvider.beginTiming("renderToString");
388
397
  let app = "";
389
398
  try {
390
399
  app = renderToString(element);
391
400
  } catch (error) {
392
- this.log.error("Error during SSR", error);
393
- const element = context.onError(error as Error, context);
401
+ this.log.error(
402
+ "renderToString has failed, fallback to error handler",
403
+ error,
404
+ );
405
+ const element = state.onError(error as Error, state);
394
406
  if (element instanceof Redirection) {
395
407
  // if the error is a redirection, return the redirection URL
396
408
  return element;
397
409
  }
398
410
 
399
411
  app = renderToString(element);
412
+ this.log.debug("Error handled successfully with fallback");
400
413
  }
401
-
402
414
  this.serverTimingProvider.endTiming("renderToString");
403
415
 
404
416
  const response = {
@@ -406,11 +418,13 @@ export class ReactServerProvider {
406
418
  };
407
419
 
408
420
  if (hydration) {
409
- const { request, context, ...rest } =
410
- this.alepha.context.als?.getStore() ?? {};
421
+ const { request, context, ...store } =
422
+ this.alepha.context.als?.getStore() ?? {}; /// TODO: als must be protected, find a way to iterate on alepha.state
411
423
 
412
424
  const hydrationData: ReactHydrationState = {
413
- ...rest,
425
+ ...store,
426
+ // map react.router.state to the hydration state
427
+ "react.router.state": undefined,
414
428
  layers: state.layers.map((it) => ({
415
429
  ...it,
416
430
  error: it.error
@@ -1,38 +1,45 @@
1
+ import { $inject, Alepha } from "@alepha/core";
1
2
  import type { PageDescriptor } from "../descriptors/$page.ts";
2
- import type {
3
- AnchorProps,
4
- PageDescriptorProvider,
5
- PageReactContext,
6
- PageRoute,
7
- RouterState,
8
- } from "../providers/PageDescriptorProvider.ts";
9
- import type {
3
+ import {
10
4
  ReactBrowserProvider,
11
- RouterGoOptions,
5
+ type RouterGoOptions,
12
6
  } from "../providers/ReactBrowserProvider.ts";
7
+ import {
8
+ type AnchorProps,
9
+ ReactPageProvider,
10
+ type ReactRouterState,
11
+ } from "../providers/ReactPageProvider.ts";
12
+
13
+ export class ReactRouter<T extends object> {
14
+ protected readonly alepha = $inject(Alepha);
15
+ protected readonly pageApi = $inject(ReactPageProvider);
16
+
17
+ public get state(): ReactRouterState {
18
+ return this.alepha.state("react.router.state")!;
19
+ }
13
20
 
14
- export class RouterHookApi<T extends object> {
15
- constructor(
16
- private readonly pages: PageRoute[],
17
- private readonly context: PageReactContext,
18
- private readonly state: RouterState,
19
- private readonly layer: {
20
- path: string;
21
- },
22
- private readonly pageApi: PageDescriptorProvider,
23
- private readonly browser?: ReactBrowserProvider,
24
- ) {}
21
+ public get pages() {
22
+ return this.pageApi.getPages();
23
+ }
24
+
25
+ public get browser(): ReactBrowserProvider | undefined {
26
+ if (this.alepha.isBrowser()) {
27
+ return this.alepha.inject(ReactBrowserProvider);
28
+ }
29
+ // server-side
30
+ return undefined;
31
+ }
25
32
 
26
33
  public path(
27
34
  name: keyof VirtualRouter<T>,
28
35
  config: {
29
- params?: Record<string, string>;
30
- query?: Record<string, string>;
36
+ params?: Record<string, any>;
37
+ query?: Record<string, any>;
31
38
  } = {},
32
39
  ): string {
33
40
  return this.pageApi.pathname(name as string, {
34
41
  params: {
35
- ...this.context.params,
42
+ ...this.state.params,
36
43
  ...config.params,
37
44
  },
38
45
  query: config.query,
@@ -41,8 +48,9 @@ export class RouterHookApi<T extends object> {
41
48
 
42
49
  public getURL(): URL {
43
50
  if (!this.browser) {
44
- return this.context.url;
51
+ return this.state.url;
45
52
  }
53
+
46
54
  return new URL(this.location.href);
47
55
  }
48
56
 
@@ -54,19 +62,19 @@ export class RouterHookApi<T extends object> {
54
62
  return this.browser.location;
55
63
  }
56
64
 
57
- public get current(): RouterState {
65
+ public get current(): ReactRouterState {
58
66
  return this.state;
59
67
  }
60
68
 
61
69
  public get pathname(): string {
62
- return this.state.pathname;
70
+ return this.state.url.pathname;
63
71
  }
64
72
 
65
73
  public get query(): Record<string, string> {
66
74
  const query: Record<string, string> = {};
67
75
 
68
76
  for (const [key, value] of new URLSearchParams(
69
- this.state.search,
77
+ this.state.url.search,
70
78
  ).entries()) {
71
79
  query[key] = String(value);
72
80
  }
@@ -86,32 +94,6 @@ export class RouterHookApi<T extends object> {
86
94
  await this.browser?.invalidate(props);
87
95
  }
88
96
 
89
- /**
90
- * Create a valid href for the given pathname.
91
- *
92
- * @param pathname
93
- * @param layer
94
- */
95
- public createHref(
96
- pathname: HrefLike,
97
- layer: { path: string } = this.layer,
98
- options: { params?: Record<string, any> } = {},
99
- ) {
100
- if (typeof pathname === "object") {
101
- pathname = pathname.options.path ?? "";
102
- }
103
-
104
- if (options.params) {
105
- for (const [key, value] of Object.entries(options.params)) {
106
- pathname = pathname.replace(`:${key}`, String(value));
107
- }
108
- }
109
-
110
- return pathname.startsWith("/")
111
- ? pathname
112
- : `${layer.path}/${pathname}`.replace(/\/\/+/g, "/");
113
- }
114
-
115
97
  public async go(path: string, options?: RouterGoOptions): Promise<void>;
116
98
  public async go(
117
99
  path: keyof VirtualRouter<T>,
@@ -134,19 +116,17 @@ export class RouterHookApi<T extends object> {
134
116
  await this.browser?.go(path as string, options);
135
117
  }
136
118
 
137
- public anchor(
138
- path: string,
139
- options?: { params?: Record<string, any> },
140
- ): AnchorProps;
119
+ public anchor(path: string, options?: RouterGoOptions): AnchorProps;
141
120
  public anchor(
142
121
  path: keyof VirtualRouter<T>,
143
- options?: { params?: Record<string, any> },
122
+ options?: RouterGoOptions,
144
123
  ): AnchorProps;
145
124
  public anchor(
146
125
  path: string | keyof VirtualRouter<T>,
147
- options: { params?: Record<string, any> } = {},
126
+ options: RouterGoOptions = {},
148
127
  ): AnchorProps {
149
128
  let href = path as string;
129
+
150
130
  for (const page of this.pages) {
151
131
  if (page.name === path) {
152
132
  href = this.path(path as keyof VirtualRouter<T>, options);
@@ -155,7 +135,7 @@ export class RouterHookApi<T extends object> {
155
135
  }
156
136
 
157
137
  return {
158
- href,
138
+ href: this.base(href),
159
139
  onClick: (ev: any) => {
160
140
  ev.stopPropagation();
161
141
  ev.preventDefault();
@@ -165,6 +145,15 @@ export class RouterHookApi<T extends object> {
165
145
  };
166
146
  }
167
147
 
148
+ public base(path: string): string {
149
+ const base = import.meta.env?.BASE_URL;
150
+ if (!base || base === "/") {
151
+ return path;
152
+ }
153
+
154
+ return base + path;
155
+ }
156
+
168
157
  /**
169
158
  * Set query params.
170
159
  *
@@ -194,8 +183,6 @@ export class RouterHookApi<T extends object> {
194
183
  }
195
184
  }
196
185
 
197
- export type HrefLike = string | { options: { path?: string; name?: string } };
198
-
199
186
  export type VirtualRouter<T> = {
200
187
  [K in keyof T as T[K] extends PageDescriptor ? K : never]: T[K];
201
188
  };
@@ -1,14 +0,0 @@
1
- import { createContext } from "react";
2
- import type {
3
- PageReactContext,
4
- RouterState,
5
- } from "../providers/PageDescriptorProvider.ts";
6
-
7
- export interface RouterContextValue {
8
- state: RouterState;
9
- context: PageReactContext;
10
- }
11
-
12
- export const RouterContext = createContext<RouterContextValue | undefined>(
13
- undefined,
14
- );
@@ -1,155 +0,0 @@
1
- import { $hook, $inject, $logger, Alepha } from "@alepha/core";
2
- import { type Route, RouterProvider } from "@alepha/router";
3
- import { createElement, type ReactNode } from "react";
4
- import NotFoundPage from "../components/NotFound.tsx";
5
- import {
6
- isPageRoute,
7
- PageDescriptorProvider,
8
- type PageReactContext,
9
- type PageRequest,
10
- type PageRoute,
11
- type PageRouteEntry,
12
- type RouterRenderResult,
13
- type RouterState,
14
- type TransitionOptions,
15
- } from "./PageDescriptorProvider.ts";
16
-
17
- export interface BrowserRoute extends Route {
18
- page: PageRoute;
19
- }
20
-
21
- export class BrowserRouterProvider extends RouterProvider<BrowserRoute> {
22
- protected readonly log = $logger();
23
- protected readonly alepha = $inject(Alepha);
24
- protected readonly pageDescriptorProvider = $inject(PageDescriptorProvider);
25
-
26
- public add(entry: PageRouteEntry) {
27
- this.pageDescriptorProvider.add(entry);
28
- }
29
-
30
- protected readonly configure = $hook({
31
- on: "configure",
32
- handler: async () => {
33
- for (const page of this.pageDescriptorProvider.getPages()) {
34
- // mount only if a view is provided
35
- if (page.component || page.lazy) {
36
- this.push({
37
- path: page.match,
38
- page,
39
- });
40
- }
41
- }
42
- },
43
- });
44
-
45
- public async transition(
46
- url: URL,
47
- options: TransitionOptions = {},
48
- ): Promise<RouterRenderResult> {
49
- const { pathname, search } = url;
50
- const state: RouterState = {
51
- pathname,
52
- search,
53
- layers: [],
54
- };
55
-
56
- const context = {
57
- url,
58
- query: {},
59
- params: {},
60
- onError: () => null,
61
- ...(options.context ?? {}),
62
- } as PageRequest;
63
-
64
- await this.alepha.emit("react:transition:begin", { state, context });
65
-
66
- try {
67
- const previous = options.previous;
68
- const { route, params } = this.match(pathname);
69
-
70
- const query: Record<string, string> = {};
71
- if (search) {
72
- for (const [key, value] of new URLSearchParams(search).entries()) {
73
- query[key] = String(value);
74
- }
75
- }
76
-
77
- context.query = query;
78
- context.params = params ?? {};
79
- context.previous = previous;
80
-
81
- if (isPageRoute(route)) {
82
- const result = await this.pageDescriptorProvider.createLayers(
83
- route.page,
84
- context,
85
- );
86
-
87
- if (result.redirect) {
88
- return {
89
- redirect: result.redirect,
90
- state,
91
- context,
92
- };
93
- }
94
-
95
- state.layers = result.layers;
96
- }
97
-
98
- if (state.layers.length === 0) {
99
- state.layers.push({
100
- name: "not-found",
101
- element: createElement(NotFoundPage),
102
- index: 0,
103
- path: "/",
104
- });
105
- }
106
-
107
- await this.alepha.emit("react:transition:success", { state, context });
108
- } catch (e) {
109
- this.log.error(e);
110
- state.layers = [
111
- {
112
- name: "error",
113
- element: this.pageDescriptorProvider.renderError(e as Error),
114
- index: 0,
115
- path: "/",
116
- },
117
- ];
118
-
119
- await this.alepha.emit("react:transition:error", {
120
- error: e as Error,
121
- state,
122
- context,
123
- });
124
- }
125
-
126
- if (options.state) {
127
- options.state.layers = state.layers;
128
- options.state.pathname = state.pathname;
129
- options.state.search = state.search;
130
- }
131
-
132
- if (options.previous) {
133
- for (let i = 0; i < options.previous.length; i++) {
134
- const layer = options.previous[i];
135
- if (state.layers[i]?.name !== layer.name) {
136
- this.pageDescriptorProvider.page(layer.name)?.onLeave?.();
137
- }
138
- }
139
- }
140
-
141
- await this.alepha.emit("react:transition:end", {
142
- state: options.state,
143
- context,
144
- });
145
-
146
- return {
147
- context,
148
- state,
149
- };
150
- }
151
-
152
- public root(state: RouterState, context: PageReactContext): ReactNode {
153
- return this.pageDescriptorProvider.root(state, context);
154
- }
155
- }