@liiift-studio/sanity-visitor-insights 0.34.0 → 0.35.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/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +15 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +45 -46
- package/dist/index.mjs.map +1 -1
- package/dist/{ranges-CebNbojW.d.mts → ranges-Cc3Bv5-l.d.mts} +8 -1
- package/dist/{ranges-CebNbojW.d.ts → ranges-Cc3Bv5-l.d.ts} +8 -1
- package/dist/server.d.mts +4 -4
- package/dist/server.d.ts +4 -4
- package/dist/server.js +44 -17
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +44 -17
- package/dist/server.mjs.map +1 -1
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/{vercel-XCTmoX7Z.d.mts → vercel-TiuokGk8.d.mts} +6 -15
- package/dist/{vercel-XCTmoX7Z.d.ts → vercel-TiuokGk8.d.ts} +6 -15
- package/package.json +1 -1
- package/src/core/capture.ts +16 -3
- package/src/server/createHandler.ts +9 -1
- package/src/server/diagnostics.ts +1 -1
- package/src/server/reports/acquisition.ts +8 -1
- package/src/server/reports/measurementHealth.ts +32 -7
- package/src/server/reports/reports.test.ts +41 -2
- package/src/server/vercel.ts +40 -6
- package/src/studio/CrossSourceTimeline.tsx +18 -7
- package/src/studio/Figure.tsx +5 -0
- package/src/studio/VisitorInsightsTool.tsx +16 -6
- package/src/studio/panels.test.tsx +34 -2
- package/src/studio/panels.tsx +9 -4
- package/src/types.ts +5 -0
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as sanity from 'sanity';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { R as ReportEnvelope, a as ReportName, b as RangeKey, M as MetricValue, A as AcquisitionData, c as MeasurementHealthData, D as DiagnosticReport, J as JourneyData, T as TypefaceInterestData } from './ranges-
|
|
4
|
-
export { C as CaptureBasis, d as CaptureEstimate, e as CaptureModel, f as CheckStatus, g as Coverage, h as CrossSourceDay, i as DailyPoint, j as DateRange, k as DiagnosticCheck, E as EmailCampaign, l as EventCutover, m as JourneyOutcome, n as JourneyStep, L as LandingPage, o as LicenceTierRow, P as PREEXISTING, p as REPORT_NAMES, q as ReportError, S as SiteAnalyticsConfig, r as SourceName, s as SourceRow, t as SourceStatus, u as TimelineEvent, v as TypefaceInterestRow, U as UnavailableReason, w as coverageForRange, x as estimated, y as isReportName, z as ok, B as partial, F as previousRange, G as resolveRange, H as unavailable, I as validateSiteConfig, K as valueOrNull } from './ranges-
|
|
3
|
+
import { R as ReportEnvelope, a as ReportName, b as RangeKey, M as MetricValue, A as AcquisitionData, c as MeasurementHealthData, D as DiagnosticReport, J as JourneyData, T as TypefaceInterestData } from './ranges-Cc3Bv5-l.mjs';
|
|
4
|
+
export { C as CaptureBasis, d as CaptureEstimate, e as CaptureModel, f as CheckStatus, g as Coverage, h as CrossSourceDay, i as DailyPoint, j as DateRange, k as DiagnosticCheck, E as EmailCampaign, l as EventCutover, m as JourneyOutcome, n as JourneyStep, L as LandingPage, o as LicenceTierRow, P as PREEXISTING, p as REPORT_NAMES, q as ReportError, S as SiteAnalyticsConfig, r as SourceName, s as SourceRow, t as SourceStatus, u as TimelineEvent, v as TypefaceInterestRow, U as UnavailableReason, w as coverageForRange, x as estimated, y as isReportName, z as ok, B as partial, F as previousRange, G as resolveRange, H as unavailable, I as validateSiteConfig, K as valueOrNull } from './ranges-Cc3Bv5-l.mjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Data hook for the Studio panels.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as sanity from 'sanity';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { R as ReportEnvelope, a as ReportName, b as RangeKey, M as MetricValue, A as AcquisitionData, c as MeasurementHealthData, D as DiagnosticReport, J as JourneyData, T as TypefaceInterestData } from './ranges-
|
|
4
|
-
export { C as CaptureBasis, d as CaptureEstimate, e as CaptureModel, f as CheckStatus, g as Coverage, h as CrossSourceDay, i as DailyPoint, j as DateRange, k as DiagnosticCheck, E as EmailCampaign, l as EventCutover, m as JourneyOutcome, n as JourneyStep, L as LandingPage, o as LicenceTierRow, P as PREEXISTING, p as REPORT_NAMES, q as ReportError, S as SiteAnalyticsConfig, r as SourceName, s as SourceRow, t as SourceStatus, u as TimelineEvent, v as TypefaceInterestRow, U as UnavailableReason, w as coverageForRange, x as estimated, y as isReportName, z as ok, B as partial, F as previousRange, G as resolveRange, H as unavailable, I as validateSiteConfig, K as valueOrNull } from './ranges-
|
|
3
|
+
import { R as ReportEnvelope, a as ReportName, b as RangeKey, M as MetricValue, A as AcquisitionData, c as MeasurementHealthData, D as DiagnosticReport, J as JourneyData, T as TypefaceInterestData } from './ranges-Cc3Bv5-l.js';
|
|
4
|
+
export { C as CaptureBasis, d as CaptureEstimate, e as CaptureModel, f as CheckStatus, g as Coverage, h as CrossSourceDay, i as DailyPoint, j as DateRange, k as DiagnosticCheck, E as EmailCampaign, l as EventCutover, m as JourneyOutcome, n as JourneyStep, L as LandingPage, o as LicenceTierRow, P as PREEXISTING, p as REPORT_NAMES, q as ReportError, S as SiteAnalyticsConfig, r as SourceName, s as SourceRow, t as SourceStatus, u as TimelineEvent, v as TypefaceInterestRow, U as UnavailableReason, w as coverageForRange, x as estimated, y as isReportName, z as ok, B as partial, F as previousRange, G as resolveRange, H as unavailable, I as validateSiteConfig, K as valueOrNull } from './ranges-Cc3Bv5-l.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Data hook for the Studio panels.
|
package/dist/index.js
CHANGED
|
@@ -263,7 +263,12 @@ var REASON_TEXT = {
|
|
|
263
263
|
suppressed: "Withheld by GA4 for privacy",
|
|
264
264
|
outage: "Not recorded during part of this period",
|
|
265
265
|
source_error: "Source did not respond",
|
|
266
|
-
not_applicable: "Does not apply to this site"
|
|
266
|
+
not_applicable: "Does not apply to this site",
|
|
267
|
+
// Its own reason, because it was borrowing not_applicable — so a figure that is temporarily
|
|
268
|
+
// missing rendered "Does not apply to this site. This site's API route predates this figure."
|
|
269
|
+
// Two flatly contradictory sentences, and a hurried reader takes the first: this foundry has no
|
|
270
|
+
// revenue. That is the mistake the top of this file exists to forbid, one layer up.
|
|
271
|
+
route_outdated: "Not available from this site yet"
|
|
267
272
|
};
|
|
268
273
|
var LOCALE = "en-GB";
|
|
269
274
|
function formatCount(value) {
|
|
@@ -1131,6 +1136,7 @@ function CrossSourceTimeline({ series, markers = [], currency, onBrush }) {
|
|
|
1131
1136
|
});
|
|
1132
1137
|
const summary = `${dates.length} days from ${tickLabel(/* @__PURE__ */ new Date(`${dates[0]}T00:00:00Z`))} to ${tickLabel(/* @__PURE__ */ new Date(`${dates[dates.length - 1]}T00:00:00Z`))}. ` + series.map(shapeOf).join(". ") + ". " + missed.join(" ") + (markers.length > 0 ? ` ${markers.length} campaign send${markers.length === 1 ? "" : "s"} marked.` : "");
|
|
1133
1138
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_sanity_ui_compat2.Stack, { space: 3, children: [
|
|
1139
|
+
missed.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Card, { padding: 3, radius: 2, tone: "caution", border: true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Stack, { space: 2, children: missed.map((sentence) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Text, { size: 1, children: sentence }, sentence)) }) }),
|
|
1134
1140
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: frameStyle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1135
1141
|
"svg",
|
|
1136
1142
|
{
|
|
@@ -1422,12 +1428,6 @@ function CrossSourceTimeline({ series, markers = [], currency, onBrush }) {
|
|
|
1422
1428
|
row.source,
|
|
1423
1429
|
")"
|
|
1424
1430
|
] }, row.key)),
|
|
1425
|
-
missed.map((sentence) => (
|
|
1426
|
-
// The region's argument, stated as a number. It was drawn to scale and described
|
|
1427
|
-
// in the abstract, so the two facts it exists to convey — how much was missed and
|
|
1428
|
-
// when it was worst — were available only by looking hard at a pale fill.
|
|
1429
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Text, { size: 0, muted: true, children: sentence }, sentence)
|
|
1430
|
-
)),
|
|
1431
1431
|
series.some((row) => row.shortfall) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Text, { size: 0, muted: true, children: "Shaded: what your analytics did not see." }),
|
|
1432
1432
|
markers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Text, { size: 0, muted: true, children: "Vertical rules mark campaign sends." }),
|
|
1433
1433
|
onBrush && // The refusal replaces the instruction rather than sitting beside it, and it is a
|
|
@@ -1472,9 +1472,9 @@ function metricSortValue(metric) {
|
|
|
1472
1472
|
}
|
|
1473
1473
|
function metricOr(metric, detail) {
|
|
1474
1474
|
if (metric) return metric;
|
|
1475
|
-
return { status: "unavailable", reason: "
|
|
1475
|
+
return { status: "unavailable", reason: "route_outdated", detail };
|
|
1476
1476
|
}
|
|
1477
|
-
var OLDER_ROUTE = "This site\u2019s
|
|
1477
|
+
var OLDER_ROUTE = "This site\u2019s analytics route is older than this figure \u2014 it will appear after the site is next deployed.";
|
|
1478
1478
|
function maxOf(metrics) {
|
|
1479
1479
|
return metrics.reduce((max, metric) => metric.status === "unavailable" ? max : Math.max(max, metric.value), 0);
|
|
1480
1480
|
}
|
|
@@ -1789,7 +1789,6 @@ function Verdict({ data, previous }) {
|
|
|
1789
1789
|
say("Revenue", data.revenue, previous?.revenue, "money");
|
|
1790
1790
|
say("Traffic", data.vercelPageviews, previous?.vercelPageviews, "count");
|
|
1791
1791
|
const sends = data.campaigns?.length ?? 0;
|
|
1792
|
-
if (sends > 0) parts.push(`${sends} campaign${sends === 1 ? "" : "s"} sent`);
|
|
1793
1792
|
const shortfall = data.shortfallRatio;
|
|
1794
1793
|
const seeing = shortfall !== null && shortfall !== void 0 ? `GA4 is seeing ${formatPercent(1 - shortfall, 0)} of your traffic` : null;
|
|
1795
1794
|
const broken = shortfall === null || shortfall === void 0 ? data.capture?.discrepancy ? "measurement disagrees between sources" : null : shortfall > 0.6 ? `${seeing} \u2014 treat its figures as broken` : shortfall > 0.35 ? `${seeing} \u2014 its figures are undercounts, not what happened` : data.capture?.discrepancy ? "measurement disagrees between sources" : null;
|
|
@@ -1871,7 +1870,7 @@ function DataHealthPanel({ data, diagnostics }) {
|
|
|
1871
1870
|
] }) }),
|
|
1872
1871
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Card, { padding: 3, radius: 2, tone: "transparent", border: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 3, children: [
|
|
1873
1872
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: "Consent granted" }),
|
|
1874
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MetricFigure, { metric: data.consentRate, label: "Consent granted,
|
|
1873
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MetricFigure, { metric: data.consentRate, label: "Consent granted, share of visitors GA4 saw", unit: "percent" })
|
|
1875
1874
|
] }) })
|
|
1876
1875
|
] })
|
|
1877
1876
|
] }),
|
|
@@ -2428,7 +2427,7 @@ var PANELS = [
|
|
|
2428
2427
|
{ id: "acquisition", report: "acquisition", label: "Acquisition", blurb: "Where visitors come from" },
|
|
2429
2428
|
{ id: "journey", report: "journey", label: "Journey", blurb: "How far visitors get" },
|
|
2430
2429
|
{ id: "typeface-interest", report: "typeface-interest", label: "Typeface interest", blurb: "Viewed, tested and bought, by family" },
|
|
2431
|
-
{ id: "data-health", report: "measurement-health", label: "Data health", blurb: "Whether the
|
|
2430
|
+
{ id: "data-health", report: "measurement-health", label: "Data health", blurb: "Whether to trust what the other tabs are telling you" }
|
|
2432
2431
|
];
|
|
2433
2432
|
function RangeSelector({
|
|
2434
2433
|
value,
|
|
@@ -2615,14 +2614,14 @@ function CoverageRibbon({ ratio }) {
|
|
|
2615
2614
|
const seen = Math.round((1 - ratio) * 100);
|
|
2616
2615
|
const multiplier = seen > 0 ? Math.round(100 / seen) : null;
|
|
2617
2616
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_sanity_ui_compat4.Card, { padding: 3, radius: 2, tone: ratio > 0.6 ? "critical" : "caution", border: true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_sanity_ui_compat4.Text, { size: 1, children: [
|
|
2618
|
-
"
|
|
2617
|
+
"Most figures on this tab come from Google Analytics, which is seeing roughly ",
|
|
2619
2618
|
seen,
|
|
2620
2619
|
"% of this site\u2019s traffic in this window \u2014 so the real numbers are",
|
|
2621
2620
|
" ",
|
|
2622
2621
|
multiplier === null ? "many times" : `several times`,
|
|
2623
2622
|
" these",
|
|
2624
2623
|
multiplier !== null && multiplier > 1 ? ` (very roughly ${multiplier}\xD7)` : "",
|
|
2625
|
-
". Data health explains
|
|
2624
|
+
". Figures drawn from your orders \u2014 anything labelled as coming from Sanity \u2014 are exact and must not be scaled up. Data health explains the rest, and how uncertain it is."
|
|
2626
2625
|
] }) });
|
|
2627
2626
|
}
|
|
2628
2627
|
function ReadyReport({
|
|
@@ -2777,8 +2776,8 @@ function VisitorInsightsTool(props) {
|
|
|
2777
2776
|
const [linked] = (0, import_react4.useState)(() => typeof window === "undefined" ? {} : decodeView(window.location.hash));
|
|
2778
2777
|
const linkedRange = RANGES.some((r) => r.key === linked.range) ? linked.range : null;
|
|
2779
2778
|
const linkedTab = PANELS.some((p) => p.id === linked.tab) ? linked.tab : null;
|
|
2780
|
-
const [range, setRange] = (0, import_react4.useState)(linkedRange ?? "
|
|
2781
|
-
const [rangeBeforeBrush, setRangeBeforeBrush] = (0, import_react4.useState)(linkedRange ?? "
|
|
2779
|
+
const [range, setRange] = (0, import_react4.useState)(linkedRange ?? "month");
|
|
2780
|
+
const [rangeBeforeBrush, setRangeBeforeBrush] = (0, import_react4.useState)(linkedRange ?? "month");
|
|
2782
2781
|
const [custom, setCustom] = (0, import_react4.useState)(() => linked.from && linked.to ? { start: linked.from, end: linked.to } : { start: isoDaysAgo(30), end: isoDaysAgo(0) });
|
|
2783
2782
|
const atPresent = custom.end >= isoDaysAgo(0);
|
|
2784
2783
|
const [activePanel, setActivePanel] = (0, import_react4.useState)(linkedTab ?? "overview");
|