@koda-sl/baker-cli 0.354.0-dev.e6a354971 → 0.354.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
@@ -68,7 +68,7 @@ import {
68
68
  validateCanvasDeep,
69
69
  withoutCastName,
70
70
  ytDlpBlockSignal
71
- } from "./chunk-B7MLH3K6.js";
71
+ } from "./chunk-4HRJQGVW.js";
72
72
  import {
73
73
  csvOrJson,
74
74
  daysAgoIso,
@@ -10564,6 +10564,102 @@ function hintsFor(scope, days) {
10564
10564
  return hints2;
10565
10565
  }
10566
10566
 
10567
+ // src/commands/ads/google/list-fields.ts
10568
+ var LIST_FIELDS = /* @__PURE__ */ new Set([
10569
+ "ad.final_mobile_urls",
10570
+ "ad.final_urls",
10571
+ "ad_group.excluded_parent_asset_field_types",
10572
+ "ad_group.excluded_parent_asset_set_types",
10573
+ "ad_group.labels",
10574
+ "ad_group.primary_status_reasons",
10575
+ "ad_group_ad.ad.final_mobile_urls",
10576
+ "ad_group_ad.ad.final_urls",
10577
+ "ad_group_ad.labels",
10578
+ "ad_group_ad.primary_status_reasons",
10579
+ "ad_group_asset.primary_status_reasons",
10580
+ "ad_group_criterion.disapproval_reasons",
10581
+ "ad_group_criterion.final_mobile_urls",
10582
+ "ad_group_criterion.final_urls",
10583
+ "ad_group_criterion.labels",
10584
+ "ad_group_criterion.primary_status_reasons",
10585
+ "ad_group_criterion.topic.path",
10586
+ "ad_group_criterion.webpage.sample.sample_urls",
10587
+ "asset.dynamic_custom_asset.contextual_keywords",
10588
+ "asset.dynamic_custom_asset.similar_ids",
10589
+ "asset.dynamic_education_asset.contextual_keywords",
10590
+ "asset.dynamic_education_asset.similar_program_ids",
10591
+ "asset.dynamic_flights_asset.similar_destination_ids",
10592
+ "asset.dynamic_hotels_and_rentals_asset.contextual_keywords",
10593
+ "asset.dynamic_hotels_and_rentals_asset.similar_property_ids",
10594
+ "asset.dynamic_jobs_asset.contextual_keywords",
10595
+ "asset.dynamic_jobs_asset.similar_job_ids",
10596
+ "asset.dynamic_local_asset.contextual_keywords",
10597
+ "asset.dynamic_local_asset.similar_deal_ids",
10598
+ "asset.dynamic_real_estate_asset.contextual_keywords",
10599
+ "asset.dynamic_real_estate_asset.similar_listing_ids",
10600
+ "asset.dynamic_travel_asset.contextual_keywords",
10601
+ "asset.dynamic_travel_asset.similar_destination_ids",
10602
+ "asset.final_mobile_urls",
10603
+ "asset.final_urls",
10604
+ "asset.page_feed_asset.labels",
10605
+ "asset.structured_snippet_asset.values",
10606
+ "asset_group.final_mobile_urls",
10607
+ "asset_group.final_urls",
10608
+ "asset_group.primary_status_reasons",
10609
+ "asset_group_asset.primary_status_reasons",
10610
+ "asset_group_signal.disapproval_reasons",
10611
+ "asset_set.business_profile_location_group.dynamic_business_profile_location_group_filter.label_filters",
10612
+ "asset_set.business_profile_location_group.dynamic_business_profile_location_group_filter.listing_id_filters",
10613
+ "asset_set.location_set.business_profile_location_set.label_filters",
10614
+ "asset_set.location_set.business_profile_location_set.listing_id_filters",
10615
+ "bidding_data_exclusion.advertising_channel_types",
10616
+ "bidding_data_exclusion.campaigns",
10617
+ "bidding_data_exclusion.devices",
10618
+ "bidding_seasonality_adjustment.advertising_channel_types",
10619
+ "bidding_seasonality_adjustment.campaigns",
10620
+ "bidding_seasonality_adjustment.devices",
10621
+ "campaign.excluded_parent_asset_field_types",
10622
+ "campaign.excluded_parent_asset_set_types",
10623
+ "campaign.feed_types",
10624
+ "campaign.labels",
10625
+ "campaign.optimization_goal_setting.optimization_goal_types",
10626
+ "campaign.primary_status_reasons",
10627
+ "campaign.selective_optimization.conversion_actions",
10628
+ "campaign.shopping_setting.advertising_partner_ids",
10629
+ "campaign.text_guidelines.term_exclusions",
10630
+ "campaign_asset.primary_status_reasons",
10631
+ "campaign_criterion.location_group.geo_target_constants",
10632
+ "campaign_criterion.topic.path",
10633
+ "campaign_criterion.webpage.sample.sample_urls",
10634
+ "conversion_value_rule.audience_condition.user_interests",
10635
+ "conversion_value_rule.audience_condition.user_lists",
10636
+ "conversion_value_rule.device_condition.device_types",
10637
+ "conversion_value_rule.geo_location_condition.excluded_geo_target_constants",
10638
+ "conversion_value_rule.geo_location_condition.geo_target_constants",
10639
+ "conversion_value_rule_set.conversion_value_rules",
10640
+ "conversion_value_rule_set.dimensions",
10641
+ "custom_conversion_goal.conversion_actions",
10642
+ "customer.pay_per_conversion_eligibility_failure_reasons",
10643
+ "customer_asset.primary_status_reasons",
10644
+ "customer_client.applied_labels",
10645
+ "experiment_arm.campaigns",
10646
+ "experiment_arm.in_design_campaigns",
10647
+ "keyword_plan_campaign.language_constants",
10648
+ "local_services_employee.category_ids",
10649
+ "local_services_employee.languages_spoken",
10650
+ "metrics.interaction_event_types",
10651
+ "recommendation.campaigns",
10652
+ "shared_criterion.webpage.sample.sample_urls",
10653
+ "shopping_product.target_countries",
10654
+ "topic_constant.path",
10655
+ "user_list.lookalike_user_list.country_codes",
10656
+ "user_list.lookalike_user_list.seed_user_list_ids"
10657
+ ]);
10658
+ var LIST_FIELD_OPERATORS = "CONTAINS ALL, CONTAINS ANY, CONTAINS NONE";
10659
+ function listFieldGuidance(field) {
10660
+ return `${field} is a list field: it takes only ${LIST_FIELD_OPERATORS} \u2014 no comparison (=, !=, IN), pattern match or null test. To find rows that have it, drop that condition, keep ${field} in SELECT and filter the rows (rows without it omit the key); to match values, use ${field} CONTAINS ANY ('value')`;
10661
+ }
10662
+
10567
10663
  // src/commands/ads/google/correction-table.ts
10568
10664
  function buildCommand(query, ctx) {
10569
10665
  return `baker ads google query "${query}" --customer-id ${ctx.customerId}`;
@@ -10591,6 +10687,27 @@ var CORRECTION_RULES = [
10591
10687
  };
10592
10688
  }
10593
10689
  },
10690
+ // 0b. OPERATOR_FIELD_MISMATCH — the field refuses this operator, and Google names the
10691
+ // ones it takes. Kept ahead of the field-specific rules for the same reason as rule 0.
10692
+ // A repeated field (final_urls, labels, …) takes only CONTAINS ALL/ANY/NONE, so it has
10693
+ // no null test at all: read it unfiltered and filter the rows instead.
10694
+ {
10695
+ matchApiError: /OPERATOR_FIELD_MISMATCH|is not a valid operator to use with/i,
10696
+ fix: (ctx) => {
10697
+ const message = ctx.apiErrorMessage ?? "";
10698
+ const field = message.match(/valid operator to use with '([\w.]+)'/i)?.[1] ?? "this field";
10699
+ const operator = message.match(/([A-Z=!<>][A-Z_ =!<>]*?) is not a valid operator/)?.[1]?.trim();
10700
+ const valid = message.match(/Valid operators: (.+?)\.?(?:"|$)/i)?.[1]?.trim();
10701
+ const refused = operator ? `${operator} cannot be used on ${field}` : `${field} does not take that operator`;
10702
+ const allowed = valid ? `; its valid operators are ${valid}` : "";
10703
+ const repeated = valid?.split(",").every((op) => /^CONTAINS (ALL|ANY|NONE)$/i.test(op.trim())) ?? false;
10704
+ const guidance = repeated ? `. ${listFieldGuidance(field)}` : ". Rewrite that condition with one of them, or drop it and filter the rows";
10705
+ return {
10706
+ action: "change_operator",
10707
+ explanation: `${refused}${allowed}${guidance}`
10708
+ };
10709
+ }
10710
+ },
10594
10711
  // 1. bare keyword.text → ad_group_criterion.keyword.text (leaves <resource>.keyword.text untouched)
10595
10712
  {
10596
10713
  matchQuery: /(?<![\w.])keyword\.text\b/,
@@ -10633,10 +10750,11 @@ var CORRECTION_RULES = [
10633
10750
  };
10634
10751
  }
10635
10752
  },
10636
- // 4. CONTAINS → LIKE
10753
+ // 4. CONTAINS → LIKE. Keyed on the query alone: Google's error envelope ("Request contains an
10754
+ // invalid argument … Valid operators: CONTAINS ANY") matches any loose error pattern, and this
10755
+ // advice only makes sense when the query actually wrote CONTAINS(…).
10637
10756
  {
10638
10757
  matchQuery: /CONTAINS\s*\(/i,
10639
- matchApiError: /CONTAINS.*not.*supported|invalid.*operator.*CONTAINS/i,
10640
10758
  fix: (ctx) => {
10641
10759
  const match = ctx.originalQuery.match(/CONTAINS\s*\(\s*([^,]+),\s*'([^']+)'\s*\)/i);
10642
10760
  if (match) {
@@ -13082,6 +13200,73 @@ function rejectOversizedLimit(query, customerId) {
13082
13200
  }
13083
13201
  };
13084
13202
  }
13203
+ var WHERE_OPERATOR = /(?<![\w.])([a-z_]+(?:\.[a-z_]+)+)\s*(CONTAINS\s+(?:ALL|ANY|NONE)|IS\s+NOT\s+NULL|IS\s+NULL|NOT\s+LIKE|NOT\s+IN|NOT\s+REGEXP_MATCH|REGEXP_MATCH|LIKE|IN|BETWEEN|DURING|!=|<=|>=|=|<|>)/gi;
13204
+ function escapeRegExp(text2) {
13205
+ return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13206
+ }
13207
+ function withoutNullTest(query, field) {
13208
+ const test = `${escapeRegExp(field)}\\s+IS\\s+(?:NOT\\s+)?NULL\\b`;
13209
+ const dropped = query.replace(new RegExp(`\\bWHERE\\s+${test}\\s+AND\\s+`, "i"), "WHERE ").replace(new RegExp(`\\s+AND\\s+${test}`, "i"), "").replace(new RegExp(`\\s*\\bWHERE\\s+${test}`, "i"), "");
13210
+ return mentions(selectClause(dropped), field) ? dropped : dropped.replace(/SELECT\s+/i, `SELECT ${field}, `);
13211
+ }
13212
+ function asListCondition(query, field) {
13213
+ const condition = new RegExp(
13214
+ `(?<![\\w.])${escapeRegExp(field)}\\s*(NOT\\s+IN\\b|IN\\b|!=|=)\\s*('[^']*'|\\([^)]*\\))`,
13215
+ "i"
13216
+ );
13217
+ const match = query.match(condition);
13218
+ if (!match) return null;
13219
+ const [, operator = "", value = ""] = match;
13220
+ const contains = /^(=|IN)$/i.test(operator.trim()) ? "CONTAINS ANY" : "CONTAINS NONE";
13221
+ const values = value.startsWith("(") ? value : `(${value})`;
13222
+ return query.replace(condition, `${field} ${contains} ${values}`);
13223
+ }
13224
+ function rejectListFieldOperator(query, customerId) {
13225
+ const where = whereClause(query).replace(/'[^']*'/g, "''");
13226
+ const refusals = [...where.matchAll(WHERE_OPERATOR)].map(([, field = "", operator = ""]) => ({
13227
+ field: field.toLowerCase(),
13228
+ operator: operator.toUpperCase().replace(/\s+/g, " ")
13229
+ })).filter(({ field, operator }) => LIST_FIELDS.has(field) && !operator.startsWith("CONTAINS"));
13230
+ const first = refusals[0];
13231
+ if (!first) return null;
13232
+ const fixed = refusals.reduce(
13233
+ (corrected, { field, operator }) => corrected === null ? null : operator.startsWith("IS ") ? withoutNullTest(corrected, field) : asListCondition(corrected, field),
13234
+ query
13235
+ );
13236
+ return {
13237
+ ok: false,
13238
+ error: {
13239
+ code: "INVALID_OPERATOR",
13240
+ message: `${first.operator} cannot be used on ${first.field}: it is a list field, so Google would refuse this query.`,
13241
+ fix: {
13242
+ action: "change_operator",
13243
+ ...fixed ? { correctedCommand: buildCommand2(fixed, customerId) } : {},
13244
+ explanation: listFieldGuidance(first.field)
13245
+ },
13246
+ retryable: false
13247
+ }
13248
+ };
13249
+ }
13250
+ function rejectContainsCall(query, customerId) {
13251
+ const containsMatch = query.match(/CONTAINS\s*\(\s*([^,]+),\s*'([^']+)'\s*\)/i);
13252
+ if (!containsMatch) return null;
13253
+ const field = containsMatch[1]?.trim();
13254
+ const value = containsMatch[2] ?? "";
13255
+ const fixed = query.replace(/CONTAINS\s*\(\s*[^,]+,\s*'[^']+'\s*\)/i, `${field} LIKE '%${value}%'`);
13256
+ return {
13257
+ ok: false,
13258
+ error: {
13259
+ code: "INVALID_OPERATOR",
13260
+ message: "GAQL does not support CONTAINS. Use LIKE with % wildcards.",
13261
+ fix: {
13262
+ action: "change_operator",
13263
+ correctedCommand: buildCommand2(fixed, customerId),
13264
+ explanation: "GAQL uses LIKE '%value%' for substring matching, not CONTAINS()"
13265
+ },
13266
+ retryable: false
13267
+ }
13268
+ };
13269
+ }
13085
13270
  function validatePreflight(query, customerId, limit) {
13086
13271
  const warnings = [];
13087
13272
  const corrected = query;
@@ -13104,29 +13289,6 @@ function validatePreflight(query, customerId, limit) {
13104
13289
  }
13105
13290
  };
13106
13291
  }
13107
- const containsMatch = corrected.match(/CONTAINS\s*\(\s*([^,]+),\s*'([^']+)'\s*\)/i);
13108
- if (containsMatch) {
13109
- const field = containsMatch[1]?.trim();
13110
- const value = containsMatch[2] ?? "";
13111
- const fixed = corrected.replace(/CONTAINS\s*\(\s*[^,]+,\s*'[^']+'\s*\)/i, `${field} LIKE '%${value}%'`);
13112
- return {
13113
- valid: false,
13114
- warnings: [],
13115
- error: {
13116
- ok: false,
13117
- error: {
13118
- code: "INVALID_OPERATOR",
13119
- message: "GAQL does not support CONTAINS. Use LIKE with % wildcards.",
13120
- fix: {
13121
- action: "change_operator",
13122
- correctedCommand: buildCommand2(fixed, customerId),
13123
- explanation: "GAQL uses LIKE '%value%' for substring matching, not CONTAINS()"
13124
- },
13125
- retryable: false
13126
- }
13127
- }
13128
- };
13129
- }
13130
13292
  const openDateMatch = corrected.match(/segments\.date\s*>=\s*'(\d{4}-\d{2}-\d{2})'/i);
13131
13293
  if (openDateMatch && !/BETWEEN/i.test(corrected)) {
13132
13294
  const startDate = openDateMatch[1] ?? "";
@@ -13153,10 +13315,10 @@ function validatePreflight(query, customerId, limit) {
13153
13315
  }
13154
13316
  };
13155
13317
  }
13156
- const duringRejection = rejectUnsupportedDuring(corrected, customerId);
13157
- if (duringRejection) return { valid: false, warnings: [], error: duringRejection };
13158
- const limitRejection = rejectOversizedLimit(corrected, customerId);
13159
- if (limitRejection) return { valid: false, warnings: [], error: limitRejection };
13318
+ for (const reject of [rejectContainsCall, rejectListFieldOperator, rejectUnsupportedDuring, rejectOversizedLimit]) {
13319
+ const rejection2 = reject(corrected, customerId);
13320
+ if (rejection2) return { valid: false, warnings: [], error: rejection2 };
13321
+ }
13160
13322
  if (/\bsegments\.conversion_action_name\b/i.test(corrected) && /\bmetrics\.cost_micros\b/i.test(corrected)) {
13161
13323
  return {
13162
13324
  valid: false,
@@ -33401,19 +33563,6 @@ function pageOf(args5) {
33401
33563
  ...size ? { pageSize: Number(size) } : {}
33402
33564
  };
33403
33565
  }
33404
- function isWholePositive(raw) {
33405
- return /^[1-9]\d*$/.test(String(raw));
33406
- }
33407
- function paginationError(args5) {
33408
- if (args5.page !== void 0 && !isWholePositive(args5.page)) {
33409
- return `--page takes a page number of a list report (1, 2, \u2026), not "${String(args5.page)}". To narrow a report to one page of the site, pass --landing <internalId> (from \`baker analytics landings\`), or read one path with \`baker analytics page --path /pricing\`.`;
33410
- }
33411
- const size = args5["page-size"];
33412
- if (size !== void 0 && !isWholePositive(size)) {
33413
- return `--page-size takes a number of rows (1 to 200), not "${String(size)}".`;
33414
- }
33415
- return null;
33416
- }
33417
33566
  function requestBody(args5, options) {
33418
33567
  const candidates = {
33419
33568
  preset: options.preset,
@@ -33478,11 +33627,6 @@ async function runPreset(args5, options) {
33478
33627
  });
33479
33628
  process.exit(1);
33480
33629
  }
33481
- const pagination = paginationError(args5);
33482
- if (pagination) {
33483
- writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message: pagination } });
33484
- process.exit(1);
33485
- }
33486
33630
  try {
33487
33631
  const response = await apiPost("/api/analytics/query", requestBody(args5, options));
33488
33632
  const requestedPlatform = args5.platform === void 0 ? "" : String(args5.platform);
@@ -57541,7 +57685,7 @@ function mentionsInSource(source, names) {
57541
57685
  const out = [];
57542
57686
  for (const name of names) {
57543
57687
  const seen = /* @__PURE__ */ new Set();
57544
- for (const m of visible.matchAll(new RegExp(`\\b${escapeRegExp(name)}\\b`, "gi"))) {
57688
+ for (const m of visible.matchAll(new RegExp(`\\b${escapeRegExp2(name)}\\b`, "gi"))) {
57545
57689
  const at = m.index ?? 0;
57546
57690
  if (tables.some(([from, to]) => at >= from && at < to)) continue;
57547
57691
  const start = sentenceStart(visible, at);
@@ -57570,7 +57714,7 @@ function blankHtmlKeepingOffsets(text2) {
57570
57714
  }
57571
57715
  return out.join("");
57572
57716
  }
57573
- function escapeRegExp(value) {
57717
+ function escapeRegExp2(value) {
57574
57718
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
57575
57719
  }
57576
57720
  function detectSource(source) {
@@ -68628,36 +68772,19 @@ function findUnknownFlags(root, argv) {
68628
68772
  }
68629
68773
  return unknown;
68630
68774
  }
68631
- var SYNONYMS = {
68632
- from: ["start-date", "start", "since"],
68633
- since: ["start-date", "start", "from"],
68634
- start: ["start-date", "since", "from"],
68635
- to: ["end-date", "end", "until"],
68636
- until: ["end-date", "end", "to"],
68637
- end: ["end-date", "until", "to"],
68638
- path: ["landing"],
68639
- url: ["landing"]
68640
- };
68641
68775
  function closestFlag(unknown, command) {
68642
68776
  const accepted = acceptedNames(command);
68643
68777
  if (!accepted) {
68644
68778
  return null;
68645
68779
  }
68646
68780
  const name = unknown.replace(/^--/, "").split("=")[0] ?? "";
68647
- const synonym = SYNONYMS[name]?.find((candidate) => accepted.has(candidate));
68648
- if (synonym) {
68649
- return `--${synonym}`;
68650
- }
68651
68781
  const scored = [...accepted].filter((candidate) => !ALWAYS_ACCEPTED.has(candidate)).map((candidate) => ({
68652
68782
  candidate,
68653
68783
  prefix: sharedPrefix(name, candidate),
68654
68784
  distance: editDistance(name, candidate)
68655
- })).filter(({ prefix, distance }) => prefix >= 4 || distance <= typoBudget(name)).sort((a, b) => b.prefix - a.prefix || a.distance - b.distance);
68785
+ })).filter(({ prefix, distance }) => prefix >= 4 || distance <= 3).sort((a, b) => b.prefix - a.prefix || a.distance - b.distance);
68656
68786
  return scored[0] ? `--${scored[0].candidate}` : null;
68657
68787
  }
68658
- function typoBudget(name) {
68659
- return Math.max(1, Math.floor(name.length / 3));
68660
- }
68661
68788
  function sharedPrefix(a, b) {
68662
68789
  let i = 0;
68663
68790
  while (i < a.length && i < b.length && a[i] === b[i]) {
@@ -68681,24 +68808,6 @@ function commandFor(root, argv) {
68681
68808
  const { command, resolved } = resolveCommand2(root, argv);
68682
68809
  return resolved ? command : null;
68683
68810
  }
68684
- function suggestFlag(unknown, command) {
68685
- const flag = closestFlag(unknown, command);
68686
- if (!flag) {
68687
- return null;
68688
- }
68689
- const args5 = argsOf(command) ?? {};
68690
- const name = flag.slice(2);
68691
- const def = args5[name] ?? args5[camel(name)];
68692
- const described = def && typeof def === "object" ? def.description : void 0;
68693
- const description = typeof described === "string" ? described.split(/\.\s/)[0]?.trim().replace(/\.$/, "") : "";
68694
- return description ? { flag, description } : { flag };
68695
- }
68696
- function suggestionText(suggestion) {
68697
- if (!suggestion) {
68698
- return "";
68699
- }
68700
- return suggestion.description ? `Did you mean ${suggestion.flag}? It is described as: ${suggestion.description}. ` : `Did you mean ${suggestion.flag}? `;
68701
- }
68702
68811
  var OUTPUT_EXPLANATION = "This command has no --output option: `--output json` is accepted by every command that prints the JSON envelope, and md or files only where `baker schema` lists `output`. Drop the option and read the default output. ";
68703
68812
  function unknownFlagEnvelope(unknown, commandPath, suggestion, hasSchema) {
68704
68813
  const list = unknown.join(", ");
@@ -68709,7 +68818,7 @@ function unknownFlagEnvelope(unknown, commandPath, suggestion, hasSchema) {
68709
68818
  message: `${list} ${unknown.length > 1 ? "are not options" : "is not an option"} of \`baker ${commandPath}\`.`,
68710
68819
  fix: {
68711
68820
  action: hasSchema ? "check_schema" : "check_help",
68712
- explanation: (unknown.some((flag) => flag === "--output" || flag.startsWith("--output=")) ? OUTPUT_EXPLANATION : "") + suggestionText(suggestion) + `Run \`${hasSchema ? `baker schema ${commandPath}` : `baker ${commandPath} --help`}\` to see every option this command takes. Do not re-run the same call: until this version an unrecognised option was accepted and ignored, so a command that looked like it had filtered or narrowed the result was in fact answering the unfiltered question.`
68821
+ explanation: (unknown.some((flag) => flag === "--output" || flag.startsWith("--output=")) ? OUTPUT_EXPLANATION : "") + (suggestion ? `Did you mean ${suggestion}? ` : "") + `Run \`${hasSchema ? `baker schema ${commandPath}` : `baker ${commandPath} --help`}\` to see every option this command takes. Do not re-run the same call: until this version an unrecognised option was accepted and ignored, so a command that looked like it had filtered or narrowed the result was in fact answering the unfiltered question.`
68713
68822
  },
68714
68823
  retryable: false
68715
68824
  }
@@ -68739,7 +68848,7 @@ function refuseUnknownFlags(root, argv) {
68739
68848
  }
68740
68849
  const command = commandFor(root, argv);
68741
68850
  const first = unknown[0];
68742
- const suggestion = command && first ? suggestFlag(first, command) : null;
68851
+ const suggestion = command && first ? closestFlag(first, command) : null;
68743
68852
  const path48 = commandPathOf(root, argv);
68744
68853
  process.stdout.write(
68745
68854
  `${JSON.stringify(unknownFlagEnvelope(unknown, path48, suggestion, findSchema(path48.split(" ")) !== void 0), null, 2)}