@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/README.md +1 -1
- package/dist/cli.js +513 -193
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/templates/open-next.config.ts +59 -5
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.
|
|
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(
|
|
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
|
|
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
|
|
707
|
-
const linkedProject =
|
|
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
|
|
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/
|
|
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(
|
|
1127
|
-
return [...
|
|
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(
|
|
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((
|
|
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
|
-
(
|
|
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((
|
|
1456
|
-
name:
|
|
1457
|
-
value:
|
|
1458
|
-
description:
|
|
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
|
-
(
|
|
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
|
|
1560
|
-
const matched =
|
|
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
|
|
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 =
|
|
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(
|
|
1612
|
+
async function resolveHostname(project, options) {
|
|
1597
1613
|
if (options.hostname) {
|
|
1598
1614
|
return options.hostname;
|
|
1599
1615
|
}
|
|
1600
|
-
if (
|
|
1616
|
+
if (project.deployment_hostnames.length === 0) {
|
|
1601
1617
|
throw new Error(
|
|
1602
|
-
`Project "${
|
|
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
|
-
...
|
|
1607
|
-
...
|
|
1608
|
-
] :
|
|
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
|
|
1617
|
-
const
|
|
1618
|
-
const hostname = await resolveHostname(
|
|
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
|
-
|
|
1637
|
+
channel.id,
|
|
1622
1638
|
siteUrl,
|
|
1623
1639
|
options.storeHash,
|
|
1624
1640
|
options.accessToken,
|
|
1625
1641
|
options.apiHost
|
|
1626
1642
|
);
|
|
1627
|
-
const channelLabel =
|
|
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
|
-
|
|
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/
|
|
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
|
|
1929
|
+
$ catalyst channels update
|
|
1911
1930
|
|
|
1912
1931
|
# Skip both prompts
|
|
1913
|
-
$ catalyst
|
|
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
|
|
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
|
|
1976
|
+
$ catalyst channels link
|
|
1958
1977
|
|
|
1959
1978
|
# Non-interactive
|
|
1960
|
-
$ catalyst
|
|
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
|
|
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
|
|
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
|
|
1990
|
-
if (
|
|
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(
|
|
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 =
|
|
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
|
|
2049
|
+
$ catalyst channels create
|
|
2031
2050
|
|
|
2032
2051
|
# Non-interactive, and link it to this project afterwards
|
|
2033
|
-
$ catalyst
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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) =>
|
|
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
|
|
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
|
|
2688
|
-
const result =
|
|
2689
|
-
safeReadJson2(
|
|
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(
|
|
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 =
|
|
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(
|
|
2777
|
-
".env": existsSync5(
|
|
2778
|
-
".bigcommerce/project.json": existsSync5(
|
|
2779
|
-
".bigcommerce/wrangler.jsonc": existsSync5(
|
|
2780
|
-
".open-next/": existsSync5(
|
|
2781
|
-
"package.json": existsSync5(
|
|
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
|
|
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 =
|
|
2946
|
-
const distDir =
|
|
2986
|
+
const bigcommerceDir = join13(process.cwd(), ".bigcommerce");
|
|
2987
|
+
const distDir = join13(process.cwd(), ".bigcommerce", "dist");
|
|
2947
2988
|
try {
|
|
2948
|
-
await
|
|
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 =
|
|
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 =
|
|
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((
|
|
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
|
|
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 =
|
|
3255
|
+
const distDir = join13(process.cwd(), ".bigcommerce", "dist");
|
|
3215
3256
|
try {
|
|
3216
|
-
await
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
3758
|
+
const projects2 = await fetchProjects(context.storeHash, context.accessToken, context.apiHost);
|
|
3694
3759
|
consola.success("Projects fetched.");
|
|
3695
|
-
const destinations =
|
|
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
|
|
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((
|
|
3704
|
-
name:
|
|
3705
|
-
value:
|
|
3706
|
-
description:
|
|
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
|
|
3838
|
+
let created;
|
|
3757
3839
|
try {
|
|
3758
|
-
|
|
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 ${
|
|
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
|
|
3931
|
-
const candidates =
|
|
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 (
|
|
3934
|
-
project
|
|
4029
|
+
candidates.map(async (project) => ({
|
|
4030
|
+
project,
|
|
3935
4031
|
found: await findDomain(
|
|
3936
4032
|
domain,
|
|
3937
|
-
|
|
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 {
|
|
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.
|
|
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
|
-
`${
|
|
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/
|
|
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
|
|
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
|
|
4646
|
+
const storeProjects = await fetchProjects(storeHash, accessToken, apiHost);
|
|
4518
4647
|
consola.success("Projects fetched.");
|
|
4519
|
-
if (
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
4716
|
+
$ catalyst projects link
|
|
4588
4717
|
|
|
4589
4718
|
# Link using a project UUID directly
|
|
4590
|
-
$ catalyst
|
|
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
|
|
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
|
|
4767
|
+
$ catalyst projects delete
|
|
4639
4768
|
|
|
4640
4769
|
# Delete a specific project (still prompts for confirmation)
|
|
4641
|
-
$ catalyst
|
|
4770
|
+
$ catalyst projects delete --project-uuid <UUID>
|
|
4642
4771
|
|
|
4643
4772
|
# Skip the confirmation prompt
|
|
4644
|
-
$ catalyst
|
|
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
|
|
4783
|
+
const storeProjects = await fetchProjects(storeHash, accessToken, apiHost);
|
|
4655
4784
|
consola.success("Projects fetched.");
|
|
4656
|
-
if (
|
|
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
|
-
...
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
4731
|
-
const devVars =
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
4842
|
-
var pathExists = (p) =>
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
4911
|
-
const theirsPath =
|
|
4912
|
-
const oursPath =
|
|
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 =
|
|
4929
|
-
const oursPath =
|
|
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 =
|
|
4941
|
-
const oursPath =
|
|
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(
|
|
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:
|
|
5204
|
+
env: { ...process.env, GIT_DIR: join15(scratch, ".git") }
|
|
4992
5205
|
});
|
|
4993
5206
|
const env2 = {
|
|
4994
5207
|
...process.env,
|
|
4995
|
-
GIT_DIR:
|
|
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:
|
|
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:
|
|
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(
|
|
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(
|
|
5324
|
+
hashBlob(join15(baseDir, rel)),
|
|
5112
5325
|
headEntry(repoPath),
|
|
5113
|
-
hashBlob(
|
|
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([
|
|
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 =
|
|
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 =
|
|
5167
|
-
|
|
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 =
|
|
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(
|
|
5201
|
-
if (
|
|
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 =
|
|
5204
|
-
const guess = `${family}@${
|
|
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
|
|
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
|
|
5277
|
-
if (!
|
|
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 } =
|
|
5284
|
-
const baseRef = await resolveBaseRef(
|
|
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 (!
|
|
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(
|
|
5607
|
+
const tmpDir = await mkdtemp(join15(tmpdir(), "catalyst-upgrade-"));
|
|
5331
5608
|
try {
|
|
5332
|
-
const baseDir =
|
|
5333
|
-
const theirsDir =
|
|
5334
|
-
const emptyFile =
|
|
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 (!
|
|
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
|
|
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
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
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,
|
|
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(
|
|
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) => {
|