@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.d.mts CHANGED
@@ -1,7 +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
+ export { c as bootstrapClient } from './bootstrap-DgvWWDim.mjs';
5
5
  import { ZodSchema, z } from 'zod';
6
6
  import * as express_rate_limit from 'express-rate-limit';
7
7
  import pino, { Logger as Logger$1 } from 'pino';
package/dist/index.d.ts CHANGED
@@ -1,7 +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
+ export { c as bootstrapClient } from './bootstrap-DgvWWDim.js';
5
5
  import { ZodSchema, z } from 'zod';
6
6
  import * as express_rate_limit from 'express-rate-limit';
7
7
  import pino, { Logger as Logger$1 } from 'pino';
package/dist/index.js CHANGED
@@ -42,17 +42,19 @@ __export(globals_exports, {
42
42
  NOT_FOUND_FILE_PREFIX: () => NOT_FOUND_FILE_PREFIX,
43
43
  NOT_FOUND_PATTERN: () => NOT_FOUND_PATTERN,
44
44
  PAGE_FILE_NAME: () => PAGE_FILE_NAME,
45
+ ROUTER_DATA_KEY: () => ROUTER_DATA_KEY,
45
46
  STATIC_PATH: () => STATIC_PATH,
46
47
  STYLE_FILE_NAME: () => STYLE_FILE_NAME,
47
48
  WINDOW_DATA_KEY: () => WINDOW_DATA_KEY
48
49
  });
49
- 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;
50
+ 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;
50
51
  var init_globals = __esm({
51
52
  "constants/globals.ts"() {
52
53
  "use strict";
53
54
  BUILD_FOLDER_NAME = ".loly";
54
55
  STYLE_FILE_NAME = "styles.css";
55
56
  WINDOW_DATA_KEY = "__FW_DATA__";
57
+ ROUTER_DATA_KEY = "__LOLY_ROUTER_DATA__";
56
58
  APP_CONTAINER_ID = "__app";
57
59
  STATIC_PATH = "/static";
58
60
  NOT_FOUND_PATTERN = "/not-found";
@@ -3835,6 +3837,7 @@ function createDocumentTree(options) {
3835
3837
  const {
3836
3838
  appTree,
3837
3839
  initialData,
3840
+ routerData,
3838
3841
  meta,
3839
3842
  titleFallback,
3840
3843
  descriptionFallback,
@@ -3872,6 +3875,9 @@ function createDocumentTree(options) {
3872
3875
  ...initialData,
3873
3876
  theme
3874
3877
  });
3878
+ const routerSerialized = JSON.stringify({
3879
+ ...routerData
3880
+ });
3875
3881
  const documentTree = React.createElement(
3876
3882
  "html",
3877
3883
  { lang },
@@ -3919,6 +3925,12 @@ function createDocumentTree(options) {
3919
3925
  dangerouslySetInnerHTML: {
3920
3926
  __html: `window.${WINDOW_DATA_KEY} = ${serialized};`
3921
3927
  }
3928
+ }),
3929
+ React.createElement("script", {
3930
+ nonce,
3931
+ dangerouslySetInnerHTML: {
3932
+ __html: `window.${ROUTER_DATA_KEY} = ${routerSerialized};`
3933
+ }
3922
3934
  })
3923
3935
  );
3924
3936
  return documentTree;
@@ -3941,6 +3953,15 @@ function buildInitialData(urlPath, params, loaderResult) {
3941
3953
  };
3942
3954
  }
3943
3955
 
3956
+ // modules/rendering/routerData/index.ts
3957
+ var buildRouterData = (req) => {
3958
+ return {
3959
+ pathname: req.path,
3960
+ params: req.params,
3961
+ searchParams: req.query
3962
+ };
3963
+ };
3964
+
3944
3965
  // modules/server/handlers/middleware.ts
3945
3966
  async function runRouteMiddlewares(route, ctx) {
3946
3967
  for (const mw of route.middlewares) {
@@ -4102,6 +4123,7 @@ async function handlePageRequestInternal(options) {
4102
4123
  }
4103
4124
  }
4104
4125
  const matched = matchRoute(routes, urlPath);
4126
+ const routerData = buildRouterData(req);
4105
4127
  if (!matched) {
4106
4128
  if (notFoundPage) {
4107
4129
  const ctx2 = {
@@ -4122,6 +4144,7 @@ async function handlePageRequestInternal(options) {
4122
4144
  const documentTree2 = createDocumentTree({
4123
4145
  appTree: appTree2,
4124
4146
  initialData: initialData2,
4147
+ routerData,
4125
4148
  meta: loaderResult2.metadata ?? null,
4126
4149
  titleFallback: "Not found",
4127
4150
  descriptionFallback: "Loly demo",
@@ -4229,6 +4252,7 @@ async function handlePageRequestInternal(options) {
4229
4252
  const documentTree = createDocumentTree({
4230
4253
  appTree,
4231
4254
  initialData,
4255
+ routerData,
4232
4256
  meta: loaderResult.metadata,
4233
4257
  titleFallback: "Loly framework",
4234
4258
  descriptionFallback: "Loly demo",
@@ -4286,6 +4310,7 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
4286
4310
  loaderResult.theme = theme;
4287
4311
  }
4288
4312
  const initialData = buildInitialData(req.path, { error: String(error) }, loaderResult);
4313
+ const routerData = buildRouterData(req);
4289
4314
  initialData.error = true;
4290
4315
  if (isDataReq) {
4291
4316
  res.statusCode = 500;
@@ -4316,6 +4341,7 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
4316
4341
  const documentTree = createDocumentTree({
4317
4342
  appTree,
4318
4343
  initialData,
4344
+ routerData,
4319
4345
  meta: loaderResult.metadata ?? null,
4320
4346
  titleFallback: "Error",
4321
4347
  descriptionFallback: "An error occurred",
@@ -4589,7 +4615,11 @@ var setupApplication = async ({
4589
4615
  helmetConfig.contentSecurityPolicy = {
4590
4616
  directives: {
4591
4617
  defaultSrc: ["'self'"],
4592
- styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
4618
+ styleSrc: [
4619
+ "'self'",
4620
+ "'unsafe-inline'",
4621
+ "https://fonts.googleapis.com"
4622
+ ],
4593
4623
  scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
4594
4624
  imgSrc: ["'self'", "data:", "https:"],
4595
4625
  // Allow fetch/XHR to any HTTPS endpoint - users can restrict in their config if needed
@@ -4606,7 +4636,11 @@ var setupApplication = async ({
4606
4636
  const defaultCSP = {
4607
4637
  directives: {
4608
4638
  defaultSrc: ["'self'"],
4609
- styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
4639
+ styleSrc: [
4640
+ "'self'",
4641
+ "'unsafe-inline'",
4642
+ "https://fonts.googleapis.com"
4643
+ ],
4610
4644
  scriptSrc: ["'self'", nonceFunction],
4611
4645
  imgSrc: ["'self'", "data:", "https:"],
4612
4646
  // Allow fetch/XHR to any HTTPS endpoint - users can restrict in their config if needed
@@ -4629,10 +4663,7 @@ var setupApplication = async ({
4629
4663
  (src) => typeof src === "function"
4630
4664
  );
4631
4665
  if (!hasNonceSupport) {
4632
- mergedDirectives.scriptSrc = [
4633
- ...userScriptSrc,
4634
- nonceFunction
4635
- ];
4666
+ mergedDirectives.scriptSrc = [...userScriptSrc, nonceFunction];
4636
4667
  } else {
4637
4668
  mergedDirectives.scriptSrc = userScriptSrc;
4638
4669
  }
@@ -4640,19 +4671,25 @@ var setupApplication = async ({
4640
4671
  const userConnectSrc = userDirectives.connectSrc;
4641
4672
  if (userConnectSrc && Array.isArray(userConnectSrc)) {
4642
4673
  const defaultConnectSrc = defaultCSP.directives.connectSrc || [];
4643
- const mergedConnectSrc = [.../* @__PURE__ */ new Set([...defaultConnectSrc, ...userConnectSrc])];
4674
+ const mergedConnectSrc = [
4675
+ .../* @__PURE__ */ new Set([...defaultConnectSrc, ...userConnectSrc])
4676
+ ];
4644
4677
  mergedDirectives.connectSrc = mergedConnectSrc;
4645
4678
  }
4646
4679
  const userStyleSrc = userDirectives.styleSrc;
4647
4680
  if (userStyleSrc && Array.isArray(userStyleSrc)) {
4648
4681
  const defaultStyleSrc = defaultCSP.directives.styleSrc || [];
4649
- const mergedStyleSrc = [.../* @__PURE__ */ new Set([...defaultStyleSrc, ...userStyleSrc])];
4682
+ const mergedStyleSrc = [
4683
+ .../* @__PURE__ */ new Set([...defaultStyleSrc, ...userStyleSrc])
4684
+ ];
4650
4685
  mergedDirectives.styleSrc = mergedStyleSrc;
4651
4686
  }
4652
4687
  const userFontSrc = userDirectives.fontSrc;
4653
4688
  if (userFontSrc && Array.isArray(userFontSrc)) {
4654
4689
  const defaultFontSrc = defaultCSP.directives.fontSrc || [];
4655
- const mergedFontSrc = [.../* @__PURE__ */ new Set([...defaultFontSrc, ...userFontSrc])];
4690
+ const mergedFontSrc = [
4691
+ .../* @__PURE__ */ new Set([...defaultFontSrc, ...userFontSrc])
4692
+ ];
4656
4693
  mergedDirectives.fontSrc = mergedFontSrc;
4657
4694
  }
4658
4695
  helmetConfig.contentSecurityPolicy = {
@@ -4672,23 +4709,27 @@ var setupApplication = async ({
4672
4709
  helmetConfig.hsts = false;
4673
4710
  }
4674
4711
  if (process.env.NODE_ENV !== "development" && security?.contentSecurityPolicy !== false) {
4675
- app.use((req, res, next) => {
4676
- const nonce = crypto.randomBytes(16).toString("base64");
4677
- res.locals.nonce = nonce;
4678
- next();
4679
- });
4712
+ app.use(
4713
+ (req, res, next) => {
4714
+ const nonce = crypto.randomBytes(16).toString("base64");
4715
+ res.locals.nonce = nonce;
4716
+ next();
4717
+ }
4718
+ );
4680
4719
  }
4681
4720
  app.use(helmet(helmetConfig));
4682
4721
  const appLogger = createModuleLogger("framework");
4683
- app.use(requestLoggerMiddleware({
4684
- logger: appLogger.child({ component: "server" }),
4685
- logRequests: process.env.LOG_REQUESTS === "true",
4686
- // Default to false (only errors/warnings)
4687
- logResponses: process.env.LOG_RESPONSES !== "false",
4688
- // Default to true (but filtered)
4689
- logStaticAssets: process.env.LOG_STATIC_ASSETS === "true"
4690
- // Default to false
4691
- }));
4722
+ app.use(
4723
+ requestLoggerMiddleware({
4724
+ logger: appLogger.child({ component: "server" }),
4725
+ logRequests: process.env.LOG_REQUESTS === "true",
4726
+ // Default to false (only errors/warnings)
4727
+ logResponses: process.env.LOG_RESPONSES !== "false",
4728
+ // Default to true (but filtered)
4729
+ logStaticAssets: process.env.LOG_STATIC_ASSETS === "true"
4730
+ // Default to false
4731
+ })
4732
+ );
4692
4733
  const corsOptions = {
4693
4734
  credentials: true
4694
4735
  };
@@ -4704,7 +4745,7 @@ var setupApplication = async ({
4704
4745
  corsOptions.origin = process.env.NODE_ENV === "development";
4705
4746
  }
4706
4747
  app.use(cors(corsOptions));
4707
- if (rateLimit2) {
4748
+ if (rateLimit2 && process.env.NODE_ENV !== "development") {
4708
4749
  const generalLimiter = createRateLimiter({
4709
4750
  windowMs: rateLimit2.windowMs,
4710
4751
  max: rateLimit2.max
@@ -4898,10 +4939,12 @@ async function renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params)
4898
4939
  return;
4899
4940
  }
4900
4941
  const initialData = buildInitialData(urlPath, params, loaderResult);
4942
+ const routerData = buildRouterData(req);
4901
4943
  const appTree = buildAppTree(route, params, initialData.props);
4902
4944
  const documentTree = createDocumentTree({
4903
4945
  appTree,
4904
4946
  initialData,
4947
+ routerData,
4905
4948
  meta: loaderResult.metadata,
4906
4949
  titleFallback: "My Framework Dev",
4907
4950
  descriptionFallback: "Static page generated by @lolyjs/core.",
@@ -5092,12 +5135,23 @@ import { hydrateRoot } from "react-dom/client";
5092
5135
 
5093
5136
  // modules/runtime/client/constants.ts
5094
5137
  var WINDOW_DATA_KEY2 = "__FW_DATA__";
5138
+ var ROUTER_DATA_KEY2 = "__LOLY_ROUTER_DATA__";
5095
5139
  var APP_CONTAINER_ID2 = "__app";
5140
+ var ROUTER_NAVIGATE_KEY = "__LOLY_ROUTER_NAVIGATE__";
5096
5141
 
5097
5142
  // modules/runtime/client/window-data.ts
5098
5143
  function getWindowData() {
5144
+ if (typeof window === "undefined") {
5145
+ return null;
5146
+ }
5099
5147
  return window[WINDOW_DATA_KEY2] ?? null;
5100
5148
  }
5149
+ function getRouterData() {
5150
+ if (typeof window === "undefined") {
5151
+ return null;
5152
+ }
5153
+ return window[ROUTER_DATA_KEY2] ?? null;
5154
+ }
5101
5155
  function setWindowData(data) {
5102
5156
  window[WINDOW_DATA_KEY2] = data;
5103
5157
  if (typeof window !== "undefined") {
@@ -5108,6 +5162,16 @@ function setWindowData(data) {
5108
5162
  );
5109
5163
  }
5110
5164
  }
5165
+ function setRouterData(data) {
5166
+ window[ROUTER_DATA_KEY2] = data;
5167
+ if (typeof window !== "undefined") {
5168
+ window.dispatchEvent(
5169
+ new CustomEvent("fw-router-data-refresh", {
5170
+ detail: { data }
5171
+ })
5172
+ );
5173
+ }
5174
+ }
5111
5175
  function getCurrentTheme() {
5112
5176
  return getWindowData()?.theme ?? null;
5113
5177
  }
@@ -5172,7 +5236,7 @@ function applyMetadata(md) {
5172
5236
  }
5173
5237
 
5174
5238
  // modules/runtime/client/AppShell.tsx
5175
- import { useEffect, useState, useRef } from "react";
5239
+ import { useEffect, useState, useRef, useCallback } from "react";
5176
5240
 
5177
5241
  // modules/runtime/client/RouterView.tsx
5178
5242
  import { jsx } from "react/jsx-runtime";
@@ -5369,6 +5433,13 @@ async function handleErrorRoute(nextUrl, json, errorRoute, setState) {
5369
5433
  error: true
5370
5434
  };
5371
5435
  setWindowData(windowData);
5436
+ const url = new URL(nextUrl, typeof window !== "undefined" ? window.location.origin : "http://localhost");
5437
+ const routerData = {
5438
+ pathname: url.pathname,
5439
+ params: json.params || {},
5440
+ searchParams: Object.fromEntries(url.searchParams.entries())
5441
+ };
5442
+ setRouterData(routerData);
5372
5443
  setState({
5373
5444
  url: nextUrl,
5374
5445
  route: errorRoute,
@@ -5415,6 +5486,13 @@ async function handleNotFoundRoute(nextUrl, json, notFoundRoute, setState) {
5415
5486
  error: false
5416
5487
  };
5417
5488
  setWindowData(windowData);
5489
+ const url = new URL(nextUrl, typeof window !== "undefined" ? window.location.origin : "http://localhost");
5490
+ const routerData = {
5491
+ pathname: url.pathname,
5492
+ params: {},
5493
+ searchParams: Object.fromEntries(url.searchParams.entries())
5494
+ };
5495
+ setRouterData(routerData);
5418
5496
  if (notFoundRoute) {
5419
5497
  const components = await notFoundRoute.load();
5420
5498
  setState({
@@ -5472,6 +5550,13 @@ async function handleNormalRoute(nextUrl, json, routes, setState) {
5472
5550
  error: false
5473
5551
  };
5474
5552
  setWindowData(windowData);
5553
+ const url = new URL(nextUrl, typeof window !== "undefined" ? window.location.origin : "http://localhost");
5554
+ const routerData = {
5555
+ pathname: url.pathname,
5556
+ params: matched.params,
5557
+ searchParams: Object.fromEntries(url.searchParams.entries())
5558
+ };
5559
+ setRouterData(routerData);
5475
5560
  const components = await matched.route.load();
5476
5561
  window.scrollTo({
5477
5562
  top: 0,
@@ -5591,6 +5676,10 @@ function createPopStateHandler(navigate2) {
5591
5676
  };
5592
5677
  }
5593
5678
 
5679
+ // modules/runtime/client/RouterContext.tsx
5680
+ import { createContext, useContext } from "react";
5681
+ var RouterContext = createContext(null);
5682
+
5594
5683
  // modules/runtime/client/AppShell.tsx
5595
5684
  import { jsx as jsx2 } from "react/jsx-runtime";
5596
5685
  function AppShell({
@@ -5614,14 +5703,30 @@ function AppShell({
5614
5703
  errorRoute
5615
5704
  };
5616
5705
  }, [routes, notFoundRoute, errorRoute]);
5706
+ const handleNavigate = useCallback(
5707
+ async (nextUrl, options) => {
5708
+ await navigate(nextUrl, handlersRef.current, {
5709
+ revalidate: options?.revalidate
5710
+ });
5711
+ },
5712
+ []
5713
+ );
5714
+ useEffect(() => {
5715
+ if (typeof window !== "undefined") {
5716
+ window[ROUTER_NAVIGATE_KEY] = handleNavigate;
5717
+ return () => {
5718
+ delete window[ROUTER_NAVIGATE_KEY];
5719
+ };
5720
+ }
5721
+ }, [handleNavigate]);
5617
5722
  useEffect(() => {
5618
5723
  let isMounted = true;
5619
- async function handleNavigate(nextUrl, options) {
5724
+ async function handleNavigateInternal(nextUrl, options) {
5620
5725
  if (!isMounted) return;
5621
5726
  await navigate(nextUrl, handlersRef.current, options);
5622
5727
  }
5623
- const handleClick = createClickHandler(handleNavigate);
5624
- const handlePopState = createPopStateHandler(handleNavigate);
5728
+ const handleClick = createClickHandler(handleNavigateInternal);
5729
+ const handlePopState = createPopStateHandler(handleNavigateInternal);
5625
5730
  window.addEventListener("click", handleClick, false);
5626
5731
  window.addEventListener("popstate", handlePopState, false);
5627
5732
  return () => {
@@ -5630,11 +5735,33 @@ function AppShell({
5630
5735
  window.removeEventListener("popstate", handlePopState, false);
5631
5736
  };
5632
5737
  }, []);
5738
+ useEffect(() => {
5739
+ const handleDataRefresh = () => {
5740
+ const freshData = window?.__FW_DATA__;
5741
+ if (!freshData) return;
5742
+ const currentPathname = window.location.pathname;
5743
+ const freshPathname = freshData.pathname;
5744
+ if (freshPathname === currentPathname) {
5745
+ if (freshData.metadata !== void 0) {
5746
+ applyMetadata(freshData.metadata);
5747
+ }
5748
+ setState((prevState) => ({
5749
+ ...prevState,
5750
+ props: freshData.props ?? prevState.props,
5751
+ params: freshData.params ?? prevState.params
5752
+ }));
5753
+ }
5754
+ };
5755
+ window.addEventListener("fw-data-refresh", handleDataRefresh);
5756
+ return () => {
5757
+ window.removeEventListener("fw-data-refresh", handleDataRefresh);
5758
+ };
5759
+ }, [state.url]);
5633
5760
  const isError = state.route === errorRoute;
5634
5761
  const isNotFound = state.route === notFoundRoute;
5635
5762
  const routeType = isError ? "error" : isNotFound ? "notfound" : "normal";
5636
5763
  const routeKey = `${state.url}:${routeType}`;
5637
- return /* @__PURE__ */ jsx2(RouterView, { state }, routeKey);
5764
+ return /* @__PURE__ */ jsx2(RouterContext.Provider, { value: { navigate: handleNavigate }, children: /* @__PURE__ */ jsx2(RouterView, { state }, routeKey) });
5638
5765
  }
5639
5766
 
5640
5767
  // modules/runtime/client/bootstrap.tsx
@@ -5734,6 +5861,16 @@ function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
5734
5861
  return;
5735
5862
  }
5736
5863
  const initialUrl = window.location.pathname + window.location.search;
5864
+ let routerData = getRouterData();
5865
+ if (!routerData) {
5866
+ const url = new URL(initialUrl, window.location.origin);
5867
+ routerData = {
5868
+ pathname: url.pathname,
5869
+ params: initialData?.params || {},
5870
+ searchParams: Object.fromEntries(url.searchParams.entries())
5871
+ };
5872
+ setRouterData(routerData);
5873
+ }
5737
5874
  try {
5738
5875
  const initialState = await loadInitialRoute(
5739
5876
  initialUrl,
@@ -5772,7 +5909,6 @@ function bootstrapClient(routes, notFoundRoute, errorRoute = null) {
5772
5909
  function withCache(fn, options) {
5773
5910
  const ttl = options.ttl ?? 60;
5774
5911
  return async function cachedGssp(ctx) {
5775
- console.log("TTL", ttl);
5776
5912
  return await fn(ctx);
5777
5913
  };
5778
5914
  }