@lolyjs/core 0.1.0-alpha.8 → 0.2.0-alpha.0

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,44 +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
- * @param routes - Array of client routes
251
- * @param notFoundRoute - Not-found route definition
252
- * @param errorRoute - Error route definition
253
- */
254
- declare function bootstrapClient(routes: ClientRouteLoaded[], notFoundRoute: ClientRouteLoaded | null, errorRoute?: ClientRouteLoaded | null): void;
255
-
256
219
  declare function withCache(fn: any, options: any): any;
257
220
 
258
221
  /**
@@ -479,4 +442,4 @@ declare function requestLoggerMiddleware(options?: {
479
442
  */
480
443
  declare function getRequestLogger(req: Request): Logger;
481
444
 
482
- 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,44 +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
- * @param routes - Array of client routes
251
- * @param notFoundRoute - Not-found route definition
252
- * @param errorRoute - Error route definition
253
- */
254
- declare function bootstrapClient(routes: ClientRouteLoaded[], notFoundRoute: ClientRouteLoaded | null, errorRoute?: ClientRouteLoaded | null): void;
255
-
256
219
  declare function withCache(fn: any, options: any): any;
257
220
 
258
221
  /**
@@ -479,4 +442,4 @@ declare function requestLoggerMiddleware(options?: {
479
442
  */
480
443
  declare function getRequestLogger(req: Request): Logger;
481
444
 
482
- 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);
@@ -5696,26 +5678,57 @@ async function loadInitialRoute(initialUrl, initialData, routes, notFoundRoute,
5696
5678
  props: initialData?.props ?? {}
5697
5679
  };
5698
5680
  }
5681
+ function setupHotReload2() {
5682
+ try {
5683
+ console.log("[hot-reload] Attempting to connect to /__fw/hot...");
5684
+ const eventSource = new EventSource("/__fw/hot");
5685
+ let reloadTimeout = null;
5686
+ eventSource.addEventListener("message", (event) => {
5687
+ const data = event.data;
5688
+ if (data && data.startsWith("reload:")) {
5689
+ const filePath = data.slice(7);
5690
+ console.log(`[hot-reload] File changed: ${filePath}`);
5691
+ if (reloadTimeout) {
5692
+ clearTimeout(reloadTimeout);
5693
+ }
5694
+ reloadTimeout = setTimeout(() => {
5695
+ console.log("[hot-reload] Reloading page...");
5696
+ window.location.reload();
5697
+ }, 500);
5698
+ }
5699
+ });
5700
+ eventSource.addEventListener("ping", () => {
5701
+ console.log("[hot-reload] \u2713 Connected to hot reload server");
5702
+ });
5703
+ eventSource.onopen = () => {
5704
+ console.log("[hot-reload] \u2713 SSE connection opened");
5705
+ };
5706
+ eventSource.onerror = (error) => {
5707
+ const states = ["CONNECTING", "OPEN", "CLOSED"];
5708
+ const state = states[eventSource.readyState] || "UNKNOWN";
5709
+ if (eventSource.readyState === EventSource.CONNECTING) {
5710
+ console.log("[hot-reload] Connecting...");
5711
+ } else if (eventSource.readyState === EventSource.OPEN) {
5712
+ console.warn("[hot-reload] Connection error (but connection is open):", error);
5713
+ } else {
5714
+ console.log("[hot-reload] Connection closed (readyState:", state, ")");
5715
+ }
5716
+ };
5717
+ } catch (error) {
5718
+ console.log("[hot-reload] EventSource not supported or error:", error);
5719
+ }
5720
+ }
5699
5721
  function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
5700
- console.log("[loly:runtime] bootstrapClient called", {
5701
- routesCount: routes.length,
5702
- hasNotFound: !!notFoundRoute,
5703
- hasError: !!errorRoute
5704
- });
5722
+ console.log("[client] Bootstrap starting, setting up hot reload...");
5723
+ setupHotReload2();
5705
5724
  (async function bootstrap() {
5706
5725
  const container = document.getElementById(APP_CONTAINER_ID2);
5707
5726
  const initialData = getWindowData();
5708
- console.log("[loly:runtime] bootstrap starting", {
5709
- hasContainer: !!container,
5710
- containerId: APP_CONTAINER_ID2,
5711
- hasInitialData: !!initialData
5712
- });
5713
5727
  if (!container) {
5714
- console.error(`[loly:runtime] Container #${APP_CONTAINER_ID2} not found for hydration`);
5728
+ console.error(`Container #${APP_CONTAINER_ID2} not found for hydration`);
5715
5729
  return;
5716
5730
  }
5717
5731
  const initialUrl = window.location.pathname + window.location.search;
5718
- console.log("[loly:runtime] Loading initial route", { initialUrl });
5719
5732
  try {
5720
5733
  const initialState = await loadInitialRoute(
5721
5734
  initialUrl,
@@ -5724,15 +5737,9 @@ function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
5724
5737
  notFoundRoute,
5725
5738
  errorRoute
5726
5739
  );
5727
- console.log("[loly:runtime] Initial route loaded", {
5728
- url: initialState.url,
5729
- hasRoute: !!initialState.route,
5730
- hasComponents: !!initialState.components
5731
- });
5732
5740
  if (initialData?.metadata) {
5733
5741
  applyMetadata(initialData.metadata);
5734
5742
  }
5735
- console.log("[loly:runtime] Hydrating React app");
5736
5743
  hydrateRoot(
5737
5744
  container,
5738
5745
  /* @__PURE__ */ jsx3(
@@ -5745,10 +5752,9 @@ function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
5745
5752
  }
5746
5753
  )
5747
5754
  );
5748
- console.log("[loly:runtime] React app hydrated successfully");
5749
5755
  } catch (error) {
5750
5756
  console.error(
5751
- "[loly:runtime] Error loading initial route components for",
5757
+ "[client] Error loading initial route components for",
5752
5758
  initialUrl,
5753
5759
  error
5754
5760
  );