@webtypen/webframez-react 0.0.1

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.
@@ -0,0 +1,852 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src/webframez-core.ts
30
+ var webframez_core_exports = {};
31
+ __export(webframez_core_exports, {
32
+ initWebframezReact: () => initWebframezReact,
33
+ setupWebframezCoreReactRoute: () => setupWebframezCoreReactRoute
34
+ });
35
+ module.exports = __toCommonJS(webframez_core_exports);
36
+
37
+ // src/http.ts
38
+ var import_node_fs2 = __toESM(require("node:fs"), 1);
39
+ var import_node_path2 = __toESM(require("node:path"), 1);
40
+ var import_node_child_process = require("node:child_process");
41
+
42
+ // src/server.ts
43
+ var import_server = require("react-server-dom-webpack/server");
44
+ function defaultOnError(err) {
45
+ console.error("[webframez-react] RSC render error", err);
46
+ }
47
+ function createHTMLShell(options = {}) {
48
+ const {
49
+ title = "RSC App",
50
+ rscEndpoint = "/rsc",
51
+ clientScriptUrl = "/client.js",
52
+ headTags = "",
53
+ rootHtml = "",
54
+ basename = "",
55
+ liveReloadPath,
56
+ liveReloadServerId
57
+ } = options;
58
+ const liveReloadScript = liveReloadPath && liveReloadServerId ? `<script>
59
+ (() => {
60
+ const endpoint = ${JSON.stringify(liveReloadPath)};
61
+ let currentServerId = ${JSON.stringify(liveReloadServerId)};
62
+
63
+ const connect = () => {
64
+ const source = new EventSource(endpoint);
65
+
66
+ source.onmessage = (event) => {
67
+ try {
68
+ const data = JSON.parse(event.data);
69
+ const nextServerId =
70
+ data && typeof data.serverId === "string" ? data.serverId : "";
71
+
72
+ if (nextServerId && nextServerId !== currentServerId) {
73
+ currentServerId = nextServerId;
74
+ window.location.reload();
75
+ }
76
+ } catch {
77
+ // Ignore malformed payloads in dev mode.
78
+ }
79
+ };
80
+
81
+ source.onerror = () => {
82
+ source.close();
83
+ setTimeout(connect, 350);
84
+ };
85
+ };
86
+
87
+ connect();
88
+ })();
89
+ </script>` : "";
90
+ return `<!doctype html>
91
+ <html lang="en">
92
+ <head>
93
+ <meta charset="utf-8" />
94
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
95
+ <title>${title}</title>
96
+ ${headTags}
97
+ </head>
98
+ <body>
99
+ <div id="root">${rootHtml}</div>
100
+ <script>window.__RSC_ENDPOINT = "${rscEndpoint}";</script>
101
+ <script>window.__RSC_BASENAME = "${basename}";</script>
102
+ <script type="module" src="${clientScriptUrl}"></script>
103
+ ${liveReloadScript}
104
+ </body>
105
+ </html>`;
106
+ }
107
+ function sendRSC(res, model, options = {}) {
108
+ const {
109
+ moduleMap = {},
110
+ onError = defaultOnError,
111
+ statusCode = 200,
112
+ contentType = "text/x-component"
113
+ } = options;
114
+ res.statusCode = statusCode;
115
+ res.setHeader("Content-Type", contentType);
116
+ const stream = (0, import_server.renderToPipeableStream)(model, moduleMap, { onError });
117
+ stream.pipe(res);
118
+ return stream;
119
+ }
120
+
121
+ // src/file-router.tsx
122
+ var import_node_fs = __toESM(require("node:fs"), 1);
123
+ var import_node_path = __toESM(require("node:path"), 1);
124
+
125
+ // src/router-runtime.tsx
126
+ var import_react = __toESM(require("react"), 1);
127
+ var ROUTE_CHILDREN_TAG = "webframez-route-children";
128
+ var ROUTE_CHILDREN_SENTINEL = "__webframezRouteChildren";
129
+ var ROUTE_CHILDREN_DISPLAY_NAME = "WebframezRouteChildren";
130
+ var RouteChildrenImpl = () => import_react.default.createElement(ROUTE_CHILDREN_TAG);
131
+ RouteChildrenImpl.displayName = ROUTE_CHILDREN_DISPLAY_NAME;
132
+ RouteChildrenImpl[ROUTE_CHILDREN_SENTINEL] = true;
133
+ var RouteChildren = RouteChildrenImpl;
134
+ function isRouteChildrenType(type) {
135
+ if (type === ROUTE_CHILDREN_TAG || type === RouteChildren) {
136
+ return true;
137
+ }
138
+ if (!type || typeof type !== "function" && typeof type !== "object") {
139
+ return false;
140
+ }
141
+ try {
142
+ const candidate = type;
143
+ return candidate[ROUTE_CHILDREN_SENTINEL] === true || candidate.displayName === ROUTE_CHILDREN_DISPLAY_NAME || candidate.name === "RouteChildren";
144
+ } catch {
145
+ return false;
146
+ }
147
+ }
148
+ function injectRouteChildren(node, routeChildren) {
149
+ if (node === null || node === void 0 || typeof node === "boolean") {
150
+ return node;
151
+ }
152
+ if (Array.isArray(node)) {
153
+ let changed = false;
154
+ const next = node.map((child) => {
155
+ const injected = injectRouteChildren(child, routeChildren);
156
+ if (injected !== child) {
157
+ changed = true;
158
+ }
159
+ return injected;
160
+ });
161
+ return changed ? next : node;
162
+ }
163
+ if (!import_react.default.isValidElement(node)) {
164
+ return node;
165
+ }
166
+ if (isRouteChildrenType(node.type)) {
167
+ return routeChildren;
168
+ }
169
+ const props = node.props;
170
+ if (!("children" in props)) {
171
+ return node;
172
+ }
173
+ const nextChildren = injectRouteChildren(props.children, routeChildren);
174
+ if (nextChildren === props.children) {
175
+ return node;
176
+ }
177
+ if (Array.isArray(nextChildren)) {
178
+ return import_react.default.cloneElement(node, void 0, ...nextChildren);
179
+ }
180
+ return import_react.default.cloneElement(node, void 0, nextChildren);
181
+ }
182
+
183
+ // src/file-router.tsx
184
+ var import_jsx_runtime = require("react/jsx-runtime");
185
+ function normalizePathname(pathname) {
186
+ const trimmed = pathname.replace(/\/+$/, "") || "/";
187
+ return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
188
+ }
189
+ function splitSegments(pathname) {
190
+ const normalized = normalizePathname(pathname);
191
+ if (normalized === "/") {
192
+ return [];
193
+ }
194
+ return normalized.slice(1).split("/").filter(Boolean);
195
+ }
196
+ function walkFiles(dir) {
197
+ if (!import_node_fs.default.existsSync(dir)) {
198
+ return [];
199
+ }
200
+ const result = [];
201
+ const stack = [dir];
202
+ while (stack.length > 0) {
203
+ const current = stack.pop();
204
+ if (!current) {
205
+ continue;
206
+ }
207
+ for (const entry of import_node_fs.default.readdirSync(current, { withFileTypes: true })) {
208
+ const fullPath = import_node_path.default.join(current, entry.name);
209
+ if (entry.isDirectory()) {
210
+ stack.push(fullPath);
211
+ } else if (entry.isFile()) {
212
+ result.push(fullPath);
213
+ }
214
+ }
215
+ }
216
+ return result;
217
+ }
218
+ function readSearchParams(urlSearchParams) {
219
+ const output = {};
220
+ for (const key of urlSearchParams.keys()) {
221
+ const values = urlSearchParams.getAll(key);
222
+ output[key] = values.length <= 1 ? values[0] ?? "" : values;
223
+ }
224
+ return output;
225
+ }
226
+ function escapeHtml(value) {
227
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\"/g, "&quot;").replace(/'/g, "&#39;");
228
+ }
229
+ function toRouteEntry(pagesDir, filePath) {
230
+ const normalized = filePath.replace(/\\/g, "/");
231
+ if (!normalized.endsWith("/index.js")) {
232
+ return null;
233
+ }
234
+ const relativeDir = import_node_path.default.dirname(import_node_path.default.relative(pagesDir, filePath)).replace(/\\/g, "/");
235
+ const segments = relativeDir === "." ? [] : relativeDir.split("/").filter(Boolean);
236
+ const staticCount = segments.filter((segment) => !segment.startsWith("[")).length;
237
+ return {
238
+ filePath,
239
+ segments,
240
+ staticCount
241
+ };
242
+ }
243
+ function matchRoute(entry, pathname) {
244
+ const targetSegments = splitSegments(pathname);
245
+ if (entry.segments.length !== targetSegments.length) {
246
+ return null;
247
+ }
248
+ const params = {};
249
+ for (let index = 0; index < entry.segments.length; index += 1) {
250
+ const routeSegment = entry.segments[index];
251
+ const targetSegment = targetSegments[index];
252
+ if (routeSegment.startsWith("[") && routeSegment.endsWith("]")) {
253
+ const paramName = routeSegment.slice(1, -1);
254
+ if (!paramName) {
255
+ return null;
256
+ }
257
+ params[paramName] = decodeURIComponent(targetSegment);
258
+ continue;
259
+ }
260
+ if (routeSegment !== targetSegment) {
261
+ return null;
262
+ }
263
+ }
264
+ return params;
265
+ }
266
+ function mergeHead(...configs) {
267
+ const merged = {
268
+ meta: [],
269
+ links: []
270
+ };
271
+ for (const config of configs) {
272
+ if (!config) {
273
+ continue;
274
+ }
275
+ if (config.title) {
276
+ merged.title = config.title;
277
+ }
278
+ if (config.description) {
279
+ merged.description = config.description;
280
+ }
281
+ if (config.favicon) {
282
+ merged.favicon = config.favicon;
283
+ }
284
+ if (config.meta) {
285
+ merged.meta?.push(...config.meta);
286
+ }
287
+ if (config.links) {
288
+ merged.links?.push(...config.links);
289
+ }
290
+ }
291
+ return merged;
292
+ }
293
+ function renderHeadToString(head) {
294
+ const tags = [];
295
+ if (head.description) {
296
+ tags.push(
297
+ `<meta name="description" content="${escapeHtml(head.description)}" />`
298
+ );
299
+ }
300
+ if (head.favicon) {
301
+ tags.push(`<link rel="icon" href="${escapeHtml(head.favicon)}" />`);
302
+ }
303
+ for (const meta of head.meta ?? []) {
304
+ const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
305
+ tags.push(`<meta ${attrs} />`);
306
+ }
307
+ for (const link of head.links ?? []) {
308
+ const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
309
+ tags.push(`<link ${attrs} />`);
310
+ }
311
+ return tags.join("\n");
312
+ }
313
+ function resolveModule(modulePath) {
314
+ delete require.cache[modulePath];
315
+ return require(modulePath);
316
+ }
317
+ async function resolveHead(candidate, context) {
318
+ if (!candidate.Head) {
319
+ return void 0;
320
+ }
321
+ return candidate.Head(context);
322
+ }
323
+ function findBestMatch(entries, pathname) {
324
+ const matches = [];
325
+ for (const entry of entries) {
326
+ const params = matchRoute(entry, pathname);
327
+ if (params) {
328
+ matches.push({ entry, params });
329
+ }
330
+ }
331
+ matches.sort((a, b) => {
332
+ if (b.entry.staticCount !== a.entry.staticCount) {
333
+ return b.entry.staticCount - a.entry.staticCount;
334
+ }
335
+ return b.entry.segments.length - a.entry.segments.length;
336
+ });
337
+ return matches[0] ?? null;
338
+ }
339
+ function normalizeAbortStatus(value) {
340
+ if (typeof value !== "number" || !Number.isFinite(value)) {
341
+ return 404;
342
+ }
343
+ const normalized = Math.trunc(value);
344
+ if (normalized < 100 || normalized > 599) {
345
+ return 404;
346
+ }
347
+ return normalized;
348
+ }
349
+ function createAbort(options) {
350
+ return {
351
+ __webframezRouteAbort: true,
352
+ statusCode: normalizeAbortStatus(options?.status),
353
+ message: options?.message?.trim() || "Page not found",
354
+ payload: options?.payload
355
+ };
356
+ }
357
+ function isRouteAbort(value) {
358
+ if (!value || typeof value !== "object") {
359
+ return false;
360
+ }
361
+ try {
362
+ return value.__webframezRouteAbort === true;
363
+ } catch {
364
+ return false;
365
+ }
366
+ }
367
+ function createFileRouter(options) {
368
+ const pagesDir = options.pagesDir;
369
+ const layoutPath = import_node_path.default.join(pagesDir, "layout.js");
370
+ const errorPath = import_node_path.default.join(pagesDir, "errors.js");
371
+ function buildRouteEntries() {
372
+ const files = walkFiles(pagesDir);
373
+ return files.map((filePath) => toRouteEntry(pagesDir, filePath)).filter((entry) => entry !== null).sort((a, b) => {
374
+ if (b.staticCount !== a.staticCount) {
375
+ return b.staticCount - a.staticCount;
376
+ }
377
+ return b.segments.length - a.segments.length;
378
+ });
379
+ }
380
+ async function renderError(context, statusCode, message, payload) {
381
+ const errorProps = {
382
+ ...context,
383
+ statusCode,
384
+ message,
385
+ payload
386
+ };
387
+ const layoutModule = import_node_fs.default.existsSync(layoutPath) ? resolveModule(layoutPath) : null;
388
+ if (!import_node_fs.default.existsSync(errorPath)) {
389
+ const fallback = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", { style: { fontFamily: "system-ui, sans-serif", padding: 24 }, children: [
390
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: statusCode }),
391
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: message })
392
+ ] });
393
+ return {
394
+ statusCode,
395
+ model: fallback,
396
+ head: {
397
+ title: `${statusCode} - ${message}`
398
+ }
399
+ };
400
+ }
401
+ const errorModule = resolveModule(errorPath);
402
+ const errorNode = errorModule.default(errorProps);
403
+ const layoutHead = layoutModule ? await resolveHead(layoutModule, context) : void 0;
404
+ const errorHead = await resolveHead(errorModule, errorProps);
405
+ const model = layoutModule ? injectRouteChildren(layoutModule.default(context), errorNode) : errorNode;
406
+ return {
407
+ statusCode,
408
+ model,
409
+ head: mergeHead(layoutHead, errorHead)
410
+ };
411
+ }
412
+ async function resolve(input) {
413
+ const pathname = normalizePathname(input.pathname);
414
+ const contextBase = {
415
+ pathname,
416
+ params: {},
417
+ searchParams: input.searchParams,
418
+ cookies: input.cookies ?? {},
419
+ abort: (options2) => {
420
+ throw createAbort(options2);
421
+ }
422
+ };
423
+ let activeContext = contextBase;
424
+ try {
425
+ const entries = buildRouteEntries();
426
+ const match = findBestMatch(entries, pathname);
427
+ if (!match) {
428
+ return renderError(contextBase, 404, "Page not found");
429
+ }
430
+ const context = {
431
+ ...contextBase,
432
+ params: match.params
433
+ };
434
+ activeContext = context;
435
+ const pageModule = resolveModule(match.entry.filePath);
436
+ const layoutModule = import_node_fs.default.existsSync(layoutPath) ? resolveModule(layoutPath) : null;
437
+ const pageNode = pageModule.default(context);
438
+ const layoutHead = layoutModule ? await resolveHead(layoutModule, context) : void 0;
439
+ const pageHead = await resolveHead(pageModule, context);
440
+ const model = layoutModule ? injectRouteChildren(layoutModule.default(context), pageNode) : pageNode;
441
+ return {
442
+ statusCode: 200,
443
+ model,
444
+ head: mergeHead(layoutHead, pageHead)
445
+ };
446
+ } catch (error) {
447
+ if (isRouteAbort(error)) {
448
+ return renderError(activeContext, error.statusCode, error.message, error.payload);
449
+ }
450
+ console.error("[webframez-react] Failed to resolve route", error);
451
+ return renderError(contextBase, 500, "Internal server error");
452
+ }
453
+ }
454
+ return {
455
+ resolve,
456
+ readSearchParams
457
+ };
458
+ }
459
+ function parseSearchParams(query) {
460
+ return readSearchParams(query);
461
+ }
462
+
463
+ // src/http.ts
464
+ function normalizeBasePath(basePath) {
465
+ if (!basePath || basePath === "/") {
466
+ return "";
467
+ }
468
+ const withLeadingSlash = basePath.startsWith("/") ? basePath : `/${basePath}`;
469
+ return withLeadingSlash.endsWith("/") ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
470
+ }
471
+ function stripBasePath(pathname, basePath) {
472
+ if (!basePath) {
473
+ return pathname;
474
+ }
475
+ if (pathname === basePath) {
476
+ return "/";
477
+ }
478
+ if (pathname.startsWith(`${basePath}/`)) {
479
+ return pathname.slice(basePath.length) || "/";
480
+ }
481
+ return pathname;
482
+ }
483
+ function runNodeCommand(args) {
484
+ return new Promise((resolve, reject) => {
485
+ (0, import_node_child_process.execFile)(process.execPath, args, { timeout: 1e4, maxBuffer: 1024 * 1024 * 5 }, (error, stdout, stderr) => {
486
+ if (error) {
487
+ const out = stderr && stderr.trim() !== "" ? stderr : stdout;
488
+ reject(new Error(out || error.message));
489
+ return;
490
+ }
491
+ resolve({ stdout, stderr });
492
+ });
493
+ });
494
+ }
495
+ async function renderInitialHtmlInWorker(options) {
496
+ const payload = Buffer.from(JSON.stringify(options), "utf8").toString("base64url");
497
+ const script = `
498
+ const path = require("node:path");
499
+ const input = JSON.parse(Buffer.from(process.argv[1], "base64url").toString("utf8"));
500
+ globalThis.__RSC_BASENAME = input.basename || "";
501
+ const { createFileRouter } = require("webframez-react/router");
502
+ const reactDomPkg = require.resolve("react-dom/package.json", {
503
+ paths: [process.cwd(), input.pagesDir]
504
+ });
505
+ const reactDomServer = require(path.join(path.dirname(reactDomPkg), "server.node.js"));
506
+
507
+ (async () => {
508
+ const router = createFileRouter({ pagesDir: input.pagesDir });
509
+ const resolved = await router.resolve({
510
+ pathname: input.pathname,
511
+ searchParams: input.searchParams || {},
512
+ cookies: input.cookies || {},
513
+ });
514
+ const html = reactDomServer.renderToString(resolved.model);
515
+ process.stdout.write(JSON.stringify({ html }));
516
+ })().catch((error) => {
517
+ process.stderr.write(error && (error.stack || String(error)) ? (error.stack || String(error)) : "Unknown SSR worker error");
518
+ process.exit(1);
519
+ });
520
+ `;
521
+ const { stdout } = await runNodeCommand(["-e", script, payload]);
522
+ const parsed = JSON.parse(stdout || "{}");
523
+ return parsed.html || "";
524
+ }
525
+ function withRequestBasename(basename, fn) {
526
+ const target = globalThis;
527
+ const previous = target.__RSC_BASENAME;
528
+ target.__RSC_BASENAME = basename;
529
+ const finish = () => {
530
+ target.__RSC_BASENAME = previous;
531
+ };
532
+ try {
533
+ const result = fn();
534
+ if (result && typeof result.then === "function") {
535
+ return result.finally(finish);
536
+ }
537
+ finish();
538
+ return result;
539
+ } catch (error) {
540
+ finish();
541
+ throw error;
542
+ }
543
+ }
544
+ function parseCookies(rawCookieHeader) {
545
+ const raw = Array.isArray(rawCookieHeader) ? rawCookieHeader.join("; ") : rawCookieHeader ?? "";
546
+ const output = {};
547
+ if (!raw || raw.trim() === "") {
548
+ return output;
549
+ }
550
+ for (const pair of raw.split(";")) {
551
+ const part = pair.trim();
552
+ if (!part) {
553
+ continue;
554
+ }
555
+ const eqIndex = part.indexOf("=");
556
+ const name = eqIndex >= 0 ? part.slice(0, eqIndex).trim() : part.trim();
557
+ const value = eqIndex >= 0 ? part.slice(eqIndex + 1).trim() : "";
558
+ if (!name) {
559
+ continue;
560
+ }
561
+ output[name] = decodeURIComponent(value);
562
+ }
563
+ return output;
564
+ }
565
+ function createNodeRequestHandler(options) {
566
+ const devServerId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
567
+ const distRootDir = import_node_path2.default.resolve(options.distRootDir);
568
+ const pagesDir = import_node_path2.default.resolve(options.pagesDir ?? import_node_path2.default.join(distRootDir, "pages"));
569
+ const manifestPath = import_node_path2.default.resolve(
570
+ options.manifestPath ?? import_node_path2.default.join(distRootDir, "react-client-manifest.json")
571
+ );
572
+ const assetsPrefix = options.assetsPrefix ?? "/assets/";
573
+ const rscPath = options.rscPath ?? "/rsc";
574
+ const clientScriptUrl = options.clientScriptUrl ?? "/assets/client.js";
575
+ const basePath = normalizeBasePath(options.basePath);
576
+ const nodeEnv = process.env.NODE_ENV || "";
577
+ const runningInWatchMode = Array.isArray(process.execArgv) && process.execArgv.includes("--watch");
578
+ const liveReloadEnabled = options.liveReloadPath !== false && (nodeEnv === "development" || runningInWatchMode);
579
+ const liveReloadPath = !liveReloadEnabled ? "" : options.liveReloadPath ?? `${basePath || ""}/__webframez_live_reload`;
580
+ const liveReloadClients = /* @__PURE__ */ new Set();
581
+ const router = createFileRouter({ pagesDir });
582
+ const moduleMap = JSON.parse(import_node_fs2.default.readFileSync(manifestPath, "utf-8"));
583
+ return async function handleRequest(req, res) {
584
+ if (!req.url) {
585
+ res.statusCode = 400;
586
+ res.end("Bad request");
587
+ return;
588
+ }
589
+ const url = new URL(req.url, "http://localhost");
590
+ const requestCookies = parseCookies(req.headers.cookie);
591
+ if (liveReloadPath && url.pathname === liveReloadPath) {
592
+ const accept = String(req.headers.accept || "");
593
+ if (accept.includes("text/event-stream")) {
594
+ res.statusCode = 200;
595
+ res.setHeader("Content-Type", "text/event-stream; charset=utf-8");
596
+ res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
597
+ res.setHeader("Connection", "keep-alive");
598
+ res.setHeader("X-Accel-Buffering", "no");
599
+ if (typeof res.flushHeaders === "function") {
600
+ res.flushHeaders();
601
+ }
602
+ res.write(`retry: 400
603
+ `);
604
+ res.write(`data: ${JSON.stringify({ serverId: devServerId })}
605
+
606
+ `);
607
+ liveReloadClients.add(res);
608
+ const heartbeat = setInterval(() => {
609
+ if (!res.writableEnded && !res.destroyed) {
610
+ res.write(`: ping
611
+
612
+ `);
613
+ }
614
+ }, 15e3);
615
+ const cleanup = () => {
616
+ clearInterval(heartbeat);
617
+ liveReloadClients.delete(res);
618
+ };
619
+ req.on("close", cleanup);
620
+ res.on("close", cleanup);
621
+ return;
622
+ }
623
+ res.statusCode = 200;
624
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
625
+ res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
626
+ res.end(JSON.stringify({ serverId: devServerId, clients: liveReloadClients.size }));
627
+ return;
628
+ }
629
+ if (url.pathname === rscPath) {
630
+ const pathname = stripBasePath(url.searchParams.get("path") || "/", basePath);
631
+ const search = new URLSearchParams(url.searchParams.get("search") || "");
632
+ const resolved2 = await withRequestBasename(
633
+ basePath,
634
+ () => router.resolve({
635
+ pathname,
636
+ searchParams: parseSearchParams(search),
637
+ cookies: requestCookies
638
+ })
639
+ );
640
+ sendRSC(res, resolved2.model, {
641
+ moduleMap,
642
+ statusCode: resolved2.statusCode
643
+ });
644
+ return;
645
+ }
646
+ if (url.pathname.startsWith(assetsPrefix)) {
647
+ const relative = url.pathname.slice(assetsPrefix.length);
648
+ const filePath = import_node_path2.default.resolve(distRootDir, relative);
649
+ if (!filePath.startsWith(distRootDir)) {
650
+ res.statusCode = 400;
651
+ res.end("Invalid path");
652
+ return;
653
+ }
654
+ const ext = import_node_path2.default.extname(filePath);
655
+ if (ext === ".js" || ext === ".mjs") {
656
+ res.setHeader("Content-Type", "text/javascript; charset=utf-8");
657
+ } else if (ext === ".json") {
658
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
659
+ }
660
+ const stream = import_node_fs2.default.createReadStream(filePath);
661
+ stream.on("error", () => {
662
+ res.statusCode = 404;
663
+ res.end("Not found");
664
+ });
665
+ stream.pipe(res);
666
+ return;
667
+ }
668
+ const resolved = await withRequestBasename(
669
+ basePath,
670
+ () => router.resolve({
671
+ pathname: stripBasePath(url.pathname, basePath),
672
+ searchParams: parseSearchParams(url.searchParams),
673
+ cookies: requestCookies
674
+ })
675
+ );
676
+ let rootHtml = "";
677
+ try {
678
+ rootHtml = await renderInitialHtmlInWorker({
679
+ pagesDir,
680
+ pathname: stripBasePath(url.pathname, basePath),
681
+ searchParams: parseSearchParams(url.searchParams),
682
+ cookies: requestCookies,
683
+ basename: basePath
684
+ });
685
+ } catch (error) {
686
+ console.error("[webframez-react] Failed to render initial HTML", error);
687
+ }
688
+ res.statusCode = resolved.statusCode;
689
+ res.setHeader("Content-Type", "text/html");
690
+ res.end(
691
+ createHTMLShell({
692
+ title: resolved.head.title || "Webframez React",
693
+ headTags: renderHeadToString(resolved.head),
694
+ clientScriptUrl,
695
+ rscEndpoint: rscPath,
696
+ rootHtml,
697
+ basename: basePath,
698
+ liveReloadPath: liveReloadPath || void 0,
699
+ liveReloadServerId: liveReloadPath ? devServerId : void 0
700
+ })
701
+ );
702
+ };
703
+ }
704
+
705
+ // src/webframez-core.ts
706
+ function normalizeMountPath(path3) {
707
+ const trimmed = (path3 || "").trim();
708
+ let normalized = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
709
+ if (normalized.endsWith("/**")) {
710
+ normalized = normalized.slice(0, -3);
711
+ } else if (normalized.endsWith("/*")) {
712
+ normalized = normalized.slice(0, -2);
713
+ }
714
+ while (normalized.length > 1 && normalized.endsWith("/")) {
715
+ normalized = normalized.slice(0, -1);
716
+ }
717
+ return normalized || "/";
718
+ }
719
+ function buildRenderDefaults(path3) {
720
+ const mountPath = normalizeMountPath(path3);
721
+ const routePath = mountPath === "/" ? "/*" : `${mountPath}/*`;
722
+ if (mountPath === "/") {
723
+ return {
724
+ routePath,
725
+ basePath: void 0,
726
+ assetsPrefix: void 0,
727
+ rscPath: void 0,
728
+ clientScriptUrl: void 0
729
+ };
730
+ }
731
+ return {
732
+ routePath,
733
+ basePath: mountPath,
734
+ assetsPrefix: `${mountPath}/assets/`,
735
+ rscPath: `${mountPath}/rsc`,
736
+ clientScriptUrl: `${mountPath}/assets/client.js`
737
+ };
738
+ }
739
+ async function waitForResponseFinish(res) {
740
+ if (res.writableEnded || res.destroyed) {
741
+ return;
742
+ }
743
+ await new Promise((resolve, reject) => {
744
+ const onFinish = () => {
745
+ cleanup();
746
+ resolve();
747
+ };
748
+ const onClose = () => {
749
+ cleanup();
750
+ resolve();
751
+ };
752
+ const onError = (error) => {
753
+ cleanup();
754
+ reject(error);
755
+ };
756
+ const cleanup = () => {
757
+ res.off("finish", onFinish);
758
+ res.off("close", onClose);
759
+ res.off("error", onError);
760
+ };
761
+ res.on("finish", onFinish);
762
+ res.on("close", onClose);
763
+ res.on("error", onError);
764
+ });
765
+ }
766
+ function normalizeMethods(method) {
767
+ if (!method) {
768
+ return ["GET"];
769
+ }
770
+ return Array.isArray(method) ? method : [method];
771
+ }
772
+ function registerByMethod(route, method, path3, component, routeOptions) {
773
+ if (method === "GET") {
774
+ route.get(path3, component, routeOptions);
775
+ return;
776
+ }
777
+ if (method === "POST") {
778
+ route.post(path3, component, routeOptions);
779
+ return;
780
+ }
781
+ if (method === "PUT") {
782
+ route.put(path3, component, routeOptions);
783
+ return;
784
+ }
785
+ route.delete(path3, component, routeOptions);
786
+ }
787
+ function registerRouteRenderer(route, methodName) {
788
+ route.extend(methodName, () => {
789
+ return (path3, options) => {
790
+ if (!options || !options.distRootDir) {
791
+ throw new Error(
792
+ `Route.${methodName} requires at least { distRootDir }`
793
+ );
794
+ }
795
+ const defaults = buildRenderDefaults(path3);
796
+ const {
797
+ method,
798
+ routeOptions,
799
+ basePath,
800
+ assetsPrefix,
801
+ rscPath,
802
+ clientScriptUrl,
803
+ ...nodeHandlerOptions
804
+ } = options;
805
+ const handleNodeRequest = createNodeRequestHandler({
806
+ ...nodeHandlerOptions,
807
+ basePath: basePath ?? defaults.basePath,
808
+ assetsPrefix: assetsPrefix ?? defaults.assetsPrefix,
809
+ rscPath: rscPath ?? defaults.rscPath,
810
+ clientScriptUrl: clientScriptUrl ?? defaults.clientScriptUrl
811
+ });
812
+ const methods = normalizeMethods(method);
813
+ for (const currentMethod of methods) {
814
+ registerByMethod(
815
+ route,
816
+ currentMethod,
817
+ defaults.routePath,
818
+ async (req, res) => {
819
+ if (!req.message || !res.res) {
820
+ throw new Error(
821
+ `Route.${methodName} only supports node/http requests`
822
+ );
823
+ }
824
+ await handleNodeRequest(req.message, res.res);
825
+ await waitForResponseFinish(res.res);
826
+ },
827
+ routeOptions
828
+ );
829
+ }
830
+ };
831
+ });
832
+ }
833
+ function initWebframezReact(route) {
834
+ if (!route || typeof route.extend !== "function") {
835
+ throw new Error(
836
+ "initWebframezReact requires a webframez-core Route facade"
837
+ );
838
+ }
839
+ if (typeof route.renderReact !== "function") {
840
+ registerRouteRenderer(route, "renderReact");
841
+ }
842
+ if (typeof route.reactRender !== "function") {
843
+ registerRouteRenderer(route, "reactRender");
844
+ }
845
+ return route;
846
+ }
847
+ var setupWebframezCoreReactRoute = initWebframezReact;
848
+ // Annotate the CommonJS export names for ESM import in node:
849
+ 0 && (module.exports = {
850
+ initWebframezReact,
851
+ setupWebframezCoreReactRoute
852
+ });