@koda-sl/baker-cli 0.344.0-dev.9890d9fcc → 0.344.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
@@ -67,7 +67,7 @@ import {
67
67
  validateCanvasDeep,
68
68
  withoutCastName,
69
69
  ytDlpBlockSignal
70
- } from "./chunk-D5P4COPD.js";
70
+ } from "./chunk-FFOURYP5.js";
71
71
  import {
72
72
  csvOrJson,
73
73
  daysAgoIso,
@@ -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: `Do not abort \u2014 infer the change from performance instead. Query monthly metrics per campaign (${monthlySpendTrendQuery()}) 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.`
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: `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: ${monthlySpendTrendQuery()} 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.`
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 BETWEEN_RE = /^BETWEEN\s+'[0-9]{4}-[0-9]{2}-[0-9]{2}'\s+AND\s+'[0-9]{4}-[0-9]{2}-[0-9]{2}'$/i;
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
- const trimmed = value.trim();
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, now = /* @__PURE__ */ new Date()) {
12460
+ function expandPreset(preset, params) {
12534
12461
  const dateRange = params.dateRange ?? preset.defaultDateRange;
12535
12462
  const limit = params.limit ?? preset.defaultLimit;
12536
- if (preset.gaqlTemplate.includes("{dateRange}") && !isValidDateRange(dateRange)) {
12537
- return { query: "", dateRangeError: dateRangeError(dateRange, now) };
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 range = dateRange.trim();
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: a Google date literal (TODAY, YESTERDAY, LAST_7_DAYS, LAST_14_DAYS, LAST_30_DAYS, THIS_MONTH, LAST_MONTH \u2026) or BETWEEN 'YYYY-MM-DD' AND 'YYYY-MM-DD'. Google has no LAST_90_DAYS \u2014 use BETWEEN for any other window.",
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);