@koda-sl/baker-cli 0.291.0 → 0.292.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
@@ -10681,11 +10681,11 @@ function parseKeywordEntry(raw, defaultMatch) {
10681
10681
  function rawTextEntries(value, rawArgs) {
10682
10682
  return repeatedValues(rawArgs, "text", value).flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
10683
10683
  }
10684
- function rawFileEntries(path44) {
10685
- if (typeof path44 !== "string" || path44.length === 0) {
10684
+ function rawFileEntries(path45) {
10685
+ if (typeof path45 !== "string" || path45.length === 0) {
10686
10686
  return [];
10687
10687
  }
10688
- return readFileSync2(path44, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
10688
+ return readFileSync2(path45, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
10689
10689
  }
10690
10690
  function keywordEntries(args, rawArgs) {
10691
10691
  const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
@@ -10708,14 +10708,14 @@ function keywordEntries(args, rawArgs) {
10708
10708
  }
10709
10709
  return entries;
10710
10710
  }
10711
- function loadJsonFileArg(path44) {
10712
- if (typeof path44 !== "string" || path44.length === 0) {
10711
+ function loadJsonFileArg(path45) {
10712
+ if (typeof path45 !== "string" || path45.length === 0) {
10713
10713
  return {};
10714
10714
  }
10715
- const inline = path44.trimStart().startsWith("{");
10716
- const source = inline ? "inline JSON" : path44;
10715
+ const inline = path45.trimStart().startsWith("{");
10716
+ const source = inline ? "inline JSON" : path45;
10717
10717
  try {
10718
- const parsed = JSON.parse(inline ? path44 : readFileSync2(path44, "utf8"));
10718
+ const parsed = JSON.parse(inline ? path45 : readFileSync2(path45, "utf8"));
10719
10719
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
10720
10720
  failWriteValidation(`${source} must contain a JSON object`);
10721
10721
  }
@@ -10852,10 +10852,10 @@ async function stageUpdate(kind, customerId, target, payload, hints2) {
10852
10852
  async function stageTarget(kind, customerId, target, hints2) {
10853
10853
  await stageGoogleOp({ kind, customerId, target }, hints2);
10854
10854
  }
10855
- async function draftAction(path44, body, chat) {
10855
+ async function draftAction(path45, body, chat) {
10856
10856
  try {
10857
10857
  const chatId = resolveChatId(chat);
10858
- const response = await apiPost(path44, { chatId, ...body });
10858
+ const response = await apiPost(path45, { chatId, ...body });
10859
10859
  writeJsonEnvelope(response);
10860
10860
  } catch (err) {
10861
10861
  handleGoogleError(err);
@@ -15271,17 +15271,17 @@ function ignoredColumnHints(columns) {
15271
15271
  `Ignored ${columns.length} column(s) that are not part of a conversion: ${columns.join(", ")}. A conversion carries an order id, a date, and something to match on: a click id (gclid / gbraid / wbraid), an email, a phone, or all four of givenName + familyName + country + postalCode. Optionally a value + currency, conversionCount, and eventSource (WEB / APP / IN_STORE / PHONE / OTHER). A column named just \`Name\` is ignored on purpose \u2014 Google needs the first and last name separately, so split it before re-running.`
15272
15272
  ];
15273
15273
  }
15274
- function readRowsFile(path44) {
15275
- const inline = path44.trimStart().startsWith("[") || path44.trimStart().startsWith("{");
15274
+ function readRowsFile(path45) {
15275
+ const inline = path45.trimStart().startsWith("[") || path45.trimStart().startsWith("{");
15276
15276
  let text2;
15277
15277
  try {
15278
- text2 = inline ? path44 : readFileSync4(path44, "utf8");
15278
+ text2 = inline ? path45 : readFileSync4(path45, "utf8");
15279
15279
  } catch (err) {
15280
- failWriteValidation(`could not read ${path44}: ${err instanceof Error ? err.message : String(err)}`);
15280
+ failWriteValidation(`could not read ${path45}: ${err instanceof Error ? err.message : String(err)}`);
15281
15281
  }
15282
15282
  const parsed = parseConversionRowsText(text2);
15283
15283
  if (!parsed.ok) {
15284
- failWriteValidation(`${inline ? "the conversions passed inline" : path44}: ${parsed.error}`);
15284
+ failWriteValidation(`${inline ? "the conversions passed inline" : path45}: ${parsed.error}`);
15285
15285
  }
15286
15286
  return { rows: parsed.rows, ignoredColumns: parsed.ignoredColumns };
15287
15287
  }
@@ -17120,19 +17120,19 @@ function failWriteValidation2(message) {
17120
17120
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
17121
17121
  process.exit(1);
17122
17122
  }
17123
- function loadJsonFileArg2(path44) {
17124
- if (typeof path44 !== "string" || path44.length === 0) {
17123
+ function loadJsonFileArg2(path45) {
17124
+ if (typeof path45 !== "string" || path45.length === 0) {
17125
17125
  return {};
17126
17126
  }
17127
17127
  try {
17128
- const parsed = JSON.parse(readFileSync5(path44, "utf8"));
17128
+ const parsed = JSON.parse(readFileSync5(path45, "utf8"));
17129
17129
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
17130
- failWriteValidation2(`${path44} must contain a JSON object`);
17130
+ failWriteValidation2(`${path45} must contain a JSON object`);
17131
17131
  }
17132
17132
  return parsed;
17133
17133
  } catch (err) {
17134
17134
  if (err instanceof SyntaxError) {
17135
- failWriteValidation2(`${path44} is not valid JSON: ${err.message}`);
17135
+ failWriteValidation2(`${path45} is not valid JSON: ${err.message}`);
17136
17136
  }
17137
17137
  throw err;
17138
17138
  }
@@ -17217,15 +17217,15 @@ function parseLocaleFlag(value) {
17217
17217
  }
17218
17218
  return { language: match[1], country: match[2].toUpperCase() };
17219
17219
  }
17220
- function loadTargetingFileArg(path44) {
17221
- if (typeof path44 !== "string" || path44.length === 0) {
17220
+ function loadTargetingFileArg(path45) {
17221
+ if (typeof path45 !== "string" || path45.length === 0) {
17222
17222
  return void 0;
17223
17223
  }
17224
- const parsed = loadJsonFileArg2(path44);
17224
+ const parsed = loadJsonFileArg2(path45);
17225
17225
  const criteria = parsed.targetingCriteria ?? parsed;
17226
17226
  if (!criteria.include) {
17227
17227
  failWriteValidation2(
17228
- `${path44} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
17228
+ `${path45} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
17229
17229
  );
17230
17230
  }
17231
17231
  return criteria;
@@ -17260,14 +17260,14 @@ function parseCsvLine(line) {
17260
17260
  cells.push(current);
17261
17261
  return cells.map((cell3) => cell3.trim());
17262
17262
  }
17263
- function parseListFileArg(path44, maxRows) {
17264
- if (typeof path44 !== "string" || path44.length === 0) {
17263
+ function parseListFileArg(path45, maxRows) {
17264
+ if (typeof path45 !== "string" || path45.length === 0) {
17265
17265
  return void 0;
17266
17266
  }
17267
- const raw = readFileSync5(path44, "utf8");
17267
+ const raw = readFileSync5(path45, "utf8");
17268
17268
  const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
17269
17269
  if (lines.length < 2) {
17270
- failWriteValidation2(`${path44} needs a header row and at least one data row`);
17270
+ failWriteValidation2(`${path45} needs a header row and at least one data row`);
17271
17271
  }
17272
17272
  const columns = parseCsvLine(lines[0]).map((column) => column.trim());
17273
17273
  const rows = [];
@@ -17286,7 +17286,7 @@ function parseListFileArg(path44, maxRows) {
17286
17286
  }
17287
17287
  }
17288
17288
  if (rows.length > maxRows) {
17289
- failWriteValidation2(`${path44} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
17289
+ failWriteValidation2(`${path45} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
17290
17290
  }
17291
17291
  return { columns, rows };
17292
17292
  }
@@ -17382,11 +17382,11 @@ function readPositionals(args) {
17382
17382
  function splitIdList(raw) {
17383
17383
  return raw.split(",").map((id) => id.trim()).filter(Boolean);
17384
17384
  }
17385
- function idsFileEntries(path44) {
17386
- if (typeof path44 !== "string" || path44.length === 0) {
17385
+ function idsFileEntries(path45) {
17386
+ if (typeof path45 !== "string" || path45.length === 0) {
17387
17387
  return [];
17388
17388
  }
17389
- return readFileSync5(path44, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
17389
+ return readFileSync5(path45, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
17390
17390
  }
17391
17391
  function requireTargets(args, entity) {
17392
17392
  const positionals = readPositionals(args);
@@ -20106,9 +20106,9 @@ function compactRow(row) {
20106
20106
  ...destination.postUrn ? { postUrn: destination.postUrn } : {}
20107
20107
  };
20108
20108
  }
20109
- function readPath(row, path44) {
20109
+ function readPath(row, path45) {
20110
20110
  let current = row;
20111
- for (const segment of path44.split(".")) {
20111
+ for (const segment of path45.split(".")) {
20112
20112
  const record = asRecord2(current);
20113
20113
  if (!record) return void 0;
20114
20114
  current = record[segment];
@@ -20118,10 +20118,10 @@ function readPath(row, path44) {
20118
20118
  function projectFields(rows, paths) {
20119
20119
  return rows.map((row) => {
20120
20120
  const projected = {};
20121
- for (const path44 of paths) {
20122
- const value = readPath(row, path44);
20121
+ for (const path45 of paths) {
20122
+ const value = readPath(row, path45);
20123
20123
  if (value !== void 0) {
20124
- projected[path44] = value;
20124
+ projected[path45] = value;
20125
20125
  }
20126
20126
  }
20127
20127
  return projected;
@@ -21717,11 +21717,11 @@ var updateStatusSchema = z29.enum(UPDATE_STATUSES);
21717
21717
  function currencyMinimums2(currencyCode) {
21718
21718
  return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
21719
21719
  }
21720
- function validateDailyBudgetFloor(money, ctx, path44) {
21720
+ function validateDailyBudgetFloor(money, ctx, path45) {
21721
21721
  if (money?.currencyCode) {
21722
21722
  const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
21723
21723
  if (Number(money.amount) < min) {
21724
- ctx.addIssue({ code: "custom", path: path44, message: `below the ${min} ${money.currencyCode} daily minimum` });
21724
+ ctx.addIssue({ code: "custom", path: path45, message: `below the ${min} ${money.currencyCode} daily minimum` });
21725
21725
  }
21726
21726
  }
21727
21727
  }
@@ -22481,19 +22481,19 @@ function failWriteValidation3(message) {
22481
22481
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
22482
22482
  process.exit(1);
22483
22483
  }
22484
- function loadJsonFileArg3(path44) {
22485
- if (typeof path44 !== "string" || path44.length === 0) {
22484
+ function loadJsonFileArg3(path45) {
22485
+ if (typeof path45 !== "string" || path45.length === 0) {
22486
22486
  return {};
22487
22487
  }
22488
22488
  try {
22489
- const parsed = JSON.parse(readFileSync9(path44, "utf8"));
22489
+ const parsed = JSON.parse(readFileSync9(path45, "utf8"));
22490
22490
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
22491
- failWriteValidation3(`${path44} must contain a JSON object`);
22491
+ failWriteValidation3(`${path45} must contain a JSON object`);
22492
22492
  }
22493
22493
  return parsed;
22494
22494
  } catch (err) {
22495
22495
  if (err instanceof SyntaxError) {
22496
- failWriteValidation3(`${path44} is not valid JSON: ${err.message}`);
22496
+ failWriteValidation3(`${path45} is not valid JSON: ${err.message}`);
22497
22497
  }
22498
22498
  throw err;
22499
22499
  }
@@ -28098,12 +28098,12 @@ function flowExists(slug) {
28098
28098
  return existsSync3(join2(flowsDir(), slug, "_data.json"));
28099
28099
  }
28100
28100
  function readFlowTree(slug) {
28101
- const path44 = join2(flowsDir(), slug, "_data.json");
28102
- if (!existsSync3(path44)) {
28101
+ const path45 = join2(flowsDir(), slug, "_data.json");
28102
+ if (!existsSync3(path45)) {
28103
28103
  failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
28104
28104
  }
28105
28105
  try {
28106
- return JSON.parse(readFileSync11(path44, "utf-8"));
28106
+ return JSON.parse(readFileSync11(path45, "utf-8"));
28107
28107
  } catch (error) {
28108
28108
  failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
28109
28109
  }
@@ -30010,11 +30010,11 @@ function unwrap(response) {
30010
30010
  }
30011
30011
  return response.data;
30012
30012
  }
30013
- async function readAvatars(path44, params) {
30014
- return unwrap(await apiGet(path44, params));
30013
+ async function readAvatars(path45, params) {
30014
+ return unwrap(await apiGet(path45, params));
30015
30015
  }
30016
- async function writeAvatars(path44, body) {
30017
- return unwrap(await apiPost(path44, body));
30016
+ async function writeAvatars(path45, body) {
30017
+ return unwrap(await apiPost(path45, body));
30018
30018
  }
30019
30019
 
30020
30020
  // src/commands/avatars/create.ts
@@ -30848,12 +30848,12 @@ function missingFontFiles(urls, available) {
30848
30848
  function planFontAdoption(sources, families) {
30849
30849
  const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
30850
30850
  const byFamily = /* @__PURE__ */ new Map();
30851
- for (const { path: path44, source } of sources) {
30852
- const dir = posix.dirname(path44);
30851
+ for (const { path: path45, source } of sources) {
30852
+ const dir = posix.dirname(path45);
30853
30853
  for (const face of declaredFontFaces(source)) {
30854
30854
  if (!wanted.has(face.family)) continue;
30855
30855
  const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
30856
- perFile.set(path44, [...perFile.get(path44) ?? [], rebaseFontFaceSrc(face.block, dir)]);
30856
+ perFile.set(path45, [...perFile.get(path45) ?? [], rebaseFontFaceSrc(face.block, dir)]);
30857
30857
  byFamily.set(face.family, perFile);
30858
30858
  }
30859
30859
  }
@@ -39542,6 +39542,9 @@ function buildHistoryHints(tests, landing) {
39542
39542
  }
39543
39543
  function buildStartHints(staged) {
39544
39544
  const hints2 = [];
39545
+ for (const entry of staged.drift) {
39546
+ hints2.push(driftHint(staged, entry));
39547
+ }
39545
39548
  if (staged.composition && staged.composition.forked.length === 0) {
39546
39549
  hints2.push(
39547
39550
  `\u201C${staged.variant}\u201D does not have its own copy of any section \u2014 it renders exactly the same page as \u201C${staged.landing}\u201D, so this test cannot find anything. Fork the section you want to test (\`baker landing variant ${staged.landing} --fork Hero.astro\`) and edit only that file.`
@@ -39555,6 +39558,17 @@ function buildStartHints(staged) {
39555
39558
  }
39556
39559
  return hints2;
39557
39560
  }
39561
+ function driftHint(staged, entry) {
39562
+ const suffix = ` Check both versions before reading that number, and give the new version's step its own name if it is not the same action (\`baker analytics conversions --candidates\`).`;
39563
+ if (entry.reason === "trigger_differs") {
39564
+ return `\u201C${staged.landing}\u201D and \u201C${staged.variant}\u201D both count \`${entry.name}\`, but one fires it when the element is **scrolled into view** and the other when the visitor **does something**. Those are two different questions with one answer, and the test will report a difference that is the trigger rather than the page.${suffix}`;
39565
+ }
39566
+ if (entry.reason === "variant_only_missing" || entry.reason === "control_only_missing") {
39567
+ const missing = entry.reason === "variant_only_missing" ? staged.variant : staged.landing;
39568
+ return `Only one version fires \`${entry.name}\` \u2014 \u201C${missing}\u201D does not declare it at all, so it can only ever read zero for that conversion and the comparison will look like a collapse.${suffix}`;
39569
+ }
39570
+ return `Both versions count \`${entry.name}\`, and the section the test changed is what fires it \u2014 the markup around it is not the same on the two pages. If the new version fires it on a different action (a block that is visible from the start rather than one the visitor opens), that conversion is comparing two different things.${suffix}`;
39571
+ }
39558
39572
 
39559
39573
  // ../api/src/experiments/composition.ts
39560
39574
  import { z as z35 } from "zod";
@@ -40112,6 +40126,72 @@ async function readPageInternalId(root, slug) {
40112
40126
  }
40113
40127
  }
40114
40128
 
40129
+ // src/commands/experiment/measurement.ts
40130
+ import { readFile as readFile24 } from "fs/promises";
40131
+ import path31 from "path";
40132
+ function eventDeclarationsIn(source) {
40133
+ const found = [];
40134
+ const pattern = /\b(data-baker-(?:view|event))\s*=\s*["']([^"']+)["']/g;
40135
+ for (const match of source.matchAll(pattern)) {
40136
+ const at = match.index;
40137
+ const opens = source.lastIndexOf("<", at);
40138
+ const closes = source.indexOf(">", at);
40139
+ found.push({
40140
+ name: match[2],
40141
+ attribute: match[1],
40142
+ markup: normalise(source.slice(opens < 0 ? at : opens, closes < 0 ? at + match[0].length : closes + 1))
40143
+ });
40144
+ }
40145
+ return found;
40146
+ }
40147
+ function normalise(markup) {
40148
+ return markup.replace(/\s+/g, " ").trim();
40149
+ }
40150
+ function measurementDrift(control, variant) {
40151
+ const names = [...new Set([...control, ...variant].map((entry) => entry.name))].sort();
40152
+ return names.flatMap((name) => {
40153
+ const left = control.filter((entry) => entry.name === name);
40154
+ const right = variant.filter((entry) => entry.name === name);
40155
+ if (right.length === 0) return [{ name, reason: "variant_only_missing" }];
40156
+ if (left.length === 0) return [{ name, reason: "control_only_missing" }];
40157
+ if (setOf(left, (entry) => entry.attribute) !== setOf(right, (entry) => entry.attribute)) {
40158
+ return [{ name, reason: "trigger_differs" }];
40159
+ }
40160
+ if (setOf(left, (entry) => entry.markup) !== setOf(right, (entry) => entry.markup)) {
40161
+ return [{ name, reason: "markup_differs" }];
40162
+ }
40163
+ return [];
40164
+ });
40165
+ }
40166
+ function setOf(entries, of) {
40167
+ return [...new Set(entries.map(of))].sort().join("\n");
40168
+ }
40169
+ async function readMeasurementDrift(root, controlSlug, composition) {
40170
+ if (!composition) return [];
40171
+ try {
40172
+ const pageSections = sectionsOf([...await landingGraphFiles(root, controlSlug)], controlSlug);
40173
+ const shared = new Set(composition.shared);
40174
+ const [control, variant] = await Promise.all([
40175
+ declarationsOf(
40176
+ root,
40177
+ pageSections.filter((file) => !shared.has(file))
40178
+ ),
40179
+ declarationsOf(root, composition.forked)
40180
+ ]);
40181
+ return measurementDrift(control, variant);
40182
+ } catch {
40183
+ return [];
40184
+ }
40185
+ }
40186
+ function sectionsOf(files, slug) {
40187
+ const prefix = `src/pages/${slug}/`;
40188
+ return files.filter((file) => file.startsWith(prefix) && !file.slice(prefix.length).startsWith("index."));
40189
+ }
40190
+ async function declarationsOf(root, files) {
40191
+ const sources = await Promise.all(files.map((file) => readFile24(path31.join(root, file), "utf8").catch(() => "")));
40192
+ return sources.flatMap(eventDeclarationsIn);
40193
+ }
40194
+
40115
40195
  // src/commands/experiment/update.ts
40116
40196
  function buildUpdateRequest(args, rawArgs) {
40117
40197
  const expect = args.expect === void 0 ? void 0 : String(args.expect);
@@ -40302,16 +40382,10 @@ var startCommand = defineCommand119({
40302
40382
  }
40303
40383
  const evidence = parseEvidence(args.evidence, rawArgs);
40304
40384
  if (evidence && "error" in evidence) fail5(evidence.error);
40305
- const composition = await readComposition(process.cwd(), String(args.landing), String(args.variant));
40306
- const [controlForms, variantForms] = await Promise.all([
40307
- readForms(process.cwd(), String(args.landing)),
40308
- readForms(process.cwd(), String(args.variant))
40309
- ]);
40310
- const newForms = formsOnlyIn(variantForms, controlForms);
40311
- const [landingInternalId, variantInternalId] = await Promise.all([
40312
- readPageInternalId(process.cwd(), String(args.landing)),
40313
- readPageInternalId(process.cwd(), String(args.variant))
40314
- ]);
40385
+ const { composition, newForms, drift, landingInternalId, variantInternalId } = await readStartContext(
40386
+ String(args.landing),
40387
+ String(args.variant)
40388
+ );
40315
40389
  try {
40316
40390
  const response = await apiPost("/api/experiments/start", {
40317
40391
  landingSlug: String(args.landing),
@@ -40341,7 +40415,8 @@ var startCommand = defineCommand119({
40341
40415
  goal: String(response.data.goal),
40342
40416
  variantGoal: args.variantGoal === void 0 ? void 0 : String(args.variantGoal),
40343
40417
  composition,
40344
- newForms
40418
+ newForms,
40419
+ drift
40345
40420
  }),
40346
40421
  "Staged. The split starts when this session is published \u2014 publishing is the review of the new variant.",
40347
40422
  "Check it as often as you like \u2014 `baker experiment status` is safe to read at any moment and holds its error rate however often you ask. What is never safe is reading the numbers instead of the verdict: `keep_running` means this test cannot yet tell the two pages apart, whatever the rates happen to say today."
@@ -40735,6 +40810,29 @@ var experimentCommand = defineCommand119({
40735
40810
  fold: foldCommand
40736
40811
  }
40737
40812
  });
40813
+ async function readStartContext(landing, variant) {
40814
+ const root = process.cwd();
40815
+ const composition = await readComposition(root, landing, variant);
40816
+ const [controlForms, variantForms, landingInternalId, variantInternalId] = await Promise.all([
40817
+ readForms(root, landing),
40818
+ readForms(root, variant),
40819
+ // Each page's own identity, so a renamed folder cannot detach the test —
40820
+ // the variant has no row in Baker until this session is published.
40821
+ readPageInternalId(root, landing),
40822
+ readPageInternalId(root, variant)
40823
+ ]);
40824
+ return {
40825
+ composition,
40826
+ newForms: formsOnlyIn(variantForms, controlForms),
40827
+ // Whether the two versions still fire the company's conversions on the same
40828
+ // action. Nothing downstream can ask this — a split can be perfect while the
40829
+ // two pages disagree about what an event means — and the only place the
40830
+ // answer exists is the workspace this command is already standing in.
40831
+ drift: await readMeasurementDrift(root, landing, composition),
40832
+ landingInternalId,
40833
+ variantInternalId
40834
+ };
40835
+ }
40738
40836
 
40739
40837
  // src/commands/flows/index.ts
40740
40838
  import { defineCommand as defineCommand124 } from "citty";
@@ -41032,10 +41130,10 @@ function parseValueExpression(raw) {
41032
41130
  return parts.map(parsePart);
41033
41131
  }
41034
41132
  function trackingFieldIds() {
41035
- const path44 = join4(flowsDir(), "..", "tracking.ts");
41036
- if (!existsSync6(path44)) return null;
41133
+ const path45 = join4(flowsDir(), "..", "tracking.ts");
41134
+ if (!existsSync6(path45)) return null;
41037
41135
  try {
41038
- const source = readFileSync13(path44, "utf-8");
41136
+ const source = readFileSync13(path45, "utf-8");
41039
41137
  const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
41040
41138
  if (!block2) return null;
41041
41139
  const ids = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
@@ -41576,13 +41674,13 @@ function specsFromFile(parsed) {
41576
41674
  return `${destField}${type}=${entry?.value ?? ""}`;
41577
41675
  });
41578
41676
  }
41579
- function readSpecFile(path44) {
41677
+ function readSpecFile(path45) {
41580
41678
  let raw;
41581
41679
  try {
41582
- raw = path44 === "-" ? readFileSync14(0, "utf-8") : readFileSync14(path44, "utf-8");
41680
+ raw = path45 === "-" ? readFileSync14(0, "utf-8") : readFileSync14(path45, "utf-8");
41583
41681
  } catch (error) {
41584
41682
  refuse(
41585
- `Could not read ${path44 === "-" ? "the mapping from stdin" : `"${path44}"`}: ${error instanceof Error ? error.message : String(error)}`
41683
+ `Could not read ${path45 === "-" ? "the mapping from stdin" : `"${path45}"`}: ${error instanceof Error ? error.message : String(error)}`
41586
41684
  );
41587
41685
  }
41588
41686
  let parsed;
@@ -41590,7 +41688,7 @@ function readSpecFile(path44) {
41590
41688
  parsed = JSON.parse(raw);
41591
41689
  } catch (error) {
41592
41690
  refuse(
41593
- `${path44 === "-" ? "stdin" : `"${path44}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
41691
+ `${path45 === "-" ? "stdin" : `"${path45}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
41594
41692
  'Expected { "map": { "<destField>": "<value>", \u2026 } }'
41595
41693
  );
41596
41694
  }
@@ -41860,18 +41958,18 @@ var ARRAY_FIELDS = [
41860
41958
  "tagIds"
41861
41959
  ];
41862
41960
  var ARRAY_OWNERS = ["", "body"];
41863
- function dropUnsetOptionals(sideEffect, path44) {
41961
+ function dropUnsetOptionals(sideEffect, path45) {
41864
41962
  return OPTIONAL_STRINGS.flatMap((key) => {
41865
41963
  if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
41866
41964
  delete sideEffect[key];
41867
- return [{ path: path44, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
41965
+ return [{ path: path45, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
41868
41966
  });
41869
41967
  }
41870
- function fillNulledArrays(target, prefix, path44) {
41968
+ function fillNulledArrays(target, prefix, path45) {
41871
41969
  return ARRAY_FIELDS.flatMap((key) => {
41872
41970
  if (!(key in target) || target[key] !== null) return [];
41873
41971
  target[key] = [];
41874
- return [{ path: path44, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
41972
+ return [{ path: path45, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
41875
41973
  });
41876
41974
  }
41877
41975
  function sideEffectsOf(node) {
@@ -41881,13 +41979,13 @@ function sideEffectsOf(node) {
41881
41979
  );
41882
41980
  }
41883
41981
  function normalizeSideEffect(sideEffect, where) {
41884
- const path44 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
41982
+ const path45 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
41885
41983
  const arrays = ARRAY_OWNERS.flatMap((owner) => {
41886
41984
  const target = owner ? sideEffect[owner] : sideEffect;
41887
41985
  if (!target || typeof target !== "object") return [];
41888
- return fillNulledArrays(target, owner ? `${owner}.` : "", path44);
41986
+ return fillNulledArrays(target, owner ? `${owner}.` : "", path45);
41889
41987
  });
41890
- return [...dropUnsetOptionals(sideEffect, path44), ...arrays];
41988
+ return [...dropUnsetOptionals(sideEffect, path45), ...arrays];
41891
41989
  }
41892
41990
  function normalizeFlowTree(tree) {
41893
41991
  const changes = [];
@@ -42439,10 +42537,10 @@ async function stageOps(ops) {
42439
42537
  handleError2(err);
42440
42538
  }
42441
42539
  }
42442
- async function draftAction2(path44, body, chat) {
42540
+ async function draftAction2(path45, body, chat) {
42443
42541
  const chatId = resolveChatId(chat);
42444
42542
  try {
42445
- const data = await apiPost(path44, { chatId, ...body });
42543
+ const data = await apiPost(path45, { chatId, ...body });
42446
42544
  writeJsonEnvelope({ ok: true, data });
42447
42545
  return data;
42448
42546
  } catch (err) {
@@ -44856,7 +44954,7 @@ function cropSprite(input, region) {
44856
44954
 
44857
44955
  // src/lib/image/io.ts
44858
44956
  import { randomBytes } from "crypto";
44859
- import { glob as fsGlob, readFile as readFile24, rename, stat as stat6, writeFile as writeFile12 } from "fs/promises";
44957
+ import { glob as fsGlob, readFile as readFile25, rename, stat as stat6, writeFile as writeFile12 } from "fs/promises";
44860
44958
  import { dirname as dirname2, extname as extname2, join as join5, resolve as resolve4 } from "path";
44861
44959
  var REMOTE_RE = /^https?:\/\//i;
44862
44960
  var GLOB_RE = /[*?[\]{}]/;
@@ -44889,11 +44987,11 @@ async function readImageBuffer(pathOrUrl) {
44889
44987
  const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
44890
44988
  return buffer;
44891
44989
  }
44892
- return readFile24(pathOrUrl);
44990
+ return readFile25(pathOrUrl);
44893
44991
  }
44894
- async function isDirectory(path44) {
44992
+ async function isDirectory(path45) {
44895
44993
  try {
44896
- const s = await stat6(path44);
44994
+ const s = await stat6(path45);
44897
44995
  return s.isDirectory();
44898
44996
  } catch {
44899
44997
  return false;
@@ -45212,13 +45310,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
45212
45310
  }
45213
45311
  function disambiguate(paths) {
45214
45312
  const taken = /* @__PURE__ */ new Set();
45215
- return paths.map((path44) => {
45216
- if (!taken.has(path44)) {
45217
- taken.add(path44);
45218
- return path44;
45313
+ return paths.map((path45) => {
45314
+ if (!taken.has(path45)) {
45315
+ taken.add(path45);
45316
+ return path45;
45219
45317
  }
45220
- const ext = extname3(path44);
45221
- const stem = path44.slice(0, path44.length - ext.length);
45318
+ const ext = extname3(path45);
45319
+ const stem = path45.slice(0, path45.length - ext.length);
45222
45320
  let n = 2;
45223
45321
  while (taken.has(`${stem}-${n}${ext}`)) n += 1;
45224
45322
  const unique = `${stem}-${n}${ext}`;
@@ -45344,10 +45442,10 @@ async function runDownloads(plan) {
45344
45442
  const paths = disambiguate(fetched.map((item) => item.path));
45345
45443
  const downloaded = [];
45346
45444
  for (const [index, item] of fetched.entries()) {
45347
- const path44 = paths[index] ?? item.path;
45445
+ const path45 = paths[index] ?? item.path;
45348
45446
  try {
45349
- await atomicWrite(path44, item.buffer);
45350
- downloaded.push({ input: item.input, output: path44, bytes: item.buffer.length, contentType: item.contentType });
45447
+ await atomicWrite(path45, item.buffer);
45448
+ downloaded.push({ input: item.input, output: path45, bytes: item.buffer.length, contentType: item.contentType });
45351
45449
  } catch (err) {
45352
45450
  failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
45353
45451
  }
@@ -48311,8 +48409,8 @@ Full guide: __tooling__/docs/tools/baker/images.md`
48311
48409
  import { defineCommand as defineCommand175 } from "citty";
48312
48410
 
48313
48411
  // src/commands/landing/critique.ts
48314
- import { readdir as readdir12, readFile as readFile26, stat as stat8 } from "fs/promises";
48315
- import path33 from "path";
48412
+ import { readdir as readdir12, readFile as readFile27, stat as stat8 } from "fs/promises";
48413
+ import path34 from "path";
48316
48414
  import { defineCommand as defineCommand164 } from "citty";
48317
48415
 
48318
48416
  // src/engine/landing/lib/constants.ts
@@ -49504,13 +49602,13 @@ function describeCounts(findings) {
49504
49602
 
49505
49603
  // src/commands/landing/snapshot.ts
49506
49604
  import { mkdir as mkdir9, rename as rename2, writeFile as writeFile13 } from "fs/promises";
49507
- import path31 from "path";
49605
+ import path32 from "path";
49508
49606
  var CRITIC_VERSION = "4";
49509
49607
  function critiqueCacheDir(projectRoot) {
49510
- return path31.join(projectRoot, ".cache", "landing-critique");
49608
+ return path32.join(projectRoot, ".cache", "landing-critique");
49511
49609
  }
49512
49610
  function snapshotPath(projectRoot, slug) {
49513
- return path31.join(critiqueCacheDir(projectRoot), `${slug}.json`);
49611
+ return path32.join(critiqueCacheDir(projectRoot), `${slug}.json`);
49514
49612
  }
49515
49613
  async function writeCritiqueSnapshot(projectRoot, snapshot) {
49516
49614
  await mkdir9(critiqueCacheDir(projectRoot), { recursive: true });
@@ -49522,8 +49620,8 @@ async function writeCritiqueSnapshot(projectRoot, snapshot) {
49522
49620
  }
49523
49621
 
49524
49622
  // src/commands/landing/source-version.ts
49525
- import { readdir as readdir11, readFile as readFile25, stat as stat7 } from "fs/promises";
49526
- import path32 from "path";
49623
+ import { readdir as readdir11, readFile as readFile26, stat as stat7 } from "fs/promises";
49624
+ import path33 from "path";
49527
49625
  var CRITIQUED_ROOTS = ["src/pages/", "src/components/"];
49528
49626
  async function landingSourceRelPaths(root, slug) {
49529
49627
  const files = await landingGraphFiles(root, slug);
@@ -49532,7 +49630,7 @@ async function landingSourceRelPaths(root, slug) {
49532
49630
  async function readLandingSources(root, slug) {
49533
49631
  const rel = await landingSourceRelPaths(root, slug);
49534
49632
  const out = [];
49535
- for (const r of rel) out.push({ path: r, text: await readFile25(path32.join(root, r), "utf8") });
49633
+ for (const r of rel) out.push({ path: r, text: await readFile26(path33.join(root, r), "utf8") });
49536
49634
  return out;
49537
49635
  }
49538
49636
  async function computeLandingSourceSha(root, slug) {
@@ -49541,7 +49639,7 @@ async function computeLandingSourceSha(root, slug) {
49541
49639
  for (const r of rel) {
49542
49640
  let bytes;
49543
49641
  try {
49544
- bytes = await readFile25(path32.join(root, r));
49642
+ bytes = await readFile26(path33.join(root, r));
49545
49643
  } catch {
49546
49644
  bytes = Buffer.alloc(0);
49547
49645
  }
@@ -49551,16 +49649,16 @@ async function computeLandingSourceSha(root, slug) {
49551
49649
  }
49552
49650
  async function computeLegacyLandingSourceSha(landingDir) {
49553
49651
  const rel = [];
49554
- if (await isFile2(path32.join(landingDir, "index.astro"))) rel.push("index.astro");
49555
- for (const abs of await walkAstro(path32.join(landingDir, "_components"))) {
49556
- rel.push(path32.relative(landingDir, abs).split(path32.sep).join("/"));
49652
+ if (await isFile2(path33.join(landingDir, "index.astro"))) rel.push("index.astro");
49653
+ for (const abs of await walkAstro(path33.join(landingDir, "_components"))) {
49654
+ rel.push(path33.relative(landingDir, abs).split(path33.sep).join("/"));
49557
49655
  }
49558
49656
  rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
49559
49657
  const parts = [];
49560
49658
  for (const r of rel) {
49561
49659
  let bytes;
49562
49660
  try {
49563
- bytes = await readFile25(path32.join(landingDir, r));
49661
+ bytes = await readFile26(path33.join(landingDir, r));
49564
49662
  } catch {
49565
49663
  bytes = Buffer.alloc(0);
49566
49664
  }
@@ -49584,7 +49682,7 @@ async function walkAstro(dir) {
49584
49682
  }
49585
49683
  const out = [];
49586
49684
  for (const entry of entries) {
49587
- const abs = path32.join(dir, entry.name);
49685
+ const abs = path33.join(dir, entry.name);
49588
49686
  if (entry.isDirectory()) out.push(...await walkAstro(abs));
49589
49687
  else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
49590
49688
  }
@@ -49645,7 +49743,7 @@ var critiqueCommand2 = defineCommand164({
49645
49743
  { availableSlugs: await listLandingSlugs(projectRoot) }
49646
49744
  );
49647
49745
  }
49648
- if (!await isDir2(path33.resolve(projectRoot, "src", "pages", slug))) {
49746
+ if (!await isDir2(path34.resolve(projectRoot, "src", "pages", slug))) {
49649
49747
  fail6("NOT_FOUND", `No landing at src/pages/${slug}/`, {
49650
49748
  availableSlugs: await listLandingSlugs(projectRoot)
49651
49749
  });
@@ -49688,7 +49786,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors) {
49688
49786
  const [sources, compositionSha, sourceSha] = await Promise.all([
49689
49787
  readLandingSources(projectRoot, slug),
49690
49788
  computeLandingSourceSha(projectRoot, slug),
49691
- computeLegacyLandingSourceSha(path33.resolve(projectRoot, "src", "pages", slug))
49789
+ computeLegacyLandingSourceSha(path34.resolve(projectRoot, "src", "pages", slug))
49692
49790
  ]);
49693
49791
  const report = critiqueLanding({ slug, sources, brand, competitors });
49694
49792
  let snapshotFailed = false;
@@ -49708,7 +49806,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors) {
49708
49806
  return { slug, report, snapshotFailed };
49709
49807
  }
49710
49808
  async function readCompetitorNames(projectRoot) {
49711
- const dir = path33.join(projectRoot, "src", "content", "competitors");
49809
+ const dir = path34.join(projectRoot, "src", "content", "competitors");
49712
49810
  let entries;
49713
49811
  try {
49714
49812
  entries = (await readdir12(dir)).filter((f) => f.endsWith(".md"));
@@ -49717,9 +49815,9 @@ async function readCompetitorNames(projectRoot) {
49717
49815
  }
49718
49816
  const names = [];
49719
49817
  for (const entry of entries) {
49720
- names.push(path33.basename(entry, ".md").replace(/[-_]+/g, " "));
49818
+ names.push(path34.basename(entry, ".md").replace(/[-_]+/g, " "));
49721
49819
  try {
49722
- const head = (await readFile26(path33.join(dir, entry), "utf8")).slice(0, 2e3);
49820
+ const head = (await readFile27(path34.join(dir, entry), "utf8")).slice(0, 2e3);
49723
49821
  const titled = /^\s*(?:title|name)\s*:\s*["']?([^"'\n]+)["']?\s*$/im.exec(head);
49724
49822
  if (titled?.[1]) names.push(titled[1].trim());
49725
49823
  } catch {
@@ -49729,7 +49827,7 @@ async function readCompetitorNames(projectRoot) {
49729
49827
  }
49730
49828
  async function listLandingSlugs(projectRoot) {
49731
49829
  try {
49732
- const entries = await readdir12(path33.join(projectRoot, "src", "pages"), { withFileTypes: true });
49830
+ const entries = await readdir12(path34.join(projectRoot, "src", "pages"), { withFileTypes: true });
49733
49831
  return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
49734
49832
  } catch {
49735
49833
  return [];
@@ -49881,7 +49979,7 @@ var addCommand = defineCommand165({
49881
49979
 
49882
49980
  // src/commands/landing/inspiration/code.ts
49883
49981
  import { mkdir as mkdir10, writeFile as writeFile14 } from "fs/promises";
49884
- import path34 from "path";
49982
+ import path35 from "path";
49885
49983
  import { defineCommand as defineCommand166 } from "citty";
49886
49984
  registerSchema({
49887
49985
  command: "landing.inspiration.code",
@@ -49904,9 +50002,9 @@ var codeCommand = defineCommand166({
49904
50002
  try {
49905
50003
  const id = args.id;
49906
50004
  const data = await apiGet("/api/landing-inspiration/section-code", { id });
49907
- const dir = path34.join(process.cwd(), ".baker", "inspiration", id);
50005
+ const dir = path35.join(process.cwd(), ".baker", "inspiration", id);
49908
50006
  await mkdir10(dir, { recursive: true });
49909
- const file = path34.join(dir, "section.html");
50007
+ const file = path35.join(dir, "section.html");
49910
50008
  await writeFile14(file, data.html);
49911
50009
  const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
49912
50010
  const fidelity = fidelityHint(data.fidelity);
@@ -49916,7 +50014,7 @@ var codeCommand = defineCommand166({
49916
50014
  ok: true,
49917
50015
  data: {
49918
50016
  id,
49919
- file: path34.relative(process.cwd(), file),
50017
+ file: path35.relative(process.cwd(), file),
49920
50018
  bytes: data.html.length,
49921
50019
  fidelity: data.fidelity,
49922
50020
  reproduction_notes: data.reproductionNotes,
@@ -50348,7 +50446,7 @@ function classifyCaptureFailure(error) {
50348
50446
 
50349
50447
  // src/engine/landing-library/run.ts
50350
50448
  import { mkdir as mkdir11, writeFile as writeFile16 } from "fs/promises";
50351
- import path36 from "path";
50449
+ import path37 from "path";
50352
50450
 
50353
50451
  // ../proxy/src/preflight.ts
50354
50452
  import http from "http";
@@ -51764,9 +51862,9 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
51764
51862
 
51765
51863
  // src/engine/landing-library/report.ts
51766
51864
  import { writeFile as writeFile15 } from "fs/promises";
51767
- import path35 from "path";
51865
+ import path36 from "path";
51768
51866
  async function writeCaptureReport(manifest, outDir) {
51769
- const file = path35.join(outDir, "report.html");
51867
+ const file = path36.join(outDir, "report.html");
51770
51868
  await writeFile15(file, renderReport(manifest));
51771
51869
  return file;
51772
51870
  }
@@ -51945,32 +52043,32 @@ async function reproducePage(args) {
51945
52043
  const { browser, page, outDir, pageUrl, livePageShot } = args;
51946
52044
  const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
51947
52045
  if (!built) return { bundle: null, fidelity: null };
51948
- await writeFile16(path36.join(outDir, "page.html"), built.html);
52046
+ await writeFile16(path37.join(outDir, "page.html"), built.html);
51949
52047
  const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
51950
52048
  wholePage: true,
51951
52049
  timeoutMs: 6e4
51952
52050
  });
51953
52051
  if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
51954
- await writeFile16(path36.join(outDir, "page-rendered.png"), rendered);
52052
+ await writeFile16(path37.join(outDir, "page-rendered.png"), rendered);
51955
52053
  const { score, note } = await scoreFidelity(livePageShot, rendered);
51956
52054
  return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
51957
52055
  }
51958
52056
  async function captureOneSection(args) {
51959
52057
  const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args;
51960
- const dir = path36.join(sectionsDir, String(candidate.index).padStart(2, "0"));
52058
+ const dir = path37.join(sectionsDir, String(candidate.index).padStart(2, "0"));
51961
52059
  await mkdir11(dir, { recursive: true });
51962
52060
  const desktop = await captureSection(page, candidate);
51963
- if (desktop) await writeFile16(path36.join(dir, "desktop.png"), desktop);
52061
+ if (desktop) await writeFile16(path37.join(dir, "desktop.png"), desktop);
51964
52062
  const visualHash = desktop ? await perceptualHash(desktop) : null;
51965
52063
  const motion = await collectMotion(page, candidate.selector);
51966
52064
  const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
51967
52065
  let fidelity = null;
51968
52066
  let fidelityNote;
51969
52067
  if (built) {
51970
- await writeFile16(path36.join(dir, "section.html"), built.html);
52068
+ await writeFile16(path37.join(dir, "section.html"), built.html);
51971
52069
  const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
51972
52070
  if (rendered && desktop) {
51973
- await writeFile16(path36.join(dir, "section-rendered.png"), rendered);
52071
+ await writeFile16(path37.join(dir, "section-rendered.png"), rendered);
51974
52072
  const result = await scoreFidelity(desktop, rendered);
51975
52073
  fidelity = result.score;
51976
52074
  fidelityNote = result.note;
@@ -51978,9 +52076,9 @@ async function captureOneSection(args) {
51978
52076
  }
51979
52077
  return {
51980
52078
  ...candidate,
51981
- desktopShot: desktop ? path36.relative(outDir, path36.join(dir, "desktop.png")) : null,
52079
+ desktopShot: desktop ? path37.relative(outDir, path37.join(dir, "desktop.png")) : null,
51982
52080
  mobileShot: null,
51983
- bundle: built ? path36.relative(outDir, path36.join(dir, "section.html")) : null,
52081
+ bundle: built ? path37.relative(outDir, path37.join(dir, "section.html")) : null,
51984
52082
  fidelity,
51985
52083
  ...fidelityNote ? { fidelityNote } : {},
51986
52084
  ...built ? { cssStats: built.stats } : {},
@@ -51999,9 +52097,9 @@ async function captureMobileShots(args) {
51999
52097
  for (const section of sections) {
52000
52098
  const shot = await captureSectionOnMobile(mobile.page, section);
52001
52099
  if (!shot) continue;
52002
- const file = path36.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
52100
+ const file = path37.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
52003
52101
  await writeFile16(file, shot);
52004
- section.mobileShot = path36.relative(outDir, file);
52102
+ section.mobileShot = path37.relative(outDir, file);
52005
52103
  }
52006
52104
  } finally {
52007
52105
  await mobile.context.close();
@@ -52016,10 +52114,10 @@ async function captureMotionTakes(args) {
52016
52114
  const filmOne = async (section) => {
52017
52115
  const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
52018
52116
  if (!take) return;
52019
- const dir = path36.join(sectionsDir, String(section.index).padStart(2, "0"));
52020
- const file = path36.join(dir, "motion-filmstrip.png");
52117
+ const dir = path37.join(sectionsDir, String(section.index).padStart(2, "0"));
52118
+ const file = path37.join(dir, "motion-filmstrip.png");
52021
52119
  await writeFile16(file, take.filmstrip);
52022
- section.motionFilmstrip = path36.relative(outDir, file);
52120
+ section.motionFilmstrip = path37.relative(outDir, file);
52023
52121
  log(` [${section.index}] ${section.motion.summary}`);
52024
52122
  };
52025
52123
  const queue = [...moving];
@@ -52076,7 +52174,7 @@ async function captureAlternateViews(args) {
52076
52174
  async function reproduceWholePage(args) {
52077
52175
  const { browser, page, outDir, pageUrl, withCode, log } = args;
52078
52176
  const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
52079
- if (fullPage) await writeFile16(path36.join(outDir, "full-page.png"), fullPage);
52177
+ if (fullPage) await writeFile16(path37.join(outDir, "full-page.png"), fullPage);
52080
52178
  if (!withCode) return { bundle: null, fidelity: null };
52081
52179
  const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
52082
52180
  log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
@@ -52147,7 +52245,7 @@ async function openViaLadder(args) {
52147
52245
  async function scrapeLanding(options) {
52148
52246
  const timeoutMs = options.timeoutMs ?? 45e3;
52149
52247
  const log = options.onProgress ?? (() => void 0);
52150
- const sectionsDir = path36.join(options.outDir, "sections");
52248
+ const sectionsDir = path37.join(options.outDir, "sections");
52151
52249
  const nonPublic = refuseNonPublicUrl(options.url);
52152
52250
  if (nonPublic) {
52153
52251
  throw new BlockedPageError({
@@ -52209,7 +52307,7 @@ async function scrapeLanding(options) {
52209
52307
  security: prepared.security,
52210
52308
  captureTier: tier
52211
52309
  };
52212
- await writeFile16(path36.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
52310
+ await writeFile16(path37.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
52213
52311
  `);
52214
52312
  if (options.report !== false) {
52215
52313
  const reportPath = await writeCaptureReport(manifest, options.outDir);
@@ -52224,28 +52322,28 @@ async function scrapeLanding(options) {
52224
52322
 
52225
52323
  // src/commands/landing/inspiration/captureOut.ts
52226
52324
  import { existsSync as existsSync9 } from "fs";
52227
- import path37 from "path";
52325
+ import path38 from "path";
52228
52326
  var SCRATCH_DIR = ".baker";
52229
52327
  function isWithin(parent, target) {
52230
- const relative = path37.relative(parent, target);
52231
- return relative === "" || !relative.startsWith("..") && !path37.isAbsolute(relative);
52328
+ const relative = path38.relative(parent, target);
52329
+ return relative === "" || !relative.startsWith("..") && !path38.isAbsolute(relative);
52232
52330
  }
52233
52331
  function findRepoRoot(from) {
52234
- let dir = path37.resolve(from);
52332
+ let dir = path38.resolve(from);
52235
52333
  for (; ; ) {
52236
- if (existsSync9(path37.join(dir, ".git"))) return dir;
52237
- const parent = path37.dirname(dir);
52334
+ if (existsSync9(path38.join(dir, ".git"))) return dir;
52335
+ const parent = path38.dirname(dir);
52238
52336
  if (parent === dir) return null;
52239
52337
  dir = parent;
52240
52338
  }
52241
52339
  }
52242
52340
  function checkCaptureOut(out, options) {
52243
52341
  const { cwd, repoRoot } = options;
52244
- const resolved = path37.resolve(cwd, out);
52342
+ const resolved = path38.resolve(cwd, out);
52245
52343
  if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
52246
- const scratch = path37.join(repoRoot, SCRATCH_DIR);
52344
+ const scratch = path38.join(repoRoot, SCRATCH_DIR);
52247
52345
  if (isWithin(scratch, resolved)) return { ok: true };
52248
- const suggestion = path37.posix.join(SCRATCH_DIR, "teardowns", path37.basename(resolved) || "capture");
52346
+ const suggestion = path38.posix.join(SCRATCH_DIR, "teardowns", path38.basename(resolved) || "capture");
52249
52347
  return {
52250
52348
  ok: false,
52251
52349
  error: {
@@ -52419,12 +52517,12 @@ var scrapeCommand = defineCommand169({
52419
52517
  });
52420
52518
 
52421
52519
  // src/commands/landing/inspiration/search.ts
52422
- import path39 from "path";
52520
+ import path40 from "path";
52423
52521
  import { defineCommand as defineCommand170 } from "citty";
52424
52522
 
52425
52523
  // src/commands/landing/inspiration/shot.ts
52426
52524
  import { mkdir as mkdir12, writeFile as writeFile17 } from "fs/promises";
52427
- import path38 from "path";
52525
+ import path39 from "path";
52428
52526
  import sharp6 from "sharp";
52429
52527
  var READABLE_SHOT = {
52430
52528
  maxWidth: 1440,
@@ -52450,9 +52548,9 @@ async function downloadReadableShot(url, file) {
52450
52548
  const response = await fetch(url);
52451
52549
  if (!response.ok) return null;
52452
52550
  const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
52453
- await mkdir12(path38.dirname(file), { recursive: true });
52551
+ await mkdir12(path39.dirname(file), { recursive: true });
52454
52552
  await writeFile17(file, shot);
52455
- return path38.relative(process.cwd(), file);
52553
+ return path39.relative(process.cwd(), file);
52456
52554
  } catch {
52457
52555
  return null;
52458
52556
  }
@@ -52544,13 +52642,13 @@ function buildSearchBody(args) {
52544
52642
  return body;
52545
52643
  }
52546
52644
  async function downloadShots(results) {
52547
- const dir = path39.join(process.cwd(), ".baker", "inspiration");
52645
+ const dir = path40.join(process.cwd(), ".baker", "inspiration");
52548
52646
  const saved = /* @__PURE__ */ new Map();
52549
52647
  await Promise.all(
52550
52648
  results.map(async (result) => {
52551
52649
  const file = await downloadReadableShot(
52552
52650
  result.desktopShotUrl,
52553
- path39.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
52651
+ path40.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
52554
52652
  );
52555
52653
  if (file) saved.set(result.id, file);
52556
52654
  })
@@ -52778,7 +52876,7 @@ var sequencesCommand = defineCommand171({
52778
52876
  });
52779
52877
 
52780
52878
  // src/commands/landing/inspiration/view.ts
52781
- import path40 from "path";
52879
+ import path41 from "path";
52782
52880
  import { defineCommand as defineCommand172 } from "citty";
52783
52881
  registerSchema({
52784
52882
  command: "landing.inspiration.view",
@@ -52811,12 +52909,12 @@ var viewCommand2 = defineCommand172({
52811
52909
  const id = args.id;
52812
52910
  const data = await apiGet("/api/landing-inspiration/section", { id });
52813
52911
  const section = data.section;
52814
- const dir = path40.join(process.cwd(), ".baker", "inspiration", id);
52912
+ const dir = path41.join(process.cwd(), ".baker", "inspiration", id);
52815
52913
  const ext = READABLE_SHOT.extension;
52816
52914
  const [desktop, mobile, filmstrip] = await Promise.all([
52817
- downloadReadableShot(section.desktopShotUrl, path40.join(dir, `desktop.${ext}`)),
52818
- downloadReadableShot(section.mobileShotUrl, path40.join(dir, `mobile.${ext}`)),
52819
- downloadReadableShot(section.motionFilmstripUrl, path40.join(dir, `motion-filmstrip.${ext}`))
52915
+ downloadReadableShot(section.desktopShotUrl, path41.join(dir, `desktop.${ext}`)),
52916
+ downloadReadableShot(section.mobileShotUrl, path41.join(dir, `mobile.${ext}`)),
52917
+ downloadReadableShot(section.motionFilmstripUrl, path41.join(dir, `motion-filmstrip.${ext}`))
52820
52918
  ]);
52821
52919
  const full = args.full;
52822
52920
  const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
@@ -52922,8 +53020,8 @@ Full guide: __tooling__/docs/tools/baker/landing.md`
52922
53020
 
52923
53021
  // src/commands/landing/variant.ts
52924
53022
  import { randomUUID as randomUUID2 } from "crypto";
52925
- import { mkdir as mkdir13, readdir as readdir13, readFile as readFile27, stat as stat9, writeFile as writeFile18 } from "fs/promises";
52926
- import path41 from "path";
53023
+ import { mkdir as mkdir13, readdir as readdir13, readFile as readFile28, stat as stat9, writeFile as writeFile18 } from "fs/promises";
53024
+ import path42 from "path";
52927
53025
  import { defineCommand as defineCommand174 } from "citty";
52928
53026
  registerSchema({
52929
53027
  command: "landing.variant",
@@ -52950,7 +53048,7 @@ registerSchema({
52950
53048
  var SLUG_RE2 = /^[a-z0-9][a-z0-9-]*$/;
52951
53049
  async function readInternalId(definitionPath) {
52952
53050
  try {
52953
- const text2 = await readFile27(definitionPath, "utf8");
53051
+ const text2 = await readFile28(definitionPath, "utf8");
52954
53052
  return /^internalId:\s*"?([^"\s]+)"?\s*$/m.exec(text2)?.[1] ?? null;
52955
53053
  } catch {
52956
53054
  return null;
@@ -52959,7 +53057,7 @@ async function readInternalId(definitionPath) {
52959
53057
  async function archivedVariantNumbers(root, internalId) {
52960
53058
  if (!internalId) return [];
52961
53059
  try {
52962
- const entries = await readdir13(path41.resolve(root, "src/_variants", internalId), { withFileTypes: true });
53060
+ const entries = await readdir13(path42.resolve(root, "src/_variants", internalId), { withFileTypes: true });
52963
53061
  return entries.filter((entry) => entry.isDirectory()).map((entry) => Number(entry.name)).filter((n) => Number.isInteger(n) && n > 0);
52964
53062
  } catch {
52965
53063
  return [];
@@ -52989,7 +53087,7 @@ async function exists(p) {
52989
53087
  }
52990
53088
  async function listLandingSlugs2(root) {
52991
53089
  try {
52992
- const entries = await readdir13(path41.resolve(root, "src", "pages"), { withFileTypes: true });
53090
+ const entries = await readdir13(path42.resolve(root, "src", "pages"), { withFileTypes: true });
52993
53091
  return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
52994
53092
  } catch {
52995
53093
  return [];
@@ -53005,7 +53103,7 @@ async function listComponents(componentsDir, prefix = "") {
53005
53103
  const out = [];
53006
53104
  for (const entry of entries) {
53007
53105
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
53008
- if (entry.isDirectory()) out.push(...await listComponents(path41.join(componentsDir, entry.name), rel));
53106
+ if (entry.isDirectory()) out.push(...await listComponents(path42.join(componentsDir, entry.name), rel));
53009
53107
  else if (entry.name.endsWith(".astro")) out.push(rel);
53010
53108
  }
53011
53109
  return out.sort();
@@ -53072,16 +53170,16 @@ async function resolveTargets(projectRoot, opts) {
53072
53170
  `"${controlSlug}" is already a variant of "${variantOfSlug(controlSlug)}". Make the next variant of the page itself: \`baker landing variant ${variantOfSlug(controlSlug)}\`.`
53073
53171
  );
53074
53172
  }
53075
- const controlDir = path41.resolve(projectRoot, "src", "pages", controlSlug);
53173
+ const controlDir = path42.resolve(projectRoot, "src", "pages", controlSlug);
53076
53174
  if (!await isDir3(controlDir)) {
53077
53175
  fail7("NOT_FOUND", `No page at src/pages/${controlSlug}/`, {
53078
53176
  availableSlugs: await listLandingSlugs2(projectRoot)
53079
53177
  });
53080
53178
  }
53081
- if (!await exists(path41.join(controlDir, "index.astro"))) {
53179
+ if (!await exists(path42.join(controlDir, "index.astro"))) {
53082
53180
  fail7("NOT_FOUND", `src/pages/${controlSlug}/index.astro is missing, so there is no page to make a variant of.`);
53083
53181
  }
53084
- const internalId = await readInternalId(path41.join(controlDir, "_definition.md"));
53182
+ const internalId = await readInternalId(path42.join(controlDir, "_definition.md"));
53085
53183
  const variantSlugValue = variantSlug(
53086
53184
  controlSlug,
53087
53185
  nextVariantNumber(
@@ -53090,8 +53188,8 @@ async function resolveTargets(projectRoot, opts) {
53090
53188
  await archivedVariantNumbers(projectRoot, internalId)
53091
53189
  )
53092
53190
  );
53093
- const variantDir = path41.resolve(projectRoot, "src", "pages", variantSlugValue);
53094
- const available = await listComponents(path41.join(controlDir, "_components"));
53191
+ const variantDir = path42.resolve(projectRoot, "src", "pages", variantSlugValue);
53192
+ const available = await listComponents(path42.join(controlDir, "_components"));
53095
53193
  const forks = [];
53096
53194
  for (const name of parseForks(opts.fork, opts.rawArgs)) {
53097
53195
  const resolved = resolveForkName(name, available);
@@ -53107,26 +53205,26 @@ async function resolveTargets(projectRoot, opts) {
53107
53205
  }
53108
53206
  async function writeVariant(opts) {
53109
53207
  const { controlDir, variantDir, controlSlug, variantSlug: variantSlug2, forks } = opts;
53110
- const forkedTargets = new Set(forks.map((f) => path41.join(controlDir, "_components", f)));
53208
+ const forkedTargets = new Set(forks.map((f) => path42.join(controlDir, "_components", f)));
53111
53209
  const written = [];
53112
- const indexText = await readFile27(path41.join(controlDir, "index.astro"), "utf8");
53210
+ const indexText = await readFile28(path42.join(controlDir, "index.astro"), "utf8");
53113
53211
  await mkdir13(variantDir, { recursive: true });
53114
53212
  await writeFile18(
53115
- path41.join(variantDir, "index.astro"),
53213
+ path42.join(variantDir, "index.astro"),
53116
53214
  repointFile(indexText, { fromDir: controlDir, toDir: variantDir, controlDir, variantDir, forkedTargets }),
53117
53215
  "utf8"
53118
53216
  );
53119
53217
  written.push(`src/pages/${variantSlug2}/index.astro`);
53120
53218
  for (const fork of forks) {
53121
- const fromFile = path41.join(controlDir, "_components", fork);
53122
- const toFile = path41.join(variantDir, "_components", fork);
53123
- const text2 = await readFile27(fromFile, "utf8");
53124
- await mkdir13(path41.dirname(toFile), { recursive: true });
53219
+ const fromFile = path42.join(controlDir, "_components", fork);
53220
+ const toFile = path42.join(variantDir, "_components", fork);
53221
+ const text2 = await readFile28(fromFile, "utf8");
53222
+ await mkdir13(path42.dirname(toFile), { recursive: true });
53125
53223
  await writeFile18(
53126
53224
  toFile,
53127
53225
  repointFile(text2, {
53128
- fromDir: path41.dirname(fromFile),
53129
- toDir: path41.dirname(toFile),
53226
+ fromDir: path42.dirname(fromFile),
53227
+ toDir: path42.dirname(toFile),
53130
53228
  controlDir,
53131
53229
  variantDir,
53132
53230
  forkedTargets
@@ -53135,9 +53233,9 @@ async function writeVariant(opts) {
53135
53233
  );
53136
53234
  written.push(`src/pages/${variantSlug2}/_components/${fork}`);
53137
53235
  }
53138
- const controlDefinitionPath = path41.join(controlDir, "_definition.md");
53236
+ const controlDefinitionPath = path42.join(controlDir, "_definition.md");
53139
53237
  if (await exists(controlDefinitionPath)) {
53140
- const definition = buildVariantDefinition(await readFile27(controlDefinitionPath, "utf8"), {
53238
+ const definition = buildVariantDefinition(await readFile28(controlDefinitionPath, "utf8"), {
53141
53239
  internalId: randomUUID2().replace(/-/g, "").slice(0, 8),
53142
53240
  variantSlug: variantSlug2,
53143
53241
  controlSlug,
@@ -53145,11 +53243,11 @@ async function writeVariant(opts) {
53145
53243
  ...opts.because ? { because: opts.because } : {},
53146
53244
  ...opts.change ? { change: opts.change } : {}
53147
53245
  });
53148
- await writeFile18(path41.join(variantDir, "_definition.md"), definition, "utf8");
53246
+ await writeFile18(path42.join(variantDir, "_definition.md"), definition, "utf8");
53149
53247
  written.push(`src/pages/${variantSlug2}/_definition.md`);
53150
53248
  }
53151
- await mkdir13(path41.join(variantDir, "_images"), { recursive: true });
53152
- await writeFile18(path41.join(variantDir, "_images", ".gitkeep"), "", "utf8");
53249
+ await mkdir13(path42.join(variantDir, "_images"), { recursive: true });
53250
+ await writeFile18(path42.join(variantDir, "_images", ".gitkeep"), "", "utf8");
53153
53251
  return written;
53154
53252
  }
53155
53253
  var variantCommand = defineCommand174({
@@ -55171,8 +55269,8 @@ var listCommand16 = defineCommand193({
55171
55269
  });
55172
55270
 
55173
55271
  // src/commands/scheduled-actions/templates.ts
55174
- import { readFile as readFile28 } from "fs/promises";
55175
- import path42 from "path";
55272
+ import { readFile as readFile29 } from "fs/promises";
55273
+ import path43 from "path";
55176
55274
  import { defineCommand as defineCommand194 } from "citty";
55177
55275
  registerSchema({
55178
55276
  command: "scheduled-actions.templates",
@@ -55275,7 +55373,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
55275
55373
  }
55276
55374
  if (save.length > 0) {
55277
55375
  const briefFile = flag("brief-file");
55278
- const brief = briefFile.length > 0 ? await readFile28(path42.resolve(briefFile), "utf8") : flag("brief");
55376
+ const brief = briefFile.length > 0 ? await readFile29(path43.resolve(briefFile), "utf8") : flag("brief");
55279
55377
  if (brief.trim().length === 0) {
55280
55378
  failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
55281
55379
  }
@@ -55746,7 +55844,7 @@ function parseImageRefs(spec) {
55746
55844
  }
55747
55845
  var defaultDeps = {
55748
55846
  ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
55749
- upload: (path44) => uploadLocalImage({ file: path44, contentType: detectImageContentType(path44), source: "uploaded" })
55847
+ upload: (path45) => uploadLocalImage({ file: path45, contentType: detectImageContentType(path45), source: "uploaded" })
55750
55848
  };
55751
55849
  async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
55752
55850
  const refs = parseImageRefs(spec);
@@ -55766,10 +55864,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
55766
55864
  }
55767
55865
  return { imageIds, added };
55768
55866
  }
55769
- function uploadFailure(path44) {
55867
+ function uploadFailure(path45) {
55770
55868
  return (error) => {
55771
55869
  if (error instanceof ApiError) throw error;
55772
- throw new ApiError("VALIDATION_ERROR", `Could not read "${path44}" as an image.`);
55870
+ throw new ApiError("VALIDATION_ERROR", `Could not read "${path45}" as an image.`);
55773
55871
  };
55774
55872
  }
55775
55873
 
@@ -56831,10 +56929,10 @@ async function stageOp4(op) {
56831
56929
  handleError5(err);
56832
56930
  }
56833
56931
  }
56834
- async function draftAction3(path44, body, chat) {
56932
+ async function draftAction3(path45, body, chat) {
56835
56933
  const chatId = resolveChatId(chat);
56836
56934
  try {
56837
- const data = await apiPost(path44, { chatId, ...body });
56935
+ const data = await apiPost(path45, { chatId, ...body });
56838
56936
  writeJsonEnvelope({ ok: true, data });
56839
56937
  return data;
56840
56938
  } catch (err) {
@@ -57971,7 +58069,7 @@ var groupCommand2 = defineCommand219({
57971
58069
  // src/commands/videos/ingest.ts
57972
58070
  import { mkdtemp as mkdtemp2, rm as rm8, stat as stat10 } from "fs/promises";
57973
58071
  import { tmpdir as tmpdir3 } from "os";
57974
- import path43 from "path";
58072
+ import path44 from "path";
57975
58073
  import { defineCommand as defineCommand220 } from "citty";
57976
58074
 
57977
58075
  // src/lib/streamUpload.ts
@@ -58322,7 +58420,7 @@ function ingestUrl(args) {
58322
58420
  }
58323
58421
  async function downloadThenIngest(args, country) {
58324
58422
  const vimeoCookie = captureVimeoCookie();
58325
- const workDir = await mkdtemp2(path43.join(tmpdir3(), "videos-ingest-"));
58423
+ const workDir = await mkdtemp2(path44.join(tmpdir3(), "videos-ingest-"));
58326
58424
  try {
58327
58425
  const probe = await probeYtDlp({ url: args.url, country, vimeoCookie, cookieDir: workDir });
58328
58426
  if (isAudioOnly(probe.info)) {
@@ -58458,7 +58556,7 @@ var searchCommand4 = defineCommand221({
58458
58556
  var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
58459
58557
 
58460
58558
  // src/commands/videos/upload.ts
58461
- import { readFile as readFile29, stat as stat11 } from "fs/promises";
58559
+ import { readFile as readFile30, stat as stat11 } from "fs/promises";
58462
58560
  import { basename as basename3, extname as extname4 } from "path";
58463
58561
  import { defineCommand as defineCommand222 } from "citty";
58464
58562
  var MIME_MAP = {
@@ -58555,7 +58653,7 @@ var uploadCommand2 = defineCommand222({
58555
58653
  originalFilename,
58556
58654
  descriptionContext
58557
58655
  });
58558
- const fileBuffer = await readFile29(filePath);
58656
+ const fileBuffer = await readFile30(filePath);
58559
58657
  const uploadResponse = await fetch(uploadUrl, {
58560
58658
  method: "PUT",
58561
58659
  headers: { "Content-Type": contentType },
@@ -59942,7 +60040,7 @@ function unknownFlagEnvelope(unknown, commandPath, suggestion) {
59942
60040
  };
59943
60041
  }
59944
60042
  function commandPathOf(root, argv) {
59945
- const path44 = [];
60043
+ const path45 = [];
59946
60044
  let command = root;
59947
60045
  for (const token of argv) {
59948
60046
  if (token === "--" || token.startsWith("-")) {
@@ -59953,10 +60051,10 @@ function commandPathOf(root, argv) {
59953
60051
  if (next === void 0 || typeof next !== "object") {
59954
60052
  break;
59955
60053
  }
59956
- path44.push(token);
60054
+ path45.push(token);
59957
60055
  command = next;
59958
60056
  }
59959
- return path44.join(" ");
60057
+ return path45.join(" ");
59960
60058
  }
59961
60059
  function refuseUnknownFlags(root, argv) {
59962
60060
  const unknown = findUnknownFlags(root, argv);