@bigcommerce/catalyst 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -25,7 +25,7 @@ import { randomBytes, randomUUID } from "crypto";
25
25
  // package.json
26
26
  var package_default = {
27
27
  name: "@bigcommerce/catalyst",
28
- version: "1.1.1",
28
+ version: "1.2.0",
29
29
  type: "module",
30
30
  repository: {
31
31
  type: "git",
@@ -119,6 +119,8 @@ function getUserConfig() {
119
119
  // src/cli/lib/telemetry.ts
120
120
  var TELEMETRY_KEY_ENABLED = "telemetry.enabled";
121
121
  var TELEMETRY_KEY_ID = `telemetry.anonymousId`;
122
+ var TELEMETRY_FLUSH_INTERVAL_MS = 200;
123
+ var TELEMETRY_HTTP_REQUEST_TIMEOUT_MS = 200;
122
124
  var Telemetry = class {
123
125
  constructor() {
124
126
  this.commandName = "unknown";
@@ -133,7 +135,10 @@ var Telemetry = class {
133
135
  this.correlationId = randomUUID();
134
136
  this.startTime = Date.now();
135
137
  this.analytics = new Analytics({
136
- writeKey: "OCkxLj3KTc1N3ebEbnFBqbhxfE3tVb3Z"
138
+ writeKey: "OCkxLj3KTc1N3ebEbnFBqbhxfE3tVb3Z",
139
+ flushInterval: TELEMETRY_FLUSH_INTERVAL_MS,
140
+ httpRequestTimeout: TELEMETRY_HTTP_REQUEST_TIMEOUT_MS,
141
+ maxRetries: 0
137
142
  });
138
143
  }
139
144
  durationMs() {
@@ -2712,7 +2717,7 @@ function collectDiagnostics({
2712
2717
  env: env2 = process.env
2713
2718
  } = {}) {
2714
2719
  const state = getProjectState(cwd);
2715
- const telemetry3 = getTelemetry();
2720
+ const telemetry2 = getTelemetry();
2716
2721
  const projectJson = readProjectJson(cwd);
2717
2722
  const storefront = readStorefrontInfo(cwd);
2718
2723
  const cliEnv = [{ source: "process.env", values: env2 }];
@@ -2764,8 +2769,8 @@ function collectDiagnostics({
2764
2769
  buildEnvVars: resolveEnvVars(buildEnv, BUILD_ENV_VARS)
2765
2770
  },
2766
2771
  telemetry: {
2767
- enabled: telemetry3.isEnabled(),
2768
- correlationId: telemetry3.correlationId
2772
+ enabled: telemetry2.isEnabled(),
2773
+ correlationId: telemetry2.correlationId
2769
2774
  },
2770
2775
  files: {
2771
2776
  ".env.local": existsSync5(join11(cwd, ".env.local")),
@@ -3147,8 +3152,8 @@ Example:
3147
3152
  const config2 = getProjectConfig();
3148
3153
  const apiHost = resolveApiHost(options, config2);
3149
3154
  const { storeHash, accessToken } = resolveCredentials(options, config2);
3150
- const telemetry3 = getTelemetry();
3151
- await telemetry3.identify(storeHash);
3155
+ const telemetry2 = getTelemetry();
3156
+ await telemetry2.identify(storeHash);
3152
3157
  const linkedProjectUuid = options.projectUuid ?? config2.get("projectUuid");
3153
3158
  let projectUuid;
3154
3159
  const promptForProject = async () => {
@@ -3301,7 +3306,22 @@ var logEntrySchema = z14.object({
3301
3306
  }).nullable().optional()
3302
3307
  });
3303
3308
  var queryLogsSchema = z14.object({
3304
- data: z14.array(logEntrySchema)
3309
+ data: z14.array(logEntrySchema),
3310
+ meta: z14.object({
3311
+ cursor_pagination: z14.object({
3312
+ // The REST proxy exposes page availability via links, while the
3313
+ // gRPC response uses has_next_page/has_prev_page. Accept both while
3314
+ // the API contract is transitioning.
3315
+ has_next_page: z14.boolean().optional(),
3316
+ has_prev_page: z14.boolean().optional(),
3317
+ start_cursor: z14.string().nullable().optional(),
3318
+ end_cursor: z14.string().nullable().optional(),
3319
+ links: z14.object({
3320
+ next: z14.string().optional(),
3321
+ previous: z14.string().optional()
3322
+ }).optional()
3323
+ }).optional()
3324
+ }).optional()
3305
3325
  });
3306
3326
  var v3ErrorSchema = z14.object({
3307
3327
  title: z14.string().optional(),
@@ -3416,6 +3436,8 @@ async function queryLogs(projectUuid, storeHash, accessToken, apiHost, params) {
3416
3436
  if (params.urlLike) search.set("url:like", params.urlLike);
3417
3437
  if (params.levelMin) search.set("level:min", params.levelMin);
3418
3438
  if (params.limit != null) search.set("limit", String(params.limit));
3439
+ if (params.after) search.set("after", params.after);
3440
+ if (params.before) search.set("before", params.before);
3419
3441
  const response = await fetch(
3420
3442
  `https://${apiHost}/stores/${storeHash}/v3/infrastructure/logs/${projectUuid}?${search.toString()}`,
3421
3443
  {
@@ -3434,7 +3456,9 @@ async function queryLogs(projectUuid, storeHash, accessToken, apiHost, params) {
3434
3456
  );
3435
3457
  }
3436
3458
  if (response.status === 404) {
3437
- throw new UserActionableError("Project not found. Check the project UUID.");
3459
+ throw new UserActionableError(
3460
+ "Project not found. Check the project UUID, and that --store-hash and --access-token belong to the store that owns the project."
3461
+ );
3438
3462
  }
3439
3463
  if (response.status === 400 || response.status === 422) {
3440
3464
  const body = await response.json().catch(() => null);
@@ -4083,7 +4107,6 @@ var env = new Command8("env").configureHelp({ showGlobalOptions: true }).descrip
4083
4107
  import { Command as Command9, InvalidArgumentError as InvalidArgumentError4, Option as Option8 } from "commander";
4084
4108
  import { colorize as colorize10 } from "consola/utils";
4085
4109
  import { z as z16 } from "zod";
4086
- var telemetry = new Telemetry();
4087
4110
  var DEFAULT_CONNECTION_TTL_MS = 1 * 60 * 1e3;
4088
4111
  var MAX_RETRIES = 5;
4089
4112
  var isFatalStatusCode = (status) => status >= 400 && status < 500;
@@ -4282,7 +4305,7 @@ Examples:
4282
4305
  const config2 = getProjectConfig();
4283
4306
  const apiHost = resolveApiHost(options, config2);
4284
4307
  const { storeHash, accessToken } = resolveCredentials(options, config2);
4285
- await telemetry.identify(storeHash);
4308
+ await getTelemetry().identify(storeHash);
4286
4309
  const projectUuid = resolveProjectUuid(options);
4287
4310
  await tailLogs(projectUuid, storeHash, accessToken, apiHost, options.format);
4288
4311
  } catch (error) {
@@ -4297,17 +4320,68 @@ var parseIntInRange = (flag, min, max) => (value) => {
4297
4320
  }
4298
4321
  return parsed;
4299
4322
  };
4323
+ function invocationPrefix() {
4324
+ const packageManager = process.env.npm_config_user_agent?.split("/")[0];
4325
+ switch (packageManager) {
4326
+ case "pnpm":
4327
+ case "yarn":
4328
+ return `${packageManager} catalyst`;
4329
+ case "bun":
4330
+ return "bunx catalyst";
4331
+ case "npm":
4332
+ return "npx catalyst";
4333
+ default:
4334
+ return "catalyst";
4335
+ }
4336
+ }
4337
+ function printPaginationHints(pagination, start2, end, options) {
4338
+ const hasNextPage = Boolean(pagination?.links?.next) || pagination?.has_next_page === true;
4339
+ const hasPrevPage = Boolean(pagination?.links?.previous) || pagination?.has_prev_page === true;
4340
+ const endCursor = pagination?.end_cursor;
4341
+ const startCursor = pagination?.start_cursor;
4342
+ if (!(hasNextPage && endCursor) && !(hasPrevPage && startCursor)) return;
4343
+ const quoteArg = (value) => /\s/.test(value) ? `'${value}'` : value;
4344
+ const baseFlags = [
4345
+ `--start ${quoteArg(start2)}`,
4346
+ `--end ${quoteArg(end)}`,
4347
+ ...options.method ? [`--method ${quoteArg(options.method)}`] : [],
4348
+ ...options.statusCode != null ? [`--status-code ${options.statusCode}`] : [],
4349
+ ...options.urlLike ? [`--url-like ${quoteArg(options.urlLike)}`] : [],
4350
+ ...options.levelMin ? [`--level-min ${options.levelMin}`] : [],
4351
+ ...options.limit != null ? [`--limit ${options.limit}`] : [],
4352
+ ...options.format !== "default" ? [`--format ${options.format}`] : []
4353
+ ];
4354
+ const command = `${invocationPrefix()} logs query ${baseFlags.join(" ")}`;
4355
+ if (hasNextPage && endCursor) {
4356
+ consola.info(`More results available. Next page:
4357
+ ${command} --after ${quoteArg(endCursor)}`);
4358
+ }
4359
+ if (hasPrevPage && startCursor) {
4360
+ consola.info(
4361
+ `Newer results available. Previous page:
4362
+ ${command} --before ${quoteArg(startCursor)}`
4363
+ );
4364
+ }
4365
+ }
4300
4366
  var query = new Command9("query").configureHelp({ showGlobalOptions: true }).description("Query historical logs from your deployed application.").addHelpText(
4301
4367
  "after",
4302
4368
  `
4303
4369
  Specify a time window with \`--since\` (relative to now) or \`--start\`/\`--end\`
4304
4370
  (ISO-8601 timestamps or Unix epoch seconds, UTC). The window may not exceed
4305
- 7 days. Entries print oldest-first; timestamps are UTC.
4371
+ 7 days. Entries print oldest-first; timestamps are UTC. Page toward older
4372
+ entries by passing a previous page's end cursor to \`--after\` (the CLI prints
4373
+ a ready-to-run command when more entries are available), or toward newer
4374
+ entries with \`--before\`. Cursors are only valid with the same window and
4375
+ filters they came from. With \`--format json\`, the pagination cursors are
4376
+ emitted as a final \`{"meta": ...}\` line after the entries.
4306
4377
 
4307
4378
  Examples:
4308
4379
  # Last hour of logs
4309
4380
  $ catalyst logs query --since 1h
4310
4381
 
4382
+ # Newest 20 errors in the window (follow the printed --after hint for more)
4383
+ $ catalyst logs query --since 24h --level-min error --limit 20
4384
+
4311
4385
  # Everything from today (UTC)
4312
4386
  $ catalyst logs query --start 2026-06-11T00:00:00Z
4313
4387
 
@@ -4334,7 +4408,20 @@ Examples:
4334
4408
  ).option("--url-like <substring>", "Filter by URL substring (case-sensitive).").addOption(
4335
4409
  new Option8("--level-min <level>", "Minimum log level.").choices(Array.from(LOG_LEVELS))
4336
4410
  ).addOption(
4337
- new Option8("--limit <n>", "Maximum number of entries to return (1-500).").argParser(parseIntInRange("--limit", 1, 500)).default(100)
4411
+ new Option8(
4412
+ "--limit <count>",
4413
+ "Maximum entries per page (1-500). Defaults to the API default (100)."
4414
+ ).argParser(parseIntInRange("--limit", 1, 500))
4415
+ ).addOption(
4416
+ new Option8(
4417
+ "--after <cursor>",
4418
+ "Return the page after (older than) this cursor, from a previous page's end_cursor."
4419
+ ).conflicts("before")
4420
+ ).addOption(
4421
+ new Option8(
4422
+ "--before <cursor>",
4423
+ "Return the page before (newer than) this cursor, from a previous page's start_cursor."
4424
+ )
4338
4425
  ).addOption(
4339
4426
  new Option8("--format <format>", "Output format for log entries.").choices(["json", "pretty", "default", "short", "request"]).default("default")
4340
4427
  ).action(async (options) => {
@@ -4342,7 +4429,7 @@ Examples:
4342
4429
  const config2 = getProjectConfig();
4343
4430
  const apiHost = resolveApiHost(options, config2);
4344
4431
  const { storeHash, accessToken } = resolveCredentials(options, config2);
4345
- await telemetry.identify(storeHash);
4432
+ await getTelemetry().identify(storeHash);
4346
4433
  const projectUuid = resolveProjectUuid(options);
4347
4434
  const { start: start2, end } = resolveTimeWindow(options);
4348
4435
  const result = await queryLogs(projectUuid, storeHash, accessToken, apiHost, {
@@ -4352,13 +4439,22 @@ Examples:
4352
4439
  statusCode: options.statusCode,
4353
4440
  urlLike: options.urlLike,
4354
4441
  levelMin: options.levelMin,
4355
- limit: options.limit
4442
+ limit: options.limit,
4443
+ after: options.after,
4444
+ before: options.before
4356
4445
  });
4357
4446
  const entries = result.data;
4358
4447
  const ordered = [...entries].reverse();
4359
4448
  if (options.format === "json") {
4360
4449
  ordered.forEach((entry) => process.stdout.write(`${JSON.stringify(entry)}
4361
4450
  `));
4451
+ const cursorPagination = result.meta?.cursor_pagination;
4452
+ if (cursorPagination) {
4453
+ process.stdout.write(
4454
+ `${JSON.stringify({ meta: { cursor_pagination: cursorPagination } })}
4455
+ `
4456
+ );
4457
+ }
4362
4458
  return;
4363
4459
  }
4364
4460
  if (options.format === "pretty") {
@@ -4374,11 +4470,7 @@ Examples:
4374
4470
  consola.info(
4375
4471
  `${entries.length} ${entries.length === 1 ? "entry" : "entries"} shown (oldest first, times in UTC).`
4376
4472
  );
4377
- if (entries.length === options.limit) {
4378
- consola.info(
4379
- `Limit of ${options.limit} reached; older entries may exist. Narrow the time window or raise --limit (max 500) to see more.`
4380
- );
4381
- }
4473
+ printPaginationHints(result.meta?.cursor_pagination, start2, end, options);
4382
4474
  } catch (error) {
4383
4475
  consola.error(error instanceof Error ? error.message : error);
4384
4476
  process.exit(1);
@@ -4673,7 +4765,7 @@ import { Argument, Command as Command12, Option as Option9 } from "commander";
4673
4765
  import { colorize as colorize12 } from "consola/utils";
4674
4766
  var telemetryService = getTelemetry();
4675
4767
  var isEnabled = telemetryService.isEnabled();
4676
- var telemetry2 = new Command12("telemetry").configureHelp({ showGlobalOptions: true }).description(
4768
+ var telemetry = new Command12("telemetry").configureHelp({ showGlobalOptions: true }).description(
4677
4769
  "View or change CLI telemetry collection status. Enabling telemetry helps BigCommerce support diagnose and troubleshoot errors you encounter when using the CLI."
4678
4770
  ).addHelpText(
4679
4771
  "after",
@@ -5402,17 +5494,17 @@ function getCommandPath(cmd) {
5402
5494
  return parts.join(" ");
5403
5495
  }
5404
5496
  var telemetryPreHook = async (thisCommand, actionCommand) => {
5405
- const telemetry3 = getTelemetry();
5497
+ const telemetry2 = getTelemetry();
5406
5498
  const commandName = getCommandPath(actionCommand);
5407
- telemetry3.commandName = commandName;
5408
- return await telemetry3.track(commandName, {
5499
+ telemetry2.commandName = commandName;
5500
+ return await telemetry2.track(commandName, {
5409
5501
  ...parseArguments(thisCommand.args)
5410
5502
  });
5411
5503
  };
5412
5504
  var telemetryPostHook = async () => {
5413
- const telemetry3 = getTelemetry();
5414
- await telemetry3.track("command_completed", { durationMs: telemetry3.durationMs() });
5415
- await telemetry3.analytics.closeAndFlush();
5505
+ const telemetry2 = getTelemetry();
5506
+ await telemetry2.track("command_completed", { durationMs: telemetry2.durationMs() });
5507
+ await telemetry2.analytics.closeAndFlush();
5416
5508
  };
5417
5509
 
5418
5510
  // src/cli/program.ts
@@ -5424,21 +5516,21 @@ consola.log(colorize13("cyanBright", `\u25E2 ${package_default.name} v${package_
5424
5516
  `));
5425
5517
  program.name(package_default.name).version(package_default.version).summary("CLI tool for Catalyst development").description(
5426
5518
  "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."
5427
- ).configureHelp({ showGlobalOptions: true }).addCommand(version).addCommand(create2).addCommand(start).addCommand(build).addCommand(deploy).addCommand(domains).addCommand(logs).addCommand(project).addCommand(env).addCommand(channel).addCommand(auth).addCommand(upgrade).addCommand(telemetry2).addCommand(debug).hook("preAction", telemetryPreHook).hook("postAction", telemetryPostHook);
5519
+ ).configureHelp({ showGlobalOptions: true }).addCommand(version).addCommand(create2).addCommand(start).addCommand(build).addCommand(deploy).addCommand(domains).addCommand(logs).addCommand(project).addCommand(env).addCommand(channel).addCommand(auth).addCommand(upgrade).addCommand(telemetry).addCommand(debug).hook("preAction", telemetryPreHook).hook("postAction", telemetryPostHook);
5428
5520
 
5429
5521
  // src/cli/index.ts
5430
5522
  var handleFatalError = async (error) => {
5431
- const telemetry3 = getTelemetry();
5523
+ const telemetry2 = getTelemetry();
5432
5524
  const errorMessage = error instanceof Error ? error.message : String(error);
5433
5525
  const errorStack = error instanceof Error ? error.stack : void 0;
5434
5526
  try {
5435
- await telemetry3.track("error", {
5436
- commandName: telemetry3.commandName,
5527
+ await telemetry2.track("error", {
5528
+ commandName: telemetry2.commandName,
5437
5529
  errorMessage,
5438
5530
  errorStack,
5439
- durationMs: telemetry3.durationMs()
5531
+ durationMs: telemetry2.durationMs()
5440
5532
  });
5441
- await telemetry3.analytics.closeAndFlush();
5533
+ await telemetry2.analytics.closeAndFlush();
5442
5534
  } catch {
5443
5535
  }
5444
5536
  if (error instanceof UserActionableError) {
@@ -5446,9 +5538,9 @@ var handleFatalError = async (error) => {
5446
5538
  process.exit(1);
5447
5539
  }
5448
5540
  consola.error(errorMessage);
5449
- if (telemetry3.isEnabled()) {
5541
+ if (telemetry2.isEnabled()) {
5450
5542
  consola.info(
5451
- `Correlation ID: ${telemetry3.correlationId}
5543
+ `Correlation ID: ${telemetry2.correlationId}
5452
5544
  Share this Correlation ID with BigCommerce support.`
5453
5545
  );
5454
5546
  } else {