@lolyjs/core 0.2.0-alpha.23 → 0.2.0-alpha.25

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/cli.js CHANGED
@@ -11464,22 +11464,36 @@ function copyStaticAssets(projectRoot, outDir) {
11464
11464
  const assetsSrc = path10.join(projectRoot, "assets");
11465
11465
  const assetsDest = path10.join(outDir, "assets");
11466
11466
  copyDirRecursive(assetsSrc, assetsDest);
11467
- const appDir = path10.join(projectRoot, "app");
11467
+ const publicDir = path10.join(projectRoot, "public");
11468
11468
  const candidates = ["favicon.ico", "favicon.png"];
11469
11469
  for (const name of candidates) {
11470
- const fromApp = path10.join(appDir, name);
11471
- const fromRoot = path10.join(projectRoot, name);
11472
- let src = null;
11473
- if (fs10.existsSync(fromApp)) src = fromApp;
11474
- else if (fs10.existsSync(fromRoot)) src = fromRoot;
11475
- if (src) {
11470
+ const fromPublic = path10.join(publicDir, name);
11471
+ if (fs10.existsSync(fromPublic)) {
11476
11472
  const dest = path10.join(outDir, name);
11477
11473
  ensureDir(path10.dirname(dest));
11478
- fs10.copyFileSync(src, dest);
11474
+ fs10.copyFileSync(fromPublic, dest);
11479
11475
  break;
11480
11476
  }
11481
11477
  }
11482
11478
  }
11479
+ function getFaviconInfo(projectRoot, staticDir = "public", isDev = false) {
11480
+ const candidates = [
11481
+ { name: "favicon.ico", type: "image/x-icon" },
11482
+ { name: "favicon.png", type: "image/png" }
11483
+ ];
11484
+ const publicDir = path10.join(projectRoot, staticDir);
11485
+ for (const candidate of candidates) {
11486
+ const publicPath = path10.join(publicDir, candidate.name);
11487
+ if (fs10.existsSync(publicPath)) {
11488
+ return {
11489
+ path: `/${candidate.name}`,
11490
+ // Served at root from public/
11491
+ type: candidate.type
11492
+ };
11493
+ }
11494
+ }
11495
+ return null;
11496
+ }
11483
11497
  function generateAssetManifest(outDir, stats) {
11484
11498
  const manifest = {
11485
11499
  client: {
@@ -11964,8 +11978,12 @@ function createDocumentTree(options) {
11964
11978
  clientJsPath = "/static/client.js",
11965
11979
  clientCssPath = "/static/client.css",
11966
11980
  nonce,
11967
- includeInlineScripts = true
11981
+ includeInlineScripts = true,
11968
11982
  // Default true - scripts inline in body for both SSR and SSG
11983
+ faviconPath = FAVICON_PATH,
11984
+ // Default to /static/favicon.png for backward compatibility
11985
+ faviconType = "image/png"
11986
+ // Default to PNG for backward compatibility
11969
11987
  } = options;
11970
11988
  const metaObj = meta ?? null;
11971
11989
  const title = metaObj?.title ?? titleFallback ?? "My Framework Dev";
@@ -12272,10 +12290,11 @@ function createDocumentTree(options) {
12272
12290
  href: chunkHref,
12273
12291
  as: "script"
12274
12292
  }),
12275
- React.createElement("link", {
12293
+ faviconPath && React.createElement("link", {
12294
+ key: "favicon",
12276
12295
  rel: "icon",
12277
- href: FAVICON_PATH,
12278
- type: "image/png"
12296
+ href: faviconPath,
12297
+ type: faviconType || (faviconPath.endsWith(".ico") ? "image/x-icon" : "image/png")
12279
12298
  }),
12280
12299
  React.createElement("link", {
12281
12300
  rel: "stylesheet",
@@ -12805,7 +12824,7 @@ async function handlePageRequest(options) {
12805
12824
  const { errorPage, req, res, routeChunks, theme, projectRoot } = options;
12806
12825
  const reqLogger = getRequestLogger(req);
12807
12826
  if (errorPage) {
12808
- await renderErrorPageWithStream(errorPage, req, res, error, routeChunks || {}, theme, projectRoot, options.env);
12827
+ await renderErrorPageWithStream(errorPage, req, res, error, routeChunks || {}, theme, projectRoot, options.env, options.config);
12809
12828
  } else {
12810
12829
  reqLogger.error("Unhandled error in page request", error, {
12811
12830
  urlPath: options.urlPath,
@@ -12831,11 +12850,13 @@ async function handlePageRequestInternal(options) {
12831
12850
  env = "dev",
12832
12851
  ssgOutDir,
12833
12852
  theme,
12834
- projectRoot
12853
+ projectRoot,
12854
+ config
12835
12855
  } = options;
12836
12856
  const clientJsPath = env === "dev" ? "/static/client.js" : projectRoot ? getClientJsPath(projectRoot) : "/static/client.js";
12837
12857
  const clientCssPath = env === "dev" ? "/static/client.css" : projectRoot ? getClientCssPath(projectRoot) : "/static/client.css";
12838
12858
  const assetManifest = env === "prod" && projectRoot ? loadAssetManifest(projectRoot) : null;
12859
+ const faviconInfo = projectRoot && config ? getFaviconInfo(projectRoot, config.directories.static, env === "dev") : null;
12839
12860
  const isDataReq = isDataRequest(req);
12840
12861
  const skipLayoutHooks = isDataReq && req.headers["x-skip-layout-hooks"] === "true";
12841
12862
  if (env === "prod" && ssgOutDir) {
@@ -12950,7 +12971,9 @@ async function handlePageRequestInternal(options) {
12950
12971
  theme,
12951
12972
  clientJsPath,
12952
12973
  clientCssPath,
12953
- nonce: nonce2
12974
+ nonce: nonce2,
12975
+ faviconPath: faviconInfo?.path || null,
12976
+ faviconType: faviconInfo?.type || null
12954
12977
  });
12955
12978
  let didError2 = false;
12956
12979
  const { pipe: pipe2, abort: abort2 } = renderToPipeableStream(documentTree2, {
@@ -12965,7 +12988,7 @@ async function handlePageRequestInternal(options) {
12965
12988
  const reqLogger2 = getRequestLogger(req);
12966
12989
  reqLogger2.error("SSR shell error", err, { route: "not-found" });
12967
12990
  if (!res.headersSent && errorPage) {
12968
- renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot, env);
12991
+ renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot, env, config);
12969
12992
  } else if (!res.headersSent) {
12970
12993
  res.statusCode = 500;
12971
12994
  res.setHeader("Content-Type", "text/html; charset=utf-8");
@@ -13080,7 +13103,7 @@ async function handlePageRequestInternal(options) {
13080
13103
  return;
13081
13104
  } else {
13082
13105
  if (errorPage) {
13083
- await renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env);
13106
+ await renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env, config);
13084
13107
  return;
13085
13108
  } else {
13086
13109
  throw error;
@@ -13158,7 +13181,9 @@ async function handlePageRequestInternal(options) {
13158
13181
  theme,
13159
13182
  clientJsPath,
13160
13183
  clientCssPath,
13161
- nonce
13184
+ nonce,
13185
+ faviconPath: faviconInfo?.path || null,
13186
+ faviconType: faviconInfo?.type || null
13162
13187
  });
13163
13188
  let didError = false;
13164
13189
  const { pipe, abort } = renderToPipeableStream(documentTree, {
@@ -13206,7 +13231,7 @@ async function handlePageRequestInternal(options) {
13206
13231
  abort();
13207
13232
  });
13208
13233
  }
13209
- async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env = "dev") {
13234
+ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env = "dev", config) {
13210
13235
  try {
13211
13236
  const isDataReq = isDataRequest(req);
13212
13237
  const skipLayoutHooks = isDataReq && req.headers["x-skip-layout-hooks"] === "true";
@@ -13217,6 +13242,7 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
13217
13242
  pathname: req.path,
13218
13243
  locals: { error }
13219
13244
  };
13245
+ const faviconInfo = projectRoot && config ? getFaviconInfo(projectRoot, config.directories.static, env === "dev") : null;
13220
13246
  const layoutProps = {};
13221
13247
  const reqLogger = getRequestLogger(req);
13222
13248
  if (!skipLayoutHooks && errorPage.layoutServerHooks && errorPage.layoutServerHooks.length > 0) {
@@ -13322,7 +13348,9 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
13322
13348
  theme,
13323
13349
  clientJsPath,
13324
13350
  clientCssPath,
13325
- nonce
13351
+ nonce,
13352
+ faviconPath: faviconInfo?.path || null,
13353
+ faviconType: faviconInfo?.type || null
13326
13354
  });
13327
13355
  let didError = false;
13328
13356
  const { pipe, abort } = renderToPipeableStream(documentTree, {
@@ -13366,11 +13394,12 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
13366
13394
  }
13367
13395
 
13368
13396
  // modules/build/ssg/renderer.ts
13369
- async function renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params) {
13397
+ async function renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params, config) {
13370
13398
  const routeChunks = loadChunksFromManifest(projectRoot);
13371
13399
  const assetManifest = loadAssetManifest(projectRoot);
13372
13400
  const clientJsPath = getClientJsPath(projectRoot);
13373
13401
  const clientCssPath = getClientCssPath(projectRoot);
13402
+ const faviconInfo = config ? getFaviconInfo(projectRoot, config.directories.static, false) : null;
13374
13403
  const chunkName = routeChunks[route.pattern];
13375
13404
  let chunkHref = null;
13376
13405
  if (chunkName != null) {
@@ -13478,8 +13507,10 @@ async function renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params)
13478
13507
  entrypointFiles,
13479
13508
  clientJsPath,
13480
13509
  clientCssPath,
13481
- includeInlineScripts: true
13510
+ includeInlineScripts: true,
13482
13511
  // SSG needs inline scripts (renderToString doesn't support bootstrapScripts)
13512
+ faviconPath: faviconInfo?.path || null,
13513
+ faviconType: faviconInfo?.type || null
13483
13514
  });
13484
13515
  const html = "<!DOCTYPE html>" + renderToString(documentTree);
13485
13516
  const dir = pathToOutDir(ssgOutDir, urlPath);
@@ -13492,7 +13523,7 @@ async function renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params)
13492
13523
 
13493
13524
  // modules/build/ssg/builder.ts
13494
13525
  init_globals();
13495
- async function buildStaticPages(projectRoot, routes) {
13526
+ async function buildStaticPages(projectRoot, routes, config) {
13496
13527
  const ssgOutDir = path19.join(projectRoot, BUILD_FOLDER_NAME, "ssg");
13497
13528
  ensureDir(ssgOutDir);
13498
13529
  for (const route of routes) {
@@ -13541,7 +13572,7 @@ async function buildStaticPages(projectRoot, routes) {
13541
13572
  }
13542
13573
  for (const params of allParams) {
13543
13574
  const urlPath = buildPathFromPattern(route.pattern, params);
13544
- await renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params);
13575
+ await renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params, config);
13545
13576
  }
13546
13577
  }
13547
13578
  console.log(`\u2705 [framework][ssg] Finished building all static pages`);
@@ -14190,7 +14221,7 @@ async function buildApp(options = {}) {
14190
14221
  writeClientBoostrapManifest(projectRoot);
14191
14222
  writeClientRoutesManifest(routes, projectRoot);
14192
14223
  await buildClientBundle(projectRoot);
14193
- await buildStaticPages(projectRoot, routes);
14224
+ await buildStaticPages(projectRoot, routes, config);
14194
14225
  delete process.env.LOLY_BUILD;
14195
14226
  console.log(`[framework][build] Build completed successfully`);
14196
14227
  }
@@ -16432,7 +16463,8 @@ function setupRoutes(options) {
16432
16463
  env: isDev ? "dev" : "prod",
16433
16464
  ssgOutDir,
16434
16465
  theme: req.cookies?.theme || "light",
16435
- projectRoot
16466
+ projectRoot,
16467
+ config
16436
16468
  });
16437
16469
  });
16438
16470
  }
@@ -17046,7 +17078,6 @@ async function executeGuard(guardFn, ctx) {
17046
17078
  const guardCtx = {
17047
17079
  user: ctx.user,
17048
17080
  req: ctx.req,
17049
- socket: ctx.socket,
17050
17081
  namespace: ctx.pathname
17051
17082
  };
17052
17083
  const result = await guardFn(guardCtx);
@@ -17153,9 +17184,6 @@ var generateActions = (socket, namespace, presence) => {
17153
17184
  return {
17154
17185
  emit: async (event, payload) => {
17155
17186
  if (!presence) {
17156
- console.warn(
17157
- "[loly:realtime] toUser() requires presence manager. Make sure realtime is properly configured."
17158
- );
17159
17187
  return;
17160
17188
  }
17161
17189
  const socketIds = await presence.getSocketsForUser(userId);
@@ -17258,19 +17286,12 @@ async function setupWssEvents(options) {
17258
17286
  const subClient = pubClient.duplicate();
17259
17287
  io.adapter(createAdapter(pubClient, subClient));
17260
17288
  } catch (error) {
17261
- console.error(
17262
- "[loly:realtime] Failed to setup Redis adapter:",
17263
- error instanceof Error ? error.message : String(error)
17264
- );
17265
17289
  throw error;
17266
17290
  }
17267
17291
  }
17268
17292
  for (const wssRoute of wssRoutes) {
17269
17293
  const normalized = wssRoute.normalized;
17270
17294
  if (!normalized) {
17271
- console.warn(
17272
- `[loly:realtime] Skipping route ${wssRoute.pattern}: No normalized route definition`
17273
- );
17274
17295
  continue;
17275
17296
  }
17276
17297
  let namespacePath = normalized.namespace || wssRoute.pattern.replace(/^\/wss/, "");
@@ -17281,9 +17302,7 @@ async function setupWssEvents(options) {
17281
17302
  namespacePath = "/";
17282
17303
  }
17283
17304
  const namespace = io.of(namespacePath);
17284
- console.log(`[loly:realtime] Registered namespace: ${namespacePath} (from pattern: ${wssRoute.pattern})`);
17285
17305
  namespace.on("connection", async (socket) => {
17286
- console.log(`[loly:realtime] Client connected to namespace ${namespacePath}, socket: ${socket.id}`);
17287
17306
  const requestId = generateRequestId();
17288
17307
  socket.requestId = requestId;
17289
17308
  const log = createWssLogger(namespacePath, socket);
@@ -17295,8 +17314,6 @@ async function setupWssEvents(options) {
17295
17314
  await presence.addSocketForUser(String(user.id), socket.id);
17296
17315
  }
17297
17316
  const baseCtx = {
17298
- socket,
17299
- io: namespace.server,
17300
17317
  req: {
17301
17318
  headers: socket.handshake.headers,
17302
17319
  ip: socket.handshake.address,