@koda-sl/baker-cli 0.344.0-dev.9890d9fcc → 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 +3 -5
- package/dist/cli.js +309 -369
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2362,7 +2362,6 @@ var GOOGLE_ADS_LIMITS = {
|
|
|
2362
2362
|
businessNameMax: 25
|
|
2363
2363
|
}
|
|
2364
2364
|
};
|
|
2365
|
-
var GOOGLE_ADS_QUERY_MAX_PAGE_SIZE = 1e4;
|
|
2366
2365
|
var KEYWORD_MATCH_TYPES = ["EXACT", "PHRASE", "BROAD"];
|
|
2367
2366
|
var ADVERTISING_CHANNEL_TYPES = [
|
|
2368
2367
|
"SEARCH",
|
|
@@ -10080,72 +10079,7 @@ function urlOptionsHints(report) {
|
|
|
10080
10079
|
return hints2;
|
|
10081
10080
|
}
|
|
10082
10081
|
|
|
10083
|
-
// src/commands/ads/google/during-literals.ts
|
|
10084
|
-
var GAQL_DURING_LITERALS = [
|
|
10085
|
-
"TODAY",
|
|
10086
|
-
"YESTERDAY",
|
|
10087
|
-
"LAST_7_DAYS",
|
|
10088
|
-
"LAST_14_DAYS",
|
|
10089
|
-
"LAST_30_DAYS",
|
|
10090
|
-
"LAST_BUSINESS_WEEK",
|
|
10091
|
-
"THIS_MONTH",
|
|
10092
|
-
"LAST_MONTH",
|
|
10093
|
-
"THIS_WEEK_SUN_TODAY",
|
|
10094
|
-
"THIS_WEEK_MON_TODAY",
|
|
10095
|
-
"LAST_WEEK_SUN_SAT",
|
|
10096
|
-
"LAST_WEEK_MON_SUN"
|
|
10097
|
-
];
|
|
10098
|
-
function isDuringLiteral(value) {
|
|
10099
|
-
return GAQL_DURING_LITERALS.includes(value.toUpperCase());
|
|
10100
|
-
}
|
|
10101
|
-
function isoDay(date) {
|
|
10102
|
-
return date.toISOString().slice(0, 10);
|
|
10103
|
-
}
|
|
10104
|
-
function utcDate(year, month, day) {
|
|
10105
|
-
return new Date(Date.UTC(year, month, day));
|
|
10106
|
-
}
|
|
10107
|
-
function equivalentBetween(literal, now) {
|
|
10108
|
-
const upper2 = literal.toUpperCase();
|
|
10109
|
-
const today = utcDate(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
|
|
10110
|
-
const yesterday = utcDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate() - 1);
|
|
10111
|
-
const range = (from, to) => `BETWEEN '${isoDay(from)}' AND '${isoDay(to)}'`;
|
|
10112
|
-
const year = today.getUTCFullYear();
|
|
10113
|
-
const quarterStart = Math.floor(today.getUTCMonth() / 3) * 3;
|
|
10114
|
-
const rolling = upper2.match(/^LAST_(\d+)_(DAYS|WEEKS|MONTHS|YEARS)$/);
|
|
10115
|
-
if (rolling) {
|
|
10116
|
-
const n = Number(rolling[1]);
|
|
10117
|
-
const unit = rolling[2];
|
|
10118
|
-
if (n < 1) return null;
|
|
10119
|
-
if (unit === "DAYS" || unit === "WEEKS") {
|
|
10120
|
-
const days = unit === "DAYS" ? n : n * 7;
|
|
10121
|
-
return range(utcDate(year, today.getUTCMonth(), today.getUTCDate() - days), yesterday);
|
|
10122
|
-
}
|
|
10123
|
-
const months = unit === "MONTHS" ? n : n * 12;
|
|
10124
|
-
return range(utcDate(year, today.getUTCMonth() - months, today.getUTCDate()), yesterday);
|
|
10125
|
-
}
|
|
10126
|
-
switch (upper2) {
|
|
10127
|
-
case "THIS_YEAR":
|
|
10128
|
-
return range(utcDate(year, 0, 1), today);
|
|
10129
|
-
case "LAST_YEAR":
|
|
10130
|
-
return range(utcDate(year - 1, 0, 1), utcDate(year - 1, 11, 31));
|
|
10131
|
-
case "THIS_QUARTER":
|
|
10132
|
-
return range(utcDate(year, quarterStart, 1), today);
|
|
10133
|
-
case "LAST_QUARTER":
|
|
10134
|
-
return range(utcDate(year, quarterStart - 3, 1), utcDate(year, quarterStart, 0));
|
|
10135
|
-
default:
|
|
10136
|
-
return null;
|
|
10137
|
-
}
|
|
10138
|
-
}
|
|
10139
|
-
function duringLiteralExplanation(literal, between) {
|
|
10140
|
-
const accepted = GAQL_DURING_LITERALS.join(", ");
|
|
10141
|
-
const lead = between ? `Google does not accept ${literal} after DURING. The same window as explicit dates is ${between}.` : `Google does not accept ${literal} after DURING, and it names no fixed window \u2014 use BETWEEN 'YYYY-MM-DD' AND 'YYYY-MM-DD', or drop the date filter.`;
|
|
10142
|
-
return `${lead} DURING accepts only: ${accepted}. Any other window needs BETWEEN.`;
|
|
10143
|
-
}
|
|
10144
|
-
|
|
10145
10082
|
// src/commands/ads/google/changes-window.ts
|
|
10146
|
-
function monthlySpendTrendQuery(now = /* @__PURE__ */ new Date()) {
|
|
10147
|
-
return `SELECT campaign.name, segments.month, metrics.cost_micros, metrics.conversions FROM campaign WHERE segments.date ${equivalentBetween("LAST_12_MONTHS", now)}`;
|
|
10148
|
-
}
|
|
10149
10083
|
function isChangeScope(value) {
|
|
10150
10084
|
return CHANGE_SCOPES.includes(value);
|
|
10151
10085
|
}
|
|
@@ -10199,7 +10133,7 @@ function lookbackTooLong(scope, days, max) {
|
|
|
10199
10133
|
message: `Google's account history API reaches back ${CHANGE_SCOPE_MAX_DAYS.summary} days at most; ${days} days is outside that window.`,
|
|
10200
10134
|
fix: {
|
|
10201
10135
|
action: "use_different_resource",
|
|
10202
|
-
explanation:
|
|
10136
|
+
explanation: "Do not abort \u2014 infer the change from performance instead. Query monthly metrics per campaign (SELECT campaign.name, segments.month, metrics.cost_micros, metrics.conversions FROM campaign WHERE segments.date DURING LAST_12_MONTHS) to see which campaigns stopped or started spending. That shows what changed and when, though not who changed it. Older history exists only in the Google Ads web UI, which has no API \u2014 ask the user to check it if the 'who' matters."
|
|
10203
10137
|
}
|
|
10204
10138
|
};
|
|
10205
10139
|
}
|
|
@@ -10558,7 +10492,7 @@ var CORRECTION_RULES = [
|
|
|
10558
10492
|
matchApiError: /changeStatusError\W{0,4}START_DATE_TOO_OLD/i,
|
|
10559
10493
|
fix: () => ({
|
|
10560
10494
|
action: "use_different_resource",
|
|
10561
|
-
explanation:
|
|
10495
|
+
explanation: "Google's account history reaches back 90 days at most, and this query asks for more. Do not abort \u2014 infer the change from performance: SELECT campaign.name, segments.month, metrics.cost_micros, metrics.conversions FROM campaign WHERE segments.date DURING LAST_12_MONTHS shows which campaigns started or stopped spending and when, though not who changed them. Anything older with attribution exists only in the Google Ads web UI, which has no API."
|
|
10562
10496
|
})
|
|
10563
10497
|
},
|
|
10564
10498
|
// 18c. change_status rejects a date range it cannot bound. Google raises this both for
|
|
@@ -12385,16 +12319,9 @@ import { resolve } from "path";
|
|
|
12385
12319
|
import { defineCommand as defineCommand28 } from "citty";
|
|
12386
12320
|
|
|
12387
12321
|
// src/commands/ads/google/presets.ts
|
|
12388
|
-
var
|
|
12322
|
+
var GAQL_DATE_RANGE_RE = /^(?:TODAY|YESTERDAY|LAST_7_DAYS|LAST_14_DAYS|LAST_30_DAYS|LAST_90_DAYS|THIS_MONTH|LAST_MONTH|THIS_QUARTER|LAST_QUARTER|THIS_YEAR|LAST_YEAR|ALL_TIME|BETWEEN\s+'[0-9]{4}-[0-9]{2}-[0-9]{2}'\s+AND\s+'[0-9]{4}-[0-9]{2}-[0-9]{2}')$/i;
|
|
12389
12323
|
function isValidDateRange(value) {
|
|
12390
|
-
|
|
12391
|
-
return isDuringLiteral(trimmed) || BETWEEN_RE.test(trimmed);
|
|
12392
|
-
}
|
|
12393
|
-
function dateRangeError(dateRange, now) {
|
|
12394
|
-
if (/^[A-Za-z0-9_]+$/.test(dateRange.trim())) {
|
|
12395
|
-
return `Invalid date range: "${dateRange}". ${duringLiteralExplanation(dateRange, equivalentBetween(dateRange, now))} Pass it as --date-range "BETWEEN 'YYYY-MM-DD' AND 'YYYY-MM-DD'".`;
|
|
12396
|
-
}
|
|
12397
|
-
return `Invalid date range: "${dateRange}". Use a GAQL date literal (e.g. LAST_30_DAYS, TODAY) or BETWEEN 'YYYY-MM-DD' AND 'YYYY-MM-DD'.`;
|
|
12324
|
+
return GAQL_DATE_RANGE_RE.test(value.trim());
|
|
12398
12325
|
}
|
|
12399
12326
|
var SERVING_CHAINS = {
|
|
12400
12327
|
keyword_view: ["campaign.status", "ad_group.status", "ad_group_criterion.status"],
|
|
@@ -12530,15 +12457,17 @@ function buildStatusScope(statusFields, includePaused) {
|
|
|
12530
12457
|
const predicate = includePaused ? "!= 'REMOVED'" : "= 'ENABLED'";
|
|
12531
12458
|
return statusFields.map((field) => ` AND ${field} ${predicate}`).join("");
|
|
12532
12459
|
}
|
|
12533
|
-
function expandPreset(preset, params
|
|
12460
|
+
function expandPreset(preset, params) {
|
|
12534
12461
|
const dateRange = params.dateRange ?? preset.defaultDateRange;
|
|
12535
12462
|
const limit = params.limit ?? preset.defaultLimit;
|
|
12536
|
-
if (
|
|
12537
|
-
return {
|
|
12463
|
+
if (!isValidDateRange(dateRange)) {
|
|
12464
|
+
return {
|
|
12465
|
+
query: "",
|
|
12466
|
+
dateRangeError: `Invalid date range: "${dateRange}". Use a GAQL date literal (e.g. LAST_30_DAYS, TODAY) or BETWEEN 'YYYY-MM-DD' AND 'YYYY-MM-DD'.`
|
|
12467
|
+
};
|
|
12538
12468
|
}
|
|
12539
12469
|
const statusScope = buildStatusScope(preset.statusFields, params.includePaused ?? false);
|
|
12540
|
-
const
|
|
12541
|
-
const query = preset.gaqlTemplate.replace(/DURING\s+\{dateRange\}/g, BETWEEN_RE.test(range) ? range : `DURING ${range}`).replace(/\{dateRange\}/g, range).replace(/\{limit\}/g, String(limit)).replace(/\{statusScope\}/g, statusScope);
|
|
12470
|
+
const query = preset.gaqlTemplate.replace(/\{dateRange\}/g, dateRange).replace(/\{limit\}/g, String(limit)).replace(/\{statusScope\}/g, statusScope);
|
|
12542
12471
|
return { query };
|
|
12543
12472
|
}
|
|
12544
12473
|
|
|
@@ -12697,28 +12626,6 @@ function applyAutoFixes(query, limit) {
|
|
|
12697
12626
|
}
|
|
12698
12627
|
return { query: corrected, warnings };
|
|
12699
12628
|
}
|
|
12700
|
-
function rejectUnsupportedDuring(query, customerId) {
|
|
12701
|
-
for (const match of query.matchAll(/(?<=[a-z_]\.[a-z_.]+\s+)DURING\s+([A-Za-z0-9_]+)/gi)) {
|
|
12702
|
-
const literal = match[1] ?? "";
|
|
12703
|
-
if (isDuringLiteral(literal)) continue;
|
|
12704
|
-
const between = equivalentBetween(literal, /* @__PURE__ */ new Date());
|
|
12705
|
-
const fixed = between ? query.replace(match[0], between) : void 0;
|
|
12706
|
-
return {
|
|
12707
|
-
ok: false,
|
|
12708
|
-
error: {
|
|
12709
|
-
code: "INVALID_DATE_RANGE",
|
|
12710
|
-
message: `DURING ${literal} is not a Google Ads date range, so Google would refuse this query.`,
|
|
12711
|
-
fix: {
|
|
12712
|
-
action: "narrow_date_range",
|
|
12713
|
-
...fixed ? { correctedCommand: buildCommand2(fixed, customerId) } : {},
|
|
12714
|
-
explanation: duringLiteralExplanation(literal, between)
|
|
12715
|
-
},
|
|
12716
|
-
retryable: false
|
|
12717
|
-
}
|
|
12718
|
-
};
|
|
12719
|
-
}
|
|
12720
|
-
return null;
|
|
12721
|
-
}
|
|
12722
12629
|
function validatePreflight(query, customerId, limit) {
|
|
12723
12630
|
const warnings = [];
|
|
12724
12631
|
const corrected = query;
|
|
@@ -12790,8 +12697,6 @@ function validatePreflight(query, customerId, limit) {
|
|
|
12790
12697
|
}
|
|
12791
12698
|
};
|
|
12792
12699
|
}
|
|
12793
|
-
const duringRejection = rejectUnsupportedDuring(corrected, customerId);
|
|
12794
|
-
if (duringRejection) return { valid: false, warnings: [], error: duringRejection };
|
|
12795
12700
|
if (/\bsegments\.conversion_action_name\b/i.test(corrected) && /\bmetrics\.cost_micros\b/i.test(corrected)) {
|
|
12796
12701
|
return {
|
|
12797
12702
|
valid: false,
|
|
@@ -13000,7 +12905,7 @@ registerSchema({
|
|
|
13000
12905
|
},
|
|
13001
12906
|
"date-range": {
|
|
13002
12907
|
type: "string",
|
|
13003
|
-
description: "Date range for presets
|
|
12908
|
+
description: "Date range for presets (LAST_7_DAYS, LAST_30_DAYS, LAST_90_DAYS, or BETWEEN 'X' AND 'Y')",
|
|
13004
12909
|
required: false
|
|
13005
12910
|
},
|
|
13006
12911
|
"include-paused": {
|
|
@@ -13439,35 +13344,9 @@ async function executeQuery(finalQuery, args5, customerId, limit, warnings, useC
|
|
|
13439
13344
|
function shouldReadCache(args5) {
|
|
13440
13345
|
return !args5["no-cache"] && !args5.out;
|
|
13441
13346
|
}
|
|
13442
|
-
var DEFAULT_QUERY_LIMIT = 200;
|
|
13443
|
-
function parseQueryLimit(raw) {
|
|
13444
|
-
if (raw === void 0 || raw === null || raw === "") return { ok: true, limit: DEFAULT_QUERY_LIMIT };
|
|
13445
|
-
const limit = Number(raw);
|
|
13446
|
-
if (Number.isInteger(limit) && limit >= 1 && limit <= GOOGLE_ADS_QUERY_MAX_PAGE_SIZE) return { ok: true, limit };
|
|
13447
|
-
return {
|
|
13448
|
-
ok: false,
|
|
13449
|
-
error: {
|
|
13450
|
-
ok: false,
|
|
13451
|
-
error: {
|
|
13452
|
-
code: "VALIDATION_ERROR",
|
|
13453
|
-
message: `--limit must be a whole number from 1 to ${GOOGLE_ADS_QUERY_MAX_PAGE_SIZE}, got '${String(raw)}'.`,
|
|
13454
|
-
fix: {
|
|
13455
|
-
action: "retry_with_flag",
|
|
13456
|
-
explanation: `--limit is the size of one page, capped at ${GOOGLE_ADS_QUERY_MAX_PAGE_SIZE}. To read more rows, pass --limit ${GOOGLE_ADS_QUERY_MAX_PAGE_SIZE} with --all --out <file> and put your own LIMIT in the statement above the row count you expect.`
|
|
13457
|
-
},
|
|
13458
|
-
retryable: false
|
|
13459
|
-
}
|
|
13460
|
-
}
|
|
13461
|
-
};
|
|
13462
|
-
}
|
|
13463
13347
|
async function validateArgs(args5) {
|
|
13464
|
-
const parsedLimit = parseQueryLimit(args5.limit);
|
|
13465
|
-
if (!parsedLimit.ok) {
|
|
13466
|
-
writeAdsJson(parsedLimit.error);
|
|
13467
|
-
process.exit(1);
|
|
13468
|
-
}
|
|
13469
|
-
const { limit } = parsedLimit;
|
|
13470
13348
|
const customerId = await resolveCustomerId(args5);
|
|
13349
|
+
const limit = args5.limit ? Number(args5.limit) : 200;
|
|
13471
13350
|
const gaql = resolveGaql(args5, limit);
|
|
13472
13351
|
if (!gaql) {
|
|
13473
13352
|
process.exit(1);
|
|
@@ -31153,6 +31032,278 @@ function mergeStoredWithCounts(stored, reported) {
|
|
|
31153
31032
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
31154
31033
|
}
|
|
31155
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
|
+
|
|
31156
31307
|
// src/commands/analytics/errorEnvelope.ts
|
|
31157
31308
|
function analyticsErrorEnvelope(err) {
|
|
31158
31309
|
if (err instanceof ApiError) {
|
|
@@ -31644,223 +31795,6 @@ var ANALYTICS_PRESET_INFO = [
|
|
|
31644
31795
|
}
|
|
31645
31796
|
];
|
|
31646
31797
|
|
|
31647
|
-
// src/commands/analytics/render.ts
|
|
31648
|
-
var SPARKLINE_POINTS = 30;
|
|
31649
|
-
var DASHBOARD_ONLY_FIELDS = /* @__PURE__ */ new Set([
|
|
31650
|
-
// The Convex row id. It exists so a dashboard can link to the row; an agent
|
|
31651
|
-
// addresses a landing by `landingId`, which is the id in its `_definition.md`.
|
|
31652
|
-
"landingRef"
|
|
31653
|
-
]);
|
|
31654
|
-
function isAbsent(value) {
|
|
31655
|
-
if (value === "") return true;
|
|
31656
|
-
if (Array.isArray(value)) return value.length === 0;
|
|
31657
|
-
if (typeof value === "object" && value !== null) return Object.keys(value).length === 0;
|
|
31658
|
-
return false;
|
|
31659
|
-
}
|
|
31660
|
-
function resample(series, points) {
|
|
31661
|
-
if (series.length <= points || points < 1) return [...series];
|
|
31662
|
-
const out = [];
|
|
31663
|
-
for (let i = 0; i < points; i += 1) {
|
|
31664
|
-
const from = Math.floor(i * series.length / points);
|
|
31665
|
-
const to = Math.floor((i + 1) * series.length / points);
|
|
31666
|
-
let sum = 0;
|
|
31667
|
-
for (let j = from; j < to; j += 1) sum += series[j] ?? 0;
|
|
31668
|
-
out.push(sum);
|
|
31669
|
-
}
|
|
31670
|
-
return out;
|
|
31671
|
-
}
|
|
31672
|
-
function pruneValue(value, full) {
|
|
31673
|
-
if (Array.isArray(value)) return value.map((entry) => pruneValue(entry, full));
|
|
31674
|
-
if (typeof value === "object" && value !== null) {
|
|
31675
|
-
return pruneRow(value, full);
|
|
31676
|
-
}
|
|
31677
|
-
return value;
|
|
31678
|
-
}
|
|
31679
|
-
function pruneRow(row, full) {
|
|
31680
|
-
const out = {};
|
|
31681
|
-
for (const [key, value] of Object.entries(row)) {
|
|
31682
|
-
if (DASHBOARD_ONLY_FIELDS.has(key)) continue;
|
|
31683
|
-
if (key === "sparkline" && Array.isArray(value)) {
|
|
31684
|
-
if (!full) continue;
|
|
31685
|
-
out[key] = resample(value, SPARKLINE_POINTS);
|
|
31686
|
-
continue;
|
|
31687
|
-
}
|
|
31688
|
-
if (isAbsent(value)) continue;
|
|
31689
|
-
out[key] = pruneValue(value, full);
|
|
31690
|
-
}
|
|
31691
|
-
return out;
|
|
31692
|
-
}
|
|
31693
|
-
function pruneForAgent(data, full) {
|
|
31694
|
-
return pruneRow(data, full);
|
|
31695
|
-
}
|
|
31696
|
-
function isTabular(rows) {
|
|
31697
|
-
return rows.every(
|
|
31698
|
-
(row) => typeof row === "object" && row !== null && !Array.isArray(row) && Object.values(row).every(
|
|
31699
|
-
(value) => value === null || typeof value !== "object" || Array.isArray(value) && value.every((e) => typeof e !== "object")
|
|
31700
|
-
)
|
|
31701
|
-
);
|
|
31702
|
-
}
|
|
31703
|
-
function cell2(value, key) {
|
|
31704
|
-
if (value === null || value === void 0) return "";
|
|
31705
|
-
if (Array.isArray(value)) return value.join(" ");
|
|
31706
|
-
if (typeof value === "number" && !Number.isInteger(value)) {
|
|
31707
|
-
if (key?.endsWith("Ms")) return String(Math.round(value));
|
|
31708
|
-
return value.toFixed(4).replace(/0+$/, "").replace(/\.$/, "");
|
|
31709
|
-
}
|
|
31710
|
-
return String(value);
|
|
31711
|
-
}
|
|
31712
|
-
var IDENTITY_COLUMNS = [
|
|
31713
|
-
"landingId",
|
|
31714
|
-
"name",
|
|
31715
|
-
"slug",
|
|
31716
|
-
"path",
|
|
31717
|
-
"flowSlug",
|
|
31718
|
-
"stepId",
|
|
31719
|
-
"value",
|
|
31720
|
-
"key",
|
|
31721
|
-
"bucket",
|
|
31722
|
-
"tag",
|
|
31723
|
-
"eventName",
|
|
31724
|
-
"release",
|
|
31725
|
-
"place",
|
|
31726
|
-
"bot",
|
|
31727
|
-
"source",
|
|
31728
|
-
"medium",
|
|
31729
|
-
"campaign",
|
|
31730
|
-
"platform",
|
|
31731
|
-
"sessionId",
|
|
31732
|
-
"eventId"
|
|
31733
|
-
];
|
|
31734
|
-
function byIdentityFirst(a, b) {
|
|
31735
|
-
const ia = IDENTITY_COLUMNS.indexOf(a);
|
|
31736
|
-
const ib = IDENTITY_COLUMNS.indexOf(b);
|
|
31737
|
-
if (ia !== -1 || ib !== -1)
|
|
31738
|
-
return (ia === -1 ? Number.MAX_SAFE_INTEGER : ia) - (ib === -1 ? Number.MAX_SAFE_INTEGER : ib);
|
|
31739
|
-
return 0;
|
|
31740
|
-
}
|
|
31741
|
-
var BLOCK_ORDER = ["window", "totals", "previous", "pageInfo"];
|
|
31742
|
-
function byBlockOrder(a, b) {
|
|
31743
|
-
const rank = (key) => {
|
|
31744
|
-
const at = BLOCK_ORDER.indexOf(key);
|
|
31745
|
-
if (at !== -1) return at;
|
|
31746
|
-
return key === "preset" ? BLOCK_ORDER.length + 1 : BLOCK_ORDER.length;
|
|
31747
|
-
};
|
|
31748
|
-
return rank(a) - rank(b);
|
|
31749
|
-
}
|
|
31750
|
-
function columnsOf(rows) {
|
|
31751
|
-
const seen = [];
|
|
31752
|
-
for (const row of rows) {
|
|
31753
|
-
for (const key of Object.keys(row)) {
|
|
31754
|
-
if (!seen.includes(key)) seen.push(key);
|
|
31755
|
-
}
|
|
31756
|
-
}
|
|
31757
|
-
return seen.filter((key) => rows.some((row) => !isAbsent(row[key]) && row[key] !== null && row[key] !== void 0)).sort(byIdentityFirst);
|
|
31758
|
-
}
|
|
31759
|
-
function table2(rows) {
|
|
31760
|
-
const cols = columnsOf(rows);
|
|
31761
|
-
if (cols.length === 0) return "";
|
|
31762
|
-
const head = `| ${cols.join(" | ")} |`;
|
|
31763
|
-
const rule = `| ${cols.map(() => "---").join(" | ")} |`;
|
|
31764
|
-
const body = rows.map((row) => `| ${cols.map((col) => cell2(row[col], col)).join(" | ")} |`);
|
|
31765
|
-
return [head, rule, ...body].join("\n");
|
|
31766
|
-
}
|
|
31767
|
-
function isRowList(value) {
|
|
31768
|
-
return Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry));
|
|
31769
|
-
}
|
|
31770
|
-
function labelOf(row, index) {
|
|
31771
|
-
for (const key of IDENTITY_COLUMNS) {
|
|
31772
|
-
const value = row[key];
|
|
31773
|
-
if (typeof value === "string" && value !== "") return value;
|
|
31774
|
-
}
|
|
31775
|
-
return `#${index + 1}`;
|
|
31776
|
-
}
|
|
31777
|
-
function nestedBlock(name, rows) {
|
|
31778
|
-
const parents = rows.map(
|
|
31779
|
-
(row) => Object.fromEntries(Object.entries(row).filter(([, value]) => !isRowList(value) && !isPlainRow(value)))
|
|
31780
|
-
);
|
|
31781
|
-
const parts = [`### ${name}`, table2(parents)];
|
|
31782
|
-
rows.forEach((row, index) => {
|
|
31783
|
-
for (const [key, value] of Object.entries(row)) {
|
|
31784
|
-
if (isRowList(value)) parts.push(`#### ${name}[${labelOf(row, index)}].${key}`, table2(value));
|
|
31785
|
-
else if (isPlainRow(value)) parts.push(`#### ${name}[${labelOf(row, index)}].${key}`, table2([value]));
|
|
31786
|
-
}
|
|
31787
|
-
});
|
|
31788
|
-
return parts.filter((part) => part !== "").join("\n");
|
|
31789
|
-
}
|
|
31790
|
-
function isPlainRow(value) {
|
|
31791
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
31792
|
-
}
|
|
31793
|
-
function block(name, value) {
|
|
31794
|
-
if (Array.isArray(value)) {
|
|
31795
|
-
if (value.length === 0) return "";
|
|
31796
|
-
if (isTabular(value)) return `### ${name}
|
|
31797
|
-
${table2(value)}`;
|
|
31798
|
-
if (isRowList(value)) return nestedBlock(name, value);
|
|
31799
|
-
return `### ${name}
|
|
31800
|
-
${JSON.stringify(value)}`;
|
|
31801
|
-
}
|
|
31802
|
-
if (typeof value === "object" && value !== null) {
|
|
31803
|
-
const entries = Object.entries(value);
|
|
31804
|
-
if (entries.length === 0) return "";
|
|
31805
|
-
if (entries.every(([, v]) => v === null || typeof v !== "object")) {
|
|
31806
|
-
return `### ${name}
|
|
31807
|
-
${entries.map(([k, v]) => `- ${k}: ${v === null ? "null" : cell2(v, k)}`).join("\n")}`;
|
|
31808
|
-
}
|
|
31809
|
-
return `### ${name}
|
|
31810
|
-
${JSON.stringify(value)}`;
|
|
31811
|
-
}
|
|
31812
|
-
return `### ${name}
|
|
31813
|
-
${cell2(value)}`;
|
|
31814
|
-
}
|
|
31815
|
-
function renderMarkdown3(envelope) {
|
|
31816
|
-
const parts = [];
|
|
31817
|
-
for (const warning of envelope.warnings ?? []) parts.push(`> **${warning.code}** \u2014 ${warning.message}`);
|
|
31818
|
-
for (const hint of envelope.hints ?? []) parts.push(`> ${hint}`);
|
|
31819
|
-
for (const [name, value] of Object.entries(envelope.data).sort(([a], [b]) => byBlockOrder(a, b))) {
|
|
31820
|
-
const rendered = block(name, value);
|
|
31821
|
-
if (rendered !== "") parts.push(rendered);
|
|
31822
|
-
}
|
|
31823
|
-
return `${parts.join("\n\n")}
|
|
31824
|
-
`;
|
|
31825
|
-
}
|
|
31826
|
-
function missingBreakdownHint(data, asked) {
|
|
31827
|
-
if (asked.eventName === void 0) return [];
|
|
31828
|
-
const breakdown = data.eventBreakdown;
|
|
31829
|
-
if (Array.isArray(breakdown) && breakdown.length > 0) return [];
|
|
31830
|
-
const known = Array.isArray(data.customEvents) ? data.customEvents.flatMap((row) => isPlainRow(row) && typeof row.eventName === "string" ? [row.eventName] : []) : [];
|
|
31831
|
-
return [
|
|
31832
|
-
`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" : ""}` : ""}.`
|
|
31833
|
-
];
|
|
31834
|
-
}
|
|
31835
|
-
var ANALYTICS_OUTPUT_FORMATS = ["json", "md"];
|
|
31836
|
-
function isAnalyticsOutputFormat(value) {
|
|
31837
|
-
return ANALYTICS_OUTPUT_FORMATS.includes(value);
|
|
31838
|
-
}
|
|
31839
|
-
function writeAnalyticsReport(envelope, format) {
|
|
31840
|
-
if (format === "md") {
|
|
31841
|
-
process.stdout.write(renderMarkdown3(envelope));
|
|
31842
|
-
return;
|
|
31843
|
-
}
|
|
31844
|
-
process.stdout.write(`${JSON.stringify(envelope)}
|
|
31845
|
-
`);
|
|
31846
|
-
}
|
|
31847
|
-
var ROWS_BEFORE_FORMAT_HINT = 25;
|
|
31848
|
-
function rowCount(data) {
|
|
31849
|
-
let total = 0;
|
|
31850
|
-
for (const value of Object.values(data)) {
|
|
31851
|
-
if (Array.isArray(value)) total += value.length;
|
|
31852
|
-
}
|
|
31853
|
-
return total;
|
|
31854
|
-
}
|
|
31855
|
-
function formatHint(data, format) {
|
|
31856
|
-
if (format === "md") return [];
|
|
31857
|
-
const rows = rowCount(data);
|
|
31858
|
-
if (rows < ROWS_BEFORE_FORMAT_HINT) return [];
|
|
31859
|
-
return [
|
|
31860
|
-
`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.`
|
|
31861
|
-
];
|
|
31862
|
-
}
|
|
31863
|
-
|
|
31864
31798
|
// src/commands/analytics/sends.ts
|
|
31865
31799
|
import { defineCommand as defineCommand97 } from "citty";
|
|
31866
31800
|
var SET_FLAGS = ["--source-event", "--platform", "--tag", "--event"];
|
|
@@ -33035,17 +32969,7 @@ async function runPreset(args5, options) {
|
|
|
33035
32969
|
const response = await apiPost("/api/analytics/query", requestBody(args5, options));
|
|
33036
32970
|
const requestedPlatform = args5.platform === void 0 ? "" : String(args5.platform);
|
|
33037
32971
|
const adPlatformScope = isAdPlatform(requestedPlatform) ? requestedPlatform : void 0;
|
|
33038
|
-
const format =
|
|
33039
|
-
if (!isAnalyticsOutputFormat(format)) {
|
|
33040
|
-
writeJsonEnvelope({
|
|
33041
|
-
ok: false,
|
|
33042
|
-
error: {
|
|
33043
|
-
code: "VALIDATION_ERROR",
|
|
33044
|
-
message: `Unknown --output "${format}". Use one of: ${ANALYTICS_OUTPUT_FORMATS.join(", ")}.`
|
|
33045
|
-
}
|
|
33046
|
-
});
|
|
33047
|
-
process.exit(1);
|
|
33048
|
-
}
|
|
32972
|
+
const format = outputFormatOrExit(args5.output);
|
|
33049
32973
|
const data = pruneForAgent(response.data, args5.full === true);
|
|
33050
32974
|
const feed = "stream" in data ? shapeFeed(data.stream, args5.everything === true) : void 0;
|
|
33051
32975
|
if (feed !== void 0) data.stream = feed.rows;
|
|
@@ -33075,6 +32999,20 @@ async function runPreset(args5, options) {
|
|
|
33075
32999
|
handleError3(err);
|
|
33076
33000
|
}
|
|
33077
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
|
+
}
|
|
33078
33016
|
function presetCommand(options) {
|
|
33079
33017
|
registerSchema({
|
|
33080
33018
|
command: `analytics.${options.name}`,
|
|
@@ -33576,7 +33514,8 @@ var conversionsCommand3 = (() => {
|
|
|
33576
33514
|
type: "string",
|
|
33577
33515
|
description: "Lookback for the counts and for --candidates. Default 30",
|
|
33578
33516
|
required: false
|
|
33579
|
-
}
|
|
33517
|
+
},
|
|
33518
|
+
output: SHARED_ARGS.output
|
|
33580
33519
|
};
|
|
33581
33520
|
registerSchema({
|
|
33582
33521
|
command: "analytics.conversions",
|
|
@@ -33591,6 +33530,7 @@ var conversionsCommand3 = (() => {
|
|
|
33591
33530
|
args: args5,
|
|
33592
33531
|
run: async ({ args: raw }) => {
|
|
33593
33532
|
try {
|
|
33533
|
+
const format = outputFormatOrExit(raw.output);
|
|
33594
33534
|
const args6 = parseConversionsArgs(raw, process.argv);
|
|
33595
33535
|
for (const eventKey of args6.eventKeys) refuseUnmatchableKey(eventKey);
|
|
33596
33536
|
const before = await apiPost("/api/analytics/conversions", {});
|
|
@@ -33600,13 +33540,13 @@ var conversionsCommand3 = (() => {
|
|
|
33600
33540
|
preset: "conversions",
|
|
33601
33541
|
days: args6.days
|
|
33602
33542
|
});
|
|
33603
|
-
|
|
33604
|
-
|
|
33605
|
-
|
|
33606
|
-
|
|
33607
|
-
report.data.eventCatalog ?? []
|
|
33608
|
-
)
|
|
33543
|
+
const envelope = conversionsEnvelope(
|
|
33544
|
+
args6,
|
|
33545
|
+
mergeStoredWithCounts(stored, report.data.conversions ?? []),
|
|
33546
|
+
report.data.eventCatalog ?? []
|
|
33609
33547
|
);
|
|
33548
|
+
if (format === "md") process.stdout.write(renderConversionsMarkdown(envelope));
|
|
33549
|
+
else writeJsonEnvelope(envelope);
|
|
33610
33550
|
} catch (err) {
|
|
33611
33551
|
if (err instanceof ConversionArgsError) {
|
|
33612
33552
|
handleError3(new ApiError("VALIDATION_ERROR", err.message));
|