@drip-apex/apex-cli 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -127,11 +127,11 @@ var require_argument = __commonJS({
127
127
  /**
128
128
  * @package
129
129
  */
130
- _concatValue(value, previous) {
130
+ _concatValue(value2, previous) {
131
131
  if (previous === this.defaultValue || !Array.isArray(previous)) {
132
- return [value];
132
+ return [value2];
133
133
  }
134
- return previous.concat(value);
134
+ return previous.concat(value2);
135
135
  }
136
136
  /**
137
137
  * Set the default value, and optionally supply the description to be displayed in the help.
@@ -140,8 +140,8 @@ var require_argument = __commonJS({
140
140
  * @param {string} [description]
141
141
  * @return {Argument}
142
142
  */
143
- default(value, description) {
144
- this.defaultValue = value;
143
+ default(value2, description) {
144
+ this.defaultValue = value2;
145
145
  this.defaultValueDescription = description;
146
146
  return this;
147
147
  }
@@ -815,8 +815,8 @@ var require_option = __commonJS({
815
815
  * @param {string} [description]
816
816
  * @return {Option}
817
817
  */
818
- default(value, description) {
819
- this.defaultValue = value;
818
+ default(value2, description) {
819
+ this.defaultValue = value2;
820
820
  this.defaultValueDescription = description;
821
821
  return this;
822
822
  }
@@ -917,11 +917,11 @@ var require_option = __commonJS({
917
917
  /**
918
918
  * @package
919
919
  */
920
- _concatValue(value, previous) {
920
+ _concatValue(value2, previous) {
921
921
  if (previous === this.defaultValue || !Array.isArray(previous)) {
922
- return [value];
922
+ return [value2];
923
923
  }
924
- return previous.concat(value);
924
+ return previous.concat(value2);
925
925
  }
926
926
  /**
927
927
  * Only allow option value to be one of choices.
@@ -1004,7 +1004,7 @@ var require_option = __commonJS({
1004
1004
  this.positiveOptions.set(option.attributeName(), option);
1005
1005
  }
1006
1006
  });
1007
- this.negativeOptions.forEach((value, key) => {
1007
+ this.negativeOptions.forEach((value2, key) => {
1008
1008
  if (this.positiveOptions.has(key)) {
1009
1009
  this.dualOptions.add(key);
1010
1010
  }
@@ -1017,12 +1017,12 @@ var require_option = __commonJS({
1017
1017
  * @param {Option} option
1018
1018
  * @returns {boolean}
1019
1019
  */
1020
- valueFromOption(value, option) {
1020
+ valueFromOption(value2, option) {
1021
1021
  const optionKey = option.attributeName();
1022
1022
  if (!this.dualOptions.has(optionKey)) return true;
1023
1023
  const preset = this.negativeOptions.get(optionKey).presetArg;
1024
1024
  const negativeValue = preset !== void 0 ? preset : false;
1025
- return option.negate === (negativeValue === value);
1025
+ return option.negate === (negativeValue === value2);
1026
1026
  }
1027
1027
  };
1028
1028
  function camelcase(str) {
@@ -1159,8 +1159,8 @@ var require_command = __commonJS({
1159
1159
  "node_modules/commander/lib/command.js"(exports2) {
1160
1160
  var EventEmitter = require("node:events").EventEmitter;
1161
1161
  var childProcess2 = require("node:child_process");
1162
- var path26 = require("node:path");
1163
- var fs30 = require("node:fs");
1162
+ var path27 = require("node:path");
1163
+ var fs31 = require("node:fs");
1164
1164
  var process16 = require("node:process");
1165
1165
  var { Argument: Argument2, humanReadableArgName } = require_argument();
1166
1166
  var { CommanderError: CommanderError2 } = require_error();
@@ -1650,9 +1650,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
1650
1650
  * @param {string} invalidArgumentMessage
1651
1651
  * @private
1652
1652
  */
1653
- _callParseArg(target, value, previous, invalidArgumentMessage) {
1653
+ _callParseArg(target, value2, previous, invalidArgumentMessage) {
1654
1654
  try {
1655
- return target.parseArg(value, previous);
1655
+ return target.parseArg(value2, previous);
1656
1656
  } catch (err) {
1657
1657
  if (err.code === "commander.invalidArgument") {
1658
1658
  const message = `${invalidArgumentMessage} ${err.message}`;
@@ -1937,8 +1937,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
1937
1937
  * @param {object} value
1938
1938
  * @return {Command} `this` command for chaining
1939
1939
  */
1940
- setOptionValue(key, value) {
1941
- return this.setOptionValueWithSource(key, value, void 0);
1940
+ setOptionValue(key, value2) {
1941
+ return this.setOptionValueWithSource(key, value2, void 0);
1942
1942
  }
1943
1943
  /**
1944
1944
  * Store option value and where the value came from.
@@ -1948,11 +1948,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1948
1948
  * @param {string} source - expected values are default/config/env/cli/implied
1949
1949
  * @return {Command} `this` command for chaining
1950
1950
  */
1951
- setOptionValueWithSource(key, value, source) {
1951
+ setOptionValueWithSource(key, value2, source) {
1952
1952
  if (this._storeOptionsAsProperties) {
1953
- this[key] = value;
1953
+ this[key] = value2;
1954
1954
  } else {
1955
- this._optionValues[key] = value;
1955
+ this._optionValues[key] = value2;
1956
1956
  }
1957
1957
  this._optionValueSources[key] = source;
1958
1958
  return this;
@@ -2141,7 +2141,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2141
2141
  * @param {string} subcommandName
2142
2142
  */
2143
2143
  _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
2144
- if (fs30.existsSync(executableFile)) return;
2144
+ if (fs31.existsSync(executableFile)) return;
2145
2145
  const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
2146
2146
  const executableMissing = `'${executableFile}' does not exist
2147
2147
  - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
@@ -2159,11 +2159,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
2159
2159
  let launchWithNode = false;
2160
2160
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2161
2161
  function findFile(baseDir, baseName) {
2162
- const localBin = path26.resolve(baseDir, baseName);
2163
- if (fs30.existsSync(localBin)) return localBin;
2164
- if (sourceExt.includes(path26.extname(baseName))) return void 0;
2162
+ const localBin = path27.resolve(baseDir, baseName);
2163
+ if (fs31.existsSync(localBin)) return localBin;
2164
+ if (sourceExt.includes(path27.extname(baseName))) return void 0;
2165
2165
  const foundExt = sourceExt.find(
2166
- (ext) => fs30.existsSync(`${localBin}${ext}`)
2166
+ (ext) => fs31.existsSync(`${localBin}${ext}`)
2167
2167
  );
2168
2168
  if (foundExt) return `${localBin}${foundExt}`;
2169
2169
  return void 0;
@@ -2175,21 +2175,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
2175
2175
  if (this._scriptPath) {
2176
2176
  let resolvedScriptPath;
2177
2177
  try {
2178
- resolvedScriptPath = fs30.realpathSync(this._scriptPath);
2178
+ resolvedScriptPath = fs31.realpathSync(this._scriptPath);
2179
2179
  } catch {
2180
2180
  resolvedScriptPath = this._scriptPath;
2181
2181
  }
2182
- executableDir = path26.resolve(
2183
- path26.dirname(resolvedScriptPath),
2182
+ executableDir = path27.resolve(
2183
+ path27.dirname(resolvedScriptPath),
2184
2184
  executableDir
2185
2185
  );
2186
2186
  }
2187
2187
  if (executableDir) {
2188
2188
  let localFile = findFile(executableDir, executableFile);
2189
2189
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2190
- const legacyName = path26.basename(
2190
+ const legacyName = path27.basename(
2191
2191
  this._scriptPath,
2192
- path26.extname(this._scriptPath)
2192
+ path27.extname(this._scriptPath)
2193
2193
  );
2194
2194
  if (legacyName !== this._name) {
2195
2195
  localFile = findFile(
@@ -2200,7 +2200,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2200
2200
  }
2201
2201
  executableFile = localFile || executableFile;
2202
2202
  }
2203
- launchWithNode = sourceExt.includes(path26.extname(executableFile));
2203
+ launchWithNode = sourceExt.includes(path27.extname(executableFile));
2204
2204
  let proc;
2205
2205
  if (process16.platform !== "win32") {
2206
2206
  if (launchWithNode) {
@@ -2335,13 +2335,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
2335
2335
  * @private
2336
2336
  */
2337
2337
  _processArguments() {
2338
- const myParseArg = (argument, value, previous) => {
2339
- let parsedValue = value;
2340
- if (value !== null && argument.parseArg) {
2341
- const invalidValueMessage = `error: command-argument value '${value}' is invalid for argument '${argument.name()}'.`;
2338
+ const myParseArg = (argument, value2, previous) => {
2339
+ let parsedValue = value2;
2340
+ if (value2 !== null && argument.parseArg) {
2341
+ const invalidValueMessage = `error: command-argument value '${value2}' is invalid for argument '${argument.name()}'.`;
2342
2342
  parsedValue = this._callParseArg(
2343
2343
  argument,
2344
- value,
2344
+ value2,
2345
2345
  previous,
2346
2346
  invalidValueMessage
2347
2347
  );
@@ -2351,25 +2351,25 @@ Expecting one of '${allowedValues.join("', '")}'`);
2351
2351
  this._checkNumberOfArguments();
2352
2352
  const processedArgs = [];
2353
2353
  this.registeredArguments.forEach((declaredArg, index) => {
2354
- let value = declaredArg.defaultValue;
2354
+ let value2 = declaredArg.defaultValue;
2355
2355
  if (declaredArg.variadic) {
2356
2356
  if (index < this.args.length) {
2357
- value = this.args.slice(index);
2357
+ value2 = this.args.slice(index);
2358
2358
  if (declaredArg.parseArg) {
2359
- value = value.reduce((processed, v) => {
2359
+ value2 = value2.reduce((processed, v) => {
2360
2360
  return myParseArg(declaredArg, v, processed);
2361
2361
  }, declaredArg.defaultValue);
2362
2362
  }
2363
- } else if (value === void 0) {
2364
- value = [];
2363
+ } else if (value2 === void 0) {
2364
+ value2 = [];
2365
2365
  }
2366
2366
  } else if (index < this.args.length) {
2367
- value = this.args[index];
2367
+ value2 = this.args[index];
2368
2368
  if (declaredArg.parseArg) {
2369
- value = myParseArg(declaredArg, value, declaredArg.defaultValue);
2369
+ value2 = myParseArg(declaredArg, value2, declaredArg.defaultValue);
2370
2370
  }
2371
2371
  }
2372
- processedArgs[index] = value;
2372
+ processedArgs[index] = value2;
2373
2373
  });
2374
2374
  this.processedArgs = processedArgs;
2375
2375
  }
@@ -2626,15 +2626,15 @@ Expecting one of '${allowedValues.join("', '")}'`);
2626
2626
  const option = this._findOption(arg);
2627
2627
  if (option) {
2628
2628
  if (option.required) {
2629
- const value = args.shift();
2630
- if (value === void 0) this.optionMissingArgument(option);
2631
- this.emit(`option:${option.name()}`, value);
2629
+ const value2 = args.shift();
2630
+ if (value2 === void 0) this.optionMissingArgument(option);
2631
+ this.emit(`option:${option.name()}`, value2);
2632
2632
  } else if (option.optional) {
2633
- let value = null;
2633
+ let value2 = null;
2634
2634
  if (args.length > 0 && !maybeOption(args[0])) {
2635
- value = args.shift();
2635
+ value2 = args.shift();
2636
2636
  }
2637
- this.emit(`option:${option.name()}`, value);
2637
+ this.emit(`option:${option.name()}`, value2);
2638
2638
  } else {
2639
2639
  this.emit(`option:${option.name()}`);
2640
2640
  }
@@ -3047,7 +3047,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3047
3047
  * @return {Command}
3048
3048
  */
3049
3049
  nameFromFilename(filename) {
3050
- this._name = path26.basename(filename, path26.extname(filename));
3050
+ this._name = path27.basename(filename, path27.extname(filename));
3051
3051
  return this;
3052
3052
  }
3053
3053
  /**
@@ -3061,9 +3061,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3061
3061
  * @param {string} [path]
3062
3062
  * @return {(string|null|Command)}
3063
3063
  */
3064
- executableDir(path27) {
3065
- if (path27 === void 0) return this._executableDir;
3066
- this._executableDir = path27;
3064
+ executableDir(path28) {
3065
+ if (path28 === void 0) return this._executableDir;
3066
+ this._executableDir = path28;
3067
3067
  return this;
3068
3068
  }
3069
3069
  /**
@@ -3427,12 +3427,12 @@ function assembleStyles() {
3427
3427
  green = Math.floor(remainder / 6) / 5;
3428
3428
  blue = remainder % 6 / 5;
3429
3429
  }
3430
- const value = Math.max(red, green, blue) * 2;
3431
- if (value === 0) {
3430
+ const value2 = Math.max(red, green, blue) * 2;
3431
+ if (value2 === 0) {
3432
3432
  return 30;
3433
3433
  }
3434
3434
  let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
3435
- if (value === 2) {
3435
+ if (value2 === 2) {
3436
3436
  result += 60;
3437
3437
  }
3438
3438
  return result;
@@ -6295,22 +6295,22 @@ var init_ora = __esm({
6295
6295
  get text() {
6296
6296
  return this.#text;
6297
6297
  }
6298
- set text(value = "") {
6299
- this.#text = value;
6298
+ set text(value2 = "") {
6299
+ this.#text = value2;
6300
6300
  this.#updateLineCount();
6301
6301
  }
6302
6302
  get prefixText() {
6303
6303
  return this.#prefixText;
6304
6304
  }
6305
- set prefixText(value = "") {
6306
- this.#prefixText = value;
6305
+ set prefixText(value2 = "") {
6306
+ this.#prefixText = value2;
6307
6307
  this.#updateLineCount();
6308
6308
  }
6309
6309
  get suffixText() {
6310
6310
  return this.#suffixText;
6311
6311
  }
6312
- set suffixText(value = "") {
6313
- this.#suffixText = value;
6312
+ set suffixText(value2 = "") {
6313
+ this.#suffixText = value2;
6314
6314
  this.#updateLineCount();
6315
6315
  }
6316
6316
  get isSpinning() {
@@ -6347,20 +6347,20 @@ var init_ora = __esm({
6347
6347
  get isEnabled() {
6348
6348
  return this.#isEnabled && !this.#isSilent;
6349
6349
  }
6350
- set isEnabled(value) {
6351
- if (typeof value !== "boolean") {
6350
+ set isEnabled(value2) {
6351
+ if (typeof value2 !== "boolean") {
6352
6352
  throw new TypeError("The `isEnabled` option must be a boolean");
6353
6353
  }
6354
- this.#isEnabled = value;
6354
+ this.#isEnabled = value2;
6355
6355
  }
6356
6356
  get isSilent() {
6357
6357
  return this.#isSilent;
6358
6358
  }
6359
- set isSilent(value) {
6360
- if (typeof value !== "boolean") {
6359
+ set isSilent(value2) {
6360
+ if (typeof value2 !== "boolean") {
6361
6361
  throw new TypeError("The `isSilent` option must be a boolean");
6362
6362
  }
6363
- this.#isSilent = value;
6363
+ this.#isSilent = value2;
6364
6364
  }
6365
6365
  frame() {
6366
6366
  const now = Date.now();
@@ -6613,7 +6613,7 @@ var init_wsl_utils = __esm({
6613
6613
 
6614
6614
  // node_modules/define-lazy-prop/index.js
6615
6615
  function defineLazyProperty(object, propertyName, valueGetter) {
6616
- const define = (value) => Object.defineProperty(object, propertyName, { value, enumerable: true, writable: true });
6616
+ const define = (value2) => Object.defineProperty(object, propertyName, { value: value2, enumerable: true, writable: true });
6617
6617
  Object.defineProperty(object, propertyName, {
6618
6618
  configurable: true,
6619
6619
  enumerable: true,
@@ -6622,8 +6622,8 @@ function defineLazyProperty(object, propertyName, valueGetter) {
6622
6622
  define(result);
6623
6623
  return result;
6624
6624
  },
6625
- set(value) {
6626
- define(value);
6625
+ set(value2) {
6626
+ define(value2);
6627
6627
  }
6628
6628
  });
6629
6629
  return object;
@@ -7092,8 +7092,8 @@ var import_node_crypto2 = require("node:crypto");
7092
7092
  var import_node_crypto = require("node:crypto");
7093
7093
  var invocationId = (0, import_node_crypto.randomUUID)();
7094
7094
  var identity;
7095
- function setClientIdentity(value) {
7096
- identity = { ...value, invocationId };
7095
+ function setClientIdentity(value2) {
7096
+ identity = { ...value2, invocationId };
7097
7097
  }
7098
7098
  function getClientIdentity() {
7099
7099
  return identity ? { ...identity } : void 0;
@@ -7124,7 +7124,7 @@ function extractErrorDetails(json) {
7124
7124
  if (!json) return void 0;
7125
7125
  const nested = json.error && typeof json.error === "object" && !Array.isArray(json.error) ? json.error : null;
7126
7126
  const code = typeof json.code === "string" ? json.code : typeof nested?.code === "string" ? nested.code : void 0;
7127
- for (const value of [json.details, json.meta, nested?.details]) if (value && typeof value === "object" && !Array.isArray(value)) return { ...value, ...code ? { code } : {} };
7127
+ for (const value2 of [json.details, json.meta, nested?.details]) if (value2 && typeof value2 === "object" && !Array.isArray(value2)) return { ...value2, ...code ? { code } : {} };
7128
7128
  const details = Object.fromEntries(["limit", "current", "max", "actual", "field", "code"].filter((key) => key in json).map((key) => [key, json[key]]));
7129
7129
  if (code) details.code = code;
7130
7130
  return Object.keys(details).length ? details : void 0;
@@ -7133,23 +7133,23 @@ var warnedAboutVersionSkew = false;
7133
7133
  function createHttpClient(creds, options) {
7134
7134
  const warn3 = options.warn ?? ((message) => process.stderr.write(message));
7135
7135
  function inspectVersion(headers) {
7136
- const value = headers.get(APEX_API_VERSION_HEADER);
7137
- if (!value || warnedAboutVersionSkew) return;
7138
- const version = Number(value);
7136
+ const value2 = headers.get(APEX_API_VERSION_HEADER);
7137
+ if (!value2 || warnedAboutVersionSkew) return;
7138
+ const version = Number(value2);
7139
7139
  if (!Number.isFinite(version) || version === Number(SUPPORTED_APEX_API_VERSION)) return;
7140
7140
  warnedAboutVersionSkew = true;
7141
7141
  warn3(version > 1 ? "CLI is behind the server \u2014 update.\n" : "Server API is older than this CLI supports.\n");
7142
7142
  }
7143
- async function request2(method, path26, body5, requestTimeoutMs, form = false, requestOptions) {
7143
+ async function request2(method, path27, body5, requestTimeoutMs, form = false, requestOptions) {
7144
7144
  const started2 = performance.now();
7145
7145
  const observe = (status4, requestId2, errorCode, responseHeaders) => {
7146
7146
  const semver = (name) => {
7147
- const value = responseHeaders?.get(name) ?? void 0;
7148
- return value && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(value) ? value : void 0;
7147
+ const value2 = responseHeaders?.get(name) ?? void 0;
7148
+ return value2 && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(value2) ? value2 : void 0;
7149
7149
  };
7150
7150
  const serverTiming = responseHeaders?.get("server-timing");
7151
7151
  try {
7152
- options.onResponse?.({ method, path: path26, status: status4, requestId: requestId2, errorCode: typeof errorCode === "string" ? errorCode : void 0, durationMs: Math.max(0, Math.round(performance.now() - started2)), serverTiming: serverTiming?.slice(0, 300) || void 0, cliLatest: semver("x-apex-cli-latest"), cliMinimum: semver("x-apex-cli-minimum") });
7152
+ options.onResponse?.({ method, path: path27, status: status4, requestId: requestId2, errorCode: typeof errorCode === "string" ? errorCode : void 0, durationMs: Math.max(0, Math.round(performance.now() - started2)), serverTiming: serverTiming?.slice(0, 300) || void 0, cliLatest: semver("x-apex-cli-latest"), cliMinimum: semver("x-apex-cli-minimum") });
7153
7153
  } catch {
7154
7154
  }
7155
7155
  };
@@ -7165,7 +7165,7 @@ function createHttpClient(creds, options) {
7165
7165
  if (creds.resolvedContext?.orgId) headers["X-Apex-Org"] = creds.resolvedContext.orgId;
7166
7166
  if (creds.resolvedContext?.shopId) headers["X-Apex-Shop"] = creds.resolvedContext.shopId;
7167
7167
  }
7168
- const mock = options.transport?.(method, path26, body5, headers);
7168
+ const mock = options.transport?.(method, path27, body5, headers);
7169
7169
  if (mock !== void 0) {
7170
7170
  const version = options.testTransportVersion?.();
7171
7171
  if (version) inspectVersion(new Headers({ [APEX_API_VERSION_HEADER]: version }));
@@ -7174,7 +7174,7 @@ function createHttpClient(creds, options) {
7174
7174
  }
7175
7175
  let response;
7176
7176
  try {
7177
- response = await fetch(`${creds.baseUrl}${path26}`, { method, headers, body: form ? body5 : body5 ? JSON.stringify(body5) : void 0, signal: AbortSignal.timeout(timeoutMs) });
7177
+ response = await fetch(`${creds.baseUrl}${path27}`, { method, headers, body: form ? body5 : body5 ? JSON.stringify(body5) : void 0, signal: AbortSignal.timeout(timeoutMs) });
7178
7178
  } catch (error) {
7179
7179
  const outer = error && typeof error === "object" ? error : void 0;
7180
7180
  const nested = outer?.cause && typeof outer.cause === "object" ? outer.cause : void 0;
@@ -7195,7 +7195,7 @@ function createHttpClient(creds, options) {
7195
7195
  if (!response.ok) return { ok: false, status: response.status, data: json, error: extractErrorMessage(json, response.status), errorDetails: extractErrorDetails(json), requestId, retryAfter: response.headers.get("retry-after"), rateLimit: { limit: response.headers.get("x-ratelimit-limit"), remaining: response.headers.get("x-ratelimit-remaining"), reset: response.headers.get("x-ratelimit-reset") } };
7196
7196
  return { ok: true, status: response.status, data: json };
7197
7197
  }
7198
- return { request: (method, path26, body5, timeoutMs, requestOptions) => request2(method, path26, body5, timeoutMs, false, requestOptions), requestForm: (method, path26, body5) => request2(method, path26, body5, void 0, true) };
7198
+ return { request: (method, path27, body5, timeoutMs, requestOptions) => request2(method, path27, body5, timeoutMs, false, requestOptions), requestForm: (method, path27, body5) => request2(method, path27, body5, void 0, true) };
7199
7199
  }
7200
7200
 
7201
7201
  // ../api-client/src/credentials.ts
@@ -7286,18 +7286,18 @@ function compareVersions(left, right) {
7286
7286
  }
7287
7287
  function readCache() {
7288
7288
  try {
7289
- const value = JSON.parse((0, import_node_fs2.readFileSync)(cachePath(), "utf8"));
7290
- return value && typeof value === "object" ? value : {};
7289
+ const value2 = JSON.parse((0, import_node_fs2.readFileSync)(cachePath(), "utf8"));
7290
+ return value2 && typeof value2 === "object" ? value2 : {};
7291
7291
  } catch {
7292
7292
  return {};
7293
7293
  }
7294
7294
  }
7295
7295
  function writeCache(patch, remove5 = []) {
7296
7296
  try {
7297
- const value = { ...readCache(), ...patch };
7298
- for (const key of remove5) delete value[key];
7297
+ const value2 = { ...readCache(), ...patch };
7298
+ for (const key of remove5) delete value2[key];
7299
7299
  (0, import_node_fs2.mkdirSync)(import_node_path2.default.dirname(cachePath()), { recursive: true });
7300
- (0, import_node_fs2.writeFileSync)(cachePath(), JSON.stringify(value), { mode: 384 });
7300
+ (0, import_node_fs2.writeFileSync)(cachePath(), JSON.stringify(value2), { mode: 384 });
7301
7301
  } catch {
7302
7302
  }
7303
7303
  }
@@ -7362,7 +7362,7 @@ function clearUpdateCache() {
7362
7362
  function currentUpdateState(current, minimumOverride) {
7363
7363
  if (minimumOverride && versionParts(minimumOverride))
7364
7364
  recordServerCliPolicy({ cliMinimum: minimumOverride });
7365
- const latest = [npmLatest, serverLatest, serverMinimum, current].filter((value) => Boolean(value)).reduce((highest, value) => compareVersions(value, highest) > 0 ? value : highest, current);
7365
+ const latest = [npmLatest, serverLatest, serverMinimum, current].filter((value2) => Boolean(value2)).reduce((highest, value2) => compareVersions(value2, highest) > 0 ? value2 : highest, current);
7366
7366
  const minimum = serverMinimum;
7367
7367
  const required = Boolean(minimum && compareVersions(current, minimum) < 0);
7368
7368
  if (!required && process.env.APEX_NO_UPDATE_CHECK === "1") return;
@@ -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.16.0",
7426
+ version: "0.18.0",
7427
7427
  license: "MIT",
7428
7428
  description: "Apex CLI for managing experiments, approved roadmap ideas, and draft workspaces.",
7429
7429
  private: false,
@@ -7771,8 +7771,8 @@ process.on?.("beforeExit", writePendingUpdateNotice);
7771
7771
  function minimumFromError(error, reason2) {
7772
7772
  if (reason2 !== "cli_upgrade_required") return;
7773
7773
  const api = error.api?.body;
7774
- const value = error.details?.minimum ?? api?.error?.details?.minimum;
7775
- return typeof value === "string" ? value : void 0;
7774
+ const value2 = error.details?.minimum ?? api?.error?.details?.minimum;
7775
+ return typeof value2 === "string" ? value2 : void 0;
7776
7776
  }
7777
7777
  async function handleCliError(err, command) {
7778
7778
  if (err && typeof err === "object" && "exitCode" in err && err.exitCode === 0) {
@@ -7907,6 +7907,13 @@ function errorNextSteps(error) {
7907
7907
  command: error.details.retry,
7908
7908
  why: "Review the requested action before retrying."
7909
7909
  });
7910
+ const body5 = error.api?.body;
7911
+ const next = body5?.error?.next ?? body5?.next;
7912
+ if (reason2 === "page_required")
7913
+ steps.push({
7914
+ command: "apex pages list",
7915
+ why: typeof next === "string" ? next : "Click goals need a page: pass --page-id <page ID> or --page <page ID or key> (list pages with apex pages list)"
7916
+ });
7910
7917
  const hints = error.details?.next_steps;
7911
7918
  if (Array.isArray(hints))
7912
7919
  for (const hint of hints) {
@@ -7920,6 +7927,8 @@ function errorNextSteps(error) {
7920
7927
  command: "apex experiments plan",
7921
7928
  why: "Correct the reported blocking issues, then validate the folder again."
7922
7929
  });
7930
+ if (steps.length === 0 && typeof next === "string" && next)
7931
+ steps.push({ command: "see next", why: next });
7923
7932
  return steps;
7924
7933
  }
7925
7934
 
@@ -8384,9 +8393,9 @@ var staticRouteSegments = /* @__PURE__ */ new Set([
8384
8393
  "webhooks",
8385
8394
  "workspace"
8386
8395
  ]);
8387
- function normalizeRoute(path26) {
8388
- if (!path26.startsWith("/api/")) return ":route";
8389
- const parts = path26.split(/[?#]/, 1)[0].split("/");
8396
+ function normalizeRoute(path27) {
8397
+ if (!path27.startsWith("/api/")) return ":route";
8398
+ const parts = path27.split(/[?#]/, 1)[0].split("/");
8390
8399
  const files = parts.indexOf("files");
8391
8400
  if (files !== -1 && parts.length > files + 1) parts.splice(files + 1, parts.length, ":id");
8392
8401
  return parts.map((part) => !part || staticRouteSegments.has(part) ? part : ":id").join("/");
@@ -8438,19 +8447,19 @@ function classifyError(exitCode, error, status4) {
8438
8447
  function redactAttempted(token) {
8439
8448
  return /^(?:apx_|sk_|pk_|rk_|ghp_|gho_|xox|Bearer)/.test(token) || /^[A-Za-z0-9_-]{24,}$/.test(token) || /:\/\/|[/\\@=]/.test(token) || token.length > 40 ? "(redacted)" : token;
8440
8449
  }
8441
- function normalizeErrorCode(value, exitCode) {
8442
- const normalized = value.toLowerCase();
8450
+ function normalizeErrorCode(value2, exitCode) {
8451
+ const normalized = value2.toLowerCase();
8443
8452
  return normalized.length <= 64 && /^[a-z0-9._-]+$/.test(normalized) ? normalized : `exit_${exitCode}`;
8444
8453
  }
8445
8454
  function invocationErrorCode(exitCode, error, commanderExit) {
8446
8455
  if (exitCode === 0) return void 0;
8447
- let value = firstError?.errorCode;
8448
- if (!value && firstError)
8449
- value = firstError.status === 410 ? "gone" : `http_${firstError.status}`;
8456
+ let value2 = firstError?.errorCode;
8457
+ if (!value2 && firstError)
8458
+ value2 = firstError.status === 410 ? "gone" : `http_${firstError.status}`;
8450
8459
  const detail5 = error && typeof error === "object" ? error : {};
8451
- if (!value && typeof detail5.details?.reason === "string" && !detail5.api) {
8460
+ if (!value2 && typeof detail5.details?.reason === "string" && !detail5.api) {
8452
8461
  const reason2 = detail5.details.reason.toLowerCase();
8453
- value = [
8462
+ value2 = [
8454
8463
  "network",
8455
8464
  "dns",
8456
8465
  "timeout",
@@ -8461,20 +8470,20 @@ function invocationErrorCode(exitCode, error, commanderExit) {
8461
8470
  "unknown"
8462
8471
  ].includes(reason2) ? `network_${reason2}` : reason2 || void 0;
8463
8472
  }
8464
- if (!value && commanderExit && typeof detail5.code === "string")
8465
- value = detail5.code.slice("commander.".length).replace(/([a-z])([A-Z])/g, "$1_$2");
8466
- if (!value) {
8473
+ if (!value2 && commanderExit && typeof detail5.code === "string")
8474
+ value2 = detail5.code.slice("commander.".length).replace(/([a-z])([A-Z])/g, "$1_$2");
8475
+ if (!value2) {
8467
8476
  const apiCode = detail5.api?.body?.code ?? detail5.api?.body?.error?.code ?? detail5.details?.code;
8468
- if (typeof apiCode === "string" && apiCode) value = apiCode;
8469
- else if (detail5.api?.status === 401 || exitCode === 2) value = "auth_required";
8470
- else if (detail5.api?.status === 403) value = "auth_forbidden";
8471
- else if (detail5.api?.status === 404) value = "not_found";
8472
- else if (detail5.api?.status === 429) value = "rate_limited";
8473
- else if (detail5.api?.status === 409 || exitCode === 5) value = "conflict";
8474
- else if (exitCode === 4) value = "confirmation_required";
8475
- else if (exitCode === 3) value = "validation_failed";
8477
+ if (typeof apiCode === "string" && apiCode) value2 = apiCode;
8478
+ else if (detail5.api?.status === 401 || exitCode === 2) value2 = "auth_required";
8479
+ else if (detail5.api?.status === 403) value2 = "auth_forbidden";
8480
+ else if (detail5.api?.status === 404) value2 = "not_found";
8481
+ else if (detail5.api?.status === 429) value2 = "rate_limited";
8482
+ else if (detail5.api?.status === 409 || exitCode === 5) value2 = "conflict";
8483
+ else if (exitCode === 4) value2 = "confirmation_required";
8484
+ else if (exitCode === 3) value2 = "validation_failed";
8476
8485
  }
8477
- return normalizeErrorCode(value ?? `exit_${exitCode}`, exitCode);
8486
+ return normalizeErrorCode(value2 ?? `exit_${exitCode}`, exitCode);
8478
8487
  }
8479
8488
  async function finishInvocation({
8480
8489
  exitCode: rawExitCode,
@@ -8563,8 +8572,8 @@ function pagePath(id, action78) {
8563
8572
  return `/api/v1/landing-pages/${encodeURIComponent(id)}${action78 ? `/${action78}` : ""}`;
8564
8573
  }
8565
8574
  function landingPagesApi(request2) {
8566
- const call = async (method, path26, body5) => {
8567
- const response = await request2(method, path26, body5);
8575
+ const call = async (method, path27, body5) => {
8576
+ const response = await request2(method, path27, body5);
8568
8577
  return { ...response, data: response.data?.data };
8569
8578
  };
8570
8579
  return {
@@ -8650,6 +8659,19 @@ function validateLandingPageLocalFiles(files) {
8650
8659
  }
8651
8660
 
8652
8661
  // src/api.ts
8662
+ function lookupCommerceCatalog(creds, params) {
8663
+ const query = new URLSearchParams();
8664
+ if (params.query) query.set("q", params.query);
8665
+ if (params.handle) query.set("handle", params.handle);
8666
+ if (params.product_id) query.set("product_id", params.product_id);
8667
+ if (params.cursor) query.set("cursor", params.cursor);
8668
+ if (params.limit !== void 0) query.set("limit", String(params.limit));
8669
+ return request(
8670
+ creds,
8671
+ "GET",
8672
+ `/api/v1/commerce/catalog${query.size ? `?${query}` : ""}`
8673
+ );
8674
+ }
8653
8675
  function simulateTriggers(creds, body5) {
8654
8676
  return request(creds, "POST", "/api/v1/tracking/simulate-trigger", body5);
8655
8677
  }
@@ -8673,11 +8695,11 @@ function getUserKeyIdentity(creds) {
8673
8695
  function getAccountContext(creds) {
8674
8696
  return userRequest(creds, "GET", "/api/v1/me/context");
8675
8697
  }
8676
- function userRequest(creds, method, path26, body5) {
8698
+ function userRequest(creds, method, path27, body5) {
8677
8699
  return request(
8678
8700
  { ...creds, apiKey: creds.userApiKey ?? "", baseUrl: creds.userBaseUrl ?? creds.baseUrl },
8679
8701
  method,
8680
- path26,
8702
+ path27,
8681
8703
  body5
8682
8704
  );
8683
8705
  }
@@ -8763,10 +8785,10 @@ function sharedClient(creds) {
8763
8785
  }
8764
8786
  return client;
8765
8787
  }
8766
- async function request(creds, method, path26, body5, requestTimeoutMs, options) {
8788
+ async function request(creds, method, path27, body5, requestTimeoutMs, options) {
8767
8789
  const response = await sharedClient(creds).request(
8768
8790
  method,
8769
- path26,
8791
+ path27,
8770
8792
  body5,
8771
8793
  requestTimeoutMs,
8772
8794
  options
@@ -8774,8 +8796,8 @@ async function request(creds, method, path26, body5, requestTimeoutMs, options)
8774
8796
  recordApiResponse(response);
8775
8797
  return response;
8776
8798
  }
8777
- async function requestForm(creds, method, path26, body5) {
8778
- const response = await sharedClient(creds).requestForm(method, path26, body5);
8799
+ async function requestForm(creds, method, path27, body5) {
8800
+ const response = await sharedClient(creds).requestForm(method, path27, body5);
8779
8801
  recordApiResponse(response);
8780
8802
  return response;
8781
8803
  }
@@ -9299,6 +9321,17 @@ function getAdminQuarantine(creds) {
9299
9321
  "/api/v1/admin/tracking/quarantine"
9300
9322
  );
9301
9323
  }
9324
+ function getAdminShopSettings(creds, shopRef) {
9325
+ return request(
9326
+ userCredentials(creds),
9327
+ "GET",
9328
+ `/api/v1/admin/shops/${encodeURIComponent(shopRef)}/settings`
9329
+ );
9330
+ }
9331
+ function setAdminShopSetting(creds, shopId, body5) {
9332
+ const pinned = { ...userCredentials(creds), resolvedContext: { shopId } };
9333
+ return request(pinned, "PATCH", `/api/v1/admin/shops/${encodeURIComponent(shopId)}/settings`, body5);
9334
+ }
9302
9335
  function archiveAdminShop(creds, shopId) {
9303
9336
  return request(
9304
9337
  userCredentials(creds),
@@ -9318,8 +9351,8 @@ function requestTargetingResource(creds, method, kind5, id, body5, limit, cursor
9318
9351
  if (limit) params.set("limit", String(limit));
9319
9352
  if (cursor) params.set("cursor", cursor);
9320
9353
  const query = params.toString();
9321
- const path26 = `/api/v1/${kind5}${id ? `/${id.split("/").map(encodeURIComponent).join("/")}` : ""}${query ? `?${query}` : ""}`;
9322
- return request(creds, method, path26, body5);
9354
+ const path27 = `/api/v1/${kind5}${id ? `/${id.split("/").map(encodeURIComponent).join("/")}` : ""}${query ? `?${query}` : ""}`;
9355
+ return request(creds, method, path27, body5);
9323
9356
  }
9324
9357
  function managedPath(kind5, id, action78) {
9325
9358
  return `/api/v1/${kind5}${id ? `/${encodeURIComponent(id)}` : ""}${action78 ? `/${action78}` : ""}`;
@@ -9355,7 +9388,7 @@ function getConfigResource(creds, family, id) {
9355
9388
  }
9356
9389
  function landingPages(creds) {
9357
9390
  return landingPagesApi(
9358
- (method, path26, body5) => request(creds, method, path26, body5)
9391
+ (method, path27, body5) => request(creds, method, path27, body5)
9359
9392
  );
9360
9393
  }
9361
9394
  function deleteAsset(creds, id) {
@@ -9597,29 +9630,36 @@ function moveAdminCustomerStores(creds, orgId, body5) {
9597
9630
  body5
9598
9631
  );
9599
9632
  }
9633
+ function getExperimentResultsByChannel(creds, id) {
9634
+ return request(
9635
+ creds,
9636
+ "GET",
9637
+ `/api/v1/experiments/${encodeURIComponent(id)}/results/by-channel`
9638
+ );
9639
+ }
9600
9640
 
9601
9641
  // src/context.ts
9602
9642
  var import_node_readline = __toESM(require("node:readline"));
9603
- function match(value, rows, kind5) {
9604
- const byId = rows.find((row) => row.id === value);
9643
+ function match(value2, rows, kind5) {
9644
+ const byId = rows.find((row) => row.id === value2);
9605
9645
  if (byId) return byId;
9606
- const exact = rows.filter((row) => row.name.toLowerCase() === value.toLowerCase());
9646
+ const exact = rows.filter((row) => row.name.toLowerCase() === value2.toLowerCase());
9607
9647
  if (exact.length === 1) return exact[0];
9608
9648
  if (exact.length > 1)
9609
9649
  throw new CliError(
9610
- `Ambiguous ${kind5} "${value}". Candidates: ${exact.map((row) => `${row.name} (${row.id})`).join(", ")}`,
9650
+ `Ambiguous ${kind5} "${value2}". Candidates: ${exact.map((row) => `${row.name} (${row.id})`).join(", ")}`,
9611
9651
  EXIT_CODES.validation,
9612
9652
  { code: "ambiguous_context", candidates: exact.map(({ id, name }) => ({ id, name })) }
9613
9653
  );
9614
- const candidates = rows.filter((row) => row.name.toLowerCase().startsWith(value.toLowerCase()));
9654
+ const candidates = rows.filter((row) => row.name.toLowerCase().startsWith(value2.toLowerCase()));
9615
9655
  if (candidates.length === 1) return candidates[0];
9616
9656
  if (candidates.length > 1)
9617
9657
  throw new CliError(
9618
- `Ambiguous ${kind5} "${value}". Candidates: ${candidates.map((row) => `${row.name} (${row.id})`).join(", ")}`,
9658
+ `Ambiguous ${kind5} "${value2}". Candidates: ${candidates.map((row) => `${row.name} (${row.id})`).join(", ")}`,
9619
9659
  EXIT_CODES.validation,
9620
9660
  { code: "ambiguous_context", candidates: candidates.map(({ id, name }) => ({ id, name })) }
9621
9661
  );
9622
- throw new CliError(`Unknown ${kind5} "${value}".`, EXIT_CODES.validation);
9662
+ throw new CliError(`Unknown ${kind5} "${value2}".`, EXIT_CODES.validation);
9623
9663
  }
9624
9664
  function resolveSelection(tree, input) {
9625
9665
  const [orgValue, shopValue, extra] = input.split("/");
@@ -10188,12 +10228,12 @@ var evidenceKinds = [
10188
10228
  ];
10189
10229
 
10190
10230
  // src/lib/experiment-handover.ts
10191
- function summarizeHandoverQa(value) {
10192
- const record4 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2) ? value2 : {};
10193
- const qa3 = record4(value);
10194
- const run3 = record4(qa3.latestRun);
10195
- const summary = record4(run3.summary);
10196
- const count2 = (value2) => typeof value2 === "number" && Number.isInteger(value2) && value2 >= 0 ? value2 : null;
10231
+ function summarizeHandoverQa(value2) {
10232
+ const record4 = (value3) => value3 !== null && typeof value3 === "object" && !Array.isArray(value3) ? value3 : {};
10233
+ const qa3 = record4(value2);
10234
+ const run4 = record4(qa3.latestRun);
10235
+ const summary = record4(run4.summary);
10236
+ const count2 = (value3) => typeof value3 === "number" && Number.isInteger(value3) && value3 >= 0 ? value3 : null;
10197
10237
  let requiredDone = count2(summary.completedRequired);
10198
10238
  let requiredTotal = count2(summary.required);
10199
10239
  if (requiredDone !== null && requiredTotal !== null && requiredDone > requiredTotal) {
@@ -10203,7 +10243,7 @@ function summarizeHandoverQa(value) {
10203
10243
  const blockers = Array.isArray(summary.blockers) && summary.blockers.every((item) => typeof item === "string") ? summary.blockers : null;
10204
10244
  const approvedAt = typeof qa3.qaApprovedAt === "string" ? Date.parse(qa3.qaApprovedAt) : Number.NaN;
10205
10245
  return {
10206
- run_status: typeof run3.status === "string" ? run3.status : null,
10246
+ run_status: typeof run4.status === "string" ? run4.status : null,
10207
10247
  required_done: requiredDone,
10208
10248
  required_total: requiredTotal,
10209
10249
  blockers,
@@ -10276,13 +10316,13 @@ function deviceLabel(targeting) {
10276
10316
  var ALL_PAGES_LABEL = "All pages";
10277
10317
  var CHECKOUT_LABEL = "Checkout";
10278
10318
  var UNKNOWN_PAGE_NAME = "Saved Page";
10279
- function isRecord(value) {
10280
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
10319
+ function isRecord(value2) {
10320
+ return Boolean(value2 && typeof value2 === "object" && !Array.isArray(value2));
10281
10321
  }
10282
- function arrayOf(value) {
10283
- if (Array.isArray(value)) return value;
10284
- if (value === void 0 || value === null) return [];
10285
- return [value];
10322
+ function arrayOf(value2) {
10323
+ if (Array.isArray(value2)) return value2;
10324
+ if (value2 === void 0 || value2 === null) return [];
10325
+ return [value2];
10286
10326
  }
10287
10327
  function ruleKey(rule) {
10288
10328
  if (typeof rule === "string") return JSON.stringify(["include", "simple", rule.trim(), ""]);
@@ -10360,11 +10400,11 @@ function classifyIdeaReference(reference) {
10360
10400
 
10361
10401
  // ../api-client/src/experiments.ts
10362
10402
  var ID_REFERENCE = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[1-9][0-9]{9})$/i;
10363
- function isExperimentTestKey(value) {
10364
- return /^([A-Z0-9]{2,8})-(\d+)$/.test(value);
10403
+ function isExperimentTestKey(value2) {
10404
+ return /^([A-Z0-9]{2,8})-(\d+)$/.test(value2);
10365
10405
  }
10366
- function record(value) {
10367
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
10406
+ function record(value2) {
10407
+ return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : {};
10368
10408
  }
10369
10409
  async function resolveExperimentReference(request2, idOrSlug, variationRef) {
10370
10410
  const explicitSlug = idOrSlug.startsWith("slug:");
@@ -10448,9 +10488,9 @@ function summarizeResults(payload) {
10448
10488
  degraded: Object.values(degradedFlags).some(Boolean),
10449
10489
  degradedFlags,
10450
10490
  missingMetrics: [...missing],
10451
- warnings: Array.isArray(data.warnings) ? data.warnings.map((value) => ({
10452
- message: record(value).message,
10453
- value
10491
+ warnings: Array.isArray(data.warnings) ? data.warnings.map((value2) => ({
10492
+ message: record(value2).message,
10493
+ value: value2
10454
10494
  })) : [],
10455
10495
  metrics: Array.isArray(data.metrics) ? data.metrics.map(record) : [],
10456
10496
  resultsWeekdays: data.results_weekdays,
@@ -10502,7 +10542,7 @@ function contentHash(bytes) {
10502
10542
  return import_node_crypto5.default.createHash("sha256").update(bytes).digest("hex");
10503
10543
  }
10504
10544
  function deliveryUrl(baseUrl, asset) {
10505
- const returnedUrl = asset.deliveryUrl ?? asset.delivery_url ?? asset.url;
10545
+ const returnedUrl = asset.delivery_url ?? asset.deliveryUrl ?? asset.url;
10506
10546
  if (returnedUrl) return returnedUrl;
10507
10547
  const origin = new URL(baseUrl).origin;
10508
10548
  return `${origin}/api/assets/${encodeURIComponent(asset.id)}/file`;
@@ -10514,25 +10554,25 @@ function extensionForMimeType(mimeType) {
10514
10554
  const safeSubtype = subtype?.replace(/[^a-z0-9.+-]/g, "");
10515
10555
  return safeSubtype ? `.${safeSubtype}` : ".bin";
10516
10556
  }
10517
- function dataUriFromMatch(value, match2) {
10557
+ function dataUriFromMatch(value2, match2) {
10518
10558
  let uri = match2[0];
10519
10559
  const start = match2.index ?? 0;
10520
- if (/url\(\s*$/i.test(value.slice(0, start))) {
10560
+ if (/url\(\s*$/i.test(value2.slice(0, start))) {
10521
10561
  const closingParenthesis = uri.indexOf(")");
10522
10562
  if (closingParenthesis >= 0) uri = uri.slice(0, closingParenthesis);
10523
10563
  }
10524
10564
  return uri;
10525
10565
  }
10526
- function surroundingNonWhitespaceNonQuoteRun(value, start, end) {
10527
- const before = value.slice(0, start).match(/[^\s"']*$/)?.[0] ?? "";
10528
- const after = value.slice(end).match(/^[^\s"']*/)?.[0] ?? "";
10529
- return `${before}${value.slice(start, end)}${after}`;
10566
+ function surroundingNonWhitespaceNonQuoteRun(value2, start, end) {
10567
+ const before = value2.slice(0, start).match(/[^\s"']*$/)?.[0] ?? "";
10568
+ const after = value2.slice(end).match(/^[^\s"']*/)?.[0] ?? "";
10569
+ return `${before}${value2.slice(start, end)}${after}`;
10530
10570
  }
10531
10571
  function isInsideRange(start, end, ranges) {
10532
10572
  return ranges.some((range) => start >= range.start && end <= range.end);
10533
10573
  }
10534
- function applyReplacements(value, replacements) {
10535
- let rewritten = value;
10574
+ function applyReplacements(value2, replacements) {
10575
+ let rewritten = value2;
10536
10576
  for (const replacement of replacements.sort((a, b) => b.start - a.start)) {
10537
10577
  rewritten = rewritten.slice(0, replacement.start) + replacement.value + rewritten.slice(replacement.end);
10538
10578
  }
@@ -10563,9 +10603,9 @@ function createAssetReferenceRewriter(options) {
10563
10603
  }
10564
10604
  return pending;
10565
10605
  };
10566
- const rewrite = async (value, baseDir = options.baseDir) => {
10567
- if (options.enabled === false || !value) return value;
10568
- let rewritten = value;
10606
+ const rewrite = async (value2, baseDir = options.baseDir) => {
10607
+ if (options.enabled === false || !value2) return value2;
10608
+ let rewritten = value2;
10569
10609
  const inlineMatches = [...rewritten.matchAll(DATA_IMAGE_URI_PATTERN)].map((match2) => ({ match: match2, uri: dataUriFromMatch(rewritten, match2) })).filter(({ uri }) => uri.length > INLINE_DATA_URI_MAX_CHARS);
10570
10610
  for (const { uri } of inlineMatches) {
10571
10611
  const commaIndex = uri.indexOf(",");
@@ -10640,26 +10680,134 @@ function createAssetReferenceRewriter(options) {
10640
10680
  }
10641
10681
  return applyReplacements(rewritten, bareReplacements);
10642
10682
  };
10643
- const rewriteUnknown = async (value, baseDir = options.baseDir) => {
10644
- if (typeof value === "string") return rewrite(value, baseDir);
10645
- if (Array.isArray(value)) {
10646
- return Promise.all(value.map((entry) => rewriteUnknown(entry, baseDir)));
10683
+ const rewriteUnknown = async (value2, baseDir = options.baseDir) => {
10684
+ if (typeof value2 === "string") return rewrite(value2, baseDir);
10685
+ if (Array.isArray(value2)) {
10686
+ return Promise.all(value2.map((entry) => rewriteUnknown(entry, baseDir)));
10647
10687
  }
10648
- if (value !== null && typeof value === "object") {
10688
+ if (value2 !== null && typeof value2 === "object") {
10649
10689
  return Object.fromEntries(
10650
10690
  await Promise.all(
10651
- Object.entries(value).map(async ([key, entry]) => [
10691
+ Object.entries(value2).map(async ([key, entry]) => [
10652
10692
  key,
10653
10693
  await rewriteUnknown(entry, baseDir)
10654
10694
  ])
10655
10695
  )
10656
10696
  );
10657
10697
  }
10658
- return value;
10698
+ return value2;
10659
10699
  };
10660
10700
  return { rewrite, rewriteUnknown };
10661
10701
  }
10662
10702
 
10703
+ // src/lib/argv-encoding.ts
10704
+ var cp1252Characters = /* @__PURE__ */ new Map([
10705
+ [8364, 128],
10706
+ [8218, 130],
10707
+ [402, 131],
10708
+ [8222, 132],
10709
+ [8230, 133],
10710
+ [8224, 134],
10711
+ [8225, 135],
10712
+ [710, 136],
10713
+ [8240, 137],
10714
+ [352, 138],
10715
+ [8249, 139],
10716
+ [338, 140],
10717
+ [381, 142],
10718
+ [8216, 145],
10719
+ [8217, 146],
10720
+ [8220, 147],
10721
+ [8221, 148],
10722
+ [8226, 149],
10723
+ [8211, 150],
10724
+ [8212, 151],
10725
+ [732, 152],
10726
+ [8482, 153],
10727
+ [353, 154],
10728
+ [8250, 155],
10729
+ [339, 156],
10730
+ [382, 158],
10731
+ [376, 159]
10732
+ ]);
10733
+ function repairCp1252Mojibake(value2) {
10734
+ if (!/[^\x00-\x7f]/.test(value2) || value2.includes("\uFFFD")) return null;
10735
+ const bytes = [];
10736
+ for (const character of value2) {
10737
+ const codePoint = character.codePointAt(0);
10738
+ const byte = codePoint <= 255 ? codePoint : cp1252Characters.get(codePoint);
10739
+ if (byte === void 0) return null;
10740
+ bytes.push(byte);
10741
+ }
10742
+ try {
10743
+ const repaired = new TextDecoder("utf-8", { fatal: true }).decode(new Uint8Array(bytes));
10744
+ return /[^\x00-\x7f]/.test(repaired) ? repaired : null;
10745
+ } catch {
10746
+ return null;
10747
+ }
10748
+ }
10749
+ function argumentValues(argv) {
10750
+ let precedingOption;
10751
+ return argv.slice(2).map((token, offset) => {
10752
+ const index = offset + 2;
10753
+ const equals = token.startsWith("--") ? token.indexOf("=") : -1;
10754
+ if (token.startsWith("-")) {
10755
+ precedingOption = equals === -1 ? token : token.slice(0, equals);
10756
+ }
10757
+ return {
10758
+ index,
10759
+ option: precedingOption ?? `argument ${offset + 1}`,
10760
+ prefix: equals === -1 ? "" : token.slice(0, equals + 1),
10761
+ value: equals === -1 ? token : token.slice(equals + 1)
10762
+ };
10763
+ });
10764
+ }
10765
+ function repairArgv(argv, options) {
10766
+ const active = options.env.APEX_ARGV_ENCODING === "repair" || options.platform === "win32" && options.env.APEX_ARGV_ENCODING !== "off";
10767
+ if (!active) return { argv, repairs: [] };
10768
+ const repairedArgv = [...argv];
10769
+ const repairs = [];
10770
+ for (const { index, option, prefix, value: value2 } of argumentValues(argv)) {
10771
+ const repaired = repairCp1252Mojibake(value2);
10772
+ if (repaired !== null) {
10773
+ repairedArgv[index] = prefix + repaired;
10774
+ repairs.push({ index, option });
10775
+ }
10776
+ }
10777
+ return { argv: repairedArgv, repairs };
10778
+ }
10779
+ function replacementCharacterOptions(argv) {
10780
+ return argumentValues(argv).filter(({ value: value2 }) => value2.includes("\uFFFD")).map(({ option }) => option);
10781
+ }
10782
+ function replacementCharacterFields(record4, fields3) {
10783
+ const paths = [];
10784
+ function visit2(value2, parts, path27) {
10785
+ if (parts.length === 0) {
10786
+ if (typeof value2 === "string" && value2.includes("\uFFFD")) paths.push(path27);
10787
+ return;
10788
+ }
10789
+ if (value2 === null || typeof value2 !== "object") return;
10790
+ const [part, ...rest] = parts;
10791
+ const array = part.endsWith("[]");
10792
+ const key = array ? part.slice(0, -2) : part;
10793
+ const next = value2[key];
10794
+ const nextPath = path27 ? `${path27}.${key}` : key;
10795
+ if (array) {
10796
+ if (Array.isArray(next)) {
10797
+ next.forEach((item, index) => visit2(item, rest, `${nextPath}[${index}]`));
10798
+ }
10799
+ } else visit2(next, rest, nextPath);
10800
+ }
10801
+ for (const field of fields3) visit2(record4, field.split("."), "");
10802
+ return paths;
10803
+ }
10804
+ function warnReplacementCharacters(label, record4, fields3) {
10805
+ for (const path27 of replacementCharacterFields(record4, fields3)) {
10806
+ process.stderr.write(`apex: stored name contains U+FFFD: ${label}.${path27}
10807
+ `);
10808
+ }
10809
+ }
10810
+
10663
10811
  // src/generated/experiment-filesystem.schema.json
10664
10812
  var experiment_filesystem_schema_default = {
10665
10813
  $id: "https://api.drip-apex.com/schemas/experiment-filesystem-v1.json",
@@ -12862,27 +13010,27 @@ function printExperiment(exp, dashboardUrl = null) {
12862
13010
  }
12863
13011
  console.log();
12864
13012
  }
12865
- function formatPct(value, digits = 1) {
12866
- return typeof value === "number" && Number.isFinite(value) ? `${value.toFixed(digits)}%` : "-";
13013
+ function formatPct(value2, digits = 1) {
13014
+ return typeof value2 === "number" && Number.isFinite(value2) ? `${value2.toFixed(digits)}%` : "-";
12867
13015
  }
12868
- function confidenceBasisLabel(value) {
12869
- if (value === "fixed_horizon") return "fixed-horizon";
12870
- if (value === "sequential") return "always-valid";
13016
+ function confidenceBasisLabel(value2) {
13017
+ if (value2 === "fixed_horizon") return "fixed-horizon";
13018
+ if (value2 === "sequential") return "always-valid";
12871
13019
  return "unknown";
12872
13020
  }
12873
- function statisticsStateLabel(value) {
12874
- return value === "untrustworthy" ? "verdict withheld" : String(value ?? "unknown");
13021
+ function statisticsStateLabel(value2) {
13022
+ return value2 === "untrustworthy" ? "verdict withheld" : String(value2 ?? "unknown");
12875
13023
  }
12876
- function formatRevenue(value, currency) {
12877
- if (value === null || value === void 0) return "\u2014";
12878
- if (!currency || typeof value !== "number" || !Number.isFinite(value)) return String(value);
13024
+ function formatRevenue(value2, currency) {
13025
+ if (value2 === null || value2 === void 0) return "\u2014";
13026
+ if (!currency || typeof value2 !== "number" || !Number.isFinite(value2)) return String(value2);
12879
13027
  try {
12880
13028
  return new Intl.NumberFormat("en-US", {
12881
13029
  style: "currency",
12882
13030
  currency: currency.trim().toUpperCase()
12883
- }).format(value);
13031
+ }).format(value2);
12884
13032
  } catch {
12885
- return String(value);
13033
+ return String(value2);
12886
13034
  }
12887
13035
  }
12888
13036
  function audienceLine(segmentApplied) {
@@ -12971,8 +13119,8 @@ function printExperimentResults(results2, currency) {
12971
13119
  const deltas = arm.deltaVsControl;
12972
13120
  const cm = deltas?.cm;
12973
13121
  const ci = cm?.ci95;
12974
- const count2 = (value) => typeof value === "number" && Number.isFinite(value) ? String(value) : "-";
12975
- const money = (value) => typeof value === "number" ? formatRevenue(value, String(plane.currency ?? currency ?? "")) : "-";
13122
+ const count2 = (value2) => typeof value2 === "number" && Number.isFinite(value2) ? String(value2) : "-";
13123
+ const money = (value2) => typeof value2 === "number" ? formatRevenue(value2, String(plane.currency ?? currency ?? "")) : "-";
12976
13124
  return [
12977
13125
  String(arm.name ?? arm.variationId ?? "-"),
12978
13126
  // Fail closed: a missing count is unavailable, never a measured 0.
@@ -13025,13 +13173,13 @@ var EXPERIMENT_FILESYSTEM_PATH_SCHEMA = {
13025
13173
  variation: filesystemPaths.filter((entry) => entry.startsWith("variations/*/")).map((entry) => entry.slice("variations/*/".length))
13026
13174
  };
13027
13175
  var SERVER_OWNED_EXPERIMENT_FILES = /* @__PURE__ */ new Set(["qa.json", "launch.json"]);
13028
- var escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13176
+ var escapeRegex = (value2) => value2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13029
13177
  var pathAlternation = (paths) => paths.map(escapeRegex).join("|");
13030
13178
  var FILESYSTEM_FILE_RE = new RegExp(
13031
13179
  `^(?:${pathAlternation(EXPERIMENT_FILESYSTEM_PATH_SCHEMA.topLevel)}|experiment/(?:${pathAlternation(EXPERIMENT_FILESYSTEM_PATH_SCHEMA.experiment)})|commerce/(?:${pathAlternation(EXPERIMENT_FILESYSTEM_PATH_SCHEMA.commerce)})|variations/[^/]+/(?:${pathAlternation(EXPERIMENT_FILESYSTEM_PATH_SCHEMA.variation)}))$`
13032
13180
  );
13033
- function sha256Text(value) {
13034
- return import_node_crypto6.default.createHash("sha256").update(value).digest("hex");
13181
+ function sha256Text(value2) {
13182
+ return import_node_crypto6.default.createHash("sha256").update(value2).digest("hex");
13035
13183
  }
13036
13184
  function folderPath(folder) {
13037
13185
  return import_node_path6.default.resolve(process.cwd(), folder);
@@ -13216,6 +13364,19 @@ function printFilesystemPlan(plan2) {
13216
13364
  console.log(` - [${issue.code}] ${issue.message}${context ? ` (${context})` : ""}`);
13217
13365
  });
13218
13366
  }
13367
+ if ((plan2.commerceLaunchPreview?.length ?? 0) > 0) {
13368
+ console.log(source_default.bold("\nLaunch preview"));
13369
+ for (const preview2 of plan2.commerceLaunchPreview ?? []) {
13370
+ console.log(` ${preview2.kind} (${preview2.path})`);
13371
+ for (const issue of preview2.issues) {
13372
+ const variation = issue.variation ? ` (variation: ${issue.variation})` : "";
13373
+ console.log(source_default.yellow(` - [${issue.code}] ${issue.message}${variation}`));
13374
+ }
13375
+ console.log(` Checked at plan: ${preview2.checked.join(", ") || "none"}`);
13376
+ for (const item of preview2.launchOnly)
13377
+ console.log(` Checked at ${item.when ?? "launch"}: ${item.check}: ${item.reason}`);
13378
+ }
13379
+ }
13219
13380
  console.log(source_default.bold("\nChanges"));
13220
13381
  if (plan2.changes.length === 0) {
13221
13382
  console.log(source_default.dim(" No changes."));
@@ -13299,38 +13460,38 @@ function printQaPreviewUrls(experimentId, result) {
13299
13460
  );
13300
13461
  }
13301
13462
  }
13302
- function qaVersionMarker(value) {
13303
- const version = value;
13463
+ function qaVersionMarker(value2) {
13464
+ const version = value2;
13304
13465
  return version.stale ? " [stale: checked against an earlier version]" : version.unversioned ? " [unversioned]" : "";
13305
13466
  }
13306
- function printQaRun(run3) {
13467
+ function printQaRun(run4) {
13307
13468
  console.log();
13308
- console.log(source_default.bold(`QA run ${run3.id}`));
13309
- console.log(` computedAt: ${run3.computedAt}`);
13310
- console.log(` Status: ${statusBadge(run3.summary.status)}`);
13311
- console.log(` Required: ${run3.summary.completedRequired}/${run3.summary.required}`);
13312
- if (run3.approvedAt) console.log(` Approved: ${run3.approvedAt}`);
13313
- if (run3.completedAt) console.log(` Complete: ${run3.completedAt}`);
13314
- if (run3.summary.blockers.length > 0) {
13469
+ console.log(source_default.bold(`QA run ${run4.id}`));
13470
+ console.log(` computedAt: ${run4.computedAt}`);
13471
+ console.log(` Status: ${statusBadge(run4.summary.status)}`);
13472
+ console.log(` Required: ${run4.summary.completedRequired}/${run4.summary.required}`);
13473
+ if (run4.approvedAt) console.log(` Approved: ${run4.approvedAt}`);
13474
+ if (run4.completedAt) console.log(` Complete: ${run4.completedAt}`);
13475
+ if (run4.summary.blockers.length > 0) {
13315
13476
  console.log(source_default.yellow("\nBlockers"));
13316
- run3.summary.blockers.forEach((blocker) => console.log(` - ${blocker}`));
13477
+ run4.summary.blockers.forEach((blocker) => console.log(` - ${blocker}`));
13317
13478
  }
13318
13479
  console.log(source_default.bold("\nChecklist"));
13319
- for (const item of run3.checklist) {
13480
+ for (const item of run4.checklist) {
13320
13481
  const required = item.required ? source_default.yellow(" required") : "";
13321
13482
  const notes = item.notes ? source_default.dim(` \u2014 ${item.notes}`) : "";
13322
13483
  console.log(` ${statusBadge(item.status)} ${item.id}${required}${qaVersionMarker(item)}`);
13323
13484
  console.log(` ${item.title}${notes}`);
13324
13485
  }
13325
- if (run3.urls.length > 0) {
13486
+ if (run4.urls.length > 0) {
13326
13487
  console.log(source_default.bold("\nQA URLs"));
13327
- run3.urls.forEach((url) => {
13488
+ run4.urls.forEach((url) => {
13328
13489
  console.log(` - ${url.label}: ${source_default.cyan(url.url)}`);
13329
13490
  });
13330
13491
  }
13331
- if ((run3.evidenceRecords ?? []).length > 0) {
13492
+ if ((run4.evidenceRecords ?? []).length > 0) {
13332
13493
  console.log(source_default.bold("\nEvidence records"));
13333
- for (const record4 of run3.evidenceRecords ?? []) {
13494
+ for (const record4 of run4.evidenceRecords ?? []) {
13334
13495
  const device = record4.device ? ` \xB7 ${record4.device}` : "";
13335
13496
  console.log(
13336
13497
  ` - ${record4.kind} \xB7 ${record4.label} \xB7 ${record4.status}${device} \xB7 ${record4.capturedAt}${qaVersionMarker(record4)}`
@@ -13343,18 +13504,18 @@ async function confirmApply(message) {
13343
13504
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
13344
13505
  return null;
13345
13506
  }
13346
- const readline3 = await import("node:readline");
13347
- const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
13507
+ const readline4 = await import("node:readline");
13508
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
13348
13509
  const answer = await new Promise((resolve2) => {
13349
13510
  rl.question(source_default.yellow(`${message} [y/N] `), resolve2);
13350
13511
  });
13351
13512
  rl.close();
13352
13513
  return answer.toLowerCase() === "y";
13353
13514
  }
13354
- function parseEvidenceInput(value) {
13355
- if (!value) return void 0;
13356
- const source = value.startsWith("@") ? value.slice(1) : value;
13357
- const text2 = value.startsWith("@") || import_node_fs8.default.existsSync(source) ? import_node_fs8.default.readFileSync(import_node_path6.default.resolve(process.cwd(), source), "utf8") : value;
13515
+ function parseEvidenceInput(value2) {
13516
+ if (!value2) return void 0;
13517
+ const source = value2.startsWith("@") ? value2.slice(1) : value2;
13518
+ const text2 = value2.startsWith("@") || import_node_fs8.default.existsSync(source) ? import_node_fs8.default.readFileSync(import_node_path6.default.resolve(process.cwd(), source), "utf8") : value2;
13358
13519
  const parsed = JSON.parse(text2);
13359
13520
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
13360
13521
  throw new Error("QA evidence must be a JSON object");
@@ -13362,24 +13523,24 @@ function parseEvidenceInput(value) {
13362
13523
  return parsed;
13363
13524
  }
13364
13525
  function qaChoice(flags, description, choices) {
13365
- return new Option(flags, description).choices([...choices]).argParser((value) => value);
13526
+ return new Option(flags, description).choices([...choices]).argParser((value2) => value2);
13366
13527
  }
13367
- function validateQaChoice(command, option, value, allowed2) {
13368
- if (value !== void 0 && !allowed2.includes(value)) {
13369
- fail(command, `Invalid ${option}: ${value}`, EXIT_CODES.validation, {
13528
+ function validateQaChoice(command, option, value2, allowed2) {
13529
+ if (value2 !== void 0 && !allowed2.includes(value2)) {
13530
+ fail(command, `Invalid ${option}: ${value2}`, EXIT_CODES.validation, {
13370
13531
  option,
13371
13532
  allowedValues: [...allowed2]
13372
13533
  });
13373
13534
  }
13374
13535
  }
13375
- function qaSummary(run3, changed) {
13536
+ function qaSummary(run4, changed) {
13376
13537
  return {
13377
13538
  run: {
13378
- id: run3.id,
13379
- status: run3.status,
13380
- summary: run3.summary,
13381
- approvedAt: run3.approvedAt,
13382
- updatedAt: run3.updatedAt
13539
+ id: run4.id,
13540
+ status: run4.status,
13541
+ summary: run4.summary,
13542
+ approvedAt: run4.approvedAt,
13543
+ updatedAt: run4.updatedAt
13383
13544
  },
13384
13545
  changed,
13385
13546
  validationErrors: []
@@ -13392,8 +13553,8 @@ function tryReadFilesystemSession(folder) {
13392
13553
  return null;
13393
13554
  }
13394
13555
  }
13395
- async function resolveExperimentForFilesystemOrId(value) {
13396
- const candidate = folderPath(value);
13556
+ async function resolveExperimentForFilesystemOrId(value2) {
13557
+ const candidate = folderPath(value2);
13397
13558
  const session = tryReadFilesystemSession(candidate);
13398
13559
  if (session) {
13399
13560
  return {
@@ -13402,8 +13563,8 @@ async function resolveExperimentForFilesystemOrId(value) {
13402
13563
  label: import_node_path6.default.basename(candidate)
13403
13564
  };
13404
13565
  }
13405
- const exp = await resolveExperiment(value);
13406
- if (!exp) throw new Error(`Experiment "${value}" not found.`);
13566
+ const exp = await resolveExperiment(value2);
13567
+ if (!exp) throw new Error(`Experiment "${value2}" not found.`);
13407
13568
  return { experimentId: exp.id, folder: null, label: exp.name };
13408
13569
  }
13409
13570
  async function refreshExperimentFilesystemFolderIfNeeded(creds, folder, experimentId) {
@@ -13525,8 +13686,8 @@ var list = new Command("list").alias("ls").description(
13525
13686
  fields3.map((field) => field.toUpperCase()),
13526
13687
  experiments.map(
13527
13688
  (experiment2) => fields3.map((field) => {
13528
- const value = experiment2[field];
13529
- return value == null ? "-" : typeof value === "object" ? JSON.stringify(value) : String(value);
13689
+ const value2 = experiment2[field];
13690
+ return value2 == null ? "-" : typeof value2 === "object" ? JSON.stringify(value2) : String(value2);
13530
13691
  })
13531
13692
  )
13532
13693
  );
@@ -13564,6 +13725,7 @@ var get = new Command("get").description(
13564
13725
  exp.shop_id ?? creds.resolvedContext?.shopId,
13565
13726
  exp.id
13566
13727
  );
13728
+ warnReplacementCharacters("experiment", exp, ["name", "description", "variations[].name"]);
13567
13729
  output(this, { ...exp, dashboard_url: dashboardUrl }, () => printExperiment(exp, dashboardUrl));
13568
13730
  });
13569
13731
  var attachIdea = new Command("attach-idea").description("Attach an experiment to an idea").argument("<experiment>", EXPERIMENT_REFERENCE_DESCRIPTION).requiredOption("--idea <key>", "Idea UUID or Apex backlog key").option("--follow-up", "Attach as a follow-up test").action(async function action5(experimentRef, opts) {
@@ -13599,6 +13761,22 @@ var detachIdea = new Command("detach-idea").description("Detach an experiment fr
13599
13761
  );
13600
13762
  output(this, res.data, () => printSuccess("Idea detached."));
13601
13763
  });
13764
+ function printResultsByChannel(body5) {
13765
+ const data = body5.data ?? body5;
13766
+ if (data.status !== "ready") {
13767
+ console.log(`Channel split ${data.status}: ${data.reason}`);
13768
+ return;
13769
+ }
13770
+ console.log(`By channel through ${data.as_of} (${data.metric}, ${data.currency})`);
13771
+ const channels = data.channels;
13772
+ for (const row of channels) {
13773
+ console.log(`${row.channel}: ${row.state}`);
13774
+ if (row.state !== "readable" || !row.arms) continue;
13775
+ for (const [arm, values] of Object.entries(row.arms))
13776
+ console.log(` ${arm}: ${values.visitors} visitors, ${values.perVisitor} per visitor`);
13777
+ }
13778
+ console.log("Channel comparisons are hypotheses for follow-up tests.");
13779
+ }
13602
13780
  function printResultsExplain(body5, samples) {
13603
13781
  const data = body5.data;
13604
13782
  console.log("Diagnostic results explanation");
@@ -13643,7 +13821,13 @@ ${JSON.stringify(data.order, null, 2)}`);
13643
13821
  console.log(`
13644
13822
  Definitions: ${data.definitions_url}`);
13645
13823
  }
13646
- 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("--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) {
13824
+ 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("--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) {
13825
+ if (opts.byChannel && (opts.explain || opts.segment || opts.excludeSegment || opts.currency || opts.goal || opts.order || opts.samples || this.getOptionValueSource("wait") !== "default"))
13826
+ fail(
13827
+ this,
13828
+ "--by-channel reads the current run without other result filters",
13829
+ EXIT_CODES.validation
13830
+ );
13647
13831
  if (opts.explain && (opts.segment !== void 0 || opts.excludeSegment !== void 0))
13648
13832
  fail(
13649
13833
  this,
@@ -13665,12 +13849,12 @@ var results = new Command("results").description("Read experiment results and ti
13665
13849
  fail(this, "--wait must be a non-negative number of seconds", EXIT_CODES.validation);
13666
13850
  }
13667
13851
  const startedWaitingAt = Date.now();
13668
- let res = opts.explain ? await getExperimentResultsExplain(creds, exp.id, opts) : await getExperimentResults(creds, exp.id, {
13852
+ let res = opts.byChannel ? await getExperimentResultsByChannel(creds, exp.id) : opts.explain ? await getExperimentResultsExplain(creds, exp.id, opts) : await getExperimentResults(creds, exp.id, {
13669
13853
  currency: opts.currency,
13670
13854
  segment: opts.segment,
13671
13855
  exclude_segment: opts.excludeSegment
13672
13856
  });
13673
- while (!opts.explain && isResultsComputing(res)) {
13857
+ while (!opts.byChannel && !opts.explain && isResultsComputing(res)) {
13674
13858
  const elapsedSeconds = (Date.now() - startedWaitingAt) / 1e3;
13675
13859
  const remainingSeconds = waitSeconds - elapsedSeconds;
13676
13860
  if (remainingSeconds <= 0) break;
@@ -13700,7 +13884,7 @@ var results = new Command("results").description("Read experiment results and ti
13700
13884
  output(
13701
13885
  this,
13702
13886
  res.data,
13703
- () => opts.explain ? printResultsExplain(res.data, Boolean(opts.samples)) : printExperimentResults(res.data, opts.currency)
13887
+ () => opts.byChannel ? printResultsByChannel(res.data) : opts.explain ? printResultsExplain(res.data, Boolean(opts.samples)) : printExperimentResults(res.data, opts.currency)
13704
13888
  );
13705
13889
  } catch (error) {
13706
13890
  spinner?.fail("Failed to fetch experiment results");
@@ -13759,6 +13943,7 @@ var update = new Command("update").description("Update an experiment").addOption
13759
13943
  ...opts.countingType !== void 0 ? { counting_type: opts.countingType } : {}
13760
13944
  });
13761
13945
  if (!res.ok) fail(this, res.error ?? "Failed to update experiment", classifyStatus(res.status));
13946
+ warnReplacementCharacters("experiment", res.data, ["name", "description", "variations[].name"]);
13762
13947
  const stored = res.data?.results_weekdays ?? null;
13763
13948
  output(this, res.data, () => {
13764
13949
  if (opts.countingType !== void 0)
@@ -13769,17 +13954,17 @@ var update = new Command("update").description("Update an experiment").addOption
13769
13954
  );
13770
13955
  });
13771
13956
  });
13772
- function collectCreateOption(value, previous) {
13773
- return [...previous, value];
13957
+ function collectCreateOption(value2, previous) {
13958
+ return [...previous, value2];
13774
13959
  }
13775
- function parseHttpDestination(value) {
13776
- const trimmed = value.trim();
13960
+ function parseHttpDestination(value2) {
13961
+ const trimmed = value2.trim();
13777
13962
  try {
13778
13963
  const url = new URL(trimmed);
13779
13964
  if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error();
13780
13965
  } catch {
13781
13966
  throw new CliError(
13782
- `Invalid redirect destination "${value}": expected an HTTP(S) URL.`,
13967
+ `Invalid redirect destination "${value2}": expected an HTTP(S) URL.`,
13783
13968
  EXIT_CODES.validation
13784
13969
  );
13785
13970
  }
@@ -13823,8 +14008,8 @@ function normalizeNewGoalKey(key) {
13823
14008
  const normalized = key.trim().toLowerCase().replace(/-/g, "_");
13824
14009
  return normalized === "page" ? "page_id" : normalized;
13825
14010
  }
13826
- function validateNewGoal(command, value, body5) {
13827
- const invalid = (reason2) => fail(command, `Invalid --goal "new:${value}"; ${reason2}`, EXIT_CODES.validation);
14011
+ function validateNewGoal(command, value2, body5) {
14012
+ const invalid = (reason2) => fail(command, `Invalid --goal "new:${value2}"; ${reason2}`, EXIT_CODES.validation);
13828
14013
  const text2 = (field) => {
13829
14014
  const raw = body5[field];
13830
14015
  return typeof raw === "string" && raw.length > 0 ? raw : void 0;
@@ -13872,20 +14057,20 @@ function validateNewGoal(command, value, body5) {
13872
14057
  if (valueUnit && !GOAL_VALUE_UNITS.includes(valueUnit))
13873
14058
  invalid(`value-unit must be one of ${GOAL_VALUE_UNITS.join(", ")}`);
13874
14059
  }
13875
- function parseNewGoal(command, value) {
14060
+ function parseNewGoal(command, value2) {
13876
14061
  const body5 = {};
13877
- for (const field of value.split(",")) {
14062
+ for (const field of value2.split(",")) {
13878
14063
  const separator = field.indexOf("=");
13879
14064
  if (separator <= 0 || separator === field.length - 1) {
13880
14065
  fail(
13881
14066
  command,
13882
- `Invalid --goal "new:${value}"; use new:type=<type>,selector=<css>,name=<text>`,
14067
+ `Invalid --goal "new:${value2}"; use new:type=<type>,selector=<css>,name=<text>`,
13883
14068
  EXIT_CODES.validation
13884
14069
  );
13885
14070
  }
13886
14071
  body5[normalizeNewGoalKey(field.slice(0, separator))] = field.slice(separator + 1).trim();
13887
14072
  }
13888
- validateNewGoal(command, value, body5);
14073
+ validateNewGoal(command, value2, body5);
13889
14074
  return body5;
13890
14075
  }
13891
14076
  async function resolveNewGoalPages(command, creds, goals, interactive) {
@@ -13928,8 +14113,8 @@ async function resolveNewGoalPages(command, creds, goals, interactive) {
13928
14113
  }
13929
14114
  }
13930
14115
  async function askCreate(question) {
13931
- const readline3 = await import("node:readline");
13932
- const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
14116
+ const readline4 = await import("node:readline");
14117
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
13933
14118
  return new Promise(
13934
14119
  (resolve2) => rl.question(question, (answer) => {
13935
14120
  rl.close();
@@ -14026,8 +14211,8 @@ var create = new Command("create").description(
14026
14211
  if (!Number.isFinite(traffic) || traffic < 0 || traffic > 100) {
14027
14212
  fail(this, "--traffic must be a number from 0 to 100", EXIT_CODES.validation);
14028
14213
  }
14029
- const sourceRules = [...opts.sourceUrl, ...opts.pageUrl].map((value) => value.trim());
14030
- if (sourceRules.some((value) => value.length === 0)) {
14214
+ const sourceRules = [...opts.sourceUrl, ...opts.pageUrl].map((value2) => value2.trim());
14215
+ if (sourceRules.some((value2) => value2.length === 0)) {
14031
14216
  fail(this, "--source-url/--page-url cannot be empty", EXIT_CODES.validation);
14032
14217
  }
14033
14218
  if (opts.type !== "redirect" && opts.destination.length > 0) {
@@ -14241,6 +14426,7 @@ var create = new Command("create").description(
14241
14426
  previewNote = `No preview link: the experiment has no ${variation ? "store URL" : "treatment variation"} yet. Add one, then run \`apex preview ${res.data.id}\`.`;
14242
14427
  }
14243
14428
  }
14429
+ warnReplacementCharacters("experiment", res.data, ["name", "description", "variations[].name"]);
14244
14430
  output(
14245
14431
  this,
14246
14432
  {
@@ -14330,8 +14516,8 @@ var del = new Command("delete").alias("rm").description("Delete an experiment").
14330
14516
  `apex experiments delete ${idOrSlug} --force`
14331
14517
  );
14332
14518
  }
14333
- const readline3 = await import("node:readline");
14334
- const rl = readline3.createInterface({
14519
+ const readline4 = await import("node:readline");
14520
+ const rl = readline4.createInterface({
14335
14521
  input: process.stdin,
14336
14522
  output: process.stderr
14337
14523
  });
@@ -14946,7 +15132,7 @@ var applyFilesystem = new Command("apply").option(
14946
15132
  );
14947
15133
  for (const variation of receipt.variations) {
14948
15134
  console.log(
14949
- ` ${variation.name} (${variation.slug}): ${Object.entries(variation.digests).map(([key, value]) => `${key}=${value ?? "null"}`).join(" ")}`
15135
+ ` ${variation.name} (${variation.slug}): ${Object.entries(variation.digests).map(([key, value2]) => `${key}=${value2 ?? "null"}`).join(" ")}`
14950
15136
  );
14951
15137
  }
14952
15138
  }
@@ -15019,18 +15205,18 @@ var qaCheck = new Command("check").description("Update one QA checklist item wit
15019
15205
  classifyStatus(res.status),
15020
15206
  res.errorDetails
15021
15207
  );
15022
- const run3 = res.data.run;
15208
+ const run4 = res.data.run;
15023
15209
  await refreshExperimentFilesystemFolderIfNeeded(
15024
15210
  creds,
15025
15211
  resolved.folder,
15026
15212
  resolved.experimentId
15027
15213
  );
15028
- const data = opts.summary ? qaSummary(run3, run3.checklist.find((item) => item.id === opts.item) ?? null) : res.data;
15214
+ const data = opts.summary ? qaSummary(run4, run4.checklist.find((item) => item.id === opts.item) ?? null) : res.data;
15029
15215
  output(this, data, () => {
15030
15216
  spinner.succeed("Updated QA checklist");
15031
15217
  if (opts.summary) process.stdout.write(`${JSON.stringify(data, null, 2)}
15032
15218
  `);
15033
- else printQaRun(run3);
15219
+ else printQaRun(run4);
15034
15220
  });
15035
15221
  } catch (error) {
15036
15222
  spinner.fail("Failed to update QA checklist");
@@ -15065,21 +15251,21 @@ var qaNotes = new Command("notes").description("Update QA run notes or acceptanc
15065
15251
  classifyStatus(res.status),
15066
15252
  res.errorDetails
15067
15253
  );
15068
- const run3 = res.data.run;
15254
+ const run4 = res.data.run;
15069
15255
  await refreshExperimentFilesystemFolderIfNeeded(
15070
15256
  creds,
15071
15257
  resolved.folder,
15072
15258
  resolved.experimentId
15073
15259
  );
15074
- const data = opts.summary ? qaSummary(run3, {
15075
- notes: run3.notes,
15076
- acceptanceCriteriaSummary: run3.acceptanceCriteriaSummary
15260
+ const data = opts.summary ? qaSummary(run4, {
15261
+ notes: run4.notes,
15262
+ acceptanceCriteriaSummary: run4.acceptanceCriteriaSummary
15077
15263
  }) : res.data;
15078
15264
  output(this, data, () => {
15079
15265
  spinner.succeed("Updated QA notes");
15080
15266
  if (opts.summary) process.stdout.write(`${JSON.stringify(data, null, 2)}
15081
15267
  `);
15082
- else printQaRun(run3);
15268
+ else printQaRun(run4);
15083
15269
  });
15084
15270
  } catch (error) {
15085
15271
  spinner.fail("Failed to update QA notes");
@@ -15119,9 +15305,9 @@ var qaApprove = new Command("approve").description("Approve QA and move the expe
15119
15305
  }
15120
15306
  if (typeof details.split_hash === "string" && details.split_hash.length > 0) {
15121
15307
  const weights = Array.isArray(details.variation_weights) ? details.variation_weights : [];
15122
- for (const value of weights) {
15123
- if (value && typeof value === "object") {
15124
- const variation = value;
15308
+ for (const value2 of weights) {
15309
+ if (value2 && typeof value2 === "object") {
15310
+ const variation = value2;
15125
15311
  process.stderr.write(
15126
15312
  `${variation.name ?? variation.id ?? "Variation"}: ${(variation.weight ?? 0) * 100}%${variation.weight === 0 ? " (no traffic)" : ""}
15127
15313
  `
@@ -15197,17 +15383,17 @@ var qaReopen = new Command("reopen").description("Reopen QA and clear scheduled
15197
15383
  throw error;
15198
15384
  }
15199
15385
  });
15200
- function parseJsonValue(value) {
15201
- const source = value.startsWith("@") ? value.slice(1) : value;
15202
- const text2 = value.startsWith("@") || import_node_fs8.default.existsSync(source) ? import_node_fs8.default.readFileSync(import_node_path6.default.resolve(process.cwd(), source), "utf8") : value;
15386
+ function parseJsonValue(value2) {
15387
+ const source = value2.startsWith("@") ? value2.slice(1) : value2;
15388
+ const text2 = value2.startsWith("@") || import_node_fs8.default.existsSync(source) ? import_node_fs8.default.readFileSync(import_node_path6.default.resolve(process.cwd(), source), "utf8") : value2;
15203
15389
  return JSON.parse(text2);
15204
15390
  }
15205
- function collectFile(value, previous = []) {
15206
- return [...previous, value];
15391
+ function collectFile(value2, previous = []) {
15392
+ return [...previous, value2];
15207
15393
  }
15208
- function normalizeEvidenceDevice(value) {
15209
- if (!value) return void 0;
15210
- const normalized = value.trim().toLowerCase();
15394
+ function normalizeEvidenceDevice(value2) {
15395
+ if (!value2) return void 0;
15396
+ const normalized = value2.trim().toLowerCase();
15211
15397
  if (normalized === "desktop_chrome" || normalized === "desktop_safari") return "desktop";
15212
15398
  if (normalized === "iphone_15_pro" || normalized === "galaxy_s24") return "mobile";
15213
15399
  if (normalized === "ipad_pro") return "tablet";
@@ -15294,15 +15480,15 @@ var qaEvidence = new Command("evidence").description("Attach screenshot jobs or
15294
15480
  );
15295
15481
  }
15296
15482
  spinner.stop();
15297
- const run3 = response.data.run;
15483
+ const run4 = response.data.run;
15298
15484
  const data = opts.summary ? qaSummary(
15299
- run3,
15300
- run3.evidenceRecords.filter((record4) => !previousIds.has(record4.id))
15485
+ run4,
15486
+ run4.evidenceRecords.filter((record4) => !previousIds.has(record4.id))
15301
15487
  ) : response.data;
15302
15488
  output(this, data, () => {
15303
15489
  if (opts.summary) process.stdout.write(`${JSON.stringify(data, null, 2)}
15304
15490
  `);
15305
- else printQaRun(run3);
15491
+ else printQaRun(run4);
15306
15492
  });
15307
15493
  } catch (error) {
15308
15494
  spinner.fail("Failed to write QA evidence");
@@ -15334,14 +15520,14 @@ var qa = new Command("qa").description("Manage experiment QA runs from the CLI")
15334
15520
  data,
15335
15521
  () => printTable(
15336
15522
  ["KIND", "STATE", "SUMMARY"],
15337
- Object.entries(data.kinds).map(([kind5, value]) => [
15523
+ Object.entries(data.kinds).map(([kind5, value2]) => [
15338
15524
  kind5,
15339
- value.evidence.state,
15340
- value.result?.summary ?? value.evidence.reasons[0] ?? "Saved evidence is current."
15525
+ value2.evidence.state,
15526
+ value2.result?.summary ?? value2.evidence.reasons[0] ?? "Saved evidence is current."
15341
15527
  ])
15342
15528
  )
15343
15529
  );
15344
- const states = Object.values(data.kinds).map((value) => value?.evidence.state);
15530
+ const states = Object.values(data.kinds).map((value2) => value2?.evidence.state);
15345
15531
  if (states.some((state) => state !== "pass")) {
15346
15532
  console.error(
15347
15533
  "Next: fix stale, pending, or failing evidence and run apex experiments qa probe again."
@@ -15405,11 +15591,11 @@ var SCREENSHOT_DEVICES = [
15405
15591
  "ipad_pro",
15406
15592
  "desktop_safari"
15407
15593
  ];
15408
- var safeFilenamePart = (value) => value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "screenshot";
15409
- var commaList = (value) => value.split(",").map((item) => item.trim()).filter(Boolean);
15410
- function shellQuote(value) {
15411
- if (value.length > 0 && /^[A-Za-z0-9_@%+=:,./-]+$/.test(value)) return value;
15412
- return `'${value.replaceAll("'", "'\\''")}'`;
15594
+ var safeFilenamePart = (value2) => value2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "screenshot";
15595
+ var commaList = (value2) => value2.split(",").map((item) => item.trim()).filter(Boolean);
15596
+ function shellQuote(value2) {
15597
+ if (value2.length > 0 && /^[A-Za-z0-9_@%+=:,./-]+$/.test(value2)) return value2;
15598
+ return `'${value2.replaceAll("'", "'\\''")}'`;
15413
15599
  }
15414
15600
  function isTransientResumeStatus(status4) {
15415
15601
  return status4 === 429 || status4 !== void 0 && status4 >= 500;
@@ -15794,11 +15980,197 @@ Examples:
15794
15980
  `
15795
15981
  );
15796
15982
 
15983
+ // src/commands/results.ts
15984
+ var import_node_fs9 = __toESM(require("node:fs"));
15985
+ var import_node_path7 = __toESM(require("node:path"));
15986
+ var import_promises3 = __toESM(require("node:readline/promises"));
15987
+ var OUTCOMES = /* @__PURE__ */ new Set(["won", "lost", "inconclusive"]);
15988
+ function collect(value2, previous) {
15989
+ return [...previous, value2];
15990
+ }
15991
+ function pair(command, value2, flag) {
15992
+ const index = value2.indexOf("=");
15993
+ if (index <= 0 || index === value2.length - 1)
15994
+ fail(command, `Invalid ${flag} "${value2}": use left=right.`, EXIT_CODES.validation);
15995
+ return [value2.slice(0, index).trim(), value2.slice(index + 1).trim()];
15996
+ }
15997
+ function apiFailure(command, response) {
15998
+ const body5 = response.data;
15999
+ fail(
16000
+ command,
16001
+ body5?.error?.message ?? response.error ?? `Request failed (HTTP ${response.status})`,
16002
+ classifyStatus(response.status),
16003
+ void 0,
16004
+ { status: response.status, body: response.data }
16005
+ );
16006
+ }
16007
+ function value(row, key) {
16008
+ const item = row[key];
16009
+ return item == null ? "-" : String(item);
16010
+ }
16011
+ function printPreview(preview2) {
16012
+ printTable(
16013
+ ["ROW", "TITLE", "OUTCOME", "METRIC", "LIFT", "SAMPLE", "CONF", "DEVICE", "DATES", "LEARNING"],
16014
+ preview2.rows.map((row, index) => [
16015
+ String(row.source_row ?? index + 1),
16016
+ value(row, "title"),
16017
+ value(row, "outcome"),
16018
+ value(row, "primary_metric"),
16019
+ value(row, "primary_lift"),
16020
+ value(row, "sample_size"),
16021
+ value(row, "confidence"),
16022
+ value(row, "device"),
16023
+ [row.started_at, row.ended_at].filter(Boolean).join(" \u2013 ") || "-",
16024
+ value(row, "learning").slice(0, 60)
16025
+ ])
16026
+ );
16027
+ for (const error of preview2.error_rows ?? [])
16028
+ console.error(`${error.source_name}:${error.source_row} ${error.field}: ${error.error}`);
16029
+ for (const warning of preview2.warnings ?? [])
16030
+ console.error(`Warning: ${typeof warning === "string" ? warning : JSON.stringify(warning)}`);
16031
+ }
16032
+ var importCommand = new Command("import").description("Upload past test results from CSV or JSON files").requiredOption("--file <path>", "CSV or JSON file (repeatable)", collect, []).option(
16033
+ "--map <column=field>",
16034
+ "Map a file column to an Apex field (repeatable). Fields: title, outcome, learning, primary_lift, sample_size, page_url, page_label, device, description, started_at, ended_at, legacy_id",
16035
+ collect,
16036
+ []
16037
+ ).option(
16038
+ "--outcome-value <value=outcome>",
16039
+ "Map a value of the outcome column to won, lost or inconclusive (repeatable)",
16040
+ collect,
16041
+ []
16042
+ ).option("--source <name>", "Source name stored on every imported row (default: the file name)").option("--dry-run", "Inspect and preview without importing").option("--yes", "Confirm the import without prompting").action(async function(options) {
16043
+ const creds = requireCredentials();
16044
+ const shopId = creds.resolvedContext?.shopId;
16045
+ const shopCredentials = creds;
16046
+ const shopLabel = shopCredentials.shopDomain ?? shopCredentials.domain ?? shopId ?? "the selected shop";
16047
+ const makeForm = () => {
16048
+ const form = new FormData();
16049
+ for (const fileArg of options.file) {
16050
+ const resolved = import_node_path7.default.resolve(fileArg);
16051
+ let bytes;
16052
+ try {
16053
+ bytes = import_node_fs9.default.readFileSync(resolved);
16054
+ } catch (error) {
16055
+ fail(
16056
+ this,
16057
+ `Could not read ${fileArg}: ${error instanceof Error ? error.message : String(error)}`,
16058
+ EXIT_CODES.validation,
16059
+ { code: "local_file_error" }
16060
+ );
16061
+ }
16062
+ form.append("files", new Blob([bytes]), import_node_path7.default.basename(resolved));
16063
+ }
16064
+ return form;
16065
+ };
16066
+ const inspected = await requestForm(creds, "POST", "/api/v1/test-results/import/inspect", makeForm());
16067
+ if (!inspected.ok || !inspected.data) apiFailure(this, inspected);
16068
+ const mapping = { ...inspected.data.data.suggested_mapping.columns };
16069
+ for (const raw of options.map) {
16070
+ const [column, field] = pair(this, raw, "--map");
16071
+ mapping[field] = column;
16072
+ }
16073
+ const outcomeValues = { ...inspected.data.data.suggested_mapping.outcome_values };
16074
+ for (const raw of options.outcomeValue) {
16075
+ const [source, outcome] = pair(this, raw, "--outcome-value");
16076
+ if (!OUTCOMES.has(outcome))
16077
+ fail(
16078
+ this,
16079
+ `Invalid outcome "${outcome}": use won, lost, or inconclusive.`,
16080
+ EXIT_CODES.validation
16081
+ );
16082
+ outcomeValues[source] = outcome;
16083
+ }
16084
+ const missing = ["title", "outcome", "learning"].filter((field) => !mapping[field]);
16085
+ if (missing.length)
16086
+ fail(
16087
+ this,
16088
+ `Unmapped required columns: ${missing.join(", ")}. Use --map <column=field>.`,
16089
+ EXIT_CODES.validation
16090
+ );
16091
+ const previewForm = makeForm();
16092
+ previewForm.append(
16093
+ "mapping",
16094
+ JSON.stringify({ columns: mapping, outcome_values: outcomeValues })
16095
+ );
16096
+ const previewResponse = await requestForm(
16097
+ creds,
16098
+ "POST",
16099
+ "/api/v1/test-results/import/preview",
16100
+ previewForm
16101
+ );
16102
+ if (!previewResponse.ok || !previewResponse.data) apiFailure(this, previewResponse);
16103
+ const preview2 = previewResponse.data.data;
16104
+ if (preview2.error_rows?.length) {
16105
+ if (!shouldUseJson(this)) printPreview(preview2);
16106
+ fail(
16107
+ this,
16108
+ `Preview found ${preview2.error_rows.length} row error(s); nothing was imported.`,
16109
+ EXIT_CODES.validation,
16110
+ { preview: preview2 }
16111
+ );
16112
+ }
16113
+ if (options.dryRun) {
16114
+ output(this, previewResponse.data, () => printPreview(preview2));
16115
+ return;
16116
+ }
16117
+ if (!shouldUseJson(this)) printPreview(preview2);
16118
+ if (!options.yes) {
16119
+ const globals = this.optsWithGlobals();
16120
+ const retryArgs = [
16121
+ ...options.file.map((file) => `--file ${JSON.stringify(file)}`),
16122
+ ...options.map.map((value2) => `--map ${JSON.stringify(value2)}`),
16123
+ ...options.outcomeValue.map((value2) => `--outcome-value ${JSON.stringify(value2)}`),
16124
+ ...options.source !== void 0 ? [`--source ${JSON.stringify(options.source)}`] : [],
16125
+ ...globals.shop ? [`--shop ${JSON.stringify(globals.shop)}`] : [],
16126
+ ...globals.org ? [`--org ${JSON.stringify(globals.org)}`] : []
16127
+ ];
16128
+ const retry = `apex results import ${retryArgs.join(" ")} --yes`;
16129
+ if (!process.stdin.isTTY)
16130
+ confirmationRequired(
16131
+ this,
16132
+ "Import confirmation is required in non-TTY mode; rerun with --yes.",
16133
+ retry
16134
+ );
16135
+ const rl = import_promises3.default.createInterface({ input: process.stdin, output: process.stderr });
16136
+ const answer = await rl.question(
16137
+ `Import ${preview2.rows.length} rows into ${shopLabel}? [y/N] `
16138
+ );
16139
+ rl.close();
16140
+ if (!/^y(es)?$/i.test(answer.trim()))
16141
+ confirmationRequired(this, "Import cancelled; rerun with --yes to confirm.", retry);
16142
+ }
16143
+ const merged = { imported: 0, skipped: 0, test_results: [] };
16144
+ for (let offset = 0; offset < preview2.rows.length; offset += 500) {
16145
+ const response = await request(
16146
+ creds,
16147
+ "POST",
16148
+ "/api/v1/test-results/import/confirm",
16149
+ {
16150
+ rows: preview2.rows.slice(offset, offset + 500).map((row) => options.source ? { ...row, source_name: options.source } : row)
16151
+ }
16152
+ );
16153
+ if (!response.ok || !response.data) apiFailure(this, response);
16154
+ merged.imported += response.data.data.imported;
16155
+ merged.skipped += response.data.data.skipped;
16156
+ merged.test_results.push(...response.data.data.test_results);
16157
+ }
16158
+ for (const row of merged.test_results)
16159
+ if (row.shop_id != null)
16160
+ assertResponseShop(String(row.shop_id), shopId, creds.credentialKind);
16161
+ output(
16162
+ this,
16163
+ merged,
16164
+ () => console.log(`Imported ${merged.imported}, skipped ${merged.skipped} (already present).`)
16165
+ );
16166
+ });
16167
+ var resultsCommand = new Command("results").description("Import and manage test results").addCommand(importCommand);
16168
+
15797
16169
  // src/commands/push.ts
15798
16170
  init_source();
15799
- var import_node_fs9 = __toESM(require("node:fs"));
16171
+ var import_node_fs10 = __toESM(require("node:fs"));
15800
16172
  var import_node_os7 = __toESM(require("node:os"));
15801
- var import_node_path7 = __toESM(require("node:path"));
16173
+ var import_node_path8 = __toESM(require("node:path"));
15802
16174
  var import_node_url2 = require("node:url");
15803
16175
  function toApiBody(exp) {
15804
16176
  return {
@@ -15839,7 +16211,7 @@ async function rewriteExperimentAssets(exp, rewrite, baseDir) {
15839
16211
  };
15840
16212
  }
15841
16213
  async function importConfigModule(absPath) {
15842
- const ext = import_node_path7.default.extname(absPath).toLowerCase();
16214
+ const ext = import_node_path8.default.extname(absPath).toLowerCase();
15843
16215
  if (ext === ".js" || ext === ".mjs" || ext === ".cjs") {
15844
16216
  return await import((0, import_node_url2.pathToFileURL)(absPath).href);
15845
16217
  }
@@ -15853,8 +16225,8 @@ async function importConfigModule(absPath) {
15853
16225
  ` + (err instanceof Error ? err.message : String(err))
15854
16226
  );
15855
16227
  }
15856
- const tmpDir = import_node_fs9.default.mkdtempSync(import_node_path7.default.join(import_node_os7.default.tmpdir(), "drip-config-"));
15857
- const outFile = import_node_path7.default.join(tmpDir, "config.mjs");
16228
+ const tmpDir = import_node_fs10.default.mkdtempSync(import_node_path8.default.join(import_node_os7.default.tmpdir(), "drip-config-"));
16229
+ const outFile = import_node_path8.default.join(tmpDir, "config.mjs");
15858
16230
  try {
15859
16231
  await esbuild.build({
15860
16232
  entryPoints: [absPath],
@@ -15870,15 +16242,15 @@ async function importConfigModule(absPath) {
15870
16242
  return await import(href);
15871
16243
  } finally {
15872
16244
  try {
15873
- import_node_fs9.default.rmSync(tmpDir, { recursive: true, force: true });
16245
+ import_node_fs10.default.rmSync(tmpDir, { recursive: true, force: true });
15874
16246
  } catch {
15875
16247
  }
15876
16248
  }
15877
16249
  }
15878
16250
  throw new Error(`Unsupported config extension "${ext}". Use .drip.config.ts or .drip.config.js.`);
15879
16251
  }
15880
- function isRecord2(value) {
15881
- return !!value && typeof value === "object" && !Array.isArray(value);
16252
+ function isRecord2(value2) {
16253
+ return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
15882
16254
  }
15883
16255
  var pushCommand = new Command("push").description("Push experiment definitions from config file to the API (idempotent by slug)").option("-c, --config <path>", "Config file path").option("--dry-run", "Show what would be pushed without making changes").option("--no-asset-upload", "Keep inline and local image references unchanged").action(async function action23(opts) {
15884
16256
  const creds = requireCredentials();
@@ -15891,7 +16263,7 @@ var pushCommand = new Command("push").description("Push experiment definitions f
15891
16263
  );
15892
16264
  }
15893
16265
  const spinner = process.stderr.isTTY ? await createSpinner(`Loading config from ${source_default.cyan(configPath)}...`) : null;
15894
- const absoluteConfigPath = import_node_path7.default.resolve(configPath);
16266
+ const absoluteConfigPath = import_node_path8.default.resolve(configPath);
15895
16267
  let experiments;
15896
16268
  try {
15897
16269
  const mod = await importConfigModule(absoluteConfigPath);
@@ -15958,7 +16330,7 @@ var pushCommand = new Command("push").description("Push experiment definitions f
15958
16330
  return;
15959
16331
  }
15960
16332
  const assetRewriter = createAssetReferenceRewriter({
15961
- baseDir: import_node_path7.default.dirname(absoluteConfigPath),
16333
+ baseDir: import_node_path8.default.dirname(absoluteConfigPath),
15962
16334
  baseUrl: creds.baseUrl,
15963
16335
  enabled: opts.assetUpload !== false,
15964
16336
  upload: async (file) => {
@@ -15988,7 +16360,7 @@ var pushCommand = new Command("push").description("Push experiment definitions f
15988
16360
  let body5;
15989
16361
  try {
15990
16362
  body5 = toApiBody(
15991
- await rewriteExperimentAssets(exp, assetRewriter, import_node_path7.default.dirname(absoluteConfigPath))
16363
+ await rewriteExperimentAssets(exp, assetRewriter, import_node_path8.default.dirname(absoluteConfigPath))
15992
16364
  );
15993
16365
  } catch (error) {
15994
16366
  const message = error instanceof Error ? error.message : String(error);
@@ -16110,9 +16482,9 @@ var previewCommand = new Command("preview").description(
16110
16482
  );
16111
16483
  const expiresAt = new Date(data.expires_at);
16112
16484
  const remainingMs = expiresAt.getTime() - Date.now();
16113
- const value = remainingMs < 864e5 ? Math.max(1, Math.round(remainingMs / 36e5)) : Math.round(remainingMs / 864e5);
16485
+ const value2 = remainingMs < 864e5 ? Math.max(1, Math.round(remainingMs / 36e5)) : Math.round(remainingMs / 864e5);
16114
16486
  const unit = remainingMs < 864e5 ? "hour" : "day";
16115
- const duration = `${value} ${unit}${value === 1 ? "" : "s"}`;
16487
+ const duration = `${value2} ${unit}${value2 === 1 ? "" : "s"}`;
16116
16488
  console.log(
16117
16489
  `Valid until ${expiresAt.toISOString()} (${duration}). Re-run this command for a fresh link.`
16118
16490
  );
@@ -16191,8 +16563,8 @@ var previewCommand = new Command("preview").description(
16191
16563
  const qaFile = snapshot.data.files.find((file) => file.path === "qa.json");
16192
16564
  if (qaFile) {
16193
16565
  try {
16194
- const run3 = JSON.parse(qaFile.content)?.latestRun;
16195
- if (run3 && typeof run3.id === "string" && run3.id.trim()) qaRun = run3;
16566
+ const run4 = JSON.parse(qaFile.content)?.latestRun;
16567
+ if (run4 && typeof run4.id === "string" && run4.id.trim()) qaRun = run4;
16196
16568
  } catch {
16197
16569
  }
16198
16570
  }
@@ -16258,25 +16630,25 @@ var previewCommand = new Command("preview").description(
16258
16630
 
16259
16631
  // src/commands/harness.ts
16260
16632
  init_source();
16261
- var import_node_fs10 = __toESM(require("node:fs"));
16262
- var import_node_path8 = __toESM(require("node:path"));
16633
+ var import_node_fs11 = __toESM(require("node:fs"));
16634
+ var import_node_path9 = __toESM(require("node:path"));
16263
16635
  var import_node_child_process6 = require("node:child_process");
16264
- function slugify(value, fallback2 = "variation") {
16265
- const slug = value.normalize("NFKD").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
16636
+ function slugify(value2, fallback2 = "variation") {
16637
+ const slug = value2.normalize("NFKD").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
16266
16638
  return slug || fallback2;
16267
16639
  }
16268
16640
  function readJson(filePath) {
16269
- if (!import_node_fs10.default.existsSync(filePath)) return null;
16270
- return JSON.parse(import_node_fs10.default.readFileSync(filePath, "utf8"));
16641
+ if (!import_node_fs11.default.existsSync(filePath)) return null;
16642
+ return JSON.parse(import_node_fs11.default.readFileSync(filePath, "utf8"));
16271
16643
  }
16272
16644
  function resolveFolder(folder) {
16273
- return import_node_path8.default.resolve(process.cwd(), folder);
16645
+ return import_node_path9.default.resolve(process.cwd(), folder);
16274
16646
  }
16275
16647
  function experimentConfigPath(folder) {
16276
- return import_node_path8.default.join(folder, "experiment.config.json");
16648
+ return import_node_path9.default.join(folder, "experiment.config.json");
16277
16649
  }
16278
16650
  function apexConfigPath(folder) {
16279
- return import_node_path8.default.join(folder, "apex.harness.json");
16651
+ return import_node_path9.default.join(folder, "apex.harness.json");
16280
16652
  }
16281
16653
  function loadHarnessConfig(folder) {
16282
16654
  const experiment2 = readJson(experimentConfigPath(folder)) ?? {};
@@ -16285,8 +16657,8 @@ function loadHarnessConfig(folder) {
16285
16657
  const targetOrigin = experiment2.targetOrigin?.replace(/\/+$/, "") ?? "";
16286
16658
  const defaultPath = experiment2.defaultPath ?? "/";
16287
16659
  const targetUrl = targetOrigin ? new URL(defaultPath, targetOrigin).toString() : "";
16288
- const id = experiment2.id ?? import_node_path8.default.basename(import_node_path8.default.dirname(folder));
16289
- const variation = import_node_path8.default.basename(folder);
16660
+ const id = experiment2.id ?? import_node_path9.default.basename(import_node_path9.default.dirname(folder));
16661
+ const variation = import_node_path9.default.basename(folder);
16290
16662
  return {
16291
16663
  experiment: experiment2,
16292
16664
  apex: {
@@ -16320,37 +16692,37 @@ function runAbHarness(args, cwd) {
16320
16692
  function artifactPaths(folder, experiment2) {
16321
16693
  const variant = experiment2.variant;
16322
16694
  if (variant?.endsWith(".bundle.js")) {
16323
- const jsPath = import_node_path8.default.join(folder, variant.replace(/\.bundle\.js$/, ".js"));
16324
- const cssPath = import_node_path8.default.join(folder, variant.replace(/\.bundle\.js$/, ".css"));
16325
- return { jsPath, cssPath: import_node_fs10.default.existsSync(cssPath) ? cssPath : null };
16695
+ const jsPath = import_node_path9.default.join(folder, variant.replace(/\.bundle\.js$/, ".js"));
16696
+ const cssPath = import_node_path9.default.join(folder, variant.replace(/\.bundle\.js$/, ".css"));
16697
+ return { jsPath, cssPath: import_node_fs11.default.existsSync(cssPath) ? cssPath : null };
16326
16698
  }
16327
- const buildDir = import_node_path8.default.join(folder, "build");
16328
- const files = import_node_fs10.default.existsSync(buildDir) ? import_node_fs10.default.readdirSync(buildDir) : [];
16699
+ const buildDir = import_node_path9.default.join(folder, "build");
16700
+ const files = import_node_fs11.default.existsSync(buildDir) ? import_node_fs11.default.readdirSync(buildDir) : [];
16329
16701
  const js = files.find(
16330
16702
  (file) => file.endsWith(".js") && !file.endsWith(".bundle.js") && !file.endsWith(".min.js")
16331
16703
  );
16332
16704
  const css = files.find((file) => file.endsWith(".css") && !file.endsWith(".min.css"));
16333
16705
  if (!js) throw new Error("Could not find harness JS artifact in build/");
16334
16706
  return {
16335
- jsPath: import_node_path8.default.join(buildDir, js),
16336
- cssPath: css ? import_node_path8.default.join(buildDir, css) : null
16707
+ jsPath: import_node_path9.default.join(buildDir, js),
16708
+ cssPath: css ? import_node_path9.default.join(buildDir, css) : null
16337
16709
  };
16338
16710
  }
16339
16711
  async function uploadVersion(params) {
16340
16712
  const creds = requireCredentials();
16341
16713
  const { jsPath, cssPath } = artifactPaths(params.folder, params.experiment);
16342
- if (!import_node_fs10.default.existsSync(jsPath)) throw new Error(`Missing JS artifact: ${jsPath}`);
16714
+ if (!import_node_fs11.default.existsSync(jsPath)) throw new Error(`Missing JS artifact: ${jsPath}`);
16343
16715
  const form = new FormData();
16344
16716
  form.append(
16345
16717
  "js",
16346
- new Blob([import_node_fs10.default.readFileSync(jsPath)], { type: "application/javascript" }),
16347
- import_node_path8.default.basename(jsPath)
16718
+ new Blob([import_node_fs11.default.readFileSync(jsPath)], { type: "application/javascript" }),
16719
+ import_node_path9.default.basename(jsPath)
16348
16720
  );
16349
- if (cssPath && import_node_fs10.default.existsSync(cssPath) && import_node_fs10.default.statSync(cssPath).size > 0) {
16721
+ if (cssPath && import_node_fs11.default.existsSync(cssPath) && import_node_fs11.default.statSync(cssPath).size > 0) {
16350
16722
  form.append(
16351
16723
  "css",
16352
- new Blob([import_node_fs10.default.readFileSync(cssPath)], { type: "text/css" }),
16353
- import_node_path8.default.basename(cssPath)
16724
+ new Blob([import_node_fs11.default.readFileSync(cssPath)], { type: "text/css" }),
16725
+ import_node_path9.default.basename(cssPath)
16354
16726
  );
16355
16727
  }
16356
16728
  form.append(
@@ -16359,8 +16731,8 @@ async function uploadVersion(params) {
16359
16731
  ...params.apex,
16360
16732
  resetJs: cleanupResetJs(params.experiment),
16361
16733
  builtAt: (/* @__PURE__ */ new Date()).toISOString(),
16362
- jsPath: import_node_path8.default.relative(params.folder, jsPath),
16363
- cssPath: cssPath ? import_node_path8.default.relative(params.folder, cssPath) : null
16734
+ jsPath: import_node_path9.default.relative(params.folder, jsPath),
16735
+ cssPath: cssPath ? import_node_path9.default.relative(params.folder, cssPath) : null
16364
16736
  })
16365
16737
  );
16366
16738
  const res = await uploadHarnessVersion(creds, params.session.sessionId, form);
@@ -16402,11 +16774,11 @@ var harnessCommand = new Command("harness").description(
16402
16774
  harnessCommand.command("init").argument("<folder>", "AB Test Harness experiment folder").option("--force", "Overwrite existing apex.harness.json").action(function action24(folderArg, opts) {
16403
16775
  const folder = resolveFolder(folderArg);
16404
16776
  const targetPath = apexConfigPath(folder);
16405
- if (import_node_fs10.default.existsSync(targetPath) && !opts.force) {
16777
+ if (import_node_fs11.default.existsSync(targetPath) && !opts.force) {
16406
16778
  fail(this, `${targetPath} already exists. Use --force to overwrite.`, EXIT_CODES.conflict);
16407
16779
  }
16408
16780
  const { apex } = loadHarnessConfig(folder);
16409
- import_node_fs10.default.writeFileSync(targetPath, `${JSON.stringify(apex, null, 2)}
16781
+ import_node_fs11.default.writeFileSync(targetPath, `${JSON.stringify(apex, null, 2)}
16410
16782
  `, "utf8");
16411
16783
  output(
16412
16784
  this,
@@ -16472,7 +16844,7 @@ harnessCommand.command("dev").argument("<folder>", "AB Test Harness experiment f
16472
16844
  }
16473
16845
  };
16474
16846
  let debounce = null;
16475
- const watcher = import_node_fs10.default.watch(folder, { recursive: true }, (_event, filename) => {
16847
+ const watcher = import_node_fs11.default.watch(folder, { recursive: true }, (_event, filename) => {
16476
16848
  const name = String(filename ?? "");
16477
16849
  if (!name || name.startsWith("build/") || name.includes("/build/")) return;
16478
16850
  if (debounce) clearTimeout(debounce);
@@ -16515,8 +16887,8 @@ harnessCommand.command("publish").argument("<folder>", "AB Test Harness experime
16515
16887
  // src/commands/operator.ts
16516
16888
  init_source();
16517
16889
  var import_node_crypto7 = __toESM(require("node:crypto"));
16518
- var import_node_fs11 = __toESM(require("node:fs"));
16519
- var import_node_path9 = __toESM(require("node:path"));
16890
+ var import_node_fs12 = __toESM(require("node:fs"));
16891
+ var import_node_path10 = __toESM(require("node:path"));
16520
16892
  var CANONICAL_FILE_RE = /^(src\/(?:index\.js|index\.scss|info\.js|mutations\.json)|experiment\.config\.json|metrics\.json|targeting\.json|dev-doc\.md|brand\.md|goals\/[a-zA-Z0-9_.-]+\.json|segments\/[a-zA-Z0-9_.-]+\.json|network\/[a-zA-Z0-9_.-]+\.recipe\.json)$/;
16521
16893
  function workspaceIssueFile(field, evidence) {
16522
16894
  if (field === "custom_js" || field === "reset_js") return "src/index.js";
@@ -16530,9 +16902,9 @@ function workspaceIssueFile(field, evidence) {
16530
16902
  if (field === "anti_flicker_selectors") return "experiment.config.json";
16531
16903
  return null;
16532
16904
  }
16533
- function formatWorkspaceBlockingIssues(value) {
16534
- if (!value || typeof value !== "object") return null;
16535
- const issues = value.blockingIssues;
16905
+ function formatWorkspaceBlockingIssues(value2) {
16906
+ if (!value2 || typeof value2 !== "object") return null;
16907
+ const issues = value2.blockingIssues;
16536
16908
  if (!Array.isArray(issues) || issues.length === 0) return null;
16537
16909
  return issues.map((rawIssue, index) => {
16538
16910
  const issue = rawIssue && typeof rawIssue === "object" ? rawIssue : {};
@@ -16564,39 +16936,39 @@ function formatHarnessRefusal(message, details) {
16564
16936
  return issues ? `${message}
16565
16937
  ${issues}` : message;
16566
16938
  }
16567
- function sha256Text2(value) {
16568
- return import_node_crypto7.default.createHash("sha256").update(value).digest("hex");
16939
+ function sha256Text2(value2) {
16940
+ return import_node_crypto7.default.createHash("sha256").update(value2).digest("hex");
16569
16941
  }
16570
- function byteLength(value) {
16571
- return Buffer.byteLength(value, "utf8");
16942
+ function byteLength(value2) {
16943
+ return Buffer.byteLength(value2, "utf8");
16572
16944
  }
16573
- function slugify2(value, fallback2 = "operator") {
16574
- const slug = value.normalize("NFKD").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
16945
+ function slugify2(value2, fallback2 = "operator") {
16946
+ const slug = value2.normalize("NFKD").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
16575
16947
  return slug || fallback2;
16576
16948
  }
16577
16949
  function folderPath2(folder) {
16578
- return import_node_path9.default.resolve(process.cwd(), folder);
16950
+ return import_node_path10.default.resolve(process.cwd(), folder);
16579
16951
  }
16580
16952
  function apexDir(folder) {
16581
- return import_node_path9.default.join(folder, ".apex");
16953
+ return import_node_path10.default.join(folder, ".apex");
16582
16954
  }
16583
16955
  function sessionPath(folder) {
16584
- return import_node_path9.default.join(apexDir(folder), "session.json");
16956
+ return import_node_path10.default.join(apexDir(folder), "session.json");
16585
16957
  }
16586
16958
  function readJsonFile(filePath) {
16587
- if (!import_node_fs11.default.existsSync(filePath)) return null;
16588
- return JSON.parse(import_node_fs11.default.readFileSync(filePath, "utf8"));
16959
+ if (!import_node_fs12.default.existsSync(filePath)) return null;
16960
+ return JSON.parse(import_node_fs12.default.readFileSync(filePath, "utf8"));
16589
16961
  }
16590
- function writeJsonFile(filePath, value) {
16591
- import_node_fs11.default.mkdirSync(import_node_path9.default.dirname(filePath), { recursive: true });
16592
- import_node_fs11.default.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}
16962
+ function writeJsonFile(filePath, value2) {
16963
+ import_node_fs12.default.mkdirSync(import_node_path10.default.dirname(filePath), { recursive: true });
16964
+ import_node_fs12.default.writeFileSync(filePath, `${JSON.stringify(value2, null, 2)}
16593
16965
  `, "utf8");
16594
16966
  }
16595
16967
  function readSession(folder) {
16596
16968
  const session = readJsonFile(sessionPath(folder));
16597
16969
  if (!session) {
16598
- const filesystemMarker = import_node_path9.default.join(apexDir(folder), "experiment-filesystem.json");
16599
- if (import_node_fs11.default.existsSync(filesystemMarker)) {
16970
+ const filesystemMarker = import_node_path10.default.join(apexDir(folder), "experiment-filesystem.json");
16971
+ if (import_node_fs12.default.existsSync(filesystemMarker)) {
16600
16972
  throw new CliError(
16601
16973
  "This folder is an experiment filesystem (apex experiments \u2026), not an operator workspace \u2014 run `apex experiments plan` here, or `apex workspace checkout` into a different folder.",
16602
16974
  EXIT_CODES.general,
@@ -16626,9 +16998,9 @@ function readSession(folder) {
16626
16998
  return session;
16627
16999
  }
16628
17000
  function writeSession(folder, session) {
16629
- import_node_fs11.default.mkdirSync(apexDir(folder), { recursive: true });
16630
- import_node_fs11.default.writeFileSync(
16631
- import_node_path9.default.join(apexDir(folder), ".gitignore"),
17001
+ import_node_fs12.default.mkdirSync(apexDir(folder), { recursive: true });
17002
+ import_node_fs12.default.writeFileSync(
17003
+ import_node_path10.default.join(apexDir(folder), ".gitignore"),
16632
17004
  "session.json\nharness-session.json\n",
16633
17005
  "utf8"
16634
17006
  );
@@ -16652,15 +17024,15 @@ function listLocalWorkspaceFiles(folder) {
16652
17024
  "brand.md"
16653
17025
  ];
16654
17026
  function visit2(dir) {
16655
- if (!import_node_fs11.default.existsSync(dir)) return;
16656
- for (const entry of import_node_fs11.default.readdirSync(dir, { withFileTypes: true })) {
16657
- const absPath = import_node_path9.default.join(dir, entry.name);
17027
+ if (!import_node_fs12.default.existsSync(dir)) return;
17028
+ for (const entry of import_node_fs12.default.readdirSync(dir, { withFileTypes: true })) {
17029
+ const absPath = import_node_path10.default.join(dir, entry.name);
16658
17030
  if (entry.isDirectory()) {
16659
17031
  visit2(absPath);
16660
17032
  continue;
16661
17033
  }
16662
- const relativePath = validateCanonicalPath(import_node_path9.default.relative(folder, absPath));
16663
- const content = import_node_fs11.default.readFileSync(absPath, "utf8");
17034
+ const relativePath = validateCanonicalPath(import_node_path10.default.relative(folder, absPath));
17035
+ const content = import_node_fs12.default.readFileSync(absPath, "utf8");
16664
17036
  files.push({
16665
17037
  path: relativePath,
16666
17038
  absPath,
@@ -16670,11 +17042,11 @@ function listLocalWorkspaceFiles(folder) {
16670
17042
  });
16671
17043
  }
16672
17044
  }
16673
- for (const root of roots) visit2(import_node_path9.default.join(folder, root));
17045
+ for (const root of roots) visit2(import_node_path10.default.join(folder, root));
16674
17046
  for (const file of topLevel) {
16675
- const absPath = import_node_path9.default.join(folder, file);
16676
- if (!import_node_fs11.default.existsSync(absPath)) continue;
16677
- const content = import_node_fs11.default.readFileSync(absPath, "utf8");
17047
+ const absPath = import_node_path10.default.join(folder, file);
17048
+ if (!import_node_fs12.default.existsSync(absPath)) continue;
17049
+ const content = import_node_fs12.default.readFileSync(absPath, "utf8");
16678
17050
  files.push({
16679
17051
  path: file,
16680
17052
  absPath,
@@ -16698,7 +17070,7 @@ function validateJsonFiles(files) {
16698
17070
  }
16699
17071
  }
16700
17072
  function readExperimentConfig(folder) {
16701
- return readJsonFile(import_node_path9.default.join(folder, "experiment.config.json")) ?? {};
17073
+ return readJsonFile(import_node_path10.default.join(folder, "experiment.config.json")) ?? {};
16702
17074
  }
16703
17075
  function resolveVariation(exp, variationArg) {
16704
17076
  const variations = exp.variations ?? [];
@@ -16715,9 +17087,9 @@ function resolveVariation(exp, variationArg) {
16715
17087
  function writeWorkspaceFiles(folder, workspace) {
16716
17088
  for (const file of workspace.files) {
16717
17089
  const relativePath = validateCanonicalPath(file.path);
16718
- const absPath = import_node_path9.default.join(folder, relativePath);
16719
- import_node_fs11.default.mkdirSync(import_node_path9.default.dirname(absPath), { recursive: true });
16720
- import_node_fs11.default.writeFileSync(absPath, file.content, "utf8");
17090
+ const absPath = import_node_path10.default.join(folder, relativePath);
17091
+ import_node_fs12.default.mkdirSync(import_node_path10.default.dirname(absPath), { recursive: true });
17092
+ import_node_fs12.default.writeFileSync(absPath, file.content, "utf8");
16721
17093
  }
16722
17094
  }
16723
17095
  function sessionFromWorkspace(input) {
@@ -16785,10 +17157,10 @@ async function uploadCurrentArtifactVersion(folder, sessionId) {
16785
17157
  form.append(
16786
17158
  "js",
16787
17159
  new Blob([js.content], { type: "application/javascript" }),
16788
- import_node_path9.default.basename(js.path)
17160
+ import_node_path10.default.basename(js.path)
16789
17161
  );
16790
17162
  if (css && css.content.trim()) {
16791
- form.append("css", new Blob([css.content], { type: "text/css" }), import_node_path9.default.basename(css.path));
17163
+ form.append("css", new Blob([css.content], { type: "text/css" }), import_node_path10.default.basename(css.path));
16792
17164
  }
16793
17165
  form.append("manifest", JSON.stringify(manifest));
16794
17166
  const res = await uploadHarnessVersion(creds, sessionId, form);
@@ -16915,7 +17287,7 @@ operatorCommand.command("checkout").argument("<experiment-id>", "Apex experiment
16915
17287
  classifyStatus(workspace.status),
16916
17288
  workspace.errorDetails
16917
17289
  );
16918
- import_node_fs11.default.mkdirSync(folder, { recursive: true });
17290
+ import_node_fs12.default.mkdirSync(folder, { recursive: true });
16919
17291
  writeWorkspaceFiles(folder, workspace.data);
16920
17292
  writeSession(
16921
17293
  folder,
@@ -16982,8 +17354,8 @@ operatorCommand.command("dev").argument("[folder]", "Operator workspace folder",
16982
17354
  const session = readSession(folder);
16983
17355
  const harnessSession = await createExactHarnessSession(folder, session);
16984
17356
  const version = await uploadCurrentArtifactVersion(folder, harnessSession.sessionId);
16985
- import_node_fs11.default.mkdirSync(apexDir(folder), { recursive: true });
16986
- writeJsonFile(import_node_path9.default.join(apexDir(folder), "harness-session.json"), {
17357
+ import_node_fs12.default.mkdirSync(apexDir(folder), { recursive: true });
17358
+ writeJsonFile(import_node_path10.default.join(apexDir(folder), "harness-session.json"), {
16987
17359
  sessionId: harnessSession.sessionId,
16988
17360
  experimentId: harnessSession.experimentId,
16989
17361
  variationId: harnessSession.variationId,
@@ -17038,7 +17410,7 @@ operatorCommand.command("dev").argument("[folder]", "Operator workspace folder",
17038
17410
  }
17039
17411
  };
17040
17412
  let timer = null;
17041
- const watcher = import_node_fs11.default.watch(folder, { recursive: true }, (_event, filename) => {
17413
+ const watcher = import_node_fs12.default.watch(folder, { recursive: true }, (_event, filename) => {
17042
17414
  const name = String(filename ?? "");
17043
17415
  if (!name || name.startsWith(".apex/") || name.includes("/.apex/")) return;
17044
17416
  if (timer) clearTimeout(timer);
@@ -17073,17 +17445,17 @@ operatorCommand.command("publish").argument("[folder]", "Operator workspace fold
17073
17445
 
17074
17446
  // src/commands/roadmap.ts
17075
17447
  init_source();
17076
- var import_node_fs12 = __toESM(require("node:fs"));
17077
- var import_node_path10 = __toESM(require("node:path"));
17448
+ var import_node_fs13 = __toESM(require("node:fs"));
17449
+ var import_node_path11 = __toESM(require("node:path"));
17078
17450
  var LEGACY_OUTPUT_REMOVAL_VERSION = "0.7.0";
17079
- function truncate(value, max) {
17080
- const text2 = typeof value === "string" ? value : value == null ? "" : String(value);
17451
+ function truncate(value2, max) {
17452
+ const text2 = typeof value2 === "string" ? value2 : value2 == null ? "" : String(value2);
17081
17453
  if (text2.length <= max) return text2;
17082
17454
  return `${text2.slice(0, Math.max(0, max - 3))}...`;
17083
17455
  }
17084
- function score(value) {
17085
- if (typeof value !== "number" || !Number.isFinite(value)) return "-";
17086
- return Number.isInteger(value) ? String(value) : value.toFixed(1);
17456
+ function score(value2) {
17457
+ if (typeof value2 !== "number" || !Number.isFinite(value2)) return "-";
17458
+ return Number.isInteger(value2) ? String(value2) : value2.toFixed(1);
17087
17459
  }
17088
17460
  function experimentState(item) {
17089
17461
  const linked = item.linked_experiments ?? [];
@@ -17184,9 +17556,9 @@ var handoff = new Command("handoff").description(
17184
17556
  `;
17185
17557
  const outputData = opts.instructions ? { roadmapItemId: id, instructions: String(payload.instructions ?? "") } : payload;
17186
17558
  if (opts.out || opts.output) {
17187
- const target = import_node_path10.default.resolve(process.cwd(), opts.out ?? opts.output);
17188
- import_node_fs12.default.mkdirSync(import_node_path10.default.dirname(target), { recursive: true });
17189
- import_node_fs12.default.writeFileSync(target, rendered, "utf8");
17559
+ const target = import_node_path11.default.resolve(process.cwd(), opts.out ?? opts.output);
17560
+ import_node_fs13.default.mkdirSync(import_node_path11.default.dirname(target), { recursive: true });
17561
+ import_node_fs13.default.writeFileSync(target, rendered, "utf8");
17190
17562
  output(this, { path: target, roadmapItemId: id }, () => printSuccess(`Wrote ${target}`));
17191
17563
  return;
17192
17564
  }
@@ -17200,13 +17572,13 @@ roadmapCommand.addCommand(handoff);
17200
17572
 
17201
17573
  // src/commands/tracking.ts
17202
17574
  init_source();
17203
- var import_node_fs13 = __toESM(require("node:fs"));
17204
- var import_node_path11 = __toESM(require("node:path"));
17575
+ var import_node_fs14 = __toESM(require("node:fs"));
17576
+ var import_node_path12 = __toESM(require("node:path"));
17205
17577
  function normalizeUrl(input) {
17206
- const value = input.trim();
17207
- if (!value) throw new Error("URL cannot be empty");
17578
+ const value2 = input.trim();
17579
+ if (!value2) throw new Error("URL cannot be empty");
17208
17580
  try {
17209
- return new URL(value.includes("://") ? value : `https://${value}`);
17581
+ return new URL(value2.includes("://") ? value2 : `https://${value2}`);
17210
17582
  } catch {
17211
17583
  throw new Error(`Invalid URL: ${input}`);
17212
17584
  }
@@ -17248,14 +17620,14 @@ function commonCookieDomain(hostnames) {
17248
17620
  }
17249
17621
  var DEFAULT_MANIFEST_PATH = ".apex/tracking-manifest.json";
17250
17622
  function readManifest(filePath) {
17251
- if (!import_node_fs13.default.existsSync(filePath)) throw new Error(`Manifest not found: ${filePath}`);
17252
- const parsed = JSON.parse(import_node_fs13.default.readFileSync(filePath, "utf8"));
17623
+ if (!import_node_fs14.default.existsSync(filePath)) throw new Error(`Manifest not found: ${filePath}`);
17624
+ const parsed = JSON.parse(import_node_fs14.default.readFileSync(filePath, "utf8"));
17253
17625
  if (parsed.schemaVersion !== 1) throw new Error("Unsupported tracking manifest schema");
17254
17626
  return parsed;
17255
17627
  }
17256
17628
  function writeManifest(filePath, manifest) {
17257
- import_node_fs13.default.mkdirSync(import_node_path11.default.dirname(filePath), { recursive: true });
17258
- import_node_fs13.default.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}
17629
+ import_node_fs14.default.mkdirSync(import_node_path12.default.dirname(filePath), { recursive: true });
17630
+ import_node_fs14.default.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}
17259
17631
  `, "utf8");
17260
17632
  }
17261
17633
  function buildSnippet(shopId, endpoint) {
@@ -17322,7 +17694,7 @@ function buildManifest(urlArgs, opts) {
17322
17694
  };
17323
17695
  }
17324
17696
  function manifestPath(input) {
17325
- return import_node_path11.default.resolve(process.cwd(), input ?? DEFAULT_MANIFEST_PATH);
17697
+ return import_node_path12.default.resolve(process.cwd(), input ?? DEFAULT_MANIFEST_PATH);
17326
17698
  }
17327
17699
  function printManifestPlan(manifest) {
17328
17700
  console.log(source_default.bold("Apex tracking ingestion plan"));
@@ -17472,7 +17844,7 @@ trackingCommand.command("latency").description("Measure forced-preview versus na
17472
17844
  ["natural", summary.natural],
17473
17845
  ...summary.qa_session ? [["qa_session", summary.qa_session]] : []
17474
17846
  ];
17475
- const ms = (value) => value === null ? "-" : String(Math.round(value));
17847
+ const ms = (value2) => value2 === null ? "-" : String(Math.round(value2));
17476
17848
  printTable(
17477
17849
  [
17478
17850
  "Bucket",
@@ -17508,7 +17880,7 @@ trackingCommand.command("latency").description("Measure forced-preview versus na
17508
17880
  });
17509
17881
 
17510
17882
  // src/commands/events.ts
17511
- var import_node_fs14 = __toESM(require("node:fs"));
17883
+ var import_node_fs15 = __toESM(require("node:fs"));
17512
17884
  var maxWindowMs = 31 * 864e5;
17513
17885
  function correctedWindowStep(opts, nowMs) {
17514
17886
  const fromMs = Date.parse(opts.from);
@@ -17519,7 +17891,7 @@ function correctedWindowStep(opts, nowMs) {
17519
17891
  why: "No valid export window exists from this start; --to must be after --from and at or before now."
17520
17892
  };
17521
17893
  }
17522
- const quote = (value) => `'${value.replace(/'/g, "'\\''")}'`;
17894
+ const quote = (value2) => `'${value2.replace(/'/g, "'\\''")}'`;
17523
17895
  const args = [
17524
17896
  "apex events export",
17525
17897
  `--from ${quote(opts.from)}`,
@@ -17611,7 +17983,7 @@ eventsCommand.command("export").description(
17611
17983
  if (opts.jsonSummary && !opts.out) {
17612
17984
  fail(command, "--json-summary requires --out for the event rows", EXIT_CODES.validation);
17613
17985
  }
17614
- const output2 = opts.out ? import_node_fs14.default.createWriteStream(opts.out, { flags: "wx" }) : null;
17986
+ const output2 = opts.out ? import_node_fs15.default.createWriteStream(opts.out, { flags: "wx" }) : null;
17615
17987
  let outputError = null;
17616
17988
  output2?.on("error", (error) => {
17617
17989
  outputError = error;
@@ -17659,7 +18031,7 @@ eventsCommand.command("export").description(
17659
18031
  from: opts.from,
17660
18032
  to: opts.to,
17661
18033
  experiment: opts.experiment,
17662
- eventTypes: opts.eventType?.split(",").map((value) => value.trim()).filter(Boolean),
18034
+ eventTypes: opts.eventType?.split(",").map((value2) => value2.trim()).filter(Boolean),
17663
18035
  qa: opts.qa,
17664
18036
  limit,
17665
18037
  cursor
@@ -17750,7 +18122,7 @@ eventsCommand.command("export").description(
17750
18122
  } catch (error) {
17751
18123
  if (output2 && opts.out) {
17752
18124
  output2.destroy();
17753
- import_node_fs14.default.rmSync(opts.out, { force: true });
18125
+ import_node_fs15.default.rmSync(opts.out, { force: true });
17754
18126
  const suffix = `The partial file ${opts.out} was removed.`;
17755
18127
  if (error instanceof CliError)
17756
18128
  throw new CliError(
@@ -17771,8 +18143,8 @@ eventsCommand.command("export").description(
17771
18143
 
17772
18144
  // src/commands/goals.ts
17773
18145
  init_source();
17774
- var import_node_fs15 = __toESM(require("node:fs"));
17775
- var import_node_path12 = __toESM(require("node:path"));
18146
+ var import_node_fs16 = __toESM(require("node:fs"));
18147
+ var import_node_path13 = __toESM(require("node:path"));
17776
18148
 
17777
18149
  // ../sdk/src/safe-regex.ts
17778
18150
  var MAX_PATTERN_LENGTH = 1024;
@@ -17819,8 +18191,10 @@ var GOALS_PAGE_SIZE = 100;
17819
18191
  var MAX_GOAL_PAGES = 100;
17820
18192
  var MAX_PAGE_LOOKUP_PAGES = 1e3;
17821
18193
  var UUID_V1_TO_V8 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
17822
- var PAGE_OPTION_DESCRIPTION = "Page scope: page ID or page key (required for click goals, optional for pageview; a page key needs pages:read; a page ID does not; list pages with apex pages list)";
17823
- var PAGE_NEXT_STEPS = [{ command: "apex pages list", why: "Find the page ID or key." }];
18194
+ var PAGE_OPTION_DESCRIPTION = "Page scope: page ID or page key with lookup (required for click goals, optional for pageview; a page key needs pages:read; a page ID does not; list pages with apex pages list)";
18195
+ var PAGE_REQUIRED_MESSAGE = "Click goals need a page: pass --page-id <page ID> or --page <page ID or key> (list pages with apex pages list)";
18196
+ var PAGE_NEXT_STEPS = [{ command: "apex pages list", why: PAGE_REQUIRED_MESSAGE }];
18197
+ var PAGE_ID_OPTION_DESCRIPTION = "Page ID only (UUID; no lookup or pages:read permission needed)";
17824
18198
  var NON_QUANTIFIABLE = /* @__PURE__ */ new Set(["(", "|", "^", "$"]);
17825
18199
  function urlPatternDependence(pattern) {
17826
18200
  let fragment = false;
@@ -17949,10 +18323,10 @@ function printSuggestions(data) {
17949
18323
  for (const trigger of data.triggers) console.log(`- ${trigger.page}: ${trigger.description}`);
17950
18324
  }
17951
18325
  }
17952
- function parseCapture(command, value) {
17953
- if (!value) return void 0;
17954
- const source = value.startsWith("@") ? value.slice(1) : value;
17955
- const text2 = value.startsWith("@") || import_node_fs15.default.existsSync(source) ? import_node_fs15.default.readFileSync(import_node_path12.default.resolve(process.cwd(), source), "utf8") : value;
18326
+ function parseCapture(command, value2) {
18327
+ if (!value2) return void 0;
18328
+ const source = value2.startsWith("@") ? value2.slice(1) : value2;
18329
+ const text2 = value2.startsWith("@") || import_node_fs16.default.existsSync(source) ? import_node_fs16.default.readFileSync(import_node_path13.default.resolve(process.cwd(), source), "utf8") : value2;
17956
18330
  try {
17957
18331
  return JSON.parse(text2);
17958
18332
  } catch (error) {
@@ -17961,10 +18335,10 @@ function parseCapture(command, value) {
17961
18335
  }
17962
18336
  }
17963
18337
  function cleanBody(input) {
17964
- return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
18338
+ return Object.fromEntries(Object.entries(input).filter(([, value2]) => value2 !== void 0));
17965
18339
  }
17966
- async function resolveGoalPageId(command, creds, value) {
17967
- if (UUID_V1_TO_V8.test(value)) return value;
18340
+ async function resolveGoalPageId(command, creds, value2) {
18341
+ if (UUID_V1_TO_V8.test(value2)) return value2;
17968
18342
  let cursor;
17969
18343
  for (let page = 0; page < MAX_PAGE_LOOKUP_PAGES; page += 1) {
17970
18344
  const response = await requestTargetingResource(
@@ -18002,7 +18376,7 @@ async function resolveGoalPageId(command, creds, value) {
18002
18376
  }
18003
18377
  if (!("data" in response.data) || !Array.isArray(response.data.data)) break;
18004
18378
  const pageData = response.data;
18005
- const match2 = pageData.data.find((item) => item.key === value);
18379
+ const match2 = pageData.data.find((item) => item.key === value2);
18006
18380
  if (match2) return match2.id;
18007
18381
  const next = pageData.pagination?.next_cursor ?? null;
18008
18382
  if (!pageData.pagination?.has_more || !next || next === cursor) break;
@@ -18010,11 +18384,29 @@ async function resolveGoalPageId(command, creds, value) {
18010
18384
  }
18011
18385
  fail(
18012
18386
  command,
18013
- `No active page with key ${value} in this shop; list pages with apex pages list`,
18387
+ `No active page with key ${value2} in this shop; list pages with apex pages list`,
18014
18388
  EXIT_CODES.validation,
18015
18389
  { code: "page_not_found", next_steps: PAGE_NEXT_STEPS }
18016
18390
  );
18017
18391
  }
18392
+ async function goalPageOption(command, creds, opts, allowNull = false) {
18393
+ if (opts.page !== void 0 && opts.pageId !== void 0) {
18394
+ fail(command, "Use either --page or --page-id, not both", EXIT_CODES.validation, {
18395
+ code: "page_options_conflict"
18396
+ });
18397
+ }
18398
+ if (typeof opts.pageId === "string") {
18399
+ if (allowNull && opts.pageId === "null") return null;
18400
+ if (!UUID_V1_TO_V8.test(opts.pageId)) {
18401
+ fail(command, "--page-id must be a page UUID", EXIT_CODES.validation, {
18402
+ code: "page_id_invalid"
18403
+ });
18404
+ }
18405
+ return opts.pageId;
18406
+ }
18407
+ if (allowNull && opts.page === "null") return null;
18408
+ return typeof opts.page === "string" ? resolveGoalPageId(command, creds, opts.page) : void 0;
18409
+ }
18018
18410
  function printGoal(goal) {
18019
18411
  console.log();
18020
18412
  console.log(source_default.bold(goal.name));
@@ -18037,6 +18429,7 @@ var list2 = new Command("list").alias("ls").description("List goals").option("-n
18037
18429
  if (!res.ok || !res.data) {
18038
18430
  fail(this, res.error ?? "Failed to list goals", classifyStatus(res.status), res.errorDetails);
18039
18431
  }
18432
+ warnReplacementCharacters("goals", res.data, ["data[].name"]);
18040
18433
  output(this, res.data, () => {
18041
18434
  if (res.data.data.length === 0) {
18042
18435
  console.log(source_default.dim("No goals found."));
@@ -18055,22 +18448,25 @@ var list2 = new Command("list").alias("ls").description("List goals").option("-n
18055
18448
  );
18056
18449
  });
18057
18450
  });
18058
- var create2 = new Command("create").description("Create a goal").requiredOption("--name <name>", "Goal name").requiredOption("--type <type>", "click|pageview|custom|revenue").option("--measurement <measurement>", "conversion|value", "conversion").option("--value-property <property>", "Numeric event property for value goals").option("--value-unit <unit>", "currency|number|duration_seconds|percent").option("--selector <selector>", "CSS selector for click goals").option("--text-match <text>", "Text match for click goals").option("--url-pattern <pattern>", "URL pattern for pageview goals").option("--event-name <name>", "Event name for custom goals").option("--page <id-or-key>", PAGE_OPTION_DESCRIPTION).option("--count-once", "Count only once per visitor").option("--capture <json-or-path>", "Capture JSON array or @path").action(async function action35(opts) {
18451
+ var create2 = new Command("create").description("Create a goal").requiredOption("--name <name>", "Goal name").requiredOption("--type <type>", "click|pageview|custom|revenue").option("--measurement <measurement>", "conversion|value", "conversion").option("--value-property <property>", "Numeric event property for value goals").option("--value-unit <unit>", "currency|number|duration_seconds|percent").option("--selector <selector>", "CSS selector for click goals").option("--text-match <text>", "Text match for click goals").option("--url-pattern <pattern>", "URL pattern for pageview goals").option("--event-name <name>", "Event name for custom goals").option("--page <id-or-key>", PAGE_OPTION_DESCRIPTION).option("--page-id <id>", PAGE_ID_OPTION_DESCRIPTION).option("--count-once", "Count only once per visitor").option("--capture <json-or-path>", "Capture JSON array or @path").action(async function action35(opts) {
18059
18452
  const creds = requireCredentials();
18060
- if (opts.type === "click" && !opts.page) {
18061
- fail(
18062
- this,
18063
- "Click goals need a page: pass --page <page id or key> (list pages with apex pages list)",
18064
- EXIT_CODES.validation,
18065
- { code: "page_required", next_steps: PAGE_NEXT_STEPS }
18066
- );
18453
+ if (opts.page !== void 0 && opts.pageId !== void 0) {
18454
+ fail(this, "Use either --page or --page-id, not both", EXIT_CODES.validation, {
18455
+ code: "page_options_conflict"
18456
+ });
18457
+ }
18458
+ if (opts.type === "click" && !opts.page && !opts.pageId) {
18459
+ fail(this, PAGE_REQUIRED_MESSAGE, EXIT_CODES.validation, {
18460
+ code: "page_required",
18461
+ next_steps: PAGE_NEXT_STEPS
18462
+ });
18067
18463
  }
18068
- if ((opts.type === "custom" || opts.type === "revenue") && opts.page) {
18069
- fail(this, `--page is not supported for ${opts.type} goals`, EXIT_CODES.validation, {
18464
+ if ((opts.type === "custom" || opts.type === "revenue") && (opts.page || opts.pageId)) {
18465
+ fail(this, `Page scope is not supported for ${opts.type} goals`, EXIT_CODES.validation, {
18070
18466
  code: "page_not_supported"
18071
18467
  });
18072
18468
  }
18073
- const pageId = typeof opts.page === "string" ? await resolveGoalPageId(this, creds, opts.page) : void 0;
18469
+ const pageId = await goalPageOption(this, creds, opts);
18074
18470
  const body5 = cleanBody({
18075
18471
  name: opts.name,
18076
18472
  type: opts.type,
@@ -18094,14 +18490,15 @@ var create2 = new Command("create").description("Create a goal").requiredOption(
18094
18490
  res.errorDetails
18095
18491
  );
18096
18492
  }
18493
+ warnReplacementCharacters("goal", res.data, ["name"]);
18097
18494
  output(this, res.data, () => printGoal(res.data));
18098
18495
  });
18099
18496
  var update2 = new Command("update").description("Update a goal").argument("<id>", "Goal ID").option("--name <name>", "Goal name").option("--type <type>", "click|pageview|custom|revenue").option("--measurement <measurement>", "conversion|value").option(
18100
18497
  "--value-property <property>",
18101
18498
  "Numeric event property for value goals; pass null to clear"
18102
- ).option("--value-unit <unit>", "currency|number|duration_seconds|percent; pass null to clear").option("--selector <selector>", "CSS selector for click goals").option("--text-match <text>", "Text match for click goals").option("--url-pattern <pattern>", "URL pattern for pageview goals").option("--event-name <name>", "Event name for custom goals").option("--page <id-or-key>", `${PAGE_OPTION_DESCRIPTION}; pass null to clear`).option("--count-once", "Count only once per visitor").option("--no-count-once", "Do not count only once per visitor").option("--capture <json-or-path>", "Capture JSON array or @path").action(async function action36(id, opts) {
18499
+ ).option("--value-unit <unit>", "currency|number|duration_seconds|percent; pass null to clear").option("--selector <selector>", "CSS selector for click goals").option("--text-match <text>", "Text match for click goals").option("--url-pattern <pattern>", "URL pattern for pageview goals").option("--event-name <name>", "Event name for custom goals").option("--page <id-or-key>", `${PAGE_OPTION_DESCRIPTION}; pass null to clear`).option("--page-id <id>", `${PAGE_ID_OPTION_DESCRIPTION}; pass null to clear`).option("--count-once", "Count only once per visitor").option("--no-count-once", "Do not count only once per visitor").option("--capture <json-or-path>", "Capture JSON array or @path").action(async function action36(id, opts) {
18103
18500
  const creds = requireCredentials();
18104
- const pageId = opts.page === "null" ? null : typeof opts.page === "string" ? await resolveGoalPageId(this, creds, opts.page) : void 0;
18501
+ const pageId = await goalPageOption(this, creds, opts, true);
18105
18502
  const body5 = cleanBody({
18106
18503
  name: opts.name,
18107
18504
  type: opts.type,
@@ -18128,6 +18525,7 @@ var update2 = new Command("update").description("Update a goal").argument("<id>"
18128
18525
  res.errorDetails
18129
18526
  );
18130
18527
  }
18528
+ warnReplacementCharacters("goal", res.data, ["name"]);
18131
18529
  output(this, res.data, () => printGoal(res.data));
18132
18530
  });
18133
18531
  var test = new Command("test").description("Dry run a goal URL pattern against traffic").option(
@@ -18235,8 +18633,8 @@ var suggest = new Command("suggest").description("Suggest goals and funnels from
18235
18633
  "Confirmation required; rerun with --yes to create suggested goals.",
18236
18634
  EXIT_CODES.confirmationRequired
18237
18635
  );
18238
- const readline3 = await import("node:readline");
18239
- const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
18636
+ const readline4 = await import("node:readline");
18637
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
18240
18638
  const answer = await new Promise(
18241
18639
  (resolve2) => rl.question(source_default.yellow("Create this goal? [y/N] "), resolve2)
18242
18640
  );
@@ -18275,12 +18673,12 @@ Examples:
18275
18673
 
18276
18674
  // src/commands/funnels.ts
18277
18675
  init_source();
18278
- var import_node_fs16 = __toESM(require("node:fs"));
18279
- var import_node_path13 = __toESM(require("node:path"));
18676
+ var import_node_fs17 = __toESM(require("node:fs"));
18677
+ var import_node_path14 = __toESM(require("node:path"));
18280
18678
  function readSteps(command, file) {
18281
18679
  let parsed;
18282
18680
  try {
18283
- parsed = JSON.parse(import_node_fs16.default.readFileSync(import_node_path13.default.resolve(process.cwd(), file), "utf8"));
18681
+ parsed = JSON.parse(import_node_fs17.default.readFileSync(import_node_path14.default.resolve(process.cwd(), file), "utf8"));
18284
18682
  } catch (error) {
18285
18683
  fail(
18286
18684
  command,
@@ -18410,8 +18808,8 @@ function printEnvironments(items) {
18410
18808
  [24, 32, 8, 64]
18411
18809
  );
18412
18810
  }
18413
- function normalizeEnvironmentDomain(value) {
18414
- const candidate = value.trim().toLowerCase();
18811
+ function normalizeEnvironmentDomain(value2) {
18812
+ const candidate = value2.trim().toLowerCase();
18415
18813
  if (!candidate) return "";
18416
18814
  try {
18417
18815
  const parsed = new URL(
@@ -18470,11 +18868,11 @@ var setDomain = new Command("set-domain").description("Map a domain to a runtime
18470
18868
  ...item,
18471
18869
  domains: item.key === opts.environment ? [
18472
18870
  ...item.domains.filter(
18473
- (value) => normalizeEnvironmentDomain(value) !== normalizedDomain
18871
+ (value2) => normalizeEnvironmentDomain(value2) !== normalizedDomain
18474
18872
  ),
18475
18873
  normalizedDomain
18476
18874
  ] : item.domains.filter(
18477
- (value) => normalizeEnvironmentDomain(value) !== normalizedDomain
18875
+ (value2) => normalizeEnvironmentDomain(value2) !== normalizedDomain
18478
18876
  )
18479
18877
  }));
18480
18878
  result = await updateEnvironments(requireCredentials(), {
@@ -18505,7 +18903,7 @@ var removeDomain = new Command("remove-domain").description("Remove a domain map
18505
18903
  const items = current.environments.items.map((item) => ({
18506
18904
  ...item,
18507
18905
  domains: item.domains.filter(
18508
- (value) => normalizeEnvironmentDomain(value) !== normalizedDomain
18906
+ (value2) => normalizeEnvironmentDomain(value2) !== normalizedDomain
18509
18907
  )
18510
18908
  }));
18511
18909
  result = await updateEnvironments(requireCredentials(), {
@@ -18594,8 +18992,8 @@ var off = new Command("off").description("Disable consent gating").action(async
18594
18992
  var consentCommand = new Command("consent").description("Manage consent gating").addCommand(get2).addCommand(set).addCommand(off);
18595
18993
 
18596
18994
  // src/commands/project-code.ts
18597
- var import_node_fs17 = require("node:fs");
18598
- var import_node_path14 = require("node:path");
18995
+ var import_node_fs18 = require("node:fs");
18996
+ var import_node_path15 = require("node:path");
18599
18997
 
18600
18998
  // src/commands/runtime-settings-shop.ts
18601
18999
  var uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
@@ -18640,17 +19038,17 @@ var fields = ["enabled", "css", "helperJs", "js"];
18640
19038
  function readPatch(command, file) {
18641
19039
  let input;
18642
19040
  try {
18643
- input = JSON.parse((0, import_node_fs17.readFileSync)(file, "utf8"));
19041
+ input = JSON.parse((0, import_node_fs18.readFileSync)(file, "utf8"));
18644
19042
  } catch (error) {
18645
19043
  fail(command, `Cannot read project code JSON: ${String(error)}`, EXIT_CODES.validation);
18646
19044
  }
18647
19045
  if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).length === 0) {
18648
19046
  fail(command, "Project code must be a nonempty partial object", EXIT_CODES.validation);
18649
19047
  }
18650
- for (const [key, value] of Object.entries(input)) {
19048
+ for (const [key, value2] of Object.entries(input)) {
18651
19049
  if (!fields.includes(key))
18652
19050
  fail(command, `Unknown project code field: ${key}`, EXIT_CODES.validation);
18653
- if (key === "enabled" ? typeof value !== "boolean" : value !== null && (typeof value !== "string" || value.length > 2e4)) {
19051
+ if (key === "enabled" ? typeof value2 !== "boolean" : value2 !== null && (typeof value2 !== "string" || value2.length > 2e4)) {
18654
19052
  fail(
18655
19053
  command,
18656
19054
  `${key} must be ${key === "enabled" ? "a boolean" : "null or a string of at most 20,000 characters"}`,
@@ -18743,9 +19141,9 @@ for (const mode of ["diff", "set"]) {
18743
19141
  );
18744
19142
  return;
18745
19143
  }
18746
- const backupPath = (0, import_node_path14.resolve)(options.backup);
19144
+ const backupPath = (0, import_node_path15.resolve)(options.backup);
18747
19145
  try {
18748
- (0, import_node_fs17.writeFileSync)(
19146
+ (0, import_node_fs18.writeFileSync)(
18749
19147
  backupPath,
18750
19148
  `${JSON.stringify({ ...identity2, ...current, capturedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
18751
19149
  `,
@@ -18804,17 +19202,17 @@ for (const mode of ["diff", "set"]) {
18804
19202
  }
18805
19203
 
18806
19204
  // src/commands/triggers.ts
18807
- var import_node_fs18 = __toESM(require("node:fs"));
18808
- var import_node_path15 = __toESM(require("node:path"));
19205
+ var import_node_fs19 = __toESM(require("node:fs"));
19206
+ var import_node_path16 = __toESM(require("node:path"));
18809
19207
 
18810
19208
  // src/trigger-proposal.ts
18811
- function runtimePath(path26) {
18812
- return path26.replaceAll("[]", ".0");
19209
+ function runtimePath(path27) {
19210
+ return path27.replaceAll("[]", ".0");
18813
19211
  }
18814
- function fieldsFrom(value, prefix = "", descriptorList = false) {
18815
- if (!value || typeof value !== "object") return [];
18816
- if (Array.isArray(value)) {
18817
- return value.flatMap(
19212
+ function fieldsFrom(value2, prefix = "", descriptorList = false) {
19213
+ if (!value2 || typeof value2 !== "object") return [];
19214
+ if (Array.isArray(value2)) {
19215
+ return value2.flatMap(
18818
19216
  (item, index) => fieldsFrom(
18819
19217
  item,
18820
19218
  descriptorList ? prefix : prefix ? `${prefix}.${index}` : String(index),
@@ -18822,7 +19220,7 @@ function fieldsFrom(value, prefix = "", descriptorList = false) {
18822
19220
  )
18823
19221
  );
18824
19222
  }
18825
- const record4 = value;
19223
+ const record4 = value2;
18826
19224
  const explicitPath = typeof record4.path === "string" ? record4.path : void 0;
18827
19225
  if (descriptorList && explicitPath) {
18828
19226
  const rawValues = record4.values ?? record4.samples ?? record4.examples ?? record4.example;
@@ -18849,7 +19247,7 @@ function fieldsFrom(value, prefix = "", descriptorList = false) {
18849
19247
  return fieldsFrom(child, next);
18850
19248
  });
18851
19249
  }
18852
- function isDescriptorArray(value) {
19250
+ function isDescriptorArray(value2) {
18853
19251
  const descriptorKeys = /* @__PURE__ */ new Set([
18854
19252
  "path",
18855
19253
  "values",
@@ -18864,7 +19262,7 @@ function isDescriptorArray(value) {
18864
19262
  "seen",
18865
19263
  "type_collision"
18866
19264
  ]);
18867
- return Array.isArray(value) && value.length > 0 && value.every((item) => {
19265
+ return Array.isArray(value2) && value2.length > 0 && value2.every((item) => {
18868
19266
  if (!item || typeof item !== "object" || Array.isArray(item)) return false;
18869
19267
  const record4 = item;
18870
19268
  return typeof record4.path === "string" && record4.path.length > 0 && Object.keys(record4).every((key) => descriptorKeys.has(key));
@@ -18887,15 +19285,15 @@ function candidatesFrom(report) {
18887
19285
  }
18888
19286
  return [root];
18889
19287
  }
18890
- function score2(path26, patterns) {
19288
+ function score2(path27, patterns) {
18891
19289
  return patterns.reduce(
18892
- (total, pattern, index) => total + (pattern.test(path26) ? patterns.length - index : 0),
19290
+ (total, pattern, index) => total + (pattern.test(path27) ? patterns.length - index : 0),
18893
19291
  0
18894
19292
  );
18895
19293
  }
18896
19294
  function best(fields3, patterns, numeric2 = false) {
18897
19295
  const ranked = fields3.filter(
18898
- (field) => !numeric2 || field.numericType === true || field.values.some((value) => typeof value === "number") || field.magnitude !== void 0
19296
+ (field) => !numeric2 || field.numericType === true || field.values.some((value2) => typeof value2 === "number") || field.magnitude !== void 0
18899
19297
  ).map((field) => ({ field, score: score2(field.path.toLowerCase(), patterns) })).filter((item) => item.score > 0).sort((a, b) => b.score - a.score || (b.field.magnitude ?? 0) - (a.field.magnitude ?? 0));
18900
19298
  if (!ranked.length) return [];
18901
19299
  const top = ranked[0].score;
@@ -19020,55 +19418,55 @@ var pathPattern = new RegExp(runtime_settings_trigger_constants_default.dotPathP
19020
19418
  var signalPathPattern = new RegExp(runtime_settings_trigger_constants_default.signalPathPattern);
19021
19419
  var valueTypes = new Set(runtime_settings_trigger_constants_default.valueTypes);
19022
19420
  var whenOps = new Set(runtime_settings_trigger_constants_default.whenOps);
19023
- function strictObject(value, location, keys) {
19024
- if (!value || typeof value !== "object" || Array.isArray(value))
19421
+ function strictObject(value2, location, keys) {
19422
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2))
19025
19423
  throw new Error(`${location} must be an object`);
19026
- const record4 = value;
19424
+ const record4 = value2;
19027
19425
  for (const key of Object.keys(record4))
19028
19426
  if (!keys.has(key)) throw new Error(`${location}.${key} is not allowed`);
19029
19427
  return record4;
19030
19428
  }
19031
- function optionalType(value, type, location) {
19032
- if (value !== void 0 && typeof value !== type)
19429
+ function optionalType(value2, type, location) {
19430
+ if (value2 !== void 0 && typeof value2 !== type)
19033
19431
  throw new Error(`${location} must be a ${type}`);
19034
19432
  }
19035
- function strictPath(value, location, required = false) {
19036
- if (value === void 0 && !required) return;
19037
- if (typeof value !== "string" || !pathPattern.test(value))
19433
+ function strictPath(value2, location, required = false) {
19434
+ if (value2 === void 0 && !required) return;
19435
+ if (typeof value2 !== "string" || !pathPattern.test(value2))
19038
19436
  throw new Error(
19039
19437
  `${location} must use dot-path syntax with property or array-index segments (for example: ecommerce.items.0.price); bracket syntax is not accepted`
19040
19438
  );
19041
19439
  }
19042
- function strictSource(value, location, allowSignal, required = false) {
19043
- if (value === void 0 && !required) return;
19044
- const sources = Array.isArray(value) ? value : [value];
19045
- if (Array.isArray(value) && (value.length < 1 || value.length > runtime_settings_trigger_constants_default.maxSourcePaths))
19440
+ function strictSource(value2, location, allowSignal, required = false) {
19441
+ if (value2 === void 0 && !required) return;
19442
+ const sources = Array.isArray(value2) ? value2 : [value2];
19443
+ if (Array.isArray(value2) && (value2.length < 1 || value2.length > runtime_settings_trigger_constants_default.maxSourcePaths))
19046
19444
  throw new Error(`${location} must contain 1 to ${runtime_settings_trigger_constants_default.maxSourcePaths} paths`);
19047
19445
  sources.forEach((source, index) => {
19048
- const sourceLocation = Array.isArray(value) ? `${location}[${index}]` : location;
19446
+ const sourceLocation = Array.isArray(value2) ? `${location}[${index}]` : location;
19049
19447
  if (allowSignal && typeof source === "string" && signalPathPattern.test(source)) return;
19050
19448
  strictPath(source, sourceLocation, true);
19051
19449
  });
19052
19450
  }
19053
- function defaultMatchesValueType(value, valueType) {
19451
+ function defaultMatchesValueType(value2, valueType) {
19054
19452
  if (valueType === "number") {
19055
- if (typeof value !== "string") return Number.isFinite(Number(value));
19056
- const text2 = String(value).trim();
19453
+ if (typeof value2 !== "string") return Number.isFinite(Number(value2));
19454
+ const text2 = String(value2).trim();
19057
19455
  const normalized = /^-?\d{1,3}(\.\d{3})+,\d+$/.test(text2) ? text2.replace(/\./g, "").replace(",", ".") : /^-?\d+,\d+$/.test(text2) ? text2.replace(",", ".") : text2;
19058
19456
  return Number.isFinite(Number(normalized));
19059
19457
  }
19060
19458
  if (valueType === "boolean") {
19061
- if (typeof value === "boolean") return true;
19459
+ if (typeof value2 === "boolean") return true;
19062
19460
  return ["1", "true", "yes", "on", "0", "false", "no", "off"].includes(
19063
- String(value).trim().toLowerCase()
19461
+ String(value2).trim().toLowerCase()
19064
19462
  );
19065
19463
  }
19066
19464
  return true;
19067
19465
  }
19068
- function strictTransform(value, valueType, location) {
19069
- if (value === void 0) return;
19466
+ function strictTransform(value2, valueType, location) {
19467
+ if (value2 === void 0) return;
19070
19468
  const transform = strictObject(
19071
- value,
19469
+ value2,
19072
19470
  location,
19073
19471
  /* @__PURE__ */ new Set(["trim", "lowercase", "map", "scale", "default"])
19074
19472
  );
@@ -19101,7 +19499,7 @@ function strictTransform(value, valueType, location) {
19101
19499
  if (transform.default !== void 0 && !defaultMatchesValueType(transform.default, valueType))
19102
19500
  throw new Error(`${location}.default must coerce to the mapping valueType`);
19103
19501
  }
19104
- function strictMapping(value, location, revenueProperty) {
19502
+ function strictMapping(value2, location, revenueProperty) {
19105
19503
  const keys = /* @__PURE__ */ new Set([
19106
19504
  "from",
19107
19505
  "to",
@@ -19109,7 +19507,7 @@ function strictMapping(value, location, revenueProperty) {
19109
19507
  "transform",
19110
19508
  ...revenueProperty ? [] : ["ttlDays", "persist"]
19111
19509
  ]);
19112
- const mapping = strictObject(value, location, keys);
19510
+ const mapping = strictObject(value2, location, keys);
19113
19511
  strictSource(mapping.from, `${location}.from`, revenueProperty, true);
19114
19512
  strictPath(mapping.to, `${location}.to`, true);
19115
19513
  if (mapping.valueType !== void 0 && !valueTypes.has(String(mapping.valueType)))
@@ -19120,12 +19518,12 @@ function strictMapping(value, location, revenueProperty) {
19120
19518
  optionalType(mapping.persist, "boolean", `${location}.persist`);
19121
19519
  }
19122
19520
  }
19123
- function strictWhen(value, location) {
19124
- if (value === void 0) return;
19125
- if (!Array.isArray(value) || value.length < 1 || value.length > runtime_settings_trigger_constants_default.maxWhenEntries) {
19521
+ function strictWhen(value2, location) {
19522
+ if (value2 === void 0) return;
19523
+ if (!Array.isArray(value2) || value2.length < 1 || value2.length > runtime_settings_trigger_constants_default.maxWhenEntries) {
19126
19524
  throw new Error(`${location} must contain 1 to ${runtime_settings_trigger_constants_default.maxWhenEntries} entries`);
19127
19525
  }
19128
- value.forEach((entry, index) => {
19526
+ value2.forEach((entry, index) => {
19129
19527
  const entryLocation = `${location}[${index}]`;
19130
19528
  const condition = strictObject(entry, entryLocation, /* @__PURE__ */ new Set(["path", "op", "value"]));
19131
19529
  strictPath(condition.path, `${entryLocation}.path`, true);
@@ -19154,12 +19552,12 @@ function strictWhen(value, location) {
19154
19552
  }
19155
19553
  });
19156
19554
  }
19157
- function strictTriggers(value) {
19158
- if (!Array.isArray(value) || value.length > runtime_settings_trigger_constants_default.maxTriggers)
19555
+ function strictTriggers(value2) {
19556
+ if (!Array.isArray(value2) || value2.length > runtime_settings_trigger_constants_default.maxTriggers)
19159
19557
  throw new Error(
19160
19558
  `trigger file must contain an array of at most ${runtime_settings_trigger_constants_default.maxTriggers} triggers`
19161
19559
  );
19162
- return value.map((item, index) => {
19560
+ return value2.map((item, index) => {
19163
19561
  if (!item || typeof item !== "object" || Array.isArray(item))
19164
19562
  throw new Error(`trigger[${index}] must be an object`);
19165
19563
  const record4 = item;
@@ -19314,7 +19712,7 @@ var get3 = new Command("get").description("Get data layer triggers").requiredOpt
19314
19712
  var set2 = new Command("set").requiredOption("--file <json>", "JSON trigger array").requiredOption("--shop <domain-or-id>", "Shop domain or Apex shop ID").action(async function(opts) {
19315
19713
  let triggers;
19316
19714
  try {
19317
- triggers = strictTriggers(JSON.parse(import_node_fs18.default.readFileSync(import_node_path15.default.resolve(opts.file), "utf8")));
19715
+ triggers = strictTriggers(JSON.parse(import_node_fs19.default.readFileSync(import_node_path16.default.resolve(opts.file), "utf8")));
19318
19716
  } catch (error) {
19319
19717
  fail(
19320
19718
  this,
@@ -19341,7 +19739,7 @@ var propose = new Command("propose").requiredOption("--from-file <u2-report.json
19341
19739
  let proposals;
19342
19740
  let report;
19343
19741
  try {
19344
- report = JSON.parse(import_node_fs18.default.readFileSync(import_node_path15.default.resolve(opts.fromFile), "utf8"));
19742
+ report = JSON.parse(import_node_fs19.default.readFileSync(import_node_path16.default.resolve(opts.fromFile), "utf8"));
19345
19743
  proposals = proposeTriggers(report);
19346
19744
  } catch (error) {
19347
19745
  fail(this, error instanceof Error ? error.message : String(error), EXIT_CODES.validation);
@@ -19369,28 +19767,28 @@ var propose = new Command("propose").requiredOption("--from-file <u2-report.json
19369
19767
  `);
19370
19768
  });
19371
19769
  });
19372
- function collect(value, previous) {
19373
- return [...previous, value];
19770
+ function collect2(value2, previous) {
19771
+ return [...previous, value2];
19374
19772
  }
19375
- function parseParamValue(value) {
19376
- if (!value.startsWith("[") && !value.startsWith("{")) return value;
19773
+ function parseParamValue(value2) {
19774
+ if (!value2.startsWith("[") && !value2.startsWith("{")) return value2;
19377
19775
  try {
19378
- return JSON.parse(value);
19776
+ return JSON.parse(value2);
19379
19777
  } catch {
19380
- return value;
19778
+ return value2;
19381
19779
  }
19382
19780
  }
19383
- var recipe = new Command("apply-recipe").argument("<recipeId>").requiredOption("--shop <domain-or-id>", "Shop domain or Apex shop ID").option("--param <k=v>", "Recipe parameter", collect, []).action(async function(recipeId, opts) {
19781
+ var recipe = new Command("apply-recipe").argument("<recipeId>").requiredOption("--shop <domain-or-id>", "Shop domain or Apex shop ID").option("--param <k=v>", "Recipe parameter", collect2, []).action(async function(recipeId, opts) {
19384
19782
  const params = Object.fromEntries(
19385
- opts.param.map((pair) => {
19386
- const i = pair.indexOf("=");
19783
+ opts.param.map((pair2) => {
19784
+ const i = pair2.indexOf("=");
19387
19785
  if (i < 1)
19388
19786
  fail(
19389
19787
  this,
19390
- `TRIGGERS_CONFIG_ABSENT: invalid --param ${pair}; expected k=v`,
19788
+ `TRIGGERS_CONFIG_ABSENT: invalid --param ${pair2}; expected k=v`,
19391
19789
  EXIT_CODES.validation
19392
19790
  );
19393
- return [pair.slice(0, i), parseParamValue(pair.slice(i + 1))];
19791
+ return [pair2.slice(0, i), parseParamValue(pair2.slice(i + 1))];
19394
19792
  })
19395
19793
  );
19396
19794
  const identity2 = await identify(this, opts.shop);
@@ -19417,7 +19815,7 @@ var recipe = new Command("apply-recipe").argument("<recipeId>").requiredOption("
19417
19815
  writeWarnings(result);
19418
19816
  });
19419
19817
  });
19420
- var enable = new Command("enable-capture").requiredOption("--hours <n>").requiredOption("--shop <domain-or-id>", "Shop domain or Apex shop ID").option("--url-pattern <p>", "URL pattern", collect, []).action(async function(opts) {
19818
+ var enable = new Command("enable-capture").requiredOption("--hours <n>").requiredOption("--shop <domain-or-id>", "Shop domain or Apex shop ID").option("--url-pattern <p>", "URL pattern", collect2, []).action(async function(opts) {
19421
19819
  const hours = Number(opts.hours);
19422
19820
  if (!Number.isFinite(hours) || hours <= 0 || hours > 48)
19423
19821
  fail(
@@ -19461,11 +19859,11 @@ var triggersCommand = new Command("triggers").description("Manage runtime data l
19461
19859
 
19462
19860
  // src/commands/domains.ts
19463
19861
  init_source();
19464
- function normalizeDomain(value) {
19465
- return value.trim().replace(/^https?:\/\//i, "").replace(/[/?#].*$/, "").replace(/\.$/, "").toLowerCase();
19862
+ function normalizeDomain(value2) {
19863
+ return value2.trim().replace(/^https?:\/\//i, "").replace(/[/?#].*$/, "").replace(/\.$/, "").toLowerCase();
19466
19864
  }
19467
- function landingHost(value) {
19468
- const trimmed = value.trim();
19865
+ function landingHost(value2) {
19866
+ const trimmed = value2.trim();
19469
19867
  try {
19470
19868
  const withScheme = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
19471
19869
  return new URL(withScheme).hostname.toLowerCase().replace(/\.$/, "") || normalizeDomain(trimmed);
@@ -19643,8 +20041,8 @@ var register = new Command("register").description("Register a new domain on the
19643
20041
  `apex domains register ${host} --years ${years} --confirm-price "${quote.total} ${quote.currency}"`
19644
20042
  );
19645
20043
  }
19646
- const readline3 = await import("node:readline");
19647
- const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
20044
+ const readline4 = await import("node:readline");
20045
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
19648
20046
  console.error(describeQuote(quote));
19649
20047
  const answer = await new Promise(
19650
20048
  (resolve2) => rl.question(
@@ -19731,9 +20129,9 @@ var domainsCommand = new Command("domains").description("Manage install domains
19731
20129
 
19732
20130
  // src/commands/handover.ts
19733
20131
  init_source();
19734
- function normalizeDomain2(value) {
20132
+ function normalizeDomain2(value2) {
19735
20133
  try {
19736
- const candidate = value.trim().toLowerCase();
20134
+ const candidate = value2.trim().toLowerCase();
19737
20135
  const parsed = new URL(
19738
20136
  /^[a-z][a-z0-9+.-]*:\/\//.test(candidate) ? candidate : `http://${candidate}`
19739
20137
  );
@@ -19756,8 +20154,8 @@ var NAMED_HTML_ENTITIES = {
19756
20154
  apos: "'",
19757
20155
  nbsp: "\xA0"
19758
20156
  };
19759
- function decodeHtmlEntities(value) {
19760
- return value.replace(/&(#[0-9]+|#[xX][0-9a-fA-F]+|[a-zA-Z]+);/g, (match2, entity2) => {
20157
+ function decodeHtmlEntities(value2) {
20158
+ return value2.replace(/&(#[0-9]+|#[xX][0-9a-fA-F]+|[a-zA-Z]+);/g, (match2, entity2) => {
19761
20159
  if (entity2.startsWith("#")) {
19762
20160
  const hex = entity2[1] === "x" || entity2[1] === "X";
19763
20161
  const code = Number.parseInt(hex ? entity2.slice(2) : entity2.slice(1), hex ? 16 : 10);
@@ -19917,9 +20315,9 @@ var qa2 = new Command("qa").description("Check static link/form handover paramet
19917
20315
  var handoverCommand = new Command("handover").description("Manage cross-domain identity handover").addCommand(get4).addCommand(set4).addCommand(off2).addCommand(qa2);
19918
20316
 
19919
20317
  // src/commands/order-journey.ts
19920
- function instant(value) {
20318
+ function instant(value2) {
19921
20319
  const m = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(Z|[+-]\d{2}:\d{2})$/.exec(
19922
- value
20320
+ value2
19923
20321
  );
19924
20322
  if (!m) return null;
19925
20323
  const year = Number(m[1]), month = Number(m[2]), day = Number(m[3]);
@@ -20223,22 +20621,22 @@ var CAVEAT = "Labeled estimate only: fail-closed consent tracking makes decliner
20223
20621
  var PRODUCTION_WORKER_BASE_URL = "https://events.drip-apex.com";
20224
20622
  var PRODUCTION_ENVIRONMENT_KEY = "production";
20225
20623
  var VISITOR_SERIES_DAYS = 60;
20226
- function hostOnly(value) {
20227
- if (!value) return null;
20624
+ function hostOnly(value2) {
20625
+ if (!value2) return null;
20228
20626
  try {
20229
- return new URL(value.includes("://") ? value : `https://${value}`).hostname.toLowerCase();
20627
+ return new URL(value2.includes("://") ? value2 : `https://${value2}`).hostname.toLowerCase();
20230
20628
  } catch {
20231
- return value.toLowerCase().replace(/^https?:\/\//, "").split("/")[0] || null;
20629
+ return value2.toLowerCase().replace(/^https?:\/\//, "").split("/")[0] || null;
20232
20630
  }
20233
20631
  }
20234
20632
  function configuredHosts(domains, envs) {
20235
20633
  const set7 = /* @__PURE__ */ new Set();
20236
- for (const value of [
20634
+ for (const value2 of [
20237
20635
  domains.domain,
20238
20636
  ...domains.additionalDomains ?? [],
20239
20637
  ...domains.storeOrigins ?? []
20240
20638
  ]) {
20241
- const host = hostOnly(value);
20639
+ const host = hostOnly(value2);
20242
20640
  if (host) set7.add(host);
20243
20641
  }
20244
20642
  for (const env2 of envs?.environments.items ?? [])
@@ -20339,8 +20737,8 @@ var PURCHASE_CADENCE_WINDOW_MINUTES = PURCHASE_WINDOW_MINUTES * 4;
20339
20737
  var MIN_CAPTURE_WINDOW_MINUTES = 60;
20340
20738
  var DUPLICATE_RATE_THRESHOLD = 0.02;
20341
20739
  var MAGNITUDE_TOLERANCE_RATE = 0.1;
20342
- function record3(value) {
20343
- return value && typeof value === "object" && !Array.isArray(value) ? value : null;
20740
+ function record3(value2) {
20741
+ return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
20344
20742
  }
20345
20743
  function purchasePlane(config) {
20346
20744
  if (!config) return { present: false, names: [] };
@@ -20355,13 +20753,13 @@ function purchasePlane(config) {
20355
20753
  return { present: names.length > 0, names };
20356
20754
  }
20357
20755
  function publicationTime(config) {
20358
- for (const value of [
20756
+ for (const value2 of [
20359
20757
  config?.generated_at,
20360
20758
  config?.publicationTimestamp,
20361
20759
  config?.publishedAt,
20362
20760
  config?.updatedAt
20363
20761
  ]) {
20364
- if (typeof value === "string" && Number.isFinite(Date.parse(value))) return Date.parse(value);
20762
+ if (typeof value2 === "string" && Number.isFinite(Date.parse(value2))) return Date.parse(value2);
20365
20763
  }
20366
20764
  return null;
20367
20765
  }
@@ -20382,14 +20780,14 @@ function eventTime(event) {
20382
20780
  const raw = event.timestamp;
20383
20781
  return typeof raw === "string" && Number.isFinite(Date.parse(raw)) ? Date.parse(raw) : null;
20384
20782
  }
20385
- function numeric(value) {
20386
- const parsed = typeof value === "number" ? value : typeof value === "string" && value.trim() !== "" ? Number(value) : NaN;
20783
+ function numeric(value2) {
20784
+ const parsed = typeof value2 === "number" ? value2 : typeof value2 === "string" && value2.trim() !== "" ? Number(value2) : NaN;
20387
20785
  return Number.isFinite(parsed) ? parsed : null;
20388
20786
  }
20389
- function lineItemSum(value) {
20390
- if (!Array.isArray(value)) return null;
20787
+ function lineItemSum(value2) {
20788
+ if (!Array.isArray(value2)) return null;
20391
20789
  let sum = 0;
20392
- for (const item of value) {
20790
+ for (const item of value2) {
20393
20791
  const row = record3(item);
20394
20792
  const price = numeric(row?.price);
20395
20793
  if (price === null) return null;
@@ -20592,8 +20990,8 @@ async function fetchServedConfig(workerBase, shopId) {
20592
20990
  }
20593
20991
  function originHosts(values) {
20594
20992
  const set7 = /* @__PURE__ */ new Set();
20595
- for (const value of values) {
20596
- const host = hostOnly(value);
20993
+ for (const value2 of values) {
20994
+ const host = hostOnly(value2);
20597
20995
  if (host) set7.add(host);
20598
20996
  }
20599
20997
  return [...set7].sort();
@@ -20970,15 +21368,15 @@ function withCaveat(check, caveat) {
20970
21368
  return caveat.length ? { ...check, evidence: [...check.evidence, ...caveat] } : check;
20971
21369
  }
20972
21370
  var DAY_MS = 864e5;
20973
- function parseDay(value) {
20974
- const text2 = value?.trim();
21371
+ function parseDay(value2) {
21372
+ const text2 = value2?.trim();
20975
21373
  if (!text2 || !/^\d{4}-\d{2}-\d{2}$/.test(text2)) return null;
20976
21374
  const parsed = Date.parse(`${text2}T00:00:00.000Z`);
20977
21375
  if (Number.isNaN(parsed)) return null;
20978
21376
  return toDay(parsed) === text2 ? parsed : null;
20979
21377
  }
20980
- function toDay(value) {
20981
- return new Date(value).toISOString().slice(0, 10);
21378
+ function toDay(value2) {
21379
+ return new Date(value2).toISOString().slice(0, 10);
20982
21380
  }
20983
21381
  function windowDays(from, to) {
20984
21382
  return to < from ? 0 : Math.round((to - from) / DAY_MS) + 1;
@@ -21085,18 +21483,18 @@ ${CAVEAT}`
21085
21483
  });
21086
21484
 
21087
21485
  // src/commands/analytics.ts
21088
- function display(value) {
21089
- if (value === null || value === void 0) return "\u2014";
21090
- if (typeof value === "object") return JSON.stringify(value);
21091
- return String(value);
21092
- }
21093
- function positiveInteger(command, value, option) {
21094
- if (value === void 0) return void 0;
21095
- const parsed = Number(value);
21486
+ function display(value2) {
21487
+ if (value2 === null || value2 === void 0) return "\u2014";
21488
+ if (typeof value2 === "object") return JSON.stringify(value2);
21489
+ return String(value2);
21490
+ }
21491
+ function positiveInteger(command, value2, option) {
21492
+ if (value2 === void 0) return void 0;
21493
+ const parsed = Number(value2);
21096
21494
  if (!Number.isInteger(parsed) || parsed <= 0)
21097
21495
  fail(command, `${option} must be a positive integer`, EXIT_CODES.validation, {
21098
21496
  field: option.slice(2),
21099
- actual: value
21497
+ actual: value2
21100
21498
  });
21101
21499
  return parsed;
21102
21500
  }
@@ -21227,7 +21625,7 @@ Examples:
21227
21625
  );
21228
21626
 
21229
21627
  // src/commands/install.ts
21230
- var import_promises3 = require("node:dns/promises");
21628
+ var import_promises4 = require("node:dns/promises");
21231
21629
  var import_node_http2 = require("node:http");
21232
21630
  var import_node_https = require("node:https");
21233
21631
  var import_node_net = require("node:net");
@@ -21285,7 +21683,7 @@ var testResolver;
21285
21683
  var testFetch;
21286
21684
  async function resolveHost(hostname) {
21287
21685
  const mocked = testResolver?.(hostname) ?? null;
21288
- const addresses = mocked ?? await (0, import_promises3.lookup)(hostname, { all: true });
21686
+ const addresses = mocked ?? await (0, import_promises4.lookup)(hostname, { all: true });
21289
21687
  return addresses.map((entry) => entry.address);
21290
21688
  }
21291
21689
  async function validateUrl(raw, allowedHosts) {
@@ -21334,8 +21732,8 @@ function decoderFor(encoding) {
21334
21732
  return (0, import_node_zlib.createBrotliDecompress)();
21335
21733
  }
21336
21734
  function normalizeEncoding(header) {
21337
- const value = (header ?? "").trim().toLowerCase();
21338
- return !value || value === "identity" ? null : value;
21735
+ const value2 = (header ?? "").trim().toLowerCase();
21736
+ return !value2 || value2 === "identity" ? null : value2;
21339
21737
  }
21340
21738
  function decodeBody(raw, header, limits, hostname, inputTruncated = false) {
21341
21739
  const encoding = normalizeEncoding(header);
@@ -21398,9 +21796,9 @@ function overLimitMessage(hostname, maxBytes) {
21398
21796
  function responseFromIncoming(res, body5, decoded) {
21399
21797
  const status4 = res.statusCode ?? 0;
21400
21798
  const headers = new Headers();
21401
- for (const [key, value] of Object.entries(res.headers)) {
21402
- if (Array.isArray(value)) for (const entry of value) headers.append(key, entry);
21403
- else if (typeof value === "string") headers.set(key, value);
21799
+ for (const [key, value2] of Object.entries(res.headers)) {
21800
+ if (Array.isArray(value2)) for (const entry of value2) headers.append(key, entry);
21801
+ else if (typeof value2 === "string") headers.set(key, value2);
21404
21802
  }
21405
21803
  if (decoded) {
21406
21804
  headers.delete("content-encoding");
@@ -21651,11 +22049,11 @@ function buildConfigUrl(workerBaseUrl, shopId, environmentKey) {
21651
22049
  if (normalized && normalized !== "production") url.searchParams.set("env", normalized);
21652
22050
  return url.toString();
21653
22051
  }
21654
- function readNumber(value) {
21655
- return typeof value === "number" && Number.isFinite(value) ? value : null;
22052
+ function readNumber(value2) {
22053
+ return typeof value2 === "number" && Number.isFinite(value2) ? value2 : null;
21656
22054
  }
21657
- function readString(value) {
21658
- return typeof value === "string" && value.trim() ? value : null;
22055
+ function readString(value2) {
22056
+ return typeof value2 === "string" && value2.trim() ? value2 : null;
21659
22057
  }
21660
22058
  function isRecognizedConfig(config) {
21661
22059
  const hasRevision = readNumber(config.publicationRevision) !== null || readNumber(config.publication_revision) !== null;
@@ -21927,7 +22325,7 @@ var installCommand = new Command("install").description("Check Apex storefront i
21927
22325
  "trackRevenue.enabled",
21928
22326
  servedConfig.revenueTriggerEnabled === null ? "unknown" : servedConfig.revenueTriggerEnabled ? "yes" : "no"
21929
22327
  ]
21930
- ].map(([field, value]) => [String(field), String(value)])
22328
+ ].map(([field, value2]) => [String(field), String(value2)])
21931
22329
  );
21932
22330
  if (warnings.length)
21933
22331
  printTable(
@@ -21961,15 +22359,15 @@ var installCommand = new Command("install").description("Check Apex storefront i
21961
22359
 
21962
22360
  // src/commands/flags.ts
21963
22361
  init_source();
21964
- var import_node_fs19 = __toESM(require("node:fs"));
21965
- var import_node_path16 = __toESM(require("node:path"));
22362
+ var import_node_fs20 = __toESM(require("node:fs"));
22363
+ var import_node_path17 = __toESM(require("node:path"));
21966
22364
 
21967
22365
  // src/edge-status.ts
21968
22366
  function getEdgeDeliveryStatus(deliveryType, expectedWorstCaseSeconds = 360) {
21969
22367
  return deliveryType === "api" ? { edge_delivery: "not_applicable", message: `API-delivered flag \u2014 edge delivery is not applicable. A previously cached visual payload may remain at some PoPs for up to ~${expectedWorstCaseSeconds}s; a single-vantage status cannot prove global propagation.` } : { edge_delivery: "applicable", message: null };
21970
22368
  }
21971
- function parsePublicationMetadata(value) {
21972
- const record4 = value && typeof value === "object" ? value : {};
22369
+ function parsePublicationMetadata(value2) {
22370
+ const record4 = value2 && typeof value2 === "object" ? value2 : {};
21973
22371
  return {
21974
22372
  publication_revision: Number.isSafeInteger(record4.publication_revision) && record4.publication_revision >= 0 ? record4.publication_revision : null,
21975
22373
  generated_at: typeof record4.generated_at === "string" && !Number.isNaN(Date.parse(record4.generated_at)) ? record4.generated_at : null
@@ -22000,11 +22398,11 @@ function formatEdgePropagationStatus(status4) {
22000
22398
 
22001
22399
  // src/commands/flags.ts
22002
22400
  var kind = "flags";
22003
- function body(command, value, opts) {
22401
+ function body(command, value2, opts) {
22004
22402
  let parsed = {};
22005
- if (value) {
22006
- const source = value.startsWith("@") ? value.slice(1) : value;
22007
- const text2 = value.startsWith("@") || import_node_fs19.default.existsSync(source) ? import_node_fs19.default.readFileSync(import_node_path16.default.resolve(process.cwd(), source), "utf8") : value;
22403
+ if (value2) {
22404
+ const source = value2.startsWith("@") ? value2.slice(1) : value2;
22405
+ const text2 = value2.startsWith("@") || import_node_fs20.default.existsSync(source) ? import_node_fs20.default.readFileSync(import_node_path17.default.resolve(process.cwd(), source), "utf8") : value2;
22008
22406
  try {
22009
22407
  parsed = JSON.parse(text2);
22010
22408
  } catch (e) {
@@ -22106,8 +22504,8 @@ function write(name) {
22106
22504
  c.action(async function(...args) {
22107
22505
  const o = this.opts();
22108
22506
  const id = name === "update" ? String(args[0]) : void 0;
22109
- const value = o.file ? `@${o.file}` : String(args[name === "update" ? 1 : 0] ?? "") || void 0;
22110
- const b = body(this, value, o);
22507
+ const value2 = o.file ? `@${o.file}` : String(args[name === "update" ? 1 : 0] ?? "") || void 0;
22508
+ const b = body(this, value2, o);
22111
22509
  if (name === "update" && !Object.keys(b).length)
22112
22510
  fail(this, "Provide at least one field to update.", EXIT_CODES.validation);
22113
22511
  const r = name === "create" ? await createManagedDelivery(requireCredentials(), kind, b) : await updateManagedDelivery(requireCredentials(), kind, id, b);
@@ -22137,16 +22535,16 @@ Examples:
22137
22535
  );
22138
22536
 
22139
22537
  // src/commands/backlog.ts
22140
- var import_node_fs20 = __toESM(require("node:fs"));
22141
- var import_node_path17 = __toESM(require("node:path"));
22538
+ var import_node_fs21 = __toESM(require("node:fs"));
22539
+ var import_node_path18 = __toESM(require("node:path"));
22142
22540
  init_source();
22143
22541
  var kind2 = "backlog";
22144
22542
  var singular = "backlog item";
22145
- function parse(command, value) {
22146
- if (!value) return {};
22543
+ function parse(command, value2) {
22544
+ if (!value2) return {};
22147
22545
  try {
22148
- const source = value.startsWith("@") ? value.slice(1) : value;
22149
- const text2 = value.startsWith("@") || import_node_fs20.default.existsSync(source) ? import_node_fs20.default.readFileSync(import_node_path17.default.resolve(process.cwd(), source), "utf8") : value;
22546
+ const source = value2.startsWith("@") ? value2.slice(1) : value2;
22547
+ const text2 = value2.startsWith("@") || import_node_fs21.default.existsSync(source) ? import_node_fs21.default.readFileSync(import_node_path18.default.resolve(process.cwd(), source), "utf8") : value2;
22150
22548
  const body5 = JSON.parse(text2);
22151
22549
  if (!body5 || typeof body5 !== "object" || Array.isArray(body5))
22152
22550
  throw new Error("body must be an object");
@@ -22159,11 +22557,11 @@ function parse(command, value) {
22159
22557
  );
22160
22558
  }
22161
22559
  }
22162
- function collect2(value, previous) {
22163
- return [...previous, value];
22560
+ function collect3(value2, previous) {
22561
+ return [...previous, value2];
22164
22562
  }
22165
- function customFieldsRecord(value) {
22166
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
22563
+ function customFieldsRecord(value2) {
22564
+ return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : {};
22167
22565
  }
22168
22566
  function typedFieldValue(definition, raw) {
22169
22567
  if (raw === "null") return null;
@@ -22185,11 +22583,11 @@ function typedFieldValue(definition, raw) {
22185
22583
  }
22186
22584
  async function customFieldsFromPairs(command, pairs) {
22187
22585
  if (pairs.length === 0) return void 0;
22188
- const parsedPairs = pairs.map((pair) => {
22189
- const separator = pair.indexOf("=");
22586
+ const parsedPairs = pairs.map((pair2) => {
22587
+ const separator = pair2.indexOf("=");
22190
22588
  if (separator <= 0)
22191
- fail(command, `Invalid --field "${pair}": use key=value.`, EXIT_CODES.validation);
22192
- return { key: pair.slice(0, separator), raw: pair.slice(separator + 1) };
22589
+ fail(command, `Invalid --field "${pair2}": use key=value.`, EXIT_CODES.validation);
22590
+ return { key: pair2.slice(0, separator), raw: pair2.slice(separator + 1) };
22193
22591
  });
22194
22592
  const fields3 = await listBacklogFields(requireCredentials());
22195
22593
  if (!fields3.ok || !fields3.data) {
@@ -22213,8 +22611,8 @@ function detail2(x, labels) {
22213
22611
  const custom = customFieldsRecord(x.custom_fields);
22214
22612
  if (Object.keys(custom).length > 0) {
22215
22613
  console.log();
22216
- for (const [key, value] of Object.entries(custom)) {
22217
- console.log(` ${labels.get(key) ?? key}: ${value === null ? "-" : String(value)}`);
22614
+ for (const [key, value2] of Object.entries(custom)) {
22615
+ console.log(` ${labels.get(key) ?? key}: ${value2 === null ? "-" : String(value2)}`);
22218
22616
  }
22219
22617
  }
22220
22618
  console.log();
@@ -22266,7 +22664,7 @@ function write2(action78) {
22266
22664
  c.argument("[json]", "Inline JSON, @file, or file path").option("--file <path>", "JSON body file").option(
22267
22665
  "--field <key=value>",
22268
22666
  "Set one of the shop's own fields; repeatable. Use key=null to clear it.",
22269
- collect2,
22667
+ collect3,
22270
22668
  []
22271
22669
  );
22272
22670
  c.action(async function(...args) {
@@ -22312,8 +22710,8 @@ var remove = new Command("delete").description("Delete a backlog item").argument
22312
22710
  "Deleting a backlog item requires confirmation in non-TTY mode.",
22313
22711
  `apex backlog delete ${id} --force`
22314
22712
  );
22315
- const readline3 = await import("node:readline");
22316
- const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
22713
+ const readline4 = await import("node:readline");
22714
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
22317
22715
  const answer = await new Promise(
22318
22716
  (resolve2) => rl.question(source_default.yellow(`Delete backlog item ${id}? [y/N] `), resolve2)
22319
22717
  );
@@ -22340,12 +22738,12 @@ function fieldDetail(field) {
22340
22738
  if (field.archived_at) console.log(` Archived: ${field.archived_at}`);
22341
22739
  console.log();
22342
22740
  }
22343
- function boolOption(command, flag, value) {
22344
- if (value === void 0) return void 0;
22345
- const trimmed = value.trim().toLowerCase();
22741
+ function boolOption(command, flag, value2) {
22742
+ if (value2 === void 0) return void 0;
22743
+ const trimmed = value2.trim().toLowerCase();
22346
22744
  if (["true", "yes", "y", "1"].includes(trimmed)) return true;
22347
22745
  if (["false", "no", "n", "0"].includes(trimmed)) return false;
22348
- fail(command, `Invalid ${flag} "${value}": use true or false.`, EXIT_CODES.validation);
22746
+ fail(command, `Invalid ${flag} "${value2}": use true or false.`, EXIT_CODES.validation);
22349
22747
  }
22350
22748
  var fieldsList = new Command("list").alias("ls").description("List the shop's idea fields").option("--include-archived", "Include archived fields").action(async function(o) {
22351
22749
  const r = await listBacklogFields(requireCredentials(), o.includeArchived);
@@ -22373,7 +22771,7 @@ var fieldsCreate = new Command("create").description("Add an idea field").requir
22373
22771
  ).option("--key <key>", "Field key; derived from the label when omitted").option(
22374
22772
  "--option <value>",
22375
22773
  "A choice for a single_choice field; repeatable",
22376
- collect2,
22774
+ collect3,
22377
22775
  []
22378
22776
  ).option("--required <true|false>", "Every idea must carry a value").option("--client-visible <true|false>", "Share the value with the client").action(async function(o) {
22379
22777
  const required = boolOption(this, "--required", o.required);
@@ -22393,7 +22791,7 @@ var fieldsCreate = new Command("create").description("Add an idea field").requir
22393
22791
  var fieldsUpdate = new Command("update").description("Update an idea field").argument("<id>", "field ID").option("--label <text>", "What the field is called").option(
22394
22792
  "--option <value>",
22395
22793
  "Replace the choices of a single_choice field; repeatable",
22396
- collect2,
22794
+ collect3,
22397
22795
  []
22398
22796
  ).option("--required <true|false>", "Every idea must carry a value").option("--client-visible <true|false>", "Share the value with the client").option("--archived <true|false>", "Archive or restore the field").action(async function(id, o) {
22399
22797
  const required = boolOption(this, "--required", o.required);
@@ -22423,16 +22821,16 @@ var fields2 = new Command("fields").description("Manage the shop's own idea fiel
22423
22821
  var backlogCommand = new Command("backlog").description("Manage backlog").addCommand(list7).addCommand(get6).addCommand(write2("create")).addCommand(write2("update")).addCommand(archive).addCommand(restore).addCommand(remove).addCommand(fields2);
22424
22822
 
22425
22823
  // src/commands/signals.ts
22426
- var import_node_fs21 = __toESM(require("node:fs"));
22427
- var import_node_path18 = __toESM(require("node:path"));
22824
+ var import_node_fs22 = __toESM(require("node:fs"));
22825
+ var import_node_path19 = __toESM(require("node:path"));
22428
22826
  init_source();
22429
22827
  var kind3 = "signals";
22430
22828
  var singular2 = "signal";
22431
- function parse2(command, value) {
22432
- if (!value) return {};
22829
+ function parse2(command, value2) {
22830
+ if (!value2) return {};
22433
22831
  try {
22434
- const source = value.startsWith("@") ? value.slice(1) : value;
22435
- const text2 = value.startsWith("@") || import_node_fs21.default.existsSync(source) ? import_node_fs21.default.readFileSync(import_node_path18.default.resolve(process.cwd(), source), "utf8") : value;
22832
+ const source = value2.startsWith("@") ? value2.slice(1) : value2;
22833
+ const text2 = value2.startsWith("@") || import_node_fs22.default.existsSync(source) ? import_node_fs22.default.readFileSync(import_node_path19.default.resolve(process.cwd(), source), "utf8") : value2;
22436
22834
  const body5 = JSON.parse(text2);
22437
22835
  if (!body5 || typeof body5 !== "object" || Array.isArray(body5))
22438
22836
  throw new Error("body must be an object");
@@ -22514,14 +22912,14 @@ var signalsCommand = new Command("signals").description("Manage signals").addCom
22514
22912
 
22515
22913
  // src/commands/personalizations.ts
22516
22914
  init_source();
22517
- var import_node_fs22 = __toESM(require("node:fs"));
22518
- var import_node_path19 = __toESM(require("node:path"));
22915
+ var import_node_fs23 = __toESM(require("node:fs"));
22916
+ var import_node_path20 = __toESM(require("node:path"));
22519
22917
  var kind4 = "personalizations";
22520
- function body2(command, value, opts) {
22918
+ function body2(command, value2, opts) {
22521
22919
  let parsed = {};
22522
- if (value) {
22523
- const source = value.startsWith("@") ? value.slice(1) : value;
22524
- const text2 = value.startsWith("@") || import_node_fs22.default.existsSync(source) ? import_node_fs22.default.readFileSync(import_node_path19.default.resolve(process.cwd(), source), "utf8") : value;
22920
+ if (value2) {
22921
+ const source = value2.startsWith("@") ? value2.slice(1) : value2;
22922
+ const text2 = value2.startsWith("@") || import_node_fs23.default.existsSync(source) ? import_node_fs23.default.readFileSync(import_node_path20.default.resolve(process.cwd(), source), "utf8") : value2;
22525
22923
  try {
22526
22924
  parsed = JSON.parse(text2);
22527
22925
  } catch (e) {
@@ -22580,8 +22978,8 @@ function write4(name) {
22580
22978
  c.action(async function(...args) {
22581
22979
  const o = this.opts();
22582
22980
  const id = name === "update" ? String(args[0]) : void 0;
22583
- const value = o.file ? `@${o.file}` : String(args[name === "update" ? 1 : 0] ?? "") || void 0;
22584
- const b = body2(this, value, o);
22981
+ const value2 = o.file ? `@${o.file}` : String(args[name === "update" ? 1 : 0] ?? "") || void 0;
22982
+ const b = body2(this, value2, o);
22585
22983
  if (name === "update" && !Object.keys(b).length)
22586
22984
  fail(this, "Provide at least one field to update.", EXIT_CODES.validation);
22587
22985
  const r = name === "create" ? await createManagedDelivery(requireCredentials(), kind4, b) : await updateManagedDelivery(requireCredentials(), kind4, id, b);
@@ -22603,8 +23001,8 @@ var personalizationsCommand = new Command("personalizations").description("Manag
22603
23001
 
22604
23002
  // src/commands/targeting-resources.ts
22605
23003
  init_source();
22606
- var import_node_fs23 = __toESM(require("node:fs"));
22607
- var import_node_path20 = __toESM(require("node:path"));
23004
+ var import_node_fs24 = __toESM(require("node:fs"));
23005
+ var import_node_path21 = __toESM(require("node:path"));
22608
23006
 
22609
23007
  // src/generated/exclusion-group-body.schema.json
22610
23008
  var exclusion_group_body_schema_default = {
@@ -22857,11 +23255,11 @@ function exclusionFailureDetails(kind5, action78, status4, details) {
22857
23255
  ]
22858
23256
  };
22859
23257
  }
22860
- function body3(command, value) {
22861
- if (!value) return {};
22862
- const source = value.startsWith("@") ? value.slice(1) : value;
23258
+ function body3(command, value2) {
23259
+ if (!value2) return {};
23260
+ const source = value2.startsWith("@") ? value2.slice(1) : value2;
22863
23261
  try {
22864
- const text2 = value.startsWith("@") || import_node_fs23.default.existsSync(source) ? import_node_fs23.default.readFileSync(import_node_path20.default.resolve(process.cwd(), source), "utf8") : value;
23262
+ const text2 = value2.startsWith("@") || import_node_fs24.default.existsSync(source) ? import_node_fs24.default.readFileSync(import_node_path21.default.resolve(process.cwd(), source), "utf8") : value2;
22865
23263
  return JSON.parse(text2);
22866
23264
  } catch (error) {
22867
23265
  fail(
@@ -22940,8 +23338,8 @@ function targetingResourceCommand(kind5) {
22940
23338
  ).option("--file <path>", "JSON body file").action(async function(...args) {
22941
23339
  const opts = this.opts();
22942
23340
  const id = action78 === "update" ? String(args[0]) : void 0;
22943
- const value = opts.file ? `@${opts.file}` : String(args[action78 === "update" ? 1 : 0] ?? "") || void 0;
22944
- const requestBody = body3(this, value);
23341
+ const value2 = opts.file ? `@${opts.file}` : String(args[action78 === "update" ? 1 : 0] ?? "") || void 0;
23342
+ const requestBody = body3(this, value2);
22945
23343
  if (action78 === "update" && typeof requestBody === "object" && requestBody !== null && !Array.isArray(requestBody) && Object.keys(requestBody).length === 0)
22946
23344
  fail(this, `Updating a ${singular3} requires at least one field.`, EXIT_CODES.validation);
22947
23345
  const response = await requestTargetingResource(
@@ -22974,8 +23372,8 @@ function targetingResourceCommand(kind5) {
22974
23372
  "Deleting an exclusion group requires confirmation in non-TTY mode.",
22975
23373
  `apex exclusion-groups delete ${id} --force`
22976
23374
  );
22977
- const readline3 = await import("node:readline");
22978
- const rl = readline3.createInterface({
23375
+ const readline4 = await import("node:readline");
23376
+ const rl = readline4.createInterface({
22979
23377
  input: process.stdin,
22980
23378
  output: process.stderr
22981
23379
  });
@@ -23001,13 +23399,13 @@ function targetingResourceCommand(kind5) {
23001
23399
  );
23002
23400
  if (kind5 === "exclusion-groups") {
23003
23401
  root.addCommand(
23004
- new Command("replace-members").description("Replace exclusion group members").argument("<id>").argument("<json>", "Request body as inline JSON, @file, or a file path (shape below)").action(async function(id, value) {
23402
+ new Command("replace-members").description("Replace exclusion group members").argument("<id>").argument("<json>", "Request body as inline JSON, @file, or a file path (shape below)").action(async function(id, value2) {
23005
23403
  const response = await requestTargetingResource(
23006
23404
  requireCredentials(),
23007
23405
  "PUT",
23008
23406
  kind5,
23009
23407
  `${id}/members`,
23010
- body3(this, value)
23408
+ body3(this, value2)
23011
23409
  );
23012
23410
  if (!response.ok || !response.data)
23013
23411
  fail(
@@ -23116,8 +23514,8 @@ var targetingCommand = new Command("targeting").description("Test experiment tar
23116
23514
  // src/commands/sos.ts
23117
23515
  var import_node_crypto8 = require("node:crypto");
23118
23516
  init_source();
23119
- var import_node_fs24 = __toESM(require("node:fs"));
23120
- var import_node_path21 = __toESM(require("node:path"));
23517
+ var import_node_fs25 = __toESM(require("node:fs"));
23518
+ var import_node_path22 = __toESM(require("node:path"));
23121
23519
  var SOS_TYPES = /* @__PURE__ */ new Set([
23122
23520
  "full_handover",
23123
23521
  "design",
@@ -23127,10 +23525,10 @@ var SOS_TYPES = /* @__PURE__ */ new Set([
23127
23525
  "bug",
23128
23526
  "help"
23129
23527
  ]);
23130
- function readText(value) {
23131
- if (!value) return void 0;
23132
- if (!value.startsWith("@")) return value;
23133
- return import_node_fs24.default.readFileSync(import_node_path21.default.resolve(process.cwd(), value.slice(1)), "utf8");
23528
+ function readText(value2) {
23529
+ if (!value2) return void 0;
23530
+ if (!value2.startsWith("@")) return value2;
23531
+ return import_node_fs25.default.readFileSync(import_node_path22.default.resolve(process.cwd(), value2.slice(1)), "utf8");
23134
23532
  }
23135
23533
  function printTicket(ticket, reused) {
23136
23534
  console.log();
@@ -23184,12 +23582,12 @@ var create4 = new Command("create").description("Create an Apex SOS ticket").opt
23184
23582
  };
23185
23583
  if (opts.attach.length > 5) fail(this, "Attach no more than 5 files", 3);
23186
23584
  const files = opts.attach.map((file) => {
23187
- const filename = import_node_path21.default.basename(file);
23188
- const content_type = mediaTypes[import_node_path21.default.extname(file).toLowerCase()];
23585
+ const filename = import_node_path22.default.basename(file);
23586
+ const content_type = mediaTypes[import_node_path22.default.extname(file).toLowerCase()];
23189
23587
  if (!content_type) fail(this, "Unsupported attachment type", 3);
23190
23588
  let size;
23191
23589
  try {
23192
- size = import_node_fs24.default.statSync(file).size;
23590
+ size = import_node_fs25.default.statSync(file).size;
23193
23591
  } catch {
23194
23592
  fail(this, `Attachment not found: ${file}`, 3);
23195
23593
  }
@@ -23197,7 +23595,7 @@ var create4 = new Command("create").description("Create an Apex SOS ticket").opt
23197
23595
  if (size <= 0 || size > cap) fail(this, "Attachment exceeds its size limit or is empty", 3);
23198
23596
  let bytes;
23199
23597
  try {
23200
- bytes = import_node_fs24.default.readFileSync(file);
23598
+ bytes = import_node_fs25.default.readFileSync(file);
23201
23599
  } catch {
23202
23600
  fail(this, `Attachment not found: ${file}`, 3);
23203
23601
  }
@@ -23333,17 +23731,17 @@ var markRead = new Command("mark-read").option("--channel <key>", "Mark one chan
23333
23731
  var inboxCommand = new Command("inbox").description("Read and send inbox messages").addCommand(list11).addCommand(send2).addCommand(markRead);
23334
23732
 
23335
23733
  // src/commands/shops.ts
23336
- var import_node_fs25 = __toESM(require("node:fs"));
23337
- var import_node_path22 = __toESM(require("node:path"));
23734
+ var import_node_fs26 = __toESM(require("node:fs"));
23735
+ var import_node_path23 = __toESM(require("node:path"));
23338
23736
  function body4(command, raw) {
23339
23737
  if (!raw) return {};
23340
23738
  try {
23341
23739
  const source = raw.startsWith("@") ? raw.slice(1) : raw;
23342
- const text2 = raw.startsWith("@") || import_node_fs25.default.existsSync(source) ? import_node_fs25.default.readFileSync(import_node_path22.default.resolve(source), "utf8") : raw;
23343
- const value = JSON.parse(text2);
23344
- if (!value || typeof value !== "object" || Array.isArray(value))
23740
+ const text2 = raw.startsWith("@") || import_node_fs26.default.existsSync(source) ? import_node_fs26.default.readFileSync(import_node_path23.default.resolve(source), "utf8") : raw;
23741
+ const value2 = JSON.parse(text2);
23742
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2))
23345
23743
  throw new Error("body must be an object");
23346
- return value;
23744
+ return value2;
23347
23745
  } catch (e) {
23348
23746
  fail(
23349
23747
  command,
@@ -23448,15 +23846,15 @@ function settings(name, description, toggle) {
23448
23846
  }
23449
23847
  var profile = new Command("store-profile").description("Read and review a store profile");
23450
23848
  function printStoreProfile(data) {
23451
- const value = data;
23849
+ const value2 = data;
23452
23850
  console.log(
23453
- `${value.shop?.name ?? "Store profile"}${value.shop?.domain ? ` (${value.shop.domain})` : ""}`
23851
+ `${value2.shop?.name ?? "Store profile"}${value2.shop?.domain ? ` (${value2.shop.domain})` : ""}`
23454
23852
  );
23455
23853
  console.log(
23456
- `Audit: ${value.latestRun?.status ?? "not run"}; findings: ${value.findings?.length ?? 0} (${value.unreviewedCount ?? 0} unreviewed)`
23854
+ `Audit: ${value2.latestRun?.status ?? "not run"}; findings: ${value2.findings?.length ?? 0} (${value2.unreviewedCount ?? 0} unreviewed)`
23457
23855
  );
23458
- console.log(`Facts: ${value.facts?.length ?? 0}; open reviews: ${value.openReviewCount ?? 0}`);
23459
- console.log(`Learning run: ${value.latestLearningRun?.status ?? "none"}`);
23856
+ console.log(`Facts: ${value2.facts?.length ?? 0}; open reviews: ${value2.openReviewCount ?? 0}`);
23857
+ console.log(`Learning run: ${value2.latestLearningRun?.status ?? "none"}`);
23460
23858
  }
23461
23859
  profile.addCommand(
23462
23860
  new Command("get").argument("<id>").action(async function(id) {
@@ -23603,13 +24001,13 @@ function dataJson(source) {
23603
24001
  error: { code: "malformed_container", message: "GTM container data is not balanced JSON" }
23604
24002
  };
23605
24003
  }
23606
- function visit(value, callback) {
23607
- if (!value || typeof value !== "object") return;
23608
- if (Array.isArray(value)) {
23609
- for (const item of value) visit(item, callback);
24004
+ function visit(value2, callback) {
24005
+ if (!value2 || typeof value2 !== "object") return;
24006
+ if (Array.isArray(value2)) {
24007
+ for (const item of value2) visit(item, callback);
23610
24008
  return;
23611
24009
  }
23612
- const object = value;
24010
+ const object = value2;
23613
24011
  callback(object);
23614
24012
  for (const nested of Object.values(object)) visit(nested, callback);
23615
24013
  }
@@ -23656,13 +24054,13 @@ function parseContainer(source, id, dataLayerName = "dataLayer") {
23656
24054
  for (const rule of rules) {
23657
24055
  if (!Array.isArray(rule)) continue;
23658
24056
  const predicateIndexes = rule.flatMap(
23659
- (part) => Array.isArray(part) && part[0] === "if" ? part.slice(1).filter((value) => typeof value === "number") : []
24057
+ (part) => Array.isArray(part) && part[0] === "if" ? part.slice(1).filter((value2) => typeof value2 === "number") : []
23660
24058
  );
23661
24059
  const predicateText = predicateIndexes.map((index) => JSON.stringify(predicates[index] ?? "")).join(" ");
23662
24060
  if (!/consent/i.test(predicateText) || !purchaseRelevantEventNames.some((name) => predicateText.includes(name)))
23663
24061
  continue;
23664
24062
  const tagIndexes = rule.flatMap(
23665
- (part) => Array.isArray(part) && part[0] === "add" ? part.slice(1).filter((value) => typeof value === "number") : []
24063
+ (part) => Array.isArray(part) && part[0] === "add" ? part.slice(1).filter((value2) => typeof value2 === "number") : []
23666
24064
  );
23667
24065
  for (const index of tagIndexes) {
23668
24066
  const tag = recordObject(tags[index]);
@@ -23682,12 +24080,12 @@ function parseContainer(source, id, dataLayerName = "dataLayer") {
23682
24080
  vtpPaths,
23683
24081
  ecommerceFrom: [...ecommerce][0] ?? null,
23684
24082
  serverContainerUrl: [...serverUrls][0] ?? null,
23685
- piiPaths: vtpPaths.filter((path26) => /email|phone|address|name/i.test(path26))
24083
+ piiPaths: vtpPaths.filter((path27) => /email|phone|address|name/i.test(path27))
23686
24084
  }
23687
24085
  };
23688
24086
  }
23689
- function recordObject(value) {
23690
- return value && typeof value === "object" && !Array.isArray(value) ? value : null;
24087
+ function recordObject(value2) {
24088
+ return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
23691
24089
  }
23692
24090
  async function mineGtm(input, fetcher = fetch, explicitContainerUrl = false) {
23693
24091
  let refs;
@@ -23732,11 +24130,11 @@ async function mineGtm(input, fetcher = fetch, explicitContainerUrl = false) {
23732
24130
  }
23733
24131
 
23734
24132
  // src/commands/discover.ts
23735
- function minutesValue(value) {
23736
- if (value === void 0) return void 0;
23737
- if (!/^\d+$/.test(value))
24133
+ function minutesValue(value2) {
24134
+ if (value2 === void 0) return void 0;
24135
+ if (!/^\d+$/.test(value2))
23738
24136
  throw new CliError("--minutes must be an integer between 1 and 20160", EXIT_CODES.validation);
23739
- const parsed = Number(value);
24137
+ const parsed = Number(value2);
23740
24138
  if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 20160)
23741
24139
  throw new CliError("--minutes must be an integer between 1 and 20160", EXIT_CODES.validation);
23742
24140
  return parsed;
@@ -23751,11 +24149,11 @@ function isContainerField(field) {
23751
24149
  const types = baseFieldTypes(field);
23752
24150
  return types.has("obj") || types.has("arr");
23753
24151
  }
23754
- function hasSyntheticPlaceholder(path26) {
23755
- return path26.split(".").map((segment) => segment.replace(/\[\]$/, "")).some((segment) => /^<[^>]+>$/.test(segment));
24152
+ function hasSyntheticPlaceholder(path27) {
24153
+ return path27.split(".").map((segment) => segment.replace(/\[\]$/, "")).some((segment) => /^<[^>]+>$/.test(segment));
23756
24154
  }
23757
- function ambiguousDottedPrefix(event, path26) {
23758
- const segments = path26.split(".");
24155
+ function ambiguousDottedPrefix(event, path27) {
24156
+ const segments = path27.split(".");
23759
24157
  for (let index = 1; index < segments.length; index += 1) {
23760
24158
  const prefix = segments.slice(0, index).join(".");
23761
24159
  const parent = event.fields.find((field) => field.path === prefix);
@@ -23764,42 +24162,42 @@ function ambiguousDottedPrefix(event, path26) {
23764
24162
  return void 0;
23765
24163
  }
23766
24164
  function dimensionEligibility(event, field) {
23767
- const path26 = field.path;
23768
- if (hasSyntheticPlaceholder(path26)) {
24165
+ const path27 = field.path;
24166
+ if (hasSyntheticPlaceholder(path27)) {
23769
24167
  return {
23770
24168
  eligible: false,
23771
- message: `Path ${JSON.stringify(path26)} is a synthetic placeholder and cannot create a dimension. Choose an observed scalar path.`
24169
+ message: `Path ${JSON.stringify(path27)} is a synthetic placeholder and cannot create a dimension. Choose an observed scalar path.`
23772
24170
  };
23773
24171
  }
23774
24172
  if (isContainerField(field)) {
23775
24173
  return {
23776
24174
  eligible: false,
23777
- message: `Path ${JSON.stringify(path26)} is container-typed. Choose an observed scalar path.`
24175
+ message: `Path ${JSON.stringify(path27)} is container-typed. Choose an observed scalar path.`
23778
24176
  };
23779
24177
  }
23780
- if (path26.includes("[]")) {
24178
+ if (path27.includes("[]")) {
23781
24179
  return {
23782
24180
  eligible: false,
23783
- message: `Path ${JSON.stringify(path26)} includes an array-element segment and cannot create a reliable dimension. Choose a scalar path outside an array.`
24181
+ message: `Path ${JSON.stringify(path27)} includes an array-element segment and cannot create a reliable dimension. Choose a scalar path outside an array.`
23784
24182
  };
23785
24183
  }
23786
24184
  if (!isStableField(field)) {
23787
24185
  return {
23788
24186
  eligible: false,
23789
- message: `Path ${JSON.stringify(path26)} does not have a stable observed type. Choose a path without a type collision.`
24187
+ message: `Path ${JSON.stringify(path27)} does not have a stable observed type. Choose a path without a type collision.`
23790
24188
  };
23791
24189
  }
23792
24190
  if (!event.event) {
23793
24191
  return {
23794
24192
  eligible: false,
23795
- message: `Path ${JSON.stringify(path26)} has no observed event name. Choose a path from a named event.`
24193
+ message: `Path ${JSON.stringify(path27)} has no observed event name. Choose a path from a named event.`
23796
24194
  };
23797
24195
  }
23798
- const ambiguousPrefix = ambiguousDottedPrefix(event, path26);
24196
+ const ambiguousPrefix = ambiguousDottedPrefix(event, path27);
23799
24197
  if (ambiguousPrefix) {
23800
24198
  return {
23801
24199
  eligible: false,
23802
- message: `Path ${JSON.stringify(path26)} could be a literal key containing dots because the discovery report has no observed container at ${JSON.stringify(ambiguousPrefix)}. Apex cannot safely create a capture for it.`
24200
+ message: `Path ${JSON.stringify(path27)} could be a literal key containing dots because the discovery report has no observed container at ${JSON.stringify(ambiguousPrefix)}. Apex cannot safely create a capture for it.`
23803
24201
  };
23804
24202
  }
23805
24203
  return { eligible: true };
@@ -23808,24 +24206,24 @@ function isNumericField(field) {
23808
24206
  const numericTypes = /* @__PURE__ */ new Set(["num", "numstr", "numstr_comma"]);
23809
24207
  return field.types.length > 0 && field.types.every((type) => numericTypes.has(type.split(":")[0]));
23810
24208
  }
23811
- function defaultDimensionLabel(path26) {
23812
- const words = path26.split(/[._-]+/).filter(Boolean).join(" ");
23813
- return words ? `${words[0].toUpperCase()}${words.slice(1)}` : path26;
24209
+ function defaultDimensionLabel(path27) {
24210
+ const words = path27.split(/[._-]+/).filter(Boolean).join(" ");
24211
+ return words ? `${words[0].toUpperCase()}${words.slice(1)}` : path27;
23814
24212
  }
23815
- function quoteShellArgument(value) {
23816
- return "'" + value.replaceAll("'", "'\\''") + "'";
24213
+ function quoteShellArgument(value2) {
24214
+ return "'" + value2.replaceAll("'", "'\\''") + "'";
23817
24215
  }
23818
- function createDimensionInvocation(path26, eventName) {
23819
- return `apex discover --create-dimension ${quoteShellArgument(path26)} --event ${quoteShellArgument(eventName)}`;
24216
+ function createDimensionInvocation(path27, eventName) {
24217
+ return `apex discover --create-dimension ${quoteShellArgument(path27)} --event ${quoteShellArgument(eventName)}`;
23820
24218
  }
23821
- function resolveDimensionObservation(command, report, path26, eventName) {
24219
+ function resolveDimensionObservation(command, report, path27, eventName) {
23822
24220
  const matches = report.data_layer.flatMap(
23823
- (event) => event.fields.filter((field) => field.path === path26).map((field) => ({ event, field }))
24221
+ (event) => event.fields.filter((field) => field.path === path27).map((field) => ({ event, field }))
23824
24222
  );
23825
24223
  if (!matches.length) {
23826
24224
  fail(
23827
24225
  command,
23828
- `Path ${JSON.stringify(path26)} was not found in the current discovery report. Run apex discover and copy an observed path exactly.`,
24226
+ `Path ${JSON.stringify(path27)} was not found in the current discovery report. Run apex discover and copy an observed path exactly.`,
23829
24227
  EXIT_CODES.validation
23830
24228
  );
23831
24229
  }
@@ -23835,7 +24233,7 @@ function resolveDimensionObservation(command, report, path26, eventName) {
23835
24233
  if (!eventMatches.length) {
23836
24234
  fail(
23837
24235
  command,
23838
- `Path ${JSON.stringify(path26)} was not observed on event ${JSON.stringify(eventName)}. Run apex discover and copy the event name exactly.`,
24236
+ `Path ${JSON.stringify(path27)} was not observed on event ${JSON.stringify(eventName)}. Run apex discover and copy the event name exactly.`,
23839
24237
  EXIT_CODES.validation
23840
24238
  );
23841
24239
  }
@@ -23843,7 +24241,7 @@ function resolveDimensionObservation(command, report, path26, eventName) {
23843
24241
  if (layers.length > 1) {
23844
24242
  fail(
23845
24243
  command,
23846
- `Path ${JSON.stringify(path26)} on event ${JSON.stringify(eventName)} was observed on multiple data layers: ${layers.join(", ")}. Dimensions bind to one layer; this ambiguity cannot be resolved automatically \u2014 capture the value under one layer, or define the dimension in the dashboard with the data layer name set explicitly.`,
24244
+ `Path ${JSON.stringify(path27)} on event ${JSON.stringify(eventName)} was observed on multiple data layers: ${layers.join(", ")}. Dimensions bind to one layer; this ambiguity cannot be resolved automatically \u2014 capture the value under one layer, or define the dimension in the dashboard with the data layer name set explicitly.`,
23847
24245
  EXIT_CODES.validation
23848
24246
  );
23849
24247
  }
@@ -23855,7 +24253,7 @@ function resolveDimensionObservation(command, report, path26, eventName) {
23855
24253
  if (namedEvents.length > 1) {
23856
24254
  fail(
23857
24255
  command,
23858
- `Path ${JSON.stringify(path26)} was observed on multiple events: ${namedEvents.join(", ")}. Retry with --event <name>.`,
24256
+ `Path ${JSON.stringify(path27)} was observed on multiple events: ${namedEvents.join(", ")}. Retry with --event <name>.`,
23859
24257
  EXIT_CODES.validation
23860
24258
  );
23861
24259
  }
@@ -23864,7 +24262,7 @@ function resolveDimensionObservation(command, report, path26, eventName) {
23864
24262
  if (layers.length > 1) {
23865
24263
  fail(
23866
24264
  command,
23867
- `Path ${JSON.stringify(path26)} on event ${JSON.stringify(namedEvents[0] ?? "")} was observed on multiple data layers: ${layers.join(", ")}. Dimensions bind to one layer; this ambiguity cannot be resolved automatically \u2014 capture the value under one layer, or define the dimension in the dashboard with the data layer name set explicitly.`,
24265
+ `Path ${JSON.stringify(path27)} on event ${JSON.stringify(namedEvents[0] ?? "")} was observed on multiple data layers: ${layers.join(", ")}. Dimensions bind to one layer; this ambiguity cannot be resolved automatically \u2014 capture the value under one layer, or define the dimension in the dashboard with the data layer name set explicitly.`,
23868
24266
  EXIT_CODES.validation
23869
24267
  );
23870
24268
  }
@@ -23899,9 +24297,9 @@ function renderDiscovery(report) {
23899
24297
  `
23900
24298
  ${event.event || "(unnamed event)"} [${event.dlName}] \u2014 ${event.count} observation(s)${event.matched ? " \u2014 matched" : ""}; t_ms p50=${event.t_ms_p50 ?? "n/a"}, p95=${event.t_ms_p95 ?? "n/a"}, pre-init=${event.pre_init_count}`
23901
24299
  );
23902
- for (const path26 of event.paths)
24300
+ for (const path27 of event.paths)
23903
24301
  lines.push(
23904
- ` URL ${path26.path}: ${path26.pageviews} pageviews, ${path26.count} pushes, ${path26.pspReturn_count} PSP returns`
24302
+ ` URL ${path27.path}: ${path27.pageviews} pageviews, ${path27.count} pushes, ${path27.pspReturn_count} PSP returns`
23905
24303
  );
23906
24304
  for (const field of event.fields) {
23907
24305
  lines.push(
@@ -23964,7 +24362,7 @@ var discoverCommand = new Command("discover").description("Read back the trackin
23964
24362
  let report;
23965
24363
  let apiSucceeded = false;
23966
24364
  let discoveryCredentials;
23967
- let apiFailure2;
24365
+ let apiFailure3;
23968
24366
  try {
23969
24367
  discoveryCredentials = requireCredentials();
23970
24368
  const result = await discoverTrackingSchema(discoveryCredentials, minutes);
@@ -23972,24 +24370,24 @@ var discoverCommand = new Command("discover").description("Read back the trackin
23972
24370
  report = result.data;
23973
24371
  apiSucceeded = true;
23974
24372
  } else
23975
- apiFailure2 = {
24373
+ apiFailure3 = {
23976
24374
  message: result.error ?? "Discovery API unavailable",
23977
24375
  status: result.status,
23978
24376
  details: result.errorDetails
23979
24377
  };
23980
24378
  } catch (error) {
23981
- apiFailure2 = {
24379
+ apiFailure3 = {
23982
24380
  message: error instanceof Error ? error.message : String(error),
23983
24381
  ...error instanceof CliError ? { details: error.details, cliError: error } : {}
23984
24382
  };
23985
24383
  }
23986
24384
  if (!report && (!gtmInput || opts.createDimension)) {
23987
- if (apiFailure2?.cliError) throw apiFailure2.cliError;
24385
+ if (apiFailure3?.cliError) throw apiFailure3.cliError;
23988
24386
  fail(
23989
24387
  this,
23990
- apiFailure2?.message ?? "Failed to discover tracking schema",
23991
- classifyStatus(apiFailure2?.status ?? 0),
23992
- apiFailure2?.details
24388
+ apiFailure3?.message ?? "Failed to discover tracking schema",
24389
+ classifyStatus(apiFailure3?.status ?? 0),
24390
+ apiFailure3?.details
23993
24391
  );
23994
24392
  }
23995
24393
  report ??= {
@@ -24004,7 +24402,7 @@ var discoverCommand = new Command("discover").description("Read back the trackin
24004
24402
  trigger_diagnoses: [],
24005
24403
  storage: [],
24006
24404
  notes: [
24007
- `GTM static analysis completed without discovery API data: ${apiFailure2?.message ?? "platform credentials were not provided"}`
24405
+ `GTM static analysis completed without discovery API data: ${apiFailure3?.message ?? "platform credentials were not provided"}`
24008
24406
  ]
24009
24407
  };
24010
24408
  if (gtmInput) {
@@ -24042,9 +24440,9 @@ var discoverCommand = new Command("discover").description("Read back the trackin
24042
24440
  if (!apiSucceeded || !discoveryCredentials) {
24043
24441
  fail(
24044
24442
  this,
24045
- apiFailure2?.message ?? "Discovery API data is required to create a dimension",
24046
- classifyStatus(apiFailure2?.status ?? 0),
24047
- apiFailure2?.details
24443
+ apiFailure3?.message ?? "Discovery API data is required to create a dimension",
24444
+ classifyStatus(apiFailure3?.status ?? 0),
24445
+ apiFailure3?.details
24048
24446
  );
24049
24447
  }
24050
24448
  const observation = resolveDimensionObservation(
@@ -24114,16 +24512,16 @@ var discoverCommand = new Command("discover").description("Read back the trackin
24114
24512
  });
24115
24513
 
24116
24514
  // src/commands/skills.ts
24117
- var import_node_fs26 = __toESM(require("node:fs"));
24118
- var import_node_path23 = __toESM(require("node:path"));
24515
+ var import_node_fs27 = __toESM(require("node:fs"));
24516
+ var import_node_path24 = __toESM(require("node:path"));
24119
24517
  function packageRoot() {
24120
24518
  const candidates = [
24121
- import_node_path23.default.resolve(__dirname, ".."),
24122
- import_node_path23.default.resolve(import_node_path23.default.dirname(process.argv[1] ?? process.cwd()), ".."),
24123
- import_node_path23.default.resolve(process.cwd(), "packages/cli")
24519
+ import_node_path24.default.resolve(__dirname, ".."),
24520
+ import_node_path24.default.resolve(import_node_path24.default.dirname(process.argv[1] ?? process.cwd()), ".."),
24521
+ import_node_path24.default.resolve(process.cwd(), "packages/cli")
24124
24522
  ];
24125
24523
  for (const candidate of candidates) {
24126
- if (import_node_fs26.default.existsSync(import_node_path23.default.join(candidate, "AGENTS.md"))) return candidate;
24524
+ if (import_node_fs27.default.existsSync(import_node_path24.default.join(candidate, "AGENTS.md"))) return candidate;
24127
24525
  }
24128
24526
  throw new Error(
24129
24527
  `Apex CLI package files not found. Looked for AGENTS.md in: ${candidates.join(", ")}`
@@ -24132,19 +24530,19 @@ function packageRoot() {
24132
24530
  function skillPaths() {
24133
24531
  const root = packageRoot();
24134
24532
  return {
24135
- agents: import_node_path23.default.join(root, "AGENTS.md"),
24136
- skill: import_node_path23.default.join(root, "skills", "apex-cli", "SKILL.md")
24533
+ agents: import_node_path24.default.join(root, "AGENTS.md"),
24534
+ skill: import_node_path24.default.join(root, "skills", "apex-cli", "SKILL.md")
24137
24535
  };
24138
24536
  }
24139
24537
  function installSkill(paths, targetRoot) {
24140
- const missing = [paths.skill, paths.agents].filter((filePath) => !import_node_fs26.default.existsSync(filePath));
24538
+ const missing = [paths.skill, paths.agents].filter((filePath) => !import_node_fs27.default.existsSync(filePath));
24141
24539
  if (missing.length > 0) {
24142
24540
  throw new Error(`Apex CLI package files not found. Missing: ${missing.join(", ")}`);
24143
24541
  }
24144
- const target = import_node_path23.default.resolve(targetRoot, "apex-cli");
24145
- import_node_fs26.default.mkdirSync(target, { recursive: true });
24146
- import_node_fs26.default.copyFileSync(paths.skill, import_node_path23.default.join(target, "SKILL.md"));
24147
- import_node_fs26.default.copyFileSync(paths.agents, import_node_path23.default.join(target, "AGENTS.md"));
24542
+ const target = import_node_path24.default.resolve(targetRoot, "apex-cli");
24543
+ import_node_fs27.default.mkdirSync(target, { recursive: true });
24544
+ import_node_fs27.default.copyFileSync(paths.skill, import_node_path24.default.join(target, "SKILL.md"));
24545
+ import_node_fs27.default.copyFileSync(paths.agents, import_node_path24.default.join(target, "AGENTS.md"));
24148
24546
  return target;
24149
24547
  }
24150
24548
  var skillsCommand = new Command("skills").description("Print or install the Apex CLI agent skill files").option("--install", "Copy the Apex CLI skill into ./.claude/skills/apex-cli").option("--target <dir>", "Install target skills directory", ".claude/skills").option("--json", "Print JSON").action(function action71(opts) {
@@ -24179,9 +24577,9 @@ function validateMoveInput(shopIds, toOrgId) {
24179
24577
 
24180
24578
  // src/commands/admin.ts
24181
24579
  var uuid2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
24182
- function switchValue(value) {
24183
- if (value === "on") return true;
24184
- if (value === "off") return false;
24580
+ function switchValue(value2) {
24581
+ if (value2 === "on") return true;
24582
+ if (value2 === "off") return false;
24185
24583
  throw new Error("Use on or off");
24186
24584
  }
24187
24585
  var settingsGet = new Command("settings").description("Show platform operator settings").action(async function() {
@@ -24250,8 +24648,8 @@ async function confirmArchive(command, shopId, force) {
24250
24648
  `apex admin shops archive ${JSON.stringify(shopId)} --force`
24251
24649
  );
24252
24650
  }
24253
- const readline3 = await import("node:readline");
24254
- const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
24651
+ const readline4 = await import("node:readline");
24652
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
24255
24653
  const answer = await new Promise((resolve2) => {
24256
24654
  rl.question("Archive this shop? Apex delivery will be disabled. [y/N] ", resolve2);
24257
24655
  });
@@ -24292,7 +24690,115 @@ var restore2 = new Command("restore").description("Restore an archived shop").ar
24292
24690
  () => console.log("Shop restored. Delivery remains controlled by the kill switch.")
24293
24691
  );
24294
24692
  });
24295
- var shops = new Command("shops").description("Archive and restore shops on the platform admin plane").addCommand(archive2).addCommand(restore2);
24693
+ function settingValueText(value2) {
24694
+ return value2 === null ? "null" : typeof value2 === "object" ? JSON.stringify(value2) : String(value2);
24695
+ }
24696
+ function printShopSettings(settings2) {
24697
+ printTable(
24698
+ ["KEY", "VALUE", "OVERRIDE", "GROUP"],
24699
+ settings2.map((entry) => [
24700
+ entry.key,
24701
+ settingValueText(entry.value),
24702
+ settingValueText(entry.override),
24703
+ entry.group
24704
+ ])
24705
+ );
24706
+ }
24707
+ async function loadShopSettings(command, shopRef) {
24708
+ const res = await getAdminShopSettings(requireUserCredentials(), shopRef);
24709
+ if (!res.ok || !res.data) {
24710
+ const candidates = res.errorDetails?.candidates;
24711
+ const hint = res.status === 409 && Array.isArray(candidates) ? ` Candidate IDs: ${candidates.join(", ")}. Use --shop <uuid>.` : "";
24712
+ fail(
24713
+ command,
24714
+ `${res.error ?? "Failed to read shop settings"}${hint}`,
24715
+ classifyStatus(res.status),
24716
+ res.errorDetails
24717
+ );
24718
+ }
24719
+ return res.data;
24720
+ }
24721
+ function findShopSetting(command, settings2, key) {
24722
+ const entry = settings2.find((setting) => setting.key === key);
24723
+ if (!entry)
24724
+ fail(
24725
+ command,
24726
+ `Unknown shop setting ${key}. Registry keys: ${settings2.map((setting) => setting.key).join(", ")}`,
24727
+ 3
24728
+ );
24729
+ return entry;
24730
+ }
24731
+ var shopSettings = new Command("settings").description(
24732
+ "Read and write registered per-shop settings"
24733
+ );
24734
+ shopSettings.addCommand(
24735
+ new Command("list").description("List every registered shop setting").requiredOption("--shop <uuid-or-domain>").option("--json", "Output JSON").action(async function(opts) {
24736
+ const res = await loadShopSettings(this, opts.shop);
24737
+ output(this, res, () => printShopSettings(res.data.settings));
24738
+ })
24739
+ );
24740
+ shopSettings.addCommand(
24741
+ new Command("get").description("Read one registered shop setting").requiredOption("--shop <uuid-or-domain>").requiredOption("--key <key>").option("--json", "Output JSON").action(async function(opts) {
24742
+ const res = await loadShopSettings(this, opts.shop);
24743
+ const entry = findShopSetting(this, res.data.settings, opts.key);
24744
+ output(this, { data: { ...res.data, settings: [entry] } }, () => printShopSettings([entry]));
24745
+ })
24746
+ );
24747
+ shopSettings.addCommand(
24748
+ new Command("set").description("Write a registered shop setting through the audited admin path").requiredOption("--shop <uuid-or-domain>").requiredOption("--key <key>").requiredOption("--value <value>").option("--reason <text>", "Audit reason").option("--expected-version <v>", "Expected storage version").option("--confirm", "Confirm sensitive setting change").option("--json", "Output JSON").action(async function(opts) {
24749
+ const resolved = (await loadShopSettings(this, opts.shop)).data;
24750
+ const entry = findShopSetting(this, resolved.settings, opts.key);
24751
+ let value2 = opts.value;
24752
+ if (opts.value === "null") value2 = null;
24753
+ else if (entry.value_type === "boolean") {
24754
+ if (["on", "true"].includes(opts.value)) value2 = true;
24755
+ else if (["off", "false"].includes(opts.value)) value2 = false;
24756
+ else fail(this, "Boolean values must be on, off, true or false", 3);
24757
+ } else if (entry.value_type === "number") {
24758
+ if (!opts.value.trim() || !Number.isFinite(Number(opts.value)))
24759
+ fail(this, "Number values must be finite numbers", 3);
24760
+ value2 = Number(opts.value);
24761
+ }
24762
+ if (entry.sensitive && !opts.confirm) {
24763
+ const quote = (text2) => "'" + text2.replaceAll("'", `'"'"'`) + "'";
24764
+ const args = [
24765
+ `--shop ${quote(resolved.shop_id)}`,
24766
+ `--key ${quote(opts.key)}`,
24767
+ `--value ${quote(opts.value)}`
24768
+ ];
24769
+ if (opts.reason !== void 0) args.push(`--reason ${quote(opts.reason)}`);
24770
+ if (opts.expectedVersion !== void 0)
24771
+ args.push(`--expected-version ${quote(opts.expectedVersion)}`);
24772
+ if (this.optsWithGlobals().json) args.push("--json");
24773
+ confirmationRequired(
24774
+ this,
24775
+ "--confirm is required because this changes a sensitive shop setting",
24776
+ `apex admin shops settings set ${args.join(" ")} --confirm`
24777
+ );
24778
+ }
24779
+ const res = await setAdminShopSetting(requireUserCredentials(), resolved.shop_id, {
24780
+ key: opts.key,
24781
+ value: value2,
24782
+ ...opts.reason !== void 0 ? { reason: opts.reason } : {},
24783
+ ...opts.expectedVersion !== void 0 ? { expectedVersion: opts.expectedVersion } : {}
24784
+ });
24785
+ if (!res.ok || !res.data)
24786
+ fail(
24787
+ this,
24788
+ res.error ?? "Failed to write shop setting",
24789
+ classifyStatus(res.status),
24790
+ res.errorDetails
24791
+ );
24792
+ if (res.data.data?.shop_id !== resolved.shop_id)
24793
+ fail(this, "The write response has a different shop identity", 2);
24794
+ output(
24795
+ this,
24796
+ res.data,
24797
+ () => console.log(`Setting ${opts.key} set to ${settingValueText(value2)} on ${resolved.domain}.`)
24798
+ );
24799
+ })
24800
+ );
24801
+ var shops = new Command("shops").description("Manage shops and their settings on the platform admin plane").addCommand(archive2).addCommand(restore2).addCommand(shopSettings);
24296
24802
  var create6 = new Command("create").description("Create an internal, demo or dogfood account").requiredOption("--name <text>").requiredOption("--category <category>", "internal, demo or dogfood").option("--type <type>", "company or agency", "company").option("--plan <plan>", "self_managed, apex_managed or drip_managed", "self_managed").option("--domain <host>").option("--platform <platform>", "shopify, shopware, woo or other").option("--claim-shop <uuid>").option("--owner <email>").option("--consultant <uuid>", "Platform access ID").option("--consultant-title <text>").requiredOption("--reason <text>", "Audit reason, 8 to 500 characters").option("--confirm", "Confirm account creation").option("--json", "Output JSON").action(async function(opts) {
24297
24803
  if (opts.category === "customer") fail(this, "Create customer accounts in Admin, Links", 3);
24298
24804
  if (!["internal", "demo", "dogfood"].includes(opts.category))
@@ -24346,7 +24852,7 @@ var create6 = new Command("create").description("Create an internal, demo or dog
24346
24852
  ["consultant", opts.consultant],
24347
24853
  ["consultant-title", opts.consultantTitle]
24348
24854
  ];
24349
- const retry = flags.filter((entry) => entry[1] !== void 0).map(([flag, value]) => `--${flag} '${value.replaceAll("'", "'\\''")}'`).join(" ");
24855
+ const retry = flags.filter((entry) => entry[1] !== void 0).map(([flag, value2]) => `--${flag} '${value2.replaceAll("'", "'\\''")}'`).join(" ");
24350
24856
  confirmationRequired(
24351
24857
  this,
24352
24858
  "--confirm is required to create an account",
@@ -24398,7 +24904,7 @@ var setCategory = new Command("set-category").description("Change an existing ac
24398
24904
  if (reason2.length < 8 || reason2.length > 500)
24399
24905
  fail(this, "--reason must be 8 to 500 characters", 3);
24400
24906
  if (!opts.confirm) {
24401
- const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
24907
+ const quote = (value2) => `'${value2.replaceAll("'", "'\\''")}'`;
24402
24908
  confirmationRequired(
24403
24909
  this,
24404
24910
  "--confirm is required to change an account category",
@@ -24430,7 +24936,7 @@ var setManagedBy = new Command("set-managed-by").description("Assign or clear a
24430
24936
  if (reason2.length < 8 || reason2.length > 500)
24431
24937
  fail(this, "--reason must be 8 to 500 characters", 3);
24432
24938
  if (!opts.confirm) {
24433
- const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
24939
+ const quote = (value2) => `'${value2.replaceAll("'", "'\\''")}'`;
24434
24940
  confirmationRequired(
24435
24941
  this,
24436
24942
  "--confirm is required to change a managing agency",
@@ -24465,7 +24971,7 @@ var setWorkspaceType = new Command("set-workspace-type").description("Change a w
24465
24971
  if (reason2.length < 8 || reason2.length > 500)
24466
24972
  fail(this, "--reason must be 8 to 500 characters", 3);
24467
24973
  if (!opts.confirm) {
24468
- const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
24974
+ const quote = (value2) => `'${value2.replaceAll("'", "'\\''")}'`;
24469
24975
  confirmationRequired(
24470
24976
  this,
24471
24977
  "--confirm is required to change a workspace type",
@@ -24510,7 +25016,7 @@ function storeMoveRequest(opts) {
24510
25016
  var moveStores = new Command("move-stores").description("Preview or confirm an audited set of store moves").requiredOption("--to <org-id>", "Destination organization UUID").requiredOption(
24511
25017
  "--shop <shop-id>",
24512
25018
  "Store UUID, repeat for each store",
24513
- (value, previous) => [...previous, value],
25019
+ (value2, previous) => [...previous, value2],
24514
25020
  []
24515
25021
  ).requiredOption("--reason <text>", "Audit reason, 8 to 500 characters").option("--keep-grants", "Preserve explicit store roles and overrides").option("--confirm", "Perform the move; otherwise return a read-only plan").option("--json", "Output JSON").action(async function(opts) {
24516
25022
  let request2;
@@ -24534,7 +25040,7 @@ var moveStores = new Command("move-stores").description("Preview or confirm an a
24534
25040
  output(this, res.data, () => {
24535
25041
  printTable(
24536
25042
  ["FIELD", "VALUE"],
24537
- Object.entries(res.data.data).map(([key, value]) => [key, JSON.stringify(value)])
25043
+ Object.entries(res.data.data).map(([key, value2]) => [key, JSON.stringify(value2)])
24538
25044
  );
24539
25045
  });
24540
25046
  });
@@ -24560,8 +25066,8 @@ async function confirm(c, force, label, retry) {
24560
25066
  if (force) return true;
24561
25067
  if (!process.stdin.isTTY || !process.stdout.isTTY)
24562
25068
  confirmationRequired(c, `${label} requires confirmation in non-TTY mode.`, retry);
24563
- const readline3 = await import("node:readline");
24564
- const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
25069
+ const readline4 = await import("node:readline");
25070
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
24565
25071
  const answer = await new Promise(
24566
25072
  (resolve2) => rl.question(source_default.yellow(`${label}? [y/N] `), resolve2)
24567
25073
  );
@@ -24800,6 +25306,48 @@ ${res.data.data.avatarDataUrl ?? "(no avatar)"}`
24800
25306
  }).addCommand(set6);
24801
25307
  var accountCommand = new Command("account").description("Manage your Apex account").addCommand(profile2);
24802
25308
 
25309
+ // src/commands/commerce.ts
25310
+ async function run3(command, params) {
25311
+ const response = await lookupCommerceCatalog(requireCredentials(), params);
25312
+ if (!response.ok || !response.data)
25313
+ fail(
25314
+ command,
25315
+ response.error ?? "Catalog lookup failed",
25316
+ classifyStatus(response.status),
25317
+ response.errorDetails
25318
+ );
25319
+ output(command, response.data, () => {
25320
+ const data = response.data.data;
25321
+ const products = data.mode === "product" ? [data.product] : data.products;
25322
+ const rows = products.flatMap(
25323
+ (product) => product.variants.map((variant) => [
25324
+ variant.id,
25325
+ variant.title,
25326
+ variant.sku ?? "",
25327
+ variant.price,
25328
+ variant.unit_cost ?? "unknown",
25329
+ // Search results carry no exclusion data; never present them as eligible.
25330
+ variant.price_test_exclusion ?? "unknown"
25331
+ ])
25332
+ );
25333
+ printTable(
25334
+ ["GID", "TITLE", "SKU", "PRICE", "COST", "EXCLUSION"],
25335
+ rows,
25336
+ [42, 24, 18, 12, 12, 14]
25337
+ );
25338
+ });
25339
+ }
25340
+ var search = new Command("search").description("Search the commerce product catalog").argument("[query]", "Product search query").option("--cursor <cursor>").option("--limit <limit>", "Products per page", (v) => Number(v)).action(async function(query, opts) {
25341
+ await run3(this, { query, ...opts });
25342
+ });
25343
+ var get10 = new Command("get").description("Get one product and its variants").argument("[handle]", "Exact product handle").option("--product-id <id>", "Shopify product ID").option("--cursor <cursor>").action(async function(handle, opts) {
25344
+ if (!handle && !opts.productId || handle && opts.productId)
25345
+ fail(this, "Provide exactly one of <handle> or --product-id", 3);
25346
+ await run3(this, { handle, product_id: opts.productId, cursor: opts.cursor });
25347
+ });
25348
+ var catalog = new Command("catalog").description("Look up products for commerce drafts").addCommand(search).addCommand(get10);
25349
+ var commerceCommand = new Command("commerce").description("Commerce catalog tools").addCommand(catalog);
25350
+
24803
25351
  // src/commands/keys.ts
24804
25352
  function selected() {
24805
25353
  const credentials = requireUserCredentials();
@@ -24884,17 +25432,17 @@ async function readResponseText(res) {
24884
25432
  let totalBytes = 0;
24885
25433
  try {
24886
25434
  while (totalBytes < MAX_RESPONSE_BYTES) {
24887
- const { done: done2, value } = await reader.read();
25435
+ const { done: done2, value: value2 } = await reader.read();
24888
25436
  if (done2) break;
24889
25437
  const remainingBytes = MAX_RESPONSE_BYTES - totalBytes;
24890
- if (value.byteLength >= remainingBytes) {
24891
- chunks.push(value.byteLength === remainingBytes ? value : value.slice(0, remainingBytes));
25438
+ if (value2.byteLength >= remainingBytes) {
25439
+ chunks.push(value2.byteLength === remainingBytes ? value2 : value2.slice(0, remainingBytes));
24892
25440
  totalBytes += remainingBytes;
24893
25441
  await reader.cancel().catch(() => void 0);
24894
25442
  break;
24895
25443
  }
24896
- chunks.push(value);
24897
- totalBytes += value.byteLength;
25444
+ chunks.push(value2);
25445
+ totalBytes += value2.byteLength;
24898
25446
  }
24899
25447
  } finally {
24900
25448
  reader.releaseLock();
@@ -24924,8 +25472,8 @@ async function fetchText(url) {
24924
25472
  });
24925
25473
  return { status: res.status, text: await readResponseText(res), finalUrl: res.url };
24926
25474
  }
24927
- function abs(base, path26) {
24928
- return new URL(path26, base).toString();
25475
+ function abs(base, path27) {
25476
+ return new URL(path27, base).toString();
24929
25477
  }
24930
25478
  function looksLikeShopifyCart(text2) {
24931
25479
  try {
@@ -24945,9 +25493,9 @@ async function collectSignals(url) {
24945
25493
  const chunks = [`__apex_primary_status_${primary.status}__`, primary.text];
24946
25494
  const protectedHit = primary.status === 403 || primary.status === 429 || matchDetectionMarkers(primary.text, "protected").length > 0;
24947
25495
  if (!protectedHit) {
24948
- for (const path26 of ["/cart.js"]) {
25496
+ for (const path27 of ["/cart.js"]) {
24949
25497
  try {
24950
- const secondary = await fetchText(abs(primary.finalUrl || url, path26));
25498
+ const secondary = await fetchText(abs(primary.finalUrl || url, path27));
24951
25499
  if (secondary.status >= 200 && secondary.status < 300 && looksLikeShopifyCart(secondary.text)) {
24952
25500
  chunks.push("__apex_secondary_shopify_cart_js__", secondary.text.slice(0, 2e3));
24953
25501
  }
@@ -25133,7 +25681,7 @@ var list14 = new Command("list").alias("ls").description(
25133
25681
  var create8 = new Command("create").description(`Create a webhook. Prints the signing secret once. Signature docs: ${SIGNATURE_DOC}`).requiredOption("--url <url>", "HTTPS callback URL").option(
25134
25682
  "--event <event>",
25135
25683
  "Webhook event to subscribe to; repeat for multiple events",
25136
- (value, previous = []) => [...previous, value],
25684
+ (value2, previous = []) => [...previous, value2],
25137
25685
  []
25138
25686
  ).action(async function action73(opts) {
25139
25687
  const events = selectedEvents(opts);
@@ -25188,7 +25736,7 @@ var deliveries = new Command("deliveries").description("List webhook deliveries
25188
25736
  var update5 = new Command("update").description("Update a webhook URL, events, or active state").argument("<id>", "Webhook id").option("--url <url>", "HTTPS callback URL").option(
25189
25737
  "--event <event>",
25190
25738
  "Replacement event; repeat for multiple events",
25191
- (value, previous = []) => [...previous, value],
25739
+ (value2, previous = []) => [...previous, value2],
25192
25740
  []
25193
25741
  ).option("--active", "Enable the webhook").option("--inactive", "Disable the webhook").action(async function action76(id, opts) {
25194
25742
  if (opts.active && opts.inactive)
@@ -25242,10 +25790,10 @@ var webhooksCommand = new Command("webhooks").description("Manage shop webhooks"
25242
25790
 
25243
25791
  // src/commands/simulate.ts
25244
25792
  init_source();
25245
- var import_node_fs27 = __toESM(require("node:fs"));
25246
- var import_node_path24 = __toESM(require("node:path"));
25793
+ var import_node_fs28 = __toESM(require("node:fs"));
25794
+ var import_node_path25 = __toESM(require("node:path"));
25247
25795
  function readJson2(file) {
25248
- const text2 = file === "-" ? import_node_fs27.default.readFileSync(0, "utf8") : import_node_fs27.default.readFileSync(import_node_path24.default.resolve(file), "utf8");
25796
+ const text2 = file === "-" ? import_node_fs28.default.readFileSync(0, "utf8") : import_node_fs28.default.readFileSync(import_node_path25.default.resolve(file), "utf8");
25249
25797
  try {
25250
25798
  return JSON.parse(text2);
25251
25799
  } catch (error) {
@@ -25257,21 +25805,21 @@ function readJson2(file) {
25257
25805
  function plural(count2, singular3) {
25258
25806
  return `${count2} ${singular3}${count2 === 1 ? "" : "s"}`;
25259
25807
  }
25260
- function isGtagEventTuple(value) {
25261
- return Array.isArray(value) && value[0] === "event" && typeof value[1] === "string";
25808
+ function isGtagEventTuple(value2) {
25809
+ return Array.isArray(value2) && value2[0] === "event" && typeof value2[1] === "string";
25262
25810
  }
25263
- function isPlainObject(value) {
25264
- if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
25265
- const prototype = Object.getPrototypeOf(value);
25811
+ function isPlainObject(value2) {
25812
+ if (value2 === null || typeof value2 !== "object" || Array.isArray(value2)) return false;
25813
+ const prototype = Object.getPrototypeOf(value2);
25266
25814
  return prototype === Object.prototype || prototype === null;
25267
25815
  }
25268
- function isExplicitPayloadBatch(value) {
25269
- if (!Array.isArray(value) || isGtagEventTuple(value)) return false;
25270
- if (value.length > 0 && value.every((entry) => isPlainObject(entry) || isGtagEventTuple(entry))) {
25816
+ function isExplicitPayloadBatch(value2) {
25817
+ if (!Array.isArray(value2) || isGtagEventTuple(value2)) return false;
25818
+ if (value2.length > 0 && value2.every((entry) => isPlainObject(entry) || isGtagEventTuple(entry))) {
25271
25819
  return true;
25272
25820
  }
25273
- if (value.length > 0 && typeof value[0] === "string") return false;
25274
- return value.some((entry) => isPlainObject(entry) || isGtagEventTuple(entry));
25821
+ if (value2.length > 0 && typeof value2[0] === "string") return false;
25822
+ return value2.some((entry) => isPlainObject(entry) || isGtagEventTuple(entry));
25275
25823
  }
25276
25824
  var simulateCommand = new Command("simulate").description("Simulate data-layer triggers without emitting events").option("--config <file>", "Trigger config JSON file").requiredOption("--payload <file|->", "Payload JSON file or stdin").option("--from-shop", "Load live triggers from the selected shop").action(async function(opts) {
25277
25825
  if (Boolean(opts.config) === Boolean(opts.fromShop)) {
@@ -25354,7 +25902,7 @@ var simulateCommand = new Command("simulate").description("Simulate data-layer t
25354
25902
  }
25355
25903
  if (!result.matches.length) {
25356
25904
  const numericPaths = result.diagnostics.numericPaths.map(
25357
- ({ path: numericPath, value }) => `${numericPath}=${value}`
25905
+ ({ path: numericPath, value: value2 }) => `${numericPath}=${value2}`
25358
25906
  ).join(", ");
25359
25907
  console.log(` numeric paths: ${numericPaths || "none"}`);
25360
25908
  }
@@ -25367,14 +25915,14 @@ var simulateCommand = new Command("simulate").description("Simulate data-layer t
25367
25915
  });
25368
25916
 
25369
25917
  // src/commands/config-resources.ts
25370
- var import_node_fs29 = __toESM(require("node:fs"));
25371
- var import_node_path26 = __toESM(require("node:path"));
25918
+ var import_node_fs30 = __toESM(require("node:fs"));
25919
+ var import_node_path27 = __toESM(require("node:path"));
25372
25920
 
25373
25921
  // src/commands/landing-pages.ts
25374
25922
  var import_node_crypto9 = __toESM(require("node:crypto"));
25375
- var import_node_fs28 = __toESM(require("node:fs"));
25376
- var import_node_path25 = __toESM(require("node:path"));
25377
- function apiFailure(command, response, fallback2) {
25923
+ var import_node_fs29 = __toESM(require("node:fs"));
25924
+ var import_node_path26 = __toESM(require("node:path"));
25925
+ function apiFailure2(command, response, fallback2) {
25378
25926
  if (!response.ok || response.data === void 0) {
25379
25927
  fail(
25380
25928
  command,
@@ -25388,13 +25936,13 @@ function sha256(body5) {
25388
25936
  return import_node_crypto9.default.createHash("sha256").update(body5).digest("hex");
25389
25937
  }
25390
25938
  function markerPath(folder) {
25391
- return import_node_path25.default.join(folder, LANDING_PAGE_CHECKOUT_MARKER);
25939
+ return import_node_path26.default.join(folder, LANDING_PAGE_CHECKOUT_MARKER);
25392
25940
  }
25393
25941
  function readMarker(folder) {
25394
25942
  const file = markerPath(folder);
25395
- if (!import_node_fs28.default.existsSync(file)) return null;
25943
+ if (!import_node_fs29.default.existsSync(file)) return null;
25396
25944
  try {
25397
- return JSON.parse(import_node_fs28.default.readFileSync(file, "utf8"));
25945
+ return JSON.parse(import_node_fs29.default.readFileSync(file, "utf8"));
25398
25946
  } catch (error) {
25399
25947
  throw new CliError(
25400
25948
  `${LANDING_PAGE_CHECKOUT_MARKER} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
@@ -25412,14 +25960,14 @@ function readMarker(folder) {
25412
25960
  }
25413
25961
  }
25414
25962
  function writeMarker(folder, marker) {
25415
- import_node_fs28.default.mkdirSync(import_node_path25.default.dirname(markerPath(folder)), { recursive: true });
25416
- import_node_fs28.default.writeFileSync(import_node_path25.default.join(folder, ".apex", ".gitignore"), "landing-page.json\n", "utf8");
25417
- import_node_fs28.default.writeFileSync(markerPath(folder), `${JSON.stringify(marker, null, 2)}
25963
+ import_node_fs29.default.mkdirSync(import_node_path26.default.dirname(markerPath(folder)), { recursive: true });
25964
+ import_node_fs29.default.writeFileSync(import_node_path26.default.join(folder, ".apex", ".gitignore"), "landing-page.json\n", "utf8");
25965
+ import_node_fs29.default.writeFileSync(markerPath(folder), `${JSON.stringify(marker, null, 2)}
25418
25966
  `, "utf8");
25419
25967
  }
25420
25968
  function requireCheckout(command, folderArg) {
25421
- const folder = import_node_path25.default.resolve(process.cwd(), folderArg);
25422
- const marker = import_node_fs28.default.existsSync(folder) && import_node_fs28.default.statSync(folder).isDirectory() ? readMarker(folder) : null;
25969
+ const folder = import_node_path26.default.resolve(process.cwd(), folderArg);
25970
+ const marker = import_node_fs29.default.existsSync(folder) && import_node_fs29.default.statSync(folder).isDirectory() ? readMarker(folder) : null;
25423
25971
  if (!marker) {
25424
25972
  fail(command, `${folderArg} is not a landing-page checkout.`, EXIT_CODES.validation, {
25425
25973
  code: "validation_failed",
@@ -25431,8 +25979,8 @@ function requireCheckout(command, folderArg) {
25431
25979
  return { folder, marker };
25432
25980
  }
25433
25981
  function resolvePageId(target) {
25434
- const folder = import_node_path25.default.resolve(process.cwd(), target);
25435
- if (import_node_fs28.default.existsSync(folder) && import_node_fs28.default.statSync(folder).isDirectory()) {
25982
+ const folder = import_node_path26.default.resolve(process.cwd(), target);
25983
+ if (import_node_fs29.default.existsSync(folder) && import_node_fs29.default.statSync(folder).isDirectory()) {
25436
25984
  const marker = readMarker(folder);
25437
25985
  if (marker) return marker.landingPageId;
25438
25986
  throw new CliError(
@@ -25454,16 +26002,16 @@ function resolvePageId(target) {
25454
26002
  function readCheckoutFiles(folder) {
25455
26003
  const files = [];
25456
26004
  const visit2 = (directory) => {
25457
- for (const entry of import_node_fs28.default.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
25458
- const absolute = import_node_path25.default.join(directory, entry.name);
25459
- const relative = import_node_path25.default.relative(folder, absolute).split(import_node_path25.default.sep).join("/");
26005
+ for (const entry of import_node_fs29.default.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
26006
+ const absolute = import_node_path26.default.join(directory, entry.name);
26007
+ const relative = import_node_path26.default.relative(folder, absolute).split(import_node_path26.default.sep).join("/");
25460
26008
  if (!isLandingPageCheckoutPath(relative)) continue;
25461
26009
  if (entry.isDirectory()) visit2(absolute);
25462
26010
  else if (entry.isFile()) {
25463
26011
  try {
25464
26012
  validateLandingPageLocalFiles([
25465
26013
  ...files,
25466
- { path: relative, bytes: import_node_fs28.default.statSync(absolute).size, sha256: "" }
26014
+ { path: relative, bytes: import_node_fs29.default.statSync(absolute).size, sha256: "" }
25467
26015
  ]);
25468
26016
  } catch (error) {
25469
26017
  throw new CliError(
@@ -25471,7 +26019,7 @@ function readCheckoutFiles(folder) {
25471
26019
  EXIT_CODES.validation
25472
26020
  );
25473
26021
  }
25474
- const body5 = import_node_fs28.default.readFileSync(absolute);
26022
+ const body5 = import_node_fs29.default.readFileSync(absolute);
25475
26023
  files.push({ path: relative, sha256: sha256(body5), bytes: body5.byteLength, body: body5 });
25476
26024
  }
25477
26025
  }
@@ -25488,8 +26036,8 @@ function readCheckoutFiles(folder) {
25488
26036
  return files;
25489
26037
  }
25490
26038
  function readManifest2(command, folder) {
25491
- const file = import_node_path25.default.join(folder, LANDING_PAGE_MANIFEST_FILE);
25492
- if (!import_node_fs28.default.existsSync(file)) {
26039
+ const file = import_node_path26.default.join(folder, LANDING_PAGE_MANIFEST_FILE);
26040
+ if (!import_node_fs29.default.existsSync(file)) {
25493
26041
  fail(
25494
26042
  command,
25495
26043
  `${LANDING_PAGE_MANIFEST_FILE} is missing from the checkout.`,
@@ -25500,7 +26048,7 @@ function readManifest2(command, folder) {
25500
26048
  );
25501
26049
  }
25502
26050
  try {
25503
- return JSON.parse(import_node_fs28.default.readFileSync(file, "utf8"));
26051
+ return JSON.parse(import_node_fs29.default.readFileSync(file, "utf8"));
25504
26052
  } catch (error) {
25505
26053
  fail(
25506
26054
  command,
@@ -25546,15 +26094,15 @@ function printPlan(plan2) {
25546
26094
  }
25547
26095
  async function confirm2(message) {
25548
26096
  if (!process.stdin.isTTY || !process.stdout.isTTY) return null;
25549
- const readline3 = await import("node:readline");
25550
- const rl = readline3.createInterface({ input: process.stdin, output: process.stderr });
26097
+ const readline4 = await import("node:readline");
26098
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stderr });
25551
26099
  const answer = await new Promise((resolve2) => rl.question(`${message} [y/N] `, resolve2));
25552
26100
  rl.close();
25553
26101
  return answer.trim().toLowerCase() === "y";
25554
26102
  }
25555
26103
  function manifestDigest(folder) {
25556
- const file = import_node_path25.default.join(folder, LANDING_PAGE_MANIFEST_FILE);
25557
- return import_node_fs28.default.existsSync(file) ? sha256(import_node_fs28.default.readFileSync(file)) : null;
26104
+ const file = import_node_path26.default.join(folder, LANDING_PAGE_MANIFEST_FILE);
26105
+ return import_node_fs29.default.existsSync(file) ? sha256(import_node_fs29.default.readFileSync(file)) : null;
25558
26106
  }
25559
26107
  function localChanges(folder, marker) {
25560
26108
  const local = new Map(readCheckoutFiles(folder).map((file) => [file.path, file.sha256]));
@@ -25572,8 +26120,8 @@ function removeStaleFiles(folder, previous, next, added = []) {
25572
26120
  if (!previous) return;
25573
26121
  for (const file of [...Object.keys(previous.files), ...added]) {
25574
26122
  if (next.has(file) || !isLandingPageCheckoutPath(file)) continue;
25575
- const absolute = import_node_path25.default.resolve(folder, file);
25576
- if (absolute.startsWith(folder + import_node_path25.default.sep) && import_node_fs28.default.existsSync(absolute)) import_node_fs28.default.rmSync(absolute);
26123
+ const absolute = import_node_path26.default.resolve(folder, file);
26124
+ if (absolute.startsWith(folder + import_node_path26.default.sep) && import_node_fs29.default.existsSync(absolute)) import_node_fs29.default.rmSync(absolute);
25577
26125
  }
25578
26126
  }
25579
26127
  async function pullCheckout(command, creds, id, folder, force = false) {
@@ -25612,8 +26160,8 @@ async function pullCheckout(command, creds, id, folder, force = false) {
25612
26160
  }
25613
26161
  const api = landingPages(creds);
25614
26162
  const listing = await api.checkout(id);
25615
- apiFailure(command, listing, "Failed to pull landing page");
25616
- import_node_fs28.default.mkdirSync(folder, { recursive: true });
26163
+ apiFailure2(command, listing, "Failed to pull landing page");
26164
+ import_node_fs29.default.mkdirSync(folder, { recursive: true });
25617
26165
  const written = {};
25618
26166
  const downloaded = [];
25619
26167
  for (const batch of landingPageBatches(listing.data.files, (file) => file.bytes)) {
@@ -25621,14 +26169,14 @@ async function pullCheckout(command, creds, id, folder, force = false) {
25621
26169
  id,
25622
26170
  batch.map((file) => file.path)
25623
26171
  );
25624
- apiFailure(command, contents, "Failed to pull landing page files");
26172
+ apiFailure2(command, contents, "Failed to pull landing page files");
25625
26173
  const returned2 = new Set(contents.data.files.map((file) => file.path));
25626
26174
  const missing = batch.filter((file) => !returned2.has(file.path)).map((file) => file.path);
25627
26175
  if (missing.length)
25628
26176
  fail(command, `The server did not return ${missing.slice(0, 5).join(", ")}. Pull again.`);
25629
26177
  for (const file of contents.data.files) {
25630
- const target = import_node_path25.default.resolve(folder, file.path);
25631
- if (!target.startsWith(folder + import_node_path25.default.sep))
26178
+ const target = import_node_path26.default.resolve(folder, file.path);
26179
+ if (!target.startsWith(folder + import_node_path26.default.sep))
25632
26180
  fail(command, `Refusing to write outside the checkout: ${file.path}`);
25633
26181
  const download = file.download_url ? await fetch(file.download_url, { signal: AbortSignal.timeout(6e4) }) : null;
25634
26182
  if (download && !download.ok)
@@ -25641,7 +26189,7 @@ async function pullCheckout(command, creds, id, folder, force = false) {
25641
26189
  }
25642
26190
  if (!existing && !force) {
25643
26191
  const clobbered = downloaded.filter(
25644
- ({ target, sha256: digest }) => import_node_fs28.default.existsSync(target) && sha256(import_node_fs28.default.readFileSync(target)) !== digest
26192
+ ({ target, sha256: digest }) => import_node_fs29.default.existsSync(target) && sha256(import_node_fs29.default.readFileSync(target)) !== digest
25645
26193
  ).map((file) => file.path);
25646
26194
  if (clobbered.length) {
25647
26195
  fail(
@@ -25665,8 +26213,8 @@ async function pullCheckout(command, creds, id, folder, force = false) {
25665
26213
  }
25666
26214
  }
25667
26215
  for (const file of downloaded) {
25668
- import_node_fs28.default.mkdirSync(import_node_path25.default.dirname(file.target), { recursive: true });
25669
- import_node_fs28.default.writeFileSync(file.target, file.body);
26216
+ import_node_fs29.default.mkdirSync(import_node_path26.default.dirname(file.target), { recursive: true });
26217
+ import_node_fs29.default.writeFileSync(file.target, file.body);
25670
26218
  written[file.path] = file.sha256;
25671
26219
  }
25672
26220
  removeStaleFiles(
@@ -25675,24 +26223,24 @@ async function pullCheckout(command, creds, id, folder, force = false) {
25675
26223
  new Set(Object.keys(written)),
25676
26224
  force ? readCheckoutFiles(folder).map((file) => file.path) : []
25677
26225
  );
25678
- import_node_fs28.default.writeFileSync(
25679
- import_node_path25.default.join(folder, LANDING_PAGE_MANIFEST_FILE),
26226
+ import_node_fs29.default.writeFileSync(
26227
+ import_node_path26.default.join(folder, LANDING_PAGE_MANIFEST_FILE),
25680
26228
  `${JSON.stringify(listing.data.manifest ?? {}, null, 2)}
25681
26229
  `,
25682
26230
  "utf8"
25683
26231
  );
25684
- const contextDir = import_node_path25.default.join(folder, LANDING_PAGE_CONTEXT_DIR);
25685
- import_node_fs28.default.mkdirSync(contextDir, { recursive: true });
26232
+ const contextDir = import_node_path26.default.join(folder, LANDING_PAGE_CONTEXT_DIR);
26233
+ import_node_fs29.default.mkdirSync(contextDir, { recursive: true });
25686
26234
  const contextName = /^[a-z0-9_-]+\.json$/i;
25687
26235
  const contextEntries = Object.entries(listing.data.context ?? {}).filter(
25688
26236
  ([name]) => contextName.test(name)
25689
26237
  );
25690
26238
  const returned = new Set(contextEntries.map(([name]) => name));
25691
- for (const name of import_node_fs28.default.readdirSync(contextDir)) {
25692
- if (contextName.test(name) && !returned.has(name)) import_node_fs28.default.rmSync(import_node_path25.default.join(contextDir, name));
26239
+ for (const name of import_node_fs29.default.readdirSync(contextDir)) {
26240
+ if (contextName.test(name) && !returned.has(name)) import_node_fs29.default.rmSync(import_node_path26.default.join(contextDir, name));
25693
26241
  }
25694
- for (const [name, value] of contextEntries) {
25695
- import_node_fs28.default.writeFileSync(import_node_path25.default.join(contextDir, name), `${JSON.stringify(value, null, 2)}
26242
+ for (const [name, value2] of contextEntries) {
26243
+ import_node_fs29.default.writeFileSync(import_node_path26.default.join(contextDir, name), `${JSON.stringify(value2, null, 2)}
25696
26244
  `, "utf8");
25697
26245
  }
25698
26246
  writeMarker(folder, {
@@ -25723,8 +26271,8 @@ var create9 = new Command("create").description("Create a landing page to build
25723
26271
  ...opts.slug ? { slug: opts.slug } : {},
25724
26272
  ...opts.idea ? { idea_key: opts.idea } : {}
25725
26273
  });
25726
- apiFailure(this, created, "Failed to create landing page");
25727
- const pulled = opts.out ? await pullCheckout(this, creds, created.data.id, import_node_path25.default.resolve(process.cwd(), opts.out)) : null;
26274
+ apiFailure2(this, created, "Failed to create landing page");
26275
+ const pulled = opts.out ? await pullCheckout(this, creds, created.data.id, import_node_path26.default.resolve(process.cwd(), opts.out)) : null;
25728
26276
  output(
25729
26277
  this,
25730
26278
  { landing_page: created.data, ...pulled ? { folder: pulled.folder } : {} },
@@ -25736,7 +26284,7 @@ var create9 = new Command("create").description("Create a landing page to build
25736
26284
  });
25737
26285
  var pull = new Command("pull").description("Pull a landing page into a checkout folder").argument("<id>", "Landing page id").option("--out <folder>", "Target folder (default: a folder named after the id)").option("--force", "Overwrite changes in the checkout that are not applied").action(async function(id, opts) {
25738
26286
  const creds = requireCredentials();
25739
- const folder = import_node_path25.default.resolve(process.cwd(), opts.out ?? id);
26287
+ const folder = import_node_path26.default.resolve(process.cwd(), opts.out ?? id);
25740
26288
  const result = await pullCheckout(this, creds, id, folder, opts.force === true);
25741
26289
  output(this, result, () => {
25742
26290
  console.log(
@@ -25754,7 +26302,7 @@ var plan = new Command("plan").description("Show what applying a checkout would
25754
26302
  manifest: readManifest2(this, folder),
25755
26303
  files: declared(files)
25756
26304
  });
25757
- apiFailure(this, response, "Failed to plan landing page");
26305
+ apiFailure2(this, response, "Failed to plan landing page");
25758
26306
  output(
25759
26307
  this,
25760
26308
  { folder, landingPageId: marker.landingPageId, plan: response.data },
@@ -25768,7 +26316,7 @@ var apply = new Command("apply").description("Apply a checkout as a new version"
25768
26316
  const files = readCheckoutFiles(folder);
25769
26317
  const manifest = readManifest2(this, folder);
25770
26318
  const planned = await api.plan(marker.landingPageId, { manifest, files: declared(files) });
25771
- apiFailure(this, planned, "Failed to plan landing page");
26319
+ apiFailure2(this, planned, "Failed to plan landing page");
25772
26320
  if (!planned.data.would_change) {
25773
26321
  output(
25774
26322
  this,
@@ -25801,7 +26349,7 @@ var apply = new Command("apply").description("Apply a checkout as a new version"
25801
26349
  sha256: file.sha256,
25802
26350
  base_version_id: planned.data.base_version_id
25803
26351
  });
25804
- apiFailure(this, granted, "Failed to grant video upload");
26352
+ apiFailure2(this, granted, "Failed to grant video upload");
25805
26353
  await uploadLandingVideo(granted.data, file.body, {
25806
26354
  progress: (uploaded, total) => {
25807
26355
  console.error(`Uploading ${file.path}: ${uploaded}/${total} bytes`);
@@ -25820,7 +26368,7 @@ var apply = new Command("apply").description("Apply a checkout as a new version"
25820
26368
  encoding: "base64"
25821
26369
  }))
25822
26370
  );
25823
- apiFailure(this, uploaded, "Failed to upload landing page files");
26371
+ apiFailure2(this, uploaded, "Failed to upload landing page files");
25824
26372
  }
25825
26373
  const applied = await api.apply(marker.landingPageId, {
25826
26374
  manifest,
@@ -25847,7 +26395,7 @@ var apply = new Command("apply").description("Apply a checkout as a new version"
25847
26395
  }
25848
26396
  );
25849
26397
  }
25850
- apiFailure(this, applied, "Failed to apply landing page");
26398
+ apiFailure2(this, applied, "Failed to apply landing page");
25851
26399
  writeMarker(folder, {
25852
26400
  ...marker,
25853
26401
  versionId: applied.data.version_id,
@@ -25873,7 +26421,7 @@ var preview = new Command("preview").description("Print a signed preview link (v
25873
26421
  resolvePageId(target),
25874
26422
  versionNumber
25875
26423
  );
25876
- apiFailure(this, response, "Failed to get preview link");
26424
+ apiFailure2(this, response, "Failed to get preview link");
25877
26425
  output(this, response.data, () => console.log(response.data.preview_url));
25878
26426
  });
25879
26427
  async function versionIdFor(command, creds, id, raw) {
@@ -25882,7 +26430,7 @@ async function versionIdFor(command, creds, id, raw) {
25882
26430
  if (!Number.isInteger(number) || number < 1)
25883
26431
  fail(command, "--version must be a version number.", EXIT_CODES.validation);
25884
26432
  const versions2 = await landingPages(creds).versions(id);
25885
- apiFailure(command, versions2, "Failed to list versions");
26433
+ apiFailure2(command, versions2, "Failed to list versions");
25886
26434
  const match2 = versions2.data.find((version) => version.version_number === number);
25887
26435
  if (!match2) fail(command, `Version ${number} not found.`, EXIT_CODES.validation);
25888
26436
  return match2.id;
@@ -25902,7 +26450,7 @@ var deploy = new Command("deploy").description(
25902
26450
  ...opts.host ? { host: opts.host } : {},
25903
26451
  ...opts.path ? { path: opts.path } : {}
25904
26452
  });
25905
- apiFailure(this, response, "Failed to deploy landing page");
26453
+ apiFailure2(this, response, "Failed to deploy landing page");
25906
26454
  output(this, response.data, () => {
25907
26455
  console.log(response.data.url);
25908
26456
  console.log(`Version ${response.data.version_number} is live.`);
@@ -25910,7 +26458,7 @@ var deploy = new Command("deploy").description(
25910
26458
  }).addHelpText("after", SPLIT_KEEPS_SERVING_HELP);
25911
26459
  var versions = new Command("versions").description("List versions").argument("[folder-or-id]", "Checkout folder or landing page id", ".").action(async function(target) {
25912
26460
  const response = await landingPages(requireCredentials()).versions(resolvePageId(target));
25913
- apiFailure(this, response, "Failed to list versions");
26461
+ apiFailure2(this, response, "Failed to list versions");
25914
26462
  output(
25915
26463
  this,
25916
26464
  response.data,
@@ -25935,7 +26483,7 @@ var rollback = new Command("rollback").description("Deploy an earlier version to
25935
26483
  const response = await landingPages(requireCredentials()).rollback(resolvePageId(target), {
25936
26484
  version_number: number
25937
26485
  });
25938
- apiFailure(this, response, "Failed to roll back landing page");
26486
+ apiFailure2(this, response, "Failed to roll back landing page");
25939
26487
  output(this, response.data, () => {
25940
26488
  console.log(response.data.url);
25941
26489
  console.log(`Version ${response.data.version_number} is live.`);
@@ -25948,8 +26496,8 @@ var experiment = new Command("experiment").description("Create a draft experimen
25948
26496
  "--variant <number>",
25949
26497
  "Variant version number (default: the manifest's experiment.variant)"
25950
26498
  ).option("--name <name>", "Experiment name").option("--primary-goal <key>", "Manifest goal key to make primary (default: the first goal)").action(async function(target, opts) {
25951
- const folder = import_node_path25.default.resolve(process.cwd(), target);
25952
- const fromManifest = import_node_fs28.default.existsSync(import_node_path25.default.join(folder, LANDING_PAGE_MANIFEST_FILE)) ? readManifest2(this, folder)?.experiment ?? {} : {};
26499
+ const folder = import_node_path26.default.resolve(process.cwd(), target);
26500
+ const fromManifest = import_node_fs29.default.existsSync(import_node_path26.default.join(folder, LANDING_PAGE_MANIFEST_FILE)) ? readManifest2(this, folder)?.experiment ?? {} : {};
25953
26501
  const control = Number(opts.control ?? fromManifest.control);
25954
26502
  const variant = Number(opts.variant ?? fromManifest.variant);
25955
26503
  if (!Number.isInteger(control) || !Number.isInteger(variant) || control < 1 || variant < 1) {
@@ -25965,7 +26513,7 @@ var experiment = new Command("experiment").description("Create a draft experimen
25965
26513
  ...opts.name ? { name: opts.name } : {},
25966
26514
  ...opts.primaryGoal ? { primary_goal: opts.primaryGoal } : {}
25967
26515
  });
25968
- apiFailure(this, response, "Failed to create experiment");
26516
+ apiFailure2(this, response, "Failed to create experiment");
25969
26517
  output(this, response.data, () => {
25970
26518
  console.log(`Draft experiment ${String(response.data.experiment.id)} created.`);
25971
26519
  console.log("Start it in the Apex dashboard when it is ready.");
@@ -26009,8 +26557,8 @@ var ALLOWED_ASSET_EXTENSIONS = Object.keys(ASSET_MIME_TYPES).join(", ");
26009
26557
  var MAX_ASSET_SIZE_BYTES = 10 * 1024 * 1024;
26010
26558
  async function confirmAssetDelete(id) {
26011
26559
  if (!process.stdin.isTTY || !process.stdout.isTTY) return null;
26012
- const readline3 = await import("node:readline");
26013
- const rl = readline3.createInterface({
26560
+ const readline4 = await import("node:readline");
26561
+ const rl = readline4.createInterface({
26014
26562
  input: process.stdin,
26015
26563
  output: process.stderr
26016
26564
  });
@@ -26038,28 +26586,30 @@ function readFamily(family, label) {
26038
26586
  this,
26039
26587
  r.data,
26040
26588
  () => printTable(
26041
- ["ID", "NAME", "STATUS"],
26042
- r.data.data.map((x) => [x.id, x.name ?? "-", x.status ?? "-"]),
26043
- [38, 32, 14]
26589
+ family === "assets" ? ["ID", "NAME", "DELIVERY URL", "STATUS"] : ["ID", "NAME", "STATUS"],
26590
+ r.data.data.map(
26591
+ (x) => family === "assets" ? [x.id, x.name ?? "-", x.delivery_url ?? "-", x.status ?? "-"] : [x.id, x.name ?? "-", x.status ?? "-"]
26592
+ ),
26593
+ family === "assets" ? [38, 32, 64, 14] : [38, 32, 14]
26044
26594
  )
26045
26595
  );
26046
26596
  });
26047
- const get10 = new Command("get").description(`Get one ${label.slice(0, -1)}`).argument("<id>").action(async function(id) {
26597
+ const get11 = new Command("get").description(`Get one ${label.slice(0, -1)}`).argument("<id>").action(async function(id) {
26048
26598
  const r = await getConfigResource(requireCredentials(), family, id);
26049
26599
  if (!r.ok || !r.data)
26050
26600
  fail(this, r.error ?? "Failed to get resource", classifyStatus(r.status), r.errorDetails);
26051
26601
  output(this, r.data, () => show(r.data));
26052
26602
  });
26053
- return { list: list15, get: get10 };
26603
+ return { list: list15, get: get11 };
26054
26604
  }
26055
26605
  var assetReads = readFamily("assets", "assets");
26056
26606
  var upload = new Command("upload").description("Upload an asset").argument("<file>").action(async function(file) {
26057
- const absolute = import_node_path26.default.resolve(process.cwd(), file);
26058
- if (!import_node_fs29.default.existsSync(absolute)) fail(this, `File not found: ${file}`, 3);
26059
- const fileStat = import_node_fs29.default.statSync(absolute);
26607
+ const absolute = import_node_path27.default.resolve(process.cwd(), file);
26608
+ if (!import_node_fs30.default.existsSync(absolute)) fail(this, `File not found: ${file}`, 3);
26609
+ const fileStat = import_node_fs30.default.statSync(absolute);
26060
26610
  if (!fileStat.isFile()) fail(this, `File not found: ${file}`, 3);
26061
26611
  if (fileStat.size > MAX_ASSET_SIZE_BYTES) fail(this, `${file} is larger than 10 MB`, 3);
26062
- const mimeType = ASSET_MIME_TYPES[import_node_path26.default.extname(absolute).toLowerCase()];
26612
+ const mimeType = ASSET_MIME_TYPES[import_node_path27.default.extname(absolute).toLowerCase()];
26063
26613
  if (!mimeType)
26064
26614
  fail(
26065
26615
  this,
@@ -26068,8 +26618,8 @@ var upload = new Command("upload").description("Upload an asset").argument("<fil
26068
26618
  );
26069
26619
  const r = await uploadAssets(requireCredentials(), [
26070
26620
  {
26071
- name: import_node_path26.default.basename(absolute),
26072
- bytes: import_node_fs29.default.readFileSync(absolute),
26621
+ name: import_node_path27.default.basename(absolute),
26622
+ bytes: import_node_fs30.default.readFileSync(absolute),
26073
26623
  type: mimeType
26074
26624
  }
26075
26625
  ]);
@@ -26101,7 +26651,7 @@ var landingPagesCommand = new Command("landing-pages").description(
26101
26651
  for (const command of landingPageWorkspaceCommands) landingPagesCommand.addCommand(command);
26102
26652
  var snippet = new Command("snippet").description("Generate an external purchase pixel snippet").option("--provider <name>").option("--handover-param <name>").option("--global-name <name>").action(async function(opts) {
26103
26653
  const body5 = Object.fromEntries(
26104
- Object.entries(opts).filter(([, value]) => value !== void 0)
26654
+ Object.entries(opts).filter(([, value2]) => value2 !== void 0)
26105
26655
  );
26106
26656
  const r = await generateExternalPixelSnippet(requireCredentials(), body5);
26107
26657
  if (!r.ok || !r.data)
@@ -26111,6 +26661,9 @@ var snippet = new Command("snippet").description("Generate an external purchase
26111
26661
  var externalPixelsCommand = new Command("external-pixels").description("Manage external pixels").addCommand(snippet);
26112
26662
 
26113
26663
  // src/cli.ts
26664
+ var argvEncoding = repairArgv(process.argv, { platform: process.platform, env: process.env });
26665
+ process.argv = argvEncoding.argv;
26666
+ var damagedArgumentOptions = replacementCharacterOptions(process.argv);
26114
26667
  var mockApiEnvironmentKey = ["APEX", "CLI", "MOCK", "API"].join("_");
26115
26668
  if (process.env[mockApiEnvironmentKey] === "1" && !cliTestHarnessActive()) {
26116
26669
  const message = "mock harness is not part of the published CLI; use the test artifact";
@@ -26154,6 +26707,8 @@ program2.addCommand(prospectCommand, { hidden: true });
26154
26707
  program2.addCommand(webhooksCommand);
26155
26708
  program2.addCommand(simulateCommand);
26156
26709
  program2.addCommand(experimentsCommand);
26710
+ program2.addCommand(resultsCommand);
26711
+ program2.addCommand(commerceCommand);
26157
26712
  program2.addCommand(pushCommand);
26158
26713
  program2.addCommand(previewCommand);
26159
26714
  program2.addCommand(roadmapCommand);
@@ -26211,6 +26766,18 @@ program2.hook("preAction", (_thisCommand, actionCommand) => {
26211
26766
  throw error;
26212
26767
  }
26213
26768
  configureRuntime(actionCommand);
26769
+ for (const { option } of argvEncoding.repairs) {
26770
+ process.stderr.write(
26771
+ `apex: repaired the text of ${option} (the console decoded UTF-8 as Windows-1252). Set the console to UTF-8: PowerShell [Console]::InputEncoding = [Console]::OutputEncoding = [Text.Encoding]::UTF8; Git Bash LANG=C.UTF-8 and chcp 65001. APEX_ARGV_ENCODING=off disables the repair.
26772
+ `
26773
+ );
26774
+ }
26775
+ for (const option of damagedArgumentOptions) {
26776
+ process.stderr.write(
26777
+ `apex: ${option} contains U+FFFD (the shell replaced a character it could not encode); the value is sent as given.
26778
+ `
26779
+ );
26780
+ }
26214
26781
  beginInvocation(actionCommand, resolveContext());
26215
26782
  });
26216
26783
  process.on("beforeExit", () => {
@@ -26227,11 +26794,11 @@ function isExplicitJsonVersionRequest(args) {
26227
26794
  } else if (arg === "--version" || arg === "-V") {
26228
26795
  hasVersion = true;
26229
26796
  } else if (arg === "--org" || arg === "--shop") {
26230
- const value = args[++index];
26231
- if (!value || value.startsWith("-")) return false;
26797
+ const value2 = args[++index];
26798
+ if (!value2 || value2.startsWith("-")) return false;
26232
26799
  } else if (arg.startsWith("--org=") || arg.startsWith("--shop=")) {
26233
- const value = arg.slice(arg.indexOf("=") + 1);
26234
- if (!value || value.startsWith("-")) return false;
26800
+ const value2 = arg.slice(arg.indexOf("=") + 1);
26801
+ if (!value2 || value2.startsWith("-")) return false;
26235
26802
  } else {
26236
26803
  return false;
26237
26804
  }