@koda-sl/baker-cli 0.344.0 → 0.345.0-dev.9f4314fa9
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 +1 -1
- package/dist/{chunk-FFOURYP5.js → chunk-D5P4COPD.js} +4 -4
- package/dist/chunk-D5P4COPD.js.map +1 -0
- package/dist/cli.js +297 -236
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-FFOURYP5.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
validateCanvasDeep,
|
|
68
68
|
withoutCastName,
|
|
69
69
|
ytDlpBlockSignal
|
|
70
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-D5P4COPD.js";
|
|
71
71
|
import {
|
|
72
72
|
csvOrJson,
|
|
73
73
|
daysAgoIso,
|
|
@@ -31032,6 +31032,278 @@ function mergeStoredWithCounts(stored, reported) {
|
|
|
31032
31032
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
31033
31033
|
}
|
|
31034
31034
|
|
|
31035
|
+
// src/commands/analytics/render.ts
|
|
31036
|
+
var SPARKLINE_POINTS = 30;
|
|
31037
|
+
var DASHBOARD_ONLY_FIELDS = /* @__PURE__ */ new Set([
|
|
31038
|
+
// The Convex row id. It exists so a dashboard can link to the row; an agent
|
|
31039
|
+
// addresses a landing by `landingId`, which is the id in its `_definition.md`.
|
|
31040
|
+
"landingRef"
|
|
31041
|
+
]);
|
|
31042
|
+
function isAbsent(value) {
|
|
31043
|
+
if (value === "") return true;
|
|
31044
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
31045
|
+
if (typeof value === "object" && value !== null) return Object.keys(value).length === 0;
|
|
31046
|
+
return false;
|
|
31047
|
+
}
|
|
31048
|
+
function resample(series, points) {
|
|
31049
|
+
if (series.length <= points || points < 1) return [...series];
|
|
31050
|
+
const out = [];
|
|
31051
|
+
for (let i = 0; i < points; i += 1) {
|
|
31052
|
+
const from = Math.floor(i * series.length / points);
|
|
31053
|
+
const to = Math.floor((i + 1) * series.length / points);
|
|
31054
|
+
let sum = 0;
|
|
31055
|
+
for (let j = from; j < to; j += 1) sum += series[j] ?? 0;
|
|
31056
|
+
out.push(sum);
|
|
31057
|
+
}
|
|
31058
|
+
return out;
|
|
31059
|
+
}
|
|
31060
|
+
function pruneValue(value, full) {
|
|
31061
|
+
if (Array.isArray(value)) return value.map((entry) => pruneValue(entry, full));
|
|
31062
|
+
if (typeof value === "object" && value !== null) {
|
|
31063
|
+
return pruneRow(value, full);
|
|
31064
|
+
}
|
|
31065
|
+
return value;
|
|
31066
|
+
}
|
|
31067
|
+
function pruneRow(row, full) {
|
|
31068
|
+
const out = {};
|
|
31069
|
+
for (const [key, value] of Object.entries(row)) {
|
|
31070
|
+
if (DASHBOARD_ONLY_FIELDS.has(key)) continue;
|
|
31071
|
+
if (key === "sparkline" && Array.isArray(value)) {
|
|
31072
|
+
if (!full) continue;
|
|
31073
|
+
out[key] = resample(value, SPARKLINE_POINTS);
|
|
31074
|
+
continue;
|
|
31075
|
+
}
|
|
31076
|
+
if (isAbsent(value)) continue;
|
|
31077
|
+
out[key] = pruneValue(value, full);
|
|
31078
|
+
}
|
|
31079
|
+
return out;
|
|
31080
|
+
}
|
|
31081
|
+
function pruneForAgent(data, full) {
|
|
31082
|
+
return pruneRow(data, full);
|
|
31083
|
+
}
|
|
31084
|
+
function isTabular(rows) {
|
|
31085
|
+
return rows.every(
|
|
31086
|
+
(row) => typeof row === "object" && row !== null && !Array.isArray(row) && Object.values(row).every(
|
|
31087
|
+
(value) => value === null || typeof value !== "object" || Array.isArray(value) && value.every((e) => typeof e !== "object")
|
|
31088
|
+
)
|
|
31089
|
+
);
|
|
31090
|
+
}
|
|
31091
|
+
function cell2(value, key) {
|
|
31092
|
+
if (value === null || value === void 0) return "";
|
|
31093
|
+
if (Array.isArray(value)) return value.join(" ");
|
|
31094
|
+
if (typeof value === "number" && !Number.isInteger(value)) {
|
|
31095
|
+
if (key?.endsWith("Ms")) return String(Math.round(value));
|
|
31096
|
+
return value.toFixed(4).replace(/0+$/, "").replace(/\.$/, "");
|
|
31097
|
+
}
|
|
31098
|
+
return String(value);
|
|
31099
|
+
}
|
|
31100
|
+
var IDENTITY_COLUMNS = [
|
|
31101
|
+
"landingId",
|
|
31102
|
+
"name",
|
|
31103
|
+
"slug",
|
|
31104
|
+
"path",
|
|
31105
|
+
"flowSlug",
|
|
31106
|
+
"stepId",
|
|
31107
|
+
"value",
|
|
31108
|
+
"key",
|
|
31109
|
+
"bucket",
|
|
31110
|
+
"tag",
|
|
31111
|
+
"eventName",
|
|
31112
|
+
"release",
|
|
31113
|
+
"place",
|
|
31114
|
+
"bot",
|
|
31115
|
+
"source",
|
|
31116
|
+
"medium",
|
|
31117
|
+
"campaign",
|
|
31118
|
+
"platform",
|
|
31119
|
+
"sessionId",
|
|
31120
|
+
"eventId"
|
|
31121
|
+
];
|
|
31122
|
+
function byIdentityFirst(a, b) {
|
|
31123
|
+
const ia = IDENTITY_COLUMNS.indexOf(a);
|
|
31124
|
+
const ib = IDENTITY_COLUMNS.indexOf(b);
|
|
31125
|
+
if (ia !== -1 || ib !== -1)
|
|
31126
|
+
return (ia === -1 ? Number.MAX_SAFE_INTEGER : ia) - (ib === -1 ? Number.MAX_SAFE_INTEGER : ib);
|
|
31127
|
+
return 0;
|
|
31128
|
+
}
|
|
31129
|
+
var BLOCK_ORDER = ["window", "totals", "previous", "pageInfo"];
|
|
31130
|
+
function byBlockOrder(a, b) {
|
|
31131
|
+
const rank = (key) => {
|
|
31132
|
+
const at = BLOCK_ORDER.indexOf(key);
|
|
31133
|
+
if (at !== -1) return at;
|
|
31134
|
+
return key === "preset" ? BLOCK_ORDER.length + 1 : BLOCK_ORDER.length;
|
|
31135
|
+
};
|
|
31136
|
+
return rank(a) - rank(b);
|
|
31137
|
+
}
|
|
31138
|
+
function columnsOf(rows) {
|
|
31139
|
+
const seen = [];
|
|
31140
|
+
for (const row of rows) {
|
|
31141
|
+
for (const key of Object.keys(row)) {
|
|
31142
|
+
if (!seen.includes(key)) seen.push(key);
|
|
31143
|
+
}
|
|
31144
|
+
}
|
|
31145
|
+
return seen.filter((key) => rows.some((row) => !isAbsent(row[key]) && row[key] !== null && row[key] !== void 0)).sort(byIdentityFirst);
|
|
31146
|
+
}
|
|
31147
|
+
function table2(rows) {
|
|
31148
|
+
const cols = columnsOf(rows);
|
|
31149
|
+
if (cols.length === 0) return "";
|
|
31150
|
+
const head = `| ${cols.join(" | ")} |`;
|
|
31151
|
+
const rule = `| ${cols.map(() => "---").join(" | ")} |`;
|
|
31152
|
+
const body = rows.map((row) => `| ${cols.map((col) => cell2(row[col], col)).join(" | ")} |`);
|
|
31153
|
+
return [head, rule, ...body].join("\n");
|
|
31154
|
+
}
|
|
31155
|
+
function isRowList(value) {
|
|
31156
|
+
return Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry));
|
|
31157
|
+
}
|
|
31158
|
+
function labelOf(row, index) {
|
|
31159
|
+
for (const key of IDENTITY_COLUMNS) {
|
|
31160
|
+
const value = row[key];
|
|
31161
|
+
if (typeof value === "string" && value !== "") return value;
|
|
31162
|
+
}
|
|
31163
|
+
return `#${index + 1}`;
|
|
31164
|
+
}
|
|
31165
|
+
function nestedBlock(name, rows) {
|
|
31166
|
+
const parents = rows.map(
|
|
31167
|
+
(row) => Object.fromEntries(Object.entries(row).filter(([, value]) => !isRowList(value) && !isPlainRow(value)))
|
|
31168
|
+
);
|
|
31169
|
+
const parts = [`### ${name}`, table2(parents)];
|
|
31170
|
+
rows.forEach((row, index) => {
|
|
31171
|
+
for (const [key, value] of Object.entries(row)) {
|
|
31172
|
+
if (isRowList(value)) parts.push(`#### ${name}[${labelOf(row, index)}].${key}`, table2(value));
|
|
31173
|
+
else if (isPlainRow(value)) parts.push(`#### ${name}[${labelOf(row, index)}].${key}`, table2([value]));
|
|
31174
|
+
}
|
|
31175
|
+
});
|
|
31176
|
+
return parts.filter((part) => part !== "").join("\n");
|
|
31177
|
+
}
|
|
31178
|
+
function isPlainRow(value) {
|
|
31179
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
31180
|
+
}
|
|
31181
|
+
function block(name, value) {
|
|
31182
|
+
if (Array.isArray(value)) {
|
|
31183
|
+
if (value.length === 0) return "";
|
|
31184
|
+
if (isTabular(value)) return `### ${name}
|
|
31185
|
+
${table2(value)}`;
|
|
31186
|
+
if (isRowList(value)) return nestedBlock(name, value);
|
|
31187
|
+
return `### ${name}
|
|
31188
|
+
${JSON.stringify(value)}`;
|
|
31189
|
+
}
|
|
31190
|
+
if (typeof value === "object" && value !== null) {
|
|
31191
|
+
const entries = Object.entries(value);
|
|
31192
|
+
if (entries.length === 0) return "";
|
|
31193
|
+
if (entries.every(([, v]) => v === null || typeof v !== "object")) {
|
|
31194
|
+
return `### ${name}
|
|
31195
|
+
${entries.map(([k, v]) => `- ${k}: ${v === null ? "null" : cell2(v, k)}`).join("\n")}`;
|
|
31196
|
+
}
|
|
31197
|
+
return `### ${name}
|
|
31198
|
+
${JSON.stringify(value)}`;
|
|
31199
|
+
}
|
|
31200
|
+
return `### ${name}
|
|
31201
|
+
${cell2(value)}`;
|
|
31202
|
+
}
|
|
31203
|
+
function renderMarkdown3(envelope) {
|
|
31204
|
+
const parts = [];
|
|
31205
|
+
for (const warning of envelope.warnings ?? []) parts.push(`> **${warning.code}** \u2014 ${warning.message}`);
|
|
31206
|
+
for (const hint of envelope.hints ?? []) parts.push(`> ${hint}`);
|
|
31207
|
+
for (const [name, value] of Object.entries(envelope.data).sort(([a], [b]) => byBlockOrder(a, b))) {
|
|
31208
|
+
const rendered = block(name, value);
|
|
31209
|
+
if (rendered !== "") parts.push(rendered);
|
|
31210
|
+
}
|
|
31211
|
+
return `${parts.join("\n\n")}
|
|
31212
|
+
`;
|
|
31213
|
+
}
|
|
31214
|
+
function missingBreakdownHint(data, asked) {
|
|
31215
|
+
if (asked.eventName === void 0) return [];
|
|
31216
|
+
const breakdown = data.eventBreakdown;
|
|
31217
|
+
if (Array.isArray(breakdown) && breakdown.length > 0) return [];
|
|
31218
|
+
const known = Array.isArray(data.customEvents) ? data.customEvents.flatMap((row) => isPlainRow(row) && typeof row.eventName === "string" ? [row.eventName] : []) : [];
|
|
31219
|
+
return [
|
|
31220
|
+
`No rows came back for --event ${asked.eventName}${asked.property ? ` --property ${asked.property}` : ""}. That event or property does not exist on these pages${known.length > 0 ? `; the ones that do are: ${known.slice(0, 12).join(", ")}${known.length > 12 ? ", \u2026" : ""}` : ""}.`
|
|
31221
|
+
];
|
|
31222
|
+
}
|
|
31223
|
+
var ANALYTICS_OUTPUT_FORMATS = ["json", "md"];
|
|
31224
|
+
function isAnalyticsOutputFormat(value) {
|
|
31225
|
+
return ANALYTICS_OUTPUT_FORMATS.includes(value);
|
|
31226
|
+
}
|
|
31227
|
+
function writeAnalyticsReport(envelope, format) {
|
|
31228
|
+
if (format === "md") {
|
|
31229
|
+
process.stdout.write(renderMarkdown3(envelope));
|
|
31230
|
+
return;
|
|
31231
|
+
}
|
|
31232
|
+
process.stdout.write(`${JSON.stringify(envelope)}
|
|
31233
|
+
`);
|
|
31234
|
+
}
|
|
31235
|
+
var ROWS_BEFORE_FORMAT_HINT = 25;
|
|
31236
|
+
function rowCount(data) {
|
|
31237
|
+
let total = 0;
|
|
31238
|
+
for (const value of Object.values(data)) {
|
|
31239
|
+
if (Array.isArray(value)) total += value.length;
|
|
31240
|
+
}
|
|
31241
|
+
return total;
|
|
31242
|
+
}
|
|
31243
|
+
function formatHint(data, format) {
|
|
31244
|
+
if (format === "md") return [];
|
|
31245
|
+
const rows = rowCount(data);
|
|
31246
|
+
if (rows < ROWS_BEFORE_FORMAT_HINT) return [];
|
|
31247
|
+
return [
|
|
31248
|
+
`This report is ${rows} rows. If you are going to read the numbers rather than pick one field out of them, --output md returns the same answer as tables, typically a third to a half smaller.`
|
|
31249
|
+
];
|
|
31250
|
+
}
|
|
31251
|
+
|
|
31252
|
+
// src/commands/analytics/conversionsMarkdown.ts
|
|
31253
|
+
function doorsCell(doors) {
|
|
31254
|
+
return doors.map((door) => `${door.door}=${door.events}/${door.sessions}`).join(" ");
|
|
31255
|
+
}
|
|
31256
|
+
function eventKeysCell(eventKeys) {
|
|
31257
|
+
const formKeys = eventKeys.filter((key) => key.startsWith("form:")).length;
|
|
31258
|
+
const others = eventKeys.filter((key) => !key.startsWith("form:"));
|
|
31259
|
+
return [...others, ...formKeys > 0 ? [`${formKeys} Form ending(s)`] : []].join(" + ");
|
|
31260
|
+
}
|
|
31261
|
+
function trafficByForm(candidates) {
|
|
31262
|
+
const byForm = /* @__PURE__ */ new Map();
|
|
31263
|
+
for (const row of candidates) {
|
|
31264
|
+
const form = byForm.get(row.flowSlug) ?? { "endings seen": 0, "endings counted": 0, events: 0, lastSeen: "" };
|
|
31265
|
+
form["endings seen"] += 1;
|
|
31266
|
+
if (row.isCounted) form["endings counted"] += 1;
|
|
31267
|
+
form.events += row.events;
|
|
31268
|
+
if (row.lastSeen && row.lastSeen.slice(0, 10) > form.lastSeen) form.lastSeen = row.lastSeen.slice(0, 10);
|
|
31269
|
+
byForm.set(row.flowSlug, form);
|
|
31270
|
+
}
|
|
31271
|
+
return byForm;
|
|
31272
|
+
}
|
|
31273
|
+
var NO_TRAFFIC = { "endings seen": 0, "endings counted": 0, events: 0, lastSeen: "" };
|
|
31274
|
+
var FORM_KEYS_HINT = "A Form's candidates are one row per Form: `baker analytics conversions --flow <slug>` lists that Form's event keys, and `--candidates --output json` lists every key.";
|
|
31275
|
+
function renderConversionsMarkdown(envelope) {
|
|
31276
|
+
const { definitions, flow, flows, candidates } = envelope.data;
|
|
31277
|
+
const data = {
|
|
31278
|
+
definitions: definitions.map(({ name, eventKeys, ...row }) => ({ name, eventKeys: eventKeysCell(eventKeys), ...row }))
|
|
31279
|
+
};
|
|
31280
|
+
if (flow) {
|
|
31281
|
+
data.flow = { slug: flow.slug, noConversionReason: flow.noConversionReason };
|
|
31282
|
+
data.flowEndings = flow.endings;
|
|
31283
|
+
}
|
|
31284
|
+
const traffic = candidates ? trafficByForm(candidates.filter((row) => row.flowSlug !== "")) : /* @__PURE__ */ new Map();
|
|
31285
|
+
if (flows) {
|
|
31286
|
+
data.flows = flows.map((row) => ({
|
|
31287
|
+
slug: row.slug,
|
|
31288
|
+
counted: row.counted.map((entry) => `${entry.name} @ ${entry.step}`).join("; "),
|
|
31289
|
+
noConversion: row.noConversion,
|
|
31290
|
+
...candidates ? traffic.get(row.slug) ?? NO_TRAFFIC : {}
|
|
31291
|
+
}));
|
|
31292
|
+
}
|
|
31293
|
+
const hints2 = [...envelope.hints ?? []];
|
|
31294
|
+
if (candidates) {
|
|
31295
|
+
const listed2 = new Set(flows?.map((row) => row.slug));
|
|
31296
|
+
const unlisted = [...traffic].filter(([slug]) => !listed2.has(slug));
|
|
31297
|
+
if (unlisted.length > 0) data.formCandidates = unlisted.map(([form, row]) => ({ form, ...row }));
|
|
31298
|
+
data.candidates = candidates.filter((row) => row.flowSlug === "").map(({ doors, flowSlug: _flowSlug, nodeType: _nodeType, ...row }) => ({
|
|
31299
|
+
...row,
|
|
31300
|
+
"doors (events/sessions)": doorsCell(doors)
|
|
31301
|
+
}));
|
|
31302
|
+
if (traffic.size > 0) hints2.push(FORM_KEYS_HINT);
|
|
31303
|
+
}
|
|
31304
|
+
return renderMarkdown3({ data, ...hints2.length > 0 ? { hints: hints2 } : {} });
|
|
31305
|
+
}
|
|
31306
|
+
|
|
31035
31307
|
// src/commands/analytics/errorEnvelope.ts
|
|
31036
31308
|
function analyticsErrorEnvelope(err) {
|
|
31037
31309
|
if (err instanceof ApiError) {
|
|
@@ -31523,223 +31795,6 @@ var ANALYTICS_PRESET_INFO = [
|
|
|
31523
31795
|
}
|
|
31524
31796
|
];
|
|
31525
31797
|
|
|
31526
|
-
// src/commands/analytics/render.ts
|
|
31527
|
-
var SPARKLINE_POINTS = 30;
|
|
31528
|
-
var DASHBOARD_ONLY_FIELDS = /* @__PURE__ */ new Set([
|
|
31529
|
-
// The Convex row id. It exists so a dashboard can link to the row; an agent
|
|
31530
|
-
// addresses a landing by `landingId`, which is the id in its `_definition.md`.
|
|
31531
|
-
"landingRef"
|
|
31532
|
-
]);
|
|
31533
|
-
function isAbsent(value) {
|
|
31534
|
-
if (value === "") return true;
|
|
31535
|
-
if (Array.isArray(value)) return value.length === 0;
|
|
31536
|
-
if (typeof value === "object" && value !== null) return Object.keys(value).length === 0;
|
|
31537
|
-
return false;
|
|
31538
|
-
}
|
|
31539
|
-
function resample(series, points) {
|
|
31540
|
-
if (series.length <= points || points < 1) return [...series];
|
|
31541
|
-
const out = [];
|
|
31542
|
-
for (let i = 0; i < points; i += 1) {
|
|
31543
|
-
const from = Math.floor(i * series.length / points);
|
|
31544
|
-
const to = Math.floor((i + 1) * series.length / points);
|
|
31545
|
-
let sum = 0;
|
|
31546
|
-
for (let j = from; j < to; j += 1) sum += series[j] ?? 0;
|
|
31547
|
-
out.push(sum);
|
|
31548
|
-
}
|
|
31549
|
-
return out;
|
|
31550
|
-
}
|
|
31551
|
-
function pruneValue(value, full) {
|
|
31552
|
-
if (Array.isArray(value)) return value.map((entry) => pruneValue(entry, full));
|
|
31553
|
-
if (typeof value === "object" && value !== null) {
|
|
31554
|
-
return pruneRow(value, full);
|
|
31555
|
-
}
|
|
31556
|
-
return value;
|
|
31557
|
-
}
|
|
31558
|
-
function pruneRow(row, full) {
|
|
31559
|
-
const out = {};
|
|
31560
|
-
for (const [key, value] of Object.entries(row)) {
|
|
31561
|
-
if (DASHBOARD_ONLY_FIELDS.has(key)) continue;
|
|
31562
|
-
if (key === "sparkline" && Array.isArray(value)) {
|
|
31563
|
-
if (!full) continue;
|
|
31564
|
-
out[key] = resample(value, SPARKLINE_POINTS);
|
|
31565
|
-
continue;
|
|
31566
|
-
}
|
|
31567
|
-
if (isAbsent(value)) continue;
|
|
31568
|
-
out[key] = pruneValue(value, full);
|
|
31569
|
-
}
|
|
31570
|
-
return out;
|
|
31571
|
-
}
|
|
31572
|
-
function pruneForAgent(data, full) {
|
|
31573
|
-
return pruneRow(data, full);
|
|
31574
|
-
}
|
|
31575
|
-
function isTabular(rows) {
|
|
31576
|
-
return rows.every(
|
|
31577
|
-
(row) => typeof row === "object" && row !== null && !Array.isArray(row) && Object.values(row).every(
|
|
31578
|
-
(value) => value === null || typeof value !== "object" || Array.isArray(value) && value.every((e) => typeof e !== "object")
|
|
31579
|
-
)
|
|
31580
|
-
);
|
|
31581
|
-
}
|
|
31582
|
-
function cell2(value, key) {
|
|
31583
|
-
if (value === null || value === void 0) return "";
|
|
31584
|
-
if (Array.isArray(value)) return value.join(" ");
|
|
31585
|
-
if (typeof value === "number" && !Number.isInteger(value)) {
|
|
31586
|
-
if (key?.endsWith("Ms")) return String(Math.round(value));
|
|
31587
|
-
return value.toFixed(4).replace(/0+$/, "").replace(/\.$/, "");
|
|
31588
|
-
}
|
|
31589
|
-
return String(value);
|
|
31590
|
-
}
|
|
31591
|
-
var IDENTITY_COLUMNS = [
|
|
31592
|
-
"landingId",
|
|
31593
|
-
"name",
|
|
31594
|
-
"slug",
|
|
31595
|
-
"path",
|
|
31596
|
-
"flowSlug",
|
|
31597
|
-
"stepId",
|
|
31598
|
-
"value",
|
|
31599
|
-
"key",
|
|
31600
|
-
"bucket",
|
|
31601
|
-
"tag",
|
|
31602
|
-
"eventName",
|
|
31603
|
-
"release",
|
|
31604
|
-
"place",
|
|
31605
|
-
"bot",
|
|
31606
|
-
"source",
|
|
31607
|
-
"medium",
|
|
31608
|
-
"campaign",
|
|
31609
|
-
"platform",
|
|
31610
|
-
"sessionId",
|
|
31611
|
-
"eventId"
|
|
31612
|
-
];
|
|
31613
|
-
function byIdentityFirst(a, b) {
|
|
31614
|
-
const ia = IDENTITY_COLUMNS.indexOf(a);
|
|
31615
|
-
const ib = IDENTITY_COLUMNS.indexOf(b);
|
|
31616
|
-
if (ia !== -1 || ib !== -1)
|
|
31617
|
-
return (ia === -1 ? Number.MAX_SAFE_INTEGER : ia) - (ib === -1 ? Number.MAX_SAFE_INTEGER : ib);
|
|
31618
|
-
return 0;
|
|
31619
|
-
}
|
|
31620
|
-
var BLOCK_ORDER = ["window", "totals", "previous", "pageInfo"];
|
|
31621
|
-
function byBlockOrder(a, b) {
|
|
31622
|
-
const rank = (key) => {
|
|
31623
|
-
const at = BLOCK_ORDER.indexOf(key);
|
|
31624
|
-
if (at !== -1) return at;
|
|
31625
|
-
return key === "preset" ? BLOCK_ORDER.length + 1 : BLOCK_ORDER.length;
|
|
31626
|
-
};
|
|
31627
|
-
return rank(a) - rank(b);
|
|
31628
|
-
}
|
|
31629
|
-
function columnsOf(rows) {
|
|
31630
|
-
const seen = [];
|
|
31631
|
-
for (const row of rows) {
|
|
31632
|
-
for (const key of Object.keys(row)) {
|
|
31633
|
-
if (!seen.includes(key)) seen.push(key);
|
|
31634
|
-
}
|
|
31635
|
-
}
|
|
31636
|
-
return seen.filter((key) => rows.some((row) => !isAbsent(row[key]) && row[key] !== null && row[key] !== void 0)).sort(byIdentityFirst);
|
|
31637
|
-
}
|
|
31638
|
-
function table2(rows) {
|
|
31639
|
-
const cols = columnsOf(rows);
|
|
31640
|
-
if (cols.length === 0) return "";
|
|
31641
|
-
const head = `| ${cols.join(" | ")} |`;
|
|
31642
|
-
const rule = `| ${cols.map(() => "---").join(" | ")} |`;
|
|
31643
|
-
const body = rows.map((row) => `| ${cols.map((col) => cell2(row[col], col)).join(" | ")} |`);
|
|
31644
|
-
return [head, rule, ...body].join("\n");
|
|
31645
|
-
}
|
|
31646
|
-
function isRowList(value) {
|
|
31647
|
-
return Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry));
|
|
31648
|
-
}
|
|
31649
|
-
function labelOf(row, index) {
|
|
31650
|
-
for (const key of IDENTITY_COLUMNS) {
|
|
31651
|
-
const value = row[key];
|
|
31652
|
-
if (typeof value === "string" && value !== "") return value;
|
|
31653
|
-
}
|
|
31654
|
-
return `#${index + 1}`;
|
|
31655
|
-
}
|
|
31656
|
-
function nestedBlock(name, rows) {
|
|
31657
|
-
const parents = rows.map(
|
|
31658
|
-
(row) => Object.fromEntries(Object.entries(row).filter(([, value]) => !isRowList(value) && !isPlainRow(value)))
|
|
31659
|
-
);
|
|
31660
|
-
const parts = [`### ${name}`, table2(parents)];
|
|
31661
|
-
rows.forEach((row, index) => {
|
|
31662
|
-
for (const [key, value] of Object.entries(row)) {
|
|
31663
|
-
if (isRowList(value)) parts.push(`#### ${name}[${labelOf(row, index)}].${key}`, table2(value));
|
|
31664
|
-
else if (isPlainRow(value)) parts.push(`#### ${name}[${labelOf(row, index)}].${key}`, table2([value]));
|
|
31665
|
-
}
|
|
31666
|
-
});
|
|
31667
|
-
return parts.filter((part) => part !== "").join("\n");
|
|
31668
|
-
}
|
|
31669
|
-
function isPlainRow(value) {
|
|
31670
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
31671
|
-
}
|
|
31672
|
-
function block(name, value) {
|
|
31673
|
-
if (Array.isArray(value)) {
|
|
31674
|
-
if (value.length === 0) return "";
|
|
31675
|
-
if (isTabular(value)) return `### ${name}
|
|
31676
|
-
${table2(value)}`;
|
|
31677
|
-
if (isRowList(value)) return nestedBlock(name, value);
|
|
31678
|
-
return `### ${name}
|
|
31679
|
-
${JSON.stringify(value)}`;
|
|
31680
|
-
}
|
|
31681
|
-
if (typeof value === "object" && value !== null) {
|
|
31682
|
-
const entries = Object.entries(value);
|
|
31683
|
-
if (entries.length === 0) return "";
|
|
31684
|
-
if (entries.every(([, v]) => v === null || typeof v !== "object")) {
|
|
31685
|
-
return `### ${name}
|
|
31686
|
-
${entries.map(([k, v]) => `- ${k}: ${v === null ? "null" : cell2(v, k)}`).join("\n")}`;
|
|
31687
|
-
}
|
|
31688
|
-
return `### ${name}
|
|
31689
|
-
${JSON.stringify(value)}`;
|
|
31690
|
-
}
|
|
31691
|
-
return `### ${name}
|
|
31692
|
-
${cell2(value)}`;
|
|
31693
|
-
}
|
|
31694
|
-
function renderMarkdown3(envelope) {
|
|
31695
|
-
const parts = [];
|
|
31696
|
-
for (const warning of envelope.warnings ?? []) parts.push(`> **${warning.code}** \u2014 ${warning.message}`);
|
|
31697
|
-
for (const hint of envelope.hints ?? []) parts.push(`> ${hint}`);
|
|
31698
|
-
for (const [name, value] of Object.entries(envelope.data).sort(([a], [b]) => byBlockOrder(a, b))) {
|
|
31699
|
-
const rendered = block(name, value);
|
|
31700
|
-
if (rendered !== "") parts.push(rendered);
|
|
31701
|
-
}
|
|
31702
|
-
return `${parts.join("\n\n")}
|
|
31703
|
-
`;
|
|
31704
|
-
}
|
|
31705
|
-
function missingBreakdownHint(data, asked) {
|
|
31706
|
-
if (asked.eventName === void 0) return [];
|
|
31707
|
-
const breakdown = data.eventBreakdown;
|
|
31708
|
-
if (Array.isArray(breakdown) && breakdown.length > 0) return [];
|
|
31709
|
-
const known = Array.isArray(data.customEvents) ? data.customEvents.flatMap((row) => isPlainRow(row) && typeof row.eventName === "string" ? [row.eventName] : []) : [];
|
|
31710
|
-
return [
|
|
31711
|
-
`No rows came back for --event ${asked.eventName}${asked.property ? ` --property ${asked.property}` : ""}. That event or property does not exist on these pages${known.length > 0 ? `; the ones that do are: ${known.slice(0, 12).join(", ")}${known.length > 12 ? ", \u2026" : ""}` : ""}.`
|
|
31712
|
-
];
|
|
31713
|
-
}
|
|
31714
|
-
var ANALYTICS_OUTPUT_FORMATS = ["json", "md"];
|
|
31715
|
-
function isAnalyticsOutputFormat(value) {
|
|
31716
|
-
return ANALYTICS_OUTPUT_FORMATS.includes(value);
|
|
31717
|
-
}
|
|
31718
|
-
function writeAnalyticsReport(envelope, format) {
|
|
31719
|
-
if (format === "md") {
|
|
31720
|
-
process.stdout.write(renderMarkdown3(envelope));
|
|
31721
|
-
return;
|
|
31722
|
-
}
|
|
31723
|
-
process.stdout.write(`${JSON.stringify(envelope)}
|
|
31724
|
-
`);
|
|
31725
|
-
}
|
|
31726
|
-
var ROWS_BEFORE_FORMAT_HINT = 25;
|
|
31727
|
-
function rowCount(data) {
|
|
31728
|
-
let total = 0;
|
|
31729
|
-
for (const value of Object.values(data)) {
|
|
31730
|
-
if (Array.isArray(value)) total += value.length;
|
|
31731
|
-
}
|
|
31732
|
-
return total;
|
|
31733
|
-
}
|
|
31734
|
-
function formatHint(data, format) {
|
|
31735
|
-
if (format === "md") return [];
|
|
31736
|
-
const rows = rowCount(data);
|
|
31737
|
-
if (rows < ROWS_BEFORE_FORMAT_HINT) return [];
|
|
31738
|
-
return [
|
|
31739
|
-
`This report is ${rows} rows. If you are going to read the numbers rather than pick one field out of them, --output md returns the same answer as tables, typically a third to a half smaller.`
|
|
31740
|
-
];
|
|
31741
|
-
}
|
|
31742
|
-
|
|
31743
31798
|
// src/commands/analytics/sends.ts
|
|
31744
31799
|
import { defineCommand as defineCommand97 } from "citty";
|
|
31745
31800
|
var SET_FLAGS = ["--source-event", "--platform", "--tag", "--event"];
|
|
@@ -32914,17 +32969,7 @@ async function runPreset(args5, options) {
|
|
|
32914
32969
|
const response = await apiPost("/api/analytics/query", requestBody(args5, options));
|
|
32915
32970
|
const requestedPlatform = args5.platform === void 0 ? "" : String(args5.platform);
|
|
32916
32971
|
const adPlatformScope = isAdPlatform(requestedPlatform) ? requestedPlatform : void 0;
|
|
32917
|
-
const format =
|
|
32918
|
-
if (!isAnalyticsOutputFormat(format)) {
|
|
32919
|
-
writeJsonEnvelope({
|
|
32920
|
-
ok: false,
|
|
32921
|
-
error: {
|
|
32922
|
-
code: "VALIDATION_ERROR",
|
|
32923
|
-
message: `Unknown --output "${format}". Use one of: ${ANALYTICS_OUTPUT_FORMATS.join(", ")}.`
|
|
32924
|
-
}
|
|
32925
|
-
});
|
|
32926
|
-
process.exit(1);
|
|
32927
|
-
}
|
|
32972
|
+
const format = outputFormatOrExit(args5.output);
|
|
32928
32973
|
const data = pruneForAgent(response.data, args5.full === true);
|
|
32929
32974
|
const feed = "stream" in data ? shapeFeed(data.stream, args5.everything === true) : void 0;
|
|
32930
32975
|
if (feed !== void 0) data.stream = feed.rows;
|
|
@@ -32954,6 +32999,20 @@ async function runPreset(args5, options) {
|
|
|
32954
32999
|
handleError3(err);
|
|
32955
33000
|
}
|
|
32956
33001
|
}
|
|
33002
|
+
function outputFormatOrExit(raw) {
|
|
33003
|
+
const format = raw === void 0 ? "json" : String(raw);
|
|
33004
|
+
if (!isAnalyticsOutputFormat(format)) {
|
|
33005
|
+
writeJsonEnvelope({
|
|
33006
|
+
ok: false,
|
|
33007
|
+
error: {
|
|
33008
|
+
code: "VALIDATION_ERROR",
|
|
33009
|
+
message: `Unknown --output "${format}". Use one of: ${ANALYTICS_OUTPUT_FORMATS.join(", ")}.`
|
|
33010
|
+
}
|
|
33011
|
+
});
|
|
33012
|
+
process.exit(1);
|
|
33013
|
+
}
|
|
33014
|
+
return format;
|
|
33015
|
+
}
|
|
32957
33016
|
function presetCommand(options) {
|
|
32958
33017
|
registerSchema({
|
|
32959
33018
|
command: `analytics.${options.name}`,
|
|
@@ -33455,7 +33514,8 @@ var conversionsCommand3 = (() => {
|
|
|
33455
33514
|
type: "string",
|
|
33456
33515
|
description: "Lookback for the counts and for --candidates. Default 30",
|
|
33457
33516
|
required: false
|
|
33458
|
-
}
|
|
33517
|
+
},
|
|
33518
|
+
output: SHARED_ARGS.output
|
|
33459
33519
|
};
|
|
33460
33520
|
registerSchema({
|
|
33461
33521
|
command: "analytics.conversions",
|
|
@@ -33470,6 +33530,7 @@ var conversionsCommand3 = (() => {
|
|
|
33470
33530
|
args: args5,
|
|
33471
33531
|
run: async ({ args: raw }) => {
|
|
33472
33532
|
try {
|
|
33533
|
+
const format = outputFormatOrExit(raw.output);
|
|
33473
33534
|
const args6 = parseConversionsArgs(raw, process.argv);
|
|
33474
33535
|
for (const eventKey of args6.eventKeys) refuseUnmatchableKey(eventKey);
|
|
33475
33536
|
const before = await apiPost("/api/analytics/conversions", {});
|
|
@@ -33479,13 +33540,13 @@ var conversionsCommand3 = (() => {
|
|
|
33479
33540
|
preset: "conversions",
|
|
33480
33541
|
days: args6.days
|
|
33481
33542
|
});
|
|
33482
|
-
|
|
33483
|
-
|
|
33484
|
-
|
|
33485
|
-
|
|
33486
|
-
report.data.eventCatalog ?? []
|
|
33487
|
-
)
|
|
33543
|
+
const envelope = conversionsEnvelope(
|
|
33544
|
+
args6,
|
|
33545
|
+
mergeStoredWithCounts(stored, report.data.conversions ?? []),
|
|
33546
|
+
report.data.eventCatalog ?? []
|
|
33488
33547
|
);
|
|
33548
|
+
if (format === "md") process.stdout.write(renderConversionsMarkdown(envelope));
|
|
33549
|
+
else writeJsonEnvelope(envelope);
|
|
33489
33550
|
} catch (err) {
|
|
33490
33551
|
if (err instanceof ConversionArgsError) {
|
|
33491
33552
|
handleError3(new ApiError("VALIDATION_ERROR", err.message));
|