@drip-apex/apex-cli 0.17.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 +23 -2
- package/dist/cli.js +216 -24
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -71,6 +71,8 @@ the first writes: `apex experiments create`, `apex goals create`,
|
|
|
71
71
|
- Stale workspace or filesystem: pull fresh files, reapply your edits, and plan again.
|
|
72
72
|
- Server limit errors: read the JSON error details; Apex includes server-owned limits when available.
|
|
73
73
|
|
|
74
|
+
- Windows text corruption: use UTF-8 console settings; Apex repairs reversible Windows-1252 arguments and warns on stderr for U+FFFD. Set `APEX_ARGV_ENCODING=off` to disable repair.
|
|
75
|
+
|
|
74
76
|
## Platform Shop Administration
|
|
75
77
|
|
|
76
78
|
- `apex admin shops archive <shopId>` archives a shop; headless callers must
|
package/README.md
CHANGED
|
@@ -199,7 +199,7 @@ including every subcommand, argument, option, and alias.
|
|
|
199
199
|
- `apex roadmap` — consume approved roadmap ideas and handoffs.
|
|
200
200
|
- `apex harness` — develop AB Test Harness projects with live QA sessions.
|
|
201
201
|
- `apex workspace` — check out, preview, and publish draft workspace files.
|
|
202
|
-
- `apex goals` — manage conversion and revenue goals.
|
|
202
|
+
- `apex goals` — manage conversion and revenue goals. Create/update accept `--page-id <id>` (UUID only, no lookup) or `--page <id-or-key>` (keys need `pages:read` for lookup); use one option.
|
|
203
203
|
- `apex flags` — manage feature flags.
|
|
204
204
|
- `apex personalizations` — manage personalization deliveries.
|
|
205
205
|
- `apex backlog` — manage experiment backlog items.
|
|
@@ -222,7 +222,7 @@ including every subcommand, argument, option, and alias.
|
|
|
222
222
|
### Shop configuration and operations
|
|
223
223
|
|
|
224
224
|
- `apex external-pixels` — manage external pixel snippets.
|
|
225
|
-
- `apex assets` — manage uploaded assets.
|
|
225
|
+
- `apex assets` — manage uploaded assets. Upload returns `delivery_url`; embed that permanent URL in variation code. `public_url` expires after 300 seconds. Delivery is anonymous once the referencing experiment or snippet is published, and resolves through the owning shop's dashboard session or a QA preview before that. Only the absolute https form is recognized as an owned reference; protocol-relative or http forms are not granted at publication.
|
|
226
226
|
- `apex landing-pages` — build landing pages with your own tools, then apply, preview, deploy and roll them back.
|
|
227
227
|
- `apex brand` — manage brand context.
|
|
228
228
|
- `apex shops` — manage shops and active-shop context.
|
|
@@ -526,6 +526,27 @@ latest/minimum policy is cached for seven days, so offline commands still enforc
|
|
|
526
526
|
known floor. `APEX_NO_UPDATE_CHECK=1` disables the npm lookup and optional update
|
|
527
527
|
notice, but never hides a required server-floor upgrade.
|
|
528
528
|
|
|
529
|
+
### Windows: UTF-8 arguments
|
|
530
|
+
|
|
531
|
+
A Windows console or launcher can decode UTF-8 as Windows-1252, turning `A – B · C`
|
|
532
|
+
into `A – B · C`. On Windows, Apex automatically repairs reversible argument
|
|
533
|
+
values at startup and writes a note to stderr. Set `APEX_ARGV_ENCODING=off` to
|
|
534
|
+
keep arguments unchanged, or `APEX_ARGV_ENCODING=repair` to enable repair on
|
|
535
|
+
another platform.
|
|
536
|
+
|
|
537
|
+
Use UTF-8 in PowerShell:
|
|
538
|
+
|
|
539
|
+
```powershell
|
|
540
|
+
[Console]::InputEncoding = [Console]::OutputEncoding = [Text.Encoding]::UTF8
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
For Git Bash, set `LANG=C.UTF-8` and run `chcp 65001`.
|
|
544
|
+
|
|
545
|
+
A replacement character (`�`, U+FFFD) means the shell already lost a character.
|
|
546
|
+
Apex sends that value as given and warns on stderr. It also warns when stored
|
|
547
|
+
experiment names, descriptions, variation names, or goal names contain U+FFFD.
|
|
548
|
+
These notes leave JSON stdout unchanged.
|
|
549
|
+
|
|
529
550
|
## Usage telemetry
|
|
530
551
|
|
|
531
552
|
Authenticated invocations send a process invocation ID, full command path, explicitly set
|
package/dist/cli.js
CHANGED
|
@@ -7423,7 +7423,7 @@ async function checkForUpdate(current) {
|
|
|
7423
7423
|
// package.json
|
|
7424
7424
|
var package_default = {
|
|
7425
7425
|
name: "@drip-apex/apex-cli",
|
|
7426
|
-
version: "0.
|
|
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,
|
|
@@ -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
|
|
|
@@ -9621,6 +9630,13 @@ function moveAdminCustomerStores(creds, orgId, body5) {
|
|
|
9621
9630
|
body5
|
|
9622
9631
|
);
|
|
9623
9632
|
}
|
|
9633
|
+
function getExperimentResultsByChannel(creds, id) {
|
|
9634
|
+
return request(
|
|
9635
|
+
creds,
|
|
9636
|
+
"GET",
|
|
9637
|
+
`/api/v1/experiments/${encodeURIComponent(id)}/results/by-channel`
|
|
9638
|
+
);
|
|
9639
|
+
}
|
|
9624
9640
|
|
|
9625
9641
|
// src/context.ts
|
|
9626
9642
|
var import_node_readline = __toESM(require("node:readline"));
|
|
@@ -10526,7 +10542,7 @@ function contentHash(bytes) {
|
|
|
10526
10542
|
return import_node_crypto5.default.createHash("sha256").update(bytes).digest("hex");
|
|
10527
10543
|
}
|
|
10528
10544
|
function deliveryUrl(baseUrl, asset) {
|
|
10529
|
-
const returnedUrl = asset.
|
|
10545
|
+
const returnedUrl = asset.delivery_url ?? asset.deliveryUrl ?? asset.url;
|
|
10530
10546
|
if (returnedUrl) return returnedUrl;
|
|
10531
10547
|
const origin = new URL(baseUrl).origin;
|
|
10532
10548
|
return `${origin}/api/assets/${encodeURIComponent(asset.id)}/file`;
|
|
@@ -10684,6 +10700,114 @@ function createAssetReferenceRewriter(options) {
|
|
|
10684
10700
|
return { rewrite, rewriteUnknown };
|
|
10685
10701
|
}
|
|
10686
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
|
+
|
|
10687
10811
|
// src/generated/experiment-filesystem.schema.json
|
|
10688
10812
|
var experiment_filesystem_schema_default = {
|
|
10689
10813
|
$id: "https://api.drip-apex.com/schemas/experiment-filesystem-v1.json",
|
|
@@ -13601,6 +13725,7 @@ var get = new Command("get").description(
|
|
|
13601
13725
|
exp.shop_id ?? creds.resolvedContext?.shopId,
|
|
13602
13726
|
exp.id
|
|
13603
13727
|
);
|
|
13728
|
+
warnReplacementCharacters("experiment", exp, ["name", "description", "variations[].name"]);
|
|
13604
13729
|
output(this, { ...exp, dashboard_url: dashboardUrl }, () => printExperiment(exp, dashboardUrl));
|
|
13605
13730
|
});
|
|
13606
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) {
|
|
@@ -13636,6 +13761,22 @@ var detachIdea = new Command("detach-idea").description("Detach an experiment fr
|
|
|
13636
13761
|
);
|
|
13637
13762
|
output(this, res.data, () => printSuccess("Idea detached."));
|
|
13638
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
|
+
}
|
|
13639
13780
|
function printResultsExplain(body5, samples) {
|
|
13640
13781
|
const data = body5.data;
|
|
13641
13782
|
console.log("Diagnostic results explanation");
|
|
@@ -13680,7 +13821,13 @@ ${JSON.stringify(data.order, null, 2)}`);
|
|
|
13680
13821
|
console.log(`
|
|
13681
13822
|
Definitions: ${data.definitions_url}`);
|
|
13682
13823
|
}
|
|
13683
|
-
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
|
+
);
|
|
13684
13831
|
if (opts.explain && (opts.segment !== void 0 || opts.excludeSegment !== void 0))
|
|
13685
13832
|
fail(
|
|
13686
13833
|
this,
|
|
@@ -13702,12 +13849,12 @@ var results = new Command("results").description("Read experiment results and ti
|
|
|
13702
13849
|
fail(this, "--wait must be a non-negative number of seconds", EXIT_CODES.validation);
|
|
13703
13850
|
}
|
|
13704
13851
|
const startedWaitingAt = Date.now();
|
|
13705
|
-
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, {
|
|
13706
13853
|
currency: opts.currency,
|
|
13707
13854
|
segment: opts.segment,
|
|
13708
13855
|
exclude_segment: opts.excludeSegment
|
|
13709
13856
|
});
|
|
13710
|
-
while (!opts.explain && isResultsComputing(res)) {
|
|
13857
|
+
while (!opts.byChannel && !opts.explain && isResultsComputing(res)) {
|
|
13711
13858
|
const elapsedSeconds = (Date.now() - startedWaitingAt) / 1e3;
|
|
13712
13859
|
const remainingSeconds = waitSeconds - elapsedSeconds;
|
|
13713
13860
|
if (remainingSeconds <= 0) break;
|
|
@@ -13737,7 +13884,7 @@ var results = new Command("results").description("Read experiment results and ti
|
|
|
13737
13884
|
output(
|
|
13738
13885
|
this,
|
|
13739
13886
|
res.data,
|
|
13740
|
-
() => 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)
|
|
13741
13888
|
);
|
|
13742
13889
|
} catch (error) {
|
|
13743
13890
|
spinner?.fail("Failed to fetch experiment results");
|
|
@@ -13796,6 +13943,7 @@ var update = new Command("update").description("Update an experiment").addOption
|
|
|
13796
13943
|
...opts.countingType !== void 0 ? { counting_type: opts.countingType } : {}
|
|
13797
13944
|
});
|
|
13798
13945
|
if (!res.ok) fail(this, res.error ?? "Failed to update experiment", classifyStatus(res.status));
|
|
13946
|
+
warnReplacementCharacters("experiment", res.data, ["name", "description", "variations[].name"]);
|
|
13799
13947
|
const stored = res.data?.results_weekdays ?? null;
|
|
13800
13948
|
output(this, res.data, () => {
|
|
13801
13949
|
if (opts.countingType !== void 0)
|
|
@@ -14278,6 +14426,7 @@ var create = new Command("create").description(
|
|
|
14278
14426
|
previewNote = `No preview link: the experiment has no ${variation ? "store URL" : "treatment variation"} yet. Add one, then run \`apex preview ${res.data.id}\`.`;
|
|
14279
14427
|
}
|
|
14280
14428
|
}
|
|
14429
|
+
warnReplacementCharacters("experiment", res.data, ["name", "description", "variations[].name"]);
|
|
14281
14430
|
output(
|
|
14282
14431
|
this,
|
|
14283
14432
|
{
|
|
@@ -18042,8 +18191,10 @@ var GOALS_PAGE_SIZE = 100;
|
|
|
18042
18191
|
var MAX_GOAL_PAGES = 100;
|
|
18043
18192
|
var MAX_PAGE_LOOKUP_PAGES = 1e3;
|
|
18044
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;
|
|
18045
|
-
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)";
|
|
18046
|
-
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)";
|
|
18047
18198
|
var NON_QUANTIFIABLE = /* @__PURE__ */ new Set(["(", "|", "^", "$"]);
|
|
18048
18199
|
function urlPatternDependence(pattern) {
|
|
18049
18200
|
let fragment = false;
|
|
@@ -18238,6 +18389,24 @@ async function resolveGoalPageId(command, creds, value2) {
|
|
|
18238
18389
|
{ code: "page_not_found", next_steps: PAGE_NEXT_STEPS }
|
|
18239
18390
|
);
|
|
18240
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
|
+
}
|
|
18241
18410
|
function printGoal(goal) {
|
|
18242
18411
|
console.log();
|
|
18243
18412
|
console.log(source_default.bold(goal.name));
|
|
@@ -18260,6 +18429,7 @@ var list2 = new Command("list").alias("ls").description("List goals").option("-n
|
|
|
18260
18429
|
if (!res.ok || !res.data) {
|
|
18261
18430
|
fail(this, res.error ?? "Failed to list goals", classifyStatus(res.status), res.errorDetails);
|
|
18262
18431
|
}
|
|
18432
|
+
warnReplacementCharacters("goals", res.data, ["data[].name"]);
|
|
18263
18433
|
output(this, res.data, () => {
|
|
18264
18434
|
if (res.data.data.length === 0) {
|
|
18265
18435
|
console.log(source_default.dim("No goals found."));
|
|
@@ -18278,22 +18448,25 @@ var list2 = new Command("list").alias("ls").description("List goals").option("-n
|
|
|
18278
18448
|
);
|
|
18279
18449
|
});
|
|
18280
18450
|
});
|
|
18281
|
-
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) {
|
|
18282
18452
|
const creds = requireCredentials();
|
|
18283
|
-
if (opts.
|
|
18284
|
-
fail(
|
|
18285
|
-
|
|
18286
|
-
|
|
18287
|
-
|
|
18288
|
-
|
|
18289
|
-
|
|
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
|
+
});
|
|
18290
18463
|
}
|
|
18291
|
-
if ((opts.type === "custom" || opts.type === "revenue") && opts.page) {
|
|
18292
|
-
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, {
|
|
18293
18466
|
code: "page_not_supported"
|
|
18294
18467
|
});
|
|
18295
18468
|
}
|
|
18296
|
-
const pageId =
|
|
18469
|
+
const pageId = await goalPageOption(this, creds, opts);
|
|
18297
18470
|
const body5 = cleanBody({
|
|
18298
18471
|
name: opts.name,
|
|
18299
18472
|
type: opts.type,
|
|
@@ -18317,14 +18490,15 @@ var create2 = new Command("create").description("Create a goal").requiredOption(
|
|
|
18317
18490
|
res.errorDetails
|
|
18318
18491
|
);
|
|
18319
18492
|
}
|
|
18493
|
+
warnReplacementCharacters("goal", res.data, ["name"]);
|
|
18320
18494
|
output(this, res.data, () => printGoal(res.data));
|
|
18321
18495
|
});
|
|
18322
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(
|
|
18323
18497
|
"--value-property <property>",
|
|
18324
18498
|
"Numeric event property for value goals; pass null to clear"
|
|
18325
|
-
).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) {
|
|
18326
18500
|
const creds = requireCredentials();
|
|
18327
|
-
const pageId =
|
|
18501
|
+
const pageId = await goalPageOption(this, creds, opts, true);
|
|
18328
18502
|
const body5 = cleanBody({
|
|
18329
18503
|
name: opts.name,
|
|
18330
18504
|
type: opts.type,
|
|
@@ -18351,6 +18525,7 @@ var update2 = new Command("update").description("Update a goal").argument("<id>"
|
|
|
18351
18525
|
res.errorDetails
|
|
18352
18526
|
);
|
|
18353
18527
|
}
|
|
18528
|
+
warnReplacementCharacters("goal", res.data, ["name"]);
|
|
18354
18529
|
output(this, res.data, () => printGoal(res.data));
|
|
18355
18530
|
});
|
|
18356
18531
|
var test = new Command("test").description("Dry run a goal URL pattern against traffic").option(
|
|
@@ -26411,9 +26586,11 @@ function readFamily(family, label) {
|
|
|
26411
26586
|
this,
|
|
26412
26587
|
r.data,
|
|
26413
26588
|
() => printTable(
|
|
26414
|
-
["ID", "NAME", "STATUS"],
|
|
26415
|
-
r.data.data.map(
|
|
26416
|
-
|
|
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]
|
|
26417
26594
|
)
|
|
26418
26595
|
);
|
|
26419
26596
|
});
|
|
@@ -26484,6 +26661,9 @@ var snippet = new Command("snippet").description("Generate an external purchase
|
|
|
26484
26661
|
var externalPixelsCommand = new Command("external-pixels").description("Manage external pixels").addCommand(snippet);
|
|
26485
26662
|
|
|
26486
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);
|
|
26487
26667
|
var mockApiEnvironmentKey = ["APEX", "CLI", "MOCK", "API"].join("_");
|
|
26488
26668
|
if (process.env[mockApiEnvironmentKey] === "1" && !cliTestHarnessActive()) {
|
|
26489
26669
|
const message = "mock harness is not part of the published CLI; use the test artifact";
|
|
@@ -26586,6 +26766,18 @@ program2.hook("preAction", (_thisCommand, actionCommand) => {
|
|
|
26586
26766
|
throw error;
|
|
26587
26767
|
}
|
|
26588
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
|
+
}
|
|
26589
26781
|
beginInvocation(actionCommand, resolveContext());
|
|
26590
26782
|
});
|
|
26591
26783
|
process.on("beforeExit", () => {
|