@koda-sl/baker-cli 0.354.0-dev.e6a354971 → 0.354.0-dev.faae9de67
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/README.md +4 -9
- package/dist/cli.js +93 -61
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -98,12 +98,7 @@ All commands return a JSON envelope:
|
|
|
98
98
|
### Unknown options are refused
|
|
99
99
|
|
|
100
100
|
An option a command does not declare stops the run with `UNKNOWN_ARGUMENT`, naming the
|
|
101
|
-
real one
|
|
102
|
-
description. A word the command spells differently is matched by meaning (`--from` and
|
|
103
|
-
`--since` → `--start-date`, `--to` and `--until` → `--end-date`, `--path` and `--url` →
|
|
104
|
-
`--landing`, each only where the command has it); otherwise only a near spelling is
|
|
105
|
-
offered, so a short option is never pointed at an unrelated one (`--from` at `--full`).
|
|
106
|
-
Previously such an option was accepted and ignored, so a call that
|
|
101
|
+
closest real one. Previously such an option was accepted and ignored, so a call that
|
|
107
102
|
looked like it had filtered or narrowed a result was in fact answering the unfiltered
|
|
108
103
|
question — and nothing in the output said so. Both spellings of an option work
|
|
109
104
|
(`--run-id` and `--runId`), as they always did. The error's `fix` names the exact call
|
|
@@ -1338,7 +1333,7 @@ Shared flags: `--days <n>` (default 30) or `--start-date` / `--end-date` (`YYYY-
|
|
|
1338
1333
|
|
|
1339
1334
|
`--measure` decides whose activity is measured and defaults to `campaigns` (only visitors a Baker landing brought). `--measure all` measures the client's entire website and is a decision somebody has to ask for. `host` is the client's own landing domain when one shares a domain with the site being measured, so the script and its events stay first-party; `firstParty` in the response says which, and removing that domain from Baker means re-running this command.
|
|
1340
1335
|
|
|
1341
|
-
**Paging the list reports.** `people`, `submissions` and `delivery` are lists of individual things rather than breakdowns — `delivery` and `submissions` are the ones the CLI exposes, `people` backs the People section of the dashboard's landing Analytics screen over the same wire — and only they take `--page <n>` (1-based) and `--page-size <n>` (up to 200, default 50). The response carries `pageInfo` with `hasMore` — when it is true there **are** more rows, so a total must never be reported from one page. Pass neither flag and a list comes back at the size it always did.
|
|
1336
|
+
**Paging the list reports.** `people`, `submissions` and `delivery` are lists of individual things rather than breakdowns — `delivery` and `submissions` are the ones the CLI exposes, `people` backs the People section of the dashboard's landing Analytics screen over the same wire — and only they take `--page <n>` (1-based) and `--page-size <n>` (up to 200, default 50). The response carries `pageInfo` with `hasMore` — when it is true there **are** more rows, so a total must never be reported from one page. Pass neither flag and a list comes back at the size it always did.
|
|
1342
1337
|
|
|
1343
1338
|
**A conversion is a company-level named event, not a property of a Form.** `baker analytics conversions` is the only place "what counts" is decided, and an empty `definitions` list means every conversion number in every other report is zero — not because nobody converted, but because nothing is named as an outcome and Baker never guesses which event is the point of a page. `--candidates` lists every event these pages actually produced — each Form step and trigger, every `data-baker-*` event, every outbound destination — with volume and whether it is already counted; that list is where an event key comes from, so never invent one. `--event <key> --name "Booked a call"` starts counting it **immediately and retroactively**: the whole stored history is rescored, so naming the right event today also fixes last month. Nothing here is staged and publishing is not involved. Two events given the same `--name` become one row and one number, which is how a call booked on three different Forms reads as one outcome. `--count-mode every_time` is for the outcomes people genuinely repeat (a guide downloaded twice is two downloads); the default counts once per visit.
|
|
1344
1339
|
|
|
@@ -1426,8 +1421,8 @@ baker experiment plan --landing oferta --variant oferta--1 --goal submit:book-a-
|
|
|
1426
1421
|
baker experiment start --landing oferta --variant oferta--1 --because "60% of visitors never scroll past the hero" --change "put the price in the headline"
|
|
1427
1422
|
baker experiment update --id temp_exp_1 --goal submit:book-a-call # a staged test can still change what it measures
|
|
1428
1423
|
baker experiment update --id a3f91c2b --because "…" # a live one can only change its wording
|
|
1429
|
-
baker experiment status # the verdict on every test
|
|
1430
|
-
baker experiment status --id a3f91c2b --full # one test, with the
|
|
1424
|
+
baker experiment status # the verdict on every test, first; dates as calendar dates
|
|
1425
|
+
baker experiment status --id a3f91c2b --full # one test, with everything the dashboard reads and the posteriors
|
|
1431
1426
|
baker experiment history --landing oferta # every finished test on a page: claim, numbers, decision, learning
|
|
1432
1427
|
baker experiment pause --id a3f91c2b # everyone sees the original for now
|
|
1433
1428
|
baker experiment resume --id a3f91c2b # the split picks up with the same sides
|
package/dist/cli.js
CHANGED
|
@@ -33401,19 +33401,6 @@ function pageOf(args5) {
|
|
|
33401
33401
|
...size ? { pageSize: Number(size) } : {}
|
|
33402
33402
|
};
|
|
33403
33403
|
}
|
|
33404
|
-
function isWholePositive(raw) {
|
|
33405
|
-
return /^[1-9]\d*$/.test(String(raw));
|
|
33406
|
-
}
|
|
33407
|
-
function paginationError(args5) {
|
|
33408
|
-
if (args5.page !== void 0 && !isWholePositive(args5.page)) {
|
|
33409
|
-
return `--page takes a page number of a list report (1, 2, \u2026), not "${String(args5.page)}". To narrow a report to one page of the site, pass --landing <internalId> (from \`baker analytics landings\`), or read one path with \`baker analytics page --path /pricing\`.`;
|
|
33410
|
-
}
|
|
33411
|
-
const size = args5["page-size"];
|
|
33412
|
-
if (size !== void 0 && !isWholePositive(size)) {
|
|
33413
|
-
return `--page-size takes a number of rows (1 to 200), not "${String(size)}".`;
|
|
33414
|
-
}
|
|
33415
|
-
return null;
|
|
33416
|
-
}
|
|
33417
33404
|
function requestBody(args5, options) {
|
|
33418
33405
|
const candidates = {
|
|
33419
33406
|
preset: options.preset,
|
|
@@ -33478,11 +33465,6 @@ async function runPreset(args5, options) {
|
|
|
33478
33465
|
});
|
|
33479
33466
|
process.exit(1);
|
|
33480
33467
|
}
|
|
33481
|
-
const pagination = paginationError(args5);
|
|
33482
|
-
if (pagination) {
|
|
33483
|
-
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message: pagination } });
|
|
33484
|
-
process.exit(1);
|
|
33485
|
-
}
|
|
33486
33468
|
try {
|
|
33487
33469
|
const response = await apiPost("/api/analytics/query", requestBody(args5, options));
|
|
33488
33470
|
const requestedPlatform = args5.platform === void 0 ? "" : String(args5.platform);
|
|
@@ -47133,6 +47115,80 @@ async function declarationsOf(root, files) {
|
|
|
47133
47115
|
return sources.flatMap(eventDeclarationsIn);
|
|
47134
47116
|
}
|
|
47135
47117
|
|
|
47118
|
+
// src/commands/experiment/readout.ts
|
|
47119
|
+
function isoMoment(ms) {
|
|
47120
|
+
return ms === null ? null : new Date(ms).toISOString();
|
|
47121
|
+
}
|
|
47122
|
+
function isoDay2(ms) {
|
|
47123
|
+
return ms === null ? null : new Date(ms).toISOString().slice(0, 10);
|
|
47124
|
+
}
|
|
47125
|
+
function compactArm(arm) {
|
|
47126
|
+
return { visitors: arm.visitors, conversions: arm.conversions, rate: Math.round(arm.rate * 1e4) / 1e4 };
|
|
47127
|
+
}
|
|
47128
|
+
function compactMetric(metric) {
|
|
47129
|
+
return {
|
|
47130
|
+
name: metric.name,
|
|
47131
|
+
reading: metric.reading,
|
|
47132
|
+
control: metric.control.conversions,
|
|
47133
|
+
variant: metric.variant.conversions
|
|
47134
|
+
};
|
|
47135
|
+
}
|
|
47136
|
+
function fullArm(arm) {
|
|
47137
|
+
return {
|
|
47138
|
+
...arm,
|
|
47139
|
+
...arm.snapshotCapturedAt === void 0 ? {} : { snapshotCapturedAt: isoMoment(arm.snapshotCapturedAt) }
|
|
47140
|
+
};
|
|
47141
|
+
}
|
|
47142
|
+
function statusReadout(row, { full, now }) {
|
|
47143
|
+
const decisionWhen = {
|
|
47144
|
+
earliestDecisionAt: isoDay2(row.earliestDecisionAt),
|
|
47145
|
+
...row.earliestDecisionAt === null ? {} : { earliestDecisionIn: describeWait(row.earliestDecisionAt, now) }
|
|
47146
|
+
};
|
|
47147
|
+
const moments = {
|
|
47148
|
+
startedAt: isoMoment(row.startedAt),
|
|
47149
|
+
pausedAt: isoMoment(row.pausedAt),
|
|
47150
|
+
finishedAt: isoMoment(row.finishedAt)
|
|
47151
|
+
};
|
|
47152
|
+
if (full) {
|
|
47153
|
+
return {
|
|
47154
|
+
...row,
|
|
47155
|
+
...moments,
|
|
47156
|
+
...decisionWhen,
|
|
47157
|
+
control: fullArm(row.control),
|
|
47158
|
+
variant: fullArm(row.variant)
|
|
47159
|
+
};
|
|
47160
|
+
}
|
|
47161
|
+
return {
|
|
47162
|
+
experimentId: row.experimentId,
|
|
47163
|
+
landingSlug: row.landingSlug,
|
|
47164
|
+
variantSlug: row.variantSlug,
|
|
47165
|
+
status: row.status,
|
|
47166
|
+
verdict: row.verdict,
|
|
47167
|
+
summary: row.summary,
|
|
47168
|
+
...row.winner === null ? {} : { winner: row.winner },
|
|
47169
|
+
...row.invalidReason === null ? {} : { invalidReason: row.invalidReason },
|
|
47170
|
+
stillNeeded: row.stillNeeded,
|
|
47171
|
+
...decisionWhen,
|
|
47172
|
+
willNotConclude: row.willNotConclude,
|
|
47173
|
+
control: compactArm(row.control),
|
|
47174
|
+
variant: compactArm(row.variant),
|
|
47175
|
+
secondary: row.secondary.map(compactMetric),
|
|
47176
|
+
hypothesisStatement: row.hypothesisStatement,
|
|
47177
|
+
minDetectableRelativeLift: row.minDetectableRelativeLift,
|
|
47178
|
+
goal: row.goal,
|
|
47179
|
+
...row.variantGoal === null ? {} : { variantGoal: row.variantGoal },
|
|
47180
|
+
goalLabel: row.goalLabel,
|
|
47181
|
+
...moments,
|
|
47182
|
+
...row.status === "finished" ? {
|
|
47183
|
+
survivor: row.survivor,
|
|
47184
|
+
hypothesisOutcome: row.hypothesisOutcome,
|
|
47185
|
+
fold: row.fold,
|
|
47186
|
+
learning: row.learning,
|
|
47187
|
+
endReason: row.endReason
|
|
47188
|
+
} : {}
|
|
47189
|
+
};
|
|
47190
|
+
}
|
|
47191
|
+
|
|
47136
47192
|
// src/commands/experiment/update.ts
|
|
47137
47193
|
function buildUpdateRequest(args5, rawArgs) {
|
|
47138
47194
|
const expect = args5.expect === void 0 ? void 0 : String(args5.expect);
|
|
@@ -47374,18 +47430,26 @@ registerSchema({
|
|
|
47374
47430
|
args: {
|
|
47375
47431
|
id: { type: "string", description: "One test, by its id", required: false },
|
|
47376
47432
|
landing: LANDING_FILTER_ARG,
|
|
47377
|
-
full: {
|
|
47433
|
+
full: {
|
|
47434
|
+
type: "boolean",
|
|
47435
|
+
description: "Every field the dashboard reads too \u2014 the hypothesis in parts, what each version owns, the preview pictures and the posteriors behind the verdict",
|
|
47436
|
+
required: false
|
|
47437
|
+
}
|
|
47378
47438
|
}
|
|
47379
47439
|
});
|
|
47380
47440
|
var statusCommand6 = defineCommand124({
|
|
47381
47441
|
meta: {
|
|
47382
47442
|
name: "status",
|
|
47383
|
-
description: "The verdict on each test. Read `verdict` and nothing else to decide: keep_running | winner | no_difference | cannot_win | invalid. `cannot_win` means even the best case is under the lift the test was looking for \u2014 end it and keep the original. `stillNeeded` is the data (visitors, conversions) the test still needs; a test is gated on data, never on days. `summary` is one sentence you can show the client, and `secondary` lists every other conversion the company counts on the same two arms \u2014 context, never a second decision. The numbers under `--full` are for printing, never for deciding \u2014 a test that says keep_running has not finished, however good its numbers look."
|
|
47443
|
+
description: "The verdict on each test. Read `verdict` and nothing else to decide: keep_running | winner | no_difference | cannot_win | invalid. `cannot_win` means even the best case is under the lift the test was looking for \u2014 end it and keep the original. `stillNeeded` is the data (visitors, conversions) the test still needs; a test is gated on data, never on days. `earliestDecisionAt` is the day this page's traffic would get it there (YYYY-MM-DD) and `earliestDecisionIn` how far off that is \u2014 say those, never work a date out yourself. `summary` is one sentence you can show the client, and `secondary` lists every other conversion the company counts on the same two arms \u2014 context, never a second decision. The numbers under `--full` are for printing, never for deciding \u2014 a test that says keep_running has not finished, however good its numbers look."
|
|
47384
47444
|
},
|
|
47385
47445
|
args: {
|
|
47386
47446
|
id: { type: "string", description: "One test, by its id", required: false },
|
|
47387
47447
|
landing: LANDING_FILTER_ARG,
|
|
47388
|
-
full: {
|
|
47448
|
+
full: {
|
|
47449
|
+
type: "boolean",
|
|
47450
|
+
description: "Every field the dashboard reads too \u2014 the hypothesis in parts, what each version owns, the preview pictures and the posteriors behind the verdict",
|
|
47451
|
+
required: false
|
|
47452
|
+
}
|
|
47389
47453
|
},
|
|
47390
47454
|
run: async ({ args: args5 }) => {
|
|
47391
47455
|
try {
|
|
@@ -47394,10 +47458,13 @@ var statusCommand6 = defineCommand124({
|
|
|
47394
47458
|
...args5.landing ? { landingSlug: String(args5.landing) } : {},
|
|
47395
47459
|
...args5.full === true ? { full: true } : {}
|
|
47396
47460
|
});
|
|
47461
|
+
const now = Date.now();
|
|
47397
47462
|
writeJsonEnvelope({
|
|
47398
47463
|
ok: true,
|
|
47399
|
-
data:
|
|
47400
|
-
|
|
47464
|
+
data: {
|
|
47465
|
+
experiments: response.data.experiments.map((row) => statusReadout(row, { full: args5.full === true, now }))
|
|
47466
|
+
},
|
|
47467
|
+
hints: buildStatusHints(response.data.experiments, now)
|
|
47401
47468
|
});
|
|
47402
47469
|
} catch (error) {
|
|
47403
47470
|
reportApiError(error);
|
|
@@ -68628,36 +68695,19 @@ function findUnknownFlags(root, argv) {
|
|
|
68628
68695
|
}
|
|
68629
68696
|
return unknown;
|
|
68630
68697
|
}
|
|
68631
|
-
var SYNONYMS = {
|
|
68632
|
-
from: ["start-date", "start", "since"],
|
|
68633
|
-
since: ["start-date", "start", "from"],
|
|
68634
|
-
start: ["start-date", "since", "from"],
|
|
68635
|
-
to: ["end-date", "end", "until"],
|
|
68636
|
-
until: ["end-date", "end", "to"],
|
|
68637
|
-
end: ["end-date", "until", "to"],
|
|
68638
|
-
path: ["landing"],
|
|
68639
|
-
url: ["landing"]
|
|
68640
|
-
};
|
|
68641
68698
|
function closestFlag(unknown, command) {
|
|
68642
68699
|
const accepted = acceptedNames(command);
|
|
68643
68700
|
if (!accepted) {
|
|
68644
68701
|
return null;
|
|
68645
68702
|
}
|
|
68646
68703
|
const name = unknown.replace(/^--/, "").split("=")[0] ?? "";
|
|
68647
|
-
const synonym = SYNONYMS[name]?.find((candidate) => accepted.has(candidate));
|
|
68648
|
-
if (synonym) {
|
|
68649
|
-
return `--${synonym}`;
|
|
68650
|
-
}
|
|
68651
68704
|
const scored = [...accepted].filter((candidate) => !ALWAYS_ACCEPTED.has(candidate)).map((candidate) => ({
|
|
68652
68705
|
candidate,
|
|
68653
68706
|
prefix: sharedPrefix(name, candidate),
|
|
68654
68707
|
distance: editDistance(name, candidate)
|
|
68655
|
-
})).filter(({ prefix, distance }) => prefix >= 4 || distance <=
|
|
68708
|
+
})).filter(({ prefix, distance }) => prefix >= 4 || distance <= 3).sort((a, b) => b.prefix - a.prefix || a.distance - b.distance);
|
|
68656
68709
|
return scored[0] ? `--${scored[0].candidate}` : null;
|
|
68657
68710
|
}
|
|
68658
|
-
function typoBudget(name) {
|
|
68659
|
-
return Math.max(1, Math.floor(name.length / 3));
|
|
68660
|
-
}
|
|
68661
68711
|
function sharedPrefix(a, b) {
|
|
68662
68712
|
let i = 0;
|
|
68663
68713
|
while (i < a.length && i < b.length && a[i] === b[i]) {
|
|
@@ -68681,24 +68731,6 @@ function commandFor(root, argv) {
|
|
|
68681
68731
|
const { command, resolved } = resolveCommand2(root, argv);
|
|
68682
68732
|
return resolved ? command : null;
|
|
68683
68733
|
}
|
|
68684
|
-
function suggestFlag(unknown, command) {
|
|
68685
|
-
const flag = closestFlag(unknown, command);
|
|
68686
|
-
if (!flag) {
|
|
68687
|
-
return null;
|
|
68688
|
-
}
|
|
68689
|
-
const args5 = argsOf(command) ?? {};
|
|
68690
|
-
const name = flag.slice(2);
|
|
68691
|
-
const def = args5[name] ?? args5[camel(name)];
|
|
68692
|
-
const described = def && typeof def === "object" ? def.description : void 0;
|
|
68693
|
-
const description = typeof described === "string" ? described.split(/\.\s/)[0]?.trim().replace(/\.$/, "") : "";
|
|
68694
|
-
return description ? { flag, description } : { flag };
|
|
68695
|
-
}
|
|
68696
|
-
function suggestionText(suggestion) {
|
|
68697
|
-
if (!suggestion) {
|
|
68698
|
-
return "";
|
|
68699
|
-
}
|
|
68700
|
-
return suggestion.description ? `Did you mean ${suggestion.flag}? It is described as: ${suggestion.description}. ` : `Did you mean ${suggestion.flag}? `;
|
|
68701
|
-
}
|
|
68702
68734
|
var OUTPUT_EXPLANATION = "This command has no --output option: `--output json` is accepted by every command that prints the JSON envelope, and md or files only where `baker schema` lists `output`. Drop the option and read the default output. ";
|
|
68703
68735
|
function unknownFlagEnvelope(unknown, commandPath, suggestion, hasSchema) {
|
|
68704
68736
|
const list = unknown.join(", ");
|
|
@@ -68709,7 +68741,7 @@ function unknownFlagEnvelope(unknown, commandPath, suggestion, hasSchema) {
|
|
|
68709
68741
|
message: `${list} ${unknown.length > 1 ? "are not options" : "is not an option"} of \`baker ${commandPath}\`.`,
|
|
68710
68742
|
fix: {
|
|
68711
68743
|
action: hasSchema ? "check_schema" : "check_help",
|
|
68712
|
-
explanation: (unknown.some((flag) => flag === "--output" || flag.startsWith("--output=")) ? OUTPUT_EXPLANATION : "") +
|
|
68744
|
+
explanation: (unknown.some((flag) => flag === "--output" || flag.startsWith("--output=")) ? OUTPUT_EXPLANATION : "") + (suggestion ? `Did you mean ${suggestion}? ` : "") + `Run \`${hasSchema ? `baker schema ${commandPath}` : `baker ${commandPath} --help`}\` to see every option this command takes. Do not re-run the same call: until this version an unrecognised option was accepted and ignored, so a command that looked like it had filtered or narrowed the result was in fact answering the unfiltered question.`
|
|
68713
68745
|
},
|
|
68714
68746
|
retryable: false
|
|
68715
68747
|
}
|
|
@@ -68739,7 +68771,7 @@ function refuseUnknownFlags(root, argv) {
|
|
|
68739
68771
|
}
|
|
68740
68772
|
const command = commandFor(root, argv);
|
|
68741
68773
|
const first = unknown[0];
|
|
68742
|
-
const suggestion = command && first ?
|
|
68774
|
+
const suggestion = command && first ? closestFlag(first, command) : null;
|
|
68743
68775
|
const path48 = commandPathOf(root, argv);
|
|
68744
68776
|
process.stdout.write(
|
|
68745
68777
|
`${JSON.stringify(unknownFlagEnvelope(unknown, path48, suggestion, findSchema(path48.split(" ")) !== void 0), null, 2)}
|