@koda-sl/baker-cli 0.236.0 → 0.237.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/cli.js +107 -6
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1204,6 +1204,8 @@ Shared flags: `--days <n>` (default 30) or `--start-date` / `--end-date` (`YYYY-
|
|
|
1204
1204
|
|
|
1205
1205
|
**Campaign parameters are per platform, and so are the numbers.** `baker analytics ads` returns `adPlatformTraffic` (visits, conversions and *tagged* visits for each of the nine platforms), `adParams` for the platform in `--platform` only, and `adParamsUnattributed` for names arriving on visits with no click id and no recognisable source. Read `adPlatformTraffic` first: a platform with visits and zero tagged visits has untagged ad URLs, and no mapping can fix that from Baker's side. `baker analytics map` needs `--platform` for any change, accepts a parameter name **nobody has sent yet** — which is how a tracking template is configured before its campaign runs — and takes an answer back with `--remove`. `--set <name>=ignore` says a parameter is not campaign information at all, so Baker stops storing it and stops listing it.
|
|
1206
1206
|
|
|
1207
|
+
**The one recommendation about the ad account itself.** `baker analytics ads --platform <p>` warns when that platform's traffic carries no campaign id, ad group id or ad id — the three tiers every "which ad worked" question is grouped by — and returns the exact URL suffix, in the platform's own macro language, that would start sending the missing ones. It is the only mapping gap that cannot be fixed afterwards: naming a parameter Baker already captures corrects the history, because roles are resolved when a report is read, but a parameter the ads never sent has no stored values for a meaning to apply to. The hint stays quiet for a platform with little traffic, and defers to the untagged-account finding rather than repeating it.
|
|
1208
|
+
|
|
1207
1209
|
An unrecognised `--timezone` is refused with a `VALIDATION_ERROR` rather than defaulting to UTC, and `--output` accepts only `json` or `md`. Pass `--timezone` whenever the client is not in UTC. Day boundaries are drawn in UTC by default, which moves two hours of every Spanish evening onto the following day — not a rounding error on a day-of-week comparison, but the wrong day.
|
|
1208
1210
|
|
|
1209
1211
|
Reading the output:
|
package/dist/cli.js
CHANGED
|
@@ -11347,6 +11347,62 @@ var AD_PARAM_ROLES = [
|
|
|
11347
11347
|
"other",
|
|
11348
11348
|
"ignore"
|
|
11349
11349
|
];
|
|
11350
|
+
var AD_PARAM_ROLE_SPECS = {
|
|
11351
|
+
campaign_id: { role: "campaign_id", label: "Campaign ID", description: "The top level of this account's structure." },
|
|
11352
|
+
campaign_name: { role: "campaign_name", label: "Campaign name", description: "The campaign's name." },
|
|
11353
|
+
adgroup_id: { role: "adgroup_id", label: "Ad group ID", description: "The targeting container inside a campaign." },
|
|
11354
|
+
adgroup_name: { role: "adgroup_name", label: "Ad group name", description: "The ad group's name." },
|
|
11355
|
+
ad_id: { role: "ad_id", label: "Ad ID", description: "The individual ad that was clicked." },
|
|
11356
|
+
ad_name: { role: "ad_name", label: "Ad name", description: "The individual ad's name." },
|
|
11357
|
+
keyword: {
|
|
11358
|
+
role: "keyword",
|
|
11359
|
+
label: "Keyword",
|
|
11360
|
+
description: "The keyword that matched, as it is written in the account \u2014 not what the visitor typed."
|
|
11361
|
+
},
|
|
11362
|
+
match_type: {
|
|
11363
|
+
role: "match_type",
|
|
11364
|
+
label: "Match type",
|
|
11365
|
+
description: "How loosely the keyword matched: broad, phrase or exact."
|
|
11366
|
+
},
|
|
11367
|
+
search_query: { role: "search_query", label: "Search query", description: "What the visitor actually typed." },
|
|
11368
|
+
audience: {
|
|
11369
|
+
role: "audience",
|
|
11370
|
+
label: "Audience",
|
|
11371
|
+
description: "The remarketing list or interest segment the visitor was in."
|
|
11372
|
+
},
|
|
11373
|
+
placement: {
|
|
11374
|
+
role: "placement",
|
|
11375
|
+
label: "Placement",
|
|
11376
|
+
description: "Where the ad appeared \u2014 a feed, a story, a site on the display network."
|
|
11377
|
+
},
|
|
11378
|
+
network: {
|
|
11379
|
+
role: "network",
|
|
11380
|
+
label: "Network",
|
|
11381
|
+
description: "Which network served it: search, a search partner, display, or a video surface."
|
|
11382
|
+
},
|
|
11383
|
+
site_source: {
|
|
11384
|
+
role: "site_source",
|
|
11385
|
+
label: "App",
|
|
11386
|
+
description: "Which app the click came from, on platforms that span several."
|
|
11387
|
+
},
|
|
11388
|
+
device: { role: "device", label: "Device", description: "Mobile, tablet or desktop, as the platform saw it." },
|
|
11389
|
+
product_id: { role: "product_id", label: "Product ID", description: "The shopping item that was clicked." },
|
|
11390
|
+
account_name: {
|
|
11391
|
+
role: "account_name",
|
|
11392
|
+
label: "Account name",
|
|
11393
|
+
description: "Which ad account paid for the click, where an agency runs several."
|
|
11394
|
+
},
|
|
11395
|
+
other: {
|
|
11396
|
+
role: "other",
|
|
11397
|
+
label: "Something else",
|
|
11398
|
+
description: "Keep the values and let me filter by this name, without saying what it means."
|
|
11399
|
+
},
|
|
11400
|
+
ignore: {
|
|
11401
|
+
role: "ignore",
|
|
11402
|
+
label: "Ignore it",
|
|
11403
|
+
description: "Not campaign information. Baker stops storing it and stops asking about it."
|
|
11404
|
+
}
|
|
11405
|
+
};
|
|
11350
11406
|
var RESERVED_PARAM_NAMES = [
|
|
11351
11407
|
"utm_source",
|
|
11352
11408
|
"utm_medium",
|
|
@@ -11365,6 +11421,9 @@ var AD_PLATFORMS = [
|
|
|
11365
11421
|
"pinterest",
|
|
11366
11422
|
"snapchat"
|
|
11367
11423
|
];
|
|
11424
|
+
function isAdPlatform(value) {
|
|
11425
|
+
return AD_PLATFORMS.includes(value);
|
|
11426
|
+
}
|
|
11368
11427
|
var GOOGLE = {
|
|
11369
11428
|
platform: "google",
|
|
11370
11429
|
label: "Google Ads",
|
|
@@ -11557,6 +11616,10 @@ var AD_PLATFORM_DEFINITIONS = {
|
|
|
11557
11616
|
pinterest: PINTEREST,
|
|
11558
11617
|
snapchat: SNAPCHAT
|
|
11559
11618
|
};
|
|
11619
|
+
var ESSENTIAL_ROLES = ["campaign_id", "adgroup_id", "ad_id", "keyword", "match_type"];
|
|
11620
|
+
function essentialAdDimensions(platform) {
|
|
11621
|
+
return AD_PLATFORM_DEFINITIONS[platform].dimensions.filter((entry) => ESSENTIAL_ROLES.includes(entry.role));
|
|
11622
|
+
}
|
|
11560
11623
|
function normalizeAdParamName(raw) {
|
|
11561
11624
|
return raw.trim().toLowerCase().slice(0, 64);
|
|
11562
11625
|
}
|
|
@@ -12361,10 +12424,19 @@ var analyticsLandingRowSchema = z24.object({
|
|
|
12361
12424
|
*
|
|
12362
12425
|
* Derived from the click ids a visit arrived with, never from `utm_source`:
|
|
12363
12426
|
* a label is whatever somebody typed into a tracking template, a `gclid` is a
|
|
12364
|
-
* click.
|
|
12365
|
-
* page as advertised.
|
|
12427
|
+
* click. Ordered by traffic, busiest first.
|
|
12366
12428
|
*/
|
|
12367
|
-
channels: z24.array(z24.string())
|
|
12429
|
+
channels: z24.array(z24.string()),
|
|
12430
|
+
/**
|
|
12431
|
+
* Visits per entry of {@link channels}, in the same order.
|
|
12432
|
+
*
|
|
12433
|
+
* The half that makes a mark readable. A mark on its own says "advertised
|
|
12434
|
+
* here", which is why it used to be withheld until three visits had arrived —
|
|
12435
|
+
* and withholding it is what made a page whose whole day was five Meta clicks
|
|
12436
|
+
* show nothing at all. The count says how much, so one test click and a live
|
|
12437
|
+
* campaign are distinguishable by reading rather than by absence.
|
|
12438
|
+
*/
|
|
12439
|
+
channelSessions: z24.array(z24.number().int().nonnegative())
|
|
12368
12440
|
});
|
|
12369
12441
|
var analyticsTagRowSchema = z24.object({
|
|
12370
12442
|
tag: z24.string(),
|
|
@@ -12923,7 +12995,14 @@ var analyticsVisitProfileSchema = z24.object({
|
|
|
12923
12995
|
utmSource: z24.string(),
|
|
12924
12996
|
utmMedium: z24.string(),
|
|
12925
12997
|
utmCampaign: z24.string(),
|
|
12926
|
-
/**
|
|
12998
|
+
/**
|
|
12999
|
+
* Which platform the click id proves, in the `AD_PLATFORMS` vocabulary —
|
|
13000
|
+
* `google` | `meta` | `tiktok` | `x` | `linkedin` | `microsoft` | `""`.
|
|
13001
|
+
*
|
|
13002
|
+
* The same spelling every mark, mapping and report uses. It briefly said
|
|
13003
|
+
* `google_ads` / `meta_ads` here alone, which meant every logo lookup on the
|
|
13004
|
+
* People screen missed and a paid visit drew a neutral globe.
|
|
13005
|
+
*/
|
|
12927
13006
|
clickPlatform: z24.string(),
|
|
12928
13007
|
/** Every ValueTrack-style parameter the URL carried, unresolved. */
|
|
12929
13008
|
campaignParams: z24.record(z24.string(), z24.string()),
|
|
@@ -23060,7 +23139,22 @@ function shapeHints(data) {
|
|
|
23060
23139
|
}
|
|
23061
23140
|
return hints;
|
|
23062
23141
|
}
|
|
23063
|
-
function
|
|
23142
|
+
function adHierarchyHints(data, platform) {
|
|
23143
|
+
if (platform === void 0) return [];
|
|
23144
|
+
const traffic = (data.adPlatformTraffic ?? []).find((entry) => entry.platform === platform);
|
|
23145
|
+
if (traffic === void 0 || traffic.sessions < MIN_SESSIONS_FOR_TAGGING_CLAIM) return [];
|
|
23146
|
+
if (traffic.taggedSessions === 0) return [];
|
|
23147
|
+
const carried = new Set(
|
|
23148
|
+
(data.adParams ?? []).filter((row) => row.role !== void 0 && row.awaitingTraffic !== true).map((row) => row.role)
|
|
23149
|
+
);
|
|
23150
|
+
const missing = essentialAdDimensions(platform).filter((entry) => !carried.has(entry.role));
|
|
23151
|
+
if (missing.length === 0) return [];
|
|
23152
|
+
const suffix = missing.map((entry) => `${entry.names[0] ?? entry.role}=${entry.macro}`).join("&");
|
|
23153
|
+
return [
|
|
23154
|
+
`${platform} traffic carries no ${missing.map((entry) => AD_PARAM_ROLE_SPECS[entry.role].label.toLowerCase()).join(", ")}, so no report can say which ad sent a visit \u2014 only which platform. Add \`${suffix}\` to that account's tracking template. This cannot be backfilled: a parameter's values are only stored once it starts arriving.`
|
|
23155
|
+
];
|
|
23156
|
+
}
|
|
23157
|
+
function buildAnalyticsHints(data, platform) {
|
|
23064
23158
|
const hints = [];
|
|
23065
23159
|
for (const funnel of data.funnels ?? []) {
|
|
23066
23160
|
const worst = funnel.worstStep;
|
|
@@ -23086,6 +23180,7 @@ function buildAnalyticsHints(data) {
|
|
|
23086
23180
|
);
|
|
23087
23181
|
}
|
|
23088
23182
|
hints.push(...shapeHints(data));
|
|
23183
|
+
hints.push(...adHierarchyHints(data, platform));
|
|
23089
23184
|
if ((data.topCampaigns ?? []).length === 0 && (data.topSources ?? []).length > 0) {
|
|
23090
23185
|
hints.push(
|
|
23091
23186
|
"No campaign tagging on any traffic \u2014 ad clicks cannot be attributed to a campaign until the destination URLs carry utm parameters."
|
|
@@ -23514,6 +23609,8 @@ function requestBody(args, options) {
|
|
|
23514
23609
|
async function runPreset(args, options) {
|
|
23515
23610
|
try {
|
|
23516
23611
|
const response = await apiPost("/api/analytics/query", requestBody(args, options));
|
|
23612
|
+
const requestedPlatform = args.platform === void 0 ? "" : String(args.platform);
|
|
23613
|
+
const adPlatformScope = isAdPlatform(requestedPlatform) ? requestedPlatform : void 0;
|
|
23517
23614
|
const format = args.output === void 0 ? "json" : String(args.output);
|
|
23518
23615
|
if (!isAnalyticsOutputFormat(format)) {
|
|
23519
23616
|
writeJsonEnvelope({
|
|
@@ -23527,7 +23624,11 @@ async function runPreset(args, options) {
|
|
|
23527
23624
|
}
|
|
23528
23625
|
const data = pruneForAgent(response.data, args.full === true);
|
|
23529
23626
|
const hints = [
|
|
23530
|
-
|
|
23627
|
+
// The platform the ad blocks were scoped to, so the tagging hint can
|
|
23628
|
+
// only speak about the inventory it was actually given. `adParams` is one
|
|
23629
|
+
// platform's list; a hint drawn from it without knowing which platform
|
|
23630
|
+
// would be a claim about Meta made from Google's URLs.
|
|
23631
|
+
...buildAnalyticsHints(response.data, adPlatformScope),
|
|
23531
23632
|
...missingBreakdownHint(response.data, {
|
|
23532
23633
|
eventName: options.eventName,
|
|
23533
23634
|
property: options.property
|