@drip-apex/apex-cli 0.20.0 → 0.21.1

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.
Files changed (3) hide show
  1. package/README.md +82 -0
  2. package/dist/cli.js +1210 -377
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -3074,14 +3074,14 @@ Expecting one of '${allowedValues.join("', '")}'`);
3074
3074
  */
3075
3075
  helpInformation(contextOptions) {
3076
3076
  const helper = this.createHelp();
3077
- const context = this._getOutputContext(contextOptions);
3077
+ const context2 = this._getOutputContext(contextOptions);
3078
3078
  helper.prepareContext({
3079
- error: context.error,
3080
- helpWidth: context.helpWidth,
3081
- outputHasColors: context.hasColors
3079
+ error: context2.error,
3080
+ helpWidth: context2.helpWidth,
3081
+ outputHasColors: context2.hasColors
3082
3082
  });
3083
3083
  const text2 = helper.formatHelp(this, helper);
3084
- if (context.hasColors) return text2;
3084
+ if (context2.hasColors) return text2;
3085
3085
  return this._outputConfiguration.stripColor(text2);
3086
3086
  }
3087
3087
  /**
@@ -3243,15 +3243,15 @@ Expecting one of '${allowedValues.join("', '")}'`);
3243
3243
  Expecting one of '${allowedValues.join("', '")}'`);
3244
3244
  }
3245
3245
  const helpEvent = `${position}Help`;
3246
- this.on(helpEvent, (context) => {
3246
+ this.on(helpEvent, (context2) => {
3247
3247
  let helpStr;
3248
3248
  if (typeof text2 === "function") {
3249
- helpStr = text2({ error: context.error, command: context.command });
3249
+ helpStr = text2({ error: context2.error, command: context2.command });
3250
3250
  } else {
3251
3251
  helpStr = text2;
3252
3252
  }
3253
3253
  if (helpStr) {
3254
- context.write(`${helpStr}
3254
+ context2.write(`${helpStr}
3255
3255
  `);
3256
3256
  }
3257
3257
  });
@@ -7210,7 +7210,7 @@ function readStoredCredentials(file = import_node_path.default.join(import_node_
7210
7210
  }
7211
7211
  }
7212
7212
  function resolveCredentials(env2 = process.env, stored = readStoredCredentials()) {
7213
- const context = {
7213
+ const context2 = {
7214
7214
  orgId: env2.DRIP_ORG || stored?.context?.orgId,
7215
7215
  shopId: env2.DRIP_SHOP || stored?.context?.shopId
7216
7216
  };
@@ -7223,7 +7223,7 @@ function resolveCredentials(env2 = process.env, stored = readStoredCredentials()
7223
7223
  apiKey: environmentApiKey,
7224
7224
  baseUrl: environmentBaseUrl ?? "https://app.drip-apex.com",
7225
7225
  credentialKind: user ? "user" : "shop",
7226
- resolvedContext: context
7226
+ resolvedContext: context2
7227
7227
  };
7228
7228
  }
7229
7229
  if (stored?.userApiKey) return {
@@ -7231,9 +7231,9 @@ function resolveCredentials(env2 = process.env, stored = readStoredCredentials()
7231
7231
  apiKey: stored.userApiKey,
7232
7232
  baseUrl: stored.userBaseUrl ?? stored.baseUrl,
7233
7233
  credentialKind: "user",
7234
- resolvedContext: context
7234
+ resolvedContext: context2
7235
7235
  };
7236
- if (stored?.apiKey) return { ...stored, credentialKind: "shop", resolvedContext: context };
7236
+ if (stored?.apiKey) return { ...stored, credentialKind: "shop", resolvedContext: context2 };
7237
7237
  return null;
7238
7238
  }
7239
7239
 
@@ -7423,7 +7423,7 @@ async function checkForUpdate(current) {
7423
7423
  // package.json
7424
7424
  var package_default = {
7425
7425
  name: "@drip-apex/apex-cli",
7426
- version: "0.20.0",
7426
+ version: "0.21.1",
7427
7427
  license: "MIT",
7428
7428
  description: "Apex by DRIP CLI: build, preview and run A/B tests on your online shop from the terminal or an AI coding agent (Claude Code, Codex, Cursor).",
7429
7429
  keywords: [
@@ -7546,8 +7546,8 @@ var envelopeContext = {
7546
7546
  shop_id: null,
7547
7547
  credential: initialEnvironmentApiKey?.startsWith("apx_uk_") ? "user" : "shop"
7548
7548
  };
7549
- function setEnvelopeContext(context) {
7550
- envelopeContext = { ...envelopeContext, ...context };
7549
+ function setEnvelopeContext(context2) {
7550
+ envelopeContext = { ...envelopeContext, ...context2 };
7551
7551
  }
7552
7552
  var jsonMode = false;
7553
7553
  var commandName = "apex";
@@ -7724,8 +7724,11 @@ function drainAndExit(code) {
7724
7724
  exitFallback = setTimeout(() => process.exit(code), 500);
7725
7725
  exitFallback.unref();
7726
7726
  }
7727
+ function hasUsableSpinnerWidth(columns) {
7728
+ return typeof columns === "number" && Number.isFinite(columns) && columns > 0;
7729
+ }
7727
7730
  async function createSpinner(message) {
7728
- if (jsonMode || !process.stderr.isTTY) {
7731
+ if (jsonMode || !process.stderr.isTTY || !hasUsableSpinnerWidth(process.stderr.columns)) {
7729
7732
  return {
7730
7733
  start() {
7731
7734
  return this;
@@ -7955,14 +7958,14 @@ function argvOption(name) {
7955
7958
  return void 0;
7956
7959
  }
7957
7960
  var didWarnStoredContext = false;
7958
- function warnStoredContextFallback(creds, flags, context) {
7961
+ function warnStoredContextFallback(creds, flags, context2) {
7959
7962
  if (didWarnStoredContext) return;
7960
7963
  const storedOrg = !flags.orgId && !process.env.DRIP_ORG && Boolean(creds?.context?.orgId);
7961
7964
  const storedShop = !flags.shopId && !process.env.DRIP_SHOP && Boolean(creds?.context?.shopId);
7962
7965
  if (!storedOrg && !storedShop) return;
7963
7966
  const selections = [
7964
- storedOrg ? `org ${context.orgId}` : null,
7965
- storedShop ? `shop ${context.shopId}` : null
7967
+ storedOrg ? `org ${context2.orgId}` : null,
7968
+ storedShop ? `shop ${context2.shopId}` : null
7966
7969
  ].filter(Boolean).join(" / ");
7967
7970
  console.error(`Using ${selections} from 'apex use' context (pass --org/--shop to override).`);
7968
7971
  didWarnStoredContext = true;
@@ -7996,21 +7999,21 @@ function resolveContext(creds = getCredentials(), flags = { orgId: argvOption("o
7996
7999
  shopId: flags.shopId || process.env.DRIP_SHOP || void 0
7997
8000
  };
7998
8001
  }
7999
- const context = {
8002
+ const context2 = {
8000
8003
  orgId: flags.orgId || process.env.DRIP_ORG || creds?.context?.orgId,
8001
8004
  shopId: flags.shopId || process.env.DRIP_SHOP || creds?.context?.shopId
8002
8005
  };
8003
- warnStoredContextFallback(creds, flags, context);
8004
- return context;
8006
+ warnStoredContextFallback(creds, flags, context2);
8007
+ return context2;
8005
8008
  }
8006
8009
  function configureInvocationContext() {
8007
8010
  const creds = getCredentials();
8008
8011
  const credential = defaultCredentialKind(creds);
8009
- const context = resolveContext(creds, void 0, credential);
8012
+ const context2 = resolveContext(creds, void 0, credential);
8010
8013
  setEnvelopeContext({
8011
8014
  credential,
8012
- org_id: context.orgId ?? null,
8013
- shop_id: context.shopId ?? null
8015
+ org_id: context2.orgId ?? null,
8016
+ shop_id: context2.shopId ?? null
8014
8017
  });
8015
8018
  }
8016
8019
  var DRIP_DIR = import_node_path3.default.join(import_node_os4.default.homedir(), ".drip");
@@ -8059,15 +8062,15 @@ function requireCredentials(fallbackShopId) {
8059
8062
  EXIT_CODES.auth
8060
8063
  );
8061
8064
  }
8062
- const context = resolveContext(creds, void 0, defaultCredentialKind(creds));
8063
- if (!context.shopId) context.shopId = fallbackShopId?.trim() || void 0;
8065
+ const context2 = resolveContext(creds, void 0, defaultCredentialKind(creds));
8066
+ if (!context2.shopId) context2.shopId = fallbackShopId?.trim() || void 0;
8064
8067
  if (defaultCredentialKind(creds) === "user") {
8065
8068
  setEnvelopeContext({
8066
8069
  credential: "user",
8067
- org_id: context.orgId ?? null,
8068
- shop_id: context.shopId ?? null
8070
+ org_id: context2.orgId ?? null,
8071
+ shop_id: context2.shopId ?? null
8069
8072
  });
8070
- if (!context.shopId && !getEnvironmentApiKey()) {
8073
+ if (!context2.shopId && !getEnvironmentApiKey()) {
8071
8074
  throw new CliError(
8072
8075
  'Shop context is required. Run "apex use", or pass --shop <id>.',
8073
8076
  EXIT_CODES.validation,
@@ -8083,22 +8086,22 @@ function requireCredentials(fallbackShopId) {
8083
8086
  userApiKey: environmentApiKey,
8084
8087
  userBaseUrl: creds.baseUrl,
8085
8088
  credentialKind: "user",
8086
- resolvedContext: context
8089
+ resolvedContext: context2
8087
8090
  }
8088
8091
  ) : {
8089
8092
  ...creds,
8090
8093
  apiKey: creds.userApiKey,
8091
8094
  baseUrl: creds.userBaseUrl ?? creds.baseUrl,
8092
8095
  credentialKind: "user",
8093
- resolvedContext: context
8096
+ resolvedContext: context2
8094
8097
  };
8095
8098
  }
8096
8099
  setEnvelopeContext({
8097
8100
  credential: "shop",
8098
- org_id: context.orgId ?? null,
8099
- shop_id: context.shopId ?? null
8101
+ org_id: context2.orgId ?? null,
8102
+ shop_id: context2.shopId ?? null
8100
8103
  });
8101
- return { ...creds, credentialKind: "shop", resolvedContext: context };
8104
+ return { ...creds, credentialKind: "shop", resolvedContext: context2 };
8102
8105
  }
8103
8106
  function resolveCheckShop(explicitShopId, fallbackShopId) {
8104
8107
  const explicit = explicitShopId?.trim() || null;
@@ -8157,11 +8160,11 @@ function requireUserCredentials() {
8157
8160
  );
8158
8161
  }
8159
8162
  const resolvedCredentials = creds;
8160
- const context = resolveContext(creds, void 0, "user");
8163
+ const context2 = resolveContext(creds, void 0, "user");
8161
8164
  setEnvelopeContext({
8162
8165
  credential: "user",
8163
- org_id: context.orgId ?? null,
8164
- shop_id: context.shopId ?? null
8166
+ org_id: context2.orgId ?? null,
8167
+ shop_id: context2.shopId ?? null
8165
8168
  });
8166
8169
  return environmentUserKey ? (
8167
8170
  // The user-plane request path reads userApiKey/userBaseUrl, so the
@@ -8172,14 +8175,14 @@ function requireUserCredentials() {
8172
8175
  userApiKey: environmentUserKey,
8173
8176
  userBaseUrl: resolvedCredentials.baseUrl,
8174
8177
  credentialKind: "user",
8175
- resolvedContext: context
8178
+ resolvedContext: context2
8176
8179
  }
8177
8180
  ) : {
8178
8181
  ...resolvedCredentials,
8179
8182
  apiKey: resolvedCredentials.userApiKey,
8180
8183
  baseUrl: resolvedCredentials.userBaseUrl ?? resolvedCredentials.baseUrl,
8181
8184
  credentialKind: "user",
8182
- resolvedContext: context
8185
+ resolvedContext: context2
8183
8186
  };
8184
8187
  }
8185
8188
  var CONFIG_NAMES = [".drip.config.ts", ".drip.config.js", "drip.config.ts", "drip.config.js"];
@@ -8257,7 +8260,7 @@ function detectSessionId(env2) {
8257
8260
  const hostId = env2.CLAUDE_CODE_SESSION_ID || env2.CODEX_THREAD_ID;
8258
8261
  return hostId ? `h_${(0, import_node_crypto2.createHash)("sha256").update(hostId).digest("hex").slice(0, 32)}` : void 0;
8259
8262
  }
8260
- function beginInvocation(command, context = {}) {
8263
+ function beginInvocation(command, context2 = {}) {
8261
8264
  resolvedPath = telemetryCommandPath(command);
8262
8265
  helpPath = void 0;
8263
8266
  attempted = void 0;
@@ -8280,8 +8283,8 @@ function beginInvocation(command, context = {}) {
8280
8283
  json_mode: shouldUseJson(command),
8281
8284
  tty: process.stdout.isTTY === true,
8282
8285
  credential_kind: process.env.APEX_API_KEY || process.env.DRIP_API_KEY ? "env" : defaultCredentialKind(getCredentials()),
8283
- org_id: context.orgId,
8284
- shop_id: context.shopId
8286
+ org_id: context2.orgId,
8287
+ shop_id: context2.shopId
8285
8288
  };
8286
8289
  }
8287
8290
  var staticRouteSegments = /* @__PURE__ */ new Set([
@@ -8877,6 +8880,14 @@ function deleteExperiment(creds, id) {
8877
8880
  function getExperimentFilesystem(creds, id) {
8878
8881
  return request(creds, "GET", `/api/v1/experiments/${id}/filesystem`);
8879
8882
  }
8883
+ function getExperimentSaleReport(creds, id, baseline) {
8884
+ const query = baseline === void 0 ? "" : `?${new URLSearchParams({ baseline })}`;
8885
+ return request(
8886
+ creds,
8887
+ "GET",
8888
+ `/api/v1/experiments/${id}/commerce/sale-report${query}`
8889
+ );
8890
+ }
8880
8891
  function getExperimentResults(creds, id, opts) {
8881
8892
  const params = new URLSearchParams();
8882
8893
  if (opts?.currency) params.set("currency", opts.currency);
@@ -9467,6 +9478,59 @@ function getFlagStatus(creds, id) {
9467
9478
  function createManagedDelivery(creds, kind5, body6) {
9468
9479
  return request(creds, "POST", managedPath(kind5), body6);
9469
9480
  }
9481
+ function flagAudienceResult(result) {
9482
+ return { ...result, data: result.data?.data };
9483
+ }
9484
+ async function setFlagRules(creds, id, body6) {
9485
+ return flagAudienceResult(
9486
+ await request(
9487
+ creds,
9488
+ "PUT",
9489
+ `${managedPath("flags", id)}/rules`,
9490
+ body6
9491
+ )
9492
+ );
9493
+ }
9494
+ async function reorderFlagRules(creds, id, body6) {
9495
+ return flagAudienceResult(
9496
+ await request(
9497
+ creds,
9498
+ "POST",
9499
+ `${managedPath("flags", id)}/rules/reorder`,
9500
+ body6
9501
+ )
9502
+ );
9503
+ }
9504
+ async function createFlagExperience(creds, id, body6) {
9505
+ return flagAudienceResult(
9506
+ await request(
9507
+ creds,
9508
+ "POST",
9509
+ `${managedPath("flags", id)}/experiences`,
9510
+ body6
9511
+ )
9512
+ );
9513
+ }
9514
+ async function updateFlagExperience(creds, id, experienceId, body6) {
9515
+ return flagAudienceResult(
9516
+ await request(
9517
+ creds,
9518
+ "PATCH",
9519
+ `${managedPath("flags", id)}/experiences/${encodeURIComponent(experienceId)}`,
9520
+ body6
9521
+ )
9522
+ );
9523
+ }
9524
+ async function deleteFlagExperience(creds, id, experienceId, body6) {
9525
+ return flagAudienceResult(
9526
+ await request(
9527
+ creds,
9528
+ "DELETE",
9529
+ `${managedPath("flags", id)}/experiences/${encodeURIComponent(experienceId)}`,
9530
+ body6
9531
+ )
9532
+ );
9533
+ }
9470
9534
  function updateFlagCode(creds, id, body6) {
9471
9535
  return request(creds, "PATCH", `${managedPath("flags", id)}/code`, body6);
9472
9536
  }
@@ -9555,9 +9619,9 @@ function listOrgMembers(creds) {
9555
9619
  "/api/v1/org/members"
9556
9620
  );
9557
9621
  }
9558
- function inviteOrg(creds, email, org_role, default_shop_role) {
9622
+ function inviteOrg(creds, email2, org_role, default_shop_role) {
9559
9623
  return request(userCreds(creds), "POST", "/api/v1/org/invitations", {
9560
- email,
9624
+ email: email2,
9561
9625
  org_role,
9562
9626
  default_shop_role
9563
9627
  });
@@ -9589,9 +9653,9 @@ function listTeamMembers(creds, shop) {
9589
9653
  `/api/v1/team/members?shop_id=${encodeURIComponent(shop)}`
9590
9654
  );
9591
9655
  }
9592
- function inviteTeam(creds, email, shop_id, role, permission_overrides) {
9656
+ function inviteTeam(creds, email2, shop_id, role, permission_overrides) {
9593
9657
  return request(userCreds(creds), "POST", "/api/v1/team/invitations", {
9594
- email,
9658
+ email: email2,
9595
9659
  shop_id,
9596
9660
  role,
9597
9661
  ...permission_overrides ? { permission_overrides } : {}
@@ -9882,6 +9946,21 @@ function putCommerceDefinition(creds, experiment2, definition2, expectedRevision
9882
9946
  expectedRevision
9883
9947
  });
9884
9948
  }
9949
+ function notificationCredentials(creds, shopId) {
9950
+ return { ...creds, resolvedContext: { ...creds.resolvedContext, shopId } };
9951
+ }
9952
+ function getReadyEmail(creds, shopId) {
9953
+ return userRequest(notificationCredentials(creds, shopId), "GET", `/api/v1/notifications/ready/email?shop_id=${encodeURIComponent(shopId)}`);
9954
+ }
9955
+ function setReadyEmail(creds, shopId, body6) {
9956
+ return userRequest(notificationCredentials(creds, shopId), "PUT", `/api/v1/notifications/ready/email?shop_id=${encodeURIComponent(shopId)}`, body6);
9957
+ }
9958
+ function getReadySlack(creds, shopId) {
9959
+ return userRequest(notificationCredentials(creds, shopId), "GET", `/api/v1/notifications/ready/slack?shop_id=${encodeURIComponent(shopId)}`);
9960
+ }
9961
+ function setReadySlack(creds, shopId, body6) {
9962
+ return userRequest(notificationCredentials(creds, shopId), "PUT", `/api/v1/notifications/ready/slack?shop_id=${encodeURIComponent(shopId)}`, body6);
9963
+ }
9885
9964
 
9886
9965
  // src/context.ts
9887
9966
  var import_node_readline = __toESM(require("node:readline"));
@@ -10322,21 +10401,21 @@ var whoamiCommand = new Command("whoami").description("Show your identity, crede
10322
10401
  return;
10323
10402
  }
10324
10403
  if (defaultCredentialKind(stored) === "shop") {
10325
- const context = resolveContext(stored, void 0, "shop");
10404
+ const context2 = resolveContext(stored, void 0, "shop");
10326
10405
  setEnvelopeContext({
10327
10406
  credential: "shop",
10328
- org_id: context.orgId ?? null,
10329
- shop_id: context.shopId ?? null
10407
+ org_id: context2.orgId ?? null,
10408
+ shop_id: context2.shopId ?? null
10330
10409
  });
10331
10410
  const data2 = {
10332
10411
  logged_in: true,
10333
10412
  credential: { kind: "shop" },
10334
- context: { org_id: context.orgId ?? null, shop_id: context.shopId ?? null }
10413
+ context: { org_id: context2.orgId ?? null, shop_id: context2.shopId ?? null }
10335
10414
  };
10336
10415
  output(this, data2, () => {
10337
10416
  console.log(
10338
10417
  `Credential: shop key (pre-bound server-side)
10339
- Declared shop: ${context.shopId ?? "none \u2014 set DRIP_SHOP to assert one"}
10418
+ Declared shop: ${context2.shopId ?? "none \u2014 set DRIP_SHOP to assert one"}
10340
10419
  For account-wide visibility run "apex login" and "apex whoami" without APEX_API_KEY (or DRIP_API_KEY).`
10341
10420
  );
10342
10421
  });
@@ -10438,6 +10517,30 @@ var useCommand = new Command("use").description("Set the current Apex organizati
10438
10517
  );
10439
10518
  });
10440
10519
 
10520
+ // src/lib/schedule-time.ts
10521
+ function formatScheduleTime(iso, zone) {
10522
+ if (!iso) return "none";
10523
+ const instant2 = new Date(iso);
10524
+ if (!Number.isFinite(instant2.getTime())) return "unavailable";
10525
+ if (!zone) return "unavailable (shop timezone unavailable)";
10526
+ try {
10527
+ const parts = Object.fromEntries(
10528
+ new Intl.DateTimeFormat("en-CA", {
10529
+ timeZone: zone,
10530
+ year: "numeric",
10531
+ month: "2-digit",
10532
+ day: "2-digit",
10533
+ hour: "2-digit",
10534
+ minute: "2-digit",
10535
+ hourCycle: "h23"
10536
+ }).formatToParts(instant2).map((p) => [p.type, p.value])
10537
+ );
10538
+ return `${parts.year}-${parts.month}-${parts.day} ${parts.hour}:${parts.minute} ${zone}`;
10539
+ } catch {
10540
+ return "unavailable (shop timezone unavailable)";
10541
+ }
10542
+ }
10543
+
10441
10544
  // ../shared-runtime/src/experiment-exposure.ts
10442
10545
  var EXPERIMENT_EXPOSURE_ERROR = "Experiment exposure must be greater than 0% and at most 100%.";
10443
10546
  function isExperimentExposure(value2) {
@@ -11157,6 +11260,22 @@ var experiment_filesystem_schema_default = {
11157
11260
  definition: {
11158
11261
  additionalProperties: false,
11159
11262
  properties: {
11263
+ audience: {
11264
+ additionalProperties: false,
11265
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
11266
+ properties: {
11267
+ globalHoldout: {
11268
+ enum: ["respect", "ignore"],
11269
+ type: "string"
11270
+ },
11271
+ holdoutPercent: {
11272
+ maximum: 50,
11273
+ minimum: 0,
11274
+ type: "number"
11275
+ }
11276
+ },
11277
+ type: "object"
11278
+ },
11160
11279
  baseCurrency: {
11161
11280
  maxLength: 3,
11162
11281
  minLength: 3,
@@ -11166,6 +11285,90 @@ var experiment_filesystem_schema_default = {
11166
11285
  levers: {
11167
11286
  additionalProperties: false,
11168
11287
  properties: {
11288
+ discountCodes: {
11289
+ additionalProperties: false,
11290
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
11291
+ properties: {
11292
+ scope: {
11293
+ enum: ["assigned", "all_carts"],
11294
+ type: "string"
11295
+ },
11296
+ versions: {
11297
+ additionalProperties: {
11298
+ anyOf: [
11299
+ {
11300
+ additionalProperties: false,
11301
+ properties: {
11302
+ unchanged: {
11303
+ const: true,
11304
+ type: "boolean"
11305
+ }
11306
+ },
11307
+ required: ["unchanged"],
11308
+ type: "object"
11309
+ },
11310
+ {
11311
+ additionalProperties: false,
11312
+ properties: {
11313
+ except: {
11314
+ additionalProperties: false,
11315
+ properties: {
11316
+ codes: {
11317
+ items: {
11318
+ maxLength: 255,
11319
+ minLength: 1,
11320
+ type: "string"
11321
+ },
11322
+ maxItems: 100,
11323
+ type: "array"
11324
+ },
11325
+ prefixes: {
11326
+ items: {
11327
+ type: "string"
11328
+ },
11329
+ maxItems: 20,
11330
+ type: "array"
11331
+ }
11332
+ },
11333
+ type: "object"
11334
+ },
11335
+ fallbackLanguage: {
11336
+ type: "string"
11337
+ },
11338
+ messages: {
11339
+ additionalProperties: {
11340
+ maxLength: 120,
11341
+ minLength: 1,
11342
+ type: "string"
11343
+ },
11344
+ maxProperties: 20,
11345
+ propertyNames: {
11346
+ type: "string"
11347
+ },
11348
+ type: "object"
11349
+ },
11350
+ policy: {
11351
+ enum: ["allow_all", "block_all", "block_except"],
11352
+ type: "string"
11353
+ }
11354
+ },
11355
+ required: ["policy"],
11356
+ type: "object"
11357
+ }
11358
+ ]
11359
+ },
11360
+ propertyNames: {
11361
+ maxLength: 80,
11362
+ minLength: 1,
11363
+ pattern: "^[a-z0-9-]+$",
11364
+ type: "string"
11365
+ },
11366
+ type: "object"
11367
+ }
11368
+ },
11369
+ required: ["versions"],
11370
+ type: "object"
11371
+ },
11169
11372
  offer: {
11170
11373
  oneOf: [
11171
11374
  {
@@ -11176,6 +11379,11 @@ var experiment_filesystem_schema_default = {
11176
11379
  pattern: "^gid:\\/\\/shopify\\/ProductVariant\\/\\d+$",
11177
11380
  type: "string"
11178
11381
  },
11382
+ merchantDiscounts: {
11383
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
11384
+ enum: ["stack", "best_for_customer"],
11385
+ type: "string"
11386
+ },
11179
11387
  offerType: {
11180
11388
  const: "discount_depth",
11181
11389
  type: "string"
@@ -11267,6 +11475,11 @@ var experiment_filesystem_schema_default = {
11267
11475
  pattern: "^gid:\\/\\/shopify\\/ProductVariant\\/\\d+$",
11268
11476
  type: "string"
11269
11477
  },
11478
+ merchantDiscounts: {
11479
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
11480
+ enum: ["stack", "best_for_customer"],
11481
+ type: "string"
11482
+ },
11270
11483
  offerType: {
11271
11484
  const: "gwp_threshold",
11272
11485
  type: "string"
@@ -11462,6 +11675,11 @@ var experiment_filesystem_schema_default = {
11462
11675
  pattern: "^gid:\\/\\/shopify\\/ProductVariant\\/\\d+$",
11463
11676
  type: "string"
11464
11677
  },
11678
+ merchantDiscounts: {
11679
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
11680
+ enum: ["stack", "best_for_customer"],
11681
+ type: "string"
11682
+ },
11465
11683
  offerType: {
11466
11684
  const: "quantity_breaks",
11467
11685
  type: "string"
@@ -11652,23 +11870,250 @@ var experiment_filesystem_schema_default = {
11652
11870
  price: {
11653
11871
  additionalProperties: false,
11654
11872
  properties: {
11873
+ amountOffDisplay: {
11874
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
11875
+ enum: ["badge", "strikethrough", "none"],
11876
+ type: "string"
11877
+ },
11878
+ chargeVia: {
11879
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
11880
+ enum: ["auto", "cart_transform", "discount", "expand"],
11881
+ type: "string"
11882
+ },
11883
+ display: {
11884
+ additionalProperties: false,
11885
+ properties: {
11886
+ badge: {
11887
+ anyOf: [
11888
+ {
11889
+ enum: ["hide", "theme"],
11890
+ type: "string"
11891
+ },
11892
+ {
11893
+ additionalProperties: false,
11894
+ properties: {
11895
+ backgroundColor: {
11896
+ pattern: "^#[0-9a-fA-F]{6}$",
11897
+ type: "string"
11898
+ },
11899
+ position: {
11900
+ enum: [
11901
+ "theme_slot",
11902
+ "price_before",
11903
+ "price_after",
11904
+ "image_top_left",
11905
+ "image_top_right"
11906
+ ],
11907
+ type: "string"
11908
+ },
11909
+ surfaces: {
11910
+ items: {
11911
+ enum: ["product", "card", "quick_view", "search"],
11912
+ type: "string"
11913
+ },
11914
+ type: "array"
11915
+ },
11916
+ text: {
11917
+ maxLength: 40,
11918
+ minLength: 1,
11919
+ type: "string"
11920
+ },
11921
+ textColor: {
11922
+ pattern: "^#[0-9a-fA-F]{6}$",
11923
+ type: "string"
11924
+ },
11925
+ translations: {
11926
+ additionalProperties: {
11927
+ type: "string"
11928
+ },
11929
+ propertyNames: {
11930
+ type: "string"
11931
+ },
11932
+ type: "object"
11933
+ }
11934
+ },
11935
+ required: ["text"],
11936
+ type: "object"
11937
+ }
11938
+ ],
11939
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it."
11940
+ },
11941
+ priceHoldMs: {
11942
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
11943
+ maximum: 8e3,
11944
+ minimum: 0,
11945
+ type: "integer"
11946
+ },
11947
+ soldOut: {
11948
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
11949
+ enum: ["test_price", "catalog_price"],
11950
+ type: "string"
11951
+ },
11952
+ unitPrice: {
11953
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
11954
+ enum: ["update", "hide", "keep"],
11955
+ type: "string"
11956
+ }
11957
+ },
11958
+ type: "object"
11959
+ },
11960
+ feed: {
11961
+ additionalProperties: false,
11962
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
11963
+ properties: {
11964
+ compareAt: {
11965
+ enum: ["before", "existing", "none"],
11966
+ type: "string"
11967
+ },
11968
+ fixedPriceMarkets: {
11969
+ enum: ["skip", "write"],
11970
+ type: "string"
11971
+ },
11972
+ routeClicks: {
11973
+ additionalProperties: false,
11974
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
11975
+ properties: {
11976
+ existingVisitors: {
11977
+ enum: ["move", "keep"],
11978
+ type: "string"
11979
+ },
11980
+ match: {
11981
+ additionalProperties: false,
11982
+ properties: {
11983
+ email: {
11984
+ items: {
11985
+ additionalProperties: false,
11986
+ properties: {
11987
+ equals: {
11988
+ type: "string"
11989
+ },
11990
+ param: {
11991
+ type: "string"
11992
+ }
11993
+ },
11994
+ required: ["param"],
11995
+ type: "object"
11996
+ },
11997
+ type: "array"
11998
+ },
11999
+ google_shopping: {
12000
+ items: {
12001
+ additionalProperties: false,
12002
+ properties: {
12003
+ equals: {
12004
+ type: "string"
12005
+ },
12006
+ param: {
12007
+ type: "string"
12008
+ }
12009
+ },
12010
+ required: ["param"],
12011
+ type: "object"
12012
+ },
12013
+ type: "array"
12014
+ }
12015
+ },
12016
+ type: "object"
12017
+ },
12018
+ sources: {
12019
+ items: {
12020
+ enum: ["google_shopping", "email"],
12021
+ type: "string"
12022
+ },
12023
+ type: "array"
12024
+ }
12025
+ },
12026
+ type: "object"
12027
+ },
12028
+ version: {
12029
+ maxLength: 80,
12030
+ minLength: 1,
12031
+ pattern: "^[a-z0-9-]+$",
12032
+ type: "string"
12033
+ }
12034
+ },
12035
+ type: "object"
12036
+ },
11655
12037
  fixedPriceMarkets: {
11656
12038
  description: "Reserved price feature. Refused at compile with commerce_definition_feature_unavailable."
11657
12039
  },
11658
12040
  foreignCurrencies: {
11659
12041
  description: "Reserved price feature. Refused at compile with commerce_definition_feature_unavailable."
11660
12042
  },
12043
+ merchantDiscounts: {
12044
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
12045
+ enum: ["stack", "best_for_customer"],
12046
+ type: "string"
12047
+ },
12048
+ plane: {
12049
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
12050
+ enum: ["auto", "variants", "rules"],
12051
+ type: "string"
12052
+ },
11661
12053
  products: {
11662
12054
  additionalProperties: false,
11663
12055
  properties: {
11664
12056
  select: {
11665
12057
  additionalProperties: false,
12058
+ if: {
12059
+ properties: {
12060
+ all: {
12061
+ const: true
12062
+ }
12063
+ },
12064
+ required: ["all"]
12065
+ },
11666
12066
  properties: {
11667
12067
  all: {
11668
12068
  const: true,
11669
- description: "Reserved whole-shop selection. Refused at compile with commerce_definition_feature_unavailable (whole-shop pricing is not supported).",
12069
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
11670
12070
  type: "boolean"
11671
12071
  },
12072
+ bundles: {
12073
+ additionalProperties: false,
12074
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
12075
+ properties: {
12076
+ exclude: {
12077
+ enum: ["parents_and_components", "components_only"],
12078
+ type: "string"
12079
+ },
12080
+ markers: {
12081
+ additionalProperties: false,
12082
+ properties: {
12083
+ metafields: {
12084
+ items: {
12085
+ additionalProperties: false,
12086
+ properties: {
12087
+ key: {
12088
+ type: "string"
12089
+ },
12090
+ namespace: {
12091
+ type: "string"
12092
+ }
12093
+ },
12094
+ required: ["namespace", "key"],
12095
+ type: "object"
12096
+ },
12097
+ type: "array"
12098
+ },
12099
+ productTypes: {
12100
+ items: {
12101
+ type: "string"
12102
+ },
12103
+ type: "array"
12104
+ },
12105
+ tags: {
12106
+ items: {
12107
+ type: "string"
12108
+ },
12109
+ type: "array"
12110
+ }
12111
+ },
12112
+ type: "object"
12113
+ }
12114
+ },
12115
+ type: "object"
12116
+ },
11672
12117
  collections: {
11673
12118
  default: [],
11674
12119
  items: {
@@ -11679,6 +12124,16 @@ var experiment_filesystem_schema_default = {
11679
12124
  maxItems: 100,
11680
12125
  type: "array"
11681
12126
  },
12127
+ excludeCollections: {
12128
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
12129
+ items: {
12130
+ maxLength: 200,
12131
+ minLength: 1,
12132
+ type: "string"
12133
+ },
12134
+ maxItems: 100,
12135
+ type: "array"
12136
+ },
11682
12137
  excludeProducts: {
11683
12138
  default: [],
11684
12139
  items: {
@@ -11689,6 +12144,24 @@ var experiment_filesystem_schema_default = {
11689
12144
  maxItems: 100,
11690
12145
  type: "array"
11691
12146
  },
12147
+ excludeSkus: {
12148
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
12149
+ items: {
12150
+ maxLength: 255,
12151
+ minLength: 1,
12152
+ type: "string"
12153
+ },
12154
+ maxItems: 5e3,
12155
+ type: "array"
12156
+ },
12157
+ excludeTags: {
12158
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
12159
+ items: {
12160
+ type: "string"
12161
+ },
12162
+ maxItems: 100,
12163
+ type: "array"
12164
+ },
11692
12165
  includeProducts: {
11693
12166
  default: [],
11694
12167
  items: {
@@ -11699,6 +12172,14 @@ var experiment_filesystem_schema_default = {
11699
12172
  maxItems: 100,
11700
12173
  type: "array"
11701
12174
  },
12175
+ includeSkus: {
12176
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
12177
+ items: {
12178
+ type: "string"
12179
+ },
12180
+ maxItems: 5e3,
12181
+ type: "array"
12182
+ },
11702
12183
  productTypes: {
11703
12184
  default: [],
11704
12185
  items: {
@@ -11727,6 +12208,25 @@ var experiment_filesystem_schema_default = {
11727
12208
  type: "array"
11728
12209
  }
11729
12210
  },
12211
+ then: {
12212
+ properties: {
12213
+ collections: {
12214
+ maxItems: 0
12215
+ },
12216
+ includeProducts: {
12217
+ maxItems: 0
12218
+ },
12219
+ productTypes: {
12220
+ maxItems: 0
12221
+ },
12222
+ tags: {
12223
+ maxItems: 0
12224
+ },
12225
+ vendors: {
12226
+ maxItems: 0
12227
+ }
12228
+ }
12229
+ },
11730
12230
  type: "object"
11731
12231
  }
11732
12232
  },
@@ -11759,6 +12259,91 @@ var experiment_filesystem_schema_default = {
11759
12259
  enum: ["include", "exclude"],
11760
12260
  type: "string"
11761
12261
  },
12262
+ badge: {
12263
+ anyOf: [
12264
+ {
12265
+ enum: ["hide", "theme"],
12266
+ type: "string"
12267
+ },
12268
+ {
12269
+ additionalProperties: false,
12270
+ properties: {
12271
+ backgroundColor: {
12272
+ pattern: "^#[0-9a-fA-F]{6}$",
12273
+ type: "string"
12274
+ },
12275
+ position: {
12276
+ enum: [
12277
+ "theme_slot",
12278
+ "price_before",
12279
+ "price_after",
12280
+ "image_top_left",
12281
+ "image_top_right"
12282
+ ],
12283
+ type: "string"
12284
+ },
12285
+ surfaces: {
12286
+ items: {
12287
+ enum: [
12288
+ "product",
12289
+ "card",
12290
+ "quick_view",
12291
+ "search"
12292
+ ],
12293
+ type: "string"
12294
+ },
12295
+ type: "array"
12296
+ },
12297
+ text: {
12298
+ maxLength: 40,
12299
+ minLength: 1,
12300
+ type: "string"
12301
+ },
12302
+ textColor: {
12303
+ pattern: "^#[0-9a-fA-F]{6}$",
12304
+ type: "string"
12305
+ },
12306
+ translations: {
12307
+ additionalProperties: {
12308
+ type: "string"
12309
+ },
12310
+ propertyNames: {
12311
+ type: "string"
12312
+ },
12313
+ type: "object"
12314
+ }
12315
+ },
12316
+ required: ["text"],
12317
+ type: "object"
12318
+ }
12319
+ ],
12320
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it."
12321
+ },
12322
+ conditions: {
12323
+ additionalProperties: false,
12324
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
12325
+ properties: {
12326
+ minimumQuantity: {
12327
+ maximum: 9007199254740991,
12328
+ minimum: 2,
12329
+ type: "integer"
12330
+ },
12331
+ minimumSubtotal: {
12332
+ maxLength: 32,
12333
+ pattern: "^\\d+(?:\\.\\d{1,6})?$",
12334
+ type: "string"
12335
+ },
12336
+ quantityBasis: {
12337
+ enum: ["line", "product", "in_scope"],
12338
+ type: "string"
12339
+ },
12340
+ subtotalBasis: {
12341
+ enum: ["cart", "in_scope"],
12342
+ type: "string"
12343
+ }
12344
+ },
12345
+ type: "object"
12346
+ },
11762
12347
  discountCodes: {
11763
12348
  additionalProperties: false,
11764
12349
  properties: {
@@ -11854,7 +12439,8 @@ var experiment_filesystem_schema_default = {
11854
12439
  type: "string"
11855
12440
  },
11856
12441
  value: {
11857
- enum: ["0.95", "0.99", "0.00"],
12442
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
12443
+ pattern: "^0\\.\\d{2}$",
11858
12444
  type: "string"
11859
12445
  }
11860
12446
  },
@@ -12011,6 +12597,31 @@ var experiment_filesystem_schema_default = {
12011
12597
  {
12012
12598
  additionalProperties: false,
12013
12599
  properties: {
12600
+ conditions: {
12601
+ additionalProperties: false,
12602
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
12603
+ properties: {
12604
+ minimumQuantity: {
12605
+ maximum: 9007199254740991,
12606
+ minimum: 2,
12607
+ type: "integer"
12608
+ },
12609
+ minimumSubtotal: {
12610
+ maxLength: 32,
12611
+ pattern: "^\\d+(?:\\.\\d{1,6})?$",
12612
+ type: "string"
12613
+ },
12614
+ quantityBasis: {
12615
+ enum: ["line", "product", "in_scope"],
12616
+ type: "string"
12617
+ },
12618
+ subtotalBasis: {
12619
+ enum: ["cart", "in_scope"],
12620
+ type: "string"
12621
+ }
12622
+ },
12623
+ type: "object"
12624
+ },
12014
12625
  method: {
12015
12626
  const: "amount_off",
12016
12627
  type: "string"
@@ -12220,6 +12831,36 @@ var experiment_filesystem_schema_default = {
12220
12831
  },
12221
12832
  type: "object"
12222
12833
  },
12834
+ mode: {
12835
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
12836
+ enum: ["test", "personalization", "sale"],
12837
+ type: "string"
12838
+ },
12839
+ planning: {
12840
+ additionalProperties: false,
12841
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
12842
+ properties: {
12843
+ baselineDays: {
12844
+ exclusiveMinimum: 0,
12845
+ maximum: 9007199254740991,
12846
+ type: "integer"
12847
+ },
12848
+ maxDays: {
12849
+ exclusiveMinimum: 0,
12850
+ maximum: 9007199254740991,
12851
+ type: "integer"
12852
+ },
12853
+ metric: {
12854
+ enum: ["profit", "revenue"],
12855
+ type: "string"
12856
+ },
12857
+ minimumDetectableEffect: {
12858
+ exclusiveMinimum: 0,
12859
+ type: "number"
12860
+ }
12861
+ },
12862
+ type: "object"
12863
+ },
12223
12864
  priceFloor: {
12224
12865
  additionalProperties: false,
12225
12866
  properties: {
@@ -12246,6 +12887,42 @@ var experiment_filesystem_schema_default = {
12246
12887
  },
12247
12888
  type: "object"
12248
12889
  },
12890
+ reporting: {
12891
+ additionalProperties: false,
12892
+ properties: {
12893
+ sale: {
12894
+ additionalProperties: false,
12895
+ properties: {
12896
+ baseline: {
12897
+ enum: ["auto", "holdout", "last_year", "prior_period", "manual"],
12898
+ type: "string"
12899
+ },
12900
+ lastYearAlign: {
12901
+ enum: ["weekday", "date"],
12902
+ type: "string"
12903
+ }
12904
+ },
12905
+ type: "object"
12906
+ }
12907
+ },
12908
+ type: "object"
12909
+ },
12910
+ results: {
12911
+ additionalProperties: false,
12912
+ properties: {
12913
+ breakdownDimension: {
12914
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
12915
+ enum: ["channel", "device", "country", "visitor_kind"],
12916
+ type: "string"
12917
+ },
12918
+ metric: {
12919
+ description: "Reserved option. Refused with commerce_definition_feature_unavailable until its owner activates it.",
12920
+ enum: ["auto", "profit", "revenue"],
12921
+ type: "string"
12922
+ }
12923
+ },
12924
+ type: "object"
12925
+ },
12249
12926
  schemaVersion: {
12250
12927
  const: 2,
12251
12928
  type: "number"
@@ -14135,14 +14812,7 @@ var experiment_filesystem_schema_default = {
14135
14812
  ]
14136
14813
  },
14137
14814
  description: {
14138
- anyOf: [
14139
- {
14140
- type: "string"
14141
- },
14142
- {
14143
- type: "null"
14144
- }
14145
- ]
14815
+ type: ["string", "null"]
14146
14816
  },
14147
14817
  environmentKey: {
14148
14818
  type: "string"
@@ -14152,14 +14822,7 @@ var experiment_filesystem_schema_default = {
14152
14822
  type: "string"
14153
14823
  },
14154
14824
  id: {
14155
- anyOf: [
14156
- {
14157
- type: "string"
14158
- },
14159
- {
14160
- type: "null"
14161
- }
14162
- ]
14825
+ type: ["string", "null"]
14163
14826
  },
14164
14827
  name: {
14165
14828
  type: "string"
@@ -14175,37 +14838,16 @@ var experiment_filesystem_schema_default = {
14175
14838
  type: "number"
14176
14839
  },
14177
14840
  slug: {
14178
- anyOf: [
14179
- {
14180
- type: "string"
14181
- },
14182
- {
14183
- type: "null"
14184
- }
14185
- ]
14841
+ type: ["string", "null"]
14186
14842
  },
14187
14843
  statisticalPower: {
14188
14844
  type: "number"
14189
14845
  },
14190
14846
  storeUrl: {
14191
- anyOf: [
14192
- {
14193
- type: "string"
14194
- },
14195
- {
14196
- type: "null"
14197
- }
14198
- ]
14847
+ type: ["string", "null"]
14199
14848
  },
14200
14849
  testKey: {
14201
- anyOf: [
14202
- {
14203
- type: "string"
14204
- },
14205
- {
14206
- type: "null"
14207
- }
14208
- ]
14850
+ type: ["string", "null"]
14209
14851
  },
14210
14852
  trafficAllocation: {
14211
14853
  exclusiveMinimum: 0,
@@ -14236,14 +14878,7 @@ var experiment_filesystem_schema_default = {
14236
14878
  additionalProperties: false,
14237
14879
  properties: {
14238
14880
  endedAt: {
14239
- anyOf: [
14240
- {
14241
- type: "string"
14242
- },
14243
- {
14244
- type: "null"
14245
- }
14246
- ]
14881
+ type: ["string", "null"]
14247
14882
  },
14248
14883
  launchApproval: {
14249
14884
  anyOf: [
@@ -14251,24 +14886,10 @@ var experiment_filesystem_schema_default = {
14251
14886
  additionalProperties: false,
14252
14887
  properties: {
14253
14888
  decidedAt: {
14254
- anyOf: [
14255
- {
14256
- type: "string"
14257
- },
14258
- {
14259
- type: "null"
14260
- }
14261
- ]
14889
+ type: ["string", "null"]
14262
14890
  },
14263
14891
  note: {
14264
- anyOf: [
14265
- {
14266
- type: "string"
14267
- },
14268
- {
14269
- type: "null"
14270
- }
14271
- ]
14892
+ type: ["string", "null"]
14272
14893
  },
14273
14894
  status: {
14274
14895
  type: "string"
@@ -14300,38 +14921,17 @@ var experiment_filesystem_schema_default = {
14300
14921
  type: "boolean"
14301
14922
  },
14302
14923
  scheduledEndAt: {
14303
- anyOf: [
14304
- {
14305
- type: "string"
14306
- },
14307
- {
14308
- type: "null"
14309
- }
14310
- ]
14924
+ type: ["string", "null"]
14311
14925
  },
14312
14926
  scheduledStartAt: {
14313
- anyOf: [
14314
- {
14315
- type: "string"
14316
- },
14317
- {
14318
- type: "null"
14319
- }
14320
- ]
14927
+ type: ["string", "null"]
14321
14928
  },
14322
14929
  schemaVersion: {
14323
14930
  const: 1,
14324
14931
  type: "number"
14325
14932
  },
14326
14933
  startedAt: {
14327
- anyOf: [
14328
- {
14329
- type: "string"
14330
- },
14331
- {
14332
- type: "null"
14333
- }
14334
- ]
14934
+ type: ["string", "null"]
14335
14935
  },
14336
14936
  status: {
14337
14937
  type: "string"
@@ -14371,14 +14971,7 @@ var experiment_filesystem_schema_default = {
14371
14971
  type: "boolean"
14372
14972
  },
14373
14973
  eventName: {
14374
- anyOf: [
14375
- {
14376
- type: "string"
14377
- },
14378
- {
14379
- type: "null"
14380
- }
14381
- ]
14974
+ type: ["string", "null"]
14382
14975
  },
14383
14976
  filter: {
14384
14977
  anyOf: [
@@ -14389,14 +14982,7 @@ var experiment_filesystem_schema_default = {
14389
14982
  ]
14390
14983
  },
14391
14984
  id: {
14392
- anyOf: [
14393
- {
14394
- type: "string"
14395
- },
14396
- {
14397
- type: "null"
14398
- }
14399
- ]
14985
+ type: ["string", "null"]
14400
14986
  },
14401
14987
  isPrimary: {
14402
14988
  type: "boolean"
@@ -14405,69 +14991,27 @@ var experiment_filesystem_schema_default = {
14405
14991
  name: {
14406
14992
  type: "string"
14407
14993
  },
14408
- pageId: {
14409
- anyOf: [
14410
- {
14411
- type: "string"
14412
- },
14413
- {
14414
- type: "null"
14415
- }
14416
- ]
14417
- },
14418
- selector: {
14419
- anyOf: [
14420
- {
14421
- type: "string"
14422
- },
14423
- {
14424
- type: "null"
14425
- }
14426
- ]
14427
- },
14994
+ pageId: {
14995
+ type: ["string", "null"]
14996
+ },
14997
+ selector: {
14998
+ type: ["string", "null"]
14999
+ },
14428
15000
  textMatch: {
14429
- anyOf: [
14430
- {
14431
- type: "string"
14432
- },
14433
- {
14434
- type: "null"
14435
- }
14436
- ]
15001
+ type: ["string", "null"]
14437
15002
  },
14438
15003
  type: {
14439
15004
  enum: ["click", "pageview", "custom", "revenue"],
14440
15005
  type: "string"
14441
15006
  },
14442
15007
  urlPattern: {
14443
- anyOf: [
14444
- {
14445
- type: "string"
14446
- },
14447
- {
14448
- type: "null"
14449
- }
14450
- ]
15008
+ type: ["string", "null"]
14451
15009
  },
14452
15010
  valueProperty: {
14453
- anyOf: [
14454
- {
14455
- type: "string"
14456
- },
14457
- {
14458
- type: "null"
14459
- }
14460
- ]
15011
+ type: ["string", "null"]
14461
15012
  },
14462
15013
  valueUnit: {
14463
- anyOf: [
14464
- {
14465
- type: "string"
14466
- },
14467
- {
14468
- type: "null"
14469
- }
14470
- ]
15014
+ type: ["string", "null"]
14471
15015
  }
14472
15016
  },
14473
15017
  required: ["name", "type"],
@@ -14476,14 +15020,7 @@ var experiment_filesystem_schema_default = {
14476
15020
  type: "array"
14477
15021
  },
14478
15022
  primaryGoalId: {
14479
- anyOf: [
14480
- {
14481
- type: "string"
14482
- },
14483
- {
14484
- type: "null"
14485
- }
14486
- ]
15023
+ type: ["string", "null"]
14487
15024
  },
14488
15025
  schemaVersion: {
14489
15026
  const: 1,
@@ -14503,24 +15040,10 @@ var experiment_filesystem_schema_default = {
14503
15040
  additionalProperties: false,
14504
15041
  properties: {
14505
15042
  approvedAt: {
14506
- anyOf: [
14507
- {
14508
- type: "string"
14509
- },
14510
- {
14511
- type: "null"
14512
- }
14513
- ]
15043
+ type: ["string", "null"]
14514
15044
  },
14515
15045
  completedAt: {
14516
- anyOf: [
14517
- {
14518
- type: "string"
14519
- },
14520
- {
14521
- type: "null"
14522
- }
14523
- ]
15046
+ type: ["string", "null"]
14524
15047
  },
14525
15048
  id: {
14526
15049
  type: "string"
@@ -14549,24 +15072,10 @@ var experiment_filesystem_schema_default = {
14549
15072
  ]
14550
15073
  },
14551
15074
  qaApprovedAt: {
14552
- anyOf: [
14553
- {
14554
- type: "string"
14555
- },
14556
- {
14557
- type: "null"
14558
- }
14559
- ]
15075
+ type: ["string", "null"]
14560
15076
  },
14561
15077
  qaApprovedBy: {
14562
- anyOf: [
14563
- {
14564
- type: "string"
14565
- },
14566
- {
14567
- type: "null"
14568
- }
14569
- ]
15078
+ type: ["string", "null"]
14570
15079
  },
14571
15080
  readonly: {
14572
15081
  const: true,
@@ -14790,44 +15299,16 @@ var experiment_filesystem_schema_default = {
14790
15299
  additionalProperties: false,
14791
15300
  properties: {
14792
15301
  hash: {
14793
- anyOf: [
14794
- {
14795
- type: "string"
14796
- },
14797
- {
14798
- type: "null"
14799
- }
14800
- ]
15302
+ type: ["string", "null"]
14801
15303
  },
14802
15304
  reviewedAt: {
14803
- anyOf: [
14804
- {
14805
- type: "string"
14806
- },
14807
- {
14808
- type: "null"
14809
- }
14810
- ]
15305
+ type: ["string", "null"]
14811
15306
  },
14812
15307
  reviewedBy: {
14813
- anyOf: [
14814
- {
14815
- type: "string"
14816
- },
14817
- {
14818
- type: "null"
14819
- }
14820
- ]
15308
+ type: ["string", "null"]
14821
15309
  },
14822
15310
  source: {
14823
- anyOf: [
14824
- {
14825
- type: "string"
14826
- },
14827
- {
14828
- type: "null"
14829
- }
14830
- ]
15311
+ type: ["string", "null"]
14831
15312
  },
14832
15313
  status: {
14833
15314
  type: "string"
@@ -14841,50 +15322,22 @@ var experiment_filesystem_schema_default = {
14841
15322
  type: "string"
14842
15323
  },
14843
15324
  cssArtifactId: {
14844
- anyOf: [
14845
- {
14846
- type: "string"
14847
- },
14848
- {
14849
- type: "null"
14850
- }
14851
- ]
15325
+ type: ["string", "null"]
14852
15326
  },
14853
15327
  id: {
14854
- anyOf: [
14855
- {
14856
- type: "string"
14857
- },
14858
- {
14859
- type: "null"
14860
- }
14861
- ]
15328
+ type: ["string", "null"]
14862
15329
  },
14863
15330
  isControl: {
14864
15331
  type: "boolean"
14865
15332
  },
14866
15333
  jsArtifactId: {
14867
- anyOf: [
14868
- {
14869
- type: "string"
14870
- },
14871
- {
14872
- type: "null"
14873
- }
14874
- ]
15334
+ type: ["string", "null"]
14875
15335
  },
14876
15336
  name: {
14877
15337
  type: "string"
14878
15338
  },
14879
15339
  redirectUrl: {
14880
- anyOf: [
14881
- {
14882
- type: "string"
14883
- },
14884
- {
14885
- type: "null"
14886
- }
14887
- ]
15340
+ type: ["string", "null"]
14888
15341
  },
14889
15342
  schemaVersion: {
14890
15343
  const: 1,
@@ -15442,14 +15895,14 @@ function urlRuleClause(count2) {
15442
15895
  function effectivePageTargetingLabel(targeting) {
15443
15896
  const raw = isRecord(targeting) ? targeting : {};
15444
15897
  const pages = pageEntries(raw);
15445
- const rules = authoredUrlRules(raw);
15898
+ const rules2 = authoredUrlRules(raw);
15446
15899
  const pagesLabel = pages.map((page) => {
15447
15900
  const readable = page.name ?? UNKNOWN_PAGE_NAME;
15448
15901
  return page.include ? readable : `Exclude ${readable}`;
15449
15902
  }).join(", ");
15450
- if (pages.length && rules.length) return `${pagesLabel} and ${urlRuleClause(rules.length)}`;
15903
+ if (pages.length && rules2.length) return `${pagesLabel} and ${urlRuleClause(rules2.length)}`;
15451
15904
  if (pages.length) return pagesLabel;
15452
- if (rules.length) return `${urlRuleClause(rules.length)} (no saved Page)`;
15905
+ if (rules2.length) return `${urlRuleClause(rules2.length)} (no saved Page)`;
15453
15906
  return ALL_PAGES_LABEL;
15454
15907
  }
15455
15908
 
@@ -15735,7 +16188,10 @@ function printExperiment(exp, dashboardUrl = null) {
15735
16188
  console.log(` Classification: ${exp.counting_type}`);
15736
16189
  console.log(` Traffic: ${(exp.traffic_allocation * 100).toFixed(0)}%`);
15737
16190
  console.log(` QA approved: ${exp.qa_approved_at ?? "none"}`);
15738
- console.log(` Scheduled: ${exp.scheduled_start_at ?? "none"}`);
16191
+ console.log(` Scheduled: ${formatScheduleTime(exp.scheduled_start_at, exp.schedule_timezone)}`);
16192
+ console.log(
16193
+ ` Scheduled end: ${formatScheduleTime(exp.scheduled_end_at, exp.schedule_timezone)}`
16194
+ );
15739
16195
  console.log(` Started: ${exp.started_at ?? "none"}`);
15740
16196
  if (exp.targeting !== void 0) {
15741
16197
  console.log(` Targeting: ${effectivePageTargetingLabel(exp.targeting)}`);
@@ -16181,8 +16637,8 @@ function printFilesystemPlan(plan2) {
16181
16637
  if ((plan2.blockingIssues?.length ?? 0) > 0) {
16182
16638
  console.log(source_default.red("\nBlocking issues"));
16183
16639
  (plan2.blockingIssues ?? []).forEach((issue) => {
16184
- const context = [issue.path, issue.variation ? `variation: ${issue.variation}` : null].filter(Boolean).join(", ");
16185
- console.log(` - [${issue.code}] ${issue.message}${context ? ` (${context})` : ""}`);
16640
+ const context2 = [issue.path, issue.variation ? `variation: ${issue.variation}` : null].filter(Boolean).join(", ");
16641
+ console.log(` - [${issue.code}] ${issue.message}${context2 ? ` (${context2})` : ""}`);
16186
16642
  });
16187
16643
  }
16188
16644
  if ((plan2.commerceLaunchPreview?.length ?? 0) > 0) {
@@ -16275,7 +16731,12 @@ async function getQaPreviewUrls(creds, experimentId) {
16275
16731
  );
16276
16732
  const storeUrl = res.data?.store_url;
16277
16733
  if (!res.ok || !res.data || !storeUrl) {
16278
- return { previewUrls: [], unavailable: true, dashboardUrl };
16734
+ return {
16735
+ previewUrls: [],
16736
+ unavailable: true,
16737
+ dashboardUrl,
16738
+ scheduleTimeZone: res.data?.schedule_timezone
16739
+ };
16279
16740
  }
16280
16741
  const experiment2 = res.data;
16281
16742
  const previewUrls = await Promise.all(
@@ -16294,7 +16755,12 @@ async function getQaPreviewUrls(creds, experimentId) {
16294
16755
  };
16295
16756
  })
16296
16757
  );
16297
- return { previewUrls, unavailable: false, dashboardUrl };
16758
+ return {
16759
+ previewUrls,
16760
+ unavailable: false,
16761
+ dashboardUrl,
16762
+ scheduleTimeZone: experiment2.schedule_timezone
16763
+ };
16298
16764
  } catch {
16299
16765
  return { previewUrls: [], unavailable: true, dashboardUrl: fallbackDashboardUrl };
16300
16766
  }
@@ -16709,7 +17175,103 @@ ${JSON.stringify(data.order, null, 2)}`);
16709
17175
  console.log(`
16710
17176
  Definitions: ${data.definitions_url}`);
16711
17177
  }
16712
- var results = new Command("results").description("Read experiment results and timeseries by ID or slug").argument("<id-or-slug>", EXPERIMENT_REFERENCE_DESCRIPTION).option("--currency <code>", "Currency code for revenue formatting").option("--segment <dim:value>", "Include one audience segment").option("--exclude-segment <dim:value>", "Exclude one audience segment (include wins)").option("--ad-roas", "Read revenue and allocated Meta ad spend per variant").option("--by-channel", "Read the hourly arrival-channel money snapshot").option("--explain", "Explain raw-event exclusions and residuals").option("--goal <id>", "Goal ID or event key for --explain").option("--order <key>", "Order label trail for --explain").option("--samples", "Show sample identifiers with --explain").option("--wait <seconds>", "Wait for a cold server-side results build", "120").action(async function action7(idOrSlug, opts) {
17178
+ function printSaleReport(body6) {
17179
+ const report = body6.data;
17180
+ const value2 = (metric) => metric.value === null ? `unavailable (${metric.reason ?? "unknown"})` : String(metric.value);
17181
+ const totals = (label, row) => {
17182
+ console.log(`
17183
+ ${label} (${row.currency ?? "currency unavailable"})`);
17184
+ console.log(
17185
+ ` Visitors ${value2(row.visitors)}; orders ${row.orders ?? `unavailable (${row.ordersReason})`}; units ${row.units ?? `unavailable (${row.unitsReason})`}`
17186
+ );
17187
+ console.log(
17188
+ ` Net revenue ${value2(row.netRevenue)}; launch gross ${value2(row.grossAtLaunchBaseline)}; AOV ${value2(row.aov)}`
17189
+ );
17190
+ console.log(
17191
+ ` CM1 ${value2(row.cm1)}; refunds ${value2(row.refunds)}; cost coverage ${value2(row.costCoverage)}`
17192
+ );
17193
+ console.log(
17194
+ ` Discounts: markdown ${value2(row.discountCost.markdown)}; Apex amount-off ${value2(row.discountCost.apexAmountOff)}; other ${value2(row.discountCost.other)}`
17195
+ );
17196
+ console.log(
17197
+ ` Shopify discount total ${value2(row.discountCost.shopifyTotal)}; codes subset ${value2(row.discountCost.codes)}`
17198
+ );
17199
+ if (row.coverage.shopCurrencyAssumedOrders > 0)
17200
+ console.log(` Shop currency assumed for ${row.coverage.shopCurrencyAssumedOrders} orders`);
17201
+ };
17202
+ console.log(
17203
+ `Sale report: ${report.mode}; ${report.currency ?? "currency unavailable"}; computed ${report.computedAt}`
17204
+ );
17205
+ console.log(`Window: ${report.window.start ?? "not started"} to ${report.window.end}`);
17206
+ for (const arm of report.arms)
17207
+ totals(
17208
+ `${arm.name}${arm.isControl ? " (control)" : ""}${arm.archived ? " (archived history)" : ""}`,
17209
+ arm.totals
17210
+ );
17211
+ totals("Campaign", report.campaign);
17212
+ totals("Sale", report.sale);
17213
+ const exclusions = (label, reasons) => {
17214
+ const counts = Object.entries(reasons ?? {}).filter(
17215
+ ([, count2]) => Number.isSafeInteger(count2) && count2 > 0
17216
+ );
17217
+ return counts.length > 0 ? [`${label} exclusions: ${counts.map(([reason2, count2]) => `${reason2} ${count2}`).join("; ")}`] : [];
17218
+ };
17219
+ const exclusionLines = [
17220
+ ...exclusions("Campaign", report.diagnostics?.excludedOrders),
17221
+ ...exclusions("Historical", report.diagnostics?.historicalExcludedOrders)
17222
+ ];
17223
+ if (exclusionLines.length > 0) console.log(`
17224
+ ${exclusionLines.join("\n")}`);
17225
+ console.log(
17226
+ `
17227
+ Baseline: ${report.baseline.chosen ?? `unavailable (${report.baseline.reason})`} (requested ${report.baseline.requested})`
17228
+ );
17229
+ for (const attempt of report.baseline.attempts)
17230
+ console.log(
17231
+ ` ${attempt.kind}: ${attempt.reason ?? "selected"}${attempt.detail ? ` (${attempt.detail})` : ""}`
17232
+ );
17233
+ if (report.baseline.chosen === "manual")
17234
+ console.log(`Manual report uses saved snapshot cutoff: ${report.window.end}`);
17235
+ if (report.baseline.totals) totals("Baseline", report.baseline.totals);
17236
+ console.log(
17237
+ report.comparison.randomisation === "randomised" ? "Randomised holdout comparison" : "Descriptive comparison (not_randomised)"
17238
+ );
17239
+ if (report.comparison.populationMismatch) console.log(report.comparison.populationMismatch);
17240
+ for (const key of ["revenue", "orders", "cm1"]) {
17241
+ const metric = report.comparison[key];
17242
+ console.log(` ${key} difference: ${value2(metric)}`);
17243
+ console.log(
17244
+ metric.interval ? ` ${(metric.confidenceLevel * 100).toFixed(0)}% interval: ${metric.interval.lower} to ${metric.interval.upper}` : ` Interval unavailable (${metric.intervalReason ?? "not_randomised"})`
17245
+ );
17246
+ }
17247
+ }
17248
+ var results = new Command("results").description("Read experiment results and timeseries by ID or slug").argument("<id-or-slug>", EXPERIMENT_REFERENCE_DESCRIPTION).option("--currency <code>", "Currency code for revenue formatting").option("--segment <dim:value>", "Include one audience segment").option("--exclude-segment <dim:value>", "Exclude one audience segment (include wins)").option("--sale", "Read sale and no-split campaign totals and baseline comparisons").addOption(
17249
+ new Option("--baseline <kind>", "Sale baseline (requires --sale)").choices([
17250
+ "auto",
17251
+ "holdout",
17252
+ "last_year",
17253
+ "prior_period",
17254
+ "manual"
17255
+ ])
17256
+ ).option("--ad-roas", "Read revenue and allocated Meta ad spend per variant").option("--by-channel", "Read the hourly arrival-channel money snapshot").option("--explain", "Explain raw-event exclusions and residuals").option("--goal <id>", "Goal ID or event key for --explain").option("--order <key>", "Order label trail for --explain").option("--samples", "Show sample identifiers with --explain").option("--wait <seconds>", "Wait for a cold server-side results build", "120").action(async function action7(idOrSlug, opts) {
17257
+ if (opts.baseline !== void 0 && !opts.sale)
17258
+ fail(this, "--baseline requires --sale", EXIT_CODES.validation);
17259
+ if (opts.sale && ([
17260
+ opts.currency,
17261
+ opts.segment,
17262
+ opts.excludeSegment,
17263
+ opts.explain,
17264
+ opts.byChannel,
17265
+ opts.adRoas,
17266
+ opts.goal,
17267
+ opts.order,
17268
+ opts.samples
17269
+ ].some((value2) => value2 !== void 0 && value2 !== false) || this.getOptionValueSource("wait") !== "default"))
17270
+ fail(
17271
+ this,
17272
+ "--sale reads the current run without other result filters or explicit --wait",
17273
+ EXIT_CODES.validation
17274
+ );
16713
17275
  if ((opts.byChannel || opts.adRoas) && (opts.byChannel && opts.adRoas || opts.explain || opts.segment || opts.excludeSegment || opts.currency || opts.goal || opts.order || opts.samples || this.getOptionValueSource("wait") !== "default"))
16714
17276
  fail(
16715
17277
  this,
@@ -16732,6 +17294,19 @@ var results = new Command("results").description("Read experiment results and ti
16732
17294
  spinner?.fail(`Experiment "${idOrSlug}" not found`);
16733
17295
  fail(this, `Experiment "${idOrSlug}" not found.`, EXIT_CODES.validation);
16734
17296
  }
17297
+ if (opts.sale) {
17298
+ const response = await getExperimentSaleReport(creds, exp.id, opts.baseline);
17299
+ if (!response.ok || !response.data)
17300
+ fail(
17301
+ this,
17302
+ response.error ?? "Failed to fetch sale report",
17303
+ classifyStatus(response.status),
17304
+ response.errorDetails
17305
+ );
17306
+ spinner?.stop();
17307
+ output(this, response.data, () => printSaleReport(response.data));
17308
+ return;
17309
+ }
16735
17310
  const waitSeconds = Number(opts.wait ?? "120");
16736
17311
  if (!Number.isFinite(waitSeconds) || waitSeconds < 0) {
16737
17312
  fail(this, "--wait must be a non-negative number of seconds", EXIT_CODES.validation);
@@ -17709,8 +18284,8 @@ var experimentStatus = new Command("status").description(
17709
18284
  "Versions are unavailable without a pulled folder. Pull the experiment to compare local files."
17710
18285
  );
17711
18286
  if (creds.credentialKind === "user") {
17712
- const context = await getAccountContext(creds);
17713
- const owner = context.data?.data?.orgs?.find(
18287
+ const context2 = await getAccountContext(creds);
18288
+ const owner = context2.data?.data?.orgs?.find(
17714
18289
  (item) => item.shops?.some((item2) => item2.id === shopId)
17715
18290
  );
17716
18291
  if (owner) {
@@ -17809,6 +18384,12 @@ Base URL: ${creds.baseUrl}`
17809
18384
  `Experiment: ${experiment2.test_key ?? experiment2.id} / ${experiment2.name} (${experiment2.status})
17810
18385
  Dashboard: ${data.experiment.dashboard_url ?? "unknown"}`
17811
18386
  );
18387
+ console.log(
18388
+ `Scheduled start: ${formatScheduleTime(experiment2.scheduled_start_at, experiment2.schedule_timezone)}`
18389
+ );
18390
+ console.log(
18391
+ `Scheduled end: ${formatScheduleTime(experiment2.scheduled_end_at, experiment2.schedule_timezone)}`
18392
+ );
17812
18393
  console.log("Versions:");
17813
18394
  if (versions2) {
17814
18395
  for (const file of versions2.files) console.log(` ${file.state}: ${file.path}`);
@@ -18204,7 +18785,7 @@ var applyFilesystem = new Command("apply").option(
18204
18785
  console.log(` Classification: ${receipt.delivery_type} / ${receipt.counting_type}`);
18205
18786
  console.log(` Traffic: ${(receipt.traffic_allocation * 100).toFixed(0)}%`);
18206
18787
  console.log(
18207
- ` Schedule: ${receipt.schedule.scheduled_start_at ?? "none"} \u2192 ${receipt.schedule.scheduled_end_at ?? "none"}`
18788
+ ` Schedule: ${formatScheduleTime(receipt.schedule.scheduled_start_at, previewResult.scheduleTimeZone)} \u2192 ${formatScheduleTime(receipt.schedule.scheduled_end_at, previewResult.scheduleTimeZone)}`
18208
18789
  );
18209
18790
  console.log(` Goals: ${receipt.goal_count}`);
18210
18791
  console.log(
@@ -19096,6 +19677,10 @@ var import_node_path10 = __toESM(require("node:path"));
19096
19677
  var import_node_url2 = require("node:url");
19097
19678
  function toApiBody(exp) {
19098
19679
  return {
19680
+ ...exp.commerce !== void 0 ? {
19681
+ expectedCommerceRevision: exp.commerce.revision,
19682
+ ...exp.commerce.definition !== void 0 ? { definition: exp.commerce.definition } : {}
19683
+ } : {},
19099
19684
  name: exp.name,
19100
19685
  description: exp.description ?? null,
19101
19686
  traffic_allocation: exp.trafficAllocation === void 0 ? 1 : exp.trafficAllocation,
@@ -19106,7 +19691,8 @@ function toApiBody(exp) {
19106
19691
  name: v.name ?? v.id ?? "Unnamed",
19107
19692
  weight: v.weight,
19108
19693
  is_control: v.isControl ?? v.is_control ?? false,
19109
- mutations: v.mutations ?? [],
19694
+ ...v.mutations !== void 0 ? { mutations: v.mutations } : {},
19695
+ ...(v.artifactHash ?? v.artifact_hash) !== void 0 ? { expectedArtifactHash: v.artifactHash ?? v.artifact_hash } : {},
19110
19696
  custom_css: v.css ?? v.custom_css ?? null,
19111
19697
  custom_js: v.js ?? v.custom_js ?? null,
19112
19698
  reset_js: v.resetJs ?? v.reset_js ?? null,
@@ -19121,7 +19707,7 @@ async function rewriteExperimentAssets(exp, rewrite, baseDir) {
19121
19707
  variations: await Promise.all(
19122
19708
  exp.variations.map(async (variation) => ({
19123
19709
  ...variation,
19124
- mutations: await rewrite.rewriteUnknown(variation.mutations ?? [], baseDir),
19710
+ ...variation.mutations !== void 0 ? { mutations: await rewrite.rewriteUnknown(variation.mutations, baseDir) } : {},
19125
19711
  css: variation.css === void 0 ? void 0 : await rewrite.rewrite(variation.css, baseDir),
19126
19712
  custom_css: variation.custom_css === void 0 ? void 0 : await rewrite.rewrite(variation.custom_css, baseDir),
19127
19713
  js: variation.js === void 0 ? void 0 : await rewrite.rewrite(variation.js, baseDir),
@@ -19310,7 +19896,9 @@ var pushCommand = new Command("push").description("Push experiment definitions f
19310
19896
  slug: exp.slug,
19311
19897
  status: "failed",
19312
19898
  error: res.error,
19313
- statusCode: res.status
19899
+ statusCode: res.status,
19900
+ code: typeof res.errorDetails?.code === "string" ? res.errorDetails.code : void 0,
19901
+ details: res.errorDetails
19314
19902
  });
19315
19903
  continue;
19316
19904
  }
@@ -19813,6 +20401,8 @@ function sessionFromWorkspace(input) {
19813
20401
  variationId: input.variationId,
19814
20402
  workspaceId: input.workspace.workspace.id,
19815
20403
  workspaceRevisionId: input.workspace.workspace.currentRevisionId,
20404
+ commerceRevision: input.workspace.commerceRevision,
20405
+ artifactHash: input.workspace.artifactHash,
19816
20406
  checkedOutAt: (/* @__PURE__ */ new Date()).toISOString(),
19817
20407
  files: Object.fromEntries(
19818
20408
  input.workspace.files.map((file) => [
@@ -19902,6 +20492,7 @@ async function syncChangedFiles(folder, session) {
19902
20492
  file.path,
19903
20493
  {
19904
20494
  content: file.content,
20495
+ expected_version: session.files[file.path]?.sha256,
19905
20496
  source: "operator",
19906
20497
  title: `CLI sync ${file.path}`
19907
20498
  }
@@ -19912,6 +20503,13 @@ async function syncChangedFiles(folder, session) {
19912
20503
  classifyStatus(res.status),
19913
20504
  res.errorDetails
19914
20505
  );
20506
+ const saved = res.data;
20507
+ if (saved?.revision?.id) session.workspaceRevisionId = saved.revision.id;
20508
+ session.files[file.path] = {
20509
+ sha256: saved?.file?.sha256 ?? file.sha256,
20510
+ sizeBytes: file.sizeBytes
20511
+ };
20512
+ writeSession(folder, session);
19915
20513
  }
19916
20514
  return { localFiles, changed };
19917
20515
  }
@@ -19920,12 +20518,15 @@ async function runPublish(folderArg, opts = {}) {
19920
20518
  const session = readSession(folder);
19921
20519
  const { localFiles, changed } = await syncChangedFiles(folder, session);
19922
20520
  const creds = requireCredentials();
19923
- const build = await buildOperatorWorkspace(
19924
- creds,
19925
- session.experimentId,
19926
- session.variationId,
19927
- opts.allowGoalWrites ? { allowGoalWrites: true } : void 0
19928
- );
20521
+ const build = await buildOperatorWorkspace(creds, session.experimentId, session.variationId, {
20522
+ ...opts.allowGoalWrites ? { allowGoalWrites: true } : {},
20523
+ expectedCommerceRevision: session.commerceRevision,
20524
+ expectedArtifactHash: session.artifactHash,
20525
+ expectedWorkspaceRevision: session.workspaceRevisionId ?? void 0,
20526
+ expectedFileVersions: Object.fromEntries(
20527
+ Object.entries(session.files).filter(([path22]) => path22 !== "brand.md").map(([path22, file]) => [path22, file.sha256])
20528
+ )
20529
+ });
19929
20530
  if (!build.ok || !build.data?.ok) {
19930
20531
  const issueDetails = formatWorkspaceBlockingIssues(build.data);
19931
20532
  throw new CliError(
@@ -21862,13 +22463,13 @@ async function identifyRuntimeSettingsShop(command, requested) {
21862
22463
  if (credentials.credentialKind === "user") {
21863
22464
  let shopId = requested;
21864
22465
  if (!uuidPattern.test(requested)) {
21865
- const context = await getAccountContext({
22466
+ const context2 = await getAccountContext({
21866
22467
  ...credentials,
21867
22468
  resolvedContext: { ...credentials.resolvedContext, shopId: void 0 }
21868
22469
  });
21869
- if (!context.ok || !context.data)
21870
- fail(command, context.error ?? "Shop discovery failed", classifyStatus(context.status));
21871
- const match2 = context.data.data.orgs.flatMap((org) => org.shops).find((shop) => shop.domain.toLowerCase() === requested.toLowerCase());
22470
+ if (!context2.ok || !context2.data)
22471
+ fail(command, context2.error ?? "Shop discovery failed", classifyStatus(context2.status));
22472
+ const match2 = context2.data.data.orgs.flatMap((org) => org.shops).find((shop) => shop.domain.toLowerCase() === requested.toLowerCase());
21872
22473
  if (!match2)
21873
22474
  fail(command, "--shop domain is not available to this user", EXIT_CODES.validation);
21874
22475
  shopId = match2.id;
@@ -22484,17 +23085,17 @@ async function identify(command, requestedShop) {
22484
23085
  ...credentials,
22485
23086
  resolvedContext: { ...credentials.resolvedContext, shopId: void 0 }
22486
23087
  };
22487
- const context = await getAccountContext(discoveryCredentials);
22488
- if (!context.ok) {
23088
+ const context2 = await getAccountContext(discoveryCredentials);
23089
+ if (!context2.ok) {
22489
23090
  fail(
22490
23091
  command,
22491
- `${context.status === 401 || context.status === 403 ? "TRIGGERS_AUTH" : "TRIGGERS_CONFIG_ABSENT"}: ${context.error ?? "account context unavailable"}`,
22492
- classifyStatus(context.status),
22493
- context.errorDetails,
22494
- apiFailureOf(context)
23092
+ `${context2.status === 401 || context2.status === 403 ? "TRIGGERS_AUTH" : "TRIGGERS_CONFIG_ABSENT"}: ${context2.error ?? "account context unavailable"}`,
23093
+ classifyStatus(context2.status),
23094
+ context2.errorDetails,
23095
+ apiFailureOf(context2)
22495
23096
  );
22496
23097
  }
22497
- const match2 = context.data?.data?.orgs?.flatMap((org) => org.shops).find((shop) => shop.domain.toLowerCase() === requestedShop.toLowerCase());
23098
+ const match2 = context2.data?.data?.orgs?.flatMap((org) => org.shops).find((shop) => shop.domain.toLowerCase() === requestedShop.toLowerCase());
22498
23099
  if (!match2) {
22499
23100
  fail(
22500
23101
  command,
@@ -22829,9 +23430,9 @@ async function findLandingDomain(command, host) {
22829
23430
  return row;
22830
23431
  }
22831
23432
  async function verifyOnce(command, id) {
22832
- const checked = await verifyLandingPageDomain(requireCredentials(), id);
22833
- if (!checked.ok || !checked.data) domainFail(command, checked, "Failed to verify the domain");
22834
- return checked.data.data;
23433
+ const checked2 = await verifyLandingPageDomain(requireCredentials(), id);
23434
+ if (!checked2.ok || !checked2.data) domainFail(command, checked2, "Failed to verify the domain");
23435
+ return checked2.data.data;
22835
23436
  }
22836
23437
  var claim = new Command("claim").description("Claim a client hostname for landing pages (CNAME to the Apex edge)").argument("<host>", "hostname the client controls, for example offers.example.com").option("--wait", "check DNS and the certificate every 10 seconds for up to two minutes").action(async function action56(host, opts) {
22837
23438
  const result = await createLandingPageDomain(requireCredentials(), {
@@ -24872,14 +25473,14 @@ function generateInstallKit(input) {
24872
25473
  });
24873
25474
  else steps.push(manualOrderStep());
24874
25475
  if (input.botProtection.length) {
24875
- const rules = input.botProtection.map(
25476
+ const rules2 = input.botProtection.map(
24876
25477
  (kind5) => kind5 === "cloudflare" ? 'any(http.request.headers["x-apex-verification"][*] eq "<your token>")' : kind5 === "akamai" ? "Bot Manager: add a condition on request header X-Apex-Verification equals <your token> to your allowlist / transactional endpoint exception." : "Add an allow rule for request header X-Apex-Verification equals <your token>."
24877
25478
  );
24878
25479
  steps.push({
24879
25480
  key: "qa_access",
24880
25481
  title: "QA access",
24881
25482
  body: "Let Apex's QA browser through your bot protection: allow requests that carry the header X-Apex-Verification. Value: shown in Settings \u2192 Installation (send it to the person who edits the firewall rule separately).",
24882
- code: { language: "text", content: rules.join("\n") }
25483
+ code: { language: "text", content: rules2.join("\n") }
24883
25484
  });
24884
25485
  }
24885
25486
  steps.push({
@@ -26159,6 +26760,32 @@ function detail(x) {
26159
26760
  console.log(
26160
26761
  ` Goals: ${x.goals?.length ? x.goals.map((goal) => `${goal.name} (${goal.id})${goal.is_primary ? " [primary]" : ""}`).join(", ") : "None"}`
26161
26762
  );
26763
+ const rules2 = x.rules;
26764
+ if (rules2?.length)
26765
+ printTable(
26766
+ ["AUDIENCE", "ORDER", "EXPERIENCE", "ROLLOUT", "STOP", "STATE"],
26767
+ rules2.map((rule) => [
26768
+ `${rule.name} (${rule.id})`,
26769
+ `${rule.priority}/${rule.order_index}`,
26770
+ rule.variant_id,
26771
+ `${rule.rollout_percentage * 100}%`,
26772
+ rule.stop_after_match ? "Yes" : "No",
26773
+ rule.archived_at ? "Archived" : "Active"
26774
+ ]),
26775
+ [38, 12, 38, 10, 6, 10]
26776
+ );
26777
+ else if (Array.isArray(rules2)) console.log(" Audiences: None");
26778
+ const experiences2 = x.variants?.filter((variant) => variant.role === "rule_experience");
26779
+ if (experiences2?.length)
26780
+ printTable(
26781
+ ["EXPERIENCE", "ID", "VALUE"],
26782
+ experiences2.map((variant) => [
26783
+ variant.name ?? variant.key ?? variant.id,
26784
+ variant.id,
26785
+ JSON.stringify(variant.value) ?? "null"
26786
+ ]),
26787
+ [28, 38, 40]
26788
+ );
26162
26789
  console.log();
26163
26790
  }
26164
26791
  var list6 = new Command("list").alias("ls").description("List flags").option("-n, --limit <n>", "Max results", "50").action(async function(o) {
@@ -26271,7 +26898,9 @@ var updateCode = new Command("update-code").description("Edit the On variant's J
26271
26898
  current.errorDetails
26272
26899
  );
26273
26900
  const variants = current.data.variants;
26274
- const enabled = variants?.filter((variant) => variant.is_enabled_variant);
26901
+ const enabled = variants?.filter(
26902
+ (variant) => variant.is_enabled_variant && variant.role !== "rule_experience"
26903
+ );
26275
26904
  if (enabled?.length !== 1 || !enabled[0].updated_at)
26276
26905
  fail(this, "Flag does not have a versioned On variant", EXIT_CODES.validation);
26277
26906
  const result = await updateFlagCode(credentials, id, {
@@ -26300,7 +26929,74 @@ function lifecycle(action80) {
26300
26929
  output(this, r.data, () => detail(r.data));
26301
26930
  });
26302
26931
  }
26303
- var flagsCommand = new Command("flags").description("Manage flags").addCommand(list6).addCommand(get5).addCommand(status3).addCommand(write("create")).addCommand(write("update")).addCommand(updateCode).addCommand(lifecycle("activate")).addCommand(lifecycle("pause")).addCommand(lifecycle("archive")).addHelpText(
26932
+ function audienceWrite(action80) {
26933
+ const ruleAction = action80 === "set" || action80 === "reorder";
26934
+ const description = {
26935
+ set: "Replace live audiences and retain omitted audiences in history",
26936
+ reorder: "Reorder every live audience without replacing identities",
26937
+ create: "Create a rule experience from a JSON file",
26938
+ update: "Update a rule experience in place",
26939
+ delete: "Delete an experience with no active or historical rule references"
26940
+ }[action80];
26941
+ const command = new Command(action80).description(description).argument("<flag>");
26942
+ if (action80 === "update" || action80 === "delete") command.argument("<experience>");
26943
+ if (action80 !== "delete") command.requiredOption("--file <path>", "JSON body or rules array file");
26944
+ command.option(
26945
+ "--expected-updated-at <timestamp>",
26946
+ "Expected parent flag version (otherwise read it before saving)"
26947
+ );
26948
+ command.action(async function(flag, ...args) {
26949
+ const options = this.opts();
26950
+ let parsed = {};
26951
+ if (options.file) {
26952
+ try {
26953
+ parsed = readJsonInput(`@${options.file}`);
26954
+ } catch (error) {
26955
+ fail(
26956
+ this,
26957
+ `Cannot read JSON file: ${error instanceof Error ? error.message : String(error)}`,
26958
+ EXIT_CODES.validation
26959
+ );
26960
+ }
26961
+ }
26962
+ if (ruleAction && Array.isArray(parsed)) parsed = { rules: parsed };
26963
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
26964
+ fail(this, "Expected a JSON object", EXIT_CODES.validation);
26965
+ const payload = parsed;
26966
+ if (ruleAction && !Array.isArray(payload.rules))
26967
+ fail(this, "Expected a rules array", EXIT_CODES.validation);
26968
+ const creds = requireCredentials();
26969
+ let expected = options.expectedUpdatedAt ?? payload.expected_updated_at;
26970
+ if (expected === void 0) {
26971
+ const current = await getManagedDelivery(creds, kind, flag);
26972
+ if (!current.ok || !current.data)
26973
+ fail(
26974
+ this,
26975
+ current.error ?? "Failed to read flag",
26976
+ classifyStatus(current.status),
26977
+ current.errorDetails
26978
+ );
26979
+ expected = current.data.updated_at;
26980
+ }
26981
+ if (typeof expected !== "string" || !Number.isFinite(Date.parse(expected)))
26982
+ fail(this, "Provide a valid expected_updated_at parent version", EXIT_CODES.validation);
26983
+ const requestBody = { ...payload, expected_updated_at: expected };
26984
+ const experienceId = String(args[0]);
26985
+ const result = action80 === "set" ? await setFlagRules(creds, flag, requestBody) : action80 === "reorder" ? await reorderFlagRules(creds, flag, requestBody) : action80 === "create" ? await createFlagExperience(creds, flag, requestBody) : action80 === "update" ? await updateFlagExperience(creds, flag, experienceId, requestBody) : await deleteFlagExperience(creds, flag, experienceId, requestBody);
26986
+ if (!result.ok || !result.data)
26987
+ fail(this, result.error ?? "Save failed", classifyStatus(result.status), result.errorDetails);
26988
+ output(this, result.data, () => {
26989
+ detail(result.data);
26990
+ const warning = result.data.publication_warning;
26991
+ if (warning?.message) process.stderr.write(`Warning: ${warning.message}
26992
+ `);
26993
+ });
26994
+ });
26995
+ return command;
26996
+ }
26997
+ var rules = new Command("rules").description("Replace or reorder a flag's audiences").addCommand(audienceWrite("set")).addCommand(audienceWrite("reorder"));
26998
+ var experiences = new Command("experiences").description("Manage rule experiences; referenced history prevents deletion").addCommand(audienceWrite("create")).addCommand(audienceWrite("update")).addCommand(audienceWrite("delete"));
26999
+ var flagsCommand = new Command("flags").description("Manage flags").addCommand(list6).addCommand(get5).addCommand(status3).addCommand(write("create")).addCommand(write("update")).addCommand(updateCode).addCommand(rules).addCommand(experiences).addCommand(lifecycle("activate")).addCommand(lifecycle("pause")).addCommand(lifecycle("archive")).addHelpText(
26304
27000
  "after",
26305
27001
  `
26306
27002
  Examples:
@@ -26324,6 +27020,7 @@ function filters(o) {
26324
27020
  split: o.split,
26325
27021
  device: o.device,
26326
27022
  country: o.country,
27023
+ channel: o.channel,
26327
27024
  include_qa: o.includeQa ? "1" : void 0,
26328
27025
  include_removed: o.includeRemoved ? "1" : void 0,
26329
27026
  outcome: o.outcome,
@@ -26334,7 +27031,10 @@ function filters(o) {
26334
27031
  };
26335
27032
  }
26336
27033
  function readOptions(c, format = false) {
26337
- c.option("--range <range>", "UTC date range (7d, 14d, 30d, 90d, or inclusive dates)").option("--split <experiment>", "Compare experiment versions").option("--device <device>").option("--country <country>").option("--include-qa").option("--include-removed", "Read removed forms for up to 30 days");
27034
+ c.option("--range <range>", "UTC date range (7d, 14d, 30d, 90d, or inclusive dates)").option("--split <experiment>", "Compare experiment versions").option("--device <device>").option("--country <country>").option(
27035
+ "--channel <channel>",
27036
+ "Arrival channel from the visitor's latest storefront touch at or before the form attempt, like Results' By channel; attempts with no touch are unknown"
27037
+ ).option("--include-qa").option("--include-removed", "Read removed forms for up to 30 days");
26338
27038
  if (format) c.option("--format <format>", "csv, json, or table");
26339
27039
  return c;
26340
27040
  }
@@ -26467,14 +27167,14 @@ function render(command, response, surface, o = {}) {
26467
27167
  [
26468
27168
  "daily",
26469
27169
  "Daily",
26470
- ["DAY", "SESSIONS", "TIME (MEDIAN)"],
26471
- ["key", "sessions", "median_focus_ms"]
27170
+ ["DAY", "SESSIONS", "TIME (MEDIAN)", "DROP-OFF SHARE", "ERROR SHARE"],
27171
+ ["key", "sessions", "median_focus_ms", "dropoff_share", "error_share"]
26472
27172
  ],
26473
27173
  [
26474
27174
  "devices",
26475
27175
  "Devices",
26476
- ["DEVICE", "SESSIONS", "TIME (MEDIAN)"],
26477
- ["key", "sessions", "median_focus_ms"]
27176
+ ["DEVICE", "SESSIONS", "TIME (MEDIAN)", "DROP-OFF SHARE", "ERROR SHARE"],
27177
+ ["key", "sessions", "median_focus_ms", "dropoff_share", "error_share"]
26478
27178
  ],
26479
27179
  ["focus_buckets", "Focus histogram", ["BUCKET", "SESSIONS"], ["key", "sessions"]],
26480
27180
  [
@@ -26531,7 +27231,10 @@ async function call(command, method, suffix, options = {}, body6, surface = "det
26531
27231
  readOptions(formsCommand.command("list").description("List tracked forms")).action(async function(o) {
26532
27232
  await call(this, "GET", "", o, void 0, "list");
26533
27233
  });
26534
- formsCommand.command("add <key>").description("Add or restore a storefront form").requiredOption("--url <url>").requiredOption("--selector <css>").option("--name <text>").option("--complete-on-goal <goal>").option("--complete-on-page <path>").option("--complete-on-call").action(async function(key, o) {
27234
+ formsCommand.command("add <key>").description("Add or restore a storefront form").requiredOption("--url <url>").requiredOption("--selector <css>").option("--name <text>").option(
27235
+ "--complete-on-goal <goal>",
27236
+ "Goal ID, event name or name; ambiguous names are refused (use the goal ID)"
27237
+ ).option("--complete-on-page <path>").option("--complete-on-call").action(async function(key, o) {
26535
27238
  if ([
26536
27239
  o.completeOnGoal !== void 0,
26537
27240
  o.completeOnPage !== void 0,
@@ -27223,7 +27926,7 @@ function lifecycle3(action80) {
27223
27926
  output(this, r.data, () => detail4(r.data));
27224
27927
  });
27225
27928
  }
27226
- var personalizationsCommand = new Command("personalizations").description("Manage personalizations").addCommand(list10).addCommand(get9).addCommand(write5("create")).addCommand(write5("update")).addCommand(lifecycle3("activate")).addCommand(lifecycle3("pause")).addCommand(lifecycle3("archive"));
27929
+ var personalizationsCommand = new Command("personalizations").description("Manage personalizations through the compatible audience API").addCommand(list10).addCommand(get9).addCommand(write5("create")).addCommand(write5("update")).addCommand(lifecycle3("activate")).addCommand(lifecycle3("pause")).addCommand(lifecycle3("archive"));
27227
27930
 
27228
27931
  // src/commands/targeting-resources.ts
27229
27932
  init_source();
@@ -27576,9 +28279,9 @@ function pageBodyMatchTypeConflict(body6, action80) {
27576
28279
  const topLevel = check("url_pattern", body6.url_pattern, declared2);
27577
28280
  if (topLevel) return topLevel;
27578
28281
  for (const field of ["include_rules", "exclude_rules"]) {
27579
- const rules = body6[field];
27580
- if (!Array.isArray(rules)) continue;
27581
- for (const [index, rule] of rules.entries()) {
28282
+ const rules2 = body6[field];
28283
+ if (!Array.isArray(rules2)) continue;
28284
+ for (const [index, rule] of rules2.entries()) {
27582
28285
  const prefix = `${field}[${index}]`;
27583
28286
  if (typeof rule === "string") {
27584
28287
  const conflict = check(prefix, rule, declared2);
@@ -28548,8 +29251,8 @@ function parseContainer(source, id, dataLayerName = "dataLayer") {
28548
29251
  consentGatedTags.add(String(item.vtp_name ?? item.name ?? item.tag_id ?? "purchase tag"));
28549
29252
  }
28550
29253
  const predicates = Array.isArray(resources.predicates) ? resources.predicates : [];
28551
- const rules = Array.isArray(resources.rules) ? resources.rules : [];
28552
- for (const rule of rules) {
29254
+ const rules2 = Array.isArray(resources.rules) ? resources.rules : [];
29255
+ for (const rule of rules2) {
28553
29256
  if (!Array.isArray(rule)) continue;
28554
29257
  const predicateIndexes = rule.flatMap(
28555
29258
  (part) => Array.isArray(part) && part[0] === "if" ? part.slice(1).filter((value2) => typeof value2 === "number") : []
@@ -29089,13 +29792,28 @@ var settingsGet = new Command("settings").description("Show platform operator se
29089
29792
  res.data,
29090
29793
  () => printTable(
29091
29794
  ["SETTING", "VALUE"],
29092
- Object.entries(res.data.data).map(([k, v]) => [k, v ? "on" : "off"])
29795
+ Object.entries(res.data.data).map(([k, v]) => [
29796
+ k,
29797
+ typeof v === "boolean" ? v ? "on" : "off" : String(v)
29798
+ ])
29093
29799
  )
29094
29800
  );
29095
29801
  });
29096
- var set5 = new Command("set").description("Set platform kill switches").option("--guardian <on|off>").option("--srm-check <on|off>").option("--confirm", "Confirm immediate production impact").action(async function(opts) {
29097
- if (!opts.guardian && !opts.srmCheck) fail(this, "Provide --guardian or --srm-check", 3);
29098
- let body6 = {};
29802
+ var set5 = new Command("set").description("Set platform kill switches").option("--guardian <on|off>").option("--srm-check <on|off>").option("--undo-completion-window-days <days>", "Integer 0..365; 0 disables undo").option("--expected-version <version>", "Policy version returned by admin settings").option("--reason <text>", "Audit reason for a policy change").option("--confirm", "Confirm immediate production impact").action(async function(opts) {
29803
+ if (!opts.guardian && !opts.srmCheck && opts.undoCompletionWindowDays === void 0)
29804
+ fail(this, "Provide --guardian, --srm-check or --undo-completion-window-days", 3);
29805
+ const body6 = {};
29806
+ if (opts.undoCompletionWindowDays !== void 0) {
29807
+ const days = Number(opts.undoCompletionWindowDays);
29808
+ if (!/^(0|[1-9]\d*)$/.test(opts.undoCompletionWindowDays) || days > 365)
29809
+ fail(this, "--undo-completion-window-days must be an integer from 0 to 365", 3);
29810
+ if (!opts.expectedVersion || !opts.reason?.trim() || opts.reason.trim().length > 500)
29811
+ fail(this, "Policy changes require --expected-version and --reason (1..500 characters)", 3);
29812
+ body6.experiment_undo_completion_window_days = days;
29813
+ body6.expected_version = opts.expectedVersion;
29814
+ body6.reason = opts.reason.trim();
29815
+ } else if (opts.expectedVersion || opts.reason)
29816
+ fail(this, "--expected-version and --reason require --undo-completion-window-days", 3);
29099
29817
  try {
29100
29818
  if (opts.guardian) body6.guardian_enabled = switchValue(opts.guardian);
29101
29819
  if (opts.srmCheck) body6.srm_check_enabled = switchValue(opts.srmCheck);
@@ -29105,7 +29823,10 @@ var set5 = new Command("set").description("Set platform kill switches").option("
29105
29823
  if (!opts.confirm) {
29106
29824
  const switches = [
29107
29825
  opts.guardian ? `--guardian ${opts.guardian}` : "",
29108
- opts.srmCheck ? `--srm-check ${opts.srmCheck}` : ""
29826
+ opts.srmCheck ? `--srm-check ${opts.srmCheck}` : "",
29827
+ opts.undoCompletionWindowDays !== void 0 ? `--undo-completion-window-days ${opts.undoCompletionWindowDays}` : "",
29828
+ opts.expectedVersion ? `--expected-version '${opts.expectedVersion.replaceAll("'", `'"'"'`)}'` : "",
29829
+ opts.reason ? `--reason '${opts.reason.replaceAll("'", `'"'"'`)}'` : ""
29109
29830
  ].filter(Boolean).join(" ");
29110
29831
  confirmationRequired(
29111
29832
  this,
@@ -29619,11 +30340,11 @@ function adminClientLinkRequest(opts) {
29619
30340
  throw new Error("--consultant must be a UUID");
29620
30341
  const teammates = (opts.teammate ?? []).map((value2) => {
29621
30342
  const split = value2.lastIndexOf(":");
29622
- const email = value2.slice(0, split).trim().toLowerCase();
30343
+ const email2 = value2.slice(0, split).trim().toLowerCase();
29623
30344
  const role = value2.slice(split + 1);
29624
- if (split < 1 || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email) || !["editor", "client_viewer"].includes(role))
30345
+ if (split < 1 || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email2) || !["editor", "client_viewer"].includes(role))
29625
30346
  throw new Error("--teammate must be email:editor or email:client_viewer");
29626
- return { email, role };
30347
+ return { email: email2, role };
29627
30348
  });
29628
30349
  const discountMonths = opts.discountMonths === "forever" ? null : integerFlag("--discount-months", opts.discountMonths, 1);
29629
30350
  return {
@@ -29677,6 +30398,117 @@ var createClient = new Command("create").description("Create a personal managed-
29677
30398
  var clients = new Command("clients").description("Create managed-client links").addCommand(createClient);
29678
30399
  var adminCommand = new Command("admin").description("Operate allowlisted platform controls").addCommand(settingsGet).addCommand(quarantine).addCommand(shops).addCommand(clients).addCommand(customers);
29679
30400
 
30401
+ // src/commands/notifications.ts
30402
+ var notificationsCommand = new Command("notifications").description(
30403
+ "Manage notification settings"
30404
+ );
30405
+ var ready = notificationsCommand.command("ready").description("Ready to launch email recipients and Slack event toggle");
30406
+ function context(command, explicit) {
30407
+ const creds = requireUserCredentials();
30408
+ const shopId = explicit ?? creds.resolvedContext?.shopId;
30409
+ if (!shopId) fail(command, "Choose a shop with --shop <id> or apex use <org>/<shop>.", 3);
30410
+ return { creds, shopId };
30411
+ }
30412
+ function checked(command, response) {
30413
+ if (!response.ok)
30414
+ fail(
30415
+ command,
30416
+ response.status === 409 ? "Notification settings changed. Retry the command." : response.error ?? "Could not read or save notification settings.",
30417
+ classifyStatus(response.status),
30418
+ response.errorDetails,
30419
+ apiFailureOf(response)
30420
+ );
30421
+ if (!response.data) fail(command, "The API returned no notification settings.", 1);
30422
+ return response.data;
30423
+ }
30424
+ function print(command, data, lines) {
30425
+ output(command, data, () => {
30426
+ for (const line of lines) console.log(line);
30427
+ });
30428
+ }
30429
+ function emailLine(members, ids) {
30430
+ if (ids.length === 0) return "Email: off (no recipients)";
30431
+ const names = ids.map((id) => members.find((member2) => member2.user_id === id)?.email ?? id);
30432
+ return `Email: ${names.length} recipient${names.length === 1 ? "" : "s"}: ${names.join(", ")}`;
30433
+ }
30434
+ function slackLine(settings2) {
30435
+ const destination = settings2.destination;
30436
+ if (!destination) return "Slack: not connected";
30437
+ const paused = !destination.active || destination.disabled_at ? " (delivery paused)" : "";
30438
+ return `Slack: ${destination.ready_to_launch ? "on" : "off"}${paused}`;
30439
+ }
30440
+ ready.command("get").option("--shop <id>").action(async function(options) {
30441
+ const { creds, shopId } = context(this, options.shop);
30442
+ const email2 = checked(this, await getReadyEmail(creds, shopId)).data;
30443
+ assertResponseShop(email2.shop_id, shopId, creds.credentialKind);
30444
+ const slack2 = checked(this, await getReadySlack(creds, shopId)).data;
30445
+ assertResponseShop(slack2.shop_id, shopId, creds.credentialKind);
30446
+ print(this, { email: email2, slack: slack2 }, [
30447
+ emailLine(
30448
+ email2.members,
30449
+ email2.members.filter((member2) => member2.selected).map((member2) => member2.user_id)
30450
+ ),
30451
+ slackLine(slack2)
30452
+ ]);
30453
+ });
30454
+ var email = ready.command("email").description("Choose current shop members to receive ready emails; off clears recipients");
30455
+ for (const operation of ["set", "add", "remove", "off"]) {
30456
+ const command = email.command(operation).option("--shop <id>");
30457
+ if (operation !== "off")
30458
+ command.requiredOption("--recipients <emails>", "Comma-separated shop member emails");
30459
+ command.action(async function(options) {
30460
+ const { creds, shopId } = context(this, options.shop);
30461
+ const current = checked(this, await getReadyEmail(creds, shopId)).data;
30462
+ assertResponseShop(current.shop_id, shopId, creds.credentialKind);
30463
+ const ids = [];
30464
+ if (operation !== "off") {
30465
+ const emails = options.recipients.split(",").map((value2) => value2.trim().toLowerCase());
30466
+ if (emails.some((value2) => !value2))
30467
+ fail(this, "Provide a comma-separated list of member emails without empty entries.", 3);
30468
+ for (const address of new Set(emails)) {
30469
+ const matches = current.members.filter((member2) => member2.email?.toLowerCase() === address);
30470
+ if (matches.length !== 1)
30471
+ fail(this, `Unknown or ambiguous shop member email: ${address}`, 3);
30472
+ ids.push(matches[0].user_id);
30473
+ }
30474
+ }
30475
+ const selected2 = current.members.filter((member2) => member2.selected).map((member2) => member2.user_id);
30476
+ const recipient_user_ids = operation === "off" ? [] : operation === "set" ? ids : operation === "add" ? [.../* @__PURE__ */ new Set([...selected2, ...ids])] : selected2.filter((id) => !ids.includes(id));
30477
+ const saved = checked(
30478
+ this,
30479
+ await setReadyEmail(creds, shopId, { version: current.version, recipient_user_ids })
30480
+ );
30481
+ assertResponseShop(saved.data.shop_id, shopId, creds.credentialKind);
30482
+ print(this, saved, [emailLine(current.members, saved.data.recipient_user_ids)]);
30483
+ });
30484
+ }
30485
+ var slack = ready.command("slack").description(
30486
+ "Toggle the ready event on an existing Slack destination; other events and pause state stay as configured"
30487
+ );
30488
+ for (const enabled of [true, false]) {
30489
+ slack.command(enabled ? "on" : "off").option("--shop <id>").action(async function(options) {
30490
+ const { creds, shopId } = context(this, options.shop);
30491
+ const current = checked(this, await getReadySlack(creds, shopId)).data;
30492
+ assertResponseShop(current.shop_id, shopId, creds.credentialKind);
30493
+ if (!current.destination || !current.version)
30494
+ fail(this, "Connect a Slack destination in Settings > Integrations first.", 3);
30495
+ const result = checked(
30496
+ this,
30497
+ await setReadySlack(creds, shopId, {
30498
+ version: current.version,
30499
+ ready_to_launch: enabled
30500
+ })
30501
+ );
30502
+ assertResponseShop(result.data.shop_id, shopId, creds.credentialKind);
30503
+ if (enabled && result.data.destination && (!result.data.destination.active || result.data.destination.disabled_at)) {
30504
+ process.stderr.write(
30505
+ "Ready to launch is selected, but Slack delivery is paused. Resume the destination in Settings > Integrations.\n"
30506
+ );
30507
+ }
30508
+ print(this, result, [slackLine(result.data)]);
30509
+ });
30510
+ }
30511
+
29680
30512
  // src/commands/membership.ts
29681
30513
  init_source();
29682
30514
  async function run2(c, p, label) {
@@ -29751,7 +30583,7 @@ orgCommand.command("invite").argument("<email>").option("--role <role>", "Alias
29751
30583
  "--default-shop-role <role>",
29752
30584
  "Default shop role for every brand in the workspace (workspace defaults are internal roles)"
29753
30585
  ).choices(["editor"]).default("editor")
29754
- ).action(async function(email, o) {
30586
+ ).action(async function(email2, o) {
29755
30587
  if (o.role !== void 0) {
29756
30588
  process.stderr.write(
29757
30589
  "Warning: --role is deprecated for apex org invite; use --org-role instead. --role will be removed in v0.7.0.\n"
@@ -29764,7 +30596,7 @@ orgCommand.command("invite").argument("<email>").option("--role <role>", "Alias
29764
30596
  }
29765
30597
  await run2(
29766
30598
  this,
29767
- inviteOrg(requireUserCredentials(), email, o.role ?? o.orgRole, o.defaultShopRole),
30599
+ inviteOrg(requireUserCredentials(), email2, o.role ?? o.orgRole, o.defaultShopRole),
29768
30600
  "invite member"
29769
30601
  );
29770
30602
  });
@@ -29836,13 +30668,13 @@ teamCommand.command("invite").argument("<email>").option("--shop <id>", teamShop
29836
30668
  ).option(
29837
30669
  "--design-approver",
29838
30670
  "Also let them sign off designs once they accept (a client_viewer with this is a Client approver). Needs store owner or admin rights."
29839
- ).action(async function(email, o) {
30671
+ ).action(async function(email2, o) {
29840
30672
  o.shop = teamShop(this, o.shop);
29841
30673
  await run2(
29842
30674
  this,
29843
30675
  inviteTeam(
29844
30676
  requireUserCredentials(),
29845
- email,
30677
+ email2,
29846
30678
  o.shop,
29847
30679
  o.role,
29848
30680
  o.designApprover ? { "approvals:design": true } : void 0
@@ -31532,6 +32364,7 @@ program2.addCommand(skillsCommand);
31532
32364
  program2.addCommand(adminCommand, { hidden: true });
31533
32365
  program2.addCommand(orgCommand);
31534
32366
  program2.addCommand(teamCommand);
32367
+ program2.addCommand(notificationsCommand);
31535
32368
  program2.addCommand(upgradeCommand);
31536
32369
  addJsonOption(program2);
31537
32370
  registerTelemetryProgram(program2);