@bigcommerce/catalyst 1.2.0 → 1.3.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/dist/cli.js CHANGED
@@ -25,7 +25,7 @@ import { randomBytes, randomUUID } from "crypto";
25
25
  // package.json
26
26
  var package_default = {
27
27
  name: "@bigcommerce/catalyst",
28
- version: "1.2.0",
28
+ version: "1.3.0",
29
29
  type: "module",
30
30
  repository: {
31
31
  type: "git",
@@ -594,9 +594,9 @@ async function deleteProject(projectUuid, storeHash, accessToken, apiHost) {
594
594
  // src/cli/lib/project-config.ts
595
595
  import Conf2 from "conf";
596
596
  import { join } from "path";
597
- function getProjectConfig() {
597
+ function getProjectConfig(cwd = process.cwd()) {
598
598
  return new Conf2({
599
- cwd: join(process.cwd(), ".bigcommerce"),
599
+ cwd: join(cwd, ".bigcommerce"),
600
600
  projectSuffix: "",
601
601
  configName: "project",
602
602
  schema: {
@@ -647,7 +647,7 @@ var resolveProjectUuid = (options) => {
647
647
  const projectUuid = options.projectUuid ?? config2.get("projectUuid");
648
648
  if (!projectUuid) {
649
649
  throw new Error(
650
- "Project UUID is required. Please run either `catalyst project link` or `catalyst project create` or this command again with --project-uuid <uuid>."
650
+ "Project UUID is required. Please run either `catalyst projects link` or `catalyst projects create` or this command again with --project-uuid <uuid>."
651
651
  );
652
652
  }
653
653
  return projectUuid;
@@ -703,8 +703,8 @@ Example:
703
703
  const store = await fetchStoreProfile2(storeHash, accessToken, apiHost);
704
704
  const projectUuid = config2.get("projectUuid");
705
705
  if (projectUuid) {
706
- const projects = await fetchProjects(storeHash, accessToken, apiHost);
707
- const linkedProject = projects.find((p) => p.uuid === projectUuid);
706
+ const projects2 = await fetchProjects(storeHash, accessToken, apiHost);
707
+ const linkedProject = projects2.find((p) => p.uuid === projectUuid);
708
708
  if (linkedProject) {
709
709
  consola.info(
710
710
  `Logged in to ${store.store_name} (${storeHash}), connected to project ${linkedProject.name} (${projectUuid})`
@@ -1023,6 +1023,10 @@ async function buildCatalystProject(projectUuid, wranglerVersion = WRANGLER_VERS
1023
1023
  cwd: coreDir
1024
1024
  }
1025
1025
  );
1026
+ await copyFile(
1027
+ join4(getModuleCliPath(), "templates", "public_headers"),
1028
+ join4(openNextOutDir, "assets", "_headers")
1029
+ );
1026
1030
  await execa2(
1027
1031
  "pnpm",
1028
1032
  [
@@ -1087,13 +1091,13 @@ Examples:
1087
1091
  const projectUuid = options.projectUuid ?? config2.get("projectUuid");
1088
1092
  if (!projectUuid) {
1089
1093
  throw new Error(
1090
- "Project UUID is required. Please run `catalyst project create` or `catalyst project link` or this command again with --project-uuid <uuid>."
1094
+ "Project UUID is required. Please run `catalyst projects create` or `catalyst projects link` or this command again with --project-uuid <uuid>."
1091
1095
  );
1092
1096
  }
1093
1097
  await buildCatalystProject(projectUuid, options.wranglerVersion);
1094
1098
  });
1095
1099
 
1096
- // src/cli/commands/channel.ts
1100
+ // src/cli/commands/channels.ts
1097
1101
  import { select as select4 } from "@inquirer/prompts";
1098
1102
  import { Command as Command3, InvalidArgumentError as InvalidArgumentError2, Option as Option3 } from "commander";
1099
1103
  import { colorize as colorize5 } from "consola/utils";
@@ -1123,8 +1127,8 @@ var channelsResponseSchema = z7.object({
1123
1127
  data: z7.array(channelSchema)
1124
1128
  });
1125
1129
  var CHANNEL_PLATFORM_ORDER = ["catalyst", "next", "bigcommerce"];
1126
- function sortChannelsByPlatform(channels) {
1127
- return [...channels].sort((a, b) => {
1130
+ function sortChannelsByPlatform(channels2) {
1131
+ return [...channels2].sort((a, b) => {
1128
1132
  const aIndex = CHANNEL_PLATFORM_ORDER.indexOf(a.platform);
1129
1133
  const bIndex = CHANNEL_PLATFORM_ORDER.indexOf(b.platform);
1130
1134
  if (aIndex === -1 && bIndex === -1) return 0;
@@ -1289,6 +1293,16 @@ var OPENNEXT_CLOUDFLARE_VERSION = "1.17.3";
1289
1293
  var corePackageJsonSchema = z8.looseObject({
1290
1294
  dependencies: z8.record(z8.string(), z8.string()).optional()
1291
1295
  });
1296
+ var projectJsonSchema2 = z8.looseObject({});
1297
+ var readProjectJson = (path) => {
1298
+ if (!existsSync3(path)) return {};
1299
+ try {
1300
+ const parsed = projectJsonSchema2.safeParse(JSON.parse(readFileSync2(path, "utf-8")));
1301
+ return parsed.success ? parsed.data : {};
1302
+ } catch {
1303
+ return {};
1304
+ }
1305
+ };
1292
1306
  var writeJson = (path, value) => {
1293
1307
  mkdirSync(dirname2(path), { recursive: true });
1294
1308
  writeFileSync(path, `${JSON.stringify(value, null, 2)}
@@ -1348,13 +1362,15 @@ var setupCommerceHosting = async ({
1348
1362
  "@opennextjs/cloudflare": OPENNEXT_CLOUDFLARE_VERSION
1349
1363
  };
1350
1364
  writeJson(corePackageJsonPath, sortPackageJsonFields(pkg));
1365
+ const projectJsonPath = join5(projectDir, ".bigcommerce", "project.json");
1351
1366
  const projectJson = {
1367
+ ...readProjectJson(projectJsonPath),
1352
1368
  projectUuid,
1353
1369
  framework: "catalyst"
1354
1370
  };
1355
1371
  if (storeHash) projectJson.storeHash = storeHash;
1356
1372
  if (accessToken) projectJson.accessToken = accessToken;
1357
- writeJson(join5(projectDir, ".bigcommerce", "project.json"), projectJson);
1373
+ writeJson(projectJsonPath, projectJson);
1358
1374
  convertProxyToMiddleware(projectDir);
1359
1375
  };
1360
1376
  var NoLinkedProjectError = class extends Error {
@@ -1410,7 +1426,7 @@ async function selectOrCreateInfrastructureProject(api, linkedProjectUuid) {
1410
1426
  }
1411
1427
  async function promptForCommerceHostingProject(api, defaultName, autoUseDefaultName, useExistingOnCollision) {
1412
1428
  const existingProjects = await fetchProjects(api.storeHash, api.accessToken, api.apiHost);
1413
- const takenNames = existingProjects.map((project2) => project2.name);
1429
+ const takenNames = existingProjects.map((project) => project.name);
1414
1430
  if (autoUseDefaultName) {
1415
1431
  return autoCreateCommerceHostingProject(
1416
1432
  api,
@@ -1420,7 +1436,7 @@ async function promptForCommerceHostingProject(api, defaultName, autoUseDefaultN
1420
1436
  );
1421
1437
  }
1422
1438
  const conflict = existingProjects.find(
1423
- (project2) => project2.name.toLowerCase() === defaultName.toLowerCase()
1439
+ (project) => project.name.toLowerCase() === defaultName.toLowerCase()
1424
1440
  );
1425
1441
  if (!conflict) {
1426
1442
  return autoCreateCommerceHostingProject(
@@ -1452,10 +1468,10 @@ async function promptForCommerceHostingProject(api, defaultName, autoUseDefaultN
1452
1468
  const selected = await select({
1453
1469
  message: "Which Commerce Hosting project would you like to use?",
1454
1470
  choices: [
1455
- ...existingProjects.map((project2) => ({
1456
- name: project2.name,
1457
- value: project2,
1458
- description: project2.uuid
1471
+ ...existingProjects.map((project) => ({
1472
+ name: project.name,
1473
+ value: project,
1474
+ description: project.uuid
1459
1475
  })),
1460
1476
  new Separator(),
1461
1477
  { name: "Create a new project", value: "create-new" }
@@ -1519,7 +1535,7 @@ async function resolveCollisionChoice(existingName, useExistingOnCollision) {
1519
1535
  }
1520
1536
  async function autoCreateCommerceHostingProject(api, name, existingProjects, useExistingOnCollision) {
1521
1537
  const existing = existingProjects.find(
1522
- (project2) => project2.name.toLowerCase() === name.toLowerCase()
1538
+ (project) => project.name.toLowerCase() === name.toLowerCase()
1523
1539
  );
1524
1540
  if (existing) {
1525
1541
  const shouldUseExisting = await resolveCollisionChoice(existing.name, useExistingOnCollision);
@@ -1556,8 +1572,8 @@ async function resolveProject(options) {
1556
1572
  apiHost: options.apiHost
1557
1573
  };
1558
1574
  if (options.projectUuid) {
1559
- const projects = await fetchProjects(api.storeHash, api.accessToken, api.apiHost);
1560
- const matched = projects.find((p) => p.uuid === options.projectUuid);
1575
+ const projects2 = await fetchProjects(api.storeHash, api.accessToken, api.apiHost);
1576
+ const matched = projects2.find((p) => p.uuid === options.projectUuid);
1561
1577
  if (matched) return matched;
1562
1578
  consola.warn(
1563
1579
  `Project ${options.projectUuid} not found on this store. Pick another to continue.`
@@ -1570,13 +1586,13 @@ async function resolveChannel(options) {
1570
1586
  return { id: options.channelId };
1571
1587
  }
1572
1588
  consola.start("Fetching channels...");
1573
- const channels = await fetchAvailableChannels(
1589
+ const channels2 = await fetchAvailableChannels(
1574
1590
  options.storeHash,
1575
1591
  options.accessToken,
1576
1592
  options.apiHost
1577
1593
  );
1578
1594
  consola.success("Channels fetched.");
1579
- const catalystChannels = channels.filter((c) => c.platform === "catalyst");
1595
+ const catalystChannels = channels2.filter((c) => c.platform === "catalyst");
1580
1596
  if (catalystChannels.length === 0) {
1581
1597
  throw new Error(
1582
1598
  "No Catalyst channels found on this store. Create one with `catalyst create` and try again."
@@ -1593,19 +1609,19 @@ async function resolveChannel(options) {
1593
1609
  const matched = catalystChannels.find((c) => c.id === id);
1594
1610
  return { id, name: matched?.name };
1595
1611
  }
1596
- async function resolveHostname(project2, options) {
1612
+ async function resolveHostname(project, options) {
1597
1613
  if (options.hostname) {
1598
1614
  return options.hostname;
1599
1615
  }
1600
- if (project2.deployment_hostnames.length === 0) {
1616
+ if (project.deployment_hostnames.length === 0) {
1601
1617
  throw new Error(
1602
- `Project "${project2.name}" has no deployment hostnames yet. Run \`catalyst deploy\` first to create one.`
1618
+ `Project "${project.name}" has no deployment hostnames yet. Run \`catalyst deploy\` first to create one.`
1603
1619
  );
1604
1620
  }
1605
1621
  const ordered = options.preferHostname ? [
1606
- ...project2.deployment_hostnames.filter((h) => h === options.preferHostname),
1607
- ...project2.deployment_hostnames.filter((h) => h !== options.preferHostname)
1608
- ] : project2.deployment_hostnames;
1622
+ ...project.deployment_hostnames.filter((h) => h === options.preferHostname),
1623
+ ...project.deployment_hostnames.filter((h) => h !== options.preferHostname)
1624
+ ] : project.deployment_hostnames;
1609
1625
  const selected = await select2({
1610
1626
  message: "Select the hostname to point the channel at.",
1611
1627
  choices: ordered.map((h) => ({ name: h, value: h }))
@@ -1613,18 +1629,18 @@ async function resolveHostname(project2, options) {
1613
1629
  return selected;
1614
1630
  }
1615
1631
  async function runChannelSiteUrlFlow(options) {
1616
- const project2 = await resolveProject(options);
1617
- const channel2 = await resolveChannel(options);
1618
- const hostname = await resolveHostname(project2, options);
1632
+ const project = await resolveProject(options);
1633
+ const channel = await resolveChannel(options);
1634
+ const hostname = await resolveHostname(project, options);
1619
1635
  const siteUrl = hostname.startsWith("https://") ? hostname : `https://${hostname}`;
1620
1636
  await updateChannelSiteUrl(
1621
- channel2.id,
1637
+ channel.id,
1622
1638
  siteUrl,
1623
1639
  options.storeHash,
1624
1640
  options.accessToken,
1625
1641
  options.apiHost
1626
1642
  );
1627
- const channelLabel = channel2.name ? `"${channel2.name}" (${channel2.id})` : String(channel2.id);
1643
+ const channelLabel = channel.name ? `"${channel.name}" (${channel.id})` : String(channel.id);
1628
1644
  consola.success(`Updated channel ${channelLabel} site URL to ${siteUrl}.`);
1629
1645
  }
1630
1646
 
@@ -1749,7 +1765,8 @@ async function runCreateChannelFlow(options) {
1749
1765
  { name: "No", value: false }
1750
1766
  ]
1751
1767
  });
1752
- return createChannel(
1768
+ consola.start("Creating channel...");
1769
+ const channel = await createChannel(
1753
1770
  name,
1754
1771
  storefrontLocale,
1755
1772
  additionalLocales,
@@ -1758,6 +1775,8 @@ async function runCreateChannelFlow(options) {
1758
1775
  accessToken,
1759
1776
  cliApiOrigin
1760
1777
  );
1778
+ consola.success("Channel created.");
1779
+ return channel;
1761
1780
  }
1762
1781
 
1763
1782
  // src/cli/lib/env-config.ts
@@ -1874,7 +1893,7 @@ var writeEnv = (projectDir, envVars) => {
1874
1893
  `);
1875
1894
  };
1876
1895
 
1877
- // src/cli/commands/channel.ts
1896
+ // src/cli/commands/channels.ts
1878
1897
  var parseChannelId = (value) => {
1879
1898
  const parsed = Number.parseInt(value, 10);
1880
1899
  if (Number.isNaN(parsed)) {
@@ -1907,10 +1926,10 @@ var update = new Command3("update").configureHelp({ showGlobalOptions: true }).d
1907
1926
  `
1908
1927
  Examples:
1909
1928
  # Pick a channel and hostname interactively
1910
- $ catalyst channel update
1929
+ $ catalyst channels update
1911
1930
 
1912
1931
  # Skip both prompts
1913
- $ catalyst channel update --channel-id 123 --hostname my-storefront.example.com`
1932
+ $ catalyst channels update --channel-id 123 --hostname my-storefront.example.com`
1914
1933
  ).addOption(storeHashOption()).addOption(accessTokenOption()).addOption(apiHostOption()).addOption(projectUuidOption()).addOption(
1915
1934
  new Option3(
1916
1935
  "--channel-id <id>",
@@ -1938,7 +1957,7 @@ Examples:
1938
1957
  } catch (error) {
1939
1958
  if (error instanceof NoLinkedProjectError) {
1940
1959
  consola.info(
1941
- "When you're ready to create a project, run `catalyst project create` or re-run `catalyst channel update`."
1960
+ "When you're ready to create a project, run `catalyst projects create` or re-run `catalyst channels update`."
1942
1961
  );
1943
1962
  process.exit(0);
1944
1963
  return;
@@ -1954,13 +1973,13 @@ var link = new Command3("link").configureHelp({ showGlobalOptions: true }).descr
1954
1973
  `
1955
1974
  Examples:
1956
1975
  # Pick a channel interactively (logs you in if needed)
1957
- $ catalyst channel link
1976
+ $ catalyst channels link
1958
1977
 
1959
1978
  # Non-interactive
1960
- $ catalyst channel link --store-hash <hash> --access-token <token> --channel-id 123
1979
+ $ catalyst channels link --store-hash <hash> --access-token <token> --channel-id 123
1961
1980
 
1962
1981
  # Append extra environment variables to .env.local
1963
- $ catalyst channel link --channel-id 123 --env MY_FLAG=1`
1982
+ $ catalyst channels link --channel-id 123 --env MY_FLAG=1`
1964
1983
  ).addOption(storeHashOption()).addOption(accessTokenOption()).addOption(apiHostOption()).addOption(loginUrlOption()).addOption(
1965
1984
  new Option3("--channel-id <id>", "Link this channel directly, skipping the picker.").argParser(
1966
1985
  parseChannelId
@@ -1976,7 +1995,7 @@ Examples:
1976
1995
  const credentials = await resolveCredentialsWithLogin(options, config2, apiHost);
1977
1996
  if (!credentials) {
1978
1997
  consola.info(
1979
- "Login aborted. Re-run `catalyst channel link` when you have your credentials ready."
1998
+ "Login aborted. Re-run `catalyst channels link` when you have your credentials ready."
1980
1999
  );
1981
2000
  process.exit(0);
1982
2001
  return;
@@ -1986,8 +2005,8 @@ Examples:
1986
2005
  let channelId = options.channelId;
1987
2006
  let channelName;
1988
2007
  if (channelId === void 0) {
1989
- const channels = await fetchAvailableChannels(storeHash, accessToken, apiHost);
1990
- if (channels.length === 0) {
2008
+ const availableChannels = await fetchAvailableChannels(storeHash, accessToken, apiHost);
2009
+ if (availableChannels.length === 0) {
1991
2010
  consola.info(
1992
2011
  "No storefront channels found on this store. Create one with `catalyst create` and try again."
1993
2012
  );
@@ -1996,13 +2015,13 @@ Examples:
1996
2015
  }
1997
2016
  channelId = await select4({
1998
2017
  message: "Which channel would you like to link?",
1999
- choices: sortChannelsByPlatform(channels).map((c) => ({
2018
+ choices: sortChannelsByPlatform(availableChannels).map((c) => ({
2000
2019
  name: c.name,
2001
2020
  value: c.id,
2002
2021
  description: channelPlatformLabel(c.platform)
2003
2022
  }))
2004
2023
  });
2005
- channelName = channels.find((c) => c.id === channelId)?.name;
2024
+ channelName = availableChannels.find((c) => c.id === channelId)?.name;
2006
2025
  }
2007
2026
  const initData = await getChannelInit(channelId, storeHash, accessToken, options.cliApiOrigin);
2008
2027
  const envVars = { ...initData.envVars };
@@ -2027,13 +2046,13 @@ var create = new Command3("create").configureHelp({ showGlobalOptions: true }).d
2027
2046
  `
2028
2047
  Examples:
2029
2048
  # Create a channel interactively (logs you in if needed)
2030
- $ catalyst channel create
2049
+ $ catalyst channels create
2031
2050
 
2032
2051
  # Non-interactive, and link it to this project afterwards
2033
- $ catalyst channel create --name "My Store" --locale en --no-sample-data --link
2052
+ $ catalyst channels create --name "My Store" --locale en --no-sample-data --link
2034
2053
 
2035
2054
  # Add additional storefront languages (max 4)
2036
- $ catalyst channel create --name "My Store" --additional-locales es fr`
2055
+ $ catalyst channels create --name "My Store" --additional-locales es fr`
2037
2056
  ).addOption(storeHashOption()).addOption(accessTokenOption()).addOption(apiHostOption()).addOption(loginUrlOption()).option("--name <name>", "Name for the new channel. Skips the name prompt.").option(
2038
2057
  "--locale <locale>",
2039
2058
  'Default storefront locale (e.g. "en"). Skips the default-language prompt.'
@@ -2056,7 +2075,7 @@ Examples:
2056
2075
  const credentials = await resolveCredentialsWithLogin(options, config2, apiHost);
2057
2076
  if (!credentials) {
2058
2077
  consola.info(
2059
- "Login aborted. Re-run `catalyst channel create` when you have your credentials ready."
2078
+ "Login aborted. Re-run `catalyst channels create` when you have your credentials ready."
2060
2079
  );
2061
2080
  process.exit(0);
2062
2081
  return;
@@ -2101,7 +2120,7 @@ Examples:
2101
2120
  }
2102
2121
  process.exit(0);
2103
2122
  });
2104
- var channel = new Command3("channel").configureHelp({ showGlobalOptions: true }).description("Manage BigCommerce channels.").addCommand(create).addCommand(link).addCommand(update);
2123
+ var channels = new Command3("channels").alias("channel").configureHelp({ showGlobalOptions: true }).description("Manage BigCommerce channels.").addCommand(create).addCommand(link).addCommand(update);
2105
2124
 
2106
2125
  // src/cli/commands/create.ts
2107
2126
  import { Command as Command4, InvalidArgumentError as InvalidArgumentError3, Option as Option4 } from "@commander-js/extra-typings";
@@ -2110,10 +2129,27 @@ import { execSync as execSync2 } from "child_process";
2110
2129
  import { colorize as colorize6 } from "consola/utils";
2111
2130
  import { pathExistsSync } from "fs-extra/esm";
2112
2131
  import kebabCase from "lodash.kebabcase";
2113
- import { join as join10 } from "path";
2132
+ import { join as join11 } from "path";
2114
2133
 
2115
2134
  // src/cli/lib/detect-package-manager.ts
2135
+ import { access } from "fs/promises";
2136
+ import { join as join7 } from "path";
2116
2137
  import { detectPackageManager as detectFromDir } from "nypm";
2138
+ var PROJECT_LOCKFILES = [
2139
+ ["pnpm-lock.yaml", "pnpm"],
2140
+ ["yarn.lock", "yarn"],
2141
+ ["bun.lock", "bun"],
2142
+ ["bun.lockb", "bun"],
2143
+ ["package-lock.json", "npm"]
2144
+ ];
2145
+ var detectLockfileManager = async (dir) => {
2146
+ const found = await Promise.all(
2147
+ PROJECT_LOCKFILES.map(
2148
+ async ([file, manager]) => access(join7(dir, file)).then(() => manager).catch(() => null)
2149
+ )
2150
+ );
2151
+ return found.find((manager) => manager !== null) ?? null;
2152
+ };
2117
2153
  var detectPackageManager = () => {
2118
2154
  const userAgent = process.env.npm_config_user_agent ?? "";
2119
2155
  const name = userAgent.split(" ")[0]?.split("/")[0];
@@ -2189,7 +2225,7 @@ var extractCatalyst = async ({
2189
2225
  // src/cli/lib/init-git-repo.ts
2190
2226
  import { execSync } from "child_process";
2191
2227
  import { rmSync } from "fs";
2192
- import { join as join7 } from "path";
2228
+ import { join as join8 } from "path";
2193
2229
  var run = (command, cwd) => execSync(command, { cwd, stdio: "ignore" });
2194
2230
  var initGitRepo = (projectDir) => {
2195
2231
  try {
@@ -2198,7 +2234,7 @@ var initGitRepo = (projectDir) => {
2198
2234
  run('git commit -m "Initial commit from Catalyst"', projectDir);
2199
2235
  consola.success("Initialized a git repository.");
2200
2236
  } catch {
2201
- rmSync(join7(projectDir, ".git"), { recursive: true, force: true });
2237
+ rmSync(join8(projectDir, ".git"), { recursive: true, force: true });
2202
2238
  consola.warn("Could not initialize a git repository. Skipping.");
2203
2239
  }
2204
2240
  };
@@ -2242,7 +2278,7 @@ var installDependencies = async (projectDir, packageManager = "pnpm") => {
2242
2278
 
2243
2279
  // src/cli/lib/rewrite-core-package.ts
2244
2280
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
2245
- import { join as join8 } from "path";
2281
+ import { join as join9 } from "path";
2246
2282
  import { z as z10 } from "zod";
2247
2283
  var NATIVE_BUILD_ALLOWLIST = [
2248
2284
  "@parcel/watcher",
@@ -2284,7 +2320,7 @@ var resolvePublishedVersion = async (pkgName) => {
2284
2320
  return `^${version2}`;
2285
2321
  };
2286
2322
  var rewriteCorePackage = async (projectDir, ref) => {
2287
- const packageJsonPath = join8(projectDir, "package.json");
2323
+ const packageJsonPath = join9(projectDir, "package.json");
2288
2324
  const pkg = corePackageJsonSchema2.parse(JSON.parse(readFileSync4(packageJsonPath, "utf-8")));
2289
2325
  const workspaceDeps = DEP_FIELDS.flatMap((field) => {
2290
2326
  const deps = pkg[field];
@@ -2309,12 +2345,12 @@ var rewriteCorePackage = async (projectDir, ref) => {
2309
2345
  pkg.catalyst = { ...pkg.catalyst, version: pkg.version, ref };
2310
2346
  writeFileSync2(packageJsonPath, `${JSON.stringify(sortPackageJsonFields(pkg), null, 2)}
2311
2347
  `);
2312
- writeFileSync2(join8(projectDir, ".npmrc"), "legacy-peer-deps=true\n");
2348
+ writeFileSync2(join9(projectDir, ".npmrc"), "legacy-peer-deps=true\n");
2313
2349
  };
2314
2350
 
2315
2351
  // src/cli/lib/setup-core-project.ts
2316
2352
  import { mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
2317
- import { dirname as dirname3, join as join9 } from "path";
2353
+ import { dirname as dirname3, join as join10 } from "path";
2318
2354
  import { z as z11 } from "zod";
2319
2355
  var corePackageJsonSchema3 = z11.looseObject({
2320
2356
  scripts: z11.record(z11.string(), z11.string()).optional(),
@@ -2326,7 +2362,7 @@ var writeJson2 = (path, value) => {
2326
2362
  `);
2327
2363
  };
2328
2364
  var setupCoreProject = (projectDir) => {
2329
- const corePackageJsonPath = join9(projectDir, "package.json");
2365
+ const corePackageJsonPath = join10(projectDir, "package.json");
2330
2366
  const pkg = corePackageJsonSchema3.parse(JSON.parse(readFileSync5(corePackageJsonPath, "utf-8")));
2331
2367
  pkg.scripts = {
2332
2368
  ...pkg.scripts,
@@ -2366,10 +2402,10 @@ function parseEnvFlag(value, previous = {}) {
2366
2402
  return { ...previous, [key]: val };
2367
2403
  }
2368
2404
  async function handleChannelSelection(storeHash, accessToken, apiHost) {
2369
- const channels = await fetchAvailableChannels(storeHash, accessToken, apiHost);
2405
+ const channels2 = await fetchAvailableChannels(storeHash, accessToken, apiHost);
2370
2406
  const existingChannel = await select5({
2371
2407
  message: "Which channel would you like to use?",
2372
- choices: sortChannelsByPlatform(channels).map((ch) => ({
2408
+ choices: sortChannelsByPlatform(channels2).map((ch) => ({
2373
2409
  name: ch.name,
2374
2410
  value: ch,
2375
2411
  description: `Channel Platform: ${channelPlatformLabel(ch.platform)}`
@@ -2385,7 +2421,7 @@ async function setupProject(options) {
2385
2421
  }
2386
2422
  if (projectName) {
2387
2423
  projectName = kebabCase(projectName);
2388
- projectDir = join10(options.projectDir, projectName);
2424
+ projectDir = join11(options.projectDir, projectName);
2389
2425
  if (pathExistsSync(projectDir)) {
2390
2426
  consola.error(`${projectDir} already exists`);
2391
2427
  process.exit(1);
@@ -2395,7 +2431,7 @@ async function setupProject(options) {
2395
2431
  const validateProjectName = (i) => {
2396
2432
  const formatted = kebabCase(i);
2397
2433
  if (!formatted) return "Project name is required";
2398
- const targetDir = join10(options.projectDir, formatted);
2434
+ const targetDir = join11(options.projectDir, formatted);
2399
2435
  if (pathExistsSync(targetDir)) return `Destination '${targetDir}' already exists`;
2400
2436
  projectName = formatted;
2401
2437
  projectDir = targetDir;
@@ -2578,6 +2614,11 @@ Examples:
2578
2614
  accessToken
2579
2615
  });
2580
2616
  }
2617
+ if (storeHash && accessToken) {
2618
+ const projectConfig = getProjectConfig(projectDir);
2619
+ projectConfig.set("storeHash", storeHash);
2620
+ projectConfig.set("accessToken", accessToken);
2621
+ }
2581
2622
  writeEnv(projectDir, envVars);
2582
2623
  await installDependencies(projectDir, packageManager);
2583
2624
  initGitRepo(projectDir);
@@ -2610,7 +2651,7 @@ import { Command as Command5, Option as Option5 } from "commander";
2610
2651
  import { parse } from "dotenv";
2611
2652
  import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
2612
2653
  import { release } from "os";
2613
- import { dirname as dirname4, join as join11 } from "path";
2654
+ import { dirname as dirname4, join as join12 } from "path";
2614
2655
  import { z as z12 } from "zod";
2615
2656
  var CLI_ENV_VARS = [
2616
2657
  "CATALYST_STORE_HASH",
@@ -2627,7 +2668,7 @@ var BUILD_ENV_VARS = [
2627
2668
  "AUTH_SECRET"
2628
2669
  ];
2629
2670
  var ENV_FILES = [".env.local", ".env"];
2630
- var PROJECT_LOCKFILES = [
2671
+ var PROJECT_LOCKFILES2 = [
2631
2672
  ["pnpm-lock.yaml", "pnpm"],
2632
2673
  ["yarn.lock", "yarn"],
2633
2674
  ["bun.lock", "bun"],
@@ -2638,7 +2679,7 @@ var hasValue = (value) => value !== void 0 && value.trim() !== "";
2638
2679
  var buildEnvLayers = (cwd, env2) => {
2639
2680
  const layers = [{ source: "process.env", values: env2 }];
2640
2681
  ENV_FILES.forEach((file) => {
2641
- const path = join11(cwd, file);
2682
+ const path = join12(cwd, file);
2642
2683
  if (!existsSync5(path)) {
2643
2684
  return;
2644
2685
  }
@@ -2661,7 +2702,7 @@ var resolveValue = (envSource, configValue) => {
2661
2702
  return { present: false, source: "unset" };
2662
2703
  };
2663
2704
  var asString = (value) => typeof value === "string" ? value : void 0;
2664
- var lockfileManager = (dir) => PROJECT_LOCKFILES.find(([file]) => existsSync5(join11(dir, file)))?.[1];
2705
+ var lockfileManager = (dir) => PROJECT_LOCKFILES2.find(([file]) => existsSync5(join12(dir, file)))?.[1];
2665
2706
  var detectProjectPackageManager2 = (cwd) => {
2666
2707
  let dir = cwd;
2667
2708
  for (; ; ) {
@@ -2676,7 +2717,7 @@ var detectProjectPackageManager2 = (cwd) => {
2676
2717
  dir = parent;
2677
2718
  }
2678
2719
  };
2679
- var projectJsonSchema2 = z12.looseObject({});
2720
+ var projectJsonSchema3 = z12.looseObject({});
2680
2721
  var safeReadJson2 = (path) => {
2681
2722
  try {
2682
2723
  return JSON.parse(readFileSync6(path, "utf-8"));
@@ -2684,9 +2725,9 @@ var safeReadJson2 = (path) => {
2684
2725
  return null;
2685
2726
  }
2686
2727
  };
2687
- var readProjectJson = (cwd) => {
2688
- const result = projectJsonSchema2.safeParse(
2689
- safeReadJson2(join11(cwd, ".bigcommerce", "project.json"))
2728
+ var readProjectJson2 = (cwd) => {
2729
+ const result = projectJsonSchema3.safeParse(
2730
+ safeReadJson2(join12(cwd, ".bigcommerce", "project.json"))
2690
2731
  );
2691
2732
  return result.success ? result.data : null;
2692
2733
  };
@@ -2696,7 +2737,7 @@ var storefrontPackageSchema = z12.looseObject({
2696
2737
  catalyst: z12.looseObject({ version: z12.string().optional() }).optional()
2697
2738
  });
2698
2739
  var readStorefrontInfo = (cwd) => {
2699
- const result = storefrontPackageSchema.safeParse(safeReadJson2(join11(cwd, "package.json")));
2740
+ const result = storefrontPackageSchema.safeParse(safeReadJson2(join12(cwd, "package.json")));
2700
2741
  if (!result.success) {
2701
2742
  return { name: null, version: null };
2702
2743
  }
@@ -2718,7 +2759,7 @@ function collectDiagnostics({
2718
2759
  } = {}) {
2719
2760
  const state = getProjectState(cwd);
2720
2761
  const telemetry2 = getTelemetry();
2721
- const projectJson = readProjectJson(cwd);
2762
+ const projectJson = readProjectJson2(cwd);
2722
2763
  const storefront = readStorefrontInfo(cwd);
2723
2764
  const cliEnv = [{ source: "process.env", values: env2 }];
2724
2765
  const buildEnv = buildEnvLayers(cwd, env2);
@@ -2773,12 +2814,12 @@ function collectDiagnostics({
2773
2814
  correlationId: telemetry2.correlationId
2774
2815
  },
2775
2816
  files: {
2776
- ".env.local": existsSync5(join11(cwd, ".env.local")),
2777
- ".env": existsSync5(join11(cwd, ".env")),
2778
- ".bigcommerce/project.json": existsSync5(join11(cwd, ".bigcommerce", "project.json")),
2779
- ".bigcommerce/wrangler.jsonc": existsSync5(join11(cwd, ".bigcommerce", "wrangler.jsonc")),
2780
- ".open-next/": existsSync5(join11(cwd, ".open-next")),
2781
- "package.json": existsSync5(join11(cwd, "package.json"))
2817
+ ".env.local": existsSync5(join12(cwd, ".env.local")),
2818
+ ".env": existsSync5(join12(cwd, ".env")),
2819
+ ".bigcommerce/project.json": existsSync5(join12(cwd, ".bigcommerce", "project.json")),
2820
+ ".bigcommerce/wrangler.jsonc": existsSync5(join12(cwd, ".bigcommerce", "wrangler.jsonc")),
2821
+ ".open-next/": existsSync5(join12(cwd, ".open-next")),
2822
+ "package.json": existsSync5(join12(cwd, "package.json"))
2782
2823
  }
2783
2824
  };
2784
2825
  }
@@ -2869,8 +2910,8 @@ import { confirm as confirm3 } from "@inquirer/prompts";
2869
2910
  import AdmZip from "adm-zip";
2870
2911
  import { Command as Command6, Option as Option6 } from "commander";
2871
2912
  import { colorize as colorize7 } from "consola/utils";
2872
- import { access, readdir, readFile } from "fs/promises";
2873
- import { join as join12 } from "path";
2913
+ import { access as access2, readdir, readFile } from "fs/promises";
2914
+ import { join as join13 } from "path";
2874
2915
  import yoctoSpinner3 from "yocto-spinner";
2875
2916
  import { z as z13 } from "zod";
2876
2917
 
@@ -2942,10 +2983,10 @@ var DeploymentStatusSchema = z13.object({
2942
2983
  });
2943
2984
  var generateBundleZip = async () => {
2944
2985
  consola.info("Generating bundle...");
2945
- const bigcommerceDir = join12(process.cwd(), ".bigcommerce");
2946
- const distDir = join12(process.cwd(), ".bigcommerce", "dist");
2986
+ const bigcommerceDir = join13(process.cwd(), ".bigcommerce");
2987
+ const distDir = join13(process.cwd(), ".bigcommerce", "dist");
2947
2988
  try {
2948
- await access(distDir);
2989
+ await access2(distDir);
2949
2990
  } catch {
2950
2991
  throw new Error(`Dist directory not found: ${distDir}`);
2951
2992
  }
@@ -2953,7 +2994,7 @@ var generateBundleZip = async () => {
2953
2994
  if (buildDirContents.length === 0) {
2954
2995
  throw new Error(`Dist directory is empty: ${distDir}`);
2955
2996
  }
2956
- const outputZip = join12(bigcommerceDir, "bundle.zip");
2997
+ const outputZip = join13(bigcommerceDir, "bundle.zip");
2957
2998
  const zip = new AdmZip();
2958
2999
  zip.addLocalFolder(distDir, "output");
2959
3000
  zip.writeZip(outputZip);
@@ -2985,7 +3026,7 @@ var generateUploadSignature = async (storeHash, accessToken, apiHost) => {
2985
3026
  };
2986
3027
  var uploadBundleZip = async (uploadUrl) => {
2987
3028
  consola.info("Uploading bundle...");
2988
- const zipPath = join12(process.cwd(), ".bigcommerce", "bundle.zip");
3029
+ const zipPath = join13(process.cwd(), ".bigcommerce", "bundle.zip");
2989
3030
  const fileBuffer = await readFile(zipPath);
2990
3031
  const response = await fetch(uploadUrl, {
2991
3032
  method: "PUT",
@@ -3120,7 +3161,7 @@ var fetchProject = async (projectUuid, storeHash, accessToken, apiHost) => {
3120
3161
  }
3121
3162
  const res = await response.json();
3122
3163
  const { data } = ProjectSchema.parse(res);
3123
- return data.find((project2) => project2.uuid === projectUuid);
3164
+ return data.find((project) => project.uuid === projectUuid);
3124
3165
  };
3125
3166
  var deploy = new Command6("deploy").configureHelp({ showGlobalOptions: true }).description("Deploy your application to Cloudflare.").addHelpText(
3126
3167
  "after",
@@ -3166,7 +3207,7 @@ Example:
3166
3207
  } catch (error) {
3167
3208
  if (error instanceof NoLinkedProjectError) {
3168
3209
  consola.info(
3169
- "When you're ready to create a project, run `catalyst project create` or re-run `catalyst deploy`."
3210
+ "When you're ready to create a project, run `catalyst projects create` or re-run `catalyst deploy`."
3170
3211
  );
3171
3212
  process.exit(0);
3172
3213
  }
@@ -3211,9 +3252,9 @@ Example:
3211
3252
  await cleanupCloudflareIncompatibilities(process.cwd());
3212
3253
  }
3213
3254
  if (options.prebuilt) {
3214
- const distDir = join12(process.cwd(), ".bigcommerce", "dist");
3255
+ const distDir = join13(process.cwd(), ".bigcommerce", "dist");
3215
3256
  try {
3216
- await access(distDir);
3257
+ await access2(distDir);
3217
3258
  } catch {
3218
3259
  throw new Error(
3219
3260
  "No build output found at .bigcommerce/dist/. Run `catalyst build` first or remove `--prebuilt` to build automatically."
@@ -3295,6 +3336,7 @@ var LOG_LEVELS = ["debug", "info", "warn", "error"];
3295
3336
  var SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1e3;
3296
3337
  var logEntrySchema = z14.object({
3297
3338
  timestamp: z14.string().nullable().optional(),
3339
+ entry_type: z14.string().optional(),
3298
3340
  level: z14.string().optional(),
3299
3341
  messages: z14.array(z14.unknown()).optional(),
3300
3342
  is_exception: z14.boolean().optional(),
@@ -3411,8 +3453,16 @@ function isRedundantExceptionName(name, message) {
3411
3453
  return parts.length > 0 && parts.every((part) => message.includes(part));
3412
3454
  }
3413
3455
  var stringifyMessage = (message) => typeof message === "string" ? message : JSON.stringify(message);
3456
+ var joinMessages = (messages) => (messages ?? []).map(stringifyMessage).join(" ").replace(/\s+/g, " ").trim();
3457
+ function isRequestLogEntry(entry) {
3458
+ if (entry.entry_type != null) return entry.entry_type === "request";
3459
+ const method = entry.request?.method;
3460
+ const url = entry.request?.url;
3461
+ if (!method || !url) return false;
3462
+ return joinMessages(entry.messages) === `${method} ${url}`;
3463
+ }
3414
3464
  function formatLogEntry(entry, format = "default") {
3415
- const message = (entry.messages ?? []).map(stringifyMessage).join(" ").replace(/\s+/g, " ").trim();
3465
+ const message = joinMessages(entry.messages);
3416
3466
  if (format === "short") return message;
3417
3467
  const level = entry.level ?? "unknown";
3418
3468
  const coloredLevel = colorize8(LEVEL_COLORS[level.toLowerCase()] ?? "white", level.toUpperCase());
@@ -3425,12 +3475,16 @@ function formatLogEntry(entry, format = "default") {
3425
3475
  const status = entry.request.status_code ? ` (${entry.request.status_code})` : "";
3426
3476
  if (parts.length > 0) requestStr = ` ${parts.join(" ")}${status}`;
3427
3477
  }
3428
- return `[${entry.timestamp ?? UNKNOWN_TIME}] [${coloredLevel}]${requestStr}${exceptionStr} ${message}`;
3478
+ if (format === "request" && isRequestLogEntry(entry)) {
3479
+ return `[${entry.timestamp ?? UNKNOWN_TIME}]${requestStr}`;
3480
+ }
3481
+ return `[${entry.timestamp ?? UNKNOWN_TIME}]${requestStr} [${coloredLevel}]${exceptionStr} ${message}`;
3429
3482
  }
3430
3483
  async function queryLogs(projectUuid, storeHash, accessToken, apiHost, params) {
3431
3484
  const search = new URLSearchParams();
3432
3485
  search.set("start", params.start);
3433
3486
  search.set("end", params.end);
3487
+ if (params.entryType) search.set("entry_type", params.entryType);
3434
3488
  if (params.method) search.set("method", params.method);
3435
3489
  if (params.statusCode != null) search.set("status_code", String(params.statusCode));
3436
3490
  if (params.urlLike) search.set("url:like", params.urlLike);
@@ -3473,11 +3527,18 @@ async function queryLogs(projectUuid, storeHash, accessToken, apiHost, params) {
3473
3527
 
3474
3528
  // src/cli/lib/domains.ts
3475
3529
  var domainStatusSchema = z15.enum(["pending", "verified", "failed", "unknown"]);
3530
+ var pointingRecordsSchema = z15.object({
3531
+ a_record_value: z15.string().nullish(),
3532
+ cname_record_value: z15.string().nullish()
3533
+ });
3476
3534
  var domainSchema = z15.object({
3477
3535
  domain: z15.string(),
3478
3536
  project_uuid: z15.string(),
3479
3537
  verification_status: domainStatusSchema
3480
3538
  });
3539
+ var createdDomainSchema = domainSchema.extend({
3540
+ pointing_records: pointingRecordsSchema.nullish()
3541
+ });
3481
3542
  var ownershipVerificationSchema = z15.object({
3482
3543
  type: z15.string(),
3483
3544
  name: z15.string(),
@@ -3518,6 +3579,9 @@ function parseBoundProjectUuid(body) {
3518
3579
  var domainResponseSchema = z15.object({
3519
3580
  data: domainSchema
3520
3581
  });
3582
+ var createdDomainResponseSchema = z15.object({
3583
+ data: createdDomainSchema
3584
+ });
3521
3585
  var domainListResponseSchema = z15.object({
3522
3586
  data: z15.array(domainSchema)
3523
3587
  });
@@ -3586,7 +3650,7 @@ async function createDomain(domain, projectUuid, storeHash, accessToken, apiHost
3586
3650
  });
3587
3651
  await assertDomainResponse(response, "Failed to add domain");
3588
3652
  const result = await response.json();
3589
- return domainResponseSchema.parse(result).data;
3653
+ return createdDomainResponseSchema.parse(result).data;
3590
3654
  }
3591
3655
  async function getDomain(domain, projectUuid, storeHash, accessToken, apiHost) {
3592
3656
  const response = await fetch(domainUrl(storeHash, projectUuid, domain, apiHost), {
@@ -3657,6 +3721,7 @@ async function transferDomain(domain, projectUuid, newProjectUuid, storeHash, ac
3657
3721
  var WAIT_INTERVAL_MS = 5e3;
3658
3722
  var WAIT_TIMEOUT_MS = 5 * 60 * 1e3;
3659
3723
  var DOMAIN_STATUS_FILTERS = ["pending", "verified", "failed"];
3724
+ var RECORD_TYPE_WIDTH = "CNAME".length;
3660
3725
  var STATUS_COLORS = {
3661
3726
  pending: "yellow",
3662
3727
  verified: "green",
@@ -3690,20 +3755,20 @@ async function resolveDestinationProject(domain, context, toProjectUuid) {
3690
3755
  return toProjectUuid;
3691
3756
  }
3692
3757
  consola.start("Fetching projects...");
3693
- const projects = await fetchProjects(context.storeHash, context.accessToken, context.apiHost);
3758
+ const projects2 = await fetchProjects(context.storeHash, context.accessToken, context.apiHost);
3694
3759
  consola.success("Projects fetched.");
3695
- const destinations = projects.filter((project2) => project2.uuid !== context.projectUuid);
3760
+ const destinations = projects2.filter((project) => project.uuid !== context.projectUuid);
3696
3761
  if (destinations.length === 0) {
3697
3762
  throw new UserActionableError(
3698
- "No other projects to transfer to. Create another project with `catalyst project create` first."
3763
+ "No other projects to transfer to. Create another project with `catalyst projects create` first."
3699
3764
  );
3700
3765
  }
3701
3766
  return select6({
3702
3767
  message: `Select the project to transfer ${domain} to (Press <enter> to select).`,
3703
- choices: destinations.map((project2) => ({
3704
- name: project2.name,
3705
- value: project2.uuid,
3706
- description: project2.uuid
3768
+ choices: destinations.map((project) => ({
3769
+ name: project.name,
3770
+ value: project.uuid,
3771
+ description: project.uuid
3707
3772
  }))
3708
3773
  });
3709
3774
  }
@@ -3713,6 +3778,19 @@ function formatDomainStatus(status) {
3713
3778
  function formatDomain(domain) {
3714
3779
  return `${domain.domain} ${formatDomainStatus(domain.verification_status)}`;
3715
3780
  }
3781
+ function formatPointingRecords(records) {
3782
+ const rows = [];
3783
+ if (records?.a_record_value) {
3784
+ rows.push(`${"A".padEnd(RECORD_TYPE_WIDTH)} ${records.a_record_value}`);
3785
+ }
3786
+ if (records?.cname_record_value) {
3787
+ rows.push(`${"CNAME".padEnd(RECORD_TYPE_WIDTH)} ${records.cname_record_value}`);
3788
+ }
3789
+ if (rows.length === 0) {
3790
+ return null;
3791
+ }
3792
+ return rows.map((row) => ` ${row}`).join("\n");
3793
+ }
3716
3794
  function formatOwnershipVerification(record) {
3717
3795
  const lines = [
3718
3796
  "Add this DNS record to verify ownership:",
@@ -3744,6 +3822,10 @@ async function waitForDomainVerification({
3744
3822
  var add = new Command7("add").configureHelp({ showGlobalOptions: true }).description("Add a custom domain to the current Native Hosting project.").argument("<domain>", "Custom domain to add.").addHelpText(
3745
3823
  "after",
3746
3824
  `
3825
+ The DNS records that point the domain at this project are printed once the domain
3826
+ is added. They are returned only by this command, so save them: publish the CNAME
3827
+ for a subdomain, or the A record for an apex domain.
3828
+
3747
3829
  Examples:
3748
3830
  $ catalyst domains add www.example.com
3749
3831
 
@@ -3753,9 +3835,9 @@ Examples:
3753
3835
  const context = resolveDomainCommandContext(options);
3754
3836
  await getTelemetry().identify(context.storeHash);
3755
3837
  consola.start(`Adding domain ${domain}...`);
3756
- let result;
3838
+ let created;
3757
3839
  try {
3758
- result = await createDomain(
3840
+ created = await createDomain(
3759
3841
  domain,
3760
3842
  context.projectUuid,
3761
3843
  context.storeHash,
@@ -3782,12 +3864,26 @@ Examples:
3782
3864
  }
3783
3865
  throw error;
3784
3866
  }
3785
- consola.success(`Domain ${result.domain} added.`);
3867
+ consola.success(`Domain ${created.domain} added.`);
3868
+ const records = formatPointingRecords(created.pointing_records);
3869
+ let result = created;
3786
3870
  if (options.wait && result.verification_status === "pending") {
3787
3871
  consola.start(`Waiting for ${result.domain} to verify...`);
3788
3872
  result = await waitForDomainVerification({ domain: result.domain, ...context });
3789
3873
  }
3790
3874
  consola.log(formatDomain(result));
3875
+ if (records) {
3876
+ consola.info(`Point ${created.domain} at this project with one of these DNS records:`);
3877
+ consola.log(records);
3878
+ consola.info(
3879
+ "Use the CNAME for a subdomain, or the A record for an apex domain. Save them now \u2014 only `domains add` returns them."
3880
+ );
3881
+ }
3882
+ if (result.verification_status === "pending") {
3883
+ consola.info(
3884
+ `Run \`catalyst domains status ${result.domain}\` to check verification progress.`
3885
+ );
3886
+ }
3791
3887
  process.exit(0);
3792
3888
  });
3793
3889
  var list = new Command7("list").configureHelp({ showGlobalOptions: true }).description("List custom domains for the current Native Hosting project.").addHelpText(
@@ -3927,14 +4023,14 @@ Examples:
3927
4023
  );
3928
4024
  if (!owned) {
3929
4025
  consola.start(`Locating the project that owns ${domain}...`);
3930
- const projects = await fetchProjects(context.storeHash, context.accessToken, context.apiHost);
3931
- const candidates = projects.filter((project2) => project2.uuid !== context.projectUuid);
4026
+ const projects2 = await fetchProjects(context.storeHash, context.accessToken, context.apiHost);
4027
+ const candidates = projects2.filter((project) => project.uuid !== context.projectUuid);
3932
4028
  const matches = await Promise.all(
3933
- candidates.map(async (project2) => ({
3934
- project: project2,
4029
+ candidates.map(async (project) => ({
4030
+ project,
3935
4031
  found: await findDomain(
3936
4032
  domain,
3937
- project2.uuid,
4033
+ project.uuid,
3938
4034
  context.storeHash,
3939
4035
  context.accessToken,
3940
4036
  context.apiHost
@@ -4029,7 +4125,7 @@ Examples:
4029
4125
  consola.success(`Domain ${current.domain} removed.`);
4030
4126
  process.exit(0);
4031
4127
  });
4032
- var domains = new Command7("domains").configureHelp({ showGlobalOptions: true }).description("Manage custom domains for the current Native Hosting project.").addCommand(add).addCommand(list).addCommand(showStatus).addCommand(claim).addCommand(transfer).addCommand(remove);
4128
+ var domains = new Command7("domains").alias("domain").configureHelp({ showGlobalOptions: true }).description("Manage custom domains for the current Native Hosting project.").addCommand(add).addCommand(list).addCommand(showStatus).addCommand(claim).addCommand(transfer).addCommand(remove);
4033
4129
 
4034
4130
  // src/cli/commands/env.ts
4035
4131
  import { Command as Command8 } from "commander";
@@ -4141,8 +4237,19 @@ var StreamError = class extends Error {
4141
4237
  }
4142
4238
  };
4143
4239
  var formatMessages = (messages) => messages.map((m) => typeof m === "string" ? m : JSON.stringify(m)).join(" ");
4240
+ var formatRequestLine = (event, entry) => {
4241
+ const { request } = event;
4242
+ const msg = entry ? formatMessages(entry.messages) : "";
4243
+ const msgPart = msg ? ` ${msg}` : "";
4244
+ const level = msg ? entry?.level.toUpperCase() : void 0;
4245
+ const levelPart = level ? ` [${colorize10(LEVEL_COLORS2[level] ?? "white", level)}]` : "";
4246
+ return `[${entry?.timestamp ?? event.timestamp}] ${request.method} ${request.url} (${request.status_code})${levelPart}${msgPart}`;
4247
+ };
4144
4248
  var formatLogEvent = (event, format) => {
4145
- const { request, logs: logEntries, exceptions } = event;
4249
+ const { logs: logEntries, exceptions } = event;
4250
+ if (format === "request" && logEntries.length === 0) {
4251
+ consola.log(formatRequestLine(event));
4252
+ }
4146
4253
  logEntries.forEach((entry) => {
4147
4254
  const msg = formatMessages(entry.messages);
4148
4255
  const level = entry.level.toUpperCase();
@@ -4152,9 +4259,7 @@ var formatLogEvent = (event, format) => {
4152
4259
  consola.log(msg);
4153
4260
  break;
4154
4261
  case "request":
4155
- consola.log(
4156
- `[${entry.timestamp}] [${coloredLevel}] ${request.method} ${request.url} (${request.status_code}) ${msg}`
4157
- );
4262
+ consola.log(formatRequestLine(event, entry));
4158
4263
  break;
4159
4264
  default:
4160
4265
  consola.log(`[${entry.timestamp}] [${coloredLevel}] ${msg}`);
@@ -4290,6 +4395,19 @@ var tailLogs = async (projectUuid, storeHash, accessToken, apiHost, format, conn
4290
4395
  var tail = new Command9("tail").configureHelp({ showGlobalOptions: true }).description("Tail live logs from your deployed application.").addHelpText(
4291
4396
  "after",
4292
4397
  `
4398
+ Formats:
4399
+ default One line per log message: timestamp, level, and message.
4400
+ short Message text only.
4401
+ request One line per log message: timestamp, request method, URL, status
4402
+ code, level, and message. Requests that logged no messages are
4403
+ printed too, without the level and message.
4404
+ pretty Indented JSON of the whole event.
4405
+ json Raw JSON, one event per line (useful for piping to other tools).
4406
+
4407
+ The \`default\` and \`short\` formats only print requests that produced a log
4408
+ message. Use \`--format request\` (or \`json\`/\`pretty\`) to see every request,
4409
+ including ones with no message body.
4410
+
4293
4411
  Examples:
4294
4412
  $ catalyst logs tail
4295
4413
 
@@ -4349,6 +4467,8 @@ function printPaginationHints(pagination, start2, end, options) {
4349
4467
  ...options.urlLike ? [`--url-like ${quoteArg(options.urlLike)}`] : [],
4350
4468
  ...options.levelMin ? [`--level-min ${options.levelMin}`] : [],
4351
4469
  ...options.limit != null ? [`--limit ${options.limit}`] : [],
4470
+ // The entry_type filter derives from --format (default/short request only
4471
+ // application logs), so echoing --format is enough to reproduce it.
4352
4472
  ...options.format !== "default" ? [`--format ${options.format}`] : []
4353
4473
  ];
4354
4474
  const command = `${invocationPrefix()} logs query ${baseFlags.join(" ")}`;
@@ -4375,6 +4495,10 @@ entries with \`--before\`. Cursors are only valid with the same window and
4375
4495
  filters they came from. With \`--format json\`, the pagination cursors are
4376
4496
  emitted as a final \`{"meta": ...}\` line after the entries.
4377
4497
 
4498
+ The \`default\` and \`short\` formats show only application log output,
4499
+ excluding Cloudflare's auto-generated per-request rows. Use
4500
+ \`--format request\` (or \`json\`/\`pretty\`) to include them.
4501
+
4378
4502
  Examples:
4379
4503
  # Last hour of logs
4380
4504
  $ catalyst logs query --since 1h
@@ -4432,9 +4556,11 @@ Examples:
4432
4556
  await getTelemetry().identify(storeHash);
4433
4557
  const projectUuid = resolveProjectUuid(options);
4434
4558
  const { start: start2, end } = resolveTimeWindow(options);
4559
+ const hideRequestRows = options.format === "default" || options.format === "short";
4435
4560
  const result = await queryLogs(projectUuid, storeHash, accessToken, apiHost, {
4436
4561
  start: start2,
4437
4562
  end,
4563
+ entryType: hideRequestRows ? "log" : void 0,
4438
4564
  method: options.method,
4439
4565
  statusCode: options.statusCode,
4440
4566
  urlLike: options.urlLike,
@@ -4466,9 +4592,12 @@ Examples:
4466
4592
  return;
4467
4593
  }
4468
4594
  const lineFormat = options.format;
4469
- ordered.forEach((entry) => consola.log(formatLogEntry(entry, lineFormat)));
4595
+ const visible = hideRequestRows ? ordered.filter((entry) => !isRequestLogEntry(entry)) : ordered;
4596
+ const hiddenCount = ordered.length - visible.length;
4597
+ visible.forEach((entry) => consola.log(formatLogEntry(entry, lineFormat)));
4598
+ const shownPart = `${visible.length} ${visible.length === 1 ? "entry" : "entries"} shown`;
4470
4599
  consola.info(
4471
- `${entries.length} ${entries.length === 1 ? "entry" : "entries"} shown (oldest first, times in UTC).`
4600
+ hiddenCount > 0 ? `${shownPart}, ${hiddenCount} request ${hiddenCount === 1 ? "row" : "rows"} hidden (oldest first, times in UTC). Use --format request to see request rows.` : `${shownPart} (oldest first, times in UTC).`
4472
4601
  );
4473
4602
  printPaginationHints(result.meta?.cursor_pagination, start2, end, options);
4474
4603
  } catch (error) {
@@ -4476,9 +4605,9 @@ Examples:
4476
4605
  process.exit(1);
4477
4606
  }
4478
4607
  });
4479
- var logs = new Command9("logs").configureHelp({ showGlobalOptions: true }).description("View logs from your deployed application.").addCommand(tail, { isDefault: true }).addCommand(query);
4608
+ var logs = new Command9("logs").alias("log").configureHelp({ showGlobalOptions: true }).description("View logs from your deployed application.").addCommand(tail, { isDefault: true }).addCommand(query);
4480
4609
 
4481
- // src/cli/commands/project.ts
4610
+ // src/cli/commands/projects.ts
4482
4611
  import { confirm as confirm5, input as input5, select as select7 } from "@inquirer/prompts";
4483
4612
  import { Command as Command10 } from "commander";
4484
4613
  import { colorize as colorize11 } from "consola/utils";
@@ -4507,22 +4636,22 @@ var list3 = new Command10("list").configureHelp({ showGlobalOptions: true }).des
4507
4636
  "after",
4508
4637
  `
4509
4638
  Example:
4510
- $ catalyst project list`
4639
+ $ catalyst projects list`
4511
4640
  ).addOption(storeHashOption()).addOption(accessTokenOption()).addOption(apiHostOption()).action(async (options) => {
4512
4641
  const config2 = getProjectConfig();
4513
4642
  const apiHost = resolveApiHost(options, config2);
4514
4643
  const { storeHash, accessToken } = resolveCredentials(options, config2);
4515
4644
  await getTelemetry().identify(storeHash);
4516
4645
  consola.start("Fetching projects...");
4517
- const projects = await fetchProjects(storeHash, accessToken, apiHost);
4646
+ const storeProjects = await fetchProjects(storeHash, accessToken, apiHost);
4518
4647
  consola.success("Projects fetched.");
4519
- if (projects.length === 0) {
4648
+ if (storeProjects.length === 0) {
4520
4649
  consola.info("No projects found.");
4521
4650
  process.exit(0);
4522
4651
  return;
4523
4652
  }
4524
4653
  const linkedProjectUuid = config2.get("projectUuid");
4525
- const projectList = projects.map((p) => {
4654
+ const projectList = storeProjects.map((p) => {
4526
4655
  const marker = p.uuid === linkedProjectUuid ? ` ${colorize11("green", "[linked]")}` : "";
4527
4656
  const hostnames = p.deployment_hostnames.length === 0 ? [" (not deployed)"] : p.deployment_hostnames.map(
4528
4657
  (hostname) => ` ${colorize11("blue", `https://${hostname}`)}`
@@ -4538,7 +4667,7 @@ var create3 = new Command10("create").configureHelp({ showGlobalOptions: true })
4538
4667
  "after",
4539
4668
  `
4540
4669
  Example:
4541
- $ catalyst project create`
4670
+ $ catalyst projects create`
4542
4671
  ).addOption(storeHashOption()).addOption(accessTokenOption()).addOption(apiHostOption()).addOption(loginUrlOption()).action(async (options) => {
4543
4672
  const config2 = getProjectConfig();
4544
4673
  const apiHost = resolveApiHost(options, config2);
@@ -4558,7 +4687,7 @@ Example:
4558
4687
  } catch (error) {
4559
4688
  if (error instanceof LoginAbortedError) {
4560
4689
  consola.info(
4561
- "Login aborted. Re-run `catalyst project create` when you have your credentials ready."
4690
+ "Login aborted. Re-run `catalyst projects create` when you have your credentials ready."
4562
4691
  );
4563
4692
  process.exit(0);
4564
4693
  return;
@@ -4584,10 +4713,10 @@ var link2 = new Command10("link").configureHelp({ showGlobalOptions: true }).des
4584
4713
  `
4585
4714
  Examples:
4586
4715
  # Link interactively (prompts to select or create)
4587
- $ catalyst project link
4716
+ $ catalyst projects link
4588
4717
 
4589
4718
  # Link using a project UUID directly
4590
- $ catalyst project link --project-uuid <UUID>`
4719
+ $ catalyst projects link --project-uuid <UUID>`
4591
4720
  ).addOption(storeHashOption()).addOption(accessTokenOption()).addOption(apiHostOption()).addOption(projectUuidOption()).action(async (options) => {
4592
4721
  const config2 = getProjectConfig();
4593
4722
  const apiHost = resolveApiHost(options, config2);
@@ -4617,7 +4746,7 @@ Examples:
4617
4746
  } catch (error) {
4618
4747
  if (error instanceof NoLinkedProjectError) {
4619
4748
  consola.info(
4620
- "When you're ready to create a project, run `catalyst project create` or re-run `catalyst project link`."
4749
+ "When you're ready to create a project, run `catalyst projects create` or re-run `catalyst projects link`."
4621
4750
  );
4622
4751
  process.exit(0);
4623
4752
  throw error;
@@ -4635,13 +4764,13 @@ var del = new Command10("delete").configureHelp({ showGlobalOptions: true }).des
4635
4764
  `
4636
4765
  Examples:
4637
4766
  # Select a project to delete interactively
4638
- $ catalyst project delete
4767
+ $ catalyst projects delete
4639
4768
 
4640
4769
  # Delete a specific project (still prompts for confirmation)
4641
- $ catalyst project delete --project-uuid <UUID>
4770
+ $ catalyst projects delete --project-uuid <UUID>
4642
4771
 
4643
4772
  # Skip the confirmation prompt
4644
- $ catalyst project delete --project-uuid <UUID> --force`
4773
+ $ catalyst projects delete --project-uuid <UUID> --force`
4645
4774
  ).addOption(storeHashOption()).addOption(accessTokenOption()).addOption(apiHostOption()).addOption(projectUuidOption()).option("--force", "Skip the confirmation prompt before deleting.").action(async (options) => {
4646
4775
  const config2 = getProjectConfig();
4647
4776
  const apiHost = resolveApiHost(options, config2);
@@ -4651,9 +4780,9 @@ Examples:
4651
4780
  let targetName;
4652
4781
  if (!targetUuid) {
4653
4782
  consola.start("Fetching projects...");
4654
- const projects = await fetchProjects(storeHash, accessToken, apiHost);
4783
+ const storeProjects = await fetchProjects(storeHash, accessToken, apiHost);
4655
4784
  consola.success("Projects fetched.");
4656
- if (projects.length === 0) {
4785
+ if (storeProjects.length === 0) {
4657
4786
  consola.info("No projects found.");
4658
4787
  process.exit(0);
4659
4788
  return;
@@ -4662,7 +4791,7 @@ Examples:
4662
4791
  const selected = await select7({
4663
4792
  message: "Select a project to delete (Press <enter> to select).",
4664
4793
  choices: [
4665
- ...projects.map((p) => ({
4794
+ ...storeProjects.map((p) => ({
4666
4795
  name: p.uuid === linkedProjectUuid ? `${p.name} ${colorize11("green", "[linked]")}` : p.name,
4667
4796
  value: p.uuid,
4668
4797
  description: p.uuid
@@ -4675,7 +4804,7 @@ Examples:
4675
4804
  process.exit(0);
4676
4805
  return;
4677
4806
  }
4678
- const matched = projects.find((p) => p.uuid === selected);
4807
+ const matched = storeProjects.find((p) => p.uuid === selected);
4679
4808
  if (!matched) {
4680
4809
  throw new Error(`Selected project ${String(selected)} not found in fetched list.`);
4681
4810
  }
@@ -4703,13 +4832,13 @@ Examples:
4703
4832
  }
4704
4833
  process.exit(0);
4705
4834
  });
4706
- var project = new Command10("project").configureHelp({ showGlobalOptions: true }).description("Manage your BigCommerce infrastructure project.").addCommand(create3).addCommand(list3).addCommand(link2).addCommand(del);
4835
+ var projects = new Command10("projects").alias("project").configureHelp({ showGlobalOptions: true }).description("Manage your BigCommerce infrastructure projects.").addCommand(create3).addCommand(list3).addCommand(link2).addCommand(del);
4707
4836
 
4708
4837
  // src/cli/commands/start.ts
4709
4838
  import { Command as Command11 } from "commander";
4710
4839
  import { execa as execa3 } from "execa";
4711
4840
  import { existsSync as existsSync6, lstatSync, symlinkSync } from "fs";
4712
- import { join as join13, relative } from "path";
4841
+ import { join as join14, relative } from "path";
4713
4842
  var start = new Command11("start").configureHelp({ showGlobalOptions: true }).description(
4714
4843
  "Start a local preview of your Catalyst storefront using the OpenNext Cloudflare adapter."
4715
4844
  ).addHelpText(
@@ -4727,11 +4856,11 @@ Example:
4727
4856
  });
4728
4857
  return;
4729
4858
  }
4730
- const envLocal = join13(process.cwd(), ".env.local");
4731
- const devVars = join13(process.cwd(), ".bigcommerce", ".dev.vars");
4859
+ const envLocal = join14(process.cwd(), ".env.local");
4860
+ const devVars = join14(process.cwd(), ".bigcommerce", ".dev.vars");
4732
4861
  if (existsSync6(envLocal)) {
4733
4862
  if (!existsSync6(devVars)) {
4734
- const target = relative(join13(process.cwd(), ".bigcommerce"), envLocal);
4863
+ const target = relative(join14(process.cwd(), ".bigcommerce"), envLocal);
4735
4864
  symlinkSync(target, devVars);
4736
4865
  consola.success("Symlinked .bigcommerce/.dev.vars \u2192 .env.local");
4737
4866
  } else if (!lstatSync(devVars).isSymbolicLink()) {
@@ -4751,7 +4880,7 @@ Example:
4751
4880
  "opennextjs-cloudflare",
4752
4881
  "preview",
4753
4882
  "--config",
4754
- join13(".bigcommerce", "wrangler.jsonc")
4883
+ join14(".bigcommerce", "wrangler.jsonc")
4755
4884
  ],
4756
4885
  {
4757
4886
  stdio: "inherit",
@@ -4810,7 +4939,7 @@ import { Command as Command13, Option as Option10 } from "@commander-js/extra-ty
4810
4939
  import { confirm as confirm6 } from "@inquirer/prompts";
4811
4940
  import { execa as execa4 } from "execa";
4812
4941
  import {
4813
- access as access2,
4942
+ access as access3,
4814
4943
  copyFile as copyFile2,
4815
4944
  cp as cp2,
4816
4945
  mkdir,
@@ -4822,7 +4951,8 @@ import {
4822
4951
  writeFile as writeFile2
4823
4952
  } from "fs/promises";
4824
4953
  import { homedir, tmpdir } from "os";
4825
- import { dirname as dirname5, join as join14, relative as relative2 } from "path";
4954
+ import { dirname as dirname5, join as join15, relative as relative2 } from "path";
4955
+ import { minVersion, satisfies, lt as semverLt, validRange, valid as validSemver2 } from "semver";
4826
4956
  import yoctoSpinner4 from "yocto-spinner";
4827
4957
  import { z as z17 } from "zod";
4828
4958
  var CorePackageJson = z17.object({
@@ -4830,6 +4960,20 @@ var CorePackageJson = z17.object({
4830
4960
  version: z17.string(),
4831
4961
  catalyst: z17.object({ version: z17.string(), ref: z17.string() }).optional()
4832
4962
  });
4963
+ var DEP_FIELDS2 = [
4964
+ "dependencies",
4965
+ "devDependencies",
4966
+ "optionalDependencies",
4967
+ "peerDependencies"
4968
+ ];
4969
+ var DepsPackageJson = z17.looseObject({
4970
+ dependencies: z17.record(z17.string(), z17.string()).optional(),
4971
+ devDependencies: z17.record(z17.string(), z17.string()).optional(),
4972
+ optionalDependencies: z17.record(z17.string(), z17.string()).optional(),
4973
+ peerDependencies: z17.record(z17.string(), z17.string()).optional()
4974
+ });
4975
+ var WorkspacePackageJson = z17.object({ name: z17.string(), version: z17.string() });
4976
+ var CATALYST_CLI_PACKAGE = "@bigcommerce/catalyst";
4833
4977
  var DEFAULT_REPOSITORY = "bigcommerce/catalyst";
4834
4978
  var MOVING_TAGS = /* @__PURE__ */ new Set(["latest", "canary", "alpha"]);
4835
4979
  var KNOWN_FAMILIES = /* @__PURE__ */ new Set([
@@ -4838,14 +4982,14 @@ var KNOWN_FAMILIES = /* @__PURE__ */ new Set([
4838
4982
  "@bigcommerce/catalyst-b2b-makeswift",
4839
4983
  "@bigcommerce/catalyst-b2b"
4840
4984
  ]);
4841
- var CACHE_DIR = join14(homedir(), ".cache", "catalyst-cli", "cores");
4842
- var pathExists = (p) => access2(p).then(() => true).catch(() => false);
4985
+ var CACHE_DIR = join15(homedir(), ".cache", "catalyst-cli", "cores");
4986
+ var pathExists = (p) => access3(p).then(() => true).catch(() => false);
4843
4987
  async function listFiles(dir, prefix = "") {
4844
4988
  const entries = await readdir2(dir, { withFileTypes: true }).catch(() => []);
4845
4989
  const nested = await Promise.all(
4846
4990
  entries.map((entry) => {
4847
4991
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
4848
- return entry.isDirectory() ? listFiles(join14(dir, entry.name), rel) : Promise.resolve([rel]);
4992
+ return entry.isDirectory() ? listFiles(join15(dir, entry.name), rel) : Promise.resolve([rel]);
4849
4993
  })
4850
4994
  );
4851
4995
  return nested.flat();
@@ -4861,7 +5005,7 @@ async function computeBaseSimilarity(baseDir, destDir) {
4861
5005
  const baseFiles = await listFiles(baseDir);
4862
5006
  if (baseFiles.length === 0) return 0;
4863
5007
  const matches = await Promise.all(
4864
- baseFiles.map((rel) => filesEqual(join14(baseDir, rel), join14(destDir, rel)))
5008
+ baseFiles.map((rel) => filesEqual(join15(baseDir, rel), join15(destDir, rel)))
4865
5009
  );
4866
5010
  return matches.filter(Boolean).length / baseFiles.length;
4867
5011
  }
@@ -4880,10 +5024,79 @@ function parseRef(ref) {
4880
5024
  return { packageName: ref.slice(0, lastAt), version: ref.slice(lastAt + 1) };
4881
5025
  }
4882
5026
  async function readResolvedVersion(coreDir) {
4883
- const raw = await readFile2(join14(coreDir, "package.json"), "utf-8");
5027
+ const raw = await readFile2(join15(coreDir, "package.json"), "utf-8");
4884
5028
  const pkg = CorePackageJson.parse(JSON.parse(raw));
4885
5029
  return pkg.catalyst?.version ?? pkg.version;
4886
5030
  }
5031
+ var readDepSpecifiers = (raw) => {
5032
+ let parsed;
5033
+ try {
5034
+ parsed = DepsPackageJson.parse(JSON.parse(raw));
5035
+ } catch {
5036
+ return {};
5037
+ }
5038
+ return Object.fromEntries(DEP_FIELDS2.flatMap((field) => Object.entries(parsed[field] ?? {})));
5039
+ };
5040
+ var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5041
+ var rewriteWorkspaceSpecifier = (raw, name, version2) => raw.replace(
5042
+ new RegExp(`("${escapeRegExp(name)}"\\s*:\\s*)"workspace:[^"]*"`, "g"),
5043
+ (_match, key) => `${key}"^${version2}"`
5044
+ );
5045
+ async function normalizeWorkspaceDeps(ourPkgRaw, baseDir, theirsDir, baseVersions, targetVersions) {
5046
+ const basePath = join15(baseDir, "package.json");
5047
+ const theirsPath = join15(theirsDir, "package.json");
5048
+ const [originalBase, originalTheirs] = await Promise.all([
5049
+ readFile2(basePath, "utf-8").catch(() => null),
5050
+ readFile2(theirsPath, "utf-8").catch(() => null)
5051
+ ]);
5052
+ if (originalBase === null || originalTheirs === null) return { workspace: [], bumped: [] };
5053
+ const ourSpecifiers = readDepSpecifiers(ourPkgRaw);
5054
+ const names = new Set(
5055
+ [
5056
+ ...Object.entries(readDepSpecifiers(originalBase)),
5057
+ ...Object.entries(readDepSpecifiers(originalTheirs))
5058
+ ].filter(([, range]) => range?.startsWith("workspace:")).map(([name]) => name)
5059
+ );
5060
+ const decisions = [...names].map((name) => ({
5061
+ name,
5062
+ ours: ourSpecifiers[name],
5063
+ baseVersion: baseVersions[name],
5064
+ targetVersion: targetVersions[name]
5065
+ }));
5066
+ const workspace = decisions.flatMap(
5067
+ ({ name, ours, targetVersion }) => ours?.startsWith("workspace:") && targetVersion !== void 0 ? [{ name, ours, version: targetVersion }] : []
5068
+ );
5069
+ const normalizable = decisions.filter(({ ours }) => !ours?.startsWith("workspace:"));
5070
+ const baseRaw = normalizable.reduce(
5071
+ (acc, { name, baseVersion }) => baseVersion === void 0 ? acc : rewriteWorkspaceSpecifier(acc, name, baseVersion),
5072
+ originalBase
5073
+ );
5074
+ const theirsRaw = normalizable.reduce(
5075
+ (acc, { name, targetVersion }) => targetVersion === void 0 ? acc : rewriteWorkspaceSpecifier(acc, name, targetVersion),
5076
+ originalTheirs
5077
+ );
5078
+ const bumped = normalizable.filter(
5079
+ ({ baseVersion, targetVersion }) => baseVersion !== void 0 && targetVersion !== void 0 && baseVersion !== targetVersion
5080
+ ).map(({ name }) => name);
5081
+ await Promise.all([
5082
+ baseRaw === originalBase ? null : writeFile2(basePath, baseRaw),
5083
+ theirsRaw === originalTheirs ? null : writeFile2(theirsPath, theirsRaw)
5084
+ ]);
5085
+ return { workspace, bumped };
5086
+ }
5087
+ var migrateWorkspaceDeps = (raw, findings) => findings.reduce((acc, { name, version: version2 }) => rewriteWorkspaceSpecifier(acc, name, version2), raw);
5088
+ async function findStaleCli(pkgRaw) {
5089
+ const range = readDepSpecifiers(pkgRaw)[CATALYST_CLI_PACKAGE];
5090
+ if (range === void 0 || !validRange(range)) return null;
5091
+ const body = await fetch(`https://registry.npmjs.org/${CATALYST_CLI_PACKAGE}/latest`, {
5092
+ // A hanging registry would otherwise stall the CLI indefinitely.
5093
+ signal: AbortSignal.timeout(5e3)
5094
+ }).then((res) => res.ok ? res.json() : null).catch(() => null);
5095
+ const latest = z17.object({ version: z17.string() }).safeParse(body).data?.version;
5096
+ if (latest === void 0 || !validSemver2(latest) || satisfies(latest, range)) return null;
5097
+ const current = minVersion(range)?.version;
5098
+ return current !== void 0 && semverLt(current, latest) ? { current, latest } : null;
5099
+ }
4887
5100
  async function mergeFile(oursPath, basePath, theirsPath) {
4888
5101
  const merged = await execa4(
4889
5102
  "git",
@@ -4907,9 +5120,9 @@ async function mergeFile(oursPath, basePath, theirsPath) {
4907
5120
  return (merged.exitCode ?? 0) > 0;
4908
5121
  }
4909
5122
  async function mergeModified(rel, baseDir, theirsDir, catalystRoot) {
4910
- const basePath = join14(baseDir, rel);
4911
- const theirsPath = join14(theirsDir, rel);
4912
- const oursPath = join14(catalystRoot, rel);
5123
+ const basePath = join15(baseDir, rel);
5124
+ const theirsPath = join15(theirsDir, rel);
5125
+ const oursPath = join15(catalystRoot, rel);
4913
5126
  if (await filesEqual(basePath, theirsPath)) return null;
4914
5127
  if (!await pathExists(oursPath)) {
4915
5128
  await copyInto(theirsPath, oursPath);
@@ -4925,8 +5138,8 @@ async function mergeModified(rel, baseDir, theirsDir, catalystRoot) {
4925
5138
  return await mergeFile(oursPath, basePath, theirsPath) ? "conflicted" : "applied";
4926
5139
  }
4927
5140
  async function mergeAdded(rel, theirsDir, catalystRoot, emptyFile) {
4928
- const theirsPath = join14(theirsDir, rel);
4929
- const oursPath = join14(catalystRoot, rel);
5141
+ const theirsPath = join15(theirsDir, rel);
5142
+ const oursPath = join15(catalystRoot, rel);
4930
5143
  if (!await pathExists(oursPath)) {
4931
5144
  await copyInto(theirsPath, oursPath);
4932
5145
  return "added";
@@ -4937,8 +5150,8 @@ async function mergeAdded(rel, theirsDir, catalystRoot, emptyFile) {
4937
5150
  return hadConflict ? "conflicted" : "applied";
4938
5151
  }
4939
5152
  async function mergeDeleted(rel, baseDir, catalystRoot) {
4940
- const basePath = join14(baseDir, rel);
4941
- const oursPath = join14(catalystRoot, rel);
5153
+ const basePath = join15(baseDir, rel);
5154
+ const oursPath = join15(catalystRoot, rel);
4942
5155
  if (!await pathExists(oursPath)) return null;
4943
5156
  if (await filesEqual(oursPath, basePath)) {
4944
5157
  await rm2(oursPath, { force: true });
@@ -4984,15 +5197,15 @@ async function resolveStrategy(strategy) {
4984
5197
  return "per-file";
4985
5198
  }
4986
5199
  async function mergeCoreTree(baseDir, theirsDir, catalystRoot) {
4987
- const scratch = await mkdtemp(join14(tmpdir(), "catalyst-merge-"));
5200
+ const scratch = await mkdtemp(join15(tmpdir(), "catalyst-merge-"));
4988
5201
  try {
4989
5202
  await execa4("git", ["init", "-q", scratch]);
4990
5203
  await execa4("git", ["config", "core.autocrlf", "false"], {
4991
- env: { ...process.env, GIT_DIR: join14(scratch, ".git") }
5204
+ env: { ...process.env, GIT_DIR: join15(scratch, ".git") }
4992
5205
  });
4993
5206
  const env2 = {
4994
5207
  ...process.env,
4995
- GIT_DIR: join14(scratch, ".git"),
5208
+ GIT_DIR: join15(scratch, ".git"),
4996
5209
  GIT_AUTHOR_NAME: "catalyst",
4997
5210
  GIT_AUTHOR_EMAIL: "catalyst@bigcommerce.com",
4998
5211
  GIT_COMMITTER_NAME: "catalyst",
@@ -5002,7 +5215,7 @@ async function mergeCoreTree(baseDir, theirsDir, catalystRoot) {
5002
5215
  const sideEnv = {
5003
5216
  ...env2,
5004
5217
  GIT_WORK_TREE: workTree,
5005
- GIT_INDEX_FILE: join14(scratch, `index.${tag}`)
5218
+ GIT_INDEX_FILE: join15(scratch, `index.${tag}`)
5006
5219
  };
5007
5220
  await execa4("git", ["add", "-A"], { env: sideEnv });
5008
5221
  return (await execa4("git", ["write-tree"], { env: sideEnv })).stdout.trim();
@@ -5046,7 +5259,7 @@ async function mergeCoreTree(baseDir, theirsDir, catalystRoot) {
5046
5259
  const outEnv = {
5047
5260
  ...env2,
5048
5261
  GIT_WORK_TREE: catalystRoot,
5049
- GIT_INDEX_FILE: join14(scratch, "index.out")
5262
+ GIT_INDEX_FILE: join15(scratch, "index.out")
5050
5263
  };
5051
5264
  await execa4("git", ["read-tree", mergedTree], { env: outEnv });
5052
5265
  await execa4("git", ["checkout-index", "-a", "-f"], { env: outEnv });
@@ -5065,7 +5278,7 @@ async function mergeCoreTree(baseDir, theirsDir, catalystRoot) {
5065
5278
  }
5066
5279
  }
5067
5280
  const deleted = changes.filter((change) => change.status === "D").map((change) => change.path);
5068
- await Promise.all(deleted.map((path) => rm2(join14(catalystRoot, path), { force: true })));
5281
+ await Promise.all(deleted.map((path) => rm2(join15(catalystRoot, path), { force: true })));
5069
5282
  return {
5070
5283
  applied: changes.filter((change) => change.status !== "A" && change.status !== "D").map((change) => change.path),
5071
5284
  added: changes.filter((change) => change.status === "A").map((change) => change.path),
@@ -5108,9 +5321,9 @@ async function applyIndexState(gitRoot, relDir, baseDir, theirsDir, result, stam
5108
5321
  stillConflicted.map(async (rel) => {
5109
5322
  const repoPath = toRepoPath(rel);
5110
5323
  const [base, ours, theirs] = await Promise.all([
5111
- hashBlob(join14(baseDir, rel)),
5324
+ hashBlob(join15(baseDir, rel)),
5112
5325
  headEntry(repoPath),
5113
- hashBlob(join14(theirsDir, rel))
5326
+ hashBlob(join15(theirsDir, rel))
5114
5327
  ]);
5115
5328
  return [
5116
5329
  `0 ${NULL_OID} ${repoPath}`,
@@ -5133,10 +5346,10 @@ async function resolveProject2(cwd) {
5133
5346
  } catch {
5134
5347
  return null;
5135
5348
  }
5136
- const candidateDirs = [.../* @__PURE__ */ new Set([join14(gitRoot, "core"), gitRoot, join14(cwd, "core"), cwd])];
5349
+ const candidateDirs = [.../* @__PURE__ */ new Set([join15(gitRoot, "core"), gitRoot, join15(cwd, "core"), cwd])];
5137
5350
  const parsed = (await Promise.all(
5138
5351
  candidateDirs.map(async (dir) => {
5139
- const pkgPath = join14(dir, "package.json");
5352
+ const pkgPath = join15(dir, "package.json");
5140
5353
  const raw = await readFile2(pkgPath, "utf-8").catch(() => null);
5141
5354
  if (!raw) return null;
5142
5355
  let pkgJson;
@@ -5160,13 +5373,44 @@ async function resolveProject2(cwd) {
5160
5373
  pkg: chosen.pkg
5161
5374
  };
5162
5375
  }
5376
+ var readWorkspaceVersions = async (repoDir) => {
5377
+ const packagesDir = join15(repoDir, "packages");
5378
+ const entries = await readdir2(packagesDir, { withFileTypes: true }).catch(() => []);
5379
+ const manifests = await Promise.all(
5380
+ entries.filter((entry) => entry.isDirectory()).map(async (entry) => {
5381
+ const raw = await readFile2(join15(packagesDir, entry.name, "package.json"), "utf-8").catch(
5382
+ () => null
5383
+ );
5384
+ if (raw === null) return null;
5385
+ try {
5386
+ return WorkspacePackageJson.parse(JSON.parse(raw));
5387
+ } catch {
5388
+ return null;
5389
+ }
5390
+ })
5391
+ );
5392
+ return Object.fromEntries(
5393
+ manifests.filter((pkg) => pkg !== null).map(({ name, version: version2 }) => [name, version2])
5394
+ );
5395
+ };
5396
+ var readCachedVersions = async (path) => {
5397
+ const raw = await readFile2(path, "utf-8").catch(() => null);
5398
+ if (raw === null) return null;
5399
+ try {
5400
+ return z17.record(z17.string(), z17.string()).parse(JSON.parse(raw));
5401
+ } catch {
5402
+ return null;
5403
+ }
5404
+ };
5163
5405
  async function downloadCore(repository, ref, destDir, token) {
5164
5406
  const cacheable = !MOVING_TAGS.has(parseRef(ref).version);
5165
5407
  const cacheKey = `${repository.replace(/[^a-zA-Z0-9._-]/g, "_")}__${ref.replace(/[^a-zA-Z0-9._-]/g, "_")}`;
5166
- const cachePath = join14(CACHE_DIR, cacheKey);
5167
- if (cacheable && await pathExists(cachePath) && await pathExists(join14(cachePath, "package.json"))) {
5408
+ const cachePath = join15(CACHE_DIR, cacheKey);
5409
+ const versionsPath = `${cachePath}.versions.json`;
5410
+ const cachedVersions = cacheable && await pathExists(join15(cachePath, "package.json")) ? await readCachedVersions(versionsPath) : null;
5411
+ if (cachedVersions) {
5168
5412
  await cp2(cachePath, destDir, { recursive: true });
5169
- return;
5413
+ return cachedVersions;
5170
5414
  }
5171
5415
  const url = `https://api.github.com/repos/${repository}/tarball/${encodeURIComponent(ref)}`;
5172
5416
  const headers = {
@@ -5186,22 +5430,24 @@ async function downloadCore(repository, ref, destDir, token) {
5186
5430
  await writeFile2(tarballPath, Buffer.from(await res.arrayBuffer()));
5187
5431
  await mkdir(rawDir, { recursive: true });
5188
5432
  await execa4("tar", ["-xzf", tarballPath, "-C", rawDir, "--strip-components=1"]);
5189
- const coreDir = join14(rawDir, "core");
5433
+ const coreDir = join15(rawDir, "core");
5190
5434
  const sourceDir = await pathExists(coreDir) ? coreDir : rawDir;
5435
+ const workspaceVersions = await readWorkspaceVersions(rawDir);
5191
5436
  await rename(sourceDir, destDir);
5192
5437
  await rm2(tarballPath, { force: true });
5193
5438
  await rm2(rawDir, { recursive: true, force: true });
5194
5439
  if (cacheable) {
5195
5440
  await mkdir(CACHE_DIR, { recursive: true });
5196
- await cp2(destDir, cachePath, { recursive: true }).catch(() => {
5441
+ await cp2(destDir, cachePath, { recursive: true }).then(() => writeFile2(versionsPath, JSON.stringify(workspaceVersions))).catch(() => {
5197
5442
  });
5198
5443
  }
5444
+ return workspaceVersions;
5199
5445
  }
5200
- async function resolveBaseRef(project2, fromOption, assumeYes) {
5201
- if (project2.pkg.catalyst?.ref) return project2.pkg.catalyst.ref;
5446
+ async function resolveBaseRef(project, fromOption, assumeYes) {
5447
+ if (project.pkg.catalyst?.ref) return project.pkg.catalyst.ref;
5202
5448
  if (fromOption) return fromOption;
5203
- const family = project2.pkg.name && KNOWN_FAMILIES.has(project2.pkg.name) ? project2.pkg.name : "@bigcommerce/catalyst-core";
5204
- const guess = `${family}@${project2.pkg.version}`;
5449
+ const family = project.pkg.name && KNOWN_FAMILIES.has(project.pkg.name) ? project.pkg.name : "@bigcommerce/catalyst-core";
5450
+ const guess = `${family}@${project.pkg.version}`;
5205
5451
  consola.warn(`No \`catalyst.ref\` found in package.json. Inferred base: ${guess}`);
5206
5452
  if (assumeYes) return guess;
5207
5453
  const ok = await confirm6({ message: `Upgrade from ${guess}?`, default: true }).catch(() => false);
@@ -5211,7 +5457,25 @@ async function resolveBaseRef(project2, fromOption, assumeYes) {
5211
5457
  }
5212
5458
  return guess;
5213
5459
  }
5214
- function printSummary(result, relDir, stampedPkg) {
5460
+ function printWorkspaceFindings(findings) {
5461
+ if (findings.length === 0) return;
5462
+ const pad = Math.max(...findings.map((finding) => finding.name.length));
5463
+ const rows = findings.map((finding) => ` ${finding.name.padEnd(pad)} ${finding.ours} \u2192 ^${finding.version}`).join("\n");
5464
+ consola.warn(
5465
+ `Catalyst dependencies still using the workspace protocol (${findings.length}):
5466
+ ${rows}
5467
+
5468
+ They resolve to your local packages/ copies, which \`catalyst upgrade\` does not update \u2014 so they never receive upgrades.`
5469
+ );
5470
+ }
5471
+ function printSummary({
5472
+ result,
5473
+ relDir,
5474
+ stampedPkg,
5475
+ migratedDeps,
5476
+ packageManager,
5477
+ staleCli
5478
+ }) {
5215
5479
  const unresolved = result.conflicted.filter((f) => !(stampedPkg && f === "package.json"));
5216
5480
  const parts = [
5217
5481
  `${result.applied.length} updated`,
@@ -5221,18 +5485,31 @@ function printSummary(result, relDir, stampedPkg) {
5221
5485
  if (unresolved.length) parts.push(`${unresolved.length} with conflicts`);
5222
5486
  consola.log(`
5223
5487
  ${parts.join(", ")}`);
5488
+ const pkgConflicted = unresolved.includes("package.json");
5489
+ const depsChanged = migratedDeps > 0 || [...result.applied, ...result.added, ...result.conflicted].includes("package.json");
5490
+ if (depsChanged && !pkgConflicted) {
5491
+ consola.info(
5492
+ `Dependencies changed \u2014 run \`${packageManager} install\` to update your lockfile.`
5493
+ );
5494
+ }
5224
5495
  if (unresolved.length) {
5225
5496
  const files = unresolved.map((f) => ` ${f}`).join("\n");
5497
+ const install = depsChanged && pkgConflicted ? ` && ${packageManager} install` : "";
5226
5498
  consola.warn(
5227
5499
  `
5228
5500
  Staged the clean changes. ${unresolved.length} file(s) need conflict resolution (the <<<ours/===/theirs>>> markers):
5229
5501
  ${files}
5230
5502
 
5231
- Resolve them, then: git add ${relDir} && git commit`
5503
+ Resolve them, then: git add ${relDir} && git commit${install}`
5232
5504
  );
5233
5505
  } else {
5234
5506
  consola.success("Staged all changes \u2014 review with `git diff --cached`, then commit.");
5235
5507
  }
5508
+ if (staleCli) {
5509
+ consola.info(
5510
+ `Your Catalyst CLI is behind (${staleCli.current} \u2192 ${staleCli.latest}). Update it with \`${packageManager} add -D ${CATALYST_CLI_PACKAGE}@${staleCli.latest}\`.`
5511
+ );
5512
+ }
5236
5513
  }
5237
5514
  var upgrade = new Command13("upgrade").configureHelp({ showGlobalOptions: true }).aliases([
5238
5515
  "up",
@@ -5273,15 +5550,15 @@ Conflicts are written as standard <<<ours/===/theirs>>> markers \u2014 the upgra
5273
5550
  never aborts. Versions are git tags on the repository (not npm). Set GITHUB_TOKEN
5274
5551
  to raise the GitHub API rate limit.`
5275
5552
  ).action(async (version2, options) => {
5276
- const project2 = await resolveProject2(process.cwd());
5277
- if (!project2) {
5553
+ const project = await resolveProject2(process.cwd());
5554
+ if (!project) {
5278
5555
  consola.error(
5279
5556
  "Run `catalyst upgrade` from inside a Catalyst git repository (flat core/ repo or a project containing core/)."
5280
5557
  );
5281
5558
  process.exit(1);
5282
5559
  }
5283
- const { gitRoot, catalystRoot, relDir, pkgPath } = project2;
5284
- const baseRef = await resolveBaseRef(project2, options.from, options.yes ?? false);
5560
+ const { gitRoot, catalystRoot, relDir, pkgPath } = project;
5561
+ const baseRef = await resolveBaseRef(project, options.from, options.yes ?? false);
5285
5562
  if (!baseRef) process.exit(1);
5286
5563
  let basePackage;
5287
5564
  let upstreamRef;
@@ -5316,7 +5593,7 @@ to raise the GitHub API rate limit.`
5316
5593
  process.exit(1);
5317
5594
  }
5318
5595
  }
5319
- if (!project2.pkg.catalyst?.ref && !options.from && !options.dryRun) {
5596
+ if (!project.pkg.catalyst?.ref && !options.from && !options.dryRun) {
5320
5597
  const { version: baseVersion } = parseRef(baseRef);
5321
5598
  const rawPkg = await readFile2(pkgPath, "utf-8");
5322
5599
  const parsedPkg = z17.record(z17.string(), z17.unknown()).parse(JSON.parse(rawPkg));
@@ -5327,15 +5604,18 @@ to raise the GitHub API rate limit.`
5327
5604
  consola.success(`Added catalyst.ref \u2192 ${baseRef}`);
5328
5605
  }
5329
5606
  const token = process.env.GITHUB_TOKEN;
5330
- const tmpDir = await mkdtemp(join14(tmpdir(), "catalyst-upgrade-"));
5607
+ const tmpDir = await mkdtemp(join15(tmpdir(), "catalyst-upgrade-"));
5331
5608
  try {
5332
- const baseDir = join14(tmpDir, "base");
5333
- const theirsDir = join14(tmpDir, "theirs");
5334
- const emptyFile = join14(tmpDir, ".empty");
5609
+ const baseDir = join15(tmpDir, "base");
5610
+ const theirsDir = join15(tmpDir, "theirs");
5611
+ const emptyFile = join15(tmpDir, ".empty");
5335
5612
  await writeFile2(emptyFile, "");
5613
+ const staleCliCheck = findStaleCli(project.rawContent).catch(() => null);
5336
5614
  const downloadSpinner = yoctoSpinner4().start(`Downloading ${baseRef} and ${upstreamRef}...`);
5615
+ let baseVersions;
5616
+ let targetVersions;
5337
5617
  try {
5338
- await Promise.all([
5618
+ [baseVersions, targetVersions] = await Promise.all([
5339
5619
  downloadCore(options.repository, baseRef, baseDir, token),
5340
5620
  downloadCore(options.repository, upstreamRef, theirsDir, token)
5341
5621
  ]);
@@ -5344,7 +5624,7 @@ to raise the GitHub API rate limit.`
5344
5624
  downloadSpinner.error("Download failed");
5345
5625
  throw err;
5346
5626
  }
5347
- if (!project2.pkg.catalyst?.ref && !options.from) {
5627
+ if (!project.pkg.catalyst?.ref && !options.from) {
5348
5628
  const similarity = await computeBaseSimilarity(baseDir, catalystRoot);
5349
5629
  const pct = Math.round(similarity * 100);
5350
5630
  if (similarity < 0.5) {
@@ -5355,6 +5635,13 @@ to raise the GitHub API rate limit.`
5355
5635
  }
5356
5636
  const resolvedVersion = MOVING_TAGS.has(upstreamTagVersion) ? await readResolvedVersion(theirsDir) : upstreamTagVersion;
5357
5637
  const newRef = MOVING_TAGS.has(upstreamTagVersion) ? `${basePackage}@${resolvedVersion}` : upstreamRef;
5638
+ const { workspace: workspaceDeps, bumped } = await normalizeWorkspaceDeps(
5639
+ project.rawContent,
5640
+ baseDir,
5641
+ theirsDir,
5642
+ baseVersions,
5643
+ targetVersions
5644
+ );
5358
5645
  if (options.dryRun) {
5359
5646
  const diffSpinner = yoctoSpinner4().start("Generating diff...");
5360
5647
  const diff = await execa4(
@@ -5374,6 +5661,7 @@ to raise the GitHub API rate limit.`
5374
5661
  diffSpinner.success(`Diff ready \u2014 ${fileCount} file(s) affected.`);
5375
5662
  consola.log("\nDiff preview (--dry-run, not applied):\n");
5376
5663
  consola.log(diff.stdout);
5664
+ printWorkspaceFindings(workspaceDeps);
5377
5665
  return;
5378
5666
  }
5379
5667
  const strategy = await resolveStrategy(options.strategy);
@@ -5390,13 +5678,13 @@ to raise the GitHub API rate limit.`
5390
5678
  mergeSpinner.success("Merged cleanly.");
5391
5679
  }
5392
5680
  const patchedRaw = await readFile2(pkgPath, "utf-8");
5393
- let stampedPkg = false;
5681
+ let stampedText = null;
5394
5682
  try {
5395
5683
  const patchedPkg = z17.record(z17.string(), z17.unknown()).parse(JSON.parse(patchedRaw));
5396
5684
  patchedPkg.catalyst = { version: resolvedVersion, ref: newRef };
5397
- await writeFile2(pkgPath, `${JSON.stringify(patchedPkg, null, 2)}
5398
- `);
5399
- stampedPkg = true;
5685
+ stampedText = `${JSON.stringify(patchedPkg, null, 2)}
5686
+ `;
5687
+ await writeFile2(pkgPath, stampedText);
5400
5688
  consola.success(`catalyst.ref updated \u2192 ${newRef}`);
5401
5689
  } catch {
5402
5690
  const catalystReplacement = JSON.stringify(
@@ -5421,14 +5709,35 @@ to raise the GitHub API rate limit.`
5421
5709
  );
5422
5710
  }
5423
5711
  }
5712
+ printWorkspaceFindings(workspaceDeps);
5713
+ let migratedDeps = 0;
5714
+ if (workspaceDeps.length) {
5715
+ const accepted = stampedText !== null && (options.yes === true || Boolean(process.stdin.isTTY) && await confirm6({
5716
+ message: "Replace them with the published versions?",
5717
+ default: true
5718
+ }).catch(() => false));
5719
+ if (accepted && stampedText !== null) {
5720
+ await writeFile2(pkgPath, migrateWorkspaceDeps(stampedText, workspaceDeps));
5721
+ migratedDeps = workspaceDeps.length;
5722
+ consola.success(
5723
+ `Migrated ${migratedDeps} dependenc${migratedDeps === 1 ? "y" : "ies"} to published versions.`
5724
+ );
5725
+ } else {
5726
+ consola.info(
5727
+ "Keeping the workspace references. Switching abandons any local packages/ copy you've customized; otherwise apply the versions above in package.json and reinstall."
5728
+ );
5729
+ }
5730
+ }
5424
5731
  try {
5425
- await applyIndexState(gitRoot, relDir, baseDir, theirsDir, result, stampedPkg);
5732
+ await applyIndexState(gitRoot, relDir, baseDir, theirsDir, result, stampedText !== null);
5426
5733
  } catch (err) {
5427
5734
  consola.warn(
5428
5735
  `Couldn't auto-stage the changes (${err instanceof Error ? err.message : String(err)}). Your files are merged on disk \u2014 run \`git add ${relDir}\` yourself.`
5429
5736
  );
5430
5737
  }
5431
5738
  const gitVersion = await execa4("git", ["--version"]).then((r) => r.stdout.trim()).catch(() => "unknown");
5739
+ const packageManager = await detectLockfileManager(gitRoot) ?? "pnpm";
5740
+ const staleCli = await staleCliCheck;
5432
5741
  await getTelemetry().track("upgrade", {
5433
5742
  strategy,
5434
5743
  gitVersion,
@@ -5437,9 +5746,20 @@ to raise the GitHub API rate limit.`
5437
5746
  added: result.added.length,
5438
5747
  deleted: result.deleted.length,
5439
5748
  conflicts: result.conflicted.length,
5440
- hasConflicts: result.conflicted.length > 0
5749
+ hasConflicts: result.conflicted.length > 0,
5750
+ workspaceDepsFound: workspaceDeps.length,
5751
+ workspaceDepsMigrated: migratedDeps,
5752
+ catalystDepsBumped: bumped.length,
5753
+ cliOutdated: staleCli !== null
5754
+ });
5755
+ printSummary({
5756
+ result,
5757
+ relDir,
5758
+ stampedPkg: stampedText !== null,
5759
+ migratedDeps,
5760
+ packageManager,
5761
+ staleCli
5441
5762
  });
5442
- printSummary(result, relDir, stampedPkg);
5443
5763
  } finally {
5444
5764
  await rm2(tmpDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 500 });
5445
5765
  }
@@ -5516,7 +5836,7 @@ consola.log(colorize13("cyanBright", `\u25E2 ${package_default.name} v${package_
5516
5836
  `));
5517
5837
  program.name(package_default.name).version(package_default.version).summary("CLI tool for Catalyst development").description(
5518
5838
  "CLI tool for Catalyst development.\n\nConfiguration priority: flags > process.env > .bigcommerce/project.json. `build` and `deploy` additionally load env files (--env-path, or an auto-loaded .env.local/.env) for the build.\n\nCATALYST_STORE_HASH falls back to BIGCOMMERCE_STORE_HASH if unset.\n\nRun `catalyst <command> --help` for details on a specific command."
5519
- ).configureHelp({ showGlobalOptions: true }).addCommand(version).addCommand(create2).addCommand(start).addCommand(build).addCommand(deploy).addCommand(domains).addCommand(logs).addCommand(project).addCommand(env).addCommand(channel).addCommand(auth).addCommand(upgrade).addCommand(telemetry).addCommand(debug).hook("preAction", telemetryPreHook).hook("postAction", telemetryPostHook);
5839
+ ).configureHelp({ showGlobalOptions: true }).addCommand(version).addCommand(create2).addCommand(start).addCommand(build).addCommand(deploy).addCommand(domains).addCommand(logs).addCommand(projects).addCommand(env).addCommand(channels).addCommand(auth).addCommand(upgrade).addCommand(telemetry).addCommand(debug).hook("preAction", telemetryPreHook).hook("postAction", telemetryPostHook);
5520
5840
 
5521
5841
  // src/cli/index.ts
5522
5842
  var handleFatalError = async (error) => {