@bigcommerce/create-catalyst 0.12.0 → 0.14.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/index.js CHANGED
@@ -53,12 +53,12 @@ var __async = (__this, __arguments, generator) => {
53
53
 
54
54
  // src/index.ts
55
55
  import { program } from "@commander-js/extra-typings";
56
- import chalk8 from "chalk";
56
+ import chalk7 from "chalk";
57
57
 
58
58
  // package.json
59
59
  var package_default = {
60
60
  name: "@bigcommerce/create-catalyst",
61
- version: "0.12.0",
61
+ version: "0.14.0",
62
62
  type: "module",
63
63
  bin: "bin/index.cjs",
64
64
  files: [
@@ -77,365 +77,219 @@ var package_default = {
77
77
  },
78
78
  dependencies: {
79
79
  "@commander-js/extra-typings": "^12.1.0",
80
- "@inquirer/prompts": "^5.5.0",
80
+ "@inquirer/prompts": "^7.0.0",
81
+ "@segment/analytics-node": "^2.2.0",
81
82
  chalk: "^5.3.0",
82
83
  commander: "^12.1.0",
84
+ conf: "^13.0.1",
83
85
  dotenv: "^16.4.5",
84
86
  "fs-extra": "^11.2.0",
85
87
  giget: "^1.2.3",
86
88
  "lodash.kebabcase": "^4.1.1",
87
- "lodash.merge": "^4.6.2",
88
- nypm: "^0.3.11",
89
+ nypm: "^0.3.12",
89
90
  ora: "^8.1.0",
90
91
  semver: "^7.6.3",
92
+ "std-env": "^3.7.0",
91
93
  zod: "^3.23.8",
92
- "zod-validation-error": "^3.3.1"
94
+ "zod-validation-error": "^3.4.0"
93
95
  },
94
96
  devDependencies: {
95
97
  "@bigcommerce/eslint-config": "^2.9.1",
96
98
  "@bigcommerce/eslint-config-catalyst": "workspace:^",
97
- "@swc/core": "^1.7.24",
99
+ "@swc/core": "^1.7.35",
98
100
  "@swc/jest": "^0.2.36",
99
101
  "@types/fs-extra": "^11.0.4",
100
- "@types/jest": "^29.5.12",
102
+ "@types/jest": "^29.5.13",
101
103
  "@types/lodash.kebabcase": "^4.1.9",
102
- "@types/lodash.merge": "^4.6.9",
103
- "@types/node": "^20.16.5",
104
+ "@types/node": "^20.16.11",
104
105
  "@types/prompts": "^2.4.9",
105
106
  "@types/semver": "^7.5.8",
106
- eslint: "^8.57.0",
107
+ eslint: "^8.57.1",
107
108
  jest: "^29.7.0",
108
- msw: "^2.4.4",
109
+ msw: "^2.4.11",
109
110
  prettier: "^3.3.3",
110
- tsup: "^8.2.4",
111
- typescript: "^5.5.4"
111
+ tsup: "^8.3.0",
112
+ typescript: "^5.6.3"
112
113
  }
113
114
  };
114
115
 
115
116
  // src/commands/create.ts
116
- import { Command as Command2, Option } from "@commander-js/extra-typings";
117
+ import { Command, Option } from "@commander-js/extra-typings";
117
118
  import { input, select as select2 } from "@inquirer/prompts";
118
- import chalk6 from "chalk";
119
- import { exec as execCallback2 } from "child_process";
119
+ import chalk4 from "chalk";
120
+ import { execSync as execSync4 } from "child_process";
120
121
  import { pathExistsSync } from "fs-extra/esm";
121
- import kebabCase2 from "lodash.kebabcase";
122
- import { join as join4 } from "path";
123
- import { promisify as promisify3 } from "util";
124
- import { z as z6 } from "zod";
122
+ import kebabCase from "lodash.kebabcase";
123
+ import { join as join2 } from "path";
124
+ import { z as z3 } from "zod";
125
125
 
126
- // src/utils/apply-integration.ts
127
- import { exec as execCallback } from "child_process";
128
- import { readJsonSync } from "fs-extra/esm";
129
- import { downloadTemplate } from "giget";
130
- import { addDependency, addDevDependency } from "nypm";
131
- import { join } from "path";
132
- import { promisify as promisify2 } from "util";
126
+ // src/utils/clone-catalyst.ts
127
+ import { execSync as execSync3 } from "child_process";
133
128
 
134
- // src/commands/integration.ts
135
- import { Command } from "@commander-js/extra-typings";
136
- import { exec as execCb } from "child_process";
137
- import { parse } from "dotenv";
138
- import { outputFileSync, writeJsonSync } from "fs-extra/esm";
139
- import kebabCase from "lodash.kebabcase";
140
- import { coerce, compare } from "semver";
141
- import { promisify } from "util";
142
- import { z } from "zod";
143
- var exec = promisify(execCb);
144
- var ManifestSchema = z.object({
145
- name: z.string(),
146
- dependencies: z.object({ add: z.array(z.string()) }),
147
- devDependencies: z.object({ add: z.array(z.string()) }),
148
- environmentVariables: z.array(z.string())
149
- });
150
- var integration = new Command("integration").argument("<integration-name>", "Formatted name of the integration").option("--commit-hash <hash>", "Override integration source branch with a specific commit hash").action((integrationNameRaw, options) => __async(void 0, null, function* () {
151
- const integrationName = z.string().transform(kebabCase).parse(integrationNameRaw);
152
- const manifest = {
153
- name: integrationName,
154
- dependencies: { add: [] },
155
- devDependencies: { add: [] },
156
- environmentVariables: []
157
- };
158
- yield exec("git fetch --tags");
159
- const { stdout: headRefStdOut } = yield exec("git rev-parse --abbrev-ref HEAD");
160
- let [sourceRef] = headRefStdOut.split("\n");
161
- if (options.commitHash) {
162
- sourceRef = options.commitHash;
163
- }
164
- const { stdout: catalystTags } = yield exec("git tag --list @bigcommerce/catalyst-core@\\*");
165
- const [latestCoreTag] = catalystTags.split("\n").filter(Boolean).sort((a, b) => {
166
- const versionA = coerce(a.replace("@bigcommerce/catalyst-core@", ""));
167
- const versionB = coerce(b.replace("@bigcommerce/catalyst-core@", ""));
168
- if (versionA && versionB) {
169
- return compare(versionA, versionB);
170
- }
171
- return 0;
172
- }).reverse();
173
- const PackageDependenciesSchema = z.object({
174
- dependencies: z.object({}).passthrough(),
175
- devDependencies: z.object({}).passthrough()
176
- });
177
- const getPackageDeps = (ref) => __async(void 0, null, function* () {
178
- const { stdout } = yield exec(`git show ${ref}:core/package.json`);
179
- return PackageDependenciesSchema.parse(JSON.parse(stdout));
180
- });
181
- const integrationJson = yield getPackageDeps(sourceRef);
182
- const latestCoreTagJson = yield getPackageDeps(latestCoreTag);
183
- const diffObjectKeys = (a, b) => {
184
- return Object.keys(a).filter((key) => !Object.keys(b).includes(key));
185
- };
186
- manifest.dependencies.add = diffObjectKeys(
187
- integrationJson.dependencies,
188
- latestCoreTagJson.dependencies
189
- );
190
- manifest.devDependencies.add = diffObjectKeys(
191
- integrationJson.devDependencies,
192
- latestCoreTagJson.devDependencies
193
- );
194
- const { stdout: latestCoreEnv } = yield exec(`git show ${latestCoreTag}:core/.env.example`);
195
- const { stdout: integrationEnv } = yield exec(`git show ${sourceRef}:core/.env.example`);
196
- manifest.environmentVariables = diffObjectKeys(parse(integrationEnv), parse(latestCoreEnv));
197
- const { stdout: integrationDiff } = yield exec(
198
- `git diff ${latestCoreTag}...${sourceRef} -- ':(exclude)core/package.json' ':(exclude)pnpm-lock.yaml'`
199
- );
200
- outputFileSync(`integrations/${integrationName}/integration.patch`, integrationDiff);
201
- writeJsonSync(`integrations/${integrationName}/manifest.json`, manifest, {
202
- spaces: 2
203
- });
204
- console.log("Integration created successfully.");
205
- }));
129
+ // src/utils/checkout-ref.ts
130
+ import { execSync } from "child_process";
206
131
 
207
- // src/utils/spinner.ts
208
- import { oraPromise } from "ora";
209
- var spinner = (action, oraOpts) => __async(void 0, null, function* () {
210
- return oraPromise(action, __spreadValues({
211
- spinner: "triangle"
212
- }, oraOpts)).catch(() => process.exit(1));
213
- });
132
+ // src/utils/is-exec-exception.ts
133
+ function isExecException(error) {
134
+ return typeof error === "object" && error !== null && "stdout" in error && "stderr" in error;
135
+ }
214
136
 
215
- // src/utils/apply-integration.ts
216
- var exec2 = promisify2(execCallback);
217
- function applyIntegration(_0, _1) {
218
- return __async(this, arguments, function* (integration2, { projectDir, packageManager }) {
219
- if (!integration2) {
220
- return;
221
- }
222
- const integrationDir = join(projectDir, "integrations", integration2);
223
- yield spinner(
224
- downloadTemplate(`github:bigcommerce/catalyst/integrations/${integration2}#main`, {
225
- dir: integrationDir,
226
- offline: false
227
- }),
228
- {
229
- text: `Cloning ${integration2} integration...`,
230
- successText: `${integration2} integration cloned successfully`,
231
- failText: (err) => `Failed to clone ${integration2} integration: ${err.message}`
137
+ // src/utils/checkout-ref.ts
138
+ function checkoutRef(repoDir, ref) {
139
+ try {
140
+ execSync(`git checkout ${ref}`, {
141
+ cwd: repoDir,
142
+ stdio: "inherit",
143
+ encoding: "utf8"
144
+ });
145
+ console.log(`Checked out ref ${ref} successfully.`);
146
+ } catch (error) {
147
+ if (isExecException(error)) {
148
+ const stderr = error.stderr ? error.stderr.toString() : "";
149
+ if (stderr.includes(`fatal: reference is not a tree: ${ref}`) || stderr.includes(`fatal: ambiguous argument '${ref}'`) || stderr.includes(`unknown revision or path not in the working tree`)) {
150
+ console.error(`Ref '${ref}' not found in the repository.`);
151
+ } else {
152
+ console.error(`Error checking out ref '${ref}':`, stderr.trim());
232
153
  }
233
- );
234
- try {
235
- readJsonSync(join(integrationDir, "manifest.json"));
236
- } catch (e) {
237
- console.error(
238
- `
239
- No manifest.json found in the ${integration2} integration folder. Please check that the integration exists in the Catalyst monorepo: https://github.com/bigcommerce/catalyst`
240
- );
241
- return;
242
- }
243
- const manifest = ManifestSchema.parse(readJsonSync(join(integrationDir, "manifest.json")));
244
- if (manifest.dependencies.add.length) {
245
- yield spinner(
246
- addDependency(manifest.dependencies.add, {
247
- cwd: projectDir,
248
- silent: true,
249
- packageManager
250
- }),
251
- {
252
- text: "Installing integration dependencies...",
253
- successText: "Integration dependencies installed successfully",
254
- failText: (err) => `Failed to install dependencies: ${err.message}`
255
- }
256
- );
257
154
  }
258
- if (manifest.devDependencies.add.length) {
259
- yield spinner(
260
- addDevDependency(manifest.devDependencies.add, {
261
- cwd: projectDir,
262
- silent: true,
263
- packageManager
264
- }),
265
- {
266
- text: "Installing integration development dependencies...",
267
- successText: "Integration development dependencies installed successfully",
268
- failText: (err) => `Failed to install integration development dependencies: ${err.message}`
269
- }
270
- );
155
+ if (error instanceof Error) {
156
+ console.error(`Error checking out ref '${ref}':`, error.message);
271
157
  }
272
- yield spinner(
273
- exec2(`git apply -p2 ${join(integrationDir, "integration.patch")}`, { cwd: projectDir }),
274
- {
275
- text: "Applying integration patch...",
276
- successText: "Integration patch applied successfully",
277
- failText: (err) => `Failed to apply integration patch: ${err.message}`
278
- }
279
- );
280
- console.log("\nIntegration applied successfully! Feel free to delete the `integrations` folder.");
281
- });
158
+ console.error(`Unknown error occurred while checking out ref '${ref}'.`);
159
+ }
282
160
  }
283
161
 
284
- // src/utils/check-storefront-limit.ts
285
- import chalk from "chalk";
286
- var checkStorefrontLimit = (channels, storeInfo) => {
287
- const { data: availableChannels } = channels;
288
- const { features } = storeInfo;
289
- if (availableChannels.length >= features.storefront_limits.total_including_inactive) {
290
- console.error(
291
- chalk.yellow(
292
- `You have reached the maximum number of storefronts allowed for your plan. Please select an existing channel below, purchase additional storefront seats in your BigCommerce Control Panel, or contact BigCommerce Support: ${chalk.cyan(
293
- "https://support.bigcommerce.com"
294
- )}
295
- `
296
- )
297
- );
298
- return false;
299
- }
300
- const activeChannels = availableChannels.filter((channel) => channel.status === "active");
301
- if (activeChannels.length >= features.storefront_limits.active) {
302
- console.error(
303
- chalk.yellow(
304
- "You have reached the maximum number of active storefronts allowed for your plan. Please select an existing channel below, de-activate an active channel, or purchase additional storefront seats in your BigCommerce Control Panel.\n"
305
- )
306
- );
162
+ // src/utils/has-github-ssh.ts
163
+ import { execSync as execSync2 } from "child_process";
164
+ function hasGitHubSSH() {
165
+ try {
166
+ const output = execSync2("ssh -T git@github.com", {
167
+ encoding: "utf8",
168
+ stdio: "pipe"
169
+ }).toString();
170
+ return output.includes("successfully authenticated");
171
+ } catch (error) {
172
+ if (isExecException(error)) {
173
+ const stdout = error.stdout ? error.stdout.toString() : "";
174
+ const stderr = error.stderr ? error.stderr.toString() : "";
175
+ const combinedOutput = stdout + stderr;
176
+ if (combinedOutput.includes("successfully authenticated")) {
177
+ return true;
178
+ }
179
+ }
307
180
  return false;
308
181
  }
309
- return true;
310
- };
182
+ }
311
183
 
312
184
  // src/utils/clone-catalyst.ts
313
- import chalk2 from "chalk";
314
- import { readFile, writeFile } from "fs/promises";
315
- import { ensureDir, readJsonSync as readJsonSync2, removeSync, writeJsonSync as writeJsonSync2 } from "fs-extra/esm";
316
- import { downloadTemplate as downloadTemplate2 } from "giget";
317
- import merge from "lodash.merge";
318
- import { join as join2 } from "path";
319
- import * as z2 from "zod";
320
- var cloneCatalyst = (_0) => __async(void 0, [_0], function* ({
321
- codeEditor,
322
- includeFunctionalTests,
323
- projectDir,
185
+ var cloneCatalyst = ({
186
+ repository,
324
187
  projectName,
325
- ghRef = "main"
326
- }) {
327
- var _a;
328
- yield spinner(
329
- downloadTemplate2(`github:bigcommerce/catalyst/core#${ghRef}`, {
330
- dir: projectDir,
331
- offline: false
332
- }),
333
- {
334
- text: "Cloning Catalyst template...",
335
- successText: "Catalyst template cloned successfully",
336
- failText: (err) => chalk2.red(`Failed to clone Catalyst template: ${err.message}`)
337
- }
338
- );
339
- switch (codeEditor) {
340
- case "vscode":
341
- yield ensureDir(join2(projectDir, ".vscode"));
342
- writeJsonSync2(
343
- join2(projectDir, ".vscode/settings.json"),
344
- { "typescript.tsdk": "node_modules/typescript/lib" },
345
- { spaces: 2 }
346
- );
347
- break;
348
- default:
349
- break;
350
- }
351
- const packageJson = z2.object({
352
- name: z2.string(),
353
- description: z2.string(),
354
- version: z2.string(),
355
- scripts: z2.object({}).passthrough().optional(),
356
- dependencies: z2.object({}).passthrough(),
357
- devDependencies: z2.object({}).passthrough()
358
- }).passthrough().parse(
359
- merge({}, readJsonSync2(join2(projectDir, "package.json")), {
360
- name: projectName,
361
- description: ""
362
- })
363
- );
364
- delete packageJson.dependencies["@bigcommerce/catalyst-client"];
365
- delete packageJson.devDependencies["@bigcommerce/eslint-config-catalyst"];
366
- if (!includeFunctionalTests) {
367
- delete packageJson.devDependencies["@faker-js/faker"];
368
- delete packageJson.devDependencies["@playwright/test"];
369
- }
370
- writeJsonSync2(join2(projectDir, "package.json"), packageJson, { spaces: 2 });
371
- if (!includeFunctionalTests) {
372
- const tsConfigJson = z2.object({
373
- include: z2.array(z2.string()).optional()
374
- }).passthrough().parse(readJsonSync2(join2(projectDir, "tsconfig.json")));
375
- tsConfigJson.include = (_a = tsConfigJson.include) == null ? void 0 : _a.filter(
376
- (include) => !["tests/**/*", "playwright.config.ts"].includes(include)
377
- );
378
- writeJsonSync2(join2(projectDir, "tsconfig.json"), tsConfigJson, { spaces: 2 });
379
- }
380
- if (!includeFunctionalTests) {
381
- const eslint = (yield readFile(join2(projectDir, ".eslintrc.cjs"), { encoding: "utf-8" })).replace(/['"]playwright-report\/\*\*['"],?/, "").replace(/['"]test-results\/\*\*['"],?/, "");
382
- yield writeFile(join2(projectDir, ".eslintrc.cjs"), eslint);
383
- removeSync(join2(projectDir, "tests"));
384
- removeSync(join2(projectDir, "playwright.config.ts"));
385
- }
386
- });
387
-
388
- // src/utils/get-latest-core-tag.ts
389
- import * as z3 from "zod";
390
- var getLatestCoreTag = () => __async(void 0, null, function* () {
391
- const rawPackageJsonUrl = "https://raw.githubusercontent.com/bigcommerce/catalyst/main/core/package.json";
392
- const response = yield fetch(rawPackageJsonUrl);
393
- try {
394
- const { version } = z3.object({ version: z3.string() }).parse(yield response.json());
395
- return `@bigcommerce/catalyst-core@${version}`;
396
- } catch (e) {
397
- throw new Error("Unable to determine the latest valid Catalyst release");
188
+ projectDir,
189
+ ghRef
190
+ }) => {
191
+ const useSSH = hasGitHubSSH();
192
+ console.log(`Cloning ${repository} using ${useSSH ? "SSH" : "HTTPS"}...
193
+ `);
194
+ const cloneCommand = `git clone ${useSSH ? `git@github.com:${repository}` : `https://github.com/${repository}`}.git${projectName ? ` ${projectName}` : ""}`;
195
+ execSync3(cloneCommand, { stdio: "inherit" });
196
+ console.log();
197
+ execSync3("git remote rename origin upstream", { cwd: projectDir, stdio: "inherit" });
198
+ console.log();
199
+ if (ghRef) {
200
+ checkoutRef(projectDir, ghRef);
201
+ console.log();
398
202
  }
399
- });
203
+ };
400
204
 
401
205
  // src/utils/https.ts
402
- import chalk3 from "chalk";
403
- import { z as z5 } from "zod";
206
+ import chalk from "chalk";
207
+ import { z as z2 } from "zod";
404
208
 
405
209
  // src/utils/parse.ts
406
- import * as z4 from "zod";
210
+ import * as z from "zod";
407
211
  import { fromZodError } from "zod-validation-error";
408
- var parse2 = (data, schema) => {
212
+ var parse = (data, schema) => {
409
213
  try {
410
214
  return schema.parse(data);
411
215
  } catch (error) {
412
- if (error instanceof z4.ZodError) {
216
+ if (error instanceof z.ZodError) {
413
217
  console.error(fromZodError(error).toString());
414
218
  }
415
219
  process.exit(1);
416
220
  }
417
221
  };
418
222
 
223
+ // src/utils/user-agent.ts
224
+ import {
225
+ isDevelopment,
226
+ isLinux,
227
+ isMacOS,
228
+ isProduction,
229
+ isTest,
230
+ isWindows,
231
+ nodeVersion,
232
+ process as process2,
233
+ provider,
234
+ runtime
235
+ } from "std-env";
236
+ var { name, version } = package_default;
237
+ var getOS = () => {
238
+ if (isWindows) return "Windows";
239
+ if (isMacOS) return "macOS";
240
+ if (isLinux) return "Linux";
241
+ return "Unknown OS";
242
+ };
243
+ var getEnv = () => {
244
+ if (isDevelopment) return "Development";
245
+ if (isTest) return "Test";
246
+ if (isProduction) return "Production";
247
+ return "Unknown Env";
248
+ };
249
+ var getPlatform = () => {
250
+ const os = getOS();
251
+ const env = getEnv();
252
+ const keysOfInterest = [
253
+ os,
254
+ env,
255
+ runtime,
256
+ provider,
257
+ `Node ${nodeVersion}`,
258
+ process2.env.NODE_ENV
259
+ ].filter(Boolean);
260
+ return keysOfInterest.join("; ");
261
+ };
262
+ var detectedPlatform = getPlatform();
263
+ var getCLIUserAgent = (platform, extensions) => {
264
+ const userAgentParts = [`${name}/${version}`];
265
+ const platformValue = platform != null ? platform : detectedPlatform;
266
+ userAgentParts.push(`(${platformValue})`);
267
+ if (extensions) {
268
+ userAgentParts.push(extensions);
269
+ }
270
+ return userAgentParts.join(" ");
271
+ };
272
+
419
273
  // src/utils/https.ts
420
- var BigCommerceStoreInfo = z5.object({
421
- features: z5.object({
422
- storefront_limits: z5.object({
423
- active: z5.number(),
424
- total_including_inactive: z5.number()
274
+ var BigCommerceStoreInfo = z2.object({
275
+ features: z2.object({
276
+ storefront_limits: z2.object({
277
+ active: z2.number(),
278
+ total_including_inactive: z2.number()
425
279
  })
426
280
  })
427
281
  });
428
- var BigCommerceV3ApiResponseSchema = (schema) => z5.object({
282
+ var BigCommerceV3ApiResponseSchema = (schema) => z2.object({
429
283
  data: schema,
430
- meta: z5.object({})
284
+ meta: z2.object({})
431
285
  });
432
286
  var BigCommerceChannelsV3ResponseSchema = BigCommerceV3ApiResponseSchema(
433
- z5.array(
434
- z5.object({
435
- id: z5.number(),
436
- name: z5.string(),
437
- status: z5.string(),
438
- platform: z5.string()
287
+ z2.array(
288
+ z2.object({
289
+ id: z2.number(),
290
+ name: z2.string(),
291
+ status: z2.string(),
292
+ platform: z2.string()
439
293
  })
440
294
  )
441
295
  );
@@ -453,6 +307,7 @@ var Https = class {
453
307
  this.sampleDataApiUrl = sampleDataApiUrl != null ? sampleDataApiUrl : "";
454
308
  this.storeHash = storeHash != null ? storeHash : "";
455
309
  this.accessToken = accessToken != null ? accessToken : "";
310
+ this.userAgent = getCLIUserAgent();
456
311
  }
457
312
  auth(path, opts = {}) {
458
313
  if (!this.bigCommerceAuthUrl) {
@@ -463,7 +318,8 @@ var Https = class {
463
318
  method: "POST",
464
319
  headers: __spreadProps(__spreadValues({}, headers), {
465
320
  Accept: "application/json",
466
- "Content-Type": "application/json"
321
+ "Content-Type": "application/json",
322
+ "User-Agent": this.userAgent
467
323
  })
468
324
  }, rest);
469
325
  return fetch(`${this.bigCommerceAuthUrl}${path}`, options);
@@ -473,33 +329,33 @@ var Https = class {
473
329
  const response = yield this.auth("/device/token", {
474
330
  body: JSON.stringify({
475
331
  scopes: [
332
+ "store_channel_settings",
333
+ "store_sites",
334
+ "store_storefront_api",
335
+ "store_storefront_api_customer_impersonation",
476
336
  "store_v2_content",
477
337
  "store_v2_information",
478
- "store_v2_products",
479
- "store_cart",
480
- "store_sites",
481
- "store_channel_settings",
482
- "store_storefront_api_customer_impersonation"
338
+ "store_v2_products"
483
339
  ].join(" "),
484
340
  client_id: this.DEVICE_OAUTH_CLIENT_ID
485
341
  })
486
342
  });
487
343
  if (!response.ok) {
488
344
  console.error(
489
- chalk3.red(`
345
+ chalk.red(`
490
346
  POST /device/token failed: ${response.status} ${response.statusText}
491
347
  `)
492
348
  );
493
349
  process.exit(1);
494
350
  }
495
- const DeviceCodeSchema = z5.object({
496
- device_code: z5.string(),
497
- user_code: z5.string(),
498
- verification_uri: z5.string(),
499
- expires_in: z5.number(),
500
- interval: z5.number()
351
+ const DeviceCodeSchema = z2.object({
352
+ device_code: z2.string(),
353
+ user_code: z2.string(),
354
+ verification_uri: z2.string(),
355
+ expires_in: z2.number(),
356
+ interval: z2.number()
501
357
  });
502
- return parse2(yield response.json(), DeviceCodeSchema);
358
+ return parse(yield response.json(), DeviceCodeSchema);
503
359
  });
504
360
  }
505
361
  checkDeviceCode(deviceCode) {
@@ -513,13 +369,13 @@ POST /device/token failed: ${response.status} ${response.statusText}
513
369
  if (response.status !== 200) {
514
370
  throw new Error("Device code not yet verified");
515
371
  }
516
- const DeviceCodeSuccessSchema = z5.object({
517
- access_token: z5.string(),
518
- store_hash: z5.string(),
519
- context: z5.string(),
520
- api_uri: z5.string().url()
372
+ const DeviceCodeSuccessSchema = z2.object({
373
+ access_token: z2.string(),
374
+ store_hash: z2.string(),
375
+ context: z2.string(),
376
+ api_uri: z2.string().url()
521
377
  });
522
- return parse2(yield response.json(), DeviceCodeSuccessSchema);
378
+ return parse(yield response.json(), DeviceCodeSuccessSchema);
523
379
  });
524
380
  }
525
381
  api(path, opts = {}) {
@@ -532,7 +388,8 @@ POST /device/token failed: ${response.status} ${response.statusText}
532
388
  const options = __spreadValues({
533
389
  headers: __spreadProps(__spreadValues({}, headers), {
534
390
  Accept: "application/json",
535
- "X-Auth-Token": this.accessToken
391
+ "X-Auth-Token": this.accessToken,
392
+ "User-Agent": this.userAgent
536
393
  })
537
394
  }, rest);
538
395
  return fetch(`${this.bigCommerceApiUrl}/stores/${this.storeHash}${path}`, options);
@@ -541,24 +398,24 @@ POST /device/token failed: ${response.status} ${response.statusText}
541
398
  return __async(this, null, function* () {
542
399
  const res = yield this.api("/v2/store");
543
400
  if (!res.ok) {
544
- console.error(chalk3.red(`
401
+ console.error(chalk.red(`
545
402
  GET /v2/store failed: ${res.status} ${res.statusText}
546
403
  `));
547
404
  process.exit(1);
548
405
  }
549
- return parse2(yield res.json(), BigCommerceStoreInfo);
406
+ return parse(yield res.json(), BigCommerceStoreInfo);
550
407
  });
551
408
  }
552
409
  channels(query = "") {
553
410
  return __async(this, null, function* () {
554
411
  const res = yield this.api(`/v3/channels${query}`);
555
412
  if (!res.ok) {
556
- console.error(chalk3.red(`
413
+ console.error(chalk.red(`
557
414
  GET /v3/channels failed: ${res.status} ${res.statusText}
558
415
  `));
559
416
  process.exit(1);
560
417
  }
561
- return parse2(yield res.json(), BigCommerceChannelsV3ResponseSchema);
418
+ return parse(yield res.json(), BigCommerceChannelsV3ResponseSchema);
562
419
  });
563
420
  }
564
421
  createChannelMenus(channelId) {
@@ -578,7 +435,7 @@ GET /v3/channels failed: ${res.status} ${res.statusText}
578
435
  });
579
436
  if (!res.ok) {
580
437
  console.warn(
581
- chalk3.yellow(
438
+ chalk.yellow(
582
439
  `
583
440
  Failed to create channel menus: ${res.status} ${res.statusText}. You may want to create these later: https://developer.bigcommerce.com/docs/rest-management/channels/menus#create-channel-menus
584
441
  `
@@ -596,7 +453,7 @@ Failed to create channel menus: ${res.status} ${res.statusText}. You may want to
596
453
  });
597
454
  if (!res.ok) {
598
455
  console.error(
599
- chalk3.red(
456
+ chalk.red(
600
457
  `
601
458
  POST /v3/storefront/api-token-customer-impersonation failed: ${res.status} ${res.statusText}
602
459
  `
@@ -604,12 +461,12 @@ POST /v3/storefront/api-token-customer-impersonation failed: ${res.status} ${res
604
461
  );
605
462
  process.exit(1);
606
463
  }
607
- const BigCommerceCustomerImpersonationTokenSchema = z5.object({
608
- data: z5.object({
609
- token: z5.string()
464
+ const BigCommerceCustomerImpersonationTokenSchema = z2.object({
465
+ data: z2.object({
466
+ token: z2.string()
610
467
  })
611
468
  });
612
- return parse2(yield res.json(), BigCommerceCustomerImpersonationTokenSchema);
469
+ return parse(yield res.json(), BigCommerceCustomerImpersonationTokenSchema);
613
470
  });
614
471
  }
615
472
  sampleDataApi(path, opts = {}) {
@@ -624,11 +481,36 @@ POST /v3/storefront/api-token-customer-impersonation failed: ${res.status} ${res
624
481
  headers: __spreadProps(__spreadValues({}, headers), {
625
482
  Accept: "application/json",
626
483
  "Content-Type": "application/json",
627
- "X-Auth-Token": this.accessToken
484
+ "X-Auth-Token": this.accessToken,
485
+ "User-Agent": this.userAgent
628
486
  })
629
487
  }, rest);
630
488
  return fetch(`${this.sampleDataApiUrl}/stores/${this.storeHash}${path}`, options);
631
489
  }
490
+ checkEligibility() {
491
+ return __async(this, null, function* () {
492
+ const res = yield this.sampleDataApi("/v3/channels/catalyst/eligibility", {
493
+ method: "GET"
494
+ });
495
+ if (!res.ok) {
496
+ console.error(
497
+ chalk.red(
498
+ `
499
+ GET /v3/channels/catalyst/eligibility failed: ${res.status} ${res.statusText}
500
+ `
501
+ )
502
+ );
503
+ process.exit(1);
504
+ }
505
+ const CheckEligibilitySchema = z2.object({
506
+ data: z2.object({
507
+ eligible: z2.boolean(),
508
+ message: z2.string()
509
+ })
510
+ });
511
+ return parse(yield res.json(), CheckEligibilitySchema);
512
+ });
513
+ }
632
514
  createChannel(channelName) {
633
515
  return __async(this, null, function* () {
634
516
  const res = yield this.sampleDataApi("/v3/channels/catalyst", {
@@ -636,51 +518,51 @@ POST /v3/storefront/api-token-customer-impersonation failed: ${res.status} ${res
636
518
  });
637
519
  if (!res.ok) {
638
520
  console.error(
639
- chalk3.red(`
521
+ chalk.red(`
640
522
  POST /v3/channels/catalyst failed: ${res.status} ${res.statusText}
641
523
  `)
642
524
  );
643
525
  process.exit(1);
644
526
  }
645
- const SampleDataChannelCreateSchema = z5.object({
646
- data: z5.object({
647
- id: z5.number(),
648
- name: z5.string().min(1),
649
- storefront_api_token: z5.string()
527
+ const SampleDataChannelCreateSchema = z2.object({
528
+ data: z2.object({
529
+ id: z2.number(),
530
+ name: z2.string().min(1),
531
+ storefront_api_token: z2.string()
650
532
  })
651
533
  });
652
- return parse2(yield res.json(), SampleDataChannelCreateSchema);
534
+ return parse(yield res.json(), SampleDataChannelCreateSchema);
653
535
  });
654
536
  }
655
537
  };
656
538
 
657
539
  // src/utils/install-dependencies.ts
658
- import chalk4 from "chalk";
659
- import { addDependency as addDependency2, addDevDependency as addDevDependency2, installDependencies as installDeps } from "nypm";
660
- var installAllDeps = (projectDir, packageManager) => __async(void 0, null, function* () {
661
- yield installDeps({ cwd: projectDir, silent: true, packageManager });
662
- yield addDependency2("@bigcommerce/catalyst-client", {
663
- cwd: projectDir,
664
- silent: true,
665
- packageManager
666
- });
667
- yield addDevDependency2("@bigcommerce/eslint-config-catalyst", {
668
- cwd: projectDir,
669
- silent: true,
670
- packageManager
671
- });
540
+ import chalk2 from "chalk";
541
+ import { installDependencies as installDeps } from "nypm";
542
+
543
+ // src/utils/spinner.ts
544
+ import { oraPromise } from "ora";
545
+ var spinner = (action, oraOpts) => __async(void 0, null, function* () {
546
+ return oraPromise(action, __spreadValues({
547
+ spinner: "triangle"
548
+ }, oraOpts)).catch(() => process.exit(1));
549
+ });
550
+
551
+ // src/utils/install-dependencies.ts
552
+ var installAllDeps = (projectDir) => __async(void 0, null, function* () {
553
+ yield installDeps({ cwd: projectDir, silent: true, packageManager: "pnpm" });
672
554
  });
673
- var installDependencies = (projectDir, packageManager) => __async(void 0, null, function* () {
674
- return spinner(installAllDeps(projectDir, packageManager), {
555
+ var installDependencies = (projectDir) => __async(void 0, null, function* () {
556
+ return spinner(installAllDeps(projectDir), {
675
557
  text: `Installing dependencies. This could take a minute...`,
676
558
  successText: `Dependencies installed successfully`,
677
- failText: (err) => chalk4.red(`Failed to install dependencies: ${err.message}`)
559
+ failText: (err) => chalk2.red(`Failed to install dependencies: ${err.message}`)
678
560
  });
679
561
  });
680
562
 
681
563
  // src/utils/login.ts
682
564
  import { select } from "@inquirer/prompts";
683
- import chalk5 from "chalk";
565
+ import chalk3 from "chalk";
684
566
  var poll = (auth, deviceCode, interval, expiresIn) => __async(void 0, null, function* () {
685
567
  const intervalMs = interval * 1e3;
686
568
  const expiresAtMs = expiresIn * 1e3;
@@ -692,7 +574,7 @@ var poll = (auth, deviceCode, interval, expiresIn) => __async(void 0, null, func
692
574
  }
693
575
  yield new Promise((resolve) => setTimeout(resolve, intervalMs));
694
576
  }
695
- console.error(chalk5.red("\nDevice code expired. Please try again.\n"));
577
+ console.error(chalk3.red("\nDevice code expired. Please try again.\n"));
696
578
  process.exit(1);
697
579
  });
698
580
  var login = (bigCommerceAuthUrl, storeHash, accessToken) => __async(void 0, null, function* () {
@@ -714,11 +596,11 @@ var login = (bigCommerceAuthUrl, storeHash, accessToken) => __async(void 0, null
714
596
  console.log(
715
597
  [
716
598
  `
717
- Please visit ${chalk5.cyan(deviceCode.verification_uri)} and enter the code: ${chalk5.yellow(
599
+ Please visit ${chalk3.cyan(deviceCode.verification_uri)} and enter the code: ${chalk3.yellow(
718
600
  deviceCode.user_code
719
601
  )}
720
602
  `,
721
- `The code will expire in ${chalk5.yellow(`${deviceCode.expires_in / 60} minutes`)}
603
+ `The code will expire in ${chalk3.yellow(`${deviceCode.expires_in / 60} minutes`)}
722
604
  `
723
605
  ].join("\n")
724
606
  );
@@ -736,37 +618,118 @@ Please visit ${chalk5.cyan(deviceCode.verification_uri)} and enter the code: ${c
736
618
  };
737
619
  });
738
620
 
739
- // src/utils/pm.ts
740
- var packageManagerChoices = ["npm", "pnpm", "yarn"];
741
- function getPackageManager() {
742
- var _a;
743
- const userAgent = (_a = process.env.npm_config_user_agent) != null ? _a : "";
744
- if (userAgent.startsWith("yarn")) {
745
- return "yarn";
621
+ // src/utils/telemetry/telemetry.ts
622
+ import { Analytics } from "@segment/analytics-node";
623
+ import Conf from "conf";
624
+ import { randomBytes } from "crypto";
625
+ var TELEMETRY_KEY_ENABLED = "telemetry.enabled";
626
+ var TELEMETRY_KEY_ID = `telemetry.anonymousId`;
627
+ var Telemetry = class {
628
+ constructor() {
629
+ this.projectName = "catalyst-cli";
630
+ this.projectVersion = package_default.version;
631
+ this.setEnabled = (_enabled) => {
632
+ var _a, _b;
633
+ const enabled = Boolean(_enabled);
634
+ (_a = this.conf) == null ? void 0 : _a.set(TELEMETRY_KEY_ENABLED, enabled);
635
+ return (_b = this.conf) == null ? void 0 : _b.path;
636
+ };
637
+ this.CATALYST_TELEMETRY_DISABLED = process.env.CATALYST_TELEMETRY_DISABLED;
638
+ try {
639
+ this.conf = new Conf({
640
+ projectName: this.projectName,
641
+ projectVersion: this.projectVersion
642
+ });
643
+ } catch (e) {
644
+ this.conf = null;
645
+ }
646
+ this.sessionId = randomBytes(32).toString("hex");
647
+ this.analytics = new Analytics({
648
+ writeKey: "not-a-valid-segment-write-key"
649
+ });
746
650
  }
747
- if (userAgent.startsWith("pnpm")) {
748
- return "pnpm";
651
+ track(eventName, payload) {
652
+ return __async(this, null, function* () {
653
+ yield new Promise((resolve) => {
654
+ if (!this.isEnabled()) {
655
+ resolve(void 0);
656
+ }
657
+ this.analytics.track(
658
+ {
659
+ event: eventName,
660
+ anonymousId: this.getAnonymousId(),
661
+ properties: __spreadProps(__spreadValues({}, payload), {
662
+ sessionId: this.sessionId
663
+ }),
664
+ context: {
665
+ app: {
666
+ name: this.projectName,
667
+ version: this.projectVersion
668
+ }
669
+ }
670
+ },
671
+ resolve
672
+ );
673
+ });
674
+ });
749
675
  }
750
- return "npm";
751
- }
676
+ identify(storeHash) {
677
+ return __async(this, null, function* () {
678
+ yield new Promise((resolve) => {
679
+ if (!this.isEnabled()) {
680
+ resolve(void 0);
681
+ }
682
+ if (!storeHash) {
683
+ resolve(void 0);
684
+ }
685
+ this.analytics.identify(
686
+ {
687
+ userId: storeHash,
688
+ anonymousId: this.getAnonymousId(),
689
+ context: {
690
+ app: {
691
+ name: this.projectName,
692
+ version: this.projectVersion
693
+ }
694
+ }
695
+ },
696
+ resolve
697
+ );
698
+ });
699
+ });
700
+ }
701
+ isEnabled() {
702
+ return !this.CATALYST_TELEMETRY_DISABLED && !!this.conf && this.conf.get(TELEMETRY_KEY_ENABLED, true);
703
+ }
704
+ getAnonymousId() {
705
+ var _a, _b;
706
+ const val = (_a = this.conf) == null ? void 0 : _a.get(TELEMETRY_KEY_ID);
707
+ if (val) {
708
+ return val;
709
+ }
710
+ const generated = randomBytes(32).toString("hex");
711
+ (_b = this.conf) == null ? void 0 : _b.set(TELEMETRY_KEY_ID, generated);
712
+ return generated;
713
+ }
714
+ };
752
715
 
753
716
  // src/utils/write-env.ts
754
- import { randomBytes } from "crypto";
755
- import { outputFileSync as outputFileSync2 } from "fs-extra/esm";
756
- import { join as join3 } from "path";
717
+ import { randomBytes as randomBytes2 } from "crypto";
718
+ import { outputFileSync } from "fs-extra/esm";
719
+ import { join } from "path";
757
720
  var writeEnv = (projectDir, {
758
721
  channelId,
759
722
  storeHash,
760
723
  customerImpersonationToken
761
724
  }) => {
762
- outputFileSync2(
763
- join3(projectDir, ".env.local"),
725
+ outputFileSync(
726
+ join(projectDir, ".env.local"),
764
727
  [
765
728
  `BIGCOMMERCE_STORE_HASH=${storeHash}`,
766
729
  `BIGCOMMERCE_CHANNEL_ID=${channelId}`,
767
730
  `BIGCOMMERCE_CUSTOMER_IMPERSONATION_TOKEN=${customerImpersonationToken}`,
768
731
  "",
769
- `AUTH_SECRET=${randomBytes(32).toString("hex")}`,
732
+ `AUTH_SECRET=${randomBytes2(32).toString("hex")}`,
770
733
  `CLIENT_LOGGER=false`,
771
734
  `ENABLE_ADMIN_ROUTE=true`,
772
735
  `DEFAULT_REVALIDATE_TARGET=3600`
@@ -775,34 +738,30 @@ var writeEnv = (projectDir, {
775
738
  };
776
739
 
777
740
  // src/commands/create.ts
778
- var exec3 = promisify3(execCallback2);
779
- var create = new Command2("create").description("Command to scaffold and connect a Catalyst storefront to your BigCommerce store").option("--project-name <name>", "Name of your Catalyst project").option("--project-dir <dir>", "Directory in which to create your project", process.cwd()).option("--store-hash <hash>", "BigCommerce store hash").option("--access-token <token>", "BigCommerce access token").option("--channel-id <id>", "BigCommerce channel ID").option("--customer-impersonation-token <token>", "BigCommerce customer impersonation token").option("--integration <provider>", "Integration to apply to your new storefront").addOption(
780
- new Option(
781
- "--gh-ref <ref>",
782
- "Clone a specific ref from the bigcommerce/catalyst repository"
783
- ).default(getLatestCoreTag)
784
- ).addOption(
741
+ var telemetry = new Telemetry();
742
+ var create = new Command("create").description("Command to scaffold and connect a Catalyst storefront to your BigCommerce store").option("--project-name <name>", "Name of your Catalyst project").option("--project-dir <dir>", "Directory in which to create your project", process.cwd()).option("--store-hash <hash>", "BigCommerce store hash").option("--access-token <token>", "BigCommerce access token").option("--channel-id <id>", "BigCommerce channel ID").option("--customer-impersonation-token <token>", "BigCommerce customer impersonation token").option("--gh-ref <ref>", "Clone a specific ref from the source repository").option("--repository <repository>", "GitHub repository to clone from", "bigcommerce/catalyst").addOption(
785
743
  new Option("--bigcommerce-hostname <hostname>", "BigCommerce hostname").default("bigcommerce.com").hideHelp()
786
744
  ).addOption(
787
745
  new Option("--sample-data-api-url <url>", "BigCommerce sample data API URL").default("https://api.bc-sample.store").hideHelp()
788
- ).addOption(
789
- new Option("--package-manager <pm>", "Override detected package manager").choices(packageManagerChoices).default(getPackageManager()).hideHelp()
790
- ).addOption(
791
- new Option("--code-editor <editor>", "Your preferred code editor").choices(["vscode"]).default("vscode").hideHelp()
792
- ).addOption(
793
- new Option("--include-functional-tests", "Include the functional test suite").default(false).hideHelp()
794
746
  ).action((options) => __async(void 0, null, function* () {
795
- const { packageManager, codeEditor, includeFunctionalTests } = options;
796
- const URLSchema = z6.string().url();
797
- const sampleDataApiUrl = parse2(options.sampleDataApiUrl, URLSchema);
798
- const bigcommerceApiUrl = parse2(`https://api.${options.bigcommerceHostname}`, URLSchema);
799
- const bigcommerceAuthUrl = parse2(`https://login.${options.bigcommerceHostname}`, URLSchema);
800
- let ghRef;
801
- if (options.ghRef instanceof Function) {
802
- ghRef = yield options.ghRef();
803
- } else {
804
- ghRef = options.ghRef;
747
+ const { ghRef, repository } = options;
748
+ try {
749
+ execSync4("which git", { stdio: "ignore" });
750
+ } catch (e) {
751
+ console.error(chalk4.red("Error: git is required to create a Catalyst project\n"));
752
+ process.exit(1);
753
+ }
754
+ try {
755
+ execSync4("which pnpm", { stdio: "ignore" });
756
+ } catch (e) {
757
+ console.error(chalk4.red("Error: pnpm is required to create a Catalyst project\n"));
758
+ console.error(chalk4.yellow("Tip: Enable it by running `corepack enable pnpm`\n"));
759
+ process.exit(1);
805
760
  }
761
+ const URLSchema = z3.string().url();
762
+ const sampleDataApiUrl = parse(options.sampleDataApiUrl, URLSchema);
763
+ const bigcommerceApiUrl = parse(`https://api.${options.bigcommerceHostname}`, URLSchema);
764
+ const bigcommerceAuthUrl = parse(`https://login.${options.bigcommerceHostname}`, URLSchema);
806
765
  let projectName;
807
766
  let projectDir;
808
767
  let storeHash = options.storeHash;
@@ -813,24 +772,24 @@ var create = new Command2("create").description("Command to scaffold and connect
813
772
  channelId = parseInt(options.channelId, 10);
814
773
  }
815
774
  if (!pathExistsSync(options.projectDir)) {
816
- console.error(chalk6.red(`Error: --projectDir ${options.projectDir} is not a valid path
775
+ console.error(chalk4.red(`Error: --projectDir ${options.projectDir} is not a valid path
817
776
  `));
818
777
  process.exit(1);
819
778
  }
820
779
  if (options.projectName) {
821
- projectName = kebabCase2(options.projectName);
822
- projectDir = join4(options.projectDir, projectName);
780
+ projectName = kebabCase(options.projectName);
781
+ projectDir = join2(options.projectDir, projectName);
823
782
  if (pathExistsSync(projectDir)) {
824
- console.error(chalk6.red(`Error: ${projectDir} already exists
783
+ console.error(chalk4.red(`Error: ${projectDir} already exists
825
784
  `));
826
785
  process.exit(1);
827
786
  }
828
787
  }
829
788
  if (!options.projectName) {
830
789
  const validateProjectName = (i) => {
831
- const formatted = kebabCase2(i);
790
+ const formatted = kebabCase(i);
832
791
  if (!formatted) return "Project name is required";
833
- const targetDir = join4(options.projectDir, formatted);
792
+ const targetDir = join2(options.projectDir, formatted);
834
793
  if (pathExistsSync(targetDir)) return `Destination '${targetDir}' already exists`;
835
794
  projectName = formatted;
836
795
  projectDir = targetDir;
@@ -853,34 +812,37 @@ var create = new Command2("create").description("Command to scaffold and connect
853
812
  console.log(`
854
813
  Creating '${projectName}' at '${projectDir}'
855
814
  `);
856
- yield cloneCatalyst({ projectDir, projectName, ghRef, codeEditor, includeFunctionalTests });
857
- console.log(`
858
- Using ${chalk6.bold(packageManager)}
859
- `);
860
- yield installDependencies(projectDir, packageManager);
861
- yield applyIntegration(options.integration, { projectDir, packageManager });
815
+ cloneCatalyst({ repository, projectName, projectDir, ghRef });
816
+ yield installDependencies(projectDir);
862
817
  console.log(
863
818
  [
864
819
  `
865
- ${chalk6.green("Success!")} Created '${projectName}' at '${projectDir}'
820
+ ${chalk4.green("Success!")} Created '${projectName}' at '${projectDir}'
866
821
  `,
867
822
  `Next steps:`,
868
- chalk6.yellow(`
823
+ chalk4.yellow(`
869
824
  - cd ${projectName} && cp .env.example .env.local`),
870
- chalk6.yellow(`
825
+ chalk4.yellow(`
871
826
  - Populate .env.local with your BigCommerce API credentials
872
827
  `)
873
828
  ].join("\n")
874
829
  );
875
830
  process.exit(0);
876
831
  }
832
+ yield telemetry.identify(storeHash);
877
833
  if (!channelId || !customerImpersonationToken) {
878
834
  const bc = new Https({ bigCommerceApiUrl: bigcommerceApiUrl, storeHash, accessToken });
879
- const availableChannels = yield bc.channels("?available=true&type=storefront");
880
- const storeInfo = yield bc.storeInformation();
881
- const canCreateChannel = checkStorefrontLimit(availableChannels, storeInfo);
835
+ const sampleDataApi = new Https({
836
+ sampleDataApiUrl,
837
+ storeHash,
838
+ accessToken
839
+ });
840
+ const eligibilityResponse = yield sampleDataApi.checkEligibility();
841
+ if (!eligibilityResponse.data.eligible) {
842
+ console.warn(chalk4.yellow(eligibilityResponse.data.message));
843
+ }
882
844
  let shouldCreateChannel;
883
- if (canCreateChannel) {
845
+ if (eligibilityResponse.data.eligible) {
884
846
  shouldCreateChannel = yield select2({
885
847
  message: "Would you like to create a new channel?",
886
848
  choices: [
@@ -893,11 +855,6 @@ ${chalk6.green("Success!")} Created '${projectName}' at '${projectDir}'
893
855
  const newChannelName = yield input({
894
856
  message: "What would you like to name your new channel?"
895
857
  });
896
- const sampleDataApi = new Https({
897
- sampleDataApiUrl,
898
- storeHash,
899
- accessToken
900
- });
901
858
  const {
902
859
  data: { id: createdChannelId, storefront_api_token: storefrontApiToken }
903
860
  } = yield sampleDataApi.createChannel(newChannelName);
@@ -907,6 +864,7 @@ ${chalk6.green("Success!")} Created '${projectName}' at '${projectDir}'
907
864
  }
908
865
  if (!shouldCreateChannel) {
909
866
  const channelSortOrder = ["catalyst", "next", "bigcommerce"];
867
+ const availableChannels = yield bc.channels("?available=true&type=storefront");
910
868
  const existingChannel = yield select2({
911
869
  message: "Which channel would you like to use?",
912
870
  choices: availableChannels.data.sort(
@@ -930,46 +888,37 @@ ${chalk6.green("Success!")} Created '${projectName}' at '${projectDir}'
930
888
  console.log(`
931
889
  Creating '${projectName}' at '${projectDir}'
932
890
  `);
933
- yield cloneCatalyst({ projectDir, projectName, ghRef, codeEditor, includeFunctionalTests });
891
+ cloneCatalyst({ repository, projectName, projectDir, ghRef });
934
892
  writeEnv(projectDir, {
935
893
  channelId: channelId.toString(),
936
894
  storeHash,
937
895
  customerImpersonationToken
938
896
  });
939
- console.log(`
940
- Using ${chalk6.bold(packageManager)}
941
- `);
942
- yield installDependencies(projectDir, packageManager);
943
- yield spinner(exec3(`${packageManager} run --prefix ${projectDir} generate`), {
944
- text: "Creating GraphQL schema...",
945
- successText: "Created GraphQL schema",
946
- failText: (err) => chalk6.red(`Failed to create GraphQL schema: ${err.message}`)
947
- });
948
- yield applyIntegration(options.integration, { projectDir, packageManager });
897
+ yield installDependencies(projectDir);
949
898
  console.log(
950
899
  `
951
- ${chalk6.green("Success!")} Created '${projectName}' at '${projectDir}'
900
+ ${chalk4.green("Success!")} Created '${projectName}' at '${projectDir}'
952
901
  `,
953
902
  "\nNext steps:\n",
954
- chalk6.yellow(`
955
- cd ${projectName} && ${packageManager} run dev
903
+ chalk4.yellow(`
904
+ cd ${projectName} && pnpm run dev
956
905
  `)
957
906
  );
958
907
  }));
959
908
 
960
909
  // src/commands/init.ts
961
- import { Command as Command3, Option as Option2 } from "@commander-js/extra-typings";
910
+ import { Command as Command2, Option as Option2 } from "@commander-js/extra-typings";
962
911
  import { input as input2, select as select3 } from "@inquirer/prompts";
963
- import chalk7 from "chalk";
964
- import * as z7 from "zod";
965
- var init = new Command3("init").description("Connect a BigCommerce store with an existing Catalyst project").option("--store-hash <hash>", "BigCommerce store hash").option("--access-token <token>", "BigCommerce access token").addOption(
912
+ import chalk5 from "chalk";
913
+ import * as z4 from "zod";
914
+ var init = new Command2("init").description("Connect a BigCommerce store with an existing Catalyst project").option("--store-hash <hash>", "BigCommerce store hash").option("--access-token <token>", "BigCommerce access token").addOption(
966
915
  new Option2("--bigcommerce-hostname <hostname>", "BigCommerce hostname").default("bigcommerce.com").hideHelp()
967
916
  ).addOption(
968
917
  new Option2("--sample-data-api-url <url>", "BigCommerce sample data API URL").default("https://api.bc-sample.store").hideHelp()
969
918
  ).action((options) => __async(void 0, null, function* () {
970
919
  const projectDir = process.cwd();
971
- const URLSchema = z7.string().url();
972
- const sampleDataApiUrl = parse2(options.sampleDataApiUrl, URLSchema);
920
+ const URLSchema = z4.string().url();
921
+ const sampleDataApiUrl = parse(options.sampleDataApiUrl, URLSchema);
973
922
  const bigCommerceApiUrl = `https://api.${options.bigcommerceHostname}`;
974
923
  const bigCommerceAuthUrl = `https://login.${options.bigcommerceHostname}`;
975
924
  let storeHash = options.storeHash;
@@ -983,16 +932,22 @@ var init = new Command3("init").description("Connect a BigCommerce store with an
983
932
  }
984
933
  if (!storeHash || !accessToken) {
985
934
  console.log(
986
- chalk7.yellow("\nYou must authenticate with a store to overwrite your local environment.\n")
935
+ chalk5.yellow("\nYou must authenticate with a store to overwrite your local environment.\n")
987
936
  );
988
937
  process.exit(1);
989
938
  }
990
939
  const bc = new Https({ bigCommerceApiUrl, storeHash, accessToken });
991
- const availableChannels = yield bc.channels("?available=true&type=storefront");
992
- const storeInfo = yield bc.storeInformation();
993
- const canCreateChannel = checkStorefrontLimit(availableChannels, storeInfo);
940
+ const sampleDataApi = new Https({
941
+ sampleDataApiUrl,
942
+ storeHash,
943
+ accessToken
944
+ });
945
+ const eligibilityResponse = yield sampleDataApi.checkEligibility();
946
+ if (!eligibilityResponse.data.eligible) {
947
+ console.warn(chalk5.yellow(eligibilityResponse.data.message));
948
+ }
994
949
  let shouldCreateChannel;
995
- if (canCreateChannel) {
950
+ if (eligibilityResponse.data.eligible) {
996
951
  shouldCreateChannel = yield select3({
997
952
  message: "Would you like to create a new channel?",
998
953
  choices: [
@@ -1005,11 +960,6 @@ var init = new Command3("init").description("Connect a BigCommerce store with an
1005
960
  const newChannelName = yield input2({
1006
961
  message: "What would you like to name your new channel?"
1007
962
  });
1008
- const sampleDataApi = new Https({
1009
- sampleDataApiUrl,
1010
- storeHash,
1011
- accessToken
1012
- });
1013
963
  const {
1014
964
  data: { id: createdChannelId, storefront_api_token: storefrontApiToken }
1015
965
  } = yield sampleDataApi.createChannel(newChannelName);
@@ -1018,6 +968,7 @@ var init = new Command3("init").description("Connect a BigCommerce store with an
1018
968
  }
1019
969
  if (!shouldCreateChannel) {
1020
970
  const channelSortOrder = ["catalyst", "next", "bigcommerce"];
971
+ const availableChannels = yield bc.channels("?available=true&type=storefront");
1021
972
  const existingChannel = yield select3({
1022
973
  message: "Which channel would you like to use?",
1023
974
  choices: availableChannels.data.sort(
@@ -1044,10 +995,134 @@ var init = new Command3("init").description("Connect a BigCommerce store with an
1044
995
  });
1045
996
  }));
1046
997
 
998
+ // src/commands/integration.ts
999
+ import { Command as Command3 } from "@commander-js/extra-typings";
1000
+ import { exec as execCb } from "child_process";
1001
+ import { parse as parse2 } from "dotenv";
1002
+ import { outputFileSync as outputFileSync2, writeJsonSync } from "fs-extra/esm";
1003
+ import kebabCase2 from "lodash.kebabcase";
1004
+ import { coerce, compare } from "semver";
1005
+ import { promisify } from "util";
1006
+ import { z as z5 } from "zod";
1007
+ var exec = promisify(execCb);
1008
+ var ManifestSchema = z5.object({
1009
+ name: z5.string(),
1010
+ dependencies: z5.object({ add: z5.array(z5.string()) }),
1011
+ devDependencies: z5.object({ add: z5.array(z5.string()) }),
1012
+ environmentVariables: z5.array(z5.string())
1013
+ });
1014
+ var integration = new Command3("integration").argument("<integration-name>", "Formatted name of the integration").option("--commit-hash <hash>", "Override integration source branch with a specific commit hash").action((integrationNameRaw, options) => __async(void 0, null, function* () {
1015
+ const integrationName = z5.string().transform(kebabCase2).parse(integrationNameRaw);
1016
+ const manifest = {
1017
+ name: integrationName,
1018
+ dependencies: { add: [] },
1019
+ devDependencies: { add: [] },
1020
+ environmentVariables: []
1021
+ };
1022
+ yield exec("git fetch --tags");
1023
+ const { stdout: headRefStdOut } = yield exec("git rev-parse --abbrev-ref HEAD");
1024
+ let [sourceRef] = headRefStdOut.split("\n");
1025
+ if (options.commitHash) {
1026
+ sourceRef = options.commitHash;
1027
+ }
1028
+ const { stdout: catalystTags } = yield exec("git tag --list @bigcommerce/catalyst-core@\\*");
1029
+ const [latestCoreTag] = catalystTags.split("\n").filter(Boolean).sort((a, b) => {
1030
+ const versionA = coerce(a.replace("@bigcommerce/catalyst-core@", ""));
1031
+ const versionB = coerce(b.replace("@bigcommerce/catalyst-core@", ""));
1032
+ if (versionA && versionB) {
1033
+ return compare(versionA, versionB);
1034
+ }
1035
+ return 0;
1036
+ }).reverse();
1037
+ const PackageDependenciesSchema = z5.object({
1038
+ dependencies: z5.object({}).passthrough(),
1039
+ devDependencies: z5.object({}).passthrough()
1040
+ });
1041
+ const getPackageDeps = (ref) => __async(void 0, null, function* () {
1042
+ const { stdout } = yield exec(`git show ${ref}:core/package.json`);
1043
+ return PackageDependenciesSchema.parse(JSON.parse(stdout));
1044
+ });
1045
+ const integrationJson = yield getPackageDeps(sourceRef);
1046
+ const latestCoreTagJson = yield getPackageDeps(latestCoreTag);
1047
+ const diffObjectKeys = (a, b) => {
1048
+ return Object.keys(a).filter((key) => !Object.keys(b).includes(key));
1049
+ };
1050
+ manifest.dependencies.add = diffObjectKeys(
1051
+ integrationJson.dependencies,
1052
+ latestCoreTagJson.dependencies
1053
+ );
1054
+ manifest.devDependencies.add = diffObjectKeys(
1055
+ integrationJson.devDependencies,
1056
+ latestCoreTagJson.devDependencies
1057
+ );
1058
+ const { stdout: latestCoreEnv } = yield exec(`git show ${latestCoreTag}:core/.env.example`);
1059
+ const { stdout: integrationEnv } = yield exec(`git show ${sourceRef}:core/.env.example`);
1060
+ manifest.environmentVariables = diffObjectKeys(parse2(integrationEnv), parse2(latestCoreEnv));
1061
+ const { stdout: integrationDiff } = yield exec(
1062
+ `git diff ${latestCoreTag}...${sourceRef} -- ':(exclude)core/package.json' ':(exclude)pnpm-lock.yaml'`
1063
+ );
1064
+ outputFileSync2(`integrations/${integrationName}/integration.patch`, integrationDiff);
1065
+ writeJsonSync(`integrations/${integrationName}/manifest.json`, manifest, {
1066
+ spaces: 2
1067
+ });
1068
+ console.log("Integration created successfully.");
1069
+ }));
1070
+
1071
+ // src/commands/telemetry.ts
1072
+ import { Argument, Command as Command4, Option as Option3 } from "@commander-js/extra-typings";
1073
+
1074
+ // src/utils/telemetry/index.ts
1075
+ import chalk6 from "chalk";
1076
+ var telemetry2 = new Telemetry();
1077
+ var isEnabled = telemetry2.isEnabled();
1078
+ var catalystTelemetry = (options, arg) => {
1079
+ if (options.enable || arg === "enable") {
1080
+ telemetry2.setEnabled(true);
1081
+ isEnabled = true;
1082
+ console.log("Success!");
1083
+ } else if (options.disable || arg === "disable") {
1084
+ const path = telemetry2.setEnabled(false);
1085
+ if (isEnabled) {
1086
+ console.log(`Your preference has been saved${path ? ` to ${path}` : ""}`);
1087
+ } else {
1088
+ console.log(`Catalyst CLI telemetry collection is already disabled.`);
1089
+ }
1090
+ isEnabled = false;
1091
+ } else {
1092
+ console.log("Catalyst CLI Telemetry");
1093
+ }
1094
+ console.log(
1095
+ `
1096
+ Status: ${chalk6.bold(isEnabled ? chalk6.green("Enabled") : chalk6.red("Disabled"))}`
1097
+ );
1098
+ if (!isEnabled) {
1099
+ console.log(
1100
+ `
1101
+ You have opted-out of Catalyst CLI telemetry.
1102
+ No data will be collected from your machine.`
1103
+ );
1104
+ }
1105
+ };
1106
+
1107
+ // src/commands/telemetry.ts
1108
+ var telemetry3 = new Command4("telemetry").addArgument(new Argument("[arg]").choices(["disable", "enable", "status"])).addOption(new Option3("--enable", `Enables CLI telemetry collection.`).conflicts("disable")).option("--disable", `Disables CLI telemetry collection.`).action(
1109
+ (arg, options) => catalystTelemetry(options, arg)
1110
+ );
1111
+
1112
+ // src/hooks/telemetry.ts
1113
+ var telemetry4 = new Telemetry();
1114
+ var telemetryPreHook = (command) => __async(void 0, null, function* () {
1115
+ const [commandName] = command.args;
1116
+ yield telemetry4.track(commandName, {});
1117
+ });
1118
+ var telemetryPostHook = () => __async(void 0, null, function* () {
1119
+ yield telemetry4.analytics.closeAndFlush();
1120
+ });
1121
+
1047
1122
  // src/index.ts
1048
- console.log(chalk8.cyanBright(`
1123
+ console.log(chalk7.cyanBright(`
1049
1124
  \u25E2 ${package_default.name} v${package_default.version}
1050
1125
  `));
1051
- program.name(package_default.name).version(package_default.version).description("A command line tool to create a new Catalyst project.").addCommand(create, { isDefault: true }).addCommand(init).addCommand(integration);
1126
+ program.name(package_default.name).version(package_default.version).description("A command line tool to create a new Catalyst project.").addCommand(create, { isDefault: true }).addCommand(init).addCommand(integration).addCommand(telemetry3).hook("preAction", telemetryPreHook).hook("postAction", telemetryPostHook);
1052
1127
  program.parse(process.argv);
1053
1128
  //# sourceMappingURL=index.js.map