@alepha/react 0.7.0 → 0.7.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 (37) hide show
  1. package/README.md +1 -1
  2. package/dist/index.browser.cjs +59 -22
  3. package/dist/index.browser.js +42 -6
  4. package/dist/index.cjs +152 -85
  5. package/dist/index.d.ts +317 -206
  6. package/dist/index.js +130 -64
  7. package/dist/{useActive-DjpZBEuB.cjs → useRouterState-C2uo0jXu.cjs} +319 -140
  8. package/dist/{useActive-BX41CqY8.js → useRouterState-D5__ZcUV.js} +321 -143
  9. package/package.json +11 -10
  10. package/src/components/ClientOnly.tsx +35 -0
  11. package/src/components/ErrorBoundary.tsx +1 -1
  12. package/src/components/ErrorViewer.tsx +161 -0
  13. package/src/components/Link.tsx +9 -3
  14. package/src/components/NestedView.tsx +18 -3
  15. package/src/components/NotFound.tsx +30 -0
  16. package/src/descriptors/$page.ts +141 -30
  17. package/src/errors/RedirectionError.ts +4 -1
  18. package/src/hooks/RouterHookApi.ts +42 -24
  19. package/src/hooks/useAlepha.ts +12 -0
  20. package/src/hooks/useClient.ts +8 -6
  21. package/src/hooks/useInject.ts +2 -2
  22. package/src/hooks/useQueryParams.ts +1 -1
  23. package/src/hooks/useRouter.ts +6 -0
  24. package/src/index.browser.ts +4 -2
  25. package/src/index.shared.ts +11 -5
  26. package/src/index.ts +3 -4
  27. package/src/providers/BrowserRouterProvider.ts +4 -3
  28. package/src/providers/PageDescriptorProvider.ts +91 -20
  29. package/src/providers/ReactBrowserProvider.ts +6 -59
  30. package/src/providers/ReactBrowserRenderer.ts +72 -0
  31. package/src/providers/ReactServerProvider.ts +200 -81
  32. package/dist/index.browser.cjs.map +0 -1
  33. package/dist/index.browser.js.map +0 -1
  34. package/dist/index.cjs.map +0 -1
  35. package/dist/index.js.map +0 -1
  36. package/dist/useActive-BX41CqY8.js.map +0 -1
  37. package/dist/useActive-DjpZBEuB.cjs.map +0 -1
@@ -0,0 +1,72 @@
1
+ import { $hook, $inject, $logger, type Static, t } from "@alepha/core";
2
+ import type { ApiLinksResponse } from "@alepha/server";
3
+ import type { Root } from "react-dom/client";
4
+ import { createRoot, hydrateRoot } from "react-dom/client";
5
+ import { BrowserRouterProvider } from "./BrowserRouterProvider.ts";
6
+ import type {
7
+ PreviousLayerData,
8
+ TransitionOptions,
9
+ } from "./PageDescriptorProvider.ts";
10
+ import { ReactBrowserProvider } from "./ReactBrowserProvider.ts";
11
+
12
+ const envSchema = t.object({
13
+ REACT_ROOT_ID: t.string({ default: "root" }),
14
+ });
15
+
16
+ declare module "@alepha/core" {
17
+ interface Env extends Partial<Static<typeof envSchema>> {}
18
+ }
19
+
20
+ export class ReactBrowserRenderer {
21
+ protected readonly browserProvider = $inject(ReactBrowserProvider);
22
+ protected readonly browserRouterProvider = $inject(BrowserRouterProvider);
23
+ protected readonly env = $inject(envSchema);
24
+ protected readonly log = $logger();
25
+
26
+ protected root!: Root;
27
+
28
+ protected getRootElement() {
29
+ const root = this.browserProvider.document.getElementById(
30
+ this.env.REACT_ROOT_ID,
31
+ );
32
+ if (root) {
33
+ return root;
34
+ }
35
+
36
+ const div = this.browserProvider.document.createElement("div");
37
+ div.id = this.env.REACT_ROOT_ID;
38
+
39
+ this.browserProvider.document.body.prepend(div);
40
+
41
+ return div;
42
+ }
43
+
44
+ public readonly ready = $hook({
45
+ name: "react:browser:render",
46
+ handler: async ({ state, context, hydration }) => {
47
+ const element = this.browserRouterProvider.root(state, context);
48
+
49
+ if (hydration?.layers) {
50
+ this.root = hydrateRoot(this.getRootElement(), element);
51
+ this.log.info("Hydrated root element");
52
+ } else {
53
+ this.root ??= createRoot(this.getRootElement());
54
+ this.root.render(element);
55
+ this.log.info("Created root element");
56
+ }
57
+ },
58
+ });
59
+ }
60
+
61
+ // ---------------------------------------------------------------------------------------------------------------------
62
+
63
+ export interface RouterGoOptions {
64
+ replace?: boolean;
65
+ match?: TransitionOptions;
66
+ params?: Record<string, string>;
67
+ }
68
+
69
+ export interface ReactHydrationState {
70
+ layers?: Array<PreviousLayerData>;
71
+ links?: ApiLinksResponse;
72
+ }
@@ -1,5 +1,4 @@
1
1
  import { existsSync } from "node:fs";
2
- import { readFile } from "node:fs/promises";
3
2
  import { join } from "node:path";
4
3
  import {
5
4
  $hook,
@@ -11,24 +10,29 @@ import {
11
10
  t,
12
11
  } from "@alepha/core";
13
12
  import {
13
+ apiLinksResponseSchema,
14
14
  type ServerHandler,
15
15
  ServerLinksProvider,
16
16
  ServerRouterProvider,
17
+ ServerTimingProvider,
17
18
  } from "@alepha/server";
18
19
  import { ServerStaticProvider } from "@alepha/server-static";
19
20
  import { renderToString } from "react-dom/server";
20
21
  import { $page } from "../descriptors/$page.ts";
21
22
  import {
22
23
  PageDescriptorProvider,
24
+ type PageReactContext,
23
25
  type PageRequest,
24
26
  type PageRoute,
27
+ type RouterState,
25
28
  } from "./PageDescriptorProvider.ts";
29
+ import type { ReactHydrationState } from "./ReactBrowserProvider.ts";
26
30
  import { ServerHeadProvider } from "./ServerHeadProvider.ts";
27
31
 
28
32
  export const envSchema = t.object({
29
- REACT_SERVER_DIST: t.string({ default: "client" }),
33
+ REACT_SERVER_DIST: t.string({ default: "public" }),
30
34
  REACT_SERVER_PREFIX: t.string({ default: "" }),
31
- REACT_SSR_ENABLED: t.boolean({ default: false }),
35
+ REACT_SSR_ENABLED: t.optional(t.boolean()),
32
36
  REACT_ROOT_ID: t.string({ default: "root" }),
33
37
  });
34
38
 
@@ -47,61 +51,98 @@ export class ReactServerProvider {
47
51
  protected readonly serverStaticProvider = $inject(ServerStaticProvider);
48
52
  protected readonly serverRouterProvider = $inject(ServerRouterProvider);
49
53
  protected readonly headProvider = $inject(ServerHeadProvider);
54
+ protected readonly serverTimingProvider = $inject(ServerTimingProvider);
50
55
  protected readonly env = $inject(envSchema);
51
56
  protected readonly ROOT_DIV_REGEX = new RegExp(
52
57
  `<div([^>]*)\\s+id=["']${this.env.REACT_ROOT_ID}["']([^>]*)>(.*?)<\\/div>`,
53
58
  "is",
54
59
  );
55
60
 
56
- protected readonly configure = $hook({
61
+ public readonly onConfigure = $hook({
57
62
  name: "configure",
58
63
  handler: async () => {
59
64
  const pages = this.alepha.getDescriptorValues($page);
60
- if (pages.length === 0) {
61
- return;
62
- }
65
+
66
+ const ssrEnabled =
67
+ pages.length > 0 && this.env.REACT_SSR_ENABLED !== false;
68
+
69
+ this.alepha.state("ReactServerProvider.ssr", ssrEnabled);
63
70
 
64
71
  for (const { key, instance, value } of pages) {
65
72
  const name = value[OPTIONS].name ?? key;
66
73
 
74
+ instance[key].prerender = this.createRenderFunction(name, true);
75
+
67
76
  if (this.alepha.isTest()) {
68
77
  instance[key].render = this.createRenderFunction(name);
69
78
  }
70
79
  }
71
80
 
81
+ // development mode
72
82
  if (this.alepha.isServerless() === "vite") {
73
- await this.configureVite();
83
+ await this.configureVite(ssrEnabled);
74
84
  return;
75
85
  }
76
86
 
87
+ // production mode
77
88
  let root = "";
89
+
90
+ // non-serverless mode only -> serve static files
78
91
  if (!this.alepha.isServerless()) {
79
92
  root = this.getPublicDirectory();
80
-
81
93
  if (!root) {
82
- this.log.warn("Missing static files, SSR will be disabled");
83
- return;
94
+ this.log.warn(
95
+ "Missing static files, static file server will be disabled",
96
+ );
97
+ } else {
98
+ this.log.debug(`Using static files from: ${root}`);
99
+ await this.configureStaticServer(root);
84
100
  }
85
-
86
- await this.configureStaticServer(root);
87
101
  }
88
102
 
89
- const template =
90
- this.alepha.state("ReactServerProvider.template") ??
91
- (await readFile(join(root, "index.html"), "utf-8"));
92
-
93
- await this.registerPages(async () => template);
103
+ if (ssrEnabled) {
104
+ await this.registerPages(async () => this.template);
105
+ this.log.info("SSR OK");
106
+ return;
107
+ }
94
108
 
95
- this.alepha.state("ReactServerProvider.ssr", true);
109
+ // no SSR enabled, serve index.html for all unmatched routes
110
+ this.log.info("SSR is disabled, use History API fallback");
111
+ await this.serverRouterProvider.route({
112
+ path: "*",
113
+ handler: async ({ url, reply }) => {
114
+ if (url.pathname.includes(".")) {
115
+ // If the request is for a file (e.g., /style.css), do not fallback
116
+ reply.headers["content-type"] = "text/plain";
117
+ reply.body = "Not Found";
118
+ reply.status = 404;
119
+ return;
120
+ }
121
+
122
+ reply.headers["content-type"] = "text/html";
123
+
124
+ // serve index.html for all unmatched routes
125
+ return this.template;
126
+ },
127
+ });
96
128
  },
97
129
  });
98
130
 
99
- protected async registerPages(
100
- templateLoader: () => Promise<string | undefined>,
101
- ) {
131
+ public get template() {
132
+ return this.alepha.state("ReactServerProvider.template");
133
+ }
134
+
135
+ protected async registerPages(templateLoader: TemplateLoader) {
102
136
  for (const page of this.pageDescriptorProvider.getPages()) {
137
+ if (page.children?.length) {
138
+ continue;
139
+ }
140
+
103
141
  this.log.debug(`+ ${page.match} -> ${page.name}`);
142
+
104
143
  await this.serverRouterProvider.route({
144
+ ...page,
145
+ schema: undefined, // schema is handled by the page descriptor provider for now (shared by browser and server)
105
146
  method: "GET",
106
147
  path: page.match,
107
148
  handler: this.createHandler(page, templateLoader),
@@ -111,8 +152,8 @@ export class ReactServerProvider {
111
152
 
112
153
  protected getPublicDirectory(): string {
113
154
  const maybe = [
155
+ join(process.cwd(), `dist/${this.env.REACT_SERVER_DIST}`),
114
156
  join(process.cwd(), this.env.REACT_SERVER_DIST),
115
- join(process.cwd(), "..", this.env.REACT_SERVER_DIST),
116
157
  ];
117
158
 
118
159
  for (const it of maybe) {
@@ -131,14 +172,18 @@ export class ReactServerProvider {
131
172
  });
132
173
  }
133
174
 
134
- protected async configureVite() {
135
- const url = `http://${process.env.SERVER_HOST}:${process.env.SERVER_PORT}`;
175
+ protected async configureVite(ssrEnabled: boolean) {
176
+ if (!ssrEnabled) {
177
+ // do nothing, vite will handle everything for us
178
+ return;
179
+ }
180
+
136
181
  this.log.info("SSR (vite) OK");
137
- this.alepha.state("ReactServerProvider.ssr", true);
138
- const templateUrl = `${url}/index.html`;
182
+
183
+ const url = `http://${process.env.SERVER_HOST}:${process.env.SERVER_PORT}`;
139
184
 
140
185
  await this.registerPages(() =>
141
- fetch(templateUrl)
186
+ fetch(`${url}/index.html`)
142
187
  .then((it) => it.text())
143
188
  .catch(() => undefined),
144
189
  );
@@ -147,7 +192,7 @@ export class ReactServerProvider {
147
192
  /**
148
193
  * For testing purposes, creates a render function that can be used.
149
194
  */
150
- protected createRenderFunction(name: string) {
195
+ protected createRenderFunction(name: string, withIndex = false) {
151
196
  return async (
152
197
  options: {
153
198
  params?: Record<string, string>;
@@ -155,27 +200,39 @@ export class ReactServerProvider {
155
200
  } = {},
156
201
  ) => {
157
202
  const page = this.pageDescriptorProvider.page(name);
203
+ const url = new URL(this.pageDescriptorProvider.url(name, options));
158
204
  const context: PageRequest = {
159
- url: new URL("http://localhost"),
205
+ url,
160
206
  params: options.params ?? {},
161
207
  query: options.query ?? {},
162
208
  head: {},
163
209
  onError: () => null,
164
210
  };
165
211
 
166
- // for testing
167
212
  const state = await this.pageDescriptorProvider.createLayers(
168
213
  page,
169
214
  context,
170
215
  );
171
216
 
172
- return renderToString(this.pageDescriptorProvider.root(state, context));
217
+ if (!withIndex) {
218
+ return {
219
+ context,
220
+ html: renderToString(
221
+ this.pageDescriptorProvider.root(state, context),
222
+ ),
223
+ };
224
+ }
225
+
226
+ return {
227
+ context,
228
+ html: this.renderToHtml(this.template ?? "", state, context),
229
+ };
173
230
  };
174
231
  }
175
232
 
176
233
  protected createHandler(
177
234
  page: PageRoute,
178
- templateLoader: () => Promise<string | undefined>,
235
+ templateLoader: TemplateLoader,
179
236
  ): ServerHandler {
180
237
  return async (serverRequest) => {
181
238
  const { url, reply, query, params } = serverRequest;
@@ -193,18 +250,43 @@ export class ReactServerProvider {
193
250
  onError: () => null,
194
251
  };
195
252
 
196
- // -- links
197
253
  if (this.alepha.has(ServerLinksProvider)) {
198
254
  const srv = this.alepha.get(ServerLinksProvider);
255
+ const schema = apiLinksResponseSchema as any;
199
256
 
200
- context.links = await srv.getLinks({
201
- user: serverRequest.user,
202
- authorization: serverRequest.headers.authorization,
203
- });
257
+ context.links = this.alepha.parse(
258
+ schema,
259
+ await srv.getLinks({
260
+ user: serverRequest.user,
261
+ authorization: serverRequest.headers.authorization,
262
+ }),
263
+ ) as any;
204
264
 
205
- this.alepha.als.set("links", context.links);
265
+ this.alepha.context.set("links", context.links);
206
266
  }
207
267
 
268
+ let target: PageRoute | undefined = page; // TODO: move to PageDescriptorProvider
269
+ while (target) {
270
+ if (page.can && !page.can()) {
271
+ // if the page is not accessible, return 403
272
+ reply.status = 403;
273
+ reply.headers["content-type"] = "text/plain";
274
+ return "Forbidden";
275
+ }
276
+ target = target.parent;
277
+ }
278
+
279
+ // TODO: SSR strategies
280
+ // - only when googlebot
281
+ // - only child pages
282
+ // if (page.client) {
283
+ // // if the page is a client-only page, return 404
284
+ // reply.status = 200;
285
+ // reply.headers["content-type"] = "text/html";
286
+ // reply.body = template;
287
+ // return;
288
+ // }
289
+
208
290
  await this.alepha.emit(
209
291
  "react:server:render",
210
292
  {
@@ -216,69 +298,104 @@ export class ReactServerProvider {
216
298
  },
217
299
  );
218
300
 
301
+ this.serverTimingProvider.beginTiming("createLayers");
302
+
219
303
  const state = await this.pageDescriptorProvider.createLayers(
220
304
  page,
221
305
  context,
222
306
  );
223
307
 
308
+ this.serverTimingProvider.endTiming("createLayers");
309
+
224
310
  if (state.redirect) {
225
311
  return reply.redirect(state.redirect);
226
312
  }
227
313
 
228
- const element = this.pageDescriptorProvider.root(state, context);
229
- const app = renderToString(element);
230
-
231
- const hydrationData = {
232
- links: context.links,
233
- layers: state.layers.map((it) => ({
234
- ...it,
235
- error: it.error
236
- ? {
237
- ...it.error,
238
- name: it.error.name,
239
- message: it.error.message,
240
- stack: it.error.stack, // TODO: Hide stack in production ?
241
- }
242
- : undefined,
243
- index: undefined,
244
- path: undefined,
245
- element: undefined,
246
- })),
247
- };
248
-
249
- // create hydration data
250
- const script = `<script>window.__ssr=${JSON.stringify(hydrationData)}</script>`;
251
-
252
- const response = {
253
- html: template,
254
- };
255
-
256
- reply.status = 200;
257
314
  reply.headers["content-type"] = "text/html";
315
+
316
+ // by default, disable caching for SSR responses
317
+ // some plugins may override this
258
318
  reply.headers["cache-control"] =
259
319
  "no-store, no-cache, must-revalidate, proxy-revalidate";
260
320
  reply.headers.pragma = "no-cache";
261
321
  reply.headers.expires = "0";
262
322
 
263
- // inject app into template
264
- this.fillTemplate(response, app, script);
265
-
266
- // inject head meta
267
- if (context.head) {
268
- response.html = this.headProvider.renderHead(
269
- response.html,
270
- context.head,
271
- );
323
+ // don't cache user links
324
+ if (page.cache && serverRequest.user) {
325
+ delete context.links;
272
326
  }
273
327
 
274
- // TODO: hook for plugins "react:server:template"
275
- // { response: { html: string }, request, state }
328
+ const html = this.renderToHtml(template, state, context);
276
329
 
277
- return response.html;
330
+ page.afterHandler?.(serverRequest);
331
+
332
+ return html;
278
333
  };
279
334
  }
280
335
 
281
- fillTemplate(response: { html: string }, app: string, script: string) {
336
+ public renderToHtml(
337
+ template: string,
338
+ state: RouterState,
339
+ context: PageReactContext,
340
+ ) {
341
+ const element = this.pageDescriptorProvider.root(state, context);
342
+
343
+ this.serverTimingProvider.beginTiming("renderToString");
344
+
345
+ let app = "";
346
+ try {
347
+ app = renderToString(element);
348
+ } catch (error) {
349
+ this.log.error("Error during SSR", error);
350
+ app = renderToString(context.onError(error as Error));
351
+ }
352
+
353
+ this.serverTimingProvider.endTiming("renderToString");
354
+
355
+ const hydrationData: ReactHydrationState = {
356
+ links: context.links,
357
+ layers: state.layers.map((it) => ({
358
+ ...it,
359
+ error: it.error
360
+ ? {
361
+ ...it.error,
362
+ name: it.error.name,
363
+ message: it.error.message,
364
+ stack: it.error.stack, // TODO: Hide stack in production ?
365
+ }
366
+ : undefined,
367
+ index: undefined,
368
+ path: undefined,
369
+ element: undefined,
370
+ })),
371
+ };
372
+
373
+ // create hydration data
374
+ const script = `<script>window.__ssr=${JSON.stringify(hydrationData)}</script>`;
375
+
376
+ const response = {
377
+ html: template,
378
+ };
379
+
380
+ // inject app into template
381
+ this.fillTemplate(response, app, script);
382
+
383
+ // inject head meta
384
+ if (context.head) {
385
+ response.html = this.headProvider.renderHead(response.html, context.head);
386
+ }
387
+
388
+ // TODO: hook for plugins "react:server:template"
389
+ // { response: { html: string }, request, state }
390
+
391
+ return response.html;
392
+ }
393
+
394
+ protected fillTemplate(
395
+ response: { html: string },
396
+ app: string,
397
+ script: string,
398
+ ) {
282
399
  if (this.ROOT_DIV_REGEX.test(response.html)) {
283
400
  // replace contents of the existing <div id="root">
284
401
  response.html = response.html.replace(
@@ -305,3 +422,5 @@ export class ReactServerProvider {
305
422
  }
306
423
  }
307
424
  }
425
+
426
+ type TemplateLoader = () => Promise<string | undefined>;
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.browser.cjs","sources":["../src/index.browser.ts"],"sourcesContent":["import { $inject, Alepha, __bind } from \"@alepha/core\";\nimport { $page } from \"./descriptors/$page.ts\";\nimport { BrowserRouterProvider } from \"./providers/BrowserRouterProvider.ts\";\nimport { PageDescriptorProvider } from \"./providers/PageDescriptorProvider.ts\";\nimport { ReactBrowserProvider } from \"./providers/ReactBrowserProvider.ts\";\n\nexport * from \"./index.shared\";\nexport * from \"./providers/ReactBrowserProvider.ts\";\n\nexport class ReactModule {\n\tprotected readonly alepha = $inject(Alepha);\n\n\tconstructor() {\n\t\tthis.alepha //\n\t\t\t.with(PageDescriptorProvider)\n\t\t\t.with(ReactBrowserProvider)\n\t\t\t.with(BrowserRouterProvider);\n\t}\n}\n\n__bind($page, ReactModule);\n"],"names":["$inject","Alepha","PageDescriptorProvider","ReactBrowserProvider","BrowserRouterProvider","__bind","$page"],"mappings":";;;;;;;;;;AAQO,MAAM,WAAW,CAAC;AACzB,EAAE,MAAM,GAAGA,YAAO,CAACC,WAAM,CAAC;AAC1B,EAAE,WAAW,GAAG;AAChB,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAACC,gCAAsB,CAAC,CAAC,IAAI,CAACC,8BAAoB,CAAC,CAAC,IAAI,CAACC,+BAAqB,CAAC;AACnG;AACA;AACAC,WAAM,CAACC,eAAK,EAAE,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.browser.js","sources":["../src/index.browser.ts"],"sourcesContent":["import { $inject, Alepha, __bind } from \"@alepha/core\";\nimport { $page } from \"./descriptors/$page.ts\";\nimport { BrowserRouterProvider } from \"./providers/BrowserRouterProvider.ts\";\nimport { PageDescriptorProvider } from \"./providers/PageDescriptorProvider.ts\";\nimport { ReactBrowserProvider } from \"./providers/ReactBrowserProvider.ts\";\n\nexport * from \"./index.shared\";\nexport * from \"./providers/ReactBrowserProvider.ts\";\n\nexport class ReactModule {\n\tprotected readonly alepha = $inject(Alepha);\n\n\tconstructor() {\n\t\tthis.alepha //\n\t\t\t.with(PageDescriptorProvider)\n\t\t\t.with(ReactBrowserProvider)\n\t\t\t.with(BrowserRouterProvider);\n\t}\n}\n\n__bind($page, ReactModule);\n"],"names":[],"mappings":";;;;;;;;;AAQO,MAAM,WAAW,CAAC;AACzB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;AAC1B,EAAE,WAAW,GAAG;AAChB,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC;AACnG;AACA;AACA,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC;;;;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/providers/ServerHeadProvider.ts","../src/providers/ReactServerProvider.ts","../src/index.ts"],"sourcesContent":["export interface Head {\n\ttitle?: string;\n\thtmlAttributes?: Record<string, string>;\n\tbodyAttributes?: Record<string, string>;\n\tmeta?: Array<{ name: string; content: string }>;\n}\n\nexport class ServerHeadProvider {\n\trenderHead(template: string, head: Head): string {\n\t\tlet result = template;\n\n\t\t// Inject htmlAttributes\n\t\tconst htmlAttributes = head.htmlAttributes;\n\t\tif (htmlAttributes) {\n\t\t\tresult = result.replace(\n\t\t\t\t/<html([^>]*)>/i,\n\t\t\t\t(_, existingAttrs) =>\n\t\t\t\t\t`<html${this.mergeAttributes(existingAttrs, htmlAttributes)}>`,\n\t\t\t);\n\t\t}\n\n\t\t// Inject bodyAttributes\n\t\tconst bodyAttributes = head.bodyAttributes;\n\t\tif (bodyAttributes) {\n\t\t\tresult = result.replace(\n\t\t\t\t/<body([^>]*)>/i,\n\t\t\t\t(_, existingAttrs) =>\n\t\t\t\t\t`<body${this.mergeAttributes(existingAttrs, bodyAttributes)}>`,\n\t\t\t);\n\t\t}\n\n\t\t// Build head content\n\t\tlet headContent = \"\";\n\t\tconst title = head.title;\n\t\tif (title) {\n\t\t\tif (template.includes(\"<title>\")) {\n\t\t\t\tresult = result.replace(\n\t\t\t\t\t/<title>(.*?)<\\/title>/i,\n\t\t\t\t\t() => `<title>${this.escapeHtml(title)}</title>`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\theadContent += `<title>${this.escapeHtml(title)}</title>\\n`;\n\t\t\t}\n\t\t}\n\n\t\tif (head.meta) {\n\t\t\tfor (const meta of head.meta) {\n\t\t\t\theadContent += `<meta name=\"${this.escapeHtml(meta.name)}\" content=\"${this.escapeHtml(meta.content)}\">\\n`;\n\t\t\t}\n\t\t}\n\n\t\t// Inject into <head>...</head>\n\t\tresult = result.replace(\n\t\t\t/<head([^>]*)>(.*?)<\\/head>/is,\n\t\t\t(_, existingAttrs, existingHead) =>\n\t\t\t\t`<head${existingAttrs}>${existingHead}${headContent}</head>`,\n\t\t);\n\n\t\treturn result.trim();\n\t}\n\n\tmergeAttributes(existing: string, attrs: Record<string, string>): string {\n\t\tconst existingAttrs = this.parseAttributes(existing);\n\t\tconst merged = { ...existingAttrs, ...attrs };\n\t\treturn Object.entries(merged)\n\t\t\t.map(([k, v]) => ` ${k}=\"${this.escapeHtml(v)}\"`)\n\t\t\t.join(\"\");\n\t}\n\n\tparseAttributes(attrStr: string): Record<string, string> {\n\t\tconst attrs: Record<string, string> = {};\n\t\tconst attrRegex = /([^\\s=]+)(?:=\"([^\"]*)\")?/g;\n\t\tlet match: RegExpExecArray | null = attrRegex.exec(attrStr);\n\n\t\twhile (match) {\n\t\t\tattrs[match[1]] = match[2] ?? \"\";\n\t\t\tmatch = attrRegex.exec(attrStr);\n\t\t}\n\n\t\treturn attrs;\n\t}\n\n\tescapeHtml(str: string): string {\n\t\treturn str\n\t\t\t.replace(/&/g, \"&amp;\")\n\t\t\t.replace(/</g, \"&lt;\")\n\t\t\t.replace(/>/g, \"&gt;\")\n\t\t\t.replace(/\"/g, \"&quot;\")\n\t\t\t.replace(/'/g, \"&#039;\");\n\t}\n}\n","import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport {\n\t$hook,\n\t$inject,\n\t$logger,\n\tAlepha,\n\tOPTIONS,\n\ttype Static,\n\tt,\n} from \"@alepha/core\";\nimport {\n\ttype ServerHandler,\n\tServerLinksProvider,\n\tServerRouterProvider,\n} from \"@alepha/server\";\nimport { ServerStaticProvider } from \"@alepha/server-static\";\nimport { renderToString } from \"react-dom/server\";\nimport { $page } from \"../descriptors/$page.ts\";\nimport {\n\tPageDescriptorProvider,\n\ttype PageRequest,\n\ttype PageRoute,\n} from \"./PageDescriptorProvider.ts\";\nimport { ServerHeadProvider } from \"./ServerHeadProvider.ts\";\n\nexport const envSchema = t.object({\n\tREACT_SERVER_DIST: t.string({ default: \"client\" }),\n\tREACT_SERVER_PREFIX: t.string({ default: \"\" }),\n\tREACT_SSR_ENABLED: t.boolean({ default: false }),\n\tREACT_ROOT_ID: t.string({ default: \"root\" }),\n});\n\ndeclare module \"@alepha/core\" {\n\tinterface Env extends Partial<Static<typeof envSchema>> {}\n\tinterface State {\n\t\t\"ReactServerProvider.template\"?: string;\n\t\t\"ReactServerProvider.ssr\"?: boolean;\n\t}\n}\n\nexport class ReactServerProvider {\n\tprotected readonly log = $logger();\n\tprotected readonly alepha = $inject(Alepha);\n\tprotected readonly pageDescriptorProvider = $inject(PageDescriptorProvider);\n\tprotected readonly serverStaticProvider = $inject(ServerStaticProvider);\n\tprotected readonly serverRouterProvider = $inject(ServerRouterProvider);\n\tprotected readonly headProvider = $inject(ServerHeadProvider);\n\tprotected readonly env = $inject(envSchema);\n\tprotected readonly ROOT_DIV_REGEX = new RegExp(\n\t\t`<div([^>]*)\\\\s+id=[\"']${this.env.REACT_ROOT_ID}[\"']([^>]*)>(.*?)<\\\\/div>`,\n\t\t\"is\",\n\t);\n\n\tprotected readonly configure = $hook({\n\t\tname: \"configure\",\n\t\thandler: async () => {\n\t\t\tconst pages = this.alepha.getDescriptorValues($page);\n\t\t\tif (pages.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor (const { key, instance, value } of pages) {\n\t\t\t\tconst name = value[OPTIONS].name ?? key;\n\n\t\t\t\tif (this.alepha.isTest()) {\n\t\t\t\t\tinstance[key].render = this.createRenderFunction(name);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.alepha.isServerless() === \"vite\") {\n\t\t\t\tawait this.configureVite();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet root = \"\";\n\t\t\tif (!this.alepha.isServerless()) {\n\t\t\t\troot = this.getPublicDirectory();\n\n\t\t\t\tif (!root) {\n\t\t\t\t\tthis.log.warn(\"Missing static files, SSR will be disabled\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tawait this.configureStaticServer(root);\n\t\t\t}\n\n\t\t\tconst template =\n\t\t\t\tthis.alepha.state(\"ReactServerProvider.template\") ??\n\t\t\t\t(await readFile(join(root, \"index.html\"), \"utf-8\"));\n\n\t\t\tawait this.registerPages(async () => template);\n\n\t\t\tthis.alepha.state(\"ReactServerProvider.ssr\", true);\n\t\t},\n\t});\n\n\tprotected async registerPages(\n\t\ttemplateLoader: () => Promise<string | undefined>,\n\t) {\n\t\tfor (const page of this.pageDescriptorProvider.getPages()) {\n\t\t\tthis.log.debug(`+ ${page.match} -> ${page.name}`);\n\t\t\tawait this.serverRouterProvider.route({\n\t\t\t\tmethod: \"GET\",\n\t\t\t\tpath: page.match,\n\t\t\t\thandler: this.createHandler(page, templateLoader),\n\t\t\t});\n\t\t}\n\t}\n\n\tprotected getPublicDirectory(): string {\n\t\tconst maybe = [\n\t\t\tjoin(process.cwd(), this.env.REACT_SERVER_DIST),\n\t\t\tjoin(process.cwd(), \"..\", this.env.REACT_SERVER_DIST),\n\t\t];\n\n\t\tfor (const it of maybe) {\n\t\t\tif (existsSync(it)) {\n\t\t\t\treturn it;\n\t\t\t}\n\t\t}\n\n\t\treturn \"\";\n\t}\n\n\tprotected async configureStaticServer(root: string) {\n\t\tawait this.serverStaticProvider.serve({\n\t\t\troot,\n\t\t\tpath: this.env.REACT_SERVER_PREFIX,\n\t\t});\n\t}\n\n\tprotected async configureVite() {\n\t\tconst url = `http://${process.env.SERVER_HOST}:${process.env.SERVER_PORT}`;\n\t\tthis.log.info(\"SSR (vite) OK\");\n\t\tthis.alepha.state(\"ReactServerProvider.ssr\", true);\n\t\tconst templateUrl = `${url}/index.html`;\n\n\t\tawait this.registerPages(() =>\n\t\t\tfetch(templateUrl)\n\t\t\t\t.then((it) => it.text())\n\t\t\t\t.catch(() => undefined),\n\t\t);\n\t}\n\n\t/**\n\t * For testing purposes, creates a render function that can be used.\n\t */\n\tprotected createRenderFunction(name: string) {\n\t\treturn async (\n\t\t\toptions: {\n\t\t\t\tparams?: Record<string, string>;\n\t\t\t\tquery?: Record<string, string>;\n\t\t\t} = {},\n\t\t) => {\n\t\t\tconst page = this.pageDescriptorProvider.page(name);\n\t\t\tconst context: PageRequest = {\n\t\t\t\turl: new URL(\"http://localhost\"),\n\t\t\t\tparams: options.params ?? {},\n\t\t\t\tquery: options.query ?? {},\n\t\t\t\thead: {},\n\t\t\t\tonError: () => null,\n\t\t\t};\n\n\t\t\t// for testing\n\t\t\tconst state = await this.pageDescriptorProvider.createLayers(\n\t\t\t\tpage,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\treturn renderToString(this.pageDescriptorProvider.root(state, context));\n\t\t};\n\t}\n\n\tprotected createHandler(\n\t\tpage: PageRoute,\n\t\ttemplateLoader: () => Promise<string | undefined>,\n\t): ServerHandler {\n\t\treturn async (serverRequest) => {\n\t\t\tconst { url, reply, query, params } = serverRequest;\n\t\t\tconst template = await templateLoader();\n\t\t\tif (!template) {\n\t\t\t\tthrow new Error(\"Template not found\");\n\t\t\t}\n\n\t\t\tconst context: PageRequest = {\n\t\t\t\turl,\n\t\t\t\tparams,\n\t\t\t\tquery,\n\t\t\t\t// plugins\n\t\t\t\thead: {},\n\t\t\t\tonError: () => null,\n\t\t\t};\n\n\t\t\t// -- links\n\t\t\tif (this.alepha.has(ServerLinksProvider)) {\n\t\t\t\tconst srv = this.alepha.get(ServerLinksProvider);\n\n\t\t\t\tcontext.links = await srv.getLinks({\n\t\t\t\t\tuser: serverRequest.user,\n\t\t\t\t\tauthorization: serverRequest.headers.authorization,\n\t\t\t\t});\n\n\t\t\t\tthis.alepha.als.set(\"links\", context.links);\n\t\t\t}\n\n\t\t\tawait this.alepha.emit(\n\t\t\t\t\"react:server:render\",\n\t\t\t\t{\n\t\t\t\t\trequest: serverRequest,\n\t\t\t\t\tpageRequest: context,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlog: false,\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tconst state = await this.pageDescriptorProvider.createLayers(\n\t\t\t\tpage,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\tif (state.redirect) {\n\t\t\t\treturn reply.redirect(state.redirect);\n\t\t\t}\n\n\t\t\tconst element = this.pageDescriptorProvider.root(state, context);\n\t\t\tconst app = renderToString(element);\n\n\t\t\tconst hydrationData = {\n\t\t\t\tlinks: context.links,\n\t\t\t\tlayers: state.layers.map((it) => ({\n\t\t\t\t\t...it,\n\t\t\t\t\terror: it.error\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t...it.error,\n\t\t\t\t\t\t\t\tname: it.error.name,\n\t\t\t\t\t\t\t\tmessage: it.error.message,\n\t\t\t\t\t\t\t\tstack: it.error.stack, // TODO: Hide stack in production ?\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\tindex: undefined,\n\t\t\t\t\tpath: undefined,\n\t\t\t\t\telement: undefined,\n\t\t\t\t})),\n\t\t\t};\n\n\t\t\t// create hydration data\n\t\t\tconst script = `<script>window.__ssr=${JSON.stringify(hydrationData)}</script>`;\n\n\t\t\tconst response = {\n\t\t\t\thtml: template,\n\t\t\t};\n\n\t\t\treply.status = 200;\n\t\t\treply.headers[\"content-type\"] = \"text/html\";\n\t\t\treply.headers[\"cache-control\"] =\n\t\t\t\t\"no-store, no-cache, must-revalidate, proxy-revalidate\";\n\t\t\treply.headers.pragma = \"no-cache\";\n\t\t\treply.headers.expires = \"0\";\n\n\t\t\t// inject app into template\n\t\t\tthis.fillTemplate(response, app, script);\n\n\t\t\t// inject head meta\n\t\t\tif (context.head) {\n\t\t\t\tresponse.html = this.headProvider.renderHead(\n\t\t\t\t\tresponse.html,\n\t\t\t\t\tcontext.head,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// TODO: hook for plugins \"react:server:template\"\n\t\t\t// { response: { html: string }, request, state }\n\n\t\t\treturn response.html;\n\t\t};\n\t}\n\n\tfillTemplate(response: { html: string }, app: string, script: string) {\n\t\tif (this.ROOT_DIV_REGEX.test(response.html)) {\n\t\t\t// replace contents of the existing <div id=\"root\">\n\t\t\tresponse.html = response.html.replace(\n\t\t\t\tthis.ROOT_DIV_REGEX,\n\t\t\t\t(_match, beforeId, afterId) => {\n\t\t\t\t\treturn `<div${beforeId} id=\"${this.env.REACT_ROOT_ID}\"${afterId}>${app}</div>`;\n\t\t\t\t},\n\t\t\t);\n\t\t} else {\n\t\t\tconst bodyOpenTag = /<body([^>]*)>/i;\n\t\t\tif (bodyOpenTag.test(response.html)) {\n\t\t\t\tresponse.html = response.html.replace(bodyOpenTag, (match) => {\n\t\t\t\t\treturn `${match}\\n<div id=\"${this.env.REACT_ROOT_ID}\">${app}</div>`;\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tconst bodyCloseTagRegex = /<\\/body>/i;\n\t\tif (bodyCloseTagRegex.test(response.html)) {\n\t\t\tresponse.html = response.html.replace(\n\t\t\t\tbodyCloseTagRegex,\n\t\t\t\t`${script}\\n</body>`,\n\t\t\t);\n\t\t}\n\t}\n}\n","import { $inject, Alepha, __bind } from \"@alepha/core\";\nimport {\n\tServerLinksProvider,\n\tServerModule,\n\ttype ServerRequest,\n} from \"@alepha/server\";\nimport { $page } from \"./descriptors/$page.ts\";\nimport {\n\tPageDescriptorProvider,\n\ttype PageReactContext,\n\ttype PageRequest,\n\ttype RouterState,\n} from \"./providers/PageDescriptorProvider.ts\";\nimport type { ReactHydrationState } from \"./providers/ReactBrowserProvider.ts\";\nimport { ReactServerProvider } from \"./providers/ReactServerProvider.ts\";\nexport { default as NestedView } from \"./components/NestedView.tsx\";\n\nexport * from \"./index.shared.ts\";\nexport * from \"./providers/PageDescriptorProvider.ts\";\nexport * from \"./providers/ReactBrowserProvider.ts\";\nexport * from \"./providers/ReactServerProvider.ts\";\nexport * from \"./errors/RedirectionError.ts\";\n\ndeclare module \"@alepha/core\" {\n\tinterface Hooks {\n\t\t\"react:browser:render\": {\n\t\t\tstate: RouterState;\n\t\t\tcontext: PageReactContext;\n\t\t\thydration?: ReactHydrationState;\n\t\t};\n\t\t\"react:server:render\": {\n\t\t\trequest: ServerRequest;\n\t\t\tpageRequest: PageRequest;\n\t\t};\n\n\t\t\"react:transition:begin\": {\n\t\t\tstate: RouterState;\n\t\t\tcontext: PageReactContext;\n\t\t};\n\t\t\"react:transition:success\": {\n\t\t\tstate: RouterState;\n\t\t};\n\t\t\"react:transition:error\": {\n\t\t\terror: Error;\n\t\t\tstate: RouterState;\n\t\t\tcontext: PageReactContext;\n\t\t};\n\t\t\"react:transition:end\": {\n\t\t\tstate: RouterState;\n\t\t\tcontext: PageReactContext;\n\t\t};\n\t}\n}\n\nexport class ReactModule {\n\tprotected readonly alepha = $inject(Alepha);\n\n\tconstructor() {\n\t\tthis.alepha //\n\t\t\t.with(ServerModule)\n\t\t\t.with(ServerLinksProvider)\n\t\t\t.with(PageDescriptorProvider)\n\t\t\t.with(ReactServerProvider);\n\t}\n}\n\n__bind($page, ReactModule);\n"],"names":["t","$logger","$inject","Alepha","PageDescriptorProvider","ServerStaticProvider","ServerRouterProvider","$hook","$page","OPTIONS","readFile","join","existsSync","renderToString","ServerLinksProvider","ServerModule","__bind"],"mappings":";;;;;;;;;;;;;;;AACO,MAAM,kBAAkB,CAAC;AAChC,EAAE,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE;AAC7B,IAAI,IAAI,MAAM,GAAG,QAAQ;AACzB,IAAI,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc;AAC9C,IAAI,IAAI,cAAc,EAAE;AACxB,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO;AAC7B,QAAQ,gBAAgB;AACxB,QAAQ,CAAC,CAAC,EAAE,aAAa,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC;AAC3F,OAAO;AACP;AACA,IAAI,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc;AAC9C,IAAI,IAAI,cAAc,EAAE;AACxB,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO;AAC7B,QAAQ,gBAAgB;AACxB,QAAQ,CAAC,CAAC,EAAE,aAAa,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC;AAC3F,OAAO;AACP;AACA,IAAI,IAAI,WAAW,GAAG,EAAE;AACxB,IAAI,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK;AAC5B,IAAI,IAAI,KAAK,EAAE;AACf,MAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE;AACxC,QAAQ,MAAM,GAAG,MAAM,CAAC,OAAO;AAC/B,UAAU,wBAAwB;AAClC,UAAU,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,QAAQ;AACzD,SAAS;AACT,OAAO,MAAM;AACb,QAAQ,WAAW,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AACxD,CAAC;AACD;AACA;AACA,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE;AACnB,MAAM,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE;AACpC,QAAQ,WAAW,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC5G,CAAC;AACD;AACA;AACA,IAAI,MAAM,GAAG,MAAM,CAAC,OAAO;AAC3B,MAAM,8BAA8B;AACpC,MAAM,CAAC,CAAC,EAAE,aAAa,EAAE,YAAY,KAAK,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,YAAY,CAAC,EAAE,WAAW,CAAC,OAAO;AACrG,KAAK;AACL,IAAI,OAAO,MAAM,CAAC,IAAI,EAAE;AACxB;AACA,EAAE,eAAe,CAAC,QAAQ,EAAE,KAAK,EAAE;AACnC,IAAI,MAAM,aAAa,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC;AACxD,IAAI,MAAM,MAAM,GAAG,EAAE,GAAG,aAAa,EAAE,GAAG,KAAK,EAAE;AACjD,IAAI,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;AAC3F;AACA,EAAE,eAAe,CAAC,OAAO,EAAE;AAC3B,IAAI,MAAM,KAAK,GAAG,EAAE;AACpB,IAAI,MAAM,SAAS,GAAG,2BAA2B;AACjD,IAAI,IAAI,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;AACvC,IAAI,OAAO,KAAK,EAAE;AAClB,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;AACtC,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;AACrC;AACA,IAAI,OAAO,KAAK;AAChB;AACA,EAAE,UAAU,CAAC,GAAG,EAAE;AAClB,IAAI,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;AACjI;AACA;;ACtCY,MAAC,SAAS,GAAGA,MAAC,CAAC,MAAM,CAAC;AAClC,EAAE,iBAAiB,EAAEA,MAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpD,EAAE,mBAAmB,EAAEA,MAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AAChD,EAAE,iBAAiB,EAAEA,MAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAClD,EAAE,aAAa,EAAEA,MAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE;AAC7C,CAAC;AACM,MAAM,mBAAmB,CAAC;AACjC,EAAE,GAAG,GAAGC,YAAO,EAAE;AACjB,EAAE,MAAM,GAAGC,YAAO,CAACC,WAAM,CAAC;AAC1B,EAAE,sBAAsB,GAAGD,YAAO,CAACE,gCAAsB,CAAC;AAC1D,EAAE,oBAAoB,GAAGF,YAAO,CAACG,iCAAoB,CAAC;AACtD,EAAE,oBAAoB,GAAGH,YAAO,CAACI,2BAAoB,CAAC;AACtD,EAAE,YAAY,GAAGJ,YAAO,CAAC,kBAAkB,CAAC;AAC5C,EAAE,GAAG,GAAGA,YAAO,CAAC,SAAS,CAAC;AAC1B,EAAE,cAAc,GAAG,IAAI,MAAM;AAC7B,IAAI,CAAC,sBAAsB,EAAE,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,yBAAyB,CAAC;AAC9E,IAAI;AACJ,GAAG;AACH,EAAE,SAAS,GAAGK,UAAK,CAAC;AACpB,IAAI,IAAI,EAAE,WAAW;AACrB,IAAI,OAAO,EAAE,YAAY;AACzB,MAAM,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAACC,eAAK,CAAC;AAC1D,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;AAC9B,QAAQ;AACR;AACA,MAAM,KAAK,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,KAAK,EAAE;AACpD,QAAQ,MAAM,IAAI,GAAG,KAAK,CAACC,YAAO,CAAC,CAAC,IAAI,IAAI,GAAG;AAC/C,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE;AAClC,UAAU,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC;AAChE;AACA;AACA,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,KAAK,MAAM,EAAE;AACjD,QAAQ,MAAM,IAAI,CAAC,aAAa,EAAE;AAClC,QAAQ;AACR;AACA,MAAM,IAAI,IAAI,GAAG,EAAE;AACnB,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE;AACvC,QAAQ,IAAI,GAAG,IAAI,CAAC,kBAAkB,EAAE;AACxC,QAAQ,IAAI,CAAC,IAAI,EAAE;AACnB,UAAU,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,4CAA4C,CAAC;AACrE,UAAU;AACV;AACA,QAAQ,MAAM,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC;AAC9C;AACA,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,CAAC,IAAI,MAAMC,iBAAQ,CAACC,cAAI,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC;AAC7H,MAAM,MAAM,IAAI,CAAC,aAAa,CAAC,YAAY,QAAQ,CAAC;AACpD,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,IAAI,CAAC;AACxD;AACA,GAAG,CAAC;AACJ,EAAE,MAAM,aAAa,CAAC,cAAc,EAAE;AACtC,IAAI,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,sBAAsB,CAAC,QAAQ,EAAE,EAAE;AAC/D,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AACvD,MAAM,MAAM,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC;AAC5C,QAAQ,MAAM,EAAE,KAAK;AACrB,QAAQ,IAAI,EAAE,IAAI,CAAC,KAAK;AACxB,QAAQ,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,cAAc;AACxD,OAAO,CAAC;AACR;AACA;AACA,EAAE,kBAAkB,GAAG;AACvB,IAAI,MAAM,KAAK,GAAG;AAClB,MAAMA,cAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC;AACrD,MAAMA,cAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,iBAAiB;AAC1D,KAAK;AACL,IAAI,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE;AAC5B,MAAM,IAAIC,kBAAU,CAAC,EAAE,CAAC,EAAE;AAC1B,QAAQ,OAAO,EAAE;AACjB;AACA;AACA,IAAI,OAAO,EAAE;AACb;AACA,EAAE,MAAM,qBAAqB,CAAC,IAAI,EAAE;AACpC,IAAI,MAAM,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC;AAC1C,MAAM,IAAI;AACV,MAAM,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;AACrB,KAAK,CAAC;AACN;AACA,EAAE,MAAM,aAAa,GAAG;AACxB,IAAI,MAAM,GAAG,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AAC9E,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC;AAClC,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,IAAI,CAAC;AACtD,IAAI,MAAM,WAAW,GAAG,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC;AAC3C,IAAI,MAAM,IAAI,CAAC,aAAa;AAC5B,MAAM,MAAM,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,MAAM;AACzE,KAAK;AACL;AACA;AACA;AACA;AACA,EAAE,oBAAoB,CAAC,IAAI,EAAE;AAC7B,IAAI,OAAO,OAAO,OAAO,GAAG,EAAE,KAAK;AACnC,MAAM,MAAM,IAAI,GAAG,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC;AACzD,MAAM,MAAM,OAAO,GAAG;AACtB,QAAQ,GAAG,EAAE,IAAI,GAAG,CAAC,kBAAkB,CAAC;AACxC,QAAQ,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;AACpC,QAAQ,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE;AAClC,QAAQ,IAAI,EAAE,EAAE;AAChB,QAAQ,OAAO,EAAE,MAAM;AACvB,OAAO;AACP,MAAM,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,YAAY;AAClE,QAAQ,IAAI;AACZ,QAAQ;AACR,OAAO;AACP,MAAM,OAAOC,uBAAc,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AAC7E,KAAK;AACL;AACA,EAAE,aAAa,CAAC,IAAI,EAAE,cAAc,EAAE;AACtC,IAAI,OAAO,OAAO,aAAa,KAAK;AACpC,MAAM,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,aAAa;AACzD,MAAM,MAAM,QAAQ,GAAG,MAAM,cAAc,EAAE;AAC7C,MAAM,IAAI,CAAC,QAAQ,EAAE;AACrB,QAAQ,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC;AAC7C;AACA,MAAM,MAAM,OAAO,GAAG;AACtB,QAAQ,GAAG;AACX,QAAQ,MAAM;AACd,QAAQ,KAAK;AACb;AACA,QAAQ,IAAI,EAAE,EAAE;AAChB,QAAQ,OAAO,EAAE,MAAM;AACvB,OAAO;AACP,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAACC,0BAAmB,CAAC,EAAE;AAChD,QAAQ,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAACA,0BAAmB,CAAC;AACxD,QAAQ,OAAO,CAAC,KAAK,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC;AAC3C,UAAU,IAAI,EAAE,aAAa,CAAC,IAAI;AAClC,UAAU,aAAa,EAAE,aAAa,CAAC,OAAO,CAAC;AAC/C,SAAS,CAAC;AACV,QAAQ,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC;AACnD;AACA,MAAM,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI;AAC5B,QAAQ,qBAAqB;AAC7B,QAAQ;AACR,UAAU,OAAO,EAAE,aAAa;AAChC,UAAU,WAAW,EAAE;AACvB,SAAS;AACT,QAAQ;AACR,UAAU,GAAG,EAAE;AACf;AACA,OAAO;AACP,MAAM,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,YAAY;AAClE,QAAQ,IAAI;AACZ,QAAQ;AACR,OAAO;AACP,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE;AAC1B,QAAQ,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC;AAC7C;AACA,MAAM,MAAM,OAAO,GAAG,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC;AACtE,MAAM,MAAM,GAAG,GAAGD,uBAAc,CAAC,OAAO,CAAC;AACzC,MAAM,MAAM,aAAa,GAAG;AAC5B,QAAQ,KAAK,EAAE,OAAO,CAAC,KAAK;AAC5B,QAAQ,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM;AAC1C,UAAU,GAAG,EAAE;AACf,UAAU,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG;AAC5B,YAAY,GAAG,EAAE,CAAC,KAAK;AACvB,YAAY,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI;AAC/B,YAAY,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO;AACrC,YAAY,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC;AAC5B;AACA,WAAW,GAAG,MAAM;AACpB,UAAU,KAAK,EAAE,MAAM;AACvB,UAAU,IAAI,EAAE,MAAM;AACtB,UAAU,OAAO,EAAE;AACnB,SAAS,CAAC;AACV,OAAO;AACP,MAAM,MAAM,MAAM,GAAG,CAAC,qBAAqB,EAAE,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,UAAU,CAAC;AACtF,MAAM,MAAM,QAAQ,GAAG;AACvB,QAAQ,IAAI,EAAE;AACd,OAAO;AACP,MAAM,KAAK,CAAC,MAAM,GAAG,GAAG;AACxB,MAAM,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,GAAG,WAAW;AACjD,MAAM,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,GAAG,uDAAuD;AAC9F,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,UAAU;AACvC,MAAM,KAAK,CAAC,OAAO,CAAC,OAAO,GAAG,GAAG;AACjC,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,CAAC;AAC9C,MAAM,IAAI,OAAO,CAAC,IAAI,EAAE;AACxB,QAAQ,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU;AACpD,UAAU,QAAQ,CAAC,IAAI;AACvB,UAAU,OAAO,CAAC;AAClB,SAAS;AACT;AACA,MAAM,OAAO,QAAQ,CAAC,IAAI;AAC1B,KAAK;AACL;AACA,EAAE,YAAY,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE;AACtC,IAAI,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;AACjD,MAAM,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO;AAC3C,QAAQ,IAAI,CAAC,cAAc;AAC3B,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK;AACvC,UAAU,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC;AACxF;AACA,OAAO;AACP,KAAK,MAAM;AACX,MAAM,MAAM,WAAW,GAAG,gBAAgB;AAC1C,MAAM,IAAI,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;AAC3C,QAAQ,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,KAAK,KAAK;AACtE,UAAU,OAAO,CAAC,EAAE,KAAK;AACzB,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC;AACjD,SAAS,CAAC;AACV;AACA;AACA,IAAI,MAAM,iBAAiB,GAAG,WAAW;AACzC,IAAI,IAAI,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;AAC/C,MAAM,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO;AAC3C,QAAQ,iBAAiB;AACzB,QAAQ,CAAC,EAAE,MAAM;AACjB,OAAO;AACP,OAAO;AACP;AACA;AACA;;ACvNO,MAAM,WAAW,CAAC;AACzB,EAAE,MAAM,GAAGX,YAAO,CAACC,WAAM,CAAC;AAC1B,EAAE,WAAW,GAAG;AAChB,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAACY,mBAAY,CAAC,CAAC,IAAI,CAACD,0BAAmB,CAAC,CAAC,IAAI,CAACV,gCAAsB,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC;AACnH;AACA;AACAY,WAAM,CAACR,eAAK,EAAE,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sources":["../src/providers/ServerHeadProvider.ts","../src/providers/ReactServerProvider.ts","../src/index.ts"],"sourcesContent":["export interface Head {\n\ttitle?: string;\n\thtmlAttributes?: Record<string, string>;\n\tbodyAttributes?: Record<string, string>;\n\tmeta?: Array<{ name: string; content: string }>;\n}\n\nexport class ServerHeadProvider {\n\trenderHead(template: string, head: Head): string {\n\t\tlet result = template;\n\n\t\t// Inject htmlAttributes\n\t\tconst htmlAttributes = head.htmlAttributes;\n\t\tif (htmlAttributes) {\n\t\t\tresult = result.replace(\n\t\t\t\t/<html([^>]*)>/i,\n\t\t\t\t(_, existingAttrs) =>\n\t\t\t\t\t`<html${this.mergeAttributes(existingAttrs, htmlAttributes)}>`,\n\t\t\t);\n\t\t}\n\n\t\t// Inject bodyAttributes\n\t\tconst bodyAttributes = head.bodyAttributes;\n\t\tif (bodyAttributes) {\n\t\t\tresult = result.replace(\n\t\t\t\t/<body([^>]*)>/i,\n\t\t\t\t(_, existingAttrs) =>\n\t\t\t\t\t`<body${this.mergeAttributes(existingAttrs, bodyAttributes)}>`,\n\t\t\t);\n\t\t}\n\n\t\t// Build head content\n\t\tlet headContent = \"\";\n\t\tconst title = head.title;\n\t\tif (title) {\n\t\t\tif (template.includes(\"<title>\")) {\n\t\t\t\tresult = result.replace(\n\t\t\t\t\t/<title>(.*?)<\\/title>/i,\n\t\t\t\t\t() => `<title>${this.escapeHtml(title)}</title>`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\theadContent += `<title>${this.escapeHtml(title)}</title>\\n`;\n\t\t\t}\n\t\t}\n\n\t\tif (head.meta) {\n\t\t\tfor (const meta of head.meta) {\n\t\t\t\theadContent += `<meta name=\"${this.escapeHtml(meta.name)}\" content=\"${this.escapeHtml(meta.content)}\">\\n`;\n\t\t\t}\n\t\t}\n\n\t\t// Inject into <head>...</head>\n\t\tresult = result.replace(\n\t\t\t/<head([^>]*)>(.*?)<\\/head>/is,\n\t\t\t(_, existingAttrs, existingHead) =>\n\t\t\t\t`<head${existingAttrs}>${existingHead}${headContent}</head>`,\n\t\t);\n\n\t\treturn result.trim();\n\t}\n\n\tmergeAttributes(existing: string, attrs: Record<string, string>): string {\n\t\tconst existingAttrs = this.parseAttributes(existing);\n\t\tconst merged = { ...existingAttrs, ...attrs };\n\t\treturn Object.entries(merged)\n\t\t\t.map(([k, v]) => ` ${k}=\"${this.escapeHtml(v)}\"`)\n\t\t\t.join(\"\");\n\t}\n\n\tparseAttributes(attrStr: string): Record<string, string> {\n\t\tconst attrs: Record<string, string> = {};\n\t\tconst attrRegex = /([^\\s=]+)(?:=\"([^\"]*)\")?/g;\n\t\tlet match: RegExpExecArray | null = attrRegex.exec(attrStr);\n\n\t\twhile (match) {\n\t\t\tattrs[match[1]] = match[2] ?? \"\";\n\t\t\tmatch = attrRegex.exec(attrStr);\n\t\t}\n\n\t\treturn attrs;\n\t}\n\n\tescapeHtml(str: string): string {\n\t\treturn str\n\t\t\t.replace(/&/g, \"&amp;\")\n\t\t\t.replace(/</g, \"&lt;\")\n\t\t\t.replace(/>/g, \"&gt;\")\n\t\t\t.replace(/\"/g, \"&quot;\")\n\t\t\t.replace(/'/g, \"&#039;\");\n\t}\n}\n","import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport {\n\t$hook,\n\t$inject,\n\t$logger,\n\tAlepha,\n\tOPTIONS,\n\ttype Static,\n\tt,\n} from \"@alepha/core\";\nimport {\n\ttype ServerHandler,\n\tServerLinksProvider,\n\tServerRouterProvider,\n} from \"@alepha/server\";\nimport { ServerStaticProvider } from \"@alepha/server-static\";\nimport { renderToString } from \"react-dom/server\";\nimport { $page } from \"../descriptors/$page.ts\";\nimport {\n\tPageDescriptorProvider,\n\ttype PageRequest,\n\ttype PageRoute,\n} from \"./PageDescriptorProvider.ts\";\nimport { ServerHeadProvider } from \"./ServerHeadProvider.ts\";\n\nexport const envSchema = t.object({\n\tREACT_SERVER_DIST: t.string({ default: \"client\" }),\n\tREACT_SERVER_PREFIX: t.string({ default: \"\" }),\n\tREACT_SSR_ENABLED: t.boolean({ default: false }),\n\tREACT_ROOT_ID: t.string({ default: \"root\" }),\n});\n\ndeclare module \"@alepha/core\" {\n\tinterface Env extends Partial<Static<typeof envSchema>> {}\n\tinterface State {\n\t\t\"ReactServerProvider.template\"?: string;\n\t\t\"ReactServerProvider.ssr\"?: boolean;\n\t}\n}\n\nexport class ReactServerProvider {\n\tprotected readonly log = $logger();\n\tprotected readonly alepha = $inject(Alepha);\n\tprotected readonly pageDescriptorProvider = $inject(PageDescriptorProvider);\n\tprotected readonly serverStaticProvider = $inject(ServerStaticProvider);\n\tprotected readonly serverRouterProvider = $inject(ServerRouterProvider);\n\tprotected readonly headProvider = $inject(ServerHeadProvider);\n\tprotected readonly env = $inject(envSchema);\n\tprotected readonly ROOT_DIV_REGEX = new RegExp(\n\t\t`<div([^>]*)\\\\s+id=[\"']${this.env.REACT_ROOT_ID}[\"']([^>]*)>(.*?)<\\\\/div>`,\n\t\t\"is\",\n\t);\n\n\tprotected readonly configure = $hook({\n\t\tname: \"configure\",\n\t\thandler: async () => {\n\t\t\tconst pages = this.alepha.getDescriptorValues($page);\n\t\t\tif (pages.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor (const { key, instance, value } of pages) {\n\t\t\t\tconst name = value[OPTIONS].name ?? key;\n\n\t\t\t\tif (this.alepha.isTest()) {\n\t\t\t\t\tinstance[key].render = this.createRenderFunction(name);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.alepha.isServerless() === \"vite\") {\n\t\t\t\tawait this.configureVite();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet root = \"\";\n\t\t\tif (!this.alepha.isServerless()) {\n\t\t\t\troot = this.getPublicDirectory();\n\n\t\t\t\tif (!root) {\n\t\t\t\t\tthis.log.warn(\"Missing static files, SSR will be disabled\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tawait this.configureStaticServer(root);\n\t\t\t}\n\n\t\t\tconst template =\n\t\t\t\tthis.alepha.state(\"ReactServerProvider.template\") ??\n\t\t\t\t(await readFile(join(root, \"index.html\"), \"utf-8\"));\n\n\t\t\tawait this.registerPages(async () => template);\n\n\t\t\tthis.alepha.state(\"ReactServerProvider.ssr\", true);\n\t\t},\n\t});\n\n\tprotected async registerPages(\n\t\ttemplateLoader: () => Promise<string | undefined>,\n\t) {\n\t\tfor (const page of this.pageDescriptorProvider.getPages()) {\n\t\t\tthis.log.debug(`+ ${page.match} -> ${page.name}`);\n\t\t\tawait this.serverRouterProvider.route({\n\t\t\t\tmethod: \"GET\",\n\t\t\t\tpath: page.match,\n\t\t\t\thandler: this.createHandler(page, templateLoader),\n\t\t\t});\n\t\t}\n\t}\n\n\tprotected getPublicDirectory(): string {\n\t\tconst maybe = [\n\t\t\tjoin(process.cwd(), this.env.REACT_SERVER_DIST),\n\t\t\tjoin(process.cwd(), \"..\", this.env.REACT_SERVER_DIST),\n\t\t];\n\n\t\tfor (const it of maybe) {\n\t\t\tif (existsSync(it)) {\n\t\t\t\treturn it;\n\t\t\t}\n\t\t}\n\n\t\treturn \"\";\n\t}\n\n\tprotected async configureStaticServer(root: string) {\n\t\tawait this.serverStaticProvider.serve({\n\t\t\troot,\n\t\t\tpath: this.env.REACT_SERVER_PREFIX,\n\t\t});\n\t}\n\n\tprotected async configureVite() {\n\t\tconst url = `http://${process.env.SERVER_HOST}:${process.env.SERVER_PORT}`;\n\t\tthis.log.info(\"SSR (vite) OK\");\n\t\tthis.alepha.state(\"ReactServerProvider.ssr\", true);\n\t\tconst templateUrl = `${url}/index.html`;\n\n\t\tawait this.registerPages(() =>\n\t\t\tfetch(templateUrl)\n\t\t\t\t.then((it) => it.text())\n\t\t\t\t.catch(() => undefined),\n\t\t);\n\t}\n\n\t/**\n\t * For testing purposes, creates a render function that can be used.\n\t */\n\tprotected createRenderFunction(name: string) {\n\t\treturn async (\n\t\t\toptions: {\n\t\t\t\tparams?: Record<string, string>;\n\t\t\t\tquery?: Record<string, string>;\n\t\t\t} = {},\n\t\t) => {\n\t\t\tconst page = this.pageDescriptorProvider.page(name);\n\t\t\tconst context: PageRequest = {\n\t\t\t\turl: new URL(\"http://localhost\"),\n\t\t\t\tparams: options.params ?? {},\n\t\t\t\tquery: options.query ?? {},\n\t\t\t\thead: {},\n\t\t\t\tonError: () => null,\n\t\t\t};\n\n\t\t\t// for testing\n\t\t\tconst state = await this.pageDescriptorProvider.createLayers(\n\t\t\t\tpage,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\treturn renderToString(this.pageDescriptorProvider.root(state, context));\n\t\t};\n\t}\n\n\tprotected createHandler(\n\t\tpage: PageRoute,\n\t\ttemplateLoader: () => Promise<string | undefined>,\n\t): ServerHandler {\n\t\treturn async (serverRequest) => {\n\t\t\tconst { url, reply, query, params } = serverRequest;\n\t\t\tconst template = await templateLoader();\n\t\t\tif (!template) {\n\t\t\t\tthrow new Error(\"Template not found\");\n\t\t\t}\n\n\t\t\tconst context: PageRequest = {\n\t\t\t\turl,\n\t\t\t\tparams,\n\t\t\t\tquery,\n\t\t\t\t// plugins\n\t\t\t\thead: {},\n\t\t\t\tonError: () => null,\n\t\t\t};\n\n\t\t\t// -- links\n\t\t\tif (this.alepha.has(ServerLinksProvider)) {\n\t\t\t\tconst srv = this.alepha.get(ServerLinksProvider);\n\n\t\t\t\tcontext.links = await srv.getLinks({\n\t\t\t\t\tuser: serverRequest.user,\n\t\t\t\t\tauthorization: serverRequest.headers.authorization,\n\t\t\t\t});\n\n\t\t\t\tthis.alepha.als.set(\"links\", context.links);\n\t\t\t}\n\n\t\t\tawait this.alepha.emit(\n\t\t\t\t\"react:server:render\",\n\t\t\t\t{\n\t\t\t\t\trequest: serverRequest,\n\t\t\t\t\tpageRequest: context,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlog: false,\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tconst state = await this.pageDescriptorProvider.createLayers(\n\t\t\t\tpage,\n\t\t\t\tcontext,\n\t\t\t);\n\n\t\t\tif (state.redirect) {\n\t\t\t\treturn reply.redirect(state.redirect);\n\t\t\t}\n\n\t\t\tconst element = this.pageDescriptorProvider.root(state, context);\n\t\t\tconst app = renderToString(element);\n\n\t\t\tconst hydrationData = {\n\t\t\t\tlinks: context.links,\n\t\t\t\tlayers: state.layers.map((it) => ({\n\t\t\t\t\t...it,\n\t\t\t\t\terror: it.error\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t...it.error,\n\t\t\t\t\t\t\t\tname: it.error.name,\n\t\t\t\t\t\t\t\tmessage: it.error.message,\n\t\t\t\t\t\t\t\tstack: it.error.stack, // TODO: Hide stack in production ?\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\tindex: undefined,\n\t\t\t\t\tpath: undefined,\n\t\t\t\t\telement: undefined,\n\t\t\t\t})),\n\t\t\t};\n\n\t\t\t// create hydration data\n\t\t\tconst script = `<script>window.__ssr=${JSON.stringify(hydrationData)}</script>`;\n\n\t\t\tconst response = {\n\t\t\t\thtml: template,\n\t\t\t};\n\n\t\t\treply.status = 200;\n\t\t\treply.headers[\"content-type\"] = \"text/html\";\n\t\t\treply.headers[\"cache-control\"] =\n\t\t\t\t\"no-store, no-cache, must-revalidate, proxy-revalidate\";\n\t\t\treply.headers.pragma = \"no-cache\";\n\t\t\treply.headers.expires = \"0\";\n\n\t\t\t// inject app into template\n\t\t\tthis.fillTemplate(response, app, script);\n\n\t\t\t// inject head meta\n\t\t\tif (context.head) {\n\t\t\t\tresponse.html = this.headProvider.renderHead(\n\t\t\t\t\tresponse.html,\n\t\t\t\t\tcontext.head,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// TODO: hook for plugins \"react:server:template\"\n\t\t\t// { response: { html: string }, request, state }\n\n\t\t\treturn response.html;\n\t\t};\n\t}\n\n\tfillTemplate(response: { html: string }, app: string, script: string) {\n\t\tif (this.ROOT_DIV_REGEX.test(response.html)) {\n\t\t\t// replace contents of the existing <div id=\"root\">\n\t\t\tresponse.html = response.html.replace(\n\t\t\t\tthis.ROOT_DIV_REGEX,\n\t\t\t\t(_match, beforeId, afterId) => {\n\t\t\t\t\treturn `<div${beforeId} id=\"${this.env.REACT_ROOT_ID}\"${afterId}>${app}</div>`;\n\t\t\t\t},\n\t\t\t);\n\t\t} else {\n\t\t\tconst bodyOpenTag = /<body([^>]*)>/i;\n\t\t\tif (bodyOpenTag.test(response.html)) {\n\t\t\t\tresponse.html = response.html.replace(bodyOpenTag, (match) => {\n\t\t\t\t\treturn `${match}\\n<div id=\"${this.env.REACT_ROOT_ID}\">${app}</div>`;\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tconst bodyCloseTagRegex = /<\\/body>/i;\n\t\tif (bodyCloseTagRegex.test(response.html)) {\n\t\t\tresponse.html = response.html.replace(\n\t\t\t\tbodyCloseTagRegex,\n\t\t\t\t`${script}\\n</body>`,\n\t\t\t);\n\t\t}\n\t}\n}\n","import { $inject, Alepha, __bind } from \"@alepha/core\";\nimport {\n\tServerLinksProvider,\n\tServerModule,\n\ttype ServerRequest,\n} from \"@alepha/server\";\nimport { $page } from \"./descriptors/$page.ts\";\nimport {\n\tPageDescriptorProvider,\n\ttype PageReactContext,\n\ttype PageRequest,\n\ttype RouterState,\n} from \"./providers/PageDescriptorProvider.ts\";\nimport type { ReactHydrationState } from \"./providers/ReactBrowserProvider.ts\";\nimport { ReactServerProvider } from \"./providers/ReactServerProvider.ts\";\nexport { default as NestedView } from \"./components/NestedView.tsx\";\n\nexport * from \"./index.shared.ts\";\nexport * from \"./providers/PageDescriptorProvider.ts\";\nexport * from \"./providers/ReactBrowserProvider.ts\";\nexport * from \"./providers/ReactServerProvider.ts\";\nexport * from \"./errors/RedirectionError.ts\";\n\ndeclare module \"@alepha/core\" {\n\tinterface Hooks {\n\t\t\"react:browser:render\": {\n\t\t\tstate: RouterState;\n\t\t\tcontext: PageReactContext;\n\t\t\thydration?: ReactHydrationState;\n\t\t};\n\t\t\"react:server:render\": {\n\t\t\trequest: ServerRequest;\n\t\t\tpageRequest: PageRequest;\n\t\t};\n\n\t\t\"react:transition:begin\": {\n\t\t\tstate: RouterState;\n\t\t\tcontext: PageReactContext;\n\t\t};\n\t\t\"react:transition:success\": {\n\t\t\tstate: RouterState;\n\t\t};\n\t\t\"react:transition:error\": {\n\t\t\terror: Error;\n\t\t\tstate: RouterState;\n\t\t\tcontext: PageReactContext;\n\t\t};\n\t\t\"react:transition:end\": {\n\t\t\tstate: RouterState;\n\t\t\tcontext: PageReactContext;\n\t\t};\n\t}\n}\n\nexport class ReactModule {\n\tprotected readonly alepha = $inject(Alepha);\n\n\tconstructor() {\n\t\tthis.alepha //\n\t\t\t.with(ServerModule)\n\t\t\t.with(ServerLinksProvider)\n\t\t\t.with(PageDescriptorProvider)\n\t\t\t.with(ReactServerProvider);\n\t}\n}\n\n__bind($page, ReactModule);\n"],"names":[],"mappings":"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"}