webstudio 0.264.0 → 0.266.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
@@ -15,8 +15,9 @@ import { hideBin } from "yargs/helpers";
15
15
  import { join, dirname, normalize, basename, relative } from "node:path";
16
16
  import envPaths from "env-paths";
17
17
  import { z } from "zod";
18
- import { access, constants, writeFile, mkdir, readFile, rm, readdir, cp, rename } from "node:fs/promises";
18
+ import { access, constants, writeFile, mkdir, readFile, rm, rename, readdir, cp } from "node:fs/promises";
19
19
  import { text, isCancel, cancel, log, spinner, confirm, select as select$2 } from "@clack/prompts";
20
+ import { createTRPCUntypedClient, httpBatchLink } from "@trpc/client";
20
21
  import pc from "picocolors";
21
22
  import { existsSync, createWriteStream } from "node:fs";
22
23
  import { pipeline } from "node:stream/promises";
@@ -145,48 +146,33 @@ const loadJSONFile = async (filePath) => {
145
146
  return null;
146
147
  }
147
148
  };
148
- var getLatestBuildUsingProjectId = async (params) => {
149
- const { origin, projectId, authToken } = params;
149
+ var createTrpcClient = (origin, headers) => {
150
150
  const { sourceOrigin } = parseBuilderUrl(origin);
151
- const url = new URL(sourceOrigin);
152
- url.pathname = `/rest/buildId/${projectId}`;
153
- const headers = new Headers();
154
- headers.set("x-auth-token", authToken);
155
- const response = await fetch(url.href, { headers });
156
- if (response.ok) {
157
- return await response.json();
158
- }
159
- const message = await response.text();
160
- throw new Error(message.slice(0, 1e3));
151
+ const url = new URL("/trpc", sourceOrigin);
152
+ return createTRPCUntypedClient({
153
+ links: [
154
+ httpBatchLink({
155
+ url: url.href,
156
+ headers
157
+ })
158
+ ]
159
+ });
161
160
  };
162
161
  var loadProjectDataByBuildId = async (params) => {
163
- const { sourceOrigin } = parseBuilderUrl(params.origin);
164
- const url = new URL(sourceOrigin);
165
- url.pathname = `/rest/build/${params.buildId}`;
166
- const headers = new Headers();
167
- if ("seviceToken" in params) {
168
- headers.set("Authorization", params.seviceToken);
169
- } else {
170
- headers.set("x-auth-token", params.authToken);
171
- }
172
- const response = await fetch(url.href, {
173
- headers
162
+ const headers = "seviceToken" in params ? { Authorization: params.seviceToken } : { "x-auth-token": params.authToken };
163
+ return await createTrpcClient(params.origin, {
164
+ ...params.headers,
165
+ ...headers
166
+ }).query("build.loadProjectDataByBuildId", {
167
+ buildId: params.buildId
174
168
  });
175
- if (response.ok) {
176
- return await response.json();
177
- }
178
- const message = await response.text();
179
- throw new Error(message.slice(0, 1e3));
180
169
  };
181
170
  var loadProjectDataByProjectId = async (params) => {
182
- const result = await getLatestBuildUsingProjectId(params);
183
- if (result.buildId === null) {
184
- throw new Error(`The project is not published yet`);
185
- }
186
- return await loadProjectDataByBuildId({
187
- buildId: result.buildId,
188
- origin: params.origin,
189
- authToken: params.authToken
171
+ return await createTrpcClient(params.origin, {
172
+ ...params.headers,
173
+ "x-auth-token": params.authToken
174
+ }).query("build.loadProjectDataByProjectId", {
175
+ projectId: params.projectId
190
176
  });
191
177
  };
192
178
  var buildProjectDomainPrefix = "p-";
@@ -220,6 +206,36 @@ var parseBuilderUrl = (urlStr) => {
220
206
  sourceOrigin: sourceUrl.origin
221
207
  };
222
208
  };
209
+ const wait = (duration) => new Promise((resolve) => setTimeout(resolve, duration));
210
+ const withConfigLock = async (callback) => {
211
+ const lockPath = `${GLOBAL_CONFIG_FILE}.lock`;
212
+ const start = Date.now();
213
+ while (true) {
214
+ try {
215
+ await mkdir(lockPath);
216
+ break;
217
+ } catch (error) {
218
+ const code2 = error.code;
219
+ if (code2 !== "EEXIST") {
220
+ throw error;
221
+ }
222
+ if (Date.now() - start > 1e4) {
223
+ throw new Error(`Timed out waiting for config lock ${lockPath}`);
224
+ }
225
+ await wait(50);
226
+ }
227
+ }
228
+ try {
229
+ return await callback();
230
+ } finally {
231
+ await rm(lockPath, { recursive: true, force: true });
232
+ }
233
+ };
234
+ const writeConfigFile = async (content) => {
235
+ const temporaryFile = `${GLOBAL_CONFIG_FILE}.${process.pid}.tmp`;
236
+ await writeFile(temporaryFile, content, "utf8");
237
+ await rename(temporaryFile, GLOBAL_CONFIG_FILE);
238
+ };
223
239
  const parseShareLink = (value) => {
224
240
  const url = new URL(value.replaceAll("'", ""));
225
241
  const token = url.searchParams.get("authToken");
@@ -273,16 +289,18 @@ const link = async (options) => {
273
289
  }
274
290
  }
275
291
  const { origin, projectId, token } = parseShareLink(shareLink);
276
- const currentConfig = await readFile(GLOBAL_CONFIG_FILE, "utf-8");
277
- const currentConfigJson = jsonToGlobalConfig(JSON.parse(currentConfig));
278
- const newConfig = {
279
- ...currentConfigJson,
280
- [projectId]: {
281
- origin,
282
- token
283
- }
284
- };
285
- await writeFile(GLOBAL_CONFIG_FILE, JSON.stringify(newConfig, null, 2));
292
+ await withConfigLock(async () => {
293
+ const currentConfig = await readFile(GLOBAL_CONFIG_FILE, "utf-8");
294
+ const currentConfigJson = jsonToGlobalConfig(JSON.parse(currentConfig));
295
+ const newConfig = {
296
+ ...currentConfigJson,
297
+ [projectId]: {
298
+ origin,
299
+ token
300
+ }
301
+ };
302
+ await writeConfigFile(JSON.stringify(newConfig, null, 2));
303
+ });
286
304
  log.info(`Saved credentials for project ${projectId}.
287
305
  You can find your config at ${GLOBAL_CONFIG_FILE}`);
288
306
  const localConfig = {
@@ -294,6 +312,128 @@ You can find your config at ${GLOBAL_CONFIG_FILE}`);
294
312
  );
295
313
  log.step("The project is linked successfully");
296
314
  };
315
+ const apiCompatibilityErrorType = "webstudioApiCompatibilityError";
316
+ const apiClientHeader = "x-webstudio-client";
317
+ const apiClientVersionHeader = "x-webstudio-client-version";
318
+ const ApiCompatibilityTarget = z.enum(["browser", "cli"]);
319
+ z.enum(["browser", "cli", "service"]);
320
+ const ApiCompatibilityAction = z.discriminatedUnion("type", [
321
+ z.object({
322
+ type: z.literal("reloadBrowser")
323
+ }),
324
+ z.object({
325
+ type: z.literal("updateCli")
326
+ })
327
+ ]);
328
+ const ApiCompatibilityPayload = z.object({
329
+ type: z.literal(apiCompatibilityErrorType),
330
+ reason: z.enum(["apiRouteNotFound", "apiProcedureNotFound"]),
331
+ target: ApiCompatibilityTarget,
332
+ message: z.string(),
333
+ action: ApiCompatibilityAction
334
+ });
335
+ const isObject = (value) => typeof value === "object" && value !== null;
336
+ const getApiCompatibilityPayload = (value) => {
337
+ const findPayload = (value2, seen) => {
338
+ const parsed = ApiCompatibilityPayload.safeParse(value2);
339
+ if (parsed.success) {
340
+ return parsed.data;
341
+ }
342
+ if (isObject(value2) === false) {
343
+ return;
344
+ }
345
+ if (seen.has(value2)) {
346
+ return;
347
+ }
348
+ seen.add(value2);
349
+ if (Array.isArray(value2)) {
350
+ for (const item of value2) {
351
+ const payload = findPayload(item, seen);
352
+ if (payload !== void 0) {
353
+ return payload;
354
+ }
355
+ }
356
+ return;
357
+ }
358
+ const candidates = [
359
+ value2.payload,
360
+ value2.error,
361
+ value2.data,
362
+ isObject(value2.data) ? value2.data.apiCompatibility : void 0,
363
+ isObject(value2.shape) ? value2.shape.data : void 0,
364
+ isObject(value2.shape) && isObject(value2.shape.data) ? value2.shape.data.apiCompatibility : void 0,
365
+ value2.cause
366
+ ];
367
+ for (const candidate of candidates) {
368
+ const payload = findPayload(candidate, seen);
369
+ if (payload !== void 0) {
370
+ return payload;
371
+ }
372
+ }
373
+ };
374
+ return findPayload(value, /* @__PURE__ */ new WeakSet());
375
+ };
376
+ const name = "webstudio";
377
+ const version = "0.266.0";
378
+ const description = "Webstudio CLI";
379
+ const author = "Webstudio <github@webstudio.is>";
380
+ const homepage = "https://webstudio.is";
381
+ const type$1 = "module";
382
+ const bin = { "webstudio-cli": "./bin.js", "webstudio": "./bin.js" };
383
+ const imports = { "#cli": { "webstudio": "./src/cli.ts", "default": "./lib/cli.js" } };
384
+ const files = ["lib/*", "templates/*", "bin.js", "!*.{test,stories}.*"];
385
+ const scripts = { "typecheck": "tsgo --noEmit", "build": "rm -rf lib && vite build", "test": "vitest run" };
386
+ const license = "AGPL-3.0-or-later";
387
+ const engines = { "node": ">=22" };
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" };
390
+ const packageJson = {
391
+ name,
392
+ version,
393
+ description,
394
+ author,
395
+ homepage,
396
+ type: type$1,
397
+ bin,
398
+ imports,
399
+ files,
400
+ scripts,
401
+ license,
402
+ engines,
403
+ dependencies,
404
+ devDependencies
405
+ };
406
+ class HandledCliError extends Error {
407
+ constructor() {
408
+ super("Handled CLI error");
409
+ this.name = "HandledCliError";
410
+ }
411
+ }
412
+ const isHandledCliError = (error) => error instanceof HandledCliError;
413
+ const apiCompatibilityHeaders = {
414
+ [apiClientHeader]: "cli",
415
+ [apiClientVersionHeader]: packageJson.version
416
+ };
417
+ const updateCliCommand = "npm install -g webstudio@latest";
418
+ const getCliCompatibilityMessage = (error) => {
419
+ const payload = getApiCompatibilityPayload(error);
420
+ if ((payload == null ? void 0 : payload.action.type) !== "updateCli") {
421
+ return;
422
+ }
423
+ return `${payload.message}
424
+
425
+ Update the CLI with:
426
+ ${updateCliCommand}
427
+
428
+ Or run the latest version once with:
429
+ npx webstudio@latest sync`;
430
+ };
431
+ const stopSyncingWithError = (syncing, error) => {
432
+ const compatibilityMessage = getCliCompatibilityMessage(error);
433
+ const message = error instanceof Error ? error.message : "Unable to synchronize project data";
434
+ syncing.stop(compatibilityMessage ?? message, 2);
435
+ return compatibilityMessage;
436
+ };
297
437
  const syncOptions = (yargs) => yargs.option("buildId", {
298
438
  type: "string",
299
439
  describe: "[Experimental] Project build id to sync"
@@ -310,12 +450,21 @@ const sync = async (options) => {
310
450
  syncing.start(`Synchronizing project data`);
311
451
  if (options.buildId !== void 0 && options.origin !== void 0 && options.authToken !== void 0) {
312
452
  syncing.message(`Synchronizing project data from ${options.origin}`);
313
- project = await loadProjectDataByBuildId({
314
- buildId: options.buildId,
315
- seviceToken: options.authToken,
316
- origin: options.origin
317
- });
318
- project.origin = options.origin;
453
+ try {
454
+ project = await loadProjectDataByBuildId({
455
+ buildId: options.buildId,
456
+ seviceToken: options.authToken,
457
+ origin: options.origin,
458
+ headers: apiCompatibilityHeaders
459
+ });
460
+ project.origin = options.origin;
461
+ } catch (error) {
462
+ const compatibilityMessage = stopSyncingWithError(syncing, error);
463
+ if (compatibilityMessage !== void 0) {
464
+ throw new HandledCliError();
465
+ }
466
+ throw error;
467
+ }
319
468
  } else {
320
469
  const globalConfigText = await readFile(GLOBAL_CONFIG_FILE, "utf-8");
321
470
  const globalConfig = jsonToGlobalConfig(JSON.parse(globalConfigText));
@@ -345,15 +494,20 @@ const sync = async (options) => {
345
494
  project = options.buildId !== void 0 ? await loadProjectDataByBuildId({
346
495
  buildId: options.buildId,
347
496
  authToken: token,
348
- origin
497
+ origin,
498
+ headers: apiCompatibilityHeaders
349
499
  }) : await loadProjectDataByProjectId({
350
500
  projectId: localConfig.projectId,
351
501
  authToken: token,
352
- origin
502
+ origin,
503
+ headers: apiCompatibilityHeaders
353
504
  });
354
505
  project.origin = origin;
355
506
  } catch (error) {
356
- syncing.stop(error.message, 2);
507
+ const compatibilityMessage = stopSyncingWithError(syncing, error);
508
+ if (compatibilityMessage !== void 0) {
509
+ throw new HandledCliError();
510
+ }
357
511
  throw error;
358
512
  }
359
513
  }
@@ -751,9 +905,9 @@ function multiply_v3_m3x3(input2, matrix, out = [0, 0, 0]) {
751
905
  return out;
752
906
  }
753
907
  function isString(str) {
754
- return type$1(str) === "string";
908
+ return type(str) === "string";
755
909
  }
756
- function type$1(o2) {
910
+ function type(o2) {
757
911
  let str = Object.prototype.toString.call(o2);
758
912
  return (str.match(/^\[object\s+(.*?)\]$/)[1] || "").toLowerCase();
759
913
  }
@@ -1532,7 +1686,7 @@ const _ColorSpace = class _ColorSpace {
1532
1686
  if (!space || isInstance(space, this)) {
1533
1687
  return space;
1534
1688
  }
1535
- let argType = type$1(space);
1689
+ let argType = type(space);
1536
1690
  if (argType === "string") {
1537
1691
  let ret = _ColorSpace.registry[space.toLowerCase()];
1538
1692
  if (!ret) {
@@ -1589,7 +1743,7 @@ const _ColorSpace = class _ColorSpace {
1589
1743
  */
1590
1744
  static resolveCoord(ref, workingSpace) {
1591
1745
  var _a2;
1592
- let coordType = type$1(ref);
1746
+ let coordType = type(ref);
1593
1747
  let space, coord;
1594
1748
  if (coordType === "string") {
1595
1749
  if (ref.includes(".")) {
@@ -1612,7 +1766,7 @@ const _ColorSpace = class _ColorSpace {
1612
1766
  `Cannot resolve coordinate reference ${ref}: No color space specified and relative references are not allowed here`
1613
1767
  );
1614
1768
  }
1615
- coordType = type$1(coord);
1769
+ coordType = type(coord);
1616
1770
  if (coordType === "number" || coordType === "string" && coord >= 0) {
1617
1771
  let meta = Object.entries(space.coords)[coord];
1618
1772
  if (meta) {
@@ -2644,7 +2798,7 @@ var toValue = (styleValue, transformValue) => {
2644
2798
  case "oklch":
2645
2799
  return `oklch(${c1} ${c2} ${c3} / ${alpha})`;
2646
2800
  // Fall back to color() function for less common color spaces.
2647
- // Webstudio uses colorjs internal names; map to CSS predefined color space names.
2801
+ // colorjs uses internal short names that differ from CSS predefined color space identifiers.
2648
2802
  case "p3":
2649
2803
  return `color(display-p3 ${c1} ${c2} ${c3} / ${alpha})`;
2650
2804
  case "a98rgb":
@@ -3687,10 +3841,6 @@ var commonPageFields = {
3687
3841
  )
3688
3842
  };
3689
3843
  var HomePagePath = z.string().refine((path) => path === "", "Home page path must be empty");
3690
- var HomePage = z.object({
3691
- ...commonPageFields,
3692
- path: HomePagePath
3693
- });
3694
3844
  var DefaultPagePage = z.string().refine((path) => path !== "", "Can't be empty").refine((path) => path !== "/", "Can't be just a /").refine((path) => path.endsWith("/") === false, "Can't end with a /").refine((path) => path.includes("//") === false, "Can't contain repeating /").refine(
3695
3845
  (path) => /^[-_a-z0-9*:?\\/.]*$/.test(path),
3696
3846
  "Only a-z, 0-9, -, _, /, :, ?, . and * are allowed"
@@ -3723,7 +3873,7 @@ var PagePath = DefaultPagePage.refine(
3723
3873
  );
3724
3874
  var Page = z.object({
3725
3875
  ...commonPageFields,
3726
- path: PagePath
3876
+ path: z.union([HomePagePath, PagePath])
3727
3877
  });
3728
3878
  var ProjectMeta = z.object({
3729
3879
  // All fields are optional to ensure consistency and allow for the addition of new fields without requiring migration
@@ -3749,13 +3899,127 @@ var CompilerSettings = z.object({
3749
3899
  // All fields are optional to ensure consistency and allow for the addition of new fields without requiring migration
3750
3900
  atomicStyles: z.boolean().optional()
3751
3901
  });
3752
- z.object({
3902
+ var Pages = z.object({
3753
3903
  meta: ProjectMeta.optional(),
3754
3904
  compiler: CompilerSettings.optional(),
3755
3905
  redirects: z.array(PageRedirect).optional(),
3756
- homePage: HomePage,
3757
- pages: z.array(Page),
3758
- folders: z.array(Folder).refine((folders) => folders.length > 0, "Folders can't be empty")
3906
+ homePageId: PageId,
3907
+ rootFolderId: FolderId,
3908
+ pages: z.map(PageId, Page),
3909
+ folders: z.map(FolderId, Folder).refine((folders) => folders.size > 0, "Folders can't be empty")
3910
+ }).superRefine((pages, context) => {
3911
+ const homePage = pages.pages.get(pages.homePageId);
3912
+ const rootFolder = pages.folders.get(pages.rootFolderId);
3913
+ if (homePage === void 0) {
3914
+ context.addIssue({
3915
+ code: z.ZodIssueCode.custom,
3916
+ path: ["homePageId"],
3917
+ message: "Home page must reference an existing page"
3918
+ });
3919
+ }
3920
+ if (rootFolder === void 0) {
3921
+ context.addIssue({
3922
+ code: z.ZodIssueCode.custom,
3923
+ path: ["rootFolderId"],
3924
+ message: "Root folder must reference an existing folder"
3925
+ });
3926
+ }
3927
+ if (homePage !== void 0 && homePage.path !== "") {
3928
+ context.addIssue({
3929
+ code: z.ZodIssueCode.custom,
3930
+ path: ["pages", pages.homePageId, "path"],
3931
+ message: "Home page path must be empty"
3932
+ });
3933
+ }
3934
+ for (const [pageId, page] of pages.pages) {
3935
+ if (page.id !== pageId) {
3936
+ context.addIssue({
3937
+ code: z.ZodIssueCode.custom,
3938
+ path: ["pages", pageId, "id"],
3939
+ message: "Page id must match its record key"
3940
+ });
3941
+ }
3942
+ if (pageId !== pages.homePageId && page.path === "") {
3943
+ context.addIssue({
3944
+ code: z.ZodIssueCode.custom,
3945
+ path: ["pages", pageId, "path"],
3946
+ message: "Page path can't be empty"
3947
+ });
3948
+ }
3949
+ }
3950
+ for (const [folderId, folder] of pages.folders) {
3951
+ if (folder.id !== folderId) {
3952
+ context.addIssue({
3953
+ code: z.ZodIssueCode.custom,
3954
+ path: ["folders", folderId, "id"],
3955
+ message: "Folder id must match its record key"
3956
+ });
3957
+ }
3958
+ for (const [index, childId] of folder.children.entries()) {
3959
+ if (pages.pages.has(childId) === false && pages.folders.has(childId) === false) {
3960
+ context.addIssue({
3961
+ code: z.ZodIssueCode.custom,
3962
+ path: ["folders", folderId, "children", index],
3963
+ message: "Folder child must reference an existing page or folder"
3964
+ });
3965
+ }
3966
+ if (childId === pages.rootFolderId) {
3967
+ context.addIssue({
3968
+ code: z.ZodIssueCode.custom,
3969
+ path: ["folders", folderId, "children", index],
3970
+ message: "Root folder can't be nested"
3971
+ });
3972
+ }
3973
+ }
3974
+ }
3975
+ if (rootFolder !== void 0 && rootFolder.children[0] !== pages.homePageId) {
3976
+ context.addIssue({
3977
+ code: z.ZodIssueCode.custom,
3978
+ path: ["folders", pages.rootFolderId, "children"],
3979
+ message: "Root folder must start with the home page"
3980
+ });
3981
+ }
3982
+ const childParents = /* @__PURE__ */ new Map();
3983
+ for (const [folderId, folder] of pages.folders) {
3984
+ for (const [index, childId] of folder.children.entries()) {
3985
+ const parentId = childParents.get(childId);
3986
+ if (parentId !== void 0) {
3987
+ context.addIssue({
3988
+ code: z.ZodIssueCode.custom,
3989
+ path: ["folders", folderId, "children", index],
3990
+ message: `Child is already registered in folder "${parentId}"`
3991
+ });
3992
+ continue;
3993
+ }
3994
+ childParents.set(childId, folderId);
3995
+ }
3996
+ }
3997
+ const hasFolderCycle = (folderId, path = /* @__PURE__ */ new Set()) => {
3998
+ if (path.has(folderId)) {
3999
+ return true;
4000
+ }
4001
+ const folder = pages.folders.get(folderId);
4002
+ if (folder === void 0) {
4003
+ return false;
4004
+ }
4005
+ path.add(folderId);
4006
+ for (const childId of folder.children) {
4007
+ if (pages.folders.has(childId) && hasFolderCycle(childId, path)) {
4008
+ return true;
4009
+ }
4010
+ }
4011
+ path.delete(folderId);
4012
+ return false;
4013
+ };
4014
+ for (const folderId of pages.folders.keys()) {
4015
+ if (hasFolderCycle(folderId)) {
4016
+ context.addIssue({
4017
+ code: z.ZodIssueCode.custom,
4018
+ path: ["folders", folderId, "children"],
4019
+ message: "Folders can't contain cycles"
4020
+ });
4021
+ }
4022
+ }
3759
4023
  });
3760
4024
  var TextChild = z.object({
3761
4025
  type: z.literal("text"),
@@ -4241,7 +4505,8 @@ z.object({
4241
4505
  var common = {
4242
4506
  label: z.string().optional(),
4243
4507
  description: z.string().optional(),
4244
- required: z.boolean()
4508
+ required: z.boolean(),
4509
+ contentMode: z.boolean().optional()
4245
4510
  };
4246
4511
  var Tag = z.object({
4247
4512
  ...common,
@@ -5461,18 +5726,36 @@ var executeExpression = (expression) => {
5461
5726
  };
5462
5727
  var tokenRegex = new RegExp(":(?<name>\\w+)(?<modifier>[?*]?)|(?<wildcard>(?<!:\\w+)\\*)");
5463
5728
  var isPathnamePattern = (pathname) => tokenRegex.test(pathname);
5729
+ var ROOT_FOLDER_ID = "root";
5730
+ var isRootFolder = ({ id }) => id === ROOT_FOLDER_ID;
5731
+ var getPageById = (pages, pageId) => {
5732
+ return pages.pages.get(pageId);
5733
+ };
5734
+ var getAllPages = (pages) => {
5735
+ return Array.from(pages.pages.values());
5736
+ };
5737
+ var getAllFolders = (pages) => {
5738
+ return Array.from(pages.folders.values());
5739
+ };
5740
+ var getHomePage = (pages) => {
5741
+ const homePage = getPageById(pages, pages.homePageId);
5742
+ if (homePage === void 0) {
5743
+ throw new Error(`Home page "${pages.homePageId}" was not found.`);
5744
+ }
5745
+ return homePage;
5746
+ };
5464
5747
  var findPageByIdOrPath = (idOrPath, pages) => {
5465
- if (idOrPath === "" || idOrPath === "/" || idOrPath === pages.homePage.id) {
5466
- return pages.homePage;
5748
+ if (idOrPath === "" || idOrPath === "/" || idOrPath === pages.homePageId) {
5749
+ return getHomePage(pages);
5467
5750
  }
5468
- return pages.pages.find(
5751
+ return getAllPages(pages).find(
5469
5752
  (page) => page.id === idOrPath || getPagePath(page.id, pages) === idOrPath
5470
5753
  );
5471
5754
  };
5472
5755
  var getPagePath = (id, pages) => {
5473
5756
  const foldersMap = /* @__PURE__ */ new Map();
5474
5757
  const childParentMap = /* @__PURE__ */ new Map();
5475
- for (const folder of pages.folders) {
5758
+ for (const folder of getAllFolders(pages)) {
5476
5759
  foldersMap.set(folder.id, folder);
5477
5760
  for (const childId of folder.children) {
5478
5761
  childParentMap.set(childId, folder.id);
@@ -5480,7 +5763,7 @@ var getPagePath = (id, pages) => {
5480
5763
  }
5481
5764
  const paths = [];
5482
5765
  let currentId = id;
5483
- const allPages = [pages.homePage, ...pages.pages];
5766
+ const allPages = getAllPages(pages);
5484
5767
  for (const page of allPages) {
5485
5768
  if (page.id === id) {
5486
5769
  paths.push(page.path);
@@ -5499,7 +5782,7 @@ var getPagePath = (id, pages) => {
5499
5782
  return paths.reverse().join("/").replace(/\/+/g, "/");
5500
5783
  };
5501
5784
  var getStaticSiteMapXml = (pages, updatedAt) => {
5502
- const allPages = [pages.homePage, ...pages.pages];
5785
+ const allPages = getAllPages(pages);
5503
5786
  return allPages.filter((page) => (page.meta.documentType ?? "html") === "html").filter(
5504
5787
  (page) => executeExpression(page.meta.excludePageFromSearch) !== true
5505
5788
  ).filter((page) => false === isPathnamePattern(page.path)).map((page) => ({
@@ -6781,6 +7064,129 @@ var generateWebstudioComponent = ({
6781
7064
  `;
6782
7065
  return generatedComponent;
6783
7066
  };
7067
+ var toMap = (items, normalizeItem = (item) => item) => {
7068
+ if (items instanceof Map) {
7069
+ return new Map(
7070
+ Array.from(items, ([id, item]) => [id, normalizeItem(item)])
7071
+ );
7072
+ }
7073
+ const list = Array.isArray(items) ? items : Object.values(items);
7074
+ return new Map(list.map((item) => [item.id, normalizeItem(item)]));
7075
+ };
7076
+ var normalizePage = (page) => ({
7077
+ ...page,
7078
+ meta: page.meta ?? {}
7079
+ });
7080
+ var isLegacyPages = (pages) => {
7081
+ if (typeof pages !== "object" || pages === null) {
7082
+ return false;
7083
+ }
7084
+ return "homePage" in pages && Array.isArray(pages.pages);
7085
+ };
7086
+ var isSerializedPages = (pages) => {
7087
+ if (typeof pages !== "object" || pages === null) {
7088
+ return false;
7089
+ }
7090
+ const candidate = pages;
7091
+ return typeof candidate.homePageId === "string" && typeof candidate.rootFolderId === "string" && candidate.pages !== void 0 && candidate.folders !== void 0;
7092
+ };
7093
+ var removeOrphanFolderChildren = (pages, folders) => {
7094
+ const nextFolders = /* @__PURE__ */ new Map();
7095
+ for (const [folderId, folder] of folders) {
7096
+ nextFolders.set(folderId, {
7097
+ ...folder,
7098
+ children: folder.children.filter(
7099
+ (childId) => pages.has(childId) || folders.has(childId)
7100
+ )
7101
+ });
7102
+ }
7103
+ return nextFolders;
7104
+ };
7105
+ var migratePages = (pages) => {
7106
+ var _a2;
7107
+ if (isSerializedPages(pages) && pages.pages instanceof Map && pages.folders instanceof Map) {
7108
+ const currentPages = pages;
7109
+ const result = Pages.safeParse(currentPages);
7110
+ if (result.success) {
7111
+ return currentPages;
7112
+ }
7113
+ return {
7114
+ ...currentPages,
7115
+ folders: removeOrphanFolderChildren(
7116
+ currentPages.pages,
7117
+ currentPages.folders
7118
+ )
7119
+ };
7120
+ }
7121
+ if (isSerializedPages(pages)) {
7122
+ const nextPages2 = toMap(pages.pages, normalizePage);
7123
+ const nextFolders2 = toMap(pages.folders);
7124
+ return {
7125
+ meta: pages.meta,
7126
+ compiler: pages.compiler,
7127
+ redirects: pages.redirects,
7128
+ homePageId: pages.homePageId,
7129
+ rootFolderId: pages.rootFolderId,
7130
+ pages: nextPages2,
7131
+ folders: removeOrphanFolderChildren(nextPages2, nextFolders2)
7132
+ };
7133
+ }
7134
+ if (isLegacyPages(pages) === false) {
7135
+ throw new Error("Pages data has unsupported shape.");
7136
+ }
7137
+ const homePage = {
7138
+ ...normalizePage(pages.homePage),
7139
+ path: ""
7140
+ };
7141
+ const nextPages = /* @__PURE__ */ new Map([[homePage.id, homePage]]);
7142
+ for (const page of pages.pages) {
7143
+ if (page.id === homePage.id) {
7144
+ continue;
7145
+ }
7146
+ nextPages.set(page.id, normalizePage(page));
7147
+ }
7148
+ const nextFolders = /* @__PURE__ */ new Map();
7149
+ for (const folder of pages.folders ?? []) {
7150
+ nextFolders.set(folder.id, { ...folder, children: [...folder.children] });
7151
+ }
7152
+ const rootFolder = Array.from(nextFolders.values()).find(isRootFolder) ?? ((_a2 = pages.folders) == null ? void 0 : _a2[0]) ?? {
7153
+ id: ROOT_FOLDER_ID,
7154
+ name: "Root",
7155
+ slug: "",
7156
+ children: []
7157
+ };
7158
+ if (nextFolders.has(rootFolder.id) === false) {
7159
+ nextFolders.set(rootFolder.id, { ...rootFolder, children: [] });
7160
+ }
7161
+ const nextRootFolder = nextFolders.get(rootFolder.id);
7162
+ if (nextRootFolder === void 0) {
7163
+ throw new Error("Pages must include a root folder.");
7164
+ }
7165
+ for (const folder of nextFolders.values()) {
7166
+ folder.children = folder.children.filter(
7167
+ (childId) => childId !== homePage.id && (nextPages.has(childId) || nextFolders.has(childId))
7168
+ );
7169
+ }
7170
+ nextRootFolder.children.unshift(homePage.id);
7171
+ const referencedIds = new Set(
7172
+ Array.from(nextFolders.values()).flatMap((folder) => folder.children)
7173
+ );
7174
+ for (const page of pages.pages) {
7175
+ if (page.id !== homePage.id && referencedIds.has(page.id) === false) {
7176
+ nextRootFolder.children.push(page.id);
7177
+ referencedIds.add(page.id);
7178
+ }
7179
+ }
7180
+ return {
7181
+ meta: pages.meta,
7182
+ compiler: pages.compiler,
7183
+ redirects: pages.redirects,
7184
+ homePageId: homePage.id,
7185
+ rootFolderId: rootFolder.id,
7186
+ pages: nextPages,
7187
+ folders: nextFolders
7188
+ };
7189
+ };
6784
7190
  const BOOLEAN_ATTRIBUTES = /* @__PURE__ */ new Set([
6785
7191
  "async",
6786
7192
  "autofocus",
@@ -6892,7 +7298,7 @@ const htmlToJsx = (html2) => {
6892
7298
  }
6893
7299
  return result;
6894
7300
  };
6895
- const o$H = {
7301
+ const o$G = {
6896
7302
  category: "general",
6897
7303
  description: "Slot is a container for content that you want to reference across the project. Changes made to a Slot's children will be reflected in all other instances of that Slot.",
6898
7304
  icon: SlotComponentIcon,
@@ -6984,7 +7390,8 @@ const a$5 = {
6984
7390
  required: true,
6985
7391
  control: "code",
6986
7392
  language: "markdown",
6987
- type: "string"
7393
+ type: "string",
7394
+ contentMode: true
6988
7395
  }
6989
7396
  }
6990
7397
  };
@@ -7190,11 +7597,11 @@ var button = [
7190
7597
  { property: "text-transform", value: { type: "keyword", value: "none" } }
7191
7598
  ];
7192
7599
  var select = button;
7193
- const o$G = {};
7600
+ const o$F = {};
7194
7601
  const i$9 = {
7195
7602
  presetStyle: { body },
7196
7603
  initialProps: ["id", "class"],
7197
- props: o$G
7604
+ props: o$F
7198
7605
  };
7199
7606
  const t$g = {
7200
7607
  tag: { required: false, control: "text", type: "string" }
@@ -7283,11 +7690,11 @@ const m$8 = {
7283
7690
  }
7284
7691
  }
7285
7692
  };
7286
- const o$F = {};
7693
+ const o$E = {};
7287
7694
  const i$8 = {
7288
7695
  presetStyle: { p: p$8 },
7289
7696
  initialProps: ["id", "class"],
7290
- props: o$F
7697
+ props: o$E
7291
7698
  };
7292
7699
  const e$t = {
7293
7700
  download: {
@@ -7330,54 +7737,55 @@ const t$d = {
7330
7737
  href: {
7331
7738
  type: "string",
7332
7739
  control: "url",
7333
- required: false
7740
+ required: false,
7741
+ contentMode: true
7334
7742
  }
7335
7743
  }
7336
7744
  };
7337
7745
  const a$3 = p$7;
7338
- const o$E = {};
7746
+ const o$D = {};
7339
7747
  const e$s = {
7340
7748
  label: "Text",
7341
7749
  icon: PaintBrushIcon,
7342
7750
  presetStyle: { span },
7343
7751
  initialProps: ["id", "class"],
7344
- props: o$E
7752
+ props: o$D
7345
7753
  };
7346
- const o$D = {};
7754
+ const o$C = {};
7347
7755
  const p$6 = {
7348
7756
  label: "Bold Text",
7349
7757
  presetStyle: { b: b$4 },
7350
7758
  initialProps: ["id", "class"],
7351
- props: o$D
7759
+ props: o$C
7352
7760
  };
7353
- const o$C = {};
7761
+ const o$B = {};
7354
7762
  const e$r = {
7355
7763
  label: "Italic Text",
7356
7764
  presetStyle: { i: i$a },
7357
7765
  initialProps: ["id", "class"],
7358
- props: o$C
7766
+ props: o$B
7359
7767
  };
7360
- const o$B = {};
7361
- const o$A = {
7768
+ const o$A = {};
7769
+ const o$z = {
7362
7770
  label: "Superscript Text",
7363
7771
  presetStyle: { sup },
7364
7772
  initialProps: ["id", "class"],
7365
- props: o$B
7773
+ props: o$A
7366
7774
  };
7367
- const o$z = {};
7368
- const s$4 = {
7775
+ const o$y = {};
7776
+ const s$3 = {
7369
7777
  label: "Subscript Text",
7370
7778
  presetStyle: { sub },
7371
7779
  initialProps: ["id", "class"],
7372
- props: o$z
7780
+ props: o$y
7373
7781
  };
7374
- const o$y = {};
7782
+ const o$x = {};
7375
7783
  const e$q = {
7376
7784
  presetStyle: { button },
7377
7785
  initialProps: ["id", "class", "type", "aria-label"],
7378
- props: o$y
7786
+ props: o$x
7379
7787
  };
7380
- const o$x = {};
7788
+ const o$w = {};
7381
7789
  const p$5 = {
7382
7790
  input: [
7383
7791
  ...input,
@@ -7386,7 +7794,7 @@ const p$5 = {
7386
7794
  value: { type: "keyword", value: "block" }
7387
7795
  }
7388
7796
  ]
7389
- }, l$5 = {
7797
+ }, l$6 = {
7390
7798
  label: "Text Input",
7391
7799
  presetStyle: p$5,
7392
7800
  initialProps: [
@@ -7399,7 +7807,7 @@ const p$5 = {
7399
7807
  "required",
7400
7808
  "autofocus"
7401
7809
  ],
7402
- props: o$x
7810
+ props: o$w
7403
7811
  };
7404
7812
  const e$p = {
7405
7813
  state: {
@@ -7432,17 +7840,17 @@ const c$4 = {
7432
7840
  }
7433
7841
  }
7434
7842
  };
7435
- const o$w = {};
7436
- const r$c = {
7843
+ const o$v = {};
7844
+ const r$d = {
7437
7845
  form: [
7438
7846
  ...form,
7439
7847
  { property: "min-height", value: { type: "unit", unit: "px", value: 20 } }
7440
7848
  ]
7441
7849
  }, p$4 = {
7442
7850
  label: "Form",
7443
- presetStyle: r$c,
7851
+ presetStyle: r$d,
7444
7852
  initialProps: ["id", "class", "action"],
7445
- props: o$w
7853
+ props: o$v
7446
7854
  };
7447
7855
  const e$o = {
7448
7856
  optimize: {
@@ -7454,7 +7862,7 @@ const e$o = {
7454
7862
  },
7455
7863
  quality: { required: false, control: "number", type: "number" }
7456
7864
  };
7457
- const o$v = {
7865
+ const r$c = {
7458
7866
  img: [
7459
7867
  ...img,
7460
7868
  // Otherwise on new image insert onto canvas it can overfit screen size multiple times
@@ -7479,7 +7887,7 @@ const o$v = {
7479
7887
  }, i$7 = {
7480
7888
  category: "media",
7481
7889
  description: "Add an image asset to the page. Webstudio automatically converts images to WebP or AVIF format and makes them responsive for best performance.",
7482
- presetStyle: o$v,
7890
+ presetStyle: r$c,
7483
7891
  order: 0,
7484
7892
  initialProps: [
7485
7893
  "id",
@@ -7499,7 +7907,26 @@ const o$v = {
7499
7907
  control: "file",
7500
7908
  label: "Source",
7501
7909
  required: false,
7502
- accept: "image/*"
7910
+ accept: "image/*",
7911
+ contentMode: true
7912
+ },
7913
+ width: {
7914
+ type: "number",
7915
+ control: "number",
7916
+ required: false,
7917
+ contentMode: true
7918
+ },
7919
+ height: {
7920
+ type: "number",
7921
+ control: "number",
7922
+ required: false,
7923
+ contentMode: true
7924
+ },
7925
+ alt: {
7926
+ type: "string",
7927
+ control: "text",
7928
+ required: false,
7929
+ contentMode: true
7503
7930
  }
7504
7931
  }
7505
7932
  };
@@ -7596,7 +8023,7 @@ const t$c = {
7596
8023
  value: { type: "keyword", value: "40px" }
7597
8024
  }
7598
8025
  ]
7599
- }, l$4 = {
8026
+ }, l$5 = {
7600
8027
  presetStyle: t$c,
7601
8028
  initialProps: ["id", "class", "ordered", "start", "reversed"],
7602
8029
  props: e$m
@@ -7712,7 +8139,7 @@ const t$a = {
7712
8139
  value: { type: "keyword", value: "block" }
7713
8140
  }
7714
8141
  ]
7715
- }, l$3 = {
8142
+ }, l$4 = {
7716
8143
  label: "Text Area",
7717
8144
  presetStyle: t$a,
7718
8145
  contentModel: {
@@ -7960,7 +8387,7 @@ const i$6 = [
7960
8387
  "showControls",
7961
8388
  "controlsColor",
7962
8389
  "playsinline"
7963
- ], s$3 = {
8390
+ ], s$2 = {
7964
8391
  icon: VimeoIcon,
7965
8392
  contentModel: {
7966
8393
  category: "instance",
@@ -7969,7 +8396,13 @@ const i$6 = [
7969
8396
  },
7970
8397
  presetStyle: { div },
7971
8398
  initialProps: i$6,
7972
- props: e$i
8399
+ props: {
8400
+ ...e$i,
8401
+ url: {
8402
+ ...e$i.url,
8403
+ contentMode: true
8404
+ }
8405
+ }
7973
8406
  };
7974
8407
  const e$h = {
7975
8408
  allowFullscreen: {
@@ -8136,7 +8569,7 @@ https://support.google.com/youtube/answer/171780?hl=en#zippy=%2Cturn-on-privacy-
8136
8569
  type: "string"
8137
8570
  }
8138
8571
  };
8139
- const n$5 = [
8572
+ const i$5 = [
8140
8573
  "id",
8141
8574
  "className",
8142
8575
  "url",
@@ -8164,7 +8597,7 @@ const n$5 = [
8164
8597
  "language",
8165
8598
  "color",
8166
8599
  "playlist"
8167
- ], s$2 = {
8600
+ ], l$3 = {
8168
8601
  icon: YoutubeIcon,
8169
8602
  contentModel: {
8170
8603
  category: "instance",
@@ -8172,8 +8605,14 @@ const n$5 = [
8172
8605
  descendants: ["VimeoSpinner", "VimeoPlayButton", "VimeoPreviewImage"]
8173
8606
  },
8174
8607
  presetStyle: { div },
8175
- initialProps: n$5,
8176
- props: e$h
8608
+ initialProps: i$5,
8609
+ props: {
8610
+ ...e$h,
8611
+ url: {
8612
+ ...e$h.url,
8613
+ contentMode: true
8614
+ }
8615
+ }
8177
8616
  };
8178
8617
  const e$g = {
8179
8618
  optimize: {
@@ -8184,7 +8623,7 @@ const e$g = {
8184
8623
  },
8185
8624
  quality: { required: false, control: "number", type: "number" }
8186
8625
  };
8187
- const i$5 = {
8626
+ const i$4 = {
8188
8627
  ...i$7,
8189
8628
  category: "hidden",
8190
8629
  label: "Preview Image",
@@ -8200,7 +8639,8 @@ const i$5 = {
8200
8639
  type: "string",
8201
8640
  control: "file",
8202
8641
  label: "Source",
8203
- required: false
8642
+ required: false,
8643
+ contentMode: true
8204
8644
  }
8205
8645
  }
8206
8646
  };
@@ -8620,7 +9060,35 @@ const r$9 = {
8620
9060
  "timeStyle",
8621
9061
  "format"
8622
9062
  ],
8623
- props: e$d
9063
+ props: {
9064
+ ...e$d,
9065
+ datetime: {
9066
+ type: "string",
9067
+ control: "text",
9068
+ required: false,
9069
+ contentMode: true
9070
+ },
9071
+ language: {
9072
+ ...e$d.language,
9073
+ contentMode: true
9074
+ },
9075
+ country: {
9076
+ ...e$d.country,
9077
+ contentMode: true
9078
+ },
9079
+ dateStyle: {
9080
+ ...e$d.dateStyle,
9081
+ contentMode: true
9082
+ },
9083
+ timeStyle: {
9084
+ ...e$d.timeStyle,
9085
+ contentMode: true
9086
+ },
9087
+ format: {
9088
+ ...e$d.format,
9089
+ contentMode: true
9090
+ }
9091
+ }
8624
9092
  };
8625
9093
  const o$k = {};
8626
9094
  const o$j = {
@@ -8716,7 +9184,7 @@ const r$7 = {
8716
9184
  props: o$e
8717
9185
  };
8718
9186
  const o$d = {};
8719
- const i$4 = {
9187
+ const n$5 = {
8720
9188
  icon: VideoIcon,
8721
9189
  contentModel: {
8722
9190
  category: "instance",
@@ -8751,7 +9219,20 @@ const i$4 = {
8751
9219
  control: "file",
8752
9220
  label: "Source",
8753
9221
  required: false,
8754
- accept: ".mp4,.webm,.mpg,.mpeg,.mov"
9222
+ accept: ".mp4,.webm,.mpg,.mpeg,.mov",
9223
+ contentMode: true
9224
+ },
9225
+ width: {
9226
+ type: "number",
9227
+ control: "number",
9228
+ required: false,
9229
+ contentMode: true
9230
+ },
9231
+ height: {
9232
+ type: "number",
9233
+ control: "number",
9234
+ required: false,
9235
+ contentMode: true
8755
9236
  }
8756
9237
  }
8757
9238
  };
@@ -8773,11 +9254,11 @@ const baseComponentMetas = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.
8773
9254
  Heading: m$8,
8774
9255
  HtmlEmbed: a$6,
8775
9256
  Image: i$7,
8776
- Input: l$5,
9257
+ Input: l$6,
8777
9258
  Italic: e$r,
8778
9259
  Label: t$b,
8779
9260
  Link: p$7,
8780
- List: l$4,
9261
+ List: l$5,
8781
9262
  ListItem: p$3,
8782
9263
  MarkdownEmbed: a$5,
8783
9264
  Option: a$1,
@@ -8787,21 +9268,21 @@ const baseComponentMetas = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.
8787
9268
  RichTextLink: a$3,
8788
9269
  Select: p$2,
8789
9270
  Separator: y$2,
8790
- Slot: o$H,
9271
+ Slot: o$G,
8791
9272
  Span: e$s,
8792
- Subscript: s$4,
8793
- Superscript: o$A,
9273
+ Subscript: s$3,
9274
+ Superscript: o$z,
8794
9275
  Text: n$6,
8795
- Textarea: l$3,
9276
+ Textarea: l$4,
8796
9277
  Time: r$9,
8797
- Video: i$4,
8798
- Vimeo: s$3,
9278
+ Video: n$5,
9279
+ Vimeo: s$2,
8799
9280
  VimeoPlayButton: c$2,
8800
- VimeoPreviewImage: i$5,
9281
+ VimeoPreviewImage: i$4,
8801
9282
  VimeoSpinner: c$1,
8802
9283
  XmlNode: m$5,
8803
9284
  XmlTime: a$2,
8804
- YouTube: s$2
9285
+ YouTube: l$3
8805
9286
  }, Symbol.toStringTag, { value: "Module" }));
8806
9287
  //! SPDX-License-Identifier: LicenseRef-Webstudio,Inc-Proprietary
8807
9288
  const l$1 = (n2) => new Proxy({}, { get(w2, t2) {
@@ -10381,7 +10862,7 @@ audit=false
10381
10862
  fund=false
10382
10863
  `;
10383
10864
  const prebuild = async (options) => {
10384
- var _a2, _b2, _c2, _d2, _e2, _f2, _g2, _h2, _i2, _j, _k;
10865
+ var _a2, _b2, _c2, _d2, _e2, _f2, _g2, _h2, _i2, _j;
10385
10866
  if (options.template.length === 0) {
10386
10867
  log.error(
10387
10868
  `Template is not provided
@@ -10430,6 +10911,8 @@ Please check webstudio --help for more details`
10430
10911
  const usedMetas = new Map(
10431
10912
  Object.entries(coreMetas)
10432
10913
  );
10914
+ const pages = migratePages(siteData.build.pages);
10915
+ const allPages = getAllPages(pages);
10433
10916
  const siteDataByPage = {};
10434
10917
  const fontAssetsByPage = {};
10435
10918
  const backgroundImageAssetsByPage = {};
@@ -10438,10 +10921,10 @@ Please check webstudio --help for more details`
10438
10921
  assetBaseUrl,
10439
10922
  assets: new Map(siteData.assets.map((asset) => [asset.id, asset])),
10440
10923
  uploadingImageAssets: [],
10441
- pages: siteData.build.pages,
10924
+ pages,
10442
10925
  source: "prebuild"
10443
10926
  });
10444
- for (const page of Object.values(siteData.pages)) {
10927
+ for (const page of allPages) {
10445
10928
  const instanceMap = new Map(siteData.build.instances);
10446
10929
  const pageInstanceSet = findTreeInstanceIds(
10447
10930
  instanceMap,
@@ -10490,7 +10973,7 @@ Please check webstudio --help for more details`
10490
10973
  dataSources,
10491
10974
  resources
10492
10975
  },
10493
- pages: siteData.pages,
10976
+ pages: allPages,
10494
10977
  page,
10495
10978
  assets: siteData.assets
10496
10979
  };
@@ -10547,10 +11030,10 @@ Please check webstudio --help for more details`
10547
11030
  // pass only used metas to not generate unused preset styles
10548
11031
  componentMetas: usedMetas,
10549
11032
  assetBaseUrl,
10550
- atomic: ((_g2 = siteData.build.pages.compiler) == null ? void 0 : _g2.atomicStyles) ?? true
11033
+ atomic: ((_g2 = pages.compiler) == null ? void 0 : _g2.atomicStyles) ?? true
10551
11034
  });
10552
11035
  await createFileIfNotExists(join(generatedDir, "index.css"), cssText);
10553
- for (const page of Object.values(siteData.pages)) {
11036
+ for (const page of allPages) {
10554
11037
  const scope = createScope([
10555
11038
  // manually maintained list of occupied identifiers
10556
11039
  "useState",
@@ -10644,13 +11127,13 @@ Please check webstudio --help for more details`
10644
11127
  metas: usedMetas,
10645
11128
  tagsOverrides: framework.tags
10646
11129
  });
10647
- const projectMeta = siteData.build.pages.meta;
11130
+ const projectMeta = pages.meta;
10648
11131
  const contactEmail = (
10649
11132
  // fallback to user email when contact email is empty string
10650
11133
  (projectMeta == null ? void 0 : projectMeta.contactEmail) || ((_i2 = siteData.user) == null ? void 0 : _i2.email) || void 0
10651
11134
  );
10652
11135
  const favIconAsset = (_j = assets.get((projectMeta == null ? void 0 : projectMeta.faviconAssetId) ?? "")) == null ? void 0 : _j.name;
10653
- const pagePath = getPagePath(page.id, siteData.build.pages);
11136
+ const pagePath = getPagePath(page.id, pages);
10654
11137
  const breakpoints = siteData.build.breakpoints.map(([_, value]) => ({
10655
11138
  id: value.id,
10656
11139
  minWidth: value.minWidth,
@@ -10770,7 +11253,7 @@ Please check webstudio --help for more details`
10770
11253
  join(generatedDir, "$resources.sitemap.xml.ts"),
10771
11254
  `
10772
11255
  export const sitemap = ${JSON.stringify(
10773
- getStaticSiteMapXml(siteData.build.pages, siteData.build.updatedAt),
11256
+ getStaticSiteMapXml(pages, siteData.build.updatedAt),
10774
11257
  null,
10775
11258
  2
10776
11259
  )};
@@ -10788,7 +11271,7 @@ Please check webstudio --help for more details`
10788
11271
  export const assets = ${JSON.stringify(assetsById, null, 2)};
10789
11272
  `
10790
11273
  );
10791
- const redirects = (_k = siteData.build.pages) == null ? void 0 : _k.redirects;
11274
+ const redirects = pages.redirects;
10792
11275
  if (redirects !== void 0 && redirects.length > 0) {
10793
11276
  for (const redirect of redirects) {
10794
11277
  const generatedBasename = generateRemixRoute(redirect.old);
@@ -10962,36 +11445,6 @@ const getDeploymentInstructions = (deployTarget) => {
10962
11445
  ].join("\n");
10963
11446
  }
10964
11447
  };
10965
- const name = "webstudio";
10966
- const version = "0.264.0";
10967
- const description = "Webstudio CLI";
10968
- const author = "Webstudio <github@webstudio.is>";
10969
- const homepage = "https://webstudio.is";
10970
- const type = "module";
10971
- const bin = { "webstudio-cli": "./bin.js", "webstudio": "./bin.js" };
10972
- const imports = { "#cli": { "webstudio": "./src/cli.ts", "default": "./lib/cli.js" } };
10973
- const files = ["lib/*", "templates/*", "bin.js", "!*.{test,stories}.*"];
10974
- const scripts = { "typecheck": "tsgo --noEmit", "build": "rm -rf lib && vite build", "test": "vitest run" };
10975
- const license = "AGPL-3.0-or-later";
10976
- const engines = { "node": ">=22" };
10977
- const dependencies = { "@clack/prompts": "^0.10.0", "@emotion/hash": "^0.9.2", "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" };
10978
- 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" };
10979
- const packageJson = {
10980
- name,
10981
- version,
10982
- description,
10983
- author,
10984
- homepage,
10985
- type,
10986
- bin,
10987
- imports,
10988
- files,
10989
- scripts,
10990
- license,
10991
- engines,
10992
- dependencies,
10993
- devDependencies
10994
- };
10995
11448
  const main = async () => {
10996
11449
  try {
10997
11450
  await createFileIfNotExists(GLOBAL_CONFIG_FILE, "{}");
@@ -11029,6 +11482,9 @@ const main = async () => {
11029
11482
  cmd.command(["$0", "init"], "Setup the project", buildOptions, initFlow);
11030
11483
  await cmd.parse();
11031
11484
  } catch (error) {
11485
+ if (isHandledCliError(error)) {
11486
+ exit(1);
11487
+ }
11032
11488
  console.error(error);
11033
11489
  exit(1);
11034
11490
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webstudio",
3
- "version": "0.264.0",
3
+ "version": "0.266.0",
4
4
  "description": "Webstudio CLI",
5
5
  "author": "Webstudio <github@webstudio.is>",
6
6
  "homepage": "https://webstudio.is",
@@ -28,6 +28,7 @@
28
28
  "dependencies": {
29
29
  "@clack/prompts": "^0.10.0",
30
30
  "@emotion/hash": "^0.9.2",
31
+ "@trpc/client": "^10.45.2",
31
32
  "acorn": "^8.14.1",
32
33
  "acorn-walk": "^8.3.4",
33
34
  "change-case": "^5.4.4",
@@ -41,7 +42,9 @@
41
42
  "tinyexec": "^0.3.2",
42
43
  "warn-once": "^0.1.1",
43
44
  "yargs": "^17.7.2",
44
- "zod": "^3.24.2"
45
+ "zod": "^3.24.2",
46
+ "@webstudio-is/project-migrations": "0.266.0",
47
+ "@webstudio-is/trpc-interface": "0.266.0"
45
48
  },
46
49
  "devDependencies": {
47
50
  "@cloudflare/vite-plugin": "^1.1.0",
@@ -71,16 +74,16 @@
71
74
  "vite": "^6.3.4",
72
75
  "vitest": "^3.1.2",
73
76
  "wrangler": "^3.63.2",
74
- "@webstudio-is/css-engine": "0.264.0",
75
- "@webstudio-is/http-client": "0.264.0",
76
- "@webstudio-is/react-sdk": "0.264.0",
77
- "@webstudio-is/sdk": "0.264.0",
78
- "@webstudio-is/image": "0.264.0",
79
- "@webstudio-is/sdk-components-animation": "0.264.0",
80
- "@webstudio-is/sdk-components-react": "0.264.0",
81
- "@webstudio-is/sdk-components-react-radix": "0.264.0",
82
- "@webstudio-is/sdk-components-react-remix": "0.264.0",
83
- "@webstudio-is/sdk-components-react-router": "0.264.0",
77
+ "@webstudio-is/css-engine": "0.266.0",
78
+ "@webstudio-is/http-client": "0.266.0",
79
+ "@webstudio-is/image": "0.266.0",
80
+ "@webstudio-is/sdk": "0.266.0",
81
+ "@webstudio-is/sdk-components-animation": "0.266.0",
82
+ "@webstudio-is/sdk-components-react-radix": "0.266.0",
83
+ "@webstudio-is/sdk-components-react": "0.266.0",
84
+ "@webstudio-is/react-sdk": "0.266.0",
85
+ "@webstudio-is/sdk-components-react-remix": "0.266.0",
86
+ "@webstudio-is/sdk-components-react-router": "0.266.0",
84
87
  "@webstudio-is/tsconfig": "1.0.7"
85
88
  },
86
89
  "scripts": {
@@ -11,13 +11,13 @@
11
11
  "@remix-run/node": "2.16.5",
12
12
  "@remix-run/react": "2.16.5",
13
13
  "@remix-run/server-runtime": "2.16.5",
14
- "@webstudio-is/image": "0.264.0",
15
- "@webstudio-is/react-sdk": "0.264.0",
16
- "@webstudio-is/sdk": "0.264.0",
17
- "@webstudio-is/sdk-components-react": "0.264.0",
18
- "@webstudio-is/sdk-components-animation": "0.264.0",
19
- "@webstudio-is/sdk-components-react-radix": "0.264.0",
20
- "@webstudio-is/sdk-components-react-remix": "0.264.0",
14
+ "@webstudio-is/image": "0.266.0",
15
+ "@webstudio-is/react-sdk": "0.266.0",
16
+ "@webstudio-is/sdk": "0.266.0",
17
+ "@webstudio-is/sdk-components-react": "0.266.0",
18
+ "@webstudio-is/sdk-components-animation": "0.266.0",
19
+ "@webstudio-is/sdk-components-react-radix": "0.266.0",
20
+ "@webstudio-is/sdk-components-react-remix": "0.266.0",
21
21
  "isbot": "^5.1.25",
22
22
  "react": "18.3.0-canary-14898b6a9-20240318",
23
23
  "react-dom": "18.3.0-canary-14898b6a9-20240318"
@@ -10,13 +10,13 @@
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.264.0",
14
- "@webstudio-is/react-sdk": "0.264.0",
15
- "@webstudio-is/sdk": "0.264.0",
16
- "@webstudio-is/sdk-components-animation": "0.264.0",
17
- "@webstudio-is/sdk-components-react-radix": "0.264.0",
18
- "@webstudio-is/sdk-components-react-router": "0.264.0",
19
- "@webstudio-is/sdk-components-react": "0.264.0",
13
+ "@webstudio-is/image": "0.266.0",
14
+ "@webstudio-is/react-sdk": "0.266.0",
15
+ "@webstudio-is/sdk": "0.266.0",
16
+ "@webstudio-is/sdk-components-animation": "0.266.0",
17
+ "@webstudio-is/sdk-components-react-radix": "0.266.0",
18
+ "@webstudio-is/sdk-components-react-router": "0.266.0",
19
+ "@webstudio-is/sdk-components-react": "0.266.0",
20
20
  "isbot": "^5.1.25",
21
21
  "react": "18.3.0-canary-14898b6a9-20240318",
22
22
  "react-dom": "18.3.0-canary-14898b6a9-20240318",
@@ -8,12 +8,12 @@
8
8
  "typecheck": "tsgo --noEmit"
9
9
  },
10
10
  "dependencies": {
11
- "@webstudio-is/image": "0.264.0",
12
- "@webstudio-is/react-sdk": "0.264.0",
13
- "@webstudio-is/sdk": "0.264.0",
14
- "@webstudio-is/sdk-components-react": "0.264.0",
15
- "@webstudio-is/sdk-components-animation": "0.264.0",
16
- "@webstudio-is/sdk-components-react-radix": "0.264.0",
11
+ "@webstudio-is/image": "0.266.0",
12
+ "@webstudio-is/react-sdk": "0.266.0",
13
+ "@webstudio-is/sdk": "0.266.0",
14
+ "@webstudio-is/sdk-components-react": "0.266.0",
15
+ "@webstudio-is/sdk-components-animation": "0.266.0",
16
+ "@webstudio-is/sdk-components-react-radix": "0.266.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"