@drip-apex/apex-cli 0.21.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 +61 -0
  2. package/dist/cli.js +954 -286
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -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.21.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: [
@@ -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;
@@ -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
  }
@@ -10453,6 +10517,30 @@ var useCommand = new Command("use").description("Set the current Apex organizati
10453
10517
  );
10454
10518
  });
10455
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
+
10456
10544
  // ../shared-runtime/src/experiment-exposure.ts
10457
10545
  var EXPERIMENT_EXPOSURE_ERROR = "Experiment exposure must be greater than 0% and at most 100%.";
10458
10546
  function isExperimentExposure(value2) {
@@ -11172,6 +11260,22 @@ var experiment_filesystem_schema_default = {
11172
11260
  definition: {
11173
11261
  additionalProperties: false,
11174
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
+ },
11175
11279
  baseCurrency: {
11176
11280
  maxLength: 3,
11177
11281
  minLength: 3,
@@ -11181,6 +11285,90 @@ var experiment_filesystem_schema_default = {
11181
11285
  levers: {
11182
11286
  additionalProperties: false,
11183
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
+ },
11184
11372
  offer: {
11185
11373
  oneOf: [
11186
11374
  {
@@ -11191,6 +11379,11 @@ var experiment_filesystem_schema_default = {
11191
11379
  pattern: "^gid:\\/\\/shopify\\/ProductVariant\\/\\d+$",
11192
11380
  type: "string"
11193
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
+ },
11194
11387
  offerType: {
11195
11388
  const: "discount_depth",
11196
11389
  type: "string"
@@ -11282,6 +11475,11 @@ var experiment_filesystem_schema_default = {
11282
11475
  pattern: "^gid:\\/\\/shopify\\/ProductVariant\\/\\d+$",
11283
11476
  type: "string"
11284
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
+ },
11285
11483
  offerType: {
11286
11484
  const: "gwp_threshold",
11287
11485
  type: "string"
@@ -11477,6 +11675,11 @@ var experiment_filesystem_schema_default = {
11477
11675
  pattern: "^gid:\\/\\/shopify\\/ProductVariant\\/\\d+$",
11478
11676
  type: "string"
11479
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
+ },
11480
11683
  offerType: {
11481
11684
  const: "quantity_breaks",
11482
11685
  type: "string"
@@ -11667,23 +11870,250 @@ var experiment_filesystem_schema_default = {
11667
11870
  price: {
11668
11871
  additionalProperties: false,
11669
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
+ },
11670
12037
  fixedPriceMarkets: {
11671
12038
  description: "Reserved price feature. Refused at compile with commerce_definition_feature_unavailable."
11672
12039
  },
11673
12040
  foreignCurrencies: {
11674
12041
  description: "Reserved price feature. Refused at compile with commerce_definition_feature_unavailable."
11675
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
+ },
11676
12053
  products: {
11677
12054
  additionalProperties: false,
11678
12055
  properties: {
11679
12056
  select: {
11680
12057
  additionalProperties: false,
12058
+ if: {
12059
+ properties: {
12060
+ all: {
12061
+ const: true
12062
+ }
12063
+ },
12064
+ required: ["all"]
12065
+ },
11681
12066
  properties: {
11682
12067
  all: {
11683
12068
  const: true,
11684
- 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.",
11685
12070
  type: "boolean"
11686
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
+ },
11687
12117
  collections: {
11688
12118
  default: [],
11689
12119
  items: {
@@ -11694,6 +12124,16 @@ var experiment_filesystem_schema_default = {
11694
12124
  maxItems: 100,
11695
12125
  type: "array"
11696
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
+ },
11697
12137
  excludeProducts: {
11698
12138
  default: [],
11699
12139
  items: {
@@ -11704,6 +12144,24 @@ var experiment_filesystem_schema_default = {
11704
12144
  maxItems: 100,
11705
12145
  type: "array"
11706
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
+ },
11707
12165
  includeProducts: {
11708
12166
  default: [],
11709
12167
  items: {
@@ -11714,6 +12172,14 @@ var experiment_filesystem_schema_default = {
11714
12172
  maxItems: 100,
11715
12173
  type: "array"
11716
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
+ },
11717
12183
  productTypes: {
11718
12184
  default: [],
11719
12185
  items: {
@@ -11742,6 +12208,25 @@ var experiment_filesystem_schema_default = {
11742
12208
  type: "array"
11743
12209
  }
11744
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
+ },
11745
12230
  type: "object"
11746
12231
  }
11747
12232
  },
@@ -11774,6 +12259,91 @@ var experiment_filesystem_schema_default = {
11774
12259
  enum: ["include", "exclude"],
11775
12260
  type: "string"
11776
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
+ },
11777
12347
  discountCodes: {
11778
12348
  additionalProperties: false,
11779
12349
  properties: {
@@ -11869,7 +12439,8 @@ var experiment_filesystem_schema_default = {
11869
12439
  type: "string"
11870
12440
  },
11871
12441
  value: {
11872
- 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}$",
11873
12444
  type: "string"
11874
12445
  }
11875
12446
  },
@@ -12026,6 +12597,31 @@ var experiment_filesystem_schema_default = {
12026
12597
  {
12027
12598
  additionalProperties: false,
12028
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
+ },
12029
12625
  method: {
12030
12626
  const: "amount_off",
12031
12627
  type: "string"
@@ -12235,6 +12831,36 @@ var experiment_filesystem_schema_default = {
12235
12831
  },
12236
12832
  type: "object"
12237
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
+ },
12238
12864
  priceFloor: {
12239
12865
  additionalProperties: false,
12240
12866
  properties: {
@@ -12261,6 +12887,42 @@ var experiment_filesystem_schema_default = {
12261
12887
  },
12262
12888
  type: "object"
12263
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
+ },
12264
12926
  schemaVersion: {
12265
12927
  const: 2,
12266
12928
  type: "number"
@@ -14150,14 +14812,7 @@ var experiment_filesystem_schema_default = {
14150
14812
  ]
14151
14813
  },
14152
14814
  description: {
14153
- anyOf: [
14154
- {
14155
- type: "string"
14156
- },
14157
- {
14158
- type: "null"
14159
- }
14160
- ]
14815
+ type: ["string", "null"]
14161
14816
  },
14162
14817
  environmentKey: {
14163
14818
  type: "string"
@@ -14167,14 +14822,7 @@ var experiment_filesystem_schema_default = {
14167
14822
  type: "string"
14168
14823
  },
14169
14824
  id: {
14170
- anyOf: [
14171
- {
14172
- type: "string"
14173
- },
14174
- {
14175
- type: "null"
14176
- }
14177
- ]
14825
+ type: ["string", "null"]
14178
14826
  },
14179
14827
  name: {
14180
14828
  type: "string"
@@ -14190,37 +14838,16 @@ var experiment_filesystem_schema_default = {
14190
14838
  type: "number"
14191
14839
  },
14192
14840
  slug: {
14193
- anyOf: [
14194
- {
14195
- type: "string"
14196
- },
14197
- {
14198
- type: "null"
14199
- }
14200
- ]
14841
+ type: ["string", "null"]
14201
14842
  },
14202
14843
  statisticalPower: {
14203
14844
  type: "number"
14204
14845
  },
14205
14846
  storeUrl: {
14206
- anyOf: [
14207
- {
14208
- type: "string"
14209
- },
14210
- {
14211
- type: "null"
14212
- }
14213
- ]
14847
+ type: ["string", "null"]
14214
14848
  },
14215
14849
  testKey: {
14216
- anyOf: [
14217
- {
14218
- type: "string"
14219
- },
14220
- {
14221
- type: "null"
14222
- }
14223
- ]
14850
+ type: ["string", "null"]
14224
14851
  },
14225
14852
  trafficAllocation: {
14226
14853
  exclusiveMinimum: 0,
@@ -14251,14 +14878,7 @@ var experiment_filesystem_schema_default = {
14251
14878
  additionalProperties: false,
14252
14879
  properties: {
14253
14880
  endedAt: {
14254
- anyOf: [
14255
- {
14256
- type: "string"
14257
- },
14258
- {
14259
- type: "null"
14260
- }
14261
- ]
14881
+ type: ["string", "null"]
14262
14882
  },
14263
14883
  launchApproval: {
14264
14884
  anyOf: [
@@ -14266,24 +14886,10 @@ var experiment_filesystem_schema_default = {
14266
14886
  additionalProperties: false,
14267
14887
  properties: {
14268
14888
  decidedAt: {
14269
- anyOf: [
14270
- {
14271
- type: "string"
14272
- },
14273
- {
14274
- type: "null"
14275
- }
14276
- ]
14889
+ type: ["string", "null"]
14277
14890
  },
14278
14891
  note: {
14279
- anyOf: [
14280
- {
14281
- type: "string"
14282
- },
14283
- {
14284
- type: "null"
14285
- }
14286
- ]
14892
+ type: ["string", "null"]
14287
14893
  },
14288
14894
  status: {
14289
14895
  type: "string"
@@ -14315,38 +14921,17 @@ var experiment_filesystem_schema_default = {
14315
14921
  type: "boolean"
14316
14922
  },
14317
14923
  scheduledEndAt: {
14318
- anyOf: [
14319
- {
14320
- type: "string"
14321
- },
14322
- {
14323
- type: "null"
14324
- }
14325
- ]
14326
- },
14327
- scheduledStartAt: {
14328
- anyOf: [
14329
- {
14330
- type: "string"
14331
- },
14332
- {
14333
- type: "null"
14334
- }
14335
- ]
14924
+ type: ["string", "null"]
14925
+ },
14926
+ scheduledStartAt: {
14927
+ type: ["string", "null"]
14336
14928
  },
14337
14929
  schemaVersion: {
14338
14930
  const: 1,
14339
14931
  type: "number"
14340
14932
  },
14341
14933
  startedAt: {
14342
- anyOf: [
14343
- {
14344
- type: "string"
14345
- },
14346
- {
14347
- type: "null"
14348
- }
14349
- ]
14934
+ type: ["string", "null"]
14350
14935
  },
14351
14936
  status: {
14352
14937
  type: "string"
@@ -14386,14 +14971,7 @@ var experiment_filesystem_schema_default = {
14386
14971
  type: "boolean"
14387
14972
  },
14388
14973
  eventName: {
14389
- anyOf: [
14390
- {
14391
- type: "string"
14392
- },
14393
- {
14394
- type: "null"
14395
- }
14396
- ]
14974
+ type: ["string", "null"]
14397
14975
  },
14398
14976
  filter: {
14399
14977
  anyOf: [
@@ -14404,14 +14982,7 @@ var experiment_filesystem_schema_default = {
14404
14982
  ]
14405
14983
  },
14406
14984
  id: {
14407
- anyOf: [
14408
- {
14409
- type: "string"
14410
- },
14411
- {
14412
- type: "null"
14413
- }
14414
- ]
14985
+ type: ["string", "null"]
14415
14986
  },
14416
14987
  isPrimary: {
14417
14988
  type: "boolean"
@@ -14421,68 +14992,26 @@ var experiment_filesystem_schema_default = {
14421
14992
  type: "string"
14422
14993
  },
14423
14994
  pageId: {
14424
- anyOf: [
14425
- {
14426
- type: "string"
14427
- },
14428
- {
14429
- type: "null"
14430
- }
14431
- ]
14995
+ type: ["string", "null"]
14432
14996
  },
14433
14997
  selector: {
14434
- anyOf: [
14435
- {
14436
- type: "string"
14437
- },
14438
- {
14439
- type: "null"
14440
- }
14441
- ]
14998
+ type: ["string", "null"]
14442
14999
  },
14443
15000
  textMatch: {
14444
- anyOf: [
14445
- {
14446
- type: "string"
14447
- },
14448
- {
14449
- type: "null"
14450
- }
14451
- ]
15001
+ type: ["string", "null"]
14452
15002
  },
14453
15003
  type: {
14454
15004
  enum: ["click", "pageview", "custom", "revenue"],
14455
15005
  type: "string"
14456
15006
  },
14457
15007
  urlPattern: {
14458
- anyOf: [
14459
- {
14460
- type: "string"
14461
- },
14462
- {
14463
- type: "null"
14464
- }
14465
- ]
15008
+ type: ["string", "null"]
14466
15009
  },
14467
15010
  valueProperty: {
14468
- anyOf: [
14469
- {
14470
- type: "string"
14471
- },
14472
- {
14473
- type: "null"
14474
- }
14475
- ]
15011
+ type: ["string", "null"]
14476
15012
  },
14477
15013
  valueUnit: {
14478
- anyOf: [
14479
- {
14480
- type: "string"
14481
- },
14482
- {
14483
- type: "null"
14484
- }
14485
- ]
15014
+ type: ["string", "null"]
14486
15015
  }
14487
15016
  },
14488
15017
  required: ["name", "type"],
@@ -14491,14 +15020,7 @@ var experiment_filesystem_schema_default = {
14491
15020
  type: "array"
14492
15021
  },
14493
15022
  primaryGoalId: {
14494
- anyOf: [
14495
- {
14496
- type: "string"
14497
- },
14498
- {
14499
- type: "null"
14500
- }
14501
- ]
15023
+ type: ["string", "null"]
14502
15024
  },
14503
15025
  schemaVersion: {
14504
15026
  const: 1,
@@ -14518,24 +15040,10 @@ var experiment_filesystem_schema_default = {
14518
15040
  additionalProperties: false,
14519
15041
  properties: {
14520
15042
  approvedAt: {
14521
- anyOf: [
14522
- {
14523
- type: "string"
14524
- },
14525
- {
14526
- type: "null"
14527
- }
14528
- ]
15043
+ type: ["string", "null"]
14529
15044
  },
14530
15045
  completedAt: {
14531
- anyOf: [
14532
- {
14533
- type: "string"
14534
- },
14535
- {
14536
- type: "null"
14537
- }
14538
- ]
15046
+ type: ["string", "null"]
14539
15047
  },
14540
15048
  id: {
14541
15049
  type: "string"
@@ -14564,24 +15072,10 @@ var experiment_filesystem_schema_default = {
14564
15072
  ]
14565
15073
  },
14566
15074
  qaApprovedAt: {
14567
- anyOf: [
14568
- {
14569
- type: "string"
14570
- },
14571
- {
14572
- type: "null"
14573
- }
14574
- ]
15075
+ type: ["string", "null"]
14575
15076
  },
14576
15077
  qaApprovedBy: {
14577
- anyOf: [
14578
- {
14579
- type: "string"
14580
- },
14581
- {
14582
- type: "null"
14583
- }
14584
- ]
15078
+ type: ["string", "null"]
14585
15079
  },
14586
15080
  readonly: {
14587
15081
  const: true,
@@ -14805,44 +15299,16 @@ var experiment_filesystem_schema_default = {
14805
15299
  additionalProperties: false,
14806
15300
  properties: {
14807
15301
  hash: {
14808
- anyOf: [
14809
- {
14810
- type: "string"
14811
- },
14812
- {
14813
- type: "null"
14814
- }
14815
- ]
15302
+ type: ["string", "null"]
14816
15303
  },
14817
15304
  reviewedAt: {
14818
- anyOf: [
14819
- {
14820
- type: "string"
14821
- },
14822
- {
14823
- type: "null"
14824
- }
14825
- ]
15305
+ type: ["string", "null"]
14826
15306
  },
14827
15307
  reviewedBy: {
14828
- anyOf: [
14829
- {
14830
- type: "string"
14831
- },
14832
- {
14833
- type: "null"
14834
- }
14835
- ]
15308
+ type: ["string", "null"]
14836
15309
  },
14837
15310
  source: {
14838
- anyOf: [
14839
- {
14840
- type: "string"
14841
- },
14842
- {
14843
- type: "null"
14844
- }
14845
- ]
15311
+ type: ["string", "null"]
14846
15312
  },
14847
15313
  status: {
14848
15314
  type: "string"
@@ -14856,50 +15322,22 @@ var experiment_filesystem_schema_default = {
14856
15322
  type: "string"
14857
15323
  },
14858
15324
  cssArtifactId: {
14859
- anyOf: [
14860
- {
14861
- type: "string"
14862
- },
14863
- {
14864
- type: "null"
14865
- }
14866
- ]
15325
+ type: ["string", "null"]
14867
15326
  },
14868
15327
  id: {
14869
- anyOf: [
14870
- {
14871
- type: "string"
14872
- },
14873
- {
14874
- type: "null"
14875
- }
14876
- ]
15328
+ type: ["string", "null"]
14877
15329
  },
14878
15330
  isControl: {
14879
15331
  type: "boolean"
14880
15332
  },
14881
15333
  jsArtifactId: {
14882
- anyOf: [
14883
- {
14884
- type: "string"
14885
- },
14886
- {
14887
- type: "null"
14888
- }
14889
- ]
15334
+ type: ["string", "null"]
14890
15335
  },
14891
15336
  name: {
14892
15337
  type: "string"
14893
15338
  },
14894
15339
  redirectUrl: {
14895
- anyOf: [
14896
- {
14897
- type: "string"
14898
- },
14899
- {
14900
- type: "null"
14901
- }
14902
- ]
15340
+ type: ["string", "null"]
14903
15341
  },
14904
15342
  schemaVersion: {
14905
15343
  const: 1,
@@ -15457,14 +15895,14 @@ function urlRuleClause(count2) {
15457
15895
  function effectivePageTargetingLabel(targeting) {
15458
15896
  const raw = isRecord(targeting) ? targeting : {};
15459
15897
  const pages = pageEntries(raw);
15460
- const rules = authoredUrlRules(raw);
15898
+ const rules2 = authoredUrlRules(raw);
15461
15899
  const pagesLabel = pages.map((page) => {
15462
15900
  const readable = page.name ?? UNKNOWN_PAGE_NAME;
15463
15901
  return page.include ? readable : `Exclude ${readable}`;
15464
15902
  }).join(", ");
15465
- if (pages.length && rules.length) return `${pagesLabel} and ${urlRuleClause(rules.length)}`;
15903
+ if (pages.length && rules2.length) return `${pagesLabel} and ${urlRuleClause(rules2.length)}`;
15466
15904
  if (pages.length) return pagesLabel;
15467
- if (rules.length) return `${urlRuleClause(rules.length)} (no saved Page)`;
15905
+ if (rules2.length) return `${urlRuleClause(rules2.length)} (no saved Page)`;
15468
15906
  return ALL_PAGES_LABEL;
15469
15907
  }
15470
15908
 
@@ -15750,7 +16188,10 @@ function printExperiment(exp, dashboardUrl = null) {
15750
16188
  console.log(` Classification: ${exp.counting_type}`);
15751
16189
  console.log(` Traffic: ${(exp.traffic_allocation * 100).toFixed(0)}%`);
15752
16190
  console.log(` QA approved: ${exp.qa_approved_at ?? "none"}`);
15753
- 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
+ );
15754
16195
  console.log(` Started: ${exp.started_at ?? "none"}`);
15755
16196
  if (exp.targeting !== void 0) {
15756
16197
  console.log(` Targeting: ${effectivePageTargetingLabel(exp.targeting)}`);
@@ -16290,7 +16731,12 @@ async function getQaPreviewUrls(creds, experimentId) {
16290
16731
  );
16291
16732
  const storeUrl = res.data?.store_url;
16292
16733
  if (!res.ok || !res.data || !storeUrl) {
16293
- return { previewUrls: [], unavailable: true, dashboardUrl };
16734
+ return {
16735
+ previewUrls: [],
16736
+ unavailable: true,
16737
+ dashboardUrl,
16738
+ scheduleTimeZone: res.data?.schedule_timezone
16739
+ };
16294
16740
  }
16295
16741
  const experiment2 = res.data;
16296
16742
  const previewUrls = await Promise.all(
@@ -16309,7 +16755,12 @@ async function getQaPreviewUrls(creds, experimentId) {
16309
16755
  };
16310
16756
  })
16311
16757
  );
16312
- return { previewUrls, unavailable: false, dashboardUrl };
16758
+ return {
16759
+ previewUrls,
16760
+ unavailable: false,
16761
+ dashboardUrl,
16762
+ scheduleTimeZone: experiment2.schedule_timezone
16763
+ };
16313
16764
  } catch {
16314
16765
  return { previewUrls: [], unavailable: true, dashboardUrl: fallbackDashboardUrl };
16315
16766
  }
@@ -16724,7 +17175,103 @@ ${JSON.stringify(data.order, null, 2)}`);
16724
17175
  console.log(`
16725
17176
  Definitions: ${data.definitions_url}`);
16726
17177
  }
16727
- 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
+ );
16728
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"))
16729
17276
  fail(
16730
17277
  this,
@@ -16747,6 +17294,19 @@ var results = new Command("results").description("Read experiment results and ti
16747
17294
  spinner?.fail(`Experiment "${idOrSlug}" not found`);
16748
17295
  fail(this, `Experiment "${idOrSlug}" not found.`, EXIT_CODES.validation);
16749
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
+ }
16750
17310
  const waitSeconds = Number(opts.wait ?? "120");
16751
17311
  if (!Number.isFinite(waitSeconds) || waitSeconds < 0) {
16752
17312
  fail(this, "--wait must be a non-negative number of seconds", EXIT_CODES.validation);
@@ -17824,6 +18384,12 @@ Base URL: ${creds.baseUrl}`
17824
18384
  `Experiment: ${experiment2.test_key ?? experiment2.id} / ${experiment2.name} (${experiment2.status})
17825
18385
  Dashboard: ${data.experiment.dashboard_url ?? "unknown"}`
17826
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
+ );
17827
18393
  console.log("Versions:");
17828
18394
  if (versions2) {
17829
18395
  for (const file of versions2.files) console.log(` ${file.state}: ${file.path}`);
@@ -18219,7 +18785,7 @@ var applyFilesystem = new Command("apply").option(
18219
18785
  console.log(` Classification: ${receipt.delivery_type} / ${receipt.counting_type}`);
18220
18786
  console.log(` Traffic: ${(receipt.traffic_allocation * 100).toFixed(0)}%`);
18221
18787
  console.log(
18222
- ` 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)}`
18223
18789
  );
18224
18790
  console.log(` Goals: ${receipt.goal_count}`);
18225
18791
  console.log(
@@ -24907,14 +25473,14 @@ function generateInstallKit(input) {
24907
25473
  });
24908
25474
  else steps.push(manualOrderStep());
24909
25475
  if (input.botProtection.length) {
24910
- const rules = input.botProtection.map(
25476
+ const rules2 = input.botProtection.map(
24911
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>."
24912
25478
  );
24913
25479
  steps.push({
24914
25480
  key: "qa_access",
24915
25481
  title: "QA access",
24916
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).",
24917
- code: { language: "text", content: rules.join("\n") }
25483
+ code: { language: "text", content: rules2.join("\n") }
24918
25484
  });
24919
25485
  }
24920
25486
  steps.push({
@@ -26194,6 +26760,32 @@ function detail(x) {
26194
26760
  console.log(
26195
26761
  ` Goals: ${x.goals?.length ? x.goals.map((goal) => `${goal.name} (${goal.id})${goal.is_primary ? " [primary]" : ""}`).join(", ") : "None"}`
26196
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
+ );
26197
26789
  console.log();
26198
26790
  }
26199
26791
  var list6 = new Command("list").alias("ls").description("List flags").option("-n, --limit <n>", "Max results", "50").action(async function(o) {
@@ -26306,7 +26898,9 @@ var updateCode = new Command("update-code").description("Edit the On variant's J
26306
26898
  current.errorDetails
26307
26899
  );
26308
26900
  const variants = current.data.variants;
26309
- 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
+ );
26310
26904
  if (enabled?.length !== 1 || !enabled[0].updated_at)
26311
26905
  fail(this, "Flag does not have a versioned On variant", EXIT_CODES.validation);
26312
26906
  const result = await updateFlagCode(credentials, id, {
@@ -26335,7 +26929,74 @@ function lifecycle(action80) {
26335
26929
  output(this, r.data, () => detail(r.data));
26336
26930
  });
26337
26931
  }
26338
- 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(
26339
27000
  "after",
26340
27001
  `
26341
27002
  Examples:
@@ -26359,6 +27020,7 @@ function filters(o) {
26359
27020
  split: o.split,
26360
27021
  device: o.device,
26361
27022
  country: o.country,
27023
+ channel: o.channel,
26362
27024
  include_qa: o.includeQa ? "1" : void 0,
26363
27025
  include_removed: o.includeRemoved ? "1" : void 0,
26364
27026
  outcome: o.outcome,
@@ -26369,7 +27031,10 @@ function filters(o) {
26369
27031
  };
26370
27032
  }
26371
27033
  function readOptions(c, format = false) {
26372
- 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");
26373
27038
  if (format) c.option("--format <format>", "csv, json, or table");
26374
27039
  return c;
26375
27040
  }
@@ -26502,14 +27167,14 @@ function render(command, response, surface, o = {}) {
26502
27167
  [
26503
27168
  "daily",
26504
27169
  "Daily",
26505
- ["DAY", "SESSIONS", "TIME (MEDIAN)"],
26506
- ["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"]
26507
27172
  ],
26508
27173
  [
26509
27174
  "devices",
26510
27175
  "Devices",
26511
- ["DEVICE", "SESSIONS", "TIME (MEDIAN)"],
26512
- ["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"]
26513
27178
  ],
26514
27179
  ["focus_buckets", "Focus histogram", ["BUCKET", "SESSIONS"], ["key", "sessions"]],
26515
27180
  [
@@ -26566,7 +27231,10 @@ async function call(command, method, suffix, options = {}, body6, surface = "det
26566
27231
  readOptions(formsCommand.command("list").description("List tracked forms")).action(async function(o) {
26567
27232
  await call(this, "GET", "", o, void 0, "list");
26568
27233
  });
26569
- 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) {
26570
27238
  if ([
26571
27239
  o.completeOnGoal !== void 0,
26572
27240
  o.completeOnPage !== void 0,
@@ -27258,7 +27926,7 @@ function lifecycle3(action80) {
27258
27926
  output(this, r.data, () => detail4(r.data));
27259
27927
  });
27260
27928
  }
27261
- 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"));
27262
27930
 
27263
27931
  // src/commands/targeting-resources.ts
27264
27932
  init_source();
@@ -27611,9 +28279,9 @@ function pageBodyMatchTypeConflict(body6, action80) {
27611
28279
  const topLevel = check("url_pattern", body6.url_pattern, declared2);
27612
28280
  if (topLevel) return topLevel;
27613
28281
  for (const field of ["include_rules", "exclude_rules"]) {
27614
- const rules = body6[field];
27615
- if (!Array.isArray(rules)) continue;
27616
- 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()) {
27617
28285
  const prefix = `${field}[${index}]`;
27618
28286
  if (typeof rule === "string") {
27619
28287
  const conflict = check(prefix, rule, declared2);
@@ -28583,8 +29251,8 @@ function parseContainer(source, id, dataLayerName = "dataLayer") {
28583
29251
  consentGatedTags.add(String(item.vtp_name ?? item.name ?? item.tag_id ?? "purchase tag"));
28584
29252
  }
28585
29253
  const predicates = Array.isArray(resources.predicates) ? resources.predicates : [];
28586
- const rules = Array.isArray(resources.rules) ? resources.rules : [];
28587
- for (const rule of rules) {
29254
+ const rules2 = Array.isArray(resources.rules) ? resources.rules : [];
29255
+ for (const rule of rules2) {
28588
29256
  if (!Array.isArray(rule)) continue;
28589
29257
  const predicateIndexes = rule.flatMap(
28590
29258
  (part) => Array.isArray(part) && part[0] === "if" ? part.slice(1).filter((value2) => typeof value2 === "number") : []