lovable-ssr 0.1.13 → 0.1.15

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.
@@ -1,4 +1,5 @@
1
1
  import { type ReactNode } from 'react';
2
+ import { RouteDataParams } from '../types';
2
3
  export type RouteDataState = Record<string, Record<string, unknown>>;
3
4
  /**
4
5
  * Builds a stable cache key for route data. Includes path, route params and optionally search params
@@ -16,7 +17,7 @@ interface RouteDataProviderProps {
16
17
  children: ReactNode;
17
18
  initialData?: Record<string, unknown>;
18
19
  initialRoute?: InitialRouteShape;
19
- initialParams?: Record<'routeParams' | 'searchParams', Record<string, string>>;
20
+ initialParams?: RouteDataParams;
20
21
  }
21
22
  export declare function RouteDataProvider({ children, initialData, initialRoute, initialParams, }: RouteDataProviderProps): import("react/jsx-runtime").JSX.Element;
22
23
  export declare function useRouteData(): RouteDataContextValue;
@@ -1 +1 @@
1
- {"version":3,"file":"RouteDataContext.d.ts","sourceRoot":"","sources":["../../src/router/RouteDataContext.tsx"],"names":[],"mappings":"AAAA,OAAc,EAMZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAErE;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,MAAM,CAYR;AAED,UAAU,qBAAqB;IAC7B,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACrE;AAID,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,sBAAsB;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC,aAAa,GAAG,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAChF;AAED,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,aAGC,GACF,EAAE,sBAAsB,2CAmBxB;AAED,wBAAgB,YAAY,IAAI,qBAAqB,CAMpD"}
1
+ {"version":3,"file":"RouteDataContext.d.ts","sourceRoot":"","sources":["../../src/router/RouteDataContext.tsx"],"names":[],"mappings":"AAAA,OAAc,EAMZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAErE;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,MAAM,CAYR;AAED,UAAU,qBAAqB;IAC7B,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACrE;AAID,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,sBAAsB;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,aAAa,CAAC,EAAE,eAAe,CAAC;CACjC;AAED,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,aAGC,GACF,EAAE,sBAAsB,2CAmBxB;AAED,wBAAgB,YAAY,IAAI,qBAAqB,CAMpD"}
@@ -1,8 +1,8 @@
1
- import type { RouteConfig } from '../types.js';
1
+ import type { RouteConfig, RouteDataParams } from '../types.js';
2
2
  declare function matchPath(pathPattern: string, pathname: string): boolean;
3
3
  declare function isSsrRoute(pathname: string): boolean;
4
4
  declare function matchRoute(pathname: string): RouteConfig | undefined;
5
- declare function routeParams(routePath: string, pathname?: string): Record<'routeParams' | 'searchParams', Record<string, string>>;
5
+ declare function routeParams(routePath: string, pathname?: string): Omit<RouteDataParams, 'request'>;
6
6
  /** Parses the URL query string (e.g. "?filter=FPS" or "filter=FPS") into key/value pairs. */
7
7
  declare function searchParams(queryString: string): Record<string, string>;
8
8
  declare const RouterService: {
@@ -1 +1 @@
1
- {"version":3,"file":"RouterService.d.ts","sourceRoot":"","sources":["../../src/router/RouterService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,iBAAS,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOjE;AAED,iBAAS,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG7C;AAED,iBAAS,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAM7D;AAED,iBAAS,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,aAAa,GAAG,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAazH;AACD,6FAA6F;AAC7F,iBAAS,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGjE;AAED,QAAA,MAAM,aAAa;;;;;;CAMlB,CAAC;AAEF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"RouterService.d.ts","sourceRoot":"","sources":["../../src/router/RouterService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEhE,iBAAS,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOjE;AAED,iBAAS,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG7C;AAED,iBAAS,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAM7D;AAED,iBAAS,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,CAa3F;AACD,6FAA6F;AAC7F,iBAAS,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGjE;AAED,QAAA,MAAM,aAAa;;;;;;CAMlB,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -1,10 +1,16 @@
1
1
  import type { ReactNode } from 'react';
2
+ import { RequestContext } from '../types.js';
2
3
  export interface RenderResult {
3
4
  html: string;
4
5
  preloadedData: Record<string, unknown>;
5
6
  }
6
7
  export interface RenderOptions {
7
8
  wrap?: (children: ReactNode) => ReactNode;
9
+ /**
10
+ * Contexto opcional da request (ex.: cookies, headers).
11
+ * O servidor pode preencher isso e o getData recebe via params.request.
12
+ */
13
+ requestContext?: RequestContext;
8
14
  }
9
15
  /**
10
16
  * Renders the app for a single URL. Called once per request with that request's URL.
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/ssr/render.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,SAAS,CAAC;CAC3C;AAED;;;GAGG;AACH,wBAAsB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAqCxF"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/ssr/render.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,OAAO,EAAE,cAAc,EAAmB,MAAM,aAAa,CAAC;AAE9D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,SAAS,CAAC;IAC1C;;;OAGG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED;;;GAGG;AACH,wBAAsB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAwCxF"}
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { renderToString } from 'react-dom/server';
3
3
  import { StaticRouter } from 'react-router-dom/server';
4
- import RouterService from '../router/RouterService.js';
5
- import { RouteDataProvider } from '../router/RouteDataContext.js';
6
4
  import { AppRoutes } from '../components/AppRoutes.js';
5
+ import { RouteDataProvider } from '../router/RouteDataContext.js';
6
+ import RouterService from '../router/RouterService.js';
7
7
  /**
8
8
  * Renders the app for a single URL. Called once per request with that request's URL.
9
9
  * getData is invoked only for the route that matches this URL (never for all routes).
@@ -12,12 +12,15 @@ export async function render(url, options) {
12
12
  const fullUrl = new URL(url, 'http://localhost');
13
13
  const pathname = fullUrl.pathname || '/';
14
14
  const matchedRoute = RouterService.matchRoute(pathname);
15
- const params = matchedRoute ? RouterService.routeParams(matchedRoute.path, pathname) : {
16
- routeParams: {},
17
- searchParams: {},
18
- };
15
+ const params = matchedRoute
16
+ ? RouterService.routeParams(matchedRoute.path, pathname)
17
+ : {
18
+ routeParams: {},
19
+ searchParams: {},
20
+ };
19
21
  const searchParams = matchedRoute ? RouterService.searchParams(fullUrl.search) : {};
20
22
  params.searchParams = searchParams;
23
+ params.request = options?.requestContext;
21
24
  let preloadedData = { is_success: true };
22
25
  const getData = matchedRoute?.Component?.getData;
23
26
  if (typeof getData === 'function') {
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/ssr/server.ts"],"names":[],"mappings":"AAGA,OAAgB,EAAE,KAAK,OAAO,EAAkD,MAAM,SAAS,CAAC;AAIhG,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wGAAwG;IACxG,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA8MD,wBAAsB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC;IACtE,MAAM,EAAE,MAAM,OAAO,CAAC;IACtB,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;CACxD,CAAC,CAOD;AAED;qDACqD;AACrD,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,QAO7D"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/ssr/server.ts"],"names":[],"mappings":"AAGA,OAAgB,EAAE,KAAK,OAAO,EAAkD,MAAM,SAAS,CAAC;AAKhG,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wGAAwG;IACxG,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAkPD,wBAAsB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC;IACtE,MAAM,EAAE,MAAM,OAAO,CAAC;IACtB,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;CACxD,CAAC,CAOD;AAED;qDACqD;AACrD,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,QAO7D"}
@@ -74,7 +74,7 @@ class SsrServer {
74
74
  if (!RouterService.isSsrRoute(pathname)) {
75
75
  return await this.renderSpa(url, res);
76
76
  }
77
- return await this.renderSsr(url, res);
77
+ return await this.renderSsr(url, res, req);
78
78
  }
79
79
  catch (e) {
80
80
  if (this.vite) {
@@ -93,29 +93,60 @@ class SsrServer {
93
93
  const template = this.readTemplate(path.join(this.config.root, 'dist', 'index.html'));
94
94
  return res.status(200).set({ 'Content-Type': 'text/html' }).send(template);
95
95
  }
96
- async renderSsr(url, res) {
96
+ async renderSsr(url, res, req) {
97
97
  const { template, render } = await this.getSsrRenderer();
98
98
  if (process.env.NODE_ENV !== 'production' && process.env.LOVABLE_SSR_DEBUG) {
99
99
  console.log('[lovable-ssr] render(url)', url);
100
100
  }
101
101
  let appHtml;
102
102
  let preloadedData;
103
+ // Constrói um contexto simples de request (cookies raw + headers) para o getData.
104
+ const cookiesRaw = req.headers.cookie ?? '';
105
+ const cookies = {};
106
+ if (cookiesRaw) {
107
+ cookiesRaw.split(';').forEach((part) => {
108
+ const [k, ...rest] = part.split('=');
109
+ if (!k)
110
+ return;
111
+ const key = k.trim();
112
+ if (!key)
113
+ return;
114
+ const value = rest.join('=').trim();
115
+ cookies[key] = decodeURIComponent(value);
116
+ });
117
+ }
118
+ const requestContext = {
119
+ cookiesRaw,
120
+ cookies,
121
+ headers: req.headers,
122
+ method: req.method,
123
+ url: req.originalUrl,
124
+ };
103
125
  if (this.isProd) {
104
126
  const cacheKey = this.normalizeCacheKey(url);
105
- const cached = this._ssrCache.get(cacheKey);
106
- if (cached) {
107
- appHtml = cached.html;
108
- preloadedData = cached.preloadedData;
127
+ const hasCookies = !!cookiesRaw;
128
+ // Evita cachear respostas personalizadas por cookies (ex.: auth).
129
+ if (!hasCookies) {
130
+ const cached = this._ssrCache.get(cacheKey);
131
+ if (cached) {
132
+ appHtml = cached.html;
133
+ preloadedData = cached.preloadedData;
134
+ }
135
+ else {
136
+ const result = await render(url, { requestContext });
137
+ appHtml = typeof result.html === 'string' ? result.html : '';
138
+ preloadedData = result.preloadedData ?? {};
139
+ this._ssrCache.set(cacheKey, { html: appHtml, preloadedData });
140
+ }
109
141
  }
110
142
  else {
111
- const result = await render(url);
143
+ const result = await render(url, { requestContext });
112
144
  appHtml = typeof result.html === 'string' ? result.html : '';
113
145
  preloadedData = result.preloadedData ?? {};
114
- this._ssrCache.set(cacheKey, { html: appHtml, preloadedData });
115
146
  }
116
147
  }
117
148
  else {
118
- const result = await render(url);
149
+ const result = await render(url, { requestContext });
119
150
  appHtml = typeof result.html === 'string' ? result.html : '';
120
151
  preloadedData = result.preloadedData ?? {};
121
152
  }
package/dist/types.d.ts CHANGED
@@ -1,10 +1,23 @@
1
+ import { IncomingHttpHeaders } from 'node:http';
1
2
  import type React from 'react';
2
3
  export type ComponentWithGetData = React.ComponentType<any> & {
3
- getData?: (params?: Record<'routeParams' | 'searchParams', Record<string, string>>) => Promise<Record<string, unknown>>;
4
+ getData?: (params?: RouteDataParams) => Promise<Record<string, unknown>>;
4
5
  };
5
6
  export type RouteConfig = {
6
7
  path: string;
7
8
  Component: ComponentWithGetData;
8
9
  isSSR: boolean;
9
10
  };
11
+ export type RequestContext = {
12
+ cookiesRaw: string;
13
+ cookies: Record<string, string>;
14
+ headers: IncomingHttpHeaders;
15
+ method: string;
16
+ url: string;
17
+ };
18
+ export type RouteDataParams = {
19
+ routeParams: Record<string, string>;
20
+ searchParams: Record<string, string>;
21
+ request?: RequestContext;
22
+ };
10
23
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,MAAM,oBAAoB,GAAG,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG;IAC5D,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,aAAa,GAAG,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACzH,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,oBAAoB,CAAC;IAChC,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,MAAM,oBAAoB,GAAG,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG;IAC5D,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC1E,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,oBAAoB,CAAC;IAChC,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AACF,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,mBAAmB,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "lovable-ssr",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "SSR and route data engine for Lovable projects",
5
+ "license": "MIT",
5
6
  "keywords": [
6
7
  "ssr",
7
8
  "react",