@koda-sl/baker-cli 0.354.0-dev.faae9de67 → 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/README.md +3 -3
- package/dist/{chunk-B7MLH3K6.js → chunk-4HRJQGVW.js} +4 -4
- package/dist/chunk-4HRJQGVW.js.map +1 -0
- package/dist/cli.js +199 -122
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-B7MLH3K6.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
validateCanvasDeep,
|
|
69
69
|
withoutCastName,
|
|
70
70
|
ytDlpBlockSignal
|
|
71
|
-
} from "./chunk-
|
|
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
|
|
13157
|
-
|
|
13158
|
-
|
|
13159
|
-
|
|
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,
|
|
@@ -47115,80 +47277,6 @@ async function declarationsOf(root, files) {
|
|
|
47115
47277
|
return sources.flatMap(eventDeclarationsIn);
|
|
47116
47278
|
}
|
|
47117
47279
|
|
|
47118
|
-
// src/commands/experiment/readout.ts
|
|
47119
|
-
function isoMoment(ms) {
|
|
47120
|
-
return ms === null ? null : new Date(ms).toISOString();
|
|
47121
|
-
}
|
|
47122
|
-
function isoDay2(ms) {
|
|
47123
|
-
return ms === null ? null : new Date(ms).toISOString().slice(0, 10);
|
|
47124
|
-
}
|
|
47125
|
-
function compactArm(arm) {
|
|
47126
|
-
return { visitors: arm.visitors, conversions: arm.conversions, rate: Math.round(arm.rate * 1e4) / 1e4 };
|
|
47127
|
-
}
|
|
47128
|
-
function compactMetric(metric) {
|
|
47129
|
-
return {
|
|
47130
|
-
name: metric.name,
|
|
47131
|
-
reading: metric.reading,
|
|
47132
|
-
control: metric.control.conversions,
|
|
47133
|
-
variant: metric.variant.conversions
|
|
47134
|
-
};
|
|
47135
|
-
}
|
|
47136
|
-
function fullArm(arm) {
|
|
47137
|
-
return {
|
|
47138
|
-
...arm,
|
|
47139
|
-
...arm.snapshotCapturedAt === void 0 ? {} : { snapshotCapturedAt: isoMoment(arm.snapshotCapturedAt) }
|
|
47140
|
-
};
|
|
47141
|
-
}
|
|
47142
|
-
function statusReadout(row, { full, now }) {
|
|
47143
|
-
const decisionWhen = {
|
|
47144
|
-
earliestDecisionAt: isoDay2(row.earliestDecisionAt),
|
|
47145
|
-
...row.earliestDecisionAt === null ? {} : { earliestDecisionIn: describeWait(row.earliestDecisionAt, now) }
|
|
47146
|
-
};
|
|
47147
|
-
const moments = {
|
|
47148
|
-
startedAt: isoMoment(row.startedAt),
|
|
47149
|
-
pausedAt: isoMoment(row.pausedAt),
|
|
47150
|
-
finishedAt: isoMoment(row.finishedAt)
|
|
47151
|
-
};
|
|
47152
|
-
if (full) {
|
|
47153
|
-
return {
|
|
47154
|
-
...row,
|
|
47155
|
-
...moments,
|
|
47156
|
-
...decisionWhen,
|
|
47157
|
-
control: fullArm(row.control),
|
|
47158
|
-
variant: fullArm(row.variant)
|
|
47159
|
-
};
|
|
47160
|
-
}
|
|
47161
|
-
return {
|
|
47162
|
-
experimentId: row.experimentId,
|
|
47163
|
-
landingSlug: row.landingSlug,
|
|
47164
|
-
variantSlug: row.variantSlug,
|
|
47165
|
-
status: row.status,
|
|
47166
|
-
verdict: row.verdict,
|
|
47167
|
-
summary: row.summary,
|
|
47168
|
-
...row.winner === null ? {} : { winner: row.winner },
|
|
47169
|
-
...row.invalidReason === null ? {} : { invalidReason: row.invalidReason },
|
|
47170
|
-
stillNeeded: row.stillNeeded,
|
|
47171
|
-
...decisionWhen,
|
|
47172
|
-
willNotConclude: row.willNotConclude,
|
|
47173
|
-
control: compactArm(row.control),
|
|
47174
|
-
variant: compactArm(row.variant),
|
|
47175
|
-
secondary: row.secondary.map(compactMetric),
|
|
47176
|
-
hypothesisStatement: row.hypothesisStatement,
|
|
47177
|
-
minDetectableRelativeLift: row.minDetectableRelativeLift,
|
|
47178
|
-
goal: row.goal,
|
|
47179
|
-
...row.variantGoal === null ? {} : { variantGoal: row.variantGoal },
|
|
47180
|
-
goalLabel: row.goalLabel,
|
|
47181
|
-
...moments,
|
|
47182
|
-
...row.status === "finished" ? {
|
|
47183
|
-
survivor: row.survivor,
|
|
47184
|
-
hypothesisOutcome: row.hypothesisOutcome,
|
|
47185
|
-
fold: row.fold,
|
|
47186
|
-
learning: row.learning,
|
|
47187
|
-
endReason: row.endReason
|
|
47188
|
-
} : {}
|
|
47189
|
-
};
|
|
47190
|
-
}
|
|
47191
|
-
|
|
47192
47280
|
// src/commands/experiment/update.ts
|
|
47193
47281
|
function buildUpdateRequest(args5, rawArgs) {
|
|
47194
47282
|
const expect = args5.expect === void 0 ? void 0 : String(args5.expect);
|
|
@@ -47430,26 +47518,18 @@ registerSchema({
|
|
|
47430
47518
|
args: {
|
|
47431
47519
|
id: { type: "string", description: "One test, by its id", required: false },
|
|
47432
47520
|
landing: LANDING_FILTER_ARG,
|
|
47433
|
-
full: {
|
|
47434
|
-
type: "boolean",
|
|
47435
|
-
description: "Every field the dashboard reads too \u2014 the hypothesis in parts, what each version owns, the preview pictures and the posteriors behind the verdict",
|
|
47436
|
-
required: false
|
|
47437
|
-
}
|
|
47521
|
+
full: { type: "boolean", description: "Include the posteriors behind the verdict", required: false }
|
|
47438
47522
|
}
|
|
47439
47523
|
});
|
|
47440
47524
|
var statusCommand6 = defineCommand124({
|
|
47441
47525
|
meta: {
|
|
47442
47526
|
name: "status",
|
|
47443
|
-
description: "The verdict on each test. Read `verdict` and nothing else to decide: keep_running | winner | no_difference | cannot_win | invalid. `cannot_win` means even the best case is under the lift the test was looking for \u2014 end it and keep the original. `stillNeeded` is the data (visitors, conversions) the test still needs; a test is gated on data, never on days. `
|
|
47527
|
+
description: "The verdict on each test. Read `verdict` and nothing else to decide: keep_running | winner | no_difference | cannot_win | invalid. `cannot_win` means even the best case is under the lift the test was looking for \u2014 end it and keep the original. `stillNeeded` is the data (visitors, conversions) the test still needs; a test is gated on data, never on days. `summary` is one sentence you can show the client, and `secondary` lists every other conversion the company counts on the same two arms \u2014 context, never a second decision. The numbers under `--full` are for printing, never for deciding \u2014 a test that says keep_running has not finished, however good its numbers look."
|
|
47444
47528
|
},
|
|
47445
47529
|
args: {
|
|
47446
47530
|
id: { type: "string", description: "One test, by its id", required: false },
|
|
47447
47531
|
landing: LANDING_FILTER_ARG,
|
|
47448
|
-
full: {
|
|
47449
|
-
type: "boolean",
|
|
47450
|
-
description: "Every field the dashboard reads too \u2014 the hypothesis in parts, what each version owns, the preview pictures and the posteriors behind the verdict",
|
|
47451
|
-
required: false
|
|
47452
|
-
}
|
|
47532
|
+
full: { type: "boolean", description: "Include the posteriors behind the verdict", required: false }
|
|
47453
47533
|
},
|
|
47454
47534
|
run: async ({ args: args5 }) => {
|
|
47455
47535
|
try {
|
|
@@ -47458,13 +47538,10 @@ var statusCommand6 = defineCommand124({
|
|
|
47458
47538
|
...args5.landing ? { landingSlug: String(args5.landing) } : {},
|
|
47459
47539
|
...args5.full === true ? { full: true } : {}
|
|
47460
47540
|
});
|
|
47461
|
-
const now = Date.now();
|
|
47462
47541
|
writeJsonEnvelope({
|
|
47463
47542
|
ok: true,
|
|
47464
|
-
data:
|
|
47465
|
-
|
|
47466
|
-
},
|
|
47467
|
-
hints: buildStatusHints(response.data.experiments, now)
|
|
47543
|
+
data: response.data,
|
|
47544
|
+
hints: buildStatusHints(response.data.experiments)
|
|
47468
47545
|
});
|
|
47469
47546
|
} catch (error) {
|
|
47470
47547
|
reportApiError(error);
|
|
@@ -57608,7 +57685,7 @@ function mentionsInSource(source, names) {
|
|
|
57608
57685
|
const out = [];
|
|
57609
57686
|
for (const name of names) {
|
|
57610
57687
|
const seen = /* @__PURE__ */ new Set();
|
|
57611
|
-
for (const m of visible.matchAll(new RegExp(`\\b${
|
|
57688
|
+
for (const m of visible.matchAll(new RegExp(`\\b${escapeRegExp2(name)}\\b`, "gi"))) {
|
|
57612
57689
|
const at = m.index ?? 0;
|
|
57613
57690
|
if (tables.some(([from, to]) => at >= from && at < to)) continue;
|
|
57614
57691
|
const start = sentenceStart(visible, at);
|
|
@@ -57637,7 +57714,7 @@ function blankHtmlKeepingOffsets(text2) {
|
|
|
57637
57714
|
}
|
|
57638
57715
|
return out.join("");
|
|
57639
57716
|
}
|
|
57640
|
-
function
|
|
57717
|
+
function escapeRegExp2(value) {
|
|
57641
57718
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
57642
57719
|
}
|
|
57643
57720
|
function detectSource(source) {
|