@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/dist/cli.js CHANGED
@@ -67,7 +67,7 @@ import {
67
67
  validateCanvasDeep,
68
68
  withoutCastName,
69
69
  ytDlpBlockSignal
70
- } from "./chunk-FFOURYP5.js";
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 = args5.output === void 0 ? "json" : String(args5.output);
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
- writeJsonEnvelope(
33483
- conversionsEnvelope(
33484
- args6,
33485
- mergeStoredWithCounts(stored, report.data.conversions ?? []),
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));