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.
package/lib/cli.js CHANGED
@@ -374,7 +374,7 @@ const getApiCompatibilityPayload = (value) => {
374
374
  return findPayload(value, /* @__PURE__ */ new WeakSet());
375
375
  };
376
376
  const name = "webstudio";
377
- const version = "0.267.0";
377
+ const version = "0.269.0";
378
378
  const description = "Webstudio CLI";
379
379
  const author = "Webstudio <github@webstudio.is>";
380
380
  const homepage = "https://webstudio.is";
@@ -386,7 +386,7 @@ const scripts = { "typecheck": "tsgo --noEmit", "build": "rm -rf lib && vite bui
386
386
  const license = "AGPL-3.0-or-later";
387
387
  const engines = { "node": ">=22" };
388
388
  const dependencies = { "@clack/prompts": "^0.10.0", "@emotion/hash": "^0.9.2", "@trpc/client": "^10.45.2", "@webstudio-is/project-migrations": "workspace:*", "@webstudio-is/trpc-interface": "workspace:*", "acorn": "^8.14.1", "acorn-walk": "^8.3.4", "change-case": "^5.4.4", "deepmerge": "^4.3.1", "env-paths": "^3.0.0", "nanoid": "^5.1.5", "p-limit": "^6.2.0", "parse5": "7.3.0", "picocolors": "^1.1.1", "reserved-identifiers": "^1.0.0", "tinyexec": "^0.3.2", "warn-once": "^0.1.1", "yargs": "^17.7.2", "zod": "^3.24.2" };
389
- const devDependencies = { "@cloudflare/vite-plugin": "^1.1.0", "@netlify/vite-plugin-react-router": "^1.0.1", "@react-router/dev": "^7.5.3", "@react-router/fs-routes": "^7.5.3", "@remix-run/cloudflare": "^2.16.5", "@remix-run/cloudflare-pages": "^2.16.5", "@remix-run/dev": "^2.16.5", "@remix-run/node": "^2.16.5", "@remix-run/react": "^2.16.5", "@remix-run/server-runtime": "^2.16.5", "@types/react": "^18.2.70", "@types/react-dom": "^18.2.25", "@types/yargs": "^17.0.33", "@vercel/react-router": "^1.1.0", "@vitejs/plugin-react": "^4.4.1", "@webstudio-is/css-engine": "workspace:*", "@webstudio-is/http-client": "workspace:*", "@webstudio-is/image": "workspace:*", "@webstudio-is/react-sdk": "workspace:*", "@webstudio-is/sdk": "workspace:*", "@webstudio-is/sdk-components-animation": "workspace:*", "@webstudio-is/sdk-components-react": "workspace:*", "@webstudio-is/sdk-components-react-radix": "workspace:*", "@webstudio-is/sdk-components-react-remix": "workspace:*", "@webstudio-is/sdk-components-react-router": "workspace:*", "@webstudio-is/tsconfig": "workspace:*", "h3": "^1.15.1", "ipx": "^3.0.3", "isbot": "^5.1.25", "prettier": "3.5.3", "react": "18.3.0-canary-14898b6a9-20240318", "react-dom": "18.3.0-canary-14898b6a9-20240318", "react-router": "^7.5.3", "ts-expect": "^1.3.0", "vike": "^0.4.229", "vite": "^6.3.4", "vitest": "^3.1.2", "wrangler": "^3.63.2" };
389
+ const devDependencies = { "@cloudflare/vite-plugin": "^1.1.0", "@netlify/vite-plugin-react-router": "^1.0.1", "@react-router/dev": "^7.5.3", "@react-router/fs-routes": "^7.5.3", "@remix-run/cloudflare": "^2.16.5", "@remix-run/cloudflare-pages": "^2.16.5", "@remix-run/dev": "^2.16.5", "@remix-run/node": "^2.16.5", "@remix-run/react": "^2.16.5", "@remix-run/server-runtime": "^2.16.5", "@types/react": "^18.2.70", "@types/react-dom": "^18.2.25", "@types/yargs": "^17.0.33", "@vercel/react-router": "^1.1.0", "@vitejs/plugin-react": "^4.4.1", "@webstudio-is/css-engine": "workspace:*", "@webstudio-is/http-client": "workspace:*", "@webstudio-is/image": "workspace:*", "@webstudio-is/react-sdk": "workspace:*", "@webstudio-is/sdk": "workspace:*", "@webstudio-is/sdk-components-animation": "workspace:*", "@webstudio-is/sdk-components-react": "workspace:*", "@webstudio-is/sdk-components-react-radix": "workspace:*", "@webstudio-is/sdk-components-react-remix": "workspace:*", "@webstudio-is/sdk-components-react-router": "workspace:*", "@webstudio-is/tsconfig": "workspace:*", "@webstudio-is/wsauth": "workspace:*", "h3": "^1.15.1", "ipx": "^3.0.3", "isbot": "^5.1.25", "prettier": "3.5.3", "react": "18.3.0-canary-14898b6a9-20240318", "react-dom": "18.3.0-canary-14898b6a9-20240318", "react-router": "^7.5.3", "ts-expect": "^1.3.0", "vike": "^0.4.229", "vite": "^6.3.4", "vitest": "^3.1.2", "wrangler": "^3.63.2" };
390
390
  const packageJson = {
391
391
  name,
392
392
  version,
@@ -892,6 +892,293 @@ new Map(
892
892
  return weightData.names.map((name2) => [name2, weight]);
893
893
  }).flat()
894
894
  );
895
+ var basicLoginErrors = (login) => {
896
+ const issues = [];
897
+ if (login.length === 0) {
898
+ issues.push({ path: ["login"], message: "Login is required" });
899
+ }
900
+ if (login.includes(":")) {
901
+ issues.push({ path: ["login"], message: "Login can't contain a colon" });
902
+ }
903
+ if (/\s/.test(login)) {
904
+ issues.push({
905
+ path: ["login"],
906
+ message: "Login can't contain whitespace"
907
+ });
908
+ }
909
+ return issues;
910
+ };
911
+ var basicPasswordErrors = (password) => {
912
+ const issues = [];
913
+ if (password.length === 0) {
914
+ issues.push({ path: ["password"], message: "Password is required" });
915
+ }
916
+ if (/\s/.test(password)) {
917
+ issues.push({
918
+ path: ["password"],
919
+ message: "Password can't contain whitespace"
920
+ });
921
+ }
922
+ return issues;
923
+ };
924
+ var validateBasicAuth = ({
925
+ login,
926
+ password
927
+ }) => {
928
+ const issues = [...basicLoginErrors(login), ...basicPasswordErrors(password)];
929
+ if (issues.length > 0) {
930
+ const loginErrors = issues.filter((issue) => issue.path[0] === "login").map((issue) => issue.message);
931
+ const passwordErrors = issues.filter((issue) => issue.path[0] === "password").map((issue) => issue.message);
932
+ return {
933
+ issues,
934
+ errors: {
935
+ login: loginErrors.length > 0 ? loginErrors : void 0,
936
+ password: passwordErrors.length > 0 ? passwordErrors : void 0
937
+ }
938
+ };
939
+ }
940
+ return {
941
+ auth: {
942
+ method: "basic",
943
+ login,
944
+ password,
945
+ credentials: `${login}:${password}`
946
+ }
947
+ };
948
+ };
949
+ var createBasicAuthRoute = ({
950
+ route,
951
+ login,
952
+ password
953
+ }) => {
954
+ const routeError = validateWsAuthRoute(route);
955
+ if (routeError) {
956
+ throw new Error(routeError);
957
+ }
958
+ const auth = validateBasicAuth({ login, password }).auth;
959
+ if (auth === void 0) {
960
+ throw new Error(
961
+ 'Basic auth requires non-empty login and password; login cannot contain ":" and neither field can contain whitespace'
962
+ );
963
+ }
964
+ return { route, auth };
965
+ };
966
+ var createWsAuthRouteFromPage = ({
967
+ route,
968
+ auth
969
+ }) => {
970
+ if (auth === void 0) {
971
+ return;
972
+ }
973
+ if ("method" in auth && auth.method !== "basic" || "type" in auth && auth.type !== "basic") {
974
+ throw new Error(`Unsupported auth method for route "${route}"`);
975
+ }
976
+ return createBasicAuthRoute({
977
+ route,
978
+ login: auth.login,
979
+ password: auth.password
980
+ });
981
+ };
982
+ var isRecord = (value) => {
983
+ return typeof value === "object" && value !== null && Array.isArray(value) === false;
984
+ };
985
+ var parameterSegment = /^:\w+[?*]?$/;
986
+ var validateWsAuthRoute = (route) => {
987
+ if (route.startsWith("/") === false) {
988
+ return 'Route must start with "/"';
989
+ }
990
+ if (route === "/") {
991
+ return;
992
+ }
993
+ if (route !== "/" && route.endsWith("/")) {
994
+ return 'Route must not end with "/"';
995
+ }
996
+ if (route.includes("//")) {
997
+ return 'Route must not contain repeating "/"';
998
+ }
999
+ const segments = route.slice(1).split("/");
1000
+ for (let index = 0; index < segments.length; index += 1) {
1001
+ const segment = segments[index];
1002
+ if (segment === void 0 || segment === "") {
1003
+ return "Route contains an empty segment";
1004
+ }
1005
+ if (segment === "*" || /^:\w+\*$/.test(segment)) {
1006
+ if (index !== segments.length - 1) {
1007
+ return "Wildcard route segment must be the last segment";
1008
+ }
1009
+ continue;
1010
+ }
1011
+ if (segment.startsWith(":") && parameterSegment.test(segment) === false) {
1012
+ return `Invalid route parameter "${segment}"`;
1013
+ }
1014
+ if (segment.includes("*")) {
1015
+ return "Wildcard can only be used as * or :name*";
1016
+ }
1017
+ }
1018
+ };
1019
+ var parseJson = (content, errors) => {
1020
+ if (content.trim() === "") {
1021
+ return { version: 1, routes: {} };
1022
+ }
1023
+ try {
1024
+ return JSON.parse(content);
1025
+ } catch (error) {
1026
+ errors.push({
1027
+ path: "$",
1028
+ message: error instanceof Error ? error.message : "Invalid JSON"
1029
+ });
1030
+ }
1031
+ };
1032
+ var parseWsAuth = (content) => {
1033
+ const routes = [];
1034
+ const errors = [];
1035
+ const json = parseJson(content, errors);
1036
+ if (json === void 0) {
1037
+ return { routes, errors };
1038
+ }
1039
+ if (isRecord(json) === false) {
1040
+ errors.push({ path: "$", message: "Auth config must be an object" });
1041
+ return { routes, errors };
1042
+ }
1043
+ if (json.version !== 1) {
1044
+ errors.push({ path: "version", message: "Version must be 1" });
1045
+ }
1046
+ if (isRecord(json.routes) === false) {
1047
+ errors.push({ path: "routes", message: "Routes must be an object" });
1048
+ return { routes, errors };
1049
+ }
1050
+ for (const [route, authInput] of Object.entries(json.routes)) {
1051
+ const routeError = validateWsAuthRoute(route);
1052
+ if (routeError) {
1053
+ errors.push({
1054
+ path: `routes.${JSON.stringify(route)}`,
1055
+ message: routeError
1056
+ });
1057
+ continue;
1058
+ }
1059
+ if (isRecord(authInput) === false) {
1060
+ errors.push({
1061
+ path: `routes.${JSON.stringify(route)}`,
1062
+ message: "Auth rule must be an object"
1063
+ });
1064
+ continue;
1065
+ }
1066
+ if (authInput.method !== "basic") {
1067
+ errors.push({
1068
+ path: `routes.${JSON.stringify(route)}.method`,
1069
+ message: 'Auth method must be "basic"'
1070
+ });
1071
+ continue;
1072
+ }
1073
+ const login = authInput.login;
1074
+ const password = authInput.password;
1075
+ if (typeof login !== "string") {
1076
+ errors.push({
1077
+ path: `routes.${JSON.stringify(route)}.login`,
1078
+ message: "Login must be a string"
1079
+ });
1080
+ continue;
1081
+ }
1082
+ if (typeof password !== "string") {
1083
+ errors.push({
1084
+ path: `routes.${JSON.stringify(route)}.password`,
1085
+ message: "Password must be a string"
1086
+ });
1087
+ continue;
1088
+ }
1089
+ const validation = validateBasicAuth({ login, password });
1090
+ if (validation.auth === void 0) {
1091
+ for (const issue of validation.issues ?? []) {
1092
+ errors.push({
1093
+ path: `routes.${JSON.stringify(route)}.${issue.path[0]}`,
1094
+ message: issue.message
1095
+ });
1096
+ }
1097
+ continue;
1098
+ }
1099
+ const auth = validation.auth;
1100
+ routes.push({ route, auth });
1101
+ }
1102
+ return { routes, errors };
1103
+ };
1104
+ var parseWsAuthOrThrow = (content, sourceName) => {
1105
+ const result = parseWsAuth(content);
1106
+ if (result.errors.length > 0) {
1107
+ const message = result.errors.map((error) => `${sourceName}:${error.path} ${error.message}`).join("\n");
1108
+ throw new Error(message);
1109
+ }
1110
+ return result.routes;
1111
+ };
1112
+ var serializeWsAuth = (routes) => {
1113
+ const config = { version: 1, routes: {} };
1114
+ for (const { route, auth } of routes) {
1115
+ switch (auth.method) {
1116
+ case "basic":
1117
+ config.routes[route] = {
1118
+ method: "basic",
1119
+ login: auth.login,
1120
+ password: auth.password
1121
+ };
1122
+ break;
1123
+ }
1124
+ }
1125
+ return `${JSON.stringify(config, null, 2)}
1126
+ `;
1127
+ };
1128
+ var collectWsAuthRoutes = (sources) => {
1129
+ return sources.flatMap((source) => {
1130
+ if ("content" in source) {
1131
+ return parseWsAuthOrThrow(source.content, source.name);
1132
+ }
1133
+ return source.routes;
1134
+ });
1135
+ };
1136
+ var buildWsAuth = (sources) => {
1137
+ const content = serializeWsAuth(collectWsAuthRoutes(sources));
1138
+ const routes = parseWsAuthOrThrow(content, "Serialized auth config");
1139
+ return {
1140
+ routes,
1141
+ content
1142
+ };
1143
+ };
1144
+ var createWsAuthResources = ({
1145
+ projectContent = "",
1146
+ pages,
1147
+ projectSourceName = "Auth",
1148
+ generatedSourceName = "Generated page auth"
1149
+ }) => {
1150
+ const generatedRoutes = pages.flatMap((page) => {
1151
+ const route = createWsAuthRouteFromPage(page);
1152
+ return route === void 0 ? [] : [route];
1153
+ });
1154
+ const result = buildWsAuth([
1155
+ { name: projectSourceName, content: projectContent },
1156
+ { name: generatedSourceName, routes: generatedRoutes }
1157
+ ]);
1158
+ console.info("[wsauth] create resources", {
1159
+ projectContentLength: projectContent.length,
1160
+ projectContent,
1161
+ pageCount: pages.length,
1162
+ pages,
1163
+ generatedRouteCount: generatedRoutes.length,
1164
+ generatedRoutes,
1165
+ routeCount: result.routes.length,
1166
+ routes: result.routes
1167
+ });
1168
+ return {
1169
+ ...result,
1170
+ module: [
1171
+ `import type { WsAuthRoute } from "@webstudio-is/wsauth";`,
1172
+ "",
1173
+ `export const authRoutes: WsAuthRoute[] = ${JSON.stringify(
1174
+ result.routes,
1175
+ null,
1176
+ 2
1177
+ )};`,
1178
+ ""
1179
+ ].join("\n")
1180
+ };
1181
+ };
895
1182
  function dot3(a2, b2) {
896
1183
  return a2[0] * b2[0] + a2[1] * b2[1] + a2[2] * b2[2];
897
1184
  }
@@ -2687,8 +2974,19 @@ const oklch = new ColorSpace({
2687
2974
  }
2688
2975
  }
2689
2976
  });
2977
+ var isKeyword = (value, keyword) => {
2978
+ if (value.type === "keyword") {
2979
+ return value.value === keyword;
2980
+ }
2981
+ if (value.type === "layers") {
2982
+ return value.value.some((layer) => isKeyword(layer, keyword));
2983
+ }
2984
+ return false;
2985
+ };
2690
2986
  var prefixStyles = (styleMap) => {
2691
2987
  const newStyleMap = /* @__PURE__ */ new Map();
2988
+ const backgroundClip = styleMap.get("background-clip");
2989
+ const hasTextBackgroundClip = backgroundClip !== void 0 && isKeyword(backgroundClip, "text");
2692
2990
  for (const [property, value] of styleMap) {
2693
2991
  if (property === "background-clip") {
2694
2992
  newStyleMap.set("-webkit-background-clip", value);
@@ -2702,6 +3000,9 @@ var prefixStyles = (styleMap) => {
2702
3000
  if (property === "backdrop-filter") {
2703
3001
  newStyleMap.set("-webkit-backdrop-filter", value);
2704
3002
  }
3003
+ if (property === "color" && hasTextBackgroundClip && isKeyword(value, "transparent")) {
3004
+ newStyleMap.set("-webkit-text-fill-color", value);
3005
+ }
2705
3006
  if (property === "view-timeline-name" || property === "scroll-timeline-name" || property === "view-timeline-inset") {
2706
3007
  newStyleMap.set(`--${property}`, value);
2707
3008
  }
@@ -3807,7 +4108,36 @@ var PageTitle = z.string().refine(
3807
4108
  (val) => val.length >= MIN_TITLE_LENGTH,
3808
4109
  `Minimum ${MIN_TITLE_LENGTH} characters required`
3809
4110
  );
3810
- var documentTypes = ["html", "xml"];
4111
+ var documentTypes = ["html", "xml", "text"];
4112
+ var BasicAuthFields = {
4113
+ login: z.string(),
4114
+ password: z.string()
4115
+ };
4116
+ var validateBasicAuthFields = ({
4117
+ login,
4118
+ password
4119
+ }, context) => {
4120
+ for (const issue of validateBasicAuth({ login, password }).issues ?? []) {
4121
+ context.addIssue({
4122
+ code: z.ZodIssueCode.custom,
4123
+ path: issue.path,
4124
+ message: issue.message
4125
+ });
4126
+ }
4127
+ };
4128
+ var PageBasicAuth = z.object({
4129
+ method: z.literal("basic"),
4130
+ ...BasicAuthFields
4131
+ }).superRefine(validateBasicAuthFields);
4132
+ var LegacyPageBasicAuth = z.object({
4133
+ type: z.literal("basic"),
4134
+ ...BasicAuthFields
4135
+ }).superRefine(validateBasicAuthFields).transform(({ login, password }) => ({
4136
+ method: "basic",
4137
+ login,
4138
+ password
4139
+ }));
4140
+ var PageAuth = z.union([PageBasicAuth, LegacyPageBasicAuth]);
3811
4141
  var commonPageFields = {
3812
4142
  id: PageId,
3813
4143
  name: PageName,
@@ -3825,6 +4155,8 @@ var commonPageFields = {
3825
4155
  status: z.string().optional(),
3826
4156
  redirect: z.string().optional(),
3827
4157
  documentType: z.optional(z.enum(documentTypes)),
4158
+ content: z.string().optional(),
4159
+ auth: PageAuth.optional(),
3828
4160
  custom: z.array(
3829
4161
  z.object({
3830
4162
  property: z.string(),
@@ -3875,12 +4207,21 @@ var Page = z.object({
3875
4207
  ...commonPageFields,
3876
4208
  path: z.union([HomePagePath, PagePath])
3877
4209
  });
4210
+ var PageTemplate = z.object({
4211
+ id: PageId,
4212
+ name: PageName,
4213
+ title: PageTitle,
4214
+ rootInstanceId: z.string(),
4215
+ systemDataSourceId: z.string().optional(),
4216
+ meta: commonPageFields.meta
4217
+ });
3878
4218
  var ProjectMeta = z.object({
3879
4219
  // All fields are optional to ensure consistency and allow for the addition of new fields without requiring migration
3880
4220
  siteName: z.string().optional(),
3881
4221
  contactEmail: z.string().optional(),
3882
4222
  faviconAssetId: z.string().optional(),
3883
- code: z.string().optional()
4223
+ code: z.string().optional(),
4224
+ auth: z.string().optional()
3884
4225
  });
3885
4226
  var ProjectNewRedirectPath = z.string().min(1, "Path is required").refine((data) => {
3886
4227
  try {
@@ -3906,6 +4247,7 @@ var Pages = z.object({
3906
4247
  homePageId: PageId,
3907
4248
  rootFolderId: FolderId,
3908
4249
  pages: z.map(PageId, Page),
4250
+ pageTemplates: z.map(PageId, PageTemplate).optional(),
3909
4251
  folders: z.map(FolderId, Folder).refine((folders) => folders.size > 0, "Folders can't be empty")
3910
4252
  }).superRefine((pages, context) => {
3911
4253
  const homePage = pages.pages.get(pages.homePageId);
@@ -3947,6 +4289,22 @@ var Pages = z.object({
3947
4289
  });
3948
4290
  }
3949
4291
  }
4292
+ for (const [templateId, template] of pages.pageTemplates ?? []) {
4293
+ if (template.id !== templateId) {
4294
+ context.addIssue({
4295
+ code: z.ZodIssueCode.custom,
4296
+ path: ["pageTemplates", templateId, "id"],
4297
+ message: "Page template id must match its record key"
4298
+ });
4299
+ }
4300
+ if (pages.pages.has(templateId)) {
4301
+ context.addIssue({
4302
+ code: z.ZodIssueCode.custom,
4303
+ path: ["pageTemplates", templateId, "id"],
4304
+ message: "Page template id must not match an existing page id"
4305
+ });
4306
+ }
4307
+ }
3950
4308
  for (const [folderId, folder] of pages.folders) {
3951
4309
  if (folder.id !== folderId) {
3952
4310
  context.addIssue({
@@ -4590,6 +4948,13 @@ var Select = z.object({
4590
4948
  defaultValue: z.string().optional(),
4591
4949
  options: z.array(z.string())
4592
4950
  });
4951
+ var TimeZone = z.object({
4952
+ ...common,
4953
+ control: z.literal("timeZone"),
4954
+ type: z.literal("string"),
4955
+ defaultValue: z.string().optional(),
4956
+ options: z.array(z.string())
4957
+ });
4593
4958
  var Check = z.object({
4594
4959
  ...common,
4595
4960
  control: z.literal("check"),
@@ -4670,6 +5035,7 @@ var PropMeta = z.union([
4670
5035
  Radio,
4671
5036
  InlineRadio,
4672
5037
  Select,
5038
+ TimeZone,
4673
5039
  MultiSelect,
4674
5040
  Check,
4675
5041
  InlineCheck,
@@ -4697,6 +5063,7 @@ var componentCategories = [
4697
5063
  "localization",
4698
5064
  "radix",
4699
5065
  "xml",
5066
+ "text",
4700
5067
  "other",
4701
5068
  "hidden",
4702
5069
  "internal"
@@ -5572,6 +5939,29 @@ var parseComponentName = (componentName) => {
5572
5939
  }
5573
5940
  return [namespace, name2];
5574
5941
  };
5942
+ var getHtmlTagsFromProps = (props) => {
5943
+ const tags2 = /* @__PURE__ */ new Map();
5944
+ for (const prop of props.values()) {
5945
+ if (prop.type === "string" && prop.name === "tag") {
5946
+ tags2.set(prop.instanceId, prop.value);
5947
+ }
5948
+ }
5949
+ return tags2;
5950
+ };
5951
+ var getHtmlTagFromInstance = ({
5952
+ instance: instance2,
5953
+ metas,
5954
+ props,
5955
+ htmlTagsByInstanceId
5956
+ }) => {
5957
+ if (instance2.component === "XmlNode") {
5958
+ return;
5959
+ }
5960
+ const meta = metas.get(instance2.component);
5961
+ const metaTag = Object.keys((meta == null ? void 0 : meta.presetStyle) ?? {}).at(0);
5962
+ const propTag = (htmlTagsByInstanceId == null ? void 0 : htmlTagsByInstanceId.get(instance2.id)) ?? getHtmlTagsFromProps(props).get(instance2.id);
5963
+ return instance2.tag ?? propTag ?? metaTag;
5964
+ };
5575
5965
  var getIndexesWithinAncestors = (metas, instances, rootIds) => {
5576
5966
  const ancestors = /* @__PURE__ */ new Set();
5577
5967
  for (const meta of metas.values()) {
@@ -5624,6 +6014,49 @@ var systemParameter = {
5624
6014
  type: "parameter",
5625
6015
  name: "system"
5626
6016
  };
6017
+ var walkAssignmentTarget = (node, visitor) => {
6018
+ var _a2, _b2, _c2, _d2;
6019
+ if (node.type === "Identifier") {
6020
+ (_a2 = visitor.Identifier) == null ? void 0 : _a2.call(visitor, node, "binding");
6021
+ return;
6022
+ }
6023
+ if (node.type === "MemberExpression") {
6024
+ (_b2 = visitor.MemberExpression) == null ? void 0 : _b2.call(visitor, node);
6025
+ const { object } = node;
6026
+ if (object.type === "Identifier") {
6027
+ (_c2 = visitor.Identifier) == null ? void 0 : _c2.call(visitor, object, "memberObject");
6028
+ } else if (object.type === "MemberExpression") {
6029
+ walkAssignmentTarget(object, visitor);
6030
+ }
6031
+ return;
6032
+ }
6033
+ (_d2 = visitor.UnsupportedPattern) == null ? void 0 : _d2.call(visitor, node);
6034
+ };
6035
+ var stringMethodReturnKindByName = /* @__PURE__ */ new Map([
6036
+ ["toLowerCase", "string"],
6037
+ ["replace", "string"],
6038
+ ["split", "array"],
6039
+ ["slice", "string"],
6040
+ ["at", "unknown"],
6041
+ ["endsWith", "boolean"],
6042
+ ["includes", "boolean"],
6043
+ ["startsWith", "boolean"],
6044
+ ["toString", "string"],
6045
+ ["toUpperCase", "string"],
6046
+ ["toLocaleLowerCase", "string"],
6047
+ ["toLocaleUpperCase", "string"]
6048
+ ]);
6049
+ var arrayMethodReturnKindByName = /* @__PURE__ */ new Map([
6050
+ ["at", "unknown"],
6051
+ ["includes", "boolean"],
6052
+ ["join", "string"],
6053
+ ["slice", "array"],
6054
+ ["toString", "string"]
6055
+ ]);
6056
+ new Set(
6057
+ stringMethodReturnKindByName.keys()
6058
+ );
6059
+ new Set(arrayMethodReturnKindByName.keys());
5627
6060
  var transpileExpression = ({
5628
6061
  expression,
5629
6062
  executable = false,
@@ -5636,17 +6069,44 @@ var transpileExpression = ({
5636
6069
  const message = error.message;
5637
6070
  throw Error(`${message} in ${JSON.stringify(expression)}`);
5638
6071
  }
6072
+ const assignmentTargetMemberRanges = [];
6073
+ if (executable) {
6074
+ simple(root, {
6075
+ AssignmentExpression(node) {
6076
+ walkAssignmentTarget(node.left, {
6077
+ MemberExpression(node2) {
6078
+ assignmentTargetMemberRanges.push([node2.start, node2.end]);
6079
+ }
6080
+ });
6081
+ }
6082
+ });
6083
+ }
5639
6084
  const replacements = [];
6085
+ const replacementIndexByRange = /* @__PURE__ */ new Map();
6086
+ const addReplacement = (start, end, fragment, { replaceExisting = false } = {}) => {
6087
+ const range = `${start}:${end}`;
6088
+ const existingIndex = replacementIndexByRange.get(range);
6089
+ if (existingIndex !== void 0) {
6090
+ if (replaceExisting) {
6091
+ replacements[existingIndex] = [start, end, fragment];
6092
+ }
6093
+ return;
6094
+ }
6095
+ replacementIndexByRange.set(range, replacements.length);
6096
+ replacements.push([start, end, fragment]);
6097
+ };
5640
6098
  const replaceIdentifier = (node, assignee) => {
5641
6099
  const newName = replaceVariable == null ? void 0 : replaceVariable(node.name, assignee);
5642
6100
  if (newName) {
5643
- replacements.push([node.start, node.end, newName]);
6101
+ addReplacement(node.start, node.end, newName, {
6102
+ replaceExisting: assignee
6103
+ });
5644
6104
  }
5645
6105
  };
5646
6106
  simple(root, {
5647
6107
  Identifier: (node) => replaceIdentifier(node, false),
5648
6108
  AssignmentExpression(node) {
5649
- simple(node.left, {
6109
+ walkAssignmentTarget(node.left, {
5650
6110
  Identifier: (node2) => replaceIdentifier(node2, true)
5651
6111
  });
5652
6112
  },
@@ -5654,13 +6114,18 @@ var transpileExpression = ({
5654
6114
  if (executable === false || node.optional) {
5655
6115
  return;
5656
6116
  }
6117
+ if (assignmentTargetMemberRanges.some(
6118
+ ([start, end]) => start === node.start && end === node.end
6119
+ )) {
6120
+ return;
6121
+ }
5657
6122
  if (node.computed === false) {
5658
6123
  const dotIndex = expression.indexOf(".", node.object.end);
5659
- replacements.push([dotIndex, dotIndex, "?"]);
6124
+ addReplacement(dotIndex, dotIndex, "?");
5660
6125
  }
5661
6126
  if (node.computed === true) {
5662
6127
  const dotIndex = expression.indexOf("[", node.object.end);
5663
- replacements.push([dotIndex, dotIndex, "?."]);
6128
+ addReplacement(dotIndex, dotIndex, "?.");
5664
6129
  }
5665
6130
  },
5666
6131
  CallExpression(node) {
@@ -5670,7 +6135,7 @@ var transpileExpression = ({
5670
6135
  if (node.callee.type === "MemberExpression") {
5671
6136
  const openParenIndex = expression.indexOf("(", node.callee.end);
5672
6137
  if (openParenIndex !== -1) {
5673
- replacements.push([openParenIndex, openParenIndex, "?."]);
6138
+ addReplacement(openParenIndex, openParenIndex, "?.");
5674
6139
  }
5675
6140
  }
5676
6141
  }
@@ -5744,14 +6209,21 @@ var getHomePage = (pages) => {
5744
6209
  }
5745
6210
  return homePage;
5746
6211
  };
5747
- var findPageByIdOrPath = (idOrPath, pages) => {
6212
+ function findPageByIdOrPath(idOrPath, pages, options = {}) {
6213
+ var _a2;
5748
6214
  if (idOrPath === "" || idOrPath === "/" || idOrPath === pages.homePageId) {
5749
6215
  return getHomePage(pages);
5750
6216
  }
5751
- return getAllPages(pages).find(
6217
+ const found = getAllPages(pages).find(
5752
6218
  (page) => page.id === idOrPath || getPagePath(page.id, pages) === idOrPath
5753
6219
  );
5754
- };
6220
+ if (found) {
6221
+ return found;
6222
+ }
6223
+ if (options.includeTemplates) {
6224
+ return (_a2 = pages.pageTemplates) == null ? void 0 : _a2.get(idOrPath);
6225
+ }
6226
+ }
5755
6227
  var getPagePath = (id, pages) => {
5756
6228
  const foldersMap = /* @__PURE__ */ new Map();
5757
6229
  const childParentMap = /* @__PURE__ */ new Map();
@@ -6063,6 +6535,12 @@ var generatePageMeta = ({
6063
6535
  usedDataSources,
6064
6536
  scope: localScope
6065
6537
  });
6538
+ const contentExpression = generateExpression({
6539
+ expression: page.meta.content ?? "undefined",
6540
+ dataSources,
6541
+ usedDataSources,
6542
+ scope: localScope
6543
+ });
6066
6544
  let customExpression = "";
6067
6545
  customExpression += `[
6068
6546
  `;
@@ -6071,7 +6549,7 @@ var generatePageMeta = ({
6071
6549
  continue;
6072
6550
  }
6073
6551
  const propertyExpression = JSON.stringify(customMeta.property);
6074
- const contentExpression = generateExpression({
6552
+ const contentExpression2 = generateExpression({
6075
6553
  expression: customMeta.content,
6076
6554
  dataSources,
6077
6555
  usedDataSources,
@@ -6081,7 +6559,7 @@ var generatePageMeta = ({
6081
6559
  `;
6082
6560
  customExpression += ` property: ${propertyExpression},
6083
6561
  `;
6084
- customExpression += ` content: ${contentExpression},
6562
+ customExpression += ` content: ${contentExpression2},
6085
6563
  `;
6086
6564
  customExpression += ` },
6087
6565
  `;
@@ -6146,6 +6624,8 @@ var generatePageMeta = ({
6146
6624
  generated += ` status: ${statusExpression},
6147
6625
  `;
6148
6626
  generated += ` redirect: ${redirectExpression},
6627
+ `;
6628
+ generated += ` content: ${contentExpression},
6149
6629
  `;
6150
6630
  generated += ` custom: ${customExpression},
6151
6631
  `;
@@ -6209,22 +6689,19 @@ var generateCss = ({
6209
6689
  const scope = createScope([], normalizeClassName, "-");
6210
6690
  const tagsByComponent = /* @__PURE__ */ new Map();
6211
6691
  tagsByComponent.set(rootComponent, /* @__PURE__ */ new Set(["html"]));
6212
- const tagByInstanceId = /* @__PURE__ */ new Map();
6213
- for (const prop of props.values()) {
6214
- if (prop.type === "string" && prop.name === "tag") {
6215
- tagByInstanceId.set(prop.instanceId, prop.value);
6216
- }
6217
- }
6692
+ const htmlTagsByInstanceId = getHtmlTagsFromProps(props);
6218
6693
  for (const instance2 of instances.values()) {
6219
- const propTag = tagByInstanceId.get(instance2.id);
6220
- const meta = componentMetas.get(instance2.component);
6221
- const metaTag = Object.keys((meta == null ? void 0 : meta.presetStyle) ?? {}).at(0);
6222
6694
  let componentTags = tagsByComponent.get(instance2.component);
6223
6695
  if (componentTags === void 0) {
6224
6696
  componentTags = /* @__PURE__ */ new Set();
6225
6697
  tagsByComponent.set(instance2.component, componentTags);
6226
6698
  }
6227
- const tag = instance2.tag ?? propTag ?? metaTag;
6699
+ const tag = getHtmlTagFromInstance({
6700
+ instance: instance2,
6701
+ metas: componentMetas,
6702
+ props,
6703
+ htmlTagsByInstanceId
6704
+ });
6228
6705
  if (tag) {
6229
6706
  componentTags.add(tag);
6230
6707
  }
@@ -6926,7 +7403,7 @@ var generateJsxChildren = ({
6926
7403
  usedDataSources,
6927
7404
  scope
6928
7405
  });
6929
- generatedChildren = `{${expression}}
7406
+ generatedChildren = `{renderText(${expression})}
6930
7407
  `;
6931
7408
  continue;
6932
7409
  }
@@ -7107,11 +7584,12 @@ var migratePages = (pages) => {
7107
7584
  if (isSerializedPages(pages) && pages.pages instanceof Map && pages.folders instanceof Map) {
7108
7585
  const currentPages = pages;
7109
7586
  const result = Pages.safeParse(currentPages);
7110
- if (result.success) {
7587
+ if (result.success && currentPages.pageTemplates !== void 0) {
7111
7588
  return currentPages;
7112
7589
  }
7113
7590
  return {
7114
7591
  ...currentPages,
7592
+ pageTemplates: currentPages.pageTemplates ?? /* @__PURE__ */ new Map(),
7115
7593
  folders: removeOrphanFolderChildren(
7116
7594
  currentPages.pages,
7117
7595
  currentPages.folders
@@ -7128,6 +7606,7 @@ var migratePages = (pages) => {
7128
7606
  homePageId: pages.homePageId,
7129
7607
  rootFolderId: pages.rootFolderId,
7130
7608
  pages: nextPages2,
7609
+ pageTemplates: pages.pageTemplates === void 0 ? /* @__PURE__ */ new Map() : toMap(pages.pageTemplates),
7131
7610
  folders: removeOrphanFolderChildren(nextPages2, nextFolders2)
7132
7611
  };
7133
7612
  }
@@ -7184,6 +7663,7 @@ var migratePages = (pages) => {
7184
7663
  homePageId: homePage.id,
7185
7664
  rootFolderId: rootFolder.id,
7186
7665
  pages: nextPages,
7666
+ pageTemplates: /* @__PURE__ */ new Map(),
7187
7667
  folders: nextFolders
7188
7668
  };
7189
7669
  };
@@ -7645,7 +8125,7 @@ const g$3 = {
7645
8125
  const t$f = {
7646
8126
  tag: { required: false, control: "text", type: "string" }
7647
8127
  };
7648
- const n$6 = {
8128
+ const n$7 = {
7649
8129
  icon: TextIcon,
7650
8130
  presetStyle: {
7651
8131
  div: [
@@ -7841,14 +8321,14 @@ const c$4 = {
7841
8321
  }
7842
8322
  };
7843
8323
  const o$v = {};
7844
- const r$d = {
8324
+ const r$c = {
7845
8325
  form: [
7846
8326
  ...form,
7847
8327
  { property: "min-height", value: { type: "unit", unit: "px", value: 20 } }
7848
8328
  ]
7849
8329
  }, p$4 = {
7850
8330
  label: "Form",
7851
- presetStyle: r$d,
8331
+ presetStyle: r$c,
7852
8332
  initialProps: ["id", "class", "action"],
7853
8333
  props: o$v
7854
8334
  };
@@ -7862,7 +8342,7 @@ const e$o = {
7862
8342
  },
7863
8343
  quality: { required: false, control: "number", type: "number" }
7864
8344
  };
7865
- const r$c = {
8345
+ const r$b = {
7866
8346
  img: [
7867
8347
  ...img,
7868
8348
  // Otherwise on new image insert onto canvas it can overfit screen size multiple times
@@ -7887,7 +8367,7 @@ const r$c = {
7887
8367
  }, i$7 = {
7888
8368
  category: "media",
7889
8369
  description: "Add an image asset to the page. Webstudio automatically converts images to WebP or AVIF format and makes them responsive for best performance.",
7890
- presetStyle: r$c,
8370
+ presetStyle: r$b,
7891
8371
  order: 0,
7892
8372
  initialProps: [
7893
8373
  "id",
@@ -7978,7 +8458,7 @@ const e$n = {
7978
8458
  value: { type: "rgb", r: 226, g: 226, b: 226, alpha: 1 }
7979
8459
  }
7980
8460
  ]
7981
- }, r$b = {
8461
+ }, r$a = {
7982
8462
  presetStyle: e$n,
7983
8463
  initialProps: ["id", "class", "cite"],
7984
8464
  props: o$u
@@ -8165,7 +8645,7 @@ const e$k = {
8165
8645
  description: "Value of the form control"
8166
8646
  }
8167
8647
  };
8168
- const r$a = {
8648
+ const r$9 = {
8169
8649
  input: [
8170
8650
  ...radio,
8171
8651
  {
@@ -8176,7 +8656,7 @@ const r$a = {
8176
8656
  }, m$7 = {
8177
8657
  label: "Radio",
8178
8658
  icon: RadioCheckedIcon,
8179
- presetStyle: r$a,
8659
+ presetStyle: r$9,
8180
8660
  initialProps: ["id", "class", "name", "value", "required", "checked"],
8181
8661
  props: e$k
8182
8662
  };
@@ -8940,15 +9420,22 @@ const e$d = {
8940
9420
  defaultValue: "medium",
8941
9421
  options: ["full", "long", "medium", "short", "none"]
8942
9422
  },
9423
+ datetime: {
9424
+ required: false,
9425
+ control: "text",
9426
+ type: "string",
9427
+ defaultValue: "dateTime attribute is not set",
9428
+ description: "Machine-readable value"
9429
+ },
8943
9430
  format: {
8944
9431
  description: `Custom format template. Overrides Date Style and Time Style.
8945
9432
 
8946
9433
  Tokens: YYYY/YY (year), MMMM/MMM/MM/M (month), DDDD/DDD/DD/D (day), HH/H (hours), mm/m (minutes), ss/s (seconds)
8947
9434
 
8948
9435
  Examples:
8949
- "YYYY-MM-DD" → 2025-11-03
8950
- "DDDD, MMMM D" → Monday, November 3
8951
- "DDD, D. MMM YYYY" → Mon, 3. Nov 2025
9436
+ - "YYYY-MM-DD" → 2025-11-03
9437
+ - "DDDD, MMMM D" → Monday, November 3
9438
+ - "DDD, D. MMM YYYY" → Mon, 3. Nov 2025
8952
9439
 
8953
9440
  Day and month names use the selected language.`,
8954
9441
  required: false,
@@ -9040,9 +9527,19 @@ Day and month names use the selected language.`,
9040
9527
  type: "string",
9041
9528
  defaultValue: "none",
9042
9529
  options: ["full", "long", "medium", "short", "none"]
9530
+ },
9531
+ timeZone: {
9532
+ description: `Time zone used to format the date.
9533
+
9534
+ Use "UTC" for deterministic UTC output, "visitor" to use the browser time
9535
+ zone after hydration, or an IANA time zone like "Europe/Berlin".`,
9536
+ required: false,
9537
+ control: "text",
9538
+ type: "string",
9539
+ defaultValue: "UTC"
9043
9540
  }
9044
9541
  };
9045
- const r$9 = {
9542
+ const n$6 = {
9046
9543
  category: "localization",
9047
9544
  description: "Converts machine-readable date and time to a human-readable format.",
9048
9545
  contentModel: {
@@ -9058,6 +9555,7 @@ const r$9 = {
9058
9555
  "country",
9059
9556
  "dateStyle",
9060
9557
  "timeStyle",
9558
+ "timeZone",
9061
9559
  "format"
9062
9560
  ],
9063
9561
  props: {
@@ -9084,6 +9582,15 @@ const r$9 = {
9084
9582
  ...e$d.timeStyle,
9085
9583
  contentMode: true
9086
9584
  },
9585
+ timeZone: {
9586
+ required: false,
9587
+ control: "timeZone",
9588
+ type: "string",
9589
+ defaultValue: "UTC",
9590
+ options: ["UTC", "visitor"],
9591
+ description: 'Timezone used to display the date. Use "visitor" to display each visitor’s browser timezone after the page loads, or select/type an IANA timezone like "Europe/Berlin".',
9592
+ contentMode: true
9593
+ },
9087
9594
  format: {
9088
9595
  ...e$d.format,
9089
9596
  contentMode: true
@@ -9238,7 +9745,7 @@ const n$5 = {
9238
9745
  };
9239
9746
  const baseComponentMetas = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
9240
9747
  __proto__: null,
9241
- Blockquote: r$b,
9748
+ Blockquote: r$a,
9242
9749
  Body: i$9,
9243
9750
  Bold: p$6,
9244
9751
  Box: g$3,
@@ -9272,9 +9779,9 @@ const baseComponentMetas = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.
9272
9779
  Span: e$s,
9273
9780
  Subscript: s$3,
9274
9781
  Superscript: o$z,
9275
- Text: n$6,
9782
+ Text: n$7,
9276
9783
  Textarea: l$4,
9277
- Time: r$9,
9784
+ Time: n$6,
9278
9785
  Video: n$5,
9279
9786
  Vimeo: s$2,
9280
9787
  VimeoPlayButton: c$2,
@@ -9879,7 +10386,15 @@ const e$5 = {
9879
10386
  type: "string",
9880
10387
  description: "Current value of the element"
9881
10388
  }
9882
- }, t$2 = {}, r$3 = {}, n$2 = {};
10389
+ }, t$2 = {}, n$2 = {}, r$3 = {
10390
+ forceMount: {
10391
+ description: "Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries or keeping content available in the DOM.",
10392
+ required: false,
10393
+ control: "boolean",
10394
+ type: "boolean",
10395
+ defaultValue: true
10396
+ }
10397
+ };
9883
10398
  const C$1 = {
9884
10399
  icon: AccordionIcon,
9885
10400
  contentModel: {
@@ -9946,7 +10461,7 @@ const C$1 = {
9946
10461
  presetStyle: {
9947
10462
  button: [button, b$2].flat()
9948
10463
  },
9949
- props: r$3
10464
+ props: n$2
9950
10465
  }, H = {
9951
10466
  label: "Item Content",
9952
10467
  icon: ContentIcon,
@@ -9961,7 +10476,7 @@ const C$1 = {
9961
10476
  presetStyle: {
9962
10477
  div
9963
10478
  },
9964
- props: n$2
10479
+ props: r$3
9965
10480
  };
9966
10481
  const e$4 = {
9967
10482
  defaultValue: { required: false, control: "text", type: "string" },
@@ -10547,6 +11062,10 @@ const createFramework$2 = async () => {
10547
11062
  join(routeTemplatesDir, "xml.tsx"),
10548
11063
  "utf8"
10549
11064
  );
11065
+ const textTemplate = await readFile(
11066
+ join(routeTemplatesDir, "text.tsx"),
11067
+ "utf8"
11068
+ );
10550
11069
  const defaultSitemapTemplate = await readFile(
10551
11070
  join(routeTemplatesDir, "default-sitemap.tsx"),
10552
11071
  "utf8"
@@ -10556,7 +11075,7 @@ const createFramework$2 = async () => {
10556
11075
  "utf8"
10557
11076
  );
10558
11077
  await rm(routeTemplatesDir, { recursive: true, force: true });
10559
- const base = "@webstudio-is/sdk-components-react";
11078
+ const base = "@webstudio-is/sdk-components-react/components";
10560
11079
  const remix = "@webstudio-is/sdk-components-react-remix";
10561
11080
  const reactRadix = "@webstudio-is/sdk-components-react-radix";
10562
11081
  const animation = "@webstudio-is/sdk-components-animation";
@@ -10600,6 +11119,12 @@ const createFramework$2 = async () => {
10600
11119
  template: xmlTemplate
10601
11120
  }
10602
11121
  ],
11122
+ text: ({ pagePath }) => [
11123
+ {
11124
+ file: join("app", "routes", `${generateRemixRoute(pagePath)}.tsx`),
11125
+ template: textTemplate
11126
+ }
11127
+ ],
10603
11128
  redirect: ({ pagePath }) => [
10604
11129
  {
10605
11130
  file: join("app", "routes", `${generateRemixRoute(pagePath)}.ts`),
@@ -10628,6 +11153,10 @@ const createFramework$1 = async () => {
10628
11153
  join(routeTemplatesDir, "xml.tsx"),
10629
11154
  "utf8"
10630
11155
  );
11156
+ const textTemplate = await readFile(
11157
+ join(routeTemplatesDir, "text.tsx"),
11158
+ "utf8"
11159
+ );
10631
11160
  const defaultSitemapTemplate = await readFile(
10632
11161
  join(routeTemplatesDir, "default-sitemap.tsx"),
10633
11162
  "utf8"
@@ -10637,7 +11166,7 @@ const createFramework$1 = async () => {
10637
11166
  "utf8"
10638
11167
  );
10639
11168
  await rm(routeTemplatesDir, { recursive: true, force: true });
10640
- const base = "@webstudio-is/sdk-components-react";
11169
+ const base = "@webstudio-is/sdk-components-react/components";
10641
11170
  const reactRouter = "@webstudio-is/sdk-components-react-router";
10642
11171
  const reactRadix = "@webstudio-is/sdk-components-react-radix";
10643
11172
  const animation = "@webstudio-is/sdk-components-animation";
@@ -10681,6 +11210,12 @@ const createFramework$1 = async () => {
10681
11210
  template: xmlTemplate
10682
11211
  }
10683
11212
  ],
11213
+ text: ({ pagePath }) => [
11214
+ {
11215
+ file: join("app", "routes", `${generateRemixRoute(pagePath)}.tsx`),
11216
+ template: textTemplate
11217
+ }
11218
+ ],
10684
11219
  redirect: ({ pagePath }) => [
10685
11220
  {
10686
11221
  file: join("app", "routes", `${generateRemixRoute(pagePath)}.ts`),
@@ -10720,7 +11255,7 @@ const createFramework = async () => {
10720
11255
  "utf8"
10721
11256
  );
10722
11257
  await rm(routeTemplatesDir, { recursive: true, force: true });
10723
- const base = "@webstudio-is/sdk-components-react";
11258
+ const base = "@webstudio-is/sdk-components-react/components";
10724
11259
  const reactRadix = "@webstudio-is/sdk-components-react-radix";
10725
11260
  const animation = "@webstudio-is/sdk-components-animation";
10726
11261
  const components = {};
@@ -10743,7 +11278,8 @@ const createFramework = async () => {
10743
11278
  tags: {
10744
11279
  textarea: `${base}:Textarea`,
10745
11280
  input: `${base}:Input`,
10746
- select: `${base}:Select`
11281
+ select: `${base}:Select`,
11282
+ a: `${base}:Link`
10747
11283
  },
10748
11284
  html: ({ pagePath }) => {
10749
11285
  if (isPathnamePattern(pagePath)) {
@@ -10765,11 +11301,13 @@ const createFramework = async () => {
10765
11301
  ];
10766
11302
  },
10767
11303
  xml: () => [],
11304
+ text: () => [],
10768
11305
  redirect: () => [],
10769
11306
  defaultSitemap: () => []
10770
11307
  };
10771
11308
  };
10772
11309
  const limit = pLimit(10);
11310
+ const wsAuthFile = ".webstudio/auth.json";
10773
11311
  const downloadAsset = async (url, name2, assetBaseUrl) => {
10774
11312
  const assetPath = join("public", assetBaseUrl, name2);
10775
11313
  const tempAssetPath = `${assetPath}.tmp`;
@@ -10813,6 +11351,40 @@ const mergeJsonInto = async (sourcePath, destinationPath) => {
10813
11351
  );
10814
11352
  await writeFile(destinationPath, content, "utf8");
10815
11353
  };
11354
+ const writeWsAuthResources = async (generatedDir, pages) => {
11355
+ var _a2, _b2, _c2, _d2;
11356
+ console.info("[wsauth] prebuild create auth config", {
11357
+ file: wsAuthFile,
11358
+ projectAuthContentLength: ((_b2 = (_a2 = pages.meta) == null ? void 0 : _a2.auth) == null ? void 0 : _b2.length) ?? 0,
11359
+ projectAuthContent: (_c2 = pages.meta) == null ? void 0 : _c2.auth,
11360
+ pages: getAllPages(pages).map((page) => ({
11361
+ id: page.id,
11362
+ name: page.name,
11363
+ route: getPagePath(page.id, pages),
11364
+ auth: page.meta.auth
11365
+ }))
11366
+ });
11367
+ const { content, module } = createWsAuthResources({
11368
+ projectContent: (_d2 = pages.meta) == null ? void 0 : _d2.auth,
11369
+ pages: getAllPages(pages).map((page) => ({
11370
+ route: getPagePath(page.id, pages),
11371
+ auth: page.meta.auth
11372
+ }))
11373
+ });
11374
+ console.info("[wsauth] prebuild write auth config", {
11375
+ file: wsAuthFile,
11376
+ contentLength: content.length,
11377
+ content,
11378
+ generatedModulePath: join(generatedDir, "$resources.wsauth.server.ts"),
11379
+ generatedModule: module
11380
+ });
11381
+ await createFolderIfNotExists(dirname(wsAuthFile));
11382
+ await writeFile(wsAuthFile, content);
11383
+ await createFileIfNotExists(
11384
+ join(generatedDir, "$resources.wsauth.server.ts"),
11385
+ module
11386
+ );
11387
+ };
10816
11388
  const isCliTemplate = async (template) => {
10817
11389
  const currentPath = fileURLToPath(new URL(import.meta.url));
10818
11390
  const templatesPath = normalize(
@@ -10913,6 +11485,7 @@ Please check webstudio --help for more details`
10913
11485
  );
10914
11486
  const pages = migratePages(siteData.build.pages);
10915
11487
  const allPages = getAllPages(pages);
11488
+ await writeWsAuthResources(generatedDir, pages);
10916
11489
  const siteDataByPage = {};
10917
11490
  const fontAssetsByPage = {};
10918
11491
  const backgroundImageAssetsByPage = {};
@@ -11144,11 +11717,13 @@ Please check webstudio --help for more details`
11144
11717
 
11145
11718
 
11146
11719
  import { Fragment, useState } from "react";
11147
- import { useResource, useVariableState } from "@webstudio-is/react-sdk/runtime";
11720
+ import { renderText, useResource, useVariableState } from "@webstudio-is/react-sdk/runtime";
11148
11721
  ${importsString}
11149
11722
 
11150
11723
  export const projectId = "${siteData.build.projectId}";
11151
11724
 
11725
+ export const projectDomain = ${JSON.stringify(siteData.projectDomain)};
11726
+
11152
11727
  export const lastPublished = "${new Date(siteData.build.createdAt).toISOString()}";
11153
11728
 
11154
11729
  export const siteName = ${JSON.stringify(projectMeta == null ? void 0 : projectMeta.siteName)};
@@ -11221,7 +11796,7 @@ Please check webstudio --help for more details`
11221
11796
  await createFileIfNotExists(clientFile, pageExports);
11222
11797
  const serverFile = join(generatedDir, `${generatedBasename}.server.tsx`);
11223
11798
  await createFileIfNotExists(serverFile, serverExports);
11224
- const getTemplates = documentType === "html" ? framework.html : framework.xml;
11799
+ const getTemplates = framework[documentType];
11225
11800
  for (const { file, template } of getTemplates({ pagePath })) {
11226
11801
  const content = template.replaceAll("__CONSTANTS__", importFrom("./app/constants.mjs", file)).replaceAll(
11227
11802
  "__SITEMAP__",
@@ -11229,6 +11804,9 @@ Please check webstudio --help for more details`
11229
11804
  ).replaceAll(
11230
11805
  "__ASSETS__",
11231
11806
  importFrom(`./app/__generated__/$resources.assets`, file)
11807
+ ).replaceAll(
11808
+ "__AUTH__",
11809
+ importFrom(`./app/__generated__/$resources.wsauth.server`, file)
11232
11810
  ).replaceAll(
11233
11811
  "__CLIENT__",
11234
11812
  importFrom(`./app/__generated__/${generatedBasename}`, file)