@koda-sl/baker-cli 0.5.1 → 0.11.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 +718 -162
- package/dist/cli.js +7 -3
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +7 -1
- package/dist/client.js.map +1 -1
- package/dist/commands/ads/cache.d.ts +7 -0
- package/dist/commands/ads/cache.d.ts.map +1 -0
- package/dist/commands/ads/cache.js +73 -0
- package/dist/commands/ads/cache.js.map +1 -0
- package/dist/commands/ads/cache.test.d.ts +2 -0
- package/dist/commands/ads/cache.test.d.ts.map +1 -0
- package/dist/commands/ads/cache.test.js +44 -0
- package/dist/commands/ads/cache.test.js.map +1 -0
- package/dist/commands/ads/field-descriptions.d.ts +2 -0
- package/dist/commands/ads/field-descriptions.d.ts.map +1 -0
- package/dist/commands/ads/field-descriptions.js +91 -0
- package/dist/commands/ads/field-descriptions.js.map +1 -0
- package/dist/commands/ads/google/accounts.d.ts +14 -0
- package/dist/commands/ads/google/accounts.d.ts.map +1 -0
- package/dist/commands/ads/google/accounts.js +73 -0
- package/dist/commands/ads/google/accounts.js.map +1 -0
- package/dist/commands/ads/google/changes.d.ts +34 -0
- package/dist/commands/ads/google/changes.d.ts.map +1 -0
- package/dist/commands/ads/google/changes.js +80 -0
- package/dist/commands/ads/google/changes.js.map +1 -0
- package/dist/commands/ads/google/correction-table.d.ts +3 -0
- package/dist/commands/ads/google/correction-table.d.ts.map +1 -0
- package/dist/commands/ads/google/correction-table.js +340 -0
- package/dist/commands/ads/google/correction-table.js.map +1 -0
- package/dist/commands/ads/google/currency.d.ts +13 -0
- package/dist/commands/ads/google/currency.d.ts.map +1 -0
- package/dist/commands/ads/google/currency.js +68 -0
- package/dist/commands/ads/google/currency.js.map +1 -0
- package/dist/commands/ads/google/error-parser.d.ts +3 -0
- package/dist/commands/ads/google/error-parser.d.ts.map +1 -0
- package/dist/commands/ads/google/error-parser.js +91 -0
- package/dist/commands/ads/google/error-parser.js.map +1 -0
- package/dist/commands/ads/google/error-parser.test.d.ts +2 -0
- package/dist/commands/ads/google/error-parser.test.d.ts.map +1 -0
- package/dist/commands/ads/google/error-parser.test.js +48 -0
- package/dist/commands/ads/google/error-parser.test.js.map +1 -0
- package/dist/commands/ads/google/index.d.ts +2 -0
- package/dist/commands/ads/google/index.d.ts.map +1 -0
- package/dist/commands/ads/google/index.js +30 -0
- package/dist/commands/ads/google/index.js.map +1 -0
- package/dist/commands/ads/google/keywords/discover.d.ts +49 -0
- package/dist/commands/ads/google/keywords/discover.d.ts.map +1 -0
- package/dist/commands/ads/google/keywords/discover.js +139 -0
- package/dist/commands/ads/google/keywords/discover.js.map +1 -0
- package/dist/commands/ads/google/keywords/index.d.ts +2 -0
- package/dist/commands/ads/google/keywords/index.d.ts.map +1 -0
- package/dist/commands/ads/google/keywords/index.js +18 -0
- package/dist/commands/ads/google/keywords/index.js.map +1 -0
- package/dist/commands/ads/google/keywords/metrics.d.ts +34 -0
- package/dist/commands/ads/google/keywords/metrics.d.ts.map +1 -0
- package/dist/commands/ads/google/keywords/metrics.js +111 -0
- package/dist/commands/ads/google/keywords/metrics.js.map +1 -0
- package/dist/commands/ads/google/preflight.d.ts +3 -0
- package/dist/commands/ads/google/preflight.d.ts.map +1 -0
- package/dist/commands/ads/google/preflight.js +115 -0
- package/dist/commands/ads/google/preflight.js.map +1 -0
- package/dist/commands/ads/google/preflight.test.d.ts +2 -0
- package/dist/commands/ads/google/preflight.test.d.ts.map +1 -0
- package/dist/commands/ads/google/preflight.test.js +50 -0
- package/dist/commands/ads/google/preflight.test.js.map +1 -0
- package/dist/commands/ads/google/presets.d.ts +12 -0
- package/dist/commands/ads/google/presets.d.ts.map +1 -0
- package/dist/commands/ads/google/presets.js +71 -0
- package/dist/commands/ads/google/presets.js.map +1 -0
- package/dist/commands/ads/google/presets.test.d.ts +2 -0
- package/dist/commands/ads/google/presets.test.d.ts.map +1 -0
- package/dist/commands/ads/google/presets.test.js +79 -0
- package/dist/commands/ads/google/presets.test.js.map +1 -0
- package/dist/commands/ads/google/query.d.ts +64 -0
- package/dist/commands/ads/google/query.d.ts.map +1 -0
- package/dist/commands/ads/google/query.js +304 -0
- package/dist/commands/ads/google/query.js.map +1 -0
- package/dist/commands/ads/index.d.ts +2 -0
- package/dist/commands/ads/index.d.ts.map +1 -0
- package/dist/commands/ads/index.js +19 -0
- package/dist/commands/ads/index.js.map +1 -0
- package/dist/commands/ads/output.d.ts +17 -0
- package/dist/commands/ads/output.d.ts.map +1 -0
- package/dist/commands/ads/output.js +78 -0
- package/dist/commands/ads/output.js.map +1 -0
- package/dist/commands/ads/output.test.d.ts +2 -0
- package/dist/commands/ads/output.test.d.ts.map +1 -0
- package/dist/commands/ads/output.test.js +100 -0
- package/dist/commands/ads/output.test.js.map +1 -0
- package/dist/commands/ads/types.d.ts +69 -0
- package/dist/commands/ads/types.d.ts.map +1 -0
- package/dist/commands/ads/types.js +2 -0
- package/dist/commands/ads/types.js.map +1 -0
- package/dist/commands/research/advertisers.d.ts +34 -0
- package/dist/commands/research/advertisers.d.ts.map +1 -0
- package/dist/commands/research/advertisers.js +75 -0
- package/dist/commands/research/advertisers.js.map +1 -0
- package/dist/commands/research/countries.d.ts +2 -0
- package/dist/commands/research/countries.d.ts.map +1 -0
- package/dist/commands/research/countries.js +69 -0
- package/dist/commands/research/countries.js.map +1 -0
- package/dist/commands/research/index.d.ts +2 -0
- package/dist/commands/research/index.d.ts.map +1 -0
- package/dist/commands/research/index.js +40 -0
- package/dist/commands/research/index.js.map +1 -0
- package/dist/commands/research/intent.d.ts +24 -0
- package/dist/commands/research/intent.d.ts.map +1 -0
- package/dist/commands/research/intent.js +71 -0
- package/dist/commands/research/intent.js.map +1 -0
- package/dist/commands/research/keyword-gap.d.ts +49 -0
- package/dist/commands/research/keyword-gap.d.ts.map +1 -0
- package/dist/commands/research/keyword-gap.js +99 -0
- package/dist/commands/research/keyword-gap.js.map +1 -0
- package/dist/commands/research/keywords-for-site.d.ts +44 -0
- package/dist/commands/research/keywords-for-site.d.ts.map +1 -0
- package/dist/commands/research/keywords-for-site.js +83 -0
- package/dist/commands/research/keywords-for-site.js.map +1 -0
- package/dist/commands/research/languages.d.ts +2 -0
- package/dist/commands/research/languages.d.ts.map +1 -0
- package/dist/commands/research/languages.js +42 -0
- package/dist/commands/research/languages.js.map +1 -0
- package/dist/commands/research/lighthouse.d.ts +24 -0
- package/dist/commands/research/lighthouse.d.ts.map +1 -0
- package/dist/commands/research/lighthouse.js +60 -0
- package/dist/commands/research/lighthouse.js.map +1 -0
- package/dist/commands/research/output.d.ts +29 -0
- package/dist/commands/research/output.d.ts.map +1 -0
- package/dist/commands/research/output.js +81 -0
- package/dist/commands/research/output.js.map +1 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +5 -1
- package/dist/env.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
function buildCommand(query, ctx) {
|
|
2
|
+
return `baker ads google query "${query}" --customer-id ${ctx.customerId}`;
|
|
3
|
+
}
|
|
4
|
+
export const CORRECTION_RULES = [
|
|
5
|
+
// 1. keyword.text → ad_group_criterion.keyword.text
|
|
6
|
+
{
|
|
7
|
+
matchQuery: /\bkeyword\.text\b/,
|
|
8
|
+
matchApiError: /keyword\.text/i,
|
|
9
|
+
fix: (ctx) => {
|
|
10
|
+
const corrected = ctx.originalQuery.replace(/\bkeyword\.text\b/g, "ad_group_criterion.keyword.text");
|
|
11
|
+
return {
|
|
12
|
+
action: "retry_with_modified_query",
|
|
13
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
14
|
+
explanation: "Use ad_group_criterion.keyword.text — keywords are accessed through the criterion resource",
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
// 2. keyword.match_type → ad_group_criterion.keyword.match_type
|
|
19
|
+
{
|
|
20
|
+
matchQuery: /\bkeyword\.match_type\b/,
|
|
21
|
+
matchApiError: /keyword\.match_type/i,
|
|
22
|
+
fix: (ctx) => {
|
|
23
|
+
const corrected = ctx.originalQuery.replace(/\bkeyword\.match_type\b/g, "ad_group_criterion.keyword.match_type");
|
|
24
|
+
return {
|
|
25
|
+
action: "retry_with_modified_query",
|
|
26
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
27
|
+
explanation: "Use ad_group_criterion.keyword.match_type for keyword match type",
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
// 3. campaign_budget.* queried FROM campaign
|
|
32
|
+
{
|
|
33
|
+
matchQuery: /campaign_budget\.\w+.*FROM\s+campaign\b/i,
|
|
34
|
+
matchApiError: /campaign_budget.*not.*valid.*campaign|cannot.*select.*campaign_budget/i,
|
|
35
|
+
fix: (ctx) => {
|
|
36
|
+
const corrected = ctx.originalQuery.replace(/FROM\s+campaign\b/i, "FROM campaign_budget");
|
|
37
|
+
return {
|
|
38
|
+
action: "use_different_resource",
|
|
39
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
40
|
+
explanation: "campaign_budget fields must be queried FROM campaign_budget, not FROM campaign",
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
// 4. CONTAINS → LIKE
|
|
45
|
+
{
|
|
46
|
+
matchQuery: /CONTAINS\s*\(/i,
|
|
47
|
+
matchApiError: /CONTAINS.*not.*supported|invalid.*operator.*CONTAINS/i,
|
|
48
|
+
fix: (ctx) => {
|
|
49
|
+
const match = ctx.originalQuery.match(/CONTAINS\s*\(\s*([^,]+),\s*'([^']+)'\s*\)/i);
|
|
50
|
+
if (match) {
|
|
51
|
+
const field = match[1]?.trim();
|
|
52
|
+
const value = match[2] ?? "";
|
|
53
|
+
const corrected = ctx.originalQuery.replace(/CONTAINS\s*\(\s*[^,]+,\s*'[^']+'\s*\)/i, `${field} LIKE '%${value}%'`);
|
|
54
|
+
return {
|
|
55
|
+
action: "change_operator",
|
|
56
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
57
|
+
explanation: "GAQL uses LIKE '%value%' for substring matching, not CONTAINS()",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
action: "change_operator",
|
|
62
|
+
explanation: "Replace CONTAINS(field, 'value') with field LIKE '%value%'",
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
// 5. Missing campaign.id in ad_group queries
|
|
67
|
+
{
|
|
68
|
+
matchQuery: /FROM\s+ad_group\b/i,
|
|
69
|
+
matchApiError: /campaign\.id.*required|must.*include.*campaign\.id/i,
|
|
70
|
+
fix: (ctx) => {
|
|
71
|
+
const corrected = ctx.originalQuery.replace(/SELECT\s+/i, "SELECT campaign.id, ");
|
|
72
|
+
return {
|
|
73
|
+
action: "add_required_field",
|
|
74
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
75
|
+
explanation: "Ad group queries require campaign.id in SELECT for parent context",
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
// 6. Missing campaign.id in keyword_view queries
|
|
80
|
+
{
|
|
81
|
+
matchQuery: /FROM\s+keyword_view\b/i,
|
|
82
|
+
matchApiError: /campaign\.id.*required.*keyword/i,
|
|
83
|
+
fix: (ctx) => {
|
|
84
|
+
const corrected = ctx.originalQuery.replace(/SELECT\s+/i, "SELECT campaign.id, ");
|
|
85
|
+
return {
|
|
86
|
+
action: "add_required_field",
|
|
87
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
88
|
+
explanation: "Keyword view queries require campaign.id in SELECT",
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
// 7. shopping_performance_view.product_* → segments.product_*
|
|
93
|
+
{
|
|
94
|
+
matchQuery: /shopping_performance_view\.product_\w+/,
|
|
95
|
+
matchApiError: /shopping_performance_view\.product/i,
|
|
96
|
+
fix: (ctx) => {
|
|
97
|
+
const corrected = ctx.originalQuery.replace(/shopping_performance_view\.product_(\w+)/g, "segments.product_$1");
|
|
98
|
+
return {
|
|
99
|
+
action: "retry_with_modified_query",
|
|
100
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
101
|
+
explanation: "Product fields are segments (segments.product_*), not fields on shopping_performance_view",
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
// 8. Open-ended date range
|
|
106
|
+
{
|
|
107
|
+
matchQuery: /segments\.date\s*>=\s*'(\d{4}-\d{2}-\d{2})'/i,
|
|
108
|
+
matchApiError: /date.*range.*must.*finite|open.*ended/i,
|
|
109
|
+
fix: (ctx) => {
|
|
110
|
+
const match = ctx.originalQuery.match(/segments\.date\s*>=\s*'(\d{4}-\d{2}-\d{2})'/i);
|
|
111
|
+
const startDate = match?.[1] ?? "2024-01-01";
|
|
112
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
113
|
+
const corrected = ctx.originalQuery.replace(/segments\.date\s*>=\s*'(\d{4}-\d{2}-\d{2})'/i, `segments.date BETWEEN '${startDate}' AND '${today}'`);
|
|
114
|
+
return {
|
|
115
|
+
action: "narrow_date_range",
|
|
116
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
117
|
+
explanation: "Use BETWEEN with explicit end date — open-ended ranges are not supported",
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
// 9. Missing LIMIT
|
|
122
|
+
{
|
|
123
|
+
matchQuery: /^(?!.*\bLIMIT\b)/is,
|
|
124
|
+
fix: (ctx) => {
|
|
125
|
+
const corrected = `${ctx.originalQuery.trimEnd()} LIMIT 200`;
|
|
126
|
+
return {
|
|
127
|
+
action: "retry_with_modified_query",
|
|
128
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
129
|
+
explanation: "Added LIMIT 200 to prevent excessive data transfer",
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
// 10a. Customer not found (wrong customer ID)
|
|
134
|
+
{
|
|
135
|
+
matchApiError: /CUSTOMER_NOT_FOUND|not.*found.*customer/i,
|
|
136
|
+
fix: () => ({
|
|
137
|
+
action: "reject",
|
|
138
|
+
explanation: "Customer ID not found. Run 'baker ads google accounts' to list valid customer IDs, then retry with a valid --customer-id.",
|
|
139
|
+
}),
|
|
140
|
+
},
|
|
141
|
+
// 10b. Not accessible / login-customer-id required
|
|
142
|
+
{
|
|
143
|
+
matchApiError: /not.*accessible|login.customer.id/i,
|
|
144
|
+
fix: (ctx) => ({
|
|
145
|
+
action: "reject",
|
|
146
|
+
correctedCommand: buildCommand(ctx.originalQuery, ctx),
|
|
147
|
+
explanation: "Account not accessible — may need a manager (MCC) login-customer-id. Run 'baker ads google accounts' to refresh the accounts cache, then retry.",
|
|
148
|
+
}),
|
|
149
|
+
},
|
|
150
|
+
// 11. Campaign type by name matching
|
|
151
|
+
{
|
|
152
|
+
matchQuery: /campaign\.name\s*LIKE\s*'%\s*(shopping|pmax|search|display|video)\s*%'/i,
|
|
153
|
+
fix: (ctx) => {
|
|
154
|
+
const match = ctx.originalQuery.match(/campaign\.name\s*LIKE\s*'%\s*(shopping|pmax|search|display|video)\s*%'/i);
|
|
155
|
+
const typeMap = {
|
|
156
|
+
shopping: "SHOPPING",
|
|
157
|
+
pmax: "PERFORMANCE_MAX",
|
|
158
|
+
search: "SEARCH",
|
|
159
|
+
display: "DISPLAY",
|
|
160
|
+
video: "VIDEO",
|
|
161
|
+
};
|
|
162
|
+
const channelType = typeMap[match?.[1]?.toLowerCase() ?? ""] ?? "SEARCH";
|
|
163
|
+
const corrected = ctx.originalQuery.replace(/campaign\.name\s*LIKE\s*'%[^']*%'/i, `campaign.advertising_channel_type = '${channelType}'`);
|
|
164
|
+
return {
|
|
165
|
+
action: "retry_with_modified_query",
|
|
166
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
167
|
+
explanation: "Filter by campaign.advertising_channel_type enum, not by name pattern",
|
|
168
|
+
};
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
// 12. Incompatible fields
|
|
172
|
+
{
|
|
173
|
+
matchApiError: /incompatible|mutually.*exclusive|cannot.*select.*together/i,
|
|
174
|
+
fix: () => ({
|
|
175
|
+
action: "split_query",
|
|
176
|
+
explanation: "These fields cannot be in the same query — split into separate queries and join client-side",
|
|
177
|
+
}),
|
|
178
|
+
},
|
|
179
|
+
// 13. campaign.status = 'ACTIVE' → 'ENABLED'
|
|
180
|
+
{
|
|
181
|
+
matchQuery: /campaign\.status\s*=\s*'ACTIVE'/i,
|
|
182
|
+
fix: (ctx) => {
|
|
183
|
+
const corrected = ctx.originalQuery.replace(/campaign\.status\s*=\s*'ACTIVE'/gi, "campaign.status = 'ENABLED'");
|
|
184
|
+
return {
|
|
185
|
+
action: "retry_with_modified_query",
|
|
186
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
187
|
+
explanation: "Campaign status uses ENABLED, not ACTIVE",
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
// 14. ad_group.status = 'ACTIVE' → 'ENABLED'
|
|
192
|
+
{
|
|
193
|
+
matchQuery: /ad_group\.status\s*=\s*'ACTIVE'/i,
|
|
194
|
+
fix: (ctx) => {
|
|
195
|
+
const corrected = ctx.originalQuery.replace(/ad_group\.status\s*=\s*'ACTIVE'/gi, "ad_group.status = 'ENABLED'");
|
|
196
|
+
return {
|
|
197
|
+
action: "retry_with_modified_query",
|
|
198
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
199
|
+
explanation: "Ad group status uses ENABLED, not ACTIVE",
|
|
200
|
+
};
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
// 15. ad.final_urls → ad_group_ad.ad.final_urls
|
|
204
|
+
{
|
|
205
|
+
matchQuery: /\bad\.final_urls\b(?!.*ad_group_ad)/,
|
|
206
|
+
matchApiError: /ad\.final_urls/i,
|
|
207
|
+
fix: (ctx) => {
|
|
208
|
+
const corrected = ctx.originalQuery.replace(/\bad\.final_urls\b/g, "ad_group_ad.ad.final_urls");
|
|
209
|
+
return {
|
|
210
|
+
action: "retry_with_modified_query",
|
|
211
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
212
|
+
explanation: "Use the full path: ad_group_ad.ad.final_urls",
|
|
213
|
+
};
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
// 16. Rate limit / quota
|
|
217
|
+
{
|
|
218
|
+
matchApiError: /RESOURCE_EXHAUSTED|quota.*exceeded|rate.*limit/i,
|
|
219
|
+
fix: () => ({
|
|
220
|
+
action: "wait_and_retry",
|
|
221
|
+
explanation: "API quota exceeded — wait 30 seconds before retrying",
|
|
222
|
+
}),
|
|
223
|
+
},
|
|
224
|
+
// 17. WHERE on metrics
|
|
225
|
+
{
|
|
226
|
+
matchApiError: /cannot.*filter.*metric|WHERE.*metrics|prohibited.*where/i,
|
|
227
|
+
fix: () => ({
|
|
228
|
+
action: "reject",
|
|
229
|
+
explanation: "Cannot filter on metrics in WHERE clause — remove the metrics filter and filter results client-side",
|
|
230
|
+
}),
|
|
231
|
+
},
|
|
232
|
+
// 18. ORDER BY field not in SELECT
|
|
233
|
+
{
|
|
234
|
+
matchApiError: /order.*by.*field.*not.*selected|must.*select.*field.*order/i,
|
|
235
|
+
fix: (ctx) => {
|
|
236
|
+
const orderMatch = ctx.originalQuery.match(/ORDER\s+BY\s+([\w.]+)/i);
|
|
237
|
+
const field = orderMatch?.[1] ?? "field";
|
|
238
|
+
const corrected = ctx.originalQuery.replace(/SELECT\s+/i, `SELECT ${field}, `);
|
|
239
|
+
return {
|
|
240
|
+
action: "add_required_field",
|
|
241
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
242
|
+
explanation: `Add ${field} to SELECT — ORDER BY fields must be selected`,
|
|
243
|
+
};
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
// 19. Results include REMOVED entities
|
|
247
|
+
{
|
|
248
|
+
matchApiError: /REMOVED.*entities|status.*filter/i,
|
|
249
|
+
fix: (ctx) => {
|
|
250
|
+
const resourceMatch = ctx.originalQuery.match(/FROM\s+(\w+)/i);
|
|
251
|
+
const resource = resourceMatch?.[1] ?? "campaign";
|
|
252
|
+
const hasWhere = /WHERE/i.test(ctx.originalQuery);
|
|
253
|
+
const suffix = hasWhere ? ` AND ${resource}.status != 'REMOVED'` : ` WHERE ${resource}.status != 'REMOVED'`;
|
|
254
|
+
const limitMatch = ctx.originalQuery.match(/(\s+LIMIT\s+\d+)/i);
|
|
255
|
+
let corrected;
|
|
256
|
+
if (limitMatch) {
|
|
257
|
+
corrected = ctx.originalQuery.replace(/(\s+LIMIT\s+\d+)/i, `${suffix}$1`);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
corrected = ctx.originalQuery.trimEnd() + suffix;
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
action: "retry_with_modified_query",
|
|
264
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
265
|
+
explanation: `Add status filter to exclude REMOVED ${resource}s`,
|
|
266
|
+
};
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
// 20. change_event without date constraint
|
|
270
|
+
{
|
|
271
|
+
matchQuery: /FROM\s+change_event\b(?!.*change_date_time)/i,
|
|
272
|
+
matchApiError: /change_event.*date|date.*required.*change/i,
|
|
273
|
+
fix: (ctx) => {
|
|
274
|
+
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
|
|
275
|
+
const hasWhere = /WHERE/i.test(ctx.originalQuery);
|
|
276
|
+
const suffix = hasWhere
|
|
277
|
+
? ` AND change_event.change_date_time >= '${sevenDaysAgo}'`
|
|
278
|
+
: ` WHERE change_event.change_date_time >= '${sevenDaysAgo}'`;
|
|
279
|
+
const corrected = ctx.originalQuery.trimEnd() + suffix;
|
|
280
|
+
return {
|
|
281
|
+
action: "add_required_field",
|
|
282
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
283
|
+
explanation: "change_event queries require a date constraint on change_date_time",
|
|
284
|
+
};
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
// 21. resource_name = 'customers/X/Y/Z' → resource.id = Z
|
|
288
|
+
{
|
|
289
|
+
matchQuery: /(\w+)\.resource_name\s*=\s*'customers\/\d+\/\w+\/(\d+)'/i,
|
|
290
|
+
fix: (ctx) => {
|
|
291
|
+
const match = ctx.originalQuery.match(/(\w+)\.resource_name\s*=\s*'customers\/\d+\/\w+\/(\d+)'/i);
|
|
292
|
+
const resource = match?.[1] ?? "campaign";
|
|
293
|
+
const id = match?.[2] ?? "ID";
|
|
294
|
+
const corrected = ctx.originalQuery.replace(/(\w+)\.resource_name\s*=\s*'customers\/\d+\/\w+\/(\d+)'/i, `${resource}.id = ${id}`);
|
|
295
|
+
return {
|
|
296
|
+
action: "retry_with_modified_query",
|
|
297
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
298
|
+
explanation: `Simpler: use ${resource}.id = ${id} instead of resource_name path`,
|
|
299
|
+
};
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
// 22. segments.click_type incompatibility
|
|
303
|
+
{
|
|
304
|
+
matchApiError: /click_type.*incompatible|cannot.*click_type/i,
|
|
305
|
+
fix: (ctx) => {
|
|
306
|
+
const corrected = ctx.originalQuery.replace(/,?\s*segments\.click_type/g, "");
|
|
307
|
+
return {
|
|
308
|
+
action: "split_query",
|
|
309
|
+
correctedCommand: buildCommand(corrected, ctx),
|
|
310
|
+
explanation: "segments.click_type is incompatible with other segments — remove it or query separately",
|
|
311
|
+
};
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
// 23. Bare resource in SELECT
|
|
315
|
+
{
|
|
316
|
+
matchApiError: /cannot.*select.*bare.*resource|invalid.*field/i,
|
|
317
|
+
fix: () => ({
|
|
318
|
+
action: "retry_with_modified_query",
|
|
319
|
+
explanation: "Cannot SELECT a bare resource name — use resource.id, resource.name, or resource.resource_name",
|
|
320
|
+
}),
|
|
321
|
+
},
|
|
322
|
+
// 24. Authentication error
|
|
323
|
+
{
|
|
324
|
+
matchApiError: /unauthenticated|authentication.*required|invalid.*credentials/i,
|
|
325
|
+
fix: () => ({
|
|
326
|
+
action: "authenticate",
|
|
327
|
+
explanation: "Google Ads authentication failed — reconnect Google Ads in dashboard settings",
|
|
328
|
+
}),
|
|
329
|
+
},
|
|
330
|
+
// 25. Timeout / deadline exceeded
|
|
331
|
+
{
|
|
332
|
+
matchApiError: /timeout|deadline.*exceeded|DEADLINE_EXCEEDED/i,
|
|
333
|
+
fix: (ctx) => ({
|
|
334
|
+
action: "retry_with_modified_query",
|
|
335
|
+
correctedCommand: buildCommand(ctx.originalQuery, ctx),
|
|
336
|
+
explanation: "Query timed out — narrow the date range, add more WHERE filters, or reduce LIMIT",
|
|
337
|
+
}),
|
|
338
|
+
},
|
|
339
|
+
];
|
|
340
|
+
//# sourceMappingURL=correction-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correction-table.js","sourceRoot":"","sources":["../../../../src/commands/ads/google/correction-table.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const currencyCommand: import("citty").CommandDef<{
|
|
2
|
+
readonly "customer-id": {
|
|
3
|
+
readonly type: "string";
|
|
4
|
+
readonly description: "Google Ads customer ID (10 digits)";
|
|
5
|
+
readonly required: false;
|
|
6
|
+
};
|
|
7
|
+
readonly "no-cache": {
|
|
8
|
+
readonly type: "boolean";
|
|
9
|
+
readonly description: "Skip cache";
|
|
10
|
+
readonly required: false;
|
|
11
|
+
};
|
|
12
|
+
}>;
|
|
13
|
+
//# sourceMappingURL=currency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"currency.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/google/currency.ts"],"names":[],"mappings":"AAyBA,eAAO,MAAM,eAAe;;;;;;;;;;;EAoD1B,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { ApiError, apiGet } from "../../../client.js";
|
|
3
|
+
import { registerSchema } from "../../../schemas.js";
|
|
4
|
+
import { cacheGet, cacheSet, getManagerIdForCustomer } from "../cache.js";
|
|
5
|
+
import { resolveCustomerId, writeAdsJson } from "../output.js";
|
|
6
|
+
import { parseApiError } from "./error-parser.js";
|
|
7
|
+
registerSchema({
|
|
8
|
+
command: "ads.google.currency",
|
|
9
|
+
description: "Get the currency code for a Google Ads account. Returns currency_code, customer_id, account_name, and access_type. Call this before interpreting cost_micros values.",
|
|
10
|
+
args: {
|
|
11
|
+
"customer-id": {
|
|
12
|
+
type: "string",
|
|
13
|
+
description: "Google Ads customer ID (10 digits, no dashes). Falls back to BAKER_GOOGLE_ADS_CUSTOMER_ID env var.",
|
|
14
|
+
required: false,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
export const currencyCommand = defineCommand({
|
|
19
|
+
meta: {
|
|
20
|
+
name: "currency",
|
|
21
|
+
description: `Get account currency code. Use this to interpret metrics.cost_micros values.
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
baker ads google currency --customer-id 1234567890`,
|
|
25
|
+
},
|
|
26
|
+
args: {
|
|
27
|
+
"customer-id": { type: "string", description: "Google Ads customer ID (10 digits)", required: false },
|
|
28
|
+
"no-cache": { type: "boolean", description: "Skip cache", required: false },
|
|
29
|
+
},
|
|
30
|
+
run: async ({ args }) => {
|
|
31
|
+
const customerId = resolveCustomerId(args);
|
|
32
|
+
const useCache = !args["no-cache"];
|
|
33
|
+
const cacheKey = `currency:${customerId}`;
|
|
34
|
+
if (useCache) {
|
|
35
|
+
const cached = cacheGet("accounts", cacheKey);
|
|
36
|
+
if (cached) {
|
|
37
|
+
writeAdsJson({ ok: true, data: cached.data, cached: true });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const params = { "customer-id": customerId };
|
|
43
|
+
const managerId = getManagerIdForCustomer(customerId);
|
|
44
|
+
if (managerId)
|
|
45
|
+
params["manager-id"] = managerId;
|
|
46
|
+
if (!useCache)
|
|
47
|
+
params["skip-cache"] = "true";
|
|
48
|
+
const raw = await apiGet("/api/ads/google/currency", params);
|
|
49
|
+
const data = {
|
|
50
|
+
currency_code: raw.currencyCode,
|
|
51
|
+
customer_id: raw.customerId,
|
|
52
|
+
};
|
|
53
|
+
if (useCache) {
|
|
54
|
+
cacheSet("accounts", cacheKey, data, 24 * 60 * 60 * 1000);
|
|
55
|
+
}
|
|
56
|
+
writeAdsJson({ ok: true, data });
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
if (err instanceof ApiError) {
|
|
60
|
+
writeAdsJson(parseApiError(err.message, "", customerId));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
writeAdsJson({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error" } });
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=currency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"currency.js","sourceRoot":"","sources":["../../../../src/commands/ads/google/currency.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-parser.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/google/error-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAqB,MAAM,aAAa,CAAC;AA2BvE,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,gBAAgB,CA0D/G"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { CORRECTION_RULES } from "./correction-table.js";
|
|
2
|
+
function mapErrorCode(message) {
|
|
3
|
+
if (/field.*not.*found|unrecognized.*field|not.*valid.*field/i.test(message))
|
|
4
|
+
return "FIELD_NOT_FOUND";
|
|
5
|
+
if (/not.*valid.*resource|cannot.*select.*from/i.test(message))
|
|
6
|
+
return "WRONG_RESOURCE";
|
|
7
|
+
if (/operator|CONTAINS|LIKE/i.test(message))
|
|
8
|
+
return "INVALID_OPERATOR";
|
|
9
|
+
if (/CUSTOMER_NOT_FOUND/i.test(message))
|
|
10
|
+
return "CUSTOMER_NOT_FOUND";
|
|
11
|
+
if (/login.customer.id|not.*accessible/i.test(message))
|
|
12
|
+
return "MISSING_MANAGER_ID";
|
|
13
|
+
if (/incompatible|mutually.*exclusive/i.test(message))
|
|
14
|
+
return "INCOMPATIBLE_FIELDS";
|
|
15
|
+
if (/unauthenticated|authentication/i.test(message))
|
|
16
|
+
return "AUTH_ERROR";
|
|
17
|
+
if (/permission/i.test(message))
|
|
18
|
+
return "PERMISSION_DENIED";
|
|
19
|
+
if (/RESOURCE_EXHAUSTED|quota|rate.*limit/i.test(message))
|
|
20
|
+
return "QUOTA_EXCEEDED";
|
|
21
|
+
if (/timeout|DEADLINE_EXCEEDED/i.test(message))
|
|
22
|
+
return "TIMEOUT";
|
|
23
|
+
return "API_ERROR";
|
|
24
|
+
}
|
|
25
|
+
function isRetryable(code) {
|
|
26
|
+
return code === "QUOTA_EXCEEDED" || code === "TIMEOUT";
|
|
27
|
+
}
|
|
28
|
+
function getRetryDelay(code) {
|
|
29
|
+
if (code === "QUOTA_EXCEEDED")
|
|
30
|
+
return 30000;
|
|
31
|
+
if (code === "TIMEOUT")
|
|
32
|
+
return 5000;
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
export function parseApiError(errorMessage, originalQuery, customerId) {
|
|
36
|
+
const ctx = {
|
|
37
|
+
originalQuery,
|
|
38
|
+
customerId,
|
|
39
|
+
apiErrorMessage: errorMessage,
|
|
40
|
+
};
|
|
41
|
+
// Try API error patterns first
|
|
42
|
+
for (const rule of CORRECTION_RULES) {
|
|
43
|
+
if (rule.matchApiError?.test(errorMessage)) {
|
|
44
|
+
const fix = rule.fix(ctx);
|
|
45
|
+
const code = mapErrorCode(errorMessage);
|
|
46
|
+
return {
|
|
47
|
+
ok: false,
|
|
48
|
+
error: {
|
|
49
|
+
code,
|
|
50
|
+
message: errorMessage,
|
|
51
|
+
fix,
|
|
52
|
+
retryable: isRetryable(code),
|
|
53
|
+
retryAfterMs: getRetryDelay(code),
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Try query patterns (API might reject what preflight missed)
|
|
59
|
+
for (const rule of CORRECTION_RULES) {
|
|
60
|
+
if (rule.matchQuery?.test(originalQuery)) {
|
|
61
|
+
const fix = rule.fix(ctx);
|
|
62
|
+
const code = mapErrorCode(errorMessage);
|
|
63
|
+
return {
|
|
64
|
+
ok: false,
|
|
65
|
+
error: {
|
|
66
|
+
code,
|
|
67
|
+
message: errorMessage,
|
|
68
|
+
fix,
|
|
69
|
+
retryable: isRetryable(code),
|
|
70
|
+
retryAfterMs: getRetryDelay(code),
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Fallback: unknown error
|
|
76
|
+
const code = mapErrorCode(errorMessage);
|
|
77
|
+
return {
|
|
78
|
+
ok: false,
|
|
79
|
+
error: {
|
|
80
|
+
code,
|
|
81
|
+
message: errorMessage,
|
|
82
|
+
fix: {
|
|
83
|
+
action: "reject",
|
|
84
|
+
explanation: "Unrecognized error — verify query syntax against Google Ads GAQL reference",
|
|
85
|
+
},
|
|
86
|
+
retryable: isRetryable(code),
|
|
87
|
+
retryAfterMs: getRetryDelay(code),
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=error-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-parser.js","sourceRoot":"","sources":["../../../../src/commands/ads/google/error-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-parser.test.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/google/error-parser.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { parseApiError } from "./error-parser.js";
|
|
3
|
+
const QUERY = "SELECT campaign.name FROM campaign LIMIT 10";
|
|
4
|
+
const CID = "123-456-7890";
|
|
5
|
+
describe("parseApiError", () => {
|
|
6
|
+
it("maps field-not-found errors to FIELD_NOT_FOUND", () => {
|
|
7
|
+
const result = parseApiError("field 'campaign.foo' not found in resource", QUERY, CID);
|
|
8
|
+
expect(result.ok).toBe(false);
|
|
9
|
+
expect(result.error.code).toBe("FIELD_NOT_FOUND");
|
|
10
|
+
});
|
|
11
|
+
it("maps authentication errors to AUTH_ERROR", () => {
|
|
12
|
+
const result = parseApiError("Request is unauthenticated", QUERY, CID);
|
|
13
|
+
expect(result.ok).toBe(false);
|
|
14
|
+
expect(result.error.code).toBe("AUTH_ERROR");
|
|
15
|
+
});
|
|
16
|
+
it("maps quota exceeded to QUOTA_EXCEEDED with retry", () => {
|
|
17
|
+
const result = parseApiError("RESOURCE_EXHAUSTED: quota exceeded for project", QUERY, CID);
|
|
18
|
+
expect(result.ok).toBe(false);
|
|
19
|
+
expect(result.error.code).toBe("QUOTA_EXCEEDED");
|
|
20
|
+
expect(result.error.retryable).toBe(true);
|
|
21
|
+
expect(result.error.retryAfterMs).toBe(30000);
|
|
22
|
+
});
|
|
23
|
+
it("maps timeout to TIMEOUT with retry", () => {
|
|
24
|
+
const result = parseApiError("DEADLINE_EXCEEDED: timeout after 60s", QUERY, CID);
|
|
25
|
+
expect(result.ok).toBe(false);
|
|
26
|
+
expect(result.error.code).toBe("TIMEOUT");
|
|
27
|
+
expect(result.error.retryable).toBe(true);
|
|
28
|
+
expect(result.error.retryAfterMs).toBe(5000);
|
|
29
|
+
});
|
|
30
|
+
it("maps unknown errors to API_ERROR with retryable false", () => {
|
|
31
|
+
const result = parseApiError("Something completely unexpected happened", QUERY, CID);
|
|
32
|
+
expect(result.ok).toBe(false);
|
|
33
|
+
expect(result.error.code).toBe("API_ERROR");
|
|
34
|
+
expect(result.error.retryable).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
it("matches correction rules for keyword.text in error message", () => {
|
|
37
|
+
const query = "SELECT keyword.text FROM keyword_view LIMIT 10";
|
|
38
|
+
const result = parseApiError("keyword.text is not a valid field", query, CID);
|
|
39
|
+
expect(result.error.fix.action).toBe("retry_with_modified_query");
|
|
40
|
+
expect(result.error.fix.correctedCommand).toMatch(/ad_group_criterion\.keyword\.text/);
|
|
41
|
+
});
|
|
42
|
+
it("maps permission denied errors to PERMISSION_DENIED", () => {
|
|
43
|
+
const result = parseApiError("The caller does not have permission to access this resource", QUERY, CID);
|
|
44
|
+
expect(result.ok).toBe(false);
|
|
45
|
+
expect(result.error.code).toBe("PERMISSION_DENIED");
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
//# sourceMappingURL=error-parser.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-parser.test.js","sourceRoot":"","sources":["../../../../src/commands/ads/google/error-parser.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,KAAK,GAAG,6CAA6C,CAAC;AAC5D,MAAM,GAAG,GAAG,cAAc,CAAC;AAE3B,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,MAAM,GAAG,aAAa,CAAC,4CAA4C,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAEvF,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,MAAM,GAAG,aAAa,CAAC,4BAA4B,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAEvE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,MAAM,GAAG,aAAa,CAAC,gDAAgD,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAE3F,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,sCAAsC,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAEjF,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,MAAM,GAAG,aAAa,CAAC,0CAA0C,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAErF,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,KAAK,GAAG,gDAAgD,CAAC;QAC/D,MAAM,MAAM,GAAG,aAAa,CAAC,mCAAmC,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAE9E,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAClE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,MAAM,GAAG,aAAa,CAAC,6DAA6D,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAExG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/google/index.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,aAAa,qDAsBxB,CAAC"}
|