@koda-sl/baker-cli 0.343.0 → 0.344.0-dev.9890d9fcc
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 +4 -2
- package/canvas/end-card-composition/meta.json +5 -1
- package/canvas/feature-reveal-composition/meta.json +12 -2
- package/canvas/lower-third-composition/meta.json +7 -1
- package/canvas/video-call-composition/meta.json +27 -5
- package/dist/{chunk-FFOURYP5.js → chunk-D5P4COPD.js} +4 -4
- package/dist/chunk-D5P4COPD.js.map +1 -0
- package/dist/cli.js +135 -14
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-FFOURYP5.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
validateCanvasDeep,
|
|
68
68
|
withoutCastName,
|
|
69
69
|
ytDlpBlockSignal
|
|
70
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-D5P4COPD.js";
|
|
71
71
|
import {
|
|
72
72
|
csvOrJson,
|
|
73
73
|
daysAgoIso,
|
|
@@ -2362,6 +2362,7 @@ var GOOGLE_ADS_LIMITS = {
|
|
|
2362
2362
|
businessNameMax: 25
|
|
2363
2363
|
}
|
|
2364
2364
|
};
|
|
2365
|
+
var GOOGLE_ADS_QUERY_MAX_PAGE_SIZE = 1e4;
|
|
2365
2366
|
var KEYWORD_MATCH_TYPES = ["EXACT", "PHRASE", "BROAD"];
|
|
2366
2367
|
var ADVERTISING_CHANNEL_TYPES = [
|
|
2367
2368
|
"SEARCH",
|
|
@@ -10079,7 +10080,72 @@ function urlOptionsHints(report) {
|
|
|
10079
10080
|
return hints2;
|
|
10080
10081
|
}
|
|
10081
10082
|
|
|
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
|
+
|
|
10082
10145
|
// 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
|
+
}
|
|
10083
10149
|
function isChangeScope(value) {
|
|
10084
10150
|
return CHANGE_SCOPES.includes(value);
|
|
10085
10151
|
}
|
|
@@ -10133,7 +10199,7 @@ function lookbackTooLong(scope, days, max) {
|
|
|
10133
10199
|
message: `Google's account history API reaches back ${CHANGE_SCOPE_MAX_DAYS.summary} days at most; ${days} days is outside that window.`,
|
|
10134
10200
|
fix: {
|
|
10135
10201
|
action: "use_different_resource",
|
|
10136
|
-
explanation:
|
|
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.`
|
|
10137
10203
|
}
|
|
10138
10204
|
};
|
|
10139
10205
|
}
|
|
@@ -10492,7 +10558,7 @@ var CORRECTION_RULES = [
|
|
|
10492
10558
|
matchApiError: /changeStatusError\W{0,4}START_DATE_TOO_OLD/i,
|
|
10493
10559
|
fix: () => ({
|
|
10494
10560
|
action: "use_different_resource",
|
|
10495
|
-
explanation:
|
|
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.`
|
|
10496
10562
|
})
|
|
10497
10563
|
},
|
|
10498
10564
|
// 18c. change_status rejects a date range it cannot bound. Google raises this both for
|
|
@@ -12319,9 +12385,16 @@ import { resolve } from "path";
|
|
|
12319
12385
|
import { defineCommand as defineCommand28 } from "citty";
|
|
12320
12386
|
|
|
12321
12387
|
// src/commands/ads/google/presets.ts
|
|
12322
|
-
var
|
|
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;
|
|
12323
12389
|
function isValidDateRange(value) {
|
|
12324
|
-
|
|
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'.`;
|
|
12325
12398
|
}
|
|
12326
12399
|
var SERVING_CHAINS = {
|
|
12327
12400
|
keyword_view: ["campaign.status", "ad_group.status", "ad_group_criterion.status"],
|
|
@@ -12457,17 +12530,15 @@ function buildStatusScope(statusFields, includePaused) {
|
|
|
12457
12530
|
const predicate = includePaused ? "!= 'REMOVED'" : "= 'ENABLED'";
|
|
12458
12531
|
return statusFields.map((field) => ` AND ${field} ${predicate}`).join("");
|
|
12459
12532
|
}
|
|
12460
|
-
function expandPreset(preset, params) {
|
|
12533
|
+
function expandPreset(preset, params, now = /* @__PURE__ */ new Date()) {
|
|
12461
12534
|
const dateRange = params.dateRange ?? preset.defaultDateRange;
|
|
12462
12535
|
const limit = params.limit ?? preset.defaultLimit;
|
|
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
|
-
};
|
|
12536
|
+
if (preset.gaqlTemplate.includes("{dateRange}") && !isValidDateRange(dateRange)) {
|
|
12537
|
+
return { query: "", dateRangeError: dateRangeError(dateRange, now) };
|
|
12468
12538
|
}
|
|
12469
12539
|
const statusScope = buildStatusScope(preset.statusFields, params.includePaused ?? false);
|
|
12470
|
-
const
|
|
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);
|
|
12471
12542
|
return { query };
|
|
12472
12543
|
}
|
|
12473
12544
|
|
|
@@ -12626,6 +12697,28 @@ function applyAutoFixes(query, limit) {
|
|
|
12626
12697
|
}
|
|
12627
12698
|
return { query: corrected, warnings };
|
|
12628
12699
|
}
|
|
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
|
+
}
|
|
12629
12722
|
function validatePreflight(query, customerId, limit) {
|
|
12630
12723
|
const warnings = [];
|
|
12631
12724
|
const corrected = query;
|
|
@@ -12697,6 +12790,8 @@ function validatePreflight(query, customerId, limit) {
|
|
|
12697
12790
|
}
|
|
12698
12791
|
};
|
|
12699
12792
|
}
|
|
12793
|
+
const duringRejection = rejectUnsupportedDuring(corrected, customerId);
|
|
12794
|
+
if (duringRejection) return { valid: false, warnings: [], error: duringRejection };
|
|
12700
12795
|
if (/\bsegments\.conversion_action_name\b/i.test(corrected) && /\bmetrics\.cost_micros\b/i.test(corrected)) {
|
|
12701
12796
|
return {
|
|
12702
12797
|
valid: false,
|
|
@@ -12905,7 +13000,7 @@ registerSchema({
|
|
|
12905
13000
|
},
|
|
12906
13001
|
"date-range": {
|
|
12907
13002
|
type: "string",
|
|
12908
|
-
description: "Date range for presets (LAST_7_DAYS, LAST_30_DAYS,
|
|
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.",
|
|
12909
13004
|
required: false
|
|
12910
13005
|
},
|
|
12911
13006
|
"include-paused": {
|
|
@@ -13344,9 +13439,35 @@ async function executeQuery(finalQuery, args5, customerId, limit, warnings, useC
|
|
|
13344
13439
|
function shouldReadCache(args5) {
|
|
13345
13440
|
return !args5["no-cache"] && !args5.out;
|
|
13346
13441
|
}
|
|
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
|
+
}
|
|
13347
13463
|
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;
|
|
13348
13470
|
const customerId = await resolveCustomerId(args5);
|
|
13349
|
-
const limit = args5.limit ? Number(args5.limit) : 200;
|
|
13350
13471
|
const gaql = resolveGaql(args5, limit);
|
|
13351
13472
|
if (!gaql) {
|
|
13352
13473
|
process.exit(1);
|