@xeonr/telemetry-web 0.0.0-stage → 0.1.1
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 +170 -2
- package/dist/classify.d.ts +48 -0
- package/dist/classify.d.ts.map +1 -0
- package/dist/classify.js +117 -0
- package/dist/classify.js.map +1 -0
- package/dist/client.d.ts +135 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +511 -0
- package/dist/client.js.map +1 -0
- package/dist/connect.d.ts +11 -0
- package/dist/connect.d.ts.map +1 -0
- package/dist/connect.js +44 -0
- package/dist/connect.js.map +1 -0
- package/dist/env.d.ts +68 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +97 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +89 -0
- package/dist/index.js.map +1 -0
- package/dist/otlp.d.ts +124 -0
- package/dist/otlp.d.ts.map +1 -0
- package/dist/otlp.js +105 -0
- package/dist/otlp.js.map +1 -0
- package/dist/queue.d.ts +92 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +355 -0
- package/dist/queue.js.map +1 -0
- package/dist/react.d.ts +34 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +46 -0
- package/dist/react.js.map +1 -0
- package/dist/stack.d.ts +23 -0
- package/dist/stack.d.ts.map +1 -0
- package/dist/stack.js +52 -0
- package/dist/stack.js.map +1 -0
- package/dist/trace.d.ts +10 -0
- package/dist/trace.d.ts.map +1 -0
- package/dist/trace.js +46 -0
- package/dist/trace.js.map +1 -0
- package/dist/vitals.d.ts +20 -0
- package/dist/vitals.d.ts.map +1 -0
- package/dist/vitals.js +151 -0
- package/dist/vitals.js.map +1 -0
- package/dist/vite.d.ts +41 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +171 -0
- package/dist/vite.js.map +1 -0
- package/package.json +59 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,GAAG,EAAS,MAAM,UAAU,CAAC;AAC3C,OAAO,EAA8B,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,KAAK,KAAK,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAE7G,2EAA2E;AAC3E,MAAM,WAAW,IAAI;IACnB,+BAA+B;IAC/B,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IACb,0FAA0F;IAC1F,CAAC,EAAE,MAAM,CAAC;IACV,mCAAmC;IACnC,CAAC,CAAC,EAAE,CAAC,CAAC;IACN,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;CACrB;AAED,UAAU,MAAM;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,sEAAsE;IACtE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAED,eAAO,MAAM,QAAQ;;;;;;IAMnB,gFAAgF;;IAEhF,0BAA0B;;IAE1B,oEAAoE;;IAEpE,iDAAiD;;;CAGzC,CAAC;AAEX,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAM1F;AAED,wHAAwH;AACxH,wBAAgB,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,CAY1E;AAMD,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAM;IAC1B,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAW;IAC7B,OAAO,CAAC,GAAG,CAAc;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,IAAI,CAAoB;IAChC,OAAO,CAAC,MAAM,CAAqB;IACnC,2FAA2F;IAC3F,UAAU,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;gBAEzB,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,eAAe;IAYxC,kGAAkG;IAClG,KAAK,IAAI,IAAI;IAYb,IAAI,IAAI,IAAI;IAOZ,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAMtB,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,MAAM;IAMd,SAAS,IAAI,MAAM;IAWnB,OAAO,CAAC,UAAU;IAgBlB,wCAAwC;IACxC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAM9C,+EAA+E;IAC/E,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,GAAG;IAIX,4GAA4G;IAC5G,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG;QAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;QAAC,KAAK,EAAE,IAAI,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE;YAsBrE,IAAI;IA8BlB,iFAAiF;IAC3E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC5B,2FAA2F;IAC3F,IAAI,IAAI,IAAI;IAaZ,WAAW,IAAI,IAAI;IAwBnB,qEAAqE;IACrE,OAAO,CAAC,MAAM;CAkBf"}
|
package/dist/queue.js
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
// Getting records to the ingest, including when it — or the network — is not
|
|
2
|
+
// there. The point of client telemetry is to hear from a browser precisely
|
|
3
|
+
// when it cannot reach us, so a failed send is the normal case to design for:
|
|
4
|
+
//
|
|
5
|
+
// - Records queue in memory and go every 5 s, or as soon as 50 are waiting.
|
|
6
|
+
// - A send that fails in a way worth retrying (no answer, 408, 429, 5xx)
|
|
7
|
+
// moves its records to localStorage — bounded at 200 records / 256 KiB,
|
|
8
|
+
// oldest non-error first — and the next attempt waits for Retry-After or
|
|
9
|
+
// an exponential backoff. Offline, records go straight there.
|
|
10
|
+
// - On the next page load (or another tab's next flush) they are replayed.
|
|
11
|
+
// - When the page is hidden, everything left goes by navigator.sendBeacon
|
|
12
|
+
// as a text/plain Blob — no headers, so the service name rides in the
|
|
13
|
+
// query string, and no preflight.
|
|
14
|
+
//
|
|
15
|
+
// Every request is credentials: "omit" and text/plain (the ingest reads
|
|
16
|
+
// text/plain as OTLP JSON), so no request ever needs a CORS preflight — one
|
|
17
|
+
// fewer round trip to fail when the edge is having a bad day.
|
|
18
|
+
import { logsPayload, tracesPayload } from "./otlp.js";
|
|
19
|
+
export const DEFAULTS = {
|
|
20
|
+
flushIntervalMs: 5000,
|
|
21
|
+
maxBatch: 50,
|
|
22
|
+
maxStored: 200,
|
|
23
|
+
maxStoredBytes: 256 * 1024,
|
|
24
|
+
storageKey: "xeonr.telemetry.buffer",
|
|
25
|
+
/** in memory, before the oldest give way: a render loop must not eat the tab */
|
|
26
|
+
maxMemory: 1000,
|
|
27
|
+
/** records per request */
|
|
28
|
+
maxRequest: 500,
|
|
29
|
+
/** sendBeacon's quota is 64 KiB per page; stay under it per call */
|
|
30
|
+
beaconBytes: 60 * 1024,
|
|
31
|
+
/** fetch keepalive has the same 64 KiB budget */
|
|
32
|
+
keepaliveBytes: 60 * 1024,
|
|
33
|
+
maxBackoffMs: 5 * 60_000,
|
|
34
|
+
};
|
|
35
|
+
/** Retry-After as milliseconds: delta-seconds or an HTTP date. */
|
|
36
|
+
export function retryAfterMs(h, now) {
|
|
37
|
+
if (!h)
|
|
38
|
+
return undefined;
|
|
39
|
+
const secs = Number(h);
|
|
40
|
+
if (Number.isFinite(secs) && secs >= 0)
|
|
41
|
+
return secs * 1000;
|
|
42
|
+
const at = Date.parse(h);
|
|
43
|
+
return Number.isFinite(at) ? Math.max(0, at - now) : undefined;
|
|
44
|
+
}
|
|
45
|
+
/** Bound a list to `max` items and `maxBytes` of JSON, dropping the oldest non-error records first, then the oldest. */
|
|
46
|
+
export function bound(items, max, maxBytes) {
|
|
47
|
+
const out = items.slice();
|
|
48
|
+
const sizes = out.map((i) => JSON.stringify(i).length + 1);
|
|
49
|
+
let bytes = sizes.reduce((a, b) => a + b, 2);
|
|
50
|
+
while (out.length > 0 && (out.length > max || bytes > maxBytes)) {
|
|
51
|
+
let idx = out.findIndex((i) => !i.e);
|
|
52
|
+
if (idx < 0)
|
|
53
|
+
idx = 0;
|
|
54
|
+
bytes -= sizes[idx] ?? 0;
|
|
55
|
+
out.splice(idx, 1);
|
|
56
|
+
sizes.splice(idx, 1);
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
export class Delivery {
|
|
61
|
+
env;
|
|
62
|
+
o;
|
|
63
|
+
mem = [];
|
|
64
|
+
sending = false;
|
|
65
|
+
failures = 0;
|
|
66
|
+
timer;
|
|
67
|
+
soon;
|
|
68
|
+
warned = new Set();
|
|
69
|
+
/** Called right before the hide-time send, so last-moment records (web vitals) make it. */
|
|
70
|
+
beforeHide;
|
|
71
|
+
constructor(env, o) {
|
|
72
|
+
this.env = env;
|
|
73
|
+
this.o = {
|
|
74
|
+
flushIntervalMs: DEFAULTS.flushIntervalMs,
|
|
75
|
+
maxBatch: DEFAULTS.maxBatch,
|
|
76
|
+
maxStored: DEFAULTS.maxStored,
|
|
77
|
+
maxStoredBytes: DEFAULTS.maxStoredBytes,
|
|
78
|
+
storageKey: DEFAULTS.storageKey,
|
|
79
|
+
...stripUndefined(o),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/** Timers and page lifecycle listeners, plus a replay of whatever a previous page left behind. */
|
|
83
|
+
start() {
|
|
84
|
+
const { env } = this;
|
|
85
|
+
if (this.o.flushIntervalMs > 0)
|
|
86
|
+
this.timer = env.setInterval(() => void this.flush(), this.o.flushIntervalMs);
|
|
87
|
+
const hide = () => this.hide();
|
|
88
|
+
env.window?.addEventListener("pagehide", hide);
|
|
89
|
+
env.document?.addEventListener("visibilitychange", () => {
|
|
90
|
+
if (env.document?.visibilityState === "hidden")
|
|
91
|
+
hide();
|
|
92
|
+
});
|
|
93
|
+
env.window?.addEventListener("online", () => void this.flush());
|
|
94
|
+
if (this.readStore().items.length > 0)
|
|
95
|
+
this.schedule(1000);
|
|
96
|
+
}
|
|
97
|
+
stop() {
|
|
98
|
+
if (this.timer !== undefined)
|
|
99
|
+
this.env.clearInterval(this.timer);
|
|
100
|
+
if (this.soon !== undefined)
|
|
101
|
+
this.env.clearTimeout(this.soon);
|
|
102
|
+
this.timer = undefined;
|
|
103
|
+
this.soon = undefined;
|
|
104
|
+
}
|
|
105
|
+
get pending() {
|
|
106
|
+
return this.mem.length;
|
|
107
|
+
}
|
|
108
|
+
push(item) {
|
|
109
|
+
this.mem.push(item);
|
|
110
|
+
if (this.mem.length > DEFAULTS.maxMemory)
|
|
111
|
+
this.mem = bound(this.mem, DEFAULTS.maxMemory, Number.POSITIVE_INFINITY);
|
|
112
|
+
if (this.mem.length >= this.o.maxBatch)
|
|
113
|
+
this.schedule(0);
|
|
114
|
+
}
|
|
115
|
+
schedule(ms) {
|
|
116
|
+
if (this.soon !== undefined)
|
|
117
|
+
return;
|
|
118
|
+
this.soon = this.env.setTimeout(() => {
|
|
119
|
+
this.soon = undefined;
|
|
120
|
+
void this.flush();
|
|
121
|
+
}, ms);
|
|
122
|
+
}
|
|
123
|
+
online() {
|
|
124
|
+
return this.env.navigator?.onLine !== false;
|
|
125
|
+
}
|
|
126
|
+
// ---- the localStorage buffer ----
|
|
127
|
+
readStore() {
|
|
128
|
+
try {
|
|
129
|
+
const raw = this.env.local?.getItem(this.o.storageKey);
|
|
130
|
+
if (!raw)
|
|
131
|
+
return { retryAt: 0, items: [] };
|
|
132
|
+
const s = JSON.parse(raw);
|
|
133
|
+
return { retryAt: typeof s.retryAt === "number" ? s.retryAt : 0, items: Array.isArray(s.items) ? s.items : [] };
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return { retryAt: 0, items: [] };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
writeStore(s) {
|
|
140
|
+
const local = this.env.local;
|
|
141
|
+
if (!local)
|
|
142
|
+
return;
|
|
143
|
+
try {
|
|
144
|
+
if (s.items.length === 0 && s.retryAt <= this.env.now())
|
|
145
|
+
local.removeItem(this.o.storageKey);
|
|
146
|
+
else
|
|
147
|
+
local.setItem(this.o.storageKey, JSON.stringify({ retryAt: s.retryAt, items: bound(s.items, this.o.maxStored, this.o.maxStoredBytes - 32) }));
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Quota: halve what we keep and try once more, then give up quietly.
|
|
151
|
+
try {
|
|
152
|
+
local.setItem(this.o.storageKey, JSON.stringify({ retryAt: s.retryAt, items: bound(s.items, Math.floor(this.o.maxStored / 2), Math.floor(this.o.maxStoredBytes / 2)) }));
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
/* nothing more to do */
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** Keep records for a later attempt. */
|
|
160
|
+
persist(items, retryAt) {
|
|
161
|
+
if (items.length === 0 && retryAt === undefined)
|
|
162
|
+
return;
|
|
163
|
+
const s = this.readStore();
|
|
164
|
+
this.writeStore({ retryAt: Math.max(s.retryAt, retryAt ?? 0), items: s.items.concat(items) });
|
|
165
|
+
}
|
|
166
|
+
/** Claim stored records for sending, so another tab does not send them too. */
|
|
167
|
+
takeStored(max) {
|
|
168
|
+
const s = this.readStore();
|
|
169
|
+
if (s.items.length === 0)
|
|
170
|
+
return { items: [], retryAt: s.retryAt };
|
|
171
|
+
const items = s.items.slice(0, max);
|
|
172
|
+
this.writeStore({ retryAt: s.retryAt, items: s.items.slice(max) });
|
|
173
|
+
return { items, retryAt: s.retryAt };
|
|
174
|
+
}
|
|
175
|
+
backoff() {
|
|
176
|
+
const base = Math.min(DEFAULTS.flushIntervalMs * 2 ** Math.max(0, this.failures - 1), DEFAULTS.maxBackoffMs);
|
|
177
|
+
return base + (this.env.randomBytes(1)[0] ?? 0) / 255 * base * 0.25;
|
|
178
|
+
}
|
|
179
|
+
// ---- sending ----
|
|
180
|
+
url(signal) {
|
|
181
|
+
return `${this.o.endpoint}/v1/${signal}?service=${encodeURIComponent(this.o.service)}`;
|
|
182
|
+
}
|
|
183
|
+
/** The bodies for a set of items: at most one logs and one traces payload, a resource entry per release. */
|
|
184
|
+
bodies(items) {
|
|
185
|
+
const out = [];
|
|
186
|
+
for (const signal of ["logs", "traces"]) {
|
|
187
|
+
const mine = items.filter((i) => (signal === "logs" ? i.k === "l" : i.k === "s"));
|
|
188
|
+
if (mine.length === 0)
|
|
189
|
+
continue;
|
|
190
|
+
const byVersion = new Map();
|
|
191
|
+
for (const i of mine) {
|
|
192
|
+
const list = byVersion.get(i.v);
|
|
193
|
+
if (list)
|
|
194
|
+
list.push(i);
|
|
195
|
+
else
|
|
196
|
+
byVersion.set(i.v, [i]);
|
|
197
|
+
}
|
|
198
|
+
if (signal === "logs") {
|
|
199
|
+
const payload = { resourceLogs: [...byVersion].flatMap(([v, is]) => logsPayload(this.o.resource(v), this.o.scope, is.map((i) => i.r)).resourceLogs) };
|
|
200
|
+
out.push({ signal, items: mine, body: JSON.stringify(payload) });
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
const payload = { resourceSpans: [...byVersion].flatMap(([v, is]) => tracesPayload(this.o.resource(v), this.o.scope, is.map((i) => i.r)).resourceSpans) };
|
|
204
|
+
out.push({ signal, items: mine, body: JSON.stringify(payload) });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
209
|
+
async post(signal, body) {
|
|
210
|
+
const f = this.env.fetch;
|
|
211
|
+
if (!f)
|
|
212
|
+
return { outcome: "retry" };
|
|
213
|
+
let res;
|
|
214
|
+
try {
|
|
215
|
+
res = await f(this.url(signal), {
|
|
216
|
+
method: "POST",
|
|
217
|
+
body,
|
|
218
|
+
credentials: "omit",
|
|
219
|
+
mode: "cors",
|
|
220
|
+
// Small batches survive the page going away mid-send.
|
|
221
|
+
keepalive: body.length < DEFAULTS.keepaliveBytes,
|
|
222
|
+
headers: { "content-type": "text/plain;charset=UTF-8" },
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
return { outcome: "retry" };
|
|
227
|
+
}
|
|
228
|
+
if (res.ok)
|
|
229
|
+
return { outcome: "ok" };
|
|
230
|
+
if (res.status === 408 || res.status === 429 || res.status >= 500) {
|
|
231
|
+
return { outcome: "retry", retryAfter: retryAfterMs(res.headers.get("retry-after"), Date.now()) };
|
|
232
|
+
}
|
|
233
|
+
// 400/401/403/413: the ingest will never take these. Say so once, and drop.
|
|
234
|
+
const why = `@xeonr/telemetry-web: the ingest refused ${signal} (${res.status}); dropping them`;
|
|
235
|
+
if (!this.warned.has(why)) {
|
|
236
|
+
this.warned.add(why);
|
|
237
|
+
this.env.warn(why);
|
|
238
|
+
}
|
|
239
|
+
return { outcome: "drop" };
|
|
240
|
+
}
|
|
241
|
+
/** Send what is queued (and what an earlier page left behind). Never rejects. */
|
|
242
|
+
async flush() {
|
|
243
|
+
if (this.sending)
|
|
244
|
+
return;
|
|
245
|
+
try {
|
|
246
|
+
const now = this.env.now();
|
|
247
|
+
const stored = this.readStore();
|
|
248
|
+
if (!this.online() || now < stored.retryAt) {
|
|
249
|
+
if (this.mem.length)
|
|
250
|
+
this.persist(this.mem.splice(0));
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const taken = this.takeStored(DEFAULTS.maxRequest);
|
|
254
|
+
const batch = taken.items.concat(this.mem.splice(0, Math.max(0, DEFAULTS.maxRequest - taken.items.length)));
|
|
255
|
+
if (batch.length === 0)
|
|
256
|
+
return;
|
|
257
|
+
this.sending = true;
|
|
258
|
+
const retry = [];
|
|
259
|
+
let retryAfter;
|
|
260
|
+
for (const b of this.bodies(batch)) {
|
|
261
|
+
const r = await this.post(b.signal, b.body);
|
|
262
|
+
if (r.outcome === "retry") {
|
|
263
|
+
retry.push(...b.items);
|
|
264
|
+
if (r.retryAfter !== undefined)
|
|
265
|
+
retryAfter = Math.max(retryAfter ?? 0, r.retryAfter);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (retry.length) {
|
|
269
|
+
this.failures++;
|
|
270
|
+
this.persist(retry, this.env.now() + (retryAfter ?? this.backoff()));
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
this.failures = 0;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
/* never into the host */
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
this.sending = false;
|
|
281
|
+
}
|
|
282
|
+
if (this.mem.length >= this.o.maxBatch)
|
|
283
|
+
this.schedule(0);
|
|
284
|
+
}
|
|
285
|
+
/** The page is going away (or into the background): everything left goes by beacon now. */
|
|
286
|
+
hide() {
|
|
287
|
+
try {
|
|
288
|
+
this.beforeHide?.();
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
/* ignore */
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
this.flushBeacon();
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
/* never into the host */
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
flushBeacon() {
|
|
301
|
+
const stored = this.readStore();
|
|
302
|
+
const items = this.takeStored(Number.POSITIVE_INFINITY).items.concat(this.mem.splice(0));
|
|
303
|
+
if (items.length === 0)
|
|
304
|
+
return;
|
|
305
|
+
const beacon = this.env.navigator?.sendBeacon;
|
|
306
|
+
if (!this.online() || this.env.now() < stored.retryAt || typeof beacon !== "function") {
|
|
307
|
+
this.persist(items);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const failed = [];
|
|
311
|
+
for (const chunk of this.chunks(items)) {
|
|
312
|
+
for (const b of this.bodies(chunk)) {
|
|
313
|
+
let ok = false;
|
|
314
|
+
try {
|
|
315
|
+
ok = beacon.call(this.env.navigator, this.url(b.signal), new Blob([b.body], { type: "text/plain;charset=UTF-8" }));
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
ok = false;
|
|
319
|
+
}
|
|
320
|
+
if (!ok)
|
|
321
|
+
failed.push(...b.items);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (failed.length)
|
|
325
|
+
this.persist(failed);
|
|
326
|
+
}
|
|
327
|
+
/** Items in groups whose payload stays under the beacon's 64 KiB. */
|
|
328
|
+
chunks(items) {
|
|
329
|
+
const out = [];
|
|
330
|
+
const overhead = 4096; // resource + scope + framing, generously
|
|
331
|
+
let cur = [];
|
|
332
|
+
let size = overhead;
|
|
333
|
+
for (const i of items) {
|
|
334
|
+
const s = JSON.stringify(i.r).length + 1;
|
|
335
|
+
if (cur.length > 0 && size + s > DEFAULTS.beaconBytes) {
|
|
336
|
+
out.push(cur);
|
|
337
|
+
cur = [];
|
|
338
|
+
size = overhead;
|
|
339
|
+
}
|
|
340
|
+
cur.push(i);
|
|
341
|
+
size += s;
|
|
342
|
+
}
|
|
343
|
+
if (cur.length)
|
|
344
|
+
out.push(cur);
|
|
345
|
+
return out;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function stripUndefined(o) {
|
|
349
|
+
const out = {};
|
|
350
|
+
for (const [k, v] of Object.entries(o))
|
|
351
|
+
if (v !== undefined)
|
|
352
|
+
out[k] = v;
|
|
353
|
+
return out;
|
|
354
|
+
}
|
|
355
|
+
//# sourceMappingURL=queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"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"}
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Component, type ErrorInfo, type ReactNode } from "react";
|
|
2
|
+
import type { TelemetryClient } from "./client.ts";
|
|
3
|
+
export interface FallbackProps {
|
|
4
|
+
error: unknown;
|
|
5
|
+
/** The X-Trace-Id behind the error, when it came from a call that had one. */
|
|
6
|
+
traceId: string | undefined;
|
|
7
|
+
/** Clear the error and render the children again. */
|
|
8
|
+
reset: () => void;
|
|
9
|
+
}
|
|
10
|
+
export interface TelemetryErrorBoundaryProps {
|
|
11
|
+
/** What to render instead. A function gets the error, its trace id and a reset. Default: nothing. */
|
|
12
|
+
fallback?: ReactNode | ((p: FallbackProps) => ReactNode) | undefined;
|
|
13
|
+
/** The client to report through; default the page's (init). */
|
|
14
|
+
client?: TelemetryClient | undefined;
|
|
15
|
+
/** Called after reporting, for a host that also wants to know. */
|
|
16
|
+
onError?: ((error: unknown, info: ErrorInfo) => void) | undefined;
|
|
17
|
+
/** When any of these change, a caught error is cleared (the route, say). */
|
|
18
|
+
resetKeys?: readonly unknown[] | undefined;
|
|
19
|
+
children?: ReactNode;
|
|
20
|
+
}
|
|
21
|
+
interface State {
|
|
22
|
+
error: unknown;
|
|
23
|
+
has: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare class TelemetryErrorBoundary extends Component<TelemetryErrorBoundaryProps, State> {
|
|
26
|
+
state: State;
|
|
27
|
+
static getDerivedStateFromError(error: unknown): State;
|
|
28
|
+
componentDidCatch(error: unknown, info: ErrorInfo): void;
|
|
29
|
+
componentDidUpdate(prev: TelemetryErrorBoundaryProps): void;
|
|
30
|
+
reset: () => void;
|
|
31
|
+
render(): ReactNode;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=react.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAElE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,8EAA8E;IAC9E,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,qDAAqD;IACrD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,2BAA2B;IAC1C,qGAAqG;IACrG,QAAQ,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,aAAa,KAAK,SAAS,CAAC,GAAG,SAAS,CAAC;IACrE,+DAA+D;IAC/D,MAAM,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACrC,kEAAkE;IAClE,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAClE,4EAA4E;IAC5E,SAAS,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,UAAU,KAAK;IACb,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;CACd;AAED,qBAAa,sBAAuB,SAAQ,SAAS,CAAC,2BAA2B,EAAE,KAAK,CAAC;IAC9E,KAAK,EAAE,KAAK,CAAoC;IAEzD,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK;IAI7C,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IASxD,kBAAkB,CAAC,IAAI,EAAE,2BAA2B,GAAG,IAAI;IAOpE,KAAK,QAAO,IAAI,CAEd;IAEO,MAAM,IAAI,SAAS;CAM7B"}
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// @xeonr/telemetry-web/react — the error boundary.
|
|
2
|
+
//
|
|
3
|
+
// <TelemetryErrorBoundary fallback={({ error, traceId, reset }) => <Oops … />}>
|
|
4
|
+
// <App />
|
|
5
|
+
// </TelemetryErrorBoundary>
|
|
6
|
+
//
|
|
7
|
+
// A render error below it is reported (mechanism "react", with the component
|
|
8
|
+
// stack) and the fallback renders instead of an unmounted page.
|
|
9
|
+
import { Component } from "react";
|
|
10
|
+
import { getClient } from "./index.js";
|
|
11
|
+
import { traceIdFrom } from "./trace.js";
|
|
12
|
+
export class TelemetryErrorBoundary extends Component {
|
|
13
|
+
state = { error: undefined, has: false };
|
|
14
|
+
static getDerivedStateFromError(error) {
|
|
15
|
+
return { error, has: true };
|
|
16
|
+
}
|
|
17
|
+
componentDidCatch(error, info) {
|
|
18
|
+
try {
|
|
19
|
+
(this.props.client ?? getClient())?.captureException(error, { mechanism: "react", componentStack: info.componentStack ?? undefined });
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
/* never */
|
|
23
|
+
}
|
|
24
|
+
this.props.onError?.(error, info);
|
|
25
|
+
}
|
|
26
|
+
componentDidUpdate(prev) {
|
|
27
|
+
if (!this.state.has)
|
|
28
|
+
return;
|
|
29
|
+
const a = prev.resetKeys ?? [];
|
|
30
|
+
const b = this.props.resetKeys ?? [];
|
|
31
|
+
if (a.length !== b.length || a.some((x, i) => !Object.is(x, b[i])))
|
|
32
|
+
this.reset();
|
|
33
|
+
}
|
|
34
|
+
reset = () => {
|
|
35
|
+
this.setState({ error: undefined, has: false });
|
|
36
|
+
};
|
|
37
|
+
render() {
|
|
38
|
+
if (!this.state.has)
|
|
39
|
+
return this.props.children;
|
|
40
|
+
const { fallback } = this.props;
|
|
41
|
+
if (typeof fallback === "function")
|
|
42
|
+
return fallback({ error: this.state.error, traceId: traceIdFrom(this.state.error), reset: this.reset });
|
|
43
|
+
return fallback ?? null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,kFAAkF;AAClF,cAAc;AACd,8BAA8B;AAC9B,EAAE;AACF,6EAA6E;AAC7E,gEAAgE;AAEhE,OAAO,EAAE,SAAS,EAAkC,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA2BzC,MAAM,OAAO,sBAAuB,SAAQ,SAA6C;IAC9E,KAAK,GAAU,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IAEzD,MAAM,CAAC,wBAAwB,CAAC,KAAc;QAC5C,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;IAC9B,CAAC;IAEQ,iBAAiB,CAAC,KAAc,EAAE,IAAe;QACxD,IAAI,CAAC;YACH,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC,EAAE,gBAAgB,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,SAAS,EAAE,CAAC,CAAC;QACxI,CAAC;QAAC,MAAM,CAAC;YACP,WAAW;QACb,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAEQ,kBAAkB,CAAC,IAAiC;QAC3D,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG;YAAE,OAAO;QAC5B,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC;QACrC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IACnF,CAAC;IAED,KAAK,GAAG,GAAS,EAAE;QACjB,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IAClD,CAAC,CAAC;IAEO,MAAM;QACb,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;QAChD,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAChC,IAAI,OAAO,QAAQ,KAAK,UAAU;YAAE,OAAO,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC5I,OAAO,QAAQ,IAAI,IAAI,CAAC;IAC1B,CAAC;CACF"}
|
package/dist/stack.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface Frame {
|
|
2
|
+
file: string;
|
|
3
|
+
line?: number | undefined;
|
|
4
|
+
column?: number | undefined;
|
|
5
|
+
}
|
|
6
|
+
/** The first frame with a file, line and column, or undefined. */
|
|
7
|
+
export declare function topFrame(stack: string | undefined): Frame | undefined;
|
|
8
|
+
/** An error thrown from a browser extension's code, which no console can fix. */
|
|
9
|
+
export declare function isExtensionFrame(file: string | undefined): boolean;
|
|
10
|
+
/** A URL's query string and fragment removed, wherever it appears in a message. */
|
|
11
|
+
export declare function stripQueries(s: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* A message as it may leave the browser: no URL query strings (where tokens
|
|
14
|
+
* live), no email addresses, no bearer-looking secrets. Bounded.
|
|
15
|
+
*/
|
|
16
|
+
export declare function scrub(s: string, max?: number): string;
|
|
17
|
+
/**
|
|
18
|
+
* The stack, untouched frame by frame — symbolication needs them exactly —
|
|
19
|
+
* except that V8 repeats the message as the stack's first line, so that line
|
|
20
|
+
* gets the same scrubbing as the message itself.
|
|
21
|
+
*/
|
|
22
|
+
export declare function scrubStackHeader(stack: string, rawMessage: string, scrubbed: string): string;
|
|
23
|
+
//# sourceMappingURL=stack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAOD,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,SAAS,CAOrE;AAID,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAElE;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9C;AAKD;;;GAGG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,SAAO,GAAG,MAAM,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO5F"}
|
package/dist/stack.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Just enough stack reading to fill `code.*` and to tell an extension's
|
|
2
|
+
// error from the console's own. The stacktrace itself goes to Lantern raw:
|
|
3
|
+
// it symbolicates against the release's source maps at view time.
|
|
4
|
+
// V8: " at fn (https://x/assets/a.js:1:2)" or " at https://x/assets/a.js:1:2"
|
|
5
|
+
// Gecko/WebKit: "fn@https://x/assets/a.js:1:2" or "@https://x/assets/a.js:1:2"
|
|
6
|
+
const V8 = /^\s*at (?:.*? \()?(.+?):(\d+):(\d+)\)?\s*$/;
|
|
7
|
+
const GECKO = /^\s*[^@]*@(.+?):(\d+):(\d+)\s*$/;
|
|
8
|
+
/** The first frame with a file, line and column, or undefined. */
|
|
9
|
+
export function topFrame(stack) {
|
|
10
|
+
if (!stack)
|
|
11
|
+
return undefined;
|
|
12
|
+
for (const line of stack.split("\n")) {
|
|
13
|
+
const m = V8.exec(line) ?? GECKO.exec(line);
|
|
14
|
+
if (m && m[1])
|
|
15
|
+
return { file: m[1], line: Number(m[2]), column: Number(m[3]) };
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const EXTENSION = /^(chrome|moz|safari(-web)?|ms-browser)-extension:\/\//;
|
|
20
|
+
/** An error thrown from a browser extension's code, which no console can fix. */
|
|
21
|
+
export function isExtensionFrame(file) {
|
|
22
|
+
return !!file && EXTENSION.test(file);
|
|
23
|
+
}
|
|
24
|
+
/** A URL's query string and fragment removed, wherever it appears in a message. */
|
|
25
|
+
export function stripQueries(s) {
|
|
26
|
+
return s.replace(/(https?:\/\/[^\s?#"')]*)[?#][^\s"')]*/g, "$1");
|
|
27
|
+
}
|
|
28
|
+
const EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
|
|
29
|
+
const BEARER = /\b(bearer|token|key|secret|password)([=:\s]+)[A-Za-z0-9._~+/-]{8,}=*/gi;
|
|
30
|
+
/**
|
|
31
|
+
* A message as it may leave the browser: no URL query strings (where tokens
|
|
32
|
+
* live), no email addresses, no bearer-looking secrets. Bounded.
|
|
33
|
+
*/
|
|
34
|
+
export function scrub(s, max = 2048) {
|
|
35
|
+
return stripQueries(s).replace(EMAIL, "[email]").replace(BEARER, "$1$2[redacted]").slice(0, max);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The stack, untouched frame by frame — symbolication needs them exactly —
|
|
39
|
+
* except that V8 repeats the message as the stack's first line, so that line
|
|
40
|
+
* gets the same scrubbing as the message itself.
|
|
41
|
+
*/
|
|
42
|
+
export function scrubStackHeader(stack, rawMessage, scrubbed) {
|
|
43
|
+
if (!rawMessage || rawMessage === scrubbed)
|
|
44
|
+
return stack;
|
|
45
|
+
const at = stack.indexOf(rawMessage);
|
|
46
|
+
// Only the header: the message before the first frame.
|
|
47
|
+
const firstFrame = stack.search(/\n\s*at |\n[^\n]*@/);
|
|
48
|
+
if (at < 0 || (firstFrame >= 0 && at > firstFrame))
|
|
49
|
+
return stack;
|
|
50
|
+
return stack.slice(0, at) + scrubbed + stack.slice(at + rawMessage.length);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=stack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack.js","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,2EAA2E;AAC3E,kEAAkE;AAQlE,yFAAyF;AACzF,+EAA+E;AAC/E,MAAM,EAAE,GAAG,4CAA4C,CAAC;AACxD,MAAM,KAAK,GAAG,iCAAiC,CAAC;AAEhD,kEAAkE;AAClE,MAAM,UAAU,QAAQ,CAAC,KAAyB;IAChD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjF,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,SAAS,GAAG,uDAAuD,CAAC;AAE1E,iFAAiF;AACjF,MAAM,UAAU,gBAAgB,CAAC,IAAwB;IACvD,OAAO,CAAC,CAAC,IAAI,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAO,CAAC,CAAC,OAAO,CAAC,wCAAwC,EAAE,IAAI,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,KAAK,GAAG,iDAAiD,CAAC;AAChE,MAAM,MAAM,GAAG,wEAAwE,CAAC;AAExF;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,CAAS,EAAE,GAAG,GAAG,IAAI;IACzC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACnG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,UAAkB,EAAE,QAAgB;IAClF,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACzD,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACrC,uDAAuD;IACvD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACtD,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,EAAE,GAAG,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IACjE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;AAC7E,CAAC"}
|
package/dist/trace.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const TRACE_HEADER = "x-trace-id";
|
|
2
|
+
/**
|
|
3
|
+
* The X-Trace-Id behind an error or a response: a Response's headers, a
|
|
4
|
+
* ConnectError's metadata, an object's own `traceId`, or the same on its
|
|
5
|
+
* `cause` / `response`. Lower-case 32-hex, or undefined.
|
|
6
|
+
*/
|
|
7
|
+
export declare function traceIdFrom(v: unknown, depth?: number): string | undefined;
|
|
8
|
+
/** `1a2b3c4d…`: the first eight characters, which is what a person reads out. */
|
|
9
|
+
export declare function shortTraceId(id: string): string;
|
|
10
|
+
//# sourceMappingURL=trace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../src/trace.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,YAAY,eAAe,CAAC;AAUzC;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,MAAM,GAAG,SAAS,CAoBrE;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE/C"}
|
package/dist/trace.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// The server's trace id for a failed call, so a console can show
|
|
2
|
+
// `trace 1a2b3c4d… [copy]` and a person can hand it to whoever is on call.
|
|
3
|
+
// Every Xeonr service echoes it as `X-Trace-Id` (fleet plan §8); a Connect
|
|
4
|
+
// error carries response headers and trailers in its metadata, a fetch
|
|
5
|
+
// Response in its headers.
|
|
6
|
+
import { normaliseTraceId } from "./otlp.js";
|
|
7
|
+
export const TRACE_HEADER = "x-trace-id";
|
|
8
|
+
function isHeaderBag(v) {
|
|
9
|
+
return typeof v === "object" && v !== null && typeof v.get === "function";
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The X-Trace-Id behind an error or a response: a Response's headers, a
|
|
13
|
+
* ConnectError's metadata, an object's own `traceId`, or the same on its
|
|
14
|
+
* `cause` / `response`. Lower-case 32-hex, or undefined.
|
|
15
|
+
*/
|
|
16
|
+
export function traceIdFrom(v, depth = 0) {
|
|
17
|
+
if (typeof v !== "object" || v === null || depth > 3)
|
|
18
|
+
return undefined;
|
|
19
|
+
try {
|
|
20
|
+
const o = v;
|
|
21
|
+
if (isHeaderBag(o.headers)) {
|
|
22
|
+
const id = normaliseTraceId(o.headers.get(TRACE_HEADER));
|
|
23
|
+
if (id)
|
|
24
|
+
return id;
|
|
25
|
+
}
|
|
26
|
+
if (isHeaderBag(o.metadata)) {
|
|
27
|
+
const id = normaliseTraceId(o.metadata.get(TRACE_HEADER));
|
|
28
|
+
if (id)
|
|
29
|
+
return id;
|
|
30
|
+
}
|
|
31
|
+
if (typeof o.traceId === "string") {
|
|
32
|
+
const id = normaliseTraceId(o.traceId);
|
|
33
|
+
if (id)
|
|
34
|
+
return id;
|
|
35
|
+
}
|
|
36
|
+
return traceIdFrom(o.response, depth + 1) ?? traceIdFrom(o.cause, depth + 1);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** `1a2b3c4d…`: the first eight characters, which is what a person reads out. */
|
|
43
|
+
export function shortTraceId(id) {
|
|
44
|
+
return id.length > 8 ? `${id.slice(0, 8)}…` : id;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=trace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.js","sourceRoot":"","sources":["../src/trace.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,2EAA2E;AAC3E,2EAA2E;AAC3E,uEAAuE;AACvE,2BAA2B;AAE3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,CAAC,MAAM,YAAY,GAAG,YAAY,CAAC;AAMzC,SAAS,WAAW,CAAC,CAAU;IAC7B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,OAAQ,CAAuB,CAAC,GAAG,KAAK,UAAU,CAAC;AACnG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,CAAU,EAAE,KAAK,GAAG,CAAC;IAC/C,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACvE,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,CAAsG,CAAC;QACjH,IAAI,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,MAAM,EAAE,GAAG,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;YACzD,IAAI,EAAE;gBAAE,OAAO,EAAE,CAAC;QACpB,CAAC;QACD,IAAI,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,MAAM,EAAE,GAAG,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;YAC1D,IAAI,EAAE;gBAAE,OAAO,EAAE,CAAC;QACpB,CAAC;QACD,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YAClC,MAAM,EAAE,GAAG,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACvC,IAAI,EAAE;gBAAE,OAAO,EAAE,CAAC;QACpB,CAAC;QACD,OAAO,WAAW,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,GAAG,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;IAC/E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,OAAO,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AACnD,CAAC"}
|
package/dist/vitals.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Env } from "./env.ts";
|
|
2
|
+
export type VitalName = "LCP" | "INP" | "CLS" | "FCP" | "TTFB";
|
|
3
|
+
export type Rating = "good" | "needs-improvement" | "poor";
|
|
4
|
+
export declare function rate(name: VitalName, value: number): Rating;
|
|
5
|
+
export type VitalReporter = (name: VitalName, value: number, rating: Rating) => void;
|
|
6
|
+
/** INP's ordinal: the worst interaction, or for every 50 interactions one further down the list. */
|
|
7
|
+
export declare function inpFrom(durations: number[], interactionCount: number): number | undefined;
|
|
8
|
+
/** CLS: the largest session window of shifts (gap < 1 s, span < 5 s), ignoring shifts right after input. */
|
|
9
|
+
export declare function clsFrom(entries: {
|
|
10
|
+
startTime: number;
|
|
11
|
+
value: number;
|
|
12
|
+
hadRecentInput: boolean;
|
|
13
|
+
}[]): number;
|
|
14
|
+
export interface Vitals {
|
|
15
|
+
/** Report the metrics that are only final once the page is hidden. Idempotent. */
|
|
16
|
+
finalize(): void;
|
|
17
|
+
stop(): void;
|
|
18
|
+
}
|
|
19
|
+
export declare function observeVitals(env: Env, report: VitalReporter): Vitals;
|
|
20
|
+
//# sourceMappingURL=vitals.d.ts.map
|