@lolyjs/core 0.1.0-alpha.9 → 0.2.0-alpha.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.
package/dist/index.d.mts CHANGED
@@ -1,6 +1,7 @@
1
1
  import http from 'http';
2
2
  import { Request, Response } from 'express';
3
3
  import { Socket, Server } from 'socket.io';
4
+ export { c as bootstrapClient } from './bootstrap-BiCQmSkx.mjs';
4
5
  import { ZodSchema, z } from 'zod';
5
6
  import * as express_rate_limit from 'express-rate-limit';
6
7
  import pino, { Logger as Logger$1 } from 'pino';
@@ -215,40 +216,6 @@ interface BuildAppOptions {
215
216
  }
216
217
  declare function buildApp(options?: BuildAppOptions): Promise<void>;
217
218
 
218
- declare const WINDOW_DATA_KEY = "__FW_DATA__";
219
-
220
- type InitialData = {
221
- pathname: string;
222
- params: Record<string, string>;
223
- props: Record<string, any>;
224
- metadata?: {
225
- title?: string;
226
- description?: string;
227
- } | null;
228
- notFound?: boolean;
229
- error?: boolean;
230
- theme?: string;
231
- };
232
- declare global {
233
- interface Window {
234
- [WINDOW_DATA_KEY]?: InitialData;
235
- }
236
- }
237
- type ClientLoadedComponents = {
238
- Page: React.ComponentType<any>;
239
- layouts: React.ComponentType<any>[];
240
- };
241
- type ClientRouteLoaded = {
242
- pattern: string;
243
- paramNames: string[];
244
- load: () => Promise<ClientLoadedComponents>;
245
- };
246
-
247
- /**
248
- * Bootstraps the client-side application.
249
- */
250
- declare function bootstrapClient(routes: ClientRouteLoaded[], notFoundRoute: ClientRouteLoaded | null, errorRoute?: ClientRouteLoaded | null): void;
251
-
252
219
  declare function withCache(fn: any, options: any): any;
253
220
 
254
221
  /**
@@ -475,4 +442,4 @@ declare function requestLoggerMiddleware(options?: {
475
442
  */
476
443
  declare function getRequestLogger(req: Request): Logger;
477
444
 
478
- export { type ApiContext, type ApiMiddleware, DEFAULT_CONFIG, type FrameworkConfig, type GenerateStaticParams, type InitServerData, type LoaderResult, type LogLevel, Logger, type LoggerContext, type LoggerOptions, type MetadataLoader, type RouteMiddleware, type ServerConfig, type ServerContext, type ServerLoader, ValidationError, type WssContext, bootstrapClient, buildApp, commonSchemas, createModuleLogger, createRateLimiter, defaultRateLimiter, generateRequestId, getAppDir, getBuildDir, getLogger, getRequestLogger, getStaticDir, lenientRateLimiter, loadConfig, logger, requestLoggerMiddleware, resetLogger, safeValidate, sanitizeObject, sanitizeParams, sanitizeQuery, sanitizeString, setLogger, startDevServer, startProdServer, strictRateLimiter, validate, withCache };
445
+ export { type ApiContext, type ApiMiddleware, DEFAULT_CONFIG, type FrameworkConfig, type GenerateStaticParams, type InitServerData, type LoaderResult, type LogLevel, Logger, type LoggerContext, type LoggerOptions, type MetadataLoader, type RouteMiddleware, type ServerConfig, type ServerContext, type ServerLoader, ValidationError, type WssContext, buildApp, commonSchemas, createModuleLogger, createRateLimiter, defaultRateLimiter, generateRequestId, getAppDir, getBuildDir, getLogger, getRequestLogger, getStaticDir, lenientRateLimiter, loadConfig, logger, requestLoggerMiddleware, resetLogger, safeValidate, sanitizeObject, sanitizeParams, sanitizeQuery, sanitizeString, setLogger, startDevServer, startProdServer, strictRateLimiter, validate, withCache };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import http from 'http';
2
2
  import { Request, Response } from 'express';
3
3
  import { Socket, Server } from 'socket.io';
4
+ export { c as bootstrapClient } from './bootstrap-BiCQmSkx.js';
4
5
  import { ZodSchema, z } from 'zod';
5
6
  import * as express_rate_limit from 'express-rate-limit';
6
7
  import pino, { Logger as Logger$1 } from 'pino';
@@ -215,40 +216,6 @@ interface BuildAppOptions {
215
216
  }
216
217
  declare function buildApp(options?: BuildAppOptions): Promise<void>;
217
218
 
218
- declare const WINDOW_DATA_KEY = "__FW_DATA__";
219
-
220
- type InitialData = {
221
- pathname: string;
222
- params: Record<string, string>;
223
- props: Record<string, any>;
224
- metadata?: {
225
- title?: string;
226
- description?: string;
227
- } | null;
228
- notFound?: boolean;
229
- error?: boolean;
230
- theme?: string;
231
- };
232
- declare global {
233
- interface Window {
234
- [WINDOW_DATA_KEY]?: InitialData;
235
- }
236
- }
237
- type ClientLoadedComponents = {
238
- Page: React.ComponentType<any>;
239
- layouts: React.ComponentType<any>[];
240
- };
241
- type ClientRouteLoaded = {
242
- pattern: string;
243
- paramNames: string[];
244
- load: () => Promise<ClientLoadedComponents>;
245
- };
246
-
247
- /**
248
- * Bootstraps the client-side application.
249
- */
250
- declare function bootstrapClient(routes: ClientRouteLoaded[], notFoundRoute: ClientRouteLoaded | null, errorRoute?: ClientRouteLoaded | null): void;
251
-
252
219
  declare function withCache(fn: any, options: any): any;
253
220
 
254
221
  /**
@@ -475,4 +442,4 @@ declare function requestLoggerMiddleware(options?: {
475
442
  */
476
443
  declare function getRequestLogger(req: Request): Logger;
477
444
 
478
- export { type ApiContext, type ApiMiddleware, DEFAULT_CONFIG, type FrameworkConfig, type GenerateStaticParams, type InitServerData, type LoaderResult, type LogLevel, Logger, type LoggerContext, type LoggerOptions, type MetadataLoader, type RouteMiddleware, type ServerConfig, type ServerContext, type ServerLoader, ValidationError, type WssContext, bootstrapClient, buildApp, commonSchemas, createModuleLogger, createRateLimiter, defaultRateLimiter, generateRequestId, getAppDir, getBuildDir, getLogger, getRequestLogger, getStaticDir, lenientRateLimiter, loadConfig, logger, requestLoggerMiddleware, resetLogger, safeValidate, sanitizeObject, sanitizeParams, sanitizeQuery, sanitizeString, setLogger, startDevServer, startProdServer, strictRateLimiter, validate, withCache };
445
+ export { type ApiContext, type ApiMiddleware, DEFAULT_CONFIG, type FrameworkConfig, type GenerateStaticParams, type InitServerData, type LoaderResult, type LogLevel, Logger, type LoggerContext, type LoggerOptions, type MetadataLoader, type RouteMiddleware, type ServerConfig, type ServerContext, type ServerLoader, ValidationError, type WssContext, buildApp, commonSchemas, createModuleLogger, createRateLimiter, defaultRateLimiter, generateRequestId, getAppDir, getBuildDir, getLogger, getRequestLogger, getStaticDir, lenientRateLimiter, loadConfig, logger, requestLoggerMiddleware, resetLogger, safeValidate, sanitizeObject, sanitizeParams, sanitizeQuery, sanitizeString, setLogger, startDevServer, startProdServer, strictRateLimiter, validate, withCache };
package/dist/index.js CHANGED
@@ -1365,24 +1365,69 @@ function createClientConfig(projectRoot, mode) {
1365
1365
  init_globals();
1366
1366
  import path13 from "path";
1367
1367
  import fs12 from "fs";
1368
- function startClientBundler(projectRoot) {
1369
- const { config, outDir } = createClientConfig(projectRoot, "production");
1368
+ function startClientBundler(projectRoot, mode = "development") {
1369
+ const { config, outDir } = createClientConfig(projectRoot, mode);
1370
1370
  copyStaticAssets(projectRoot, outDir);
1371
1371
  const compiler = rspack2(config);
1372
+ let isBuilding = false;
1373
+ let buildResolve = null;
1374
+ let buildPromise = null;
1375
+ let lastBuildTime = Date.now();
1376
+ compiler.hooks.compile.tap("HotReload", () => {
1377
+ isBuilding = true;
1378
+ buildPromise = new Promise((resolve3) => {
1379
+ buildResolve = resolve3;
1380
+ });
1381
+ });
1372
1382
  compiler.watch({}, (err, stats) => {
1373
1383
  if (err) {
1374
1384
  console.error("[framework][client] Rspack error:", err);
1385
+ isBuilding = false;
1386
+ lastBuildTime = Date.now();
1387
+ if (buildResolve) {
1388
+ buildResolve();
1389
+ buildResolve = null;
1390
+ buildPromise = null;
1391
+ }
1392
+ return;
1393
+ }
1394
+ if (!stats) {
1395
+ isBuilding = false;
1396
+ lastBuildTime = Date.now();
1375
1397
  return;
1376
1398
  }
1377
- if (!stats) return;
1378
1399
  if (stats.hasErrors()) {
1379
1400
  console.error(
1380
1401
  "[framework][client] Build with errors:\n",
1381
1402
  stats.toString("errors-only")
1382
1403
  );
1404
+ } else {
1405
+ console.log("[framework][client] \u2713 Client bundle rebuilt successfully");
1406
+ }
1407
+ isBuilding = false;
1408
+ lastBuildTime = Date.now();
1409
+ if (buildResolve) {
1410
+ buildResolve();
1411
+ buildResolve = null;
1412
+ buildPromise = null;
1383
1413
  }
1384
1414
  });
1385
- return { outDir };
1415
+ return {
1416
+ outDir,
1417
+ waitForBuild: async () => {
1418
+ if (isBuilding && buildPromise) {
1419
+ await buildPromise;
1420
+ await new Promise((resolve3) => setTimeout(resolve3, 100));
1421
+ return;
1422
+ }
1423
+ const timeSinceLastBuild = Date.now() - lastBuildTime;
1424
+ if (timeSinceLastBuild < 500) {
1425
+ await new Promise((resolve3) => setTimeout(resolve3, 200));
1426
+ return;
1427
+ }
1428
+ return Promise.resolve();
1429
+ }
1430
+ };
1386
1431
  }
1387
1432
  function buildClientBundle(projectRoot) {
1388
1433
  const { config, outDir } = createClientConfig(projectRoot, "production");
@@ -3113,7 +3158,9 @@ import path14 from "path";
3113
3158
  function setupHotReload({
3114
3159
  app,
3115
3160
  appDir,
3116
- route = "/__fw/hot"
3161
+ route = "/__fw/hot",
3162
+ waitForBuild,
3163
+ onFileChange
3117
3164
  }) {
3118
3165
  const clients = /* @__PURE__ */ new Set();
3119
3166
  app.get(route, (req, res) => {
@@ -3134,9 +3181,25 @@ data: connected
3134
3181
  ignoreInitial: true,
3135
3182
  ignored: ["**/node_modules/**", `**/${BUILD_FOLDER_NAME}/**`, "**/.git/**"]
3136
3183
  });
3137
- function broadcastReload(reason, filePath) {
3184
+ async function broadcastReload(reason, filePath) {
3138
3185
  const rel = path14.relative(appDir, filePath);
3139
3186
  console.log(`[hot-reload] ${reason}: ${rel}`);
3187
+ if (onFileChange) {
3188
+ try {
3189
+ await onFileChange(filePath);
3190
+ } catch (error) {
3191
+ console.warn("[hot-reload] Error in onFileChange callback:", error);
3192
+ }
3193
+ }
3194
+ if (waitForBuild) {
3195
+ try {
3196
+ console.log("[hot-reload] Waiting for client bundle to finish...");
3197
+ await waitForBuild();
3198
+ console.log("[hot-reload] Client bundle ready, sending reload event");
3199
+ } catch (error) {
3200
+ console.warn("[hot-reload] Error waiting for build:", error);
3201
+ }
3202
+ }
3140
3203
  for (const res of clients) {
3141
3204
  res.write(`event: message
3142
3205
  data: reload:${rel}
@@ -3275,14 +3338,29 @@ function setupServer(app, options) {
3275
3338
  };
3276
3339
  };
3277
3340
  var getRoutes = getRoutes2;
3278
- setupHotReload({ app, appDir });
3341
+ const { outDir, waitForBuild } = startClientBundler(projectRoot, "development");
3342
+ const onFileChange = async (filePath) => {
3343
+ const rel = path17.relative(appDir, filePath);
3344
+ const isPageFile = filePath.includes("page.tsx") || filePath.includes("page.ts") || filePath.includes("layout.tsx") || filePath.includes("layout.ts") || filePath.includes("_not-found") || filePath.includes("_error");
3345
+ const isTsFile = filePath.endsWith(".ts") || filePath.endsWith(".tsx");
3346
+ if (isTsFile) {
3347
+ clearAppRequireCache(appDir);
3348
+ console.log(`[hot-reload] Cleared require cache for: ${rel}`);
3349
+ }
3350
+ if (isPageFile) {
3351
+ const loader = new FilesystemRouteLoader(appDir);
3352
+ const newRoutes = loader.loadRoutes();
3353
+ writeClientRoutesManifest(newRoutes, projectRoot);
3354
+ console.log("[hot-reload] Client routes manifest reloaded");
3355
+ }
3356
+ };
3357
+ setupHotReload({ app, appDir, waitForBuild, onFileChange });
3358
+ app.use("/static", express.static(outDir));
3279
3359
  const routes = routeLoader.loadRoutes();
3280
3360
  const wssRoutes = routeLoader.loadWssRoutes();
3281
3361
  const notFoundPage = routeLoader.loadNotFoundRoute();
3282
3362
  const errorPage = routeLoader.loadErrorRoute();
3283
3363
  writeClientRoutesManifest(routes, projectRoot);
3284
- const { outDir } = startClientBundler(projectRoot);
3285
- app.use("/static", express.static(outDir));
3286
3364
  return {
3287
3365
  routes,
3288
3366
  wssRoutes,
@@ -3980,7 +4058,7 @@ async function handlePageRequest(options) {
3980
4058
  const { errorPage, req, res, routeChunks, theme, projectRoot } = options;
3981
4059
  const reqLogger = getRequestLogger(req);
3982
4060
  if (errorPage) {
3983
- await renderErrorPageWithStream(errorPage, req, res, error, routeChunks || {}, theme, projectRoot);
4061
+ await renderErrorPageWithStream(errorPage, req, res, error, routeChunks || {}, theme, projectRoot, options.env);
3984
4062
  } else {
3985
4063
  reqLogger.error("Unhandled error in page request", error, {
3986
4064
  urlPath: options.urlPath,
@@ -4008,9 +4086,9 @@ async function handlePageRequestInternal(options) {
4008
4086
  theme,
4009
4087
  projectRoot
4010
4088
  } = options;
4011
- const clientJsPath = projectRoot ? getClientJsPath(projectRoot) : "/static/client.js";
4012
- const clientCssPath = projectRoot ? getClientCssPath(projectRoot) : "/static/client.css";
4013
- const assetManifest = projectRoot ? loadAssetManifest(projectRoot) : null;
4089
+ const clientJsPath = env === "dev" ? "/static/client.js" : projectRoot ? getClientJsPath(projectRoot) : "/static/client.js";
4090
+ const clientCssPath = env === "dev" ? "/static/client.css" : projectRoot ? getClientCssPath(projectRoot) : "/static/client.css";
4091
+ const assetManifest = env === "prod" && projectRoot ? loadAssetManifest(projectRoot) : null;
4014
4092
  const isDataReq = isDataRequest(req);
4015
4093
  if (env === "prod" && ssgOutDir) {
4016
4094
  if (isDataReq) {
@@ -4066,7 +4144,7 @@ async function handlePageRequestInternal(options) {
4066
4144
  const reqLogger = getRequestLogger(req);
4067
4145
  reqLogger.error("SSR shell error", err, { route: "not-found" });
4068
4146
  if (!res.headersSent && errorPage) {
4069
- renderErrorPageWithStream(errorPage, req, res, err, routeChunks);
4147
+ renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot, env);
4070
4148
  } else if (!res.headersSent) {
4071
4149
  res.statusCode = 500;
4072
4150
  res.setHeader("Content-Type", "text/html; charset=utf-8");
@@ -4113,7 +4191,7 @@ async function handlePageRequestInternal(options) {
4113
4191
  return;
4114
4192
  } else {
4115
4193
  if (errorPage) {
4116
- await renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot);
4194
+ await renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env);
4117
4195
  return;
4118
4196
  } else {
4119
4197
  throw error;
@@ -4175,7 +4253,7 @@ async function handlePageRequestInternal(options) {
4175
4253
  const reqLogger = getRequestLogger(req);
4176
4254
  reqLogger.error("SSR shell error", err, { route: matched?.route?.pattern || "unknown" });
4177
4255
  if (!res.headersSent && errorPage) {
4178
- renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot);
4256
+ renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot, env);
4179
4257
  } else if (!res.headersSent) {
4180
4258
  res.statusCode = 500;
4181
4259
  res.setHeader("Content-Type", "text/html; charset=utf-8");
@@ -4193,7 +4271,7 @@ async function handlePageRequestInternal(options) {
4193
4271
  abort();
4194
4272
  });
4195
4273
  }
4196
- async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot) {
4274
+ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env = "dev") {
4197
4275
  try {
4198
4276
  const isDataReq = isDataRequest(req);
4199
4277
  const ctx = {
@@ -4222,9 +4300,9 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
4222
4300
  return;
4223
4301
  }
4224
4302
  const appTree = buildAppTree(errorPage, { error: String(error) }, initialData.props);
4225
- const clientJsPath = projectRoot ? getClientJsPath(projectRoot) : "/static/client.js";
4226
- const clientCssPath = projectRoot ? getClientCssPath(projectRoot) : "/static/client.css";
4227
- const assetManifest = projectRoot ? loadAssetManifest(projectRoot) : null;
4303
+ const clientJsPath = env === "dev" ? "/static/client.js" : projectRoot ? getClientJsPath(projectRoot) : "/static/client.js";
4304
+ const clientCssPath = env === "dev" ? "/static/client.css" : projectRoot ? getClientCssPath(projectRoot) : "/static/client.css";
4305
+ const assetManifest = env === "prod" && projectRoot ? loadAssetManifest(projectRoot) : null;
4228
4306
  const chunkName = routeChunks[ERROR_CHUNK_KEY];
4229
4307
  let chunkHref = null;
4230
4308
  if (chunkName != null) {
@@ -5099,11 +5177,6 @@ import { useEffect, useState, useRef } from "react";
5099
5177
  // modules/runtime/client/RouterView.tsx
5100
5178
  import { jsx } from "react/jsx-runtime";
5101
5179
  function RouterView({ state }) {
5102
- console.log("[loly:RouterView] Rendering", {
5103
- url: state.url,
5104
- hasRoute: !!state.route,
5105
- hasComponents: !!state.components
5106
- });
5107
5180
  if (!state.route) {
5108
5181
  if (state.components === null) {
5109
5182
  return null;
@@ -5115,11 +5188,6 @@ function RouterView({ state }) {
5115
5188
  }
5116
5189
  const { Page, layouts } = state.components;
5117
5190
  const { params, props } = state;
5118
- console.log("[loly:RouterView] Creating page element", {
5119
- hasPage: !!Page,
5120
- layoutsCount: layouts.length,
5121
- paramsKeys: Object.keys(params)
5122
- });
5123
5191
  let element = /* @__PURE__ */ jsx(Page, { params, ...props });
5124
5192
  const layoutChain = layouts.slice().reverse();
5125
5193
  for (const Layout of layoutChain) {
@@ -5425,7 +5493,6 @@ async function navigate(nextUrl, handlers, options) {
5425
5493
  revalidate: options?.revalidate
5426
5494
  });
5427
5495
  if (json && json.error) {
5428
- console.log("[client] Error detected in response:", json);
5429
5496
  if (errorRoute) {
5430
5497
  const handled = await handleErrorRoute(
5431
5498
  nextUrl,
@@ -5467,50 +5534,23 @@ async function navigate(nextUrl, handlers, options) {
5467
5534
  }
5468
5535
  function createClickHandler(navigate2) {
5469
5536
  return function handleClick(ev) {
5470
- const target = ev.target;
5471
- const tagName = target?.tagName.toLowerCase() || "unknown";
5472
- console.log("[loly:click] Click event received", {
5473
- type: ev.type,
5474
- tagName,
5475
- target: target?.tagName,
5476
- defaultPrevented: ev.defaultPrevented,
5477
- button: ev.button,
5478
- clientX: ev.clientX,
5479
- clientY: ev.clientY
5480
- });
5481
5537
  try {
5482
- if (ev.defaultPrevented) {
5483
- console.log("[loly:click] Event already prevented, skipping");
5484
- return;
5485
- }
5486
- if (ev.type !== "click") {
5487
- console.log("[loly:click] Not a click event, skipping", { type: ev.type });
5488
- return;
5489
- }
5490
- if (ev.button !== 0) {
5491
- console.log("[loly:click] Not left button, skipping", { button: ev.button });
5492
- return;
5493
- }
5494
- if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) {
5495
- console.log("[loly:click] Modifier keys pressed, skipping");
5496
- return;
5497
- }
5538
+ if (ev.defaultPrevented) return;
5539
+ if (ev.type !== "click") return;
5540
+ if (ev.button !== 0) return;
5541
+ if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;
5542
+ const target = ev.target;
5498
5543
  if (ev.clientX === 0 && ev.clientY === 0 && ev.detail === 0) {
5499
5544
  if (target) {
5500
- const tagName3 = target.tagName.toLowerCase();
5501
- if (tagName3 === "input" || tagName3 === "textarea" || tagName3 === "button" || tagName3 === "select") {
5502
- console.log("[loly:click] Synthetic event on interactive element, skipping", { tagName: tagName3 });
5545
+ const tagName2 = target.tagName.toLowerCase();
5546
+ if (tagName2 === "input" || tagName2 === "textarea" || tagName2 === "button" || tagName2 === "select") {
5503
5547
  return;
5504
5548
  }
5505
5549
  }
5506
5550
  }
5507
- if (!target) {
5508
- console.log("[loly:click] No target, skipping");
5509
- return;
5510
- }
5511
- const tagName2 = target.tagName.toLowerCase();
5512
- if (tagName2 === "input" || tagName2 === "textarea" || tagName2 === "button" || tagName2 === "select" || target.isContentEditable || target.getAttribute("contenteditable") === "true") {
5513
- console.log("[loly:click] Target is interactive element, skipping", { tagName: tagName2 });
5551
+ if (!target) return;
5552
+ const tagName = target.tagName.toLowerCase();
5553
+ if (tagName === "input" || tagName === "textarea" || tagName === "button" || tagName === "select" || target.isContentEditable || target.getAttribute("contenteditable") === "true") {
5514
5554
  return;
5515
5555
  }
5516
5556
  const interactiveParent = target.closest("input, textarea, button, select, [contenteditable], label");
@@ -5518,60 +5558,29 @@ function createClickHandler(navigate2) {
5518
5558
  if (interactiveParent.tagName.toLowerCase() === "label") {
5519
5559
  const label = interactiveParent;
5520
5560
  if (label.control) {
5521
- console.log("[loly:click] Inside label with control, skipping");
5522
5561
  return;
5523
5562
  }
5524
5563
  } else {
5525
- console.log("[loly:click] Inside interactive parent, skipping", {
5526
- parentTag: interactiveParent.tagName.toLowerCase()
5527
- });
5528
5564
  return;
5529
5565
  }
5530
5566
  }
5531
5567
  const anchor = target.closest("a[href]");
5532
- if (!anchor) {
5533
- console.log("[loly:click] No anchor found, skipping");
5534
- return;
5535
- }
5536
- console.log("[loly:click] Anchor found, processing navigation", {
5537
- href: anchor.getAttribute("href")
5538
- });
5568
+ if (!anchor) return;
5539
5569
  const href = anchor.getAttribute("href");
5540
- if (!href) {
5541
- console.log("[loly:click] No href attribute, skipping");
5542
- return;
5543
- }
5544
- if (href.startsWith("#")) {
5545
- console.log("[loly:click] Hash link, skipping");
5546
- return;
5547
- }
5570
+ if (!href) return;
5571
+ if (href.startsWith("#")) return;
5548
5572
  const url = new URL(href, window.location.href);
5549
- if (url.origin !== window.location.origin) {
5550
- console.log("[loly:click] External link, skipping", { origin: url.origin });
5551
- return;
5552
- }
5553
- if (anchor.target && anchor.target !== "_self") {
5554
- console.log("[loly:click] Link has target, skipping", { target: anchor.target });
5555
- return;
5556
- }
5573
+ if (url.origin !== window.location.origin) return;
5574
+ if (anchor.target && anchor.target !== "_self") return;
5557
5575
  ev.preventDefault();
5558
- console.log("[loly:click] Prevented default, navigating");
5559
5576
  const nextUrl = url.pathname + url.search;
5560
5577
  const currentUrl = window.location.pathname + window.location.search;
5561
- if (nextUrl === currentUrl) {
5562
- console.log("[loly:click] Same URL, skipping", { nextUrl });
5563
- return;
5564
- }
5578
+ if (nextUrl === currentUrl) return;
5565
5579
  const shouldRevalidate = anchor.hasAttribute("data-revalidate") && anchor.getAttribute("data-revalidate") !== "false";
5566
- console.log("[loly:click] Pushing state and navigating", {
5567
- nextUrl,
5568
- currentUrl,
5569
- shouldRevalidate
5570
- });
5571
5580
  window.history.pushState({}, "", nextUrl);
5572
5581
  navigate2(nextUrl, shouldRevalidate ? { revalidate: true } : void 0);
5573
5582
  } catch (error) {
5574
- console.error("[loly:click] Error in click handler:", error);
5583
+ console.error("[navigation] Error in click handler:", error);
5575
5584
  }
5576
5585
  };
5577
5586
  }
@@ -5590,10 +5599,6 @@ function AppShell({
5590
5599
  notFoundRoute,
5591
5600
  errorRoute
5592
5601
  }) {
5593
- console.log("[loly:AppShell] Component rendering", {
5594
- url: initialState.url,
5595
- hasRoute: !!initialState.route
5596
- });
5597
5602
  const [state, setState] = useState(initialState);
5598
5603
  const handlersRef = useRef({
5599
5604
  setState,
@@ -5602,11 +5607,6 @@ function AppShell({
5602
5607
  errorRoute
5603
5608
  });
5604
5609
  useEffect(() => {
5605
- console.log("[loly:AppShell] Updating handlersRef", {
5606
- routesCount: routes.length,
5607
- hasNotFound: !!notFoundRoute,
5608
- hasError: !!errorRoute
5609
- });
5610
5610
  handlersRef.current = {
5611
5611
  setState,
5612
5612
  routes,
@@ -5615,34 +5615,16 @@ function AppShell({
5615
5615
  };
5616
5616
  }, [routes, notFoundRoute, errorRoute]);
5617
5617
  useEffect(() => {
5618
- const effectId = Math.random().toString(36).substring(7);
5619
- console.log("[loly:AppShell] Setting up event listeners", { effectId });
5620
5618
  let isMounted = true;
5621
- let listenerCount = 0;
5622
5619
  async function handleNavigate(nextUrl, options) {
5623
- if (!isMounted) {
5624
- console.warn("[loly:AppShell] navigate called but component is unmounted");
5625
- return;
5626
- }
5627
- console.log("[loly:AppShell] Navigating to", nextUrl, options);
5620
+ if (!isMounted) return;
5628
5621
  await navigate(nextUrl, handlersRef.current, options);
5629
5622
  }
5630
5623
  const handleClick = createClickHandler(handleNavigate);
5631
5624
  const handlePopState = createPopStateHandler(handleNavigate);
5632
5625
  window.addEventListener("click", handleClick, false);
5633
5626
  window.addEventListener("popstate", handlePopState, false);
5634
- listenerCount = 2;
5635
- console.log("[loly:AppShell] Event listeners added", {
5636
- clickListener: true,
5637
- popStateListener: true,
5638
- totalListeners: listenerCount
5639
- });
5640
5627
  return () => {
5641
- console.log("[loly:AppShell] Cleaning up event listeners", {
5642
- effectId,
5643
- wasMounted: isMounted,
5644
- listenersToRemove: listenerCount
5645
- });
5646
5628
  isMounted = false;
5647
5629
  window.removeEventListener("click", handleClick, false);
5648
5630
  window.removeEventListener("popstate", handlePopState, false);
@@ -5657,7 +5639,6 @@ function AppShell({
5657
5639
 
5658
5640
  // modules/runtime/client/bootstrap.tsx
5659
5641
  import { jsx as jsx3 } from "react/jsx-runtime";
5660
- var __loly_hydrated = false;
5661
5642
  async function loadInitialRoute(initialUrl, initialData, routes, notFoundRoute, errorRoute) {
5662
5643
  const isInitialNotFound = initialData?.notFound === true;
5663
5644
  const isInitialError = initialData?.error === true;
@@ -5684,7 +5665,7 @@ async function loadInitialRoute(initialUrl, initialData, routes, notFoundRoute,
5684
5665
  initialComponents = await notFoundRoute.load();
5685
5666
  } else {
5686
5667
  console.warn(
5687
- `[client] No route match found for ${initialUrl}. Routes:`,
5668
+ `[client] No route match found for ${initialUrl}. Available routes:`,
5688
5669
  routes.map((r) => r.pattern)
5689
5670
  );
5690
5671
  }
@@ -5697,29 +5678,62 @@ async function loadInitialRoute(initialUrl, initialData, routes, notFoundRoute,
5697
5678
  props: initialData?.props ?? {}
5698
5679
  };
5699
5680
  }
5700
- function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
5701
- if (__loly_hydrated) {
5702
- console.warn("[loly:runtime] bootstrapClient SKIPPED (already hydrated)");
5681
+ function setupHotReload2() {
5682
+ const nodeEnv = typeof process !== "undefined" && process?.env?.NODE_ENV || "production";
5683
+ const isDev = nodeEnv !== "production";
5684
+ if (!isDev) {
5703
5685
  return;
5704
5686
  }
5705
- __loly_hydrated = true;
5706
- console.log("[loly:runtime] bootstrapClient START");
5687
+ try {
5688
+ console.log("[hot-reload] Attempting to connect to /__fw/hot...");
5689
+ const eventSource = new EventSource("/__fw/hot");
5690
+ let reloadTimeout = null;
5691
+ eventSource.addEventListener("message", (event) => {
5692
+ const data = event.data;
5693
+ if (data && data.startsWith("reload:")) {
5694
+ const filePath = data.slice(7);
5695
+ console.log(`[hot-reload] File changed: ${filePath}`);
5696
+ if (reloadTimeout) {
5697
+ clearTimeout(reloadTimeout);
5698
+ }
5699
+ reloadTimeout = setTimeout(() => {
5700
+ console.log("[hot-reload] Reloading page...");
5701
+ window.location.reload();
5702
+ }, 500);
5703
+ }
5704
+ });
5705
+ eventSource.addEventListener("ping", () => {
5706
+ console.log("[hot-reload] \u2713 Connected to hot reload server");
5707
+ });
5708
+ eventSource.onopen = () => {
5709
+ console.log("[hot-reload] \u2713 SSE connection opened");
5710
+ };
5711
+ eventSource.onerror = (error) => {
5712
+ const states = ["CONNECTING", "OPEN", "CLOSED"];
5713
+ const state = states[eventSource.readyState] || "UNKNOWN";
5714
+ if (eventSource.readyState === EventSource.CONNECTING) {
5715
+ console.log("[hot-reload] Connecting...");
5716
+ } else if (eventSource.readyState === EventSource.OPEN) {
5717
+ console.warn("[hot-reload] Connection error (but connection is open):", error);
5718
+ } else {
5719
+ console.log("[hot-reload] Connection closed (readyState:", state, ")");
5720
+ }
5721
+ };
5722
+ } catch (error) {
5723
+ console.log("[hot-reload] EventSource not supported or error:", error);
5724
+ }
5725
+ }
5726
+ function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
5727
+ console.log("[client] Bootstrap starting, setting up hot reload...");
5728
+ setupHotReload2();
5707
5729
  (async function bootstrap() {
5708
5730
  const container = document.getElementById(APP_CONTAINER_ID2);
5709
5731
  const initialData = getWindowData();
5710
- console.log("[loly:runtime] bootstrap starting", {
5711
- hasContainer: !!container,
5712
- hasInitialData: !!initialData,
5713
- containerId: APP_CONTAINER_ID2
5714
- });
5715
5732
  if (!container) {
5716
- console.error(
5717
- `[loly:runtime] Container #${APP_CONTAINER_ID2} not found.`
5718
- );
5733
+ console.error(`Container #${APP_CONTAINER_ID2} not found for hydration`);
5719
5734
  return;
5720
5735
  }
5721
5736
  const initialUrl = window.location.pathname + window.location.search;
5722
- console.log("[loly:runtime] Loading initial route", { initialUrl });
5723
5737
  try {
5724
5738
  const initialState = await loadInitialRoute(
5725
5739
  initialUrl,
@@ -5728,15 +5742,9 @@ function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
5728
5742
  notFoundRoute,
5729
5743
  errorRoute
5730
5744
  );
5731
- console.log("[loly:runtime] Initial route loaded", {
5732
- url: initialState.url,
5733
- hasRoute: !!initialState.route,
5734
- hasComponents: !!initialState.components
5735
- });
5736
5745
  if (initialData?.metadata) {
5737
5746
  applyMetadata(initialData.metadata);
5738
5747
  }
5739
- console.log("[loly:runtime] Hydrating React app");
5740
5748
  hydrateRoot(
5741
5749
  container,
5742
5750
  /* @__PURE__ */ jsx3(
@@ -5749,9 +5757,12 @@ function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
5749
5757
  }
5750
5758
  )
5751
5759
  );
5752
- console.log("[loly:runtime] Hydrated successfully");
5753
5760
  } catch (error) {
5754
- console.error("[loly:runtime] Error during hydration:", error);
5761
+ console.error(
5762
+ "[client] Error loading initial route components for",
5763
+ initialUrl,
5764
+ error
5765
+ );
5755
5766
  window.location.reload();
5756
5767
  }
5757
5768
  })();