@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/AGENTS.md +2 -0
- package/README.md +32 -2
- package/dist/cli.js +1387 -820
- package/package.json +1 -1
- package/skills/apex-cli/SKILL.md +6 -0
package/dist/cli.js
CHANGED
|
@@ -127,11 +127,11 @@ var require_argument = __commonJS({
|
|
|
127
127
|
/**
|
|
128
128
|
* @package
|
|
129
129
|
*/
|
|
130
|
-
_concatValue(
|
|
130
|
+
_concatValue(value2, previous) {
|
|
131
131
|
if (previous === this.defaultValue || !Array.isArray(previous)) {
|
|
132
|
-
return [
|
|
132
|
+
return [value2];
|
|
133
133
|
}
|
|
134
|
-
return previous.concat(
|
|
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(
|
|
144
|
-
this.defaultValue =
|
|
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(
|
|
819
|
-
this.defaultValue =
|
|
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(
|
|
920
|
+
_concatValue(value2, previous) {
|
|
921
921
|
if (previous === this.defaultValue || !Array.isArray(previous)) {
|
|
922
|
-
return [
|
|
922
|
+
return [value2];
|
|
923
923
|
}
|
|
924
|
-
return previous.concat(
|
|
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((
|
|
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(
|
|
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 ===
|
|
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
|
|
1163
|
-
var
|
|
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,
|
|
1653
|
+
_callParseArg(target, value2, previous, invalidArgumentMessage) {
|
|
1654
1654
|
try {
|
|
1655
|
-
return target.parseArg(
|
|
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,
|
|
1941
|
-
return this.setOptionValueWithSource(key,
|
|
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,
|
|
1951
|
+
setOptionValueWithSource(key, value2, source) {
|
|
1952
1952
|
if (this._storeOptionsAsProperties) {
|
|
1953
|
-
this[key] =
|
|
1953
|
+
this[key] = value2;
|
|
1954
1954
|
} else {
|
|
1955
|
-
this._optionValues[key] =
|
|
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 (
|
|
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 =
|
|
2163
|
-
if (
|
|
2164
|
-
if (sourceExt.includes(
|
|
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) =>
|
|
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 =
|
|
2178
|
+
resolvedScriptPath = fs31.realpathSync(this._scriptPath);
|
|
2179
2179
|
} catch {
|
|
2180
2180
|
resolvedScriptPath = this._scriptPath;
|
|
2181
2181
|
}
|
|
2182
|
-
executableDir =
|
|
2183
|
-
|
|
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 =
|
|
2190
|
+
const legacyName = path27.basename(
|
|
2191
2191
|
this._scriptPath,
|
|
2192
|
-
|
|
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(
|
|
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,
|
|
2339
|
-
let parsedValue =
|
|
2340
|
-
if (
|
|
2341
|
-
const invalidValueMessage = `error: command-argument value '${
|
|
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
|
-
|
|
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
|
|
2354
|
+
let value2 = declaredArg.defaultValue;
|
|
2355
2355
|
if (declaredArg.variadic) {
|
|
2356
2356
|
if (index < this.args.length) {
|
|
2357
|
-
|
|
2357
|
+
value2 = this.args.slice(index);
|
|
2358
2358
|
if (declaredArg.parseArg) {
|
|
2359
|
-
|
|
2359
|
+
value2 = value2.reduce((processed, v) => {
|
|
2360
2360
|
return myParseArg(declaredArg, v, processed);
|
|
2361
2361
|
}, declaredArg.defaultValue);
|
|
2362
2362
|
}
|
|
2363
|
-
} else if (
|
|
2364
|
-
|
|
2363
|
+
} else if (value2 === void 0) {
|
|
2364
|
+
value2 = [];
|
|
2365
2365
|
}
|
|
2366
2366
|
} else if (index < this.args.length) {
|
|
2367
|
-
|
|
2367
|
+
value2 = this.args[index];
|
|
2368
2368
|
if (declaredArg.parseArg) {
|
|
2369
|
-
|
|
2369
|
+
value2 = myParseArg(declaredArg, value2, declaredArg.defaultValue);
|
|
2370
2370
|
}
|
|
2371
2371
|
}
|
|
2372
|
-
processedArgs[index] =
|
|
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
|
|
2630
|
-
if (
|
|
2631
|
-
this.emit(`option:${option.name()}`,
|
|
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
|
|
2633
|
+
let value2 = null;
|
|
2634
2634
|
if (args.length > 0 && !maybeOption(args[0])) {
|
|
2635
|
-
|
|
2635
|
+
value2 = args.shift();
|
|
2636
2636
|
}
|
|
2637
|
-
this.emit(`option:${option.name()}`,
|
|
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 =
|
|
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(
|
|
3065
|
-
if (
|
|
3066
|
-
this._executableDir =
|
|
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
|
|
3431
|
-
if (
|
|
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 (
|
|
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(
|
|
6299
|
-
this.#text =
|
|
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(
|
|
6306
|
-
this.#prefixText =
|
|
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(
|
|
6313
|
-
this.#suffixText =
|
|
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(
|
|
6351
|
-
if (typeof
|
|
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 =
|
|
6354
|
+
this.#isEnabled = value2;
|
|
6355
6355
|
}
|
|
6356
6356
|
get isSilent() {
|
|
6357
6357
|
return this.#isSilent;
|
|
6358
6358
|
}
|
|
6359
|
-
set isSilent(
|
|
6360
|
-
if (typeof
|
|
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 =
|
|
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 = (
|
|
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(
|
|
6626
|
-
define(
|
|
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(
|
|
7096
|
-
identity = { ...
|
|
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
|
|
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
|
|
7137
|
-
if (!
|
|
7138
|
-
const version = Number(
|
|
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,
|
|
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
|
|
7148
|
-
return
|
|
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:
|
|
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,
|
|
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}${
|
|
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,
|
|
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
|
|
7290
|
-
return
|
|
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
|
|
7298
|
-
for (const key of remove5) delete
|
|
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(
|
|
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((
|
|
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.
|
|
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
|
|
7775
|
-
return typeof
|
|
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(
|
|
8388
|
-
if (!
|
|
8389
|
-
const parts =
|
|
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(
|
|
8442
|
-
const normalized =
|
|
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
|
|
8448
|
-
if (!
|
|
8449
|
-
|
|
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 (!
|
|
8460
|
+
if (!value2 && typeof detail5.details?.reason === "string" && !detail5.api) {
|
|
8452
8461
|
const reason2 = detail5.details.reason.toLowerCase();
|
|
8453
|
-
|
|
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 (!
|
|
8465
|
-
|
|
8466
|
-
if (!
|
|
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)
|
|
8469
|
-
else if (detail5.api?.status === 401 || exitCode === 2)
|
|
8470
|
-
else if (detail5.api?.status === 403)
|
|
8471
|
-
else if (detail5.api?.status === 404)
|
|
8472
|
-
else if (detail5.api?.status === 429)
|
|
8473
|
-
else if (detail5.api?.status === 409 || exitCode === 5)
|
|
8474
|
-
else if (exitCode === 4)
|
|
8475
|
-
else if (exitCode === 3)
|
|
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(
|
|
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,
|
|
8567
|
-
const response = await request2(method,
|
|
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,
|
|
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
|
-
|
|
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,
|
|
8788
|
+
async function request(creds, method, path27, body5, requestTimeoutMs, options) {
|
|
8767
8789
|
const response = await sharedClient(creds).request(
|
|
8768
8790
|
method,
|
|
8769
|
-
|
|
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,
|
|
8778
|
-
const response = await sharedClient(creds).requestForm(method,
|
|
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
|
|
9322
|
-
return request(creds, method,
|
|
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,
|
|
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(
|
|
9604
|
-
const byId = rows.find((row) => row.id ===
|
|
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() ===
|
|
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} "${
|
|
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(
|
|
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} "${
|
|
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} "${
|
|
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(
|
|
10192
|
-
const record4 = (
|
|
10193
|
-
const qa3 = record4(
|
|
10194
|
-
const
|
|
10195
|
-
const summary = record4(
|
|
10196
|
-
const count2 = (
|
|
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
|
|
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(
|
|
10280
|
-
return Boolean(
|
|
10319
|
+
function isRecord(value2) {
|
|
10320
|
+
return Boolean(value2 && typeof value2 === "object" && !Array.isArray(value2));
|
|
10281
10321
|
}
|
|
10282
|
-
function arrayOf(
|
|
10283
|
-
if (Array.isArray(
|
|
10284
|
-
if (
|
|
10285
|
-
return [
|
|
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(
|
|
10364
|
-
return /^([A-Z0-9]{2,8})-(\d+)$/.test(
|
|
10403
|
+
function isExperimentTestKey(value2) {
|
|
10404
|
+
return /^([A-Z0-9]{2,8})-(\d+)$/.test(value2);
|
|
10365
10405
|
}
|
|
10366
|
-
function record(
|
|
10367
|
-
return
|
|
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((
|
|
10452
|
-
message: record(
|
|
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.
|
|
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(
|
|
10557
|
+
function dataUriFromMatch(value2, match2) {
|
|
10518
10558
|
let uri = match2[0];
|
|
10519
10559
|
const start = match2.index ?? 0;
|
|
10520
|
-
if (/url\(\s*$/i.test(
|
|
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(
|
|
10527
|
-
const before =
|
|
10528
|
-
const after =
|
|
10529
|
-
return `${before}${
|
|
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(
|
|
10535
|
-
let rewritten =
|
|
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 (
|
|
10567
|
-
if (options.enabled === false || !
|
|
10568
|
-
let rewritten =
|
|
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 (
|
|
10644
|
-
if (typeof
|
|
10645
|
-
if (Array.isArray(
|
|
10646
|
-
return Promise.all(
|
|
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 (
|
|
10688
|
+
if (value2 !== null && typeof value2 === "object") {
|
|
10649
10689
|
return Object.fromEntries(
|
|
10650
10690
|
await Promise.all(
|
|
10651
|
-
Object.entries(
|
|
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
|
|
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(
|
|
12866
|
-
return typeof
|
|
13013
|
+
function formatPct(value2, digits = 1) {
|
|
13014
|
+
return typeof value2 === "number" && Number.isFinite(value2) ? `${value2.toFixed(digits)}%` : "-";
|
|
12867
13015
|
}
|
|
12868
|
-
function confidenceBasisLabel(
|
|
12869
|
-
if (
|
|
12870
|
-
if (
|
|
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(
|
|
12874
|
-
return
|
|
13021
|
+
function statisticsStateLabel(value2) {
|
|
13022
|
+
return value2 === "untrustworthy" ? "verdict withheld" : String(value2 ?? "unknown");
|
|
12875
13023
|
}
|
|
12876
|
-
function formatRevenue(
|
|
12877
|
-
if (
|
|
12878
|
-
if (!currency || typeof
|
|
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(
|
|
13031
|
+
}).format(value2);
|
|
12884
13032
|
} catch {
|
|
12885
|
-
return String(
|
|
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 = (
|
|
12975
|
-
const money = (
|
|
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 = (
|
|
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(
|
|
13034
|
-
return import_node_crypto6.default.createHash("sha256").update(
|
|
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(
|
|
13303
|
-
const version =
|
|
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(
|
|
13467
|
+
function printQaRun(run4) {
|
|
13307
13468
|
console.log();
|
|
13308
|
-
console.log(source_default.bold(`QA run ${
|
|
13309
|
-
console.log(` computedAt: ${
|
|
13310
|
-
console.log(` Status: ${statusBadge(
|
|
13311
|
-
console.log(` Required: ${
|
|
13312
|
-
if (
|
|
13313
|
-
if (
|
|
13314
|
-
if (
|
|
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
|
-
|
|
13477
|
+
run4.summary.blockers.forEach((blocker) => console.log(` - ${blocker}`));
|
|
13317
13478
|
}
|
|
13318
13479
|
console.log(source_default.bold("\nChecklist"));
|
|
13319
|
-
for (const item of
|
|
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 (
|
|
13486
|
+
if (run4.urls.length > 0) {
|
|
13326
13487
|
console.log(source_default.bold("\nQA URLs"));
|
|
13327
|
-
|
|
13488
|
+
run4.urls.forEach((url) => {
|
|
13328
13489
|
console.log(` - ${url.label}: ${source_default.cyan(url.url)}`);
|
|
13329
13490
|
});
|
|
13330
13491
|
}
|
|
13331
|
-
if ((
|
|
13492
|
+
if ((run4.evidenceRecords ?? []).length > 0) {
|
|
13332
13493
|
console.log(source_default.bold("\nEvidence records"));
|
|
13333
|
-
for (const record4 of
|
|
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
|
|
13347
|
-
const rl =
|
|
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(
|
|
13355
|
-
if (!
|
|
13356
|
-
const source =
|
|
13357
|
-
const text2 =
|
|
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((
|
|
13526
|
+
return new Option(flags, description).choices([...choices]).argParser((value2) => value2);
|
|
13366
13527
|
}
|
|
13367
|
-
function validateQaChoice(command, option,
|
|
13368
|
-
if (
|
|
13369
|
-
fail(command, `Invalid ${option}: ${
|
|
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(
|
|
13536
|
+
function qaSummary(run4, changed) {
|
|
13376
13537
|
return {
|
|
13377
13538
|
run: {
|
|
13378
|
-
id:
|
|
13379
|
-
status:
|
|
13380
|
-
summary:
|
|
13381
|
-
approvedAt:
|
|
13382
|
-
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(
|
|
13396
|
-
const candidate = folderPath(
|
|
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(
|
|
13406
|
-
if (!exp) throw new Error(`Experiment "${
|
|
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
|
|
13529
|
-
return
|
|
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(
|
|
13773
|
-
return [...previous,
|
|
13957
|
+
function collectCreateOption(value2, previous) {
|
|
13958
|
+
return [...previous, value2];
|
|
13774
13959
|
}
|
|
13775
|
-
function parseHttpDestination(
|
|
13776
|
-
const trimmed =
|
|
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 "${
|
|
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,
|
|
13827
|
-
const invalid = (reason2) => fail(command, `Invalid --goal "new:${
|
|
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,
|
|
14060
|
+
function parseNewGoal(command, value2) {
|
|
13876
14061
|
const body5 = {};
|
|
13877
|
-
for (const field of
|
|
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:${
|
|
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,
|
|
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
|
|
13932
|
-
const rl =
|
|
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((
|
|
14030
|
-
if (sourceRules.some((
|
|
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
|
|
14334
|
-
const rl =
|
|
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,
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
15075
|
-
notes:
|
|
15076
|
-
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(
|
|
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
|
|
15123
|
-
if (
|
|
15124
|
-
const variation =
|
|
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(
|
|
15201
|
-
const source =
|
|
15202
|
-
const text2 =
|
|
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(
|
|
15206
|
-
return [...previous,
|
|
15391
|
+
function collectFile(value2, previous = []) {
|
|
15392
|
+
return [...previous, value2];
|
|
15207
15393
|
}
|
|
15208
|
-
function normalizeEvidenceDevice(
|
|
15209
|
-
if (!
|
|
15210
|
-
const normalized =
|
|
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
|
|
15483
|
+
const run4 = response.data.run;
|
|
15298
15484
|
const data = opts.summary ? qaSummary(
|
|
15299
|
-
|
|
15300
|
-
|
|
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(
|
|
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,
|
|
15523
|
+
Object.entries(data.kinds).map(([kind5, value2]) => [
|
|
15338
15524
|
kind5,
|
|
15339
|
-
|
|
15340
|
-
|
|
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((
|
|
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 = (
|
|
15409
|
-
var commaList = (
|
|
15410
|
-
function shellQuote(
|
|
15411
|
-
if (
|
|
15412
|
-
return `'${
|
|
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
|
|
16171
|
+
var import_node_fs10 = __toESM(require("node:fs"));
|
|
15800
16172
|
var import_node_os7 = __toESM(require("node:os"));
|
|
15801
|
-
var
|
|
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 =
|
|
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 =
|
|
15857
|
-
const outFile =
|
|
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
|
-
|
|
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(
|
|
15881
|
-
return !!
|
|
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 =
|
|
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:
|
|
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,
|
|
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
|
|
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 = `${
|
|
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
|
|
16195
|
-
if (
|
|
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
|
|
16262
|
-
var
|
|
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(
|
|
16265
|
-
const slug =
|
|
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 (!
|
|
16270
|
-
return JSON.parse(
|
|
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
|
|
16645
|
+
return import_node_path9.default.resolve(process.cwd(), folder);
|
|
16274
16646
|
}
|
|
16275
16647
|
function experimentConfigPath(folder) {
|
|
16276
|
-
return
|
|
16648
|
+
return import_node_path9.default.join(folder, "experiment.config.json");
|
|
16277
16649
|
}
|
|
16278
16650
|
function apexConfigPath(folder) {
|
|
16279
|
-
return
|
|
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 ??
|
|
16289
|
-
const variation =
|
|
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 =
|
|
16324
|
-
const cssPath =
|
|
16325
|
-
return { jsPath, cssPath:
|
|
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 =
|
|
16328
|
-
const files =
|
|
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:
|
|
16336
|
-
cssPath: css ?
|
|
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 (!
|
|
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([
|
|
16347
|
-
|
|
16718
|
+
new Blob([import_node_fs11.default.readFileSync(jsPath)], { type: "application/javascript" }),
|
|
16719
|
+
import_node_path9.default.basename(jsPath)
|
|
16348
16720
|
);
|
|
16349
|
-
if (cssPath &&
|
|
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([
|
|
16353
|
-
|
|
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:
|
|
16363
|
-
cssPath: cssPath ?
|
|
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 (
|
|
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
|
-
|
|
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 =
|
|
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
|
|
16519
|
-
var
|
|
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(
|
|
16534
|
-
if (!
|
|
16535
|
-
const issues =
|
|
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(
|
|
16568
|
-
return import_node_crypto7.default.createHash("sha256").update(
|
|
16939
|
+
function sha256Text2(value2) {
|
|
16940
|
+
return import_node_crypto7.default.createHash("sha256").update(value2).digest("hex");
|
|
16569
16941
|
}
|
|
16570
|
-
function byteLength(
|
|
16571
|
-
return Buffer.byteLength(
|
|
16942
|
+
function byteLength(value2) {
|
|
16943
|
+
return Buffer.byteLength(value2, "utf8");
|
|
16572
16944
|
}
|
|
16573
|
-
function slugify2(
|
|
16574
|
-
const slug =
|
|
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
|
|
16950
|
+
return import_node_path10.default.resolve(process.cwd(), folder);
|
|
16579
16951
|
}
|
|
16580
16952
|
function apexDir(folder) {
|
|
16581
|
-
return
|
|
16953
|
+
return import_node_path10.default.join(folder, ".apex");
|
|
16582
16954
|
}
|
|
16583
16955
|
function sessionPath(folder) {
|
|
16584
|
-
return
|
|
16956
|
+
return import_node_path10.default.join(apexDir(folder), "session.json");
|
|
16585
16957
|
}
|
|
16586
16958
|
function readJsonFile(filePath) {
|
|
16587
|
-
if (!
|
|
16588
|
-
return JSON.parse(
|
|
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,
|
|
16591
|
-
|
|
16592
|
-
|
|
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 =
|
|
16599
|
-
if (
|
|
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
|
-
|
|
16630
|
-
|
|
16631
|
-
|
|
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 (!
|
|
16656
|
-
for (const entry of
|
|
16657
|
-
const absPath =
|
|
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(
|
|
16663
|
-
const content =
|
|
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(
|
|
17045
|
+
for (const root of roots) visit2(import_node_path10.default.join(folder, root));
|
|
16674
17046
|
for (const file of topLevel) {
|
|
16675
|
-
const absPath =
|
|
16676
|
-
if (!
|
|
16677
|
-
const content =
|
|
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(
|
|
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 =
|
|
16719
|
-
|
|
16720
|
-
|
|
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
|
-
|
|
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" }),
|
|
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
|
-
|
|
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
|
-
|
|
16986
|
-
writeJsonFile(
|
|
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 =
|
|
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
|
|
17077
|
-
var
|
|
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(
|
|
17080
|
-
const text2 = typeof
|
|
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(
|
|
17085
|
-
if (typeof
|
|
17086
|
-
return Number.isInteger(
|
|
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 =
|
|
17188
|
-
|
|
17189
|
-
|
|
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
|
|
17204
|
-
var
|
|
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
|
|
17207
|
-
if (!
|
|
17578
|
+
const value2 = input.trim();
|
|
17579
|
+
if (!value2) throw new Error("URL cannot be empty");
|
|
17208
17580
|
try {
|
|
17209
|
-
return new URL(
|
|
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 (!
|
|
17252
|
-
const parsed = JSON.parse(
|
|
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
|
-
|
|
17258
|
-
|
|
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
|
|
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 = (
|
|
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
|
|
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 = (
|
|
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 ?
|
|
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((
|
|
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
|
-
|
|
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
|
|
17775
|
-
var
|
|
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
|
|
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,
|
|
17953
|
-
if (!
|
|
17954
|
-
const source =
|
|
17955
|
-
const text2 =
|
|
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(([,
|
|
18338
|
+
return Object.fromEntries(Object.entries(input).filter(([, value2]) => value2 !== void 0));
|
|
17965
18339
|
}
|
|
17966
|
-
async function resolveGoalPageId(command, creds,
|
|
17967
|
-
if (UUID_V1_TO_V8.test(
|
|
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 ===
|
|
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 ${
|
|
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.
|
|
18061
|
-
fail(
|
|
18062
|
-
|
|
18063
|
-
|
|
18064
|
-
|
|
18065
|
-
|
|
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,
|
|
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 =
|
|
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 =
|
|
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
|
|
18239
|
-
const rl =
|
|
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
|
|
18279
|
-
var
|
|
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(
|
|
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(
|
|
18414
|
-
const candidate =
|
|
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
|
-
(
|
|
18871
|
+
(value2) => normalizeEnvironmentDomain(value2) !== normalizedDomain
|
|
18474
18872
|
),
|
|
18475
18873
|
normalizedDomain
|
|
18476
18874
|
] : item.domains.filter(
|
|
18477
|
-
(
|
|
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
|
-
(
|
|
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
|
|
18598
|
-
var
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
19144
|
+
const backupPath = (0, import_node_path15.resolve)(options.backup);
|
|
18747
19145
|
try {
|
|
18748
|
-
(0,
|
|
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
|
|
18808
|
-
var
|
|
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(
|
|
18812
|
-
return
|
|
19209
|
+
function runtimePath(path27) {
|
|
19210
|
+
return path27.replaceAll("[]", ".0");
|
|
18813
19211
|
}
|
|
18814
|
-
function fieldsFrom(
|
|
18815
|
-
if (!
|
|
18816
|
-
if (Array.isArray(
|
|
18817
|
-
return
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
19288
|
+
function score2(path27, patterns) {
|
|
18891
19289
|
return patterns.reduce(
|
|
18892
|
-
(total, pattern, index) => total + (pattern.test(
|
|
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((
|
|
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(
|
|
19024
|
-
if (!
|
|
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 =
|
|
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(
|
|
19032
|
-
if (
|
|
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(
|
|
19036
|
-
if (
|
|
19037
|
-
if (typeof
|
|
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(
|
|
19043
|
-
if (
|
|
19044
|
-
const sources = Array.isArray(
|
|
19045
|
-
if (Array.isArray(
|
|
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(
|
|
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(
|
|
19451
|
+
function defaultMatchesValueType(value2, valueType) {
|
|
19054
19452
|
if (valueType === "number") {
|
|
19055
|
-
if (typeof
|
|
19056
|
-
const text2 = String(
|
|
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
|
|
19459
|
+
if (typeof value2 === "boolean") return true;
|
|
19062
19460
|
return ["1", "true", "yes", "on", "0", "false", "no", "off"].includes(
|
|
19063
|
-
String(
|
|
19461
|
+
String(value2).trim().toLowerCase()
|
|
19064
19462
|
);
|
|
19065
19463
|
}
|
|
19066
19464
|
return true;
|
|
19067
19465
|
}
|
|
19068
|
-
function strictTransform(
|
|
19069
|
-
if (
|
|
19466
|
+
function strictTransform(value2, valueType, location) {
|
|
19467
|
+
if (value2 === void 0) return;
|
|
19070
19468
|
const transform = strictObject(
|
|
19071
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
19124
|
-
if (
|
|
19125
|
-
if (!Array.isArray(
|
|
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
|
-
|
|
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(
|
|
19158
|
-
if (!Array.isArray(
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
19373
|
-
return [...previous,
|
|
19770
|
+
function collect2(value2, previous) {
|
|
19771
|
+
return [...previous, value2];
|
|
19374
19772
|
}
|
|
19375
|
-
function parseParamValue(
|
|
19376
|
-
if (!
|
|
19773
|
+
function parseParamValue(value2) {
|
|
19774
|
+
if (!value2.startsWith("[") && !value2.startsWith("{")) return value2;
|
|
19377
19775
|
try {
|
|
19378
|
-
return JSON.parse(
|
|
19776
|
+
return JSON.parse(value2);
|
|
19379
19777
|
} catch {
|
|
19380
|
-
return
|
|
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",
|
|
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((
|
|
19386
|
-
const i =
|
|
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 ${
|
|
19788
|
+
`TRIGGERS_CONFIG_ABSENT: invalid --param ${pair2}; expected k=v`,
|
|
19391
19789
|
EXIT_CODES.validation
|
|
19392
19790
|
);
|
|
19393
|
-
return [
|
|
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",
|
|
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(
|
|
19465
|
-
return
|
|
19862
|
+
function normalizeDomain(value2) {
|
|
19863
|
+
return value2.trim().replace(/^https?:\/\//i, "").replace(/[/?#].*$/, "").replace(/\.$/, "").toLowerCase();
|
|
19466
19864
|
}
|
|
19467
|
-
function landingHost(
|
|
19468
|
-
const trimmed =
|
|
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
|
|
19647
|
-
const rl =
|
|
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(
|
|
20132
|
+
function normalizeDomain2(value2) {
|
|
19735
20133
|
try {
|
|
19736
|
-
const candidate =
|
|
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(
|
|
19760
|
-
return
|
|
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(
|
|
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
|
-
|
|
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(
|
|
20227
|
-
if (!
|
|
20624
|
+
function hostOnly(value2) {
|
|
20625
|
+
if (!value2) return null;
|
|
20228
20626
|
try {
|
|
20229
|
-
return new URL(
|
|
20627
|
+
return new URL(value2.includes("://") ? value2 : `https://${value2}`).hostname.toLowerCase();
|
|
20230
20628
|
} catch {
|
|
20231
|
-
return
|
|
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
|
|
20634
|
+
for (const value2 of [
|
|
20237
20635
|
domains.domain,
|
|
20238
20636
|
...domains.additionalDomains ?? [],
|
|
20239
20637
|
...domains.storeOrigins ?? []
|
|
20240
20638
|
]) {
|
|
20241
|
-
const host = hostOnly(
|
|
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(
|
|
20343
|
-
return
|
|
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
|
|
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
|
|
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(
|
|
20386
|
-
const parsed = typeof
|
|
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(
|
|
20390
|
-
if (!Array.isArray(
|
|
20787
|
+
function lineItemSum(value2) {
|
|
20788
|
+
if (!Array.isArray(value2)) return null;
|
|
20391
20789
|
let sum = 0;
|
|
20392
|
-
for (const item of
|
|
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
|
|
20596
|
-
const host = hostOnly(
|
|
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(
|
|
20974
|
-
const text2 =
|
|
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(
|
|
20981
|
-
return new Date(
|
|
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(
|
|
21089
|
-
if (
|
|
21090
|
-
if (typeof
|
|
21091
|
-
return String(
|
|
21092
|
-
}
|
|
21093
|
-
function positiveInteger(command,
|
|
21094
|
-
if (
|
|
21095
|
-
const parsed = Number(
|
|
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:
|
|
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
|
|
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,
|
|
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
|
|
21338
|
-
return !
|
|
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,
|
|
21402
|
-
if (Array.isArray(
|
|
21403
|
-
else if (typeof
|
|
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(
|
|
21655
|
-
return typeof
|
|
22052
|
+
function readNumber(value2) {
|
|
22053
|
+
return typeof value2 === "number" && Number.isFinite(value2) ? value2 : null;
|
|
21656
22054
|
}
|
|
21657
|
-
function readString(
|
|
21658
|
-
return typeof
|
|
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,
|
|
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
|
|
21965
|
-
var
|
|
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(
|
|
21972
|
-
const record4 =
|
|
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,
|
|
22401
|
+
function body(command, value2, opts) {
|
|
22004
22402
|
let parsed = {};
|
|
22005
|
-
if (
|
|
22006
|
-
const source =
|
|
22007
|
-
const text2 =
|
|
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
|
|
22110
|
-
const b = body(this,
|
|
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
|
|
22141
|
-
var
|
|
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,
|
|
22146
|
-
if (!
|
|
22543
|
+
function parse(command, value2) {
|
|
22544
|
+
if (!value2) return {};
|
|
22147
22545
|
try {
|
|
22148
|
-
const source =
|
|
22149
|
-
const text2 =
|
|
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
|
|
22163
|
-
return [...previous,
|
|
22560
|
+
function collect3(value2, previous) {
|
|
22561
|
+
return [...previous, value2];
|
|
22164
22562
|
}
|
|
22165
|
-
function customFieldsRecord(
|
|
22166
|
-
return
|
|
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((
|
|
22189
|
-
const separator =
|
|
22586
|
+
const parsedPairs = pairs.map((pair2) => {
|
|
22587
|
+
const separator = pair2.indexOf("=");
|
|
22190
22588
|
if (separator <= 0)
|
|
22191
|
-
fail(command, `Invalid --field "${
|
|
22192
|
-
return { key:
|
|
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,
|
|
22217
|
-
console.log(` ${labels.get(key) ?? key}: ${
|
|
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
|
-
|
|
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
|
|
22316
|
-
const rl =
|
|
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,
|
|
22344
|
-
if (
|
|
22345
|
-
const trimmed =
|
|
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} "${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
22427
|
-
var
|
|
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,
|
|
22432
|
-
if (!
|
|
22829
|
+
function parse2(command, value2) {
|
|
22830
|
+
if (!value2) return {};
|
|
22433
22831
|
try {
|
|
22434
|
-
const source =
|
|
22435
|
-
const text2 =
|
|
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
|
|
22518
|
-
var
|
|
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,
|
|
22918
|
+
function body2(command, value2, opts) {
|
|
22521
22919
|
let parsed = {};
|
|
22522
|
-
if (
|
|
22523
|
-
const source =
|
|
22524
|
-
const text2 =
|
|
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
|
|
22584
|
-
const b = body2(this,
|
|
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
|
|
22607
|
-
var
|
|
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,
|
|
22861
|
-
if (!
|
|
22862
|
-
const source =
|
|
23258
|
+
function body3(command, value2) {
|
|
23259
|
+
if (!value2) return {};
|
|
23260
|
+
const source = value2.startsWith("@") ? value2.slice(1) : value2;
|
|
22863
23261
|
try {
|
|
22864
|
-
const text2 =
|
|
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
|
|
22944
|
-
const requestBody = body3(this,
|
|
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
|
|
22978
|
-
const rl =
|
|
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,
|
|
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,
|
|
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
|
|
23120
|
-
var
|
|
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(
|
|
23131
|
-
if (!
|
|
23132
|
-
if (!
|
|
23133
|
-
return
|
|
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 =
|
|
23188
|
-
const content_type = mediaTypes[
|
|
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 =
|
|
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 =
|
|
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
|
|
23337
|
-
var
|
|
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("@") ||
|
|
23343
|
-
const
|
|
23344
|
-
if (!
|
|
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
|
|
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
|
|
23849
|
+
const value2 = data;
|
|
23452
23850
|
console.log(
|
|
23453
|
-
`${
|
|
23851
|
+
`${value2.shop?.name ?? "Store profile"}${value2.shop?.domain ? ` (${value2.shop.domain})` : ""}`
|
|
23454
23852
|
);
|
|
23455
23853
|
console.log(
|
|
23456
|
-
`Audit: ${
|
|
23854
|
+
`Audit: ${value2.latestRun?.status ?? "not run"}; findings: ${value2.findings?.length ?? 0} (${value2.unreviewedCount ?? 0} unreviewed)`
|
|
23457
23855
|
);
|
|
23458
|
-
console.log(`Facts: ${
|
|
23459
|
-
console.log(`Learning run: ${
|
|
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(
|
|
23607
|
-
if (!
|
|
23608
|
-
if (Array.isArray(
|
|
23609
|
-
for (const item of
|
|
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 =
|
|
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((
|
|
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((
|
|
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((
|
|
24083
|
+
piiPaths: vtpPaths.filter((path27) => /email|phone|address|name/i.test(path27))
|
|
23686
24084
|
}
|
|
23687
24085
|
};
|
|
23688
24086
|
}
|
|
23689
|
-
function recordObject(
|
|
23690
|
-
return
|
|
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(
|
|
23736
|
-
if (
|
|
23737
|
-
if (!/^\d+$/.test(
|
|
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(
|
|
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(
|
|
23755
|
-
return
|
|
24152
|
+
function hasSyntheticPlaceholder(path27) {
|
|
24153
|
+
return path27.split(".").map((segment) => segment.replace(/\[\]$/, "")).some((segment) => /^<[^>]+>$/.test(segment));
|
|
23756
24154
|
}
|
|
23757
|
-
function ambiguousDottedPrefix(event,
|
|
23758
|
-
const segments =
|
|
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
|
|
23768
|
-
if (hasSyntheticPlaceholder(
|
|
24165
|
+
const path27 = field.path;
|
|
24166
|
+
if (hasSyntheticPlaceholder(path27)) {
|
|
23769
24167
|
return {
|
|
23770
24168
|
eligible: false,
|
|
23771
|
-
message: `Path ${JSON.stringify(
|
|
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(
|
|
24175
|
+
message: `Path ${JSON.stringify(path27)} is container-typed. Choose an observed scalar path.`
|
|
23778
24176
|
};
|
|
23779
24177
|
}
|
|
23780
|
-
if (
|
|
24178
|
+
if (path27.includes("[]")) {
|
|
23781
24179
|
return {
|
|
23782
24180
|
eligible: false,
|
|
23783
|
-
message: `Path ${JSON.stringify(
|
|
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(
|
|
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(
|
|
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,
|
|
24196
|
+
const ambiguousPrefix = ambiguousDottedPrefix(event, path27);
|
|
23799
24197
|
if (ambiguousPrefix) {
|
|
23800
24198
|
return {
|
|
23801
24199
|
eligible: false,
|
|
23802
|
-
message: `Path ${JSON.stringify(
|
|
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(
|
|
23812
|
-
const words =
|
|
23813
|
-
return words ? `${words[0].toUpperCase()}${words.slice(1)}` :
|
|
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(
|
|
23816
|
-
return "'" +
|
|
24213
|
+
function quoteShellArgument(value2) {
|
|
24214
|
+
return "'" + value2.replaceAll("'", "'\\''") + "'";
|
|
23817
24215
|
}
|
|
23818
|
-
function createDimensionInvocation(
|
|
23819
|
-
return `apex discover --create-dimension ${quoteShellArgument(
|
|
24216
|
+
function createDimensionInvocation(path27, eventName) {
|
|
24217
|
+
return `apex discover --create-dimension ${quoteShellArgument(path27)} --event ${quoteShellArgument(eventName)}`;
|
|
23820
24218
|
}
|
|
23821
|
-
function resolveDimensionObservation(command, report,
|
|
24219
|
+
function resolveDimensionObservation(command, report, path27, eventName) {
|
|
23822
24220
|
const matches = report.data_layer.flatMap(
|
|
23823
|
-
(event) => event.fields.filter((field) => field.path ===
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
24300
|
+
for (const path27 of event.paths)
|
|
23903
24301
|
lines.push(
|
|
23904
|
-
` URL ${
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
24385
|
+
if (apiFailure3?.cliError) throw apiFailure3.cliError;
|
|
23988
24386
|
fail(
|
|
23989
24387
|
this,
|
|
23990
|
-
|
|
23991
|
-
classifyStatus(
|
|
23992
|
-
|
|
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: ${
|
|
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
|
-
|
|
24046
|
-
classifyStatus(
|
|
24047
|
-
|
|
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
|
|
24118
|
-
var
|
|
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
|
-
|
|
24122
|
-
|
|
24123
|
-
|
|
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 (
|
|
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:
|
|
24136
|
-
skill:
|
|
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) => !
|
|
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 =
|
|
24145
|
-
|
|
24146
|
-
|
|
24147
|
-
|
|
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(
|
|
24183
|
-
if (
|
|
24184
|
-
if (
|
|
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
|
|
24254
|
-
const rl =
|
|
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
|
-
|
|
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,
|
|
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 = (
|
|
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 = (
|
|
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 = (
|
|
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
|
-
(
|
|
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,
|
|
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
|
|
24564
|
-
const rl =
|
|
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 (
|
|
24891
|
-
chunks.push(
|
|
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(
|
|
24897
|
-
totalBytes +=
|
|
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,
|
|
24928
|
-
return new URL(
|
|
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
|
|
25496
|
+
for (const path27 of ["/cart.js"]) {
|
|
24949
25497
|
try {
|
|
24950
|
-
const secondary = await fetchText(abs(primary.finalUrl || url,
|
|
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
|
-
(
|
|
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
|
-
(
|
|
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
|
|
25246
|
-
var
|
|
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 === "-" ?
|
|
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(
|
|
25261
|
-
return Array.isArray(
|
|
25808
|
+
function isGtagEventTuple(value2) {
|
|
25809
|
+
return Array.isArray(value2) && value2[0] === "event" && typeof value2[1] === "string";
|
|
25262
25810
|
}
|
|
25263
|
-
function isPlainObject(
|
|
25264
|
-
if (
|
|
25265
|
-
const prototype = Object.getPrototypeOf(
|
|
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(
|
|
25269
|
-
if (!Array.isArray(
|
|
25270
|
-
if (
|
|
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 (
|
|
25274
|
-
return
|
|
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}=${
|
|
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
|
|
25371
|
-
var
|
|
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
|
|
25376
|
-
var
|
|
25377
|
-
function
|
|
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
|
|
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 (!
|
|
25943
|
+
if (!import_node_fs29.default.existsSync(file)) return null;
|
|
25396
25944
|
try {
|
|
25397
|
-
return JSON.parse(
|
|
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
|
-
|
|
25416
|
-
|
|
25417
|
-
|
|
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 =
|
|
25422
|
-
const marker =
|
|
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 =
|
|
25435
|
-
if (
|
|
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
|
|
25458
|
-
const absolute =
|
|
25459
|
-
const relative =
|
|
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:
|
|
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 =
|
|
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 =
|
|
25492
|
-
if (!
|
|
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(
|
|
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
|
|
25550
|
-
const rl =
|
|
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 =
|
|
25557
|
-
return
|
|
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 =
|
|
25576
|
-
if (absolute.startsWith(folder +
|
|
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
|
-
|
|
25616
|
-
|
|
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
|
-
|
|
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 =
|
|
25631
|
-
if (!target.startsWith(folder +
|
|
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 }) =>
|
|
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
|
-
|
|
25669
|
-
|
|
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
|
-
|
|
25679
|
-
|
|
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 =
|
|
25685
|
-
|
|
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
|
|
25692
|
-
if (contextName.test(name) && !returned.has(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,
|
|
25695
|
-
|
|
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
|
-
|
|
25727
|
-
const pulled = opts.out ? await pullCheckout(this, creds, created.data.id,
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
25952
|
-
const fromManifest =
|
|
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
|
-
|
|
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
|
|
26013
|
-
const rl =
|
|
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(
|
|
26043
|
-
|
|
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
|
|
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:
|
|
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 =
|
|
26058
|
-
if (!
|
|
26059
|
-
const fileStat =
|
|
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[
|
|
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:
|
|
26072
|
-
bytes:
|
|
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(([,
|
|
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
|
|
26231
|
-
if (!
|
|
26797
|
+
const value2 = args[++index];
|
|
26798
|
+
if (!value2 || value2.startsWith("-")) return false;
|
|
26232
26799
|
} else if (arg.startsWith("--org=") || arg.startsWith("--shop=")) {
|
|
26233
|
-
const
|
|
26234
|
-
if (!
|
|
26800
|
+
const value2 = arg.slice(arg.indexOf("=") + 1);
|
|
26801
|
+
if (!value2 || value2.startsWith("-")) return false;
|
|
26235
26802
|
} else {
|
|
26236
26803
|
return false;
|
|
26237
26804
|
}
|