webstudio 0.267.0 → 0.269.0

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.
@@ -0,0 +1,107 @@
1
+ import { type LoaderFunctionArgs, redirect } from "react-router";
2
+ import { isLocalResource, loadResources } from "@webstudio-is/sdk/runtime";
3
+ import { authenticateRequest } from "@webstudio-is/wsauth";
4
+ import { projectDomain } from "__CLIENT__";
5
+ import { getPageMeta, getRemixParams, getResources } from "__SERVER__";
6
+ import { sitemap } from "__SITEMAP__";
7
+ import { assets } from "__ASSETS__";
8
+ import { authRoutes } from "__AUTH__";
9
+
10
+ const authenticateProductionRequest = (request: Request) => {
11
+ const host =
12
+ request.headers.get("x-forwarded-host") ||
13
+ request.headers.get("host") ||
14
+ "";
15
+
16
+ const requestHost = host.split(":")[0];
17
+ if (
18
+ projectDomain !== undefined &&
19
+ (requestHost === projectDomain ||
20
+ requestHost.startsWith(`${projectDomain}.`))
21
+ ) {
22
+ return;
23
+ }
24
+
25
+ return authenticateRequest(request, authRoutes);
26
+ };
27
+
28
+ const customFetch: typeof fetch = (input, init) => {
29
+ if (typeof input !== "string") {
30
+ return fetch(input, init);
31
+ }
32
+
33
+ if (isLocalResource(input, "sitemap.xml")) {
34
+ const response = new Response(JSON.stringify(sitemap));
35
+ response.headers.set("content-type", "application/json; charset=utf-8");
36
+ return Promise.resolve(response);
37
+ }
38
+
39
+ if (isLocalResource(input, "current-date")) {
40
+ const now = new Date();
41
+ const startOfDay = new Date(
42
+ Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())
43
+ );
44
+ const data = {
45
+ iso: startOfDay.toISOString(),
46
+ year: startOfDay.getUTCFullYear(),
47
+ month: startOfDay.getUTCMonth() + 1,
48
+ day: startOfDay.getUTCDate(),
49
+ timestamp: startOfDay.getTime(),
50
+ };
51
+ const response = new Response(JSON.stringify(data));
52
+ response.headers.set("content-type", "application/json; charset=utf-8");
53
+ return Promise.resolve(response);
54
+ }
55
+
56
+ if (isLocalResource(input, "assets")) {
57
+ const response = new Response(JSON.stringify(assets));
58
+ response.headers.set("content-type", "application/json; charset=utf-8");
59
+ return Promise.resolve(response);
60
+ }
61
+
62
+ return fetch(input, init);
63
+ };
64
+
65
+ export const loader = async (arg: LoaderFunctionArgs) => {
66
+ const authRoute = authenticateProductionRequest(arg.request);
67
+
68
+ const url = new URL(arg.request.url);
69
+ const host =
70
+ arg.request.headers.get("x-forwarded-host") ||
71
+ arg.request.headers.get("host") ||
72
+ "";
73
+ url.host = host;
74
+ url.protocol = "https";
75
+
76
+ const params = getRemixParams(arg.params);
77
+
78
+ const system = {
79
+ params,
80
+ search: Object.fromEntries(url.searchParams),
81
+ origin: url.origin,
82
+ pathname: url.pathname,
83
+ };
84
+
85
+ const resources = await loadResources(
86
+ customFetch,
87
+ getResources({ system }).data
88
+ );
89
+ const pageMeta = getPageMeta({ system, resources });
90
+
91
+ if (pageMeta.redirect) {
92
+ const status =
93
+ pageMeta.status === 301 || pageMeta.status === 302
94
+ ? pageMeta.status
95
+ : 302;
96
+ return redirect(pageMeta.redirect, status);
97
+ }
98
+
99
+ return new Response(pageMeta.content ?? "", {
100
+ status: pageMeta.status,
101
+ headers: {
102
+ "Content-Type": "text/plain; charset=utf-8",
103
+ "Cache-Control":
104
+ authRoute === undefined ? "public, max-age=600" : "private, no-store",
105
+ },
106
+ });
107
+ };
@@ -1,15 +1,35 @@
1
1
  import { renderToString } from "react-dom/server";
2
2
  import { type LoaderFunctionArgs, redirect } from "react-router";
3
3
  import { isLocalResource, loadResources } from "@webstudio-is/sdk/runtime";
4
+ import { authenticateRequest } from "@webstudio-is/wsauth";
4
5
  import {
5
6
  ReactSdkContext,
6
7
  xmlNodeTagSuffix,
7
8
  } from "@webstudio-is/react-sdk/runtime";
8
- import { Page, breakpoints } from "__CLIENT__";
9
+ import { Page, breakpoints, projectDomain } from "__CLIENT__";
9
10
  import { getPageMeta, getRemixParams, getResources } from "__SERVER__";
10
11
  import { assetBaseUrl, imageLoader } from "__CONSTANTS__";
11
12
  import { sitemap } from "__SITEMAP__";
12
13
  import { assets } from "__ASSETS__";
14
+ import { authRoutes } from "__AUTH__";
15
+
16
+ const authenticateProductionRequest = (request: Request) => {
17
+ const host =
18
+ request.headers.get("x-forwarded-host") ||
19
+ request.headers.get("host") ||
20
+ "";
21
+
22
+ const requestHost = host.split(":")[0];
23
+ if (
24
+ projectDomain !== undefined &&
25
+ (requestHost === projectDomain ||
26
+ requestHost.startsWith(`${projectDomain}.`))
27
+ ) {
28
+ return;
29
+ }
30
+
31
+ return authenticateRequest(request, authRoutes);
32
+ };
13
33
 
14
34
  const customFetch: typeof fetch = (input, init) => {
15
35
  if (typeof input !== "string") {
@@ -51,6 +71,8 @@ const customFetch: typeof fetch = (input, init) => {
51
71
  };
52
72
 
53
73
  export const loader = async (arg: LoaderFunctionArgs) => {
74
+ const authRoute = authenticateProductionRequest(arg.request);
75
+
54
76
  const url = new URL(arg.request.url);
55
77
  const host =
56
78
  arg.request.headers.get("x-forwarded-host") ||
@@ -108,6 +130,10 @@ export const loader = async (arg: LoaderFunctionArgs) => {
108
130
  text = text.replaceAll(xmlNodeTagSuffix, "");
109
131
 
110
132
  return new Response(`<?xml version="1.0" encoding="UTF-8"?>\n${text}`, {
111
- headers: { "Content-Type": "application/xml" },
133
+ headers: {
134
+ "Content-Type": "application/xml",
135
+ "Cache-Control":
136
+ authRoute === undefined ? "public, max-age=600" : "private, no-store",
137
+ },
112
138
  });
113
139
  };
@@ -10,13 +10,14 @@
10
10
  "dependencies": {
11
11
  "@react-router/dev": "^7.5.3",
12
12
  "@react-router/fs-routes": "^7.5.3",
13
- "@webstudio-is/image": "0.267.0",
14
- "@webstudio-is/react-sdk": "0.267.0",
15
- "@webstudio-is/sdk": "0.267.0",
16
- "@webstudio-is/sdk-components-animation": "0.267.0",
17
- "@webstudio-is/sdk-components-react-radix": "0.267.0",
18
- "@webstudio-is/sdk-components-react-router": "0.267.0",
19
- "@webstudio-is/sdk-components-react": "0.267.0",
13
+ "@webstudio-is/image": "0.269.0",
14
+ "@webstudio-is/react-sdk": "0.269.0",
15
+ "@webstudio-is/sdk": "0.269.0",
16
+ "@webstudio-is/sdk-components-animation": "0.269.0",
17
+ "@webstudio-is/sdk-components-react-radix": "0.269.0",
18
+ "@webstudio-is/sdk-components-react-router": "0.269.0",
19
+ "@webstudio-is/sdk-components-react": "0.269.0",
20
+ "@webstudio-is/wsauth": "0.269.0",
20
21
  "isbot": "^5.1.25",
21
22
  "react": "18.3.0-canary-14898b6a9-20240318",
22
23
  "react-dom": "18.3.0-canary-14898b6a9-20240318",
@@ -7,6 +7,7 @@
7
7
  },
8
8
  "dependencies": {
9
9
  "@cloudflare/vite-plugin": "^1.1.0",
10
+ "@webstudio-is/wsauth": "0.269.0",
10
11
  "wrangler": "^4.14.1"
11
12
  }
12
13
  }
@@ -1,26 +1,49 @@
1
+ import { type ComponentProps, memo, useMemo } from "react";
1
2
  import type { PageContext } from "vike/types";
2
3
  import {
3
4
  PageSettingsMeta,
4
5
  PageSettingsTitle,
5
6
  ReactSdkContext,
6
7
  } from "@webstudio-is/react-sdk/runtime";
8
+ import { LinkCurrentUrlContext } from "@webstudio-is/sdk-components-react";
7
9
  import { assetBaseUrl, imageLoader } from "__CONSTANTS__";
8
10
  import { Page, breakpoints, siteName } from "__CLIENT__";
9
11
 
12
+ const getPageKey = (url: string) => {
13
+ const { origin, pathname, search } = new URL(url);
14
+ return `${origin}${pathname}${search}`;
15
+ };
16
+
17
+ const PageBoundary = memo(
18
+ ({ pageKey, system }: ComponentProps<typeof Page> & { pageKey: string }) => {
19
+ // Use the URL as the key to force scripts in HTML Embed to reload on dynamic pages
20
+ return <Page key={pageKey} system={system} />;
21
+ },
22
+ // Vike can rerender the current page during client-side navigation and
23
+ // hash-only URL updates. Keep the generated page out of that render path,
24
+ // but let actual page URL changes remount it.
25
+ (prevProps, nextProps) => prevProps.pageKey === nextProps.pageKey
26
+ );
27
+
10
28
  const PageComponent = ({ data }: { data: PageContext["data"] }) => {
11
29
  const { system, resources, url, pageMeta } = data;
30
+ const pageKey = getPageKey(url);
31
+ const sdkContext = useMemo(
32
+ () => ({
33
+ imageLoader,
34
+ assetBaseUrl,
35
+ resources,
36
+ breakpoints,
37
+ onError: console.error,
38
+ }),
39
+ [resources]
40
+ );
41
+
12
42
  return (
13
- <ReactSdkContext.Provider
14
- value={{
15
- imageLoader,
16
- assetBaseUrl,
17
- resources,
18
- breakpoints,
19
- onError: console.error,
20
- }}
21
- >
22
- {/* Use the URL as the key to force scripts in HTML Embed to reload on dynamic pages */}
23
- <Page key={url} system={system} />
43
+ <ReactSdkContext.Provider value={sdkContext}>
44
+ <LinkCurrentUrlContext.Provider value={url}>
45
+ <PageBoundary pageKey={pageKey} system={system} />
46
+ </LinkCurrentUrlContext.Provider>
24
47
  <PageSettingsMeta
25
48
  url={url}
26
49
  pageMeta={pageMeta}
@@ -8,12 +8,12 @@
8
8
  "typecheck": "tsgo --noEmit"
9
9
  },
10
10
  "dependencies": {
11
- "@webstudio-is/image": "0.267.0",
12
- "@webstudio-is/react-sdk": "0.267.0",
13
- "@webstudio-is/sdk": "0.267.0",
14
- "@webstudio-is/sdk-components-react": "0.267.0",
15
- "@webstudio-is/sdk-components-animation": "0.267.0",
16
- "@webstudio-is/sdk-components-react-radix": "0.267.0",
11
+ "@webstudio-is/image": "0.269.0",
12
+ "@webstudio-is/react-sdk": "0.269.0",
13
+ "@webstudio-is/sdk": "0.269.0",
14
+ "@webstudio-is/sdk-components-react": "0.269.0",
15
+ "@webstudio-is/sdk-components-animation": "0.269.0",
16
+ "@webstudio-is/sdk-components-react-radix": "0.269.0",
17
17
  "react": "18.3.0-canary-14898b6a9-20240318",
18
18
  "react-dom": "18.3.0-canary-14898b6a9-20240318",
19
19
  "vike": "^0.4.229"
@@ -1,4 +1,4 @@
1
- import { type Root, createRoot } from "react-dom/client";
1
+ import { type Root, hydrateRoot } from "react-dom/client";
2
2
  import type { OnRenderClientSync } from "vike/types";
3
3
 
4
4
  let root: Root;
@@ -19,7 +19,8 @@ export const onRenderClient: OnRenderClientSync = (pageContext) => {
19
19
  </>
20
20
  );
21
21
  if (root === undefined) {
22
- root = createRoot(document.documentElement);
22
+ root = hydrateRoot(document.documentElement, htmlContent);
23
+ return;
23
24
  }
24
25
  document.documentElement.lang = lang;
25
26
  root.render(htmlContent);
Binary file