@koda-sl/baker-cli 0.241.0 → 0.243.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/dist/cli.js CHANGED
@@ -2122,6 +2122,18 @@ var BIDDING_STRATEGY_TYPES = [
2122
2122
  "MANUAL_CPV",
2123
2123
  "PERCENT_CPC"
2124
2124
  ];
2125
+ var MERGED_TARGET_STRATEGY_CHANNELS = /* @__PURE__ */ new Set(["SEARCH"]);
2126
+ var MERGED_TARGET_CPA_MESSAGE = "Google doesn't accept Target CPA as a bidding strategy of its own on a Search campaign \u2014 the target sits inside Maximize conversions. Choose Maximize conversions and give it the same target CPA; it bids exactly the same.";
2127
+ var MERGED_TARGET_ROAS_MESSAGE = "Google doesn't accept Target ROAS as a bidding strategy of its own on a Search campaign \u2014 the target sits inside Maximize conversion value. Choose Maximize conversion value and give it the same target ROAS; it bids exactly the same.";
2128
+ function mergedTargetStrategyRefusal(channelType, biddingType) {
2129
+ if (channelType === void 0 || !MERGED_TARGET_STRATEGY_CHANNELS.has(channelType)) {
2130
+ return void 0;
2131
+ }
2132
+ if (biddingType === "TARGET_CPA") {
2133
+ return MERGED_TARGET_CPA_MESSAGE;
2134
+ }
2135
+ return biddingType === "TARGET_ROAS" ? MERGED_TARGET_ROAS_MESSAGE : void 0;
2136
+ }
2125
2137
  var POSITIVE_GEO_TARGET_TYPES = ["PRESENCE_OR_INTEREST", "PRESENCE"];
2126
2138
  var NEGATIVE_GEO_TARGET_TYPES = ["PRESENCE_OR_INTEREST", "PRESENCE"];
2127
2139
  var BUDGET_DELIVERY_METHODS = ["STANDARD", "ACCELERATED"];
@@ -2846,6 +2858,10 @@ var campaignCreateSchema2 = z4.strictObject({
2846
2858
  if (p.channelType === "PERFORMANCE_MAX" && p.bidding && p.bidding.type === "MANUAL_CPC") {
2847
2859
  ctx.addIssue({ code: "custom", path: ["bidding"], message: "Performance Max does not support Manual CPC" });
2848
2860
  }
2861
+ const mergedTarget = mergedTargetStrategyRefusal(p.channelType, p.bidding?.type);
2862
+ if (mergedTarget) {
2863
+ ctx.addIssue({ code: "custom", path: ["bidding"], message: mergedTarget });
2864
+ }
2849
2865
  if (p.startDate && p.endDate && p.endDate <= p.startDate) {
2850
2866
  ctx.addIssue({ code: "custom", path: ["endDate"], message: "endDate must be after startDate" });
2851
2867
  }
@@ -12529,6 +12545,20 @@ var analyticsCampaignRowSchema = z24.object({
12529
12545
  utmSource: z24.string(),
12530
12546
  utmMedium: z24.string(),
12531
12547
  utmCampaign: z24.string(),
12548
+ /**
12549
+ * The platform a click id proves, or `""` when the visit carried none.
12550
+ *
12551
+ * What separates a campaign the ad breakdown already reports — with its
12552
+ * platform proven and its ad set and ad underneath — from one nothing else
12553
+ * can show: the newsletter, the partner link, the hand-tagged post. The two
12554
+ * are otherwise identical, because `utm_campaign={campaignid}` is the normal
12555
+ * tagging convention and produces the same ids in both places.
12556
+ *
12557
+ * Kept on the row rather than filtered out here: a CLI hint reads an empty
12558
+ * campaign list as "no campaign tagging on any traffic", and dropping the
12559
+ * paid ones would make that fire on a perfectly tagged account.
12560
+ */
12561
+ clickPlatform: z24.string(),
12532
12562
  sessions: z24.number().int().nonnegative(),
12533
12563
  conversions: z24.number().int().nonnegative(),
12534
12564
  conversionRate: z24.number().min(0).max(1).nullable()
@@ -13241,7 +13271,8 @@ var analyticsTrackingPlatformSchema = z24.object({
13241
13271
  /** The pairs to append to the account's tagging, or `""`. */
13242
13272
  suffix: z24.string(),
13243
13273
  whereItGoes: z24.string(),
13244
- fix: z24.string().optional()
13274
+ /** Which level to put them at, who can write it, and how an existing value is treated. */
13275
+ fix: z24.string()
13245
13276
  });
13246
13277
  var analyticsTrackingResponseSchema = z24.object({
13247
13278
  ok: z24.literal(true),
@@ -13613,10 +13644,16 @@ var campaignsCommand = defineCommand30({
13613
13644
  "budget-ref": { type: "string", description: "Budget ref (g_temp_*, id, or resource name)" },
13614
13645
  "bidding-strategy": {
13615
13646
  type: "string",
13616
- description: "MANUAL_CPC | MAXIMIZE_CONVERSIONS | TARGET_CPA | TARGET_ROAS | \u2026"
13647
+ description: "MANUAL_CPC | MAXIMIZE_CONVERSIONS | MAXIMIZE_CONVERSION_VALUE | TARGET_SPEND | \u2026"
13648
+ },
13649
+ "target-cpa": {
13650
+ type: "string",
13651
+ description: "Target CPA (major units). On a Search campaign pair it with --bidding-strategy MAXIMIZE_CONVERSIONS \u2014 Google keeps the target inside that strategy and rejects a standalone TARGET_CPA."
13652
+ },
13653
+ "target-roas": {
13654
+ type: "string",
13655
+ description: "Target ROAS (e.g. 4.0). On a Search campaign pair it with --bidding-strategy MAXIMIZE_CONVERSION_VALUE \u2014 Google keeps the target inside that strategy and rejects a standalone TARGET_ROAS."
13617
13656
  },
13618
- "target-cpa": { type: "string", description: "Target CPA (major units) for TARGET_CPA" },
13619
- "target-roas": { type: "string", description: "Target ROAS (e.g. 4.0) for TARGET_ROAS" },
13620
13657
  ...maxCpcArg,
13621
13658
  ...finalUrlSuffixArg,
13622
13659
  ...urlOptionsArgs,
@@ -13663,9 +13700,18 @@ var campaignsCommand = defineCommand30({
13663
13700
  ...fileArg,
13664
13701
  name: { type: "string" },
13665
13702
  "budget-ref": { type: "string" },
13666
- "bidding-strategy": { type: "string" },
13667
- "target-cpa": { type: "string" },
13668
- "target-roas": { type: "string" },
13703
+ "bidding-strategy": {
13704
+ type: "string",
13705
+ description: "MANUAL_CPC | MAXIMIZE_CONVERSIONS | MAXIMIZE_CONVERSION_VALUE | TARGET_SPEND | \u2026"
13706
+ },
13707
+ "target-cpa": {
13708
+ type: "string",
13709
+ description: "Target CPA (major units). On a Search campaign pair it with --bidding-strategy MAXIMIZE_CONVERSIONS \u2014 Google keeps the target inside that strategy and rejects a standalone TARGET_CPA."
13710
+ },
13711
+ "target-roas": {
13712
+ type: "string",
13713
+ description: "Target ROAS (e.g. 4.0). On a Search campaign pair it with --bidding-strategy MAXIMIZE_CONVERSION_VALUE \u2014 Google keeps the target inside that strategy and rejects a standalone TARGET_ROAS."
13714
+ },
13669
13715
  ...maxCpcArg,
13670
13716
  ...finalUrlSuffixArg,
13671
13717
  ...urlOptionsArgs,
@@ -23296,8 +23342,18 @@ function buildAnalyticsHints(data, platform) {
23296
23342
  "No campaign tagging on any traffic \u2014 ad clicks cannot be attributed to a campaign until the destination URLs carry utm parameters."
23297
23343
  );
23298
23344
  }
23345
+ hints.push(...campaignOriginHints(data.topCampaigns));
23299
23346
  return hints;
23300
23347
  }
23348
+ function campaignOriginHints(rows) {
23349
+ const campaigns = rows ?? [];
23350
+ const fromAds = campaigns.filter((row) => row.clickPlatform !== "");
23351
+ if (fromAds.length === 0) return [];
23352
+ const own = campaigns.length - fromAds.length;
23353
+ return [
23354
+ `${fromAds.length} of these campaigns arrived on an ad click (\`clickPlatform\` is set). \`baker analytics ads --role campaign_id\` MAY report the same campaigns under the platform's own ids \u2014 check before you quote both, or you will count one campaign twice. It may equally report nothing: \`ads\` only reads parameters the account tags separately, and \`utm_campaign\` is reserved and never reaches it, so an account whose only campaign signal is \`utm_campaign\` has its campaigns HERE and nowhere else. Never report "no campaigns" on the strength of an empty \`ads\`. ${own === 0 ? "Every row here came from an ad." : `The other ${own} carry no click id at all \u2014 a newsletter, a partner link, a tagged post \u2014 and only this report can show them.`}`
23355
+ ];
23356
+ }
23301
23357
 
23302
23358
  // src/commands/analytics/presets.ts
23303
23359
  var ANALYTICS_PRESET_INFO = [
@@ -24019,7 +24075,7 @@ var trackingCommand = (() => {
24019
24075
  Read this before auditing an ad account and before answering any "which ad performed best" question. Each dimension comes back in one of four states, and they need different things:
24020
24076
  covered \u2014 arriving and understood, nothing to do
24021
24077
  unmapped \u2014 the URLs carry it under a name Baker does not read yet. Free to fix, and it repairs the traffic already collected: run the \`mapCommand\` in the response
24022
- missing \u2014 nothing carries it. Add \`suffix\` to the account's tagging (\`whereItGoes\` says where, \`fix\` says whether Baker can stage it). This one cannot be filled in later \u2014 a parameter's values are only stored once it starts arriving
24078
+ missing \u2014 nothing carries it. \`fix\` says which LEVELS that platform accepts tagging at (widest first \u2014 prefer it), which Baker can stage and which the user must do by hand, and how each field treats what is already in it. \`suffix\` is only the MISSING pairs, so read the current value, add them, write the union. This one cannot be filled in later \u2014 a parameter's values are only stored once it starts arriving
24023
24079
  unknown \u2014 no visit arrived AND the account could not be read. Not a finding; do not report it as untagged
24024
24080
 
24025
24081
  Baker reads the client's own Google tracking templates and Meta URL parameters, so a platform that has not run yet still gets a real answer.