@drip-apex/apex-cli 0.14.0 → 0.14.2

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.
Files changed (2) hide show
  1. package/dist/cli.js +207 -93
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -7325,7 +7325,7 @@ async function checkForUpdate(current) {
7325
7325
  // package.json
7326
7326
  var package_default = {
7327
7327
  name: "@drip-apex/apex-cli",
7328
- version: "0.14.0",
7328
+ version: "0.14.2",
7329
7329
  license: "MIT",
7330
7330
  description: "Apex CLI for managing experiments, approved roadmap ideas, and draft workspaces.",
7331
7331
  private: false,
@@ -8336,11 +8336,19 @@ function listExperiments(creds, opts) {
8336
8336
  const qs = params.toString();
8337
8337
  return request(creds, "GET", `/api/v1/experiments${qs ? `?${qs}` : ""}`);
8338
8338
  }
8339
- function getExperiment(creds, id) {
8340
- return request(creds, "GET", `/api/v1/experiments/${id}`);
8339
+ function getExperiment(creds, id, includePreviewTarget = false) {
8340
+ return request(
8341
+ creds,
8342
+ "GET",
8343
+ `/api/v1/experiments/${id}${includePreviewTarget ? "?include=preview_target" : ""}`
8344
+ );
8341
8345
  }
8342
- function getExperimentBySlug(creds, slug) {
8343
- return request(creds, "GET", `/api/v1/experiments/by-slug/${slug}`);
8346
+ function getExperimentBySlug(creds, slug, includePreviewTarget = false) {
8347
+ return request(
8348
+ creds,
8349
+ "GET",
8350
+ `/api/v1/experiments/by-slug/${slug}${includePreviewTarget ? "?include=preview_target" : ""}`
8351
+ );
8344
8352
  }
8345
8353
  function upsertExperimentBySlug(creds, slug, body5) {
8346
8354
  return request(creds, "PUT", `/api/v1/experiments/by-slug/${slug}`, body5);
@@ -9541,10 +9549,10 @@ var evidenceKinds = [
9541
9549
 
9542
9550
  // src/lib/experiment-handover.ts
9543
9551
  function summarizeHandoverQa(value) {
9544
- const record3 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2) ? value2 : {};
9545
- const qa3 = record3(value);
9546
- const run3 = record3(qa3.latestRun);
9547
- const summary = record3(run3.summary);
9552
+ const record4 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2) ? value2 : {};
9553
+ const qa3 = record4(value);
9554
+ const run3 = record4(qa3.latestRun);
9555
+ const summary = record4(run3.summary);
9548
9556
  const count2 = (value2) => typeof value2 === "number" && Number.isInteger(value2) && value2 >= 0 ? value2 : null;
9549
9557
  let requiredDone = count2(summary.completedRequired);
9550
9558
  let requiredTotal = count2(summary.required);
@@ -9619,12 +9627,6 @@ function deviceLabel(targeting) {
9619
9627
  return void 0;
9620
9628
  }
9621
9629
 
9622
- // src/lib/test-key.ts
9623
- var TEST_KEY_RE = /^([A-Z0-9]{2,8})-(\d+)$/;
9624
- function isTestKeyReference(value) {
9625
- return value === value.toUpperCase() && TEST_KEY_RE.test(value.toUpperCase());
9626
- }
9627
-
9628
9630
  // src/lib/effective-page-targeting.ts
9629
9631
  var ALL_PAGES_LABEL = "All pages";
9630
9632
  var CHECKOUT_LABEL = "Checkout";
@@ -9711,6 +9713,99 @@ function classifyIdeaReference(reference) {
9711
9713
  return { kind: "invalid", value: trimmed };
9712
9714
  }
9713
9715
 
9716
+ // ../api-client/src/experiments.ts
9717
+ var ID_REFERENCE = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[1-9][0-9]{9})$/i;
9718
+ function isExperimentTestKey(value) {
9719
+ return /^([A-Z0-9]{2,8})-(\d+)$/.test(value);
9720
+ }
9721
+ function record(value) {
9722
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
9723
+ }
9724
+ async function resolveExperimentReference(request2, idOrSlug, variationRef) {
9725
+ const explicitSlug = idOrSlug.startsWith("slug:");
9726
+ const reference = explicitSlug ? idOrSlug.slice(5) : idOrSlug;
9727
+ const idShaped = !explicitSlug && ID_REFERENCE.test(reference);
9728
+ const keyShaped = !explicitSlug && isExperimentTestKey(reference);
9729
+ const slugPath = `/api/v1/experiments/by-slug/${encodeURIComponent(reference)}`;
9730
+ let res = await request2("GET", idShaped || keyShaped ? `/api/v1/experiments/${encodeURIComponent(reference)}` : slugPath);
9731
+ if (!res.ok && res.status === 404 && idShaped) res = await request2("GET", slugPath);
9732
+ if (!res.ok) return { ok: false, error: res.error ?? "Experiment not found", status: res.status, response: res };
9733
+ const experiment = record(res.data);
9734
+ if (typeof experiment.id !== "string") return { ok: false, error: "Experiment has no id" };
9735
+ const variations = Array.isArray(experiment.variations) ? experiment.variations.map(record) : [];
9736
+ if (variationRef === void 0) return { ok: true, experimentId: experiment.id, variations, experiment };
9737
+ const available = variations.map((v) => String(v.slug ?? v.name)).join(", ") || "none";
9738
+ const byIdentity = variations.filter((v) => v.id === variationRef || v.public_id === variationRef || v.slug === variationRef);
9739
+ const byName = variations.filter((v) => v.name === variationRef);
9740
+ const matches = byIdentity.length > 0 ? byIdentity : byName;
9741
+ if (matches.length > 1) return {
9742
+ ok: false,
9743
+ error: `Variation "${variationRef}" is ambiguous; use one of these slugs: ${matches.map((v) => String(v.slug)).join(", ")}`
9744
+ };
9745
+ const match2 = matches[0];
9746
+ if (typeof match2?.id !== "string") return { ok: false, error: `Variation "${variationRef}" not found. Available: ${available}` };
9747
+ return { ok: true, experimentId: experiment.id, variationId: match2.id, variations, experiment };
9748
+ }
9749
+ function summarizeResults(payload) {
9750
+ const data = record(payload);
9751
+ const experiment = record(data.experiment);
9752
+ const decision = record(data.decision);
9753
+ const outcome = record(data.effective_outcome);
9754
+ const commerce = record(data.commerce);
9755
+ const rawArms = Array.isArray(data.arms) ? data.arms.map(record) : [];
9756
+ const degradedFlags = {
9757
+ explicit: data.degraded === true,
9758
+ commerceUnavailable: commerce.unavailable === true,
9759
+ armMetricsMissing: rawArms.some((arm) => arm.primaryConversions == null || arm.primaryRate == null)
9760
+ };
9761
+ const missing = /* @__PURE__ */ new Set();
9762
+ if (degradedFlags.commerceUnavailable) missing.add("commerce orders");
9763
+ const controlRate = rawArms.find((arm) => arm.isControl === true)?.primaryRate;
9764
+ const treatmentCount = rawArms.filter((arm) => arm.isControl !== true).length;
9765
+ const arms = rawArms.map((arm) => {
9766
+ const judged = arm.isControl !== true && (decision.treatmentVariationId != null ? arm.variationId === decision.treatmentVariationId : treatmentCount === 1);
9767
+ let lift = null;
9768
+ let confidence = null;
9769
+ if (arm.isControl !== true) {
9770
+ if (judged) {
9771
+ lift = decision.lift;
9772
+ confidence = decision.judgedConfidence;
9773
+ } else if (typeof arm.primaryRate === "number" && typeof controlRate === "number" && controlRate > 0)
9774
+ lift = Math.round((arm.primaryRate / controlRate - 1) * 1e4) / 100;
9775
+ if (lift == null) missing.add("lift");
9776
+ if (judged && confidence == null) missing.add("confidence");
9777
+ }
9778
+ if (arm.visitors == null) missing.add("visitors");
9779
+ if (arm.primaryConversions == null) missing.add("conversions");
9780
+ if (arm.primaryRate == null) missing.add("conversion rate");
9781
+ return { ...arm, lift, confidence, judged };
9782
+ });
9783
+ const verdictWithheld = decision.state === "untrustworthy";
9784
+ return {
9785
+ experiment,
9786
+ runWindow: { startedAt: experiment.resultsWindowStartedAt ?? experiment.startedAt, endedAt: experiment.endedAt },
9787
+ primaryGoal: decision.primaryGoalName,
9788
+ totals: record(data.totals),
9789
+ decision,
9790
+ outcome,
9791
+ arms,
9792
+ judgedArm: arms.find((arm) => arm.judged)?.variationId,
9793
+ verdict: outcome.outcome ?? decision.state,
9794
+ verdictWithheld,
9795
+ statisticsState: (verdictWithheld ? decision.state : outcome.statisticsOutcome ?? decision.state) ?? "unknown",
9796
+ presentedOutcome: (verdictWithheld && outcome.provenance !== "manager_decided" ? decision.state : outcome.outcome ?? decision.state) ?? "unknown",
9797
+ degraded: Object.values(degradedFlags).some(Boolean),
9798
+ degradedFlags,
9799
+ missingMetrics: [...missing],
9800
+ warnings: Array.isArray(data.warnings) ? data.warnings.map((value) => ({
9801
+ message: record(value).message,
9802
+ value
9803
+ })) : [],
9804
+ metrics: Array.isArray(data.metrics) ? data.metrics.map(record) : [],
9805
+ resultsWeekdays: data.results_weekdays
9806
+ };
9807
+ }
9808
+
9714
9809
  // ../api-client/src/experiment-create.ts
9715
9810
  function deriveStoreUrl(patterns) {
9716
9811
  for (const pattern of patterns) {
@@ -9976,14 +10071,9 @@ function formatRevenue(value, currency) {
9976
10071
  }
9977
10072
  }
9978
10073
  function printExperimentResults(results2, currency) {
9979
- const experiment = results2.experiment ?? {};
9980
- const totals = results2.totals ?? {};
9981
- const decision = results2.decision ?? {};
9982
- const effectiveOutcome = results2.effective_outcome ?? {};
9983
- const outcomeProvenance = effectiveOutcome.provenance === "manager_decided" ? " (manager decided)" : "";
9984
- const verdictWithheld = decision.state === "untrustworthy";
9985
- const statisticsState = verdictWithheld ? decision.state : effectiveOutcome.statisticsOutcome ?? decision.state ?? "unknown";
9986
- const presentedOutcome = verdictWithheld && effectiveOutcome.provenance !== "manager_decided" ? decision.state : effectiveOutcome.outcome ?? decision.state ?? "unknown";
10074
+ const summary = summarizeResults(results2);
10075
+ const { experiment, totals, decision, verdictWithheld, statisticsState, presentedOutcome } = summary;
10076
+ const outcomeProvenance = summary.outcome.provenance === "manager_decided" ? " (manager decided)" : "";
9987
10077
  console.log();
9988
10078
  console.log(source_default.bold(String(experiment.name ?? experiment.id ?? "Experiment results")));
9989
10079
  console.log(` Status: ${statusBadge(String(experiment.status ?? "unknown"))}`);
@@ -9991,7 +10081,7 @@ function printExperimentResults(results2, currency) {
9991
10081
  console.log(` Conversions: ${String(totals.primaryConversions ?? 0)}`);
9992
10082
  console.log(` Revenue: ${formatRevenue(totals.revenue, currency)}`);
9993
10083
  console.log(` Outcome: ${statisticsStateLabel(presentedOutcome)}${outcomeProvenance}`);
9994
- const resultsWeekdays = results2.results_weekdays;
10084
+ const resultsWeekdays = summary.resultsWeekdays;
9995
10085
  if (resultsWeekdays && Array.isArray(resultsWeekdays.days)) {
9996
10086
  const labels = {
9997
10087
  monday: "Mon",
@@ -10014,7 +10104,7 @@ function printExperimentResults(results2, currency) {
10014
10104
  console.log(
10015
10105
  ` Confidence (${confidenceBasisLabel(decision.basis)}): ${verdictWithheld ? "withheld \u2014 not decision-grade" : formatPct(decision.judgedConfidence)}`
10016
10106
  );
10017
- const arms = Array.isArray(results2.arms) ? results2.arms : [];
10107
+ const arms = summary.arms;
10018
10108
  if (arms.length > 0) {
10019
10109
  console.log();
10020
10110
  printTable(
@@ -10032,7 +10122,7 @@ function printExperimentResults(results2, currency) {
10032
10122
  [32, 8, 6, 8, 10]
10033
10123
  );
10034
10124
  }
10035
- const metrics = Array.isArray(results2.metrics) ? results2.metrics : [];
10125
+ const metrics = summary.metrics;
10036
10126
  if (metrics.length > 0) {
10037
10127
  console.log();
10038
10128
  console.log(source_default.bold("Goal counting"));
@@ -10042,7 +10132,7 @@ function printExperimentResults(results2, currency) {
10042
10132
  });
10043
10133
  }
10044
10134
  console.log(" Definitions: https://docs.drip-apex.com/docs/experiments/metric-definitions");
10045
- const warnings = Array.isArray(results2.warnings) ? results2.warnings : [];
10135
+ const warnings = summary.warnings;
10046
10136
  if (warnings.length > 0) {
10047
10137
  console.log();
10048
10138
  console.log(source_default.yellow("Warnings"));
@@ -10345,10 +10435,10 @@ function printQaRun(run3) {
10345
10435
  }
10346
10436
  if ((run3.evidenceRecords ?? []).length > 0) {
10347
10437
  console.log(source_default.bold("\nEvidence records"));
10348
- for (const record3 of run3.evidenceRecords ?? []) {
10349
- const device = record3.device ? ` \xB7 ${record3.device}` : "";
10438
+ for (const record4 of run3.evidenceRecords ?? []) {
10439
+ const device = record4.device ? ` \xB7 ${record4.device}` : "";
10350
10440
  console.log(
10351
- ` - ${record3.kind} \xB7 ${record3.label} \xB7 ${record3.status}${device} \xB7 ${record3.capturedAt}${qaVersionMarker(record3)}`
10441
+ ` - ${record4.kind} \xB7 ${record4.label} \xB7 ${record4.status}${device} \xB7 ${record4.capturedAt}${qaVersionMarker(record4)}`
10352
10442
  );
10353
10443
  }
10354
10444
  }
@@ -10467,26 +10557,29 @@ async function refreshServerOwnedFilesIfNeeded(creds, folder, experimentId, upda
10467
10557
  }
10468
10558
  }
10469
10559
  var EXPERIMENT_REFERENCE_DESCRIPTION = "Experiment ID, slug, or test key (for example BIO-085)";
10560
+ function resolveCliExperimentReference(creds, reference, variationRef) {
10561
+ return resolveExperimentReference(
10562
+ (_method, requestPath) => {
10563
+ const prefix = "/api/v1/experiments/by-slug/";
10564
+ const bySlug = requestPath.startsWith(prefix);
10565
+ const idOrSlug = decodeURIComponent(
10566
+ requestPath.slice(bySlug ? prefix.length : "/api/v1/experiments/".length)
10567
+ );
10568
+ return bySlug ? getExperimentBySlug(creds, idOrSlug) : getExperiment(creds, idOrSlug);
10569
+ },
10570
+ reference,
10571
+ variationRef
10572
+ );
10573
+ }
10470
10574
  async function resolveExperiment(idOrSlug) {
10471
10575
  const creds = requireCredentials();
10472
- if (/^[0-9a-f-]{36}$/i.test(idOrSlug) || /^[1-9][0-9]{9}$/.test(idOrSlug) || isTestKeyReference(idOrSlug)) {
10473
- const res2 = await getExperiment(creds, idOrSlug);
10474
- if (res2.ok) return res2.data;
10475
- throw new CliError(
10476
- res2.status === 404 && isTestKeyReference(idOrSlug) ? `Experiment "${idOrSlug}" not found in selected shop ${creds.resolvedContext?.shopId ?? "(API key shop)"}. Test keys resolve within the selected shop; use --shop <shop-id> to change it.` : res2.error ?? `Experiment "${idOrSlug}" not found`,
10477
- classifyStatus(res2.status),
10478
- res2.errorDetails,
10479
- apiFailureOf(res2)
10480
- );
10481
- }
10482
- const slug = idOrSlug.startsWith("slug:") ? idOrSlug.slice(5) : idOrSlug;
10483
- const res = await getExperimentBySlug(creds, slug);
10484
- if (res.ok) return res.data;
10576
+ const res = await resolveCliExperimentReference(creds, idOrSlug);
10577
+ if (res.ok) return res.experiment;
10485
10578
  throw new CliError(
10486
- res.error ?? `Experiment "${idOrSlug}" not found`,
10487
- classifyStatus(res.status),
10488
- res.errorDetails,
10489
- apiFailureOf(res)
10579
+ res.status === 404 && isExperimentTestKey(idOrSlug) ? `Experiment "${idOrSlug}" not found in selected shop ${creds.resolvedContext?.shopId ?? "(API key shop)"}. Test keys resolve within the selected shop; use --shop <shop-id> to change it.` : res.response ? res.response.error ?? `Experiment "${idOrSlug}" not found` : res.error,
10580
+ classifyStatus(res.status ?? 500),
10581
+ res.response?.errorDetails,
10582
+ res.response ? apiFailureOf(res.response) : void 0
10490
10583
  );
10491
10584
  }
10492
10585
  var list = new Command("list").alias("ls").description(
@@ -12106,7 +12199,7 @@ var qaEvidence = new Command("evidence").description("Attach screenshot jobs or
12106
12199
  classifyStatus(current.status),
12107
12200
  current.errorDetails
12108
12201
  );
12109
- for (const record3 of current.data.run.evidenceRecords) previousIds.add(record3.id);
12202
+ for (const record4 of current.data.run.evidenceRecords) previousIds.add(record4.id);
12110
12203
  }
12111
12204
  let response;
12112
12205
  if (opts.job) {
@@ -12151,7 +12244,7 @@ var qaEvidence = new Command("evidence").description("Attach screenshot jobs or
12151
12244
  const run3 = response.data.run;
12152
12245
  const data = opts.summary ? qaSummary(
12153
12246
  run3,
12154
- run3.evidenceRecords.filter((record3) => !previousIds.has(record3.id))
12247
+ run3.evidenceRecords.filter((record4) => !previousIds.has(record4.id))
12155
12248
  ) : response.data;
12156
12249
  output(this, data, () => {
12157
12250
  if (opts.summary) process.stdout.write(`${JSON.stringify(data, null, 2)}
@@ -12842,6 +12935,14 @@ var previewCommand = new Command("preview").description(
12842
12935
  const data = created.data;
12843
12936
  output(previewCommand, data, () => {
12844
12937
  console.log(source_default.cyan(data.preview_url));
12938
+ if (data.preview_target) {
12939
+ console.log(`Chosen page: ${data.preview_target.url} (${data.preview_target.source})`);
12940
+ if (!data.preview_target.matches_targeting) {
12941
+ console.log(
12942
+ `The test will not show on this page: ${data.preview_target.reason ?? "The page does not match URL targeting."}`
12943
+ );
12944
+ }
12945
+ }
12845
12946
  console.log(
12846
12947
  data.qa_run.created ? `QA run ${data.qa_run.id} created` : `QA run ${data.qa_run.id} refreshed (${data.qa_run.automated_evidence_count} automated evidence records)`
12847
12948
  );
@@ -12867,7 +12968,7 @@ var previewCommand = new Command("preview").description(
12867
12968
  const spinner = await createSpinner("Fetching experiment...");
12868
12969
  let exp;
12869
12970
  if (/^[0-9a-f-]{36}$/i.test(idOrSlug)) {
12870
- const res = await getExperiment(creds, idOrSlug);
12971
+ const res = await getExperiment(creds, idOrSlug, !opts?.storeUrl);
12871
12972
  if (!res.ok) {
12872
12973
  spinner.fail(`Experiment "${idOrSlug}" not found`);
12873
12974
  fail(
@@ -12880,7 +12981,7 @@ var previewCommand = new Command("preview").description(
12880
12981
  exp = res.data;
12881
12982
  } else {
12882
12983
  const slug = idOrSlug.startsWith("slug:") ? idOrSlug.slice(5) : idOrSlug;
12883
- const res = await getExperimentBySlug(creds, slug);
12984
+ const res = await getExperimentBySlug(creds, slug, !opts?.storeUrl);
12884
12985
  if (!res.ok) {
12885
12986
  spinner.fail(`Experiment "${slug}" not found`);
12886
12987
  fail(
@@ -12915,7 +13016,7 @@ var previewCommand = new Command("preview").description(
12915
13016
  variation = exp.variations.find((v) => !v.is_control) ?? exp.variations[0];
12916
13017
  }
12917
13018
  spinner.stop();
12918
- const storeUrl = opts?.storeUrl ?? exp.store_url;
13019
+ const storeUrl = opts?.storeUrl ?? exp.preview_target?.url ?? exp.store_url;
12919
13020
  if (!storeUrl) {
12920
13021
  fail(
12921
13022
  previewCommand,
@@ -12933,11 +13034,24 @@ var previewCommand = new Command("preview").description(
12933
13034
  }
12934
13035
  output(
12935
13036
  previewCommand,
12936
- { experimentId: exp.id, variationId: variation.id, previewUrl: previewUrl.toString() },
13037
+ {
13038
+ experimentId: exp.id,
13039
+ variationId: variation.id,
13040
+ previewUrl: previewUrl.toString(),
13041
+ ...!opts?.storeUrl && exp.preview_target ? { previewTarget: exp.preview_target } : {}
13042
+ },
12937
13043
  () => {
12938
13044
  console.log();
12939
13045
  console.log(source_default.bold(`Preview: ${exp.name}`));
12940
13046
  console.log(`Variation: ${variation.name}${variation.is_control ? " (control)" : ""}`);
13047
+ if (!opts?.storeUrl && exp.preview_target) {
13048
+ console.log(`Chosen page: ${exp.preview_target.url} (${exp.preview_target.source})`);
13049
+ if (!exp.preview_target.matches_targeting) {
13050
+ console.log(
13051
+ `The test will not show on this page: ${exp.preview_target.reason ?? "The page does not match URL targeting."}`
13052
+ );
13053
+ }
13054
+ }
12941
13055
  console.log();
12942
13056
  console.log(source_default.cyan(previewUrl.toString()));
12943
13057
  console.log();
@@ -15333,18 +15447,18 @@ function fieldsFrom(value, prefix = "", descriptorList = false) {
15333
15447
  )
15334
15448
  );
15335
15449
  }
15336
- const record3 = value;
15337
- const explicitPath = typeof record3.path === "string" ? record3.path : void 0;
15450
+ const record4 = value;
15451
+ const explicitPath = typeof record4.path === "string" ? record4.path : void 0;
15338
15452
  if (descriptorList && explicitPath) {
15339
- const rawValues = record3.values ?? record3.samples ?? record3.examples ?? record3.example;
15453
+ const rawValues = record4.values ?? record4.samples ?? record4.examples ?? record4.example;
15340
15454
  const values = Array.isArray(rawValues) ? rawValues : rawValues === void 0 ? [] : [rawValues];
15341
- const magnitude = [record3.mean, record3.average, record3.max, record3.magnitude].find(
15455
+ const magnitude = [record4.mean, record4.average, record4.max, record4.magnitude].find(
15342
15456
  (candidate) => typeof candidate === "number"
15343
15457
  );
15344
- const numericType = Array.isArray(record3.types) && record3.types.length > 0 && record3.types.every((type) => type === "num" || type === "numstr" || type === "numstr_comma");
15458
+ const numericType = Array.isArray(record4.types) && record4.types.length > 0 && record4.types.every((type) => type === "num" || type === "numstr" || type === "numstr_comma");
15345
15459
  return [{ path: runtimePath(explicitPath), values, magnitude, numericType }];
15346
15460
  }
15347
- return Object.entries(record3).flatMap(([key, child]) => {
15461
+ return Object.entries(record4).flatMap(([key, child]) => {
15348
15462
  if (["eventName", "event_name", "name", "count", "frequency", "evidence"].includes(key))
15349
15463
  return [];
15350
15464
  const next = prefix ? `${prefix}.${key}` : key;
@@ -15377,8 +15491,8 @@ function isDescriptorArray(value) {
15377
15491
  ]);
15378
15492
  return Array.isArray(value) && value.length > 0 && value.every((item) => {
15379
15493
  if (!item || typeof item !== "object" || Array.isArray(item)) return false;
15380
- const record3 = item;
15381
- return typeof record3.path === "string" && record3.path.length > 0 && Object.keys(record3).every((key) => descriptorKeys.has(key));
15494
+ const record4 = item;
15495
+ return typeof record4.path === "string" && record4.path.length > 0 && Object.keys(record4).every((key) => descriptorKeys.has(key));
15382
15496
  });
15383
15497
  }
15384
15498
  function candidatesFrom(report) {
@@ -15534,10 +15648,10 @@ var whenOps = new Set(runtime_settings_trigger_constants_default.whenOps);
15534
15648
  function strictObject(value, location, keys) {
15535
15649
  if (!value || typeof value !== "object" || Array.isArray(value))
15536
15650
  throw new Error(`${location} must be an object`);
15537
- const record3 = value;
15538
- for (const key of Object.keys(record3))
15651
+ const record4 = value;
15652
+ for (const key of Object.keys(record4))
15539
15653
  if (!keys.has(key)) throw new Error(`${location}.${key} is not allowed`);
15540
- return record3;
15654
+ return record4;
15541
15655
  }
15542
15656
  function optionalType(value, type, location) {
15543
15657
  if (value !== void 0 && typeof value !== type)
@@ -15673,19 +15787,19 @@ function strictTriggers(value) {
15673
15787
  return value.map((item, index) => {
15674
15788
  if (!item || typeof item !== "object" || Array.isArray(item))
15675
15789
  throw new Error(`trigger[${index}] must be an object`);
15676
- const record3 = item;
15677
- for (const key of Object.keys(record3))
15790
+ const record4 = item;
15791
+ for (const key of Object.keys(record4))
15678
15792
  if (!allowed.has(key)) throw new Error(`trigger[${index}].${key} is not allowed`);
15679
- if (typeof record3.eventName !== "string" || !record3.eventName.trim())
15793
+ if (typeof record4.eventName !== "string" || !record4.eventName.trim())
15680
15794
  throw new Error(`trigger[${index}].eventName must be a non-empty string`);
15681
15795
  for (const key of ["enabled", "reEvaluate", "trackPageview"])
15682
- optionalType(record3[key], "boolean", `trigger[${index}].${key}`);
15796
+ optionalType(record4[key], "boolean", `trigger[${index}].${key}`);
15683
15797
  for (const key of ["dataLayerName", "trackEventName"])
15684
- optionalType(record3[key], "string", `trigger[${index}].${key}`);
15685
- strictWhen(record3.when, `trigger[${index}].when`);
15686
- if (record3.trackRevenue !== void 0) {
15798
+ optionalType(record4[key], "string", `trigger[${index}].${key}`);
15799
+ strictWhen(record4.when, `trigger[${index}].when`);
15800
+ if (record4.trackRevenue !== void 0) {
15687
15801
  const revenue = strictObject(
15688
- record3.trackRevenue,
15802
+ record4.trackRevenue,
15689
15803
  `trigger[${index}].trackRevenue`,
15690
15804
  /* @__PURE__ */ new Set([
15691
15805
  "enabled",
@@ -15708,21 +15822,21 @@ function strictTriggers(value) {
15708
15822
  );
15709
15823
  }
15710
15824
  }
15711
- if (record3.mappings !== void 0) {
15712
- if (!Array.isArray(record3.mappings))
15825
+ if (record4.mappings !== void 0) {
15826
+ if (!Array.isArray(record4.mappings))
15713
15827
  throw new Error(`trigger[${index}].mappings must be an array`);
15714
- record3.mappings.forEach(
15828
+ record4.mappings.forEach(
15715
15829
  (mapping, mappingIndex) => strictMapping(mapping, `trigger[${index}].mappings[${mappingIndex}]`, false)
15716
15830
  );
15717
15831
  }
15718
- if (record3.properties !== void 0) {
15719
- if (!Array.isArray(record3.properties))
15832
+ if (record4.properties !== void 0) {
15833
+ if (!Array.isArray(record4.properties))
15720
15834
  throw new Error(`trigger[${index}].properties must be an array`);
15721
- record3.properties.forEach(
15835
+ record4.properties.forEach(
15722
15836
  (mapping, mappingIndex) => strictMapping(mapping, `trigger[${index}].properties[${mappingIndex}]`, true)
15723
15837
  );
15724
15838
  }
15725
- return record3;
15839
+ return record4;
15726
15840
  });
15727
15841
  }
15728
15842
  function writeWarnings(result) {
@@ -16251,7 +16365,7 @@ function compare(a, b) {
16251
16365
  const left = a.fraction.padEnd(precision, "0"), right = b.fraction.padEnd(precision, "0");
16252
16366
  return left === right ? 0 : left < right ? -1 : 1;
16253
16367
  }
16254
- var record = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
16368
+ var record2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
16255
16369
  var text = (v) => typeof v === "string" && v.length > 0;
16256
16370
  var reason = (v) => typeof v === "string" && (/^[a-z][a-z0-9_]{0,79}$/.test(v) || /^(publishedConfig|consentPolicy|goalMapping|sdkArtifact|sdkPolicy|identityPolicy|goalDefinition|resultWindow|inputSnapshot)_(expected_unknown|observed_unknown|mismatch|binding_mismatch)$/.test(
16257
16371
  v
@@ -16260,8 +16374,8 @@ var member = (v, values) => typeof v === "string" && values.includes(v);
16260
16374
  var status = (v) => member(v, ["pass", "fail", "inconclusive"]);
16261
16375
  var count = (v) => typeof v === "number" && Number.isSafeInteger(v) && v >= 0;
16262
16376
  function validReport(v) {
16263
- if (!record(v) || v.schemaVersion !== 1 || v.finalStatisticalContribution !== "not_evaluated" || !record(v.evaluation) || !status(v.evaluation.status) || !member(v.evaluation.freshness, ["current", "stale", "unknown"]) || !Array.isArray(v.evaluation.stages) || !v.evaluation.stages.every(
16264
- (s) => record(s) && (s.freshness === void 0 || member(s.freshness, ["current", "stale", "unknown"])) && member(s.stage, [
16377
+ if (!record2(v) || v.schemaVersion !== 1 || v.finalStatisticalContribution !== "not_evaluated" || !record2(v.evaluation) || !status(v.evaluation.status) || !member(v.evaluation.freshness, ["current", "stale", "unknown"]) || !Array.isArray(v.evaluation.stages) || !v.evaluation.stages.every(
16378
+ (s) => record2(s) && (s.freshness === void 0 || member(s.freshness, ["current", "stale", "unknown"])) && member(s.stage, [
16265
16379
  "consent",
16266
16380
  "assignment",
16267
16381
  "event",
@@ -16273,17 +16387,17 @@ function validReport(v) {
16273
16387
  ))
16274
16388
  return false;
16275
16389
  if (v.receiptOutcomes !== void 0 && (!Array.isArray(v.receiptOutcomes) || v.receiptOutcomes.length > 6856 || !v.receiptOutcomes.every(
16276
- (r) => record(r) && text(r.receiptId) && text(r.stage) && text(r.producerContract) && record(r.scope) && [r.scope.experimentId, r.scope.variationId, r.scope.goalId].every(
16390
+ (r) => record2(r) && text(r.receiptId) && text(r.stage) && text(r.producerContract) && record2(r.scope) && [r.scope.experimentId, r.scope.variationId, r.scope.goalId].every(
16277
16391
  (x) => x === null || text(x)
16278
16392
  ) && member(r.freshness, ["current", "stale", "unknown"]) && Array.isArray(r.reasonCodes) && r.reasonCodes.every(reason)
16279
16393
  )))
16280
16394
  return false;
16281
16395
  const coverage = v.assignmentCoverage;
16282
- if (!record(coverage) || typeof coverage.complete !== "boolean" || typeof coverage.overflowCountIsLowerBound !== "boolean" || !(coverage.overflowCount === null || count(coverage.overflowCount)))
16396
+ if (!record2(coverage) || typeof coverage.complete !== "boolean" || typeof coverage.overflowCountIsLowerBound !== "boolean" || !(coverage.overflowCount === null || count(coverage.overflowCount)))
16283
16397
  return false;
16284
16398
  return Array.isArray(v.assignments) && v.assignments.length <= 50 && v.assignments.every(
16285
- (a) => record(a) && text(a.experimentId) && text(a.variationId) && (a.storedAttribution === null || typeof a.storedAttribution === "boolean") && reason(a.reasonCode) && Array.isArray(a.goals) && a.goals.length <= 50 && a.goals.every(
16286
- (g) => record(g) && text(g.goalId) && member(g.status, [
16399
+ (a) => record2(a) && text(a.experimentId) && text(a.variationId) && (a.storedAttribution === null || typeof a.storedAttribution === "boolean") && reason(a.reasonCode) && Array.isArray(a.goals) && a.goals.length <= 50 && a.goals.every(
16400
+ (g) => record2(g) && text(g.goalId) && member(g.status, [
16287
16401
  "eligible_for_attributed_goal_read",
16288
16402
  "excluded_from_attributed_goal_read",
16289
16403
  "unknown"
@@ -16654,7 +16768,7 @@ var PURCHASE_CADENCE_WINDOW_MINUTES = PURCHASE_WINDOW_MINUTES * 4;
16654
16768
  var MIN_CAPTURE_WINDOW_MINUTES = 60;
16655
16769
  var DUPLICATE_RATE_THRESHOLD = 0.02;
16656
16770
  var MAGNITUDE_TOLERANCE_RATE = 0.1;
16657
- function record2(value) {
16771
+ function record3(value) {
16658
16772
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
16659
16773
  }
16660
16774
  function purchasePlane(config) {
@@ -16662,8 +16776,8 @@ function purchasePlane(config) {
16662
16776
  const names = [];
16663
16777
  const triggers = config.runtime?.dataLayerTriggers ?? config.dataLayerTriggers;
16664
16778
  if (Array.isArray(triggers) && triggers.some((trigger) => {
16665
- const row = record2(trigger);
16666
- const trackRevenue = record2(row?.trackRevenue);
16779
+ const row = record3(trigger);
16780
+ const trackRevenue = record3(row?.trackRevenue);
16667
16781
  return row?.enabled !== false && trackRevenue !== null && trackRevenue.enabled !== false;
16668
16782
  }))
16669
16783
  names.push("dataLayer trigger");
@@ -16705,7 +16819,7 @@ function lineItemSum(value) {
16705
16819
  if (!Array.isArray(value)) return null;
16706
16820
  let sum = 0;
16707
16821
  for (const item of value) {
16708
- const row = record2(item);
16822
+ const row = record3(item);
16709
16823
  const price = numeric(row?.price);
16710
16824
  if (price === null) return null;
16711
16825
  sum += price * (numeric(row?.quantity) ?? 1);
@@ -18245,10 +18359,10 @@ function getEdgeDeliveryStatus(deliveryType, expectedWorstCaseSeconds = 360) {
18245
18359
  return deliveryType === "api" ? { edge_delivery: "not_applicable", message: `API-delivered flag \u2014 edge delivery is not applicable. A previously cached visual payload may remain at some PoPs for up to ~${expectedWorstCaseSeconds}s; a single-vantage status cannot prove global propagation.` } : { edge_delivery: "applicable", message: null };
18246
18360
  }
18247
18361
  function parsePublicationMetadata(value) {
18248
- const record3 = value && typeof value === "object" ? value : {};
18362
+ const record4 = value && typeof value === "object" ? value : {};
18249
18363
  return {
18250
- publication_revision: Number.isSafeInteger(record3.publication_revision) && record3.publication_revision >= 0 ? record3.publication_revision : null,
18251
- generated_at: typeof record3.generated_at === "string" && !Number.isNaN(Date.parse(record3.generated_at)) ? record3.generated_at : null
18364
+ publication_revision: Number.isSafeInteger(record4.publication_revision) && record4.publication_revision >= 0 ? record4.publication_revision : null,
18365
+ generated_at: typeof record4.generated_at === "string" && !Number.isNaN(Date.parse(record4.generated_at)) ? record4.generated_at : null
18252
18366
  };
18253
18367
  }
18254
18368
  function compareEdgePublication(currentValue, edgeValue, expectedWorstCaseSeconds, flagUpdatedAtValue, confirmedAt = (/* @__PURE__ */ new Date()).toISOString()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drip-apex/apex-cli",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "license": "MIT",
5
5
  "description": "Apex CLI for managing experiments, approved roadmap ideas, and draft workspaces.",
6
6
  "private": false,