@devvit/web-view-scripts 0.13.7-next-2026-07-06-22-55-50-e17da017f.0 → 0.13.7

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/telemetry.js CHANGED
@@ -2,7 +2,7 @@ import { EffectType } from '@devvit/protos/json/devvit/ui/effects/v1alpha/effect
2
2
  import { emitEffect } from '@devvit/shared-types/client/emit-effect.js';
3
3
  import { emitTelemetryClickEffect } from '@devvit/shared-types/client/telemetry.js';
4
4
  let telemetryMetrics;
5
- /** startTime (UTC milliseconds) is provided by the client within the bridge context for native-clock metrics. */
5
+ /** startTime (UTC milliseconds) is provided by the client within the bridge context, to be used for all metrics */
6
6
  let startTime;
7
7
  // Render duration still needs an emit guard because both FCP and navigation callbacks can attempt it.
8
8
  let renderDurationSent = false;
@@ -56,10 +56,27 @@ function measureTtfb() {
56
56
  timeEnd: startTime + navigationTiming.responseStart,
57
57
  };
58
58
  }
59
- function buildTimeOriginMetric(spanName, offset) {
59
+ /**
60
+ * Builds both the legacy v1 metric and the clock-aligned v2 metric for a span.
61
+ *
62
+ * The v1 metric keeps its historical (flawed) anchoring of the native `startTime`
63
+ * clock against a `performance.timeOrigin`-relative offset, preserving the existing
64
+ * series. The v2 metric anchors `timeStart` to `performance.timeOrigin`, the same
65
+ * clock the offset is measured from, so its absolute timestamps are correct while
66
+ * the duration stays identical to v1.
67
+ */
68
+ function buildMetricPair(spanName, offset) {
69
+ if (!startTime) {
70
+ return [];
71
+ }
60
72
  return [
61
73
  {
62
74
  spanName,
75
+ timeStart: startTime,
76
+ timeEnd: startTime + offset,
77
+ },
78
+ {
79
+ spanName: `${spanName}_v2`,
63
80
  timeStart: performance.timeOrigin,
64
81
  timeEnd: performance.timeOrigin + offset,
65
82
  },
@@ -103,13 +120,16 @@ function maybeMeasureRenderDuration() {
103
120
  function measureFcp() {
104
121
  const paintEntries = performance.getEntriesByType('paint');
105
122
  const fcpEntry = paintEntries.find((entry) => entry.name === 'first-contentful-paint');
106
- if (!fcpEntry) {
123
+ if (!fcpEntry || !startTime) {
107
124
  return [];
108
125
  }
109
- return buildTimeOriginMetric('web_view_first_contentful_paint', fcpEntry.startTime);
126
+ return buildMetricPair('web_view_first_contentful_paint', fcpEntry.startTime);
110
127
  }
111
128
  function measureLoad() {
112
- return buildTimeOriginMetric('web_view_load', performance.now());
129
+ if (!startTime) {
130
+ return [];
131
+ }
132
+ return buildMetricPair('web_view_load', performance.now());
113
133
  }
114
134
  function measureLoadEventEnd(navigationTiming = getNavigationTiming()) {
115
135
  return navigationTiming && navigationTiming.loadEventEnd > 0
@@ -138,16 +158,19 @@ function emitRenderDuration() {
138
158
  */
139
159
  function measureTti() {
140
160
  const navTiming = getNavigationTiming();
141
- const ttiTime = navTiming?.domInteractive;
142
- if (!ttiTime) {
161
+ const ttiTime = navTiming.domInteractive;
162
+ if (!startTime || !ttiTime) {
143
163
  return [];
144
164
  }
145
- return buildTimeOriginMetric('web_view_time_to_interactive', ttiTime);
165
+ return buildMetricPair('web_view_time_to_interactive', ttiTime);
146
166
  }
147
167
  function initPerformanceMonitoring() {
148
168
  telemetryMetrics = [];
149
169
  startTime = globalThis.devvit?.startTime;
150
170
  renderDurationSent = false;
171
+ if (!startTime) {
172
+ return;
173
+ }
151
174
  const initialization = measureWebViewInitialization();
152
175
  if (initialization)
153
176
  telemetryMetrics.push(initialization);