@withl5e/l5e 0.2.2 → 0.2.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.
- package/dist/entry-server.js +142 -10
- package/dist/entry-server.js.map +1 -1
- package/dist/index-BlfNBupp.js +318 -0
- package/dist/index-BlfNBupp.js.map +1 -0
- package/dist/index.js +31 -32
- package/dist/index.js.map +1 -1
- package/dist/middleware.js +3 -5
- package/dist/render-BF7iTagJ.js +202 -0
- package/dist/render-BF7iTagJ.js.map +1 -0
- package/dist/server.js +250 -244
- package/dist/server.js.map +1 -1
- package/dist/vite-plugin.js +97 -97
- package/dist/vite-plugin.js.map +1 -1
- package/package.json +4 -1
- package/src/core/entry-server.ts +16 -7
- package/src/core/index.ts +0 -2
- package/src/core/render.ts +36 -0
- package/src/core/request.ts +11 -10
- package/src/core/server.ts +23 -9
- package/src/core/vite-plugin.ts +13 -0
- package/src/middleware/index.ts +0 -48
- package/dist/entry-server-DJzzhCmz.js +0 -224
- package/dist/entry-server-DJzzhCmz.js.map +0 -1
- package/dist/index-BIt7MJT9.js +0 -163
- package/dist/index-BIt7MJT9.js.map +0 -1
package/src/core/entry-server.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference path="./jsx-types.d.ts" />
|
|
2
|
+
import serialize from 'serialize-javascript';
|
|
2
3
|
import { MetadataRenderer } from '../seo/generateMetadata';
|
|
3
4
|
import type { Metadata } from '../seo/types';
|
|
4
5
|
import {
|
|
@@ -323,7 +324,9 @@ export async function render(url: string, requestInfo: RequestInfo = {}): Promis
|
|
|
323
324
|
// Auto-render schemas vào headRegistry
|
|
324
325
|
const schemas = getSchemas();
|
|
325
326
|
schemas.forEach((schema) => {
|
|
326
|
-
|
|
327
|
+
// serialize-javascript escapes HTML-sensitive chars (<, >, &, U+2028/2029)
|
|
328
|
+
// so schema values cannot break out of the <script> block (XSS).
|
|
329
|
+
const schemaJson = serialize(schema, { isJSON: true });
|
|
327
330
|
// Push schema vào headRegistry thông qua Head component
|
|
328
331
|
// Head component chỉ push vào registry, không cần renderJsxToHtmlString
|
|
329
332
|
Head({
|
|
@@ -379,13 +382,19 @@ export async function render(url: string, requestInfo: RequestInfo = {}): Promis
|
|
|
379
382
|
return await renderErrorView(err);
|
|
380
383
|
}
|
|
381
384
|
|
|
382
|
-
// For other errors, convert to ServiceUnavailableException
|
|
385
|
+
// For other (unexpected) errors, convert to ServiceUnavailableException.
|
|
386
|
+
// Intentional HttpExceptions above keep their developer-authored message;
|
|
387
|
+
// but an unexpected error's raw message/stack must not leak to the client
|
|
388
|
+
// in production (it can carry internals, secrets, etc.). Dev keeps detail.
|
|
383
389
|
console.error(`Failed to render:`, err);
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
390
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
391
|
+
const serviceError = isProduction
|
|
392
|
+
? new ServiceUnavailableException('Internal Server Error')
|
|
393
|
+
: new ServiceUnavailableException(err.message || 'Internal Server Error', {
|
|
394
|
+
originalError: err.name,
|
|
395
|
+
stack: err.stack,
|
|
396
|
+
timestamp: new Date().toISOString(),
|
|
397
|
+
});
|
|
389
398
|
return await renderErrorView(serviceError);
|
|
390
399
|
}
|
|
391
400
|
}, requestInfo);
|
package/src/core/index.ts
CHANGED
package/src/core/render.ts
CHANGED
|
@@ -42,6 +42,39 @@ function classList(input: unknown): string {
|
|
|
42
42
|
return '';
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// Attribute-name validation, ported verbatim from React's react-dom
|
|
46
|
+
// (DOMProperty.js `isAttributeNameSafe`). Untrusted spread props can carry
|
|
47
|
+
// keys like `blah" onclick="x` or `></div><script>…`; if such a key were
|
|
48
|
+
// written as `${key}="…"` it would inject new attributes / break out of the
|
|
49
|
+
// tag. Names that don't match this grammar are dropped instead of emitted.
|
|
50
|
+
const ATTRIBUTE_NAME_START_CHAR =
|
|
51
|
+
':A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD';
|
|
52
|
+
const ATTRIBUTE_NAME_CHAR =
|
|
53
|
+
ATTRIBUTE_NAME_START_CHAR + '\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040';
|
|
54
|
+
const VALID_ATTRIBUTE_NAME_REGEX = new RegExp(
|
|
55
|
+
'^[' + ATTRIBUTE_NAME_START_CHAR + '][' + ATTRIBUTE_NAME_CHAR + ']*$',
|
|
56
|
+
);
|
|
57
|
+
const validatedAttributeNameCache: Record<string, boolean> = {};
|
|
58
|
+
const illegalAttributeNameCache: Record<string, boolean> = {};
|
|
59
|
+
|
|
60
|
+
export function isAttributeNameSafe(attributeName: string): boolean {
|
|
61
|
+
if (Object.prototype.hasOwnProperty.call(validatedAttributeNameCache, attributeName)) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
if (Object.prototype.hasOwnProperty.call(illegalAttributeNameCache, attributeName)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) {
|
|
68
|
+
validatedAttributeNameCache[attributeName] = true;
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
illegalAttributeNameCache[attributeName] = true;
|
|
72
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
73
|
+
console.error('Invalid attribute name: `%s`', attributeName);
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
45
78
|
export function escapeProp(value: string): string {
|
|
46
79
|
return value
|
|
47
80
|
.replaceAll('&', '&')
|
|
@@ -86,6 +119,9 @@ function renderAttributes(props?: Record<string, any>): string {
|
|
|
86
119
|
key !== 'cacheTag',
|
|
87
120
|
)
|
|
88
121
|
.map(([key, value]) => {
|
|
122
|
+
// Drop attribute names that could break out of the tag (injection guard).
|
|
123
|
+
if (!isAttributeNameSafe(key)) return '';
|
|
124
|
+
|
|
89
125
|
// HTML standard behavior with function execution support
|
|
90
126
|
if (value === false || value === null || value === undefined) return '';
|
|
91
127
|
if (value === true) return key;
|
package/src/core/request.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
+
import { parse as parseCookieHeader } from 'cookie';
|
|
1
2
|
import type { Request } from 'express';
|
|
2
3
|
|
|
3
4
|
export function parseCookies(cookieHeader?: string): Record<string, string> {
|
|
4
5
|
if (!cookieHeader) return {};
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
// `cookie.parse` decodes values safely (malformed %-sequences fall back to the
|
|
8
|
+
// raw value instead of throwing) and ignores prototype keys.
|
|
9
|
+
const parsed = parseCookieHeader(cookieHeader);
|
|
10
|
+
const cookies: Record<string, string> = {};
|
|
11
|
+
for (const [name, value] of Object.entries(parsed)) {
|
|
12
|
+
if (value !== undefined) {
|
|
13
|
+
cookies[name] = value;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return cookies;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export function createHeadersFromExpressRequest(req: Request): Headers {
|
package/src/core/server.ts
CHANGED
|
@@ -4,10 +4,12 @@ import fs from 'node:fs/promises';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { pathToFileURL } from 'node:url';
|
|
6
6
|
import requestIp from 'request-ip';
|
|
7
|
+
import serialize from 'serialize-javascript';
|
|
7
8
|
import type { ViteDevServer } from 'vite';
|
|
8
9
|
import { createContext, type MiddlewareHandler, type RewritePayload } from '../middleware';
|
|
9
10
|
import { bundleCss, bundleScripts, getBundledFile } from './bundler';
|
|
10
11
|
import type { RenderResult, RequestInfo } from './entry-server';
|
|
12
|
+
import { escapeProp } from './render';
|
|
11
13
|
import { createHeadersFromExpressRequest, parseCookies } from './request';
|
|
12
14
|
|
|
13
15
|
export interface ServerOptions {
|
|
@@ -27,15 +29,18 @@ export interface ServerContext {
|
|
|
27
29
|
/**
|
|
28
30
|
* Apply or replace lang attribute on <html> tag
|
|
29
31
|
*/
|
|
30
|
-
function applyHtmlLang(template: string, lang: string): string {
|
|
32
|
+
export function applyHtmlLang(template: string, lang: string): string {
|
|
33
|
+
// Escape so a loader-supplied lang (possibly derived from user input) cannot
|
|
34
|
+
// break out of the attribute / <html> tag (XSS). escapeProp handles ", <, >, &.
|
|
35
|
+
const safeLang = escapeProp(lang);
|
|
31
36
|
return template.replace(/<html\b([^>]*)>/i, (match, attrs) => {
|
|
32
37
|
// Check if lang already exists
|
|
33
38
|
if (/\blang\s*=/i.test(attrs)) {
|
|
34
|
-
// Replace existing lang value
|
|
35
|
-
return match.replace(/lang\s*=\s*"[^"]*"/i, `lang="${
|
|
39
|
+
// Replace existing lang value (function replacer avoids $-pattern issues)
|
|
40
|
+
return match.replace(/lang\s*=\s*"[^"]*"/i, () => `lang="${safeLang}"`);
|
|
36
41
|
} else {
|
|
37
42
|
// Add lang attribute
|
|
38
|
-
return `<html lang="${
|
|
43
|
+
return `<html lang="${safeLang}"${attrs}>`;
|
|
39
44
|
}
|
|
40
45
|
});
|
|
41
46
|
}
|
|
@@ -312,7 +317,7 @@ async function createPageResponse({
|
|
|
312
317
|
}
|
|
313
318
|
}
|
|
314
319
|
if (Object.keys(islandMap).length > 0) {
|
|
315
|
-
islandRegistryScript = `<script>window.__L5E_ISLANDS__=${
|
|
320
|
+
islandRegistryScript = `<script>window.__L5E_ISLANDS__=${serialize(islandMap)}</script>`;
|
|
316
321
|
}
|
|
317
322
|
}
|
|
318
323
|
|
|
@@ -341,7 +346,7 @@ async function createPageResponse({
|
|
|
341
346
|
for (const island of islandEntries) {
|
|
342
347
|
islandMap[island.key] = `/${island.src}`;
|
|
343
348
|
}
|
|
344
|
-
islandRegistryScript = `<script>window.__L5E_ISLANDS__=${
|
|
349
|
+
islandRegistryScript = `<script>window.__L5E_ISLANDS__=${serialize(islandMap)}</script>`;
|
|
345
350
|
}
|
|
346
351
|
}
|
|
347
352
|
|
|
@@ -463,7 +468,7 @@ export async function createServer(options: ServerOptions = {}): Promise<ServerC
|
|
|
463
468
|
res.send(bundledFile.content);
|
|
464
469
|
} catch (e: any) {
|
|
465
470
|
console.error('[server] Error serving bundled file:', e);
|
|
466
|
-
res.status(500).end(
|
|
471
|
+
res.status(500).end('Internal server error');
|
|
467
472
|
}
|
|
468
473
|
},
|
|
469
474
|
);
|
|
@@ -558,6 +563,14 @@ export async function createServer(options: ServerOptions = {}): Promise<ServerC
|
|
|
558
563
|
return res.status(404).send('Action not found');
|
|
559
564
|
}
|
|
560
565
|
|
|
566
|
+
// Enforce the action's declared HTTP method. Previously `app.all` accepted
|
|
567
|
+
// any method and `action.method` was ignored, so a state-changing POST
|
|
568
|
+
// action could be triggered via GET (e.g. <img src>) — a CSRF vector.
|
|
569
|
+
const allowedMethod = (action.method || 'GET').toUpperCase();
|
|
570
|
+
if (req.method.toUpperCase() !== allowedMethod) {
|
|
571
|
+
return res.status(405).set('Allow', allowedMethod).send('Method Not Allowed');
|
|
572
|
+
}
|
|
573
|
+
|
|
561
574
|
// Build RequestInfo (same pattern as HTML handler)
|
|
562
575
|
const fullUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}`;
|
|
563
576
|
const urlObject = new URL(fullUrl);
|
|
@@ -687,8 +700,9 @@ export async function createServer(options: ServerOptions = {}): Promise<ServerC
|
|
|
687
700
|
await sendWebResponse(req, res, response);
|
|
688
701
|
} catch (e: any) {
|
|
689
702
|
vite?.ssrFixStacktrace?.(e);
|
|
690
|
-
console.
|
|
691
|
-
|
|
703
|
+
console.error(e.stack);
|
|
704
|
+
// Never leak stack traces to the client in production (info disclosure).
|
|
705
|
+
res.status(500).end(isProduction ? 'Internal Server Error' : e.stack);
|
|
692
706
|
}
|
|
693
707
|
});
|
|
694
708
|
|
package/src/core/vite-plugin.ts
CHANGED
|
@@ -19,6 +19,15 @@ const VIRTUAL_L5E_ISLAND_STRATEGIES = 'virtual:l5e-island-strategies';
|
|
|
19
19
|
const VIRTUAL_L5E_ACTIONS = 'virtual:l5e-actions';
|
|
20
20
|
const VIRTUAL_L5E_MIDDLEWARE = 'virtual:l5e-middleware';
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Vite's built-in env vars on `import.meta.env`. These are statically replaced
|
|
24
|
+
* by Vite itself (DEV/PROD/SSR are booleans, MODE/BASE_URL strings), so the SSR
|
|
25
|
+
* `import.meta.env.* -> process.env.*` rewrite must skip them — otherwise it
|
|
26
|
+
* clobbers Vite defaults (e.g. `import.meta.env.DEV` would become the undefined
|
|
27
|
+
* `process.env.DEV`).
|
|
28
|
+
*/
|
|
29
|
+
const VITE_RESERVED_ENV = new Set(['MODE', 'BASE_URL', 'PROD', 'DEV', 'SSR', 'LEGACY']);
|
|
30
|
+
|
|
22
31
|
/**
|
|
23
32
|
* Recursively scan directory for .tsx and .ts files
|
|
24
33
|
*/
|
|
@@ -671,6 +680,8 @@ export function coreVite(): Plugin {
|
|
|
671
680
|
transformedCode = transformedCode.replace(
|
|
672
681
|
/import\.meta\.env\.([a-zA-Z_][a-zA-Z0-9_]*)/g,
|
|
673
682
|
(match, varName) => {
|
|
683
|
+
// Leave Vite's built-in env vars for Vite to handle.
|
|
684
|
+
if (VITE_RESERVED_ENV.has(varName)) return match;
|
|
674
685
|
hasChanges = true;
|
|
675
686
|
return `process.env.${varName}`;
|
|
676
687
|
},
|
|
@@ -682,6 +693,8 @@ export function coreVite(): Plugin {
|
|
|
682
693
|
transformedCode = transformedCode.replace(
|
|
683
694
|
/import\.meta\.env\[(['"`])([^'"`]+)\1\]/g,
|
|
684
695
|
(match, quote, varName) => {
|
|
696
|
+
// Leave Vite's built-in env vars for Vite to handle.
|
|
697
|
+
if (VITE_RESERVED_ENV.has(varName)) return match;
|
|
685
698
|
hasChanges = true;
|
|
686
699
|
return `process.env[${quote}${varName}${quote}]`;
|
|
687
700
|
},
|
package/src/middleware/index.ts
CHANGED
|
@@ -72,54 +72,6 @@ export function createContext({
|
|
|
72
72
|
return context;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
export function isLocalsSerializable(value: unknown): boolean {
|
|
76
|
-
const stack: unknown[] = [value];
|
|
77
|
-
|
|
78
|
-
while (stack.length > 0) {
|
|
79
|
-
const current = stack.pop();
|
|
80
|
-
const type = typeof current;
|
|
81
|
-
|
|
82
|
-
if (current === null || type === 'string' || type === 'number' || type === 'boolean') {
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (Array.isArray(current)) {
|
|
87
|
-
stack.push(...current);
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (type === 'object' && isPlainObject(current)) {
|
|
92
|
-
stack.push(...Object.values(current as Record<string, unknown>));
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return true;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function isPlainObject(value: unknown): value is object {
|
|
103
|
-
if (typeof value !== 'object' || value === null) return false;
|
|
104
|
-
|
|
105
|
-
const proto = Object.getPrototypeOf(value);
|
|
106
|
-
if (proto === null) return true;
|
|
107
|
-
|
|
108
|
-
let baseProto = proto;
|
|
109
|
-
while (Object.getPrototypeOf(baseProto) !== null) {
|
|
110
|
-
baseProto = Object.getPrototypeOf(baseProto);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return proto === baseProto;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export function trySerializeLocals(value: unknown): string {
|
|
117
|
-
if (isLocalsSerializable(value)) {
|
|
118
|
-
return JSON.stringify(value);
|
|
119
|
-
}
|
|
120
|
-
throw new Error("The passed value can't be serialized.");
|
|
121
|
-
}
|
|
122
|
-
|
|
123
75
|
function stringifyRewritePayload(payload: RewritePayload, currentUrl: URL): string {
|
|
124
76
|
if (payload instanceof Request) {
|
|
125
77
|
return payload.url;
|
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import { M as K } from "./generateMetadata-DLMaI0EO.js";
|
|
2
|
-
import { N as U, I as M, R as W, H as Y, S as z } from "./exceptions-CHPz01DJ.js";
|
|
3
|
-
import { R as Q, b as v, o as X, s as V, p as H, l as R, k as h, i as Z, a as q, H as tt, d as O, e as D, h as et, g as at, f as _ } from "./jsx-runtime-DVQ9eOji.js";
|
|
4
|
-
import { viewLoaders as j, viewComponents as m } from "virtual:l5e-views";
|
|
5
|
-
import rt from "virtual:l5e-route";
|
|
6
|
-
import { globalLoader as L } from "virtual:l5e-global-loader";
|
|
7
|
-
import "virtual:l5e-middleware";
|
|
8
|
-
console.log("render.ts");
|
|
9
|
-
const st = /* @__PURE__ */ new Set([
|
|
10
|
-
"area",
|
|
11
|
-
"base",
|
|
12
|
-
"br",
|
|
13
|
-
"col",
|
|
14
|
-
"embed",
|
|
15
|
-
"hr",
|
|
16
|
-
"img",
|
|
17
|
-
"input",
|
|
18
|
-
"link",
|
|
19
|
-
"meta",
|
|
20
|
-
"param",
|
|
21
|
-
"source",
|
|
22
|
-
"track",
|
|
23
|
-
"wbr"
|
|
24
|
-
]);
|
|
25
|
-
function J(t) {
|
|
26
|
-
return t ? typeof t == "string" ? t : Array.isArray(t) ? t.map(J).filter(Boolean).join(" ") : typeof t == "object" ? Object.keys(t).filter((s) => t[s]).join(" ") : "" : "";
|
|
27
|
-
}
|
|
28
|
-
function y(t) {
|
|
29
|
-
return t.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<").replaceAll(">", ">").replaceAll(`
|
|
30
|
-
`, " ").trim();
|
|
31
|
-
}
|
|
32
|
-
function N(t) {
|
|
33
|
-
return t.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">").replaceAll(`
|
|
34
|
-
`, "<br/>");
|
|
35
|
-
}
|
|
36
|
-
function ot(t) {
|
|
37
|
-
if (!t) return "";
|
|
38
|
-
let s = "";
|
|
39
|
-
if (t.classList !== void 0 && (s = J(t.classList)), t.class) {
|
|
40
|
-
const a = typeof t.class == "string" ? t.class : "";
|
|
41
|
-
s = s ? `${s} ${a}`.trim() : a;
|
|
42
|
-
}
|
|
43
|
-
const r = Object.entries(t).filter(
|
|
44
|
-
([a]) => a !== "children" && a !== "classList" && a !== "setHtml" && a !== "setText" && a !== "class" && a !== "key" && a !== "cacheTag"
|
|
45
|
-
).map(([a, n]) => {
|
|
46
|
-
if (n === !1 || n === null || n === void 0) return "";
|
|
47
|
-
if (n === !0) return a;
|
|
48
|
-
if (typeof n == "function") {
|
|
49
|
-
const i = n();
|
|
50
|
-
return `${a}="${y(i?.toString() || "")}"`;
|
|
51
|
-
}
|
|
52
|
-
return `${a}="${y(n?.toString() || "")}"`;
|
|
53
|
-
}).filter(Boolean);
|
|
54
|
-
return s && r.push(`class="${y(s)}"`), r.join(" ");
|
|
55
|
-
}
|
|
56
|
-
function w(t) {
|
|
57
|
-
if (t === null || typeof t == "boolean" || t === void 0) return "";
|
|
58
|
-
if (t && typeof t == "object" && t[Q])
|
|
59
|
-
return t.content || "";
|
|
60
|
-
if (t && typeof t == "object" && "htmlContent" in t)
|
|
61
|
-
return t.htmlContent;
|
|
62
|
-
if (typeof t == "string") return N(t.toString());
|
|
63
|
-
if (typeof t == "number") return t.toString();
|
|
64
|
-
if (Array.isArray(t))
|
|
65
|
-
return t.map((o) => w(o)).join("");
|
|
66
|
-
const { type: s, props: r, children: a } = t;
|
|
67
|
-
if (r?.cacheTag && v(r.cacheTag), typeof s == "function") {
|
|
68
|
-
const o = s({ ...r || {}, children: a });
|
|
69
|
-
return o && typeof o == "object" && "string" in o ? o.string : w(o);
|
|
70
|
-
}
|
|
71
|
-
const n = ot(r);
|
|
72
|
-
if (st.has(s.toLowerCase()))
|
|
73
|
-
return `<${s}${n ? " " + n : ""} />`;
|
|
74
|
-
let c = "";
|
|
75
|
-
return r?.setHtml !== void 0 ? c = r.setHtml?.toString() || "" : r?.setText !== void 0 ? c = N(r.setText?.toString() || "") : c = a.map((o) => w(o)).join(""), `<${s}${n ? " " + n : ""}>${c}</${s}>`;
|
|
76
|
-
}
|
|
77
|
-
function f(t) {
|
|
78
|
-
return t && typeof t == "object" && "string" in t ? t.string : w(t);
|
|
79
|
-
}
|
|
80
|
-
function ht(t) {
|
|
81
|
-
return t && typeof t == "object" && "string" in t ? t : {
|
|
82
|
-
string: w(t)
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
async function P(t, s) {
|
|
86
|
-
const r = "_error", a = `/src/views/${r}/index.tsx`;
|
|
87
|
-
V(r);
|
|
88
|
-
const n = m[a] ? await m[a]() : null;
|
|
89
|
-
if (n?.default) {
|
|
90
|
-
const i = {
|
|
91
|
-
statusCode: t.statusCode,
|
|
92
|
-
message: t.message,
|
|
93
|
-
data: t.data
|
|
94
|
-
}, c = n.default, o = f(h(c, i)), b = O(), E = D(), g = _(), S = g.length > 0 ? g.map((l) => f(l)).join(`
|
|
95
|
-
`) : void 0;
|
|
96
|
-
return {
|
|
97
|
-
html: o,
|
|
98
|
-
scripts: b.map((l) => l.path),
|
|
99
|
-
styles: E.map((l) => l.path),
|
|
100
|
-
head: S,
|
|
101
|
-
lang: s,
|
|
102
|
-
statusCode: t.statusCode
|
|
103
|
-
};
|
|
104
|
-
} else {
|
|
105
|
-
const i = h("div", {}, `${t.statusCode} - ${t.message}`);
|
|
106
|
-
return {
|
|
107
|
-
html: f(i),
|
|
108
|
-
statusCode: t.statusCode
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
async function mt(t, s = {}) {
|
|
113
|
-
return X(async () => {
|
|
114
|
-
try {
|
|
115
|
-
const r = await rt(s);
|
|
116
|
-
if (!r)
|
|
117
|
-
throw new U("Page not found", {
|
|
118
|
-
path: s.path,
|
|
119
|
-
pathname: s.pathname,
|
|
120
|
-
url: s.url?.href
|
|
121
|
-
});
|
|
122
|
-
const a = typeof r == "string" ? r : r.view;
|
|
123
|
-
s.params = typeof r == "string" ? {} : r.params ?? {}, V(a);
|
|
124
|
-
let n = {}, i;
|
|
125
|
-
const c = "/src/global-loader.ts", o = L[c] ? await L[c]() : null;
|
|
126
|
-
if (o?.loader)
|
|
127
|
-
if (o.shouldIgnore?.(a) || !1)
|
|
128
|
-
console.info(`Global loader ignored for view: ${a}`);
|
|
129
|
-
else {
|
|
130
|
-
const p = await o.loader(s);
|
|
131
|
-
n = p.props || {}, i = p.lang, p.cacheTags && v(p.cacheTags);
|
|
132
|
-
}
|
|
133
|
-
let b = {}, E, g, S, l = !1;
|
|
134
|
-
const x = `/src/views/${a}/loader.ts`, d = j[x] ? await j[x]() : null;
|
|
135
|
-
if (d?.loader) {
|
|
136
|
-
const e = await d.loader(s);
|
|
137
|
-
if (e.rawResponse)
|
|
138
|
-
return {
|
|
139
|
-
rawResponse: e.rawResponse,
|
|
140
|
-
statusCode: e.rawResponse.statusCode || 200
|
|
141
|
-
};
|
|
142
|
-
b = e.props || {}, E = e.maxAge, g = e.sMaxAge, S = e.swr, l = e.rawHtml || !1, e.cacheTags && v(e.cacheTags), e.lang && (i = e.lang);
|
|
143
|
-
} else
|
|
144
|
-
console.info(`No loader for view: ${a}`);
|
|
145
|
-
const u = { ...n, ...b };
|
|
146
|
-
if (o?.generateMetadata) {
|
|
147
|
-
const e = o.generateMetadata(s, u);
|
|
148
|
-
e && H(e);
|
|
149
|
-
}
|
|
150
|
-
if (d?.generateMetadata) {
|
|
151
|
-
const e = d.generateMetadata(s, u);
|
|
152
|
-
e && H(e);
|
|
153
|
-
}
|
|
154
|
-
if (o?.generateSchema) {
|
|
155
|
-
const e = o.generateSchema(s, u);
|
|
156
|
-
e && R(e);
|
|
157
|
-
}
|
|
158
|
-
if (d?.generateSchema) {
|
|
159
|
-
const e = d.generateSchema(s, u);
|
|
160
|
-
e && R(e);
|
|
161
|
-
}
|
|
162
|
-
const C = `/src/views/${a}/index.tsx`, A = (m[C] ? await m[C]() : null)?.default;
|
|
163
|
-
if (!A)
|
|
164
|
-
throw console.error(`View component not found: ${a}`), process.env.NODE_ENV !== "production" ? new M(`View component not found: "${a}"`, {
|
|
165
|
-
viewName: a,
|
|
166
|
-
expectedPath: C,
|
|
167
|
-
availableViews: Object.keys(m),
|
|
168
|
-
hint: `Make sure the view component exists at ${C} and exports a default component`,
|
|
169
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
170
|
-
}) : new M("Internal Server Error");
|
|
171
|
-
f(h(K, {})), Z().forEach((e) => {
|
|
172
|
-
const p = JSON.stringify(e);
|
|
173
|
-
q({
|
|
174
|
-
priority: tt.SEO,
|
|
175
|
-
children: h("script", {
|
|
176
|
-
type: "application/ld+json",
|
|
177
|
-
setHtml: p
|
|
178
|
-
})
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
const B = f(h(A, u)), I = O(), F = D(), T = et(), k = at(), $ = _(), G = $.length > 0 ? $.map((e) => f(e)).join(`
|
|
182
|
-
`) : void 0;
|
|
183
|
-
return {
|
|
184
|
-
html: B,
|
|
185
|
-
scripts: I.map((e) => e.path),
|
|
186
|
-
styles: F.map((e) => e.path),
|
|
187
|
-
islands: T.length > 0 ? T : void 0,
|
|
188
|
-
head: G,
|
|
189
|
-
lang: i,
|
|
190
|
-
maxAge: E,
|
|
191
|
-
sMaxAge: g,
|
|
192
|
-
swr: S,
|
|
193
|
-
cacheTags: k,
|
|
194
|
-
rawHtml: l
|
|
195
|
-
};
|
|
196
|
-
} catch (r) {
|
|
197
|
-
if (r instanceof W)
|
|
198
|
-
return {
|
|
199
|
-
html: "",
|
|
200
|
-
redirect: {
|
|
201
|
-
url: r.url,
|
|
202
|
-
statusCode: r.statusCode
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
if (r instanceof Y)
|
|
206
|
-
return await P(r);
|
|
207
|
-
console.error("Failed to render:", r);
|
|
208
|
-
const a = new z(r.message || "Internal Server Error", {
|
|
209
|
-
originalError: r.name,
|
|
210
|
-
stack: r.stack,
|
|
211
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
212
|
-
});
|
|
213
|
-
return await P(a);
|
|
214
|
-
}
|
|
215
|
-
}, s);
|
|
216
|
-
}
|
|
217
|
-
export {
|
|
218
|
-
y as a,
|
|
219
|
-
f as b,
|
|
220
|
-
ht as c,
|
|
221
|
-
N as e,
|
|
222
|
-
mt as r
|
|
223
|
-
};
|
|
224
|
-
//# sourceMappingURL=entry-server-DJzzhCmz.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"entry-server-DJzzhCmz.js","sources":["../src/core/render.ts","../src/core/entry-server.ts"],"sourcesContent":["import { RAW_HTML_MARKER } from './const';\nimport {\n addCacheTag,\n type HtmlContentObject,\n type JSXChild,\n type JSXNode,\n type RenderedNode,\n} from './jsx-runtime';\n\nif (true) {\n console.log('render.ts');\n}\n\nconst VOID_ELEMENTS = new Set<string>([\n 'area',\n 'base',\n 'br',\n 'col',\n 'embed',\n 'hr',\n 'img',\n 'input',\n 'link',\n 'meta',\n 'param',\n 'source',\n 'track',\n 'wbr',\n]);\n\nfunction classList(input: unknown): string {\n if (!input) return '';\n if (typeof input === 'string') return input;\n if (Array.isArray(input)) {\n return input.map(classList).filter(Boolean).join(' ');\n }\n if (typeof input === 'object') {\n return Object.keys(input as Record<string, boolean>)\n .filter((key) => (input as Record<string, boolean>)[key])\n .join(' ');\n }\n return '';\n}\n\nexport function escapeProp(value: string): string {\n return value\n .replaceAll('&', '&')\n .replaceAll('\"', '"')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\\n', ' ')\n .trim();\n}\n\nexport function escapeHTML(value: string): string {\n return value\n .replaceAll('&', '&')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\\n', '<br/>');\n}\n\nfunction renderAttributes(props?: Record<string, any>): string {\n if (!props) return '';\n\n let classValue = '';\n if (props.classList !== undefined) {\n classValue = classList(props.classList);\n }\n if (props.class) {\n const existingClass = typeof props.class === 'string' ? props.class : '';\n classValue = classValue ? `${classValue} ${existingClass}`.trim() : existingClass;\n }\n\n const attrs = Object.entries(props)\n .filter(\n ([key]) =>\n key !== 'children' &&\n key !== 'classList' &&\n key !== 'setHtml' &&\n key !== 'setText' &&\n key !== 'class' &&\n key !== 'key' &&\n key !== 'cacheTag',\n )\n .map(([key, value]) => {\n // HTML standard behavior with function execution support\n if (value === false || value === null || value === undefined) return '';\n if (value === true) return key;\n\n // Execute function props and get result (new feature)\n if (typeof value === 'function') {\n const result = value();\n return `${key}=\"${escapeProp(result?.toString() || '')}\"`;\n }\n\n return `${key}=\"${escapeProp(value?.toString() || '')}\"`;\n })\n .filter(Boolean) as string[];\n\n if (classValue) {\n attrs.push(`class=\"${escapeProp(classValue)}\"`);\n }\n\n return attrs.join(' ');\n}\n\nfunction renderJsxToHtmlStringInternal(element: JSXChild): string {\n // HTML standard: skip boolean, null, undefined children\n if (element === null || typeof element === 'boolean' || element === undefined) return '';\n\n if (element && typeof element === 'object' && (element as any)[RAW_HTML_MARKER]) {\n return (element as any).content || '';\n }\n\n // Support htmlContent object (new feature)\n if (element && typeof element === 'object' && 'htmlContent' in element) {\n return (element as HtmlContentObject).htmlContent;\n }\n\n if (typeof element === 'string') return escapeHTML(element.toString());\n if (typeof element === 'number') return element.toString();\n\n if (Array.isArray(element)) {\n return (element as JSXChild[]).map((el) => renderJsxToHtmlStringInternal(el)).join('');\n }\n\n const { type, props, children } = element as JSXNode;\n\n // Collect cache tags from props\n if (props?.cacheTag) {\n addCacheTag(props.cacheTag);\n }\n\n if (typeof type === 'function') {\n const componentResult = (type as any)({ ...(props || {}), children });\n // Check if component returned a RenderedNode\n if (componentResult && typeof componentResult === 'object' && 'string' in componentResult) {\n return (componentResult as RenderedNode).string;\n }\n return renderJsxToHtmlStringInternal(componentResult as JSXChild);\n }\n\n const attrs = renderAttributes(props);\n const isVoidElement = VOID_ELEMENTS.has((type as string).toLowerCase());\n\n if (isVoidElement) {\n return `<${type}${attrs ? ' ' + attrs : ''} />`;\n }\n\n let childrenHtml = '';\n if (props?.setHtml !== undefined) {\n childrenHtml = props.setHtml?.toString() || '';\n } else if (props?.setText !== undefined) {\n childrenHtml = escapeHTML(props.setText?.toString() || '');\n } else {\n childrenHtml = (children as JSXChild[]).map((el) => renderJsxToHtmlStringInternal(el)).join('');\n }\n\n return `<${type}${attrs ? ' ' + attrs : ''}>${childrenHtml}</${type}>`;\n}\n\nexport function renderJsxToHtmlString(element: JSXChild | RenderedNode): string {\n // If already a RenderedNode, return its string\n if (element && typeof element === 'object' && 'string' in element) {\n return (element as RenderedNode).string;\n }\n return renderJsxToHtmlStringInternal(element as JSXChild);\n}\n\nexport function renderToRenderedNode(element: JSXChild | RenderedNode): RenderedNode {\n // If already a RenderedNode, return as is\n if (element && typeof element === 'object' && 'string' in element) {\n return element as RenderedNode;\n }\n return {\n string: renderJsxToHtmlStringInternal(element as JSXChild),\n };\n}\n","/// <reference path=\"./jsx-types.d.ts\" />\nimport { MetadataRenderer } from '../seo/generateMetadata';\nimport type { Metadata } from '../seo/types';\nimport {\n HttpException,\n InternalServerErrorException,\n NotFoundException,\n RedirectException,\n ServiceUnavailableException,\n} from './exceptions';\nimport { HEAD_PRIORITY } from './head-priority';\nimport {\n addCacheTag,\n getCacheTags,\n getClientJsEntries,\n getCssEntries,\n getHeadContent,\n getIslandEntries,\n getSchemas,\n jsxFactory as h,\n Head,\n pushMetadata,\n pushSchema,\n runInRenderContext,\n setViewName,\n} from './jsx-runtime';\nimport { renderJsxToHtmlString } from './render';\n// @ts-ignore - Virtual modules provided by Vite plugin\nimport { viewComponents, viewLoaders } from 'virtual:l5e-views';\n// @ts-ignore - Virtual modules provided by Vite plugin\nimport routeHandler from 'virtual:l5e-route';\n// @ts-ignore - Virtual modules provided by Vite plugin\nimport { globalLoader } from 'virtual:l5e-global-loader';\n// @ts-ignore - Virtual modules provided by Vite plugin\nexport { loadMiddleware } from 'virtual:l5e-middleware';\n\nexport interface RawResponse {\n body: string | Buffer;\n contentType: string;\n statusCode?: number;\n headers?: Record<string, string>;\n}\n\nexport interface RenderResult {\n html?: string;\n scripts?: string[];\n styles?: string[];\n islands?: Array<{ key: string; src: string; name: string }>;\n head?: string;\n lang?: string;\n statusCode?: number;\n maxAge?: number;\n sMaxAge?: number;\n swr?: number;\n cacheTags?: string[];\n redirect?: { url: string; statusCode: number };\n rawResponse?: RawResponse;\n rawHtml?: boolean;\n}\n\nexport interface RequestInfo {\n url?: URL;\n path?: string;\n pathname?: string;\n method?: string;\n headers?: Record<string, any>;\n cookies?: Record<string, string>;\n query?: Record<string, any>;\n ip?: string;\n locals?: Record<string, unknown>;\n params?: Record<string, any>;\n}\n\nexport type RouteResult =\n | string\n | null\n | { view: string; params?: Record<string, any> };\n\n// SchemaMarkup type - có thể là single schema hoặc array of schemas\n// Sử dụng any để tương thích với schema-dts types từ frontend\nexport type SchemaMarkup = any | Array<any>;\n\nexport interface LoaderResult {\n props?: Record<string, any>;\n lang?: string;\n maxAge?: number;\n sMaxAge?: number;\n swr?: number;\n cacheTags?: string[] | Record<string, boolean>;\n rawResponse?: RawResponse;\n rawHtml?: boolean;\n}\n\nexport type LoaderFunction = (requestInfo: RequestInfo) => Promise<LoaderResult>;\n\nexport type GenerateMetadataFunction = (requestInfo: RequestInfo, props: any) => Metadata | null;\n\nexport type GenerateSchemaFunction = (requestInfo: RequestInfo, props: any) => SchemaMarkup | null;\n\nexport interface GlobalLoaderModule {\n loader: LoaderFunction;\n generateMetadata?: GenerateMetadataFunction;\n generateSchema?: GenerateSchemaFunction;\n shouldIgnore?: (viewName: string) => boolean;\n}\n\n/**\n * Helper function to render error view\n */\nasync function renderErrorView(err: HttpException, lang?: string): Promise<RenderResult> {\n const errorViewName = `_error`;\n const errorComponentPath = `/src/views/${errorViewName}/index.tsx`;\n\n // Set error view name in context\n setViewName(errorViewName);\n\n // Try to load error view\n const errorComponentModule = viewComponents[errorComponentPath]\n ? await viewComponents[errorComponentPath]()\n : null;\n\n if (errorComponentModule?.default) {\n // Render error view with exception data\n const errorProps = {\n statusCode: err.statusCode,\n message: err.message,\n data: err.data,\n };\n\n const Component = errorComponentModule.default;\n const htmlBody = renderJsxToHtmlString(h(Component, errorProps));\n const clientEntries = getClientJsEntries();\n const cssEntries = getCssEntries();\n const headContent = getHeadContent();\n\n const headHtml =\n headContent.length > 0\n ? headContent.map((content) => renderJsxToHtmlString(content)).join('\\n ')\n : undefined;\n\n return {\n html: htmlBody,\n scripts: clientEntries.map((entry) => entry.path),\n styles: cssEntries.map((entry) => entry.path),\n head: headHtml,\n lang,\n statusCode: err.statusCode,\n };\n } else {\n // No error view found, render default error message\n const html = h('div', {}, `${err.statusCode} - ${err.message}`);\n return {\n html: renderJsxToHtmlString(html),\n statusCode: err.statusCode,\n };\n }\n}\n\nexport async function render(url: string, requestInfo: RequestInfo = {}): Promise<RenderResult> {\n return runInRenderContext(async () => {\n try {\n // Step 1: Call route handler to get view name\n const rawRouteResult: RouteResult = await routeHandler(requestInfo);\n\n if (!rawRouteResult) {\n // Throw NotFoundException to render error_404 view\n throw new NotFoundException('Page not found', {\n path: requestInfo.path,\n pathname: requestInfo.pathname,\n url: requestInfo.url?.href,\n });\n }\n\n const viewName =\n typeof rawRouteResult === 'string' ? rawRouteResult : rawRouteResult.view;\n requestInfo.params =\n typeof rawRouteResult === 'string' ? {} : (rawRouteResult.params ?? {});\n\n // Set view name in render context\n setViewName(viewName);\n\n // Step 2: Load global loader (optional)\n let globalProps: Record<string, any> = {};\n let lang: string | undefined;\n\n // Try to load global loader\n const globalLoaderPathTs = '/src/global-loader.ts';\n\n const globalLoaderModule: GlobalLoaderModule | null = globalLoader[globalLoaderPathTs]\n ? await globalLoader[globalLoaderPathTs]()\n : null;\n\n // Run global loader if exists and not ignored\n if (globalLoaderModule?.loader) {\n const shouldIgnore = globalLoaderModule.shouldIgnore?.(viewName) || false;\n\n if (!shouldIgnore) {\n const globalLoaderResult = await globalLoaderModule.loader(requestInfo);\n\n globalProps = globalLoaderResult.props || {};\n lang = globalLoaderResult.lang; // Extract lang from global loader\n\n if (globalLoaderResult.cacheTags) {\n addCacheTag(globalLoaderResult.cacheTags);\n }\n\n // generateMetadata và generateSchema sẽ được gọi sau khi có props\n } else {\n console.info(`Global loader ignored for view: ${viewName}`);\n }\n }\n\n // Step 3: Dynamic import view loader (optional)\n let viewProps: Record<string, any> = {};\n let maxAge: number | undefined;\n let sMaxAge: number | undefined;\n let swr: number | undefined;\n let rawHtml: boolean = false;\n const loaderPathTs = `/src/views/${viewName}/loader.ts`;\n\n // Try TypeScript loader formats only\n const loaderModule = viewLoaders[loaderPathTs] ? await viewLoaders[loaderPathTs]() : null;\n\n if (loaderModule?.loader) {\n const loaderResult = await loaderModule.loader(requestInfo);\n\n // Check if loader returns raw response\n if (loaderResult.rawResponse) {\n return {\n rawResponse: loaderResult.rawResponse,\n statusCode: loaderResult.rawResponse.statusCode || 200,\n };\n }\n\n viewProps = loaderResult.props || {};\n maxAge = loaderResult.maxAge;\n sMaxAge = loaderResult.sMaxAge;\n swr = loaderResult.swr;\n rawHtml = loaderResult.rawHtml || false;\n\n if (loaderResult.cacheTags) {\n addCacheTag(loaderResult.cacheTags);\n }\n\n // View loader lang overrides global loader lang\n if (loaderResult.lang) {\n lang = loaderResult.lang;\n }\n } else {\n console.info(`No loader for view: ${viewName}`);\n }\n\n // Merge props: global props first, then view props (view can override)\n const props = { ...globalProps, ...viewProps };\n\n // Step 3.5: Generate metadata và schema từ generateMetadata và generateSchema functions\n // Global generateMetadata (parent metadata)\n if (globalLoaderModule?.generateMetadata) {\n const globalMetadata = globalLoaderModule.generateMetadata(requestInfo, props);\n if (globalMetadata) {\n pushMetadata(globalMetadata);\n }\n }\n\n // View generateMetadata (child metadata, sẽ merge với parent)\n if (loaderModule?.generateMetadata) {\n const viewMetadata = loaderModule.generateMetadata(requestInfo, props);\n if (viewMetadata) {\n pushMetadata(viewMetadata);\n }\n }\n\n // Global generateSchema (base schemas)\n if (globalLoaderModule?.generateSchema) {\n const globalSchema = globalLoaderModule.generateSchema(requestInfo, props);\n if (globalSchema) {\n pushSchema(globalSchema);\n }\n }\n\n // View generateSchema (view-specific schemas)\n if (loaderModule?.generateSchema) {\n const viewSchema = loaderModule.generateSchema(requestInfo, props);\n if (viewSchema) {\n pushSchema(viewSchema);\n }\n }\n\n // Step 4: Dynamic import component (required)\n const componentPathTsx = `/src/views/${viewName}/index.tsx`;\n\n const componentModule = viewComponents[componentPathTsx]\n ? await viewComponents[componentPathTsx]()\n : null;\n\n const Component = componentModule?.default;\n\n if (!Component) {\n console.error(`View component not found: ${viewName}`);\n\n // Check if in development or production mode\n const isDevelopment = process.env.NODE_ENV !== 'production';\n\n if (isDevelopment) {\n // Development: provide detailed error information\n throw new InternalServerErrorException(`View component not found: \"${viewName}\"`, {\n viewName,\n expectedPath: componentPathTsx,\n availableViews: Object.keys(viewComponents),\n hint: `Make sure the view component exists at ${componentPathTsx} and exports a default component`,\n timestamp: new Date().toISOString(),\n });\n } else {\n // Production: simple error message\n throw new InternalServerErrorException('Internal Server Error');\n }\n }\n\n // Auto-render MetadataRenderer trước khi render component\n // MetadataRenderer sẽ push metadata vào headRegistry thông qua Head component\n renderJsxToHtmlString(h(MetadataRenderer, {}));\n\n // Auto-render schemas vào headRegistry\n const schemas = getSchemas();\n schemas.forEach((schema) => {\n const schemaJson = JSON.stringify(schema);\n // Push schema vào headRegistry thông qua Head component\n // Head component chỉ push vào registry, không cần renderJsxToHtmlString\n Head({\n priority: HEAD_PRIORITY.SEO,\n children: h('script', {\n type: 'application/ld+json',\n setHtml: schemaJson,\n }),\n });\n });\n\n // Render component (có thể có Head components khác)\n const htmlBody = renderJsxToHtmlString(h(Component, props));\n const clientEntries = getClientJsEntries();\n const cssEntries = getCssEntries();\n const islandEntries = getIslandEntries();\n const cacheTags = getCacheTags();\n const headContent = getHeadContent();\n\n // Render head content to HTML string\n const headHtml =\n headContent.length > 0\n ? headContent.map((content) => renderJsxToHtmlString(content)).join('\\n ')\n : undefined;\n\n return {\n html: htmlBody,\n scripts: clientEntries.map((entry) => entry.path),\n styles: cssEntries.map((entry) => entry.path),\n islands: islandEntries.length > 0 ? islandEntries : undefined,\n head: headHtml,\n lang,\n maxAge,\n sMaxAge,\n swr,\n cacheTags,\n rawHtml,\n };\n } catch (err: any) {\n // Handle RedirectException\n if (err instanceof RedirectException) {\n return {\n html: '',\n redirect: {\n url: err.url,\n statusCode: err.statusCode,\n },\n };\n }\n\n // Handle HttpException\n if (err instanceof HttpException) {\n return await renderErrorView(err);\n }\n\n // For other errors, convert to ServiceUnavailableException\n console.error(`Failed to render:`, err);\n const serviceError = new ServiceUnavailableException(err.message || 'Internal Server Error', {\n originalError: err.name,\n stack: err.stack,\n timestamp: new Date().toISOString(),\n });\n return await renderErrorView(serviceError);\n }\n }, requestInfo);\n}\n"],"names":["VOID_ELEMENTS","classList","input","key","escapeProp","value","escapeHTML","renderAttributes","props","classValue","existingClass","attrs","result","renderJsxToHtmlStringInternal","element","RAW_HTML_MARKER","el","type","children","addCacheTag","componentResult","childrenHtml","renderJsxToHtmlString","renderToRenderedNode","renderErrorView","err","lang","errorViewName","errorComponentPath","setViewName","errorComponentModule","viewComponents","errorProps","Component","htmlBody","h","clientEntries","getClientJsEntries","cssEntries","getCssEntries","headContent","getHeadContent","headHtml","content","entry","html","render","url","requestInfo","runInRenderContext","rawRouteResult","routeHandler","NotFoundException","viewName","globalProps","globalLoaderPathTs","globalLoaderModule","globalLoader","globalLoaderResult","viewProps","maxAge","sMaxAge","swr","rawHtml","loaderPathTs","loaderModule","viewLoaders","loaderResult","globalMetadata","pushMetadata","viewMetadata","globalSchema","pushSchema","viewSchema","componentPathTsx","InternalServerErrorException","MetadataRenderer","getSchemas","schema","schemaJson","Head","HEAD_PRIORITY","islandEntries","getIslandEntries","cacheTags","getCacheTags","RedirectException","HttpException","serviceError","ServiceUnavailableException"],"mappings":"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"}
|