@lolyjs/core 0.2.0-alpha.18 → 0.2.0-alpha.19

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.js CHANGED
@@ -15001,7 +15001,7 @@ async function runRouteServerHook(route, ctx) {
15001
15001
  }
15002
15002
 
15003
15003
  // modules/server/handlers/response.ts
15004
- function handleDataResponse(res, loaderResult, theme) {
15004
+ function handleDataResponse(res, loaderResult, theme, layoutProps, pageProps, error, message) {
15005
15005
  res.setHeader("Content-Type", "application/json; charset=utf-8");
15006
15006
  if (loaderResult.redirect) {
15007
15007
  res.statusCode = 200;
@@ -15013,14 +15013,26 @@ function handleDataResponse(res, loaderResult, theme) {
15013
15013
  res.end(JSON.stringify({ notFound: true }));
15014
15014
  return;
15015
15015
  }
15016
- res.statusCode = 200;
15017
- res.end(
15018
- JSON.stringify({
15019
- props: loaderResult.props ?? {},
15020
- metadata: loaderResult.metadata ?? null,
15021
- theme: loaderResult.theme ?? theme ?? null
15022
- })
15023
- );
15016
+ const response = {
15017
+ // Combined props for backward compatibility
15018
+ props: loaderResult.props ?? {},
15019
+ metadata: loaderResult.metadata ?? null,
15020
+ theme: loaderResult.theme ?? theme ?? null
15021
+ };
15022
+ if (layoutProps !== void 0 && layoutProps !== null) {
15023
+ response.layoutProps = layoutProps;
15024
+ }
15025
+ if (pageProps !== void 0 && pageProps !== null) {
15026
+ response.pageProps = pageProps;
15027
+ }
15028
+ if (error !== void 0) {
15029
+ response.error = error;
15030
+ }
15031
+ if (message !== void 0) {
15032
+ response.message = message;
15033
+ }
15034
+ res.statusCode = error ? 500 : 200;
15035
+ res.end(JSON.stringify(response));
15024
15036
  }
15025
15037
  function handleRedirect(res, redirect) {
15026
15038
  const { destination, permanent } = redirect;
@@ -15158,6 +15170,7 @@ async function handlePageRequestInternal(options) {
15158
15170
  const clientCssPath = env === "dev" ? "/static/client.css" : projectRoot ? getClientCssPath(projectRoot) : "/static/client.css";
15159
15171
  const assetManifest = env === "prod" && projectRoot ? loadAssetManifest(projectRoot) : null;
15160
15172
  const isDataReq = isDataRequest(req);
15173
+ const skipLayoutHooks = isDataReq && req.headers["x-skip-layout-hooks"] === "true";
15161
15174
  if (env === "prod" && ssgOutDir) {
15162
15175
  if (isDataReq) {
15163
15176
  if (tryServeSsgData(res, ssgOutDir, urlPath)) {
@@ -15181,7 +15194,7 @@ async function handlePageRequestInternal(options) {
15181
15194
  locals: {}
15182
15195
  };
15183
15196
  const layoutProps2 = {};
15184
- if (notFoundPage.layoutServerHooks && notFoundPage.layoutServerHooks.length > 0) {
15197
+ if (!skipLayoutHooks && notFoundPage.layoutServerHooks && notFoundPage.layoutServerHooks.length > 0) {
15185
15198
  for (let i = 0; i < notFoundPage.layoutServerHooks.length; i++) {
15186
15199
  const layoutServerHook = notFoundPage.layoutServerHooks[i];
15187
15200
  if (layoutServerHook) {
@@ -15216,6 +15229,17 @@ async function handlePageRequestInternal(options) {
15216
15229
  ...loaderResult2,
15217
15230
  props: combinedProps2
15218
15231
  };
15232
+ if (isDataReq) {
15233
+ const pagePropsOnly = loaderResult2.props || {};
15234
+ handleDataResponse(
15235
+ res,
15236
+ combinedLoaderResult2,
15237
+ theme,
15238
+ skipLayoutHooks ? null : Object.keys(layoutProps2).length > 0 ? layoutProps2 : null,
15239
+ pagePropsOnly
15240
+ );
15241
+ return;
15242
+ }
15219
15243
  const initialData2 = buildInitialData(urlPath, {}, combinedLoaderResult2);
15220
15244
  const appTree2 = buildAppTree(notFoundPage, {}, initialData2.props);
15221
15245
  initialData2.notFound = true;
@@ -15287,7 +15311,7 @@ async function handlePageRequestInternal(options) {
15287
15311
  const layoutProps = {};
15288
15312
  const layoutMetadata = [];
15289
15313
  const reqLogger = getRequestLogger(req);
15290
- if (route.layoutServerHooks && route.layoutServerHooks.length > 0) {
15314
+ if (!skipLayoutHooks && route.layoutServerHooks && route.layoutServerHooks.length > 0) {
15291
15315
  for (let i = 0; i < route.layoutServerHooks.length; i++) {
15292
15316
  const layoutServerHook = route.layoutServerHooks[i];
15293
15317
  if (layoutServerHook) {
@@ -15371,7 +15395,14 @@ async function handlePageRequestInternal(options) {
15371
15395
  metadata: combinedMetadata
15372
15396
  };
15373
15397
  if (isDataReq) {
15374
- handleDataResponse(res, combinedLoaderResult, theme);
15398
+ const pagePropsOnly = loaderResult.props || {};
15399
+ handleDataResponse(
15400
+ res,
15401
+ combinedLoaderResult,
15402
+ theme,
15403
+ skipLayoutHooks ? null : Object.keys(layoutProps).length > 0 ? layoutProps : null,
15404
+ pagePropsOnly
15405
+ );
15375
15406
  return;
15376
15407
  }
15377
15408
  if (loaderResult.redirect) {
@@ -15465,6 +15496,7 @@ async function handlePageRequestInternal(options) {
15465
15496
  async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env = "dev") {
15466
15497
  try {
15467
15498
  const isDataReq = isDataRequest(req);
15499
+ const skipLayoutHooks = isDataReq && req.headers["x-skip-layout-hooks"] === "true";
15468
15500
  const ctx = {
15469
15501
  req,
15470
15502
  res,
@@ -15474,7 +15506,7 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
15474
15506
  };
15475
15507
  const layoutProps = {};
15476
15508
  const reqLogger = getRequestLogger(req);
15477
- if (errorPage.layoutServerHooks && errorPage.layoutServerHooks.length > 0) {
15509
+ if (!skipLayoutHooks && errorPage.layoutServerHooks && errorPage.layoutServerHooks.length > 0) {
15478
15510
  for (let i = 0; i < errorPage.layoutServerHooks.length; i++) {
15479
15511
  const layoutServerHook = errorPage.layoutServerHooks[i];
15480
15512
  if (layoutServerHook) {
@@ -15512,15 +15544,18 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
15512
15544
  const routerData = buildRouterData(req);
15513
15545
  initialData.error = true;
15514
15546
  if (isDataReq) {
15515
- res.statusCode = 500;
15516
- res.setHeader("Content-Type", "application/json; charset=utf-8");
15517
- res.end(JSON.stringify({
15518
- error: true,
15519
- message: String(error),
15520
- props: initialData.props,
15521
- metadata: combinedLoaderResult.metadata ?? null,
15522
- theme: combinedLoaderResult.theme ?? theme ?? null
15523
- }));
15547
+ const pagePropsOnly = loaderResult.props || {};
15548
+ handleDataResponse(
15549
+ res,
15550
+ combinedLoaderResult,
15551
+ theme,
15552
+ skipLayoutHooks ? null : Object.keys(layoutProps).length > 0 ? layoutProps : null,
15553
+ pagePropsOnly,
15554
+ true,
15555
+ // error flag
15556
+ String(error)
15557
+ // error message
15558
+ );
15524
15559
  return;
15525
15560
  }
15526
15561
  const appTree = buildAppTree(errorPage, { error: String(error) }, initialData.props);
@@ -17526,12 +17561,29 @@ var APP_CONTAINER_ID2 = "__app";
17526
17561
  var ROUTER_NAVIGATE_KEY = "__LOLY_ROUTER_NAVIGATE__";
17527
17562
 
17528
17563
  // modules/runtime/client/window-data.ts
17564
+ var LAYOUT_PROPS_KEY = "__FW_LAYOUT_PROPS__";
17529
17565
  function getWindowData() {
17530
17566
  if (typeof window === "undefined") {
17531
17567
  return null;
17532
17568
  }
17533
17569
  return window[WINDOW_DATA_KEY2] ?? null;
17534
17570
  }
17571
+ function getPreservedLayoutProps() {
17572
+ if (typeof window === "undefined") {
17573
+ return null;
17574
+ }
17575
+ return window[LAYOUT_PROPS_KEY] ?? null;
17576
+ }
17577
+ function setPreservedLayoutProps(props) {
17578
+ if (typeof window === "undefined") {
17579
+ return;
17580
+ }
17581
+ if (props === null) {
17582
+ delete window[LAYOUT_PROPS_KEY];
17583
+ } else {
17584
+ window[LAYOUT_PROPS_KEY] = props;
17585
+ }
17586
+ }
17535
17587
  function getRouterData() {
17536
17588
  if (typeof window === "undefined") {
17537
17589
  return null;
@@ -17867,14 +17919,16 @@ function deleteCacheEntry(key) {
17867
17919
  function buildDataUrl(url) {
17868
17920
  return url + (url.includes("?") ? "&" : "?") + "__fw_data=1";
17869
17921
  }
17870
- async function fetchRouteDataOnce(url) {
17922
+ async function fetchRouteDataOnce(url, skipLayoutHooks = true) {
17871
17923
  const dataUrl = buildDataUrl(url);
17872
- const res = await fetch(dataUrl, {
17873
- headers: {
17874
- "x-fw-data": "1",
17875
- Accept: "application/json"
17876
- }
17877
- });
17924
+ const headers = {
17925
+ "x-fw-data": "1",
17926
+ Accept: "application/json"
17927
+ };
17928
+ if (skipLayoutHooks) {
17929
+ headers["x-skip-layout-hooks"] = "true";
17930
+ }
17931
+ const res = await fetch(dataUrl, { headers });
17878
17932
  let json = {};
17879
17933
  try {
17880
17934
  const text = await res.text();
@@ -17900,7 +17954,7 @@ async function getRouteData(url, options) {
17900
17954
  deleteCacheEntry(key);
17901
17955
  }
17902
17956
  const entry = dataCache.get(key);
17903
- if (entry) {
17957
+ if (entry && !options?.revalidate) {
17904
17958
  if (entry.status === "fulfilled") {
17905
17959
  updateLRU(key);
17906
17960
  return entry.value;
@@ -17909,12 +17963,29 @@ async function getRouteData(url, options) {
17909
17963
  return entry.promise;
17910
17964
  }
17911
17965
  }
17912
- const promise = fetchRouteDataOnce(url).then((value) => {
17913
- setCacheEntry(key, { status: "fulfilled", value });
17966
+ const skipLayoutHooks = !options?.revalidate;
17967
+ const currentEntry = dataCache.get(key);
17968
+ if (currentEntry && !options?.revalidate) {
17969
+ if (currentEntry.status === "fulfilled") {
17970
+ updateLRU(key);
17971
+ return currentEntry.value;
17972
+ }
17973
+ if (currentEntry.status === "pending") {
17974
+ return currentEntry.promise;
17975
+ }
17976
+ }
17977
+ const promise = fetchRouteDataOnce(url, skipLayoutHooks).then((value) => {
17978
+ const entryAfterFetch = dataCache.get(key);
17979
+ if (!entryAfterFetch || entryAfterFetch.status === "pending") {
17980
+ setCacheEntry(key, { status: "fulfilled", value });
17981
+ }
17914
17982
  return value;
17915
17983
  }).catch((error) => {
17916
17984
  console.error("[client][cache] Error fetching route data:", error);
17917
- dataCache.set(key, { status: "rejected", error });
17985
+ const entryAfterFetch = dataCache.get(key);
17986
+ if (!entryAfterFetch || entryAfterFetch.status === "pending") {
17987
+ dataCache.set(key, { status: "rejected", error });
17988
+ }
17918
17989
  throw error;
17919
17990
  });
17920
17991
  dataCache.set(key, { status: "pending", promise });
@@ -17939,10 +18010,22 @@ async function handleErrorRoute(nextUrl, json, errorRoute, setState) {
17939
18010
  } else if (json.theme) {
17940
18011
  theme = json.theme;
17941
18012
  }
18013
+ let layoutProps = {};
18014
+ if (json.layoutProps !== void 0 && json.layoutProps !== null) {
18015
+ layoutProps = json.layoutProps;
18016
+ setPreservedLayoutProps(layoutProps);
18017
+ } else {
18018
+ const preserved = getPreservedLayoutProps();
18019
+ if (preserved) {
18020
+ layoutProps = preserved;
18021
+ }
18022
+ }
18023
+ const pageProps = json.pageProps ?? json.props ?? {
18024
+ error: json.message || "An error occurred"
18025
+ };
17942
18026
  const errorProps = {
17943
- ...json.props || {
17944
- error: json.message || "An error occurred"
17945
- },
18027
+ ...layoutProps,
18028
+ ...pageProps,
17946
18029
  theme
17947
18030
  };
17948
18031
  const windowData = {
@@ -17999,8 +18082,20 @@ async function handleNotFoundRoute(nextUrl, json, notFoundRoute, setState) {
17999
18082
  } else if (json.theme) {
18000
18083
  theme = json.theme;
18001
18084
  }
18085
+ let layoutProps = {};
18086
+ if (json.layoutProps !== void 0 && json.layoutProps !== null) {
18087
+ layoutProps = json.layoutProps;
18088
+ setPreservedLayoutProps(layoutProps);
18089
+ } else {
18090
+ const preserved = getPreservedLayoutProps();
18091
+ if (preserved) {
18092
+ layoutProps = preserved;
18093
+ }
18094
+ }
18095
+ const pageProps = json.pageProps ?? json.props ?? {};
18002
18096
  const notFoundProps = {
18003
- ...json.props ?? {},
18097
+ ...layoutProps,
18098
+ ...pageProps,
18004
18099
  theme
18005
18100
  };
18006
18101
  const windowData = {
@@ -18057,8 +18152,20 @@ async function handleNormalRoute(nextUrl, json, routes, setState) {
18057
18152
  } else if (json.theme) {
18058
18153
  theme = json.theme;
18059
18154
  }
18060
- const newProps = {
18061
- ...json.props ?? {},
18155
+ let layoutProps = {};
18156
+ if (json.layoutProps !== void 0 && json.layoutProps !== null) {
18157
+ layoutProps = json.layoutProps;
18158
+ setPreservedLayoutProps(layoutProps);
18159
+ } else {
18160
+ const preserved = getPreservedLayoutProps();
18161
+ if (preserved) {
18162
+ layoutProps = preserved;
18163
+ }
18164
+ }
18165
+ const pageProps = json.pageProps ?? json.props ?? {};
18166
+ const combinedProps = {
18167
+ ...layoutProps,
18168
+ ...pageProps,
18062
18169
  theme
18063
18170
  // Always include theme
18064
18171
  };
@@ -18070,7 +18177,7 @@ async function handleNormalRoute(nextUrl, json, routes, setState) {
18070
18177
  const windowData = {
18071
18178
  pathname: nextUrl,
18072
18179
  params: matched.params,
18073
- props: newProps,
18180
+ props: combinedProps,
18074
18181
  metadata: json.metadata ?? null,
18075
18182
  theme,
18076
18183
  notFound: false,
@@ -18098,7 +18205,7 @@ async function handleNormalRoute(nextUrl, json, routes, setState) {
18098
18205
  route: matched.route,
18099
18206
  params: matched.params,
18100
18207
  components,
18101
- props: newProps
18208
+ props: combinedProps
18102
18209
  });
18103
18210
  return true;
18104
18211
  }
@@ -18331,7 +18438,7 @@ function AppShell({
18331
18438
  return () => {
18332
18439
  window.removeEventListener("fw-data-refresh", handleDataRefresh);
18333
18440
  };
18334
- }, [state.url]);
18441
+ }, []);
18335
18442
  const isError = state.route === errorRoute;
18336
18443
  const isNotFound = state.route === notFoundRoute;
18337
18444
  const routeType = isError ? "error" : isNotFound ? "notfound" : "normal";
@@ -18530,6 +18637,9 @@ function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
18530
18637
  }
18531
18638
  const initialData = getWindowData();
18532
18639
  const initialUrl = window.location.pathname + window.location.search;
18640
+ if (initialData?.props) {
18641
+ setPreservedLayoutProps(initialData.props);
18642
+ }
18533
18643
  initializeRouterData(initialUrl, initialData);
18534
18644
  await hydrateInitialRoute(
18535
18645
  container,