webstudio 0.305.0 → 0.306.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webstudio",
3
- "version": "0.305.0",
3
+ "version": "0.306.0",
4
4
  "description": "Webstudio CLI",
5
5
  "author": "Webstudio <github@webstudio.is>",
6
6
  "homepage": "https://webstudio.is",
@@ -53,9 +53,9 @@
53
53
  "which": "^5.0.0",
54
54
  "yargs": "^17.7.2",
55
55
  "zod": "^4.4.3",
56
- "@webstudio-is/http-client": "0.305.0",
57
- "@webstudio-is/project-migrations": "0.305.0",
58
- "@webstudio-is/protocol": "0.305.0",
56
+ "@webstudio-is/project-migrations": "0.306.0",
57
+ "@webstudio-is/http-client": "0.306.0",
58
+ "@webstudio-is/protocol": "0.306.0",
59
59
  "@webstudio-is/sync-client": "0.0.0"
60
60
  },
61
61
  "devDependencies": {
@@ -80,7 +80,7 @@
80
80
  "@types/which": "^3.0.4",
81
81
  "@types/yargs": "^17.0.33",
82
82
  "@vercel/react-router": "^1.1.0",
83
- "@vitejs/plugin-react": "^4.4.1",
83
+ "@vitejs/plugin-react": "^4.7.0",
84
84
  "h3": "^1.15.1",
85
85
  "ipx": "^3.0.3",
86
86
  "isbot": "^5.1.25",
@@ -95,28 +95,28 @@
95
95
  "shiki": "4.4.1",
96
96
  "ts-expect": "^1.3.0",
97
97
  "typescript": "7.0.2",
98
- "vike": "^0.4.229",
99
- "vite": "^6.3.4",
98
+ "vike": "0.4.267-commit-feffb0f",
99
+ "vite": "7.1.12",
100
100
  "vitest": "^3.1.2",
101
101
  "wrangler": "4.130.0",
102
- "@webstudio-is/css-engine": "0.305.0",
103
- "@webstudio-is/content-engine": "0.305.0",
104
- "@webstudio-is/fonts": "0.305.0",
105
- "@webstudio-is/image": "0.305.0",
106
- "@webstudio-is/expression": "0.305.0",
102
+ "@webstudio-is/css-engine": "0.306.0",
103
+ "@webstudio-is/content-engine": "0.306.0",
104
+ "@webstudio-is/expression": "0.306.0",
105
+ "@webstudio-is/fonts": "0.306.0",
106
+ "@webstudio-is/image": "0.306.0",
107
+ "@webstudio-is/react-sdk": "0.306.0",
108
+ "@webstudio-is/query-builder": "0.306.0",
109
+ "@webstudio-is/sdk": "0.306.0",
107
110
  "@webstudio-is/project-build": "0.0.0",
108
- "@webstudio-is/query-builder": "0.305.0",
109
- "@webstudio-is/sdk": "0.305.0",
110
- "@webstudio-is/sdk-components-animation": "0.305.0",
111
- "@webstudio-is/sdk-components-react-radix": "0.305.0",
112
- "@webstudio-is/sdk-components-react": "0.305.0",
113
- "@webstudio-is/sdk-components-react-remix": "0.305.0",
114
- "@webstudio-is/react-sdk": "0.305.0",
115
- "@webstudio-is/sdk-components-registry": "0.305.0",
116
- "@webstudio-is/vision": "0.0.0",
111
+ "@webstudio-is/sdk-components-react": "0.306.0",
112
+ "@webstudio-is/sdk-components-animation": "0.306.0",
113
+ "@webstudio-is/sdk-components-react-radix": "0.306.0",
114
+ "@webstudio-is/sdk-components-registry": "0.306.0",
117
115
  "@webstudio-is/tsconfig": "1.0.7",
118
- "@webstudio-is/sdk-components-react-router": "0.305.0",
119
- "@webstudio-is/wsauth": "0.305.0"
116
+ "@webstudio-is/sdk-components-react-remix": "0.306.0",
117
+ "@webstudio-is/sdk-components-react-router": "0.306.0",
118
+ "@webstudio-is/wsauth": "0.306.0",
119
+ "@webstudio-is/vision": "0.0.0"
120
120
  },
121
121
  "scripts": {
122
122
  "typecheck": "tsc --noEmit",
@@ -12,7 +12,7 @@
12
12
  "build-cf-types": "wrangler types"
13
13
  },
14
14
  "dependencies": {
15
- "@webstudio-is/wsauth": "0.305.0",
15
+ "@webstudio-is/wsauth": "0.306.0",
16
16
  "@remix-run/cloudflare": "2.16.5",
17
17
  "@remix-run/cloudflare-pages": "2.16.5"
18
18
  },
@@ -12,12 +12,24 @@ import {
12
12
  import { useLoaderData } from "@remix-run/react";
13
13
  import {
14
14
  isLocalResource,
15
- loadResource,
16
15
  loadResources,
16
+ loadResourceWithEmail,
17
+ loadResourcesWithEmail,
17
18
  cachedFetch,
19
+ getSystemSearch,
20
+ } from "@webstudio-is/sdk/runtime";
21
+ import {
22
+ handleManagedFormSubmission,
23
+ getManagedFormFailure,
24
+ readFormDataWithLimit,
25
+ type ManagedFormResponse,
26
+ } from "@webstudio-is/sdk/form/server";
27
+ import {
28
+ managedFormRequestParamName,
18
29
  formIdFieldName,
30
+ managedFormIdFieldName,
19
31
  formBotFieldName,
20
- } from "@webstudio-is/sdk/runtime";
32
+ } from "@webstudio-is/sdk/form";
21
33
  import { authenticateRequest } from "@webstudio-is/wsauth";
22
34
  import {
23
35
  ReactSdkContext,
@@ -37,6 +49,8 @@ import {
37
49
  } from "__CLIENT__";
38
50
  import {
39
51
  getResources,
52
+ getManagedFormSubmissions,
53
+ getManagedFormResourceGraph,
40
54
  getPageMeta,
41
55
  getRemixParams,
42
56
  contactEmail,
@@ -46,6 +60,11 @@ import css from "__CSS__?url";
46
60
  import { sitemap } from "__SITEMAP__";
47
61
  import { authRoutes } from "__AUTH__";
48
62
  import { createGeneratedAssetResourceFetch } from "__ASSET_QUERY_RUNTIME__";
63
+ import {
64
+ createManagedFormEmailSender,
65
+ createManagedFormResourceFetch,
66
+ validateManagedFormEmail,
67
+ } from "__MANAGED_FORM_FETCH__";
49
68
  import { assetUrlsByPath } from "__ASSETS__";
50
69
 
51
70
  const authenticateProductionRequest = (request: Request) => {
@@ -113,7 +132,7 @@ export const loader = async (arg: LoaderFunctionArgs) => {
113
132
  const params = getRemixParams(arg.params);
114
133
  const system = {
115
134
  params,
116
- search: Object.fromEntries(url.searchParams),
135
+ ...getSystemSearch(url.searchParams),
117
136
  origin: url.origin,
118
137
  pathname: url.pathname,
119
138
  };
@@ -257,24 +276,66 @@ const getRequestHost = (request: Request): string =>
257
276
  export const action = async ({
258
277
  request,
259
278
  context,
279
+ params,
260
280
  }: ActionFunctionArgs): Promise<
261
- { success: true } | { success: false; errors: string[] }
281
+ { success: true } | { success: false; errors: string[] } | ManagedFormResponse
262
282
  > => {
263
283
  authenticateProductionRequest(request);
264
284
 
285
+ let isManagedFormRequest = false;
265
286
  try {
266
287
  const url = new URL(request.url);
267
- url.host = getRequestHost(request);
268
-
269
- const formData = await request.formData();
288
+ isManagedFormRequest =
289
+ url.searchParams.get(managedFormRequestParamName) === "1";
290
+ url.searchParams.delete(managedFormRequestParamName);
291
+ if (!isManagedFormRequest) {
292
+ url.host = getRequestHost(request);
293
+ }
294
+ const formData = isManagedFormRequest
295
+ ? await readFormDataWithLimit(request)
296
+ : await request.formData();
270
297
 
271
298
  const system = {
272
- params: {},
273
- search: {},
299
+ params: getRemixParams(params ?? {}),
300
+ ...getSystemSearch(url.searchParams),
274
301
  origin: url.origin,
275
302
  pathname: url.pathname,
276
303
  };
277
304
 
305
+ const managedFormIds = formData.getAll(managedFormIdFieldName);
306
+ if (!isManagedFormRequest && managedFormIds.length > 0) {
307
+ throw new Error("Invalid Form submission");
308
+ }
309
+ if (isManagedFormRequest) {
310
+ if (managedFormIds.length !== 1 || formData.has(formIdFieldName)) {
311
+ throw new Error("Invalid Form submission");
312
+ }
313
+ const protectedFetch = createManagedFormResourceFetch({
314
+ request,
315
+ context,
316
+ projectDomain,
317
+ });
318
+ return await handleManagedFormSubmission({
319
+ request,
320
+ formData,
321
+ url,
322
+ system,
323
+ configuration: (id) => getManagedFormSubmissions().get(id),
324
+ getGraph: (id, values) => getManagedFormResourceGraph(id, values),
325
+ createEmailSender: (data) =>
326
+ createManagedFormEmailSender({ context, formData: data, projectId }),
327
+ validateEmail: validateManagedFormEmail,
328
+ resourceFetch: protectedFetch,
329
+ validateDestination: protectedFetch.validateDestination,
330
+ trustedIp:
331
+ typeof context === "object" &&
332
+ context !== null &&
333
+ "cloudflare" in context
334
+ ? request.headers.get("cf-connecting-ip") ?? undefined
335
+ : undefined,
336
+ });
337
+ }
338
+
278
339
  const resourceName = formData.get(formIdFieldName);
279
340
  const generatedResources = getResources({ system });
280
341
  const actionResource =
@@ -308,7 +369,7 @@ export const action = async ({
308
369
  formData.delete(formIdFieldName);
309
370
  formData.delete(formBotFieldName);
310
371
 
311
- let result: Awaited<ReturnType<typeof loadResource>>;
372
+ let result: Awaited<ReturnType<typeof loadResourceWithEmail>>;
312
373
  if (actionResource === undefined) {
313
374
  if (contactEmail === undefined) {
314
375
  throw new Error("Contact email not found");
@@ -322,14 +383,14 @@ export const action = async ({
322
383
  if (resource === undefined) {
323
384
  throw Error("Resource not found");
324
385
  }
325
- result = await loadResource(fetch, resource);
386
+ result = await loadResourceWithEmail(fetch, resource);
326
387
  } else {
327
388
  const actionFetch = await createGeneratedAssetResourceFetch({
328
389
  request,
329
390
  context,
330
391
  fallback: customFetch,
331
392
  });
332
- const results = await loadResources(
393
+ const results = await loadResourcesWithEmail(
333
394
  actionFetch,
334
395
  {
335
396
  ...generatedResources.data,
@@ -348,7 +409,7 @@ export const action = async ({
348
409
  if (actionResult === undefined) {
349
410
  throw Error("Resource not found");
350
411
  }
351
- result = actionResult as Awaited<ReturnType<typeof loadResource>>;
412
+ result = actionResult as Awaited<ReturnType<typeof loadResourceWithEmail>>;
352
413
  }
353
414
  const { ok, statusText } = result;
354
415
  if (ok) {
@@ -358,6 +419,11 @@ export const action = async ({
358
419
  } catch (error) {
359
420
  console.error(error);
360
421
 
422
+ if (isManagedFormRequest) {
423
+ return getManagedFormFailure(
424
+ error instanceof Error ? error.message : "Unknown error"
425
+ );
426
+ }
361
427
  return {
362
428
  success: false,
363
429
  errors: [error instanceof Error ? error.message : "Unknown error"],
@@ -1,5 +1,5 @@
1
1
  import { type LoaderFunctionArgs, redirect } from "@remix-run/server-runtime";
2
- import { isLocalResource, loadResources } from "@webstudio-is/sdk/runtime";
2
+ import { isLocalResource, loadResources, getSystemSearch } from "@webstudio-is/sdk/runtime";
3
3
  import { authenticateRequest } from "@webstudio-is/wsauth";
4
4
  import { projectDomain } from "__CLIENT__";
5
5
  import { getPageMeta, getRemixParams, getResources } from "__SERVER__";
@@ -71,7 +71,7 @@ export const loader = async (arg: LoaderFunctionArgs) => {
71
71
 
72
72
  const system = {
73
73
  params,
74
- search: Object.fromEntries(url.searchParams),
74
+ ...getSystemSearch(url.searchParams),
75
75
  origin: url.origin,
76
76
  pathname: url.pathname,
77
77
  };
@@ -1,6 +1,6 @@
1
1
  import { renderToString } from "react-dom/server";
2
2
  import { type LoaderFunctionArgs, redirect } from "@remix-run/server-runtime";
3
- import { isLocalResource, loadResources } from "@webstudio-is/sdk/runtime";
3
+ import { isLocalResource, loadResources, getSystemSearch } from "@webstudio-is/sdk/runtime";
4
4
  import { authenticateRequest } from "@webstudio-is/wsauth";
5
5
  import {
6
6
  ReactSdkContext,
@@ -79,7 +79,7 @@ export const loader = async (arg: LoaderFunctionArgs) => {
79
79
 
80
80
  const system = {
81
81
  params,
82
- search: Object.fromEntries(url.searchParams),
82
+ ...getSystemSearch(url.searchParams),
83
83
  origin: url.origin,
84
84
  pathname: url.pathname,
85
85
  };
@@ -13,14 +13,14 @@
13
13
  "@remix-run/node": "2.16.5",
14
14
  "@remix-run/react": "2.16.5",
15
15
  "@remix-run/server-runtime": "2.16.5",
16
- "@webstudio-is/image": "0.305.0",
17
- "@webstudio-is/react-sdk": "0.305.0",
18
- "@webstudio-is/sdk": "0.305.0",
19
- "@webstudio-is/sdk-components-react": "0.305.0",
20
- "@webstudio-is/sdk-components-animation": "0.305.0",
21
- "@webstudio-is/sdk-components-react-radix": "0.305.0",
22
- "@webstudio-is/sdk-components-react-remix": "0.305.0",
23
- "@webstudio-is/wsauth": "0.305.0",
16
+ "@webstudio-is/image": "0.306.0",
17
+ "@webstudio-is/react-sdk": "0.306.0",
18
+ "@webstudio-is/sdk": "0.306.0",
19
+ "@webstudio-is/sdk-components-react": "0.306.0",
20
+ "@webstudio-is/sdk-components-animation": "0.306.0",
21
+ "@webstudio-is/sdk-components-react-radix": "0.306.0",
22
+ "@webstudio-is/sdk-components-react-remix": "0.306.0",
23
+ "@webstudio-is/wsauth": "0.306.0",
24
24
  "isbot": "^5.1.25",
25
25
  "react": "18.3.0-canary-14898b6a9-20240318",
26
26
  "react-dom": "18.3.0-canary-14898b6a9-20240318",
@@ -12,12 +12,24 @@ import {
12
12
  } from "react-router";
13
13
  import {
14
14
  isLocalResource,
15
- loadResource,
16
15
  loadResources,
17
- formIdFieldName,
18
- formBotFieldName,
16
+ loadResourceWithEmail,
17
+ loadResourcesWithEmail,
18
+ getSystemSearch,
19
19
  cachedFetch,
20
20
  } from "@webstudio-is/sdk/runtime";
21
+ import {
22
+ handleManagedFormSubmission,
23
+ getManagedFormFailure,
24
+ readFormDataWithLimit,
25
+ type ManagedFormResponse,
26
+ } from "@webstudio-is/sdk/form/server";
27
+ import {
28
+ managedFormRequestParamName,
29
+ formIdFieldName,
30
+ managedFormIdFieldName,
31
+ formBotFieldName,
32
+ } from "@webstudio-is/sdk/form";
21
33
  import { authenticateProjectRequest } from "@webstudio-is/wsauth";
22
34
  import {
23
35
  ReactSdkContext,
@@ -36,6 +48,8 @@ import {
36
48
  } from "__CLIENT__";
37
49
  import {
38
50
  getResources,
51
+ getManagedFormSubmissions,
52
+ getManagedFormResourceGraph,
39
53
  getPageMeta,
40
54
  getRemixParams,
41
55
  contactEmail,
@@ -45,6 +59,11 @@ import css from "__CSS__?url";
45
59
  import { sitemap } from "__SITEMAP__";
46
60
  import { authRoutes } from "__AUTH__";
47
61
  import { createGeneratedAssetResourceFetch } from "__ASSET_QUERY_RUNTIME__";
62
+ import {
63
+ createManagedFormEmailSender,
64
+ createManagedFormResourceFetch,
65
+ validateManagedFormEmail,
66
+ } from "__MANAGED_FORM_FETCH__";
48
67
  import { assetUrlsByPath } from "__ASSETS__";
49
68
 
50
69
  const customFetch: typeof fetch = (input, init) => {
@@ -98,7 +117,7 @@ export const loader = async (arg: LoaderFunctionArgs) => {
98
117
  const params = getRemixParams(arg.params);
99
118
  const system = {
100
119
  params,
101
- search: Object.fromEntries(url.searchParams),
120
+ ...getSystemSearch(url.searchParams),
102
121
  origin: url.origin,
103
122
  pathname: url.pathname,
104
123
  };
@@ -242,24 +261,66 @@ const getRequestHost = (request: Request): string =>
242
261
  export const action = async ({
243
262
  request,
244
263
  context,
264
+ params,
245
265
  }: ActionFunctionArgs): Promise<
246
- { success: true } | { success: false; errors: string[] }
266
+ { success: true } | { success: false; errors: string[] } | ManagedFormResponse
247
267
  > => {
248
268
  authenticateProjectRequest(request, authRoutes, projectDomain);
249
269
 
270
+ let isManagedFormRequest = false;
250
271
  try {
251
272
  const url = new URL(request.url);
252
- url.host = getRequestHost(request);
253
-
254
- const formData = await request.formData();
273
+ isManagedFormRequest =
274
+ url.searchParams.get(managedFormRequestParamName) === "1";
275
+ url.searchParams.delete(managedFormRequestParamName);
276
+ if (!isManagedFormRequest) {
277
+ url.host = getRequestHost(request);
278
+ }
279
+ const formData = isManagedFormRequest
280
+ ? await readFormDataWithLimit(request)
281
+ : await request.formData();
255
282
 
256
283
  const system = {
257
- params: {},
258
- search: {},
284
+ params: getRemixParams(params ?? {}),
285
+ ...getSystemSearch(url.searchParams),
259
286
  origin: url.origin,
260
287
  pathname: url.pathname,
261
288
  };
262
289
 
290
+ const managedFormIds = formData.getAll(managedFormIdFieldName);
291
+ if (!isManagedFormRequest && managedFormIds.length > 0) {
292
+ throw new Error("Invalid Form submission");
293
+ }
294
+ if (isManagedFormRequest) {
295
+ if (managedFormIds.length !== 1 || formData.has(formIdFieldName)) {
296
+ throw new Error("Invalid Form submission");
297
+ }
298
+ const protectedFetch = createManagedFormResourceFetch({
299
+ request,
300
+ context,
301
+ projectDomain,
302
+ });
303
+ return await handleManagedFormSubmission({
304
+ request,
305
+ formData,
306
+ url,
307
+ system,
308
+ configuration: (formId) => getManagedFormSubmissions().get(formId),
309
+ getGraph: getManagedFormResourceGraph,
310
+ createEmailSender: (formData) =>
311
+ createManagedFormEmailSender({ context, formData, projectId }),
312
+ validateEmail: validateManagedFormEmail,
313
+ resourceFetch: protectedFetch,
314
+ validateDestination: protectedFetch.validateDestination,
315
+ trustedIp:
316
+ typeof context === "object" &&
317
+ context !== null &&
318
+ "cloudflare" in context
319
+ ? request.headers.get("cf-connecting-ip") ?? undefined
320
+ : undefined,
321
+ });
322
+ }
323
+
263
324
  const resourceName = formData.get(formIdFieldName);
264
325
  const generatedResources = getResources({ system });
265
326
  const actionResource =
@@ -293,7 +354,7 @@ export const action = async ({
293
354
  formData.delete(formIdFieldName);
294
355
  formData.delete(formBotFieldName);
295
356
 
296
- let result: Awaited<ReturnType<typeof loadResource>>;
357
+ let result: Awaited<ReturnType<typeof loadResourceWithEmail>>;
297
358
  if (actionResource === undefined) {
298
359
  if (contactEmail === undefined) {
299
360
  throw new Error("Contact email not found");
@@ -307,14 +368,14 @@ export const action = async ({
307
368
  if (resource === undefined) {
308
369
  throw Error("Resource not found");
309
370
  }
310
- result = await loadResource(fetch, resource);
371
+ result = await loadResourceWithEmail(fetch, resource);
311
372
  } else {
312
373
  const actionFetch = await createGeneratedAssetResourceFetch({
313
374
  request,
314
375
  context,
315
376
  fallback: customFetch,
316
377
  });
317
- const results = await loadResources(
378
+ const results = await loadResourcesWithEmail(
318
379
  actionFetch,
319
380
  {
320
381
  ...generatedResources.data,
@@ -333,7 +394,7 @@ export const action = async ({
333
394
  if (actionResult === undefined) {
334
395
  throw Error("Resource not found");
335
396
  }
336
- result = actionResult as Awaited<ReturnType<typeof loadResource>>;
397
+ result = actionResult as Awaited<ReturnType<typeof loadResourceWithEmail>>;
337
398
  }
338
399
  const { ok, statusText } = result;
339
400
  if (ok) {
@@ -343,6 +404,11 @@ export const action = async ({
343
404
  } catch (error) {
344
405
  console.error(error);
345
406
 
407
+ if (isManagedFormRequest) {
408
+ return getManagedFormFailure(
409
+ error instanceof Error ? error.message : "Unknown error"
410
+ );
411
+ }
346
412
  return {
347
413
  success: false,
348
414
  errors: [error instanceof Error ? error.message : "Unknown error"],
@@ -1,5 +1,5 @@
1
1
  import { type LoaderFunctionArgs, redirect } from "react-router";
2
- import { isLocalResource, loadResources } from "@webstudio-is/sdk/runtime";
2
+ import { isLocalResource, loadResources, getSystemSearch } from "@webstudio-is/sdk/runtime";
3
3
  import { authenticateProjectRequest } from "@webstudio-is/wsauth";
4
4
  import { projectDomain } from "__CLIENT__";
5
5
  import { getPageMeta, getRemixParams, getResources } from "__SERVER__";
@@ -57,7 +57,7 @@ export const loader = async (arg: LoaderFunctionArgs) => {
57
57
 
58
58
  const system = {
59
59
  params,
60
- search: Object.fromEntries(url.searchParams),
60
+ ...getSystemSearch(url.searchParams),
61
61
  origin: url.origin,
62
62
  pathname: url.pathname,
63
63
  };
@@ -1,6 +1,6 @@
1
1
  import { renderToString } from "react-dom/server";
2
2
  import { type LoaderFunctionArgs, redirect } from "react-router";
3
- import { isLocalResource, loadResources } from "@webstudio-is/sdk/runtime";
3
+ import { isLocalResource, loadResources, getSystemSearch } from "@webstudio-is/sdk/runtime";
4
4
  import { authenticateProjectRequest } from "@webstudio-is/wsauth";
5
5
  import {
6
6
  ReactSdkContext,
@@ -65,7 +65,7 @@ export const loader = async (arg: LoaderFunctionArgs) => {
65
65
 
66
66
  const system = {
67
67
  params,
68
- search: Object.fromEntries(url.searchParams),
68
+ ...getSystemSearch(url.searchParams),
69
69
  origin: url.origin,
70
70
  pathname: url.pathname,
71
71
  };
@@ -15,14 +15,14 @@
15
15
  "@react-router/fs-routes": "^7.5.3",
16
16
  "@react-router/node": "^7.5.3",
17
17
  "@react-router/serve": "^7.5.3",
18
- "@webstudio-is/image": "0.305.0",
19
- "@webstudio-is/react-sdk": "0.305.0",
20
- "@webstudio-is/sdk": "0.305.0",
21
- "@webstudio-is/sdk-components-animation": "0.305.0",
22
- "@webstudio-is/sdk-components-react-radix": "0.305.0",
23
- "@webstudio-is/sdk-components-react-router": "0.305.0",
24
- "@webstudio-is/sdk-components-react": "0.305.0",
25
- "@webstudio-is/wsauth": "0.305.0",
18
+ "@webstudio-is/image": "0.306.0",
19
+ "@webstudio-is/react-sdk": "0.306.0",
20
+ "@webstudio-is/sdk": "0.306.0",
21
+ "@webstudio-is/sdk-components-animation": "0.306.0",
22
+ "@webstudio-is/sdk-components-react-radix": "0.306.0",
23
+ "@webstudio-is/sdk-components-react-router": "0.306.0",
24
+ "@webstudio-is/sdk-components-react": "0.306.0",
25
+ "@webstudio-is/wsauth": "0.306.0",
26
26
  "isbot": "^5.1.25",
27
27
  "react": "18.3.0-canary-14898b6a9-20240318",
28
28
  "react-dom": "18.3.0-canary-14898b6a9-20240318",
@@ -1,8 +1,12 @@
1
- import { defineConfig } from "vite";
1
+ import { existsSync, realpathSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ import { defineConfig, searchForWorkspaceRoot } from "vite";
2
4
  import { reactRouter } from "@react-router/dev/vite";
3
5
 
4
6
  const sourceConditions =
5
7
  process.env.WEBSTUDIO_LOCAL_CLI_BOOTSTRAPPED === "1" ? ["webstudio"] : [];
8
+ const templateDirectory = fileURLToPath(new URL(".", import.meta.url));
9
+ const templateNodeModules = new URL("./node_modules", import.meta.url);
6
10
 
7
11
  export default defineConfig({
8
12
  // Local previews share node_modules, but must not share optimizer state.
@@ -10,6 +14,16 @@ export default defineConfig({
10
14
  plugins: [reactRouter()],
11
15
  server: {
12
16
  hmr: process.env.WEBSTUDIO_PREVIEW_HMR === "disabled" ? false : undefined,
17
+ // The local CLI links node_modules from its source workspace. Vite must
18
+ // serve React Router's client entry from that workspace to hydrate Preview.
19
+ fs: {
20
+ allow: [
21
+ templateDirectory,
22
+ ...(existsSync(templateNodeModules)
23
+ ? [searchForWorkspaceRoot(realpathSync(templateNodeModules))]
24
+ : []),
25
+ ],
26
+ },
13
27
  },
14
28
  resolve: {
15
29
  conditions: [
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "dependencies": {
9
9
  "@cloudflare/vite-plugin": "^1.1.0",
10
- "@webstudio-is/wsauth": "0.305.0",
10
+ "@webstudio-is/wsauth": "0.306.0",
11
11
  "wrangler": "^4.14.1"
12
12
  }
13
13
  }
@@ -0,0 +1 @@
1
+ export const onBeforePrerenderStart = () => __PRERENDER_PATHS__;
@@ -1,5 +1,5 @@
1
1
  import type { PageContextServer } from "vike/types";
2
- import { isLocalResource, loadResources } from "@webstudio-is/sdk/runtime";
2
+ import { isLocalResource, loadResources, getSystemSearch } from "@webstudio-is/sdk/runtime";
3
3
  import { getPageMeta, getResources } from "__SERVER__";
4
4
  import { assets } from "__ASSETS__";
5
5
  import {
@@ -57,7 +57,7 @@ export const data = async (pageContext: PageContextServer) => {
57
57
  const params = pageContext.routeParams;
58
58
  const system = {
59
59
  params,
60
- search: Object.fromEntries(url.searchParams),
60
+ ...getSystemSearch(url.searchParams),
61
61
  origin: url.origin,
62
62
  pathname: url.pathname,
63
63
  };
@@ -0,0 +1,5 @@
1
+ import type { Config } from "vike/types";
2
+
3
+ export default {
4
+ clientHooks: false,
5
+ } satisfies Config;
@@ -0,0 +1,5 @@
1
+ import type { OnRenderHtmlAsync } from "vike/types";
2
+
3
+ export const onRenderHtml: OnRenderHtmlAsync = async (pageContext) => {
4
+ pageContext.content = pageContext.data.pageMeta.content ?? "";
5
+ };
@@ -0,0 +1 @@
1
+ export default __PAGE_PATH__;