@koda-sl/baker-cli 0.236.0 → 0.238.0-dev.7fb8e0c69

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
@@ -58,7 +58,7 @@ import {
58
58
  ulid,
59
59
  validateCanvasDeep,
60
60
  ytDlpBlockSignal
61
- } from "./chunk-EKLAHWSF.js";
61
+ } from "./chunk-CMPAHYLB.js";
62
62
  import {
63
63
  csvOrJson,
64
64
  daysAgoIso,
@@ -4461,6 +4461,24 @@ var FLOW_SIDE_EFFECT_CATALOG = [
4461
4461
  gotcha: "`eventTagId` comes from X Events Manager and is passed verbatim. `eventName` is an optional label only.",
4462
4462
  reference: TAGS_REF
4463
4463
  },
4464
+ {
4465
+ kind: "side-effect",
4466
+ type: "microsoftAds",
4467
+ summary: "Fires a Microsoft Ads (UET) conversion event.",
4468
+ skeleton: {
4469
+ ...SIDE_EFFECT_ENVELOPE,
4470
+ type: "microsoftAds",
4471
+ tagIds: TAG_REF_SLOT,
4472
+ eventName: "<UET event action, e.g. lead_submitted>",
4473
+ userMatching: matchingSlots("em", "ph"),
4474
+ value: null,
4475
+ currency: null,
4476
+ transactionId: null
4477
+ },
4478
+ owners: { tagIds: "tags" },
4479
+ gotcha: "`eventName` is the UET *event action*, and it only counts as a conversion once someone creates a matching custom-event conversion goal in Microsoft Advertising \u2014 the tag alone records nothing. `page_view` is refused outright (UET reserves it for SPA navigation), so pick any other action. `eventCategory` and `eventLabel` are optional plain strings (not mapped slots) \u2014 add them only when the goal filters on them. `userMatching` needs Enhanced conversions turned on for that goal, and `ph` must carry a country code (`+34\u2026`/`0034\u2026`) \u2014 map it from a phone field with the country picker, not a plain text field, or it is dropped rather than guessed at.",
4480
+ reference: TAGS_REF
4481
+ },
4464
4482
  eventSideEffect("posthog", "Captures a PostHog event.", { eventParams: [] }),
4465
4483
  {
4466
4484
  kind: "side-effect",
@@ -5902,7 +5920,8 @@ var TAG_TYPES = [
5902
5920
  "posthog",
5903
5921
  "datafast",
5904
5922
  "recaptcha",
5905
- "twitterAds"
5923
+ "twitterAds",
5924
+ "microsoftAds"
5906
5925
  ];
5907
5926
  var tagTypeSchema = z19.enum(TAG_TYPES);
5908
5927
  var TAG_IDENTIFYING_FIELD = {
@@ -5923,7 +5942,8 @@ var TAG_IDENTIFYING_FIELD = {
5923
5942
  posthog: "projectToken",
5924
5943
  datafast: "websiteId",
5925
5944
  recaptcha: "siteKey",
5926
- twitterAds: "pixelId"
5945
+ twitterAds: "pixelId",
5946
+ microsoftAds: "tagId"
5927
5947
  };
5928
5948
  var tagDraftOpKindSchema = z19.enum(["create", "update", "delete"]);
5929
5949
  var tagDraftOpViewSchema = z19.object({
@@ -11347,6 +11367,62 @@ var AD_PARAM_ROLES = [
11347
11367
  "other",
11348
11368
  "ignore"
11349
11369
  ];
11370
+ var AD_PARAM_ROLE_SPECS = {
11371
+ campaign_id: { role: "campaign_id", label: "Campaign ID", description: "The top level of this account's structure." },
11372
+ campaign_name: { role: "campaign_name", label: "Campaign name", description: "The campaign's name." },
11373
+ adgroup_id: { role: "adgroup_id", label: "Ad group ID", description: "The targeting container inside a campaign." },
11374
+ adgroup_name: { role: "adgroup_name", label: "Ad group name", description: "The ad group's name." },
11375
+ ad_id: { role: "ad_id", label: "Ad ID", description: "The individual ad that was clicked." },
11376
+ ad_name: { role: "ad_name", label: "Ad name", description: "The individual ad's name." },
11377
+ keyword: {
11378
+ role: "keyword",
11379
+ label: "Keyword",
11380
+ description: "The keyword that matched, as it is written in the account \u2014 not what the visitor typed."
11381
+ },
11382
+ match_type: {
11383
+ role: "match_type",
11384
+ label: "Match type",
11385
+ description: "How loosely the keyword matched: broad, phrase or exact."
11386
+ },
11387
+ search_query: { role: "search_query", label: "Search query", description: "What the visitor actually typed." },
11388
+ audience: {
11389
+ role: "audience",
11390
+ label: "Audience",
11391
+ description: "The remarketing list or interest segment the visitor was in."
11392
+ },
11393
+ placement: {
11394
+ role: "placement",
11395
+ label: "Placement",
11396
+ description: "Where the ad appeared \u2014 a feed, a story, a site on the display network."
11397
+ },
11398
+ network: {
11399
+ role: "network",
11400
+ label: "Network",
11401
+ description: "Which network served it: search, a search partner, display, or a video surface."
11402
+ },
11403
+ site_source: {
11404
+ role: "site_source",
11405
+ label: "App",
11406
+ description: "Which app the click came from, on platforms that span several."
11407
+ },
11408
+ device: { role: "device", label: "Device", description: "Mobile, tablet or desktop, as the platform saw it." },
11409
+ product_id: { role: "product_id", label: "Product ID", description: "The shopping item that was clicked." },
11410
+ account_name: {
11411
+ role: "account_name",
11412
+ label: "Account name",
11413
+ description: "Which ad account paid for the click, where an agency runs several."
11414
+ },
11415
+ other: {
11416
+ role: "other",
11417
+ label: "Something else",
11418
+ description: "Keep the values and let me filter by this name, without saying what it means."
11419
+ },
11420
+ ignore: {
11421
+ role: "ignore",
11422
+ label: "Ignore it",
11423
+ description: "Not campaign information. Baker stops storing it and stops asking about it."
11424
+ }
11425
+ };
11350
11426
  var RESERVED_PARAM_NAMES = [
11351
11427
  "utm_source",
11352
11428
  "utm_medium",
@@ -11365,6 +11441,9 @@ var AD_PLATFORMS = [
11365
11441
  "pinterest",
11366
11442
  "snapchat"
11367
11443
  ];
11444
+ function isAdPlatform(value) {
11445
+ return AD_PLATFORMS.includes(value);
11446
+ }
11368
11447
  var GOOGLE = {
11369
11448
  platform: "google",
11370
11449
  label: "Google Ads",
@@ -11557,6 +11636,10 @@ var AD_PLATFORM_DEFINITIONS = {
11557
11636
  pinterest: PINTEREST,
11558
11637
  snapchat: SNAPCHAT
11559
11638
  };
11639
+ var ESSENTIAL_ROLES = ["campaign_id", "adgroup_id", "ad_id", "keyword", "match_type"];
11640
+ function essentialAdDimensions(platform) {
11641
+ return AD_PLATFORM_DEFINITIONS[platform].dimensions.filter((entry) => ESSENTIAL_ROLES.includes(entry.role));
11642
+ }
11560
11643
  function normalizeAdParamName(raw) {
11561
11644
  return raw.trim().toLowerCase().slice(0, 64);
11562
11645
  }
@@ -12361,10 +12444,19 @@ var analyticsLandingRowSchema = z24.object({
12361
12444
  *
12362
12445
  * Derived from the click ids a visit arrived with, never from `utm_source`:
12363
12446
  * a label is whatever somebody typed into a tracking template, a `gclid` is a
12364
- * click. Thresholded, so one stray click from a test URL does not describe a
12365
- * page as advertised.
12447
+ * click. Ordered by traffic, busiest first.
12366
12448
  */
12367
- channels: z24.array(z24.string())
12449
+ channels: z24.array(z24.string()),
12450
+ /**
12451
+ * Visits per entry of {@link channels}, in the same order.
12452
+ *
12453
+ * The half that makes a mark readable. A mark on its own says "advertised
12454
+ * here", which is why it used to be withheld until three visits had arrived —
12455
+ * and withholding it is what made a page whose whole day was five Meta clicks
12456
+ * show nothing at all. The count says how much, so one test click and a live
12457
+ * campaign are distinguishable by reading rather than by absence.
12458
+ */
12459
+ channelSessions: z24.array(z24.number().int().nonnegative())
12368
12460
  });
12369
12461
  var analyticsTagRowSchema = z24.object({
12370
12462
  tag: z24.string(),
@@ -12923,7 +13015,14 @@ var analyticsVisitProfileSchema = z24.object({
12923
13015
  utmSource: z24.string(),
12924
13016
  utmMedium: z24.string(),
12925
13017
  utmCampaign: z24.string(),
12926
- /** Whichever click id the visit arrived with, and which platform it belongs to. */
13018
+ /**
13019
+ * Which platform the click id proves, in the `AD_PLATFORMS` vocabulary —
13020
+ * `google` | `meta` | `tiktok` | `x` | `linkedin` | `microsoft` | `""`.
13021
+ *
13022
+ * The same spelling every mark, mapping and report uses. It briefly said
13023
+ * `google_ads` / `meta_ads` here alone, which meant every logo lookup on the
13024
+ * People screen missed and a paid visit drew a neutral globe.
13025
+ */
12927
13026
  clickPlatform: z24.string(),
12928
13027
  /** Every ValueTrack-style parameter the URL carried, unresolved. */
12929
13028
  campaignParams: z24.record(z24.string(), z24.string()),
@@ -23060,7 +23159,22 @@ function shapeHints(data) {
23060
23159
  }
23061
23160
  return hints;
23062
23161
  }
23063
- function buildAnalyticsHints(data) {
23162
+ function adHierarchyHints(data, platform) {
23163
+ if (platform === void 0) return [];
23164
+ const traffic = (data.adPlatformTraffic ?? []).find((entry) => entry.platform === platform);
23165
+ if (traffic === void 0 || traffic.sessions < MIN_SESSIONS_FOR_TAGGING_CLAIM) return [];
23166
+ if (traffic.taggedSessions === 0) return [];
23167
+ const carried = new Set(
23168
+ (data.adParams ?? []).filter((row) => row.role !== void 0 && row.awaitingTraffic !== true).map((row) => row.role)
23169
+ );
23170
+ const missing = essentialAdDimensions(platform).filter((entry) => !carried.has(entry.role));
23171
+ if (missing.length === 0) return [];
23172
+ const suffix = missing.map((entry) => `${entry.names[0] ?? entry.role}=${entry.macro}`).join("&");
23173
+ return [
23174
+ `${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.`
23175
+ ];
23176
+ }
23177
+ function buildAnalyticsHints(data, platform) {
23064
23178
  const hints = [];
23065
23179
  for (const funnel of data.funnels ?? []) {
23066
23180
  const worst = funnel.worstStep;
@@ -23086,6 +23200,7 @@ function buildAnalyticsHints(data) {
23086
23200
  );
23087
23201
  }
23088
23202
  hints.push(...shapeHints(data));
23203
+ hints.push(...adHierarchyHints(data, platform));
23089
23204
  if ((data.topCampaigns ?? []).length === 0 && (data.topSources ?? []).length > 0) {
23090
23205
  hints.push(
23091
23206
  "No campaign tagging on any traffic \u2014 ad clicks cannot be attributed to a campaign until the destination URLs carry utm parameters."
@@ -23514,6 +23629,8 @@ function requestBody(args, options) {
23514
23629
  async function runPreset(args, options) {
23515
23630
  try {
23516
23631
  const response = await apiPost("/api/analytics/query", requestBody(args, options));
23632
+ const requestedPlatform = args.platform === void 0 ? "" : String(args.platform);
23633
+ const adPlatformScope = isAdPlatform(requestedPlatform) ? requestedPlatform : void 0;
23517
23634
  const format = args.output === void 0 ? "json" : String(args.output);
23518
23635
  if (!isAnalyticsOutputFormat(format)) {
23519
23636
  writeJsonEnvelope({
@@ -23527,7 +23644,11 @@ async function runPreset(args, options) {
23527
23644
  }
23528
23645
  const data = pruneForAgent(response.data, args.full === true);
23529
23646
  const hints = [
23530
- ...buildAnalyticsHints(response.data),
23647
+ // The platform the ad blocks were scoped to, so the tagging hint can
23648
+ // only speak about the inventory it was actually given. `adParams` is one
23649
+ // platform's list; a hint drawn from it without knowing which platform
23650
+ // would be a claim about Meta made from Google's URLs.
23651
+ ...buildAnalyticsHints(response.data, adPlatformScope),
23531
23652
  ...missingBreakdownHint(response.data, {
23532
23653
  eventName: options.eventName,
23533
23654
  property: options.property
@@ -33719,6 +33840,10 @@ var MAPPING_SLOTS = {
33719
33840
  twitterAds: [
33720
33841
  { kind: "record", key: "userMatching", allowed: ["em", "ph"] },
33721
33842
  { kind: "scalar", keys: ["value", "currency"] }
33843
+ ],
33844
+ microsoftAds: [
33845
+ { kind: "record", key: "userMatching", allowed: ["em", "ph"] },
33846
+ { kind: "scalar", keys: ["value", "currency", "transactionId"] }
33722
33847
  ]
33723
33848
  };
33724
33849
  var UNMAPPABLE = {