@koda-sl/baker-cli 0.343.0-dev.acf96fa0f → 0.343.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,
@@ -17043,6 +17043,113 @@ Examples:
17043
17043
  // src/commands/ads/google/write-commands.ts
17044
17044
  import { defineCommand as defineCommand31 } from "citty";
17045
17045
 
17046
+ // src/commands/ads/google/ad-copy-flags.ts
17047
+ var AD_COPY_LIST_FLAGS = ["headlines", "descriptions", "long-headlines"];
17048
+ var AD_COPY_LIST_USAGE = `repeat the flag once per item, or pass one comma-separated list and wrap an item that contains a comma in double quotes: '"Fast, free quotes",Book today'. A flag given once is read as a list, so a lone item with a comma needs its double quotes too: '"Fast, free quotes"'`;
17049
+ function skipBlanks(value, at) {
17050
+ let i = at;
17051
+ while (value[i] === " " || value[i] === " ") i++;
17052
+ return i;
17053
+ }
17054
+ function readQuotedField(value, open, flag) {
17055
+ let text2 = "";
17056
+ let j = open + 1;
17057
+ while (!(value[j] === '"' && value[j + 1] !== '"')) {
17058
+ if (j >= value.length) {
17059
+ failWriteValidation(`--${flag} has a quote with no closing quote: ${value} \u2014 ${AD_COPY_LIST_USAGE}`);
17060
+ }
17061
+ text2 += value[j];
17062
+ j += value[j] === '"' ? 2 : 1;
17063
+ }
17064
+ const after = skipBlanks(value, j + 1);
17065
+ if (after < value.length && value[after] !== ",") {
17066
+ failWriteValidation(
17067
+ `--${flag}: text follows the closing quote of "${text2}" \u2014 a quoted item must end at a comma. To put a quote inside an item, double it ("") or ${AD_COPY_LIST_USAGE}`
17068
+ );
17069
+ }
17070
+ return { text: text2, next: after + 1 };
17071
+ }
17072
+ function splitQuotedList(value, flag) {
17073
+ const fields = [];
17074
+ let i = 0;
17075
+ while (i <= value.length) {
17076
+ const start = skipBlanks(value, i);
17077
+ if (value[start] === '"') {
17078
+ const { text: text2, next } = readQuotedField(value, start, flag);
17079
+ fields.push({ text: text2, quoted: true });
17080
+ i = next;
17081
+ } else {
17082
+ const comma = value.indexOf(",", start);
17083
+ const end = comma === -1 ? value.length : comma;
17084
+ fields.push({ text: value.slice(start, end), quoted: false });
17085
+ i = end + 1;
17086
+ }
17087
+ }
17088
+ const several = fields.length > 1;
17089
+ return fields.map((field) => ({ text: field.text.trim(), splitAtComma: several && !field.quoted }));
17090
+ }
17091
+ function jsonStringArray(value) {
17092
+ if (!value.startsWith("[")) return void 0;
17093
+ try {
17094
+ const parsed = JSON.parse(value);
17095
+ return Array.isArray(parsed) && parsed.every((v) => typeof v === "string") ? parsed : void 0;
17096
+ } catch {
17097
+ return void 0;
17098
+ }
17099
+ }
17100
+ function parseCopyList(value, flag) {
17101
+ let items;
17102
+ if (Array.isArray(value)) {
17103
+ items = value.filter((v) => typeof v === "string").map((v) => ({ text: v.trim(), splitAtComma: false }));
17104
+ } else if (typeof value === "string") {
17105
+ const trimmed = value.trim();
17106
+ const json = jsonStringArray(trimmed);
17107
+ items = json ? json.map((v) => ({ text: v.trim(), splitAtComma: false })) : splitQuotedList(trimmed, flag);
17108
+ } else {
17109
+ return void 0;
17110
+ }
17111
+ const kept = items.filter((item) => item.text.length > 0);
17112
+ return kept.length > 0 ? kept : void 0;
17113
+ }
17114
+ function copyListFlag(value, flag) {
17115
+ return parseCopyList(value, flag)?.map((item) => item.text);
17116
+ }
17117
+ function refuseDashLedValue(rawArgs, flag) {
17118
+ for (let i = 0; i < rawArgs.length - 1; i++) {
17119
+ if (rawArgs[i] === "--") return;
17120
+ const value = rawArgs[i + 1];
17121
+ if (rawArgs[i] === `--${flag}` && value?.startsWith("-")) {
17122
+ failWriteValidation(
17123
+ `--${flag} is followed by "${value}", which starts with a dash and is read as another flag, not as copy. Join copy that starts with a dash to its flag with =: --${flag}="${value}"`
17124
+ );
17125
+ }
17126
+ }
17127
+ }
17128
+ function withRepeatedCopyFlags(args5, rawArgs) {
17129
+ const out = { ...args5 };
17130
+ for (const flag of AD_COPY_LIST_FLAGS) {
17131
+ refuseDashLedValue(rawArgs ?? [], flag);
17132
+ const values = repeatedFlagValues(rawArgs ?? [], flag);
17133
+ if (values.length > 1) {
17134
+ out[flag] = values;
17135
+ }
17136
+ }
17137
+ return out;
17138
+ }
17139
+ function copyListSplitHints(args5) {
17140
+ const hints2 = [];
17141
+ for (const flag of AD_COPY_LIST_FLAGS) {
17142
+ const items = parseCopyList(args5[flag], flag) ?? [];
17143
+ const fragments = items.filter((item, index) => index > 0 && item.splitAtComma && new RegExp("^\\p{Ll}", "u").test(item.text));
17144
+ if (fragments.length > 0) {
17145
+ hints2.push(
17146
+ `--${flag}: ${fragments.map((f) => `"${f.text}"`).join(", ")} starts in lowercase \u2014 it looks like the end of one item cut at a comma. Check the staged copy; if it was one item, fix this op in place with \`draft amend <ref>\` (the ref this response returned) rather than staging it again, which would leave the cut copy staged too \u2014 ${AD_COPY_LIST_USAGE}.`
17147
+ );
17148
+ }
17149
+ }
17150
+ return hints2;
17151
+ }
17152
+
17046
17153
  // src/commands/ads/google/conversion-upload-command.ts
17047
17154
  import { readFileSync as readFileSync4 } from "fs";
17048
17155
  import { defineCommand as defineCommand30 } from "citty";
@@ -18098,10 +18205,10 @@ var keywordListsCommand = defineCommand31({
18098
18205
  }
18099
18206
  });
18100
18207
  function rsaContentFromFlags(args5) {
18101
- const headlines = listFlag(args5.headlines);
18102
- const descriptions = listFlag(args5.descriptions);
18208
+ const headlines = copyListFlag(args5.headlines, "headlines");
18209
+ const descriptions = copyListFlag(args5.descriptions, "descriptions");
18103
18210
  if (!headlines || !descriptions) {
18104
- failWriteValidation("responsive search ads need --headlines and --descriptions (comma-separated), or use --file");
18211
+ failWriteValidation("responsive search ads need --headlines and --descriptions, or use --file");
18105
18212
  }
18106
18213
  const finalUrls = listFlag(args5["final-url"]);
18107
18214
  if (!finalUrls) {
@@ -18121,11 +18228,11 @@ function rdaContentFromFlags(args5, base) {
18121
18228
  format: "responsiveDisplay",
18122
18229
  ...base !== null && typeof base === "object" && !Array.isArray(base) ? base : {}
18123
18230
  };
18124
- const headlines = listFlag(args5.headlines);
18231
+ const headlines = copyListFlag(args5.headlines, "headlines");
18125
18232
  if (headlines) {
18126
18233
  content.headlines = headlines.map((text2) => ({ text: text2 }));
18127
18234
  }
18128
- const descriptions = listFlag(args5.descriptions);
18235
+ const descriptions = copyListFlag(args5.descriptions, "descriptions");
18129
18236
  if (descriptions) {
18130
18237
  content.descriptions = descriptions.map((text2) => ({ text: text2 }));
18131
18238
  }
@@ -18176,11 +18283,11 @@ function demandGenContentFromFlags(args5, base) {
18176
18283
  if (typeof args5["ad-name"] === "string") {
18177
18284
  content.name = args5["ad-name"];
18178
18285
  }
18179
- const headlines = listFlag(args5.headlines);
18286
+ const headlines = copyListFlag(args5.headlines, "headlines");
18180
18287
  if (headlines) {
18181
18288
  content.headlines = headlines.map((text2) => ({ text: text2 }));
18182
18289
  }
18183
- const descriptions = listFlag(args5.descriptions);
18290
+ const descriptions = copyListFlag(args5.descriptions, "descriptions");
18184
18291
  if (descriptions) {
18185
18292
  content.descriptions = descriptions.map((text2) => ({ text: text2 }));
18186
18293
  }
@@ -18213,15 +18320,15 @@ function demandGenVideoContentFromFlags(args5, base) {
18213
18320
  if (typeof args5["ad-name"] === "string") {
18214
18321
  content.name = args5["ad-name"];
18215
18322
  }
18216
- const headlines = listFlag(args5.headlines);
18323
+ const headlines = copyListFlag(args5.headlines, "headlines");
18217
18324
  if (headlines) {
18218
18325
  content.headlines = headlines.map((text2) => ({ text: text2 }));
18219
18326
  }
18220
- const longHeadlines = listFlag(args5["long-headlines"]);
18327
+ const longHeadlines = copyListFlag(args5["long-headlines"], "long-headlines");
18221
18328
  if (longHeadlines) {
18222
18329
  content.longHeadlines = longHeadlines.map((text2) => ({ text: text2 }));
18223
18330
  }
18224
- const descriptions = listFlag(args5.descriptions);
18331
+ const descriptions = copyListFlag(args5.descriptions, "descriptions");
18225
18332
  if (descriptions) {
18226
18333
  content.descriptions = descriptions.map((text2) => ({ text: text2 }));
18227
18334
  }
@@ -18251,11 +18358,11 @@ function appContentFromFlags(args5, base) {
18251
18358
  format: "app",
18252
18359
  ...base !== null && typeof base === "object" && !Array.isArray(base) ? base : {}
18253
18360
  };
18254
- const headlines = listFlag(args5.headlines);
18361
+ const headlines = copyListFlag(args5.headlines, "headlines");
18255
18362
  if (headlines) {
18256
18363
  content.headlines = headlines.map((text2) => ({ text: text2 }));
18257
18364
  }
18258
- const descriptions = listFlag(args5.descriptions);
18365
+ const descriptions = copyListFlag(args5.descriptions, "descriptions");
18259
18366
  if (descriptions) {
18260
18367
  content.descriptions = descriptions.map((text2) => ({ text: text2 }));
18261
18368
  }
@@ -18357,10 +18464,13 @@ var adContentArgs = {
18357
18464
  type: "string",
18358
18465
  description: "responsiveSearch (default) | responsiveDisplay | call | app | video | demandGen | demandGenVideo"
18359
18466
  },
18360
- headlines: { type: "string", description: "Comma-separated headlines (RSA/RDA/demandGen/demandGenVideo/app)" },
18467
+ headlines: {
18468
+ type: "string",
18469
+ description: `Headlines (RSA/RDA/demandGen/demandGenVideo/app) \u2014 ${AD_COPY_LIST_USAGE}`
18470
+ },
18361
18471
  descriptions: {
18362
18472
  type: "string",
18363
- description: "Comma-separated descriptions (RSA/RDA/demandGen/demandGenVideo/app)"
18473
+ description: `Descriptions (RSA/RDA/demandGen/demandGenVideo/app) \u2014 ${AD_COPY_LIST_USAGE}`
18364
18474
  },
18365
18475
  path1: { type: "string" },
18366
18476
  path2: { type: "string" },
@@ -18371,7 +18481,7 @@ var adContentArgs = {
18371
18481
  },
18372
18482
  "long-headlines": {
18373
18483
  type: "string",
18374
- description: "Comma-separated long headlines, up to 5 of 90 chars (demandGenVideo \u2014 no other format has them)"
18484
+ description: `Long headlines, up to 5 of 90 chars (demandGenVideo \u2014 no other format has them) \u2014 ${AD_COPY_LIST_USAGE}`
18375
18485
  },
18376
18486
  "business-name": { type: "string", description: "Business name (responsiveDisplay/demandGen/demandGenVideo)" },
18377
18487
  "marketing-images": {
@@ -18422,16 +18532,22 @@ var adsCommand = defineCommand31({
18422
18532
  ...adContentArgs,
18423
18533
  status: { type: "string" }
18424
18534
  },
18425
- run: async ({ args: args5 }) => {
18535
+ run: async ({ args: parsedArgs, rawArgs }) => {
18536
+ const args5 = withRepeatedCopyFlags(parsedArgs, rawArgs);
18426
18537
  const customerId = requireCustomerId(args5);
18427
18538
  const file = loadJsonFileArg(args5.file);
18428
18539
  const format = args5.format ?? "responsiveSearch";
18429
18540
  const content = adContentFromFlags(args5, format, file.content);
18430
- await stageCreate("google.ad.create", customerId, {
18431
- adGroup: requireStringFlag(args5["ad-group-ref"] ?? file.adGroup, "--ad-group-ref"),
18432
- status: args5.status ?? file.status,
18433
- content
18434
- });
18541
+ await stageCreate(
18542
+ "google.ad.create",
18543
+ customerId,
18544
+ {
18545
+ adGroup: requireStringFlag(args5["ad-group-ref"] ?? file.adGroup, "--ad-group-ref"),
18546
+ status: args5.status ?? file.status,
18547
+ content
18548
+ },
18549
+ copyListSplitHints(args5)
18550
+ );
18435
18551
  }
18436
18552
  }),
18437
18553
  update: defineCommand31({
@@ -18440,7 +18556,8 @@ var adsCommand = defineCommand31({
18440
18556
  description: "Stage an ad update. Any content change needs --format plus EVERY required field of that format (or --file with the complete content) \u2014 a field you send replaces its current value wholesale, so a list you pass must already include the entries you want to keep. Optional asset lists you omit entirely are left untouched on the ad. Status-only changes need neither --format nor content flags."
18441
18557
  },
18442
18558
  args: { ...customerIdArg, ...fileArg, ...adContentArgs, status: { type: "string" } },
18443
- run: async ({ args: args5 }) => {
18559
+ run: async ({ args: parsedArgs, rawArgs }) => {
18560
+ const args5 = withRepeatedCopyFlags(parsedArgs, rawArgs);
18444
18561
  const customerId = requireCustomerId(args5);
18445
18562
  const file = loadJsonFileArg(args5.file);
18446
18563
  const format = args5.format;
@@ -18454,7 +18571,8 @@ var adsCommand = defineCommand31({
18454
18571
  await stageUpdate("google.ad.update", customerId, requireTarget(args5, "ad"), payload, [
18455
18572
  ...payload.content !== void 0 ? [
18456
18573
  "Each content field you sent replaces its current value wholesale at publish \u2014 re-check that every headline, description, final URL and asset you want to KEEP is in this payload, not just the ones you changed."
18457
- ] : []
18574
+ ] : [],
18575
+ ...format ? copyListSplitHints(args5) : []
18458
18576
  ]);
18459
18577
  }
18460
18578
  }),
@@ -18483,9 +18601,9 @@ var assetGroupsCommand = defineCommand31({
18483
18601
  },
18484
18602
  name: { type: "string", description: "Asset group name" },
18485
18603
  "final-urls": { type: "string", description: "Comma-separated final URLs" },
18486
- headlines: { type: "string", description: "Comma-separated headlines (3\u201315, \u226430 chars)" },
18487
- "long-headlines": { type: "string", description: "Comma-separated long headlines (1\u20135, \u226490 chars)" },
18488
- descriptions: { type: "string", description: "Comma-separated descriptions (2\u20135, \u226490 chars)" },
18604
+ headlines: { type: "string", description: `Headlines (3\u201315, \u226430 chars) \u2014 ${AD_COPY_LIST_USAGE}` },
18605
+ "long-headlines": { type: "string", description: `Long headlines (1\u20135, \u226490 chars) \u2014 ${AD_COPY_LIST_USAGE}` },
18606
+ descriptions: { type: "string", description: `Descriptions (2\u20135, \u226490 chars) \u2014 ${AD_COPY_LIST_USAGE}` },
18489
18607
  "business-name": { type: "string" },
18490
18608
  "image-assets": { type: "string", description: "Comma-separated marketing image asset refs (1.91:1)" },
18491
18609
  "square-image-assets": {
@@ -18499,11 +18617,12 @@ var assetGroupsCommand = defineCommand31({
18499
18617
  "logo-assets": { type: "string", description: "Comma-separated logo image asset refs" },
18500
18618
  status: { type: "string", description: "ENABLED | PAUSED (default PAUSED)" }
18501
18619
  },
18502
- run: async ({ args: args5 }) => {
18620
+ run: async ({ args: parsedArgs, rawArgs }) => {
18621
+ const args5 = withRepeatedCopyFlags(parsedArgs, rawArgs);
18503
18622
  const customerId = requireCustomerId(args5);
18504
- const headlines = listFlag(args5.headlines);
18505
- const longHeadlines = listFlag(args5["long-headlines"]);
18506
- const descriptions = listFlag(args5.descriptions);
18623
+ const headlines = copyListFlag(args5.headlines, "headlines");
18624
+ const longHeadlines = copyListFlag(args5["long-headlines"], "long-headlines");
18625
+ const descriptions = copyListFlag(args5.descriptions, "descriptions");
18507
18626
  const payload = mergePayload(loadJsonFileArg(args5.file), {
18508
18627
  campaign: args5["campaign-ref"],
18509
18628
  name: args5.name,
@@ -18518,7 +18637,7 @@ var assetGroupsCommand = defineCommand31({
18518
18637
  logoAssets: listFlag(args5["logo-assets"]),
18519
18638
  status: args5.status
18520
18639
  });
18521
- await stageCreate("google.assetGroup.create", customerId, payload);
18640
+ await stageCreate("google.assetGroup.create", customerId, payload, copyListSplitHints(args5));
18522
18641
  }
18523
18642
  }),
18524
18643
  update: defineCommand31({