@lolyjs/core 0.2.0-alpha.1 → 0.2.0-alpha.11

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.cjs CHANGED
@@ -47,17 +47,19 @@ __export(globals_exports, {
47
47
  NOT_FOUND_FILE_PREFIX: () => NOT_FOUND_FILE_PREFIX,
48
48
  NOT_FOUND_PATTERN: () => NOT_FOUND_PATTERN,
49
49
  PAGE_FILE_NAME: () => PAGE_FILE_NAME,
50
+ ROUTER_DATA_KEY: () => ROUTER_DATA_KEY,
50
51
  STATIC_PATH: () => STATIC_PATH,
51
52
  STYLE_FILE_NAME: () => STYLE_FILE_NAME,
52
53
  WINDOW_DATA_KEY: () => WINDOW_DATA_KEY
53
54
  });
54
- var BUILD_FOLDER_NAME, STYLE_FILE_NAME, WINDOW_DATA_KEY, APP_CONTAINER_ID, STATIC_PATH, NOT_FOUND_PATTERN, ERROR_PATTERN, NOT_FOUND_CHUNK_KEY, ERROR_CHUNK_KEY, NOT_FOUND_FILE_PREFIX, ERROR_FILE_PREFIX, PAGE_FILE_NAME, LAYOUT_FILE_NAME, FAVICON_PATH, CLIENT_CSS_PATH, CLIENT_JS_PATH, ASSETS_BASE_DIR;
55
+ var BUILD_FOLDER_NAME, STYLE_FILE_NAME, WINDOW_DATA_KEY, ROUTER_DATA_KEY, APP_CONTAINER_ID, STATIC_PATH, NOT_FOUND_PATTERN, ERROR_PATTERN, NOT_FOUND_CHUNK_KEY, ERROR_CHUNK_KEY, NOT_FOUND_FILE_PREFIX, ERROR_FILE_PREFIX, PAGE_FILE_NAME, LAYOUT_FILE_NAME, FAVICON_PATH, CLIENT_CSS_PATH, CLIENT_JS_PATH, ASSETS_BASE_DIR;
55
56
  var init_globals = __esm({
56
57
  "constants/globals.ts"() {
57
58
  "use strict";
58
59
  BUILD_FOLDER_NAME = ".loly";
59
60
  STYLE_FILE_NAME = "styles.css";
60
61
  WINDOW_DATA_KEY = "__FW_DATA__";
62
+ ROUTER_DATA_KEY = "__LOLY_ROUTER_DATA__";
61
63
  APP_CONTAINER_ID = "__app";
62
64
  STATIC_PATH = "/static";
63
65
  NOT_FOUND_PATTERN = "/not-found";
@@ -3877,6 +3879,7 @@ function createDocumentTree(options) {
3877
3879
  const {
3878
3880
  appTree,
3879
3881
  initialData,
3882
+ routerData,
3880
3883
  meta,
3881
3884
  titleFallback,
3882
3885
  descriptionFallback,
@@ -3914,6 +3917,9 @@ function createDocumentTree(options) {
3914
3917
  ...initialData,
3915
3918
  theme
3916
3919
  });
3920
+ const routerSerialized = JSON.stringify({
3921
+ ...routerData
3922
+ });
3917
3923
  const documentTree = import_react.default.createElement(
3918
3924
  "html",
3919
3925
  { lang },
@@ -3961,6 +3967,12 @@ function createDocumentTree(options) {
3961
3967
  dangerouslySetInnerHTML: {
3962
3968
  __html: `window.${WINDOW_DATA_KEY} = ${serialized};`
3963
3969
  }
3970
+ }),
3971
+ import_react.default.createElement("script", {
3972
+ nonce,
3973
+ dangerouslySetInnerHTML: {
3974
+ __html: `window.${ROUTER_DATA_KEY} = ${routerSerialized};`
3975
+ }
3964
3976
  })
3965
3977
  );
3966
3978
  return documentTree;
@@ -3983,6 +3995,15 @@ function buildInitialData(urlPath, params, loaderResult) {
3983
3995
  };
3984
3996
  }
3985
3997
 
3998
+ // modules/rendering/routerData/index.ts
3999
+ var buildRouterData = (req) => {
4000
+ return {
4001
+ pathname: req.path,
4002
+ params: req.params,
4003
+ searchParams: req.query
4004
+ };
4005
+ };
4006
+
3986
4007
  // modules/server/handlers/middleware.ts
3987
4008
  async function runRouteMiddlewares(route, ctx) {
3988
4009
  for (const mw of route.middlewares) {
@@ -4144,6 +4165,7 @@ async function handlePageRequestInternal(options) {
4144
4165
  }
4145
4166
  }
4146
4167
  const matched = matchRoute(routes, urlPath);
4168
+ const routerData = buildRouterData(req);
4147
4169
  if (!matched) {
4148
4170
  if (notFoundPage) {
4149
4171
  const ctx2 = {
@@ -4164,6 +4186,7 @@ async function handlePageRequestInternal(options) {
4164
4186
  const documentTree2 = createDocumentTree({
4165
4187
  appTree: appTree2,
4166
4188
  initialData: initialData2,
4189
+ routerData,
4167
4190
  meta: loaderResult2.metadata ?? null,
4168
4191
  titleFallback: "Not found",
4169
4192
  descriptionFallback: "Loly demo",
@@ -4271,6 +4294,7 @@ async function handlePageRequestInternal(options) {
4271
4294
  const documentTree = createDocumentTree({
4272
4295
  appTree,
4273
4296
  initialData,
4297
+ routerData,
4274
4298
  meta: loaderResult.metadata,
4275
4299
  titleFallback: "Loly framework",
4276
4300
  descriptionFallback: "Loly demo",
@@ -4328,6 +4352,7 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
4328
4352
  loaderResult.theme = theme;
4329
4353
  }
4330
4354
  const initialData = buildInitialData(req.path, { error: String(error) }, loaderResult);
4355
+ const routerData = buildRouterData(req);
4331
4356
  initialData.error = true;
4332
4357
  if (isDataReq) {
4333
4358
  res.statusCode = 500;
@@ -4358,6 +4383,7 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
4358
4383
  const documentTree = createDocumentTree({
4359
4384
  appTree,
4360
4385
  initialData,
4386
+ routerData,
4361
4387
  meta: loaderResult.metadata ?? null,
4362
4388
  titleFallback: "Error",
4363
4389
  descriptionFallback: "An error occurred",
@@ -4631,7 +4657,11 @@ var setupApplication = async ({
4631
4657
  helmetConfig.contentSecurityPolicy = {
4632
4658
  directives: {
4633
4659
  defaultSrc: ["'self'"],
4634
- styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
4660
+ styleSrc: [
4661
+ "'self'",
4662
+ "'unsafe-inline'",
4663
+ "https://fonts.googleapis.com"
4664
+ ],
4635
4665
  scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
4636
4666
  imgSrc: ["'self'", "data:", "https:"],
4637
4667
  // Allow fetch/XHR to any HTTPS endpoint - users can restrict in their config if needed
@@ -4648,7 +4678,11 @@ var setupApplication = async ({
4648
4678
  const defaultCSP = {
4649
4679
  directives: {
4650
4680
  defaultSrc: ["'self'"],
4651
- styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
4681
+ styleSrc: [
4682
+ "'self'",
4683
+ "'unsafe-inline'",
4684
+ "https://fonts.googleapis.com"
4685
+ ],
4652
4686
  scriptSrc: ["'self'", nonceFunction],
4653
4687
  imgSrc: ["'self'", "data:", "https:"],
4654
4688
  // Allow fetch/XHR to any HTTPS endpoint - users can restrict in their config if needed
@@ -4671,10 +4705,7 @@ var setupApplication = async ({
4671
4705
  (src) => typeof src === "function"
4672
4706
  );
4673
4707
  if (!hasNonceSupport) {
4674
- mergedDirectives.scriptSrc = [
4675
- ...userScriptSrc,
4676
- nonceFunction
4677
- ];
4708
+ mergedDirectives.scriptSrc = [...userScriptSrc, nonceFunction];
4678
4709
  } else {
4679
4710
  mergedDirectives.scriptSrc = userScriptSrc;
4680
4711
  }
@@ -4682,19 +4713,25 @@ var setupApplication = async ({
4682
4713
  const userConnectSrc = userDirectives.connectSrc;
4683
4714
  if (userConnectSrc && Array.isArray(userConnectSrc)) {
4684
4715
  const defaultConnectSrc = defaultCSP.directives.connectSrc || [];
4685
- const mergedConnectSrc = [.../* @__PURE__ */ new Set([...defaultConnectSrc, ...userConnectSrc])];
4716
+ const mergedConnectSrc = [
4717
+ .../* @__PURE__ */ new Set([...defaultConnectSrc, ...userConnectSrc])
4718
+ ];
4686
4719
  mergedDirectives.connectSrc = mergedConnectSrc;
4687
4720
  }
4688
4721
  const userStyleSrc = userDirectives.styleSrc;
4689
4722
  if (userStyleSrc && Array.isArray(userStyleSrc)) {
4690
4723
  const defaultStyleSrc = defaultCSP.directives.styleSrc || [];
4691
- const mergedStyleSrc = [.../* @__PURE__ */ new Set([...defaultStyleSrc, ...userStyleSrc])];
4724
+ const mergedStyleSrc = [
4725
+ .../* @__PURE__ */ new Set([...defaultStyleSrc, ...userStyleSrc])
4726
+ ];
4692
4727
  mergedDirectives.styleSrc = mergedStyleSrc;
4693
4728
  }
4694
4729
  const userFontSrc = userDirectives.fontSrc;
4695
4730
  if (userFontSrc && Array.isArray(userFontSrc)) {
4696
4731
  const defaultFontSrc = defaultCSP.directives.fontSrc || [];
4697
- const mergedFontSrc = [.../* @__PURE__ */ new Set([...defaultFontSrc, ...userFontSrc])];
4732
+ const mergedFontSrc = [
4733
+ .../* @__PURE__ */ new Set([...defaultFontSrc, ...userFontSrc])
4734
+ ];
4698
4735
  mergedDirectives.fontSrc = mergedFontSrc;
4699
4736
  }
4700
4737
  helmetConfig.contentSecurityPolicy = {
@@ -4714,23 +4751,27 @@ var setupApplication = async ({
4714
4751
  helmetConfig.hsts = false;
4715
4752
  }
4716
4753
  if (process.env.NODE_ENV !== "development" && security?.contentSecurityPolicy !== false) {
4717
- app.use((req, res, next) => {
4718
- const nonce = import_crypto.default.randomBytes(16).toString("base64");
4719
- res.locals.nonce = nonce;
4720
- next();
4721
- });
4754
+ app.use(
4755
+ (req, res, next) => {
4756
+ const nonce = import_crypto.default.randomBytes(16).toString("base64");
4757
+ res.locals.nonce = nonce;
4758
+ next();
4759
+ }
4760
+ );
4722
4761
  }
4723
4762
  app.use((0, import_helmet.default)(helmetConfig));
4724
4763
  const appLogger = createModuleLogger("framework");
4725
- app.use(requestLoggerMiddleware({
4726
- logger: appLogger.child({ component: "server" }),
4727
- logRequests: process.env.LOG_REQUESTS === "true",
4728
- // Default to false (only errors/warnings)
4729
- logResponses: process.env.LOG_RESPONSES !== "false",
4730
- // Default to true (but filtered)
4731
- logStaticAssets: process.env.LOG_STATIC_ASSETS === "true"
4732
- // Default to false
4733
- }));
4764
+ app.use(
4765
+ requestLoggerMiddleware({
4766
+ logger: appLogger.child({ component: "server" }),
4767
+ logRequests: process.env.LOG_REQUESTS === "true",
4768
+ // Default to false (only errors/warnings)
4769
+ logResponses: process.env.LOG_RESPONSES !== "false",
4770
+ // Default to true (but filtered)
4771
+ logStaticAssets: process.env.LOG_STATIC_ASSETS === "true"
4772
+ // Default to false
4773
+ })
4774
+ );
4734
4775
  const corsOptions = {
4735
4776
  credentials: true
4736
4777
  };
@@ -4746,7 +4787,7 @@ var setupApplication = async ({
4746
4787
  corsOptions.origin = process.env.NODE_ENV === "development";
4747
4788
  }
4748
4789
  app.use((0, import_cors.default)(corsOptions));
4749
- if (rateLimit2) {
4790
+ if (rateLimit2 && process.env.NODE_ENV !== "development") {
4750
4791
  const generalLimiter = createRateLimiter({
4751
4792
  windowMs: rateLimit2.windowMs,
4752
4793
  max: rateLimit2.max
@@ -4940,10 +4981,12 @@ async function renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params)
4940
4981
  return;
4941
4982
  }
4942
4983
  const initialData = buildInitialData(urlPath, params, loaderResult);
4984
+ const routerData = buildRouterData(req);
4943
4985
  const appTree = buildAppTree(route, params, initialData.props);
4944
4986
  const documentTree = createDocumentTree({
4945
4987
  appTree,
4946
4988
  initialData,
4989
+ routerData,
4947
4990
  meta: loaderResult.metadata,
4948
4991
  titleFallback: "My Framework Dev",
4949
4992
  descriptionFallback: "Static page generated by @lolyjs/core.",
@@ -5134,12 +5177,23 @@ var import_client5 = require("react-dom/client");
5134
5177
 
5135
5178
  // modules/runtime/client/constants.ts
5136
5179
  var WINDOW_DATA_KEY2 = "__FW_DATA__";
5180
+ var ROUTER_DATA_KEY2 = "__LOLY_ROUTER_DATA__";
5137
5181
  var APP_CONTAINER_ID2 = "__app";
5182
+ var ROUTER_NAVIGATE_KEY = "__LOLY_ROUTER_NAVIGATE__";
5138
5183
 
5139
5184
  // modules/runtime/client/window-data.ts
5140
5185
  function getWindowData() {
5186
+ if (typeof window === "undefined") {
5187
+ return null;
5188
+ }
5141
5189
  return window[WINDOW_DATA_KEY2] ?? null;
5142
5190
  }
5191
+ function getRouterData() {
5192
+ if (typeof window === "undefined") {
5193
+ return null;
5194
+ }
5195
+ return window[ROUTER_DATA_KEY2] ?? null;
5196
+ }
5143
5197
  function setWindowData(data) {
5144
5198
  window[WINDOW_DATA_KEY2] = data;
5145
5199
  if (typeof window !== "undefined") {
@@ -5150,6 +5204,16 @@ function setWindowData(data) {
5150
5204
  );
5151
5205
  }
5152
5206
  }
5207
+ function setRouterData(data) {
5208
+ window[ROUTER_DATA_KEY2] = data;
5209
+ if (typeof window !== "undefined") {
5210
+ window.dispatchEvent(
5211
+ new CustomEvent("fw-router-data-refresh", {
5212
+ detail: { data }
5213
+ })
5214
+ );
5215
+ }
5216
+ }
5153
5217
  function getCurrentTheme() {
5154
5218
  return getWindowData()?.theme ?? null;
5155
5219
  }
@@ -5214,7 +5278,7 @@ function applyMetadata(md) {
5214
5278
  }
5215
5279
 
5216
5280
  // modules/runtime/client/AppShell.tsx
5217
- var import_react2 = require("react");
5281
+ var import_react3 = require("react");
5218
5282
 
5219
5283
  // modules/runtime/client/RouterView.tsx
5220
5284
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -5411,6 +5475,13 @@ async function handleErrorRoute(nextUrl, json, errorRoute, setState) {
5411
5475
  error: true
5412
5476
  };
5413
5477
  setWindowData(windowData);
5478
+ const url = new URL(nextUrl, typeof window !== "undefined" ? window.location.origin : "http://localhost");
5479
+ const routerData = {
5480
+ pathname: url.pathname,
5481
+ params: json.params || {},
5482
+ searchParams: Object.fromEntries(url.searchParams.entries())
5483
+ };
5484
+ setRouterData(routerData);
5414
5485
  setState({
5415
5486
  url: nextUrl,
5416
5487
  route: errorRoute,
@@ -5457,6 +5528,13 @@ async function handleNotFoundRoute(nextUrl, json, notFoundRoute, setState) {
5457
5528
  error: false
5458
5529
  };
5459
5530
  setWindowData(windowData);
5531
+ const url = new URL(nextUrl, typeof window !== "undefined" ? window.location.origin : "http://localhost");
5532
+ const routerData = {
5533
+ pathname: url.pathname,
5534
+ params: {},
5535
+ searchParams: Object.fromEntries(url.searchParams.entries())
5536
+ };
5537
+ setRouterData(routerData);
5460
5538
  if (notFoundRoute) {
5461
5539
  const components = await notFoundRoute.load();
5462
5540
  setState({
@@ -5514,6 +5592,13 @@ async function handleNormalRoute(nextUrl, json, routes, setState) {
5514
5592
  error: false
5515
5593
  };
5516
5594
  setWindowData(windowData);
5595
+ const url = new URL(nextUrl, typeof window !== "undefined" ? window.location.origin : "http://localhost");
5596
+ const routerData = {
5597
+ pathname: url.pathname,
5598
+ params: matched.params,
5599
+ searchParams: Object.fromEntries(url.searchParams.entries())
5600
+ };
5601
+ setRouterData(routerData);
5517
5602
  const components = await matched.route.load();
5518
5603
  window.scrollTo({
5519
5604
  top: 0,
@@ -5633,6 +5718,10 @@ function createPopStateHandler(navigate2) {
5633
5718
  };
5634
5719
  }
5635
5720
 
5721
+ // modules/runtime/client/RouterContext.tsx
5722
+ var import_react2 = require("react");
5723
+ var RouterContext = (0, import_react2.createContext)(null);
5724
+
5636
5725
  // modules/runtime/client/AppShell.tsx
5637
5726
  var import_jsx_runtime2 = require("react/jsx-runtime");
5638
5727
  function AppShell({
@@ -5641,14 +5730,14 @@ function AppShell({
5641
5730
  notFoundRoute,
5642
5731
  errorRoute
5643
5732
  }) {
5644
- const [state, setState] = (0, import_react2.useState)(initialState);
5645
- const handlersRef = (0, import_react2.useRef)({
5733
+ const [state, setState] = (0, import_react3.useState)(initialState);
5734
+ const handlersRef = (0, import_react3.useRef)({
5646
5735
  setState,
5647
5736
  routes,
5648
5737
  notFoundRoute,
5649
5738
  errorRoute
5650
5739
  });
5651
- (0, import_react2.useEffect)(() => {
5740
+ (0, import_react3.useEffect)(() => {
5652
5741
  handlersRef.current = {
5653
5742
  setState,
5654
5743
  routes,
@@ -5656,14 +5745,30 @@ function AppShell({
5656
5745
  errorRoute
5657
5746
  };
5658
5747
  }, [routes, notFoundRoute, errorRoute]);
5659
- (0, import_react2.useEffect)(() => {
5748
+ const handleNavigate = (0, import_react3.useCallback)(
5749
+ async (nextUrl, options) => {
5750
+ await navigate(nextUrl, handlersRef.current, {
5751
+ revalidate: options?.revalidate
5752
+ });
5753
+ },
5754
+ []
5755
+ );
5756
+ (0, import_react3.useEffect)(() => {
5757
+ if (typeof window !== "undefined") {
5758
+ window[ROUTER_NAVIGATE_KEY] = handleNavigate;
5759
+ return () => {
5760
+ delete window[ROUTER_NAVIGATE_KEY];
5761
+ };
5762
+ }
5763
+ }, [handleNavigate]);
5764
+ (0, import_react3.useEffect)(() => {
5660
5765
  let isMounted = true;
5661
- async function handleNavigate(nextUrl, options) {
5766
+ async function handleNavigateInternal(nextUrl, options) {
5662
5767
  if (!isMounted) return;
5663
5768
  await navigate(nextUrl, handlersRef.current, options);
5664
5769
  }
5665
- const handleClick = createClickHandler(handleNavigate);
5666
- const handlePopState = createPopStateHandler(handleNavigate);
5770
+ const handleClick = createClickHandler(handleNavigateInternal);
5771
+ const handlePopState = createPopStateHandler(handleNavigateInternal);
5667
5772
  window.addEventListener("click", handleClick, false);
5668
5773
  window.addEventListener("popstate", handlePopState, false);
5669
5774
  return () => {
@@ -5672,11 +5777,33 @@ function AppShell({
5672
5777
  window.removeEventListener("popstate", handlePopState, false);
5673
5778
  };
5674
5779
  }, []);
5780
+ (0, import_react3.useEffect)(() => {
5781
+ const handleDataRefresh = () => {
5782
+ const freshData = window?.__FW_DATA__;
5783
+ if (!freshData) return;
5784
+ const currentPathname = window.location.pathname;
5785
+ const freshPathname = freshData.pathname;
5786
+ if (freshPathname === currentPathname) {
5787
+ if (freshData.metadata !== void 0) {
5788
+ applyMetadata(freshData.metadata);
5789
+ }
5790
+ setState((prevState) => ({
5791
+ ...prevState,
5792
+ props: freshData.props ?? prevState.props,
5793
+ params: freshData.params ?? prevState.params
5794
+ }));
5795
+ }
5796
+ };
5797
+ window.addEventListener("fw-data-refresh", handleDataRefresh);
5798
+ return () => {
5799
+ window.removeEventListener("fw-data-refresh", handleDataRefresh);
5800
+ };
5801
+ }, [state.url]);
5675
5802
  const isError = state.route === errorRoute;
5676
5803
  const isNotFound = state.route === notFoundRoute;
5677
5804
  const routeType = isError ? "error" : isNotFound ? "notfound" : "normal";
5678
5805
  const routeKey = `${state.url}:${routeType}`;
5679
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RouterView, { state }, routeKey);
5806
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RouterContext.Provider, { value: { navigate: handleNavigate }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RouterView, { state }, routeKey) });
5680
5807
  }
5681
5808
 
5682
5809
  // modules/runtime/client/bootstrap.tsx
@@ -5776,6 +5903,16 @@ function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
5776
5903
  return;
5777
5904
  }
5778
5905
  const initialUrl = window.location.pathname + window.location.search;
5906
+ let routerData = getRouterData();
5907
+ if (!routerData) {
5908
+ const url = new URL(initialUrl, window.location.origin);
5909
+ routerData = {
5910
+ pathname: url.pathname,
5911
+ params: initialData?.params || {},
5912
+ searchParams: Object.fromEntries(url.searchParams.entries())
5913
+ };
5914
+ setRouterData(routerData);
5915
+ }
5779
5916
  try {
5780
5917
  const initialState = await loadInitialRoute(
5781
5918
  initialUrl,
@@ -5814,7 +5951,6 @@ function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
5814
5951
  function withCache(fn, options) {
5815
5952
  const ttl = options.ttl ?? 60;
5816
5953
  return async function cachedGssp(ctx) {
5817
- console.log("TTL", ttl);
5818
5954
  return await fn(ctx);
5819
5955
  };
5820
5956
  }