webstudio 0.301.0 → 0.303.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
@@ -20,7 +20,7 @@ import fs, { rm, mkdtemp, writeFile, readFile, access, constants as constants$1,
20
20
  import { gzip } from "node:zlib";
21
21
  import { execFile, spawn } from "node:child_process";
22
22
  import { promisify, isDeepStrictEqual } from "node:util";
23
- import { readdirSync, constants as constants$2, createWriteStream, existsSync, realpathSync, readFileSync } from "node:fs";
23
+ import { readdirSync, constants as constants$2, createWriteStream, existsSync, createReadStream, realpathSync, readFileSync } from "node:fs";
24
24
  import envPaths from "env-paths";
25
25
  import { randomUUID, createHash } from "node:crypto";
26
26
  import { text as text$d, isCancel, cancel, log, spinner, confirm, select as select$3 } from "@clack/prompts";
@@ -38,6 +38,7 @@ import { createServer } from "node:net";
38
38
  import "detect-port";
39
39
  import { Launcher } from "chrome-launcher";
40
40
  import { Buffer as Buffer$1 } from "node:buffer";
41
+ import semver from "semver";
41
42
  import { createRequire } from "node:module";
42
43
  import { x as x$6 } from "tinyexec";
43
44
  import makeCLI from "yargs";
@@ -52970,7 +52971,7 @@ const editCustomResponseHeaders = (headers, key2, next) => {
52970
52971
  (header2) => customResponseHeaderKey(header2) !== key2
52971
52972
  );
52972
52973
  if (next !== void 0) {
52973
- updated.unshift(next);
52974
+ updated.push(next);
52974
52975
  }
52975
52976
  return customResponseHeaders.parse(updated);
52976
52977
  };
@@ -193558,7 +193559,7 @@ const propsCollapsible = {
193558
193559
  const propsCollapsibleTrigger = {};
193559
193560
  const propsCollapsibleContent = {
193560
193561
  forceMount: {
193561
- 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.",
193562
+ description: "When true, closed content stays mounted. Keep an explicit closed-state CSS rule such as height: 0; Radix's animation-only example assumes unmounted content. Radix suppresses CSS transitions and animations while measuring height, so the first open may not animate.",
193562
193563
  required: false,
193563
193564
  control: "boolean",
193564
193565
  type: "boolean"
@@ -194167,10 +194168,10 @@ const propsAccordionHeader = {};
194167
194168
  const propsAccordionTrigger = {};
194168
194169
  const propsAccordionContent = {
194169
194170
  forceMount: {
194170
- 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.",
194171
194171
  required: false,
194172
194172
  control: "boolean",
194173
- type: "boolean"
194173
+ type: "boolean",
194174
+ description: "When true, closed content stays mounted. Keep an explicit closed-state CSS rule such as height: 0; Radix's animation-only example assumes unmounted content. Radix suppresses CSS transitions and animations while measuring height, so the first open may not animate."
194174
194175
  }
194175
194176
  };
194176
194177
  const metaAccordion = {
@@ -278902,7 +278903,7 @@ const AccordionItem = reactExports.forwardRef(({ value: value2, ...props2 }, ref
278902
278903
  });
278903
278904
  const AccordionHeader = Header;
278904
278905
  const AccordionTrigger = Trigger2;
278905
- const AccordionContent = Content2$1;
278906
+ const AccordionContent = reactExports.forwardRef(({ forceMount, ...props2 }, ref2) => /* @__PURE__ */ jsxRuntimeExports.jsx(Content2$1, { ref: ref2, forceMount: forceMount ? true : void 0, ...props2 }));
278906
278907
  const namespace$2 = "@webstudio-is/sdk-components-react-radix";
278907
278908
  const hooksAccordion = {
278908
278909
  onNavigatorSelect: (context, event) => {
@@ -323331,8 +323332,9 @@ const capabilityAreas = [
323331
323332
  },
323332
323333
  {
323333
323334
  area: "publish",
323334
- goal: "Publish, unpublish, inspect publish jobs, and manage domains.",
323335
+ goal: "Validate and publish projects, inspect publish jobs, and manage domains.",
323335
323336
  tools: [
323337
+ "validate-publish",
323336
323338
  "publish",
323337
323339
  "list-publishes",
323338
323340
  "get-publish-job",
@@ -327221,7 +327223,8 @@ const createProjectSessionMcpServer = async ({
327221
327223
  toolNameFormat = "canonical",
327222
327224
  toolHeartbeatIntervalMs = 1e4,
327223
327225
  onToolFailure,
327224
- onToolSuccess
327226
+ onToolSuccess,
327227
+ additionalInstructions
327225
327228
  }) => {
327226
327229
  const server = new Server(
327227
327230
  { name: "webstudio", version: "0.0.0" },
@@ -327231,7 +327234,7 @@ const createProjectSessionMcpServer = async ({
327231
327234
  resources: {},
327232
327235
  logging: {}
327233
327236
  },
327234
- instructions: startupGuidance
327237
+ instructions: [startupGuidance, additionalInstructions?.trim()].filter(Boolean).join("\n\n")
327235
327238
  }
327236
327239
  );
327237
327240
  const sendLog = (level, data2) => {
@@ -329285,10 +329288,10 @@ const serverOnlyRouterOperationMetadata = {
329285
329288
  },
329286
329289
  "publish.validate": {
329287
329290
  id: "publish.validate",
329288
- command: "check-publish-readiness",
329291
+ command: "validate-publish",
329289
329292
  method: "query",
329290
329293
  path: "api.publish.validate",
329291
- client: "checkPublishReadiness",
329294
+ client: "validatePublish",
329292
329295
  permit: "edit",
329293
329296
  inputSchema: {
329294
329297
  type: "object",
@@ -330279,12 +330282,12 @@ const curatedPublicApiOperationDocumentation = [
330279
330282
  examples: ["webstudio delete-resource --resource resource-id --json"]
330280
330283
  },
330281
330284
  {
330282
- command: "check-publish-readiness",
330283
- description: "Check publish access, selected domains, and content diagnostics without starting a publish. Use audit for project-wide quality checks.",
330285
+ command: "validate-publish",
330286
+ description: "Validate publish access, selected domains, and content diagnostics without starting a publish. Use audit for project-wide quality checks.",
330284
330287
  requiredOptions: ["target", "json"],
330285
330288
  examples: [
330286
- "webstudio check-publish-readiness --target production --json",
330287
- 'MCP tool: check-publish-readiness {"target":"production"}'
330289
+ "webstudio validate-publish --target production --json",
330290
+ 'MCP tool: validate-publish {"target":"production"}'
330288
330291
  ]
330289
330292
  },
330290
330293
  {
@@ -332331,7 +332334,7 @@ const createResource = projectMutationInput("create-resource");
332331
332334
  const updateResource = projectMutationInput("update-resource");
332332
332335
  const deleteResource = projectMutationInput("delete-resource");
332333
332336
  const listPublishes = projectQueryInput("list-publishes");
332334
- const checkPublishReadiness = projectQueryInput("check-publish-readiness");
332337
+ const validatePublish = projectQueryInput("validate-publish");
332335
332338
  const publish = projectMutationInput("publish");
332336
332339
  const getPublishJob = projectQueryInput("get-publish-job");
332337
332340
  const unpublish = projectConfirmedMutationInput("unpublish");
@@ -332496,7 +332499,6 @@ const httpClient = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePr
332496
332499
  bindProps,
332497
332500
  bundleVersion,
332498
332501
  checkProjectBuildPermission,
332499
- checkPublishReadiness,
332500
332502
  clearStyleSourceStyles,
332501
332503
  cloneInstance,
332502
332504
  connectContentBlockSource,
@@ -332680,6 +332682,7 @@ const httpClient = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePr
332680
332682
  upsertResource,
332681
332683
  upsertResourceProp,
332682
332684
  validateAssetQuery,
332685
+ validatePublish,
332683
332686
  verifyBindings,
332684
332687
  verifyDomain,
332685
332688
  wrapInstance
@@ -332692,7 +332695,7 @@ class HandledCliError extends Error {
332692
332695
  }
332693
332696
  const isHandledCliError = (error) => error instanceof HandledCliError;
332694
332697
  const name = "webstudio";
332695
- const version$1 = "0.301.0";
332698
+ const version$1 = "0.303.0";
332696
332699
  const description = "Webstudio CLI";
332697
332700
  const author = "Webstudio <github@webstudio.is>";
332698
332701
  const homepage = "https://webstudio.is";
@@ -333059,6 +333062,7 @@ const materializeManagedAgents = async ({
333059
333062
  return { path: path2, status: current4 === void 0 ? "created" : "updated" };
333060
333063
  };
333061
333064
  const publishedMdxTotalBytes = 32 * 1024 * 1024;
333065
+ const getPublishedMdxAssetIds = (assets) => new Set(assets.filter(isMdxFileAsset).map(({ id: id2 }) => id2));
333062
333066
  const getPublishedMdxContentDatabaseMaxBytes = ({
333063
333067
  baseBytes,
333064
333068
  assets
@@ -333164,6 +333168,8 @@ const resolvePublishedMdxDependencyClosureWithParser = async ({
333164
333168
  build: build2,
333165
333169
  artifact,
333166
333170
  onTemplateOmission,
333171
+ onMdxBlockInstanceId,
333172
+ allMdxAssetIds,
333167
333173
  parseDocument: parseDocument2
333168
333174
  }) => {
333169
333175
  const instances = new Map(
@@ -333210,11 +333216,13 @@ const resolvePublishedMdxDependencyClosureWithParser = async ({
333210
333216
  if (source === void 0) {
333211
333217
  continue;
333212
333218
  }
333219
+ onMdxBlockInstanceId?.(blockId);
333213
333220
  const staticValue = getStaticContentBlockSourceAssetId(source);
333214
333221
  const assetIds = typeof staticValue === "string" ? [staticValue] : resolvePublishedMdxAssetCandidates({
333215
333222
  build: build2,
333216
333223
  artifact,
333217
- blockInstanceIds: /* @__PURE__ */ new Set([blockId])
333224
+ blockInstanceIds: /* @__PURE__ */ new Set([blockId]),
333225
+ allMdxAssetIds
333218
333226
  }).get(blockId) ?? [];
333219
333227
  for (const assetId2 of assetIds) {
333220
333228
  const documentEntry = documentsById.get(assetId2);
@@ -333263,7 +333271,8 @@ const resolvePublishedMdxDependencyClosureWithParser = async ({
333263
333271
  build: build2,
333264
333272
  artifact,
333265
333273
  blockInstanceIds: reachableIds,
333266
- allowUnresolved: true
333274
+ allowUnresolved: true,
333275
+ allMdxAssetIds
333267
333276
  });
333268
333277
  return createPublishedBuildContentCompilationPlan(
333269
333278
  build2,
@@ -333314,7 +333323,8 @@ const resolvePublishedMdxAssetCandidates = ({
333314
333323
  build: build2,
333315
333324
  artifact,
333316
333325
  allowUnresolved = false,
333317
- blockInstanceIds = getPublishedInstanceIds(build2)
333326
+ blockInstanceIds = getPublishedInstanceIds(build2),
333327
+ allMdxAssetIds
333318
333328
  }) => {
333319
333329
  const instances = new Map(
333320
333330
  getBuildValues(build2.instances).map((instance2) => [
@@ -333341,7 +333351,7 @@ const resolvePublishedMdxAssetCandidates = ({
333341
333351
  );
333342
333352
  const documentGraph = artifact?.documentGraph === void 0 ? void 0 : createDocumentGraph(artifact.documentGraph);
333343
333353
  const documents = artifact?.documents ?? [];
333344
- const evaluateDataSource = (dataSourceId2, path2, visiting = /* @__PURE__ */ new Set()) => {
333354
+ const evaluateDataSource = (dataSourceId2, path2, visiting = /* @__PURE__ */ new Set(), mutableVariables = /* @__PURE__ */ new Set()) => {
333345
333355
  if (visiting.has(dataSourceId2)) {
333346
333356
  throw new Error("Dynamic MDX source data dependency is cyclic");
333347
333357
  }
@@ -333350,6 +333360,7 @@ const resolvePublishedMdxAssetCandidates = ({
333350
333360
  return [];
333351
333361
  }
333352
333362
  if (dataSource2.type === "variable") {
333363
+ mutableVariables.add(dataSourceId2);
333353
333364
  return [];
333354
333365
  }
333355
333366
  if (dataSource2.type === "resource") {
@@ -333428,7 +333439,8 @@ const resolvePublishedMdxAssetCandidates = ({
333428
333439
  iterables = evaluateDataSource(
333429
333440
  dependencyId,
333430
333441
  dataPath.slice(1),
333431
- visiting
333442
+ visiting,
333443
+ mutableVariables
333432
333444
  );
333433
333445
  visiting.delete(dataSourceId2);
333434
333446
  }
@@ -333459,8 +333471,14 @@ const resolvePublishedMdxAssetCandidates = ({
333459
333471
  const path2 = parseStaticMemberPath(source.value);
333460
333472
  const dataSourceId2 = path2 === void 0 ? void 0 : decodeDataVariableId(path2[0]);
333461
333473
  let assetIds = [];
333474
+ const mutableVariables = /* @__PURE__ */ new Set();
333462
333475
  try {
333463
- assetIds = dataSourceId2 === void 0 || path2 === void 0 ? [] : evaluateDataSource(dataSourceId2, path2.slice(1)).filter(
333476
+ assetIds = dataSourceId2 === void 0 || path2 === void 0 ? [] : evaluateDataSource(
333477
+ dataSourceId2,
333478
+ path2.slice(1),
333479
+ /* @__PURE__ */ new Set(),
333480
+ mutableVariables
333481
+ ).filter(
333464
333482
  (value2) => typeof value2 === "string" && value2.length > 0
333465
333483
  );
333466
333484
  } catch (error) {
@@ -333468,6 +333486,9 @@ const resolvePublishedMdxAssetCandidates = ({
333468
333486
  throw error;
333469
333487
  }
333470
333488
  }
333489
+ if (mutableVariables.size > 0 && allMdxAssetIds !== void 0) {
333490
+ assetIds.push(...allMdxAssetIds);
333491
+ }
333471
333492
  if (assetIds.length === 0) {
333472
333493
  if (allowUnresolved) {
333473
333494
  continue;
@@ -333839,7 +333860,26 @@ const sync = async (options, dependencies2 = defaultSyncDependencies) => {
333839
333860
  )
333840
333861
  });
333841
333862
  const isStaticBuild = project.build.deployment?.destination === "static";
333842
- const assetsToDownload = options.buildId !== void 0 && isStaticBuild === false ? project.assets.filter(
333863
+ const publicationBuild = {
333864
+ ...project.build,
333865
+ pages: migratePages(project.build.pages)
333866
+ };
333867
+ const allMdxAssetIds = getPublishedMdxAssetIds(project.assets);
333868
+ const projectCandidates = resolvePublishedMdxAssetCandidates({
333869
+ build: publicationBuild,
333870
+ artifact: project.assetIndex,
333871
+ allowUnresolved: true,
333872
+ allMdxAssetIds
333873
+ });
333874
+ const dynamicBlockIds = getDynamicPublishedMdxSourceBlockIds(publicationBuild);
333875
+ const publicationPlan = createPublishedBuildContentCompilationPlan(
333876
+ publicationBuild,
333877
+ projectCandidates
333878
+ );
333879
+ const needsMdxCompilation = dynamicBlockIds.length > 0 || publicationPlan?.queries.some(
333880
+ ({ id: id2 }) => id2.startsWith("__content-block-mdx__:")
333881
+ ) === true;
333882
+ const assetsToDownload = options.buildId !== void 0 && !isStaticBuild && !needsMdxCompilation ? project.assets.filter(
333843
333883
  (asset2) => asset2.format !== "md" && asset2.format !== "mdx"
333844
333884
  ) : project.assets;
333845
333885
  if (assetsToDownload.length > 0) {
@@ -333863,23 +333903,12 @@ const sync = async (options, dependencies2 = defaultSyncDependencies) => {
333863
333903
  throw new HandledCliError();
333864
333904
  }
333865
333905
  }
333866
- if (options.buildId !== void 0 && isStaticBuild && project.assetIndex === void 0) {
333867
- const publicationBuild = {
333868
- ...project.build,
333869
- pages: migratePages(project.build.pages)
333870
- };
333871
- const projectCandidates = resolvePublishedMdxAssetCandidates({
333872
- build: publicationBuild,
333873
- allowUnresolved: true
333874
- });
333875
- const needsCandidateDiscovery = getDynamicPublishedMdxSourceBlockIds(
333876
- publicationBuild
333877
- ).some((blockId) => projectCandidates.has(blockId) === false);
333878
- const candidateDiscoveryPlan = needsCandidateDiscovery ? createBuildContentCompilationPlan(publicationBuild) : void 0;
333879
- let plan = candidateDiscoveryPlan ?? createPublishedBuildContentCompilationPlan(
333880
- publicationBuild,
333881
- projectCandidates
333906
+ if (options.buildId !== void 0 && (isStaticBuild && project.assetIndex === void 0 || needsMdxCompilation)) {
333907
+ const needsCandidateDiscovery = dynamicBlockIds.some(
333908
+ (blockId) => projectCandidates.has(blockId) === false
333882
333909
  );
333910
+ const candidateDiscoveryPlan = needsCandidateDiscovery ? createBuildContentCompilationPlan(publicationBuild) : void 0;
333911
+ const plan = candidateDiscoveryPlan ?? publicationPlan;
333883
333912
  if (plan !== void 0) {
333884
333913
  syncing.message("Preparing local content index");
333885
333914
  const source = createContentSource();
@@ -333900,7 +333929,11 @@ const sync = async (options, dependencies2 = defaultSyncDependencies) => {
333900
333929
  project.assetIndex = await compileContentUntilPlanIsStable({
333901
333930
  plan,
333902
333931
  compile,
333903
- resolvePlan: async (artifact) => await resolvePlan({ build: publicationBuild, artifact })
333932
+ resolvePlan: async (artifact) => await resolvePlan({
333933
+ build: publicationBuild,
333934
+ artifact,
333935
+ allMdxAssetIds
333936
+ })
333904
333937
  });
333905
333938
  } else {
333906
333939
  project.assetIndex = await compile(plan);
@@ -340690,7 +340723,8 @@ const materializePublishedMdx = async ({
340690
340723
  cache = createPublishedMdxMaterializationCache(),
340691
340724
  dynamicAssetIdsByBlock = /* @__PURE__ */ new Map(),
340692
340725
  blockInstanceIds,
340693
- runtimeAssets: providedRuntimeAssets
340726
+ runtimeAssets: providedRuntimeAssets,
340727
+ loadDocumentSource
340694
340728
  }) => {
340695
340729
  const documentsById = new Map(
340696
340730
  artifact.documents.map((document2) => [document2._id, document2])
@@ -340718,7 +340752,7 @@ const materializePublishedMdx = async ({
340718
340752
  assetValueReferences: artifact.assetValueReferences,
340719
340753
  runtimeAssets,
340720
340754
  load: async (node2) => {
340721
- const source = artifact.contents?.[node2.contentRef];
340755
+ const source = artifact.contents?.[node2.contentRef] ?? await loadDocumentSource?.(node2);
340722
340756
  if (source === void 0 || node2.format === void 0) {
340723
340757
  throw new Error(`Published document ${node2.id} is unavailable`);
340724
340758
  }
@@ -340873,6 +340907,26 @@ const materializePublishedMdx = async ({
340873
340907
  resolution2.references.map((reference) => reference.templateName)
340874
340908
  )
340875
340909
  );
340910
+ let resolvedFrontmatter = document2.frontmatter.properties;
340911
+ try {
340912
+ resolvedFrontmatter = await resolveFrontmatter(candidate._id, document2);
340913
+ } catch (error) {
340914
+ if (error instanceof DocumentGraphResolutionError && error.code === "REQUEST_CANCELLED") {
340915
+ throw error;
340916
+ }
340917
+ if (error instanceof DocumentGraphResolutionError) {
340918
+ const linkedDocument = error.documentId === void 0 ? "Linked document" : `Linked document "${error.documentId}"`;
340919
+ const reference = error.referenceId === void 0 ? "" : ` (reference "${error.referenceId}")`;
340920
+ const failure = error.code === "DOCUMENT_LOAD_FAILED" ? "could not be loaded" : `could not be resolved: ${error.message}`;
340921
+ warnUnavailableSource({
340922
+ blockInstanceId: block.id,
340923
+ assetId: candidate._id,
340924
+ message: `${linkedDocument} ${failure}${reference} while resolving frontmatter for published MDX Asset "${candidate._id}".`
340925
+ });
340926
+ } else {
340927
+ throw error;
340928
+ }
340929
+ }
340876
340930
  const materialized = materializeMdxAuthoredContent({
340877
340931
  identity,
340878
340932
  document: document2,
@@ -340905,7 +340959,7 @@ const materializePublishedMdx = async ({
340905
340959
  dynamic: isDynamicSource,
340906
340960
  identity,
340907
340961
  document: document2,
340908
- resolvedFrontmatter: await resolveFrontmatter(candidate._id, document2),
340962
+ resolvedFrontmatter,
340909
340963
  fragment: materialized.fragment,
340910
340964
  templateDependencies: templates2.dependencies.templates,
340911
340965
  templateNames: referencedTemplateNames,
@@ -346947,6 +347001,37 @@ Please check webstudio --help for more details`
346947
347001
  exit$4(1);
346948
347002
  }
346949
347003
  }
347004
+ const loadedSiteData = await loadJSONFile(LOCAL_DATA_FILE);
347005
+ if (loadedSiteData === null) {
347006
+ throw new Error(
347007
+ `Project bundle is missing, please make sure the project is synced.`
347008
+ );
347009
+ }
347010
+ const parsedSiteData = publishedProjectBundle.safeParse(loadedSiteData);
347011
+ if (parsedSiteData.success === false) {
347012
+ throw Object.assign(
347013
+ new Error(
347014
+ `Project bundle is invalid, please make sure the project is synced. Invalid fields: ${formatZodIssues(
347015
+ parsedSiteData.error.issues,
347016
+ loadedSiteData
347017
+ )}`
347018
+ ),
347019
+ {
347020
+ code: "PROJECT_BUNDLE_INVALID",
347021
+ bundleVersion,
347022
+ issues: getZodValidationIssues(parsedSiteData.error)
347023
+ }
347024
+ );
347025
+ }
347026
+ const siteData = parsedSiteData.data;
347027
+ const pages2 = migratePages(siteData.build.pages);
347028
+ const publicationBuild = { ...siteData.build, pages: pages2 };
347029
+ const allMdxAssetIds = getPublishedMdxAssetIds(siteData.assets);
347030
+ if (siteData.assetIndex === void 0 && (createPublishedBuildContentCompilationPlan(publicationBuild) !== void 0 || hasDynamicPublishedMdxSources(publicationBuild))) {
347031
+ throw new Error(
347032
+ "Assets queries require a content database. Sync the project again before building."
347033
+ );
347034
+ }
346950
347035
  feedback.step("Scaffolding the project files");
346951
347036
  if (options.incremental !== true) {
346952
347037
  await rm(generatedDir, { recursive: true, force: true });
@@ -346985,31 +347070,6 @@ Please check webstudio --help for more details`
346985
347070
  framework = await createFramework$2(frameworkOptions);
346986
347071
  }
346987
347072
  const assetBaseUrl = await readAssetBaseUrl(join$2(cwd$1(), "app/constants.mjs"));
346988
- const loadedSiteData = await loadJSONFile(LOCAL_DATA_FILE);
346989
- if (loadedSiteData === null) {
346990
- throw new Error(
346991
- `Project bundle is missing, please make sure the project is synced.`
346992
- );
346993
- }
346994
- const parsedSiteData = publishedProjectBundle.safeParse(loadedSiteData);
346995
- if (parsedSiteData.success === false) {
346996
- throw Object.assign(
346997
- new Error(
346998
- `Project bundle is invalid, please make sure the project is synced. Invalid fields: ${formatZodIssues(
346999
- parsedSiteData.error.issues,
347000
- loadedSiteData
347001
- )}`
347002
- ),
347003
- {
347004
- code: "PROJECT_BUNDLE_INVALID",
347005
- bundleVersion,
347006
- issues: getZodValidationIssues(parsedSiteData.error)
347007
- }
347008
- );
347009
- }
347010
- const siteData = parsedSiteData.data;
347011
- const pages2 = migratePages(siteData.build.pages);
347012
- const publicationBuild = { ...siteData.build, pages: pages2 };
347013
347073
  let verifiedAssetIndex = siteData.assetIndex === void 0 ? void 0 : await verifyContentArtifact(siteData.assetIndex);
347014
347074
  if (verifiedAssetIndex !== void 0 && (options.previewIdentity === true || options.assets === true)) {
347015
347075
  verifiedAssetIndex = await hydrateLocalMarkdownContents({
@@ -347023,10 +347083,11 @@ Please check webstudio --help for more details`
347023
347083
  }
347024
347084
  let dynamicMdxCandidates;
347025
347085
  if (hasDynamicPublishedMdxSources(publicationBuild)) {
347026
- dynamicMdxCandidates = verifiedAssetIndex === void 0 ? /* @__PURE__ */ new Map() : resolvePublishedMdxAssetCandidates({
347086
+ dynamicMdxCandidates = resolvePublishedMdxAssetCandidates({
347027
347087
  build: publicationBuild,
347028
347088
  artifact: verifiedAssetIndex,
347029
- allowUnresolved: true
347089
+ allowUnresolved: true,
347090
+ allMdxAssetIds
347030
347091
  });
347031
347092
  for (const blockInstanceId of getDynamicPublishedMdxSourceBlockIds(
347032
347093
  publicationBuild
@@ -347260,7 +347321,8 @@ Please check webstudio --help for more details`
347260
347321
  ) ? resolvePublishedMdxAssetCandidates({
347261
347322
  build: pageBuild,
347262
347323
  artifact: verifiedAssetIndex,
347263
- blockInstanceIds: pendingBlockIds
347324
+ blockInstanceIds: pendingBlockIds,
347325
+ allMdxAssetIds
347264
347326
  }) : void 0;
347265
347327
  const materialized = await materializePublishedMdx({
347266
347328
  route,
@@ -347281,7 +347343,21 @@ Please check webstudio --help for more details`
347281
347343
  cache,
347282
347344
  dynamicAssetIdsByBlock: pageDynamicCandidates,
347283
347345
  blockInstanceIds: pendingBlockIds,
347284
- runtimeAssets: runtimeAssetsById
347346
+ runtimeAssets: runtimeAssetsById,
347347
+ loadDocumentSource: async ({ contentRef }) => {
347348
+ const source = createReadStream(
347349
+ getLocalAssetPath(
347350
+ contentRef,
347351
+ options.sourceAssetsDirectory ?? join$2(buildRoot, LOCAL_ASSETS_DIR)
347352
+ )
347353
+ );
347354
+ return decodeUtf8$1(
347355
+ await readBoundedBytes(
347356
+ source,
347357
+ contentEngineLimits.hydratedFileBytes
347358
+ )
347359
+ );
347360
+ }
347285
347361
  });
347286
347362
  for (const root2 of materialized.roots) {
347287
347363
  const diagnostic = getUnsafeDynamicPublishedMdxDiagnostic({
@@ -347386,6 +347462,9 @@ Please check webstudio --help for more details`
347386
347462
  ...warning2.diagnostic
347387
347463
  })
347388
347464
  );
347465
+ if (warning2.diagnostic.severity === "error") {
347466
+ throw new Error(warning2.diagnostic.message);
347467
+ }
347389
347468
  }
347390
347469
  }
347391
347470
  pageData.build.instances = Array.from(pageInstances);
@@ -347659,7 +347738,8 @@ Please check webstudio --help for more details`
347659
347738
  import type { PageMeta } from "@webstudio-is/sdk";
347660
347739
  ${generateResources({
347661
347740
  scope: scope2,
347662
- page: page2,
347741
+ // XML generation removes the body wrapper from the instance map.
347742
+ page: { ...page2, rootInstanceId },
347663
347743
  dataSources,
347664
347744
  props: props2,
347665
347745
  resources,
@@ -350346,7 +350426,7 @@ const publishCommandOptions = (yargs) => publishDomainsOption(publishTargetOptio
350346
350426
  type: "string",
350347
350427
  describe: "Optional idempotency key for retrying the same publish"
350348
350428
  });
350349
- const checkPublishReadinessCommandOptions = (yargs) => publishDomainsOption(publishTargetOption(apiCommandOptions(yargs)));
350429
+ const validatePublishCommandOptions = (yargs) => publishDomainsOption(publishTargetOption(apiCommandOptions(yargs)));
350350
350430
  const publishJobCommandOptions = (yargs) => apiCommandOptions(yargs).option("job", {
350351
350431
  type: "string",
350352
350432
  describe: "Required publish job id returned by publish or unpublish",
@@ -351786,13 +351866,13 @@ const apiCommandHandlers = {
351786
351866
  };
351787
351867
  return runProjectSessionCommand("publish", input2, connection, dependencies2);
351788
351868
  },
351789
- "check-publish-readiness": async (options, connection, dependencies2) => {
351869
+ "validate-publish": async (options, connection, dependencies2) => {
351790
351870
  const input2 = {
351791
351871
  target: publishTargetValue(options.target),
351792
351872
  domains: listStringOption(options.domain)
351793
351873
  };
351794
351874
  return runProjectSessionCommand(
351795
- "check-publish-readiness",
351875
+ "validate-publish",
351796
351876
  input2,
351797
351877
  connection,
351798
351878
  dependencies2
@@ -352249,7 +352329,7 @@ const apiCommandOptionsByCommand = {
352249
352329
  "delete-resource": deleteResourceCommandOptions,
352250
352330
  "list-domains": paginatedListCommandOptions,
352251
352331
  publish: publishCommandOptions,
352252
- "check-publish-readiness": checkPublishReadinessCommandOptions,
352332
+ "validate-publish": validatePublishCommandOptions,
352253
352333
  "get-publish-job": publishJobCommandOptions,
352254
352334
  unpublish: unpublishCommandOptions,
352255
352335
  "create-domain": createDomainCommandOptions,
@@ -352282,8 +352362,8 @@ const apiCommandMetadata = publicApiOperations.map((operation) => ({
352282
352362
  const highLevelCliCommands = [
352283
352363
  { command: "audit", operation: "audit" },
352284
352364
  {
352285
- command: "check-publish-readiness",
352286
- operation: "check-publish-readiness"
352365
+ command: "validate-publish",
352366
+ operation: "validate-publish"
352287
352367
  },
352288
352368
  { command: "permissions", operation: "permissions" },
352289
352369
  { command: "get-assets-resource", operation: "get-assets-resource" },
@@ -352837,7 +352917,7 @@ const apiCommandsByArea = {
352837
352917
  projectQuality: cliCommandMetadata.filter(({ cliCommand }) => cliCommand === "audit").map(({ cliCommand }) => cliCommand),
352838
352918
  assetsAndIndexes: cliCommandMetadata.filter(({ cliCommand }) => cliCommand.includes("asset")).map(({ cliCommand }) => cliCommand),
352839
352919
  publishAndDomains: cliCommandMetadata.filter(
352840
- ({ cliCommand }) => cliCommand === "check-publish-readiness" || cliCommand.startsWith("publish ") || cliCommand.startsWith("domains ")
352920
+ ({ cliCommand }) => cliCommand === "validate-publish" || cliCommand.startsWith("publish ") || cliCommand.startsWith("domains ")
352841
352921
  ).map(({ cliCommand }) => cliCommand),
352842
352922
  pagesAndSettings: cliCommandMetadata.filter(
352843
352923
  ({ cliCommand }) => [
@@ -354807,6 +354887,35 @@ const withTimeout = async (operation, timeout, createTimeoutError2) => {
354807
354887
  }
354808
354888
  }
354809
354889
  };
354890
+ const latestPackageUrl = "https://registry.npmjs.org/webstudio/latest";
354891
+ const requestTimeoutMs = 1500;
354892
+ const checkForCliUpdate = async ({
354893
+ currentVersion,
354894
+ request = fetch
354895
+ }) => {
354896
+ if (semver.valid(currentVersion) === null) {
354897
+ return;
354898
+ }
354899
+ try {
354900
+ const response = await request(latestPackageUrl, {
354901
+ signal: AbortSignal.timeout(requestTimeoutMs)
354902
+ });
354903
+ if (response.ok === false) {
354904
+ return;
354905
+ }
354906
+ const latestPackage = await response.json();
354907
+ if (isPlainRecord(latestPackage) === false) {
354908
+ return;
354909
+ }
354910
+ const latestVersion = typeof latestPackage.version === "string" ? semver.valid(latestPackage.version) : null;
354911
+ if (latestVersion === null || semver.gt(latestVersion, currentVersion) === false) {
354912
+ return;
354913
+ }
354914
+ return { currentVersion, latestVersion };
354915
+ } catch {
354916
+ return;
354917
+ }
354918
+ };
354810
354919
  const createProgressProjects = () => /* @__PURE__ */ Object.create(null);
354811
354920
  const maximumConcurrency = 16;
354812
354921
  const parsePositiveInteger = (value2, fallback) => {
@@ -355958,14 +356067,32 @@ const createMcpPreviewHandlers = ({
355958
356067
  quality: input2.quality,
355959
356068
  scale: input2.scale
355960
356069
  });
356070
+ const captureWhileRouteSettles = async (capture, isGeneratedSite, progress2) => {
356071
+ let result2 = await capture();
356072
+ for (let retry = 0; retry < 2 && !isGeneratedSite(result2); retry += 1) {
356073
+ progress2?.report("tool screenshot waiting for refreshed generated route");
356074
+ await sleep(1e3);
356075
+ result2 = await capture();
356076
+ }
356077
+ return result2;
356078
+ };
355961
356079
  const assertGeneratedSiteCapture = (input2, result2) => {
355962
356080
  if (input2.path !== void 0 && input2.baseUrl === void 0 && result2.navigation?.generatedSiteRootPresent === false) {
355963
- throw Object.assign(
355964
- new Error(
355965
- 'Screenshot did not render the generated Webstudio site. Capture the route with screenshot { path: "/" }; do not capture the Builder UI.'
355966
- ),
355967
- { code: "SCREENSHOT_NOT_GENERATED_SITE" }
355968
- );
356081
+ const status = result2.navigation.status;
356082
+ const routeFailed = status !== void 0 && status >= 400;
356083
+ const code2 = routeFailed ? "SCREENSHOT_ROUTE_HTTP_ERROR" : "SCREENSHOT_NOT_GENERATED_SITE";
356084
+ const message = routeFailed ? `The generated route returned HTTP ${status} before a Webstudio page could render. Check the preview server output for the route error.` : "The requested route did not render a generated Webstudio page. Check the route and preview server output; a screenshot of the home route does not verify this page.";
356085
+ throw Object.assign(new Error(message), {
356086
+ code: code2,
356087
+ issues: [
356088
+ {
356089
+ code: routeFailed ? "screenshot_route_http_error" : "generated_site_marker_missing",
356090
+ path: [],
356091
+ message,
356092
+ constraint: routeFailed ? `http_status:${status}` : "generated_site_identity_present"
356093
+ }
356094
+ ]
356095
+ });
355969
356096
  }
355970
356097
  return result2;
355971
356098
  };
@@ -356037,17 +356164,11 @@ const createMcpPreviewHandlers = ({
356037
356164
  return await captureWithSessionReconnect(
356038
356165
  input2,
356039
356166
  progress2,
356040
- async (captureSession2) => {
356041
- let captureResult = await captureSession2.capture(captureOptions);
356042
- for (let retry = 0; retry < 2 && captureResult.navigation?.generatedSiteRootPresent === false; retry += 1) {
356043
- progress2?.report(
356044
- "tool screenshot waiting for refreshed generated route"
356045
- );
356046
- await sleep(1e3);
356047
- captureResult = await captureSession2.capture(captureOptions);
356048
- }
356049
- return captureResult;
356050
- }
356167
+ async (captureSession2) => await captureWhileRouteSettles(
356168
+ () => captureSession2.capture(captureOptions),
356169
+ (result22) => result22.navigation?.generatedSiteRootPresent !== false,
356170
+ progress2
356171
+ )
356051
356172
  );
356052
356173
  }
356053
356174
  return await captureScreenshot2({
@@ -356106,8 +356227,8 @@ const createMcpPreviewHandlers = ({
356106
356227
  return await captureWithSessionReconnect(
356107
356228
  firstInput,
356108
356229
  progress2,
356109
- async (captureSession2) => await captureSession2.capturePage(
356110
- inputs.map((input2, index2) => {
356230
+ async (captureSession2) => {
356231
+ const captureOptions = inputs.map((input2, index2) => {
356111
356232
  const url2 = urls[index2];
356112
356233
  if (url2 === void 0) {
356113
356234
  throw new Error(
@@ -356115,8 +356236,18 @@ const createMcpPreviewHandlers = ({
356115
356236
  );
356116
356237
  }
356117
356238
  return getCaptureOptions(input2, url2);
356118
- })
356119
- )
356239
+ });
356240
+ if (isManagedSessionPreviewCapture(firstInput) === false) {
356241
+ return await captureSession2.capturePage(captureOptions);
356242
+ }
356243
+ return await captureWhileRouteSettles(
356244
+ () => captureSession2.capturePage(captureOptions),
356245
+ (results2) => results2.every(
356246
+ (result2) => result2.navigation?.generatedSiteRootPresent !== false
356247
+ ),
356248
+ progress2
356249
+ );
356250
+ }
356120
356251
  );
356121
356252
  },
356122
356253
  timeout,
@@ -356178,6 +356309,12 @@ const prepareMcpProjectSession = async (session) => {
356178
356309
  };
356179
356310
  const mcpStatusPrefix = "[webstudio mcp]";
356180
356311
  const renderedAuditArtifactDirectory = ".webstudio/audits";
356312
+ const getCliUpdateInstructions = (update) => {
356313
+ if (update === void 0) {
356314
+ return;
356315
+ }
356316
+ return `This MCP server runs Webstudio CLI ${update.currentVersion}, but ${update.latestVersion} is available. Tell the user before editing and recommend restarting this MCP server with the latest CLI, for example \`npx -y webstudio@latest mcp\`. Do not try to update or restart the CLI yourself. The existing API compatibility check remains the authority for whether writes are supported.`;
356317
+ };
356181
356318
  const formatMcpStatusLine = (message) => `${mcpStatusPrefix} ${message}`;
356182
356319
  const createMcpStatusReporter = (write = (line) => {
356183
356320
  stderr.write(`${line}
@@ -357483,7 +357620,7 @@ const createCliMcpHost = async ({
357483
357620
  const renderedProjectVersion = previewStatus.renderedProjectVersion;
357484
357621
  const namespacesByStatus = snapshot === void 0 ? void 0 : (status) => getBuilderStateNamespacesByStatus(snapshot.freshness, status);
357485
357622
  return createIssueReportRuntime(failureTracker.get(), {
357486
- projectId,
357623
+ projectId: connection.projectId,
357487
357624
  ...namespacesByStatus === void 0 ? {} : {
357488
357625
  session: {
357489
357626
  staleNamespaces: namespacesByStatus("stale"),
@@ -358307,6 +358444,9 @@ const mcp = async (options = {}) => {
358307
358444
  stdin.once("end", reportClose);
358308
358445
  stdin.once("close", reportClose);
358309
358446
  status.starting();
358447
+ const cliUpdatePromise = checkForCliUpdate({
358448
+ currentVersion: packageJson.version
358449
+ });
358310
358450
  const {
358311
358451
  host,
358312
358452
  toolCount,
@@ -358318,6 +358458,7 @@ const mcp = async (options = {}) => {
358318
358458
  } = await createCliMcpHost({
358319
358459
  projectId: options.project
358320
358460
  });
358461
+ const cliUpdate = await cliUpdatePromise;
358321
358462
  disposeHost = dispose;
358322
358463
  if (didReportClose) {
358323
358464
  await disposeHost().catch(() => void 0);
@@ -358328,6 +358469,7 @@ const mcp = async (options = {}) => {
358328
358469
  status.ready(toolCount);
358329
358470
  const server = await connectProjectSessionMcpServer({
358330
358471
  ...host,
358472
+ additionalInstructions: getCliUpdateInstructions(cliUpdate),
358331
358473
  toolNameFormat: options.toolNameFormat,
358332
358474
  getErrorCode: getStableErrorCode,
358333
358475
  onToolFailure: recordToolFailure,
@@ -358344,6 +358486,7 @@ const mcp = async (options = {}) => {
358344
358486
  };
358345
358487
  const __testing__ = {
358346
358488
  getMcpDownloadAsset,
358489
+ getCliUpdateInstructions,
358347
358490
  createMcpStatusReporter,
358348
358491
  formatMcpStatusLine,
358349
358492
  assertSingleOpCallToolSupported,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webstudio",
3
- "version": "0.301.0",
3
+ "version": "0.303.0",
4
4
  "description": "Webstudio CLI",
5
5
  "author": "Webstudio <github@webstudio.is>",
6
6
  "homepage": "https://webstudio.is",
@@ -53,10 +53,10 @@
53
53
  "which": "^5.0.0",
54
54
  "yargs": "^17.7.2",
55
55
  "zod": "^4.4.3",
56
- "@webstudio-is/http-client": "0.301.0",
57
- "@webstudio-is/project-migrations": "0.301.0",
58
- "@webstudio-is/protocol": "0.301.0",
59
- "@webstudio-is/sync-client": "0.0.0"
56
+ "@webstudio-is/protocol": "0.303.0",
57
+ "@webstudio-is/sync-client": "0.0.0",
58
+ "@webstudio-is/http-client": "0.303.0",
59
+ "@webstudio-is/project-migrations": "0.303.0"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@cloudflare/vite-plugin": "^1.1.0",
@@ -99,24 +99,24 @@
99
99
  "vite": "^6.3.4",
100
100
  "vitest": "^3.1.2",
101
101
  "wrangler": "4.130.0",
102
- "@webstudio-is/content-engine": "0.301.0",
103
- "@webstudio-is/css-engine": "0.301.0",
104
- "@webstudio-is/expression": "0.301.0",
105
- "@webstudio-is/image": "0.301.0",
106
- "@webstudio-is/query-builder": "0.301.0",
107
- "@webstudio-is/react-sdk": "0.301.0",
108
- "@webstudio-is/sdk-components-animation": "0.301.0",
109
- "@webstudio-is/sdk": "0.301.0",
110
- "@webstudio-is/sdk-components-react": "0.301.0",
111
- "@webstudio-is/sdk-components-react-radix": "0.301.0",
112
- "@webstudio-is/sdk-components-react-remix": "0.301.0",
113
- "@webstudio-is/fonts": "0.301.0",
114
- "@webstudio-is/sdk-components-react-router": "0.301.0",
102
+ "@webstudio-is/css-engine": "0.303.0",
103
+ "@webstudio-is/expression": "0.303.0",
104
+ "@webstudio-is/content-engine": "0.303.0",
105
+ "@webstudio-is/fonts": "0.303.0",
106
+ "@webstudio-is/image": "0.303.0",
107
+ "@webstudio-is/project-build": "0.0.0",
108
+ "@webstudio-is/react-sdk": "0.303.0",
109
+ "@webstudio-is/query-builder": "0.303.0",
110
+ "@webstudio-is/sdk": "0.303.0",
111
+ "@webstudio-is/sdk-components-animation": "0.303.0",
112
+ "@webstudio-is/sdk-components-react": "0.303.0",
113
+ "@webstudio-is/sdk-components-react-radix": "0.303.0",
114
+ "@webstudio-is/sdk-components-react-remix": "0.303.0",
115
+ "@webstudio-is/sdk-components-react-router": "0.303.0",
116
+ "@webstudio-is/sdk-components-registry": "0.303.0",
115
117
  "@webstudio-is/tsconfig": "1.0.7",
116
118
  "@webstudio-is/vision": "0.0.0",
117
- "@webstudio-is/wsauth": "0.301.0",
118
- "@webstudio-is/project-build": "0.0.0",
119
- "@webstudio-is/sdk-components-registry": "0.301.0"
119
+ "@webstudio-is/wsauth": "0.303.0"
120
120
  },
121
121
  "scripts": {
122
122
  "typecheck": "tsc --noEmit",
@@ -12,7 +12,7 @@
12
12
  "build-cf-types": "wrangler types"
13
13
  },
14
14
  "dependencies": {
15
- "@webstudio-is/wsauth": "0.301.0",
15
+ "@webstudio-is/wsauth": "0.303.0",
16
16
  "@remix-run/cloudflare": "2.16.5",
17
17
  "@remix-run/cloudflare-pages": "2.16.5"
18
18
  },
@@ -13,14 +13,14 @@
13
13
  "@remix-run/node": "2.16.5",
14
14
  "@remix-run/react": "2.16.5",
15
15
  "@remix-run/server-runtime": "2.16.5",
16
- "@webstudio-is/image": "0.301.0",
17
- "@webstudio-is/react-sdk": "0.301.0",
18
- "@webstudio-is/sdk": "0.301.0",
19
- "@webstudio-is/sdk-components-react": "0.301.0",
20
- "@webstudio-is/sdk-components-animation": "0.301.0",
21
- "@webstudio-is/sdk-components-react-radix": "0.301.0",
22
- "@webstudio-is/sdk-components-react-remix": "0.301.0",
23
- "@webstudio-is/wsauth": "0.301.0",
16
+ "@webstudio-is/image": "0.303.0",
17
+ "@webstudio-is/react-sdk": "0.303.0",
18
+ "@webstudio-is/sdk": "0.303.0",
19
+ "@webstudio-is/sdk-components-react": "0.303.0",
20
+ "@webstudio-is/sdk-components-animation": "0.303.0",
21
+ "@webstudio-is/sdk-components-react-radix": "0.303.0",
22
+ "@webstudio-is/sdk-components-react-remix": "0.303.0",
23
+ "@webstudio-is/wsauth": "0.303.0",
24
24
  "isbot": "^5.1.25",
25
25
  "react": "18.3.0-canary-14898b6a9-20240318",
26
26
  "react-dom": "18.3.0-canary-14898b6a9-20240318",
@@ -15,14 +15,14 @@
15
15
  "@react-router/fs-routes": "^7.5.3",
16
16
  "@react-router/node": "^7.5.3",
17
17
  "@react-router/serve": "^7.5.3",
18
- "@webstudio-is/image": "0.301.0",
19
- "@webstudio-is/react-sdk": "0.301.0",
20
- "@webstudio-is/sdk": "0.301.0",
21
- "@webstudio-is/sdk-components-animation": "0.301.0",
22
- "@webstudio-is/sdk-components-react-radix": "0.301.0",
23
- "@webstudio-is/sdk-components-react-router": "0.301.0",
24
- "@webstudio-is/sdk-components-react": "0.301.0",
25
- "@webstudio-is/wsauth": "0.301.0",
18
+ "@webstudio-is/image": "0.303.0",
19
+ "@webstudio-is/react-sdk": "0.303.0",
20
+ "@webstudio-is/sdk": "0.303.0",
21
+ "@webstudio-is/sdk-components-animation": "0.303.0",
22
+ "@webstudio-is/sdk-components-react-radix": "0.303.0",
23
+ "@webstudio-is/sdk-components-react-router": "0.303.0",
24
+ "@webstudio-is/sdk-components-react": "0.303.0",
25
+ "@webstudio-is/wsauth": "0.303.0",
26
26
  "isbot": "^5.1.25",
27
27
  "react": "18.3.0-canary-14898b6a9-20240318",
28
28
  "react-dom": "18.3.0-canary-14898b6a9-20240318",
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "dependencies": {
9
9
  "@cloudflare/vite-plugin": "^1.1.0",
10
- "@webstudio-is/wsauth": "0.301.0",
10
+ "@webstudio-is/wsauth": "0.303.0",
11
11
  "wrangler": "^4.14.1"
12
12
  }
13
13
  }
@@ -11,12 +11,12 @@
11
11
  "dependencies": {
12
12
  "@shikijs/langs": "4.4.1",
13
13
  "@shikijs/themes": "4.4.1",
14
- "@webstudio-is/image": "0.301.0",
15
- "@webstudio-is/react-sdk": "0.301.0",
16
- "@webstudio-is/sdk": "0.301.0",
17
- "@webstudio-is/sdk-components-react": "0.301.0",
18
- "@webstudio-is/sdk-components-animation": "0.301.0",
19
- "@webstudio-is/sdk-components-react-radix": "0.301.0",
14
+ "@webstudio-is/image": "0.303.0",
15
+ "@webstudio-is/react-sdk": "0.303.0",
16
+ "@webstudio-is/sdk": "0.303.0",
17
+ "@webstudio-is/sdk-components-react": "0.303.0",
18
+ "@webstudio-is/sdk-components-animation": "0.303.0",
19
+ "@webstudio-is/sdk-components-react-radix": "0.303.0",
20
20
  "react": "18.3.0-canary-14898b6a9-20240318",
21
21
  "react-dom": "18.3.0-canary-14898b6a9-20240318",
22
22
  "shiki": "4.4.1",